@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
|
@@ -57,6 +57,7 @@ export function defaultInheritSkills(): boolean {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
export interface BuiltinAgentOverrideBase {
|
|
60
|
+
description?: string;
|
|
60
61
|
model?: string;
|
|
61
62
|
fallbackModels?: string[];
|
|
62
63
|
thinking?: string | false;
|
|
@@ -78,6 +79,7 @@ export interface BuiltinAgentOverrideBase {
|
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
interface BuiltinAgentOverrideConfig {
|
|
82
|
+
description?: string;
|
|
81
83
|
model?: string | false;
|
|
82
84
|
fallbackModels?: string[] | false;
|
|
83
85
|
thinking?: string | false;
|
|
@@ -114,6 +116,7 @@ export interface AgentConfig {
|
|
|
114
116
|
localName?: string;
|
|
115
117
|
packageName?: string;
|
|
116
118
|
description: string;
|
|
119
|
+
aliases?: string[];
|
|
117
120
|
tools?: string[];
|
|
118
121
|
mcpDirectTools?: string[];
|
|
119
122
|
model?: string;
|
|
@@ -150,6 +153,8 @@ export interface AgentConfig {
|
|
|
150
153
|
modelSource?: AgentModelSourceInfo;
|
|
151
154
|
}
|
|
152
155
|
|
|
156
|
+
type ProjectRootResolution = "nearest" | "git-root";
|
|
157
|
+
|
|
153
158
|
interface SubagentSettings {
|
|
154
159
|
overrides: Record<string, BuiltinAgentOverrideConfig>;
|
|
155
160
|
defaultModel?: string;
|
|
@@ -420,7 +425,7 @@ function collectSettingsPackageRoots(settingsFile: string, baseDir: string): str
|
|
|
420
425
|
|
|
421
426
|
function collectPackageSubagentPaths(cwd: string, options: { includeUser: boolean; includeProject: boolean } = { includeUser: true, includeProject: true }): PackageSubagentPaths {
|
|
422
427
|
const agentDir = getAgentDir();
|
|
423
|
-
const projectRoot =
|
|
428
|
+
const projectRoot = findConfiguredProjectRoot(cwd) ?? cwd;
|
|
424
429
|
const packageRoots = [
|
|
425
430
|
projectRoot,
|
|
426
431
|
];
|
|
@@ -469,6 +474,41 @@ function collectPackageSubagentPaths(cwd: string, options: { includeUser: boolea
|
|
|
469
474
|
return { agents, chains };
|
|
470
475
|
}
|
|
471
476
|
|
|
477
|
+
function normalizeAgentAliases(rawAliases: string[] | undefined, agentName: string): string[] | undefined {
|
|
478
|
+
const aliases = [...new Set((rawAliases ?? []).map((alias) => alias.trim()).filter(Boolean))]
|
|
479
|
+
.filter((alias) => alias !== agentName);
|
|
480
|
+
return aliases.length > 0 ? aliases : undefined;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function effectiveAgentMatch(matches: AgentConfig[]): { agent?: AgentConfig; error?: string } {
|
|
484
|
+
const distinctNames = [...new Set(matches.map((agent) => agent.name))];
|
|
485
|
+
if (distinctNames.length === 1) {
|
|
486
|
+
const sourceRank = new Map<AgentConfig["source"], number>([["builtin", 0], ["package", 1], ["user", 2], ["project", 3]]);
|
|
487
|
+
return { agent: [...matches].sort((a, b) => (sourceRank.get(b.source) ?? 0) - (sourceRank.get(a.source) ?? 0))[0] };
|
|
488
|
+
}
|
|
489
|
+
return {};
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
export function resolveAgentName(name: string, agents: AgentConfig[]): { agent?: AgentConfig; error?: string } {
|
|
493
|
+
const raw = name.trim();
|
|
494
|
+
const exact = agents.filter((agent) => agent.name === raw || agent.localName === raw);
|
|
495
|
+
if (exact.length === 1) return { agent: exact[0] };
|
|
496
|
+
if (exact.length > 1) {
|
|
497
|
+
const effective = effectiveAgentMatch(exact);
|
|
498
|
+
if (effective.agent) return effective;
|
|
499
|
+
return { error: `Ambiguous agent name '${name}': ${exact.map((agent) => agent.name).join(", ")}` };
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const aliases = agents.filter((agent) => agent.aliases?.includes(raw));
|
|
503
|
+
if (aliases.length === 1) return { agent: aliases[0] };
|
|
504
|
+
if (aliases.length > 1) {
|
|
505
|
+
const effective = effectiveAgentMatch(aliases);
|
|
506
|
+
if (effective.agent) return effective;
|
|
507
|
+
return { error: `Ambiguous agent alias '${name}': ${aliases.map((agent) => agent.name).join(", ")}` };
|
|
508
|
+
}
|
|
509
|
+
return {};
|
|
510
|
+
}
|
|
511
|
+
|
|
472
512
|
function splitToolList(rawTools: string[] | undefined): { tools?: string[]; mcpDirectTools?: string[] } {
|
|
473
513
|
const mcpDirectTools: string[] = [];
|
|
474
514
|
const tools: string[] = [];
|
|
@@ -505,6 +545,7 @@ function arraysEqual(a: string[] | undefined, b: string[] | undefined): boolean
|
|
|
505
545
|
|
|
506
546
|
function cloneOverrideBase(agent: AgentConfig): BuiltinAgentOverrideBase {
|
|
507
547
|
return {
|
|
548
|
+
description: agent.description,
|
|
508
549
|
model: agent.model,
|
|
509
550
|
fallbackModels: agent.fallbackModels ? [...agent.fallbackModels] : undefined,
|
|
510
551
|
thinking: agent.thinking,
|
|
@@ -528,6 +569,7 @@ function cloneOverrideBase(agent: AgentConfig): BuiltinAgentOverrideBase {
|
|
|
528
569
|
|
|
529
570
|
function cloneOverrideValue(override: BuiltinAgentOverrideConfig): BuiltinAgentOverrideConfig {
|
|
530
571
|
return {
|
|
572
|
+
...(override.description !== undefined ? { description: override.description } : {}),
|
|
531
573
|
...(override.model !== undefined ? { model: override.model } : {}),
|
|
532
574
|
...(override.fallbackModels !== undefined
|
|
533
575
|
? { fallbackModels: override.fallbackModels === false ? false : [...override.fallbackModels] }
|
|
@@ -549,12 +591,26 @@ function cloneOverrideValue(override: BuiltinAgentOverrideConfig): BuiltinAgentO
|
|
|
549
591
|
};
|
|
550
592
|
}
|
|
551
593
|
|
|
552
|
-
|
|
594
|
+
function isProjectRootCandidate(dir: string): boolean {
|
|
595
|
+
return isDirectory(getProjectConfigDir(dir)) || isDirectory(path.join(dir, ".agents"));
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
function findProjectRootCandidates(cwd: string): string[] {
|
|
599
|
+
const roots: string[] = [];
|
|
553
600
|
let currentDir = cwd;
|
|
554
601
|
while (true) {
|
|
555
|
-
if (
|
|
556
|
-
|
|
557
|
-
|
|
602
|
+
if (isProjectRootCandidate(currentDir)) roots.push(currentDir);
|
|
603
|
+
|
|
604
|
+
const parentDir = path.dirname(currentDir);
|
|
605
|
+
if (parentDir === currentDir) return roots;
|
|
606
|
+
currentDir = parentDir;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function findNearestGitRoot(cwd: string): string | null {
|
|
611
|
+
let currentDir = cwd;
|
|
612
|
+
while (true) {
|
|
613
|
+
if (fs.existsSync(path.join(currentDir, ".git"))) return currentDir;
|
|
558
614
|
|
|
559
615
|
const parentDir = path.dirname(currentDir);
|
|
560
616
|
if (parentDir === currentDir) return null;
|
|
@@ -562,12 +618,46 @@ export function findNearestProjectRoot(cwd: string): string | null {
|
|
|
562
618
|
}
|
|
563
619
|
}
|
|
564
620
|
|
|
621
|
+
function readProjectRootResolution(projectRoot: string): ProjectRootResolution | undefined {
|
|
622
|
+
const settingsPath = path.join(getProjectConfigDir(projectRoot), "settings.json");
|
|
623
|
+
if (!fs.existsSync(settingsPath)) return undefined;
|
|
624
|
+
const settings = readSettingsFileStrict(settingsPath);
|
|
625
|
+
const subagents = settings.subagents;
|
|
626
|
+
if (!subagents || typeof subagents !== "object" || Array.isArray(subagents)) return undefined;
|
|
627
|
+
|
|
628
|
+
const value = (subagents as Record<string, unknown>).projectRootResolution;
|
|
629
|
+
if (value === undefined) return undefined;
|
|
630
|
+
if (value === "nearest" || value === "git-root") return value;
|
|
631
|
+
throw new Error(`Subagent settings in '${settingsPath}' have invalid 'projectRootResolution'; expected 'nearest' or 'git-root'.`);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
export function findNearestProjectRoot(cwd: string): string | null {
|
|
635
|
+
return findProjectRootCandidates(cwd)[0] ?? null;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function findConfiguredProjectRoot(cwd: string): string | null {
|
|
639
|
+
const candidates = findProjectRootCandidates(cwd);
|
|
640
|
+
const nearestRoot = candidates[0];
|
|
641
|
+
if (!nearestRoot) return null;
|
|
642
|
+
|
|
643
|
+
const nearestMode = readProjectRootResolution(nearestRoot);
|
|
644
|
+
if (nearestMode === "nearest") return nearestRoot;
|
|
645
|
+
|
|
646
|
+
const gitRoot = findNearestGitRoot(cwd);
|
|
647
|
+
const gitProjectRoot = gitRoot ? candidates.find((candidate) => path.resolve(candidate) === path.resolve(gitRoot)) : undefined;
|
|
648
|
+
if (gitProjectRoot && (nearestMode === "git-root" || readProjectRootResolution(gitProjectRoot) === "git-root")) {
|
|
649
|
+
return gitProjectRoot;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
return nearestRoot;
|
|
653
|
+
}
|
|
654
|
+
|
|
565
655
|
function getUserAgentSettingsPath(): string {
|
|
566
656
|
return path.join(getAgentDir(), "settings.json");
|
|
567
657
|
}
|
|
568
658
|
|
|
569
659
|
function getProjectAgentSettingsPath(cwd: string): string | null {
|
|
570
|
-
const projectRoot =
|
|
660
|
+
const projectRoot = findConfiguredProjectRoot(cwd);
|
|
571
661
|
return projectRoot ? path.join(getProjectConfigDir(projectRoot), "settings.json") : null;
|
|
572
662
|
}
|
|
573
663
|
|
|
@@ -632,6 +722,14 @@ function parseBuiltinOverrideEntry(
|
|
|
632
722
|
const input = value as Record<string, unknown>;
|
|
633
723
|
const override: BuiltinAgentOverrideConfig = {};
|
|
634
724
|
|
|
725
|
+
if ("description" in input) {
|
|
726
|
+
if (typeof input.description === "string" && input.description.trim()) {
|
|
727
|
+
override.description = input.description.trim();
|
|
728
|
+
} else {
|
|
729
|
+
throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'description'; expected a non-empty string.`);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
635
733
|
if ("model" in input) {
|
|
636
734
|
if (typeof input.model === "string" || input.model === false) override.model = input.model;
|
|
637
735
|
else throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'model'; expected a string or false.`);
|
|
@@ -879,6 +977,7 @@ function applyBuiltinOverride(
|
|
|
879
977
|
override: { ...meta, base: cloneOverrideBase(agent) },
|
|
880
978
|
};
|
|
881
979
|
|
|
980
|
+
if (override.description !== undefined) next.description = override.description;
|
|
882
981
|
if (override.model !== undefined) next.model = override.model === false ? undefined : override.model;
|
|
883
982
|
if (override.fallbackModels !== undefined) {
|
|
884
983
|
next.fallbackModels = override.fallbackModels === false ? undefined : [...override.fallbackModels];
|
|
@@ -999,6 +1098,10 @@ function applyCustomAgentOverride(
|
|
|
999
1098
|
anyFilled = true;
|
|
1000
1099
|
};
|
|
1001
1100
|
|
|
1101
|
+
if (override.description !== undefined) {
|
|
1102
|
+
mutable().description = override.description;
|
|
1103
|
+
anyFilled = true;
|
|
1104
|
+
}
|
|
1002
1105
|
if (override.model !== undefined) {
|
|
1003
1106
|
fill("model", ["model"], override.model === false ? undefined : override.model);
|
|
1004
1107
|
}
|
|
@@ -1089,10 +1192,14 @@ function applyCustomAgentOverrides(
|
|
|
1089
1192
|
|
|
1090
1193
|
export function buildBuiltinOverrideConfig(
|
|
1091
1194
|
base: BuiltinAgentOverrideBase,
|
|
1092
|
-
draft: Pick<AgentConfig, "model" | "fallbackModels" | "thinking" | "systemPromptMode" | "inheritProjectContext" | "inheritSkills" | "defaultContext" | "acceptanceRole" | "disabled" | "systemPrompt" | "skills" | "tools" | "mcpDirectTools" | "extensions" | "subagentOnlyExtensions" | "completionGuard" | "toolBudget"
|
|
1195
|
+
draft: Pick<AgentConfig, "model" | "fallbackModels" | "thinking" | "systemPromptMode" | "inheritProjectContext" | "inheritSkills" | "defaultContext" | "acceptanceRole" | "disabled" | "systemPrompt" | "skills" | "tools" | "mcpDirectTools" | "extensions" | "subagentOnlyExtensions" | "completionGuard" | "toolBudget"> & Partial<Pick<AgentConfig, "description">>,
|
|
1093
1196
|
): BuiltinAgentOverrideConfig | undefined {
|
|
1094
1197
|
const override: BuiltinAgentOverrideConfig = {};
|
|
1095
1198
|
|
|
1199
|
+
if (draft.description !== undefined) {
|
|
1200
|
+
const description = draft.description.trim();
|
|
1201
|
+
if (description && description !== base.description) override.description = description;
|
|
1202
|
+
}
|
|
1096
1203
|
if (draft.model !== base.model) override.model = draft.model ?? false;
|
|
1097
1204
|
if (!arraysEqual(draft.fallbackModels, base.fallbackModels)) override.fallbackModels = draft.fallbackModels ? [...draft.fallbackModels] : false;
|
|
1098
1205
|
if (draft.thinking !== base.thinking) override.thinking = draft.thinking ?? false;
|
|
@@ -1333,6 +1440,7 @@ function loadAgentsFromDir(dir: string, source: AgentSource): AgentConfig[] {
|
|
|
1333
1440
|
const tools = parsedTools.tools ?? [];
|
|
1334
1441
|
const mcpDirectTools = parsedTools.mcpDirectTools ?? [];
|
|
1335
1442
|
const defaultReads = parseFrontmatterList(frontmatter.defaultReads);
|
|
1443
|
+
const aliases = normalizeAgentAliases(parseFrontmatterList(frontmatter.aliases ?? frontmatter.alias), runtimeName);
|
|
1336
1444
|
const skillStr = frontmatter.skill || frontmatter.skills;
|
|
1337
1445
|
const skills = parseFrontmatterList(skillStr);
|
|
1338
1446
|
const skillPath = parseFrontmatterList(frontmatter.skillPath);
|
|
@@ -1413,6 +1521,7 @@ function loadAgentsFromDir(dir: string, source: AgentSource): AgentConfig[] {
|
|
|
1413
1521
|
localName,
|
|
1414
1522
|
packageName,
|
|
1415
1523
|
description: frontmatter.description,
|
|
1524
|
+
aliases,
|
|
1416
1525
|
tools: rawTools !== undefined ? tools : undefined,
|
|
1417
1526
|
mcpDirectTools: mcpDirectTools.length > 0 ? mcpDirectTools : undefined,
|
|
1418
1527
|
model: frontmatter.model,
|
|
@@ -1489,7 +1598,7 @@ function isDirectory(p: string): boolean {
|
|
|
1489
1598
|
}
|
|
1490
1599
|
|
|
1491
1600
|
function resolveNearestProjectAgentDirs(cwd: string): { readDirs: string[]; preferredDir: string | null } {
|
|
1492
|
-
const projectRoot =
|
|
1601
|
+
const projectRoot = findConfiguredProjectRoot(cwd);
|
|
1493
1602
|
if (!projectRoot) return { readDirs: [], preferredDir: null };
|
|
1494
1603
|
|
|
1495
1604
|
const legacyDir = path.join(projectRoot, ".agents");
|
|
@@ -1505,7 +1614,7 @@ function resolveNearestProjectAgentDirs(cwd: string): { readDirs: string[]; pref
|
|
|
1505
1614
|
}
|
|
1506
1615
|
|
|
1507
1616
|
function resolveNearestProjectChainDirs(cwd: string): { readDirs: string[]; preferredDir: string | null } {
|
|
1508
|
-
const projectRoot =
|
|
1617
|
+
const projectRoot = findConfiguredProjectRoot(cwd);
|
|
1509
1618
|
if (!projectRoot) return { readDirs: [], preferredDir: null };
|
|
1510
1619
|
|
|
1511
1620
|
const preferredDir = path.join(getProjectConfigDir(projectRoot), "chains");
|
|
@@ -379,19 +379,21 @@ function chooseHigherPrioritySkill(existing: CachedSkillEntry | undefined, candi
|
|
|
379
379
|
return candidate.order < existing.order ? candidate : existing;
|
|
380
380
|
}
|
|
381
381
|
|
|
382
|
-
function
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
382
|
+
function parseSkillDescription(content: string): string | undefined {
|
|
383
|
+
const normalized = content.replace(/\r\n/g, "\n");
|
|
384
|
+
if (!normalized.startsWith("---")) return undefined;
|
|
385
|
+
|
|
386
|
+
const endIndex = normalized.indexOf("\n---", 3);
|
|
387
|
+
if (endIndex === -1) return undefined;
|
|
387
388
|
|
|
388
|
-
|
|
389
|
-
|
|
389
|
+
const frontmatter = normalized.slice(3, endIndex).trim();
|
|
390
|
+
const match = frontmatter.match(/^description:\s*(.+)$/m);
|
|
391
|
+
return match?.[1]?.trim().replace(/^['\"]|['\"]$/g, "");
|
|
392
|
+
}
|
|
390
393
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
return match[1]?.trim().replace(/^['\"]|['\"]$/g, "");
|
|
394
|
+
function maybeReadSkillDescription(filePath: string): string | undefined {
|
|
395
|
+
try {
|
|
396
|
+
return parseSkillDescription(fs.readFileSync(filePath, "utf-8"));
|
|
395
397
|
} catch {
|
|
396
398
|
// Description parsing is best-effort metadata extraction.
|
|
397
399
|
return undefined;
|
|
@@ -585,7 +587,7 @@ function readSkill(
|
|
|
585
587
|
|
|
586
588
|
const raw = fs.readFileSync(skillPath, "utf-8");
|
|
587
589
|
const content = stripSkillFrontmatter(raw);
|
|
588
|
-
const description =
|
|
590
|
+
const description = parseSkillDescription(raw);
|
|
589
591
|
const skill: ResolvedSkill = {
|
|
590
592
|
name: skillName,
|
|
591
593
|
path: skillPath,
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure task-aware advisory agent recommender.
|
|
3
|
+
*
|
|
4
|
+
* Given an already-discovered, effective agent set and the current capability
|
|
5
|
+
* ceiling, deterministically selects at most one canonical agent to recommend
|
|
6
|
+
* for a task, or returns recovery guidance when intent is unknown or no safe
|
|
7
|
+
* candidate exists. This module is deliberately pure: it performs no
|
|
8
|
+
* filesystem/discovery access, no alias/chains/executor/RPC/preflight/settings
|
|
9
|
+
* access, no mutation, launch, scheduling, or persistence, and never writes to
|
|
10
|
+
* params. Launching stays explicit: the caller must make a separate execution
|
|
11
|
+
* call with the recommended canonical `agent.name`.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { AgentConfig, AgentSource } from "./agents.ts";
|
|
15
|
+
import { agentHasWriteTools } from "./agent-memory.ts";
|
|
16
|
+
import { classifyTaskMutationIntent, resolveAgentRoutingRole } from "../runs/shared/task-intent.ts";
|
|
17
|
+
import { isAgentAllowedByCapabilityCeiling, type ResolvedSubagentCapabilityCeiling } from "../runs/shared/capability-ceiling.ts";
|
|
18
|
+
|
|
19
|
+
/** Core tools that make an implementation agent able to write (matches agent-memory). */
|
|
20
|
+
const WRITER_TOOLS = new Set(["edit", "write", "bash"]);
|
|
21
|
+
|
|
22
|
+
/** Source precedence for deterministic ordering: project > user > package > builtin. */
|
|
23
|
+
const AGENT_SOURCE_PRECEDENCE: Record<AgentSource, number> = { builtin: 0, package: 1, user: 2, project: 3 };
|
|
24
|
+
|
|
25
|
+
export type TaskAwareAgentIntent = "implementation" | "read-only" | "unknown";
|
|
26
|
+
|
|
27
|
+
export interface TaskAwareAgentRecommendationAgent {
|
|
28
|
+
/** Canonical runtime agent name accepted by execution; never an alias. */
|
|
29
|
+
name: string;
|
|
30
|
+
source: AgentSource;
|
|
31
|
+
role: "writer" | "read-only";
|
|
32
|
+
roleBasis: "declared" | "inferred";
|
|
33
|
+
reason: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface TaskAwareAgentRecommendation {
|
|
37
|
+
intent: TaskAwareAgentIntent;
|
|
38
|
+
agent?: TaskAwareAgentRecommendationAgent;
|
|
39
|
+
/** Recovery guidance when no safe recommendation exists (unknown intent or no candidate). */
|
|
40
|
+
next?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Recommend a canonical agent for a trimmed non-empty task, or `undefined` for
|
|
45
|
+
* an empty/whitespace task (no-op). Never recommends disabled or
|
|
46
|
+
* capability-disallowed agents, never guesses for unknown intent, and never
|
|
47
|
+
* outputs an alias or a role-incompatible agent.
|
|
48
|
+
*/
|
|
49
|
+
export function recommendTaskAwareAgent(input: {
|
|
50
|
+
task: string;
|
|
51
|
+
agents: AgentConfig[];
|
|
52
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
53
|
+
}): TaskAwareAgentRecommendation | undefined {
|
|
54
|
+
const task = input.task.trim();
|
|
55
|
+
if (!task) return undefined;
|
|
56
|
+
|
|
57
|
+
const intent = classifyTaskMutationIntent("builder", task).kind;
|
|
58
|
+
if (intent === "unknown") {
|
|
59
|
+
return {
|
|
60
|
+
intent,
|
|
61
|
+
next: "Clarify whether the task is read-only analysis/review or implementation allowed to edit files.",
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const candidates: TaskAwareAgentRecommendationAgent[] = [];
|
|
66
|
+
for (const agent of input.agents) {
|
|
67
|
+
if (agent.disabled) continue;
|
|
68
|
+
if (!isAgentAllowedByCapabilityCeiling(agent.name, input.capabilityCeiling)) continue;
|
|
69
|
+
const role = resolveAgentRoutingRole(agent.name, agent.acceptanceRole);
|
|
70
|
+
if (!role) continue;
|
|
71
|
+
const hasWriteTools = agentHasWriteTools(agent);
|
|
72
|
+
if (intent === "implementation") {
|
|
73
|
+
if (role !== "writer" || !hasWriteTools) continue;
|
|
74
|
+
const allowedTools = input.capabilityCeiling?.allowedTools;
|
|
75
|
+
if (allowedTools !== undefined && !allowedTools.some((tool) => WRITER_TOOLS.has(tool))) continue;
|
|
76
|
+
} else {
|
|
77
|
+
if (role !== "read-only" || hasWriteTools) continue;
|
|
78
|
+
}
|
|
79
|
+
const roleBasis = agent.acceptanceRole !== undefined ? "declared" : "inferred";
|
|
80
|
+
candidates.push({
|
|
81
|
+
name: agent.name,
|
|
82
|
+
source: agent.source,
|
|
83
|
+
role,
|
|
84
|
+
roleBasis,
|
|
85
|
+
reason: intent === "implementation"
|
|
86
|
+
? `${roleBasis} writer role with write tools`
|
|
87
|
+
: `${roleBasis} read-only role without known write tools`,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
candidates.sort((a, b) => {
|
|
92
|
+
const sourceOrder = AGENT_SOURCE_PRECEDENCE[b.source] - AGENT_SOURCE_PRECEDENCE[a.source];
|
|
93
|
+
if (sourceOrder !== 0) return sourceOrder;
|
|
94
|
+
const declaredOrder = (a.roleBasis === "declared" ? 0 : 1) - (b.roleBasis === "declared" ? 0 : 1);
|
|
95
|
+
if (declaredOrder !== 0) return declaredOrder;
|
|
96
|
+
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const agent = candidates[0];
|
|
100
|
+
if (!agent) {
|
|
101
|
+
return {
|
|
102
|
+
intent,
|
|
103
|
+
next: intent === "implementation"
|
|
104
|
+
? "No executable agent has a writer role with write tools for this task. Check the executable/restricted sections above or adjust the capability ceiling."
|
|
105
|
+
: "No executable agent has a read-only role without known write tools for this task. Check the executable/restricted sections above.",
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return { intent, agent };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Text-only advisory lines; `undefined` (empty-task no-op) renders nothing. */
|
|
112
|
+
export function formatTaskAwareAgentRecommendation(recommendation: TaskAwareAgentRecommendation | undefined): string[] {
|
|
113
|
+
if (!recommendation) return [];
|
|
114
|
+
const lines = ["Task-aware advisory routing:", `- Intent: ${recommendation.intent}`];
|
|
115
|
+
if (recommendation.agent) {
|
|
116
|
+
lines.push(`- Recommended: ${recommendation.agent.name} (${recommendation.agent.source})`);
|
|
117
|
+
lines.push(`- Reason: ${recommendation.agent.reason}`);
|
|
118
|
+
lines.push("- Advisory only: no subagent was launched. To proceed, explicitly call subagent with this canonical agent name and the task.");
|
|
119
|
+
} else {
|
|
120
|
+
lines.push("- Recommendation: none");
|
|
121
|
+
lines.push(`- Next: ${recommendation.next ?? "Refine the task wording and retry."}`);
|
|
122
|
+
lines.push("- Advisory only: no subagent was launched.");
|
|
123
|
+
}
|
|
124
|
+
return lines;
|
|
125
|
+
}
|
|
@@ -131,6 +131,7 @@ export interface SubagentDelegationStarted {
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
export interface SubagentDelegationUpdate extends SubagentDelegationStarted {
|
|
134
|
+
runId?: string;
|
|
134
135
|
currentTool?: string;
|
|
135
136
|
currentToolArgs?: string;
|
|
136
137
|
recentOutput?: string;
|
|
@@ -230,6 +231,7 @@ export interface SubagentDelegationV2Started {
|
|
|
230
231
|
}
|
|
231
232
|
|
|
232
233
|
export interface SubagentDelegationV2Update extends SubagentDelegationV2Started {
|
|
234
|
+
runId?: string;
|
|
233
235
|
currentTool?: string;
|
|
234
236
|
currentToolArgs?: string;
|
|
235
237
|
recentOutput?: string;
|
|
@@ -266,6 +268,7 @@ export interface SubagentDelegationV2TerminalResponse extends SubagentDelegation
|
|
|
266
268
|
model?: string;
|
|
267
269
|
thinking?: string;
|
|
268
270
|
exitCode?: number;
|
|
271
|
+
launchContractDigest?: string;
|
|
269
272
|
result?: SubagentDelegationV2Value;
|
|
270
273
|
usage?: SubagentDelegationV2Usage;
|
|
271
274
|
}
|
|
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { discoverAgents, discoverAgentsAll, type AgentConfig, type AgentScope, type AgentSource } from "../agents/agents.ts";
|
|
5
|
+
import { discoverAgents, discoverAgentsAll, resolveAgentName, type AgentConfig, type AgentScope, type AgentSource } from "../agents/agents.ts";
|
|
6
6
|
import { resolveExecutionAgentScope } from "../agents/agent-scope.ts";
|
|
7
7
|
import { buildSkillInjection, normalizeSkillInput, resolveSkillsWithFallback } from "../agents/skills.ts";
|
|
8
8
|
import { buildAgentMemoryInjection } from "../agents/agent-memory.ts";
|
|
@@ -12,7 +12,7 @@ import { injectOutputPathSystemPrompt, normalizeSingleOutputOverride, resolveSin
|
|
|
12
12
|
import { getArtifactPaths, getArtifactsDir } from "../shared/artifacts.ts";
|
|
13
13
|
import { resolveEffectiveThinking } from "../shared/model-info.ts";
|
|
14
14
|
import { SUBAGENT_LIFECYCLE_ARTIFACT_VERSION, type ArtifactDirPreference, type ArtifactPaths, type JsonSchemaObject, type OutputMode } from "../shared/types.ts";
|
|
15
|
-
import type
|
|
15
|
+
import { capabilityCeilingAgentRestrictionMessage, intersectSubagentCapabilityCeilings, type ResolvedSubagentCapabilityCeiling, type SubagentCapabilityAudit } from "../runs/shared/capability-ceiling.ts";
|
|
16
16
|
import { appendTurnBudgetSystemPrompt } from "../runs/shared/turn-budget.ts";
|
|
17
17
|
import type { ResolvedTurnBudget } from "../shared/types.ts";
|
|
18
18
|
import type { ResolvedMcpDirectToolSelection } from "../runs/shared/mcp-direct-tool-allowlist.ts";
|
|
@@ -31,7 +31,8 @@ export type SubagentLaunchContractReasonCode =
|
|
|
31
31
|
| "denied_required_tool"
|
|
32
32
|
| "invalid_artifact_dir"
|
|
33
33
|
| "invalid_cwd"
|
|
34
|
-
| "unsupported_mode"
|
|
34
|
+
| "unsupported_mode"
|
|
35
|
+
| "restricted_agent";
|
|
35
36
|
|
|
36
37
|
export interface SubagentLaunchContractDiagnostic {
|
|
37
38
|
code: SubagentLaunchContractReasonCode | "host_required" | "snapshot_warning";
|
|
@@ -191,7 +192,7 @@ function normalizeAvailableModels(models: SubagentLaunchContractInput["available
|
|
|
191
192
|
function candidateList(inputAgent: string, selected: AgentConfig | undefined, cwd: string): SubagentLaunchContractAgentCandidate[] {
|
|
192
193
|
const all = discoverAgentsAll(cwd);
|
|
193
194
|
return [...all.builtin, ...all.package, ...all.user, ...all.project]
|
|
194
|
-
.filter((agent) =>
|
|
195
|
+
.filter((agent) => Boolean(resolveAgentName(inputAgent, [agent]).agent))
|
|
195
196
|
.map((agent) => ({
|
|
196
197
|
name: agent.name,
|
|
197
198
|
...(agent.localName ? { localName: agent.localName } : {}),
|
|
@@ -225,14 +226,17 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
|
|
|
225
226
|
}
|
|
226
227
|
const scope = resolveExecutionAgentScope(input.agentScope);
|
|
227
228
|
const discovered = discoverAgents(effectiveCwd, scope);
|
|
228
|
-
const
|
|
229
|
-
if (
|
|
230
|
-
return { ok: false, code: "
|
|
229
|
+
const resolvedAgent = resolveAgentName(input.agent, discovered.agents);
|
|
230
|
+
if (resolvedAgent.error) {
|
|
231
|
+
return { ok: false, code: "ambiguous_agent", message: resolvedAgent.error, diagnostics };
|
|
231
232
|
}
|
|
232
|
-
if (
|
|
233
|
-
return { ok: false, code: "
|
|
233
|
+
if (!resolvedAgent.agent) {
|
|
234
|
+
return { ok: false, code: "missing_agent", message: `Unknown agent: ${input.agent}`, diagnostics };
|
|
234
235
|
}
|
|
235
|
-
const agent =
|
|
236
|
+
const agent = resolvedAgent.agent;
|
|
237
|
+
const effectiveCapabilityCeiling = intersectSubagentCapabilityCeilings(input.capabilityCeiling, input.inheritedCapabilityCeiling);
|
|
238
|
+
const restrictionMessage = capabilityCeilingAgentRestrictionMessage(agent.name, effectiveCapabilityCeiling);
|
|
239
|
+
if (restrictionMessage) return { ok: false, code: "restricted_agent", message: restrictionMessage, diagnostics };
|
|
236
240
|
const runId = input.runId ?? "preflight";
|
|
237
241
|
const skillInput = normalizeSkillInput(input.skill);
|
|
238
242
|
const outputOverride = normalizeSingleOutputOverride(input.output, agent.output);
|
|
@@ -272,15 +276,15 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
|
|
|
272
276
|
cwd: effectiveCwd,
|
|
273
277
|
requireReadTool: resolvedSkills.resolved.length > 0,
|
|
274
278
|
structuredOutput: Boolean(input.outputSchema),
|
|
275
|
-
capabilityCeiling:
|
|
276
|
-
|
|
279
|
+
capabilityCeiling: effectiveCapabilityCeiling,
|
|
280
|
+
agentName: agent.name,
|
|
277
281
|
});
|
|
278
282
|
} catch (error) {
|
|
279
283
|
const message = error instanceof Error ? error.message : String(error);
|
|
280
284
|
diagnostics.push({ code: "denied_required_tool", severity: "error", message });
|
|
281
285
|
return { ok: false, code: "denied_required_tool", message, diagnostics };
|
|
282
286
|
}
|
|
283
|
-
const artifactsEnabled = input.artifacts
|
|
287
|
+
const artifactsEnabled = input.artifacts === true;
|
|
284
288
|
const artifactsDir = artifactsEnabled ? getArtifactsDir(input.parentSessionFile ?? null, effectiveCwd, input.artifactDir ?? "project") : undefined;
|
|
285
289
|
const artifactPaths = artifactsDir ? getArtifactPaths(artifactsDir, runId, agent.name, 0) : undefined;
|
|
286
290
|
const outputPath = resolveSingleOutputPath(behavior.output, effectiveCwd, effectiveCwd, artifactsDir ? path.join(artifactsDir, "outputs", runId) : undefined);
|
|
@@ -43,7 +43,7 @@ export const EXPAND_KEYS = allowedKeysOf(DynamicExpandSchema);
|
|
|
43
43
|
export const EXPAND_FROM_KEYS = allowedKeysOf(ExpandFromSchema);
|
|
44
44
|
export const COLLECT_KEYS = allowedKeysOf(DynamicCollectSchema);
|
|
45
45
|
|
|
46
|
-
const CHAIN_STEP_EXAMPLE = '{"agent": "builder", "task": "do X"}';
|
|
46
|
+
const CHAIN_STEP_EXAMPLE = '{"agent": "builder", "task": "do X"} or {"checkpoint": "review", "message": "Approve before implementation?"}';
|
|
47
47
|
const PARALLEL_TASK_EXAMPLE = '{"agent": "builder", "task": "do X"}';
|
|
48
48
|
const DYNAMIC_TEMPLATE_EXAMPLE = '{"agent": "builder", "task": "Review {item.path}"}';
|
|
49
49
|
const EXPAND_EXAMPLE = '{"from": {"output": "targets", "path": "/items"}, "maxItems": 4}';
|
|
@@ -58,6 +58,21 @@ function disallowedKeys(value: Record<string, unknown>, allowed: readonly string
|
|
|
58
58
|
return Object.keys(value).filter((key) => !allowed.includes(key));
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
function validateCheckpointShape(step: Record<string, unknown>, path: string): void {
|
|
62
|
+
if (step.checkpoint === undefined) return;
|
|
63
|
+
const allowed = new Set(["checkpoint", "message", "phase", "label"]);
|
|
64
|
+
const mixed = Object.keys(step).filter((key) => !allowed.has(key));
|
|
65
|
+
if (mixed.length > 0) {
|
|
66
|
+
throw new Error(`subagent ${path}: checkpoint steps cannot be mixed with child-launch properties ${quoteList(mixed)}.\n\nExample: {"checkpoint": "review", "message": "Approve before implementation?"}`);
|
|
67
|
+
}
|
|
68
|
+
if (typeof step.checkpoint !== "string" || !step.checkpoint.trim()) {
|
|
69
|
+
throw new Error(`subagent ${path}.checkpoint: expected a non-empty string.`);
|
|
70
|
+
}
|
|
71
|
+
if (step.message !== undefined && typeof step.message !== "string") {
|
|
72
|
+
throw new Error(`subagent ${path}.message: expected a string.`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
61
76
|
function typeName(value: unknown): string {
|
|
62
77
|
if (Array.isArray(value)) return "array";
|
|
63
78
|
return typeof value;
|
|
@@ -120,6 +135,7 @@ export function validateChainInput(args: unknown): void {
|
|
|
120
135
|
const stepPath = `chain step validation failed at chain[${index}]`;
|
|
121
136
|
expectObject(step, stepPath, CHAIN_STEP_KEYS, CHAIN_STEP_EXAMPLE);
|
|
122
137
|
checkNoExtraKeys(step, stepPath, ChainItem, CHAIN_STEP_KEYS, CHAIN_STEP_EXAMPLE);
|
|
138
|
+
validateCheckpointShape(step, stepPath);
|
|
123
139
|
|
|
124
140
|
if (step.expand !== undefined) {
|
|
125
141
|
const expandPath = `${stepPath}.expand`;
|
|
@@ -27,7 +27,7 @@ import { resolveCurrentSessionId } from "../shared/session-identity.ts";
|
|
|
27
27
|
import { cleanupOldChainDirs } from "../shared/settings.ts";
|
|
28
28
|
import { clearLegacyResultAnimationTimer, renderSubagentResult } from "../tui/render.ts";
|
|
29
29
|
import { openSubagentFleet } from "../tui/fleet.ts";
|
|
30
|
-
import { SubagentFleetStatus } from "../tui/fleet-status.ts";
|
|
30
|
+
import { SubagentFleetStatus, resolveFleetViewPlacement } from "../tui/fleet-status.ts";
|
|
31
31
|
import { SubagentParams } from "./schemas.ts";
|
|
32
32
|
import { validateChainInput } from "./chain-validation.ts";
|
|
33
33
|
import { createSubagentExecutor, type SubagentParamsLike } from "../runs/foreground/subagent-executor.ts";
|
|
@@ -51,7 +51,7 @@ import { SUBAGENT_CHILD_ENV, SUBAGENT_PARENT_SESSION_ENV } from "../runs/shared/
|
|
|
51
51
|
import { resolveCurrentSubagentCapabilityCeiling } from "../runs/shared/capability-ceiling.ts";
|
|
52
52
|
import { formatDuration, shortenPath } from "../shared/formatters.ts";
|
|
53
53
|
import { loadConfig } from "./config.ts";
|
|
54
|
-
import { buildSubagentToolDescription } from "./tool-description.ts";
|
|
54
|
+
import { buildSubagentToolDescription, SUBAGENT_PARENT_ROUTING_GUIDANCE } from "./tool-description.ts";
|
|
55
55
|
import {
|
|
56
56
|
type Details,
|
|
57
57
|
type SubagentState,
|
|
@@ -144,10 +144,13 @@ function createSlashResultComponent(
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
class SubagentControlNoticeComponent implements Component {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
) {
|
|
147
|
+
private readonly details: SubagentControlMessageDetails;
|
|
148
|
+
private readonly theme: ExtensionContext["ui"]["theme"];
|
|
149
|
+
|
|
150
|
+
constructor(details: SubagentControlMessageDetails, theme: ExtensionContext["ui"]["theme"]) {
|
|
151
|
+
this.details = details;
|
|
152
|
+
this.theme = theme;
|
|
153
|
+
}
|
|
151
154
|
|
|
152
155
|
invalidate(): void {}
|
|
153
156
|
|
|
@@ -194,6 +197,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
194
197
|
const waitToolConfig = resolveWaitToolConfig(config.waitTool);
|
|
195
198
|
const asyncByDefault = config.asyncByDefault === true;
|
|
196
199
|
const fleetViewEnabled = config.fleetView !== false;
|
|
200
|
+
const fleetViewPlacement = resolveFleetViewPlacement(config.fleetViewPlacement);
|
|
197
201
|
const asyncWidgetEnabled = config.asyncWidget === true || (!fleetViewEnabled && config.asyncWidget !== false);
|
|
198
202
|
const tempArtifactsDir = getArtifactsDir(null);
|
|
199
203
|
cleanupAllArtifactDirs(DEFAULT_ARTIFACT_CONFIG.cleanupDays);
|
|
@@ -237,14 +241,17 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
237
241
|
const ctx = state.lastUiContext;
|
|
238
242
|
if (!ctx?.hasUI) return;
|
|
239
243
|
await openSubagentFleet(ctx, state, { initialKey: itemKey });
|
|
240
|
-
})
|
|
244
|
+
}, { placement: fleetViewPlacement })
|
|
241
245
|
: undefined;
|
|
242
246
|
const { startResultWatcher, primeExistingResults, stopResultWatcher } = createResultWatcher(
|
|
243
247
|
pi,
|
|
244
248
|
state,
|
|
245
249
|
RESULTS_DIR,
|
|
246
250
|
10 * 60 * 1000,
|
|
247
|
-
{
|
|
251
|
+
{
|
|
252
|
+
notifier: completionNotifier,
|
|
253
|
+
deliverIntercomResults: config.intercomBridge?.resultDelivery !== false,
|
|
254
|
+
},
|
|
248
255
|
);
|
|
249
256
|
|
|
250
257
|
const runtimeCleanup = () => {
|
|
@@ -383,6 +390,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
383
390
|
events: pi.events,
|
|
384
391
|
getContext: () => state.lastUiContext,
|
|
385
392
|
execute: (id, params, signal, onUpdate, ctx) => executor.execute(id, params, signal, onUpdate, ctx),
|
|
393
|
+
state,
|
|
386
394
|
});
|
|
387
395
|
|
|
388
396
|
function effectiveParallelTaskCount(tasks: Array<{ count?: unknown }> | undefined): number {
|
|
@@ -397,6 +405,10 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
397
405
|
name: "subagent",
|
|
398
406
|
label: "Subagent",
|
|
399
407
|
description: buildSubagentToolDescription(config),
|
|
408
|
+
// Always-visible active-tool path: rendered into the main system prompt by
|
|
409
|
+
// buildSystemPrompt (default and custom-prompt branches). Parent-only; the
|
|
410
|
+
// child-safe fanout registration must not receive this guidance.
|
|
411
|
+
promptGuidelines: [SUBAGENT_PARENT_ROUTING_GUIDANCE],
|
|
400
412
|
parameters: SubagentParams,
|
|
401
413
|
|
|
402
414
|
prepareArguments(args) {
|
|
@@ -551,6 +563,8 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
551
563
|
state.lastUiContext = ctx;
|
|
552
564
|
cleanupSessionArtifacts(ctx);
|
|
553
565
|
clearPendingForegroundControlNotices(state);
|
|
566
|
+
state.foregroundControls.clear();
|
|
567
|
+
state.lastForegroundControlId = null;
|
|
554
568
|
resetJobs(ctx);
|
|
555
569
|
restoreActiveJobs(ctx);
|
|
556
570
|
scheduledRunManager.bindSession(ctx);
|