fix(tui): forward directory-scoped events when TUI is in workspace mode (#26671)
The TUI's useEvent filter returned early in the workspace branch when the inbound event's workspace did not match the TUI's active workspace — including the common case where event.workspace was undefined because the session has no workspaceID. The early return meant directory-scoped events from external POSTs (web UI, SDK, curl) targeting a session running in the same directory were silently dropped, even though the server published them correctly with the right directory. Treat events without a workspace label as directory-scoped and fall through to the directory comparator so the TUI re-renders them. Also adds an explicit return after the global-event handler so a global event isn't forwarded twice. Test cases (a)-(c) in httpapi-promptasync-events.test.ts now all forward; (d) keeps cross-workspace events dropped. https://claude.ai/code/session_011VwDFG5W32dnu1qnT3SGr1
This commit is contained in:
parent
4fdeb5aac2
commit
7aa35180ab
2 changed files with 29 additions and 15 deletions
|
|
@ -15,13 +15,17 @@ export function useEvent() {
|
||||||
// Special hack for truly global events
|
// Special hack for truly global events
|
||||||
if (event.directory === "global") {
|
if (event.directory === "global") {
|
||||||
handler(event.payload)
|
handler(event.payload)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (project.workspace.current()) {
|
// Workspace-scoped events match on workspace identity. Events without a
|
||||||
|
// workspace label fall through to the directory check — a session with
|
||||||
|
// no workspaceID can be live in the same directory as the TUI even when
|
||||||
|
// the TUI itself is attached to a workspace (#26671).
|
||||||
|
if (event.workspace !== undefined) {
|
||||||
if (event.workspace === project.workspace.current()) {
|
if (event.workspace === project.workspace.current()) {
|
||||||
handler(event.payload)
|
handler(event.payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@
|
||||||
* in the TUI's client-side filter.
|
* in the TUI's client-side filter.
|
||||||
*
|
*
|
||||||
* Test 2 (synthetic filter): replicate the TUI useEvent filter shape from
|
* Test 2 (synthetic filter): replicate the TUI useEvent filter shape from
|
||||||
* packages/opencode/src/cli/cmd/tui/context/event.ts and prove the filter
|
* packages/opencode/src/cli/cmd/tui/context/event.ts and lock in the fixed
|
||||||
* drops events when the TUI has an active workspace but the inbound event has
|
* behaviour: events without a workspace label fall through to a directory
|
||||||
* workspace=undefined — even when the directory matches. That early `return`
|
* comparison even when the TUI itself is attached to a workspace, so a
|
||||||
* in the workspace branch is the smoking gun.
|
* directory-mode session driven by an external POST is forwarded correctly.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { afterEach, describe, expect, test } from "bun:test"
|
import { afterEach, describe, expect, test } from "bun:test"
|
||||||
|
|
@ -108,9 +108,10 @@ describe("session prompt_async events (issue #26671)", () => {
|
||||||
expect(event.workspace).toBeUndefined()
|
expect(event.workspace).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
test("TUI useEvent filter drops events when active workspace is set but event.workspace is undefined", () => {
|
test("TUI useEvent filter forwards directory-scoped events even when the TUI has an active workspace", () => {
|
||||||
// Mirrors the filter at packages/opencode/src/cli/cmd/tui/context/event.ts
|
// Mirrors the (fixed) filter at packages/opencode/src/cli/cmd/tui/context/event.ts.
|
||||||
// exactly so we can document the behaviour that #26671 is observing.
|
// If someone reverts the fix back to an early-return in the workspace
|
||||||
|
// branch, case (c) below catches it.
|
||||||
type IncomingEvent = {
|
type IncomingEvent = {
|
||||||
directory: string | undefined
|
directory: string | undefined
|
||||||
workspace: string | undefined
|
workspace: string | undefined
|
||||||
|
|
@ -125,7 +126,7 @@ describe("session prompt_async events (issue #26671)", () => {
|
||||||
if (event.payload.type === "sync") return false
|
if (event.payload.type === "sync") return false
|
||||||
if (event.directory === "global") return true
|
if (event.directory === "global") return true
|
||||||
|
|
||||||
if (activeWorkspace) {
|
if (event.workspace !== undefined) {
|
||||||
return event.workspace === activeWorkspace
|
return event.workspace === activeWorkspace
|
||||||
}
|
}
|
||||||
return event.directory === activeDirectory
|
return event.directory === activeDirectory
|
||||||
|
|
@ -149,18 +150,27 @@ describe("session prompt_async events (issue #26671)", () => {
|
||||||
}),
|
}),
|
||||||
).toBe(true)
|
).toBe(true)
|
||||||
|
|
||||||
// (c) THE BUG SHAPE FOR #26671:
|
// (c) THE FIX FOR #26671:
|
||||||
// TUI is in workspace mode, the inbound event has workspace=undefined
|
// TUI is in workspace mode, the inbound event has workspace=undefined
|
||||||
// (because the session has no workspaceID and the external POST didn't
|
// (the session has no workspaceID and the external POST didn't carry
|
||||||
// carry workspace context), but the directory still matches. The filter
|
// workspace context), and the directory matches. The fixed filter
|
||||||
// bails out in the workspace branch without ever consulting directory,
|
// recognises that an event without a workspace label is directory-
|
||||||
// so the event is dropped and the TUI never re-renders.
|
// scoped and consults the directory comparator, so this is forwarded.
|
||||||
expect(
|
expect(
|
||||||
tuiFilter({
|
tuiFilter({
|
||||||
event: { directory: "/proj", workspace: undefined, payload: { type: "session.next.message.created" } },
|
event: { directory: "/proj", workspace: undefined, payload: { type: "session.next.message.created" } },
|
||||||
activeWorkspace: "W1",
|
activeWorkspace: "W1",
|
||||||
activeDirectory: "/proj",
|
activeDirectory: "/proj",
|
||||||
}),
|
}),
|
||||||
|
).toBe(true)
|
||||||
|
|
||||||
|
// (d) Cross-workspace events stay dropped.
|
||||||
|
expect(
|
||||||
|
tuiFilter({
|
||||||
|
event: { directory: "/proj", workspace: "W2", payload: { type: "session.next.message.created" } },
|
||||||
|
activeWorkspace: "W1",
|
||||||
|
activeDirectory: "/proj",
|
||||||
|
}),
|
||||||
).toBe(false)
|
).toBe(false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue