@bastani/atomic 0.9.41-alpha.1 → 0.9.41
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 +112 -0
- package/dist/builtin/intercom/CHANGELOG.md +12 -0
- package/dist/builtin/intercom/package.json +2 -2
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +14 -0
- package/dist/builtin/subagents/package.json +3 -3
- package/dist/builtin/web-access/package.json +2 -2
- package/dist/builtin/workflows/CHANGELOG.md +36 -0
- package/dist/builtin/workflows/builtin/adversarial-verification.js +2 -2
- package/dist/builtin/workflows/builtin/{chunk-qjqn389g.js → chunk-2jfst7e4.js} +72 -10
- package/dist/builtin/workflows/builtin/{chunk-4bwtzyjq.js → chunk-49kkbdpr.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-qdd897wm.js → chunk-apagcs4r.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-454hz1qt.js → chunk-dcxscfnb.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-bjva9j5k.js → chunk-hx8v4p97.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-85mx6wcf.js → chunk-k5q3t7je.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-8kkv04hz.js → chunk-n2gkzkty.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-y45tkftg.js → chunk-txbc93t6.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-x0vvjyzg.js → chunk-w4xp1w1f.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-5eh0brtd.js → chunk-z3pajrfs.js} +1 -1
- package/dist/builtin/workflows/builtin/classify-and-act.js +2 -2
- package/dist/builtin/workflows/builtin/fan-out-and-synthesize.js +2 -2
- package/dist/builtin/workflows/builtin/generate-and-filter.js +2 -2
- package/dist/builtin/workflows/builtin/goal.js +2 -2
- package/dist/builtin/workflows/builtin/index.js +10 -10
- package/dist/builtin/workflows/builtin/loop-until-done.js +2 -2
- package/dist/builtin/workflows/builtin/open-claude-design.js +2 -2
- package/dist/builtin/workflows/builtin/ralph.js +2 -2
- package/dist/builtin/workflows/builtin/tournament.js +2 -2
- package/dist/builtin/workflows/package.json +2 -2
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +511 -28
- package/dist/builtin/workflows/src/index.js +582 -99
- package/dist/core/compaction/compaction-runner.d.ts.map +1 -1
- package/dist/core/compaction/compaction-runner.js +1 -0
- package/dist/core/compaction/compaction-runner.js.map +1 -1
- package/dist/core/model-config.d.ts +28 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/provider-composer-internal.d.ts +2 -1
- package/dist/core/provider-composer-internal.d.ts.map +1 -1
- package/dist/core/provider-composer-internal.js +8 -6
- package/dist/core/provider-composer-internal.js.map +1 -1
- package/dist/core/tools/grep.js +1 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/usage-totals.d.ts.map +1 -1
- package/dist/core/usage-totals.js +2 -0
- package/dist/core/usage-totals.js.map +1 -1
- package/docs/getting-started/installation.md +19 -1
- package/docs/models-configuration.md +9 -0
- package/docs/models.md +3 -1
- package/docs/providers.md +1 -1
- package/docs/quickstart.md +1 -1
- package/docs/windows.md +1 -1
- package/docs/workflows/authoring.md +1 -1
- package/npm-shrinkwrap.json +67 -54
- package/package.json +6 -6
- package/schemas/models.schema.json +21 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,118 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.9.41] - 2026-10-22
|
|
6
|
+
|
|
7
|
+
Workflows stay contained when they fail, classifiers read images, and usage totals say where their cost came from.
|
|
8
|
+
|
|
9
|
+
- **Workflow failures stay inside their run:** an unhandled `ctx` rejection, a stray `ctx.exit()`, or a branch that outlives a kill or quit now fails or ends only its own run instead of the whole Atomic process ([#3846](https://github.com/bastani-inc/atomic/issues/3846), [#3849](https://github.com/bastani-inc/atomic/pull/3849))
|
|
10
|
+
- **Image input for more classifiers:** `models.classify()` accepts `images` for Jev and Clef on Cloudflare Workers AI and OpenRouter, with corrected Workers AI pricing ([#3847](https://github.com/bastani-inc/atomic/pull/3847))
|
|
11
|
+
- **Provider-billed cost flag:** `usage.cost.totalFromProvider` tells you whether a usage total is the provider's billed cost or a catalog estimate ([#3852](https://github.com/bastani-inc/atomic/pull/3852))
|
|
12
|
+
- **Steady sessions and installs:** SDK and RPC `steer()`/`followUp()` start a turn on an idle session, extension OAuth providers can report rejected logins, and the installers skip downloading a release that is already installed ([#3801](https://github.com/bastani-inc/atomic/issues/3801), [#3851](https://github.com/bastani-inc/atomic/pull/3851))
|
|
13
|
+
- **Terminal and rendering fixes:** Rex terminal support, terminal modes restored on unexpected exit, Markdown tables after unclosed `$$` blocks, CJK emphasis, and `@` autocomplete with regex characters ([#3847](https://github.com/bastani-inc/atomic/pull/3847))
|
|
14
|
+
|
|
15
|
+
**Breaking change:** SDK `steer()` and `followUp()` and the RPC `steer` and `follow_up` commands now start a turn when the agent is idle and the session already has messages.
|
|
16
|
+
|
|
17
|
+
### Breaking Changes
|
|
18
|
+
|
|
19
|
+
- SDK `steer()` and `followUp()`, and the RPC `steer` and `follow_up` commands, now start a turn when the agent is idle and the session already has messages. On a session with no messages yet, or after a run was aborted, the message still waits for the next prompt. If you queue a message on an idle session that has messages and then call `prompt()` (or send an RPC `prompt`) right away, that prompt now fails with "Agent is already processing" unless you pass `streamingBehavior` (`"steer"` or `"followUp"`) to queue it behind the running turn.
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- Added `usage.cost.totalFromProvider` to usage totals. Summed usage, including nested tool-call and compaction totals, reports `true` only when every part was a provider-reported billed cost; catalog-estimated and legacy sessions without the field report `false`.
|
|
24
|
+
- Codemode `models.classify()` now also accepts `images` for TypeSafe's Jev and Cloudflare's Clef models on `cloudflare-workers-ai` and OpenRouter, next to Jev on `typesafe`, and lists Clef Omni and Jev 1.14 on Workers AI. See [Use classifier models](docs/models.md#use-classifier-models). Provider-level `compat` in `models.json` now also applies to classifier models, with the System One fields `imageInput` and `sendImageOptInHeader` for proxies that serve another service's classifiers. Extension classifier models accept `compat` too. See [Models Configuration](docs/models-configuration.md#typesafe-system-one).
|
|
25
|
+
- Added support for the Rex terminal (`TERM_PROGRAM=rex`): OSC 8 hyperlinks, truecolor, and inline images. Links previously fell back to the `text (url)` format ([#11131](https://github.com/earendil-works/pi/issues/11131), [#11150](https://github.com/earendil-works/pi/pull/11150) by [@petrroll](https://github.com/petrroll), [#11183](https://github.com/earendil-works/pi/pull/11183) by [@nihaals](https://github.com/nihaals))
|
|
26
|
+
- `routeExecutionModel()` accepts an optional `inference` option (exported type `RoutingInferenceBounds`) for callers that must not wait indefinitely on routing inference: `deadline` is an `AbortSignal` whose abort ends routing as a complete inference failure, so the caller can degrade to `AutoRoutingInferenceError.currentModelRoute`, and `limit` runs each routing inference under the caller's concurrency limit. Without it, routing keeps having no built-in deadline.
|
|
27
|
+
- RPC `get_available_models` and `refresh_models` responses now list extension providers that accept an API key in `customAuthProviders`, and report extension providers' environment or configured-key authentication in `providerAuthStatuses`.
|
|
28
|
+
- Extension OAuth providers can throw `OAuthRefreshRejectedError` (exported from `@bastani/atomic`, with `isOAuthRefreshRejectedError`) from `refreshToken` when the provider rejected the stored login. The user then sees the same `run /login <provider>` guidance as for a built-in provider. See [Provider OAuth](docs/custom-provider/oauth.md#rejected-logins).
|
|
29
|
+
- Exported `noOpUIContext`, the default no-op extension UI context, from the SDK entry point ([#3801](https://github.com/bastani-inc/atomic/issues/3801))
|
|
30
|
+
- `session.workflows.waitForSettled(runId)` resolves once a workflow run's ending is recorded in the workflow journal and its lease is released, so an SDK host can wait for the journal to be final after a run reports `completed`, `failed` or `killed`. It resolves at once for a run the session is not executing, and rejects with `WorkflowRunStorageError` when the journal could not record the ending. Extensions that call `registerWorkflowRunControl` must implement `waitForSettled` ([#3811](https://github.com/bastani-inc/atomic/issues/3811))
|
|
31
|
+
- `createStructuredOutputTool()` and `generateStructuredOutput()` accept a `timeoutMs` option: the wall-clock limit in milliseconds for one whole structured decision, covering every repair attempt, transport retry, and model fallback together. Without it, the `structured_output` tool uses the session's `httpIdleTimeoutMs` setting and other calls use that setting's 10-minute default. The calling model cannot set it. Extensions can read the setting with `ctx.getHttpIdleTimeoutMs()` ([#3806](https://github.com/bastani-inc/atomic/issues/3806))
|
|
32
|
+
- The task-host `CancelCause` contract adds `pause` and `quit`, typed resumable stops for an agent that is paused and awaiting resume rather than ended. A workflow stage pausing now stops its agents with `pause` instead of `parent-handoff`; `parent-handoff` stays valid for fresh-start handoffs and for entries recorded by earlier versions.
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- `install.sh` and `install.ps1` no longer re-download Atomic when the requested release (or the latest release, when none is requested) is already installed and healthy. They print `atomic <tag> is already installed; skipping download`, keep the `Installed to` line and PATH guidance, and download nothing; on Windows a rerun also restores a missing User PATH entry. A different version, or a missing or broken launcher, `current` pointer (including one that points at a versions directory outside the install root), shim, or payload (including a missing `app.js` or other startup file), installs and repairs as before. Pass `--force` (`-Force` on Windows) or set `ATOMIC_FORCE_INSTALL=1` to reinstall anyway.
|
|
37
|
+
- Reopening `/resume` is faster: the session list is kept between opens and reloads only the sessions that were created, renamed, deleted, or changed since the last open, including changes made by other Atomic processes sharing the store.
|
|
38
|
+
- A subagent stopped with `pause` or `quit`, or by its workflow stage pausing, now reaches the parent as `Subagent <agent> quit · paused, awaiting resume` (or `paused · awaiting resume`) with `Stop reason: quit` or `pause` and the exact `subagent({ action: "resume", id: "<child-id>" })` call, as a quit workflow run is reported, instead of a `parent-handoff` stop notice saying the execution has ended and must not be restarted. Its `/tasks` row shows **Paused · awaiting resume**. Children cancelled for good keep the ended notice.
|
|
39
|
+
- The What's New banner shown after an update now leads with the release's highlights summary and points to `/changelog` for the full list of changes. Releases without a summary still show their full notes ([#3804](https://github.com/bastani-inc/atomic/issues/3804))
|
|
40
|
+
- Provider attribution headers now come from pi-ai's client identity, still sent only while `enableInstallTelemetry` is on. Chat requests to providers send Atomic's `User-Agent` (`atomic/<version> (<platform>; <runtime>; <arch>)`), including Cloudflare requests, which sent `atomic` before, and OpenCode requests send `x-opencode-client` whenever attribution is enabled, not only with a session. Extensions' `before_provider_headers` handlers no longer see the OpenRouter, NVIDIA NIM, and OpenCode attribution headers or the default `User-Agent`.
|
|
41
|
+
- The release lookup that runs before Atomic downloads `fd` or `ripgrep` now sends Atomic's `User-Agent` (`atomic/<version> (<platform>; <runtime>; <arch>)`) instead of `atomic`.
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- Fixed Cloudflare Workers AI classifier pricing: Clef Flash costs $0.038 and Jev $0.042 per million input tokens instead of Jev being listed as free.
|
|
46
|
+
- Fixed the `grep` tool following ripgrep's `RIPGREP_CONFIG_PATH` whenever it searches with `rg`, such as with `literal: true`, where config options like `--ignore-case` silently changed what the tool returned. The tool now runs `rg --no-config` ([#11203](https://github.com/earendil-works/pi/issues/11203))
|
|
47
|
+
- Fixed `@` file autocomplete returning no results for queries containing regex metacharacters such as `[` or `(` ([#10972](https://github.com/earendil-works/pi/issues/10972))
|
|
48
|
+
- Fixed an unclosed `$$` display-math block swallowing or corrupting every Markdown table after it ([#11159](https://github.com/earendil-works/pi/issues/11159))
|
|
49
|
+
- Fixed terminal modes (mouse tracking, focus reporting, Kitty keyboard protocol, bracketed paste, alternate screen) staying enabled when the process exits without calling `stop()`. A process `exit` handler now restores them synchronously ([#11170](https://github.com/earendil-works/pi/issues/11170))
|
|
50
|
+
- Fixed Markdown bold and italic not rendering next to CJK punctuation ([#10730](https://github.com/earendil-works/pi/pull/10730) by [@theBucky](https://github.com/theBucky))
|
|
51
|
+
- A rejection that workflow code leaves unhandled no longer ends the whole Atomic process. This covers a rejected `ctx.tool`, `ctx.task`, `ctx.chain`, `ctx.parallel`, `ctx.workflow`, `ctx.ui` prompt (`input`, `confirm`, `select`, `editor` or `custom`) or stage method call (`prompt`, `complete`, `steer`, `sendUserMessage`, `setModel`, `compact` and the other promise-returning stage methods), including one that is started and never awaited (`void ctx.task(...)` used to leave the run `completed` with no sign of the failure), a `ctx.tool` call made after its run has stopped (`ctx.tool admission is closed for this run`), a string, number or `undefined` thrown from a `ctx.tool` callback, and an error the workflow file throws or receives from `fs` itself, for example in a rolling pool that keeps `branch.finally(...)` as a separate promise. It also covers `ctx.exit()` called inside such a branch, which ends the run as exited with the status and outputs it asked for (on Bun the process used to end right after the run reported `completed`), and a child workflow's own branch that uses `ctx` after its parent exited. Stopping a run no longer ends the process either: a branch that keeps running after `/workflow quit`, a kill, or an abort signal from the code that started the run, and then calls `ctx.stage`, `ctx.task`, `ctx.workflow` or `ctx.exit()`, or that was waiting on a `ctx.ui` prompt when the run was killed, could end it with `workflow killed` (a kill), the quit error (`/workflow quit`), an `AbortError` or `atomic-workflows: HIL aborted` right after the run reported `killed` or `paused`, and the run keeps that status. `ctx.exit()` called in such a branch while the run is paused ends the run as exited once the run resumes, and a kill of the paused run still ends it as `killed` (on Node it used to end as `failed` with `Workflow exit is waiting for explicit resume`, and on Bun the process ended). The rejection fails its own run when it surfaces before that run decides its final status, so other runs, stages, and the interactive engine keep going; a `ctx.task`, `ctx.chain`, `ctx.parallel`, `ctx.workflow`, `ctx.ui` or stage method rejection that the workflow awaits or catches reaches that code unchanged and does not fail the run. Once the run has ended, an error that a `ctx` call rejected with, what `ctx.exit()` threw and the reason the run was stopped with are ignored (the reason of a kill is the string `workflow killed`, which is ignored by value for the rest of the process, so a branch that wakes up long after the run ended is covered too), and so is an error thrown by the workflow file for the next 30 seconds, so the remaining branches of a failing pool cannot end the process; a later one ends it as before. On Node the owning run is identified exactly; on Bun a `ctx` call's rejection, what `ctx.exit()` threw and the reason the run was stopped with are matched by identity (a `ctx` call's string, number or `undefined` only within the event-loop turn in which the call rejected, and the string a kill uses by value), and an error thrown by the workflow file itself is matched through its stack to the run of the file that calls `workflow()`, but only when no other run of that file is running or has run at the same time (a child workflow defined in the same file does not count), so a primitive thrown outside a `ctx` call on Bun is still fatal, and an error that either of two overlapping runs of one file could have thrown ends the process as before instead of failing a run that may be healthy. This works the same in the bundled install as in a source checkout. A rejection that belongs to no workflow run, including one raised by Atomic's own code, ends the process as before, with its original error and stack, and `--unhandled-rejections=warn|none|warn-with-error-code` are honored ([#3846](https://github.com/bastani-inc/atomic/issues/3846))
|
|
52
|
+
- A workflow that omits `name` is now named after the file that defines it when Atomic runs from the bundled install, as it already was in a source checkout, instead of after the bundled extension file (`index.bundle`) ([#3846](https://github.com/bastani-inc/atomic/issues/3846))
|
|
53
|
+
- `/workflow status <runId>` no longer reports `durable checkpoint topology is malformed or incomplete` for a running or crashed run whose child `ctx.workflow` calls have started but not yet recorded a stage or tool of their own, such as a pool of concurrent children when the process was killed. The children now show as running, and a crashed run can be inspected and resumed. A run that fails while such children are still starting now cancels them first, so its status can be inspected as well ([#3846](https://github.com/bastani-inc/atomic/issues/3846))
|
|
54
|
+
- Fixed `structured_output` and `generateStructuredOutput` failing the whole request with `Structured output model is unavailable or not a chat or classifier model` when one explicit `model` or `fallbackModels` ID could not be resolved, for example a display name instead of a `provider/model` ID. The unresolvable ID is now skipped and recorded in `modelAttempts`, and the remaining candidates, including the current chat model, are still tried. The request fails with the original error only when no candidate resolves ([#3814](https://github.com/bastani-inc/atomic/issues/3814))
|
|
55
|
+
- Fixed concurrent extension `select`/`confirm`/`input`/`editor` dialogs in interactive mode: a second dialog replaced the first and the first promise never settled, and a dialog opened from an inline `custom()` component hid that component for good. Closing the visible dialog now shows the previous one again ([#11166](https://github.com/earendil-works/pi/issues/11166))
|
|
56
|
+
- Fixed pasting in the shell hanging after copying text in Atomic and suspending it with Ctrl+Z on Linux: the background `wl-copy`/`xclip`/`xsel` process that keeps serving the clipboard was suspended along with Atomic ([#11179](https://github.com/earendil-works/pi/issues/11179))
|
|
57
|
+
- Fixed codemode scripts failing immediately when both codemode workers are busy, for example with parallel codemode calls or in-process subagents. Scripts now wait up to 60 seconds for a free worker, and `ATOMIC_CODEMODE_MAX_WORKERS` (or `PI_CODEMODE_MAX_WORKERS`) raises the limit of two workers ([#11184](https://github.com/earendil-works/pi/issues/11184))
|
|
58
|
+
- Fixed `mcp.json` files that start with a UTF-8 BOM failing to parse, which skipped all their MCP servers and made `atomic mcp add`/`atomic mcp remove` fail ([#11191](https://github.com/earendil-works/pi/issues/11191))
|
|
59
|
+
- The default GitHub Copilot model is now GPT-6.1 Sol. The previous default, GPT-5.4, was deprecated by GitHub on 2026-10-19 and dropped from the model catalog, so a Copilot login had no usable default model. GPT-6.1 Sol is GitHub's suggested replacement.
|
|
60
|
+
- When extensions fail to start a session, the error now names each failing extension and its own error (`Extension startup failed: <extension path>: <error>`) instead of only `Extension startup failed`. The SDK exports the thrown `ExtensionStartupError`, whose `failures` lists the extension path, event, and message of each failure, together with `isExtensionStartupFailure()` and `isTransientExtensionStartupFailure()` ([#3807](https://github.com/bastani-inc/atomic/issues/3807)).
|
|
61
|
+
- Stored OAuth logins, such as `openai-1` or `anthropic-2`, no longer stop working with `OAuth refresh failed ... invalid_grant` under heavy load from many sessions, workflow stages, or subagents until you run `/login`. A session that is slow or briefly unresponsive while it refreshes a token keeps its claim on that refresh, so another session no longer reuses the replaced refresh token. A slow token refresh is no longer cut off at its timeout. Its result is saved when the response arrives, and other sessions wait for it instead of reusing the replaced token. A token refresh the provider completed is also saved when another session took over meanwhile. A session whose refresh is rejected uses a login that another session has just saved. A login the provider has revoked or expired still fails. The error now tells you to run `/login <provider>` for that provider and shows only the provider's error code and description, never tokens.
|
|
62
|
+
- Sessions waiting for another session's OAuth token refresh no longer fail with `Lock file is already being held` before that refresh finishes under heavy load. They now wait as long as a refresh can keep its claim, then use the rotated token. Cancelling the request still stops the wait.
|
|
63
|
+
- Kimi Code logins no longer break with `invalid_grant` when a token refresh takes longer than 30 seconds or loses its connection mid-request. A revoked or expired OpenAI, Kimi Code, xAI, Meta or GitHub Copilot login now also tells you to run `/login <provider>`, for example on OpenAI's `refresh_token_expired` or `refresh_token_reused` errors.
|
|
64
|
+
- A stored OAuth login no longer breaks with `invalid_grant` after Atomic exits while a token refresh is still running, for example a subagent or `atomic -p` run ending after its request timed out, quitting an interactive session, `SIGTERM`, or `atomic auth`. Atomic now waits up to three seconds on exit for the provider's response and saves the rotated token. An `atomic -p` run no longer stays alive waiting for a token refresh that never answers. If Atomic is killed or crashes during a token refresh, run `/login <provider>`.
|
|
65
|
+
- An extension or `models.json` provider whose `$VAR` API key is unset now reports the missing key the way a built-in provider does wherever its models are used, for example `No API key found for <provider>` with the `/login` hint during compaction and branch summaries. Previously compaction, branch summaries, workflow stages, and subagents failed with `Failed to resolve API key ... from environment variable`.
|
|
66
|
+
- Extension OAuth providers whose refresh is rejected now get the built-in `run /login <provider>` guidance, and a newer login another session saved is still used first, when `refreshToken` throws an error carrying a code the built-in providers treat as a rejected login (`invalid_grant`, `refresh_token_expired`, `refresh_token_reused`, or `refresh_token_invalidated`) in its `error` or `code` property or in an OAuth error response (flat or nested under `error.code`) in `body`, `data`, `response.data`, or `response.body`, or HTTP status 401 in `status`, `statusCode`, `response.status`, or `response.statusCode`. Previously these showed the raw error message, which could include the request's tokens.
|
|
67
|
+
- In interactive sessions, `/login` lists extension providers that take an API key and logs in to them, `/logout` offers their stored keys, and `/login` shows an extension provider configured through an environment variable as configured instead of `not configured`.
|
|
68
|
+
- `/resume` search no longer keeps a session's old text after the session is rewritten with the same message count, first message, header and last-modified time, for example when an earlier message's text is replaced. Search now finds the new text and not the removed text, including when another Atomic process made the change.
|
|
69
|
+
- `structured_output` and `generateStructuredOutput` rejections now say what was wrong instead of the fixed `Invalid structured output: response does not match the decision schema.`. A schema mismatch lists up to five failing paths with the expected shape, such as `/files: expected array, got string` and `/summary: required`, followed by `and N more` when there are others, and never echoes the rejected values. When the provider's tool arguments were not valid JSON, the error is `Invalid structured output: tool call arguments were not valid JSON.` with the kind of syntax error and, when known, its position, without quoting the arguments, instead of a schema mismatch on `{}`. The same detail is sent to the model in the repair request and is kept in `modelAttempts` when a candidate model is exhausted ([#3802](https://github.com/bastani-inc/atomic/issues/3802))
|
|
70
|
+
- Shell `wait` and `kill` results now read as one status line above the task's output instead of a raw JSON envelope with internal ids: `✓ completed · exit 0`, `✗ exited 1`, `● still running · showing output so far`, or `○ cancelled`. A truncated result keeps the full-output path as a dim line, and `(no output)` is dimmed. Waiting on or killing an unknown or foreign task id now says `No background task "<id>" in this session` and points to `/tasks`, instead of `UnknownTask: UnknownTask`. The agent receives the same facts as one compact sentence (state, exit code, truncation, full-output path), the structured observation or cancellation receipt stays in the result `details`, and the failures thrown by `wait` and `kill` keep their code in a `code` property ([#3828](https://github.com/bastani-inc/atomic/issues/3828))
|
|
71
|
+
- Fixed a structured decision having no total time limit: a `structured_output` call, a `generateStructuredOutput()` call, or one routing inference for `model: "auto"` could run four slow generations back to back, plus retries, past every configured timeout, and held a workflow stage for over an hour. The whole decision is now stopped after the `httpIdleTimeoutMs` setting (10 minutes by default; disabled means effectively unbounded) and fails with `Structured output decision exceeded its <n>ms deadline; no decision was accepted.`, without trying another repair or fallback model ([#3806](https://github.com/bastani-inc/atomic/issues/3806))
|
|
72
|
+
- The error for exhausted `structured_output` and `generateStructuredOutput` repairs, and the new deadline error, now end with how each attempt finished and how long it took, such as `Attempts: 1) stopReason=stop 25m02s; 2) stopReason=length 1m30s.`, so one slow generation reads differently from several failed ones. Each provider request, including a transport retry, is listed on its own ([#3806](https://github.com/bastani-inc/atomic/issues/3806))
|
|
73
|
+
- Messages typed while the agent was busy are no longer dropped or left waiting for the end of the run. A message typed while a prompt was still being prepared, while Atomic waited to retry a failed request, just after you sent a prompt (before the screen showed that the turn had started), or while extensions were still finishing the previous run used to appear nowhere and be sent only after the run ended, or be refused with "Agent is already processing" and lost. It now shows as queued straight away and joins the run at its next turn boundary, in the order you typed it. If the agent refuses a message, the text comes back to the editor, or stays in the queue, together with the error.
|
|
74
|
+
- Queued messages are no longer left undelivered when the agent finishes before they are queued. The rest of a queued batch, such as the messages held during compaction, and a `steer` or `follow_up` sent to an idle agent whose session already has messages now start a turn instead of waiting for your next prompt.
|
|
75
|
+
- Near-neutral backgrounds such as message and selection backgrounds no longer turn too bright or blue in 256-color terminals: they now use the nearest color-cube or grayscale entry. Tool status backgrounds keep their blue, green, or red tint, so pending, successful, and failed tool blocks stay distinguishable ([#2550](https://github.com/bastani-inc/atomic/issues/2550))
|
|
76
|
+
- Fixed resumed sessions and `/tree` navigation leaving extension tools inactive when the branch recorded its tools before they were registered, such as tools from extensions installed after the session started. Extension tools the branch never added or removed are now active unless they are registered with `defaultActive: false` ([#11062](https://github.com/earendil-works/pi/issues/11062), [#11075](https://github.com/earendil-works/pi/issues/11075))
|
|
77
|
+
|
|
78
|
+
## [0.9.41-alpha.3] - 2026-10-22
|
|
79
|
+
|
|
80
|
+
Usage totals now say whether their cost was billed by the provider or estimated.
|
|
81
|
+
|
|
82
|
+
- **Provider-billed cost flag:** `usage.cost.totalFromProvider` is `true` only when every summed part, including nested tool-call and compaction totals, carries a provider-reported billed cost, and `false` for catalog estimates and older sessions ([#3852](https://github.com/bastani-inc/atomic/pull/3852))
|
|
83
|
+
|
|
84
|
+
### Added
|
|
85
|
+
|
|
86
|
+
- Added `usage.cost.totalFromProvider` to usage totals. Summed usage, including nested tool-call and compaction totals, reports `true` only when every part was a provider-reported billed cost; catalog-estimated and legacy sessions without the field report `false`.
|
|
87
|
+
|
|
88
|
+
### Changed
|
|
89
|
+
|
|
90
|
+
- `install.sh` and `install.ps1` no longer re-download Atomic when the requested release (or the latest release, when none is requested) is already installed and healthy. They print `atomic <tag> is already installed; skipping download`, keep the `Installed to` line and PATH guidance, and download nothing; on Windows a rerun also restores a missing User PATH entry. A different version, or a missing or broken launcher, `current` pointer (including one that points at a versions directory outside the install root), shim, or payload (including a missing `app.js` or other startup file), installs and repairs as before. Pass `--force` (`-Force` on Windows) or set `ATOMIC_FORCE_INSTALL=1` to reinstall anyway.
|
|
91
|
+
|
|
92
|
+
## [0.9.41-alpha.2] - 2026-10-22
|
|
93
|
+
|
|
94
|
+
Workflows that drop a promise stop crashing Atomic, and classifiers can read images.
|
|
95
|
+
|
|
96
|
+
- **Workflow failures stay inside their run:** an unhandled `ctx` rejection, a stray `ctx.exit()`, or a branch that outlives a kill or quit now fails or ends only its own run instead of the whole process, and `/workflow status` can inspect runs whose child workflows were still starting ([#3846](https://github.com/bastani-inc/atomic/issues/3846), [#3849](https://github.com/bastani-inc/atomic/pull/3849))
|
|
97
|
+
- **Image input for more classifiers:** `models.classify()` accepts `images` for Jev and Clef on Cloudflare Workers AI and OpenRouter, with corrected Workers AI pricing and `compat` settings for proxies ([#3847](https://github.com/bastani-inc/atomic/pull/3847))
|
|
98
|
+
- **Terminal and rendering fixes:** Rex terminal support, terminal modes restored on unexpected exit, Markdown tables after unclosed `$$` blocks, CJK emphasis, `@` autocomplete with regex characters, and a `grep` tool that ignores your ripgrep config ([#3847](https://github.com/bastani-inc/atomic/pull/3847))
|
|
99
|
+
|
|
100
|
+
### Added
|
|
101
|
+
|
|
102
|
+
- Codemode `models.classify()` now also accepts `images` for TypeSafe's Jev and Cloudflare's Clef models on `cloudflare-workers-ai` and OpenRouter, next to Jev on `typesafe`, and lists Clef Omni and Jev 1.14 on Workers AI. See [Use classifier models](docs/models.md#use-classifier-models). Provider-level `compat` in `models.json` now also applies to classifier models, with the System One fields `imageInput` and `sendImageOptInHeader` for proxies that serve another service's classifiers. Extension classifier models accept `compat` too. See [Models Configuration](docs/models-configuration.md#typesafe-system-one).
|
|
103
|
+
- Added support for the Rex terminal (`TERM_PROGRAM=rex`): OSC 8 hyperlinks, truecolor, and inline images. Links previously fell back to the `text (url)` format ([#11131](https://github.com/earendil-works/pi/issues/11131), [#11150](https://github.com/earendil-works/pi/pull/11150) by [@petrroll](https://github.com/petrroll), [#11183](https://github.com/earendil-works/pi/pull/11183) by [@nihaals](https://github.com/nihaals))
|
|
104
|
+
|
|
105
|
+
### Fixed
|
|
106
|
+
|
|
107
|
+
- Fixed Cloudflare Workers AI classifier pricing: Clef Flash costs $0.038 and Jev $0.042 per million input tokens instead of Jev being listed as free.
|
|
108
|
+
- Fixed the `grep` tool following ripgrep's `RIPGREP_CONFIG_PATH` whenever it searches with `rg`, such as with `literal: true`, where config options like `--ignore-case` silently changed what the tool returned. The tool now runs `rg --no-config` ([#11203](https://github.com/earendil-works/pi/issues/11203))
|
|
109
|
+
- Fixed `@` file autocomplete returning no results for queries containing regex metacharacters such as `[` or `(` ([#10972](https://github.com/earendil-works/pi/issues/10972))
|
|
110
|
+
- Fixed an unclosed `$$` display-math block swallowing or corrupting every Markdown table after it ([#11159](https://github.com/earendil-works/pi/issues/11159))
|
|
111
|
+
- Fixed terminal modes (mouse tracking, focus reporting, Kitty keyboard protocol, bracketed paste, alternate screen) staying enabled when the process exits without calling `stop()`. A process `exit` handler now restores them synchronously ([#11170](https://github.com/earendil-works/pi/issues/11170))
|
|
112
|
+
- Fixed Markdown bold and italic not rendering next to CJK punctuation ([#10730](https://github.com/earendil-works/pi/pull/10730) by [@theBucky](https://github.com/theBucky))
|
|
113
|
+
- A rejection that workflow code leaves unhandled no longer ends the whole Atomic process. This covers a rejected `ctx.tool`, `ctx.task`, `ctx.chain`, `ctx.parallel`, `ctx.workflow`, `ctx.ui` prompt (`input`, `confirm`, `select`, `editor` or `custom`) or stage method call (`prompt`, `complete`, `steer`, `sendUserMessage`, `setModel`, `compact` and the other promise-returning stage methods), including one that is started and never awaited (`void ctx.task(...)` used to leave the run `completed` with no sign of the failure), a `ctx.tool` call made after its run has stopped (`ctx.tool admission is closed for this run`), a string, number or `undefined` thrown from a `ctx.tool` callback, and an error the workflow file throws or receives from `fs` itself, for example in a rolling pool that keeps `branch.finally(...)` as a separate promise. It also covers `ctx.exit()` called inside such a branch, which ends the run as exited with the status and outputs it asked for (on Bun the process used to end right after the run reported `completed`), and a child workflow's own branch that uses `ctx` after its parent exited. Stopping a run no longer ends the process either: a branch that keeps running after `/workflow quit`, a kill, or an abort signal from the code that started the run, and then calls `ctx.stage`, `ctx.task`, `ctx.workflow` or `ctx.exit()`, or that was waiting on a `ctx.ui` prompt when the run was killed, could end it with `workflow killed` (a kill), the quit error (`/workflow quit`), an `AbortError` or `atomic-workflows: HIL aborted` right after the run reported `killed` or `paused`, and the run keeps that status. `ctx.exit()` called in such a branch while the run is paused ends the run as exited once the run resumes, and a kill of the paused run still ends it as `killed` (on Node it used to end as `failed` with `Workflow exit is waiting for explicit resume`, and on Bun the process ended). The rejection fails its own run when it surfaces before that run decides its final status, so other runs, stages, and the interactive engine keep going; a `ctx.task`, `ctx.chain`, `ctx.parallel`, `ctx.workflow`, `ctx.ui` or stage method rejection that the workflow awaits or catches reaches that code unchanged and does not fail the run. Once the run has ended, an error that a `ctx` call rejected with, what `ctx.exit()` threw and the reason the run was stopped with are ignored (the reason of a kill is the string `workflow killed`, which is ignored by value for the rest of the process, so a branch that wakes up long after the run ended is covered too), and so is an error thrown by the workflow file for the next 30 seconds, so the remaining branches of a failing pool cannot end the process; a later one ends it as before. On Node the owning run is identified exactly; on Bun a `ctx` call's rejection, what `ctx.exit()` threw and the reason the run was stopped with are matched by identity (a `ctx` call's string, number or `undefined` only within the event-loop turn in which the call rejected, and the string a kill uses by value), and an error thrown by the workflow file itself is matched through its stack to the run of the file that calls `workflow()`, but only when no other run of that file is running or has run at the same time (a child workflow defined in the same file does not count), so a primitive thrown outside a `ctx` call on Bun is still fatal, and an error that either of two overlapping runs of one file could have thrown ends the process as before instead of failing a run that may be healthy. This works the same in the bundled install as in a source checkout. A rejection that belongs to no workflow run, including one raised by Atomic's own code, ends the process as before, with its original error and stack, and `--unhandled-rejections=warn|none|warn-with-error-code` are honored ([#3846](https://github.com/bastani-inc/atomic/issues/3846))
|
|
114
|
+
- A workflow that omits `name` is now named after the file that defines it when Atomic runs from the bundled install, as it already was in a source checkout, instead of after the bundled extension file (`index.bundle`) ([#3846](https://github.com/bastani-inc/atomic/issues/3846))
|
|
115
|
+
- `/workflow status <runId>` no longer reports `durable checkpoint topology is malformed or incomplete` for a running or crashed run whose child `ctx.workflow` calls have started but not yet recorded a stage or tool of their own, such as a pool of concurrent children when the process was killed. The children now show as running, and a crashed run can be inspected and resumed. A run that fails while such children are still starting now cancels them first, so its status can be inspected as well ([#3846](https://github.com/bastani-inc/atomic/issues/3846))
|
|
116
|
+
|
|
5
117
|
## [0.9.41-alpha.1] - 2026-10-21
|
|
6
118
|
|
|
7
119
|
### Breaking Changes
|
|
@@ -4,6 +4,18 @@ All notable changes to the `pi-intercom` extension will be documented in this fi
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.9.41] - 2026-10-22
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Workflow route updates cost the Intercom broker far less when many runs are active. The broker now acknowledges each route registration itself instead of the session listing the previous version requested after every update, and each run's route connection retries on its own backoff, with limited concurrency, instead of all of them reconnecting at once. A workflow stage that starts while its run's route connection is waiting to retry waits for that retry rather than failing, and a retry that fails is retried by the stage like any other connection timeout. Mixed versions keep working: with an older broker the session listing is still used, and older sessions work with the new broker.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- `intercom list` with a `group` that names a workflow invocation group you are not a member of no longer reports `No sessions or workflow stages in this group.` The group is visible only to its members, so the peek now says that and tells you to `intercom join` it to see its stages, instead of looking like a run whose stages had all disappeared.
|
|
16
|
+
- Fixed `Intercom event relay failed (atomic:workflow-pending-stage-route): Connection timeout` repeating for every route update when 50 or more workflows ran at once. A broker that is slow to answer a connection is now a recoverable condition that background relays retry quietly. A broker that stays unresponsive for a minute is reported once, and Intercom tool calls you make still report the failure.
|
|
17
|
+
- Fixed a single workflow run's route connection failing to connect and dropping the session's healthy Intercom connection, which re-created every run's connection at the same time.
|
|
18
|
+
|
|
7
19
|
## [0.9.41-alpha.1] - 2026-10-21
|
|
8
20
|
|
|
9
21
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/intercom",
|
|
3
|
-
"version": "0.9.41
|
|
3
|
+
"version": "0.9.41",
|
|
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": [
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"@bastani/atomic": "*",
|
|
47
|
-
"@earendil-works/pi-tui": "^1.
|
|
47
|
+
"@earendil-works/pi-tui": "^1.4.0",
|
|
48
48
|
"typebox": "*"
|
|
49
49
|
},
|
|
50
50
|
"peerDependenciesMeta": {
|
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.9.41] - 2026-10-22
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added a `resumeInFlight` subagent setting (`"never"`, `"ask"`, or `"auto"`, default `"never"`) for recovery of paused and quit subagents that are awaiting resume. `ask` confirms once, listing each subagent, and `auto` resumes each from its own session, with its completion reaching the session that launched it; `never` leaves them for a manual `resume`. It applies when Atomic starts or a session is resumed, to that session's own subagents, never to workflow-stage children, and an invalid value is reported and treated as `never` ([#3827](https://github.com/bastani-inc/atomic/issues/3827)).
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Quitting a subagent now behaves like quitting a workflow. The `quit` and `pause` results say the child is paused · awaiting resume and name `{ action: "resume", id: "<child-id>" }`; the parent's completion notice says the same, with the typed `quit` or `pause` stop reason instead of `parent-handoff` and without "This execution has ended". `{ action: "status", id }` reports a paused or quit child as paused · awaiting resume with its resume call, and `{ action: "status" }` lists every such child, including after Atomic restarts. Launch cards of such children show `‖ Paused · awaiting resume` (or `‖ Quit · paused · awaiting resume`) instead of `(resumable)`.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- A subagent with `model: "auto"` no longer waits indefinitely on a slow routing model. Each routing inference now stops after the `httpIdleTimeoutMs` deadline (10 minutes by default), and the subagent runs on the current chat model when it satisfies the routing constraints ([#3806](https://github.com/bastani-inc/atomic/issues/3806))
|
|
18
|
+
|
|
5
19
|
## [0.9.41-alpha.1] - 2026-10-21
|
|
6
20
|
|
|
7
21
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/subagents",
|
|
3
|
-
"version": "0.9.41
|
|
3
|
+
"version": "0.9.41",
|
|
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": [
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"@bastani/atomic": "*",
|
|
37
37
|
"@bastani/pi-ai": "*",
|
|
38
|
-
"@earendil-works/pi-agent-core": "^1.
|
|
39
|
-
"@earendil-works/pi-tui": "^1.
|
|
38
|
+
"@earendil-works/pi-agent-core": "^1.4.0",
|
|
39
|
+
"@earendil-works/pi-tui": "^1.4.0",
|
|
40
40
|
"typebox": "*"
|
|
41
41
|
},
|
|
42
42
|
"peerDependenciesMeta": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/web-access",
|
|
3
|
-
"version": "0.9.41
|
|
3
|
+
"version": "0.9.41",
|
|
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": [
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"@bastani/atomic": "*",
|
|
34
|
-
"@earendil-works/pi-tui": "^1.
|
|
34
|
+
"@earendil-works/pi-tui": "^1.4.0"
|
|
35
35
|
},
|
|
36
36
|
"peerDependenciesMeta": {
|
|
37
37
|
"@bastani/atomic": {
|
|
@@ -6,6 +6,42 @@ 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.41] - 2026-10-22
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Added `schemaTimeoutMs` to workflow stage, task, chain, and parallel options: the wall-clock limit in milliseconds for each `structured_output` decision of a schema-backed item, covering its repair attempts, retries, and model fallbacks together. It defaults to the `httpIdleTimeoutMs` setting ([#3806](https://github.com/bastani-inc/atomic/issues/3806))
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Reopening `/workflow resume` is faster: the run list is kept between opens and rereads only the runs whose journal changed since the last open, including changes made by other Atomic processes. Whether another session still owns a run is checked when you select it.
|
|
18
|
+
- `/workflow connect` and `/workflow status` now list runs the way `/tasks` does. Active runs come before ended ones, with runs waiting for your input or blocked first, then running, then paused; ended runs follow with failed, cancelled, then completed. Within each group the newest run comes first, where before the `/workflow connect` list showed the oldest running run first ([#3824](https://github.com/bastani-inc/atomic/issues/3824)).
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- A rejection that workflow code leaves unhandled no longer ends the whole Atomic process. This covers a rejected `ctx.tool`, `ctx.task`, `ctx.chain`, `ctx.parallel`, `ctx.workflow`, `ctx.ui` prompt (`input`, `confirm`, `select`, `editor` or `custom`) or stage method call (`prompt`, `complete`, `steer`, `sendUserMessage`, `setModel`, `compact` and the other promise-returning stage methods), including one that is started and never awaited (`void ctx.task(...)` used to leave the run `completed` with no sign of the failure), a `ctx.tool` call made after its run has stopped (`ctx.tool admission is closed for this run`), a string, number or `undefined` thrown from a `ctx.tool` callback, and an error the workflow file throws or receives from `fs` itself, for example in a rolling pool that keeps `branch.finally(...)` as a separate promise. It also covers `ctx.exit()` called inside such a branch, which ends the run as exited with the status and outputs it asked for (on Bun the process used to end right after the run reported `completed`), and a child workflow's own branch that uses `ctx` after its parent exited. Stopping a run no longer ends the process either: a branch that keeps running after `/workflow quit`, a kill, or an abort signal from the code that started the run, and then calls `ctx.stage`, `ctx.task`, `ctx.workflow` or `ctx.exit()`, or that was waiting on a `ctx.ui` prompt when the run was killed, could end it with `workflow killed` (a kill), the quit error (`/workflow quit`), an `AbortError` or `atomic-workflows: HIL aborted` right after the run reported `killed` or `paused`, and the run keeps that status. `ctx.exit()` called in such a branch while the run is paused ends the run as exited once the run resumes, and a kill of the paused run still ends it as `killed` (on Node it used to end as `failed` with `Workflow exit is waiting for explicit resume`, and on Bun the process ended). The rejection fails its own run when it surfaces before that run decides its final status, so other runs, stages, and the interactive engine keep going; a `ctx.task`, `ctx.chain`, `ctx.parallel`, `ctx.workflow`, `ctx.ui` or stage method rejection that the workflow awaits or catches reaches that code unchanged and does not fail the run. Once the run has ended, an error that a `ctx` call rejected with, what `ctx.exit()` threw and the reason the run was stopped with are ignored (the reason of a kill is the string `workflow killed`, which is ignored by value for the rest of the process, so a branch that wakes up long after the run ended is covered too), and so is an error thrown by the workflow file for the next 30 seconds, so the remaining branches of a failing pool cannot end the process; a later one ends it as before. On Node the owning run is identified exactly; on Bun a `ctx` call's rejection, what `ctx.exit()` threw and the reason the run was stopped with are matched by identity (a `ctx` call's string, number or `undefined` only within the event-loop turn in which the call rejected, and the string a kill uses by value), and an error thrown by the workflow file itself is matched through its stack to the run of the file that calls `workflow()`, but only when no other run of that file is running or has run at the same time (a child workflow defined in the same file does not count), so a primitive thrown outside a `ctx` call on Bun is still fatal, and an error that either of two overlapping runs of one file could have thrown ends the process as before instead of failing a run that may be healthy. This works the same in the bundled install as in a source checkout. A rejection that belongs to no workflow run, including one raised by Atomic's own code, ends the process as before, with its original error and stack, and `--unhandled-rejections=warn|none|warn-with-error-code` are honored ([#3846](https://github.com/bastani-inc/atomic/issues/3846))
|
|
23
|
+
- A workflow that omits `name` is now named after the file that defines it when Atomic runs from the bundled install, as it already was in a source checkout, instead of after the bundled extension file (`index.bundle`) ([#3846](https://github.com/bastani-inc/atomic/issues/3846))
|
|
24
|
+
- `/workflow status <runId>` no longer reports `durable checkpoint topology is malformed or incomplete` for a running or crashed run whose child `ctx.workflow` calls have started but not yet recorded a stage or tool of their own, such as a pool of concurrent children when the process was killed. The children now show as running, and a crashed run can be inspected and resumed. A run that fails while such children are still starting now cancels them first, so its status can be inspected as well ([#3846](https://github.com/bastani-inc/atomic/issues/3846))
|
|
25
|
+
- A workflow stage no longer stays stuck selecting its model when many workflows run at once. A stage with `model: "auto"` now waits at most five minutes for model selection, counting any wait for one of eight routing slots shared by every stage, so a burst of stages queues instead of overloading the router provider. When the wait ends, the stage runs on the current chat model if that model satisfies every routing constraint; otherwise it fails with an error that names the stage and the `model-resolution` startup phase, and its startup status reads `failed` instead of staying active. A failed selection is no longer remembered, so the next prompt on the same stage selects a model again, without launching a replacement workflow.
|
|
26
|
+
- After a blocked workflow run is resumed, Intercom sends to `workflow:<rootRunId>/**`, to stage patterns, and to stages that have not started yet are accepted and delivered again instead of failing with `Workflow execution for <runId> is retired`.
|
|
27
|
+
- In a long-running session, starting a workflow from a turn that an Intercom message started no longer fails with `Workflow execution for <runId> is retired; startup cleanup failed: …` after an earlier workflow run in the session has ended or been quit. The session's Intercom connection is now opened outside the workflow run that first needed it. The `is retired` error now names the run whose execution actually ended, not the run being started.
|
|
28
|
+
- A workflow stage whose fallback session fails to start because an extension's `session_start` failed under load, for example Intercom's `Broker failed to start within timeout` or `connect ECONNREFUSED`, now retries that session with the same-model retry budget and then moves on to its next fallback model, instead of failing the stage and letting fail-fast skip its sibling stages. Any other extension startup error still fails the stage at once. The stage error, model attempts, and fallback warnings now name each failing extension and its error rather than only `Extension startup failed`.
|
|
29
|
+
- When a workflow stage falls back to another model and that fallback session cannot start, the stage now closes the session it was replacing. Before, that session kept its session database open after the stage failed, and on Windows the file could not be deleted until the process exited.
|
|
30
|
+
- Workflow stage sessions now provide `ctx.ui.theme` and the other default UI members, so extensions that read `ctx.ui.theme.fg(...)` in `session_start`, such as `examples/extensions/status-line.ts`, no longer fail stage startup with `Extension startup failed` ([#3801](https://github.com/bastani-inc/atomic/issues/3801))
|
|
31
|
+
- Workflow inputs now reject integer and number values outside the `minimum`/`maximum` declared in the input schema, such as `count: 0` for `Type.Integer({ minimum: 1, maximum: 8 })`, when the run is started, instead of passing them to the workflow body. The error names the input and the violated bound ([#3812](https://github.com/bastani-inc/atomic/issues/3812)).
|
|
32
|
+
- A `ctx.parallel(..., { failFast: false })` call that has a failing step no longer loses the results of the steps that succeeded. The `AggregateError` it rejects with now carries `partialResults`, one entry per step in step order, holding each completed step's result and `undefined` for a step that failed. The `WorkflowParallelFailure` type describes it ([#3809](https://github.com/bastani-inc/atomic/issues/3809))
|
|
33
|
+
- A workflow stage's corrective prompt after a rejected `structured_output` result now names the failing paths and expected types, or reports that the tool arguments were not valid JSON, instead of only saying the response does not match the decision schema ([#3802](https://github.com/bastani-inc/atomic/issues/3802))
|
|
34
|
+
- Disposing a session right after a workflow run reports that it ended no longer leaves the run `running` in the workflow journal, with its lease released, for readers to report as crashed. `session.dispose()` now waits up to 10 seconds for runs that have ended but are still recording their ending, before it closes the journal, and it rejects naming the run when that ending still cannot be recorded. A run whose journal rejects the write of its `completed` status now retries it once, and reports it with an error notice and a rejected `waitForSettled` when it still fails, instead of failing in silence ([#3811](https://github.com/bastani-inc/atomic/issues/3811))
|
|
35
|
+
- A schema-backed workflow stage no longer stays `running` for hours inside one `structured_output` call when a slow model keeps generating without calling the tool. The decision now stops after the `httpIdleTimeoutMs` deadline (10 minutes by default) or the stage's `schemaTimeoutMs`, and the error that names the deadline and each attempt's duration reaches the model and the failed stage unchanged instead of being treated as a structured-output contract failure ([#3806](https://github.com/bastani-inc/atomic/issues/3806))
|
|
36
|
+
|
|
37
|
+
## [0.9.41-alpha.2] - 2026-10-22
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- A rejection that workflow code leaves unhandled no longer ends the whole Atomic process. This covers a rejected `ctx.tool`, `ctx.task`, `ctx.chain`, `ctx.parallel`, `ctx.workflow`, `ctx.ui` prompt (`input`, `confirm`, `select`, `editor` or `custom`) or stage method call (`prompt`, `complete`, `steer`, `sendUserMessage`, `setModel`, `compact` and the other promise-returning stage methods), including one that is started and never awaited (`void ctx.task(...)` used to leave the run `completed` with no sign of the failure), a `ctx.tool` call made after its run has stopped (`ctx.tool admission is closed for this run`), a string, number or `undefined` thrown from a `ctx.tool` callback, and an error the workflow file throws or receives from `fs` itself, for example in a rolling pool that keeps `branch.finally(...)` as a separate promise. It also covers `ctx.exit()` called inside such a branch, which ends the run as exited with the status and outputs it asked for (on Bun the process used to end right after the run reported `completed`), and a child workflow's own branch that uses `ctx` after its parent exited. Stopping a run no longer ends the process either: a branch that keeps running after `/workflow quit`, a kill, or an abort signal from the code that started the run, and then calls `ctx.stage`, `ctx.task`, `ctx.workflow` or `ctx.exit()`, or that was waiting on a `ctx.ui` prompt when the run was killed, could end it with `workflow killed` (a kill), the quit error (`/workflow quit`), an `AbortError` or `atomic-workflows: HIL aborted` right after the run reported `killed` or `paused`, and the run keeps that status. `ctx.exit()` called in such a branch while the run is paused ends the run as exited once the run resumes, and a kill of the paused run still ends it as `killed` (on Node it used to end as `failed` with `Workflow exit is waiting for explicit resume`, and on Bun the process ended). The rejection fails its own run when it surfaces before that run decides its final status, so other runs, stages, and the interactive engine keep going; a `ctx.task`, `ctx.chain`, `ctx.parallel`, `ctx.workflow`, `ctx.ui` or stage method rejection that the workflow awaits or catches reaches that code unchanged and does not fail the run. Once the run has ended, an error that a `ctx` call rejected with, what `ctx.exit()` threw and the reason the run was stopped with are ignored (the reason of a kill is the string `workflow killed`, which is ignored by value for the rest of the process, so a branch that wakes up long after the run ended is covered too), and so is an error thrown by the workflow file for the next 30 seconds, so the remaining branches of a failing pool cannot end the process; a later one ends it as before. On Node the owning run is identified exactly; on Bun a `ctx` call's rejection, what `ctx.exit()` threw and the reason the run was stopped with are matched by identity (a `ctx` call's string, number or `undefined` only within the event-loop turn in which the call rejected, and the string a kill uses by value), and an error thrown by the workflow file itself is matched through its stack to the run of the file that calls `workflow()`, but only when no other run of that file is running or has run at the same time (a child workflow defined in the same file does not count), so a primitive thrown outside a `ctx` call on Bun is still fatal, and an error that either of two overlapping runs of one file could have thrown ends the process as before instead of failing a run that may be healthy. This works the same in the bundled install as in a source checkout. A rejection that belongs to no workflow run, including one raised by Atomic's own code, ends the process as before, with its original error and stack, and `--unhandled-rejections=warn|none|warn-with-error-code` are honored ([#3846](https://github.com/bastani-inc/atomic/issues/3846))
|
|
42
|
+
- A workflow that omits `name` is now named after the file that defines it when Atomic runs from the bundled install, as it already was in a source checkout, instead of after the bundled extension file (`index.bundle`) ([#3846](https://github.com/bastani-inc/atomic/issues/3846))
|
|
43
|
+
- `/workflow status <runId>` no longer reports `durable checkpoint topology is malformed or incomplete` for a running or crashed run whose child `ctx.workflow` calls have started but not yet recorded a stage or tool of their own, such as a pool of concurrent children when the process was killed. The children now show as running, and a crashed run can be inspected and resumed. A run that fails while such children are still starting now cancels them first, so its status can be inspected as well ([#3846](https://github.com/bastani-inc/atomic/issues/3846))
|
|
44
|
+
|
|
9
45
|
## [0.9.41-alpha.1] - 2026-10-21
|
|
10
46
|
|
|
11
47
|
### Added
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
adversarial_verification_default
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-apagcs4r.js";
|
|
4
4
|
import"./chunk-2377r57g.js";
|
|
5
5
|
import"./chunk-n1910xc4.js";
|
|
6
|
-
import"./chunk-
|
|
6
|
+
import"./chunk-2jfst7e4.js";
|
|
7
7
|
import"./chunk-9cm2ckq2.js";
|
|
8
8
|
export {
|
|
9
9
|
adversarial_verification_default as default
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
|
|
5
5
|
// dist/builtin/workflows/src/authoring/workflow.ts
|
|
6
6
|
import { basename } from "node:path";
|
|
7
|
-
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
8
8
|
|
|
9
9
|
// dist/builtin/workflows/src/shared/budget.ts
|
|
10
10
|
function validateWorkflowBudget(value, label = "budget") {
|
|
@@ -37,6 +37,59 @@ function assertWorkflowBudget(value, label = "budget") {
|
|
|
37
37
|
// dist/builtin/workflows/src/shared/workflow-heartbeat-contract.ts
|
|
38
38
|
var DEFAULT_WORKFLOW_HEARTBEAT_INTERVAL_MINUTES = 15;
|
|
39
39
|
|
|
40
|
+
// dist/builtin/workflows/src/runs/shared/prompt-callsite.ts
|
|
41
|
+
import { fileURLToPath } from "node:url";
|
|
42
|
+
var PACKAGED_WORKFLOW_RUNTIME_ROOTS = ["/dist/builtin/workflows/src/"];
|
|
43
|
+
function normalizeSlashes(path) {
|
|
44
|
+
return path.replace(/\\/g, "/");
|
|
45
|
+
}
|
|
46
|
+
function normalizeStackPath(framePath) {
|
|
47
|
+
let normalized = framePath;
|
|
48
|
+
if (normalized.startsWith("file://")) {
|
|
49
|
+
try {
|
|
50
|
+
normalized = fileURLToPath(normalized);
|
|
51
|
+
} catch {
|
|
52
|
+
try {
|
|
53
|
+
normalized = decodeURIComponent(new URL(normalized).pathname);
|
|
54
|
+
} catch {}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
normalized = normalizeSlashes(normalized).replace(/^\/([A-Za-z]:\/)/, "$1");
|
|
58
|
+
return normalized;
|
|
59
|
+
}
|
|
60
|
+
function currentModuleRuntimeRoot() {
|
|
61
|
+
const modulePath = normalizeStackPath(fileURLToPath(import.meta.url));
|
|
62
|
+
const markers = ["packages/workflows/src/", ...PACKAGED_WORKFLOW_RUNTIME_ROOTS.map((root) => root.slice(1))];
|
|
63
|
+
for (const marker of markers) {
|
|
64
|
+
const index = modulePath.indexOf(marker);
|
|
65
|
+
if (index >= 0)
|
|
66
|
+
return modulePath.slice(0, index + marker.length);
|
|
67
|
+
}
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
var CURRENT_WORKFLOW_RUNTIME_ROOT = currentModuleRuntimeRoot();
|
|
71
|
+
function isWorkflowRuntimeFrame(normalizedPath) {
|
|
72
|
+
const path = normalizeSlashes(normalizedPath);
|
|
73
|
+
if (path.startsWith("packages/workflows/src/"))
|
|
74
|
+
return true;
|
|
75
|
+
if (CURRENT_WORKFLOW_RUNTIME_ROOT !== undefined && path.startsWith(CURRENT_WORKFLOW_RUNTIME_ROOT)) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
const comparable = path.startsWith("/") ? path : `/${path}`;
|
|
79
|
+
return PACKAGED_WORKFLOW_RUNTIME_ROOTS.some((runtimeRoot) => comparable.includes(runtimeRoot));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// dist/builtin/workflows/src/shared/workflow-source.ts
|
|
83
|
+
var WORKFLOW_SOURCES = new WeakMap;
|
|
84
|
+
var OWN_MODULE_PATH = normalizeStackPath(import.meta.url);
|
|
85
|
+
function isWorkflowRuntimeModule(path) {
|
|
86
|
+
const normalized = normalizeStackPath(path);
|
|
87
|
+
return normalized === OWN_MODULE_PATH || isWorkflowRuntimeFrame(normalized);
|
|
88
|
+
}
|
|
89
|
+
function recordWorkflowSource(definition, path) {
|
|
90
|
+
WORKFLOW_SOURCES.set(definition, path);
|
|
91
|
+
}
|
|
92
|
+
|
|
40
93
|
// dist/builtin/workflows/src/workflows/identity.ts
|
|
41
94
|
function normalizeWorkflowName(name) {
|
|
42
95
|
if (!name || typeof name !== "string") {
|
|
@@ -67,7 +120,7 @@ function stackFilePath(line) {
|
|
|
67
120
|
if (!rawPath.startsWith("file://"))
|
|
68
121
|
return rawPath;
|
|
69
122
|
try {
|
|
70
|
-
return
|
|
123
|
+
return fileURLToPath2(rawPath);
|
|
71
124
|
} catch {
|
|
72
125
|
return;
|
|
73
126
|
}
|
|
@@ -78,8 +131,10 @@ function isWorkflowAuthoringImplementationFrame(filePath) {
|
|
|
78
131
|
return false;
|
|
79
132
|
return normalized.includes("/packages/workflows/") || normalized.includes("/dist/builtin/workflows/") || normalized.includes("/.atomic/agent/extensions/workflows/");
|
|
80
133
|
}
|
|
81
|
-
function
|
|
82
|
-
const
|
|
134
|
+
function callerSourcePath(entry) {
|
|
135
|
+
const capture = new Error;
|
|
136
|
+
Error.captureStackTrace(capture, entry);
|
|
137
|
+
const stack = capture.stack;
|
|
83
138
|
if (stack === undefined)
|
|
84
139
|
return;
|
|
85
140
|
for (const line of stack.split(`
|
|
@@ -89,14 +144,18 @@ function workflowNameFromCaller() {
|
|
|
89
144
|
continue;
|
|
90
145
|
if (isWorkflowAuthoringImplementationFrame(filePath))
|
|
91
146
|
continue;
|
|
92
|
-
|
|
93
|
-
if (base.length > 0)
|
|
94
|
-
return base;
|
|
147
|
+
return isWorkflowRuntimeModule(filePath) ? undefined : filePath;
|
|
95
148
|
}
|
|
96
149
|
return;
|
|
97
150
|
}
|
|
98
|
-
function
|
|
99
|
-
|
|
151
|
+
function workflowNameFromSourcePath(filePath) {
|
|
152
|
+
if (filePath === undefined)
|
|
153
|
+
return;
|
|
154
|
+
const base = basename(filePath.replace(/\\/g, "/")).replace(/\.[cm]?[jt]sx?$/, "");
|
|
155
|
+
return base.length > 0 ? base : undefined;
|
|
156
|
+
}
|
|
157
|
+
function resolveWorkflowName(name, sourcePath) {
|
|
158
|
+
const resolved = name ?? workflowNameFromSourcePath(sourcePath);
|
|
100
159
|
if (resolved === undefined) {
|
|
101
160
|
throw new TypeError("workflow: name must be provided when caller filename cannot be inferred");
|
|
102
161
|
}
|
|
@@ -132,7 +191,8 @@ function workflow(spec) {
|
|
|
132
191
|
throw new TypeError("workflow: heartbeatIntervalMinutes must be a non-negative finite number");
|
|
133
192
|
}
|
|
134
193
|
assertWorkflowBudget(spec.budget, "workflow: budget");
|
|
135
|
-
const
|
|
194
|
+
const source = callerSourcePath(workflow);
|
|
195
|
+
const name = resolveWorkflowName(spec.name, source);
|
|
136
196
|
const normalizedName = normalizeWorkflowName(name);
|
|
137
197
|
requireNonEmptyString(normalizedName, "normalized name");
|
|
138
198
|
const frozenInputs = freezeSchemaMap(spec.inputs ?? {});
|
|
@@ -154,6 +214,8 @@ function workflow(spec) {
|
|
|
154
214
|
run
|
|
155
215
|
};
|
|
156
216
|
const branded = stampWorkflowDefinition(definition);
|
|
217
|
+
if (source !== undefined)
|
|
218
|
+
recordWorkflowSource(branded, source);
|
|
157
219
|
return Object.freeze(branded);
|
|
158
220
|
}
|
|
159
221
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_ACTION_CATEGORIES2,
|
|
3
3
|
classify_and_act_default
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import"./chunk-
|
|
4
|
+
} from "./chunk-hx8v4p97.js";
|
|
5
|
+
import"./chunk-2jfst7e4.js";
|
|
6
6
|
import"./chunk-9cm2ckq2.js";
|
|
7
7
|
export {
|
|
8
8
|
DEFAULT_ACTION_CATEGORIES2 as DEFAULT_ACTION_CATEGORIES,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
generate_and_filter_default
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-z3pajrfs.js";
|
|
4
4
|
import"./chunk-2377r57g.js";
|
|
5
5
|
import"./chunk-n1910xc4.js";
|
|
6
|
-
import"./chunk-
|
|
6
|
+
import"./chunk-2jfst7e4.js";
|
|
7
7
|
import"./chunk-9cm2ckq2.js";
|
|
8
8
|
export {
|
|
9
9
|
generate_and_filter_default as default
|