@nklisch/pi-enhanced 0.4.3 → 0.5.0
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 +17 -0
- package/README.md +1 -1
- package/node_modules/@nklisch/pi-astral-pocket/README.md +127 -93
- package/node_modules/@nklisch/pi-astral-pocket/package.json +2 -2
- package/node_modules/@nklisch/pi-astral-pocket/src/distiller.ts +196 -183
- package/node_modules/@nklisch/pi-astral-pocket/src/guidance.ts +26 -62
- package/node_modules/@nklisch/pi-astral-pocket/src/index.ts +8 -5
- package/node_modules/@nklisch/pi-astral-pocket/src/sessions.ts +10 -3
- package/node_modules/@nklisch/pi-astral-pocket/src/store.ts +439 -321
- package/node_modules/@nklisch/pi-astral-pocket/src/tools.ts +172 -66
- package/node_modules/@nklisch/pi-clearance/docs/CONFIGURATION.md +3 -3
- package/node_modules/@nklisch/pi-clearance/docs/DEVELOPER_GUIDE.md +11 -12
- package/node_modules/@nklisch/pi-clearance/docs/PACK_AUTHORING.md +5 -5
- package/node_modules/@nklisch/pi-clearance/docs/PRINCIPLES.md +1 -2
- package/node_modules/@nklisch/pi-clearance/docs/REFERENCE_PATTERNS.md +13 -19
- package/node_modules/@nklisch/pi-clearance/docs/SPEC.md +4 -4
- package/node_modules/@nklisch/pi-clearance/docs/USER_GUIDE.md +2 -3
- package/node_modules/@nklisch/pi-clearance/docs/VISION.md +1 -1
- package/node_modules/@nklisch/pi-clearance/native/clearance-core.win32-x64-msvc.node +0 -0
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/README.md +2 -2
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/VISION.md +2 -3
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/architecture.md +105 -886
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/decisions/0002-extensions-on-a-minimal-core.md +44 -78
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/decisions/0003-publish-bundled-type-declarations.md +28 -57
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/decisions/0004-reconsider-ui-direction.md +49 -261
- package/node_modules/@nklisch/pi-plugins/package.json +2 -2
- package/package.json +2 -2
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/client-server-opportunities.md +0 -127
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-1-api-boundary.md +0 -8
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-10-structural-decomposition.md +0 -141
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-11-closure-to-class.md +0 -100
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-12-complexity-test-fixtures.md +0 -55
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-13-remaining-smells.md +0 -88
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-14-strip-policy.md +0 -49
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-15-domain-model-evolution.md +0 -73
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-16-invert-dependencies.md +0 -144
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-17-core-consolidation.md +0 -214
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-18-reconsider-ui.md +0 -166
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-19-implement-ui-decisions.md +0 -282
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-2-remove-scheduling.md +0 -9
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-3-remove-rpc-groupjoin.md +0 -11
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-4-implement-service.md +0 -8
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-5-decompose-index.md +0 -42
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-7-encapsulation.md +0 -173
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-8-testability.md +0 -103
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/history/phase-9-observation-ctx.md +0 -122
- package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/decisions/0001-deferred-patches.md +0 -80
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
# Phase 7: Encapsulation and dependency narrowing
|
|
2
|
-
|
|
3
|
-
Every mutable state bag became a class, every dependency bag narrowed to what its consumer uses, every callback became either a method on a collaborator or an event on an observable.
|
|
4
|
-
|
|
5
|
-
## AgentManager decomposition
|
|
6
|
-
|
|
7
|
-
AgentManager was decomposed in three steps to untangle record management, concurrency control, and execution orchestration.
|
|
8
|
-
|
|
9
|
-
### Step 1: Record state machine (#98, #102)
|
|
10
|
-
|
|
11
|
-
Extracted status-transition methods (`markRunning`, `markCompleted`, `markAborted`, `markSteered`, `markError`, `markStopped`, `resetForResume`) onto `AgentRecord`.
|
|
12
|
-
Replaced scattered field writes across 6 sites with encapsulated transition methods.
|
|
13
|
-
Issue #102 consolidated test `AgentRecord` construction into a shared factory.
|
|
14
|
-
|
|
15
|
-
### Step 2: Parent snapshot (#99)
|
|
16
|
-
|
|
17
|
-
Replaced live `ctx: ExtensionContext` capture in `SpawnArgs` with an immutable `ParentSnapshot` data object.
|
|
18
|
-
The snapshot is taken once at spawn time; queued agents execute against frozen state rather than a potentially stale session reference.
|
|
19
|
-
`runAgent()` accepts `ParentSnapshot` instead of `ctx`.
|
|
20
|
-
`pi: ExtensionAPI` was removed from `SpawnArgs` - `runAgent()` accepts a `ShellExec` function instead.
|
|
21
|
-
|
|
22
|
-
### Step 3: Session-event observation (#100)
|
|
23
|
-
|
|
24
|
-
Replaced three-layer callback threading with direct session subscriptions.
|
|
25
|
-
`record-observer.ts` subscribes to the session to update record statistics (tool uses, lifetime usage, compaction count).
|
|
26
|
-
`ui/ui-observer.ts` subscribes to the session to stream UI state (active tools, response text, turn count).
|
|
27
|
-
`SpawnOptions` and `RunOptions` dropped all `on*` callback fields except `onSessionCreated` (which delivers the session object to enable external subscriptions).
|
|
28
|
-
|
|
29
|
-
### Realized impact
|
|
30
|
-
|
|
31
|
-
| Metric | Before | After |
|
|
32
|
-
| --------------------------------- | ------ | ----------------------- |
|
|
33
|
-
| `SpawnOptions` callback fields | 6 | 1 (`onSessionCreated`) |
|
|
34
|
-
| `RunOptions` callback fields | 6 | 1 (`onSessionCreated`) |
|
|
35
|
-
| Callback layers | 3 | 0 (direct subscription) |
|
|
36
|
-
| Live `ctx` references in queue | 1 | 0 (snapshot) |
|
|
37
|
-
| Scattered status-transition sites | 6 | 1 (state machine) |
|
|
38
|
-
|
|
39
|
-
## Encapsulation roadmap
|
|
40
|
-
|
|
41
|
-
Phase 7 encapsulated mutable state into classes, replaced callbacks with semantic components, and narrowed dependency bags.
|
|
42
|
-
|
|
43
|
-
Each step was sequenced so it made the next step easier.
|
|
44
|
-
|
|
45
|
-
### Resolved smells
|
|
46
|
-
|
|
47
|
-
All nine smells identified at the start of Phase 7 were resolved:
|
|
48
|
-
|
|
49
|
-
| Smell | Resolution |
|
|
50
|
-
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
51
|
-
| Global mutable state | `AgentTypeRegistry` class (#108); `reloadCustomAgents` callback removed from dep bags |
|
|
52
|
-
| Closure bag as class | `NotificationManager` class (#116); `pendingNudges` and timer state are private fields |
|
|
53
|
-
| Mutable state bag | `AgentActivityTracker` class (#110); transition methods replace external writes |
|
|
54
|
-
| Settings relay | `SettingsManager` class (#109); 6 callback fields collapsed to one object |
|
|
55
|
-
| Post-construction mutation | `ExecutionState`, `WorktreeState`, `NotificationState` collaborators (#111); stats behind mutation methods |
|
|
56
|
-
| Fire-and-forget callbacks | `AgentManagerObserver` interface (#112); one observer object replaces 3 closure lambdas |
|
|
57
|
-
| Duplicate `SpawnOptions` | Internal type renamed to `AgentSpawnConfig` (#113); public `SpawnOptions` unchanged |
|
|
58
|
-
| Type dumping ground | `NotificationDetails`, `ParentSnapshot`, `EnvInfo` moved to their natural modules (#116); narrow subsets defined |
|
|
59
|
-
| Wide dependency bags | `AgentToolDeps` 9 → 6, `AgentMenuDeps` 8 → 7 (#114); `emitEvent` removed; description text derived from registry; `agentActivity` narrowed |
|
|
60
|
-
|
|
61
|
-
### Step A: Extract state into classes (foundation, parallel)
|
|
62
|
-
|
|
63
|
-
These three extractions are independent and can proceed in any order.
|
|
64
|
-
Each eliminates a category of global/closure state and gives orphaned callbacks a natural home.
|
|
65
|
-
|
|
66
|
-
#### A1. AgentTypeRegistry class (#108)
|
|
67
|
-
|
|
68
|
-
Wrapped the module-scoped `agents` Map and free functions in `agent-types.ts` into an injectable class.
|
|
69
|
-
`reloadCustomAgents` callback removed from `AgentToolDeps` and `AgentMenuDeps`; replaced by `registry.reload()`.
|
|
70
|
-
`DEFAULT_AGENT_NAMES` moved from `types.ts` to the registry.
|
|
71
|
-
|
|
72
|
-
#### A2. SettingsManager class (#109, #118)
|
|
73
|
-
|
|
74
|
-
Encapsulated settings load/save/apply cycle into `SettingsManager` (in `settings.ts`).
|
|
75
|
-
Owns `defaultMaxTurns`, `graceTurns`, `maxConcurrent` with normalizing property accessors.
|
|
76
|
-
Added `applyMaxConcurrent(n)`, `applyDefaultMaxTurns(n)`, `applyGraceTurns(n)` - each owns the full consequence chain: normalize → set in memory → notify callback → persist → emit event → return toast.
|
|
77
|
-
The 6 settings-related fields in `AgentMenuDeps` collapsed to `settings: AgentMenuSettings`.
|
|
78
|
-
|
|
79
|
-
#### A3. AgentActivityTracker class (#110)
|
|
80
|
-
|
|
81
|
-
Wrapped the 7-field mutable `AgentActivity` interface in an `AgentActivityTracker` class (`src/ui/agent-activity-tracker.ts`).
|
|
82
|
-
`ui-observer.ts` calls transition methods; consumers use read-only accessors.
|
|
83
|
-
The shared map on `SubagentRuntime` is `Map<string, AgentActivityTracker>`.
|
|
84
|
-
|
|
85
|
-
### Step B: Split AgentRecord lifecycle state (#111)
|
|
86
|
-
|
|
87
|
-
Split post-construction mutation into phase-specific collaborators, each born complete:
|
|
88
|
-
|
|
89
|
-
- **`ExecutionState`** (`session`, `outputFile`) - constructed in `onSessionCreated`.
|
|
90
|
-
- **`WorktreeState`** (`path`, `branch`, `cleanupResult`) - constructed at worktree setup.
|
|
91
|
-
- **`NotificationState`** (`toolCallId`, `resultConsumed`) - constructed by `AgentManager.spawn()` when `toolCallId` is provided.
|
|
92
|
-
- **`pendingSteers`** moved to `Map<string, string[]>` on `AgentManager`.
|
|
93
|
-
- Stats encapsulated behind mutation methods with read-only getters.
|
|
94
|
-
- `AgentRecordInit` trimmed from 19 optional fields to 4 construction-time fields.
|
|
95
|
-
|
|
96
|
-
### Step C: Replace AgentManager callbacks with observer (#112)
|
|
97
|
-
|
|
98
|
-
`AgentManagerObserver` interface replaces `onStart`/`onComplete`/`onCompact`.
|
|
99
|
-
`index.ts` constructs one observer object instead of 3 closure lambdas.
|
|
100
|
-
`AgentManagerOptions` drops from 9 → 7 fields.
|
|
101
|
-
|
|
102
|
-
### Step D: Disambiguate SpawnOptions and narrow dependency bags
|
|
103
|
-
|
|
104
|
-
#### D1. Disambiguate SpawnOptions (#113)
|
|
105
|
-
|
|
106
|
-
Internal `SpawnOptions` in `agent-manager.ts` renamed to `AgentSpawnConfig`.
|
|
107
|
-
Public `SpawnOptions` in `service.ts` unchanged.
|
|
108
|
-
|
|
109
|
-
#### D2. Narrow AgentToolDeps and AgentMenuDeps (#114)
|
|
110
|
-
|
|
111
|
-
| Bag | Before | After | How |
|
|
112
|
-
| --------------- | -------- | ----- | ----------------------------------------------------------------------------------------------------------- |
|
|
113
|
-
| `AgentToolDeps` | 9 fields | 6 | `emitEvent` → observer; `typeListText`/`availableTypesText` derived from registry; `agentActivity` narrowed |
|
|
114
|
-
| `AgentMenuDeps` | 8 fields | 7 | Dead `emitEvent` removed; `agentActivity` narrowed to read-only `AgentActivityReader` |
|
|
115
|
-
|
|
116
|
-
### Step E: Decompose large files and relocate types
|
|
117
|
-
|
|
118
|
-
#### E1. Split agent-tool.ts foreground/background (#115)
|
|
119
|
-
|
|
120
|
-
Extracted `foreground-runner.ts` (~175 lines) and `background-spawner.ts` (~116 lines).
|
|
121
|
-
`agent-tool.ts` reduced from 579 → 411 lines.
|
|
122
|
-
|
|
123
|
-
#### E2. Type housekeeping (#116)
|
|
124
|
-
|
|
125
|
-
- Moved `NotificationDetails`, `ParentSnapshot`, `EnvInfo` to their natural modules.
|
|
126
|
-
- Converted `createNotificationSystem` closure to `NotificationManager` class.
|
|
127
|
-
- Converted `ConversationViewer` constructor from 7 positional parameters to `ConversationViewerOptions` bag.
|
|
128
|
-
- Defined `AgentIdentity` and `AgentPromptConfig` narrow subsets; `buildAgentPrompt` narrowed to `AgentPromptConfig`.
|
|
129
|
-
|
|
130
|
-
### Phase 7 results
|
|
131
|
-
|
|
132
|
-
| Metric | Before | After |
|
|
133
|
-
| ------------------------------------------ | ------ | ----- |
|
|
134
|
-
| Module-scoped mutable state | 1 | 0 |
|
|
135
|
-
| Closure-bag "classes" | 2 | 0 |
|
|
136
|
-
| Externally-mutated state bags | 2 | 0 |
|
|
137
|
-
| `AgentManagerOptions` fields | 9 | 7 |
|
|
138
|
-
| `AgentToolDeps` fields | 9 | 6 |
|
|
139
|
-
| `AgentMenuDeps` fields | 13 | 7 |
|
|
140
|
-
| `SpawnOptions` callback fields | 6 | 1 |
|
|
141
|
-
| `RunOptions` callback fields | 6 | 1 |
|
|
142
|
-
| Callbacks threaded through deps | 8 | 0 |
|
|
143
|
-
| Types in `types.ts` without a natural home | 4 | 0 |
|
|
144
|
-
|
|
145
|
-
### Dependency graph
|
|
146
|
-
|
|
147
|
-
```mermaid
|
|
148
|
-
flowchart LR
|
|
149
|
-
A1["A1: Registry"] --> D2["D2: Narrow deps"]
|
|
150
|
-
A2["A2: Settings"] --> A2b["A2b: Apply"] --> D2
|
|
151
|
-
A3["A3: Activity Tracker"] --> D2
|
|
152
|
-
B["B: Record lifecycle"] --> D2
|
|
153
|
-
B --> C["C: Observer"] --> D1["D1: SpawnOptions"] --> D2
|
|
154
|
-
D2 --> E1["E1: agent-tool split"]
|
|
155
|
-
A1 --> E2["E2: Type housekeeping"]
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
## Related issues
|
|
159
|
-
|
|
160
|
-
- #98 — Record state machine
|
|
161
|
-
- #99 — Parent snapshot
|
|
162
|
-
- #100 — Session-event observation
|
|
163
|
-
- #102 — Consolidated test AgentRecord construction
|
|
164
|
-
- #108 — AgentTypeRegistry class
|
|
165
|
-
- #109 — SettingsManager class
|
|
166
|
-
- #110 — AgentActivityTracker class
|
|
167
|
-
- #111 — Split AgentRecord lifecycle state
|
|
168
|
-
- #112 — Replace AgentManager callbacks with observer
|
|
169
|
-
- #113 — Disambiguate SpawnOptions
|
|
170
|
-
- #114 — Narrow AgentToolDeps and AgentMenuDeps
|
|
171
|
-
- #115 — Split agent-tool.ts foreground/background
|
|
172
|
-
- #116 — Type housekeeping
|
|
173
|
-
- #118 — SettingsManager apply methods
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
# Phase 8: Testability, display extraction, and menu decomposition
|
|
2
|
-
|
|
3
|
-
Eliminated `vi.mock()` module mocking in the two most fragile test suites by injecting IO-touching collaborators; consolidated shared test fixtures; extracted display helpers into a reusable module; decomposed the largest UI file.
|
|
4
|
-
|
|
5
|
-
Steps G and H eliminated 11 of the original 12 `vi.mock()` calls in the runner tests, removing fragile call-sequence assertions in favour of injected stubs.
|
|
6
|
-
Step G resolved `session-config.test.ts`; Step H resolved both `agent-runner.test.ts` and `agent-runner-extension-tools.test.ts`.
|
|
7
|
-
|
|
8
|
-
The display and menu improvements were identified during Phase 7 but deferred because they did not gate encapsulation work.
|
|
9
|
-
The display extraction unblocked menu decomposition.
|
|
10
|
-
|
|
11
|
-
## Test pain points (resolved)
|
|
12
|
-
|
|
13
|
-
| Symptom | Resolution |
|
|
14
|
-
| ------------------------------------------------------------- | -------------------------------------------------------------- |
|
|
15
|
-
| 7 `vi.mock()` calls in `agent-runner.test.ts` | Step H (#133): injected `RunnerIO` stubs |
|
|
16
|
-
| 7 `vi.mock()` calls in `agent-runner-extension-tools.test.ts` | Step H (#133): same |
|
|
17
|
-
| 52 `as any` casts across test suite | Step I (#134): reduced to 15 |
|
|
18
|
-
| 3× duplicated `mockSession()` | Step F (#131): shared `createMockSession()` in `test/helpers/` |
|
|
19
|
-
| 3× duplicated `makeDeps()` | Step F (#131): shared `createToolDeps()` in `test/helpers/` |
|
|
20
|
-
|
|
21
|
-
The well-designed test suites - `agent-manager.test.ts` (1 mock, DI via `AgentRunner` interface), `notification.test.ts` (0 mocks, pure functions + DI), and `agent-tool.test.ts` (0 mocks, tests via deps bag) - confirmed the pattern: modules that accept collaborators through injection produce resilient tests; modules that import collaborators directly produce fragile mock-heavy tests.
|
|
22
|
-
|
|
23
|
-
## Step F: Shared test fixtures (#131)
|
|
24
|
-
|
|
25
|
-
Consolidated duplicated mock factories into `test/helpers/`.
|
|
26
|
-
|
|
27
|
-
1. `createMockSession()` - subscribable event bus with `emit()` helper; replaced 3 hand-rolled copies.
|
|
28
|
-
2. `createToolDeps()` - builds `AgentToolDeps` with sensible defaults and override support; replaced 3 `makeDeps()` copies.
|
|
29
|
-
3. `makeRecord()` - `AgentRecord` factory with sensible defaults; replaced scattered inline construction.
|
|
30
|
-
4. `STUB_CTX` - shared stub `ExtensionContext` constant; centralised unavoidable bridge casts.
|
|
31
|
-
|
|
32
|
-
Impact: reduced test boilerplate; single source of truth for mock shapes; changes to dep interfaces propagate automatically.
|
|
33
|
-
|
|
34
|
-
## Step G: Inject IO collaborators into session-config (#132)
|
|
35
|
-
|
|
36
|
-
`assembleSessionConfig` now accepts `io: AssemblerIO` as a required parameter.
|
|
37
|
-
`index.ts` constructs the real `AssemblerIO` from direct imports via the `RunnerIO.assemblerIO` field (wired in Step H).
|
|
38
|
-
`session-config.test.ts` injects stubs - all 4 `vi.mock()` calls eliminated, assertions shifted to `SessionConfig` output properties.
|
|
39
|
-
|
|
40
|
-
## Step H: Inject SDK boundary into agent-runner (#133)
|
|
41
|
-
|
|
42
|
-
`runAgent()` now accepts `io: RunnerIO` as a required parameter bundling all IO collaborators: `detectEnv`, `getAgentDir`, `createResourceLoader`, `deriveSessionDir`, `createSessionManager`, `createSettingsManager`, `createSession`, and `assemblerIO`.
|
|
43
|
-
|
|
44
|
-
`createAgentRunner(io: RunnerIO): AgentRunner` factory captures the boundary at construction time so `AgentManager` and the `AgentRunner` interface remain unchanged.
|
|
45
|
-
`index.ts` constructs the real `RunnerIO` from Pi SDK imports and sibling modules.
|
|
46
|
-
|
|
47
|
-
Impact: all 7 `vi.mock()` calls eliminated from both `agent-runner.test.ts` and `agent-runner-extension-tools.test.ts`; tests verify behavior (turn limits, tool filtering, response collection) through injected stubs; SDK imports moved to the extension entry point.
|
|
48
|
-
|
|
49
|
-
## Step I: Reduce `as any` casts in tests (#134)
|
|
50
|
-
|
|
51
|
-
Reduced `as any` count from 93 to 15 (plus 13 explicit `as unknown as T` bridge casts).
|
|
52
|
-
|
|
53
|
-
Production changes:
|
|
54
|
-
|
|
55
|
-
- `ResourceLoaderOptions.appendSystemPromptOverride` typed to match `DefaultResourceLoaderOptions`; `createResourceLoader` factory cast removed from `index.ts`.
|
|
56
|
-
- `CreateSessionOptions.settingsManager` / `RunnerIO.createSettingsManager` typed as `SettingsManager`.
|
|
57
|
-
- `WidgetLike` interface in `runtime.ts` narrows the widget field.
|
|
58
|
-
- Local `ToolCallContent` / `BashExecutionMessage` type guards replace `as any` duck-typing in `conversation-viewer.ts` and `agent-runner.ts`.
|
|
59
|
-
- `textResult()` return no longer casts `details as any`.
|
|
60
|
-
- `toAgentSession()` helper and `STUB_CTX` constant centralise unavoidable bridge casts.
|
|
61
|
-
|
|
62
|
-
Remaining 15 `as any` casts are: 8 menu-handler `ctx as any` (deferred - requires `AgentManager.spawn` to accept `ParentSnapshot` directly), 2 `print-mode.test.ts` (same ExtensionContext/API pattern), 2 private-field test access, 1 `createSession` SDK bridge in `index.ts`, 1 `foreground-runner.ts` `AgentToolResult<any>` detail, 1 `stub-ctx.ts` comment.
|
|
63
|
-
|
|
64
|
-
## Step J: Extract display helpers (#135)
|
|
65
|
-
|
|
66
|
-
`ui/display.ts` now contains all pure formatters, display helpers, constants, and shared types (`Theme`, `AgentDetails`).
|
|
67
|
-
`agent-widget.ts` dropped from 522 → ~340 lines.
|
|
68
|
-
All consumer modules (menu, tools, renderer, conversation viewer) import from `ui/display.ts` directly.
|
|
69
|
-
`test/agent-widget.test.ts` renamed to `test/display.test.ts`.
|
|
70
|
-
|
|
71
|
-
## Step K: Decompose agent-menu.ts (#136)
|
|
72
|
-
|
|
73
|
-
`agent-menu.ts` (668 lines) decomposed into four modules:
|
|
74
|
-
|
|
75
|
-
1. `ui/agent-file-ops.ts` - `AgentFileOps` interface (`exists`, `read`, `write`, `remove`, `ensureDir`, `findAgentFile`) + `FsAgentFileOps` production implementation.
|
|
76
|
-
2. `ui/agent-config-editor.ts` - `showAgentDetail` with edit/delete/reset/eject/disable/enable transitions (~200 lines).
|
|
77
|
-
3. `ui/agent-creation-wizard.ts` - AI-generation and manual-form creation paths (~250 lines).
|
|
78
|
-
4. `ui/agent-menu.ts` - menu orchestration, agent listing, running-agent viewer, settings form (~300 lines).
|
|
79
|
-
|
|
80
|
-
Impact: `agent-menu.ts` dropped from 668 → 296 lines; extracted modules receive `AgentFileOps` via injection; `vi.mock("node:fs")` eliminated from `agent-menu.test.ts`.
|
|
81
|
-
|
|
82
|
-
## Step dependencies
|
|
83
|
-
|
|
84
|
-
```mermaid
|
|
85
|
-
flowchart LR
|
|
86
|
-
subgraph testability["Testability track"]
|
|
87
|
-
F["F: Shared fixtures"] --> G["G: session-config IO"] --> H["H: agent-runner SDK"] --> I["I: Reduce as-any"]
|
|
88
|
-
end
|
|
89
|
-
subgraph display["Display track"]
|
|
90
|
-
J["J: Display extraction"] --> K["K: Menu decomposition"]
|
|
91
|
-
end
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
The two tracks are independent and can proceed in parallel.
|
|
95
|
-
|
|
96
|
-
## Related issues
|
|
97
|
-
|
|
98
|
-
- #131 — Shared test fixtures
|
|
99
|
-
- #132 — Inject IO collaborators into session-config
|
|
100
|
-
- #133 — Inject SDK boundary into agent-runner
|
|
101
|
-
- #134 — Reduce as-any casts in tests
|
|
102
|
-
- #135 — Extract display helpers
|
|
103
|
-
- #136 — Decompose agent-menu.ts
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
# Phase 9: Observation consolidation, ctx elimination, and remaining mocks
|
|
2
|
-
|
|
3
|
-
Target: consolidate the dual observation model so stats live in one place; remove `ExtensionContext` from all internal APIs; eliminate remaining `vi.mock()` calls and `as any` casts; split widget rendering from lifecycle; apply dependency bag convention.
|
|
4
|
-
|
|
5
|
-
## Current smells
|
|
6
|
-
|
|
7
|
-
| Smell | Location | Evidence | Severity |
|
|
8
|
-
| ------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | -------- |
|
|
9
|
-
| `execute` does config resolution for its callees | `agent-tool.ts` (145-line `execute`) | ~60 lines unpack config, resolve model, compute metadata, repack into 16-field bags for spawners; `ctx` threaded 4 layers deep | Medium |
|
|
10
|
-
| ~~Wide `ctx` in menu handlers~~ | ~~`agent-menu.ts`, `agent-config-editor.ts`, `agent-creation-wizard.ts`~~ | Resolved by #146: `MenuUI` interface introduced; 42 `ctx as any` casts eliminated across 5 test files | Done |
|
|
11
|
-
| ~~Direct SDK import in `conversation-viewer.ts`~~ | ~~`conversation-viewer.test.ts`~~ | Resolved by #147: `wrapText` injected via `ConversationViewerOptions`; `vi.mock("@earendil-works/pi-tui")` eliminated | Done |
|
|
12
|
-
| ~~Widget mixes rendering, lifecycle, and state~~ | ~~`agent-widget.ts` (370 lines)~~ | Resolved by #148: rendering extracted to `widget-renderer.ts`; widget is now 198 lines | Done |
|
|
13
|
-
| `deps.` prefix noise in function bodies | remaining modules across tools, UI, service-adapter | Functions accept a `deps` bag and access every field as `deps.foo`; hides real dependencies and lengthens every call line | Low |
|
|
14
|
-
|
|
15
|
-
## Dependency bag convention
|
|
16
|
-
|
|
17
|
-
Applied incrementally as each step touches a module:
|
|
18
|
-
|
|
19
|
-
- **≤4 fields** - accept as plain parameters; drop the interface.
|
|
20
|
-
- **≥5 fields** - keep a named interface but destructure in the function signature (`{ manager, widget }: ForegroundDeps`) so the function body uses bare names, not `deps.foo`.
|
|
21
|
-
|
|
22
|
-
This eliminates the `deps.` prefix noise across ~124 callsites in 12 modules.
|
|
23
|
-
|
|
24
|
-
## Step L: Consolidate observation model (#144)
|
|
25
|
-
|
|
26
|
-
Removed `_toolUses` and `_lifetimeUsage` from `AgentActivityTracker`.
|
|
27
|
-
UI consumers read stats from `AgentRecord` instead of the tracker.
|
|
28
|
-
The UI observer retains event subscriptions for re-render triggers but no longer accumulates stats independently.
|
|
29
|
-
|
|
30
|
-
Add `session` and `outputFile` convenience getters on `AgentRecord` to hide the `execution?.` traversal.
|
|
31
|
-
The 15+ callsites that navigate `record.execution?.session` simplify to `record.session`.
|
|
32
|
-
|
|
33
|
-
Apply the dependency bag convention to touched modules: `NotificationDeps` (4 fields) becomes plain parameters on `NotificationManager` constructor.
|
|
34
|
-
|
|
35
|
-
Impact: eliminates dual counting; removes `??` fallback pattern from widget and conversation viewer; hides `ExecutionState` structure from consumers.
|
|
36
|
-
|
|
37
|
-
## Step M: Decompose execute and push ExtensionContext to the boundary (#145)
|
|
38
|
-
|
|
39
|
-
Extracted config resolution into `resolveSpawnConfig` (pure function in `spawn-config.ts`).
|
|
40
|
-
Injected three collaborators (`buildSnapshot`, `getModelInfo`, `getSessionInfo`) into `createAgentTool` so `execute` no longer reads `ctx` beyond `ctx.ui` (already delegated to `widget.setUICtx`).
|
|
41
|
-
`AgentManager.spawn()` and `spawnAndWait()` accept `ParentSnapshot` instead of `ExtensionContext`.
|
|
42
|
-
`service-adapter.ts` calls `buildParentSnapshot(session.ctx)` at its boundary.
|
|
43
|
-
`foreground-runner` and `background-spawner` receive `ResolvedSpawnConfig` + domain values (`snapshot`, `parentSessionFile`, `parentSessionId`) instead of `ctx`.
|
|
44
|
-
|
|
45
|
-
Dissolved `ForegroundDeps`, `BackgroundDeps`, and `AdapterDeps` into plain parameters.
|
|
46
|
-
`AgentToolDeps` is destructured in the `createAgentTool` signature.
|
|
47
|
-
|
|
48
|
-
After this step, `ExtensionContext` appears only in:
|
|
49
|
-
|
|
50
|
-
- `index.ts` closures (wired at extension startup)
|
|
51
|
-
- `service-adapter.ts` (cross-extension boundary)
|
|
52
|
-
- Menu handlers (addressed by Step N)
|
|
53
|
-
|
|
54
|
-
Impact: `execute` dropped from ~145 to ~25 lines; eliminated 16-field parameter bags; eliminated `vi.mock("../src/parent-snapshot.js")` in `agent-manager.test.ts`; foreground/background runner tests no longer need `ctx` mocks; `AgentManager` operates entirely on domain types.
|
|
55
|
-
|
|
56
|
-
## Step N: Narrow UI context for menu handlers (#146)
|
|
57
|
-
|
|
58
|
-
Defined `MenuUI` interface (exported from `agent-menu.ts`) with `select`, `confirm`, `input`, `notify`, `editor`, and `custom` methods — the exact subset `ctx.ui` methods used by menu handlers.
|
|
59
|
-
All inner functions in `agent-menu.ts`, `agent-config-editor.ts`, and `agent-creation-wizard.ts` now accept `(ui: MenuUI)` instead of `(ctx: ExtensionContext)`.
|
|
60
|
-
`index.ts` passes `ctx.ui`, `ctx.modelRegistry`, and `buildParentSnapshot(ctx)` to the handler.
|
|
61
|
-
|
|
62
|
-
`AgentMenuManager.spawnAndWait` and `WizardManager.spawnAndWait` both accept `ParentSnapshot` (enabled by Step M).
|
|
63
|
-
Creation wizard threads `parentSnapshot` from `showCreateWizard(ui, parentSnapshot)` → `showGenerateWizard(ui, parentSnapshot, targetDir)` → `manager.spawnAndWait(parentSnapshot, ...)`.
|
|
64
|
-
|
|
65
|
-
Applied the dependency bag convention:
|
|
66
|
-
|
|
67
|
-
- `AgentConfigEditorDeps` (4 fields), `GetResultDeps` (4 fields), `SteerToolDeps` (4 fields) dissolved into plain parameters.
|
|
68
|
-
- `AgentMenuDeps` (8 fields) and `AgentCreationWizardDeps` (5 fields) kept as interfaces, destructured in the function signature.
|
|
69
|
-
|
|
70
|
-
After Steps M and N, `ExtensionContext` appears only at true boundaries: `index.ts` closures and `service-adapter.ts` (cross-extension bridge).
|
|
71
|
-
|
|
72
|
-
Impact: eliminated 42 `ctx as any` casts across 5 test files (`agent-menu.test.ts`: 8, `agent-config-editor.test.ts`: 20, `agent-creation-wizard.test.ts`: 14); tests construct plain `MenuUI`-shaped objects with no cast.
|
|
73
|
-
|
|
74
|
-
## Step O: Inject text wrapping into ConversationViewer (#147)
|
|
75
|
-
|
|
76
|
-
Accepted `wrapText: (text: string, width: number) => string[]` via `ConversationViewerOptions`.
|
|
77
|
-
`agent-menu.ts` passes the real `wrapTextWithAnsi` import at the `ConversationViewer` construction site.
|
|
78
|
-
Tests inject a stub or the real function directly via options — no module-level mock needed.
|
|
79
|
-
|
|
80
|
-
Applied the dependency bag convention: `ConversationViewerOptions` is now destructured in the constructor signature.
|
|
81
|
-
|
|
82
|
-
Impact: eliminated the hoisted `vi.mock("@earendil-works/pi-tui")` from `conversation-viewer.test.ts`; 1 test deleted (mock-mechanism sentinel); net −1 test.
|
|
83
|
-
|
|
84
|
-
## Step P: Split AgentWidget rendering (#148)
|
|
85
|
-
|
|
86
|
-
Extracted pure rendering functions (`renderWidgetLines`, `renderFinishedLine`, `renderRunningLines`) from `AgentWidget` into `ui/widget-renderer.ts`.
|
|
87
|
-
The widget is now a thin lifecycle/polling wrapper (198 lines, down from 374) that delegates to pure render functions.
|
|
88
|
-
Rendering functions receive data (agent list, activity map, registry) and return formatted strings - testable without widget lifecycle. 23 new unit tests cover all status variants, overflow, tree connectors, and empty states.
|
|
89
|
-
|
|
90
|
-
## Step dependencies
|
|
91
|
-
|
|
92
|
-
```mermaid
|
|
93
|
-
flowchart LR
|
|
94
|
-
subgraph observation["Observation track"]
|
|
95
|
-
L["L: Consolidate observation #144"] --> P["P: Split widget rendering #148"]
|
|
96
|
-
end
|
|
97
|
-
subgraph ctx["ctx elimination track"]
|
|
98
|
-
M["M: Decompose execute / push ctx #145"] --> N["N: Narrow UI context #146"]
|
|
99
|
-
end
|
|
100
|
-
O["O: Inject text wrapping #147"]
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
The three tracks are independent of each other.
|
|
104
|
-
|
|
105
|
-
## Projected impact
|
|
106
|
-
|
|
107
|
-
| Metric | Before | After |
|
|
108
|
-
| ---------------------------------- | ------------------------ | ------------------------ |
|
|
109
|
-
| `vi.mock()` calls remaining | 4 | 1 (`print-mode.test.ts`) |
|
|
110
|
-
| `as any` casts remaining | 45 | ~5 |
|
|
111
|
-
| Independent tool-use counters | 2 | 1 |
|
|
112
|
-
| `record.execution?.` traversals | 15+ | 0 |
|
|
113
|
-
| `ExtensionContext` in domain types | 1 (`AgentManager.spawn`) | 0 |
|
|
114
|
-
| `deps.` prefix accesses | ~124 | 0 |
|
|
115
|
-
|
|
116
|
-
## Related issues
|
|
117
|
-
|
|
118
|
-
- #144 — Consolidate observation model
|
|
119
|
-
- #145 — Decompose execute and push ExtensionContext to boundary
|
|
120
|
-
- #146 — Narrow UI context for menu handlers
|
|
121
|
-
- #147 — Inject text wrapping into ConversationViewer
|
|
122
|
-
- #148 — Split AgentWidget rendering
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
status: superseded
|
|
3
|
-
date: 2026-05-11
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# 0001 — Deferred fork patches and upstream-PR strategy
|
|
7
|
-
|
|
8
|
-
## Status
|
|
9
|
-
|
|
10
|
-
Superseded by [`docs/architecture/architecture.md`](../architecture/architecture.md), which commits to a hard fork with material scope reduction (scheduling removal, `SubagentsAPI` boundary, `index.ts` decomposition).
|
|
11
|
-
The original rationale below remains useful context.
|
|
12
|
-
|
|
13
|
-
## Context
|
|
14
|
-
|
|
15
|
-
This fork was created to land three pieces of work identified during RepOne issue [#442](https://github.com/Tiny-IG-Software/repone/issues/442):
|
|
16
|
-
|
|
17
|
-
1. **Peer-dep rename** — `@mariozechner/pi-*` → `@earendil-works/pi-*`.
|
|
18
|
-
2. **Patch 2 — Re-activate extension tools post-`bindExtensions`** (Spike 3 finding).
|
|
19
|
-
3. **Patch 3 — Inject `<active_agent>` tag** (Spike 4 finding).
|
|
20
|
-
|
|
21
|
-
A fourth piece of work was scoped during the same spike round but deferred:
|
|
22
|
-
|
|
23
|
-
- **Patch 1 — Mirror parent's `additionalExtensionPaths` (and siblings) into the child's `DefaultResourceLoader`** (Spike 2 finding).
|
|
24
|
-
|
|
25
|
-
This ADR records why Patch 1 was deferred and the strategy for upstream PRs back to [`tintinweb/pi-subagents`](https://github.com/tintinweb/pi-subagents).
|
|
26
|
-
|
|
27
|
-
## Decision
|
|
28
|
-
|
|
29
|
-
### Patch 1 is deferred
|
|
30
|
-
|
|
31
|
-
The original Spike 2 finding was that the parent's `additionalExtensionPaths` does not propagate to the child's `DefaultResourceLoader`.
|
|
32
|
-
The fix was sketched as "plumb parent's `additionalExtensionPaths` (and siblings) into the child."
|
|
33
|
-
|
|
34
|
-
During planning for this fork, two implementation constraints surfaced:
|
|
35
|
-
|
|
36
|
-
1. The parent's `DefaultResourceLoader.additionalExtensionPaths` is **private** — no public getter on `ExtensionContext`.
|
|
37
|
-
2. The parent's CLI flags (e.g., `pi -e <path>`) are parsed in `main.js` and not surfaced through any extension API.
|
|
38
|
-
|
|
39
|
-
A working patch would have to either:
|
|
40
|
-
|
|
41
|
-
- Accept new fields in `RunOptions` so callers supply the paths explicitly, **or**
|
|
42
|
-
- Reach into `process.argv` to re-resolve `-e`/`--extensions` flags from the child's perspective.
|
|
43
|
-
|
|
44
|
-
Neither matches the production need.
|
|
45
|
-
For RepOne (and any consumer that installs extensions via `pi install`), extensions are settings-discoverable: children inherit them independently of the parent's `DefaultResourceLoader` configuration.
|
|
46
|
-
The `pi -e <path>` ephemeral-extension case is the only beneficiary of Patch 1, and it does not appear in our workflow.
|
|
47
|
-
|
|
48
|
-
We therefore defer Patch 1 rather than carry a speculative patch in the fork's diff against upstream.
|
|
49
|
-
A follow-up issue on the RepOne board (linked from #443) captures the criterion for revisiting: **a workflow that needs `pi -e <path>` ephemeral extensions to reach children**.
|
|
50
|
-
|
|
51
|
-
### Upstream PRs are open
|
|
52
|
-
|
|
53
|
-
All three divergences now have upstream PRs, opened after production validation in RepOne:
|
|
54
|
-
|
|
55
|
-
1. **Peer-dep migration** — [tintinweb/pi-subagents#71](https://github.com/tintinweb/pi-subagents/pull/71) (`fix(deps)!: migrate from deprecated @mariozechner/pi-* to @earendil-works/pi-*`)
|
|
56
|
-
2. **Post-bind re-filter** — [tintinweb/pi-subagents#72](https://github.com/tintinweb/pi-subagents/pull/72) (`fix(agent-runner): re-filter active tools after bindExtensions so extension tools land in child`)
|
|
57
|
-
3. **Active-agent tag** — [tintinweb/pi-subagents#73](https://github.com/tintinweb/pi-subagents/pull/73) (`feat(prompts): inject <active_agent name="..."/> tag for permission resolution`)
|
|
58
|
-
|
|
59
|
-
If these land upstream, upstream gains the peer-dep fix and the two RepOne patches.
|
|
60
|
-
However, the fork now diverges intentionally beyond those patches — see [`docs/architecture/architecture.md`](../architecture/architecture.md) for the full scope of planned changes.
|
|
61
|
-
|
|
62
|
-
## Consequences
|
|
63
|
-
|
|
64
|
-
### Positive
|
|
65
|
-
|
|
66
|
-
- The fork's diff against upstream stays minimal — three patches plus tooling alignment.
|
|
67
|
-
- We avoid landing a speculative Patch 1 that would need rework if upstream's `ExtensionContext` API changes.
|
|
68
|
-
- Production evidence strengthened the upstream PRs.
|
|
69
|
-
|
|
70
|
-
### Negative
|
|
71
|
-
|
|
72
|
-
- The `pi -e <path>` ephemeral-extension case in subagents will not work until Patch 1 lands.
|
|
73
|
-
We accept this because no consumer in scope uses that pattern.
|
|
74
|
-
|
|
75
|
-
### Operational
|
|
76
|
-
|
|
77
|
-
- Upstream PRs are open and linked above.
|
|
78
|
-
If merged, upstream gains the three patches, but the fork continues independently with broader architectural changes per [`docs/architecture/architecture.md`](../architecture/architecture.md).
|
|
79
|
-
- The architecture document governs the fork's direction going forward; this ADR's original "thin-patch" framing no longer describes the fork's trajectory.
|
|
80
|
-
- When Patch 1 is eventually added, it should be a separate ADR in `docs/decisions/` with its own follow-up.
|