@syengup/friday-channel-next 0.1.36 → 0.1.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/dist/index.js +1 -1
  2. package/dist/src/agent/dispatch-bridge.d.ts +1 -1
  3. package/dist/src/agent/node-pairing-bridge.d.ts +11 -8
  4. package/dist/src/agent/node-pairing-bridge.js +6 -2
  5. package/dist/src/agent/subagent-registry.js +0 -3
  6. package/dist/src/channel-actions.js +3 -1
  7. package/dist/src/channel.js +0 -2
  8. package/dist/src/collect-message-media-paths.js +10 -1
  9. package/dist/src/friday-session.js +34 -10
  10. package/dist/src/history/normalize-message.js +22 -8
  11. package/dist/src/http/handlers/agent-config.js +10 -4
  12. package/dist/src/http/handlers/cancel.js +4 -2
  13. package/dist/src/http/handlers/device-approve.js +3 -1
  14. package/dist/src/http/handlers/files-download.js +6 -8
  15. package/dist/src/http/handlers/files.js +1 -1
  16. package/dist/src/http/handlers/health.js +18 -4
  17. package/dist/src/http/handlers/history-messages.js +1 -1
  18. package/dist/src/http/handlers/history-sessions.js +5 -3
  19. package/dist/src/http/handlers/messages.js +25 -11
  20. package/dist/src/http/handlers/models-list.js +1 -1
  21. package/dist/src/http/handlers/nodes-approve.js +1 -6
  22. package/dist/src/http/handlers/plugin-info.js +1 -1
  23. package/dist/src/http/server.js +4 -2
  24. package/dist/src/link-preview/og-parse.js +3 -1
  25. package/dist/src/plugin-install-info.js +4 -1
  26. package/dist/src/session/session-manager.js +9 -3
  27. package/dist/src/session-usage-store.js +3 -1
  28. package/dist/src/skills-discovery.d.ts +5 -4
  29. package/dist/src/skills-discovery.js +27 -22
  30. package/dist/src/sse/offline-queue.js +4 -1
  31. package/dist/src/tool-catalog.js +2 -3
  32. package/dist/src/upgrade-runtime.d.ts +1 -1
  33. package/dist/src/version.js +3 -1
  34. package/index.ts +43 -35
  35. package/install.js +131 -43
  36. package/package.json +10 -1
  37. package/src/agent/abort-run.ts +2 -3
  38. package/src/agent/dispatch-bridge.ts +2 -1
  39. package/src/agent/media-bridge.ts +9 -2
  40. package/src/agent/node-pairing-bridge.ts +29 -15
  41. package/src/agent/run-usage-accumulator.ts +4 -2
  42. package/src/agent/subagent-registry.ts +0 -4
  43. package/src/agent-run-context-bridge.ts +3 -1
  44. package/src/channel-actions.test.ts +10 -4
  45. package/src/channel-actions.ts +3 -1
  46. package/src/channel.outbound.test.ts +18 -4
  47. package/src/channel.ts +121 -123
  48. package/src/collect-message-media-paths.ts +15 -6
  49. package/src/config.ts +1 -4
  50. package/src/e2e/agents-list.e2e.test.ts +9 -2
  51. package/src/e2e/attachments-inbound.e2e.test.ts +5 -1
  52. package/src/e2e/attachments-outbound.e2e.test.ts +7 -2
  53. package/src/e2e/auto-approve.integration.test.ts +13 -7
  54. package/src/e2e/cancel-reconnect-errors.e2e.test.ts +18 -3
  55. package/src/e2e/connect-and-connected.e2e.test.ts +5 -1
  56. package/src/e2e/offline-replay.e2e.test.ts +17 -3
  57. package/src/e2e/send-text.e2e.test.ts +11 -2
  58. package/src/e2e/slash-commands.e2e.test.ts +5 -1
  59. package/src/e2e/status-cors-auth.e2e.test.ts +11 -2
  60. package/src/e2e/subagent-smoke.e2e.test.ts +68 -28
  61. package/src/e2e/subagent.e2e.test.ts +136 -53
  62. package/src/e2e/tool-lifecycle.e2e.test.ts +5 -1
  63. package/src/friday-session.forward-agent.test.ts +44 -12
  64. package/src/friday-session.ts +44 -20
  65. package/src/history/normalize-message.test.ts +35 -8
  66. package/src/history/normalize-message.ts +24 -12
  67. package/src/history/read-transcript.ts +1 -4
  68. package/src/http/handlers/agent-config.test.ts +10 -3
  69. package/src/http/handlers/agent-config.ts +22 -8
  70. package/src/http/handlers/agents-list.test.ts +1 -5
  71. package/src/http/handlers/cancel.test.ts +12 -3
  72. package/src/http/handlers/cancel.ts +4 -2
  73. package/src/http/handlers/device-approve.test.ts +12 -3
  74. package/src/http/handlers/device-approve.ts +33 -21
  75. package/src/http/handlers/files-download.ts +17 -13
  76. package/src/http/handlers/files.test.ts +8 -2
  77. package/src/http/handlers/files.ts +21 -7
  78. package/src/http/handlers/health.test.ts +43 -11
  79. package/src/http/handlers/health.ts +22 -6
  80. package/src/http/handlers/history-messages.test.ts +51 -9
  81. package/src/http/handlers/history-messages.ts +4 -1
  82. package/src/http/handlers/history-sessions.test.ts +46 -9
  83. package/src/http/handlers/history-sessions.ts +5 -3
  84. package/src/http/handlers/history-set-title.test.ts +14 -5
  85. package/src/http/handlers/link-preview.test.ts +57 -16
  86. package/src/http/handlers/link-preview.ts +4 -1
  87. package/src/http/handlers/messages.test.ts +12 -8
  88. package/src/http/handlers/messages.ts +57 -19
  89. package/src/http/handlers/models-list.ts +14 -8
  90. package/src/http/handlers/nodes-approve.test.ts +15 -4
  91. package/src/http/handlers/nodes-approve.ts +38 -40
  92. package/src/http/handlers/plugin-info.ts +5 -6
  93. package/src/http/handlers/plugin-upgrade.ts +4 -1
  94. package/src/http/handlers/sse.ts +3 -1
  95. package/src/http/server.ts +9 -6
  96. package/src/link-preview/og-parse.test.ts +6 -2
  97. package/src/link-preview/og-parse.ts +10 -3
  98. package/src/link-preview/preview-service.ts +4 -1
  99. package/src/link-preview/ssrf-guard.test.ts +72 -15
  100. package/src/link-preview/ssrf-guard.ts +2 -1
  101. package/src/media-fetch.test.ts +7 -2
  102. package/src/media-fetch.ts +1 -2
  103. package/src/openclaw.d.ts +16 -9
  104. package/src/plugin-install-info.ts +20 -9
  105. package/src/run-metadata.ts +2 -1
  106. package/src/session/session-manager.ts +19 -11
  107. package/src/session-usage-snapshot.ts +3 -1
  108. package/src/session-usage-store.ts +3 -1
  109. package/src/skills-discovery.test.ts +14 -10
  110. package/src/skills-discovery.ts +43 -27
  111. package/src/sse/emitter.test.ts +1 -1
  112. package/src/sse/emitter.ts +9 -3
  113. package/src/sse/offline-queue.ts +17 -8
  114. package/src/test-support/app-simulator.ts +17 -3
  115. package/src/test-support/mock-dispatch.ts +17 -4
  116. package/src/thinking-levels.ts +3 -1
  117. package/src/tool-catalog.ts +16 -7
  118. package/src/upgrade-runtime.ts +4 -2
  119. package/src/version.ts +5 -1
  120. package/tsconfig.json +1 -1
@@ -8,7 +8,11 @@
8
8
  */
9
9
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
10
10
  import { createAppSimulator } from "../test-support/app-simulator.js";
11
- import { createTempHistoryDir, removeTempHistoryDir, setMockRuntime } from "../test-support/mock-runtime.js";
11
+ import {
12
+ createTempHistoryDir,
13
+ removeTempHistoryDir,
14
+ setMockRuntime,
15
+ } from "../test-support/mock-runtime.js";
12
16
  import { __setMockNodePairingForTests } from "../agent/node-pairing-bridge.js";
13
17
 
14
18
  const FAKE_DEVICE_ID = "a80b8c4b305fb02c5772c409c6dfcbacde691b61557f7779511ad1a5be8fdf06";
@@ -146,12 +150,14 @@ describe("e2e two-step auto-approval", () => {
146
150
  it("Step 2: node 已在 paired 中且有 caps 时返回 alreadyApproved", async () => {
147
151
  const mockListNodePairing = vi.fn().mockResolvedValueOnce({
148
152
  pending: [],
149
- paired: [{
150
- nodeId: FAKE_DEVICE_ID,
151
- approvedAtMs: 1700000000000,
152
- caps: ["location", "canvas"],
153
- commands: ["canvas.present"],
154
- }],
153
+ paired: [
154
+ {
155
+ nodeId: FAKE_DEVICE_ID,
156
+ approvedAtMs: 1700000000000,
157
+ caps: ["location", "canvas"],
158
+ commands: ["canvas.present"],
159
+ },
160
+ ],
155
161
  });
156
162
  const mockApproveNodePairing = vi.fn();
157
163
 
@@ -1,7 +1,11 @@
1
1
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
2
2
  import { createAppSimulator } from "../test-support/app-simulator.js";
3
3
  import { mockDispatchScript, resetMockDispatch } from "../test-support/mock-dispatch.js";
4
- import { createTempHistoryDir, removeTempHistoryDir, setMockRuntime } from "../test-support/mock-runtime.js";
4
+ import {
5
+ createTempHistoryDir,
6
+ removeTempHistoryDir,
7
+ setMockRuntime,
8
+ } from "../test-support/mock-runtime.js";
5
9
 
6
10
  describe("e2e cancel reconnect errors", () => {
7
11
  let historyDir = "";
@@ -15,7 +19,12 @@ describe("e2e cancel reconnect errors", () => {
15
19
  });
16
20
 
17
21
  it("cancel 缺 runId 与 200 cancel", async () => {
18
- mockDispatchScript().lifecycle("start").partial("a").deliverFinal({ text: "a" }).lifecycle("end").install();
22
+ mockDispatchScript()
23
+ .lifecycle("start")
24
+ .partial("a")
25
+ .deliverFinal({ text: "a" })
26
+ .lifecycle("end")
27
+ .install();
19
28
  const app = createAppSimulator({ token: "test-token" });
20
29
  await app.connectSSE();
21
30
  const sent = await app.sendMessage({ text: "go", sessionKey: "c1" });
@@ -35,7 +44,13 @@ describe("e2e cancel reconnect errors", () => {
35
44
  });
36
45
 
37
46
  it("Last-Event-ID replay 与多 device 隔离", async () => {
38
- mockDispatchScript().lifecycle("start").partial("h").partial("hi").deliverFinal({ text: "hi" }).lifecycle("end").install();
47
+ mockDispatchScript()
48
+ .lifecycle("start")
49
+ .partial("h")
50
+ .partial("hi")
51
+ .deliverFinal({ text: "hi" })
52
+ .lifecycle("end")
53
+ .install();
39
54
  const appA = createAppSimulator({ token: "test-token", deviceId: "A" });
40
55
  await appA.connectSSE();
41
56
  await appA.sendMessage({ text: "one", sessionKey: "r1" });
@@ -1,6 +1,10 @@
1
1
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
2
2
  import { createAppSimulator } from "../test-support/app-simulator.js";
3
- import { createTempHistoryDir, removeTempHistoryDir, setMockRuntime } from "../test-support/mock-runtime.js";
3
+ import {
4
+ createTempHistoryDir,
5
+ removeTempHistoryDir,
6
+ setMockRuntime,
7
+ } from "../test-support/mock-runtime.js";
4
8
 
5
9
  describe("e2e connect and connected", () => {
6
10
  let historyDir = "";
@@ -1,7 +1,11 @@
1
1
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
2
2
  import { createAppSimulator } from "../test-support/app-simulator.js";
3
3
  import { mockDispatchScript, resetMockDispatch } from "../test-support/mock-dispatch.js";
4
- import { createTempHistoryDir, removeTempHistoryDir, setMockRuntime } from "../test-support/mock-runtime.js";
4
+ import {
5
+ createTempHistoryDir,
6
+ removeTempHistoryDir,
7
+ setMockRuntime,
8
+ } from "../test-support/mock-runtime.js";
5
9
 
6
10
  describe("e2e offline SSE replay", () => {
7
11
  let historyDir = "";
@@ -17,7 +21,12 @@ describe("e2e offline SSE replay", () => {
17
21
  });
18
22
 
19
23
  it("断开后产生的事件在 Last-Event-ID 重连后回放", async () => {
20
- mockDispatchScript().lifecycle("start").partial("x").deliverFinal({ text: "x" }).lifecycle("end").install();
24
+ mockDispatchScript()
25
+ .lifecycle("start")
26
+ .partial("x")
27
+ .deliverFinal({ text: "x" })
28
+ .lifecycle("end")
29
+ .install();
21
30
 
22
31
  const app = createAppSimulator({ token: "test-token", deviceId: "replay-dev" });
23
32
  await app.connectSSE({ deviceId: "replay-dev" });
@@ -28,7 +37,12 @@ describe("e2e offline SSE replay", () => {
28
37
  app.disconnectSSE();
29
38
 
30
39
  resetMockDispatch();
31
- mockDispatchScript().lifecycle("start").partial("y").deliverFinal({ text: "y" }).lifecycle("end").install();
40
+ mockDispatchScript()
41
+ .lifecycle("start")
42
+ .partial("y")
43
+ .deliverFinal({ text: "y" })
44
+ .lifecycle("end")
45
+ .install();
32
46
  await app.sendMessage({ text: "again", sessionKey: "r1", deviceId: "replay-dev" });
33
47
  await new Promise((r) => setTimeout(r, 80));
34
48
 
@@ -1,7 +1,11 @@
1
1
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
2
2
  import { createAppSimulator } from "../test-support/app-simulator.js";
3
3
  import { mockDispatchScript, resetMockDispatch } from "../test-support/mock-dispatch.js";
4
- import { createTempHistoryDir, removeTempHistoryDir, setMockRuntime } from "../test-support/mock-runtime.js";
4
+ import {
5
+ createTempHistoryDir,
6
+ removeTempHistoryDir,
7
+ setMockRuntime,
8
+ } from "../test-support/mock-runtime.js";
5
9
 
6
10
  describe("e2e send text", () => {
7
11
  let historyDir = "";
@@ -41,7 +45,12 @@ describe("e2e send text", () => {
41
45
  });
42
46
 
43
47
  it("assistant 流式事件可达", async () => {
44
- mockDispatchScript().lifecycle("start").partial("abXdef").deliverFinal({ text: "abXdef" }).lifecycle("end").install();
48
+ mockDispatchScript()
49
+ .lifecycle("start")
50
+ .partial("abXdef")
51
+ .deliverFinal({ text: "abXdef" })
52
+ .lifecycle("end")
53
+ .install();
45
54
  const app = createAppSimulator({ token: "test-token" });
46
55
  await app.connectSSE();
47
56
  await app.sendMessage({ text: "hi", sessionKey: "s1" });
@@ -1,7 +1,11 @@
1
1
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
2
2
  import { createAppSimulator } from "../test-support/app-simulator.js";
3
3
  import { mockDispatchScript, resetMockDispatch } from "../test-support/mock-dispatch.js";
4
- import { createTempHistoryDir, removeTempHistoryDir, setMockRuntime } from "../test-support/mock-runtime.js";
4
+ import {
5
+ createTempHistoryDir,
6
+ removeTempHistoryDir,
7
+ setMockRuntime,
8
+ } from "../test-support/mock-runtime.js";
5
9
 
6
10
  describe("e2e slash commands", () => {
7
11
  let historyDir = "";
@@ -1,6 +1,10 @@
1
1
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
2
  import { createAppSimulator } from "../test-support/app-simulator.js";
3
- import { createTempHistoryDir, removeTempHistoryDir, setMockRuntime } from "../test-support/mock-runtime.js";
3
+ import {
4
+ createTempHistoryDir,
5
+ removeTempHistoryDir,
6
+ setMockRuntime,
7
+ } from "../test-support/mock-runtime.js";
4
8
  import { registerFridayNextHttpRoutes } from "../http/server.js";
5
9
 
6
10
  describe("e2e status cors auth", () => {
@@ -22,7 +26,12 @@ describe("e2e status cors auth", () => {
22
26
  });
23
27
 
24
28
  it("CORS 预检", async () => {
25
- setMockRuntime({ historyDir, authToken: "test-token", corsEnabled: true, allowOrigin: "https://app.example" });
29
+ setMockRuntime({
30
+ historyDir,
31
+ authToken: "test-token",
32
+ corsEnabled: true,
33
+ allowOrigin: "https://app.example",
34
+ });
26
35
  const app = createAppSimulator({ token: "test-token" });
27
36
  const res = await app.options("/friday-next/events", "https://app.example");
28
37
  expect(res.status).toBe(204);
@@ -12,10 +12,7 @@ import {
12
12
  ensureSubagentFromSpawnTool,
13
13
  resetForTest as resetSubagentRegistry,
14
14
  } from "../agent/subagent-registry.js";
15
- import {
16
- forwardAgentEventRaw,
17
- registerFridaySessionDeviceMapping,
18
- } from "../friday-session.js";
15
+ import { forwardAgentEventRaw, registerFridaySessionDeviceMapping } from "../friday-session.js";
19
16
  import { sseEmitter } from "../sse/emitter.js";
20
17
 
21
18
  const deviceId = "IOS-DEVICE-AAAA-BBBB-CCCC-DDDD";
@@ -37,17 +34,22 @@ function describeFrame(frame: { event?: string; data?: Record<string, unknown> }
37
34
  case "connected":
38
35
  return `connected deviceId=${data.deviceId} lastSeq=${data.lastSeq}`;
39
36
  case "subagent": {
40
- const extra = data.phase === "ended" ? ` outcome=${data.outcome} error=${data.error ?? "-"}` : "";
37
+ const extra =
38
+ data.phase === "ended" ? ` outcome=${data.outcome} error=${data.error ?? "-"}` : "";
41
39
  return `subagent phase=${data.phase} runId=${data.runId ?? "(pending)"} label=${data.label ?? "-"} parentRunId=${data.parentRunId ?? "-"} depth=${data.depth}${extra}`;
42
40
  }
43
41
  case "agent": {
44
42
  const sub = data.subagent as Record<string, unknown> | undefined;
45
- const subTag = sub ? ` SUB="agent:${sub.label ?? "?"}" depth=${sub.depth} parent=${sub.parentRunId ?? "-"}` : " (main)";
43
+ const subTag = sub
44
+ ? ` SUB="agent:${sub.label ?? "?"}" depth=${sub.depth} parent=${sub.parentRunId ?? "-"}`
45
+ : " (main)";
46
46
  const inner = (data.data ?? {}) as Record<string, unknown>;
47
47
  let detail = "";
48
- if (data.stream === "thinking") detail = ` thinking: "${String(inner.text ?? "").slice(0, 50)}"`;
48
+ if (data.stream === "thinking")
49
+ detail = ` thinking: "${String(inner.text ?? "").slice(0, 50)}"`;
49
50
  else if (data.stream === "tool") detail = ` tool=${inner.name ?? "?"} phase=${inner.phase}`;
50
- else if (data.stream === "assistant") detail = ` text="${String(inner.text ?? inner.phase ?? "")}"`;
51
+ else if (data.stream === "assistant")
52
+ detail = ` text="${String(inner.text ?? inner.phase ?? "")}"`;
51
53
  else if (data.stream === "lifecycle") detail = ` phase=${inner.phase}`;
52
54
  return `agent stream=${data.stream} runId=${String(data.runId).slice(0, 30)}…${subTag}${detail}`;
53
55
  }
@@ -72,8 +74,11 @@ describe("subagent smoke", () => {
72
74
 
73
75
  // ── Main run lifecycle start ──────────────────────────
74
76
  forwardAgentEventRaw({
75
- runId: mainRunId, seq: 1, stream: "lifecycle",
76
- sessionKey: mainSessionKey, data: { phase: "start" },
77
+ runId: mainRunId,
78
+ seq: 1,
79
+ stream: "lifecycle",
80
+ sessionKey: mainSessionKey,
81
+ data: { phase: "start" },
77
82
  });
78
83
 
79
84
  // ── Subagent A: tool.start (spawning) ─────────────────
@@ -82,10 +87,13 @@ describe("subagent smoke", () => {
82
87
  const compoundA = `announce:v1:${childKeyA}:${bareA}`;
83
88
 
84
89
  forwardAgentEventRaw({
85
- runId: mainRunId, seq: 2, stream: "tool",
90
+ runId: mainRunId,
91
+ seq: 2,
92
+ stream: "tool",
86
93
  sessionKey: mainSessionKey,
87
94
  data: {
88
- phase: "start", name: "sessions_spawn",
95
+ phase: "start",
96
+ name: "sessions_spawn",
89
97
  toolCallId: "call_a",
90
98
  args: { taskName: "code-reviewer" },
91
99
  },
@@ -93,10 +101,14 @@ describe("subagent smoke", () => {
93
101
 
94
102
  // ── Subagent A: tool.result (spawned) ─────────────────
95
103
  forwardAgentEventRaw({
96
- runId: mainRunId, seq: 3, stream: "tool",
104
+ runId: mainRunId,
105
+ seq: 3,
106
+ stream: "tool",
97
107
  sessionKey: mainSessionKey,
98
108
  data: {
99
- phase: "result", name: "sessions_spawn", toolCallId: "call_1",
109
+ phase: "result",
110
+ name: "sessions_spawn",
111
+ toolCallId: "call_1",
100
112
  meta: "code-reviewer",
101
113
  result: { details: spawnResult(childKeyA, bareA, "code-reviewer") },
102
114
  },
@@ -105,26 +117,41 @@ describe("subagent smoke", () => {
105
117
 
106
118
  // ── Subagent A: agent events (subagent's own sessionKey) ─
107
119
  forwardAgentEventRaw({
108
- runId: compoundA, seq: 1, stream: "lifecycle",
109
- data: { phase: "start" }, sessionKey: childKeyA,
120
+ runId: compoundA,
121
+ seq: 1,
122
+ stream: "lifecycle",
123
+ data: { phase: "start" },
124
+ sessionKey: childKeyA,
110
125
  });
111
126
  forwardAgentEventRaw({
112
- runId: compoundA, seq: 2, stream: "thinking",
113
- data: { text: "Let me review the code for issues…", delta: "Let me review the code for issues…", reasoningPrefixChars: 0 },
127
+ runId: compoundA,
128
+ seq: 2,
129
+ stream: "thinking",
130
+ data: {
131
+ text: "Let me review the code for issues…",
132
+ delta: "Let me review the code for issues…",
133
+ reasoningPrefixChars: 0,
134
+ },
114
135
  sessionKey: childKeyA,
115
136
  });
116
137
  forwardAgentEventRaw({
117
- runId: compoundA, seq: 3, stream: "tool",
138
+ runId: compoundA,
139
+ seq: 3,
140
+ stream: "tool",
118
141
  data: { phase: "start", name: "read", args: { path: "src/app.ts" } },
119
142
  sessionKey: childKeyA,
120
143
  });
121
144
  forwardAgentEventRaw({
122
- runId: compoundA, seq: 4, stream: "tool",
145
+ runId: compoundA,
146
+ seq: 4,
147
+ stream: "tool",
123
148
  data: { phase: "result", name: "read", result: "file contents…" },
124
149
  sessionKey: childKeyA,
125
150
  });
126
151
  forwardAgentEventRaw({
127
- runId: compoundA, seq: 5, stream: "assistant",
152
+ runId: compoundA,
153
+ seq: 5,
154
+ stream: "assistant",
128
155
  data: { phase: "delta", text: "Found 3 issues in the code." },
129
156
  sessionKey: childKeyA,
130
157
  });
@@ -135,20 +162,27 @@ describe("subagent smoke", () => {
135
162
  const compoundB = `announce:v1:${childKeyB}:${bareB}`;
136
163
 
137
164
  forwardAgentEventRaw({
138
- runId: compoundA, seq: 7, stream: "tool",
165
+ runId: compoundA,
166
+ seq: 7,
167
+ stream: "tool",
139
168
  sessionKey: mainSessionKey,
140
169
  data: {
141
- phase: "start", name: "sessions_spawn",
170
+ phase: "start",
171
+ name: "sessions_spawn",
142
172
  toolCallId: "call_b",
143
173
  args: { taskName: "lint" },
144
174
  },
145
175
  });
146
176
 
147
177
  forwardAgentEventRaw({
148
- runId: compoundA, seq: 8, stream: "tool",
178
+ runId: compoundA,
179
+ seq: 8,
180
+ stream: "tool",
149
181
  sessionKey: mainSessionKey,
150
182
  data: {
151
- phase: "result", name: "sessions_spawn", toolCallId: "call_2",
183
+ phase: "result",
184
+ name: "sessions_spawn",
185
+ toolCallId: "call_2",
152
186
  meta: "lint",
153
187
  result: { details: spawnResult(childKeyB, bareB, "lint") },
154
188
  },
@@ -157,21 +191,27 @@ describe("subagent smoke", () => {
157
191
 
158
192
  // ── Subagent B: agent event (subagent's own sessionKey) ─
159
193
  forwardAgentEventRaw({
160
- runId: compoundB, seq: 1, stream: "assistant",
194
+ runId: compoundB,
195
+ seq: 1,
196
+ stream: "assistant",
161
197
  data: { phase: "delta", text: "No lint errors found." },
162
198
  sessionKey: childKeyB,
163
199
  });
164
200
 
165
201
  // ── Subagent B ended (subagent's own sessionKey) ──────
166
202
  forwardAgentEventRaw({
167
- runId: compoundB, seq: 2, stream: "lifecycle",
203
+ runId: compoundB,
204
+ seq: 2,
205
+ stream: "lifecycle",
168
206
  data: { phase: "end" },
169
207
  sessionKey: childKeyB,
170
208
  });
171
209
 
172
210
  // ── Subagent A ended (subagent's own sessionKey) ──────
173
211
  forwardAgentEventRaw({
174
- runId: compoundA, seq: 7, stream: "lifecycle",
212
+ runId: compoundA,
213
+ seq: 7,
214
+ stream: "lifecycle",
175
215
  data: { phase: "end" },
176
216
  sessionKey: childKeyA,
177
217
  });