@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
|
@@ -23,8 +23,12 @@ import {
|
|
|
23
23
|
SUBAGENT_ORCHESTRATOR_SESSION_ID_ENV,
|
|
24
24
|
SUBAGENT_SUPERVISOR_CHANNEL_DIR_ENV,
|
|
25
25
|
SUBAGENT_RUN_ID_ENV,
|
|
26
|
+
PI_INTERCOM_STABLE_ID_ENV,
|
|
27
|
+
PI_INTERCOM_SESSION_ID_ENV,
|
|
26
28
|
applyThinkingSuffix,
|
|
27
29
|
buildPiArgs,
|
|
30
|
+
projectLaunchResolvedChildExtensions,
|
|
31
|
+
resolvePiLaunchToolPlan,
|
|
28
32
|
} from "../../src/runs/shared/pi-args.ts";
|
|
29
33
|
|
|
30
34
|
const originalEnv = {
|
|
@@ -45,6 +49,10 @@ const originalEnv = {
|
|
|
45
49
|
[MCP_DIRECT_CHILD_TOOLS_ENV]: process.env[MCP_DIRECT_CHILD_TOOLS_ENV],
|
|
46
50
|
[TOOL_BUDGET_ZERO_AUTH_ENV]: process.env[TOOL_BUDGET_ZERO_AUTH_ENV],
|
|
47
51
|
[SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV]: process.env[SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV],
|
|
52
|
+
[PI_INTERCOM_STABLE_ID_ENV]: process.env[PI_INTERCOM_STABLE_ID_ENV],
|
|
53
|
+
[PI_INTERCOM_SESSION_ID_ENV]: process.env[PI_INTERCOM_SESSION_ID_ENV],
|
|
54
|
+
MCP_HASH_ROOT: process.env.MCP_HASH_ROOT,
|
|
55
|
+
MCP_HASH_TOKEN: process.env.MCP_HASH_TOKEN,
|
|
48
56
|
};
|
|
49
57
|
const originalCwd = process.cwd();
|
|
50
58
|
const tempRoots: string[] = [];
|
|
@@ -126,6 +134,29 @@ afterEach(() => {
|
|
|
126
134
|
});
|
|
127
135
|
|
|
128
136
|
describe("buildPiArgs session wiring", () => {
|
|
137
|
+
it("projects launch-resolved extension identifiers without raw paths", () => {
|
|
138
|
+
const privateExt = path.join(os.tmpdir(), "private-extension-root", "secret-extension.ts");
|
|
139
|
+
const toolExt = path.join(os.tmpdir(), "tool-extension-root", "tool-extension.ts");
|
|
140
|
+
const plan = resolvePiLaunchToolPlan({
|
|
141
|
+
tools: ["read", toolExt],
|
|
142
|
+
extensions: [privateExt],
|
|
143
|
+
subagentOnlyExtensions: ["package-extension"],
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const projection = projectLaunchResolvedChildExtensions(plan);
|
|
147
|
+
|
|
148
|
+
assert.equal(projection.version, 1);
|
|
149
|
+
assert.equal(projection.source, "launch-resolved");
|
|
150
|
+
assert.equal(projection.disableAmbientExtensions, true);
|
|
151
|
+
assert.equal(projection.runtime.length, 1);
|
|
152
|
+
assert.equal(projection.configured.length, 3);
|
|
153
|
+
assert.equal(projection.effective.length, 4);
|
|
154
|
+
for (const id of [...projection.runtime, ...projection.configured, ...projection.effective]) {
|
|
155
|
+
assert.match(id, /^sha256:[a-f0-9]{16}$/);
|
|
156
|
+
}
|
|
157
|
+
assert.ok(!JSON.stringify(projection).includes(os.tmpdir()), "projection should not expose raw extension paths");
|
|
158
|
+
});
|
|
159
|
+
|
|
129
160
|
it("uses --session when sessionFile is provided", () => {
|
|
130
161
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-args-session-"));
|
|
131
162
|
try {
|
|
@@ -415,11 +446,24 @@ describe("buildPiArgs system prompt mode wiring", () => {
|
|
|
415
446
|
|
|
416
447
|
const extensionArgs = args.filter((arg, index) => args[index - 1] === "--extension");
|
|
417
448
|
assert.ok(extensionArgs.some((arg) => arg.endsWith(path.join("src", "runs", "shared", "subagent-prompt-runtime.ts"))));
|
|
449
|
+
assert.ok(args.includes("--no-context-files"));
|
|
418
450
|
assert.equal(env.SELESAI_SUBAGENT_CHILD, "1");
|
|
419
451
|
assert.equal(env.SELESAI_SUBAGENT_INHERIT_PROJECT_CONTEXT, "0");
|
|
420
452
|
assert.equal(env.SELESAI_SUBAGENT_INHERIT_SKILLS, "1");
|
|
421
453
|
});
|
|
422
454
|
|
|
455
|
+
it("keeps context file loading enabled when project context is inherited", () => {
|
|
456
|
+
const { args } = buildPiArgs({
|
|
457
|
+
baseArgs: ["-p"],
|
|
458
|
+
task: "hello",
|
|
459
|
+
sessionEnabled: false,
|
|
460
|
+
inheritProjectContext: true,
|
|
461
|
+
inheritSkills: true,
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
assert.equal(args.includes("--no-context-files"), false);
|
|
465
|
+
});
|
|
466
|
+
|
|
423
467
|
it("passes tool budget through env", () => {
|
|
424
468
|
const { env } = buildPiArgs({
|
|
425
469
|
baseArgs: ["-p"],
|
|
@@ -475,6 +519,8 @@ describe("buildPiArgs system prompt mode wiring", () => {
|
|
|
475
519
|
});
|
|
476
520
|
|
|
477
521
|
it("passes child intercom and orchestrator metadata through env", () => {
|
|
522
|
+
process.env[PI_INTERCOM_STABLE_ID_ENV] = "subagent-chat-parent";
|
|
523
|
+
process.env[PI_INTERCOM_SESSION_ID_ENV] = "session-parent-runtime";
|
|
478
524
|
const { env } = buildPiArgs({
|
|
479
525
|
baseArgs: ["-p"],
|
|
480
526
|
task: "hello",
|
|
@@ -490,6 +536,8 @@ describe("buildPiArgs system prompt mode wiring", () => {
|
|
|
490
536
|
});
|
|
491
537
|
|
|
492
538
|
assert.equal(env.SELESAI_SUBAGENT_INTERCOM_SESSION_NAME, "subagent-worker-78f659a3");
|
|
539
|
+
assert.equal(env[PI_INTERCOM_STABLE_ID_ENV], "subagent-worker-78f659a3");
|
|
540
|
+
assert.equal(env[PI_INTERCOM_SESSION_ID_ENV], undefined);
|
|
493
541
|
assert.equal(env.SELESAI_SUBAGENT_ORCHESTRATOR_TARGET, "subagent-chat-parent");
|
|
494
542
|
assert.equal(env[SUBAGENT_ORCHESTRATOR_SESSION_ID_ENV], "session-parent-123");
|
|
495
543
|
assert.equal(env.SELESAI_SUBAGENT_RUN_ID, "78f659a3");
|
|
@@ -499,6 +547,21 @@ describe("buildPiArgs system prompt mode wiring", () => {
|
|
|
499
547
|
assert.match(env[SUBAGENT_SUPERVISOR_CHANNEL_DIR_ENV] ?? "", /supervisor-channels/);
|
|
500
548
|
});
|
|
501
549
|
|
|
550
|
+
it("clears inherited pi-intercom identity when no child intercom session name is set", () => {
|
|
551
|
+
process.env[PI_INTERCOM_STABLE_ID_ENV] = "subagent-chat-parent";
|
|
552
|
+
process.env[PI_INTERCOM_SESSION_ID_ENV] = "session-parent-runtime";
|
|
553
|
+
const { env } = buildPiArgs({
|
|
554
|
+
baseArgs: ["-p"],
|
|
555
|
+
task: "hello",
|
|
556
|
+
sessionEnabled: false,
|
|
557
|
+
inheritProjectContext: true,
|
|
558
|
+
inheritSkills: true,
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
assert.equal(env[PI_INTERCOM_STABLE_ID_ENV], undefined);
|
|
562
|
+
assert.equal(env[PI_INTERCOM_SESSION_ID_ENV], undefined);
|
|
563
|
+
});
|
|
564
|
+
|
|
502
565
|
it("does not create a supervisor channel without an exact parent session id", () => {
|
|
503
566
|
const { env } = buildPiArgs({
|
|
504
567
|
baseArgs: ["-p"],
|
|
@@ -592,6 +655,46 @@ describe("buildPiArgs system prompt mode wiring", () => {
|
|
|
592
655
|
assert.equal(args[args.indexOf("--tools") + 1], "read,bash");
|
|
593
656
|
});
|
|
594
657
|
|
|
658
|
+
it("includes adapter tool filters in MCP cache identity", () => {
|
|
659
|
+
const base = { command: "npx", args: ["browser-mcp"] };
|
|
660
|
+
|
|
661
|
+
assert.notEqual(
|
|
662
|
+
computeMcpServerHash(base),
|
|
663
|
+
computeMcpServerHash({ ...base, includeTools: ["browser_navigate"] }),
|
|
664
|
+
);
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
it("matches pi-mcp-adapter 2.15.0 metadata cache hashes", () => {
|
|
668
|
+
process.env.MCP_HASH_ROOT = "/tmp/mcp-root";
|
|
669
|
+
process.env.MCP_HASH_TOKEN = "token-value";
|
|
670
|
+
|
|
671
|
+
assert.deepEqual(
|
|
672
|
+
[
|
|
673
|
+
computeMcpServerHash({
|
|
674
|
+
command: "npx",
|
|
675
|
+
args: ["-y", "browser-mcp"],
|
|
676
|
+
env: { ROOT: "{env:MCP_HASH_ROOT}", SECRET_COMMAND: "!op read test" },
|
|
677
|
+
cwd: "${MCP_HASH_ROOT}/server",
|
|
678
|
+
exposeResources: false,
|
|
679
|
+
includeTools: ["browser_navigate"],
|
|
680
|
+
excludeTools: ["browser_close"],
|
|
681
|
+
}),
|
|
682
|
+
computeMcpServerHash({
|
|
683
|
+
url: "https://example.test/$env:MCP_HASH_TOKEN",
|
|
684
|
+
headers: { Authorization: "Bearer ${MCP_HASH_TOKEN}", Secret: "!op read test" },
|
|
685
|
+
auth: "bearer",
|
|
686
|
+
bearerTokenEnv: "MCP_HASH_TOKEN",
|
|
687
|
+
}),
|
|
688
|
+
computeMcpServerHash({ socket: "{env:MCP_HASH_ROOT}/rmcp.sock" }),
|
|
689
|
+
],
|
|
690
|
+
[
|
|
691
|
+
"8af47bd5a801f42bd252789826df883c6f4db6f1d425b82f8561d75063fbe3a8",
|
|
692
|
+
"77db141e556d24c3740a4b4f0cd50d8c23309a28062282cc1ca724207359ef5a",
|
|
693
|
+
"77f7f77a3e8df990d8c78e99ca81508fd8d1f954e814ee7cbfa46719f71eb46b",
|
|
694
|
+
],
|
|
695
|
+
);
|
|
696
|
+
});
|
|
697
|
+
|
|
595
698
|
it("augments explicit builtin allowlists with selected direct MCP tool names", () => {
|
|
596
699
|
const fixture = createMcpFixture();
|
|
597
700
|
writeMcpFixture(fixture);
|
|
@@ -252,6 +252,24 @@ Package skill content.
|
|
|
252
252
|
assert.equal(history[1]?.taskHash, taskHash("legacy customer secret"));
|
|
253
253
|
});
|
|
254
254
|
|
|
255
|
+
it("re-sanitizes run history after an external write", () => {
|
|
256
|
+
recordRun("env-agent", "first secret", 0, 1);
|
|
257
|
+
const historyPath = path.join(agentDir, "run-history.jsonl");
|
|
258
|
+
fs.appendFileSync(historyPath, `${JSON.stringify({
|
|
259
|
+
agent: "env-agent",
|
|
260
|
+
task: "externally written secret",
|
|
261
|
+
ts: 2,
|
|
262
|
+
status: "ok",
|
|
263
|
+
duration: 2,
|
|
264
|
+
})}\n`);
|
|
265
|
+
|
|
266
|
+
recordRun("env-agent", "second secret", 0, 3);
|
|
267
|
+
|
|
268
|
+
const rawHistory = fs.readFileSync(historyPath, "utf-8");
|
|
269
|
+
assert.doesNotMatch(rawHistory, /first secret|externally written secret|second secret/);
|
|
270
|
+
assert.equal(loadRunsForAgent("env-agent").length, 3);
|
|
271
|
+
});
|
|
272
|
+
|
|
255
273
|
it("uses the configured agent dir for subagent bridge instruction files", () => {
|
|
256
274
|
const instructionPath = path.join(agentDir, "extensions", "subagent", "bridge.md");
|
|
257
275
|
writeFile(instructionPath, "Native bridge for {orchestratorTarget}");
|
|
@@ -107,6 +107,7 @@ Project prompt.
|
|
|
107
107
|
{ provider: "test", id: "fallback", fullId: "test/fallback" },
|
|
108
108
|
],
|
|
109
109
|
capabilityCeiling: ceiling,
|
|
110
|
+
artifacts: true,
|
|
110
111
|
});
|
|
111
112
|
|
|
112
113
|
assert.equal(result.ok, true);
|
|
@@ -143,6 +144,7 @@ Project prompt.
|
|
|
143
144
|
{ provider: "test", id: "fallback", fullId: "test/fallback" },
|
|
144
145
|
],
|
|
145
146
|
capabilityCeiling: ceiling,
|
|
147
|
+
artifacts: true,
|
|
146
148
|
});
|
|
147
149
|
assert.equal(repeated.ok, true);
|
|
148
150
|
assert.equal(repeated.contract.digest, result.contract.digest);
|
|
@@ -153,6 +155,52 @@ Project prompt.
|
|
|
153
155
|
}
|
|
154
156
|
});
|
|
155
157
|
|
|
158
|
+
it("resolves agent aliases to the canonical launch contract agent", async () => {
|
|
159
|
+
const cwd = path.join(tempDir, "repo-alias");
|
|
160
|
+
fs.mkdirSync(cwd, { recursive: true });
|
|
161
|
+
writeAgent(path.join(cwd, ".selesai", "agents", "worker.md"), `---
|
|
162
|
+
name: worker
|
|
163
|
+
description: Project worker
|
|
164
|
+
aliases: shipper, worker-alias
|
|
165
|
+
---
|
|
166
|
+
Project prompt.
|
|
167
|
+
`);
|
|
168
|
+
|
|
169
|
+
const result = await resolveSubagentLaunchContract({
|
|
170
|
+
agent: "shipper",
|
|
171
|
+
cwd,
|
|
172
|
+
task: "Implement the change",
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
assert.equal(result.ok, true);
|
|
176
|
+
assert.equal(result.contract.agent.name, "worker");
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("reports alias collisions as ambiguous agents", async () => {
|
|
180
|
+
const cwd = path.join(tempDir, "repo-alias-collision");
|
|
181
|
+
fs.mkdirSync(cwd, { recursive: true });
|
|
182
|
+
writeAgent(path.join(cwd, ".selesai", "agents", "worker.md"), `---
|
|
183
|
+
name: worker
|
|
184
|
+
description: Project worker
|
|
185
|
+
aliases: shared-alias
|
|
186
|
+
---
|
|
187
|
+
Project prompt.
|
|
188
|
+
`);
|
|
189
|
+
writeAgent(path.join(cwd, ".selesai", "agents", "reviewer.md"), `---
|
|
190
|
+
name: reviewer
|
|
191
|
+
description: Project reviewer
|
|
192
|
+
aliases: shared-alias
|
|
193
|
+
---
|
|
194
|
+
Review prompt.
|
|
195
|
+
`);
|
|
196
|
+
|
|
197
|
+
const result = await resolveSubagentLaunchContract({ agent: "shared-alias", cwd });
|
|
198
|
+
|
|
199
|
+
assert.equal(result.ok, false);
|
|
200
|
+
assert.equal(result.code, "ambiguous_agent");
|
|
201
|
+
assert.match(result.message, /Ambiguous agent alias 'shared-alias': reviewer, worker|Ambiguous agent alias 'shared-alias': worker, reviewer/);
|
|
202
|
+
});
|
|
203
|
+
|
|
156
204
|
it("changes definition and launch digests when selected agent content changes", async () => {
|
|
157
205
|
const cwd = path.join(tempDir, "repo");
|
|
158
206
|
fs.mkdirSync(cwd, { recursive: true });
|
|
@@ -3,7 +3,7 @@ import assert from "node:assert/strict";
|
|
|
3
3
|
|
|
4
4
|
import { visibleWidth } from "@earendil-works/pi-tui";
|
|
5
5
|
import { row } from "../../src/tui/render-helpers.ts";
|
|
6
|
-
import { renderSubagentResult } from "../../src/tui/render.ts";
|
|
6
|
+
import { renderSubagentResult, truncLine } from "../../src/tui/render.ts";
|
|
7
7
|
|
|
8
8
|
const theme = {
|
|
9
9
|
fg(_name: string, text: string): string {
|
|
@@ -49,6 +49,18 @@ test("row keeps styled multiline content within the available width", () => {
|
|
|
49
49
|
assert.doesNotMatch(rendered, /[\r\n\t]/);
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
+
test("truncLine preserves ANSI styles and resets through the ellipsis", () => {
|
|
53
|
+
assert.equal(truncLine("\u001b[31mabcdef\u001b[0m", 4), "\u001b[31mabc\u001b[31m…");
|
|
54
|
+
assert.equal(truncLine("\u001b[31mab\u001b[0mcdef", 4), "\u001b[31mab\u001b[0mc…");
|
|
55
|
+
assert.equal(truncLine("ab\u001b[xcd", 4), "ab\u001b[…");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("truncLine respects grapheme display width", () => {
|
|
59
|
+
const rendered = truncLine("🙂🙂🙂", 5);
|
|
60
|
+
assert.equal(rendered, "🙂🙂…");
|
|
61
|
+
assert.equal(visibleWidth(rendered), 5);
|
|
62
|
+
});
|
|
63
|
+
|
|
52
64
|
test("compact chain rendering uses workflow graph spans for dynamic fanout results", () => {
|
|
53
65
|
const component = renderSubagentResult({
|
|
54
66
|
content: [{ type: "text", text: "done" }],
|
|
@@ -43,13 +43,14 @@ describe("result intercom formatter", () => {
|
|
|
43
43
|
assert.match(payload.message, /^subagent results/m);
|
|
44
44
|
assert.match(payload.message, /Run: run-123/);
|
|
45
45
|
assert.match(payload.message, /Mode: chain/);
|
|
46
|
-
assert.match(payload.message, /
|
|
46
|
+
assert.match(payload.message, /Process status: failed/);
|
|
47
47
|
assert.match(payload.message, /Children: 1 completed, 1 failed/);
|
|
48
|
+
assert.match(payload.message, /Outputs: 2 unknown \(semantic adequacy unassessed\)/);
|
|
48
49
|
assert.match(payload.message, /Chain steps: 4/);
|
|
49
50
|
assert.match(payload.message, /Intercom targets below identify child sessions used while they were running/);
|
|
50
|
-
assert.match(payload.message, /1\. reviewer-a — completed/);
|
|
51
|
+
assert.match(payload.message, /1\. reviewer-a — process completed · output unknown/);
|
|
51
52
|
assert.match(payload.message, /Run intercom target: subagent-reviewer-a-run-123-1/);
|
|
52
|
-
assert.match(payload.message, /2\. reviewer-b — failed/);
|
|
53
|
+
assert.match(payload.message, /2\. reviewer-b — process failed · output unknown/);
|
|
53
54
|
assert.match(payload.message, /Output artifact: \/tmp\/a\.md/);
|
|
54
55
|
assert.match(payload.message, /Session: \/tmp\/a-session\.jsonl/);
|
|
55
56
|
});
|
|
@@ -161,6 +162,27 @@ describe("result intercom formatter", () => {
|
|
|
161
162
|
assert.match(payload.message, /↳ reviewer — complete \[nested-a\]/);
|
|
162
163
|
});
|
|
163
164
|
|
|
165
|
+
it("separates process failure from output presence and gives salvage guidance", () => {
|
|
166
|
+
const payload = buildSubagentResultIntercomPayload({
|
|
167
|
+
to: "chat",
|
|
168
|
+
runId: "run-salvage",
|
|
169
|
+
mode: "parallel",
|
|
170
|
+
source: "foreground",
|
|
171
|
+
children: [
|
|
172
|
+
{ agent: "a", status: "completed", outputState: "present", summary: "answer a" },
|
|
173
|
+
{ agent: "b", status: "failed", outputState: "present", summary: "runner crashed after answer b" },
|
|
174
|
+
{ agent: "c", status: "failed", outputState: "absent", summary: "startup failed" },
|
|
175
|
+
],
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
assert.equal(payload.status, "failed");
|
|
179
|
+
assert.equal(payload.children[1]?.outputState, "present");
|
|
180
|
+
assert.match(payload.message, /Process status: failed/);
|
|
181
|
+
assert.match(payload.message, /Outputs: 2 present, 1 absent \(semantic adequacy unassessed\)/);
|
|
182
|
+
assert.match(payload.message, /Inspect that output before retrying/);
|
|
183
|
+
assert.match(payload.message, /2\. b — process failed · output present/);
|
|
184
|
+
});
|
|
185
|
+
|
|
164
186
|
it("keeps full child summaries inside grouped payloads", () => {
|
|
165
187
|
const longSummary = `${"x".repeat(2000)}\n${"y".repeat(2000)}`;
|
|
166
188
|
const payload = buildSubagentResultIntercomPayload({
|
|
@@ -217,9 +239,12 @@ describe("result intercom formatter", () => {
|
|
|
217
239
|
assert.equal(stripped.results[0]?.truncation, undefined);
|
|
218
240
|
});
|
|
219
241
|
|
|
220
|
-
it("resolves paused and
|
|
242
|
+
it("resolves paused, detached, and signal-terminated statuses", () => {
|
|
221
243
|
assert.equal(resolveSubagentResultStatus({ interrupted: true }), "paused");
|
|
222
244
|
assert.equal(resolveSubagentResultStatus({ detached: true }), "detached");
|
|
245
|
+
assert.equal(resolveSubagentResultStatus({ processSignal: "SIGTERM", exitCode: 1 }), "stopped");
|
|
246
|
+
assert.equal(resolveSubagentResultStatus({ processSignal: "SIGTERM", exitCode: 1, timedOut: true }), "failed");
|
|
247
|
+
assert.equal(resolveSubagentResultStatus({ processSignal: "SIGTERM", exitCode: 0, success: true }), "completed");
|
|
223
248
|
assert.equal(resolveSubagentResultStatus({ success: true }), "completed");
|
|
224
249
|
assert.equal(resolveSubagentResultStatus({ exitCode: 1 }), "failed");
|
|
225
250
|
});
|
|
@@ -42,12 +42,12 @@ function once(events: FakeEvents, event: string): Promise<unknown> {
|
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
function ctx() {
|
|
45
|
+
function ctx(sessionId = "session-123", sessionFile = "/sessions/parent.jsonl") {
|
|
46
46
|
return {
|
|
47
47
|
cwd: "/repo",
|
|
48
48
|
sessionManager: {
|
|
49
|
-
getSessionId: () =>
|
|
50
|
-
getSessionFile: () =>
|
|
49
|
+
getSessionId: () => sessionId,
|
|
50
|
+
getSessionFile: () => sessionFile,
|
|
51
51
|
},
|
|
52
52
|
} as any;
|
|
53
53
|
}
|
|
@@ -91,6 +91,14 @@ describe("subagent extension RPC bridge", () => {
|
|
|
91
91
|
(reply as { data: { capabilities?: { nonRecoveringSteer?: boolean } } }).data.capabilities?.nonRecoveringSteer,
|
|
92
92
|
true,
|
|
93
93
|
);
|
|
94
|
+
assert.equal(
|
|
95
|
+
(reply as { data: { capabilities?: { resume?: boolean } } }).data.capabilities?.resume,
|
|
96
|
+
true,
|
|
97
|
+
);
|
|
98
|
+
assert.deepEqual(
|
|
99
|
+
(reply as { data: { capabilities?: { fleetStatus?: unknown } } }).data.capabilities?.fleetStatus,
|
|
100
|
+
{ version: 1 },
|
|
101
|
+
);
|
|
94
102
|
|
|
95
103
|
bridge.dispose();
|
|
96
104
|
});
|
|
@@ -137,7 +145,149 @@ describe("subagent extension RPC bridge", () => {
|
|
|
137
145
|
assert.equal(reply.success, true);
|
|
138
146
|
assert.deepEqual(executedParams, { action: "status", id: "abc123" });
|
|
139
147
|
assert.equal((reply as { data: { text?: string } }).data.text, "Run: abc123");
|
|
148
|
+
assert.deepEqual((reply as { data: { fleet?: unknown } }).data.fleet, {
|
|
149
|
+
version: 1, entries: [], totalActive: 0, omitted: 0,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
bridge.dispose();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("projects bounded display-safe active fleet records without internal ids", async () => {
|
|
156
|
+
const events = new FakeEvents();
|
|
157
|
+
const state = {
|
|
158
|
+
currentSessionId: "/sessions/parent.jsonl",
|
|
159
|
+
foregroundControls: new Map(),
|
|
160
|
+
asyncJobs: new Map([["async-private-id", {
|
|
161
|
+
asyncId: "async-private-id", sessionId: "/sessions/parent.jsonl", status: "running", mode: "single",
|
|
162
|
+
description: ["Review", "\u001b]8;;hostile\u0007", "the diff"].join("\n"),
|
|
163
|
+
startedAt: 100, steps: [{ agent: "reviewer", label: "opaque label", status: "running", startedAt: 120, model: "anthropic/claude-opus-4-8:high", thinking: "high", tokens: { input: 12, output: 34, total: 46 } }],
|
|
164
|
+
}]]),
|
|
165
|
+
} as any;
|
|
166
|
+
const bridge = registerSubagentRpcBridge({
|
|
167
|
+
events, getContext: () => ctx("runtime-session-id", "/sessions/parent.jsonl"), state,
|
|
168
|
+
execute: async () => ({ content: [{ type: "text", text: "Active async runs: 1" }], details: { mode: "management", results: [] } } as any),
|
|
169
|
+
});
|
|
170
|
+
const reply = await request(events, "fleet-status", "status");
|
|
171
|
+
const fleet = (reply as { data: { fleet: { entries: Array<Record<string, unknown>> } } }).data.fleet;
|
|
172
|
+
assert.equal(fleet.entries.length, 1);
|
|
173
|
+
assert.equal((fleet as { totalActive?: number }).totalActive, 1);
|
|
174
|
+
assert.equal((fleet as { omitted?: number }).omitted, 0);
|
|
175
|
+
assert.deepEqual(fleet.entries[0], {
|
|
176
|
+
key: "fleet-1", agent: "reviewer", role: "opaque label", model: "anthropic/claude-opus-4-8:high", effort: "high",
|
|
177
|
+
startedAt: 120, tokens: { input: 12, output: 34, total: 46 }, goal: "Review the diff",
|
|
178
|
+
});
|
|
179
|
+
assert.equal(JSON.stringify(fleet).includes("async-private-id"), false);
|
|
180
|
+
bridge.dispose();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("projects resolved foreground model, effort, split usage, and goal", async () => {
|
|
184
|
+
const events = new FakeEvents();
|
|
185
|
+
const state = {
|
|
186
|
+
currentSessionId: "session-123",
|
|
187
|
+
foregroundControls: new Map([["private-run", {
|
|
188
|
+
runId: "private-run",
|
|
189
|
+
sessionId: "session-123",
|
|
190
|
+
mode: "single",
|
|
191
|
+
startedAt: 90,
|
|
192
|
+
activeChildren: new Map([[0, {
|
|
193
|
+
index: 0,
|
|
194
|
+
agent: "worker",
|
|
195
|
+
description: "Implement the fix",
|
|
196
|
+
startedAt: 100,
|
|
197
|
+
updatedAt: 110,
|
|
198
|
+
model: "openai/gpt-5.6-terra:high",
|
|
199
|
+
thinking: "high",
|
|
200
|
+
inputTokens: 321,
|
|
201
|
+
outputTokens: 45,
|
|
202
|
+
tokens: 366,
|
|
203
|
+
}]]),
|
|
204
|
+
}]]),
|
|
205
|
+
asyncJobs: new Map(),
|
|
206
|
+
} as any;
|
|
207
|
+
state.foregroundControls.set("private-old", {
|
|
208
|
+
runId: "private-old",
|
|
209
|
+
sessionId: "old-session",
|
|
210
|
+
mode: "single",
|
|
211
|
+
startedAt: 50,
|
|
212
|
+
currentAgent: "reviewer",
|
|
213
|
+
description: "Old work",
|
|
214
|
+
});
|
|
215
|
+
const bridge = registerSubagentRpcBridge({
|
|
216
|
+
events,
|
|
217
|
+
getContext: () => ctx("session-123", "session-123"),
|
|
218
|
+
state,
|
|
219
|
+
execute: async () => ({ content: [], details: { mode: "management", results: [] } } as any),
|
|
220
|
+
});
|
|
221
|
+
const reply = await request(events, "foreground-fleet", "status");
|
|
222
|
+
assert.deepEqual((reply as any).data.fleet, {
|
|
223
|
+
version: 1,
|
|
224
|
+
totalActive: 1,
|
|
225
|
+
omitted: 0,
|
|
226
|
+
entries: [{
|
|
227
|
+
key: "fleet-1",
|
|
228
|
+
agent: "worker",
|
|
229
|
+
model: "openai/gpt-5.6-terra:high",
|
|
230
|
+
effort: "high",
|
|
231
|
+
startedAt: 100,
|
|
232
|
+
tokens: { input: 321, output: 45, total: 366 },
|
|
233
|
+
goal: "Implement the fix",
|
|
234
|
+
}],
|
|
235
|
+
});
|
|
236
|
+
assert.equal(JSON.stringify((reply as any).data.fleet).includes("private-run"), false);
|
|
237
|
+
bridge.dispose();
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it("uses monotonic opaque keys across removal/insertion and resets them per session", async () => {
|
|
241
|
+
const events = new FakeEvents();
|
|
242
|
+
const jobs = new Map<string, any>([
|
|
243
|
+
["private-a", { asyncId: "private-a", sessionId: "A", status: "running", mode: "single", startedAt: 1, agents: ["alpha"] }],
|
|
244
|
+
["private-b", { asyncId: "private-b", sessionId: "A", status: "running", mode: "single", startedAt: 2, agents: ["beta"] }],
|
|
245
|
+
["private-unattributed", { asyncId: "private-unattributed", status: "running", mode: "single", startedAt: 3, agents: ["unknown"] }],
|
|
246
|
+
]);
|
|
247
|
+
const state = { currentSessionId: "A", foregroundControls: new Map(), asyncJobs: jobs } as any;
|
|
248
|
+
let activeSession = "A";
|
|
249
|
+
const bridge = registerSubagentRpcBridge({ events, getContext: () => ctx(activeSession, activeSession), state, execute: async () => ({ content: [], details: { mode: "management", results: [] } } as any) });
|
|
250
|
+
const keys = async (id: string) => ((await request(events, id, "status")) as any).data.fleet.entries.map((entry: { key: string }) => entry.key);
|
|
251
|
+
assert.deepEqual(await keys("keys-a"), ["fleet-1", "fleet-2"]);
|
|
252
|
+
jobs.delete("private-b"); jobs.set("private-c", { asyncId: "private-c", sessionId: "A", status: "running", mode: "single", startedAt: 3, agents: ["gamma"] });
|
|
253
|
+
assert.deepEqual(await keys("keys-b"), ["fleet-1", "fleet-3"]);
|
|
254
|
+
state.currentSessionId = "B";
|
|
255
|
+
activeSession = "B";
|
|
256
|
+
assert.deepEqual(await keys("keys-c"), []);
|
|
257
|
+
jobs.set("private-d", { asyncId: "private-d", sessionId: "B", status: "running", mode: "single", startedAt: 4, agents: ["delta"] });
|
|
258
|
+
assert.deepEqual(await keys("keys-d"), ["fleet-1"]);
|
|
259
|
+
bridge.dispose();
|
|
260
|
+
});
|
|
140
261
|
|
|
262
|
+
it("reports bounded overflow and excludes unattributed or foreign-session jobs", async () => {
|
|
263
|
+
const events = new FakeEvents();
|
|
264
|
+
const jobs = new Map<string, any>();
|
|
265
|
+
for (let index = 0; index < 18; index += 1) {
|
|
266
|
+
jobs.set(`private-${index}`, {
|
|
267
|
+
asyncId: `private-${index}`,
|
|
268
|
+
sessionId: "session-123",
|
|
269
|
+
status: "running",
|
|
270
|
+
mode: "single",
|
|
271
|
+
startedAt: index + 1,
|
|
272
|
+
agents: [`worker-${index}`],
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
jobs.set("unattributed", { asyncId: "unattributed", status: "running", mode: "single", startedAt: 20, agents: ["hidden"] });
|
|
276
|
+
jobs.set("foreign", { asyncId: "foreign", sessionId: "other", status: "running", mode: "single", startedAt: 21, agents: ["hidden"] });
|
|
277
|
+
const state = { currentSessionId: "session-123", foregroundControls: new Map(), asyncJobs: jobs } as any;
|
|
278
|
+
const bridge = registerSubagentRpcBridge({
|
|
279
|
+
events,
|
|
280
|
+
getContext: () => ctx("session-123", "session-123"),
|
|
281
|
+
state,
|
|
282
|
+
execute: async () => ({ content: [], details: { mode: "management", results: [] } } as any),
|
|
283
|
+
});
|
|
284
|
+
const reply = await request(events, "fleet-overflow", "status");
|
|
285
|
+
const fleet = (reply as any).data.fleet;
|
|
286
|
+
assert.equal(fleet.entries.length, 16);
|
|
287
|
+
assert.equal(fleet.totalActive, 18);
|
|
288
|
+
assert.equal(fleet.omitted, 2);
|
|
289
|
+
assert.equal(JSON.stringify(fleet).includes("unattributed"), false);
|
|
290
|
+
assert.equal(JSON.stringify(fleet).includes("foreign"), false);
|
|
141
291
|
bridge.dispose();
|
|
142
292
|
});
|
|
143
293
|
|
|
@@ -298,6 +448,75 @@ describe("subagent extension RPC bridge", () => {
|
|
|
298
448
|
bridge.dispose();
|
|
299
449
|
});
|
|
300
450
|
|
|
451
|
+
it("delegates resume through the existing package-owned revival action", async () => {
|
|
452
|
+
const events = new FakeEvents();
|
|
453
|
+
let executedParams: unknown;
|
|
454
|
+
const bridge = registerSubagentRpcBridge({
|
|
455
|
+
events,
|
|
456
|
+
getContext: () => ctx(),
|
|
457
|
+
execute: async (_id, params) => {
|
|
458
|
+
executedParams = params;
|
|
459
|
+
return {
|
|
460
|
+
content: [{ type: "text", text: "Revived async subagent from run-1." }],
|
|
461
|
+
details: { mode: "single", results: [], asyncId: "run-2", asyncDir: "/tmp/run-2" },
|
|
462
|
+
} as any;
|
|
463
|
+
},
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
const reply = await request(events, "resume-1", "resume", {
|
|
467
|
+
id: "run-1",
|
|
468
|
+
index: 0,
|
|
469
|
+
message: " Continue with the focused review. ",
|
|
470
|
+
output: " /tmp/revived-output.md ",
|
|
471
|
+
outputMode: "file-only",
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
assert.equal(reply.success, true);
|
|
475
|
+
assert.deepEqual(executedParams, {
|
|
476
|
+
action: "resume",
|
|
477
|
+
id: "run-1",
|
|
478
|
+
index: 0,
|
|
479
|
+
message: "Continue with the focused review.",
|
|
480
|
+
output: "/tmp/revived-output.md",
|
|
481
|
+
outputMode: "file-only",
|
|
482
|
+
});
|
|
483
|
+
assert.equal((reply as { data: { details?: { asyncId?: string } } }).data.details?.asyncId, "run-2");
|
|
484
|
+
|
|
485
|
+
bridge.dispose();
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
it("rejects targetless or empty RPC resume before executor dispatch", async () => {
|
|
489
|
+
const events = new FakeEvents();
|
|
490
|
+
let executeCalls = 0;
|
|
491
|
+
const bridge = registerSubagentRpcBridge({
|
|
492
|
+
events,
|
|
493
|
+
getContext: () => ctx(),
|
|
494
|
+
execute: async () => {
|
|
495
|
+
executeCalls++;
|
|
496
|
+
return { content: [], details: { mode: "management", results: [] } } as any;
|
|
497
|
+
},
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
const targetless = await request(events, "resume-no-target", "resume", { message: "continue" });
|
|
501
|
+
const empty = await request(events, "resume-empty", "resume", { id: "run-1", message: " " });
|
|
502
|
+
const inlineOutput = await request(events, "resume-inline", "resume", {
|
|
503
|
+
id: "run-1",
|
|
504
|
+
message: "continue",
|
|
505
|
+
output: "/tmp/output.md",
|
|
506
|
+
outputMode: "inline",
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
assert.equal(targetless.success, false);
|
|
510
|
+
assert.equal((targetless as { error: { code: string } }).error.code, "invalid_params");
|
|
511
|
+
assert.match((targetless as { error: { message: string } }).error.message, /requires id, runId, or dir/);
|
|
512
|
+
assert.equal(empty.success, false);
|
|
513
|
+
assert.equal((empty as { error: { code: string } }).error.code, "invalid_params");
|
|
514
|
+
assert.equal(inlineOutput.success, false);
|
|
515
|
+
assert.match((inlineOutput as { error: { message: string } }).error.message, /file-only/);
|
|
516
|
+
assert.equal(executeCalls, 0);
|
|
517
|
+
bridge.dispose();
|
|
518
|
+
});
|
|
519
|
+
|
|
301
520
|
it("delegates interrupt through the existing executor action", async () => {
|
|
302
521
|
const events = new FakeEvents();
|
|
303
522
|
let executedParams: unknown;
|
|
@@ -328,7 +547,7 @@ describe("subagent extension RPC bridge", () => {
|
|
|
328
547
|
fs.mkdirSync(asyncDir, { recursive: true });
|
|
329
548
|
fs.writeFileSync(path.join(asyncDir, "status.json"), JSON.stringify({
|
|
330
549
|
runId: "run-stop",
|
|
331
|
-
sessionId: "
|
|
550
|
+
sessionId: "/sessions/parent.jsonl",
|
|
332
551
|
mode: "single",
|
|
333
552
|
state: "running",
|
|
334
553
|
pid: 4242,
|
|
@@ -994,6 +994,36 @@ describe("async run status inspection", () => {
|
|
|
994
994
|
}
|
|
995
995
|
});
|
|
996
996
|
|
|
997
|
+
it("shows signal-terminated result-only runs as stopped", () => {
|
|
998
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-run-status-signal-result-"));
|
|
999
|
+
try {
|
|
1000
|
+
const asyncRoot = path.join(root, "runs");
|
|
1001
|
+
const resultsDir = path.join(root, "results");
|
|
1002
|
+
fs.mkdirSync(path.join(asyncRoot, "run-signal-result"), { recursive: true });
|
|
1003
|
+
fs.mkdirSync(resultsDir, { recursive: true });
|
|
1004
|
+
fs.writeFileSync(path.join(resultsDir, "run-signal-result.json"), JSON.stringify({
|
|
1005
|
+
id: "run-signal-result",
|
|
1006
|
+
agent: "worker",
|
|
1007
|
+
success: false,
|
|
1008
|
+
state: "failed",
|
|
1009
|
+
summary: "Subagent process terminated by signal SIGTERM.",
|
|
1010
|
+
results: [{ agent: "worker", success: false, exitCode: 1, processSignal: "SIGTERM" }],
|
|
1011
|
+
}, null, 2), "utf-8");
|
|
1012
|
+
|
|
1013
|
+
const result = inspectSubagentStatus({ id: "run-signal-result" }, {
|
|
1014
|
+
asyncDirRoot: asyncRoot,
|
|
1015
|
+
resultsDir,
|
|
1016
|
+
});
|
|
1017
|
+
|
|
1018
|
+
const text = textContent(result);
|
|
1019
|
+
assert.equal(result.isError, undefined);
|
|
1020
|
+
assert.match(text, /State: stopped/);
|
|
1021
|
+
assert.match(text, /Resume: unavailable; stopped runs are not resumable/);
|
|
1022
|
+
} finally {
|
|
1023
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
1024
|
+
}
|
|
1025
|
+
});
|
|
1026
|
+
|
|
997
1027
|
it("falls back to an existing result when async dir has no status file", () => {
|
|
998
1028
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-run-status-result-fallback-"));
|
|
999
1029
|
try {
|