@bastani/atomic 0.9.5-alpha.7 → 0.9.5-alpha.8
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 +17 -2
- package/dist/builtin/cursor/package.json +2 -2
- package/dist/builtin/intercom/CHANGELOG.md +17 -0
- package/dist/builtin/intercom/README.md +27 -21
- package/dist/builtin/intercom/broker/broker.ts +3 -6
- package/dist/builtin/intercom/broker/paths.ts +42 -5
- package/dist/builtin/intercom/broker/spawn.ts +78 -10
- package/dist/builtin/intercom/config.ts +6 -3
- package/dist/builtin/intercom/contact-supervisor-tool.ts +6 -4
- package/dist/builtin/intercom/index-heavy.ts +1 -1
- package/dist/builtin/intercom/index.ts +162 -130
- package/dist/builtin/intercom/intercom-tool.ts +9 -3
- package/dist/builtin/intercom/package.json +10 -1
- package/dist/builtin/intercom/result-renderers.ts +1 -1
- package/dist/builtin/intercom/ui/compose.ts +2 -2
- package/dist/builtin/intercom/ui/inline-message.ts +2 -2
- package/dist/builtin/intercom/ui/session-list.ts +2 -2
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +6 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/tui/render-event-formatting.ts +15 -48
- package/dist/builtin/subagents/src/tui/render-layout.ts +9 -0
- package/dist/builtin/subagents/src/tui/render-widget-graph.ts +27 -25
- package/dist/builtin/subagents/src/tui/render-widget.ts +46 -69
- package/dist/builtin/subagents/src/tui/render.ts +1 -1
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +11 -0
- package/dist/builtin/workflows/README.md +3 -1
- package/dist/builtin/workflows/builtin/goal-artifacts.ts +3 -1
- package/dist/builtin/workflows/builtin/goal-prompts.ts +23 -2
- package/dist/builtin/workflows/builtin/goal-reducer.ts +21 -0
- package/dist/builtin/workflows/builtin/goal-review.ts +60 -6
- package/dist/builtin/workflows/builtin/goal-runner.ts +32 -21
- package/dist/builtin/workflows/builtin/goal-types.ts +7 -1
- package/dist/builtin/workflows/builtin/ralph-core.ts +52 -4
- package/dist/builtin/workflows/builtin/ralph-review-gate.ts +10 -3
- package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +23 -2
- package/dist/builtin/workflows/builtin/ralph-runner.ts +38 -16
- package/dist/builtin/workflows/builtin/review-convergence.ts +111 -0
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/skills/create-spec/SKILL.md +2 -0
- package/dist/builtin/workflows/src/durable/child-primitive.ts +12 -8
- package/dist/builtin/workflows/src/durable/dbos-backend.ts +47 -2
- package/dist/builtin/workflows/src/durable/dbos-envelope.ts +28 -0
- package/dist/builtin/workflows/src/durable/stage-primitive.ts +165 -27
- package/dist/builtin/workflows/src/durable/types.ts +14 -1
- package/dist/builtin/workflows/src/engine/run.ts +12 -13
- package/dist/builtin/workflows/src/extension/workflow-prompts.ts +2 -0
- package/dist/builtin/workflows/src/runs/foreground/executor-continuation.ts +13 -3
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +1 -1
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +18 -19
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-unresolved-overflow.ts +38 -0
- package/dist/builtin/workflows/src/shared/persistence-restore-helpers.ts +18 -1
- package/dist/builtin/workflows/src/shared/persistence-restore.ts +23 -5
- package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +4 -0
- package/dist/bun/internal-intercom-broker.d.ts +6 -0
- package/dist/bun/internal-intercom-broker.d.ts.map +1 -0
- package/dist/bun/internal-intercom-broker.js +39 -0
- package/dist/bun/internal-intercom-broker.js.map +1 -0
- package/dist/bun/split-loader.d.ts.map +1 -1
- package/dist/bun/split-loader.js +21 -8
- package/dist/bun/split-loader.js.map +1 -1
- package/dist/core/agent-session-auto-compaction.d.ts +4 -5
- package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
- package/dist/core/agent-session-auto-compaction.js +49 -9
- package/dist/core/agent-session-auto-compaction.js.map +1 -1
- package/dist/core/agent-session-events.d.ts.map +1 -1
- package/dist/core/agent-session-events.js +3 -1
- package/dist/core/agent-session-events.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +9 -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-models.d.ts +2 -2
- package/dist/core/agent-session-models.d.ts.map +1 -1
- package/dist/core/agent-session-models.js.map +1 -1
- package/dist/core/agent-session-prompt.d.ts.map +1 -1
- package/dist/core/agent-session-prompt.js +1 -0
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session-retry.d.ts +2 -0
- package/dist/core/agent-session-retry.d.ts.map +1 -1
- package/dist/core/agent-session-retry.js +152 -4
- package/dist/core/agent-session-retry.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +1 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +1 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session-types.d.ts +15 -1
- 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 +4 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +4 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/context-compaction-runner.d.ts.map +1 -1
- package/dist/core/compaction/context-compaction-runner.js +36 -20
- package/dist/core/compaction/context-compaction-runner.js.map +1 -1
- package/dist/core/copilot-anthropic-sse-repair.d.ts +23 -0
- package/dist/core/copilot-anthropic-sse-repair.d.ts.map +1 -0
- package/dist/core/copilot-anthropic-sse-repair.js +340 -0
- package/dist/core/copilot-anthropic-sse-repair.js.map +1 -0
- package/dist/core/copilot-gemini-reasoning.d.ts +16 -8
- package/dist/core/copilot-gemini-reasoning.d.ts.map +1 -1
- package/dist/core/copilot-gemini-reasoning.js +27 -22
- package/dist/core/copilot-gemini-reasoning.js.map +1 -1
- package/dist/core/copilot-hosts.d.ts +12 -0
- package/dist/core/copilot-hosts.d.ts.map +1 -0
- package/dist/core/copilot-hosts.js +33 -0
- package/dist/core/copilot-hosts.js.map +1 -0
- package/dist/core/extensions/agent-events.d.ts +1 -1
- package/dist/core/extensions/agent-events.d.ts.map +1 -1
- package/dist/core/extensions/agent-events.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +5 -5
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/sdk-types.d.ts +2 -0
- 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 +1 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/settings-manager-basic-accessors.d.ts +1 -0
- package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-basic-accessors.js +6 -0
- package/dist/core/settings-manager-basic-accessors.js.map +1 -1
- package/dist/core/settings-types.d.ts +1 -0
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.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/main-deferred-startup.d.ts +12 -0
- package/dist/main-deferred-startup.d.ts.map +1 -1
- package/dist/main-deferred-startup.js +21 -0
- package/dist/main-deferred-startup.js.map +1 -1
- package/dist/main-early-input.d.ts +41 -0
- package/dist/main-early-input.d.ts.map +1 -0
- package/dist/main-early-input.js +147 -0
- package/dist/main-early-input.js.map +1 -0
- package/dist/main.d.ts +0 -6
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +19 -13
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-events.js +15 -0
- package/dist/modes/interactive/components/chat-session-host-events.js.map +1 -1
- package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-agent-events.js +24 -0
- package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.js +279 -181
- package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.d.ts +9 -0
- package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.js +36 -0
- package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts +4 -0
- package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-types.d.ts +3 -0
- package/dist/modes/interactive/interactive-mode-types.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-types.js.map +1 -1
- package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-render-chat.js +21 -9
- package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
- package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-startup.js +6 -1
- package/dist/modes/interactive/interactive-startup.js.map +1 -1
- package/docs/compaction.md +4 -0
- package/docs/json.md +3 -1
- package/docs/providers.md +1 -0
- package/docs/rpc.md +3 -1
- package/docs/settings.md +26 -0
- package/docs/terminal-setup.md +4 -0
- package/docs/usage.md +2 -2
- package/docs/workflows.md +13 -3
- package/npm-shrinkwrap.json +513 -23
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.9.5-alpha.8] - 2026-07-08
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added main-chat `fallbackModels` support for retryable provider/model failures ([#1418](https://github.com/bastani-inc/atomic/issues/1418)). Users can configure an ordered fallback chain in settings or SDK session options with per-candidate reasoning suffixes such as `:high` and `:xhigh`; normal same-model retry remains first when enabled, fallback switches are recorded as session model changes, and the UI reports fallback progress.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Fixed provider context-window overflow recovery so an exhausted overflow auto-compaction attempt emits an explicit unresolved-overflow signal instead of silently leaving callers to retry the same model, and planner calls that themselves overflow now degrade through the deterministic overflow-eviction ladder rather than throwing before non-model reduction can run. Planner overflow is now recognized whether it is returned as an assistant error message or thrown by the provider stream before `agent.prompt()` completes, so overflow recovery skips the critical planner retry and goes directly to deterministic non-model reduction in both forms.
|
|
14
|
+
- Fixed overflow auto-compaction retry reliability so `AgentSession.prompt()` now waits for the `willRetry: true` post-compaction continuation before resolving. Late unresolved-overflow signals from that continuation are observed by workflow callers before they can mark the original prompt successful.
|
|
15
|
+
- Fixed normal interactive TTY startup so typing before the prompt box is fully mounted is captured instead of being invisibly blocked by deferred resource initialization. Atomic now starts a short-lived raw input buffer only on the existing deferred-startup fast path, keeps it active until the TUI input handler is mounted, replays draft text into the editor, queues only ordinary Enter-submitted prompts for the prompt loop, and preserves command-like early submissions such as `/settings` or `!pwd` as standalone editor submissions so normal command routing handles them after mount. Once a command-like startup submission is captured, later captured submissions wait behind it and replay in original input order after that command is routed, preventing a later ordinary prompt from leapfrogging the earlier command without merging commands and following prompts into one draft. The capture now restores terminal raw mode across early startup exits/signals and ignores split escape sequences so partial arrow-key/protocol bytes never become draft text. Trust prompts, explicit resource flags, metadata commands, non-TTY modes, and explicit provider/model selection stay on the synchronous startup path.
|
|
16
|
+
- Fixed the bundled intercom extension to keep broker runtime files under the active Atomic agent directory, including custom `ATOMIC_CODING_AGENT_DIR` values and the legacy `PI_CODING_AGENT_DIR` alias, while documenting the `~/.atomic/agent/intercom/` primary path and `~/.pi/agent/intercom/` fallback. The default pi-compatible `npx --no-install tsx` broker sentinel is now hardened to launch through the current runtime (`process.execPath`): Node-based installs use a resolved `tsx` CLI with a bundled `jiti` fallback, Bun source-checkout runs use the current Bun executable directly, and standalone Atomic Bun binaries use a narrow internal split-launcher broker handoff, so default startup does not depend on `npx`, `tsx`, or `bun` being on `PATH`; explicit custom broker configs remain supported.
|
|
17
|
+
- Fixed GitHub Copilot Claude/Anthropic Messages streams that cleanly report a terminal stop reason but omit the required `message_stop` SSE event. Atomic now adds that single terminal event only for closed, non-error Copilot `/v1/messages` event streams before provider parsing, including complete final SSE frames that reach EOF without a trailing blank-line separator and GitHub Enterprise/GHE tenant routing hosts such as `copilot-api.<enterprise>.ghe.com`, while leaving malformed, truncated, already well-formed, non-Copilot, look-alike host, non-SSE, Gemini, and OpenAI-style streams to the normal parser/retry behavior.
|
|
18
|
+
- Fixed bundled workflow durable resume hydration so replayed parallel reviewer fanout preserves branch structure and completed workflow stages restore persisted summaries, durations, session/model metadata, and checkpoint contents in status and graph views.
|
|
19
|
+
|
|
5
20
|
## [0.9.5-alpha.7] - 2026-07-07
|
|
6
21
|
|
|
7
22
|
### Changed
|
|
@@ -619,8 +634,8 @@
|
|
|
619
634
|
- Fixed provider auth-status reporting for explicit `$ENV_VAR` config values, preserved uppercase literal credentials during config-value migrations (including legacy `~/.pi/agent` roots), preserved `models.json` JSONC comments/formatting during migration, and accepted the upstream `supportsDeveloperRole` flag for custom OpenAI Responses models.
|
|
620
635
|
- Fixed RPC client requests to reject promptly when the child agent process exits or its stdio fails, completed the RPC-mode output/backpressure and `excludeFromContext` bash-command port, and preserved steering/follow-up queue modes across extension-triggered RPC session reloads.
|
|
621
636
|
- Fixed SDK provider stream options so HTTP idle timeouts and WebSocket connect timeouts from settings are forwarded to provider streams while preserving per-request overrides.
|
|
622
|
-
- Fixed interactive startup input handling so prompts submitted before the main input loop is installed are queued instead of dropped, and
|
|
623
|
-
- Fixed the initial `--resume` session picker and all-sessions pane to honor a custom `--session-dir
|
|
637
|
+
- Fixed interactive startup input handling so prompts submitted before the main input loop is installed are queued instead of dropped, captured command-like and ordinary startup submissions replay in original input order without later prompts leapfrogging earlier commands, prompts typed behind an active startup command stay behind that command even while a prior prompt is streaming, and pre-session selectors or confirmations such as `--resume` and cross-project `--session` no longer leak typed text into chat input.
|
|
638
|
+
- Fixed the initial `--resume` session picker and all-sessions pane to honor a custom `--session-dir`, and fixed signal-triggered shutdown ordering so extension `session_shutdown` cleanup runs before terminal restore writes.
|
|
624
639
|
- Fixed plain metadata commands (`--version`, `--help`, `--list-models`) to keep their output on stdout for scripts/completions while keeping auto-install/startup chatter off stdout.
|
|
625
640
|
- Fixed OAuth login dialog prompt/manual input rendering so submitted values remain stable, auth storage writes to consistently use `0600` file mode, and self-update command generation to bypass package-manager minimum-release-age delays.
|
|
626
641
|
- Fixed changelog link normalization to produce Atomic repository/tag-pinned links from local package links and legacy pi-mono URLs, wired into startup and `/changelog` output.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/cursor",
|
|
3
|
-
"version": "0.9.5-alpha.
|
|
3
|
+
"version": "0.9.5-alpha.8",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Experimental first-party Atomic extension for Cursor OAuth, model discovery, and streaming provider registration.",
|
|
6
6
|
"contributors": [
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@bastani/atomic-natives": "0.9.5-alpha.
|
|
43
|
+
"@bastani/atomic-natives": "0.9.5-alpha.8",
|
|
44
44
|
"@bufbuild/protobuf": "^2.12.1",
|
|
45
45
|
"@earendil-works/pi-ai": "^0.80.3"
|
|
46
46
|
}
|
|
@@ -4,6 +4,23 @@ All notable changes to the `pi-intercom` extension will be documented in this fi
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.9.5-alpha.8] - 2026-07-08
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Added preferred `atomic` package manifest metadata alongside the legacy `pi` manifest metadata so Atomic package discovery uses Atomic-branded metadata without breaking pi-compatible installs.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Updated intercom documentation to describe Atomic's `~/.atomic/agent/intercom/` primary config/runtime path and legacy `~/.pi/agent/intercom/` fallback.
|
|
16
|
+
- Updated the registered intercom tool description and prompt snippet to use Atomic/Pi-neutral local agent session wording instead of hard-coding Pi-branded session names in model-visible text.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Fixed Atomic intercom broker runtime paths to derive from the active Atomic agent directory, so `ATOMIC_CODING_AGENT_DIR` moves the broker socket, PID, spawn lock, and Windows launcher together while the legacy `PI_CODING_AGENT_DIR` alias remains supported.
|
|
21
|
+
- Fixed raw TypeScript intercom imports to use the bundled `@earendil-works/pi-tui` package name, matching Atomic's runtime dependency while preserving the public intercom API.
|
|
22
|
+
- Hardened default intercom broker startup so the pi-compatible `npx --no-install tsx` sentinel launches through the current runtime (`process.execPath`): Node-based installs use a resolved `tsx` CLI with a bundled `jiti` fallback, Bun source-checkout runs use the current Bun executable directly, and standalone Atomic Bun binaries re-enter the split launcher through a narrow internal broker handoff instead of treating the Atomic executable as a generic Bun interpreter; explicit custom `brokerCommand`/`brokerArgs` configs remain pass-through overrides.
|
|
23
|
+
|
|
7
24
|
## [0.9.4] - 2026-07-03
|
|
8
25
|
|
|
9
26
|
### Changed
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
<img src="banner.png" alt="pi-intercom" width="1100">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
# Pi Intercom
|
|
5
|
+
# Pi / Atomic Intercom
|
|
6
6
|
|
|
7
|
-
Direct 1:1 messaging between pi sessions on the same machine. Send context, findings, or requests from one session to another — whether you're driving the conversation or letting agents coordinate.
|
|
7
|
+
Direct 1:1 messaging between Atomic or pi sessions on the same machine. Send context, findings, or requests from one session to another — whether you're driving the conversation or letting agents coordinate.
|
|
8
8
|
|
|
9
9
|
```text
|
|
10
10
|
User flow: ALT+M or run /intercom to pick a session and send a message
|
|
@@ -12,51 +12,53 @@ User flow: ALT+M or run /intercom to pick a session and send a message
|
|
|
12
12
|
|
|
13
13
|
## Why
|
|
14
14
|
|
|
15
|
-
Sometimes you're running multiple pi sessions — one researching, one executing, one reviewing.
|
|
15
|
+
Sometimes you're running multiple Atomic/pi sessions — one researching, one executing, one reviewing. Intercom lets you:
|
|
16
16
|
|
|
17
17
|
- **User-driven orchestration** — Send context or findings from your research session to your execution session
|
|
18
18
|
- **Agent collaboration** — An agent can reach out to another session when it needs help or wants to share results
|
|
19
|
-
- **Session awareness** — See what other pi sessions are running and their current status
|
|
19
|
+
- **Session awareness** — See what other Atomic/pi sessions are running and their current status
|
|
20
20
|
|
|
21
|
-
Unlike pi-messenger (a shared chat room for multi-agent swarms),
|
|
21
|
+
Unlike pi-messenger (a shared chat room for multi-agent swarms), intercom is for targeted 1:1 communication where you pick the recipient.
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Intercom also integrates with delegated subagents: child agents get a child-only `contact_supervisor` tool when subagent bridge metadata is present. Atomic-prefixed bridge environment variables are supported, and legacy `PI_*` bridge metadata remains compatible. Use `reason: "need_decision"` for blocking clarification, `reason: "interview_request"` for multiple structured supervisor answers, and `reason: "progress_update"` for meaningful plan-changing updates. Normal sessions only see the regular `intercom` tool.
|
|
24
24
|
|
|
25
25
|
## In One Minute
|
|
26
26
|
|
|
27
|
-
Each pi session that has
|
|
27
|
+
Each Atomic/pi session that has intercom loaded and enabled connects to a tiny local broker over a local IPC transport. The broker keeps track of connected sessions and routes direct messages to the one you target by name or session ID. The extension gives you both a tool (`intercom`) and a small overlay UI (`/intercom` or `ALT+M`). Incoming messages are rendered inline inside the recipient session, can trigger a turn immediately, and are also stored in session history as extension entries.
|
|
28
28
|
|
|
29
29
|
## Install
|
|
30
30
|
|
|
31
|
+
Atomic bundles `@bastani/intercom` as a first-party extension; no separate install is needed for normal Atomic sessions. For legacy pi installations of the upstream package:
|
|
32
|
+
|
|
31
33
|
```bash
|
|
32
34
|
pi install npm:pi-intercom
|
|
33
35
|
```
|
|
34
36
|
|
|
35
|
-
Then restart Pi. The extension auto-connects to the broker on startup and registers the bundled `
|
|
37
|
+
Then restart Atomic or Pi. The extension auto-connects to the broker on startup and registers the bundled `intercom` skill for common coordination patterns.
|
|
36
38
|
|
|
37
39
|
**Recommended:** Add this snippet to your project's `AGENTS.md` to help agents understand when to coordinate across sessions:
|
|
38
40
|
|
|
39
41
|
```xml
|
|
40
|
-
<
|
|
41
|
-
Coordinate with other local pi sessions on related codebases. Use `/skill:
|
|
42
|
+
<intercom>
|
|
43
|
+
Coordinate with other local Atomic/pi sessions on related codebases. Use `/skill:intercom` for patterns.
|
|
42
44
|
|
|
43
45
|
**When:** Same codebase (parallel work), reference codebase (consulting patterns), related repos (shared libraries).
|
|
44
46
|
|
|
45
47
|
**Not when:** Unrelated codebases, trivial questions, or when you can proceed independently.
|
|
46
48
|
|
|
47
49
|
**Principle:** Prefer `send` for notifications; `ask` only when blocked waiting for input.
|
|
48
|
-
</
|
|
50
|
+
</intercom>
|
|
49
51
|
```
|
|
50
52
|
|
|
51
53
|
A session becomes intercom-connected when all of these are true:
|
|
52
|
-
- the
|
|
53
|
-
- `enabled` is not set to `false` in `~/.pi/agent/intercom/config.json`
|
|
54
|
+
- the intercom extension is installed/bundled and loaded in that session
|
|
55
|
+
- `enabled` is not set to `false` in `~/.atomic/agent/intercom/config.json` (Atomic) or the legacy `~/.pi/agent/intercom/config.json` fallback
|
|
54
56
|
- the session has started or reloaded after the extension was installed
|
|
55
57
|
- the local broker is running or can be auto-started
|
|
56
58
|
|
|
57
59
|
The session list only shows intercom-connected sessions, not every open Pi process on the machine.
|
|
58
60
|
|
|
59
|
-
If a session is unnamed,
|
|
61
|
+
If a session is unnamed, intercom exposes a runtime-only fallback alias like `subagent-chat-1a2b3c4d` so other sessions can still target it. That alias is not persisted as the session title, so resume pickers can keep showing the transcript snippet instead of a generic `session-...` name.
|
|
60
62
|
|
|
61
63
|
## Quick Start
|
|
62
64
|
|
|
@@ -359,7 +361,7 @@ Only registered in sessions where `pi-subagents` supplied the required child bri
|
|
|
359
361
|
|
|
360
362
|
## Config
|
|
361
363
|
|
|
362
|
-
Create `~/.pi/agent/intercom/config.json
|
|
364
|
+
Create `~/.atomic/agent/intercom/config.json` for Atomic. Legacy pi-compatible installs and fallbacks continue to read `~/.pi/agent/intercom/config.json` when the Atomic config is absent:
|
|
363
365
|
|
|
364
366
|
```json
|
|
365
367
|
{
|
|
@@ -372,16 +374,18 @@ Create `~/.pi/agent/intercom/config.json`:
|
|
|
372
374
|
}
|
|
373
375
|
```
|
|
374
376
|
|
|
377
|
+
The default `npx --no-install tsx` pair is a compatibility sentinel: intercom recognizes it and starts the broker through the current Atomic/Pi runtime (`process.execPath`). Node-based installs use that runtime with a resolved `tsx` CLI, falling back to Atomic's bundled `jiti` loader when `tsx` is unavailable; Bun source-checkout runs use the current Bun executable directly; standalone Atomic Bun binaries re-enter the split launcher through a narrow internal broker handoff. Default startup therefore does not rely on `npx`, `tsx`, or `bun` being on `PATH`.
|
|
378
|
+
|
|
375
379
|
| Setting | Default | Description |
|
|
376
380
|
|---------|---------|-------------|
|
|
377
|
-
| `brokerCommand` | `"npx"` | Command used to start the local broker process |
|
|
381
|
+
| `brokerCommand` | `"npx"` | Command used to start the local broker process; the default sentinel is hardened internally to avoid PATH lookup |
|
|
378
382
|
| `brokerArgs` | `["--no-install", "tsx"]` | Arguments passed to `brokerCommand` before the broker script path |
|
|
379
383
|
| `confirmSend` | false | Show a confirmation dialog before non-reply sends from an interactive session with UI |
|
|
380
384
|
| `enabled` | true | Enable/disable intercom entirely |
|
|
381
385
|
| `replyHint` | true | Include reply instruction in incoming messages |
|
|
382
386
|
| `status` | — | Optional custom status suffix shown after the automatic lifecycle status, for example `thinking · researching` |
|
|
383
387
|
|
|
384
|
-
For example, if you
|
|
388
|
+
Existing pi-compatible configs that set a custom broker command still work. For example, if you intentionally want to use Bun from `PATH`, configure it explicitly:
|
|
385
389
|
|
|
386
390
|
```json
|
|
387
391
|
{
|
|
@@ -390,13 +394,13 @@ For example, if you have Bun installed and want it to start the broker directly,
|
|
|
390
394
|
}
|
|
391
395
|
```
|
|
392
396
|
|
|
393
|
-
|
|
397
|
+
Intercom publishes live session status automatically. Sessions register as `idle`, switch to `thinking` while the agent is running, show `tool:<name>` during tool execution, and return to `idle` on agent completion. If `status` is set in config, it is appended as context instead of replacing the lifecycle status.
|
|
394
398
|
|
|
395
399
|
## How It Works
|
|
396
400
|
|
|
397
401
|
```mermaid
|
|
398
402
|
graph TB
|
|
399
|
-
subgraph A["Pi Session A"]
|
|
403
|
+
subgraph A["Atomic/Pi Session A"]
|
|
400
404
|
A1[Intercom Client]
|
|
401
405
|
A2[intercom tool]
|
|
402
406
|
A3[UI overlays]
|
|
@@ -407,7 +411,7 @@ graph TB
|
|
|
407
411
|
B2[Message Router]
|
|
408
412
|
end
|
|
409
413
|
|
|
410
|
-
subgraph B["Pi Session B"]
|
|
414
|
+
subgraph B["Atomic/Pi Session B"]
|
|
411
415
|
B3[Intercom Client]
|
|
412
416
|
B4[intercom tool]
|
|
413
417
|
B5[UI overlays]
|
|
@@ -424,10 +428,12 @@ Messages use length-prefixed JSON over a local socket/pipe transport (4-byte len
|
|
|
424
428
|
|
|
425
429
|
Async extension work (startup, inbound flushes, reconnects, overlays, and relays) no-ops if the session shuts down or reloads before it settles.
|
|
426
430
|
|
|
427
|
-
Runtime files live
|
|
431
|
+
Runtime files live under the active agent directory. Atomic defaults to `~/.atomic/agent/intercom/`; setting `ATOMIC_CODING_AGENT_DIR` moves the broker socket, PID, spawn lock, Windows launcher, and config below that directory. The legacy `PI_CODING_AGENT_DIR` alias remains supported when the Atomic variable is unset. Legacy pi-compatible defaults use `~/.pi/agent/intercom/`.
|
|
432
|
+
|
|
428
433
|
- `broker.sock` — Unix domain socket for communication (macOS/Linux only; Windows uses a named pipe instead)
|
|
429
434
|
- `broker-launch.vbs` — Windows helper script used to launch the broker without a console window
|
|
430
435
|
- `broker.pid` — Broker process ID
|
|
436
|
+
- `broker.spawn.lock` — Short-lived lock used to avoid duplicate auto-spawns
|
|
431
437
|
- `config.json` — User configuration
|
|
432
438
|
|
|
433
439
|
## Design Decisions
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import net from "net";
|
|
2
2
|
import { writeFileSync, unlinkSync, mkdirSync } from "fs";
|
|
3
|
-
import { join } from "path";
|
|
4
|
-
import { homedir } from "os";
|
|
5
3
|
import { randomUUID } from "crypto";
|
|
6
|
-
import { CONFIG_DIR_NAME } from "@bastani/atomic";
|
|
7
4
|
import { writeMessage, createMessageReader } from "./framing.js";
|
|
8
|
-
import { getBrokerSocketPath } from "./paths.js";
|
|
5
|
+
import { getBrokerPidPath, getBrokerSocketPath, getIntercomDirPath } from "./paths.js";
|
|
9
6
|
import type { SessionInfo, Message, Attachment, BrokerMessage } from "../types.js";
|
|
10
7
|
|
|
11
|
-
const INTERCOM_DIR =
|
|
8
|
+
const INTERCOM_DIR = getIntercomDirPath();
|
|
12
9
|
const SOCKET_PATH = getBrokerSocketPath();
|
|
13
|
-
const PID_PATH =
|
|
10
|
+
const PID_PATH = getBrokerPidPath();
|
|
14
11
|
|
|
15
12
|
interface ConnectedSession {
|
|
16
13
|
socket: net.Socket;
|
|
@@ -1,6 +1,31 @@
|
|
|
1
|
-
import { join } from "path";
|
|
2
1
|
import { homedir } from "os";
|
|
3
|
-
import {
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
|
|
4
|
+
function getHomeDir(): string {
|
|
5
|
+
if (process.platform === "win32") {
|
|
6
|
+
if (process.env.USERPROFILE) return process.env.USERPROFILE;
|
|
7
|
+
if (process.env.HOMEDRIVE && process.env.HOMEPATH) return `${process.env.HOMEDRIVE}${process.env.HOMEPATH}`;
|
|
8
|
+
if (process.env.HOME) return process.env.HOME;
|
|
9
|
+
return homedir();
|
|
10
|
+
}
|
|
11
|
+
return process.env.HOME || process.env.USERPROFILE || homedir();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function expandTildePath(path: string): string {
|
|
15
|
+
if (path === "~") return getHomeDir();
|
|
16
|
+
if (path.startsWith("~/") || (process.platform === "win32" && path.startsWith("~\\"))) {
|
|
17
|
+
return join(getHomeDir(), path.slice(2));
|
|
18
|
+
}
|
|
19
|
+
return path;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getAgentDir(): string {
|
|
23
|
+
const atomicAgentDir = process.env.ATOMIC_CODING_AGENT_DIR;
|
|
24
|
+
if (atomicAgentDir) return expandTildePath(atomicAgentDir);
|
|
25
|
+
const piAgentDir = process.env.PI_CODING_AGENT_DIR;
|
|
26
|
+
if (piAgentDir) return expandTildePath(piAgentDir);
|
|
27
|
+
return join(getHomeDir(), ".atomic", "agent");
|
|
28
|
+
}
|
|
4
29
|
|
|
5
30
|
function sanitizePipeSegment(value: string): string {
|
|
6
31
|
return value
|
|
@@ -9,13 +34,25 @@ function sanitizePipeSegment(value: string): string {
|
|
|
9
34
|
.toLowerCase() || "default";
|
|
10
35
|
}
|
|
11
36
|
|
|
37
|
+
export function getIntercomDirPath(agentDir: string = getAgentDir()): string {
|
|
38
|
+
return join(agentDir, "intercom");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function getBrokerPidPath(agentDir: string = getAgentDir()): string {
|
|
42
|
+
return join(getIntercomDirPath(agentDir), "broker.pid");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function getBrokerSpawnLockPath(agentDir: string = getAgentDir()): string {
|
|
46
|
+
return join(getIntercomDirPath(agentDir), "broker.spawn.lock");
|
|
47
|
+
}
|
|
48
|
+
|
|
12
49
|
export function getBrokerSocketPath(
|
|
13
50
|
platform: NodeJS.Platform = process.platform,
|
|
14
|
-
|
|
51
|
+
agentDir: string = getAgentDir(),
|
|
15
52
|
): string {
|
|
16
53
|
if (platform === "win32") {
|
|
17
|
-
return `\\\\.\\pipe\\pi-intercom-${sanitizePipeSegment(
|
|
54
|
+
return `\\\\.\\pipe\\pi-intercom-${sanitizePipeSegment(agentDir)}`;
|
|
18
55
|
}
|
|
19
56
|
|
|
20
|
-
return join(
|
|
57
|
+
return join(getIntercomDirPath(agentDir), "broker.sock");
|
|
21
58
|
}
|
|
@@ -2,16 +2,25 @@ import { spawn } from "child_process";
|
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
|
|
3
3
|
import { join, dirname } from "path";
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
|
-
import {
|
|
5
|
+
import { createRequire } from "module";
|
|
6
6
|
import net from "net";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
import { isBunBinary } from "@bastani/atomic";
|
|
8
|
+
import {
|
|
9
|
+
getBrokerPidPath,
|
|
10
|
+
getBrokerSocketPath,
|
|
11
|
+
getBrokerSpawnLockPath,
|
|
12
|
+
getIntercomDirPath,
|
|
13
|
+
} from "./paths.js";
|
|
14
|
+
|
|
15
|
+
const INTERCOM_DIR = getIntercomDirPath();
|
|
11
16
|
const EXTENSION_DIR = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
12
17
|
const BROKER_SOCKET = getBrokerSocketPath();
|
|
13
|
-
const BROKER_PID =
|
|
14
|
-
const BROKER_SPAWN_LOCK =
|
|
18
|
+
const BROKER_PID = getBrokerPidPath();
|
|
19
|
+
const BROKER_SPAWN_LOCK = getBrokerSpawnLockPath();
|
|
20
|
+
|
|
21
|
+
type BrokerRuntime = "node" | "bun-source" | "bun-binary";
|
|
22
|
+
|
|
23
|
+
export const INTERNAL_INTERCOM_BROKER_ARG = "--atomic-internal-intercom-broker";
|
|
15
24
|
|
|
16
25
|
type BrokerLaunchSpec =
|
|
17
26
|
| {
|
|
@@ -31,8 +40,55 @@ function sleep(ms: number): Promise<void> {
|
|
|
31
40
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
32
41
|
}
|
|
33
42
|
|
|
43
|
+
function getCurrentBrokerRuntime(): BrokerRuntime {
|
|
44
|
+
if (!process.versions.bun) return "node";
|
|
45
|
+
return isBunBinary ? "bun-binary" : "bun-source";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function requireFromExtensionDir(extensionDir: string): NodeJS.Require {
|
|
49
|
+
return createRequire(join(extensionDir, "package.json"));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function resolveTsxCliPath(extensionDir: string): string | null {
|
|
53
|
+
try {
|
|
54
|
+
const tsxMain = requireFromExtensionDir(extensionDir).resolve("tsx");
|
|
55
|
+
return join(dirname(tsxMain), "cli.mjs");
|
|
56
|
+
} catch {
|
|
57
|
+
const bundledTsx = join(extensionDir, "node_modules", "tsx", "dist", "cli.mjs");
|
|
58
|
+
return existsSync(bundledTsx) ? bundledTsx : null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
34
62
|
export function getTsxCliPath(extensionDir: string = EXTENSION_DIR): string {
|
|
35
|
-
return join(extensionDir, "node_modules", "tsx", "dist", "cli.mjs");
|
|
63
|
+
return resolveTsxCliPath(extensionDir) ?? join(extensionDir, "node_modules", "tsx", "dist", "cli.mjs");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function getJitiCliPath(extensionDir: string = EXTENSION_DIR): string {
|
|
67
|
+
try {
|
|
68
|
+
const jitiPackage = requireFromExtensionDir(extensionDir).resolve("jiti/package.json");
|
|
69
|
+
return join(dirname(jitiPackage), "lib", "jiti-cli.mjs");
|
|
70
|
+
} catch {
|
|
71
|
+
return join(extensionDir, "node_modules", "jiti", "lib", "jiti-cli.mjs");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function getDefaultBrokerRunnerPath(extensionDir: string): string {
|
|
76
|
+
return resolveTsxCliPath(extensionDir) ?? getJitiCliPath(extensionDir);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function getDefaultBrokerCommandParts(
|
|
80
|
+
brokerPath: string,
|
|
81
|
+
extensionDir: string,
|
|
82
|
+
runtimePath: string,
|
|
83
|
+
runtime: BrokerRuntime,
|
|
84
|
+
): { command: string; args: string[] } {
|
|
85
|
+
if (runtime === "bun-source") {
|
|
86
|
+
return { command: runtimePath, args: [brokerPath] };
|
|
87
|
+
}
|
|
88
|
+
if (runtime === "bun-binary") {
|
|
89
|
+
return { command: runtimePath, args: [INTERNAL_INTERCOM_BROKER_ARG, brokerPath] };
|
|
90
|
+
}
|
|
91
|
+
return { command: runtimePath, args: [getDefaultBrokerRunnerPath(extensionDir), brokerPath] };
|
|
36
92
|
}
|
|
37
93
|
|
|
38
94
|
function quoteWindowsArg(value: string): string {
|
|
@@ -56,9 +112,11 @@ export function getWindowsBrokerCommandLine(
|
|
|
56
112
|
nodePath: string = process.execPath,
|
|
57
113
|
brokerCommand = "npx",
|
|
58
114
|
brokerArgs: string[] = ["--no-install", "tsx"],
|
|
115
|
+
runtime: BrokerRuntime = getCurrentBrokerRuntime(),
|
|
59
116
|
): string {
|
|
60
117
|
if (usesDefaultBrokerCommand(brokerCommand, brokerArgs)) {
|
|
61
|
-
|
|
118
|
+
const launch = getDefaultBrokerCommandParts(brokerPath, extensionDir, nodePath, runtime);
|
|
119
|
+
return [quoteWindowsArg(launch.command), ...launch.args.map(quoteWindowsArg)].join(" ");
|
|
62
120
|
}
|
|
63
121
|
|
|
64
122
|
return [quoteWindowsArg(brokerCommand), ...brokerArgs.map(quoteWindowsArg), quoteWindowsArg(brokerPath)].join(" ");
|
|
@@ -90,6 +148,7 @@ export function getBrokerLaunchSpec(
|
|
|
90
148
|
platform: NodeJS.Platform = process.platform,
|
|
91
149
|
intercomDir: string = INTERCOM_DIR,
|
|
92
150
|
nodePath: string = process.execPath,
|
|
151
|
+
runtime: BrokerRuntime = getCurrentBrokerRuntime(),
|
|
93
152
|
): BrokerLaunchSpec {
|
|
94
153
|
if (platform === "win32") {
|
|
95
154
|
const launcherPath = getWindowsHiddenLauncherPath(intercomDir);
|
|
@@ -98,7 +157,16 @@ export function getBrokerLaunchSpec(
|
|
|
98
157
|
command: "wscript.exe",
|
|
99
158
|
args: [launcherPath],
|
|
100
159
|
launcherPath,
|
|
101
|
-
launcherCommandLine: getWindowsBrokerCommandLine(brokerPath, extensionDir, nodePath, brokerCommand, brokerArgs),
|
|
160
|
+
launcherCommandLine: getWindowsBrokerCommandLine(brokerPath, extensionDir, nodePath, brokerCommand, brokerArgs, runtime),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (usesDefaultBrokerCommand(brokerCommand, brokerArgs)) {
|
|
165
|
+
const launch = getDefaultBrokerCommandParts(brokerPath, extensionDir, nodePath, runtime);
|
|
166
|
+
return {
|
|
167
|
+
kind: "direct",
|
|
168
|
+
command: launch.command,
|
|
169
|
+
args: launch.args,
|
|
102
170
|
};
|
|
103
171
|
}
|
|
104
172
|
|
|
@@ -2,7 +2,7 @@ import { existsSync, readFileSync } from "fs";
|
|
|
2
2
|
import { getAgentConfigPaths } from "@bastani/atomic";
|
|
3
3
|
|
|
4
4
|
export interface IntercomConfig {
|
|
5
|
-
/** Broker command used to spawn the broker process (e.g. "npx" or "
|
|
5
|
+
/** Broker command used to spawn the broker process (e.g. "npx", "bun", or "node") */
|
|
6
6
|
brokerCommand: string;
|
|
7
7
|
|
|
8
8
|
/** Arguments passed to the broker command before the broker script path */
|
|
@@ -24,9 +24,12 @@ export interface IntercomConfig {
|
|
|
24
24
|
const CONFIG_PATHS = getAgentConfigPaths("intercom", "config.json");
|
|
25
25
|
const CONFIG_PATH = CONFIG_PATHS.find((path) => existsSync(path)) ?? CONFIG_PATHS[0]!;
|
|
26
26
|
|
|
27
|
+
export const DEFAULT_BROKER_COMMAND = "npx";
|
|
28
|
+
export const DEFAULT_BROKER_ARGS = ["--no-install", "tsx"] as const;
|
|
29
|
+
|
|
27
30
|
const defaults: IntercomConfig = {
|
|
28
|
-
brokerCommand:
|
|
29
|
-
brokerArgs: [
|
|
31
|
+
brokerCommand: DEFAULT_BROKER_COMMAND,
|
|
32
|
+
brokerArgs: [...DEFAULT_BROKER_ARGS],
|
|
30
33
|
confirmSend: false,
|
|
31
34
|
enabled: true,
|
|
32
35
|
replyHint: true,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@bastani/atomic";
|
|
2
2
|
import { randomUUID } from "crypto";
|
|
3
3
|
import { Type } from "typebox";
|
|
4
|
-
import { Text } from "@
|
|
4
|
+
import { Text } from "@earendil-works/pi-tui";
|
|
5
5
|
import type { IntercomClient } from "./broker/client.ts";
|
|
6
6
|
import type { Message } from "./types.ts";
|
|
7
7
|
import { renderContactSupervisorResult } from "./result-renderers.js";
|
|
@@ -249,9 +249,11 @@ export function registerContactSupervisorTool(pi: ExtensionAPI, deps: ContactSup
|
|
|
249
249
|
return {
|
|
250
250
|
content: [{ type: "text", text: `**Reply from supervisor:**\n${replyText}${replyAttachments}` }],
|
|
251
251
|
isError: false,
|
|
252
|
-
|
|
253
|
-
?
|
|
254
|
-
|
|
252
|
+
details: structuredReply
|
|
253
|
+
? structuredReply.value !== undefined
|
|
254
|
+
? { structuredReply: structuredReply.value }
|
|
255
|
+
: { structuredReplyParseError: structuredReply.error }
|
|
256
|
+
: {},
|
|
255
257
|
};
|
|
256
258
|
} catch (error) {
|
|
257
259
|
rejectReplyWaiter(toError(error));
|
|
@@ -393,7 +393,7 @@ export default function piIntercomExtension(pi: ExtensionAPI) {
|
|
|
393
393
|
}
|
|
394
394
|
throw toError(error);
|
|
395
395
|
} finally {
|
|
396
|
-
if (
|
|
396
|
+
if (reconnectPromiseGeneration === generationAtStart) {
|
|
397
397
|
reconnectPromise = null;
|
|
398
398
|
reconnectPromiseGeneration = null;
|
|
399
399
|
}
|