summaryrefslogtreecommitdiffhomepage
path: root/packages/app
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-03-06 11:03:32 -0600
committerAdam <[email protected]>2026-03-06 11:03:37 -0600
commita71b11caca88243a9e4399317bcc5234d432976c (patch)
tree3854bfd906ddaae04351d8e327dc5d6e6fc9d536 /packages/app
parente9568999c385242756c2ea3530560481cf97999d (diff)
downloadopencode-a71b11caca88243a9e4399317bcc5234d432976c.tar.gz
opencode-a71b11caca88243a9e4399317bcc5234d432976c.zip
fix(app): stale keyed show errors
Diffstat (limited to 'packages/app')
-rw-r--r--packages/app/e2e/actions.ts51
-rw-r--r--packages/app/e2e/session/session-child-navigation.spec.ts37
2 files changed, 88 insertions, 0 deletions
diff --git a/packages/app/e2e/actions.ts b/packages/app/e2e/actions.ts
index fbb13008b..919a1add8 100644
--- a/packages/app/e2e/actions.ts
+++ b/packages/app/e2e/actions.ts
@@ -445,6 +445,57 @@ export async function seedSessionPermission(
return { id: result.id }
}
+export async function seedSessionTask(
+ sdk: ReturnType<typeof createSdk>,
+ input: {
+ sessionID: string
+ description: string
+ prompt: string
+ subagentType?: string
+ },
+) {
+ const text = [
+ "Your only valid response is one task tool call.",
+ `Use this JSON input: ${JSON.stringify({
+ description: input.description,
+ prompt: input.prompt,
+ subagent_type: input.subagentType ?? "general",
+ })}`,
+ "Do not output plain text.",
+ "Wait for the task to start and return the child session id.",
+ ].join("\n")
+
+ const result = await seed({
+ sdk,
+ sessionID: input.sessionID,
+ prompt: text,
+ timeout: 90_000,
+ probe: async () => {
+ const messages = await sdk.session.messages({ sessionID: input.sessionID, limit: 50 }).then((x) => x.data ?? [])
+ const part = messages
+ .flatMap((message) => message.parts)
+ .find((part) => {
+ if (part.type !== "tool" || part.tool !== "task") return false
+ if (part.state.input?.description !== input.description) return false
+ return typeof part.state.metadata?.sessionId === "string" && part.state.metadata.sessionId.length > 0
+ })
+
+ if (!part) return
+ const id = part.state.metadata?.sessionId
+ if (typeof id !== "string" || !id) return
+ const child = await sdk.session
+ .get({ sessionID: id })
+ .then((x) => x.data)
+ .catch(() => undefined)
+ if (!child?.id) return
+ return { sessionID: id }
+ },
+ })
+
+ if (!result) throw new Error("Timed out seeding task tool")
+ return result
+}
+
export async function seedSessionTodos(
sdk: ReturnType<typeof createSdk>,
input: {
diff --git a/packages/app/e2e/session/session-child-navigation.spec.ts b/packages/app/e2e/session/session-child-navigation.spec.ts
new file mode 100644
index 000000000..ac2dca33c
--- /dev/null
+++ b/packages/app/e2e/session/session-child-navigation.spec.ts
@@ -0,0 +1,37 @@
+import { seedSessionTask, withSession } from "../actions"
+import { test, expect } from "../fixtures"
+
+test("task tool child-session link does not trigger stale show errors", async ({ page, sdk, gotoSession }) => {
+ test.setTimeout(120_000)
+
+ const errs: string[] = []
+ const onError = (err: Error) => {
+ errs.push(err.message)
+ }
+ page.on("pageerror", onError)
+
+ await withSession(sdk, `e2e child nav ${Date.now()}`, async (session) => {
+ const child = await seedSessionTask(sdk, {
+ sessionID: session.id,
+ description: "Open child session",
+ prompt: "Search the repository for AssistantParts and then reply with exactly CHILD_OK.",
+ })
+
+ try {
+ await gotoSession(session.id)
+
+ const link = page
+ .locator("a.subagent-link")
+ .filter({ hasText: /open child session/i })
+ .first()
+ await expect(link).toBeVisible({ timeout: 30_000 })
+ await link.click()
+
+ await expect(page).toHaveURL(new RegExp(`/session/${child.sessionID}(?:[/?#]|$)`), { timeout: 30_000 })
+ await page.waitForTimeout(1000)
+ expect(errs).toEqual([])
+ } finally {
+ page.off("pageerror", onError)
+ }
+ })
+})