@bastani/atomic 0.9.4-alpha.5 → 0.9.4-alpha.7
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 +55 -0
- package/dist/builtin/cursor/CHANGELOG.md +6 -0
- package/dist/builtin/cursor/package.json +4 -4
- package/dist/builtin/intercom/CHANGELOG.md +6 -0
- package/dist/builtin/intercom/package.json +2 -2
- package/dist/builtin/mcp/CHANGELOG.md +6 -0
- package/dist/builtin/mcp/package.json +4 -4
- package/dist/builtin/subagents/CHANGELOG.md +20 -0
- package/dist/builtin/subagents/package.json +4 -4
- package/dist/builtin/subagents/src/runs/background/async-execution-chain.ts +11 -1
- package/dist/builtin/subagents/src/runs/background/async-execution-single.ts +11 -1
- package/dist/builtin/subagents/src/runs/background/async-execution-types.ts +2 -0
- package/dist/builtin/subagents/src/runs/background/subagent-runner-step.ts +27 -4
- package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +25 -0
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-dynamic-step.ts +1 -0
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-runner.ts +1 -0
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-step.ts +1 -0
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-sequential-step.ts +1 -0
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-types.ts +2 -0
- package/dist/builtin/subagents/src/runs/foreground/chain-execution.ts +3 -1
- package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +22 -0
- package/dist/builtin/subagents/src/runs/foreground/execution-run-sync.ts +15 -5
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-async.ts +5 -1
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +2 -0
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +4 -1
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +4 -1
- package/dist/builtin/subagents/src/runs/shared/attempt-watchdog.ts +126 -0
- package/dist/builtin/subagents/src/runs/shared/model-candidate-filter.ts +53 -0
- package/dist/builtin/subagents/src/runs/shared/model-fallback.ts +52 -53
- package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +2 -0
- package/dist/builtin/subagents/src/shared/model-info.ts +16 -0
- package/dist/builtin/subagents/src/shared/types-config.ts +2 -0
- package/dist/builtin/web-access/CHANGELOG.md +6 -0
- package/dist/builtin/web-access/package.json +2 -2
- package/dist/builtin/workflows/CHANGELOG.md +12 -0
- package/dist/builtin/workflows/package.json +2 -2
- package/dist/builtin/workflows/src/runs/shared/model-fallback-failures.ts +34 -4
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +13 -25
- package/dist/cli/file-processor.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 +11 -3
- package/dist/core/agent-session-extension-bindings.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +4 -1
- package/dist/core/agent-session-methods.d.ts.map +1 -1
- package/dist/core/agent-session-methods.js.map +1 -1
- package/dist/core/agent-session-prompt.d.ts.map +1 -1
- package/dist/core/agent-session-prompt.js +11 -4
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session-state.d.ts.map +1 -1
- package/dist/core/agent-session-state.js +2 -2
- package/dist/core/agent-session-state.js.map +1 -1
- package/dist/core/agent-session-tool-hooks.d.ts +7 -1
- package/dist/core/agent-session-tool-hooks.d.ts.map +1 -1
- package/dist/core/agent-session-tool-hooks.js +26 -4
- package/dist/core/agent-session-tool-hooks.js.map +1 -1
- package/dist/core/agent-session-tree.d.ts.map +1 -1
- package/dist/core/agent-session-tree.js +3 -1
- package/dist/core/agent-session-tree.js.map +1 -1
- package/dist/core/agent-session.d.ts +1 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/copilot-model-catalog.d.ts +25 -74
- package/dist/core/copilot-model-catalog.d.ts.map +1 -1
- package/dist/core/copilot-model-catalog.js +118 -68
- package/dist/core/copilot-model-catalog.js.map +1 -1
- package/dist/core/copilot-model-synthesis.d.ts +10 -0
- package/dist/core/copilot-model-synthesis.d.ts.map +1 -0
- package/dist/core/copilot-model-synthesis.js +89 -0
- package/dist/core/copilot-model-synthesis.js.map +1 -0
- package/dist/core/extensions/api-types.d.ts +2 -1
- 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/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-core.d.ts.map +1 -1
- package/dist/core/extensions/loader-core.js +3 -3
- package/dist/core/extensions/loader-core.js.map +1 -1
- package/dist/core/extensions/session-events.d.ts +7 -1
- package/dist/core/extensions/session-events.d.ts.map +1 -1
- package/dist/core/extensions/session-events.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +28 -1
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/model-registry-builtins.d.ts.map +1 -1
- package/dist/core/model-registry-builtins.js +21 -3
- package/dist/core/model-registry-builtins.js.map +1 -1
- package/dist/core/model-resolver-defaults.d.ts.map +1 -1
- package/dist/core/model-resolver-defaults.js +1 -1
- package/dist/core/model-resolver-defaults.js.map +1 -1
- package/dist/core/resource-loader-reload.d.ts.map +1 -1
- package/dist/core/resource-loader-reload.js +2 -1
- package/dist/core/resource-loader-reload.js.map +1 -1
- package/dist/core/session-manager-core.d.ts +2 -12
- package/dist/core/session-manager-core.d.ts.map +1 -1
- package/dist/core/session-manager-core.js +8 -16
- package/dist/core/session-manager-core.js.map +1 -1
- package/dist/core/settings-manager-resource-accessors.d.ts +1 -0
- package/dist/core/settings-manager-resource-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-resource-accessors.js +11 -0
- package/dist/core/settings-manager-resource-accessors.js.map +1 -1
- package/dist/core/settings-manager-ui-accessors.d.ts +2 -0
- package/dist/core/settings-manager-ui-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-ui-accessors.js +9 -0
- package/dist/core/settings-manager-ui-accessors.js.map +1 -1
- package/dist/core/settings-types.d.ts +2 -0
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/timings.d.ts +7 -5
- package/dist/core/timings.d.ts.map +1 -1
- package/dist/core/timings.js +42 -24
- package/dist/core/timings.js.map +1 -1
- package/dist/core/tools/read-selectors.d.ts.map +1 -1
- package/dist/core/tools/read-selectors.js +19 -1
- package/dist/core/tools/read-selectors.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +12 -23
- package/dist/core/tools/read.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 +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main-session.d.ts.map +1 -1
- package/dist/main-session.js +14 -5
- package/dist/main-session.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +3 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +22 -15
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/chat-message-renderer.d.ts +1 -0
- package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-message-renderer.js +5 -5
- package/dist/modes/interactive/components/chat-message-renderer.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 +1 -0
- package/dist/modes/interactive/components/chat-session-host-rendering.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts +6 -2
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +16 -5
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-agent-events.js +1 -1
- package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
- package/dist/modes/interactive/interactive-editor-actions.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-editor-actions.js +1 -0
- package/dist/modes/interactive/interactive-editor-actions.js.map +1 -1
- package/dist/modes/interactive/interactive-extension-context.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-extension-context.js +1 -0
- package/dist/modes/interactive/interactive-extension-context.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.js +2 -0
- package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
- package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-model-routing.js +1 -0
- package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
- package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-render-chat.js +4 -3
- package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
- package/dist/modes/interactive/interactive-session-runtime.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-session-runtime.js +1 -0
- package/dist/modes/interactive/interactive-session-runtime.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +28 -51
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +27 -51
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-command-handler.js +19 -0
- package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +26 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/rpc-entry.d.ts +3 -0
- package/dist/rpc-entry.d.ts.map +1 -0
- package/dist/rpc-entry.js +12 -0
- package/dist/rpc-entry.js.map +1 -0
- package/dist/utils/image-convert.d.ts +1 -0
- package/dist/utils/image-convert.d.ts.map +1 -1
- package/dist/utils/image-convert.js +21 -15
- package/dist/utils/image-convert.js.map +1 -1
- package/dist/utils/image-process.d.ts +18 -0
- package/dist/utils/image-process.d.ts.map +1 -0
- package/dist/utils/image-process.js +83 -0
- package/dist/utils/image-process.js.map +1 -0
- package/dist/utils/mime.d.ts.map +1 -1
- package/dist/utils/mime.js +41 -0
- package/dist/utils/mime.js.map +1 -1
- package/docs/extensions.md +11 -0
- package/docs/models.md +3 -3
- package/docs/providers.md +1 -1
- package/docs/rpc.md +60 -2
- package/docs/sdk.md +1 -1
- package/docs/settings.md +4 -1
- package/docs/subagents.md +6 -0
- package/docs/workflows.md +2 -0
- package/npm-shrinkwrap.json +138 -140
- package/package.json +15 -10
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,61 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.9.4-alpha.7] - 2026-07-02
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added dynamic GitHub Copilot model population from the live CAPI `/models` catalog: picker-enabled, non-disabled plain chat ids are synthesized from catalog metadata (endpoints, capabilities, limits, and display names) while built-in `pi-ai` definitions still win, namespaced enterprise deployments such as `org/deployment/model` are skipped, and cached catalog metadata enables the same models on cold start.
|
|
10
|
+
- Added catalog-driven thinking-level gating for GitHub Copilot models so dynamically synthesized entries and bundled `pi-ai` Copilot models only offer the reasoning levels advertised by CAPI's `capabilities.supports.reasoning_effort` arrays, while models without an effort array keep their existing thinking behavior.
|
|
11
|
+
- Added a subagent watchdog escape hatch: setting `ATOMIC_SUBAGENT_ATTEMPT_IDLE_TIMEOUT_MS` or `ATOMIC_SUBAGENT_ATTEMPT_TIMEOUT_MS` to `0` (or a negative value) now disables the corresponding per-attempt timeout entirely; non-numeric values are ignored and the defaults apply. The `ATOMIC_SUBAGENT_ATTEMPT_KILL_GRACE_MS` SIGTERM→SIGKILL grace period intentionally cannot be disabled — `0`, negative, or non-numeric values fall back to its default so escalation always stays bounded ([#1581](https://github.com/bastani-inc/atomic/pull/1581)).
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Reverted the `@types/node` devDependency from `26.0.1` back to `24.12.4` to match the deliberate root `overrides` pin that aligns with upstream Pi's resolved lockfile (see [#1489](https://github.com/bastani-inc/atomic/pull/1489)); the dependabot bump in [#1548](https://github.com/bastani-inc/atomic/pull/1548) was a semantic no-op because the override forced resolution to `24.12.4`. Dependabot now ignores `@types/node` so it stops proposing inert bumps.
|
|
16
|
+
- Fixed the `read` tool to parse colon-delimited `file:START:END` (and grep-style `file:LINE:COL`) path selectors as a line range instead of leaving the leading number glued to the path (`file:395`), which produced a bogus `ENOENT` and pushed models (e.g. Opus) to fall back to `sed` ([#1585](https://github.com/bastani-inc/atomic/issues/1585)).
|
|
17
|
+
- Fixed GitHub Copilot models to use the live `max_output_tokens` value from the Copilot model catalog, preventing `github-copilot/claude-opus-4.8` from being capped by Atomic's stale built-in output-token limit after compaction ([#1582](https://github.com/bastani-inc/atomic/issues/1582)).
|
|
18
|
+
- Fixed active GitHub Copilot sessions to adopt live catalog model metadata as soon as the catalog loads, so fallback models refresh their supported reasoning levels without requiring a restart.
|
|
19
|
+
- Fixed workflow and subagent model fallback chains so request/context incompatibility failures (HTTP 400/413/422 bad/unprocessable/payload-too-large request, unsupported tool/parameter, context-length/context-window overflow, `invalid_request`/`bad_request`/`too_large` errors) advance to the next candidate instead of stopping. This ensures that when none of the configured fallback candidates can serve the current request, Atomic falls back to the currently selected user model rather than failing outright. Refusals, content-filter/safety blocks, cancellations, and task failures still stop the chain and are never retried on another model ([#1580](https://github.com/bastani-inc/atomic/issues/1580)).
|
|
20
|
+
- Fixed foreground and background subagent model attempts that produced no child activity from hanging indefinitely. Atomic now bounds each candidate with an idle watchdog and wall-clock cap, records retryable timeout attempts so fallback continues, and skips known unauthenticated providers before spawning while preserving unknown/custom providers and the current-model last resort ([#1580](https://github.com/bastani-inc/atomic/issues/1580)).
|
|
21
|
+
- Fixed the subagent per-attempt idle watchdog so an in-flight tool execution counts as activity: a slow, quiet tool call (long build or test run with no interim output) is no longer killed as a stalled attempt; the wall-clock cap still bounds such attempts ([#1581](https://github.com/bastani-inc/atomic/pull/1581)).
|
|
22
|
+
- Aligned the subagents and workflows model-failure classifiers' direct-message precedence and added a cross-package conformance test suite so the two classifier copies cannot silently drift ([#1581](https://github.com/bastani-inc/atomic/pull/1581)).
|
|
23
|
+
- Fixed the background subagent runner to spawn one default-model attempt when no model candidates were ever configured (no primary model, no fallbacks, and no current model), mirroring the foreground path instead of silently exiting 1 with no error and no spawn; an explicitly empty candidate list produced by pre-spawn auth filtering is still respected and surfaced as an error ([#1581](https://github.com/bastani-inc/atomic/pull/1581)).
|
|
24
|
+
|
|
25
|
+
## [0.9.4-alpha.6] - 2026-07-01
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- Added `get_entries` and `get_tree` RPC commands for reading session entries and tree snapshots over RPC, with corresponding `RpcClient.getEntries`/`getTree` helpers and response types (inherited from upstream Pi [#6078](https://github.com/earendil-works/pi/pull/6078)).
|
|
30
|
+
- Added a package `./rpc-entry` export and `src/rpc-entry.ts` entrypoint for launching Atomic directly in RPC mode (inherited from upstream Pi).
|
|
31
|
+
- Added `session_info_changed` as an extension event so extensions can observe session name changes, wired through `AgentSession.setSessionName` to the extension runner (inherited from upstream Pi [#6175](https://github.com/earendil-works/pi/pull/6175)).
|
|
32
|
+
- Added an `externalEditor` settings.json override for Ctrl+G external editor commands, with default fallbacks to Notepad on Windows and `nano` elsewhere (inherited from upstream Pi [#6122](https://github.com/earendil-works/pi/issues/6122)).
|
|
33
|
+
- Added an `outputPad` setting (`0 | 1`, default `1`) controlling horizontal padding for user messages, assistant messages, and thinking blocks (inherited from upstream Pi [#6168](https://github.com/earendil-works/pi/issues/6168)).
|
|
34
|
+
- Added BMP image detection and shared `processImage`/`convertImageBytesToPng` helpers so unsupported image formats are normalized (converted to PNG) before inlining (inherited from upstream Pi [#6047](https://github.com/earendil-works/pi/issues/6047)).
|
|
35
|
+
- Added `resetTimings`/`time` timing namespaces so extension load timings are recorded and reset separately from main startup timings (inherited from upstream Pi [#6030](https://github.com/earendil-works/pi/pull/6030), [#6063](https://github.com/earendil-works/pi/pull/6063)).
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- Synced Atomic's `@earendil-works/pi-agent-core`, `pi-ai`, and `pi-tui` runtime dependencies from `^0.80.2` to `^0.80.3` and ported the relevant coding-agent changes while preserving Atomic branding, package names, the versionless-`main` release convention, bundled first-party extensions, and user-facing Atomic docs.
|
|
40
|
+
- Bumped `@earendil-works/pi-agent-core`, `pi-ai`, and `pi-tui` from `^0.80.2` to `^0.80.3` across `@bastani/atomic` and all bundled first-party extensions, and regenerated `bun.lock`, `package-lock.json`, and `npm-shrinkwrap.json`.
|
|
41
|
+
- Changed the default OpenAI model to `gpt-5.5` (inherited from upstream Pi). The Atomic-specific `github-copilot` default remains `gpt-5.4`.
|
|
42
|
+
- Deferred the shared `isRetryableAssistantError` retry classifier because Atomic's `_isRetryableError` carries Atomic-specific Copilot Gemini handling (`content_filter`/`finish_reason: error` for CAPI-mapped MALFORMED_FUNCTION_CALL); adopting it safely requires merging Atomic's extra patterns back in during a follow-up sync.
|
|
43
|
+
- Deferred the interactive status-indicator stabilization refactor (Loader → StatusIndicator classes) because it is a large TUI refactor that needs visual QA.
|
|
44
|
+
|
|
45
|
+
### Fixed
|
|
46
|
+
|
|
47
|
+
- Fixed extension `setActiveTools` changes to apply before the next provider request in the same run, by installing a `prepareNextTurnWithContext` hook that refreshes system prompt and tools (inherited from upstream Pi [#6162](https://github.com/earendil-works/pi/issues/6162)).
|
|
48
|
+
- Fixed `before_agent_start` system prompt overrides to survive when extension tool changes occur mid-run (inherited from upstream Pi [#6162](https://github.com/earendil-works/pi/issues/6162)).
|
|
49
|
+
- Fixed invalid session file errors at CLI entry to print a clean red error message instead of a stack trace, via `openSessionOrExit` wrappers around `SessionManager.open` call sites (inherited from upstream Pi [#6002](https://github.com/earendil-works/pi/issues/6002)).
|
|
50
|
+
- Fixed resumed/transcript chat rendering to respect the `outputPad` setting for user and assistant messages rendered through `renderChatMessageEntry` and the chat-session-host render path (inherited from upstream Pi [#6168](https://github.com/earendil-works/pi/issues/6168)).
|
|
51
|
+
- Fixed the dedicated `rpc-entry` to use Atomic's APP_NAME-based env marker (`ATOMIC_CODING_AGENT`) instead of the upstream `PI_CODING_AGENT`, and to force `--mode rpc` as the last CLI argument so a caller-supplied `--mode` cannot override it.
|
|
52
|
+
- Fixed pre-prompt compaction to stop after compaction instead of continuing immediately (already present in Atomic; documented for parity with upstream Pi [#6074](https://github.com/earendil-works/pi/pull/6074)).
|
|
53
|
+
- Fixed `--session` and `SessionManager` to reject non-empty invalid session files without overwriting them (inherited from upstream Pi [#6002](https://github.com/earendil-works/pi/issues/6002)).
|
|
54
|
+
- Fixed user-message transcript rendering to keep visible backslashes in Markdown escape sequences such as `\"` (inherited from upstream Pi [#6105](https://github.com/earendil-works/pi/issues/6105)).
|
|
55
|
+
- Fixed assistant messages stopped by output length to show a visible incomplete-response error (inherited from upstream Pi [#4290](https://github.com/earendil-works/pi/issues/4290)).
|
|
56
|
+
- Fixed `--no-session --session-id` so ephemeral CLI runs can use deterministic session IDs for provider cache affinity (inherited from upstream Pi [#6070](https://github.com/earendil-works/pi/issues/6070)).
|
|
57
|
+
- Fixed disk BMP image files to be detected, converted to PNG, and attached through `read` and CLI `@file` inputs (inherited from upstream Pi [#6047](https://github.com/earendil-works/pi/issues/6047)).
|
|
58
|
+
- Fixed a crash when undici emits an internal client error while terminating a mid-stream HTTP response, by attaching error-suppressing listeners to the global dispatcher and per-origin pools (inherited from upstream Pi [#6133](https://github.com/earendil-works/pi/issues/6133)).
|
|
59
|
+
|
|
5
60
|
## [0.9.4-alpha.5] - 2026-07-01
|
|
6
61
|
|
|
7
62
|
### Fixed
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.9.4-alpha.6] - 2026-07-01
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- Aligned the Cursor provider dependency with upstream pi-ai `^0.80.3`; no Cursor provider source changes were needed for this metadata sync.
|
|
10
|
+
|
|
5
11
|
## [0.9.4-alpha.5] - 2026-07-01
|
|
6
12
|
|
|
7
13
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/cursor",
|
|
3
|
-
"version": "0.9.4-alpha.
|
|
3
|
+
"version": "0.9.4-alpha.7",
|
|
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,8 +40,8 @@
|
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@bastani/atomic-natives": "0.9.4-alpha.
|
|
44
|
-
"@bufbuild/protobuf": "^2.
|
|
45
|
-
"@earendil-works/pi-ai": "^0.80.
|
|
43
|
+
"@bastani/atomic-natives": "0.9.4-alpha.7",
|
|
44
|
+
"@bufbuild/protobuf": "^2.12.1",
|
|
45
|
+
"@earendil-works/pi-ai": "^0.80.3"
|
|
46
46
|
}
|
|
47
47
|
}
|
|
@@ -4,6 +4,12 @@ All notable changes to the `pi-intercom` extension will be documented in this fi
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.9.4-alpha.6] - 2026-07-01
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Aligned the intercom extension peer dependency with upstream pi TUI `^0.80.3`; no intercom extension source changes were needed for this metadata sync.
|
|
12
|
+
|
|
7
13
|
## [0.9.4-alpha.5] - 2026-07-01
|
|
8
14
|
|
|
9
15
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/intercom",
|
|
3
|
-
"version": "0.9.4-alpha.
|
|
3
|
+
"version": "0.9.4-alpha.7",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension providing a private coordination channel between parent and child agent sessions. Fork of: https://github.com/nicobailon/pi-intercom",
|
|
6
6
|
"contributors": [
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@bastani/atomic": "*",
|
|
42
|
-
"@earendil-works/pi-tui": "^0.80.
|
|
42
|
+
"@earendil-works/pi-tui": "^0.80.3"
|
|
43
43
|
},
|
|
44
44
|
"peerDependenciesMeta": {
|
|
45
45
|
"@bastani/atomic": {
|
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.9.4-alpha.6] - 2026-07-01
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Aligned the MCP extension peer dependencies with upstream pi AI/TUI `^0.80.3`; no MCP extension source changes were needed for this metadata sync.
|
|
15
|
+
|
|
10
16
|
## [0.9.4-alpha.5] - 2026-07-01
|
|
11
17
|
|
|
12
18
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/mcp",
|
|
3
|
-
"version": "0.9.4-alpha.
|
|
3
|
+
"version": "0.9.4-alpha.7",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension that adapts MCP (Model Context Protocol) servers into the coding agent. Fork of: https://github.com/nicobailon/pi-mcp-adapter",
|
|
6
6
|
"contributors": [
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"@bastani/atomic": "*",
|
|
35
|
-
"@earendil-works/pi-ai": "^0.80.
|
|
36
|
-
"@earendil-works/pi-tui": "^0.80.
|
|
35
|
+
"@earendil-works/pi-ai": "^0.80.3",
|
|
36
|
+
"@earendil-works/pi-tui": "^0.80.3",
|
|
37
37
|
"zod": "^3.25.0 || ^4.0.0"
|
|
38
38
|
},
|
|
39
39
|
"peerDependenciesMeta": {
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@modelcontextprotocol/ext-apps": "^1.7.
|
|
51
|
+
"@modelcontextprotocol/ext-apps": "^1.7.4",
|
|
52
52
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
53
53
|
"open": "^11.0.0",
|
|
54
54
|
"typebox": "^1.1.24",
|
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.9.4-alpha.7] - 2026-07-02
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed subagent model fallback so request/context incompatibility failures (HTTP 400/413/422 bad/unprocessable/payload-too-large request, unsupported tool/parameter, context-length/context-window overflow, `invalid_request`/`bad_request`/`too_large` errors) advance the chain to the next candidate instead of stopping. When none of the configured candidates can serve the request, the subagent now falls back to the current user-selected model. Refusals, content-filter/safety blocks, cancellations, and task failures still stop the chain ([#1580](https://github.com/bastani-inc/atomic/issues/1580)).
|
|
10
|
+
- Fixed foreground and background subagent model attempts that produced no child activity from hanging indefinitely. Each candidate attempt now has a conservative idle watchdog and absolute wall-clock cap, records a retryable timeout failure, and advances to the next fallback candidate; known providers without configured auth are skipped before spawning while unknown/custom providers and the current-model last resort are still attempted ([#1580](https://github.com/bastani-inc/atomic/issues/1580)).
|
|
11
|
+
- Fixed the per-attempt idle watchdog so an in-flight tool execution counts as activity: a slow, quiet tool call (long build or test run with no interim output) is no longer killed as a stalled attempt; the wall-clock cap still bounds such attempts ([#1581](https://github.com/bastani-inc/atomic/pull/1581)).
|
|
12
|
+
- Aligned the subagents model-failure classifier's direct-message precedence with the workflows classifier (direct-message classification now runs after nested cause/diagnostic traversal and before outer status/code classification), and added a cross-package conformance test suite so the two classifier copies cannot silently drift ([#1581](https://github.com/bastani-inc/atomic/pull/1581)).
|
|
13
|
+
- Fixed the background subagent runner to spawn one default-model attempt when no model candidates were ever configured (no primary model, no fallbacks, and no current model), mirroring the foreground path instead of silently exiting 1 with no error and no spawn. An explicitly empty candidate list produced by pre-spawn auth filtering is still respected and surfaced as an error ([#1581](https://github.com/bastani-inc/atomic/pull/1581)).
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Added a watchdog escape hatch: setting `ATOMIC_SUBAGENT_ATTEMPT_IDLE_TIMEOUT_MS` or `ATOMIC_SUBAGENT_ATTEMPT_TIMEOUT_MS` to `0` (or a negative value) now disables the corresponding per-attempt timeout entirely; non-numeric values are ignored and the defaults apply. The `ATOMIC_SUBAGENT_ATTEMPT_KILL_GRACE_MS` SIGTERM→SIGKILL grace period intentionally cannot be disabled — `0`, negative, or non-numeric values fall back to its default so escalation always stays bounded ([#1581](https://github.com/bastani-inc/atomic/pull/1581)).
|
|
18
|
+
|
|
19
|
+
## [0.9.4-alpha.6] - 2026-07-01
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- Aligned the subagents extension peer dependencies with upstream pi `^0.80.3` runtime packages; no subagents extension source changes were needed for this metadata sync.
|
|
24
|
+
|
|
5
25
|
## [0.9.4-alpha.5] - 2026-07-01
|
|
6
26
|
|
|
7
27
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/subagents",
|
|
3
|
-
"version": "0.9.4-alpha.
|
|
3
|
+
"version": "0.9.4-alpha.7",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension for delegating tasks to subagents with chains, parallel execution, and TUI clarification. Fork of: https://github.com/nicobailon/pi-subagents",
|
|
6
6
|
"contributors": [
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"@bastani/atomic": "*",
|
|
41
|
-
"@earendil-works/pi-agent-core": "^0.80.
|
|
42
|
-
"@earendil-works/pi-ai": "^0.80.
|
|
43
|
-
"@earendil-works/pi-tui": "^0.80.
|
|
41
|
+
"@earendil-works/pi-agent-core": "^0.80.3",
|
|
42
|
+
"@earendil-works/pi-ai": "^0.80.3",
|
|
43
|
+
"@earendil-works/pi-tui": "^0.80.3"
|
|
44
44
|
},
|
|
45
45
|
"peerDependenciesMeta": {
|
|
46
46
|
"@bastani/atomic": {
|
|
@@ -12,6 +12,7 @@ import type { RunnerStep } from "../shared/parallel-utils.ts";
|
|
|
12
12
|
import { injectSingleOutputInstruction, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
13
13
|
import { ChainOutputValidationError, validateChainOutputBindings } from "../shared/chain-outputs.ts";
|
|
14
14
|
import { buildModelCandidates, resolveModelCandidate } from "../shared/model-fallback.ts";
|
|
15
|
+
import { filterSpawnableModelCandidates } from "../shared/model-candidate-filter.ts";
|
|
15
16
|
import { NESTED_RUNS_DIR, nestedResultsPath, resolveInheritedNestedRouteFromEnv, resolveNestedParentAddressFromEnv, writeNestedEvent } from "../shared/nested-events.ts";
|
|
16
17
|
import { createStructuredOutputRuntime } from "../shared/structured-output.ts";
|
|
17
18
|
import { resolveExpectedWorktreeAgentCwd } from "../shared/worktree.ts";
|
|
@@ -57,6 +58,7 @@ export function executeAsyncChain(
|
|
|
57
58
|
const resultMode = params.resultMode ?? "chain";
|
|
58
59
|
const chainSkills = params.chainSkills ?? [];
|
|
59
60
|
const availableModels = params.availableModels;
|
|
61
|
+
const knownModelProviders = params.knownModelProviders;
|
|
60
62
|
const fastModeScope = resolveSubagentCodexFastModeScope(workflowStageSubagentGuard);
|
|
61
63
|
const runnerCwd = resolveChildCwd(ctx.cwd, cwd);
|
|
62
64
|
const firstStep = chain[0];
|
|
@@ -149,9 +151,16 @@ export function executeAsyncChain(
|
|
|
149
151
|
|
|
150
152
|
const primaryModel = resolveModelCandidate(behavior.model ?? a.model, availableModels, ctx.currentModelProvider);
|
|
151
153
|
const model = applyThinkingSuffix(primaryModel, a.thinking);
|
|
152
|
-
const
|
|
154
|
+
const rawModelCandidates = buildModelCandidates(behavior.model ?? a.model, a.fallbackModels, availableModels, ctx.currentModelProvider, ctx.currentModel, a.fallbackThinkingLevels)
|
|
153
155
|
.map((candidate) => applyThinkingSuffix(candidate, a.thinking))
|
|
154
156
|
.filter((candidate): candidate is string => typeof candidate === "string");
|
|
157
|
+
const filteredCandidates = filterSpawnableModelCandidates({
|
|
158
|
+
candidates: rawModelCandidates,
|
|
159
|
+
availableModels,
|
|
160
|
+
knownModelProviders,
|
|
161
|
+
currentModel: applyThinkingSuffix(ctx.currentModel, a.thinking),
|
|
162
|
+
});
|
|
163
|
+
const modelCandidates = filteredCandidates.candidates;
|
|
155
164
|
const fastModeSettings = getSubagentCodexFastModeSettings(stepCwd);
|
|
156
165
|
return {
|
|
157
166
|
agent: s.agent,
|
|
@@ -165,6 +174,7 @@ export function executeAsyncChain(
|
|
|
165
174
|
thinking: resolveEffectiveThinking(model, a.thinking),
|
|
166
175
|
...resolveSubagentModelFastModeMetadata({ model, modelCandidates, cwd: stepCwd, settings: fastModeSettings, scope: fastModeScope }),
|
|
167
176
|
modelCandidates,
|
|
177
|
+
modelAttempts: filteredCandidates.skippedAttempts,
|
|
168
178
|
codexFastModeSettings: fastModeSettings,
|
|
169
179
|
codexFastModeScope: fastModeScope,
|
|
170
180
|
tools: a.tools,
|
|
@@ -9,6 +9,7 @@ import { resolveChildCwd } from "../../shared/utils.ts";
|
|
|
9
9
|
import { applyThinkingSuffix, SUBAGENT_INTERCOM_SESSION_NAME_ENV } from "../shared/pi-args.ts";
|
|
10
10
|
import { injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
11
11
|
import { buildModelCandidates, resolveModelCandidate } from "../shared/model-fallback.ts";
|
|
12
|
+
import { filterSpawnableModelCandidates } from "../shared/model-candidate-filter.ts";
|
|
12
13
|
import { NESTED_RUNS_DIR, nestedResultsPath, resolveInheritedNestedRouteFromEnv, resolveNestedParentAddressFromEnv, writeNestedEvent } from "../shared/nested-events.ts";
|
|
13
14
|
import {
|
|
14
15
|
UNAVAILABLE_SUBAGENT_SKILL_ERROR,
|
|
@@ -50,6 +51,7 @@ export function executeAsyncSingle(
|
|
|
50
51
|
const runnerCwd = resolveChildCwd(ctx.cwd, cwd);
|
|
51
52
|
const skillNames = params.skills ?? agentConfig.skills ?? [];
|
|
52
53
|
const availableModels = params.availableModels;
|
|
54
|
+
const knownModelProviders = params.knownModelProviders;
|
|
53
55
|
const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(skillNames, runnerCwd, ctx.cwd);
|
|
54
56
|
if (missingSkills.includes("subagent")) return formatAsyncStartError("single", UNAVAILABLE_SUBAGENT_SKILL_ERROR);
|
|
55
57
|
let systemPrompt = agentConfig.systemPrompt?.trim() ?? "";
|
|
@@ -84,9 +86,16 @@ export function executeAsyncSingle(
|
|
|
84
86
|
resolveModelCandidate(params.modelOverride ?? agentConfig.model, availableModels, ctx.currentModelProvider),
|
|
85
87
|
agentConfig.thinking,
|
|
86
88
|
);
|
|
87
|
-
const
|
|
89
|
+
const rawModelCandidates = buildModelCandidates(params.modelOverride ?? agentConfig.model, agentConfig.fallbackModels, availableModels, ctx.currentModelProvider, ctx.currentModel)
|
|
88
90
|
.map((candidate) => applyThinkingSuffix(candidate, agentConfig.thinking))
|
|
89
91
|
.filter((candidate): candidate is string => typeof candidate === "string");
|
|
92
|
+
const filteredCandidates = filterSpawnableModelCandidates({
|
|
93
|
+
candidates: rawModelCandidates,
|
|
94
|
+
availableModels,
|
|
95
|
+
knownModelProviders,
|
|
96
|
+
currentModel: applyThinkingSuffix(ctx.currentModel, agentConfig.thinking),
|
|
97
|
+
});
|
|
98
|
+
const modelCandidates = filteredCandidates.candidates;
|
|
90
99
|
const fastModeSettings = getSubagentCodexFastModeSettings(runnerCwd);
|
|
91
100
|
const fastModeScope = resolveSubagentCodexFastModeScope(workflowStageSubagentGuard);
|
|
92
101
|
let spawnResult: AsyncSpawnResult = {};
|
|
@@ -103,6 +112,7 @@ export function executeAsyncSingle(
|
|
|
103
112
|
thinking: resolveEffectiveThinking(model, agentConfig.thinking),
|
|
104
113
|
...resolveSubagentModelFastModeMetadata({ model, modelCandidates, cwd: runnerCwd, settings: fastModeSettings, scope: fastModeScope }),
|
|
105
114
|
modelCandidates,
|
|
115
|
+
modelAttempts: filteredCandidates.skippedAttempts,
|
|
106
116
|
codexFastModeSettings: fastModeSettings,
|
|
107
117
|
codexFastModeScope: fastModeScope,
|
|
108
118
|
tools: agentConfig.tools,
|
|
@@ -26,6 +26,7 @@ export interface AsyncChainParams {
|
|
|
26
26
|
agents: AgentConfig[];
|
|
27
27
|
ctx: AsyncExecutionContext;
|
|
28
28
|
availableModels?: AvailableModelInfo[];
|
|
29
|
+
knownModelProviders?: string[];
|
|
29
30
|
cwd?: string;
|
|
30
31
|
maxOutput?: MaxOutputConfig;
|
|
31
32
|
artifactsDir?: string;
|
|
@@ -62,6 +63,7 @@ export interface AsyncSingleParams {
|
|
|
62
63
|
outputMode?: "inline" | "file-only";
|
|
63
64
|
modelOverride?: string;
|
|
64
65
|
availableModels?: AvailableModelInfo[];
|
|
66
|
+
knownModelProviders?: string[];
|
|
65
67
|
maxSubagentDepth: number;
|
|
66
68
|
workflowStageSubagentGuard?: boolean;
|
|
67
69
|
worktreeSetupHook?: string;
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
import { formatModelAttemptNote, isRetryableModelFailure } from "../shared/model-fallback.ts";
|
|
18
18
|
import type { ArtifactPaths, ModelAttempt } from "../../shared/types.ts";
|
|
19
19
|
import type { RunPiStreamingResult, SingleStepContext, SubagentStep } from "./subagent-runner-types.ts";
|
|
20
|
-
import { fastModeForStepAttempt } from "./subagent-runner-utils.ts";
|
|
20
|
+
import { emptyUsage, fastModeForStepAttempt } from "./subagent-runner-utils.ts";
|
|
21
21
|
import { runPiStreaming } from "./subagent-runner-streaming.ts";
|
|
22
22
|
|
|
23
23
|
export { outputEntryFromAsyncResult };
|
|
@@ -61,14 +61,26 @@ export async function runSingleStep(
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
// `!== undefined` is intentional: an explicitly empty array means every candidate
|
|
65
|
+
// was removed by pre-spawn filtering (see filterSpawnableModelCandidates) and must
|
|
66
|
+
// be respected — do not "simplify" this back to `.length > 0`, which would spawn a
|
|
67
|
+
// doomed default attempt. Pre-spawn filtering always records each removal as a
|
|
68
|
+
// skipped attempt in step.modelAttempts, so an empty array WITHOUT skipped attempts
|
|
69
|
+
// means no candidates were configured at all (no primary model, no fallbacks, no
|
|
70
|
+
// current model); mirror the foreground path (execution-run-sync.ts `modelsToTry`)
|
|
71
|
+
// and run one default-model attempt instead of silently exiting with no attempt.
|
|
72
|
+
// The filtered-to-empty case is surfaced as an error below.
|
|
73
|
+
const preSkippedAttempts = step.modelAttempts ?? [];
|
|
74
|
+
const candidates = step.modelCandidates !== undefined && (step.modelCandidates.length > 0 || preSkippedAttempts.length > 0)
|
|
65
75
|
? step.modelCandidates
|
|
66
76
|
: step.model
|
|
67
77
|
? [step.model]
|
|
68
78
|
: [undefined];
|
|
69
79
|
const attemptedModels: string[] = [];
|
|
70
|
-
const modelAttempts: ModelAttempt[] = [];
|
|
71
|
-
const attemptNotes: string[] =
|
|
80
|
+
const modelAttempts: ModelAttempt[] = [...preSkippedAttempts];
|
|
81
|
+
const attemptNotes: string[] = modelAttempts
|
|
82
|
+
.filter((attempt) => !attempt.success && attempt.exitCode === null && attempt.error)
|
|
83
|
+
.map((attempt) => `[fallback] ${attempt.error}`);
|
|
72
84
|
const pendingAttemptNotes: string[] = [];
|
|
73
85
|
const eventsPath = path.join(path.dirname(ctx.outputFile), "events.jsonl");
|
|
74
86
|
let finalResult: RunPiStreamingResult | undefined;
|
|
@@ -205,6 +217,17 @@ export async function runSingleStep(
|
|
|
205
217
|
if (!tryNextModel) break;
|
|
206
218
|
}
|
|
207
219
|
|
|
220
|
+
if (!finalResult && candidates.length === 0 && modelAttempts.length > 0) {
|
|
221
|
+
finalResult = {
|
|
222
|
+
stderr: "",
|
|
223
|
+
exitCode: 1,
|
|
224
|
+
messages: [],
|
|
225
|
+
usage: emptyUsage(),
|
|
226
|
+
error: "No spawnable subagent model candidates after pre-spawn filtering.",
|
|
227
|
+
finalOutput: "",
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
208
231
|
const rawOutput = finalResult?.finalOutput ?? "";
|
|
209
232
|
const outputForPersistence = rawOutput;
|
|
210
233
|
const resolvedOutput = step.outputPath && finalResult?.exitCode === 0
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
shouldStartSubagentFinalDrain,
|
|
13
13
|
} from "../shared/final-drain.ts";
|
|
14
14
|
import { modelFailureMessage } from "../shared/model-fallback.ts";
|
|
15
|
+
import { createAttemptWatchdog } from "../shared/attempt-watchdog.ts";
|
|
15
16
|
import type { ChildEvent, ChildEventContext, RunPiStreamingResult } from "./subagent-runner-types.ts";
|
|
16
17
|
import { emptyUsage } from "./subagent-runner-utils.ts";
|
|
17
18
|
|
|
@@ -55,6 +56,7 @@ export function runPiStreaming(
|
|
|
55
56
|
let assistantError: string | undefined;
|
|
56
57
|
let assistantFailureSignal: unknown;
|
|
57
58
|
let interrupted = false;
|
|
59
|
+
let activeToolExecutions = 0;
|
|
58
60
|
const rawStdoutLines: string[] = [];
|
|
59
61
|
|
|
60
62
|
const writeOutputLine = (line: string) => {
|
|
@@ -99,6 +101,11 @@ export function runPiStreaming(
|
|
|
99
101
|
appendChildEvent(event);
|
|
100
102
|
onChildEvent?.(event);
|
|
101
103
|
|
|
104
|
+
// Track in-flight tool executions so the idle watchdog does not mistake a
|
|
105
|
+
// slow, quiet tool call for a stalled attempt.
|
|
106
|
+
if (event.type === "tool_execution_start") activeToolExecutions += 1;
|
|
107
|
+
else if (event.type === "tool_execution_end") activeToolExecutions = Math.max(0, activeToolExecutions - 1);
|
|
108
|
+
|
|
102
109
|
if (event.type === "tool_execution_start" && event.toolName) {
|
|
103
110
|
const toolArgs = extractToolArgsPreview(event.args ?? {});
|
|
104
111
|
writeOutputLine(toolArgs ? `${event.toolName}: ${toolArgs}` : event.toolName);
|
|
@@ -163,7 +170,22 @@ export function runPiStreaming(
|
|
|
163
170
|
let finalHardKillTimer: NodeJS.Timeout | undefined;
|
|
164
171
|
let settled = false;
|
|
165
172
|
const clearStdioGuard = attachPostExitStdioGuard(child, { idleMs: 2000, hardMs: 8000 });
|
|
173
|
+
const attemptWatchdog = createAttemptWatchdog({
|
|
174
|
+
child,
|
|
175
|
+
isSettled: () => settled,
|
|
176
|
+
// An in-flight tool execution counts as watchdog activity so a slow, quiet
|
|
177
|
+
// tool call is not mistaken for a stalled attempt. Caveat: the counter only
|
|
178
|
+
// decrements on tool_execution_end, so if a tool ends abnormally without its
|
|
179
|
+
// end event the idle watchdog is deferred indefinitely and the wall-clock cap
|
|
180
|
+
// becomes the sole backstop — do not lower the wall cap assuming idle fires.
|
|
181
|
+
isToolActive: () => activeToolExecutions > 0,
|
|
182
|
+
onTimeout(message) {
|
|
183
|
+
forcedTerminationSignal = true;
|
|
184
|
+
error ??= message;
|
|
185
|
+
},
|
|
186
|
+
});
|
|
166
187
|
child.stdout.on("data", (chunk: Buffer) => {
|
|
188
|
+
attemptWatchdog.activity();
|
|
167
189
|
const text = chunk.toString();
|
|
168
190
|
stdoutBuf += text;
|
|
169
191
|
const lines = stdoutBuf.split("\n");
|
|
@@ -172,6 +194,7 @@ export function runPiStreaming(
|
|
|
172
194
|
});
|
|
173
195
|
|
|
174
196
|
child.stderr.on("data", (chunk: Buffer) => {
|
|
197
|
+
attemptWatchdog.activity();
|
|
175
198
|
processStderrText(chunk.toString());
|
|
176
199
|
});
|
|
177
200
|
registerInterrupt?.(() => {
|
|
@@ -220,6 +243,7 @@ export function runPiStreaming(
|
|
|
220
243
|
registerInterrupt?.(undefined);
|
|
221
244
|
clearDrainTimers();
|
|
222
245
|
clearStdioGuard();
|
|
246
|
+
attemptWatchdog.clear();
|
|
223
247
|
if (stdoutBuf.trim()) processStdoutLine(stdoutBuf);
|
|
224
248
|
if (stderrBuf.trim()) appendChildLine("subagent.child.stderr", stderrBuf);
|
|
225
249
|
outputStream.end();
|
|
@@ -246,6 +270,7 @@ export function runPiStreaming(
|
|
|
246
270
|
registerInterrupt?.(undefined);
|
|
247
271
|
clearDrainTimers();
|
|
248
272
|
clearStdioGuard();
|
|
273
|
+
attemptWatchdog.clear();
|
|
249
274
|
outputStream.end();
|
|
250
275
|
const finalOutput = getFinalOutput(messages) || rawStdoutLines.join("\n").trim();
|
|
251
276
|
const spawnErrorMessage = spawnError instanceof Error ? spawnError.message : String(spawnError);
|
|
@@ -96,6 +96,7 @@ export async function runDynamicParallelChainStep(input: {
|
|
|
96
96
|
agents: context.agents,
|
|
97
97
|
stepIndex,
|
|
98
98
|
availableModels: context.availableModels,
|
|
99
|
+
knownModelProviders: context.knownModelProviders,
|
|
99
100
|
chainDir: context.chainDir,
|
|
100
101
|
prev: state.prev,
|
|
101
102
|
originalTask: context.originalTask,
|
|
@@ -125,6 +125,7 @@ export async function runParallelChainTasks(input: ParallelChainRunInput): Promi
|
|
|
125
125
|
nestedRoute: input.nestedRoute,
|
|
126
126
|
modelOverride: effectiveModel,
|
|
127
127
|
availableModels: input.availableModels,
|
|
128
|
+
knownModelProviders: input.knownModelProviders,
|
|
128
129
|
currentModel: currentModelFullId(input.ctx.model),
|
|
129
130
|
preferredModelProvider: input.ctx.model?.provider,
|
|
130
131
|
skills: behavior.skills === false ? [] : behavior.skills,
|
|
@@ -78,6 +78,7 @@ export async function runStaticParallelChainStep(input: {
|
|
|
78
78
|
agents: context.agents,
|
|
79
79
|
stepIndex,
|
|
80
80
|
availableModels: context.availableModels,
|
|
81
|
+
knownModelProviders: context.knownModelProviders,
|
|
81
82
|
chainDir: context.chainDir,
|
|
82
83
|
prev: state.prev,
|
|
83
84
|
originalTask: context.originalTask,
|
|
@@ -126,6 +126,7 @@ export async function runSequentialChainStep(input: {
|
|
|
126
126
|
nestedRoute: context.params.nestedRoute,
|
|
127
127
|
modelOverride: effectiveModel,
|
|
128
128
|
availableModels: context.availableModels,
|
|
129
|
+
knownModelProviders: context.knownModelProviders,
|
|
129
130
|
currentModel: currentModelFullId(context.ctx.model),
|
|
130
131
|
preferredModelProvider: context.ctx.model?.provider,
|
|
131
132
|
skills: behavior.skills === false ? [] : behavior.skills,
|
|
@@ -109,6 +109,7 @@ export interface ParallelChainRunInput {
|
|
|
109
109
|
agents: AgentConfig[];
|
|
110
110
|
stepIndex: number;
|
|
111
111
|
availableModels: ModelInfo[];
|
|
112
|
+
knownModelProviders: string[];
|
|
112
113
|
chainDir: string;
|
|
113
114
|
prev: string;
|
|
114
115
|
originalTask: string;
|
|
@@ -179,6 +180,7 @@ export interface ChainRuntimeContext {
|
|
|
179
180
|
chainSkills: string[];
|
|
180
181
|
chainDir: string;
|
|
181
182
|
availableModels: ModelInfo[];
|
|
183
|
+
knownModelProviders: string[];
|
|
182
184
|
originalTask: string;
|
|
183
185
|
chainAgents: string[];
|
|
184
186
|
chainSteps: ChainStep[];
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Public foreground chain execution API.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
|
|
5
|
+
import { collectKnownModelProviders, toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
|
|
6
6
|
import {
|
|
7
7
|
createChainDir,
|
|
8
8
|
isDynamicParallelStep,
|
|
@@ -117,6 +117,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
117
117
|
let templates: ResolvedTemplates = resolveChainTemplates(chainSteps);
|
|
118
118
|
let tuiBehaviorOverrides: Awaited<ReturnType<typeof resolveChainClarification>>["tuiBehaviorOverrides"];
|
|
119
119
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
120
|
+
const knownModelProviders = collectKnownModelProviders(ctx.modelRegistry);
|
|
120
121
|
const context: ChainRuntimeContext = {
|
|
121
122
|
params,
|
|
122
123
|
agents,
|
|
@@ -140,6 +141,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
140
141
|
chainSkills,
|
|
141
142
|
chainDir,
|
|
142
143
|
availableModels,
|
|
144
|
+
knownModelProviders,
|
|
143
145
|
originalTask,
|
|
144
146
|
chainAgents,
|
|
145
147
|
chainSteps,
|
|
@@ -17,6 +17,7 @@ import { applyThinkingSuffix, buildPiArgs, cleanupTempDir } from "../shared/pi-a
|
|
|
17
17
|
import { getPiSpawnCommand } from "../shared/pi-spawn.ts";
|
|
18
18
|
import { assistantStopReason, isAssistantFailureStopReason, shouldStartSubagentFinalDrain } from "../shared/final-drain.ts";
|
|
19
19
|
import { modelFailureMessage } from "../shared/model-fallback.ts";
|
|
20
|
+
import { createAttemptWatchdog } from "../shared/attempt-watchdog.ts";
|
|
20
21
|
import {
|
|
21
22
|
createMutatingFailureState,
|
|
22
23
|
didMutatingToolFail,
|
|
@@ -204,6 +205,7 @@ export async function runSingleAttempt(
|
|
|
204
205
|
settled = true;
|
|
205
206
|
clearFinalDrainTimers();
|
|
206
207
|
clearStdioGuard();
|
|
208
|
+
attemptWatchdog.clear();
|
|
207
209
|
if (activityTimer) {
|
|
208
210
|
clearInterval(activityTimer);
|
|
209
211
|
activityTimer = undefined;
|
|
@@ -358,13 +360,31 @@ export async function runSingleAttempt(
|
|
|
358
360
|
|
|
359
361
|
let stderrBuf = "";
|
|
360
362
|
const clearStdioGuard = attachPostExitStdioGuard(proc, { idleMs: 2000, hardMs: 8000 });
|
|
363
|
+
const attemptWatchdog = createAttemptWatchdog({
|
|
364
|
+
child: proc,
|
|
365
|
+
isSettled: () => settled || processClosed || detached,
|
|
366
|
+
// A slow, quiet tool call (long build/test run) must not be mistaken for a
|
|
367
|
+
// stalled attempt: an in-flight tool execution counts as watchdog activity.
|
|
368
|
+
// Caveat: `progress.currentTool` is cleared on tool_execution_end, so if a
|
|
369
|
+
// tool ends abnormally without emitting its end event the idle watchdog is
|
|
370
|
+
// deferred indefinitely and the wall-clock cap becomes the sole backstop for
|
|
371
|
+
// this attempt — do not lower the wall cap assuming idle always fires.
|
|
372
|
+
isToolActive: () => progress.currentTool !== undefined,
|
|
373
|
+
onTimeout(message) {
|
|
374
|
+
forcedTerminationSignal = true;
|
|
375
|
+
result.error ??= message;
|
|
376
|
+
progress.error = message;
|
|
377
|
+
},
|
|
378
|
+
});
|
|
361
379
|
proc.stdout.on("data", (d) => {
|
|
380
|
+
attemptWatchdog.activity();
|
|
362
381
|
buf += d.toString();
|
|
363
382
|
const lines = buf.split("\n");
|
|
364
383
|
buf = lines.pop() || "";
|
|
365
384
|
lines.forEach(processLine);
|
|
366
385
|
});
|
|
367
386
|
proc.stderr.on("data", (d) => {
|
|
387
|
+
attemptWatchdog.activity();
|
|
368
388
|
stderrBuf += d.toString();
|
|
369
389
|
});
|
|
370
390
|
proc.on("exit", () => {
|
|
@@ -374,6 +394,7 @@ export async function runSingleAttempt(
|
|
|
374
394
|
proc.on("close", (code, signal) => {
|
|
375
395
|
clearFinalDrainTimers();
|
|
376
396
|
clearStdioGuard();
|
|
397
|
+
attemptWatchdog.clear();
|
|
377
398
|
void jsonlWriter.close().catch(() => undefined);
|
|
378
399
|
cleanupTempDir(tempDir);
|
|
379
400
|
if (detached) {
|
|
@@ -394,6 +415,7 @@ export async function runSingleAttempt(
|
|
|
394
415
|
proc.on("error", (error) => {
|
|
395
416
|
clearFinalDrainTimers();
|
|
396
417
|
clearStdioGuard();
|
|
418
|
+
attemptWatchdog.clear();
|
|
397
419
|
void jsonlWriter.close().catch(() => undefined);
|
|
398
420
|
cleanupTempDir(tempDir);
|
|
399
421
|
if (!result.error) result.error = error instanceof Error ? error.message : String(error);
|