@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
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow 脚本创作管线(generate)——pi-sw tool-workflow-script.ts actionGenerate
|
|
3
|
+
* 的校验管线下沉(convergence D-6 / W4)。
|
|
4
|
+
*
|
|
5
|
+
* 五道闸 + round-trip + tmp 写盘,闸序与 pi 现版一致:
|
|
6
|
+
* 1. ESM import 拒绝(Worker 跑 CJS);'export const meta' 例外
|
|
7
|
+
* 2. 非 meta 的 export 拒绝
|
|
8
|
+
* 3. meta 声明必需(@pi-meta YAML 块注释或 legacy const meta,过渡期 m0)
|
|
9
|
+
* 4. agent() 调用必需
|
|
10
|
+
* 5. 语法检查(包 async IIFE,与 runtime 包裹形态一致)
|
|
11
|
+
* 6. round-trip:@pi-meta 存在时 parseResourceMetaDetailed 校验 YAML(报行列)
|
|
12
|
+
* 7. 通过全部校验 → tmp 目录写盘
|
|
13
|
+
*
|
|
14
|
+
* 与 pi 版的差异(均为宿主职责,不属纯函数边界):
|
|
15
|
+
* - 不含 signal aborted 检查(AbortSignal 是 pi tool 契约层关注,宿主改接时自留);
|
|
16
|
+
* - 返回结构化结果而非 throw——pi 只对 execute throw 置 isError:true 的契约
|
|
17
|
+
* 转换由宿主(C5 改接)负责,core 保持纯函数。
|
|
18
|
+
*
|
|
19
|
+
* 报错文案逐字平移自 pi 现版(含 round-trip 的 line/col 信息)——pi 侧行为
|
|
20
|
+
* 不变是 CA2 验收前提,任何文案改动必须同步两处。
|
|
21
|
+
*
|
|
22
|
+
* tmp 目录经 options.tmpDir 宿主注入(缺省 pi 布局,与 workflow-files.ts
|
|
23
|
+
* 同源常量——save/delete/generate 三入口共享同一目录参数化口径)。
|
|
24
|
+
*
|
|
25
|
+
* 层归属:orchestration(创作闭环,与 lintScript / workflow-files 同域)。
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
29
|
+
import { resolve } from "node:path";
|
|
30
|
+
|
|
31
|
+
import { parseResourceMetaDetailed } from "../shared/meta-parser.ts";
|
|
32
|
+
import { DEFAULT_WORKFLOW_TMP_DIR } from "./workflow-files.ts";
|
|
33
|
+
|
|
34
|
+
/** generate 目录注入参数:tmp 落盘目录宿主注入(缺省 DEFAULT_WORKFLOW_TMP_DIR)。 */
|
|
35
|
+
export interface GenerateWorkflowScriptOptions {
|
|
36
|
+
tmpDir?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 管线结果:成功 = 落盘绝对路径;失败 = 报错文案(逐字对齐 pi 现版,
|
|
41
|
+
* 含 round-trip 的行列信息——供宿主转 throw 后 LLM 自纠正)。
|
|
42
|
+
*/
|
|
43
|
+
export type GenerateWorkflowScriptResult =
|
|
44
|
+
| { ok: true; path: string }
|
|
45
|
+
| { ok: false; error: string };
|
|
46
|
+
|
|
47
|
+
// ── 校验闸(每闸返回 error 文案;undefined = 通过。文案逐字对齐 pi 现版) ──
|
|
48
|
+
|
|
49
|
+
/** 闸 0:参数完备(缺 name/script 即拒)。 */
|
|
50
|
+
function checkRequiredParams(name: string, script: string): string | undefined {
|
|
51
|
+
if (!name || !script) {
|
|
52
|
+
return "generate requires 'name' and 'script' parameters";
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** 闸 1:ESM 语法拒绝(Worker 跑 CJS);'export const meta' 例外。stripped = 注释剥离后源码。 */
|
|
58
|
+
function checkEsamSyntax(stripped: string): string | undefined {
|
|
59
|
+
if (/\bimport\s+(?:type\s+)?[\w{*]/.test(stripped)) {
|
|
60
|
+
return "Script uses ESM 'import' syntax. Workflow scripts run in a CJS Worker — use require() instead.";
|
|
61
|
+
}
|
|
62
|
+
const hasExportMeta = /\bexport\s+const\s+meta\s*=/.test(stripped);
|
|
63
|
+
const otherExports = stripped.match(/\bexport\s+(?:const|let|var|function|default|\{)/g);
|
|
64
|
+
if (otherExports && !hasExportMeta) {
|
|
65
|
+
return "Script uses ESM 'export' (non-meta). Use 'const meta = {...}' at top level instead.";
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 闸 2:meta 声明必需(/* @pi-meta */ YAML 块注释或 legacy const meta,过渡期 m0)
|
|
71
|
+
function checkMetaDeclaration(script: string, hasPiMeta: boolean): string | undefined {
|
|
72
|
+
const hasLegacyMeta = script.includes("const meta") || script.includes("export const meta");
|
|
73
|
+
if (!hasPiMeta && !hasLegacyMeta) {
|
|
74
|
+
return "Script must contain a meta declaration: a /* @pi-meta */ YAML block comment (preferred) or legacy const meta = { ... }. The block has the form: a block comment starting with /* @pi-meta followed by YAML (name/description/phases/parameters?/usage?), closed by */ on its own line.";
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** 闸 3:agent() 调用必需。 */
|
|
80
|
+
function checkAgentUsage(stripped: string): string | undefined {
|
|
81
|
+
if (!/\bagent\s*\(/.test(stripped)) {
|
|
82
|
+
return "Script does not contain any agent() calls. A workflow must call agent() at least once.";
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** 闸 4:语法检查(包 async IIFE,与 runtime 包裹形态一致)。 */
|
|
88
|
+
function checkSyntax(script: string): string | undefined {
|
|
89
|
+
const cjsScript = script.replace(/\bexport\s+const\s+meta\b/, "const meta");
|
|
90
|
+
try {
|
|
91
|
+
new Function(`(async () => { ${cjsScript} })();`);
|
|
92
|
+
return undefined;
|
|
93
|
+
} catch (err: unknown) {
|
|
94
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
95
|
+
return `Syntax error in script: ${msg}`;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** 闸 4b:round-trip——@pi-meta 存在时 parseResourceMetaDetailed 校验 YAML(报行列)。 */
|
|
100
|
+
function checkMetaRoundTrip(script: string, hasPiMeta: boolean): string | undefined {
|
|
101
|
+
if (!hasPiMeta) return undefined;
|
|
102
|
+
const detailed = parseResourceMetaDetailed(script, "workflow");
|
|
103
|
+
if (detailed.ok) return undefined;
|
|
104
|
+
const loc = "linePos" in detailed && detailed.linePos
|
|
105
|
+
? ` (line ${detailed.linePos.line}, col ${detailed.linePos.col})`
|
|
106
|
+
: "";
|
|
107
|
+
return `Generated /* @pi-meta */ YAML cannot be parsed${loc}: ${detailed.error}. Common causes: YAML indent errors, patternProperties regex must use double backslash (\\d not \d), or a stray star-slash inside the YAML body. Fix the meta block and retry.`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 校验并落盘一个 AI 生成的 workflow 临时脚本。
|
|
112
|
+
*
|
|
113
|
+
* @param name 脚本名(落盘 <tmpDir>/{name}.js)
|
|
114
|
+
* @param script 完整脚本源码
|
|
115
|
+
* @param options 目录注入(tmpDir 缺省 pi 布局,相对 cwd resolve)
|
|
116
|
+
*/
|
|
117
|
+
export function generateWorkflowScript(
|
|
118
|
+
name: string,
|
|
119
|
+
script: string,
|
|
120
|
+
options?: GenerateWorkflowScriptOptions,
|
|
121
|
+
): GenerateWorkflowScriptResult {
|
|
122
|
+
const paramError = checkRequiredParams(name, script);
|
|
123
|
+
if (paramError) return { ok: false, error: paramError };
|
|
124
|
+
|
|
125
|
+
// 1. Reject ESM syntax (Worker runs CJS); 'export const meta' 例外
|
|
126
|
+
const stripped = script.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
127
|
+
const esamError = checkEsamSyntax(stripped);
|
|
128
|
+
if (esamError) return { ok: false, error: esamError };
|
|
129
|
+
|
|
130
|
+
// 2. Validate meta declaration (/* @pi-meta */ new format preferred; legacy const meta accepted during transition — m0)
|
|
131
|
+
const hasPiMeta = /\/\*\s*@pi-meta\s*\n/.test(script);
|
|
132
|
+
const metaError = checkMetaDeclaration(script, hasPiMeta);
|
|
133
|
+
if (metaError) return { ok: false, error: metaError };
|
|
134
|
+
|
|
135
|
+
// 3. Check agent usage
|
|
136
|
+
const agentError = checkAgentUsage(stripped);
|
|
137
|
+
if (agentError) return { ok: false, error: agentError };
|
|
138
|
+
|
|
139
|
+
// 4. Syntax check (wrap in async IIFE like runtime)
|
|
140
|
+
const syntaxError = checkSyntax(script);
|
|
141
|
+
if (syntaxError) return { ok: false, error: syntaxError };
|
|
142
|
+
|
|
143
|
+
// 4b. Round-trip: validate /* @pi-meta */ YAML before writing (v5 §4.7 / ERR4 — report linePos, don't write bad files)
|
|
144
|
+
const roundTripError = checkMetaRoundTrip(script, hasPiMeta);
|
|
145
|
+
if (roundTripError) return { ok: false, error: roundTripError };
|
|
146
|
+
|
|
147
|
+
// 5. Write to tmp directory
|
|
148
|
+
const tmpDir = resolve(options?.tmpDir ?? DEFAULT_WORKFLOW_TMP_DIR);
|
|
149
|
+
mkdirSync(tmpDir, { recursive: true });
|
|
150
|
+
const filePath = resolve(tmpDir, `${name}.js`);
|
|
151
|
+
writeFileSync(filePath, script, "utf-8");
|
|
152
|
+
|
|
153
|
+
return { ok: true, path: filePath };
|
|
154
|
+
}
|
|
@@ -76,13 +76,13 @@ function checkEntryPoint(source: string): LintFinding[] {
|
|
|
76
76
|
function checkLine(lineText: string, lineNum: number): LintFinding[] {
|
|
77
77
|
const results: LintFinding[] = [];
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
// 跳过注释行
|
|
80
80
|
const trimmed = lineText.trim();
|
|
81
81
|
if (trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("/*")) {
|
|
82
82
|
return results;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
// result.output / result.parsedOutput / result.content
|
|
86
86
|
const resultAccessPatterns: Array<{ regex: RegExp; field: string }> = [
|
|
87
87
|
{ regex: /\bresult\s*\.\s*output\b/, field: "output" },
|
|
88
88
|
{ regex: /\bresult\s*\.\s*parsedOutput\b/, field: "parsedOutput" },
|
|
@@ -99,7 +99,7 @@ function checkLine(lineText: string, lineNum: number): LintFinding[] {
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
// 文件传状态(readFileSync of STATE)
|
|
103
103
|
if (/readFileSync\(.*STATE.*\)|readFileSync\(.*state.*\.json/i.test(lineText)) {
|
|
104
104
|
results.push({
|
|
105
105
|
severity: "warning",
|
|
@@ -109,7 +109,7 @@ function checkLine(lineText: string, lineNum: number): LintFinding[] {
|
|
|
109
109
|
});
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
// unlinkSync 清理状态
|
|
113
113
|
if (/unlinkSync.*state/i.test(lineText)) {
|
|
114
114
|
results.push({
|
|
115
115
|
severity: "warning",
|
|
@@ -169,7 +169,7 @@ function forEachAgentCallRange(
|
|
|
169
169
|
const line = lines[i];
|
|
170
170
|
const trimmed = line.trim();
|
|
171
171
|
|
|
172
|
-
|
|
172
|
+
// 跳过注释
|
|
173
173
|
if (trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("/*")) {
|
|
174
174
|
continue;
|
|
175
175
|
}
|
|
@@ -177,12 +177,12 @@ function forEachAgentCallRange(
|
|
|
177
177
|
// 匹配/计括号都用剔除字符串与注释后的行,避免字面量内容干扰
|
|
178
178
|
const codeLine = stripStringsAndComments(line);
|
|
179
179
|
|
|
180
|
-
|
|
180
|
+
// 检测 agent 调用开始
|
|
181
181
|
if (!inAgentCall && /\bagent\s*\(/.test(codeLine)) {
|
|
182
182
|
inAgentCall = true;
|
|
183
183
|
depth = 0;
|
|
184
184
|
agentStartLine = i;
|
|
185
|
-
|
|
185
|
+
// 从 agent( 开始计括号
|
|
186
186
|
const afterAgent = codeLine.replace(/^.*?\bagent\s*\(/, "(");
|
|
187
187
|
// 非字面量实参(agent(callVar) / agent(expr))→ 跳过(见函数头注释)。
|
|
188
188
|
// 形如 `agent(` 换行 `{` 的多行字面量调用(argTail 为空)保留原追踪行为。
|
|
@@ -196,7 +196,7 @@ function forEachAgentCallRange(
|
|
|
196
196
|
if (ch === ")" || ch === "}" || ch === "]") depth--;
|
|
197
197
|
}
|
|
198
198
|
if (depth <= 0) {
|
|
199
|
-
|
|
199
|
+
// 单行 agent 调用
|
|
200
200
|
callback(agentStartLine, i);
|
|
201
201
|
inAgentCall = false;
|
|
202
202
|
}
|
|
@@ -247,15 +247,15 @@ function checkAgentCallOptions(
|
|
|
247
247
|
for (let i = startLine; i <= endLine; i++) {
|
|
248
248
|
const line = lines[i];
|
|
249
249
|
|
|
250
|
-
|
|
250
|
+
// 跳过变量声明(const/let/var outputSchema = ...)
|
|
251
251
|
if (/\b(?:const|let|var)\s+outputSchema\b/.test(line)) {
|
|
252
252
|
continue;
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
-
|
|
255
|
+
// 匹配:outputSchema 作为对象 key(简写或显式)
|
|
256
256
|
if (/\boutputSchema\s*[,\}]/.test(line) || /\boutputSchema\s*:/.test(line)) {
|
|
257
|
-
|
|
258
|
-
|
|
257
|
+
// 排除:outputSchema 作为 value(在另一个 key 的冒号后)
|
|
258
|
+
// e.g. "schema: outputSchema," — outputSchema 前是冒号
|
|
259
259
|
const beforeOutput = line.substring(0, line.indexOf("outputSchema"));
|
|
260
260
|
if (/:\s*$/.test(beforeOutput)) {
|
|
261
261
|
continue; // outputSchema 是 value,不是 key
|
|
@@ -329,7 +329,7 @@ function checkAgentDescription(source: string): LintFinding[] {
|
|
|
329
329
|
// 对象以展开开头(agent({ ...call, ... })):description 来自运行时对象、调用点静态
|
|
330
330
|
// 不可见——无法验证即不报(review-fix-loop 的 agent({ ...call, agent: ... }) 即此形态)。
|
|
331
331
|
if (/\{\s*\.\.\./.test(range)) return;
|
|
332
|
-
|
|
332
|
+
// 匹配 description 或 label 作为对象 key(后跟冒号)
|
|
333
333
|
if (!/\b(description|label)\s*:/.test(range)) {
|
|
334
334
|
findings.push({
|
|
335
335
|
severity: "warning",
|
|
@@ -416,7 +416,7 @@ function isIIFEAwaited(source: string, iifeStart: number): boolean {
|
|
|
416
416
|
function checkBareAsyncIIFE(source: string): LintFinding[] {
|
|
417
417
|
if (!ENTRY_POINT_PATTERNS.some((p) => p.test(source))) return [];
|
|
418
418
|
|
|
419
|
-
|
|
419
|
+
// 用 matchAll 检查所有 IIFE(脚本可能有多个,每个都需独立判断)
|
|
420
420
|
const findings: LintFinding[] = [];
|
|
421
421
|
for (const match of source.matchAll(BARE_ASYNC_IIFE_PATTERN)) {
|
|
422
422
|
const iifeStart = match.index ?? 0;
|
|
@@ -519,13 +519,13 @@ function checkMetaPhases(source: string): LintFinding[] {
|
|
|
519
519
|
function checkPhaseConsistency(source: string): LintFinding[] {
|
|
520
520
|
const findings: LintFinding[] = [];
|
|
521
521
|
|
|
522
|
-
|
|
522
|
+
// 提取 meta.phases 声明的字符串 + 声明所在行号
|
|
523
523
|
const declared = new Map<string, number>();
|
|
524
524
|
const phasesArrayMatch = source.match(/phases\s*:\s*\[[^\]]*\]/);
|
|
525
525
|
if (phasesArrayMatch && phasesArrayMatch.index !== undefined) {
|
|
526
526
|
const inner = phasesArrayMatch[0];
|
|
527
|
-
|
|
528
|
-
|
|
527
|
+
// 对象数组(如 [{title,detail}])由 checkMetaPhases 单独报,这里跳过提取,
|
|
528
|
+
// 避免从对象字段里误抽出字符串作 declared。
|
|
529
529
|
if (!/\[\s*\{/.test(inner)) {
|
|
530
530
|
const phasesLine = source.slice(0, phasesArrayMatch.index).split("\n").length;
|
|
531
531
|
for (const m of inner.matchAll(/['"]([^'"]+)['"]/g)) {
|
|
@@ -534,7 +534,7 @@ function checkPhaseConsistency(source: string): LintFinding[] {
|
|
|
534
534
|
}
|
|
535
535
|
}
|
|
536
536
|
|
|
537
|
-
|
|
537
|
+
// 提取所有 phase() 调用实参 + 首次出现的行号
|
|
538
538
|
const called = new Map<string, number>();
|
|
539
539
|
for (const m of source.matchAll(/\bphase\s*\(\s*['"]([^'"]+)['"]/g)) {
|
|
540
540
|
if (m.index === undefined) continue;
|
|
@@ -542,10 +542,10 @@ function checkPhaseConsistency(source: string): LintFinding[] {
|
|
|
542
542
|
if (!called.has(m[1])) called.set(m[1], lineNum);
|
|
543
543
|
}
|
|
544
544
|
|
|
545
|
-
|
|
545
|
+
// 两者都为空 → 跳过(脚本不使用 phase 机制)
|
|
546
546
|
if (declared.size === 0 && called.size === 0) return [];
|
|
547
547
|
|
|
548
|
-
|
|
548
|
+
// 声明了但从未 phase() 调用
|
|
549
549
|
for (const [name, line] of declared) {
|
|
550
550
|
if (!called.has(name)) {
|
|
551
551
|
findings.push({
|
|
@@ -557,7 +557,7 @@ function checkPhaseConsistency(source: string): LintFinding[] {
|
|
|
557
557
|
}
|
|
558
558
|
}
|
|
559
559
|
|
|
560
|
-
|
|
560
|
+
// 调用了但未声明
|
|
561
561
|
for (const [name, line] of called) {
|
|
562
562
|
if (!declared.has(name)) {
|
|
563
563
|
findings.push({
|
|
@@ -724,10 +724,10 @@ export function lintScript(source: string): LintResult {
|
|
|
724
724
|
const lines = source.split("\n");
|
|
725
725
|
const findings: LintFinding[] = [];
|
|
726
726
|
|
|
727
|
-
|
|
727
|
+
// 入口检查(必须有 agent/parallel/pipeline 之一)
|
|
728
728
|
findings.push(...checkEntryPoint(source));
|
|
729
729
|
|
|
730
|
-
|
|
730
|
+
// 逐行检查(result.output、文件传状态等)
|
|
731
731
|
for (let i = 0; i < lines.length; i++) {
|
|
732
732
|
findings.push(...checkLine(lines[i], i + 1));
|
|
733
733
|
}
|
|
@@ -735,23 +735,23 @@ export function lintScript(source: string): LintResult {
|
|
|
735
735
|
// agent 调用上下文检查(outputSchema 作为 key)
|
|
736
736
|
findings.push(...checkAgentCalls(source));
|
|
737
737
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
738
|
+
// [HISTORICAL] 顶层未 await 的异步 IIFE + 内部调 agent——子进程被提前 kill。
|
|
739
|
+
// 教训来源:daily-news-impact.js 用 (async function main(){...})();() 包裹整个脚本,
|
|
740
|
+
// worker 外层 IIFE 不等内层 IIFE 就 postMessage("return"),主线程 transition done
|
|
741
|
+
// → release runtime → controller.abort() → spawn 后 2ms SIGKILL 子进程。
|
|
742
|
+
// 诊断耗时 4 轮:先后误判为 model 故障 / 工具缺失 / turn-signal abort / 并发门闩 gate 异常,
|
|
743
|
+
// 最终靠 worker-host → handleReturn → release → abort 的调用栈定位。
|
|
744
744
|
findings.push(...checkBareAsyncIIFE(source));
|
|
745
745
|
|
|
746
|
-
|
|
747
|
-
|
|
746
|
+
// m4 W1-W3:meta 质量(description/when/notFor 短单句 + 不含已声明参数名)。
|
|
747
|
+
// 仅对 IF1 解析成功的 @pi-meta 执行——旧 const meta 格式(D1 无 adapter)不检查。
|
|
748
748
|
const meta = parseResourceMeta(source, "workflow");
|
|
749
749
|
if (meta && meta.kind === "workflow") {
|
|
750
750
|
findings.push(...checkMetaQuality(meta));
|
|
751
751
|
}
|
|
752
752
|
|
|
753
|
-
|
|
754
|
-
|
|
753
|
+
// 显示性检查(warning):agent 缺 description / meta.phases 形式 / phase 一致性。
|
|
754
|
+
// 目的:让 TUI /workflows 视图避免 unnamed agent 与 (unnamed) phase 分组。
|
|
755
755
|
findings.push(...checkAgentDescription(source));
|
|
756
756
|
findings.push(...checkMetaPhases(source));
|
|
757
757
|
findings.push(...checkPhaseConsistency(source));
|
|
@@ -29,7 +29,7 @@ export type WorkerExitHandler = (code: number) => void;
|
|
|
29
29
|
|
|
30
30
|
export class WorkerHandle {
|
|
31
31
|
private readonly worker: Worker;
|
|
32
|
-
|
|
32
|
+
/**
|
|
33
33
|
* 竞态守卫。true = 此 handle 仍是当前活动 handle;false = 已 terminate,
|
|
34
34
|
* 后续事件(已终止 worker 延迟触发的 message/error/exit)必须忽略。
|
|
35
35
|
*
|
|
@@ -42,17 +42,17 @@ export class WorkerHandle {
|
|
|
42
42
|
this.worker = worker;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
/** 此 handle 是否仍是当前活动 handle(terminate 后 false,G-025)。 */
|
|
46
46
|
get isCurrent(): boolean {
|
|
47
47
|
return this.current;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
/** 底层 Worker(WorkerHost/RunRuntime 偶尔需要直接访问,如 ref/href)。 */
|
|
51
51
|
get raw(): Worker {
|
|
52
52
|
return this.worker;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
/**
|
|
56
56
|
* 向 worker 发送消息。terminate 后 no-op(已终止 handle 的 postMessage 无意义)。
|
|
57
57
|
*/
|
|
58
58
|
postMessage(msg: unknown): void {
|
|
@@ -60,7 +60,7 @@ export class WorkerHandle {
|
|
|
60
60
|
this.worker.postMessage(msg);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
/**
|
|
64
64
|
* 终止 worker 线程。幂等——重复调用安全,第二次起 no-op。
|
|
65
65
|
* 置 isCurrent=false 后再 await worker.terminate,确保并发 exit 事件
|
|
66
66
|
* 在 terminate resolve 之前到达时也被守卫拦下。
|
|
@@ -71,13 +71,13 @@ export class WorkerHandle {
|
|
|
71
71
|
try {
|
|
72
72
|
await this.worker.terminate();
|
|
73
73
|
} catch (err) {
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
// terminate 失败不阻断(worker 可能已退出)。current 已 false,安全。
|
|
75
|
+
// 不向上抛——调用方(RunRuntime.release)不应被底层线程错误打断。
|
|
76
76
|
void err;
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
/**
|
|
81
81
|
* 绑定 message 回调。仅当 isCurrent 时触发——已终止 handle 的事件被吞掉。
|
|
82
82
|
* 返回 this 便于链式 onMessage(...).onError(...).onExit(...)。
|
|
83
83
|
*/
|
|
@@ -89,7 +89,7 @@ export class WorkerHandle {
|
|
|
89
89
|
return this;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
/**
|
|
93
93
|
* 绑定 error 回调。仅当 isCurrent 时触发。
|
|
94
94
|
*/
|
|
95
95
|
onError(handler: WorkerErrorHandler): this {
|
|
@@ -100,7 +100,7 @@ export class WorkerHandle {
|
|
|
100
100
|
return this;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
/**
|
|
104
104
|
* 绑定 exit 回调。仅当 isCurrent 时触发——这是 G-025 的关键守卫:
|
|
105
105
|
* terminate(old) → startWorker(new) → old exit 触发时,old handle.current
|
|
106
106
|
* 已为 false,回调 no-op,不会误删 new worker。
|
|
@@ -28,7 +28,7 @@ import { buildWorkerScript } from "./worker-script-builder.ts";
|
|
|
28
28
|
// ── WorkerHostImpl ───────────────────────────────────────────
|
|
29
29
|
|
|
30
30
|
export class WorkerHostImpl implements WorkerHost {
|
|
31
|
-
|
|
31
|
+
/**
|
|
32
32
|
* 启动一个 Worker thread 运行 workflow 脚本。
|
|
33
33
|
*
|
|
34
34
|
* 1. 用 buildWorkerScript(spec.scriptSource) 包装用户脚本(注入 agent/parallel/
|
|
@@ -59,9 +59,9 @@ export class WorkerHostImpl implements WorkerHost {
|
|
|
59
59
|
name: spec.scriptName,
|
|
60
60
|
description: spec.description,
|
|
61
61
|
},
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
// D-12 regression fix (round-2 #1):注入 budget,否则 worker 内 $BUDGET.total 恒为 0。
|
|
63
|
+
// 旧 agent-call-handler.ts 删除时同时丢失了 budget 注入和 budget-update 发送方,
|
|
64
|
+
// 导致依赖 $BUDGET 做动态预算分支的脚本静默得到全 0。
|
|
65
65
|
budget: {
|
|
66
66
|
maxTokens: spec.budgetTokens,
|
|
67
67
|
usedTokens: 0,
|
|
@@ -77,9 +77,9 @@ export class WorkerHostImpl implements WorkerHost {
|
|
|
77
77
|
|
|
78
78
|
const handle = new WorkerHandle(worker);
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
// 绑定事件——WorkerHandle 内部用 isCurrent 守卫,terminate 后回调 no-op(G-025)。
|
|
81
|
+
// handlers 的 onMessage/onError/onExit 都是 async,这里 void 掉 promise(worker 事件
|
|
82
|
+
// 不能 await,且 handler 内部错误由 lifecycle 统一捕获)。
|
|
83
83
|
handle.onMessage((raw) => {
|
|
84
84
|
void handlers.onMessage(raw);
|
|
85
85
|
});
|
|
@@ -87,9 +87,9 @@ export class WorkerHostImpl implements WorkerHost {
|
|
|
87
87
|
void handlers.onError(err);
|
|
88
88
|
});
|
|
89
89
|
handle.onExit((code) => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
// C.3 修复:传 handle 给 onExit,调用方用 handle.isCurrent 做竞态防护。
|
|
91
|
+
// 注意:此时 handle.isCurrent 仍为 true(onExit 回调仅在 isCurrent 时触发,
|
|
92
|
+
// WorkerHandle 的内部守卫已过滤掉 terminate 后的 stale exit)。
|
|
93
93
|
void handlers.onExit(code, handle);
|
|
94
94
|
});
|
|
95
95
|
|