@maplezzk/pi-interactive-subagents 3.12.0 → 3.13.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.
- package/README.md +11 -0
- package/README.zh-CN.md +11 -0
- package/package.json +2 -2
- package/pi-extension/subagents/index.ts +38 -33
package/README.md
CHANGED
|
@@ -505,3 +505,14 @@ The sub-agent status supervision and turn-only interruption features were inspir
|
|
|
505
505
|
## License
|
|
506
506
|
|
|
507
507
|
MIT
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
## Naming composition
|
|
511
|
+
|
|
512
|
+
Subagents do not depend on `pi-naming`. Terminal creation supplies an initial surface label where supported; this extension does not schedule delayed title overwrites or rename the parent's terminal when `/plan` runs. Agent identity and activity remain visible in the subagent widget.
|
|
513
|
+
|
|
514
|
+
Each launch (Pi or Claude) and Pi resume writes fresh `PI_TERMINAL_RENAME_CONTEXT` ownership data through `pi-terminal-mux`, replacing inherited values. This allows a cooperating naming extension to update only the child's explicitly owned terminal target, never the shared workspace. Shared or unverified window/tab targets remain unchanged.
|
|
515
|
+
|
|
516
|
+
To use automatic titles or `/rename` in Pi children, explicitly add the installed `pi-naming` entrypoint to `subagentExtensions`. Merely installing both packages in the parent does not enable extensions in isolated child sessions. Naming uses the same terminal-mux protocol and does not import this package.
|
|
517
|
+
|
|
518
|
+
Publication requires a terminal-mux dependency version providing the ownership API.
|
package/README.zh-CN.md
CHANGED
|
@@ -72,3 +72,14 @@ zellij --session pi # 然后运行 pi
|
|
|
72
72
|
## 许可证
|
|
73
73
|
|
|
74
74
|
MIT
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
## 与命名插件组合
|
|
78
|
+
|
|
79
|
+
子代理插件不依赖 `pi-naming`。终端创建时在支持的范围内设置初始 surface 名称;不安排延迟覆盖标题,也不因 `/plan` 修改父终端名称。子代理身份与活动状态仍在 widget 中显示。
|
|
80
|
+
|
|
81
|
+
每次启动(Pi 或 Claude)以及 Pi 恢复时,通过 `pi-terminal-mux` 写入新的 `PI_TERMINAL_RENAME_CONTEXT` 归属信息,覆盖继承值。配合命名插件时,只允许更新子代理明确拥有的终端目标,不修改共享 workspace。共享或无法确认独占的 window/tab 保持不变。
|
|
82
|
+
|
|
83
|
+
如需在 Pi 子代理中使用自动标题或 `/rename`,将已安装的 `pi-naming` 入口显式加入 `subagentExtensions`。仅在父会话安装两个包不会开启隔离子会话的扩展。naming 通过相同的 mux 协议协作,不导入本包。
|
|
84
|
+
|
|
85
|
+
发布时 terminal-mux 依赖版本必须包含归属 API。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maplezzk/pi-interactive-subagents",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.13.1",
|
|
4
4
|
"description": "Interactive async subagents for pi — spawn, orchestrate, and manage sub-agent sessions in multiplexer panes. Fork of HazAT/pi-interactive-subagents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.ts",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"ajv": "^8.20.0",
|
|
65
65
|
"pi-extensions-i18n": "^0.4.0",
|
|
66
|
-
"pi-terminal-mux": "^0.
|
|
66
|
+
"pi-terminal-mux": "^0.5.1"
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|
|
69
69
|
"@earendil-works/pi-coding-agent": ">=0.80.0 <0.81.0",
|
|
@@ -25,9 +25,8 @@ import {
|
|
|
25
25
|
isMuxAvailable,
|
|
26
26
|
sendEscape,
|
|
27
27
|
shellEscape,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
renameAgent,
|
|
28
|
+
createSurfaceRenameContext,
|
|
29
|
+
TERMINAL_RENAME_CONTEXT_ENV,
|
|
31
30
|
readScreen,
|
|
32
31
|
getLastSplitSource,
|
|
33
32
|
clearLastSplitSource,
|
|
@@ -897,10 +896,25 @@ function handleSubagentInterrupt(
|
|
|
897
896
|
running.statusState = forceStatusAfterInterrupt(running.statusState, now);
|
|
898
897
|
updateWidget();
|
|
899
898
|
|
|
900
|
-
//
|
|
901
|
-
//
|
|
902
|
-
//
|
|
903
|
-
//
|
|
899
|
+
// Interrupting from the parent is terminal: Escape stops the child's active
|
|
900
|
+
// turn, while the `.exit` sidecar tells the watcher to close the surface and
|
|
901
|
+
// remove the child from the running set. Without the sidecar, the child Pi
|
|
902
|
+
// returns to its prompt and the watcher waits forever.
|
|
903
|
+
if (running.sessionFile) {
|
|
904
|
+
const exitFile = `${running.sessionFile}.exit`;
|
|
905
|
+
try {
|
|
906
|
+
writeFileSync(exitFile, JSON.stringify({ type: "done" }));
|
|
907
|
+
} catch (writeErr: unknown) {
|
|
908
|
+
const errorMessage = writeErr instanceof Error ? writeErr.message : String(writeErr);
|
|
909
|
+
const error =
|
|
910
|
+
`Failed to signal subagent "${running.name}" termination via ${exitFile}: ` +
|
|
911
|
+
errorMessage;
|
|
912
|
+
return {
|
|
913
|
+
content: [{ type: "text" as const, text: error }],
|
|
914
|
+
details: { error, id: running.id, name: running.name },
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
}
|
|
904
918
|
|
|
905
919
|
return {
|
|
906
920
|
content: [{ type: "text" as const, text: `Interrupt requested for subagent "${running.name}".` }],
|
|
@@ -1119,7 +1133,14 @@ function registerMuxConfigCommand(pi: ExtensionAPI): void {
|
|
|
1119
1133
|
}
|
|
1120
1134
|
}
|
|
1121
1135
|
|
|
1136
|
+
/** 每次启动或恢复都用新 surface 重建归属,不能继承父进程的改名范围。 */
|
|
1137
|
+
function buildTerminalRenameEnvironment(surface: string, backend = getMuxBackend()): string {
|
|
1138
|
+
const context = createSurfaceRenameContext(surface, backend);
|
|
1139
|
+
return `${TERMINAL_RENAME_CONTEXT_ENV}=${shellEscape(JSON.stringify(context))}`;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1122
1142
|
export const __test__ = {
|
|
1143
|
+
buildTerminalRenameEnvironment,
|
|
1123
1144
|
borderLine,
|
|
1124
1145
|
parseMuxConfigRequest,
|
|
1125
1146
|
getShellReadyDelayMs,
|
|
@@ -1198,6 +1219,7 @@ async function launchSubagent(
|
|
|
1198
1219
|
// For new surfaces, pause briefly so the shell is ready before sending the command.
|
|
1199
1220
|
const surfacePreCreated = !!options?.surface;
|
|
1200
1221
|
const surface = options?.surface ?? createSurface(params.name);
|
|
1222
|
+
const renameEnvironment = buildTerminalRenameEnvironment(surface);
|
|
1201
1223
|
const splitFrom = surfacePreCreated ? undefined : (getLastSplitSource() ?? undefined);
|
|
1202
1224
|
if (!surfacePreCreated) clearLastSplitSource();
|
|
1203
1225
|
if (!surfacePreCreated) {
|
|
@@ -1236,7 +1258,7 @@ async function launchSubagent(
|
|
|
1236
1258
|
const sentinelFile = `/tmp/pi-claude-${id}-done`;
|
|
1237
1259
|
const pluginDir = join(SUBAGENTS_DIR, "plugin");
|
|
1238
1260
|
|
|
1239
|
-
const cmdParts: string[] = [];
|
|
1261
|
+
const cmdParts: string[] = [renameEnvironment];
|
|
1240
1262
|
cmdParts.push(`PI_CLAUDE_SENTINEL=${shellEscape(sentinelFile)}`);
|
|
1241
1263
|
cmdParts.push("claude");
|
|
1242
1264
|
cmdParts.push("--dangerously-skip-permissions");
|
|
@@ -1340,7 +1362,7 @@ async function launchSubagent(
|
|
|
1340
1362
|
}
|
|
1341
1363
|
|
|
1342
1364
|
// Build env prefix: denied tools + subagent identity + config dir propagation
|
|
1343
|
-
const envParts: string[] = [];
|
|
1365
|
+
const envParts: string[] = [renameEnvironment];
|
|
1344
1366
|
|
|
1345
1367
|
// If the target cwd has its own .pi/agent/, use that as the config root.
|
|
1346
1368
|
// Otherwise propagate the current/global agent dir.
|
|
@@ -1413,12 +1435,6 @@ async function launchSubagent(
|
|
|
1413
1435
|
].join("\n"),
|
|
1414
1436
|
});
|
|
1415
1437
|
|
|
1416
|
-
// 延迟重命名 agent 标题(左侧侧栏),需要等 pi 启动被 herdr 检测到
|
|
1417
|
-
const agentName = params.name;
|
|
1418
|
-
const agentSurface = surface;
|
|
1419
|
-
setTimeout(() => renameAgent(agentSurface, agentName), 3000);
|
|
1420
|
-
setTimeout(() => renameAgent(agentSurface, agentName), 5000);
|
|
1421
|
-
|
|
1422
1438
|
const running: RunningSubagent = {
|
|
1423
1439
|
id,
|
|
1424
1440
|
name: params.name,
|
|
@@ -1849,13 +1865,11 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
1849
1865
|
name: "subagent_interrupt",
|
|
1850
1866
|
label: "Interrupt Subagent",
|
|
1851
1867
|
description:
|
|
1852
|
-
"Send Escape to the active turn of a currently running Pi-backed subagent. " +
|
|
1853
|
-
"The
|
|
1854
|
-
"and does not emit a subagent_result solely because of this request.",
|
|
1868
|
+
"Send Escape to stop the active turn of a currently running Pi-backed subagent, then terminate the child Pi process. " +
|
|
1869
|
+
"The parent watcher consumes the completion signal, closes the child pane, and removes the subagent from the running set.",
|
|
1855
1870
|
promptSnippet:
|
|
1856
|
-
"Send Escape to the active turn of a currently running Pi-backed subagent. " +
|
|
1857
|
-
"The
|
|
1858
|
-
"and does not emit a subagent_result solely because of this request.",
|
|
1871
|
+
"Send Escape to stop the active turn of a currently running Pi-backed subagent, then terminate the child Pi process. " +
|
|
1872
|
+
"The parent watcher consumes the completion signal, closes the child pane, and removes the subagent from the running set.",
|
|
1859
1873
|
parameters: Type.Object({
|
|
1860
1874
|
id: Type.Optional(Type.String({ description: "Exact running subagent id" })),
|
|
1861
1875
|
name: Type.Optional(Type.String({ description: "Exact running subagent display name" })),
|
|
@@ -2036,6 +2050,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
2036
2050
|
const entryCountBefore = getNewEntries(params.sessionPath, 0).length;
|
|
2037
2051
|
|
|
2038
2052
|
const surface = createSurface(name);
|
|
2053
|
+
const renameEnvironment = buildTerminalRenameEnvironment(surface);
|
|
2039
2054
|
await new Promise<void>((resolve) => setTimeout(resolve, getShellReadyDelayMs()));
|
|
2040
2055
|
|
|
2041
2056
|
// Build pi resume command
|
|
@@ -2065,7 +2080,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
2065
2080
|
}
|
|
2066
2081
|
|
|
2067
2082
|
// Build env prefix — propagate PI_CODING_AGENT_DIR for config isolation
|
|
2068
|
-
const resumeEnvParts: string[] = [];
|
|
2083
|
+
const resumeEnvParts: string[] = [renameEnvironment];
|
|
2069
2084
|
const { allowSubagentSpawning } = loadSubagentSpawningConfig();
|
|
2070
2085
|
if (process.env.PI_CODING_AGENT_DIR) {
|
|
2071
2086
|
resumeEnvParts.push(`PI_CODING_AGENT_DIR=${shellEscape(process.env.PI_CODING_AGENT_DIR)}`);
|
|
@@ -2073,6 +2088,7 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
2073
2088
|
resumeEnvParts.push(`PI_SUBAGENT_NAME=${shellEscape(name)}`);
|
|
2074
2089
|
resumeEnvParts.push(`PI_SUBAGENT_SESSION=${shellEscape(params.sessionPath)}`);
|
|
2075
2090
|
resumeEnvParts.push(`PI_SUBAGENT_ID=${shellEscape(id)}`);
|
|
2091
|
+
resumeEnvParts.push(`PI_SUBAGENT_SURFACE=${shellEscape(surface)}`);
|
|
2076
2092
|
resumeEnvParts.push(`PI_SUBAGENT_ACTIVITY_FILE=${shellEscape(activityFile)}`);
|
|
2077
2093
|
resumeEnvParts.push(
|
|
2078
2094
|
`PI_DENY_TOOLS=${shellEscape([...resolveDenyTools(null, allowSubagentSpawning)].join(","))}`,
|
|
@@ -2380,17 +2396,6 @@ export default function subagentsExtension(pi: ExtensionAPI) {
|
|
|
2380
2396
|
return;
|
|
2381
2397
|
}
|
|
2382
2398
|
|
|
2383
|
-
// Rename workspace and tab to show this is a planning session
|
|
2384
|
-
if (isMuxAvailable()) {
|
|
2385
|
-
try {
|
|
2386
|
-
const label = task.length > 40 ? task.slice(0, 40) + "..." : task;
|
|
2387
|
-
renameWorkspace(`🎯 ${label}`);
|
|
2388
|
-
renameCurrentTab(`🎯 Plan: ${label}`);
|
|
2389
|
-
} catch {
|
|
2390
|
-
// non-critical -- do not block the plan
|
|
2391
|
-
}
|
|
2392
|
-
}
|
|
2393
|
-
|
|
2394
2399
|
// Load the plan skill from the subagents extension directory
|
|
2395
2400
|
const planSkillPath = join(SUBAGENTS_DIR, "plan-skill.md");
|
|
2396
2401
|
let content = readFileSync(planSkillPath, "utf8");
|