@bastani/atomic 0.9.44-alpha.7 → 0.9.45-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +18 -0
  3. package/dist/builtin/intercom/package.json +1 -1
  4. package/dist/builtin/mcp/CHANGELOG.md +7 -0
  5. package/dist/builtin/mcp/package.json +1 -1
  6. package/dist/builtin/subagents/CHANGELOG.md +9 -0
  7. package/dist/builtin/subagents/package.json +1 -1
  8. package/dist/builtin/web-access/package.json +1 -1
  9. package/dist/builtin/workflows/CHANGELOG.md +42 -0
  10. package/dist/builtin/workflows/package.json +1 -1
  11. package/dist/builtin/workflows/src/durable/journal/internal.d.ts +6 -37
  12. package/dist/builtin/workflows/src/durable/journal/resolution.d.ts +2 -0
  13. package/dist/builtin/workflows/src/durable/journal/sql-run-journal.d.ts +5 -4
  14. package/dist/builtin/workflows/src/durable/journal/types.d.ts +1 -1
  15. package/dist/builtin/workflows/src/extension/index.bundle.mjs +40393 -40289
  16. package/dist/builtin/workflows/src/extension/local-worker-entry.mjs +55675 -55570
  17. package/dist/builtin/workflows/src/index.js +126 -22
  18. package/dist/builtin/workflows/src/local-worker-entry.mjs +55675 -55570
  19. package/dist/core/settings-schema.d.ts.map +1 -1
  20. package/dist/core/settings-schema.js +1 -1
  21. package/dist/core/settings-schema.js.map +1 -1
  22. package/dist/core/storage/provider.d.ts +6 -3
  23. package/dist/core/storage/provider.d.ts.map +1 -1
  24. package/dist/core/storage/provider.js +7 -5
  25. package/dist/core/storage/provider.js.map +1 -1
  26. package/dist/core/storage/run-journal.d.ts +34 -0
  27. package/dist/core/storage/run-journal.d.ts.map +1 -1
  28. package/dist/core/storage/run-journal.js.map +1 -1
  29. package/docs/settings.md +1 -1
  30. package/docs/storage.md +3 -1
  31. package/docs/workflows/api-reference.md +8 -1
  32. package/docs/workflows/operations.md +1 -1
  33. package/npm-shrinkwrap.json +32 -32
  34. package/package.json +3 -3
  35. package/schemas/settings.schema.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,64 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.9.45-alpha.1] - 2026-10-25
6
+
7
+ Custom storage adapters now plug into workflow journals as first-class citizens, and large local worker pools start reliably on busy machines.
8
+
9
+ - **Journal adapters that build on Atomic's own journal:** a storage adapter loaded by path can import `@bastani/atomic/workflows` and get the running Atomic's module, so `SqlRunJournal` subclasses and journal errors such as `StaleLeaseError` work instead of failing with `Storage unavailable` ([#3963](https://github.com/bastani-inc/atomic/issues/3963), [#3969](https://github.com/bastani-inc/atomic/pull/3969))
10
+ - **Fast status, resume and receipts for custom journals:** a run journal that implements the optional `changeToken()`, `runVersions()`, `snapshotTail()` and `appendMany()` members gets the same faster `/workflow status`, resume and batched delivery receipts as the built-in SQL journal ([#3964](https://github.com/bastani-inc/atomic/issues/3964), [#3968](https://github.com/bastani-inc/atomic/pull/3968))
11
+ - **Worker pools that start on small or busy machines:** without an explicit `workflows.localWorkers.startupTimeoutMs`, the startup deadline scales with pool size and machine load instead of failing after 30 s ([#3962](https://github.com/bastani-inc/atomic/issues/3962), [#3965](https://github.com/bastani-inc/atomic/pull/3965))
12
+
13
+ ### Added
14
+
15
+ - `StorageProvider` journal factories accept `hostModules`, and `loadStorageAdapter()` takes an optional third `hostModules` argument, to give adapter modules extra live host modules by specifier ([#3963](https://github.com/bastani-inc/atomic/issues/3963))
16
+ - The `RunJournal` interface in `@bastani/atomic/storage` has optional `changeToken()`, `runVersions()`, `snapshotTail()` and `appendMany()` members, with the `RunVersion`, `RunSnapshotTail` and `RunJournalBatchAppend` types. A storage adapter's run journal that implements them gets the same faster `/workflow status`, resume and batched delivery receipts as the built-in SQL journal ([#3964](https://github.com/bastani-inc/atomic/issues/3964))
17
+
18
+ ### Fixed
19
+
20
+ - A storage adapter loaded by path to open a workflow journal can now import `@bastani/atomic/workflows`. It gets the running Atomic's workflows module, so an `openRunJournal` hook can build on `SqlRunJournal` and its errors, such as `StaleLeaseError`, pass Atomic's `instanceof` checks. Before, the import resolved to a path inside `dist/index.js` and the journal failed with `Storage unavailable` ([#3963](https://github.com/bastani-inc/atomic/issues/3963))
21
+
22
+ ## [0.9.44] - 2026-10-25
23
+
24
+ Large agent swarms and pooled workflow fan-outs now run for hours without freezing the TUI, dropping messages or slowing down, and recover cleanly from a crash.
25
+
26
+ - **A TUI that stays responsive under heavy fan-out:** local worker pools run in their own coordinator process, journals no longer grow on every heartbeat, and the root's main thread stays idle while hundreds of pooled children run ([#3905](https://github.com/bastani-inc/atomic/pull/3905))
27
+ - **Intercom that keeps working at scale:** pooled stages wait for their run's route instead of failing with `Live workflow-stage route has no registered workflow owner`, runs share one broker connection, a refused route no longer drops the others, and route announcements are batched and bounded ([#3907](https://github.com/bastani-inc/atomic/pull/3907), [#3915](https://github.com/bastani-inc/atomic/pull/3915), [#3939](https://github.com/bastani-inc/atomic/pull/3939), [#3951](https://github.com/bastani-inc/atomic/pull/3951))
28
+ - **Messages that arrive once, on time and without interrupting:** Intercom `send` and `ask` default to `"steer"` (with `followUp` and `interrupt` available), replies reach the right sender, a message that reaches a stage before its prompt becomes context for its first turn, and queued messages survive pauses, worker replacement and reconnects ([#3921](https://github.com/bastani-inc/atomic/issues/3921), [#3923](https://github.com/bastani-inc/atomic/pull/3923), [#3924](https://github.com/bastani-inc/atomic/pull/3924), [#3927](https://github.com/bastani-inc/atomic/pull/3927), [#3958](https://github.com/bastani-inc/atomic/pull/3958), [#3959](https://github.com/bastani-inc/atomic/pull/3959))
29
+ - **Journals and costs that stay flat over a long run:** journal writes are group-committed and batched, each delivery receipt is written once, pending-message changes are stored as deltas, and pool commits, delivery transitions and receipt bookkeeping cost what changed rather than the run's history ([#3922](https://github.com/bastani-inc/atomic/pull/3922), [#3930](https://github.com/bastani-inc/atomic/pull/3930), [#3937](https://github.com/bastani-inc/atomic/pull/3937), [#3940](https://github.com/bastani-inc/atomic/pull/3940), [#3942](https://github.com/bastani-inc/atomic/pull/3942)–[#3948](https://github.com/bastani-inc/atomic/pull/3948), [#3952](https://github.com/bastani-inc/atomic/pull/3952))
30
+ - **Fast, reliable recovery:** `/workflow status` and resume load large journals in seconds, resume no longer rebuilds every child's history, and resumed pooled children no longer fail with checkpoint identity conflicts or spurious `Storage unavailable` errors; a stage never waits forever to start ([#3950](https://github.com/bastani-inc/atomic/pull/3950), [#3954](https://github.com/bastani-inc/atomic/pull/3954), [#3955](https://github.com/bastani-inc/atomic/pull/3955), [#3958](https://github.com/bastani-inc/atomic/pull/3958))
31
+ - **Peer-to-peer swarms by default:** subagents and workflow stages talk to each other directly, subagent `parallel.*` limits and workflow `defaultConcurrency` default to 1000, and workflow `maxDepth` now defaults to 3; set them lower to throttle ([#3912](https://github.com/bastani-inc/atomic/pull/3912))
32
+ - **Completion notices you can rely on:** background subagent and shell completions reach the parent after a restart, after a paused stage resumes, and when the parent is idle with a session saved to a file ([#3909](https://github.com/bastani-inc/atomic/pull/3909), [#3926](https://github.com/bastani-inc/atomic/pull/3926), [#3953](https://github.com/bastani-inc/atomic/pull/3953))
33
+ - **Upstream sync, tools and security:** streamed tool-call arguments are parsed once per delta, Cua Driver 0.38.0 approves its own macOS permission prompts (`ATOMIC_CUA_AUTO_APPROVE=0` to keep asking), herdr 0.9.5 and agent-browser 0.39.2 are bundled, and `undici`, `zod`, `fast-uri`, `body-parser` and `tokio` close their published advisories ([#3910](https://github.com/bastani-inc/atomic/pull/3910), [#3911](https://github.com/bastani-inc/atomic/pull/3911), [#3935](https://github.com/bastani-inc/atomic/pull/3935))
34
+
35
+ **Breaking change:** `appendGrammarToolInputJsonDelta()` in `@bastani/pi-ai` now takes only the new input delta and no longer closes the input; call `closeGrammarToolInputJson()` when the input is complete ([#3910](https://github.com/bastani-inc/atomic/pull/3910))
36
+
37
+ ### Fixed
38
+
39
+ - An Intercom message sent to one specific workflow stage while that stage is paused, blocked or awaiting input and has not started yet is now queued for that stage and reaches it when it starts. Previously the send was accepted as a message for a possible future stage (`notInKnownSet`) and matched only the stage's old id, so it was lost when the stage started under a new id, as it does when a pooled worker is replaced. Messages sent to the whole run (`workflow:<rootRunId>/**`) were not affected ([#3869](https://github.com/bastani-inc/atomic/issues/3869))
40
+ - A workflow stage no longer waits forever before its first turn when an Intercom message to a pattern target such as `workflow:<run>/child-a/**` is queued just after the stage's queued messages were delivered at startup. The stage now starts once that startup delivery has finished, and later messages reach it as ordinary live messages. Before, the stage waited for a second delivery that never ran, so its prompt was never sent and its pooled work stayed leased with its lease still renewed. In a 640-child pooled load test this left 7 children (about 1%) stuck for 20–45 minutes.
41
+ - A workflow stage never waits more than 60 seconds without delivery progress for its queued Intercom messages before it starts. If a startup delivery stops making progress, the stage logs one warning that names the undelivered messages and whether the delivery is still running. It then starts, and the messages stay queued: they arrive live or on the stage's next delivery.
42
+ - A sticky Intercom message that could not reach a workflow stage while the stage was disconnected from the broker is now delivered when the stage reconnects, exactly as it would have arrived live: an `interrupt` still cancels the stage's running operation, and an ask becomes the stage's reply target. Messages the stage already has are not delivered again.
43
+ - Under heavy write load a local SQLite store (`node-sqlite`, including the workflow journal) commits transactions in groups: transactions requested while another is committing share one `BEGIN IMMEDIATE … COMMIT`, each in its own savepoint and in request order. A transaction that fails rolls back only its own writes, and every caller still settles only after the shared commit. In a 480-child pooled workflow the pool coordinator was committing about 60 transcript transactions a second, each taking the write lock and syncing on its own, which starved the workflow host's writes until Intercom sends timed out and worker leases expired.
44
+ - A background task completion that arrives just as a paused workflow stage is resumed now starts the stage's next turn. Pausing a stage cancels its background shells, and their completion notices are written to the session before the stage acts on them. If the stage was resumed during that write, the notice was queued after the resume had already released the stage's held messages, so the stage sat idle with the notice unanswered until something else started a turn.
45
+ - A local SQLite store (`node-sqlite`, including the workflow journal) now waits for another process's write lock instead of failing after the 5 second busy timeout. When the lock is busy, it retries the start of the write transaction for up to 30 seconds without blocking the event loop. Single statements outside a transaction still use the configured busy timeout. Other failures, and a lock still held at that point, fail at once. `Storage unavailable` errors now include the SQLite reason, such as `database is locked (SQLite code 5)`, so the reason survives when local workers pass an error to another process
46
+ - Updated `undici`, which Atomic uses for HTTP requests, to 8.12.3. A server can no longer crash Atomic by sending more than five `Content-Encoding` values ([GHSA-xjj6-f33q-929c](https://github.com/advisories/GHSA-xjj6-f33q-929c)), Server-Sent Events streams can no longer grow parser memory without bound ([GHSA-4qv8-2p9j-2685](https://github.com/advisories/GHSA-4qv8-2p9j-2685)), and Subresource Integrity checks no longer skip uppercase algorithm names ([GHSA-f72v-6w45-q472](https://github.com/advisories/GHSA-f72v-6w45-q472)).
47
+ - Updated the MCP SDK dependencies in the published lockfile to `zod` 4.6.7 ([GHSA-mrhx-c7xc-m3w5](https://github.com/advisories/GHSA-mrhx-c7xc-m3w5)), `fast-uri` 3.1.9 ([GHSA-w74x-v6f9-j3xg](https://github.com/advisories/GHSA-w74x-v6f9-j3xg), [GHSA-wjr9-j795-jxcr](https://github.com/advisories/GHSA-wjr9-j795-jxcr), [GHSA-xhq7-xmqj-769h](https://github.com/advisories/GHSA-xhq7-xmqj-769h), [GHSA-333x-w4r6-6crp](https://github.com/advisories/GHSA-333x-w4r6-6crp)), and `body-parser` 2.4.0 ([GHSA-27r3-jxmh-p828](https://github.com/advisories/GHSA-27r3-jxmh-p828)).
48
+ - Updated the native module's `tokio` crate to 1.54.1, replacing the yanked 1.54.0 release and its `Interval` race ([GHSA-5wjr-9356-4wc8](https://github.com/advisories/GHSA-5wjr-9356-4wc8)).
49
+ - Updated the gondolin example extension's lockfile to `undici` 6.29.1, resolving [GHSA-xjj6-f33q-929c](https://github.com/advisories/GHSA-xjj6-f33q-929c) and [GHSA-4qv8-2p9j-2685](https://github.com/advisories/GHSA-4qv8-2p9j-2685) in its `@earendil-works/gondolin` dependency.
50
+ - The standalone binary resolves its builtin packages only from the `builtin/` directory beside the executable, so the Intercom broker starts for pooled workflow stages even when the build tree still exists on the host ([#3917](https://github.com/bastani-inc/atomic/issues/3917))
51
+ - Fixed workflow stages failing with `Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message.` when an Intercom message, such as a sibling's send or a sticky `workflow:<rootRunId>/**` broadcast, reached the stage after it went live but before the workflow sent the stage's own prompt. The message started a turn of its own and the stage prompt was rejected. Messages that arrive before the stage prompt now become context for the stage's first turn, and the stage prompt always starts that turn ([#3919](https://github.com/bastani-inc/atomic/issues/3919))
52
+ - Background subagent completions that arrive while the parent session is finishing a turn now always get their reply turn, so exiting or disposing a session saved to a file no longer fails with `Cannot dispose a session with a queued protected reconciliation` ([#3925](https://github.com/bastani-inc/atomic/issues/3925))
53
+ - Fixed background subagent and shell completion notices never reaching the parent conversation after the session was restarted or resumed in a new process: the new process numbers its tasks from the start again, so a completion matched an earlier one already acknowledged in the session history and was silently dropped. Each completion now carries an identity unique to its process generation, and every settled task still delivers exactly one notice ([#3908](https://github.com/bastani-inc/atomic/issues/3908))
54
+ - Atomic now starts when a package configured in `settings.json` fails to install (for example when npm cannot reach the registry), instead of crashing with an uncaught error. It skips that package, warns with the `atomic remove` command that drops it (`--local` for project packages), and tries again on the next start or `/reload`. `--help`, `--list-models` and `atomic config` show the warning too ([pi #11088](https://github.com/earendil-works/pi/issues/11088))
55
+ - `pi.exec()` no longer corrupts multi-byte UTF-8 characters split across output chunks, which came back as `�` replacement characters ([pi #11312](https://github.com/earendil-works/pi/issues/11312))
56
+ - Automatic `fd` and `rg` downloads no longer fetch x86-64 binaries on ARM32 and other architectures without a prebuilt release. Atomic now warns and recommends installing the tool with the system package manager ([pi #11209](https://github.com/earendil-works/pi/issues/11209))
57
+ - Streamed tool calls no longer re-run their call renderer on every argument delta; the interactive mode renders the latest arguments once per frame, so a large streamed `write` no longer re-highlights its whole content per delta ([pi #9265](https://github.com/earendil-works/pi/issues/9265))
58
+
59
+ ### Added
60
+
61
+ - `/model` and `/scoped-models` now show a model's name next to its ID when the name adds words the ID lacks, such as a renamed `models.json` override ([pi #5916](https://github.com/earendil-works/pi/issues/5916), [pi #11347](https://github.com/earendil-works/pi/pull/11347))
62
+
5
63
  ## [0.9.44-alpha.7] - 2026-10-25
6
64
 
7
65
  Workflow stages always get their queued Intercom messages and never wait forever to start, even around pauses, worker replacement and reconnects.
@@ -4,6 +4,24 @@ All notable changes to the `pi-intercom` extension will be documented in this fi
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.9.44] - 2026-10-25
8
+
9
+ ### Changed
10
+
11
+ - `intercom list` lists at most 5 sticky delivery states per workflow row: states that still need action first, then the most recent deliveries. A final line reports how many were omitted and the total in each state, and the row details carry the same totals as `deliveryStateCounts` and `omittedDeliveryStates`. In a 640-child workflow, each list result had stored about 3 MB of delivery states in the session transcript. A test with 8,000 queued messages in 163 rows shrinks the list result from about 4.6 MB to about 0.2 MB. The broker applies the same bound to rows announced by older workflow hosts.
12
+ - Intercom `send` and `ask` take a `delivery` option, and the default is now `"steer"`: a message to a working subagent or live workflow stage arrives after the recipient's current tool calls finish instead of cancelling its work, so peers in a swarm no longer abort each other's long-running tool calls. `"followUp"` waits until the recipient finishes and stops. `"interrupt"` keeps the previous behavior of cancelling the recipient's current model call or tool call, and is now opt-in ([#3921](https://github.com/bastani-inc/atomic/issues/3921))
13
+ - The `intercom` skill now presents unlimited peer-to-peer (swarm) coordination as the default, with a broadcast example and guidance for agents to discover and talk to peers proactively. It also covers scaling a swarm to hundreds or thousands of agents organized into teams.
14
+
15
+ ### Fixed
16
+
17
+ - A pooled workflow stage whose Intercom extension delivers queued messages at startup no longer fails with `Extension startup failed: …intercom…: Storage unavailable: node-sqlite at <journal>` while other processes are writing heavily to the workflow journal. The journal write now waits for the lock, and any remaining failure states its SQLite cause
18
+ - Intercom `send` and `ask` to a workflow-stage target (`workflow:<rootRunId>/...`, including patterns) no longer fetch the whole session list before sending. The broker resolves those targets itself, so the extra request only added load, and when many stages sent at once some sends failed with `List sessions timeout` ([#3931](https://github.com/bastani-inc/atomic/issues/3931))
19
+ - Receiving a large Intercom message (for example a big workflow roster) no longer copies its data again for every chunk that arrives, which made large frames cost quadratic time ([#3931](https://github.com/bastani-inc/atomic/issues/3931))
20
+ - An implicit Intercom `reply` no longer answers a message the agent has not seen yet. Before, a queued message (for example a `"followUp"` ask that arrived while a tool ran) could become the reply target as soon as any new turn started, including the continuation after that tool call, so the reply reached the wrong sender or thread. A message now becomes the reply target only when it enters the agent's context, including when Intercom is loaded through its lazy entry point, and every ask that enters the context in the same turn (for example several `"followUp"` asks delivered together) can still be answered with an implicit `reply` after the first one is answered ([#3921](https://github.com/bastani-inc/atomic/issues/3921), follow-up to [#3924](https://github.com/bastani-inc/atomic/pull/3924))
21
+ - Workflow stages that start while their run's Intercom route is still registering, or is registering again after its connection dropped, no longer fail every Intercom call with `Live workflow-stage route has no registered workflow owner`. The stage waits for the route to come back before it uses Intercom, instead of giving up after a few quick retries ([#3904](https://github.com/bastani-inc/atomic/issues/3904))
22
+ - Workflow route owners now register the routes of every run in a workflow invocation over one broker connection instead of opening one connection per run. The connection is per workflow invocation rather than per host because the broker grants route ownership per invocation group. All child runs of one root share that connection, whatever the fan-out size, so a large fan-out of child workflows no longer builds a connection backlog at the broker, and a run whose route moves to a new capability keeps the connection the other runs use. A run whose route the broker refuses moves to a connection of its own, so it cannot take the other runs' routes down with it. Only refused runs get such a connection, so the number of extra connections is bounded by the refused runs, not by the number of runs ([#3904](https://github.com/bastani-inc/atomic/issues/3904))
23
+ - When the Intercom broker refuses one workflow run's route, for example a route that another session owns, it now refuses that route on its own and keeps the shared route connection open, so the routes of the other runs in the same workflow invocation are no longer dropped for a reconnect. Authorization is unchanged: the same registrations are refused for the same reasons. The route owner retries only the refused run, on its own backoff. With an older broker, which still ends the connection, the refused run still moves to a connection of its own. Clients that do not ask for this, including older ones, keep the previous behavior ([#3906](https://github.com/bastani-inc/atomic/issues/3906))
24
+
7
25
  ## [0.9.44-alpha.6] - 2026-10-25
8
26
 
9
27
  ### Changed
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/intercom",
3
- "version": "0.9.44-alpha.7",
3
+ "version": "0.9.45-alpha.1",
4
4
  "private": true,
5
5
  "description": "Atomic extension providing a private coordination channel between parent and child agent sessions. Fork of: https://github.com/nicobailon/pi-intercom",
6
6
  "contributors": [
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.9.44] - 2026-10-25
11
+
12
+ ### Fixed
13
+
14
+ - MCP sign-in now accepts only `http:` and `https:` authorization endpoints and other OAuth metadata URLs. Before, a malicious MCP server could supply a `file:` or custom protocol authorization endpoint (for example `search-ms:`), which the platform URL handler would launch on sign-in ([pi 9ffd27cf](https://github.com/earendil-works/pi/commit/9ffd27cfaaeed5c1ecf9369f393c3222975729b4))
15
+ - The excerpt of an HTTP error body in Streamable HTTP errors no longer keeps the whole response body alive ([pi #9265](https://github.com/earendil-works/pi/issues/9265))
16
+
10
17
  ## [0.9.44-alpha.1] - 2026-10-24
11
18
 
12
19
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/mcp",
3
- "version": "0.9.44-alpha.7",
3
+ "version": "0.9.45-alpha.1",
4
4
  "private": true,
5
5
  "description": "Atomic's pi-compatible MCP integration for server connections, OAuth, tools, resources, and terminal management.",
6
6
  "contributors": [
@@ -2,6 +2,15 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.9.44] - 2026-10-25
6
+
7
+ ### Changed
8
+
9
+ - **Security policy change:** on macOS, the agent now approves Cua Driver's own Accessibility and Screen Recording permission prompts itself by default, without asking, by clicking them through the host's VNC or Screen Sharing server when credentials for it are available (for example `CUA_VNC_SERVER` and `CUA_VNC_PASSWORD`). It reports each approval and when it happened, never approves any other permission or another app's dialog, and types a password only when you supplied one. Set `ATOMIC_CUA_AUTO_APPROVE=0` to turn this off and have the agent ask you to grant the permissions, as before.
10
+ - Updated the bundled Cua Driver skill to upstream `cua-driver-rs-v0.38.0`. Refusals, `unverifiable` actions and `suspected_noop` results now come with a `hint` and a safe `next_call` to try, and three single actions in a row suggest batching with `run_steps`. Steps inside `run_steps` and `run_script` skip most of the cursor glide and window watch on macOS. The macOS guide adds installing and updating the driver and granting its permissions with an MDM profile on managed Macs. It also covers `invoke_menu` refusing unless the requested window is key, background `<select>` menus in Chrome and Electron via `set_value`, Electron drags, and handing a URL to a specific Windows browser with `additional_arguments`.
11
+ - Updated the bundled Herdr skill to upstream `v0.9.5`. It can now control agents and panes on a saved SSH machine with `herdr --machine <label-or-id>`, and it explains that an agent suspended with Ctrl+Z keeps its identity, that Antigravity and Grok can look idle while background work continues, that `pane run` only acknowledges admission (and rejects over-long lines on macOS), and that CLI closes skip the TUI's last-pane confirmation. The explicit-request trigger and the `HERDR_ENV=1` requirement are unchanged.
12
+ - Subagents now default to peer-to-peer (swarm) coordination with no limits on communication between agents: children discover siblings with `intercom list` and broadcast, debate, claim, hand off, and ask each other directly instead of relaying through the supervisor. Explicit user approval and authorization gates still apply. Swarms scale with the task: for very complex work or requested heavy parallelism, hundreds or thousands of agents self-organize into teams, and the subagents guide documents the settings that bound swarm size. The `parallel.maxTasks`, `parallel.concurrency` and `parallel.runningCapacity` defaults rise from 50, 3 and 4 to 1000, so one parent can run up to 1000 children at once without configuration; set them lower in `~/.atomic/agent/extensions/subagent/config.json` to throttle, or higher for larger swarms.
13
+
5
14
  ## [0.9.44-alpha.1] - 2026-10-24
6
15
 
7
16
  ### Changed
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/subagents",
3
- "version": "0.9.44-alpha.7",
3
+ "version": "0.9.45-alpha.1",
4
4
  "private": true,
5
5
  "description": "Atomic extension for delegating tasks to subagents with parallel execution. Fork of: https://github.com/nicobailon/pi-subagents",
6
6
  "contributors": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/web-access",
3
- "version": "0.9.44-alpha.7",
3
+ "version": "0.9.45-alpha.1",
4
4
  "private": true,
5
5
  "description": "Atomic extension for web search, URL fetching, GitHub repo cloning, PDF/video extraction. Fork of: https://github.com/nicobailon/pi-web-access",
6
6
  "contributors": [
@@ -6,6 +6,48 @@ 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.45-alpha.1] - 2026-10-25
10
+
11
+ ### Added
12
+
13
+ - A run journal from a storage adapter, or any custom `RunJournal`, can now opt into the faster `/workflow status`, resume and batched delivery receipts that the built-in SQL journal uses, by implementing the optional `changeToken()`, `runVersions()`, `snapshotTail()` and `appendMany()` members. `registerRunJournalConformance` checks them when present ([#3964](https://github.com/bastani-inc/atomic/issues/3964))
14
+
15
+ ### Fixed
16
+
17
+ - Journal adapters loaded by path (`ATOMIC_WORKFLOWS_JOURNAL`, `workflows.journal`, or a run's `journal: { adapter }`) can import `@bastani/atomic/workflows` and receive the workflows module that opened the journal, including its `SqlRunJournal` and journal error classes ([#3963](https://github.com/bastani-inc/atomic/issues/3963))
18
+ - A large local worker pool no longer fails at startup with `Local worker startup timed out after 30000 ms` on a small or busy machine. When `localWorkers.startupTimeoutMs` is not set, the readiness deadline now scales with the number of workers and the 1-minute load average per CPU core, from 30000 ms up to 600000 ms; on an idle machine with spare cores it stays 30000 ms. If workers still miss the deadline, the error now says they were too slow to start and names `workflows.localWorkers.startupTimeoutMs` ([#3962](https://github.com/bastani-inc/atomic/issues/3962))
19
+
20
+ ## [0.9.44] - 2026-10-25
21
+
22
+ ### Fixed
23
+
24
+ - After a pooled root was killed and resumed, some child workflows no longer fail with `Extension startup failed: …intercom…: Storage unavailable: custom at custom journal`. While a resumed root and its workers were busy, a worker's lease could run out a moment before the pool extended it. When the root then recorded that a queued Intercom message had reached the worker's stage, it refused the write as if the attempt had been replaced and reported the refusal as unavailable storage, so the stage failed at startup. In a 640-child run this failed 21 children. The pool already keeps a live worker's child when its lease runs out (#3888), and the root now does the same when recording deliveries. Only a reassigned attempt or a new root epoch is refused, with `Stale work lease` or the session reason, and the replaced attempt's child is replayed. A failure of a custom journal now also names the cause's error class and code, though not its message, which may contain credentials.
25
+ - A long-running workflow fan-out whose stages keep sending Intercom messages to stage patterns (for example `workflow:<rootRunId>/child-*/**`) no longer slows down with every message it has sent. Pattern messages stay queued until the root run ends, and each delivery receipt made the workflow host re-check every queued message and every recorded delivery against every run. In a 480-child load test this saturated the host, Intercom sends timed out after 10 s, and about half of them were never delivered. A delivery now re-checks only the messages it changed and the messages delivered to stages that have since ended: with 3,000 queued messages, recording one delivery takes about 1.2 ms instead of 6.7 ms. Delivery results are unchanged.
26
+ - An Intercom send to a workflow stage no longer times out when a wave of stages starts at the same time. Every pending-message change for a run used to wait for the previous one's journal write, so a send that arrived while about 160 starting stages recorded their deliveries waited behind about 160 writes. Changes that arrive while a write is in progress are now applied together and saved in one write. In a test with a 40 ms write, a send issued in the middle of a 160-stage burst returned after 2 writes (about 90 ms) instead of 81 (about 3.3 s).
27
+ - Large workflow fan-outs no longer saturate the local worker pool's coordinator. Every pool commit, including each batch of session transcript entries, was checked for JSON-serializability with a recursive schema whose `$ref` evaluation cost far more than the write itself, so with 160 workers the coordinator spent about 90% of its time validating and stages stopped starting. Plain JSON values now pass a direct structural check; anything unusual still goes through the schema, with the same errors.
28
+ - Intercom sends to stage patterns no longer time out while a large workflow fan-out records delivery receipts. The workflow host wrote each receipt in its own journal transaction, one after another, and a pattern message waited behind every receipt queued before it. When another process also held the journal's write lock, that wait exceeded the 10-second send acknowledgement, and in a 480-child load test delivery fell to 0–2%. Receipts and settlements queued back to back for a run are now written together in one transaction, in the same order and with the same durability. In a test that queues a pattern message halfway through 2,000 receipts from 160 callers while a second process holds the write lock, the message persisted in under 30 ms instead of more than 4 seconds.
29
+ - Resuming a large workflow with many completed child workflows no longer takes minutes before any work restarts. Resume rebuilt every nested run from the whole checkpoint journal once for each cached child, and each rebuild itself rescanned the journal for every child boundary. Resume now rebuilds the nested runs once, reuses them while replay records only its own bookkeeping, and rebuilds each in a single pass. Resuming a pooled root with 640 completed children went from 156 s to 1.3 s (64 children: 271 ms to 37 ms), and resumed runs reach the same state as before.
30
+ - The local worker pool coordinator no longer slows down as a long pooled run grows. Each checkpoint a worker committed was validated against every checkpoint the root had ever recorded, and each commit re-added every usage report, so after about 15 minutes of a 640-child run the coordinator sat near full CPU and Intercom sends began to fail. Validation now reads only the checkpoints recorded since the previous commit, and the root's usage total is kept up to date as usage is written. Validation results and totals are unchanged. With 5,000 recorded checkpoints, a commit takes about 1.2 ms instead of 16 ms, and reading the usage total for 40,000 usage rows takes 0.02 ms instead of 8 ms.
31
+ - Late in a long workflow fan-out, Intercom sends no longer time out while the host announces workflow routes or records delivery receipts. Each root route announcement recomputed every queued pattern message's delivery state on every matching stage, and every recorded receipt re-read the delivery count of every queued message. Both grow for the whole run (about 7,700 queued messages after 21 minutes in a 640-child load test), so one announcement could block the host for seconds. Announcements and receipts now recompute only what changed. With 8,000 queued messages, an announcement after one delivery makes 2 receipt checks instead of 16,000, and recording a receipt reads 1 delivery count instead of 8,000. Delivery counts are unchanged.
32
+ - `/workflow status` from a new session, and resuming a workflow, no longer stall on a journal that holds many delivery receipts. Loading the journal re-read the delivery count of every queued message for every receipt. After a 640-child run with about 800,000 receipts, status had not rendered after 3 minutes. In a test with 200,000 receipts over 8,000 queued messages, opening and loading the run now takes about 3.4 s instead of 168 s.
33
+ - Each delivery of a pattern (sticky) Intercom message to a workflow stage is now written to the run's journal once. When a delivered stage ended, the workflow host archived that delivery's receipt together with every earlier receipt still kept for the message. Those earlier receipts stayed kept, so they were written again each time a later stage ended. In pooled load-test journals about 93% of delivery receipts were exact copies, with one receipt written up to 423 times, and each copy took a slot in the run's journal writes. Receipts that are already archived are no longer written again. Delivery counts are unchanged: the copies never raised the recorded count.
34
+ - A pooled child workflow resumed more than once no longer fails with `Pool checkpoint identity conflict`. Replaying a completed stage records a small replay-metadata checkpoint under an id derived from the stage, stamped with the time of that replay. The first resume recorded it. When a later resume, or a replacement worker, replayed the stage again, the pool coordinator compared the new record byte for byte with the stored one, saw a different time and failed the child. In a 640-child load test, 31 children failed this way after one SIGKILL and resume. A re-recorded checkpoint that differs only in its record time now keeps the first write. A checkpoint with any other difference is still rejected.
35
+ - A large workflow fan-out no longer pins the workflow host's main thread rebuilding Intercom routes. Every store change re-announced the route of every run (480 children in a load test), so under a stream of messages and delivery receipts the host spent most of its time on announcements and Intercom sends began to time out. Store changes now publish routes in batches about 25 ms apart, spaced further when needed so announcing takes at most about a fifth of the host's time, and a stage that waits for its route still gets the latest one first.
36
+ - A pending-message write that loses a race with another writer to the same run (for example a stage's delivery receipt) no longer stores the run's whole list of pending stage messages in the journal. It is rebuilt as a change on top of the other writer's entry and retried, and only falls back to the full list after repeated conflicts. In a 160-worker fan-out, 2% of queued events had fallen back and written 93% of their bytes, up to 1.5 MB each and growing with the list ([#3929](https://github.com/bastani-inc/atomic/issues/3929))
37
+ - A workflow whose stages send many Intercom messages to stage patterns (for example `workflow:<rootRunId>/child-*/**`) no longer saturates the workflow host. The host matched every queued pattern message against every stage on each change; it now indexes the patterns and each stage's paths once. In a 150-child fan-out test, delivered sends went from 566 of 900 to 900 of 900 and host event-loop lag p99 from about 1 s to under 200 ms ([#3931](https://github.com/bastani-inc/atomic/issues/3931))
38
+ - Workflow journal events no longer store a run's whole list of pending stage messages each time. Pattern sends stay queued until the root run ends, so every queued message rewrote the growing list and the journal grew quadratically with the number of sends. Each event now records only what changed; older journals still read as before ([#3929](https://github.com/bastani-inc/atomic/issues/3929))
39
+ - A sticky Intercom broadcast, such as `workflow:<rootRunId>/**`, that reaches many workflow stages no longer makes the workflow journal grow quadratically. Recording one more delivery used to rewrite every stage's deliveries in the run's queued-message event; each event now stores only the deliveries that changed since the run's previous event. In a 160-worker run this shrinks those events from about 160 MB to about 9 MB. Journals written by earlier versions read and resume unchanged ([#3920](https://github.com/bastani-inc/atomic/issues/3920))
40
+ - Running many local workers at once no longer freezes the interactive TUI. A local worker pool now runs its workers under a separate pool coordinator process, which handles worker messages, heartbeats, pool journal writes and the Intercom routes of pooled children. The root process only receives periodic progress summaries. If the coordinator process dies, its workers exit with it and `ctx.workflow(child)` throws a retryable `WorkflowInfrastructureError` with code `worker_lost` and the message `Local worker pool coordinator exited (…)` ([#3903](https://github.com/bastani-inc/atomic/issues/3903))
41
+ - Local worker heartbeats and session appends no longer grow the workflow journal on every renewal or entry. Lease renewals update the child's work entry in place instead of adding a row, and session appends no longer repeat the stage checkpoint and the full request with each entry. The first time a pool opens an existing journal, it removes the stored renewal rows and keeps only a digest of each recorded pool request; older runs stay readable and resumable ([#3903](https://github.com/bastani-inc/atomic/issues/3903))
42
+ - `/workflow status` no longer reports `durable checkpoint topology is malformed or incomplete` for a running or crashed root, for example one that was killed, whose child workflow had failed or been skipped while the root kept going. Resuming such a root also replays its completed children from their checkpoints again instead of failing ([#3903](https://github.com/bastani-inc/atomic/issues/3903))
43
+ - Stages of child workflows that run on local workers no longer fail every Intercom call (`join`, `list`, `status`, `send`) with `Live workflow-stage route has no registered workflow owner` when a root fans out many children at once. A pooled stage now starts only after the broker holds its run's Intercom route, and a stage whose route is registering again after a dropped connection waits for it instead of failing. All pooled runs of one root invocation share one route connection, one per invocation rather than per host because the broker grants route ownership per invocation group. A run whose route the broker refuses moves to a connection of its own, so it no longer takes the other runs' routes down with it; only refused runs get one, so the number of extra connections is bounded by the refused runs, not by the number of runs. Pausing, resuming or stopping the root while a stage waits for its route still takes effect at once ([#3904](https://github.com/bastani-inc/atomic/issues/3904))
44
+
45
+ ### Changed
46
+
47
+ - Route announcements carry at most 5 sticky delivery states per possible-stage row, with per-state totals and the number omitted. Each state leaves out its target, which the row already names. Workflow status and run inspection list at most 5 delivery states per run in the same way, with the same omitted-states line. Previously each announcement, `intercom list` result and status result carried every delivery state. With 8,000 queued messages across 163 rows, an announcement shrinks from about 4.6 MB to about 0.19 MB.
48
+ - Workflow stages now default to peer-to-peer (swarm) exchange with no limits on communication between agents: stages coordinate with each other directly instead of relaying through the supervisor, and each stage still returns its own structured result. Workflow guidance now scales swarms to hundreds or thousands of agents for very complex work or requested heavy parallelism, organized into named subgroups, per-stage subagent sets and child workflows. The `defaultConcurrency` setting, which bounds how many workflow stages and `ctx.parallel(...)` tasks run at once when no per-call `concurrency` is given, now defaults to 1000 instead of 3; set it lower to limit concurrency.
49
+ - The `maxDepth` setting, which limits how many workflow levels `ctx.workflow(...)` can nest, now defaults to 3 instead of 4. Set it higher in the workflow config to allow deeper nesting.
50
+
9
51
  ## [0.9.44-alpha.6] - 2026-10-25
10
52
 
11
53
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/workflows",
3
- "version": "0.9.44-alpha.7",
3
+ "version": "0.9.45-alpha.1",
4
4
  "private": true,
5
5
  "description": "Atomic extension for multi-stage workflow authoring and execution.",
6
6
  "contributors": [
@@ -1,50 +1,19 @@
1
1
  import type { SqliteExecutor } from "@bastani/atomic/storage";
2
- import { type AppendOutcome, type JournalEntry, type Lease, type RunEvent, type RunJournal } from "./types.js";
2
+ import { type AppendOutcome, type Lease, type RunJournal, type RunJournalBatchAppend, type RunSnapshotTail, type RunVersion } from "./types.js";
3
+ export type { RunJournalBatchAppend as JournalBatchAppend, RunSnapshotTail, RunVersion } from "./types.js";
3
4
  export declare const JOURNAL_READ_PAGE_SIZE = 256;
4
5
  /** Event types the durable backend folds without replacing the run's handle; every other event carries it. */
5
6
  export declare const NON_SNAPSHOT_EVENT_TYPES: readonly ["CheckpointRecorded", "MessageSettled", "MessageDelivered"];
6
- /** A run's latest event. A run whose history was deleted and recorded again ends with a different event. */
7
- export interface RunVersion {
8
- readonly seq: number;
9
- readonly at: number;
10
- }
11
- /**
12
- * A run's latest snapshot event (one carrying the run's handle) and the checkpoints recorded after it: enough to
13
- * list the run in a catalog without reading its history.
14
- */
15
- export interface RunSnapshotTail {
16
- /** The run's latest event of any type. */
17
- readonly version: RunVersion;
18
- readonly snapshot: JournalEntry;
19
- readonly laterCheckpoints: number;
20
- /** `completedAt` of the last of `laterCheckpoints`. */
21
- readonly lastCheckpointAt?: number;
22
- }
23
- /** Lets a durable backend re-read only the runs that changed; kept off the public journal shape. */
7
+ /** Lets a durable backend re-read only the runs that changed. */
24
8
  export interface RunJournalVersions {
25
- /** Moves whenever any connection, in this process or another, commits a change to the journal's store. */
26
9
  changeToken(): Promise<string>;
27
- /** Every listed run with its latest event; a run with no events has version `{ seq: 0, at: 0 }`. */
28
10
  runVersions(): Promise<ReadonlyMap<string, RunVersion>>;
29
- /** Undefined for a run with no snapshot event. */
30
11
  snapshotTail(runId: string): Promise<RunSnapshotTail | undefined>;
31
12
  }
32
- export declare function registerRunJournalVersions(journal: RunJournal, versions: RunJournalVersions): void;
33
- /** Change tracking for a journal that registered it; other journals are re-read in full. */
13
+ /** Change tracking for a journal that implements all three members; other journals are re-read in full. */
34
14
  export declare function runJournalVersions(journal: RunJournal): RunJournalVersions | undefined;
35
- /**
36
- * One append of a batch. `context` runs a function in the async context the append was issued from; that context,
37
- * not the batch's, decides the append's pool append guard.
38
- */
39
- export interface JournalBatchAppend {
40
- readonly event: RunEvent;
41
- readonly key?: string;
42
- readonly context: <R>(action: () => R) => R;
43
- }
44
- /** Appends to one run in one transaction; each append is settled on its own. Kept off the public journal shape. */
45
- export type RunJournalAppendMany = (lease: Lease, appends: readonly JournalBatchAppend[]) => Promise<readonly PromiseSettledResult<AppendOutcome>[]>;
46
- export declare function registerRunJournalAppendMany(journal: RunJournal, appendMany: RunJournalAppendMany): void;
47
- /** Batched appends for a journal that registered them; other journals append one event at a time. */
15
+ export type RunJournalAppendMany = (lease: Lease, appends: readonly RunJournalBatchAppend[]) => Promise<readonly PromiseSettledResult<AppendOutcome>[]>;
16
+ /** Batched appends for a journal that implements them; other journals append one event at a time. */
48
17
  export declare function runJournalAppendMany(journal: RunJournal): RunJournalAppendMany | undefined;
49
18
  export type JournalAppendGuard = (tx: SqliteExecutor, runId: string) => Promise<void>;
50
19
  export interface SqlPoolStorage {
@@ -9,6 +9,8 @@ export interface RunJournalSettings {
9
9
  };
10
10
  storage?: StorageSettingsSelection;
11
11
  }
12
+ /** Sets the live modules a journal adapter imports by specifier; the workflows entry modules provide themselves. */
13
+ export declare function provideRunJournalHostModules(modules: Readonly<Record<string, object>>): void;
12
14
  export interface RunJournalResolutionOptions {
13
15
  journal?: RunJournalOption;
14
16
  settings?: RunJournalSettings;
@@ -1,4 +1,5 @@
1
1
  import { type SqliteDatabase, type StorageDescriptor } from "@bastani/atomic/storage";
2
+ import { type JournalBatchAppend, type RunSnapshotTail, type RunVersion } from "./internal.js";
2
3
  import { type AppendOutcome, type JournalEntry, type Lease, type LeaseOutcome, type LeaseState, type RunEvent, type RunHeader, type RunJournal, type RunSummary } from "./types.js";
3
4
  export { JOURNAL_READ_PAGE_SIZE } from "./internal.js";
4
5
  export declare class SqlRunJournal implements RunJournal {
@@ -27,13 +28,13 @@ export declare class SqlRunJournal implements RunJournal {
27
28
  * `data_version` moves when another connection commits and `total_changes()` when this one writes; the
28
29
  * table aggregates also catch run changes if the adapter does not serve every statement from one connection.
29
30
  */
30
- private changeToken;
31
- private runVersions;
31
+ changeToken(): Promise<string>;
32
+ runVersions(): Promise<ReadonlyMap<string, RunVersion>>;
32
33
  /**
33
34
  * The latest snapshot event is found by walking the run's `(run_id, seq)` key back from its last event, so
34
35
  * only the events after it are visited and only its payload is read.
35
36
  */
36
- private snapshotTail;
37
+ snapshotTail(runId: string): Promise<RunSnapshotTail | undefined>;
37
38
  private requireRun;
38
39
  /** Whether this run exists only in the legacy tables, whose events are then read in place. */
39
40
  private isUnconvertedLegacyRun;
@@ -58,7 +59,7 @@ export declare class SqlRunJournal implements RunJournal {
58
59
  * one append's guard or payload failure is reported for it alone and the rest still commit. A lease or storage
59
60
  * failure rejects the whole batch, with nothing written.
60
61
  */
61
- private appendMany;
62
+ appendMany(lease: Lease, appends: readonly JournalBatchAppend[]): Promise<readonly PromiseSettledResult<AppendOutcome>[]>;
62
63
  /** Validate an append outside any transaction; its payload error is raised where `append` always raised it. */
63
64
  private prepare;
64
65
  private insert;
@@ -37,7 +37,7 @@ export declare const JOURNAL_LEASE: {
37
37
  readonly absentEpoch: 0;
38
38
  };
39
39
  export type JournalLeaseSemantics = typeof JOURNAL_LEASE;
40
- export type { AppendOutcome, JournalEntry, Lease, LeaseOutcome, LeaseState, RunEvent, RunEventType, RunHeader, RunJournal, RunSummary, } from "@bastani/atomic/storage";
40
+ export type { AppendOutcome, JournalEntry, Lease, LeaseOutcome, LeaseState, RunEvent, RunEventType, RunHeader, RunJournal, RunJournalBatchAppend, RunSnapshotTail, RunSummary, RunVersion, } from "@bastani/atomic/storage";
41
41
  export { RUN_EVENT_TYPES } from "@bastani/atomic/storage";
42
42
  export interface JournalLeaseRow {
43
43
  [JOURNAL_COLUMNS.holderId]: string | null;