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

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 (89) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +7 -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 +44 -4
  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 +6 -0
  11. package/dist/builtin/subagents/README.md +2 -2
  12. package/dist/builtin/subagents/package.json +1 -1
  13. package/dist/builtin/subagents/src/extension/index.bundle.mjs +2 -2
  14. package/dist/builtin/web-access/package.json +1 -1
  15. package/dist/builtin/workflows/CHANGELOG.md +13 -0
  16. package/dist/builtin/workflows/package.json +1 -1
  17. package/dist/builtin/workflows/src/extension/index.bundle.mjs +56 -33
  18. package/dist/builtin/workflows/src/index.js +14 -4
  19. package/dist/core/agent-session-message-queue.d.ts.map +1 -1
  20. package/dist/core/agent-session-message-queue.js +2 -2
  21. package/dist/core/agent-session-message-queue.js.map +1 -1
  22. package/dist/core/agent-session-methods.d.ts +3 -0
  23. package/dist/core/agent-session-methods.d.ts.map +1 -1
  24. package/dist/core/agent-session-methods.js.map +1 -1
  25. package/dist/core/agent-session-tasks.d.ts +5 -0
  26. package/dist/core/agent-session-tasks.d.ts.map +1 -1
  27. package/dist/core/agent-session-tasks.js +8 -1
  28. package/dist/core/agent-session-tasks.js.map +1 -1
  29. package/dist/core/extensions/loader-api.d.ts.map +1 -1
  30. package/dist/core/extensions/loader-api.js +3 -0
  31. package/dist/core/extensions/loader-api.js.map +1 -1
  32. package/dist/core/extensions/loader-host-modules.d.ts +1 -1
  33. package/dist/core/extensions/loader-host-modules.d.ts.map +1 -1
  34. package/dist/core/extensions/loader-host-modules.js +2 -1
  35. package/dist/core/extensions/loader-host-modules.js.map +1 -1
  36. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  37. package/dist/core/extensions/loader-virtual-modules.js +4 -1
  38. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  39. package/dist/core/retry-policy.d.ts +1 -0
  40. package/dist/core/retry-policy.d.ts.map +1 -1
  41. package/dist/core/retry-policy.js +2 -1
  42. package/dist/core/retry-policy.js.map +1 -1
  43. package/dist/core/settings-manager-basic-accessors.d.ts +1 -0
  44. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  45. package/dist/core/settings-manager-basic-accessors.js +2 -0
  46. package/dist/core/settings-manager-basic-accessors.js.map +1 -1
  47. package/dist/core/settings-types.d.ts +1 -0
  48. package/dist/core/settings-types.d.ts.map +1 -1
  49. package/dist/core/settings-types.js.map +1 -1
  50. package/dist/core/tasks/agent-adapter.d.ts +9 -0
  51. package/dist/core/tasks/agent-adapter.d.ts.map +1 -1
  52. package/dist/core/tasks/agent-adapter.js +36 -2
  53. package/dist/core/tasks/agent-adapter.js.map +1 -1
  54. package/dist/core/tasks/pause.d.ts +4 -0
  55. package/dist/core/tasks/pause.d.ts.map +1 -0
  56. package/dist/core/tasks/pause.js +49 -0
  57. package/dist/core/tasks/pause.js.map +1 -0
  58. package/dist/core/tasks/supervisor.d.ts +3 -1
  59. package/dist/core/tasks/supervisor.d.ts.map +1 -1
  60. package/dist/core/tasks/supervisor.js +48 -22
  61. package/dist/core/tasks/supervisor.js.map +1 -1
  62. package/dist/core/tools/bash.d.ts +2 -0
  63. package/dist/core/tools/bash.d.ts.map +1 -1
  64. package/dist/core/tools/bash.js +1 -1
  65. package/dist/core/tools/bash.js.map +1 -1
  66. package/dist/core/tools/powershell.d.ts.map +1 -1
  67. package/dist/core/tools/powershell.js +1 -0
  68. package/dist/core/tools/powershell.js.map +1 -1
  69. package/dist/core/tools/resource-selectors.d.ts +1 -1
  70. package/dist/core/tools/resource-selectors.d.ts.map +1 -1
  71. package/dist/core/tools/resource-selectors.js +7 -4
  72. package/dist/core/tools/resource-selectors.js.map +1 -1
  73. package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
  74. package/dist/modes/interactive/components/chat-session-host.js +3 -2
  75. package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
  76. package/docs/background-tasks.md +7 -1
  77. package/docs/development.md +23 -74
  78. package/docs/extensions.md +4 -0
  79. package/docs/herdr.md +25 -78
  80. package/docs/intercom.md +4 -0
  81. package/docs/models/evals.md +2 -0
  82. package/docs/models/model-selection.md +10 -7
  83. package/docs/settings.md +4 -0
  84. package/docs/subagents.md +3 -1
  85. package/docs/tools.md +4 -0
  86. package/docs/workflows/api-reference.md +1 -1
  87. package/docs/workflows/operations.md +8 -3
  88. package/npm-shrinkwrap.json +59 -59
  89. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.9.19-alpha.3] - 2026-09-09
6
+
7
+ ### Fixed
8
+
9
+ - Fixed `Failed to initialize class constructor` when npm-installed Node sessions create multiple task supervisors, preventing shell commands and subagent launches from failing during task-host initialization.
10
+ - Fixed foreground subagent launches and explicit waits blocking parent user steering and incoming Intercom asks/sends. Admitted messages now release the waiting parent's observations in main and workflow-stage chat without cancelling children or affecting other owners.
11
+ - Fixed PowerShell internal-URL path quoting so apostrophes and smart single quotes in resolved paths remain literal instead of allowing injected commands. Bash quoting, deliberate shell commands, and balanced command prefixes are unchanged.
12
+ - Capped agent retry backoff with `retry.maxAgentDelayMs` (60 seconds by default), preserving independent provider retry limits ([#8826](https://github.com/earendil-works/pi/issues/8826)).
13
+ - Rejected extension tools with missing or non-object parameter schema containers during registration instead of breaking provider requests ([#9300](https://github.com/earendil-works/pi/issues/9300)).
14
+ - Workflow-stage pause now blocks new task launches, cancels active and admitted queued agents and commands, and waits for in-flight command admission and resource cleanup. Main-chat and sibling tasks stay unaffected; queued user and Intercom messages survive, and resume permits fresh work without restarting cancelled executions.
15
+ - Fixed npm-installed Node startup failing with a missing upstream AI package after the retry-policy update. Core retry imports now use Atomic's declared AI dependency.
16
+ - Fixed an unhandled rejection when session cancellation interrupts an in-flight stage-chat pause, while preserving pause error reporting and rejection for callers awaiting completion.
17
+ - Fixed Windows extension reloads re-evaluating Atomic's host modules, avoiding duplicate host classes and long reload delays while preserving edits to extension dependencies. The supported `@earendil-works/pi-coding-agent` import also shares host identity with `@bastani/atomic` after reload.
18
+
5
19
  ## [0.9.19-alpha.2] - 2026-09-08
6
20
 
7
21
  ### Added
@@ -4,6 +4,13 @@ All notable changes to the `pi-intercom` extension will be documented in this fi
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.9.19-alpha.3] - 2026-09-09
8
+
9
+ ### Fixed
10
+
11
+ - Session listings display host-provided live/terminal reply capability separately from idle activity. Closed workflow generations publish `closed` with post-mortem-only or unavailable reply routing rather than remaining misleadingly idle; group isolation and ask rejection rules are unchanged.
12
+ - Canonical workflow-path asks correlate replies from connected retained stages even after their active roster rows disappear. Closed stages without post-mortem routing reject asks with lifecycle guidance rather than attempting a model turn.
13
+
7
14
  ## [0.9.19-alpha.2] - 2026-09-08
8
15
 
9
16
  ### Breaking Changes
@@ -86,6 +86,7 @@ interface PendingStageAcknowledgment {
86
86
  readonly message: Message;
87
87
  readonly liveTargetId?: string;
88
88
  readonly signature?: string;
89
+ readonly resolveReplyTarget?: true;
89
90
  readonly timeout: NodeJS.Timeout;
90
91
  }
91
92
 
@@ -681,6 +682,7 @@ class IntercomBroker {
681
682
  message: route.message,
682
683
  ...(route.liveTargetId === undefined ? {} : { liveTargetId: route.liveTargetId }),
683
684
  ...(route.signature === undefined ? {} : { signature: route.signature }),
685
+ ...(route.resolveReplyTarget === true ? { resolveReplyTarget: true } : {}),
684
686
  timeout,
685
687
  });
686
688
  if (
@@ -828,6 +830,8 @@ class IntercomBroker {
828
830
  return;
829
831
  }
830
832
  this.pendingQuestions.record(from.info.id, target.info.id, pending.messageId);
833
+ if (pending.resolveReplyTarget === true && pending.attemptId !== undefined)
834
+ writeMessageIfOpen(pending.senderSocket, { type: "question_target", messageId: pending.messageId, attemptId: pending.attemptId, sessionId: target.info.id });
831
835
  }
832
836
  writeMessageIfOpen(pending.senderSocket, {
833
837
  type: "delivered",
@@ -880,6 +884,8 @@ class IntercomBroker {
880
884
  return;
881
885
  }
882
886
  this.pendingQuestions.record(from.info.id, target.info.id, pending.messageId);
887
+ if (pending.resolveReplyTarget === true && pending.attemptId !== undefined)
888
+ writeMessageIfOpen(pending.senderSocket, { type: "question_target", messageId: pending.messageId, attemptId: pending.attemptId, sessionId: target.info.id });
883
889
  }
884
890
  writeMessageIfOpen(pending.senderSocket, {
885
891
  type: "delivered",
@@ -903,6 +909,8 @@ class IntercomBroker {
903
909
  });
904
910
  return;
905
911
  }
912
+ if (pending.message.expectsReply === true && pending.resolveReplyTarget === true && pending.attemptId !== undefined)
913
+ writeMessageIfOpen(pending.senderSocket, { type: "question_target", messageId: pending.messageId, attemptId: pending.attemptId, sessionId: target.info.id });
906
914
  if (!writeMessageIfOpen(target.socket, { type: "message", from: pending.sender, message: pending.message })) {
907
915
  this.deliveredMessages.forget(pending.messageId, pending.signature);
908
916
  writeMessageIfOpen(pending.senderSocket, {
@@ -38,6 +38,8 @@ export interface SendOptions {
38
38
  logicalTarget?: string;
39
39
  /** Public reply must use the broker's exact pending reverse question route. */
40
40
  requirePendingReply?: true;
41
+ /** Broker-authorized identity for a canonical-path ask, delivered before its recipient can reply. */
42
+ onReplyTarget?: (sessionId: string) => void;
41
43
  }
42
44
 
43
45
  export interface SendResult {
@@ -153,6 +155,7 @@ export class IntercomClient extends EventEmitter {
153
155
  /** Source identity is captured once at connect, never read from env per message. */
154
156
  private _messageSource: Message["source"] | undefined;
155
157
  private pendingSends = new PendingSendRegistry();
158
+ private pendingReplyTargets = new Map<string, { messageId: string; bind(sessionId: string): void }>();
156
159
  private pendingGroupLists = new Map<string, { resolve: (groups: GroupSummary[]) => void; reject: (error: Error) => void }>();
157
160
  private pendingLists = new Map<string, { resolve: (directory: SessionDirectory) => void; reject: (e: Error) => void }>();
158
161
  private pendingPresence = new Map<string, {
@@ -415,6 +418,15 @@ export class IntercomClient extends EventEmitter {
415
418
  this.emit("_registration_failed", new Error(brokerMessage.reason));
416
419
  break;
417
420
  }
421
+ case "question_target": {
422
+ const { messageId, attemptId, sessionId } = brokerMessage;
423
+ if (typeof messageId !== "string" || typeof attemptId !== "string" || typeof sessionId !== "string") {
424
+ throw new Error("Invalid question_target message");
425
+ }
426
+ const pending = this.pendingReplyTargets.get(attemptId);
427
+ if (pending?.messageId === messageId) pending.bind(sessionId);
428
+ break;
429
+ }
418
430
  case "sessions": {
419
431
  const { requestId, sessions, workflowStages, workflowFutureStages } = brokerMessage;
420
432
  if (
@@ -989,12 +1001,16 @@ export class IntercomClient extends EventEmitter {
989
1001
  source: this._messageSource,
990
1002
  content: { text: options.text, attachments: options.attachments },
991
1003
  };
1004
+ if (options.onReplyTarget !== undefined) {
1005
+ this.pendingReplyTargets.set(acquired.attempt.attemptId, { messageId, bind: options.onReplyTarget });
1006
+ }
992
1007
  try {
993
1008
  writeMessage(socket, {
994
1009
  type,
995
1010
  to,
996
1011
  ...(options.logicalTarget === undefined ? {} : { logicalTarget: options.logicalTarget }),
997
1012
  ...(options.requirePendingReply === undefined ? {} : { requirePendingReply: options.requirePendingReply }),
1013
+ ...(options.onReplyTarget === undefined ? {} : { resolveReplyTarget: true as const }),
998
1014
  message,
999
1015
  attemptId: acquired.attempt.attemptId,
1000
1016
  ...extra,
@@ -1002,6 +1018,8 @@ export class IntercomClient extends EventEmitter {
1002
1018
  } catch (error) {
1003
1019
  this.pendingSends.reject(acquired.attempt, toError(error));
1004
1020
  }
1021
+ const clearReplyTarget = () => { this.pendingReplyTargets.delete(acquired.attempt.attemptId); };
1022
+ void acquired.attempt.promise.then(clearReplyTarget, clearReplyTarget);
1005
1023
  return acquired.attempt.promise;
1006
1024
  }
1007
1025
  updatePresence(updates: PresenceUpdates): boolean {
@@ -59,6 +59,7 @@ export interface PendingStageRoute {
59
59
  readonly attemptId?: string;
60
60
  readonly liveTargetId?: string;
61
61
  readonly signature?: string;
62
+ readonly resolveReplyTarget?: true;
62
63
  }
63
64
 
64
65
  export type PendingStageRouter = (route: PendingStageRoute) => boolean;
@@ -326,6 +327,8 @@ export function handleBrokerSend(
326
327
  return;
327
328
  }
328
329
  pendingQuestions.record(fromSession.info.id, target.info.id, message.id);
330
+ if (clientMessage.resolveReplyTarget === true && attemptId !== undefined)
331
+ write(socket, { type: "question_target", messageId: message.id, attemptId, sessionId: target.info.id });
329
332
  }
330
333
  write(socket, { type: "delivered", messageId: message.id, attemptId });
331
334
  return;
@@ -369,6 +372,7 @@ export function handleBrokerSend(
369
372
  ...(attemptId ? { attemptId } : {}),
370
373
  liveTargetId: target.info.id,
371
374
  signature,
375
+ ...(clientMessage.resolveReplyTarget === true ? { resolveReplyTarget: true } : {}),
372
376
  })
373
377
  ) {
374
378
  return;
@@ -416,6 +420,8 @@ export function handleBrokerSend(
416
420
  return;
417
421
  }
418
422
  if (message.expectsReply === true) pendingQuestions.record(fromSession.info.id, target.info.id, message.id);
423
+ if (message.expectsReply === true && clientMessage.resolveReplyTarget === true && attemptId !== undefined)
424
+ write(socket, { type: "question_target", messageId: message.id, attemptId, sessionId: target.info.id });
419
425
  write(socket, { type: "delivered", messageId: message.id, attemptId });
420
426
  return;
421
427
  }
@@ -433,6 +439,8 @@ export function handleBrokerSend(
433
439
  });
434
440
  return;
435
441
  }
442
+ if (message.expectsReply === true && clientMessage.resolveReplyTarget === true && attemptId !== undefined)
443
+ write(socket, { type: "question_target", messageId: message.id, attemptId, sessionId: target.info.id });
436
444
  const finishDelivery = (): void => {
437
445
  let accepted: ReturnType<DeliveredMessageCache["accept"]>;
438
446
  try {
@@ -508,6 +516,7 @@ export function handleBrokerSend(
508
516
  message,
509
517
  ...(attemptId ? { attemptId } : {}),
510
518
  signature,
519
+ ...(clientMessage.resolveReplyTarget === true ? { resolveReplyTarget: true } : {}),
511
520
  })
512
521
  ) {
513
522
  return;
@@ -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
  };
@@ -2414,7 +2437,8 @@ function formatSessionListRow(session, currentCwd, isSelf) {
2414
2437
  const name = session.name || "Unnamed session";
2415
2438
  const normalizedGroup = normalizeGroup(session.group);
2416
2439
  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));
2440
+ const capabilityTag = session.replyCapability === undefined ? undefined : `replyCapability: ${session.replyCapability}`;
2441
+ const tags = [isSelf ? "self" : session.cwd === currentCwd ? "same cwd" : undefined, session.status, capabilityTag, groupTag].filter((tag) => Boolean(tag));
2418
2442
  const suffix = tags.length ? ` [${tags.join(", ")}]` : "";
2419
2443
  return `• ${name} (${session.id}) — ${session.cwd} (${session.model})${suffix}`;
2420
2444
  }
@@ -3755,7 +3779,8 @@ ${message}${attachmentText}`);
3755
3779
  text: message ?? "",
3756
3780
  attachments,
3757
3781
  replyTo,
3758
- expectsReply: true
3782
+ expectsReply: true,
3783
+ ...parseWorkflowStageTarget(to)?.kind === "path" ? { onReplyTarget: wait.bindSender } : {}
3759
3784
  });
3760
3785
  if (sendResult.queued === true) {
3761
3786
  retryIdentities.release(retryIdentity);
@@ -4487,7 +4512,10 @@ var init_overlay = __esm(() => {
4487
4512
  function registerIntercomLifecycle(pi, deps) {
4488
4513
  const activeReplyTracker = () => typeof deps.replyTracker === "function" ? deps.replyTracker() : deps.replyTracker;
4489
4514
  let hasActiveSession = false;
4515
+ let unbindStageClose;
4490
4516
  async function cleanupRuntime(reason) {
4517
+ unbindStageClose?.();
4518
+ unbindStageClose = undefined;
4491
4519
  deps.setRuntimeStarted(false);
4492
4520
  deps.setShuttingDown(true);
4493
4521
  deps.setDisposed(true);
@@ -4538,6 +4566,12 @@ function registerIntercomLifecycle(pi, deps) {
4538
4566
  deps.setSessionStartedAt(Date.now());
4539
4567
  deps.setAgentRunning(false);
4540
4568
  deps.activeTools.clear();
4569
+ const stage = ctx.orchestrationContext;
4570
+ const closeSignal = stage?.kind === "workflow-stage" ? stage.messageAdmission?.boundary?.closeSignal : undefined;
4571
+ closeSignal?.addEventListener("abort", deps.syncPresenceStatus, { once: true });
4572
+ unbindStageClose = () => closeSignal?.removeEventListener("abort", deps.syncPresenceStatus);
4573
+ if (closeSignal?.aborted)
4574
+ deps.syncPresenceStatus();
4541
4575
  });
4542
4576
  pi.on("session_shutdown", async () => {
4543
4577
  if (!hasActiveSession)
@@ -6263,7 +6297,8 @@ function piIntercomExtension(pi, testOverrides = {}) {
6263
6297
  }
6264
6298
  function currentStatus() {
6265
6299
  const activeToolName = activeTools.values().next().value;
6266
- const lifecycleStatus = activeToolName ? `tool:${activeToolName}` : agentRunning ? "thinking" : "idle";
6300
+ const stage = getLiveContext()?.orchestrationContext;
6301
+ 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
6302
  return config.status ? `${lifecycleStatus} · ${config.status}` : lifecycleStatus;
6268
6303
  }
6269
6304
  function resolveSessionHomeGroup() {
@@ -6411,7 +6446,12 @@ function piIntercomExtension(pi, testOverrides = {}) {
6411
6446
  const stageAdmission = liveContext.orchestrationContext?.kind === "workflow-stage" ? liveContext.orchestrationContext.messageAdmission : undefined;
6412
6447
  const stageClosed = stageAdmission?.isOpen() === false;
6413
6448
  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));
6449
+ routeClosedWorkflowStageMessage(entry, inboundDeliveries, replyTracker, replyWaiters.pending(), () => {
6450
+ if (message.expectsReply === true && liveContext.orchestrationContext?.lateMessageRouter === undefined) {
6451
+ 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.");
6452
+ }
6453
+ return sendIncomingMessage(entry, isDeliveryFeedback(message) ? "prelude" : "trigger", messageGeneration, false);
6454
+ }, () => client, () => Boolean(getLiveContext(liveContext, messageGeneration)), (runId) => stageAdmission.boundary.ownsSubagentRun(runId));
6415
6455
  return;
6416
6456
  }
6417
6457
  const admission = inboundDeliveries.admit(from, message);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/intercom",
3
- "version": "0.9.19-alpha.2",
3
+ "version": "0.9.19-alpha.3",
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.3",
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,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.9.19-alpha.3] - 2026-09-09
6
+
7
+ ### Changed
8
+
9
+ - 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.
10
+
5
11
  ## [0.9.19-alpha.2] - 2026-09-08
6
12
 
7
13
  ### Added
@@ -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. |
@@ -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
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/subagents",
3
- "version": "0.9.19-alpha.2",
3
+ "version": "0.9.19-alpha.3",
4
4
  "private": true,
5
5
  "description": "Atomic extension for delegating tasks to subagents with parallel execution. Fork of: https://github.com/nicobailon/pi-subagents",
6
6
  "contributors": [
@@ -8495,7 +8495,7 @@ function resolveTempScopeId(options) {
8495
8495
  return "shared";
8496
8496
  }
8497
8497
  var MAX_PARALLEL_TASKS = 50;
8498
- var MAX_CONCURRENCY = 4;
8498
+ var MAX_CONCURRENCY = 3;
8499
8499
  var TEMP_ROOT_DIR = path6.join(os4.tmpdir(), `${APP_NAME2}-subagents-${resolveTempScopeId()}`);
8500
8500
  var TEMP_ARTIFACTS_DIR = path6.join(TEMP_ROOT_DIR, "artifacts");
8501
8501
  var SLASH_RESULT_TYPE = "subagent-slash-result";
@@ -17175,7 +17175,7 @@ var SubagentParams = Type.Object({
17175
17175
  })),
17176
17176
  concurrency: Type.Optional(Type.Integer({
17177
17177
  minimum: 1,
17178
- description: "Top-level PARALLEL mode only: max concurrent tasks. Defaults to config.parallel.concurrency or 4."
17178
+ description: "Top-level PARALLEL mode only: max concurrent tasks. Defaults to config.parallel.concurrency or 3."
17179
17179
  })),
17180
17180
  group: Type.Optional(GroupSchema),
17181
17181
  worktree: Type.Optional(Type.Boolean({
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/web-access",
3
- "version": "0.9.19-alpha.2",
3
+ "version": "0.9.19-alpha.3",
4
4
  "private": true,
5
5
  "description": "Atomic extension for web search, URL fetching, GitHub repo cloning, PDF/video extraction. Fork of: https://github.com/nicobailon/pi-web-access",
6
6
  "contributors": [
@@ -6,6 +6,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.9.19-alpha.3] - 2026-09-09
10
+
11
+ ### Fixed
12
+
13
+ - Local `workflow stages` now uses the same expanded nested graph as exact stage/transcript lookup, matching retained durable inspection instead of listing hidden import boundaries.
14
+ - Runner-created workflow sessions retain their pending-delivery context while receiving the host late-message route, so completed-stage Intercom asks can reopen the exact retained conversation without re-running the workflow.
15
+ - Stage pause now cancels owned active and admitted queued agents and commands before acknowledging completion, including shells admitted during already-in-flight setup. It waits for cleanup without permanently closing message admission; resume releases queued user and Intercom messages and permits fresh work, without reviving cancelled executions or affecting sibling stages.
16
+ - Fixed the `BACKGROUND` workflow list's stage count and `single`/`chain` label to include recursively nested stages as they appear, without double-counting expanded workflow boundaries. Completed, failed, and skipped stages retain their existing progress semantics. Recursive counts share run lookup preparation across cards within each refresh.
17
+
18
+ ### Changed
19
+
20
+ - Reduced default workflow stage and authored parallel concurrency from 4 to 3, preserving explicit configuration and per-call overrides.
21
+
9
22
  ## [0.9.19-alpha.2] - 2026-09-08
10
23
 
11
24
  ### Added
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/workflows",
3
- "version": "0.9.19-alpha.2",
3
+ "version": "0.9.19-alpha.3",
4
4
  "private": true,
5
5
  "description": "Atomic extension for multi-stage workflow authoring and execution.",
6
6
  "contributors": [