@gotgenes/pi-subagents 7.2.1 → 7.2.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 +23 -0
- package/docs/architecture/architecture.md +5 -5
- package/docs/plans/0195-convert-tool-factories-to-classes.md +264 -0
- package/docs/retro/0194-align-tool-interfaces-for-structural-typing.md +26 -0
- package/docs/retro/0195-convert-tool-factories-to-classes.md +42 -0
- package/package.json +1 -1
- package/src/index.ts +8 -42
- package/src/lifecycle/agent-runner.ts +0 -11
- package/src/tools/agent-tool.ts +228 -216
- package/src/tools/get-result-tool.ts +112 -87
- package/src/tools/steer-tool.ts +99 -77
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,29 @@ 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
|
+
## [7.2.3](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v7.2.2...pi-subagents-v7.2.3) (2026-05-25)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* enforce fallow dead-code gate in CI ([#195](https://github.com/gotgenes/pi-packages/issues/195)) ([b1bd734](https://github.com/gotgenes/pi-packages/commit/b1bd734e1d2f5921521bebb1735db8f8c402b53b))
|
|
14
|
+
|
|
15
|
+
## [7.2.2](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v7.2.1...pi-subagents-v7.2.2) (2026-05-25)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* remove unused AgentSession import and over-annotated mock return type in get-result-tool ([c3bc590](https://github.com/gotgenes/pi-packages/commit/c3bc59015e766d961422f4a7ecd23643c4cabefd))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Documentation
|
|
24
|
+
|
|
25
|
+
* mark [#195](https://github.com/gotgenes/pi-packages/issues/195) tool factory conversions complete in architecture roadmap ([cb5562b](https://github.com/gotgenes/pi-packages/commit/cb5562b57ef613ab0c1e136c9d9712c71e831bcd))
|
|
26
|
+
* plan convert tool factories to classes ([#195](https://github.com/gotgenes/pi-packages/issues/195)) ([ec916c2](https://github.com/gotgenes/pi-packages/commit/ec916c22a9d5f3453fba5784e3d2f5ecdc68740d))
|
|
27
|
+
* **retro:** add planning stage notes for issue [#195](https://github.com/gotgenes/pi-packages/issues/195) ([2ca0c96](https://github.com/gotgenes/pi-packages/commit/2ca0c964f8e73b0b0247daf9834986a9344be060))
|
|
28
|
+
* **retro:** add retro notes for issue [#194](https://github.com/gotgenes/pi-packages/issues/194) ([d7d973f](https://github.com/gotgenes/pi-packages/commit/d7d973f88ca48cf1a19b24bd396ef15a606a98bc))
|
|
29
|
+
* **retro:** add TDD stage notes for issue [#195](https://github.com/gotgenes/pi-packages/issues/195) ([921b1f8](https://github.com/gotgenes/pi-packages/commit/921b1f8e500baf9b2fffeb140336d4561bdaebf1))
|
|
30
|
+
|
|
8
31
|
## [7.2.1](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v7.2.0...pi-subagents-v7.2.1) (2026-05-25)
|
|
9
32
|
|
|
10
33
|
|
|
@@ -457,7 +457,7 @@ Bags with 10+ fields are the highest priority for decomposition.
|
|
|
457
457
|
| `ResourceLoaderOptions` | 10 | agent-runner (SDK bridge) | Medium |
|
|
458
458
|
| `RunnerIO` | split → `EnvironmentIO` (3) + `SessionFactoryIO` (5+1) | agent-runner | ✓ done |
|
|
459
459
|
| `CreateSessionOptions` | 9 | agent-runner (SDK bridge) | Medium |
|
|
460
|
-
| `AgentToolDeps` | 8 | agent-tool |
|
|
460
|
+
| `AgentToolDeps` | 8 | agent-tool | ✓ done |
|
|
461
461
|
| `AgentMenuDeps` | 8 | agent-menu | Low |
|
|
462
462
|
| `ConversationViewerOptions` | 8 | conversation-viewer | Low |
|
|
463
463
|
| `AgentRecordInit` | 8 | agent-record | Low |
|
|
@@ -688,13 +688,13 @@ After this step, `AgentManager` structurally satisfies `AgentToolManager` and `S
|
|
|
688
688
|
|
|
689
689
|
### Layer 3: Convert closure factories to classes ([#195][195], [#196][196])
|
|
690
690
|
|
|
691
|
-
With Layers 0–2 complete, each factory is a mechanical conversion:
|
|
691
|
+
With Layers 0–2 complete, each factory is a mechanical conversion. ✓ Tool factories converted in [#195][195]:
|
|
692
692
|
|
|
693
693
|
| Factory | Class | Constructor params |
|
|
694
694
|
| -------------------------------- | ------------------------------ | --------------------------------------------------- |
|
|
695
|
-
| `createAgentTool({...})` | `AgentTool`
|
|
696
|
-
| `createGetResultTool(...)` | `GetResultTool`
|
|
697
|
-
| `createSteerTool(...)` | `SteerTool`
|
|
695
|
+
| `createAgentTool({...})` | `AgentTool` ✓ | `manager`, `runtime`, `settings`, `registry` |
|
|
696
|
+
| `createGetResultTool(...)` | `GetResultTool` ✓ | `manager`, `notifications`, `registry` |
|
|
697
|
+
| `createSteerTool(...)` | `SteerTool` ✓ | `manager`, `events` |
|
|
698
698
|
| `createAgentRunner(runnerIO)` | `AgentRunner` (concrete class) | `io: RunnerIO` |
|
|
699
699
|
| `createAgentsMenuHandler({...})` | `AgentsMenuHandler` | `manager`, `registry`, `settings`, `fileOps`, paths |
|
|
700
700
|
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 195
|
|
3
|
+
issue_title: "Convert tool factories to classes"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Convert tool factories to classes
|
|
7
|
+
|
|
8
|
+
## Problem Statement
|
|
9
|
+
|
|
10
|
+
`createAgentTool`, `createGetResultTool`, and `createSteerTool` are closure factories that capture dependencies and return tool definitions.
|
|
11
|
+
This pattern hides dependencies in closures, forces `index.ts` to build narrow adapter objects at each call site, and prevents direct structural typing between real objects and tool interfaces.
|
|
12
|
+
With #193 (runtime owns queries) and #194 (interfaces aligned) both complete, the conversions are mechanical.
|
|
13
|
+
|
|
14
|
+
## Goals
|
|
15
|
+
|
|
16
|
+
- Convert all three tool factories to classes with constructor-injected dependencies.
|
|
17
|
+
- Each class exposes a `toToolDefinition()` method that wraps the tool with `defineTool()`.
|
|
18
|
+
- `index.ts` passes real objects directly — no adapter closures for tool wiring.
|
|
19
|
+
- Eliminate 16+ adapter closures from `index.ts`.
|
|
20
|
+
|
|
21
|
+
## Non-Goals
|
|
22
|
+
|
|
23
|
+
- Converting `createAgentRunner` or `createAgentsMenuHandler` — those belong to #196.
|
|
24
|
+
- Simplifying `index.ts` beyond tool wiring — that's #196 Layer 4.
|
|
25
|
+
- Changing tool behavior or parameters — this is a purely structural refactoring.
|
|
26
|
+
|
|
27
|
+
## Background
|
|
28
|
+
|
|
29
|
+
### Dependencies (both closed)
|
|
30
|
+
|
|
31
|
+
- Issue #193 moved context queries (`buildSnapshot`, `getModelInfo`, `getSessionInfo`) onto `SubagentRuntime`.
|
|
32
|
+
- Issue #194 aligned tool interfaces so `AgentManager` and `SubagentRuntime` structurally satisfy the narrow tool interfaces without adapters.
|
|
33
|
+
|
|
34
|
+
### Existing module layout
|
|
35
|
+
|
|
36
|
+
The three factory functions live in:
|
|
37
|
+
|
|
38
|
+
- `src/tools/agent-tool.ts` — `createAgentTool(deps: AgentToolDeps)`
|
|
39
|
+
- `src/tools/get-result-tool.ts` — `createGetResultTool(getRecord, cancelNudge, getConversation, registry)`
|
|
40
|
+
- `src/tools/steer-tool.ts` — `createSteerTool(getRecord, emitEvent, steerAgent, queueSteer)`
|
|
41
|
+
|
|
42
|
+
Each returns a plain object with `name`, `label`, `description`, `parameters`, `execute`, and optional render methods.
|
|
43
|
+
`index.ts` wraps each in `defineTool()` at registration time.
|
|
44
|
+
|
|
45
|
+
### Architecture doc reference
|
|
46
|
+
|
|
47
|
+
Phase 11, Layer 3 in `docs/architecture/architecture.md` (lines 689–709).
|
|
48
|
+
|
|
49
|
+
## Design Overview
|
|
50
|
+
|
|
51
|
+
### Class structure
|
|
52
|
+
|
|
53
|
+
Each class stores constructor-injected dependencies as instance fields and exposes:
|
|
54
|
+
|
|
55
|
+
1. A `toToolDefinition()` method that calls `defineTool(this.buildToolSpec())` (or similar) — returning the Pi SDK tool registration object.
|
|
56
|
+
2. Private methods that correspond to the existing function body sections.
|
|
57
|
+
|
|
58
|
+
### Constructor signatures
|
|
59
|
+
|
|
60
|
+
| Class | Constructor params |
|
|
61
|
+
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
62
|
+
| `AgentTool` | `manager: AgentToolManager`, `runtime: AgentToolRuntime`, `settings: AgentToolSettings`, `registry: AgentTypeRegistry`, `agentDir: string` |
|
|
63
|
+
| `GetResultTool` | `manager: GetResultToolManager`, `notifications: GetResultToolNotifications`, `registry: AgentConfigLookup` |
|
|
64
|
+
| `SteerTool` | `manager: SteerToolManager`, `events: SteerToolEvents` |
|
|
65
|
+
|
|
66
|
+
### Narrow interfaces
|
|
67
|
+
|
|
68
|
+
Each class defines its own narrow interface for the collaborators it uses.
|
|
69
|
+
These already exist (e.g., `AgentToolManager`, `BackgroundManagerDeps`) — they stay as-is; the class uses them directly.
|
|
70
|
+
|
|
71
|
+
#### `AgentTool`
|
|
72
|
+
|
|
73
|
+
The existing `AgentToolDeps` interface (8 fields) collapses into 5 constructor params.
|
|
74
|
+
The `widget`, `agentActivity`, `buildSnapshot`, `getModelInfo`, and `getSessionInfo` fields merge into a single `runtime` param because `SubagentRuntime` structurally satisfies all of them after #193/#194.
|
|
75
|
+
|
|
76
|
+
New narrow interface for the runtime slice:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
export interface AgentToolRuntime {
|
|
80
|
+
readonly agentActivity: AgentActivityAccess;
|
|
81
|
+
setUICtx(ctx: UICtx): void;
|
|
82
|
+
ensureTimer(): void;
|
|
83
|
+
update(): void;
|
|
84
|
+
markFinished(id: string): void;
|
|
85
|
+
buildSnapshot(inheritContext: boolean): ParentSnapshot;
|
|
86
|
+
getModelInfo(): ModelInfo;
|
|
87
|
+
getSessionInfo(): { parentSessionFile: string; parentSessionId: string };
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`SubagentRuntime` satisfies this structurally — no adapter needed in `index.ts`.
|
|
92
|
+
|
|
93
|
+
#### `GetResultTool`
|
|
94
|
+
|
|
95
|
+
New narrow interfaces:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
export interface GetResultToolManager {
|
|
99
|
+
getRecord(id: string): AgentRecord | undefined;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface GetResultToolNotifications {
|
|
103
|
+
cancelNudge(key: string): void;
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`getAgentConversation` is a pure function — imported directly by the class, not injected.
|
|
108
|
+
|
|
109
|
+
#### `SteerTool`
|
|
110
|
+
|
|
111
|
+
New narrow interfaces:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
export interface SteerToolManager {
|
|
115
|
+
getRecord(id: string): AgentRecord | undefined;
|
|
116
|
+
queueSteer(id: string, message: string): boolean;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface SteerToolEvents {
|
|
120
|
+
emit(name: string, data: unknown): void;
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`steerAgent` is a pure function — imported directly by the class, not injected.
|
|
125
|
+
|
|
126
|
+
### Call site after conversion
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
const agentTool = new AgentTool(manager, runtime, settings, registry, getAgentDir());
|
|
130
|
+
const getResultTool = new GetResultTool(manager, notifications, registry);
|
|
131
|
+
const steerTool = new SteerTool(manager, pi.events);
|
|
132
|
+
pi.registerTool(agentTool.toToolDefinition());
|
|
133
|
+
pi.registerTool(getResultTool.toToolDefinition());
|
|
134
|
+
pi.registerTool(steerTool.toToolDefinition());
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Design decisions
|
|
138
|
+
|
|
139
|
+
1. **`toToolDefinition()` on each class** — encapsulates the `defineTool()` call so `index.ts` doesn't import `defineTool` for each tool.
|
|
140
|
+
2. **Pure functions imported directly** — `steerAgent` and `getAgentConversation` are pure utilities with no state; injecting them adds indirection without testability benefit.
|
|
141
|
+
3. **`agentDir` stays a constructor param** — it's a static string computed once at startup; putting it on the registry would add scope responsibility the registry doesn't need.
|
|
142
|
+
4. **Keep existing `AgentToolDeps` temporarily** — remove it in the same PR once the class replaces all usage.
|
|
143
|
+
|
|
144
|
+
## Module-Level Changes
|
|
145
|
+
|
|
146
|
+
### `src/tools/agent-tool.ts`
|
|
147
|
+
|
|
148
|
+
- Add `AgentToolRuntime` interface (narrow runtime slice).
|
|
149
|
+
- Add `AgentToolSettings` type alias (existing inline type, extracted for clarity).
|
|
150
|
+
- Add `AgentTool` class with constructor storing `manager`, `runtime`, `settings`, `registry`, `agentDir`.
|
|
151
|
+
- Move the existing factory body into class methods: `toToolDefinition()`, private `execute()`.
|
|
152
|
+
- Remove `createAgentTool` function and `AgentToolDeps` interface.
|
|
153
|
+
- Keep `AgentToolManager`, `AgentToolWidget`, `AgentActivityAccess` interfaces (still used by `background-spawner` and `foreground-runner` — though `AgentToolWidget` may be replaceable by `AgentToolRuntime`).
|
|
154
|
+
|
|
155
|
+
### `src/tools/get-result-tool.ts`
|
|
156
|
+
|
|
157
|
+
- Add `GetResultToolManager` and `GetResultToolNotifications` interfaces.
|
|
158
|
+
- Add `GetResultTool` class with constructor.
|
|
159
|
+
- Import `getAgentConversation` directly.
|
|
160
|
+
- Move factory body into class method.
|
|
161
|
+
- Remove `createGetResultTool` function.
|
|
162
|
+
|
|
163
|
+
### `src/tools/steer-tool.ts`
|
|
164
|
+
|
|
165
|
+
- Add `SteerToolManager` and `SteerToolEvents` interfaces.
|
|
166
|
+
- Add `SteerTool` class with constructor.
|
|
167
|
+
- Import `steerAgent` directly.
|
|
168
|
+
- Move factory body into class method.
|
|
169
|
+
- Remove `createSteerTool` function.
|
|
170
|
+
|
|
171
|
+
### `src/tools/background-spawner.ts`
|
|
172
|
+
|
|
173
|
+
- Update `BackgroundWidgetDeps` to reference `AgentToolRuntime` instead (or keep as-is since `AgentToolRuntime` is a superset).
|
|
174
|
+
Actually no change needed — `spawnBackground` already accepts narrow interfaces; the `AgentTool` class passes `this.runtime` which satisfies `BackgroundWidgetDeps` structurally.
|
|
175
|
+
|
|
176
|
+
### `src/tools/foreground-runner.ts`
|
|
177
|
+
|
|
178
|
+
- Same as above — no change needed.
|
|
179
|
+
The `AgentTool` class passes `this.runtime` which satisfies `ForegroundWidgetDeps` structurally.
|
|
180
|
+
|
|
181
|
+
### `src/index.ts`
|
|
182
|
+
|
|
183
|
+
- Remove adapter closures for all three tool registrations.
|
|
184
|
+
- Replace with class construction + `toToolDefinition()` calls.
|
|
185
|
+
- Remove `defineTool` import (moves into tool classes).
|
|
186
|
+
- Remove unused imports that were only needed for adapter closures.
|
|
187
|
+
|
|
188
|
+
### `test/tools/agent-tool.test.ts`
|
|
189
|
+
|
|
190
|
+
- Replace `createToolDeps()` with class construction in test helpers.
|
|
191
|
+
- Update `execute()` helper to instantiate `AgentTool` and call its execute.
|
|
192
|
+
- Keep all existing test cases — behavior is unchanged.
|
|
193
|
+
|
|
194
|
+
### `test/tools/get-result-tool.test.ts`
|
|
195
|
+
|
|
196
|
+
- Replace positional args with class construction.
|
|
197
|
+
- Update `makeDeps` and `execute` helpers.
|
|
198
|
+
- Keep all existing test cases.
|
|
199
|
+
|
|
200
|
+
### `test/tools/steer-tool.test.ts`
|
|
201
|
+
|
|
202
|
+
- Replace positional args with class construction.
|
|
203
|
+
- Update `makeDeps` and `execute` helpers.
|
|
204
|
+
- Keep all existing test cases.
|
|
205
|
+
|
|
206
|
+
### `test/helpers/make-deps.ts`
|
|
207
|
+
|
|
208
|
+
- Update `createToolDeps` to construct `AgentTool`-compatible deps (or remove if no longer needed).
|
|
209
|
+
Likely transforms into a factory that builds mock `AgentToolRuntime`, `AgentToolManager`, etc.
|
|
210
|
+
|
|
211
|
+
### `docs/architecture/architecture.md`
|
|
212
|
+
|
|
213
|
+
- Update the Layer 3 table to mark #195 as done.
|
|
214
|
+
- Update the file listing for `src/tools/` to reflect class names (if the listing is that detailed).
|
|
215
|
+
|
|
216
|
+
## Test Impact Analysis
|
|
217
|
+
|
|
218
|
+
1. **No new test surfaces** — this is a structural refactoring with identical behavior.
|
|
219
|
+
Existing tests already cover all tool paths.
|
|
220
|
+
2. **Test helper changes** — `createToolDeps()` and per-tool `makeDeps()` functions need updating to use class construction instead of function calls, but the mock shapes remain the same.
|
|
221
|
+
3. **All existing tests stay** — they exercise the tool execution logic which doesn't change.
|
|
222
|
+
|
|
223
|
+
## TDD Order
|
|
224
|
+
|
|
225
|
+
1. **Convert `SteerTool` to class** — smallest tool (fewest deps, no render methods).
|
|
226
|
+
- Update `steer-tool.ts`: add interfaces, add class, remove factory.
|
|
227
|
+
- Update `test/tools/steer-tool.test.ts`: use class construction.
|
|
228
|
+
- Verify: `pnpm vitest run test/tools/steer-tool.test.ts`
|
|
229
|
+
- Commit: `refactor: convert createSteerTool to SteerTool class (#195)`
|
|
230
|
+
|
|
231
|
+
2. **Convert `GetResultTool` to class** — medium complexity (3 deps, no render methods).
|
|
232
|
+
- Update `get-result-tool.ts`: add interfaces, add class, import `getAgentConversation`, remove factory.
|
|
233
|
+
- Update `test/tools/get-result-tool.test.ts`: use class construction.
|
|
234
|
+
- Verify: `pnpm vitest run test/tools/get-result-tool.test.ts`
|
|
235
|
+
- Commit: `refactor: convert createGetResultTool to GetResultTool class (#195)`
|
|
236
|
+
|
|
237
|
+
3. **Convert `AgentTool` to class** — largest (5 deps, render methods, delegates to spawner/runner).
|
|
238
|
+
- Add `AgentToolRuntime` and `AgentToolSettings` interfaces.
|
|
239
|
+
- Add `AgentTool` class.
|
|
240
|
+
- Remove `createAgentTool` and `AgentToolDeps`.
|
|
241
|
+
- Update `test/tools/agent-tool.test.ts` and `test/helpers/make-deps.ts`.
|
|
242
|
+
- Verify: `pnpm vitest run test/tools/agent-tool.test.ts`
|
|
243
|
+
- Commit: `refactor: convert createAgentTool to AgentTool class (#195)`
|
|
244
|
+
|
|
245
|
+
4. **Update `index.ts`** — wire class constructors, remove adapter closures and `defineTool` import.
|
|
246
|
+
- Verify: `pnpm vitest run` (full suite) + `pnpm run check` (type-check).
|
|
247
|
+
- Commit: `refactor: wire tool classes in index.ts, remove adapter closures (#195)`
|
|
248
|
+
|
|
249
|
+
5. **Update architecture doc** — mark Layer 3 (partial) as complete.
|
|
250
|
+
- Commit: `docs: mark #195 complete in architecture roadmap`
|
|
251
|
+
|
|
252
|
+
## Risks and Mitigations
|
|
253
|
+
|
|
254
|
+
| Risk | Mitigation |
|
|
255
|
+
| --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
256
|
+
| `background-spawner` and `foreground-runner` accept narrow interfaces that differ from `AgentToolRuntime` | `AgentToolRuntime` is a superset — pass `this.runtime` and structural typing handles it. |
|
|
257
|
+
| Test factories import `AgentToolDeps` which will be removed | Step 3 updates all test imports in the same commit. |
|
|
258
|
+
| `defineTool` import removed from `index.ts` — must live somewhere | Each class's `toToolDefinition()` imports it internally. |
|
|
259
|
+
| Spreading class instances in tests breaks method access | Tests will construct the class, not spread it. Step 3 uses lift-and-shift for `make-deps.ts`. |
|
|
260
|
+
|
|
261
|
+
## Open Questions
|
|
262
|
+
|
|
263
|
+
- Should `AgentToolWidget` interface be removed entirely (replaced by `AgentToolRuntime`)?
|
|
264
|
+
Defer to implementation — if `background-spawner` and `foreground-runner` can accept `AgentToolRuntime` directly, remove it; otherwise keep the narrow subset interfaces.
|
|
@@ -34,3 +34,29 @@ The `pnpm run check` type-gate caught a previously-unnoticed `test/helpers/make-
|
|
|
34
34
|
- The `NotificationManager` constructor takes `updateWidget` as a positional callback parameter name — this does NOT need renaming (it's not a structural interface member).
|
|
35
35
|
- The rename from `updateWidget` → `update` is safe because the `WidgetLike` interface in `runtime.ts` already uses `update()` — no naming conflict within the class.
|
|
36
36
|
- All three changes are independent of each other and could be committed in any order, but the plan sequences them for clean `pnpm run check` passes at each step.
|
|
37
|
+
|
|
38
|
+
## Stage: Final Retrospective (2026-05-24T21:15:00Z)
|
|
39
|
+
|
|
40
|
+
### Session summary
|
|
41
|
+
|
|
42
|
+
Planning, TDD implementation, and shipping completed in one continuous session.
|
|
43
|
+
Released as `pi-subagents-v7.2.1` with zero rework or deviations from the plan.
|
|
44
|
+
|
|
45
|
+
### Observations
|
|
46
|
+
|
|
47
|
+
#### What went well
|
|
48
|
+
|
|
49
|
+
- Clean execution end-to-end: 3 refactor commits + 1 docs commit, all planned in advance.
|
|
50
|
+
- The `pnpm run check` gate after step 2 caught `test/helpers/make-deps.test.ts` — a file the plan didn't list — preventing a broken intermediate state.
|
|
51
|
+
This validates the "run type-check after each step" pattern for interface-alignment work.
|
|
52
|
+
- Phased architecture approach paid off: Layers 0 and 1 being done made Layer 2 entirely mechanical.
|
|
53
|
+
|
|
54
|
+
#### What caused friction (agent side)
|
|
55
|
+
|
|
56
|
+
- None identified.
|
|
57
|
+
The issue scope was tight, the plan was unambiguous, and no rabbit-holes arose.
|
|
58
|
+
|
|
59
|
+
#### What caused friction (user side)
|
|
60
|
+
|
|
61
|
+
- None identified.
|
|
62
|
+
The issue body and architecture doc provided complete context with no ambiguity.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 195
|
|
3
|
+
issue_title: "Convert tool factories to classes"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Retro: #195 — Convert tool factories to classes
|
|
7
|
+
|
|
8
|
+
## Stage: Planning (2026-05-24T12:00:00Z)
|
|
9
|
+
|
|
10
|
+
### Session summary
|
|
11
|
+
|
|
12
|
+
Produced a 5-step TDD plan converting `createAgentTool`, `createGetResultTool`, and `createSteerTool` to classes with constructor-injected dependencies.
|
|
13
|
+
Verified both prerequisites (#193, #194) are closed and their effects visible in the current source.
|
|
14
|
+
Designed narrow interfaces (`AgentToolRuntime`, `GetResultToolManager`, `SteerToolManager`, `SteerToolEvents`, etc.) that `SubagentRuntime`, `AgentManager`, and `NotificationManager` satisfy structurally.
|
|
15
|
+
|
|
16
|
+
### Observations
|
|
17
|
+
|
|
18
|
+
- The conversion is mechanical — no behavioral changes, just structural.
|
|
19
|
+
Existing tests cover all paths; only test helpers need updating.
|
|
20
|
+
- `steerAgent` and `getAgentConversation` are pure functions that can be imported directly by the classes rather than injected — simplifies the constructor signature.
|
|
21
|
+
- `agentDir` doesn't fit neatly on any existing collaborator, so it remains a constructor param for `AgentTool`.
|
|
22
|
+
- The `AgentToolWidget` interface may become redundant once `AgentToolRuntime` replaces it as the type passed to `spawnBackground`/`runForeground`, but this is deferred to implementation.
|
|
23
|
+
- Ordered TDD steps from smallest (SteerTool) to largest (AgentTool) to build confidence incrementally.
|
|
24
|
+
|
|
25
|
+
## Stage: Implementation — TDD (2026-05-24T21:26:00Z)
|
|
26
|
+
|
|
27
|
+
### Session summary
|
|
28
|
+
|
|
29
|
+
Completed all 5 planned TDD cycles (SteerTool → GetResultTool → AgentTool → `index.ts` wiring → architecture doc).
|
|
30
|
+
All 854 tests pass; type check and lint clean.
|
|
31
|
+
Total: 5 commits across source + test files, plus 1 cleanup fix commit.
|
|
32
|
+
|
|
33
|
+
### Observations
|
|
34
|
+
|
|
35
|
+
- `steerAgent` was inlined as `session.steer(message)` directly in `SteerTool.execute()` rather than imported as a module function.
|
|
36
|
+
This eliminated the dep entirely — the mock session's `steer` vi.fn() handles it in tests without `vi.mock`.
|
|
37
|
+
- The `verbose` test in `get-result-tool.test.ts` was upgraded to drive the real `getAgentConversation` function via `createMockSession({ messages: [...] })` overrides, making it a stronger integration test.
|
|
38
|
+
- `AgentToolWidget` was eliminated: `AgentToolRuntime` (a superset) replaced it, and `background-spawner` and `foreground-runner` already define their own narrow `BackgroundWidgetDeps`/`ForegroundWidgetDeps` interfaces.
|
|
39
|
+
- `createToolDeps()` changed shape from `AgentToolDeps` bag to `AgentToolFixture` (`{ manager, runtime, settings, registry, agentDir }`).
|
|
40
|
+
This required updating `background-spawner.test.ts` and `foreground-runner.test.ts` (not listed in the plan) to destructure `{ manager, runtime }` instead of `{ manager, widget, agentActivity }`.
|
|
41
|
+
- Biome flagged an unused `import type { AgentSession }` in `get-result-tool.ts` (left by ESLint's cast removal in step 2) — caught by `pnpm run lint` and fixed in a separate commit.
|
|
42
|
+
- The `ReturnType<typeof vi.fn>` annotation on `makeNotifications()` in the get-result-tool test triggered a TypeScript error; fixed by removing the return type annotation entirely (per testing skill guidance).
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -15,7 +15,6 @@ import { join } from "node:path";
|
|
|
15
15
|
import {
|
|
16
16
|
createAgentSession,
|
|
17
17
|
DefaultResourceLoader,
|
|
18
|
-
defineTool,
|
|
19
18
|
type ExtensionAPI,
|
|
20
19
|
getAgentDir,
|
|
21
20
|
SettingsManager as SdkSettingsManager,
|
|
@@ -25,7 +24,7 @@ import { AgentTypeRegistry } from "#src/config/agent-types";
|
|
|
25
24
|
import { loadCustomAgents } from "#src/config/custom-agents";
|
|
26
25
|
import { SessionLifecycleHandler, ToolStartHandler } from "#src/handlers/index";
|
|
27
26
|
import { AgentManager, type AgentManagerObserver } from "#src/lifecycle/agent-manager";
|
|
28
|
-
import { createAgentRunner,
|
|
27
|
+
import { createAgentRunner, type RunnerIO } from "#src/lifecycle/agent-runner";
|
|
29
28
|
import { buildParentSnapshot } from "#src/lifecycle/parent-snapshot";
|
|
30
29
|
import { GitWorktreeManager } from "#src/lifecycle/worktree";
|
|
31
30
|
import { buildEventData, type NotificationDetails, NotificationManager } from "#src/observation/notification";
|
|
@@ -40,16 +39,13 @@ import { buildAgentPrompt } from "#src/session/prompts";
|
|
|
40
39
|
import { deriveSubagentSessionDir } from "#src/session/session-dir";
|
|
41
40
|
import { preloadSkills } from "#src/session/skill-loader";
|
|
42
41
|
import { SettingsManager } from "#src/settings";
|
|
43
|
-
import {
|
|
44
|
-
import {
|
|
42
|
+
import { AgentTool } from "#src/tools/agent-tool";
|
|
43
|
+
import { GetResultTool } from "#src/tools/get-result-tool";
|
|
45
44
|
import { getModelLabelFromConfig } from "#src/tools/helpers";
|
|
46
|
-
import {
|
|
45
|
+
import { SteerTool } from "#src/tools/steer-tool";
|
|
47
46
|
import { FsAgentFileOps } from "#src/ui/agent-file-ops";
|
|
48
47
|
import { createAgentsMenuHandler } from "#src/ui/agent-menu";
|
|
49
|
-
import {
|
|
50
|
-
AgentWidget,
|
|
51
|
-
type UICtx,
|
|
52
|
-
} from "#src/ui/agent-widget";
|
|
48
|
+
import { AgentWidget } from "#src/ui/agent-widget";
|
|
53
49
|
|
|
54
50
|
export default function (pi: ExtensionAPI) {
|
|
55
51
|
// ---- Register custom notification renderer ----
|
|
@@ -185,45 +181,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
185
181
|
|
|
186
182
|
// ---- Agent tool ----
|
|
187
183
|
|
|
188
|
-
pi.registerTool(
|
|
189
|
-
manager: {
|
|
190
|
-
spawn: (snapshot, type, prompt, opts) => manager.spawn(snapshot, type, prompt, opts),
|
|
191
|
-
spawnAndWait: (snapshot, type, prompt, opts) => manager.spawnAndWait(snapshot, type, prompt, opts),
|
|
192
|
-
resume: (id, prompt, signal) => manager.resume(id, prompt, signal),
|
|
193
|
-
getRecord: (id) => manager.getRecord(id),
|
|
194
|
-
},
|
|
195
|
-
widget: {
|
|
196
|
-
setUICtx: (ctx) => runtime.setUICtx(ctx as UICtx),
|
|
197
|
-
ensureTimer: () => runtime.ensureTimer(),
|
|
198
|
-
update: () => runtime.update(),
|
|
199
|
-
markFinished: (id) => runtime.markFinished(id),
|
|
200
|
-
},
|
|
201
|
-
agentActivity: runtime.agentActivity,
|
|
202
|
-
registry,
|
|
203
|
-
agentDir: getAgentDir(),
|
|
204
|
-
settings,
|
|
205
|
-
buildSnapshot: runtime.buildSnapshot.bind(runtime),
|
|
206
|
-
getModelInfo: runtime.getModelInfo.bind(runtime),
|
|
207
|
-
getSessionInfo: runtime.getSessionInfo.bind(runtime),
|
|
208
|
-
})));
|
|
184
|
+
pi.registerTool(new AgentTool(manager, runtime, settings, registry, getAgentDir()).toToolDefinition());
|
|
209
185
|
|
|
210
186
|
// ---- get_subagent_result tool ----
|
|
211
187
|
|
|
212
|
-
pi.registerTool(
|
|
213
|
-
(id) => manager.getRecord(id),
|
|
214
|
-
(key) => notifications.cancelNudge(key),
|
|
215
|
-
(session) => getAgentConversation(session),
|
|
216
|
-
registry,
|
|
217
|
-
)));
|
|
188
|
+
pi.registerTool(new GetResultTool(manager, notifications, registry).toToolDefinition());
|
|
218
189
|
|
|
219
190
|
// ---- steer_subagent tool ----
|
|
220
191
|
|
|
221
|
-
pi.registerTool(
|
|
222
|
-
(id) => manager.getRecord(id),
|
|
223
|
-
(name, data) => pi.events.emit(name, data),
|
|
224
|
-
(session, message) => steerAgent(session, message),
|
|
225
|
-
(id, message) => manager.queueSteer(id, message),
|
|
226
|
-
)));
|
|
192
|
+
pi.registerTool(new SteerTool(manager, pi.events).toToolDefinition());
|
|
227
193
|
|
|
228
194
|
// ---- /agents interactive menu ----
|
|
229
195
|
|
|
@@ -434,17 +434,6 @@ export async function resumeAgent(
|
|
|
434
434
|
return collector.getText().trim() || getLastAssistantText(session);
|
|
435
435
|
}
|
|
436
436
|
|
|
437
|
-
/**
|
|
438
|
-
* Send a steering message to a running subagent.
|
|
439
|
-
* The message will interrupt the agent after its current tool execution.
|
|
440
|
-
*/
|
|
441
|
-
export async function steerAgent(
|
|
442
|
-
session: AgentSession,
|
|
443
|
-
message: string,
|
|
444
|
-
): Promise<void> {
|
|
445
|
-
await session.steer(message);
|
|
446
|
-
}
|
|
447
|
-
|
|
448
437
|
/**
|
|
449
438
|
* Get the subagent's conversation messages as formatted text.
|
|
450
439
|
*/
|