@bastani/atomic 0.9.5-alpha.9 → 0.9.5
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 +106 -0
- package/README.md +2 -2
- package/dist/builtin/cursor/CHANGELOG.md +12 -0
- package/dist/builtin/cursor/package.json +3 -3
- package/dist/builtin/cursor/src/model-mapper.ts +6 -3
- package/dist/builtin/intercom/CHANGELOG.md +37 -0
- package/dist/builtin/intercom/README.md +4 -2
- package/dist/builtin/intercom/broker/broker.ts +6 -114
- package/dist/builtin/intercom/broker/client.ts +29 -54
- package/dist/builtin/intercom/broker/delivered-message-cache.ts +44 -0
- package/dist/builtin/intercom/broker/pending-send-registry.ts +142 -0
- package/dist/builtin/intercom/broker/send-handler.ts +106 -0
- package/dist/builtin/intercom/broker/send-signature.ts +37 -0
- package/dist/builtin/intercom/foreground-detach-handoff.ts +136 -0
- package/dist/builtin/intercom/index-heavy.ts +49 -53
- package/dist/builtin/intercom/index.ts +278 -262
- package/dist/builtin/intercom/lazy-heavy-proxy.ts +114 -0
- package/dist/builtin/intercom/lazy-subagent-ack.ts +20 -0
- package/dist/builtin/intercom/lazy-tool-execution.ts +39 -0
- package/dist/builtin/intercom/lifecycle-lease.ts +51 -0
- package/dist/builtin/intercom/lifecycle.ts +37 -20
- package/dist/builtin/intercom/package.json +3 -6
- package/dist/builtin/intercom/reply-routing.ts +17 -0
- package/dist/builtin/intercom/subagent-relay.ts +58 -12
- package/dist/builtin/intercom/types.ts +3 -3
- package/dist/builtin/mcp/CHANGELOG.md +27 -0
- package/dist/builtin/mcp/OAUTH.md +1 -0
- package/dist/builtin/mcp/README.md +4 -0
- package/dist/builtin/mcp/apps-cancellation.ts +32 -0
- package/dist/builtin/mcp/call-tool-result.ts +9 -0
- package/dist/builtin/mcp/caller-wait.ts +50 -0
- package/dist/builtin/mcp/command-registration.ts +82 -0
- package/dist/builtin/mcp/direct-tool-executor.ts +279 -0
- package/dist/builtin/mcp/direct-tools.ts +6 -239
- package/dist/builtin/mcp/host-html-template.ts +4 -2
- package/dist/builtin/mcp/index.ts +258 -250
- package/dist/builtin/mcp/init.ts +96 -87
- package/dist/builtin/mcp/mcp-auth-flow.ts +247 -237
- package/dist/builtin/mcp/mcp-callback-server.ts +89 -68
- package/dist/builtin/mcp/mcp-oauth-provider.ts +23 -1
- package/dist/builtin/mcp/metadata-hydration.ts +21 -6
- package/dist/builtin/mcp/package.json +3 -3
- package/dist/builtin/mcp/proxy-auth.ts +4 -4
- package/dist/builtin/mcp/proxy-call.ts +111 -40
- package/dist/builtin/mcp/proxy-connect.ts +35 -15
- package/dist/builtin/mcp/proxy-info-modes.ts +49 -19
- package/dist/builtin/mcp/proxy-modes.ts +3 -3
- package/dist/builtin/mcp/session-cleanup-barrier.ts +43 -0
- package/dist/builtin/mcp/state-lease.ts +12 -0
- package/dist/builtin/mcp/ui-server.ts +8 -8
- package/dist/builtin/mcp/ui-session.ts +9 -18
- package/dist/builtin/subagents/CHANGELOG.md +66 -0
- package/dist/builtin/subagents/README.md +30 -29
- package/dist/builtin/subagents/agents/code-simplifier.md +2 -2
- package/dist/builtin/subagents/agents/codebase-analyzer.md +2 -2
- package/dist/builtin/subagents/agents/codebase-locator.md +2 -2
- package/dist/builtin/subagents/agents/codebase-online-researcher.md +2 -2
- package/dist/builtin/subagents/agents/codebase-pattern-finder.md +2 -2
- package/dist/builtin/subagents/agents/codebase-research-analyzer.md +2 -2
- package/dist/builtin/subagents/agents/codebase-research-locator.md +2 -2
- package/dist/builtin/subagents/agents/debugger.md +2 -2
- package/dist/builtin/subagents/agents/worker.md +2 -2
- package/dist/builtin/subagents/package.json +5 -5
- package/dist/builtin/subagents/prompts/review-loop.md +1 -1
- package/dist/builtin/subagents/skills/subagent/SKILL.md +37 -36
- package/dist/builtin/subagents/src/extension/api-lifecycle.ts +64 -0
- package/dist/builtin/subagents/src/extension/fanout-child.ts +44 -51
- package/dist/builtin/subagents/src/extension/index.ts +287 -307
- package/dist/builtin/subagents/src/extension/prompt-guidance.ts +7 -10
- package/dist/builtin/subagents/src/extension/schemas.ts +22 -5
- package/dist/builtin/subagents/src/extension/tool-description.ts +29 -0
- package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +13 -2
- package/dist/builtin/subagents/src/intercom/result-intercom.ts +4 -4
- package/dist/builtin/subagents/src/runs/background/async-event-journal.ts +106 -0
- package/dist/builtin/subagents/src/runs/background/async-execution-common.ts +3 -2
- package/dist/builtin/subagents/src/runs/background/async-execution-single.ts +13 -2
- package/dist/builtin/subagents/src/runs/background/async-execution-types.ts +3 -0
- package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +1 -1
- package/dist/builtin/subagents/src/runs/background/completion-claims.ts +189 -0
- package/dist/builtin/subagents/src/runs/background/completion-dedupe.ts +44 -7
- package/dist/builtin/subagents/src/runs/background/completion-notification.ts +34 -0
- package/dist/builtin/subagents/src/runs/background/notify.ts +72 -22
- package/dist/builtin/subagents/src/runs/background/result-delivery-processor.ts +232 -0
- package/dist/builtin/subagents/src/runs/background/result-file-claims.ts +151 -0
- package/dist/builtin/subagents/src/runs/background/result-quarantine.ts +72 -0
- package/dist/builtin/subagents/src/runs/background/result-retry-scheduler.ts +48 -0
- package/dist/builtin/subagents/src/runs/background/result-status.ts +81 -0
- package/dist/builtin/subagents/src/runs/background/result-watcher-data.ts +59 -0
- package/dist/builtin/subagents/src/runs/background/result-watcher.ts +172 -238
- package/dist/builtin/subagents/src/runs/background/run-id-resolver.ts +3 -2
- package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +73 -14
- package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +17 -13
- package/dist/builtin/subagents/src/runs/background/top-level-async.ts +1 -2
- 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 +7 -9
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-types.ts +3 -13
- package/dist/builtin/subagents/src/runs/foreground/chain-execution.ts +2 -15
- package/dist/builtin/subagents/src/runs/foreground/execution-attempt-finalize.ts +2 -2
- package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +53 -44
- package/dist/builtin/subagents/src/runs/foreground/execution-detach-reservations.ts +48 -0
- package/dist/builtin/subagents/src/runs/foreground/execution-detach-route.ts +17 -0
- package/dist/builtin/subagents/src/runs/foreground/execution-run-sync.ts +39 -26
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-async.ts +3 -1
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-chain.ts +3 -51
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-context.ts +6 -11
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-input.ts +11 -2
- 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 +5 -112
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-resume.ts +2 -0
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +68 -118
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-status.ts +79 -7
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-types.ts +2 -2
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +3 -0
- package/dist/builtin/subagents/src/runs/shared/pi-args.ts +2 -2
- package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +27 -11
- package/dist/builtin/subagents/src/shared/artifacts.ts +22 -11
- package/dist/builtin/subagents/src/shared/event-jsonl-writer.ts +294 -0
- package/dist/builtin/subagents/src/shared/exclusive-file-publication.ts +44 -0
- package/dist/builtin/subagents/src/shared/jsonl-writer.ts +1 -0
- package/dist/builtin/subagents/src/shared/model-info.ts +2 -2
- package/dist/builtin/subagents/src/shared/settings.ts +20 -10
- package/dist/builtin/subagents/src/shared/types-async.ts +3 -1
- package/dist/builtin/subagents/src/shared/types-config.ts +2 -0
- package/dist/builtin/subagents/src/shared/types-runtime.ts +3 -2
- package/dist/builtin/subagents/src/slash/slash-commands.ts +9 -12
- package/dist/builtin/subagents/src/slash/slash-live-state.ts +63 -32
- package/dist/builtin/subagents/src/tui/render-result.ts +7 -0
- package/dist/builtin/subagents/src/tui/render-stable-output.ts +1 -0
- package/dist/builtin/subagents/src/tui/render-widget.ts +128 -89
- package/dist/builtin/web-access/CHANGELOG.md +28 -0
- package/dist/builtin/web-access/README.md +4 -0
- package/dist/builtin/web-access/content-tools.ts +8 -3
- package/dist/builtin/web-access/index-heavy.ts +1 -1
- package/dist/builtin/web-access/index.ts +190 -46
- package/dist/builtin/web-access/lifecycle-lease.ts +38 -0
- package/dist/builtin/web-access/package.json +2 -2
- package/dist/builtin/web-access/result-renderers.ts +1 -1
- package/dist/builtin/web-access/summary-review.ts +1 -1
- package/dist/builtin/web-access/web-search-activity.ts +1 -1
- package/dist/builtin/web-access/web-search-return.ts +7 -0
- package/dist/builtin/web-access/web-search-summary.ts +1 -1
- package/dist/builtin/web-access/web-search-tool.ts +4 -2
- package/dist/builtin/workflows/CHANGELOG.md +68 -0
- package/dist/builtin/workflows/README.md +4 -4
- package/dist/builtin/workflows/builtin/deep-research-codebase-utils.ts +27 -4
- package/dist/builtin/workflows/builtin/goal-runner.ts +47 -26
- package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +10 -0
- package/dist/builtin/workflows/builtin/ralph-core.ts +9 -9
- package/dist/builtin/workflows/builtin/ralph-models.ts +71 -36
- package/dist/builtin/workflows/builtin/ralph-runner.ts +20 -31
- package/dist/builtin/workflows/package.json +2 -2
- package/dist/builtin/workflows/src/durable/stage-primitive.ts +2 -1
- package/dist/builtin/workflows/src/engine/primitives/task.ts +2 -1
- package/dist/builtin/workflows/src/extension/workflow-prompts.ts +17 -22
- package/dist/builtin/workflows/src/extension/workflow-schema.ts +15 -2
- package/dist/builtin/workflows/src/runs/foreground/executor-hil.ts +1 -2
- package/dist/builtin/workflows/src/runs/shared/model-fallback-candidates.ts +1 -1
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +2 -2
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.ts +2 -2
- package/dist/builtin/workflows/src/shared/resume-continuation.ts +2 -0
- package/dist/builtin/workflows/src/tui/stage-chat-view-archive-history.ts +1 -23
- package/dist/builtin/workflows/src/tui/stage-chat-view-footer-status.ts +54 -9
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +5 -2
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +2 -2
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session-events.d.ts.map +1 -1
- package/dist/core/agent-session-events.js +2 -1
- package/dist/core/agent-session-events.js.map +1 -1
- package/dist/core/agent-session-message-queue.d.ts.map +1 -1
- package/dist/core/agent-session-message-queue.js +1 -1
- package/dist/core/agent-session-message-queue.js.map +1 -1
- package/dist/core/agent-session-prompt.d.ts.map +1 -1
- package/dist/core/agent-session-prompt.js +1 -1
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session-retry.d.ts.map +1 -1
- package/dist/core/agent-session-retry.js +1 -1
- package/dist/core/agent-session-retry.js.map +1 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +3 -2
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session-types.d.ts.map +1 -1
- package/dist/core/agent-session-types.js +1 -1
- package/dist/core/agent-session-types.js.map +1 -1
- package/dist/core/anthropic-thinking-guard.d.ts +5 -5
- package/dist/core/anthropic-thinking-guard.d.ts.map +1 -1
- package/dist/core/anthropic-thinking-guard.js +42 -32
- package/dist/core/anthropic-thinking-guard.js.map +1 -1
- package/dist/core/atomic-guide-command.d.ts.map +1 -1
- package/dist/core/atomic-guide-command.js +15 -15
- package/dist/core/atomic-guide-command.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +14 -9
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +2 -0
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +79 -46
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/context-assistant-turns.d.ts +42 -0
- package/dist/core/compaction/context-assistant-turns.d.ts.map +1 -0
- package/dist/core/compaction/context-assistant-turns.js +87 -0
- package/dist/core/compaction/context-assistant-turns.js.map +1 -0
- package/dist/core/compaction/context-compaction-critical.d.ts +1 -1
- package/dist/core/compaction/context-compaction-critical.d.ts.map +1 -1
- package/dist/core/compaction/context-compaction-critical.js +2 -2
- package/dist/core/compaction/context-compaction-critical.js.map +1 -1
- package/dist/core/compaction/context-compaction-eviction-alternates.d.ts +18 -0
- package/dist/core/compaction/context-compaction-eviction-alternates.d.ts.map +1 -0
- package/dist/core/compaction/context-compaction-eviction-alternates.js +186 -0
- package/dist/core/compaction/context-compaction-eviction-alternates.js.map +1 -0
- package/dist/core/compaction/context-compaction-eviction.d.ts +10 -2
- package/dist/core/compaction/context-compaction-eviction.d.ts.map +1 -1
- package/dist/core/compaction/context-compaction-eviction.js +196 -146
- package/dist/core/compaction/context-compaction-eviction.js.map +1 -1
- package/dist/core/compaction/context-compaction-prompt.d.ts.map +1 -1
- package/dist/core/compaction/context-compaction-prompt.js +1 -1
- package/dist/core/compaction/context-compaction-prompt.js.map +1 -1
- package/dist/core/compaction/context-compaction-types.d.ts +2 -0
- package/dist/core/compaction/context-compaction-types.d.ts.map +1 -1
- package/dist/core/compaction/context-compaction-types.js.map +1 -1
- package/dist/core/compaction/context-deletion-application.d.ts +5 -4
- package/dist/core/compaction/context-deletion-application.d.ts.map +1 -1
- package/dist/core/compaction/context-deletion-application.js +17 -15
- package/dist/core/compaction/context-deletion-application.js.map +1 -1
- package/dist/core/compaction/context-deletion-store.d.ts.map +1 -1
- package/dist/core/compaction/context-deletion-store.js +1 -1
- package/dist/core/compaction/context-deletion-store.js.map +1 -1
- package/dist/core/compaction/context-deletion-targets.d.ts +2 -1
- package/dist/core/compaction/context-deletion-targets.d.ts.map +1 -1
- package/dist/core/compaction/context-deletion-targets.js +36 -31
- package/dist/core/compaction/context-deletion-targets.js.map +1 -1
- package/dist/core/compaction/context-deletion-tool-helpers.d.ts.map +1 -1
- package/dist/core/compaction/context-deletion-tool-helpers.js +116 -43
- package/dist/core/compaction/context-deletion-tool-helpers.js.map +1 -1
- package/dist/core/compaction/context-transcript-analysis.d.ts +1 -2
- package/dist/core/compaction/context-transcript-analysis.d.ts.map +1 -1
- package/dist/core/compaction/context-transcript-analysis.js +33 -37
- package/dist/core/compaction/context-transcript-analysis.js.map +1 -1
- package/dist/core/copilot-model-synthesis.d.ts.map +1 -1
- package/dist/core/copilot-model-synthesis.js +3 -1
- package/dist/core/copilot-model-synthesis.js.map +1 -1
- package/dist/core/extensions/provider-types.d.ts +2 -7
- package/dist/core/extensions/provider-types.d.ts.map +1 -1
- package/dist/core/extensions/provider-types.js.map +1 -1
- package/dist/core/messages.d.ts +9 -0
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +100 -18
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-registry-builtins.d.ts.map +1 -1
- package/dist/core/model-registry-builtins.js +5 -0
- package/dist/core/model-registry-builtins.js.map +1 -1
- package/dist/core/model-registry-custom-loader.d.ts.map +1 -1
- package/dist/core/model-registry-custom-loader.js +49 -8
- package/dist/core/model-registry-custom-loader.js.map +1 -1
- package/dist/core/model-registry-dynamic.d.ts.map +1 -1
- package/dist/core/model-registry-dynamic.js +11 -5
- package/dist/core/model-registry-dynamic.js.map +1 -1
- package/dist/core/model-registry-loader.d.ts.map +1 -1
- package/dist/core/model-registry-loader.js +8 -0
- package/dist/core/model-registry-loader.js.map +1 -1
- package/dist/core/model-registry-schemas.d.ts +93 -8
- package/dist/core/model-registry-schemas.d.ts.map +1 -1
- package/dist/core/model-registry-schemas.js +24 -12
- package/dist/core/model-registry-schemas.js.map +1 -1
- package/dist/core/model-registry-types.d.ts +7 -6
- package/dist/core/model-registry-types.d.ts.map +1 -1
- package/dist/core/model-registry-types.js.map +1 -1
- package/dist/core/model-registry.d.ts +5 -0
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +23 -0
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver-cli.d.ts.map +1 -1
- package/dist/core/model-resolver-cli.js +33 -5
- package/dist/core/model-resolver-cli.js.map +1 -1
- package/dist/core/model-resolver-initial.d.ts +6 -1
- package/dist/core/model-resolver-initial.d.ts.map +1 -1
- package/dist/core/model-resolver-initial.js +12 -7
- package/dist/core/model-resolver-initial.js.map +1 -1
- package/dist/core/model-resolver.d.ts +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +1 -1
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/resource-loader-context-files.d.ts +1 -0
- package/dist/core/resource-loader-context-files.d.ts.map +1 -1
- package/dist/core/resource-loader-context-files.js +13 -10
- package/dist/core/resource-loader-context-files.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +2 -2
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-entry-normalization.d.ts +7 -0
- package/dist/core/session-entry-normalization.d.ts.map +1 -0
- package/dist/core/session-entry-normalization.js +14 -0
- package/dist/core/session-entry-normalization.js.map +1 -0
- package/dist/core/session-manager-core.d.ts.map +1 -1
- package/dist/core/session-manager-core.js +1 -0
- package/dist/core/session-manager-core.js.map +1 -1
- package/dist/core/session-manager-history.d.ts +2 -2
- package/dist/core/session-manager-history.d.ts.map +1 -1
- package/dist/core/session-manager-history.js +127 -108
- package/dist/core/session-manager-history.js.map +1 -1
- package/dist/core/settings-manager-basic-accessors.d.ts +2 -2
- package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-basic-accessors.js.map +1 -1
- package/dist/core/settings-types.d.ts +1 -1
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +11 -8
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/main-session.d.ts.map +1 -1
- package/dist/main-session.js +1 -0
- package/dist/main-session.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector-options.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector-options.js +3 -1
- package/dist/modes/interactive/components/settings-selector-options.js.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +2 -1
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-agent-events.js +2 -1
- package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
- package/dist/modes/interactive/interactive-child-ordering.d.ts +7 -0
- package/dist/modes/interactive/interactive-child-ordering.d.ts.map +1 -0
- package/dist/modes/interactive/interactive-child-ordering.js +26 -0
- package/dist/modes/interactive/interactive-child-ordering.js.map +1 -0
- package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-deferred-startup.js +2 -2
- package/dist/modes/interactive/interactive-deferred-startup.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-model-routing.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-model-routing.js +8 -3
- package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
- package/dist/modes/interactive/theme/theme-class.d.ts +1 -1
- package/dist/modes/interactive/theme/theme-class.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-class.js +2 -0
- package/dist/modes/interactive/theme/theme-class.js.map +1 -1
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +3 -0
- package/dist/utils/clipboard-image.js.map +1 -1
- package/docs/changelog.mdx +19 -0
- package/docs/compaction.md +55 -48
- package/docs/custom-provider.md +18 -5
- package/docs/extensions.md +7 -3
- package/docs/models.md +69 -10
- package/docs/providers.md +1 -1
- package/docs/quickstart.md +11 -7
- package/docs/rpc.md +2 -2
- package/docs/sdk.md +1 -1
- package/docs/settings.md +3 -3
- package/docs/subagents.md +36 -5
- package/docs/tools.md +1 -1
- package/docs/usage.md +4 -2
- package/docs/workflows.md +26 -43
- package/examples/extensions/preset.ts +2 -2
- package/examples/extensions/subagent/README.md +2 -2
- package/examples/extensions/subagent/index.ts +1 -2
- package/examples/extensions/subagent/schemas.ts +4 -1
- package/examples/sdk/12-full-control.ts +1 -1
- package/npm-shrinkwrap.json +48 -51
- package/package.json +7 -7
- package/dist/builtin/subagents/src/runs/foreground/chain-clarify-behavior.ts +0 -75
- package/dist/builtin/subagents/src/runs/foreground/chain-clarify-component.ts +0 -202
- package/dist/builtin/subagents/src/runs/foreground/chain-clarify-edit.ts +0 -97
- package/dist/builtin/subagents/src/runs/foreground/chain-clarify-editor.ts +0 -160
- package/dist/builtin/subagents/src/runs/foreground/chain-clarify-frame.ts +0 -72
- package/dist/builtin/subagents/src/runs/foreground/chain-clarify-render-modes.ts +0 -161
- package/dist/builtin/subagents/src/runs/foreground/chain-clarify-render-selectors.ts +0 -203
- package/dist/builtin/subagents/src/runs/foreground/chain-clarify-selectors.ts +0 -234
- package/dist/builtin/subagents/src/runs/foreground/chain-clarify-state.ts +0 -103
- package/dist/builtin/subagents/src/runs/foreground/chain-clarify-types.ts +0 -29
- package/dist/builtin/subagents/src/runs/foreground/chain-clarify.ts +0 -9
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-clarify.ts +0 -117
|
@@ -1,91 +1,47 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { isSafeFsWatchPathError, watchWithErrorHandler } from "@bastani/atomic";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
type SubagentResultIntercomChild,
|
|
11
|
-
type SubagentState,
|
|
12
|
-
} from "../../shared/types.ts";
|
|
13
|
-
import {
|
|
14
|
-
attachNestedChildrenToResultChildren,
|
|
15
|
-
buildSubagentResultIntercomPayload,
|
|
16
|
-
compactNestedResultChildren,
|
|
17
|
-
deliverSubagentResultIntercomEvent,
|
|
18
|
-
resolveSubagentResultStatus,
|
|
19
|
-
} from "../../intercom/result-intercom.ts";
|
|
20
|
-
import { projectNestedRegistryForRoot, sanitizeSummary } from "../shared/nested-events.ts";
|
|
4
|
+
import { createFileCoalescer } from "../../shared/file-coalescer.js";
|
|
5
|
+
import type { IntercomEventBus, SubagentState } from "../../shared/types.js";
|
|
6
|
+
import { processResultEntry, type ResultProcessorFs } from "./result-delivery-processor.js";
|
|
7
|
+
import { claimIdFromScheduleKey, claimScheduleKey, listResultClaims } from "./result-file-claims.js";
|
|
8
|
+
import { createRetryScheduler } from "./result-retry-scheduler.js";
|
|
9
|
+
import type { ResultFileData } from "./result-watcher-data.js";
|
|
21
10
|
|
|
22
11
|
const WATCHER_RESTART_DELAY_MS = 3000;
|
|
23
12
|
const POLL_INTERVAL_MS = 3000;
|
|
13
|
+
const DIRECTORY_RESCAN_DELAY_MS = 50;
|
|
14
|
+
const STATUS_RECHECK_BASE_MS = 250;
|
|
15
|
+
const STATUS_RECHECK_MAX_MS = 30_000;
|
|
16
|
+
const DELIVERY_RETRY_BASE_MS = 1000;
|
|
17
|
+
const DELIVERY_RETRY_MAX_MS = 30_000;
|
|
24
18
|
|
|
25
|
-
type ResultWatcherFs = Pick<typeof fs, "existsSync" | "
|
|
26
|
-
realpathSync?: typeof fs.realpathSync;
|
|
27
|
-
};
|
|
28
|
-
|
|
19
|
+
type ResultWatcherFs = ResultProcessorFs & Pick<typeof fs, "existsSync" | "readdirSync" | "mkdirSync" | "watch">;
|
|
29
20
|
type ResultWatcherTimers = {
|
|
30
21
|
setTimeout: typeof setTimeout;
|
|
31
22
|
clearTimeout: typeof clearTimeout;
|
|
32
23
|
setInterval: typeof setInterval;
|
|
33
24
|
clearInterval: typeof clearInterval;
|
|
34
25
|
};
|
|
35
|
-
|
|
36
26
|
type ResultWatcherSafeWatch = typeof watchWithErrorHandler;
|
|
37
27
|
|
|
38
28
|
type ResultWatcherDeps = {
|
|
39
29
|
fs?: ResultWatcherFs;
|
|
40
30
|
timers?: ResultWatcherTimers;
|
|
41
31
|
safeWatch?: ResultWatcherSafeWatch;
|
|
32
|
+
statusRecheckBaseMs?: number;
|
|
33
|
+
statusRecheckMaxMs?: number;
|
|
34
|
+
deliveryRetryBaseMs?: number;
|
|
35
|
+
deliveryRetryMaxMs?: number;
|
|
36
|
+
maxNoProgressFailures?: number;
|
|
37
|
+
intercomTimeoutMs?: number | false;
|
|
38
|
+
allowedStatusRoots?: string[];
|
|
39
|
+
maxStatusBytes?: number;
|
|
42
40
|
};
|
|
43
41
|
|
|
44
|
-
type ResultFileChild = {
|
|
45
|
-
agent?: string;
|
|
46
|
-
output?: string;
|
|
47
|
-
error?: string;
|
|
48
|
-
success?: boolean;
|
|
49
|
-
sessionFile?: string;
|
|
50
|
-
artifactPaths?: { outputPath?: string };
|
|
51
|
-
intercomTarget?: string;
|
|
52
|
-
children?: unknown;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
type ResultFileData = {
|
|
56
|
-
id?: string;
|
|
57
|
-
runId?: string;
|
|
58
|
-
agent?: string;
|
|
59
|
-
success?: boolean;
|
|
60
|
-
state?: string;
|
|
61
|
-
mode?: string;
|
|
62
|
-
summary?: string;
|
|
63
|
-
results?: ResultFileChild[];
|
|
64
|
-
nestedChildren?: unknown;
|
|
65
|
-
sessionId?: string;
|
|
66
|
-
cwd?: string;
|
|
67
|
-
sessionFile?: string;
|
|
68
|
-
asyncDir?: string;
|
|
69
|
-
intercomTarget?: string;
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
function sanitizeNestedResultChildren(value: unknown, resultPath: string, label: string): NestedRunSummary[] | undefined {
|
|
73
|
-
if (value === undefined) return undefined;
|
|
74
|
-
if (!Array.isArray(value)) {
|
|
75
|
-
console.error(`Ignoring invalid nested children in subagent result file '${resultPath}' at ${label}: expected an array.`);
|
|
76
|
-
return undefined;
|
|
77
|
-
}
|
|
78
|
-
const children = value.map((child) => sanitizeSummary(child)).filter((child): child is NestedRunSummary => Boolean(child));
|
|
79
|
-
if (children.length !== value.length) {
|
|
80
|
-
console.error(`Ignoring ${value.length - children.length} invalid nested child record(s) in subagent result file '${resultPath}' at ${label}.`);
|
|
81
|
-
}
|
|
82
|
-
return children.length ? children : undefined;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
42
|
function getErrorCode(error: unknown): string | undefined {
|
|
86
43
|
return typeof error === "object" && error !== null && "code" in error
|
|
87
|
-
? (error as NodeJS.ErrnoException).code
|
|
88
|
-
: undefined;
|
|
44
|
+
? (error as NodeJS.ErrnoException).code : undefined;
|
|
89
45
|
}
|
|
90
46
|
|
|
91
47
|
function isNotFoundError(error: unknown): boolean {
|
|
@@ -103,215 +59,193 @@ export function createResultWatcher(
|
|
|
103
59
|
resultsDir: string,
|
|
104
60
|
completionTtlMs: number,
|
|
105
61
|
deps: ResultWatcherDeps = {},
|
|
106
|
-
): {
|
|
107
|
-
startResultWatcher: () => void;
|
|
108
|
-
primeExistingResults: () => void;
|
|
109
|
-
stopResultWatcher: () => void;
|
|
110
|
-
} {
|
|
62
|
+
): { startResultWatcher: () => void; primeExistingResults: () => void; stopResultWatcher: () => void } {
|
|
111
63
|
const fsApi = deps.fs ?? fs;
|
|
112
64
|
const timers = deps.timers ?? { setTimeout, clearTimeout, setInterval, clearInterval };
|
|
113
65
|
const safeWatch = deps.safeWatch ?? watchWithErrorHandler;
|
|
66
|
+
let directoryRescanTimer: ReturnType<typeof setTimeout> | null = null;
|
|
67
|
+
let stopped = false;
|
|
68
|
+
let processingEpoch = 0;
|
|
69
|
+
let watcherInstallation = 0;
|
|
70
|
+
let activeWatcherInstallation = 0;
|
|
71
|
+
const inFlight = new Set<string>();
|
|
72
|
+
const rerunAfterFlight = new Set<string>();
|
|
73
|
+
const statusRetries = createRetryScheduler(timers, deps.statusRecheckBaseMs ?? STATUS_RECHECK_BASE_MS, deps.statusRecheckMaxMs ?? STATUS_RECHECK_MAX_MS);
|
|
74
|
+
const deliveryRetries = createRetryScheduler(timers, deps.deliveryRetryBaseMs ?? DELIVERY_RETRY_BASE_MS, deps.deliveryRetryMaxMs ?? DELIVERY_RETRY_MAX_MS);
|
|
75
|
+
const isEpochActive = (epoch: number): boolean => !stopped && epoch === processingEpoch;
|
|
76
|
+
const entryExists = (entry: string): boolean => {
|
|
77
|
+
const claimId = claimIdFromScheduleKey(entry);
|
|
78
|
+
return claimId ? Boolean(listResultClaims(resultsDir, fsApi).some((claim) => claim.id === claimId))
|
|
79
|
+
: fsApi.existsSync(path.join(resultsDir, entry));
|
|
80
|
+
};
|
|
81
|
+
const ownsResult = (data: ResultFileData): boolean => !stopped
|
|
82
|
+
&& (!data.sessionId || data.sessionId === state.currentSessionId)
|
|
83
|
+
&& Boolean(data.sessionId || !data.cwd || (state.baseCwd && data.cwd === state.baseCwd));
|
|
84
|
+
|
|
85
|
+
const coalescerKey = (entry: string, epoch: number): string => `${epoch}\0${entry}`;
|
|
86
|
+
const parseCoalescerKey = (key: string): { epoch: number; entry: string } => {
|
|
87
|
+
const split = key.indexOf("\0");
|
|
88
|
+
return { epoch: Number(key.slice(0, split)), entry: key.slice(split + 1) };
|
|
89
|
+
};
|
|
90
|
+
const scheduleResultEntry = (entry: string, delay = 0, epoch = processingEpoch): boolean => {
|
|
91
|
+
if (!isEpochActive(epoch) || statusRetries.has(entry) || deliveryRetries.has(entry)) return false;
|
|
92
|
+
return state.resultFileCoalescer.schedule(coalescerKey(entry, epoch), delay);
|
|
93
|
+
};
|
|
94
|
+
const scheduleStatusRecheck = (entry: string, epoch: number) => {
|
|
95
|
+
if (!isEpochActive(epoch)) return;
|
|
96
|
+
statusRetries.schedule(entry, () => {
|
|
97
|
+
if (!isEpochActive(epoch) || !entryExists(entry)) { statusRetries.clear(entry); return; }
|
|
98
|
+
scheduleResultEntry(entry, 0, epoch);
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
const scheduleDeliveryRetry = (entry: string, epoch: number) => {
|
|
102
|
+
if (!isEpochActive(epoch)) return;
|
|
103
|
+
deliveryRetries.schedule(entry, () => {
|
|
104
|
+
if (!isEpochActive(epoch) || !entryExists(entry)) { deliveryRetries.clear(entry); return; }
|
|
105
|
+
scheduleResultEntry(entry, 0, epoch);
|
|
106
|
+
});
|
|
107
|
+
};
|
|
114
108
|
|
|
115
|
-
const handleResult = async (
|
|
116
|
-
|
|
117
|
-
|
|
109
|
+
const handleResult = async (entry: string, epoch: number) => {
|
|
110
|
+
if (!isEpochActive(epoch)) return;
|
|
111
|
+
const flightKey = coalescerKey(entry, epoch);
|
|
112
|
+
if (inFlight.has(flightKey)) { rerunAfterFlight.add(flightKey); return; }
|
|
113
|
+
inFlight.add(flightKey);
|
|
118
114
|
try {
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
try {
|
|
128
|
-
nestedChildren = compactNestedResultChildren(projectNestedRegistryForRoot(runId)?.children);
|
|
129
|
-
} catch (error) {
|
|
130
|
-
console.error(`Failed to enrich subagent result file '${resultPath}' with nested registry children; will retry later:`, error);
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
const now = Date.now();
|
|
135
|
-
const completionKey = buildCompletionKey(data, `result:${file}`);
|
|
136
|
-
if (markSeenWithTtl(state.completionSeen, completionKey, now, completionTtlMs)) {
|
|
137
|
-
fsApi.unlinkSync(resultPath);
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const hasResultChildren = Array.isArray(data.results) && data.results.length > 0;
|
|
142
|
-
const resultChildren = hasResultChildren
|
|
143
|
-
? data.results!
|
|
144
|
-
: [{
|
|
145
|
-
agent: data.agent,
|
|
146
|
-
output: data.summary,
|
|
147
|
-
success: data.success,
|
|
148
|
-
}];
|
|
149
|
-
const normalizedChildren = attachNestedChildrenToResultChildren(runId, resultChildren.map((result = {}, index): SubagentResultIntercomChild => {
|
|
150
|
-
const baseOutput = result.output ?? data.summary;
|
|
151
|
-
const hasRealOutput = typeof baseOutput === "string" && baseOutput.trim().length > 0;
|
|
152
|
-
const output = hasRealOutput ? baseOutput : "(no output)";
|
|
153
|
-
const summary = result.success === false && result.error
|
|
154
|
-
? `${result.error}${hasRealOutput ? `\n\nOutput:\n${baseOutput}` : ""}`
|
|
155
|
-
: output;
|
|
156
|
-
const sessionPath = result.sessionFile ?? (resultChildren.length === 1 ? data.sessionFile : undefined);
|
|
157
|
-
const childNestedChildren = sanitizeNestedResultChildren(result.children, resultPath, `results[${index}].children`);
|
|
158
|
-
return {
|
|
159
|
-
agent: result.agent ?? data.agent ?? `step-${index + 1}`,
|
|
160
|
-
status: resolveSubagentResultStatus({
|
|
161
|
-
success: result.success,
|
|
162
|
-
state: data.state === "paused" || typeof result.success !== "boolean" ? data.state : undefined,
|
|
163
|
-
}),
|
|
164
|
-
summary,
|
|
165
|
-
index,
|
|
166
|
-
artifactPath: result.artifactPaths?.outputPath,
|
|
167
|
-
...(typeof sessionPath === "string" && fsApi.existsSync(sessionPath) ? { sessionPath } : {}),
|
|
168
|
-
...(result.intercomTarget ? { intercomTarget: result.intercomTarget } : {}),
|
|
169
|
-
...(childNestedChildren ? { children: childNestedChildren } : {}),
|
|
170
|
-
};
|
|
171
|
-
}), nestedChildren);
|
|
172
|
-
|
|
173
|
-
const intercomTarget = data.intercomTarget?.trim();
|
|
174
|
-
if (intercomTarget) {
|
|
175
|
-
const mode = data.mode === "single" || data.mode === "parallel" || data.mode === "chain"
|
|
176
|
-
? data.mode
|
|
177
|
-
: resultChildren.length > 1 ? "chain" : "single";
|
|
178
|
-
const payload = buildSubagentResultIntercomPayload({
|
|
179
|
-
to: intercomTarget,
|
|
180
|
-
runId,
|
|
181
|
-
mode,
|
|
182
|
-
source: "async",
|
|
183
|
-
children: normalizedChildren,
|
|
184
|
-
asyncId: data.id,
|
|
185
|
-
asyncDir: data.asyncDir,
|
|
186
|
-
});
|
|
187
|
-
const delivered = await deliverSubagentResultIntercomEvent(pi.events, payload);
|
|
188
|
-
if (!delivered) {
|
|
189
|
-
console.error(`Subagent async grouped result intercom delivery was not acknowledged for '${resultPath}'.`);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
pi.events.emit(SUBAGENT_ASYNC_COMPLETE_EVENT, {
|
|
194
|
-
...data,
|
|
195
|
-
runId,
|
|
196
|
-
...(nestedChildren?.length ? { nestedChildren } : {}),
|
|
197
|
-
...(Array.isArray(data.results) ? {
|
|
198
|
-
results: hasResultChildren
|
|
199
|
-
? normalizedChildren.map((child, index) => ({
|
|
200
|
-
...data.results![index],
|
|
201
|
-
agent: child.agent,
|
|
202
|
-
status: child.status,
|
|
203
|
-
summary: child.summary,
|
|
204
|
-
index: child.index,
|
|
205
|
-
artifactPath: child.artifactPath,
|
|
206
|
-
sessionPath: child.sessionPath,
|
|
207
|
-
children: child.children,
|
|
208
|
-
}))
|
|
209
|
-
: [],
|
|
210
|
-
} : {}),
|
|
115
|
+
const outcome = await processResultEntry(entry, {
|
|
116
|
+
pi, state, resultsDir, completionTtlMs, fsApi,
|
|
117
|
+
allowedStatusRoots: deps.allowedStatusRoots,
|
|
118
|
+
maxStatusBytes: deps.maxStatusBytes,
|
|
119
|
+
maxNoProgressFailures: deps.maxNoProgressFailures,
|
|
120
|
+
intercomTimeoutMs: deps.intercomTimeoutMs,
|
|
121
|
+
isActive: () => isEpochActive(epoch),
|
|
122
|
+
ownsResult: (data) => isEpochActive(epoch) && ownsResult(data),
|
|
211
123
|
});
|
|
212
|
-
|
|
124
|
+
const nextEntry = outcome.entry ?? entry;
|
|
125
|
+
if (outcome.status === "status-pending") scheduleStatusRecheck(nextEntry, epoch);
|
|
126
|
+
else if (outcome.status === "delivery-retry") scheduleDeliveryRetry(nextEntry, epoch);
|
|
127
|
+
else { statusRetries.clear(entry); deliveryRetries.clear(entry); }
|
|
213
128
|
} catch (error) {
|
|
214
|
-
if (isNotFoundError(error))
|
|
215
|
-
|
|
129
|
+
if (!isNotFoundError(error)) {
|
|
130
|
+
console.error(`Failed to process subagent result entry '${entry}':`, error);
|
|
131
|
+
if (isEpochActive(epoch) && entryExists(entry)) scheduleDeliveryRetry(entry, epoch);
|
|
132
|
+
}
|
|
133
|
+
} finally {
|
|
134
|
+
inFlight.delete(flightKey);
|
|
135
|
+
if (rerunAfterFlight.delete(flightKey) && entryExists(entry)) scheduleResultEntry(entry, 0, epoch);
|
|
216
136
|
}
|
|
217
137
|
};
|
|
218
138
|
|
|
219
|
-
state.resultFileCoalescer = createFileCoalescer((
|
|
220
|
-
|
|
221
|
-
|
|
139
|
+
state.resultFileCoalescer = createFileCoalescer((key) => {
|
|
140
|
+
const { epoch, entry } = parseCoalescerKey(key);
|
|
141
|
+
void handleResult(entry, epoch);
|
|
142
|
+
}, 50, { setTimeout: timers.setTimeout, clearTimeout: timers.clearTimeout });
|
|
222
143
|
|
|
223
144
|
const primeExistingResults = () => {
|
|
145
|
+
if (stopped) return;
|
|
146
|
+
const epoch = processingEpoch;
|
|
224
147
|
try {
|
|
225
|
-
fsApi.readdirSync(resultsDir)
|
|
226
|
-
|
|
227
|
-
.forEach((file) => state.resultFileCoalescer.schedule(file, 0));
|
|
148
|
+
for (const file of fsApi.readdirSync(resultsDir)) if (file.endsWith(".json")) scheduleResultEntry(file, 0, epoch);
|
|
149
|
+
for (const claim of listResultClaims(resultsDir, fsApi)) scheduleResultEntry(claimScheduleKey(claim.id), 0, epoch);
|
|
228
150
|
} catch (error) {
|
|
229
|
-
if (isNotFoundError(error))
|
|
230
|
-
console.error(`Failed to scan subagent result directory '${resultsDir}':`, error);
|
|
151
|
+
if (!isNotFoundError(error)) console.error(`Failed to scan subagent result directory '${resultsDir}':`, error);
|
|
231
152
|
}
|
|
232
153
|
};
|
|
154
|
+
const scheduleDirectoryRescan = (epoch: number) => {
|
|
155
|
+
if (!isEpochActive(epoch)) return;
|
|
156
|
+
if (directoryRescanTimer) timers.clearTimeout(directoryRescanTimer);
|
|
157
|
+
directoryRescanTimer = timers.setTimeout(() => {
|
|
158
|
+
directoryRescanTimer = null;
|
|
159
|
+
if (isEpochActive(epoch)) primeExistingResults();
|
|
160
|
+
}, DIRECTORY_RESCAN_DELAY_MS);
|
|
161
|
+
directoryRescanTimer.unref?.();
|
|
162
|
+
};
|
|
233
163
|
|
|
234
|
-
const startPollingFallback = (reason: unknown) => {
|
|
164
|
+
const startPollingFallback = (reason: unknown, epoch: number, installation?: number) => {
|
|
165
|
+
if (!isEpochActive(epoch) || (installation !== undefined && installation !== activeWatcherInstallation)) return;
|
|
235
166
|
state.watcher?.close();
|
|
236
167
|
state.watcher = null;
|
|
168
|
+
activeWatcherInstallation = 0;
|
|
237
169
|
if (state.watcherRestartTimer) return;
|
|
238
|
-
|
|
239
|
-
console.error(
|
|
240
|
-
`Subagent result watcher for '${resultsDir}' fell back to polling because native fs.watch is unavailable (${getErrorCode(reason) ?? "unknown error"}).`,
|
|
241
|
-
);
|
|
170
|
+
console.error(`Subagent result watcher for '${resultsDir}' fell back to polling because native fs.watch is unavailable (${getErrorCode(reason) ?? "unknown error"}).`);
|
|
242
171
|
primeExistingResults();
|
|
243
|
-
state.watcherRestartTimer = timers.setInterval(primeExistingResults, POLL_INTERVAL_MS);
|
|
172
|
+
state.watcherRestartTimer = timers.setInterval(() => { if (isEpochActive(epoch)) primeExistingResults(); }, POLL_INTERVAL_MS);
|
|
244
173
|
state.watcherRestartTimer.unref?.();
|
|
245
174
|
};
|
|
246
175
|
|
|
247
|
-
|
|
248
|
-
if (state.watcherRestartTimer) return;
|
|
249
|
-
|
|
176
|
+
function scheduleRestart(epoch: number): void {
|
|
177
|
+
if (!isEpochActive(epoch) || state.watcherRestartTimer) return;
|
|
178
|
+
let timer!: ReturnType<typeof setTimeout>;
|
|
179
|
+
timer = timers.setTimeout(() => {
|
|
180
|
+
if (state.watcherRestartTimer !== timer || !isEpochActive(epoch)) return;
|
|
250
181
|
state.watcherRestartTimer = null;
|
|
251
|
-
try {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
startPollingFallback(error);
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
console.error(`Failed to restart subagent result watcher for '${resultsDir}':`, error);
|
|
260
|
-
scheduleRestart();
|
|
182
|
+
try { fsApi.mkdirSync(resultsDir, { recursive: true }); openResultWatcher(epoch); }
|
|
183
|
+
catch (error) {
|
|
184
|
+
if (!isEpochActive(epoch)) return;
|
|
185
|
+
if (shouldFallBackToPolling(error)) startPollingFallback(error, epoch);
|
|
186
|
+
else { console.error(`Failed to restart subagent result watcher for '${resultsDir}':`, error); scheduleRestart(epoch); }
|
|
261
187
|
}
|
|
262
188
|
}, WATCHER_RESTART_DELAY_MS);
|
|
263
|
-
state.watcherRestartTimer
|
|
264
|
-
|
|
189
|
+
state.watcherRestartTimer = timer;
|
|
190
|
+
timer.unref?.();
|
|
191
|
+
}
|
|
265
192
|
|
|
266
|
-
|
|
267
|
-
if (state.watcher) return;
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
193
|
+
function openResultWatcher(epoch: number): void {
|
|
194
|
+
if (!isEpochActive(epoch) || state.watcher) return;
|
|
195
|
+
const installation = ++watcherInstallation;
|
|
196
|
+
let handle: fs.FSWatcher | null = null;
|
|
197
|
+
let pendingSynchronousError: Error | undefined;
|
|
198
|
+
const handleWatcherError = (error: Error) => {
|
|
199
|
+
if (!handle) { pendingSynchronousError = error; return; }
|
|
200
|
+
if (!isEpochActive(epoch) || installation !== activeWatcherInstallation || state.watcher !== handle) return;
|
|
201
|
+
if (shouldFallBackToPolling(error)) { startPollingFallback(error, epoch, installation); return; }
|
|
202
|
+
console.error(`Subagent result watcher failed for '${resultsDir}':`, error);
|
|
203
|
+
handle.close();
|
|
204
|
+
if (state.watcher === handle) state.watcher = null;
|
|
205
|
+
activeWatcherInstallation = 0;
|
|
206
|
+
scheduleRestart(epoch);
|
|
207
|
+
};
|
|
273
208
|
try {
|
|
274
|
-
|
|
275
|
-
if (
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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,
|
|
292
|
-
});
|
|
293
|
-
state.watcher?.unref?.();
|
|
209
|
+
handle = safeWatch(resultsDir, (_event, file) => {
|
|
210
|
+
if (!isEpochActive(epoch) || installation !== activeWatcherInstallation) return;
|
|
211
|
+
if (file) { const name = file.toString(); if (name.endsWith(".json")) scheduleResultEntry(name, 50, epoch); }
|
|
212
|
+
scheduleDirectoryRescan(epoch);
|
|
213
|
+
}, handleWatcherError, { watch: fsApi.watch, realpathSyncNative: fsApi.realpathSync?.native });
|
|
214
|
+
if (!handle) throw pendingSynchronousError ?? new Error("Result watcher installation returned no handle");
|
|
215
|
+
if (!isEpochActive(epoch)) { handle.close(); return; }
|
|
216
|
+
state.watcher = handle;
|
|
217
|
+
activeWatcherInstallation = installation;
|
|
218
|
+
handle.unref?.();
|
|
219
|
+
if (pendingSynchronousError) handleWatcherError(pendingSynchronousError);
|
|
294
220
|
} catch (error) {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
scheduleRestart();
|
|
221
|
+
handle?.close();
|
|
222
|
+
if (state.watcher === handle) state.watcher = null;
|
|
223
|
+
activeWatcherInstallation = 0;
|
|
224
|
+
if (!isEpochActive(epoch)) return;
|
|
225
|
+
if (shouldFallBackToPolling(error)) startPollingFallback(error, epoch);
|
|
226
|
+
else { console.error(`Failed to start subagent result watcher for '${resultsDir}':`, error); scheduleRestart(epoch); }
|
|
302
227
|
}
|
|
303
|
-
}
|
|
228
|
+
}
|
|
304
229
|
|
|
230
|
+
const startResultWatcher = () => {
|
|
231
|
+
stopped = false;
|
|
232
|
+
processingEpoch += 1;
|
|
233
|
+
if (state.watcherRestartTimer) {
|
|
234
|
+
timers.clearTimeout(state.watcherRestartTimer); timers.clearInterval(state.watcherRestartTimer); state.watcherRestartTimer = null;
|
|
235
|
+
}
|
|
236
|
+
openResultWatcher(processingEpoch);
|
|
237
|
+
};
|
|
305
238
|
const stopResultWatcher = () => {
|
|
239
|
+
stopped = true;
|
|
240
|
+
processingEpoch += 1;
|
|
306
241
|
state.watcher?.close();
|
|
307
242
|
state.watcher = null;
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
timers.clearInterval(state.watcherRestartTimer);
|
|
311
|
-
}
|
|
243
|
+
activeWatcherInstallation = 0;
|
|
244
|
+
if (state.watcherRestartTimer) { timers.clearTimeout(state.watcherRestartTimer); timers.clearInterval(state.watcherRestartTimer); }
|
|
312
245
|
state.watcherRestartTimer = null;
|
|
313
|
-
|
|
246
|
+
if (directoryRescanTimer) timers.clearTimeout(directoryRescanTimer);
|
|
247
|
+
directoryRescanTimer = null;
|
|
248
|
+
statusRetries.clearAll(); deliveryRetries.clearAll(); state.resultFileCoalescer.clear();
|
|
314
249
|
};
|
|
315
|
-
|
|
316
250
|
return { startResultWatcher, primeExistingResults, stopResultWatcher };
|
|
317
251
|
}
|
|
@@ -28,7 +28,8 @@ function exactAsyncLocation(id: string, asyncDirRoot: string, resultsDir: string
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function foregroundIds(state: SubagentState | undefined): string[] {
|
|
31
|
-
|
|
31
|
+
if (!state) return [];
|
|
32
|
+
return [...new Set([...state.foregroundControls.keys(), ...(state.foregroundRuns?.keys() ?? [])])];
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
export function nestedScopeFromState(state: SubagentState | undefined): NestedRunResolutionScope | undefined {
|
|
@@ -57,7 +58,7 @@ export function resolveSubagentRunId(id: string, deps: ResolveSubagentRunIdDeps
|
|
|
57
58
|
const resultsDir = deps.resultsDir ?? RESULTS_DIR;
|
|
58
59
|
|
|
59
60
|
const nestedScope = deps.nested ?? nestedScopeFromState(deps.state);
|
|
60
|
-
if (deps.state?.foregroundControls.has(id)) return { kind: "foreground", id };
|
|
61
|
+
if (deps.state?.foregroundControls.has(id) || deps.state?.foregroundRuns?.has(id)) return { kind: "foreground", id };
|
|
61
62
|
const exactAsync = exactAsyncLocation(id, asyncDirRoot, resultsDir);
|
|
62
63
|
if (exactAsync) return { kind: "async", id, location: exactAsync };
|
|
63
64
|
const exactNested = findNestedRunMatchesById(id, nestedScope ? { scope: nestedScope } : {});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { writeAtomicJson } from "../../shared/atomic-json.ts";
|
|
4
|
+
import { publishFileExclusive, unlinkIfPresent } from "../../shared/exclusive-file-publication.js";
|
|
4
5
|
import { RESULTS_DIR, type AsyncParallelGroupStatus, type AsyncStatus, type NestedRunSummary, type SubagentRunMode } from "../../shared/types.ts";
|
|
5
6
|
import { normalizeParallelGroups } from "./parallel-groups.ts";
|
|
6
7
|
import { nestedSummaryFromAsyncStatus, projectNestedEvents, resolveNestedAsyncDir, writeNestedEvent, type NestedRoute } from "../shared/nested-events.ts";
|
|
@@ -28,6 +29,7 @@ interface ReconcileAsyncRunOptions {
|
|
|
28
29
|
startedRun?: StartedRunMetadata;
|
|
29
30
|
missingStatusGraceMs?: number;
|
|
30
31
|
staleAlivePidMs?: number;
|
|
32
|
+
publish?: (source: string, destination: string) => "published" | "exists";
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
interface ReconcileAsyncRunResult {
|
|
@@ -53,6 +55,52 @@ function appendJsonl(filePath: string, payload: object): void {
|
|
|
53
55
|
fs.appendFileSync(filePath, `${JSON.stringify(payload)}\n`, "utf-8");
|
|
54
56
|
}
|
|
55
57
|
|
|
58
|
+
function repairStagePath(resultPath: string): string {
|
|
59
|
+
return path.join(path.dirname(resultPath), `.${path.basename(resultPath)}.stale-repair-stage`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function readStagedRepair(resultPath: string): object | undefined {
|
|
63
|
+
try {
|
|
64
|
+
return JSON.parse(fs.readFileSync(repairStagePath(resultPath), "utf-8")) as object;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
if (isNotFoundError(error)) return undefined;
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function stagedRepairMessage(staged: object): string | undefined {
|
|
72
|
+
const summary = (staged as { summary?: unknown }).summary;
|
|
73
|
+
return typeof summary === "string" ? summary : undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function retireObsoleteStage(resultPath: string): void {
|
|
77
|
+
const stagePath = repairStagePath(resultPath);
|
|
78
|
+
const retiredPath = `${stagePath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.retired`;
|
|
79
|
+
try {
|
|
80
|
+
fs.renameSync(stagePath, retiredPath);
|
|
81
|
+
try { fs.unlinkSync(retiredPath); } catch (error) { if (!isNotFoundError(error)) console.error(error); }
|
|
82
|
+
} catch (error) {
|
|
83
|
+
if (!isNotFoundError(error)) console.error(error);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function publishStagedRepair(
|
|
88
|
+
resultPath: string,
|
|
89
|
+
status: AsyncStatus,
|
|
90
|
+
staged: object,
|
|
91
|
+
publish: (source: string, destination: string) => "published" | "exists" = publishFileExclusive,
|
|
92
|
+
): ReconcileAsyncRunResult {
|
|
93
|
+
const stagePath = repairStagePath(resultPath);
|
|
94
|
+
const outcome = publish(stagePath, resultPath);
|
|
95
|
+
unlinkIfPresent(stagePath);
|
|
96
|
+
return {
|
|
97
|
+
status,
|
|
98
|
+
repaired: outcome === "published",
|
|
99
|
+
resultPath,
|
|
100
|
+
message: outcome === "published" ? stagedRepairMessage(staged) : "A concurrently published result was preserved.",
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
56
104
|
function readStatusFile(asyncDir: string): AsyncStatus | null {
|
|
57
105
|
const statusPath = path.join(asyncDir, "status.json");
|
|
58
106
|
let content: string;
|
|
@@ -222,19 +270,25 @@ function buildFailedRepair(status: AsyncStatus, asyncDir: string, now: number, r
|
|
|
222
270
|
};
|
|
223
271
|
}
|
|
224
272
|
|
|
225
|
-
function writeFailedRepair(asyncDir: string, status: AsyncStatus, resultPath: string, now: number, reason?: string): ReconcileAsyncRunResult {
|
|
273
|
+
function writeFailedRepair(asyncDir: string, status: AsyncStatus, resultPath: string, now: number, reason?: string, publish?: ReconcileAsyncRunOptions["publish"]): ReconcileAsyncRunResult {
|
|
226
274
|
const repair = buildFailedRepair(status, asyncDir, now, reason);
|
|
227
|
-
|
|
275
|
+
// Stage the exact public payload beside its destination. Publication consumes
|
|
276
|
+
// the hidden stage with one atomic rename after terminal status is durable.
|
|
277
|
+
writeAtomicJson(repairStagePath(resultPath), repair.result);
|
|
228
278
|
writeAtomicJson(path.join(asyncDir, "status.json"), repair.status);
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
279
|
+
try {
|
|
280
|
+
appendJsonl(path.join(asyncDir, "events.jsonl"), {
|
|
281
|
+
type: "subagent.run.repaired_stale",
|
|
282
|
+
ts: now,
|
|
283
|
+
runId: repair.status.runId,
|
|
284
|
+
pid: status.pid,
|
|
285
|
+
resultPath,
|
|
286
|
+
message: repair.message,
|
|
287
|
+
});
|
|
288
|
+
} catch (error) {
|
|
289
|
+
console.error(`Failed to append stale-repair event for '${asyncDir}'; publishing the recoverable result:`, error);
|
|
290
|
+
}
|
|
291
|
+
return publishStagedRepair(resultPath, repair.status, repair.result, publish);
|
|
238
292
|
}
|
|
239
293
|
|
|
240
294
|
function terminal(state: AsyncStatus["state"]): boolean {
|
|
@@ -305,6 +359,7 @@ export function reconcileAsyncRun(asyncDir: string, options: ReconcileAsyncRunOp
|
|
|
305
359
|
const runId = effectiveStatus.runId || path.basename(asyncDir);
|
|
306
360
|
const resultPath = path.join(options.resultsDir ?? RESULTS_DIR, `${runId}.json`);
|
|
307
361
|
if (fs.existsSync(resultPath)) {
|
|
362
|
+
retireObsoleteStage(resultPath);
|
|
308
363
|
const terminalStatus = effectiveStatus.state === "running" || effectiveStatus.state === "queued"
|
|
309
364
|
? terminalStatusFromResult(effectiveStatus, resultPath, now)
|
|
310
365
|
: undefined;
|
|
@@ -315,6 +370,11 @@ export function reconcileAsyncRun(asyncDir: string, options: ReconcileAsyncRunOp
|
|
|
315
370
|
return { status: effectiveStatus, repaired: false, resultPath };
|
|
316
371
|
}
|
|
317
372
|
|
|
373
|
+
const stagedRepair = readStagedRepair(resultPath);
|
|
374
|
+
if (stagedRepair && terminal(effectiveStatus.state)) {
|
|
375
|
+
return publishStagedRepair(resultPath, effectiveStatus, stagedRepair, options.publish);
|
|
376
|
+
}
|
|
377
|
+
|
|
318
378
|
if (effectiveStatus.state !== "running" || typeof effectiveStatus.pid !== "number") {
|
|
319
379
|
return { status: status ?? null, repaired: false, resultPath };
|
|
320
380
|
}
|
|
@@ -332,8 +392,7 @@ export function reconcileAsyncRun(asyncDir: string, options: ReconcileAsyncRunOp
|
|
|
332
392
|
const lastUpdate = effectiveStatus.lastUpdate ?? effectiveStatus.startedAt;
|
|
333
393
|
if (now - lastUpdate <= staleAfterMs) return { status: status ?? null, repaired: false, resultPath };
|
|
334
394
|
const message = `Async runner process ${effectiveStatus.pid} still has a live PID, but status has not updated for ${now - lastUpdate}ms. Marked run failed by stale-run reconciliation because PID ownership cannot be verified.`;
|
|
335
|
-
return writeFailedRepair(asyncDir, effectiveStatus, resultPath, now, message);
|
|
395
|
+
return writeFailedRepair(asyncDir, effectiveStatus, resultPath, now, message, options.publish);
|
|
336
396
|
}
|
|
337
|
-
|
|
338
|
-
return writeFailedRepair(asyncDir, effectiveStatus, resultPath, now);
|
|
397
|
+
return writeFailedRepair(asyncDir, effectiveStatus, resultPath, now, undefined, options.publish);
|
|
339
398
|
}
|