@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
|
@@ -1,22 +1,24 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import * as fs from "node:fs";
|
|
2
3
|
import * as os from "node:os";
|
|
3
4
|
import * as path from "node:path";
|
|
4
|
-
import "../../shared/env.ts";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { encodeNestedPathEnv, parseNestedPathEnv, type NestedPathEntry } from "./nested-path.ts";
|
|
7
7
|
import { resolveMcpDirectToolSelections, type ResolvedMcpDirectToolSelection } from "./mcp-direct-tool-allowlist.ts";
|
|
8
8
|
import { resolvePiPackageRoot } from "./pi-spawn.ts";
|
|
9
|
+
import { RUNTIME_EXTENSION_ACK_PATH_ENV } from "./runtime-acknowledged-extensions.ts";
|
|
9
10
|
import { STRUCTURED_OUTPUT_CAPTURE_ENV, STRUCTURED_OUTPUT_SCHEMA_ENV } from "./structured-output.ts";
|
|
10
|
-
import { TEMP_ROOT_DIR, type JsonSchemaObject, type ResolvedToolBudget } from "../../shared/types.ts";
|
|
11
|
+
import { TEMP_ROOT_DIR, type JsonSchemaObject, type LaunchResolvedChildExtensionsV1, type ResolvedToolBudget } from "../../shared/types.ts";
|
|
11
12
|
import { THINKING_LEVELS } from "../../shared/model-info.ts";
|
|
12
13
|
import { TOOL_BUDGET_ENV, TOOL_BUDGET_ZERO_AUTH_ENV, encodeToolBudgetEnv } from "./tool-budget.ts";
|
|
13
14
|
import { CHILD_TOOL_DIAGNOSTIC_PATH_ENV, MCP_DIRECT_CHILD_TOOLS_ENV, REQUIRED_CHILD_TOOLS_ENV } from "./tool-availability.ts";
|
|
14
15
|
import { CHILD_WATCHDOG_CONFIG_ENV, encodeChildWatchdogConfig, type ChildWatchdogConfig } from "../../watchdog/child-status.ts";
|
|
15
16
|
import { WAIT_TOOL_ENABLED_ENV } from "../background/wait-config.ts";
|
|
16
17
|
import { SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV } from "../../shared/utils.ts";
|
|
17
|
-
import { SUBAGENT_CAPABILITY_CEILING_ENV, decodeSubagentCapabilityCeiling, encodeSubagentCapabilityCeiling, intersectSubagentCapabilityCeilings, type ResolvedSubagentCapabilityCeiling, type SubagentCapabilityAudit } from "./capability-ceiling.ts";
|
|
18
|
+
import { SUBAGENT_CAPABILITY_CEILING_ENV, capabilityCeilingAgentRestrictionSources, decodeSubagentCapabilityCeiling, encodeSubagentCapabilityCeiling, intersectSubagentCapabilityCeilings, isAgentAllowedByCapabilityCeiling, type ResolvedSubagentCapabilityCeiling, type SubagentCapabilityAudit } from "./capability-ceiling.ts";
|
|
18
19
|
|
|
19
20
|
const TASK_ARG_LIMIT = 8000;
|
|
21
|
+
const MAX_LAUNCH_RESOLVED_EXTENSION_IDS = 32;
|
|
20
22
|
const PROMPT_RUNTIME_EXTENSION_PATH = path.join(path.dirname(fileURLToPath(import.meta.url)), "subagent-prompt-runtime.ts");
|
|
21
23
|
const FANOUT_CHILD_EXTENSION_PATH = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "extension", "fanout-child.ts");
|
|
22
24
|
export const SUBAGENT_CHILD_ENV = "SELESAI_SUBAGENT_CHILD";
|
|
@@ -39,6 +41,8 @@ export const SUBAGENT_PARENT_SESSION_ENV = "SELESAI_SUBAGENT_PARENT_SESSION";
|
|
|
39
41
|
export const SUBAGENT_STEER_INBOX_ENV = "SELESAI_SUBAGENT_STEER_INBOX";
|
|
40
42
|
export const SUBAGENT_STEER_CAPABILITY_ENV = "SELESAI_SUBAGENT_STEER_CAPABILITY";
|
|
41
43
|
export const SUBAGENT_STEER_ACK_DIR_ENV = "SELESAI_SUBAGENT_STEER_ACK_DIR";
|
|
44
|
+
export const PI_INTERCOM_STABLE_ID_ENV = "PI_INTERCOM_STABLE_ID";
|
|
45
|
+
export const PI_INTERCOM_SESSION_ID_ENV = "PI_INTERCOM_SESSION_ID";
|
|
42
46
|
|
|
43
47
|
export interface BuildPiArgsInput {
|
|
44
48
|
parentSessionId?: string;
|
|
@@ -93,6 +97,7 @@ export interface BuildPiArgsResult {
|
|
|
93
97
|
env: Record<string, string | undefined>;
|
|
94
98
|
tempDir?: string;
|
|
95
99
|
toolDiagnosticPath?: string;
|
|
100
|
+
runtimeAcknowledgedExtensionsPath?: string;
|
|
96
101
|
capabilityAudit?: SubagentCapabilityAudit;
|
|
97
102
|
}
|
|
98
103
|
|
|
@@ -123,6 +128,7 @@ export interface ResolvePiLaunchToolPlanInput {
|
|
|
123
128
|
structuredOutput?: boolean;
|
|
124
129
|
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
125
130
|
inheritedCapabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
131
|
+
agentName?: string;
|
|
126
132
|
}
|
|
127
133
|
|
|
128
134
|
export interface PiLaunchToolPlan {
|
|
@@ -145,6 +151,37 @@ export interface PiLaunchToolPlan {
|
|
|
145
151
|
capabilityAudit?: SubagentCapabilityAudit;
|
|
146
152
|
}
|
|
147
153
|
|
|
154
|
+
function extensionIdentifier(value: string): string {
|
|
155
|
+
return `sha256:${createHash("sha256").update(path.normalize(value.trim())).digest("hex").slice(0, 16)}`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function boundedExtensionIdentifiers(values: string[]): { ids: string[]; omitted: number } {
|
|
159
|
+
const ids = [...new Set(values.map(extensionIdentifier))];
|
|
160
|
+
return {
|
|
161
|
+
ids: ids.slice(0, MAX_LAUNCH_RESOLVED_EXTENSION_IDS),
|
|
162
|
+
omitted: Math.max(0, ids.length - MAX_LAUNCH_RESOLVED_EXTENSION_IDS),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function projectLaunchResolvedChildExtensions(toolPlan: Pick<PiLaunchToolPlan, "runtimeExtensions" | "configuredExtensions" | "extensionArgs" | "disableAmbientExtensions">): LaunchResolvedChildExtensionsV1 {
|
|
167
|
+
const runtime = boundedExtensionIdentifiers(toolPlan.runtimeExtensions);
|
|
168
|
+
const configured = boundedExtensionIdentifiers(toolPlan.configuredExtensions);
|
|
169
|
+
const effective = boundedExtensionIdentifiers(toolPlan.extensionArgs);
|
|
170
|
+
return {
|
|
171
|
+
version: 1,
|
|
172
|
+
source: "launch-resolved",
|
|
173
|
+
disableAmbientExtensions: toolPlan.disableAmbientExtensions,
|
|
174
|
+
runtime: runtime.ids,
|
|
175
|
+
configured: configured.ids,
|
|
176
|
+
effective: effective.ids,
|
|
177
|
+
omitted: {
|
|
178
|
+
runtime: runtime.omitted,
|
|
179
|
+
configured: configured.omitted,
|
|
180
|
+
effective: effective.omitted,
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
148
185
|
export function resolvePiLaunchToolPlan(input: ResolvePiLaunchToolPlanInput): PiLaunchToolPlan {
|
|
149
186
|
const capabilityCeiling = intersectSubagentCapabilityCeilings(input.capabilityCeiling, input.inheritedCapabilityCeiling);
|
|
150
187
|
const allowedToolSet = capabilityCeiling?.allowedTools === undefined ? undefined : new Set(capabilityCeiling.allowedTools);
|
|
@@ -191,6 +228,8 @@ export function resolvePiLaunchToolPlan(input: ResolvePiLaunchToolPlanInput): Pi
|
|
|
191
228
|
removedExtensionCount: capabilityCeiling.denyExtensions ? (input.extensions?.length ?? 0) + (input.subagentOnlyExtensions?.length ?? 0) + ((input.tools ?? []).filter((tool) => tool.includes("/") || tool.endsWith(".ts") || tool.endsWith(".js")).length) : 0,
|
|
192
229
|
requestedMcpToolCount: input.mcpDirectTools?.length ?? 0,
|
|
193
230
|
effectiveMcpTools,
|
|
231
|
+
agentAllowed: input.agentName === undefined ? true : isAgentAllowedByCapabilityCeiling(input.agentName, capabilityCeiling),
|
|
232
|
+
...(capabilityCeilingAgentRestrictionSources(capabilityCeiling) ? { agentRestrictionSources: capabilityCeilingAgentRestrictionSources(capabilityCeiling) } : {}),
|
|
194
233
|
} satisfies SubagentCapabilityAudit : undefined;
|
|
195
234
|
return {
|
|
196
235
|
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
@@ -244,6 +283,7 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
|
|
|
244
283
|
structuredOutput: input.structuredOutput,
|
|
245
284
|
capabilityCeiling: input.capabilityCeiling,
|
|
246
285
|
inheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),
|
|
286
|
+
agentName: input.childAgentName,
|
|
247
287
|
});
|
|
248
288
|
if (toolPlan.explicitToolAllowlist) {
|
|
249
289
|
args.push(toolPlan.effectiveToolAllowlist.length > 0 ? "--tools" : "--no-tools");
|
|
@@ -254,6 +294,9 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
|
|
|
254
294
|
}
|
|
255
295
|
for (const extPath of toolPlan.extensionArgs) args.push("--extension", extPath);
|
|
256
296
|
|
|
297
|
+
if (!input.inheritProjectContext) {
|
|
298
|
+
args.push("--no-context-files");
|
|
299
|
+
}
|
|
257
300
|
if (!input.inheritSkills) {
|
|
258
301
|
args.push("--no-skills");
|
|
259
302
|
}
|
|
@@ -281,6 +324,9 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
|
|
|
281
324
|
const env: Record<string, string | undefined> = {};
|
|
282
325
|
const piPackageRoot = process.env[SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV] ?? resolvePiPackageRoot();
|
|
283
326
|
if (piPackageRoot) env[SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV] = piPackageRoot;
|
|
327
|
+
if (!tempDir) tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-"));
|
|
328
|
+
const runtimeAcknowledgedExtensionsPath = path.join(tempDir, "runtime-acknowledged-extensions.json");
|
|
329
|
+
env[RUNTIME_EXTENSION_ACK_PATH_ENV] = runtimeAcknowledgedExtensionsPath;
|
|
284
330
|
let toolDiagnosticPath: string | undefined;
|
|
285
331
|
if (toolPlan.requiredChildTools.length > 0) {
|
|
286
332
|
if (!tempDir) tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-"));
|
|
@@ -329,6 +375,8 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
|
|
|
329
375
|
: "";
|
|
330
376
|
env.SELESAI_SUBAGENT_INHERIT_PROJECT_CONTEXT = input.inheritProjectContext ? "1" : "0";
|
|
331
377
|
env.SELESAI_SUBAGENT_INHERIT_SKILLS = input.inheritSkills ? "1" : "0";
|
|
378
|
+
env[PI_INTERCOM_STABLE_ID_ENV] = input.intercomSessionName || undefined;
|
|
379
|
+
env[PI_INTERCOM_SESSION_ID_ENV] = undefined;
|
|
332
380
|
if (input.intercomSessionName) {
|
|
333
381
|
env.SELESAI_SUBAGENT_INTERCOM_SESSION_NAME = input.intercomSessionName;
|
|
334
382
|
}
|
|
@@ -376,7 +424,7 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
|
|
|
376
424
|
|
|
377
425
|
env[SUBAGENT_PARENT_SESSION_ENV] = input.parentSessionId ?? process.env[SUBAGENT_PARENT_SESSION_ENV] ?? "";
|
|
378
426
|
|
|
379
|
-
return { args, env, tempDir, toolDiagnosticPath, capabilityAudit: toolPlan.capabilityAudit };
|
|
427
|
+
return { args, env, tempDir, toolDiagnosticPath, runtimeAcknowledgedExtensionsPath, capabilityAudit: toolPlan.capabilityAudit };
|
|
380
428
|
}
|
|
381
429
|
|
|
382
430
|
export const parseParentPathEnv = parseNestedPathEnv;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function formatProcessSignalError(signal: string): string {
|
|
2
|
+
return `Subagent process terminated by signal ${signal}.`;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function isUnexplainedProcessSignal(input: {
|
|
6
|
+
processSignal?: string | null;
|
|
7
|
+
interrupted?: boolean;
|
|
8
|
+
timedOut?: boolean;
|
|
9
|
+
stopped?: boolean;
|
|
10
|
+
turnBudgetExceeded?: boolean;
|
|
11
|
+
forcedDrainAfterFinalSuccess?: boolean;
|
|
12
|
+
}): boolean {
|
|
13
|
+
return Boolean(input.processSignal)
|
|
14
|
+
&& input.interrupted !== true
|
|
15
|
+
&& input.timedOut !== true
|
|
16
|
+
&& input.stopped !== true
|
|
17
|
+
&& input.turnBudgetExceeded !== true
|
|
18
|
+
&& input.forcedDrainAfterFinalSuccess !== true;
|
|
19
|
+
}
|
|
@@ -18,6 +18,7 @@ const ROTATE_KEEP = 1000;
|
|
|
18
18
|
const PRIVATE_DIR_MODE = 0o700;
|
|
19
19
|
const PRIVATE_FILE_MODE = 0o600;
|
|
20
20
|
const REDACTED_TASK = "[redacted]";
|
|
21
|
+
const historyFileStates = new Map<string, { mtimeMs: number; ctimeMs: number; size: number; ino: number; lineCount: number }>();
|
|
21
22
|
|
|
22
23
|
function getHistoryPath(): string {
|
|
23
24
|
return path.join(getAgentDir(), "run-history.jsonl");
|
|
@@ -30,8 +31,12 @@ function hashTask(task: string): string {
|
|
|
30
31
|
function hardenHistoryStorage(historyPath: string): void {
|
|
31
32
|
const historyDir = path.dirname(historyPath);
|
|
32
33
|
fs.mkdirSync(historyDir, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
33
|
-
try {
|
|
34
|
-
|
|
34
|
+
try {
|
|
35
|
+
if ((fs.statSync(historyDir).mode & 0o777) !== PRIVATE_DIR_MODE) fs.chmodSync(historyDir, PRIVATE_DIR_MODE);
|
|
36
|
+
} catch {}
|
|
37
|
+
try {
|
|
38
|
+
if ((fs.statSync(historyPath).mode & 0o777) !== PRIVATE_FILE_MODE) fs.chmodSync(historyPath, PRIVATE_FILE_MODE);
|
|
39
|
+
} catch {}
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
function sanitizeHistoryLine(line: string): string | undefined {
|
|
@@ -80,11 +85,39 @@ function writePrivateHistory(historyPath: string, lines: string[]): void {
|
|
|
80
85
|
try { fs.chmodSync(historyPath, PRIVATE_FILE_MODE); } catch {}
|
|
81
86
|
}
|
|
82
87
|
|
|
83
|
-
function
|
|
84
|
-
|
|
88
|
+
function rememberHistoryFile(historyPath: string, lineCount: number): void {
|
|
89
|
+
const stat = fs.statSync(historyPath);
|
|
90
|
+
historyFileStates.set(historyPath, {
|
|
91
|
+
mtimeMs: stat.mtimeMs,
|
|
92
|
+
ctimeMs: stat.ctimeMs,
|
|
93
|
+
size: stat.size,
|
|
94
|
+
ino: stat.ino,
|
|
95
|
+
lineCount,
|
|
96
|
+
});
|
|
97
|
+
if (historyFileStates.size > 8) historyFileStates.delete(historyFileStates.keys().next().value!);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function sanitizeHistoryFile(historyPath: string): number {
|
|
101
|
+
let stat: fs.Stats;
|
|
102
|
+
try {
|
|
103
|
+
stat = fs.statSync(historyPath);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return 0;
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
const cached = historyFileStates.get(historyPath);
|
|
109
|
+
if (cached
|
|
110
|
+
&& cached.mtimeMs === stat.mtimeMs
|
|
111
|
+
&& cached.ctimeMs === stat.ctimeMs
|
|
112
|
+
&& cached.size === stat.size
|
|
113
|
+
&& cached.ino === stat.ino) {
|
|
114
|
+
return cached.lineCount;
|
|
115
|
+
}
|
|
85
116
|
const raw = fs.readFileSync(historyPath, "utf-8");
|
|
86
117
|
const { lines, changed } = sanitizeHistoryLines(raw);
|
|
87
118
|
if (changed) writePrivateHistory(historyPath, lines);
|
|
119
|
+
rememberHistoryFile(historyPath, lines.length);
|
|
120
|
+
return lines.length;
|
|
88
121
|
}
|
|
89
122
|
|
|
90
123
|
function appendPrivateHistoryLine(historyPath: string, line: string): void {
|
|
@@ -94,7 +127,6 @@ function appendPrivateHistoryLine(historyPath: string, line: string): void {
|
|
|
94
127
|
} finally {
|
|
95
128
|
fs.closeSync(fd);
|
|
96
129
|
}
|
|
97
|
-
try { fs.chmodSync(historyPath, PRIVATE_FILE_MODE); } catch {}
|
|
98
130
|
}
|
|
99
131
|
|
|
100
132
|
export function recordRun(agent: string, task: string, exitCode: number, durationMs: number): void {
|
|
@@ -110,8 +142,11 @@ export function recordRun(agent: string, task: string, exitCode: number, duratio
|
|
|
110
142
|
};
|
|
111
143
|
const historyPath = getHistoryPath();
|
|
112
144
|
hardenHistoryStorage(historyPath);
|
|
113
|
-
|
|
145
|
+
let lineCount: number | undefined;
|
|
146
|
+
try { lineCount = sanitizeHistoryFile(historyPath); } catch {}
|
|
114
147
|
appendPrivateHistoryLine(historyPath, JSON.stringify(entry));
|
|
148
|
+
if (lineCount === undefined) historyFileStates.delete(historyPath);
|
|
149
|
+
else rememberHistoryFile(historyPath, lineCount + 1);
|
|
115
150
|
} catch {
|
|
116
151
|
// Best-effort — never crash the execution flow for history recording
|
|
117
152
|
}
|
|
@@ -134,9 +169,10 @@ export function loadRunsForAgent(agent: string): RunEntry[] {
|
|
|
134
169
|
lines = lines.slice(-ROTATE_KEEP);
|
|
135
170
|
changed = true;
|
|
136
171
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
172
|
+
try {
|
|
173
|
+
if (changed) writePrivateHistory(historyPath, lines);
|
|
174
|
+
rememberHistoryFile(historyPath, lines.length);
|
|
175
|
+
} catch {}
|
|
140
176
|
|
|
141
177
|
return lines
|
|
142
178
|
.map((line) => { try { return JSON.parse(line) as RunEntry; } catch { return undefined; } })
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import type { RuntimeAcknowledgedChildExtensionsV1 } from "../../shared/types.ts";
|
|
4
|
+
|
|
5
|
+
export const RUNTIME_EXTENSION_ACK_EVENT = "subagent:acknowledge-extension";
|
|
6
|
+
export const RUNTIME_EXTENSION_ACK_PATH_ENV = "SELESAI_SUBAGENT_RUNTIME_ACKNOWLEDGED_EXTENSIONS";
|
|
7
|
+
export const MAX_RUNTIME_ACKNOWLEDGED_EXTENSION_IDS = 32;
|
|
8
|
+
export const MAX_RUNTIME_ACKNOWLEDGED_EXTENSION_ID_LENGTH = 128;
|
|
9
|
+
// Generous bound for a valid capture (32 ids x 128 chars plus envelope); larger files are child misbehavior.
|
|
10
|
+
export const MAX_RUNTIME_ACKNOWLEDGED_EXTENSION_FILE_BYTES = 64 * 1024;
|
|
11
|
+
|
|
12
|
+
export function isRuntimeAcknowledgedExtensionId(value: unknown): value is string {
|
|
13
|
+
return typeof value === "string"
|
|
14
|
+
&& value.length > 0
|
|
15
|
+
&& value.length <= MAX_RUNTIME_ACKNOWLEDGED_EXTENSION_ID_LENGTH
|
|
16
|
+
&& /^[A-Za-z0-9._:@+-]+$/.test(value)
|
|
17
|
+
&& !value.includes("..")
|
|
18
|
+
&& !value.includes("/")
|
|
19
|
+
&& !value.includes("\\");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function projectRuntimeAcknowledgedExtensions(ids: Iterable<unknown>): RuntimeAcknowledgedChildExtensionsV1 | undefined {
|
|
23
|
+
const unique: string[] = [];
|
|
24
|
+
const seen = new Set<string>();
|
|
25
|
+
for (const id of ids) {
|
|
26
|
+
if (!isRuntimeAcknowledgedExtensionId(id) || seen.has(id)) continue;
|
|
27
|
+
seen.add(id);
|
|
28
|
+
unique.push(id);
|
|
29
|
+
}
|
|
30
|
+
if (unique.length === 0) return undefined;
|
|
31
|
+
return {
|
|
32
|
+
version: 1,
|
|
33
|
+
source: "child-runtime",
|
|
34
|
+
ids: unique.slice(0, MAX_RUNTIME_ACKNOWLEDGED_EXTENSION_IDS),
|
|
35
|
+
omitted: Math.max(0, unique.length - MAX_RUNTIME_ACKNOWLEDGED_EXTENSION_IDS),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function sanitizeRuntimeAcknowledgedExtensions(value: unknown): RuntimeAcknowledgedChildExtensionsV1 | undefined {
|
|
40
|
+
if (!value || typeof value !== "object") return undefined;
|
|
41
|
+
const raw = value as Record<string, unknown>;
|
|
42
|
+
if (raw.version !== 1 || raw.source !== "child-runtime" || !Array.isArray(raw.ids)) return undefined;
|
|
43
|
+
const projected = projectRuntimeAcknowledgedExtensions(raw.ids);
|
|
44
|
+
if (!projected) return undefined;
|
|
45
|
+
const omitted = typeof raw.omitted === "number" && Number.isFinite(raw.omitted)
|
|
46
|
+
? Math.max(0, Math.floor(raw.omitted))
|
|
47
|
+
: 0;
|
|
48
|
+
return { ...projected, omitted: projected.omitted + omitted };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function readRuntimeAcknowledgedExtensions(filePath: string | undefined): RuntimeAcknowledgedChildExtensionsV1 | undefined {
|
|
52
|
+
if (!filePath) return undefined;
|
|
53
|
+
try {
|
|
54
|
+
if (fs.statSync(filePath).size > MAX_RUNTIME_ACKNOWLEDGED_EXTENSION_FILE_BYTES) return undefined;
|
|
55
|
+
const parsed = JSON.parse(fs.readFileSync(filePath, "utf-8")) as unknown;
|
|
56
|
+
return sanitizeRuntimeAcknowledgedExtensions(parsed);
|
|
57
|
+
} catch {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function writeRuntimeAcknowledgedExtensions(filePath: string, ids: Iterable<unknown>): void {
|
|
63
|
+
const projection = projectRuntimeAcknowledgedExtensions(ids);
|
|
64
|
+
try {
|
|
65
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
66
|
+
if (projection) fs.writeFileSync(filePath, JSON.stringify(projection), { mode: 0o600 });
|
|
67
|
+
else fs.rmSync(filePath, { force: true });
|
|
68
|
+
} catch {
|
|
69
|
+
// Runtime acknowledgement is best-effort observability only.
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import type { Message } from "@earendil-works/pi-ai";
|
|
4
|
-
import type { OutputMode, SavedOutputReference } from "../../shared/types.ts";
|
|
4
|
+
import type { MaxOutputConfig, OutputMode, SavedOutputReference } from "../../shared/types.ts";
|
|
5
|
+
import { truncateOutput } from "../../shared/types.ts";
|
|
5
6
|
import { hasMutationToolCapability } from "./completion-guard.ts";
|
|
6
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Fixed internal context-fallback cap used when persisted output is unavailable
|
|
10
|
+
* (persistence/read failure, `output: false`, or legacy results without a saved
|
|
11
|
+
* path). Deliberately not exposed as a public parameter; reuse `truncateOutput()`
|
|
12
|
+
* so the excerpt is bounded by both lines and bytes and never falls back to
|
|
13
|
+
* unbounded output.
|
|
14
|
+
*/
|
|
15
|
+
export const CONTEXT_FALLBACK_LIMIT: Required<MaxOutputConfig> = { bytes: 4096, lines: 80 };
|
|
16
|
+
|
|
7
17
|
export interface SingleOutputSnapshot {
|
|
8
18
|
exists: boolean;
|
|
9
19
|
mtimeMs?: number;
|
|
@@ -125,6 +135,34 @@ function formatByteSize(bytes: number): string {
|
|
|
125
135
|
return `${value.toFixed(1)} ${units[unitIndex]}`;
|
|
126
136
|
}
|
|
127
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Bounded fallback for deliveries where the durable full output cannot be
|
|
140
|
+
* referenced: persistence/write errors, unreadable saved paths, or no saved
|
|
141
|
+
* path at all (legacy/`output: false`). Contains process status, the intended
|
|
142
|
+
* output path, the concrete error, and a bounded 80-line/4-KiB excerpt of the
|
|
143
|
+
* available output. Never claims a full artifact exists.
|
|
144
|
+
*/
|
|
145
|
+
export function formatBoundedPersistenceFallback(input: {
|
|
146
|
+
error: string;
|
|
147
|
+
outputPath?: string;
|
|
148
|
+
fullOutput: string;
|
|
149
|
+
exitCode: number;
|
|
150
|
+
processError?: string;
|
|
151
|
+
}): string {
|
|
152
|
+
const status = input.processError?.trim()
|
|
153
|
+
? `Process status: ${input.processError}`
|
|
154
|
+
: `Process status: ${input.exitCode === 0 ? "completed" : `failed (exit ${input.exitCode})`}`;
|
|
155
|
+
const bounded = truncateOutput(input.fullOutput, CONTEXT_FALLBACK_LIMIT);
|
|
156
|
+
const lines = [
|
|
157
|
+
`[Full output unavailable] ${input.error}`,
|
|
158
|
+
status,
|
|
159
|
+
...(input.outputPath ? [`Intended output path: ${input.outputPath}`] : []),
|
|
160
|
+
"Full output is unavailable; showing a bounded excerpt (first 80 lines / 4 KiB).",
|
|
161
|
+
bounded.text,
|
|
162
|
+
];
|
|
163
|
+
return lines.join("\n");
|
|
164
|
+
}
|
|
165
|
+
|
|
128
166
|
export function formatSavedOutputReference(savedPath: string, fullOutput: string): SavedOutputReference {
|
|
129
167
|
const absolutePath = path.resolve(savedPath);
|
|
130
168
|
const bytes = Buffer.byteLength(fullOutput, "utf-8");
|
|
@@ -217,19 +255,35 @@ export function finalizeSingleOutput(params: {
|
|
|
217
255
|
savedPath?: string;
|
|
218
256
|
outputReference?: SavedOutputReference;
|
|
219
257
|
saveError?: string;
|
|
258
|
+
error?: string;
|
|
220
259
|
}): { displayOutput: string; savedPath?: string; outputReference?: SavedOutputReference; saveError?: string } {
|
|
221
|
-
|
|
222
|
-
if (params.exitCode === 0 && params.savedPath) {
|
|
260
|
+
if (params.savedPath) {
|
|
223
261
|
const outputReference = params.outputReference ?? formatSavedOutputReference(params.savedPath, params.fullOutput);
|
|
224
|
-
if (params.outputMode === "file-only") {
|
|
262
|
+
if (params.exitCode === 0 && params.outputMode === "file-only") {
|
|
225
263
|
return { displayOutput: outputReference.message, savedPath: params.savedPath, outputReference };
|
|
226
264
|
}
|
|
227
|
-
|
|
265
|
+
if (params.exitCode !== 0) {
|
|
266
|
+
// Failed runs with a successfully persisted result surface the error/status
|
|
267
|
+
// plus the saved-output reference, never raw child output.
|
|
268
|
+
const status = params.error?.trim() || `Subagent failed with exit code ${params.exitCode}`;
|
|
269
|
+
return { displayOutput: `${status}\n\n${outputReference.message}`, savedPath: params.savedPath, outputReference };
|
|
270
|
+
}
|
|
271
|
+
const displayOutput = `${params.truncatedOutput || params.fullOutput}\n\n${outputReference.message}`;
|
|
228
272
|
return { displayOutput, savedPath: params.savedPath, outputReference };
|
|
229
273
|
}
|
|
230
|
-
if (params.
|
|
231
|
-
|
|
232
|
-
|
|
274
|
+
if (params.saveError && params.outputPath) {
|
|
275
|
+
// Persistence/read failure: only the bounded fallback is delivered. It must
|
|
276
|
+
// not claim a full artifact exists.
|
|
277
|
+
return {
|
|
278
|
+
displayOutput: formatBoundedPersistenceFallback({
|
|
279
|
+
error: params.saveError,
|
|
280
|
+
outputPath: params.outputPath,
|
|
281
|
+
fullOutput: params.fullOutput,
|
|
282
|
+
exitCode: params.exitCode,
|
|
283
|
+
processError: params.error,
|
|
284
|
+
}),
|
|
285
|
+
saveError: params.saveError,
|
|
286
|
+
};
|
|
233
287
|
}
|
|
234
|
-
return { displayOutput };
|
|
288
|
+
return { displayOutput: params.truncatedOutput || params.fullOutput };
|
|
235
289
|
}
|
|
@@ -4,6 +4,7 @@ import type { ExtensionAPI } from "@selesai/code";
|
|
|
4
4
|
import { registerNativeSupervisorClient } from "../../intercom/native-supervisor-channel.ts";
|
|
5
5
|
import { consumeSteerRequestsFromDir, steerAckPathFromDir, writeSteerAckAt, writeSteerCapabilityAt, writeSteerRequestToDir, type SteerRequest } from "../background/control-channel.ts";
|
|
6
6
|
import { SUBAGENT_CHILD_AGENT_ENV, SUBAGENT_CHILD_INDEX_ENV, SUBAGENT_FANOUT_CHILD_ENV, SUBAGENT_STEER_ACK_DIR_ENV, SUBAGENT_STEER_CAPABILITY_ENV, SUBAGENT_STEER_INBOX_ENV } from "./pi-args.ts";
|
|
7
|
+
import { RUNTIME_EXTENSION_ACK_EVENT, RUNTIME_EXTENSION_ACK_PATH_ENV, isRuntimeAcknowledgedExtensionId, writeRuntimeAcknowledgedExtensions } from "./runtime-acknowledged-extensions.ts";
|
|
7
8
|
import { createStructuredOutputToolParameters, STRUCTURED_OUTPUT_CAPTURE_ENV, STRUCTURED_OUTPUT_SCHEMA_ENV, validateStructuredOutputValue } from "./structured-output.ts";
|
|
8
9
|
import {
|
|
9
10
|
CHILD_TOOL_DIAGNOSTIC_PATH_ENV,
|
|
@@ -34,7 +35,7 @@ const STRUCTURED_OUTPUT_INSTRUCTIONS = [
|
|
|
34
35
|
|
|
35
36
|
export const CHILD_SUBAGENT_BOUNDARY_INSTRUCTIONS = [
|
|
36
37
|
"You are a child subagent, not the parent orchestrator.",
|
|
37
|
-
"The parent session owns delegation, orchestration, review fanout, and follow-up
|
|
38
|
+
"The parent session owns delegation, orchestration, review fanout, and follow-up builder launches.",
|
|
38
39
|
"Ignore prior parent-only orchestration instructions in inherited conversation history.",
|
|
39
40
|
"Do not propose or run subagents. Complete only your assigned role-specific task with the tools available to you.",
|
|
40
41
|
"If you need to edit files, use the available editing tools. Do not print tool-call syntax, patches, or pseudo-tool calls as text.",
|
|
@@ -99,6 +100,34 @@ function refreshChildToolDiagnostic(pi: ExtensionAPI): ChildToolDiagnostic | und
|
|
|
99
100
|
return writeChildToolDiagnostic(filePath, required, available, process.env[SUBAGENT_CHILD_AGENT_ENV]?.trim(), readMcpDirectChildTools());
|
|
100
101
|
}
|
|
101
102
|
|
|
103
|
+
function registerRuntimeExtensionAcknowledgements(pi: ExtensionAPI): void {
|
|
104
|
+
const outputPath = process.env[RUNTIME_EXTENSION_ACK_PATH_ENV]?.trim();
|
|
105
|
+
if (!outputPath) return;
|
|
106
|
+
const ids: string[] = [];
|
|
107
|
+
let finalized = false;
|
|
108
|
+
const acknowledge = (payload: unknown): undefined => {
|
|
109
|
+
if (finalized || !payload || typeof payload !== "object") return undefined;
|
|
110
|
+
const id = (payload as { id?: unknown }).id;
|
|
111
|
+
if (isRuntimeAcknowledgedExtensionId(id)) ids.push(id);
|
|
112
|
+
return undefined;
|
|
113
|
+
};
|
|
114
|
+
const finalize = (): undefined => {
|
|
115
|
+
if (finalized) return undefined;
|
|
116
|
+
finalized = true;
|
|
117
|
+
writeRuntimeAcknowledgedExtensions(outputPath, ids);
|
|
118
|
+
return undefined;
|
|
119
|
+
};
|
|
120
|
+
try {
|
|
121
|
+
const events = (pi as { events?: { on?: (event: string, handler: (payload: unknown) => unknown) => unknown } }).events;
|
|
122
|
+
events?.on?.(RUNTIME_EXTENSION_ACK_EVENT, acknowledge);
|
|
123
|
+
const onRuntimeEvent = pi.on as unknown as (event: string, handler: (event?: unknown, ctx?: unknown) => unknown) => void;
|
|
124
|
+
onRuntimeEvent("agent_end", finalize);
|
|
125
|
+
onRuntimeEvent("session_shutdown", finalize);
|
|
126
|
+
} catch {
|
|
127
|
+
// Acknowledgement collection is optional observability and must not affect child execution.
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
102
131
|
function findSectionEnd(prompt: string, startIndex: number, nextHeaders: string[]): number {
|
|
103
132
|
let endIndex = prompt.length;
|
|
104
133
|
for (const header of nextHeaders) {
|
|
@@ -346,6 +375,7 @@ export function registerSteeringInbox(
|
|
|
346
375
|
}
|
|
347
376
|
|
|
348
377
|
export default function registerSubagentPromptRuntime(pi: ExtensionAPI): void {
|
|
378
|
+
registerRuntimeExtensionAcknowledgements(pi);
|
|
349
379
|
registerSteeringInbox(pi);
|
|
350
380
|
registerToolBudget(pi, decodeToolBudgetEnv(process.env[TOOL_BUDGET_ENV], { allowZero: process.env[TOOL_BUDGET_ZERO_AUTH_ENV] === "1" }));
|
|
351
381
|
registerChildWatchdog(pi);
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { Usage } from "../../shared/types.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Retry delays for child processes that exit before any model or tool activity.
|
|
5
|
+
* The schedule is intentionally short and bounded to avoid amplifying persistent
|
|
6
|
+
* launch failures while allowing concurrent Pi startup locks to clear.
|
|
7
|
+
*/
|
|
8
|
+
export const SUBAGENT_STARTUP_RETRY_DELAYS_MS = [250, 750, 1500] as const;
|
|
9
|
+
|
|
10
|
+
/** A genuine startup race should fail well before a model request can complete. */
|
|
11
|
+
export const MAX_SUBAGENT_STARTUP_FAILURE_DURATION_MS = 2000;
|
|
12
|
+
|
|
13
|
+
export interface SubagentStartupFailureEvidence {
|
|
14
|
+
exitCode: number | null | undefined;
|
|
15
|
+
error?: string;
|
|
16
|
+
finalOutput?: string;
|
|
17
|
+
messageCount: number;
|
|
18
|
+
toolCount: number;
|
|
19
|
+
usage: Usage;
|
|
20
|
+
durationMs: number;
|
|
21
|
+
protocolError?: unknown;
|
|
22
|
+
processSignal?: string | null;
|
|
23
|
+
observedMutationAttempt?: boolean;
|
|
24
|
+
detached?: boolean;
|
|
25
|
+
interrupted?: boolean;
|
|
26
|
+
timedOut?: boolean;
|
|
27
|
+
stopped?: boolean;
|
|
28
|
+
turnBudgetExceeded?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function hasUsage(usage: Usage): boolean {
|
|
32
|
+
return usage.input !== 0
|
|
33
|
+
|| usage.output !== 0
|
|
34
|
+
|| usage.cacheRead !== 0
|
|
35
|
+
|| usage.cacheWrite !== 0
|
|
36
|
+
|| usage.cost !== 0
|
|
37
|
+
|| usage.turns !== 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Classify only unexplained, zero-activity child exits as startup failures.
|
|
42
|
+
* This intentionally fails closed: any model output, tool activity, usage,
|
|
43
|
+
* mutation evidence, diagnostic, or lifecycle interruption prevents a retry.
|
|
44
|
+
*/
|
|
45
|
+
export function isRetryableSubagentStartupFailure(evidence: SubagentStartupFailureEvidence): boolean {
|
|
46
|
+
return evidence.exitCode !== undefined
|
|
47
|
+
&& evidence.exitCode !== null
|
|
48
|
+
&& evidence.exitCode !== 0
|
|
49
|
+
&& !evidence.error?.trim()
|
|
50
|
+
&& !evidence.finalOutput?.trim()
|
|
51
|
+
&& evidence.messageCount === 0
|
|
52
|
+
&& evidence.toolCount === 0
|
|
53
|
+
&& !hasUsage(evidence.usage)
|
|
54
|
+
&& evidence.durationMs >= 0
|
|
55
|
+
&& evidence.durationMs <= MAX_SUBAGENT_STARTUP_FAILURE_DURATION_MS
|
|
56
|
+
&& evidence.protocolError === undefined
|
|
57
|
+
&& (evidence.processSignal === undefined || evidence.processSignal === null)
|
|
58
|
+
&& evidence.observedMutationAttempt !== true
|
|
59
|
+
&& evidence.detached !== true
|
|
60
|
+
&& evidence.interrupted !== true
|
|
61
|
+
&& evidence.timedOut !== true
|
|
62
|
+
&& evidence.stopped !== true
|
|
63
|
+
&& evidence.turnBudgetExceeded !== true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function formatSubagentStartupRetryNote(input: {
|
|
67
|
+
model: string;
|
|
68
|
+
attempt: number;
|
|
69
|
+
maxAttempts: number;
|
|
70
|
+
delayMs: number;
|
|
71
|
+
}): string {
|
|
72
|
+
return `[startup-retry] ${input.model} exited before model or tool activity (attempt ${input.attempt}/${input.maxAttempts}). Retrying the same model in ${input.delayMs}ms.`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function formatSubagentStartupRetryExhaustedError(input: {
|
|
76
|
+
model: string;
|
|
77
|
+
attempts: number;
|
|
78
|
+
}): string {
|
|
79
|
+
return `Subagent failed to start after ${input.attempts} attempts on ${input.model}; no model, tool, output, usage, or diagnostic activity was observed. This may be a concurrent Pi startup race. Retry the run or temporarily lower subagent concurrency.`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Wait for a retry delay, returning false when any lifecycle signal aborts it. */
|
|
83
|
+
export async function waitForSubagentStartupRetry(delayMs: number, signals: Array<AbortSignal | undefined>): Promise<boolean> {
|
|
84
|
+
const activeSignals = signals.filter((signal): signal is AbortSignal => Boolean(signal));
|
|
85
|
+
if (activeSignals.some((signal) => signal.aborted)) return false;
|
|
86
|
+
if (delayMs <= 0) return true;
|
|
87
|
+
|
|
88
|
+
return await new Promise<boolean>((resolve) => {
|
|
89
|
+
let settled = false;
|
|
90
|
+
const finish = (shouldRetry: boolean): void => {
|
|
91
|
+
if (settled) return;
|
|
92
|
+
settled = true;
|
|
93
|
+
clearTimeout(timer);
|
|
94
|
+
for (const signal of activeSignals) signal.removeEventListener("abort", abort);
|
|
95
|
+
resolve(shouldRetry);
|
|
96
|
+
};
|
|
97
|
+
const abort = (): void => finish(false);
|
|
98
|
+
const timer = setTimeout(() => finish(true), delayMs);
|
|
99
|
+
for (const signal of activeSignals) signal.addEventListener("abort", abort, { once: true });
|
|
100
|
+
});
|
|
101
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { AcceptanceRole } from "../../shared/types.ts";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Shared task mutation-intent classifier.
|
|
3
5
|
*
|
|
@@ -40,7 +42,7 @@ const REVIEWER_REQUIRED_EDIT_PATTERNS = [
|
|
|
40
42
|
const NO_EDIT_PROHIBITION_PATTERN = /\b(?:do not|don't|must not)\s+(?:edit|modify|write(?:\s+to)?|touch|change)\b((?:(?!\b(?:but|and|then)\b)[^.;,:!?\n–—-])*)/gi;
|
|
41
43
|
|
|
42
44
|
/** Objects of a no-edit prohibition that mean "the codebase in general" rather than a named scope. */
|
|
43
|
-
const GENERIC_PROHIBITION_OBJECT = /^\s*(?:(?:any|all|the|these|those|your|our|existing|project|source|sources|repo|repository)[\s/,-]*)*(?:files?|code|codebase|sources?|anything|repo(?:sitory)?)?\s*$/i;
|
|
45
|
+
const GENERIC_PROHIBITION_OBJECT = /^\s*(?:(?:any|all|the|these|those|your|our|existing|project|product|source|sources|config|configs|repo|repository)[\s/,-]*)*(?:files?|code|codebase|sources?|anything|repo(?:sitory)?)?\s*$/i;
|
|
44
46
|
|
|
45
47
|
const SCOPED_NO_EDIT_CONSTRAINT_PATTERNS = [
|
|
46
48
|
/\bdo not edit files?\s+outside\b/i,
|
|
@@ -139,6 +141,25 @@ function isReviewerStyleAgent(agent: string): boolean {
|
|
|
139
141
|
return /\b(?:architect|commentator|explorer|recapper|researcher)\b/i.test(agent);
|
|
140
142
|
}
|
|
141
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Resolve an agent's acceptance routing role: a declared `acceptanceRole`
|
|
146
|
+
* always wins; otherwise the established name heuristics apply (builder-named
|
|
147
|
+
* agents are writers; architect/commentator/explorer/recapper/researcher/
|
|
148
|
+
* analyst-named agents are read-only). This is the single shared source of
|
|
149
|
+
* truth for acceptance inference and task-aware advisory routing so the two
|
|
150
|
+
* cannot drift. Alias values are never role signals.
|
|
151
|
+
*/
|
|
152
|
+
export function resolveAgentRoutingRole(
|
|
153
|
+
agentName: string,
|
|
154
|
+
acceptanceRole?: AcceptanceRole,
|
|
155
|
+
): "writer" | "read-only" | undefined {
|
|
156
|
+
if (acceptanceRole !== undefined) return acceptanceRole;
|
|
157
|
+
const agent = agentName.toLowerCase();
|
|
158
|
+
if (/\bbuilder\b/.test(agent)) return "writer";
|
|
159
|
+
if (/\b(?:architect|commentator|explorer|recapper|researcher|analyst)\b/.test(agent)) return "read-only";
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
|
|
142
163
|
function hasImplementationIntent(agent: string, taskText: string): boolean {
|
|
143
164
|
if (isReviewerStyleAgent(agent)) return REVIEWER_REQUIRED_EDIT_PATTERNS.some((pattern) => pattern.test(taskText));
|
|
144
165
|
if (agent === "builder") return WORKER_IMPLEMENTATION_PATTERNS.some((pattern) => pattern.test(taskText));
|