Fix notifications opening the wrong workspace (#2331)

* fix(app): open notifications on the right host

Agent notifications previously omitted workspace ownership, so a cold target host was treated as missing and fell back to its empty home route. Carry the authoritative workspace and keep older notifications on a target-host resolver until lookup is conclusive.

* fix(app): separate notification and agent URL routing

Notifications use their authoritative workspace target directly. Stable agent URLs remain server-and-agent targets whose workspace is resolved by the agent route.

* fix(protocol): preserve notification workspace targets

Both attention message variants retain workspaceId through outbound validation so notification clicks receive the authoritative route target.

* test(app): use workspace-scoped notification target

* test(server): give notification agents workspace targets

* test(app): target notification workspace in navigation e2e
This commit is contained in:
Mohamed Boudra
2026-07-23 03:32:28 +08:00
committed by GitHub
parent de69b2a2af
commit 5dfe50ca74
19 changed files with 627 additions and 110 deletions
@@ -138,6 +138,65 @@ const SourceSchema = z.object({
);
});
it.each([
{
name: "dedicated attention message",
message: {
type: "agent_attention_required",
payload: {
agentId: "agent-1",
reason: "finished",
timestamp: "2026-07-22T18:00:00.000Z",
shouldNotify: true,
notification: {
title: "Agent finished",
body: "Done",
data: {
serverId: "server-1",
workspaceId: "workspace-1",
agentId: "agent-1",
reason: "finished",
},
},
},
},
},
{
name: "agent stream attention event",
message: {
type: "agent_stream",
payload: {
agentId: "agent-1",
timestamp: "2026-07-22T18:00:00.000Z",
event: {
type: "attention_required",
provider: "codex",
reason: "finished",
timestamp: "2026-07-22T18:00:00.000Z",
shouldNotify: true,
notification: {
title: "Agent finished",
body: "Done",
data: {
serverId: "server-1",
workspaceId: "workspace-1",
agentId: "agent-1",
reason: "finished",
},
},
},
},
},
},
])("preserves workspaceId in a $name", ({ message }) => {
const envelope = { type: "session", message };
expect(GeneratedWSOutboundMessageSchema.safeParse(envelope)).toEqual({
success: true,
data: envelope,
});
});
it("emits runtime imports with .js extensions", async () => {
const generated = await readFile(generatedWSOutboundPath, "utf8");
expect(generated).toContain('from "../../validation/ws-outbound-schema-metadata.js"');