@zhushanwen/pi-subagent-workflow 0.1.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.
Files changed (143) hide show
  1. package/agents/context-builder.md +17 -0
  2. package/agents/general-purpose.md +16 -0
  3. package/agents/oracle.md +17 -0
  4. package/agents/planner.md +17 -0
  5. package/agents/researcher.md +17 -0
  6. package/agents/reviewer.md +17 -0
  7. package/agents/scout.md +17 -0
  8. package/agents/worker.md +16 -0
  9. package/examples/README.md +43 -0
  10. package/examples/chain.example.js +92 -0
  11. package/examples/map-reduce.example.js +99 -0
  12. package/examples/parallel.example.js +82 -0
  13. package/examples/scatter-gather.example.js +106 -0
  14. package/index.ts +1 -0
  15. package/package.json +66 -0
  16. package/skills/workflow-script-format/SKILL.md +328 -0
  17. package/src/execution/__tests__/agent-registry.test.ts +164 -0
  18. package/src/execution/__tests__/agent-result-mapper.test.ts +128 -0
  19. package/src/execution/__tests__/alive-store.test.ts +147 -0
  20. package/src/execution/__tests__/bg-notify-render.test.ts +256 -0
  21. package/src/execution/__tests__/concurrency-pool.test.ts +217 -0
  22. package/src/execution/__tests__/config.test.ts +110 -0
  23. package/src/execution/__tests__/crash-recovery.test.ts +311 -0
  24. package/src/execution/__tests__/execute-nesting.test.ts +359 -0
  25. package/src/execution/__tests__/execute-options-mapper.test.ts +138 -0
  26. package/src/execution/__tests__/execution-record.test.ts +959 -0
  27. package/src/execution/__tests__/finalized-marker.test.ts +82 -0
  28. package/src/execution/__tests__/format-schema-instruction.test.ts +135 -0
  29. package/src/execution/__tests__/format.test.ts +320 -0
  30. package/src/execution/__tests__/helpers/mock-extension-api.ts +30 -0
  31. package/src/execution/__tests__/list-component.test.ts +347 -0
  32. package/src/execution/__tests__/model-resolver.test.ts +356 -0
  33. package/src/execution/__tests__/output-collector.test.ts +61 -0
  34. package/src/execution/__tests__/path-encoding.test.ts +75 -0
  35. package/src/execution/__tests__/pi-invocation.test.ts +73 -0
  36. package/src/execution/__tests__/record-store.test.ts +545 -0
  37. package/src/execution/__tests__/run-spawn-edges.test.ts +439 -0
  38. package/src/execution/__tests__/run-spawn-integration.test.ts +897 -0
  39. package/src/execution/__tests__/sdk-contract.test.ts +272 -0
  40. package/src/execution/__tests__/session-context-resolver.test.ts +167 -0
  41. package/src/execution/__tests__/session-file-gc.test.ts +247 -0
  42. package/src/execution/__tests__/session-reconstructor.test.ts +359 -0
  43. package/src/execution/__tests__/session-runner-schema-env.test.ts +314 -0
  44. package/src/execution/__tests__/session-start-reaper.test.ts +227 -0
  45. package/src/execution/__tests__/spawn-args.test.ts +244 -0
  46. package/src/execution/__tests__/spawn-event-adapter.test.ts +167 -0
  47. package/src/execution/__tests__/subagent-service.test.ts +678 -0
  48. package/src/execution/__tests__/subprocess-agent-runner.test.ts +389 -0
  49. package/src/execution/__tests__/temp-prompt.test.ts +53 -0
  50. package/src/execution/__tests__/timeout-integration.test.ts +381 -0
  51. package/src/execution/__tests__/tombstone-store.test.ts +73 -0
  52. package/src/execution/__tests__/tool-action.test.ts +330 -0
  53. package/src/execution/__tests__/turn-limiter.test.ts +65 -0
  54. package/src/execution/__tests__/worktree-manager.test.ts +423 -0
  55. package/src/execution/__tests__/worktree-registry.test.ts +161 -0
  56. package/src/execution/agent-registry.ts +252 -0
  57. package/src/execution/agent-result-mapper.ts +84 -0
  58. package/src/execution/alive-store.ts +92 -0
  59. package/src/execution/best-effort.ts +30 -0
  60. package/src/execution/concurrency-pool.ts +84 -0
  61. package/src/execution/config.ts +73 -0
  62. package/src/execution/execute-options-mapper.ts +86 -0
  63. package/src/execution/execution-record.ts +778 -0
  64. package/src/execution/finalized-marker.ts +51 -0
  65. package/src/execution/model-config-service.ts +225 -0
  66. package/src/execution/model-resolver.ts +247 -0
  67. package/src/execution/notifier.ts +168 -0
  68. package/src/execution/output-collector.ts +88 -0
  69. package/src/execution/path-encoding.ts +34 -0
  70. package/src/execution/pi-invocation.ts +70 -0
  71. package/src/execution/record-store.ts +350 -0
  72. package/src/execution/session-context-resolver.ts +64 -0
  73. package/src/execution/session-file-gc.ts +98 -0
  74. package/src/execution/session-reconstructor.ts +450 -0
  75. package/src/execution/session-runner.ts +725 -0
  76. package/src/execution/spawn-event-adapter.ts +150 -0
  77. package/src/execution/subagent-service.ts +973 -0
  78. package/src/execution/subprocess-agent-runner.ts +108 -0
  79. package/src/execution/temp-prompt.ts +57 -0
  80. package/src/execution/tombstone-store.ts +72 -0
  81. package/src/execution/turn-limiter.ts +88 -0
  82. package/src/execution/types.ts +634 -0
  83. package/src/execution/worktree-manager.ts +285 -0
  84. package/src/execution/worktree-registry.ts +144 -0
  85. package/src/index.ts +454 -0
  86. package/src/interface/bg-notify-render.ts +286 -0
  87. package/src/interface/commands.ts +157 -0
  88. package/src/interface/format.ts +501 -0
  89. package/src/interface/gui-adapter.ts +136 -0
  90. package/src/interface/helpers.ts +110 -0
  91. package/src/interface/list-component.ts +643 -0
  92. package/src/interface/list-shared.ts +84 -0
  93. package/src/interface/list-view.ts +373 -0
  94. package/src/interface/reentry-guard.ts +30 -0
  95. package/src/interface/subagent-actions.ts +294 -0
  96. package/src/interface/subagent-tool.ts +294 -0
  97. package/src/interface/subagents.ts +30 -0
  98. package/src/interface/tool-render.ts +333 -0
  99. package/src/interface/tool-workflow-script.ts +351 -0
  100. package/src/interface/tool-workflow.ts +485 -0
  101. package/src/interface/views/WorkflowsView.ts +944 -0
  102. package/src/interface/views/detail-content.ts +298 -0
  103. package/src/interface/views/format.ts +320 -0
  104. package/src/orchestration/__tests__/concurrency-gate.test.ts +125 -0
  105. package/src/orchestration/__tests__/config-loader.test.ts +381 -0
  106. package/src/orchestration/__tests__/error-recovery-handlers.test.ts +332 -0
  107. package/src/orchestration/__tests__/error-recovery-workflow-call.test.ts +166 -0
  108. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +248 -0
  109. package/src/orchestration/__tests__/lifecycle.test.ts +385 -0
  110. package/src/orchestration/__tests__/script-lint.test.ts +347 -0
  111. package/src/orchestration/__tests__/worker-script-builder.test.ts +42 -0
  112. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +319 -0
  113. package/src/orchestration/agent-opts-resolver.ts +128 -0
  114. package/src/orchestration/concurrency-gate.ts +69 -0
  115. package/src/orchestration/config-loader.ts +313 -0
  116. package/src/orchestration/error-recovery.ts +578 -0
  117. package/src/orchestration/execute-agent-call.ts +174 -0
  118. package/src/orchestration/jsonl-run-store.ts +292 -0
  119. package/src/orchestration/launcher.ts +368 -0
  120. package/src/orchestration/lifecycle.ts +373 -0
  121. package/src/orchestration/models/__tests__/budget.test.ts +367 -0
  122. package/src/orchestration/models/agent-call.ts +76 -0
  123. package/src/orchestration/models/budget.ts +148 -0
  124. package/src/orchestration/models/ports.ts +165 -0
  125. package/src/orchestration/models/run-runtime.ts +91 -0
  126. package/src/orchestration/models/run-spec.ts +54 -0
  127. package/src/orchestration/models/run-state.ts +44 -0
  128. package/src/orchestration/models/trace.ts +102 -0
  129. package/src/orchestration/models/types.ts +242 -0
  130. package/src/orchestration/models/workflow-run.ts +275 -0
  131. package/src/orchestration/models/workflow-script-registry.ts +32 -0
  132. package/src/orchestration/models/workflow-script.ts +90 -0
  133. package/src/orchestration/node-ops.ts +192 -0
  134. package/src/orchestration/script-lint.ts +387 -0
  135. package/src/orchestration/skill-discovery.ts +60 -0
  136. package/src/orchestration/worker-handle.ts +115 -0
  137. package/src/orchestration/worker-host.ts +93 -0
  138. package/src/orchestration/worker-script-builder.ts +281 -0
  139. package/src/orchestration/workflow-files.ts +85 -0
  140. package/src/orchestration/workflow-script-registry-impl.ts +128 -0
  141. package/src/shared/__tests__/resource-discovery.test.ts +226 -0
  142. package/src/shared/agent-event.ts +13 -0
  143. package/src/shared/resource-discovery.ts +535 -0
@@ -0,0 +1,634 @@
1
+ // src/types.ts
2
+ //
3
+ // 跨层共享的核心类型契约。Core/Runtime/TUI 三层均可 import 本文件。
4
+ //
5
+ // 分层铁律:
6
+ // - Core 不 import Runtime/TUI(零 Pi 依赖,可单测)
7
+ // - Runtime 编排 Core,产出 Details/Record 给 TUI
8
+ // - TUI 只读 Record/Details 快照,永不持有可变引用
9
+
10
+ import type { ModelInfo, ModelRegistryLike } from "./model-resolver.ts";
11
+
12
+ // ============================================================
13
+ // 全局常量
14
+ // ============================================================
15
+
16
+ /**
17
+ * 未显式指定 agent 时的兌底名。
18
+ *
19
+ * 必须是真实存在、可被 agentRegistry 发现的 agent(用户 agentDir 内置的通用 agent)。
20
+ * Service 层(resolveIdentity)与 TUI 层(extractAgentName)共用此常量,保证
21
+ * 「调用时显示的名」与「实际加载的 agent.md」一致。
22
+ *
23
+ * [HISTORICAL] 旧实现两处各硬编码:service 用 "default"(虚构名),format 用
24
+ * "worker"(真实但不是兌底语义)。导致不传 agent 时,block 标题显示 worker,
25
+ * 但实际执行兌底逻辑不一致。统一为 general-purpose 后名实相符。
26
+ */
27
+ export const DEFAULT_AGENT_NAME = "general-purpose";
28
+
29
+ // ============================================================
30
+ // 执行状态机
31
+ // ============================================================
32
+
33
+ /** 唯一执行状态。所有路径共用。crashed 为进程崩溃终态(重建推断)。 */
34
+ export type ExecutionStatus = "running" | "done" | "failed" | "cancelled" | "crashed";
35
+
36
+ /** 执行模式。background = 调用方立即拿 handle 返回,子 agent 在 detached promise 里跑。 */
37
+ export type ExecutionMode = "background";
38
+
39
+ // ============================================================
40
+ // Agent 事件流(Core → Record 的唯一更新驱动)
41
+ // ============================================================
42
+
43
+ /**
44
+ * Pi session.subscribe 上报的事件。Runtime 把它喂给 updateFromEvent。
45
+ *
46
+ * 设计:AgentEvent 携带 updateFromEvent 收口进 record 所需的**全部数据**——
47
+ * tool_end 带 result(供 turn.toolCalls 存完整 ToolCall),无需翻译层旁路累积。
48
+ */
49
+ export type AgentEvent =
50
+ | { type: "tool_start"; toolName: string; args?: unknown }
51
+ | { type: "tool_end"; toolName: string; args?: unknown; result?: ToolCallResult; isError?: boolean }
52
+ | { type: "text_delta"; delta: string }
53
+ | { type: "thinking_delta"; delta: string }
54
+ | { type: "turn_end"; summary?: string }
55
+ | { type: "message_end"; usage?: AgentUsage; error?: string }
56
+ | { type: "compaction" }
57
+ | { type: "error"; message: string };
58
+
59
+ /** token 用量(message_end 时由 Core 累加进 record.totalTokens)。 */
60
+ export interface AgentUsage {
61
+ input: number;
62
+ output: number;
63
+ cacheRead: number;
64
+ cacheWrite: number;
65
+ /** 本 message 的成本(USD,来自 SDK usage.cost.total)。可选——无成本数据时缺省。 */
66
+ cost?: number;
67
+ }
68
+
69
+ export interface AgentUsageTotal extends AgentUsage {
70
+ /** 上述四项之和。投影时不再手工求和。 */
71
+ total: number;
72
+ /** 累计成本(USD,来自 SdkEvent.message.usage.cost.total 求和)。无成本数据时为 0。 */
73
+ cost: number;
74
+ }
75
+
76
+ /**
77
+ * eventLog 条目(getEventLog 派生产出的元素)。所有字段 readonly。
78
+ *
79
+ * text_output / thinking 类型已移除——它们是 100 字切片的碎片副产物,
80
+ * 现在完整内容收口在 record.turns[] 里,eventLog 只承载离散语义事件
81
+ * (tool 调用 / turn 边界 / error)。
82
+ */
83
+ export interface AgentEventLogEntry {
84
+ readonly type: "tool_start" | "tool_end" | "turn_end" | "error";
85
+ readonly label: string;
86
+ /** 事件发生的墙钟时间戳(Date.now(),ms)。由 getEventLog 从 turns[] 派生时记录。 */
87
+ readonly ts: number;
88
+ readonly status?: "running" | "done" | "failed";
89
+ }
90
+
91
+ /**
92
+ * [STEP3] displayItem:从 turns[] 派生的展示项(对齐 nicobailon getDisplayItems)。
93
+ *
94
+ * 与 eventLog 的区别:eventLog 承载离散语义事件(tool_start/tool_end/turn_end),
95
+ * displayItem 承载「可渲染单元」(toolCall 含完整 name+args 供 formatToolCall 格式化;
96
+ * text 含 assistant 正文)。renderResult compact 分支改用 displayItems 后,
97
+ * 行格式与 nicobailon 完全一致(→ formatToolCall)。
98
+ */
99
+ export interface DisplayItem {
100
+ readonly type: "toolCall" | "text";
101
+ /** toolCall:tool 名称(bash/read/edit...);text:无。 */
102
+ readonly name?: string;
103
+ /** toolCall:tool 原始 args(供 formatToolCall 提取路径/命令);text:无。 */
104
+ readonly args?: Record<string, unknown>;
105
+ /** toolCall:执行状态(running 时无✓/✗标记);text:正文文本。 */
106
+ readonly status?: "running" | "done" | "failed";
107
+ /** text:assistant 正文(compact 时取首行/截断)。 */
108
+ readonly text?: string;
109
+ }
110
+
111
+ // ============================================================
112
+ // Agent 结果(一次执行的 outcome)
113
+ // ============================================================
114
+
115
+ /**
116
+ * SDK AgentSessionEvent 的最小可用子集(duck-typed,避免强耦合 SDK 类型)。
117
+ * 由 session-runner 内部消费,驱动累积器和事件翻译。
118
+ */
119
+ export type SdkEvent = {
120
+ type: string;
121
+ toolCallId?: string;
122
+ toolName?: string;
123
+ args?: unknown;
124
+ result?: ToolCallResult;
125
+ isError?: boolean;
126
+ message?: {
127
+ usage?: AgentUsage & { cost?: { total: number } };
128
+ stopReason?: string;
129
+ errorMessage?: string;
130
+ };
131
+ assistantMessageEvent?: { type?: string; delta?: string };
132
+ reason?: string;
133
+ };
134
+
135
+ /** tool 调用结果(tool_execution_end 时累积,含 structured-output 的 details)。 */
136
+ export interface ToolCallResult {
137
+ content?: unknown[];
138
+ details?: unknown;
139
+ }
140
+
141
+ /**
142
+ * tool 调用(导出的纯净数据形状,不含内部状态)。
143
+ *
144
+ * tool_start 到达但 tool_end 未到时,调用为进行中;一旦 tool_end 到达,
145
+ * result/isError 填充完成。对外投影(AgentResult.toolCalls / getAllToolCalls)
146
+ * 一律返回此类型——**不泄漏 running/done/failed 内部状态机**。
147
+ *
148
+ * 进行中状态由 execution-record 内部的 `InternalToolCall`(= ToolCall + _status)承载,
149
+ * 只存在于 record.turns[].toolCalls,跨边界导出时由 getAllToolCalls strip _status。
150
+ */
151
+ export interface ToolCall {
152
+ toolName: string;
153
+ args?: unknown;
154
+ result?: ToolCallResult;
155
+ isError?: boolean;
156
+ }
157
+
158
+ /**
159
+ * 内部 ToolCall:在 ToolCall 基础上追加 _status 进行中状态标记与 startedTs 时间戳。
160
+ *
161
+ * running = tool_start 已收到但 tool_end 未到;
162
+ * done/failed = tool_end 已到。
163
+ *
164
+ * 仅存在于 ExecutionRecord.turns[].toolCalls(Core 内部可变状态)。
165
+ * 跨边界导出(getAllToolCalls → AgentResult.toolCalls / 持久化)由 getAllToolCalls
166
+ * 映射回 ToolCall(丢弃 _status / startedTs),保证导出形状清洁。
167
+ */
168
+ export interface InternalToolCall extends ToolCall {
169
+ _status: "running" | "done" | "failed";
170
+ /** tool_start 到达时的墙钟时间戳(Date.now(),ms)。getEventLog 派生 tool 条目 ts 用。 */
171
+ startedTs: number;
172
+ }
173
+
174
+ /**
175
+ * 一个 turn 的完整内容(ExecutionRecord.turns[] 的元素)。
176
+ *
177
+ * 收口设计:text/thinking 流式累积**完整内容**(非 100 字切片),
178
+ * toolCalls 存完整 ToolCall(含 result + _status 内部状态)。turn_end 到达后 closed=true,
179
+ * 下次 text/thinking/tool 时开新 turn。
180
+ *
181
+ * eventLog / currentActivity / result 均从 turns[] 派生,不再独立存储。
182
+ */
183
+ export interface Turn {
184
+ /** 本 turn assistant 正文(text_delta 流式累积,完整)。 */
185
+ text: string;
186
+ /** 本 turn 推理(thinking_delta 流式累积,完整)。 */
187
+ thinking: string;
188
+ /** 本 turn 工具调用(InternalToolCall:含完整 result + _status 进行中标记)。 */
189
+ toolCalls: InternalToolCall[];
190
+ /** 本 turn message_end 的 token 增量(聚合得 totalUsage)。 */
191
+ usageDelta?: AgentUsage;
192
+ /** turn_end 是否已到达。false=正在进行;true=已闭合,下次内容开新 turn。 */
193
+ closed: boolean;
194
+ /** turn_end 到达时的墙钟时间戳(Date.now(),ms)。getEventLog 派生 turn_end 条目 ts 用。 */
195
+ closedTs?: number;
196
+ }
197
+
198
+ /** 一次 session 执行的完整结果。collectResult 产出,写入 Record.outcome。 */
199
+ export interface AgentResult {
200
+ text: string;
201
+ turns: number;
202
+ durationMs: number;
203
+ success: boolean;
204
+ error?: string;
205
+ sessionId: string;
206
+ toolCalls: ToolCall[];
207
+ usage?: AgentUsageTotal;
208
+ /** /resume /fork 可恢复的 session 文件名(不含目录)。 */
209
+ sessionFile?: string;
210
+ /** schema 模式下,structured-output tool 的 result.details(已通过 schema 校验)。 */
211
+ parsedOutput?: unknown;
212
+ }
213
+
214
+ // ============================================================
215
+ // ExecutionRecord —— 唯一状态对象(Core 拥有,Runtime 引用)
216
+ // ============================================================
217
+
218
+ /**
219
+ * 所有执行路径的唯一状态源。
220
+ *
221
+ * 收口设计:一次执行的完整内容(text/thinking/toolCalls/usage)按 turn 收口在
222
+ * `turns: Turn[]` 里。eventLog / currentActivity / result 文本均从 turns[] 派生
223
+ * (getEventLog / getCurrentActivity / getFullText),不再独立存储切片或缓冲。
224
+ *
225
+ * 生命周期:createRecord() 创建 → updateFromEvent() 实时更新(累积进 turns)→
226
+ * completeRecord() 冻结 → archive 立即移出内存(读时从 session.jsonl 重建)。
227
+ *
228
+ * TUI 永远拿 RecordSnapshot(.slice() 快照),不直接持此可变对象。
229
+ */
230
+ /**
231
+ * worktree handle 值对象。仅 worktree:true 时持有——worktree 是独立维度,
232
+ * 需显式开启(且要求 fork:true),fork alone 不创建 worktree。
233
+ * Object.freeze 守卫保证不可变。
234
+ */
235
+ export interface WorktreeHandle {
236
+ /** checkout 目录(子 agent 工作目录,tmpdir 下)。 */
237
+ readonly path: string;
238
+ readonly branch: string;
239
+ readonly baseCommit: string;
240
+ /** 主仓库根目录(cleanup/scan 需要,不再靠路径反推)。 */
241
+ readonly mainCwd: string;
242
+ }
243
+
244
+ /** alive marker:子进程存活标记,用于心跳检测和 crash 推断。 */
245
+ export interface AliveMarker {
246
+ readonly pid: number;
247
+ readonly id: string;
248
+ readonly startedAt: number;
249
+ }
250
+
251
+ /** git diff patch 结果。 */
252
+ export interface PatchResult {
253
+ readonly patchFile: string;
254
+ readonly failed: boolean;
255
+ /** patch 是否实际写入 patchFile。true=diff 非空且写盘成功;false=空 diff 或写失败。
256
+ * 调用方据此回填 record.patchFile,避免悬空路径(`git apply` 不存在的文件)。 */
257
+ readonly written: boolean;
258
+ }
259
+
260
+ /** resolveSessionContext 纯函数的入参(#3 SessionContextResolver)。 */
261
+ export interface SessionResolveInput {
262
+ fork?: boolean;
263
+ cwd?: string;
264
+ mainCwd: string;
265
+ mainSessionFile?: string;
266
+ parentForkDepth?: number;
267
+ /** agent 配置目录(getSubagentSessionDir 需要)。 */
268
+ agentDir: string;
269
+ /** worktree checkout 路径(来自 WorktreeHandle.path,作为 effectiveCwd)。 */
270
+ worktreePath?: string;
271
+ }
272
+
273
+ /** resolveSessionContext 纯函数的返回值。 */
274
+ export interface ResolvedSessionContext {
275
+ readonly shouldFork: boolean;
276
+ readonly forkSource: string | undefined;
277
+ readonly effectiveCwd: string;
278
+ readonly sessionDir: string;
279
+ }
280
+
281
+ /** fork depth 超限错误。 */
282
+ export class ForkDepthExceededError extends Error {
283
+ constructor(message: string) {
284
+ super(message);
285
+ this.name = "ForkDepthExceededError";
286
+ }
287
+ }
288
+
289
+ /** worktree 有未提交变更错误。 */
290
+ export class DirtyWorktreeError extends Error {
291
+ constructor(message: string) {
292
+ super(message);
293
+ this.name = "DirtyWorktreeError";
294
+ }
295
+ }
296
+
297
+ export interface ExecutionRecord {
298
+ /** 唯一 ID(sync: "run-N",bg: "bg-N-xxx")。 */
299
+ readonly id: string;
300
+
301
+ // ── 身份(创建时确定,不可变)──
302
+ readonly agent: string;
303
+ readonly model: string;
304
+ readonly thinkingLevel: string | undefined;
305
+ readonly mode: ExecutionMode;
306
+ readonly task: string;
307
+ readonly startedAt: number;
308
+ /** 根 Pi session ID(session 隔离过滤用)。递归链上所有层 record 同值。 */
309
+ readonly rootSessionId: string | undefined;
310
+ /** 直接父 subagent record ID(层级树构建用)。顶层 record 为 undefined。 */
311
+ readonly parentRecordId: string | undefined;
312
+ /** subagent 递归深度。顶层(主 session 直接创建)=0,每层嵌套 +1。 */
313
+ readonly depth: number;
314
+
315
+ // ── 状态(实时更新)──
316
+ status: ExecutionStatus;
317
+ /** 完整执行内容,按 turn 组织。createRecord 初始化为 [空 turn]。 */
318
+ turns: Turn[];
319
+ /** turn 计数(= turns.filter(closed).length,冗余存储供投影直接读)。 */
320
+ turnCount: number;
321
+ totalTokens: number;
322
+ /** 运行期最近一次 error 事件的消息(getEventLog 派生 error 条目用)。 */
323
+ lastError: string | undefined;
324
+
325
+ // ── 完成 ──
326
+ endedAt: number | undefined;
327
+ result: string | undefined;
328
+ error: string | undefined;
329
+ /** 完整 AgentResult(含 usage/toolCalls,完成时填)。 */
330
+ agentResult: AgentResult | undefined;
331
+
332
+ /** session jsonl 文件名。session 创建成功后由 session-runner.run() 回填(窗口期内 undefined)。 */
333
+ sessionFile?: string;
334
+
335
+ /** [MF#3] fork+worktree 模式下子 agent 改动的 patch 文件路径(worktree 外,供调用方应用)。 */
336
+ patchFile?: string;
337
+
338
+ /** worktree 隔离时的 handle(仅 worktree:true 时存在;fork alone 无此字段)。 */
339
+ worktreeHandle?: WorktreeHandle;
340
+
341
+ // ── 控制(仅 background 持有)──
342
+ controller: AbortController | undefined;
343
+ }
344
+
345
+ // ============================================================
346
+ // Runtime → TUI 的投影契约
347
+ // ============================================================
348
+
349
+ /**
350
+ * Tool 返回的 details(内层扁平结构)。
351
+ * 由 project(record) 唯一产出——sync/bg 两路径字段一致。
352
+ * 含 mode + sessionFile(供外层 SubagentToolResult 分组 + spinner 判断)。
353
+ *
354
+ * 分层(spec FR-3):此为**内层**,不感知 action/外层分组。
355
+ * 外层 SubagentToolResult 由 adapter 包裹产出(加 action/subagentId/sessionFile + 分组)。
356
+ */
357
+ export interface SubagentToolDetails {
358
+ status: ExecutionStatus;
359
+ mode: ExecutionMode;
360
+ agent: string;
361
+ model: string;
362
+ thinkingLevel: string | undefined;
363
+ turns: number;
364
+ totalTokens: number;
365
+ elapsedSeconds: number;
366
+ eventLog: AgentEventLogEntry[];
367
+ /** [STEP3] 从 turns[] 派生的展示项(对齐 nicobailon getDisplayItems)。 */
368
+ displayItems: DisplayItem[];
369
+ result?: string;
370
+ error?: string;
371
+ /** running 时的当前活动行(tool/thinking/text 优先级)。 */
372
+ currentActivity?: { type: "tool" | "text" | "thinking"; label: string };
373
+ /** schema 模式下,structured-output tool 的 result.details(对齐 workflow agent-pool)。 */
374
+ parsedOutput?: unknown;
375
+ /** session jsonl 文件名(不含目录)。窗口期内可能 undefined(session 尚未创建成功)。 */
376
+ sessionFile?: string;
377
+ /** [MF#3] fork+worktree 模式下子 agent 改动的 patch 文件路径(worktree 外,供调用方应用)。 */
378
+ patchFile?: string;
379
+ }
380
+
381
+ // ============================================================
382
+ // Runtime 公共 API 的入参/出参
383
+ // ============================================================
384
+
385
+ /** Hub.execute 的入参(sync/bg 共用)。mode 由 Hub 内部判定,不暴露给调用方。 */
386
+ export interface ExecuteOptions {
387
+ task: string;
388
+ agent?: string;
389
+ model?: string;
390
+ thinkingLevel?: string;
391
+ skillPath?: string;
392
+ appendSystemPrompt?: string[];
393
+ schema?: Record<string, unknown>;
394
+ /** D-A6 bridge: workflow schemaEnv 经 ExecuteOptions 透传到 runSpawn childEnv。 */
395
+ schemaEnv?: string;
396
+ maxTurns?: number;
397
+ graceTurns?: number;
398
+ /** sync 模式来自 Pi tool 框架;background 模式 hub 忽略,自建 controller。 */
399
+ signal?: AbortSignal;
400
+ /** 主 agent 当前模型(模型解析第三层兼底)。execute 调用方从 ctx.model 传入。 */
401
+ ctxModel?: ModelInfo;
402
+ /** live 状态回流(对话流 block 实时刷新)。 */
403
+ onUpdate?: (details: SubagentToolDetails) => void;
404
+ /** background 完成回调(sync 不调)。 */
405
+ onComplete?: (record: RecordSnapshot) => void;
406
+ /** 是否继承父会话上下文(fork 模式,只继承上下文)。 */
407
+ fork?: boolean;
408
+ /** 文件系统隔离:true=创建新 git worktree(要求 fork:true),WorktreeHandle=复用外部已创建的;undefined=不隔离(parent cwd)。 */
409
+ worktree?: boolean | WorktreeHandle;
410
+ /** 覆盖执行 cwd(默认 mainCwd)。 */
411
+ cwd?: string;
412
+ // 注:fork 深度不从外部传入(曾暴露 parentForkDepth,改用 ALS 后 execute 内部从调用链派生,
413
+ // 公开字段成为死字段误导调用方,已移除)。深度限制检查见 session-runner.ts 内部 RunOptions.parentForkDepth
414
+ // (与历史残留的 types.ts RunOptions 同名不同 interface——后者已删除)。
415
+ }
416
+
417
+ /**
418
+ * execute 返回值。
419
+ * background: { mode:"background", subagentId, sessionFile, details } —— 立即返回。
420
+ * subagentId 供后续 cancel/list 用;sessionFile 窗口期可能 undefined。
421
+ */
422
+ export type ExecutionHandle = {
423
+ mode: "background";
424
+ subagentId: string;
425
+ sessionFile: string | undefined;
426
+ details: SubagentToolDetails;
427
+ };
428
+
429
+ // ============================================================
430
+ // tool action 出参(外层分组,adapter 产出)
431
+ // ============================================================
432
+
433
+ /** list 的 item 结构(8 字段)。 */
434
+ export interface SubagentListItem {
435
+ subagentId: string;
436
+ agent: string;
437
+ status: ExecutionStatus;
438
+ mode: ExecutionMode;
439
+ /** 运行秒数(running 态实时计算,终态 endedAt-startedAt)。 */
440
+ duration: number;
441
+ model: string;
442
+ totalTokens: number;
443
+ /** session jsonl 文件名(窗口期内可能 undefined)。 */
444
+ sessionFile?: string;
445
+ }
446
+
447
+ /** background 启动的内层响应(挂在 SubagentToolResult.bgResponse)。 */
448
+ export interface BgResponse {
449
+ status: "running";
450
+ mode: "background";
451
+ /** 启动提示文案("detached, will notify on completion")。 */
452
+ message: string;
453
+ }
454
+
455
+ /** list 的内层响应(挂在 SubagentToolResult.listResponse)。 */
456
+ export interface ListResponse {
457
+ /** items 中 status==="running" 的计数(受 limit 截断如实反映,非全局总数)。 */
458
+ running: number;
459
+ items: SubagentListItem[];
460
+ }
461
+
462
+ /** cancel 的内层响应(挂在 SubagentToolResult.cancelResponse)。 */
463
+ export interface CancelResponse {
464
+ cancelled: true;
465
+ }
466
+
467
+ /**
468
+ * Tool 外层出参(renderResult + LLM content JSON 同源)。
469
+ * adapter 唯一产出:领域对象(bg/list/cancel 三选一)+ action/subagentId/sessionFile。
470
+ *
471
+ * - background 启动 → bgResponse(subagentId 有值;sessionFile 窗口期可能 undefined)
472
+ * - list → listResponse(最外层 subagentId/sessionFile 为 null,sessionFile 在各 item 内)
473
+ * - cancel → cancelResponse(subagentId 有值;sessionFile 无意义,可为 null)
474
+ */
475
+ export type SubagentToolResult =
476
+ | { action: "start"; subagentId: string; sessionFile: string | null; bgResponse: BgResponse }
477
+ | { action: "list"; subagentId: null; sessionFile: null; listResponse: ListResponse }
478
+ | { action: "cancel"; subagentId: string; sessionFile: null; cancelResponse: CancelResponse };
479
+
480
+ // ============================================================
481
+ // TUI list 视图的合并 record(4 源 merge 后的形状)
482
+ // ============================================================
483
+
484
+ /** /subagents list 左列展示单元。来自内存(running) 或 session.jsonl 重建(终态)。 */
485
+ export interface SubagentRecord {
486
+ id: string;
487
+ agent: string;
488
+ /** 任务提示词(详情面板置顶展示)。磁盘/内存源均有。 */
489
+ task: string;
490
+ status: ExecutionStatus;
491
+ mode: ExecutionMode;
492
+ startedAt: number;
493
+ /** 根 Pi session ID(session 隔离过滤用)。递归链上所有层 record 同值。 */
494
+ rootSessionId: string | undefined;
495
+ /** 直接父 subagent record ID(层级树构建用)。顶层 record 为 undefined。 */
496
+ parentRecordId: string | undefined;
497
+ /** subagent 递归深度。顶层 =0,每层嵌套 +1。 */
498
+ depth: number;
499
+ endedAt: number | undefined;
500
+ turns: number;
501
+ totalTokens: number;
502
+ model: string;
503
+ thinkingLevel: string | undefined;
504
+ eventLog: AgentEventLogEntry[];
505
+ /** [STEP3] 从 turns[] 派生的展示项(对齐 nicobailon getDisplayItems)。 */
506
+ displayItems: DisplayItem[];
507
+ /** running 时的当前活动行(仅内存源;磁盘重建无此数据)。streaming 可观测性用。 */
508
+ currentActivity?: { type: "tool" | "text" | "thinking"; label: string };
509
+ result?: string;
510
+ error?: string;
511
+ sessionFile?: string;
512
+ /** [MF#3] fork+worktree 模式下子 agent 改动的 patch 文件路径(worktree 外,供调用方应用)。 */
513
+ patchFile?: string;
514
+ /** 外部 Pi 实例(进程隔离模式下由外部启动的子进程)。 */
515
+ externalInstance?: AliveMarker;
516
+ /** fork 模式下的 worktree handle。 */
517
+ worktreeHandle?: WorktreeHandle;
518
+ }
519
+
520
+ // ============================================================
521
+ // 配置(global + session)
522
+ // ============================================================
523
+
524
+ /**
525
+ * 全局配置(~/.pi/agent/subagents/config.json)。
526
+ *
527
+ * 模型解析已退化为「主 agent model 优先,仅 override 时查 registry」——
528
+ * 不再有 category/fallback/yolo 字段。config.json 只保留 maxConcurrent
529
+ * (pool 大小)。旧 config.json 中的 categories/fallback 等字段读取时忽略。
530
+ */
531
+ export interface SubagentsGlobalConfig {
532
+ version: number;
533
+ maxConcurrent: number;
534
+ }
535
+
536
+ // ============================================================
537
+ // 只读快照(TUI 消费,永不 mutate)
538
+ // ============================================================
539
+
540
+ /**
541
+ * Record 的只读视图。store.snapshot() 返回。
542
+ * TUI 拿到此类型,保证不会回写 Core 状态。
543
+ *
544
+ * 不含 eventLog——snapshot 的消费点(cancel 判 mode/status、hasRunning 判 mode、
545
+ * toNotifyRecord 取 result/error)均不读 eventLog。需要 eventLog 的场景用 project()
546
+ * 投影的 SubagentToolDetails。需要完整内容用 record.turns[](Core 内部)。
547
+ */
548
+ export interface RecordSnapshot {
549
+ readonly id: string;
550
+ readonly agent: string;
551
+ readonly model: string;
552
+ readonly thinkingLevel: string | undefined;
553
+ readonly mode: ExecutionMode;
554
+ readonly task: string;
555
+ readonly status: ExecutionStatus;
556
+ readonly turns: number;
557
+ readonly totalTokens: number;
558
+ readonly startedAt: number;
559
+ readonly endedAt: number | undefined;
560
+ readonly result: string | undefined;
561
+ readonly error: string | undefined;
562
+ readonly sessionFile: string | undefined;
563
+ }
564
+
565
+ // Re-export 用于 ExecuteOptions 的 agent/model 契约
566
+ // ============================================================
567
+ // SDK duck-typed 接口(测试可 mock,session-runner 消费)
568
+ // ============================================================
569
+
570
+ /** AgentSession 的最小可用接口(duck-typed,与 SDK AgentSession 结构兼容)。 */
571
+ export interface AgentSessionLike {
572
+ prompt(task: string, options?: unknown): Promise<void>;
573
+ steer(message: string): Promise<void>;
574
+ abort(): Promise<void>;
575
+ dispose(): void;
576
+ subscribe(fn: (event: unknown) => void): () => void;
577
+ sessionId: string;
578
+ readonly sessionManager: {
579
+ getSessionFile(): string | undefined;
580
+ getSessionId(): string;
581
+ /** 写 custom entry(subagent-identity 持久化用)。SDK SessionManager.appendCustomEntry 的 duck-type。 */
582
+ appendCustomEntry(customType: string, data?: unknown): string;
583
+ };
584
+ messages: ReadonlyArray<{
585
+ role: string;
586
+ content?: ReadonlyArray<{ type: string; text?: string }>;
587
+ }>;
588
+ getAllTools(): Array<{ name: string }>;
589
+ setActiveToolsByName(names: string[]): void;
590
+ }
591
+
592
+ /** DefaultResourceLoader 的最小可用接口(duck-typed)。 */
593
+ export interface ResourceLoaderLike {
594
+ reload(): Promise<void>;
595
+ }
596
+
597
+ /** createAgentSession 入参的类型化子集(对应 SDK CreateAgentSessionOptions)。 */
598
+ export interface CreateAgentSessionArgs {
599
+ model: unknown;
600
+ thinkingLevel?: string;
601
+ cwd: string;
602
+ resourceLoader: ResourceLoaderLike;
603
+ modelRegistry: ModelRegistryLike;
604
+ sessionManager: unknown;
605
+ }
606
+
607
+ /** DefaultResourceLoader 构造参数的类型化子集。 */
608
+ export interface ResourceLoaderOptions {
609
+ cwd: string;
610
+ agentDir: string;
611
+ appendSystemPrompt: string[];
612
+ additionalSkillPaths?: string[];
613
+ }
614
+
615
+ /** SessionManager 实例的最小接口(duck-typed,fork 路径消费 SDK 静态方法的返回值)。 */
616
+ export interface SessionManagerLike {
617
+ getLeafId(): string | null;
618
+ createBranchedSession(leafId: string): string | undefined;
619
+ getSessionFile(): string | undefined;
620
+ getSessionId(): string;
621
+ }
622
+
623
+ /** Pi SDK 动态 import 的形状(getSdk() 获取)。 */
624
+ export interface SdkLike {
625
+ DefaultResourceLoader: new (opts: ResourceLoaderOptions) => ResourceLoaderLike;
626
+ SessionManager: {
627
+ inMemory(cwd?: string): SessionManagerLike;
628
+ create(cwd: string, sessionDir?: string): SessionManagerLike;
629
+ open(sessionFile: string, sessionDir?: string, cwdOverride?: string): SessionManagerLike;
630
+ /** [MF#1] fork 静态方法:从源 session 文件 fork 到目标 cwd,返回 SessionManager。 */
631
+ forkFrom(sourcePath: string, targetCwd: string, sessionDir?: string): SessionManagerLike;
632
+ };
633
+ createAgentSession: (opts: CreateAgentSessionArgs) => Promise<{ session: AgentSessionLike }>;
634
+ }