@bastani/atomic 0.9.40 → 0.9.41-alpha.2
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 +75 -0
- package/README.md +1 -1
- package/dist/builtin/intercom/CHANGELOG.md +12 -0
- package/dist/builtin/intercom/README.md +1 -1
- package/dist/builtin/intercom/broker/broker.ts +24 -21
- package/dist/builtin/intercom/broker/client.ts +97 -6
- package/dist/builtin/intercom/broker/list-reply.ts +53 -0
- package/dist/builtin/intercom/index.bundle.mjs +278 -46
- package/dist/builtin/intercom/package.json +2 -2
- package/dist/builtin/intercom/recoverable-disconnect.ts +27 -1
- package/dist/builtin/intercom/types.ts +8 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +14 -0
- package/dist/builtin/subagents/README.md +14 -0
- package/dist/builtin/subagents/package.json +3 -3
- package/dist/builtin/subagents/src/extension/index.bundle.mjs +99 -19
- package/dist/builtin/web-access/package.json +2 -2
- package/dist/builtin/workflows/CHANGELOG.md +33 -0
- package/dist/builtin/workflows/README.md +2 -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/authoring.d.ts +1 -1
- package/dist/builtin/workflows/src/durable/journal/internal.d.ts +16 -0
- package/dist/builtin/workflows/src/durable/journal/legacy-journal-tables.d.ts +9 -0
- package/dist/builtin/workflows/src/durable/journal/sql-run-journal.d.ts +6 -0
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +950 -116
- package/dist/builtin/workflows/src/index.js +806 -131
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +17 -1
- package/dist/cli/credential-print.d.ts.map +1 -1
- package/dist/cli/credential-print.js +1 -1
- package/dist/cli/credential-print.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-extension-bindings.d.ts +2 -1
- package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
- package/dist/core/agent-session-extension-bindings.js +33 -18
- package/dist/core/agent-session-extension-bindings.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +2 -1
- package/dist/core/agent-session-methods.d.ts.map +1 -1
- package/dist/core/agent-session-methods.js.map +1 -1
- package/dist/core/agent-session-prompt.d.ts.map +1 -1
- package/dist/core/agent-session-prompt.js +114 -21
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +6 -0
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-state.d.ts +19 -1
- package/dist/core/agent-session-state.d.ts.map +1 -1
- package/dist/core/agent-session-state.js +41 -6
- package/dist/core/agent-session-state.js.map +1 -1
- package/dist/core/auth-storage-backends.d.ts +3 -1
- package/dist/core/auth-storage-backends.d.ts.map +1 -1
- package/dist/core/auth-storage-backends.js +79 -12
- package/dist/core/auth-storage-backends.js.map +1 -1
- package/dist/core/client-identity.d.ts +8 -0
- package/dist/core/client-identity.d.ts.map +1 -0
- package/dist/core/client-identity.js +16 -0
- package/dist/core/client-identity.js.map +1 -0
- package/dist/core/compaction/planner-outcome.d.ts.map +1 -1
- package/dist/core/compaction/planner-outcome.js +2 -1
- package/dist/core/compaction/planner-outcome.js.map +1 -1
- package/dist/core/execution-model-router.d.ts +18 -0
- package/dist/core/execution-model-router.d.ts.map +1 -1
- package/dist/core/execution-model-router.js +14 -8
- package/dist/core/execution-model-router.js.map +1 -1
- package/dist/core/extension-oauth-refresh-rejection.d.ts +12 -0
- package/dist/core/extension-oauth-refresh-rejection.d.ts.map +1 -0
- package/dist/core/extension-oauth-refresh-rejection.js +70 -0
- package/dist/core/extension-oauth-refresh-rejection.js.map +1 -0
- package/dist/core/extension-startup-failure.d.ts +30 -0
- package/dist/core/extension-startup-failure.d.ts.map +1 -0
- package/dist/core/extension-startup-failure.js +62 -0
- package/dist/core/extension-startup-failure.js.map +1 -0
- package/dist/core/extensions/context-types.d.ts +2 -0
- package/dist/core/extensions/context-types.d.ts.map +1 -1
- package/dist/core/extensions/context-types.js.map +1 -1
- package/dist/core/extensions/provider-types.d.ts +2 -0
- package/dist/core/extensions/provider-types.d.ts.map +1 -1
- package/dist/core/extensions/provider-types.js.map +1 -1
- package/dist/core/extensions/runner-context.d.ts +1 -0
- package/dist/core/extensions/runner-context.d.ts.map +1 -1
- package/dist/core/extensions/runner-context.js +8 -0
- package/dist/core/extensions/runner-context.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +1 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +3 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/runtime-types.d.ts +1 -0
- package/dist/core/extensions/runtime-types.d.ts.map +1 -1
- package/dist/core/extensions/runtime-types.js.map +1 -1
- package/dist/core/extensions/workflow-run-control-hub.d.ts +1 -0
- package/dist/core/extensions/workflow-run-control-hub.d.ts.map +1 -1
- package/dist/core/extensions/workflow-run-control-hub.js +3 -0
- package/dist/core/extensions/workflow-run-control-hub.js.map +1 -1
- package/dist/core/extensions/workflow-run-control.d.ts +8 -0
- package/dist/core/extensions/workflow-run-control.d.ts.map +1 -1
- package/dist/core/extensions/workflow-run-control.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/model-fallback-failures.d.ts.map +1 -1
- package/dist/core/model-fallback-failures.js +12 -0
- package/dist/core/model-fallback-failures.js.map +1 -1
- package/dist/core/model-resolver-defaults.js +1 -1
- package/dist/core/model-resolver-defaults.js.map +1 -1
- package/dist/core/model-runtime-streaming.d.ts.map +1 -1
- package/dist/core/model-runtime-streaming.js +9 -3
- package/dist/core/model-runtime-streaming.js.map +1 -1
- package/dist/core/model-runtime.d.ts +5 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +6 -0
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/provider-composer-internal.d.ts +8 -6
- package/dist/core/provider-composer-internal.d.ts.map +1 -1
- package/dist/core/provider-composer-internal.js +21 -8
- package/dist/core/provider-composer-internal.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +12 -4
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/storage/session/sql-session-store.d.ts +35 -0
- package/dist/core/storage/session/sql-session-store.d.ts.map +1 -1
- package/dist/core/storage/session/sql-session-store.js +158 -42
- package/dist/core/storage/session/sql-session-store.js.map +1 -1
- package/dist/core/structured-output/deadline.d.ts +40 -0
- package/dist/core/structured-output/deadline.d.ts.map +1 -0
- package/dist/core/structured-output/deadline.js +110 -0
- package/dist/core/structured-output/deadline.js.map +1 -0
- package/dist/core/structured-output/index.d.ts.map +1 -1
- package/dist/core/structured-output/index.js +103 -47
- package/dist/core/structured-output/index.js.map +1 -1
- package/dist/core/structured-output/rejection-detail.d.ts +9 -0
- package/dist/core/structured-output/rejection-detail.d.ts.map +1 -0
- package/dist/core/structured-output/rejection-detail.js +129 -0
- package/dist/core/structured-output/rejection-detail.js.map +1 -0
- package/dist/core/structured-output/types.d.ts +8 -1
- package/dist/core/structured-output/types.d.ts.map +1 -1
- package/dist/core/structured-output/types.js.map +1 -1
- package/dist/core/tasks/completion.d.ts +7 -1
- package/dist/core/tasks/completion.d.ts.map +1 -1
- package/dist/core/tasks/completion.js +28 -6
- package/dist/core/tasks/completion.js.map +1 -1
- package/dist/core/tasks/contracts.d.ts +6 -1
- package/dist/core/tasks/contracts.d.ts.map +1 -1
- package/dist/core/tasks/contracts.js.map +1 -1
- package/dist/core/tasks/pause.d.ts +2 -1
- package/dist/core/tasks/pause.d.ts.map +1 -1
- package/dist/core/tasks/pause.js +3 -2
- package/dist/core/tasks/pause.js.map +1 -1
- package/dist/core/tools/bash-pty-native.d.ts.map +1 -1
- package/dist/core/tools/bash-pty-native.js +10 -5
- package/dist/core/tools/bash-pty-native.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +16 -32
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/grep.js +1 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/kill.d.ts.map +1 -1
- package/dist/core/tools/kill.js +17 -3
- package/dist/core/tools/kill.js.map +1 -1
- package/dist/core/tools/shell-task-render.d.ts +55 -0
- package/dist/core/tools/shell-task-render.d.ts.map +1 -0
- package/dist/core/tools/shell-task-render.js +123 -0
- package/dist/core/tools/shell-task-render.js.map +1 -0
- package/dist/core/tools/shell-task-status.d.ts +52 -0
- package/dist/core/tools/shell-task-status.d.ts.map +1 -0
- package/dist/core/tools/shell-task-status.js +184 -0
- package/dist/core/tools/shell-task-status.js.map +1 -0
- package/dist/core/tools/structured-output.d.ts +5 -0
- package/dist/core/tools/structured-output.d.ts.map +1 -1
- package/dist/core/tools/structured-output.js +3 -0
- package/dist/core/tools/structured-output.js.map +1 -1
- package/dist/extensions/codemode/execute.d.ts.map +1 -1
- package/dist/extensions/codemode/execute.js +134 -57
- package/dist/extensions/codemode/execute.js.map +1 -1
- package/dist/extensions/mcp/config.d.ts.map +1 -1
- package/dist/extensions/mcp/config.js +3 -2
- package/dist/extensions/mcp/config.js.map +1 -1
- package/dist/index-extensions.d.ts +1 -0
- package/dist/index-extensions.d.ts.map +1 -1
- package/dist/index-extensions.js +3 -0
- package/dist/index-extensions.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +19 -6
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/task-row.d.ts.map +1 -1
- package/dist/modes/interactive/components/task-row.js +11 -1
- package/dist/modes/interactive/components/task-row.js.map +1 -1
- package/dist/modes/interactive/interactive-auth-routing.js +12 -10
- package/dist/modes/interactive/interactive-auth-routing.js.map +1 -1
- package/dist/modes/interactive/interactive-extension-custom-ui.js +20 -30
- package/dist/modes/interactive/interactive-extension-custom-ui.js.map +1 -1
- package/dist/modes/interactive/interactive-extension-dialogs.js +23 -96
- package/dist/modes/interactive/interactive-extension-dialogs.js.map +1 -1
- package/dist/modes/interactive/interactive-extension-runtime.js +7 -8
- package/dist/modes/interactive/interactive-extension-runtime.js.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.js +27 -4
- package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.d.ts +20 -10
- package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.js +40 -5
- package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-deps.d.ts +1 -1
- package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-deps.js +1 -1
- package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts +2 -4
- package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-types.d.ts +4 -1
- package/dist/modes/interactive/interactive-mode-types.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-types.js.map +1 -1
- package/dist/modes/interactive/interactive-prompt-restore.d.ts +3 -0
- package/dist/modes/interactive/interactive-prompt-restore.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-prompt-restore.js +9 -1
- package/dist/modes/interactive/interactive-prompt-restore.js.map +1 -1
- package/dist/modes/interactive/interactive-prompt-turn.js +30 -2
- package/dist/modes/interactive/interactive-prompt-turn.js.map +1 -1
- package/dist/modes/interactive/interactive-queueing.js +43 -8
- package/dist/modes/interactive/interactive-queueing.js.map +1 -1
- package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-startup.js +12 -3
- package/dist/modes/interactive/interactive-startup.js.map +1 -1
- package/dist/modes/interactive/theme/color-utils.d.ts +6 -1
- package/dist/modes/interactive/theme/color-utils.d.ts.map +1 -1
- package/dist/modes/interactive/theme/color-utils.js +39 -17
- package/dist/modes/interactive/theme/color-utils.js.map +1 -1
- package/dist/modes/interactive/theme/theme-class.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-class.js +2 -2
- package/dist/modes/interactive/theme/theme-class.js.map +1 -1
- package/dist/modes/interactive/theme/theme-tokens.d.ts +6 -0
- package/dist/modes/interactive/theme/theme-tokens.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-tokens.js +7 -3
- package/dist/modes/interactive/theme/theme-tokens.js.map +1 -1
- package/dist/modes/interactive-engine/isolated-auth.d.ts +3 -0
- package/dist/modes/interactive-engine/isolated-auth.d.ts.map +1 -1
- package/dist/modes/interactive-engine/isolated-auth.js +13 -1
- package/dist/modes/interactive-engine/isolated-auth.js.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.js +11 -2
- package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
- package/dist/modes/interactive-engine/remote-model-catalog.d.ts +8 -0
- package/dist/modes/interactive-engine/remote-model-catalog.d.ts.map +1 -1
- package/dist/modes/interactive-engine/remote-model-catalog.js +54 -0
- package/dist/modes/interactive-engine/remote-model-catalog.js.map +1 -1
- package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-command-handler.js +4 -13
- package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
- package/dist/modes/rpc/rpc-model-catalog.d.ts +9 -0
- package/dist/modes/rpc/rpc-model-catalog.d.ts.map +1 -0
- package/dist/modes/rpc/rpc-model-catalog.js +19 -0
- package/dist/modes/rpc/rpc-model-catalog.js.map +1 -0
- package/dist/modes/rpc/rpc-provider-auth.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-provider-auth.js +2 -6
- package/dist/modes/rpc/rpc-provider-auth.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +6 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/utils/changelog.d.ts +6 -0
- package/dist/utils/changelog.d.ts.map +1 -1
- package/dist/utils/changelog.js +11 -0
- package/dist/utils/changelog.js.map +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +4 -0
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +3 -2
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/background-tasks.md +5 -3
- package/docs/codemode.md +2 -2
- package/docs/custom-provider/oauth.md +42 -1
- package/docs/custom-provider/registration.md +2 -0
- package/docs/environment-variables.md +1 -0
- package/docs/extensions/api-reference.md +3 -4
- package/docs/extensions/authoring.md +1 -1
- package/docs/extensions/events.md +1 -1
- package/docs/models-configuration.md +9 -0
- package/docs/models.md +3 -1
- package/docs/providers.md +4 -4
- package/docs/rpc/protocol.md +4 -2
- package/docs/sdk/reference.md +2 -0
- package/docs/sdk/structured-decisions.md +7 -5
- package/docs/sdk.md +5 -2
- package/docs/settings.md +4 -2
- package/docs/subagents/reference.md +1 -1
- package/docs/subagents.md +15 -1
- package/docs/themes/reference.md +3 -1
- package/docs/usage.md +3 -1
- package/docs/workflows/api-reference.md +28 -0
- package/docs/workflows/authoring.md +1 -1
- package/docs/workflows/operations.md +2 -2
- package/npm-shrinkwrap.json +67 -54
- package/package.json +6 -6
- package/schemas/models.schema.json +21 -0
- package/dist/core/provider-attribution.d.ts +0 -4
- package/dist/core/provider-attribution.d.ts.map +0 -1
- package/dist/core/provider-attribution.js +0 -87
- package/dist/core/provider-attribution.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,81 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.9.41-alpha.2] - 2026-10-22
|
|
6
|
+
|
|
7
|
+
Workflows that drop a promise stop crashing Atomic, and classifiers can read images.
|
|
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 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))
|
|
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 and `compat` settings for proxies ([#3847](https://github.com/bastani-inc/atomic/pull/3847))
|
|
11
|
+
- **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))
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- 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).
|
|
16
|
+
- 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))
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- 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.
|
|
21
|
+
- 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))
|
|
22
|
+
- Fixed `@` file autocomplete returning no results for queries containing regex metacharacters such as `[` or `(` ([#10972](https://github.com/earendil-works/pi/issues/10972))
|
|
23
|
+
- Fixed an unclosed `$$` display-math block swallowing or corrupting every Markdown table after it ([#11159](https://github.com/earendil-works/pi/issues/11159))
|
|
24
|
+
- 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))
|
|
25
|
+
- 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))
|
|
26
|
+
- 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))
|
|
27
|
+
- 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))
|
|
28
|
+
- `/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))
|
|
29
|
+
|
|
30
|
+
## [0.9.41-alpha.1] - 2026-10-21
|
|
31
|
+
|
|
32
|
+
### Breaking Changes
|
|
33
|
+
|
|
34
|
+
- 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.
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- `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.
|
|
39
|
+
- 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`.
|
|
40
|
+
- 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).
|
|
41
|
+
- Exported `noOpUIContext`, the default no-op extension UI context, from the SDK entry point ([#3801](https://github.com/bastani-inc/atomic/issues/3801))
|
|
42
|
+
- `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))
|
|
43
|
+
- `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))
|
|
44
|
+
- 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.
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
|
|
48
|
+
- 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.
|
|
49
|
+
- 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.
|
|
50
|
+
- 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))
|
|
51
|
+
- 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`.
|
|
52
|
+
- 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`.
|
|
53
|
+
|
|
54
|
+
### Fixed
|
|
55
|
+
|
|
56
|
+
- 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))
|
|
57
|
+
- 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))
|
|
58
|
+
- 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))
|
|
59
|
+
- 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))
|
|
60
|
+
- 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))
|
|
61
|
+
- 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.
|
|
62
|
+
- 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)).
|
|
63
|
+
- 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.
|
|
64
|
+
- 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.
|
|
65
|
+
- 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.
|
|
66
|
+
- 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>`.
|
|
67
|
+
- 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`.
|
|
68
|
+
- 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.
|
|
69
|
+
- 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`.
|
|
70
|
+
- `/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.
|
|
71
|
+
- `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))
|
|
72
|
+
- 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))
|
|
73
|
+
- 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))
|
|
74
|
+
- 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))
|
|
75
|
+
- 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.
|
|
76
|
+
- 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.
|
|
77
|
+
- 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))
|
|
78
|
+
- 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))
|
|
79
|
+
|
|
5
80
|
## [0.9.40] - 2026-10-20
|
|
6
81
|
|
|
7
82
|
### Breaking Changes
|
package/README.md
CHANGED
|
@@ -194,7 +194,7 @@ Type `/` in the editor to trigger commands. [Extensions](#extensions) can regist
|
|
|
194
194
|
| `/share` | Upload as private GitHub gist with shareable HTML link |
|
|
195
195
|
| `/reload` | Reload keybindings, extensions, skills, prompts, and context files (themes hot-reload automatically) |
|
|
196
196
|
| `/hotkeys` | Show all keyboard shortcuts |
|
|
197
|
-
| `/changelog` | Display version history |
|
|
197
|
+
| `/changelog` | Display the full version history |
|
|
198
198
|
| `/exit` | Exit Atomic |
|
|
199
199
|
| `/quit` | Quit Atomic |
|
|
200
200
|
|
|
@@ -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-alpha.1] - 2026-10-21
|
|
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.40] - 2026-10-20
|
|
8
20
|
|
|
9
21
|
### Added
|
|
@@ -373,7 +373,7 @@ Only registered in sessions where `pi-subagents` supplied the required child bri
|
|
|
373
373
|
|
|
374
374
|
**`groups`** — Lists every group represented by a connected session, with session counts and membership markers, so callers can discover names instead of guessing.
|
|
375
375
|
|
|
376
|
-
**`list`** — Keeps session-listing semantics: returns the current session and every active session sharing at least one membership. Pass `group` for a read-only view of one group.
|
|
376
|
+
**`list`** — Keeps session-listing semantics: returns the current session and every active session sharing at least one membership. Pass `group` for a read-only view of one group. A workflow invocation group (`workflow:<rootRunId>`) is visible only to its members, so peeking at one you have not joined says so and tells you to `join` it instead of listing it as empty.
|
|
377
377
|
|
|
378
378
|
Live target lookup accepts an exact full Intercom session ID, a unique 8-hex prefix of a UUID-backed session, or an exact case-insensitive session name. Prefix matching stays inside the sender's existing authorized visibility; collisions list the matching full UUIDs and require one of them. Join `workflow:<rootRunId>` and use `intercom list` to see materialized `PENDING`/`RUNNING` workflow stages with canonical `workflow:<rootRunId>/<segment>[/<segment>...]` paths, plus possible future literals, globs, nested paths, and their queued counts. `*` matches one segment and `**` any depth. `workflow:<rootRunId>/**` reaches live stages now and remains sticky for every future stage until root termination; narrower patterns behave the same for their matches. A valid path outside the known set queues with `notInKnownSet` and settles undeliverable at terminal only if never delivered. Use `ask` only on live targets. An eligible invocation member has directional list/send/live-ask control over stages in owned `workflow:<rootRunId>/...` subgroups. A subgroup stage or another workflow root cannot turn a mutable join into parent control; sibling subgroups stay isolated, and explicit `group: "default"` remains non-owned.
|
|
379
379
|
|
|
@@ -71,10 +71,9 @@ import { handleBrokerPresence } from "./presence-handler.js";
|
|
|
71
71
|
import {
|
|
72
72
|
knownGroupSummaries,
|
|
73
73
|
sessionGroups,
|
|
74
|
-
sessionsInGroup,
|
|
75
|
-
sessionsVisibleTo,
|
|
76
74
|
setSessionGroups,
|
|
77
75
|
} from "./group-membership.js";
|
|
76
|
+
import { replyToList } from "./list-reply.js";
|
|
78
77
|
import { PendingQuestionIndex } from "./pending-question-index.js";
|
|
79
78
|
import { type ReceiptSender, RecipientReceipts, UNCERTAIN_RECEIPT_REASON } from "./recipient-receipts.js";
|
|
80
79
|
import { matchStagePathSegments } from "../workflow-stage-path-matching.js";
|
|
@@ -1567,6 +1566,7 @@ class IntercomBroker {
|
|
|
1567
1566
|
sessionId: id,
|
|
1568
1567
|
...(supervisorId ? { supervisorSessionId: supervisorId } : {}),
|
|
1569
1568
|
...(clientMessage.acknowledgesReceipt === true ? { acknowledgesReceipt: true as const } : {}),
|
|
1569
|
+
acknowledgesPendingStageRoutes: true as const,
|
|
1570
1570
|
});
|
|
1571
1571
|
if (isAgentRecipient(info)) this.broadcastToMemberships({ type: "session_joined", session: info }, sessionGroups(info), id);
|
|
1572
1572
|
// #3761: messages written to this recipient's previous connection that it never confirmed saving go out again.
|
|
@@ -1636,25 +1636,18 @@ class IntercomBroker {
|
|
|
1636
1636
|
if (requester === undefined) throw new Error("Session not found");
|
|
1637
1637
|
const requestId = clientMessage.requestId;
|
|
1638
1638
|
const selectedGroup = clientMessage.group;
|
|
1639
|
-
const visible = (sessions: ReadonlyMap<string, Pick<ConnectedSession, "info">>): SessionInfo[] =>
|
|
1640
|
-
typeof selectedGroup === "string"
|
|
1641
|
-
? this.canInspectSelectedGroup(requester, selectedGroup)
|
|
1642
|
-
? sessionsInGroup(sessions, selectedGroup)
|
|
1643
|
-
: []
|
|
1644
|
-
: sessionsVisibleTo(sessions, requester.info);
|
|
1645
|
-
const respond = (sessions: SessionInfo[]): void => {
|
|
1646
|
-
writeMessageIfOpen(socket, {
|
|
1647
|
-
type: "sessions",
|
|
1648
|
-
requestId,
|
|
1649
|
-
sessions,
|
|
1650
|
-
workflowStages: this.workflowStagesVisibleTo(requester, selectedGroup),
|
|
1651
|
-
workflowFutureStages: this.workflowFutureStagesVisibleTo(requester, selectedGroup),
|
|
1652
|
-
});
|
|
1653
|
-
};
|
|
1654
1639
|
const multiNode = this.multiNode;
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1640
|
+
replyToList({
|
|
1641
|
+
requestId,
|
|
1642
|
+
requesterInfo: requester.info,
|
|
1643
|
+
selectedGroup,
|
|
1644
|
+
localSessions: () => this.sessions,
|
|
1645
|
+
remoteSessions: multiNode === undefined ? undefined : () => multiNode.remoteSessions(),
|
|
1646
|
+
canInspectSelectedGroup: (group) => this.canInspectSelectedGroup(requester, group),
|
|
1647
|
+
workflowStages: () => this.workflowStagesVisibleTo(requester, selectedGroup),
|
|
1648
|
+
workflowFutureStages: () => this.workflowFutureStagesVisibleTo(requester, selectedGroup),
|
|
1649
|
+
write: (reply) => writeMessageIfOpen(socket, reply),
|
|
1650
|
+
});
|
|
1658
1651
|
break;
|
|
1659
1652
|
}
|
|
1660
1653
|
|
|
@@ -1772,10 +1765,18 @@ class IntercomBroker {
|
|
|
1772
1765
|
!clientMessage.runId ||
|
|
1773
1766
|
typeof clientMessage.group !== "string" ||
|
|
1774
1767
|
typeof clientMessage.capability !== "string" ||
|
|
1775
|
-
!clientMessage.capability
|
|
1768
|
+
!clientMessage.capability ||
|
|
1769
|
+
(clientMessage.requestId !== undefined && typeof clientMessage.requestId !== "string")
|
|
1776
1770
|
) {
|
|
1777
1771
|
throw new Error("Invalid pending-stage route registration");
|
|
1778
1772
|
}
|
|
1773
|
+
const routeRequestId = clientMessage.requestId;
|
|
1774
|
+
// The route is applied synchronously, so this answer proves the update is visible to every later request.
|
|
1775
|
+
const acknowledgeRoute = (): void => {
|
|
1776
|
+
if (routeRequestId !== undefined) {
|
|
1777
|
+
writeMessageIfOpen(socket, { type: "pending_stage_route_registered", requestId: routeRequestId });
|
|
1778
|
+
}
|
|
1779
|
+
};
|
|
1779
1780
|
const owner = this.sessions.get(currentId);
|
|
1780
1781
|
const existing = this.pendingStageRoutes.get(clientMessage.runId);
|
|
1781
1782
|
const ownerGroup = normalizeGroup(owner?.registrationGroup);
|
|
@@ -1854,6 +1855,7 @@ class IntercomBroker {
|
|
|
1854
1855
|
});
|
|
1855
1856
|
this.retireAmbiguousNameAliases(clientMessage.runId);
|
|
1856
1857
|
}
|
|
1858
|
+
acknowledgeRoute();
|
|
1857
1859
|
break;
|
|
1858
1860
|
}
|
|
1859
1861
|
this.pendingStageRoutes.set(clientMessage.runId, {
|
|
@@ -1877,6 +1879,7 @@ class IntercomBroker {
|
|
|
1877
1879
|
});
|
|
1878
1880
|
this.retireAmbiguousNameAliases(clientMessage.runId);
|
|
1879
1881
|
}
|
|
1882
|
+
acknowledgeRoute();
|
|
1880
1883
|
break;
|
|
1881
1884
|
}
|
|
1882
1885
|
|
|
@@ -20,11 +20,15 @@ import { buildSendSignature, PendingSendRegistry } from "./pending-send-registry
|
|
|
20
20
|
import { readSubagentMessageSource } from "../source-ownership.js";
|
|
21
21
|
import { isMessage, isSessionInfo } from "./client-message-validation.js";
|
|
22
22
|
import { normalizeGroups } from "../group.js";
|
|
23
|
-
import { IntercomClientDisconnectedError } from "../recoverable-disconnect.js";
|
|
23
|
+
import { IntercomBrokerTimeoutError, IntercomClientDisconnectedError } from "../recoverable-disconnect.js";
|
|
24
24
|
import { IntercomLiveRouteRefusedError, isLiveWorkflowStageRouteRefusalCode } from "../live-route-refusal.js";
|
|
25
25
|
import { SEND_RESPONSE_TIMEOUT_MS } from "../retry-policy.js";
|
|
26
26
|
|
|
27
27
|
const BROKER_SOCKET = getBrokerSocketPath();
|
|
28
|
+
/** How long `connect()` waits for the broker to answer `register`. */
|
|
29
|
+
export const BROKER_CONNECT_TIMEOUT_MS = 10_000;
|
|
30
|
+
/** How long an acknowledged pending-stage route registration waits for the broker's answer. */
|
|
31
|
+
export const PENDING_STAGE_ROUTE_ACK_TIMEOUT_MS = 10_000;
|
|
28
32
|
const GROUP_REQUEST_TIMEOUT_MS = 5000;
|
|
29
33
|
const PRESENCE_ACK_TIMEOUT_MS = 5000;
|
|
30
34
|
const RESTORING_RECIPIENT_REQUEST_TIMEOUT_MS = 5000;
|
|
@@ -53,6 +57,8 @@ export interface IntercomClientOptions {
|
|
|
53
57
|
readonly preferredSessionId?: string;
|
|
54
58
|
/** #3761: this client confirms each received message with `acknowledgeReceived` once it has saved it. */
|
|
55
59
|
readonly acknowledgesReceipt?: true;
|
|
60
|
+
/** Registration answer bound for `connect()`; defaults to `BROKER_CONNECT_TIMEOUT_MS`. */
|
|
61
|
+
readonly connectTimeoutMs?: number;
|
|
56
62
|
}
|
|
57
63
|
|
|
58
64
|
export interface SendResult {
|
|
@@ -164,8 +170,11 @@ export class IntercomClient extends EventEmitter {
|
|
|
164
170
|
private readonly returnAddress: string;
|
|
165
171
|
private readonly preferredSessionId: string | undefined;
|
|
166
172
|
private readonly acknowledgesReceipt: boolean;
|
|
173
|
+
private readonly connectTimeoutMs: number;
|
|
167
174
|
/** #3761: the broker of the current registration echoed `acknowledgesReceipt`, so it understands `received`. */
|
|
168
175
|
private brokerAcknowledgesReceipt = false;
|
|
176
|
+
/** The broker of the current registration answers a pending-stage route registration that carries a `requestId`. */
|
|
177
|
+
private brokerAcknowledgesPendingStageRoutes = false;
|
|
169
178
|
private socket: net.Socket | null = null;
|
|
170
179
|
private _sessionId: string | null = null;
|
|
171
180
|
private _supervisorSessionId: string | null = null;
|
|
@@ -183,6 +192,7 @@ export class IntercomClient extends EventEmitter {
|
|
|
183
192
|
private pendingMemberships = new Map<string, { resolve: (groups: string[]) => void; reject: (error: Error) => void }>();
|
|
184
193
|
private _groups = normalizeGroups();
|
|
185
194
|
private pendingLiveWorkflowStageRoutes = new Map<string, { resolve(): void; reject(error: Error): void }>();
|
|
195
|
+
private pendingStageRouteRegistrations = new Map<string, { resolve(): void; reject(error: Error): void }>();
|
|
186
196
|
|
|
187
197
|
private pendingSupervisorAuthorizations = new Map<string, {
|
|
188
198
|
resolve: (authorization: SupervisorAuthorization) => void;
|
|
@@ -198,6 +208,7 @@ export class IntercomClient extends EventEmitter {
|
|
|
198
208
|
this.returnAddress = returnAddress;
|
|
199
209
|
this.preferredSessionId = options.preferredSessionId;
|
|
200
210
|
this.acknowledgesReceipt = options.acknowledgesReceipt === true;
|
|
211
|
+
this.connectTimeoutMs = options.connectTimeoutMs ?? BROKER_CONNECT_TIMEOUT_MS;
|
|
201
212
|
}
|
|
202
213
|
|
|
203
214
|
private failPending(error: Error): void {
|
|
@@ -209,6 +220,7 @@ export class IntercomClient extends EventEmitter {
|
|
|
209
220
|
for (const pending of this.pendingPresence.values()) pending.reject(error);
|
|
210
221
|
for (const pending of this.pendingMemberships.values()) pending.reject(error);
|
|
211
222
|
for (const pending of this.pendingLiveWorkflowStageRoutes.values()) pending.reject(error);
|
|
223
|
+
for (const pending of this.pendingStageRouteRegistrations.values()) pending.reject(error);
|
|
212
224
|
this.pendingLists.clear();
|
|
213
225
|
this.pendingGroupLists.clear();
|
|
214
226
|
this.pendingSupervisorAuthorizations.clear();
|
|
@@ -216,6 +228,7 @@ export class IntercomClient extends EventEmitter {
|
|
|
216
228
|
this.pendingPresence.clear();
|
|
217
229
|
this.pendingMemberships.clear();
|
|
218
230
|
this.pendingLiveWorkflowStageRoutes.clear();
|
|
231
|
+
this.pendingStageRouteRegistrations.clear();
|
|
219
232
|
}
|
|
220
233
|
|
|
221
234
|
|
|
@@ -279,9 +292,9 @@ export class IntercomClient extends EventEmitter {
|
|
|
279
292
|
this.socket = null;
|
|
280
293
|
}
|
|
281
294
|
socket.destroy();
|
|
282
|
-
reject(new
|
|
295
|
+
reject(new IntercomBrokerTimeoutError("Connection timeout"));
|
|
283
296
|
}
|
|
284
|
-
},
|
|
297
|
+
}, this.connectTimeoutMs);
|
|
285
298
|
|
|
286
299
|
let connectionEstablished = false;
|
|
287
300
|
|
|
@@ -318,6 +331,7 @@ export class IntercomClient extends EventEmitter {
|
|
|
318
331
|
this._sessionId = null;
|
|
319
332
|
this._supervisorSessionId = null;
|
|
320
333
|
this.brokerAcknowledgesReceipt = false;
|
|
334
|
+
this.brokerAcknowledgesPendingStageRoutes = false;
|
|
321
335
|
this._messageSource = undefined;
|
|
322
336
|
this.disconnectError = null;
|
|
323
337
|
if (connectionEstablished && !wasDisconnecting) {
|
|
@@ -425,7 +439,8 @@ export class IntercomClient extends EventEmitter {
|
|
|
425
439
|
case "registered": {
|
|
426
440
|
if (typeof brokerMessage.sessionId !== "string"
|
|
427
441
|
|| (brokerMessage.supervisorSessionId !== undefined && typeof brokerMessage.supervisorSessionId !== "string")
|
|
428
|
-
|| (brokerMessage.acknowledgesReceipt !== undefined && brokerMessage.acknowledgesReceipt !== true)
|
|
442
|
+
|| (brokerMessage.acknowledgesReceipt !== undefined && brokerMessage.acknowledgesReceipt !== true)
|
|
443
|
+
|| (brokerMessage.acknowledgesPendingStageRoutes !== undefined && brokerMessage.acknowledgesPendingStageRoutes !== true)) {
|
|
429
444
|
throw new Error("Invalid registered message");
|
|
430
445
|
}
|
|
431
446
|
|
|
@@ -438,6 +453,7 @@ export class IntercomClient extends EventEmitter {
|
|
|
438
453
|
: null;
|
|
439
454
|
// #3761: a broker from before receipts never echoes the capability and drops a client that sends `received`.
|
|
440
455
|
this.brokerAcknowledgesReceipt = this.acknowledgesReceipt && brokerMessage.acknowledgesReceipt === true;
|
|
456
|
+
this.brokerAcknowledgesPendingStageRoutes = brokerMessage.acknowledgesPendingStageRoutes === true;
|
|
441
457
|
this.emit("_registered", { type: "registered", sessionId: brokerMessage.sessionId });
|
|
442
458
|
break;
|
|
443
459
|
}
|
|
@@ -480,13 +496,14 @@ export class IntercomClient extends EventEmitter {
|
|
|
480
496
|
break;
|
|
481
497
|
}
|
|
482
498
|
case "sessions": {
|
|
483
|
-
const { requestId, sessions, workflowStages, workflowFutureStages } = brokerMessage;
|
|
499
|
+
const { requestId, sessions, workflowStages, workflowFutureStages, restricted } = brokerMessage;
|
|
484
500
|
if (
|
|
485
501
|
typeof requestId !== "string" ||
|
|
486
502
|
!Array.isArray(sessions) ||
|
|
487
503
|
!sessions.every(isSessionInfo) ||
|
|
488
504
|
(workflowStages !== undefined && !isWorkflowStageRosterEntries(workflowStages)) ||
|
|
489
|
-
(workflowFutureStages !== undefined && !isWorkflowFutureStageRosterEntries(workflowFutureStages))
|
|
505
|
+
(workflowFutureStages !== undefined && !isWorkflowFutureStageRosterEntries(workflowFutureStages)) ||
|
|
506
|
+
(restricted !== undefined && restricted !== true)
|
|
490
507
|
) {
|
|
491
508
|
throw new Error("Invalid sessions message");
|
|
492
509
|
}
|
|
@@ -497,6 +514,7 @@ export class IntercomClient extends EventEmitter {
|
|
|
497
514
|
sessions,
|
|
498
515
|
workflowStages: workflowStages ?? [],
|
|
499
516
|
workflowFutureStages: workflowFutureStages ?? [],
|
|
517
|
+
...(restricted === true ? { restricted: true as const } : {}),
|
|
500
518
|
});
|
|
501
519
|
break;
|
|
502
520
|
}
|
|
@@ -604,6 +622,15 @@ export class IntercomClient extends EventEmitter {
|
|
|
604
622
|
this.emit("pending_stage_route_revoked", { runId });
|
|
605
623
|
break;
|
|
606
624
|
}
|
|
625
|
+
case "pending_stage_route_registered": {
|
|
626
|
+
const { requestId } = brokerMessage;
|
|
627
|
+
if (typeof requestId !== "string") throw new Error("Invalid pending-stage route registration acknowledgement");
|
|
628
|
+
const pending = this.pendingStageRouteRegistrations.get(requestId);
|
|
629
|
+
if (!pending) return;
|
|
630
|
+
this.pendingStageRouteRegistrations.delete(requestId);
|
|
631
|
+
pending.resolve();
|
|
632
|
+
break;
|
|
633
|
+
}
|
|
607
634
|
case "live_workflow_stage_route_registered": {
|
|
608
635
|
const { requestId } = brokerMessage;
|
|
609
636
|
if (typeof requestId !== "string") throw new Error("Invalid live workflow-stage route registration");
|
|
@@ -1029,6 +1056,70 @@ export class IntercomClient extends EventEmitter {
|
|
|
1029
1056
|
});
|
|
1030
1057
|
}
|
|
1031
1058
|
|
|
1059
|
+
/**
|
|
1060
|
+
* Register a pending-stage route and settle once the broker has applied it, so a request on any other
|
|
1061
|
+
* socket observes the update. A broker that does not advertise the answer is covered by a session listing
|
|
1062
|
+
* on this socket, which that broker only answers after the registration ahead of it.
|
|
1063
|
+
*/
|
|
1064
|
+
registerPendingStageRouteAcknowledged(
|
|
1065
|
+
runId: string,
|
|
1066
|
+
group: string,
|
|
1067
|
+
capability: string,
|
|
1068
|
+
stages?: WorkflowStageRosterAnnouncement[],
|
|
1069
|
+
possibleStages?: WorkflowPossibleStageAnnouncement[],
|
|
1070
|
+
parent?: WorkflowRunParentAnnouncement,
|
|
1071
|
+
dormant?: boolean,
|
|
1072
|
+
): Promise<void> {
|
|
1073
|
+
let socket: net.Socket;
|
|
1074
|
+
try {
|
|
1075
|
+
socket = this.requireActiveSocket();
|
|
1076
|
+
} catch (error) {
|
|
1077
|
+
return Promise.reject(toError(error));
|
|
1078
|
+
}
|
|
1079
|
+
if (!this.brokerAcknowledgesPendingStageRoutes) {
|
|
1080
|
+
try {
|
|
1081
|
+
this.registerPendingStageRoute(runId, group, capability, stages, possibleStages, parent, dormant);
|
|
1082
|
+
} catch (error) {
|
|
1083
|
+
return Promise.reject(toError(error));
|
|
1084
|
+
}
|
|
1085
|
+
return this.listSessions().then(() => undefined);
|
|
1086
|
+
}
|
|
1087
|
+
return new Promise((resolve, reject) => {
|
|
1088
|
+
const requestId = randomUUID();
|
|
1089
|
+
const timeout = setTimeout(() => {
|
|
1090
|
+
if (!this.pendingStageRouteRegistrations.delete(requestId)) return;
|
|
1091
|
+
reject(new IntercomBrokerTimeoutError("Pending-stage route registration timeout"));
|
|
1092
|
+
}, PENDING_STAGE_ROUTE_ACK_TIMEOUT_MS);
|
|
1093
|
+
this.pendingStageRouteRegistrations.set(requestId, {
|
|
1094
|
+
resolve: () => {
|
|
1095
|
+
clearTimeout(timeout);
|
|
1096
|
+
resolve();
|
|
1097
|
+
},
|
|
1098
|
+
reject: (error) => {
|
|
1099
|
+
clearTimeout(timeout);
|
|
1100
|
+
reject(error);
|
|
1101
|
+
},
|
|
1102
|
+
});
|
|
1103
|
+
try {
|
|
1104
|
+
writeMessage(socket, {
|
|
1105
|
+
type: "register_pending_stage_route",
|
|
1106
|
+
requestId,
|
|
1107
|
+
runId,
|
|
1108
|
+
group,
|
|
1109
|
+
capability,
|
|
1110
|
+
stages,
|
|
1111
|
+
possibleStages,
|
|
1112
|
+
...(parent === undefined ? {} : { parent }),
|
|
1113
|
+
...(dormant === true ? { dormant: true as const } : {}),
|
|
1114
|
+
});
|
|
1115
|
+
} catch (error) {
|
|
1116
|
+
clearTimeout(timeout);
|
|
1117
|
+
this.pendingStageRouteRegistrations.delete(requestId);
|
|
1118
|
+
reject(toError(error));
|
|
1119
|
+
}
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1032
1123
|
registerLiveWorkflowStageRoute(runId: string, stageKeys: readonly string[], capability: string): Promise<void> {
|
|
1033
1124
|
let socket: net.Socket;
|
|
1034
1125
|
try {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { SessionInfo, WorkflowFutureStageRosterEntry, WorkflowStageRosterEntry } from "../types.js";
|
|
2
|
+
import { sessionsInGroup, sessionsVisibleTo } from "./group-membership.js";
|
|
3
|
+
|
|
4
|
+
type SessionDirectoryView = ReadonlyMap<string, { readonly info: SessionInfo }>;
|
|
5
|
+
|
|
6
|
+
export interface SessionsReply {
|
|
7
|
+
readonly type: "sessions";
|
|
8
|
+
readonly requestId: string;
|
|
9
|
+
readonly sessions: SessionInfo[];
|
|
10
|
+
readonly workflowStages: WorkflowStageRosterEntry[];
|
|
11
|
+
readonly workflowFutureStages: WorkflowFutureStageRosterEntry[];
|
|
12
|
+
readonly restricted?: true;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ListReplyRequest {
|
|
16
|
+
readonly requestId: string;
|
|
17
|
+
readonly requesterInfo: SessionInfo;
|
|
18
|
+
readonly selectedGroup: string | undefined;
|
|
19
|
+
readonly localSessions: () => SessionDirectoryView;
|
|
20
|
+
/** Present only in multi-node mode; sessions connected through other nodes are live peers too. */
|
|
21
|
+
readonly remoteSessions: (() => Promise<SessionDirectoryView>) | undefined;
|
|
22
|
+
readonly canInspectSelectedGroup: (selectedGroup: string) => boolean;
|
|
23
|
+
readonly workflowStages: () => WorkflowStageRosterEntry[];
|
|
24
|
+
readonly workflowFutureStages: () => WorkflowFutureStageRosterEntry[];
|
|
25
|
+
readonly write: (reply: SessionsReply) => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The restriction is decided when the reply is built, after any remote read, so a group joined while that read was
|
|
30
|
+
* pending is honoured and the message, rows and `restricted` flag always describe the same decision.
|
|
31
|
+
*/
|
|
32
|
+
export function replyToList(request: ListReplyRequest): void {
|
|
33
|
+
const respond = (remote: SessionDirectoryView | undefined): void => {
|
|
34
|
+
const { selectedGroup } = request;
|
|
35
|
+
const restricted = selectedGroup !== undefined && !request.canInspectSelectedGroup(selectedGroup);
|
|
36
|
+
const visible = (sessions: SessionDirectoryView): SessionInfo[] =>
|
|
37
|
+
restricted
|
|
38
|
+
? []
|
|
39
|
+
: selectedGroup !== undefined
|
|
40
|
+
? sessionsInGroup(sessions, selectedGroup)
|
|
41
|
+
: sessionsVisibleTo(sessions, request.requesterInfo);
|
|
42
|
+
request.write({
|
|
43
|
+
type: "sessions",
|
|
44
|
+
requestId: request.requestId,
|
|
45
|
+
sessions: [...visible(request.localSessions()), ...(remote === undefined ? [] : visible(remote))],
|
|
46
|
+
workflowStages: restricted ? [] : request.workflowStages(),
|
|
47
|
+
workflowFutureStages: restricted ? [] : request.workflowFutureStages(),
|
|
48
|
+
...(restricted ? { restricted: true as const } : {}),
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
if (request.remoteSessions === undefined) respond(undefined);
|
|
52
|
+
else void request.remoteSessions().then(respond);
|
|
53
|
+
}
|