@zhushanwen/pi-subagent-cli 0.1.1

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.
@@ -0,0 +1,784 @@
1
+ import { ToolCall, AgentFailureKind, ReplayRecordView, AgentEvent, UiRequest, UiResponse, HostStreamDeltaParams, HostRoundLifecycleParams, EngineStream, InteractResult, EnginePort, EngineCapabilities, ProbeReport, EngineAgentCallOpts, RunContext, EngineHandle, AgentOutcome, InteractAction, SessionView, ReverseRequestClock, ToolCallResult, UiRequestHandler } from '@zhushanwen/subagent-engine-sdk';
2
+ export { ParsedChannel, parseChannel } from '@zhushanwen/subagent-engine-sdk';
3
+ import { ChildProcess } from 'node:child_process';
4
+
5
+ /** spawn 调用描述符:command + args(透传给 spawnEngineChild)。 */
6
+ interface PiInvocation {
7
+ /** 可执行文件路径(node/bun/pi 二进制)。 */
8
+ command: string;
9
+ /** 命令行参数(可能含 [scriptPath, ...userArgs] 或直接 [...userArgs])。 */
10
+ args: string[];
11
+ }
12
+ /** getPiInvocation 可选项。 */
13
+ interface PiInvocationOptions {
14
+ /**
15
+ * false = 强制直连 spawn 真实 pi(不经 relay 代理)。唯一现役消费点是 probe
16
+ * ——探针意图是 pi 本体可解析性,经 relay 探到的是 runtime 健康,语义错位。
17
+ */
18
+ relay?: boolean;
19
+ }
20
+ declare function getPiInvocation(userArgs: string[], opts?: PiInvocationOptions): PiInvocation;
21
+
22
+ /**
23
+ * 镜像 flag 集合:从主进程 argv 解析出的可透传给子进程的 flag。
24
+ * 面向 buildSpawnArgs 的入参形态(解析后)。undefined 字段语义与解析为空一致。
25
+ */
26
+ interface MirrorFlags {
27
+ noExtensions: boolean;
28
+ approve: boolean;
29
+ extensionPaths: string[];
30
+ noContextFiles: boolean;
31
+ }
32
+ declare function mirrorMainProcessFlags(argv: readonly string[]): MirrorFlags;
33
+
34
+ /**
35
+ * Stale context 检测模式(P1-5;W4b 对齐 pi 0.84.x 真实文案)。
36
+ * 命中时分诊 failureKind="stale_context"——重试只会再次失败。
37
+ */
38
+ declare const STALE_CONTEXT_PATTERNS: readonly ["ctx is stale", "stale after session replacement", "context canceled", "aborted"];
39
+ /** 判断错误信息是否表示 stale/canceled pi session context。 */
40
+ declare function isStaleContextErrorMsg(msg: string | undefined): boolean;
41
+ /**
42
+ * [MF-1] 确定性 schema 失败标记(error 文本前缀,产出方 = 本模块的
43
+ * describeMissingParsedOutput)。标记词与 stale 词表零交集(防归因污染)。
44
+ */
45
+ declare const DETERMINISTIC_SCHEMA_FAILURE_PREFIX = "Structured output failed deterministically:";
46
+ /** [MF-1] 判断错误信息是否为确定性 schema 失败(命中标记前缀)。 */
47
+ declare function isDeterministicSchemaFailureMsg(msg: string | undefined): boolean;
48
+ /**
49
+ * [D5-③] 错误文案 → 结构化失败分诊标签(产出侧唯一识别点,stale 在前)。
50
+ * 未命中任何词表 → unknown——消费侧默认退避重试(语义守恒)。
51
+ */
52
+ declare function classifyFailureKind(msg: string | undefined): AgentFailureKind | undefined;
53
+ /** collectOutcome 的入参(session 身份 + 执行控制字段,执行内容从 record 读)。 */
54
+ interface CollectResultArgs {
55
+ startTime: number;
56
+ success: boolean;
57
+ error: string | undefined;
58
+ sessionId: string;
59
+ sessionFile: string | undefined;
60
+ /**
61
+ * [F-1] 本次执行是否要求结构化产出(schema 或 schemaEnv 任一存在)。
62
+ * true 且 run 结束仍无有效 parsedOutput 时:结果不得静默 success。
63
+ */
64
+ schemaExpected?: boolean;
65
+ }
66
+ /**
67
+ * 从 toolCalls 提取 structured-output 的 result.details(schema 模式产出)。
68
+ * isError:true 的调用一律跳过(失败调用的 details 不是通过校验的产出)。
69
+ */
70
+ declare function extractParsedOutput(toolCalls: ToolCall[]): unknown;
71
+ /**
72
+ * [F-R1] 中和动态错误摘要中的 stale-context 命中词(大小写不敏感子串替换为
73
+ * "[redacted]"——防拼接结果被 classifyFailureKind 误诊 stale_context)。
74
+ */
75
+ declare function neutralizeStalePatterns(text: string): string;
76
+ /**
77
+ * schema 模式下无有效 parsedOutput 时的三态失败归因(F-1)。
78
+ * 三态判定优先级:校验失败(有 isError 调用)> 从未调用 SO tool > 调用过但无 details。
79
+ */
80
+ declare function describeMissingParsedOutput(toolCalls: ToolCall[]): string | undefined;
81
+ /** 引擎侧 run 收集结果(AgentOutcome 的载荷装配点)。 */
82
+ interface CollectedOutcome {
83
+ content: string;
84
+ turns: number;
85
+ durationMs: number;
86
+ success: boolean;
87
+ error: string | undefined;
88
+ sessionId: string;
89
+ sessionFile: string | undefined;
90
+ toolCalls: ToolCall[];
91
+ parsedOutput: unknown;
92
+ usage: {
93
+ input: number;
94
+ output: number;
95
+ cacheRead: number;
96
+ cacheWrite: number;
97
+ cost?: number;
98
+ } | undefined;
99
+ failureKind: AgentFailureKind | undefined;
100
+ }
101
+ /**
102
+ * 从 ReplayRecordView + args 组装引擎侧结果。
103
+ *
104
+ * success 双来源判定(调用方传入):
105
+ * ① 子进程 spawn/执行失败 → args.success=false
106
+ * ② 执行成功但 record.lastError 非空(message_end stopReason=error)→ success=false
107
+ */
108
+ declare function collectOutcome(record: ReplayRecordView, args: CollectResultArgs): CollectedOutcome;
109
+
110
+ /** 按 record 取活跃子进程(undefined = 无句柄,对齐 getChildByRecord 语义)。 */
111
+ declare function getActiveChild(recordId: string): ChildProcess | undefined;
112
+ /** 全量收割(dispose):SIGTERM + 30s SIGKILL 升级;返回收割数。 */
113
+ declare function killAllActiveChildren(signal?: NodeJS.Signals): number;
114
+
115
+ /** run 的宿主回调面(server.ts 注入:协议通知 + host/* 反向请求)。 */
116
+ interface SpawnRunCallbacks {
117
+ /** AgentEvent 出口(→ `event` 通知,runId + 单调 seq 由 server 层组装)。 */
118
+ onEvent: (event: AgentEvent) => void;
119
+ /** sessionFile/sessionId 就绪回填(→ host/handleReady)。 */
120
+ onHandleReady?: (partial: {
121
+ sessionRef: Record<string, string>;
122
+ poolKey: string;
123
+ }) => void;
124
+ /** 一次性子进程 pid 上报(→ host/childSpawned)。 */
125
+ onChildSpawned?: (pid: number, recordId: string) => void;
126
+ /** 子进程状态变更(→ host/childStateChanged;killed 必含)。 */
127
+ onChildStateChanged?: (p: {
128
+ pid: number;
129
+ recordId: string;
130
+ state: "running" | "exited";
131
+ killed: boolean;
132
+ exitCode?: number;
133
+ signal?: string;
134
+ }) => void;
135
+ /** host/askUser 两阶段等待体(handler 缺失时队列自动 cancelled 降级)。 */
136
+ askUser?: (request: UiRequest) => Promise<UiResponse>;
137
+ /** text_delta 分流出口(→ host/streamDelta)。 */
138
+ onDelta?: (delta: string) => void;
139
+ /**
140
+ * [chatMode] agent_end(非 willRetry,队列排空)到达:本轮收敛。不 kill 子进程
141
+ * (长驻),由调用方(chat-session)上报 roundLifecycle settled 相位。
142
+ */
143
+ onChatRoundEnd?: () => void;
144
+ /**
145
+ * [chatMode] agent_settled(真空闲边界)到达:run 在此 resolve(exit 0 口径),
146
+ * 进程保活。调用方(chat-session)据此上报 idle 相位(usage + anchor)。
147
+ */
148
+ onChatAgentSettled?: () => void;
149
+ }
150
+ /** runSpawnOnce 的入参(协议 RunParams 的引擎侧还原形态)。 */
151
+ interface SpawnRunParams {
152
+ /** record 锚(= run.params.runId;镜像键 / relay RECORD_ID 重写源)。 */
153
+ recordId: string;
154
+ /** 完整 task 文本(含 schema 指令——协议 task.prompt)。 */
155
+ task: string;
156
+ /** agent 名(slug 派生 + prompt 临时文件名)。 */
157
+ agentName: string;
158
+ /** canonical "provider/id"(缺省回落 pi 自身解析)。 */
159
+ model: string | undefined;
160
+ /** thinking level 白名单字面量(协议 ctx 透传)。 */
161
+ thinkingLevel?: string;
162
+ /** subagent session 目录(--session-dir)。 */
163
+ sessionDir: string;
164
+ /** spawn cwd。 */
165
+ cwd: string;
166
+ /** schema env JSON 字符串(PI_WORKFLOW_SCHEMA 注入)。 */
167
+ schemaEnv?: string;
168
+ /** hard turn limit。 */
169
+ maxTurns?: number;
170
+ /** soft limit 后宽限轮数(默认 2)。 */
171
+ graceTurns?: number;
172
+ /** 中断信号(协议 cancel → server 层 AbortController)。 */
173
+ signal?: AbortSignal;
174
+ /** 根 session id(relay SESSION_ID 重写源,协议 ctx 还原)。 */
175
+ sessionRootId?: string;
176
+ /** 追加 system prompt 片段(agent body 之外的调用方片段)。 */
177
+ appendSystemPrompt?: string[];
178
+ /** skill 路径(--skill 多值)。 */
179
+ skillPaths?: string[];
180
+ /** agent 工具白名单(--tools)。 */
181
+ agentTools?: string[];
182
+ /** fork 源 session 文件(--fork)。 */
183
+ forkSource?: string;
184
+ /** 镜像 flag 覆盖(缺省自动镜像主进程 argv)。 */
185
+ mirrorFlags?: MirrorFlags;
186
+ /** resume 目标 session 文件(冷续写:--session 续写原文件)。 */
187
+ resumeSessionFile?: string;
188
+ /**
189
+ * [v1.x chat 会话形态] 长驻模式:agent_end(非 willRetry)不 kill 子进程(轮收敛
190
+ * 交 callbacks.onChatRoundEnd 上报),agent_settled(真空闲)resolve run(exit 0
191
+ * 口径,进程保活——对齐 inproc chatMode「进程长驻」语义)。缺省 = 一次性 run。
192
+ */
193
+ chatMode?: boolean;
194
+ }
195
+ /** runSpawnOnce 的产物。 */
196
+ interface SpawnRunResult extends Omit<CollectedOutcome, "sessionId"> {
197
+ /** 会话头身份(header / get_state 握手回填;全 miss 时 undefined)。 */
198
+ sessionId: string | undefined;
199
+ }
200
+ declare function runSpawnOnce(params: SpawnRunParams, callbacks: SpawnRunCallbacks): Promise<SpawnRunResult>;
201
+
202
+ /** 引擎 → 宿主反向通道发射面(server 构造后注入;roundLifecycle/streamDelta 为 v1.x 新面)。 */
203
+ interface ChatHostChannels {
204
+ /** host/streamDelta(续聊轮 recordId 键形态;首轮 runId 键经 server 既有 run wiring)。 */
205
+ streamDelta(params: HostStreamDeltaParams): void;
206
+ /** host/roundLifecycle(三相位;键形态由会话状态决定:首轮 runId / 续聊 recordId)。 */
207
+ roundLifecycle(params: HostRoundLifecycleParams): void;
208
+ /** host/askUser(chat 会话跨 run 存活,runId 固定为 spawn 轮的 runId——W3 消费注意)。 */
209
+ askUser(runId: string, request: UiRequest): Promise<UiResponse>;
210
+ }
211
+ /** startRound 的宿主回调面(server 注入:首轮事件通知 + run 键 delta + handle 回填)。 */
212
+ interface ChatRoundStartOptions {
213
+ /** spawn 轮的 runId(首轮 roundLifecycle/streamDelta 关联键 + askUser 关联键)。 */
214
+ runId: string;
215
+ /** 首轮 AgentEvent 出口(→ `event` 通知,runId + seq 由 server 层组装)。 */
216
+ onEvent(event: AgentEvent): void;
217
+ /** 首轮 text_delta 出口(runId 键——经 server 既有 stream wiring)。 */
218
+ stream?: EngineStream;
219
+ onHandleReady?(partial: {
220
+ sessionRef: Record<string, string>;
221
+ poolKey: string;
222
+ }): void;
223
+ onChildSpawned?(pid: number, recordId: string): void;
224
+ }
225
+ /** 会话 spawn 执行器(缺省 runSpawnOnce chatMode 形态;测试注入 fake)。 */
226
+ type ChatSpawnExecutor = (params: SpawnRunParams, callbacks: SpawnRunCallbacks) => Promise<SpawnRunResult>;
227
+ /** ChatSessionRegistry 构造依赖。 */
228
+ interface ChatSessionRegistryDeps {
229
+ spawnRunner?: ChatSpawnExecutor;
230
+ }
231
+ /**
232
+ * chat 会话注册表(引擎进程内的长驻会话状态面)。
233
+ *
234
+ * 生命周期:startRound(首轮/冷续 spawn)建立会话 → 首轮 agent_settled 后 run 应答、
235
+ * 进程保活 → interact message 续聊轮(recordId 键事件)→ interact close / cancel /
236
+ * 子进程退出消亡。dispose 收割由 spawn-runner 的 killAllActiveChildren 兜底(PiEngine
237
+ * dispose 调用),会话条目随子进程 close 事件自清。
238
+ */
239
+ declare class ChatSessionRegistry {
240
+ private readonly executor;
241
+ private channels;
242
+ private readonly sessions;
243
+ constructor(deps?: ChatSessionRegistryDeps);
244
+ /** server 构造后注入反向通道发射面(未注入 = 无宿主面:帧丢弃 + askUser 自动 cancelled)。 */
245
+ bindHostChannels(channels: ChatHostChannels | undefined): void;
246
+ /** recordId 是否有活会话(interact 路由判据;不建立会话)。 */
247
+ has(recordId: string): boolean;
248
+ /**
249
+ * run 会话形态入口(首轮 + 冷续):spawn 长驻子进程 + 首轮 prompt,本轮 agent_settled
250
+ * (真空闲)时 resolve(exit 0 口径,进程保活——对齐 inproc chatMode 首轮 resolve 语义)。
251
+ * resume 锚点存在 = 冷续(--session 续写原文件),不存在 = 首轮新建。
252
+ */
253
+ startRound(params: SpawnRunParams, opts: ChatRoundStartOptions): Promise<SpawnRunResult>;
254
+ /**
255
+ * interact message 热路径:prompt + streamingBehavior 直写 stdin(pi 权威裁决
256
+ * busy/idle:busy 时 followUp 入队 / steer 抢占,idle 时开新 turn——pi 上游语义,
257
+ * abort 不清 followUp 队列)。冷路径(无活进程)→ engine_session_not_resumable +
258
+ * 冷续指引(宿主发新 run chat+resume 接续,D1-A)。
259
+ */
260
+ deliverMessage(recordId: string, text: string, interrupt: boolean): InteractResult;
261
+ /**
262
+ * interact close:force = 立即杀链收割(在途轮 → failed aborted);缺省 = 优雅关闭
263
+ * (在途轮标记 closeAfterRound,agent_settled 收口后收割——对齐 inproc closeSubagent
264
+ * 的 force 分流);idle 态(无在途轮)立即收割(对齐 closeChatIdle 的进程回收)。
265
+ */
266
+ close(recordId: string, force: boolean): InteractResult;
267
+ /**
268
+ * interact cancel(D3 协议层收敛语义,对齐 run 域 cancel 形态):
269
+ * 受理(SIGTERM——run 域 AbortController→SIGTERM 同构)→ 等待目标轮次终态相位
270
+ * (roundLifecycle settled/idle/failed)→ 超 CANCEL_SETTLE_GRACE_MS 未收敛走与
271
+ * run 域同构的杀链升级(SIGTERM → grace → SIGKILL)。应答在收敛/升级完成后返回
272
+ * (协议层收敛语义);无在途轮时无收敛对象,受理即返回(进程回收经 close 事件收口)。
273
+ */
274
+ cancel(recordId: string): Promise<InteractResult>;
275
+ /** agent_end(非 willRetry,队列排空):本轮收敛 → settled 相位(带本轮用量)。 */
276
+ private handleRoundEnd;
277
+ /** agent_settled(真空闲):idle 相位(usage + anchor 回填)+ 优雅关闭收割。 */
278
+ private handleAgentSettled;
279
+ /** 子进程退出:会话消亡;在途轮 → failed 相位(引擎主动杀 = aborted / 否则 crashed)。 */
280
+ private handleChildExited;
281
+ /**
282
+ * 相位发射(关联键 = 首轮 runId / 续聊 recordId)+ 轮终等待体逐一 resolve。
283
+ * superseded 会话抑制宿主发射(S5,守卫放单一咽喉点而非仅 handleChildExited):
284
+ * 同 recordId 冷续 run 已重建会话,旧会话残留终态帧(killChain 杀死在途轮的 failed、
285
+ * SIGTERM 优雅收口竞态下的 settled+idle——pi trap 语义先收口再退)以 recordId 键发射
286
+ * 会与新会话在途轮同键串扰,core 可能把旧轮终态误配到新轮(误终态化)。superseded
287
+ * 信号本身 = 新 run 的 start(宿主主动发起),core 无需旧会话帧。等待体仍逐一
288
+ * resolve:cancel 等待中 killReason 被后续冷续覆写为 superseded 的极端时序下收敛
289
+ * 不悬挂。
290
+ *
291
+ * [F-4 S6 引擎半边] 旧轮的 idle 相位(agent_settled 空闲边界)在 settled→idle 间隙
292
+ * 宿主已投递新轮时抑制宿主发射(`armedSeq !== settledSeq` = settled 以来有新轮 arm,
293
+ * 该 idle 帧属旧轮——时序恒晚于新轮投递)。与 S6 的 armedSeq 守卫(handleAgentSettled
294
+ * 保 settled 相位、不清新轮 roundActive)同模式:旧 idle 帧若放行,core 的
295
+ * handleChatRoundPhase(idle) 无轮次身份可判——disarmRoundFromProtocol 拆掉新轮中段
296
+ * 守护 + armChatIdleTimer 挂 5min idle timer → 新轮进行中超 5min 被误杀。新轮的
297
+ * idle 由新轮自身 agent_settled 发射(其 armedSeq === settledSeq)。等待体仍逐一
298
+ * resolve(在途 cancel 的收敛兜底语义不变)。
299
+ */
300
+ private emitPhase;
301
+ /** message_end 用量累加(轮内增量;interact 轮无 event 通知通道,用量经相位帧回填)。 */
302
+ private accumulateUsage;
303
+ /** 轮终等待(cancel 收敛判据;超时 resolve false——不清理其他等待体)。 */
304
+ private waitForRoundTerminal;
305
+ }
306
+
307
+ /** PiEngine 构造依赖。 */
308
+ interface PiEngineDeps {
309
+ /**
310
+ * 数据根(协议 initialize 的 hostInfo.dataRoot;subagent session 目录相对它推导)。
311
+ * 缺省读 env XYZ_AGENT_DATA_DIR——两者皆无时 probe 报错(resolveEngineDataDir 语义,
312
+ * 显式报 engine_not_found 附期望路径,不猜 cwd)。
313
+ */
314
+ dataDir?: string;
315
+ /** 版本探测执行器(测试注入 fake 避免真实子进程)。 */
316
+ probeVersion?: (invocation: PiInvocation) => Promise<string | undefined>;
317
+ /** spawn 执行器(测试注入 fake;缺省 runSpawnOnce)。 */
318
+ spawnRunner?: (params: Parameters<typeof runSpawnOnce>[0], callbacks: SpawnRunCallbacks) => Promise<SpawnRunResult>;
319
+ }
320
+ /** pi 引擎适配器(EnginePort 实现,协议服务器的驱动对象)。 */
321
+ declare class PiEngine implements EnginePort {
322
+ readonly id = "pi";
323
+ private readonly deps;
324
+ private probeCache;
325
+ /** [v1.x] chat 会话注册表(长驻会话状态面——见 chat-session.ts 文件头)。 */
326
+ private readonly chatSessions;
327
+ constructor(deps?: PiEngineDeps);
328
+ /** pi 链路实际接通的能力(与 core PiEngine.capabilities() 逐位一致——manifest 同源)。 */
329
+ capabilities(): EngineCapabilities;
330
+ /** 探针(D7):invocation 可解析(二进制/脚本存在)+ 版本解析。relay:false 显式
331
+ * 直连——探针测 pi 本体可解析性,经 relay 探到的是 runtime 健康,语义错位。 */
332
+ probe(opts?: {
333
+ force?: boolean;
334
+ }): Promise<ProbeReport>;
335
+ /** run 的引擎侧实现:协议 RunParams → spawn-runner 单次执行。
336
+ *
337
+ * run 期间事件经 ctx.onEvent(server 层 → `event` 通知);session 身份经
338
+ * ctx.onHandleReady(→ host/handleReady);子进程 pid/状态经镜像回调上报。
339
+ * 抛错语义(core PiEngine.run ①):prepare 期失败 reject,不产生 handle。
340
+ *
341
+ * [v1.x] ctx.chat 存在 = chat 会话形态(首轮/冷续):委托 chat-session 长驻执行
342
+ * (agent_end 不 kill、agent_settled resolve——本轮收口进程保活,续聊经 interact)。 */
343
+ run(task: EngineAgentCallOpts, ctx: RunContext): Promise<{
344
+ handle: EngineHandle;
345
+ outcome: AgentOutcome;
346
+ }>;
347
+ /**
348
+ * [v1.x] run 会话形态主体(首轮/冷续,chat-domain 设计 §3.2 D1-A):spawn 长驻子进程
349
+ * + 首轮 prompt,本轮 agent_settled(真空闲)resolve(进程保活)。record 锚定键 =
350
+ * chat.recordId(区别于一次性 run 的 runId 锚定——interact/roundLifecycle 据此定位);
351
+ * resume 锚点存在 = 冷续(--session 续写原文件),不存在 = 首轮新建。
352
+ */
353
+ private runChatRound;
354
+ /**
355
+ * D1 交互控制面。[v1.x] chat 会话(chat-session 命中)优先路由:
356
+ * - message:热路径 sendPromptCommand + streamingBehavior(interrupt=steer 抢占/
357
+ * 缺省 followUp 排队——pi 上游语义)+ EPIPE 兜底(耗尽 → roundLifecycle failed);
358
+ * 冷路径(进程死)→ engine_session_not_resumable + 冷续指引(宿主发新 run chat+resume);
359
+ * - close:force=杀链立即收割 / 缺省=优雅(轮收口后收割);
360
+ * - cancel:D3 收敛语义(受理 → 等轮终相位 → 超 CANCEL_SETTLE_GRACE_MS 杀链升级)。
361
+ *
362
+ * 未命中(一次性 run 的活跃子进程 / 冷句柄)走下方既有路径:message 热路径直写、
363
+ * close = SIGTERM、cancel = SIGTERM(无收敛等待——run 域 cancel 帧另有 AbortController
364
+ * 通道,收敛由 run 应答承载)。
365
+ */
366
+ interact(handle: EngineHandle, action: InteractAction): Promise<InteractResult>;
367
+ /** chat 会话命中分支(长驻):cancel/close/message 直派 chat-session
368
+ * (close force=杀链立即收割 / 缺省=优雅;message streamingBehavior 由 interrupt 决定)。 */
369
+ private interactChatSession;
370
+ /** 未命中分支:一次性 run 的活跃子进程 / 冷句柄——message 热路径直写 stdin、
371
+ * close/cancel = SIGTERM(run 域 cancel 帧另有 AbortController 通道,收敛由 run 应答承载)。 */
372
+ private interactRunDomain;
373
+ /** message 热路径(进程活)sendPromptCommand 直写 stdin + EPIPE 兜底
374
+ * (连续失败达阈值 → 抛错升级为 engine_interact_failed;未达 → not_resumable 降级)。 */
375
+ private deliverHotPathMessage;
376
+ /**
377
+ * read 三级降级(协议化形态):②级 journal 重放(SDK journal-replay 纯投影)
378
+ * → ③级 outcome-only。①级 pi 原生读取(session-reconstructor)按 §2.7 保持
379
+ * core,不随迁(deviations 登记)。
380
+ */
381
+ read(handle: EngineHandle): Promise<SessionView>;
382
+ /** dispose:全量收割活跃子进程 + 清 EPIPE 计数(幂等)。 */
383
+ dispose(): Promise<void>;
384
+ private askUserHandler;
385
+ /** server 层注入 host/askUser 两阶段等待体(ui-request-queue 消费)。 */
386
+ bindAskUser(handler: ((req: UiRequest) => Promise<UiResponse>) | undefined): void;
387
+ /**
388
+ * server 层注入 [v1.x] chat 会话的反向通道发射面(host/roundLifecycle、recordId 键
389
+ * host/streamDelta、会话级 host/askUser)。会话跨 run 存活,绑定是引擎进程生命周期
390
+ * 级(区别于一次性 run 的 per-run askUser 绑定)。
391
+ */
392
+ bindHostChannels(channels: ChatHostChannels | undefined): void;
393
+ }
394
+
395
+ /** 出站帧写入面(main.ts 注入 process.stdout;测试注入内存缓冲)。 */
396
+ type FrameWriter = (frame: unknown) => void;
397
+ /** 入站帧来源(readline 已拆行的请求帧 + 反向请求应答帧混流)。 */
398
+ interface EngineProtocolServerOptions {
399
+ /** stdout 写入面(每帧一行 JSON)。 */
400
+ write: FrameWriter;
401
+ /** 引擎实例(缺省 createDefaultPiEngine——测试注入 fake/DI 实例)。 */
402
+ engine?: EnginePort & {
403
+ bindAskUser?(handler: ((req: UiRequest) => Promise<UiResponse>) | undefined): void;
404
+ /** [v1.x] chat 会话反向通道发射面绑定(roundLifecycle / recordId 键 streamDelta)。 */
405
+ bindHostChannels?(channels: ChatHostChannels | undefined): void;
406
+ };
407
+ /** 反向请求计时面(armEngineSelfDestruct 产物;缺省不计时——测试用)。 */
408
+ reverseClock?: ReverseRequestClock;
409
+ /** 应答等待缺省超时(反向请求两阶段等待上限兜底;默认 REVERSE_TIMEOUT_DEFAULT_MS)。 */
410
+ reverseTimeoutMs?: number;
411
+ }
412
+ /** 构造缺省 pi 引擎(main.ts 的 server 构造缺省值;测试注入 fake)。 */
413
+ declare function createDefaultPiEngine(): PiEngine;
414
+ /**
415
+ * 引擎协议服务器。生命周期 = 进程生命周期(单引擎实例,无重建面——崩溃重建归
416
+ * core EngineClient:杀进程再 spawn)。dispose 方法释放引擎常驻资源但进程不退出。
417
+ */
418
+ declare class EngineProtocolServer {
419
+ private readonly write;
420
+ private readonly engine;
421
+ private readonly reverseClock;
422
+ private readonly reverseTimeoutMs;
423
+ private readonly activeRuns;
424
+ private readonly reversePending;
425
+ /** 10 正向方法 → EnginePort 装配表(构造期冻结;表驱动分发)。 */
426
+ private readonly dispatchTable;
427
+ private revSeq;
428
+ private initialized;
429
+ constructor(opts: EngineProtocolServerOptions);
430
+ /** 入站帧消费(请求帧 + 反向请求应答帧;main.ts 的行解析器拆行后喂入)。 */
431
+ handleFrame(frame: unknown): void;
432
+ /** 10 正向方法 → EnginePort 装配表(协议载荷 cast 收敛在各方法适配行)。 */
433
+ private buildDispatchTable;
434
+ /** 10 正向方法分发(表驱动;未知方法 → engine_protocol_unknown_method)。 */
435
+ private dispatch;
436
+ private initialize;
437
+ private run;
438
+ /** run.chat 帧校验 + chat 能力位 gate(A6 方向防御):recordId 非空 + conversation
439
+ * 位 unsupported 同步拒——判据单源 = SDK assertChatConversationSupported(与 core
440
+ * capability-gate 同一能力位,防两侧判据漂移)。本引擎 manifest 声明 native,
441
+ * 此处仅防御 manifest/实装漂移。 */
442
+ private assertChatRunFrame;
443
+ /** RunContext 装配(协议 ctx 还原 + host/* 反向通道接线;事件 seq 由 emitEvent 计数)。 */
444
+ private buildRunContext;
445
+ private cancel;
446
+ private interact;
447
+ private read;
448
+ private validateModel;
449
+ private emitEvent;
450
+ /** 反向请求发送(公开面:main.ts 的 host/log 桥接消费;内部 run 通道同路)。 */
451
+ reverseRequest(method: string, params: unknown): Promise<unknown>;
452
+ private reverseRequestInternal;
453
+ /** 反向请求应答落位。
454
+ *
455
+ * ack 两阶段(R9-2):人机交互通道(host/askUser / host/permission)宿主先回
456
+ * `{ack:true}`——只 ack 计时面(移出 in-flight 自灭计时),**不终结等待**;最终
457
+ * 结果帧才 settle。数据面通道宿主直接回终态({ok:true} 等),ack 即 settle。 */
458
+ private settleReverse;
459
+ }
460
+
461
+ /** pi 引擎的 registry key(D9:缺省引擎 = 'pi')。 */
462
+ declare const PI_ENGINE_ID = "pi";
463
+ /** pi 适配器版本(handle.adapterVersion 数据源——golden 样本对齐排查锚点)。 */
464
+ declare const PI_ADAPTER_VERSION = "1.0.0";
465
+ /** pi 无隔离池(PI_CODING_AGENT_DIR 全局一份,设计 §3.3.9),poolKey 恒 'shared'。 */
466
+ declare const PI_POOL_KEY = "shared";
467
+
468
+ /**
469
+ * pi stdout 的 SdkEvent 形态(core execution/types.ts SdkEvent 的结构子集——
470
+ * spawn-runner 的翻译 switch 只消费这些字段;SDK 契约类型未收该类型,包内自持)。
471
+ */
472
+ interface SdkEvent {
473
+ type: string;
474
+ toolCallId?: string;
475
+ toolName?: string;
476
+ args?: unknown;
477
+ result?: ToolCallResult;
478
+ isError?: boolean;
479
+ message?: {
480
+ usage?: {
481
+ input?: number;
482
+ output?: number;
483
+ cacheRead?: number;
484
+ cacheWrite?: number;
485
+ cost?: {
486
+ total: number;
487
+ };
488
+ };
489
+ stopReason?: string;
490
+ errorMessage?: string;
491
+ };
492
+ reason?: string;
493
+ assistantMessageEvent?: {
494
+ type?: string;
495
+ delta?: string;
496
+ };
497
+ [key: string]: unknown;
498
+ }
499
+ /** pi stdout header 行(session 元信息)。type 固定为 "session"。 */
500
+ interface SpawnSessionHeader {
501
+ readonly type: "session";
502
+ readonly id: string;
503
+ readonly timestamp: string;
504
+ readonly cwd: string;
505
+ readonly parentSession?: string;
506
+ readonly version?: number;
507
+ }
508
+ /** Pi 原生 extension_ui_request 的方法特定字段(按 method 平铺)。
509
+ * 与 Pi rpc-types.ts L230-265 的 RpcExtensionUIRequest 1:1 对应。 */
510
+ type ExtensionUiRequest = {
511
+ method: "select";
512
+ title: string;
513
+ options: string[];
514
+ timeout?: number;
515
+ } | {
516
+ method: "confirm";
517
+ title: string;
518
+ message: string;
519
+ timeout?: number;
520
+ } | {
521
+ method: "input";
522
+ title: string;
523
+ placeholder?: string;
524
+ timeout?: number;
525
+ } | {
526
+ method: "editor";
527
+ title: string;
528
+ prefill?: string;
529
+ } | {
530
+ method: "notify";
531
+ message: string;
532
+ notifyType?: "info" | "warning" | "error";
533
+ } | {
534
+ method: "setStatus";
535
+ statusKey: string;
536
+ statusText: string | undefined;
537
+ } | {
538
+ method: "setWidget";
539
+ widgetKey: string;
540
+ widgetLines: string[] | undefined;
541
+ widgetPlacement?: "aboveEditor" | "belowEditor";
542
+ } | {
543
+ method: "setTitle";
544
+ title: string;
545
+ } | {
546
+ method: "set_editor_text";
547
+ text: string;
548
+ } | {
549
+ method: string;
550
+ raw: Record<string, unknown>;
551
+ };
552
+ /** parseSpawnLine 的分类结果。 */
553
+ type ParsedSpawnLine = {
554
+ kind: "header";
555
+ header: SpawnSessionHeader;
556
+ } | {
557
+ kind: "event";
558
+ event: SdkEvent;
559
+ } | {
560
+ kind: "response";
561
+ id?: string;
562
+ command: string;
563
+ success: boolean;
564
+ data?: unknown;
565
+ error?: string;
566
+ } | {
567
+ kind: "extension_ui_request";
568
+ id: string;
569
+ request: ExtensionUiRequest;
570
+ } | {
571
+ kind: "invalid";
572
+ raw: string;
573
+ error: string;
574
+ };
575
+ /**
576
+ * 解析 pi stdout 的一行。
577
+ *
578
+ * 分类规则(判定顺序关键——extension_ui_request 必须在 event 之前判定):
579
+ * - 空白行 → null;合法 JSON 按 type 值分派 header/ui_request/response/event
580
+ * - 非法 JSON / 无 type → invalid(记录 error,不抛——单行损坏不中断流)
581
+ */
582
+ declare function parseSpawnLine(line: string): ParsedSpawnLine | null;
583
+ /**
584
+ * 从已收集的 header + 事件流推导子进程的 session 文件路径。
585
+ *
586
+ * pi session 文件命名规则(session-manager.ts:846):
587
+ * `${fileTimestamp}_${sessionId}.jsonl`,fileTimestamp = header.timestamp 的
588
+ * 冒号/点替换为连字符。
589
+ */
590
+ declare function deriveSessionFilePath(header: SpawnSessionHeader, sessionDir: string): string;
591
+ /**
592
+ * 在 sessionDir 中按 sessionId 后缀匹配查找实际存在的 session 文件(命名规则
593
+ * 变化时的兜底)。@returns 匹配到的文件绝对路径,或 undefined。
594
+ */
595
+ declare function findSessionFileByHeaderId(sessionDir: string, sessionId: string): string | undefined;
596
+
597
+ /** pi CLI 认可的 thinking level 后缀白名单。 */
598
+ type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "max";
599
+ /**
600
+ * spawn 侧已裁决的模型身份:`--model` 值恒为 `${provider}/${id}`(+ 可选白名单
601
+ * `:level` 后缀)。解析自协议 ctx.model 的 canonical "provider/id" 词形。
602
+ */
603
+ interface SpawnModelRef {
604
+ provider: string;
605
+ id: string;
606
+ }
607
+ /** "provider/id" canonical 词形 → SpawnModelRef(无斜杠/畸形 → undefined)。 */
608
+ declare function parseSpawnModelRef(ref: string | undefined): SpawnModelRef | undefined;
609
+ /**
610
+ * 组装 pi CLI 参数(不含 task 本身——task 由 spawn 后 sendPromptCommand 写 stdin)。
611
+ *
612
+ * [单写者不变量] session JSONL 完整性依赖「每 session 单写进程」:子进程写独立
613
+ * subagent sessionDir,任何改动不得让两个进程指向同一 session 文件写路径。
614
+ */
615
+ declare function buildSpawnArgs(params: {
616
+ modelRef: SpawnModelRef;
617
+ thinkingLevel: ThinkingLevel | undefined;
618
+ agentTools: string[] | undefined;
619
+ appendSystemPromptPath: string | undefined;
620
+ sessionDir: string;
621
+ /** resume 目标 session 文件路径(--session 续写原文件而非新建)。 */
622
+ sessionFile?: string;
623
+ forkSource: string | undefined;
624
+ skillPaths: string[] | undefined;
625
+ /** 镜像自主进程 argv 的 flag(--no-extensions/--approve/--extension/--no-context-files)。 */
626
+ mirrorFlags?: MirrorFlags;
627
+ }): string[];
628
+ /**
629
+ * 将 schemaEnv 注入 childEnv。
630
+ *
631
+ * [SO-DATA-4] 注入前按 UTF-8 字节长度校验,超 SCHEMA_ENV_MAX_BYTES(256KiB)
632
+ * fail-fast 拒绝:env 值过大叠加全量继承的 process.env 可能触发 execve 的 E2BIG。
633
+ *
634
+ * @throws Error schemaEnv 序列化后超过 SCHEMA_ENV_MAX_BYTES
635
+ */
636
+ declare function applySchemaEnvToChildEnv(childEnv: Record<string, string | undefined>, schemaEnv?: string): void;
637
+ /**
638
+ * 构建环境信息块(P7 防注入:环境数据标记为 data,非指令)。
639
+ * git branch 异步获取(execFile),失败静默为空(非 git 目录 / git 缺失是高频正常路径)。
640
+ *
641
+ * @param forkDepth 当前 fork 链深度(undefined=非 fork session,视为 0)
642
+ * @param nestingDepth 通用嵌套深度(undefined=顶层)
643
+ */
644
+ declare function buildEnvBlock(cwd: string, forkDepth?: number, nestingDepth?: number): Promise<string>;
645
+ /**
646
+ * 把 pi assistantMessageEvent 分流为 text_delta / thinking_delta AgentEvent。
647
+ * toolcall_delta 等其他带 delta 的事件不混入 text stream。
648
+ */
649
+ declare function mapAssistantMessageDelta(ame: {
650
+ type?: string;
651
+ delta?: string;
652
+ }): {
653
+ type: "text_delta";
654
+ delta: string;
655
+ } | {
656
+ type: "thinking_delta";
657
+ delta: string;
658
+ } | null;
659
+
660
+ /** 连续 EPIPE 失败阈值:达到即不再尝试 resume(避免无限 spawn → EPIPE → resume 循环)。 */
661
+ declare const EPIPE_FAILURE_THRESHOLD = 2;
662
+ /** 递增 recordId 的 EPIPE 连续失败计数,返回递增后的新计数。 */
663
+ declare function recordEpipeFailure(recordId: string): number;
664
+ /** 成功写入时清零某 record 的 EPIPE 连续失败计数(热路径成功 → 重置)。 */
665
+ declare function clearEpipeFailure(recordId: string): void;
666
+ /** dispose 时清空所有 EPIPE 计数(防跨 session 泄漏)。 */
667
+ declare function resetAllEpipeFailures(): void;
668
+ /**
669
+ * 按 UiResponse 形状构造 Pi 原生 extension_ui_response 并写 stdin。
670
+ *
671
+ * SR-5:ack(fire-and-forget)不写 stdin——Pi 对 fire-and-forget method 不期待响应。
672
+ * 其他三种 shape(value/confirmed/cancelled)按对应字段写。
673
+ *
674
+ * [R1] 背压检查:write 返回 false 时记 warn(不阻塞,内核缓冲会随后排空)。
675
+ * [R2] 序列化失败(循环引用/BigInt)降级 cancelled——宁可取消单次 dialog 也不崩进程。
676
+ */
677
+ declare function respond(child: ChildProcess, id: string, out: UiResponse, signal?: AbortSignal): void;
678
+ /**
679
+ * spawn 后向 rpc 子进程 stdin 写 prompt 命令,驱动 agent 开始处理 task。
680
+ *
681
+ * pi 的 runRpcMode 只通过 stdin RpcCommand 驱动——positional task arg / -p flag
682
+ * 在 rpc mode 下被 resolveAppMode 无视,必须在 spawn 后主动喂 prompt 命令。
683
+ *
684
+ * [V2 决策 3] chatMode 续聊热路径用 prompt + streamingBehavior 统一投递,pi 权威
685
+ * 裁决 busy/idle:busy 时 followUp 入队/steer 抢占;idle 时开新 turn。省略
686
+ * streamingBehavior 时行为不变(首帧 prompt)。
687
+ */
688
+ declare function sendPromptCommand(child: ChildProcess, task: string, options?: {
689
+ streamingBehavior?: "followUp" | "steer";
690
+ }): void;
691
+ /**
692
+ * 向子进程 stdin 写 get_state 命令,查询 sessionFile/sessionId(FR-4 RPC 握手)。
693
+ *
694
+ * @returns 请求 id(用于匹配 response)
695
+ */
696
+ declare function sendGetStateCommand(child: ChildProcess): string;
697
+
698
+ /** 队列依赖(引擎侧形态:host/askUser 反向请求的应答回调由 server 注入)。 */
699
+ interface UiRequestQueueDeps {
700
+ /**
701
+ * UI 请求处理回调(= host/askUser 反向请求的两阶段等待体)。未设置时不再
702
+ * 静默忽略——本地去重告警 + respond(cancelled)(子进程不永久挂起)。
703
+ */
704
+ uiRequestHandler?: UiRequestHandler;
705
+ }
706
+ /**
707
+ * 创建 UI 请求队列。返回 enqueue 函数,调用方将 extension_ui_request 入队。
708
+ *
709
+ * 多个 extension_ui_request 并发到达时,队列保证 FIFO 串行处理。
710
+ *
711
+ * 设计:队列是 run 生命周期内的闭包状态(非模块级),每个子进程实例独立队列,
712
+ * 无跨 session 泄漏。
713
+ *
714
+ * @param child 子进程(stdin 写入 extension_ui_response)
715
+ * @param deps 队列依赖(含 uiRequestHandler 回调)
716
+ * @returns enqueue 函数:(id, request) => void
717
+ */
718
+ declare function createUiRequestQueue(child: ChildProcess, deps: UiRequestQueueDeps): (id: string, request: ExtensionUiRequest) => void;
719
+
720
+ /**
721
+ * 预防性 wrap-up 提示(启动时注入 --append-system-prompt)。
722
+ *
723
+ * rpc steer 通道未接通时的短期补偿(见 spawn-runner limiter 注释)。
724
+ */
725
+ declare const WRAP_UP_HINT: string;
726
+ /** turn limiter 配置。 */
727
+ interface TurnLimiterOptions {
728
+ maxTurns: number;
729
+ graceTurns: number;
730
+ steer: (msg: string) => void;
731
+ abort: () => void;
732
+ }
733
+ /**
734
+ * soft/hard turn 限制。
735
+ *
736
+ * onTurnEnd(currentTurns):
737
+ * 已 aborted 或 maxTurns<=0(禁用)→ 直接 return
738
+ * currentTurns >= maxTurns 且未 steer → steer(WRAP_UP_MESSAGE)(仅一次)
739
+ * 已 steer 且 currentTurns >= maxTurns + graceTurns → abort()(仅一次)
740
+ */
741
+ interface TurnLimiter {
742
+ /** 每次 turn_end 调用。 */
743
+ onTurnEnd(currentTurns: number): void;
744
+ /** 重置 steered/aborted 标志(新一轮开始)。 */
745
+ reset(): void;
746
+ /** 是否已发过 steer(诊断用)。 */
747
+ readonly didSteer: boolean;
748
+ /** 是否已 abort(诊断用)。 */
749
+ readonly didAbort: boolean;
750
+ }
751
+ /** 工厂函数。 */
752
+ declare function createTurnLimiter(opts: TurnLimiterOptions): TurnLimiter;
753
+
754
+ /** get_state 握手结果。 */
755
+ interface GetStateResult {
756
+ sessionFile?: string;
757
+ sessionId?: string;
758
+ }
759
+ /** get_state response 监听器注册函数形态(stdout pump / 测试注入)。 */
760
+ type AddGetStateResponseListener = (id: string, resolver: (data: unknown) => void) => void | (() => void);
761
+ /**
762
+ * FR-4: 通过 get_state RPC 查询子进程获取 sessionFile/sessionId。
763
+ *
764
+ * 最多重试 GET_STATE_MAX_RETRIES 次,单次超时 GET_STATE_TIMEOUT_MS 后等待
765
+ * GET_STATE_RETRY_INTERVAL_MS 再发起下一次重试。
766
+ */
767
+ declare function performGetStateHandshake(child: ChildProcess, addResponseListener: AddGetStateResponseListener): Promise<GetStateResult>;
768
+ /**
769
+ * [T1/RC-1] 单次 get_state 请求(agent_end 决策点惰性回补专用)。
770
+ *
771
+ * 不做重试循环、不 share 握手语义:调用方在子进程 idle 时现场补一次查询,
772
+ * 超时/失败即放弃,由调用方走既有保守分支。永不 reject——stdin 已断的同步写
773
+ * 失败按「回补失败」处理 resolve 空对象。
774
+ */
775
+ declare function requestGetStateOnce(child: ChildProcess, addResponseListener: AddGetStateResponseListener, timeoutMs: number): Promise<GetStateResult>;
776
+
777
+ /**
778
+ * 从 handle 自描述的 journalPath 重放会话视图。
779
+ *
780
+ * @returns undefined = journalPath 缺失 / 文件不存在 / 无有效事件(降级链落 ③级)
781
+ */
782
+ declare function replayJournalToSessionView(handle: EngineHandle, engineId: string): SessionView | undefined;
783
+
784
+ export { type ChatHostChannels, type ChatRoundStartOptions, ChatSessionRegistry, type ChatSessionRegistryDeps, type ChatSpawnExecutor, DETERMINISTIC_SCHEMA_FAILURE_PREFIX, EPIPE_FAILURE_THRESHOLD, EngineProtocolServer, type GetStateResult, type MirrorFlags, PI_ADAPTER_VERSION, PI_ENGINE_ID, PI_POOL_KEY, PiEngine, type PiEngineDeps, type PiInvocation, STALE_CONTEXT_PATTERNS, type SpawnModelRef, type SpawnRunCallbacks, type SpawnRunParams, type SpawnRunResult, type ThinkingLevel, type UiRequestQueueDeps, WRAP_UP_HINT, applySchemaEnvToChildEnv, buildEnvBlock, buildSpawnArgs, classifyFailureKind, clearEpipeFailure, collectOutcome, createDefaultPiEngine, createTurnLimiter, createUiRequestQueue, deriveSessionFilePath, describeMissingParsedOutput, extractParsedOutput, findSessionFileByHeaderId, getActiveChild, getPiInvocation, isDeterministicSchemaFailureMsg, isStaleContextErrorMsg, killAllActiveChildren, mapAssistantMessageDelta, mirrorMainProcessFlags, neutralizeStalePatterns, parseSpawnLine, parseSpawnModelRef, performGetStateHandshake, recordEpipeFailure, replayJournalToSessionView, requestGetStateOnce, resetAllEpipeFailures, respond, runSpawnOnce, sendGetStateCommand, sendPromptCommand };