@ian-pascoe/pi-minimal-subagents 0.20.1 → 0.21.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/README.md CHANGED
@@ -51,9 +51,11 @@ From this package checkout, load the source directly with
51
51
  pass `turn_id` to re-read a turn whose result is still retained, claimed or
52
52
  not.
53
53
  4. `subagent_status` inspects children; `subagent_cancel` stops work but keeps
54
- the session; `subagent_delete` removes it. Deleted IDs cannot be reused, and
55
- spawning with an existing `agent_id` fails with that child's state and the
56
- next step (`agent_message`, `subagent_delete`, or another `agent_id`).
54
+ the session; `subagent_delete` removes it. A deleted child's ID can be
55
+ reused, and the new child starts fresh; generated `agent-N` IDs are never
56
+ reused. Spawning with a live child's `agent_id` fails with that child's
57
+ state and the next step (`agent_message`, `subagent_delete`, or another
58
+ `agent_id`).
57
59
 
58
60
  ## Configuration
59
61
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ian-pascoe/pi-minimal-subagents",
3
- "version": "0.20.1",
3
+ "version": "0.21.0",
4
4
  "private": false,
5
5
  "description": "Persistent nested subagents with bounded delegation for Pi",
6
6
  "keywords": [
@@ -203,6 +203,11 @@ export class MinimalSubagentsCoordinator {
203
203
  private readonly runtimeInitializations = new Map<string, Promise<ChildAgentRuntime>>();
204
204
  private readonly tombstones = new Set<string>();
205
205
  private readonly pendingAgentIds = new Set<string>();
206
+ /**
207
+ * Agent IDs of every subtree whose deletion is still running, each with the deletions covering
208
+ * it; none of them may spawn. A branch restore forgets them all.
209
+ */
210
+ private readonly deletingAgentIds = new Map<string, Set<symbol>>();
206
211
  private deliveryLedger: DeliveryLedger = createDeliveryLedger();
207
212
  private readonly waiters = new Map<string, Set<TurnWaiter>>();
208
213
  private readonly pendingParentMessages = new Map<string, PendingParentMessage[]>();
@@ -258,6 +263,8 @@ export class MinimalSubagentsCoordinator {
258
263
  this.assertAccepting();
259
264
  this.assertCallerExists(callerId);
260
265
  this.assertCallerMaySpawn(callerId);
266
+ this.assertSpawnCallerNotDeleting(callerId);
267
+ const callerAgent = this.agents.get(callerId);
261
268
  if (parameters.task.trim().length === 0) {
262
269
  throw new Error("Minimal subagents spawn validation: task must not be empty");
263
270
  }
@@ -265,12 +272,11 @@ export class MinimalSubagentsCoordinator {
265
272
  const friendlyId = parameters.agent_id ?? this.generateFriendlyId(callerId);
266
273
  this.validateFriendlyId(friendlyId);
267
274
  const agentId = this.buildChildAgentId(callerId, friendlyId);
268
- if (this.tombstones.has(agentId)) {
269
- throw new Error(
270
- `Minimal subagents agent ID ${agentId} belonged to a deleted agent and cannot be reused; choose a different agent_id`,
271
- );
272
- }
273
- if (this.agents.has(agentId) || this.pendingAgentIds.has(agentId)) {
275
+ if (
276
+ this.agents.has(agentId) ||
277
+ this.pendingAgentIds.has(agentId) ||
278
+ this.deletingAgentIds.has(agentId)
279
+ ) {
274
280
  throw new Error(this.duplicateAgentIdMessage(agentId));
275
281
  }
276
282
 
@@ -366,6 +372,13 @@ export class MinimalSubagentsCoordinator {
366
372
  agent.session_file = identity.sessionFile;
367
373
  agent.session_id = identity.sessionId;
368
374
  agent.session_leaf_id = identity.sessionLeafId;
375
+ // The caller may have been deleted, and its Agent ID reused, while the session was created.
376
+ if (callerId !== "root" && this.agents.get(callerId) !== callerAgent) {
377
+ throw new Error(`Minimal subagents spawn: caller ${callerId} was deleted`);
378
+ }
379
+ this.assertSpawnCallerNotDeleting(callerId);
380
+ // A deleted Agent ID is free again; the new Child Agent shares nothing with the deleted one.
381
+ this.tombstones.delete(agentId);
369
382
  this.agents.set(agentId, agent);
370
383
  this.dependencies.registry.append(
371
384
  createRegistryEvent(this.dependencies.registry.rootSessionId, "agent-created", { agent }),
@@ -761,53 +774,113 @@ export class MinimalSubagentsCoordinator {
761
774
  failures: [],
762
775
  };
763
776
  const failedAncestors = new Set<string>();
777
+ const deletion = Symbol(agentId);
778
+ const deletingIds = ordered.map((agent) => agent.agent_id);
779
+ for (const id of deletingIds) {
780
+ const deletions = this.deletingAgentIds.get(id) ?? new Set<symbol>();
781
+ deletions.add(deletion);
782
+ this.deletingAgentIds.set(id, deletions);
783
+ }
784
+ try {
785
+ for (const agent of ordered) {
786
+ if (
787
+ [...failedAncestors].some(
788
+ (failedId) => agent.agent_id === failedId || failedId.startsWith(`${agent.agent_id}.`),
789
+ )
790
+ ) {
791
+ continue;
792
+ }
793
+ // A branch restore during an earlier await replaces every record; leave the new ones alone.
794
+ if (!this.isCurrentAgent(agent)) continue;
795
+ await this.deleteOne(agent, result, failedAncestors);
796
+ }
797
+ } finally {
798
+ for (const id of deletingIds) {
799
+ const deletions = this.deletingAgentIds.get(id);
800
+ deletions?.delete(deletion);
801
+ if (deletions?.size === 0) this.deletingAgentIds.delete(id);
802
+ }
803
+ }
804
+ return result;
805
+ }
764
806
 
765
- for (const agent of ordered) {
766
- if (
767
- [...failedAncestors].some(
768
- (failedId) => agent.agent_id === failedId || failedId.startsWith(`${agent.agent_id}.`),
769
- )
770
- ) {
771
- continue;
807
+ private async deleteOne(
808
+ agent: PersistedAgent,
809
+ result: DeleteResult,
810
+ failedAncestors: Set<string>,
811
+ ): Promise<void> {
812
+ const runtime = this.runtimes.get(agent.agent_id);
813
+ try {
814
+ if (agent.active_turn_id) await this.cancelActiveTurn(agent);
815
+ await runtime?.dispose();
816
+ if (!this.isCurrentAgent(agent)) return;
817
+ this.runtimes.delete(agent.agent_id);
818
+ if (agent.session_file) {
819
+ await this.dependencies.sessions.trashSession(agent);
820
+ result.trashed_session_files.push(agent.session_file);
772
821
  }
773
- const runtime = this.runtimes.get(agent.agent_id);
822
+ if (!this.isCurrentAgent(agent)) return;
823
+ this.agents.delete(agent.agent_id);
824
+ // A runtime that finished opening during the awaits above belongs to the deleted agent.
825
+ const openedLate = this.runtimes.get(agent.agent_id);
826
+ this.runtimes.delete(agent.agent_id);
827
+ // A respawn with this Agent ID must neither join the old runtime opening nor queue behind
828
+ // the old agent's deliveries; each settles on its own and drops only its own map entry.
829
+ this.runtimeInitializations.delete(agent.agent_id);
830
+ this.recipientQueues.delete(agent.agent_id);
831
+ this.pruneDeliveryStateForDeletedAgent(agent.agent_id);
832
+ this.pruneRecentMessageProjectionsForDeletedAgent(agent.agent_id);
833
+ this.tombstones.add(agent.agent_id);
834
+ result.deleted_agent_ids.push(agent.agent_id);
835
+ this.dependencies.registry.append(
836
+ createRegistryEvent(this.dependencies.registry.rootSessionId, "agent-deleted", {
837
+ agent_ids: [agent.agent_id],
838
+ }),
839
+ );
774
840
  try {
775
- if (agent.active_turn_id) await this.cancelActiveTurn(agent);
776
- await runtime?.dispose();
777
- this.runtimes.delete(agent.agent_id);
778
- if (agent.session_file) {
779
- await this.dependencies.sessions.trashSession(agent);
780
- result.trashed_session_files.push(agent.session_file);
781
- }
782
- this.agents.delete(agent.agent_id);
783
- this.pruneDeliveryStateForDeletedAgent(agent.agent_id);
784
- this.pruneRecentMessageProjectionsForDeletedAgent(agent.agent_id);
785
- this.tombstones.add(agent.agent_id);
786
- result.deleted_agent_ids.push(agent.agent_id);
787
- this.dependencies.registry.append(
788
- createRegistryEvent(this.dependencies.registry.rootSessionId, "agent-deleted", {
789
- agent_ids: [agent.agent_id],
790
- }),
791
- );
792
- } catch (error) {
793
- failedAncestors.add(agent.agent_id);
794
- if (runtime && agent.session_file) {
795
- try {
796
- this.runtimes.set(agent.agent_id, await this.dependencies.sessions.openRuntime(agent));
797
- } catch (restoreError) {
798
- agent.availability = "unavailable";
799
- agent.unavailable_reason = `Deletion recovery failed: ${
800
- restoreError instanceof Error ? restoreError.message : String(restoreError)
801
- }`;
802
- }
841
+ await openedLate?.dispose();
842
+ } catch {
843
+ // The agent is already deleted; a runtime that fails to close changes nothing it owns.
844
+ }
845
+ } catch (error) {
846
+ failedAncestors.add(agent.agent_id);
847
+ if (runtime && agent.session_file && this.isCurrentAgent(agent)) {
848
+ try {
849
+ this.runtimes.set(agent.agent_id, await this.dependencies.sessions.openRuntime(agent));
850
+ } catch (restoreError) {
851
+ agent.availability = "unavailable";
852
+ agent.unavailable_reason = `Deletion recovery failed: ${
853
+ restoreError instanceof Error ? restoreError.message : String(restoreError)
854
+ }`;
803
855
  }
804
- result.failures.push({
805
- agent_id: agent.agent_id,
806
- error: error instanceof Error ? error.message : String(error),
807
- });
808
856
  }
857
+ result.failures.push({
858
+ agent_id: agent.agent_id,
859
+ error: error instanceof Error ? error.message : String(error),
860
+ });
861
+ }
862
+ }
863
+
864
+ /**
865
+ * Reject a coordinator tool call from a session that no longer owns `callerId`, such as a
866
+ * deleted Child Agent's runtime whose Agent ID a new Child Agent now reuses.
867
+ */
868
+ assertCurrentCaller(callerId: string, sessionId: string): void {
869
+ if (callerId === "root") return;
870
+ if (this.agents.get(callerId)?.session_id !== sessionId) {
871
+ throw new Error(`Minimal subagents caller ${callerId}: caller session is no longer current`);
872
+ }
873
+ }
874
+
875
+ /** Report whether this record is still the live agent for its Agent ID on the current branch. */
876
+ private isCurrentAgent(agent: PersistedAgent): boolean {
877
+ return this.agents.get(agent.agent_id) === agent;
878
+ }
879
+
880
+ private assertSpawnCallerNotDeleting(callerId: string): void {
881
+ if (this.deletingAgentIds.has(callerId)) {
882
+ throw new Error(`Minimal subagents spawn: ${callerId} is being deleted`);
809
883
  }
810
- return result;
811
884
  }
812
885
 
813
886
  /** Restore non-deleted descendants, interrupt unfinished work, and reconcile pending successful output. */
@@ -829,6 +902,7 @@ export class MinimalSubagentsCoordinator {
829
902
  this.runtimes.clear();
830
903
  this.runtimeInitializations.clear();
831
904
  this.pendingAgentIds.clear();
905
+ this.deletingAgentIds.clear();
832
906
  this.tombstones.clear();
833
907
  this.waiters.clear();
834
908
  this.waitHandedDeliveryIds.clear();
@@ -1245,7 +1319,7 @@ export class MinimalSubagentsCoordinator {
1245
1319
  const initialization = this.dependencies.sessions
1246
1320
  .openRuntime(agent)
1247
1321
  .then(async (runtime) => {
1248
- if (this.agents.get(agent.agent_id) !== agent) {
1322
+ if (!this.isCurrentAgent(agent)) {
1249
1323
  await runtime.dispose();
1250
1324
  throw new Error(`Minimal subagents runtime replaced while opening ${agent.agent_id}`);
1251
1325
  }
@@ -1260,7 +1334,7 @@ export class MinimalSubagentsCoordinator {
1260
1334
  return runtime;
1261
1335
  })
1262
1336
  .catch((error) => {
1263
- if (!openingForTurn && this.agents.get(agent.agent_id) === agent) {
1337
+ if (!openingForTurn && this.isCurrentAgent(agent)) {
1264
1338
  agent.availability = "unavailable";
1265
1339
  agent.latest_activity_at = this.now().toISOString();
1266
1340
  agent.unavailable_reason = error instanceof Error ? error.message : String(error);
@@ -1390,6 +1464,8 @@ export class MinimalSubagentsCoordinator {
1390
1464
  try {
1391
1465
  const handOff = async () => {
1392
1466
  if (delivery.destination_agent_id !== "root") {
1467
+ // A stale hand-off must not open the runtime of a respawn that reuses the Agent ID.
1468
+ if (!this.isTerminalDeliveryCurrent(delivery)) return;
1393
1469
  await this.ensureRuntime(
1394
1470
  this.requireUsableAgent(delivery.destination_agent_id, "message"),
1395
1471
  );
@@ -1676,7 +1752,7 @@ export class MinimalSubagentsCoordinator {
1676
1752
  const target = this.requireUsableAgent(targetId, "message");
1677
1753
  const runtime = this.runtimes.get(targetId) ?? (await this.ensureRuntime(target));
1678
1754
  this.assertAccepting();
1679
- if (!isCurrentDelivery()) {
1755
+ if (!isCurrentDelivery() || !this.isCurrentAgent(target)) {
1680
1756
  throw new Error("Minimal subagents delivery abandoned after session branch change");
1681
1757
  }
1682
1758
  const visibleMessage = addCoordinatorMessageEnvelope(message);
@@ -1688,12 +1764,12 @@ export class MinimalSubagentsCoordinator {
1688
1764
  const runMessage = runtime
1689
1765
  .runMessage(visibleMessage)
1690
1766
  .then((outcome) => {
1691
- if (this.agents.get(target.agent_id) === target && target.active_turn_id === turnId) {
1767
+ if (this.isCurrentAgent(target) && target.active_turn_id === turnId) {
1692
1768
  this.settleTurn(target, turnId, terminalTurnResult(target.agent_id, turnId, outcome));
1693
1769
  }
1694
1770
  })
1695
1771
  .catch((cause) => {
1696
- if (this.agents.get(target.agent_id) === target && target.active_turn_id === turnId) {
1772
+ if (this.isCurrentAgent(target) && target.active_turn_id === turnId) {
1697
1773
  this.settleTurn(target, turnId, {
1698
1774
  agent_id: target.agent_id,
1699
1775
  turn_id: turnId,
@@ -1896,14 +1972,17 @@ export class MinimalSubagentsCoordinator {
1896
1972
  private duplicateAgentIdMessage(agentId: string): string {
1897
1973
  const prefix = `Minimal subagents duplicate agent ID: ${agentId}`;
1898
1974
  const existing = this.agents.get(agentId);
1975
+ if (!existing && this.deletingAgentIds.has(agentId)) {
1976
+ return `${prefix} is still being deleted; retry shortly or choose another agent_id`;
1977
+ }
1899
1978
  if (!existing) {
1900
1979
  return `${prefix} is still being created; retry shortly or choose another agent_id`;
1901
1980
  }
1902
1981
  if (existing.availability === "unavailable") {
1903
- return `${prefix} already exists (unavailable). Use subagent_delete to remove it (a deleted ID cannot be reused), or choose another agent_id.`;
1982
+ return `${prefix} already exists (unavailable). Use subagent_delete to remove it (its ID then becomes free for a new child), or choose another agent_id.`;
1904
1983
  }
1905
1984
  const state = existing.active_turn_id ? "running" : "idle";
1906
- return `${prefix} already exists (${state}). Use agent_message to continue it, subagent_delete to remove it (a deleted ID cannot be reused), or choose another agent_id.`;
1985
+ return `${prefix} already exists (${state}). Use agent_message to continue it, subagent_delete to remove it (its ID then becomes free for a new child), or choose another agent_id.`;
1907
1986
  }
1908
1987
 
1909
1988
  private buildAgentSummary(agent: PersistedAgent, includeDescendants = true): AgentSummary {
@@ -2430,7 +2509,7 @@ export class MinimalSubagentsCoordinator {
2430
2509
  const turnId = agent.active_turn_id;
2431
2510
  const runtime = this.runtimes.get(agent.agent_id);
2432
2511
  if (runtime) await runtime.abort();
2433
- if (this.agents.get(agent.agent_id) !== agent) continue;
2512
+ if (!this.isCurrentAgent(agent)) continue;
2434
2513
  this.settleTurn(agent, turnId, {
2435
2514
  agent_id: agent.agent_id,
2436
2515
  turn_id: turnId,
@@ -2445,7 +2524,7 @@ export class MinimalSubagentsCoordinator {
2445
2524
  const turnId = agent.active_turn_id;
2446
2525
  const runtime = this.runtimes.get(agent.agent_id);
2447
2526
  if (runtime) await runtime.abort();
2448
- if (this.agents.get(agent.agent_id) !== agent) return undefined;
2527
+ if (!this.isCurrentAgent(agent)) return undefined;
2449
2528
  this.settleTurn(agent, turnId, {
2450
2529
  agent_id: agent.agent_id,
2451
2530
  turn_id: turnId,
@@ -532,6 +532,7 @@ export class MinimalSubagentsLifecycleController {
532
532
  status: (...args) => this.requireCoordinator().status(...args),
533
533
  cancel: (...args) => this.requireCoordinator().cancel(...args),
534
534
  delete: (...args) => this.requireCoordinator().delete(...args),
535
+ assertCurrentCaller: (...args) => this.requireCoordinator().assertCurrentCaller(...args),
535
536
  };
536
537
  private uiController: MinimalSubagentsUiController | undefined;
537
538
  private statusPanelController: MinimalSubagentsStatusPanelController | undefined;
@@ -1247,12 +1247,12 @@ export function replayRegistryEntries(
1247
1247
  "invalid-event-fields",
1248
1248
  `agent-created contains post-creation state for ${event.agent.agent_id}`,
1249
1249
  );
1250
- } else if (tombstones.has(event.agent.agent_id) || agents.has(event.agent.agent_id)) {
1250
+ } else if (agents.has(event.agent.agent_id)) {
1251
1251
  reportDiagnostic(
1252
1252
  diagnostics,
1253
1253
  entryIndex,
1254
1254
  "invalid-event-reference",
1255
- `agent-created duplicates or resurrects ${event.agent.agent_id}`,
1255
+ `agent-created duplicates live agent ${event.agent.agent_id}`,
1256
1256
  );
1257
1257
  } else if (event.agent.parent_id !== "root" && !agents.has(event.agent.parent_id)) {
1258
1258
  reportDiagnostic(
@@ -1262,6 +1262,8 @@ export function replayRegistryEntries(
1262
1262
  `agent-created parent is missing for ${event.agent.agent_id}`,
1263
1263
  );
1264
1264
  } else {
1265
+ // Re-creating a deleted Agent ID starts an unrelated Child Agent (ADR 0007).
1266
+ tombstones.delete(event.agent.agent_id);
1265
1267
  agents.set(event.agent.agent_id, structuredClone(event.agent));
1266
1268
  }
1267
1269
  break;
@@ -57,7 +57,7 @@ export function createCoordinatorToolSchemas(modelIds: readonly string[]) {
57
57
  Type.String({
58
58
  pattern: FRIENDLY_AGENT_ID_PATTERN,
59
59
  description:
60
- "Friendly peer-unique ID segment; a root child uses this segment as its canonical ID",
60
+ "Friendly ID segment, unique among live siblings and reusable after deletion; a root child uses this segment as its canonical ID",
61
61
  }),
62
62
  ),
63
63
  session_context: Type.Optional(SessionContextSchema),
@@ -68,6 +68,7 @@ export type CoordinatorToolOperations = Pick<
68
68
  | "status"
69
69
  | "cancel"
70
70
  | "delete"
71
+ | "assertCurrentCaller"
71
72
  >;
72
73
 
73
74
  /** Dependencies and caller policy used to create caller-bound coordinator tools. */
@@ -101,9 +102,15 @@ function buildModelRolePromptGuidelines(
101
102
 
102
103
  async function runCoordinatorToolActivity<T>(
103
104
  options: CoordinatorToolDefinitionOptions,
105
+ context: ExtensionContext,
104
106
  operation: () => Promise<T> | T,
105
107
  ): Promise<T> {
106
108
  try {
109
+ // A deleted Child Agent's leftover tool call must not act as a respawn reusing its Agent ID.
110
+ options.coordinator.assertCurrentCaller(
111
+ options.callerId,
112
+ context.sessionManager.getSessionId(),
113
+ );
107
114
  return await operation();
108
115
  } finally {
109
116
  options.onActivity?.();
@@ -301,7 +308,7 @@ export function createCoordinatorToolDefinitions(
301
308
  openWorldHint: true,
302
309
  },
303
310
  async execute(_toolCallId, parameters, _signal, _onUpdate, context) {
304
- return runCoordinatorToolActivity(options, async () => {
311
+ return runCoordinatorToolActivity(options, context, async () => {
305
312
  const result = await options.coordinator.spawn(
306
313
  options.callerId,
307
314
  parameters,
@@ -334,8 +341,8 @@ export function createCoordinatorToolDefinitions(
334
341
  idempotentHint: false,
335
342
  openWorldHint: false,
336
343
  },
337
- async execute(toolCallId, parameters) {
338
- return runCoordinatorToolActivity(options, async () => {
344
+ async execute(toolCallId, parameters, _signal, _onUpdate, context) {
345
+ return runCoordinatorToolActivity(options, context, async () => {
339
346
  const result = await options.coordinator.sendAgentMessage(
340
347
  options.callerId,
341
348
  {
@@ -393,7 +400,7 @@ export function createCoordinatorToolDefinitions(
393
400
  // Pi hands a Queued Steer over only after this tool call returns, so end the wait for it.
394
401
  const steerWatch = watchQueuedSteer(context.sessionManager.getSessionId());
395
402
  try {
396
- return await runCoordinatorToolActivity(options, async () => {
403
+ return await runCoordinatorToolActivity(options, context, async () => {
397
404
  const result = await options.coordinator.wait(
398
405
  options.callerId,
399
406
  parameters.agent_id,
@@ -434,8 +441,8 @@ export function createCoordinatorToolDefinitions(
434
441
  idempotentHint: true,
435
442
  openWorldHint: false,
436
443
  },
437
- async execute(_toolCallId, parameters) {
438
- return runCoordinatorToolActivity(options, () => {
444
+ async execute(_toolCallId, parameters, _signal, _onUpdate, context) {
445
+ return runCoordinatorToolActivity(options, context, () => {
439
446
  const result = options.coordinator.status(options.callerId, parameters.agent_id);
440
447
  return structuredToolResult(
441
448
  result,
@@ -459,8 +466,8 @@ export function createCoordinatorToolDefinitions(
459
466
  idempotentHint: true,
460
467
  openWorldHint: false,
461
468
  },
462
- async execute(_toolCallId, parameters) {
463
- return runCoordinatorToolActivity(options, async () => {
469
+ async execute(_toolCallId, parameters, _signal, _onUpdate, context) {
470
+ return runCoordinatorToolActivity(options, context, async () => {
464
471
  const result = await options.coordinator.cancel(
465
472
  options.callerId,
466
473
  parameters.agent_id,
@@ -476,8 +483,8 @@ export function createCoordinatorToolDefinitions(
476
483
  name: "subagent_delete",
477
484
  label: "Subagent Delete",
478
485
  description:
479
- "Delete one direct child's persistent session and retain durable ID tombstones. Recursive deletion includes its subtree and defaults to true.",
480
- promptSnippet: "Delete subagent sessions and tombstone their IDs",
486
+ "Delete one direct child's persistent session. Recursive deletion includes its subtree and defaults to true. A deleted ID can be reused; a new subagent with it starts fresh, with none of the deleted child's session or results.",
487
+ promptSnippet: "Delete subagent sessions, freeing their IDs",
481
488
  parameters: options.schemas.subagent_delete,
482
489
  annotations: {
483
490
  readOnlyHint: false,
@@ -485,8 +492,8 @@ export function createCoordinatorToolDefinitions(
485
492
  idempotentHint: true,
486
493
  openWorldHint: false,
487
494
  },
488
- async execute(_toolCallId, parameters) {
489
- return runCoordinatorToolActivity(options, async () => {
495
+ async execute(_toolCallId, parameters, _signal, _onUpdate, context) {
496
+ return runCoordinatorToolActivity(options, context, async () => {
490
497
  const result = await options.coordinator.delete(
491
498
  options.callerId,
492
499
  parameters.agent_id,