@bastani/atomic 0.9.19-alpha.2 → 0.9.19-alpha.4

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 (197) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +15 -0
  3. package/dist/builtin/intercom/broker/broker.ts +8 -0
  4. package/dist/builtin/intercom/broker/client.ts +18 -0
  5. package/dist/builtin/intercom/broker/send-handler.ts +9 -0
  6. package/dist/builtin/intercom/index.bundle.mjs +161 -30
  7. package/dist/builtin/intercom/package.json +1 -1
  8. package/dist/builtin/intercom/types.ts +2 -1
  9. package/dist/builtin/mcp/package.json +1 -1
  10. package/dist/builtin/subagents/CHANGELOG.md +18 -0
  11. package/dist/builtin/subagents/README.md +14 -14
  12. package/dist/builtin/subagents/package.json +1 -1
  13. package/dist/builtin/subagents/skills/subagent/SKILL.md +14 -14
  14. package/dist/builtin/subagents/src/extension/index.bundle.mjs +142 -60
  15. package/dist/builtin/web-access/package.json +1 -1
  16. package/dist/builtin/workflows/CHANGELOG.md +33 -0
  17. package/dist/builtin/workflows/README.md +14 -15
  18. package/dist/builtin/workflows/builtin/{chunk-mygmtz6e.js → chunk-9k3ywfgd.js} +1 -1
  19. package/dist/builtin/workflows/builtin/{chunk-r9qq7xfj.js → chunk-ahbntmx2.js} +1 -1
  20. package/dist/builtin/workflows/builtin/{chunk-zggjyg5z.js → chunk-n8zysf1t.js} +1 -1
  21. package/dist/builtin/workflows/builtin/goal.js +2 -2
  22. package/dist/builtin/workflows/builtin/index.js +3 -3
  23. package/dist/builtin/workflows/builtin/ralph.js +2 -2
  24. package/dist/builtin/workflows/package.json +5 -5
  25. package/dist/builtin/workflows/src/extension/index.bundle.mjs +171 -207
  26. package/dist/builtin/workflows/src/index.js +33 -6
  27. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +2 -1
  28. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +1 -1
  29. package/dist/core/agent-session-auto-compaction.js +1 -1
  30. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  31. package/dist/core/agent-session-compaction.js +1 -1
  32. package/dist/core/agent-session-compaction.js.map +1 -1
  33. package/dist/core/agent-session-custom-message-commit.d.ts.map +1 -1
  34. package/dist/core/agent-session-custom-message-commit.js +33 -2
  35. package/dist/core/agent-session-custom-message-commit.js.map +1 -1
  36. package/dist/core/agent-session-message-queue.d.ts +1 -1
  37. package/dist/core/agent-session-message-queue.d.ts.map +1 -1
  38. package/dist/core/agent-session-message-queue.js +22 -8
  39. package/dist/core/agent-session-message-queue.js.map +1 -1
  40. package/dist/core/agent-session-methods.d.ts +7 -1
  41. package/dist/core/agent-session-methods.d.ts.map +1 -1
  42. package/dist/core/agent-session-methods.js.map +1 -1
  43. package/dist/core/agent-session-persistent-custom-messages.d.ts +1 -1
  44. package/dist/core/agent-session-persistent-custom-messages.d.ts.map +1 -1
  45. package/dist/core/agent-session-persistent-custom-messages.js +13 -3
  46. package/dist/core/agent-session-persistent-custom-messages.js.map +1 -1
  47. package/dist/core/agent-session-post-tool-compaction.js +1 -1
  48. package/dist/core/agent-session-post-tool-compaction.js.map +1 -1
  49. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  50. package/dist/core/agent-session-prompt.js +83 -0
  51. package/dist/core/agent-session-prompt.js.map +1 -1
  52. package/dist/core/agent-session-queue-pause.d.ts.map +1 -1
  53. package/dist/core/agent-session-queue-pause.js +11 -0
  54. package/dist/core/agent-session-queue-pause.js.map +1 -1
  55. package/dist/core/agent-session-tasks.d.ts +5 -0
  56. package/dist/core/agent-session-tasks.d.ts.map +1 -1
  57. package/dist/core/agent-session-tasks.js +8 -1
  58. package/dist/core/agent-session-tasks.js.map +1 -1
  59. package/dist/core/agent-session-tool-hooks.d.ts.map +1 -1
  60. package/dist/core/agent-session-tool-hooks.js +2 -0
  61. package/dist/core/agent-session-tool-hooks.js.map +1 -1
  62. package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
  63. package/dist/core/agent-session-tool-registry.js +12 -0
  64. package/dist/core/agent-session-tool-registry.js.map +1 -1
  65. package/dist/core/agent-session-transfer.d.ts.map +1 -1
  66. package/dist/core/agent-session-transfer.js +7 -0
  67. package/dist/core/agent-session-transfer.js.map +1 -1
  68. package/dist/core/agent-session-types.d.ts +2 -0
  69. package/dist/core/agent-session-types.d.ts.map +1 -1
  70. package/dist/core/agent-session-types.js +2 -0
  71. package/dist/core/agent-session-types.js.map +1 -1
  72. package/dist/core/agent-session.d.ts +4 -0
  73. package/dist/core/agent-session.d.ts.map +1 -1
  74. package/dist/core/agent-session.js +22 -0
  75. package/dist/core/agent-session.js.map +1 -1
  76. package/dist/core/extensions/context-types.d.ts +6 -0
  77. package/dist/core/extensions/context-types.d.ts.map +1 -1
  78. package/dist/core/extensions/context-types.js.map +1 -1
  79. package/dist/core/extensions/loader-api.d.ts.map +1 -1
  80. package/dist/core/extensions/loader-api.js +3 -0
  81. package/dist/core/extensions/loader-api.js.map +1 -1
  82. package/dist/core/extensions/loader-host-modules.d.ts +1 -1
  83. package/dist/core/extensions/loader-host-modules.d.ts.map +1 -1
  84. package/dist/core/extensions/loader-host-modules.js +2 -1
  85. package/dist/core/extensions/loader-host-modules.js.map +1 -1
  86. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  87. package/dist/core/extensions/loader-virtual-modules.js +4 -1
  88. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  89. package/dist/core/extensions/runner-context.d.ts +1 -1
  90. package/dist/core/extensions/runner-context.d.ts.map +1 -1
  91. package/dist/core/extensions/runner-context.js +9 -3
  92. package/dist/core/extensions/runner-context.js.map +1 -1
  93. package/dist/core/extensions/runner.js +1 -1
  94. package/dist/core/extensions/runner.js.map +1 -1
  95. package/dist/core/extensions/workflow-events.d.ts +1 -1
  96. package/dist/core/extensions/workflow-events.d.ts.map +1 -1
  97. package/dist/core/extensions/workflow-events.js.map +1 -1
  98. package/dist/core/retry-policy.d.ts +1 -0
  99. package/dist/core/retry-policy.d.ts.map +1 -1
  100. package/dist/core/retry-policy.js +2 -1
  101. package/dist/core/retry-policy.js.map +1 -1
  102. package/dist/core/sdk-exports.d.ts +1 -1
  103. package/dist/core/sdk-exports.d.ts.map +1 -1
  104. package/dist/core/sdk-exports.js +1 -1
  105. package/dist/core/sdk-exports.js.map +1 -1
  106. package/dist/core/settings-manager-basic-accessors.d.ts +6 -3
  107. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  108. package/dist/core/settings-manager-basic-accessors.js +25 -8
  109. package/dist/core/settings-manager-basic-accessors.js.map +1 -1
  110. package/dist/core/settings-manager.d.ts +1 -1
  111. package/dist/core/settings-manager.d.ts.map +1 -1
  112. package/dist/core/settings-manager.js.map +1 -1
  113. package/dist/core/settings-types.d.ts +6 -0
  114. package/dist/core/settings-types.d.ts.map +1 -1
  115. package/dist/core/settings-types.js.map +1 -1
  116. package/dist/core/slash-commands.d.ts.map +1 -1
  117. package/dist/core/slash-commands.js +4 -5
  118. package/dist/core/slash-commands.js.map +1 -1
  119. package/dist/core/tasks/agent-adapter.d.ts +14 -2
  120. package/dist/core/tasks/agent-adapter.d.ts.map +1 -1
  121. package/dist/core/tasks/agent-adapter.js +45 -4
  122. package/dist/core/tasks/agent-adapter.js.map +1 -1
  123. package/dist/core/tasks/completion.d.ts.map +1 -1
  124. package/dist/core/tasks/completion.js +5 -1
  125. package/dist/core/tasks/completion.js.map +1 -1
  126. package/dist/core/tasks/pause.d.ts +4 -0
  127. package/dist/core/tasks/pause.d.ts.map +1 -0
  128. package/dist/core/tasks/pause.js +49 -0
  129. package/dist/core/tasks/pause.js.map +1 -0
  130. package/dist/core/tasks/supervisor.d.ts +3 -1
  131. package/dist/core/tasks/supervisor.d.ts.map +1 -1
  132. package/dist/core/tasks/supervisor.js +48 -22
  133. package/dist/core/tasks/supervisor.js.map +1 -1
  134. package/dist/core/tools/bash-pty-native.d.ts +16 -2
  135. package/dist/core/tools/bash-pty-native.d.ts.map +1 -1
  136. package/dist/core/tools/bash-pty-native.js +82 -0
  137. package/dist/core/tools/bash-pty-native.js.map +1 -1
  138. package/dist/core/tools/bash.d.ts +15 -2
  139. package/dist/core/tools/bash.d.ts.map +1 -1
  140. package/dist/core/tools/bash.js +49 -4
  141. package/dist/core/tools/bash.js.map +1 -1
  142. package/dist/core/tools/index.d.ts +4 -1
  143. package/dist/core/tools/index.d.ts.map +1 -1
  144. package/dist/core/tools/index.js +12 -0
  145. package/dist/core/tools/index.js.map +1 -1
  146. package/dist/core/tools/kill.d.ts +17 -0
  147. package/dist/core/tools/kill.d.ts.map +1 -0
  148. package/dist/core/tools/kill.js +49 -0
  149. package/dist/core/tools/kill.js.map +1 -0
  150. package/dist/core/tools/powershell.d.ts +84 -28
  151. package/dist/core/tools/powershell.d.ts.map +1 -1
  152. package/dist/core/tools/powershell.js +11 -3
  153. package/dist/core/tools/powershell.js.map +1 -1
  154. package/dist/core/tools/resource-selectors.d.ts +1 -1
  155. package/dist/core/tools/resource-selectors.d.ts.map +1 -1
  156. package/dist/core/tools/resource-selectors.js +7 -4
  157. package/dist/core/tools/resource-selectors.js.map +1 -1
  158. package/dist/core/workflow-stage-admission.d.ts +14 -0
  159. package/dist/core/workflow-stage-admission.d.ts.map +1 -1
  160. package/dist/core/workflow-stage-admission.js +63 -3
  161. package/dist/core/workflow-stage-admission.js.map +1 -1
  162. package/dist/index.d.ts +2 -2
  163. package/dist/index.d.ts.map +1 -1
  164. package/dist/index.js +1 -1
  165. package/dist/index.js.map +1 -1
  166. package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
  167. package/dist/modes/interactive/components/chat-session-host.js +3 -2
  168. package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
  169. package/dist/modes/interactive/components/task-row.d.ts.map +1 -1
  170. package/dist/modes/interactive/components/task-row.js +8 -1
  171. package/dist/modes/interactive/components/task-row.js.map +1 -1
  172. package/docs/background-tasks.md +31 -2
  173. package/docs/compaction.md +20 -0
  174. package/docs/computer-use.md +390 -0
  175. package/docs/development.md +23 -74
  176. package/docs/docs.json +1 -0
  177. package/docs/extensions.md +15 -1
  178. package/docs/herdr.md +26 -77
  179. package/docs/index.md +1 -0
  180. package/docs/intercom.md +14 -2
  181. package/docs/models/evals.md +2 -0
  182. package/docs/models/model-selection.md +10 -7
  183. package/docs/quickstart.md +1 -1
  184. package/docs/sdk.md +11 -1
  185. package/docs/settings.md +24 -0
  186. package/docs/subagents.md +10 -6
  187. package/docs/tmux.md +2 -0
  188. package/docs/tools.md +11 -1
  189. package/docs/usage.md +1 -1
  190. package/docs/workflows/api-reference.md +5 -4
  191. package/docs/workflows/authoring.md +3 -3
  192. package/docs/workflows/operations.md +44 -33
  193. package/docs/workflows/reliable-design.md +3 -3
  194. package/docs/workflows/verification.md +99 -40
  195. package/docs/workflows.md +4 -2
  196. package/npm-shrinkwrap.json +59 -59
  197. package/package.json +3 -3
@@ -523,6 +523,7 @@ var init_client = __esm(() => {
523
523
  _supervisorSessionId = null;
524
524
  _messageSource;
525
525
  pendingSends = new PendingSendRegistry;
526
+ pendingReplyTargets = new Map;
526
527
  pendingGroupLists = new Map;
527
528
  pendingLists = new Map;
528
529
  pendingPresence = new Map;
@@ -730,6 +731,16 @@ var init_client = __esm(() => {
730
731
  this.emit("_registration_failed", new Error(brokerMessage.reason));
731
732
  break;
732
733
  }
734
+ case "question_target": {
735
+ const { messageId, attemptId, sessionId } = brokerMessage;
736
+ if (typeof messageId !== "string" || typeof attemptId !== "string" || typeof sessionId !== "string") {
737
+ throw new Error("Invalid question_target message");
738
+ }
739
+ const pending = this.pendingReplyTargets.get(attemptId);
740
+ if (pending?.messageId === messageId)
741
+ pending.bind(sessionId);
742
+ break;
743
+ }
733
744
  case "sessions": {
734
745
  const { requestId, sessions, workflowStages, workflowFutureStages } = brokerMessage;
735
746
  if (typeof requestId !== "string" || !Array.isArray(sessions) || !sessions.every(isSessionInfo) || workflowStages !== undefined && !isWorkflowStageRosterEntries(workflowStages) || workflowFutureStages !== undefined && !isWorkflowFutureStageRosterEntries(workflowFutureStages)) {
@@ -1246,12 +1257,16 @@ var init_client = __esm(() => {
1246
1257
  source: this._messageSource,
1247
1258
  content: { text: options.text, attachments: options.attachments }
1248
1259
  };
1260
+ if (options.onReplyTarget !== undefined) {
1261
+ this.pendingReplyTargets.set(acquired.attempt.attemptId, { messageId, bind: options.onReplyTarget });
1262
+ }
1249
1263
  try {
1250
1264
  writeMessage(socket, {
1251
1265
  type,
1252
1266
  to,
1253
1267
  ...options.logicalTarget === undefined ? {} : { logicalTarget: options.logicalTarget },
1254
1268
  ...options.requirePendingReply === undefined ? {} : { requirePendingReply: options.requirePendingReply },
1269
+ ...options.onReplyTarget === undefined ? {} : { resolveReplyTarget: true },
1255
1270
  message,
1256
1271
  attemptId: acquired.attempt.attemptId,
1257
1272
  ...extra
@@ -1259,6 +1274,10 @@ var init_client = __esm(() => {
1259
1274
  } catch (error) {
1260
1275
  this.pendingSends.reject(acquired.attempt, toError(error));
1261
1276
  }
1277
+ const clearReplyTarget = () => {
1278
+ this.pendingReplyTargets.delete(acquired.attempt.attemptId);
1279
+ };
1280
+ acquired.attempt.promise.then(clearReplyTarget, clearReplyTarget);
1262
1281
  return acquired.attempt.promise;
1263
1282
  }
1264
1283
  updatePresence(updates) {
@@ -1799,6 +1818,10 @@ class ReplyWaiterRegistry {
1799
1818
  ok: true,
1800
1819
  wait: {
1801
1820
  promise,
1821
+ bindSender: (sender) => {
1822
+ if (this.waiters.get(replyTo) === record)
1823
+ record.from = sender;
1824
+ },
1802
1825
  cancel: (error) => record.reject(error)
1803
1826
  }
1804
1827
  };
@@ -1966,6 +1989,11 @@ class ReplyTracker {
1966
1989
  endTurn() {
1967
1990
  this.currentTurnContext = null;
1968
1991
  }
1992
+ restoreTurnContext() {
1993
+ if (this.currentTurnContext)
1994
+ this.pendingTurnContexts.unshift(this.currentTurnContext);
1995
+ this.currentTurnContext = null;
1996
+ }
1969
1997
  reset() {
1970
1998
  this.pendingAsks.clear();
1971
1999
  this.pendingTurnContexts.length = 0;
@@ -2414,7 +2442,8 @@ function formatSessionListRow(session, currentCwd, isSelf) {
2414
2442
  const name = session.name || "Unnamed session";
2415
2443
  const normalizedGroup = normalizeGroup(session.group);
2416
2444
  const groupTag = normalizedGroup !== DEFAULT_GROUP ? `group: ${normalizedGroup}` : undefined;
2417
- const tags = [isSelf ? "self" : session.cwd === currentCwd ? "same cwd" : undefined, session.status, groupTag].filter((tag) => Boolean(tag));
2445
+ const capabilityTag = session.replyCapability === undefined ? undefined : `replyCapability: ${session.replyCapability}`;
2446
+ const tags = [isSelf ? "self" : session.cwd === currentCwd ? "same cwd" : undefined, session.status, capabilityTag, groupTag].filter((tag) => Boolean(tag));
2418
2447
  const suffix = tags.length ? ` [${tags.join(", ")}]` : "";
2419
2448
  return `• ${name} (${session.id}) — ${session.cwd} (${session.model})${suffix}`;
2420
2449
  }
@@ -3755,7 +3784,8 @@ ${message}${attachmentText}`);
3755
3784
  text: message ?? "",
3756
3785
  attachments,
3757
3786
  replyTo,
3758
- expectsReply: true
3787
+ expectsReply: true,
3788
+ ...parseWorkflowStageTarget(to)?.kind === "path" ? { onReplyTarget: wait.bindSender } : {}
3759
3789
  });
3760
3790
  if (sendResult.queued === true) {
3761
3791
  retryIdentities.release(retryIdentity);
@@ -4487,7 +4517,10 @@ var init_overlay = __esm(() => {
4487
4517
  function registerIntercomLifecycle(pi, deps) {
4488
4518
  const activeReplyTracker = () => typeof deps.replyTracker === "function" ? deps.replyTracker() : deps.replyTracker;
4489
4519
  let hasActiveSession = false;
4520
+ let unbindStageClose;
4490
4521
  async function cleanupRuntime(reason) {
4522
+ unbindStageClose?.();
4523
+ unbindStageClose = undefined;
4491
4524
  deps.setRuntimeStarted(false);
4492
4525
  deps.setShuttingDown(true);
4493
4526
  deps.setDisposed(true);
@@ -4538,6 +4571,12 @@ function registerIntercomLifecycle(pi, deps) {
4538
4571
  deps.setSessionStartedAt(Date.now());
4539
4572
  deps.setAgentRunning(false);
4540
4573
  deps.activeTools.clear();
4574
+ const stage = ctx.orchestrationContext;
4575
+ const closeSignal = stage?.kind === "workflow-stage" ? stage.messageAdmission?.boundary?.closeSignal : undefined;
4576
+ closeSignal?.addEventListener("abort", deps.syncPresenceStatus, { once: true });
4577
+ unbindStageClose = () => closeSignal?.removeEventListener("abort", deps.syncPresenceStatus);
4578
+ if (closeSignal?.aborted)
4579
+ deps.syncPresenceStatus();
4541
4580
  });
4542
4581
  pi.on("session_shutdown", async () => {
4543
4582
  if (!hasActiveSession)
@@ -4545,10 +4584,15 @@ function registerIntercomLifecycle(pi, deps) {
4545
4584
  hasActiveSession = false;
4546
4585
  await cleanupRuntime("Session shutting down");
4547
4586
  });
4548
- pi.on("turn_end", () => {
4587
+ pi.on("turn_end", (event) => {
4549
4588
  if (!deps.getLiveContext())
4550
4589
  return;
4551
- activeReplyTracker().endTurn();
4590
+ const message = event.message;
4591
+ const cancelledBeforeReply = message?.role === "assistant" && message.stopReason === "aborted" && message.content?.length === 0;
4592
+ if (cancelledBeforeReply)
4593
+ activeReplyTracker().restoreTurnContext();
4594
+ else
4595
+ activeReplyTracker().endTurn();
4552
4596
  deps.scheduleInboundFlush();
4553
4597
  });
4554
4598
  pi.on("agent_start", () => {
@@ -5640,7 +5684,7 @@ function createIncomingMessageSender(input) {
5640
5684
  if (!input.canDeliver(generation)) {
5641
5685
  return Promise.reject(new Error("Intercom session retired before inbound delivery"));
5642
5686
  }
5643
- if (delivery === "trigger" && trackReplyContext) {
5687
+ if ((delivery === "trigger" || delivery === "interrupt") && trackReplyContext) {
5644
5688
  input.queueTurnContext(turnContext ?? { from: entry.from, message: entry.message, receivedAt: Date.now() });
5645
5689
  }
5646
5690
  const baseOptions = {
@@ -5648,7 +5692,7 @@ function createIncomingMessageSender(input) {
5648
5692
  persistWhenStreaming: true,
5649
5693
  ...stageAdmissionBarrier ? { stageAdmissionBarrier } : {}
5650
5694
  };
5651
- const options = delivery === "trigger" ? { ...baseOptions, triggerTurn: true } : delivery === "followUp" ? { ...baseOptions, deliverAs: "followUp" } : baseOptions;
5695
+ const options = delivery === "interrupt" ? { ...baseOptions, triggerTurn: true, deliverAs: "interrupt" } : delivery === "trigger" ? { ...baseOptions, triggerTurn: true } : delivery === "followUp" ? { ...baseOptions, deliverAs: "followUp" } : baseOptions;
5652
5696
  return Promise.resolve(input.pi.sendMessage(buildIncomingCustomMessage(entry), options));
5653
5697
  };
5654
5698
  }
@@ -6000,13 +6044,15 @@ function retryStableDelivery(options) {
6000
6044
  function admitWorkflowStageInbound(ctx, deliver, firstRefusal, onAdmissionFailure) {
6001
6045
  if (ctx.orchestrationContext?.kind !== "workflow-stage")
6002
6046
  return false;
6047
+ const boundary = ctx.orchestrationContext.messageAdmission?.boundary;
6048
+ const run = (barrier) => boundary?.runMessageDelivery(() => deliver(barrier), () => deliver(), true) ?? Promise.resolve(deliver(barrier));
6003
6049
  try {
6004
6050
  let busy = false;
6005
6051
  try {
6006
6052
  busy = ctx.isIdle?.() === false;
6007
6053
  } catch {}
6008
6054
  if (!busy || !firstRefusal)
6009
- return Promise.resolve(deliver());
6055
+ return run();
6010
6056
  let firstRefusalPromise;
6011
6057
  const admissionBarrier = () => {
6012
6058
  firstRefusalPromise ??= (async () => {
@@ -6019,12 +6065,29 @@ function admitWorkflowStageInbound(ctx, deliver, firstRefusal, onAdmissionFailur
6019
6065
  })();
6020
6066
  return firstRefusalPromise;
6021
6067
  };
6022
- return Promise.resolve(deliver(admissionBarrier));
6068
+ return run(admissionBarrier);
6023
6069
  } catch (error) {
6024
6070
  return Promise.reject(error);
6025
6071
  }
6026
6072
  }
6027
6073
 
6074
+ // dist/builtin/intercom/active-session-admission.ts
6075
+ function admitActiveSessionInbound(ctx, deliver, firstRefusal, onAdmissionFailure) {
6076
+ const executionEnded = ctx.subagentPolicy?.executionEnded;
6077
+ if (executionEnded !== undefined) {
6078
+ if (executionEnded.aborted || ctx.subagentPolicy?.messageAdmission?.isOpen() === false) {
6079
+ return Promise.reject(new Error("Subagent execution is terminal and cannot accept messages"));
6080
+ }
6081
+ try {
6082
+ return ctx.subagentPolicy?.messageAdmission?.run(deliver) ?? Promise.resolve(deliver());
6083
+ } catch (error) {
6084
+ return Promise.reject(error);
6085
+ }
6086
+ }
6087
+ return admitWorkflowStageInbound(ctx, deliver, firstRefusal, onAdmissionFailure);
6088
+ }
6089
+ var init_active_session_admission = () => {};
6090
+
6028
6091
  // dist/builtin/intercom/workflow-reply-tracker.ts
6029
6092
  function bindWorkflowReplyTracker(ctx, current) {
6030
6093
  const state = ctx.orchestrationContext?.messageAdmission?.extensionState;
@@ -6078,7 +6141,7 @@ async function settleOpenStageDeliveryFailure(input, error) {
6078
6141
  input.admission.release(input.reservation, failure);
6079
6142
  return;
6080
6143
  }
6081
- const delivered = await sendWorkflowStageDeliveryFailure(input.entry, failure, input.tracker, input.currentClient, () => input.currentClient()?.isConnected() === true, "Running workflow stage could not admit intercom ask");
6144
+ const delivered = await sendWorkflowStageDeliveryFailure(input.entry, failure, input.tracker, input.currentClient, () => input.currentClient()?.isConnected() === true, input.failurePrefix ?? "Running workflow stage could not admit intercom ask");
6082
6145
  if (delivered)
6083
6146
  input.commit();
6084
6147
  else
@@ -6263,7 +6326,8 @@ function piIntercomExtension(pi, testOverrides = {}) {
6263
6326
  }
6264
6327
  function currentStatus() {
6265
6328
  const activeToolName = activeTools.values().next().value;
6266
- const lifecycleStatus = activeToolName ? `tool:${activeToolName}` : agentRunning ? "thinking" : "idle";
6329
+ const stage = getLiveContext()?.orchestrationContext;
6330
+ const lifecycleStatus = stage?.kind === "workflow-stage" && stage.messageAdmission?.isOpen() === false ? `closed · reply: ${stage.lateMessageRouter === undefined ? "unavailable" : "post-mortem only"}` : activeToolName ? `tool:${activeToolName}` : agentRunning ? "thinking" : "idle";
6267
6331
  return config.status ? `${lifecycleStatus} · ${config.status}` : lifecycleStatus;
6268
6332
  }
6269
6333
  function resolveSessionHomeGroup() {
@@ -6411,7 +6475,12 @@ function piIntercomExtension(pi, testOverrides = {}) {
6411
6475
  const stageAdmission = liveContext.orchestrationContext?.kind === "workflow-stage" ? liveContext.orchestrationContext.messageAdmission : undefined;
6412
6476
  const stageClosed = stageAdmission?.isOpen() === false;
6413
6477
  if (stageClosed) {
6414
- routeClosedWorkflowStageMessage(entry, inboundDeliveries, replyTracker, replyWaiters.pending(), () => sendIncomingMessage(entry, isDeliveryFeedback(message) ? "prelude" : "trigger", messageGeneration, false), () => client, () => Boolean(getLiveContext(liveContext, messageGeneration)), (runId) => stageAdmission.boundary.ownsSubagentRun(runId));
6478
+ routeClosedWorkflowStageMessage(entry, inboundDeliveries, replyTracker, replyWaiters.pending(), () => {
6479
+ if (message.expectsReply === true && liveContext.orchestrationContext?.lateMessageRouter === undefined) {
6480
+ throw new Error("Workflow stage is closed and cannot reply because post-mortem routing is unavailable. Contact a live stage or start new work with explicit context.");
6481
+ }
6482
+ return sendIncomingMessage(entry, isDeliveryFeedback(message) ? "prelude" : "trigger", messageGeneration, false);
6483
+ }, () => client, () => Boolean(getLiveContext(liveContext, messageGeneration)), (runId) => stageAdmission.boundary.ownsSubagentRun(runId));
6415
6484
  return;
6416
6485
  }
6417
6486
  const admission = inboundDeliveries.admit(from, message);
@@ -6439,17 +6508,18 @@ function piIntercomExtension(pi, testOverrides = {}) {
6439
6508
  tracker: replyTracker,
6440
6509
  replyContext,
6441
6510
  currentClient: () => client,
6442
- commit
6511
+ commit,
6512
+ failurePrefix: liveContext.subagentPolicy?.executionEnded === undefined ? undefined : "Subagent could not admit intercom ask"
6443
6513
  });
6444
- const stageDelivery = admitWorkflowStageInbound(liveContext, (admissionBarrier) => {
6514
+ const activeDelivery = admitActiveSessionInbound(liveContext, (admissionBarrier) => {
6445
6515
  replyTracker.queueTurnContext(replyContext);
6446
6516
  return retryStableDelivery({
6447
- deliver: () => sendIncomingMessage(entry, "trigger", messageGeneration, false, undefined, admissionBarrier),
6448
- isCurrent: () => Boolean(getLiveContext(liveContext, messageGeneration))
6517
+ deliver: () => sendIncomingMessage(entry, "interrupt", messageGeneration, false, undefined, admissionBarrier),
6518
+ isCurrent: () => Boolean(getLiveContext(liveContext, messageGeneration)) && liveContext.subagentPolicy?.executionEnded?.aborted !== true && liveContext.subagentPolicy?.messageAdmission?.isOpen() !== false
6449
6519
  });
6450
6520
  }, () => foregroundDetachHandoff.claim(from, message, messageGeneration, () => Boolean(getLiveContext(liveContext, messageGeneration))), release);
6451
- if (stageDelivery !== false) {
6452
- stageDelivery.then(commit, release);
6521
+ if (activeDelivery !== false) {
6522
+ activeDelivery.then(commit, release);
6453
6523
  return;
6454
6524
  }
6455
6525
  return (async () => {
@@ -6459,7 +6529,7 @@ function piIntercomExtension(pi, testOverrides = {}) {
6459
6529
  release(new Error("Intercom session retired before inbound delivery"));
6460
6530
  return;
6461
6531
  }
6462
- if (!activeContext.isIdle()) {
6532
+ if (!activeContext.isIdle() && !activeContext.getAgentTaskHost?.().hasActiveTaskWaits) {
6463
6533
  if (!activeContext.hasUI) {
6464
6534
  const activeClient = client;
6465
6535
  if (!message.replyTo && activeClient?.isConnected()) {
@@ -6502,6 +6572,13 @@ function piIntercomExtension(pi, testOverrides = {}) {
6502
6572
  });
6503
6573
  return;
6504
6574
  }
6575
+ if (!activeContext.isIdle()) {
6576
+ const disposition = await foregroundDetachHandoff.claim(from, message, messageGeneration, () => Boolean(getLiveContext(liveContext, messageGeneration)));
6577
+ if (disposition === "abandoned") {
6578
+ release(new Error("Intercom session retired during foreground-owner admission"));
6579
+ return;
6580
+ }
6581
+ }
6505
6582
  replyTracker.queueTurnContext(replyContext);
6506
6583
  await retryStableDelivery({ deliver: () => sendIncomingMessage(entry, "trigger", messageGeneration, false), isCurrent: () => Boolean(getLiveContext(liveContext, messageGeneration)) });
6507
6584
  commit();
@@ -7078,6 +7155,7 @@ var init_index_heavy = __esm(() => {
7078
7155
  init_terminal_ordering_barrier();
7079
7156
  init_inbound_message_admission();
7080
7157
  init_late_stage_message_router();
7158
+ init_active_session_admission();
7081
7159
  init_workflow_reply_tracker();
7082
7160
  init_closed_workflow_stage_message();
7083
7161
  init_group();
@@ -7095,11 +7173,20 @@ var init_index_heavy = __esm(() => {
7095
7173
  });
7096
7174
 
7097
7175
  // dist/builtin/intercom/index.ts
7098
- init_result_renderers();
7099
7176
  import { APP_NAME as APP_NAME2, getEnvValue } from "@bastani/atomic";
7100
7177
  import { Text as Text4 } from "@earendil-works/pi-tui";
7101
7178
  import { Type as Type3 } from "typebox";
7102
7179
 
7180
+ // dist/builtin/intercom/context-owner.ts
7181
+ var CONTEXT_OWNERS_KEY = Symbol.for("atomic-coding-agent/extension-context-owners@1");
7182
+ function getExtensionContextOwner(context) {
7183
+ const owners = globalThis[CONTEXT_OWNERS_KEY];
7184
+ return owners?.get(context) ?? context;
7185
+ }
7186
+
7187
+ // dist/builtin/intercom/index.ts
7188
+ init_result_renderers();
7189
+
7103
7190
  // dist/builtin/intercom/lazy-tool-execution.ts
7104
7191
  init_recoverable_disconnect();
7105
7192
  init_retry_policy();
@@ -7361,10 +7448,37 @@ function renderHeavyToolResult(loadedHeavy, name, args) {
7361
7448
  return renderer(...args);
7362
7449
  return renderIntercomToolResult(name, args);
7363
7450
  }
7364
- function reportRelayFailure(eventName, error) {
7451
+ function diagnosticDetail(error) {
7452
+ try {
7453
+ return String(error instanceof Error ? error.message : error);
7454
+ } catch {
7455
+ return "Unprintable error";
7456
+ }
7457
+ }
7458
+ function captureDiagnosticRoute(ctx) {
7459
+ try {
7460
+ return ctx?.hasUI && ctx.mode === "tui" ? ctx : "console";
7461
+ } catch {
7462
+ return "silent";
7463
+ }
7464
+ }
7465
+ function reportDiagnostic(route, message, error, level, consoleMessage = message) {
7466
+ if (route === "console") {
7467
+ console.error(consoleMessage, error);
7468
+ return;
7469
+ }
7470
+ if (route === "silent")
7471
+ return;
7472
+ try {
7473
+ route.ui.notify(message, level);
7474
+ } catch {}
7475
+ }
7476
+ function reportRelayFailure(route, eventName, error) {
7365
7477
  if (isRecoverableIntercomDisconnect(error))
7366
7478
  return;
7367
- console.error(`Intercom event relay failed (${eventName}):`, error);
7479
+ const prefix = `Intercom event relay failed (${eventName}):`;
7480
+ const detail = diagnosticDetail(error);
7481
+ reportDiagnostic(route, `${prefix} ${detail}`, error, "error", prefix);
7368
7482
  }
7369
7483
  var WARM_UP_RETRY_ATTEMPTS = 5;
7370
7484
  function intercom(pi, options = {}) {
@@ -7372,6 +7486,7 @@ function intercom(pi, options = {}) {
7372
7486
  let heavyAttempt = null;
7373
7487
  let loadedHeavy = null;
7374
7488
  let sessionSnapshot = null;
7489
+ let shutdownDiagnosticRoute;
7375
7490
  let lifecycleGeneration = 0;
7376
7491
  let nextLeaseId = 1;
7377
7492
  let activeLease = createLifecycleLease(nextLeaseId++);
@@ -7455,6 +7570,8 @@ function intercom(pi, options = {}) {
7455
7570
  await promise;
7456
7571
  }
7457
7572
  async function loadHeavy(ctx) {
7573
+ let diagnosticRoute = captureDiagnosticRoute(ctx);
7574
+ let diagnosticOwner = ctx && getExtensionContextOwner(ctx);
7458
7575
  const lease = activeLease;
7459
7576
  if (lease.retired)
7460
7577
  throw new Error("Intercom initialization unavailable: no active session");
@@ -7471,6 +7588,7 @@ function intercom(pi, options = {}) {
7471
7588
  return handle;
7472
7589
  }
7473
7590
  let promise;
7591
+ let replayCtx = null;
7474
7592
  promise = (async () => {
7475
7593
  const captured = {
7476
7594
  tools: new Map,
@@ -7479,7 +7597,6 @@ function intercom(pi, options = {}) {
7479
7597
  shortcuts: new Map,
7480
7598
  eventHandlers: new Map
7481
7599
  };
7482
- let replayCtx = null;
7483
7600
  let cleaned = false;
7484
7601
  const cleanupCandidate = async () => {
7485
7602
  const shutdown = lease.shutdown;
@@ -7491,7 +7608,9 @@ function intercom(pi, options = {}) {
7491
7608
  try {
7492
7609
  await dispatchHandlers(captured, "session_shutdown", event, cleanupCtx);
7493
7610
  } catch (cleanupError) {
7494
- console.error("Intercom failed to clean rejected lazy candidate:", cleanupError);
7611
+ const prefix = "Intercom failed to clean rejected lazy candidate:";
7612
+ const detail = diagnosticDetail(cleanupError);
7613
+ reportDiagnostic(shutdown?.diagnosticRoute ?? diagnosticRoute, `${prefix} ${detail}`, cleanupError, "error", prefix);
7495
7614
  }
7496
7615
  };
7497
7616
  try {
@@ -7503,6 +7622,11 @@ function intercom(pi, options = {}) {
7503
7622
  sessionSnapshot = { event: createSyntheticSessionStartEvent(), ctx, generation: ++lifecycleGeneration, lease };
7504
7623
  }
7505
7624
  await ensureSessionStartReplayed(captured, lease, (replayContext) => {
7625
+ const owner = getExtensionContextOwner(replayContext);
7626
+ if (owner !== diagnosticOwner) {
7627
+ diagnosticRoute = captureDiagnosticRoute(replayContext);
7628
+ diagnosticOwner = owner;
7629
+ }
7506
7630
  replayCtx = replayContext;
7507
7631
  });
7508
7632
  assertLease(lease);
@@ -7521,8 +7645,8 @@ function intercom(pi, options = {}) {
7521
7645
  if (heavyAttempt?.promise === promise)
7522
7646
  heavyAttempt = null;
7523
7647
  if (!isRecoverableIntercomDisconnect(error)) {
7524
- const message = error instanceof Error ? error.message : String(error);
7525
- console.error(`Intercom heavy initialization failed; a later call will retry: ${message}`, error);
7648
+ const message = diagnosticDetail(error);
7649
+ reportDiagnostic(diagnosticRoute, `Intercom heavy initialization failed; a later call will retry: ${message}`, error, "warning");
7526
7650
  }
7527
7651
  });
7528
7652
  return promise;
@@ -7633,6 +7757,7 @@ function intercom(pi, options = {}) {
7633
7757
  }
7634
7758
  const generation = ++lifecycleGeneration;
7635
7759
  sessionSnapshot = { event, ctx, generation, lease };
7760
+ shutdownDiagnosticRoute = undefined;
7636
7761
  cancelWarmUpRetry();
7637
7762
  if (ctx.orchestrationContext?.kind === "workflow-stage" && ctx.orchestrationContext.pendingStageDelivery !== undefined) {
7638
7763
  const pendingStageDelivery = ctx.orchestrationContext.pendingStageDelivery;
@@ -7650,7 +7775,9 @@ function intercom(pi, options = {}) {
7650
7775
  pi.on("session_shutdown", async (event, ctx) => {
7651
7776
  const lease = activeLease;
7652
7777
  const generation = ++lifecycleGeneration;
7653
- retireLifecycleLease(lease, { event, ctx, generation });
7778
+ const diagnosticRoute = captureDiagnosticRoute(ctx);
7779
+ retireLifecycleLease(lease, { event, ctx, generation, diagnosticRoute });
7780
+ shutdownDiagnosticRoute = diagnosticRoute;
7654
7781
  cancelWarmUpRetry();
7655
7782
  const retiredHeavy = loadedHeavy?.heavy ?? null;
7656
7783
  const retiredAttempt = heavyAttempt?.lease === lease ? heavyAttempt.promise : null;
@@ -7771,13 +7898,15 @@ function intercom(pi, options = {}) {
7771
7898
  return;
7772
7899
  payload.handled = true;
7773
7900
  const forwarded = { ...payload, handled: false, completion: undefined };
7774
- payload.completion = loadHeavy(latestLifecycleContext()).then(async (handle) => {
7901
+ const ctx = latestLifecycleContext();
7902
+ const diagnosticRoute = ctx ? captureDiagnosticRoute(ctx) : shutdownDiagnosticRoute ?? "console";
7903
+ payload.completion = loadHeavy(ctx).then(async (handle) => {
7775
7904
  handle.assertCurrent();
7776
7905
  await dispatchEventHandlers(handle.heavy, PENDING_STAGE_UNDELIVERABLE_EVENT2, forwarded);
7777
7906
  handle.assertCurrent();
7778
7907
  return forwarded.handled === true && forwarded.completion !== undefined ? await forwarded.completion : false;
7779
7908
  }).catch((error) => {
7780
- reportRelayFailure(PENDING_STAGE_UNDELIVERABLE_EVENT2, error);
7909
+ reportRelayFailure(diagnosticRoute, PENDING_STAGE_UNDELIVERABLE_EVENT2, error);
7781
7910
  return false;
7782
7911
  });
7783
7912
  });
@@ -7787,7 +7916,9 @@ function intercom(pi, options = {}) {
7787
7916
  PENDING_STAGE_ROUTE_EVENT2
7788
7917
  ]) {
7789
7918
  pi.events.on(eventName, (payload) => {
7790
- const completion = loadHeavy(latestLifecycleContext()).then(async (handle) => {
7919
+ const ctx = latestLifecycleContext();
7920
+ const diagnosticRoute = ctx ? captureDiagnosticRoute(ctx) : shutdownDiagnosticRoute ?? "console";
7921
+ const completion = loadHeavy(ctx).then(async (handle) => {
7791
7922
  handle.assertCurrent();
7792
7923
  await dispatchEventHandlers(handle.heavy, eventName, payload);
7793
7924
  handle.assertCurrent();
@@ -7802,7 +7933,7 @@ function intercom(pi, options = {}) {
7802
7933
  }
7803
7934
  completion.catch((error) => {
7804
7935
  rejectLazyResultRelay(pi, eventName, payload, error);
7805
- reportRelayFailure(eventName, error);
7936
+ reportRelayFailure(diagnosticRoute, eventName, error);
7806
7937
  });
7807
7938
  });
7808
7939
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/intercom",
3
- "version": "0.9.19-alpha.2",
3
+ "version": "0.9.19-alpha.4",
4
4
  "private": true,
5
5
  "description": "Atomic extension providing a private coordination channel between parent and child agent sessions. Fork of: https://github.com/nicobailon/pi-intercom",
6
6
  "contributors": [
@@ -124,7 +124,7 @@ export type ClientMessage =
124
124
  | { type: "join_group"; requestId: string; group: string }
125
125
  | { type: "leave_group"; requestId: string; group?: string }
126
126
  | { type: "authorize_supervisor"; requestId: string; childName: string; capability?: string }
127
- | { type: "send" | "supervisor_send"; to: string; logicalTarget?: string; requirePendingReply?: true; message: Message; attemptId?: string }
127
+ | { type: "send" | "supervisor_send"; to: string; logicalTarget?: string; requirePendingReply?: true; resolveReplyTarget?: true; message: Message; attemptId?: string }
128
128
  | {
129
129
  type: "send_pending_stage_notification";
130
130
  runId: string;
@@ -168,6 +168,7 @@ export type ClientMessage =
168
168
  export type BrokerMessage =
169
169
  | { type: "registered"; sessionId: string; supervisorSessionId?: string }
170
170
  | { type: "registration_failed"; reason: string }
171
+ | { type: "question_target"; messageId: string; attemptId: string; sessionId: string }
171
172
  | { type: "sessions"; requestId: string; sessions: SessionInfo[]; workflowStages?: WorkflowStageRosterEntry[]; workflowFutureStages?: WorkflowFutureStageRosterEntry[] }
172
173
  | { type: "groups"; requestId: string; groups: GroupSummary[] }
173
174
  | { type: "membership_ack"; requestId: string; groups: string[] }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/mcp",
3
- "version": "0.9.19-alpha.2",
3
+ "version": "0.9.19-alpha.4",
4
4
  "private": true,
5
5
  "description": "Atomic extension that adapts MCP (Model Context Protocol) servers into the coding agent. Fork of: https://github.com/nicobailon/pi-mcp-adapter",
6
6
  "contributors": [
@@ -2,6 +2,24 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.9.19-alpha.4] - 2026-09-10
6
+
7
+ ### Breaking Changes
8
+
9
+ - Renamed the terminal subagent control action from `interrupt` to `kill`. Replace `subagent({ action: "interrupt", id })` with `subagent({ action: "kill", id })`, including calls using `runId`. The old action is rejected, not aliased. Killed children cannot be resumed; launch a fresh child with explicit context for follow-up work. Command results and status report killed, while parent cancellation and lower-level host/native interruption retain their existing semantics.
10
+
11
+ ### Fixed
12
+
13
+ - Preserved an explicit kill when parent cancellation arrives during execution-capacity waiting, without allowing a late kill to replace an earlier parent cancellation.
14
+ - Kept grouped Intercom cancellation status consistent when a killed child has parent-cancelled siblings, while retaining each child's outcome.
15
+ - Intercom `send` and `ask` interrupt a working foreground or background child immediately: its current model call or cancellable tool is cancelled and the message is handled in the same child execution without relaunch. Messages admitted during startup, consumed input preflight, overlapping SDK interrupt turns, or final settlement still join the original task rather than creating competing work or disappearing ahead of the result; explicit user abort and owner cancellation remain terminal.
16
+
17
+ ## [0.9.19-alpha.3] - 2026-09-09
18
+
19
+ ### Changed
20
+
21
+ - Reduced default top-level parallel subagent concurrency from 4 to 3. Explicit configuration and per-call overrides remain honored; the separate per-parent native turn cap remains 4.
22
+
5
23
  ## [0.9.19-alpha.2] - 2026-09-08
6
24
 
7
25
  ### Added
@@ -181,7 +181,7 @@ subagent({ agent: "codebase-analyzer", task: "Trace authentication.", wait: { ki
181
181
  subagent({ agent: "codebase-analyzer", task: "Trace authentication.", wait: { kind: "foreground", budgetMs: 30000 } })
182
182
  subagent({ action: "wait", id: taskId, budgetMs: 1000 })
183
183
  subagent({ action: "status", id: taskId })
184
- subagent({ action: "interrupt", id: taskId })
184
+ subagent({ action: "kill", id: taskId })
185
185
  ```
186
186
 
187
187
  In owner-bound sessions, omitted `wait` means background. Explicit foreground waits use the owner's agent observation budget, normally 30 seconds, unless overridden. Expiry releases the caller, not the execution. Wait or inspect the returned task ID instead of launching a duplicate. Existing SDK callers without an owner retain their original execution path.
@@ -200,7 +200,7 @@ clarify → gather context → worker → fresh reviewers → worker
200
200
 
201
201
  Packaged `worker` defaults to forked context when a launch omits `context`; every other builtin runs fresh. Pass `context: "fresh"` when you intentionally want a fresh `worker` run.
202
202
 
203
- Child-safety boundaries are enforced at runtime by typed admission policy. In-process child sessions load bundled extensions through normal discovery. The `subagent` tool may therefore be registered when the child's active tool selection permits it, including the default no-allowlist case; an explicit allowlist may omit it. Tool presence does not grant fanout: fanout is authorized only when the resolved builtin `tools` list includes `subagent`. Typed admission policy lets a non-fanout child use only `list`, `get`, and `status`; delegation and `interrupt` receive the fanout refusal. A management-restricted child is also refused `create`, `update`, and `delete`. The bundled `pi-subagents` skill remains parent-only and is stripped from child prompts, including fanout-authorized children. No admitted child may delegate or control another child: launches and `interrupt` are refused for every child regardless of its fanout authorization. Children receive boundary instructions that they are not the parent orchestrator and must complete their assigned task directly. Forked child context filtering also removes parent-only subagent artifacts (including old hidden orchestration-instruction messages, slash/status/control messages, and prior parent `subagent` tool-call/tool-result history) while preserving ordinary prose and unrelated tool calls/results.
203
+ Child-safety boundaries are enforced at runtime by typed admission policy. In-process child sessions load bundled extensions through normal discovery. The `subagent` tool may therefore be registered when the child's active tool selection permits it, including the default no-allowlist case; an explicit allowlist may omit it. Tool presence does not grant fanout: fanout is authorized only when the resolved builtin `tools` list includes `subagent`. Typed admission policy lets a non-fanout child use only `list`, `get`, and `status`; delegation and `kill` receive the fanout refusal. A management-restricted child is also refused `create`, `update`, and `delete`. The bundled `pi-subagents` skill remains parent-only and is stripped from child prompts, including fanout-authorized children. No admitted child may delegate or control another child: launches and `kill` are refused for every child regardless of its fanout authorization. Children receive boundary instructions that they are not the parent orchestrator and must complete their assigned task directly. Forked child context filtering also removes parent-only subagent artifacts (including old hidden orchestration-instruction messages, slash/status/control messages, and prior parent `subagent` tool-call/tool-result history) while preserving ordinary prose and unrelated tool calls/results.
204
204
 
205
205
 
206
206
 
@@ -230,13 +230,13 @@ The child can use one dedicated coordination tool:
230
230
 
231
231
  Child-side routine completion handoffs are still not expected. In parallel runs, blocking `contact_supervisor` decisions/interviews and parent-targeted `intercom.ask` wait only in the requesting child. The supervisor replies through Intercom to the exact question; the same child continues with its original context and run identity. Sends and progress updates return without a reply.
232
232
 
233
- An exact-child probe/commit handshake may release parallel foreground observations, including queued slots, so the parent can respond. It does not end any execution or spend a running concurrency slot. Active siblings keep working, queued siblings start once capacity becomes available, and worktrees remain until their owners finish. Do not relaunch children to answer an ask. Targeted interruption, explicit batch cancellation, and owner closure remain separate controls.
233
+ An exact-child probe/commit handshake may release parallel foreground observations, including queued slots, so the parent can respond. It does not end any execution or spend a running concurrency slot. Active siblings keep working, queued siblings start once capacity becomes available, and worktrees remain until their owners finish. Do not relaunch children to answer an ask. Targeted kill, explicit batch cancellation, and owner closure remain separate controls.
234
234
 
235
235
  Single-child launches retain their existing terminal parent-ask handoff: the exact live child ends before broker send or waiter admission and the parent receives the verbatim question, ordered attachments, identity, and `[TASK_CONTEXT]` for a fresh child. The bridge still obtains each child's capability during admission; the child connects only when it uses Intercom.
236
236
 
237
237
  Parent-side Atomic sends grouped completion results through Intercom: one grouped message per foreground parent `subagent` run and one per detached child completion while its owning session remains live. When a workflow stage completes, Atomic cancels its still-running detached children and suppresses their late findings and completion notifications instead of forwarding them to the parent/main chat. Intercom-confirmed delivery returns a compact receipt with artifact/session paths; without that confirmation, the normal full output is preserved. Grouped messages include child Intercom targets and full child summaries.
238
238
 
239
- If a child appears stalled, needs-attention notices can show up in the parent session with useful next actions, such as checking `subagent({ action: "status" })`, interrupting the run, or nudging the child.
239
+ If a child appears stalled, needs-attention notices can show up in the parent session with useful next actions, such as checking `subagent({ action: "status" })`, killing the run, or nudging the child.
240
240
 
241
241
  If messages do not show up, check the bridge from the intercom side with `intercom({ action: "status" })`.
242
242
 
@@ -434,7 +434,7 @@ What the bundled skill covers:
434
434
  - **Role-agent prompting guidance**: compact contract prompts instead of long scripts, what to include in role-specific meta prompts, and retrieval budgets for researchers
435
435
  - **Safety boundaries**: child agents must not run subagents, must not invent intercom targets, and must escalate unapproved decisions
436
436
  - **Intercom conventions**: when to ask vs send, and how parent-side result delivery works with `pi-intercom`
437
- - **Control signals**: attention signals, soft interrupts, and status
437
+ - **Control signals**: attention signals, terminal kills, and status
438
438
 
439
439
  If you are writing an agent that orchestrates subagents, the bundled skill helps it behave correctly without guessing the patterns. If you are a human user, you do not need to read it directly; the README and prompt shortcuts encode the same workflows in user-facing form.
440
440
 
@@ -524,11 +524,11 @@ Agent definitions are not loaded into context by default. Management actions let
524
524
  |-------|------|---------|-------------|
525
525
  | `agent` | string | - | Agent name for single mode, or target for management actions. |
526
526
  | `task` | string | - | Task string for single mode. |
527
- | `action` | string | - | `list`, `get`, `create`, `update`, `delete`, `status`, `wait`, or `interrupt`. |
527
+ | `action` | string | - | `list`, `get`, `create`, `update`, `delete`, `status`, `wait`, or `kill`. |
528
528
  | `config` | object/string | - | Agent config for create/update. |
529
529
  | `wait` | object | background in owner-bound sessions | `{ kind: "background" }` yields after admission; `{ kind: "foreground", budgetMs?: number }` waits before automatically yielding. |
530
530
  | `budgetMs` | number | owner wait policy | Observation budget for `action: "wait"`; not a child execution deadline. |
531
- | `id` | string | - | Task ID returned by an owner-bound launch for `wait`, `status`, or `interrupt`. |
531
+ | `id` | string | - | Task ID returned by an owner-bound launch for `wait`, `status`, or `kill`. |
532
532
  | `output` | `string \| false` | agent default | Override single-agent output file. |
533
533
  | `outputMode` | `"inline" \| "file-only"` | `inline` | Return saved output inline or as a concise saved-file reference. `file-only` requires an `output` path. |
534
534
  | `reads` | `string[] \| false` | - | Single-agent files to read before execution, or `false` to disable. Relative paths resolve against the effective child `cwd`; absolute paths pass through. |
@@ -536,7 +536,7 @@ Agent definitions are not loaded into context by default. Management actions let
536
536
  | `skill` | `string \| string[] \| false` | agent default | Override skills or disable all. |
537
537
  | `model` | string | agent default | Override model. |
538
538
  | `tasks` | array | - | Top-level parallel tasks. Supports `agent`, `task`, `cwd`, `count`, `output`, `outputMode`, `reads`, `progress`, `skill`, and `model`. |
539
- | `concurrency` | number | config or `4` | Top-level parallel concurrency. |
539
+ | `concurrency` | number | config or `3` | Top-level parallel concurrency. |
540
540
  | `worktree` | boolean | false | Create isolated git worktrees for parallel tasks. |
541
541
  | `context` | `fresh \| fork` | agent default or `fresh` | `fork` creates real branched sessions from the parent leaf. Packaged `worker` defaults to `fork`; every other builtin runs fresh. |
542
542
  | `agentScope` | `user \| project \| both` | `both` | Agent discovery scope. Project wins on collisions. |
@@ -557,10 +557,10 @@ Status and control actions:
557
557
  ```ts
558
558
  subagent({ action: "status" })
559
559
  subagent({ action: "status", id: "<run-id>" })
560
- subagent({ action: "interrupt", id: "<run-id>" })
560
+ subagent({ action: "kill", id: "<run-id>" })
561
561
  ```
562
562
 
563
- Completed, interrupted, and single-child terminal-handoff children cannot be revived by a prior run ID; follow-up work requires a fresh launch with explicit context. A parallel child waiting for a reply is not terminal and continues in its original execution. Explicit cancellation of a still-running child uses the interrupted/abort state: receipts and progress present it as cancelled rather than failed, persisted metadata keeps the abort cause, and bounded partial findings remain available.
563
+ Completed, killed, interrupted, and single-child terminal-handoff children cannot be revived by a prior run ID; follow-up work requires a fresh launch with explicit context. A parallel child waiting for a reply is not terminal and continues in its original execution. Parent cancellation of a still-running child uses the interrupted/abort state: receipts and progress present it as cancelled rather than failed, persisted metadata keeps the abort cause, and bounded partial findings remain available. Migrate old subagent calls from `action: "interrupt"` to `action: "kill"`; the old action is rejected. Kill is terminal and cannot be resumed. Workflow controls use `pause`; host interrupt APIs are unchanged.
564
564
 
565
565
  ## Worktree isolation
566
566
 
@@ -603,7 +603,7 @@ Subagent configuration controls discovery, parallel limits, session storage, con
603
603
  }
604
604
  ```
605
605
 
606
- `maxTasks` defaults to `50`; `concurrency` defaults to `4`. `maxTasks` can set a lower per-call task limit but cannot exceed the hard maximum of `50`. Per-call `concurrency` takes precedence.
606
+ `maxTasks` defaults to `50`; `concurrency` defaults to `3`. `maxTasks` can set a lower per-call task limit but cannot exceed the hard maximum of `50`. Per-call `concurrency` takes precedence.
607
607
 
608
608
  ### `defaultSessionDir`
609
609
 
@@ -681,7 +681,7 @@ Foreground runs persist their session and user-facing artifacts beside the paren
681
681
  run-history.jsonl
682
682
  ```
683
683
 
684
- Task-ID status, wait, and interrupt resolve the same owner as launch. Run-ID status uses the existing Rust subagent registry. Owner-bound completion delivery uses persisted intent and acknowledgement records in session history; no second PID registry or execution is created for the UI.
684
+ Task-ID status, wait, and kill resolve the same owner as launch. Run-ID status uses the existing Rust subagent registry. Owner-bound completion delivery uses persisted intent and acknowledgement records in session history; no second PID registry or execution is created for the UI.
685
685
 
686
686
  ## Completion and output
687
687
 
@@ -716,7 +716,7 @@ This is disabled by default. Session data may contain source code, paths, enviro
716
716
 
717
717
  ## Delegation boundary
718
718
 
719
- Delegation is exactly one level deep, and nothing configures it. A top-level session main chat or a workflow stage may call `subagent`. A session that was itself admitted as a subagent child may not: every launch and `interrupt` it attempts is refused with guidance to complete its assigned task directly. The observing actions `list`, `get`, and `status` stay available to a child. Child sessions retain bundled workflow definitions as resources but do not load the workflows extension or expose its `workflow` tool; orchestration stays owned by the parent session.
719
+ Delegation is exactly one level deep, and nothing configures it. A top-level session, main chat or a workflow stage, may call `subagent`. A session that was itself admitted as a subagent child may not: every launch and `kill` it attempts is refused with guidance to complete its assigned task directly. The observing actions `list`, `get`, and `status` stay available to a child. Child sessions retain bundled workflow definitions as resources but do not load the workflows extension or expose its `workflow` tool; orchestration stays owned by the parent session.
720
720
 
721
721
  There is no configuration option, agent frontmatter field, or tool parameter for the delegation level. The rule is enforced twice: the subagent executor refuses a child before any run starts, and the Rust `SubagentControl` admission door refuses a child deeper than the single permitted level. Admitted depth is typed admission state and is not inherited through an environment variable.
722
722
 
@@ -773,7 +773,7 @@ The main runtime files are:
773
773
  |------|---------|
774
774
  | `src/extension/index.ts` | Extension registration, tool registration, message/render wiring. |
775
775
  | `src/agents/agents.ts` | Agent discovery and frontmatter parsing. |
776
- | `src/runs/foreground/subagent-executor.ts` | Main execution routing for single, parallel, management, status, and interrupt actions. |
776
+ | `src/runs/foreground/subagent-executor.ts` | Main execution routing for single, parallel, management, status, and kill actions. |
777
777
  | `src/runs/foreground/execution.ts` | Core foreground `runSync` handling. |
778
778
  | `src/runs/foreground/notify.ts` | Completion-notification delivery for a detached Intercom child. |
779
779
  | `src/runs/foreground/completion-notification.ts` | Local completion acknowledgement and ordering barrier for detached children. |