@bastani/atomic 0.9.5-alpha.7 → 0.9.5-alpha.9
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 +33 -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/CHANGELOG.md +11 -0
- package/dist/builtin/mcp/README.md +6 -6
- package/dist/builtin/mcp/direct-tools.ts +41 -23
- package/dist/builtin/mcp/index.ts +31 -4
- package/dist/builtin/mcp/init.ts +4 -43
- package/dist/builtin/mcp/metadata-hydration.ts +37 -0
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/mcp/proxy-info-modes.ts +67 -14
- package/dist/builtin/mcp/startup-warmup.ts +98 -0
- package/dist/builtin/mcp/tool-result-renderer.ts +3 -3
- package/dist/builtin/subagents/CHANGELOG.md +18 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/extension/index.ts +14 -25
- package/dist/builtin/subagents/src/extension/startup-maintenance.ts +89 -0
- package/dist/builtin/subagents/src/runs/background/async-execution-common.ts +23 -18
- package/dist/builtin/subagents/src/runs/background/result-watcher.ts +20 -10
- package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +13 -3
- package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +10 -2
- package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +28 -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 +72 -66
- 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 +29 -0
- package/dist/builtin/workflows/README.md +4 -2
- 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 +71 -22
- 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/skills/impeccable/SKILL.md +16 -13
- package/dist/builtin/workflows/skills/impeccable/reference/adapt.md +1 -0
- package/dist/builtin/workflows/skills/impeccable/reference/adapt.native.md +58 -0
- package/dist/builtin/workflows/skills/impeccable/reference/android.md +40 -0
- package/dist/builtin/workflows/skills/impeccable/reference/animate.md +2 -0
- package/dist/builtin/workflows/skills/impeccable/reference/audit.md +2 -0
- package/dist/builtin/workflows/skills/impeccable/reference/audit.native.md +139 -0
- package/dist/builtin/workflows/skills/impeccable/reference/bolder.md +69 -62
- package/dist/builtin/workflows/skills/impeccable/reference/critique.md +21 -7
- package/dist/builtin/workflows/skills/impeccable/reference/document.md +1 -1
- package/dist/builtin/workflows/skills/impeccable/reference/hooks.md +92 -0
- package/dist/builtin/workflows/skills/impeccable/reference/init.md +29 -7
- package/dist/builtin/workflows/skills/impeccable/reference/ios.md +45 -0
- package/dist/builtin/workflows/skills/impeccable/reference/layout.md +25 -1
- package/dist/builtin/workflows/skills/impeccable/reference/typeset.md +23 -1
- package/dist/builtin/workflows/skills/impeccable/scripts/context-signals.mjs +2 -1
- package/dist/builtin/workflows/skills/impeccable/scripts/context.mjs +75 -14
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/browser/injected/index.mjs +1 -1
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/cli/main.mjs +31 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/design-system.mjs +64 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +118 -11
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +16 -11
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/registry/antipatterns.mjs +66 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/rules/checks.mjs +42 -10
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/fonts.mjs +30 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/page.mjs +4 -6
- package/dist/builtin/workflows/skills/impeccable/scripts/hook-admin.mjs +660 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/hook-before-edit.mjs +516 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/hook-lib.mjs +1764 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/hook.mjs +61 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/lib/impeccable-config.mjs +4 -2
- package/dist/builtin/workflows/skills/impeccable/scripts/lib/is-generated.mjs +2 -2
- package/dist/builtin/workflows/skills/impeccable/scripts/live/svelte-component.mjs +13 -16
- package/dist/builtin/workflows/skills/impeccable/scripts/live-accept.mjs +17 -11
- package/dist/builtin/workflows/skills/impeccable/scripts/live-browser.js +104 -37
- package/dist/builtin/workflows/skills/impeccable/scripts/live-wrap.mjs +9 -0
- package/dist/builtin/workflows/src/durable/backend.ts +12 -0
- package/dist/builtin/workflows/src/durable/child-primitive.ts +12 -8
- package/dist/builtin/workflows/src/durable/dbos-backend.ts +63 -2
- package/dist/builtin/workflows/src/durable/dbos-envelope.ts +28 -0
- package/dist/builtin/workflows/src/durable/resume-catalog.ts +12 -0
- package/dist/builtin/workflows/src/durable/resume-runtime.ts +5 -2
- package/dist/builtin/workflows/src/durable/stage-primitive.ts +165 -27
- package/dist/builtin/workflows/src/durable/types.ts +30 -1
- package/dist/builtin/workflows/src/engine/primitives/task.ts +1 -0
- package/dist/builtin/workflows/src/engine/run-durable-finalize.ts +1 -2
- package/dist/builtin/workflows/src/engine/run-returned-status.ts +50 -16
- package/dist/builtin/workflows/src/engine/run.ts +32 -32
- package/dist/builtin/workflows/src/engine/runtime.ts +5 -0
- package/dist/builtin/workflows/src/extension/extension-factory.ts +3 -0
- package/dist/builtin/workflows/src/extension/extension-lifecycle.ts +9 -2
- package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +137 -27
- package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +22 -6
- package/dist/builtin/workflows/src/extension/public-types.ts +1 -1
- package/dist/builtin/workflows/src/extension/workflow-command-completions.ts +13 -1
- package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +19 -4
- package/dist/builtin/workflows/src/extension/workflow-command-surfaces.ts +9 -0
- package/dist/builtin/workflows/src/extension/workflow-prompts.ts +2 -0
- package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +48 -23
- package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +20 -8
- package/dist/builtin/workflows/src/extension/workflow-tool.ts +24 -8
- package/dist/builtin/workflows/src/runs/background/status.ts +4 -3
- package/dist/builtin/workflows/src/runs/foreground/executor-continuation.ts +13 -3
- package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +29 -8
- package/dist/builtin/workflows/src/runs/foreground/executor-hil.ts +2 -1
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +4 -3
- 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/runs/shared/worktree-git.ts +155 -20
- package/dist/builtin/workflows/src/runs/shared/worktree-types.ts +6 -0
- package/dist/builtin/workflows/src/runs/shared/worktree.ts +2 -1
- 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/builtin/workflows/src/shared/returned-run-status.ts +116 -0
- package/dist/builtin/workflows/src/shared/workflow-failures-decisions.ts +2 -0
- package/dist/builtin/workflows/src/tui/graph-view-render-helpers.ts +21 -3
- package/dist/builtin/workflows/src/tui/graph-view-state.ts +3 -0
- package/dist/builtin/workflows/src/tui/graph-view-types.ts +3 -0
- package/dist/builtin/workflows/src/tui/status-list.ts +22 -15
- package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +4 -4
- 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 +21 -2
- package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
- package/dist/core/agent-session-auto-compaction.js +163 -11
- 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 +10 -1
- package/dist/core/agent-session-events.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +12 -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 +6 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +6 -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/extensions/loader-virtual-modules.d.ts +8 -1
- package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.js +2 -1
- package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +6 -0
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +72 -29
- package/dist/core/footer-data-provider.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/openai-responses-payload-sanitizer.d.ts +1 -0
- package/dist/core/openai-responses-payload-sanitizer.d.ts.map +1 -1
- package/dist/core/openai-responses-payload-sanitizer.js +24 -9
- package/dist/core/openai-responses-payload-sanitizer.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/core/slash-commands.d.ts +2 -0
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +141 -0
- package/dist/core/slash-commands.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 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.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 +23 -2
- 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 +154 -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 +20 -14
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-actions.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-actions.js +0 -1
- package/dist/modes/interactive/components/chat-session-host-actions.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 +25 -0
- package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
- package/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-autocomplete.js +21 -9
- package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
- package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-deferred-startup.js +41 -28
- package/dist/modes/interactive/interactive-deferred-startup.js.map +1 -1
- package/dist/modes/interactive/interactive-hotkeys-debug.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-hotkeys-debug.js +1 -0
- package/dist/modes/interactive/interactive-hotkeys-debug.js.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.js +318 -185
- package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.d.ts +12 -0
- package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.js +39 -0
- package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-deps.d.ts +1 -1
- package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-deps.js +1 -1
- package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts +6 -2
- 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-model-routing.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-model-routing.js +3 -1
- package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
- package/dist/modes/interactive/interactive-process-lifecycle.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-process-lifecycle.js +0 -3
- package/dist/modes/interactive/interactive-process-lifecycle.js.map +1 -1
- package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-render-chat.js +36 -10
- package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
- package/dist/modes/interactive/interactive-session-routing.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-session-routing.js +5 -1
- package/dist/modes/interactive/interactive-session-routing.js.map +1 -1
- package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-startup.js +26 -22
- package/dist/modes/interactive/interactive-startup.js.map +1 -1
- package/dist/modes/interactive/theme/global-theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/global-theme.js +26 -2
- package/dist/modes/interactive/theme/global-theme.js.map +1 -1
- package/dist/utils/fs-watch.d.ts +24 -1
- package/dist/utils/fs-watch.d.ts.map +1 -1
- package/dist/utils/fs-watch.js +60 -5
- package/dist/utils/fs-watch.js.map +1 -1
- package/docs/compaction.md +8 -0
- package/docs/development.md +4 -0
- package/docs/extensions.md +5 -0
- package/docs/json.md +3 -1
- package/docs/providers.md +1 -0
- package/docs/quickstart.md +1 -1
- package/docs/rpc.md +3 -1
- package/docs/settings.md +27 -1
- package/docs/subagents.md +4 -0
- package/docs/terminal-setup.md +4 -0
- package/docs/usage.md +2 -2
- package/docs/windows.md +4 -0
- package/docs/workflows.md +21 -9
- package/npm-shrinkwrap.json +513 -23
- package/package.json +3 -2
|
@@ -7,21 +7,18 @@ import { APP_NAME, getEnvValue } from "@bastani/atomic";
|
|
|
7
7
|
import { type ExtensionAPI, type ExtensionContext, type ToolDefinition } from "@bastani/atomic";
|
|
8
8
|
import { Box, Container, Spacer, Text, truncateToWidth, visibleWidth, wrapTextWithAnsi, type Component } from "@earendil-works/pi-tui";
|
|
9
9
|
import { discoverAgents } from "../agents/agents.ts";
|
|
10
|
-
import {
|
|
10
|
+
import { getArtifactsDir } from "../shared/artifacts.ts";
|
|
11
11
|
import { resolveCurrentSessionId } from "../shared/session-identity.ts";
|
|
12
|
-
import { cleanupOldChainDirs } from "../shared/settings.ts";
|
|
13
12
|
import { advanceResultPulseFrame, renderLiveSubagentResult, renderSubagentResult, stopResultAnimations, stopWidgetAnimation, type SubagentResultRenderState } from "../tui/render.ts";
|
|
14
13
|
import { SubagentParams } from "./schemas.ts";
|
|
15
14
|
import { createSubagentExecutor, type SubagentParamsLike } from "../runs/foreground/subagent-executor.ts";
|
|
16
15
|
import { createAsyncJobTracker } from "../runs/background/async-job-tracker.ts";
|
|
17
|
-
import { createResultWatcher } from "../runs/background/result-watcher.ts";
|
|
18
16
|
import { registerSlashCommands } from "../slash/slash-commands.ts";
|
|
19
17
|
import { registerPromptTemplateDelegationBridge } from "../slash/prompt-template-bridge.ts";
|
|
20
18
|
import { registerSlashSubagentBridge } from "../slash/slash-bridge.ts";
|
|
21
19
|
import { clearSlashSnapshots, getSlashRenderableSnapshot, resolveSlashMessageDetails, restoreSlashFinalSnapshots, type SlashMessageDetails } from "../slash/slash-live-state.ts";
|
|
22
20
|
import { inspectSubagentStatus } from "../runs/background/run-status.ts";
|
|
23
21
|
import registerSubagentNotify, { type SubagentNotifyDetails } from "../runs/background/notify.ts";
|
|
24
|
-
import { cleanupOldNestedRuntimeDirs } from "../runs/shared/nested-events.ts";
|
|
25
22
|
import { SUBAGENT_CHILD_ENV, SUBAGENT_FANOUT_CHILD_ENV } from "../runs/shared/pi-args.ts";
|
|
26
23
|
import registerFanoutChildSubagentExtension from "./fanout-child.ts";
|
|
27
24
|
import { formatDuration, shortenPath } from "../shared/formatters.ts";
|
|
@@ -29,6 +26,7 @@ import { loadConfig } from "./config.ts";
|
|
|
29
26
|
import { DEFAULT_PROMPT_GUIDANCE } from "./prompt-guidance.ts";
|
|
30
27
|
import { type Details, type SubagentState, ASYNC_DIR, DEFAULT_ARTIFACT_CONFIG, RESULTS_DIR, SLASH_RESULT_TYPE, SUBAGENT_ASYNC_COMPLETE_EVENT, SUBAGENT_ASYNC_STARTED_EVENT, SUBAGENT_CONTROL_EVENT, WIDGET_KEY } from "../shared/types.ts";
|
|
31
28
|
import { clearPendingForegroundControlNotices, formatSubagentControlNotice, handleSubagentControlNotice, SUBAGENT_CONTROL_MESSAGE_TYPE, type SubagentControlMessageDetails } from "./control-notices.ts";
|
|
29
|
+
import { createSubagentStartupMaintenance } from "./startup-maintenance.ts";
|
|
32
30
|
export { loadConfig } from "./config.ts";
|
|
33
31
|
function getSubagentSessionRoot(parentSessionFile: string | null): string {
|
|
34
32
|
if (parentSessionFile) {
|
|
@@ -171,12 +169,9 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
171
169
|
}
|
|
172
170
|
ensureAccessibleDir(RESULTS_DIR);
|
|
173
171
|
ensureAccessibleDir(ASYNC_DIR);
|
|
174
|
-
cleanupOldChainDirs();
|
|
175
172
|
const config = loadConfig();
|
|
176
173
|
const asyncByDefault = config.asyncByDefault === true;
|
|
177
174
|
const tempArtifactsDir = getArtifactsDir(null);
|
|
178
|
-
cleanupAllArtifactDirs(DEFAULT_ARTIFACT_CONFIG.cleanupDays);
|
|
179
|
-
cleanupOldNestedRuntimeDirs(DEFAULT_ARTIFACT_CONFIG.cleanupDays);
|
|
180
175
|
const state: SubagentState = {
|
|
181
176
|
baseCwd: "",
|
|
182
177
|
currentSessionId: null,
|
|
@@ -197,16 +192,16 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
197
192
|
clear: () => {},
|
|
198
193
|
},
|
|
199
194
|
};
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
);
|
|
206
|
-
|
|
207
|
-
|
|
195
|
+
const maintenance = createSubagentStartupMaintenance(pi, state, {
|
|
196
|
+
resultsDir: RESULTS_DIR,
|
|
197
|
+
artifactCleanupDays: DEFAULT_ARTIFACT_CONFIG.cleanupDays,
|
|
198
|
+
resultTtlMs: 10 * 60 * 1000,
|
|
199
|
+
});
|
|
200
|
+
maintenance.scheduleStartupCleanup();
|
|
201
|
+
maintenance.startResultWatcherDeferred();
|
|
202
|
+
maintenance.primeExistingResultsDeferred();
|
|
208
203
|
const runtimeCleanup = () => {
|
|
209
|
-
|
|
204
|
+
maintenance.stop();
|
|
210
205
|
stopWidgetAnimation();
|
|
211
206
|
stopResultAnimations();
|
|
212
207
|
clearPendingForegroundControlNotices(state);
|
|
@@ -430,13 +425,7 @@ DIAGNOSTICS:
|
|
|
430
425
|
if (state.asyncJobs.size > 0) ensurePoller();
|
|
431
426
|
});
|
|
432
427
|
const cleanupSessionArtifacts = (ctx: ExtensionContext) => {
|
|
433
|
-
|
|
434
|
-
const sessionFile = ctx.sessionManager.getSessionFile();
|
|
435
|
-
if (sessionFile) {
|
|
436
|
-
cleanupOldArtifacts(getArtifactsDir(sessionFile), DEFAULT_ARTIFACT_CONFIG.cleanupDays);
|
|
437
|
-
}
|
|
438
|
-
} catch {
|
|
439
|
-
}
|
|
428
|
+
maintenance.cleanupSessionArtifactsDeferred(ctx);
|
|
440
429
|
};
|
|
441
430
|
const resetSessionState = (ctx: ExtensionContext) => {
|
|
442
431
|
state.baseCwd = ctx.cwd;
|
|
@@ -447,7 +436,7 @@ DIAGNOSTICS:
|
|
|
447
436
|
resetJobs(ctx);
|
|
448
437
|
hydrateActiveJobs(ctx);
|
|
449
438
|
restoreSlashFinalSnapshots(ctx.sessionManager.getEntries());
|
|
450
|
-
|
|
439
|
+
maintenance.primeExistingResultsDeferred();
|
|
451
440
|
};
|
|
452
441
|
pi.on("session_start", (_event, ctx) => {
|
|
453
442
|
resetSessionState(ctx);
|
|
@@ -462,7 +451,7 @@ DIAGNOSTICS:
|
|
|
462
451
|
if (globalStore[eventUnsubscribeStoreKey] === eventUnsubscribes) {
|
|
463
452
|
delete globalStore[eventUnsubscribeStoreKey];
|
|
464
453
|
}
|
|
465
|
-
|
|
454
|
+
maintenance.stop();
|
|
466
455
|
stopWidgetAnimation();
|
|
467
456
|
stopResultAnimations();
|
|
468
457
|
if (state.poller) clearInterval(state.poller);
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from "@bastani/atomic";
|
|
2
|
+
import type { SubagentState } from "../shared/types.ts";
|
|
3
|
+
import { cleanupAllArtifactDirs, cleanupOldArtifacts, getArtifactsDir } from "../shared/artifacts.ts";
|
|
4
|
+
import { cleanupOldChainDirs } from "../shared/settings.ts";
|
|
5
|
+
import { cleanupOldNestedRuntimeDirs } from "../runs/shared/nested-events.ts";
|
|
6
|
+
import { createResultWatcher } from "../runs/background/result-watcher.ts";
|
|
7
|
+
|
|
8
|
+
export interface SubagentStartupMaintenance {
|
|
9
|
+
scheduleStartupCleanup(): void;
|
|
10
|
+
startResultWatcherDeferred(): void;
|
|
11
|
+
primeExistingResultsDeferred(): void;
|
|
12
|
+
cleanupSessionArtifactsDeferred(ctx: ExtensionContext): void;
|
|
13
|
+
stop(): void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function scheduleMacrotask(task: () => void): () => void {
|
|
17
|
+
let cancelled = false;
|
|
18
|
+
const handle = setImmediate(() => {
|
|
19
|
+
if (!cancelled) task();
|
|
20
|
+
});
|
|
21
|
+
handle.unref?.();
|
|
22
|
+
return () => {
|
|
23
|
+
cancelled = true;
|
|
24
|
+
clearImmediate(handle);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function swallowCleanup(task: () => void): void {
|
|
29
|
+
try {
|
|
30
|
+
task();
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function createSubagentStartupMaintenance(
|
|
36
|
+
pi: ExtensionAPI,
|
|
37
|
+
state: SubagentState,
|
|
38
|
+
options: {
|
|
39
|
+
resultsDir: string;
|
|
40
|
+
artifactCleanupDays: number;
|
|
41
|
+
resultTtlMs: number;
|
|
42
|
+
},
|
|
43
|
+
): SubagentStartupMaintenance {
|
|
44
|
+
const { startResultWatcher, primeExistingResults, stopResultWatcher } = createResultWatcher(
|
|
45
|
+
pi,
|
|
46
|
+
state,
|
|
47
|
+
options.resultsDir,
|
|
48
|
+
options.resultTtlMs,
|
|
49
|
+
);
|
|
50
|
+
const cancelTasks = new Set<() => void>();
|
|
51
|
+
const schedule = (task: () => void): void => {
|
|
52
|
+
const cancel = scheduleMacrotask(() => {
|
|
53
|
+
cancelTasks.delete(cancel);
|
|
54
|
+
task();
|
|
55
|
+
});
|
|
56
|
+
cancelTasks.add(cancel);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
scheduleStartupCleanup() {
|
|
61
|
+
schedule(() => {
|
|
62
|
+
swallowCleanup(cleanupOldChainDirs);
|
|
63
|
+
swallowCleanup(() => cleanupAllArtifactDirs(options.artifactCleanupDays));
|
|
64
|
+
swallowCleanup(() => cleanupOldNestedRuntimeDirs(options.artifactCleanupDays));
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
startResultWatcherDeferred() {
|
|
68
|
+
schedule(startResultWatcher);
|
|
69
|
+
},
|
|
70
|
+
primeExistingResultsDeferred() {
|
|
71
|
+
schedule(primeExistingResults);
|
|
72
|
+
},
|
|
73
|
+
cleanupSessionArtifactsDeferred(ctx) {
|
|
74
|
+
let sessionFile: string | null | undefined;
|
|
75
|
+
try {
|
|
76
|
+
sessionFile = ctx.sessionManager.getSessionFile();
|
|
77
|
+
} catch {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (!sessionFile) return;
|
|
81
|
+
schedule(() => swallowCleanup(() => cleanupOldArtifacts(getArtifactsDir(sessionFile), options.artifactCleanupDays)));
|
|
82
|
+
},
|
|
83
|
+
stop() {
|
|
84
|
+
for (const cancel of cancelTasks) cancel();
|
|
85
|
+
cancelTasks.clear();
|
|
86
|
+
stopResultWatcher();
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
@@ -4,7 +4,7 @@ import * as path from "node:path";
|
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { APP_NAME } from "@bastani/atomic";
|
|
7
|
-
import { resolvePiPackageRoot } from "../shared/pi-spawn.ts";
|
|
7
|
+
import { formatPiSpawnError, resolvePiPackageRoot, validatePiSpawnCwd } from "../shared/pi-spawn.ts";
|
|
8
8
|
import { getAsyncConfigPath, TEMP_ROOT_DIR, type SubagentRunMode } from "../../shared/types.ts";
|
|
9
9
|
import type { AsyncExecutionResult, AsyncSpawnResult } from "./async-execution-types.ts";
|
|
10
10
|
|
|
@@ -85,33 +85,38 @@ export function writeAsyncRunnerConfig(cfg: object, suffix: string): string {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
export function spawnRunner(cfg: object, suffix: string, cwd: string): AsyncSpawnResult {
|
|
88
|
+
const cwdValidation = validatePiSpawnCwd(cwd);
|
|
89
|
+
if (!cwdValidation.ok) return { error: cwdValidation.error };
|
|
90
|
+
|
|
88
91
|
if (!jitiCliPath) {
|
|
89
92
|
return { error: "upstream jiti for TypeScript execution could not be found; ensure package dependencies are installed" };
|
|
90
93
|
}
|
|
91
94
|
|
|
92
|
-
try {
|
|
93
|
-
const cwdStats = fs.statSync(cwd);
|
|
94
|
-
if (!cwdStats.isDirectory()) {
|
|
95
|
-
return { error: `cwd is not a directory: ${cwd}` };
|
|
96
|
-
}
|
|
97
|
-
} catch {
|
|
98
|
-
return { error: `cwd does not exist: ${cwd}` };
|
|
99
|
-
}
|
|
100
|
-
|
|
101
95
|
const cfgPath = writeAsyncRunnerConfig(cfg, suffix);
|
|
102
96
|
const runner = path.join(path.dirname(fileURLToPath(import.meta.url)), "subagent-runner.ts");
|
|
103
97
|
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
98
|
+
const spawnSpec = { command: process.execPath, args: [jitiCliPath, runner, cfgPath] };
|
|
99
|
+
let proc: ReturnType<typeof spawn>;
|
|
100
|
+
try {
|
|
101
|
+
proc = spawn(spawnSpec.command, spawnSpec.args, {
|
|
102
|
+
cwd,
|
|
103
|
+
detached: true,
|
|
104
|
+
stdio: "ignore",
|
|
105
|
+
windowsHide: true,
|
|
106
|
+
});
|
|
107
|
+
} catch (error) {
|
|
108
|
+
const spawnError = error instanceof Error ? error : new Error(String(error));
|
|
109
|
+
return { error: formatPiSpawnError(spawnError, spawnSpec, cwd) };
|
|
110
|
+
}
|
|
110
111
|
proc.on("error", (error) => {
|
|
111
|
-
console.error(`[${APP_NAME}-subagents] async spawn failed: ${error
|
|
112
|
+
console.error(`[${APP_NAME}-subagents] async spawn failed: ${formatPiSpawnError(error, spawnSpec, cwd)}`);
|
|
112
113
|
});
|
|
113
114
|
if (typeof proc.pid !== "number") {
|
|
114
|
-
|
|
115
|
+
const noPidError = Object.assign(
|
|
116
|
+
new Error(`spawn ${spawnSpec.command} failed before assigning a pid`),
|
|
117
|
+
fs.existsSync(spawnSpec.command) ? {} : { code: "ENOENT" },
|
|
118
|
+
);
|
|
119
|
+
return { error: formatPiSpawnError(noPidError, spawnSpec, cwd) };
|
|
115
120
|
}
|
|
116
121
|
proc.unref();
|
|
117
122
|
return { pid: proc.pid };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
+
import { isSafeFsWatchPathError, watchWithErrorHandler } from "@bastani/atomic";
|
|
3
4
|
import { buildCompletionKey, markSeenWithTtl } from "./completion-dedupe.ts";
|
|
4
5
|
import { createFileCoalescer } from "../../shared/file-coalescer.ts";
|
|
5
6
|
import {
|
|
@@ -21,7 +22,9 @@ import { projectNestedRegistryForRoot, sanitizeSummary } from "../shared/nested-
|
|
|
21
22
|
const WATCHER_RESTART_DELAY_MS = 3000;
|
|
22
23
|
const POLL_INTERVAL_MS = 3000;
|
|
23
24
|
|
|
24
|
-
type ResultWatcherFs = Pick<typeof fs, "existsSync" | "readFileSync" | "unlinkSync" | "readdirSync" | "mkdirSync" | "watch"
|
|
25
|
+
type ResultWatcherFs = Pick<typeof fs, "existsSync" | "readFileSync" | "unlinkSync" | "readdirSync" | "mkdirSync" | "watch"> & {
|
|
26
|
+
realpathSync?: typeof fs.realpathSync;
|
|
27
|
+
};
|
|
25
28
|
|
|
26
29
|
type ResultWatcherTimers = {
|
|
27
30
|
setTimeout: typeof setTimeout;
|
|
@@ -30,9 +33,12 @@ type ResultWatcherTimers = {
|
|
|
30
33
|
clearInterval: typeof clearInterval;
|
|
31
34
|
};
|
|
32
35
|
|
|
36
|
+
type ResultWatcherSafeWatch = typeof watchWithErrorHandler;
|
|
37
|
+
|
|
33
38
|
type ResultWatcherDeps = {
|
|
34
39
|
fs?: ResultWatcherFs;
|
|
35
40
|
timers?: ResultWatcherTimers;
|
|
41
|
+
safeWatch?: ResultWatcherSafeWatch;
|
|
36
42
|
};
|
|
37
43
|
|
|
38
44
|
type ResultFileChild = {
|
|
@@ -88,7 +94,7 @@ function isNotFoundError(error: unknown): boolean {
|
|
|
88
94
|
|
|
89
95
|
function shouldFallBackToPolling(error: unknown): boolean {
|
|
90
96
|
const code = getErrorCode(error);
|
|
91
|
-
return code === "EMFILE" || code === "ENOSPC";
|
|
97
|
+
return code === "EMFILE" || code === "ENOSPC" || isSafeFsWatchPathError(error);
|
|
92
98
|
}
|
|
93
99
|
|
|
94
100
|
export function createResultWatcher(
|
|
@@ -104,6 +110,7 @@ export function createResultWatcher(
|
|
|
104
110
|
} {
|
|
105
111
|
const fsApi = deps.fs ?? fs;
|
|
106
112
|
const timers = deps.timers ?? { setTimeout, clearTimeout, setInterval, clearInterval };
|
|
113
|
+
const safeWatch = deps.safeWatch ?? watchWithErrorHandler;
|
|
107
114
|
|
|
108
115
|
const handleResult = async (file: string) => {
|
|
109
116
|
const resultPath = path.join(resultsDir, file);
|
|
@@ -264,13 +271,7 @@ export function createResultWatcher(
|
|
|
264
271
|
state.watcherRestartTimer = null;
|
|
265
272
|
}
|
|
266
273
|
try {
|
|
267
|
-
|
|
268
|
-
if (ev !== "rename" || !file) return;
|
|
269
|
-
const fileName = file.toString();
|
|
270
|
-
if (!fileName.endsWith(".json")) return;
|
|
271
|
-
state.resultFileCoalescer.schedule(fileName);
|
|
272
|
-
});
|
|
273
|
-
state.watcher.on("error", (error) => {
|
|
274
|
+
const handleWatcherError = (error: Error) => {
|
|
274
275
|
if (shouldFallBackToPolling(error)) {
|
|
275
276
|
startPollingFallback(error);
|
|
276
277
|
return;
|
|
@@ -279,8 +280,17 @@ export function createResultWatcher(
|
|
|
279
280
|
state.watcher?.close();
|
|
280
281
|
state.watcher = null;
|
|
281
282
|
scheduleRestart();
|
|
283
|
+
};
|
|
284
|
+
state.watcher = safeWatch(resultsDir, (ev, file) => {
|
|
285
|
+
if (ev !== "rename" || !file) return;
|
|
286
|
+
const fileName = file.toString();
|
|
287
|
+
if (!fileName.endsWith(".json")) return;
|
|
288
|
+
state.resultFileCoalescer.schedule(fileName);
|
|
289
|
+
}, handleWatcherError, {
|
|
290
|
+
watch: fsApi.watch,
|
|
291
|
+
realpathSyncNative: fsApi.realpathSync?.native,
|
|
282
292
|
});
|
|
283
|
-
state.watcher
|
|
293
|
+
state.watcher?.unref?.();
|
|
284
294
|
} catch (error) {
|
|
285
295
|
if (shouldFallBackToPolling(error)) {
|
|
286
296
|
startPollingFallback(error);
|
|
@@ -5,7 +5,7 @@ import { appendJsonl } from "../../shared/artifacts.ts";
|
|
|
5
5
|
import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
|
|
6
6
|
import { detectSubagentError, extractTextFromContent, extractToolArgsPreview, getFinalOutput } from "../../shared/utils.ts";
|
|
7
7
|
import { getSubagentDepthEnv } from "../../shared/types.ts";
|
|
8
|
-
import { getPiSpawnCommand } from "../shared/pi-spawn.ts";
|
|
8
|
+
import { formatPiSpawnError, getPiSpawnCommand, validatePiSpawnCwd } from "../shared/pi-spawn.ts";
|
|
9
9
|
import {
|
|
10
10
|
assistantStopReason,
|
|
11
11
|
isAssistantFailureStopReason,
|
|
@@ -29,6 +29,17 @@ export function runPiStreaming(
|
|
|
29
29
|
registerInterrupt?: (interrupt: (() => void) | undefined) => void,
|
|
30
30
|
onChildEvent?: (event: ChildEvent) => void,
|
|
31
31
|
): Promise<RunPiStreamingResult> {
|
|
32
|
+
const cwdValidation = validatePiSpawnCwd(cwd);
|
|
33
|
+
if (!cwdValidation.ok) {
|
|
34
|
+
return Promise.resolve({
|
|
35
|
+
stderr: "",
|
|
36
|
+
exitCode: 1,
|
|
37
|
+
messages: [],
|
|
38
|
+
usage: emptyUsage(),
|
|
39
|
+
error: cwdValidation.error,
|
|
40
|
+
finalOutput: "",
|
|
41
|
+
});
|
|
42
|
+
}
|
|
32
43
|
return new Promise((resolve) => {
|
|
33
44
|
const outputStream = fs.createWriteStream(outputFile, { flags: "w" });
|
|
34
45
|
const spawnEnv = {
|
|
@@ -273,8 +284,7 @@ export function runPiStreaming(
|
|
|
273
284
|
attemptWatchdog.clear();
|
|
274
285
|
outputStream.end();
|
|
275
286
|
const finalOutput = getFinalOutput(messages) || rawStdoutLines.join("\n").trim();
|
|
276
|
-
const
|
|
277
|
-
const finalError = error ?? assistantError ?? spawnErrorMessage;
|
|
287
|
+
const finalError = error ?? assistantError ?? formatPiSpawnError(spawnError, spawnSpec, cwd);
|
|
278
288
|
resolve({
|
|
279
289
|
stderr,
|
|
280
290
|
exitCode: 1,
|
|
@@ -14,7 +14,7 @@ import { extractTextFromContent, extractToolArgsPreview, getFinalOutput } from "
|
|
|
14
14
|
import { createJsonlWriter } from "../../shared/jsonl-writer.ts";
|
|
15
15
|
import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
|
|
16
16
|
import { applyThinkingSuffix, buildPiArgs, cleanupTempDir } from "../shared/pi-args.ts";
|
|
17
|
-
import { getPiSpawnCommand } from "../shared/pi-spawn.ts";
|
|
17
|
+
import { formatPiSpawnError, getPiSpawnCommand, validatePiSpawnCwd } 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
20
|
import { createAttemptWatchdog } from "../shared/attempt-watchdog.ts";
|
|
@@ -124,6 +124,14 @@ export async function runSingleAttempt(
|
|
|
124
124
|
workflowStageSubagentGuard: options.workflowStageSubagentGuard,
|
|
125
125
|
}),
|
|
126
126
|
};
|
|
127
|
+
const cwdValidation = validatePiSpawnCwd(runCwd);
|
|
128
|
+
if (!cwdValidation.ok) {
|
|
129
|
+
cleanupTempDir(tempDir);
|
|
130
|
+
result.error = cwdValidation.error;
|
|
131
|
+
return finalizeSingleAttempt({
|
|
132
|
+
result, progress, exitCode: 1, interruptedByControl, allControlEvents: controlRuntime.allControlEvents, options, shared, startTime,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
127
135
|
const exitCode = await new Promise<number>((resolve) => {
|
|
128
136
|
const spawnSpec = getPiSpawnCommand(args);
|
|
129
137
|
const proc = spawn(spawnSpec.command, spawnSpec.args, {
|
|
@@ -418,7 +426,7 @@ export async function runSingleAttempt(
|
|
|
418
426
|
attemptWatchdog.clear();
|
|
419
427
|
void jsonlWriter.close().catch(() => undefined);
|
|
420
428
|
cleanupTempDir(tempDir);
|
|
421
|
-
if (!result.error) result.error = error
|
|
429
|
+
if (!result.error) result.error = formatPiSpawnError(error, spawnSpec, runCwd);
|
|
422
430
|
finish(1);
|
|
423
431
|
});
|
|
424
432
|
|
|
@@ -42,9 +42,36 @@ export interface PiSpawnDeps {
|
|
|
42
42
|
resolvePackageJson?: () => string;
|
|
43
43
|
resolvePackageEntry?: () => string;
|
|
44
44
|
piPackageRoot?: string;
|
|
45
|
+
statSync?: (filePath: string) => fs.Stats;
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
export type PiSpawnCwdValidationResult = { ok: true } | { ok: false; error: string };
|
|
49
|
+
|
|
50
|
+
export function validatePiSpawnCwd(cwd: string, deps: PiSpawnDeps = {}): PiSpawnCwdValidationResult {
|
|
51
|
+
const statSync = deps.statSync ?? fs.statSync;
|
|
52
|
+
try {
|
|
53
|
+
const cwdStats = statSync(cwd);
|
|
54
|
+
if (!cwdStats.isDirectory()) return { ok: false, error: `cwd is not a directory: ${cwd}` };
|
|
55
|
+
return { ok: true };
|
|
56
|
+
} catch (error) {
|
|
57
|
+
const fsError = error as NodeJS.ErrnoException;
|
|
58
|
+
if (fsError.code === "ENOENT") return { ok: false, error: `cwd does not exist: ${cwd}` };
|
|
59
|
+
if (fsError.code === "ENOTDIR") return { ok: false, error: `cwd path contains a non-directory component: ${cwd}` };
|
|
60
|
+
const details = fsError.message ? ` (${fsError.message})` : "";
|
|
61
|
+
return { ok: false, error: `cwd is not accessible: ${cwd}${details}` };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function formatPiSpawnError(error: Error, spawnSpec: PiSpawnCommand, cwd: string): string {
|
|
66
|
+
const spawnError = error as NodeJS.ErrnoException;
|
|
67
|
+
const message = error.message || String(error);
|
|
68
|
+
if (spawnError.code === "ENOENT") {
|
|
69
|
+
return `failed to spawn subagent runtime '${spawnSpec.command}' from cwd '${cwd}': runtime executable was not found or could not be launched (${message})`;
|
|
70
|
+
}
|
|
71
|
+
return `failed to spawn subagent runtime '${spawnSpec.command}' from cwd '${cwd}': ${message}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface PiSpawnCommand {
|
|
48
75
|
command: string;
|
|
49
76
|
args: string[];
|
|
50
77
|
}
|
|
@@ -5,7 +5,7 @@ import { formatDuration, formatModelThinking, shortenPath } from "../shared/form
|
|
|
5
5
|
import { formatNestedAggregate } from "../runs/shared/nested-render.ts";
|
|
6
6
|
import { formatAgentRunningLabel } from "../shared/status-format.ts";
|
|
7
7
|
import { buildLiveStatusLine, formatCurrentToolLine, formatTokenStat, formatToolUseStat, statJoin } from "./render-status-progress.ts";
|
|
8
|
-
import { runningGlyph,
|
|
8
|
+
import { runningGlyph, runningPulseGlyph, truncLine, type Theme } from "./render-layout.ts";
|
|
9
9
|
|
|
10
10
|
export function formatWidgetAgents(agents: string[]): string {
|
|
11
11
|
const distinct = [...new Set(agents)];
|
|
@@ -40,47 +40,9 @@ export function widgetActivity(job: AsyncJobState): string {
|
|
|
40
40
|
return "Done";
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
export function widgetStepRunningSeed(step: NonNullable<AsyncJobState["steps"]>[number], fallbackIndex?: number): number | undefined {
|
|
44
|
-
return runningSeed(
|
|
45
|
-
fallbackIndex,
|
|
46
|
-
step.index,
|
|
47
|
-
step.toolCount,
|
|
48
|
-
step.turnCount,
|
|
49
|
-
step.tokens?.total,
|
|
50
|
-
step.lastActivityAt,
|
|
51
|
-
step.currentToolStartedAt,
|
|
52
|
-
step.durationMs,
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function widgetStepsRunningSeed(steps: Array<NonNullable<AsyncJobState["steps"]>[number]> | undefined): number | undefined {
|
|
57
|
-
let seed: number | undefined;
|
|
58
|
-
for (const [index, step] of (steps ?? []).entries()) seed = runningSeed(seed, widgetStepRunningSeed(step, index));
|
|
59
|
-
return seed;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function widgetJobRunningSeed(job: AsyncJobState): number | undefined {
|
|
63
|
-
return runningSeed(
|
|
64
|
-
job.updatedAt,
|
|
65
|
-
job.lastActivityAt,
|
|
66
|
-
job.toolCount,
|
|
67
|
-
job.turnCount,
|
|
68
|
-
job.totalTokens?.total,
|
|
69
|
-
job.currentStep,
|
|
70
|
-
job.runningSteps,
|
|
71
|
-
job.completedSteps,
|
|
72
|
-
widgetStepsRunningSeed(job.steps),
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export function widgetJobsRunningSeed(jobs: AsyncJobState[]): number | undefined {
|
|
77
|
-
let seed: number | undefined;
|
|
78
|
-
for (const job of jobs) seed = runningSeed(seed, widgetJobRunningSeed(job));
|
|
79
|
-
return seed;
|
|
80
|
-
}
|
|
81
43
|
|
|
82
|
-
export function widgetStatusGlyph(job: AsyncJobState, theme: Theme,
|
|
83
|
-
if (job.status === "running") return theme.fg("accent",
|
|
44
|
+
export function widgetStatusGlyph(job: AsyncJobState, theme: Theme, pulseFrame?: number): string {
|
|
45
|
+
if (job.status === "running") return theme.fg("accent", runningPulseGlyph(pulseFrame));
|
|
84
46
|
if (job.status === "queued") return theme.fg("muted", "◦");
|
|
85
47
|
if (job.status === "complete") return theme.fg("success", "✓");
|
|
86
48
|
if (job.status === "paused") return theme.fg("warning", "■");
|
|
@@ -95,6 +57,14 @@ export function widgetStepGlyph(status: AsyncJobStep["status"], theme: Theme, se
|
|
|
95
57
|
return theme.fg("muted", "◦");
|
|
96
58
|
}
|
|
97
59
|
|
|
60
|
+
export function widgetStepPulseGlyph(status: AsyncJobStep["status"], theme: Theme, pulseFrame?: number): string {
|
|
61
|
+
if (status === "running") return theme.fg("accent", runningPulseGlyph(pulseFrame));
|
|
62
|
+
if (status === "complete" || status === "completed") return theme.fg("success", "✓");
|
|
63
|
+
if (status === "failed") return theme.fg("error", "✗");
|
|
64
|
+
if (status === "paused") return theme.fg("warning", "■");
|
|
65
|
+
return theme.fg("muted", "◦");
|
|
66
|
+
}
|
|
67
|
+
|
|
98
68
|
export function widgetStepStatus(status: AsyncJobStep["status"], theme: Theme): string {
|
|
99
69
|
if (status === "running") return theme.fg("accent", "running");
|
|
100
70
|
if (status === "complete" || status === "completed") return theme.fg("success", "complete");
|
|
@@ -186,17 +156,14 @@ export function nestedRunName(run: NestedRunSummary): string {
|
|
|
186
156
|
return run.id;
|
|
187
157
|
}
|
|
188
158
|
|
|
189
|
-
export function nestedStatusGlyph(state: NestedRunSummary["state"] | NestedStepSummary["status"], theme: Theme,
|
|
190
|
-
if (state === "running") return theme.fg("accent",
|
|
159
|
+
export function nestedStatusGlyph(state: NestedRunSummary["state"] | NestedStepSummary["status"], theme: Theme, pulseFrame?: number): string {
|
|
160
|
+
if (state === "running") return theme.fg("accent", runningPulseGlyph(pulseFrame));
|
|
191
161
|
if (state === "complete" || state === "completed") return theme.fg("success", "✓");
|
|
192
162
|
if (state === "failed") return theme.fg("error", "✗");
|
|
193
163
|
if (state === "paused") return theme.fg("warning", "■");
|
|
194
164
|
return theme.fg("muted", "◦");
|
|
195
165
|
}
|
|
196
166
|
|
|
197
|
-
export function nestedRunSeed(run: NestedRunSummary): number | undefined {
|
|
198
|
-
return runningSeed(run.lastUpdate, run.lastActivityAt, run.currentStep, run.toolCount, run.turnCount, run.totalTokens?.total, run.currentToolStartedAt);
|
|
199
|
-
}
|
|
200
167
|
|
|
201
168
|
export function nestedActivity(input: Pick<NestedRunSummary | NestedStepSummary, "activityState" | "lastActivityAt" | "currentTool" | "currentToolStartedAt" | "currentPath" | "turnCount" | "toolCount">, state: NestedRunSummary["state"] | NestedStepSummary["status"], snapshotNow?: number): string {
|
|
202
169
|
const facts: string[] = [];
|
|
@@ -240,7 +207,7 @@ export function formatNestedWidgetLines(children: NestedRunSummary[] | undefined
|
|
|
240
207
|
}
|
|
241
208
|
const activity = nestedActivity(child, child.state, snapshotNow ?? child.lastUpdate);
|
|
242
209
|
const error = child.error ? ` · ${child.error}` : "";
|
|
243
|
-
lines.push(theme.fg("dim", `${prefix}↳ ${nestedStatusGlyph(child.state, theme,
|
|
210
|
+
lines.push(theme.fg("dim", `${prefix}↳ ${nestedStatusGlyph(child.state, theme, now)} ${nestedRunName(child)} · ${child.state} · ${activity}${error}`));
|
|
244
211
|
if (depth === maxDepth) {
|
|
245
212
|
const aggregate = formatNestedAggregate([...(child.steps?.flatMap((step) => step.children ?? []) ?? []), ...(child.children ?? [])]);
|
|
246
213
|
if (aggregate && lines.length < lineBudget) lines.push(theme.fg("dim", `${prefix} ↳ ${aggregate}`));
|
|
@@ -248,7 +215,7 @@ export function formatNestedWidgetLines(children: NestedRunSummary[] | undefined
|
|
|
248
215
|
}
|
|
249
216
|
for (const step of child.steps ?? []) {
|
|
250
217
|
if (lines.length >= lineBudget) return;
|
|
251
|
-
lines.push(theme.fg("dim", `${prefix} ↳ ${nestedStatusGlyph(step.status, theme,
|
|
218
|
+
lines.push(theme.fg("dim", `${prefix} ↳ ${nestedStatusGlyph(step.status, theme, now)} ${step.agent} · ${step.status} · ${nestedActivity(step, step.status, snapshotNow ?? child.lastUpdate)}`));
|
|
252
219
|
append(step.children, depth + 1, `${prefix} `);
|
|
253
220
|
}
|
|
254
221
|
append(child.children, depth + 1, `${prefix} `);
|
|
@@ -129,6 +129,15 @@ export function pulseGlyph(frame?: number): string {
|
|
|
129
129
|
return PULSE_FRAMES[index % PULSE_FRAMES.length]!;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Running async/background subagent pulse. It reuses the exact foreground pulse
|
|
134
|
+
* glyph frames while accepting an explicit frame advanced by the widget only on
|
|
135
|
+
* real async progress/status updates, matching foreground result semantics.
|
|
136
|
+
*/
|
|
137
|
+
export function runningPulseGlyph(frame?: number): string {
|
|
138
|
+
return pulseGlyph(frame);
|
|
139
|
+
}
|
|
140
|
+
|
|
132
141
|
export function progressRunningSeed(progress: ProgressSeedSource | undefined): number | undefined {
|
|
133
142
|
if (!progress) return undefined;
|
|
134
143
|
return runningSeed(
|