@bastani/atomic 0.9.15 → 0.9.16-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 (82) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +10 -0
  3. package/dist/builtin/intercom/README.md +8 -8
  4. package/dist/builtin/intercom/contact-supervisor-tool.ts +68 -32
  5. package/dist/builtin/intercom/index-heavy.ts +1 -0
  6. package/dist/builtin/intercom/index.ts +21 -15
  7. package/dist/builtin/intercom/intercom-tool.ts +66 -10
  8. package/dist/builtin/intercom/package.json +1 -1
  9. package/dist/builtin/intercom/parent-ask-handoff.ts +72 -0
  10. package/dist/builtin/mcp/package.json +1 -1
  11. package/dist/builtin/subagents/CHANGELOG.md +13 -0
  12. package/dist/builtin/subagents/README.md +23 -12
  13. package/dist/builtin/subagents/agents/worker.md +2 -2
  14. package/dist/builtin/subagents/package.json +1 -1
  15. package/dist/builtin/subagents/skills/subagent/SKILL.md +10 -17
  16. package/dist/builtin/subagents/src/extension/index.ts +5 -30
  17. package/dist/builtin/subagents/src/extension/notification-content.ts +1 -1
  18. package/dist/builtin/subagents/src/extension/schemas.ts +2 -14
  19. package/dist/builtin/subagents/src/extension/tool-description.ts +1 -2
  20. package/dist/builtin/subagents/src/extension/tool-rendering.ts +53 -0
  21. package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +1 -1
  22. package/dist/builtin/subagents/src/intercom/result-intercom.ts +4 -4
  23. package/dist/builtin/subagents/src/runs/foreground/execution-parent-ask-handoff.ts +78 -0
  24. package/dist/builtin/subagents/src/runs/foreground/inprocess-run-sync.ts +7 -5
  25. package/dist/builtin/subagents/src/runs/foreground/notify.ts +5 -6
  26. package/dist/builtin/subagents/src/runs/foreground/parent-ask-output.ts +48 -0
  27. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-burst-display.ts +46 -0
  28. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-burst.ts +403 -0
  29. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-context.ts +48 -9
  30. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-input.ts +3 -1
  31. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-live-update.ts +11 -0
  32. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +164 -93
  33. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +36 -29
  34. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parent-ask-projection.ts +11 -0
  35. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +42 -17
  36. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-status.ts +3 -121
  37. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-types.ts +7 -2
  38. package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +30 -220
  39. package/dist/builtin/subagents/src/runs/inprocess/control-status.ts +0 -34
  40. package/dist/builtin/subagents/src/runs/inprocess/index.ts +0 -12
  41. package/dist/builtin/subagents/src/runs/inprocess/prompt-behavior.ts +1 -1
  42. package/dist/builtin/subagents/src/runs/inprocess/runner.ts +2 -141
  43. package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +8 -0
  44. package/dist/builtin/subagents/src/shared/status-format.ts +3 -3
  45. package/dist/builtin/subagents/src/shared/types-config.ts +41 -0
  46. package/dist/builtin/subagents/src/shared/types-foreground-state.ts +7 -15
  47. package/dist/builtin/subagents/src/shared/types-results.ts +3 -1
  48. package/dist/builtin/subagents/src/shared/types-runtime.ts +1 -11
  49. package/dist/builtin/subagents/src/tui/render-progress.ts +2 -2
  50. package/dist/builtin/subagents/src/tui/render-result-compact.ts +2 -2
  51. package/dist/builtin/subagents/src/tui/render-result.ts +38 -14
  52. package/dist/builtin/subagents/src/tui/render-status-progress.ts +1 -1
  53. package/dist/builtin/web-access/package.json +1 -1
  54. package/dist/builtin/workflows/CHANGELOG.md +10 -0
  55. package/dist/builtin/workflows/README.md +1 -1
  56. package/dist/builtin/workflows/package.json +1 -1
  57. package/dist/builtin/workflows/src/durable/dbos-embedded-postgres-root.ts +548 -39
  58. package/dist/builtin/workflows/src/durable/dbos-embedded-postgres.ts +529 -65
  59. package/dist/builtin/workflows/src/durable/dbos-lifecycle.ts +30 -8
  60. package/dist/builtin/workflows/src/durable/dbos-local-postgres.ts +36 -3
  61. package/dist/builtin/workflows/src/durable/local-command.ts +68 -10
  62. package/dist/builtin/workflows/src/extension/dispatcher.ts +7 -1
  63. package/dist/builtin/workflows/src/extension/extension-factory.ts +2 -2
  64. package/dist/builtin/workflows/src/extension/index.bundle.mjs +1094 -298
  65. package/dist/builtin/workflows/src/extension/public-types.ts +2 -2
  66. package/dist/builtin/workflows/src/extension/render-result.ts +16 -2
  67. package/dist/builtin/workflows/src/extension/runtime.ts +8 -1
  68. package/dist/builtin/workflows/src/extension/workflow-request-abort.ts +15 -0
  69. package/dist/builtin/workflows/src/extension/workflow-tool-content.ts +4 -3
  70. package/dist/builtin/workflows/src/extension/workflow-tool-registration.ts +107 -13
  71. package/dist/builtin/workflows/src/extension/workflow-tool.ts +22 -12
  72. package/dist/builtin/workflows/src/tui/widget.ts +25 -6
  73. package/dist/core/event-bus.d.ts.map +1 -1
  74. package/dist/core/event-bus.js +19 -3
  75. package/dist/core/event-bus.js.map +1 -1
  76. package/docs/intercom.md +28 -26
  77. package/docs/subagents.md +15 -11
  78. package/docs/workflows.md +27 -10
  79. package/npm-shrinkwrap.json +32 -32
  80. package/package.json +3 -3
  81. package/dist/builtin/subagents/src/runs/inprocess/attempt-handles.ts +0 -104
  82. /package/dist/builtin/subagents/src/runs/foreground/{subagent-executor-resume.ts → subagent-executor-cwd.ts} +0 -0
package/docs/intercom.md CHANGED
@@ -152,7 +152,7 @@ Name sessions with `/name` so they can target each other (for example `/name pla
152
152
  | `leave` | Returns the session to its resolved home group from startup. It takes no `group` parameter. |
153
153
  | `list` | Returns the current session plus other active intercom-connected sessions with name, full session ID, working directory, model, and live status (`idle`, `thinking`, or `tool:<name>`, derived from lifecycle events). Every displayed full session ID is a valid target. |
154
154
  | `send` | Fire-and-forget delivery. Requires `to` and `message`; returns delivery confirmation or the delivery-failure reason. Cannot message the current session. |
155
- | `ask` | Sends a message and blocks until the recipient replies (10-minute timeout). The reply is returned as the tool result, so the agent continues in the same turn. |
155
+ | `ask` | Sends a message and blocks until the recipient replies (10-minute timeout). From a foreground child to its resolved launching parent, it instead ends the child and returns a fresh-subagent handoff through the parent `subagent` call. |
156
156
  | `reply` | Replies to the intercom-triggered message of the current turn; otherwise falls back to the single unresolved inbound ask. With multiple pending asks, pass `to` or inspect with `pending` first. |
157
157
  | `pending` | Lists unresolved inbound asks with sender, message ID, elapsed time, and a short preview. |
158
158
  | `status` | Shows connection status, session ID, current group, and the count of active sessions in that group. A `group` filter remains a read-only peek. |
@@ -179,19 +179,19 @@ Every session belongs to exactly one intercom **group**. Sessions with no group
179
179
  - `list`/`status` show your own group and only same-group peers. Pass `group: "name"` to `list`/`status` for a **read-only** peek at another group's membership. `send`/`ask` are always locked to your own group and error if you pass a different `group`.
180
180
  - `session_joined`/`session_left`/`presence_update` are group-scoped, so you never see peers outside your group appear or disappear.
181
181
 
182
- A session's home group is resolved with this precedence: explicit stage/task/subagent group > runtime-owned workflow invocation group or inherited launching-session group > env `ATOMIC_INTERCOM_GROUP` (legacy `PI_INTERCOM_GROUP`) > Intercom `config.json` `"group"` > `"default"`. Each top-level workflow invocation gets a stable non-default group from its persistent run identity; Intercom-capable stages, nested workflows, and their delegated subagents inherit it without authors threading IDs. Explicit `group: "default"` opts back into the shared group. Boolean `true` and the trimmed, case-insensitive strings `"true"`/`"auto"` request an automatically generated subgroup; those string names are reserved. Stages and children without Intercom access receive no group. See [workflows.md](/workflows) for workflow-stage rules. The subagent-only `contact_supervisor` path can cross groups only after a broker capability binds the registered child socket to the issuing supervisor session. The broker, not the client, marks validated traffic as `supervisor`; ordinary `send` frames remain isolated even if a raw client forges that flag, and replies cross back only through an exact broker-recorded `replyTo` match. Parent-held authorization state is restored after broker reconnects. The lightweight wrapper synchronously claims supervisor-authorization requests and lazy-loads the broker provider; a claimed provider failure aborts launch, while runtimes with no provider omit supervisor metadata and do not expose a broken channel.
182
+ A session's home group is resolved with this precedence: explicit stage/task/subagent group > runtime-owned workflow invocation group or inherited launching-session group > env `ATOMIC_INTERCOM_GROUP` (legacy `PI_INTERCOM_GROUP`) > Intercom `config.json` `"group"` > `"default"`. Each top-level workflow invocation gets a stable non-default group from its persistent run identity; Intercom-capable stages, nested workflows, and their delegated subagents inherit it without authors threading IDs. Explicit `group: "default"` opts back into the shared group. Boolean `true` and the trimmed, case-insensitive strings `"true"`/`"auto"` request an automatically generated subgroup; those string names are reserved. Stages and children without Intercom access receive no group. See [workflows.md](/workflows) for workflow-stage rules. The subagent-only `contact_supervisor` path can cross groups only after a broker capability binds the registered child socket to the issuing supervisor session. The broker, not the client, marks validated traffic as `supervisor`; ordinary `send` frames remain isolated even if a raw client forges that flag, and replies cross back only through an exact broker-recorded `replyTo` match. Parent-held authorization state is restored after broker reconnects. Before an Intercom-enabled foreground child first runs, the parent wrapper may lazy-load and connect the broker provider to mint that exact child's capability; queued children request no capability. The child still connects only when it uses an Intercom delivery path, and claimed decisions or interviews terminally hand off before child send or waiter admission. A claimed provider failure aborts launch, while runtimes with no provider omit supervisor metadata and do not expose a broken channel.
183
183
 
184
184
  ### send vs ask vs reply
185
185
 
186
186
  **`send`** is fire-and-forget — the tool returns immediately after delivery. By default it sends immediately, including in interactive sessions. If you want an approval dialog before non-reply sends, set `confirmSend: true` in config; replies that include `replyTo` still skip confirmation so reply-hint flows continue without an extra approval step.
187
187
 
188
- **`ask`** sends the message and blocks until the recipient responds (10-minute timeout). The reply comes back as the tool result, so the agent continues in the same turn with full context. No confirmation dialog if you're asking and waiting, the intent is clear. Only one pending `ask` is allowed per session at a time; if several blocking requests race (parallel `ask` calls, or `ask` alongside `contact_supervisor`), one wins the reservation and each other call returns a normal "Already waiting for a reply" tool error without disturbing the pending ask.
188
+ **`ask`** normally sends the message and blocks until the recipient responds (10-minute timeout). The reply comes back as the tool result, so the agent continues in the same turn with full context. A foreground child asking its resolved launching parent is the exception: Atomic ends the child before send or waiter admission and returns a dynamic `[TASK_CONTEXT]` handoff through the parent `subagent` call. The supervisor answer is placed in a fresh child task with a new run identity. Asks to siblings and other peers keep the normal waiter path. Only one normal pending `ask` is allowed per session at a time; if several such requests race, one wins the reservation and each other call returns "Already waiting for a reply" without disturbing it.
189
189
 
190
190
  **`reply`** is receiver-side sugar for replying to an inbound ask. In the turn triggered by an incoming intercom message, `intercom({ action: "reply", message: "..." })` targets that exact sender and message automatically. If you reply later, it falls back to the single unresolved inbound ask; with multiple pending asks, use `pending` to inspect them and pass `to` to disambiguate. Under the hood this is still a normal `send` with the exact `replyTo` value.
191
191
 
192
192
  ### Attachments
193
193
 
194
- `send`, `ask`, and `reply` accept an `attachments` array of `{ type, name, content, language? }` objects where `type` is `"file"`, `"snippet"`, or `"context"`. Attachment content is included in the recipient's agent-visible message body. Attachments are supported in the protocol but not in the ALT+M compose overlay.
194
+ `send`, `ask`, and `reply` accept an `attachments` array of `{ type, name, content, language? }` objects where `type` is `"file"`, `"snippet"`, or `"context"`. Attachment content is included in the recipient's agent-visible message body. When a parent-targeted foreground `ask` is terminally handed off at the source, the same ordered attachment array is retained and rendered with the question for the launching parent; duplicate names and content are not rewritten. Attachments are supported in the protocol but not in the ALT+M compose overlay.
195
195
 
196
196
  ## Coordination Patterns
197
197
 
@@ -253,7 +253,7 @@ When Atomic's [subagent runtime](/subagents) admits a delegated child, the child
253
253
 
254
254
  `contact_supervisor` is registered from the typed admission record. The record binds the supervisor target, canonical child identity, child index, session name, and any broker-issued capability to that in-process child session; none of those values are inherited from environment variables. If the parent did not grant supervisor coordination, the session receives only the regular `intercom` tool.
255
255
 
256
- The child identity remains stable across foreground continuation, interruption, and cold resume. Intercom detach uses the same in-process continuation as foreground coordination, so the terminal envelope retains one canonical path.
256
+ A parent-targeted blocking ask makes the current child terminal for continuation. The handoff identifies the previous agent and run, but follow-up uses a fresh child and new run identity. Ordinary Intercom detach remains separate for sends, progress updates, and non-parent asks.
257
257
 
258
258
  | Parameter | Type | Description |
259
259
  |-----------|------|-------------|
@@ -265,11 +265,11 @@ The child identity remains stable across foreground continuation, interruption,
265
265
 
266
266
  | Reason | Behavior | Use When |
267
267
  |--------|----------|----------|
268
- | `need_decision` | Sends a formatted ask to the supervisor and blocks until it replies (10-minute timeout) | The subagent is blocked, uncertain, needs approval, or faces a product/API/scope decision |
269
- | `interview_request` | Sends structured questions and blocks until the supervisor replies | The subagent needs multiple machine-readable answers from the supervisor in one exchange |
270
- | `progress_update` | Fire-and-forget update to the supervisor | Meaningful progress or unexpected discoveries that change the plan |
268
+ | `need_decision` | Ends a live foreground child and returns the original question plus a fresh-child `[TASK_CONTEXT]` handoff through the parent `subagent` call | The subagent is blocked, uncertain, needs approval, or faces a product/API/scope decision |
269
+ | `interview_request` | Ends a live foreground child and returns the structured questions in a fresh-child handoff through the parent `subagent` call | The subagent needs multiple machine-readable answers from the supervisor in one exchange |
270
+ | `progress_update` | Fire-and-forget update to the supervisor; does not end the child | Meaningful progress or unexpected discoveries that change the plan |
271
271
 
272
- Do not use `contact_supervisor` for routine completion handoffs return the final subagent result normally. Blocking calls share the same single reply-waiter reservation as `ask`, with the same "Already waiting for a reply" semantics.
272
+ Do not use `contact_supervisor` for routine completion handoffs—return the final subagent result normally. Blocking reasons are intercepted before broker connection or reply-waiter admission when the exact foreground child claims them. If no live owner claims a request, the existing Intercom send/wait fallback remains available.
273
273
 
274
274
  ```typescript
275
275
  // Blocked subagent asks for guidance
@@ -277,7 +277,7 @@ contact_supervisor({
277
277
  reason: "need_decision",
278
278
  message: "The auth service returns 403 instead of 401 for expired tokens. Should I treat 403 as a re-auth trigger or a hard failure?"
279
279
  })
280
- // → Reply from supervisor: Treat 403 as re-auth trigger. Update the token refresh logic.
280
+ // → Parent subagent call returns a fresh-child [TASK_CONTEXT] handoff
281
281
 
282
282
  // Fire-and-forget progress update
283
283
  contact_supervisor({
@@ -289,20 +289,22 @@ contact_supervisor({
289
289
 
290
290
  ### What the Supervisor Sees
291
291
 
292
- The supervisor receives a formatted message with run metadata:
293
-
294
- ```
295
- **From subagent-worker-78f659a3-1**
296
-
297
- Subagent needs a supervisor decision.
298
- Run: 78f659a3
299
- Agent: worker
300
- Child index: 0
292
+ For a claimed foreground parent ask, the supervisor receives terminal run metadata, the original question, ordered attachments, the previous agent identity, and an explicit fresh-start call:
301
293
 
294
+ ```text
295
+ Subagent yielded for parent input (worker, child 1).
296
+ Previous run (terminal): 78f659a3
297
+ Question:
302
298
  Which API should I use?
299
+
300
+ Start a fresh subagent with a new run identity, replacing <SUPERVISOR_ANSWER> with your answer:
301
+ subagent({
302
+ "agent": "worker",
303
+ "task": "[TASK_CONTEXT] ... Continue with this supervisor answer: <SUPERVISOR_ANSWER>"
304
+ })
303
305
  ```
304
306
 
305
- Reply hints work the same as regular `intercom` ask/reply flows. The supervisor replies with `intercom({ action: "reply", message: "..." })` and the subagent receives the answer as the tool result.
307
+ The generated task context includes the original delegated task, what the previous child was working on, the question, and the supervisor answer placeholder. Parallel asks do not retain active sibling sets; any follow-up is an explicit fresh launch.
306
308
 
307
309
  ### Structured Interview Replies
308
310
 
@@ -322,7 +324,7 @@ contact_supervisor({
322
324
  })
323
325
  ```
324
326
 
325
- The supervisor message includes the structured questions plus a fenced JSON answer example using this stable shape:
327
+ The handoff includes the structured questions without reordering or rewriting them. The supervisor can include a plain or fenced JSON answer in the fresh child task; this stable shape keeps answers tied to question IDs:
326
328
 
327
329
  ```json
328
330
  {
@@ -333,7 +335,7 @@ The supervisor message includes the structured questions plus a fenced JSON answ
333
335
  }
334
336
  ```
335
337
 
336
- The supervisor can reply with plain JSON or a fenced `json` block. If the reply matches the `{ "responses": [...] }` shape and references valid question ids/options, the child tool result includes it in `details.structuredReply` while still showing the raw reply text; parse errors are surfaced in `details.structuredReplyParseError`.
338
+ Atomic preserves the supplied answer in the fresh task context. The parent-ask handoff does not create an Intercom reply or a `structuredReply` tool-result field. An unclaimed fallback request keeps the existing Intercom structured-reply parsing behavior.
337
339
 
338
340
  ## Workflow and Subagent Notifications
339
341
 
@@ -359,7 +361,7 @@ workflow({
359
361
 
360
362
  When neither `enabled` nor `delivery` is set, direct `parallel` runs default to `control-and-result` when Intercom is available; otherwise delivery is off. Treat Intercom payloads from direct runs as user-visible workflow output.
361
363
 
362
- While a workflow stage generation is open, incoming Intercom messages are admitted through the stage session's native steering/follow-up queue. If that stage is busy running a foreground subagent, Atomic synchronously reserves the message in the stage generation before starting the exact child's probe/commit detach handshake. Model-visible queue insertion waits inside that reservation until detach is acknowledged or the owner is unclaimed/disappears, so terminal stage close cannot overtake and silently drop the message. The stage drains the admitted delivery before publishing its terminal snapshot. A destination-side admission failure returns a correlated actionable error to a blocking asker instead of waiting for the 10-minute reply timeout.
364
+ While a workflow stage generation is open, incoming Intercom messages are admitted through the stage session's native steering/follow-up queue. Parent-targeted blocking asks from that stage's own foreground child bypass destination delivery: the child ends at the source and returns a fresh-child handoff through the stage's `subagent` call. Other messages keep the destination-side reservation and exact-child probe/commit detach handshake, so terminal stage close cannot overtake an admitted delivery. A destination-side admission failure returns a correlated actionable error to a blocking non-parent asker instead of waiting for the 10-minute reply timeout.
363
365
 
364
366
  ### Subagent Control Notices
365
367
 
@@ -370,13 +372,13 @@ The `subagent` tool's `control` options select which control events notify the p
370
372
 
371
373
  Detached subagent result delivery over Intercom is confirmation-based and preserves a successful delivery phase across watcher replacement. Each delegated child gets a deterministic Intercom target derived from its run/agent/index identity, and run results report those targets ("Run intercom target" / "Previous intercom target"; targets may be inactive after completion). `subagent({ action: "doctor" })` reports Intercom bridge availability and whether Intercom is enabled in config.
372
374
 
373
- If live child-to-parent coordination is needed, invoke `intercom({ action: "status" })` in the parent before launching; the child connects on its first `contact_supervisor` or `intercom` call. Fresh child sessions receive the bundled Intercom wrapper through normal package discovery unless an explicit `extensions` allowlist excludes it.
375
+ If live peer coordination is needed, invoke `intercom({ action: "status" })` in the parent before launching; the child connects on its first ordinary Intercom call. A claimed `contact_supervisor` decision or interview can yield before child broker connection because typed admission already identifies the launching parent. Fresh child sessions receive the bundled Intercom wrapper through normal package discovery unless an explicit `extensions` allowlist excludes it.
374
376
 
375
377
  ### Delivery Ordering
376
378
 
377
- During a foreground subagent run, Atomic probes for the exact live foreground owner before delivery: the matching child reserves the request, accepts a generation-scoped detach commit, and acknowledges it before messages enter the parent's model-visible steering queue. A commit accepted by one member of a foreground parallel group releases supervision for all active siblings while retaining their in-process session ownership, allowing the aggregate tool call to return. If the owner disappears between probe and commit, a still-current receiver uses its ordinary fallback route rather than dropping the broker-delivered message. Blocking calls stay alive until the exact threaded reply; generation cancellation or replacement invalidates stale handshakes.
379
+ Blocking `contact_supervisor` decisions and interviews, plus `intercom.ask` calls whose resolved target is the launching parent, end at the source before Intercom send or waiter admission. The parent receives the verbatim question, ordered attachments, child identity, and fresh-start handoff. In parallel, the claim interrupts active siblings and prevents queued work from starting without retaining the sibling set. Progress updates, sends, and asks to other peers retain the probe/commit detach path.
378
380
 
379
- For delegated children, queued messages and terminal lifecycle notices are ordered per child: pre-terminal messages are admitted FIFO and atomically together with the paused, completed, or failed notice, exact terminal-identity deduplication prevents double admission, failed dispatches remain retryable, and correlated ask replies bypass unrelated queued sends. See [Subagents](/subagents) for the full coordination contract.
381
+ For delegated children, queued messages and terminal lifecycle notices remain ordered per child. Exact terminal-identity deduplication prevents double admission, failed dispatches remain retryable, and correlated ask replies bypass unrelated queued sends. See [Subagents](/subagents) for the full coordination contract.
380
382
 
381
383
  ## Configuration
382
384
 
package/docs/subagents.md CHANGED
@@ -38,17 +38,15 @@ Subagents now run and return their results directly. Atomic does not infer accep
38
38
 
39
39
  ## Foreground supervisor coordination
40
40
 
41
- When a foreground child sends `intercom.ask`, `intercom.send`, or `contact_supervisor` coordination, Atomic first probes for the exact foreground owner. Only an exact live child reserves the request; Atomic then sends a generation-scoped detach commit and waits for that child to acknowledge it before placing the message in the parent's model-visible steering queue. This first-refusal ordering also applies when the parent is a busy workflow stage: detach completes before the request enters the stage AgentSession generation boundary, breaking the child-waits-for-reply / stage-waits-for-child cycle. Blocking `need_decision` and `interview_request` calls remain actionable through [Intercom](/intercom)'s pending/reply tracker, and the exact threaded reply resumes the retained child without delayed duplicate delivery. Unmatched messages retain existing routing—ordinary parents queue until idle, while open workflow stages fall back to their native generation admission.
41
+ When a foreground child calls `contact_supervisor` with `need_decision` or `interview_request`, or uses `intercom.ask` against its resolved launching parent, Atomic claims the request before broker send or reply-waiter admission. The current child ends and the parent `subagent` call returns the original question verbatim, the child agent identity, ordered attachments with duplicates preserved, and a dynamically generated `[TASK_CONTEXT]` handoff.
42
42
 
43
- Only the matching foreground child can authorize release of the parent `subagent` tool. For a parallel foreground group, that accepted commit releases foreground supervision for every active sibling as one unit, so a long-running sibling cannot keep a blocking child request trapped behind the aggregate tool call; tasks still waiting behind the concurrency limit are skipped and never launched unsupervised. Children are in-process `AgentSession` instances governed by the shared Rust control plane: there is no child OS process, idle watchdog, stdout drain, or detached placeholder to recover. A detached call becomes `continued` through `continue_detached`; its canonical child remains live and later delivers one terminal result. Fire-and-forget `intercom.send` and `progress_update` also release foreground supervision promptly, but do not create a reply waiter.
43
+ The handoff explicitly tells the parent to start a fresh child with a normal launch such as `subagent({ agent: "worker", task: "[TASK_CONTEXT] ... Continue with this supervisor answer: ..." })`. The new child receives a new run identity. Completed, interrupted, and parent-question children are terminal for continuation; a prior run ID cannot revive one.
44
44
 
45
- Blocking coordination is race-safe: a session holds at most one outbound reply waiter, and concurrent blocking requests (parallel `intercom.ask` calls, or `intercom.ask` racing `contact_supervisor`) settle atomically. One request wins the reservation; every other concurrent call returns a normal "Already waiting for a reply" tool error without crashing the agent process or disturbing the pending ask. Cancellation and send failures release only their own waiter, and threaded replies still resolve the exact winning request.
45
+ For a parallel foreground run, one claimed parent ask interrupts the active siblings and closes the worker gate. Tasks still queued behind the concurrency limit never launch or request supervisor authorization. No sibling set or worktree/session execution state is retained for later continuation. Follow-up work is launched explicitly as fresh SINGLE or PARALLEL work with the necessary context.
46
46
 
47
- Subagent result announcements are also resilient in sessions that never receive an extension `session_start` (for example non-interactive in-process child sessions): the lazy Intercom runtime initializes from the most recent turn/tool lifecycle context and delivers self-addressed results locally. If no context is available at all, the relay acknowledges the announcement as undelivered the `subagent` tool then falls back to returning results inline — instead of recording connection errors in the session transcript.
47
+ `intercom.send`, `contact_supervisor` progress updates, and `intercom.ask` calls resolved to a sibling or other peer keep their existing Intercom delivery path. Non-parent blocking asks keep the single race-safe reply-waiter slot and exact threaded replies.
48
48
 
49
- Intercom connection remains tool-driven. Foreground launches do not import the heavy Intercom runtime or connect either the parent or bridged child automatically. If live child-to-parent coordination is needed, the parent model should invoke `intercom({ action: "status" })` before launch; the child then connects on its first `contact_supervisor` or `intercom` call. Cancellation or session replacement still invalidates the handshake generation, so stale acknowledgements cannot surface or detach a child.
50
-
51
- Atomic's implementation adapts the prompt foreground release and later-result recovery contracts proven in `nicobailon/pi-subagents` commits `1b55c8c`, `589e51e`, `68fb528`, and `9dfe3df`; it retains Atomic's broker and raw-TypeScript architecture rather than copying upstream's filesystem transport.
49
+ When the Intercom bridge is active, the parent may connect long enough to issue the initial child capability; the child's own connection remains tool-driven. A claimed parent decision or interview ends before child send or waiter admission. Non-interactive children still run normal extension lifecycle and remain in-process `AgentSession` instances while live.
52
50
 
53
51
  ## Migration from acceptance gates
54
52
 
@@ -118,9 +116,9 @@ Tool examples:
118
116
  subagent({ agent: "codebase-analyzer", task: "Trace the auth flow with file references." })
119
117
  ```
120
118
 
121
- Use `interrupt` when you want a resumable stop. Use `resume` for a follow-up to a reachable or retained child. Use `doctor` for read-only setup diagnostics.
119
+ Use `interrupt` to stop a live child. Interrupted children are terminal for continuation; launch a fresh child with an explicit context handoff for follow-up work. Use `doctor` for read-only setup diagnostics.
122
120
 
123
- Status, interrupt, list, and resume use the Rust registry and status watch for live children; terminal delivery is an in-memory bounded envelope with the artifact and run-history record persisted once. There is no PID polling, result-claim file, stale-run reconciliation, or detached runner process.
121
+ Status and interrupt use the live Rust registry and status watch; list and doctor remain read-only management actions. No retained foreground-run map, resume generation, session rehydration, or bare-run-ID continuation exists. Terminal delivery remains an in-memory bounded envelope with artifacts and run history persisted once.
124
122
 
125
123
  Inside workflow stages, completion delivery observes the stage generation boundary. A completion received before the boundary closes is queued through the stage AgentSession and processed before the stage publishes its terminal snapshot. A completion that arrives after close is routed once to the parent/main chat and cannot reopen or append to the completed stage transcript. Explicit post-mortem stage chat is still available separately.
126
124
 
@@ -149,13 +147,19 @@ Fresh child sessions use normal Atomic package discovery when an agent omits `ex
149
147
 
150
148
  Top-level parallel calls support up to 50 subagents after expanding each task's optional `count`. The extension's `parallel.maxTasks` setting defaults to 50 and can enforce a lower task limit; `parallel.concurrency` independently controls how many of those children run at once, while the Rust turn limiter admits at most four running turns per parent.
151
149
 
150
+ When one assistant response emits several sibling execution-mode `subagent` tool calls, Atomic collects that synchronous burst before starting a child and runs it as one indexed parallel set. Each original tool call still receives one result containing only the children it requested, and its live result, progress, control, and artifact updates are projected to that same route without sibling data. The TUI redraws the shared run as one aggregate parallel widget rather than retaining one widget per original call. A single call keeps its original SINGLE or PARALLEL mode, calls awaited in sequence remain separate runs, and management actions bypass collection. An execution call that arrives after a child has started still receives the existing in-progress rejection. Prefer one explicit `{ tasks: [...] }` call when planning parallel work; burst collection handles sibling calls emitted by a model.
151
+
152
+ For a collected burst, each call contributes its top-level `agent` task first and then its `tasks` entries in array order. Atomic preserves duplicates, expands `count` in place, and applies the configured task cap after flattening and expansion; the hard maximum remains 50. Each call-level `cwd` selects that call's agent-discovery scope and child base directory. A task-level `cwd` stays relative to that call base and changes only that child's execution directory, not agent discovery. This per-origin discovery rule applies only to collected sibling calls; an ordinary explicit `{ tasks: [...] }` call keeps one discovery scope from its top-level `cwd`. Per-call and per-task `group` values also stay with their originating children. Shared run options must match across every sibling call: `concurrency`, `worktree`, `context`, `share`, `control`, `sessionDir`, `maxOutput`, `artifacts`, `includeProgress`, and `agentScope`. A mismatch rejects the whole burst before any child launches and names the incompatible field.
153
+
154
+ For a collected `worktree: true` burst, every call-level `cwd` must resolve to the same path. That common path becomes the shared worktree root; differing origins reject the burst before launch, and any task-level `cwd` must still resolve to that root. Each projected caller result keeps shared worktree diff text and terminal control guidance while its child results and standard child-output sections remain route-local.
155
+
152
156
  Subagent tasks, parallel items, and the top-level call accept a `group` field that sets the spawned child's [Intercom](/intercom) home group, so same-group subagents can intercom each other while staying isolated from other groups. A named string joins that group; `true` auto-generates one shared UUID group per parallel set. Precedence is `explicit subagent group > inherited current-session group > config > "default"`. Workflow stages carry their runtime-owned invocation group, so children launched without `group` automatically join the workflow group; callers do not need to copy or generate an ID. In other sessions, omission inherits that launching session's resolved group. The child group is applied only when the child has Intercom access (the peer `intercom` tool or subagent-only `contact_supervisor` tool); a child without Intercom receives no group. `contact_supervisor` still reaches the supervisor across group boundaries because Atomic requests a broker capability during typed admission and binds the child's registration to the issuing supervisor. Foreground paths use exact child scopes. The lightweight Intercom wrapper lazy-loads the authorization provider; provider failures abort launch, while hosts without a provider omit supervisor metadata instead of exposing a broken channel.
153
157
 
154
158
  When a subagent call or parallel task uses a `cwd`, Atomic validates that working directory before starting the child runtime. Missing or non-directory paths are reported as `cwd` problems instead of lower-level runtime errors.
155
159
 
156
160
  Single-agent calls also accept `reads: string[] | false`. Atomic prepends those files as read context for foreground execution through the same in-process session path, including `/run agent[reads=a.md+b.md]`. Relative entries resolve against the effective child `cwd` (including a relative top-level `cwd` resolved from the parent); absolute entries are unchanged. Invalid values fail before the child session starts.
157
161
 
158
- Single-agent calls accept `progress: boolean` in foreground and resumed mode. `progress: true` creates a run-scoped `progress.md` under isolated subagent artifact storage and instructs the child to maintain it without writing `progress.md` into the child `cwd`; `progress: false` disables an agent's `defaultProgress`. When `progress` is omitted, the agent's default is inherited, except that inherited progress is suppressed for read-only tasks (`progress: true` still explicitly opts in). Foreground runs remove this run-owned progress storage after the child exits when `artifacts: false`, including children temporarily detached for intercom coordination. This is separate from `includeProgress: true`, which only includes detailed runtime progress telemetry in the final tool result and does not create or maintain a file.
162
+ Single-agent calls accept `progress: boolean` in foreground mode. `progress: true` creates a run-scoped `progress.md` under isolated subagent artifact storage and instructs the child to maintain it without writing `progress.md` into the child `cwd`; `progress: false` disables an agent's `defaultProgress`. When `progress` is omitted, the agent's default is inherited, except that inherited progress is suppressed for read-only tasks (`progress: true` still explicitly opts in). Foreground runs remove this run-owned progress storage after the child exits when `artifacts: false`, including children temporarily detached for intercom coordination. This is separate from `includeProgress: true`, which only includes detailed runtime progress telemetry in the final tool result and does not create or maintain a file.
159
163
 
160
164
  ```ts
161
165
  subagent({ agent: "worker", task: "Implement the approved fix.", progress: true })
@@ -168,7 +172,7 @@ Child-safety boundaries are enforced by typed admission policy and the bundled s
168
172
  - In-process child sessions load bundled extensions through normal discovery. The `subagent` tool may therefore be registered when the child's active tool selection permits it, including the default no-allowlist case; an explicit allowlist may omit it. Tool presence does not grant fanout. The bundled subagents skill remains parent-only and is stripped from child prompts, including fanout-authorized children.
169
173
  - Child context is filtered to remove parent orchestration artifacts, old control/status messages, and prior parent `subagent` tool calls/results.
170
174
  - Children are instructed that they are not the parent orchestrator and must complete their assigned task directly rather than delegating.
171
- - Delegation is exactly one level deep and is not configurable. A session admitted as a subagent child is refused every launch, `resume`, and `interrupt`; only `list`, `get`, `status`, and `doctor` stay available. A management-restricted child is also refused `create`, `update`, and `delete`.
175
+ - Delegation is exactly one level deep and is not configurable. A session admitted as a subagent child is refused every launch and `interrupt`; only `list`, `get`, `status`, and `doctor` stay available. A management-restricted child is also refused `create`, `update`, and `delete`.
172
176
  - The rule is enforced twice: the subagent executor refuses a child before any run starts, and the Rust admission door refuses a child deeper than the single permitted level. Admitted depth is typed admission state, never inherited from process environment state.
173
177
 
174
178
  This keeps the parent session responsible for orchestration.
package/docs/workflows.md CHANGED
@@ -117,7 +117,7 @@ Named workflow runs execute in the background. By default, after launch expect a
117
117
 
118
118
  For a request with several implementation items, do not turn list order into one serial workflow by default. Triage dependencies first, then launch independent items as a bounded wave of separate top-level runs; see [Task queues and software factories](#task-queues-and-software-factories).
119
119
 
120
- While a workflow is running, the visible below-editor `BACKGROUND` panel advances its elapsed label every second from the moment the run starts; it does not require opening or switching to the orchestrator. Updates repaint the existing mounted panel in place, paused timers stay frozen, the panel renders every qualifying top-level run, and terminal or quit cards retain their brief recent-run expiry. Quit cards remain resumable and discoverable with `/workflow status` after they leave the panel. A run waiting for human input uses the blue `?` indicator in the BACKGROUND panel, the `/workflow connect` picker, and the `/workflow status` listing; answering or cancelling the prompt restores the run's current indicator.
120
+ While a workflow is running, the visible below-editor `BACKGROUND` panel advances its elapsed label every second from the moment the run starts; it does not require opening or switching to the orchestrator. Updates repaint the existing mounted panel in place, paused timers stay frozen, the panel renders every qualifying top-level run, and terminal or quit cards retain their brief recent-run expiry. A zero-stage workflow whose work consists only of `ctx.tool(...)` calls mounts the same panel without a synthetic stage: at normal widths its run metadata reports the live-tool total when more than one is active, followed by pending and running durable tool-node names and statuses as space permits; the collapsed narrow form reports only the number of live tools. Quit cards remain resumable and discoverable with `/workflow status` after they leave the panel. A run waiting for human input uses the blue `?` indicator in the BACKGROUND panel, the `/workflow connect` picker, and the `/workflow status` listing; answering or cancelling the prompt restores the run's current indicator.
121
121
 
122
122
  ### Workflow run identifiers and the BACKGROUND panel
123
123
 
@@ -125,7 +125,7 @@ Workflow run identifiers are shown in full everywhere they are presented to user
125
125
 
126
126
  Stage targeting is exact but not UUID-bound, because stage identifiers are not all bare UUIDs. A `stageId` resolves by exact stage id — a bare UUID at the root, the full `runId:stageId` composite for a stage inside a nested workflow, or `tool:<argsHash>` for a `ctx.tool` node — or by exact stage or tool name. Partial names no longer match, so `build` will not select `build-check`. Two stages that share an exact name are still reported as ambiguous, listing the full matching identifiers.
127
127
 
128
- At 80 columns and wider, each `BACKGROUND` card uses two rows so the id is not squeezed beside the workflow name: the first row contains the status glyph and full UUID, and the second contains the workflow name followed by its mode, progress, and elapsed/status metadata. The panel renders every qualifying top-level run, so each card is two rows high (plus the existing spacing between cards). Below 80 columns, the panel keeps its collapsed count-only form and does not render an id.
128
+ At 80 columns and wider, each `BACKGROUND` card uses two rows so the id is not squeezed beside the workflow name: the first row contains the status glyph and full UUID, and the second contains the workflow name followed by its mode, progress, live-tool total when more than one is active, pending/running `ctx.tool` node names and statuses as space permits, and elapsed/status metadata. Tool nodes are read-only durable graph nodes, not attachable stage chats. The panel renders every qualifying top-level run, so each card is two rows high (plus the existing spacing between cards). Below 80 columns, the panel keeps its collapsed form, omits ids and tool names, and includes a live-tool count when one or more tool nodes are pending or running.
129
129
 
130
130
  For chat surfaces such as workflow status, run detail, dispatch confirmation, and the run picker, a full id wraps onto continuation rows when the card is narrower than the id. The renderer never ellipsizes the id and keeps the card border closed at its minimum layout width, while terminals below that floor — including sub-30-column terminals — can hard-clip the box. An awaiting-input attribution banner is titled `AWAITING INPUT` and contains the same two identity rows — `?` plus the full run id, then the workflow name and optional metadata — while the existing prompt question and options remain below it in the normal prompt UI.
131
131
 
@@ -141,7 +141,7 @@ The rendered card shape at the 80-column breakpoint is:
141
141
  │ build-check · chain · 0/2 · 12m │
142
142
  ```
143
143
 
144
- Below the breakpoint the same run set is represented by the collapsed count line, for example ` ▾ 4 background · 2 ● · 1 quit`.
144
+ Below the breakpoint the same run set is represented by the collapsed count line, for example ` ▾ 4 background · 2 ● · 1 quit`; a tool-only run adds its live count, for example ` ▾ 1 background · 1 ● · 1 tool`.
145
145
 
146
146
  ### Or hand-write the TypeScript
147
147
 
@@ -2456,7 +2456,7 @@ readonly excludedTools?: readonly string[];
2456
2456
 
2457
2457
  `tools` is an allowlist across built-in and bundled extension tools; list every tool the stage should see. `excludedTools` and `noTools: "all"` still win.
2458
2458
 
2459
- The bundled `subagent` tool is available by default on the same terms as main chat. A workflow stage is a top-level session, so it may delegate once; the children it launches may not delegate at all. Delegation is exactly one level deep and nothing configures it — there is no config option, agent frontmatter field, or tool parameter for the level. The in-process admission door carries each childs issued depth in its typed child policy, the executor refuses any launch, `resume`, or `interrupt` from a session that was itself admitted as a child, and the Rust `SubagentControl` admission door refuses a child deeper than the single permitted level. That depth is never carried through process environment. Bundled subagent definitions from `@bastani/subagents` are available to that tool. Explicitly list tools such as `subagent`, `web_search`, `fetch_content`, or `intercom` when using an allowlist; in-process child sessions load the bundled resources while suppressing the workflow extension lifecycle.
2459
+ The bundled `subagent` tool is available by default on the same terms as main chat. A workflow stage is a top-level session, so it may delegate once; the children it launches may not delegate or control another child. Delegation is exactly one level deep and nothing configures it — there is no config option, agent frontmatter field, or tool parameter for the level. The in-process admission door carries each child's issued depth in its typed child policy, the executor refuses any launch or `interrupt` from a session that was itself admitted as a child, and the Rust `SubagentControl` admission door refuses a child deeper than the single permitted level. That depth is never carried through process environment. Bundled subagent definitions from `@bastani/subagents` are available to that tool. Explicitly list tools such as `subagent`, `web_search`, `fetch_content`, or `intercom` when using an allowlist; in-process child sessions load the bundled resources while suppressing the workflow extension lifecycle.
2460
2460
 
2461
2461
  Workflow stages use the same upstream-compatible `bash` tool as normal Atomic sessions. Enabled commands run through the configured shell with the stage process permissions. There is no command-text allow/deny option: expose or hide shell access with these tool fields, prefer narrow custom tools for repeatable operations, and use a container, VM, or other sandbox for stronger isolation.
2462
2462
 
@@ -2997,6 +2997,21 @@ The workflow tool action surface is:
2997
2997
  - messaging on nonterminal root runs and run control: `send`, `pause`, `interrupt`, `quit`, `resume`
2998
2998
  - rediscovery: `reload`
2999
2999
 
3000
+ Every registered `workflow` tool call has one hard 30-second wall-clock deadline at the shared public tool boundary. The deadline covers request handling through the returned result; for background `run` and `resume`, it therefore covers startup/resume admission and acknowledgement only, not the workflow execution that continues after acknowledgement. A deadline returns one structured result:
3001
+
3002
+ ```json
3003
+ {
3004
+ "action": "run",
3005
+ "runId": "339e05a4-2289-408e-9076-d1a348f582ae",
3006
+ "status": "failed",
3007
+ "code": "WORKFLOW_TIMEOUT",
3008
+ "timeoutMs": 30000,
3009
+ "error": "Workflow run request timed out after 30000ms. The outcome is unknown. Inspect workflow status before retrying."
3010
+ }
3011
+ ```
3012
+
3013
+ Expiry aborts the request operation signal so work that supports cancellation can stop, discards any later success or error, and never retries the action. The interactive engine remains available for the next command. For mutating actions (`reload`, `run`, `send`, `pause`, `resume`, `interrupt`, and `quit`), the error additionally says that the outcome is unknown and instructs you to inspect workflow status before retrying; a timeout never claims that a mutation succeeded. When a timed-out `run` has already allocated its detached run, the structured result includes that exact full `runId`; inspect `status` with that id before any retry. A timeout before run allocation has no `runId`. Read-only actions (`models`, `list`, `get`, `inputs`, `status`, `stages`, `stage`, and `transcript`) omit that unknown-state guidance.
3014
+
3000
3015
  From interactive chat, named workflow launches run in the background so the parent chat stays available. Run `/workflow connect <run>` to see agents working and chat with and steer each stage. Inspection and control calls (`status`, `stages`, `stage`, `transcript`, `send`, `pause`, `resume`, `interrupt`, `quit`) remain available while work runs.
3001
3016
 
3002
3017
  `workflow({ action: "models" })` returns the registry's configured-auth catalog snapshot in registry order. Each entry includes `provider`, `id`, `fullId`, an `isCurrent` marker, and `availableThinkingLevels` derived from the real model's `reasoning` and `thinkingLevelMap` metadata. This is not proof of credentials, entitlements, OAuth freshness, or live provider access, and it exposes no authentication details.
@@ -3220,7 +3235,7 @@ The target sees the original ask, and its normal `intercom.reply` remains correl
3220
3235
  This reopens only the conversation. The workflow DAG and terminal stage snapshot remain completed and are never resumed or re-dispatched. If the target run or stage was deleted, lacks a valid retained conversation, is non-resumable, or fails to reopen, the caller receives a bounded actionable `intercom.ask` tool error instead of waiting indefinitely.
3221
3236
 
3222
3237
 
3223
- Workflow stage sessions and first-party subagent transcripts created inside them are classified as **internal** at creation and excluded from the standard `/resume`, `atomic -r`, `--continue`, and global history surfaces. Fork-context stages and subagents inherit the owning run/stage marker in their initial JSONL header, avoiding a briefly visible ordinary session. They remain resumable and inspectable through the workflow-specific commands and tool actions shown here (`/workflow resume`, `/workflow attach`, `workflow({ action: "status" | "stages" | "stage" | "resume" })`), which read the run/stage store and its `sessionFile` links directly.
3238
+ Workflow stage sessions and first-party subagent transcripts created inside them are classified as **internal** at creation and excluded from the standard `/resume`, `atomic -r`, `--continue`, and global history surfaces. Fork-context stages and subagents inherit the owning run/stage marker in their initial JSONL header, avoiding a briefly visible ordinary session. Workflow stage sessions remain resumable and inspectable through the workflow-specific commands and tool actions shown here (`/workflow resume`, `/workflow attach`, `workflow({ action: "status" | "stages" | "stage" | "resume" })`), which read the run/stage store and its `sessionFile` links directly. Subagent transcripts remain artifacts only; no workflow command revives their terminal child identities.
3224
3239
 
3225
3240
  Passing a stage session's file path to `--session` still opens it explicitly. Classification requires exact `internal: true` plus complete run/stage metadata; malformed legacy markers and ordinary user forks remain in standard history. Legacy workflow sessions created before this marker behavior lack provable ownership and continue to appear until they age out.
3226
3241
 
@@ -3287,13 +3302,13 @@ The readiness prompt can be answered in the attached stage UI or with `workflow(
3287
3302
 
3288
3303
  ## Durable Workflows and Cross-Session Resume
3289
3304
 
3290
- Atomic workflows use **DBOS/Postgres as their sole persistent workflow backend**. Atomic configures and launches DBOS lazily on the first workflow action, reuses that process-wide instance, and awaits readiness before workflow execution, resume, inspection, or deletion can access durable state. `DBOS_SYSTEM_DATABASE_URL` may select an existing database; DBOS query and write failures fail the workflow action and never select another backend.
3305
+ Atomic workflows use **DBOS/Postgres as their sole persistent workflow backend**. Atomic configures and launches DBOS lazily on the first workflow action, reuses that process-wide instance, and awaits readiness before workflow execution, resume, inspection, or deletion can access durable state. `DBOS_SYSTEM_DATABASE_URL` may select an existing database. Once DBOS is ready, query and write failures fail the workflow action and never switch backends.
3291
3306
 
3292
- **Zero-configuration local database.** Without `DBOS_SYSTEM_DATABASE_URL`, Atomic runs DBOS against its own embedded Postgres built from npm-distributed binaries — no Docker daemon or system Postgres install. The cluster lives under `~/.atomic/postgres/v18` on dedicated port `5439`; the first workflow action initializes it once and starts it with `pg_ctl` as a detached daemon that survives Atomic exiting, is shared by every concurrent Atomic session, and is never stopped by Atomic.
3307
+ **Zero-configuration local database.** Without `DBOS_SYSTEM_DATABASE_URL`, Atomic runs DBOS against its own embedded Postgres built from npm-distributed binaries — no Docker daemon or system Postgres install. The cluster lives under `~/.atomic/postgres/v18` on dedicated port `5439`; the first workflow action initializes it once and starts `postgres` directly behind an opaque retained native process lease. Concurrent Atomic sessions may attach to the same cluster, and an abrupt process exit releases the lease without killing Postgres. During orderly durable shutdown, only the process holding that exact lease sends fast shutdown and waits for the retained process; attached or replacement clusters are left untouched.
3293
3308
 
3294
- **Running as root (Linux).** PostgreSQL refuses to run as UID 0, so a root Atomic process (containers, CI sandboxes, eval harnesses) resolves an unprivileged system account (`postgres`, `nobody`, or `daemon`), keeps the cluster under `/var/lib/atomic-postgres` instead (a root home directory is untraversable for that account), and runs every Postgres command with dropped privileges. When the embedded binaries themselves sit under an untraversable prefix (for example a root-owned `~/.nvm` global install), Atomic copies the Postgres runtime into the cluster directory once and reuses it.
3309
+ **Running as root (Linux).** PostgreSQL refuses to run as UID 0, so a root Atomic process (containers, CI sandboxes, eval harnesses) resolves an unprivileged system account (`postgres`, `nobody`, or `daemon`) and keeps the cluster under `/var/lib/atomic-postgres` instead (a root home directory is untraversable for that account). Before any owner command runs, Atomic probes that candidate runner itself and accepts it only when it proves the account's exact UID, exact primary GID, membership in that primary group, and no root group; legitimate additional nonroot groups remain valid. The retained native direct-Postgres spawn also clears inherited supplementary groups before setting the primary GID and UID. When the embedded binaries themselves sit under an untraversable prefix (for example a root-owned `~/.nvm` global install), Atomic publishes and reuses one exact package-content runtime generation under a root-owned cache. Published runtime files remain readable/executable but not writable by the Postgres account. Runtime reuse and publication re-snapshot the current source, publication validates the deterministic path after rename, and source mutation, corrupt content, or setup-lease displacement fails closed without unbounded repair copies.
3295
3310
 
3296
- When the embedded binaries are unavailable for the platform, Atomic falls back to DBOS's reusable `dbos-db` Docker container. If no durable backend can be provisioned at all, workflows **degrade to a process-local in-memory backend with a loud warning** instead of refusing to run: the run executes normally, but its state does not survive the process and `/workflow resume` after exit has nothing to restore. Set `DBOS_SYSTEM_DATABASE_URL` to an existing Postgres to restore durability.
3311
+ If embedded provisioning fails without leaving retained-process cleanup pending, Atomic tries DBOS's reusable `dbos-db` Docker container. If DBOS still cannot become ready, workflows **degrade to a process-local in-memory backend with a loud warning** instead of refusing to run: the run executes normally, but its state does not survive the process and `/workflow resume` after exit has nothing to restore. Fix the configured database or set `DBOS_SYSTEM_DATABASE_URL` to a working Postgres to restore durability.
3297
3312
 
3298
3313
  **Multiple concurrent Atomic sessions.** Every Atomic process launches DBOS with a unique executor id, and running root workflows carry owner/heartbeat metadata. Once an active model stage has a session path, Atomic records that identity after the stage-start record and awaits the checkpoint before the first model use, then runs serialized, unref'd liveness checkpoints on a bounded 30-second cadence for the root and nested scoped workflows. Each accepted checkpoint refreshes root metadata; timers stop on every stage exit and cannot keep Atomic alive. A persistent checkpoint fault fails the active stage instead of disappearing in a detached timer. A stage that is shutting down drains the checkpoint still in flight rather than abandoning it, so a failure that lands after the model turn finished is reported instead of discarded, and a stage whose final durability checkpoint fails is recorded as `failed` rather than `completed` — its caller receives the error and its concurrency slot is released either way. **Running workflows are never resume targets**: a running row with a fresh heartbeat is hidden from every session's picker and refused by direct `/workflow resume <id>` — resuming a workflow that is executing elsewhere would double-dispatch it. Once the heartbeat goes stale (about two minutes after a crash), an exact inspection or the resume picker reports the workflow as `crashed`.
3299
3314
 
@@ -3515,7 +3530,9 @@ Atomic loads workflow files with [jiti](https://github.com/unjs/jiti), so TypeSc
3515
3530
 
3516
3531
  Run `/workflow reload` after adding, editing, renaming, or deleting workflow modules or changing workflow config. Reload rescans project and user conventional directories, legacy `.pi` locations, configured file/directory paths, and package resources without restarting Atomic. The workflow tool's `reload` action uses the same in-process path.
3517
3532
 
3518
- Reload builds a complete replacement registry before publishing it. Concurrent requests are serialized and coalesced, stale discovery from an earlier session cannot overwrite newer state, and a fatal refresh failure retains the previous registry. Reload is safe while workflows are running: existing runs keep the definition and runtime snapshot they started with, while subsequent list/get/inputs/help/completion/invocation calls use the newly published registry.
3533
+ Reload builds a complete replacement registry before publishing it. Concurrent requests are serialized and coalesced, stale discovery from an earlier session cannot overwrite newer state, and a fatal refresh failure retains the previous registry. Reload is safe while workflows are running: existing runs keep the definition and runtime snapshot they started with, their mounted `BACKGROUND` card and live tool-node metadata keep updating in place, and subsequent list/get/inputs/help/completion/invocation calls use the newly published registry.
3534
+
3535
+ The top-level `/reload` command replaces the extension generation as well as rediscovering resources. Within the same Atomic process, the replacement workflows extension adopts the current session's run store and control registries before installing its UI, then remounts the below-editor panel from the adopted snapshot. Installed builds can evaluate the extension and host SDK through separate jiti module copies; Atomic canonicalizes each generation's `pi.events` facade through a process-shared session-bus map so both copies adopt the same store, job, cancellation, and tool-control owners. In-flight `ctx.tool` callbacks and their durable node controls therefore remain owned by that live run and may settle normally after reload; completed siblings remain checkpointed. A run or active tool node can also arrive after the UI is installed through durability hydration, which invalidates the store and mounts or updates the panel immediately. This same-process handoff is distinct from a process crash: after a real process exit there is no live callback to preserve, so explicit `/workflow resume` replays checkpoints and re-executes only unfinished tool work.
3519
3536
 
3520
3537
  The `/workflow` argument-completion popup reads that same live registry. Project, user, package-provided, and built-in workflow names therefore appear immediately after reload both after `/workflow ` and after `/workflow inputs `; restarting Atomic is not required.
3521
3538
 
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@bastani/atomic",
3
- "version": "0.9.15",
3
+ "version": "0.9.16-alpha.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bastani/atomic",
9
- "version": "0.9.15",
9
+ "version": "0.9.16-alpha.1",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
- "@bastani/atomic-natives": "0.9.15",
12
+ "@bastani/atomic-natives": "0.9.16-alpha.1",
13
13
  "@dbos-inc/dbos-sdk": "4.25.14",
14
14
  "@earendil-works/pi-agent-core": "^0.84.2",
15
- "@bastani/pi-ai": "0.9.15",
15
+ "@bastani/pi-ai": "0.9.16-alpha.1",
16
16
  "@earendil-works/pi-client": "^0.84.2",
17
17
  "@earendil-works/pi-protocol": "^0.84.2",
18
18
  "@earendil-works/pi-tui": "^0.84.2",
@@ -517,18 +517,18 @@
517
517
  }
518
518
  },
519
519
  "node_modules/@bastani/atomic-natives": {
520
- "version": "0.9.15",
521
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives/-/atomic-natives-0.9.15.tgz",
520
+ "version": "0.9.16-alpha.1",
521
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives/-/atomic-natives-0.9.16-alpha.1.tgz",
522
522
  "license": "MIT",
523
523
  "optionalDependencies": {
524
- "@bastani/atomic-natives-darwin-arm64": "0.9.15",
525
- "@bastani/atomic-natives-darwin-x64": "0.9.15",
526
- "@bastani/atomic-natives-linux-arm64-gnu": "0.9.15",
527
- "@bastani/atomic-natives-linux-arm64-musl": "0.9.15",
528
- "@bastani/atomic-natives-linux-x64-gnu": "0.9.15",
529
- "@bastani/atomic-natives-linux-x64-musl": "0.9.15",
530
- "@bastani/atomic-natives-win32-arm64-msvc": "0.9.15",
531
- "@bastani/atomic-natives-win32-x64-msvc": "0.9.15"
524
+ "@bastani/atomic-natives-darwin-arm64": "0.9.16-alpha.1",
525
+ "@bastani/atomic-natives-darwin-x64": "0.9.16-alpha.1",
526
+ "@bastani/atomic-natives-linux-arm64-gnu": "0.9.16-alpha.1",
527
+ "@bastani/atomic-natives-linux-arm64-musl": "0.9.16-alpha.1",
528
+ "@bastani/atomic-natives-linux-x64-gnu": "0.9.16-alpha.1",
529
+ "@bastani/atomic-natives-linux-x64-musl": "0.9.16-alpha.1",
530
+ "@bastani/atomic-natives-win32-arm64-msvc": "0.9.16-alpha.1",
531
+ "@bastani/atomic-natives-win32-x64-msvc": "0.9.16-alpha.1"
532
532
  },
533
533
  "engines": {
534
534
  "bun": ">=1.4.0",
@@ -536,8 +536,8 @@
536
536
  }
537
537
  },
538
538
  "node_modules/@bastani/atomic-natives-darwin-arm64": {
539
- "version": "0.9.15",
540
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-darwin-arm64/-/atomic-natives-darwin-arm64-0.9.15.tgz",
539
+ "version": "0.9.16-alpha.1",
540
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-darwin-arm64/-/atomic-natives-darwin-arm64-0.9.16-alpha.1.tgz",
541
541
  "license": "MIT",
542
542
  "os": [
543
543
  "darwin"
@@ -548,8 +548,8 @@
548
548
  "optional": true
549
549
  },
550
550
  "node_modules/@bastani/atomic-natives-darwin-x64": {
551
- "version": "0.9.15",
552
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-darwin-x64/-/atomic-natives-darwin-x64-0.9.15.tgz",
551
+ "version": "0.9.16-alpha.1",
552
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-darwin-x64/-/atomic-natives-darwin-x64-0.9.16-alpha.1.tgz",
553
553
  "license": "MIT",
554
554
  "os": [
555
555
  "darwin"
@@ -560,8 +560,8 @@
560
560
  "optional": true
561
561
  },
562
562
  "node_modules/@bastani/atomic-natives-linux-arm64-gnu": {
563
- "version": "0.9.15",
564
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-arm64-gnu/-/atomic-natives-linux-arm64-gnu-0.9.15.tgz",
563
+ "version": "0.9.16-alpha.1",
564
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-arm64-gnu/-/atomic-natives-linux-arm64-gnu-0.9.16-alpha.1.tgz",
565
565
  "license": "MIT",
566
566
  "os": [
567
567
  "linux"
@@ -575,8 +575,8 @@
575
575
  "optional": true
576
576
  },
577
577
  "node_modules/@bastani/atomic-natives-linux-arm64-musl": {
578
- "version": "0.9.15",
579
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-arm64-musl/-/atomic-natives-linux-arm64-musl-0.9.15.tgz",
578
+ "version": "0.9.16-alpha.1",
579
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-arm64-musl/-/atomic-natives-linux-arm64-musl-0.9.16-alpha.1.tgz",
580
580
  "license": "MIT",
581
581
  "os": [
582
582
  "linux"
@@ -590,8 +590,8 @@
590
590
  "optional": true
591
591
  },
592
592
  "node_modules/@bastani/atomic-natives-linux-x64-gnu": {
593
- "version": "0.9.15",
594
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-x64-gnu/-/atomic-natives-linux-x64-gnu-0.9.15.tgz",
593
+ "version": "0.9.16-alpha.1",
594
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-x64-gnu/-/atomic-natives-linux-x64-gnu-0.9.16-alpha.1.tgz",
595
595
  "license": "MIT",
596
596
  "os": [
597
597
  "linux"
@@ -605,8 +605,8 @@
605
605
  "optional": true
606
606
  },
607
607
  "node_modules/@bastani/atomic-natives-linux-x64-musl": {
608
- "version": "0.9.15",
609
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-x64-musl/-/atomic-natives-linux-x64-musl-0.9.15.tgz",
608
+ "version": "0.9.16-alpha.1",
609
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-x64-musl/-/atomic-natives-linux-x64-musl-0.9.16-alpha.1.tgz",
610
610
  "license": "MIT",
611
611
  "os": [
612
612
  "linux"
@@ -620,8 +620,8 @@
620
620
  "optional": true
621
621
  },
622
622
  "node_modules/@bastani/atomic-natives-win32-arm64-msvc": {
623
- "version": "0.9.15",
624
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-win32-arm64-msvc/-/atomic-natives-win32-arm64-msvc-0.9.15.tgz",
623
+ "version": "0.9.16-alpha.1",
624
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-win32-arm64-msvc/-/atomic-natives-win32-arm64-msvc-0.9.16-alpha.1.tgz",
625
625
  "license": "MIT",
626
626
  "os": [
627
627
  "win32"
@@ -632,8 +632,8 @@
632
632
  "optional": true
633
633
  },
634
634
  "node_modules/@bastani/atomic-natives-win32-x64-msvc": {
635
- "version": "0.9.15",
636
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-win32-x64-msvc/-/atomic-natives-win32-x64-msvc-0.9.15.tgz",
635
+ "version": "0.9.16-alpha.1",
636
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-win32-x64-msvc/-/atomic-natives-win32-x64-msvc-0.9.16-alpha.1.tgz",
637
637
  "license": "MIT",
638
638
  "os": [
639
639
  "win32"
@@ -644,8 +644,8 @@
644
644
  "optional": true
645
645
  },
646
646
  "node_modules/@bastani/pi-ai": {
647
- "version": "0.9.15",
648
- "resolved": "https://registry.npmjs.org/@bastani/pi-ai/-/pi-ai-0.9.15.tgz",
647
+ "version": "0.9.16-alpha.1",
648
+ "resolved": "https://registry.npmjs.org/@bastani/pi-ai/-/pi-ai-0.9.16-alpha.1.tgz",
649
649
  "license": "MIT",
650
650
  "dependencies": {
651
651
  "@anthropic-ai/sdk": "0.91.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/atomic",
3
- "version": "0.9.15",
3
+ "version": "0.9.16-alpha.1",
4
4
  "description": "Atomic coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "atomicConfig": {
@@ -83,10 +83,10 @@
83
83
  "prepublishOnly": "bun run clean && bun run build && bun run shrinkwrap"
84
84
  },
85
85
  "dependencies": {
86
- "@bastani/atomic-natives": "0.9.15",
86
+ "@bastani/atomic-natives": "0.9.16-alpha.1",
87
87
  "@dbos-inc/dbos-sdk": "4.25.14",
88
88
  "@earendil-works/pi-agent-core": "^0.84.2",
89
- "@bastani/pi-ai": "0.9.15",
89
+ "@bastani/pi-ai": "0.9.16-alpha.1",
90
90
  "@earendil-works/pi-client": "^0.84.2",
91
91
  "@earendil-works/pi-protocol": "^0.84.2",
92
92
  "@earendil-works/pi-tui": "^0.84.2",