@bastani/atomic 0.9.12 → 0.9.13-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +6 -0
  3. package/dist/builtin/intercom/README.md +5 -12
  4. package/dist/builtin/intercom/broker/client.ts +11 -2
  5. package/dist/builtin/intercom/contact-supervisor-tool.ts +13 -3
  6. package/dist/builtin/intercom/index-heavy.ts +13 -3
  7. package/dist/builtin/intercom/index.ts +43 -19
  8. package/dist/builtin/intercom/intercom-utils.ts +15 -29
  9. package/dist/builtin/intercom/package.json +1 -1
  10. package/dist/builtin/intercom/source-ownership.ts +9 -12
  11. package/dist/builtin/mcp/CHANGELOG.md +6 -0
  12. package/dist/builtin/mcp/README.md +3 -1
  13. package/dist/builtin/mcp/index.ts +10 -16
  14. package/dist/builtin/mcp/package.json +1 -1
  15. package/dist/builtin/mcp/startup-warmup.ts +20 -8
  16. package/dist/builtin/subagents/CHANGELOG.md +30 -0
  17. package/dist/builtin/subagents/README.md +22 -30
  18. package/dist/builtin/subagents/package.json +1 -1
  19. package/dist/builtin/subagents/src/extension/doctor.ts +1 -1
  20. package/dist/builtin/subagents/src/extension/fanout-child.ts +76 -56
  21. package/dist/builtin/subagents/src/extension/index.ts +23 -32
  22. package/dist/builtin/subagents/src/extension/startup-maintenance.ts +25 -44
  23. package/dist/builtin/subagents/src/intercom/result-intercom.ts +7 -7
  24. package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +166 -434
  25. package/dist/builtin/subagents/src/runs/background/notify.ts +29 -8
  26. package/dist/builtin/subagents/src/runs/foreground/chain-execution-dynamic-step.ts +2 -2
  27. package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-runner.ts +4 -4
  28. package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-step.ts +2 -2
  29. package/dist/builtin/subagents/src/runs/foreground/chain-execution-sequential-step.ts +2 -2
  30. package/dist/builtin/subagents/src/runs/foreground/execution-run-sync.ts +1 -281
  31. package/dist/builtin/subagents/src/runs/foreground/execution-structured-retries.ts +11 -75
  32. package/dist/builtin/subagents/src/runs/foreground/inprocess-run-sync.ts +457 -0
  33. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-async.ts +119 -246
  34. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-chain.ts +1 -1
  35. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-context.ts +18 -9
  36. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +1 -1
  37. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +4 -4
  38. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-resume.ts +1 -646
  39. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-runtime.ts +2 -7
  40. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +5 -5
  41. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-status.ts +7 -8
  42. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-types.ts +14 -5
  43. package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +158 -93
  44. package/dist/builtin/subagents/src/runs/inprocess/background-single.ts +179 -0
  45. package/dist/builtin/subagents/src/runs/inprocess/background.ts +89 -0
  46. package/dist/builtin/subagents/src/runs/inprocess/child-policy.ts +27 -0
  47. package/dist/builtin/subagents/src/runs/inprocess/control-registry.ts +55 -0
  48. package/dist/builtin/subagents/src/runs/inprocess/control-status.ts +106 -0
  49. package/dist/builtin/subagents/src/runs/inprocess/index.ts +50 -0
  50. package/dist/builtin/subagents/src/runs/inprocess/nested-routing.ts +218 -0
  51. package/dist/builtin/subagents/src/runs/{shared/subagent-prompt-runtime.ts → inprocess/prompt-behavior.ts} +37 -75
  52. package/dist/builtin/subagents/src/runs/inprocess/runner.ts +1138 -0
  53. package/dist/builtin/subagents/src/runs/{shared/nested-events.ts → inprocess/runtime-support/nested-api.ts} +13 -7
  54. package/dist/builtin/subagents/src/runs/{shared/nested-events-control.ts → inprocess/runtime-support/nested-control.ts} +18 -17
  55. package/dist/builtin/subagents/src/runs/{shared/nested-events-core.ts → inprocess/runtime-support/nested-core.ts} +14 -61
  56. package/dist/builtin/subagents/src/runs/{shared/nested-events-projection.ts → inprocess/runtime-support/nested-projection.ts} +4 -10
  57. package/dist/builtin/subagents/src/runs/{shared/nested-events-registry.ts → inprocess/runtime-support/nested-registry.ts} +19 -4
  58. package/dist/builtin/subagents/src/runs/{shared/nested-render.ts → inprocess/runtime-support/nested-rendering.ts} +3 -3
  59. package/dist/builtin/subagents/src/runs/{shared/nested-events-sanitize.ts → inprocess/runtime-support/nested-sanitize.ts} +3 -3
  60. package/dist/builtin/subagents/src/runs/inprocess.ts +1 -0
  61. package/dist/builtin/subagents/src/runs/shared/dynamic-fanout.ts +10 -4
  62. package/dist/builtin/subagents/src/runs/shared/model-candidate-filter.ts +0 -1
  63. package/dist/builtin/subagents/src/runs/shared/model-fallback.ts +15 -492
  64. package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +13 -11
  65. package/dist/builtin/subagents/src/runs/shared/run-history.ts +3 -5
  66. package/dist/builtin/subagents/src/runs/shared/single-output.ts +4 -4
  67. package/dist/builtin/subagents/src/runs/shared/workflow-graph.ts +5 -5
  68. package/dist/builtin/subagents/src/shared/types-async.ts +0 -1
  69. package/dist/builtin/subagents/src/shared/types-config.ts +18 -0
  70. package/dist/builtin/subagents/src/shared/types-results.ts +9 -2
  71. package/dist/builtin/subagents/src/slash/prompt-template-bridge.ts +2 -3
  72. package/dist/builtin/subagents/src/slash/slash-live-state.ts +2 -2
  73. package/dist/builtin/subagents/src/tui/render-chain-graph.ts +10 -4
  74. package/dist/builtin/subagents/src/tui/render-event-formatting.ts +12 -11
  75. package/dist/builtin/subagents/src/tui/render-result-compact.ts +49 -12
  76. package/dist/builtin/subagents/src/tui/render-result.ts +46 -17
  77. package/dist/builtin/subagents/src/tui/render-stable-output.ts +1 -1
  78. package/dist/builtin/subagents/src/tui/render-status-progress.ts +8 -5
  79. package/dist/builtin/subagents/src/tui/render-widget-graph.ts +0 -8
  80. package/dist/builtin/subagents/src/tui/render-widget.ts +0 -24
  81. package/dist/builtin/web-access/package.json +1 -1
  82. package/dist/builtin/workflows/CHANGELOG.md +25 -0
  83. package/dist/builtin/workflows/README.md +13 -3
  84. package/dist/builtin/workflows/package.json +1 -1
  85. package/dist/builtin/workflows/src/durable/backend.ts +7 -0
  86. package/dist/builtin/workflows/src/durable/dbos-metadata.ts +11 -1
  87. package/dist/builtin/workflows/src/durable/resume-runtime.ts +1 -0
  88. package/dist/builtin/workflows/src/durable/tool-primitive.ts +4 -28
  89. package/dist/builtin/workflows/src/durable/types.ts +5 -0
  90. package/dist/builtin/workflows/src/engine/run.ts +17 -1
  91. package/dist/builtin/workflows/src/extension/atomic-stage-session.ts +12 -39
  92. package/dist/builtin/workflows/src/extension/config-file-loader.ts +10 -1
  93. package/dist/builtin/workflows/src/extension/config-loader.ts +1 -1
  94. package/dist/builtin/workflows/src/extension/dispatcher.ts +4 -0
  95. package/dist/builtin/workflows/src/extension/index.bundle.mjs +1151 -737
  96. package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +303 -38
  97. package/dist/builtin/workflows/src/extension/render-result.ts +4 -0
  98. package/dist/builtin/workflows/src/extension/runtime-durable-resume.ts +6 -3
  99. package/dist/builtin/workflows/src/extension/runtime.ts +7 -1
  100. package/dist/builtin/workflows/src/extension/wiring.ts +53 -11
  101. package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +12 -3
  102. package/dist/builtin/workflows/src/extension/workflow-durable-resume-command.ts +5 -2
  103. package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +9 -7
  104. package/dist/builtin/workflows/src/extension/workflow-status-summary.ts +11 -0
  105. package/dist/builtin/workflows/src/extension/workflow-tool-content.ts +18 -1
  106. package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +10 -8
  107. package/dist/builtin/workflows/src/extension/workflow-tool.ts +9 -4
  108. package/dist/builtin/workflows/src/runs/background/quit.ts +28 -4
  109. package/dist/builtin/workflows/src/runs/background/resume-acknowledgements.ts +2 -1
  110. package/dist/builtin/workflows/src/runs/background/status.ts +55 -8
  111. package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +4 -0
  112. package/dist/builtin/workflows/src/runs/foreground/executor-prompt-nodes.ts +3 -1
  113. package/dist/builtin/workflows/src/runs/foreground/executor-stage-control.ts +1 -1
  114. package/dist/builtin/workflows/src/runs/foreground/stage-runner-context.ts +1 -1
  115. package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +688 -38
  116. package/dist/builtin/workflows/src/runs/foreground/stage-runner-types.ts +3 -1
  117. package/dist/builtin/workflows/src/runs/shared/model-fallback-failures.ts +19 -514
  118. package/dist/builtin/workflows/src/runs/shared/retry.ts +44 -0
  119. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +5 -0
  120. package/dist/builtin/workflows/src/shared/authoring-contract-stage.ts +6 -0
  121. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +5 -1
  122. package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +5 -0
  123. package/dist/builtin/workflows/src/shared/persistence-restore-helpers.ts +5 -0
  124. package/dist/builtin/workflows/src/shared/persistence-restore.ts +3 -0
  125. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +4 -0
  126. package/dist/builtin/workflows/src/shared/run-indicator-status.ts +75 -0
  127. package/dist/builtin/workflows/src/shared/store-public-types.ts +43 -7
  128. package/dist/builtin/workflows/src/shared/store-run-methods.ts +42 -8
  129. package/dist/builtin/workflows/src/shared/store-stage-methods.ts +31 -8
  130. package/dist/builtin/workflows/src/shared/store-types.ts +34 -0
  131. package/dist/builtin/workflows/src/shared/timing.ts +10 -0
  132. package/dist/builtin/workflows/src/tui/chat-surface-message.ts +22 -2
  133. package/dist/builtin/workflows/src/tui/session-list.ts +1 -1
  134. package/dist/builtin/workflows/src/tui/session-overlays.ts +14 -10
  135. package/dist/builtin/workflows/src/tui/session-picker.ts +20 -4
  136. package/dist/builtin/workflows/src/tui/status-list.ts +31 -47
  137. package/dist/builtin/workflows/src/tui/store-widget-installer.ts +9 -1
  138. package/dist/builtin/workflows/src/tui/widget.ts +16 -46
  139. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  140. package/dist/core/agent-session-auto-compaction.js +54 -11
  141. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  142. package/dist/core/agent-session-events.d.ts.map +1 -1
  143. package/dist/core/agent-session-events.js +49 -14
  144. package/dist/core/agent-session-events.js.map +1 -1
  145. package/dist/core/agent-session-methods.d.ts +13 -0
  146. package/dist/core/agent-session-methods.d.ts.map +1 -1
  147. package/dist/core/agent-session-methods.js.map +1 -1
  148. package/dist/core/agent-session-models.d.ts.map +1 -1
  149. package/dist/core/agent-session-models.js +12 -1
  150. package/dist/core/agent-session-models.js.map +1 -1
  151. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  152. package/dist/core/agent-session-prompt.js +4 -0
  153. package/dist/core/agent-session-prompt.js.map +1 -1
  154. package/dist/core/agent-session-retry.d.ts +21 -4
  155. package/dist/core/agent-session-retry.d.ts.map +1 -1
  156. package/dist/core/agent-session-retry.js +232 -106
  157. package/dist/core/agent-session-retry.js.map +1 -1
  158. package/dist/core/agent-session-state.d.ts.map +1 -1
  159. package/dist/core/agent-session-state.js +2 -1
  160. package/dist/core/agent-session-state.js.map +1 -1
  161. package/dist/core/agent-session-tool-hooks.d.ts.map +1 -1
  162. package/dist/core/agent-session-tool-hooks.js +12 -0
  163. package/dist/core/agent-session-tool-hooks.js.map +1 -1
  164. package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
  165. package/dist/core/agent-session-tool-registry.js +1 -1
  166. package/dist/core/agent-session-tool-registry.js.map +1 -1
  167. package/dist/core/agent-session-types.d.ts +3 -1
  168. package/dist/core/agent-session-types.d.ts.map +1 -1
  169. package/dist/core/agent-session-types.js.map +1 -1
  170. package/dist/core/agent-session.d.ts +12 -1
  171. package/dist/core/agent-session.d.ts.map +1 -1
  172. package/dist/core/agent-session.js +7 -0
  173. package/dist/core/agent-session.js.map +1 -1
  174. package/dist/core/extensions/context-types.d.ts +25 -0
  175. package/dist/core/extensions/context-types.d.ts.map +1 -1
  176. package/dist/core/extensions/context-types.js.map +1 -1
  177. package/dist/core/extensions/index.d.ts +1 -1
  178. package/dist/core/extensions/index.d.ts.map +1 -1
  179. package/dist/core/extensions/index.js.map +1 -1
  180. package/dist/core/extensions/runner-context.d.ts +2 -1
  181. package/dist/core/extensions/runner-context.d.ts.map +1 -1
  182. package/dist/core/extensions/runner-context.js +4 -0
  183. package/dist/core/extensions/runner-context.js.map +1 -1
  184. package/dist/core/extensions/runner.d.ts +3 -2
  185. package/dist/core/extensions/runner.d.ts.map +1 -1
  186. package/dist/core/extensions/runner.js +3 -1
  187. package/dist/core/extensions/runner.js.map +1 -1
  188. package/dist/core/model-fallback-failures.d.ts +30 -0
  189. package/dist/core/model-fallback-failures.d.ts.map +1 -0
  190. package/dist/core/model-fallback-failures.js +610 -0
  191. package/dist/core/model-fallback-failures.js.map +1 -0
  192. package/dist/core/retry-policy.d.ts +30 -0
  193. package/dist/core/retry-policy.d.ts.map +1 -0
  194. package/dist/core/retry-policy.js +25 -0
  195. package/dist/core/retry-policy.js.map +1 -0
  196. package/dist/core/sdk-types.d.ts +8 -2
  197. package/dist/core/sdk-types.d.ts.map +1 -1
  198. package/dist/core/sdk-types.js.map +1 -1
  199. package/dist/core/sdk.d.ts.map +1 -1
  200. package/dist/core/sdk.js +6 -1
  201. package/dist/core/sdk.js.map +1 -1
  202. package/dist/core/session-manager-core.d.ts +2 -0
  203. package/dist/core/session-manager-core.d.ts.map +1 -1
  204. package/dist/core/session-manager-core.js +7 -0
  205. package/dist/core/session-manager-core.js.map +1 -1
  206. package/dist/index-extensions.d.ts +1 -1
  207. package/dist/index-extensions.d.ts.map +1 -1
  208. package/dist/index-extensions.js.map +1 -1
  209. package/dist/index.d.ts +3 -0
  210. package/dist/index.d.ts.map +1 -1
  211. package/dist/index.js +2 -0
  212. package/dist/index.js.map +1 -1
  213. package/docs/intercom.md +5 -10
  214. package/docs/settings.md +7 -1
  215. package/docs/subagents.md +19 -19
  216. package/docs/workflows.md +21 -5
  217. package/examples/extensions/gondolin/package-lock.json +3 -3
  218. package/npm-shrinkwrap.json +29 -29
  219. package/package.json +2 -2
  220. package/dist/builtin/subagents/src/runs/background/async-event-journal.ts +0 -112
  221. package/dist/builtin/subagents/src/runs/background/async-execution-chain.ts +0 -543
  222. package/dist/builtin/subagents/src/runs/background/async-execution-common.ts +0 -140
  223. package/dist/builtin/subagents/src/runs/background/async-execution-single.ts +0 -279
  224. package/dist/builtin/subagents/src/runs/background/async-execution-types.ts +0 -99
  225. package/dist/builtin/subagents/src/runs/background/async-execution.ts +0 -17
  226. package/dist/builtin/subagents/src/runs/background/async-resume.ts +0 -402
  227. package/dist/builtin/subagents/src/runs/background/async-status.ts +0 -411
  228. package/dist/builtin/subagents/src/runs/background/completion-claims.ts +0 -196
  229. package/dist/builtin/subagents/src/runs/background/completion-dedupe.ts +0 -102
  230. package/dist/builtin/subagents/src/runs/background/parallel-groups.ts +0 -59
  231. package/dist/builtin/subagents/src/runs/background/result-delivery-processor.ts +0 -300
  232. package/dist/builtin/subagents/src/runs/background/result-file-claims.ts +0 -166
  233. package/dist/builtin/subagents/src/runs/background/result-quarantine.ts +0 -77
  234. package/dist/builtin/subagents/src/runs/background/result-retry-scheduler.ts +0 -48
  235. package/dist/builtin/subagents/src/runs/background/result-status.ts +0 -87
  236. package/dist/builtin/subagents/src/runs/background/result-watcher-data.ts +0 -72
  237. package/dist/builtin/subagents/src/runs/background/result-watcher.ts +0 -320
  238. package/dist/builtin/subagents/src/runs/background/run-id-resolver.ts +0 -105
  239. package/dist/builtin/subagents/src/runs/background/run-status.ts +0 -358
  240. package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +0 -457
  241. package/dist/builtin/subagents/src/runs/background/subagent-runner-dynamic.ts +0 -376
  242. package/dist/builtin/subagents/src/runs/background/subagent-runner-finalize.ts +0 -150
  243. package/dist/builtin/subagents/src/runs/background/subagent-runner-output.ts +0 -109
  244. package/dist/builtin/subagents/src/runs/background/subagent-runner-parallel-helpers.ts +0 -128
  245. package/dist/builtin/subagents/src/runs/background/subagent-runner-parallel.ts +0 -309
  246. package/dist/builtin/subagents/src/runs/background/subagent-runner-sequential.ts +0 -173
  247. package/dist/builtin/subagents/src/runs/background/subagent-runner-state.ts +0 -578
  248. package/dist/builtin/subagents/src/runs/background/subagent-runner-step.ts +0 -330
  249. package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +0 -330
  250. package/dist/builtin/subagents/src/runs/background/subagent-runner-types.ts +0 -210
  251. package/dist/builtin/subagents/src/runs/background/subagent-runner-utils.ts +0 -69
  252. package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +0 -90
  253. package/dist/builtin/subagents/src/runs/background/top-level-async.ts +0 -12
  254. package/dist/builtin/subagents/src/runs/foreground/execution-attempt-control.ts +0 -140
  255. package/dist/builtin/subagents/src/runs/foreground/execution-attempt-finalize.ts +0 -108
  256. package/dist/builtin/subagents/src/runs/foreground/execution-attempt-types.ts +0 -17
  257. package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +0 -558
  258. package/dist/builtin/subagents/src/runs/shared/attempt-watchdog.ts +0 -126
  259. package/dist/builtin/subagents/src/runs/shared/final-drain.ts +0 -39
  260. package/dist/builtin/subagents/src/runs/shared/pi-args.ts +0 -339
  261. package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +0 -165
  262. package/dist/builtin/subagents/src/runs/shared/spawn-env.ts +0 -21
  263. package/dist/builtin/subagents/src/shared/post-exit-stdio-guard.ts +0 -86
  264. /package/dist/builtin/subagents/src/runs/{shared/nested-path.ts → inprocess/runtime-support/nested-paths.ts} +0 -0
@@ -60,6 +60,8 @@ export interface RunSyncOptions {
60
60
  signal?: AbortSignal;
61
61
  interruptSignal?: AbortSignal;
62
62
  allowIntercomDetach?: boolean;
63
+ /** Start the admitted child and settle this call with a continued result immediately. */
64
+ backgroundContinuation?: boolean;
63
65
  intercomEvents?: IntercomEventBus;
64
66
  onDetachedExit?: (result: SingleResult) => void;
65
67
  /** Shared foreground-group signal used to release sibling supervision after one exact child commits Intercom detach. */
@@ -71,6 +73,12 @@ export interface RunSyncOptions {
71
73
  controlConfig?: ResolvedControlConfig;
72
74
  intercomSessionName?: string;
73
75
  orchestratorIntercomTarget?: string;
76
+ /** Typed supervisor capability issued for this child; never read from environment. */
77
+ supervisorAuthorization?: {
78
+ capability: string;
79
+ supervisorSessionId: string;
80
+ childName: string;
81
+ };
74
82
  /** Resolved intercom home group for the spawned child (explicit subagent group or inherited stage group). */
75
83
  intercomGroup?: string;
76
84
  maxOutput?: MaxOutputConfig;
@@ -106,6 +114,16 @@ export interface RunSyncOptions {
106
114
  schemaPath: string;
107
115
  outputPath: string;
108
116
  };
117
+ /** Test-only in-process session stub configuration; production runs create a real AgentSession. */
118
+ testSession?: {
119
+ output?: string;
120
+ structuredOutputAfterPrompt?: number;
121
+ promptLogPath?: string;
122
+ /** Hold a test prompt open until the caller releases the supplied promise. */
123
+ promptGate?: Promise<void>;
124
+ /** Match AgentSession.abort() settling an active prompt without throwing. */
125
+ abortResolvesPrompt?: boolean;
126
+ };
109
127
  }
110
128
 
111
129
  export type IntercomBridgeMode = "off" | "fork-only" | "always";
@@ -2,6 +2,7 @@
2
2
  * Result, progress, and core subagent public types.
3
3
  */
4
4
 
5
+ import type { SessionStats } from "@bastani/atomic";
5
6
  import type { AgentToolResult } from "@earendil-works/pi-agent-core";
6
7
  import type { Message } from "@earendil-works/pi-ai/compat";
7
8
  import type { NestedRunAddress, NestedRunSummary, NestedStepSummary } from "./types-async.ts";
@@ -271,15 +272,21 @@ export interface ModelAttempt {
271
272
  model: string;
272
273
  reasoningLevel?: string;
273
274
  success: boolean;
274
- exitCode?: number | null;
275
275
  error?: string;
276
276
  usage?: Usage;
277
277
  }
278
278
 
279
+ export type SubagentAttemptStatus = "ok" | "error" | "skipped" | "interrupted" | "continued";
280
+
279
281
  export interface SingleResult {
280
282
  agent: string;
281
283
  task: string;
282
- exitCode: number;
284
+ /** Typed terminal outcome; this is the only result discriminator. */
285
+ status: SubagentAttemptStatus;
286
+ cause?: string;
287
+ stats?: SessionStats;
288
+ path?: string;
289
+ envelope?: string;
283
290
  detached?: boolean;
284
291
  detachedReason?: string;
285
292
  interrupted?: boolean;
@@ -77,9 +77,9 @@ interface PromptTemplateBridgeResult {
77
77
  details?: {
78
78
  results?: Array<{
79
79
  agent?: string;
80
+ status?: "ok" | "error" | "skipped" | "interrupted" | "continued";
80
81
  messages?: unknown[];
81
82
  finalOutput?: string;
82
- exitCode?: number;
83
83
  error?: string;
84
84
  model?: string;
85
85
  toolCalls?: Array<{ text?: string; expandedText?: string }>;
@@ -367,12 +367,11 @@ export function registerPromptTemplateDelegationBridge<Ctx extends { cwd?: strin
367
367
  errorText: "Missing result for delegated parallel task.",
368
368
  };
369
369
  }
370
- const exitCode = typeof step.exitCode === "number" ? step.exitCode : undefined;
371
370
  const errorText = step.error;
372
371
  return {
373
372
  agent: step.agent ?? task.agent,
374
373
  messages: buildDelegationMessages(step),
375
- isError: (exitCode !== undefined && exitCode !== 0) || !!errorText,
374
+ isError: step.status === "error" || !!errorText,
376
375
  errorText: errorText || undefined,
377
376
  };
378
377
  })
@@ -79,7 +79,7 @@ function createPlaceholderResult(
79
79
  return {
80
80
  agent,
81
81
  task,
82
- exitCode: 0,
82
+ status: "continued",
83
83
  messages: EMPTY_MESSAGES,
84
84
  usage: cloneUsage(),
85
85
  progress: {
@@ -292,7 +292,7 @@ export function failSlashResult(
292
292
  const initial = buildSlashInitialResult(requestId, params, owner).result;
293
293
  const failedResults = initial.details.results.map((result) => ({
294
294
  ...result,
295
- exitCode: 1,
295
+ status: "error" as const,
296
296
  error: message,
297
297
  progress: result.progress ? { ...result.progress, status: "failed" as const } : result.progress,
298
298
  }));
@@ -105,8 +105,8 @@ export function isDoneResult(result: Details["results"][number]): boolean {
105
105
  const status = result.progress?.status;
106
106
  if (status === "completed") return true;
107
107
  if (status === "running" || status === "pending") return false;
108
- if (result.interrupted || result.detached) return false;
109
- return result.exitCode === 0;
108
+ if (result.interrupted || result.detached || result.status !== "ok") return false;
109
+ return true;
110
110
  }
111
111
 
112
112
  export function workflowGraphHasStatus(
@@ -188,7 +188,7 @@ export function buildMultiProgressLabel(
188
188
  if (details.mode === "parallel") {
189
189
  const totalCount = details.totalSteps ?? details.results.length;
190
190
  const statuses = new Array(totalCount).fill("pending") as Array<
191
- "pending" | "running" | "completed" | "failed" | "detached"
191
+ "pending" | "running" | "completed" | "failed" | "paused" | "detached"
192
192
  >;
193
193
  for (const progress of details.progress ?? []) {
194
194
  if (progress.index >= 0 && progress.index < totalCount) statuses[progress.index] = progress.status;
@@ -202,7 +202,13 @@ export function buildMultiProgressLabel(
202
202
  if (index < 0 || index >= totalCount) continue;
203
203
  const status =
204
204
  result.progress?.status ??
205
- (result.interrupted || result.detached ? "detached" : result.exitCode === 0 ? "completed" : "failed");
205
+ (result.interrupted || result.status === "interrupted"
206
+ ? "paused"
207
+ : result.detached || result.status === "continued"
208
+ ? "detached"
209
+ : result.status === "ok"
210
+ ? "completed"
211
+ : "failed");
206
212
  statuses[index] = status;
207
213
  }
208
214
  const running = statuses.filter((status) => status === "running").length;
@@ -1,6 +1,4 @@
1
- import * as path from "node:path";
2
- import { flatToLogicalStepIndex } from "../runs/background/parallel-groups.ts";
3
- import { formatNestedAggregate } from "../runs/shared/nested-render.ts";
1
+ import { formatNestedAggregate } from "../runs/inprocess/runtime-support/nested-rendering.ts";
4
2
  import { formatDuration, formatModelThinking, shortenPath } from "../shared/formatters.ts";
5
3
  import { formatAgentRunningLabel } from "../shared/status-format.ts";
6
4
  import {
@@ -19,6 +17,17 @@ import {
19
17
  statJoin,
20
18
  } from "./render-status-progress.ts";
21
19
 
20
+ function flatToLogicalStepIndex(
21
+ currentStep: number,
22
+ _total: number,
23
+ groups: Array<{ start: number; count: number; stepIndex?: number }>,
24
+ ): number {
25
+ return (
26
+ groups.find((group) => currentStep >= group.start && currentStep < group.start + group.count)?.stepIndex ??
27
+ currentStep
28
+ );
29
+ }
30
+
22
31
  export function formatWidgetAgents(agents: string[]): string {
23
32
  const distinct = [...new Set(agents)];
24
33
  if (distinct.length === 1 && agents.length > 1) return `${distinct[0]} ×${agents.length}`;
@@ -167,14 +176,6 @@ export function widgetStepActivityLine(
167
176
  return "";
168
177
  }
169
178
 
170
- export function widgetOutputPath(
171
- job: AsyncJobState,
172
- step: NonNullable<AsyncJobState["steps"]>[number],
173
- ): string | undefined {
174
- if (typeof step.index !== "number") return undefined;
175
- return path.join(job.asyncDir, `output-${step.index}.log`);
176
- }
177
-
178
179
  export function nestedRunName(run: NestedRunSummary): string {
179
180
  if (run.agent) return run.agent;
180
181
  if (run.agents?.length) return formatWidgetAgents(run.agents);
@@ -73,7 +73,7 @@ export function renderSingleCompact(
73
73
 
74
74
  c.addChild(new Text(truncLine(theme.fg("dim", ` ⎿ ${resultStatusLine(r, output)}`), width), 0, 0));
75
75
  const preview = firstOutputLine(output);
76
- if (preview && r.exitCode === 0 && !hasEmptyTextOutputWithoutOutputTarget(r.task, output)) {
76
+ if (preview && r.status === "ok" && !hasEmptyTextOutputWithoutOutputTarget(r.task, output)) {
77
77
  c.addChild(new Text(truncLine(theme.fg("dim", ` ${preview}`), width), 0, 0));
78
78
  }
79
79
  if (r.sessionFile)
@@ -95,11 +95,18 @@ export function renderMultiCompact(d: Details, theme: Theme, now?: number, pulse
95
95
  d.results.some((r) => r.progress?.status === "running") ||
96
96
  workflowGraphHasStatus(d, ["running"]);
97
97
  const failed =
98
- d.results.some((r) => r.exitCode !== 0 && r.progress?.status !== "running") ||
98
+ d.results.some((r) => r.status === "error" && r.progress?.status !== "running") ||
99
99
  workflowGraphHasStatus(d, ["failed"]);
100
100
  const paused =
101
- d.results.some((r) => (r.interrupted || r.detached) && r.progress?.status !== "running") ||
102
- workflowGraphHasStatus(d, ["paused", "detached"]);
101
+ d.results.some(
102
+ (r) =>
103
+ (r.interrupted ||
104
+ r.detached ||
105
+ r.status === "interrupted" ||
106
+ r.status === "continued" ||
107
+ r.status === "skipped") &&
108
+ r.progress?.status !== "running",
109
+ ) || workflowGraphHasStatus(d, ["paused", "detached"]);
103
110
  let totalSummary = d.progressSummary;
104
111
  if (!totalSummary) {
105
112
  let sawProgress = false;
@@ -140,11 +147,13 @@ export function renderMultiCompact(d: Details, theme: Theme, now?: number, pulse
140
147
  );
141
148
 
142
149
  const useResultsDirectly = multiLabel.hasParallelInChain || !d.chainAgents?.length;
150
+ const progressSpan = d.progress?.length ? Math.max(...d.progress.map((p) => p.index + 1)) : 0;
151
+ const resultsSpan = Math.max(d.results.length, progressSpan, d.mode === "parallel" ? (d.totalSteps ?? 0) : 0);
143
152
  const displayStart = multiLabel.showActiveGroupOnly ? multiLabel.groupStartIndex : 0;
144
153
  const displayEnd = multiLabel.showActiveGroupOnly
145
154
  ? multiLabel.groupEndIndex
146
155
  : useResultsDirectly
147
- ? d.results.length
156
+ ? resultsSpan
148
157
  : d.chainAgents!.length;
149
158
  const chainEntries = buildChainRenderEntries(d, multiLabel);
150
159
  const renderEntries =
@@ -152,6 +161,7 @@ export function renderMultiCompact(d: Details, theme: Theme, now?: number, pulse
152
161
  Array.from({ length: displayEnd - displayStart }, (_, offset): ChainRenderEntry => {
153
162
  const i = displayStart + offset;
154
163
  const r = d.results[i];
164
+ const progressAgent = d.progress?.find((p) => p.index === i)?.agent;
155
165
  const fallbackLabel = itemTitle.toLowerCase();
156
166
  const rowNumber = multiLabel.showActiveGroupOnly ? i - multiLabel.groupStartIndex + 1 : i + 1;
157
167
  return {
@@ -159,10 +169,11 @@ export function renderMultiCompact(d: Details, theme: Theme, now?: number, pulse
159
169
  resultIndex: i,
160
170
  rowNumber,
161
171
  agentName: useResultsDirectly
162
- ? r?.agent || `${fallbackLabel}-${rowNumber}`
163
- : d.chainAgents![i] || r?.agent || `${fallbackLabel}-${rowNumber}`,
172
+ ? r?.agent || progressAgent || `${fallbackLabel}-${rowNumber}`
173
+ : d.chainAgents![i] || r?.agent || progressAgent || `${fallbackLabel}-${rowNumber}`,
164
174
  };
165
175
  });
176
+ let liveDetailHintShown = false;
166
177
  for (const entry of renderEntries) {
167
178
  if (entry.kind === "placeholder") {
168
179
  const glyph = widgetStepGlyph(entry.status as AsyncJobStep["status"], theme);
@@ -186,8 +197,24 @@ export function renderMultiCompact(d: Details, theme: Theme, now?: number, pulse
186
197
  const rowNumber = entry.rowNumber;
187
198
  const agentName = entry.agentName;
188
199
  if (!r) {
189
- const pendingLabel = chainEntries ? resultRowLabel(d, multiLabel, i, rowNumber) : `${itemTitle} ${rowNumber}`;
190
- c.addChild(new Text(truncLine(theme.fg("dim", ` ◦ ${pendingLabel}: ${agentName} · pending`), width), 0, 0));
200
+ const rowLabel = resultRowLabel(d, multiLabel, i, rowNumber);
201
+ const runningProg = d.progress?.find((p) => p.index === i && p.status === "running") as
202
+ | AgentProgress
203
+ | undefined;
204
+ if (runningProg) {
205
+ const runningStats = formatProgressStats(theme, runningProg, true, now);
206
+ const runningLine = `${theme.fg("accent", pulseGlyph(pulseFrame))} ${rowLabel}: ${themeBold(theme, agentName)}${runningStats ? ` ${theme.fg("dim", "·")} ${runningStats}` : ""}`;
207
+ c.addChild(new Text(truncLine(` ${runningLine}`, width), 0, 0));
208
+ const activity = compactCurrentActivity(runningProg, now);
209
+ c.addChild(new Text(truncLine(theme.fg("dim", ` ⎿ ${activity}`), width), 0, 0));
210
+ const expandHint = keyHintIfBound("app.tools.expand", "for live detail");
211
+ if (expandHint) {
212
+ c.addChild(new Text(truncLine(theme.fg("accent", ` Press ${expandHint}`), width), 0, 0));
213
+ liveDetailHintShown = true;
214
+ }
215
+ continue;
216
+ }
217
+ c.addChild(new Text(truncLine(theme.fg("dim", ` ◦ ${rowLabel}: ${agentName} · pending`), width), 0, 0));
191
218
  continue;
192
219
  }
193
220
  const output = getSingleResultOutput(r);
@@ -213,14 +240,20 @@ export function renderMultiCompact(d: Details, theme: Theme, now?: number, pulse
213
240
  const activity = compactCurrentActivity(rProg, now);
214
241
  c.addChild(new Text(truncLine(theme.fg("dim", ` ⎿ ${activity}`), width), 0, 0));
215
242
  const expandHint = keyHintIfBound("app.tools.expand", "for live detail");
216
- if (expandHint) c.addChild(new Text(truncLine(theme.fg("accent", ` Press ${expandHint}`), width), 0, 0));
243
+ if (expandHint) {
244
+ c.addChild(new Text(truncLine(theme.fg("accent", ` Press ${expandHint}`), width), 0, 0));
245
+ liveDetailHintShown = true;
246
+ }
217
247
  } else if (
218
248
  !rPending &&
219
- (r.exitCode !== 0 || r.interrupted || r.detached || hasEmptyTextOutputWithoutOutputTarget(r.task, output))
249
+ (r.status !== "ok" || r.interrupted || r.detached || hasEmptyTextOutputWithoutOutputTarget(r.task, output))
220
250
  ) {
221
251
  c.addChild(
222
252
  new Text(
223
- truncLine(theme.fg(r.exitCode !== 0 ? "error" : "dim", ` ⎿ ${resultStatusLine(r, output)}`), width),
253
+ truncLine(
254
+ theme.fg(r.status === "error" ? "error" : "dim", ` ⎿ ${resultStatusLine(r, output)}`),
255
+ width,
256
+ ),
224
257
  0,
225
258
  0,
226
259
  ),
@@ -233,6 +266,10 @@ export function renderMultiCompact(d: Details, theme: Theme, now?: number, pulse
233
266
  new Text(truncLine(theme.fg("dim", ` output: ${shortenPath(r.artifactPaths.outputPath)}`), width), 0, 0),
234
267
  );
235
268
  }
269
+ if (hasRunning && !liveDetailHintShown) {
270
+ const groupHint = keyHintIfBound("app.tools.expand", "for live detail");
271
+ if (groupHint) c.addChild(new Text(truncLine(theme.fg("accent", ` Press ${groupHint}`), width), 0, 0));
272
+ }
236
273
  if (d.artifacts)
237
274
  c.addChild(new Text(truncLine(theme.fg("dim", ` artifacts: ${shortenPath(d.artifacts.dir)}`), width), 0, 0));
238
275
  return c;
@@ -84,7 +84,8 @@ export function renderSubagentResult(
84
84
  );
85
85
  return container;
86
86
  }
87
- if (!d?.results.length) {
87
+ const liveMultiProgress = (d?.mode === "parallel" || d?.mode === "chain") && (d?.progress?.length ?? 0) > 0;
88
+ if (!d?.results.length && !liveMultiProgress) {
88
89
  const t = result.content[0];
89
90
  const text = t?.type === "text" ? t.text : "(no output)";
90
91
  const contextPrefix = d?.context === "fork" ? `${theme.fg("warning", "[fork]")} ` : "";
@@ -100,9 +101,9 @@ export function renderSubagentResult(
100
101
  const isRunning = r.progress?.status === "running";
101
102
  const icon = isRunning
102
103
  ? theme.fg("warning", "running")
103
- : r.detached
104
+ : r.detached || r.status === "continued"
104
105
  ? theme.fg("warning", "detached")
105
- : r.exitCode === 0
106
+ : r.status === "ok"
106
107
  ? theme.fg("success", "ok")
107
108
  : theme.fg("error", "failed");
108
109
  const contextBadge = d.context === "fork" ? theme.fg("warning", " [fork]") : "";
@@ -202,11 +203,11 @@ export function renderSubagentResult(
202
203
  d.results.some((r) => r.progress?.status === "running") ||
203
204
  workflowGraphHasStatus(d, ["running"]);
204
205
  const ok = d.results.filter(
205
- (r) => r.progress?.status === "completed" || (r.exitCode === 0 && r.progress?.status !== "running"),
206
+ (r) => r.progress?.status === "completed" || (r.status === "ok" && r.progress?.status !== "running"),
206
207
  ).length;
207
208
  const hasEmptyWithoutTarget = d.results.some(
208
209
  (r) =>
209
- r.exitCode === 0 &&
210
+ r.status === "ok" &&
210
211
  r.progress?.status !== "running" &&
211
212
  hasEmptyTextOutputWithoutOutputTarget(r.task, getSingleResultOutput(r)),
212
213
  );
@@ -248,15 +249,14 @@ export function renderSubagentResult(
248
249
  const modeLabel = d.mode;
249
250
  const contextBadge = d.context === "fork" ? theme.fg("warning", " [fork]") : "";
250
251
  const multiLabel = buildMultiProgressLabel(d, hasRunning);
251
- const itemTitle = multiLabel.itemTitle;
252
252
 
253
253
  const chainVis =
254
254
  d.chainAgents?.length && !multiLabel.hasParallelInChain
255
255
  ? d.chainAgents
256
256
  .map((agent, i) => {
257
257
  const result = d.results[i];
258
- const isFailed = result && result.exitCode !== 0 && result.progress?.status !== "running";
259
- const isComplete = result && result.exitCode === 0 && result.progress?.status !== "running";
258
+ const isFailed = result && result.status === "error" && result.progress?.status !== "running";
259
+ const isComplete = result && result.status === "ok" && result.progress?.status !== "running";
260
260
  const isEmptyWithoutTarget =
261
261
  Boolean(result) &&
262
262
  Boolean(isComplete) &&
@@ -293,11 +293,13 @@ export function renderSubagentResult(
293
293
  }
294
294
 
295
295
  const useResultsDirectly = multiLabel.hasParallelInChain || !d.chainAgents?.length;
296
+ const progressSpan = d.progress?.length ? Math.max(...d.progress.map((p) => p.index + 1)) : 0;
297
+ const resultsSpan = Math.max(d.results.length, progressSpan, d.mode === "parallel" ? (d.totalSteps ?? 0) : 0);
296
298
  const displayStart = multiLabel.showActiveGroupOnly ? multiLabel.groupStartIndex : 0;
297
299
  const displayEnd = multiLabel.showActiveGroupOnly
298
300
  ? multiLabel.groupEndIndex
299
301
  : useResultsDirectly
300
- ? d.results.length
302
+ ? resultsSpan
301
303
  : d.chainAgents!.length;
302
304
  const chainEntries = buildChainRenderEntries(d, multiLabel);
303
305
  const renderEntries =
@@ -305,14 +307,15 @@ export function renderSubagentResult(
305
307
  Array.from({ length: displayEnd - displayStart }, (_, offset): ChainRenderEntry => {
306
308
  const i = displayStart + offset;
307
309
  const r = d.results[i];
310
+ const progressAgent = d.progress?.find((p) => p.index === i)?.agent;
308
311
  const rowNumber = multiLabel.showActiveGroupOnly ? i - multiLabel.groupStartIndex + 1 : i + 1;
309
312
  return {
310
313
  kind: "result",
311
314
  resultIndex: i,
312
315
  rowNumber,
313
316
  agentName: useResultsDirectly
314
- ? r?.agent || `step-${rowNumber}`
315
- : d.chainAgents![i] || r?.agent || `step-${rowNumber}`,
317
+ ? r?.agent || progressAgent || `step-${rowNumber}`
318
+ : d.chainAgents![i] || r?.agent || progressAgent || `step-${rowNumber}`,
316
319
  };
317
320
  });
318
321
 
@@ -335,8 +338,32 @@ export function renderSubagentResult(
335
338
  const agentName = entry.agentName;
336
339
 
337
340
  if (!r) {
338
- const pendingLabel = chainEntries ? resultRowLabel(d, multiLabel, i, rowNumber) : `${itemTitle} ${rowNumber}`;
339
- c.addChild(new Text(fit(theme.fg("dim", ` ${pendingLabel}: ${agentName}`)), 0, 0));
341
+ const rowLabel = resultRowLabel(d, multiLabel, i, rowNumber);
342
+ const runningProg = d.progress?.find((p) => p.index === i && p.status === "running") as
343
+ | AgentProgress
344
+ | undefined;
345
+ if (runningProg) {
346
+ const runningStats = ` | ${runningProg.toolCount} tools, ${formatDuration(displayProgressDurationMs(runningProg, options.now))}`;
347
+ c.addChild(
348
+ new Text(
349
+ fit(
350
+ `${theme.fg("warning", "running")} ${rowLabel}: ${theme.bold(theme.fg("warning", agentName))}${runningStats}`,
351
+ ),
352
+ 0,
353
+ 0,
354
+ ),
355
+ );
356
+ const progressSnapshotNow = snapshotNowForProgress(runningProg, options.now);
357
+ const liveStatusLine = buildLiveStatusLine(runningProg, progressSnapshotNow);
358
+ if (liveStatusLine) {
359
+ c.addChild(new Text(fit(theme.fg("accent", ` ${liveStatusLine}`)), 0, 0));
360
+ }
361
+ const expandHint = keyHintIfBound("app.tools.expand", "for live detail");
362
+ if (expandHint) c.addChild(new Text(fit(theme.fg("accent", ` Press ${expandHint}`)), 0, 0));
363
+ c.addChild(new Spacer(1));
364
+ continue;
365
+ }
366
+ c.addChild(new Text(fit(theme.fg("dim", ` ${rowLabel}: ${agentName}`)), 0, 0));
340
367
  c.addChild(new Text(theme.fg("dim", ` status: pending`), 0, 0));
341
368
  c.addChild(new Spacer(1));
342
369
  continue;
@@ -352,11 +379,13 @@ export function renderSubagentResult(
352
379
  const resultOutput = getSingleResultOutput(r);
353
380
  const statusIcon = rRunning
354
381
  ? theme.fg("warning", "running")
355
- : r.exitCode !== 0
382
+ : r.status === "error"
356
383
  ? theme.fg("error", "failed")
357
- : hasEmptyTextOutputWithoutOutputTarget(r.task, resultOutput)
358
- ? theme.fg("warning", "warning")
359
- : theme.fg("success", "done");
384
+ : r.status === "skipped" || r.status === "interrupted" || r.status === "continued"
385
+ ? theme.fg("warning", r.status)
386
+ : hasEmptyTextOutputWithoutOutputTarget(r.task, resultOutput)
387
+ ? theme.fg("warning", "warning")
388
+ : theme.fg("success", "done");
360
389
  const stats = rProg
361
390
  ? ` | ${rProg.toolCount} tools, ${formatDuration(displayProgressDurationMs(rProg, options.now))}`
362
391
  : "";
@@ -65,7 +65,7 @@ export function subagentResultRenderKey(
65
65
  ...details.results.map((entry) =>
66
66
  [
67
67
  entry.agent,
68
- entry.exitCode,
68
+ entry.status,
69
69
  entry.interrupted === true ? "interrupted" : "",
70
70
  entry.detached === true ? "detached" : "",
71
71
  progressRenderKey(entry.progress),
@@ -134,8 +134,10 @@ export function firstOutputLine(text: string): string {
134
134
 
135
135
  export function resultStatusLine(result: Details["results"][number], output: string): string {
136
136
  if (result.detached) return result.detachedReason ? `Detached: ${result.detachedReason}` : "Detached";
137
- if (result.interrupted) return "Paused";
138
- if (result.exitCode !== 0) return `Error: ${result.error ?? (firstOutputLine(output) || `exit ${result.exitCode}`)}`;
137
+ if (result.status === "continued") return "Continued";
138
+ if (result.interrupted || result.status === "interrupted") return "Paused";
139
+ if (result.status === "skipped") return result.error ? `Skipped: ${result.error}` : "Skipped";
140
+ if (result.status === "error") return `Error: ${result.error ?? (firstOutputLine(output) || "failed")}`;
139
141
  if (hasEmptyTextOutputWithoutOutputTarget(result.task, output)) return "Done (no text output)";
140
142
  return "Done";
141
143
  }
@@ -148,9 +150,10 @@ export function resultGlyph(
148
150
  pulseFrame?: number,
149
151
  ): string {
150
152
  if (running) return theme.fg("accent", pulseGlyph(pulseFrame));
151
- if (result.detached) return theme.fg("warning", "■");
152
- if (result.interrupted) return theme.fg("warning", "");
153
- if (result.exitCode !== 0) return theme.fg("error", "");
153
+ if (result.detached || result.status === "continued") return theme.fg("warning", "■");
154
+ if (result.interrupted || result.status === "interrupted" || result.status === "skipped")
155
+ return theme.fg("warning", "");
156
+ if (result.status === "error") return theme.fg("error", "✗");
154
157
  if (hasEmptyTextOutputWithoutOutputTarget(result.task, output)) return theme.fg("warning", "✓");
155
158
  return theme.fg("success", "✓");
156
159
  }
@@ -1,5 +1,4 @@
1
1
  import { keyHintIfBound } from "@bastani/atomic";
2
- import { shortenPath } from "../shared/formatters.ts";
3
2
  import { aggregateStepStatus, formatParallelOutcome } from "../shared/status-format.ts";
4
3
  import type { AsyncJobState } from "../shared/types.ts";
5
4
  import { buildAsyncChainStepSpans } from "./render-chain-graph.ts";
@@ -8,7 +7,6 @@ import {
8
7
  modelThinkingBadge,
9
8
  widgetActivity,
10
9
  widgetJobName,
11
- widgetOutputPath,
12
10
  widgetStats,
13
11
  widgetStatusGlyph,
14
12
  widgetStepActivity,
@@ -130,8 +128,6 @@ export function foregroundStyleWidgetStepLines(
130
128
  if (step.status === "running") {
131
129
  const expandHint = keyHintIfBound("app.tools.expand", "for live detail");
132
130
  if (!expanded && expandHint) lines.push(` ${theme.fg("accent", `Press ${expandHint}`)}`);
133
- const output = widgetOutputPath(job, step);
134
- if (output) lines.push(` ${theme.fg("dim", `output: ${shortenPath(output)}`)}`);
135
131
  if (expanded) {
136
132
  const liveStatus = buildLiveStatusLine(step, job.updatedAt);
137
133
  if (liveStatus && liveStatus !== activity) lines.push(` ${theme.fg("accent", liveStatus)}`);
@@ -247,10 +243,6 @@ export function compactSingleWidgetLines(
247
243
  pulseFrame,
248
244
  ))
249
245
  lines.push(` ${nestedLine}`);
250
- if (step.status === "running") {
251
- const output = widgetOutputPath(job, step);
252
- if (output) lines.push(` ${theme.fg("dim", `output: ${shortenPath(output)}`)}`);
253
- }
254
246
  }
255
247
  const expandHint = keyHintIfBound("app.tools.expand", "for live detail");
256
248
  if (expandHint && job.steps.some((step) => step.status === "running"))
@@ -170,28 +170,6 @@ function getWidgetOwnerKey(ctx: ExtensionContext): string {
170
170
  return `${sessionOwner}|cwd:${cwdOwner}`;
171
171
  }
172
172
 
173
- function widgetStatusText(jobs: AsyncJobState[]): string | undefined {
174
- if (jobs.length === 0) return undefined;
175
- const counts = new Map<string, number>();
176
- for (const job of jobs) counts.set(job.status, (counts.get(job.status) ?? 0) + 1);
177
- const ordered = ["running", "queued", "complete", "failed", "paused"];
178
- const parts = ordered
179
- .map((status) => {
180
- const count = counts.get(status) ?? 0;
181
- return count > 0 ? `${count} ${status}` : undefined;
182
- })
183
- .filter((part): part is string => part !== undefined);
184
- return `Async agents: ${parts.join(", ")}`;
185
- }
186
-
187
- function setWidgetStatus(ctx: ExtensionContext, jobs: AsyncJobState[]): void {
188
- try {
189
- ctx.ui.setStatus?.(WIDGET_KEY, widgetStatusText(jobs));
190
- } catch {
191
- // Status mirroring must never prevent the primary widget render path.
192
- }
193
- }
194
-
195
173
  function requestWidgetRender(ctx: ExtensionContext): void {
196
174
  (ctx as RenderRequestingContext).ui.requestRender?.();
197
175
  }
@@ -343,7 +321,6 @@ export function renderWidget(ctx: ExtensionContext, jobs: AsyncJobState[], owner
343
321
  if (requestedSurface.activeOwner && requestedSurface.activeOwner !== owner) return;
344
322
  if (state.surfaceKey && getSurfaceState(state.surfaceKey).activeOwner !== owner) return;
345
323
  if (state.mounted && state.mountedOwnerKey !== ownerKey) return;
346
- if (ctx.hasUI) setWidgetStatus(ctx, []);
347
324
  stopWidgetAnimation(ctx, owner);
348
325
  return;
349
326
  }
@@ -363,7 +340,6 @@ export function renderWidget(ctx: ExtensionContext, jobs: AsyncJobState[], owner
363
340
  state.latestJobs = [...jobs];
364
341
  refreshWidgetSnapshot(state, jobs);
365
342
  }
366
- setWidgetStatus(ctx, jobs);
367
343
  if (!state.mounted) {
368
344
  const surface = getSurfaceState(requestedSurfaceKey);
369
345
  if (surface.activeOwner && surface.activeOwner !== owner) return;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/web-access",
3
- "version": "0.9.12",
3
+ "version": "0.9.13-alpha.1",
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,31 @@ 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.13-alpha.1] - 2026-08-05
10
+
11
+ ### Added
12
+
13
+ - Added four user-initiated workflow lifecycle notices — `started`, `paused`, `quit`, and `resumed` — so the main chat learns when a top-level background run begins, stops, or picks back up. Each travels the same steer delivery, capped-backoff retry, and notice-card path the failure notice already uses, so an agent waiting on a background run no longer mistakes a deliberate stop for work still in progress. `WORKFLOW STARTED` (`▶`), `WORKFLOW PAUSED` (`⏸`, warning tone), `WORKFLOW QUIT` (`⏹`, warning tone, also reporting whether the run stayed resumable), and `WORKFLOW RESUMED` (`▶`) render as cards; the paused and quit text states that the stop was deliberate and user-requested and tells the model not to resume the run or take over the work unless asked, hinting `/workflow resume <run-id>`, while the resumed text does not, because the run is progressing again ([#2177](https://github.com/bastani-inc/atomic/issues/2177)).
14
+ - Only user actions raise those notices. `/workflow <name>`, `/workflow pause`, `/workflow quit`, and `/workflow resume` notify; the matching `workflow({ action: "run" | "pause" | "quit" | "resume" })` tool calls stay silent because the tool result already reports them, and `/workflow interrupt` raises nothing. Engine-internal transitions stay silent as well: a notice exists only where a control path recorded an actor, which is what keeps answering a human-in-the-loop prompt, per-stage control, and the resume-acknowledgement pass out of the chat ([#2177](https://github.com/bastani-inc/atomic/issues/2177)).
15
+ - Lifecycle notices now carry two attributions. *Origin* — who launched the run — renders on every kind as "which you started" or "which the user started", is recorded once at dispatch, survives session restore and durable resume, and is inherited by a continuation from the run it continues rather than recomputed. *Actor* — who performed this one event — renders as "The user paused". A run with no recorded origin, including a legacy or restored snapshot, omits the clause entirely instead of guessing ([#2177](https://github.com/bastani-inc/atomic/issues/2177)).
16
+ - One request now produces exactly one notice. A whole-run pause or resume reports at run scope; a stage-scoped pause or resume that leaves siblings paused reports at stage scope, and one that stops or restarts the whole run reports the run instead, never both. A quit reports the quit alone rather than the pause it publishes on the way. Because control actions are reversible, they are deduplicated by run id together with the occurrence timestamp, so pause → resume → pause → resume notifies four times while repeated snapshot invalidations at one unchanged state notify once. Resuming reports a resume and never a start, whoever requested it: resuming a failed or blocked run launches a continuation under a fresh id whose notice names both run ids, while resuming a quit run reuses the original workflow id so durable checkpoints replay and its notice names the one id. A run already started, paused, or quit when notifications install (restore, replay, `/reload`, session-preserving reinstall) stays silent, and nested child runs never notify at top level. The default `workflowNotifications.notifyOn` becomes `["started", "completed", "failed", "blocked", "awaiting_input", "paused", "quit", "resumed"]`; a config that pins `notifyOn` explicitly keeps exactly the kinds it lists ([#2177](https://github.com/bastani-inc/atomic/issues/2177)).
17
+
18
+ ### Changed
19
+
20
+ - Workflow stage resource loading now carries typed subagent policy in session options and no longer mutates process-global child environment flags during reload. Concurrent stage creation preserves tool allowlists and orchestration limits without the process-era isolation race ([#2188](https://github.com/bastani-inc/atomic/issues/2188)).
21
+
22
+ ### Fixed
23
+
24
+ - Fixed thrown transient stage failures — rate limits, provider outages, network timeouts, transport errors — to retry the current model with bounded exponential backoff from `settings.retry` before advancing through `fallbackModels`; disabling retry keeps immediate candidate advancement. Failures another request to the same model cannot repair — a rejected credential, an unavailable model, an incompatible request — skip the same-model retry and advance to the next candidate immediately, for prompt delivery and session creation alike ([#2170](https://github.com/bastani-inc/atomic/issues/2170)).
25
+ - Fixed a same-candidate stage retry losing the stage prompt. The retry resumed the existing session turn with a transcript that no longer ended in the prompt it was resuming, which the agent rejects; the prompt is now retained for that path and still re-sent on the re-prompt path ([#2170](https://github.com/bastani-inc/atomic/issues/2170)).
26
+ - Fixed a same-candidate stage retry stopping outright when the failed attempt had already streamed a completed assistant message. Resuming the existing turn is now attempted only when the transcript, as converted for the provider, still ends in a message the agent can continue from; otherwise the retry re-sends the prompt, which recovers instead of failing. Messages that convert away — a note excluded from context, an empty branch summary — no longer look like a valid tail ([#2170](https://github.com/bastani-inc/atomic/issues/2170)).
27
+ - Fixed an eagerly created stage session never reaching its `fallbackModels`. A session created before any prompt — by an eager stage call, a control attach, or `ctx.__ensureSession()` — retried only its first candidate and then failed the stage. It now walks the configured chain, and a failure that exhausts the chain is no longer cached and replayed to every later caller ([#2170](https://github.com/bastani-inc/atomic/issues/2170)).
28
+ - Fixed a paused eager session creation dropping its replacement objective when the stage had explicit model candidates. The resumed text is now authoritative for the next prompt and is sent exactly once ([#2170](https://github.com/bastani-inc/atomic/issues/2170)).
29
+ - Fixed a context overflow that compaction had already failed to resolve being retried on the same candidate for the whole `settings.retry` budget. Re-sending an identical request cannot fit a context compaction could not shrink, so the chain now advances immediately while still treating the failure as fallbackable ([#2170](https://github.com/bastani-inc/atomic/issues/2170)).
30
+ - Fixed a stage running two candidate walks at once. Advancing a candidate released the shared creation gate while the walk was still creating the next session, so a concurrent `ctx.__ensureSession()` or a first `ctx.prompt()` could start a second walk, duplicate provider work, and leave the losing session live and undisposed. Concurrent callers now join the creation already in flight ([#2170](https://github.com/bastani-inc/atomic/issues/2170)).
31
+ - Fixed a controlled pause that both started and finished while a stage session was still being created losing its replacement objective, so the stale objective was prompted instead ([#2170](https://github.com/bastani-inc/atomic/issues/2170)).
32
+ - Fixed a controlled pause during a same-candidate continuation aborting the stage unrecoverably. The abort a pause causes is no longer classified as a terminal model failure: the pause settles, and resuming with a replacement objective re-prompts on the same candidate instead of spending a fallback ([#2170](https://github.com/bastani-inc/atomic/issues/2170)).
33
+
9
34
  ## [0.9.12] - 2026-08-04
10
35
 
11
36
  Cumulative release of the `0.9.12-alpha.1` prerelease. The summary below covers the user-visible outcome of that work; the per-change detail remains in the prerelease section below.