@zhushanwen/pi-subagent-workflow 0.3.3 → 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/agents/explorer.md +2 -2
- package/agents/orchestrator.md +7 -2
- package/agents/researcher.md +3 -3
- package/package.json +1 -1
- package/src/execution/__tests__/agent-registry.test.ts +19 -2
- package/src/execution/__tests__/format.test.ts +15 -1
- package/src/execution/__tests__/sdk-contract.test.ts +9 -11
- package/src/execution/__tests__/spawn-args.test.ts +18 -1
- package/src/execution/__tests__/subagent-service.test.ts +4 -1
- package/src/execution/__tests__/tool-action.test.ts +10 -5
- package/src/execution/model-resolver.ts +1 -1
- package/src/execution/subagent-service.ts +1 -1
- package/src/interface/__tests__/detectors.test.ts +3 -28
- package/src/interface/__tests__/subagent-tool-prompt.test.ts +54 -11
- package/src/interface/__tests__/tool-render.test.ts +122 -0
- package/src/interface/__tests__/workflow-tool-prompt.test.ts +1 -1
- package/src/interface/format.ts +11 -7
- package/src/interface/subagent-actions.ts +16 -5
- package/src/interface/subagent-tool.ts +81 -98
- package/src/interface/tool-render.ts +9 -11
- package/src/interface/tool-workflow.ts +10 -90
- package/src/orchestration/error-recovery.ts +2 -2
- package/src/orchestration/models/ports.ts +3 -3
- package/src/orchestration/models/workflow-run.ts +3 -3
- package/src/orchestration/worker-script-builder.ts +1 -1
- package/src/orchestration/node-ops.ts +0 -194
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Workflow Extension — node-ops
|
|
3
|
-
*
|
|
4
|
-
* 单节点操作 free functions(D-12)。
|
|
5
|
-
*
|
|
6
|
-
* 2 个导出函数:
|
|
7
|
-
* - retryNode(run, callId, deps) — 重置 call + 主线程重跑(不 replaceRuntime)
|
|
8
|
-
* - skipNode(run, callId, deps) — 标记 call done + 占位 result
|
|
9
|
-
*
|
|
10
|
-
* **D.5(方案 A)**:retryNode 的语义是「重试单个失败 call」——只重置 call 状态 +
|
|
11
|
-
* 主线程直接调 executeAgentCall,worker 不重启,已完成调用不受影响(worker 重启是
|
|
12
|
-
* worker-error-retry handleWorkerError 的语义,不在本职责内)。
|
|
13
|
-
*
|
|
14
|
-
* **retryNode 不影响脚本流程**:worker 在首次失败结果被 postAgentResult 投递后即
|
|
15
|
-
* resolve 并删除该 callId 的 pending Promise(worker-script-builder agent-result 分支)。
|
|
16
|
-
* retryNode 的二次 postMessage 因此被 worker 丢弃——新结果只更新 trace/TUI,
|
|
17
|
-
* 回不到脚本(脚本早已带着首次结果往下走)。这是 D.5「不重启 worker」的直接后果:
|
|
18
|
-
* 要让新结果回到脚本必须重启 worker 重跑整个脚本(旧 orchestrator.ts 语义),
|
|
19
|
-
* 与「不干扰已完成调用」的设计意图冲突。故 retryNode 定位为「失败节点的诊断性重跑
|
|
20
|
-
* + trace 刷新」,不承诺改变脚本输出。tool-workflow 的描述已如实声明此语义。
|
|
21
|
-
*
|
|
22
|
-
* **G6-001**:retryNode 前置 status==="running"(paused 下拒绝,要 retry 先 resume)。
|
|
23
|
-
*
|
|
24
|
-
* 层归属:Engine。依赖 LifecycleDeps + WorkflowRun + executeAgentCall。
|
|
25
|
-
*
|
|
26
|
-
* 参考:domain-models.md §失败处理矩阵(retryNode 语义)、clarification.md D.5/G6-001。
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
import { postBudgetUpdate } from "./error-recovery.ts";
|
|
30
|
-
import { executeAgentCall } from "./execute-agent-call.ts";
|
|
31
|
-
import type { LifecycleDeps } from "./models/ports.ts";
|
|
32
|
-
import type { AgentResult } from "./models/types.ts";
|
|
33
|
-
import type { WorkflowRun } from "./models/workflow-run.ts";
|
|
34
|
-
|
|
35
|
-
// ── skipNode 占位结果 ────────────────────────────────────────
|
|
36
|
-
|
|
37
|
-
/** skipNode 注入的占位结果(零 usage,避免污染 budget)。 */
|
|
38
|
-
const SKIP_PLACEHOLDER: AgentResult = {
|
|
39
|
-
content: "",
|
|
40
|
-
usage: {
|
|
41
|
-
input: 0,
|
|
42
|
-
output: 0,
|
|
43
|
-
cacheRead: 0,
|
|
44
|
-
cacheWrite: 0,
|
|
45
|
-
cost: 0,
|
|
46
|
-
contextTokens: 0,
|
|
47
|
-
turns: 0,
|
|
48
|
-
},
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
// ── retryNode ────────────────────────────────────────────────
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* 重试单个失败 agent call(诊断性重跑 + trace 刷新,不影响脚本流程)。
|
|
55
|
-
*
|
|
56
|
-
* **D.5 修复**:不 replaceRuntime、不重启 worker。只重置 call 状态(status=pending,
|
|
57
|
-
* attempts=0, result=undefined)+ 同步 trace 节点 + 主线程直接调 executeAgentCall。
|
|
58
|
-
* worker 仍在运行,已完成调用不受影响。
|
|
59
|
-
*
|
|
60
|
-
* **结果不回到脚本**:见文件头说明——worker 在首次失败结果投递后已 resolve 并删除
|
|
61
|
-
* 该 callId 的 pending Promise,本函数末尾的 postMessage 通常被 worker 丢弃。新结果
|
|
62
|
-
* 只反映在 trace/TUI,不改变脚本输出(若需让脚本拿新结果,须重启 worker 重跑整个
|
|
63
|
-
* 脚本,与 D.5 冲突,未采用)。
|
|
64
|
-
*
|
|
65
|
-
* 与 worker-error-retry的区别:
|
|
66
|
-
* - handleWorkerError:worker 本身崩溃 → replaceRuntime 重启整个 worker
|
|
67
|
-
* - retryNode:单个 call 失败 → 主线程重跑该 call,worker 不动
|
|
68
|
-
*
|
|
69
|
-
* **G6-001**:前置 status==="running"。paused 下抛错(要 retry 先 resume)。
|
|
70
|
-
*
|
|
71
|
-
* @param run WorkflowRun 聚合根
|
|
72
|
-
* @param callId 要重试的 call id(必须已存在于 run.state.calls)
|
|
73
|
-
* @param deps LifecycleDeps(runner 用于重跑 call)
|
|
74
|
-
* @throws run.state.status !== "running"(G6-001)
|
|
75
|
-
* @throws callId 不存在
|
|
76
|
-
*/
|
|
77
|
-
export async function retryNode(
|
|
78
|
-
run: WorkflowRun,
|
|
79
|
-
callId: number,
|
|
80
|
-
deps: LifecycleDeps,
|
|
81
|
-
): Promise<void> {
|
|
82
|
-
// G6-001:前置 status==="running"
|
|
83
|
-
if (run.state.status !== "running") {
|
|
84
|
-
throw new Error(
|
|
85
|
-
`retryNode: requires status==="running" (current: ${run.state.status}, runId=${run.runId})`,
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const call = run.state.calls.get(callId);
|
|
90
|
-
if (!call) {
|
|
91
|
-
throw new Error(`retryNode: call ${callId} not found in run ${run.runId}`);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// 重置 call 状态:done → pending(绕过 AgentCall 状态机守卫,因为是显式 reset 语义)
|
|
95
|
-
call.status = "pending";
|
|
96
|
-
call.attempts = 0;
|
|
97
|
-
call.result = undefined;
|
|
98
|
-
call.sessionId = undefined;
|
|
99
|
-
call.sessionFile = undefined;
|
|
100
|
-
|
|
101
|
-
// 同步 trace 节点:回退到 pending
|
|
102
|
-
run.state.trace.update(callId, {
|
|
103
|
-
status: "pending",
|
|
104
|
-
result: undefined,
|
|
105
|
-
error: undefined,
|
|
106
|
-
completedAt: undefined,
|
|
107
|
-
sessionId: undefined,
|
|
108
|
-
sessionFile: undefined,
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
// 主线程重跑(不重启 worker)——executeAgentCall 内部 markRunning + runner.run
|
|
112
|
-
// G6-001 保证 status==="running" ⟺ runtime defined;retryNode 已守 status==="running"
|
|
113
|
-
// 前置,故 run.runtime 必存在。非空断言,不再用 fallback 掩盖不变式违反。
|
|
114
|
-
const signal = run.runtime!.controller.signal;
|
|
115
|
-
await executeAgentCall(call, deps.runner, run.state.budget, signal, run.state.trace);
|
|
116
|
-
|
|
117
|
-
// 回发结果给 worker(best-effort:worker 通常已在首次失败结果投递后 resolve 并删除
|
|
118
|
-
// 该 callId 的 pending Promise,故本 postMessage 多被丢弃——见文件头 D.5 说明)。
|
|
119
|
-
// 保留是为覆盖「executeAgentCall 已完成但 dispatchAgentCall.then 尚未投递结果」的
|
|
120
|
-
// 极窄竞态窗口,以及与 skipNode 的回发路径对称。结果无论如何都已写入 trace/TUI。
|
|
121
|
-
if (call.result) {
|
|
122
|
-
run.runtime?.worker.postMessage({
|
|
123
|
-
type: "agent-result",
|
|
124
|
-
callId,
|
|
125
|
-
result: call.result,
|
|
126
|
-
cached: false,
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// D-12 regression fix (round-2 #1):retry 重跑消费 usage 后同步 worker $BUDGET
|
|
131
|
-
postBudgetUpdate(run);
|
|
132
|
-
|
|
133
|
-
await deps.store.save(run);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// ── skipNode ─────────────────────────────────────────────────
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* 跳过单个 agent call(注入占位 result)。
|
|
140
|
-
*
|
|
141
|
-
* 标记 call.status="done" + 写入 SKIP_PLACEHOLDER result + 同步 trace 节点为 completed。
|
|
142
|
-
* 若 worker 仍活着,立即回发 agent-result(解锁 worker pending await)。
|
|
143
|
-
*
|
|
144
|
-
* 与 retryNode 的区别:skipNode 不重跑——直接用占位结果「假装完成」。
|
|
145
|
-
* 用于用户显式跳过失败节点继续执行的场景。
|
|
146
|
-
*
|
|
147
|
-
* 不要求 status==="running"——paused 下也可 skip(标记后 resume 时该 call 走 callCache
|
|
148
|
-
* replay)。但若 worker 已 terminate(runtime undefined),只标记不回发。
|
|
149
|
-
*
|
|
150
|
-
* @param run WorkflowRun 聚合根
|
|
151
|
-
* @param callId 要跳过的 call id(若不存在,仅注入到 calls Map 占位)
|
|
152
|
-
* @param deps LifecycleDeps(store 持久化)
|
|
153
|
-
*/
|
|
154
|
-
export async function skipNode(
|
|
155
|
-
run: WorkflowRun,
|
|
156
|
-
callId: number,
|
|
157
|
-
deps: LifecycleDeps,
|
|
158
|
-
): Promise<void> {
|
|
159
|
-
const call = run.state.calls.get(callId);
|
|
160
|
-
|
|
161
|
-
if (call) {
|
|
162
|
-
// 已有 call:标记 done + 占位 result(绕过状态机守卫,显式 skip 语义)
|
|
163
|
-
call.status = "done";
|
|
164
|
-
call.result = SKIP_PLACEHOLDER;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// 同步 trace 节点
|
|
168
|
-
run.state.trace.update(callId, {
|
|
169
|
-
status: "completed",
|
|
170
|
-
result: SKIP_PLACEHOLDER,
|
|
171
|
-
completedAt: new Date().toISOString(),
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
// 若 worker 仍活着,回发 agent-result(解锁 worker pending await)
|
|
175
|
-
if (run.runtime) {
|
|
176
|
-
try {
|
|
177
|
-
run.runtime.worker.postMessage({
|
|
178
|
-
type: "agent-result",
|
|
179
|
-
callId,
|
|
180
|
-
result: SKIP_PLACEHOLDER,
|
|
181
|
-
cached: true,
|
|
182
|
-
});
|
|
183
|
-
} catch (err) {
|
|
184
|
-
// P1-8: worker 可能在 has 与 postMessage 间 exit——预期竞态,不恢复
|
|
185
|
-
void err;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// D-12 regression fix (round-2 #1):skip 后同步 worker $BUDGET(占位 result 零 usage,
|
|
190
|
-
// 值不变,但保持 $BUDGET 与主线程一致)
|
|
191
|
-
postBudgetUpdate(run);
|
|
192
|
-
|
|
193
|
-
await deps.store.save(run);
|
|
194
|
-
}
|