apex-code 0.0.1-alpha.0 → 0.0.1-alpha.2
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 +66 -681
- package/dist/cli/args.d.ts +5 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +45 -6
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/auth-command.d.ts.map +1 -1
- package/dist/cli/auth-command.js +3 -3
- package/dist/cli/auth-command.js.map +1 -1
- package/dist/cli/cost-command.d.ts +19 -0
- package/dist/cli/cost-command.d.ts.map +1 -0
- package/dist/cli/cost-command.js +107 -0
- package/dist/cli/cost-command.js.map +1 -0
- package/dist/cli/startup-ui.d.ts +1 -1
- package/dist/cli/startup-ui.d.ts.map +1 -1
- package/dist/cli/startup-ui.js +1 -2
- package/dist/cli/startup-ui.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +45 -16
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +2 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +14 -7
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +2 -0
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +9 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +30 -4
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +46 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +137 -19
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +6 -6
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +1 -1
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/context/deferred-schemas.d.ts +92 -0
- package/dist/core/context/deferred-schemas.d.ts.map +1 -0
- package/dist/core/context/deferred-schemas.js +74 -0
- package/dist/core/context/deferred-schemas.js.map +1 -0
- package/dist/core/context/eviction.d.ts +47 -0
- package/dist/core/context/eviction.d.ts.map +1 -0
- package/dist/core/context/eviction.js +83 -0
- package/dist/core/context/eviction.js.map +1 -0
- package/dist/core/context/pipeline.d.ts +90 -0
- package/dist/core/context/pipeline.d.ts.map +1 -0
- package/dist/core/context/pipeline.js +109 -0
- package/dist/core/context/pipeline.js.map +1 -0
- package/dist/core/credential-failover.d.ts +30 -0
- package/dist/core/credential-failover.d.ts.map +1 -0
- package/dist/core/credential-failover.js +46 -0
- package/dist/core/credential-failover.js.map +1 -0
- package/dist/core/credential-pool.d.ts +60 -0
- package/dist/core/credential-pool.d.ts.map +1 -0
- package/dist/core/credential-pool.js +84 -0
- package/dist/core/credential-pool.js.map +1 -0
- package/dist/core/delegation/agents.d.ts +15 -0
- package/dist/core/delegation/agents.d.ts.map +1 -0
- package/dist/core/delegation/agents.js +55 -0
- package/dist/core/delegation/agents.js.map +1 -0
- package/dist/core/delegation/ceiling.d.ts +29 -0
- package/dist/core/delegation/ceiling.d.ts.map +1 -0
- package/dist/core/delegation/ceiling.js +34 -0
- package/dist/core/delegation/ceiling.js.map +1 -0
- package/dist/core/delegation/runtime.d.ts +94 -0
- package/dist/core/delegation/runtime.d.ts.map +1 -0
- package/dist/core/delegation/runtime.js +117 -0
- package/dist/core/delegation/runtime.js.map +1 -0
- package/dist/core/durable-state/daemon.d.ts +37 -0
- package/dist/core/durable-state/daemon.d.ts.map +1 -0
- package/dist/core/durable-state/daemon.js +62 -0
- package/dist/core/durable-state/daemon.js.map +1 -0
- package/dist/core/durable-state/provenance.d.ts +7 -0
- package/dist/core/durable-state/provenance.d.ts.map +1 -0
- package/dist/core/durable-state/provenance.js +24 -0
- package/dist/core/durable-state/provenance.js.map +1 -0
- package/dist/core/durable-state/sqlite.d.ts +84 -0
- package/dist/core/durable-state/sqlite.d.ts.map +1 -0
- package/dist/core/durable-state/sqlite.js +331 -0
- package/dist/core/durable-state/sqlite.js.map +1 -0
- package/dist/core/environment.d.ts +12 -0
- package/dist/core/environment.d.ts.map +1 -0
- package/dist/core/environment.js +69 -0
- package/dist/core/environment.js.map +1 -0
- package/dist/core/evidence-policy.d.ts +17 -0
- package/dist/core/evidence-policy.d.ts.map +1 -0
- package/dist/core/evidence-policy.js +25 -0
- package/dist/core/evidence-policy.js.map +1 -0
- package/dist/core/evidence.d.ts +16 -0
- package/dist/core/evidence.d.ts.map +1 -0
- package/dist/core/evidence.js +51 -0
- package/dist/core/evidence.js.map +1 -0
- package/dist/core/experimental.d.ts.map +1 -1
- package/dist/core/experimental.js +2 -1
- package/dist/core/experimental.js.map +1 -1
- package/dist/core/extensions/types.d.ts +2 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/model-config.d.ts +8 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +21 -5
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-resolver.d.ts +20 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +59 -0
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts +73 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +225 -8
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/observability/aggregate.d.ts +21 -0
- package/dist/core/observability/aggregate.d.ts.map +1 -0
- package/dist/core/observability/aggregate.js +36 -0
- package/dist/core/observability/aggregate.js.map +1 -0
- package/dist/core/observability/otlp.d.ts +66 -0
- package/dist/core/observability/otlp.d.ts.map +1 -0
- package/dist/core/observability/otlp.js +95 -0
- package/dist/core/observability/otlp.js.map +1 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +3 -2
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/permissions/gate.d.ts +35 -0
- package/dist/core/permissions/gate.d.ts.map +1 -0
- package/dist/core/permissions/gate.js +72 -0
- package/dist/core/permissions/gate.js.map +1 -0
- package/dist/core/permissions/modes.d.ts +18 -0
- package/dist/core/permissions/modes.d.ts.map +1 -0
- package/dist/core/permissions/modes.js +57 -0
- package/dist/core/permissions/modes.js.map +1 -0
- package/dist/core/permissions/responder.d.ts +30 -0
- package/dist/core/permissions/responder.d.ts.map +1 -0
- package/dist/core/permissions/responder.js +32 -0
- package/dist/core/permissions/responder.js.map +1 -0
- package/dist/core/permissions/rules.d.ts +34 -0
- package/dist/core/permissions/rules.d.ts.map +1 -0
- package/dist/core/permissions/rules.js +74 -0
- package/dist/core/permissions/rules.js.map +1 -0
- package/dist/core/permissions/startup.d.ts +33 -0
- package/dist/core/permissions/startup.d.ts.map +1 -0
- package/dist/core/permissions/startup.js +55 -0
- package/dist/core/permissions/startup.js.map +1 -0
- package/dist/core/permissions/store.d.ts +114 -0
- package/dist/core/permissions/store.d.ts.map +1 -0
- package/dist/core/permissions/store.js +229 -0
- package/dist/core/permissions/store.js.map +1 -0
- package/dist/core/project-trust.d.ts.map +1 -1
- package/dist/core/project-trust.js +1 -1
- package/dist/core/project-trust.js.map +1 -1
- package/dist/core/provider-attribution.d.ts.map +1 -1
- package/dist/core/provider-attribution.js +6 -7
- package/dist/core/provider-attribution.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts +2 -2
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +20 -16
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/sandbox/child-entry.d.ts +2 -0
- package/dist/core/sandbox/child-entry.d.ts.map +1 -0
- package/dist/core/sandbox/child-entry.js +11 -0
- package/dist/core/sandbox/child-entry.js.map +1 -0
- package/dist/core/sandbox/cli-launch.d.ts +27 -0
- package/dist/core/sandbox/cli-launch.d.ts.map +1 -0
- package/dist/core/sandbox/cli-launch.js +133 -0
- package/dist/core/sandbox/cli-launch.js.map +1 -0
- package/dist/core/sandbox/cli-supervisor.d.ts +25 -0
- package/dist/core/sandbox/cli-supervisor.d.ts.map +1 -0
- package/dist/core/sandbox/cli-supervisor.js +57 -0
- package/dist/core/sandbox/cli-supervisor.js.map +1 -0
- package/dist/core/sandbox/linux-backend.d.ts +18 -0
- package/dist/core/sandbox/linux-backend.d.ts.map +1 -0
- package/dist/core/sandbox/linux-backend.js +186 -0
- package/dist/core/sandbox/linux-backend.js.map +1 -0
- package/dist/core/sandbox/macos-backend.d.ts +18 -0
- package/dist/core/sandbox/macos-backend.d.ts.map +1 -0
- package/dist/core/sandbox/macos-backend.js +195 -0
- package/dist/core/sandbox/macos-backend.js.map +1 -0
- package/dist/core/sandbox/network-proxy.d.ts +19 -0
- package/dist/core/sandbox/network-proxy.d.ts.map +1 -0
- package/dist/core/sandbox/network-proxy.js +79 -0
- package/dist/core/sandbox/network-proxy.js.map +1 -0
- package/dist/core/sandbox/policy.d.ts +32 -0
- package/dist/core/sandbox/policy.d.ts.map +1 -0
- package/dist/core/sandbox/policy.js +38 -0
- package/dist/core/sandbox/policy.js.map +1 -0
- package/dist/core/sandbox/supervisor.d.ts +31 -0
- package/dist/core/sandbox/supervisor.d.ts.map +1 -0
- package/dist/core/sandbox/supervisor.js +18 -0
- package/dist/core/sandbox/supervisor.js.map +1 -0
- package/dist/core/sandbox/violations.d.ts +23 -0
- package/dist/core/sandbox/violations.d.ts.map +1 -0
- package/dist/core/sandbox/violations.js +30 -0
- package/dist/core/sandbox/violations.js.map +1 -0
- package/dist/core/sdk.d.ts +20 -36
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +129 -2
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +41 -2
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +57 -0
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-share.d.ts +16 -0
- package/dist/core/session-share.d.ts.map +1 -0
- package/dist/core/session-share.js +22 -0
- package/dist/core/session-share.js.map +1 -0
- package/dist/core/settings-manager.d.ts +38 -9
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +39 -24
- 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 +5 -5
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/timings.d.ts +1 -1
- package/dist/core/timings.d.ts.map +1 -1
- package/dist/core/timings.js +3 -2
- package/dist/core/timings.js.map +1 -1
- package/dist/core/tools/ask-user.d.ts +25 -0
- package/dist/core/tools/ask-user.d.ts.map +1 -0
- package/dist/core/tools/ask-user.js +49 -0
- package/dist/core/tools/ask-user.js.map +1 -0
- package/dist/core/tools/bash-command-segments.d.ts +22 -0
- package/dist/core/tools/bash-command-segments.d.ts.map +1 -0
- package/dist/core/tools/bash-command-segments.js +128 -0
- package/dist/core/tools/bash-command-segments.js.map +1 -0
- package/dist/core/tools/bash.d.ts +26 -3
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +105 -16
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/contract.d.ts +156 -0
- package/dist/core/tools/contract.d.ts.map +1 -0
- package/dist/core/tools/contract.js +48 -0
- package/dist/core/tools/contract.js.map +1 -0
- package/dist/core/tools/delegate.d.ts +38 -0
- package/dist/core/tools/delegate.d.ts.map +1 -0
- package/dist/core/tools/delegate.js +66 -0
- package/dist/core/tools/delegate.js.map +1 -0
- package/dist/core/tools/edit.d.ts +2 -2
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +24 -0
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts +2 -2
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +12 -0
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts +2 -2
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +12 -0
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +29 -4
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +118 -2
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts +2 -2
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +12 -0
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +1 -1
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/core/tools/path-permission.d.ts +18 -0
- package/dist/core/tools/path-permission.d.ts.map +1 -0
- package/dist/core/tools/path-permission.js +28 -0
- package/dist/core/tools/path-permission.js.map +1 -0
- package/dist/core/tools/plan-present.d.ts +27 -0
- package/dist/core/tools/plan-present.d.ts.map +1 -0
- package/dist/core/tools/plan-present.js +55 -0
- package/dist/core/tools/plan-present.js.map +1 -0
- package/dist/core/tools/read.d.ts +2 -2
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +12 -0
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/test.d.ts +34 -0
- package/dist/core/tools/test.d.ts.map +1 -0
- package/dist/core/tools/test.js +75 -0
- package/dist/core/tools/test.js.map +1 -0
- package/dist/core/tools/todo-write.d.ts +31 -0
- package/dist/core/tools/todo-write.d.ts.map +1 -0
- package/dist/core/tools/todo-write.js +51 -0
- package/dist/core/tools/todo-write.js.map +1 -0
- package/dist/core/tools/tool-definition-wrapper.d.ts +13 -2
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.js +11 -1
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
- package/dist/core/tools/tool-schema.d.ts +22 -0
- package/dist/core/tools/tool-schema.d.ts.map +1 -0
- package/dist/core/tools/tool-schema.js +45 -0
- package/dist/core/tools/tool-schema.js.map +1 -0
- package/dist/core/tools/web-fetch.d.ts +26 -0
- package/dist/core/tools/web-fetch.d.ts.map +1 -0
- package/dist/core/tools/web-fetch.js +62 -0
- package/dist/core/tools/web-fetch.js.map +1 -0
- package/dist/core/tools/web-search.d.ts +26 -0
- package/dist/core/tools/web-search.d.ts.map +1 -0
- package/dist/core/tools/web-search.js +53 -0
- package/dist/core/tools/web-search.js.map +1 -0
- package/dist/core/tools/write.d.ts +6 -2
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +32 -1
- package/dist/core/tools/write.js.map +1 -1
- package/dist/core/usage-performance-store.d.ts +56 -0
- package/dist/core/usage-performance-store.d.ts.map +1 -0
- package/dist/core/usage-performance-store.js +85 -0
- package/dist/core/usage-performance-store.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +48 -6
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +2 -2
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +9 -6
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.d.ts +1 -4
- package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.js +11 -39
- package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +8 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +49 -20
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +2 -2
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +6 -6
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +2 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts.map +1 -1
- package/dist/modes/interactive/external-editor.js +58 -9
- package/dist/modes/interactive/external-editor.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +2 -3
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +92 -104
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme-schema.json +2 -2
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +15 -10
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/rpc-entry.d.ts.map +1 -1
- package/dist/rpc-entry.js +2 -1
- package/dist/rpc-entry.js.map +1 -1
- package/dist/server/create-harness.d.ts +1 -1
- package/dist/server/create-harness.d.ts.map +1 -1
- package/dist/server/create-harness.js +6 -5
- package/dist/server/create-harness.js.map +1 -1
- package/dist/testing/replay/metrics.d.ts +24 -0
- package/dist/testing/replay/metrics.d.ts.map +1 -0
- package/dist/testing/replay/metrics.js +35 -0
- package/dist/testing/replay/metrics.js.map +1 -0
- package/dist/testing/replay/recorded-provider.d.ts +10 -0
- package/dist/testing/replay/recorded-provider.d.ts.map +1 -0
- package/dist/testing/replay/recorded-provider.js +108 -0
- package/dist/testing/replay/recorded-provider.js.map +1 -0
- package/dist/testing/replay/runner.d.ts +56 -0
- package/dist/testing/replay/runner.d.ts.map +1 -0
- package/dist/testing/replay/runner.js +406 -0
- package/dist/testing/replay/runner.js.map +1 -0
- package/dist/utils/apex-code-user-agent.d.ts +2 -0
- package/dist/utils/apex-code-user-agent.d.ts.map +1 -0
- package/dist/utils/apex-code-user-agent.js +5 -0
- package/dist/utils/apex-code-user-agent.js.map +1 -0
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +1 -1
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +32 -0
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +198 -100
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/version-check.d.ts +5 -5
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +23 -14
- package/dist/utils/version-check.js.map +1 -1
- package/dist/utils/windows-self-update.d.ts.map +1 -1
- package/dist/utils/windows-self-update.js +1 -1
- package/dist/utils/windows-self-update.js.map +1 -1
- package/docs/compaction.md +21 -21
- package/docs/containerization.md +24 -21
- package/docs/custom-provider.md +9 -9
- package/docs/development.md +11 -11
- package/docs/environment-variables.md +36 -77
- package/docs/extensions.md +47 -47
- package/docs/index.md +12 -24
- package/docs/json.md +6 -6
- package/docs/keybindings.md +3 -3
- package/docs/llama-cpp.md +7 -7
- package/docs/models.md +11 -11
- package/docs/packages.md +46 -42
- package/docs/prompt-templates.md +3 -3
- package/docs/providers.md +10 -10
- package/docs/quickstart.md +49 -32
- package/docs/rpc.md +11 -11
- package/docs/sdk.md +4 -4
- package/docs/security.md +18 -18
- package/docs/session-format.md +9 -9
- package/docs/sessions.md +13 -13
- package/docs/settings.md +10 -14
- package/docs/shell-aliases.md +1 -1
- package/docs/skills.md +8 -8
- package/docs/terminal-setup.md +12 -12
- package/docs/termux.md +4 -4
- package/docs/themes.md +8 -8
- package/docs/tmux.md +4 -4
- package/docs/tui.md +3 -3
- package/docs/usage.md +54 -51
- package/docs/windows.md +1 -1
- package/npm-shrinkwrap.json +5 -5
- package/package.json +2 -3
- package/dist/core/telemetry.d.ts +0 -3
- package/dist/core/telemetry.d.ts.map +0 -1
- package/dist/core/telemetry.js +0 -9
- package/dist/core/telemetry.js.map +0 -1
- package/dist/utils/pi-user-agent.d.ts +0 -2
- package/dist/utils/pi-user-agent.d.ts.map +0 -1
- package/dist/utils/pi-user-agent.js +0 -5
- package/dist/utils/pi-user-agent.js.map +0 -1
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/** Non-secret credential selection state for a single process. */
|
|
2
|
+
export function createCredentialIdentity(value) {
|
|
3
|
+
if (!value.trim())
|
|
4
|
+
throw new Error("Credential identity must not be empty");
|
|
5
|
+
return value;
|
|
6
|
+
}
|
|
7
|
+
export class CredentialPool {
|
|
8
|
+
entries;
|
|
9
|
+
now;
|
|
10
|
+
cooldownMs;
|
|
11
|
+
blockedUntilByIdentity = new Map();
|
|
12
|
+
refreshLeaseByIdentity = new Map();
|
|
13
|
+
nextIndex = 0;
|
|
14
|
+
constructor({ entries, now = Date.now, cooldownMs = 60_000 }) {
|
|
15
|
+
const seen = new Set();
|
|
16
|
+
for (const entry of entries) {
|
|
17
|
+
if (seen.has(entry.identity)) {
|
|
18
|
+
throw new Error(`Duplicate credential identity "${entry.identity}"`);
|
|
19
|
+
}
|
|
20
|
+
seen.add(entry.identity);
|
|
21
|
+
}
|
|
22
|
+
this.entries = entries;
|
|
23
|
+
this.now = now;
|
|
24
|
+
this.cooldownMs = cooldownMs;
|
|
25
|
+
}
|
|
26
|
+
select({ providerId, attempted, }) {
|
|
27
|
+
if (this.entries.length === 0)
|
|
28
|
+
return undefined;
|
|
29
|
+
const now = this.now();
|
|
30
|
+
for (let offset = 0; offset < this.entries.length; offset++) {
|
|
31
|
+
const index = (this.nextIndex + offset) % this.entries.length;
|
|
32
|
+
const entry = this.entries[index];
|
|
33
|
+
const blockedUntil = this.blockedUntilByIdentity.get(entry.identity);
|
|
34
|
+
if (entry.providerId !== providerId ||
|
|
35
|
+
attempted.has(entry.identity) ||
|
|
36
|
+
(blockedUntil !== undefined && blockedUntil > now)) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
this.nextIndex = (index + 1) % this.entries.length;
|
|
40
|
+
return entry;
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
recordFailure({ identity, kind }) {
|
|
45
|
+
if (kind === "rate_limited" || kind === "blocked") {
|
|
46
|
+
this.blockedUntilByIdentity.set(identity, this.now() + this.cooldownMs);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
snapshot() {
|
|
50
|
+
return this.entries.map((entry) => ({
|
|
51
|
+
identity: entry.identity,
|
|
52
|
+
providerId: entry.providerId,
|
|
53
|
+
blockedUntil: this.blockedUntilByIdentity.get(entry.identity),
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
activeLease(identity) {
|
|
57
|
+
const lease = this.refreshLeaseByIdentity.get(identity);
|
|
58
|
+
if (!lease)
|
|
59
|
+
return undefined;
|
|
60
|
+
if (lease.expiresAt <= this.now()) {
|
|
61
|
+
this.refreshLeaseByIdentity.delete(identity);
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
return lease;
|
|
65
|
+
}
|
|
66
|
+
/** Grant a refresh lease for an identity, unless another owner already holds an unexpired one. */
|
|
67
|
+
acquireRefreshLease({ identity, owner, durationMs, }) {
|
|
68
|
+
const existing = this.activeLease(identity);
|
|
69
|
+
if (existing && existing.owner !== owner)
|
|
70
|
+
return undefined;
|
|
71
|
+
const lease = { identity, owner, expiresAt: this.now() + durationMs };
|
|
72
|
+
this.refreshLeaseByIdentity.set(identity, lease);
|
|
73
|
+
return lease;
|
|
74
|
+
}
|
|
75
|
+
/** Release a held refresh lease. Returns false if the caller is not the current owner. */
|
|
76
|
+
releaseRefreshLease({ identity, owner }) {
|
|
77
|
+
const existing = this.activeLease(identity);
|
|
78
|
+
if (!existing || existing.owner !== owner)
|
|
79
|
+
return false;
|
|
80
|
+
this.refreshLeaseByIdentity.delete(identity);
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=credential-pool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-pool.js","sourceRoot":"","sources":["../../src/core/credential-pool.ts"],"names":[],"mappings":"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","sourcesContent":["/** Non-secret credential selection state for a single process. */\n\nexport type CredentialIdentity = string & { readonly __brand: \"CredentialIdentity\" };\n\nexport function createCredentialIdentity(value: string): CredentialIdentity {\n\tif (!value.trim()) throw new Error(\"Credential identity must not be empty\");\n\treturn value as CredentialIdentity;\n}\n\nexport interface CredentialPoolEntry {\n\tidentity: CredentialIdentity;\n\tproviderId: string;\n}\n\nexport type CredentialFailureKind = \"rate_limited\" | \"blocked\" | \"temporary\";\n\nexport interface CredentialPoolOptions {\n\tentries: readonly CredentialPoolEntry[];\n\tnow?: () => number;\n\tcooldownMs?: number;\n}\n\nexport interface CredentialPoolSelection {\n\tidentity: CredentialIdentity;\n\tproviderId: string;\n}\n\nexport interface CredentialPoolSnapshotEntry {\n\tidentity: CredentialIdentity;\n\tproviderId: string;\n\tblockedUntil: number | undefined;\n}\n\nexport interface CredentialRefreshLease {\n\tidentity: CredentialIdentity;\n\towner: string;\n\texpiresAt: number;\n}\n\nexport class CredentialPool {\n\tprivate readonly entries: readonly CredentialPoolEntry[];\n\tprivate readonly now: () => number;\n\tprivate readonly cooldownMs: number;\n\tprivate readonly blockedUntilByIdentity = new Map<CredentialIdentity, number>();\n\tprivate readonly refreshLeaseByIdentity = new Map<CredentialIdentity, CredentialRefreshLease>();\n\tprivate nextIndex = 0;\n\n\tconstructor({ entries, now = Date.now, cooldownMs = 60_000 }: CredentialPoolOptions) {\n\t\tconst seen = new Set<CredentialIdentity>();\n\t\tfor (const entry of entries) {\n\t\t\tif (seen.has(entry.identity)) {\n\t\t\t\tthrow new Error(`Duplicate credential identity \"${entry.identity}\"`);\n\t\t\t}\n\t\t\tseen.add(entry.identity);\n\t\t}\n\t\tthis.entries = entries;\n\t\tthis.now = now;\n\t\tthis.cooldownMs = cooldownMs;\n\t}\n\n\tselect({\n\t\tproviderId,\n\t\tattempted,\n\t}: {\n\t\tproviderId: string;\n\t\tattempted: ReadonlySet<CredentialIdentity>;\n\t}): CredentialPoolSelection | undefined {\n\t\tif (this.entries.length === 0) return undefined;\n\t\tconst now = this.now();\n\t\tfor (let offset = 0; offset < this.entries.length; offset++) {\n\t\t\tconst index = (this.nextIndex + offset) % this.entries.length;\n\t\t\tconst entry = this.entries[index];\n\t\t\tconst blockedUntil = this.blockedUntilByIdentity.get(entry.identity);\n\t\t\tif (\n\t\t\t\tentry.providerId !== providerId ||\n\t\t\t\tattempted.has(entry.identity) ||\n\t\t\t\t(blockedUntil !== undefined && blockedUntil > now)\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.nextIndex = (index + 1) % this.entries.length;\n\t\t\treturn entry;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\trecordFailure({ identity, kind }: { identity: CredentialIdentity; kind: CredentialFailureKind }): void {\n\t\tif (kind === \"rate_limited\" || kind === \"blocked\") {\n\t\t\tthis.blockedUntilByIdentity.set(identity, this.now() + this.cooldownMs);\n\t\t}\n\t}\n\n\tsnapshot(): CredentialPoolSnapshotEntry[] {\n\t\treturn this.entries.map((entry) => ({\n\t\t\tidentity: entry.identity,\n\t\t\tproviderId: entry.providerId,\n\t\t\tblockedUntil: this.blockedUntilByIdentity.get(entry.identity),\n\t\t}));\n\t}\n\n\tprivate activeLease(identity: CredentialIdentity): CredentialRefreshLease | undefined {\n\t\tconst lease = this.refreshLeaseByIdentity.get(identity);\n\t\tif (!lease) return undefined;\n\t\tif (lease.expiresAt <= this.now()) {\n\t\t\tthis.refreshLeaseByIdentity.delete(identity);\n\t\t\treturn undefined;\n\t\t}\n\t\treturn lease;\n\t}\n\n\t/** Grant a refresh lease for an identity, unless another owner already holds an unexpired one. */\n\tacquireRefreshLease({\n\t\tidentity,\n\t\towner,\n\t\tdurationMs,\n\t}: {\n\t\tidentity: CredentialIdentity;\n\t\towner: string;\n\t\tdurationMs: number;\n\t}): CredentialRefreshLease | undefined {\n\t\tconst existing = this.activeLease(identity);\n\t\tif (existing && existing.owner !== owner) return undefined;\n\t\tconst lease: CredentialRefreshLease = { identity, owner, expiresAt: this.now() + durationMs };\n\t\tthis.refreshLeaseByIdentity.set(identity, lease);\n\t\treturn lease;\n\t}\n\n\t/** Release a held refresh lease. Returns false if the caller is not the current owner. */\n\treleaseRefreshLease({ identity, owner }: { identity: CredentialIdentity; owner: string }): boolean {\n\t\tconst existing = this.activeLease(identity);\n\t\tif (!existing || existing.owner !== owner) return false;\n\t\tthis.refreshLeaseByIdentity.delete(identity);\n\t\treturn true;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Markdown agent-definition discovery (roadmap Phase 5, task 5.5).
|
|
2
|
+
* User definitions are always eligible. Project definitions are eligible only
|
|
3
|
+
* through the existing project-trust decision; discovery never prompts or creates
|
|
4
|
+
* a second trust mechanism. Files are parsed on lookup so edits take effect on the
|
|
5
|
+
* next delegation and malformed definitions simply remain unavailable.
|
|
6
|
+
*/
|
|
7
|
+
import type { AgentDefinitionResolver } from "./runtime.ts";
|
|
8
|
+
export interface CreateAgentDefinitionResolverOptions {
|
|
9
|
+
cwd: string;
|
|
10
|
+
agentDir: string;
|
|
11
|
+
/** Delegated to SettingsManager.isProjectTrusted() in production. */
|
|
12
|
+
isProjectTrusted: () => boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare function createAgentDefinitionResolver(options: CreateAgentDefinitionResolverOptions): AgentDefinitionResolver;
|
|
15
|
+
//# sourceMappingURL=agents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../../src/core/delegation/agents.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAmB,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAS7E,MAAM,WAAW,oCAAoC;IACpD,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,gBAAgB,EAAE,MAAM,OAAO,CAAC;CAChC;AAyCD,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,oCAAoC,GAAG,uBAAuB,CAKpH","sourcesContent":["/** Markdown agent-definition discovery (roadmap Phase 5, task 5.5).\n * User definitions are always eligible. Project definitions are eligible only\n * through the existing project-trust decision; discovery never prompts or creates\n * a second trust mechanism. Files are parsed on lookup so edits take effect on the\n * next delegation and malformed definitions simply remain unavailable.\n */\n\nimport { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { parseFrontmatter } from \"../../utils/frontmatter.ts\";\nimport type { AgentDefinition, AgentDefinitionResolver } from \"./runtime.ts\";\n\ninterface AgentFrontmatter extends Record<string, unknown> {\n\tname?: unknown;\n\tdescription?: unknown;\n\ttools?: unknown;\n\tmodel?: unknown;\n}\n\nexport interface CreateAgentDefinitionResolverOptions {\n\tcwd: string;\n\tagentDir: string;\n\t/** Delegated to SettingsManager.isProjectTrusted() in production. */\n\tisProjectTrusted: () => boolean;\n}\n\nfunction parseDefinition(content: string): AgentDefinition | undefined {\n\ttry {\n\t\tconst { frontmatter, body } = parseFrontmatter<AgentFrontmatter>(content);\n\t\tif (\n\t\t\ttypeof frontmatter.name !== \"string\" ||\n\t\t\t!frontmatter.name ||\n\t\t\ttypeof frontmatter.description !== \"string\" ||\n\t\t\t!frontmatter.description ||\n\t\t\t!Array.isArray(frontmatter.tools) ||\n\t\t\t!frontmatter.tools.every((tool): tool is string => typeof tool === \"string\" && tool.length > 0) ||\n\t\t\t(frontmatter.model !== undefined && (typeof frontmatter.model !== \"string\" || !frontmatter.model))\n\t\t)\n\t\t\treturn undefined;\n\t\treturn {\n\t\t\tname: frontmatter.name,\n\t\t\tdescription: frontmatter.description,\n\t\t\ttools: [...frontmatter.tools],\n\t\t\tmodel: frontmatter.model,\n\t\t\tsystemPrompt: body,\n\t\t};\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction discover(dir: string, agentType: string): AgentDefinition | undefined {\n\tif (!existsSync(dir)) return undefined;\n\ttry {\n\t\tfor (const filename of readdirSync(dir).sort()) {\n\t\t\tif (!filename.endsWith(\".md\")) continue;\n\t\t\tconst definition = parseDefinition(readFileSync(join(dir, filename), \"utf8\"));\n\t\t\tif (definition?.name === agentType) return definition;\n\t\t}\n\t} catch {\n\t\t// A directory that cannot be read behaves as having no usable definitions.\n\t}\n\treturn undefined;\n}\n\nexport function createAgentDefinitionResolver(options: CreateAgentDefinitionResolverOptions): AgentDefinitionResolver {\n\tconst userDir = join(options.agentDir, \"agents\");\n\tconst projectDir = join(options.cwd, \".apex-code\", \"agents\");\n\treturn (agentType) =>\n\t\tdiscover(userDir, agentType) ?? (options.isProjectTrusted() ? discover(projectDir, agentType) : undefined);\n}\n"]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** Markdown agent-definition discovery (roadmap Phase 5, task 5.5).
|
|
2
|
+
* User definitions are always eligible. Project definitions are eligible only
|
|
3
|
+
* through the existing project-trust decision; discovery never prompts or creates
|
|
4
|
+
* a second trust mechanism. Files are parsed on lookup so edits take effect on the
|
|
5
|
+
* next delegation and malformed definitions simply remain unavailable.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { parseFrontmatter } from "../../utils/frontmatter.js";
|
|
10
|
+
function parseDefinition(content) {
|
|
11
|
+
try {
|
|
12
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
13
|
+
if (typeof frontmatter.name !== "string" ||
|
|
14
|
+
!frontmatter.name ||
|
|
15
|
+
typeof frontmatter.description !== "string" ||
|
|
16
|
+
!frontmatter.description ||
|
|
17
|
+
!Array.isArray(frontmatter.tools) ||
|
|
18
|
+
!frontmatter.tools.every((tool) => typeof tool === "string" && tool.length > 0) ||
|
|
19
|
+
(frontmatter.model !== undefined && (typeof frontmatter.model !== "string" || !frontmatter.model)))
|
|
20
|
+
return undefined;
|
|
21
|
+
return {
|
|
22
|
+
name: frontmatter.name,
|
|
23
|
+
description: frontmatter.description,
|
|
24
|
+
tools: [...frontmatter.tools],
|
|
25
|
+
model: frontmatter.model,
|
|
26
|
+
systemPrompt: body,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function discover(dir, agentType) {
|
|
34
|
+
if (!existsSync(dir))
|
|
35
|
+
return undefined;
|
|
36
|
+
try {
|
|
37
|
+
for (const filename of readdirSync(dir).sort()) {
|
|
38
|
+
if (!filename.endsWith(".md"))
|
|
39
|
+
continue;
|
|
40
|
+
const definition = parseDefinition(readFileSync(join(dir, filename), "utf8"));
|
|
41
|
+
if (definition?.name === agentType)
|
|
42
|
+
return definition;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// A directory that cannot be read behaves as having no usable definitions.
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
export function createAgentDefinitionResolver(options) {
|
|
51
|
+
const userDir = join(options.agentDir, "agents");
|
|
52
|
+
const projectDir = join(options.cwd, ".apex-code", "agents");
|
|
53
|
+
return (agentType) => discover(userDir, agentType) ?? (options.isProjectTrusted() ? discover(projectDir, agentType) : undefined);
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=agents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../../../src/core/delegation/agents.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAiB9D,SAAS,eAAe,CAAC,OAAe,EAA+B;IACtE,IAAI,CAAC;QACJ,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,GAAG,gBAAgB,CAAmB,OAAO,CAAC,CAAC;QAC1E,IACC,OAAO,WAAW,CAAC,IAAI,KAAK,QAAQ;YACpC,CAAC,WAAW,CAAC,IAAI;YACjB,OAAO,WAAW,CAAC,WAAW,KAAK,QAAQ;YAC3C,CAAC,WAAW,CAAC,WAAW;YACxB,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC;YACjC,CAAC,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;YAC/F,CAAC,WAAW,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,OAAO,WAAW,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YAElG,OAAO,SAAS,CAAC;QAClB,OAAO;YACN,IAAI,EAAE,WAAW,CAAC,IAAI;YACtB,WAAW,EAAE,WAAW,CAAC,WAAW;YACpC,KAAK,EAAE,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC;YAC7B,KAAK,EAAE,WAAW,CAAC,KAAK;YACxB,YAAY,EAAE,IAAI;SAClB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,SAAS,CAAC;IAClB,CAAC;AAAA,CACD;AAED,SAAS,QAAQ,CAAC,GAAW,EAAE,SAAiB,EAA+B;IAC9E,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IACvC,IAAI,CAAC;QACJ,KAAK,MAAM,QAAQ,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YAChD,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAAE,SAAS;YACxC,MAAM,UAAU,GAAG,eAAe,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;YAC9E,IAAI,UAAU,EAAE,IAAI,KAAK,SAAS;gBAAE,OAAO,UAAU,CAAC;QACvD,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;QACR,2EAA2E;IAC5E,CAAC;IACD,OAAO,SAAS,CAAC;AAAA,CACjB;AAED,MAAM,UAAU,6BAA6B,CAAC,OAA6C,EAA2B;IACrH,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC;IAC7D,OAAO,CAAC,SAAS,EAAE,EAAE,CACpB,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AAAA,CAC5G","sourcesContent":["/** Markdown agent-definition discovery (roadmap Phase 5, task 5.5).\n * User definitions are always eligible. Project definitions are eligible only\n * through the existing project-trust decision; discovery never prompts or creates\n * a second trust mechanism. Files are parsed on lookup so edits take effect on the\n * next delegation and malformed definitions simply remain unavailable.\n */\n\nimport { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { parseFrontmatter } from \"../../utils/frontmatter.ts\";\nimport type { AgentDefinition, AgentDefinitionResolver } from \"./runtime.ts\";\n\ninterface AgentFrontmatter extends Record<string, unknown> {\n\tname?: unknown;\n\tdescription?: unknown;\n\ttools?: unknown;\n\tmodel?: unknown;\n}\n\nexport interface CreateAgentDefinitionResolverOptions {\n\tcwd: string;\n\tagentDir: string;\n\t/** Delegated to SettingsManager.isProjectTrusted() in production. */\n\tisProjectTrusted: () => boolean;\n}\n\nfunction parseDefinition(content: string): AgentDefinition | undefined {\n\ttry {\n\t\tconst { frontmatter, body } = parseFrontmatter<AgentFrontmatter>(content);\n\t\tif (\n\t\t\ttypeof frontmatter.name !== \"string\" ||\n\t\t\t!frontmatter.name ||\n\t\t\ttypeof frontmatter.description !== \"string\" ||\n\t\t\t!frontmatter.description ||\n\t\t\t!Array.isArray(frontmatter.tools) ||\n\t\t\t!frontmatter.tools.every((tool): tool is string => typeof tool === \"string\" && tool.length > 0) ||\n\t\t\t(frontmatter.model !== undefined && (typeof frontmatter.model !== \"string\" || !frontmatter.model))\n\t\t)\n\t\t\treturn undefined;\n\t\treturn {\n\t\t\tname: frontmatter.name,\n\t\t\tdescription: frontmatter.description,\n\t\t\ttools: [...frontmatter.tools],\n\t\t\tmodel: frontmatter.model,\n\t\t\tsystemPrompt: body,\n\t\t};\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction discover(dir: string, agentType: string): AgentDefinition | undefined {\n\tif (!existsSync(dir)) return undefined;\n\ttry {\n\t\tfor (const filename of readdirSync(dir).sort()) {\n\t\t\tif (!filename.endsWith(\".md\")) continue;\n\t\t\tconst definition = parseDefinition(readFileSync(join(dir, filename), \"utf8\"));\n\t\t\tif (definition?.name === agentType) return definition;\n\t\t}\n\t} catch {\n\t\t// A directory that cannot be read behaves as having no usable definitions.\n\t}\n\treturn undefined;\n}\n\nexport function createAgentDefinitionResolver(options: CreateAgentDefinitionResolverOptions): AgentDefinitionResolver {\n\tconst userDir = join(options.agentDir, \"agents\");\n\tconst projectDir = join(options.cwd, \".apex-code\", \"agents\");\n\treturn (agentType) =>\n\t\tdiscover(userDir, agentType) ?? (options.isProjectTrusted() ? discover(projectDir, agentType) : undefined);\n}\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The capability ceiling (roadmap Phase 5, ADR 0008, contracts.md §1.1 invariant 4).
|
|
3
|
+
* A pure function over capability sets: no knowledge of tools, sessions, or agent
|
|
4
|
+
* definitions. A delegated child's requested capability set must be a subset of its
|
|
5
|
+
* parent's, with `exec` expanded to the full set first -- a subprocess a parent can
|
|
6
|
+
* spawn can reach every other capability, so a parent holding `exec` is treated as
|
|
7
|
+
* holding everything for ceiling purposes (contracts.md §1.1's escalation rule).
|
|
8
|
+
*
|
|
9
|
+
* Refuses rather than narrows on a partial match: a request outside the ceiling names
|
|
10
|
+
* the offending capability and stops, instead of silently admitting the child with
|
|
11
|
+
* less than it asked for. A silently narrowed child produces work that looks complete
|
|
12
|
+
* and is not, which is worse than a refusal the caller can read and route around.
|
|
13
|
+
*/
|
|
14
|
+
import { type Capability } from "../tools/contract.ts";
|
|
15
|
+
export type CapabilityCeilingResult = {
|
|
16
|
+
allowed: true;
|
|
17
|
+
capabilities: ReadonlySet<Capability>;
|
|
18
|
+
} | {
|
|
19
|
+
allowed: false;
|
|
20
|
+
deniedCapability: Capability;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Admits the exact `requested` set when every capability in it is covered by the
|
|
24
|
+
* (possibly exec-expanded) `parent` set. The admitted set is the request, not the
|
|
25
|
+
* expanded parent -- a child is granted what its own tools need, not everything its
|
|
26
|
+
* parent could theoretically reach.
|
|
27
|
+
*/
|
|
28
|
+
export declare function computeCapabilityCeiling(parent: ReadonlySet<Capability>, requested: ReadonlySet<Capability>): CapabilityCeilingResult;
|
|
29
|
+
//# sourceMappingURL=ceiling.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ceiling.d.ts","sourceRoot":"","sources":["../../../src/core/delegation/ceiling.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEzE,MAAM,MAAM,uBAAuB,GAChC;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,YAAY,EAAE,WAAW,CAAC,UAAU,CAAC,CAAA;CAAE,GACxD;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,gBAAgB,EAAE,UAAU,CAAA;CAAE,CAAC;AAOpD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACvC,MAAM,EAAE,WAAW,CAAC,UAAU,CAAC,EAC/B,SAAS,EAAE,WAAW,CAAC,UAAU,CAAC,GAChC,uBAAuB,CAQzB","sourcesContent":["/**\n * The capability ceiling (roadmap Phase 5, ADR 0008, contracts.md §1.1 invariant 4).\n * A pure function over capability sets: no knowledge of tools, sessions, or agent\n * definitions. A delegated child's requested capability set must be a subset of its\n * parent's, with `exec` expanded to the full set first -- a subprocess a parent can\n * spawn can reach every other capability, so a parent holding `exec` is treated as\n * holding everything for ceiling purposes (contracts.md §1.1's escalation rule).\n *\n * Refuses rather than narrows on a partial match: a request outside the ceiling names\n * the offending capability and stops, instead of silently admitting the child with\n * less than it asked for. A silently narrowed child produces work that looks complete\n * and is not, which is worse than a refusal the caller can read and route around.\n */\n\nimport { ALL_CAPABILITIES, type Capability } from \"../tools/contract.ts\";\n\nexport type CapabilityCeilingResult =\n\t| { allowed: true; capabilities: ReadonlySet<Capability> }\n\t| { allowed: false; deniedCapability: Capability };\n\n/** `exec` implies every other capability (contracts.md §1.1); every other capability stands alone. */\nfunction expandCapabilities(capabilities: ReadonlySet<Capability>): ReadonlySet<Capability> {\n\treturn capabilities.has(\"exec\") ? ALL_CAPABILITIES : capabilities;\n}\n\n/**\n * Admits the exact `requested` set when every capability in it is covered by the\n * (possibly exec-expanded) `parent` set. The admitted set is the request, not the\n * expanded parent -- a child is granted what its own tools need, not everything its\n * parent could theoretically reach.\n */\nexport function computeCapabilityCeiling(\n\tparent: ReadonlySet<Capability>,\n\trequested: ReadonlySet<Capability>,\n): CapabilityCeilingResult {\n\tconst expandedParent = expandCapabilities(parent);\n\tfor (const capability of requested) {\n\t\tif (!expandedParent.has(capability)) {\n\t\t\treturn { allowed: false, deniedCapability: capability };\n\t\t}\n\t}\n\treturn { allowed: true, capabilities: requested };\n}\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The capability ceiling (roadmap Phase 5, ADR 0008, contracts.md §1.1 invariant 4).
|
|
3
|
+
* A pure function over capability sets: no knowledge of tools, sessions, or agent
|
|
4
|
+
* definitions. A delegated child's requested capability set must be a subset of its
|
|
5
|
+
* parent's, with `exec` expanded to the full set first -- a subprocess a parent can
|
|
6
|
+
* spawn can reach every other capability, so a parent holding `exec` is treated as
|
|
7
|
+
* holding everything for ceiling purposes (contracts.md §1.1's escalation rule).
|
|
8
|
+
*
|
|
9
|
+
* Refuses rather than narrows on a partial match: a request outside the ceiling names
|
|
10
|
+
* the offending capability and stops, instead of silently admitting the child with
|
|
11
|
+
* less than it asked for. A silently narrowed child produces work that looks complete
|
|
12
|
+
* and is not, which is worse than a refusal the caller can read and route around.
|
|
13
|
+
*/
|
|
14
|
+
import { ALL_CAPABILITIES } from "../tools/contract.js";
|
|
15
|
+
/** `exec` implies every other capability (contracts.md §1.1); every other capability stands alone. */
|
|
16
|
+
function expandCapabilities(capabilities) {
|
|
17
|
+
return capabilities.has("exec") ? ALL_CAPABILITIES : capabilities;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Admits the exact `requested` set when every capability in it is covered by the
|
|
21
|
+
* (possibly exec-expanded) `parent` set. The admitted set is the request, not the
|
|
22
|
+
* expanded parent -- a child is granted what its own tools need, not everything its
|
|
23
|
+
* parent could theoretically reach.
|
|
24
|
+
*/
|
|
25
|
+
export function computeCapabilityCeiling(parent, requested) {
|
|
26
|
+
const expandedParent = expandCapabilities(parent);
|
|
27
|
+
for (const capability of requested) {
|
|
28
|
+
if (!expandedParent.has(capability)) {
|
|
29
|
+
return { allowed: false, deniedCapability: capability };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return { allowed: true, capabilities: requested };
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=ceiling.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ceiling.js","sourceRoot":"","sources":["../../../src/core/delegation/ceiling.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,gBAAgB,EAAmB,MAAM,sBAAsB,CAAC;AAMzE,uGAAsG;AACtG,SAAS,kBAAkB,CAAC,YAAqC,EAA2B;IAC3F,OAAO,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,YAAY,CAAC;AAAA,CAClE;AAED;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CACvC,MAA+B,EAC/B,SAAkC,EACR;IAC1B,MAAM,cAAc,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAClD,KAAK,MAAM,UAAU,IAAI,SAAS,EAAE,CAAC;QACpC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;YACrC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,CAAC;QACzD,CAAC;IACF,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;AAAA,CAClD","sourcesContent":["/**\n * The capability ceiling (roadmap Phase 5, ADR 0008, contracts.md §1.1 invariant 4).\n * A pure function over capability sets: no knowledge of tools, sessions, or agent\n * definitions. A delegated child's requested capability set must be a subset of its\n * parent's, with `exec` expanded to the full set first -- a subprocess a parent can\n * spawn can reach every other capability, so a parent holding `exec` is treated as\n * holding everything for ceiling purposes (contracts.md §1.1's escalation rule).\n *\n * Refuses rather than narrows on a partial match: a request outside the ceiling names\n * the offending capability and stops, instead of silently admitting the child with\n * less than it asked for. A silently narrowed child produces work that looks complete\n * and is not, which is worse than a refusal the caller can read and route around.\n */\n\nimport { ALL_CAPABILITIES, type Capability } from \"../tools/contract.ts\";\n\nexport type CapabilityCeilingResult =\n\t| { allowed: true; capabilities: ReadonlySet<Capability> }\n\t| { allowed: false; deniedCapability: Capability };\n\n/** `exec` implies every other capability (contracts.md §1.1); every other capability stands alone. */\nfunction expandCapabilities(capabilities: ReadonlySet<Capability>): ReadonlySet<Capability> {\n\treturn capabilities.has(\"exec\") ? ALL_CAPABILITIES : capabilities;\n}\n\n/**\n * Admits the exact `requested` set when every capability in it is covered by the\n * (possibly exec-expanded) `parent` set. The admitted set is the request, not the\n * expanded parent -- a child is granted what its own tools need, not everything its\n * parent could theoretically reach.\n */\nexport function computeCapabilityCeiling(\n\tparent: ReadonlySet<Capability>,\n\trequested: ReadonlySet<Capability>,\n): CapabilityCeilingResult {\n\tconst expandedParent = expandCapabilities(parent);\n\tfor (const capability of requested) {\n\t\tif (!expandedParent.has(capability)) {\n\t\t\treturn { allowed: false, deniedCapability: capability };\n\t\t}\n\t}\n\treturn { allowed: true, capabilities: requested };\n}\n"]}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The delegation runtime (roadmap Phase 5, ADR 0008, task 5.2). Consumes the
|
|
3
|
+
* capability ceiling (ceiling.ts) and a derived permission store
|
|
4
|
+
* (`../permissions/store.ts`'s `DerivedPermissionRuleStore`) to run a real child
|
|
5
|
+
* agent whose authority can never exceed its parent's.
|
|
6
|
+
*
|
|
7
|
+
* Agent definitions and child-session construction are both injected rather than
|
|
8
|
+
* built in here: agent discovery is a test fixture in this task and becomes real
|
|
9
|
+
* markdown/frontmatter discovery in task 5.5 (`core/delegation/agents.ts`, not yet
|
|
10
|
+
* written); child-session construction is injected so this module stays free of
|
|
11
|
+
* `AgentSession`/`createAgentSession` and therefore free of the import cycle that
|
|
12
|
+
* would create (`sdk.ts` already imports `agent-session.ts`).
|
|
13
|
+
*/
|
|
14
|
+
import type { Capability } from "../tools/contract.ts";
|
|
15
|
+
/** A delegatable agent's static configuration. Markdown + frontmatter in production (task 5.5); a plain object in tests until then. */
|
|
16
|
+
export interface AgentDefinition {
|
|
17
|
+
name: string;
|
|
18
|
+
description: string;
|
|
19
|
+
/** Tool names this agent may use. Never trusted directly -- the runtime derives the actual child tool list from the computed capability set, not from this list verbatim. */
|
|
20
|
+
tools: string[];
|
|
21
|
+
/** Optional model id. Falls back to the parent's current model when unset or unresolvable. */
|
|
22
|
+
model?: string;
|
|
23
|
+
systemPrompt: string;
|
|
24
|
+
}
|
|
25
|
+
/** Resolve an agent type to its definition, or `undefined` if unknown. A plain function fixture in this task; real markdown/frontmatter discovery (task 5.5) is just a different implementation of the same shape. */
|
|
26
|
+
export type AgentDefinitionResolver = (agentType: string) => AgentDefinition | undefined;
|
|
27
|
+
/** A running or completed child, as far as the runtime needs to know. */
|
|
28
|
+
export interface ChildSessionHandle {
|
|
29
|
+
/** Run the task to completion and return the child's final output text. */
|
|
30
|
+
run(task: string): Promise<{
|
|
31
|
+
output: string;
|
|
32
|
+
}>;
|
|
33
|
+
/** Release the child's resources. Always called, including after a failed run. */
|
|
34
|
+
dispose(): void;
|
|
35
|
+
}
|
|
36
|
+
export interface BuildChildSessionRequest {
|
|
37
|
+
agentType: string;
|
|
38
|
+
definition: AgentDefinition;
|
|
39
|
+
/** The child's tool allowlist, already ceiling-checked -- exactly `definition.tools`, never narrowed. */
|
|
40
|
+
toolNames: string[];
|
|
41
|
+
/** The child's own recursion depth (the parent's depth + 1), for the runtime constructing it to record on the child's session header (task 5.3). */
|
|
42
|
+
depth: number;
|
|
43
|
+
/** Stable id used for the child session and its artifact directory. */
|
|
44
|
+
sessionId: string;
|
|
45
|
+
/** Per-child artifact root, created before the child session is constructed. */
|
|
46
|
+
artifactDir?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface DelegationRuntimeOptions {
|
|
49
|
+
resolveAgent: AgentDefinitionResolver;
|
|
50
|
+
/** The parent's own effective capability set (its authority), unexpanded -- `exec` expansion happens inside the ceiling check, not here. */
|
|
51
|
+
getParentCapabilities: () => ReadonlySet<Capability>;
|
|
52
|
+
/** Capability set a named tool requires, from the live registry. `undefined` for an unknown tool name. */
|
|
53
|
+
getToolCapabilities: (toolName: string) => ReadonlySet<Capability> | undefined;
|
|
54
|
+
/** The parent session's own recursion depth (`SessionManager.getDelegationDepth()`), read fresh at delegation time. */
|
|
55
|
+
getDelegationDepth: () => number;
|
|
56
|
+
/** Delegation is refused once `getDelegationDepth() >= maxDelegationDepth` -- assigned by the runtime, not by the tool, so the bound applies to any future delegation entry point. */
|
|
57
|
+
maxDelegationDepth: number;
|
|
58
|
+
/** Parent session directory. When supplied, child artifacts are rooted beneath it. */
|
|
59
|
+
getParentSessionDir?: () => string;
|
|
60
|
+
buildChildSession: (request: BuildChildSessionRequest) => Promise<ChildSessionHandle>;
|
|
61
|
+
}
|
|
62
|
+
export interface DelegationResult {
|
|
63
|
+
agentType: string;
|
|
64
|
+
task: string;
|
|
65
|
+
output: string;
|
|
66
|
+
/** Present for background work; pass this handle to retrieveDelegationResult. */
|
|
67
|
+
handleId?: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Run one delegation to completion. Throws rather than returning a failure value,
|
|
71
|
+
* matching this codebase's convention for model-facing routine failures (e.g.
|
|
72
|
+
* `tool_schema`'s unknown-tool case, `web_search`'s unconfigured-backend case): a
|
|
73
|
+
* thrown `Error` here is caught by the agent loop and surfaces as a normal,
|
|
74
|
+
* model-readable `isError` tool result, not a fatal turn failure.
|
|
75
|
+
*
|
|
76
|
+
* The capability check is all-or-nothing by design (ceiling.ts): a definition
|
|
77
|
+
* requesting a tool the parent's own capabilities cannot cover refuses the whole
|
|
78
|
+
* delegation, naming the capability, rather than silently building a child missing
|
|
79
|
+
* that one tool. `buildChildSession` is therefore never called for a refused
|
|
80
|
+
* delegation -- there is no code path that produces a child holding a capability
|
|
81
|
+
* outside the ceiling.
|
|
82
|
+
*
|
|
83
|
+
* The depth check (task 5.3) runs right after agent-type resolution and before the
|
|
84
|
+
* capability check, so recursion is bounded regardless of what is being delegated --
|
|
85
|
+
* a refusal at the bound still names the agent type, but never reaches the ceiling
|
|
86
|
+
* or `buildChildSession`. `buildChildSession` receives the child's depth (parent + 1)
|
|
87
|
+
* so the caller can record it on the child's own session header.
|
|
88
|
+
*/
|
|
89
|
+
export declare function runDelegation(options: DelegationRuntimeOptions, agentType: string, task: string, request?: {
|
|
90
|
+
background?: boolean;
|
|
91
|
+
}): Promise<DelegationResult>;
|
|
92
|
+
/** Retrieve a background delegation. Running children are awaited; unknown handles fail explicitly. */
|
|
93
|
+
export declare function retrieveDelegationResult(options: DelegationRuntimeOptions, handleId: string, expectedAgentType?: string): Promise<DelegationResult>;
|
|
94
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/core/delegation/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGvD,uIAAuI;AACvI,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,6KAA6K;IAC7K,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,8FAA8F;IAC9F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACrB;AAED,sNAAsN;AACtN,MAAM,MAAM,uBAAuB,GAAG,CAAC,SAAS,EAAE,MAAM,KAAK,eAAe,GAAG,SAAS,CAAC;AAEzF,yEAAyE;AACzE,MAAM,WAAW,kBAAkB;IAClC,2EAA2E;IAC3E,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,kFAAkF;IAClF,OAAO,IAAI,IAAI,CAAC;CAChB;AAED,MAAM,WAAW,wBAAwB;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,eAAe,CAAC;IAC5B,yGAAyG;IACzG,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,oJAAoJ;IACpJ,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB;IACxC,YAAY,EAAE,uBAAuB,CAAC;IACtC,4IAA4I;IAC5I,qBAAqB,EAAE,MAAM,WAAW,CAAC,UAAU,CAAC,CAAC;IACrD,0GAA0G;IAC1G,mBAAmB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,WAAW,CAAC,UAAU,CAAC,GAAG,SAAS,CAAC;IAC/E,uHAAuH;IACvH,kBAAkB,EAAE,MAAM,MAAM,CAAC;IACjC,sLAAsL;IACtL,kBAAkB,EAAE,MAAM,CAAC;IAC3B,sFAAsF;IACtF,mBAAmB,CAAC,EAAE,MAAM,MAAM,CAAC;IACnC,iBAAiB,EAAE,CAAC,OAAO,EAAE,wBAAwB,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACtF;AAED,MAAM,WAAW,gBAAgB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAoBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,aAAa,CAClC,OAAO,EAAE,wBAAwB,EACjC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GACpC,OAAO,CAAC,gBAAgB,CAAC,CA8D3B;AAED,uGAAuG;AACvG,wBAAsB,wBAAwB,CAC7C,OAAO,EAAE,wBAAwB,EACjC,QAAQ,EAAE,MAAM,EAChB,iBAAiB,CAAC,EAAE,MAAM,GACxB,OAAO,CAAC,gBAAgB,CAAC,CAS3B","sourcesContent":["/**\n * The delegation runtime (roadmap Phase 5, ADR 0008, task 5.2). Consumes the\n * capability ceiling (ceiling.ts) and a derived permission store\n * (`../permissions/store.ts`'s `DerivedPermissionRuleStore`) to run a real child\n * agent whose authority can never exceed its parent's.\n *\n * Agent definitions and child-session construction are both injected rather than\n * built in here: agent discovery is a test fixture in this task and becomes real\n * markdown/frontmatter discovery in task 5.5 (`core/delegation/agents.ts`, not yet\n * written); child-session construction is injected so this module stays free of\n * `AgentSession`/`createAgentSession` and therefore free of the import cycle that\n * would create (`sdk.ts` already imports `agent-session.ts`).\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { mkdirSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport type { Capability } from \"../tools/contract.ts\";\nimport { computeCapabilityCeiling } from \"./ceiling.ts\";\n\n/** A delegatable agent's static configuration. Markdown + frontmatter in production (task 5.5); a plain object in tests until then. */\nexport interface AgentDefinition {\n\tname: string;\n\tdescription: string;\n\t/** Tool names this agent may use. Never trusted directly -- the runtime derives the actual child tool list from the computed capability set, not from this list verbatim. */\n\ttools: string[];\n\t/** Optional model id. Falls back to the parent's current model when unset or unresolvable. */\n\tmodel?: string;\n\tsystemPrompt: string;\n}\n\n/** Resolve an agent type to its definition, or `undefined` if unknown. A plain function fixture in this task; real markdown/frontmatter discovery (task 5.5) is just a different implementation of the same shape. */\nexport type AgentDefinitionResolver = (agentType: string) => AgentDefinition | undefined;\n\n/** A running or completed child, as far as the runtime needs to know. */\nexport interface ChildSessionHandle {\n\t/** Run the task to completion and return the child's final output text. */\n\trun(task: string): Promise<{ output: string }>;\n\t/** Release the child's resources. Always called, including after a failed run. */\n\tdispose(): void;\n}\n\nexport interface BuildChildSessionRequest {\n\tagentType: string;\n\tdefinition: AgentDefinition;\n\t/** The child's tool allowlist, already ceiling-checked -- exactly `definition.tools`, never narrowed. */\n\ttoolNames: string[];\n\t/** The child's own recursion depth (the parent's depth + 1), for the runtime constructing it to record on the child's session header (task 5.3). */\n\tdepth: number;\n\t/** Stable id used for the child session and its artifact directory. */\n\tsessionId: string;\n\t/** Per-child artifact root, created before the child session is constructed. */\n\tartifactDir?: string;\n}\n\nexport interface DelegationRuntimeOptions {\n\tresolveAgent: AgentDefinitionResolver;\n\t/** The parent's own effective capability set (its authority), unexpanded -- `exec` expansion happens inside the ceiling check, not here. */\n\tgetParentCapabilities: () => ReadonlySet<Capability>;\n\t/** Capability set a named tool requires, from the live registry. `undefined` for an unknown tool name. */\n\tgetToolCapabilities: (toolName: string) => ReadonlySet<Capability> | undefined;\n\t/** The parent session's own recursion depth (`SessionManager.getDelegationDepth()`), read fresh at delegation time. */\n\tgetDelegationDepth: () => number;\n\t/** Delegation is refused once `getDelegationDepth() >= maxDelegationDepth` -- assigned by the runtime, not by the tool, so the bound applies to any future delegation entry point. */\n\tmaxDelegationDepth: number;\n\t/** Parent session directory. When supplied, child artifacts are rooted beneath it. */\n\tgetParentSessionDir?: () => string;\n\tbuildChildSession: (request: BuildChildSessionRequest) => Promise<ChildSessionHandle>;\n}\n\nexport interface DelegationResult {\n\tagentType: string;\n\ttask: string;\n\toutput: string;\n\t/** Present for background work; pass this handle to retrieveDelegationResult. */\n\thandleId?: string;\n}\n\ninterface BackgroundDelegation {\n\tagentType: string;\n\tpromise: Promise<DelegationResult>;\n}\n\n// Results deliberately stay available for the lifetime of their parent runtime.\n// Phase 5 promises in-process retrieval only; restart durability belongs to Phase 6.\nconst backgroundByRuntime = new WeakMap<object, Map<string, BackgroundDelegation>>();\n\nfunction background(options: DelegationRuntimeOptions): Map<string, BackgroundDelegation> {\n\tlet value = backgroundByRuntime.get(options);\n\tif (!value) {\n\t\tvalue = new Map();\n\t\tbackgroundByRuntime.set(options, value);\n\t}\n\treturn value;\n}\n\n/**\n * Run one delegation to completion. Throws rather than returning a failure value,\n * matching this codebase's convention for model-facing routine failures (e.g.\n * `tool_schema`'s unknown-tool case, `web_search`'s unconfigured-backend case): a\n * thrown `Error` here is caught by the agent loop and surfaces as a normal,\n * model-readable `isError` tool result, not a fatal turn failure.\n *\n * The capability check is all-or-nothing by design (ceiling.ts): a definition\n * requesting a tool the parent's own capabilities cannot cover refuses the whole\n * delegation, naming the capability, rather than silently building a child missing\n * that one tool. `buildChildSession` is therefore never called for a refused\n * delegation -- there is no code path that produces a child holding a capability\n * outside the ceiling.\n *\n * The depth check (task 5.3) runs right after agent-type resolution and before the\n * capability check, so recursion is bounded regardless of what is being delegated --\n * a refusal at the bound still names the agent type, but never reaches the ceiling\n * or `buildChildSession`. `buildChildSession` receives the child's depth (parent + 1)\n * so the caller can record it on the child's own session header.\n */\nexport async function runDelegation(\n\toptions: DelegationRuntimeOptions,\n\tagentType: string,\n\ttask: string,\n\trequest: { background?: boolean } = {},\n): Promise<DelegationResult> {\n\tconst definition = options.resolveAgent(agentType);\n\tif (!definition) {\n\t\tthrow new Error(`Unknown agent type \"${agentType}\".`);\n\t}\n\n\tconst depth = options.getDelegationDepth();\n\tif (depth >= options.maxDelegationDepth) {\n\t\tthrow new Error(\n\t\t\t`Delegation depth limit (${options.maxDelegationDepth}) reached at depth ${depth}; cannot delegate to agent \"${agentType}\" further.`,\n\t\t);\n\t}\n\n\tconst requestedCapabilities = new Set<Capability>();\n\tfor (const toolName of definition.tools) {\n\t\tconst capabilities = options.getToolCapabilities(toolName);\n\t\tif (!capabilities) {\n\t\t\tthrow new Error(`Agent \"${agentType}\" requests unknown tool \"${toolName}\".`);\n\t\t}\n\t\tfor (const capability of capabilities) requestedCapabilities.add(capability);\n\t}\n\n\tconst ceiling = computeCapabilityCeiling(options.getParentCapabilities(), requestedCapabilities);\n\tif (!ceiling.allowed) {\n\t\tthrow new Error(\n\t\t\t`Delegating to agent \"${agentType}\" requires capability \"${ceiling.deniedCapability}\", which exceeds the parent's authority.`,\n\t\t);\n\t}\n\n\tconst sessionId = randomUUID();\n\tlet artifactDir: string | undefined;\n\tconst parentSessionDir = options.getParentSessionDir?.();\n\tif (parentSessionDir) {\n\t\tconst parentRoot = resolve(parentSessionDir);\n\t\tartifactDir = join(parentRoot, \"delegations\", sessionId);\n\t\tmkdirSync(artifactDir, { recursive: true });\n\t}\n\tconst child = await options.buildChildSession({\n\t\tagentType,\n\t\tdefinition,\n\t\ttoolNames: definition.tools,\n\t\tdepth: depth + 1,\n\t\tsessionId,\n\t\tartifactDir,\n\t});\n\tconst execute = async (): Promise<DelegationResult> => {\n\t\ttry {\n\t\t\tconst { output } = await child.run(task);\n\t\t\treturn { agentType, task, output };\n\t\t} finally {\n\t\t\tchild.dispose();\n\t\t}\n\t};\n\tif (!request.background) return execute();\n\tconst handleId = sessionId;\n\tconst promise = execute();\n\t// Retrieval owns propagation of a child failure; mark the background promise\n\t// observed now so a child that fails before retrieval does not become an\n\t// unhandled rejection.\n\tvoid promise.catch(() => {});\n\tbackground(options).set(handleId, { agentType, promise });\n\treturn { agentType, task, output: `Delegation started. Retrieve result with handle \"${handleId}\".`, handleId };\n}\n\n/** Retrieve a background delegation. Running children are awaited; unknown handles fail explicitly. */\nexport async function retrieveDelegationResult(\n\toptions: DelegationRuntimeOptions,\n\thandleId: string,\n\texpectedAgentType?: string,\n): Promise<DelegationResult> {\n\tconst entry = background(options).get(handleId);\n\tif (!entry) throw new Error(`Unknown delegation handle \"${handleId}\".`);\n\tif (expectedAgentType !== undefined && entry.agentType !== expectedAgentType) {\n\t\tthrow new Error(\n\t\t\t`Delegation handle \"${handleId}\" belongs to agent \"${entry.agentType}\", not \"${expectedAgentType}\".`,\n\t\t);\n\t}\n\treturn entry.promise;\n}\n"]}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The delegation runtime (roadmap Phase 5, ADR 0008, task 5.2). Consumes the
|
|
3
|
+
* capability ceiling (ceiling.ts) and a derived permission store
|
|
4
|
+
* (`../permissions/store.ts`'s `DerivedPermissionRuleStore`) to run a real child
|
|
5
|
+
* agent whose authority can never exceed its parent's.
|
|
6
|
+
*
|
|
7
|
+
* Agent definitions and child-session construction are both injected rather than
|
|
8
|
+
* built in here: agent discovery is a test fixture in this task and becomes real
|
|
9
|
+
* markdown/frontmatter discovery in task 5.5 (`core/delegation/agents.ts`, not yet
|
|
10
|
+
* written); child-session construction is injected so this module stays free of
|
|
11
|
+
* `AgentSession`/`createAgentSession` and therefore free of the import cycle that
|
|
12
|
+
* would create (`sdk.ts` already imports `agent-session.ts`).
|
|
13
|
+
*/
|
|
14
|
+
import { randomUUID } from "node:crypto";
|
|
15
|
+
import { mkdirSync } from "node:fs";
|
|
16
|
+
import { join, resolve } from "node:path";
|
|
17
|
+
import { computeCapabilityCeiling } from "./ceiling.js";
|
|
18
|
+
// Results deliberately stay available for the lifetime of their parent runtime.
|
|
19
|
+
// Phase 5 promises in-process retrieval only; restart durability belongs to Phase 6.
|
|
20
|
+
const backgroundByRuntime = new WeakMap();
|
|
21
|
+
function background(options) {
|
|
22
|
+
let value = backgroundByRuntime.get(options);
|
|
23
|
+
if (!value) {
|
|
24
|
+
value = new Map();
|
|
25
|
+
backgroundByRuntime.set(options, value);
|
|
26
|
+
}
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Run one delegation to completion. Throws rather than returning a failure value,
|
|
31
|
+
* matching this codebase's convention for model-facing routine failures (e.g.
|
|
32
|
+
* `tool_schema`'s unknown-tool case, `web_search`'s unconfigured-backend case): a
|
|
33
|
+
* thrown `Error` here is caught by the agent loop and surfaces as a normal,
|
|
34
|
+
* model-readable `isError` tool result, not a fatal turn failure.
|
|
35
|
+
*
|
|
36
|
+
* The capability check is all-or-nothing by design (ceiling.ts): a definition
|
|
37
|
+
* requesting a tool the parent's own capabilities cannot cover refuses the whole
|
|
38
|
+
* delegation, naming the capability, rather than silently building a child missing
|
|
39
|
+
* that one tool. `buildChildSession` is therefore never called for a refused
|
|
40
|
+
* delegation -- there is no code path that produces a child holding a capability
|
|
41
|
+
* outside the ceiling.
|
|
42
|
+
*
|
|
43
|
+
* The depth check (task 5.3) runs right after agent-type resolution and before the
|
|
44
|
+
* capability check, so recursion is bounded regardless of what is being delegated --
|
|
45
|
+
* a refusal at the bound still names the agent type, but never reaches the ceiling
|
|
46
|
+
* or `buildChildSession`. `buildChildSession` receives the child's depth (parent + 1)
|
|
47
|
+
* so the caller can record it on the child's own session header.
|
|
48
|
+
*/
|
|
49
|
+
export async function runDelegation(options, agentType, task, request = {}) {
|
|
50
|
+
const definition = options.resolveAgent(agentType);
|
|
51
|
+
if (!definition) {
|
|
52
|
+
throw new Error(`Unknown agent type "${agentType}".`);
|
|
53
|
+
}
|
|
54
|
+
const depth = options.getDelegationDepth();
|
|
55
|
+
if (depth >= options.maxDelegationDepth) {
|
|
56
|
+
throw new Error(`Delegation depth limit (${options.maxDelegationDepth}) reached at depth ${depth}; cannot delegate to agent "${agentType}" further.`);
|
|
57
|
+
}
|
|
58
|
+
const requestedCapabilities = new Set();
|
|
59
|
+
for (const toolName of definition.tools) {
|
|
60
|
+
const capabilities = options.getToolCapabilities(toolName);
|
|
61
|
+
if (!capabilities) {
|
|
62
|
+
throw new Error(`Agent "${agentType}" requests unknown tool "${toolName}".`);
|
|
63
|
+
}
|
|
64
|
+
for (const capability of capabilities)
|
|
65
|
+
requestedCapabilities.add(capability);
|
|
66
|
+
}
|
|
67
|
+
const ceiling = computeCapabilityCeiling(options.getParentCapabilities(), requestedCapabilities);
|
|
68
|
+
if (!ceiling.allowed) {
|
|
69
|
+
throw new Error(`Delegating to agent "${agentType}" requires capability "${ceiling.deniedCapability}", which exceeds the parent's authority.`);
|
|
70
|
+
}
|
|
71
|
+
const sessionId = randomUUID();
|
|
72
|
+
let artifactDir;
|
|
73
|
+
const parentSessionDir = options.getParentSessionDir?.();
|
|
74
|
+
if (parentSessionDir) {
|
|
75
|
+
const parentRoot = resolve(parentSessionDir);
|
|
76
|
+
artifactDir = join(parentRoot, "delegations", sessionId);
|
|
77
|
+
mkdirSync(artifactDir, { recursive: true });
|
|
78
|
+
}
|
|
79
|
+
const child = await options.buildChildSession({
|
|
80
|
+
agentType,
|
|
81
|
+
definition,
|
|
82
|
+
toolNames: definition.tools,
|
|
83
|
+
depth: depth + 1,
|
|
84
|
+
sessionId,
|
|
85
|
+
artifactDir,
|
|
86
|
+
});
|
|
87
|
+
const execute = async () => {
|
|
88
|
+
try {
|
|
89
|
+
const { output } = await child.run(task);
|
|
90
|
+
return { agentType, task, output };
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
child.dispose();
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
if (!request.background)
|
|
97
|
+
return execute();
|
|
98
|
+
const handleId = sessionId;
|
|
99
|
+
const promise = execute();
|
|
100
|
+
// Retrieval owns propagation of a child failure; mark the background promise
|
|
101
|
+
// observed now so a child that fails before retrieval does not become an
|
|
102
|
+
// unhandled rejection.
|
|
103
|
+
void promise.catch(() => { });
|
|
104
|
+
background(options).set(handleId, { agentType, promise });
|
|
105
|
+
return { agentType, task, output: `Delegation started. Retrieve result with handle "${handleId}".`, handleId };
|
|
106
|
+
}
|
|
107
|
+
/** Retrieve a background delegation. Running children are awaited; unknown handles fail explicitly. */
|
|
108
|
+
export async function retrieveDelegationResult(options, handleId, expectedAgentType) {
|
|
109
|
+
const entry = background(options).get(handleId);
|
|
110
|
+
if (!entry)
|
|
111
|
+
throw new Error(`Unknown delegation handle "${handleId}".`);
|
|
112
|
+
if (expectedAgentType !== undefined && entry.agentType !== expectedAgentType) {
|
|
113
|
+
throw new Error(`Delegation handle "${handleId}" belongs to agent "${entry.agentType}", not "${expectedAgentType}".`);
|
|
114
|
+
}
|
|
115
|
+
return entry.promise;
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/core/delegation/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The delegation runtime (roadmap Phase 5, ADR 0008, task 5.2). Consumes the\n * capability ceiling (ceiling.ts) and a derived permission store\n * (`../permissions/store.ts`'s `DerivedPermissionRuleStore`) to run a real child\n * agent whose authority can never exceed its parent's.\n *\n * Agent definitions and child-session construction are both injected rather than\n * built in here: agent discovery is a test fixture in this task and becomes real\n * markdown/frontmatter discovery in task 5.5 (`core/delegation/agents.ts`, not yet\n * written); child-session construction is injected so this module stays free of\n * `AgentSession`/`createAgentSession` and therefore free of the import cycle that\n * would create (`sdk.ts` already imports `agent-session.ts`).\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { mkdirSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport type { Capability } from \"../tools/contract.ts\";\nimport { computeCapabilityCeiling } from \"./ceiling.ts\";\n\n/** A delegatable agent's static configuration. Markdown + frontmatter in production (task 5.5); a plain object in tests until then. */\nexport interface AgentDefinition {\n\tname: string;\n\tdescription: string;\n\t/** Tool names this agent may use. Never trusted directly -- the runtime derives the actual child tool list from the computed capability set, not from this list verbatim. */\n\ttools: string[];\n\t/** Optional model id. Falls back to the parent's current model when unset or unresolvable. */\n\tmodel?: string;\n\tsystemPrompt: string;\n}\n\n/** Resolve an agent type to its definition, or `undefined` if unknown. A plain function fixture in this task; real markdown/frontmatter discovery (task 5.5) is just a different implementation of the same shape. */\nexport type AgentDefinitionResolver = (agentType: string) => AgentDefinition | undefined;\n\n/** A running or completed child, as far as the runtime needs to know. */\nexport interface ChildSessionHandle {\n\t/** Run the task to completion and return the child's final output text. */\n\trun(task: string): Promise<{ output: string }>;\n\t/** Release the child's resources. Always called, including after a failed run. */\n\tdispose(): void;\n}\n\nexport interface BuildChildSessionRequest {\n\tagentType: string;\n\tdefinition: AgentDefinition;\n\t/** The child's tool allowlist, already ceiling-checked -- exactly `definition.tools`, never narrowed. */\n\ttoolNames: string[];\n\t/** The child's own recursion depth (the parent's depth + 1), for the runtime constructing it to record on the child's session header (task 5.3). */\n\tdepth: number;\n\t/** Stable id used for the child session and its artifact directory. */\n\tsessionId: string;\n\t/** Per-child artifact root, created before the child session is constructed. */\n\tartifactDir?: string;\n}\n\nexport interface DelegationRuntimeOptions {\n\tresolveAgent: AgentDefinitionResolver;\n\t/** The parent's own effective capability set (its authority), unexpanded -- `exec` expansion happens inside the ceiling check, not here. */\n\tgetParentCapabilities: () => ReadonlySet<Capability>;\n\t/** Capability set a named tool requires, from the live registry. `undefined` for an unknown tool name. */\n\tgetToolCapabilities: (toolName: string) => ReadonlySet<Capability> | undefined;\n\t/** The parent session's own recursion depth (`SessionManager.getDelegationDepth()`), read fresh at delegation time. */\n\tgetDelegationDepth: () => number;\n\t/** Delegation is refused once `getDelegationDepth() >= maxDelegationDepth` -- assigned by the runtime, not by the tool, so the bound applies to any future delegation entry point. */\n\tmaxDelegationDepth: number;\n\t/** Parent session directory. When supplied, child artifacts are rooted beneath it. */\n\tgetParentSessionDir?: () => string;\n\tbuildChildSession: (request: BuildChildSessionRequest) => Promise<ChildSessionHandle>;\n}\n\nexport interface DelegationResult {\n\tagentType: string;\n\ttask: string;\n\toutput: string;\n\t/** Present for background work; pass this handle to retrieveDelegationResult. */\n\thandleId?: string;\n}\n\ninterface BackgroundDelegation {\n\tagentType: string;\n\tpromise: Promise<DelegationResult>;\n}\n\n// Results deliberately stay available for the lifetime of their parent runtime.\n// Phase 5 promises in-process retrieval only; restart durability belongs to Phase 6.\nconst backgroundByRuntime = new WeakMap<object, Map<string, BackgroundDelegation>>();\n\nfunction background(options: DelegationRuntimeOptions): Map<string, BackgroundDelegation> {\n\tlet value = backgroundByRuntime.get(options);\n\tif (!value) {\n\t\tvalue = new Map();\n\t\tbackgroundByRuntime.set(options, value);\n\t}\n\treturn value;\n}\n\n/**\n * Run one delegation to completion. Throws rather than returning a failure value,\n * matching this codebase's convention for model-facing routine failures (e.g.\n * `tool_schema`'s unknown-tool case, `web_search`'s unconfigured-backend case): a\n * thrown `Error` here is caught by the agent loop and surfaces as a normal,\n * model-readable `isError` tool result, not a fatal turn failure.\n *\n * The capability check is all-or-nothing by design (ceiling.ts): a definition\n * requesting a tool the parent's own capabilities cannot cover refuses the whole\n * delegation, naming the capability, rather than silently building a child missing\n * that one tool. `buildChildSession` is therefore never called for a refused\n * delegation -- there is no code path that produces a child holding a capability\n * outside the ceiling.\n *\n * The depth check (task 5.3) runs right after agent-type resolution and before the\n * capability check, so recursion is bounded regardless of what is being delegated --\n * a refusal at the bound still names the agent type, but never reaches the ceiling\n * or `buildChildSession`. `buildChildSession` receives the child's depth (parent + 1)\n * so the caller can record it on the child's own session header.\n */\nexport async function runDelegation(\n\toptions: DelegationRuntimeOptions,\n\tagentType: string,\n\ttask: string,\n\trequest: { background?: boolean } = {},\n): Promise<DelegationResult> {\n\tconst definition = options.resolveAgent(agentType);\n\tif (!definition) {\n\t\tthrow new Error(`Unknown agent type \"${agentType}\".`);\n\t}\n\n\tconst depth = options.getDelegationDepth();\n\tif (depth >= options.maxDelegationDepth) {\n\t\tthrow new Error(\n\t\t\t`Delegation depth limit (${options.maxDelegationDepth}) reached at depth ${depth}; cannot delegate to agent \"${agentType}\" further.`,\n\t\t);\n\t}\n\n\tconst requestedCapabilities = new Set<Capability>();\n\tfor (const toolName of definition.tools) {\n\t\tconst capabilities = options.getToolCapabilities(toolName);\n\t\tif (!capabilities) {\n\t\t\tthrow new Error(`Agent \"${agentType}\" requests unknown tool \"${toolName}\".`);\n\t\t}\n\t\tfor (const capability of capabilities) requestedCapabilities.add(capability);\n\t}\n\n\tconst ceiling = computeCapabilityCeiling(options.getParentCapabilities(), requestedCapabilities);\n\tif (!ceiling.allowed) {\n\t\tthrow new Error(\n\t\t\t`Delegating to agent \"${agentType}\" requires capability \"${ceiling.deniedCapability}\", which exceeds the parent's authority.`,\n\t\t);\n\t}\n\n\tconst sessionId = randomUUID();\n\tlet artifactDir: string | undefined;\n\tconst parentSessionDir = options.getParentSessionDir?.();\n\tif (parentSessionDir) {\n\t\tconst parentRoot = resolve(parentSessionDir);\n\t\tartifactDir = join(parentRoot, \"delegations\", sessionId);\n\t\tmkdirSync(artifactDir, { recursive: true });\n\t}\n\tconst child = await options.buildChildSession({\n\t\tagentType,\n\t\tdefinition,\n\t\ttoolNames: definition.tools,\n\t\tdepth: depth + 1,\n\t\tsessionId,\n\t\tartifactDir,\n\t});\n\tconst execute = async (): Promise<DelegationResult> => {\n\t\ttry {\n\t\t\tconst { output } = await child.run(task);\n\t\t\treturn { agentType, task, output };\n\t\t} finally {\n\t\t\tchild.dispose();\n\t\t}\n\t};\n\tif (!request.background) return execute();\n\tconst handleId = sessionId;\n\tconst promise = execute();\n\t// Retrieval owns propagation of a child failure; mark the background promise\n\t// observed now so a child that fails before retrieval does not become an\n\t// unhandled rejection.\n\tvoid promise.catch(() => {});\n\tbackground(options).set(handleId, { agentType, promise });\n\treturn { agentType, task, output: `Delegation started. Retrieve result with handle \"${handleId}\".`, handleId };\n}\n\n/** Retrieve a background delegation. Running children are awaited; unknown handles fail explicitly. */\nexport async function retrieveDelegationResult(\n\toptions: DelegationRuntimeOptions,\n\thandleId: string,\n\texpectedAgentType?: string,\n): Promise<DelegationResult> {\n\tconst entry = background(options).get(handleId);\n\tif (!entry) throw new Error(`Unknown delegation handle \"${handleId}\".`);\n\tif (expectedAgentType !== undefined && entry.agentType !== expectedAgentType) {\n\t\tthrow new Error(\n\t\t\t`Delegation handle \"${handleId}\" belongs to agent \"${entry.agentType}\", not \"${expectedAgentType}\".`,\n\t\t);\n\t}\n\treturn entry.promise;\n}\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type GitProvenance } from "./provenance.ts";
|
|
2
|
+
import { type CommandJournalRecord, type RecoveryDiagnostic, type SessionLeaseMode, type SessionLeaseRecord } from "./sqlite.ts";
|
|
3
|
+
export interface DurableStateDaemonOptions {
|
|
4
|
+
databasePath: string;
|
|
5
|
+
daemonId: string;
|
|
6
|
+
cwd?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface AttachClientInput {
|
|
9
|
+
sessionId: string;
|
|
10
|
+
clientId: string;
|
|
11
|
+
mode: SessionLeaseMode;
|
|
12
|
+
ttlMs: number;
|
|
13
|
+
}
|
|
14
|
+
export interface MutationInput {
|
|
15
|
+
id?: string;
|
|
16
|
+
sessionId: string;
|
|
17
|
+
clientId: string;
|
|
18
|
+
command: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Local daemon state coordinator. All session mutations are admitted through a
|
|
22
|
+
* durable lease and journaled before the caller receives its operation callback.
|
|
23
|
+
*/
|
|
24
|
+
export declare class DurableStateDaemon {
|
|
25
|
+
#private;
|
|
26
|
+
readonly recoveryDiagnostics: readonly RecoveryDiagnostic[];
|
|
27
|
+
readonly provenance: GitProvenance;
|
|
28
|
+
constructor(options: DurableStateDaemonOptions);
|
|
29
|
+
attach(input: AttachClientInput): SessionLeaseRecord;
|
|
30
|
+
detach(sessionId: string, clientId: string): void;
|
|
31
|
+
beginMutation(input: MutationInput): CommandJournalRecord;
|
|
32
|
+
runMutation<T>(input: MutationInput, operation: () => Promise<T>): Promise<T>;
|
|
33
|
+
getCommand(id: string): CommandJournalRecord | undefined;
|
|
34
|
+
getCommandCount(): number;
|
|
35
|
+
dispose(): void;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=daemon.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../../src/core/durable-state/daemon.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAqB,MAAM,iBAAiB,CAAC;AACxE,OAAO,EACN,KAAK,oBAAoB,EAGzB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,yBAAyB;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AACD,MAAM,WAAW,iBAAiB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;CACd;AACD,MAAM,WAAW,aAAa;IAC7B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,qBAAa,kBAAkB;;IAC9B,QAAQ,CAAC,mBAAmB,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAC5D,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC;IAInC,YAAY,OAAO,EAAE,yBAAyB,EAM7C;IAED,MAAM,CAAC,KAAK,EAAE,iBAAiB,GAAG,kBAAkB,CAWnD;IAED,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAMhD;IAED,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,oBAAoB,CAGxD;IAEK,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAGlF;IAED,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,oBAAoB,GAAG,SAAS,CAEvD;IACD,eAAe,IAAI,MAAM,CAExB;IACD,OAAO,IAAI,IAAI,CAEd;CAQD","sourcesContent":["import { type GitProvenance, readGitProvenance } from \"./provenance.ts\";\nimport {\n\ttype CommandJournalRecord,\n\ttype DurableStateStore,\n\topenDurableStateStore,\n\ttype RecoveryDiagnostic,\n\ttype SessionLeaseMode,\n\ttype SessionLeaseRecord,\n} from \"./sqlite.ts\";\n\nexport interface DurableStateDaemonOptions {\n\tdatabasePath: string;\n\tdaemonId: string;\n\tcwd?: string;\n}\nexport interface AttachClientInput {\n\tsessionId: string;\n\tclientId: string;\n\tmode: SessionLeaseMode;\n\tttlMs: number;\n}\nexport interface MutationInput {\n\tid?: string;\n\tsessionId: string;\n\tclientId: string;\n\tcommand: string;\n}\n\n/**\n * Local daemon state coordinator. All session mutations are admitted through a\n * durable lease and journaled before the caller receives its operation callback.\n */\nexport class DurableStateDaemon {\n\treadonly recoveryDiagnostics: readonly RecoveryDiagnostic[];\n\treadonly provenance: GitProvenance;\n\treadonly #store: DurableStateStore;\n\treadonly #attachments = new Map<string, Map<string, SessionLeaseRecord>>();\n\n\tconstructor(options: DurableStateDaemonOptions) {\n\t\tthis.#store = openDurableStateStore(options.databasePath);\n\t\tthis.provenance = readGitProvenance(options.cwd ?? process.cwd());\n\t\tthis.recoveryDiagnostics = this.#store.recoverUnfinishedCommandsWithDiagnostics(\n\t\t\t`daemon ${options.daemonId} started before command completion`,\n\t\t);\n\t}\n\n\tattach(input: AttachClientInput): SessionLeaseRecord {\n\t\tconst lease = this.#store.acquireLease({\n\t\t\tsessionId: input.sessionId,\n\t\t\townerId: input.clientId,\n\t\t\tmode: input.mode,\n\t\t\tttlMs: input.ttlMs,\n\t\t});\n\t\tconst clients = this.#attachments.get(input.sessionId) ?? new Map<string, SessionLeaseRecord>();\n\t\tclients.set(input.clientId, lease);\n\t\tthis.#attachments.set(input.sessionId, clients);\n\t\treturn lease;\n\t}\n\n\tdetach(sessionId: string, clientId: string): void {\n\t\tthis.#store.releaseLease(sessionId, clientId);\n\t\tconst clients = this.#attachments.get(sessionId);\n\t\tif (!clients) return;\n\t\tclients.delete(clientId);\n\t\tif (clients.size === 0) this.#attachments.delete(sessionId);\n\t}\n\n\tbeginMutation(input: MutationInput): CommandJournalRecord {\n\t\tthis.#requireExclusiveAttachment(input.sessionId, input.clientId);\n\t\treturn this.#store.beginCommand({ id: input.id, sessionId: input.sessionId, command: input.command });\n\t}\n\n\tasync runMutation<T>(input: MutationInput, operation: () => Promise<T>): Promise<T> {\n\t\tthis.#requireExclusiveAttachment(input.sessionId, input.clientId);\n\t\treturn this.#store.runCommand({ id: input.id, sessionId: input.sessionId, command: input.command }, operation);\n\t}\n\n\tgetCommand(id: string): CommandJournalRecord | undefined {\n\t\treturn this.#store.getCommand(id);\n\t}\n\tgetCommandCount(): number {\n\t\treturn this.#store.commandCount();\n\t}\n\tdispose(): void {\n\t\tthis.#store.close();\n\t}\n\n\t#requireExclusiveAttachment(sessionId: string, clientId: string): void {\n\t\tconst attachment = this.#attachments.get(sessionId)?.get(clientId);\n\t\tif (!attachment || attachment.mode !== \"exclusive\" || Date.parse(attachment.expiresAt) <= Date.now()) {\n\t\t\tthrow new Error(`Client does not hold an active exclusive lease for ${sessionId}`);\n\t\t}\n\t}\n}\n"]}
|