@herbertgao/pi-subagents 0.15.2 → 0.15.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/CHANGELOG.md +6 -0
- package/package.json +2 -2
- package/src/agent-manager.ts +1 -0
- package/src/agent-runner.ts +3 -1
- package/src/index.ts +33 -35
- package/src/nested-tools.ts +26 -35
- package/src/prompts.ts +15 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.15.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#27](https://github.com/HerbertGao/pi-extensions/pull/27) [`32bb76c`](https://github.com/HerbertGao/pi-extensions/commit/32bb76c117971de27db9c2b521d19df3ba9ea322) Thanks [@HerbertGao](https://github.com/HerbertGao)! - Keep worktree-isolated agents inside their copy, preserve real Agent tool startup errors, and render unknown or failed Agent results without misleading completion status.
|
|
8
|
+
|
|
3
9
|
## 0.15.2
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@herbertgao/pi-subagents",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.3",
|
|
4
4
|
"description": "Claude Code-style autonomous subagents for Pi, with HerbertGao-maintained UI extensions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -66,6 +66,6 @@
|
|
|
66
66
|
"package": "@tintinweb/pi-subagents",
|
|
67
67
|
"version": "0.15.0",
|
|
68
68
|
"repository": "https://github.com/tintinweb/pi-subagents",
|
|
69
|
-
"commit": "
|
|
69
|
+
"commit": "c83dd82"
|
|
70
70
|
}
|
|
71
71
|
}
|
package/src/agent-manager.ts
CHANGED
|
@@ -321,6 +321,7 @@ export class AgentManager {
|
|
|
321
321
|
// stay undefined otherwise so plain worktree runs keep resolving config
|
|
322
322
|
// (incl. relative extension paths and memory) inside the worktree copy.
|
|
323
323
|
cwd: worktreeCwd ?? customCwd,
|
|
324
|
+
worktreeBase: worktreeCwd ? baseCwd : undefined,
|
|
324
325
|
configCwd:
|
|
325
326
|
options.configCwd ?? (customCwd !== undefined ? ctx.cwd : undefined),
|
|
326
327
|
signal: record.abortController!.signal,
|
package/src/agent-runner.ts
CHANGED
|
@@ -414,6 +414,8 @@ export interface RunOptions {
|
|
|
414
414
|
thinkingLevel?: ThinkingLevel
|
|
415
415
|
/** Override working directory (e.g. for worktree isolation). */
|
|
416
416
|
cwd?: string
|
|
417
|
+
/** Original checkout path when cwd is an isolated worktree copy. */
|
|
418
|
+
worktreeBase?: string
|
|
417
419
|
/**
|
|
418
420
|
* Where .pi config is discovered (project extensions, skills, pi settings,
|
|
419
421
|
* agent memory). Default: same as the working directory. The manager sets
|
|
@@ -602,7 +604,7 @@ export async function runAgent(
|
|
|
602
604
|
const parentSystemPrompt = ctx.getSystemPrompt()
|
|
603
605
|
|
|
604
606
|
// Build prompt extras (memory, skill preloading)
|
|
605
|
-
const extras: PromptExtras = {}
|
|
607
|
+
const extras: PromptExtras = { worktreeBase: options.worktreeBase }
|
|
606
608
|
|
|
607
609
|
// Resolve extensions/skills: isolated overrides to false
|
|
608
610
|
const extensions = options.isolated ? false : config.extensions
|
package/src/index.ts
CHANGED
|
@@ -1241,12 +1241,12 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
1241
1241
|
)
|
|
1242
1242
|
},
|
|
1243
1243
|
|
|
1244
|
-
renderResult(result, { expanded, isPartial }, theme) {
|
|
1244
|
+
renderResult(result, { expanded, isPartial }, theme, renderContext) {
|
|
1245
|
+
const resultText =
|
|
1246
|
+
result.content[0]?.type === "text" ? result.content[0].text : ""
|
|
1245
1247
|
const details = result.details as AgentDetails | undefined
|
|
1246
|
-
if (!details) {
|
|
1247
|
-
|
|
1248
|
-
result.content[0]?.type === "text" ? result.content[0].text : ""
|
|
1249
|
-
return new Text(text, 0, 0)
|
|
1248
|
+
if (renderContext?.isError || !details?.status) {
|
|
1249
|
+
return new Text(resultText, 0, 0)
|
|
1250
1250
|
}
|
|
1251
1251
|
|
|
1252
1252
|
// Helper: build "haiku · thinking: high · ↻5≤30 · 3 tool uses · 33.8k tokens" stats string
|
|
@@ -1301,8 +1301,6 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
1301
1301
|
line += " " + theme.fg("dim", "·") + " " + theme.fg("dim", duration)
|
|
1302
1302
|
|
|
1303
1303
|
if (expanded) {
|
|
1304
|
-
const resultText =
|
|
1305
|
-
result.content[0]?.type === "text" ? result.content[0].text : ""
|
|
1306
1304
|
if (resultText) {
|
|
1307
1305
|
const lines = resultText.split("\n").slice(0, 50)
|
|
1308
1306
|
for (const l of lines) {
|
|
@@ -1332,6 +1330,10 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
1332
1330
|
return new Text(line, 0, 0)
|
|
1333
1331
|
}
|
|
1334
1332
|
|
|
1333
|
+
if (details.status !== "error" && details.status !== "aborted") {
|
|
1334
|
+
return new Text(resultText, 0, 0)
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1335
1337
|
// ---- Error / Aborted (hard max_turns) ----
|
|
1336
1338
|
const s = stats(details)
|
|
1337
1339
|
let line = theme.fg("error", "✗") + (s ? " " + s : "")
|
|
@@ -1598,23 +1600,22 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
1598
1600
|
}
|
|
1599
1601
|
}
|
|
1600
1602
|
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
}
|
|
1603
|
+
// A throw here means the agent never started. Let it out: Pi marks a
|
|
1604
|
+
// tool call failed only when execute throws, while a returned message
|
|
1605
|
+
// reads to the model as a subagent that ran and reported this.
|
|
1606
|
+
id = manager.spawn(pi, ctx, subagentType, params.prompt, {
|
|
1607
|
+
description: params.description,
|
|
1608
|
+
model,
|
|
1609
|
+
maxTurns: effectiveMaxTurns,
|
|
1610
|
+
isolated,
|
|
1611
|
+
inheritContext,
|
|
1612
|
+
thinkingLevel: thinking,
|
|
1613
|
+
isBackground: true,
|
|
1614
|
+
isolation,
|
|
1615
|
+
invocation: agentInvocation,
|
|
1616
|
+
rootSessionId: ctx.sessionManager.getSessionId(),
|
|
1617
|
+
...bgCallbacks,
|
|
1618
|
+
})
|
|
1618
1619
|
|
|
1619
1620
|
// Set output file + join mode synchronously after spawn, before the
|
|
1620
1621
|
// event loop yields — onSessionCreated is async so this is safe.
|
|
@@ -1774,18 +1775,15 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
1774
1775
|
},
|
|
1775
1776
|
)
|
|
1776
1777
|
record = fgResult.record
|
|
1777
|
-
}
|
|
1778
|
+
} finally {
|
|
1779
|
+
// A startup throw propagates as a failed tool call without leaving
|
|
1780
|
+
// the spinner running or a finished agent in the widget.
|
|
1778
1781
|
clearInterval(spinnerInterval)
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
// Clean up foreground agent from widget
|
|
1785
|
-
if (fgId) {
|
|
1786
|
-
agentActivity.delete(fgId)
|
|
1787
|
-
widget.markFinished(fgId)
|
|
1788
|
-
fleet.onAgentFinished(fgId)
|
|
1782
|
+
if (fgId) {
|
|
1783
|
+
agentActivity.delete(fgId)
|
|
1784
|
+
widget.markFinished(fgId)
|
|
1785
|
+
fleet.onAgentFinished(fgId)
|
|
1786
|
+
}
|
|
1789
1787
|
}
|
|
1790
1788
|
|
|
1791
1789
|
// Get final token count
|
package/src/nested-tools.ts
CHANGED
|
@@ -399,47 +399,38 @@ export function createNestedSubagentTools(
|
|
|
399
399
|
// one earlier would silently give a grandchild the wrong worktree base, the
|
|
400
400
|
// wrong conversation under inherit_context, and the wrong inherited model.
|
|
401
401
|
//
|
|
402
|
-
//
|
|
403
|
-
//
|
|
404
|
-
//
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
const id = context.manager.spawn(
|
|
408
|
-
context.pi,
|
|
409
|
-
ctx,
|
|
410
|
-
resolvedType,
|
|
411
|
-
params.prompt,
|
|
412
|
-
{
|
|
413
|
-
...options,
|
|
414
|
-
isBackground: true,
|
|
415
|
-
},
|
|
416
|
-
)
|
|
417
|
-
// Synchronous, before the event loop yields — onSessionCreated fires
|
|
418
|
-
// asynchronously inside runAgent, so the file is attached in time.
|
|
419
|
-
attachTranscript(id)
|
|
420
|
-
return textResult(
|
|
421
|
-
`Nested agent started in background. Agent ID: ${id}`,
|
|
422
|
-
)
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
const { record } = await context.manager.spawnAndWait(
|
|
402
|
+
// Startup throws mean no child ran. Let them propagate so Pi marks the
|
|
403
|
+
// nested Agent tool call failed instead of presenting the message as a
|
|
404
|
+
// successful child result.
|
|
405
|
+
if (invocation.runInBackground) {
|
|
406
|
+
const id = context.manager.spawn(
|
|
426
407
|
context.pi,
|
|
427
408
|
ctx,
|
|
428
409
|
resolvedType,
|
|
429
410
|
params.prompt,
|
|
430
|
-
{
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
formatRecord(record, "inline"),
|
|
435
|
-
record.status === "error",
|
|
436
|
-
)
|
|
437
|
-
} catch (err) {
|
|
438
|
-
return textResult(
|
|
439
|
-
err instanceof Error ? err.message : String(err),
|
|
440
|
-
true,
|
|
411
|
+
{
|
|
412
|
+
...options,
|
|
413
|
+
isBackground: true,
|
|
414
|
+
},
|
|
441
415
|
)
|
|
416
|
+
// Synchronous, before the event loop yields — onSessionCreated fires
|
|
417
|
+
// asynchronously inside runAgent, so the file is attached in time.
|
|
418
|
+
attachTranscript(id)
|
|
419
|
+
return textResult(`Nested agent started in background. Agent ID: ${id}`)
|
|
442
420
|
}
|
|
421
|
+
|
|
422
|
+
const { record } = await context.manager.spawnAndWait(
|
|
423
|
+
context.pi,
|
|
424
|
+
ctx,
|
|
425
|
+
resolvedType,
|
|
426
|
+
params.prompt,
|
|
427
|
+
{ ...options, signal },
|
|
428
|
+
attachTranscript,
|
|
429
|
+
)
|
|
430
|
+
return textResult(
|
|
431
|
+
formatRecord(record, "inline"),
|
|
432
|
+
record.status === "error",
|
|
433
|
+
)
|
|
443
434
|
},
|
|
444
435
|
})
|
|
445
436
|
|
package/src/prompts.ts
CHANGED
|
@@ -10,6 +10,8 @@ export interface PromptExtras {
|
|
|
10
10
|
memoryBlock?: string
|
|
11
11
|
/** Preloaded skill contents to inject. */
|
|
12
12
|
skillBlocks?: { name: string; content: string }[]
|
|
13
|
+
/** Original checkout path when cwd is an isolated worktree copy. */
|
|
14
|
+
worktreeBase?: string
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
/**
|
|
@@ -55,6 +57,12 @@ Platform: ${env.platform}`
|
|
|
55
57
|
}
|
|
56
58
|
const extrasSuffix =
|
|
57
59
|
extraSections.length > 0 ? "\n\n" + extraSections.join("\n") : ""
|
|
60
|
+
const worktreeSection = extras?.worktreeBase
|
|
61
|
+
? `\n\n<worktree_isolation>
|
|
62
|
+
Your working directory is an isolated git worktree copy of ${extras.worktreeBase}.
|
|
63
|
+
Work only inside it — never in ${extras.worktreeBase}, even if other instructions name that path as your working directory.
|
|
64
|
+
</worktree_isolation>`
|
|
65
|
+
: ""
|
|
58
66
|
|
|
59
67
|
if (config.promptMode === "append") {
|
|
60
68
|
const identity = parentSystemPrompt || genericBase
|
|
@@ -88,6 +96,7 @@ You are operating as a sub-agent invoked to handle a specific task.
|
|
|
88
96
|
"\n\n" +
|
|
89
97
|
activeAgentTag +
|
|
90
98
|
envBlock +
|
|
99
|
+
worktreeSection +
|
|
91
100
|
customSection +
|
|
92
101
|
extrasSuffix
|
|
93
102
|
)
|
|
@@ -100,7 +109,12 @@ You have been invoked to handle a specific task autonomously.
|
|
|
100
109
|
${envBlock}`
|
|
101
110
|
|
|
102
111
|
return (
|
|
103
|
-
activeAgentTag +
|
|
112
|
+
activeAgentTag +
|
|
113
|
+
replaceHeader +
|
|
114
|
+
worktreeSection +
|
|
115
|
+
"\n\n" +
|
|
116
|
+
config.systemPrompt +
|
|
117
|
+
extrasSuffix
|
|
104
118
|
)
|
|
105
119
|
}
|
|
106
120
|
|