@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.
- package/README.md +39 -12
- package/agents/analyst.md +61 -0
- package/agents/coder.md +70 -0
- package/agents/debugger.md +67 -0
- package/agents/doc-reviewer.md +3 -3
- package/agents/explorer.md +50 -18
- package/agents/general-purpose.md +19 -8
- package/agents/orchestrator.md +37 -32
- package/agents/planner.md +45 -11
- package/agents/researcher.md +53 -11
- package/agents/reviewer.md +74 -0
- package/package.json +1 -1
- package/skills/workflow-script-format/SKILL.md +1 -1
- package/src/execution/__tests__/__fixtures__/truncline.snapshot.json +1 -0
- package/src/execution/__tests__/agent-registry.test.ts +13 -11
- package/src/execution/__tests__/ask-user-transit-e2e.test.ts +10 -4
- package/src/execution/__tests__/before-agent-start-injection.test.ts +132 -0
- package/src/execution/__tests__/bg-notify-render.test.ts +15 -15
- package/src/execution/__tests__/chatmode-first-round-closure-service.test.ts +365 -0
- package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +190 -0
- package/src/execution/__tests__/chatmode-round-notify-real-chain.test.ts +215 -0
- package/src/execution/__tests__/conversation-wiring.test.ts +198 -0
- package/src/execution/__tests__/crash-recovery.test.ts +8 -2
- package/src/execution/__tests__/delivery-methods.test.ts +385 -0
- package/src/execution/__tests__/epipe-fallback.test.ts +241 -0
- package/src/execution/__tests__/execute-and-await-worktree.test.ts +49 -2
- package/src/execution/__tests__/execute-nesting.test.ts +20 -72
- package/src/execution/__tests__/execution-record.test.ts +199 -0
- package/src/execution/__tests__/finalize-record.test.ts +170 -14
- package/src/execution/__tests__/format.test.ts +131 -7
- package/src/execution/__tests__/gc-timer.test.ts +184 -0
- package/src/execution/__tests__/get-record-for-action-restart.test.ts +254 -0
- package/src/execution/__tests__/helpers/spawn-mock.ts +25 -7
- package/src/execution/__tests__/index-session-start-identity.test.ts +371 -0
- package/src/execution/__tests__/index-session-start.test.ts +257 -5
- package/src/execution/__tests__/lifecycle-manager-lock.test.ts +211 -0
- package/src/execution/__tests__/lifecycle-manager.test.ts +337 -0
- package/src/execution/__tests__/lifecycle-predicates.test.ts +116 -0
- package/src/execution/__tests__/list-component.test.ts +59 -5
- package/src/execution/__tests__/list-fields.test.ts +109 -0
- package/src/execution/__tests__/model-resolver.test.ts +38 -1
- package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +287 -0
- package/src/execution/__tests__/nested-visibility.test.ts +325 -0
- package/src/execution/__tests__/notifier-flush.test.ts +209 -7
- package/src/execution/__tests__/one-shot-upgrade.test.ts +205 -0
- package/src/execution/__tests__/parent-child-matrix.test.ts +336 -0
- package/src/execution/__tests__/record-store.test.ts +158 -52
- package/src/execution/__tests__/recursive-visibility-baseline.test.ts +11 -12
- package/src/execution/__tests__/recursive-visibility-env.test.ts +18 -20
- package/src/execution/__tests__/resource-policy.test.ts +109 -0
- package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +267 -0
- package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +253 -0
- package/src/execution/__tests__/run-spawn-edges.test.ts +18 -25
- package/src/execution/__tests__/run-spawn-integration.test.ts +29 -25
- package/src/execution/__tests__/run-spawn-resume.test.ts +322 -0
- package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +14 -11
- package/src/execution/__tests__/session-pending.test.ts +61 -2
- package/src/execution/__tests__/session-reconstructor.test.ts +4 -4
- package/src/execution/__tests__/session-runner-epipe.test.ts +178 -0
- package/src/execution/__tests__/session-runner-schema-env.test.ts +15 -21
- package/src/execution/__tests__/session-start-reaper.test.ts +10 -8
- package/src/execution/__tests__/spawn-args.test.ts +127 -49
- package/src/execution/__tests__/spawn-worktree-guidance.test.ts +1 -0
- package/src/execution/__tests__/spawned-children.test.ts +92 -0
- package/src/execution/__tests__/status-refactor.test.ts +345 -0
- package/src/execution/__tests__/stdin-writer.test.ts +97 -0
- package/src/execution/__tests__/subagent-service-message-close.test.ts +598 -0
- package/src/execution/__tests__/subagent-service-parent-guard.test.ts +180 -0
- package/src/execution/__tests__/subagent-service.test.ts +49 -11
- package/src/execution/__tests__/timeout-integration.test.ts +27 -13
- package/src/execution/__tests__/tool-action.test.ts +12 -10
- package/src/execution/__tests__/truncline-snapshot.test.ts +81 -0
- package/src/execution/__tests__/turn-limiter-semantics.test.ts +194 -0
- package/src/execution/__tests__/worktree-manager.test.ts +292 -89
- package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +13 -12
- package/src/execution/argv-mirror.ts +21 -2
- package/src/execution/execution-record.ts +126 -9
- package/src/execution/finalize-record.ts +90 -13
- package/src/execution/host-mode.ts +1 -1
- package/src/execution/lifecycle-manager.ts +484 -0
- package/src/execution/lifecycle-predicates.ts +65 -0
- package/src/execution/manifest-store.ts +61 -16
- package/src/execution/model-resolver.ts +26 -5
- package/src/execution/notifier.ts +69 -12
- package/src/execution/pi-invocation.ts +21 -1
- package/src/execution/record-store.ts +554 -107
- package/src/execution/session-pending.ts +116 -45
- package/src/execution/session-reconstructor.ts +224 -7
- package/src/execution/session-runner.ts +290 -75
- package/src/execution/sessions-index.ts +304 -0
- package/src/execution/stdin-writer.ts +93 -7
- package/src/execution/stream-sink.ts +20 -3
- package/src/execution/subagent-service.ts +867 -138
- package/src/execution/turn-limiter.ts +14 -0
- package/src/execution/types.ts +204 -22
- package/src/execution/worktree-manager.ts +128 -49
- package/src/execution/worktree-registry.ts +13 -2
- package/src/index.ts +277 -19
- package/src/injectors/subagent-list-injector.ts +26 -8
- package/src/injectors/workflow-list-injector.ts +25 -8
- package/src/interface/__tests__/subagent-tool-prompt.test.ts +18 -5
- package/src/interface/__tests__/tool-render.test.ts +15 -13
- package/src/interface/bg-notify-render.ts +32 -8
- package/src/interface/command-actions.ts +26 -7
- package/src/interface/commands.ts +21 -22
- package/src/interface/format.ts +44 -17
- package/src/interface/gui-mappers.ts +6 -8
- package/src/interface/helpers.ts +170 -10
- package/src/interface/list-component.ts +53 -14
- package/src/interface/subagent-actions.ts +235 -17
- package/src/interface/subagent-tool.ts +82 -17
- package/src/interface/subagents.ts +2 -1
- package/src/interface/tool-render.ts +11 -24
- package/src/interface/tool-workflow.ts +20 -35
- package/src/interface/views/WorkflowsView.ts +89 -32
- package/src/interface/views/__tests__/WorkflowsView-signature.test.ts +264 -0
- package/src/interface/views/__tests__/detail-content-session-file.test.ts +1 -1
- package/src/interface/views/format.ts +3 -3
- package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +325 -0
- package/src/orchestration/__tests__/args-validator.test.ts +1 -1
- package/src/orchestration/__tests__/config-loader.test.ts +38 -0
- package/src/orchestration/__tests__/error-recovery-handlers.test.ts +394 -4
- package/src/orchestration/__tests__/error-recovery-workflow-call.test.ts +4 -4
- package/src/orchestration/__tests__/execute-agent-call.test.ts +95 -0
- package/src/orchestration/__tests__/jsonl-run-store-session-file.test.ts +657 -18
- package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +0 -2
- package/src/orchestration/__tests__/lifecycle.test.ts +332 -149
- package/src/orchestration/__tests__/skill-discovery.test.ts +157 -0
- package/src/orchestration/__tests__/test-mocks.ts +191 -0
- package/src/orchestration/__tests__/worker-script-template-snapshot.test.ts +98 -0
- package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +0 -2
- package/src/orchestration/__tests__/workflow-script-lint-memo.test.ts +110 -0
- package/src/orchestration/__tests__/workflows-e2e.test.ts +1 -1
- package/src/orchestration/agent-opts-resolver.ts +4 -1
- package/src/orchestration/args-validator.ts +2 -2
- package/src/orchestration/config-loader.ts +30 -1
- package/src/orchestration/error-recovery.ts +133 -29
- package/src/orchestration/execute-agent-call.ts +31 -7
- package/src/orchestration/jsonl-run-store.ts +287 -40
- package/src/orchestration/launcher.ts +7 -1
- package/src/orchestration/lifecycle.ts +135 -132
- package/src/orchestration/models/__tests__/trace.test.ts +408 -0
- package/src/orchestration/models/budget.ts +1 -1
- package/src/orchestration/models/run-runtime.ts +15 -17
- package/src/orchestration/models/run-spec.ts +2 -2
- package/src/orchestration/models/run-state.ts +3 -3
- package/src/orchestration/models/trace.ts +95 -15
- package/src/orchestration/models/types.ts +8 -9
- package/src/orchestration/models/workflow-run.ts +50 -71
- package/src/orchestration/models/workflow-script.ts +32 -1
- package/src/orchestration/skill-discovery.ts +30 -0
- package/src/orchestration/worker-handle.ts +1 -1
- package/src/orchestration/worker-host.ts +1 -1
- package/src/orchestration/worker-script-builder.ts +29 -10
- package/src/shared/__tests__/agent-ref.test.ts +34 -0
- package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +280 -0
- package/src/shared/__tests__/resource-discovery.test.ts +55 -0
- package/src/shared/__tests__/schema-jsonify.test.ts +81 -0
- package/src/shared/agent-ref.ts +16 -0
- package/src/shared/resource-discovery.ts +147 -58
- package/src/shared/schema-jsonify.ts +53 -0
- package/workflows/README.md +4 -4
- package/agents/code-reviewer.md +0 -47
- package/agents/context-builder.md +0 -21
- package/agents/oracle.md +0 -34
- 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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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 "
|
|
214
|
-
|
|
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
|
-
|
|
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 "
|
|
221
|
-
|
|
222
|
-
|
|
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 &&
|
|
79
|
+
if (currentScript && !isBunVirtualScript && currentScriptExists()) {
|
|
60
80
|
return { command: process.execPath, args: [currentScript, ...userArgs] };
|
|
61
81
|
}
|
|
62
82
|
|