@bastani/atomic 0.8.21-0 → 0.8.22-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.
Files changed (235) hide show
  1. package/CHANGELOG.md +46 -9
  2. package/dist/builtin/intercom/broker/broker.ts +3 -3
  3. package/dist/builtin/intercom/config.ts +3 -3
  4. package/dist/builtin/intercom/index.ts +1 -1
  5. package/dist/builtin/intercom/package.json +1 -1
  6. package/dist/builtin/intercom/ui/compose.ts +2 -2
  7. package/dist/builtin/mcp/host-html-template.ts +0 -3
  8. package/dist/builtin/mcp/package.json +1 -1
  9. package/dist/builtin/subagents/CHANGELOG.md +13 -4
  10. package/dist/builtin/subagents/agents/codebase-online-researcher.md +9 -9
  11. package/dist/builtin/subagents/agents/debugger.md +6 -6
  12. package/dist/builtin/subagents/package.json +1 -1
  13. package/dist/builtin/subagents/prompts/parallel-handoff-plan.md +1 -1
  14. package/dist/builtin/subagents/skills/browser-use/SKILL.md +234 -0
  15. package/dist/builtin/subagents/skills/browser-use/references/cdp-python.md +76 -0
  16. package/dist/builtin/subagents/skills/browser-use/references/multi-session.md +92 -0
  17. package/dist/builtin/subagents/skills/subagent/SKILL.md +4 -4
  18. package/dist/builtin/subagents/src/agents/skills.ts +19 -1
  19. package/dist/builtin/subagents/src/extension/index.ts +24 -22
  20. package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +7 -1
  21. package/dist/builtin/subagents/src/runs/background/async-execution.ts +23 -7
  22. package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +98 -3
  23. package/dist/builtin/subagents/src/runs/background/async-status.ts +3 -1
  24. package/dist/builtin/subagents/src/runs/background/run-status.ts +1 -1
  25. package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +3 -0
  26. package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +37 -12
  27. package/dist/builtin/subagents/src/runs/foreground/chain-clarify.ts +15 -15
  28. package/dist/builtin/subagents/src/runs/foreground/execution.ts +26 -2
  29. package/dist/builtin/subagents/src/runs/shared/nested-render.ts +1 -1
  30. package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +7 -0
  31. package/dist/builtin/subagents/src/runs/shared/pi-args.ts +28 -1
  32. package/dist/builtin/subagents/src/shared/fast-mode.ts +80 -0
  33. package/dist/builtin/subagents/src/shared/formatters.ts +4 -2
  34. package/dist/builtin/subagents/src/shared/types.ts +4 -2
  35. package/dist/builtin/subagents/src/shared/utils.ts +3 -61
  36. package/dist/builtin/subagents/src/tui/render.ts +303 -157
  37. package/dist/builtin/web-access/package.json +1 -1
  38. package/dist/builtin/workflows/CHANGELOG.md +101 -35
  39. package/dist/builtin/workflows/README.md +228 -41
  40. package/dist/builtin/workflows/builtin/deep-research-codebase.ts +535 -541
  41. package/dist/builtin/workflows/builtin/goal.ts +39 -25
  42. package/dist/builtin/workflows/builtin/open-claude-design.ts +66 -69
  43. package/dist/builtin/workflows/builtin/ralph.ts +21 -21
  44. package/dist/builtin/workflows/package.json +6 -5
  45. package/dist/builtin/workflows/skills/research-codebase/SKILL.md +1 -1
  46. package/dist/builtin/workflows/src/extension/background-ui-adapter.ts +2 -2
  47. package/dist/builtin/workflows/src/extension/discovery.ts +25 -146
  48. package/dist/builtin/workflows/src/extension/dispatcher.ts +72 -24
  49. package/dist/builtin/workflows/src/extension/hil-answer-notifications.ts +363 -0
  50. package/dist/builtin/workflows/src/extension/index.ts +690 -352
  51. package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +99 -62
  52. package/dist/builtin/workflows/src/extension/render-call.ts +2 -1
  53. package/dist/builtin/workflows/src/extension/render-result.ts +9 -3
  54. package/dist/builtin/workflows/src/extension/renderers.ts +5 -3
  55. package/dist/builtin/workflows/src/extension/runtime.ts +68 -33
  56. package/dist/builtin/workflows/src/extension/status-writer.ts +1 -1
  57. package/dist/builtin/workflows/src/extension/wiring.ts +34 -13
  58. package/dist/builtin/workflows/src/extension/workflow-module-loader.ts +142 -0
  59. package/dist/builtin/workflows/src/extension/workflow-schema.ts +4 -4
  60. package/dist/builtin/workflows/src/index.ts +2 -0
  61. package/dist/builtin/workflows/src/intercom/result-intercom.ts +1 -1
  62. package/dist/builtin/workflows/src/runs/background/runner.ts +6 -4
  63. package/dist/builtin/workflows/src/runs/background/status.ts +45 -21
  64. package/dist/builtin/workflows/src/runs/foreground/executor.ts +624 -52
  65. package/dist/builtin/workflows/src/runs/foreground/stage-control-registry.ts +1 -1
  66. package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +80 -24
  67. package/dist/builtin/workflows/src/runs/shared/validate-inputs.ts +61 -24
  68. package/dist/builtin/workflows/src/runs/shared/workflow-runner.ts +32 -10
  69. package/dist/builtin/workflows/src/sdk-surface.ts +6 -0
  70. package/dist/builtin/workflows/src/shared/expanded-workflow-graph.ts +178 -0
  71. package/dist/builtin/workflows/src/shared/persistence-restore.ts +92 -12
  72. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +21 -3
  73. package/dist/builtin/workflows/src/shared/render-inputs-schema.ts +1 -2
  74. package/dist/builtin/workflows/src/shared/run-visibility.ts +9 -0
  75. package/dist/builtin/workflows/src/shared/schema-introspection.ts +121 -0
  76. package/dist/builtin/workflows/src/shared/serializable.ts +132 -0
  77. package/dist/builtin/workflows/src/shared/stage-ui-broker.ts +91 -9
  78. package/dist/builtin/workflows/src/shared/store-types.ts +31 -3
  79. package/dist/builtin/workflows/src/shared/store.ts +58 -14
  80. package/dist/builtin/workflows/src/shared/types.ts +105 -40
  81. package/dist/builtin/workflows/src/tui/chat-surface-message.ts +129 -13
  82. package/dist/builtin/workflows/src/tui/chat-surface.ts +6 -1
  83. package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +3 -2
  84. package/dist/builtin/workflows/src/tui/graph-canvas.ts +1 -1
  85. package/dist/builtin/workflows/src/tui/graph-view.ts +91 -65
  86. package/dist/builtin/workflows/src/tui/inline-form-card.ts +1 -1
  87. package/dist/builtin/workflows/src/tui/inline-form-overlay.ts +3 -2
  88. package/dist/builtin/workflows/src/tui/inputs-overlay.ts +3 -2
  89. package/dist/builtin/workflows/src/tui/inputs-picker.ts +8 -7
  90. package/dist/builtin/workflows/src/tui/keybindings-adapter.ts +2 -0
  91. package/dist/builtin/workflows/src/tui/node-card.ts +34 -8
  92. package/dist/builtin/workflows/src/tui/overlay-adapter.ts +4 -11
  93. package/dist/builtin/workflows/src/tui/prompt-card.ts +98 -50
  94. package/dist/builtin/workflows/src/tui/session-list.ts +7 -2
  95. package/dist/builtin/workflows/src/tui/session-picker.ts +2 -0
  96. package/dist/builtin/workflows/src/tui/stage-chat-view.ts +226 -55
  97. package/dist/builtin/workflows/src/tui/status-helpers.ts +2 -0
  98. package/dist/builtin/workflows/src/tui/store-widget-installer.ts +37 -158
  99. package/dist/builtin/workflows/src/tui/toast.ts +2 -2
  100. package/dist/builtin/workflows/src/tui/widget.ts +53 -12
  101. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +270 -19
  102. package/dist/builtin/workflows/src/tui/workflow-notice-card.ts +184 -0
  103. package/dist/builtin/workflows/src/workflows/define-workflow.ts +138 -43
  104. package/dist/config.d.ts +9 -0
  105. package/dist/config.d.ts.map +1 -1
  106. package/dist/config.js +45 -0
  107. package/dist/config.js.map +1 -1
  108. package/dist/core/agent-session.d.ts +27 -9
  109. package/dist/core/agent-session.d.ts.map +1 -1
  110. package/dist/core/agent-session.js +196 -17
  111. package/dist/core/agent-session.js.map +1 -1
  112. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  113. package/dist/core/atomic-guide-command.js +2 -2
  114. package/dist/core/atomic-guide-command.js.map +1 -1
  115. package/dist/core/codex-fast-mode.d.ts +36 -0
  116. package/dist/core/codex-fast-mode.d.ts.map +1 -0
  117. package/dist/core/codex-fast-mode.js +117 -0
  118. package/dist/core/codex-fast-mode.js.map +1 -0
  119. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  120. package/dist/core/compaction/branch-summarization.js +1 -1
  121. package/dist/core/compaction/branch-summarization.js.map +1 -1
  122. package/dist/core/compaction/compaction.d.ts.map +1 -1
  123. package/dist/core/compaction/compaction.js +1 -1
  124. package/dist/core/compaction/compaction.js.map +1 -1
  125. package/dist/core/extensions/index.d.ts +4 -1
  126. package/dist/core/extensions/index.d.ts.map +1 -1
  127. package/dist/core/extensions/index.js +1 -0
  128. package/dist/core/extensions/index.js.map +1 -1
  129. package/dist/core/extensions/loader.d.ts +7 -2
  130. package/dist/core/extensions/loader.d.ts.map +1 -1
  131. package/dist/core/extensions/loader.js +23 -8
  132. package/dist/core/extensions/loader.js.map +1 -1
  133. package/dist/core/extensions/reactive-widget.d.ts +58 -0
  134. package/dist/core/extensions/reactive-widget.d.ts.map +1 -0
  135. package/dist/core/extensions/reactive-widget.js +182 -0
  136. package/dist/core/extensions/reactive-widget.js.map +1 -0
  137. package/dist/core/extensions/runner.d.ts.map +1 -1
  138. package/dist/core/extensions/runner.js +1 -0
  139. package/dist/core/extensions/runner.js.map +1 -1
  140. package/dist/core/extensions/types.d.ts +26 -12
  141. package/dist/core/extensions/types.d.ts.map +1 -1
  142. package/dist/core/extensions/types.js.map +1 -1
  143. package/dist/core/messages.d.ts +1 -1
  144. package/dist/core/messages.d.ts.map +1 -1
  145. package/dist/core/messages.js +8 -2
  146. package/dist/core/messages.js.map +1 -1
  147. package/dist/core/model-registry.d.ts +4 -0
  148. package/dist/core/model-registry.d.ts.map +1 -1
  149. package/dist/core/model-registry.js +11 -0
  150. package/dist/core/model-registry.js.map +1 -1
  151. package/dist/core/resource-loader.d.ts +9 -1
  152. package/dist/core/resource-loader.d.ts.map +1 -1
  153. package/dist/core/resource-loader.js +49 -21
  154. package/dist/core/resource-loader.js.map +1 -1
  155. package/dist/core/sdk.d.ts.map +1 -1
  156. package/dist/core/sdk.js +22 -13
  157. package/dist/core/sdk.js.map +1 -1
  158. package/dist/core/session-manager.d.ts +7 -5
  159. package/dist/core/session-manager.d.ts.map +1 -1
  160. package/dist/core/session-manager.js +5 -3
  161. package/dist/core/session-manager.js.map +1 -1
  162. package/dist/core/settings-manager.d.ts +16 -0
  163. package/dist/core/settings-manager.d.ts.map +1 -1
  164. package/dist/core/settings-manager.js +64 -5
  165. package/dist/core/settings-manager.js.map +1 -1
  166. package/dist/core/slash-commands.d.ts.map +1 -1
  167. package/dist/core/slash-commands.js +1 -0
  168. package/dist/core/slash-commands.js.map +1 -1
  169. package/dist/core/system-prompt.d.ts.map +1 -1
  170. package/dist/core/system-prompt.js +7 -4
  171. package/dist/core/system-prompt.js.map +1 -1
  172. package/dist/core/tools/ask-user-question/ask-user-question.d.ts.map +1 -1
  173. package/dist/core/tools/ask-user-question/ask-user-question.js +2 -2
  174. package/dist/core/tools/ask-user-question/ask-user-question.js.map +1 -1
  175. package/dist/index.d.ts +4 -3
  176. package/dist/index.d.ts.map +1 -1
  177. package/dist/index.js +3 -2
  178. package/dist/index.js.map +1 -1
  179. package/dist/main.d.ts +3 -0
  180. package/dist/main.d.ts.map +1 -1
  181. package/dist/main.js +12 -0
  182. package/dist/main.js.map +1 -1
  183. package/dist/modes/interactive/chat-input-actions.d.ts.map +1 -1
  184. package/dist/modes/interactive/chat-input-actions.js.map +1 -1
  185. package/dist/modes/interactive/components/diff.d.ts.map +1 -1
  186. package/dist/modes/interactive/components/diff.js +0 -1
  187. package/dist/modes/interactive/components/diff.js.map +1 -1
  188. package/dist/modes/interactive/components/fast-mode-selector.d.ts +27 -0
  189. package/dist/modes/interactive/components/fast-mode-selector.d.ts.map +1 -0
  190. package/dist/modes/interactive/components/fast-mode-selector.js +105 -0
  191. package/dist/modes/interactive/components/fast-mode-selector.js.map +1 -0
  192. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  193. package/dist/modes/interactive/components/footer.js +7 -12
  194. package/dist/modes/interactive/components/footer.js.map +1 -1
  195. package/dist/modes/interactive/components/index.d.ts +1 -0
  196. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  197. package/dist/modes/interactive/components/index.js +1 -0
  198. package/dist/modes/interactive/components/index.js.map +1 -1
  199. package/dist/modes/interactive/interactive-mode.d.ts +4 -0
  200. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  201. package/dist/modes/interactive/interactive-mode.js +132 -30
  202. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  203. package/dist/modes/print-mode.d.ts.map +1 -1
  204. package/dist/modes/print-mode.js +53 -6
  205. package/dist/modes/print-mode.js.map +1 -1
  206. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  207. package/dist/modes/rpc/rpc-mode.js +3 -0
  208. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  209. package/docs/compaction.md +1 -1
  210. package/docs/custom-provider.md +2 -2
  211. package/docs/development.md +2 -2
  212. package/docs/docs.json +2 -2
  213. package/docs/extensions.md +18 -13
  214. package/docs/providers.md +5 -1
  215. package/docs/quickstart.md +5 -3
  216. package/docs/rpc.md +5 -5
  217. package/docs/sdk.md +12 -12
  218. package/docs/settings.md +18 -0
  219. package/docs/themes.md +6 -6
  220. package/docs/tui.md +20 -18
  221. package/docs/usage.md +2 -0
  222. package/docs/workflows.md +403 -39
  223. package/examples/extensions/qna.ts +2 -2
  224. package/package.json +4 -4
  225. package/dist/builtin/subagents/skills/playwright-cli/SKILL.md +0 -392
  226. package/dist/builtin/subagents/skills/playwright-cli/references/element-attributes.md +0 -23
  227. package/dist/builtin/subagents/skills/playwright-cli/references/playwright-tests.md +0 -39
  228. package/dist/builtin/subagents/skills/playwright-cli/references/request-mocking.md +0 -87
  229. package/dist/builtin/subagents/skills/playwright-cli/references/running-code.md +0 -241
  230. package/dist/builtin/subagents/skills/playwright-cli/references/session-management.md +0 -225
  231. package/dist/builtin/subagents/skills/playwright-cli/references/spec-driven-testing.md +0 -305
  232. package/dist/builtin/subagents/skills/playwright-cli/references/storage-state.md +0 -275
  233. package/dist/builtin/subagents/skills/playwright-cli/references/test-generation.md +0 -134
  234. package/dist/builtin/subagents/skills/playwright-cli/references/tracing.md +0 -139
  235. package/dist/builtin/subagents/skills/playwright-cli/references/video-recording.md +0 -143
@@ -71,11 +71,47 @@ export function parseSkillBlock(text) {
71
71
  userMessage: afterClosing ? afterClosing.slice(2).trim() || undefined : undefined,
72
72
  };
73
73
  }
74
+ function drainAgentMessageQueue(queue) {
75
+ if (!queue)
76
+ return [];
77
+ const drained = [];
78
+ while (queue.hasItems()) {
79
+ drained.push(...queue.drain());
80
+ }
81
+ return drained;
82
+ }
83
+ function normalizeInterruptAbortMessage(value) {
84
+ const trimmed = value?.trim();
85
+ return trimmed && trimmed.length > 0 ? trimmed : undefined;
86
+ }
87
+ function isGenericAbortText(value) {
88
+ const normalized = value.trim().toLowerCase().replace(/[.!]+$/, "");
89
+ return (normalized === "operation aborted" ||
90
+ normalized === "the operation was aborted" ||
91
+ normalized === "request was aborted" ||
92
+ normalized === "this operation was aborted" ||
93
+ normalized === "extension custom ui aborted");
94
+ }
95
+ function isSingleGenericAbortTextContent(content) {
96
+ return (Array.isArray(content) &&
97
+ content.length === 1 &&
98
+ typeof content[0] === "object" &&
99
+ content[0] !== null &&
100
+ content[0].type === "text" &&
101
+ typeof content[0].text === "string" &&
102
+ isGenericAbortText(content[0].text));
103
+ }
104
+ function replacementAbortContent(text) {
105
+ return [{ type: "text", text }];
106
+ }
74
107
  // ============================================================================
75
108
  // Constants
76
109
  // ============================================================================
77
110
  /** Standard thinking levels */
78
111
  const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high"];
112
+ function customMessageExcludesContext(message) {
113
+ return message.excludeFromContext === true;
114
+ }
79
115
  // ============================================================================
80
116
  // AgentSession Class
81
117
  // ============================================================================
@@ -87,6 +123,14 @@ export class AgentSession {
87
123
  this._steeringMessages = [];
88
124
  /** Tracks pending follow-up messages for UI display. Removed when delivered. */
89
125
  this._followUpMessages = [];
126
+ /** Serializes interrupt custom-message delivery so only one immediate prompt runs at a time. */
127
+ this._interruptDeliveryQueue = Promise.resolve();
128
+ /** Number of interrupt custom messages enqueued or currently delivering. */
129
+ this._pendingInterruptDeliveries = 0;
130
+ /** Queues held out of pi-agent-core while an interrupt sequence is active. */
131
+ this._activeInterruptQueueHold = undefined;
132
+ /** Replacement text for generic abort results produced by an interrupt-delivered custom message. */
133
+ this._activeInterruptAbortMessage = undefined;
90
134
  /** Messages queued to be included with the next user prompt as context ("asides"). */
91
135
  this._pendingNextTurnMessages = [];
92
136
  // Compaction state
@@ -150,6 +194,10 @@ export class AgentSession {
150
194
  includeAllExtensionTools: true,
151
195
  });
152
196
  }
197
+ /** Orchestration context for this session, when owned by a workflow/subagent runtime. */
198
+ get orchestrationContext() {
199
+ return this._orchestrationContext;
200
+ }
153
201
  /** Model registry for API key resolution and model discovery */
154
202
  get modelRegistry() {
155
203
  return this._modelRegistry;
@@ -291,6 +339,7 @@ export class AgentSession {
291
339
  }
292
340
  }
293
341
  }
342
+ this._applyInterruptAbortMessage(event);
294
343
  // Emit to extensions first
295
344
  await this._emitExtensionEvent(event);
296
345
  // Notify all listeners
@@ -300,7 +349,7 @@ export class AgentSession {
300
349
  // Check if this is a custom message from extensions
301
350
  if (event.message.role === "custom") {
302
351
  // Persist as CustomMessageEntry
303
- this.sessionManager.appendCustomMessageEntry(event.message.customType, event.message.content, event.message.display, event.message.details);
352
+ this.sessionManager.appendCustomMessageEntry(event.message.customType, event.message.content, event.message.display, event.message.details, customMessageExcludesContext(event.message));
304
353
  }
305
354
  else if (event.message.role === "user" ||
306
355
  event.message.role === "assistant" ||
@@ -342,6 +391,27 @@ export class AgentSession {
342
391
  await this._checkCompaction(msg);
343
392
  }
344
393
  }
394
+ _applyInterruptAbortMessage(event) {
395
+ const abortMessage = this._activeInterruptAbortMessage;
396
+ if (!abortMessage)
397
+ return;
398
+ if (event.type === "tool_execution_end" && event.isError && isSingleGenericAbortTextContent(event.result.content)) {
399
+ event.result.content = replacementAbortContent(abortMessage);
400
+ return;
401
+ }
402
+ if (event.type !== "message_start" && event.type !== "message_end")
403
+ return;
404
+ if (event.message.role === "toolResult" && event.message.isError && isSingleGenericAbortTextContent(event.message.content)) {
405
+ event.message.content = replacementAbortContent(abortMessage);
406
+ return;
407
+ }
408
+ if (event.message.role === "assistant") {
409
+ const assistantMessage = event.message;
410
+ if (assistantMessage.stopReason === "aborted") {
411
+ assistantMessage.errorMessage = abortMessage;
412
+ }
413
+ }
414
+ }
345
415
  /** Resolve the pending retry promise */
346
416
  _resolveRetry() {
347
417
  if (this._retryResolve) {
@@ -952,11 +1022,11 @@ export class AgentSession {
952
1022
  if (images) {
953
1023
  content.push(...images);
954
1024
  }
955
- this.agent.steer({
1025
+ this._queueAgentMessage({
956
1026
  role: "user",
957
1027
  content,
958
1028
  timestamp: Date.now(),
959
- });
1029
+ }, "steer");
960
1030
  }
961
1031
  /**
962
1032
  * Internal: Queue a follow-up message (already expanded, no extension command check).
@@ -968,11 +1038,11 @@ export class AgentSession {
968
1038
  if (images) {
969
1039
  content.push(...images);
970
1040
  }
971
- this.agent.followUp({
1041
+ this._queueAgentMessage({
972
1042
  role: "user",
973
1043
  content,
974
1044
  timestamp: Date.now(),
975
- });
1045
+ }, "followUp");
976
1046
  }
977
1047
  /**
978
1048
  * Throw an error if the text is an extension command.
@@ -988,14 +1058,16 @@ export class AgentSession {
988
1058
  /**
989
1059
  * Send a custom message to the session. Creates a CustomMessageEntry.
990
1060
  *
991
- * Handles three cases:
992
- * - Streaming: queues message, processed when loop pulls from queue
1061
+ * Handles five cases:
1062
+ * - Streaming + interrupt trigger: aborts the active run and starts an immediate custom-message turn
1063
+ * - Streaming + explicit display-only context exclusion: appends to state/session, no turn and no queue
1064
+ * - Streaming otherwise: queues message, processed when loop pulls from queue
993
1065
  * - Not streaming + triggerTurn: appends to state/session, starts new turn
994
1066
  * - Not streaming + no trigger: appends to state/session, no turn
995
1067
  *
996
1068
  * @param message Custom message with customType, content, display, details
997
1069
  * @param options.triggerTurn If true and not streaming, triggers a new LLM turn
998
- * @param options.deliverAs Delivery mode: "steer", "followUp", or "nextTurn"
1070
+ * @param options.deliverAs Delivery mode: "steer", "followUp", "nextTurn", or "interrupt"
999
1071
  */
1000
1072
  async sendCustomMessage(message, options) {
1001
1073
  const appMessage = {
@@ -1005,26 +1077,129 @@ export class AgentSession {
1005
1077
  display: message.display,
1006
1078
  details: message.details,
1007
1079
  timestamp: Date.now(),
1080
+ ...(options?.excludeFromContext === true ? { excludeFromContext: true } : {}),
1008
1081
  };
1009
1082
  if (options?.deliverAs === "nextTurn") {
1010
1083
  this._pendingNextTurnMessages.push(appMessage);
1011
1084
  }
1085
+ else if (options?.deliverAs === "interrupt" && options.triggerTurn) {
1086
+ await this._enqueueInterruptCustomMessage(appMessage, options);
1087
+ }
1088
+ else if (this.isStreaming && options?.excludeFromContext === true && options.triggerTurn !== true && options.deliverAs === undefined) {
1089
+ this._appendCustomMessage(appMessage);
1090
+ }
1012
1091
  else if (this.isStreaming) {
1013
- if (options?.deliverAs === "followUp") {
1014
- this.agent.followUp(appMessage);
1092
+ this._queueAgentMessage(appMessage, options?.deliverAs === "followUp" ? "followUp" : "steer");
1093
+ }
1094
+ else if (options?.triggerTurn) {
1095
+ await this.agent.prompt(appMessage);
1096
+ }
1097
+ else {
1098
+ this._appendCustomMessage(appMessage);
1099
+ }
1100
+ }
1101
+ _appendCustomMessage(message) {
1102
+ this.agent.state.messages.push(message);
1103
+ this.sessionManager.appendCustomMessageEntry(message.customType, message.content, message.display, message.details, customMessageExcludesContext(message));
1104
+ this._emit({ type: "message_start", message });
1105
+ this._emit({ type: "message_end", message });
1106
+ }
1107
+ _enqueueInterruptCustomMessage(message, options) {
1108
+ this._pendingInterruptDeliveries += 1;
1109
+ // Establish the hold synchronously when the interrupt is enqueued, not when
1110
+ // the serialized delivery callback later starts. Callers commonly fire and
1111
+ // forget sendCustomMessage(), then queue additional steer/follow-up messages
1112
+ // before the promise chain gets a microtask; those messages must be captured
1113
+ // in the active interrupt hold instead of pi-agent-core's live queues.
1114
+ this._ensureActiveInterruptQueueHold();
1115
+ const delivery = this._interruptDeliveryQueue.then(async () => {
1116
+ try {
1117
+ await this._sendInterruptCustomMessageNow(message, options);
1118
+ }
1119
+ finally {
1120
+ this._pendingInterruptDeliveries -= 1;
1121
+ if (this._pendingInterruptDeliveries === 0) {
1122
+ this._restoreAndClearActiveInterruptQueueHold();
1123
+ }
1124
+ }
1125
+ });
1126
+ this._interruptDeliveryQueue = delivery.catch(() => undefined);
1127
+ return delivery;
1128
+ }
1129
+ async _sendInterruptCustomMessageNow(message, options) {
1130
+ this.abortRetry();
1131
+ this._ensureActiveInterruptQueueHold();
1132
+ if (this.isStreaming) {
1133
+ const previousAbortMessage = this._activeInterruptAbortMessage;
1134
+ this._activeInterruptAbortMessage = normalizeInterruptAbortMessage(options?.interruptAbortMessage);
1135
+ try {
1136
+ this.agent.abort();
1137
+ await this.agent.waitForIdle();
1138
+ await this._agentEventQueue;
1139
+ }
1140
+ finally {
1141
+ this._activeInterruptAbortMessage = previousAbortMessage;
1142
+ }
1143
+ }
1144
+ await this.agent.prompt(message);
1145
+ }
1146
+ _ensureActiveInterruptQueueHold() {
1147
+ if (this._activeInterruptQueueHold !== undefined) {
1148
+ return this._activeInterruptQueueHold;
1149
+ }
1150
+ const drained = this._drainQueuedAgentMessages();
1151
+ this._activeInterruptQueueHold = {
1152
+ steering: [...drained.steering],
1153
+ followUp: [...drained.followUp],
1154
+ };
1155
+ return this._activeInterruptQueueHold;
1156
+ }
1157
+ _restoreAndClearActiveInterruptQueueHold() {
1158
+ const hold = this._activeInterruptQueueHold;
1159
+ if (hold === undefined) {
1160
+ return;
1161
+ }
1162
+ const currentCoreQueues = this._drainQueuedAgentMessages();
1163
+ this._restoreQueuedAgentMessages({
1164
+ steering: [...hold.steering, ...currentCoreQueues.steering],
1165
+ followUp: [...hold.followUp, ...currentCoreQueues.followUp],
1166
+ });
1167
+ this._activeInterruptQueueHold = undefined;
1168
+ }
1169
+ _queueAgentMessage(message, delivery) {
1170
+ const hold = this._activeInterruptQueueHold;
1171
+ if (hold !== undefined) {
1172
+ if (delivery === "followUp") {
1173
+ hold.followUp.push(message);
1015
1174
  }
1016
1175
  else {
1017
- this.agent.steer(appMessage);
1176
+ hold.steering.push(message);
1018
1177
  }
1178
+ return;
1019
1179
  }
1020
- else if (options?.triggerTurn) {
1021
- await this.agent.prompt(appMessage);
1180
+ if (delivery === "followUp") {
1181
+ this.agent.followUp(message);
1022
1182
  }
1023
1183
  else {
1024
- this.agent.state.messages.push(appMessage);
1025
- this.sessionManager.appendCustomMessageEntry(message.customType, message.content, message.display, message.details);
1026
- this._emit({ type: "message_start", message: appMessage });
1027
- this._emit({ type: "message_end", message: appMessage });
1184
+ this.agent.steer(message);
1185
+ }
1186
+ }
1187
+ _drainQueuedAgentMessages() {
1188
+ // pi-agent-core exposes public clear methods but no public drain/restore pair.
1189
+ // Interrupts need to prevent the aborting run from consuming queued steer/follow-up
1190
+ // messages while still preserving those queues for a later turn.
1191
+ const agentWithQueues = this.agent;
1192
+ return {
1193
+ steering: drainAgentMessageQueue(agentWithQueues.steeringQueue),
1194
+ followUp: drainAgentMessageQueue(agentWithQueues.followUpQueue),
1195
+ };
1196
+ }
1197
+ _restoreQueuedAgentMessages(queues) {
1198
+ for (const message of queues.steering) {
1199
+ this.agent.steer(message);
1200
+ }
1201
+ for (const message of queues.followUp) {
1202
+ this.agent.followUp(message);
1028
1203
  }
1029
1204
  }
1030
1205
  /**
@@ -1075,6 +1250,10 @@ export class AgentSession {
1075
1250
  this._steeringMessages = [];
1076
1251
  this._followUpMessages = [];
1077
1252
  this.agent.clearAllQueues();
1253
+ if (this._activeInterruptQueueHold !== undefined) {
1254
+ this._activeInterruptQueueHold.steering.length = 0;
1255
+ this._activeInterruptQueueHold.followUp.length = 0;
1256
+ }
1078
1257
  this._emitQueueUpdate();
1079
1258
  return { steering, followUp };
1080
1259
  }