@bastani/atomic 0.9.13-alpha.4 → 0.9.14-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 (101) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/dist/builtin/i-have-adhd/CHANGELOG.md +13 -0
  3. package/dist/builtin/i-have-adhd/package.json +1 -1
  4. package/dist/builtin/intercom/CHANGELOG.md +26 -0
  5. package/dist/builtin/intercom/package.json +1 -1
  6. package/dist/builtin/mcp/CHANGELOG.md +15 -0
  7. package/dist/builtin/mcp/package.json +1 -1
  8. package/dist/builtin/subagents/CHANGELOG.md +41 -0
  9. package/dist/builtin/subagents/package.json +1 -1
  10. package/dist/builtin/web-access/CHANGELOG.md +14 -0
  11. package/dist/builtin/web-access/package.json +1 -1
  12. package/dist/builtin/workflows/CHANGELOG.md +58 -0
  13. package/dist/builtin/workflows/README.md +9 -0
  14. package/dist/builtin/workflows/builtin/adversarial-verification.ts +4 -0
  15. package/dist/builtin/workflows/builtin/classify-and-act.ts +4 -0
  16. package/dist/builtin/workflows/builtin/fan-out-and-synthesize.ts +4 -0
  17. package/dist/builtin/workflows/builtin/generate-and-filter.ts +4 -0
  18. package/dist/builtin/workflows/builtin/goal.ts +4 -0
  19. package/dist/builtin/workflows/builtin/loop-until-done.ts +4 -0
  20. package/dist/builtin/workflows/builtin/open-claude-design.ts +4 -0
  21. package/dist/builtin/workflows/builtin/ralph.ts +4 -0
  22. package/dist/builtin/workflows/builtin/tournament.ts +4 -0
  23. package/dist/builtin/workflows/package.json +1 -1
  24. package/dist/builtin/workflows/src/authoring/workflow.ts +9 -0
  25. package/dist/builtin/workflows/src/authoring.d.ts +3 -0
  26. package/dist/builtin/workflows/src/durable/completed-catalog.ts +13 -2
  27. package/dist/builtin/workflows/src/durable/workflow-heartbeat-anchor.ts +153 -0
  28. package/dist/builtin/workflows/src/engine/run-durable-stage-session.ts +28 -0
  29. package/dist/builtin/workflows/src/engine/run.ts +1 -0
  30. package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +138 -0
  31. package/dist/builtin/workflows/src/extension/index.bundle.mjs +766 -4
  32. package/dist/builtin/workflows/src/extension/workflow-heartbeat-delivery.ts +197 -0
  33. package/dist/builtin/workflows/src/extension/workflow-heartbeat-notice.ts +115 -0
  34. package/dist/builtin/workflows/src/extension/workflow-heartbeat-scheduler.ts +962 -0
  35. package/dist/builtin/workflows/src/sdk-surface.ts +9 -0
  36. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +1 -0
  37. package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +1 -0
  38. package/dist/builtin/workflows/src/shared/persistence-restore.ts +12 -0
  39. package/dist/builtin/workflows/src/shared/workflow-authoring-types.d.ts +1 -0
  40. package/dist/builtin/workflows/src/shared/workflow-authoring-types.ts +1 -0
  41. package/dist/builtin/workflows/src/shared/workflow-heartbeat-contract.d.ts +21 -0
  42. package/dist/builtin/workflows/src/shared/workflow-heartbeat-contract.ts +25 -0
  43. package/dist/core/extensions/index.d.ts +1 -0
  44. package/dist/core/extensions/index.d.ts.map +1 -1
  45. package/dist/core/extensions/index.js +1 -0
  46. package/dist/core/extensions/index.js.map +1 -1
  47. package/dist/core/extensions/ui-types.d.ts +26 -0
  48. package/dist/core/extensions/ui-types.d.ts.map +1 -1
  49. package/dist/core/extensions/ui-types.js +17 -1
  50. package/dist/core/extensions/ui-types.js.map +1 -1
  51. package/dist/core/tools/ask-user-question/ask-user-question.d.ts +21 -0
  52. package/dist/core/tools/ask-user-question/ask-user-question.d.ts.map +1 -1
  53. package/dist/core/tools/ask-user-question/ask-user-question.js +34 -9
  54. package/dist/core/tools/ask-user-question/ask-user-question.js.map +1 -1
  55. package/dist/core/tools/ask-user-question/view/components/multi-select-view.d.ts.map +1 -1
  56. package/dist/core/tools/ask-user-question/view/components/multi-select-view.js +5 -2
  57. package/dist/core/tools/ask-user-question/view/components/multi-select-view.js.map +1 -1
  58. package/dist/core/tools/ask-user-question/view/components/submit-picker.d.ts.map +1 -1
  59. package/dist/core/tools/ask-user-question/view/components/submit-picker.js +3 -1
  60. package/dist/core/tools/ask-user-question/view/components/submit-picker.js.map +1 -1
  61. package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts.map +1 -1
  62. package/dist/core/tools/ask-user-question/view/components/wrapping-select.js +8 -1
  63. package/dist/core/tools/ask-user-question/view/components/wrapping-select.js.map +1 -1
  64. package/dist/index-extensions.d.ts +1 -1
  65. package/dist/index-extensions.d.ts.map +1 -1
  66. package/dist/index-extensions.js +1 -1
  67. package/dist/index-extensions.js.map +1 -1
  68. package/dist/modes/interactive/components/reserved-bottom-overlay.d.ts +95 -0
  69. package/dist/modes/interactive/components/reserved-bottom-overlay.d.ts.map +1 -0
  70. package/dist/modes/interactive/components/reserved-bottom-overlay.js +234 -0
  71. package/dist/modes/interactive/components/reserved-bottom-overlay.js.map +1 -0
  72. package/dist/modes/interactive/interactive-extension-custom-ui.js +94 -6
  73. package/dist/modes/interactive/interactive-extension-custom-ui.js.map +1 -1
  74. package/dist/modes/interactive/interactive-mode-base.d.ts +3 -0
  75. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  76. package/dist/modes/interactive/interactive-mode-base.js +5 -1
  77. package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
  78. package/dist/modes/interactive/interactive-mode-surface.d.ts +2 -0
  79. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  80. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  81. package/dist/modes/interactive/interactive-tui.d.ts +3 -0
  82. package/dist/modes/interactive/interactive-tui.d.ts.map +1 -1
  83. package/dist/modes/interactive/interactive-tui.js +14 -0
  84. package/dist/modes/interactive/interactive-tui.js.map +1 -1
  85. package/dist/modes/interactive-engine/engine-custom-ui.d.ts +2 -0
  86. package/dist/modes/interactive-engine/engine-custom-ui.d.ts.map +1 -1
  87. package/dist/modes/interactive-engine/engine-custom-ui.js +1 -0
  88. package/dist/modes/interactive-engine/engine-custom-ui.js.map +1 -1
  89. package/dist/modes/interactive-engine/protocol.d.ts +1 -0
  90. package/dist/modes/interactive-engine/protocol.d.ts.map +1 -1
  91. package/dist/modes/interactive-engine/protocol.js +1 -0
  92. package/dist/modes/interactive-engine/protocol.js.map +1 -1
  93. package/dist/modes/interactive-engine/remote-component.d.ts +3 -1
  94. package/dist/modes/interactive-engine/remote-component.d.ts.map +1 -1
  95. package/dist/modes/interactive-engine/remote-component.js +3 -2
  96. package/dist/modes/interactive-engine/remote-component.js.map +1 -1
  97. package/docs/extensions.md +27 -0
  98. package/docs/keybindings.md +1 -0
  99. package/docs/workflows.md +34 -0
  100. package/npm-shrinkwrap.json +29 -29
  101. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -2,6 +2,99 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.9.14-alpha.1] - 2026-08-14
6
+
7
+ ### Fixed
8
+
9
+ - **The whole transcript stays readable while the `ask_user_question` dialog is open** ([#2378](https://github.com/bastani-inc/atomic/issues/2378)). The dialog used to mount inline in the fullscreen dock, where it is a flex sibling of the transcript viewport, so a tall side-by-side questionnaire took the transcript's rows: on a 200×40 terminal the viewport collapsed from 34 rows to 6, and because the page step is derived from that viewport, `PageUp` crawled two lines at a time through a six-line window. The dialog is now pinned to the bottom as an overlay, which is not measured into the layout, so the transcript keeps its full viewport height and full page step — a single `PageUp` moves a full page and `Home` reaches the top of the scrollback. The overlay is also bounded so at least six transcript rows always survive on a short terminal, and the rows it does cover are added back to the transcript's scroll extent, so scrolling to the end raises the newest output into the visible strip instead of leaving it stranded behind the dialog. Transcript page, Home/End, and wheel input remain available while the questionnaire's Notes editor is active. Notes keeps text and edit keys, and the dialog still owns arrows, `enter`, `tab`, `space`, `esc`, clicks, and selection.
10
+ - **The active questionnaire row stays visible on a short terminal.** Bounding a tall dialog has to drop rows, and dropping them from a fixed head cropped away the control you were using: at 80 columns a single-select option disappeared as the arrow keys moved down, multi-select choices plus their Next and Submit rows vanished, and the focused Notes input was hidden. The dialog now keeps a window around every active row, including single- and multi-select choices, Next, Submit, Cancel, inline sentinels, and focused text inputs.
11
+
12
+ ### Added
13
+
14
+ - `ctx.ui.custom()` accepts `reserveTranscriptRows` for bottom-anchored overlay mounts. A blocking dialog sets it to be height-bounded against the terminal and to have its covered transcript-bottom suffix added to the scroll extent. Numeric and percentage `maxHeight` limits are applied before active-row windowing, so the focused control is not removed by a second fixed-head crop. Top and bottom margins are included in the overlay geometry, and concurrent overlays share the connected covered suffix instead of adding duplicate padding. Mount and resize height changes settle through one automatic repaint. A hidden overlay reserves nothing, and the reserve returns when it becomes visible again; handle-based, closed, or raw host removal releases the exact registration. Reserving overlays require a bottom anchor and reject `row` or a nonzero `offsetY`; leave the option unset for an overlay meant to take the screen, such as the workflow graph.
15
+ - `OVERLAY_ACTIVE_ROW_MARKER` is exported for components mounted that way. Embed it in the line you most need kept — the selected row of a list — and the bound places what it keeps around that row instead of taking a fixed head. It is a zero-width APC sequence the host strips before painting, so it never reaches the terminal.
16
+
17
+ ## [0.9.13] - 2026-08-13
18
+
19
+ Cumulative release of the `0.9.13-alpha.1` – `0.9.13-alpha.4` prereleases. The summary below covers the user-visible outcome of that work; the per-change detail remains in the prerelease sections below.
20
+
21
+ ### Breaking Changes
22
+
23
+ - **Interactive TTY sessions always use the fullscreen renderer.** The `--tui-mode` flag, the `tuiMode` JSON setting, the `/settings` TUI-mode row, the settings-manager accessors, and the exported `TuiMode` alias are removed, with no replacement for `--tui-mode regular`. `fullscreenScrollbar` remains configurable, non-TTY output continues through print mode, and internal `TERM=dumb` and CI guards keep the main-screen renderer as a non-user-selectable fallback.
24
+ - **The `bash` tool no longer accepts its `async` parameter,** and the `__atomic_bash_job` and `__atomic_bash_job_cancel` polling and cancellation commands are gone. Drop `async` from bash calls, and run a long-lived command in a separate shell or process manager.
25
+ - **Streaming `message_update` events are delta-only across the public API.** JSON, RPC, SDK, and extension subscribers receive only `type` plus the raw `assistantMessageEvent`; the cumulative `message` and wire-only `assistantMessageEvent.partial` fields are gone. `message_start` seeds the message, ordered deltas build it without deduplication, and `message_end` supplies the authoritative final message, so a consumer that read `message` or `partial` must accumulate deltas itself. This also removes quadratic wire amplification: an 8000-character answer wrote 8,428,280 bytes across 501 frames and now writes 65,588.
26
+ - **`ctx.ui.custom()` components must report input consumption.** `handleInput` returns `true` when it consumes a key and `false` or `undefined` when it does not; a handler returning `void` must migrate. In fullscreen mode an unhandled viewport key may reach the transcript, and a remote component disposed before its reply is deliberately dropped rather than replayed.
27
+ - **Custom extension runtime and UI implementations must adopt lifecycle fields.** An exported `ExtensionRuntime` must provide `trackEventBusSubscription(unsubscribe)`, and `ExtensionUIContext.getChatRenderSettings()` must return `markdownTransformers`. `pi.events` is bound to an extension instance: subscriptions auto-unsubscribe on reload or disposal, and calling `on()` or `emit()` through a captured stale handle throws.
28
+ - **Extension provider OAuth `login` and `refreshToken` take abort signals.** The contracts are `login(callbacks, signal)` and `refreshToken(credentials, signal)` with a required concrete `AbortSignal`, so cancelling authentication or credential refresh abandons the extension's in-flight network work. Implementations must accept the parameter and forward it into their requests.
29
+ - **`ModelRegistry.refresh()` returns `ModelsRefreshResult`** — `{ aborted, errors }`, a per-provider `ReadonlyMap<string, Error>` plus a cancellation flag — instead of `Promise<void>`, so a refresh in which some providers failed, or which was aborted, is no longer indistinguishable from a clean success. A caller that awaited it only for completion needs no change.
30
+ - **`ModelRuntime.setRuntimeApiKey()` no longer refreshes the model catalog.** It takes auth-cancellation options and does only what its name says: record the runtime key and mark the provider configured, aborting early if the signal is already aborted. A caller that needs a fresh catalog must now call `refresh({ providers: [providerId], signal })`.
31
+
32
+ ### Added
33
+
34
+ - Added the fullscreen interactive TUI layout. The transcript scrolls above a sticky dock holding the editor, status line, usage meter, extension widgets, and footer, and terminal resizes preserve that layout. Wheel and trackpad gestures reach focused workflow overlays before the transcript viewport, and events those overlays do not consume fall through ([#2269](https://github.com/bastani-inc/atomic/pull/2269), [#2274](https://github.com/bastani-inc/atomic/pull/2274)).
35
+ - Added a draggable fullscreen transcript scrollbar configured as `auto`, `always`, or `hidden` through `/settings`. Themes may set `scrollbarThumb`, which falls back to `selectedBg` for existing custom themes ([#2275](https://github.com/bastani-inc/atomic/pull/2275)).
36
+ - Added fullscreen transcript navigation for page, opt-in half-page, marked-message, `home`, and `end` actions, while `ctrl+home`, `ctrl+end`, `ctrl+pageUp`, and `ctrl+pageDown` remain available to the editor. Matching viewport bindings pass through to focused overlays and inline custom components. Prompt history can be enabled with explicit `tui.editor.historyPrevious`/`historyNext` bindings such as `ctrl+p`/`ctrl+n`, which take precedence over ordinary action handlers while the editor is focused ([#2281](https://github.com/bastani-inc/atomic/pull/2281), [#2296](https://github.com/bastani-inc/atomic/pull/2296), [#2300](https://github.com/bastani-inc/atomic/pull/2300)).
37
+ - Added a clickable, centered jump-to-bottom indicator above the fullscreen input dock when the transcript is scrolled away from the live end. It displays the live `tui.altScreen.bottom` key binding and supports mouse activation ([#2361](https://github.com/bastani-inc/atomic/issues/2361)).
38
+ - Windows fullscreen sessions paste system clipboard text on a secondary-button press, with focus checks and bracketed-paste framing preserved for host and engine-owned editors.
39
+ - Added generated session summaries to the resume picker, so `/resume` and `atomic -r` show what a conversation was about in a dedicated summary column beside the session name or first message. Atomic writes a one-line summary once the agent goes idle, reusing the session's existing model, credentials, stream function, and retry policy, and persists it as a `session_summary` entry anchored to the message it describes; a later message or branch summary retires it. Generation is best-effort and invisible: it is skipped for very short sessions, workflow stage sessions, and `--print`/JSON modes, never blocks a turn, is cancelled when the next prompt starts, and a slow request that outlives the conversation discards its own result. Set `sessionSummary.enabled` to `false` to disable it ([#1033](https://github.com/bastani-inc/atomic/issues/1033)).
40
+ - Added the bundled, default-on ADHD-friendly response mode. Its hidden rules message persists across turns, resumes, branches, and compaction; `/i-have-adhd on|off`, `--no-adhd`, `.i-have-adhd-off`, and the `stop adhd mode` / `normal mode` phrases control it.
41
+ - Added `atomic auth check` to verify a provider or model's effective authentication before a session starts. It reports `ready`, `not_ready`, or `invalid`, supports JSON output and a no-refresh read-only mode, and emits no credential material unless the explicit `--credentials` opt-in is present.
42
+ - Added an **experimental** `@bastani/atomic/client` entrypoint with `RemoteSession` lifecycle management and transcript projection helpers for remote protocol sessions.
43
+ - Added the configurable and package-exported `createCodingAgentHarness()` factory, with Atomic's hashline coding tools, live prompt metadata, Atomic and `PI_*` bash session variables, and injectable pi-agent-core execution environments for read (including directory trees), bash, edit, write, and find, rejecting at runtime an environment that omits `renameFile()`. The default `search` tool remains local until its filesystem and matching pipeline gains a complete remote operations seam; read and edit retain local path-variant probes and notebook projection, read also retains local archive, SQLite, and internal-resource selectors, write retains local generated-file, shebang, conflict, and resource helpers, and bash validates its cwd locally.
44
+ - Added `pi.registerMarkdownTransformer()` for ordered, synchronous, display-only Markdown transforms in user, assistant, and assistant-thinking output. Each transformer receives the prior output plus message type, streaming state, and available width; an error leaves the current display text intact and never alters stored messages or model context ([#7231](https://github.com/earendil-works/pi/pull/7231) by [@xl0](https://github.com/xl0)).
45
+ - Added themed Unicode Mermaid diagrams in interactive transcripts, with optional rendering while responses stream ([#7624](https://github.com/earendil-works/pi/pull/7624) by [@xl0](https://github.com/xl0)), and configurable terminal-friendly Unicode LaTeX math via the `markdown.latex` setting. See [Markdown settings](docs/settings.md#markdown).
46
+ - Added per-directory `AGENTS.override.md` context files. An override replaces that directory's `AGENTS.md` or `CLAUDE.md` while global and ancestor context still layers normally; the same canonical filename works through Atomic's legacy `.pi` and `PI_*` agent-directory aliases ([#7681](https://github.com/earendil-works/pi/pull/7681) by [@m3](https://github.com/m3)).
47
+ - Added `AI_AGENT=atomic` attribution at the CLI, RPC, and compiled-binary entry points and across Atomic-owned child-process environments, including bash and tool commands, isolated RPC children, subagent and workflow runners, MCP and web-access subprocesses, and the intercom broker. Child environments follow upstream's overwrite policy while cloning rather than mutating caller-supplied environment objects.
48
+ - Added arbitrary OpenAI-compatible `samplingParams` for custom model definitions, per-model overrides, extension providers, and per-request stream options. Parameters merge by key, preserve provider-specific names, and apply to OpenAI-compatible request bodies; a malformed configuration shape now fails during `models.json` loading. Added opt-in vLLM `compat.supportsThinkingTokenBudget` handling and `compat.supportsFinishReason` stop inference for finishless OpenAI-compatible streams.
49
+ - Added built-in Baseten support with `BASETEN_API_KEY` and the `zai-org/GLM-5.2` default, and Qwen Token Plan Individual with the documented subscription catalog, the shared international `QWEN_TOKEN_PLAN_API_KEY`, and `qwen3.8-max` as its default model.
50
+ - Tool output that spills to disk is now stored per session and reaped on age, so the system temp directory can no longer fill up with orphaned files. `bash` overflow logs, streamed spill files, and the in-memory-session tool-result fallback move into one owner- and session-scoped tree, `<tmpdir>/atomic-<uid>/<session-id>/`, created `0700` with `0600` files where the platform supports it; on Windows the domain-qualified account name plus a short digest replaces the uid, so two domain principals sharing a bare account name cannot land on one tree. Every component below the system temp directory is created and checked one level at a time: a symlink is refused, a foreign-owned directory is refused, and one of this account's left too permissive is tightened and re-checked. Refusal fails closed — the tool runs with no spill file rather than writing command output through a link someone else planted. On Windows that ownership check reads the security descriptor through PowerShell `Get-Acl` and decides from SIDs alone, caching a successful verification per path per process keyed to the directory's metadata identity ([#2245](https://github.com/bastani-inc/atomic/issues/2245)).
51
+ - Persisted tool-output files are capped at 64 MB each, cut on a character boundary even when a multi-byte character spans two chunks, with bytes that are not valid UTF-8 written through unchanged. A spill file that fails to write is no longer advertised, and a storage fault never changes the outcome of the command that produced the output. A deferred, unref'd sweep started shortly after session startup removes session temp trees and `tool-results` directories whose newest file is more than 30 days old, throttled once a day per target and guarded by an exclusive lock; session transcripts and `.jsonl` session files are outside its scope, and a tree belonging to a running session is never reaped.
52
+ - Added `terminate` to blocked extension `tool_call` results. A terminating blocked call suppresses the follow-up model request only when every finalized call in its batch also terminates.
53
+ - Added `CredentialSynchronizationError` for a credential mutation that commits successfully but fails to update local model state, so credential-save failures stay distinct from post-commit synchronization failures in interactive auth surfaces.
54
+ - `CreateAgentSessionOptions` and `ExtensionContext` carry an immutable typed subagent child capability policy for in-process tool registration, and `CreateAgentSessionOptions` supports construction-time system-prompt and inherited-context transforms, so an in-process child session applies typed policy without extension injection ([#2188](https://github.com/bastani-inc/atomic/issues/2188)).
55
+ - Added immutable system-prompt contribution constants for Atomic's builtin coding tools, keeping custom harnesses and tool registries aligned with the prompt metadata the builtin factories use.
56
+
57
+ ### Changed
58
+
59
+ - Adopted the pi 0.84.1 coding-agent, agent, AI, client, protocol, telemetry, and TUI runtime surfaces, which supply the renderer behavior described above. Atomic keeps its isolated interactive engine, provider, path, and legacy `PI_*`/`.pi` compatibility surfaces, and uses the dependency's renderer and layout implementations rather than carrying forked copies.
60
+ - Bundled subagent launches use in-process `AgentSession` children with typed admission, canonical identities, and live continuation, and typed child policy now carries MCP direct-tool selection and admission-issued Intercom identity and capability, so an in-process extension consumes child policy without inheritable environment state ([#2188](https://github.com/bastani-inc/atomic/issues/2188)).
61
+ - Live tool-call argument previews no longer update while a call's arguments are still streaming. The assistant message is rebuilt from `message_update` deltas, and `toolcall_start`/`toolcall_delta` carry an unparsable fragment of JSON rather than a usable value, so the accumulator ignores them and installs the complete parsed call at `toolcall_end`. The tool card still appears as soon as the call starts, and the arguments it shows are correct and complete; the progressively filled preview is gone, which is the deliberate trade for removing the quadratic amplification above.
62
+ - Footer and workflow stage-chat extension statuses are joined with a dim `•` separator instead of a bare space, so adjacent statuses read as distinct items.
63
+
64
+ ### Fixed
65
+
66
+ - Fixed a stall where a message queued during a turn was shown in the transcript but never answered, leaving the session idle until you typed another message. Escape and Ctrl+C hold queued work by pausing the agent's own steering and follow-up queues, but a message the agent loop has already polled is in neither queue: it is written straight into the transcript, so the interrupt cancelled its reply and nothing scheduled a new one. This was most visible after answering an `ask_user_question` questionnaire. An admitted message whose reply was cancelled before it produced any output now receives that reply, and a message sent while that reply is still streaming is delivered as soon as it finishes. Messages that are still held are unchanged, and a reply that had already streamed some output is never restarted ([#2362](https://github.com/bastani-inc/atomic/issues/2362)).
67
+ - Fixed fullscreen mouse routing for focused workflow graphs and stage chats. SGR/X10 wheel and click sequences reach the focused overlay through the bounded custom-input reply path before the alternate-screen viewport, so graph scrolling and click-to-attach work without changing keyboard viewport fallback ([#2303](https://github.com/bastani-inc/atomic/issues/2303)). OSC-8 jump-to-bottom activation lets the focused overlay claim its own transcript jump before falling back to the host transcript ([#2370](https://github.com/bastani-inc/atomic/issues/2370)).
68
+ - Fixed fullscreen viewport shortcuts stealing matching keys from a focused overlay or inline custom component: a focused handler receives the key exactly once and can return `false` to let a no-op fall through to transcript navigation, input replies correlate with each forwarded key, and transcript scrolling proceeds on an explicit `false`, a failed reply, or a bounded timeout. Fullscreen shutdown avoids restarting the replacement renderer.
69
+ - Fixed isolated overlay components such as workflow stage chat failing to re-render after a terminal height change, which could clip the composer and footer after a vertical resize, and fixed isolated fullscreen tool renderers rebuilding Kitty image components on every engine render, which retransmitted visible payloads and rendered the transcript twice per frame.
70
+ - Fixed fullscreen overlay text selection when one input read contains multiple SGR mouse reports. Reports are replayed one at a time, while Shift/Option/Ctrl-modified presses and motion stay out of application selection; modifier-bearing SGR releases still close an active selection, and legacy X10 release remains excluded because pi-tui cannot identify it for selection.
71
+ - Fixed main-chat model fallback to classify provider failures consistently with workflows, advance rejected credentials and incompatible or unavailable models to the next candidate, and restore the user-selected model at the next turn without overriding an explicit `/model` choice. A failure that requesting the same model again cannot repair leaves that model out of the chain for the rest of the turn at every reasoning level, while transient rate-limit and transport failures keep those reasoning variants. A context overflow compaction cannot resolve now advances the chain instead of ending the turn, a reasoning-level change no longer strands the session on the fallback model, and gRPC `ResourceExhausted` errors are retried like other transient failures ([#2170](https://github.com/bastani-inc/atomic/issues/2170)).
72
+ - Fixed a GitHub Copilot `openai-responses` session hard-stopping with `400 invalid_request_body` once a replayed tool call entered its context. The outbound sanitizer treated an *absent* `function_call.id` the same as a malformed one and synthesized a replacement from `call_id`; across a model-identity mismatch that value was always exactly 64 characters, the server rejected it, and because the value is a pure function of the tool call every resend rebuilt the same rejected identifier. An absent id is now left absent, only a present malformed id is repaired, and the synthesized-length budget and validator agree on a maximum of 63 characters ([#2204](https://github.com/bastani-inc/atomic/issues/2204)).
73
+ - Fixed a `null` request-header suppression marker being discarded instead of suppressing the provider default header. pi documents a `null` value in `ProviderHeaders` as suppressing the provider or API default header of the same name, but Atomic narrowed the type and enforced the narrowing at runtime in two places, so the default was sent anyway and a header map of only suppression markers arrived as `{}`. Both filters are gone. This was never reachable from `models.json`, whose schema rejects `null`; the marker has to come from pi's catalog headers, credential-derived request auth, or the `before_provider_headers` hook. The agent-session filter also affected compaction, auto-compaction, post-tool compaction, and branch-summary requests.
74
+ - Fixed the model list widening to a provider's whole built-in catalog after an extension registered an additive provider override, so a GitHub Copilot account entitled to one model could be offered every catalog model. Republishing the model list now carries the previous availability projection across by model ID, including an empty one, and a registration exposes a provider's catalog provisionally only when it is what newly configures the provider.
75
+ - Fixed registration-triggered offline catalog refreshes racing newer credential-resolved refreshes, so configured `$ENV` and `!command` credentials remain authoritative ([#2318](https://github.com/bastani-inc/atomic/issues/2318)), and fixed `atomic auth check --no-refresh` resolving command-backed API keys as configured values rather than treating `!command` as a literal credential. `--json` no longer labels an unresolved model as its provider, and `print-api-key` consistently rejects `--min-expiry` before or after a `--` terminator.
76
+ - Fixed compaction across the board: manual `/compact` keeps agent events live, cancels unfinished automatic compaction safely, and then runs the requested compaction instead of rejecting it or returning an automatic boundary with different parameters; input queued during manual compaction sends after completion; a response truncated before its requested output limit gets one compact-and-retry attempt while output-cap stops retain bounded direct continuation; a stale automatic post-compaction retry no longer resumes after a manual `/compact`; a short transcript with repeated below-cap truncations no longer retries compaction more than once; and `ChatSessionHost` no longer leaves queued prompts stuck when compaction is cancelled or fails.
77
+ - Fixed a chat surface built on `ChatSessionHost` — the workflow stage chat, and any extension pane using it — showing no assistant text when opened onto a session that was already streaming. A host mounted part-way through a turn saw neither the `message_start` nor the fragments that had already gone past. A host now adopts the session's in-flight assistant message when it mounts and continues it with later deltas, and streaming consumers assemble into their own copy of the message rather than mutating the provider's live partial. `ChatSessionHost` also no longer tail-windows settled assistant rows while a later answer streams.
78
+ - Fixed in-process child sessions losing their admission-issued nesting depth and delegation limit; `SubagentChildPolicy` carries both the admitted `depth` and the effective `maxSubagentDepth` ([#2220](https://github.com/bastani-inc/atomic/pull/2220), regression from [#2205](https://github.com/bastani-inc/atomic/pull/2205)).
79
+ - Reduced redundant repaints of a Ctrl+O-expanded live subagent widget, which were scrolling the chat window to the bottom and clearing terminal scrollback during a run. Progress is now published only at milestones that change what the widget shows. A genuine above-fold milestone change can still require that redraw when the dock plus the live widget exceed the terminal height; that path is upstream (earendil-works/pi#4785, #7194) ([#2213](https://github.com/bastani-inc/atomic/pull/2213), regression from [#2205](https://github.com/bastani-inc/atomic/pull/2205)).
80
+ - Fixed the scroll anchor in `ScrollableComponentViewport` drifting when its content changes height. The offset is now re-derived from the anchored row rather than compensated only on growth, and a windowed component can report a row map (`rowSegments`) identifying each run of rows by the entry that produced it, so a compaction that deletes entries above the viewer still leaves them on the entry they were reading; if that entry was compacted away, the viewer lands on the nearest surviving entry above it ([#2214](https://github.com/bastani-inc/atomic/pull/2214), regression source [#2205](https://github.com/bastani-inc/atomic/pull/2205)).
81
+ - Fixed workflow runs awaiting human input to use the blue `?` indicator in the BACKGROUND panel, `/workflow connect` picker, and `/workflow status` listing, including prompts raised by hidden nested workflow children; the indicator returns to the run's current state when the prompt resolves.
82
+ - Release archives no longer ship packages built for a different architecture. Every archive was produced on one Linux x64 runner whose `node_modules` was copied verbatim, so 0.9.12's arm64 archives both contained `@esbuild/linux-x64`. The build now prunes the `@embedded-postgres` leaves that cannot run on the archive being staged, then fails if any staged package's own `os`/`cpu`/`libc` declaration is incompatible ([#2208](https://github.com/bastani-inc/atomic/issues/2208)).
83
+ - Fixed extension event-bus subscriptions surviving session reload or disposal ([#7193](https://github.com/earendil-works/pi/issues/7193), [#7656](https://github.com/earendil-works/pi/pull/7656) by [@tudoroancea](https://github.com/tudoroancea)), and moved the public `isStaleExtensionContextError()` predicate and its marker to the host beside the error path, so session replacement and reload guards cannot drift from host wording.
84
+ - Fixed image blocks returned by builtin and extension tools entering history at their original size; they now honor `images.autoResize` after `tool_result` handlers run, and a failed conversion leaves the original intact. Fixed same-state `ctx.ui.setToolsExpanded()` calls still traversing the transcript, extension-facing `ModelRegistry.complete()` bypassing the active model runtime, and custom editors installed through `ctx.ui.setEditorComponent()` not inheriting the default autocomplete dropdown limit.
85
+ - Fixed bare exact `--model` IDs shared by providers choosing catalog order; Atomic uses the sole authenticated matching provider or reports an ambiguity with fully qualified choices ([#7327](https://github.com/earendil-works/pi/issues/7327)). Fixed `/model <name>` and `/scoped-models` waiting for remote catalog refreshes before using cached models ([#7153](https://github.com/earendil-works/pi/issues/7153), [#7443](https://github.com/earendil-works/pi/issues/7443)), and project `retry.provider` overrides discarding unmodified global provider retry fields ([#7572](https://github.com/earendil-works/pi/issues/7572)).
86
+ - Fixed credential-resolved model endpoints — including GitHub Copilot Business and Enterprise hosts — falling back to the catalog endpoint for Verbatim Compaction, branch summaries, MCP sampling, and direct web summaries ([#6768](https://github.com/earendil-works/pi/issues/6768), [#7579](https://github.com/earendil-works/pi/issues/7579)).
87
+ - Fixed Windows fallback `find` glob patterns with paths returning no results when `fd` uses native separators ([#6817](https://github.com/earendil-works/pi/issues/6817)), `find` results at filesystem roots losing their first segment ([#6104](https://github.com/earendil-works/pi/issues/6104), [#7569](https://github.com/earendil-works/pi/pull/7569)), Git Bash, MSYS, Cygwin, and WSL drive paths resolving against the current Windows drive ([#7064](https://github.com/earendil-works/pi/issues/7064), [#7547](https://github.com/earendil-works/pi/issues/7547)), and linked project session directories being omitted from the session picker ([#7552](https://github.com/earendil-works/pi/issues/7552)).
88
+ - Fixed standalone x64 binaries failing to start on pre-Haswell CPUs that lack AVX2/BMI2 by using Bun's baseline target, including musl archives ([#7390](https://github.com/earendil-works/pi/issues/7390)), malformed package-manifest resource fields blocking valid resources or crashing startup ([#7187](https://github.com/earendil-works/pi/issues/7187)), Git package updates left incomplete after `git clean` fails ([#7570](https://github.com/earendil-works/pi/issues/7570)), and transient management requests not being retried ([#7632](https://github.com/earendil-works/pi/issues/7632)).
89
+ - Fixed native macOS `Cmd+V` so clipboard images paste into the editor without duplicate image inserts from terminal key repeats ([#2186](https://github.com/bastani-inc/atomic/issues/2186)), and reduced automatic terminal-theme detection latency by starting color-scheme and background probes together.
90
+
91
+ ### Removed
92
+
93
+ - Removed background bash jobs and the `async` parameter from the `bash` tool. Bash commands always run in the foreground; use a separate shell or process manager when a command must outlive the tool call. Polling and cancellation commands and automatic completion follow-ups are gone.
94
+ - Removed the Ctrl+X shortcut for copying the last agent message; `/copy` still copies it to the clipboard and confirms in the status line.
95
+ - Removed the `tsx` dependency from the shipped runtime. The Intercom broker's Node path runs on Atomic's bundled `jiti` loader, which is dependency-free pure JavaScript. `tsx` was the only thing pulling `esbuild`, so this removes `esbuild`, its 26 platform packages, and `fsevents` from `npm-shrinkwrap.json` and from every release archive — about 11.5 MB across 72 files. Worth noting for extension authors: TypeScript reached through the Node broker path is now transpiled by jiti (Babel-based) rather than esbuild, and jiti defaults JSX support and `tsconfig` path aliases to off while enabling legacy decorators. An extension that resolved `tsx` from the bundled `node_modules` was relying on an undocumented dependency and will no longer find it ([#2208](https://github.com/bastani-inc/atomic/issues/2208)).
96
+ - Removed the Atomic-only workflow custom-terminal mouse-scroll control from the isolated engine protocol and host terminal-mode arbiter; remote overlays retain the typed autowrap control only. Focused workflow overlays leave `ctrl+t` to the host `app.thinking.toggle` action, while the tree selector still receives the same key through `app.tree.filter.noTools`. Fullscreen pi-tui owns application selection over overlays; copy still uses OSC 52, and terminals that refuse OSC 52 writes require the modifier-drag bypass (Shift/Option, as provided by the terminal).
97
+
5
98
  ## [0.9.13-alpha.4] - 2026-08-13
6
99
 
7
100
  ### Added
@@ -4,6 +4,19 @@ All notable changes to the `@bastani/i-have-adhd` extension will be documented i
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.9.13] - 2026-08-13
8
+
9
+ Cumulative release of the `0.9.13-alpha.2` prerelease. The summary below covers the user-visible outcome of that work; the per-change detail remains in the prerelease section below.
10
+
11
+ ### Added
12
+
13
+ - Added the upstream `i-have-adhd` skill and Atomic extension as a bundled first-party package, with `/i-have-adhd [on|off]`, the `--no-adhd` startup flag, the `.i-have-adhd-off` agent-directory flag file, and the `stop adhd mode` / `normal mode` stop phrases.
14
+
15
+ ### Changed
16
+
17
+ - ADHD-friendly output is on by default for new sessions, and saved per-session state survives restarts, branches, and compaction.
18
+ - The footer status reads `ADHD Mode` and appears only while the mode is on.
19
+
7
20
  ## [0.9.13-alpha.2] - 2026-08-12
8
21
 
9
22
  ### Added
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/i-have-adhd",
3
- "version": "0.9.13-alpha.4",
3
+ "version": "0.9.14-alpha.1",
4
4
  "private": true,
5
5
  "description": "Atomic extension that shapes responses for readers with ADHD. Fork of: https://github.com/ayghri/i-have-adhd",
6
6
  "contributors": [
@@ -4,6 +4,32 @@ All notable changes to the `pi-intercom` extension will be documented in this fi
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.9.13] - 2026-08-13
8
+
9
+ Cumulative release of the `0.9.13-alpha.1` – `0.9.13-alpha.3` prereleases. The summary below covers the user-visible outcome of that work; the per-change detail remains in the prerelease sections below.
10
+
11
+ ### Breaking Changes
12
+
13
+ - Removed unique-ID-prefix targeting. `send`, `ask`, and `reply` now accept only an exact full session ID or an exact case-insensitive session name.
14
+
15
+ ### Added
16
+
17
+ - Added the runtime `join` and `leave` actions for named group membership. Joining updates broker presence without changing the session ID, keeps `send`/`ask` group-isolated, lets later subagents inherit the joined group, and reports success only after broker acknowledgement. `default` remains explicit, while `true` and `auto` stay reserved for generated subagent groups ([#2334](https://github.com/bastani-inc/atomic/issues/2334)).
18
+
19
+ ### Changed
20
+
21
+ - Intercom displays full session and message IDs everywhere: list rows, the session-list overlay, inbound message headers, reply-to lines, message-id result badges, and unnamed-session `subagent-chat-<id>` aliases.
22
+ - In-process child identities and supervisor capabilities arrive through typed admission policy and are captured on each `IntercomClient` at connect time instead of being read from environment for every message. A descendant cannot inherit a parent's supervisor grant through process environment; legacy Atomic/Pi bridge metadata remains available for older hosts ([#2188](https://github.com/bastani-inc/atomic/issues/2188)).
23
+ - Detached broker processes now receive `AI_AGENT=atomic` for generic child-process attribution.
24
+
25
+ ### Fixed
26
+
27
+ - Fixed the broker failing to start on standalone-binary installs, where every `intercom` call and every `subagent` delegation failed with `Intercom not connected: Intercom broker exited before startup with code 1`. The detached broker never inherits the host extension loader's `@bastani/atomic` alias and release archives ship no physical copy of that optional peer, so group resolution now reads `ATOMIC_INTERCOM_GROUP` (and the legacy `PI_INTERCOM_GROUP`) locally instead of importing the host package ([#2208](https://github.com/bastani-inc/atomic/issues/2208)).
28
+ - Broker startup is far faster on every platform, because dropping that import keeps the whole coding-agent module graph out of it: measured spawn-to-socket-connectable startup went from 1104 ms minimum / 1318 ms average to 85 ms / 96 ms on Node with tsx, and from 252 ms / 260 ms to 17 ms / 18 ms on Bun. Readiness now polls at 10 ms with bounded backoff up to the previous 100 ms interval, and the overall startup timeout is unchanged ([#2208](https://github.com/bastani-inc/atomic/issues/2208)).
29
+ - Broker stderr is no longer discarded. It is captured to `broker.log` in the agent's intercom directory, and both the "exited before startup" error and the readiness-timeout error quote the log path and a bounded tail of that output. The log is capped at 8 KiB by the broker itself, installed by the entrypoint's first import so a dependency cannot write ahead of the cap, and the cap covers `process.stderr.write`, `console.error`/`console.warn`, and default fatal printing for uncaught exceptions and unhandled rejections ([#2208](https://github.com/bastani-inc/atomic/issues/2208)).
30
+ - Fixed the session-list overlay clipping full session IDs at narrow terminal widths by wrapping IDs onto dim rows and marking unavoidable truncation with a visible ellipsis; inline sender and reply-to IDs now also signal overflow.
31
+ - Fixed relay and detach-handshake callbacks that finish after an extension reload turning a late delivery into an unhandled rejection. Stale event-bus emissions and subscription cleanup are best-effort, and stale-context detection uses the host's exported predicate instead of a duplicated error-message marker.
32
+
7
33
  ## [0.9.13-alpha.3] - 2026-08-13
8
34
 
9
35
  ### Breaking Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/intercom",
3
- "version": "0.9.13-alpha.4",
3
+ "version": "0.9.14-alpha.1",
4
4
  "private": true,
5
5
  "description": "Atomic extension providing a private coordination channel between parent and child agent sessions. Fork of: https://github.com/nicobailon/pi-intercom",
6
6
  "contributors": [
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.9.13] - 2026-08-13
11
+
12
+ Cumulative release of the `0.9.13-alpha.1` – `0.9.13-alpha.2` prereleases. The summary below covers the user-visible outcome of that work; the per-change detail remains in the prerelease sections below.
13
+
14
+ ### Changed
15
+
16
+ - In-process subagents receive MCP direct-tool selection through typed admission policy: an omitted selection preserves MCP configuration defaults, a list selects exact server/tool entries, and an empty list disables direct tools. The process-era environment bridge is gone ([#2188](https://github.com/bastani-inc/atomic/issues/2188)).
17
+ - MCP server processes and npm/Glimpse resolver probes now receive `AI_AGENT=atomic` for generic child-process attribution; the reserved marker overrides a configured `AI_AGENT` value.
18
+
19
+ ### Fixed
20
+
21
+ - Restored credential-resolved endpoints for MCP sampling requests, so GitHub Copilot Business and Enterprise accounts keep their own endpoint alongside dynamic API keys and request headers ([#7579](https://github.com/earendil-works/pi/issues/7579)).
22
+ - Fixed image blocks returned by MCP tools bypassing the host's `images.autoResize` processing before they enter session history.
23
+ - Fixed deferred MCP initialization's stale-context check by consuming the host's exported predicate instead of copying its error-message marker.
24
+
10
25
  ## [0.9.13-alpha.2] - 2026-08-12
11
26
 
12
27
  ### Changed
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/mcp",
3
- "version": "0.9.13-alpha.4",
3
+ "version": "0.9.14-alpha.1",
4
4
  "private": true,
5
5
  "description": "Atomic extension that adapts MCP (Model Context Protocol) servers into the coding agent. Fork of: https://github.com/nicobailon/pi-mcp-adapter",
6
6
  "contributors": [
@@ -2,6 +2,47 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.9.13] - 2026-08-13
6
+
7
+ Cumulative release of the `0.9.13-alpha.1` – `0.9.13-alpha.3` prereleases. The summary below covers the user-visible outcome of that work; the per-change detail remains in the prerelease sections below.
8
+
9
+ ### Breaking Changes
10
+
11
+ - **Subagents no longer run in OS child processes, and no longer run in the background.** Every call executes in the foreground as an in-process child of the parent session. The `subagent` tool rejects `async: true`, `/run --bg` and the related background slash flags are gone, and the `asyncByDefault` and `forceTopLevelAsync` settings have been removed — delete those keys from user configuration. `/run` and `/parallel` return their results directly; foreground Intercom coordination and resumable interrupt/resume controls remain ([#2188](https://github.com/bastani-inc/atomic/issues/2188)).
12
+ - **Removed CHAIN execution mode from the `subagent` tool.** Only SINGLE (`{agent, task?, progress?}`) and top-level PARALLEL (`{tasks: [...], concurrency?, worktree?}`) remain. The `chain` parameter is gone from the schema, so a call that still passes it is rejected by ordinary schema validation rather than by a migration shim. Also removed with no replacement: sequential chain steps, static parallel steps inside a chain, dynamic fan-out (`expand` / `collect` / `as` / `{outputs.name}`), the `{previous}` and `{chain_dir}` template variables and chain-scoped `{task}` substitution, the `chainName` and `chainDir` parameters, and saved chain definitions in `.chain.md` / `.chain.json`. Migrate a sequential chain to successive `subagent` calls that pass each step's result forward in the task text, and an in-chain parallel step to a top-level PARALLEL call.
13
+ - **Removed the `/chain` and `/run-chain` slash commands.** `/run` and `/parallel` are unchanged apart from the dropped `--bg` flag. Re-express a saved `.chain.md` workflow as `/run` and `/parallel` invocations, or as an `@bastani/workflows` workflow, which keeps first-class sequential composition through `ctx.chain(...)`.
14
+ - **Action required: delete leftover `.chain.md` files from your agents directories.** Saved-chain discovery is gone, so agent discovery no longer recognises — or skips — the `.chain.md` suffix. A leftover file carries `name` and `description` frontmatter, so it now loads as an ordinary agent and appears in `subagent list`, `/run` completions, and agent management.
15
+ - Removed the `ATOMIC_SUBAGENT_*` process-era environment bridge variables and the synthesized numeric child-result exit-code protocol (`0/1/-1/-2/143`). Child identity, policy, supervisor capability, and terminal outcomes now cross typed admission; results use a required `status` (`ok | error | skipped | interrupted | continued`) plus `cause` and `stats` ([#2188](https://github.com/bastani-inc/atomic/issues/2188)).
16
+
17
+ ### Added
18
+
19
+ - Added the in-process subagent control plane and `InProcessChildRunner` foundation, backed by the Rust N-API registry, with typed statuses, session statistics, and bounded result envelopes ([#2188](https://github.com/bastani-inc/atomic/issues/2188)).
20
+
21
+ ### Changed
22
+
23
+ - In-process admission resolves child management and fanout capabilities, MCP direct-tool selection, Intercom identity, prompt boundaries, project-context and inherited-skill filtering, and the nesting depth limit as one typed policy carried into the session and the tool executor. A restricted child cannot create, update, or delete agent definitions, fanout authorization cannot be widened by a descendant, and a supervisor grant is never inherited through environment ([#2188](https://github.com/bastani-inc/atomic/issues/2188)).
24
+ - Nested children route live `interrupt` and cold `resume` through the Rust control plane. Nested event and control records use an in-memory host route while keeping the validated file shape for cold-start compatibility, session JSONL is flushed before forced interruption, and the former 200 ms nested-control polling loop is replaced by event-driven delivery ([#2188](https://github.com/bastani-inc/atomic/issues/2188)).
25
+ - Children advance through their model-fallback ladder via the SDK session's `fallbackModels`, the exact classification, same-model retry, and candidate-advancement behavior main chat and workflow stages converged on. Pre-spawn auth filtering still builds the ladder, and results report the effective `model`, `attemptedModels`, and skipped pre-spawn attempts ([#2170](https://github.com/bastani-inc/atomic/issues/2170), [#2188](https://github.com/bastani-inc/atomic/issues/2188)).
26
+ - Unnamed subagent sessions expose runtime-only `subagent-chat-<full-id>` Intercom aliases, so supervisor targeting uses the full session ID.
27
+ - Subagent worktree Git and setup-hook commands and optional global npm probes now receive `AI_AGENT=atomic` for generic child-process attribution, without mutating caller-supplied environment objects.
28
+
29
+ ### Fixed
30
+
31
+ - Fixed a fork-context subagent ignoring its agent's configured model. The selected candidate was only ever a `provider/model[:thinking]` string, so nothing reached the child session's `model` argument and the session fell back to the model in the session file — for a fork, the parent's. A child declaring `openai-codex/gpt-5.6-luna:max` therefore ran every turn on the parent's model, and an explicit `model:` override lost its first attempt the same way. The candidate is now resolved against the model registry and applied to the child session together with its thinking level, for fresh and fork contexts across single, parallel, and resume paths.
32
+ - Fixed every `subagent` action being refused with "Subagent fanout is not authorized for this child." for a child without fanout authorization. The fanout check ran before the management branch, so the observing actions — `list`, `get`, `status`, `doctor` — were rejected with a message about delegation they do not perform, and mutating management was refused by a gate the bug made unreachable. Those four now pass, while `resume` and `interrupt` correctly receive the fanout refusal ([#2220](https://github.com/bastani-inc/atomic/pull/2220), regression from [#2205](https://github.com/bastani-inc/atomic/pull/2205)).
33
+ - Fixed the in-process depth guard and nested workflow-stage children. Admission-issued child depth travels in the typed policy into every path, an agent definition's narrower `maxSubagentDepth` is preserved across cold reload and resume, and in-process children load the bundled package resources needed to register `subagent` instead of starting with only the base built-in tools ([#2220](https://github.com/bastani-inc/atomic/pull/2220), regression from [#2205](https://github.com/bastani-inc/atomic/pull/2205)).
34
+ - Reduced redundant live subagent repaints by publishing progress only for widget-visible milestones. The runner had published `AgentProgress` from a catch-all over every child session event, including streaming deltas, so a subagent expanded with Ctrl+O repainted for the whole run, and each repaint above the terminal fold costs a pi-tui full redraw that clears scrollback. Measured against a 20-second replay in tight geometry, scrollback clears drop from 23 to 3. A genuine above-fold milestone change can still require that redraw; that path is upstream (earendil-works/pi#4785, #7194) ([#2213](https://github.com/bastani-inc/atomic/pull/2213), regression from [#2205](https://github.com/bastani-inc/atomic/pull/2205)).
35
+ - Fixed running widgets and result rows dropping the child's effective reasoning level and Codex fast-mode marker. Resolved model, thinking, and applied fast-mode metadata now travel from launch through fallback to completion, and workflow-stage children keep the real orchestration context so eligible requests get `service_tier: "priority"` ([#2335](https://github.com/bastani-inc/atomic/issues/2335), [#2336](https://github.com/bastani-inc/atomic/issues/2336)).
36
+ - Fixed progress and status lines stripping the provider prefix from the model name, so a child rendered as `claude-fable-5` where the main chat showed `anthropic/claude-fable-5`. The full model id is displayed, including every segment of a routed id; known thinking suffixes are still split into the `thinking <level>` label.
37
+ - Fixed `/run`, `/parallel`, and prompt-template requests waiting forever when an extension reload or session replacement made their response runtime stale after a long child run. Stale drops are classified through the host's exported predicate and reject the request's settlement path, bridge shutdown uses a distinct terminal error so ordinary deactivation renders a failure result, and completion, authorization, notification, and continuation paths all consume the host predicate instead of copying its marker.
38
+ - Fixed in-process parallel siblings colliding on the same canonical identity and terminal artifact path, and fixed repeated collapsed-tool-output requests from launch paths forcing an already-collapsed host transcript to update.
39
+ - Exposed `registerPromptTemplateBridgeRequestSettlement` from `@bastani/subagents` for the separately installed `pi-prompt-template-model` emitter, which owns a different extension runtime and cannot be registered by Atomic.
40
+
41
+ ### Removed
42
+
43
+ - Removed every remaining OS child-process code path from subagent execution: the detached jiti async runner and its twelve `subagent-runner*` modules, the `async-execution-*` spawn layer, the async event journal, the idle/wall attempt watchdog, the stdout drain grace, the CLI spawn resolver, the environment-bridge builder, and the post-exit stdio guard ([#2188](https://github.com/bastani-inc/atomic/issues/2188)).
44
+ - Removed every chain-only implementation surface: the chain execution engine and its step runners, chain output bindings and named-output resolution, the chain serializer and saved-chain discovery, the saved-chain slash mapping, chain TUI graph rendering, chain reporting in `subagent doctor`, and the chain types, results, settings, and formatters. Agent discovery now treats every `.md` file in an agents directory uniformly.
45
+
5
46
  ## [0.9.13-alpha.3] - 2026-08-13
6
47
 
7
48
  ### Breaking Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/subagents",
3
- "version": "0.9.13-alpha.4",
3
+ "version": "0.9.14-alpha.1",
4
4
  "private": true,
5
5
  "description": "Atomic extension for delegating tasks to subagents with parallel execution. Fork of: https://github.com/nicobailon/pi-subagents",
6
6
  "contributors": [
@@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.9.13] - 2026-08-13
8
+
9
+ Cumulative release of the `0.9.13-alpha.2` prerelease. The summary below covers the user-visible outcome of that work; the per-change detail remains in the prerelease section below.
10
+
11
+ ### Changed
12
+
13
+ - GitHub, credential-store, and Bun subprocesses now receive `AI_AGENT=atomic` for generic child-process attribution.
14
+ - Query rewriting and summary-review model calls now go through Atomic's model runtime, so registered custom providers and resolved request authentication apply.
15
+
16
+ ### Fixed
17
+
18
+ - Frames and thumbnails returned by `fetch_content` now honor the host's `images.autoResize` processing before they enter session history.
19
+ - Background content fetches that complete after an extension reload no longer throw through stale runtime handles. Non-stale failures still raise a visible error notification, pending state stays alive until notification admission settles, and stale-context checks use the host's exported predicate instead of a copied error-message marker.
20
+
7
21
  ## [0.9.13-alpha.2] - 2026-08-12
8
22
 
9
23
  ### Changed
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/web-access",
3
- "version": "0.9.13-alpha.4",
3
+ "version": "0.9.14-alpha.1",
4
4
  "private": true,
5
5
  "description": "Atomic extension for web search, URL fetching, GitHub repo cloning, PDF/video extraction. Fork of: https://github.com/nicobailon/pi-web-access",
6
6
  "contributors": [
@@ -6,6 +6,64 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.9.14-alpha.1] - 2026-08-14
10
+
11
+ ### Added
12
+
13
+ - Added `heartbeatIntervalMinutes` to the workflow authoring surface: an optional cadence in minutes that resolves to the `15`-minute default when omitted, treats `0` as an explicit disable, and rejects negative and non-finite values with a `TypeError` while the definition is authored, so a bad cadence fails at authoring rather than at schedule time. Every compiled definition carries the resolved value, and a distinct `workflows:workflow-heartbeat` event contract is declared alongside it — run id, workflow name, start time, scheduled time, and interval, with heartbeat identity keyed by run id plus scheduled time. The default interval constant, heartbeat custom type, and heartbeat event types are exported from the `@bastani/workflows` package root ([#1975](https://github.com/bastani-inc/atomic/issues/1975)).
14
+ - Added workflow heartbeat scheduling and queued parent delivery for `heartbeatIntervalMinutes`, so a positive cadence now produces real heartbeats. While a top-level run is active, one heartbeat is raised per `startedAt + n × interval` boundary computed from the run's persisted start time — never from the previous delivery — so a slow send, a retry, or a process restart cannot shift the cadence. A resolved interval of `0` creates no timer and no durable record at all. One globally-next-due wake-up serves every enabled run rather than one recurring timer per run. At most one heartbeat per run is outstanding at a time: an outstanding heartbeat holds its slot until its card is actually consumed into the conversation — not merely until the parent's turn ends, which can happen while the card is still queued or its queue is paused — so a boundary that falls before that is skipped rather than stacked behind it, and the cadence then resumes at the first future boundary. However long a turn runs, and however often the queue is paused, only one unread heartbeat per run is ever waiting on a host that reports message consumption, which Atomic's chat host does. Delivery reuses the lifecycle-notice parent path (`triggerTurn`, `deliverAs: "steer"`, `persistWhenStreaming`) under the distinct `workflows:workflow-heartbeat` custom type with the five-field payload, so a busy parent chat is queued and never interrupted. Paused runs emit nothing and receive no backfill; a resumed or restarted run takes the first future boundary. Simultaneously due heartbeats reach the parent in scheduled-time order with the run id as the stable tie-break — a heartbeat being retried holds its place rather than letting a later one overtake it — terminal state is re-checked immediately before enqueue, again immediately before processing, and again before every delivery attempt including retries, and nested child workflow runs never heartbeat the parent chat. A run keeps the cadence its own definition was authored with, so editing, renaming, deleting, or reloading a workflow mid-run changes what the next launch uses and leaves runs already in flight on their launch cadence; the anchor record carries that cadence too, so it survives a durable resume. A run that launched with heartbeats disabled writes no durable record at all, so if it is resumed in a new process it adopts whatever the workflow then declares. A cadence below a millisecond schedules at the finest representable instant, and the cadence range carries a documented representable upper limit: above roughly 3 × 10^303 minutes `startedAt + interval` exceeds the largest finite timestamp, so no first boundary exists, nothing is scheduled, no durable record is written, and `ATOMIC_WORKFLOW_DEBUG=1` says so — such a cadence stays a valid positive interval but delivers no heartbeat, and `0` remains the only value that declares heartbeats off. A durable resume re-dispatches under the original run id but records a fresh start time, so each run also writes one reserved durable cadence-anchor record as soon as it has durable progress of its own, before its first boundary comes due; the anchor is read only as the earlier of itself and the run's current start time, so it keeps a resumed run on its original cadence without being able to move a boundary or raise a missed one. The delivered card names the workflow, run id, cadence, and elapsed run time and hints `/workflow status <run-id>`, rendered through the existing workflow notice-card renderer ([#1975](https://github.com/bastani-inc/atomic/issues/1975)).
15
+ - Added terminal heartbeat cleanup and restart recovery, so a workflow's heartbeats stop when the run does. Reaching any terminal status — completed, failed, blocked, skipped, cancelled, or killed — drops everything that run's cadence held in one pass: its armed wake-up, its next scheduled boundary, its outstanding slot and the card text held for it, any heartbeat of its own still waiting in the delivery queue along with the retry timer that belonged to it, and its cadence and durable-anchor memos. No pending timer and no queued heartbeat outlives the run that owns it. The pass is idempotent: repeating it creates no state, resurrects no schedule, and does not throw, including for a run the scheduler never saw. Both completion races converge on suppression — a run that finishes before its due boundary is enqueued has that boundary dropped and is refused at enqueue, and one that finishes after enqueue but before processing has its queued identity discarded rather than sent. Cleanup keys on the run's observed state rather than on a transition event, so it also covers the paths that reach a terminal snapshot without a normal end transition — durable replay, completed-catalog reconstruction, and a restore that inserts an already-failed run — and the same pass runs at startup, making it the recovery pass: a stale durable anchor or leftover queued record belonging to an already-terminal run is discarded rather than replayed, its anchor is neither read nor rewritten, and a run the store no longer holds is dropped the same way. An active run is never affected by another run's cleanup, a paused run keeps the floor that stops it re-raising a boundary it already delivered, and recovery for an active run still selects the first future boundary rather than replaying a missed one. A late durable acknowledgement or a send that settles after cleanup re-creates nothing. A heartbeat the chat host had already accepted into the parent's queue is past that pass, because no queued message can be withdrawn, so it is invalidated at the moment the parent reads it instead: a heartbeat whose run has since reached a terminal state, or whose run this process no longer knows at all, is excluded from the model's context and never steers the parent about a run that is over. That closes both remaining stale paths — a card parked through a long turn while its run finished, and a card recovered from a previous process at startup, which would otherwise replay a boundary raised before the restart. The card already rendered in the transcript is deliberately left in place as a true record that the heartbeat was raised; only the model-facing steer is invalidated ([#1975](https://github.com/bastani-inc/atomic/issues/1975)).
16
+ A run that is only blocked on a recoverable provider or rate-limit failure remains stored as `running` and resumable: it raises no new heartbeat while blocked, but keeps its cadence state and any card already waiting with the parent. Cleanup runs only when the run's own status is terminal.
17
+ Parent pickup is keyed by the typed `workflows:workflow-heartbeat` entry and exact run id plus scheduled time rather than rendered card text, so a foreign custom message cannot release an unread heartbeat's slot and a durable resume that reuses the run id cannot revive an old admitted card after terminal cleanup ([#1975](https://github.com/bastani-inc/atomic/issues/1975)).
18
+ The cadence-anchor record is now written at the durability boundary itself, so it is durable before the checkpoint that made the run resumable is acknowledged. The scheduler's own write is guarded against runs with no durable progress and can therefore only be issued after resumability already exists; a process that exited inside that window left a resumable run carrying no record of what it launched with, and the next process then took both a freshly minted start time and whatever cadence the definition declared by then, shifting the phase and the cadence of a run already in flight ([#1975](https://github.com/bastani-inc/atomic/issues/1975)).
19
+ - Every bundled builtin workflow now states its heartbeat cadence rather than inheriting it. `adversarial-verification`, `classify-and-act`, `fan-out-and-synthesize`, `generate-and-filter`, `goal`, `loop-until-done`, `ralph`, and `tournament` declare the `15`-minute default explicitly, so these long autonomous runs report to the parent chat on a known cadence and a future change to the global default cannot silently re-cadence them. `open-claude-design` sets `0` and runs quiet: it is driven by the user reviewing generated HTML turn by turn, so the parent chat is already holding it to its goal and a periodic alignment steer would interrupt that review rather than inform it. Behaviour is unchanged for the eight that were already resolving to the default; only `open-claude-design` changes ([#1975](https://github.com/bastani-inc/atomic/issues/1975)).
20
+
21
+ ## [0.9.13] - 2026-08-13
22
+
23
+ Cumulative release of the `0.9.13-alpha.1` – `0.9.13-alpha.4` prereleases. The summary below covers the user-visible outcome of that work; the per-change detail remains in the prerelease sections below.
24
+
25
+ ### Breaking Changes
26
+
27
+ - Custom workflow UI components must return `true` when they consume input and `false` when they do not. In fullscreen mode an unhandled result lets the transcript process matching viewport navigation keys instead of swallowing them.
28
+ - Added `failed` to the `ctx.exit()` status union. An author-initiated failed exit persists its reason and partial declared outputs, returns the discriminated `{ exited: true, status: "failed" }` child result instead of throwing, and defaults to non-resumable unless `resumable: true` is set. Downstream exhaustive switches over `WorkflowExitStatus` must handle it ([#2242](https://github.com/bastani-inc/atomic/issues/2242)).
29
+ - Workflow `runId` and `stageId` targets never accepted prefixes. Run ids require the full 36-character UUID and stage ids and names match whole; the README claims to the contrary are corrected.
30
+
31
+ ### Added
32
+
33
+ - Added four user-initiated lifecycle notices — `started`, `paused`, `quit`, and `resumed` — so the main chat learns when a top-level background run begins, stops, or picks back up. Each travels the same steer delivery, capped-backoff retry, and notice-card path as the existing failure notice, so an agent waiting on a background run no longer mistakes a deliberate stop for work still in progress. The paused and quit cards state that the stop was user-requested and tell the model not to resume or take over unless asked ([#2177](https://github.com/bastani-inc/atomic/issues/2177)).
34
+ - Only user actions raise those notices, and one request produces exactly one notice. `/workflow <name>`, `/workflow pause`, `/workflow quit`, and `/workflow resume` notify; the matching tool calls stay silent because the tool result already reports them, and engine-internal transitions stay silent. Each notice carries an origin ("which you started" or "which the user started", recorded once at dispatch and inherited by a continuation) and an actor. The default `workflowNotifications.notifyOn` becomes `["started", "completed", "failed", "blocked", "awaiting_input", "paused", "quit", "resumed"]`; a config that pins `notifyOn` keeps exactly the kinds it lists ([#2177](https://github.com/bastani-inc/atomic/issues/2177)).
35
+ - Added the shared "Jump to bottom" follow indicator to workflow stage chat when its transcript is scrolled away from the live end, while preserving composer and footer rows in tight overlays.
36
+
37
+ ### Changed
38
+
39
+ - Rebuilt the workflow graph overlay on pi-tui 0.84.1's `VStack` and `ScrollView` layout primitives. The graph body adapts to terminal height and resize, supports vertical wheel scrolling and a draggable scrollbar, and keeps wide-graph horizontal panning and existing prompt and navigation key handling ([#2223](https://github.com/bastani-inc/atomic/issues/2223)).
40
+ - Model fallback attempt metadata now includes per-attempt input and output tokens, cache reads and writes, provider-reported total cost, and meaningful assistant-turn counts when usage is available ([#2197](https://github.com/bastani-inc/atomic/issues/2197)).
41
+ - Stage resource loading carries typed subagent policy in session options instead of mutating process-global child environment flags during reload, so concurrent stage creation preserves tool allowlists and orchestration limits ([#2188](https://github.com/bastani-inc/atomic/issues/2188)).
42
+ - Updated the Ralph research stage model configuration: the primary model moves from `openai-codex/gpt-5.6-luna:max` to `anthropic/claude-opus-5:high`, and the fallback chain is rebuilt around high and xhigh thinking levels, adding Kimi K3 and `openrouter/sakana/fugu-ultra:high`.
43
+ - Extended workflow-authoring guidance with a stacked implementation-slices starter pattern targeting roughly 100–500 changed lines between verification points, each slice routed through a child lifecycle on an explicitly created branch, stopping at the first failed gate ([#2262](https://github.com/bastani-inc/atomic/issues/2262)).
44
+ - Updated authored-workflow model guidance to treat benchmark thinking levels as measurement settings, assign effort by stage role and failure cost, require a compact `Stage | Model | Thinking | Role` declaration before launch, and reserve `max` for high-cost-of-error roles or explicit user requests ([#2203](https://github.com/bastani-inc/atomic/issues/2203)).
45
+ - Added bounded constructive-quorum deliberation to Goal and Ralph reviewer prompts: reviewers form independent preliminary verdicts, exchange evidence once over Intercom, and record evidence-backed verdict changes before the existing reducer counts final votes ([#2237](https://github.com/bastani-inc/atomic/issues/2237)).
46
+ - Workflow command, worktree Git and setup-hook, and Playwright CLI subprocesses now receive `AI_AGENT=atomic` for generic child-process attribution, without mutating caller-supplied environment objects.
47
+
48
+ ### Fixed
49
+
50
+ - Fixed long model turns losing their active stage session identity and root liveness when the process died between turn boundaries. Stage start is recorded before the first session checkpoint, that identity must persist before the first model use, and serialized unref'd 30-second checkpoints cover root and nested scoped stages. A checkpoint still in flight at shutdown is drained rather than abandoned, and a stage whose final durability checkpoint fails is recorded as failed instead of completed while its caller still receives the error and its concurrency slot is still released.
51
+ - Fixed exact workflow inspection returning `Run not found` after process loss even when DBOS retained a stale running, resumable root. Status, stages, stage, and transcript actions with a full run id, and `/workflow status <id>`, now hydrate only that DBOS root on a local miss, reconstruct its checkpoint DAG as read-only state, report stale owners as `crashed` with resume guidance, protect fresh foreign owners, and reject cyclic or nonreciprocal topology without claiming, transitioning, dispatching, or auto-resuming the run.
52
+ - Fixed stage model fallback end to end: transient failures retry the current model with bounded exponential backoff from `settings.retry` before advancing through `fallbackModels`, unrepairable failures advance immediately, a same-candidate retry no longer loses the stage prompt or stops after a completed assistant message, an eagerly created session walks the whole chain, concurrent callers join the creation already in flight instead of starting a second walk, and a controlled pause no longer aborts the stage or strands its replacement objective ([#2170](https://github.com/bastani-inc/atomic/issues/2170)).
53
+ - Fixed workflow stages being unable to use the `subagent` tool at all, and nested subagents in stage sessions losing the bundled `subagent` extension. Stages are top-level sessions rather than subagent children, so stage policy sets `fanoutAuthorized: true`, and in-process child resource loading carries the bundled package roots while disabling only the workflow extension's repeated stage lifecycle. Nesting remains bounded by the typed depth policy and Rust admission's five-level ceiling ([#2220](https://github.com/bastani-inc/atomic/pull/2220), regression from [#2205](https://github.com/bastani-inc/atomic/pull/2205)).
54
+ - Fixed the `DISPATCHED` confirmation panel being destroyed by any workflow input containing a newline. Values were truncated by visible width, which a control character does not have, so an embedded newline survived and emitted physical lines no border wrapped — and multi-line `prompt` or `acceptance_criteria` input is ordinary, so this fired for most real launches. String values and the object/array JSON projection now collapse control characters, `U+2028`, and `U+2029` to single spaces before truncation, which also stops escape sequences in an input value injecting ANSI styling into the chat surface.
55
+ - Fixed fullscreen workflow graph and stage-chat mouse routing. Focused overlays receive SGR/X10 wheel and click press/release sequences through the host's bounded custom-input reply channel before pi-tui's alternate-screen viewport, restoring graph scrolling and click-to-attach without a workflow-owned TTY-tracking seam ([#2303](https://github.com/bastani-inc/atomic/issues/2303)).
56
+ - Fixed a stage chat opened on a stage that was already mid-turn showing an empty transcript. The chat now seeds itself from the stage session's in-flight assistant message when it mounts, and later deltas continue that same message.
57
+ - Fixed the stage-chat jump-to-bottom indicator: it stayed hidden in paused and read-only archive chats, and clicking it scrolled the host transcript instead of returning that stage chat to the live end ([#2370](https://github.com/bastani-inc/atomic/issues/2370)).
58
+ - Fixed graph overlay rendering details: switcher wheel input scrolling an obscured graph, the scrollbar covering the final graph column, lost live vertical position in `ScrollView`, broken OSC-8 hyperlink terminators, corrupted geometry for unhosted overlays, node cards appending the Codex `fast` marker to dependency metadata ([#2337](https://github.com/bastani-inc/atomic/issues/2337)), and the statusline echoing the `i-have-adhd` badge ([#2341](https://github.com/bastani-inc/atomic/issues/2341), [#2223](https://github.com/bastani-inc/atomic/issues/2223)).
59
+ - Fixed completed `ctx.exit()` runs losing the reserved returned-status convention, legacy child replay records losing their exit discriminator, and failed author-exit resume surfaces falling back to snapshot inspection instead of durable retry.
60
+ - Fixed late workflow messages and MCP scope cleanup that finish after an extension reload. Stale event-bus calls stop without retrying through stale APIs, stale-context guards consume the host's exported predicate, late-message routing rejects stale drops consistently for Intercom and generic batches, and active routes keep their normal fallback behavior.
61
+ - Fixed the inline workflow form editor not inheriting the host's `autocompleteMaxVisible` setting when Atomic installs it as a custom editor.
62
+
63
+ ### Removed
64
+
65
+ - Removed workflow-owned mouse-scroll reporting and attached-stage copy mode. Fullscreen pi-tui keeps application-owned drag and multi-click selection available over workflow overlays, and `ctrl+t` is left to the host keybindings. Copy still uses OSC 52; terminals that refuse OSC 52 writes require the modifier-drag bypass (Shift/Option, as provided by the terminal).
66
+
9
67
  ## [0.9.13-alpha.4] - 2026-08-13
10
68
 
11
69
  ### Added
@@ -702,6 +702,14 @@ Because human input is runtime-only and workflows no longer carry a declaration-
702
702
 
703
703
  For library or package authoring, define reusable workflows with `workflow({...})` and export the returned definition. Hand-written objects with `__piWorkflow: true` are rejected by discovery and composition; `workflow({...})` is the public authoring surface. Standalone TypeScript workflow packages import `workflow` from `@bastani/workflows` and `Type` from `typebox` directly with no local `.d.ts` file or `declare module` shim. Migration from the removed builder API is mechanical: move `.description(...)` to `description`, `.input(key, schema)` calls into `inputs`, `.output(key, schema)` calls into `outputs`, `.worktreeFromInputs(...)` to `worktreeFromInputs`, and the `.run(fn)` callback to `run: fn`; delete `.compile()`. The former imperative `runWorkflow` object-form API is removed; use workflow definitions with the exported `run()` / registry helpers for programmatic execution.
704
704
 
705
+ Set `heartbeatIntervalMinutes` to declare the workflow's heartbeat cadence in minutes. Omitting it uses the `15`-minute default; `0` explicitly disables heartbeats. Negative and non-finite values are rejected when the workflow definition is authored. The scheduler consumes this validated setting directly: while a top-level run is active it raises one heartbeat per `startedAt + n × interval` boundary, computed from the run's persisted start time rather than the previous delivery, so retries and restarts cannot shift the cadence. The example below heartbeats every 30 minutes, so a run started at 09:00 raises boundaries at 09:30, 10:00, 10:30, and so on.
706
+
707
+ Each heartbeat reaches the parent chat as a `workflows:workflow-heartbeat` card through the same queued-steer delivery lifecycle notices use (`triggerTurn`, `deliverAs: "steer"`, `persistWhenStreaming`), so a busy parent is never interrupted — the steer lands at its next protocol-safe boundary. At most one heartbeat per run is outstanding at a time: it holds its slot until its card is actually consumed into the conversation, not merely until the parent's turn ends, so a boundary that falls before that is skipped rather than stacked behind it and the cadence then resumes at the first future boundary. Parent pickup follows the typed heartbeat entry and releases the slot only for the exact `runId + scheduledAt` identity; another custom message cannot release it by copying the rendered text. Paused runs emit nothing and are never backfilled, a resumed or restarted run takes the first future boundary rather than bursting missed ones, simultaneous heartbeats process in `scheduledAt` order with the run id as the tie-break, and a run that reaches a terminal state is suppressed before every delivery attempt.
708
+
709
+ When a run reaches any terminal status — completed, failed, blocked, skipped, cancelled, or killed — one idempotent cleanup pass drops its wake-up timer, its next boundary, its outstanding slot, any heartbeat of its own still queued inside the scheduler with the retry timer that belonged to it, and its cadence and durable-anchor memos. Repeating the pass creates nothing and resurrects nothing. Because it reads observed state rather than transition events, it is also the recovery pass: a later process discards a stale durable anchor or leftover queued record for an already-terminal run instead of replaying it, and never replays a missed boundary for an active one. A heartbeat the host already accepted into the parent's queue is past that pass, since nothing withdraws a queued message, so it is invalidated when the parent reads it instead: its exact `runId + scheduledAt` must still be pending for a current nonterminal run. A same-ID durable resume therefore cannot revive an old card, and a heartbeat whose run is terminal or absent from this process is excluded from the model's context. That covers a card parked through a long turn and a card recovered from a previous process alike. The rendered card stays in the transcript as a record of what was raised; only the model-facing steer is invalidated ([#1975](https://github.com/bastani-inc/atomic/issues/1975)).
710
+
711
+ A recoverable provider or rate-limit block is not the terminal `blocked` status: the run remains stored as `running` and resumable. It raises no new heartbeat while blocked, but keeps its cadence state and any card already waiting with the parent; cleanup runs only once the run's own status becomes terminal.
712
+
705
713
  ```ts
706
714
  import { workflow } from "@bastani/workflows";
707
715
  import { Type } from "typebox";
@@ -709,6 +717,7 @@ import { Type } from "typebox";
709
717
  export default workflow({
710
718
  name: "audit-auth",
711
719
  description: "Audit the authentication module.",
720
+ heartbeatIntervalMinutes: 30,
712
721
  inputs: {
713
722
  prompt: Type.String({ default: "Investigate the auth module" }),
714
723
  },
@@ -6,6 +6,10 @@ import { runAdversarialVerification } from "./adversarial-verification-runner.js
6
6
  export default workflow({
7
7
  name: "adversarial-verification",
8
8
  description: "Produce a candidate, challenge it with fresh-context rubric-based verifiers, and reduce their evidence through a bounded repair loop.",
9
+ // The 15-minute default, stated rather than inherited: this is a per-workflow
10
+ // product decision, so a future change to the global default must not silently
11
+ // re-cadence a long autonomous run.
12
+ heartbeatIntervalMinutes: 15,
9
13
  inputs: {
10
14
  task: Type.String({ description: "Task whose candidate result must be independently verified." }),
11
15
  verifier_count: Type.Integer({ minimum: 1, maximum: 5, default: 3, description: "Number of independent verifiers per review round." }),
@@ -8,6 +8,10 @@ export const DEFAULT_ACTION_CATEGORIES = ["analysis", "implementation", "researc
8
8
  export default workflow({
9
9
  name: "classify-and-act",
10
10
  description: "Classify a task with structured confidence, route deterministically to an isolated category action, and ask for human selection when confidence is low.",
11
+ // The 15-minute default, stated rather than inherited: this is a per-workflow
12
+ // product decision, so a future change to the global default must not silently
13
+ // re-cadence a long autonomous run.
14
+ heartbeatIntervalMinutes: 15,
11
15
  inputs: {
12
16
  prompt: Type.String({ description: "Task to classify and execute." }),
13
17
  categories: Type.Array(Type.String({ minLength: 1 }), {
@@ -6,6 +6,10 @@ import { runFanOutAndSynthesize } from "./fan-out-and-synthesize-runner.js";
6
6
  export default workflow({
7
7
  name: "fan-out-and-synthesize",
8
8
  description: "Partition a task, run bounded independent artifact branches, then synthesize all evidence at an explicit barrier.",
9
+ // The 15-minute default, stated rather than inherited: this is a per-workflow
10
+ // product decision, so a future change to the global default must not silently
11
+ // re-cadence a long autonomous run.
12
+ heartbeatIntervalMinutes: 15,
9
13
  inputs: {
10
14
  prompt: Type.String({ description: "Task to partition, investigate, and synthesize." }),
11
15
  max_branches: Type.Integer({
@@ -6,6 +6,10 @@ import { runGenerateAndFilter } from "./generate-and-filter-runner.js";
6
6
  export default workflow({
7
7
  name: "generate-and-filter",
8
8
  description: "Generate more independent candidates than needed, deduplicate and filter them by rubric, optionally judge them, and return a parent-consumable shortlist.",
9
+ // The 15-minute default, stated rather than inherited: this is a per-workflow
10
+ // product decision, so a future change to the global default must not silently
11
+ // re-cadence a long autonomous run.
12
+ heartbeatIntervalMinutes: 15,
9
13
  inputs: {
10
14
  prompt: Type.String({ description: "Prompt for candidate generation and selection." }),
11
15
  num_candidates: Type.Integer({ minimum: 2, maximum: 20, default: 8, description: "Number of independent candidates to generate." }),
@@ -15,6 +15,10 @@ import { DEFAULT_MAX_TURNS } from "./goal-types.js";
15
15
  export default workflow({
16
16
  name: "goal",
17
17
  description: "Goal Runner workflow with bounded sub-agent orchestration turns, immutable acceptance criteria, ledger artifacts, parallel reviewers, and reducer-gated completion. When launching follow-up goal runs from review findings, pass the ORIGINAL task text as acceptance_criteria so deltas cannot drift from the literal contract. If the task includes submitting a pull request (or MR/review), remove that final action from the objective text and set create_pr=true instead when preparing the workflow inputs.",
18
+ // The 15-minute default, stated rather than inherited: this is a per-workflow
19
+ // product decision, so a future change to the global default must not silently
20
+ // re-cadence a long autonomous run.
21
+ heartbeatIntervalMinutes: 15,
18
22
  inputs: {
19
23
  objective: Type.String({ description: "The objective or delta for this Goal Runner workflow run. Do not include PR/MR submission instructions here; strip them from the task text and request them via create_pr=true instead." }),
20
24
  acceptance_criteria: Type.Optional(Type.String({ description: "Original immutable task contract this run must remain consistent with. Defaults to objective. Orchestrators launching follow-up runs from reviewer findings should pass the ORIGINAL task text here." })),
@@ -6,6 +6,10 @@ import { runLoopUntilDone } from "./loop-until-done-runner.js";
6
6
  export default workflow({
7
7
  name: "loop-until-done",
8
8
  description: "Repeat evidence-producing work and independent completion evaluation against a durable ledger until done or an inspectable iteration-limit failure.",
9
+ // The 15-minute default, stated rather than inherited: this is a per-workflow
10
+ // product decision, so a future change to the global default must not silently
11
+ // re-cadence a long autonomous run.
12
+ heartbeatIntervalMinutes: 15,
9
13
  inputs: {
10
14
  prompt: Type.String({ description: "Objective whose explicit completion condition controls the bounded loop." }),
11
15
  max_iterations: Type.Integer({
@@ -27,6 +27,10 @@ import {
27
27
  export default workflow({
28
28
  name: "open-claude-design",
29
29
  description: "AI-powered design workflow: combined discovery/init → design-system/reference research → curated reference discovery → HTML generation → live-driven refinement → rich HTML handoff. The discovery stage asks what to build, the output type, and which references to emulate, then runs impeccable init for PRODUCT.md/DESIGN.md (references take precedence over project context). The user iteratively reviews the generated HTML.",
30
+ // Disabled: this workflow is driven by the user reviewing generated HTML,
31
+ // so the parent chat is already the thing holding it to its goal. A periodic
32
+ // alignment steer would interrupt that review rather than inform it.
33
+ heartbeatIntervalMinutes: 0,
30
34
  inputs: {
31
35
  prompt: Type.String({
32
36
  description: "What to design (for example, a dashboard, page, component, or prototype). The discovery stage refines this into a confirmed brief and asks for the output type and references.",
@@ -13,6 +13,10 @@ import { runRalphWorkflow } from "./ralph-runner.js";
13
13
  export default workflow({
14
14
  name: "ralph",
15
15
  description: "Raw prompt → research-prompt-refinement → research → orchestrate → multi-model parallel review loop with bounded iteration and immutable acceptance criteria. When launching follow-up ralph runs from review findings, pass the ORIGINAL task text as acceptance_criteria so deltas cannot drift from the literal contract. If the task includes submitting a pull request (or MR/review), remove that final action from the prompt text and set create_pr=true instead when preparing the workflow inputs.",
16
+ // The 15-minute default, stated rather than inherited: this is a per-workflow
17
+ // product decision, so a future change to the global default must not silently
18
+ // re-cadence a long autonomous run.
19
+ heartbeatIntervalMinutes: 15,
16
20
  inputs: {
17
21
  prompt: Type.String({ description: "The task or goal to research, execute, and refine. Do not include PR/MR submission instructions here; strip them from the task text and request them via create_pr=true instead." }),
18
22
  acceptance_criteria: Type.Optional(Type.String({ description: "Original immutable task contract this run must remain consistent with. Defaults to prompt. Orchestrators launching follow-up runs from reviewer findings should pass the ORIGINAL task text here." })),