@zhushanwen/pi-subagent-workflow 7.3.2 → 7.3.3
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhushanwen/pi-subagent-workflow",
|
|
3
|
-
"version": "7.3.
|
|
3
|
+
"version": "7.3.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"description": "Unified subagent execution and multi-agent workflow orchestration for Pi — spawned-process agent runtime with sync/background modes, stateful workflow management with persistence, state machine, and execution tracing.",
|
|
@@ -764,6 +764,26 @@ export async function runSpawn(
|
|
|
764
764
|
// abortRunningControllers 跳过它,靠本 Set 兜底)。close/error 后移除(已退出无需再 kill)。
|
|
765
765
|
spawnedChildren.add(child);
|
|
766
766
|
|
|
767
|
+
// [worktree-reaper-fix] 同步补全注册表 pid:spawn 返回后 child.pid 立即可得(Node.js
|
|
768
|
+
// 同步属性),无需等任何 stdout 事件。原补全点挂在 header 分支(下方 stdout handler 内),
|
|
769
|
+
// 而 RPC mode(buildSpawnArgs 固定 --mode rpc)不输出 header 行——pid 恒为 0,超
|
|
770
|
+
// SPAWN_GRACE_MS 后被 reaper 当孤儿误删活 worktree(2026-08-11 cw 递归编排整树失活事故)。
|
|
771
|
+
// header 分支调用保留:json mode 回切时仍能补全,updatePid 同 branch 覆盖写幂等,无副作用。
|
|
772
|
+
// [S1] 防御:必须放在 spawnedChildren.add 之后(onWorktreePid 抛错时子进程已被跟踪,
|
|
773
|
+
// dispose 兜底 kill 不会泄漏),且包 try/catch(补全失败不阻断 spawn 主流程——
|
|
774
|
+
// 注册表写失败最坏后果是条目停留 pid=0,由 reaper 宽限回收兜底)。
|
|
775
|
+
if (opts.worktree && child.pid) {
|
|
776
|
+
try {
|
|
777
|
+
ctx.onWorktreePid?.(opts.worktree.branch, child.pid);
|
|
778
|
+
} catch (err) {
|
|
779
|
+
logger.warn("[worktree] worktree pid registration failed (defensive)", {
|
|
780
|
+
branch: opts.worktree.branch,
|
|
781
|
+
pid: child.pid,
|
|
782
|
+
err: err instanceof Error ? err.message : String(err),
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
767
787
|
// stdout/stderr 用 utf8 编码:stream 自动按字符边界切分,避免多字节
|
|
768
788
|
// UTF-8(CJK/emoji)跨 chunk 时 toString() 产生 U+FFFD 替换符导致 JSON.parse 失败。
|
|
769
789
|
// [m2] 先 setEncoding 再注册 signal listener/watchdog:若 setEncoding 抛错,try/finally
|
|
@@ -997,9 +1017,12 @@ export async function runSpawn(
|
|
|
997
1017
|
// [worktree-reaper-fix] 拼 spawnCwd 进错误消息:ENOENT 的 err.message 只含 command 名,
|
|
998
1018
|
// 无 cwd 线索(worktree 被 reaper 误删后 cwd 指向虚空)会导致误诊——2026-08-11 事故
|
|
999
1019
|
// AI 误判"node 被卸载"的直接原因。
|
|
1020
|
+
// [S3] code 读取带运行时 guard:非 ErrnoException(普通 Error)时 code 为 undefined,
|
|
1021
|
+
// 不加 cwd hint(行为与修复前一致);仅 ENOENT 才拼 cwd。
|
|
1000
1022
|
spawnedChildren.delete(child);
|
|
1001
1023
|
const errno = err as NodeJS.ErrnoException;
|
|
1002
|
-
const
|
|
1024
|
+
const errCode = "code" in err ? errno.code : undefined;
|
|
1025
|
+
const cwdHint = errCode === "ENOENT" ? ` (cwd: ${spawnCwd})` : "";
|
|
1003
1026
|
record.lastError = `${err.message}${cwdHint}`;
|
|
1004
1027
|
resolve(SIGNAL_EXIT_CODE_THRESHOLD); // 非零退出
|
|
1005
1028
|
});
|
package/src/index.ts
CHANGED
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import * as fs from "node:fs";
|
|
17
|
-
import * as os from "node:os";
|
|
18
17
|
import * as path from "node:path";
|
|
19
18
|
|
|
20
19
|
import type { ExtensionAPI, ExtensionContext, SessionShutdownEvent, SessionStartEvent, SessionTreeEvent } from "@earendil-works/pi-coding-agent";
|
|
@@ -166,9 +165,10 @@ export default function subagentsWorkflowExtension(pi: ExtensionAPI): void {
|
|
|
166
165
|
}
|
|
167
166
|
|
|
168
167
|
function resolveSessionDir(): string {
|
|
169
|
-
const defaultDir =
|
|
168
|
+
const defaultDir = getAgentDir();
|
|
170
169
|
const sessionSlug = `--${process.cwd().replace(/^\//, "").replace(/\//g, "-")}--`;
|
|
171
|
-
|
|
170
|
+
// F2:根改 getAgentDir() 派生(实例隔离);保留 sessionScopedDir 存在则用之的探测语义
|
|
171
|
+
const sessionScopedDir = path.join(getAgentDir(), "sessions", sessionSlug);
|
|
172
172
|
return fs.existsSync(sessionScopedDir) ? sessionScopedDir : defaultDir;
|
|
173
173
|
}
|
|
174
174
|
|
package/src/interface/format.ts
CHANGED
|
@@ -310,6 +310,7 @@ export function formatToolCall(
|
|
|
310
310
|
theme: ThemeLike,
|
|
311
311
|
): string {
|
|
312
312
|
const shortenPath = (p: string): string => {
|
|
313
|
+
// 仅用于显示层路径缩写(~ 替换 home 前缀),不读取 pi 目录(TC9 合法命中)
|
|
313
314
|
const home = os.homedir();
|
|
314
315
|
return p.startsWith(home) ? `~${p.slice(home.length)}` : p;
|
|
315
316
|
};
|
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import { promises as fsPromises } from "node:fs";
|
|
26
|
-
import { homedir } from "node:os";
|
|
27
26
|
import { join as pathJoin } from "node:path";
|
|
28
27
|
|
|
29
28
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
29
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
30
30
|
import { Key, matchesKey } from "@earendil-works/pi-tui";
|
|
31
31
|
|
|
32
32
|
import {
|
|
@@ -858,11 +858,11 @@ const TRACE_ACTIVITY_WIDTH = 80;
|
|
|
858
858
|
|
|
859
859
|
/**
|
|
860
860
|
* 导出完整 workflow trace 到 Markdown 文件。
|
|
861
|
-
*
|
|
861
|
+
* 路径:<agentDir>/workflow-traces/{runId}.md(agentDir = getAgentDir(),实例隔离)
|
|
862
862
|
* 对齐 main 的 saveTraceToFile(WorkflowsView.ts:365-396)。
|
|
863
863
|
*/
|
|
864
864
|
function saveTraceToFile(run: WorkflowRun, ctx: ExtensionContext): void {
|
|
865
|
-
const dir = pathJoin(
|
|
865
|
+
const dir = pathJoin(getAgentDir(), "workflow-traces");
|
|
866
866
|
const filePath = pathJoin(dir, `${run.runId}.md`);
|
|
867
867
|
const lines: string[] = [];
|
|
868
868
|
lines.push(`# Workflow Trace: ${run.spec.scriptName} (${run.runId})`, "");
|
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import * as fs from "node:fs";
|
|
9
|
-
import * as os from "node:os";
|
|
10
9
|
import * as path from "node:path";
|
|
11
10
|
|
|
11
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
12
|
+
|
|
12
13
|
// ── Skill path resolution (with npm dir cache) ─────────────────────
|
|
13
14
|
|
|
14
15
|
const skillCandidatesCache = new Map<string, string[]>();
|
|
@@ -32,8 +33,9 @@ function getNpmSkillCandidates(npmSkillsDir: string): string[] {
|
|
|
32
33
|
* Resolve a skill name to its directory or SKILL.md path.
|
|
33
34
|
* Search order:
|
|
34
35
|
* 1. Project-level: .agents/skills/<name>/
|
|
35
|
-
* 2. Global:
|
|
36
|
-
*
|
|
36
|
+
* 2. Global: <agentDir>/skills/<name>/(agentDir = getAgentDir(),实例隔离:
|
|
37
|
+
* PI_CODING_AGENT_DIR 场景读隔离目录,不碰 ~/.pi/agent)
|
|
38
|
+
* 3. npm packages: <agentDir>/npm/node_modules/<pkg>/skills/<name>/
|
|
37
39
|
* Returns the directory path if found, undefined otherwise.
|
|
38
40
|
*/
|
|
39
41
|
export function resolveSkillPath(skillName: string): string | undefined {
|
|
@@ -41,11 +43,11 @@ export function resolveSkillPath(skillName: string): string | undefined {
|
|
|
41
43
|
// Project-level
|
|
42
44
|
path.resolve(process.cwd(), ".agents/skills", skillName),
|
|
43
45
|
// Global user skills
|
|
44
|
-
path.join(
|
|
46
|
+
path.join(getAgentDir(), "skills", skillName),
|
|
45
47
|
];
|
|
46
48
|
|
|
47
49
|
// npm package skills (cached)
|
|
48
|
-
const npmSkillsDir = path.join(
|
|
50
|
+
const npmSkillsDir = path.join(getAgentDir(), "npm/node_modules");
|
|
49
51
|
for (const pkgSkillsBase of getNpmSkillCandidates(npmSkillsDir)) {
|
|
50
52
|
candidates.push(path.join(pkgSkillsBase, skillName));
|
|
51
53
|
}
|