@gotgenes/pi-subagents 13.2.1 → 13.2.2

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.
Files changed (42) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +3 -1
  3. package/dist/public.d.ts +34 -35
  4. package/docs/architecture/architecture.md +50 -49
  5. package/docs/architecture/history/phase-16-invert-dependencies.md +2 -1
  6. package/docs/decisions/0003-publish-bundled-type-declarations.md +3 -1
  7. package/docs/plans/0051-update-adr-0001-hard-fork.md +8 -6
  8. package/docs/plans/0257-extract-child-session-factory.md +3 -1
  9. package/docs/plans/0262-add-workspace-provider-seam.md +41 -39
  10. package/docs/plans/0264-remove-extension-lifecycle-control.md +100 -98
  11. package/docs/plans/0265-born-complete-subagent-session.md +5 -2
  12. package/docs/plans/0270-type-consumable-public-surface.md +3 -1
  13. package/docs/plans/0280-rename-agent-to-subagent.md +197 -0
  14. package/docs/retro/0051-update-adr-0001-hard-fork.md +4 -2
  15. package/docs/retro/0257-extract-child-session-factory.md +3 -1
  16. package/docs/retro/0262-add-workspace-provider-seam.md +3 -1
  17. package/docs/retro/0264-remove-extension-lifecycle-control.md +3 -1
  18. package/docs/retro/0270-type-consumable-public-surface.md +4 -2
  19. package/docs/retro/0280-rename-agent-to-subagent.md +96 -0
  20. package/package.json +1 -1
  21. package/src/index.ts +9 -9
  22. package/src/lifecycle/create-subagent-session.ts +1 -1
  23. package/src/lifecycle/{agent-manager.ts → subagent-manager.ts} +27 -27
  24. package/src/lifecycle/subagent-session.ts +2 -2
  25. package/src/lifecycle/{agent.ts → subagent.ts} +28 -28
  26. package/src/lifecycle/turn-limits.ts +1 -1
  27. package/src/lifecycle/workspace.ts +2 -2
  28. package/src/observation/notification.ts +9 -9
  29. package/src/observation/record-observer.ts +9 -9
  30. package/src/runtime.ts +1 -1
  31. package/src/service/service-adapter.ts +10 -10
  32. package/src/service/service.ts +5 -9
  33. package/src/tools/agent-tool.ts +5 -5
  34. package/src/tools/background-spawner.ts +3 -3
  35. package/src/tools/foreground-runner.ts +5 -5
  36. package/src/tools/get-result-tool.ts +2 -2
  37. package/src/tools/steer-tool.ts +2 -2
  38. package/src/types.ts +1 -1
  39. package/src/ui/agent-creation-wizard.ts +2 -2
  40. package/src/ui/agent-menu.ts +5 -5
  41. package/src/ui/agent-widget.ts +2 -2
  42. package/src/ui/conversation-viewer.ts +3 -3
@@ -1,5 +1,5 @@
1
1
  /**
2
- * agent-manager.ts - Tracks agents, background execution, resume support.
2
+ * subagent-manager.ts - Tracks subagents, background execution, resume support.
3
3
  *
4
4
  * Background agents are subject to a configurable concurrency limit (default: 4).
5
5
  * Excess agents are queued and auto-started as running agents complete.
@@ -9,10 +9,10 @@
9
9
  import { randomUUID } from "node:crypto";
10
10
  import type { Model } from "@earendil-works/pi-ai";
11
11
  import { debugLog } from "#src/debug";
12
- import { Agent, type AgentLifecycleObserver } from "#src/lifecycle/agent";
13
12
  import type { ConcurrencyQueue } from "#src/lifecycle/concurrency-queue";
14
13
  import type { CreateSubagentSessionParams } from "#src/lifecycle/create-subagent-session";
15
14
  import type { ParentSnapshot } from "#src/lifecycle/parent-snapshot";
15
+ import { Subagent, type SubagentLifecycleObserver } from "#src/lifecycle/subagent";
16
16
  import type { SubagentSession } from "#src/lifecycle/subagent-session";
17
17
  import type { WorkspaceProvider } from "#src/lifecycle/workspace";
18
18
 
@@ -20,15 +20,15 @@ import type { RunConfig } from "#src/runtime";
20
20
  import type { AgentInvocation, CompactionInfo, ParentSessionInfo, SubagentType, ThinkingLevel } from "#src/types";
21
21
 
22
22
  /** Observer interface for agent lifecycle notifications. */
23
- export interface AgentManagerObserver {
24
- onAgentStarted(record: Agent): void;
25
- onAgentCompleted(record: Agent): void;
26
- onAgentCompacted(record: Agent, info: CompactionInfo): void;
23
+ export interface SubagentManagerObserver {
24
+ onSubagentStarted(record: Subagent): void;
25
+ onSubagentCompleted(record: Subagent): void;
26
+ onSubagentCompacted(record: Subagent, info: CompactionInfo): void;
27
27
  /** Fires synchronously after a background agent record is created (before run). */
28
- onAgentCreated(record: Agent): void;
28
+ onSubagentCreated(record: Subagent): void;
29
29
  }
30
30
 
31
- export interface AgentManagerOptions {
31
+ export interface SubagentManagerOptions {
32
32
  /** Assembly factory that produces a born-complete SubagentSession per spawn. */
33
33
  createSubagentSession: (params: CreateSubagentSessionParams) => Promise<SubagentSession>;
34
34
  /** Concurrency queue — owns scheduling, limit checks, and drain logic. */
@@ -36,7 +36,7 @@ export interface AgentManagerOptions {
36
36
  /** Base working directory handed to a workspace provider (the parent cwd). */
37
37
  baseCwd: string;
38
38
  getRunConfig?: () => RunConfig;
39
- observer?: AgentManagerObserver;
39
+ observer?: SubagentManagerObserver;
40
40
  }
41
41
 
42
42
  export interface AgentSpawnConfig {
@@ -56,16 +56,16 @@ export interface AgentSpawnConfig {
56
56
  invocation?: AgentInvocation;
57
57
  /** Parent abort signal - when aborted, the subagent is also stopped. */
58
58
  signal?: AbortSignal;
59
- /** Per-agent lifecycle observer — replaces onSessionCreated callback. */
60
- observer?: AgentLifecycleObserver;
59
+ /** Per-subagent lifecycle observer — replaces onSessionCreated callback. */
60
+ observer?: SubagentLifecycleObserver;
61
61
  /** Parent session identity - grouped fields that travel together from the tool boundary. */
62
62
  parentSession?: ParentSessionInfo;
63
63
  }
64
64
 
65
- export class AgentManager {
66
- private agents = new Map<string, Agent>();
65
+ export class SubagentManager {
66
+ private agents = new Map<string, Subagent>();
67
67
  private cleanupInterval: ReturnType<typeof setInterval>;
68
- private readonly observer?: AgentManagerObserver;
68
+ private readonly observer?: SubagentManagerObserver;
69
69
  private readonly createSubagentSession: (params: CreateSubagentSessionParams) => Promise<SubagentSession>;
70
70
  private readonly queue: ConcurrencyQueue;
71
71
  private readonly baseCwd: string;
@@ -77,7 +77,7 @@ export class AgentManager {
77
77
  return this._workspaceProvider;
78
78
  }
79
79
 
80
- constructor(options: AgentManagerOptions) {
80
+ constructor(options: SubagentManagerOptions) {
81
81
  this.createSubagentSession = options.createSubagentSession;
82
82
  this.queue = options.queue;
83
83
  this.baseCwd = options.baseCwd;
@@ -106,11 +106,11 @@ export class AgentManager {
106
106
  }
107
107
 
108
108
  /** Compose a per-agent lifecycle observer from manager and spawn-config concerns. */
109
- private buildObserver(options: AgentSpawnConfig): AgentLifecycleObserver {
109
+ private buildObserver(options: AgentSpawnConfig): SubagentLifecycleObserver {
110
110
  return {
111
111
  onStarted: (agent) => {
112
112
  if (options.isBackground) this.queue.markStarted();
113
- this.observer?.onAgentStarted(agent);
113
+ this.observer?.onSubagentStarted(agent);
114
114
  },
115
115
  onSessionCreated: options.observer?.onSessionCreated
116
116
  ? (agent) => options.observer!.onSessionCreated!(agent)
@@ -118,11 +118,11 @@ export class AgentManager {
118
118
  onRunFinished: (agent) => {
119
119
  if (options.isBackground) {
120
120
  this.queue.markFinished();
121
- try { this.observer?.onAgentCompleted(agent); } catch (err) { debugLog("onAgentCompleted observer", err); }
121
+ try { this.observer?.onSubagentCompleted(agent); } catch (err) { debugLog("onSubagentCompleted observer", err); }
122
122
  }
123
123
  },
124
124
  onCompacted: (agent, info) => {
125
- this.observer?.onAgentCompacted(agent, info);
125
+ this.observer?.onSubagentCompacted(agent, info);
126
126
  },
127
127
  };
128
128
  }
@@ -138,7 +138,7 @@ export class AgentManager {
138
138
  options: AgentSpawnConfig,
139
139
  ): string {
140
140
  const id = randomUUID().slice(0, 17);
141
- const record = new Agent({
141
+ const record = new Subagent({
142
142
  id,
143
143
  type,
144
144
  description: options.description,
@@ -163,7 +163,7 @@ export class AgentManager {
163
163
  this.agents.set(id, record);
164
164
 
165
165
  if (options.isBackground) {
166
- this.observer?.onAgentCreated(record);
166
+ this.observer?.onSubagentCreated(record);
167
167
  }
168
168
 
169
169
  if (options.isBackground && !options.bypassQueue && this.queue.isFull()) {
@@ -185,7 +185,7 @@ export class AgentManager {
185
185
  type: SubagentType,
186
186
  prompt: string,
187
187
  options: Omit<AgentSpawnConfig, "isBackground">,
188
- ): Promise<Agent> {
188
+ ): Promise<Subagent> {
189
189
  const id = this.spawn(snapshot, type, prompt, { ...options, isBackground: false });
190
190
  const record = this.agents.get(id)!;
191
191
  await record.promise;
@@ -194,24 +194,24 @@ export class AgentManager {
194
194
 
195
195
  /**
196
196
  * Resume an existing agent session with a new prompt.
197
- * Delegates to Agent.resume(), which owns the observer subscription lifecycle.
197
+ * Delegates to Subagent.resume(), which owns the observer subscription lifecycle.
198
198
  */
199
199
  async resume(
200
200
  id: string,
201
201
  prompt: string,
202
202
  signal?: AbortSignal,
203
- ): Promise<Agent | undefined> {
203
+ ): Promise<Subagent | undefined> {
204
204
  const agent = this.agents.get(id);
205
205
  if (!agent?.isSessionReady()) return undefined;
206
206
  await agent.resume(prompt, signal);
207
207
  return agent;
208
208
  }
209
209
 
210
- getRecord(id: string): Agent | undefined {
210
+ getRecord(id: string): Subagent | undefined {
211
211
  return this.agents.get(id);
212
212
  }
213
213
 
214
- listAgents(): Agent[] {
214
+ listAgents(): Subagent[] {
215
215
  return [...this.agents.values()].sort(
216
216
  (a, b) => b.startedAt - a.startedAt,
217
217
  );
@@ -232,7 +232,7 @@ export class AgentManager {
232
232
  }
233
233
 
234
234
  /** Dispose a record's session and remove it from the map. */
235
- private removeRecord(id: string, record: Agent): void {
235
+ private removeRecord(id: string, record: Subagent): void {
236
236
  record.disposeSession();
237
237
  this.agents.delete(id);
238
238
  }
@@ -4,10 +4,10 @@
4
4
  * A SubagentSession wraps one SDK AgentSession plus its turn-driving and teardown.
5
5
  * It is born complete: `createSubagentSession()` returns a fully usable instance
6
6
  * (session created, extensions bound, recursion guard applied), so the only thing
7
- * left for `Agent` to do is coordinate — drive the turn loop, steer, dispose.
7
+ * left for `Subagent` to do is coordinate — drive the turn loop, steer, dispose.
8
8
  *
9
9
  * Turn driving lives here, on the object that owns the AgentSession, rather than
10
- * reaching through `subagentSession.session` from `Agent` (Law of Demeter).
10
+ * reaching through `subagentSession.session` from `Subagent` (Law of Demeter).
11
11
  */
12
12
 
13
13
  import {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * agent.ts — Agent class with encapsulated status-transition logic and per-agent behavior.
2
+ * subagent.ts — Subagent class with encapsulated status-transition logic and per-subagent behavior.
3
3
  *
4
4
  * Status transitions (status, result, error, startedAt, completedAt) are owned
5
5
  * by the class and exposed via transition methods. External code reads these
@@ -8,8 +8,8 @@
8
8
  * Stats (toolUses, lifetimeUsage, compactionCount) are owned by the class and
9
9
  * accumulated via mutation methods (incrementToolUses, addUsage, incrementCompactions).
10
10
  *
11
- * Behavior (abort, steer buffering) lives on the agent rather than on
12
- * AgentManager — each agent manages its own lifecycle concerns.
11
+ * Behavior (abort, steer buffering) lives on the subagent rather than on
12
+ * SubagentManager — each subagent manages its own lifecycle concerns.
13
13
  *
14
14
  * The child's working directory is supplied by a registered WorkspaceProvider
15
15
  * (the workspace seam); with no provider the child runs in the parent cwd.
@@ -28,23 +28,23 @@ import type { LifetimeUsage } from "#src/lifecycle/usage";
28
28
  import { addUsage } from "#src/lifecycle/usage";
29
29
  import type { Workspace, WorkspaceProvider } from "#src/lifecycle/workspace";
30
30
  import { NotificationState } from "#src/observation/notification-state";
31
- import { subscribeAgentObserver } from "#src/observation/record-observer";
31
+ import { subscribeSubagentObserver } from "#src/observation/record-observer";
32
32
  import type { RunConfig } from "#src/runtime";
33
33
  import type { AgentInvocation, CompactionInfo, ParentSessionInfo, SubagentType, ThinkingLevel } from "#src/types";
34
34
 
35
- /** Per-agent lifecycle observer — created by AgentManager for each spawn. */
36
- export interface AgentLifecycleObserver {
37
- /** Fires when the agent transitions to running (inside run(), after markRunning). */
38
- onStarted?(agent: Agent): void;
39
- /** Fires once the session is created — the agent's subagentSession is now available. */
40
- onSessionCreated?(agent: Agent): void;
35
+ /** Per-subagent lifecycle observer — created by SubagentManager for each spawn. */
36
+ export interface SubagentLifecycleObserver {
37
+ /** Fires when the subagent transitions to running (inside run(), after markRunning). */
38
+ onStarted?(agent: Subagent): void;
39
+ /** Fires once the session is created — the subagent's subagentSession is now available. */
40
+ onSessionCreated?(agent: Subagent): void;
41
41
  /** Fires once when the run completes or fails (for concurrency drain). */
42
- onRunFinished?(agent: Agent): void;
42
+ onRunFinished?(agent: Subagent): void;
43
43
  /** Fires on compaction events during the run. */
44
- onCompacted?(agent: Agent, info: CompactionInfo): void;
44
+ onCompacted?(agent: Subagent, info: CompactionInfo): void;
45
45
  }
46
46
 
47
- export type AgentStatus =
47
+ export type SubagentStatus =
48
48
  | "queued"
49
49
  | "running"
50
50
  | "completed"
@@ -53,7 +53,7 @@ export type AgentStatus =
53
53
  | "stopped"
54
54
  | "error";
55
55
 
56
- export interface AgentInit {
56
+ export interface SubagentInit {
57
57
  // Identity
58
58
  id: string;
59
59
  type: SubagentType;
@@ -61,7 +61,7 @@ export interface AgentInit {
61
61
  invocation?: AgentInvocation;
62
62
 
63
63
  // Status (for tests and restore scenarios)
64
- status?: AgentStatus;
64
+ status?: SubagentStatus;
65
65
  startedAt?: number;
66
66
  completedAt?: number;
67
67
  result?: string;
@@ -70,7 +70,7 @@ export interface AgentInit {
70
70
  // Shared deps (required for run(), optional for tests)
71
71
  /** Assembly factory that produces a born-complete SubagentSession. */
72
72
  createSubagentSession?: (params: CreateSubagentSessionParams) => Promise<SubagentSession>;
73
- observer?: AgentLifecycleObserver;
73
+ observer?: SubagentLifecycleObserver;
74
74
  getRunConfig?: () => RunConfig;
75
75
  /** Resolves the registered workspace provider (if any) at run-start. */
76
76
  getWorkspaceProvider?: () => WorkspaceProvider | undefined;
@@ -88,7 +88,7 @@ export interface AgentInit {
88
88
  signal?: AbortSignal;
89
89
  }
90
90
 
91
- export class Agent {
91
+ export class Subagent {
92
92
  // Identity — set once at construction
93
93
  readonly id: string;
94
94
  readonly type: SubagentType;
@@ -96,8 +96,8 @@ export class Agent {
96
96
  readonly invocation?: AgentInvocation;
97
97
 
98
98
  // Transition state — encapsulated behind getters, mutated only via transition methods
99
- private _status: AgentStatus;
100
- get status(): AgentStatus { return this._status; }
99
+ private _status: SubagentStatus;
100
+ get status(): SubagentStatus { return this._status; }
101
101
 
102
102
  private _result?: string;
103
103
  get result(): string | undefined { return this._result; }
@@ -128,7 +128,7 @@ export class Agent {
128
128
 
129
129
  // Shared deps — optional (required for run())
130
130
  private readonly _createSubagentSession?: (params: CreateSubagentSessionParams) => Promise<SubagentSession>;
131
- readonly observer?: AgentLifecycleObserver;
131
+ readonly observer?: SubagentLifecycleObserver;
132
132
  private readonly _getRunConfig?: () => RunConfig;
133
133
  private readonly _getWorkspaceProvider?: () => WorkspaceProvider | undefined;
134
134
  private readonly _baseCwd: string;
@@ -200,7 +200,7 @@ export class Agent {
200
200
  return this.subagentSession?.messages ?? [];
201
201
  }
202
202
 
203
- constructor(init: AgentInit) {
203
+ constructor(init: SubagentInit) {
204
204
  // Identity
205
205
  this.id = init.id;
206
206
  this.type = init.type;
@@ -254,10 +254,10 @@ export class Agent {
254
254
  */
255
255
  async run(): Promise<void> {
256
256
  if (!this._createSubagentSession) {
257
- throw new Error("Agent not configured for execution — missing session factory");
257
+ throw new Error("Subagent not configured for execution — missing session factory");
258
258
  }
259
259
  if (!this._snapshot || !this._prompt) {
260
- throw new Error("Agent not configured for execution — missing snapshot or prompt");
260
+ throw new Error("Subagent not configured for execution — missing snapshot or prompt");
261
261
  }
262
262
 
263
263
  this.markRunning(Date.now());
@@ -301,7 +301,7 @@ export class Agent {
301
301
  }
302
302
 
303
303
  this.flushPendingSteers();
304
- this.attachObserver(subscribeAgentObserver(this.subagentSession, this, {
304
+ this.attachObserver(subscribeSubagentObserver(this.subagentSession, this, {
305
305
  onCompact: (r, info) => this.observer?.onCompacted?.(r, info),
306
306
  }));
307
307
  this.observer?.onSessionCreated?.(this);
@@ -332,11 +332,11 @@ export class Agent {
332
332
  async resume(prompt: string, signal?: AbortSignal): Promise<void> {
333
333
  const subagentSession = this.subagentSession;
334
334
  if (!subagentSession) {
335
- throw new Error("Agent not configured for resume — missing session");
335
+ throw new Error("Subagent not configured for resume — missing session");
336
336
  }
337
337
 
338
338
  this.resetForResume(Date.now());
339
- this.attachObserver(subscribeAgentObserver(subagentSession, this, {
339
+ this.attachObserver(subscribeSubagentObserver(subagentSession, this, {
340
340
  onCompact: (r, info) => this.observer?.onCompacted?.(r, info),
341
341
  }));
342
342
 
@@ -428,7 +428,7 @@ export class Agent {
428
428
  /**
429
429
  * Abort a running agent: fire AbortController and transition to stopped.
430
430
  * Returns false if the agent is not running.
431
- * Queue removal is handled by AgentManager via ConcurrencyQueue.dequeue().
431
+ * Queue removal is handled by SubagentManager via ConcurrencyQueue.dequeue().
432
432
  */
433
433
  abort(): boolean {
434
434
  if (this._status !== "running") return false;
@@ -497,7 +497,7 @@ export class Agent {
497
497
 
498
498
  let finalResult = result.responseText;
499
499
  if (this._workspace) {
500
- const finalStatus: AgentStatus = result.aborted
500
+ const finalStatus: SubagentStatus = result.aborted
501
501
  ? "aborted"
502
502
  : result.steered
503
503
  ? "steered"
@@ -2,7 +2,7 @@
2
2
  * turn-limits.ts — Pure turn-limit normalization for subagent execution.
3
3
  *
4
4
  * Extracted from agent-runner.ts (issue #265) so the turn-counting policy has a
5
- * focused home independent of session assembly. Consumed by the Agent tool's
5
+ * focused home independent of session assembly. Consumed by the subagent tool's
6
6
  * spawn-config resolution and by the turn loop in SubagentSession.
7
7
  */
8
8
 
@@ -11,7 +11,7 @@
11
11
  * synchronously at run-start. The core has no knowledge of git or worktrees.
12
12
  */
13
13
 
14
- import type { AgentStatus } from "#src/lifecycle/agent";
14
+ import type { SubagentStatus } from "#src/lifecycle/subagent";
15
15
  import type { AgentInvocation, SubagentType } from "#src/types";
16
16
 
17
17
  /** Context the core hands a provider when a child run starts. */
@@ -24,7 +24,7 @@ export interface WorkspacePrepareContext {
24
24
 
25
25
  /** Outcome the core reports to a workspace when the run ends. */
26
26
  export interface WorkspaceDisposeOutcome {
27
- status: AgentStatus;
27
+ status: SubagentStatus;
28
28
  description: string;
29
29
  }
30
30
 
@@ -1,6 +1,6 @@
1
1
  import { debugLog } from "#src/debug";
2
2
  import { getLifetimeTotal } from "#src/lifecycle/usage";
3
- import type { Agent } from "#src/types";
3
+ import type { Subagent } from "#src/types";
4
4
  import type { AgentActivityTracker } from "#src/ui/agent-activity-tracker";
5
5
 
6
6
  /** Details attached to custom notification messages for visual rendering. */
@@ -42,7 +42,7 @@ export function getStatusLabel(status: string, error?: string): string {
42
42
  }
43
43
 
44
44
  /** Format a structured task notification matching Claude Code's <task-notification> XML. */
45
- export function formatTaskNotification(record: Agent, resultMaxLen: number): string {
45
+ export function formatTaskNotification(record: Subagent, resultMaxLen: number): string {
46
46
  const status = getStatusLabel(record.status, record.error);
47
47
  const durationMs = record.completedAt ? record.completedAt - record.startedAt : 0;
48
48
  const totalTokens = getLifetimeTotal(record.lifetimeUsage);
@@ -64,7 +64,7 @@ export function formatTaskNotification(record: Agent, resultMaxLen: number): str
64
64
  toolCallId ? `<tool-use-id>${escapeXml(toolCallId)}</tool-use-id>` : null,
65
65
  outputFile ? `<output-file>${escapeXml(outputFile)}</output-file>` : null,
66
66
  `<status>${escapeXml(status)}</status>`,
67
- `<summary>Agent "${escapeXml(record.description)}" ${record.status}</summary>`,
67
+ `<summary>Subagent "${escapeXml(record.description)}" ${record.status}</summary>`,
68
68
  `<result>${escapeXml(resultPreview)}</result>`,
69
69
  `<usage><total_tokens>${totalTokens}</total_tokens><tool_uses>${record.toolUses}</tool_uses>${ctxXml}${compactXml}<duration_ms>${durationMs}</duration_ms></usage>`,
70
70
  "</task-notification>",
@@ -75,7 +75,7 @@ export function formatTaskNotification(record: Agent, resultMaxLen: number): str
75
75
 
76
76
  /** Build notification details for the custom message renderer. */
77
77
  export function buildNotificationDetails(
78
- record: Agent,
78
+ record: Subagent,
79
79
  resultMaxLen: number,
80
80
  activity?: AgentActivityTracker,
81
81
  ): NotificationDetails {
@@ -100,8 +100,8 @@ export function buildNotificationDetails(
100
100
  };
101
101
  }
102
102
 
103
- /** Build event data for lifecycle events from an Agent. */
104
- export function buildEventData(record: Agent) {
103
+ /** Build event data for lifecycle events from a Subagent. */
104
+ export function buildEventData(record: Subagent) {
105
105
  const durationMs = record.completedAt ? record.completedAt - record.startedAt : Date.now() - record.startedAt;
106
106
  const u = record.lifetimeUsage;
107
107
  const total = getLifetimeTotal(u);
@@ -126,7 +126,7 @@ export function buildEventData(record: Agent) {
126
126
 
127
127
  export interface NotificationSystem {
128
128
  cancelNudge: (key: string) => void;
129
- sendCompletion: (record: Agent) => void;
129
+ sendCompletion: (record: Subagent) => void;
130
130
  cleanupCompleted: (id: string) => void;
131
131
  dispose: () => void;
132
132
  }
@@ -154,7 +154,7 @@ export class NotificationManager implements NotificationSystem {
154
154
  }
155
155
  }
156
156
 
157
- sendCompletion(record: Agent): void {
157
+ sendCompletion(record: Subagent): void {
158
158
  this.agentActivity.delete(record.id);
159
159
  this.markFinished(record.id);
160
160
  this.scheduleNudge(record.id, () => this.emitIndividualNudge(record));
@@ -187,7 +187,7 @@ export class NotificationManager implements NotificationSystem {
187
187
  );
188
188
  }
189
189
 
190
- private emitIndividualNudge(record: Agent): void {
190
+ private emitIndividualNudge(record: Subagent): void {
191
191
  if (record.notification?.resultConsumed) return;
192
192
 
193
193
  const notification = formatTaskNotification(record, 500);
@@ -1,19 +1,19 @@
1
1
  /**
2
- * record-observer.ts — Subscribes to session events and updates Agent stats.
2
+ * record-observer.ts — Subscribes to session events and updates Subagent stats.
3
3
  *
4
- * Replaces the scattered callback-wrapping logic in AgentManager's startAgent()
4
+ * Replaces the scattered callback-wrapping logic in SubagentManager's startAgent()
5
5
  * and resume() with a single direct subscription.
6
6
  */
7
7
 
8
- import type { Agent } from "#src/lifecycle/agent";
8
+ import type { Subagent } from "#src/lifecycle/subagent";
9
9
  import type { CompactionInfo, SubscribableSession } from "#src/types";
10
10
 
11
- export interface AgentObserverOptions {
12
- onCompact?: (record: Agent, info: CompactionInfo) => void;
11
+ export interface SubagentObserverOptions {
12
+ onCompact?: (record: Subagent, info: CompactionInfo) => void;
13
13
  }
14
14
 
15
15
  /**
16
- * Subscribe to session events and accumulate stats on the agent record.
16
+ * Subscribe to session events and accumulate stats on the subagent record.
17
17
  *
18
18
  * Handles:
19
19
  * - `tool_execution_end` → `record.incrementToolUses()`
@@ -22,10 +22,10 @@ export interface AgentObserverOptions {
22
22
  *
23
23
  * @returns An unsubscribe function.
24
24
  */
25
- export function subscribeAgentObserver(
25
+ export function subscribeSubagentObserver(
26
26
  session: SubscribableSession,
27
- record: Agent,
28
- options?: AgentObserverOptions,
27
+ record: Subagent,
28
+ options?: SubagentObserverOptions,
29
29
  ): () => void {
30
30
  return session.subscribe((event) => {
31
31
  if (event.type === "tool_execution_end") {
package/src/runtime.ts CHANGED
@@ -49,7 +49,7 @@ export class SubagentRuntime {
49
49
  */
50
50
  readonly agentActivity: Map<string, AgentActivityTracker> = new Map();
51
51
  /**
52
- * Persistent widget reference. Null until constructed after AgentManager.
52
+ * Persistent widget reference. Null until constructed after SubagentManager.
53
53
  * Delegation methods use optional chaining so callers never need `widget!`.
54
54
  */
55
55
  widget: WidgetLike | null = null;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * service-adapter.ts — Adapter that wraps AgentManager to satisfy SubagentsService.
2
+ * service-adapter.ts — Adapter that wraps SubagentManager to satisfy SubagentsService.
3
3
  *
4
4
  * Handles model resolution at the API boundary, record serialization
5
5
  * (stripping non-serializable fields), and session gating.
@@ -9,13 +9,13 @@ import type { ParentSnapshot } from "#src/lifecycle/parent-snapshot";
9
9
  import type { WorkspaceProvider } from "#src/lifecycle/workspace";
10
10
  import type { SpawnOptions, SubagentRecord, SubagentsService } from "#src/service/service";
11
11
  import type { ModelRegistry } from "#src/session/model-resolver";
12
- import type { Agent, SessionContext } from "#src/types";
12
+ import type { SessionContext, Subagent } from "#src/types";
13
13
 
14
- /** Narrow interface for the AgentManager — avoids coupling to the concrete class. */
15
- export interface AgentManagerLike {
14
+ /** Narrow interface for the SubagentManager — avoids coupling to the concrete class. */
15
+ export interface SubagentManagerLike {
16
16
  spawn(snapshot: ParentSnapshot, type: string, prompt: string, options: unknown): string;
17
- getRecord(id: string): Agent | undefined;
18
- listAgents(): Agent[];
17
+ getRecord(id: string): Subagent | undefined;
18
+ listAgents(): Subagent[];
19
19
  abort(id: string): boolean;
20
20
  waitForAll(): Promise<void>;
21
21
  hasRunning(): boolean;
@@ -31,10 +31,10 @@ export interface ServiceRuntimeLike {
31
31
  buildSnapshot(inheritContext: boolean): ParentSnapshot;
32
32
  }
33
33
 
34
- /** Adapter that wraps AgentManager to satisfy SubagentsService. */
34
+ /** Adapter that wraps SubagentManager to satisfy SubagentsService. */
35
35
  export class SubagentsServiceAdapter implements SubagentsService {
36
36
  constructor(
37
- private readonly manager: AgentManagerLike,
37
+ private readonly manager: SubagentManagerLike,
38
38
  private readonly resolveModel: (input: string, registry: ModelRegistry) => unknown,
39
39
  private readonly runtime: ServiceRuntimeLike,
40
40
  ) {}
@@ -108,10 +108,10 @@ export class SubagentsServiceAdapter implements SubagentsService {
108
108
  }
109
109
 
110
110
  /**
111
- * Convert an internal Agent to a serializable SubagentRecord.
111
+ * Convert an internal Subagent to a serializable SubagentRecord.
112
112
  * Uses an explicit allowlist — new fields must be opted in.
113
113
  */
114
- export function toSubagentRecord(record: Agent): SubagentRecord {
114
+ export function toSubagentRecord(record: Subagent): SubagentRecord {
115
115
  const out: SubagentRecord = {
116
116
  id: record.id,
117
117
  type: record.type,
@@ -9,6 +9,7 @@
9
9
  * svc?.spawn("Explore", "Check for stale TODOs");
10
10
  */
11
11
 
12
+ import type { SubagentStatus } from "#src/lifecycle/subagent";
12
13
  import type { LifetimeUsage } from "#src/lifecycle/usage";
13
14
  import type {
14
15
  Workspace,
@@ -18,6 +19,10 @@ import type {
18
19
  WorkspaceProvider,
19
20
  } from "#src/lifecycle/workspace";
20
21
 
22
+
23
+ // SubagentStatus is defined in the lifecycle layer (single home) and re-exported
24
+ // here for the public API surface — mirrors the LifetimeUsage / workspace pattern.
25
+ export type { SubagentStatus } from "#src/lifecycle/subagent";
21
26
  // Generative extension seam (ADR 0002, Phase 16 Step 2). The provider type
22
27
  // and all four collaborator types it references are re-exported by name so
23
28
  // consumers can import them directly rather than recovering them via
@@ -31,15 +36,6 @@ export type {
31
36
  WorkspaceProvider,
32
37
  };
33
38
 
34
- export type SubagentStatus =
35
- | "queued"
36
- | "running"
37
- | "completed"
38
- | "steered"
39
- | "aborted"
40
- | "stopped"
41
- | "error";
42
-
43
39
  /** Serializable snapshot of an agent's state — no live session objects. */
44
40
  export interface SubagentRecord {
45
41
  id: string;
@@ -4,14 +4,14 @@ import { defineTool } from "@earendil-works/pi-coding-agent";
4
4
  import { Text } from "@earendil-works/pi-tui";
5
5
  import { Type } from "@sinclair/typebox";
6
6
  import { AgentTypeRegistry } from "#src/config/agent-types";
7
- import type { AgentSpawnConfig } from "#src/lifecycle/agent-manager";
8
7
  import type { ParentSnapshot } from "#src/lifecycle/parent-snapshot";
8
+ import type { AgentSpawnConfig } from "#src/lifecycle/subagent-manager";
9
9
  import { spawnBackground } from "#src/tools/background-spawner";
10
10
  import { runForeground } from "#src/tools/foreground-runner";
11
11
  import { buildDetails, buildTypeListText, textResult } from "#src/tools/helpers";
12
12
  import { renderAgentResult } from "#src/tools/result-renderer";
13
13
  import { type ModelInfo, resolveSpawnConfig } from "#src/tools/spawn-config";
14
- import type { Agent, ParentSessionInfo } from "#src/types";
14
+ import type { ParentSessionInfo, Subagent } from "#src/types";
15
15
  import { AgentActivityTracker } from "#src/ui/agent-activity-tracker";
16
16
  import { type UICtx } from "#src/ui/agent-widget";
17
17
  import { type AgentDetails, getDisplayName } from "#src/ui/display";
@@ -33,9 +33,9 @@ export interface AgentActivityAccess {
33
33
  /** Narrow manager interface — only the methods the Agent tool calls. */
34
34
  export interface AgentToolManager {
35
35
  spawn: (snapshot: ParentSnapshot, type: string, prompt: string, opts: AgentSpawnConfig) => string;
36
- spawnAndWait: (snapshot: ParentSnapshot, type: string, prompt: string, opts: Omit<AgentSpawnConfig, "isBackground">) => Promise<Agent>;
37
- resume: (id: string, prompt: string, signal: AbortSignal) => Promise<Agent | undefined>;
38
- getRecord: (id: string) => Agent | undefined;
36
+ spawnAndWait: (snapshot: ParentSnapshot, type: string, prompt: string, opts: Omit<AgentSpawnConfig, "isBackground">) => Promise<Subagent>;
37
+ resume: (id: string, prompt: string, signal: AbortSignal) => Promise<Subagent | undefined>;
38
+ getRecord: (id: string) => Subagent | undefined;
39
39
  }
40
40
 
41
41
  /** Narrow runtime interface — the Agent tool's slice of SubagentRuntime. */
@@ -1,16 +1,16 @@
1
- import type { AgentSpawnConfig } from "#src/lifecycle/agent-manager";
2
1
  import type { ParentSnapshot } from "#src/lifecycle/parent-snapshot";
2
+ import type { AgentSpawnConfig } from "#src/lifecycle/subagent-manager";
3
3
  import type { AgentActivityAccess } from "#src/tools/agent-tool";
4
4
  import { textResult } from "#src/tools/helpers";
5
5
  import type { ResolvedSpawnConfig } from "#src/tools/spawn-config";
6
- import type { Agent, ParentSessionInfo } from "#src/types";
6
+ import type { ParentSessionInfo, Subagent } from "#src/types";
7
7
  import { AgentActivityTracker } from "#src/ui/agent-activity-tracker";
8
8
  import { subscribeUIObserver } from "#src/ui/ui-observer";
9
9
 
10
10
  /** Narrow manager interface for the background spawner. */
11
11
  export interface BackgroundManagerDeps {
12
12
  spawn(snapshot: ParentSnapshot, type: string, prompt: string, opts: AgentSpawnConfig): string;
13
- getRecord(id: string): Agent | undefined;
13
+ getRecord(id: string): Subagent | undefined;
14
14
  }
15
15
 
16
16
  /** Narrow widget interface for the background spawner. */