@bastani/atomic 0.9.39 → 0.9.40-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.
- package/CHANGELOG.md +28 -0
- package/dist/builtin/intercom/CHANGELOG.md +14 -0
- package/dist/builtin/intercom/broker/broker.ts +523 -72
- package/dist/builtin/intercom/broker/client.ts +136 -5
- package/dist/builtin/intercom/broker/database-wake-up-bus.ts +274 -0
- package/dist/builtin/intercom/broker/group-membership.ts +2 -2
- package/dist/builtin/intercom/broker/multinode-router.ts +1500 -0
- package/dist/builtin/intercom/broker/multinode-state-store.ts +569 -0
- package/dist/builtin/intercom/broker/open-broker-state.ts +160 -0
- package/dist/builtin/intercom/broker/paths.ts +6 -0
- package/dist/builtin/intercom/broker/pending-send-registry.ts +14 -0
- package/dist/builtin/intercom/broker/recipient-receipts.ts +93 -0
- package/dist/builtin/intercom/broker/restoring-recipients.ts +263 -0
- package/dist/builtin/intercom/broker/send-handler.ts +241 -25
- package/dist/builtin/intercom/broker/session-id-reclaim.ts +41 -0
- package/dist/builtin/intercom/broker/socket-writes.ts +47 -0
- package/dist/builtin/intercom/broker/spawn.ts +10 -2
- package/dist/builtin/intercom/broker/sql-held-send-store.ts +96 -0
- package/dist/builtin/intercom/broker/sql-known-session-store.ts +129 -0
- package/dist/builtin/intercom/broker/sql-lease-store.ts +194 -0
- package/dist/builtin/intercom/broker/sql-outbox-store.ts +249 -0
- package/dist/builtin/intercom/broker/sql-pending-ask-store.ts +105 -0
- package/dist/builtin/intercom/broker/sql-restoring-reservation-store.ts +295 -0
- package/dist/builtin/intercom/broker/sql-sent-message-authority.ts +126 -0
- package/dist/builtin/intercom/broker/supervisor-channel.ts +10 -0
- package/dist/builtin/intercom/broker/wake-up-bus.ts +60 -0
- package/dist/builtin/intercom/index.bundle.mjs +793 -91
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/intercom/retry-policy.ts +4 -0
- package/dist/builtin/intercom/session-target.ts +24 -0
- package/dist/builtin/intercom/types.ts +57 -3
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +31 -0
- package/dist/builtin/subagents/README.md +18 -12
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/skills/agent-browser/SKILL.md +1 -1
- package/dist/builtin/subagents/skills/cua-driver/BROWSER.md +55 -8
- package/dist/builtin/subagents/skills/cua-driver/EMBEDDING.md +7 -2
- package/dist/builtin/subagents/skills/cua-driver/LINUX.md +7 -1
- package/dist/builtin/subagents/skills/cua-driver/MACOS.md +151 -27
- package/dist/builtin/subagents/skills/cua-driver/RUNTIME.md +1 -1
- package/dist/builtin/subagents/skills/cua-driver/SKILL.md +33 -17
- package/dist/builtin/subagents/skills/cua-driver/WINDOWS.md +5 -0
- package/dist/builtin/subagents/skills/cua-driver/WORKFLOW.md +46 -8
- package/dist/builtin/subagents/skills/subagent/SKILL.md +18 -20
- package/dist/builtin/subagents/src/extension/index.bundle.mjs +8032 -6034
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +18 -0
- package/dist/builtin/workflows/README.md +1 -1
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/skills/impeccable/SKILL.md +8 -8
- package/dist/builtin/workflows/skills/impeccable/reference/adapt.md +3 -3
- package/dist/builtin/workflows/skills/impeccable/reference/adapt.native.md +1 -1
- package/dist/builtin/workflows/skills/impeccable/reference/audit.md +4 -2
- package/dist/builtin/workflows/skills/impeccable/reference/audit.native.md +2 -2
- package/dist/builtin/workflows/skills/impeccable/reference/craft-floor.md +1 -1
- package/dist/builtin/workflows/skills/impeccable/reference/critique.md +22 -9
- package/dist/builtin/workflows/skills/impeccable/reference/degraded/finish-reviewer.md +2 -2
- package/dist/builtin/workflows/skills/impeccable/reference/distill.md +3 -3
- package/dist/builtin/workflows/skills/impeccable/reference/extract.md +3 -3
- package/dist/builtin/workflows/skills/impeccable/reference/harden.md +2 -4
- package/dist/builtin/workflows/skills/impeccable/reference/hooks.md +4 -4
- package/dist/builtin/workflows/skills/impeccable/reference/live.md +1 -1
- package/dist/builtin/workflows/skills/impeccable/reference/new-work.md +6 -6
- package/dist/builtin/workflows/skills/impeccable/reference/onboard.md +3 -3
- package/dist/builtin/workflows/skills/impeccable/reference/optimize.md +3 -3
- package/dist/builtin/workflows/skills/impeccable/reference/overdrive.md +3 -3
- package/dist/builtin/workflows/skills/impeccable/reference/quieter.md +3 -3
- package/dist/builtin/workflows/skills/impeccable/reference/typeset.md +10 -0
- package/dist/builtin/workflows/skills/impeccable/reference/visualize.md +1 -1
- package/dist/builtin/workflows/skills/impeccable/scripts/VERSION +1 -1
- package/dist/builtin/workflows/skills/impeccable/scripts/impeccable +6 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/impeccable.cmd +47 -22
- package/dist/builtin/workflows/skills/impeccable/scripts/live-browser-dom.js +34 -1
- package/dist/builtin/workflows/skills/impeccable/scripts/live-browser-session.js +25 -1
- package/dist/builtin/workflows/skills/impeccable/scripts/live-browser.js +33 -8
- package/dist/builtin/workflows/src/durable/journal/resolution.d.ts +7 -7
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +22855 -22308
- package/dist/builtin/workflows/src/index.js +106 -35
- package/dist/cli/args.d.ts +2 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +16 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session-extension-bindings.d.ts +2 -0
- package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
- package/dist/core/agent-session-extension-bindings.js +6 -6
- package/dist/core/agent-session-extension-bindings.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +6 -3
- 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-persistent-custom-messages.d.ts +7 -2
- package/dist/core/agent-session-persistent-custom-messages.d.ts.map +1 -1
- package/dist/core/agent-session-persistent-custom-messages.js +29 -12
- package/dist/core/agent-session-persistent-custom-messages.js.map +1 -1
- package/dist/core/agent-session-recovery.d.ts +14 -0
- package/dist/core/agent-session-recovery.d.ts.map +1 -0
- package/dist/core/agent-session-recovery.js +83 -0
- package/dist/core/agent-session-recovery.js.map +1 -0
- package/dist/core/agent-session-tasks.d.ts.map +1 -1
- package/dist/core/agent-session-tasks.js +4 -6
- package/dist/core/agent-session-tasks.js.map +1 -1
- package/dist/core/agent-session-types.d.ts +13 -0
- package/dist/core/agent-session-types.d.ts.map +1 -1
- package/dist/core/agent-session-types.js.map +1 -1
- package/dist/core/agent-session.d.ts +2 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +4 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/api-types.d.ts +6 -2
- package/dist/core/extensions/api-types.d.ts.map +1 -1
- package/dist/core/extensions/api-types.js.map +1 -1
- package/dist/core/extensions/event-results.d.ts +21 -0
- package/dist/core/extensions/event-results.d.ts.map +1 -1
- package/dist/core/extensions/event-results.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader-api.d.ts.map +1 -1
- package/dist/core/extensions/loader-api.js +4 -0
- package/dist/core/extensions/loader-api.js.map +1 -1
- package/dist/core/extensions/runner-events.d.ts +9 -2
- package/dist/core/extensions/runner-events.d.ts.map +1 -1
- package/dist/core/extensions/runner-events.js +29 -0
- package/dist/core/extensions/runner-events.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +5 -3
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +7 -2
- 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/session-events.d.ts +30 -2
- package/dist/core/extensions/session-events.d.ts.map +1 -1
- package/dist/core/extensions/session-events.js.map +1 -1
- package/dist/core/sdk-types.d.ts +3 -2
- package/dist/core/sdk-types.d.ts.map +1 -1
- package/dist/core/sdk-types.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +3 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-storage.d.ts.map +1 -1
- package/dist/core/session-storage.js +15 -9
- package/dist/core/session-storage.js.map +1 -1
- package/dist/core/storage/process-selection.d.ts +39 -0
- package/dist/core/storage/process-selection.d.ts.map +1 -0
- package/dist/core/storage/process-selection.js +154 -0
- package/dist/core/storage/process-selection.js.map +1 -0
- package/dist/core/storage/provider.d.ts +6 -1
- package/dist/core/storage/provider.d.ts.map +1 -1
- package/dist/core/storage/provider.js +3 -2
- package/dist/core/storage/provider.js.map +1 -1
- package/dist/core/storage/selection.d.ts +1 -1
- package/dist/core/storage/selection.d.ts.map +1 -1
- package/dist/core/storage/selection.js.map +1 -1
- package/dist/core/storage/session/jsonl-session-store.d.ts +2 -0
- package/dist/core/storage/session/jsonl-session-store.d.ts.map +1 -1
- package/dist/core/storage/session/jsonl-session-store.js +38 -2
- package/dist/core/storage/session/jsonl-session-store.js.map +1 -1
- package/dist/core/tasks/completion.d.ts +11 -2
- package/dist/core/tasks/completion.d.ts.map +1 -1
- package/dist/core/tasks/completion.js +40 -16
- package/dist/core/tasks/completion.js.map +1 -1
- package/dist/core/tasks/pause.d.ts +4 -1
- package/dist/core/tasks/pause.d.ts.map +1 -1
- package/dist/core/tasks/pause.js +8 -5
- package/dist/core/tasks/pause.js.map +1 -1
- package/dist/index-extensions.d.ts +1 -1
- package/dist/index-extensions.d.ts.map +1 -1
- package/dist/index-extensions.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +7 -1
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-rendering.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-rendering.js +11 -1
- package/dist/modes/interactive/components/chat-session-host-rendering.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-state.d.ts +2 -0
- package/dist/modes/interactive/components/chat-session-host-state.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-state.js +2 -0
- package/dist/modes/interactive/components/chat-session-host-state.js.map +1 -1
- package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-deferred-startup.js +4 -0
- package/dist/modes/interactive/interactive-deferred-startup.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts +2 -1
- 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-render-chat.js +3 -0
- package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
- package/dist/modes/interactive/interactive-session-routing.js +2 -0
- package/dist/modes/interactive/interactive-session-routing.js.map +1 -1
- package/dist/modes/interactive/interactive-session-runtime.d.ts +2 -1
- package/dist/modes/interactive/interactive-session-runtime.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-session-runtime.js +29 -0
- package/dist/modes/interactive/interactive-session-runtime.js.map +1 -1
- package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-startup.js +4 -1
- package/dist/modes/interactive/interactive-startup.js.map +1 -1
- package/dist/modes/interactive-engine/engine-args.d.ts.map +1 -1
- package/dist/modes/interactive-engine/engine-args.js +4 -0
- package/dist/modes/interactive-engine/engine-args.js.map +1 -1
- package/dist/modes/interactive-engine/engine-render-service.d.ts.map +1 -1
- package/dist/modes/interactive-engine/engine-render-service.js +4 -4
- package/dist/modes/interactive-engine/engine-render-service.js.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.js +4 -0
- package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
- package/dist/modes/interactive-engine/remote-renderer.js +1 -1
- package/dist/modes/interactive-engine/remote-renderer.js.map +1 -1
- package/dist/modes/rpc/rpc-client-api.d.ts +4 -1
- package/dist/modes/rpc/rpc-client-api.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client-api.js +4 -0
- package/dist/modes/rpc/rpc-client-api.js.map +1 -1
- package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-command-handler.js +3 -0
- package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
- package/dist/modes/rpc/rpc-command-timeouts.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-command-timeouts.js +1 -0
- package/dist/modes/rpc/rpc-command-timeouts.js.map +1 -1
- package/dist/modes/rpc/rpc-input-scheduler.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-input-scheduler.js +6 -1
- package/dist/modes/rpc/rpc-input-scheduler.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +12 -1
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +44 -11
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/tools-manager.js +1 -1
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/background-tasks.md +5 -3
- package/docs/computer-use.md +2 -2
- package/docs/environment-variables.md +4 -1
- package/docs/extensions/events.md +29 -2
- package/docs/getting-started/first-session.md +1 -1
- package/docs/guides/intercom.md +1 -1
- package/docs/intercom/operations.md +103 -1
- package/docs/intercom/reference.md +10 -3
- package/docs/intercom.md +8 -6
- package/docs/keybindings.md +1 -1
- package/docs/models.md +1 -1
- package/docs/providers.md +4 -2
- package/docs/reference/cli.md +5 -0
- package/docs/rpc/protocol.md +19 -0
- package/docs/rpc.md +3 -0
- package/docs/sdk/web-server.md +1 -1
- package/docs/sdk.md +5 -2
- package/docs/sessions.md +17 -1
- package/docs/settings.md +5 -3
- package/docs/skills.md +1 -1
- package/docs/storage.md +54 -3
- package/docs/subagents.md +49 -9
- package/docs/usage.md +1 -1
- package/docs/windows.md +14 -4
- package/docs/workflows/api-reference.md +1 -1
- package/docs/workflows/authoring.md +1 -1
- package/docs/workflows/operations.md +10 -6
- package/npm-shrinkwrap.json +37 -37
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools-manager.js","sourceRoot":"","sources":["../../src/utils/tools-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { type SpawnSyncReturns, spawnSync } from \"child_process\";\nimport { chmodSync, existsSync, mkdirSync, readdirSync, renameSync, rmSync } from \"fs\";\nimport { writeFile } from \"fs/promises\";\nimport { arch, platform } from \"os\";\nimport { join } from \"path\";\nimport { APP_NAME, ENV_OFFLINE, getBinDir, getEnvValue } from \"../config.js\";\nimport { createChildProcessEnvironment } from \"./child-process.ts\";\nimport { fetchWithRetry } from \"./management-http.ts\";\n\nconst TOOLS_DIR = getBinDir();\nconst NETWORK_TIMEOUT_MS = 10_000;\nconst DOWNLOAD_TIMEOUT_MS = 120_000;\n\nfunction isOfflineModeEnabled(): boolean {\n\tconst value = getEnvValue(ENV_OFFLINE);\n\tif (!value) return false;\n\treturn value === \"1\" || value.toLowerCase() === \"true\" || value.toLowerCase() === \"yes\";\n}\n\ninterface ToolConfig {\n\tname: string;\n\trepo: string; // GitHub repo (e.g., \"sharkdp/fd\")\n\tbinaryName: string; // Name of the binary inside the archive\n\tsystemBinaryNames?: string[]; // Alternative system command names to try before downloading\n\ttagPrefix: string; // Prefix for tags (e.g., \"v\" for v1.0.0, \"\" for 1.0.0)\n\tgetAssetName: (version: string, plat: string, architecture: string) => string | null;\n}\n\nconst TOOLS: Record<string, ToolConfig> = {\n\tfd: {\n\t\tname: \"fd\",\n\t\trepo: \"sharkdp/fd\",\n\t\tbinaryName: \"fd\",\n\t\tsystemBinaryNames: [\"fd\", \"fdfind\"],\n\t\ttagPrefix: \"v\",\n\t\tgetAssetName: (version, plat, architecture) => {\n\t\t\tif (plat === \"darwin\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-apple-darwin.tar.gz`;\n\t\t\t} else if (plat === \"linux\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-unknown-linux-musl.tar.gz`;\n\t\t\t} else if (plat === \"win32\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-pc-windows-msvc.zip`;\n\t\t\t}\n\t\t\treturn null;\n\t\t},\n\t},\n\trg: {\n\t\tname: \"ripgrep\",\n\t\trepo: \"BurntSushi/ripgrep\",\n\t\tbinaryName: \"rg\",\n\t\ttagPrefix: \"\",\n\t\tgetAssetName: (version, plat, architecture) => {\n\t\t\tif (plat === \"darwin\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `ripgrep-${version}-${archStr}-apple-darwin.tar.gz`;\n\t\t\t} else if (plat === \"linux\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `ripgrep-${version}-${archStr}-unknown-linux-musl.tar.gz`;\n\t\t\t} else if (plat === \"win32\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `ripgrep-${version}-${archStr}-pc-windows-msvc.zip`;\n\t\t\t}\n\t\t\treturn null;\n\t\t},\n\t},\n};\n\n// Check if a command exists in PATH by trying to run it\nfunction commandExists(cmd: string): boolean {\n\ttry {\n\t\tconst result = spawnSync(cmd, [\"--version\"], { stdio: \"pipe\", env: createChildProcessEnvironment() });\n\t\t// Check for ENOENT error (command not found)\n\t\treturn result.error === undefined || result.error === null;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n// Get the path to a tool (system-wide or in our tools dir)\nexport function getToolPath(tool: \"fd\" | \"rg\"): string | null {\n\tconst config = TOOLS[tool];\n\tif (!config) return null;\n\n\t// Check our tools directory first\n\tconst localPath = join(TOOLS_DIR, config.binaryName + (platform() === \"win32\" ? \".exe\" : \"\"));\n\tif (existsSync(localPath)) {\n\t\treturn localPath;\n\t}\n\n\t// Check system PATH - if found, just return the command name (it's in PATH)\n\tconst systemBinaryNames = config.systemBinaryNames ?? [config.binaryName];\n\tfor (const systemBinaryName of systemBinaryNames) {\n\t\tif (commandExists(systemBinaryName)) {\n\t\t\treturn systemBinaryName;\n\t\t}\n\t}\n\n\treturn null;\n}\n\n// Resolve the latest release version from the release page redirect, avoiding\n// the anonymous api.github.com quota shared by corporate proxies and CI runners.\nexport async function getLatestVersion(repo: string): Promise<string> {\n\tconst response = await fetchWithRetry(\n\t\t`https://github.com/${repo}/releases/latest`,\n\t\t{ headers: { \"User-Agent\": APP_NAME }, redirect: \"manual\" },\n\t\t{ timeoutMs: NETWORK_TIMEOUT_MS },\n\t);\n\ttry {\n\t\tawait response.body?.cancel();\n\t} catch {\n\t\t// Discarding the redirect body is best-effort.\n\t}\n\tconst location = response.status >= 300 && response.status < 400 ? response.headers.get(\"location\") : null;\n\tif (!location) throw new Error(`Failed to resolve latest ${repo} release: HTTP ${response.status} without redirect`);\n\tconst tag = new URL(location, \"https://github.com\").pathname.split(\"/\").pop();\n\tif (!tag || !location.includes(\"/releases/tag/\")) {\n\t\tthrow new Error(`Failed to resolve latest ${repo} release: unexpected redirect to ${location}`);\n\t}\n\treturn decodeURIComponent(tag).replace(/^v/, \"\");\n}\n\n// Download a file from URL\nasync function downloadFile(url: string, dest: string): Promise<void> {\n\tconst response = await fetchWithRetry(url, undefined, { timeoutMs: DOWNLOAD_TIMEOUT_MS });\n\n\tif (!response.ok) {\n\t\tthrow new Error(`Download failed with HTTP ${response.status}: ${url}`);\n\t}\n\n\tif (!response.body) {\n\t\tthrow new Error(\"No response body\");\n\t}\n\n\tawait writeFile(dest, Buffer.from(await response.arrayBuffer()));\n}\n\nfunction findBinaryRecursively(rootDir: string, binaryFileName: string): string | null {\n\tconst stack: string[] = [rootDir];\n\n\twhile (stack.length > 0) {\n\t\tconst currentDir = stack.pop();\n\t\tif (!currentDir) continue;\n\n\t\tconst entries = readdirSync(currentDir, { withFileTypes: true });\n\t\tfor (const entry of entries) {\n\t\t\tconst fullPath = join(currentDir, entry.name);\n\t\t\tif (entry.isFile() && entry.name === binaryFileName) {\n\t\t\t\treturn fullPath;\n\t\t\t}\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tstack.push(fullPath);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction formatSpawnFailure(result: SpawnSyncReturns<Buffer>): string {\n\tif (result.error?.message) {\n\t\treturn result.error.message;\n\t}\n\tconst stderr = result.stderr?.toString().trim();\n\tif (stderr) {\n\t\treturn stderr;\n\t}\n\tconst stdout = result.stdout?.toString().trim();\n\tif (stdout) {\n\t\treturn stdout;\n\t}\n\treturn `exit status ${result.status ?? \"unknown\"}`;\n}\n\nfunction runExtractionCommand(command: string, args: string[]): string | null {\n\tconst result = spawnSync(command, args, { stdio: \"pipe\", env: createChildProcessEnvironment() });\n\tif (!result.error && result.status === 0) {\n\t\treturn null;\n\t}\n\treturn `${command}: ${formatSpawnFailure(result)}`;\n}\n\nfunction extractTarGzArchive(archivePath: string, extractDir: string, assetName: string): void {\n\tconst failure = runExtractionCommand(\"tar\", [\"xzf\", archivePath, \"-C\", extractDir]);\n\tif (failure) {\n\t\tthrow new Error(`Failed to extract ${assetName}: ${failure}`);\n\t}\n}\n\nfunction getWindowsTarCommand(): string {\n\tconst systemRoot = process.env.SystemRoot ?? process.env.WINDIR;\n\tif (systemRoot) {\n\t\tconst systemTar = join(systemRoot, \"System32\", \"tar.exe\");\n\t\tif (existsSync(systemTar)) {\n\t\t\treturn systemTar;\n\t\t}\n\t}\n\treturn \"tar.exe\";\n}\n\nfunction extractZipArchive(archivePath: string, extractDir: string, assetName: string): void {\n\tconst failures: string[] = [];\n\n\tif (platform() === \"win32\") {\n\t\t// Windows ships bsdtar as tar.exe, which supports zip files. Prefer the\n\t\t// System32 binary over Git Bash's GNU tar, which does not handle zip archives.\n\t\tconst tarFailure = runExtractionCommand(getWindowsTarCommand(), [\"xf\", archivePath, \"-C\", extractDir]);\n\t\tif (!tarFailure) return;\n\t\tfailures.push(tarFailure);\n\n\t\tconst script =\n\t\t\t\"& { param($archive, $destination) $ErrorActionPreference = 'Stop'; Expand-Archive -LiteralPath $archive -DestinationPath $destination -Force }\";\n\t\tconst powershellFailure = runExtractionCommand(\"powershell.exe\", [\n\t\t\t\"-NoLogo\",\n\t\t\t\"-NoProfile\",\n\t\t\t\"-NonInteractive\",\n\t\t\t\"-ExecutionPolicy\",\n\t\t\t\"Bypass\",\n\t\t\t\"-Command\",\n\t\t\tscript,\n\t\t\tarchivePath,\n\t\t\textractDir,\n\t\t]);\n\t\tif (!powershellFailure) return;\n\t\tfailures.push(powershellFailure);\n\t} else {\n\t\tconst unzipFailure = runExtractionCommand(\"unzip\", [\"-q\", archivePath, \"-d\", extractDir]);\n\t\tif (!unzipFailure) return;\n\t\tfailures.push(unzipFailure);\n\n\t\tconst tarFailure = runExtractionCommand(\"tar\", [\"xf\", archivePath, \"-C\", extractDir]);\n\t\tif (!tarFailure) return;\n\t\tfailures.push(tarFailure);\n\t}\n\n\tthrow new Error(`Failed to extract ${assetName}: ${failures.join(\"; \")}`);\n}\n\n// Download and install a tool\nasync function downloadTool(tool: \"fd\" | \"rg\"): Promise<string> {\n\tconst config = TOOLS[tool];\n\tif (!config) throw new Error(`Unknown tool: ${tool}`);\n\n\tconst plat = platform();\n\tconst architecture = arch();\n\n\t// fd is pinned on darwin/x64; skip the version lookup there.\n\tconst version =\n\t\ttool === \"fd\" && plat === \"darwin\" && architecture === \"x64\" ? \"10.3.0\" : await getLatestVersion(config.repo);\n\n\t// Get asset name for this platform\n\tconst assetName = config.getAssetName(version, plat, architecture);\n\tif (!assetName) {\n\t\tthrow new Error(`Unsupported platform: ${plat}/${architecture}`);\n\t}\n\n\t// Create tools directory\n\tmkdirSync(TOOLS_DIR, { recursive: true });\n\n\tconst downloadUrl = `https://github.com/${config.repo}/releases/download/${config.tagPrefix}${version}/${assetName}`;\n\tconst archivePath = join(TOOLS_DIR, assetName);\n\tconst binaryExt = plat === \"win32\" ? \".exe\" : \"\";\n\tconst binaryPath = join(TOOLS_DIR, config.binaryName + binaryExt);\n\n\t// Download\n\tawait downloadFile(downloadUrl, archivePath);\n\n\t// Extract into a unique temp directory. fd and rg downloads can run concurrently\n\t// during startup, so sharing a fixed directory causes races.\n\tconst extractDir = join(\n\t\tTOOLS_DIR,\n\t\t`extract_tmp_${config.binaryName}_${process.pid}_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`,\n\t);\n\tmkdirSync(extractDir, { recursive: true });\n\n\ttry {\n\t\tif (assetName.endsWith(\".tar.gz\")) {\n\t\t\textractTarGzArchive(archivePath, extractDir, assetName);\n\t\t} else if (assetName.endsWith(\".zip\")) {\n\t\t\textractZipArchive(archivePath, extractDir, assetName);\n\t\t} else {\n\t\t\tthrow new Error(`Unsupported archive format: ${assetName}`);\n\t\t}\n\n\t\t// Find the binary in extracted files. Some archives contain files directly\n\t\t// at root, others nest under a versioned subdirectory.\n\t\tconst binaryFileName = config.binaryName + binaryExt;\n\t\tconst extractedDir = join(extractDir, assetName.replace(/\\.(tar\\.gz|zip)$/, \"\"));\n\t\tconst extractedBinaryCandidates = [join(extractedDir, binaryFileName), join(extractDir, binaryFileName)];\n\t\tlet extractedBinary = extractedBinaryCandidates.find((candidate) => existsSync(candidate));\n\n\t\tif (!extractedBinary) {\n\t\t\textractedBinary = findBinaryRecursively(extractDir, binaryFileName) ?? undefined;\n\t\t}\n\n\t\tif (extractedBinary) {\n\t\t\trenameSync(extractedBinary, binaryPath);\n\t\t} else {\n\t\t\tthrow new Error(`Binary not found in archive: expected ${binaryFileName} under ${extractDir}`);\n\t\t}\n\n\t\t// Make executable (Unix only)\n\t\tif (plat !== \"win32\") {\n\t\t\tchmodSync(binaryPath, 0o755);\n\t\t}\n\t} finally {\n\t\t// Cleanup\n\t\trmSync(archivePath, { force: true });\n\t\trmSync(extractDir, { recursive: true, force: true });\n\t}\n\n\treturn binaryPath;\n}\n\n// Termux package names for tools\nconst TERMUX_PACKAGES: Record<string, string> = {\n\tfd: \"fd\",\n\trg: \"ripgrep\",\n};\n\n/** A managed-tool readiness update, reported to the caller that asked. */\nexport interface ToolStatus {\n\ttype: \"info\" | \"warning\";\n\tmessage: string;\n}\n\n/**\n * Ensure a tool is available, downloading if necessary.\n *\n * Progress and problems are reported through `onStatus`; the function never\n * writes to the console itself, because in a fullscreen session a console\n * write lands in the alternate screen and corrupts the frame. Callers without\n * a status sink stay silent.\n *\n * Returns the tool path, or undefined if unavailable.\n */\nexport async function ensureTool(\n\ttool: \"fd\" | \"rg\",\n\tonStatus?: (status: ToolStatus) => void,\n): Promise<string | undefined> {\n\tconst existingPath = getToolPath(tool);\n\tif (existingPath) {\n\t\treturn existingPath;\n\t}\n\n\tconst config = TOOLS[tool];\n\tif (!config) return undefined;\n\n\tif (isOfflineModeEnabled()) {\n\t\tonStatus?.({ type: \"warning\", message: `${config.name} not found. Offline mode enabled, skipping download.` });\n\t\treturn undefined;\n\t}\n\n\t// On Android/Termux, Linux binaries don't work due to Bionic libc incompatibility.\n\t// Users must install via pkg.\n\tif (platform() === \"android\") {\n\t\tconst pkgName = TERMUX_PACKAGES[tool] ?? tool;\n\t\tonStatus?.({ type: \"warning\", message: `${config.name} not found. Install with: pkg install ${pkgName}` });\n\t\treturn undefined;\n\t}\n\n\t// Tool not found - download it\n\tonStatus?.({ type: \"info\", message: `${config.name} not found. Downloading...` });\n\n\ttry {\n\t\tconst path = await downloadTool(tool);\n\t\tonStatus?.({ type: \"info\", message: `${config.name} installed to ${path}` });\n\t\treturn path;\n\t} catch (e) {\n\t\t// Include the error cause chain: fetch failures surface as a bare\n\t\t// \"fetch failed\" TypeError with the actionable detail (DNS, TLS,\n\t\t// timeout) hidden in the cause. Depth-capped to guard against\n\t\t// circular cause chains.\n\t\tconst messages: string[] = [];\n\t\tfor (\n\t\t\tlet current: unknown = e, depth = 0;\n\t\t\tcurrent instanceof Error && depth < 5;\n\t\t\tcurrent = current.cause, depth++\n\t\t) {\n\t\t\tif (!messages.includes(current.message)) messages.push(current.message);\n\t\t}\n\t\tonStatus?.({\n\t\t\ttype: \"warning\",\n\t\t\tmessage: `Failed to download ${config.name}: ${messages.length > 0 ? messages.join(\": \") : String(e)}`,\n\t\t});\n\t\treturn undefined;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tools-manager.js","sourceRoot":"","sources":["../../src/utils/tools-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { type SpawnSyncReturns, spawnSync } from \"child_process\";\nimport { chmodSync, existsSync, mkdirSync, readdirSync, renameSync, rmSync } from \"fs\";\nimport { writeFile } from \"fs/promises\";\nimport { arch, platform } from \"os\";\nimport { join } from \"path\";\nimport { APP_NAME, ENV_OFFLINE, getBinDir, getEnvValue } from \"../config.js\";\nimport { createChildProcessEnvironment } from \"./child-process.ts\";\nimport { fetchWithRetry } from \"./management-http.ts\";\n\nconst TOOLS_DIR = getBinDir();\nconst NETWORK_TIMEOUT_MS = 10_000;\nconst DOWNLOAD_TIMEOUT_MS = 120_000;\n\nfunction isOfflineModeEnabled(): boolean {\n\tconst value = getEnvValue(ENV_OFFLINE);\n\tif (!value) return false;\n\treturn value === \"1\" || value.toLowerCase() === \"true\" || value.toLowerCase() === \"yes\";\n}\n\ninterface ToolConfig {\n\tname: string;\n\trepo: string; // GitHub repo (e.g., \"sharkdp/fd\")\n\tbinaryName: string; // Name of the binary inside the archive\n\tsystemBinaryNames?: string[]; // Alternative system command names to try before downloading\n\ttagPrefix: string; // Prefix for tags (e.g., \"v\" for v1.0.0, \"\" for 1.0.0)\n\tgetAssetName: (version: string, plat: string, architecture: string) => string | null;\n}\n\nconst TOOLS: Record<string, ToolConfig> = {\n\tfd: {\n\t\tname: \"fd\",\n\t\trepo: \"sharkdp/fd\",\n\t\tbinaryName: \"fd\",\n\t\tsystemBinaryNames: [\"fd\", \"fdfind\"],\n\t\ttagPrefix: \"v\",\n\t\tgetAssetName: (version, plat, architecture) => {\n\t\t\tif (plat === \"darwin\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-apple-darwin.tar.gz`;\n\t\t\t} else if (plat === \"linux\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-unknown-linux-musl.tar.gz`;\n\t\t\t} else if (plat === \"win32\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-pc-windows-msvc.zip`;\n\t\t\t}\n\t\t\treturn null;\n\t\t},\n\t},\n\trg: {\n\t\tname: \"ripgrep\",\n\t\trepo: \"BurntSushi/ripgrep\",\n\t\tbinaryName: \"rg\",\n\t\ttagPrefix: \"\",\n\t\tgetAssetName: (version, plat, architecture) => {\n\t\t\tif (plat === \"darwin\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `ripgrep-${version}-${archStr}-apple-darwin.tar.gz`;\n\t\t\t} else if (plat === \"linux\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `ripgrep-${version}-${archStr}-unknown-linux-musl.tar.gz`;\n\t\t\t} else if (plat === \"win32\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `ripgrep-${version}-${archStr}-pc-windows-msvc.zip`;\n\t\t\t}\n\t\t\treturn null;\n\t\t},\n\t},\n};\n\n// Check if a command exists in PATH by trying to run it\nfunction commandExists(cmd: string): boolean {\n\ttry {\n\t\tconst result = spawnSync(cmd, [\"--version\"], { stdio: \"pipe\", env: createChildProcessEnvironment() });\n\t\t// Check for ENOENT error (command not found)\n\t\treturn result.error === undefined || result.error === null;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n// Get the path to a tool (system-wide or in our tools dir)\nexport function getToolPath(tool: \"fd\" | \"rg\"): string | null {\n\tconst config = TOOLS[tool];\n\tif (!config) return null;\n\n\t// Check our tools directory first\n\tconst localPath = join(TOOLS_DIR, config.binaryName + (platform() === \"win32\" ? \".exe\" : \"\"));\n\tif (existsSync(localPath)) {\n\t\treturn localPath;\n\t}\n\n\t// Check system PATH - if found, just return the command name (it's in PATH)\n\tconst systemBinaryNames = config.systemBinaryNames ?? [config.binaryName];\n\tfor (const systemBinaryName of systemBinaryNames) {\n\t\tif (commandExists(systemBinaryName)) {\n\t\t\treturn systemBinaryName;\n\t\t}\n\t}\n\n\treturn null;\n}\n\n// Resolve the latest release version from the release page redirect, avoiding\n// the anonymous api.github.com quota shared by corporate proxies and CI runners.\nexport async function getLatestVersion(repo: string): Promise<string> {\n\tconst response = await fetchWithRetry(\n\t\t`https://github.com/${repo}/releases/latest`,\n\t\t{ headers: { \"User-Agent\": APP_NAME }, redirect: \"manual\" },\n\t\t{ timeoutMs: NETWORK_TIMEOUT_MS },\n\t);\n\ttry {\n\t\tawait response.body?.cancel();\n\t} catch {\n\t\t// Discarding the redirect body is best-effort.\n\t}\n\tconst location = response.status >= 300 && response.status < 400 ? response.headers.get(\"location\") : null;\n\tif (!location) throw new Error(`Failed to resolve latest ${repo} release: HTTP ${response.status} without redirect`);\n\tconst tag = new URL(location, \"https://github.com\").pathname.split(\"/\").pop();\n\tif (!tag || !location.includes(\"/releases/tag/\")) {\n\t\tthrow new Error(`Failed to resolve latest ${repo} release: unexpected redirect to ${location}`);\n\t}\n\treturn decodeURIComponent(tag).replace(/^v/, \"\");\n}\n\n// Download a file from URL\nasync function downloadFile(url: string, dest: string): Promise<void> {\n\tconst response = await fetchWithRetry(url, undefined, { timeoutMs: DOWNLOAD_TIMEOUT_MS });\n\n\tif (!response.ok) {\n\t\tthrow new Error(`Download failed with HTTP ${response.status}: ${url}`);\n\t}\n\n\tif (!response.body) {\n\t\tthrow new Error(\"No response body\");\n\t}\n\n\tawait writeFile(dest, Buffer.from(await response.arrayBuffer()));\n}\n\nfunction findBinaryRecursively(rootDir: string, binaryFileName: string): string | null {\n\tconst stack: string[] = [rootDir];\n\n\twhile (stack.length > 0) {\n\t\tconst currentDir = stack.pop();\n\t\tif (!currentDir) continue;\n\n\t\tconst entries = readdirSync(currentDir, { withFileTypes: true });\n\t\tfor (const entry of entries) {\n\t\t\tconst fullPath = join(currentDir, entry.name);\n\t\t\tif (entry.isFile() && entry.name === binaryFileName) {\n\t\t\t\treturn fullPath;\n\t\t\t}\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tstack.push(fullPath);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction formatSpawnFailure(result: SpawnSyncReturns<Buffer>): string {\n\tif (result.error?.message) {\n\t\treturn result.error.message;\n\t}\n\tconst stderr = result.stderr?.toString().trim();\n\tif (stderr) {\n\t\treturn stderr;\n\t}\n\tconst stdout = result.stdout?.toString().trim();\n\tif (stdout) {\n\t\treturn stdout;\n\t}\n\treturn `exit status ${result.status ?? \"unknown\"}`;\n}\n\nfunction runExtractionCommand(command: string, args: string[]): string | null {\n\tconst result = spawnSync(command, args, { stdio: \"pipe\", env: createChildProcessEnvironment() });\n\tif (!result.error && result.status === 0) {\n\t\treturn null;\n\t}\n\treturn `${command}: ${formatSpawnFailure(result)}`;\n}\n\nfunction extractTarGzArchive(archivePath: string, extractDir: string, assetName: string): void {\n\tconst failure = runExtractionCommand(\"tar\", [\"xzf\", archivePath, \"-C\", extractDir]);\n\tif (failure) {\n\t\tthrow new Error(`Failed to extract ${assetName}: ${failure}`);\n\t}\n}\n\nfunction getWindowsTarCommand(): string {\n\tconst systemRoot = process.env.SystemRoot;\n\tif (systemRoot) {\n\t\tconst systemTar = join(systemRoot, \"System32\", \"tar.exe\");\n\t\tif (existsSync(systemTar)) {\n\t\t\treturn systemTar;\n\t\t}\n\t}\n\treturn \"tar.exe\";\n}\n\nfunction extractZipArchive(archivePath: string, extractDir: string, assetName: string): void {\n\tconst failures: string[] = [];\n\n\tif (platform() === \"win32\") {\n\t\t// Windows ships bsdtar as tar.exe, which supports zip files. Prefer the\n\t\t// System32 binary over Git Bash's GNU tar, which does not handle zip archives.\n\t\tconst tarFailure = runExtractionCommand(getWindowsTarCommand(), [\"xf\", archivePath, \"-C\", extractDir]);\n\t\tif (!tarFailure) return;\n\t\tfailures.push(tarFailure);\n\n\t\tconst script =\n\t\t\t\"& { param($archive, $destination) $ErrorActionPreference = 'Stop'; Expand-Archive -LiteralPath $archive -DestinationPath $destination -Force }\";\n\t\tconst powershellFailure = runExtractionCommand(\"powershell.exe\", [\n\t\t\t\"-NoLogo\",\n\t\t\t\"-NoProfile\",\n\t\t\t\"-NonInteractive\",\n\t\t\t\"-ExecutionPolicy\",\n\t\t\t\"Bypass\",\n\t\t\t\"-Command\",\n\t\t\tscript,\n\t\t\tarchivePath,\n\t\t\textractDir,\n\t\t]);\n\t\tif (!powershellFailure) return;\n\t\tfailures.push(powershellFailure);\n\t} else {\n\t\tconst unzipFailure = runExtractionCommand(\"unzip\", [\"-q\", archivePath, \"-d\", extractDir]);\n\t\tif (!unzipFailure) return;\n\t\tfailures.push(unzipFailure);\n\n\t\tconst tarFailure = runExtractionCommand(\"tar\", [\"xf\", archivePath, \"-C\", extractDir]);\n\t\tif (!tarFailure) return;\n\t\tfailures.push(tarFailure);\n\t}\n\n\tthrow new Error(`Failed to extract ${assetName}: ${failures.join(\"; \")}`);\n}\n\n// Download and install a tool\nasync function downloadTool(tool: \"fd\" | \"rg\"): Promise<string> {\n\tconst config = TOOLS[tool];\n\tif (!config) throw new Error(`Unknown tool: ${tool}`);\n\n\tconst plat = platform();\n\tconst architecture = arch();\n\n\t// fd is pinned on darwin/x64; skip the version lookup there.\n\tconst version =\n\t\ttool === \"fd\" && plat === \"darwin\" && architecture === \"x64\" ? \"10.3.0\" : await getLatestVersion(config.repo);\n\n\t// Get asset name for this platform\n\tconst assetName = config.getAssetName(version, plat, architecture);\n\tif (!assetName) {\n\t\tthrow new Error(`Unsupported platform: ${plat}/${architecture}`);\n\t}\n\n\t// Create tools directory\n\tmkdirSync(TOOLS_DIR, { recursive: true });\n\n\tconst downloadUrl = `https://github.com/${config.repo}/releases/download/${config.tagPrefix}${version}/${assetName}`;\n\tconst archivePath = join(TOOLS_DIR, assetName);\n\tconst binaryExt = plat === \"win32\" ? \".exe\" : \"\";\n\tconst binaryPath = join(TOOLS_DIR, config.binaryName + binaryExt);\n\n\t// Download\n\tawait downloadFile(downloadUrl, archivePath);\n\n\t// Extract into a unique temp directory. fd and rg downloads can run concurrently\n\t// during startup, so sharing a fixed directory causes races.\n\tconst extractDir = join(\n\t\tTOOLS_DIR,\n\t\t`extract_tmp_${config.binaryName}_${process.pid}_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`,\n\t);\n\tmkdirSync(extractDir, { recursive: true });\n\n\ttry {\n\t\tif (assetName.endsWith(\".tar.gz\")) {\n\t\t\textractTarGzArchive(archivePath, extractDir, assetName);\n\t\t} else if (assetName.endsWith(\".zip\")) {\n\t\t\textractZipArchive(archivePath, extractDir, assetName);\n\t\t} else {\n\t\t\tthrow new Error(`Unsupported archive format: ${assetName}`);\n\t\t}\n\n\t\t// Find the binary in extracted files. Some archives contain files directly\n\t\t// at root, others nest under a versioned subdirectory.\n\t\tconst binaryFileName = config.binaryName + binaryExt;\n\t\tconst extractedDir = join(extractDir, assetName.replace(/\\.(tar\\.gz|zip)$/, \"\"));\n\t\tconst extractedBinaryCandidates = [join(extractedDir, binaryFileName), join(extractDir, binaryFileName)];\n\t\tlet extractedBinary = extractedBinaryCandidates.find((candidate) => existsSync(candidate));\n\n\t\tif (!extractedBinary) {\n\t\t\textractedBinary = findBinaryRecursively(extractDir, binaryFileName) ?? undefined;\n\t\t}\n\n\t\tif (extractedBinary) {\n\t\t\trenameSync(extractedBinary, binaryPath);\n\t\t} else {\n\t\t\tthrow new Error(`Binary not found in archive: expected ${binaryFileName} under ${extractDir}`);\n\t\t}\n\n\t\t// Make executable (Unix only)\n\t\tif (plat !== \"win32\") {\n\t\t\tchmodSync(binaryPath, 0o755);\n\t\t}\n\t} finally {\n\t\t// Cleanup\n\t\trmSync(archivePath, { force: true });\n\t\trmSync(extractDir, { recursive: true, force: true });\n\t}\n\n\treturn binaryPath;\n}\n\n// Termux package names for tools\nconst TERMUX_PACKAGES: Record<string, string> = {\n\tfd: \"fd\",\n\trg: \"ripgrep\",\n};\n\n/** A managed-tool readiness update, reported to the caller that asked. */\nexport interface ToolStatus {\n\ttype: \"info\" | \"warning\";\n\tmessage: string;\n}\n\n/**\n * Ensure a tool is available, downloading if necessary.\n *\n * Progress and problems are reported through `onStatus`; the function never\n * writes to the console itself, because in a fullscreen session a console\n * write lands in the alternate screen and corrupts the frame. Callers without\n * a status sink stay silent.\n *\n * Returns the tool path, or undefined if unavailable.\n */\nexport async function ensureTool(\n\ttool: \"fd\" | \"rg\",\n\tonStatus?: (status: ToolStatus) => void,\n): Promise<string | undefined> {\n\tconst existingPath = getToolPath(tool);\n\tif (existingPath) {\n\t\treturn existingPath;\n\t}\n\n\tconst config = TOOLS[tool];\n\tif (!config) return undefined;\n\n\tif (isOfflineModeEnabled()) {\n\t\tonStatus?.({ type: \"warning\", message: `${config.name} not found. Offline mode enabled, skipping download.` });\n\t\treturn undefined;\n\t}\n\n\t// On Android/Termux, Linux binaries don't work due to Bionic libc incompatibility.\n\t// Users must install via pkg.\n\tif (platform() === \"android\") {\n\t\tconst pkgName = TERMUX_PACKAGES[tool] ?? tool;\n\t\tonStatus?.({ type: \"warning\", message: `${config.name} not found. Install with: pkg install ${pkgName}` });\n\t\treturn undefined;\n\t}\n\n\t// Tool not found - download it\n\tonStatus?.({ type: \"info\", message: `${config.name} not found. Downloading...` });\n\n\ttry {\n\t\tconst path = await downloadTool(tool);\n\t\tonStatus?.({ type: \"info\", message: `${config.name} installed to ${path}` });\n\t\treturn path;\n\t} catch (e) {\n\t\t// Include the error cause chain: fetch failures surface as a bare\n\t\t// \"fetch failed\" TypeError with the actionable detail (DNS, TLS,\n\t\t// timeout) hidden in the cause. Depth-capped to guard against\n\t\t// circular cause chains.\n\t\tconst messages: string[] = [];\n\t\tfor (\n\t\t\tlet current: unknown = e, depth = 0;\n\t\t\tcurrent instanceof Error && depth < 5;\n\t\t\tcurrent = current.cause, depth++\n\t\t) {\n\t\t\tif (!messages.includes(current.message)) messages.push(current.message);\n\t\t}\n\t\tonStatus?.({\n\t\t\ttype: \"warning\",\n\t\t\tmessage: `Failed to download ${config.name}: ${messages.length > 0 ? messages.join(\": \") : String(e)}`,\n\t\t});\n\t\treturn undefined;\n\t}\n}\n"]}
|
package/docs/background-tasks.md
CHANGED
|
@@ -107,6 +107,8 @@ subagent({ action: "wait", id: taskId })
|
|
|
107
107
|
subagent({ action: "status", id: taskId })
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
+
To stop, hold, or continue a child, use `pause`, `quit`, and `resume`; see [Run control](/subagents#run-control).
|
|
111
|
+
|
|
110
112
|
Do not launch a duplicate just to retrieve its result. Use the task ID returned at launch. IDs are scoped to the session or workflow stage that owns them.
|
|
111
113
|
|
|
112
114
|
Foreground subagent launches and explicit `action: "wait"` calls also yield when user steering or an Intercom ask/send is admitted to the waiting parent, in main chat or a live workflow stage. The message stays in the normal delivery queue so the parent can handle it and reply. This releases only observation: children keep running under the same task IDs, and other owners' waits are unaffected.
|
|
@@ -125,7 +127,7 @@ The parent model also receives the result context. Delivery retries do not relau
|
|
|
125
127
|
|
|
126
128
|
Supervisor progress messages are labelled **Historical supervisor update** with their original `Sent:` timestamp. They describe the child's state when sent, not its current status, and can arrive after completion. Prefer a later correction or final result over an earlier progress hypothesis; use `/tasks` for the current task state.
|
|
127
129
|
|
|
128
|
-
Stopping a subagent through `/tasks` (`x`, then `y`) delivers a **stopped** card and stop context to the parent model. The notice arrives only after termination is confirmed, not while the task is **Stopping**. It requires no final response from the child. Cancelling queued work sends the notice without starting it.
|
|
130
|
+
Stopping a subagent through `/tasks` (`x`, then `y`) delivers a **stopped** card and stop context to the parent model. The notice arrives only after termination is confirmed, not while the task is **Stopping**. It requires no final response from the child. Cancelling queued work sends the notice without starting it. A subagent stopped this way cannot be resumed; to stop one and keep it resumable, use `subagent({ action: "quit", id })` (see [Run control](/subagents#run-control)).
|
|
129
131
|
|
|
130
132
|
Repeated stop requests or late child results do not duplicate the notice or overwrite the recorded outcome. A task that finished before cancellation keeps its actual result. Closing the owning session or workflow stage still suppresses late notices.
|
|
131
133
|
|
|
@@ -229,9 +231,9 @@ Custom operations adapters receive `wait` but must implement it themselves. Exte
|
|
|
229
231
|
|
|
230
232
|
Background means independent of the current observation, not independent of its owner. **Pausing main chat** aborts the foreground turn only; its background agents and shells keep running. Detaching a workflow pane, ending a model turn, or closing `/tasks` also leaves owned background work alone.
|
|
231
233
|
|
|
232
|
-
**Pausing a workflow stage** blocks new launches
|
|
234
|
+
**Pausing a workflow stage** blocks new launches, stops its active and queued agents, and cancels its shells. The stopped subagents stay resumable with `subagent({ action: "resume", id })`. Pause completes only after confirming cleanup; failures are reported rather than treated as a successful pause. Main-chat tasks, sibling stages, and future stage generations are unaffected.
|
|
233
235
|
|
|
234
|
-
Pause does not close the stage's message generation: queued user and Intercom messages remain held for resume. Resume permits fresh launches but never resurrects cancelled
|
|
236
|
+
Pause does not close the stage's message generation: queued user and Intercom messages remain held for resume. Resume permits fresh launches but never resurrects cancelled shells; retained task results remain inspectable. Closing a session or stage generation still cancels its remaining owned work. Explicit `/tasks` stop and declared execution timeouts remain separate controls.
|
|
235
237
|
|
|
236
238
|
On native Windows, Suspend opens a PowerShell subshell rather than freezing Atomic. Exit the subshell to restore the same session; owned background tasks continue while it is open.
|
|
237
239
|
|
package/docs/computer-use.md
CHANGED
|
@@ -212,7 +212,7 @@ Report what the installer did. On macOS it places `CuaDriver.app` in `/Applicati
|
|
|
212
212
|
|
|
213
213
|
Never loop on the installer. In a workflow stage, a refused or failed install is reported as `blocked`/`needs_human` together with the exact installer command above, not worked around with an alternative tool.
|
|
214
214
|
|
|
215
|
-
The bundled skill's `version` (`0.
|
|
215
|
+
The bundled skill's `version` (`0.36.0`) identifies its source release, not the running daemon. Check the installed driver's advertised schema before using unfamiliar parameters; do not upgrade software or change permissions merely to make a recipe work. If an upgrade is authorized, run `cua-driver update --apply` once and restart the daemon with the platform's startup command below. `cua-driver check-update` only checks; it changes nothing.
|
|
216
216
|
|
|
217
217
|
### Turn telemetry off
|
|
218
218
|
|
|
@@ -485,7 +485,7 @@ See [workflow authoring](/workflows/authoring) for stages and human-input gates.
|
|
|
485
485
|
| `cua-driver`, uv, or another command is missing | Install it when permitted with one bounded attempt, refresh PATH, and check its version in the same shell that will launch automation. |
|
|
486
486
|
| `cua-driver call` fails or `permissions status` reports `unknown` | The daemon is not running. Start it with `open -n -g -a CuaDriver --args serve` on macOS, an interactive-session `cua-driver serve` on Windows, or a foreground `cua-driver serve` inside the graphical session on Linux, then rerun `cua-driver status`. |
|
|
487
487
|
| Grants look correct but actions or captures are refused | Stale TCC grants. Toggle CuaDriver off and on under Accessibility and Screen & System Audio Recording, or click **+** and re-add `/Applications/CuaDriver.app`, then fully relaunch the daemon. An in-process SDK runtime needs the node host relaunched instead. |
|
|
488
|
-
| Contract, schema, or protocol mismatch | Version skew between the `@trycua/cua-driver` SDK pin and the running driver. Check `cua-driver --version` and reinstall the SDK at that exact version. The bundled skill (`0.
|
|
488
|
+
| Contract, schema, or protocol mismatch | Version skew between the `@trycua/cua-driver` SDK pin and the running driver. Check `cua-driver --version` and reinstall the SDK at that exact version. The bundled skill (`0.36.0`) identifies its source release, not the daemon's protocol. If a driver upgrade is authorized, run `cua-driver update --apply` once and restart the daemon. |
|
|
489
489
|
| Snapshot reports `degraded` or `truncated` | Do not act on it. Re-observe, narrow the query, or fall back to a pixel action from the same snapshot only where the skill allows it. |
|
|
490
490
|
| Black screenshot or no desktop | Check screen permissions, display/session ownership, X11 versus Wayland, and remote-session state. |
|
|
491
491
|
| Input reaches the wrong app | Stop. Confirm focus, window identity, scaling, and that no other controller shares the desktop. |
|
|
@@ -16,6 +16,9 @@ Atomic accepts environment variables for configuration, provider credentials, an
|
|
|
16
16
|
| `ATOMIC_EXPERIMENTAL` | `PI_EXPERIMENTAL` | Set to `1` to enable experimental features and preferred strict JSON-schema constrained sampling for additional built-in tools; `read`, `edit`, `write`, `bash`, and PowerShell already prefer strict sampling by default. The footer shows an `xp` badge |
|
|
17
17
|
| `ATOMIC_CLIPBOARD_OSC52` | `PI_CLIPBOARD_OSC52` | Set to `1` to also send copied text to the terminal with OSC 52 after the local clipboard write, as Atomic does over SSH. Use it when a remote client reaches Atomic without SSH variables, such as a multiplexer attached from another machine or a remote IDE daemon |
|
|
18
18
|
| `ATOMIC_WORKFLOW_JOURNAL_PATH` | None | Non-empty value selects the local SQLite workflow journal file; overrides `workflows.journal` settings, but not SDK `RunOpts.journal` or `createAgentSession({ journal })` |
|
|
19
|
+
| `ATOMIC_STORAGE` | None | JSON storage selection for this process's sessions and workflow runs, in the same form as the shared `storage` setting. Overrides settings but not `--storage`, the session flags, or the domain variables. See [Selecting storage per process](/storage#selecting-storage-per-process) |
|
|
20
|
+
| `ATOMIC_SESSIONS_STORAGE` | None | JSON session-only storage selection, in the same form as `sessions.storage`. Conflicts with a non-empty `ATOMIC_CODING_AGENT_SESSION_DIR` |
|
|
21
|
+
| `ATOMIC_WORKFLOWS_JOURNAL` | None | JSON workflow-run-only storage selection, in the same form as `workflows.journal`. Conflicts with a non-empty `ATOMIC_WORKFLOW_JOURNAL_PATH` |
|
|
19
22
|
| `ATOMIC_HEAP_MB` | `PI_HEAP_MB` | Heap size in MiB for the Atomic process that runs agents when Atomic runs on Node (npm installs): the interactive engine, or a headless run (`-p`, `--mode json`, `--mode rpc`), which restarts itself once with the larger heap. By default it gets half of system memory when that is more than Node's default limit, because every in-process subagent and workflow stage shares that heap. `0` keeps Node's default. A `--max-old-space-size` in `NODE_OPTIONS` or on Atomic's own command line takes precedence. The standalone binary does not use this value |
|
|
20
23
|
|
|
21
24
|
`VISUAL` and `EDITOR` select the Ctrl+G external editor when `externalEditor` is unset.
|
|
@@ -28,7 +31,7 @@ The renderer also owns `PI_HYPERLINKS`, `PI_IMAGE_PROTOCOL`, and `PI_TRUE_COLOR`
|
|
|
28
31
|
|
|
29
32
|
## Workflow journal
|
|
30
33
|
|
|
31
|
-
Sessions and workflow runs default to the same local SQLite file, `~/.atomic/workflows/journal.sqlite`, with `session_` and `wf_` table prefixes. With a custom `ATOMIC_CODING_AGENT_DIR`, the file is `workflows/journal.sqlite` under the agent directory's parent. `ATOMIC_WORKFLOW_JOURNAL_PATH` changes the workflow journal only; `ATOMIC_CODING_AGENT_SESSION_DIR` selects JSONL for sessions only.
|
|
34
|
+
Sessions and workflow runs default to the same local SQLite file, `~/.atomic/workflows/journal.sqlite`, with `session_` and `wf_` table prefixes. With a custom `ATOMIC_CODING_AGENT_DIR`, the file is `workflows/journal.sqlite` under the agent directory's parent. `ATOMIC_WORKFLOW_JOURNAL_PATH` changes the workflow journal only; `ATOMIC_CODING_AGENT_SESSION_DIR` selects JSONL for sessions only. These two path variables resolve relative paths against the working directory, and empty values leave settings in effect. `ATOMIC_STORAGE`, `ATOMIC_SESSIONS_STORAGE`, and `ATOMIC_WORKFLOWS_JOURNAL` instead hold the same JSON selections as the settings, adapters included, and resolve relative paths as the settings do. See [Selecting storage per process](/storage#selecting-storage-per-process).
|
|
32
35
|
|
|
33
36
|
In `settings.json`, shared `storage` selects a database for both domains; `sessions.storage` and `workflows.journal` override their respective domains. Relative settings paths resolve against Atomic home. To select process-local memory explicitly, use `{ "memory": true }` in the appropriate selection. Memory cannot recover data after process exit and is never selected after a storage failure. See [Workflow storage](/settings#workflow-storage).
|
|
34
37
|
|
|
@@ -17,7 +17,8 @@ Atomic starts
|
|
|
17
17
|
├─► session_start / resources_discover (trust-safe interactive bootstrap, when needed)
|
|
18
18
|
├─► project_trust (user/global and CLI extensions only, before project resources load)
|
|
19
19
|
├─► session_start { reason: "startup" } (extensions not already started)
|
|
20
|
-
|
|
20
|
+
├─► resources_discover { reason: "startup" }
|
|
21
|
+
└─► after the transcript renders: session_recovery_restore / session_recovery_replay
|
|
21
22
|
│
|
|
22
23
|
▼
|
|
23
24
|
user sends prompt ─────────────────────────────────────────┐
|
|
@@ -60,7 +61,10 @@ user sends another prompt ◄─────────────────
|
|
|
60
61
|
├─► session_before_switch (can cancel)
|
|
61
62
|
├─► session_shutdown
|
|
62
63
|
├─► session_start { reason: "new" | "resume", previousSessionId? }
|
|
63
|
-
|
|
64
|
+
├─► resources_discover { reason: "startup" }
|
|
65
|
+
└─► after the transcript renders (resume only):
|
|
66
|
+
├─► session_recovery_restore
|
|
67
|
+
└─► session_recovery_replay (only when interrupted deliveries are pending)
|
|
64
68
|
|
|
65
69
|
/fork or /clone
|
|
66
70
|
├─► session_before_fork (can cancel)
|
|
@@ -146,6 +150,29 @@ pi.on("session_start", async (event, ctx) => {
|
|
|
146
150
|
});
|
|
147
151
|
```
|
|
148
152
|
|
|
153
|
+
#### session_recovery_restore / session_recovery_replay
|
|
154
|
+
|
|
155
|
+
Fired once per session when the host recovers work an earlier process left unfinished. The interactive host does this after the transcript is visible. SDK and RPC hosts do it when they call `session.startPendingRecovery()` or send `start_pending_recovery`. Startup binding never fires them.
|
|
156
|
+
|
|
157
|
+
`session_recovery_restore` comes first. Restore background work here, such as child agents.
|
|
158
|
+
|
|
159
|
+
`session_recovery_replay` follows, but only when delivery cards that were shown before the exit never reached the model. Return a `drop` decision for each card whose sender or target no longer exists; the card is closed and never offered again. Return `defer` when you cannot tell yet, for example because the peer's service is unreachable: the card stays pending without a turn, and the next recovery of the session offers it again. If handlers disagree, `drop` wins over `defer`. Cards with no decision are replayed in a visible turn.
|
|
160
|
+
|
|
161
|
+
Notices from either event go into one message for the user and the agent. That message never starts a turn.
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
pi.on("session_recovery_restore", async (event, ctx) => {
|
|
165
|
+
// event.reason - "startup" | "resume" | "workflow_stage_resume"
|
|
166
|
+
return { notices: ["Worker reviewer could not be restored and is gone."] };
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
pi.on("session_recovery_replay", async (event, ctx) => {
|
|
170
|
+
// event.pending - [{ entryId, customType, content, details, timestamp, stageAdmissionKey?, delivery }]
|
|
171
|
+
const stale = event.pending.filter((entry) => entry.customType === "my-extension:worker-note");
|
|
172
|
+
return { decisions: stale.map((entry) => ({ entryId: entry.entryId, action: "drop" })) };
|
|
173
|
+
});
|
|
174
|
+
```
|
|
175
|
+
|
|
149
176
|
#### session_info_changed
|
|
150
177
|
|
|
151
178
|
Fired when the current session display name is set via `/name`, RPC, or `pi.setSessionName()`.
|
|
@@ -120,7 +120,7 @@ Skills are reusable expert instructions. Trigger one with `/skill:<name>` follow
|
|
|
120
120
|
| `liteparse` | Pull text, tables, or values out of PDF, DOCX, PPTX, XLSX, and image files locally. | `/skill:liteparse` |
|
|
121
121
|
| `show-me` | Explain a topic visually with concise diagrams, code-shape sketches, or focused HTML artifacts. HumanLayer, MIT licensed. | `/skill:show-me` |
|
|
122
122
|
|
|
123
|
-
Impeccable 4.
|
|
123
|
+
Impeccable 4.7.0 runs its commands (`context`, `live`, `detect`, hooks, and the rest) through a self-contained engine binary instead of Node scripts. The bundled launcher, `scripts/impeccable` (`impeccable.cmd` on Windows), looks for a binary next to itself, then in `~/.impeccable/bin/`, and otherwise downloads the pinned engine release for your platform into `~/.impeccable/bin/<version>/` and verifies it against its published SHA-256 before running it, so the first use needs network access. Point `IMPECCABLE_BIN` at a preinstalled engine or set `IMPECCABLE_HOME` to a writable cache when that is not possible. Atomic's launcher keeps the engine's anonymous telemetry pings and its `npx impeccable update` check switched off, since the skill ships with each Atomic release. A concept roll still fetches its catalog with a `GET /api/roll` request to `impeccable.style`, which carries the scope, mode, a short seed key, and a re-roll counter, but no project files, prompts, code, or conversation. Live sessions resolve to the selected app root and support SvelteKit, Nuxt, TanStack Start, Astro, Next.js, Vite, and static HTML injection. The image fallback runs only with `OPENAI_API_KEY`, sends prompts and optional reference images to OpenAI, and spends that account's API credit. Generated image prompts are embedded in the image or a sidecar, so do not include secrets.
|
|
124
124
|
|
|
125
125
|
Use `/skill:research-codebase` for a focused subsystem or question. For repository-wide research, use `fan-out-and-synthesize` with distinct repository partitions and an artifact synthesis barrier. Use Goal for ledger-backed bounded orchestration and Ralph for research-first delegated implementation with iterative review; task size alone does not select either workflow.
|
|
126
126
|
|
package/docs/guides/intercom.md
CHANGED
|
@@ -5,7 +5,7 @@ description: "What Intercom is, when sessions should talk to each other, and whe
|
|
|
5
5
|
|
|
6
6
|
# Intercom
|
|
7
7
|
|
|
8
|
-
Intercom is a message channel between agent sessions running on the same machine. Sessions in the same group can send each other messages, ask questions and wait for an answer, and coordinate without going through you.
|
|
8
|
+
Intercom is a message channel between agent sessions running on the same machine, or on several machines that share a storage database ([Running Intercom across machines](/intercom/operations#running-intercom-across-machines)). Sessions in the same group can send each other messages, ask questions and wait for an answer, and coordinate without going through you.
|
|
9
9
|
|
|
10
10
|
## When to use one
|
|
11
11
|
|
|
@@ -20,6 +20,8 @@ Start with the state you can observe. The existing sections below explain the co
|
|
|
20
20
|
| Broker restart or concurrent startup | Sessions disappear briefly, or several sessions invoke Intercom at the same time. | Usually no action is needed. Clients reconnect, and the spawn lock prevents duplicate brokers. See [How it works](#how-it-works). |
|
|
21
21
|
| Broker does not start | The startup or readiness error quotes the broker log path and includes a bounded tail. | Read `~/.atomic/agent/intercom/broker.log`, or the equivalent path below `ATOMIC_CODING_AGENT_DIR`. See [How it works](#how-it-works). |
|
|
22
22
|
| Half-closed peer or undeliverable send | The send returns `Session not found`; its message id remains retryable. | Refresh the session list and retry after the target reconnects. See [How it works](#how-it-works). |
|
|
23
|
+
| Subagent being restored after a restart | A send or ask to a background subagent that its owner will restore, or is restoring, waits instead of returning `Session not found`. For a plain session's subagent this holds from the moment its reopened session starts. For a subagent of an interrupted workflow stage it holds from the moment the session that launched the run is back and the run is addressable, until the stage, once running again, restores it. It holds for the owner and for any session in the subagent's group, whether the message uses the subagent's name, its Atomic session ID, or the Intercom session ID it had before the restart. The name and Atomic session ID are held from the first moment; the pre-restart ID is held once the owner has read it from the subagent's own session, usually within a fraction of a second. Each waiting message is delivered once, in the order it was sent, when the subagent reconnects. If the subagent has not reconnected within 20 seconds, the sender gets a "still being restored" error. If the subagent did not survive the restart, the sender gets an explicit "did not survive the restart and is gone" error. If another Atomic session takes over the interrupted run, the senders waiting for its subagents get an error saying so. None of these returns `Session not found`. | Retry later if the subagent has not reconnected yet, launch a new subagent if the old one is gone, or send again to the session that took over the run. See [How it works](#how-it-works). |
|
|
24
|
+
| Queued for an offline session | With [multi-node routing](#running-intercom-across-machines) on, a send, ask, or reply to a session that has disconnected is reported as queued at once (`Message queued for <target>`, with `target: "offline session"` in the result details). An ask returns without waiting for an answer. | Nothing to retry: the message is delivered when that session connects to Intercom again on any machine that uses the same storage. Do not send it again as a new message. See [Offline sessions](#offline-sessions). |
|
|
23
25
|
|
|
24
26
|
## How Connection Works
|
|
25
27
|
|
|
@@ -65,6 +67,8 @@ Initialization may retry before delivery begins. Exhaustion or cancellation ther
|
|
|
65
67
|
|
|
66
68
|
Recoverable disconnects trigger retries; unrelated errors stop them. Cancellation stops new attempts but does not undo a successful receipt. Retry and reply waits share the original 11-minute operation deadline.
|
|
67
69
|
|
|
70
|
+
`delivered` means the recipient saved the message to its session. A message saved by a busy recipient is shown when it goes idle, as before; if the recipient is killed or crashes first, resuming its session shows each saved message once, in arrival order, after the transcript appears. Saved messages go through the same sender check as interrupted deliveries: one from a sender that is still connected is shown and may start a turn, one from a sender that is gone is added once, in full, to the session history without starting a turn, and the recovery notice says the sender is gone. One whose sender cannot be checked is deferred until the session is opened again: Intercom was unreachable, or the sender is a relaunched subagent that has not reconnected yet. If the recipient does not confirm the save within about 8 seconds, the sender gets a failure saying the outcome is uncertain: the recipient may still save and show the message. Check with the recipient before sending it again as a new message. Intercom's own retries reuse the original message ID, and a recipient that already saved a message does not show it twice. A recipient that disconnects before confirming receives the message again when it reconnects. While a broker started by an earlier Atomic version is still running after an upgrade, `delivered` keeps its earlier meaning, that the message reached the recipient, and the recipient still saves it on arrival; the broker exits when idle, and the next one uses the new meaning.
|
|
71
|
+
|
|
68
72
|
`outcome: "unknown"` means delivery may have occurred, including an accepted ask that ended without a reply. **Check with the recipient before sending again.** A new tool invocation is a new operation, not another retry of the old one.
|
|
69
73
|
|
|
70
74
|
Intercom refuses new delivery when it cannot safely track duplicates, including capacity or storage failures. Do not delete its runtime files to force a resend when an outcome is unknown. Transport remains local to this machine.
|
|
@@ -118,6 +122,100 @@ Parallel communication does not cancel the batch. A blocking ask waits only in i
|
|
|
118
122
|
|
|
119
123
|
Queued messages from a child arrive before its completion notice. Delivery retries do not rerun work or change its outcome.
|
|
120
124
|
|
|
125
|
+
## Running Intercom across machines
|
|
126
|
+
|
|
127
|
+
By default, each machine's broker serves only the sessions on that machine. With multi-node routing, brokers on different machines that use the same shared storage database deliver messages to each other through it. Sessions still connect only to the broker on their own machine, and brokers never connect to each other: they read and write the shared database.
|
|
128
|
+
|
|
129
|
+
### Turning it on
|
|
130
|
+
|
|
131
|
+
Give every machine the same shared top-level `storage` selection, including the same `prefix`. To reach other machines, the selection must be an [adapter](/storage#selecting-an-adapter) whose module implements `openDatabase` and connects to a database that every machine can reach, as described in [Sharing storage across hosts](/storage#sharing-storage-across-hosts). Install the adapter module on every machine.
|
|
132
|
+
|
|
133
|
+
Multi-node routing needs Atomic installed from npm (`npm install -g @bastani/atomic`). The standalone binary's broker cannot load storage adapters, so routing stays off there even with a shared `storage` selection, and sessions on other machines cannot be reached. Use an npm installation on every machine that should take part.
|
|
134
|
+
|
|
135
|
+
The broker takes its selection from one of two places:
|
|
136
|
+
|
|
137
|
+
- `--storage`, `--storage-file`, or `ATOMIC_STORAGE` on the Atomic process that starts the broker. See [Selecting storage per process](/storage#selecting-storage-per-process).
|
|
138
|
+
- Otherwise, the top-level `storage` in the global settings file, `~/.atomic/agent/settings.json` (or `settings.json` in the agent directory under `ATOMIC_CODING_AGENT_DIR`).
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"storage": {
|
|
143
|
+
"adapter": "@example/atomic-sqlite",
|
|
144
|
+
"options": { "url": "libsql://team.example" },
|
|
145
|
+
"secrets": { "authToken": { "env": "TEAM_DB_TOKEN" } },
|
|
146
|
+
"prefix": "team_"
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
One broker serves every session in an agent directory and keeps the selection it started with until it exits. After changing the selection, close the Atomic sessions on that machine. The broker exits five seconds after its last session disconnects, and the next session that uses Intercom starts one with the new selection.
|
|
152
|
+
|
|
153
|
+
Multi-node routing stays off, and Intercom behaves exactly as on a single machine, when the selection is:
|
|
154
|
+
|
|
155
|
+
- absent, including when only project settings (`.atomic/settings.json`), `sessions.storage`, `workflows.journal`, `ATOMIC_SESSIONS_STORAGE`, or `ATOMIC_WORKFLOWS_JOURNAL` select storage;
|
|
156
|
+
- in memory: `{ "memory": true }` or `{ "path": ":memory:" }`;
|
|
157
|
+
- an adapter that implements only `openSessionStore` or `openRunJournal` and has no `openDatabase`;
|
|
158
|
+
- invalid, for example an `env` secret that is not set in the broker's environment, or an adapter module that cannot be loaded. The broker then writes `Intercom multi-node routing is off: <reason>` to `broker.log`.
|
|
159
|
+
|
|
160
|
+
A `path` selection, including the default storage file, is a local SQLite file. Only brokers on the same machine that open the same file share it. Do not put it on a network filesystem.
|
|
161
|
+
|
|
162
|
+
Intercom creates its own tables in the selected database, named with the selection's `prefix` like the session and workflow tables. Brokers that use different prefixes cannot see each other's sessions, so a prefix also separates teams or tenants that share one database.
|
|
163
|
+
|
|
164
|
+
### What works across machines
|
|
165
|
+
|
|
166
|
+
A session connected to a broker on another machine appears in `intercom list` and is messaged exactly like a local one:
|
|
167
|
+
|
|
168
|
+
- `send`, `ask`, and `reply` keep the same sender identity, reply threads, and `delivered` meaning: `delivered` still means the recipient saved the message.
|
|
169
|
+
- Group isolation is the same. A session sees and reaches only sessions that share a group with it.
|
|
170
|
+
- Supervisor messages from `contact_supervisor` keep their supervisor routing.
|
|
171
|
+
- A send to a subagent that its owner is restoring on another machine is held, and reports the same errors, exactly as on one machine. See the [connection states](#connection-states-and-recovery) table.
|
|
172
|
+
- Duplicate protection still holds: Intercom's own retries reuse the message ID, and a recipient never shows a message twice.
|
|
173
|
+
|
|
174
|
+
When a session on this machine and one on another machine have the same name, the name reaches the local one. Give sessions unique names, or address them by session ID.
|
|
175
|
+
|
|
176
|
+
### Latency
|
|
177
|
+
|
|
178
|
+
Brokers wake each other through the shared database. Each broker checks it for wake-ups every half second, so a message to a live session on another machine usually arrives within about half a second plus the database round trips, and its `delivered` confirmation takes about as long again. A slow or distant database adds to each step.
|
|
179
|
+
|
|
180
|
+
If a wake-up is missed, a backup check every 30 seconds finds the message, so delivery can take up to about 30 seconds. A sender waits about 8 seconds for a live recipient to confirm, then gets the uncertain-outcome failure described in [How it works](#how-it-works). The message is still delivered once. Check with the recipient before sending it again as a new message.
|
|
181
|
+
|
|
182
|
+
Sessions on the same machine do not depend on the shared database. Before delivering locally, the broker checks the database for an earlier cross-machine attempt of the same message, and waits at most 0.3 seconds for it, including while the database is still opening when the broker starts. Connecting to Intercom waits at most 5 seconds for the database, and `intercom list` waits at most 2 seconds for sessions on other machines before it shows the local ones alone.
|
|
183
|
+
|
|
184
|
+
### Offline sessions
|
|
185
|
+
|
|
186
|
+
A session that quit cleanly or crashed stays known to every broker for about 7 days after it was last connected. It does not appear in `intercom list`, but you can still message it by name, Intercom session ID, or Atomic session ID, within the same group rules:
|
|
187
|
+
|
|
188
|
+
- A send, ask, or reply returns at once as queued, `Message queued for <target>`, with `target: "offline session"` in the result details. An ask does not wait for an answer.
|
|
189
|
+
- The message stays in the shared database and is delivered, in order, when that same Atomic session connects to Intercom again with the same home group, on any machine that uses the same storage.
|
|
190
|
+
- The broker reports the later delivery only to a sender that is still connected. The tool result that already returned stays queued, so check with the recipient if you need confirmation.
|
|
191
|
+
|
|
192
|
+
A target that no session has used in that time, or never used, still returns `Session not found`.
|
|
193
|
+
|
|
194
|
+
When a whole machine or its broker stops, its sessions still look connected for up to about 30 seconds. A send to one of them in that window waits about 8 seconds and ends with the uncertain-outcome failure; after it, sends are queued. Any message one of those sessions had not yet confirmed is delivered again when it reconnects, and shown once.
|
|
195
|
+
|
|
196
|
+
### Data and security
|
|
197
|
+
|
|
198
|
+
- Message text and attachments are stored in the shared database until they are delivered, together with the sender's identity. Delivered messages are deleted about 12 minutes later, which keeps duplicate protection for retries. Messages for an offline session stay until it reconnects.
|
|
199
|
+
- Session names, session IDs, and group memberships are stored there too, so brokers can list and route to sessions on other machines.
|
|
200
|
+
- Anyone who can read the database can read Intercom messages that cross machines, just as they can read sessions stored there. Protect it with the database's own credentials and transport security, and pass credentials through `secrets` (`{ "env": ... }` or `{ "file": ... }`) rather than `options`. The broker reads `env` secrets from the environment of the Atomic process that started it.
|
|
201
|
+
- Intercom opens no network port and no broker-to-broker connection. Its security across machines is the database's security.
|
|
202
|
+
|
|
203
|
+
### Troubleshooting multi-node
|
|
204
|
+
|
|
205
|
+
**A session on another machine does not appear in `intercom list`.**
|
|
206
|
+
|
|
207
|
+
- Check that both machines use the same storage selection and the same `prefix`, from settings or from the process that started each broker. A broker started before you changed the selection keeps the old one until it exits.
|
|
208
|
+
- Check `broker.log` on both machines for `Intercom multi-node routing is off: <reason>` or `Intercom multi-node routing is unavailable: <reason>`. An absent, in-memory, or domain-only selection stays off without a message, and a `path` selection reaches only brokers on the same machine.
|
|
209
|
+
- A session appears only after it connects to Intercom, which happens lazily. Ask it to use Intercom once, for example `intercom({ action: "status" })`.
|
|
210
|
+
- Both sessions must share a group.
|
|
211
|
+
- When the database is slow to answer, `intercom list` shows only local sessions. Try again.
|
|
212
|
+
|
|
213
|
+
**A send stays queued.** The recipient is offline. It receives the message when the same Atomic session connects to Intercom again with the same home group, on a machine that uses the same storage and prefix. A session reopened on a machine with a different selection never sees it.
|
|
214
|
+
|
|
215
|
+
**The database is unavailable.** Messages between sessions on the same machine keep working. A send to a session on another machine fails with `Session not found on this node, and Intercom could not reach the shared storage database to look for it on other nodes. Try again once the database is available.` If the database cannot be opened when the broker starts, the broker logs `Intercom multi-node routing is unavailable: <reason>` and retries after 1 second, doubling up to every 30 seconds. Intercom never falls back to in-memory storage. If sends keep failing after the database is back, close the sessions on that machine so that a new broker starts.
|
|
216
|
+
|
|
217
|
+
**An ask to a session on another machine never gets an answer.** If that session disconnects before it answers, the asker is not told, and waits until the ask times out after 10 minutes.
|
|
218
|
+
|
|
121
219
|
## Keyboard Shortcuts
|
|
122
220
|
|
|
123
221
|
| Key | Action |
|
|
@@ -129,8 +227,12 @@ Queued messages from a child arrive before its completion notice. Delivery retri
|
|
|
129
227
|
|
|
130
228
|
## Limitations
|
|
131
229
|
|
|
132
|
-
- **
|
|
230
|
+
- **Local connections only** — Each session connects to the broker on its own machine over local sockets/pipes. Sessions on other machines are reachable only through a shared storage database; see [Running Intercom across machines](#running-intercom-across-machines)
|
|
133
231
|
- **No dedicated intercom log** — Messages are kept in session history; there is no separate intercom transcript or inbox
|
|
134
232
|
- **No attachments UI** — `file`, `snippet`, and `context` attachments are supported in the protocol, but not in the compose overlay
|
|
135
233
|
- **Only connected sessions appear** — The list shows sessions that have connected to the broker, not every open Atomic process
|
|
136
234
|
- **Broker lifecycle** — The broker auto-spawns on first use and exits when idle; sessions reconnect automatically if it restarts
|
|
235
|
+
- **Unsaved sessions** — A session started with `--no-session` keeps received messages only in memory; if it exits before showing them, they are lost
|
|
236
|
+
- **Paused or quit subagent** — A send or ask to a subagent you paused with `subagent({ action: "pause" })` waits until you resume it, but only while the Atomic process that paused it keeps running. After a restart, or for a subagent you quit, sends return `Session not found` until you resume it with `subagent({ action: "resume", id, message? })`. See [Run control](/subagents#run-control)
|
|
237
|
+
- **Subagent whose owning process is not running** — Sends to a subagent are held only while an Atomic process that owns it is running: the reopened session for a plain session's subagent, the session that launched the run for a workflow stage's subagent (once the crashed session's lease has expired, about 120 seconds, and the run is addressable). Atomic keeps no durable mailbox for a subagent. While no such process runs, for example before the owning session is reopened, a send to the subagent is not queued and returns `Session not found`, the same as for a top-level session that is not running. The same applies for the few milliseconds after the owning session connects to Intercom and before it has placed its hold. Sends are also not held for a stage's subagent whose stage session cannot be read, or for a subagent that cannot use Intercom. A hold that nothing restores ends on its own: after 60 seconds for a session whose host does not run recovery, such as `atomic -p`, and when its owning session disconnects
|
|
238
|
+
- **Offline sessions with multi-node routing** — With [multi-node routing](#running-intercom-across-machines) on, a send to a session that disconnected within about the last 7 days, including a subagent's session, is queued for it instead of returning `Session not found`, unless one of the holds above applies first. See [Offline sessions](#offline-sessions)
|
|
@@ -56,12 +56,15 @@ Live-session lookup accepts:
|
|
|
56
56
|
- An exact full Intercom session ID.
|
|
57
57
|
- An exact case-insensitive session name.
|
|
58
58
|
- A unique 8-character hexadecimal prefix of a UUID-backed session ID.
|
|
59
|
+
- A connected session's Atomic session ID (the ID `atomic --session` resumes), when exactly one session you can see registered it.
|
|
59
60
|
|
|
60
|
-
Exact names and IDs take precedence. Ambiguous prefixes report only authorized candidates and require the full UUID; other truncations are rejected. An isolated child can select its authorized supervisor by a unique UUID prefix without gaining access to unrelated groups.
|
|
61
|
+
Exact names and IDs take precedence, and an Atomic session ID is tried only after the other forms find nothing. Ambiguous prefixes report only authorized candidates and require the full UUID; other truncations are rejected. An isolated child can select its authorized supervisor by a unique UUID prefix without gaining access to unrelated groups.
|
|
62
|
+
|
|
63
|
+
A reopened session or a restored background subagent registers under the Intercom session ID it had before the restart, so peers can keep using the ID they saw in `intercom list`. The session records that ID in its history as an `intercom_session_identity` entry. The broker never gives an ID to a new registration while a live session holds it; the restarted session then gets a fresh ID. While the broker remembers who last held an ID, only that same Atomic session can take it back.
|
|
61
64
|
|
|
62
65
|
Workflow stages use the canonical `workflow:<rootRunId>/<segment>[/<segment>...]` path printed by `intercom list` and workflow status surfaces. An exact target works while the row is `PENDING` and after it becomes `RUNNING`. Each segment may be a stage name, run id, or glob: `*` matches one segment and may be embedded, while `**` matches any depth.
|
|
63
66
|
|
|
64
|
-
Status surfaces label pending stages whose pre-start delivery capability is unavailable without presenting a usable target. They never advertise a retained pending stage after its run terminates. The `sessionId` shown by `workflow status`
|
|
67
|
+
Status surfaces label pending stages whose pre-start delivery capability is unavailable without presenting a usable target. They never advertise a retained pending stage after its run terminates. The `sessionId` shown by `workflow status` is the stage's Atomic session ID, not a workflow path: it reaches the stage only while the stage is connected and visible to you, and it never queues for a pending stage.
|
|
65
68
|
|
|
66
69
|
Agent list rows put the copyable exact ID or canonical workflow path first, followed by status and working directory. Meaningful names remain secondary metadata; redundant generated aliases are hidden only in the list, not removed from lookup. Pending and future workflow rows retain lifecycle and queued counts. Copy the target rather than reconstructing it from a name.
|
|
67
70
|
|
|
@@ -99,6 +102,10 @@ Only a workflow invocation member with eligible invocation-control authority can
|
|
|
99
102
|
|
|
100
103
|
If the destination stage is skipped, the run terminates, or the stage becomes terminal before its session initializes, Atomic marks the queued message undeliverable and sends the correlated failure notification when acknowledgment was requested. Use `ask` only on a live, reply-capable exact target. Pending, future, and pattern asks return `pending_stage_ask_unsupported`; use ordinary `send`, because a stage may start much later or never start.
|
|
101
104
|
|
|
105
|
+
A workflow run that was interrupted and can continue with `/workflow resume` stays addressable after the Atomic session that launched it restarts. Once the crashed session's lease expires (about 120 seconds), its stages show as pending in `intercom list`, and sends to `workflow:<rootRunId>/<stage>` or `workflow:<rootRunId>/**` are queued and delivered once after `/workflow resume`. Asks are refused while the run is dormant, and only the launching session answers for it.
|
|
106
|
+
|
|
107
|
+
The background subagents that the run's stages launched stay addressable too. While the run waits, the launching session holds sends to them by name, Atomic session ID, or pre-restart Intercom session ID. When the stage runs again and restores them, it takes the hold over, so each held message is delivered once after the subagent reconnects. If another session takes over the run, it also takes over these targets, and senders still waiting for a subagent get an error that names the other session. A stage whose saved session cannot be read has no held subagents; the stage's own route is unaffected. See [Intercom operations](/intercom/operations#connection-states-and-recovery).
|
|
108
|
+
|
|
102
109
|
### Groups
|
|
103
110
|
|
|
104
111
|
Every session belongs to a non-empty set of intercom **groups**. Sessions with no group configured retain exactly the legacy behavior: they belong only to the implicit `"default"` group and can see and message each other. A session can ordinarily discover, resolve, and message another session when their membership sets intersect; exact-ID sends with no shared membership are rejected by the broker.
|
|
@@ -118,7 +125,7 @@ Supervisor coordination is granted by the launching parent and can cross groups
|
|
|
118
125
|
|
|
119
126
|
**`ask`** sends the message and blocks until the recipient responds (10-minute timeout). If the recipient disconnects after delivery, only the exact ask to that peer fails promptly; the timeout remains the backstop for a connected but unresponsive recipient. Up to `maxPendingAsks` waits (default: 6) may run concurrently, including same-target and mixed-target fan-out. Exact sender/message correlation keeps out-of-order replies and selective disconnects from cross-settling another call. Parallel children use this path even when asking their launching parent: only the requester waits, siblings keep executing, and the reply returns from the waiting tool call in the original child execution. A single-child launch retains the exception that a claimed parent ask ends that child and returns a dynamic `[TASK_CONTEXT]` handoff.
|
|
120
127
|
|
|
121
|
-
An `idle` registration does not guarantee reply capability. Completed, failed, interrupted, or cancelled noninteractive subagent children reject new asks immediately with `Target noninteractive child is terminal and cannot reply`; launch a fresh child with explicit context instead. If termination wins after admission but before a reply, the exact pending ask fails explicitly too. Live interactive idle sessions remain askable, and completed workflow stages with a retained reply-capable post-mortem conversation keep their existing reopening behavior. `send` transport behavior is unchanged; a retained registration is not a promise that a terminal child's model will consume a send.
|
|
128
|
+
An `idle` registration does not guarantee reply capability. Completed, failed, interrupted, or cancelled noninteractive subagent children reject new asks immediately with `Target noninteractive child is terminal and cannot reply`; continue a resumable child with `subagent({ action: "resume", id, message })`, or launch a fresh child with explicit context instead. If termination wins after admission but before a reply, the exact pending ask fails explicitly too. Live interactive idle sessions remain askable, and completed workflow stages with a retained reply-capable post-mortem conversation keep their existing reopening behavior. `send` transport behavior is unchanged; a retained registration is not a promise that a terminal child's model will consume a send.
|
|
122
129
|
|
|
123
130
|
**`contact_supervisor`** keeps a narrower policy: one blocking decision/interview wait per child may coexist with ordinary peer asks, but a second concurrent supervisor wait receives `Already waiting for a supervisor reply`. Claimed foreground handoffs allocate no waiter. Mutual peer asks are supported, although both sessions must process inbound work to reply; the per-waiter timeout remains the backstop.
|
|
124
131
|
|
package/docs/intercom.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Intercom"
|
|
3
|
-
description: "Direct messaging between Atomic sessions on
|
|
3
|
+
description: "Direct messaging between Atomic sessions, on one machine or across machines that share storage"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
> Atomic sessions can talk to each other. Press ALT+M to message another session, or ask the agent to coordinate with a peer.
|
|
7
7
|
|
|
8
8
|
# Intercom
|
|
9
9
|
|
|
10
|
-
Atomic bundles `@bastani/intercom` for direct 1:1 messaging between sessions on the same machine. Send context, findings, or requests yourself, or let agents coordinate. No separate install is needed.
|
|
10
|
+
Atomic bundles `@bastani/intercom` for direct 1:1 messaging between sessions on the same machine, or on several machines that share a storage database. Send context, findings, or requests yourself, or let agents coordinate. No separate install is needed.
|
|
11
11
|
|
|
12
12
|
The extension registers commands and tools at startup. Connections are lazy: a session connects when you or the model invoke Intercom. Workflow stages and admitted subagent children connect at session start so they are listable and steerable immediately.
|
|
13
13
|
|
|
@@ -29,9 +29,10 @@ The extension registers commands and tools at startup. Connections are lazy: a s
|
|
|
29
29
|
|
|
30
30
|
## Where to go next
|
|
31
31
|
|
|
32
|
-
Intercom coordinates several Atomic sessions on one machine. Read this page for the quick start and the coordination patterns, then continue:
|
|
32
|
+
Intercom coordinates several Atomic sessions on one machine, or across machines that share storage. Read this page for the quick start and the coordination patterns, then continue:
|
|
33
33
|
|
|
34
34
|
- [Intercom operations](/intercom/operations) covers connection states, delivery, notifications, shortcuts, and recovery.
|
|
35
|
+
- [Running Intercom across machines](/intercom/operations#running-intercom-across-machines) explains how sessions on different machines message each other through shared storage.
|
|
35
36
|
- [Intercom reference](/intercom/reference) — the `intercom` tool contract and every intercom setting.
|
|
36
37
|
|
|
37
38
|
## Table of Contents
|
|
@@ -57,6 +58,7 @@ Intercom coordinates several Atomic sessions on one machine. Read this page for
|
|
|
57
58
|
- [Workflow Delivery Modes](/intercom/operations#workflow-delivery-modes)
|
|
58
59
|
- [Subagent Control Notices](/intercom/operations#subagent-control-notices)
|
|
59
60
|
- [Delivery Ordering](/intercom/operations#delivery-ordering)
|
|
61
|
+
- [Running Intercom across machines](/intercom/operations#running-intercom-across-machines)
|
|
60
62
|
- [Configuration](/intercom/reference#configuration)
|
|
61
63
|
- [Keyboard Shortcuts](/intercom/operations#keyboard-shortcuts)
|
|
62
64
|
- [How It Works](/intercom/operations#how-it-works)
|
|
@@ -126,13 +128,13 @@ Found the issue — UserService.validate() doesn't check for null input.
|
|
|
126
128
|
See auth.ts:142-156.
|
|
127
129
|
```
|
|
128
130
|
|
|
129
|
-
The reply hint (enabled by default) points to `intercom({ action: "reply", ... })`, so recipients never need raw sender or `replyTo` IDs. Idle recipients get a new turn immediately; busy interactive recipients receive the message once they go idle. Attachment content is included in the agent-visible body, and messages are rendered inline and stored in Atomic session history.
|
|
131
|
+
The reply hint (enabled by default) points to `intercom({ action: "reply", ... })`, so recipients never need raw sender or `replyTo` IDs. Idle recipients get a new turn immediately; busy interactive recipients receive the message once they go idle. The recipient saves each message to its session as soon as it arrives, and the sender sees `delivered` only after that save. If the recipient closes or crashes before it shows a saved message, resuming that session with `atomic --session <id>` or `/resume` shows the message then, once, in arrival order, if its sender is still connected. A message from a sender that is gone is added once, in full, to the session history without starting a turn, and the recovery notice says the sender is gone. A reply already returned to a waiting `ask`, or a message the recipient refused, is not shown again when the session reopens. If Intercom cannot be reached when the session reopens, or the sender is a background subagent that was relaunched but has not reconnected yet, the message is deferred: it starts no turn, the notice says so, and the next reopen of the session decides again. Sessions started with `--no-session` are not saved, so their messages are not recovered. Attachment content is included in the agent-visible body, and messages are rendered inline and stored in Atomic session history.
|
|
130
132
|
|
|
131
133
|
Working subagents and live workflow stages process `send` and `ask` as priority input. The current model call or cancellable tool is cancelled, then the message is handled in the same task and session. Tools that ignore cancellation finish first; completed side effects are not undone or replayed.
|
|
132
134
|
|
|
133
|
-
This works for foreground and background children. Startup messages join the original task, multiple messages keep arrival order, and asks retain their reply thread.
|
|
135
|
+
This works for foreground and background children. Startup messages join the original task, multiple messages keep arrival order, and asks retain their reply thread. Subagent `pause` and `quit`, owner cancellation, host stop, and terminal children or closed stages take precedence over later input. Use an exact child name or full session ID from `intercom list`, not a workflow-stage path.
|
|
134
136
|
|
|
135
|
-
A busy non-interactive recipient that is neither an admitted subagent nor a workflow stage can still refuse a message without interrupting its task. A successful `send` receipt
|
|
137
|
+
A busy non-interactive recipient that is neither an admitted subagent nor a workflow stage can still refuse a message without interrupting its task. A successful `send` receipt means the recipient saved the message, not that its model accepted it. The refusal carries the original reply thread: a waiting `ask` returns an error; otherwise the sender sees **Intercom delivery failed** feedback with a `Sent:` timestamp. That feedback bypasses the ordinary idle queue and does not trigger a standalone agent turn. During an active turn, protected delivery makes it visible and reconciles it at a protocol-safe boundary. Its wording describes the refused send, not the recipient's later activity.
|
|
136
138
|
|
|
137
139
|
Atomic enables ordinary `intercom` by default, but it respects tool allowlists, exclusions and `noTools: "all"`. Include `"intercom"` in an explicit allowlist when needed. SDK hosts can disable the entire package with `builtins: { intercom: false }`; reload does not restore it. `contact_supervisor` remains subagent-only. Registration is lightweight; broker connection and heavy initialization remain lazy until an Intercom surface is used.
|
|
138
140
|
|
package/docs/keybindings.md
CHANGED
|
@@ -278,7 +278,7 @@ Create `~/.atomic/agent/keybindings.json`:
|
|
|
278
278
|
|
|
279
279
|
Each action can have a single key or an array of keys. User config overrides defaults.
|
|
280
280
|
|
|
281
|
-
On native Windows, `app.suspend` opens an interactive PowerShell subshell with `alt+z`, leaving `ctrl+z` available for editor undo. Type `exit` to return to the same Atomic session. Atomic and its owned background tasks continue running; this is not process suspension. Ctrl+C in the subshell does not clear or exit Atomic. PowerShell 7 (`pwsh.exe`) is preferred, with Windows PowerShell (`powershell.exe`) as fallback
|
|
281
|
+
On native Windows, `app.suspend` opens an interactive PowerShell subshell with `alt+z`, leaving `ctrl+z` available for editor undo. Type `exit` to return to the same Atomic session. Atomic and its owned background tasks continue running; this is not process suspension. Ctrl+C in the subshell does not clear or exit Atomic. PowerShell 7 (`pwsh.exe`) is preferred, with Windows PowerShell (`powershell.exe`) as fallback; see [Windows](/windows) for the locations checked. In WSL, the normal Linux `ctrl+z`/`fg` behavior still applies.
|
|
282
282
|
|
|
283
283
|
### Emacs Example
|
|
284
284
|
|
package/docs/models.md
CHANGED
|
@@ -31,7 +31,7 @@ OpenAI's GPT-6 Luna Decisions classifier is `openai-decisions/gpt-6-luna`. It us
|
|
|
31
31
|
|
|
32
32
|
GPT-6 Luna accepts up to 128 image blocks through `images`, alongside the JSON state. Other classifiers reject images unless their catalog input and API support them. Refused answers produce an error result. The endpoint rejects inputs above 922K tokens; very large inputs, currently above roughly 600K tokens, can hit its gateway time limit. These 504 responses are not retried, so reduce the input before trying again.
|
|
33
33
|
|
|
34
|
-
TypeSafe Jev and Cloudflare Clef classifiers are also available through their providers. [Local llama.cpp models](/llama-cpp#classification) support native decision models and a next-token-probability fallback for chat models.
|
|
34
|
+
TypeSafe Jev and Cloudflare Clef classifiers are also available through their providers. The direct TypeSafe classifiers, `typesafe/jev-latest` and `typesafe/jev-1.14.0`, accept up to 262K tokens and PNG or JPEG images through `images`; see [TypeSafe Jev](/providers#typesafe-jev). [Local llama.cpp models](/llama-cpp#classification) support native decision models and a next-token-probability fallback for chat models.
|
|
35
35
|
|
|
36
36
|
## Table of Contents
|
|
37
37
|
|
package/docs/providers.md
CHANGED
|
@@ -638,11 +638,13 @@ For router-mode discovery, load/unload management, and Hugging Face downloads wi
|
|
|
638
638
|
|
|
639
639
|
## TypeSafe Jev
|
|
640
640
|
|
|
641
|
-
`typesafe/jev-latest`
|
|
641
|
+
`typesafe/jev-latest` and `typesafe/jev-1.14.0` are the built-in TypeSafe classifiers. `jev-latest` follows TypeSafe's latest stable Jev; `jev-1.14.0` pins Jev 1.14. Name one explicitly in `compactionModel`, `routerModel`, `structured_output`, or `generateStructuredOutput()` when you want it. An unset or `auto` compaction model or router uses the current chat model; saved credentials do not select a classifier. Jev is never an execution `auto` candidate, chat `--model`, or `/model` choice. Any other classifier works the same way only when the current model registry lists it. A models.dev listing does not register a classifier. See [Compaction model](/compaction#compaction-model) for verbatim classifier compaction.
|
|
642
|
+
|
|
643
|
+
Both direct TypeSafe classifiers turn on TypeSafe's [long-context](https://docs.typesafe.ai/models.md) and [image](https://docs.typesafe.ai/images.md) previews for every request, so they accept up to 262K tokens and PNG or JPEG images in `images`. Long requests are slower and less accurate than requests within TypeSafe's default 64K. Each image must be 280 to 1568 pixels per side. Atomic adds an `images` field to the state that lists the images in order, so questions can refer to `` `images` `` or `` `images[0]` ``; a state with its own `images` field cannot send images. TypeSafe marks images as a preview whose format and accuracy can change.
|
|
642
644
|
|
|
643
645
|
Use `/login typesafe` to save an API key under `typesafe` in `auth.json`, or set `TYPESAFE_API_KEY` in Atomic's process environment. Stored credentials take precedence over the environment key. `/logout typesafe` removes the saved TypeSafe credential; an environment key remains active until you unset it. Jev appears in `/login` but not `/model`, because it only makes structured decisions. Do not put the key in prompts, decision state, or `settings.json`.
|
|
644
646
|
|
|
645
|
-
Atomic also registers `vercel-ai-gateway/typesafe-ai/jev`, `opencode/jev-1.13`, and `opencode/jev-1.13-free` as classifiers. Use the corresponding gateway's credentials (`AI_GATEWAY_API_KEY` or `/login vercel-ai-gateway`; `/login opencode` for Zen) and name the exact ID. They do not appear in the chat model selector. Gateway Jev uses its TypeSafe endpoint, not Chat Completions. The old `openrouter/~typesafe/jev-latest` ID remains unregistered.
|
|
647
|
+
Atomic also registers `vercel-ai-gateway/typesafe-ai/jev`, `opencode/jev-1.13`, and `opencode/jev-1.13-free` as classifiers. Use the corresponding gateway's credentials (`AI_GATEWAY_API_KEY` or `/login vercel-ai-gateway`; `/login opencode` for Zen) and name the exact ID. They do not appear in the chat model selector. Gateway Jev uses its TypeSafe endpoint, not Chat Completions, keeps the gateway's own context limit, and does not accept images. The old `openrouter/~typesafe/jev-latest` ID remains unregistered.
|
|
646
648
|
|
|
647
649
|
An explicit `routerModel` other than `auto` selects that exact registered chat or classifier model for workflow-stage and subagent `model: "auto"` routing. An unset or `auto` router uses the current chat model. General `generateStructuredOutput()` calls and the `structured_output` tool select their own model: an optional exact `model`, then `fallbackModels`, then the current chat model. Those calls resolve the ID through the current model registry and use the generic classify operation for a registered classifier, including `typesafe/jev-latest`. They do not inherit `routerModel`. A catalog entry does not prove that classify is available or entitled. User-issued `/workflow` commands launch directly.
|
|
648
650
|
|