@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.
- package/dist/index.js +1 -1
- package/dist/src/agent/dispatch-bridge.d.ts +1 -1
- package/dist/src/agent/node-pairing-bridge.d.ts +11 -8
- package/dist/src/agent/node-pairing-bridge.js +6 -2
- package/dist/src/agent/subagent-registry.js +0 -3
- package/dist/src/channel-actions.js +3 -1
- package/dist/src/channel.js +0 -2
- package/dist/src/collect-message-media-paths.js +10 -1
- package/dist/src/friday-session.js +34 -10
- package/dist/src/history/normalize-message.js +22 -8
- package/dist/src/http/handlers/agent-config.js +10 -4
- package/dist/src/http/handlers/cancel.js +4 -2
- package/dist/src/http/handlers/device-approve.js +3 -1
- package/dist/src/http/handlers/files-download.js +6 -8
- package/dist/src/http/handlers/files.js +1 -1
- package/dist/src/http/handlers/health.js +18 -4
- package/dist/src/http/handlers/history-messages.js +1 -1
- package/dist/src/http/handlers/history-sessions.js +5 -3
- package/dist/src/http/handlers/messages.js +25 -11
- package/dist/src/http/handlers/models-list.js +1 -1
- package/dist/src/http/handlers/nodes-approve.js +1 -6
- package/dist/src/http/handlers/plugin-info.js +1 -1
- package/dist/src/http/server.js +4 -2
- package/dist/src/link-preview/og-parse.js +3 -1
- package/dist/src/plugin-install-info.js +4 -1
- package/dist/src/session/session-manager.js +9 -3
- package/dist/src/session-usage-store.js +3 -1
- package/dist/src/skills-discovery.d.ts +5 -4
- package/dist/src/skills-discovery.js +27 -22
- package/dist/src/sse/offline-queue.js +4 -1
- package/dist/src/tool-catalog.js +2 -3
- package/dist/src/upgrade-runtime.d.ts +1 -1
- package/dist/src/version.js +3 -1
- package/index.ts +43 -35
- package/install.js +131 -43
- package/package.json +10 -1
- package/src/agent/abort-run.ts +2 -3
- package/src/agent/dispatch-bridge.ts +2 -1
- package/src/agent/media-bridge.ts +9 -2
- package/src/agent/node-pairing-bridge.ts +29 -15
- package/src/agent/run-usage-accumulator.ts +4 -2
- package/src/agent/subagent-registry.ts +0 -4
- package/src/agent-run-context-bridge.ts +3 -1
- package/src/channel-actions.test.ts +10 -4
- package/src/channel-actions.ts +3 -1
- package/src/channel.outbound.test.ts +18 -4
- package/src/channel.ts +121 -123
- package/src/collect-message-media-paths.ts +15 -6
- package/src/config.ts +1 -4
- package/src/e2e/agents-list.e2e.test.ts +9 -2
- package/src/e2e/attachments-inbound.e2e.test.ts +5 -1
- package/src/e2e/attachments-outbound.e2e.test.ts +7 -2
- package/src/e2e/auto-approve.integration.test.ts +13 -7
- package/src/e2e/cancel-reconnect-errors.e2e.test.ts +18 -3
- package/src/e2e/connect-and-connected.e2e.test.ts +5 -1
- package/src/e2e/offline-replay.e2e.test.ts +17 -3
- package/src/e2e/send-text.e2e.test.ts +11 -2
- package/src/e2e/slash-commands.e2e.test.ts +5 -1
- package/src/e2e/status-cors-auth.e2e.test.ts +11 -2
- package/src/e2e/subagent-smoke.e2e.test.ts +68 -28
- package/src/e2e/subagent.e2e.test.ts +136 -53
- package/src/e2e/tool-lifecycle.e2e.test.ts +5 -1
- package/src/friday-session.forward-agent.test.ts +44 -12
- package/src/friday-session.ts +44 -20
- package/src/history/normalize-message.test.ts +35 -8
- package/src/history/normalize-message.ts +24 -12
- package/src/history/read-transcript.ts +1 -4
- package/src/http/handlers/agent-config.test.ts +10 -3
- package/src/http/handlers/agent-config.ts +22 -8
- package/src/http/handlers/agents-list.test.ts +1 -5
- package/src/http/handlers/cancel.test.ts +12 -3
- package/src/http/handlers/cancel.ts +4 -2
- package/src/http/handlers/device-approve.test.ts +12 -3
- package/src/http/handlers/device-approve.ts +33 -21
- package/src/http/handlers/files-download.ts +17 -13
- package/src/http/handlers/files.test.ts +8 -2
- package/src/http/handlers/files.ts +21 -7
- package/src/http/handlers/health.test.ts +43 -11
- package/src/http/handlers/health.ts +22 -6
- package/src/http/handlers/history-messages.test.ts +51 -9
- package/src/http/handlers/history-messages.ts +4 -1
- package/src/http/handlers/history-sessions.test.ts +46 -9
- package/src/http/handlers/history-sessions.ts +5 -3
- package/src/http/handlers/history-set-title.test.ts +14 -5
- package/src/http/handlers/link-preview.test.ts +57 -16
- package/src/http/handlers/link-preview.ts +4 -1
- package/src/http/handlers/messages.test.ts +12 -8
- package/src/http/handlers/messages.ts +57 -19
- package/src/http/handlers/models-list.ts +14 -8
- package/src/http/handlers/nodes-approve.test.ts +15 -4
- package/src/http/handlers/nodes-approve.ts +38 -40
- package/src/http/handlers/plugin-info.ts +5 -6
- package/src/http/handlers/plugin-upgrade.ts +4 -1
- package/src/http/handlers/sse.ts +3 -1
- package/src/http/server.ts +9 -6
- package/src/link-preview/og-parse.test.ts +6 -2
- package/src/link-preview/og-parse.ts +10 -3
- package/src/link-preview/preview-service.ts +4 -1
- package/src/link-preview/ssrf-guard.test.ts +72 -15
- package/src/link-preview/ssrf-guard.ts +2 -1
- package/src/media-fetch.test.ts +7 -2
- package/src/media-fetch.ts +1 -2
- package/src/openclaw.d.ts +16 -9
- package/src/plugin-install-info.ts +20 -9
- package/src/run-metadata.ts +2 -1
- package/src/session/session-manager.ts +19 -11
- package/src/session-usage-snapshot.ts +3 -1
- package/src/session-usage-store.ts +3 -1
- package/src/skills-discovery.test.ts +14 -10
- package/src/skills-discovery.ts +43 -27
- package/src/sse/emitter.test.ts +1 -1
- package/src/sse/emitter.ts +9 -3
- package/src/sse/offline-queue.ts +17 -8
- package/src/test-support/app-simulator.ts +17 -3
- package/src/test-support/mock-dispatch.ts +17 -4
- package/src/thinking-levels.ts +3 -1
- package/src/tool-catalog.ts +16 -7
- package/src/upgrade-runtime.ts +4 -2
- package/src/version.ts +5 -1
- 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 {
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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 {
|
|
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()
|
|
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()
|
|
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 {
|
|
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 {
|
|
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()
|
|
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()
|
|
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 {
|
|
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()
|
|
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 {
|
|
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 {
|
|
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({
|
|
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 =
|
|
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
|
|
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")
|
|
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")
|
|
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,
|
|
76
|
-
|
|
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,
|
|
90
|
+
runId: mainRunId,
|
|
91
|
+
seq: 2,
|
|
92
|
+
stream: "tool",
|
|
86
93
|
sessionKey: mainSessionKey,
|
|
87
94
|
data: {
|
|
88
|
-
phase: "start",
|
|
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,
|
|
104
|
+
runId: mainRunId,
|
|
105
|
+
seq: 3,
|
|
106
|
+
stream: "tool",
|
|
97
107
|
sessionKey: mainSessionKey,
|
|
98
108
|
data: {
|
|
99
|
-
phase: "result",
|
|
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,
|
|
109
|
-
|
|
120
|
+
runId: compoundA,
|
|
121
|
+
seq: 1,
|
|
122
|
+
stream: "lifecycle",
|
|
123
|
+
data: { phase: "start" },
|
|
124
|
+
sessionKey: childKeyA,
|
|
110
125
|
});
|
|
111
126
|
forwardAgentEventRaw({
|
|
112
|
-
runId: compoundA,
|
|
113
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
165
|
+
runId: compoundA,
|
|
166
|
+
seq: 7,
|
|
167
|
+
stream: "tool",
|
|
139
168
|
sessionKey: mainSessionKey,
|
|
140
169
|
data: {
|
|
141
|
-
phase: "start",
|
|
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,
|
|
178
|
+
runId: compoundA,
|
|
179
|
+
seq: 8,
|
|
180
|
+
stream: "tool",
|
|
149
181
|
sessionKey: mainSessionKey,
|
|
150
182
|
data: {
|
|
151
|
-
phase: "result",
|
|
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,
|
|
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,
|
|
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,
|
|
212
|
+
runId: compoundA,
|
|
213
|
+
seq: 7,
|
|
214
|
+
stream: "lifecycle",
|
|
175
215
|
data: { phase: "end" },
|
|
176
216
|
sessionKey: childKeyA,
|
|
177
217
|
});
|