@bastani/atomic 0.9.19-alpha.2 → 0.9.19-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/dist/builtin/intercom/CHANGELOG.md +15 -0
- package/dist/builtin/intercom/broker/broker.ts +8 -0
- package/dist/builtin/intercom/broker/client.ts +18 -0
- package/dist/builtin/intercom/broker/send-handler.ts +9 -0
- package/dist/builtin/intercom/index.bundle.mjs +161 -30
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/intercom/types.ts +2 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +18 -0
- package/dist/builtin/subagents/README.md +14 -14
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/skills/subagent/SKILL.md +14 -14
- package/dist/builtin/subagents/src/extension/index.bundle.mjs +142 -60
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +33 -0
- package/dist/builtin/workflows/README.md +14 -15
- package/dist/builtin/workflows/builtin/{chunk-mygmtz6e.js → chunk-9k3ywfgd.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-r9qq7xfj.js → chunk-ahbntmx2.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-zggjyg5z.js → chunk-n8zysf1t.js} +1 -1
- package/dist/builtin/workflows/builtin/goal.js +2 -2
- package/dist/builtin/workflows/builtin/index.js +3 -3
- package/dist/builtin/workflows/builtin/ralph.js +2 -2
- package/dist/builtin/workflows/package.json +5 -5
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +171 -207
- package/dist/builtin/workflows/src/index.js +33 -6
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +2 -1
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +1 -1
- package/dist/core/agent-session-auto-compaction.js +1 -1
- package/dist/core/agent-session-auto-compaction.js.map +1 -1
- package/dist/core/agent-session-compaction.js +1 -1
- package/dist/core/agent-session-compaction.js.map +1 -1
- package/dist/core/agent-session-custom-message-commit.d.ts.map +1 -1
- package/dist/core/agent-session-custom-message-commit.js +33 -2
- package/dist/core/agent-session-custom-message-commit.js.map +1 -1
- package/dist/core/agent-session-message-queue.d.ts +1 -1
- package/dist/core/agent-session-message-queue.d.ts.map +1 -1
- package/dist/core/agent-session-message-queue.js +22 -8
- package/dist/core/agent-session-message-queue.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +7 -1
- package/dist/core/agent-session-methods.d.ts.map +1 -1
- package/dist/core/agent-session-methods.js.map +1 -1
- package/dist/core/agent-session-persistent-custom-messages.d.ts +1 -1
- package/dist/core/agent-session-persistent-custom-messages.d.ts.map +1 -1
- package/dist/core/agent-session-persistent-custom-messages.js +13 -3
- package/dist/core/agent-session-persistent-custom-messages.js.map +1 -1
- package/dist/core/agent-session-post-tool-compaction.js +1 -1
- package/dist/core/agent-session-post-tool-compaction.js.map +1 -1
- package/dist/core/agent-session-prompt.d.ts.map +1 -1
- package/dist/core/agent-session-prompt.js +83 -0
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session-queue-pause.d.ts.map +1 -1
- package/dist/core/agent-session-queue-pause.js +11 -0
- package/dist/core/agent-session-queue-pause.js.map +1 -1
- package/dist/core/agent-session-tasks.d.ts +5 -0
- package/dist/core/agent-session-tasks.d.ts.map +1 -1
- package/dist/core/agent-session-tasks.js +8 -1
- package/dist/core/agent-session-tasks.js.map +1 -1
- package/dist/core/agent-session-tool-hooks.d.ts.map +1 -1
- package/dist/core/agent-session-tool-hooks.js +2 -0
- package/dist/core/agent-session-tool-hooks.js.map +1 -1
- package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
- package/dist/core/agent-session-tool-registry.js +12 -0
- package/dist/core/agent-session-tool-registry.js.map +1 -1
- package/dist/core/agent-session-transfer.d.ts.map +1 -1
- package/dist/core/agent-session-transfer.js +7 -0
- package/dist/core/agent-session-transfer.js.map +1 -1
- package/dist/core/agent-session-types.d.ts +2 -0
- package/dist/core/agent-session-types.d.ts.map +1 -1
- package/dist/core/agent-session-types.js +2 -0
- package/dist/core/agent-session-types.js.map +1 -1
- package/dist/core/agent-session.d.ts +4 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +22 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/context-types.d.ts +6 -0
- package/dist/core/extensions/context-types.d.ts.map +1 -1
- package/dist/core/extensions/context-types.js.map +1 -1
- package/dist/core/extensions/loader-api.d.ts.map +1 -1
- package/dist/core/extensions/loader-api.js +3 -0
- package/dist/core/extensions/loader-api.js.map +1 -1
- package/dist/core/extensions/loader-host-modules.d.ts +1 -1
- package/dist/core/extensions/loader-host-modules.d.ts.map +1 -1
- package/dist/core/extensions/loader-host-modules.js +2 -1
- package/dist/core/extensions/loader-host-modules.js.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.js +4 -1
- package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
- package/dist/core/extensions/runner-context.d.ts +1 -1
- package/dist/core/extensions/runner-context.d.ts.map +1 -1
- package/dist/core/extensions/runner-context.js +9 -3
- package/dist/core/extensions/runner-context.js.map +1 -1
- package/dist/core/extensions/runner.js +1 -1
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/workflow-events.d.ts +1 -1
- package/dist/core/extensions/workflow-events.d.ts.map +1 -1
- package/dist/core/extensions/workflow-events.js.map +1 -1
- package/dist/core/retry-policy.d.ts +1 -0
- package/dist/core/retry-policy.d.ts.map +1 -1
- package/dist/core/retry-policy.js +2 -1
- package/dist/core/retry-policy.js.map +1 -1
- package/dist/core/sdk-exports.d.ts +1 -1
- package/dist/core/sdk-exports.d.ts.map +1 -1
- package/dist/core/sdk-exports.js +1 -1
- package/dist/core/sdk-exports.js.map +1 -1
- package/dist/core/settings-manager-basic-accessors.d.ts +6 -3
- package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-basic-accessors.js +25 -8
- package/dist/core/settings-manager-basic-accessors.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/settings-types.d.ts +6 -0
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +4 -5
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/tasks/agent-adapter.d.ts +14 -2
- package/dist/core/tasks/agent-adapter.d.ts.map +1 -1
- package/dist/core/tasks/agent-adapter.js +45 -4
- package/dist/core/tasks/agent-adapter.js.map +1 -1
- package/dist/core/tasks/completion.d.ts.map +1 -1
- package/dist/core/tasks/completion.js +5 -1
- package/dist/core/tasks/completion.js.map +1 -1
- package/dist/core/tasks/pause.d.ts +4 -0
- package/dist/core/tasks/pause.d.ts.map +1 -0
- package/dist/core/tasks/pause.js +49 -0
- package/dist/core/tasks/pause.js.map +1 -0
- package/dist/core/tasks/supervisor.d.ts +3 -1
- package/dist/core/tasks/supervisor.d.ts.map +1 -1
- package/dist/core/tasks/supervisor.js +48 -22
- package/dist/core/tasks/supervisor.js.map +1 -1
- package/dist/core/tools/bash-pty-native.d.ts +16 -2
- package/dist/core/tools/bash-pty-native.d.ts.map +1 -1
- package/dist/core/tools/bash-pty-native.js +82 -0
- package/dist/core/tools/bash-pty-native.js.map +1 -1
- package/dist/core/tools/bash.d.ts +15 -2
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +49 -4
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/index.d.ts +4 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +12 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/kill.d.ts +17 -0
- package/dist/core/tools/kill.d.ts.map +1 -0
- package/dist/core/tools/kill.js +49 -0
- package/dist/core/tools/kill.js.map +1 -0
- package/dist/core/tools/powershell.d.ts +84 -28
- package/dist/core/tools/powershell.d.ts.map +1 -1
- package/dist/core/tools/powershell.js +11 -3
- package/dist/core/tools/powershell.js.map +1 -1
- package/dist/core/tools/resource-selectors.d.ts +1 -1
- package/dist/core/tools/resource-selectors.d.ts.map +1 -1
- package/dist/core/tools/resource-selectors.js +7 -4
- package/dist/core/tools/resource-selectors.js.map +1 -1
- package/dist/core/workflow-stage-admission.d.ts +14 -0
- package/dist/core/workflow-stage-admission.d.ts.map +1 -1
- package/dist/core/workflow-stage-admission.js +63 -3
- package/dist/core/workflow-stage-admission.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.js +3 -2
- package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
- package/dist/modes/interactive/components/task-row.d.ts.map +1 -1
- package/dist/modes/interactive/components/task-row.js +8 -1
- package/dist/modes/interactive/components/task-row.js.map +1 -1
- package/docs/background-tasks.md +31 -2
- package/docs/compaction.md +20 -0
- package/docs/computer-use.md +390 -0
- package/docs/development.md +23 -74
- package/docs/docs.json +1 -0
- package/docs/extensions.md +15 -1
- package/docs/herdr.md +26 -77
- package/docs/index.md +1 -0
- package/docs/intercom.md +14 -2
- package/docs/models/evals.md +2 -0
- package/docs/models/model-selection.md +10 -7
- package/docs/quickstart.md +1 -1
- package/docs/sdk.md +11 -1
- package/docs/settings.md +24 -0
- package/docs/subagents.md +10 -6
- package/docs/tmux.md +2 -0
- package/docs/tools.md +11 -1
- package/docs/usage.md +1 -1
- package/docs/workflows/api-reference.md +5 -4
- package/docs/workflows/authoring.md +3 -3
- package/docs/workflows/operations.md +44 -33
- package/docs/workflows/reliable-design.md +3 -3
- package/docs/workflows/verification.md +99 -40
- package/docs/workflows.md +4 -2
- package/npm-shrinkwrap.json +59 -59
- package/package.json +3 -3
|
@@ -27,6 +27,8 @@ Send material updates through Intercom to every affected workflow stage, includi
|
|
|
27
27
|
|
|
28
28
|
At 80 columns and wider, each `BACKGROUND` card keeps the full run identity and preserves its mode, progress, live-tool details, and elapsed/status metadata. When the remaining single-row budget permits, it adds bounded pending-stage details: a target is either shown exactly or replaced by a `stage`-labeled canonical ID, and `… N more` reports omitted pending stages. If no bounded pending-stage form fits, the pending label is omitted entirely rather than displacing the existing metadata. Tool nodes are read-only durable graph nodes, not attachable stage chats. Below 80 columns, the panel keeps its aggregate collapsed form and omits run IDs, stage identities, targets, and tool names.
|
|
29
29
|
|
|
30
|
+
The panel's stage progress includes recursively nested child workflows and updates as new stages appear. The numerator counts completed, failed, and skipped stages; the denominator counts all currently materialized stages, not future work. Expanded children replace their workflow boundary rather than counting both, and durable tool nodes are not stages. A failed, skipped, missing, or invalid child expansion keeps its boundary summary. `single`/`chain` follows this same stage count.
|
|
31
|
+
|
|
30
32
|
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. Pending-stage targets in run detail use the same rule: the exact address wraps instead of being ellipsized, and narrow status cards wrap the canonical stage ID or drop its display-name decoration rather than rendering a partial ID. The renderer 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.
|
|
31
33
|
|
|
32
34
|
The `/workflow connect` run picker shows five runs at a time; use the arrow keys or mouse wheel to scroll through additional retained runs.
|
|
@@ -59,7 +61,7 @@ The workflow tool action surface is:
|
|
|
59
61
|
- discovery: `list`, `get`, `inputs`, plus `models` for the configured model catalog
|
|
60
62
|
- execution: named `run` with validated `workflow` and `inputs`
|
|
61
63
|
- inspection: `status`, `stages`, `stage`, `transcript`
|
|
62
|
-
- prompt response: `answer`; run control: `pause`, `
|
|
64
|
+
- prompt response: `answer`; run control: `pause`, `quit`, `resume`; free-form stage communication: ordinary Intercom `send`/live `ask` to `workflow:<rootRunId>/<segment>[/<segment>...]` path targets, including `*` and `**` globs
|
|
63
65
|
- rediscovery: `reload`
|
|
64
66
|
|
|
65
67
|
Every registered `workflow` tool call has one hard two-minute 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:
|
|
@@ -75,13 +77,13 @@ Every registered `workflow` tool call has one hard two-minute wall-clock deadlin
|
|
|
75
77
|
}
|
|
76
78
|
```
|
|
77
79
|
|
|
78
|
-
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`, `answer`, `pause`, `resume`,
|
|
80
|
+
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`, `answer`, `pause`, `resume`, 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.
|
|
79
81
|
|
|
80
82
|
Explicit user interruption is different from the request deadline. Before startup acknowledgement, interrupting a `run` tool call cancels its initialization owner, records an allocated root as locally `killed`, and prevents delayed setup from starting workflow code. An already-aborted request starts no action. Interrupted resume preparation releases a claim it has acquired instead of dispatching later. After startup acknowledgement, the workflow is detached: aborting the original caller does not stop it; use run-level controls.
|
|
81
83
|
|
|
82
84
|
Cancellation does not undo mutations that already happened. A non-cancellable database write may still finish, with the final cancellation record queued after it. Inspect status and external effects before retrying an interrupted mutation. Request timeout retains its unknown-outcome behavior and does not cancel an accepted detached launch.
|
|
83
85
|
|
|
84
|
-
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, prompt-response, and control calls (`status`, `stages`, `stage`, `transcript`, `answer`, `pause`, `resume`, `
|
|
86
|
+
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, prompt-response, and control calls (`status`, `stages`, `stage`, `transcript`, `answer`, `pause`, `resume`, `quit`) remain available while work runs.
|
|
85
87
|
|
|
86
88
|
The no-`runId` status listing includes bounded pending-stage rows after each run summary. Each row gives the display name, canonical stage ID, literal `pending` lifecycle, `pendingStageDeliveryAvailable`, and either the exact usable Intercom target or `unavailable`. Interactive status cards and run detail show the same identity/availability distinction within their width budgets. Status cards wrap exact targets onto continuation rows instead of rendering a partially truncated address; bounded omissions retain an explicit remaining-stage count.
|
|
87
89
|
|
|
@@ -120,6 +122,8 @@ If you copy a HIL workflow example into a headless session, it can pass dispatch
|
|
|
120
122
|
|
|
121
123
|
<p align="center"><img src="../images/workflow-input-picker.png" alt="Workflow Input Picker" width="600" /></p>
|
|
122
124
|
|
|
125
|
+
Graph node cards show each model stage's effective model and thinking level beneath its status, including after fallback and durable resume. Long model names are truncated first, preserving the complete thinking level and a canonical `-fast` model suffix. This suffix is model identity, not a separate fast-mode switch or proof of service tier. Thinking `off` is omitted; unresolved model identity shows `—`. Tool nodes retain their `durable tool` body, and the `BACKGROUND` summary is unchanged.
|
|
126
|
+
|
|
123
127
|
## Workflow Commands
|
|
124
128
|
|
|
125
129
|
```text
|
|
@@ -129,10 +133,9 @@ If you copy a HIL workflow example into a headless session, it can pass dispatch
|
|
|
129
133
|
/workflow <name> [key=value ...]
|
|
130
134
|
/workflow connect [run-id]
|
|
131
135
|
/workflow attach [run-id] [stage-id-or-name]
|
|
132
|
-
/workflow pause [run-id]
|
|
136
|
+
/workflow pause [run-id|--all]
|
|
133
137
|
/workflow status [run-id]
|
|
134
138
|
/workflow status --all
|
|
135
|
-
/workflow interrupt <run-id|--all>
|
|
136
139
|
/workflow quit <run-id|--all>
|
|
137
140
|
/workflow resume <run-id> [stage-id-or-name] [message]
|
|
138
141
|
/workflows [full-workflow-uuid]
|
|
@@ -145,7 +148,7 @@ Common controls:
|
|
|
145
148
|
/workflow status # list retained active and terminal runs
|
|
146
149
|
/workflow connect <run-id> # graph viewer, including terminal runs
|
|
147
150
|
/workflow attach <run-id> <stage> # chat with a single stage
|
|
148
|
-
/workflow
|
|
151
|
+
/workflow pause <run-id> # pause resumably
|
|
149
152
|
/workflow resume <run-id> [stage] msg # forward a steer message and resume
|
|
150
153
|
/workflow quit <run-id> # pause gracefully and keep the run resumable
|
|
151
154
|
/workflows [run-id] # retained alias for /workflow resume (history picker)
|
|
@@ -164,7 +167,7 @@ Surface behavior:
|
|
|
164
167
|
- **Footer context** - An attached live stage chat shows its own current folder and Git branch and mirrors live extension status lines such as the MCP server indicator. Branch changes trigger a repaint through the host's cached footer provider. The compact `/tasks` picker retains this context and the stage's model and reasoning level even while its foreground turn streams; detail, transcript, input, and confirmation pages remain fullscreen.
|
|
165
168
|
- **Working animation lifecycle** - Ordinary attached-stage work keeps the same exact one-cell `∀` visible while following the active workflow theme's dark → accent → bright/bold → accent → dark luminance ramp every 88ms. Every agent and SDK turn resets to the dark regular phase with a fresh lifecycle-relative cadence; turn, terminal, error, replacement, and disposal cleanup stop the active timer without stale repaint. In an eligible retained-stage chat, every accepted idle follow-up — including a workflow-authored `stage.sendUserMessage(...)` after a prior turn ended — shows Working on admission or attach, including while Atomic restores a saved retained conversation, and keeps it through prompt startup, pre-turn compaction, and agent handoff. Attaching or remounting mid-delivery paints immediately rather than waiting for the turn's first event. A message queued into a live turn with `followUp`/`steer` uses that turn's existing status instead of starting a new one. A no-turn result, prompt or restore error, or terminal completion removes it; once the last accepted post-terminal delivery settles, a leftover start cannot bring it back. An accepted manual retry clears stale status from the prior prompt before showing new pre-stream activity. `NO_COLOR` retains regular/bold activity without foreground-color escapes. Reduced motion uses a static regular accent `∀` without an animation timer; factual automatic retry, fallback, compaction, cancellation, and error copy retains precedence.
|
|
166
169
|
- **Subagent statusline** - If a subagent is running while the fullscreen workflow graph is open, the graph statusline mirrors its summary so the run remains visible; hide the graph with `h`, leave it with `ctrl+x`, or reconnect later to return to the full below-editor widget.
|
|
167
|
-
- **Run control** - Use `
|
|
170
|
+
- **Run control** - Use `pause` and `resume` for resumable live work. Pause holds a stage's queued steering and follow-up items in place without dequeuing them or starting continuation; `resume` releases those items once in their existing per-queue order, but queue release alone does not start a model turn. `resume` on a non-paused run reopens the saved snapshot or overlay. Use `quit` to pause a live run gracefully while preserving it for `/workflow resume`. `/workflow pause` selects the active run by default, accepts a full run id or `--all`, and does not open a stage picker. Use the workflow tool's `stageId` for stage or tool-node targeting.
|
|
168
171
|
- **Rediscovery** - Use `/workflow reload` after adding, editing, installing, or removing workflow resources or package manifest workflow entries and you want Atomic to rediscover them in-process ([Reloading workflow resources](#reloading-workflow-resources)).
|
|
169
172
|
- **Status listing** - `/workflow status` lists all retained active and terminal top-level runs by default; implementation-owned nested child runs are flattened into their parent workflow rather than listed separately. `/workflow status --all` is retained as a compatibility alias.
|
|
170
173
|
|
|
@@ -188,7 +191,7 @@ Mounted HIL and custom prompts take precedence: a `/skill:` answer is literal pr
|
|
|
188
191
|
|
|
189
192
|
`/tasks` opens the owner task list locally, never a skill or model message. Empty and populated lists use the same compact picker as main chat, without combining tasks from other chats. Enter inspects the selected task; focused actions offer retained transcript inspection, foreground waiting, confirmed cancellation, and stdin when available. Terminal tasks omit live actions. Escape returns to the picker with selection preserved, then to chat. A stage question arriving during inspection remains pending and is shown when you leave the inspector. Task navigation does not create a main-chat input-needed notice; genuine main-chat prompts waiting behind a visible graph still do.
|
|
190
193
|
|
|
191
|
-
`/tasks` is also suggested in the stage slash menu independently of `enableSkillCommands`. Its [background-task status](/background-tasks) appears below MCP status;
|
|
194
|
+
`/tasks` is also suggested in the stage slash menu independently of `enableSkillCommands`. Its [background-task status](/background-tasks) appears below MCP status; the graph-return shortcut does not overwrite the task-inspection hint. Pausing a workflow stage cancels its active and admitted queued background agents and commands and waits for cleanup. It blocks fresh launches immediately; already-in-flight command setup may briefly start during the transition but must drain and be cancelled before pause completes. Stage-scoped pause leaves main-chat tasks and sibling stages alone. Merely detaching the chat or ending a foreground turn does not cancel background work. Closing the stage generation still cancels remaining owned work.
|
|
192
195
|
|
|
193
196
|
If inspection fails, the host displays the error and keeps your input for retry. It does not send the command to the model.
|
|
194
197
|
|
|
@@ -220,9 +223,7 @@ workflow({ action: "resume", runId: "<full-run-uuid>", stageId: "review", messag
|
|
|
220
223
|
|
|
221
224
|
workflow({ action: "pause", runId: "<full-run-uuid>" })
|
|
222
225
|
workflow({ action: "pause", runId: "<full-run-uuid>", stageId: "review" })
|
|
223
|
-
|
|
224
|
-
workflow({ action: "interrupt", runId: "<full-run-uuid>" })
|
|
225
|
-
workflow({ action: "interrupt", all: true })
|
|
226
|
+
workflow({ action: "pause", all: true })
|
|
226
227
|
|
|
227
228
|
workflow({ action: "resume", runId: "<full-run-uuid>" })
|
|
228
229
|
workflow({ action: "resume", runId: "<full-run-uuid>", stageId: "review", message: "continue" })
|
|
@@ -232,7 +233,7 @@ workflow({ action: "quit", all: true })
|
|
|
232
233
|
|
|
233
234
|
// Abort one in-flight ctx.tool node without pausing the run.
|
|
234
235
|
workflow({ action: "quit", runId: "<full-run-uuid>", stageId: "tool:<argsHash>" })
|
|
235
|
-
workflow({ action: "
|
|
236
|
+
workflow({ action: "pause", runId: "<full-run-uuid>", stageId: "publish-artifact" })
|
|
236
237
|
|
|
237
238
|
workflow({ action: "reload", reason: "added team workflow" })
|
|
238
239
|
```
|
|
@@ -241,29 +242,31 @@ Control behavior:
|
|
|
241
242
|
|
|
242
243
|
- `runId` requires the full 36-character run UUID for every lifecycle and inspection action, including `status`. User-facing status surfaces print that exact value, so pass it back verbatim; typed prefixes are rejected with a distinct `Run id must be a full 36-character UUID` diagnostic rather than resolved. Because ids are matched exactly and are unique, no run target is ambiguous. Status lists and run pickers show top-level user-launched workflows; nested child runs are implementation details of the expanded parent graph.
|
|
243
244
|
- `status`, `stages`, `stage`, and `transcript` with an explicit full `runId` first use the current session store, then perform one exact DBOS hydration when that id is absent locally. This is inspection only: Atomic does not claim ownership, change status, run workflow code, or resume the workflow. A stale durable `running` root is shown as `crashed` with its resumability and an explicit `/workflow resume <id>` hint; fresh work owned by another Atomic process remains `running`, offers read-only status guidance, and stays protected from local control or resume. Deleted/tombstoned, absent, malformed, cyclic, orphaned, nonreciprocal, out-of-scope, and duplicate-node records report distinct failures instead of inventing a partial graph. `status` without `runId` remains current-session-only and never scans durable history.
|
|
244
|
-
- `status` without `runId` lists every top-level run in the session with a concise per-run summary: the full run id, workflow name, run status, started/ended timing with pause-adjusted elapsed time, currently active stages, and awaiting-input details (count plus the stage, prompt id, kind, and message for each pending human prompt). In-flight runs are listed first. The summaries carry the exact identifiers that `answer`, `pause`, `resume`,
|
|
245
|
+
- `status` without `runId` lists every top-level run in the session with a concise per-run summary: the full run id, workflow name, run status, started/ended timing with pause-adjusted elapsed time, currently active stages, and awaiting-input details (count plus the stage, prompt id, kind, and message for each pending human prompt). In-flight runs are listed first. The summaries carry the exact identifiers that `answer`, `pause`, `resume`, and `quit` accept, so an orchestrating agent can list runs and act on them directly.
|
|
245
246
|
- `statusFilter` narrows the `status` run listing: run statuses (`pending`, `running`, `paused`, `blocked`, `completed`, `failed`, `skipped`, `cancelled`, `killed`) match runs directly, `awaiting_input` selects runs with at least one stage awaiting input or pending human prompt, and `all` (the default) includes everything.
|
|
246
247
|
- `format: "json"` on data-bearing inspection actions (`status`, `stages`, `stage`, `transcript`) returns the full structured result; the default text output for `status` is the concise per-run summary list.
|
|
247
248
|
- `status` / `status <runId>` show terminal `ctx.exit(...)` statuses (`completed`, `skipped`, `cancelled`, or `blocked`) and the optional exit reason when one was supplied.
|
|
248
249
|
- `stages` lists stage summaries, including flattened stages from nested `ctx.workflow(...)` imports and `sessionFile`/`transcriptPath` when a stage has a persisted session. Use `statusFilter: "all"` to include completed, failed, skipped, and pending stages.
|
|
249
250
|
- `stage` returns details for one stage by exact stage id or exact stage name, including nested child stages shown in the expanded graph and the persisted `sessionFile` when available. User-facing graph and control messages print full stage IDs; pass one back verbatim, or use the stage's exact name. Prefixes and partial names no longer resolve. Two stages sharing an exact name return an ambiguity diagnostic rather than selecting one.
|
|
251
|
+
- Local and retained/durable `stages` listings use the same expanded graph as `stage` and `transcript`: pass the listed ID back unchanged with that listing's `runId`. Valid nested boundaries are replaced by their descendants; missing, empty, or invalid children retain an inspectable boundary summary. Detail/transcript results identify the actual owning run and local stage ID.
|
|
252
|
+
- Intercom's slash-separated `target` is a messaging address, not a `workflow` `stageId`. An Intercom stage row also includes its owning `runId` and local `stageId`; pass that pair to `stage` or `transcript`, or use the expanded ID from `workflow stages` with the root run ID.
|
|
250
253
|
- `transcript` is reference-first with a small preview by default: it returns metadata, transcript paths, and up to 5 recent entries. For targeted lookup, quote the exact `sessionFile`/`transcriptPath` value without changing platform separators (preserve Windows backslashes), search it with `rg` or `grep`, then read only small surrounding ranges. Text results include JSON-escaped `sessionFileJson`/`transcriptPathJson` lines for copy-safe path literals. Pass explicit `tail` or `limit` to override the 5-entry preview; `tail` overrides `limit`; `includeToolOutput` includes captured snapshot tool output in snapshot transcript results.
|
|
251
254
|
- `answer` responds only to a pending primitive or structured human-input prompt. It accepts `promptId` plus `response`, `text`, or `message`, preserves prompt-kind validation, and never sends stage chat, steers, resumes, or starts a model turn.
|
|
252
255
|
- Send free-form updates through ordinary Intercom to `workflow:<rootRunId>/<segment>[/<segment>...]`; `*` matches one segment and `**` any depth. Use `intercom list` inside the invocation group to see live, pending, and possible future targets. Atomic delivers immediately to live stages and queues matching future stages, delivering them before their first model turn. `workflow:<rootRunId>/**` remains sticky for every future descendant until root termination; narrower name and pattern sends reach every future match. Valid paths outside the known set queue with a `notInKnownSet` warning and settle undeliverable at terminal only if never delivered. Use `ask` once the target has a reply-capable live session. Use `workflow resume` only for paused workflow control.
|
|
253
|
-
- `pause
|
|
254
|
-
- `
|
|
256
|
+
- `pause` and `quit` can target one top-level run or `all: true`; `stageId` cannot be combined with `all: true`. Stage-scoped `pause` controls can target a visible nested child stage from the expanded graph. Atomic routes stage controls to the owning nested run internally.
|
|
257
|
+
- `pause` and `quit` can also name one in-flight `ctx.tool` node with `stageId`, by expanded node id, local `tool:<argsHash>` id, or tool name. Both mean the same thing for a tool: abort that single call now. Tool nodes stay non-attachable — this is an abort control, not a chat target. Identifiers resolve exactly first and then uniquely; a name shared by two tool nodes (or by a stage and a tool) returns the same ambiguity diagnostic stages get, listing each match as `<name> (tool)`.
|
|
255
258
|
- Aborting one tool node leaves every sibling stage and sibling tool node running and does not pause the run. The node becomes `cancelled`, writes no replayable checkpoint, and re-runs on a later resume. Whether the run itself survives is ordinary author control flow: an awaited `ctx.tool` that is aborted rejects, exactly as it would for any other failure, unless the workflow catches it. A node that has already settled reports that it is not running rather than silently succeeding.
|
|
256
259
|
- If targeted cancellation escapes workflow code, the failed run records `failedToolNodeId`, not a fabricated `failedStageId`. Resume the run without a stage override to retry that unfinished tool and replay completed work. The tool remains a non-attachable `cancelled` tool node.
|
|
257
260
|
- Whole-run `quit` stays authoritative even if workflow code catches the tool rejection. A catch may run cleanup, but its returned outputs do not convert the quit into a completed run: the executor suspends and quit's paused/resumable record stands. To abort one call and intentionally keep the workflow going, target that node instead of quitting the run.
|
|
258
261
|
- A targeted tool abort reports the node outcome and the run separately: `status: "cancelled"` for the node it cancelled, `stageId` for that node, `abandoned` when the callback ignored its signal, and `workflowStatus` for the run status *observed* when the action returned. It never reports `paused`, and it never predicts what the run does next.
|
|
259
|
-
- `pause`
|
|
260
|
-
-
|
|
262
|
+
- `pause` preserves resumable live work. With no active stage/tool handle (including initialization and between-node waits), it keeps the same executor paused rather than quitting it. Run-level `resume` on that live process releases the barrier exactly once; later tracked steps, cached replay, and run completion wait for that explicit resume. An already-started untracked JavaScript await or non-cancellable I/O may still finish; pause cannot physically freeze arbitrary author code or roll back effects. A stage declared during this pause defers registration until its asynchronous method runs after resume; synchronous session access requires resume first.
|
|
263
|
+
- A stage pause cancels that generation's owned task executions, not its queued messages or future workflow stages. User and Intercom messages remain held until resume; resume allows fresh tasks and never restarts the cancelled executions. Cancellation and cleanup failures reject the pause rather than reporting a successful stop.
|
|
261
264
|
- A whole-run executor-only pause also holds already-live owned child workflows, including children with no tracked node yet. Resume persists the root's running transition before releasing those child executors; a child-scoped control leaves sibling workflows alone.
|
|
262
265
|
- Live executor resume does not require a checkpoint. Cross-process resume still requires durable checkpoint or pending-prompt progress: losing a zero-progress live owner cannot reconstruct its JavaScript continuation. Cancelling the original startup request before acknowledgement remains terminal cancellation, not this live pause.
|
|
263
266
|
- `resume` can target a stage with `stageId`; the target may be an exact stage id or an exact stage name. `message` is forwarded to paused work. For a live interrupted streaming prompt, Atomic preserves the existing prompt loop without duplicating the user message and injects `Continue where you left off. If you believe you are finished with your original task (or a redefined task if the user told you), stop.` when required before normal readiness-gate completion. For a paused stage that was idle waiting for a new stage-chat turn, a non-empty message resumes the stage and starts exactly one fresh prompt containing that message; an empty resume releases the pause without creating a prompt.
|
|
264
267
|
- An explicit workflow-tool `resume` target that is absent from the current session store triggers targeted DBOS discovery before Atomic returns `Run not found`. The target must be a full run UUID; an eligible exact ID resumes under the original workflow ID, and a malformed target is rejected before any durable lookup happens. Resource-loading and durable-backend failures remain visible. Ordinary workflow-tool `status` listing stays session-local and does not eagerly hydrate durable history.
|
|
265
268
|
- Exact-id durable inspection is separate from resume. `status`, `stages`, `stage`, and `transcript` may hydrate one missing-local root for read-only inspection, but they never claim it or execute replay. Only an explicit `resume` action enters the claim-and-dispatch path.
|
|
266
|
-
- Run-level `quit` gracefully pauses in-flight work and preserves eligible checkpointed runs for `/workflow resume`. Runs waiting during initialization or between nodes are also controllable, even with no active stage or tool.
|
|
269
|
+
- Run-level `quit` gracefully pauses in-flight work and preserves eligible checkpointed runs for `/workflow resume`. Runs waiting during initialization or between nodes are also controllable, even with no active stage or tool. An executor-only quit retires the owner (including one already paused); without usable durable progress it is nonresumable and offers no resume hint.
|
|
267
270
|
- `reload` refreshes discovered workflow resources in-process; the optional `reason` is echoed in the result.
|
|
268
271
|
|
|
269
272
|
Use slash commands for graph connect and stage attach because those are interactive TUI surfaces. When a run needs user input or attention, tell the user instead of polling silently.
|
|
@@ -272,7 +275,7 @@ Use slash commands for graph connect and stage attach because those are interact
|
|
|
272
275
|
|
|
273
276
|
Graceful quit is idempotent for an already-paused resumable run. If a run is waiting on `ctx.ui`, quit preserves its current DBOS prompt reservation. Answers cannot advance paused workflow code until explicit resume; checkpointing the answer releases exactly that reservation generation. Concurrent and nested prompts use composed scopes and independent DBOS reservation tokens.
|
|
274
277
|
|
|
275
|
-
When no stage or tool owns the pending await, whole-run `pause
|
|
278
|
+
When no stage or tool owns the pending await, whole-run `pause` holds the live executor until resume; `quit` retires it at a durability boundary. Neither forcibly stops arbitrary JavaScript outside `ctx.*`; already-started untracked code and non-cancellable I/O may finish later. Control results disclose this limit. An executor-only quit before any checkpoint progress is retained as paused/nonresumable; start a new run only after reconciling any external effects.
|
|
276
279
|
|
|
277
280
|
**Quit closes `ctx.tool` admission before it becomes a durability boundary.** A run-level quit pauses controllable stages and waits for their acknowledgements, then closes the root-shared tool-admission boundary shared by the root run and every nested run. Closing is what makes the following scan final: a call admitted while the stage pauses were still being acknowledged is included, and no call can start afterwards — not even while the durable write is in flight. Quit then aborts that complete set, waits a bounded interval for the callbacks to settle, and only then records the durable paused transition and marks the run resumable.
|
|
278
281
|
|
|
@@ -369,13 +372,13 @@ Ended recoverable blocks, including reviewer execution failures, use this same i
|
|
|
369
372
|
|
|
370
373
|
Completed top-level `ctx.tool` nodes also replay into the fresh run. See [`ctx.tool` — durable cached tool execution](#ctxtool--durable-cached-tool-execution). A fail-closed topology mismatch ends that continuation; the durable source stays blocked and resumable, and the same session can retry after the continuation settles.
|
|
371
374
|
|
|
372
|
-
|
|
375
|
+
Attributed control actions on a top-level run report themselves too. `/workflow <name>` emits a `WORKFLOW STARTED` notice (`▶`), `/workflow quit` a `WORKFLOW QUIT` notice (`⏹`, warning tone, carrying a `resumable` field), and `/workflow resume` a `WORKFLOW RESUMED` notice (`▶`). These travel the same steer delivery, capped-backoff retry, and notice-card path as the failure notice. The quit text states that the stop was deliberate and user-requested and tells the model not to resume the run or take the work over unless asked, with `/workflow resume <run-id>` as the card hint; the resumed text does not, because the run is progressing again.
|
|
373
376
|
|
|
374
|
-
**Only user actions notify.** The equivalent `workflow({ action: "run" | "pause" | "quit" | "resume" })` tool calls stay silent: the tool result already tells the agent what it just did
|
|
377
|
+
**Only attributed user actions notify.** The equivalent `workflow({ action: "run" | "pause" | "quit" | "resume" })` tool calls stay silent: the tool result already tells the agent what it just did. `/workflow pause` does not attribute an actor or raise a main-chat control notice. Engine-internal transitions are silent too — answering a human-in-the-loop prompt resumes the run internally without waking the model. Workflow activity and lifecycle observation report pause requests independently of chat notices.
|
|
375
378
|
|
|
376
|
-
**Two attributions.** *Origin* is who launched the run and renders on every kind as "which you started" or "which the user started"; it is set once at dispatch, persisted through session restore and durable resume, and inherited by a continuation from the run it continues. *Actor* is who performed this one event and renders as "The user
|
|
379
|
+
**Two attributions.** *Origin* is who launched the run and renders on every kind as "which you started" or "which the user started"; it is set once at dispatch, persisted through session restore and durable resume, and inherited by a continuation from the run it continues. *Actor* is who performed this one event and renders as "The user resumed" or "You resumed". They differ routinely — the agent starts a run and the user quits it. A run with no recorded origin, including a restored snapshot, omits the clause entirely rather than guessing.
|
|
377
380
|
|
|
378
|
-
**One notice per request.** A whole-run
|
|
381
|
+
**One notice per attributed request.** A whole-run resume reports at run scope; a stage-scoped resume reports at stage scope when siblings remain paused. A quit reports only the quit, never the pause it publishes on the way. Notices are deduplicated by run id and occurrence timestamp, so repeated snapshot invalidations at one unchanged state emit one. Resuming reports a resume and never a start, whoever asked for it. Resuming a failed or blocked run launches a continuation under a fresh run id, and its notice names both; resuming a quit run reuses the original workflow id so durable checkpoints replay. A run already started, paused, or quit when notifications install — restore, replay, `/reload`, or a session-preserving reinstall — is seeded as delivered and stays silent, and nested child runs never notify at top level.
|
|
379
382
|
|
|
380
383
|
Configure lifecycle behavior with `workflowNotifications.enabled` (default `true`) and `workflowNotifications.notifyOn` (default `["started", "completed", "failed", "blocked", "budget_warning", "awaiting_input", "paused", "quit", "resumed"]`). A config that pins `notifyOn` explicitly keeps exactly the kinds it lists, so `notifyOn: ["failed"]` suppresses every control notice. `budget_warning` is delivered once per run and dimension through the same lifecycle-notice renderer.
|
|
381
384
|
|
|
@@ -419,6 +422,12 @@ Atomic workflows use **DBOS/Postgres as their sole persistent workflow backend**
|
|
|
419
422
|
|
|
420
423
|
**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.
|
|
421
424
|
|
|
425
|
+
**Administrator accounts (Windows).** Atomic can start embedded Postgres from an elevated terminal or an administrative account without changing your account or system permissions. The server runs with reduced privileges, as it does under PostgreSQL's own launcher. Regular Windows accounts remain supported. On either path, server output goes to `~/.atomic/postgres/v18.log`, not your terminal; unrelated commands starting concurrently do not keep the server's log file open. If Postgres exits during startup, Atomic reports the recent log output instead of waiting for a generic readiness timeout. Check that log for configuration or cluster errors; do not delete the cluster while a server may still be using it.
|
|
426
|
+
|
|
427
|
+
If another service takes port `5439` while the embedded server is starting, a reachable port does not override a detected server exit. Atomic also reports process-status query failures instead of hiding them behind a readiness timeout. Inspect the reported error and server log before retrying.
|
|
428
|
+
|
|
429
|
+
Custom Windows launchers can still use executable names resolved through `PATH` or relative executable paths. `.cmd` and `.bat` launchers accept arguments on administrative accounts, including when the launcher path contains spaces. Explicit `cmd.exe` invocations retain their usual argument handling. Working directories with a `\\?\` prefix are supported when removing the prefix preserves the exact path; if `cmd.exe` reports an unsupported UNC working directory, use an ordinary local directory path. If startup reports that batch file arguments are invalid, remove carriage returns and line breaks from those arguments. If it reports that a string contains NUL characters, check the supplied paths, arguments, and environment entries. Remove the embedded NUL rather than retrying with a truncated value; no server is started for that request.
|
|
430
|
+
|
|
422
431
|
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.
|
|
423
432
|
|
|
424
433
|
**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`.
|
|
@@ -447,11 +456,11 @@ Durable `/workflow resume` preserves completed stage metadata, active-stage elap
|
|
|
447
456
|
|
|
448
457
|
Each new Atomic process that reopens unfinished work starts from the latest saved baseline, so repeated process-boundary resumes keep stable boundary/child ids, status, graph, and lifecycle duration cumulative without double-counting pauses. A stage paused at ten seconds resumes at ten seconds, and the main-chat dashboard reports prior-session elapsed plus current-session elapsed. Completed inspection uses that same accumulated run timing rather than DBOS record wall-clock age.
|
|
449
458
|
|
|
450
|
-
Repeated, sibling, sequential, parallel, and multi-level child calls keep independent composed scopes and stable boundary order. The expanded graph routes attach, send, pause,
|
|
459
|
+
Repeated, sibling, sequential, parallel, and multi-level child calls keep independent composed scopes and stable boundary order. The expanded graph routes attach, send, pause, and resume through each stage's ordinary owning `{ runId, stageId}`. Resolution is exact: an expanded id, a local stage id, or a name must match whole, and colliding names return an ambiguity diagnostic rather than selecting the first match silently.
|
|
451
460
|
|
|
452
461
|
### `ctx.tool` — durable cached tool execution
|
|
453
462
|
|
|
454
|
-
The `ctx.tool(name, args, fn, options?)` primitive runs arbitrary TypeScript code as a first-class durable graph node and caches the result durably. The node is non-attachable and has no stage chat controls, and its graph card body is the constant `durable tool` in every state — status, timing, and dependency rows keep their own rows, and the card does not preview the result or error. In the graph viewer, focusing the node and pressing Enter, clicking it, or choosing it from the switcher opens a read-only host-style operator card from the snapshot: a status-tinted shaded rectangle with the same inner padding and header/body gap as the main-chat tool block, inset from the orchestrator header and footer bars, a `$ <tool-name>` call header, an optional short argument summary, and the result or error as its body. Running and completed call headers have no status marker; pending, failed, cached, and cancelled calls retain their quiet markers. It is collapsed by default and wraps the fully bounded result or error before showing its last visual rows, with `... (N earlier lines, ctrl+o Expand)` above the tail when the action is bound; the configured `app.tools.expand` action (`ctrl+o` by default) toggles the full bounded result or error and then a muted callback-source block when source exists. The graph statusline advertises the resolved expand key with `expand` or `collapse` alongside return-to-graph and scroll hints, including remapped keys, and omits that segment entirely when the action is unbound. The footer says `Took` for settled calls or `Elapsed` for running calls, using the same second-resolution duration as the main-chat tool block, with cached/replayed markers kept as a quiet suffix. The operator surface has no ARGS/RESULT/SOURCE/TIMING/MARKERS debug table and does not expose raw clock fields. Source capture uses `fn.toString()` at registration without re-executing the callback or reading a file. `↑`/`↓`, `PageUp`/`PageDown`, `Home`/`End`, the wheel, and the scrollbar all scroll the block, so a long payload stays readable on a keyboard-only session or a terminal without mouse reporting; Escape or `ctrl+x` returns to the graph. The message block is read-only and never offers chat attachment, steering,
|
|
463
|
+
The `ctx.tool(name, args, fn, options?)` primitive runs arbitrary TypeScript code as a first-class durable graph node and caches the result durably. The node is non-attachable and has no stage chat controls, and its graph card body is the constant `durable tool` in every state — status, timing, and dependency rows keep their own rows, and the card does not preview the result or error. In the graph viewer, focusing the node and pressing Enter, clicking it, or choosing it from the switcher opens a read-only host-style operator card from the snapshot: a status-tinted shaded rectangle with the same inner padding and header/body gap as the main-chat tool block, inset from the orchestrator header and footer bars, a `$ <tool-name>` call header, an optional short argument summary, and the result or error as its body. Running and completed call headers have no status marker; pending, failed, cached, and cancelled calls retain their quiet markers. It is collapsed by default and wraps the fully bounded result or error before showing its last visual rows, with `... (N earlier lines, ctrl+o Expand)` above the tail when the action is bound; the configured `app.tools.expand` action (`ctrl+o` by default) toggles the full bounded result or error and then a muted callback-source block when source exists. The graph statusline advertises the resolved expand key with `expand` or `collapse` alongside return-to-graph and scroll hints, including remapped keys, and omits that segment entirely when the action is unbound. The footer says `Took` for settled calls or `Elapsed` for running calls, using the same second-resolution duration as the main-chat tool block, with cached/replayed markers kept as a quiet suffix. The operator surface has no ARGS/RESULT/SOURCE/TIMING/MARKERS debug table and does not expose raw clock fields. Source capture uses `fn.toString()` at registration without re-executing the callback or reading a file. `↑`/`↓`, `PageUp`/`PageDown`, `Home`/`End`, the wheel, and the scrollbar all scroll the block, so a long payload stays readable on a keyboard-only session or a terminal without mouse reporting; Escape or `ctrl+x` returns to the graph. The message block is read-only and never offers chat attachment, steering, pause, or resume. Bounded payloads remain width-safe and mark truncation explicitly with `… [truncated]`; source tabs expand and control bytes become `\xNN`, while cyclic payloads, throwing `toJSON`, or throwing property getters render `<cycle>`, `<unserializable>`, or `<unreadable>` instead of crashing the view. The same cap applies to what the live run snapshot retains for a tool node, while durable checkpoints keep the exact output, raw-args `argsHash`, and replay behavior unchanged.
|
|
455
464
|
|
|
456
465
|
When the workflow body fulfills but one or more admitted tool calls failed, Atomic promotes the first observed failure to the terminal run failure, regardless of admission order, and persists that selected tool-node identity for status inspection and lifecycle output. A direct uncaught `await ctx.tool(...)` rejection keeps the original error and persists its failed-node link through session and durable restore. First-event arbitration also preserves the selected node when concurrent failures throw the same object or primitive; unrelated later stage or body errors do not inherit a caught tool's origin. Tool admission remains open while author code can catch a failure and continue. Once the body settles and failure has won before any real cancellation, Atomic closes admission, cancels remaining non-failed tool nodes, waits for observed failed nodes to finish publication, and publishes the failed root without waiting for callbacks that ignore cancellation.
|
|
457
466
|
|
|
@@ -467,7 +476,7 @@ Recoverable output is explicit data flow. Atomic does not add a failed tool outc
|
|
|
467
476
|
|
|
468
477
|
Cancellation, closed tool admission, and durable-storage faults still throw. They never become ordinary `{ ok: false }` callback outcomes. Omitting `failureMode: "return"` also keeps the existing behavior: an exhausted callback error rejects `ctx.tool` and fails the workflow unless author code catches it. Atomic persists that failed node and the root's selected tool link for later inspection, but excludes the failure record from the replay cache, so a resume or rerun calls the function again. Command failures that expose `exitCode`, `stdout`, or `stderr` remain failures even when a wrapper also uses cancellation-like text or codes; only a real run cancellation that wins the terminal race produces a killed/cancelled root.
|
|
469
478
|
|
|
470
|
-
**Per-node cancellation and per-attempt deadlines.** Each logical `ctx.tool` call runs under its own `AbortController`, combined with the run's signal and handed to the callback as `{ signal }`. A run abort cascades to every live node; `workflow({ action: "quit"|"
|
|
479
|
+
**Per-node cancellation and per-attempt deadlines.** Each logical `ctx.tool` call runs under its own `AbortController`, combined with the run's signal and handed to the callback as `{ signal }`. A run abort cascades to every live node; `workflow({ action: "quit"|"pause", runId, stageId })` naming one tool node aborts exactly that node and leaves its siblings alone. Without `timeoutMs`, retries share that logical call signal. With `timeoutMs`, every attempt gets a fresh signal and deadline; expiry aborts that attempt and becomes an ordinary attempt failure, while run cancellation and operator abort remain cancellation.
|
|
471
480
|
|
|
472
481
|
A cancelled call is recorded as `cancelled`, not `failed`, and is never a run failure by itself: it writes no replayable `tool:` checkpoint and no `return_failure` outcome even under `failureMode: "return"`, so a cancellation can never replay as data. Targeted node aborts in either failure mode, and all return-mode cancellations, keep one inspection-only `tool-failure:` record carrying the cancellation message and a `cancelled: true` marker. This record preserves the unfinished node identity and parent edges across DBOS hydration, but is excluded from replay lookup. A callback that ignores its signal and returns late is caught before persistence, so its value cannot become a successful checkpoint either. Resume recomputes the same ordinal and `argsHash` from authored order; a tool-frontier continuation rejects changed identity or incompatible parents rather than running a different unfinished callback.
|
|
473
482
|
|
|
@@ -522,6 +531,8 @@ Changing `iteration` makes each loop pass a distinct durable call. Reusing the s
|
|
|
522
531
|
|
|
523
532
|
The `/workflow resume` command mirrors `/resume` ergonomics and `/workflows` is its alias. With no id, it builds one newest-first picker from live runs that satisfy the shared resumability predicate and current DBOS resumable/completed records. DBOS is the authoritative catalog; selected records are hydrated and revalidated before resume or inspection. Running workflows never appear: fresh-heartbeat rows are excluded in every session to prevent double dispatch, and stale ones surface as `crashed`. A row whose durable checkpoint or referenced artifact is missing is not resumable and is omitted rather than offered and rejected later. Naming such an id explicitly still produces the existing clear no-checkpoint/not-resumable error.
|
|
524
533
|
|
|
534
|
+
If resume reports a checkpoint decoding error or an unavailable serializer, retain the full run ID and original diagnostic when reporting the problem. Do not delete saved progress or launch a fresh copy just to bypass the error: that can repeat completed side effects.
|
|
535
|
+
|
|
525
536
|
The resume picker lists only runs the resume path would actually accept. One shared predicate (`isWorkflowRunResumable` in `packages/workflows/src/durable/resume-eligibility.ts`) backs both the picker and the `resume` command, so a row can never be offered and then refused. A run stops being resumable when it reaches a terminal state without a durable checkpoint or pending prompt progress, when its durable entry is explicitly deleted with Ctrl+D, or when its referenced artifacts are gone. The broader `connect`/`attach` pickers and `/workflow status` keep listing terminal runs for inspection; only `resume` is filtered.
|
|
526
537
|
|
|
527
538
|
Rows carry semantic colors — completed green, paused yellow, failed/blocked/crashed red — and show checkpoint progress without the redundant pending-prompt count. The open picker live-updates on local run changes plus a bounded cross-session poll, so state transitions appear (and freshly running workflows disappear) without reopening it.
|
|
@@ -559,7 +570,7 @@ Validation uses the final retained transcript for a repeated stage replay key, s
|
|
|
559
570
|
|
|
560
571
|
### Recovering an uncaught tool abort
|
|
561
572
|
|
|
562
|
-
After `workflow({ action: "
|
|
573
|
+
After `workflow({ action: "pause", runId, stageId: "tool:<argsHash>" })`, inspect the run. If the author did not catch cancellation, its terminal status is `failed`, its selected frontier is `failedToolNodeId`, and the tool node is `cancelled`. Use `/workflow resume <full-run-uuid>` or `workflow({ action: "resume", runId: "<full-run-uuid>" })` **without `stageId`**. Completed model and tool checkpoints replay without invoking their callbacks; only the unfinished tool and subsequent work execute. This does not roll back external work a cancelled callback performed before abort, so verify that retrying that unfinished operation is safe.
|
|
563
574
|
|
|
564
575
|
Fresh processes hydrate the same typed DBOS checkpoints before resume. Older records without `failedToolNodeId` are accepted only when the retained graph and inspection-only tool failure record identify one unfinished node matching the terminal targeted-abort error, with completed predecessor checkpoints intact. Missing checkpoints, multiple unfinished frontiers, corrupt identities, and cyclic or missing parent edges fail closed with `insufficient_state`. Error text alone, model/chat transcript text, and generic status tool-result snapshots are not executable recovery state.
|
|
565
576
|
|
|
@@ -584,7 +595,7 @@ The bounded, offline loading/recovery checks are:
|
|
|
584
595
|
npm run test:integration -- test/integration/workflow-tool-abort-resume.test.ts test/integration/workflow-tool-frontier-consumption.test.ts test/integration/workflow-tool-node-quit-cli.test.ts
|
|
585
596
|
```
|
|
586
597
|
|
|
587
|
-
These cover public
|
|
598
|
+
These cover public pause/resume, typed recovery and rejection, a fresh DBOS adapter, and the built Node CLI with a fixture provider. The CLI fixture uses an isolated in-memory backend, not the production database or a release workflow.
|
|
588
599
|
|
|
589
600
|
**Historical release incident.** Run `877a91c7-ed68-4ace-9895-cf2555d9b154` (`publish-release`) stopped at `wait-required-ci`, node `tool:h09e55b6c3ff8718bdf7066c90323715f`, after `prepare-changelog-branch` and `validate-commit-push-open-pr`. Read-only investigation found completed checkpoints and the targeted-abort terminal error, but no typed unfinished-tool checkpoint for that node. The patch therefore cannot safely resume this exact retained record automatically; it does not import its chat/status snapshots or edit the database.
|
|
590
601
|
|
|
@@ -600,7 +611,7 @@ The supported explicit recovery route is operator reconciliation followed, if au
|
|
|
600
611
|
| **Process crash** | Workflow remains `running` in durable state. Exact-id status/inspection reconstructs its retained checkpoint DAG as `crashed` once the owner heartbeat is stale and shows whether explicit resume is available. `/workflow resume <id>` is still required to claim the root and continue from the last completed checkpoint. |
|
|
601
612
|
| **`ctx.tool` retry/default failure** | When `retriesAllowed: true`, the tool function is retried with exponential backoff. Cancellation is checked before each attempt, during retry backoff, and through the callback's own `signal`. Without `failureMode: "return"`, an exhausted callback error propagates and the workflow fails. |
|
|
602
613
|
| **Recoverable `ctx.tool` failure** | With `failureMode: "return"`, exhausted callback failures are durably returned after retries. The tool node remains failed, downstream handoff is explicit, and replay returns the same outcome with `cached: true`. Cancellation and storage faults still throw. |
|
|
603
|
-
| **`ctx.tool` node quit/
|
|
614
|
+
| **`ctx.tool` node quit/pause** | `quit`/`pause` with a tool node id or name aborts that call's signal, marks the node `cancelled`, and leaves sibling stages and tools running. The action returns `status: "cancelled"` with the separately observed `workflowStatus`; it never reports the run as paused. No replayable `tool:` checkpoint and no `return_failure` outcome are written — return mode writes only inspection metadata — so resume re-runs exactly that call at the same ordinal and node id. |
|
|
604
615
|
| **Run quit with in-flight tools** | Quit closes tool admission after stage pauses acknowledge, rescans every root/nested node, aborts that set, and waits a bounded interval before recording the durable paused/resumable transition, so the run is not declared quiesced while a callback still runs and no late call can slip in. A tool-only run pauses as resumable instead of reporting no controllable stages. A call attempted after the close is refused with the graceful-quit signal. Catching the cancellation in workflow code cannot turn the quit into a completed run. |
|
|
605
616
|
| **Abandoned `ctx.tool` callback** | A callback that ignores its abort signal is abandoned after the bounded wait: quit proceeds, the node is published as `cancelled`, each abandoned call is reported as an owning `{runId, nodeId}` identity, and the stale background job is detached so resume relaunches a fresh executor under the same workflow id. A late return from that callback is discarded before persistence, cannot become a checkpoint, and cannot mutate or unregister the replacement run. |
|
|
606
617
|
| **`ctx.ui` pending prompt** | If a UI prompt was not answered before interruption, resume leaves off on that prompt — the user must answer it to continue. |
|
|
@@ -719,7 +730,7 @@ export default workflow({
|
|
|
719
730
|
|
|
720
731
|
`maxDurationMs` and `maxTokens` must be non-negative finite integers. `maxCost` and `warnAtPercent` must be non-negative finite numbers. Invalid config produces `CONFIG_INVALID`; invalid authored or direct-run declarations throw a `TypeError` before the workflow body runs. Nested `ctx.workflow(child)` calls use the child's own declared budget and remain subject to the root run's duration scope; a root exhaustion wins simultaneous child exhaustion, while a child-only exhaustion soft-lands that child run and returns to the parent.
|
|
721
732
|
|
|
722
|
-
`maxDurationMs` is enforced at stage and durable-tool boundaries, and again immediately after a completed `ctx.task` persists its result checkpoint. Elapsed run time excludes paused time and resumed runs carry prior elapsed time. If that post-task boundary finds the duration ceiling already exhausted, the root becomes a resumable `budget_exceeded` block instead of remaining raw `running` with no active or control node. When every authored task in `ctx.parallel(..., { failFast: false })` reaches that boundary together after persisting a complete result, Atomic waits for the tasks to settle and deterministically uses the earliest authored exhausted task as the resume frontier rather than replacing the budget stop with a generic parallel failure. A raised-budget resume then replays those completed task results without rerunning their models and continues after the parallel barrier. If any authored parallel task was still pending at exhaustion, or any task instead has an ordinary failure, the existing aggregate-failure behavior wins. A root still awaiting that task-result persist remains quit/
|
|
733
|
+
`maxDurationMs` is enforced at stage and durable-tool boundaries, and again immediately after a completed `ctx.task` persists its result checkpoint. Elapsed run time excludes paused time and resumed runs carry prior elapsed time. If that post-task boundary finds the duration ceiling already exhausted, the root becomes a resumable `budget_exceeded` block instead of remaining raw `running` with no active or control node. When every authored task in `ctx.parallel(..., { failFast: false })` reaches that boundary together after persisting a complete result, Atomic waits for the tasks to settle and deterministically uses the earliest authored exhausted task as the resume frontier rather than replacing the budget stop with a generic parallel failure. A raised-budget resume then replays those completed task results without rerunning their models and continues after the parallel barrier. If any authored parallel task was still pending at exhaustion, or any task instead has an ordinary failure, the existing aggregate-failure behavior wins. A root still awaiting that task-result persist remains quit/pause-controllable and is not diagnosed as a stranded root; aborting it after the persist has already started does not leave an unobserved checkpoint rejection or block durable finalization behind the original write. Catching that rejection in workflow code and returning outputs does not overwrite the requested pause. Exact-id quit or pause of a nested task tail suspends the aggregate root so the parent snapshot and durable handle do not stay raw `running`. A later resume reuses a completed task result or, if only the terminal stage checkpoint exists, that complete `WorkflowTaskResult` including the exact persisted text (schema-backed `maxOutput` truncation included), primitive structured values such as `null`, generated worktree artifacts, warnings, session, and model metadata, without rerunning the model. Exact `workflow status <id>` uses the live tool-control registry and reports the same stranded-root diagnostic as the listing. Same-process DBOS hydration rejects unknown checkpoint history instead of skipping it. `maxTokens` counts uncached input plus output tokens across the complete run tree, including nested children and stage retries; cache reads and writes remain reported counters and `maxCost` charges the summed `usage.cost`. A `budget_warning` lifecycle notice is emitted once per run and dimension at `warnAtPercent` (default `80`); exhaustion gives an already-live frontier stage one current-turn wrap-up, then records a resumable `budget_exceeded` blocked result with its reading, ceiling, frontier, wrap-up summary, and the wrap-up turn's own `wrapUpUsage` when model usage is available. No new stage is created just to host a wrap-up; when no stage turn is live at the exhausting boundary, the run stops with no wrap-up summary and leaves the once-per-run delivery allowance unused. A resumed run carries prior duration, token, and cost spend without double-charging replayed completions; pass a raised resume budget to continue with the prior spend. Nested child budgets meter only their subtree while the root meter still includes child spend, and a child-only exhaustion returns to the parent as a blocked child result while the parent continues.
|
|
723
734
|
|
|
724
735
|
## Workflow Configuration
|
|
725
736
|
|
|
@@ -745,7 +756,7 @@ Example config:
|
|
|
745
756
|
"team": { "path": "./workflows/team.ts" },
|
|
746
757
|
"shared": { "path": "/shared/team/workflows" }
|
|
747
758
|
},
|
|
748
|
-
"defaultConcurrency":
|
|
759
|
+
"defaultConcurrency": 3,
|
|
749
760
|
"maxDepth": 4,
|
|
750
761
|
"budget": { "maxDurationMs": 0, "maxTokens": 0, "maxCost": 0, "warnAtPercent": 80 },
|
|
751
762
|
"persistRuns": true,
|
|
@@ -765,14 +776,14 @@ Runtime config defaults:
|
|
|
765
776
|
|
|
766
777
|
| Key | Default | Purpose |
|
|
767
778
|
|-----|---------|---------|
|
|
768
|
-
| `defaultConcurrency` | `
|
|
779
|
+
| `defaultConcurrency` | `3` | Default stage concurrency and concurrency for authored `ctx.parallel(...)` execution |
|
|
769
780
|
| `maxDepth` | `4` | Maximum workflow nesting depth |
|
|
770
781
|
| `budget` | `{ maxDurationMs: 0, maxTokens: 0, maxCost: 0, warnAtPercent: 80 }` | Default per-run budget declaration; `0` disables a dimension; warnings default to `80` percent |
|
|
771
782
|
| `persistRuns` | `true` | Persist run metadata for status/resume/history |
|
|
772
783
|
| `statusFile` | `false` | Write a derived status file; defaults under `.atomic/workflows/status.json` when enabled |
|
|
773
784
|
| `resumeInFlight` | `"ask"` | Behavior when discovering resumable in-flight work |
|
|
774
785
|
| `workflowNotifications.enabled` | `true` | Emit workflow lifecycle notices into the active main chat |
|
|
775
|
-
| `workflowNotifications.notifyOn` | `["started", "completed", "failed", "blocked", "budget_warning", "awaiting_input", "paused", "quit", "resumed"]` | Lifecycle states to track; terminal `completed`/`failed`/`blocked` outcomes, active recoverable blocks, duration budget warnings, and
|
|
786
|
+
| `workflowNotifications.notifyOn` | `["started", "completed", "failed", "blocked", "budget_warning", "awaiting_input", "paused", "quit", "resumed"]` | Lifecycle states to track; terminal `completed`/`failed`/`blocked` outcomes, active recoverable blocks, duration budget warnings, and attributed user `started`/`quit`/`resumed` actions on a top-level run create main-chat notices. `pause` does not attribute an actor; `awaiting_input` is tracked for dedupe/restore without waking the main agent. |
|
|
776
787
|
| `worktree.symlinkDirectories` | `["node_modules"]` | Main-root directories symlinked into each runner-managed temporary worktree during post-creation setup |
|
|
777
788
|
|
|
778
789
|
Invalid JSON or invalid shapes produce `CONFIG_INVALID` diagnostics. Missing config files are ignored.
|
|
@@ -1168,7 +1168,7 @@ Good workflows are information-flow systems, not just prompt sequences. Keep sta
|
|
|
1168
1168
|
- Do not rely on undeclared child outputs; returning a key that is not declared in `outputs` fails the run. Declare every child-workflow field you expose in `outputs` — including `result` — and return values matching those schemas from `run` (see [Outputs](/workflows/authoring#outputs)).
|
|
1169
1169
|
- Do not expect to select or rename child outputs at the call site; parent workflows receive the child's declared output contract as `child.outputs` after checking `child.exited === false`, and a partial declared-output map when `child.exited === true`.
|
|
1170
1170
|
- Do not expect named workflow runs to block the chat turn; they are background tasks.
|
|
1171
|
-
- Use `
|
|
1171
|
+
- Use `pause` when the user asks to pause specific live work resumably; use `quit` for a graceful run-level process boundary.
|
|
1172
1172
|
- Keep stage names readable because they appear in workflow status and UI.
|
|
1173
1173
|
- Do not ask a stage to reason from workflow or stage names that are only orchestration labels. Model stages see their local prompt, artifacts, tools, and reads; describe the concrete action and evidence instead of referring to an implementation-specific nickname.
|
|
1174
1174
|
- Do not write stage prompts that depend on hidden workflow-wide awareness; make each model stage locally scoped and self-described ([Locally Scoped Stage Prompts](#locally-scoped-stage-prompts)).
|
|
@@ -1348,7 +1348,7 @@ A failed test becomes the next objective.
|
|
|
1348
1348
|
Validation failed on `[command]`. Treat that as the source of truth. Fix the root cause only, rerun the failing check, then report the result.
|
|
1349
1349
|
```
|
|
1350
1350
|
|
|
1351
|
-
#### 8.
|
|
1351
|
+
#### 8. Pause stale or wrong work
|
|
1352
1352
|
|
|
1353
1353
|
If a run is solving the wrong problem, based on outdated assumptions, or duplicating another run, stop it. Continuing usually creates more cleanup.
|
|
1354
1354
|
|
|
@@ -2093,7 +2093,7 @@ These anti-patterns target run prompts; [Common Mistakes](#common-mistakes) cove
|
|
|
2093
2093
|
| `Fix this.` | `Fix [specific failure]; done means [test command] passes.` |
|
|
2094
2094
|
| No validation step | Require tests, smoke checks, typecheck, or explicit manual verification. |
|
|
2095
2095
|
| Broad refactors | Constrain the run to the files needed for the objective. |
|
|
2096
|
-
| Letting a wrong stage continue | Redirect or
|
|
2096
|
+
| Letting a wrong stage continue | Redirect or pause as soon as the agent follows the wrong signal. |
|
|
2097
2097
|
| Accepting unverified summaries | Ask for changed files, commands run, results, and remaining risks. |
|
|
2098
2098
|
| Mixing investigation and implementation too early | Ask for root cause and proposed fix before code changes. |
|
|
2099
2099
|
| Ignoring blocked stages | Answer directly with one decision and any constraints. |
|