@ian-pascoe/pi-minimal-subagents 0.2.0 → 0.2.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ian-pascoe/pi-minimal-subagents",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "private": false,
5
5
  "description": "Persistent nested subagents with bounded delegation for Pi",
6
6
  "keywords": [
@@ -36,18 +36,7 @@ export function buildEligibleModelIds(input: {
36
36
  const source = scopeConfigured
37
37
  ? input.scopedModels.map((entry) => entry.model)
38
38
  : input.availableModels;
39
- const seen = new Set<string>();
40
- const result: string[] = [];
41
-
42
- for (const model of source) {
43
- const canonicalId = `${model.provider}/${model.id}`;
44
- if (!seen.has(canonicalId)) {
45
- seen.add(canonicalId);
46
- result.push(canonicalId);
47
- }
48
- }
49
-
50
- return result;
39
+ return [...new Set(source.map(({ provider, id }) => `${provider}/${id}`))];
51
40
  }
52
41
 
53
42
  /** Supplies inherited tools, the ancestor ceiling, and runtime availability for exact tool resolution. */
@@ -51,8 +51,8 @@ interface MinimalSubagentsSettingsDocument {
51
51
  }
52
52
 
53
53
  interface MinimalSubagentsConfigInput {
54
- globalSettings: MinimalSubagentsSettingsDocument;
55
- projectSettings: MinimalSubagentsSettingsDocument;
54
+ globalSettings: MinimalSubagentsSettingsDocumentInput;
55
+ projectSettings: MinimalSubagentsSettingsDocumentInput;
56
56
  eligibleModelIds: readonly string[];
57
57
  }
58
58
 
@@ -126,19 +126,8 @@ function parseModelRolesWireValue(value: JsonValue): ModelRolesWireValue {
126
126
  };
127
127
  }
128
128
 
129
- function parsePiSettingsDocument(
130
- settings: MinimalSubagentsSettingsDocumentInput,
131
- ): MinimalSubagentsSettingsDocument {
132
- if (!Value.Check(SettingsDocumentSchema, settings)) return {};
133
- const parsed: MinimalSubagentsSettingsDocument = {};
134
- if (settings.minimalSubagents !== undefined) {
135
- parsed.minimalSubagents = settings.minimalSubagents;
136
- }
137
- return parsed;
138
- }
139
-
140
129
  function readMinimalSubagentsSettings(
141
- settings: MinimalSubagentsSettingsDocument,
130
+ settings: MinimalSubagentsSettingsDocumentInput,
142
131
  scope: SettingsScope,
143
132
  warnings: string[],
144
133
  ): ParsedMinimalSubagentsSettings {
@@ -359,8 +348,8 @@ export function resolveMinimalSubagentsSettings(
359
348
  eligibleModelIds: readonly string[],
360
349
  ): ResolvedMinimalSubagentsConfig {
361
350
  return resolveMinimalSubagentsConfig({
362
- globalSettings: parsePiSettingsDocument(settings.getGlobalSettings()),
363
- projectSettings: parsePiSettingsDocument(settings.getProjectSettings()),
351
+ globalSettings: settings.getGlobalSettings(),
352
+ projectSettings: settings.getProjectSettings(),
364
353
  eligibleModelIds,
365
354
  });
366
355
  }
@@ -1,5 +1,4 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import type { AgentMessage } from "@earendil-works/pi-agent-core";
3
2
  import { assembleImportedContext, contextContainsImages } from "./minimal-subagents-context.js";
4
3
  import {
5
4
  canAgentContractSpawn,
@@ -119,7 +118,6 @@ export class MinimalSubagentsCoordinator {
119
118
  private readonly agents = new Map<string, PersistedAgent>();
120
119
  private readonly runtimes = new Map<string, ChildAgentRuntime>();
121
120
  private readonly runtimeInitializations = new Map<string, Promise<ChildAgentRuntime>>();
122
- private readonly importedMessages = new Map<string, AgentMessage[]>();
123
121
  private readonly tombstones = new Set<string>();
124
122
  private readonly pendingAgentIds = new Set<string>();
125
123
  private deliveryLedger: DeliveryLedger = createDeliveryLedger();
@@ -252,7 +250,6 @@ export class MinimalSubagentsCoordinator {
252
250
  agent.session_id = identity.sessionId;
253
251
  agent.session_leaf_id = identity.sessionLeafId;
254
252
  this.agents.set(agentId, agent);
255
- this.importedMessages.set(agentId, imported.messages);
256
253
  this.dependencies.registry.append(
257
254
  createRegistryEvent(this.dependencies.registry.rootSessionId, "agent-created", { agent }),
258
255
  );
@@ -505,7 +502,6 @@ export class MinimalSubagentsCoordinator {
505
502
  result.trashed_session_files.push(agent.session_file);
506
503
  }
507
504
  this.agents.delete(agent.agent_id);
508
- this.importedMessages.delete(agent.agent_id);
509
505
  this.pruneDeliveryStateForDeletedAgent(agent.agent_id);
510
506
  this.pruneRecentMessageProjectionsForDeletedAgent(agent.agent_id);
511
507
  this.tombstones.add(agent.agent_id);
@@ -520,10 +516,7 @@ export class MinimalSubagentsCoordinator {
520
516
  failedAncestors.add(agent.agent_id);
521
517
  if (runtime && agent.session_file) {
522
518
  try {
523
- this.runtimes.set(
524
- agent.agent_id,
525
- await this.dependencies.sessions.restoreRuntime(agent),
526
- );
519
+ this.runtimes.set(agent.agent_id, await this.dependencies.sessions.openRuntime(agent));
527
520
  } catch (restoreError) {
528
521
  agent.availability = "unavailable";
529
522
  agent.unavailable_reason = `Deletion recovery failed: ${
@@ -559,7 +552,6 @@ export class MinimalSubagentsCoordinator {
559
552
  for (const runtime of abandonedRuntimes) runtime.dispose();
560
553
  this.runtimes.clear();
561
554
  this.runtimeInitializations.clear();
562
- this.importedMessages.clear();
563
555
  this.pendingAgentIds.clear();
564
556
  this.tombstones.clear();
565
557
  this.deliveryLedger = createDeliveryLedger();
@@ -623,7 +615,7 @@ export class MinimalSubagentsCoordinator {
623
615
  });
624
616
  continue;
625
617
  }
626
- const runtime = await this.dependencies.sessions.restoreRuntime(agent);
618
+ const runtime = await this.dependencies.sessions.openRuntime(agent);
627
619
  if (restoreEpoch !== this.lifecycleEpoch) {
628
620
  runtime.dispose();
629
621
  return;
@@ -902,19 +894,14 @@ export class MinimalSubagentsCoordinator {
902
894
  new Error(agent.clone_error ?? `No persistent session exists for ${agent.agent_id}`),
903
895
  );
904
896
  }
905
- const importedMessages = this.importedMessages.get(agent.agent_id);
906
- const initialization = (
907
- importedMessages
908
- ? this.dependencies.sessions.createRuntime({ agent, importedMessages })
909
- : this.dependencies.sessions.restoreRuntime(agent)
910
- )
897
+ const initialization = this.dependencies.sessions
898
+ .openRuntime(agent)
911
899
  .then((runtime) => {
912
900
  if (this.agents.get(agent.agent_id) !== agent) {
913
901
  runtime.dispose();
914
902
  throw new Error(`Minimal subagents runtime replaced while opening ${agent.agent_id}`);
915
903
  }
916
904
  this.runtimes.set(agent.agent_id, runtime);
917
- this.importedMessages.delete(agent.agent_id);
918
905
  return runtime;
919
906
  })
920
907
  .finally(() => {
@@ -1791,10 +1778,7 @@ export class MinimalSubagentsCoordinator {
1791
1778
  message.details.source_agent_id,
1792
1779
  message.details.source_turn_id,
1793
1780
  );
1794
- let releaseClaim!: () => void;
1795
- const claimPromise = new Promise<void>((resolve) => {
1796
- releaseClaim = resolve;
1797
- });
1781
+ const { promise: claimPromise, resolve: releaseClaim } = Promise.withResolvers<void>();
1798
1782
  const pending: PendingParentMessage = {
1799
1783
  deliveryId: delivery.delivery_id,
1800
1784
  message,
@@ -1914,10 +1898,7 @@ export class MinimalSubagentsCoordinator {
1914
1898
  }
1915
1899
 
1916
1900
  private createRecipientIdleWait(agentId: string): CancelableWait {
1917
- let release!: () => void;
1918
- const promise = new Promise<void>((resolve) => {
1919
- release = resolve;
1920
- });
1901
+ const { promise, resolve: release } = Promise.withResolvers<void>();
1921
1902
  const waiters = this.recipientIdleWaiters.get(agentId) ?? new Set();
1922
1903
  waiters.add(release);
1923
1904
  this.recipientIdleWaiters.set(agentId, waiters);
@@ -1932,10 +1913,7 @@ export class MinimalSubagentsCoordinator {
1932
1913
  }
1933
1914
 
1934
1915
  private createAutomaticDeliveryClaimWait(deliveryKey: string): CancelableWait {
1935
- let release!: () => void;
1936
- const promise = new Promise<void>((resolve) => {
1937
- release = resolve;
1938
- });
1916
+ const { promise, resolve: release } = Promise.withResolvers<void>();
1939
1917
  const waiters = this.automaticDeliveryClaimWaiters.get(deliveryKey) ?? new Set();
1940
1918
  waiters.add(release);
1941
1919
  this.automaticDeliveryClaimWaiters.set(deliveryKey, waiters);
@@ -58,23 +58,13 @@ function deliveryTurnKey(sourceAgentId: string, sourceTurnId: string): string {
58
58
  return `${sourceAgentId}\u0000${sourceTurnId}`;
59
59
  }
60
60
 
61
- function cloneTerminalDelivery(delivery: PersistedDelivery): PersistedDelivery {
62
- return structuredClone(delivery);
63
- }
64
-
65
- function cloneCoordinationDelivery(
66
- delivery: PersistedCoordinationDelivery,
67
- ): PersistedCoordinationDelivery {
68
- return structuredClone(delivery);
69
- }
70
-
71
61
  function createTransition(
72
62
  ledger: DeliveryLedger,
73
63
  prunedTerminalDeliveries: readonly PersistedDelivery[] = [],
74
64
  ): DeliveryLedgerTransition {
75
65
  return {
76
66
  ledger,
77
- prunedTerminalDeliveries: prunedTerminalDeliveries.map(cloneTerminalDelivery),
67
+ prunedTerminalDeliveries: prunedTerminalDeliveries.map((delivery) => structuredClone(delivery)),
78
68
  };
79
69
  }
80
70
 
@@ -134,7 +124,7 @@ export function createDeliveryLedger(
134
124
  ): DeliveryLedger {
135
125
  const coordinationDeliveries = (snapshot.coordination_deliveries ?? [])
136
126
  .filter((delivery) => !delivery.settled)
137
- .map(cloneCoordinationDelivery);
127
+ .map((delivery) => structuredClone(delivery));
138
128
  let nextSequence = Math.max(
139
129
  1,
140
130
  snapshot.next_delivery_sequence ?? 1,
@@ -146,7 +136,7 @@ export function createDeliveryLedger(
146
136
  const terminalDeliveries = (snapshot.deliveries ?? [])
147
137
  .filter((delivery) => !delivery.settled)
148
138
  .map((delivery) => {
149
- const restored = cloneTerminalDelivery(delivery);
139
+ const restored = structuredClone(delivery);
150
140
  if (restored.sequence === undefined) restored.sequence = nextSequence++;
151
141
  return restored;
152
142
  });
@@ -161,8 +151,10 @@ export function createDeliveryLedger(
161
151
  /** Serialize pending Delivery Ledger state without exposing mutable internal references. */
162
152
  export function deliveryLedgerSnapshot(ledger: DeliveryLedger): DeliveryLedgerSnapshot {
163
153
  return {
164
- deliveries: ledger.terminalDeliveries.map(cloneTerminalDelivery),
165
- coordination_deliveries: ledger.coordinationDeliveries.map(cloneCoordinationDelivery),
154
+ deliveries: ledger.terminalDeliveries.map((delivery) => structuredClone(delivery)),
155
+ coordination_deliveries: ledger.coordinationDeliveries.map((delivery) =>
156
+ structuredClone(delivery),
157
+ ),
166
158
  wait_claimed_turns: [...ledger.waitClaimedTurns],
167
159
  next_delivery_sequence: ledger.nextSequence,
168
160
  };
@@ -194,7 +186,7 @@ export function addTerminalDelivery(
194
186
  ],
195
187
  nextSequence: ledger.nextSequence + 1,
196
188
  });
197
- return { ...transition, delivery: cloneTerminalDelivery(delivery) };
189
+ return { ...transition, delivery: structuredClone(delivery) };
198
190
  }
199
191
 
200
192
  /** Replay or update one already-sequenced terminal delivery. */
@@ -202,7 +194,7 @@ export function upsertTerminalDelivery(
202
194
  ledger: DeliveryLedger,
203
195
  delivery: PersistedDelivery,
204
196
  ): DeliveryLedgerTransition {
205
- const restored = cloneTerminalDelivery(delivery);
197
+ const restored = structuredClone(delivery);
206
198
  const existing = ledger.terminalDeliveries.find(
207
199
  (current) =>
208
200
  deliveryTurnKey(current.source_agent_id, current.source_turn_id) ===
@@ -251,7 +243,7 @@ export function addCoordinationDelivery(
251
243
  ],
252
244
  nextSequence: ledger.nextSequence + 1,
253
245
  },
254
- delivery: cloneCoordinationDelivery(delivery),
246
+ delivery: structuredClone(delivery),
255
247
  prunedTerminalDeliveries: [],
256
248
  };
257
249
  }
@@ -267,7 +259,7 @@ export function upsertCoordinationDelivery(
267
259
  ...ledger.coordinationDeliveries.filter(
268
260
  (current) => current.delivery_id !== delivery.delivery_id,
269
261
  ),
270
- cloneCoordinationDelivery(delivery),
262
+ structuredClone(delivery),
271
263
  ],
272
264
  nextSequence: Math.max(ledger.nextSequence, delivery.sequence + 1),
273
265
  });
@@ -505,7 +497,7 @@ export function findTerminalDelivery(
505
497
  const delivery = ledger.terminalDeliveries.find(
506
498
  (candidate) => deliveryTurnKey(candidate.source_agent_id, candidate.source_turn_id) === key,
507
499
  );
508
- return delivery ? cloneTerminalDelivery(delivery) : undefined;
500
+ return delivery ? structuredClone(delivery) : undefined;
509
501
  }
510
502
 
511
503
  /** Return one pending Coordination Message by stable delivery identity. */
@@ -516,7 +508,7 @@ export function findCoordinationDelivery(
516
508
  const delivery = ledger.coordinationDeliveries.find(
517
509
  (candidate) => candidate.delivery_id === deliveryId,
518
510
  );
519
- return delivery ? cloneCoordinationDelivery(delivery) : undefined;
511
+ return delivery ? structuredClone(delivery) : undefined;
520
512
  }
521
513
 
522
514
  /** Report whether one source turn currently has a durable wait claim. */
@@ -740,12 +740,6 @@ function validateRegistrySnapshot(
740
740
  }
741
741
  const identityError = validateDeliveryIdentity(delivery, version);
742
742
  if (identityError) return invalidRegistrySnapshot("invalid-delivery-identity", identityError);
743
- if (!isOwnedTurnId(delivery.source_agent_id, delivery.source_turn_id)) {
744
- return invalidRegistrySnapshot(
745
- "invalid-delivery-identity",
746
- "terminal source_turn_id must belong to source_agent_id",
747
- );
748
- }
749
743
  const source = agentsById.get(delivery.source_agent_id);
750
744
  if (!source) {
751
745
  return invalidRegistrySnapshot(
@@ -763,17 +757,6 @@ function validateRegistrySnapshot(
763
757
  for (const delivery of coordinationDeliveries) {
764
758
  const identityError = validateCoordinationIdentity(delivery);
765
759
  if (identityError) return invalidRegistrySnapshot("invalid-delivery-identity", identityError);
766
- if (
767
- !isOwnedTurnId(
768
- delivery.message.details.source_agent_id,
769
- delivery.message.details.source_turn_id,
770
- )
771
- ) {
772
- return invalidRegistrySnapshot(
773
- "invalid-delivery-identity",
774
- "Coordination Message source_turn_id must belong to source_agent_id",
775
- );
776
- }
777
760
  if (
778
761
  !areAdjacentAgents(
779
762
  delivery.message.details.source_agent_id,
@@ -1228,13 +1211,7 @@ export function replayRegistryEntries(
1228
1211
  }
1229
1212
  });
1230
1213
 
1231
- let checkpointIndex = -1;
1232
- for (let index = parsedEvents.length - 1; index >= 0; index--) {
1233
- if (parsedEvents[index]?.event.event === "checkpoint") {
1234
- checkpointIndex = index;
1235
- break;
1236
- }
1237
- }
1214
+ const checkpointIndex = parsedEvents.findLastIndex(({ event }) => event.event === "checkpoint");
1238
1215
  const checkpointEvent = checkpointIndex >= 0 ? parsedEvents[checkpointIndex]?.event : undefined;
1239
1216
  const checkpoint = checkpointEvent?.event === "checkpoint" ? checkpointEvent.snapshot : undefined;
1240
1217
  const agents = new Map(
@@ -3,7 +3,7 @@ import type {
3
3
  MessageRenderer,
4
4
  ToolDefinition,
5
5
  } from "@earendil-works/pi-coding-agent";
6
- import { Type, type Static, type TSchema } from "typebox";
6
+ import { Type, type Static } from "typebox";
7
7
  import { Value } from "typebox/value";
8
8
 
9
9
  /** Names of the six coordinator tools with custom transcript renderers. */
@@ -107,16 +107,6 @@ const ManagementCallArgumentsSchema = Type.Object({
107
107
  recursive: Type.Optional(Type.Boolean()),
108
108
  });
109
109
 
110
- /** Tool-call contracts keep each coordinator name correlated with its own arguments. */
111
- export const coordinatorToolCallSchemas = {
112
- subagent: SpawnCallArgumentsSchema,
113
- agent_message: MessageCallArgumentsSchema,
114
- subagent_wait: WaitCallArgumentsSchema,
115
- subagent_status: StatusCallArgumentsSchema,
116
- subagent_cancel: ManagementCallArgumentsSchema,
117
- subagent_delete: ManagementCallArgumentsSchema,
118
- } as const satisfies { readonly [Name in CoordinatorToolName]: TSchema };
119
-
120
110
  const SpawnDetailsSchema = Type.Object({
121
111
  agent_id: Type.String(),
122
112
  turn_id: Type.String(),
@@ -183,15 +173,11 @@ const DeleteDetailsSchema = Type.Object({
183
173
  ),
184
174
  });
185
175
 
186
- /** Result-detail contracts keep current and tolerated legacy transcript shapes explicit. */
187
- export const coordinatorToolResultSchemas = {
188
- subagent: SpawnDetailsSchema,
189
- agent_message: Type.Union([CurrentMessageDetailsSchema, LegacyMessageDetailsSchema]),
190
- subagent_wait: Type.Union([WaitMessageDetailsSchema, WaitTurnDetailsSchema]),
191
- subagent_status: StatusDetailsSchema,
192
- subagent_cancel: CancelDetailsSchema,
193
- subagent_delete: DeleteDetailsSchema,
194
- } as const satisfies { readonly [Name in CoordinatorToolName]: TSchema };
176
+ const MessageRenderDetailsSchema = Type.Union([
177
+ CurrentMessageDetailsSchema,
178
+ LegacyMessageDetailsSchema,
179
+ ]);
180
+ const WaitRenderDetailsSchema = Type.Union([WaitMessageDetailsSchema, WaitTurnDetailsSchema]);
195
181
 
196
182
  export type SpawnCallArguments = Static<typeof SpawnCallArgumentsSchema>;
197
183
  export type MessageCallArguments = Static<typeof MessageCallArgumentsSchema>;
@@ -199,12 +185,8 @@ export type WaitCallArguments = Static<typeof WaitCallArgumentsSchema>;
199
185
  export type StatusCallArguments = Static<typeof StatusCallArgumentsSchema>;
200
186
  export type ManagementCallArguments = Static<typeof ManagementCallArgumentsSchema>;
201
187
  export type SpawnRenderDetails = Static<typeof SpawnDetailsSchema>;
202
- export type MessageRenderDetails = Static<
203
- typeof CurrentMessageDetailsSchema | typeof LegacyMessageDetailsSchema
204
- >;
205
- export type WaitRenderDetails = Static<
206
- typeof WaitMessageDetailsSchema | typeof WaitTurnDetailsSchema
207
- >;
188
+ export type MessageRenderDetails = Static<typeof MessageRenderDetailsSchema>;
189
+ export type WaitRenderDetails = Static<typeof WaitRenderDetailsSchema>;
208
190
  export type StatusRenderDetails = Static<typeof StatusDetailsSchema>;
209
191
  export type CancelRenderDetails = Static<typeof CancelDetailsSchema>;
210
192
  export type DeleteRenderDetails = Static<typeof DeleteDetailsSchema>;
@@ -263,13 +245,9 @@ export function parseCoordinatorToolResult(
263
245
  case "subagent":
264
246
  return Value.Check(SpawnDetailsSchema, details) ? { toolName, details } : undefined;
265
247
  case "agent_message":
266
- return Value.Check(coordinatorToolResultSchemas.agent_message, details)
267
- ? { toolName, details }
268
- : undefined;
248
+ return Value.Check(MessageRenderDetailsSchema, details) ? { toolName, details } : undefined;
269
249
  case "subagent_wait":
270
- return Value.Check(coordinatorToolResultSchemas.subagent_wait, details)
271
- ? { toolName, details }
272
- : undefined;
250
+ return Value.Check(WaitRenderDetailsSchema, details) ? { toolName, details } : undefined;
273
251
  case "subagent_status":
274
252
  return Value.Check(StatusDetailsSchema, details) ? { toolName, details } : undefined;
275
253
  case "subagent_cancel":
@@ -34,7 +34,6 @@ import {
34
34
  type RenderStatusAgent,
35
35
  type SpawnCallArguments,
36
36
  type SpawnRenderDetails,
37
- type StatusCallArguments,
38
37
  type StatusRenderDetails,
39
38
  type WaitCallArguments,
40
39
  type WaitRenderDetails,
@@ -259,59 +258,6 @@ function renderManagementToolCall(
259
258
  );
260
259
  }
261
260
 
262
- type CoordinatorToolCallRenderers = {
263
- readonly subagent: (args: SpawnCallArguments, theme: MinimalSubagentsRenderTheme) => Component;
264
- readonly agent_message: (
265
- args: MessageCallArguments,
266
- theme: MinimalSubagentsRenderTheme,
267
- ) => Component;
268
- readonly subagent_wait: (
269
- args: WaitCallArguments,
270
- theme: MinimalSubagentsRenderTheme,
271
- ) => Component;
272
- readonly subagent_status: (
273
- args: StatusCallArguments,
274
- theme: MinimalSubagentsRenderTheme,
275
- ) => Component;
276
- readonly subagent_cancel: (
277
- args: ManagementCallArguments,
278
- theme: MinimalSubagentsRenderTheme,
279
- ) => Component;
280
- readonly subagent_delete: (
281
- args: ManagementCallArguments,
282
- theme: MinimalSubagentsRenderTheme,
283
- ) => Component;
284
- };
285
-
286
- const COORDINATOR_TOOL_CALL_RENDERERS = {
287
- subagent: (args, theme) =>
288
- new Text(
289
- `${coordinatorToolCallTitle(theme, "Subagent")} ${theme.fg("accent", args.agent_id ?? "generated")}${coordinatorToolCallPreview(theme, args.task)}`,
290
- 0,
291
- 0,
292
- ),
293
- agent_message: (args, theme) =>
294
- new Text(
295
- `${coordinatorToolCallTitle(theme, "Message")} ${theme.fg("accent", args.agent_id ?? "parent")}${coordinatorToolCallPreview(theme, args.message)}`,
296
- 0,
297
- 0,
298
- ),
299
- subagent_wait: (args, theme) =>
300
- new Text(
301
- `${coordinatorToolCallTitle(theme, "Wait")} ${theme.fg("accent", args.agent_id ?? "agent")}`,
302
- 0,
303
- 0,
304
- ),
305
- subagent_status: (args, theme) =>
306
- new Text(
307
- `${coordinatorToolCallTitle(theme, "Status")} ${theme.fg("accent", args.agent_id ?? "children")}`,
308
- 0,
309
- 0,
310
- ),
311
- subagent_cancel: (args, theme) => renderManagementToolCall("Cancel", args, theme),
312
- subagent_delete: (args, theme) => renderManagementToolCall("Delete", args, theme),
313
- } satisfies CoordinatorToolCallRenderers;
314
-
315
261
  function renderSpawnResult(
316
262
  details: SpawnRenderDetails,
317
263
  options: ToolRenderResultOptions,
@@ -660,51 +606,6 @@ function renderDeleteResult(
660
606
  return container;
661
607
  }
662
608
 
663
- type CoordinatorToolResultRenderers = {
664
- readonly subagent: (
665
- details: SpawnRenderDetails,
666
- options: ToolRenderResultOptions,
667
- theme: MinimalSubagentsRenderTheme,
668
- args: SpawnCallArguments,
669
- ) => Component;
670
- readonly agent_message: (
671
- details: MessageRenderDetails,
672
- options: ToolRenderResultOptions,
673
- theme: MinimalSubagentsRenderTheme,
674
- args: MessageCallArguments,
675
- ) => Component;
676
- readonly subagent_wait: (
677
- details: WaitRenderDetails,
678
- options: ToolRenderResultOptions,
679
- theme: MinimalSubagentsRenderTheme,
680
- args: WaitCallArguments,
681
- ) => Component;
682
- readonly subagent_status: (
683
- details: StatusRenderDetails,
684
- options: ToolRenderResultOptions,
685
- theme: MinimalSubagentsRenderTheme,
686
- ) => Component;
687
- readonly subagent_cancel: (
688
- details: CancelRenderDetails,
689
- options: ToolRenderResultOptions,
690
- theme: MinimalSubagentsRenderTheme,
691
- ) => Component;
692
- readonly subagent_delete: (
693
- details: DeleteRenderDetails,
694
- options: ToolRenderResultOptions,
695
- theme: MinimalSubagentsRenderTheme,
696
- ) => Component;
697
- };
698
-
699
- const COORDINATOR_TOOL_RESULT_RENDERERS = {
700
- subagent: renderSpawnResult,
701
- agent_message: renderMessageResult,
702
- subagent_wait: renderWaitResult,
703
- subagent_status: renderStatusResult,
704
- subagent_cancel: renderCancelResult,
705
- subagent_delete: renderDeleteResult,
706
- } satisfies CoordinatorToolResultRenderers;
707
-
708
609
  /** Render one of the six coordinator tool calls with a shared native Pi grammar. */
709
610
  export function renderCoordinatorToolCall(
710
611
  toolName: CoordinatorToolName,
@@ -715,17 +616,33 @@ export function renderCoordinatorToolCall(
715
616
  if (parsed === undefined) return new Text(coordinatorToolCallTitle(theme, toolName), 0, 0);
716
617
  switch (parsed.toolName) {
717
618
  case "subagent":
718
- return COORDINATOR_TOOL_CALL_RENDERERS.subagent(parsed.args, theme);
619
+ return new Text(
620
+ `${coordinatorToolCallTitle(theme, "Subagent")} ${theme.fg("accent", parsed.args.agent_id ?? "generated")}${coordinatorToolCallPreview(theme, parsed.args.task)}`,
621
+ 0,
622
+ 0,
623
+ );
719
624
  case "agent_message":
720
- return COORDINATOR_TOOL_CALL_RENDERERS.agent_message(parsed.args, theme);
625
+ return new Text(
626
+ `${coordinatorToolCallTitle(theme, "Message")} ${theme.fg("accent", parsed.args.agent_id ?? "parent")}${coordinatorToolCallPreview(theme, parsed.args.message)}`,
627
+ 0,
628
+ 0,
629
+ );
721
630
  case "subagent_wait":
722
- return COORDINATOR_TOOL_CALL_RENDERERS.subagent_wait(parsed.args, theme);
631
+ return new Text(
632
+ `${coordinatorToolCallTitle(theme, "Wait")} ${theme.fg("accent", parsed.args.agent_id ?? "agent")}`,
633
+ 0,
634
+ 0,
635
+ );
723
636
  case "subagent_status":
724
- return COORDINATOR_TOOL_CALL_RENDERERS.subagent_status(parsed.args, theme);
637
+ return new Text(
638
+ `${coordinatorToolCallTitle(theme, "Status")} ${theme.fg("accent", parsed.args.agent_id ?? "children")}`,
639
+ 0,
640
+ 0,
641
+ );
725
642
  case "subagent_cancel":
726
- return COORDINATOR_TOOL_CALL_RENDERERS.subagent_cancel(parsed.args, theme);
643
+ return renderManagementToolCall("Cancel", parsed.args, theme);
727
644
  case "subagent_delete":
728
- return COORDINATOR_TOOL_CALL_RENDERERS.subagent_delete(parsed.args, theme);
645
+ return renderManagementToolCall("Delete", parsed.args, theme);
729
646
  }
730
647
  }
731
648
 
@@ -743,44 +660,32 @@ export function renderCoordinatorToolResult(
743
660
  const parsedCall = parseCoordinatorToolCall(toolName, args);
744
661
  switch (parsedResult.toolName) {
745
662
  case "subagent":
746
- return COORDINATOR_TOOL_RESULT_RENDERERS.subagent(
663
+ return renderSpawnResult(
747
664
  parsedResult.details,
748
665
  options,
749
666
  theme,
750
667
  parsedCall?.toolName === "subagent" ? parsedCall.args : {},
751
668
  );
752
669
  case "agent_message":
753
- return COORDINATOR_TOOL_RESULT_RENDERERS.agent_message(
670
+ return renderMessageResult(
754
671
  parsedResult.details,
755
672
  options,
756
673
  theme,
757
674
  parsedCall?.toolName === "agent_message" ? parsedCall.args : {},
758
675
  );
759
676
  case "subagent_wait":
760
- return COORDINATOR_TOOL_RESULT_RENDERERS.subagent_wait(
677
+ return renderWaitResult(
761
678
  parsedResult.details,
762
679
  options,
763
680
  theme,
764
681
  parsedCall?.toolName === "subagent_wait" ? parsedCall.args : {},
765
682
  );
766
683
  case "subagent_status":
767
- return COORDINATOR_TOOL_RESULT_RENDERERS.subagent_status(
768
- parsedResult.details,
769
- options,
770
- theme,
771
- );
684
+ return renderStatusResult(parsedResult.details, options, theme);
772
685
  case "subagent_cancel":
773
- return COORDINATOR_TOOL_RESULT_RENDERERS.subagent_cancel(
774
- parsedResult.details,
775
- options,
776
- theme,
777
- );
686
+ return renderCancelResult(parsedResult.details, options, theme);
778
687
  case "subagent_delete":
779
- return COORDINATOR_TOOL_RESULT_RENDERERS.subagent_delete(
780
- parsedResult.details,
781
- options,
782
- theme,
783
- );
688
+ return renderDeleteResult(parsedResult.details, options, theme);
784
689
  }
785
690
  }
786
691
 
@@ -53,7 +53,6 @@ import type {
53
53
  PersistedAgent,
54
54
  PersistedSessionIdentity,
55
55
  ProjectContextMode,
56
- RuntimeCreationRequest,
57
56
  RuntimeProfile,
58
57
  RuntimeTurnOutcome,
59
58
  } from "./minimal-subagents-types.js";
@@ -203,12 +202,14 @@ function findLatestChildSessionRecord<TRecordSchema extends TSchema>(
203
202
  customType: string,
204
203
  schema: TRecordSchema,
205
204
  ): Static<TRecordSchema> | undefined {
206
- for (let index = entries.length - 1; index >= 0; index--) {
207
- const entry = entries[index];
208
- if (!entry || entry.type !== "custom" || entry.customType !== customType) continue;
209
- if (Value.Check(schema, entry.data)) return entry.data;
210
- }
211
- return undefined;
205
+ return entries.findLast(
206
+ (
207
+ entry,
208
+ ): entry is Extract<SessionEntry, { type: "custom" }> & {
209
+ data: Static<TRecordSchema>;
210
+ } =>
211
+ entry.type === "custom" && entry.customType === customType && Value.Check(schema, entry.data),
212
+ )?.data;
212
213
  }
213
214
 
214
215
  function findLatestForkGeneration(
@@ -247,18 +248,17 @@ function findCurrentForkOwnership(
247
248
  entries: ReturnType<SessionManager["getBranch"]>,
248
249
  cloneSessionId: string,
249
250
  ): ForkOwnershipRecord | undefined {
250
- for (let index = entries.length - 1; index >= 0; index--) {
251
- const entry = entries[index];
252
- if (!entry || entry.type !== "custom" || entry.customType !== FORK_OWNERSHIP_ENTRY_TYPE)
253
- continue;
254
- if (
251
+ return entries.findLast(
252
+ (
253
+ entry,
254
+ ): entry is Extract<SessionEntry, { type: "custom" }> & {
255
+ data: ForkOwnershipRecord;
256
+ } =>
257
+ entry.type === "custom" &&
258
+ entry.customType === FORK_OWNERSHIP_ENTRY_TYPE &&
255
259
  Value.Check(ForkOwnershipRecordSchema, entry.data) &&
256
- entry.data.clone_session_id === cloneSessionId
257
- ) {
258
- return entry.data;
259
- }
260
- }
261
- return undefined;
260
+ entry.data.clone_session_id === cloneSessionId,
261
+ )?.data;
262
262
  }
263
263
 
264
264
  function verifyForkCloneProvenance(
@@ -527,17 +527,6 @@ class PiChildAgentRuntime implements ChildAgentRuntime {
527
527
  });
528
528
  }
529
529
 
530
- get sessionFile(): string {
531
- const sessionFile = this.session.sessionFile;
532
- if (!sessionFile)
533
- throw new Error("Minimal subagents child runtime lost its persistent session file");
534
- return sessionFile;
535
- }
536
-
537
- get sessionId(): string {
538
- return this.session.sessionId;
539
- }
540
-
541
530
  get sessionLeafId(): string | undefined {
542
531
  return this.session.sessionManager.getLeafId() ?? undefined;
543
532
  }
@@ -720,14 +709,6 @@ export class PiAgentSessionFactory implements AgentSessionFactory {
720
709
  });
721
710
  }
722
711
 
723
- createRuntime(request: RuntimeCreationRequest): Promise<ChildAgentRuntime> {
724
- return this.openRuntime(request.agent);
725
- }
726
-
727
- restoreRuntime(agent: PersistedAgent): Promise<ChildAgentRuntime> {
728
- return this.openRuntime(agent);
729
- }
730
-
731
712
  resolveLaunchMissingDependencies(agent: PersistedAgent): Promise<string[]> {
732
713
  return this.findMissingDependencies(agent, false);
733
714
  }
@@ -967,7 +948,8 @@ export class PiAgentSessionFactory implements AgentSessionFactory {
967
948
  return discovery;
968
949
  }
969
950
 
970
- private async openRuntime(agent: PersistedAgent): Promise<ChildAgentRuntime> {
951
+ /** Open one verified persisted Child Agent runtime for launch or restoration. */
952
+ async openRuntime(agent: PersistedAgent): Promise<ChildAgentRuntime> {
971
953
  if (!agent.session_file)
972
954
  throw new Error(`Minimal subagents restore: ${agent.agent_id} has no session file`);
973
955
  const model = this.modelById.get(agent.launch_contract.model);
@@ -9,7 +9,6 @@ import {
9
9
  type ToolDefinition,
10
10
  type ToolRenderResultOptions,
11
11
  } from "@earendil-works/pi-coding-agent";
12
- import type { Static } from "typebox";
13
12
  import type { MinimalSubagentsCoordinator } from "./minimal-subagents-coordinator.js";
14
13
  import type { MinimalSubagentsModelRole } from "./minimal-subagents-config.js";
15
14
  import {
@@ -53,27 +52,6 @@ export interface CoordinatorToolDefinitionOptions {
53
52
  onAttention?: (message: string) => void;
54
53
  }
55
54
 
56
- /** Arguments consumed by the wait tool's narrow coordinator execution seam. */
57
- export type CoordinatorWaitToolParameters = Static<
58
- ReturnType<typeof createCoordinatorToolSchemas>["subagent_wait"]
59
- >;
60
-
61
- /** Forward one typed wait-tool request without requiring an unrelated Pi execution context. */
62
- export function executeCoordinatorWaitTool(
63
- coordinator: Pick<CoordinatorToolOperations, "wait">,
64
- callerId: string,
65
- parameters: CoordinatorWaitToolParameters,
66
- signal: AbortSignal | undefined,
67
- ): Promise<WaitResult> {
68
- return coordinator.wait(
69
- callerId,
70
- parameters.agent_id,
71
- parameters.timeout_ms,
72
- signal,
73
- parameters.turn_id,
74
- );
75
- }
76
-
77
55
  function buildModelRolePromptGuidelines(
78
56
  modelRoles: readonly MinimalSubagentsModelRole[],
79
57
  ): string[] | undefined {
@@ -243,11 +221,12 @@ export function createCoordinatorToolDefinitions(
243
221
  waitingInterval.unref?.();
244
222
  try {
245
223
  return await runCoordinatorToolActivity(options, async () => {
246
- const result = await executeCoordinatorWaitTool(
247
- options.coordinator,
224
+ const result = await options.coordinator.wait(
248
225
  options.callerId,
249
- parameters,
226
+ parameters.agent_id,
227
+ parameters.timeout_ms,
250
228
  signal,
229
+ parameters.turn_id,
251
230
  );
252
231
  return {
253
232
  ...structuredToolResult(result),
@@ -1,9 +1,6 @@
1
1
  import type { AgentMessage, ThinkingLevel } from "@earendil-works/pi-agent-core";
2
2
  import type { Usage } from "@earendil-works/pi-ai";
3
3
 
4
- /** Canonical path-like identity for one persistent subagent. */
5
- export type AgentId = string & { readonly __agentId: unique symbol };
6
-
7
4
  /** Stable identity for one prompt and its complete assistant/tool loop. */
8
5
  export type TurnId = string & { readonly __turnId: unique symbol };
9
6
 
@@ -190,8 +187,6 @@ export interface CoordinatorMessage {
190
187
 
191
188
  /** Process-local adapter around one SDK-created Pi child session. */
192
189
  export interface ChildAgentRuntime {
193
- readonly sessionFile: string;
194
- readonly sessionId: string;
195
190
  readonly sessionLeafId: string | undefined;
196
191
  readonly isRunning: boolean;
197
192
  runPrompt(
@@ -219,17 +214,11 @@ export interface PersistedSessionIdentity {
219
214
  sessionLeafId?: string;
220
215
  }
221
216
 
222
- /** Combines a persisted agent record with first-launch imported context. */
223
- export interface RuntimeCreationRequest {
224
- agent: PersistedAgent;
225
- importedMessages: AgentMessage[];
226
- }
227
-
228
217
  /** Pi-specific session operations injected into the pure coordinator. */
229
218
  export interface AgentSessionFactory {
230
219
  createIdentity(agent: PersistedAgent, importedMessages: AgentMessage[]): PersistedSessionIdentity;
231
- createRuntime(request: RuntimeCreationRequest): Promise<ChildAgentRuntime>;
232
- restoreRuntime(agent: PersistedAgent): Promise<ChildAgentRuntime>;
220
+ /** Open one verified persisted Child Agent runtime for launch or restoration. */
221
+ openRuntime(agent: PersistedAgent): Promise<ChildAgentRuntime>;
233
222
  resolveLaunchMissingDependencies(agent: PersistedAgent): Promise<string[]>;
234
223
  resolveRestorationMissingDependencies(agent: PersistedAgent): Promise<string[]>;
235
224
  resolveThinkingLevel(modelId: string, requested: ThinkingLevel): ThinkingLevel;