@selesai/code 0.5.28 → 0.5.29
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/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 +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- 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/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 +85 -0
- package/dist/extensions/pi-subagents/LICENSE +21 -0
- package/dist/extensions/pi-subagents/README.md +146 -52
- package/dist/extensions/pi-subagents/agents/architect.md +1 -0
- package/dist/extensions/pi-subagents/agents/builder.md +1 -0
- 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 +21 -989
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +256 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +430 -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 +282 -0
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +71 -30
- 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/api/delegation.ts +3 -0
- package/dist/extensions/pi-subagents/src/api/preflight.ts +16 -12
- package/dist/extensions/pi-subagents/src/extension/chain-validation.ts +17 -1
- package/dist/extensions/pi-subagents/src/extension/index.ts +17 -7
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +248 -6
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +31 -6
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +8 -7
- 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 +63 -16
- 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 +29 -4
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +29 -6
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +29 -3
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +372 -28
- 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 +143 -43
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +527 -247
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +42 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +426 -123
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +15 -7
- 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/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 +1 -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 +155 -10
- package/dist/extensions/pi-subagents/src/shared/utils.ts +73 -4
- package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +5 -0
- 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 +27 -28
- 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 +2 -2
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +348 -7
- 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 +26 -1
- 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 +3 -3
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +213 -13
- package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +34 -0
- 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 +118 -12
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +850 -7
- 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/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-frontmatter.test.ts +128 -0
- 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/capability-ceiling-agent-allowlist.test.ts +102 -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 +4 -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 +11 -11
- 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 +10 -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 +46 -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 +34 -2
- 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-intent.test.ts +3 -0
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +1 -1
- 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/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/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 +15 -6
- package/dist/extensions/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
|
@@ -32,7 +32,7 @@ const LEVEL_RANK: Record<Exclude<AcceptanceLevel, "auto">, number> = {
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
const VALID_LEVELS = new Set<AcceptanceLevel>(["auto", "none", "attested", "checked", "verified"]);
|
|
35
|
-
const
|
|
35
|
+
const VALID_EVIDENCE_KINDS: AcceptanceEvidenceKind[] = [
|
|
36
36
|
"changed-files",
|
|
37
37
|
"tests-added",
|
|
38
38
|
"commands-run",
|
|
@@ -42,7 +42,10 @@ const VALID_EVIDENCE = new Set<AcceptanceEvidenceKind>([
|
|
|
42
42
|
"diff-summary",
|
|
43
43
|
"review-findings",
|
|
44
44
|
"manual-notes",
|
|
45
|
-
]
|
|
45
|
+
];
|
|
46
|
+
const VALID_EVIDENCE = new Set<AcceptanceEvidenceKind>(VALID_EVIDENCE_KINDS);
|
|
47
|
+
const ACCEPTANCE_EVIDENCE_HELP = `Supported evidence kinds: ${VALID_EVIDENCE_KINDS.join(", ")}. Example: { level: "checked", evidence: ["commands-run", "changed-files"] }.`;
|
|
48
|
+
const ACCEPTANCE_OBJECT_EXAMPLE = "Example: { level: \"checked\", evidence: [\"commands-run\", \"changed-files\"] }.";
|
|
46
49
|
const ACCEPTANCE_CONFIG_KEYS = new Set(["level", "criteria", "evidence", "verify", "review", "stopRules", "reason"]);
|
|
47
50
|
const ACCEPTANCE_GATE_KEYS = new Set(["id", "must", "evidence", "severity"]);
|
|
48
51
|
const ACCEPTANCE_VERIFY_KEYS = new Set(["id", "command", "timeoutMs", "cwd", "env", "allowFailure"]);
|
|
@@ -153,6 +156,11 @@ function explicitAcceptanceCanDisable(explicit: AcceptanceConfig): boolean {
|
|
|
153
156
|
return explicit.level === "none" && typeof explicit.reason === "string" && explicit.reason.trim().length > 0;
|
|
154
157
|
}
|
|
155
158
|
|
|
159
|
+
function unsupportedEvidenceKindMessage(pathLabel: string, item: unknown): string {
|
|
160
|
+
const value = typeof item === "string" ? ` "${item}"` : "";
|
|
161
|
+
return `${pathLabel}${value} is not a supported evidence kind. ${ACCEPTANCE_EVIDENCE_HELP}`;
|
|
162
|
+
}
|
|
163
|
+
|
|
156
164
|
export function validateAcceptanceInput(input: unknown, pathLabel = "acceptance"): string[] {
|
|
157
165
|
const errors: string[] = [];
|
|
158
166
|
if (input === undefined) return errors;
|
|
@@ -164,7 +172,7 @@ export function validateAcceptanceInput(input: unknown, pathLabel = "acceptance"
|
|
|
164
172
|
return errors;
|
|
165
173
|
}
|
|
166
174
|
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
167
|
-
errors.push(`${pathLabel} must be a string level, false, or an object
|
|
175
|
+
errors.push(`${pathLabel} must be a string level, false, or an object. ${ACCEPTANCE_OBJECT_EXAMPLE}`);
|
|
168
176
|
return errors;
|
|
169
177
|
}
|
|
170
178
|
const value = input as Record<string, unknown>;
|
|
@@ -202,11 +210,11 @@ export function validateAcceptanceInput(input: unknown, pathLabel = "acceptance"
|
|
|
202
210
|
criterionIds.add(normalizedId);
|
|
203
211
|
}
|
|
204
212
|
if (typeof gate.must !== "string" || !gate.must.trim()) errors.push(`${criterionPath}.must is required.`);
|
|
205
|
-
if (gate.evidence !== undefined && !Array.isArray(gate.evidence)) errors.push(`${criterionPath}.evidence must be an array
|
|
213
|
+
if (gate.evidence !== undefined && !Array.isArray(gate.evidence)) errors.push(`${criterionPath}.evidence must be an array. ${ACCEPTANCE_EVIDENCE_HELP}`);
|
|
206
214
|
if (Array.isArray(gate.evidence)) {
|
|
207
215
|
for (const [evidenceIndex, item] of gate.evidence.entries()) {
|
|
208
216
|
if (typeof item !== "string" || !VALID_EVIDENCE.has(item as AcceptanceEvidenceKind)) {
|
|
209
|
-
errors.push(`${criterionPath}.evidence[${evidenceIndex}]
|
|
217
|
+
errors.push(unsupportedEvidenceKindMessage(`${criterionPath}.evidence[${evidenceIndex}]`, item));
|
|
210
218
|
}
|
|
211
219
|
}
|
|
212
220
|
}
|
|
@@ -218,11 +226,11 @@ export function validateAcceptanceInput(input: unknown, pathLabel = "acceptance"
|
|
|
218
226
|
if (Array.isArray(value.evidence)) {
|
|
219
227
|
for (const [index, item] of value.evidence.entries()) {
|
|
220
228
|
if (typeof item !== "string" || !VALID_EVIDENCE.has(item as AcceptanceEvidenceKind)) {
|
|
221
|
-
errors.push(`${pathLabel}.evidence[${index}]
|
|
229
|
+
errors.push(unsupportedEvidenceKindMessage(`${pathLabel}.evidence[${index}]`, item));
|
|
222
230
|
}
|
|
223
231
|
}
|
|
224
232
|
} else if (value.evidence !== undefined) {
|
|
225
|
-
errors.push(`${pathLabel}.evidence must be an array
|
|
233
|
+
errors.push(`${pathLabel}.evidence must be an array. ${ACCEPTANCE_EVIDENCE_HELP}`);
|
|
226
234
|
}
|
|
227
235
|
if (value.verify !== undefined && !Array.isArray(value.verify)) errors.push(`${pathLabel}.verify must be an array.`);
|
|
228
236
|
if (Array.isArray(value.verify)) {
|
|
@@ -6,12 +6,14 @@ export const SUBAGENT_CAPABILITY_CEILING_REGISTRY_KEY = "pi-subagents.capability
|
|
|
6
6
|
export const SUBAGENT_CAPABILITY_CEILING_ENV = "SELESAI_SUBAGENT_CAPABILITY_CEILING_V1";
|
|
7
7
|
|
|
8
8
|
export type SubagentCapabilityCeiling =
|
|
9
|
-
| { allowedTools: readonly string[]; denyExtensions?: boolean }
|
|
10
|
-
| { allowedTools?: readonly string[]; denyExtensions: boolean }
|
|
9
|
+
| { allowedTools: readonly string[]; allowedAgents?: readonly string[]; denyExtensions?: boolean }
|
|
10
|
+
| { allowedTools?: readonly string[]; allowedAgents?: readonly string[]; denyExtensions: boolean }
|
|
11
|
+
| { allowedTools?: readonly string[]; allowedAgents: readonly string[]; denyExtensions?: boolean };
|
|
11
12
|
|
|
12
13
|
export interface ResolvedSubagentCapabilityCeiling {
|
|
13
14
|
version: typeof SUBAGENT_CAPABILITY_CEILING_VERSION;
|
|
14
15
|
allowedTools?: string[];
|
|
16
|
+
allowedAgents?: string[];
|
|
15
17
|
denyExtensions: boolean;
|
|
16
18
|
sources: string[];
|
|
17
19
|
}
|
|
@@ -26,6 +28,8 @@ export interface SubagentCapabilityAudit {
|
|
|
26
28
|
removedExtensionCount: number;
|
|
27
29
|
requestedMcpToolCount: number;
|
|
28
30
|
effectiveMcpTools: string[];
|
|
31
|
+
agentAllowed: boolean;
|
|
32
|
+
agentRestrictionSources?: string[];
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
export interface RegisterSubagentCapabilityCeilingOptions {
|
|
@@ -62,23 +66,28 @@ function validateText(value: unknown, field: string): string {
|
|
|
62
66
|
function normalizeCeiling(ceiling: SubagentCapabilityCeiling): ResolvedSubagentCapabilityCeiling {
|
|
63
67
|
if (!ceiling || typeof ceiling !== "object" || Array.isArray(ceiling)) throw new Error("Invalid capability ceiling; expected an object.");
|
|
64
68
|
const hasAllowedTools = Object.hasOwn(ceiling, "allowedTools");
|
|
69
|
+
const hasAllowedAgents = Object.hasOwn(ceiling, "allowedAgents");
|
|
65
70
|
const hasDenyExtensions = Object.hasOwn(ceiling, "denyExtensions");
|
|
66
|
-
if (!hasAllowedTools && !hasDenyExtensions) throw new Error("Invalid capability ceiling; expected allowedTools or denyExtensions.");
|
|
71
|
+
if (!hasAllowedTools && !hasAllowedAgents && !hasDenyExtensions) throw new Error("Invalid capability ceiling; expected allowedTools, allowedAgents, or denyExtensions.");
|
|
67
72
|
if (hasDenyExtensions && typeof ceiling.denyExtensions !== "boolean") throw new Error("Invalid capability ceiling denyExtensions; expected a boolean.");
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (
|
|
73
|
+
const normalizeList = (field: "allowedTools" | "allowedAgents", pattern: RegExp): string[] | undefined => {
|
|
74
|
+
if (!Object.hasOwn(ceiling, field)) return undefined;
|
|
75
|
+
const values = ceiling[field];
|
|
76
|
+
if (!Array.isArray(values)) throw new Error(`Invalid capability ceiling ${field}; expected an array.`);
|
|
77
|
+
if (values.length > 256) throw new Error(`Invalid capability ceiling ${field}; expected at most 256 names.`);
|
|
78
|
+
return [...new Set(values.map((entry) => {
|
|
79
|
+
const name = validateText(entry, `${field} entry`);
|
|
80
|
+
if (!pattern.test(name)) throw new Error(`Invalid capability ceiling ${field} entry '${name}'.`);
|
|
81
|
+
if (Buffer.byteLength(name, "utf8") > 128) throw new Error(`Invalid capability ceiling ${field} entry '${name}'; max 128 UTF-8 bytes.`);
|
|
76
82
|
return name;
|
|
77
83
|
}))].sort();
|
|
78
|
-
}
|
|
84
|
+
};
|
|
85
|
+
const allowedTools = normalizeList("allowedTools", /^[A-Za-z0-9_.:-]+$/u);
|
|
86
|
+
const allowedAgents = normalizeList("allowedAgents", /^[A-Za-z0-9_.:-]+$/u);
|
|
79
87
|
return {
|
|
80
88
|
version: SUBAGENT_CAPABILITY_CEILING_VERSION,
|
|
81
|
-
...(allowedTools ? { allowedTools } : {}),
|
|
89
|
+
...(allowedTools !== undefined ? { allowedTools } : {}),
|
|
90
|
+
...(allowedAgents !== undefined ? { allowedAgents } : {}),
|
|
82
91
|
denyExtensions: ceiling.denyExtensions === true,
|
|
83
92
|
sources: [],
|
|
84
93
|
};
|
|
@@ -132,14 +141,17 @@ export function registerSubagentCapabilityCeiling(options: RegisterSubagentCapab
|
|
|
132
141
|
export function intersectSubagentCapabilityCeilings(...ceilings: Array<ResolvedSubagentCapabilityCeiling | undefined>): ResolvedSubagentCapabilityCeiling | undefined {
|
|
133
142
|
const active = ceilings.filter((ceiling): ceiling is ResolvedSubagentCapabilityCeiling => ceiling !== undefined);
|
|
134
143
|
if (active.length === 0) return undefined;
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
144
|
+
const intersectLists = (field: "allowedTools" | "allowedAgents"): string[] | undefined => {
|
|
145
|
+
const definedLists = active.filter((ceiling) => ceiling[field] !== undefined).map((ceiling) => new Set(ceiling[field]));
|
|
146
|
+
if (definedLists.length === 0) return undefined;
|
|
147
|
+
return [...definedLists[0]!].filter((entry) => definedLists.every((list) => list.has(entry))).sort();
|
|
148
|
+
};
|
|
149
|
+
const allowedTools = intersectLists("allowedTools");
|
|
150
|
+
const allowedAgents = intersectLists("allowedAgents");
|
|
140
151
|
return {
|
|
141
152
|
version: SUBAGENT_CAPABILITY_CEILING_VERSION,
|
|
142
|
-
...(allowedTools ? { allowedTools } : {}),
|
|
153
|
+
...(allowedTools !== undefined ? { allowedTools } : {}),
|
|
154
|
+
...(allowedAgents !== undefined ? { allowedAgents } : {}),
|
|
143
155
|
denyExtensions: active.some((ceiling) => ceiling.denyExtensions),
|
|
144
156
|
sources: [...new Set(active.flatMap((ceiling) => ceiling.sources))].sort(),
|
|
145
157
|
};
|
|
@@ -158,6 +170,26 @@ export function resolveCurrentSubagentCapabilityCeiling(sessionId: string | unde
|
|
|
158
170
|
return resolveSubagentCapabilityCeiling(sessionId, decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]));
|
|
159
171
|
}
|
|
160
172
|
|
|
173
|
+
export function isAgentAllowedByCapabilityCeiling(agentName: string, ceiling: ResolvedSubagentCapabilityCeiling | undefined): boolean {
|
|
174
|
+
return ceiling?.allowedAgents === undefined || ceiling.allowedAgents.includes(agentName);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function capabilityCeilingAgentRestrictionMessage(agentName: string, ceiling: ResolvedSubagentCapabilityCeiling | undefined): string | undefined {
|
|
178
|
+
if (isAgentAllowedByCapabilityCeiling(agentName, ceiling)) return undefined;
|
|
179
|
+
const sources = ceiling?.sources.length ? ceiling.sources.join(", ") : "unknown source";
|
|
180
|
+
const allowed = ceiling?.allowedAgents?.length ? ceiling.allowedAgents.join(", ") : "(none)";
|
|
181
|
+
return `Capability ceiling from ${sources} does not allow agent '${agentName}'. Allowed agents: ${allowed}.`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function assertAgentAllowedByCapabilityCeiling(agentName: string, ceiling: ResolvedSubagentCapabilityCeiling | undefined): void {
|
|
185
|
+
const message = capabilityCeilingAgentRestrictionMessage(agentName, ceiling);
|
|
186
|
+
if (message) throw new Error(message);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function capabilityCeilingAgentRestrictionSources(ceiling: ResolvedSubagentCapabilityCeiling | undefined): string[] | undefined {
|
|
190
|
+
return ceiling?.allowedAgents === undefined ? undefined : [...ceiling.sources];
|
|
191
|
+
}
|
|
192
|
+
|
|
161
193
|
export function encodeSubagentCapabilityCeiling(ceiling: ResolvedSubagentCapabilityCeiling | undefined): string | undefined {
|
|
162
194
|
if (!ceiling) return undefined;
|
|
163
195
|
return Buffer.from(JSON.stringify(ceiling), "utf8").toString("base64url");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isDynamicParallelStep, isParallelStep, type ChainStep, type SequentialStep } from "../../shared/settings.ts";
|
|
1
|
+
import { isCheckpointStep, isDynamicParallelStep, isParallelStep, type ChainStep, type SequentialStep } from "../../shared/settings.ts";
|
|
2
2
|
import type { ChainOutputMap, ChainOutputMapEntry, SingleResult } from "../../shared/types.ts";
|
|
3
3
|
import { getSingleResultOutput } from "../../shared/utils.ts";
|
|
4
4
|
import { DynamicFanoutError, hasDynamicFanoutFields, type DynamicFanoutConfig, validateDynamicStepShape } from "./dynamic-fanout.ts";
|
|
@@ -14,6 +14,7 @@ export interface ChainOutputValidationContext {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
function outputNamesForStep(step: ChainStep): string[] {
|
|
17
|
+
if (isCheckpointStep(step)) return [];
|
|
17
18
|
if (isParallelStep(step)) return step.parallel.map((task) => task.as).filter((name): name is string => Boolean(name));
|
|
18
19
|
if (isDynamicParallelStep(step)) return [step.collect.as];
|
|
19
20
|
const name = (step as SequentialStep).as;
|
|
@@ -21,6 +22,7 @@ function outputNamesForStep(step: ChainStep): string[] {
|
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
function taskTemplatesForStep(step: ChainStep): string[] {
|
|
25
|
+
if (isCheckpointStep(step)) return [];
|
|
24
26
|
if (isParallelStep(step)) return step.parallel.map((task) => task.task ?? "{previous}");
|
|
25
27
|
if (isDynamicParallelStep(step)) return [step.parallel.task ?? "{previous}", step.parallel.label ?? ""].filter(Boolean);
|
|
26
28
|
return [(step as SequentialStep).task ?? "{previous}"];
|
|
@@ -51,7 +51,7 @@ const RUNNER_DYNAMIC_PARALLEL_KEYS = new Set([
|
|
|
51
51
|
...DYNAMIC_PARALLEL_KEYS,
|
|
52
52
|
"outputName", "structured", "inheritProjectContext", "inheritSkills", "skills", "outputPath", "namespaceOutputPath", "maxSubagentDepth", "waitToolEnabled",
|
|
53
53
|
"structuredOutput", "structuredOutputSchema", "tools", "extensions", "subagentOnlyExtensions", "mcpDirectTools", "capabilityCeiling", "completionGuard", "systemPrompt",
|
|
54
|
-
"systemPromptMode", "thinking", "modelCandidates", "sessionFile", "effectiveAcceptance", "acceptanceInput", "acceptanceRole", "parentSessionId",
|
|
54
|
+
"systemPromptMode", "thinking", "modelCandidates", "sessionFile", "effectiveAcceptance", "acceptanceInput", "acceptanceRole", "parentSessionId", "launchResolvedExtensions",
|
|
55
55
|
]);
|
|
56
56
|
const DYNAMIC_COLLECT_KEYS = new Set(["as", "outputSchema"]);
|
|
57
57
|
|
|
@@ -27,6 +27,7 @@ type ImportKind = keyof typeof IMPORT_PATHS;
|
|
|
27
27
|
interface ServerEntry {
|
|
28
28
|
command?: string;
|
|
29
29
|
args?: string[];
|
|
30
|
+
socket?: string;
|
|
30
31
|
env?: Record<string, string>;
|
|
31
32
|
cwd?: string;
|
|
32
33
|
url?: string;
|
|
@@ -35,6 +36,7 @@ interface ServerEntry {
|
|
|
35
36
|
bearerToken?: string;
|
|
36
37
|
bearerTokenEnv?: string;
|
|
37
38
|
exposeResources?: boolean;
|
|
39
|
+
includeTools?: string[];
|
|
38
40
|
excludeTools?: string[];
|
|
39
41
|
directTools?: boolean | string[];
|
|
40
42
|
}
|
|
@@ -271,14 +273,16 @@ export function computeMcpServerHash(definition: ServerEntry): string {
|
|
|
271
273
|
const identity: Record<string, unknown> = {
|
|
272
274
|
command: definition.command,
|
|
273
275
|
args: definition.args,
|
|
276
|
+
socket: resolveConfigPath(definition.socket),
|
|
274
277
|
env: interpolateEnvRecord(definition.env),
|
|
275
278
|
cwd: resolveConfigPath(definition.cwd),
|
|
276
|
-
url: definition
|
|
279
|
+
url: resolveServerUrl(definition),
|
|
277
280
|
headers: interpolateEnvRecord(definition.headers),
|
|
278
281
|
auth: definition.auth,
|
|
279
282
|
bearerToken: resolveBearerToken(definition),
|
|
280
283
|
bearerTokenEnv: definition.bearerTokenEnv,
|
|
281
284
|
exposeResources: definition.exposeResources,
|
|
285
|
+
includeTools: definition.includeTools,
|
|
282
286
|
excludeTools: definition.excludeTools,
|
|
283
287
|
};
|
|
284
288
|
return createHash("sha256").update(stableStringify(identity)).digest("hex");
|
|
@@ -333,22 +337,51 @@ function resourceNameToToolName(name: string): string {
|
|
|
333
337
|
}
|
|
334
338
|
|
|
335
339
|
function interpolateEnvRecord(values: Record<string, string> | undefined): Record<string, string> | undefined {
|
|
336
|
-
if (!values
|
|
337
|
-
|
|
338
|
-
for (const [key, value] of Object.entries(values)) {
|
|
339
|
-
if (typeof value === "string") resolved[key] = interpolateEnvVars(value);
|
|
340
|
-
}
|
|
341
|
-
return resolved;
|
|
340
|
+
if (!values) return undefined;
|
|
341
|
+
return Object.fromEntries(Object.entries(values).map(([key, value]) => [key, interpolateSecretExpression(value)]));
|
|
342
342
|
}
|
|
343
343
|
|
|
344
344
|
function interpolateEnvVars(value: string): string {
|
|
345
345
|
return value
|
|
346
346
|
.replace(/\$\{(\w+)\}/g, (_, name: string) => process.env[name] ?? "")
|
|
347
|
-
.replace(/\$env:(\w+)/g, (_, name: string) => process.env[name] ?? "")
|
|
347
|
+
.replace(/\$env:(\w+)/g, (_, name: string) => process.env[name] ?? "")
|
|
348
|
+
.replace(/\{env:(\w+)\}/g, (_, name: string) => process.env[name] ?? "");
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function interpolateSecretExpression(value: string): string {
|
|
352
|
+
if (value.startsWith("!!")) return interpolateEnvVars(value.slice(1));
|
|
353
|
+
return value.startsWith("!") ? value : interpolateEnvVars(value);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function getMissingEnvVars(value: string): string[] {
|
|
357
|
+
const missing = new Set<string>();
|
|
358
|
+
for (const match of value.matchAll(/\$\{(\w+)\}|\$env:(\w+)|\{env:(\w+)\}/g)) {
|
|
359
|
+
const name = match[1] ?? match[2] ?? match[3];
|
|
360
|
+
if (name && process.env[name] === undefined) missing.add(name);
|
|
361
|
+
}
|
|
362
|
+
return [...missing];
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function resolveServerUrl(definition: Pick<ServerEntry, "url">): string | undefined {
|
|
366
|
+
if (definition.url == null) return undefined;
|
|
367
|
+
if (typeof definition.url !== "string") throw new Error("MCP server URL must be a string");
|
|
368
|
+
|
|
369
|
+
const missing = getMissingEnvVars(definition.url);
|
|
370
|
+
if (missing.length > 0) {
|
|
371
|
+
throw new Error(`Missing environment variable${missing.length === 1 ? "" : "s"} in MCP server URL: ${missing.join(", ")}`);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const resolved = interpolateEnvVars(definition.url);
|
|
375
|
+
try {
|
|
376
|
+
new URL(resolved);
|
|
377
|
+
} catch (error) {
|
|
378
|
+
throw new Error(`Invalid MCP server URL after environment interpolation: ${resolved}`, { cause: error });
|
|
379
|
+
}
|
|
380
|
+
return resolved;
|
|
348
381
|
}
|
|
349
382
|
|
|
350
383
|
function resolveConfigPath(value: string | undefined): string | undefined {
|
|
351
|
-
if (
|
|
384
|
+
if (value === undefined) return undefined;
|
|
352
385
|
const resolved = interpolateEnvVars(value);
|
|
353
386
|
if (resolved === "~") return os.homedir();
|
|
354
387
|
if (resolved.startsWith("~/") || resolved.startsWith("~\\")) return path.join(os.homedir(), resolved.slice(2));
|
|
@@ -356,8 +389,8 @@ function resolveConfigPath(value: string | undefined): string | undefined {
|
|
|
356
389
|
}
|
|
357
390
|
|
|
358
391
|
function resolveBearerToken(definition: Pick<ServerEntry, "bearerToken" | "bearerTokenEnv">): string | undefined {
|
|
359
|
-
if (
|
|
360
|
-
return
|
|
392
|
+
if (definition.bearerToken !== undefined) return interpolateSecretExpression(definition.bearerToken);
|
|
393
|
+
return definition.bearerTokenEnv ? process.env[definition.bearerTokenEnv] : undefined;
|
|
361
394
|
}
|
|
362
395
|
|
|
363
396
|
function stableStringify(value: unknown): string {
|
|
@@ -30,6 +30,14 @@ export interface ParentModel {
|
|
|
30
30
|
id: string;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
export function normalizeParentModel(model: unknown): ParentModel | undefined {
|
|
34
|
+
if (!model || typeof model !== "object") return undefined;
|
|
35
|
+
const candidate = model as { provider?: unknown; id?: unknown };
|
|
36
|
+
if (typeof candidate.provider !== "string" || typeof candidate.id !== "string") return undefined;
|
|
37
|
+
if (!candidate.provider || !candidate.id) return undefined;
|
|
38
|
+
return { provider: candidate.provider, id: candidate.id };
|
|
39
|
+
}
|
|
40
|
+
|
|
33
41
|
/**
|
|
34
42
|
* Normalize a model id or provider segment for fuzzy comparison: case-fold,
|
|
35
43
|
* treat dots/underscores as dashes (so `4.5` matches `4-5`), and collapse
|
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
TEMP_ROOT_DIR,
|
|
8
8
|
type AsyncJobState,
|
|
9
9
|
type AsyncStatus,
|
|
10
|
+
type LaunchResolvedChildExtensionsV1,
|
|
11
|
+
type RuntimeAcknowledgedChildExtensionsV1,
|
|
10
12
|
type NestedRouteInfo,
|
|
11
13
|
type TurnBudgetState,
|
|
12
14
|
type NestedRunSummary,
|
|
@@ -197,6 +199,55 @@ function sanitizeCost(value: unknown): NestedRunSummary["totalCost"] | undefined
|
|
|
197
199
|
: undefined;
|
|
198
200
|
}
|
|
199
201
|
|
|
202
|
+
function sanitizeLaunchResolvedExtensions(value: unknown): LaunchResolvedChildExtensionsV1 | undefined {
|
|
203
|
+
if (!value || typeof value !== "object") return undefined;
|
|
204
|
+
const raw = value as Record<string, unknown>;
|
|
205
|
+
if (raw.version !== 1 || raw.source !== "launch-resolved" || typeof raw.disableAmbientExtensions !== "boolean") return undefined;
|
|
206
|
+
const stringList = (input: unknown): string[] => Array.isArray(input)
|
|
207
|
+
? input.filter((item): item is string => typeof item === "string" && /^sha256:[a-f0-9]{16}$/.test(item)).slice(0, 32)
|
|
208
|
+
: [];
|
|
209
|
+
const omitted = raw.omitted && typeof raw.omitted === "object" ? raw.omitted as Record<string, unknown> : {};
|
|
210
|
+
const omittedCount = (key: string): number => Math.max(0, Math.floor(clampNumber(omitted[key]) ?? 0));
|
|
211
|
+
return {
|
|
212
|
+
version: 1,
|
|
213
|
+
source: "launch-resolved",
|
|
214
|
+
disableAmbientExtensions: raw.disableAmbientExtensions,
|
|
215
|
+
runtime: stringList(raw.runtime),
|
|
216
|
+
configured: stringList(raw.configured),
|
|
217
|
+
effective: stringList(raw.effective),
|
|
218
|
+
omitted: {
|
|
219
|
+
runtime: omittedCount("runtime"),
|
|
220
|
+
configured: omittedCount("configured"),
|
|
221
|
+
effective: omittedCount("effective"),
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function sanitizeRuntimeAcknowledgedExtensions(value: unknown): RuntimeAcknowledgedChildExtensionsV1 | undefined {
|
|
227
|
+
if (!value || typeof value !== "object") return undefined;
|
|
228
|
+
const raw = value as Record<string, unknown>;
|
|
229
|
+
if (raw.version !== 1 || raw.source !== "child-runtime" || !Array.isArray(raw.ids)) return undefined;
|
|
230
|
+
const ids: string[] = [];
|
|
231
|
+
const seen = new Set<string>();
|
|
232
|
+
for (const item of raw.ids) {
|
|
233
|
+
if (typeof item !== "string" || item.length === 0 || item.length > 128 || !/^[A-Za-z0-9._:@+-]+$/.test(item) || item.includes("..") || item.includes("/") || item.includes("\\") || seen.has(item)) continue;
|
|
234
|
+
seen.add(item);
|
|
235
|
+
ids.push(item);
|
|
236
|
+
}
|
|
237
|
+
if (ids.length === 0) return undefined;
|
|
238
|
+
return {
|
|
239
|
+
version: 1,
|
|
240
|
+
source: "child-runtime",
|
|
241
|
+
ids: ids.slice(0, 32),
|
|
242
|
+
omitted: Math.max(0, ids.length - 32) + Math.max(0, Math.floor(clampNumber(raw.omitted) ?? 0)),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function runtimeAcknowledgedEntry(value: unknown): { runtimeAcknowledgedExtensions: RuntimeAcknowledgedChildExtensionsV1 } | Record<string, never> {
|
|
247
|
+
const sanitized = sanitizeRuntimeAcknowledgedExtensions(value);
|
|
248
|
+
return sanitized ? { runtimeAcknowledgedExtensions: sanitized } : {};
|
|
249
|
+
}
|
|
250
|
+
|
|
200
251
|
function sanitizeTurnBudget(value: unknown): TurnBudgetState | undefined {
|
|
201
252
|
if (!value || typeof value !== "object") return undefined;
|
|
202
253
|
const raw = value as Record<string, unknown>;
|
|
@@ -249,6 +300,8 @@ function sanitizeStep(input: unknown, depth: number): NestedStepSummary | undefi
|
|
|
249
300
|
...(sanitizeTurnBudget(raw.turnBudget) ? { turnBudget: sanitizeTurnBudget(raw.turnBudget) } : {}),
|
|
250
301
|
...(raw.turnBudgetExceeded === true ? { turnBudgetExceeded: true } : {}),
|
|
251
302
|
...(raw.wrapUpRequested === true ? { wrapUpRequested: true } : {}),
|
|
303
|
+
...(sanitizeLaunchResolvedExtensions(raw.launchResolvedExtensions) ? { launchResolvedExtensions: sanitizeLaunchResolvedExtensions(raw.launchResolvedExtensions) } : {}),
|
|
304
|
+
...(sanitizeRuntimeAcknowledgedExtensions(raw.runtimeAcknowledgedExtensions) ? { runtimeAcknowledgedExtensions: sanitizeRuntimeAcknowledgedExtensions(raw.runtimeAcknowledgedExtensions) } : {}),
|
|
252
305
|
...(depth < MAX_DEPTH && Array.isArray(raw.children) ? { children: raw.children.map((child) => sanitizeSummary(child, depth + 1)).filter((child): child is NestedRunSummary => Boolean(child)).slice(0, MAX_CHILDREN) } : {}),
|
|
253
306
|
};
|
|
254
307
|
}
|
|
@@ -306,6 +359,8 @@ export function sanitizeSummary(input: unknown, depth = 0): NestedRunSummary | u
|
|
|
306
359
|
...(raw.turnBudgetExceeded === true ? { turnBudgetExceeded: true } : {}),
|
|
307
360
|
...(raw.wrapUpRequested === true ? { wrapUpRequested: true } : {}),
|
|
308
361
|
...(stringValue(raw.error, 1024) ? { error: stringValue(raw.error, 1024) } : {}),
|
|
362
|
+
...(sanitizeLaunchResolvedExtensions(raw.launchResolvedExtensions) ? { launchResolvedExtensions: sanitizeLaunchResolvedExtensions(raw.launchResolvedExtensions) } : {}),
|
|
363
|
+
...(sanitizeRuntimeAcknowledgedExtensions(raw.runtimeAcknowledgedExtensions) ? { runtimeAcknowledgedExtensions: sanitizeRuntimeAcknowledgedExtensions(raw.runtimeAcknowledgedExtensions) } : {}),
|
|
309
364
|
...(steps && steps.length > 0 ? { steps } : {}),
|
|
310
365
|
...(depth < MAX_DEPTH && Array.isArray(raw.children) ? { children: raw.children.map((child) => sanitizeSummary(child, depth + 1)).filter((child): child is NestedRunSummary => Boolean(child)).slice(0, MAX_CHILDREN) } : {}),
|
|
311
366
|
};
|
|
@@ -788,32 +843,50 @@ export function writeNestedControlResult(route: NestedRoute, result: Omit<Nested
|
|
|
788
843
|
writeRouteRecord(route.eventSink, sanitized.ts, sanitized);
|
|
789
844
|
}
|
|
790
845
|
|
|
791
|
-
|
|
846
|
+
function readControlResultsFromFile(route: NestedRoute, eventPath: string): NestedControlResultRecord[] {
|
|
847
|
+
if (!containedPath(route.eventSink, eventPath)) return [];
|
|
848
|
+
try {
|
|
849
|
+
const stat = fs.statSync(eventPath);
|
|
850
|
+
if (!stat.isFile() || stat.size > MAX_EVENT_BYTES) return [];
|
|
851
|
+
const content = fs.readFileSync(eventPath, "utf-8");
|
|
852
|
+
const lines = content.includes("\n") ? content.split("\n").filter((line) => line.trim()) : [content];
|
|
853
|
+
return lines.map((line) => parseControlResult(line, route)).filter((result): result is NestedControlResultRecord => Boolean(result));
|
|
854
|
+
} catch {
|
|
855
|
+
return [];
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
function listNestedEventFiles(route: NestedRoute): string[] {
|
|
792
860
|
validateRouteShape(route);
|
|
793
|
-
let entries: string[] = [];
|
|
794
861
|
try {
|
|
795
|
-
|
|
862
|
+
return fs.readdirSync(route.eventSink).filter((entry) => entry.endsWith(".json") || entry.endsWith(".jsonl"));
|
|
796
863
|
} catch (error) {
|
|
797
|
-
if ((error as NodeJS.ErrnoException).code
|
|
864
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return [];
|
|
865
|
+
throw error;
|
|
798
866
|
}
|
|
799
|
-
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
export function snapshotNestedEventFiles(route: NestedRoute): Set<string> {
|
|
870
|
+
return new Set(listNestedEventFiles(route));
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
export function findNestedControlResult(route: NestedRoute, requestId: string, targetRunId: string, ignoredFiles: ReadonlySet<string>): NestedControlResultRecord | undefined {
|
|
874
|
+
const entries = listNestedEventFiles(route)
|
|
875
|
+
.filter((entry) => !ignoredFiles.has(entry))
|
|
876
|
+
.sort()
|
|
877
|
+
.reverse();
|
|
800
878
|
for (const entry of entries) {
|
|
801
|
-
const
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
const stat = fs.statSync(eventPath);
|
|
805
|
-
if (!stat.isFile() || stat.size > MAX_EVENT_BYTES) continue;
|
|
806
|
-
const content = fs.readFileSync(eventPath, "utf-8");
|
|
807
|
-
const lines = content.includes("\n") ? content.split("\n").filter((line) => line.trim()) : [content];
|
|
808
|
-
for (const line of lines) {
|
|
809
|
-
const result = parseControlResult(line, route);
|
|
810
|
-
if (result) results.push(result);
|
|
811
|
-
}
|
|
812
|
-
} catch {
|
|
813
|
-
continue;
|
|
814
|
-
}
|
|
879
|
+
const result = readControlResultsFromFile(route, path.join(route.eventSink, entry))
|
|
880
|
+
.find((candidate) => candidate.requestId === requestId && candidate.targetRunId === targetRunId);
|
|
881
|
+
if (result) return result;
|
|
815
882
|
}
|
|
816
|
-
return
|
|
883
|
+
return undefined;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
export function readNestedControlResults(route: NestedRoute): NestedControlResultRecord[] {
|
|
887
|
+
return listNestedEventFiles(route)
|
|
888
|
+
.sort()
|
|
889
|
+
.flatMap((entry) => readControlResultsFromFile(route, path.join(route.eventSink, entry)));
|
|
817
890
|
}
|
|
818
891
|
|
|
819
892
|
export function nestedRouteEnv(route: NestedRoute): Record<string, string> {
|
|
@@ -875,6 +948,8 @@ export function nestedSummaryFromAsyncStatus(status: AsyncStatus, asyncDir: stri
|
|
|
875
948
|
...(status.sessionId ? { sessionId: status.sessionId } : {}),
|
|
876
949
|
mode: status.mode ?? fallback.mode,
|
|
877
950
|
...(status.processTerminal ? { processTerminal: sanitizeProcessTerminal(status.processTerminal, { runId: status.runId || fallback.id, runnerProcessInstanceId: status.processTerminal.runnerProcessInstanceId }, `${asyncDir}/status.json`) } : {}),
|
|
951
|
+
...(status.launchResolvedExtensions ? { launchResolvedExtensions: status.launchResolvedExtensions } : {}),
|
|
952
|
+
...runtimeAcknowledgedEntry(status.runtimeAcknowledgedExtensions),
|
|
878
953
|
...(status.capabilityCeiling ? { capabilityCeiling: status.capabilityCeiling } : {}),
|
|
879
954
|
...(status.capabilityAudit ? { capabilityAudit: status.capabilityAudit } : {}),
|
|
880
955
|
state: status.state,
|
|
@@ -914,6 +989,8 @@ export function nestedSummaryFromAsyncStatus(status: AsyncStatus, asyncDir: stri
|
|
|
914
989
|
...(step.startedAt !== undefined ? { startedAt: step.startedAt } : {}),
|
|
915
990
|
...(step.endedAt !== undefined ? { endedAt: step.endedAt } : {}),
|
|
916
991
|
...(step.error ? { error: step.error } : {}),
|
|
992
|
+
...(step.launchResolvedExtensions ? { launchResolvedExtensions: step.launchResolvedExtensions } : {}),
|
|
993
|
+
...runtimeAcknowledgedEntry(step.runtimeAcknowledgedExtensions),
|
|
917
994
|
...(step.timedOut !== undefined ? { timedOut: step.timedOut } : {}),
|
|
918
995
|
...(step.stopped !== undefined ? { stopped: step.stopped } : {}),
|
|
919
996
|
...(step.turnBudget ? { turnBudget: step.turnBudget } : {}),
|
|
@@ -46,6 +46,8 @@ export interface RunnerSubagentStep {
|
|
|
46
46
|
definitionDigest?: string;
|
|
47
47
|
launchBindingTask?: string;
|
|
48
48
|
launchContractDigest?: string;
|
|
49
|
+
launchResolvedExtensions?: import("../../shared/types.ts").LaunchResolvedChildExtensionsV1;
|
|
50
|
+
runtimeAcknowledgedExtensions?: import("../../shared/types.ts").RuntimeAcknowledgedChildExtensionsV1;
|
|
49
51
|
effectiveAcceptance?: import("../../shared/types.ts").ResolvedAcceptanceConfig;
|
|
50
52
|
acceptanceInput?: import("../../shared/types.ts").AcceptanceInput;
|
|
51
53
|
acceptanceRole?: import("../../shared/types.ts").AcceptanceRole;
|
|
@@ -55,6 +57,13 @@ export interface RunnerSubagentStep {
|
|
|
55
57
|
capabilityAudit?: import("./capability-ceiling.ts").SubagentCapabilityAudit;
|
|
56
58
|
}
|
|
57
59
|
|
|
60
|
+
export interface RunnerCheckpointStep {
|
|
61
|
+
checkpoint: string;
|
|
62
|
+
message?: string;
|
|
63
|
+
phase?: string;
|
|
64
|
+
label?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
58
67
|
export interface ParallelStepGroup {
|
|
59
68
|
parallel: RunnerSubagentStep[];
|
|
60
69
|
concurrency?: number;
|
|
@@ -79,7 +88,11 @@ export interface DynamicRunnerGroup {
|
|
|
79
88
|
gateOn?: import("../../shared/types.ts").ChainGateLayer;
|
|
80
89
|
}
|
|
81
90
|
|
|
82
|
-
export type RunnerStep = RunnerSubagentStep | ParallelStepGroup | DynamicRunnerGroup;
|
|
91
|
+
export type RunnerStep = RunnerSubagentStep | ParallelStepGroup | DynamicRunnerGroup | RunnerCheckpointStep;
|
|
92
|
+
|
|
93
|
+
export function isCheckpointRunnerStep(step: RunnerStep): step is RunnerCheckpointStep {
|
|
94
|
+
return "checkpoint" in step;
|
|
95
|
+
}
|
|
83
96
|
|
|
84
97
|
export function isParallelGroup(step: RunnerStep): step is ParallelStepGroup {
|
|
85
98
|
return "parallel" in step && Array.isArray(step.parallel);
|
|
@@ -92,7 +105,9 @@ export function isDynamicRunnerGroup(step: RunnerStep): step is DynamicRunnerGro
|
|
|
92
105
|
export function flattenSteps(steps: RunnerStep[]): RunnerSubagentStep[] {
|
|
93
106
|
const flat: RunnerSubagentStep[] = [];
|
|
94
107
|
for (const step of steps) {
|
|
95
|
-
if (
|
|
108
|
+
if (isCheckpointRunnerStep(step)) {
|
|
109
|
+
continue;
|
|
110
|
+
} else if (isParallelGroup(step)) {
|
|
96
111
|
for (const task of step.parallel) flat.push(task);
|
|
97
112
|
} else if (isDynamicRunnerGroup(step)) {
|
|
98
113
|
continue;
|
|
@@ -144,29 +159,45 @@ export async function mapConcurrent<T, R>(
|
|
|
144
159
|
limit: number,
|
|
145
160
|
fn: (item: T, i: number) => Promise<R>,
|
|
146
161
|
globalSemaphore?: Semaphore,
|
|
162
|
+
/** Invoked after every worker has stopped, including workers outliving an early rejection. */
|
|
163
|
+
onSchedulingSettled?: () => void,
|
|
147
164
|
): Promise<R[]> {
|
|
148
165
|
const safeLimit = Math.max(1, Math.floor(limit) || 1);
|
|
149
166
|
const results: R[] = new Array(items.length);
|
|
150
167
|
let next = 0;
|
|
168
|
+
let liveWorkers = Math.min(safeLimit, items.length);
|
|
169
|
+
const notifySchedulingSettled = () => {
|
|
170
|
+
try {
|
|
171
|
+
onSchedulingSettled?.();
|
|
172
|
+
} catch {
|
|
173
|
+
// Scheduling lifecycle cleanup must not replace the worker result.
|
|
174
|
+
}
|
|
175
|
+
};
|
|
151
176
|
|
|
152
177
|
async function worker(_workerIndex: number): Promise<void> {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
178
|
+
try {
|
|
179
|
+
while (next < items.length) {
|
|
180
|
+
const i = next++;
|
|
181
|
+
if (globalSemaphore) {
|
|
182
|
+
await globalSemaphore.acquire();
|
|
183
|
+
try {
|
|
184
|
+
results[i] = await fn(items[i], i);
|
|
185
|
+
} finally {
|
|
186
|
+
globalSemaphore.release();
|
|
187
|
+
}
|
|
188
|
+
} else {
|
|
158
189
|
results[i] = await fn(items[i], i);
|
|
159
|
-
} finally {
|
|
160
|
-
globalSemaphore.release();
|
|
161
190
|
}
|
|
162
|
-
} else {
|
|
163
|
-
results[i] = await fn(items[i], i);
|
|
164
191
|
}
|
|
192
|
+
} finally {
|
|
193
|
+
liveWorkers--;
|
|
194
|
+
if (liveWorkers === 0) notifySchedulingSettled();
|
|
165
195
|
}
|
|
166
196
|
}
|
|
167
197
|
|
|
198
|
+
if (liveWorkers === 0) notifySchedulingSettled();
|
|
168
199
|
await Promise.all(
|
|
169
|
-
Array.from({ length:
|
|
200
|
+
Array.from({ length: liveWorkers }, (_, wi) => worker(wi)),
|
|
170
201
|
);
|
|
171
202
|
return results;
|
|
172
203
|
}
|