@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
|
@@ -203,44 +203,44 @@ export async function executeAgentCall(
|
|
|
203
203
|
|
|
204
204
|
const result = await runner.run(call.opts, signal, onEvent, stream);
|
|
205
205
|
|
|
206
|
-
|
|
206
|
+
// 累加 usage(加权由 budget.consume 内部按权重常量处理,见 budget.ts)
|
|
207
207
|
if (result.usage) {
|
|
208
208
|
budget.consume(result.usage);
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
|
|
211
|
+
// stale-context:不重试(P1-5)
|
|
212
212
|
if (result.error !== undefined && isStaleContextErrorMsg(result.error)) {
|
|
213
213
|
finalizeCall(call, result, trace, isOrphaned);
|
|
214
214
|
budget.incrementCallCount();
|
|
215
215
|
return;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
|
|
219
|
-
|
|
218
|
+
// [MF-1] 确定性 schema 失败:不重试(gate 终止/不可满足 schema 同 schema 重试必同
|
|
219
|
+
// 结果——重试纯烧钱;三态可重试性矩阵见 DETERMINISTIC_SCHEMA_FAILURE_PREFIX)
|
|
220
220
|
if (result.error !== undefined && isDeterministicSchemaFailureMsg(result.error)) {
|
|
221
221
|
finalizeCall(call, result, trace, isOrphaned);
|
|
222
222
|
budget.incrementCallCount();
|
|
223
223
|
return;
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
-
|
|
226
|
+
// signal 已 abort:调用方终止,不重试(避免无意义的递归)
|
|
227
227
|
if (signal.aborted) {
|
|
228
228
|
finalizeCall(call, result, trace, isOrphaned);
|
|
229
229
|
budget.incrementCallCount();
|
|
230
230
|
return;
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
-
|
|
233
|
+
// 预算超限:不重试(重试只会突破预算且无意义)
|
|
234
234
|
if (result.error !== undefined && budget.isExceeded()) {
|
|
235
235
|
finalizeCall(call, result, trace, isOrphaned);
|
|
236
236
|
budget.incrementCallCount();
|
|
237
237
|
return;
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
|
|
240
|
+
// 可重试失败:退避后递归
|
|
241
241
|
if (result.error !== undefined && call.attempts < MAX_ATTEMPTS) {
|
|
242
242
|
await delay(backoffDelay(call.attempts));
|
|
243
|
-
|
|
243
|
+
// 退避期间 signal 可能 abort
|
|
244
244
|
if (signal.aborted) {
|
|
245
245
|
finalizeCall(call, result, trace, isOrphaned);
|
|
246
246
|
budget.incrementCallCount();
|
|
@@ -250,7 +250,7 @@ export async function executeAgentCall(
|
|
|
250
250
|
return;
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
-
|
|
253
|
+
// 终态(成功或达到重试上限的失败)
|
|
254
254
|
finalizeCall(call, result, trace, isOrphaned);
|
|
255
255
|
budget.incrementCallCount();
|
|
256
256
|
}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
// src/orchestration/file-run-store.ts
|
|
2
|
+
//
|
|
3
|
+
// RunStore port 的通用文件实现(D2 设计件——zsw 回接 host-surface 单元)。
|
|
4
|
+
//
|
|
5
|
+
// 为什么需要它:pi 壳的 JsonlRunStore 深耦合 pi session(appendEntry /
|
|
6
|
+
// sessionManager,经 pi SDK 落盘 session JSONL),zcode 侧宿主没有这两个设施,
|
|
7
|
+
// 无法复用。RunStore port 早在 ports.ts 定义却只有 pi 一份 Infra 实现——本文件
|
|
8
|
+
// 补上「宿主无关」的第二份实现,双宿主的 workflow state 持久化从此同源(消灭
|
|
9
|
+
// 失败模式 B:行为不一致各自修)。
|
|
10
|
+
//
|
|
11
|
+
// 落盘布局:<dataRoot>/workflow-state/<runId>.jsonl(D2 规定,与 pi 壳
|
|
12
|
+
// <sessionDir>/workflow-state/<runId>.jsonl 同名分量、锚点不同:pi 锚 session,
|
|
13
|
+
// 本实现锚宿主数据根——zcode 宿主无 session dir 概念,daemon 重启后按 dataRoot
|
|
14
|
+
// 重水合孤儿 run)。
|
|
15
|
+
//
|
|
16
|
+
// dataRoot 通道选型:直接走 getHostServices().dataRoot()(core/host-services.ts),
|
|
17
|
+
// 不用 getEngineDataDir(engine/common/data-dir.ts)——后者是引擎 journal/隔离池
|
|
18
|
+
// 通道,带 XYZ_AGENT_DATA_DIR env 优先 + warn-once 语义(xyz-agent 宿主注入专用);
|
|
19
|
+
// workflow run 快照是宿主编排状态,语义归属宿主数据根本身,宿主 configureCore
|
|
20
|
+
// 注入什么就落什么,不引入第二条 env 覆盖链。
|
|
21
|
+
|
|
22
|
+
import { appendFile, mkdir, readdir, readFile, stat, unlink } from "node:fs/promises";
|
|
23
|
+
import { join } from "node:path";
|
|
24
|
+
|
|
25
|
+
import { getHostServices } from "../core/host-services.ts";
|
|
26
|
+
import { getLogger } from "../core/logger.ts";
|
|
27
|
+
import type { RunStore } from "./models/ports.ts";
|
|
28
|
+
import { WorkflowRun } from "./models/workflow-run.ts";
|
|
29
|
+
import { SNAPSHOT_VERSION, fromRunSnapshot, toRunSnapshot } from "./run-snapshot.ts";
|
|
30
|
+
|
|
31
|
+
const logger = getLogger("file-run-store");
|
|
32
|
+
|
|
33
|
+
/** run 状态目录名(<dataRoot> 下的固定分量)。 */
|
|
34
|
+
const STATE_DIR_NAME = "workflow-state";
|
|
35
|
+
|
|
36
|
+
// ── 磁盘保留(C1,语义对齐 pi jsonl-run-store mtime 裁剪) ─────────
|
|
37
|
+
|
|
38
|
+
/** run state 文件名 glob:runId 形如 `wf-<ts>-<rand>`(lifecycle.ts 生成),只删命中者。
|
|
39
|
+
* 同目录可能存在的非 state 文件永不碰(对齐 pi STATE_FILE_GLOB)。 */
|
|
40
|
+
const STATE_FILE_GLOB = /^wf-.*\.jsonl$/;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 磁盘保留默认上限(OR-5 跨 run 保留修复):envName 通道在 env 未设/空时生效。
|
|
44
|
+
*
|
|
45
|
+
* OR-5 将「STATE_MAX_RUNS opt-in 默认关」(无界累积)改为默认开:跨 run state
|
|
46
|
+
* 文件按 mtime 裁剪到本上限。取值 50 是无真实 run 体积分布数据下的保守值
|
|
47
|
+
* (设计 §11-4:标定待 S-A 验收后复核)——偏大不碍事(有界即达标),偏小会
|
|
48
|
+
* 误删仍被引用的 run 缓存,故取保守端。env 显式设置(有效正数)优先于本值;
|
|
49
|
+
* 显式非法值是 opt-out 通道(不清理,见 pruneStateFilesBeyondCap)。
|
|
50
|
+
*/
|
|
51
|
+
export const DEFAULT_STATE_MAX_RUNS = 50;
|
|
52
|
+
|
|
53
|
+
// ── save 节流(OR-5 单 run 快照 O(n²) 主修) ──────────────────
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 同一 run 两次快照落盘的最小间隔(ms)。OR-5 单 run O(n²) 主修参数:现状每
|
|
57
|
+
* 次 save 都 append 全量快照(快照体积 O(calls) × save 次数 O(calls)),节流后
|
|
58
|
+
* 落盘次数有界为 ceil(run 时长 / 本间隔)(§11-4 量级推演见 impl-plan 偏差登记:
|
|
59
|
+
* 100-call run 从 ~200 次落盘 / ~50MB 降到 ~17 次 / ~8MB,增量 append diff 需
|
|
60
|
+
* 改造两宿主共享 codec(基线+delta 行 + loadAll 重放 + 版本兼容),收益不抵
|
|
61
|
+
* 复杂度,节流即终案)。取值对齐 jsonl-run-store 去抖同款考量:agent-call 间隔
|
|
62
|
+
* 秒级,60s 窗口把快照次数压到与「分钟级 run 时长」同量级,又不让崩溃窗口
|
|
63
|
+
* (未落盘的 running 尾部丢失,等价崩溃链由恢复路径收编)超出分钟级。
|
|
64
|
+
*/
|
|
65
|
+
export const DEFAULT_SAVE_MIN_INTERVAL_MS = 60_000;
|
|
66
|
+
|
|
67
|
+
/** FileRunStore 构造参数(全部可选;缺省即生产形态)。 */
|
|
68
|
+
export interface FileRunStoreOptions {
|
|
69
|
+
/**
|
|
70
|
+
* save 节流最小间隔(ms);0 = 禁用节流(每次 save 都落盘)。缺省
|
|
71
|
+
* {@link DEFAULT_SAVE_MIN_INTERVAL_MS}。测试经此注入小窗口(fake timers 推进)。
|
|
72
|
+
*/
|
|
73
|
+
saveMinIntervalMs?: number;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Node fs 错误 code 判定(ENOENT = 路径不存在,并发删除场景;对齐 pi isEnoentError)。 */
|
|
77
|
+
function isEnoentError(err: unknown): boolean {
|
|
78
|
+
return typeof err === "object" && err !== null && "code" in err &&
|
|
79
|
+
(err as { code?: unknown }).code === "ENOENT";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ── 快照形状 / 序列化 / 重水合 ────────────────────────────────
|
|
83
|
+
//
|
|
84
|
+
// 投影与版本衔接语义收敛于 ./run-snapshot.ts 单源 codec(下沉收口 D4/U8):
|
|
85
|
+
// 本 store 只保留 IO 策略(append-only + 从尾向头取最后有效行)。版本衔接的
|
|
86
|
+
// 宿主侧职责(D4 裁决②③,见 parseLine):「缺 v 宽容读」预处理与「版本不
|
|
87
|
+
// 匹配 warn 可见性」在此实现——不内聚进 codec,保 pi 侧「v1 存量静默跳过」
|
|
88
|
+
// 语义不被宽容化误读。
|
|
89
|
+
|
|
90
|
+
// ── FileRunStore ────────────────────────────────────────────
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* RunStore port 的宿主无关文件实现(port 见 models/ports.ts)。
|
|
94
|
+
*
|
|
95
|
+
* - save:append-only + 节流——快照行仍全量(崩溃时旧快照仍在,loadAll 取最后
|
|
96
|
+
* 一条有效行恢复到最后一致状态),但同一 running run 两次落盘有最小间隔
|
|
97
|
+
* (OR-5 ⑥a:节流前每次状态变更都 append 全量快照,快照体积 O(calls) ×
|
|
98
|
+
* save 次数 O(calls) = 单 run 磁盘 O(n²);节流参数与语义见 save 注释)。
|
|
99
|
+
* - loadAll:扫 <dataRoot>/workflow-state/*.jsonl,每文件从尾向头取第一条形状
|
|
100
|
+
* 有效的快照行;损坏行(JSON.parse 失败 / 形状校验不过 / 版本不匹配)跳过并
|
|
101
|
+
* warn——单行损坏不拖垮整个 run 的恢复(与 pi 壳 kill-9 恢复同容忍度)。
|
|
102
|
+
* 版本衔接(快照 codec 归 run-snapshot.ts 单源,D4):存量无 v 行按当前版本
|
|
103
|
+
* 宽容读、写入恒补 v、v 不匹配跳过 + warn(三裁决明细见 parseLine 注释)。
|
|
104
|
+
* - stateFilePath:纯路径计算(<dataRoot>/workflow-state/<runId>.jsonl),不建目录。
|
|
105
|
+
*
|
|
106
|
+
* 未 configureCore 即 save/loadAll 会抛 core_host_not_configured(dataRoot 端口
|
|
107
|
+
* 语义,host-services.ts §3.4)——宿主壳必须在初始化最早期注入。
|
|
108
|
+
*/
|
|
109
|
+
export class FileRunStore implements RunStore {
|
|
110
|
+
/** run 状态目录绝对路径(dataRoot 每次现取——宿主覆盖配置即刻生效,对齐
|
|
111
|
+
* data-dir.ts「不缓存路径防测试/宿主切换读到旧值」先例)。 */
|
|
112
|
+
private stateDir(): string {
|
|
113
|
+
return join(getHostServices().dataRoot(), STATE_DIR_NAME);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** save 节流最小间隔(ms),0 = 禁用。 */
|
|
117
|
+
private readonly saveMinIntervalMs: number;
|
|
118
|
+
/**
|
|
119
|
+
* per-runId 上次实际落盘时刻(节流判据)。终态落盘成功即删(终态后 runId 不再
|
|
120
|
+
* save);残留条目只出现在「running 中 run 消失(崩溃/宿主弃用)」场景,单条
|
|
121
|
+
* ~100B 可忽略(对齐 jsonl-run-store chains「每 runId 残留 settled Promise」
|
|
122
|
+
* 的取舍先例)。时间源 Date.now()(fake timers 下可推进,测试友好)。
|
|
123
|
+
*/
|
|
124
|
+
private readonly lastSavedAt = new Map<string, number>();
|
|
125
|
+
|
|
126
|
+
constructor(opts?: FileRunStoreOptions) {
|
|
127
|
+
this.saveMinIntervalMs = Math.max(0, opts?.saveMinIntervalMs ?? DEFAULT_SAVE_MIN_INTERVAL_MS);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
stateFilePath(runId: string): string {
|
|
131
|
+
return join(this.stateDir(), `${runId}.jsonl`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* 快照落盘(OR-5 ⑥a 节流后):
|
|
136
|
+
* - 首写(该 runId 尚无落盘记录)永不节流——保证新 run 至少一条快照,
|
|
137
|
+
* loadAll 重水合可发现;
|
|
138
|
+
* - 终态(status 非 running)永不节流——最终状态必落盘,末行即终态快照;
|
|
139
|
+
* - running 中间态距上次落盘不足 {@link saveMinIntervalMs} → 跳过本次 append
|
|
140
|
+
* (状态仍在调用方内存 runs Map,下次落盘带全量最新快照;本文件最后一条
|
|
141
|
+
* 快照因此最多落后真实状态一个节流窗口——崩溃语义与 jsonl-run-store 去抖
|
|
142
|
+
* 同源:未落盘的 running 尾部丢失,等价崩溃链由恢复路径收编)。
|
|
143
|
+
*
|
|
144
|
+
* 节流判据在落盘成功后才更新(IO 失败不吞下一次重试机会)。
|
|
145
|
+
*/
|
|
146
|
+
async save(run: WorkflowRun): Promise<void> {
|
|
147
|
+
const isTerminal = run.state.status !== "running";
|
|
148
|
+
const now = Date.now();
|
|
149
|
+
const last = this.lastSavedAt.get(run.runId);
|
|
150
|
+
if (!isTerminal && last !== undefined && now - last < this.saveMinIntervalMs) {
|
|
151
|
+
return; // 节流窗口内:跳过本次全量快照 append
|
|
152
|
+
}
|
|
153
|
+
// mkdir recursive 每次 save 前执行:幂等零成本(目录已存在时仅一次 stat),
|
|
154
|
+
// 且免「构造时预建」——构造时建会在宿主尚未 configureCore 的窗口抛错。
|
|
155
|
+
await mkdir(this.stateDir(), { recursive: true });
|
|
156
|
+
// toRunSnapshot 补 v 字段(D4 裁决②写入侧)+ strip live 落盘
|
|
157
|
+
const line = JSON.stringify(toRunSnapshot(run));
|
|
158
|
+
await appendFile(this.stateFilePath(run.runId), line + "\n", "utf8");
|
|
159
|
+
if (isTerminal) {
|
|
160
|
+
this.lastSavedAt.delete(run.runId);
|
|
161
|
+
} else {
|
|
162
|
+
this.lastSavedAt.set(run.runId, now);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async loadAll(): Promise<WorkflowRun[]> {
|
|
167
|
+
let files: string[];
|
|
168
|
+
try {
|
|
169
|
+
files = await readdir(this.stateDir());
|
|
170
|
+
} catch {
|
|
171
|
+
// 目录不存在 = 从未持久化过(首启/干净环境),空集是正常态不是错误。
|
|
172
|
+
return [];
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const runs: WorkflowRun[] = [];
|
|
176
|
+
for (const file of files) {
|
|
177
|
+
if (!file.endsWith(".jsonl")) continue;
|
|
178
|
+
const run = await this.loadLatestValidLine(join(this.stateDir(), file), file);
|
|
179
|
+
if (run) runs.push(run);
|
|
180
|
+
}
|
|
181
|
+
return runs;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** 单文件从尾向头取第一条有效快照行;整文件无有效行返回 undefined(warn)。 */
|
|
185
|
+
private async loadLatestValidLine(absPath: string, display: string): Promise<WorkflowRun | undefined> {
|
|
186
|
+
let content: string;
|
|
187
|
+
try {
|
|
188
|
+
content = await readFile(absPath, "utf8");
|
|
189
|
+
} catch (err) {
|
|
190
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
191
|
+
logger.warn(`[file-run-store] skip unreadable state file ${display}: ${msg}`);
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const lines = content.split("\n");
|
|
196
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
197
|
+
const line = lines[i].trim();
|
|
198
|
+
if (line === "") continue; // 尾部空行(末行 \n 产物)静默跳过
|
|
199
|
+
const run = this.parseLine(line, display, i);
|
|
200
|
+
if (run) return run;
|
|
201
|
+
// 损坏行 warn 后继续向前找——最后一条「有效」行可能早于文件尾部(半行写入崩溃)
|
|
202
|
+
}
|
|
203
|
+
logger.warn(`[file-run-store] no valid snapshot line in ${display} (empty or all corrupted)`);
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* 单行解析 + 版本衔接预处理(D4 裁决②③,宿主侧职责)+ 形状校验;损坏
|
|
209
|
+
* warn 并返回 undefined。
|
|
210
|
+
*
|
|
211
|
+
* - 缺 v 字段(core 存量行)→ 就地补当前版本再进 codec(「缺版本 = 当前
|
|
212
|
+
* 版本」宽容读,不做自动迁移——写回时经 toRunSnapshot 自然补 v 完成渐进
|
|
213
|
+
* 收敛);预处理留在 store 层而非 codec,保 pi 侧「v1 存量静默跳过」语义
|
|
214
|
+
* 不被宽容化误读(D4 裁决②归属裁决)。
|
|
215
|
+
* - v 存在但不匹配(未知更高版本/降级写入)→ 跳过 + warn(补可见性,对齐
|
|
216
|
+
* pi 静默跳过语义;字符串版本无大小序,不引入比较逻辑——D4 裁决③)。
|
|
217
|
+
* 此处版本判断仅为 warn 可见性,数据防线仍是 codec 内 guard(双保险,
|
|
218
|
+
* pi 切换 codec 后共享同一防线)。
|
|
219
|
+
*/
|
|
220
|
+
private parseLine(line: string, display: string, lineNo: number): WorkflowRun | undefined {
|
|
221
|
+
let parsed: unknown;
|
|
222
|
+
try {
|
|
223
|
+
parsed = JSON.parse(line);
|
|
224
|
+
} catch (err) {
|
|
225
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
226
|
+
logger.warn(`[file-run-store] skip corrupted line ${display}:${lineNo}: ${msg}`);
|
|
227
|
+
return undefined;
|
|
228
|
+
}
|
|
229
|
+
if (parsed !== null && typeof parsed === "object") {
|
|
230
|
+
const rec = parsed as { v?: unknown };
|
|
231
|
+
if (rec.v === undefined) {
|
|
232
|
+
rec.v = SNAPSHOT_VERSION;
|
|
233
|
+
} else if (rec.v !== SNAPSHOT_VERSION) {
|
|
234
|
+
logger.warn(
|
|
235
|
+
`[file-run-store] skip snapshot with unsupported version ${display}:${lineNo}: v=${JSON.stringify(rec.v)} (this build only reads v=${JSON.stringify(SNAPSHOT_VERSION)}; the run line is skipped). To recover: upgrade @zhushanwen/subagent-core, or migrate/delete this state file if its runs are no longer needed`,
|
|
236
|
+
);
|
|
237
|
+
return undefined;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const run = fromRunSnapshot(parsed);
|
|
241
|
+
if (run === undefined) {
|
|
242
|
+
logger.warn(`[file-run-store] skip malformed snapshot ${display}:${lineNo} (shape validation failed)`);
|
|
243
|
+
return undefined;
|
|
244
|
+
}
|
|
245
|
+
return run;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* 把 workflow-state 目录裁剪到上限个最新 state 文件(mtime 升序删最旧,C1)。
|
|
250
|
+
*
|
|
251
|
+
* 语义对齐 pi jsonl-run-store.pruneStateFilesBeyondCap(逐段同构):
|
|
252
|
+
* - 只删本目录内命中 {@link STATE_FILE_GLOB} 的文件;任何失败都不抛(清理是
|
|
253
|
+
* 旁路维护,不能拖垮持久化主链路):readdir 失败静默放弃本轮(ENOENT =
|
|
254
|
+
* 从未持久化,正常态),单个 unlink 失败(非 ENOENT)warn 留证后继续删
|
|
255
|
+
* 其余——ENOENT 视为并发删除竞态下的已达成目标,不告警;
|
|
256
|
+
* - stat 全集取 mtime,allSettled 部分降级——单文件 stat 失败(并发删除
|
|
257
|
+
* ENOENT 等)静默跳过该文件,不阻断本轮裁剪。
|
|
258
|
+
*
|
|
259
|
+
* 上限解析(envName 通道,OR-5 ⑥b 默认开;显式非法值 opt-out 对齐 pi 解析风格):
|
|
260
|
+
* - `envName` 提供 → env 通道:`process.env[envName]` 未设/空 → 按默认上限
|
|
261
|
+
* {@link DEFAULT_STATE_MAX_RUNS} 裁剪(**默认开**——OR-5 修复前的 opt-in
|
|
262
|
+
* 「默认关」正是跨 run 无界累积缺陷本身);设了有限正数 → 上限 = env 值
|
|
263
|
+
* (env 值即上限);设了非法值(非有限数/≤0)→ 不清理(显式 opt-out 通道:
|
|
264
|
+
* 用户意图不明时不动磁盘——对齐本方法 readdir/stat 失败一律放弃的保守哲学,
|
|
265
|
+
* 宿主如需自管保留可设足够大的正数值);
|
|
266
|
+
* - `envName` 缺省 → 无 env 通道,直接按 `max` 参数裁剪(上限 = max,调用方
|
|
267
|
+
* 自管启用时机)。
|
|
268
|
+
*
|
|
269
|
+
* 本方法只做磁盘裁剪,不动内存 runs Map(内存侧淘汰归
|
|
270
|
+
* lifecycle.evictDoneRunsBeyondCap,两域独立)。
|
|
271
|
+
*
|
|
272
|
+
* @param max 上限(envName 缺省时生效;env 通道启用时被 env 值覆盖)
|
|
273
|
+
* @param envName opt-in 开关 + 上限覆盖 env 变量名(可选;pi 先例
|
|
274
|
+
* `XYZ_SUBAGENT_STATE_MAX_RUNS`)
|
|
275
|
+
*/
|
|
276
|
+
async pruneStateFilesBeyondCap(max: number, envName?: string): Promise<void> {
|
|
277
|
+
let cap = max;
|
|
278
|
+
if (envName !== undefined) {
|
|
279
|
+
// 未设/空 → 默认开(OR-5 ⑥b:DEFAULT_STATE_MAX_RUNS);非法/≤0 → 不清理
|
|
280
|
+
// (显式 opt-out 通道,见方法注释);有效正数 → env 值覆盖
|
|
281
|
+
const raw = process.env[envName];
|
|
282
|
+
if (raw === undefined || raw === "") {
|
|
283
|
+
cap = DEFAULT_STATE_MAX_RUNS;
|
|
284
|
+
} else {
|
|
285
|
+
const parsed = Number(raw);
|
|
286
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return;
|
|
287
|
+
cap = parsed;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const stateDir = this.stateDir();
|
|
292
|
+
let names: string[];
|
|
293
|
+
try {
|
|
294
|
+
names = await readdir(stateDir);
|
|
295
|
+
} catch (err) {
|
|
296
|
+
if (!isEnoentError(err)) {
|
|
297
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
298
|
+
logger.warn(`[file-run-store] state retention: readdir ${stateDir} failed: ${reason}`);
|
|
299
|
+
}
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
const stateFiles = names.filter((n) => STATE_FILE_GLOB.test(n)).sort();
|
|
303
|
+
if (stateFiles.length <= cap) return;
|
|
304
|
+
|
|
305
|
+
// stat 全集取 mtime;allSettled 部分降级(单文件失败静默跳过,不阻断本轮)
|
|
306
|
+
const settled = await Promise.allSettled(
|
|
307
|
+
stateFiles.map(async (name) => {
|
|
308
|
+
const full = join(stateDir, name);
|
|
309
|
+
return { full, mtimeMs: (await stat(full)).mtimeMs };
|
|
310
|
+
}),
|
|
311
|
+
);
|
|
312
|
+
const byMtimeAsc = settled
|
|
313
|
+
.flatMap((r) => (r.status === "fulfilled" ? [r.value] : []))
|
|
314
|
+
.sort((a, b) => a.mtimeMs - b.mtimeMs);
|
|
315
|
+
const victims = byMtimeAsc.slice(0, byMtimeAsc.length - cap);
|
|
316
|
+
for (const victim of victims) {
|
|
317
|
+
try {
|
|
318
|
+
await unlink(victim.full);
|
|
319
|
+
logger.debug(`[file-run-store] state retention: pruned ${victim.full}`);
|
|
320
|
+
} catch (err) {
|
|
321
|
+
if (isEnoentError(err)) continue; // 并发删除已达成目标
|
|
322
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
323
|
+
logger.warn(`[file-run-store] state retention: failed to delete ${victim.full}: ${reason}`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
@@ -184,11 +184,11 @@ async function pollRunToResult(
|
|
|
184
184
|
return finalRun
|
|
185
185
|
? toResult(finalRun)
|
|
186
186
|
: {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
187
|
+
status: "done",
|
|
188
|
+
reason: "time_limited",
|
|
189
|
+
error: `Workflow timed out after ${explicitTimeoutMs}ms`,
|
|
190
|
+
runId,
|
|
191
|
+
};
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
// ── runAndWait ───────────────────────────────────────────────
|
|
@@ -214,6 +214,11 @@ async function pollRunToResult(
|
|
|
214
214
|
* @param timeoutMs 超时上限(可选)。[预算语义对齐 + U2] 未传或 <=0 = 不限(轮询至 done /
|
|
215
215
|
* abort 为止,不限时由 XYZ_SUBAGENT_RUN_WATCHDOG_MS 兜底)——旧实现默认 10 分钟会误杀长任务,
|
|
216
216
|
* 且 0/负值会落成立即超时;仅显式正数才限时。
|
|
217
|
+
* @param model Run 级 model override(可选)。[host-surface] 经 spec.model → workerData →
|
|
218
|
+
* $MODEL → agent() fallback(RunSpec Option B 同一路径)。缺省不覆盖——宿主未显式指定
|
|
219
|
+
* model 时维持脚本内 agent() 显式参数 / agent .md / 引擎默认的既有解析序。此前该入口
|
|
220
|
+
* 无 model 通道,消费方(zsw CLI --model)只能丢弃该参数——行为劈叉于走 buildSpec 的
|
|
221
|
+
* 异步入口。
|
|
217
222
|
* @returns WorkflowRunResult(status 恒 "done")
|
|
218
223
|
*/
|
|
219
224
|
export async function runAndWait(
|
|
@@ -222,8 +227,9 @@ export async function runAndWait(
|
|
|
222
227
|
deps: LauncherDeps,
|
|
223
228
|
signal?: AbortSignal,
|
|
224
229
|
timeoutMs?: number,
|
|
230
|
+
model?: string,
|
|
225
231
|
): Promise<WorkflowRunResult> {
|
|
226
|
-
|
|
232
|
+
// 1. registry 查找脚本(workflowRef = 绝对路径,S2 路径统一)
|
|
227
233
|
const script = await deps.registry.getPath(name);
|
|
228
234
|
if (!script) {
|
|
229
235
|
return {
|
|
@@ -234,7 +240,7 @@ export async function runAndWait(
|
|
|
234
240
|
};
|
|
235
241
|
}
|
|
236
242
|
|
|
237
|
-
|
|
243
|
+
// 2. lint 校验(失败抛错——脚本本身有问题,不应静默吞)
|
|
238
244
|
const lintResult = script.validate();
|
|
239
245
|
if (!lintResult.valid) {
|
|
240
246
|
const errors = lintResult.findings
|
|
@@ -244,26 +250,29 @@ export async function runAndWait(
|
|
|
244
250
|
throw new Error(`Workflow script '${name}' has lint errors: ${errors}`);
|
|
245
251
|
}
|
|
246
252
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
253
|
+
// 3. 构建 RunSpec
|
|
254
|
+
// 不设 budgetTimeMs:runAndWait 自身用轮询 deadline(pollRunToResult 内 while + safeAbort)
|
|
255
|
+
// 实施 timeout,并产出「Workflow timed out after Xms」的具体错误信息。spec 级
|
|
256
|
+
// 时间预算(lifecycle.scheduleTimeBudget)服务于 fire-and-forget 的交互式 run
|
|
257
|
+
// (tool-workflow actionRun),若在此也设会与轮询 deadline 同时触发产生竞态。
|
|
258
|
+
// model 是例外:时间预算必须二选一(竞态),model 只有单通道(spec Option B),
|
|
259
|
+
// 调用方显式传入即透传,不与任何轮询面竞争。
|
|
252
260
|
const spec: RunSpec = {
|
|
253
261
|
scriptSource: script.toExecutable(),
|
|
254
262
|
args,
|
|
255
263
|
budgetTokens: undefined,
|
|
264
|
+
model,
|
|
256
265
|
scriptName: script.name,
|
|
257
266
|
scriptPath: script.path,
|
|
258
267
|
description: script.meta.description,
|
|
259
268
|
parameters: script.meta.parameters,
|
|
260
269
|
};
|
|
261
270
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
271
|
+
// 4. 启动 workflow + 5. 轮询至 done(含 6. timeout → abortRun,C.7)
|
|
272
|
+
// pending-notification 的 register/unregister 由 runWorkflow(启动注册)+
|
|
273
|
+
// transition("done") 路径(完成注销)统一处理,runAndWait 不再重复 emit。
|
|
274
|
+
// m3:chokepoint 校验失败(ArgsValidationError)→ 返回 invalid_args 结果(run 从未
|
|
275
|
+
// 创建,runId 恒 ''),非 ArgsValidationError 保持传播。
|
|
267
276
|
let runId: string;
|
|
268
277
|
try {
|
|
269
278
|
runId = await runWorkflow(spec, deps, signal);
|
|
@@ -296,7 +305,7 @@ async function safeAbort(
|
|
|
296
305
|
try {
|
|
297
306
|
await abortRun(runId, deps, reason, doneReason);
|
|
298
307
|
} catch (err) {
|
|
299
|
-
|
|
308
|
+
// run 可能已终态或不存在——忽略,调用方据 toResult 判断
|
|
300
309
|
void err;
|
|
301
310
|
}
|
|
302
311
|
}
|
|
@@ -332,7 +341,7 @@ export async function executeNestedWorkflow(
|
|
|
332
341
|
parentRun: WorkflowRun,
|
|
333
342
|
deps: LauncherDeps,
|
|
334
343
|
): Promise<{ content: string; parsedOutput?: unknown; error?: string }> {
|
|
335
|
-
|
|
344
|
+
// Step 1: 循环检测——parentWorkflowChain 不存在时为 [](顶层 run)
|
|
336
345
|
const chain = [
|
|
337
346
|
...(parentRun.spec.parentWorkflowChain ?? []),
|
|
338
347
|
parentRun.spec.scriptName,
|
|
@@ -344,9 +353,9 @@ export async function executeNestedWorkflow(
|
|
|
344
353
|
};
|
|
345
354
|
}
|
|
346
355
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
356
|
+
// Step 2: signal 继承——子 run 响应父 run abort
|
|
357
|
+
// [L-2] 提取命名 onParentAbort 以便 finally removeEventListener,防子 run 完成后
|
|
358
|
+
// parentSignal 上残留 listener(多次嵌套调用会累积)。
|
|
350
359
|
const childController = new AbortController();
|
|
351
360
|
const parentSignal = parentRun.runtime?.controller.signal;
|
|
352
361
|
const onParentAbort = (): void => childController.abort();
|
|
@@ -358,10 +367,10 @@ export async function executeNestedWorkflow(
|
|
|
358
367
|
}
|
|
359
368
|
}
|
|
360
369
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
370
|
+
// Step 3+:registry 查找 + lint + RunSpec + runWorkflow + poll 全程 try(m3 E8——
|
|
371
|
+
// try 起点提到 Step 2 的 listener 注册之后,覆盖 Step 3-6。runWorkflow throw
|
|
372
|
+
// (含 chokepoint ArgsValidationError)与 not found/lint 早返回均走 finally 移除
|
|
373
|
+
// parentSignal listener——修复原 try 外 runWorkflow 的泄漏路径)。
|
|
365
374
|
try {
|
|
366
375
|
// Step 3: registry 查找 + lint(失败返回 error result,不抛错)
|
|
367
376
|
const script = await deps.registry.getPath(name);
|