@gotgenes/pi-subagents 6.18.1 → 6.18.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 +26 -0
- package/docs/architecture/architecture.md +22 -22
- package/docs/plans/0166-extract-parent-session-info.md +231 -0
- package/docs/plans/0180-reorder-append-prompt-for-kv-cache.md +100 -0
- package/docs/retro/0165-decompose-resolved-spawn-config.md +38 -0
- package/docs/retro/0166-extract-parent-session-info.md +66 -0
- package/docs/retro/0180-reorder-append-prompt-for-kv-cache.md +33 -0
- package/package.json +1 -1
- package/src/lifecycle/agent-manager.ts +14 -10
- package/src/lifecycle/agent-runner.ts +5 -6
- package/src/session/prompts.ts +11 -5
- package/src/tools/agent-tool.ts +4 -3
- package/src/tools/background-spawner.ts +3 -7
- package/src/tools/foreground-runner.ts +3 -5
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [6.18.3](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v6.18.2...pi-subagents-v6.18.3) (2026-05-24)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Performance Improvements
|
|
12
|
+
|
|
13
|
+
* reorder append-mode prompt for KV cache reuse ([#180](https://github.com/gotgenes/pi-packages/issues/180)) ([5f688bd](https://github.com/gotgenes/pi-packages/commit/5f688bd1d008e20987d28626c5f5d0df0f66b854))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Documentation
|
|
17
|
+
|
|
18
|
+
* plan reorder append-mode prompt for KV cache reuse ([#180](https://github.com/gotgenes/pi-packages/issues/180)) ([bb0ddec](https://github.com/gotgenes/pi-packages/commit/bb0ddec8a7beb37baace5698e4fa4d09e61497d6))
|
|
19
|
+
* **retro:** add planning stage notes for issue [#180](https://github.com/gotgenes/pi-packages/issues/180) ([3413158](https://github.com/gotgenes/pi-packages/commit/341315898baa09652df18731ad318c89861ec62c))
|
|
20
|
+
* **retro:** add retro notes for issue [#166](https://github.com/gotgenes/pi-packages/issues/166) ([fae30ce](https://github.com/gotgenes/pi-packages/commit/fae30cec3dd99bbac490a2764a8340aa12fc171c))
|
|
21
|
+
* **retro:** add TDD stage notes for issue [#180](https://github.com/gotgenes/pi-packages/issues/180) ([1560f2d](https://github.com/gotgenes/pi-packages/commit/1560f2d6f7029cbbe0cc7b1efe1aba2a243e8357))
|
|
22
|
+
|
|
23
|
+
## [6.18.2](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v6.18.1...pi-subagents-v6.18.2) (2026-05-24)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Documentation
|
|
27
|
+
|
|
28
|
+
* plan extract ParentSessionInfo from AgentSpawnConfig ([#166](https://github.com/gotgenes/pi-packages/issues/166)) ([aff7b35](https://github.com/gotgenes/pi-packages/commit/aff7b35c98503fbb3da6a287631a2aa5c4d498fd))
|
|
29
|
+
* **retro:** add planning stage notes for issue [#166](https://github.com/gotgenes/pi-packages/issues/166) ([6138473](https://github.com/gotgenes/pi-packages/commit/613847313d56a9df8b479726d831679391bd0c1a))
|
|
30
|
+
* **retro:** add retro notes for issue [#165](https://github.com/gotgenes/pi-packages/issues/165) ([2a3e70d](https://github.com/gotgenes/pi-packages/commit/2a3e70dc6b903b4c053e7f6ebc09169bc3e34bf6))
|
|
31
|
+
* **retro:** add TDD stage notes for issue [#166](https://github.com/gotgenes/pi-packages/issues/166) ([2696da5](https://github.com/gotgenes/pi-packages/commit/2696da599de72f1a881577a4de8fedc57472a695))
|
|
32
|
+
* update architecture doc — AgentSpawnConfig step 3 complete ([125450b](https://github.com/gotgenes/pi-packages/commit/125450ba9ea5753b4cad07ed4d1675dcdbc7e319))
|
|
33
|
+
|
|
8
34
|
## [6.18.1](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v6.18.0...pi-subagents-v6.18.1) (2026-05-24)
|
|
9
35
|
|
|
10
36
|
|
|
@@ -500,20 +500,20 @@ These are fire-and-forget broadcast events — no request IDs, no reply channels
|
|
|
500
500
|
These interfaces carry hidden dependencies that obscure true coupling.
|
|
501
501
|
Bags with 10+ fields are the highest priority for decomposition.
|
|
502
502
|
|
|
503
|
-
| Interface | Fields
|
|
504
|
-
| --------------------------- |
|
|
505
|
-
| `ResolvedSpawnConfig` |
|
|
506
|
-
| `AgentSpawnConfig` | 13
|
|
507
|
-
| `RunOptions` | 12
|
|
508
|
-
| `SessionConfig` | 11
|
|
509
|
-
| `NotificationDetails` | 10
|
|
510
|
-
| `ResourceLoaderOptions` | 10
|
|
511
|
-
| `RunnerIO` | 9 methods
|
|
512
|
-
| `CreateSessionOptions` | 9
|
|
513
|
-
| `AgentToolDeps` | 8
|
|
514
|
-
| `AgentMenuDeps` | 8
|
|
515
|
-
| `ConversationViewerOptions` | 8
|
|
516
|
-
| `AgentRecordInit` | 8
|
|
503
|
+
| Interface | Fields | Consumers | Severity |
|
|
504
|
+
| --------------------------- | ---------------------------------- | ------------------------------------------------- | -------- |
|
|
505
|
+
| `ResolvedSpawnConfig` | 3 nested | foreground-runner, background-spawner, agent-tool | ✓ done |
|
|
506
|
+
| `AgentSpawnConfig` | 13 → 13 (ParentSessionInfo nested) | agent-manager (internal) | ✓ done |
|
|
507
|
+
| `RunOptions` | 12 | agent-runner | High |
|
|
508
|
+
| `SessionConfig` | 11 | agent-runner (output of assembler) | High |
|
|
509
|
+
| `NotificationDetails` | 10 | notification | Medium |
|
|
510
|
+
| `ResourceLoaderOptions` | 10 | agent-runner (SDK bridge) | Medium |
|
|
511
|
+
| `RunnerIO` | 9 methods | agent-runner | Medium |
|
|
512
|
+
| `CreateSessionOptions` | 9 | agent-runner (SDK bridge) | Medium |
|
|
513
|
+
| `AgentToolDeps` | 8 | agent-tool | Low |
|
|
514
|
+
| `AgentMenuDeps` | 8 | agent-menu | Low |
|
|
515
|
+
| `ConversationViewerOptions` | 8 | conversation-viewer | Low |
|
|
516
|
+
| `AgentRecordInit` | 8 | agent-record | Low |
|
|
517
517
|
|
|
518
518
|
### Complexity hotspots
|
|
519
519
|
|
|
@@ -586,20 +586,20 @@ interface SpawnPresentation {
|
|
|
586
586
|
`agent-tool` uses all three to build the `AgentSpawnConfig` and the result text.
|
|
587
587
|
After decomposition, each consumer declares its real dependencies explicitly.
|
|
588
588
|
|
|
589
|
-
#### AgentSpawnConfig
|
|
589
|
+
#### AgentSpawnConfig — ParentSessionInfo extracted (done, [#166][166])
|
|
590
590
|
|
|
591
|
-
|
|
591
|
+
The `parentSessionFile`, `parentSessionId`, and `toolCallId` fields were grouped into `ParentSessionInfo`:
|
|
592
592
|
|
|
593
593
|
```typescript
|
|
594
|
-
/** Parent session identity — always travel together. */
|
|
595
|
-
interface ParentSessionInfo {
|
|
594
|
+
/** Parent session identity — always travel together from the tool boundary. */
|
|
595
|
+
export interface ParentSessionInfo {
|
|
596
596
|
parentSessionFile?: string;
|
|
597
597
|
parentSessionId?: string;
|
|
598
598
|
toolCallId?: string;
|
|
599
599
|
}
|
|
600
600
|
```
|
|
601
601
|
|
|
602
|
-
|
|
602
|
+
`AgentSpawnConfig` now carries `parentSession?: ParentSessionInfo` instead of three flat optional fields.
|
|
603
603
|
|
|
604
604
|
#### RunOptions (12 fields → extract RunContext)
|
|
605
605
|
|
|
@@ -676,10 +676,10 @@ Split the 15-field bag into `SpawnIdentity`, `SpawnExecution`, and `SpawnPresent
|
|
|
676
676
|
Each consumer declares its real dependencies.
|
|
677
677
|
Enables Step 3 (narrowing AgentSpawnConfig, [#166][166]).
|
|
678
678
|
|
|
679
|
-
### Step 3: Extract ParentSessionInfo from AgentSpawnConfig ([#166][166])
|
|
679
|
+
### Step 3: Extract ParentSessionInfo from AgentSpawnConfig ([#166][166]) — Complete
|
|
680
680
|
|
|
681
|
-
|
|
682
|
-
|
|
681
|
+
Extracted `parentSessionFile`, `parentSessionId`, `toolCallId` into `ParentSessionInfo`.
|
|
682
|
+
`AgentSpawnConfig`, `BackgroundParams`, `ForegroundParams`, and `RunOptions` all carry the nested group.
|
|
683
683
|
|
|
684
684
|
### Step 4: Narrow RunnerIO ([#167][167])
|
|
685
685
|
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 166
|
|
3
|
+
issue_title: "refactor(pi-subagents): extract ParentSessionInfo from AgentSpawnConfig (13 fields)"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Extract ParentSessionInfo from AgentSpawnConfig
|
|
7
|
+
|
|
8
|
+
## Problem Statement
|
|
9
|
+
|
|
10
|
+
`AgentSpawnConfig` in `agent-manager.ts` has 13 fields.
|
|
11
|
+
Three of those fields — `parentSessionFile`, `parentSessionId`, and `toolCallId` — form a natural cluster around parent session identity.
|
|
12
|
+
They always travel together through `agent-tool.ts` → `foreground-runner.ts` / `background-spawner.ts` → `AgentManager.spawn()`.
|
|
13
|
+
Extracting them into a named value object reduces `AgentSpawnConfig` from 13 to 11 fields (10 + 1 nested) and introduces the `ParentSessionInfo` domain concept.
|
|
14
|
+
|
|
15
|
+
## Goals
|
|
16
|
+
|
|
17
|
+
- Extract `ParentSessionInfo` interface with `parentSessionFile`, `parentSessionId`, and `toolCallId`.
|
|
18
|
+
- Replace the three flat optional fields on `AgentSpawnConfig` with a single optional `parentSession?: ParentSessionInfo` field.
|
|
19
|
+
- Replace the flat fields on `BackgroundParams`, `ForegroundParams`, and `RunOptions` with the same grouped type.
|
|
20
|
+
- Update all callers (agent-tool, foreground-runner, background-spawner, agent-manager, agent-runner) and their tests.
|
|
21
|
+
- Non-breaking refactor — no public API changes (the `SubagentsService` boundary does not expose these fields).
|
|
22
|
+
|
|
23
|
+
## Non-Goals
|
|
24
|
+
|
|
25
|
+
- Changing the `NotificationState` or `notification` module — they remain as-is; `toolCallId` is just extracted from the group at the `AgentManager.spawn` boundary.
|
|
26
|
+
- Further decomposition of `AgentSpawnConfig` (e.g., extracting execution or callback clusters) — tracked separately.
|
|
27
|
+
- Modifying `session-dir.ts` or `deriveSubagentSessionDir` — the function signature stays the same; callers just unwrap `parentSession.parentSessionFile` before calling it.
|
|
28
|
+
|
|
29
|
+
## Background
|
|
30
|
+
|
|
31
|
+
Issue #165 (closed) decomposed `ResolvedSpawnConfig` into `SpawnIdentity`, `SpawnExecution`, and `SpawnPresentation`.
|
|
32
|
+
This issue continues that structural improvement by grouping the parent-session fields that flow from `agent-tool.ts` through to `agent-runner.ts`.
|
|
33
|
+
|
|
34
|
+
The three fields are:
|
|
35
|
+
|
|
36
|
+
- `parentSessionFile` — path to the parent session's JSONL file, used by `deriveSubagentSessionDir` to place child sessions next to the parent.
|
|
37
|
+
- `parentSessionId` — session ID of the parent agent, stored in the child session's `parentSession` header via `sessionManager.newSession()`.
|
|
38
|
+
- `toolCallId` — tool call ID for background notification wiring; when set, `AgentManager.spawn` creates a `NotificationState`.
|
|
39
|
+
|
|
40
|
+
All three originate in `agent-tool.ts`'s `execute` function and are threaded unchanged through intermediate modules.
|
|
41
|
+
|
|
42
|
+
### Current flow
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
agent-tool execute → getSessionInfo() + toolCallId param
|
|
46
|
+
→ BackgroundParams { parentSessionFile, parentSessionId, toolCallId }
|
|
47
|
+
→ spawnBackground → manager.spawn(opts: AgentSpawnConfig { ...flat fields })
|
|
48
|
+
→ AgentManager.spawn → options.toolCallId → NotificationState
|
|
49
|
+
→ startAgent → runner.run(RunOptions { parentSessionFile, parentSessionId })
|
|
50
|
+
→ deriveSessionDir(parentSessionFile, ...)
|
|
51
|
+
→ sessionManager.newSession({ parentSession: parentSessionId })
|
|
52
|
+
|
|
53
|
+
→ ForegroundParams { parentSessionFile, parentSessionId }
|
|
54
|
+
→ runForeground → manager.spawnAndWait(opts: AgentSpawnConfig { ...flat fields })
|
|
55
|
+
→ (same AgentManager path)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### After extraction
|
|
59
|
+
|
|
60
|
+
```text
|
|
61
|
+
agent-tool execute → getSessionInfo() + toolCallId param
|
|
62
|
+
→ parentSession: ParentSessionInfo { parentSessionFile, parentSessionId, toolCallId }
|
|
63
|
+
→ BackgroundParams { parentSession }
|
|
64
|
+
→ spawnBackground → manager.spawn(opts: AgentSpawnConfig { parentSession })
|
|
65
|
+
→ AgentManager.spawn → parentSession.toolCallId → NotificationState
|
|
66
|
+
→ startAgent → runner.run(RunOptions { parentSession })
|
|
67
|
+
→ deriveSessionDir(parentSession?.parentSessionFile, ...)
|
|
68
|
+
→ sessionManager.newSession({ parentSession: parentSession?.parentSessionId })
|
|
69
|
+
|
|
70
|
+
→ ForegroundParams { parentSession }
|
|
71
|
+
→ runForeground → manager.spawnAndWait(opts: AgentSpawnConfig { parentSession })
|
|
72
|
+
→ (same AgentManager path)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Design Overview
|
|
76
|
+
|
|
77
|
+
### `ParentSessionInfo` interface
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
export interface ParentSessionInfo {
|
|
81
|
+
/** Path to the parent session's JSONL file (for deriving the subagent session directory). */
|
|
82
|
+
parentSessionFile?: string;
|
|
83
|
+
/** Session ID of the parent agent (stored in the child session's parentSession header). */
|
|
84
|
+
parentSessionId?: string;
|
|
85
|
+
/** Tool call ID for background notification wiring. When set, spawn attaches NotificationState. */
|
|
86
|
+
toolCallId?: string;
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
All three fields remain optional — they are only available when spawning from an active session (the `SubagentsService` boundary omits them entirely).
|
|
91
|
+
|
|
92
|
+
The interface lives in `lifecycle/agent-manager.ts` alongside `AgentSpawnConfig` since that is the primary consumer.
|
|
93
|
+
If a future refactoring moves `AgentSpawnConfig` to its own file, `ParentSessionInfo` should move with it.
|
|
94
|
+
|
|
95
|
+
### Consumer call-site sketch
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
// agent-tool.ts execute():
|
|
99
|
+
const parentSession: ParentSessionInfo = {
|
|
100
|
+
parentSessionFile: sessionInfo.parentSessionFile,
|
|
101
|
+
parentSessionId: sessionInfo.parentSessionId,
|
|
102
|
+
toolCallId,
|
|
103
|
+
};
|
|
104
|
+
// ...
|
|
105
|
+
spawnBackground(manager, widget, agentActivity, { config, snapshot, parentSession });
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The grouped object eliminates the three-field spread that was repeated in both `spawnBackground` and `runForeground` call sites.
|
|
109
|
+
|
|
110
|
+
### `AgentSpawnConfig` change
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
export interface AgentSpawnConfig {
|
|
114
|
+
// ... existing fields (description, model, maxTurns, etc.)
|
|
115
|
+
/** Parent session identity — grouped fields that travel together from the tool boundary. */
|
|
116
|
+
parentSession?: ParentSessionInfo;
|
|
117
|
+
// Remove: parentSessionFile, parentSessionId, toolCallId
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### `RunOptions` change
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
export interface RunOptions {
|
|
125
|
+
// ... existing fields
|
|
126
|
+
/** Parent session identity (file path + session ID). */
|
|
127
|
+
parentSession?: ParentSessionInfo;
|
|
128
|
+
// Remove: parentSessionFile, parentSessionId
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Note: `RunOptions` does not use `toolCallId` — it was never threaded to the runner.
|
|
133
|
+
The runner only reads `parentSessionFile` and `parentSessionId` from the group.
|
|
134
|
+
|
|
135
|
+
### `getSessionInfo` return type update
|
|
136
|
+
|
|
137
|
+
The `getSessionInfo` callback in `AgentToolDeps` currently returns `{ parentSessionFile: string; parentSessionId: string }`.
|
|
138
|
+
It should remain unchanged — it does not include `toolCallId` (which comes from the `execute` callback's first argument).
|
|
139
|
+
The `agent-tool.ts` execute function constructs a `ParentSessionInfo` by merging `getSessionInfo()` output with `toolCallId`.
|
|
140
|
+
|
|
141
|
+
## Module-Level Changes
|
|
142
|
+
|
|
143
|
+
### New types
|
|
144
|
+
|
|
145
|
+
1. `src/lifecycle/agent-manager.ts` — add `ParentSessionInfo` interface (exported).
|
|
146
|
+
|
|
147
|
+
### Modified interfaces
|
|
148
|
+
|
|
149
|
+
1. `src/lifecycle/agent-manager.ts` — `AgentSpawnConfig`: replace `parentSessionFile?`, `parentSessionId?`, `toolCallId?` with `parentSession?: ParentSessionInfo`.
|
|
150
|
+
2. `src/lifecycle/agent-runner.ts` — `RunOptions`: replace `parentSessionFile?`, `parentSessionId?` with `parentSession?: ParentSessionInfo`.
|
|
151
|
+
3. `src/tools/background-spawner.ts` — `BackgroundParams`: replace `parentSessionFile`, `parentSessionId`, `toolCallId` with `parentSession: ParentSessionInfo`.
|
|
152
|
+
4. `src/tools/foreground-runner.ts` — `ForegroundParams`: replace `parentSessionFile`, `parentSessionId` with `parentSession: ParentSessionInfo`.
|
|
153
|
+
|
|
154
|
+
### Modified implementations
|
|
155
|
+
|
|
156
|
+
1. `src/lifecycle/agent-manager.ts` — `AgentManager.spawn()`: read `options.parentSession?.toolCallId` instead of `options.toolCallId`; pass `parentSession` to `RunOptions`.
|
|
157
|
+
2. `src/lifecycle/agent-runner.ts` — `runAgent()`: read `options.parentSession?.parentSessionFile` and `options.parentSession?.parentSessionId`.
|
|
158
|
+
3. `src/tools/agent-tool.ts` — `createAgentTool` execute: construct `ParentSessionInfo` from `getSessionInfo()` + `toolCallId`, pass as `parentSession` to both spawners.
|
|
159
|
+
4. `src/tools/background-spawner.ts` — `spawnBackground()`: read `params.parentSession` and pass fields to `AgentSpawnConfig`.
|
|
160
|
+
5. `src/tools/foreground-runner.ts` — `runForeground()`: read `params.parentSession` and pass fields to `AgentSpawnConfig`.
|
|
161
|
+
|
|
162
|
+
### No changes needed
|
|
163
|
+
|
|
164
|
+
- `src/tools/agent-tool.ts` — `AgentToolDeps.getSessionInfo` return type stays the same.
|
|
165
|
+
- `src/session/session-dir.ts` — `deriveSubagentSessionDir` signature unchanged.
|
|
166
|
+
- `src/observation/notification-state.ts` — constructor signature unchanged.
|
|
167
|
+
- `src/service/service-adapter.ts` — does not pass parent session fields.
|
|
168
|
+
- `src/index.ts` — `getSessionInfo` callback unchanged.
|
|
169
|
+
|
|
170
|
+
## Test Impact Analysis
|
|
171
|
+
|
|
172
|
+
### New tests enabled
|
|
173
|
+
|
|
174
|
+
No new unit tests are enabled — this is a structural grouping, not new behavior.
|
|
175
|
+
|
|
176
|
+
### Existing tests that need updates
|
|
177
|
+
|
|
178
|
+
1. `test/lifecycle/agent-manager.test.ts` — update spawn calls from flat `parentSessionFile`/`parentSessionId`/`toolCallId` to nested `parentSession: { ... }` form; update assertions to read from `parentSession`.
|
|
179
|
+
2. `test/lifecycle/agent-runner.test.ts` — update `RunOptions` construction from flat to nested `parentSession`.
|
|
180
|
+
3. `test/tools/agent-tool.test.ts` — update assertion checking `toolCallId` on spawn opts to check `parentSession.toolCallId`.
|
|
181
|
+
4. `test/tools/background-spawner.test.ts` — update `makeParams` factory from flat fields to `parentSession: { ... }`.
|
|
182
|
+
5. `test/tools/foreground-runner.test.ts` — update params construction from flat fields to `parentSession: { ... }`.
|
|
183
|
+
6. `test/helpers/make-deps.ts` — `getSessionInfo` mock stays unchanged (returns flat `{ parentSessionFile, parentSessionId }`).
|
|
184
|
+
|
|
185
|
+
### Tests that stay as-is
|
|
186
|
+
|
|
187
|
+
- `test/session/session-dir.test.ts` — tests `deriveSubagentSessionDir` directly, no interface change.
|
|
188
|
+
- `test/observation/notification-state.test.ts` — tests `NotificationState` constructor directly.
|
|
189
|
+
- `test/observation/notification.test.ts` — tests notification formatting with `record.notification`, not spawn config.
|
|
190
|
+
|
|
191
|
+
## TDD Order
|
|
192
|
+
|
|
193
|
+
1. **Define `ParentSessionInfo` and update `AgentSpawnConfig`** — add interface, replace three flat fields with `parentSession?`.
|
|
194
|
+
Update `AgentManager.spawn` and `startAgent` to read from the nested group.
|
|
195
|
+
Update `agent-manager.test.ts` to use nested form.
|
|
196
|
+
Run `pnpm run check` to verify no downstream type errors remain.
|
|
197
|
+
Commit: `refactor: define ParentSessionInfo and nest in AgentSpawnConfig`
|
|
198
|
+
|
|
199
|
+
2. **Update `RunOptions` in `agent-runner.ts`** — replace flat `parentSessionFile?`/`parentSessionId?` with `parentSession?`.
|
|
200
|
+
Update `runAgent` to read `options.parentSession?.parentSessionFile` and `options.parentSession?.parentSessionId`.
|
|
201
|
+
Update `agent-runner.test.ts`.
|
|
202
|
+
Commit: `refactor: nest ParentSessionInfo in RunOptions`
|
|
203
|
+
|
|
204
|
+
3. **Update `BackgroundParams` and `spawnBackground`** — replace three flat fields with `parentSession: ParentSessionInfo`.
|
|
205
|
+
Update `spawnBackground` to pass `parentSession` to spawn opts.
|
|
206
|
+
Update `background-spawner.test.ts`.
|
|
207
|
+
Commit: `refactor: nest ParentSessionInfo in BackgroundParams`
|
|
208
|
+
|
|
209
|
+
4. **Update `ForegroundParams` and `runForeground`** — replace two flat fields with `parentSession: ParentSessionInfo`.
|
|
210
|
+
Update `runForeground` to pass `parentSession` to spawn opts.
|
|
211
|
+
Update `foreground-runner.test.ts`.
|
|
212
|
+
Commit: `refactor: nest ParentSessionInfo in ForegroundParams`
|
|
213
|
+
|
|
214
|
+
5. **Update `agent-tool.ts` execute** — construct `ParentSessionInfo` from `getSessionInfo()` + `toolCallId`, pass as `parentSession` to both spawner call sites.
|
|
215
|
+
Update `agent-tool.test.ts`.
|
|
216
|
+
Commit: `refactor: construct ParentSessionInfo in agent-tool execute`
|
|
217
|
+
|
|
218
|
+
6. **Final verification** — run full test suite (`pnpm vitest run`) and type check (`pnpm run check`).
|
|
219
|
+
No separate commit unless adjustments are needed.
|
|
220
|
+
|
|
221
|
+
## Risks and Mitigations
|
|
222
|
+
|
|
223
|
+
| Risk | Mitigation |
|
|
224
|
+
| ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
225
|
+
| Deep-merge trap: test factories using `Partial<BackgroundParams>` spread may silently ignore nested overrides | Audit `makeParams` in `background-spawner.test.ts` — convert from flat-field spread to nested `parentSession` construction |
|
|
226
|
+
| `toolCallId` conditionally absent for foreground calls | `ParentSessionInfo.toolCallId` is optional; `ForegroundParams.parentSession` includes it but won't set it — matches current behavior |
|
|
227
|
+
| Type check passes but runtime breaks due to nested access on undefined | `parentSession?` is optional on `AgentSpawnConfig`; all reads use optional chaining (`options.parentSession?.toolCallId`) |
|
|
228
|
+
|
|
229
|
+
## Open Questions
|
|
230
|
+
|
|
231
|
+
None — the extraction is mechanical and the issue description is unambiguous.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 180
|
|
3
|
+
issue_title: "perf(pi-subagents): reorder append-mode system prompt to enable KV cache reuse"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Reorder append-mode system prompt for KV cache reuse
|
|
7
|
+
|
|
8
|
+
## Problem Statement
|
|
9
|
+
|
|
10
|
+
In append mode, `buildAgentPrompt()` places varying, agent-specific content (the `<active_agent>` tag and env block) *before* the large shared inherited system prompt (~8k tokens).
|
|
11
|
+
LLM KV caching works on prefixes — the cache is only reusable when the beginning of the prompt matches.
|
|
12
|
+
Every subagent spawn reprocesses the entire inherited prompt from scratch because the prefix differs per agent.
|
|
13
|
+
|
|
14
|
+
## Goals
|
|
15
|
+
|
|
16
|
+
- Reorder the append-mode system prompt so shared/stable content comes first and varying content follows.
|
|
17
|
+
- Preserve the `<active_agent>` tag at any position — pi-permission-system's `ACTIVE_AGENT_TAG_REGEX.exec()` searches the full string.
|
|
18
|
+
- Keep replace-mode prompt ordering unchanged (it has no shared inherited content to cache).
|
|
19
|
+
- Update tests and JSDoc to reflect the new ordering.
|
|
20
|
+
|
|
21
|
+
## Non-Goals
|
|
22
|
+
|
|
23
|
+
- Changing replace-mode prompt assembly (no shared prefix to cache).
|
|
24
|
+
- Modifying pi-permission-system (its regex parsing is already position-independent).
|
|
25
|
+
- Changing the *content* of any prompt section — only reordering.
|
|
26
|
+
|
|
27
|
+
## Background
|
|
28
|
+
|
|
29
|
+
`buildAgentPrompt()` in `src/session/prompts.ts` assembles the system prompt for subagents.
|
|
30
|
+
In append mode, the current ordering is:
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
1. <active_agent name="${name}"/> ← VARIES per agent
|
|
34
|
+
2. # Environment ... ← VARIES per runtime
|
|
35
|
+
3. <inherited_system_prompt> ← SHARED (~8k tokens)
|
|
36
|
+
4. <sub_agent_context> ← SHARED (static)
|
|
37
|
+
5. <agent_instructions> ← VARIES per agent
|
|
38
|
+
6. memory / skills ← VARIES
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
pi-permission-system's `getActiveAgentNameFromSystemPrompt()` in `src/active-agent.ts` uses `ACTIVE_AGENT_TAG_REGEX.exec(systemPrompt)` — a regex search that finds the tag at any position, confirmed by reading the source.
|
|
42
|
+
|
|
43
|
+
## Design Overview
|
|
44
|
+
|
|
45
|
+
Move shared/stable sections to the front of the append-mode prompt:
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
1. <inherited_system_prompt> ← SHARED (~8k tokens, NOW CACHEABLE)
|
|
49
|
+
2. <sub_agent_context> ← SHARED (static)
|
|
50
|
+
3. <active_agent name="${name}"/> ← VARIES (after cached prefix)
|
|
51
|
+
4. # Environment ... ← VARIES
|
|
52
|
+
5. <agent_instructions> ← VARIES per agent
|
|
53
|
+
6. memory / skills ← VARIES
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
This is a pure reordering — no content changes.
|
|
57
|
+
The `<active_agent>` tag remains in the system prompt for pi-permission-system to find via regex.
|
|
58
|
+
The env block and agent instructions still provide context to the model; their position relative to the inherited prompt is not semantically significant.
|
|
59
|
+
|
|
60
|
+
## Module-Level Changes
|
|
61
|
+
|
|
62
|
+
### `src/session/prompts.ts`
|
|
63
|
+
|
|
64
|
+
1. Reorder the return statement in the `config.promptMode === "append"` branch to place `identity` (wrapped in `<inherited_system_prompt>`) and `bridge` before `activeAgentTag` and `envBlock`.
|
|
65
|
+
2. Update the JSDoc comment on `buildAgentPrompt()` — replace "Both modes prepend" language with a description that notes the tag is included (not necessarily prepended) in append mode.
|
|
66
|
+
|
|
67
|
+
### `test/session/prompts.test.ts`
|
|
68
|
+
|
|
69
|
+
1. Update "prepends `<active_agent>` tag in append mode" — change from asserting `prompt.startsWith()` to asserting the tag appears *after* the inherited system prompt.
|
|
70
|
+
2. Update "active_agent tag appears before envBlock in both modes" — the append-mode assertions change: the tag should still appear before the env block, but no longer at index 0.
|
|
71
|
+
The replace-mode assertions remain unchanged (`tagIdx === 0`).
|
|
72
|
+
|
|
73
|
+
## Test Impact Analysis
|
|
74
|
+
|
|
75
|
+
- Two existing tests assert `<active_agent>` is prepended (index 0) in append mode — these must change to assert the new ordering.
|
|
76
|
+
- All other prompt tests use `toContain()` and are position-independent — they pass without changes.
|
|
77
|
+
- No new test files or test surfaces are needed; the existing test suite covers the reordering adequately once the positional assertions are updated.
|
|
78
|
+
|
|
79
|
+
## TDD Order
|
|
80
|
+
|
|
81
|
+
1. **Red: update positional assertions for append mode.**
|
|
82
|
+
Change the two append-mode tests to assert the new ordering: `<inherited_system_prompt>` appears before `<active_agent>`, and the tag appears before the env block but not at index 0.
|
|
83
|
+
Commit: `test: assert cache-friendly prompt ordering in append mode (#180)`
|
|
84
|
+
|
|
85
|
+
2. **Green: reorder the append-mode return statement.**
|
|
86
|
+
Move `identity` + `<inherited_system_prompt>` wrapper and `bridge` before `activeAgentTag` + `envBlock` in the return expression.
|
|
87
|
+
Update the JSDoc on `buildAgentPrompt()`.
|
|
88
|
+
Commit: `perf: reorder append-mode prompt for KV cache reuse (#180)`
|
|
89
|
+
|
|
90
|
+
## Risks and Mitigations
|
|
91
|
+
|
|
92
|
+
| Risk | Mitigation |
|
|
93
|
+
| -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
94
|
+
| pi-permission-system depends on tag position | Confirmed `ACTIVE_AGENT_TAG_REGEX.exec()` searches the full string — position-independent. |
|
|
95
|
+
| Model behavior changes with reordered prompt | The same content is present; only ordering changes. The inherited system prompt as the "base" followed by specialization is arguably more natural. |
|
|
96
|
+
| Replace mode accidentally affected | Replace mode has its own code path and is not touched by this change. |
|
|
97
|
+
|
|
98
|
+
## Open Questions
|
|
99
|
+
|
|
100
|
+
None — the design is straightforward and confirmed safe by code inspection.
|
|
@@ -38,3 +38,41 @@ The decomposition touched 7 files (4 source, 3 test) and kept the test count fla
|
|
|
38
38
|
- Step 1 breaking all consumers simultaneously was handled smoothly by completing all steps before pushing, as planned.
|
|
39
39
|
No transitional alias was needed.
|
|
40
40
|
- The `background-spawner.test.ts` description-override test was the only unexpected friction point — the flat spread issue wasn't caught by the plan.
|
|
41
|
+
|
|
42
|
+
## Stage: Final Retrospective (2026-05-24T15:00:14Z)
|
|
43
|
+
|
|
44
|
+
### Session summary
|
|
45
|
+
|
|
46
|
+
Shipped issue #165 (CI green, released as `pi-subagents-v6.18.1`) and ran the final retrospective.
|
|
47
|
+
The most impactful outcome across all three sessions was the skill description improvements (commit `51f52ef`), which addressed a recurring `instruction-violation` pattern.
|
|
48
|
+
|
|
49
|
+
### Observations
|
|
50
|
+
|
|
51
|
+
#### What went well
|
|
52
|
+
|
|
53
|
+
- The user's probing question ("This is consistent, though.
|
|
54
|
+
Why?") turned a simple skill-loading skip into a generalizable improvement to three skill descriptions and two prompt instructions.
|
|
55
|
+
This is a good example of the user investing a redirecting question instead of a correction.
|
|
56
|
+
- TDD execution was clean — 4 cycles, no rework, no type errors at the end.
|
|
57
|
+
The plan's risk mitigation ("land steps 1–4 on the same branch") worked as intended.
|
|
58
|
+
- Ship stage had zero friction: push, CI, close, release-please merge, tag — all first-try.
|
|
59
|
+
|
|
60
|
+
#### What caused friction (agent side)
|
|
61
|
+
|
|
62
|
+
- `instruction-violation` — Skipped loading the `colgrep` skill during planning despite explicit instructions.
|
|
63
|
+
Root cause: skill descriptions that read like tool reference manuals get deprioritized because the agent perceives them as redundant with the tool schema already in context.
|
|
64
|
+
Impact: no rework on the plan itself, but triggered a productive detour to improve skill descriptions.
|
|
65
|
+
User-caught.
|
|
66
|
+
- `missing-context` — The plan didn't anticipate that `Partial<ResolvedSpawnConfig>` spread in test factories would silently break after nesting.
|
|
67
|
+
The `testing` skill already warns about spread-related pitfalls, but not this specific variant (flat keys ignored by top-level spread on a nested structure).
|
|
68
|
+
Impact: one test failure during step 4 that required a verbose inline fix (writing out the full `execution` sub-object).
|
|
69
|
+
Self-identified during implementation.
|
|
70
|
+
|
|
71
|
+
#### What caused friction (user side)
|
|
72
|
+
|
|
73
|
+
- None observed.
|
|
74
|
+
The user's intervention on the `colgrep` skill was well-timed and produced a higher-value outcome than skipping it would have.
|
|
75
|
+
|
|
76
|
+
### Changes made
|
|
77
|
+
|
|
78
|
+
1. Added a TDD planning rule to `.pi/skills/testing/SKILL.md` warning about `Partial<T>` spread not deep-merging into nested interfaces after a flat-to-nested refactor.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 166
|
|
3
|
+
issue_title: "refactor(pi-subagents): extract ParentSessionInfo from AgentSpawnConfig (13 fields)"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Retro: #166 — Extract ParentSessionInfo from AgentSpawnConfig
|
|
7
|
+
|
|
8
|
+
## Stage: Planning (2026-05-24T16:00:00Z)
|
|
9
|
+
|
|
10
|
+
### Session summary
|
|
11
|
+
|
|
12
|
+
Produced a 6-step TDD plan to extract `ParentSessionInfo` from `AgentSpawnConfig`.
|
|
13
|
+
The refactoring groups three co-traveling fields (`parentSessionFile`, `parentSessionId`, `toolCallId`) into a named value object, reducing `AgentSpawnConfig` from 13 to 11 fields.
|
|
14
|
+
|
|
15
|
+
### Observations
|
|
16
|
+
|
|
17
|
+
- The `SubagentsService` boundary (`service-adapter.ts`) does not pass any of the three fields, so this is a purely internal refactoring with no public API impact.
|
|
18
|
+
- `getSessionInfo` in `AgentToolDeps` returns only `parentSessionFile` and `parentSessionId`; `toolCallId` comes from the `execute` callback's first argument — the plan keeps this separation and merges them at the `agent-tool.ts` boundary.
|
|
19
|
+
- `RunOptions` in `agent-runner.ts` never carried `toolCallId` (it was consumed in `AgentManager.spawn` before reaching the runner), so the nested `parentSession` on `RunOptions` only holds the two session fields.
|
|
20
|
+
- The deep-merge trap from the testing skill is relevant: `background-spawner.test.ts` has a `makeParams` factory that spreads flat fields — must be converted to nested `parentSession` construction.
|
|
21
|
+
- Issue #165 (decompose `ResolvedSpawnConfig`) is closed, so this plan builds on stable ground.
|
|
22
|
+
|
|
23
|
+
## Stage: Implementation — TDD (2026-05-24T17:00:00Z)
|
|
24
|
+
|
|
25
|
+
### Session summary
|
|
26
|
+
|
|
27
|
+
All 5 TDD cycles completed across `agent-manager.ts`, `agent-runner.ts`, `background-spawner.ts`, `foreground-runner.ts`, and `agent-tool.ts`.
|
|
28
|
+
Test count held steady at 805 (no net new tests — refactor only).
|
|
29
|
+
Type check and lint both clean after all steps.
|
|
30
|
+
|
|
31
|
+
### Observations
|
|
32
|
+
|
|
33
|
+
- The `AgentSpawnConfig` field count went from 15 to 13 (not 13 → 10 as originally estimated) — the architecture doc quoted the issue's stale count; the actual pre-refactor interface had 15 fields (`bypassQueue` and others were already present).
|
|
34
|
+
The architecture doc was updated to reflect "done" with a note about the nested group rather than a specific before/after number.
|
|
35
|
+
- The deep-merge trap (noted in planning) did materialise: `background-spawner.test.ts`'s `makeParams` spread `Partial<BackgroundParams>` with flat fields.
|
|
36
|
+
Fixed by replacing the three flat fields with a single `parentSession` object at the factory level — top-level spread still works correctly since `parentSession` is one field.
|
|
37
|
+
- `RunOptions` in `agent-runner.ts` needed a new import of `ParentSessionInfo` from `agent-manager.ts`; no circular dependency since `agent-runner.ts` already imports from `agent-manager.ts`.
|
|
38
|
+
- `agent-tool.ts` still imports `AgentSpawnConfig` (needed by `AgentToolManager` interface) — the new `ParentSessionInfo` import was added alongside it.
|
|
39
|
+
- All 5 commits are clean `refactor:` messages; architecture doc update is a separate `docs:` commit.
|
|
40
|
+
|
|
41
|
+
## Stage: Final Retrospective (2026-05-24T18:00:00Z)
|
|
42
|
+
|
|
43
|
+
### Session summary
|
|
44
|
+
|
|
45
|
+
Planning, TDD implementation (5 steps), shipping, and CI verification all completed in a single session.
|
|
46
|
+
Released as `pi-subagents-v6.18.2`.
|
|
47
|
+
Zero rework — every TDD step went green on first attempt.
|
|
48
|
+
|
|
49
|
+
### Observations
|
|
50
|
+
|
|
51
|
+
#### What went well
|
|
52
|
+
|
|
53
|
+
- The planning session's identification of the deep-merge trap in `background-spawner.test.ts`'s `makeParams` factory paid off — the TDD implementation handled it without friction because the risk was anticipated.
|
|
54
|
+
- The 5-step inside-out TDD order (manager → runner → background → foreground → agent-tool) was the right sequence.
|
|
55
|
+
Each step only introduced type errors in files that subsequent steps would fix, with no circular breakage.
|
|
56
|
+
- Clean mechanical execution — 805 tests before and after, zero rework commits, lint and type-check clean throughout.
|
|
57
|
+
|
|
58
|
+
#### What caused friction (agent side)
|
|
59
|
+
|
|
60
|
+
- `missing-context` — The plan repeated the issue body's stale "13 fields" count without verifying against the actual `AgentSpawnConfig` interface (which had 15 fields after `bypassQueue` was added in a prior issue).
|
|
61
|
+
The plan also inconsistently claimed the extraction would reduce the count to both "11" and "10" in different places.
|
|
62
|
+
Impact: required corrections in the architecture doc update, but no implementation rework.
|
|
63
|
+
|
|
64
|
+
#### What caused friction (user side)
|
|
65
|
+
|
|
66
|
+
- None observed — the user let the session run autonomously through all stages without intervention.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 180
|
|
3
|
+
issue_title: "perf(pi-subagents): reorder append-mode system prompt to enable KV cache reuse"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Retro: #180 — Reorder append-mode system prompt for KV cache reuse
|
|
7
|
+
|
|
8
|
+
## Stage: Planning (2026-05-24T20:00:00Z)
|
|
9
|
+
|
|
10
|
+
### Session summary
|
|
11
|
+
|
|
12
|
+
Produced a plan to reorder the append-mode system prompt in `buildAgentPrompt()` so the shared inherited content (~8k tokens) comes before the varying `<active_agent>` tag and env block, enabling LLM KV cache prefix reuse across subagent invocations.
|
|
13
|
+
|
|
14
|
+
### Observations
|
|
15
|
+
|
|
16
|
+
- Confirmed pi-permission-system's `ACTIVE_AGENT_TAG_REGEX.exec()` is position-independent — no changes needed in that package despite the `pkg:pi-permission-system` label on the issue.
|
|
17
|
+
- Only two tests assert positional ordering in append mode (`startsWith` and `tagIdx === 0`); all other prompt tests use `toContain()` and are unaffected.
|
|
18
|
+
- Replace mode is a separate code path and is not touched.
|
|
19
|
+
- The TDD cycle is minimal: one red step (update two positional assertions), one green step (reorder the return statement + update JSDoc).
|
|
20
|
+
|
|
21
|
+
## Stage: Implementation — TDD (2026-05-24T20:15:00Z)
|
|
22
|
+
|
|
23
|
+
### Session summary
|
|
24
|
+
|
|
25
|
+
Completed both TDD cycles in `buildAgentPrompt()` in `src/session/prompts.ts`.
|
|
26
|
+
Two positional assertions in `test/session/prompts.test.ts` were updated to expect the new ordering (red), then the append-mode return statement was reordered and the JSDoc updated (green).
|
|
27
|
+
Test count unchanged at 805 across 50 files.
|
|
28
|
+
|
|
29
|
+
### Observations
|
|
30
|
+
|
|
31
|
+
- The JSDoc bullet for append mode also described the old ordering ("env header + parent system prompt + ...") and was corrected as part of the green step.
|
|
32
|
+
- The `<active_agent>` tag is followed by a `\n\n`, so when it moves after `<sub_agent_context>`, a `\n\n` separator between the bridge and the tag was needed to maintain clean section boundaries.
|
|
33
|
+
- No deviations from the plan; both steps were exactly as described.
|
package/package.json
CHANGED
|
@@ -53,6 +53,15 @@ interface SpawnArgs {
|
|
|
53
53
|
options: AgentSpawnConfig;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
export interface ParentSessionInfo {
|
|
57
|
+
/** Path to the parent session's JSONL file (for deriving the subagent session directory). */
|
|
58
|
+
parentSessionFile?: string;
|
|
59
|
+
/** Session ID of the parent agent (stored in the child session's parentSession header). */
|
|
60
|
+
parentSessionId?: string;
|
|
61
|
+
/** Tool call ID for background notification wiring. When set, spawn attaches NotificationState. */
|
|
62
|
+
toolCallId?: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
56
65
|
export interface AgentSpawnConfig {
|
|
57
66
|
description: string;
|
|
58
67
|
model?: Model<any>;
|
|
@@ -75,12 +84,8 @@ export interface AgentSpawnConfig {
|
|
|
75
84
|
signal?: AbortSignal;
|
|
76
85
|
/** Called when the agent session is created — receives the session and the agent's record. */
|
|
77
86
|
onSessionCreated?: (session: AgentSession, record: AgentRecord) => void;
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
/** Session ID of the parent agent (stored in the child session's parentSession header). */
|
|
81
|
-
parentSessionId?: string;
|
|
82
|
-
/** Tool call ID for background notification wiring. When set, spawn attaches NotificationState. */
|
|
83
|
-
toolCallId?: string;
|
|
87
|
+
/** Parent session identity — grouped fields that travel together from the tool boundary. */
|
|
88
|
+
parentSession?: ParentSessionInfo;
|
|
84
89
|
}
|
|
85
90
|
|
|
86
91
|
export class AgentManager {
|
|
@@ -158,8 +163,8 @@ export class AgentManager {
|
|
|
158
163
|
});
|
|
159
164
|
this.agents.set(id, record);
|
|
160
165
|
|
|
161
|
-
if (options.toolCallId) {
|
|
162
|
-
record.notification = new NotificationState(options.toolCallId);
|
|
166
|
+
if (options.parentSession?.toolCallId) {
|
|
167
|
+
record.notification = new NotificationState(options.parentSession.toolCallId);
|
|
163
168
|
}
|
|
164
169
|
|
|
165
170
|
if (options.isBackground) {
|
|
@@ -228,8 +233,7 @@ export class AgentManager {
|
|
|
228
233
|
isolated: options.isolated,
|
|
229
234
|
thinkingLevel: options.thinkingLevel,
|
|
230
235
|
cwd: worktreeCwd,
|
|
231
|
-
|
|
232
|
-
parentSessionId: options.parentSessionId,
|
|
236
|
+
parentSession: options.parentSession,
|
|
233
237
|
signal: record.abortController!.signal,
|
|
234
238
|
registry: this.registry,
|
|
235
239
|
onSessionCreated: (session) => {
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type SettingsManager,
|
|
10
10
|
} from "@earendil-works/pi-coding-agent";
|
|
11
11
|
import type { AgentConfigLookup } from "#src/config/agent-types";
|
|
12
|
+
import type { ParentSessionInfo } from "#src/lifecycle/agent-manager";
|
|
12
13
|
import type { ParentSnapshot } from "#src/lifecycle/parent-snapshot";
|
|
13
14
|
import { extractText } from "#src/session/context";
|
|
14
15
|
import type { EnvInfo } from "#src/session/env";
|
|
@@ -146,10 +147,8 @@ export interface RunOptions {
|
|
|
146
147
|
thinkingLevel?: ThinkingLevel;
|
|
147
148
|
/** Override working directory (e.g. for worktree isolation). */
|
|
148
149
|
cwd?: string;
|
|
149
|
-
/**
|
|
150
|
-
|
|
151
|
-
/** Session ID of the parent agent (stored in the child session's parentSession header). */
|
|
152
|
-
parentSessionId?: string;
|
|
150
|
+
/** Parent session identity (file path + session ID). */
|
|
151
|
+
parentSession?: ParentSessionInfo;
|
|
153
152
|
/** Called once after session creation — session delivery mechanism. */
|
|
154
153
|
onSessionCreated?: (session: AgentSession) => void;
|
|
155
154
|
/**
|
|
@@ -308,9 +307,9 @@ export async function runAgent(
|
|
|
308
307
|
// Create a persisted SessionManager so transcripts are written in Pi's
|
|
309
308
|
// official JSONL format. Falls back to a temp directory when the parent
|
|
310
309
|
// session is not persisted (e.g. headless/API mode).
|
|
311
|
-
const sessionDir = io.deriveSessionDir(options.parentSessionFile, cfg.effectiveCwd);
|
|
310
|
+
const sessionDir = io.deriveSessionDir(options.parentSession?.parentSessionFile, cfg.effectiveCwd);
|
|
312
311
|
const sessionManager = io.createSessionManager(cfg.effectiveCwd, sessionDir);
|
|
313
|
-
sessionManager.newSession({ parentSession: options.parentSessionId });
|
|
312
|
+
sessionManager.newSession({ parentSession: options.parentSession?.parentSessionId });
|
|
314
313
|
|
|
315
314
|
const { session } = await io.createSession({
|
|
316
315
|
cwd: cfg.effectiveCwd,
|
package/src/session/prompts.ts
CHANGED
|
@@ -17,12 +17,14 @@ export interface PromptExtras {
|
|
|
17
17
|
* Build the system prompt for an agent from its config.
|
|
18
18
|
*
|
|
19
19
|
* - "replace" mode: env header + config.systemPrompt (full control, no parent identity)
|
|
20
|
-
* - "append" mode:
|
|
20
|
+
* - "append" mode: parent system prompt + sub-agent context + env header + config.systemPrompt
|
|
21
21
|
* - "append" with empty systemPrompt: pure parent clone
|
|
22
22
|
*
|
|
23
|
-
* Both modes
|
|
23
|
+
* Both modes include an `<active_agent name="${config.name}"/>` tag so downstream
|
|
24
24
|
* extensions (e.g. `@gotgenes/pi-permission-system`) can resolve per-agent policy
|
|
25
25
|
* inside the child session by parsing the system prompt.
|
|
26
|
+
* In replace mode the tag is prepended; in append mode it follows the shared
|
|
27
|
+
* inherited content so the stable prefix is cacheable by the LLM's KV cache.
|
|
26
28
|
*
|
|
27
29
|
* @param parentSystemPrompt The parent agent's effective system prompt (for append mode).
|
|
28
30
|
* @param extras Optional extra sections to inject (memory, preloaded skills).
|
|
@@ -76,13 +78,17 @@ You are operating as a sub-agent invoked to handle a specific task.
|
|
|
76
78
|
? `\n\n<agent_instructions>\n${config.systemPrompt}\n</agent_instructions>`
|
|
77
79
|
: "";
|
|
78
80
|
|
|
81
|
+
// Place shared/stable content first so the LLM's KV cache can reuse the
|
|
82
|
+
// inherited prefix across all subagent invocations. The <active_agent> tag
|
|
83
|
+
// and env block vary per call and are placed after the cacheable prefix.
|
|
79
84
|
return (
|
|
80
|
-
|
|
81
|
-
envBlock +
|
|
82
|
-
"\n\n<inherited_system_prompt>\n" +
|
|
85
|
+
"<inherited_system_prompt>\n" +
|
|
83
86
|
identity +
|
|
84
87
|
"\n</inherited_system_prompt>\n\n" +
|
|
85
88
|
bridge +
|
|
89
|
+
"\n\n" +
|
|
90
|
+
activeAgentTag +
|
|
91
|
+
envBlock +
|
|
86
92
|
customSection +
|
|
87
93
|
extrasSuffix
|
|
88
94
|
);
|
package/src/tools/agent-tool.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { AgentToolResult } from "@earendil-works/pi-coding-agent";
|
|
|
3
3
|
import { Text } from "@earendil-works/pi-tui";
|
|
4
4
|
import { Type } from "@sinclair/typebox";
|
|
5
5
|
import { AgentTypeRegistry } from "#src/config/agent-types";
|
|
6
|
-
import type { AgentSpawnConfig } from "#src/lifecycle/agent-manager";
|
|
6
|
+
import type { AgentSpawnConfig, ParentSessionInfo } from "#src/lifecycle/agent-manager";
|
|
7
7
|
import type { ParentSnapshot } from "#src/lifecycle/parent-snapshot";
|
|
8
8
|
import { spawnBackground } from "#src/tools/background-spawner";
|
|
9
9
|
import { runForeground } from "#src/tools/foreground-runner";
|
|
@@ -303,6 +303,7 @@ Guidelines:
|
|
|
303
303
|
// ---- Boundary extraction (after config so inheritContext is resolved) ----
|
|
304
304
|
const snapshot = buildSnapshot(config.execution.inheritContext);
|
|
305
305
|
const { parentSessionFile, parentSessionId } = getSessionInfo();
|
|
306
|
+
const parentSession: ParentSessionInfo = { parentSessionFile, parentSessionId, toolCallId };
|
|
306
307
|
|
|
307
308
|
// ---- Resume existing agent ----
|
|
308
309
|
if (params.resume) {
|
|
@@ -337,7 +338,7 @@ Guidelines:
|
|
|
337
338
|
manager,
|
|
338
339
|
widget,
|
|
339
340
|
agentActivity,
|
|
340
|
-
{ config, snapshot,
|
|
341
|
+
{ config, snapshot, parentSession },
|
|
341
342
|
);
|
|
342
343
|
}
|
|
343
344
|
|
|
@@ -346,7 +347,7 @@ Guidelines:
|
|
|
346
347
|
manager,
|
|
347
348
|
widget,
|
|
348
349
|
agentActivity,
|
|
349
|
-
{ config, snapshot,
|
|
350
|
+
{ config, snapshot, parentSession },
|
|
350
351
|
signal,
|
|
351
352
|
onUpdate,
|
|
352
353
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentSpawnConfig } from "#src/lifecycle/agent-manager";
|
|
1
|
+
import type { AgentSpawnConfig, ParentSessionInfo } from "#src/lifecycle/agent-manager";
|
|
2
2
|
import type { ParentSnapshot } from "#src/lifecycle/parent-snapshot";
|
|
3
3
|
import type { AgentActivityAccess } from "#src/tools/agent-tool";
|
|
4
4
|
import { textResult } from "#src/tools/helpers";
|
|
@@ -24,9 +24,7 @@ export interface BackgroundWidgetDeps {
|
|
|
24
24
|
export interface BackgroundParams {
|
|
25
25
|
config: ResolvedSpawnConfig;
|
|
26
26
|
snapshot: ParentSnapshot;
|
|
27
|
-
|
|
28
|
-
parentSessionId: string;
|
|
29
|
-
toolCallId: string;
|
|
27
|
+
parentSession: ParentSessionInfo;
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
/**
|
|
@@ -46,8 +44,7 @@ export function spawnBackground(
|
|
|
46
44
|
let id: string;
|
|
47
45
|
try {
|
|
48
46
|
id = manager.spawn(params.snapshot, identity.subagentType, execution.prompt, {
|
|
49
|
-
|
|
50
|
-
parentSessionId: params.parentSessionId,
|
|
47
|
+
parentSession: params.parentSession,
|
|
51
48
|
description: execution.description,
|
|
52
49
|
model: execution.model,
|
|
53
50
|
maxTurns: execution.effectiveMaxTurns,
|
|
@@ -57,7 +54,6 @@ export function spawnBackground(
|
|
|
57
54
|
isBackground: true,
|
|
58
55
|
isolation: execution.isolation,
|
|
59
56
|
invocation: execution.agentInvocation,
|
|
60
|
-
toolCallId: params.toolCallId,
|
|
61
57
|
onSessionCreated: (session) => {
|
|
62
58
|
bgState.setSession(session);
|
|
63
59
|
subscribeUIObserver(session, bgState);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentToolResult } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import type { AgentSpawnConfig } from "#src/lifecycle/agent-manager";
|
|
2
|
+
import type { AgentSpawnConfig, ParentSessionInfo } from "#src/lifecycle/agent-manager";
|
|
3
3
|
import type { ParentSnapshot } from "#src/lifecycle/parent-snapshot";
|
|
4
4
|
import type { AgentActivityAccess } from "#src/tools/agent-tool";
|
|
5
5
|
import {
|
|
@@ -39,8 +39,7 @@ export interface ForegroundWidgetDeps {
|
|
|
39
39
|
export interface ForegroundParams {
|
|
40
40
|
config: ResolvedSpawnConfig;
|
|
41
41
|
snapshot: ParentSnapshot;
|
|
42
|
-
|
|
43
|
-
parentSessionId: string;
|
|
42
|
+
parentSession: ParentSessionInfo;
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
/**
|
|
@@ -109,8 +108,7 @@ export async function runForeground(
|
|
|
109
108
|
isolation: execution.isolation,
|
|
110
109
|
invocation: execution.agentInvocation,
|
|
111
110
|
signal,
|
|
112
|
-
|
|
113
|
-
parentSessionId: params.parentSessionId,
|
|
111
|
+
parentSession: params.parentSession,
|
|
114
112
|
onSessionCreated: (session, record) => {
|
|
115
113
|
fgState.setSession(session);
|
|
116
114
|
recordRef = record;
|