@herbertgao/pi-subagents 0.18.0 → 0.18.2

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 CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.18.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [#227](https://github.com/HerbertGao/pi-extensions/pull/227) [`d052f3f`](https://github.com/HerbertGao/pi-extensions/commit/d052f3f3c81e9b6a74d344e9de0f83b8d28341d3) Thanks [@HerbertGao](https://github.com/HerbertGao)! - Retain completed subagent results until they are consumed, including when cleanup runs after ten minutes.
8
+
9
+ Fix a separate Pi 0.87 compatibility issue in mention clones: restore history through SessionManager and provide the live prompt through the before_agent_start hook instead of writing getter-only agent state.
10
+
11
+ ## 0.18.1
12
+
13
+ ### Patch Changes
14
+
15
+ - [#219](https://github.com/HerbertGao/pi-extensions/pull/219) [`6525a59`](https://github.com/HerbertGao/pi-extensions/commit/6525a5995b8bb815517e326f1eef78d25c0a3c5e) Thanks [@HerbertGao](https://github.com/HerbertGao)! - Bundle `pi-multi-account@1.22.0` for automatic multi-account failover and rotation across supported Pi providers. Update the aggregate host to Pi 0.85.1 and widen the maintained child package compatibility ranges.
16
+
3
17
  ## 0.18.0
4
18
 
5
19
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@herbertgao/pi-subagents",
3
- "version": "0.18.0",
3
+ "version": "0.18.2",
4
4
  "description": "Claude Code-style autonomous subagents and workflow orchestration for Pi, with HerbertGao-maintained UI extensions.",
5
5
  "keywords": [
6
6
  "agent",
@@ -59,7 +59,7 @@
59
59
  "@earendil-works/pi-tui": ">=0.84.0"
60
60
  },
61
61
  "engines": {
62
- "node": ">=22.19.0"
62
+ "node": ">=24"
63
63
  },
64
64
  "pi": {
65
65
  "extensions": [
@@ -440,8 +440,8 @@ export class AgentManager {
440
440
 
441
441
  /**
442
442
  * Evicted agents that can still be reached by name, keyed by handle. Outlives
443
- * the 10-minute record cleanup — that timer exists to bound memory, not to
444
- * expire a conversation the user might still want — and is cleared alongside
443
+ * the 10-minute record cleanup — that timer bounds memory after results are
444
+ * consumed — and is cleared alongside
445
445
  * completed records on session start/switch.
446
446
  */
447
447
  private tombstones = new Map<string, AgentTombstone>()
@@ -483,7 +483,7 @@ export class AgentManager {
483
483
  this.onCompact = onCompact
484
484
  this.onUsage = onUsage
485
485
  this.maxConcurrent = maxConcurrent
486
- // Cleanup completed agents after 10 minutes (but keep sessions for resume)
486
+ // Cleanup consumed completed agents after 10 minutes
487
487
  this.cleanupInterval = setInterval(() => this.cleanup(), 60_000)
488
488
  this.cleanupInterval.unref()
489
489
  }
@@ -1640,6 +1640,7 @@ export class AgentManager {
1640
1640
  for (const [id, record] of this.agents) {
1641
1641
  if (record.status === "running" || record.status === "queued") continue
1642
1642
  if ((record.completedAt ?? 0) >= cutoff) continue
1643
+ if (!record.resultConsumed) continue
1643
1644
  this.removeRecord(id, record)
1644
1645
  }
1645
1646
  }
@@ -1648,7 +1649,7 @@ export class AgentManager {
1648
1649
  * Remove all completed/stopped/errored records immediately.
1649
1650
  * Called on session start/switch so tasks from a prior session don't persist.
1650
1651
  * Pass skipUnconsumed=true to preserve records the LLM hasn't read yet
1651
- * (resultConsumed=false) they will be evicted by the 10-minute cleanup timer instead.
1652
+ * (resultConsumed=false). The cleanup timer only evicts consumed results.
1652
1653
  */
1653
1654
  clearCompleted(skipUnconsumed = false): void {
1654
1655
  for (const [id, record] of this.agents) {
@@ -64,8 +64,11 @@
64
64
  import type { Model } from "@earendil-works/pi-ai"
65
65
  import {
66
66
  buildSessionContext,
67
+ convertToLlm,
67
68
  createAgentSession,
69
+ DefaultResourceLoader,
68
70
  type ExtensionContext,
71
+ getAgentDir,
69
72
  SessionManager,
70
73
  type ToolDefinition,
71
74
  } from "@earendil-works/pi-coding-agent"
@@ -163,12 +166,43 @@ export async function runMentionClone(
163
166
  // `/think` is on anyway. Same shim shape as `modelRuntime` below.
164
167
  const thinkingLevel = (ctx as { thinkingLevel?: ThinkingLevel })
165
168
  .thinkingLevel
166
- const created = await runInChildSessionContext(() =>
167
- createAgentSession({
169
+ const sessionManager = SessionManager.inMemory(ctx.cwd)
170
+ for (const entry of conversation.messages) {
171
+ // The live prompt is copied below. Do not replay the parent's system
172
+ // messages: newer Pi also stores its tool declarations there.
173
+ if ((entry.role as string) === "system") continue
174
+ // Summary messages are projections, not appendable session entries.
175
+ // Preserve exactly the user message Pi would send for each summary.
176
+ if (
177
+ entry.role === "compactionSummary" ||
178
+ entry.role === "branchSummary"
179
+ ) {
180
+ for (const message of convertToLlm([entry]))
181
+ sessionManager.appendMessage(message)
182
+ } else {
183
+ sessionManager.appendMessage(entry)
184
+ }
185
+ }
186
+ const systemPrompt = ctx.getSystemPrompt?.()
187
+ const created = await runInChildSessionContext(async () => {
188
+ const resourceLoader = new DefaultResourceLoader({
189
+ cwd: ctx.cwd,
190
+ agentDir: getAgentDir(),
191
+ extensionFactories: [
192
+ (pi) => {
193
+ pi.on("before_agent_start", () =>
194
+ systemPrompt ? { systemPrompt } : undefined,
195
+ )
196
+ },
197
+ ],
198
+ })
199
+ await resourceLoader.reload()
200
+ return createAgentSession({
168
201
  cwd: ctx.cwd,
169
202
  // Nothing about the copy is worth persisting, and an in-memory manager
170
203
  // is also what keeps the real session untouched.
171
- sessionManager: SessionManager.inMemory(ctx.cwd),
204
+ sessionManager,
205
+ resourceLoader,
172
206
  model: ctx.model as Model<never> | undefined,
173
207
  ...(thinkingLevel && { thinkingLevel }),
174
208
  modelRegistry: ctx.modelRegistry,
@@ -185,21 +219,10 @@ export async function runMentionClone(
185
219
  // agent-runner's `tools: sessionTools` beside its nested `customTools`.
186
220
  tools: [cloneAgentTool.name],
187
221
  customTools: [cloneAgentTool],
188
- } as Parameters<typeof createAgentSession>[0]),
189
- )
222
+ } as Parameters<typeof createAgentSession>[0])
223
+ })
190
224
  session = created.session
191
225
 
192
- // The clone rebuilds a system prompt from cwd and agentDir, which is close
193
- // but not the live one — extensions contribute to it per turn. Copy the
194
- // real thing, so the copy reasons under the instructions the user's model
195
- // is actually working under.
196
- const systemPrompt = ctx.getSystemPrompt?.()
197
- if (systemPrompt) session.agent.state.systemPrompt = systemPrompt
198
-
199
- // The conversation itself. Pushed rather than assigned so the array the
200
- // session was built around stays the one it goes on using.
201
- session.agent.state.messages.push(...conversation.messages)
202
-
203
226
  // User text first, reminder after — the order Claude Code's attachment
204
227
  // renderer produces, where the reminder trails the message it is about.
205
228
  await session.prompt(`${message}\n\n${agentMentionReminder(type)}`)