@selesai/code 0.5.28 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README.md +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +9 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config-selector.d.ts.map +1 -1
- package/dist/cli/config-selector.js +1 -1
- package/dist/cli/config-selector.js.map +1 -1
- package/dist/cli/credential-print.d.ts +23 -0
- package/dist/cli/credential-print.d.ts.map +1 -0
- package/dist/cli/credential-print.js +117 -0
- package/dist/cli/credential-print.js.map +1 -0
- package/dist/cli/startup-ui.d.ts.map +1 -1
- package/dist/cli/startup-ui.js +1 -1
- package/dist/cli/startup-ui.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +3 -0
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session.d.ts +12 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +20 -14
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +11 -3
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +1 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +11 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +14 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +10 -0
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +1 -1
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/llama/provider.d.ts.map +1 -1
- package/dist/core/llama/provider.js +8 -3
- package/dist/core/llama/provider.js.map +1 -1
- package/dist/core/model-config.d.ts +30 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +6 -0
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +2 -2
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +1 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +20 -3
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts +2 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +5 -4
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +13 -6
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +24 -11
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/resource-loader.d.ts +15 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +66 -9
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +19 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/system-prompt.test.d.ts +2 -0
- package/dist/core/system-prompt.test.d.ts.map +1 -0
- package/dist/core/system-prompt.test.js +89 -0
- package/dist/core/system-prompt.test.js.map +1 -0
- package/dist/core/tools/bash.d.ts +2 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +34 -5
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.js +3 -1
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
- package/dist/defaults/models.json +13 -45
- package/dist/defaults/settings.json +1 -2
- package/dist/extensions/copy-turn.test.ts +131 -0
- package/dist/extensions/copy-turn.ts +6 -1
- package/dist/extensions/package.json +0 -1
- package/dist/extensions/pi-intercom/CHANGELOG.md +249 -0
- package/dist/extensions/pi-intercom/README.md +102 -39
- package/dist/extensions/pi-intercom/broker/broker.ts +1233 -36
- package/dist/extensions/pi-intercom/broker/client.test.ts +83 -0
- package/dist/extensions/pi-intercom/broker/client.ts +315 -12
- package/dist/extensions/pi-intercom/broker/extension-state.ts +186 -0
- package/dist/extensions/pi-intercom/broker/extension.test.ts +387 -0
- package/dist/extensions/pi-intercom/broker/framing.test.ts +114 -0
- package/dist/extensions/pi-intercom/broker/framing.ts +82 -24
- package/dist/extensions/pi-intercom/broker/paths.test.ts +153 -0
- package/dist/extensions/pi-intercom/broker/paths.ts +117 -8
- package/dist/extensions/pi-intercom/broker/runtime-claim.test.ts +34 -0
- package/dist/extensions/pi-intercom/broker/runtime-claim.ts +21 -0
- package/dist/extensions/pi-intercom/broker/spawn.test.ts +160 -23
- package/dist/extensions/pi-intercom/broker/spawn.ts +113 -27
- package/dist/extensions/pi-intercom/config.test.ts +93 -0
- package/dist/extensions/pi-intercom/config.ts +55 -6
- package/dist/extensions/pi-intercom/cwd.test.ts +40 -0
- package/dist/extensions/pi-intercom/cwd.ts +31 -0
- package/dist/extensions/pi-intercom/extension-api.ts +44 -0
- package/dist/extensions/pi-intercom/format-context.test.ts +31 -0
- package/dist/extensions/pi-intercom/format-context.ts +32 -0
- package/dist/extensions/pi-intercom/index.ts +742 -145
- package/dist/extensions/pi-intercom/intercom.integration.test.ts +2646 -0
- package/dist/extensions/pi-intercom/package.json +15 -5
- package/dist/extensions/pi-intercom/reply-tracker.test.ts +134 -0
- package/dist/extensions/pi-intercom/reply-tracker.ts +31 -13
- package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +13 -11
- package/dist/extensions/pi-intercom/test/inline-message.test.ts +184 -0
- package/dist/extensions/pi-intercom/test/overlay-width.test.ts +66 -0
- package/dist/extensions/pi-intercom/types.ts +94 -4
- package/dist/extensions/pi-intercom/ui/compose.ts +8 -4
- package/dist/extensions/pi-intercom/ui/inline-message.ts +61 -25
- package/dist/extensions/pi-intercom/ui/session-list.ts +7 -3
- package/dist/extensions/pi-subagents/CHANGELOG.md +88 -0
- package/dist/extensions/pi-subagents/LICENSE +21 -0
- package/dist/extensions/pi-subagents/README.md +158 -69
- package/dist/extensions/pi-subagents/agents/architect.md +5 -4
- package/dist/extensions/pi-subagents/agents/builder.md +6 -4
- package/dist/extensions/pi-subagents/agents/commentator.md +3 -2
- package/dist/extensions/pi-subagents/agents/explorer.md +3 -2
- package/dist/extensions/pi-subagents/agents/recapper.md +3 -2
- package/dist/extensions/pi-subagents/agents/researcher.md +4 -3
- package/dist/extensions/pi-subagents/package-lock.json +2 -2
- package/dist/extensions/pi-subagents/package.json +3 -3
- package/dist/extensions/pi-subagents/prompts/review-loop.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +22 -988
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +257 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +431 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +144 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +281 -0
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +125 -37
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +4 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +118 -9
- package/dist/extensions/pi-subagents/src/agents/skills.ts +14 -12
- package/dist/extensions/pi-subagents/src/agents/task-aware-routing.ts +125 -0
- package/dist/extensions/pi-subagents/src/api/delegation.ts +3 -0
- package/dist/extensions/pi-subagents/src/api/preflight.ts +17 -13
- package/dist/extensions/pi-subagents/src/extension/chain-validation.ts +17 -1
- package/dist/extensions/pi-subagents/src/extension/index.ts +22 -8
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +248 -6
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +33 -8
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +32 -14
- package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +9 -4
- package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +33 -4
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +86 -21
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +24 -13
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +7 -5
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -2
- package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +48 -5
- package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +68 -1
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +56 -5
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +92 -11
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +29 -3
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +388 -29
- package/dist/extensions/pi-subagents/src/runs/foreground/async-stop-action.ts +65 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +3 -3
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +214 -60
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +546 -252
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +42 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +553 -154
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +19 -13
- package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +51 -19
- package/dist/extensions/pi-subagents/src/runs/shared/chain-outputs.ts +3 -1
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +44 -11
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +8 -0
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +97 -20
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +43 -12
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +52 -4
- package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +19 -0
- package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +45 -9
- package/dist/extensions/pi-subagents/src/runs/shared/runtime-acknowledged-extensions.ts +71 -0
- package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +63 -9
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +31 -1
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +101 -0
- package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +22 -1
- package/dist/extensions/pi-subagents/src/runs/shared/usage-budget.ts +65 -0
- package/dist/extensions/pi-subagents/src/runs/shared/workflow-graph.ts +26 -1
- package/dist/extensions/pi-subagents/src/shared/settings.ts +17 -1
- package/dist/extensions/pi-subagents/src/shared/types.ts +196 -12
- package/dist/extensions/pi-subagents/src/shared/utils.ts +102 -5
- package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +10 -1
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +46 -4
- package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +5 -3
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +87 -24
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +182 -9
- package/dist/extensions/pi-subagents/src/tui/render.ts +55 -34
- package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +14 -7
- package/dist/extensions/pi-subagents/src/watchdog/review.ts +5 -4
- package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +170 -17
- package/dist/extensions/pi-subagents/src/watchdog/scope.ts +62 -0
- package/dist/extensions/pi-subagents/src/watchdog/settings.ts +41 -1
- package/dist/extensions/pi-subagents/src/watchdog/types.ts +10 -0
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +113 -8
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +419 -47
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +84 -0
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +4 -2
- package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +51 -2
- package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +62 -22
- package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +48 -0
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +8 -6
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +231 -19
- package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +48 -7
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +44 -0
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +199 -17
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +898 -16
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +112 -23
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +17 -5
- package/dist/extensions/pi-subagents/test/support/mock-pi.ts +2 -0
- package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +18 -2
- package/dist/extensions/pi-subagents/test/support/register-loader.mjs +3 -3
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +3 -1
- package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +198 -6
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +161 -1
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +47 -2
- package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +60 -0
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +8 -0
- package/dist/extensions/pi-subagents/test/unit/builtin-agent-documentation.test.ts +63 -0
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +136 -0
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +6 -0
- package/dist/extensions/pi-subagents/test/unit/chain-validation.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +48 -48
- package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +23 -0
- package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +28 -2
- package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +176 -8
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +164 -5
- package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +41 -1
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +17 -12
- package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +18 -2
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +20 -1
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +30 -2
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +39 -2
- package/dist/extensions/pi-subagents/test/unit/parallel-utils.test.ts +22 -0
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +103 -0
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +18 -0
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +48 -0
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +13 -1
- package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +29 -4
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +223 -4
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +30 -0
- package/dist/extensions/pi-subagents/test/unit/runtime-acknowledged-extensions.test.ts +52 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +46 -2
- package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +91 -1
- package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/streamed-progress-bounds.test.ts +78 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +41 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +77 -0
- package/dist/extensions/pi-subagents/test/unit/task-aware-routing.test.ts +213 -0
- package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +26 -1
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +61 -10
- package/dist/extensions/pi-subagents/test/unit/total-cost.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +210 -1
- package/dist/extensions/pi-subagents/test/unit/watchdog-scope.test.ts +33 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +32 -0
- package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +43 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts +3 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +10 -2
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts +1 -2
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +16 -46
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +4 -1
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +22 -13
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts +12 -0
- package/dist/modes/interactive/external-editor.d.ts.map +1 -0
- package/dist/modes/interactive/external-editor.js +37 -0
- package/dist/modes/interactive/external-editor.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +72 -64
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +14 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/skills/ponytail/SKILL.md +1 -3
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +19 -8
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +1 -1
- package/dist/utils/version-check.js.map +1 -1
- package/docs/compaction.md +1 -1
- package/docs/custom-provider.md +14 -5
- package/docs/environment-variables.md +86 -0
- package/docs/extensions.md +15 -5
- package/docs/index.md +1 -0
- package/docs/models.md +9 -2
- package/docs/plans/subagent-delegation/phase-0-correctness.md +265 -0
- package/docs/plans/subagent-delegation/phase-1-behavioral-contract.md +486 -0
- package/docs/plans/subagent-delegation/phase-2-context-controls.md +282 -0
- package/docs/plans/subagent-delegation/phase-3-advisory-routing.md +362 -0
- package/docs/plans/subagent-delegation/phase-4-optional-enforcement.md +381 -0
- package/docs/providers.md +21 -2
- package/docs/rpc.md +23 -6
- package/docs/session-format.md +2 -0
- package/docs/settings.md +1 -1
- package/docs/usage.md +0 -13
- package/examples/extensions/custom-compaction.ts +5 -2
- package/examples/extensions/custom-provider-anthropic/index.ts +9 -3
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/handoff.ts +11 -3
- package/examples/extensions/message-renderer.ts +3 -3
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/summarize.ts +5 -2
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/sdk/12-full-control.ts +3 -1
- package/package.json +16 -7
- package/dist/extensions/caveman/caveman-instructions.cjs +0 -11
- package/dist/extensions/caveman/index.js +0 -118
- package/dist/extensions/caveman/package.json +0 -8
- package/dist/extensions/caveman/test/extension.test.js +0 -203
- package/dist/extensions/caveman/test/helpers.test.js +0 -58
- package/dist/skills/caveman/SKILL.md +0 -50
|
@@ -5,8 +5,8 @@ import { writeAtomicJson } from "../../shared/atomic-json.ts";
|
|
|
5
5
|
import { appendJsonl } from "../../shared/artifacts.ts";
|
|
6
6
|
import type { AsyncParallelGroupStatus, AsyncStatus, WorkflowGraphNode, WorkflowGraphSnapshot } from "../../shared/types.ts";
|
|
7
7
|
import { readStatus } from "../../shared/utils.ts";
|
|
8
|
-
import type { DynamicRunnerGroup, ParallelStepGroup, RunnerStep, RunnerSubagentStep } from "../shared/parallel-utils.ts";
|
|
9
|
-
import { isDynamicRunnerGroup, isParallelGroup } from "../shared/parallel-utils.ts";
|
|
8
|
+
import type { DynamicRunnerGroup, ParallelStepGroup, RunnerStep, RunnerSubagentStep, RunnerCheckpointStep } from "../shared/parallel-utils.ts";
|
|
9
|
+
import { isCheckpointRunnerStep, isDynamicRunnerGroup, isParallelGroup } from "../shared/parallel-utils.ts";
|
|
10
10
|
|
|
11
11
|
const APPEND_REQUESTS_DIR = "append-requests";
|
|
12
12
|
|
|
@@ -51,7 +51,9 @@ export function countPendingChainAppendRequests(asyncDir: string): number {
|
|
|
51
51
|
export function runnerStepOutputNames(steps: RunnerStep[]): string[] {
|
|
52
52
|
const names: string[] = [];
|
|
53
53
|
for (const step of steps) {
|
|
54
|
-
if (
|
|
54
|
+
if (isCheckpointRunnerStep(step)) {
|
|
55
|
+
continue;
|
|
56
|
+
} else if (isParallelGroup(step)) {
|
|
55
57
|
names.push(...step.parallel.map((task) => task.outputName).filter((name): name is string => Boolean(name)));
|
|
56
58
|
} else if (isDynamicRunnerGroup(step)) {
|
|
57
59
|
if (step.collect.as) names.push(step.collect.as);
|
|
@@ -128,9 +130,22 @@ export function consumeChainAppendRequests(asyncDir: string): ChainAppendRequest
|
|
|
128
130
|
return requests.sort((left, right) => left.createdAt - right.createdAt || left.id.localeCompare(right.id));
|
|
129
131
|
}
|
|
130
132
|
|
|
133
|
+
const MAX_STATUS_STEP_DESCRIPTION_CHARS = 160;
|
|
134
|
+
|
|
135
|
+
/** Bounded one-line per-step task description persisted into status.json for fleet display. */
|
|
136
|
+
export function statusStepDescription(task: string | undefined): string | undefined {
|
|
137
|
+
const description = task?.replace(/\s+/g, " ").trim();
|
|
138
|
+
if (!description) return undefined;
|
|
139
|
+
return description.length > MAX_STATUS_STEP_DESCRIPTION_CHARS
|
|
140
|
+
? `${description.slice(0, MAX_STATUS_STEP_DESCRIPTION_CHARS - 1)}…`
|
|
141
|
+
: description;
|
|
142
|
+
}
|
|
143
|
+
|
|
131
144
|
function statusStepForTask(task: RunnerSubagentStep): StatusStep {
|
|
145
|
+
const description = statusStepDescription(task.task);
|
|
132
146
|
return {
|
|
133
147
|
agent: task.agent,
|
|
148
|
+
...(description ? { description } : {}),
|
|
134
149
|
...(task.context ? { context: task.context } : {}),
|
|
135
150
|
phase: task.phase,
|
|
136
151
|
label: task.label,
|
|
@@ -147,7 +162,20 @@ function statusStepForTask(task: RunnerSubagentStep): StatusStep {
|
|
|
147
162
|
};
|
|
148
163
|
}
|
|
149
164
|
|
|
150
|
-
function
|
|
165
|
+
function statusStepForCheckpoint(step: RunnerCheckpointStep, stepIndex: number): StatusStep {
|
|
166
|
+
return {
|
|
167
|
+
agent: `checkpoint:${step.checkpoint}`,
|
|
168
|
+
phase: step.phase,
|
|
169
|
+
label: step.label ?? step.checkpoint,
|
|
170
|
+
status: "pending",
|
|
171
|
+
checkpoint: { name: step.checkpoint, ...(step.message ? { message: step.message } : {}), status: "pending", stepIndex },
|
|
172
|
+
recentTools: [],
|
|
173
|
+
recentOutput: [],
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function statusStepsForRunnerStep(step: RunnerStep, stepIndex: number): StatusStep[] {
|
|
178
|
+
if (isCheckpointRunnerStep(step)) return [statusStepForCheckpoint(step, stepIndex)];
|
|
151
179
|
if (isParallelGroup(step)) return step.parallel.map(statusStepForTask);
|
|
152
180
|
if (isDynamicRunnerGroup(step)) {
|
|
153
181
|
return [{
|
|
@@ -239,6 +267,21 @@ function graphNodeForDynamic(step: DynamicRunnerGroup, stepIndex: number): Workf
|
|
|
239
267
|
|
|
240
268
|
function appendWorkflowNode(graph: WorkflowGraphSnapshot | undefined, step: RunnerStep, stepIndex: number, flatIndex: number): void {
|
|
241
269
|
if (!graph) return;
|
|
270
|
+
if (isCheckpointRunnerStep(step)) {
|
|
271
|
+
const node: WorkflowGraphNode = {
|
|
272
|
+
id: `step-${stepIndex}`,
|
|
273
|
+
kind: "checkpoint",
|
|
274
|
+
phase: step.phase,
|
|
275
|
+
label: step.label?.trim() || step.checkpoint,
|
|
276
|
+
status: "pending",
|
|
277
|
+
flatIndex,
|
|
278
|
+
stepIndex,
|
|
279
|
+
checkpoint: { name: step.checkpoint, ...(step.message ? { message: step.message } : {}), status: "pending", stepIndex },
|
|
280
|
+
};
|
|
281
|
+
graph.nodes.push(node);
|
|
282
|
+
pushPhase(graph, step.phase, node.id);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
242
285
|
if (isParallelGroup(step)) {
|
|
243
286
|
graph.nodes.push(graphNodeForParallel(step, stepIndex, flatIndex, graph));
|
|
244
287
|
return;
|
|
@@ -263,7 +306,7 @@ export function appendRunnerStepsToStatus(input: {
|
|
|
263
306
|
for (const step of input.steps) {
|
|
264
307
|
const stepIndex = input.status.chainStepCount ?? input.status.steps?.length ?? 0;
|
|
265
308
|
const flatIndex = input.status.steps?.length ?? 0;
|
|
266
|
-
const statusSteps = statusStepsForRunnerStep(step);
|
|
309
|
+
const statusSteps = statusStepsForRunnerStep(step, stepIndex);
|
|
267
310
|
input.status.steps ??= [];
|
|
268
311
|
input.status.steps.push(...statusSteps);
|
|
269
312
|
if (isParallelGroup(step)) {
|
|
@@ -53,6 +53,13 @@ export interface StopRequest {
|
|
|
53
53
|
reason?: string;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
export interface CheckpointDecisionRequest {
|
|
57
|
+
type: "approve-checkpoint" | "reject-checkpoint";
|
|
58
|
+
ts?: number;
|
|
59
|
+
source?: string;
|
|
60
|
+
reason?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
56
63
|
export interface SteerRequest {
|
|
57
64
|
type: "steer";
|
|
58
65
|
id: string;
|
|
@@ -110,6 +117,14 @@ export function stopRequestPath(asyncDir: string): string {
|
|
|
110
117
|
return path.join(controlInboxDir(asyncDir), "stop.json");
|
|
111
118
|
}
|
|
112
119
|
|
|
120
|
+
export function approveCheckpointRequestPath(asyncDir: string): string {
|
|
121
|
+
return path.join(controlInboxDir(asyncDir), "approve-checkpoint.json");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function rejectCheckpointRequestPath(asyncDir: string): string {
|
|
125
|
+
return path.join(controlInboxDir(asyncDir), "reject-checkpoint.json");
|
|
126
|
+
}
|
|
127
|
+
|
|
113
128
|
/** Directory of parent-to-runner steering requests. */
|
|
114
129
|
export function steerRequestsDir(asyncDir: string): string {
|
|
115
130
|
return path.join(controlInboxDir(asyncDir), STEER_REQUESTS_DIR);
|
|
@@ -254,6 +269,18 @@ export function requestAsyncStop(
|
|
|
254
269
|
return requestPath;
|
|
255
270
|
}
|
|
256
271
|
|
|
272
|
+
export function requestAsyncCheckpointDecision(
|
|
273
|
+
asyncDir: string,
|
|
274
|
+
type: CheckpointDecisionRequest["type"],
|
|
275
|
+
payload: Omit<CheckpointDecisionRequest, "type"> = {},
|
|
276
|
+
deps: { now?: () => number } = {},
|
|
277
|
+
): string {
|
|
278
|
+
const requestPath = type === "approve-checkpoint" ? approveCheckpointRequestPath(asyncDir) : rejectCheckpointRequestPath(asyncDir);
|
|
279
|
+
const request: CheckpointDecisionRequest = { ...payload, ts: payload.ts ?? deps.now?.() ?? Date.now(), type };
|
|
280
|
+
writeAtomicJson(requestPath, request);
|
|
281
|
+
return requestPath;
|
|
282
|
+
}
|
|
283
|
+
|
|
257
284
|
export function requestAsyncSteer(
|
|
258
285
|
asyncDir: string,
|
|
259
286
|
payload: { message: string; targetIndex?: number; targetIndexes?: number[]; source?: string; id?: string; ts?: number },
|
|
@@ -380,8 +407,15 @@ function parseSteerRequest(raw: unknown): SteerRequest | undefined {
|
|
|
380
407
|
|
|
381
408
|
export function consumeSteerRequestsFromDir(dir: string, fsImpl: Pick<typeof fs, "existsSync" | "rmSync" | "readdirSync" | "readFileSync"> = fs): SteerRequest[] {
|
|
382
409
|
if (!fsImpl.existsSync(dir)) return [];
|
|
410
|
+
let entries: string[];
|
|
411
|
+
try {
|
|
412
|
+
entries = fsImpl.readdirSync(dir).filter((name) => name.endsWith(".json")).sort();
|
|
413
|
+
} catch {
|
|
414
|
+
// Leave requests in place so the periodic poll can retry the scan.
|
|
415
|
+
return [];
|
|
416
|
+
}
|
|
383
417
|
const requests: SteerRequest[] = [];
|
|
384
|
-
for (const entry of
|
|
418
|
+
for (const entry of entries) {
|
|
385
419
|
const requestPath = path.join(dir, entry);
|
|
386
420
|
let parsed: SteerRequest | undefined;
|
|
387
421
|
try {
|
|
@@ -450,6 +484,21 @@ export function consumeStopRequest(
|
|
|
450
484
|
return true;
|
|
451
485
|
}
|
|
452
486
|
|
|
487
|
+
export function consumeCheckpointDecisionRequest(
|
|
488
|
+
asyncDir: string,
|
|
489
|
+
fsImpl: Pick<typeof fs, "existsSync" | "rmSync"> = fs,
|
|
490
|
+
): "approved" | "rejected" | undefined {
|
|
491
|
+
if (fsImpl.existsSync(rejectCheckpointRequestPath(asyncDir))) {
|
|
492
|
+
try { fsImpl.rmSync(rejectCheckpointRequestPath(asyncDir), { force: true, recursive: true }); } catch {}
|
|
493
|
+
return "rejected";
|
|
494
|
+
}
|
|
495
|
+
if (fsImpl.existsSync(approveCheckpointRequestPath(asyncDir))) {
|
|
496
|
+
try { fsImpl.rmSync(approveCheckpointRequestPath(asyncDir), { force: true, recursive: true }); } catch {}
|
|
497
|
+
return "approved";
|
|
498
|
+
}
|
|
499
|
+
return undefined;
|
|
500
|
+
}
|
|
501
|
+
|
|
453
502
|
/**
|
|
454
503
|
* Parent side: portable interrupt = authoritative file request + best-effort OS
|
|
455
504
|
* signal. The signal is only a latency optimization on Unix; ENOSYS on Windows
|
|
@@ -506,6 +555,21 @@ export function deliverStopRequest(input: {
|
|
|
506
555
|
requestAsyncStop(input.asyncDir, input.source ? { source: input.source } : {}, { now: input.now });
|
|
507
556
|
}
|
|
508
557
|
|
|
558
|
+
export function deliverCheckpointDecisionRequest(input: {
|
|
559
|
+
asyncDir: string;
|
|
560
|
+
decision: "approved" | "rejected";
|
|
561
|
+
now?: () => number;
|
|
562
|
+
source?: string;
|
|
563
|
+
reason?: string;
|
|
564
|
+
}): void {
|
|
565
|
+
requestAsyncCheckpointDecision(
|
|
566
|
+
input.asyncDir,
|
|
567
|
+
input.decision === "approved" ? "approve-checkpoint" : "reject-checkpoint",
|
|
568
|
+
{ ...(input.source ? { source: input.source } : {}), ...(input.reason ? { reason: input.reason } : {}) },
|
|
569
|
+
{ now: input.now },
|
|
570
|
+
);
|
|
571
|
+
}
|
|
572
|
+
|
|
509
573
|
/**
|
|
510
574
|
* Runner side: watch the control inbox and route interrupt requests into
|
|
511
575
|
* `onInterrupt`. Uses `fs.watch` when available plus an interval poll as a
|
|
@@ -519,6 +583,7 @@ export function watchAsyncControlInbox(
|
|
|
519
583
|
onTimeout?: () => void;
|
|
520
584
|
onStop?: () => void;
|
|
521
585
|
onSteer?: (request: SteerRequest) => void;
|
|
586
|
+
onCheckpointDecision?: (decision: "approved" | "rejected") => void;
|
|
522
587
|
onSteerCapability?: (capability: SteerCapability) => void;
|
|
523
588
|
onSteerAck?: (ack: SteerAck) => void;
|
|
524
589
|
pollIntervalMs?: number;
|
|
@@ -542,6 +607,8 @@ export function watchAsyncControlInbox(
|
|
|
542
607
|
if (consumeStopRequest(asyncDir, fsImpl)) opts.onStop?.();
|
|
543
608
|
if (consumeTimeoutRequest(asyncDir, fsImpl)) opts.onTimeout?.();
|
|
544
609
|
if (consumeInterruptRequest(asyncDir, fsImpl)) opts.onInterrupt();
|
|
610
|
+
const checkpointDecision = consumeCheckpointDecisionRequest(asyncDir, fsImpl);
|
|
611
|
+
if (checkpointDecision) opts.onCheckpointDecision?.(checkpointDecision);
|
|
545
612
|
for (const request of consumeSteerRequests(asyncDir, fsImpl)) opts.onSteer?.(request);
|
|
546
613
|
for (const capability of consumeSteerCapabilities(asyncDir, fsImpl)) opts.onSteerCapability?.(capability);
|
|
547
614
|
for (const ack of consumeSteerAcks(asyncDir, fsImpl)) opts.onSteerAck?.(ack);
|
|
@@ -15,10 +15,11 @@ import {
|
|
|
15
15
|
resolveCompletionBatchConfig,
|
|
16
16
|
} from "./completion-batcher.ts";
|
|
17
17
|
import { SUBAGENT_ASYNC_COMPLETE_EVENT, SUBAGENT_FOREGROUND_COMPLETE_EVENT, type ParallelHandoffReference, type SubagentState } from "../../shared/types.ts";
|
|
18
|
+
import { isUnexplainedProcessSignal } from "../shared/process-signal.ts";
|
|
18
19
|
|
|
19
20
|
export interface SubagentNotifyDetails {
|
|
20
21
|
agent: string;
|
|
21
|
-
status: "completed" | "failed" | "paused";
|
|
22
|
+
status: "completed" | "failed" | "paused" | "stopped";
|
|
22
23
|
source?: "async" | "foreground";
|
|
23
24
|
taskInfo?: string;
|
|
24
25
|
resultPreview: string;
|
|
@@ -37,6 +38,21 @@ export interface CompletionNotification {
|
|
|
37
38
|
summary?: string;
|
|
38
39
|
exitCode?: number;
|
|
39
40
|
state?: string;
|
|
41
|
+
processSignal?: string | null;
|
|
42
|
+
interrupted?: boolean;
|
|
43
|
+
timedOut?: boolean;
|
|
44
|
+
stopped?: boolean;
|
|
45
|
+
turnBudgetExceeded?: boolean;
|
|
46
|
+
results?: Array<{
|
|
47
|
+
status?: string;
|
|
48
|
+
success?: boolean;
|
|
49
|
+
exitCode?: number | null;
|
|
50
|
+
processSignal?: string | null;
|
|
51
|
+
interrupted?: boolean;
|
|
52
|
+
timedOut?: boolean;
|
|
53
|
+
stopped?: boolean;
|
|
54
|
+
turnBudgetExceeded?: boolean;
|
|
55
|
+
}>;
|
|
40
56
|
timestamp?: number;
|
|
41
57
|
durationMs?: number;
|
|
42
58
|
cwd?: string;
|
|
@@ -48,6 +64,8 @@ export interface CompletionNotification {
|
|
|
48
64
|
totalTasks?: number;
|
|
49
65
|
sessionId?: string | null;
|
|
50
66
|
triggerTurn?: boolean;
|
|
67
|
+
/** True when an acknowledged grouped intercom relay already delivered this run. */
|
|
68
|
+
intercomDelivered?: boolean;
|
|
51
69
|
parallelHandoff?: ParallelHandoffReference;
|
|
52
70
|
}
|
|
53
71
|
|
|
@@ -90,7 +108,7 @@ export function formatSingleCompletion(details: SubagentNotifyDetails): string {
|
|
|
90
108
|
|
|
91
109
|
export function parseSubagentNotifyContent(content: string): SubagentNotifyDetails | undefined {
|
|
92
110
|
const lines = content.split("\n");
|
|
93
|
-
const match = (lines[0] ?? "").match(/^(Background task|Detached foreground task) (completed|failed|paused): \*\*(.+?)\*\*(?:\s+(\([^)]*\)))?$/);
|
|
111
|
+
const match = (lines[0] ?? "").match(/^(Background task|Detached foreground task) (completed|failed|paused|stopped): \*\*(.+?)\*\*(?:\s+(\([^)]*\)))?$/);
|
|
94
112
|
if (!match) return undefined;
|
|
95
113
|
const body = lines.slice(2);
|
|
96
114
|
let sessionIndex = -1;
|
|
@@ -174,15 +192,47 @@ function completionBatchKey(result: CompletionNotification): string {
|
|
|
174
192
|
return cwd ? `cwd:${cwd}` : "unknown";
|
|
175
193
|
}
|
|
176
194
|
|
|
195
|
+
/**
|
|
196
|
+
* Reference-first notification preview: compact child summaries built by the
|
|
197
|
+
* result watcher from each child's authoritative output path/log. Never uses
|
|
198
|
+
* `results[].output` or the run-level `summary` as parent-facing text; the run
|
|
199
|
+
* summary remains only a defensive fallback for legacy payloads without results.
|
|
200
|
+
*/
|
|
201
|
+
function buildReferenceFirstPreview(result: CompletionNotification): string {
|
|
202
|
+
const children = Array.isArray(result.results) ? result.results : [];
|
|
203
|
+
if (children.length === 0) {
|
|
204
|
+
return typeof result.summary === "string" ? result.summary : "";
|
|
205
|
+
}
|
|
206
|
+
if (children.length === 1) {
|
|
207
|
+
const childSummary = typeof children[0]?.summary === "string" && children[0].summary.trim()
|
|
208
|
+
? children[0].summary
|
|
209
|
+
: "(no output)";
|
|
210
|
+
return childSummary;
|
|
211
|
+
}
|
|
212
|
+
return children.map((child, index) => {
|
|
213
|
+
const agentLabel = typeof child?.agent === "string" ? child.agent : `step-${index + 1}`;
|
|
214
|
+
const childSummary = typeof child?.summary === "string" && child.summary.trim()
|
|
215
|
+
? child.summary
|
|
216
|
+
: "(no output)";
|
|
217
|
+
return `${index + 1}. ${agentLabel}\n${childSummary}`;
|
|
218
|
+
}).join("\n\n");
|
|
219
|
+
}
|
|
220
|
+
|
|
177
221
|
export function buildCompletionDetails(result: CompletionNotification): SubagentNotifyDetails {
|
|
178
222
|
const agent = result.agent ?? "unknown";
|
|
179
223
|
const summary = typeof result.summary === "string" ? result.summary : "";
|
|
180
|
-
const
|
|
224
|
+
const stopped = result.stopped === true
|
|
225
|
+
|| result.state === "stopped"
|
|
226
|
+
|| (result.success !== true && result.exitCode !== 0 && isUnexplainedProcessSignal(result))
|
|
227
|
+
|| result.results?.some((child) => child.stopped === true
|
|
228
|
+
|| child.status === "stopped"
|
|
229
|
+
|| (child.success !== true && child.exitCode !== 0 && isUnexplainedProcessSignal(child))) === true;
|
|
230
|
+
const paused = !stopped && !result.success && (
|
|
181
231
|
result.exitCode === 0
|
|
182
232
|
|| result.state === "paused"
|
|
183
233
|
|| summary.startsWith("Paused after interrupt.")
|
|
184
234
|
);
|
|
185
|
-
const status = paused ? "paused" : result.success ? "completed" : "failed";
|
|
235
|
+
const status = stopped ? "stopped" : paused ? "paused" : result.success ? "completed" : "failed";
|
|
186
236
|
const taskInfo =
|
|
187
237
|
result.taskIndex !== undefined && result.totalTasks !== undefined
|
|
188
238
|
? ` (${result.taskIndex + 1}/${result.totalTasks})`
|
|
@@ -205,7 +255,7 @@ export function buildCompletionDetails(result: CompletionNotification): Subagent
|
|
|
205
255
|
status,
|
|
206
256
|
...(result.source ? { source: result.source } : {}),
|
|
207
257
|
...(taskInfo ? { taskInfo } : {}),
|
|
208
|
-
resultPreview:
|
|
258
|
+
resultPreview: buildReferenceFirstPreview(result),
|
|
209
259
|
...(typeof result.durationMs === "number" ? { durationMs: result.durationMs } : {}),
|
|
210
260
|
...(handoffPath ? { handoffPath } : {}),
|
|
211
261
|
...(session ? { sessionLabel: session.label, sessionValue: session.value } : {}),
|
|
@@ -250,6 +300,7 @@ export default function registerSubagentNotify(
|
|
|
250
300
|
|
|
251
301
|
const deliver = (result: CompletionNotification): Promise<boolean> => {
|
|
252
302
|
if (disposed || typeof result.sessionId !== "string" || result.sessionId !== state.currentSessionId) return Promise.resolve(false);
|
|
303
|
+
if (result.intercomDelivered === true) return Promise.resolve(true);
|
|
253
304
|
const key = buildCompletionKey(result, "notify");
|
|
254
305
|
const seenAt = seen.get(key);
|
|
255
306
|
if (seenAt !== undefined && now() - seenAt <= ttlMs) return Promise.resolve(true);
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type NestedRunSummary,
|
|
9
9
|
type ParallelHandoffReference,
|
|
10
10
|
type SubagentResultIntercomChild,
|
|
11
|
+
type SubagentOutputState,
|
|
11
12
|
type SubagentState,
|
|
12
13
|
} from "../../shared/types.ts";
|
|
13
14
|
import {
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
} from "../../intercom/result-intercom.ts";
|
|
20
21
|
import { projectNestedRegistryForRoot, sanitizeSummary } from "../shared/nested-events.ts";
|
|
21
22
|
import { resolveWatchPath } from "../../shared/utils.ts";
|
|
23
|
+
import { formatBoundedPersistenceFallback, formatSavedOutputReference } from "../shared/single-output.ts";
|
|
22
24
|
import type { CompletionNotifier, CompletionNotification } from "./notify.ts";
|
|
23
25
|
|
|
24
26
|
const WATCHER_RESTART_DELAY_MS = 3000;
|
|
@@ -38,15 +40,26 @@ type ResultWatcherDeps = {
|
|
|
38
40
|
fs?: ResultWatcherFs;
|
|
39
41
|
timers?: ResultWatcherTimers;
|
|
40
42
|
notifier?: Pick<CompletionNotifier, "deliver">;
|
|
43
|
+
/** External grouped-result transport. Disable when native completion notifications own delivery. */
|
|
44
|
+
deliverIntercomResults?: boolean;
|
|
41
45
|
};
|
|
42
46
|
|
|
43
47
|
type ResultFileChild = {
|
|
44
48
|
agent?: string;
|
|
45
49
|
output?: string;
|
|
50
|
+
/** Authoritative durable output location: explicit/generated saved path or async output log. */
|
|
51
|
+
outputPath?: string;
|
|
52
|
+
savedOutputPath?: string;
|
|
53
|
+
outputState?: SubagentOutputState;
|
|
46
54
|
error?: string;
|
|
47
55
|
success?: boolean;
|
|
48
56
|
state?: string;
|
|
57
|
+
exitCode?: number | null;
|
|
58
|
+
interrupted?: boolean;
|
|
59
|
+
timedOut?: boolean;
|
|
49
60
|
stopped?: boolean;
|
|
61
|
+
turnBudgetExceeded?: boolean;
|
|
62
|
+
processSignal?: string | null;
|
|
50
63
|
sessionFile?: string;
|
|
51
64
|
artifactPaths?: { outputPath?: string };
|
|
52
65
|
intercomTarget?: string;
|
|
@@ -84,6 +97,57 @@ function isNotFound(error: unknown): boolean {
|
|
|
84
97
|
return errorCode(error) === "ENOENT";
|
|
85
98
|
}
|
|
86
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Resolve the authoritative output location for one async child: explicit/generated
|
|
102
|
+
* saved output path first, else the persisted per-child output log
|
|
103
|
+
* (`asyncDir/output-<flat-index>.log`). Legacy result files without saved-path
|
|
104
|
+
* fields fall back to the output log when the run directory is known.
|
|
105
|
+
*/
|
|
106
|
+
function childOutputPath(data: ResultFileData, result: ResultFileChild | undefined, index: number): string | undefined {
|
|
107
|
+
if (typeof result?.outputPath === "string" && result.outputPath.trim()) return result.outputPath;
|
|
108
|
+
if (typeof result?.savedOutputPath === "string" && result.savedOutputPath.trim()) return result.savedOutputPath;
|
|
109
|
+
if (typeof data.asyncDir === "string" && data.asyncDir.trim()) return path.join(data.asyncDir, `output-${index}.log`);
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Reference-only child summary for async delivery: the saved-output reference
|
|
115
|
+
* (or output-log reference) plus process error/status for failed children. Never
|
|
116
|
+
* emits `results[].output` or the run `summary` as parent-facing text; when the
|
|
117
|
+
* output location is absent or unreadable, a bounded fallback is used instead.
|
|
118
|
+
*/
|
|
119
|
+
function buildChildReferenceSummary(fsApi: ResultWatcherFs, input: {
|
|
120
|
+
outputPath: string | undefined;
|
|
121
|
+
outputText: string;
|
|
122
|
+
exitCode: number;
|
|
123
|
+
error?: string;
|
|
124
|
+
}): string {
|
|
125
|
+
const statusError = input.exitCode !== 0 && input.error ? input.error : undefined;
|
|
126
|
+
if (input.outputPath) {
|
|
127
|
+
try {
|
|
128
|
+
const content = fsApi.readFileSync(input.outputPath, "utf-8");
|
|
129
|
+
const ref = formatSavedOutputReference(input.outputPath, content);
|
|
130
|
+
return statusError ? `${statusError}\n\n${ref.message}` : ref.message;
|
|
131
|
+
} catch (error) {
|
|
132
|
+
const fallback = formatBoundedPersistenceFallback({
|
|
133
|
+
error: `Failed to read saved output: ${error instanceof Error ? error.message : String(error)}`,
|
|
134
|
+
outputPath: input.outputPath,
|
|
135
|
+
fullOutput: input.outputText,
|
|
136
|
+
exitCode: input.exitCode,
|
|
137
|
+
processError: input.error,
|
|
138
|
+
});
|
|
139
|
+
return statusError ? `${statusError}\n\n${fallback}` : fallback;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const fallback = formatBoundedPersistenceFallback({
|
|
143
|
+
error: "No saved output path is available for this child.",
|
|
144
|
+
fullOutput: input.outputText,
|
|
145
|
+
exitCode: input.exitCode,
|
|
146
|
+
processError: input.error,
|
|
147
|
+
});
|
|
148
|
+
return statusError ? `${statusError}\n\n${fallback}` : fallback;
|
|
149
|
+
}
|
|
150
|
+
|
|
87
151
|
function shouldPoll(error: unknown): boolean {
|
|
88
152
|
const code = errorCode(error);
|
|
89
153
|
return code === "EMFILE" || code === "ENOSPC";
|
|
@@ -108,6 +172,7 @@ export function createResultWatcher(
|
|
|
108
172
|
const fsApi = deps.fs ?? fs;
|
|
109
173
|
const timers = deps.timers ?? { setTimeout, clearTimeout, setInterval, clearInterval };
|
|
110
174
|
const notifier = deps.notifier ?? { deliver: async () => true };
|
|
175
|
+
const deliverIntercomResults = deps.deliverIntercomResults !== false;
|
|
111
176
|
const pendingTriggerTurn = new Map<string, boolean>();
|
|
112
177
|
const processing = new Set<string>();
|
|
113
178
|
let deliveryActive = true;
|
|
@@ -171,14 +236,16 @@ export function createResultWatcher(
|
|
|
171
236
|
const hasResultChildren = Array.isArray(data.results) && data.results.length > 0;
|
|
172
237
|
const resultChildren: ResultFileChild[] = hasResultChildren
|
|
173
238
|
? data.results!
|
|
174
|
-
: [{ agent: data.agent ?? undefined,
|
|
239
|
+
: [{ agent: data.agent ?? undefined, outputPath: childOutputPath(data, undefined, 0), outputState: "unknown", success: data.success }];
|
|
175
240
|
const normalizedChildren = attachNestedChildrenToResultChildren(runId, resultChildren.map((result = {}, index): SubagentResultIntercomChild => {
|
|
176
|
-
const baseOutput = result.output ?? data.summary;
|
|
177
|
-
const
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
:
|
|
241
|
+
const baseOutput = hasResultChildren ? result.output : result.output ?? data.summary;
|
|
242
|
+
const outputText = typeof baseOutput === "string" ? baseOutput : "";
|
|
243
|
+
const summary = buildChildReferenceSummary(fsApi, {
|
|
244
|
+
outputPath: childOutputPath(data, result, index),
|
|
245
|
+
outputText,
|
|
246
|
+
exitCode: result.success === false ? 1 : result.success === true ? 0 : typeof result.exitCode === "number" ? result.exitCode : data.success === false ? 1 : 0,
|
|
247
|
+
error: result.error ?? (result.success === false ? (typeof data.error === "string" ? data.error : undefined) : undefined),
|
|
248
|
+
});
|
|
182
249
|
const sessionPath = result.sessionFile ?? (resultChildren.length === 1 ? data.sessionFile : undefined);
|
|
183
250
|
const childNestedChildren = sanitizeNestedResultChildren(result.children, resultPath, `results[${index}].children`);
|
|
184
251
|
const childState = result.state === "paused" || result.state === "stopped"
|
|
@@ -190,7 +257,18 @@ export function createResultWatcher(
|
|
|
190
257
|
: undefined;
|
|
191
258
|
return {
|
|
192
259
|
agent: result.agent ?? data.agent ?? `step-${index + 1}`,
|
|
193
|
-
status: resolveSubagentResultStatus({
|
|
260
|
+
status: resolveSubagentResultStatus({
|
|
261
|
+
success: result.success,
|
|
262
|
+
state: childState,
|
|
263
|
+
interrupted: result.interrupted,
|
|
264
|
+
timedOut: result.timedOut,
|
|
265
|
+
stopped: result.stopped,
|
|
266
|
+
turnBudgetExceeded: result.turnBudgetExceeded,
|
|
267
|
+
processSignal: result.processSignal,
|
|
268
|
+
}),
|
|
269
|
+
outputState: result.outputState === "present" || result.outputState === "absent" || result.outputState === "unknown"
|
|
270
|
+
? result.outputState
|
|
271
|
+
: "unknown",
|
|
194
272
|
summary,
|
|
195
273
|
index,
|
|
196
274
|
artifactPath: result.artifactPaths?.outputPath,
|
|
@@ -201,11 +279,12 @@ export function createResultWatcher(
|
|
|
201
279
|
}), nestedChildren);
|
|
202
280
|
|
|
203
281
|
const intercomTarget = data.intercomTarget?.trim();
|
|
204
|
-
|
|
282
|
+
let intercomDelivered = false;
|
|
283
|
+
if (deliverIntercomResults && intercomTarget && triggerTurn) {
|
|
205
284
|
const mode = data.mode === "single" || data.mode === "parallel" || data.mode === "chain"
|
|
206
285
|
? data.mode
|
|
207
286
|
: resultChildren.length > 1 ? "chain" : "single";
|
|
208
|
-
|
|
287
|
+
intercomDelivered = await deliverSubagentResultIntercomEvent(pi.events, buildSubagentResultIntercomPayload({
|
|
209
288
|
to: intercomTarget,
|
|
210
289
|
runId,
|
|
211
290
|
mode,
|
|
@@ -216,7 +295,7 @@ export function createResultWatcher(
|
|
|
216
295
|
...(data.parallelHandoff ? { parallelHandoff: data.parallelHandoff } : {}),
|
|
217
296
|
}));
|
|
218
297
|
if (!ownsSession(data.sessionId, epoch)) return;
|
|
219
|
-
if (!
|
|
298
|
+
if (!intercomDelivered) console.error(`Subagent async grouped result intercom delivery was not acknowledged for '${resultPath}'.`);
|
|
220
299
|
}
|
|
221
300
|
|
|
222
301
|
const accepted = await notifier.deliver({
|
|
@@ -224,6 +303,7 @@ export function createResultWatcher(
|
|
|
224
303
|
id: data.id ?? runId,
|
|
225
304
|
runId,
|
|
226
305
|
triggerTurn,
|
|
306
|
+
intercomDelivered,
|
|
227
307
|
...(nestedChildren?.length ? { nestedChildren } : {}),
|
|
228
308
|
...(Array.isArray(data.results) ? {
|
|
229
309
|
results: hasResultChildren ? normalizedChildren.map((child, index) => ({
|
|
@@ -249,6 +329,7 @@ export function createResultWatcher(
|
|
|
249
329
|
...data,
|
|
250
330
|
runId,
|
|
251
331
|
triggerTurn,
|
|
332
|
+
intercomDelivered,
|
|
252
333
|
...(nestedChildren?.length ? { nestedChildren } : {}),
|
|
253
334
|
...(Array.isArray(data.results) ? {
|
|
254
335
|
results: hasResultChildren ? normalizedChildren.map((child, index) => ({
|
|
@@ -8,6 +8,7 @@ import { formatModelThinking } from "../../shared/formatters.ts";
|
|
|
8
8
|
import { formatActivityLabel } from "../../shared/status-format.ts";
|
|
9
9
|
import { ASYNC_DIR, RESULTS_DIR, type AsyncStatus, type Details, type ForegroundResumeRun, type NestedRunSummary, type SteeringStatus, type SubagentState } from "../../shared/types.ts";
|
|
10
10
|
import { resolveSubagentIntercomTarget } from "../../intercom/intercom-bridge.ts";
|
|
11
|
+
import { resolveSubagentResultStatus } from "../../intercom/result-intercom.ts";
|
|
11
12
|
import { readProcessTerminal, sanitizeProcessTerminal } from "./process-terminal.ts";
|
|
12
13
|
import { resolveAsyncRunLocation } from "./async-resume.ts";
|
|
13
14
|
import { resolveSubagentRunId } from "./run-id-resolver.ts";
|
|
@@ -39,6 +40,11 @@ function hasExistingSessionFile(value: unknown): value is string {
|
|
|
39
40
|
return typeof value === "string" && fs.existsSync(value);
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
function formatCheckpointGuidance(runId: string | undefined, checkpoint: AsyncStatus["checkpoint"] | undefined): string | undefined {
|
|
44
|
+
if (!runId || !checkpoint || checkpoint.status !== "pending") return undefined;
|
|
45
|
+
return `Checkpoint: ${checkpoint.name}${checkpoint.message ? ` — ${checkpoint.message}` : ""}\nApprove: subagent({ action: "approve-checkpoint", id: "${runId}" })\nReject: subagent({ action: "reject-checkpoint", id: "${runId}" })`;
|
|
46
|
+
}
|
|
47
|
+
|
|
42
48
|
function formatResumeGuidance(runId: string | undefined, children: Array<{ agent?: unknown; sessionFile?: unknown }>, fallbackSessionFile?: unknown, options: { stopped?: boolean } = {}): string {
|
|
43
49
|
if (options.stopped) return "Resume: unavailable; stopped runs are not resumable. Start a new run instead.";
|
|
44
50
|
const knownChildren = children
|
|
@@ -126,6 +132,12 @@ function formatRememberedForegroundStatus(run: ForegroundResumeRun): string {
|
|
|
126
132
|
else lines.push(`Transcript: subagent({ action: "status", id: "${run.runId}", index: 0, view: "transcript" })`);
|
|
127
133
|
const detached = run.children.some((child) => child.status === "detached");
|
|
128
134
|
const resumable = run.children.find((child) => hasExistingSessionFile(child.sessionFile));
|
|
135
|
+
if (run.checkpoint?.status === "pending") {
|
|
136
|
+
lines.push(`Checkpoint: ${run.checkpoint.name}${run.checkpoint.message ? ` — ${run.checkpoint.message}` : ""}`);
|
|
137
|
+
lines.push(`Approve: subagent({ action: "approve-checkpoint", id: "${run.runId}" })`);
|
|
138
|
+
lines.push(`Reject: subagent({ action: "reject-checkpoint", id: "${run.runId}" })`);
|
|
139
|
+
return lines.join("\n");
|
|
140
|
+
}
|
|
129
141
|
if (detached) {
|
|
130
142
|
lines.push(`Recovery: reply to the supervisor request first, then wait with subagent_wait({ id: "${run.runId}" }); do not resume or launch a replacement while any child remains detached.`);
|
|
131
143
|
} else if (resumable) {
|
|
@@ -403,7 +415,7 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
|
|
|
403
415
|
if (status.sessionFile) lines.push(`Session: ${status.sessionFile}`);
|
|
404
416
|
if (status.state === "running") lines.push(`Steer running child: subagent({ action: "steer", id: "${status.runId}", message: "..." })`);
|
|
405
417
|
if (status.state !== "running") {
|
|
406
|
-
lines.push(formatResumeGuidance(status.runId, status.steps ?? [], status.sessionFile, { stopped: status.state === "stopped" || status.stopped === true }));
|
|
418
|
+
lines.push(formatCheckpointGuidance(status.runId, status.checkpoint) ?? formatResumeGuidance(status.runId, status.steps ?? [], status.sessionFile, { stopped: status.state === "stopped" || status.stopped === true }));
|
|
407
419
|
}
|
|
408
420
|
if (fs.existsSync(logPath)) lines.push(`Log: ${logPath}`);
|
|
409
421
|
if (fs.existsSync(eventsPath)) lines.push(`Events: ${eventsPath}`);
|
|
@@ -415,7 +427,7 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
|
|
|
415
427
|
if (resultPath) {
|
|
416
428
|
try {
|
|
417
429
|
const raw = fs.readFileSync(resultPath, "utf-8");
|
|
418
|
-
const data = JSON.parse(raw) as { id?: string; runId?: string; agent?: string; success?: boolean; summary?: string; output?: string; exitCode?: number; state?: string; sessionFile?: string; parallelHandoff?: { path?: string }; results?: Array<{ agent?: string; output?: string; summary?: string; sessionFile?: string; state?: string; success?: boolean; exitCode?: number | null }> };
|
|
430
|
+
const data = JSON.parse(raw) as { id?: string; runId?: string; agent?: string; success?: boolean; summary?: string; output?: string; exitCode?: number; state?: string; stopped?: boolean; timedOut?: boolean; turnBudgetExceeded?: boolean; processSignal?: string | null; sessionFile?: string; parallelHandoff?: { path?: string }; results?: Array<{ agent?: string; output?: string; summary?: string; sessionFile?: string; state?: string; success?: boolean; exitCode?: number | null; stopped?: boolean; timedOut?: boolean; turnBudgetExceeded?: boolean; interrupted?: boolean; processSignal?: string | null }> };
|
|
419
431
|
if (params.view === "transcript") {
|
|
420
432
|
try {
|
|
421
433
|
return { content: [{ type: "text", text: formatAsyncResultTranscript(data, resultPath, { index: params.index, lines: params.lines }) }], details: { mode: "single", results: [] } };
|
|
@@ -424,7 +436,21 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
|
|
|
424
436
|
return { content: [{ type: "text", text: message }], isError: true, details: { mode: "single", results: [] } };
|
|
425
437
|
}
|
|
426
438
|
}
|
|
427
|
-
const
|
|
439
|
+
const childStatuses = Array.isArray(data.results)
|
|
440
|
+
? data.results.map((child) => resolveSubagentResultStatus({
|
|
441
|
+
success: child.success,
|
|
442
|
+
state: child.state,
|
|
443
|
+
interrupted: child.interrupted,
|
|
444
|
+
timedOut: child.timedOut,
|
|
445
|
+
stopped: child.stopped,
|
|
446
|
+
turnBudgetExceeded: child.turnBudgetExceeded,
|
|
447
|
+
processSignal: child.processSignal,
|
|
448
|
+
exitCode: typeof child.exitCode === "number" ? child.exitCode : undefined,
|
|
449
|
+
}))
|
|
450
|
+
: [];
|
|
451
|
+
const status = data.state === "stopped" || data.stopped === true || childStatuses.includes("stopped")
|
|
452
|
+
? "stopped"
|
|
453
|
+
: data.success ? "complete" : data.state === "paused" || data.exitCode === 0 ? "paused" : "failed";
|
|
428
454
|
const runId = data.runId ?? data.id ?? resolvedId;
|
|
429
455
|
const lines = [`Run: ${runId}`, `State: ${status}`, `Result: ${resultPath}`];
|
|
430
456
|
if (data.parallelHandoff?.path) lines.push(`Parallel handoff: ${data.parallelHandoff.path}`);
|