@bastani/atomic 0.9.45 → 0.9.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +79 -0
- package/dist/builtin/intercom/CHANGELOG.md +22 -0
- package/dist/builtin/intercom/broker/broker.ts +124 -10
- package/dist/builtin/intercom/broker/client.ts +177 -25
- package/dist/builtin/intercom/broker/multinode-router.ts +228 -27
- package/dist/builtin/intercom/broker/multinode-state-store.ts +10 -0
- package/dist/builtin/intercom/broker/recipient-receipts.ts +38 -4
- package/dist/builtin/intercom/broker/send-handler.ts +9 -2
- package/dist/builtin/intercom/broker/sql-outbox-store.ts +29 -4
- package/dist/builtin/intercom/group.ts +25 -0
- package/dist/builtin/intercom/index.bundle.mjs +177 -35
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/intercom/retry-policy.ts +7 -0
- package/dist/builtin/intercom/types.ts +21 -0
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +21 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/extension/index.bundle.mjs +152 -56
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +79 -0
- package/dist/builtin/workflows/builtin/{chunk-6901nsd6.js → chunk-6jpwc09k.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-vwx023ge.js → chunk-qhm35ntx.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-jvna28mq.js → chunk-zhyqnf5g.js} +331 -68
- package/dist/builtin/workflows/builtin/goal.js +2 -2
- package/dist/builtin/workflows/builtin/index.js +3 -3
- package/dist/builtin/workflows/builtin/ralph.js +2 -2
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/skills/prompt-engineer/SKILL.md +1 -0
- package/dist/builtin/workflows/skills/prompt-engineer/references/gpt_6.md +2 -2
- package/dist/builtin/workflows/skills/prompt-engineer/references/gpt_6_1_sol.md +2 -2
- package/dist/builtin/workflows/skills/prompt-engineer/references/gpt_6_1_terra.md +77 -0
- package/dist/builtin/workflows/src/authoring.d.ts +1 -1
- package/dist/builtin/workflows/src/durable/journal/internal.d.ts +2 -0
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +7104 -3387
- package/dist/builtin/workflows/src/extension/local-worker-entry.mjs +11105 -7264
- package/dist/builtin/workflows/src/index.js +6796 -3606
- package/dist/builtin/workflows/src/local-worker-entry.mjs +11105 -7264
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +19 -0
- package/dist/core/agent-session-boundaries.d.ts.map +1 -1
- package/dist/core/agent-session-boundaries.js +1 -0
- package/dist/core/agent-session-boundaries.js.map +1 -1
- package/dist/core/agent-session-events.d.ts.map +1 -1
- package/dist/core/agent-session-events.js +2 -1
- package/dist/core/agent-session-events.js.map +1 -1
- package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
- package/dist/core/agent-session-extension-bindings.js +15 -2
- package/dist/core/agent-session-extension-bindings.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +3 -2
- package/dist/core/agent-session-methods.d.ts.map +1 -1
- package/dist/core/agent-session-methods.js.map +1 -1
- package/dist/core/agent-session-prompt.d.ts +4 -2
- package/dist/core/agent-session-prompt.d.ts.map +1 -1
- package/dist/core/agent-session-prompt.js +13 -8
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session-recovery.d.ts.map +1 -1
- package/dist/core/agent-session-recovery.js +92 -1
- package/dist/core/agent-session-recovery.js.map +1 -1
- package/dist/core/agent-session-tasks.d.ts.map +1 -1
- package/dist/core/agent-session-tasks.js +8 -1
- package/dist/core/agent-session-tasks.js.map +1 -1
- package/dist/core/agent-session-transfer.d.ts.map +1 -1
- package/dist/core/agent-session-transfer.js +5 -0
- package/dist/core/agent-session-transfer.js.map +1 -1
- package/dist/core/agent-session.d.ts +2 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +2 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/copilot-errors.d.ts +6 -0
- package/dist/core/copilot-errors.d.ts.map +1 -0
- package/dist/core/copilot-errors.js +17 -0
- package/dist/core/copilot-errors.js.map +1 -0
- package/dist/core/execution-model-router.d.ts.map +1 -1
- package/dist/core/execution-model-router.js +12 -2
- package/dist/core/execution-model-router.js.map +1 -1
- package/dist/core/extensions/context-types.d.ts +7 -0
- package/dist/core/extensions/context-types.d.ts.map +1 -1
- package/dist/core/extensions/context-types.js.map +1 -1
- package/dist/core/extensions/runner-context.d.ts.map +1 -1
- package/dist/core/extensions/runner-context.js +6 -0
- package/dist/core/extensions/runner-context.js.map +1 -1
- package/dist/core/extensions/ui-types.d.ts +9 -1
- package/dist/core/extensions/ui-types.d.ts.map +1 -1
- package/dist/core/extensions/ui-types.js.map +1 -1
- package/dist/core/extensions/workflow-activity-hub.d.ts +4 -0
- package/dist/core/extensions/workflow-activity-hub.d.ts.map +1 -1
- package/dist/core/extensions/workflow-activity-hub.js +26 -5
- package/dist/core/extensions/workflow-activity-hub.js.map +1 -1
- package/dist/core/extensions/workflow-events.d.ts +5 -0
- package/dist/core/extensions/workflow-events.d.ts.map +1 -1
- package/dist/core/extensions/workflow-events.js.map +1 -1
- package/dist/core/messages.d.ts +5 -0
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +1 -1
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-fallback-failures.d.ts +1 -1
- package/dist/core/model-fallback-failures.d.ts.map +1 -1
- package/dist/core/model-fallback-failures.js +37 -3
- package/dist/core/model-fallback-failures.js.map +1 -1
- package/dist/core/model-resolver-defaults.js +2 -2
- package/dist/core/model-resolver-defaults.js.map +1 -1
- package/dist/core/model-runtime-streaming.js +2 -2
- package/dist/core/model-runtime-streaming.js.map +1 -1
- package/dist/core/package-manager-npm.d.ts.map +1 -1
- package/dist/core/package-manager-npm.js +7 -3
- package/dist/core/package-manager-npm.js.map +1 -1
- package/dist/core/provider-composer.d.ts.map +1 -1
- package/dist/core/provider-composer.js +19 -11
- package/dist/core/provider-composer.js.map +1 -1
- package/dist/core/provider-rate-limits.d.ts +55 -6
- package/dist/core/provider-rate-limits.d.ts.map +1 -1
- package/dist/core/provider-rate-limits.js +182 -29
- package/dist/core/provider-rate-limits.js.map +1 -1
- package/dist/core/provider-request-admission.d.ts.map +1 -1
- package/dist/core/provider-request-admission.js +42 -18
- package/dist/core/provider-request-admission.js.map +1 -1
- package/dist/core/storage/node-sqlite.d.ts.map +1 -1
- package/dist/core/storage/node-sqlite.js +176 -19
- package/dist/core/storage/node-sqlite.js.map +1 -1
- package/dist/core/storage/provider.d.ts +4 -2
- package/dist/core/storage/provider.d.ts.map +1 -1
- package/dist/core/storage/provider.js +94 -5
- package/dist/core/storage/provider.js.map +1 -1
- package/dist/core/structured-output/index.d.ts.map +1 -1
- package/dist/core/structured-output/index.js +15 -8
- package/dist/core/structured-output/index.js.map +1 -1
- package/dist/core/structured-output/provider-failure.d.ts +17 -0
- package/dist/core/structured-output/provider-failure.d.ts.map +1 -0
- package/dist/core/structured-output/provider-failure.js +28 -0
- package/dist/core/structured-output/provider-failure.js.map +1 -0
- package/dist/core/tasks/completion.d.ts +6 -1
- package/dist/core/tasks/completion.d.ts.map +1 -1
- package/dist/core/tasks/completion.js +33 -18
- package/dist/core/tasks/completion.js.map +1 -1
- package/dist/core/tasks/pause.d.ts +2 -1
- package/dist/core/tasks/pause.d.ts.map +1 -1
- package/dist/core/tasks/pause.js +3 -2
- package/dist/core/tasks/pause.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +13 -7
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +6 -4
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/shell-task-render.d.ts +1 -0
- package/dist/core/tools/shell-task-render.d.ts.map +1 -1
- package/dist/core/tools/shell-task-render.js +7 -2
- package/dist/core/tools/shell-task-render.js.map +1 -1
- package/dist/core/tools/shell-task-status.d.ts.map +1 -1
- package/dist/core/tools/shell-task-status.js +2 -1
- package/dist/core/tools/shell-task-status.js.map +1 -1
- package/dist/extensions/tool-search/tool.d.ts +5 -0
- package/dist/extensions/tool-search/tool.d.ts.map +1 -1
- package/dist/extensions/tool-search/tool.js +38 -5
- package/dist/extensions/tool-search/tool.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +20 -12
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-actions.js +3 -2
- package/dist/modes/interactive/components/chat-session-host-actions.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +42 -14
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/interactive-extension-context.js +2 -1
- package/dist/modes/interactive/interactive-extension-context.js.map +1 -1
- package/dist/modes/interactive/interactive-extension-custom-ui.js +1 -1
- package/dist/modes/interactive/interactive-extension-custom-ui.js.map +1 -1
- package/dist/modes/interactive/interactive-extension-dialogs.js +10 -4
- package/dist/modes/interactive/interactive-extension-dialogs.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
- package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-startup.js +5 -6
- package/dist/modes/interactive/interactive-startup.js.map +1 -1
- package/dist/modes/interactive-engine/engine-custom-ui.d.ts +3 -0
- package/dist/modes/interactive-engine/engine-custom-ui.d.ts.map +1 -1
- package/dist/modes/interactive-engine/engine-custom-ui.js +1 -0
- package/dist/modes/interactive-engine/engine-custom-ui.js.map +1 -1
- package/dist/modes/interactive-engine/protocol.d.ts +6 -0
- package/dist/modes/interactive-engine/protocol.d.ts.map +1 -1
- package/dist/modes/interactive-engine/protocol.js +9 -0
- package/dist/modes/interactive-engine/protocol.js.map +1 -1
- package/dist/modes/interactive-engine/remote-component.d.ts.map +1 -1
- package/dist/modes/interactive-engine/remote-component.js +3 -2
- package/dist/modes/interactive-engine/remote-component.js.map +1 -1
- package/dist/modes/rpc/rpc-client-process.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client-process.js +8 -1
- package/dist/modes/rpc/rpc-client-process.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +7 -1
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/utils/ansi.d.ts +2 -0
- package/dist/utils/ansi.d.ts.map +1 -1
- package/dist/utils/ansi.js +5 -0
- package/dist/utils/ansi.js.map +1 -1
- package/docs/codemode.md +1 -1
- package/docs/intercom/reference.md +3 -3
- package/docs/llama-cpp.md +25 -14
- package/docs/models/evals.md +31 -12
- package/docs/models/reference.md +23 -51
- package/docs/models.md +3 -4
- package/docs/providers.md +5 -15
- package/docs/settings.md +1 -1
- package/docs/storage.md +2 -2
- package/docs/tools.md +1 -1
- package/docs/tui.md +2 -0
- package/docs/workflows/api-reference.md +20 -1
- package/docs/workflows/operations.md +17 -4
- package/npm-shrinkwrap.json +64 -44
- package/package.json +4 -4
|
@@ -6,6 +6,85 @@ 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.46] - 2026-10-27
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `RunResult.failure` (type `RunFailureClassification`, exported from `@bastani/atomic/workflows`) describes how a failed or blocked run's failure was classified: `kind`, `code`, `retryable`, `resumable`, `recoverability`, `disposition` and `retryAfterMs`. It is absent for runs that did not fail and for results recorded by earlier releases ([#4031](https://github.com/bastani-inc/atomic/issues/4031))
|
|
14
|
+
- Opening a running stage that runs on a local worker (`workflows.localWorkers`) from the workflow graph or `/workflow attach` now shows its live chat: the transcript so far, the reply as it streams, and tool calls, with `on local worker` in the chat header. It no longer shows "Post-mortem chat is available only after the stage completes". Only stages you have open stream to the root, so pools with many workers send nothing extra for stages nobody watches. ([#3996](https://github.com/bastani-inc/atomic/issues/3996))
|
|
15
|
+
- Enter (steer) and Ctrl+F (follow-up) in the chat of a stage that runs on a local worker now reach that stage and report the same delivery as an in-process stage. Each message is admitted at most once, even when a reply is lost and the send is retried. The stage's graph node shows `✉ N queued` with no chat open. SDK runs with `localWorkers` now accept `stageControlRegistry`, whose handles for pooled stages steer, queue follow-ups and stream events the same way ([#3996](https://github.com/bastani-inc/atomic/issues/3996))
|
|
16
|
+
- A single stage on a local worker can now be paused and resumed (`/workflow pause <run> <stage>`, stage chat, `session.workflows.pause/resume` with `stageId`) without pausing the pool; it keeps its worker's lease while paused. When the worker running an open stage dies or its attempt is fenced, the chat shows `RECONNECTING`, reattaches to the replacement attempt, and redelivers once each, in send order and in their original mode (a steer still steers), the messages the replacement does not have ([#3996](https://github.com/bastani-inc/atomic/issues/3996))
|
|
17
|
+
- Children on local workers can now ask for input. Their `ctx.ui` prompts show on their stage and are answered from the stage chat or the workflow tool's `answer` action, and a stage's `ask_user_question` shows its form in the stage chat as for an in-process stage; each answer reaches only the prompt it names on the worker's current attempt. Previously `ctx.ui` in a pooled child failed the child. SDK runs with `localWorkers` now accept `ui`, which answers those prompts (a rejecting `ui` fails the child with its error), and `stageUiBroker`, which receives the stages' `ask_user_question` forms (a broker that rejects a form fails that question in the stage with its error). `ctx.ui.custom` in a pooled child fails it with an error naming the limitation. When a worker dies, the replacement asks again a `ctx.ui` prompt that was still pending, and does not ask again one already answered. A stage's `ask_user_question` still waiting when its worker dies (or when a durable run resumes a stage) shows its form again on the replacement, and the answer becomes that call's result; previously the call was reported to the model as interrupted and the stage could finish without the answer ([#3996](https://github.com/bastani-inc/atomic/issues/3996))
|
|
18
|
+
- Added a GPT-6.1 Terra reference to the prompt-engineering skill, based on OpenAI's updated GPT-6 guide, covering when to choose Terra, effort selection, migration from GPT-5.6 Terra, computer-use and writing prompts, and selective use of the shared templates observed on Astra.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Starting a large local worker pool (`workflows.localWorkers`) no longer freezes the root's input and rendering for hundreds of milliseconds while the workers start. When a workflow starts many pooled children at once, such as `Promise.all` over 150 `ctx.workflow` calls, their journal work used to run back to back in one turn of the event loop, and each child's dispatch listed and sorted every checkpoint of the root. While many journal operations are queued, journal work now gives the event loop a turn after 8 ms, checked before each operation, and a child's dispatch looks up only its own boundary. When those children finish at once, completing each one no longer reads every checkpoint of the root, and the sweep for undeliverable stage messages checks run ownership only for runs that have messages to settle, and children finishing together complete in turns of the event loop instead of all at once
|
|
23
|
+
- An Intercom send to a workflow path pattern that matches a live or pending stage, such as `workflow:<run>/**/work-2` for a stage in a child on a local worker, no longer reports `notInKnownSet` ("not in the workflow's known stage set"). The message was delivered, but the known set came only from the launch-time scan of the workflow source, which cannot see stages of children loaded with `loadWorkerWorkflow`. Stages that already exist and can receive messages now count as known; a target that matches neither the scan nor any such stage still gets the warning.
|
|
24
|
+
- Large pooled fan-outs no longer stall the root's main thread when the workflow graph or a stage chat is open: typing in a stage chat during dispatch, wave turnover and run end of 150+ local workers stayed responsive only with nothing attached. Each store change used to re-project, re-freeze, re-expand and re-serialize every run of the root for the graph snapshot, the graph view, the attach pane, the workflow activity bridge and the BACKGROUND widget. Unchanged runs and stages now keep their frozen graph projections and expansions, the graph view and attach pane catch up once before they render or handle input, an open stage chat projects only its own run, the activity bridge publishes each root's final activity once per turn of the event loop (reading published activity, a root run's own lifecycle event and `waitForSettled` always see it first), store-driven widget refreshes spend at most 8 ms per 50 ms after the first four, and the coordinator gets the root's boundaries once per turn instead of once per enqueue and published batch. A pooled child whose worker was replaced also keeps its last known stages, marked `reconnecting`, until the replacement republishes them, instead of briefly showing no stages ([#4009](https://github.com/bastani-inc/atomic/issues/4009))
|
|
25
|
+
- Stages on local workers (`workflows.localWorkers`) now share provider rate limits. When one worker's stage gets an HTTP 429 or an exhausted quota (HTTP 402), the other workers and the root apply the same cooldown or unavailable mark, with the same provider or model scope and the same `Retry-After` end. They hold requests to that provider or model back, or move their fallback walk past it, instead of each sending requests until it gets its own 429. A worker that starts later gets the limits still running. Limits are sent only when a request is rate limited, at most once per provider or model every 250 ms ([#4023](https://github.com/bastani-inc/atomic/issues/4023))
|
|
26
|
+
- Text typed into a stage chat reaches the stage the same way whether the stage is idle, streaming, or still preparing its turn. Only `/skill:` commands expand. Before, Enter or Ctrl+F while the stage was busy expanded prompt templates such as `/review` and ran registered extension commands, or refused them, while the same text sent to an idle stage arrived as typed
|
|
27
|
+
- A message given when resuming a stage that was paused in the middle of its workflow prompt is now delivered as typed, as it is when resuming an idle stage chat. It used to go through the stage prompt's own options, which expand prompt templates and run registered extension commands
|
|
28
|
+
- A failed child workflow (`ctx.workflow`), or a failed stage, no longer has its failure classified from workflow or stage names. Before, a name containing words such as `rate-limit`, `overloaded` or `quota` could turn an ordinary failure, such as a tool that throws `ENOENT`, into a resumable rate limit or provider outage. The root then stayed `running`, blocked for a resume that could not help, and `run()` returned status `running` with the error. A parent now classifies a failed child from the child's own classification (`RunResult.failure`), at any nesting depth, for in-process children and children on local workers, including a child result a resumed root reads back from the journal. A stage's model-resolution failure is classified from the routing error it wraps. A child that really fails on a rate limit still blocks the root resumably. Error messages are unchanged ([#4031](https://github.com/bastani-inc/atomic/issues/4031))
|
|
29
|
+
- A stage whose model hits an exhausted quota (HTTP 402) or a rate limit that resets beyond the retry-delay cap now moves straight to the next fallback model, without spending retries or cooldown-wait rounds on it. A model-specific rate limit no longer delays a fallback on the same provider, and an HTTP 529 overload waits for its `Retry-After` like a 503 ([#3984](https://github.com/bastani-inc/atomic/issues/3984))
|
|
30
|
+
- A stage whose whole model and fallback chain returns HTTP 429 now waits for the earliest recorded provider cooldown and retries, instead of failing after one attempt per model. The wait is bounded: at most `retry.maxRetries` extra rounds per prompt, and only when the earliest cooldown fits the retry-delay cap (`retry.provider.maxRetryDelayMs`, else `retry.maxAgentDelayMs`, else 60 s). A persistent 429 still fails the stage once those rounds are spent ([#3984](https://github.com/bastani-inc/atomic/issues/3984))
|
|
31
|
+
- A workflow run whose stage replaced its session through model fallback no longer fails with `This extension ctx is stale after session replacement` and hides the stage's real error (such as a rate limit). An unhandled stale extension-context rejection no longer fails the run. It is logged to stderr with its stack and the run id, so the code that kept the retired session's context can be found ([#3981](https://github.com/bastani-inc/atomic/issues/3981))
|
|
32
|
+
- Disposing an SDK session right after a workflow run reports `completed`, without awaiting `session.workflows.waitForSettled(runId)`, no longer prints one `atomic-workflows: released stage listener failed Error: SessionClosed` warning per stage ([#3966](https://github.com/bastani-inc/atomic/issues/3966))
|
|
33
|
+
- A workflow stage that has sent its first prompt no longer reads `startup first-dispatch (0s total, 0s on current step; dispatched)` in the workflow tool's `stages` and `stage` output, which looked like a stalled stage for the whole run. The line now reads `startup complete (dispatched after 2s)` ([#3967](https://github.com/bastani-inc/atomic/issues/3967))
|
|
34
|
+
- A local-worker pool no longer fails every in-flight child with `Local worker process cleanup unconfirmed; permits quarantined` when one worker exits while its tool process groups are still running. The coordinator now reaps that worker's orphaned process groups (SIGTERM, then SIGKILL, with a bounded wait), holds the child's work lease so no other worker can claim it meanwhile, releases the permits of groups that are gone or hold only zombies, and replays only that worker's child on a replacement. If a group is still alive when the bound expires, only that child fails, with a retryable `WorkflowInfrastructureError` (`worker_cleanup_unconfirmed`) that names the process groups; their permits stay quarantined and the child is not replayed alongside them, also not by recovery after a stalled worker is stopped or by a durable resume (both read the quarantine from the journal), and closing the pool reports those groups. A durable resume reaps such an attempt's groups with the same bounded sequence, then replays it in the resumed root or fails only that child ([#3986](https://github.com/bastani-inc/atomic/issues/3986))
|
|
35
|
+
- A direct Intercom send to a live stage in a local worker pool no longer routinely fails with `The recipient did not confirm that it saved the message in time` while the pool is busy. Saving a message to a pooled stage's session no longer waits for the worker to re-read the root workflow's journal or for a usage-total transaction; it is confirmed once the coordinator has committed it ([#3983](https://github.com/bastani-inc/atomic/issues/3983))
|
|
36
|
+
- Resuming a quit or crashed root whose children run in `workflows.localWorkers` no longer fails in-flight children with `Session not found`. Resume used to make the previous run's pooled children claimable before the resumed root had enqueued them again, so workers crashed on `Stale work lease`, the pool failed with `Local worker retry limit`, and later children ran in the root process, which cannot see pooled stage sessions. A pooled child is now claimed only after the resumed root enqueues it, a child started after the pool has failed gets the pool's error instead of running in the root process, the three-attempt retry limit counts only crashes within one run of the root (a child resumed many times keeps its full budget), and a worker whose attempt was handed to another worker ends that attempt and serves new work instead of crashing and using up a retry ([#3979](https://github.com/bastani-inc/atomic/issues/3979))
|
|
37
|
+
- Large local worker pools no longer lose child workflows to `database is locked … gave up after waiting 30000 ms for the write lock` after a few hours. Resource admission reconciliation, which every pooled worker runs before it starts a command, read the whole resource permit history twice for every live worker while it held the journal's write lock. With 160 workers and tens of thousands of permits, each pass held the lock for nearly a second. Reconciliation now uses indexes, keeps only the most recent 256 released permits, and takes the write lock only when it has a permit to fence or prune ([#3978](https://github.com/bastani-inc/atomic/issues/3978))
|
|
38
|
+
- Workflow runs and local worker definitions load again after something such as a `/tmp` cleanup deletes the `jiti` transpile cache directory, instead of failing with `ENOENT: no such file or directory, open '/tmp/jiti/workflows-<name>.<hash>.cjs'` until Atomic restarts ([#3985](https://github.com/bastani-inc/atomic/issues/3985))
|
|
39
|
+
- A sticky Intercom send to a workflow pattern target (for example `workflow:<rootRunId>/**`) no longer fails newly starting pooled stages with `Extension startup failed: … pending-stage message <id> changed during delivery`. A stage drain whose transcript snapshot was taken before a slow session append finished used to deliver the already-receipted message again and then fail. The drain now treats an existing delivery receipt for that stage and session as success, so the message reaches each stage exactly once ([#3980](https://github.com/bastani-inc/atomic/issues/3980))
|
|
40
|
+
- A workflow that starts many child workflows at once, such as `Promise.all` over 150 `ctx.workflow` calls, no longer stalls the root session's input and rendering while the children start. Each workflow store change used to re-read every run and stage to detect lifecycle transitions, so a fan-out's cost grew with the square of its width; only the run, stage or tool node that changed is now re-read. Lifecycle events are unchanged
|
|
41
|
+
- A finished stage that ran on a local worker now reopens for post-mortem chat. It used to report `The retained session is missing, unreadable, or invalid` in the same session and SESSION UNAVAILABLE after a restart, because its session lives in the workflow journal rather than the host's session store ([#3996](https://github.com/bastani-inc/atomic/issues/3996))
|
|
42
|
+
- A workflow heartbeat no longer goes missing when its wake-up timer fires a moment early. The early wake-up found nothing due and rescheduled, and if the boundary passed while that pass was running, the heartbeat moved to the following interval instead of being sent. The scheduler now reads the clock once per pass, so a boundary that comes due during it is still delivered
|
|
43
|
+
- An idle local worker that meets its root's released lease (between the root's teardown and the pool's close) now ends cleanly instead of crashing, and the pool no longer spawns replacements that crash the same way ([#3979](https://github.com/bastani-inc/atomic/issues/3979))
|
|
44
|
+
- A stage chat open on a `ctx.ui` prompt or `ask_user_question` form of a stage on a local worker (`workflows.localWorkers`) now takes the answer after that worker dies: it shows the prompt the replacement worker asks again and delivers your answer to it once. Before, the open chat kept a prompt that dropped typed text and ignored Enter until you closed and reopened the stage. An answer you submit after the worker died but before the replacement asks again is now kept and given to the replacement's identical prompt instead of being lost, so you are not asked twice. The re-asked `ask_user_question` form no longer shows "Tool execution was interrupted after it started…" above it. A replacement worker whose child asks a different `ctx.ui` question at the same point (its text depends on data) no longer fails the child with `Pool topology scope: direct child source orders are not unique`: the unanswered question of the dead worker is replaced, an open stage chat moves to the new question, and an answer held for the old question is discarded. A child that really takes different steps when it runs again now fails with an error that says so in plain words. The forms of `ask_user_question` and the readiness gate are now built by one helper for in-process and local-worker stages, so they cannot drift apart ([#4022](https://github.com/bastani-inc/atomic/issues/4022))
|
|
45
|
+
- A child on a local worker that loses its worker three times now fails alone with a retryable `WorkflowInfrastructureError` (`worker_retry_limit`) that names its child run and definition export, as #3986 already did for a child whose processes survive cleanup. Before, it failed the whole pool within milliseconds: every other child, including one waiting on a prompt whose worker was never touched, was rejected with `Local worker retry limit`. The same applies to a child whose every attempt the root refused as stale. The failed child, its nested runs and their unfinished stages show failed with its error, so the graph counts none of them as active. Workers that keep crashing without work, worker startup failures and a coordinator that dies still fail the pool. When a pool closes without a quit, the root's unfinished work rows end `failed` instead of staying `ready` or `leased`; quarantined attempts keep their leases, and a resumed root replays failed work it enqueues again ([#3996](https://github.com/bastani-inc/atomic/issues/3996))
|
|
46
|
+
- The post-mortem chat of a completed or failed stage that ran on a local worker no longer shows `on local worker` in its header. Post-mortem chat runs in the root, so the header now matches an in-process stage's ([#3996](https://github.com/bastani-inc/atomic/issues/3996))
|
|
47
|
+
- Pausing a stage or a run (`/workflow pause <run> [stage]`) no longer tells the stage that its interrupted background shell ended for good. The shell's notice now says a pause interrupted it and that the step should be redone or continued, and the "Continue where you left off" resume prompt says to redo or continue a step the pause interrupted. Previously the notice said "This execution has ended. Do not restart it", and a resumed stage could stop early with its remaining work never run ([#3996](https://github.com/bastani-inc/atomic/issues/3996))
|
|
48
|
+
|
|
49
|
+
## [0.9.46-alpha.1] - 2026-10-27
|
|
50
|
+
|
|
51
|
+
### Added
|
|
52
|
+
|
|
53
|
+
- `RunResult.failure` (type `RunFailureClassification`, exported from `@bastani/atomic/workflows`) describes how a failed or blocked run's failure was classified: `kind`, `code`, `retryable`, `resumable`, `recoverability`, `disposition` and `retryAfterMs`. It is absent for runs that did not fail and for results recorded by earlier releases ([#4031](https://github.com/bastani-inc/atomic/issues/4031))
|
|
54
|
+
- Opening a running stage that runs on a local worker (`workflows.localWorkers`) from the workflow graph or `/workflow attach` now shows its live chat: the transcript so far, the reply as it streams, and tool calls, with `on local worker` in the chat header. It no longer shows "Post-mortem chat is available only after the stage completes". Only stages you have open stream to the root, so pools with many workers send nothing extra for stages nobody watches. ([#3996](https://github.com/bastani-inc/atomic/issues/3996))
|
|
55
|
+
- Enter (steer) and Ctrl+F (follow-up) in the chat of a stage that runs on a local worker now reach that stage and report the same delivery as an in-process stage. Each message is admitted at most once, even when a reply is lost and the send is retried. The stage's graph node shows `✉ N queued` with no chat open. SDK runs with `localWorkers` now accept `stageControlRegistry`, whose handles for pooled stages steer, queue follow-ups and stream events the same way ([#3996](https://github.com/bastani-inc/atomic/issues/3996))
|
|
56
|
+
- A single stage on a local worker can now be paused and resumed (`/workflow pause <run> <stage>`, stage chat, `session.workflows.pause/resume` with `stageId`) without pausing the pool; it keeps its worker's lease while paused. When the worker running an open stage dies or its attempt is fenced, the chat shows `RECONNECTING`, reattaches to the replacement attempt, and redelivers once each, in send order and in their original mode (a steer still steers), the messages the replacement does not have ([#3996](https://github.com/bastani-inc/atomic/issues/3996))
|
|
57
|
+
- Children on local workers can now ask for input. Their `ctx.ui` prompts show on their stage and are answered from the stage chat or the workflow tool's `answer` action, and a stage's `ask_user_question` shows its form in the stage chat as for an in-process stage; each answer reaches only the prompt it names on the worker's current attempt. Previously `ctx.ui` in a pooled child failed the child. SDK runs with `localWorkers` now accept `ui`, which answers those prompts (a rejecting `ui` fails the child with its error), and `stageUiBroker`, which receives the stages' `ask_user_question` forms (a broker that rejects a form fails that question in the stage with its error). `ctx.ui.custom` in a pooled child fails it with an error naming the limitation. When a worker dies, the replacement asks again a `ctx.ui` prompt that was still pending, and does not ask again one already answered. A stage's `ask_user_question` still waiting when its worker dies (or when a durable run resumes a stage) shows its form again on the replacement, and the answer becomes that call's result; previously the call was reported to the model as interrupted and the stage could finish without the answer ([#3996](https://github.com/bastani-inc/atomic/issues/3996))
|
|
58
|
+
- Added a GPT-6.1 Terra reference to the prompt-engineering skill, based on OpenAI's updated GPT-6 guide, covering when to choose Terra, effort selection, migration from GPT-5.6 Terra, computer-use and writing prompts, and selective use of the shared templates observed on Astra.
|
|
59
|
+
|
|
60
|
+
### Fixed
|
|
61
|
+
|
|
62
|
+
- Starting a large local worker pool (`workflows.localWorkers`) no longer freezes the root's input and rendering for hundreds of milliseconds while the workers start. When a workflow starts many pooled children at once, such as `Promise.all` over 150 `ctx.workflow` calls, their journal work used to run back to back in one turn of the event loop, and each child's dispatch listed and sorted every checkpoint of the root. While many journal operations are queued, journal work now gives the event loop a turn after 8 ms, checked before each operation, and a child's dispatch looks up only its own boundary. When those children finish at once, completing each one no longer reads every checkpoint of the root, and the sweep for undeliverable stage messages checks run ownership only for runs that have messages to settle, and children finishing together complete in turns of the event loop instead of all at once
|
|
63
|
+
- An Intercom send to a workflow path pattern that matches a live or pending stage, such as `workflow:<run>/**/work-2` for a stage in a child on a local worker, no longer reports `notInKnownSet` ("not in the workflow's known stage set"). The message was delivered, but the known set came only from the launch-time scan of the workflow source, which cannot see stages of children loaded with `loadWorkerWorkflow`. Stages that already exist and can receive messages now count as known; a target that matches neither the scan nor any such stage still gets the warning.
|
|
64
|
+
- Large pooled fan-outs no longer stall the root's main thread when the workflow graph or a stage chat is open: typing in a stage chat during dispatch, wave turnover and run end of 150+ local workers stayed responsive only with nothing attached. Each store change used to re-project, re-freeze, re-expand and re-serialize every run of the root for the graph snapshot, the graph view, the attach pane, the workflow activity bridge and the BACKGROUND widget. Unchanged runs and stages now keep their frozen graph projections and expansions, the graph view and attach pane catch up once before they render or handle input, an open stage chat projects only its own run, the activity bridge publishes each root's final activity once per turn of the event loop (reading published activity, a root run's own lifecycle event and `waitForSettled` always see it first), store-driven widget refreshes spend at most 8 ms per 50 ms after the first four, and the coordinator gets the root's boundaries once per turn instead of once per enqueue and published batch. A pooled child whose worker was replaced also keeps its last known stages, marked `reconnecting`, until the replacement republishes them, instead of briefly showing no stages ([#4009](https://github.com/bastani-inc/atomic/issues/4009))
|
|
65
|
+
- Stages on local workers (`workflows.localWorkers`) now share provider rate limits. When one worker's stage gets an HTTP 429 or an exhausted quota (HTTP 402), the other workers and the root apply the same cooldown or unavailable mark, with the same provider or model scope and the same `Retry-After` end. They hold requests to that provider or model back, or move their fallback walk past it, instead of each sending requests until it gets its own 429. A worker that starts later gets the limits still running. Limits are sent only when a request is rate limited, at most once per provider or model every 250 ms ([#4023](https://github.com/bastani-inc/atomic/issues/4023))
|
|
66
|
+
- Text typed into a stage chat reaches the stage the same way whether the stage is idle, streaming, or still preparing its turn. Only `/skill:` commands expand. Before, Enter or Ctrl+F while the stage was busy expanded prompt templates such as `/review` and ran registered extension commands, or refused them, while the same text sent to an idle stage arrived as typed
|
|
67
|
+
- A message given when resuming a stage that was paused in the middle of its workflow prompt is now delivered as typed, as it is when resuming an idle stage chat. It used to go through the stage prompt's own options, which expand prompt templates and run registered extension commands
|
|
68
|
+
- A failed child workflow (`ctx.workflow`), or a failed stage, no longer has its failure classified from workflow or stage names. Before, a name containing words such as `rate-limit`, `overloaded` or `quota` could turn an ordinary failure, such as a tool that throws `ENOENT`, into a resumable rate limit or provider outage. The root then stayed `running`, blocked for a resume that could not help, and `run()` returned status `running` with the error. A parent now classifies a failed child from the child's own classification (`RunResult.failure`), at any nesting depth, for in-process children and children on local workers, including a child result a resumed root reads back from the journal. A stage's model-resolution failure is classified from the routing error it wraps. A child that really fails on a rate limit still blocks the root resumably. Error messages are unchanged ([#4031](https://github.com/bastani-inc/atomic/issues/4031))
|
|
69
|
+
- A stage whose model hits an exhausted quota (HTTP 402) or a rate limit that resets beyond the retry-delay cap now moves straight to the next fallback model, without spending retries or cooldown-wait rounds on it. A model-specific rate limit no longer delays a fallback on the same provider, and an HTTP 529 overload waits for its `Retry-After` like a 503 ([#3984](https://github.com/bastani-inc/atomic/issues/3984))
|
|
70
|
+
- A stage whose whole model and fallback chain returns HTTP 429 now waits for the earliest recorded provider cooldown and retries, instead of failing after one attempt per model. The wait is bounded: at most `retry.maxRetries` extra rounds per prompt, and only when the earliest cooldown fits the retry-delay cap (`retry.provider.maxRetryDelayMs`, else `retry.maxAgentDelayMs`, else 60 s). A persistent 429 still fails the stage once those rounds are spent ([#3984](https://github.com/bastani-inc/atomic/issues/3984))
|
|
71
|
+
- A workflow run whose stage replaced its session through model fallback no longer fails with `This extension ctx is stale after session replacement` and hides the stage's real error (such as a rate limit). An unhandled stale extension-context rejection no longer fails the run. It is logged to stderr with its stack and the run id, so the code that kept the retired session's context can be found ([#3981](https://github.com/bastani-inc/atomic/issues/3981))
|
|
72
|
+
- Disposing an SDK session right after a workflow run reports `completed`, without awaiting `session.workflows.waitForSettled(runId)`, no longer prints one `atomic-workflows: released stage listener failed Error: SessionClosed` warning per stage ([#3966](https://github.com/bastani-inc/atomic/issues/3966))
|
|
73
|
+
- A workflow stage that has sent its first prompt no longer reads `startup first-dispatch (0s total, 0s on current step; dispatched)` in the workflow tool's `stages` and `stage` output, which looked like a stalled stage for the whole run. The line now reads `startup complete (dispatched after 2s)` ([#3967](https://github.com/bastani-inc/atomic/issues/3967))
|
|
74
|
+
- A local-worker pool no longer fails every in-flight child with `Local worker process cleanup unconfirmed; permits quarantined` when one worker exits while its tool process groups are still running. The coordinator now reaps that worker's orphaned process groups (SIGTERM, then SIGKILL, with a bounded wait), holds the child's work lease so no other worker can claim it meanwhile, releases the permits of groups that are gone or hold only zombies, and replays only that worker's child on a replacement. If a group is still alive when the bound expires, only that child fails, with a retryable `WorkflowInfrastructureError` (`worker_cleanup_unconfirmed`) that names the process groups; their permits stay quarantined and the child is not replayed alongside them, also not by recovery after a stalled worker is stopped or by a durable resume (both read the quarantine from the journal), and closing the pool reports those groups. A durable resume reaps such an attempt's groups with the same bounded sequence, then replays it in the resumed root or fails only that child ([#3986](https://github.com/bastani-inc/atomic/issues/3986))
|
|
75
|
+
- A direct Intercom send to a live stage in a local worker pool no longer routinely fails with `The recipient did not confirm that it saved the message in time` while the pool is busy. Saving a message to a pooled stage's session no longer waits for the worker to re-read the root workflow's journal or for a usage-total transaction; it is confirmed once the coordinator has committed it ([#3983](https://github.com/bastani-inc/atomic/issues/3983))
|
|
76
|
+
- Resuming a quit or crashed root whose children run in `workflows.localWorkers` no longer fails in-flight children with `Session not found`. Resume used to make the previous run's pooled children claimable before the resumed root had enqueued them again, so workers crashed on `Stale work lease`, the pool failed with `Local worker retry limit`, and later children ran in the root process, which cannot see pooled stage sessions. A pooled child is now claimed only after the resumed root enqueues it, a child started after the pool has failed gets the pool's error instead of running in the root process, the three-attempt retry limit counts only crashes within one run of the root (a child resumed many times keeps its full budget), and a worker whose attempt was handed to another worker ends that attempt and serves new work instead of crashing and using up a retry ([#3979](https://github.com/bastani-inc/atomic/issues/3979))
|
|
77
|
+
- Large local worker pools no longer lose child workflows to `database is locked … gave up after waiting 30000 ms for the write lock` after a few hours. Resource admission reconciliation, which every pooled worker runs before it starts a command, read the whole resource permit history twice for every live worker while it held the journal's write lock. With 160 workers and tens of thousands of permits, each pass held the lock for nearly a second. Reconciliation now uses indexes, keeps only the most recent 256 released permits, and takes the write lock only when it has a permit to fence or prune ([#3978](https://github.com/bastani-inc/atomic/issues/3978))
|
|
78
|
+
- Workflow runs and local worker definitions load again after something such as a `/tmp` cleanup deletes the `jiti` transpile cache directory, instead of failing with `ENOENT: no such file or directory, open '/tmp/jiti/workflows-<name>.<hash>.cjs'` until Atomic restarts ([#3985](https://github.com/bastani-inc/atomic/issues/3985))
|
|
79
|
+
- A sticky Intercom send to a workflow pattern target (for example `workflow:<rootRunId>/**`) no longer fails newly starting pooled stages with `Extension startup failed: … pending-stage message <id> changed during delivery`. A stage drain whose transcript snapshot was taken before a slow session append finished used to deliver the already-receipted message again and then fail. The drain now treats an existing delivery receipt for that stage and session as success, so the message reaches each stage exactly once ([#3980](https://github.com/bastani-inc/atomic/issues/3980))
|
|
80
|
+
- A workflow that starts many child workflows at once, such as `Promise.all` over 150 `ctx.workflow` calls, no longer stalls the root session's input and rendering while the children start. Each workflow store change used to re-read every run and stage to detect lifecycle transitions, so a fan-out's cost grew with the square of its width; only the run, stage or tool node that changed is now re-read. Lifecycle events are unchanged
|
|
81
|
+
- A finished stage that ran on a local worker now reopens for post-mortem chat. It used to report `The retained session is missing, unreadable, or invalid` in the same session and SESSION UNAVAILABLE after a restart, because its session lives in the workflow journal rather than the host's session store ([#3996](https://github.com/bastani-inc/atomic/issues/3996))
|
|
82
|
+
- A workflow heartbeat no longer goes missing when its wake-up timer fires a moment early. The early wake-up found nothing due and rescheduled, and if the boundary passed while that pass was running, the heartbeat moved to the following interval instead of being sent. The scheduler now reads the clock once per pass, so a boundary that comes due during it is still delivered
|
|
83
|
+
- An idle local worker that meets its root's released lease (between the root's teardown and the pool's close) now ends cleanly instead of crashing, and the pool no longer spawns replacements that crash the same way ([#3979](https://github.com/bastani-inc/atomic/issues/3979))
|
|
84
|
+
- A stage chat open on a `ctx.ui` prompt or `ask_user_question` form of a stage on a local worker (`workflows.localWorkers`) now takes the answer after that worker dies: it shows the prompt the replacement worker asks again and delivers your answer to it once. Before, the open chat kept a prompt that dropped typed text and ignored Enter until you closed and reopened the stage. An answer you submit after the worker died but before the replacement asks again is now kept and given to the replacement's identical prompt instead of being lost, so you are not asked twice. The re-asked `ask_user_question` form no longer shows "Tool execution was interrupted after it started…" above it. A replacement worker whose child asks a different `ctx.ui` question at the same point (its text depends on data) no longer fails the child with `Pool topology scope: direct child source orders are not unique`: the unanswered question of the dead worker is replaced, an open stage chat moves to the new question, and an answer held for the old question is discarded. A child that really takes different steps when it runs again now fails with an error that says so in plain words. The forms of `ask_user_question` and the readiness gate are now built by one helper for in-process and local-worker stages, so they cannot drift apart ([#4022](https://github.com/bastani-inc/atomic/issues/4022))
|
|
85
|
+
- A child on a local worker that loses its worker three times now fails alone with a retryable `WorkflowInfrastructureError` (`worker_retry_limit`) that names its child run and definition export, as #3986 already did for a child whose processes survive cleanup. Before, it failed the whole pool within milliseconds: every other child, including one waiting on a prompt whose worker was never touched, was rejected with `Local worker retry limit`. The same applies to a child whose every attempt the root refused as stale. The failed child, its nested runs and their unfinished stages show failed with its error, so the graph counts none of them as active. Workers that keep crashing without work, worker startup failures and a coordinator that dies still fail the pool. When a pool closes without a quit, the root's unfinished work rows end `failed` instead of staying `ready` or `leased`; quarantined attempts keep their leases, and a resumed root replays failed work it enqueues again ([#3996](https://github.com/bastani-inc/atomic/issues/3996))
|
|
86
|
+
- The post-mortem chat of a completed or failed stage that ran on a local worker no longer shows `on local worker` in its header. Post-mortem chat runs in the root, so the header now matches an in-process stage's ([#3996](https://github.com/bastani-inc/atomic/issues/3996))
|
|
87
|
+
|
|
9
88
|
## [0.9.45] - 2026-10-25
|
|
10
89
|
|
|
11
90
|
### Fixed
|