@zhushanwen/pi-subagent-workflow 7.3.4 → 8.0.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 (166) hide show
  1. package/README.md +39 -12
  2. package/agents/analyst.md +61 -0
  3. package/agents/coder.md +70 -0
  4. package/agents/debugger.md +67 -0
  5. package/agents/doc-reviewer.md +3 -3
  6. package/agents/explorer.md +50 -18
  7. package/agents/general-purpose.md +19 -8
  8. package/agents/orchestrator.md +37 -32
  9. package/agents/planner.md +45 -11
  10. package/agents/researcher.md +53 -11
  11. package/agents/reviewer.md +74 -0
  12. package/package.json +1 -1
  13. package/skills/workflow-script-format/SKILL.md +1 -1
  14. package/src/execution/__tests__/__fixtures__/truncline.snapshot.json +1 -0
  15. package/src/execution/__tests__/agent-registry.test.ts +13 -11
  16. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +10 -4
  17. package/src/execution/__tests__/before-agent-start-injection.test.ts +132 -0
  18. package/src/execution/__tests__/bg-notify-render.test.ts +15 -15
  19. package/src/execution/__tests__/chatmode-first-round-closure-service.test.ts +365 -0
  20. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +190 -0
  21. package/src/execution/__tests__/chatmode-round-notify-real-chain.test.ts +215 -0
  22. package/src/execution/__tests__/conversation-wiring.test.ts +198 -0
  23. package/src/execution/__tests__/crash-recovery.test.ts +8 -2
  24. package/src/execution/__tests__/delivery-methods.test.ts +385 -0
  25. package/src/execution/__tests__/epipe-fallback.test.ts +241 -0
  26. package/src/execution/__tests__/execute-and-await-worktree.test.ts +49 -2
  27. package/src/execution/__tests__/execute-nesting.test.ts +20 -72
  28. package/src/execution/__tests__/execution-record.test.ts +199 -0
  29. package/src/execution/__tests__/finalize-record.test.ts +170 -14
  30. package/src/execution/__tests__/format.test.ts +131 -7
  31. package/src/execution/__tests__/gc-timer.test.ts +184 -0
  32. package/src/execution/__tests__/get-record-for-action-restart.test.ts +254 -0
  33. package/src/execution/__tests__/helpers/spawn-mock.ts +25 -7
  34. package/src/execution/__tests__/index-session-start-identity.test.ts +371 -0
  35. package/src/execution/__tests__/index-session-start.test.ts +257 -5
  36. package/src/execution/__tests__/lifecycle-manager-lock.test.ts +211 -0
  37. package/src/execution/__tests__/lifecycle-manager.test.ts +337 -0
  38. package/src/execution/__tests__/lifecycle-predicates.test.ts +116 -0
  39. package/src/execution/__tests__/list-component.test.ts +59 -5
  40. package/src/execution/__tests__/list-fields.test.ts +109 -0
  41. package/src/execution/__tests__/model-resolver.test.ts +38 -1
  42. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +287 -0
  43. package/src/execution/__tests__/nested-visibility.test.ts +325 -0
  44. package/src/execution/__tests__/notifier-flush.test.ts +209 -7
  45. package/src/execution/__tests__/one-shot-upgrade.test.ts +205 -0
  46. package/src/execution/__tests__/parent-child-matrix.test.ts +336 -0
  47. package/src/execution/__tests__/record-store.test.ts +158 -52
  48. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +11 -12
  49. package/src/execution/__tests__/recursive-visibility-env.test.ts +18 -20
  50. package/src/execution/__tests__/resource-policy.test.ts +109 -0
  51. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +267 -0
  52. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +253 -0
  53. package/src/execution/__tests__/run-spawn-edges.test.ts +18 -25
  54. package/src/execution/__tests__/run-spawn-integration.test.ts +29 -25
  55. package/src/execution/__tests__/run-spawn-resume.test.ts +322 -0
  56. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +14 -11
  57. package/src/execution/__tests__/session-pending.test.ts +61 -2
  58. package/src/execution/__tests__/session-reconstructor.test.ts +4 -4
  59. package/src/execution/__tests__/session-runner-epipe.test.ts +178 -0
  60. package/src/execution/__tests__/session-runner-schema-env.test.ts +15 -21
  61. package/src/execution/__tests__/session-start-reaper.test.ts +10 -8
  62. package/src/execution/__tests__/spawn-args.test.ts +127 -49
  63. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +1 -0
  64. package/src/execution/__tests__/spawned-children.test.ts +92 -0
  65. package/src/execution/__tests__/status-refactor.test.ts +345 -0
  66. package/src/execution/__tests__/stdin-writer.test.ts +97 -0
  67. package/src/execution/__tests__/subagent-service-message-close.test.ts +598 -0
  68. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +180 -0
  69. package/src/execution/__tests__/subagent-service.test.ts +49 -11
  70. package/src/execution/__tests__/timeout-integration.test.ts +27 -13
  71. package/src/execution/__tests__/tool-action.test.ts +12 -10
  72. package/src/execution/__tests__/truncline-snapshot.test.ts +81 -0
  73. package/src/execution/__tests__/turn-limiter-semantics.test.ts +194 -0
  74. package/src/execution/__tests__/worktree-manager.test.ts +292 -89
  75. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +13 -12
  76. package/src/execution/argv-mirror.ts +21 -2
  77. package/src/execution/execution-record.ts +126 -9
  78. package/src/execution/finalize-record.ts +90 -13
  79. package/src/execution/host-mode.ts +1 -1
  80. package/src/execution/lifecycle-manager.ts +484 -0
  81. package/src/execution/lifecycle-predicates.ts +65 -0
  82. package/src/execution/manifest-store.ts +61 -16
  83. package/src/execution/model-resolver.ts +26 -5
  84. package/src/execution/notifier.ts +69 -12
  85. package/src/execution/pi-invocation.ts +21 -1
  86. package/src/execution/record-store.ts +554 -107
  87. package/src/execution/session-pending.ts +116 -45
  88. package/src/execution/session-reconstructor.ts +224 -7
  89. package/src/execution/session-runner.ts +290 -75
  90. package/src/execution/sessions-index.ts +304 -0
  91. package/src/execution/stdin-writer.ts +93 -7
  92. package/src/execution/stream-sink.ts +20 -3
  93. package/src/execution/subagent-service.ts +867 -138
  94. package/src/execution/turn-limiter.ts +14 -0
  95. package/src/execution/types.ts +204 -22
  96. package/src/execution/worktree-manager.ts +128 -49
  97. package/src/execution/worktree-registry.ts +13 -2
  98. package/src/index.ts +277 -19
  99. package/src/injectors/subagent-list-injector.ts +26 -8
  100. package/src/injectors/workflow-list-injector.ts +25 -8
  101. package/src/interface/__tests__/subagent-tool-prompt.test.ts +18 -5
  102. package/src/interface/__tests__/tool-render.test.ts +15 -13
  103. package/src/interface/bg-notify-render.ts +32 -8
  104. package/src/interface/command-actions.ts +26 -7
  105. package/src/interface/commands.ts +21 -22
  106. package/src/interface/format.ts +44 -17
  107. package/src/interface/gui-mappers.ts +6 -8
  108. package/src/interface/helpers.ts +170 -10
  109. package/src/interface/list-component.ts +53 -14
  110. package/src/interface/subagent-actions.ts +235 -17
  111. package/src/interface/subagent-tool.ts +82 -17
  112. package/src/interface/subagents.ts +2 -1
  113. package/src/interface/tool-render.ts +11 -24
  114. package/src/interface/tool-workflow.ts +20 -35
  115. package/src/interface/views/WorkflowsView.ts +89 -32
  116. package/src/interface/views/__tests__/WorkflowsView-signature.test.ts +264 -0
  117. package/src/interface/views/__tests__/detail-content-session-file.test.ts +1 -1
  118. package/src/interface/views/format.ts +3 -3
  119. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +325 -0
  120. package/src/orchestration/__tests__/args-validator.test.ts +1 -1
  121. package/src/orchestration/__tests__/config-loader.test.ts +38 -0
  122. package/src/orchestration/__tests__/error-recovery-handlers.test.ts +394 -4
  123. package/src/orchestration/__tests__/error-recovery-workflow-call.test.ts +4 -4
  124. package/src/orchestration/__tests__/execute-agent-call.test.ts +95 -0
  125. package/src/orchestration/__tests__/jsonl-run-store-session-file.test.ts +657 -18
  126. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +0 -2
  127. package/src/orchestration/__tests__/lifecycle.test.ts +332 -149
  128. package/src/orchestration/__tests__/skill-discovery.test.ts +157 -0
  129. package/src/orchestration/__tests__/test-mocks.ts +191 -0
  130. package/src/orchestration/__tests__/worker-script-template-snapshot.test.ts +98 -0
  131. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +0 -2
  132. package/src/orchestration/__tests__/workflow-script-lint-memo.test.ts +110 -0
  133. package/src/orchestration/__tests__/workflows-e2e.test.ts +1 -1
  134. package/src/orchestration/agent-opts-resolver.ts +4 -1
  135. package/src/orchestration/args-validator.ts +2 -2
  136. package/src/orchestration/config-loader.ts +30 -1
  137. package/src/orchestration/error-recovery.ts +133 -29
  138. package/src/orchestration/execute-agent-call.ts +31 -7
  139. package/src/orchestration/jsonl-run-store.ts +287 -40
  140. package/src/orchestration/launcher.ts +7 -1
  141. package/src/orchestration/lifecycle.ts +135 -132
  142. package/src/orchestration/models/__tests__/trace.test.ts +408 -0
  143. package/src/orchestration/models/budget.ts +1 -1
  144. package/src/orchestration/models/run-runtime.ts +15 -17
  145. package/src/orchestration/models/run-spec.ts +2 -2
  146. package/src/orchestration/models/run-state.ts +3 -3
  147. package/src/orchestration/models/trace.ts +95 -15
  148. package/src/orchestration/models/types.ts +8 -9
  149. package/src/orchestration/models/workflow-run.ts +50 -71
  150. package/src/orchestration/models/workflow-script.ts +32 -1
  151. package/src/orchestration/skill-discovery.ts +30 -0
  152. package/src/orchestration/worker-handle.ts +1 -1
  153. package/src/orchestration/worker-host.ts +1 -1
  154. package/src/orchestration/worker-script-builder.ts +29 -10
  155. package/src/shared/__tests__/agent-ref.test.ts +34 -0
  156. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +280 -0
  157. package/src/shared/__tests__/resource-discovery.test.ts +55 -0
  158. package/src/shared/__tests__/schema-jsonify.test.ts +81 -0
  159. package/src/shared/agent-ref.ts +16 -0
  160. package/src/shared/resource-discovery.ts +147 -58
  161. package/src/shared/schema-jsonify.ts +53 -0
  162. package/workflows/README.md +4 -4
  163. package/agents/code-reviewer.md +0 -47
  164. package/agents/context-builder.md +0 -21
  165. package/agents/oracle.md +0 -34
  166. package/agents/worker.md +0 -20
@@ -82,11 +82,13 @@ const MODEL_LIST_LIMIT = 20;
82
82
  // ║ 3. ctxModel (主 agent 当前模型,直接透传) ║
83
83
  // ║ ║
84
84
  // ║ 1/2 级查 registry + auth 校验;3 级无需 lookup(主 agent 在用 ║
85
- // ║ 说明 auth OK)。thinkingLevel 无指定时 undefined(model 默认) ║
85
+ // ║ 说明 auth OK)。thinkingLevel 无显式指定时兜底「模型最高可用档」║
86
+ // ║ (maxThinkingForModel,含 "max" 时用 max)——不落回 pi 默认 ║
87
+ // ║ medium,subagent 任务需要最大推理深度。 ║
86
88
  // ║ ║
87
89
  // ║ 显式指定但 lookup/auth 失败 → 抛错(不静默降级到主 agent, ║
88
90
  // ║ 因为用户明确要求了某个 model,降级会造成「以为用了 X 实际用 Y」║
89
- // ╚═══════════════════════════════════════════════════════════════╝
91
+ // ╚════════════════════════════════════════════════════════════════╝
90
92
  *
91
93
  * @param agentConfig agent .md 解析结果(查 model override + thinkingLevel)
92
94
  * @param modelRegistry registry(仅 override 路径用)
@@ -120,11 +122,12 @@ export function resolveModel(
120
122
  );
121
123
  }
122
124
 
123
- // 3. 主 agent model(直接透传,thinkingLevel override 或 undefined)
125
+ // 3. 主 agent model(直接透传)。无显式 thinking 兜底最高可用档。
124
126
  if (ctxModel) {
125
127
  return {
126
128
  model: ctxModel,
127
- thinkingLevel: paramOverride?.thinkingLevel ?? agentConfig?.thinkingLevel,
129
+ thinkingLevel: paramOverride?.thinkingLevel ?? agentConfig?.thinkingLevel
130
+ ?? maxThinkingForModel(ctxModel),
128
131
  };
129
132
  }
130
133
 
@@ -166,10 +169,28 @@ function lookupAndResolve(
166
169
  }
167
170
  return {
168
171
  model,
169
- thinkingLevel: resolveThinkingLevel(model, requestedThinking),
172
+ // 无显式请求时兜底「模型最高可用档」(不落 pi 默认 medium)。
173
+ thinkingLevel: resolveThinkingLevel(model, requestedThinking ?? maxThinkingForModel(model)),
170
174
  };
171
175
  }
172
176
 
177
+ /**
178
+ * subagent 默认 thinking:模型最高可用档。
179
+ *
180
+ * - thinkingLevelMap 配置了级别 → map 中最高档(模型显式配置 "max" 时即 max)
181
+ * - reasoning 但无 map → "xhigh"(pi 合法最高档;"max" 非 pi 合法值——
182
+ * spawn 侧 model:"max" 后缀会被 pi 判为非法 thinking 后缀导致 model 解析失败,
183
+ * 故无 map 时不能直接用 "max")
184
+ * - 非 reasoning 模型 → undefined(不支持 thinking)
185
+ */
186
+ function maxThinkingForModel(
187
+ model: { reasoning: boolean; thinkingLevelMap?: Record<string, unknown> },
188
+ ): string | undefined {
189
+ const levels = availableThinkingLevels(model);
190
+ if (levels.length > 0) return levels[levels.length - 1];
191
+ return model.reasoning ? "xhigh" : undefined;
192
+ }
193
+
173
194
  /**
174
195
  * 解析 "provider/modelId" 并查 registry。
175
196
  *
@@ -8,10 +8,20 @@
8
8
  // - 通过 pi.sendMessage({ deliverAs:"followUp", triggerTurn:true }) 注入——
9
9
  // 当前 turn 结束后唤醒父 agent 处理结果(followUp 不打断 streaming、不锁滚动)
10
10
 
11
- /** 一条待发送的完成通知记录。 */
11
+ /**
12
+ * 一条待发送的完成通知记录。
13
+ * SP-1: done/failed/crashed 合并为 closed + closedReason L2 子枚举。
14
+ */
12
15
  export interface BgNotifyRecord {
13
16
  id: string;
14
- status: "done" | "failed" | "cancelled";
17
+ /**
18
+ * 完成状态。v4 B-1 两态收敛:closed(终态,含 cancelled,closedReason 表达 L2 原因)
19
+ * 或 running(对话模式轮次完成,旧 idle 折入 running,携带本轮结果送回主 agent)。
20
+ * toNotifyRecord 守卫放行后经此联合穷尽。
21
+ */
22
+ status: "running" | "closed";
23
+ /** L2 关闭原因子枚举(仅 status="closed" 时有意义)。供通知文案按需展示。 */
24
+ closedReason?: import("./types.ts").ClosedReason;
15
25
  agent: string;
16
26
  /** 执行所用 model(RecordSnapshot.model),用于完成通知显示。 */
17
27
  model?: string;
@@ -19,10 +29,25 @@ export interface BgNotifyRecord {
19
29
  error?: string;
20
30
  startedAt: number;
21
31
  endedAt: number | undefined;
32
+ /**
33
+ * 对话轮次计数(仅 idle 有意义)。dedup key 按 `id:round` 去重——对话模式每轮 round
34
+ * 不同,60s 内多轮不被吞;非 chatMode round 恒定(0/undefined),key 同旧 id 行为不变。
35
+ */
36
+ round?: number;
22
37
  /** [MF#1] worktree 模式下子 agent 改动的 patch 路径(worktree 外,cleanup 后留存)。
23
38
  * done 时通知文本显式提示 `git apply`,否则 background 子 agent 在隔离 worktree 的改动
24
39
  * 会静默丢失——父 LLM 不知 patch 路径,无法应用。 */
25
40
  patchFile?: string;
41
+ /** [wave2] 子 agent session 文件路径(增量语义的全文恢复通道)。
42
+ * 仅 chatMode 透传(toNotifyRecord 条件透传)——轮次/完成通知末尾追加
43
+ * "Full transcript: <path>" 指针行,父 LLM 可按需读全文;one-shot 不透传,
44
+ * 通知输出逐字节不变。缺失时 buildLlmContent 省略整行。 */
45
+ sessionFile?: string;
46
+ /** [C-2] close 终态通知的轮次统计(文案 "completed after N rounds." 用)。
47
+ * 仅 chatMode close 语义(notifyClosed)构造时携带——此时 dedup 身份 round 已被
48
+ * 置 undefined(与轮次通知的 id:round key 区分,终态不被吞),轮数改由本字段进
49
+ * 文案。one-shot 完成通知不设置,文案保持 "completed. Result:" 逐字节(G4)。 */
50
+ totalRounds?: number;
26
51
  }
27
52
 
28
53
  /** notifier 依赖的 pi 最小接口(解耦,便于测试)。 */
@@ -108,9 +133,12 @@ export class BgNotifier {
108
133
  if (now - ts >= DEDUP_TTL_MS) this.dedup.delete(id);
109
134
  }
110
135
  }
111
- const lastSeen = this.dedup.get(record.id);
136
+ // dedup key:idle(对话模式每轮完成)按 id:round 去重——不同轮次不互相掩蔽;
137
+ // 非 idle(closed/cancelled)round 恒定 undefined,key 同旧行为不变。
138
+ const dedupKey = record.round != null ? `${record.id}:${record.round}` : record.id;
139
+ const lastSeen = this.dedup.get(dedupKey);
112
140
  if (lastSeen !== undefined && now - lastSeen < DEDUP_TTL_MS) return;
113
- this.dedup.set(record.id, now);
141
+ this.dedup.set(dedupKey, now);
114
142
 
115
143
  this.pending.push(record);
116
144
 
@@ -209,18 +237,47 @@ export class BgNotifier {
209
237
  private buildLlmContent(record: BgNotifyRecord): string {
210
238
  const agent = record.agent;
211
239
  const id = record.id;
240
+ // [wave2 指针行] 增量语义下轮次通知只携带本轮增量,异步 flush 窗口丢失时不可重发
241
+ //(见 subagent-service.ts onRoundSettled 注释),恢复通道是 session 文件全文。
242
+ // 仅 chatMode 透传 sessionFile,one-shot 通知不含该行;cancelled/gc-failed 不追加
243
+ //(无成功结果可读,指针无意义);缺失时省略整行(追加空串 = 输出逐字节不变)。
244
+ const transcriptPointer = record.sessionFile
245
+ ? `\n\nFull transcript: ${record.sessionFile}`
246
+ : "";
212
247
  switch (record.status) {
213
- case "done": {
214
- const base = `Subagent "${agent}" (${id}) completed. Result:\n${record.result ?? "(empty)"}`;
248
+ case "closed": {
249
+ // v4 B-1: closed 统一终态(含 cancelled)。按 closedReason 派生通知文案。
250
+ const reason = record.closedReason ?? "gc";
251
+ if (reason === "cancelled") {
252
+ return `Subagent "${agent}" (${id}) cancelled.`;
253
+ }
254
+ // 失败场景(gc + 有 error):展示错误。判定必须先于 patchFile——失败轮也会写
255
+ // patchFile(doFinalizeRecord Step 0 对 worktreeHandle 无条件 collectPatch),若
256
+ // patch 分支在前,gc 失败 + worktree 并存时 LLM 被告知 completed。顺序与三处
257
+ // 同构契约的另外两处一致(shared/subagent.ts deriveClosedDisplay、
258
+ // bg-notify-render.ts renderRecordLines:cancelled → gc+error → patch/result)。
259
+ if (record.error && reason === "gc") {
260
+ return `Subagent "${agent}" (${id}) failed: ${record.error}`;
261
+ }
262
+ // 成功完成(user-close)或通用结束(gc/parent-shutdown 等):展示结果。
263
+ // [C-2] chatMode close 终态通知附轮次统计(设计 D2 路径①"completed after N rounds")。
264
+ // totalRounds 仅 close 语义携带(notifyClosed);one-shot 完成通知不设置(round 无轮次
265
+ // 语义),文案保持 "completed. Result:" 逐字节(G4 硬约束,one-shot 字节锁测试锚定)。
266
+ const roundsSuffix =
267
+ record.totalRounds != null && record.totalRounds > 0
268
+ ? ` after ${record.totalRounds} round${record.totalRounds === 1 ? "" : "s"}`
269
+ : "";
270
+ const base = `Subagent "${agent}" (${id}) completed${roundsSuffix}. Result:\n${record.result ?? "(empty)"}`;
215
271
  if (record.patchFile) {
216
- return `${base}\n\nThis subagent ran in an isolated worktree; its file changes were captured as a patch:\n ${record.patchFile}\nTo bring these changes into the current repo, run: \`git apply ${record.patchFile}\``;
272
+ // [wave2 review] return 拆行:模板串内不可直接换行(会改变输出内容),提取 patchHint 中转变量
273
+ const patchHint = `\n\nThis subagent ran in an isolated worktree; its file changes were captured as a patch:\n ${record.patchFile}\nTo bring these changes into the current repo, run: \`git apply ${record.patchFile}\``;
274
+ return `${base}${patchHint}${transcriptPointer}`;
217
275
  }
218
- return base;
276
+ return `${base}${transcriptPointer}`;
219
277
  }
220
- case "failed":
221
- return `Subagent "${agent}" (${id}) failed: ${record.error ?? "(unknown error)"}`;
222
- case "cancelled":
223
- return `Subagent "${agent}" (${id}) cancelled.`;
278
+ case "running":
279
+ // v4 B-1: 对话模式轮次完成(旧 idle 折入 running):携带本轮结果送回主 agent,等待下一轮 message。
280
+ return `Subagent "${agent}" (${id}) finished a round. Reply:\n${record.result ?? "(empty)"}${transcriptPointer}`;
224
281
  }
225
282
  }
226
283
 
@@ -51,12 +51,32 @@ function isGenericRuntime(execPath: string): boolean {
51
51
  * 2. execPath 非通用运行时(pi standalone binary)→ <execPath> <userArgs>
52
52
  * 3. 通用运行时但无可用脚本路径 → "pi" <userArgs>(依赖 PATH 中可找到 pi)
53
53
  */
54
+ // [perf] 决策链只依赖 process.argv[1] / process.execPath(进程内恒定),其中唯一的
55
+ // 磁盘探测(existsSync)结果 memo 一次——每次 spawn 免重复 syscall。memo 键含
56
+ // argv[1] 值:测试 mock 切换 argv[1] 模拟不同运行时时自动失效重算。
57
+ let scriptExistsCache: { script: string | undefined; exists: boolean } | undefined;
58
+
59
+ /** process.argv[1] 是真实磁盘脚本(非 bun 虚拟路径)且存在。 */
60
+ function currentScriptExists(): boolean {
61
+ const currentScript = process.argv[1];
62
+ if (scriptExistsCache === undefined || scriptExistsCache.script !== currentScript) {
63
+ scriptExistsCache = {
64
+ script: currentScript,
65
+ exists:
66
+ currentScript !== undefined &&
67
+ !currentScript.startsWith(BUN_VIRTUAL_PREFIX) &&
68
+ fs.existsSync(currentScript),
69
+ };
70
+ }
71
+ return scriptExistsCache.exists;
72
+ }
73
+
54
74
  export function getPiInvocation(userArgs: string[]): PiInvocation {
55
75
  const currentScript = process.argv[1];
56
76
  const isBunVirtualScript = currentScript?.startsWith(BUN_VIRTUAL_PREFIX);
57
77
 
58
78
  // 分支 1:有真实脚本路径 → 复现启动方式(node <pi-script> <args>)
59
- if (currentScript && !isBunVirtualScript && fs.existsSync(currentScript)) {
79
+ if (currentScript && !isBunVirtualScript && currentScriptExists()) {
60
80
  return { command: process.execPath, args: [currentScript, ...userArgs] };
61
81
  }
62
82