@gotgenes/pi-subagents 7.0.0 → 7.1.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/docs/architecture/architecture.md +188 -91
- package/docs/architecture/history/phase-10-structural-decomposition.md +141 -0
- package/docs/plans/0192-define-session-context-interface.md +107 -0
- package/docs/retro/0185-remove-persistent-agent-memory.md +34 -0
- package/docs/retro/0192-define-session-context-interface.md +35 -0
- package/package.json +1 -1
- package/src/types.ts +21 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,23 @@ 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.1.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v7.0.0...pi-subagents-v7.1.0) (2026-05-24)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **pi-subagents:** define SessionContext narrow interface ([#192](https://github.com/gotgenes/pi-packages/issues/192)) ([a043d4d](https://github.com/gotgenes/pi-packages/commit/a043d4d970a8aacc084a125d9a07b860a7fb6e9b))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Documentation
|
|
17
|
+
|
|
18
|
+
* **pi-subagents:** archive Phase 10, propose Phase 11 roadmap ([d474eef](https://github.com/gotgenes/pi-packages/commit/d474eef98c1a39757d933da291725ff126f1b8ac))
|
|
19
|
+
* **pi-subagents:** revise Phase 11 roadmap — layered class conversion ([35d1083](https://github.com/gotgenes/pi-packages/commit/35d1083445aece783e344c37fc949395e190f9e5))
|
|
20
|
+
* plan SessionContext narrow interface ([#192](https://github.com/gotgenes/pi-packages/issues/192)) ([95cb16e](https://github.com/gotgenes/pi-packages/commit/95cb16e46aa630ecc34f423aaaa4ff02845ed5b5))
|
|
21
|
+
* **retro:** add planning stage notes for issue [#192](https://github.com/gotgenes/pi-packages/issues/192) ([31fd729](https://github.com/gotgenes/pi-packages/commit/31fd7290985b0ef1d240cd5afd5e0e0e7eec9131))
|
|
22
|
+
* **retro:** add retro notes for issue [#185](https://github.com/gotgenes/pi-packages/issues/185) ([66e49cf](https://github.com/gotgenes/pi-packages/commit/66e49cfb4129b9bba3b78e0850402bc61d99dda8))
|
|
23
|
+
* **retro:** add TDD stage notes for issue [#192](https://github.com/gotgenes/pi-packages/issues/192) ([6cf3f95](https://github.com/gotgenes/pi-packages/commit/6cf3f95f6c39f3f81c1d335348d5abd74d948ff3))
|
|
24
|
+
|
|
8
25
|
## [7.0.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v6.19.1...pi-subagents-v7.0.0) (2026-05-24)
|
|
9
26
|
|
|
10
27
|
|
|
@@ -314,16 +314,16 @@ The widget reads agent state by polling a shared `Map<string, AgentActivityTrack
|
|
|
314
314
|
|
|
315
315
|
```mermaid
|
|
316
316
|
flowchart TD
|
|
317
|
-
subgraph core["@gotgenes/pi-subagents
|
|
317
|
+
subgraph core["@gotgenes/pi-subagents"]
|
|
318
318
|
direction TB
|
|
319
|
-
exports["SubagentsService
|
|
320
|
-
engine["Agent
|
|
321
|
-
ui_int["Internal UI: widget, viewer
|
|
319
|
+
exports["SubagentsService API<br/>publish / getSubagentsService<br/>SubagentRecord, SubagentStatus"]
|
|
320
|
+
engine["Tools: Agent, get_subagent_result,<br/>steer_subagent<br/>AgentManager, agent-runner"]
|
|
321
|
+
ui_int["Internal UI: widget, viewer,<br/>/agents menu"]
|
|
322
322
|
end
|
|
323
323
|
|
|
324
|
-
core -- "Symbol.for
|
|
325
|
-
core -- "Symbol.for
|
|
326
|
-
core -- "Symbol.for
|
|
324
|
+
core -- "Symbol.for on globalThis" --> sched["scheduling extension<br/>(hypothetical)"]
|
|
325
|
+
core -- "Symbol.for on globalThis" --> subui["pi-subagents-ui<br/>(deferred)"]
|
|
326
|
+
core -- "Symbol.for on globalThis" --> future["any future extension"]
|
|
327
327
|
```
|
|
328
328
|
|
|
329
329
|
Consumers call `getSubagentsService()?.spawn(...)` at runtime.
|
|
@@ -599,121 +599,218 @@ export type RunnerIO = EnvironmentIO & SessionFactoryIO;
|
|
|
599
599
|
`RunnerIO` is kept as a type alias for the intersection.
|
|
600
600
|
All existing consumers satisfy both sub-interfaces via structural typing with no call-site changes.
|
|
601
601
|
|
|
602
|
-
## Improvement roadmap (Phase
|
|
602
|
+
## Improvement roadmap (Phase 11)
|
|
603
603
|
|
|
604
|
-
Phase
|
|
604
|
+
Phase 11 converts closure factories to classes, eliminating the adapter closure density in `index.ts` (the package's #1 churn hotspot: 128 commits, accelerating, fan-out 25).
|
|
605
|
+
The approach is layered: each step makes the next step trivial.
|
|
605
606
|
|
|
606
|
-
|
|
607
|
+
> "Make the change that makes the change easy." —Kent Beck
|
|
607
608
|
|
|
608
|
-
|
|
609
|
-
All `src/` internal imports now use `#src/` path aliases (same style as `test/` files), eliminating relative depth arithmetic for future moves.
|
|
609
|
+
### Findings
|
|
610
610
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
611
|
+
| Metric | Value |
|
|
612
|
+
| ------------------------- | --------------------------------------- |
|
|
613
|
+
| Health score | 75/100 (B) |
|
|
614
|
+
| #1 hotspot | `index.ts` (128 commits, accelerating) |
|
|
615
|
+
| Dead exports | 1 (`getToolCallName` re-export) |
|
|
616
|
+
| Production duplication | 0 |
|
|
617
|
+
| Test duplication | 1,396 lines (69 clone groups, 22 files) |
|
|
618
|
+
| `as any` casts in index | 5 |
|
|
619
|
+
| Adapter closures in index | 44 |
|
|
620
|
+
| Index fan-out | 25 imports |
|
|
614
621
|
|
|
615
|
-
###
|
|
622
|
+
### Root cause
|
|
616
623
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
Enables Step 3 (narrowing AgentSpawnConfig, [#166][166]).
|
|
624
|
+
The 44 adapter closures in `index.ts` exist because the tool factories accept narrow interfaces that don't structurally match the real objects.
|
|
625
|
+
The real objects can't satisfy the interfaces because:
|
|
620
626
|
|
|
621
|
-
|
|
627
|
+
1. `SubagentRuntime.currentCtx` is typed `{ pi: unknown; ctx: unknown }` — so every consumer must `as any` cast to read fields.
|
|
628
|
+
2. Context queries (`buildSnapshot`, `getModelInfo`, `getSessionInfo`) live as closures in index.ts instead of methods on the state holder.
|
|
629
|
+
3. `AgentToolManager` mixes fields from `AgentManager` and `SettingsManager` (source mismatch).
|
|
630
|
+
4. `AgentToolWidget` uses different method names than `SubagentRuntime` (name mismatch).
|
|
622
631
|
|
|
623
|
-
|
|
624
|
-
`AgentSpawnConfig`, `BackgroundParams`, `ForegroundParams`, and `RunOptions` all carry the nested group.
|
|
632
|
+
Fix these structural misalignments and the class conversions become mechanical.
|
|
625
633
|
|
|
626
|
-
###
|
|
634
|
+
### Layer 0: Define `SessionContext` narrow interface ([#192][192])
|
|
627
635
|
|
|
628
|
-
`
|
|
629
|
-
`
|
|
630
|
-
|
|
636
|
+
`SubagentRuntime` currently types its context as `unknown` to avoid SDK coupling.
|
|
637
|
+
But `ExtensionContext` is exported by the SDK — the `unknown` is a historical choice, not a constraint.
|
|
638
|
+
Define a narrow `SessionContext` interface capturing the 5 fields runtime actually needs:
|
|
631
639
|
|
|
632
|
-
|
|
640
|
+
```typescript
|
|
641
|
+
export interface SessionContext {
|
|
642
|
+
readonly cwd: string;
|
|
643
|
+
readonly model: unknown;
|
|
644
|
+
readonly modelRegistry: ModelRegistry | undefined;
|
|
645
|
+
getSystemPrompt(): string;
|
|
646
|
+
readonly sessionManager: {
|
|
647
|
+
getSessionFile(): string | undefined;
|
|
648
|
+
getSessionId(): string;
|
|
649
|
+
getBranch(): unknown[];
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
```
|
|
633
653
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
`
|
|
654
|
+
- Target: `src/types.ts`
|
|
655
|
+
- Smell: Category C (platform type threading)
|
|
656
|
+
- Outcome: typed foundation for Layers 1–4; no `as any` needed by consumers of `SubagentRuntime`
|
|
657
|
+
|
|
658
|
+
### Layer 1: `SubagentRuntime` stores typed context, owns its queries ([#193][193])
|
|
659
|
+
|
|
660
|
+
Change `currentCtx` from `{ pi: unknown; ctx: unknown }` to `SessionContext | undefined`.
|
|
661
|
+
The single `as SessionContext` cast moves into `handleSessionStart` — the boundary where the SDK hands us the value.
|
|
662
|
+
Add typed methods: `buildSnapshot(inheritContext)`, `getModelInfo()`, `getSessionInfo()`.
|
|
663
|
+
|
|
664
|
+
- Target: `src/runtime.ts`, `src/handlers/lifecycle.ts`
|
|
665
|
+
- Smell: Category C (closure queries on mutable field → methods on state owner)
|
|
666
|
+
- Outcome: 3 closure queries in index.ts → 0; `SubagentRuntime` is self-sufficient for tool deps
|
|
667
|
+
- Enables: Layer 3 (tools accept `SubagentRuntime` directly)
|
|
668
|
+
|
|
669
|
+
### Layer 2: Align interfaces so real objects satisfy tool deps structurally ([#194][194])
|
|
637
670
|
|
|
638
|
-
|
|
671
|
+
Three alignment changes:
|
|
639
672
|
|
|
640
|
-
|
|
641
|
-
|
|
673
|
+
1. **Move `getMaxConcurrent` off `AgentToolManager`** — it reads from `SettingsManager`, not `AgentManager`.
|
|
674
|
+
The tool already receives `settings`; read it from there.
|
|
675
|
+
2. **Rename widget methods** — align `SubagentRuntime` method names with `AgentToolWidget` (either rename `updateWidget()` → `update()` on runtime, or rename the interface to match).
|
|
676
|
+
3. **Remove dead re-export** — `getToolCallName` in `ui/message-formatters.ts` (fallow finding).
|
|
642
677
|
|
|
643
|
-
|
|
678
|
+
After this step, `AgentManager` structurally satisfies `AgentToolManager` and `SubagentRuntime` structurally satisfies `AgentToolWidget`.
|
|
644
679
|
|
|
645
|
-
|
|
646
|
-
|
|
680
|
+
- Target: `src/tools/agent-tool.ts` (interface), `src/runtime.ts` (method names), `src/ui/message-formatters.ts`
|
|
681
|
+
- Smell: Category C (source mismatch, name mismatch) + Category A (dead export)
|
|
682
|
+
- Outcome: structural typing connects real objects to tool interfaces without adapters
|
|
683
|
+
- Enables: Layer 3 (class constructors accept real objects directly)
|
|
647
684
|
|
|
648
|
-
###
|
|
685
|
+
### Layer 3: Convert closure factories to classes ([#195][195], [#196][196])
|
|
649
686
|
|
|
650
|
-
|
|
651
|
-
`renderResult` reduced from ~80 lines (cognitive complexity 43) to a 10-line guard + `renderAgentResult` dispatcher.
|
|
652
|
-
The inline `stats()` closure became the exported `renderStats` helper, shared by all status renderers.
|
|
687
|
+
With Layers 0–2 complete, each factory is a mechanical conversion:
|
|
653
688
|
|
|
654
|
-
|
|
689
|
+
| Factory | Class | Constructor params |
|
|
690
|
+
| -------------------------------- | ------------------------------ | --------------------------------------------------- |
|
|
691
|
+
| `createAgentTool({...})` | `AgentTool` | `manager`, `runtime`, `settings`, `registry` |
|
|
692
|
+
| `createGetResultTool(...)` | `GetResultTool` | `manager`, `notifications`, `registry` |
|
|
693
|
+
| `createSteerTool(...)` | `SteerTool` | `manager`, `events` |
|
|
694
|
+
| `createAgentRunner(runnerIO)` | `AgentRunner` (concrete class) | `io: RunnerIO` |
|
|
695
|
+
| `createAgentsMenuHandler({...})` | `AgentsMenuHandler` | `manager`, `registry`, `settings`, `fileOps`, paths |
|
|
655
696
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
697
|
+
Each class satisfies the existing interface via structural typing.
|
|
698
|
+
The `defineTool()` wrapper moves into a `toToolDefinition()` method on each tool class.
|
|
699
|
+
|
|
700
|
+
- Target: `src/tools/*.ts`, `src/lifecycle/agent-runner.ts`, `src/ui/agent-menu.ts`
|
|
701
|
+
- Smell: Category C (closure factories masquerading as classes)
|
|
702
|
+
- Outcome: deps are constructor params (inspectable, testable); no captured closures
|
|
703
|
+
- Enables: Layer 4 (index.ts simplification)
|
|
704
|
+
|
|
705
|
+
### Layer 4: Simplify index.ts (included in [#196][196])
|
|
706
|
+
|
|
707
|
+
With real objects satisfying tool interfaces and queries living on `SubagentRuntime`, the composition root becomes pure construction:
|
|
708
|
+
|
|
709
|
+
```typescript
|
|
710
|
+
const runtime = new SubagentRuntime();
|
|
711
|
+
const settings = new SettingsManager(...);
|
|
712
|
+
const manager = new AgentManager(...);
|
|
713
|
+
const agentTool = new AgentTool(manager, runtime, settings, registry);
|
|
714
|
+
pi.registerTool(agentTool.toToolDefinition());
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
No adapter closures.
|
|
718
|
+
No `as any`.
|
|
719
|
+
Fan-out drops from 25 to ~15 (internal factories eliminated).
|
|
720
|
+
|
|
721
|
+
- Target: `src/index.ts`
|
|
722
|
+
- Smell: Category B (god file) + Category C (adapter closure density)
|
|
723
|
+
- Outcome: index.ts shrinks from 280 to ~150 lines; churn hotspot stabilizes
|
|
659
724
|
|
|
660
725
|
### Step dependencies
|
|
661
726
|
|
|
662
727
|
```mermaid
|
|
663
728
|
flowchart LR
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
S2["#165: ResolvedSpawnConfig"] --> S3["#166: AgentSpawnConfig"]
|
|
669
|
-
S4["#167: RunnerIO"]
|
|
670
|
-
S5["#168: SessionConfig"]
|
|
671
|
-
S6["#169: RunOptions"]
|
|
672
|
-
end
|
|
673
|
-
subgraph complexity["Complexity reduction"]
|
|
674
|
-
S7["#170: buildContentLines"]
|
|
675
|
-
S8["#171: renderResult"]
|
|
676
|
-
S9["#172: Shared turn-formatting"]
|
|
677
|
-
end
|
|
678
|
-
S1 --> S2 & S4 & S5 & S6
|
|
679
|
-
S1 --> S7 & S8 & S9
|
|
729
|
+
L0["Layer 0: SessionContext interface"] --> L1["Layer 1: Runtime owns queries"]
|
|
730
|
+
L1 --> L3["Layer 3: Classes replace factories"]
|
|
731
|
+
L2["Layer 2: Align interfaces"] --> L3
|
|
732
|
+
L3 --> L4["Layer 4: Simplify index.ts"]
|
|
680
733
|
```
|
|
681
734
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
735
|
+
Layers 0 and 2 are independent of each other.
|
|
736
|
+
Layer 1 depends on Layer 0.
|
|
737
|
+
Layer 3 depends on both Layer 1 and Layer 2.
|
|
738
|
+
Layer 4 depends on Layer 3.
|
|
739
|
+
|
|
740
|
+
## Improvement roadmap (Phase 12)
|
|
741
|
+
|
|
742
|
+
Phase 12 addresses the remaining fallow refactoring targets and test duplication.
|
|
743
|
+
These are independent of Phase 11 and can proceed in parallel if desired.
|
|
744
|
+
|
|
745
|
+
### Step 1: Decompose `renderWidgetLines` (cognitive 44)
|
|
746
|
+
|
|
747
|
+
`renderWidgetLines` in `ui/widget-renderer.ts` handles agent-status formatting, tree connectors, overflow, and empty states.
|
|
748
|
+
Extract per-status renderers and a tree-connector utility.
|
|
749
|
+
|
|
750
|
+
- Target: `src/ui/widget-renderer.ts`
|
|
751
|
+
- Outcome: cognitive complexity < 10
|
|
752
|
+
|
|
753
|
+
### Step 2: Decompose `showAgentDetail` (cognitive 33)
|
|
754
|
+
|
|
755
|
+
`showAgentDetail` in `ui/agent-config-editor.ts` handles display, edit, eject, and delete flows.
|
|
756
|
+
Extract sub-functions per menu action.
|
|
757
|
+
|
|
758
|
+
- Target: `src/ui/agent-config-editor.ts`
|
|
759
|
+
- Outcome: cognitive complexity < 10
|
|
760
|
+
|
|
761
|
+
### Step 3: Decompose `update` in `agent-widget.ts` (cognitive 31)
|
|
762
|
+
|
|
763
|
+
`update` mixes timer lifecycle, agent list assembly, render delegation, and visibility state.
|
|
764
|
+
Extract `assembleWidgetState` (pure) and timer management.
|
|
765
|
+
|
|
766
|
+
- Target: `src/ui/agent-widget.ts`
|
|
767
|
+
- Outcome: cognitive complexity < 10
|
|
768
|
+
|
|
769
|
+
### Step 4: Extract shared test fixtures
|
|
770
|
+
|
|
771
|
+
The 3 heaviest clone families:
|
|
772
|
+
|
|
773
|
+
- `agent-runner.test.ts` + `agent-runner-extension-tools.test.ts` (60-line shared setup)
|
|
774
|
+
- `agent-menu.test.ts` + `agent-creation-wizard.test.ts` + `agent-config-editor.test.ts` (54+51+24 lines)
|
|
775
|
+
- `agent-manager.test.ts` (18 internal clone groups, 210 duplicated lines)
|
|
776
|
+
|
|
777
|
+
Extract shared factories into `test/fixtures/` modules.
|
|
778
|
+
|
|
779
|
+
- Target: new `test/fixtures/` modules
|
|
780
|
+
- Outcome: test duplication reduced by ~400 lines
|
|
685
781
|
|
|
686
782
|
## Refactoring history
|
|
687
783
|
|
|
688
|
-
Phases 1–5 and 7–
|
|
784
|
+
Phases 1–5 and 7–10 are complete.
|
|
689
785
|
Phase 6 (UI extraction to a separate package) is deferred.
|
|
690
786
|
Detailed records are preserved in per-phase history files:
|
|
691
787
|
|
|
692
|
-
| Phase | Title | Status | History
|
|
693
|
-
| ----- | --------------------------------------------------- | -------- |
|
|
694
|
-
| 1 | Export SubagentsService API boundary | Complete | [phase-1-api-boundary.md](history/phase-1-api-boundary.md)
|
|
695
|
-
| 2 | Remove scheduling subsystem | Complete | [phase-2-remove-scheduling.md](history/phase-2-remove-scheduling.md)
|
|
696
|
-
| 3 | Remove group-join, RPC; replace output-file | Complete | [phase-3-remove-rpc-groupjoin.md](history/phase-3-remove-rpc-groupjoin.md)
|
|
697
|
-
| 4 | Implement and publish SubagentsService | Complete | [phase-4-implement-service.md](history/phase-4-implement-service.md)
|
|
698
|
-
| 5 | Decompose index.ts | Complete | [phase-5-decompose-index.md](history/phase-5-decompose-index.md)
|
|
699
|
-
| 6 | Extract UI to separate package | Deferred | —
|
|
700
|
-
| 7 | Encapsulation and dependency narrowing | Complete | [phase-7-encapsulation.md](history/phase-7-encapsulation.md)
|
|
701
|
-
| 8 | Testability, display extraction, menu decomposition | Complete | [phase-8-testability.md](history/phase-8-testability.md)
|
|
702
|
-
| 9 | Observation consolidation, ctx elimination | Complete | [phase-9-observation-ctx.md](history/phase-9-observation-ctx.md)
|
|
788
|
+
| Phase | Title | Status | History |
|
|
789
|
+
| ----- | --------------------------------------------------- | -------- | ------------------------------------------------------------------------------------ |
|
|
790
|
+
| 1 | Export SubagentsService API boundary | Complete | [phase-1-api-boundary.md](history/phase-1-api-boundary.md) |
|
|
791
|
+
| 2 | Remove scheduling subsystem | Complete | [phase-2-remove-scheduling.md](history/phase-2-remove-scheduling.md) |
|
|
792
|
+
| 3 | Remove group-join, RPC; replace output-file | Complete | [phase-3-remove-rpc-groupjoin.md](history/phase-3-remove-rpc-groupjoin.md) |
|
|
793
|
+
| 4 | Implement and publish SubagentsService | Complete | [phase-4-implement-service.md](history/phase-4-implement-service.md) |
|
|
794
|
+
| 5 | Decompose index.ts | Complete | [phase-5-decompose-index.md](history/phase-5-decompose-index.md) |
|
|
795
|
+
| 6 | Extract UI to separate package | Deferred | — |
|
|
796
|
+
| 7 | Encapsulation and dependency narrowing | Complete | [phase-7-encapsulation.md](history/phase-7-encapsulation.md) |
|
|
797
|
+
| 8 | Testability, display extraction, menu decomposition | Complete | [phase-8-testability.md](history/phase-8-testability.md) |
|
|
798
|
+
| 9 | Observation consolidation, ctx elimination | Complete | [phase-9-observation-ctx.md](history/phase-9-observation-ctx.md) |
|
|
799
|
+
| 10 | Domain organization, bag decomposition, complexity | Complete | [phase-10-structural-decomposition.md](history/phase-10-structural-decomposition.md) |
|
|
703
800
|
|
|
704
801
|
### Structural refactoring issues
|
|
705
802
|
|
|
706
|
-
| Phase | Issue | Summary
|
|
707
|
-
| ------------------ | ---------------------------------------------------------- |
|
|
708
|
-
| Foundation | #69, #71, #76, #80 | SubagentRuntime, pure assembler, cwd injection, config consolidation
|
|
709
|
-
| Core decomposition | #84, #72, #87, #70 | WorktreeManager, AgentManager DI, runtime methods, handler extraction
|
|
710
|
-
| Interface polish | #66, #77 | SDK types, projectAgentsDir
|
|
711
|
-
| Features | #61 | JSONL session transcripts
|
|
712
|
-
| AgentManager | #98, #99, #100, #102 | Record state machine, ParentSnapshot, session-event observation, test factory
|
|
713
|
-
| Encapsulation | #108, #109, #110, #111, #112, #113, #114, #115, #116, #118 | Registry, settings, activity tracker, record lifecycle, observer, spawn options, deps narrowing, tool split, type housekeeping
|
|
714
|
-
| Testability | #131, #132, #133, #134, #135, #136 | Shared fixtures, session-config IO, runner SDK boundary, as-any reduction, display extraction, menu decomposition
|
|
715
|
-
| Observation/ctx | #144, #145, #146, #147, #148 | Observation consolidation, execute decomposition, UI context, text wrapping injection, widget rendering split
|
|
716
|
-
| Phase 10 | #164, #166, #167, #168, #169, #170, #171
|
|
803
|
+
| Phase | Issue | Summary |
|
|
804
|
+
| ------------------ | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
805
|
+
| Foundation | #69, #71, #76, #80 | SubagentRuntime, pure assembler, cwd injection, config consolidation |
|
|
806
|
+
| Core decomposition | #84, #72, #87, #70 | WorktreeManager, AgentManager DI, runtime methods, handler extraction |
|
|
807
|
+
| Interface polish | #66, #77 | SDK types, projectAgentsDir |
|
|
808
|
+
| Features | #61 | JSONL session transcripts |
|
|
809
|
+
| AgentManager | #98, #99, #100, #102 | Record state machine, ParentSnapshot, session-event observation, test factory |
|
|
810
|
+
| Encapsulation | #108, #109, #110, #111, #112, #113, #114, #115, #116, #118 | Registry, settings, activity tracker, record lifecycle, observer, spawn options, deps narrowing, tool split, type housekeeping |
|
|
811
|
+
| Testability | #131, #132, #133, #134, #135, #136 | Shared fixtures, session-config IO, runner SDK boundary, as-any reduction, display extraction, menu decomposition |
|
|
812
|
+
| Observation/ctx | #144, #145, #146, #147, #148 | Observation consolidation, execute decomposition, UI context, text wrapping injection, widget rendering split |
|
|
813
|
+
| Phase 10 | #164, #165, #166, #167, #168, #169, #170, #171, #172 | Domain directories, ResolvedSpawnConfig, ParentSessionInfo, RunnerIO split, ToolFilterConfig, RunContext, buildContentLines, renderResult, content-items |
|
|
717
814
|
|
|
718
815
|
The remaining open issue is #22 (parent-session resolution), a cross-extension track that does not gate the structural work.
|
|
719
816
|
|
|
@@ -733,12 +830,12 @@ The upstream test suite is run periodically as a regression canary for the agent
|
|
|
733
830
|
[gotgenes/pi-packages]: https://github.com/gotgenes/pi-packages
|
|
734
831
|
[tintinweb/pi-subagents]: https://github.com/tintinweb/pi-subagents
|
|
735
832
|
|
|
736
|
-
[164]: https://github.com/gotgenes/pi-packages/issues/164
|
|
737
|
-
[165]: https://github.com/gotgenes/pi-packages/issues/165
|
|
738
833
|
[166]: https://github.com/gotgenes/pi-packages/issues/166
|
|
739
834
|
[167]: https://github.com/gotgenes/pi-packages/issues/167
|
|
740
835
|
[168]: https://github.com/gotgenes/pi-packages/issues/168
|
|
741
836
|
[169]: https://github.com/gotgenes/pi-packages/issues/169
|
|
742
|
-
[
|
|
743
|
-
[
|
|
744
|
-
[
|
|
837
|
+
[192]: https://github.com/gotgenes/pi-packages/issues/192
|
|
838
|
+
[193]: https://github.com/gotgenes/pi-packages/issues/193
|
|
839
|
+
[194]: https://github.com/gotgenes/pi-packages/issues/194
|
|
840
|
+
[195]: https://github.com/gotgenes/pi-packages/issues/195
|
|
841
|
+
[196]: https://github.com/gotgenes/pi-packages/issues/196
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Phase 10: Domain organization, bag decomposition, and complexity reduction
|
|
2
|
+
|
|
3
|
+
Target: reorganize source into domain directories; decompose remaining wide parameter bags into focused value objects; reduce cyclomatic complexity in rendering functions; eliminate production code duplication.
|
|
4
|
+
|
|
5
|
+
## Current smells
|
|
6
|
+
|
|
7
|
+
| Smell | Location | Evidence | Severity |
|
|
8
|
+
| ------------------------------------------------------- | -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | -------- |
|
|
9
|
+
| Flat `src/` directory | `src/` root | 20+ files in a single directory with no domain grouping; hard to see module boundaries | Medium |
|
|
10
|
+
| `ResolvedSpawnConfig` is a 15-field bag | `spawn-config.ts` | Mixes identity (name, slug), execution (model, permissions), and presentation (icon, color) concerns in one interface | Medium |
|
|
11
|
+
| `AgentSpawnConfig` threads parent session fields | `agent-manager.ts` | `parentSessionFile`, `parentSessionId`, `toolCallId` always travel together but are separate parameters | Low |
|
|
12
|
+
| `RunnerIO` is too wide | `foreground-runner.ts` | 9 methods spanning environment concerns (cwd, env) and session factory concerns (create, attach, resume) | Medium |
|
|
13
|
+
| `SessionConfig` mixes tool filtering with session setup | `session-config.ts` | `toolNames`, `disallowedSet`, `extensions` are a cohesive filter group buried in a larger config | Low |
|
|
14
|
+
| `RunOptions` is a 12-field bag | `foreground-runner.ts` | Mixes execution context (exec, registry, cwd) with per-run options (model, prompt, permissions) | Medium |
|
|
15
|
+
| `buildContentLines` high complexity | `ui/build-content-lines.ts` | ~60-line function with a switch over every content type; each branch is an independent formatter | Medium |
|
|
16
|
+
| `renderResult` high complexity | `tools/agent-tool.ts` | ~80-line function with status-based branching; each status is an independent rendering concern | Medium |
|
|
17
|
+
| Duplicated turn-formatting logic | `session/session-runner.ts`, `tools/agent-tool.ts` | 18 lines of identical `ToolCallContent` extraction and `getToolCallName` logic in two production files | Low |
|
|
18
|
+
|
|
19
|
+
## Step 1: Reorganize source into domain directories (#164)
|
|
20
|
+
|
|
21
|
+
Moved files into `config/`, `session/`, `lifecycle/`, `observation/`, and `service/` subdirectories.
|
|
22
|
+
All `src/` internal imports now use `#src/` path aliases.
|
|
23
|
+
Root `src/` reduced to 5 files + 8 directories.
|
|
24
|
+
|
|
25
|
+
Impact: domain boundaries are visible in the directory tree; imports communicate intent via path alias.
|
|
26
|
+
|
|
27
|
+
## Step 2: Decompose ResolvedSpawnConfig (#165)
|
|
28
|
+
|
|
29
|
+
Split the 15-field `ResolvedSpawnConfig` into three focused value objects:
|
|
30
|
+
|
|
31
|
+
- `SpawnIdentity` — name, slug, key
|
|
32
|
+
- `SpawnExecution` — model, permissions, tools, prompt, systemPrompt
|
|
33
|
+
- `SpawnPresentation` — icon, color, description
|
|
34
|
+
|
|
35
|
+
`ResolvedSpawnConfig` composes these three plus the remaining spawn-level fields.
|
|
36
|
+
|
|
37
|
+
Impact: each consumer receives only the fields it needs; adding a presentation field no longer touches execution code.
|
|
38
|
+
|
|
39
|
+
## Step 3: Extract ParentSessionInfo from AgentSpawnConfig (#166)
|
|
40
|
+
|
|
41
|
+
Extracted `parentSessionFile`, `parentSessionId`, and `toolCallId` into `ParentSessionInfo`.
|
|
42
|
+
`AgentSpawnConfig` and `AgentManager.spawn()` accept the single value object instead of three separate parameters.
|
|
43
|
+
|
|
44
|
+
Impact: eliminated parameter co-travel; the three fields that always move together are now a single concept.
|
|
45
|
+
|
|
46
|
+
## Step 4: Narrow RunnerIO (#167)
|
|
47
|
+
|
|
48
|
+
Split `RunnerIO` into two focused interfaces:
|
|
49
|
+
|
|
50
|
+
- `EnvironmentIO` (3 methods) — cwd, env, platform concerns
|
|
51
|
+
- `SessionFactoryIO` (5+1 methods) — create, attach, resume, and related session lifecycle
|
|
52
|
+
|
|
53
|
+
`RunnerIO` kept as a backward-compatible type alias (`EnvironmentIO & SessionFactoryIO`).
|
|
54
|
+
|
|
55
|
+
Impact: consumers declare which IO surface they actually need; test doubles shrink to the relevant subset.
|
|
56
|
+
|
|
57
|
+
## Step 5: Extract ToolFilterConfig from SessionConfig (#168)
|
|
58
|
+
|
|
59
|
+
Grouped `toolNames`, `disallowedSet`, and `extensions` into `ToolFilterConfig`.
|
|
60
|
+
`filterActiveTools` accepts a single `ToolFilterConfig` argument instead of three separate parameters.
|
|
61
|
+
|
|
62
|
+
Impact: tool-filtering concern is encapsulated; `SessionConfig` is narrower and more focused.
|
|
63
|
+
|
|
64
|
+
## Step 6: Extract RunContext from RunOptions (#169)
|
|
65
|
+
|
|
66
|
+
Extracted `exec`, `registry`, `cwd`, and `parentSession` into `RunContext`.
|
|
67
|
+
`RunOptions` reduced from 12 fields to 9 (the 4 extracted fields replaced by a single `context` field, plus the remaining per-run options).
|
|
68
|
+
|
|
69
|
+
Impact: execution context is a reusable value object; `RunOptions` focuses on per-run configuration.
|
|
70
|
+
|
|
71
|
+
## Step 7: Reduce buildContentLines complexity (#170)
|
|
72
|
+
|
|
73
|
+
Extracted per-content-type formatters into `ui/message-formatters.ts`.
|
|
74
|
+
Each content type (text, tool-use, tool-result, image, etc.) has a dedicated pure function.
|
|
75
|
+
`buildContentLines` is now a ~30-line dispatch loop that delegates to the appropriate formatter.
|
|
76
|
+
|
|
77
|
+
Impact: cyclomatic complexity of `buildContentLines` dropped from ~15 to ~5; each formatter is independently testable.
|
|
78
|
+
|
|
79
|
+
## Step 8: Reduce renderResult complexity (#171)
|
|
80
|
+
|
|
81
|
+
Extracted per-status formatters into `tools/result-renderer.ts`.
|
|
82
|
+
Each result status (success, error, timeout, cancelled) has a dedicated pure function.
|
|
83
|
+
`renderResult` reduced from ~80 lines to a 10-line guard that dispatches to the appropriate renderer.
|
|
84
|
+
|
|
85
|
+
Impact: cyclomatic complexity of `renderResult` dropped from ~10 to ~3; status rendering is independently testable.
|
|
86
|
+
|
|
87
|
+
## Step 9: Extract shared turn-formatting logic (#172)
|
|
88
|
+
|
|
89
|
+
Extracted `ToolCallContent`, `getToolCallName`, and `extractAssistantContent` into `session/content-items.ts`.
|
|
90
|
+
Both `session/session-runner.ts` and `tools/agent-tool.ts` import from the shared module.
|
|
91
|
+
|
|
92
|
+
Impact: eliminated 18 lines of production code duplication; single source of truth for turn-content extraction.
|
|
93
|
+
|
|
94
|
+
## Step dependencies
|
|
95
|
+
|
|
96
|
+
```mermaid
|
|
97
|
+
flowchart LR
|
|
98
|
+
subgraph bag["Bag decomposition track"]
|
|
99
|
+
S2["2: Decompose ResolvedSpawnConfig #165"] --> S3["3: Extract ParentSessionInfo #166"]
|
|
100
|
+
S4["4: Narrow RunnerIO #167"]
|
|
101
|
+
S5["5: Extract ToolFilterConfig #168"]
|
|
102
|
+
S6["6: Extract RunContext #169"]
|
|
103
|
+
end
|
|
104
|
+
subgraph complexity["Complexity reduction track"]
|
|
105
|
+
S7["7: Reduce buildContentLines #170"]
|
|
106
|
+
S8["8: Reduce renderResult #171"]
|
|
107
|
+
S9["9: Extract turn-formatting #172"]
|
|
108
|
+
end
|
|
109
|
+
S1["1: Reorganize into domain dirs #164"] --> bag
|
|
110
|
+
S1 --> complexity
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Step 1 unblocked all other steps.
|
|
114
|
+
Within the bag decomposition track, Step 2 enabled Step 3.
|
|
115
|
+
The bag decomposition and complexity reduction tracks were independent of each other.
|
|
116
|
+
|
|
117
|
+
## Impact
|
|
118
|
+
|
|
119
|
+
| Metric | Before | After |
|
|
120
|
+
| ------------------------------ | ------------------------------ | --------------------------------------------- |
|
|
121
|
+
| Health score | ~65 | 75 |
|
|
122
|
+
| `src/` root files | 20+ | 5 files + 8 directories |
|
|
123
|
+
| `ResolvedSpawnConfig` fields | 15 | 3 composed value objects |
|
|
124
|
+
| `RunOptions` fields | 12 | 9 |
|
|
125
|
+
| `RunnerIO` methods | 9 | 3 (`EnvironmentIO`) + 6 (`SessionFactoryIO`) |
|
|
126
|
+
| `buildContentLines` complexity | ~15 | ~5 |
|
|
127
|
+
| `renderResult` lines | ~80 | ~10 |
|
|
128
|
+
| Production duplication | 18 lines | 0 |
|
|
129
|
+
| Test duplication | ~1,400 lines (69 clone groups) | ~1,400 lines (69 clone groups) — not targeted |
|
|
130
|
+
|
|
131
|
+
## Related issues
|
|
132
|
+
|
|
133
|
+
- #164 — Reorganize source into domain directories
|
|
134
|
+
- #165 — Decompose ResolvedSpawnConfig
|
|
135
|
+
- #166 — Extract ParentSessionInfo from AgentSpawnConfig
|
|
136
|
+
- #167 — Narrow RunnerIO
|
|
137
|
+
- #168 — Extract ToolFilterConfig from SessionConfig
|
|
138
|
+
- #169 — Extract RunContext from RunOptions
|
|
139
|
+
- #170 — Reduce buildContentLines complexity
|
|
140
|
+
- #171 — Reduce renderResult complexity
|
|
141
|
+
- #172 — Extract shared turn-formatting logic
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 192
|
|
3
|
+
issue_title: "Define SessionContext narrow interface"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Define `SessionContext` narrow interface
|
|
7
|
+
|
|
8
|
+
## Problem Statement
|
|
9
|
+
|
|
10
|
+
`SubagentRuntime.currentCtx` is typed `{ pi: unknown; ctx: unknown }`.
|
|
11
|
+
Every consumer must cast through `as any` to read fields from the SDK context.
|
|
12
|
+
This forces context queries (`buildSnapshot`, `getModelInfo`, `getSessionInfo`) to live as closures in `index.ts` with repeated `as any` casts, rather than as typed methods on the state holder.
|
|
13
|
+
|
|
14
|
+
The SDK exports `ExtensionContext` — the `unknown` typing is a historical choice, not a constraint.
|
|
15
|
+
|
|
16
|
+
## Goals
|
|
17
|
+
|
|
18
|
+
- Define a narrow `SessionContext` interface in `src/types.ts` capturing the 5 fields `SubagentRuntime` actually reads.
|
|
19
|
+
- Pure additive — no consumers change in this step.
|
|
20
|
+
- Provide the typed foundation for Layer 1 (#193) and subsequent closure-to-class conversion issues.
|
|
21
|
+
|
|
22
|
+
## Non-Goals
|
|
23
|
+
|
|
24
|
+
- Changing `SubagentRuntime.currentCtx` type (that's #193).
|
|
25
|
+
- Converting closure factories to classes (#195, #196).
|
|
26
|
+
- Removing any `as any` casts from `index.ts` (that's #193).
|
|
27
|
+
|
|
28
|
+
## Background
|
|
29
|
+
|
|
30
|
+
Phase 11, Layer 0 in `docs/architecture/architecture.md`.
|
|
31
|
+
This is the first step in a 5-issue sequence (issues #192–#196) that converts closure factories to classes, eliminating 44 adapter closures in `index.ts`.
|
|
32
|
+
|
|
33
|
+
The SDK's `ExtensionContext` interface (in `@earendil-works/pi-coding-agent`) is broad — it exposes `ui`, `abort()`, `shutdown()`, `compact()`, etc.
|
|
34
|
+
ISP (Interface Segregation Principle) from `code-design` mandates a narrow interface capturing only what `SubagentRuntime` needs.
|
|
35
|
+
|
|
36
|
+
The 5 fields consumed by runtime (traced from `index.ts` lines 214–223 and `lifecycle/parent-snapshot.ts`):
|
|
37
|
+
|
|
38
|
+
1. `cwd` — working directory for agent sessions.
|
|
39
|
+
2. `model` — parent model instance for fallback resolution.
|
|
40
|
+
3. `modelRegistry` — resolving config model strings.
|
|
41
|
+
4. `getSystemPrompt()` — system prompt for append-mode agents.
|
|
42
|
+
5. `sessionManager.getSessionFile()` / `.getSessionId()` / `.getBranch()` — session identification and context inheritance.
|
|
43
|
+
|
|
44
|
+
The local `ModelRegistry` interface (in `src/session/model-resolver.ts`) already exists as a narrow ISP interface.
|
|
45
|
+
`SessionContext` will reference it rather than redeclaring model-registry methods inline.
|
|
46
|
+
|
|
47
|
+
## Design Overview
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import type { ModelRegistry } from "#src/session/model-resolver";
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Narrow interface capturing the 5 ExtensionContext fields SubagentRuntime needs.
|
|
54
|
+
* Avoids coupling runtime to the full SDK ExtensionContext surface.
|
|
55
|
+
*/
|
|
56
|
+
export interface SessionContext {
|
|
57
|
+
readonly cwd: string;
|
|
58
|
+
readonly model: unknown;
|
|
59
|
+
readonly modelRegistry: ModelRegistry | undefined;
|
|
60
|
+
getSystemPrompt(): string;
|
|
61
|
+
readonly sessionManager: {
|
|
62
|
+
getSessionFile(): string | undefined;
|
|
63
|
+
getSessionId(): string;
|
|
64
|
+
getBranch(): unknown[];
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Design decisions:
|
|
70
|
+
|
|
71
|
+
1. `model` stays `unknown` — the runtime only passes it through to `resolveModel`; narrowing it gains nothing and would couple to `@earendil-works/pi-ai`'s `Model<Api>` generic.
|
|
72
|
+
2. `modelRegistry` is `ModelRegistry | undefined` — the SDK type says `ModelRegistry` (non-optional), but `SubagentRuntime.currentCtx` can be undefined, and the architecture doc specifies this signature.
|
|
73
|
+
The `| undefined` reflects reality at the cast boundary (pre-bind, the registry may not exist).
|
|
74
|
+
3. `sessionManager` uses an inline structural type rather than importing `ReadonlySessionManager` — we only need 3 of its 13 methods; a separate named type would be over-engineering for a nested structural slice.
|
|
75
|
+
4. `getBranch()` returns `unknown[]` — the runtime passes entries through to `buildParentContext()` which already type-narrows internally.
|
|
76
|
+
|
|
77
|
+
## Module-Level Changes
|
|
78
|
+
|
|
79
|
+
| File | Change |
|
|
80
|
+
| -------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
81
|
+
| `src/types.ts` | Add `SessionContext` interface export. Add `import type { ModelRegistry }` from `#src/session/model-resolver`. |
|
|
82
|
+
|
|
83
|
+
No other files change — this is pure additive.
|
|
84
|
+
|
|
85
|
+
## Test Impact Analysis
|
|
86
|
+
|
|
87
|
+
1. No new unit tests are needed — `SessionContext` is a pure type definition with no runtime behavior.
|
|
88
|
+
2. No existing tests become redundant.
|
|
89
|
+
3. A compile-time check (`pnpm run check`) verifies the interface is well-formed and the import resolves.
|
|
90
|
+
|
|
91
|
+
## TDD Order
|
|
92
|
+
|
|
93
|
+
1. **Add `SessionContext` interface to `src/types.ts`** — add the interface with its import.
|
|
94
|
+
Verify with `pnpm run check` (type-check passes).
|
|
95
|
+
Commit: `feat(pi-subagents): define SessionContext narrow interface (#192)`
|
|
96
|
+
|
|
97
|
+
## Risks and Mitigations
|
|
98
|
+
|
|
99
|
+
| Risk | Mitigation |
|
|
100
|
+
| ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
101
|
+
| Interface shape doesn't match real `ExtensionContext` at runtime | Traced all 5 fields against SDK `.d.ts` declarations; shapes align exactly. |
|
|
102
|
+
| Circular import from `types.ts` → `session/model-resolver.ts` | `model-resolver.ts` does not import from `types.ts`; no cycle. |
|
|
103
|
+
| Future SDK changes break the narrow interface | The cast boundary (Layer 1, #193) will be the single enforcement point — structural typing ensures compile-time detection. |
|
|
104
|
+
|
|
105
|
+
## Open Questions
|
|
106
|
+
|
|
107
|
+
None — the issue's "Proposed change" section fully specifies the interface shape.
|
|
@@ -37,3 +37,37 @@ New file `safe-fs.test.ts` was created with 13 tests for the extracted utilities
|
|
|
37
37
|
Fix: inline the literal union `"user" | "project" | "local"` directly in `memory.ts` as a local type, so it compiles cleanly until deletion in step 4.
|
|
38
38
|
- The `SKILL.md` for `package-pi-subagents` also listed `memory.ts` in the session domain table — updated alongside `architecture.md` in the docs commit.
|
|
39
39
|
- No deviations from the plan other than the two minor bugs above (both self-corrected within the same TDD step).
|
|
40
|
+
|
|
41
|
+
## Stage: Final Retrospective (2026-05-24T22:47:55Z)
|
|
42
|
+
|
|
43
|
+
### Session summary
|
|
44
|
+
|
|
45
|
+
Shipped issue #185 as `pi-subagents-v7.0.0`.
|
|
46
|
+
CI passed, issue closed, release-please PR #190 merged.
|
|
47
|
+
Three sessions total: planning, TDD (5 steps / 5 commits), shipping.
|
|
48
|
+
|
|
49
|
+
### Observations
|
|
50
|
+
|
|
51
|
+
#### What went well
|
|
52
|
+
|
|
53
|
+
- The issue's "Scope" section was precise enough that the planning session required no `ask_user` and the Explore agent's trace matched the final commit diff exactly.
|
|
54
|
+
- Consumers-first, declaration-last ordering kept each commit independently compilable (after the two self-corrected fixes).
|
|
55
|
+
- The `SKILL.md` domain table update was caught naturally during the docs step even though the plan didn't list it.
|
|
56
|
+
|
|
57
|
+
#### What caused friction (agent side)
|
|
58
|
+
|
|
59
|
+
- `missing-context` — In TDD step 1, `memory.ts` was updated to re-export `isUnsafeName` from `safe-fs`, but the function was not imported into `memory.ts`'s own scope.
|
|
60
|
+
`resolveMemoryDir` threw a `ReferenceError` at runtime.
|
|
61
|
+
Impact: one extra test-run cycle (~5 seconds) and a trivial one-line fix; no rework commit.
|
|
62
|
+
- `missing-context` — In TDD step 3, removing `MemoryScope` from `types.ts` broke `memory.ts` (scheduled for deletion in step 4).
|
|
63
|
+
The plan said "consumers-first" but didn't account for the doomed module itself being a consumer of the type.
|
|
64
|
+
Impact: one extra `pnpm run check` cycle and a local type inline; no rework commit.
|
|
65
|
+
Both share the same root cause: incremental deletion plans must account for doomed files' own imports at each intermediate step.
|
|
66
|
+
|
|
67
|
+
#### What caused friction (user side)
|
|
68
|
+
|
|
69
|
+
- None observed — all three sessions ran without user corrections or redirections.
|
|
70
|
+
|
|
71
|
+
### Changes made
|
|
72
|
+
|
|
73
|
+
1. Added a TDD planning rule to `.pi/skills/testing/SKILL.md` about accounting for doomed modules' own imports during multi-step deletion.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 192
|
|
3
|
+
issue_title: "Define SessionContext narrow interface"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Retro: #192 — Define SessionContext narrow interface
|
|
7
|
+
|
|
8
|
+
## Stage: Planning (2026-05-24T16:00:00Z)
|
|
9
|
+
|
|
10
|
+
### Session summary
|
|
11
|
+
|
|
12
|
+
Planned the pure-additive `SessionContext` interface for `src/types.ts`.
|
|
13
|
+
Traced all 5 consumed fields against the SDK's `ExtensionContext` type declarations to confirm shape alignment.
|
|
14
|
+
Single TDD step: add the interface and verify with `pnpm run check`.
|
|
15
|
+
|
|
16
|
+
### Observations
|
|
17
|
+
|
|
18
|
+
- The interface is trivial in scope — one new export with no consumers changing.
|
|
19
|
+
This is intentionally the smallest possible first step to unblock Layer 1 (#193).
|
|
20
|
+
- `ModelRegistry` already exists as a local narrow interface in `src/session/model-resolver.ts`; `SessionContext` imports it rather than redeclaring.
|
|
21
|
+
- `sessionManager` uses an inline structural type (3 methods) rather than importing the SDK's `ReadonlySessionManager` (13 methods) — ISP applies here.
|
|
22
|
+
- No design ambiguity required `ask_user`; the issue's proposed change section was fully specified.
|
|
23
|
+
|
|
24
|
+
## Stage: Implementation — TDD (2026-05-24T19:55:00Z)
|
|
25
|
+
|
|
26
|
+
### Session summary
|
|
27
|
+
|
|
28
|
+
Added the `SessionContext` interface to `src/types.ts` with an `import type { ModelRegistry }` from `#src/session/model-resolver`.
|
|
29
|
+
Single compile-time step — no runtime tests needed for a pure type definition.
|
|
30
|
+
Baseline: 53 test files, 848 tests; final: unchanged.
|
|
31
|
+
|
|
32
|
+
### Observations
|
|
33
|
+
|
|
34
|
+
- Pre-existing lint failure in `docs/architecture/architecture.md` (5 unused MD053 link references for issues #164, #165, #170, #171, #172) was fixed as part of the baseline verification and included in the feat commit.
|
|
35
|
+
- The interface landed exactly as planned — no deviations from the plan's Design Overview.
|
package/package.json
CHANGED
package/src/types.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import type { ThinkingLevel } from "@earendil-works/pi-ai";
|
|
6
6
|
import type { AgentSessionEvent } from "@earendil-works/pi-coding-agent";
|
|
7
|
+
import type { ModelRegistry } from "#src/session/model-resolver";
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
export { AgentRecord } from "#src/lifecycle/agent-record";
|
|
@@ -77,6 +78,26 @@ export interface AgentInvocation {
|
|
|
77
78
|
isolation?: IsolationMode;
|
|
78
79
|
}
|
|
79
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Narrow shell-exec callback replacing `ExtensionAPI` in `detectEnv()`.
|
|
83
|
+
* Matches the shape of `pi.exec()` without carrying an SDK dependency.
|
|
84
|
+
*/
|
|
85
|
+
/**
|
|
86
|
+
* Narrow interface capturing the ExtensionContext fields SubagentRuntime needs.
|
|
87
|
+
* Avoids coupling runtime to the full SDK ExtensionContext surface (ISP).
|
|
88
|
+
*/
|
|
89
|
+
export interface SessionContext {
|
|
90
|
+
readonly cwd: string;
|
|
91
|
+
readonly model: unknown;
|
|
92
|
+
readonly modelRegistry: ModelRegistry | undefined;
|
|
93
|
+
getSystemPrompt(): string;
|
|
94
|
+
readonly sessionManager: {
|
|
95
|
+
getSessionFile(): string | undefined;
|
|
96
|
+
getSessionId(): string;
|
|
97
|
+
getBranch(): unknown[];
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
80
101
|
/**
|
|
81
102
|
* Narrow shell-exec callback replacing `ExtensionAPI` in `detectEnv()`.
|
|
82
103
|
* Matches the shape of `pi.exec()` without carrying an SDK dependency.
|