@zhushanwen/subagent-core 0.8.0 → 0.9.0
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/{chunk-OL5BK4VN.js → chunk-LZJOEDGQ.js} +455 -203
- package/dist/engine-discovery-scan-Da4Dkzsi.d.cts +818 -0
- package/dist/engine-discovery-scan-Da4Dkzsi.d.ts +818 -0
- package/dist/execution/engine/engine-discovery-scan.cjs +216 -155
- package/dist/execution/engine/engine-discovery-scan.d.cts +1 -2
- package/dist/execution/engine/engine-discovery-scan.d.ts +1 -2
- package/dist/execution/engine/engine-discovery-scan.js +1 -1
- package/dist/execution/relay-env.js +5 -4
- package/dist/index.cjs +7686 -5894
- package/dist/index.d.cts +1876 -1360
- package/dist/index.d.ts +1876 -1360
- package/dist/index.js +7048 -5493
- package/dist.bundle/index.cjs +7747 -5957
- package/package.json +4 -4
- package/src/__tests__/manifest-store.test.ts +16 -15
- package/src/__tests__/robustness-medium-batch1.test.ts +10 -14
- package/src/__tests__/robustness-medium-batch2.test.ts +7 -2
- package/src/execution/__tests__/alive-store.test.ts +117 -1
- package/src/execution/__tests__/batch-finalized.test.ts +206 -0
- package/src/execution/__tests__/chat-engine-routing.test.ts +117 -1
- package/src/execution/__tests__/{cold-resurrect.test.ts → cold-lookup.test.ts} +133 -33
- package/src/execution/__tests__/collect-coordinator-service.test.ts +7 -5
- package/src/execution/__tests__/conversation-continuation.test.ts +1452 -0
- package/src/execution/__tests__/delivery-methods.test.ts +90 -176
- package/src/execution/__tests__/dialog-queue.test.ts +70 -3
- package/src/execution/__tests__/dispose-manifest-recovery.test.ts +475 -0
- package/src/execution/__tests__/execute-and-await-worktree.test.ts +3 -3
- package/src/execution/__tests__/execute-nesting.test.ts +8 -5
- package/src/execution/__tests__/execution-record.test.ts +8 -148
- package/src/execution/__tests__/finalize-record.test.ts +295 -129
- package/src/execution/__tests__/get-record-for-action-restart.test.ts +21 -29
- package/src/execution/__tests__/helpers/fake-engine-port.ts +13 -68
- package/src/execution/__tests__/helpers/legacy-sidecar.ts +33 -0
- package/src/execution/__tests__/helpers/subagent-service-mocks.ts +9 -5
- package/src/execution/__tests__/idle-gc.test.ts +60 -0
- package/src/execution/__tests__/lifecycle-manager.test.ts +3 -254
- package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +43 -22
- package/src/execution/__tests__/model-config-service.test.ts +111 -0
- package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +10 -6
- package/src/execution/__tests__/notify-ledger.test.ts +42 -1
- package/src/execution/__tests__/rebuild-indexes.test.ts +329 -0
- package/src/execution/__tests__/record-binding.test.ts +613 -0
- package/src/execution/__tests__/record-origin.test.ts +318 -0
- package/src/execution/__tests__/record-store-intent-api.test.ts +600 -0
- package/src/execution/__tests__/record-store-orphan-revive.test.ts +71 -3
- package/src/execution/__tests__/record-store.test.ts +72 -54
- package/src/execution/__tests__/recursive-visibility-baseline.test.ts +8 -5
- package/src/execution/__tests__/round-supervisor-workflow-origin.test.ts +205 -0
- package/src/execution/__tests__/run-orchestration-write-lease.test.ts +293 -0
- package/src/execution/__tests__/run-orchestration.test.ts +199 -0
- package/src/execution/__tests__/session-file-gc.test.ts +35 -4
- package/src/execution/__tests__/session-reconstructor.test.ts +78 -0
- package/src/execution/__tests__/state-marker.test.ts +288 -0
- package/src/execution/__tests__/stream-sink-retirement.test.ts +8 -5
- package/src/execution/__tests__/subagent-actions-core.test.ts +67 -16
- package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +0 -1
- package/src/execution/__tests__/subagent-service-notify-gate.test.ts +27 -11
- package/src/execution/__tests__/subagent-service-parent-guard.test.ts +17 -11
- package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +130 -53
- package/src/execution/__tests__/subagent-service.test.ts +17 -215
- package/src/execution/__tests__/subprocess-agent-runner-no-progress-killall.test.ts +138 -0
- package/src/execution/__tests__/subprocess-agent-runner.test.ts +105 -660
- package/src/execution/__tests__/sync-collect-recovery.test.ts +23 -16
- package/src/execution/__tests__/terminal-write-latency.test.ts +79 -0
- package/src/execution/__tests__/workflow-agent-dispatch.test.ts +782 -0
- package/src/execution/alive-store.ts +37 -28
- package/src/execution/{cold-resurrect.ts → cold-lookup.ts} +48 -45
- package/src/execution/config.ts +1 -1
- package/src/execution/conversation-continuation.ts +541 -0
- package/src/execution/dialog-queue.ts +60 -12
- package/src/execution/engine/__tests__/common/capability-gate.test.ts +1 -1
- package/src/execution/engine/__tests__/common/journal-wiring.test.ts +16 -1
- package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/fake-engine-protocol.mjs +17 -51
- package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/smoke-run.fixture.json +0 -5
- package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +1 -1
- package/src/execution/engine/__tests__/conformance/protocol-blackbox.test.ts +5 -9
- package/src/execution/engine/__tests__/engine-discovery-scan.test.ts +0 -3
- package/src/execution/engine/__tests__/engine-discovery.test.ts +0 -3
- package/src/execution/engine/__tests__/model-prompt.test.ts +0 -3
- package/src/execution/engine/__tests__/registry.test.ts +0 -1
- package/src/execution/engine/__tests__/routing.test.ts +0 -1
- package/src/execution/engine/__tests__/run-failure-worktree-cleanup.test.ts +0 -3
- package/src/execution/engine/client/__tests__/__fixtures__/fake-engine.mjs +0 -3
- package/src/execution/engine/client/__tests__/engine-client-reap.test.ts +212 -0
- package/src/execution/engine/client/__tests__/mirror.test.ts +73 -1
- package/src/execution/engine/client/__tests__/protocol-closure.test.ts +2 -9
- package/src/execution/engine/client/__tests__/remote-engine.test.ts +46 -12
- package/src/execution/engine/client/engine-client.ts +81 -25
- package/src/execution/engine/client/mirror.ts +9 -0
- package/src/execution/engine/client/remote-engine.ts +52 -34
- package/src/execution/engine/client/reverse-router.ts +6 -27
- package/src/execution/engine/common/__tests__/run-signals.test.ts +79 -0
- package/src/execution/engine/common/capability-gate.ts +27 -0
- package/src/execution/engine/common/data-dir.ts +3 -2
- package/src/execution/engine/common/errors.ts +1 -1
- package/src/execution/engine/common/journal-wiring.ts +18 -9
- package/src/execution/engine/common/run-signals.ts +83 -0
- package/src/execution/engine/d8-compat.ts +1 -1
- package/src/execution/engine/host/__tests__/host-bridge.test.ts +3 -18
- package/src/execution/engine/host/host-bridge.ts +19 -41
- package/src/execution/engine/host/pi-host-binding.ts +5 -5
- package/src/execution/engine/port.ts +34 -54
- package/src/execution/engine/types.ts +11 -14
- package/src/execution/execution-record.ts +15 -47
- package/src/execution/finalize-record.ts +94 -197
- package/src/execution/idle-gc.ts +9 -3
- package/src/execution/lifecycle-manager.ts +25 -361
- package/src/execution/lifecycle-predicates.ts +22 -19
- package/src/execution/manifest-store.ts +40 -42
- package/src/execution/model-config-service.ts +1 -1
- package/src/execution/notifier.ts +39 -1
- package/src/execution/notify-host.ts +13 -4
- package/src/execution/record-entry.ts +20 -2
- package/src/execution/record-store.ts +925 -160
- package/src/execution/relay-env.ts +8 -3
- package/src/execution/round-supervisor/domain.ts +17 -5
- package/src/execution/round-supervisor/service-binding.test.ts +89 -53
- package/src/execution/round-supervisor/service-binding.ts +71 -21
- package/src/execution/round-supervisor/supervisor.ts +23 -9
- package/src/execution/service/record-access.ts +547 -0
- package/src/execution/service/record-lifecycle.ts +542 -0
- package/src/execution/service/run-orchestration.ts +1647 -0
- package/src/execution/service/service-bootstrap.ts +111 -0
- package/src/execution/service/service-constants.ts +27 -0
- package/src/execution/service/session-baselines.ts +401 -0
- package/src/execution/service/sync-collect-domain.ts +442 -0
- package/src/execution/service/workflow-dispatch.ts +567 -0
- package/src/execution/session-file-gc.ts +11 -8
- package/src/execution/session-reconstructor.ts +44 -1
- package/src/execution/sessions-index.ts +20 -3
- package/src/execution/settled-watchdog.ts +86 -43
- package/src/execution/state-marker.ts +516 -0
- package/src/execution/stream-sink.ts +1 -1
- package/src/execution/subagent-actions-core.ts +53 -31
- package/src/execution/subagent-service.ts +643 -2980
- package/src/execution/subprocess-agent-runner.ts +46 -323
- package/src/execution/types.ts +60 -43
- package/src/execution/ui-request-handler-factory.ts +5 -0
- package/src/execution/worktree-manager.ts +1 -1
- package/src/execution/worktree-registry.ts +1 -1
- package/src/index.ts +6 -5
- package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +2 -3
- package/src/orchestration/__tests__/agent-call-stream.test.ts +24 -49
- package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +4 -4
- package/src/orchestration/__tests__/file-run-store.test.ts +5 -7
- package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +2 -3
- package/src/orchestration/__tests__/run-snapshot.test.ts +27 -43
- package/src/orchestration/__tests__/worker-message-pump-workflow-dispatch.test.ts +274 -0
- package/src/orchestration/execute-agent-call.ts +1 -1
- package/src/orchestration/file-run-store.ts +1 -1
- package/src/orchestration/models/ports.ts +20 -13
- package/src/orchestration/models/types.ts +11 -13
- package/src/orchestration/run-snapshot.ts +18 -22
- package/src/orchestration/worker-message-pump.ts +33 -57
- package/src/shared/atomic-write.ts +10 -8
- package/dist/chunk-T2SYBW3I.js +0 -24
- package/dist/engine-discovery-scan-BXLA8y-z.d.cts +0 -1624
- package/dist/engine-discovery-scan-BXLA8y-z.d.ts +0 -1624
- package/src/execution/__tests__/chat-round-first-round-watchdog.test.ts +0 -255
- package/src/execution/__tests__/finalized-marker.test.ts +0 -104
- package/src/execution/__tests__/lifecycle-manager-lock.test.ts +0 -211
- package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +0 -488
- package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +0 -75
- package/src/execution/__tests__/tombstone-store.test.ts +0 -73
- package/src/execution/engine/__tests__/conformance/chat-round-protocol.test.ts +0 -230
- package/src/execution/finalized-marker.ts +0 -70
- package/src/execution/tombstone-store.ts +0 -72
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
// src/orchestration/__tests__/worker-message-pump-workflow-dispatch.test.ts
|
|
2
|
+
//
|
|
3
|
+
// [H2 W3] pump 切换 executeWorkflowAgent 端到端单测(设计
|
|
4
|
+
// subagent-workflow-record-unification.md §3.5 终态数据流 / §5 W3)。
|
|
5
|
+
//
|
|
6
|
+
// 真实链路形态:handleWorkerMessage(agent-call) → dispatchAgentCall →
|
|
7
|
+
// deps.workflowAgentDispatch(SubagentService.executeWorkflowAgent 注入形态)→
|
|
8
|
+
// fake pi EnginePort(协议 seam 替身,与 W2 workflow-agent-dispatch.test 同源)。
|
|
9
|
+
//
|
|
10
|
+
// 锁四组面:
|
|
11
|
+
// 1. record 进 store:dispatch 在途时 record 带 origin:"workflow" +
|
|
12
|
+
// parentRunId=run.runId(真实 record 经 store 治理面可见——进度源切换的
|
|
13
|
+
// 数据前提);record 级 pending:register 照旧发射。
|
|
14
|
+
// 2. worker 协议回包不变:引擎应答 → executeAgentCall finalize →
|
|
15
|
+
// postMessage({type:"agent-result", callId, result})(cached:false);
|
|
16
|
+
// trace node 终态摘要保真(node.result 来自 executeWorkflowAgent 的
|
|
17
|
+
// AgentResult 出口,D2「node 保留 result」)。
|
|
18
|
+
// 3. 成功即终态化(D7 联动回归):settle 后 record closed/gc + record 级
|
|
19
|
+
// pending:unregister(archive 出内存,run 视图经磁盘面回查终态)。
|
|
20
|
+
// 4. 注入面回退:未注入 workflowAgentDispatch(旧测试 deps)→ deps.runner.run
|
|
21
|
+
// 被调(SAR 旧编排路径,W4 归位;两路互不串扰)。
|
|
22
|
+
//
|
|
23
|
+
// 替身形态与测试红线对齐 W2 workflow-agent-dispatch.test.ts:XYZ_AGENT_DATA_DIR
|
|
24
|
+
// 指 tmpdir(不触真实数据目录);logger mock 防噪声。
|
|
25
|
+
|
|
26
|
+
import * as fs from "node:fs";
|
|
27
|
+
import * as os from "node:os";
|
|
28
|
+
import * as path from "node:path";
|
|
29
|
+
|
|
30
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
31
|
+
|
|
32
|
+
const { loggerMock } = vi.hoisted(() => ({
|
|
33
|
+
loggerMock: { debug: vi.fn(), warn: vi.fn(), error: vi.fn() },
|
|
34
|
+
}));
|
|
35
|
+
vi.mock("../../core/logger.ts", () => ({ getLogger: () => loggerMock }));
|
|
36
|
+
|
|
37
|
+
import { Budget } from "../models/budget.ts";
|
|
38
|
+
import { RunRuntime } from "../models/run-runtime.ts";
|
|
39
|
+
import { Trace } from "../models/trace.ts";
|
|
40
|
+
import { WorkflowRun } from "../models/workflow-run.ts";
|
|
41
|
+
import type { LifecycleDeps, WorkerHandlers } from "../models/ports.ts";
|
|
42
|
+
import type { AgentCallOpts } from "../models/types.ts";
|
|
43
|
+
import type { WorkerHandle } from "../worker-handle.ts";
|
|
44
|
+
import { handleWorkerMessage } from "../worker-message-pump.ts";
|
|
45
|
+
import { ModelConfigService } from "../../execution/model-config-service.ts";
|
|
46
|
+
import type { ModelInfo, ModelRegistryLike } from "../../execution/model-resolver.ts";
|
|
47
|
+
import type { RecordStore } from "../../execution/record-store.ts";
|
|
48
|
+
import { SubagentService } from "../../execution/subagent-service.ts";
|
|
49
|
+
import type { PiLike } from "../../execution/subagent-service.ts";
|
|
50
|
+
import { clearEngines } from "../../execution/engine/registry.ts";
|
|
51
|
+
import { registerFakePiEngine, type FakePiEnginePort } from "../../execution/__tests__/helpers/fake-engine-port.ts";
|
|
52
|
+
|
|
53
|
+
// ── harness ──────────────────────────────────────────────────
|
|
54
|
+
|
|
55
|
+
function makeEmptyRegistry(): ModelRegistryLike {
|
|
56
|
+
return { getAvailable: () => [], find: () => undefined, hasConfiguredAuth: () => true };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const ctxModel: ModelInfo = { id: "m", name: "M", provider: "p", reasoning: false };
|
|
60
|
+
|
|
61
|
+
interface PumpHarness {
|
|
62
|
+
service: SubagentService;
|
|
63
|
+
store: RecordStore;
|
|
64
|
+
pi: { appendEntry: ReturnType<typeof vi.fn>; events: { emit: ReturnType<typeof vi.fn> }; sendMessage: ReturnType<typeof vi.fn> };
|
|
65
|
+
fake: FakePiEnginePort;
|
|
66
|
+
run: WorkflowRun;
|
|
67
|
+
postMessage: ReturnType<typeof vi.fn>;
|
|
68
|
+
deps: LifecycleDeps;
|
|
69
|
+
tmpRoot: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function makePumpHarness(runId: string): PumpHarness {
|
|
73
|
+
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "wf-pump-it-"));
|
|
74
|
+
process.env.XYZ_AGENT_DATA_DIR = path.join(tmpRoot, "engine-data");
|
|
75
|
+
const agentDir = path.join(tmpRoot, "agent");
|
|
76
|
+
const modelService = new ModelConfigService({ agentDir, cwd: agentDir });
|
|
77
|
+
modelService.initModel({ modelRegistry: makeEmptyRegistry(), sessionId: "wf-pump-it", ctxModel });
|
|
78
|
+
const service = new SubagentService({ cwd: agentDir, modelService });
|
|
79
|
+
const pi = {
|
|
80
|
+
appendEntry: vi.fn(),
|
|
81
|
+
events: { emit: vi.fn() },
|
|
82
|
+
sendMessage: vi.fn(),
|
|
83
|
+
};
|
|
84
|
+
service.initSession({ pi: pi as unknown as PiLike, sessionId: "wf-pump-it" });
|
|
85
|
+
clearEngines();
|
|
86
|
+
const fake = registerFakePiEngine();
|
|
87
|
+
|
|
88
|
+
const run = new WorkflowRun(
|
|
89
|
+
runId,
|
|
90
|
+
{ scriptName: "test-wf", scriptSource: "agent('调研 A')", args: {}, scriptPath: "/tmp/test.js" },
|
|
91
|
+
{ status: "running", budget: new Budget({ maxTokens: 100_000 }), calls: new Map(), trace: new Trace(), errorLogs: [] },
|
|
92
|
+
{ startedAt: new Date().toISOString() },
|
|
93
|
+
);
|
|
94
|
+
const postMessage = vi.fn();
|
|
95
|
+
const initialWorker = { postMessage, terminate: vi.fn(async () => {}) } as unknown as WorkerHandle;
|
|
96
|
+
run.assignRuntime(new RunRuntime(initialWorker, new AbortController()));
|
|
97
|
+
|
|
98
|
+
// 组合根注入形态(extension index.ts makeDeps 的等价内联):dispatch 闭包捕获
|
|
99
|
+
// service 单例,parentRunId 由 pump 侧补 run.runId。
|
|
100
|
+
const deps = {
|
|
101
|
+
store: { save: vi.fn(async () => {}) },
|
|
102
|
+
workerHost: { start: vi.fn() },
|
|
103
|
+
runner: { run: vi.fn(async () => ({ content: "legacy-runner", durationMs: 1, error: undefined, toolCalls: [] })) },
|
|
104
|
+
runs: new Map([[runId, run]]),
|
|
105
|
+
eventBus: { emit: vi.fn() },
|
|
106
|
+
log: vi.fn(),
|
|
107
|
+
workflowAgentDispatch: (opts: AgentCallOpts, parentRunId: string, signal?: AbortSignal) =>
|
|
108
|
+
service.executeWorkflowAgent(opts, parentRunId, signal),
|
|
109
|
+
} as unknown as LifecycleDeps;
|
|
110
|
+
return { service, store: Reflect.get(service, "store") as RecordStore, pi, fake, run, postMessage, deps, tmpRoot };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function makeHandlers(): WorkerHandlers {
|
|
114
|
+
return {
|
|
115
|
+
onMessage: vi.fn(async () => {}),
|
|
116
|
+
onError: vi.fn(async () => {}),
|
|
117
|
+
onExit: vi.fn(async () => {}),
|
|
118
|
+
} as unknown as WorkerHandlers;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** 微任务冲刷(dispatch → 路由 → record 注册 → acquire → engine.run 链)。 */
|
|
122
|
+
async function flush(times = 8): Promise<void> {
|
|
123
|
+
for (let i = 0; i < times; i++) {
|
|
124
|
+
await new Promise<void>((resolve) => setImmediate(resolve));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function findAgentResultPost(postMessage: ReturnType<typeof vi.fn>, callId: number) {
|
|
129
|
+
return postMessage.mock.calls
|
|
130
|
+
.map((c) => c[0] as { type?: string; callId?: number; result?: { content?: string; error?: string }; cached?: boolean })
|
|
131
|
+
.find((m) => m.type === "agent-result" && m.callId === callId);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
let prevDataDirEnv: string | undefined;
|
|
135
|
+
|
|
136
|
+
beforeEach(() => {
|
|
137
|
+
prevDataDirEnv = process.env["XYZ_AGENT_DATA_DIR"];
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
afterEach(() => {
|
|
141
|
+
vi.restoreAllMocks();
|
|
142
|
+
if (prevDataDirEnv === undefined) delete process.env["XYZ_AGENT_DATA_DIR"];
|
|
143
|
+
else process.env["XYZ_AGENT_DATA_DIR"] = prevDataDirEnv;
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// ── 1+2+3:注入 dispatch 的端到端主路径 ───────────────────────
|
|
147
|
+
|
|
148
|
+
describe("pump → executeWorkflowAgent 端到端", () => {
|
|
149
|
+
it("record 进 store(origin=workflow + parentRunId)→ settle → 回包/终态摘要/D7 终态化全链", async () => {
|
|
150
|
+
const h = makePumpHarness("wf-pump-e2e-1");
|
|
151
|
+
await handleWorkerMessage(
|
|
152
|
+
h.run,
|
|
153
|
+
{ type: "agent-call", callId: 1, opts: { prompt: "调研 A", description: "research-a" } },
|
|
154
|
+
h.deps,
|
|
155
|
+
makeHandlers(),
|
|
156
|
+
);
|
|
157
|
+
await flush();
|
|
158
|
+
|
|
159
|
+
// 1. dispatch 在途:真实 record 在 store(origin/parentRunId——views store 订阅的数据
|
|
160
|
+
// 前提)。经 views 同款查询面(queries.collectRecordsByParentRunId)断言命中,
|
|
161
|
+
// 再经 getMutable 拿真 record 验 originFields(listRunning 是 RecordSnapshot
|
|
162
|
+
// 轻投影,无 origin 字段——勿用)。
|
|
163
|
+
const found = h.service.queries.collectRecordsByParentRunId("wf-pump-e2e-1", 100);
|
|
164
|
+
expect(found).toHaveLength(1);
|
|
165
|
+
expect(found[0]!.origin).toBe("workflow");
|
|
166
|
+
expect(found[0]!.parentRunId).toBe("wf-pump-e2e-1");
|
|
167
|
+
expect(found[0]!.status).toBe("running");
|
|
168
|
+
const record = h.store.getMutable(found[0]!.id)!;
|
|
169
|
+
expect(record.origin).toBe("workflow");
|
|
170
|
+
expect(record.parentRunId).toBe("wf-pump-e2e-1");
|
|
171
|
+
// 引擎收到 run(taskId = record.id——守护/journal 键,W2 已锁定)
|
|
172
|
+
expect(h.fake.runs).toHaveLength(1);
|
|
173
|
+
// record 级 pending:register 照旧(D5:record 域发射面不动)
|
|
174
|
+
const registered = h.pi.events.emit.mock.calls.find((c) => c[0] === "pending:register");
|
|
175
|
+
expect(registered?.[1]).toMatchObject({ id: record.id, type: "subagent" });
|
|
176
|
+
|
|
177
|
+
// 2. 引擎应答 → worker 协议回包 + trace node 终态摘要
|
|
178
|
+
// (settle 后链路含 journal.close 的真 fs await,用 waitFor 收敛)
|
|
179
|
+
h.fake.runs[0]!.settle({ content: "ok", sessionId: "sess-1", sessionFile: "/tmp/sess-1.jsonl", durationMs: 5 });
|
|
180
|
+
await vi.waitFor(() => {
|
|
181
|
+
expect(findAgentResultPost(h.postMessage, 1)).toBeDefined();
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const posted = findAgentResultPost(h.postMessage, 1);
|
|
185
|
+
expect(posted).toBeDefined();
|
|
186
|
+
expect(posted!.cached).toBe(false);
|
|
187
|
+
expect(posted!.result!.content).toBe("ok");
|
|
188
|
+
expect(posted!.result!.error).toBeUndefined();
|
|
189
|
+
// node 终态摘要(D2:live 删除后 node 保留 result——数据源 = executeWorkflowAgent
|
|
190
|
+
// 的 AgentResult 出口,经 executeAgentCall finalizeCall 写入)
|
|
191
|
+
const node = h.run.state.trace.find(1);
|
|
192
|
+
expect(node?.status).toBe("completed");
|
|
193
|
+
expect(node?.result?.content).toBe("ok");
|
|
194
|
+
expect(node?.result?.sessionFile).toBe("/tmp/sess-1.jsonl");
|
|
195
|
+
expect(node?.completedAt).toBeDefined();
|
|
196
|
+
expect(h.run.state.calls.get(1)?.status).toBe("done");
|
|
197
|
+
|
|
198
|
+
// 3. D7 联动:成功即终态化(closed/gc + archive 出内存)+ record 级 unregister。
|
|
199
|
+
// (archive 后从磁盘重建面回查终态的「run 视图不丢行」行为由 W1 record-store
|
|
200
|
+
// 的 mergedRecords 测试锁定——本 harness 的 pi mock 不落盘,不在此重复。)
|
|
201
|
+
expect(h.store.listRunning()).toHaveLength(0);
|
|
202
|
+
const unregistered = h.pi.events.emit.mock.calls.find((c) => c[0] === "pending:unregister");
|
|
203
|
+
expect(unregistered?.[1]).toMatchObject({ id: record.id });
|
|
204
|
+
|
|
205
|
+
// 4. run 级 pending 配对不回归(D5:lifecycle↔pump 的 run 级注册/注销对零改动
|
|
206
|
+
// ——lifecycle.ts 零 diff 由 git 自查;此处锁 pump 侧 run 终态注销行为):
|
|
207
|
+
// 脚本 return → finalizeRun emit pending:unregister with id=runId。
|
|
208
|
+
await handleWorkerMessage(h.run, { type: "return", result: "done" }, h.deps, makeHandlers());
|
|
209
|
+
const runUnregister = h.deps.eventBus!.emit as ReturnType<typeof vi.fn>;
|
|
210
|
+
const runUnregCall = runUnregister.mock.calls.find((c) => c[0] === "pending:unregister");
|
|
211
|
+
expect(runUnregCall?.[1]).toMatchObject({ id: "wf-pump-e2e-1" });
|
|
212
|
+
expect(h.run.state.status).toBe("done");
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it("失败结果:引擎 error outcome → 回包 error + trace failed 终态摘要", async () => {
|
|
216
|
+
const h = makePumpHarness("wf-pump-e2e-2");
|
|
217
|
+
await handleWorkerMessage(
|
|
218
|
+
h.run,
|
|
219
|
+
{ type: "agent-call", callId: 2, opts: { prompt: "调研 B", description: "research-b" } },
|
|
220
|
+
h.deps,
|
|
221
|
+
makeHandlers(),
|
|
222
|
+
);
|
|
223
|
+
await flush();
|
|
224
|
+
expect(h.fake.runs).toHaveLength(1);
|
|
225
|
+
|
|
226
|
+
// 失败结果带巨额 usage 打爆 run 预算(Budget maxTokens=100k)——executeAgentCall
|
|
227
|
+
// 的「预算超限不重试」分支直接 finalize(否则 error 结果走 1s/2s 退避重试,
|
|
228
|
+
// 测试需 fake timers 推进;重试语义已由 execute-agent-call.test.ts 锁定,此处
|
|
229
|
+
// 聚焦 pump 切换后的回包/终态摘要)。
|
|
230
|
+
h.fake.runs[0]!.settle({ content: "", error: "engine blew up", durationMs: 3, usage: { input: 1_000_000, output: 1, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 1 } });
|
|
231
|
+
await vi.waitFor(() => {
|
|
232
|
+
expect(findAgentResultPost(h.postMessage, 2)).toBeDefined();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const posted = findAgentResultPost(h.postMessage, 2);
|
|
236
|
+
expect(posted).toBeDefined();
|
|
237
|
+
expect(posted!.result!.error).toBe("engine blew up");
|
|
238
|
+
const node = h.run.state.trace.find(2);
|
|
239
|
+
expect(node?.status).toBe("failed");
|
|
240
|
+
expect(node?.result?.error).toBe("engine blew up");
|
|
241
|
+
expect(h.run.state.calls.get(2)?.status).toBe("done");
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// ── 4:注入面回退(旧 deps 不注入 dispatch) ──────────────────
|
|
246
|
+
|
|
247
|
+
describe("workflowAgentDispatch 未注入回退", () => {
|
|
248
|
+
it("旧 deps(仅 runner)→ deps.runner.run 被调([H2 W4] SAR.run 已掏空为纯转调 executeWorkflowAgent,两分支执行体归一;生产恒注入 dispatch 本分支不可达,仅为旧测试 deps 形态兼容)", async () => {
|
|
249
|
+
const run = new WorkflowRun(
|
|
250
|
+
"wf-pump-fallback-1",
|
|
251
|
+
{ scriptName: "test-wf", scriptSource: "agent('hi')", args: {}, scriptPath: "/tmp/test.js" },
|
|
252
|
+
{ status: "running", budget: new Budget(), calls: new Map(), trace: new Trace(), errorLogs: [] },
|
|
253
|
+
{ startedAt: new Date().toISOString() },
|
|
254
|
+
);
|
|
255
|
+
const runnerRun = vi.fn(async () => ({ content: "legacy", durationMs: 1, error: undefined, toolCalls: [] }));
|
|
256
|
+
const postMessage = vi.fn();
|
|
257
|
+
const deps = {
|
|
258
|
+
store: { save: vi.fn(async () => {}) },
|
|
259
|
+
workerHost: { start: vi.fn() },
|
|
260
|
+
runner: { run: runnerRun },
|
|
261
|
+
runs: new Map(),
|
|
262
|
+
eventBus: { emit: vi.fn() },
|
|
263
|
+
log: vi.fn(),
|
|
264
|
+
} as unknown as LifecycleDeps;
|
|
265
|
+
run.assignRuntime(new RunRuntime({ postMessage, terminate: vi.fn(async () => {}) } as unknown as WorkerHandle, new AbortController()));
|
|
266
|
+
|
|
267
|
+
await handleWorkerMessage(run, { type: "agent-call", callId: 7, opts: { prompt: "p" } }, deps, makeHandlers());
|
|
268
|
+
await flush();
|
|
269
|
+
|
|
270
|
+
expect(runnerRun).toHaveBeenCalledTimes(1);
|
|
271
|
+
const posted = findAgentResultPost(postMessage, 7);
|
|
272
|
+
expect(posted?.result?.content).toBe("legacy");
|
|
273
|
+
});
|
|
274
|
+
});
|
|
@@ -127,7 +127,7 @@ function delay(ms: number): Promise<void> {
|
|
|
127
127
|
* @param budget Budget 值对象(consume + isExceeded 检查)
|
|
128
128
|
* @param signal AbortSignal(runner.run 传播;abort 后不重试)
|
|
129
129
|
* @param trace Trace 值对象(finalizeCall 时 update)
|
|
130
|
-
* @param onEvent
|
|
130
|
+
* @param onEvent 透传 service 派发路径(journal 转发 + 守护刷新源)
|
|
131
131
|
* @param stream streaming sink(透传 runner.run)
|
|
132
132
|
* @param isOrphaned 孤儿判定谓词(OB2,可选,默认恒 false):true 时 finalizeCall
|
|
133
133
|
* 跳过 trace.update(判定语义与正确性论证见 finalizeCall 文档注释)。递归重试
|
|
@@ -240,7 +240,7 @@ export class FileRunStore implements RunStore {
|
|
|
240
240
|
// mkdir recursive 每次 save 前执行:幂等零成本(目录已存在时仅一次 stat),
|
|
241
241
|
// 且免「构造时预建」——构造时建会在宿主尚未 configureCore 的窗口抛错。
|
|
242
242
|
await mkdir(this.stateDir(), { recursive: true });
|
|
243
|
-
// toRunSnapshot 补 v 字段(D4
|
|
243
|
+
// toRunSnapshot 补 v 字段(D4 裁决②写入侧);live strip 已随 [H2 W3] live 字段删除退役
|
|
244
244
|
const line = JSON.stringify(toRunSnapshot(run));
|
|
245
245
|
await appendFile(this.stateFilePath(run.runId), line + "\n", "utf8");
|
|
246
246
|
if (isTerminal) {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* 层归属:Engine。零 infra 依赖(AC-1)。
|
|
12
12
|
*/
|
|
13
|
-
import type {
|
|
13
|
+
import type { SubagentStream } from "../../execution/stream-sink.ts";
|
|
14
14
|
import type { AgentEvent } from "../../shared/agent-event.ts";
|
|
15
15
|
import type { WorkerHandle } from "../worker-handle.ts";
|
|
16
16
|
import type { RunSpec } from "./run-spec.ts";
|
|
@@ -22,11 +22,11 @@ import type { WorkflowRun } from "./workflow-run.ts";
|
|
|
22
22
|
/**
|
|
23
23
|
* Agent 子进程执行 port。Infra 实现:SubprocessAgentRunner。
|
|
24
24
|
*
|
|
25
|
-
* run 执行单次 agent 调用(委托 SubagentService.
|
|
26
|
-
* signal 用于 abort 传播。
|
|
25
|
+
* run 执行单次 agent 调用(委托 SubagentService.executeWorkflowAgent,[H2 W4] 纯转调),
|
|
26
|
+
* 返回结构化结果。signal 用于 abort 传播。
|
|
27
27
|
*
|
|
28
|
-
* onEvent(可选):强类型 AgentEvent
|
|
29
|
-
*
|
|
28
|
+
* onEvent(可选):强类型 AgentEvent 回调,透传 service 派发路径(journal 转发/守护
|
|
29
|
+
* 刷新源),生产 pump 侧恒 undefined。
|
|
30
30
|
*
|
|
31
31
|
* D-005: onEvent 签名从 raw Record<string,unknown> 升级为 AgentEvent——委托后不再有
|
|
32
32
|
* raw JSONL 中间层(executeAndAwait 直接出 AgentEvent,session-runner handleSdkEvent 出口)。
|
|
@@ -153,12 +153,19 @@ export interface LifecycleDeps {
|
|
|
153
153
|
parentRun: WorkflowRun,
|
|
154
154
|
) => Promise<unknown>;
|
|
155
155
|
/**
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
156
|
+
* [H2 W3] workflow 域 agent() 统一派发入口(SubagentService.executeWorkflowAgent 的
|
|
157
|
+
* deps 注入形态,设计 §3.5 终态数据流)。窄函数类型——不引 execution 层具体类,
|
|
158
|
+
* 保持本 ports 文件零 infra/execution 依赖。由组合根(extension index.ts makeDeps)
|
|
159
|
+
* 注入:闭包捕获 getSubagentService() 单例,parentRunId 由 pump 侧补 run.runId。
|
|
160
|
+
*
|
|
161
|
+
* 可选——未注入时(旧测试 deps)dispatchAgentCall 回退 deps.runner([H2 W4] 后
|
|
162
|
+
* SAR 已掏空为纯转调 executeWorkflowAgent,两分支执行体归一非双轨,差异仅 parentRunId
|
|
163
|
+
* 来源:注入 = 真实 run.runId,回退 = SAR_UNATTACHED_PARENT_RUN_ID 占位;生产装配
|
|
164
|
+
* 两字段同时注入,dispatch 恒优先)。
|
|
165
|
+
*/
|
|
166
|
+
workflowAgentDispatch?: (
|
|
167
|
+
opts: AgentCallOpts,
|
|
168
|
+
parentRunId: string,
|
|
169
|
+
signal?: AbortSignal,
|
|
170
|
+
) => Promise<AgentResult>;
|
|
164
171
|
}
|
|
@@ -20,7 +20,7 @@ import type {
|
|
|
20
20
|
ToolCallEntry,
|
|
21
21
|
} from "@zhushanwen/subagent-engine-sdk";
|
|
22
22
|
|
|
23
|
-
import type {
|
|
23
|
+
import type { WorktreeHandle } from "../../execution/types.ts";
|
|
24
24
|
|
|
25
25
|
// ── 状态机 ────────────────────────────────────────────────────
|
|
26
26
|
|
|
@@ -76,7 +76,8 @@ export function canRunTransition(from: RunStatus, to: RunStatus): boolean {
|
|
|
76
76
|
* 历史值 20 偏紧——描述性 slug 如 "audit-structured-output"(23)/ "fix-subagent-wf-tools"(21)
|
|
77
77
|
* 会撞上限,放宽到 35 兼顾「短到能塞进 TUI 标题行」与「容纳合理描述性 kebab-case 名」。
|
|
78
78
|
* 放本文件的原因:约束对象是 AgentCallOpts.description(slug 的源字段,见下方 slug 派生说明),
|
|
79
|
-
* 与字段同文件;
|
|
79
|
+
* 与字段同文件;subagent-actions-core(slug 校验)、subagent-service(record slug 截断)
|
|
80
|
+
* 与壳侧 tool schema maxLength 共享引用。
|
|
80
81
|
*/
|
|
81
82
|
export const SLUG_MAX_LENGTH = 35;
|
|
82
83
|
|
|
@@ -351,20 +352,17 @@ export interface ExecutionTraceNode {
|
|
|
351
352
|
*/
|
|
352
353
|
sessionId?: string;
|
|
353
354
|
/**
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
355
|
+
* Session JSONL 绝对路径。finalizeCall 从 result.sessionFile 透传。
|
|
356
|
+
* 持久化到快照(serializeRun),跨 session 重水合后保留。
|
|
357
|
+
*/
|
|
357
358
|
sessionFile?: string;
|
|
358
|
-
/**
|
|
359
|
-
* Live 执行进度对象(running 时存在,done 时由 dispatchAgentCall 清除)。
|
|
360
|
-
*
|
|
361
|
-
* 挂在 node 上(D-10 单源延伸:AgentCall.traceNode 与 Trace.nodes 共享同一引用)。
|
|
362
|
-
* TUI 通过 trace.toArray() 读 node.live,派生 getEventLog/getCurrentActivity 实时展示。
|
|
363
|
-
* 不持久化(序列化时 strip;重跑时由 dispatchAgentCall 重建)。
|
|
364
|
-
*/
|
|
365
|
-
live?: ExecutionRecord;
|
|
366
359
|
}
|
|
367
360
|
|
|
361
|
+
// [H2 W3] `live?: ExecutionRecord` 字段已删除(设计 subagent-workflow-record-
|
|
362
|
+
// unification.md D2:trace node 的 live 字段删除,只保留 result 终态摘要)。运行期
|
|
363
|
+
// 实时进度改由 views 经 store 查询 collectRecordsByParentRunId(parentRunId) 订阅
|
|
364
|
+
// 真实 record(origin:"workflow"),不再经 pump 旁路 progress record 挂载。
|
|
365
|
+
|
|
368
366
|
/**
|
|
369
367
|
* Trace.update 用的 patch(字段全可选)。
|
|
370
368
|
*
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
//
|
|
5
5
|
// 为什么需要它:WorkflowRun 的 JSONL 快照投影此前两宿主各写一份(core
|
|
6
6
|
// file-run-store.ts 的 toSnapshot/fromSnapshot 与 pi 壳 jsonl-run-store.ts 的
|
|
7
|
-
// serializeRun/deserializeRun
|
|
8
|
-
//
|
|
7
|
+
// serializeRun/deserializeRun)——字段集一致但语义细节分叉(版本 guard、
|
|
8
|
+
// budgetRef 剔除),修一处漏一处。本模块收敛为单源 codec:字段演进
|
|
9
9
|
// 单点(G2),两宿主(FileRunStore / pi JsonlRunStore)各自只保留 IO 策略
|
|
10
10
|
// (rewrite/append/去抖),投影与版本衔接语义全部经此模块。
|
|
11
11
|
//
|
|
@@ -103,10 +103,9 @@ export interface RunSnapshot {
|
|
|
103
103
|
* WorkflowRun → 单行快照。
|
|
104
104
|
*
|
|
105
105
|
* - 补 v 字段(D4 裁决②:写入恒带当前版本)。
|
|
106
|
-
* -
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
* 中的 run(save 后 run 可继续跑)。
|
|
106
|
+
* - trace 节点全量序列化([H2 W3] live strip 分支随 ExecutionTraceNode.live 字段
|
|
107
|
+
* 删除而退役——节点不再携带运行期对象,无需防御性剥离;旧快照经 fromRunSnapshot
|
|
108
|
+
* 重水合时多余键自然丢弃,存量行零迁移)。
|
|
110
109
|
* - spec.budgetRef 剔除:父 Budget 共享引用是进程内优化(嵌套 workflow 预算
|
|
111
110
|
* 共享),非持久化数据;Budget 实例若混入 spec 落盘将退化为普通对象投影
|
|
112
111
|
* (重水合后类型不符的脏字段)——重水合后 budget 从 state.budget 独立重建,
|
|
@@ -131,22 +130,19 @@ export function toRunSnapshot(run: WorkflowRun): RunSnapshot {
|
|
|
131
130
|
usedCost: run.state.budget.usedCost,
|
|
132
131
|
totalCallCount: run.state.budget.totalCallCount,
|
|
133
132
|
},
|
|
134
|
-
calls: Array.from(run.state.calls.values(), (c) => {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}),
|
|
148
|
-
// trace 节点浅拷贝时 strip live 字段
|
|
149
|
-
trace: run.state.trace.toArray().map(({ live: _live, ...rest }) => rest),
|
|
133
|
+
calls: Array.from(run.state.calls.values(), (c) => ({
|
|
134
|
+
id: c.id,
|
|
135
|
+
opts: c.opts,
|
|
136
|
+
status: c.status,
|
|
137
|
+
attempts: c.attempts,
|
|
138
|
+
result: c.result,
|
|
139
|
+
sessionId: c.sessionId,
|
|
140
|
+
sessionFile: c.sessionFile,
|
|
141
|
+
traceNode: c.traceNode,
|
|
142
|
+
})),
|
|
143
|
+
// trace 节点全量拷贝序列化(toArray 返回内部数组只读引用,拷贝为可变数组
|
|
144
|
+
// 落盘——节点不再携带运行期对象,无需防御性剥离)
|
|
145
|
+
trace: [...run.state.trace.toArray()],
|
|
150
146
|
errorLogs: run.state.errorLogs,
|
|
151
147
|
error: run.state.error,
|
|
152
148
|
scriptResult: run.state.scriptResult,
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* pending:unregister → onRunDone」终态序列的唯一定义点(D5-② 单点化,收敛原
|
|
10
10
|
* 8 处逐字复制;OR-8 收口步骤与 OR-4/B-4 双围栏内化于本函数)
|
|
11
11
|
*
|
|
12
|
-
* 4
|
|
12
|
+
* 4 个 handle* 路由函数(domain-models.md §失败处理矩阵)+ 终态化/重建/防御 helper 若干:
|
|
13
13
|
* - handleWorkerMessage(run, raw, deps, handlers) — 路由 agent_call/return/error/log
|
|
14
14
|
* - handleWorkerError(run, err, deps, handlers) — worker uncaught error
|
|
15
15
|
* - handleWorkerExit(run, code, handle, deps, handlers) — worker exit
|
|
@@ -39,14 +39,10 @@
|
|
|
39
39
|
|
|
40
40
|
import { getLogger } from "../core/logger.ts";
|
|
41
41
|
|
|
42
|
-
import { SLUG_MAX_LENGTH } from "./models/types.ts";
|
|
43
|
-
import { createRecord, updateFromEvent } from "../execution/execution-record.ts";
|
|
44
|
-
import { SubagentStream } from "../execution/stream-sink.ts";
|
|
45
|
-
import type { AgentEvent } from "../shared/agent-event.ts";
|
|
46
42
|
import { resolveAgentOpts } from "./agent-opts-resolver.ts";
|
|
47
43
|
import { executeAgentCall } from "./execute-agent-call.ts";
|
|
48
44
|
import { AgentCall } from "./models/agent-call.ts";
|
|
49
|
-
import type { LifecycleDeps, WorkerHandlers } from "./models/ports.ts";
|
|
45
|
+
import type { AgentRunner, LifecycleDeps, WorkerHandlers } from "./models/ports.ts";
|
|
50
46
|
import { RunRuntime } from "./models/run-runtime.ts";
|
|
51
47
|
import type { WorkerLogEntry } from "./models/types.ts";
|
|
52
48
|
import type { AgentCallOpts, AgentResult, DoneReason, ExecutionTraceNode } from "./models/types.ts";
|
|
@@ -400,7 +396,7 @@ function discardInFlightCalls(run: WorkflowRun): number[] {
|
|
|
400
396
|
* result 以 IN_FLIGHT_CALL_CANCELLED_MSG 承载取消原因;
|
|
401
397
|
* - trace 节点置 failed + 固定取消文案 + completedAt(「failed + Cancelled 文案」
|
|
402
398
|
* 即取消的既有表达形态,不新增状态枚举);
|
|
403
|
-
* - node
|
|
399
|
+
* - node 无运行期附属对象([H2 W3] trace.live 退役后节点只剩终态摘要字段)。
|
|
404
400
|
*
|
|
405
401
|
* 调用点约定:每个 transition("done") 成功后、store.save 之前——先收口再落盘,
|
|
406
402
|
* 内存态与持久化快照在同一时点收敛(「run-snapshot 落盘前」的实现形态)。
|
|
@@ -419,7 +415,6 @@ export function closeOutInFlightCalls(run: WorkflowRun): number[] {
|
|
|
419
415
|
if (call.status === "running") {
|
|
420
416
|
call.markDone({ content: "", error: IN_FLIGHT_CALL_CANCELLED_MSG });
|
|
421
417
|
}
|
|
422
|
-
call.traceNode.live = undefined;
|
|
423
418
|
run.state.trace.update(callId, {
|
|
424
419
|
status: "failed",
|
|
425
420
|
result: { content: "", error: IN_FLIGHT_CALL_CANCELLED_MSG },
|
|
@@ -697,24 +692,13 @@ function dispatchAgentCall(
|
|
|
697
692
|
return;
|
|
698
693
|
}
|
|
699
694
|
|
|
700
|
-
// 构建 trace
|
|
695
|
+
// 构建 trace 节点([H2 W3] trace.live 退役——实时进度改由 views 经 store 订阅
|
|
696
|
+
// collectRecordsByParentRunId(parentRunId) 查询真实 record,设计 D2;节点保留
|
|
697
|
+
// 终态摘要 result,终态由 executeAgentCall → finalizeCall 写入)。
|
|
701
698
|
const agentName = msg.opts.description ?? msg.opts.agent ?? "unknown";
|
|
702
|
-
// slug 复用 agentName(超长截断),live record 的 slug 仅用于 TUI 展示。
|
|
703
|
-
const liveSlug = agentName.length > SLUG_MAX_LENGTH ? agentName.slice(0, SLUG_MAX_LENGTH) : agentName;
|
|
704
699
|
const now = new Date().toISOString();
|
|
705
|
-
// 未显式指定 model
|
|
700
|
+
// 未显式指定 model 的展示口径。
|
|
706
701
|
const model = msg.opts.model ?? "default";
|
|
707
|
-
// live record:收口 agent 执行过程中的 text/thinking/toolCalls/usage,
|
|
708
|
-
// 供 TUI 在 agent 运行期间显示进度(getEventLog/getCurrentActivity)。
|
|
709
|
-
// 完成时由下方 .then 清除(终态由 node.result 承载)。
|
|
710
|
-
const liveRecord = createRecord(String(msg.callId), {
|
|
711
|
-
agent: agentName,
|
|
712
|
-
model,
|
|
713
|
-
mode: "background",
|
|
714
|
-
task: msg.opts.prompt,
|
|
715
|
-
slug: liveSlug,
|
|
716
|
-
startedAt: Date.now(),
|
|
717
|
-
});
|
|
718
702
|
const node: ExecutionTraceNode = {
|
|
719
703
|
stepIndex: msg.callId,
|
|
720
704
|
agent: agentName,
|
|
@@ -723,7 +707,6 @@ function dispatchAgentCall(
|
|
|
723
707
|
status: "running" as const,
|
|
724
708
|
phase: msg.phase,
|
|
725
709
|
startedAt: now,
|
|
726
|
-
live: liveRecord,
|
|
727
710
|
};
|
|
728
711
|
run.state.trace.append(node);
|
|
729
712
|
|
|
@@ -750,8 +733,6 @@ function dispatchAgentCall(
|
|
|
750
733
|
const errorResult: AgentResult = { content: "", error: resolved.error };
|
|
751
734
|
call.markDone(errorResult);
|
|
752
735
|
run.state.calls.set(msg.callId, call);
|
|
753
|
-
// 无子进程执行,清除空 live record(终态由 result 承载)
|
|
754
|
-
node.live = undefined;
|
|
755
736
|
run.state.trace.update(msg.callId, {
|
|
756
737
|
status: "failed",
|
|
757
738
|
result: errorResult,
|
|
@@ -777,19 +758,20 @@ function dispatchAgentCall(
|
|
|
777
758
|
// 后者已守 terminal(isTerminal)早期 return)。fallback new AbortController 已移除。
|
|
778
759
|
const runtime = run.runtime!;
|
|
779
760
|
const signal = runtime.controller.signal;
|
|
780
|
-
//
|
|
781
|
-
//
|
|
782
|
-
//
|
|
783
|
-
//
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
//
|
|
788
|
-
//
|
|
789
|
-
//
|
|
790
|
-
const
|
|
791
|
-
|
|
792
|
-
:
|
|
761
|
+
// [H2 W3] 执行 port 切换(设计 §3.5 终态数据流:pump 薄化为「消息转调 + run 级
|
|
762
|
+
// 收尾」):workflowAgentDispatch = SubagentService.executeWorkflowAgent 的注入
|
|
763
|
+
// 形态——真实 record(origin:"workflow" + parentRunId=run.runId)进 store、事件
|
|
764
|
+
// 流经 service 内 journal 接线进 record、streaming/守护/池归 service 派发路径。
|
|
765
|
+
// 未注入时(旧测试 deps)回退 deps.runner——[H2 W4] SAR.run 已掏空为纯转调
|
|
766
|
+
// executeWorkflowAgent,两分支执行体归一(同一 service 编排),仅 parentRunId
|
|
767
|
+
// 来源不同(注入路径闭包携带真实 run.runId;回退路径为 SAR 直调占位)。生产
|
|
768
|
+
// 装配恒注入 dispatch(extension index.ts makeDeps),回退分支生产不可达。
|
|
769
|
+
// 旁路 progress record 族(createRecord + updateFromEvent + SubagentStream +
|
|
770
|
+
// trace.live 挂载)随本切换整体退役——TUI/GUI 实时进度改从 store 订阅(D2)。
|
|
771
|
+
const dispatch = deps.workflowAgentDispatch;
|
|
772
|
+
const runner: AgentRunner = dispatch
|
|
773
|
+
? { run: (rOpts, rSignal) => dispatch(rOpts, run.runId, rSignal) }
|
|
774
|
+
: deps.runner;
|
|
793
775
|
// 原 gate.withSlot(fn, signal) 语义内联:pre-aborted 时 reject AbortError(
|
|
794
776
|
// 下方 .catch 依赖此约定不记错),否则直接执行——并发调度归 ConcurrencyPool。
|
|
795
777
|
const dispatchCall = async (): Promise<void> => {
|
|
@@ -798,22 +780,17 @@ function dispatchAgentCall(
|
|
|
798
780
|
abortErr.name = "AbortError";
|
|
799
781
|
throw abortErr;
|
|
800
782
|
}
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
stream?.dispose();
|
|
808
|
-
}
|
|
783
|
+
// OB2(S7 残留):isOrphaned 谓词注入——旧代际 finalize 在 trace.update 前被
|
|
784
|
+
// 拦截(判定语义与下方 .then/.catch 守卫同一 isOrphanedCall,详见
|
|
785
|
+
// execute-agent-call.ts finalizeCall 文档注释)。onEvent/stream 两实参显式
|
|
786
|
+
// undefined 占位([H2 W3] live record 更新与 SubagentStream 已随旁路退役;
|
|
787
|
+
// 位置参数不得前移——isOrphaned 是第 8 形参)。
|
|
788
|
+
await executeAgentCall(call, runner, run.state.budget, signal, run.state.trace, undefined, undefined, () => isOrphanedCall(run, msg.callId, call));
|
|
809
789
|
};
|
|
810
790
|
void dispatchCall()
|
|
811
791
|
.then(() => {
|
|
812
|
-
//
|
|
813
|
-
//
|
|
814
|
-
// M4: 必须在 stale guard 之前清,否则跨 rebuild 的迟到 completion 会累积 live record。
|
|
815
|
-
node.live = undefined;
|
|
816
|
-
// run 终止(终态)后到达的 stale completion 不写 state
|
|
792
|
+
// run 终止(终态)后到达的 stale completion 不写 state(终态由
|
|
793
|
+
// executeAgentCall → finalizeCall 写入 node.result,node 无运行期附属对象)。
|
|
817
794
|
if (run.state.status !== "running") return;
|
|
818
795
|
// 孤儿 call 守卫(S7-second 竞态):rebuild 的 discardInFlightCalls 已移除本
|
|
819
796
|
// call、或重跑 dispatch 已用新实例替换同 callId 条目时,本 completion 属于旧
|
|
@@ -852,13 +829,12 @@ function dispatchAgentCall(
|
|
|
852
829
|
// 兜底回发:executeAgentCall 抛非 Abort 异常时(如 runner undefined 的 TypeError、
|
|
853
830
|
// dispatchCall 内部 bug)原 catch 仅 console.error,worker 内对 callId 的 pending
|
|
854
831
|
// Promise 永不 resolve → agent() 永久 await → worker 脚本挂死。构造 failed AgentResult
|
|
855
|
-
//(与 resolveAgentOpts
|
|
832
|
+
//(与 resolveAgentOpts 失败路径一致的模式)postAgentResult 回 worker,
|
|
856
833
|
// 让 pending Promise resolve(结果为 error),脚本可继续或失败退出。
|
|
857
834
|
// 孤儿 call 守卫(与 .then 对称,S7-second 竞态):rebuild 后本 call 已被 discard
|
|
858
835
|
// 移除/替换——markDone 虽在孤儿实例上无害,但 trace.update 会污染重跑新建的同
|
|
859
836
|
// stepIndex 节点、postAgentResult 会劫持新 worker 的同 callId pending。孤儿时只
|
|
860
|
-
// 留日志,全部跳过。
|
|
861
|
-
node.live = undefined;
|
|
837
|
+
// 留日志,全部跳过。
|
|
862
838
|
if (isOrphanedCall(run, msg.callId, call)) {
|
|
863
839
|
deps.log?.("debug", "workflow:worker-message-pump", "orphan agent call failure dropped", { runId: run.runId, callId: msg.callId });
|
|
864
840
|
return;
|
|
@@ -871,8 +847,8 @@ function dispatchAgentCall(
|
|
|
871
847
|
if (call.status === "pending") call.markRunning();
|
|
872
848
|
call.markDone(errorResult);
|
|
873
849
|
}
|
|
874
|
-
// state 一致性三件套(与 resolveAgentOpts 失败
|
|
875
|
-
// trace 标 failed +
|
|
850
|
+
// state 一致性三件套(与 resolveAgentOpts 失败 / .then 路径对等):
|
|
851
|
+
// trace 标 failed + 持久化(catch 恰是最需留证的场景)。
|
|
876
852
|
// stale 终态(run 已 done)时 run.runtime 为 undefined,postAgentResult 用
|
|
877
853
|
// optional chaining 跳过 worker 回发;trace/state 写入仍执行(无害,终态快照已存)。
|
|
878
854
|
run.state.trace.update(msg.callId, {
|