@gotgenes/pi-subagents 5.7.0 → 5.8.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 CHANGED
@@ -5,6 +5,19 @@ 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
+ ## [5.8.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v5.7.0...pi-subagents-v5.8.0) (2026-05-20)
9
+
10
+
11
+ ### Features
12
+
13
+ * add SessionLifecycleHandler ([d8d95fa](https://github.com/gotgenes/pi-packages/commit/d8d95fa92561e0068444c3c925f21b50825a741c))
14
+ * add ToolStartHandler ([c293e41](https://github.com/gotgenes/pi-packages/commit/c293e41e25d9742f807f85d0efe0a0e5605b29a6))
15
+
16
+
17
+ ### Documentation
18
+
19
+ * **retro:** add retro notes for issue [#87](https://github.com/gotgenes/pi-packages/issues/87) ([1701fe4](https://github.com/gotgenes/pi-packages/commit/1701fe41d582d7180b2622c2e03db954e8d2c2af))
20
+
8
21
  ## [5.7.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v5.6.0...pi-subagents-v5.7.0) (2026-05-20)
9
22
 
10
23
 
@@ -0,0 +1,42 @@
1
+ ---
2
+ issue: 87
3
+ issue_title: "refactor: evolve SubagentRuntime from data bag to object with methods"
4
+ ---
5
+
6
+ # Retro: #87 — evolve SubagentRuntime from data bag to object with methods
7
+
8
+ ## Final Retrospective (2026-05-20T18:00:00Z)
9
+
10
+ ### Session summary
11
+
12
+ Planned, implemented, and shipped #87 — converting `SubagentRuntime` from a plain interface + factory into a class with session-context methods (`setSessionContext`, `clearSessionContext`) and widget delegation methods (`setUICtx`, `onTurnStart`, `markFinished`, `updateWidget`, `ensureTimer`).
13
+ All 10 call sites in `index.ts` were migrated, eliminating raw `currentCtx` field writes and `runtime.widget!` reach-throughs.
14
+ Released as `pi-subagents-v5.7.0`.
15
+
16
+ ### Observations
17
+
18
+ #### What went well
19
+
20
+ - The 3-step TDD cycle executed cleanly with zero rework or deviations from the plan.
21
+ Test count went from 5 → 16 in `runtime.test.ts`; all 512 package tests stayed green throughout.
22
+ - The user identified a missing architecture update and a pre-existing hallucination (`@earendil-works/pi-subagents` → `@gotgenes/pi-subagents`) after the plan commit.
23
+ The fix was a single well-scoped commit (`ddee1a0`) that corrected 10 scope references, updated the dependency graph to include #87 as a precursor to #70, and fixed list numbering per phase heading.
24
+ - The plan's "Call sites to migrate" tables with exact line numbers and before/after code made step 3 (the refactoring commit) purely mechanical — no design decisions at implementation time.
25
+
26
+ #### What caused friction (agent side)
27
+
28
+ - `missing-context` — In TDD step 2, the new `describe` block used `vi.fn()` but the test file's import was `{ describe, expect, it }` without `vi`.
29
+ The first red run surfaced this immediately (`ReferenceError: vi is not defined`) and it was fixed before the green step.
30
+ Impact: one extra test run, no rework commits.
31
+ - `scope-drift` — The `/plan-issue` prompt does not instruct updating `architecture.md`, but the user noticed #87 was missing from the roadmap's dependency graph.
32
+ The agent hadn't checked `architecture.md` during planning even though #87 is explicitly listed as a precursor to #70 in the #70 plan.
33
+ Impact: user-caught; required a follow-up edit pass after the plan commit.
34
+
35
+ #### What caused friction (user side)
36
+
37
+ No friction observed.
38
+ The user's correction about `architecture.md` was well-targeted and caught a real gap.
39
+
40
+ ### Changes made
41
+
42
+ 1. Retro file created at `packages/pi-subagents/docs/retro/0087-evolve-subagent-runtime-methods.md`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gotgenes/pi-subagents",
3
- "version": "5.7.0",
3
+ "version": "5.8.0",
4
4
  "exports": {
5
5
  ".": "./src/service.ts"
6
6
  },
@@ -0,0 +1,6 @@
1
+ export {
2
+ type LifecycleManager,
3
+ type LifecycleRuntime,
4
+ SessionLifecycleHandler,
5
+ } from "./lifecycle.js";
6
+ export { ToolStartHandler, type ToolStartRuntime } from "./tool-start.js";
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Session lifecycle event handlers: session_start, session_before_switch, session_shutdown.
3
+ *
4
+ * Extracted from index.ts so each handler can be tested in isolation
5
+ * with mocked narrow interfaces.
6
+ */
7
+
8
+ /** Narrow manager interface — only the methods lifecycle handlers call. */
9
+ export interface LifecycleManager {
10
+ clearCompleted(): void;
11
+ abortAll(): void;
12
+ dispose(): void;
13
+ }
14
+
15
+ /** Narrow runtime interface — only the methods lifecycle handlers call. */
16
+ export interface LifecycleRuntime {
17
+ setSessionContext(pi: unknown, ctx: unknown): void;
18
+ clearSessionContext(): void;
19
+ }
20
+
21
+ /**
22
+ * Handles session lifecycle events.
23
+ *
24
+ * Constructor deps:
25
+ * - `pi` — the ExtensionAPI instance, stored in runtime on session_start
26
+ * - `runtime` — owns session context state
27
+ * - `manager` — manages agent lifecycle (clear, abort, dispose)
28
+ * - `disposeNotifications` — tears down the notification system on shutdown
29
+ * - `unpublishService` — unpublishes the SubagentsService symbol on shutdown
30
+ */
31
+ export class SessionLifecycleHandler {
32
+ constructor(
33
+ private readonly pi: unknown,
34
+ private readonly runtime: LifecycleRuntime,
35
+ private readonly manager: LifecycleManager,
36
+ private readonly disposeNotifications: () => void,
37
+ private readonly unpublishService: () => void,
38
+ ) {}
39
+
40
+ handleSessionStart(_event: unknown, ctx: unknown): void {
41
+ this.runtime.setSessionContext(this.pi, ctx);
42
+ this.manager.clearCompleted();
43
+ }
44
+
45
+ handleSessionBeforeSwitch(): void {
46
+ this.manager.clearCompleted();
47
+ }
48
+
49
+ // Cleanup order matters:
50
+ // 1. Unpublish service — prevent new cross-extension calls
51
+ // 2. Clear session context — no more session state
52
+ // 3. Abort all agents — stop running work
53
+ // 4. Dispose notifications — cancel pending nudges/timers
54
+ // 5. Dispose manager — final cleanup
55
+ async handleSessionShutdown(): Promise<void> {
56
+ this.unpublishService();
57
+ this.runtime.clearSessionContext();
58
+ this.manager.abortAll();
59
+ this.disposeNotifications();
60
+ this.manager.dispose();
61
+ }
62
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * tool_execution_start event handler.
3
+ *
4
+ * Extracted from index.ts so the handler can be tested in isolation
5
+ * with a mocked narrow runtime interface.
6
+ */
7
+
8
+ /** Narrow runtime interface — only the widget-delegation methods the handler calls. */
9
+ export interface ToolStartRuntime {
10
+ setUICtx(ctx: unknown): void;
11
+ onTurnStart(): void;
12
+ }
13
+
14
+ /** Minimal context shape for tool_execution_start — only the field the handler reads. */
15
+ interface ToolStartCtx {
16
+ ui: unknown;
17
+ }
18
+
19
+ /**
20
+ * Handles tool_execution_start events.
21
+ *
22
+ * Grabs UI context from the first tool execution of each turn
23
+ * and signals the widget to clear lingering state.
24
+ */
25
+ export class ToolStartHandler {
26
+ constructor(private readonly runtime: ToolStartRuntime) {}
27
+
28
+ handleToolExecutionStart(_event: unknown, ctx: ToolStartCtx): void {
29
+ this.runtime.setUICtx(ctx.ui);
30
+ this.runtime.onTurnStart();
31
+ }
32
+ }
package/src/index.ts CHANGED
@@ -16,6 +16,7 @@ import { AgentManager } from "./agent-manager.js";
16
16
  import { getAgentConversation, normalizeMaxTurns, resumeAgent, runAgent, steerAgent } from "./agent-runner.js";
17
17
  import { getAvailableTypes, getDefaultAgentNames, getUserAgentNames, registerAgents, resolveAgentConfig, } from "./agent-types.js";
18
18
  import { loadCustomAgents } from "./custom-agents.js";
19
+ import { SessionLifecycleHandler, ToolStartHandler } from "./handlers/index.js";
19
20
  import { type ModelRegistry, resolveModel } from "./model-resolver.js";
20
21
  import { buildEventData, createNotificationSystem } from "./notification.js";
21
22
  import { createNotificationRenderer } from "./renderer.js";
@@ -122,33 +123,24 @@ export default function (pi: ExtensionAPI) {
122
123
  });
123
124
  publishSubagentsService(service);
124
125
 
125
- pi.on("session_start", async (_event, ctx) => {
126
- runtime.setSessionContext(pi, ctx);
127
- manager.clearCompleted();
128
- });
129
-
130
- pi.on("session_before_switch", () => {
131
- manager.clearCompleted();
132
- });
126
+ const lifecycle = new SessionLifecycleHandler(
127
+ pi,
128
+ runtime,
129
+ manager,
130
+ () => notifications.dispose(),
131
+ unpublishSubagentsService,
132
+ );
133
133
 
134
- // On shutdown, abort all agents immediately and clean up.
135
- // If the session is going down, there's nothing left to consume agent results.
136
- pi.on("session_shutdown", async () => {
137
- unpublishSubagentsService();
138
- runtime.clearSessionContext();
139
- manager.abortAll();
140
- notifications.dispose();
141
- manager.dispose();
142
- });
134
+ pi.on("session_start", (event, ctx) => lifecycle.handleSessionStart(event, ctx));
135
+ pi.on("session_before_switch", () => lifecycle.handleSessionBeforeSwitch());
136
+ pi.on("session_shutdown", () => lifecycle.handleSessionShutdown());
143
137
 
144
138
  // Live widget: show running agents above editor
145
139
  runtime.widget = new AgentWidget(manager, runtime.agentActivity);
146
140
 
147
141
  // Grab UI context from first tool execution + clear lingering widget on new turn
148
- pi.on("tool_execution_start", async (_event, ctx) => {
149
- runtime.setUICtx(ctx.ui as UICtx);
150
- runtime.onTurnStart();
151
- });
142
+ const toolStart = new ToolStartHandler(runtime);
143
+ pi.on("tool_execution_start", (event, ctx) => toolStart.handleToolExecutionStart(event, ctx));
152
144
 
153
145
  /** Build the full type list text dynamically from the unified registry. */
154
146
  const buildTypeListText = () => {