@zhushanwen/subagent-core 0.2.0 → 0.4.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/README.md +45 -2
- package/agents/analyst.md +60 -0
- package/agents/coder.md +69 -0
- package/agents/debugger.md +66 -0
- package/agents/doc-reviewer.md +49 -0
- package/agents/explorer.md +63 -0
- package/agents/general-purpose.md +32 -0
- package/agents/orchestrator.md +61 -0
- package/agents/planner.md +53 -0
- package/agents/researcher.md +67 -0
- package/agents/reviewer.md +73 -0
- package/dist/chunk-4KN4TTG7.js +240 -0
- package/dist/chunk-APZY4IME.js +27 -0
- package/dist/chunk-X7SZ5HLQ.js +66 -0
- package/dist/execution/engine/engines/zcode/constants.cjs +65 -0
- package/dist/execution/engine/engines/zcode/constants.d.cts +93 -1
- package/dist/execution/engine/engines/zcode/constants.d.ts +93 -1
- package/dist/execution/engine/engines/zcode/constants.js +43 -1
- package/dist/execution/engine/engines/zcode/reader.d.cts +1 -1
- package/dist/execution/engine/engines/zcode/reader.d.ts +1 -1
- package/dist/execution/engine/engines/zcode/reader.js +4 -234
- package/dist/execution/engine/paths.js +7 -19
- package/dist/index.cjs +14709 -100
- package/dist/index.d.cts +4053 -200
- package/dist/index.d.ts +4053 -200
- package/dist/index.js +14358 -102
- package/dist/{types-BxyAidGf.d.cts → types-DpUO16pj.d.cts} +464 -1
- package/dist/{types-BxyAidGf.d.ts → types-DpUO16pj.d.ts} +464 -1
- package/package.json +6 -3
- package/src/__tests__/review-fix-loop-script.test.ts +90 -2
- package/src/__tests__/review-fix-loop-utils.test.ts +21 -0
- package/src/__tests__/smoke.test.ts +9 -2
- package/src/execution/__tests__/agent-profile.test.ts +232 -0
- package/src/execution/__tests__/agents-assembly.test.ts +219 -0
- package/src/execution/__tests__/chat-engine-routing.test.ts +71 -0
- package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +1 -0
- package/src/execution/__tests__/create-concurrency-pool.test.ts +194 -0
- package/src/execution/__tests__/delivery-methods.test.ts +2 -0
- package/src/execution/__tests__/descendant-sweep.test.ts +269 -0
- package/src/execution/__tests__/dialog-queue.test.ts +199 -1
- package/src/execution/__tests__/epipe-fallback.test.ts +2 -0
- package/src/execution/__tests__/execution-runtime-face.test.ts +272 -0
- package/src/execution/__tests__/finalize-record.test.ts +106 -0
- package/src/execution/__tests__/gc-timer.test.ts +2 -0
- package/src/execution/__tests__/get-record-for-action-restart.test.ts +2 -0
- package/src/execution/__tests__/get-state-handshake.test.ts +127 -0
- package/src/execution/__tests__/keep-alive-no-progress.test.ts +378 -0
- package/src/execution/__tests__/kill-all-escalation.test.ts +196 -0
- package/src/execution/__tests__/lifecycle-manager-idle-timer-identity.test.ts +117 -0
- package/src/execution/__tests__/lifecycle-manager.test.ts +39 -0
- package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +99 -0
- package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +113 -0
- package/src/execution/__tests__/notify-ledger.test.ts +294 -0
- package/src/execution/__tests__/record-store-orphan-revive.test.ts +159 -0
- package/src/execution/__tests__/record-store.test.ts +83 -0
- package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +4 -0
- package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +1 -0
- package/src/execution/__tests__/run-spawn-edges.test.ts +407 -17
- package/src/execution/__tests__/run-spawn-integration.test.ts +1 -1
- package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +12 -4
- package/src/execution/__tests__/service-kill-escalation.test.ts +89 -0
- package/src/execution/__tests__/session-pending.test.ts +228 -0
- package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +110 -0
- package/src/execution/__tests__/session-runner-close-prune.test.ts +181 -0
- package/src/execution/__tests__/session-runner-epipe.test.ts +1 -0
- package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +226 -0
- package/src/execution/__tests__/settled-watchdog.test.ts +289 -0
- package/src/execution/__tests__/spawned-children.test.ts +153 -10
- package/src/execution/__tests__/subagent-actions-core.test.ts +997 -0
- package/src/execution/__tests__/subagent-service-message-close.test.ts +71 -10
- package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +174 -0
- package/src/execution/__tests__/subagent-service-notify-gate.test.ts +266 -0
- package/src/execution/__tests__/subagent-service-parent-guard.test.ts +3 -0
- package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +316 -0
- package/src/execution/__tests__/timeout-integration.test.ts +7 -7
- package/src/execution/__tests__/ui-request-handler-factory.test.ts +95 -1
- package/src/execution/__tests__/ui-request-queue.test.ts +25 -0
- package/src/execution/__tests__/worktree-git-ops.test.ts +571 -0
- package/src/execution/__tests__/worktree-reconcile-aging.test.ts +204 -0
- package/src/execution/agent-registry.ts +225 -1
- package/src/execution/agents-assembly.ts +88 -0
- package/src/execution/concurrency-pool.ts +71 -9
- package/src/execution/dialog-queue.ts +101 -3
- package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +49 -2
- package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +87 -1
- package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +26 -0
- package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +83 -1
- package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +131 -0
- package/src/execution/engine/__tests__/registry.test.ts +90 -1
- package/src/execution/engine/engine-discovery.ts +12 -16
- package/src/execution/engine/engines/pi/task-spec-mapper.ts +3 -3
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +274 -0
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +36 -0
- package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +472 -0
- package/src/execution/engine/engines/zcode/__tests__/preparer-appserver.test.ts +387 -0
- package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +780 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +815 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +462 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +119 -1
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +136 -6
- package/src/execution/engine/engines/zcode/appserver-home.ts +442 -0
- package/src/execution/engine/engines/zcode/appserver-probe.ts +141 -0
- package/src/execution/engine/engines/zcode/connection.ts +585 -0
- package/src/execution/engine/engines/zcode/constants.ts +129 -0
- package/src/execution/engine/engines/zcode/golden-sample.ts +54 -8
- package/src/execution/engine/engines/zcode/preparer.ts +25 -22
- package/src/execution/engine/engines/zcode/session-channel.ts +656 -0
- package/src/execution/engine/engines/zcode/zcode-engine.ts +909 -41
- package/src/execution/engine/host-task-spec.ts +3 -3
- package/src/execution/engine/port.ts +41 -1
- package/src/execution/engine/registry.ts +53 -0
- package/src/execution/finalize-record.ts +63 -5
- package/src/execution/get-state-handshake.ts +85 -10
- package/src/execution/lifecycle-manager.ts +27 -3
- package/src/execution/manifest-store.ts +53 -62
- package/src/execution/notifier.ts +10 -10
- package/src/execution/notify-ledger.ts +117 -16
- package/src/execution/record-entry.ts +8 -2
- package/src/execution/record-store.ts +29 -15
- package/src/execution/session-pending.ts +213 -62
- package/src/execution/session-runner.ts +974 -128
- package/src/execution/sessions-index.ts +10 -55
- package/src/execution/settled-watchdog.ts +99 -0
- package/src/execution/subagent-actions-core.ts +686 -0
- package/src/execution/subagent-service.ts +396 -49
- package/src/execution/ui-request-handler-factory.ts +27 -4
- package/src/execution/worktree-git-ops.ts +397 -0
- package/src/execution/worktree-manager.ts +72 -10
- package/src/execution/worktree-registry.ts +6 -12
- package/src/index.ts +413 -6
- package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +42 -3
- package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +2 -4
- package/src/orchestration/__tests__/args-meta.test.ts +358 -0
- package/src/orchestration/__tests__/error-recovery-handlers.test.ts +1 -8
- package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +312 -0
- package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +382 -0
- package/src/orchestration/__tests__/file-run-store-prune.test.ts +168 -0
- package/src/orchestration/__tests__/file-run-store-throttle.test.ts +168 -0
- package/src/orchestration/__tests__/file-run-store.test.ts +389 -0
- package/src/orchestration/__tests__/helpers/flush-microtasks.ts +13 -0
- package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +34 -0
- package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +357 -0
- package/src/orchestration/__tests__/lifecycle-recover-crashed.test.ts +289 -0
- package/src/orchestration/__tests__/lifecycle.test.ts +77 -0
- package/src/orchestration/__tests__/run-snapshot.test.ts +353 -0
- package/src/orchestration/__tests__/script-generate.test.ts +314 -0
- package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +275 -0
- package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +78 -1
- package/src/orchestration/__tests__/workflow-files.test.ts +186 -0
- package/src/orchestration/__tests__/workflow-run-summary.test.ts +119 -0
- package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +124 -0
- package/src/orchestration/agent-opts-resolver.ts +7 -7
- package/src/orchestration/args-meta.ts +198 -0
- package/src/orchestration/error-recovery.ts +416 -146
- package/src/orchestration/execute-agent-call.ts +9 -9
- package/src/orchestration/file-run-store.ts +327 -0
- package/src/orchestration/launcher.ts +35 -26
- package/src/orchestration/lifecycle.ts +355 -79
- package/src/orchestration/models/agent-call.ts +7 -7
- package/src/orchestration/models/budget.ts +5 -5
- package/src/orchestration/models/run-runtime.ts +13 -13
- package/src/orchestration/models/trace.ts +8 -8
- package/src/orchestration/models/workflow-run.ts +27 -27
- package/src/orchestration/models/workflow-script.ts +4 -4
- package/src/orchestration/run-snapshot.ts +266 -0
- package/src/orchestration/script-generate.ts +154 -0
- package/src/orchestration/script-lint.ts +33 -33
- package/src/orchestration/worker-handle.ts +10 -10
- package/src/orchestration/worker-host.ts +10 -10
- package/src/orchestration/worker-script-builder.ts +401 -346
- package/src/orchestration/workflow-files.ts +37 -11
- package/src/orchestration/workflow-run-summary.ts +69 -0
- package/src/orchestration/workflow-script-registry-impl.ts +31 -9
- package/src/shared/__tests__/agent-ref.test.ts +209 -2
- package/src/shared/__tests__/atomic-write.test.ts +267 -0
- package/src/shared/__tests__/bounded-serialize.test.ts +236 -0
- package/src/shared/__tests__/injection-render.test.ts +518 -0
- package/src/shared/__tests__/resource-discovery-host-roots.test.ts +474 -0
- package/src/shared/__tests__/resource-discovery.test.ts +3 -2
- package/src/shared/agent-ref.ts +143 -2
- package/src/shared/atomic-write.ts +320 -0
- package/src/shared/bounded-serialize.ts +154 -0
- package/src/shared/injection-render.ts +279 -0
- package/src/shared/meta-parser.ts +41 -1
- package/src/shared/resource-discovery.ts +104 -37
- package/src/shared/resource-meta.ts +14 -0
- package/src/shared/xml-injection.ts +9 -9
- package/workflows/README.md +9 -9
- package/workflows/chain.js +4 -2
- package/workflows/map-reduce.js +5 -3
- package/workflows/parallel.js +5 -3
- package/workflows/review-fix-loop-utils.cjs +14 -3
- package/workflows/review-fix-loop.js +18 -7
- package/workflows/scatter-gather.js +4 -2
- package/dist/chunk-3VOERJPJ.js +0 -22
|
@@ -30,12 +30,28 @@
|
|
|
30
30
|
* { type: "return", runId: string, result: unknown }
|
|
31
31
|
* { type: "error", runId: string, error: string }
|
|
32
32
|
* { type: "log", phase: string, message: string }
|
|
33
|
+
* —— log() 的唯一通路:主线程 handleWorkerMessage 的 log case 即时消费(push 进
|
|
34
|
+
* run.state.errorLogs + debug 留痕,error-recovery.ts)。[B-3] 旧实现同时把内容
|
|
35
|
+
* 记入 _workerLogs 随 return/error 消息再带回一份——同一日志在 errorLogs 占两格、
|
|
36
|
+
* TUI 双份,已退役。崩溃场景(return/error 消息未发出)丢 log 条目可接受:
|
|
37
|
+
* log 是 T7 补充可观测,非持久化权威。
|
|
33
38
|
*
|
|
34
39
|
* Main → Worker (parentPort.on("message")):
|
|
35
40
|
* { type: "agent-result", callId: number, result: AgentResult, cached: boolean }
|
|
36
41
|
* { type: "workflow-result", callId: number, result: unknown }
|
|
37
42
|
* { type: "budget-update", budget: unknown }
|
|
38
43
|
* { type: "abort", reason: string }
|
|
44
|
+
* —— [OR-3] 主线程在 abortRun/terminateRunningRuns 终止 run 时、worker.terminate
|
|
45
|
+
* 之前广播本消息;worker 侧 _pendingCalls 全部 reject(WorkflowAbortedError)并
|
|
46
|
+
* 清理,脚本 catch 后自然收尾退出(P-T3:广播与 worker 自然退出交错不产
|
|
47
|
+
* unhandledRejection)。
|
|
48
|
+
*
|
|
49
|
+
* [OR-3] per-call 超时:agent() 的 pending 在 opts.timeoutMs > 0 时接线消息层自己的
|
|
50
|
+
* 超时——主线程对 agent-call 永不回话(畸形消息丢弃 / postMessage 双重失败 / runner
|
|
51
|
+
* 不 settle)时 pending 以错误 resolve(对齐 agent-result 失败容错策略:不 reject、
|
|
52
|
+
* 不放大成脚本 error → rebuild)。缺省(未传 timeoutMs)= 不限,与 runner 侧
|
|
53
|
+
* per-call timeout 语义一致。workflow() 无 timeout 协议字段,不接线(其 pending
|
|
54
|
+
* 由 abort 广播与嵌套 run 自身上界兜底)。
|
|
39
55
|
*/
|
|
40
56
|
|
|
41
57
|
// ── Build worker source ─────────────────────────────────────
|
|
@@ -52,361 +68,400 @@
|
|
|
52
68
|
|
|
53
69
|
/** PRE 段:use strict … User workflow script 注释行(不含 userScript)。 */
|
|
54
70
|
const WORKER_TEMPLATE_PRE = [
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
71
|
+
'"use strict";',
|
|
72
|
+
'// Module-scope helpers: accessible to BOTH the IIFE body AND the outer',
|
|
73
|
+
'// .then()/.catch() handlers (which run outside the IIFE). _workerLogs/',
|
|
74
|
+
'// _pushWorkerLog/_safePost + the parentPort/workerData handles must all live',
|
|
75
|
+
'// here — a previous version declared _safePost inside the IIFE, so the',
|
|
76
|
+
'// .then()/.catch() return/error handlers threw ReferenceError: _safePost is',
|
|
77
|
+
'// not defined, crashing the Worker on EVERY script return (exit code 1) and',
|
|
78
|
+
'// losing all diagnostics. require() is cached, so destructuring once at module',
|
|
79
|
+
'// load and reusing everywhere avoids the redundant require calls that used to',
|
|
80
|
+
'// appear in the IIFE and the outer .then/.catch.',
|
|
81
|
+
'const { parentPort: _parentPort, workerData: _workerData } = require("node:worker_threads");',
|
|
82
|
+
'const _workerLogs = [];',
|
|
83
|
+
'// IF6(#12): known agent() fields — hoisted to module scope, built once per worker',
|
|
84
|
+
'// (was rebuilt inside agent() on every call; field set is call-invariant).',
|
|
85
|
+
'const _KNOWN_FIELDS = new Set(["prompt", "description", "schema", "model", "scene", "label", "task", "agent", "phase", "skill", "timeoutMs", "maxTurns", "cwd", "fork", "worktree", "returnMeta", "thinkingLevel", "engine"]);',
|
|
86
|
+
'function _pushWorkerLog(level, args) {',
|
|
87
|
+
' try { _workerLogs.push({ level, message: args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ") }); } catch (e) { /* swallow */ }',
|
|
88
|
+
'}',
|
|
89
|
+
'// ── safePostMessage wrapper: 统一 postMessage 防御(DataCloneError 等)──',
|
|
90
|
+
'// Module-scope so the outer .then/.catch return/error handlers can use it.',
|
|
91
|
+
'// context 取值约定(诊断标识):固定为消息类型字面量——',
|
|
92
|
+
'// "agent-call" / "workflow-call" / "return" / "error",调用方据此在日志里',
|
|
93
|
+
'// 一眼定位是哪类 postMessage 失败。新增调用点必须传对应 context。',
|
|
94
|
+
'function _safePost(msg, context) {',
|
|
95
|
+
' try { _parentPort.postMessage(msg); return true; }',
|
|
96
|
+
' catch (e) {',
|
|
97
|
+
' const errMsg = e && e.message ? e.message : String(e);',
|
|
98
|
+
' const stack = e && e.stack ? e.stack : "";',
|
|
99
|
+
' _pushWorkerLog("error", ["[postMessage failed:" + context + "]", errMsg, stack]);',
|
|
100
|
+
' return false;',
|
|
101
|
+
' }',
|
|
102
|
+
'}',
|
|
103
|
+
'(async () => {',
|
|
104
|
+
' const parentPort = _parentPort;',
|
|
105
|
+
' const workerData = _workerData;',
|
|
106
|
+
'',
|
|
107
|
+
' if (!parentPort) {',
|
|
108
|
+
' throw new Error("Workflow worker: parentPort is null — not running in a Worker thread");',
|
|
109
|
+
' }',
|
|
110
|
+
'',
|
|
111
|
+
' // ── Intercept console.* to avoid leaking worker diagnostics into the input area ──',
|
|
112
|
+
' console.log = function (...args) { _pushWorkerLog("log", args); };',
|
|
113
|
+
' console.warn = function (...args) { _pushWorkerLog("warn", args); };',
|
|
114
|
+
' console.error = function (...args) { _pushWorkerLog("error", args); };',
|
|
115
|
+
' console.info = function (...args) { _pushWorkerLog("info", args); };',
|
|
116
|
+
'',
|
|
117
|
+
' // ── Internal state ──',
|
|
118
|
+
' let _callIdCounter = 0;',
|
|
119
|
+
' let _agentCallCount = 0;',
|
|
120
|
+
' const _pendingCalls = new Map();',
|
|
121
|
+
' const _callCache = workerData.callCache instanceof Map',
|
|
122
|
+
' ? workerData.callCache',
|
|
123
|
+
' : new Map(Object.entries(workerData.callCache || {}).map(([k, v]) => [Number(k), v]));',
|
|
124
|
+
'',
|
|
125
|
+
' // ── Injected globals ──',
|
|
126
|
+
' const $ARGS = (workerData.args && typeof workerData.args === "object") ? workerData.args : {};\n' +
|
|
111
127
|
' const args = $ARGS;',
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
'
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
128
|
+
' const $WORKSPACE = typeof workerData.workspace === "string" ? workerData.workspace : "";',
|
|
129
|
+
' const _budgetData = {',
|
|
130
|
+
' total: (workerData.budget && workerData.budget.maxTokens) || 0,',
|
|
131
|
+
' _spentTokens: (workerData.budget && workerData.budget.usedTokens) ?? 0,',
|
|
132
|
+
' _spentCost: (workerData.budget && workerData.budget.usedCost) ?? 0,',
|
|
133
|
+
' };',
|
|
134
|
+
' const $BUDGET = {',
|
|
135
|
+
' get total() { return _budgetData.total; },',
|
|
136
|
+
' spent() { return _budgetData._spentTokens; },',
|
|
137
|
+
' remaining() { return Math.max(0, _budgetData.total - _budgetData._spentTokens); },',
|
|
138
|
+
' };',
|
|
139
|
+
'',
|
|
140
|
+
' // ── Run-level model/thinkingLevel override (Option B symmetric single-path) ──',
|
|
141
|
+
' // Injected from workerData (set by workerHost from RunSpec.model/thinkingLevel).',
|
|
142
|
+
' // agent() reads these as run-level defaults; per-call explicit opts still win.',
|
|
143
|
+
' const $MODEL = (workerData.model && typeof workerData.model === "string") ? workerData.model : undefined;',
|
|
144
|
+
' const $THINKING_LEVEL = (workerData.thinkingLevel && typeof workerData.thinkingLevel === "string") ? workerData.thinkingLevel : undefined;',
|
|
145
|
+
'',
|
|
146
|
+
' // ── WorkflowAbortedError ──',
|
|
147
|
+
' class WorkflowAbortedError extends Error {',
|
|
148
|
+
' constructor(reason) {',
|
|
149
|
+
' super("Workflow aborted: " + (reason || "No reason"));',
|
|
150
|
+
' this.name = "WorkflowAbortedError";',
|
|
151
|
+
' this.reason = reason || "";',
|
|
152
|
+
' }',
|
|
153
|
+
' }',
|
|
154
|
+
'',
|
|
155
|
+
' // ── Message handler (main thread → worker) ──',
|
|
156
|
+
' parentPort.on("message", (msg) => {',
|
|
157
|
+
' if (msg.type === "agent-result") {',
|
|
158
|
+
' const pending = _pendingCalls.get(msg.callId);',
|
|
159
|
+
' if (pending) {',
|
|
160
|
+
' _pendingCalls.delete(msg.callId);',
|
|
161
|
+
' // [OR-3] 结果已到,清除 per-call 超时 timer(有 timer 才清,防御旧 pending 无该字段)',
|
|
162
|
+
' if (pending.timer) { clearTimeout(pending.timer); }',
|
|
163
|
+
' if (typeof msg.result !== "undefined") {',
|
|
164
|
+
' _callCache.set(msg.callId, msg.result);',
|
|
165
|
+
' }',
|
|
166
|
+
' // 失败不传播到 agent 外部:resolve 而非 reject。',
|
|
167
|
+
' // 旧实现在 result.error 时 reject,单 agent 失败会冒到 worker 顶层 .catch()',
|
|
168
|
+
' // → 发 type:"error" → handleScriptError → rebuildRuntime → SIGKILL 同伴进程,',
|
|
169
|
+
' // 把单点失败放大成整批崩溃。改为始终 resolve(错误时回退到 content 文本),',
|
|
170
|
+
' // 让 parallel() 下的脚本容错循环(parseResult → null → skip)自然接管。',
|
|
171
|
+
' // 错误原因已由主线程 executeAgentCall → trace.update(result.error) 保留在 trace/TUI,',
|
|
172
|
+
' // 不丢失。失败 resolve 为空字符串是既定容错策略。',
|
|
173
|
+
' // [MF-4] schema 模式下失败时 agent() 仍 resolve(content 回退、不 throw)——',
|
|
174
|
+
' // 需要检查错误时请用 returnMeta:true(resolve 值含 error 字段)。',
|
|
175
|
+
' // parsedOutput: validated data object from structured-output execute().',
|
|
176
|
+
' // Fallback to content (raw text) when no schema was requested or on error.',
|
|
177
|
+
' // W2 改动 9(b):returnMeta===true 时 resolve {value,sessionFile,worktreePath,error,usage,durationMs,sessionId}',
|
|
178
|
+
' // (对称缓存重放 9c);否则单值。usage/durationMs/sessionId 为 rfl 仪表透传(tier-1 §7.1,',
|
|
179
|
+
' // AgentResult 已含、此处原样转发,缺省 undefined 兼容旧主线程)。',
|
|
180
|
+
' const _value = msg.result.parsedOutput ?? msg.result.content;',
|
|
181
|
+
' if (pending.returnMeta) {',
|
|
182
|
+
' pending.resolve({',
|
|
183
|
+
' value: _value,',
|
|
184
|
+
' sessionFile: msg.result.sessionFile,',
|
|
185
|
+
' worktreePath: msg.result.worktreePath,',
|
|
186
|
+
' error: msg.result.error,',
|
|
187
|
+
' usage: msg.result.usage,',
|
|
188
|
+
' durationMs: msg.result.durationMs,',
|
|
189
|
+
' sessionId: msg.result.sessionId,',
|
|
190
|
+
' });',
|
|
191
|
+
' } else {',
|
|
192
|
+
' pending.resolve(_value);',
|
|
193
|
+
' }',
|
|
194
|
+
' }',
|
|
195
|
+
' } else if (msg.type === "workflow-result") {',
|
|
196
|
+
' const pending = _pendingCalls.get(msg.callId);',
|
|
197
|
+
' if (pending) {',
|
|
198
|
+
' _pendingCalls.delete(msg.callId);',
|
|
199
|
+
' // [OR-3] 结果已到,清除 per-call 超时 timer(workflow() 当前不挂 timer,防御性对称清理)',
|
|
200
|
+
' if (pending.timer) { clearTimeout(pending.timer); }',
|
|
201
|
+
' pending.resolve(msg.result);',
|
|
202
|
+
' }',
|
|
203
|
+
' } else if (msg.type === "abort") {',
|
|
204
|
+
' // [OR-3] 主线程 abortRun/terminateRunningRuns 的优雅解阻广播:全部 pending',
|
|
205
|
+
' // reject(脚本 catch WorkflowAbortedError 可感知取消)+ 清理 timer + 清 Map。',
|
|
206
|
+
' // 后续迟到结果命中空 Map(no-op),不产主线程 unhandledRejection。',
|
|
207
|
+
' const err = new WorkflowAbortedError(msg.reason);',
|
|
208
|
+
' _pendingCalls.forEach((p) => { if (p.timer) { clearTimeout(p.timer); } p.reject(err); });',
|
|
209
|
+
' _pendingCalls.clear();',
|
|
210
|
+
' } else if (msg.type === "budget-update" && msg.budget) {',
|
|
211
|
+
' _budgetData._spentTokens = msg.budget.usedTokens ?? _budgetData._spentTokens;',
|
|
212
|
+
' _budgetData._spentCost = msg.budget.usedCost ?? _budgetData._spentCost;',
|
|
213
|
+
' }',
|
|
214
|
+
' // "budget-warning" is informational; no required handling',
|
|
215
|
+
' });',
|
|
216
|
+
'',
|
|
217
|
+
// ── phase global ──
|
|
218
|
+
' let _currentPhase = "";',
|
|
219
|
+
' function phase(name) { _currentPhase = String(name); }',
|
|
220
|
+
'',
|
|
221
|
+
// ── log global ──
|
|
222
|
+
' function log(msg) {',
|
|
223
|
+
' var _logText = String(msg);',
|
|
224
|
+
' // [B-3] 单通路:只发独立 {type:"log"} 消息,主线程 handleWorkerLog 即时消费',
|
|
225
|
+
' //(push 进 errorLogs)。不再同时记入 _workerLogs——旧双通路让同一日志随',
|
|
226
|
+
' // return/error 的 workerLogs 再带回一份,errorLogs 占两格、TUI 双份。',
|
|
227
|
+
' try { parentPort.postMessage({ type: "log", phase: _currentPhase, message: _logText }); } catch(e) { /* swallow */ }',
|
|
228
|
+
' }',
|
|
229
|
+
'',
|
|
230
|
+
// ── agent global — CC-compatible multi-signature ──
|
|
231
|
+
' async function agent(firstArg, secondArg) {',
|
|
232
|
+
' let opts;',
|
|
233
|
+
' if (typeof firstArg === "string") {',
|
|
234
|
+
' opts = {',
|
|
235
|
+
' prompt: firstArg,',
|
|
236
|
+
' description: (secondArg && typeof secondArg === "object" && secondArg.label) || undefined,',
|
|
237
|
+
' schema: (secondArg && typeof secondArg === "object" && secondArg.schema) || undefined,',
|
|
238
|
+
' model: (secondArg && typeof secondArg === "object" && secondArg.model) || $MODEL,',
|
|
239
|
+
' scene: (secondArg && typeof secondArg === "object" && secondArg.scene) || undefined,\n' +
|
|
213
240
|
' phase: (secondArg && typeof secondArg === "object" && secondArg.phase) || undefined,',
|
|
214
|
-
|
|
215
|
-
|
|
241
|
+
' thinkingLevel: (secondArg && typeof secondArg === "object" && secondArg.thinkingLevel) || $THINKING_LEVEL,',
|
|
242
|
+
' // step 级 turn 上限(turn limiter;显式 0/负 = 显式不限,压过 spawn watchdog env 兑底,SP-6)\n' +
|
|
216
243
|
' // ?? 语义保真:仅 null/undefined 归 undefined(走 env 兑底),显式 0 保留(U5 参数 > env)',
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
'
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
'
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
'
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
'
|
|
267
|
-
'
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
244
|
+
' maxTurns: (secondArg && typeof secondArg === "object" ? secondArg.maxTurns : undefined) ?? undefined,',
|
|
245
|
+
' // [OR-3] per-call timeoutMs 透传(string 分支此前丢弃该字段——对象分支已有;',
|
|
246
|
+
' // worker pending 消息层超时依赖它流到 agent-call 消息)',
|
|
247
|
+
' timeoutMs: (secondArg && typeof secondArg === "object" ? secondArg.timeoutMs : undefined),',
|
|
248
|
+
' // P4 D9③:step 级 engine 显式指定(仅限必须某引擎独有能力的场景)',
|
|
249
|
+
' engine: (secondArg && typeof secondArg === "object" && secondArg.engine) || undefined,',
|
|
250
|
+
' };',
|
|
251
|
+
' } else if (typeof firstArg === "object" && firstArg !== null) {',
|
|
252
|
+
' if (firstArg.prompt) {',
|
|
253
|
+
' opts = firstArg;',
|
|
254
|
+
' } else if (firstArg.task || firstArg.agent) {',
|
|
255
|
+
' // fork/worktree/returnMeta forwarded here (parity with agent({prompt,...}) and direct-pass',
|
|
256
|
+
' // branches). Previously this shortcut dropped them (w1 only noted, not wired).',
|
|
257
|
+
' opts = {',
|
|
258
|
+
' prompt: firstArg.task || firstArg.prompt || "",',
|
|
259
|
+
' description: firstArg.label || firstArg.description,',
|
|
260
|
+
' agent: firstArg.agent,',
|
|
261
|
+
' schema: firstArg.schema,',
|
|
262
|
+
' model: firstArg.model || $MODEL,',
|
|
263
|
+
' scene: firstArg.scene,',
|
|
264
|
+
' skill: firstArg.skill,',
|
|
265
|
+
' timeoutMs: firstArg.timeoutMs,',
|
|
266
|
+
' maxTurns: firstArg.maxTurns,',
|
|
267
|
+
' cwd: firstArg.cwd,',
|
|
268
|
+
' fork: firstArg.fork,',
|
|
269
|
+
' worktree: firstArg.worktree,',
|
|
270
|
+
' returnMeta: firstArg.returnMeta,',
|
|
271
|
+
' thinkingLevel: firstArg.thinkingLevel || $THINKING_LEVEL,',
|
|
272
|
+
' engine: firstArg.engine,',
|
|
273
|
+
' };',
|
|
274
|
+
' } else {',
|
|
275
|
+
' opts = firstArg;',
|
|
276
|
+
' }',
|
|
277
|
+
' } else {',
|
|
278
|
+
' throw new Error("agent() requires a prompt string or options object as first argument");',
|
|
279
|
+
' }',
|
|
280
|
+
'',
|
|
281
|
+
' // Run-level fallback for object branches (opts = firstArg direct assign).',
|
|
282
|
+
' // Priority: agent() explicit > $MODEL global (set from workerData.model).',
|
|
283
|
+
' if (!opts.model && $MODEL) opts.model = $MODEL;',
|
|
284
|
+
' if (!opts.thinkingLevel && $THINKING_LEVEL) opts.thinkingLevel = $THINKING_LEVEL;',
|
|
285
|
+
'',
|
|
286
|
+
' // Validate known agent() fields to catch API misuse early (_KNOWN_FIELDS at module scope)',
|
|
287
|
+
' const _unknownFields = Object.keys(opts).filter((k) => !_KNOWN_FIELDS.has(k));',
|
|
288
|
+
' if (_unknownFields.length > 0) {',
|
|
289
|
+
' _pushWorkerLog("warn", ["[workflow] agent() received unknown fields: " + _unknownFields.join(", ") + ". Known fields: prompt, description, schema, model, scene, label, task, agent, phase, skill, timeoutMs, maxTurns, cwd, fork, worktree, returnMeta, thinkingLevel, engine"]);',
|
|
290
|
+
' }',
|
|
291
|
+
'',
|
|
292
|
+
' const callId = _callIdCounter;',
|
|
293
|
+
' _callIdCounter++;',
|
|
294
|
+
' _agentCallCount++;',
|
|
295
|
+
' if (_callCache.has(callId)) {',
|
|
296
|
+
' const cached = _callCache.get(callId);',
|
|
297
|
+
' // 与 live handler 对齐:失败也 resolve(回退 content),不 throw。',
|
|
298
|
+
' // 见 agent-result 消息处理的注释:拒绝传播失败到 agent 外部。',
|
|
299
|
+
' // W2 改动 9(c):returnMeta 分支(对称 handler 9b),opts 仍在闭包作用域。',
|
|
300
|
+
' if (!cached) return undefined;',
|
|
301
|
+
' const _cachedValue = cached.parsedOutput ?? cached.content;',
|
|
302
|
+
' if (opts.returnMeta === true) {',
|
|
303
|
+
' return {',
|
|
304
|
+
' value: _cachedValue,',
|
|
305
|
+
' sessionFile: cached.sessionFile,',
|
|
306
|
+
' worktreePath: cached.worktreePath,',
|
|
307
|
+
' error: cached.error,',
|
|
308
|
+
' usage: cached.usage,',
|
|
309
|
+
' durationMs: cached.durationMs,',
|
|
310
|
+
' sessionId: cached.sessionId,',
|
|
311
|
+
' };',
|
|
312
|
+
' }',
|
|
313
|
+
' return _cachedValue;',
|
|
314
|
+
' }',
|
|
315
|
+
'',
|
|
316
|
+
' const _effectivePhase = opts.phase || _currentPhase;\n' +
|
|
287
317
|
' delete opts.phase;\n' +
|
|
288
318
|
'\n' +
|
|
289
319
|
' if (!_safePost({ type: "agent-call", callId, opts, phase: _effectivePhase }, "agent-call")) {',
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
'
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
'
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
320
|
+
' return Promise.reject(new Error("postMessage failed for agent-call (callId=" + callId + "): see workerLogs"));',
|
|
321
|
+
' }',
|
|
322
|
+
' return new Promise((resolve, reject) => {',
|
|
323
|
+
' const _pending = { resolve: resolve, reject: reject, returnMeta: opts.returnMeta === true, timer: undefined };',
|
|
324
|
+
' // [OR-3] per-call timeoutMs:消息层自己的超时——主线程对 agent-call 永不回话',
|
|
325
|
+
' //(畸形消息丢弃 / postMessage 双重失败 / runner 不 settle)时 pending 不再永挂',
|
|
326
|
+
' //(旧实现零超时 → agent() 永挂 → worker 不退出 → run 永久 running)。',
|
|
327
|
+
' // 超时以错误 resolve(对齐 agent-result 失败容错策略:不 reject、不把单点超时',
|
|
328
|
+
' // 放大成脚本 error → rebuild),并记入 _workerLogs 留诊断。仅显式 timeoutMs > 0',
|
|
329
|
+
' // 启用(缺省不限,与 runner 侧 per-call timeout 语义一致)。timer unref:脚本',
|
|
330
|
+
' // 已 return 时残留 timer 不延迟 worker 自然退出。',
|
|
331
|
+
' if (typeof opts.timeoutMs === "number" && opts.timeoutMs > 0) {',
|
|
332
|
+
' _pending.timer = setTimeout(() => {',
|
|
333
|
+
' if (!_pendingCalls.delete(callId)) { return; }',
|
|
334
|
+
' const _timeoutMsg = "agent call timed out after " + opts.timeoutMs + "ms (callId=" + callId + ") — no agent-result received from main thread";',
|
|
335
|
+
' _pushWorkerLog("warn", ["[workflow] " + _timeoutMsg]);',
|
|
336
|
+
' if (opts.returnMeta === true) {',
|
|
337
|
+
' _pending.resolve({ value: "", error: _timeoutMsg });',
|
|
338
|
+
' } else {',
|
|
339
|
+
' // [B-5] 非 returnMeta 超时 resolve 单值错误消息字符串(对齐 agent-result',
|
|
340
|
+
' // 失败路径 resolve 单值 _value = parsedOutput ?? content 的形态)——',
|
|
341
|
+
' // 旧实现 resolve {content:"", error} 对象,字符串消费型脚本(r.trim() 类)',
|
|
342
|
+
' // 在超时路径 TypeError。',
|
|
343
|
+
' _pending.resolve(_timeoutMsg);',
|
|
344
|
+
' }',
|
|
345
|
+
' }, opts.timeoutMs);',
|
|
346
|
+
' if (_pending.timer && typeof _pending.timer.unref === "function") { _pending.timer.unref(); }',
|
|
347
|
+
' }',
|
|
348
|
+
' _pendingCalls.set(callId, _pending);',
|
|
349
|
+
' });',
|
|
350
|
+
' }',
|
|
351
|
+
'',
|
|
352
|
+
// ── parallel global — CC-compatible ──
|
|
353
|
+
// allSettled 语义:单个 agent 的意外 reject(postMessage 失败等基础设施异常、abort)
|
|
354
|
+
// 不拖垮整批。rejected 结果降级为错误消息字符串(与 agent() 的 error→content 回退一致,
|
|
355
|
+
// parseResult(string) → null → 脚本 soft-fail)。B1 之后 agent() 不再因 agent 失败 reject,
|
|
356
|
+
// 这里作为纵深防御保留。
|
|
357
|
+
//
|
|
358
|
+
// **Promise 项处理**:CC-compatible 写法 `parallel([agent({...}), ...])` 传入的是已实例化
|
|
359
|
+
// 的 Promise 数组(agent() 同步返回 Promise)。Promise 是 object 但无 .then 鸭辨分支时
|
|
360
|
+
// 会落到 `agent(c)` 把 Promise 当 opts 传 → postMessage DataCloneError。必须在函数/opts
|
|
361
|
+
// 分支前用 thenable 鸭辨直接返回 in-flight Promise,让 allSettled 接管。
|
|
362
|
+
' async function parallel(calls) {',
|
|
363
|
+
' if (typeof calls === "function") { return calls(); }',
|
|
364
|
+
' const settled = await Promise.allSettled(calls.map((c) => {',
|
|
365
|
+
' if (c && typeof c.then === "function") { return c; }',
|
|
366
|
+
' if (typeof c === "function") { return c(); }',
|
|
367
|
+
' if (typeof c === "object" && c !== null && (c.task || c.agent)) { return agent(c); }',
|
|
368
|
+
' return agent(c);',
|
|
369
|
+
' }));',
|
|
370
|
+
' return settled.map((r) => {',
|
|
371
|
+
' if (r.status === "fulfilled") {',
|
|
372
|
+
' const v = r.value;',
|
|
373
|
+
' if (v !== null && typeof v === "object" && !Array.isArray(v)) {',
|
|
374
|
+
' // 主线程 fallback(postAgentResult/postResult serialization failed)回发的对象含 error 字段',
|
|
375
|
+
' // → 归一化为 failed 形状,与脚本侧 r.status === "failed" 检查统一',
|
|
376
|
+
' if (typeof v.error === "string" && v.error.length > 0) return { status: "failed", error: v.error };',
|
|
377
|
+
' return v;',
|
|
378
|
+
' }',
|
|
379
|
+
' return { status: "failed", error: "agent returned non-object result (type=" + typeof v + ")" };',
|
|
380
|
+
' }',
|
|
381
|
+
' const reason = r.reason;',
|
|
382
|
+
' const errMsg = reason instanceof Error ? reason.message : String(reason);',
|
|
383
|
+
' return { status: "failed", error: errMsg };',
|
|
384
|
+
' });',
|
|
385
|
+
' }',
|
|
386
|
+
'',
|
|
387
|
+
// ── pipeline global ──
|
|
388
|
+
' async function pipeline(firstArg, ...restStages) {',
|
|
389
|
+
' // Single-arg mode: pipeline([stage1, stage2, ...])',
|
|
390
|
+
' if (Array.isArray(firstArg) && restStages.length === 0) {',
|
|
391
|
+
' let result;',
|
|
392
|
+
' for (let i = 0; i < firstArg.length; i++) {',
|
|
393
|
+
' try { result = await firstArg[i](result); }',
|
|
394
|
+
' catch (e) {',
|
|
395
|
+
' const msg = e && e.message ? e.message : String(e);',
|
|
396
|
+
' _pushWorkerLog("error", ["[pipeline stage " + i + " failed]", msg]);',
|
|
397
|
+
' throw e;',
|
|
398
|
+
' }',
|
|
399
|
+
' }',
|
|
400
|
+
' return result;',
|
|
401
|
+
' }',
|
|
402
|
+
' // Cartesian product mode: pipeline([items], stage1, stage2, ...)',
|
|
403
|
+
' if (Array.isArray(firstArg) && restStages.length > 0 && typeof restStages[0] === "function") {',
|
|
404
|
+
' const results = [];',
|
|
405
|
+
' for (let idx = 0; idx < firstArg.length; idx++) {',
|
|
406
|
+
' const item = firstArg[idx];',
|
|
407
|
+
' let val = item;',
|
|
408
|
+
' let failed = false;',
|
|
409
|
+
' for (const stage of restStages) {',
|
|
410
|
+
' if (failed) break;',
|
|
411
|
+
' try { val = await stage(val); }',
|
|
412
|
+
' catch (e) {',
|
|
413
|
+
' const msg = e && e.message ? e.message : String(e);',
|
|
414
|
+
' _pushWorkerLog("error", ["[pipeline cartesian stage failed for item " + (idx + 1) + "]", msg]);',
|
|
415
|
+
' val = null; failed = true;',
|
|
416
|
+
' }',
|
|
417
|
+
' }',
|
|
418
|
+
' results.push(val);',
|
|
419
|
+
' }',
|
|
420
|
+
' return results;',
|
|
421
|
+
' }',
|
|
422
|
+
' throw new Error("pipeline() expects pipeline([stage1, ...]) or pipeline([items], stage1, ...)");',
|
|
423
|
+
' }',
|
|
424
|
+
'',
|
|
425
|
+
' // ── workflow global — nested workflow invocation ──',
|
|
426
|
+
' async function workflow(name, args) {',
|
|
427
|
+
' if (typeof name !== "string" || name.length === 0) {',
|
|
428
|
+
' throw new Error("workflow() requires a workflow name string as first argument");',
|
|
429
|
+
' }',
|
|
430
|
+
' const workflowArgs = (typeof args === "object" && args !== null) ? args : {};',
|
|
431
|
+
' const callId = _callIdCounter;',
|
|
432
|
+
' _callIdCounter++;',
|
|
433
|
+
' if (!_safePost({ type: "workflow-call", callId, name, args: workflowArgs }, "workflow-call")) {',
|
|
434
|
+
' return Promise.reject(new Error("postMessage failed for workflow-call (name=" + name + "): see workerLogs"));',
|
|
435
|
+
' }',
|
|
436
|
+
' return new Promise((resolve, reject) => {',
|
|
437
|
+
' _pendingCalls.set(callId, { resolve, reject });',
|
|
438
|
+
' });',
|
|
439
|
+
' }',
|
|
440
|
+
'',
|
|
441
|
+
' // ── User workflow script ──',
|
|
442
|
+
].join("\n");
|
|
388
443
|
|
|
389
444
|
/** POST 段:auto-invoke execute() + 顶层 .then/.catch return/error handler。 */
|
|
390
445
|
const WORKER_TEMPLATE_POST = [
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
446
|
+
' // ── Auto-invoke execute() for module.exports pattern ──',
|
|
447
|
+
' if (typeof module !== "undefined" && module.exports && typeof module.exports.execute === "function") {',
|
|
448
|
+
' return await module.exports.execute({ agent, parallel, pipeline, phase, log, workflow, $ARGS, $WORKSPACE, $BUDGET });',
|
|
449
|
+
' }',
|
|
450
|
+
'})().then((result) => {',
|
|
451
|
+
' const runId = (_workerData.args && typeof _workerData.args === "object" && _workerData.args._runId) || "";',
|
|
452
|
+
' if (!_safePost({ type: "return", runId, result, workerLogs: _workerLogs }, "return")) {',
|
|
453
|
+
' // [F1] return 值不可克隆(含 function/Symbol/循环引用 → DataCloneError)时 _safePost',
|
|
454
|
+
' // 只能记日志返回 false——若不补救,worker 将静默 exit(0),主线程收不到任何终态消息,',
|
|
455
|
+
' // run 永久 running、runAndWait 悬挂。回发可克隆的 error 消息(DataCloneError 详情',
|
|
456
|
+
' // 已由 _safePost 记入 _workerLogs 随消息带回),让主线程 handleScriptError 接管,',
|
|
457
|
+
' // run 经既有重试矩阵收敛到终态 failed。',
|
|
458
|
+
' _safePost({ type: "error", runId, error: "Workflow return value could not be delivered (structured-clone failed) — see workerLogs for the postMessage error", workerLogs: _workerLogs }, "error");',
|
|
459
|
+
' }',
|
|
460
|
+
'}).catch((err) => {',
|
|
461
|
+
' const runId = (_workerData.args && typeof _workerData.args === "object" && _workerData.args._runId) || "";',
|
|
462
|
+
' _safePost({ type: "error", runId, error: err.message || String(err), workerLogs: _workerLogs }, "error");',
|
|
463
|
+
'});',
|
|
464
|
+
].join("\n");
|
|
410
465
|
|
|
411
466
|
/**
|
|
412
467
|
* Build the complete worker source text by wrapping the user's workflow script
|