diff --git a/packages/tui/src/context/data.tsx b/packages/tui/src/context/data.tsx
index 55d1503794..fab50ba169 100644
--- a/packages/tui/src/context/data.tsx
+++ b/packages/tui/src/context/data.tsx
@@ -714,12 +714,15 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
},
},
form: {
- list(sessionID: string) {
- return store.session.form[sessionID]
+ list(sessionID: string, ref?: LocationRef) {
+ const forms = store.session.form[sessionID]
+ if (sessionID !== "global" || !ref) return forms
+ const key = locationKey(ref)
+ return forms?.filter((form) => form.location && locationKey(form.location) === key)
},
- async refresh(sessionID: string) {
+ async refresh(sessionID: string, ref?: LocationRef) {
if (sessionID === "global") {
- const result = await sdk.api.form.listRequests({ location: locationQuery(defaultLocation()) })
+ const result = await sdk.api.form.listRequests({ location: locationQuery(ref ?? defaultLocation()) })
const location = { directory: result.location.directory, workspaceID: result.location.workspaceID }
setStore(
"session",
diff --git a/packages/tui/src/routes/session/index.tsx b/packages/tui/src/routes/session/index.tsx
index 4286ce8f92..67109dd9fb 100644
--- a/packages/tui/src/routes/session/index.tsx
+++ b/packages/tui/src/routes/session/index.tsx
@@ -185,7 +185,7 @@ export function Session() {
const sessionIDs = session()?.parentID ? [route.sessionID] : [route.sessionID, ...descendantSessionIDs()]
return [
...sessionIDs.flatMap((sessionID) => data.session.form.list(sessionID) ?? []),
- ...(data.session.form.list("global") ?? []),
+ ...(data.session.form.list("global", location()) ?? []),
]
})
const [composer, setComposer] = createStore({
@@ -266,7 +266,7 @@ export function Session() {
await Promise.all([
data.session.permission.refresh(sessionID),
data.session.form.refresh(sessionID),
- data.session.form.refresh("global"),
+ data.session.form.refresh("global", info.location),
])
project.workspace.set(info.location.workspaceID)
@@ -949,8 +949,8 @@ export function Session() {
0}>
-
- {(form) => }
+
+ {() => {(form) => }}
{null}
diff --git a/packages/tui/test/cli/tui/data.test.tsx b/packages/tui/test/cli/tui/data.test.tsx
index 3d2a2a1d88..99e5dbce76 100644
--- a/packages/tui/test/cli/tui/data.test.tsx
+++ b/packages/tui/test/cli/tui/data.test.tsx
@@ -698,6 +698,118 @@ test("adds and dismisses permission requests from live events", async () => {
}
})
+test("tracks global forms by location", async () => {
+ const events = createEventStream()
+ const calls = createFetch(undefined, events)
+ const other = { directory: "/tmp/opencode-other", workspaceID: "wrk_other" }
+ let data!: ReturnType
+
+ function Probe() {
+ data = useData()
+ return
+ }
+
+ const app = await testRender(() => (
+
+
+
+
+
+
+
+
+
+ ))
+
+ try {
+ await wait(() => data.connection.status() === "connected")
+ events.emit({
+ id: "evt_form_created_1",
+ created: 0,
+ location: other,
+ type: "form.created",
+ data: {
+ form: {
+ id: "frm_1",
+ sessionID: "global",
+ mode: "form",
+ fields: [],
+ },
+ },
+ })
+
+ await wait(() => data.session.form.list("global", other)?.length === 1)
+ expect(data.session.form.list("global", { directory }) ?? []).toEqual([])
+
+ events.emit({
+ id: "evt_form_replied_1",
+ created: 1,
+ location: other,
+ type: "form.replied",
+ data: { id: "frm_1", sessionID: "global", answer: {} },
+ })
+ await wait(() => data.session.form.list("global", other)?.length === 0)
+ } finally {
+ app.renderer.destroy()
+ }
+})
+
+test("refreshes global forms for the requested location", async () => {
+ const events = createEventStream()
+ const requests: URL[] = []
+ const other = { directory: "/tmp/opencode-other", workspaceID: "wrk_other" }
+ const calls = createFetch((url) => {
+ if (url.pathname !== "/api/form/request") return
+ requests.push(url)
+ const requestedDirectory = url.searchParams.get("location[directory]") ?? directory
+ const requestedWorkspace = url.searchParams.get("location[workspace]") ?? undefined
+ return json({
+ location: {
+ directory: requestedDirectory,
+ workspaceID: requestedWorkspace,
+ project: { id: "proj_test", directory: requestedDirectory },
+ },
+ data:
+ requestedDirectory === other.directory
+ ? [{ id: "frm_other", sessionID: "global", mode: "form", fields: [] }]
+ : [],
+ })
+ }, events)
+ let data!: ReturnType
+
+ function Probe() {
+ data = useData()
+ return
+ }
+
+ const app = await testRender(() => (
+
+
+
+
+
+
+
+
+
+ ))
+
+ try {
+ await wait(() => requests.length > 0)
+ requests.length = 0
+
+ await data.session.form.refresh("global", other)
+
+ expect(requests).toHaveLength(1)
+ expect(requests[0]?.searchParams.get("location[directory]")).toBe(other.directory)
+ expect(requests[0]?.searchParams.get("location[workspace]")).toBe(other.workspaceID)
+ expect(data.session.form.list("global", other)?.map((form) => form.id)).toEqual(["frm_other"])
+ expect(data.session.form.list("global", { directory }) ?? []).toEqual([])
+ } finally {
+ app.renderer.destroy()
+ }
+})
+
test("settles pending tools when a live failure arrives", async () => {
const events = createEventStream()
const calls = createFetch(undefined, events)
diff --git a/packages/tui/test/fixture/tui-sdk.ts b/packages/tui/test/fixture/tui-sdk.ts
index b8fc93c428..d4613ae358 100644
--- a/packages/tui/test/fixture/tui-sdk.ts
+++ b/packages/tui/test/fixture/tui-sdk.ts
@@ -100,7 +100,8 @@ export function createFetch(override?: FetchHandler, events?: ReturnType