@zhushanwen/pi-subagent-workflow 0.2.0 → 0.3.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 +56 -0
- package/agents/{scout.md → explorer.md} +1 -1
- package/agents/orchestrator.md +48 -0
- package/package.json +1 -1
- package/src/execution/__tests__/agent-registry.test.ts +3 -3
- package/src/execution/__tests__/ask-user-transit-e2e.test.ts +484 -0
- package/src/execution/__tests__/channel-registry-handshake.test.ts +233 -0
- package/src/execution/__tests__/crash-recovery.test.ts +5 -1
- package/src/execution/__tests__/dialog-queue.test.ts +299 -0
- package/src/execution/__tests__/execute-nesting.test.ts +1 -1
- package/src/execution/__tests__/execute-options-mapper.test.ts +1 -1
- package/src/execution/__tests__/finalize-record.test.ts +173 -0
- package/src/execution/__tests__/gui-mode-dispatch.test.ts +2 -3
- package/src/execution/__tests__/helpers/spawn-mock.ts +209 -0
- package/src/execution/__tests__/host-mode.test.ts +87 -0
- package/src/execution/__tests__/index-session-start.test.ts +342 -0
- package/src/execution/__tests__/list-component.test.ts +1 -1
- package/src/execution/__tests__/notifier-flush.test.ts +78 -0
- package/src/execution/__tests__/path-encoding.test.ts +30 -1
- package/src/execution/__tests__/record-store.test.ts +86 -2
- package/src/execution/__tests__/records-cwd-isolation.test.ts +91 -0
- package/src/execution/__tests__/rpc-mode.test.ts +89 -0
- package/src/execution/__tests__/run-spawn-edges.test.ts +157 -153
- package/src/execution/__tests__/run-spawn-integration.test.ts +85 -151
- package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +193 -0
- package/src/execution/__tests__/session-file-gc.test.ts +46 -0
- package/src/execution/__tests__/session-start-reaper.test.ts +7 -1
- package/src/execution/__tests__/spawn-args.test.ts +14 -19
- package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +189 -0
- package/src/execution/__tests__/stdin-writer.test.ts +353 -0
- package/src/execution/__tests__/subagent-service.test.ts +73 -3
- package/src/execution/__tests__/tool-action.test.ts +1 -1
- package/src/execution/__tests__/ui-channels.test.ts +187 -0
- package/src/execution/__tests__/ui-interaction-model.test.ts +67 -0
- package/src/execution/__tests__/ui-request-handler-factory.test.ts +166 -0
- package/src/execution/__tests__/ui-request-handler.test.ts +204 -0
- package/src/execution/__tests__/ui-request-observability.test.ts +101 -0
- package/src/execution/__tests__/ui-request-queue.test.ts +133 -0
- package/src/execution/__tests__/worktree-manager.test.ts +1 -1
- package/src/execution/agent-registry.ts +1 -1
- package/src/execution/channel-registry-access.ts +138 -0
- package/src/execution/dialog-queue.ts +329 -0
- package/src/execution/finalize-record.ts +160 -0
- package/src/execution/get-state-handshake.ts +104 -0
- package/src/execution/host-mode.ts +52 -0
- package/src/execution/manifest-store.ts +206 -0
- package/src/execution/notifier.ts +5 -1
- package/src/execution/path-encoding.ts +18 -0
- package/src/execution/pi-invocation.ts +1 -1
- package/src/execution/record-store.ts +108 -2
- package/src/execution/session-file-gc.ts +25 -3
- package/src/execution/session-runner.ts +216 -32
- package/src/execution/spawn-event-adapter.ts +219 -6
- package/src/execution/stdin-writer.ts +106 -0
- package/src/execution/subagent-service.ts +167 -197
- package/src/execution/ui-channels.ts +216 -0
- package/src/execution/ui-interaction-model.ts +48 -0
- package/src/execution/ui-request-handler-factory.ts +175 -0
- package/src/execution/ui-request-observability.ts +77 -0
- package/src/execution/ui-request-queue.ts +168 -0
- package/src/index.ts +90 -6
- package/src/interface/format.ts +2 -0
- package/src/interface/subagent-actions.ts +9 -2
- package/src/interface/subagent-tool.ts +9 -8
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// src/execution/stdin-writer.ts
|
|
2
|
+
//
|
|
3
|
+
// 向 rpc 子进程 stdin 写入命令的 helper 集合。
|
|
4
|
+
//
|
|
5
|
+
// pi --mode rpc 通过 stdin 的 JSON RpcCommand / RpcExtensionUIResponse 驱动:
|
|
6
|
+
// - extension_ui_response(主进程回答子进程的 UI 请求,如 ask_user)
|
|
7
|
+
// - prompt(驱动子进程开始处理 task)
|
|
8
|
+
// 两者共用 child.stdin.write + 背压检查,提取到此模块统一维护。
|
|
9
|
+
|
|
10
|
+
import type { ChildProcess } from "node:child_process";
|
|
11
|
+
import * as crypto from "node:crypto";
|
|
12
|
+
|
|
13
|
+
import type { UiResponse } from "./dialog-queue.ts";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 按 UiResponse 形状构造 Pi 原生 extension_ui_response 并写 stdin。
|
|
17
|
+
*
|
|
18
|
+
* SR-5:ack(fire-and-forget)不写 stdin——Pi 对 fire-and-forget method 不期待响应,
|
|
19
|
+
* 写入会触发协议错配。其他三种 shape(value/confirmed/cancelled)按对应字段写。
|
|
20
|
+
*
|
|
21
|
+
* [R1] 背压检查:child.stdin.write 返回 false 时记 warn(不阻塞,内核缓冲会随后排空)。
|
|
22
|
+
* [R2] 序列化在本函数内逐分支完成。JSON.stringify 可能抛错(out.value 含循环引用 /
|
|
23
|
+
* BigInt 等不可序列化结构),try/catch 降级为 cancelled——宁可取消单次 dialog 也不让
|
|
24
|
+
* 父进程崩溃(UI 请求通道不应被脏数据拖垮)。
|
|
25
|
+
*
|
|
26
|
+
* @param child 子进程(stdin 写入响应)
|
|
27
|
+
* @param id 请求 id(关联 response)
|
|
28
|
+
* @param out UiResponse({value}/{confirmed}/{cancelled}/{ack})
|
|
29
|
+
* @param signal abort signal(已 aborted 时跳过写入)
|
|
30
|
+
*/
|
|
31
|
+
export function respond(child: ChildProcess, id: string, out: UiResponse, signal?: AbortSignal): void {
|
|
32
|
+
if (signal?.aborted) return;
|
|
33
|
+
let line: string | undefined;
|
|
34
|
+
try {
|
|
35
|
+
if ("value" in out) line = JSON.stringify({ type: "extension_ui_response", id, value: out.value });
|
|
36
|
+
else if ("confirmed" in out) line = JSON.stringify({ type: "extension_ui_response", id, confirmed: out.confirmed });
|
|
37
|
+
else if ("cancelled" in out) line = JSON.stringify({ type: "extension_ui_response", id, cancelled: true });
|
|
38
|
+
} catch (err) {
|
|
39
|
+
// [R2] out.value 含循环引用/BigInt 等不可序列化结构——降级 cancelled,避免父进程崩溃。
|
|
40
|
+
console.warn(`[subagents] JSON.stringify failed for ui response ${id}, degrading to cancelled:`, err);
|
|
41
|
+
line = JSON.stringify({ type: "extension_ui_response", id, cancelled: true });
|
|
42
|
+
}
|
|
43
|
+
// ack: fire-and-forget,不写 stdin(SR-5)
|
|
44
|
+
if (line === undefined) return;
|
|
45
|
+
writeStdinLine(child, line, `ui response for request ${id}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* spawn 后向 rpc 子进程 stdin 写 prompt 命令,驱动 agent 开始处理 task。
|
|
50
|
+
*
|
|
51
|
+
* pi 的 runRpcMode 只通过 stdin RpcCommand 驱动——positional task arg / -p flag
|
|
52
|
+
* 在 rpc mode 下被 resolveAppMode 无视。必须在 spawn 后主动喂 prompt 命令,
|
|
53
|
+
* 否则子进程阻塞等 stdin、永不进入推理(totalTokens 恒 0)。
|
|
54
|
+
*
|
|
55
|
+
* 时机:spawn 后立即写。stdin 是 pipe,内核缓冲保证数据不丢;
|
|
56
|
+
* pi 在 await rebindSession() 后才挂 stdin reader(rpc-mode.ts:778-781),
|
|
57
|
+
* reader 处理 prompt 时 session 已就绪。
|
|
58
|
+
*
|
|
59
|
+
* @param child 子进程(stdin 写入 prompt 命令)
|
|
60
|
+
* @param task 完整 task 文本(含 schema 指令)
|
|
61
|
+
*/
|
|
62
|
+
export function sendPromptCommand(child: ChildProcess, task: string): void {
|
|
63
|
+
if (!child.stdin || child.stdin.destroyed) return;
|
|
64
|
+
const command = JSON.stringify({
|
|
65
|
+
id: crypto.randomUUID(),
|
|
66
|
+
type: "prompt",
|
|
67
|
+
message: task,
|
|
68
|
+
});
|
|
69
|
+
writeStdinLine(child, command, "prompt command");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 向 rpc 子进程 stdin 写 get_state 命令,查询 sessionFile/sessionId。
|
|
74
|
+
*
|
|
75
|
+
* FR-4: RPC get_state 握手。当 stdout header 未携带 sessionFile 时,
|
|
76
|
+
* 通过此命令向子进程查询当前 session 状态。子进程收到后返回
|
|
77
|
+
* {type:"response", command:"get_state", success:true, data:{sessionFile, sessionId}}。
|
|
78
|
+
*
|
|
79
|
+
* @param child 子进程(stdin 写入 get_state 命令)
|
|
80
|
+
* @returns 请求 id(用于匹配 response)
|
|
81
|
+
*/
|
|
82
|
+
export function sendGetStateCommand(child: ChildProcess): string {
|
|
83
|
+
const id = crypto.randomUUID();
|
|
84
|
+
const command = JSON.stringify({
|
|
85
|
+
id,
|
|
86
|
+
type: "get_state",
|
|
87
|
+
});
|
|
88
|
+
writeStdinLine(child, command, "get_state command");
|
|
89
|
+
return id;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 向子进程 stdin 写一行(自动补换行),带背压检查。
|
|
94
|
+
*
|
|
95
|
+
* [R1] write 返回 false 时记 warn(不阻塞,内核缓冲会随后排空)。
|
|
96
|
+
* stdin 已关闭/销毁时跳过——respond 已检查 signal,sendPromptCommand 已检查 destroyed。
|
|
97
|
+
*
|
|
98
|
+
* @param child 子进程
|
|
99
|
+
* @param line JSON 行(不含换行)
|
|
100
|
+
* @param warnTag warn 日志的语义标记
|
|
101
|
+
*/
|
|
102
|
+
function writeStdinLine(child: ChildProcess, line: string, warnTag: string): void {
|
|
103
|
+
if (!child.stdin || child.stdin.destroyed) return;
|
|
104
|
+
const ok = child.stdin.write(line + "\n");
|
|
105
|
+
if (!ok) console.warn(`[subagents] stdin backpressure on ${warnTag}`);
|
|
106
|
+
}
|