@treeseed/sdk 0.11.0 → 0.12.4
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/README.md +9 -4
- package/dist/agent-capacity.d.ts +916 -0
- package/dist/agent-capacity.js +1600 -0
- package/dist/agent-tools.d.ts +37 -0
- package/dist/agent-tools.js +301 -0
- package/dist/api/app.d.ts +1 -1
- package/dist/api/auth/d1-database.d.ts +2 -2
- package/dist/api/auth/d1-provider.d.ts +4 -4
- package/dist/api/auth/d1-store.d.ts +2 -2
- package/dist/api/auth/memory-provider.d.ts +1 -1
- package/dist/api/auth/tokens.d.ts +1 -1
- package/dist/api/config.d.ts +3 -1
- package/dist/api/config.js +34 -10
- package/dist/api/http.d.ts +2 -2
- package/dist/api/index.d.ts +10 -10
- package/dist/api/index.js +4 -2
- package/dist/api/operations-routes.d.ts +3 -3
- package/dist/api/operations.d.ts +1 -1
- package/dist/api/providers.d.ts +1 -1
- package/dist/api/providers.js +21 -4
- package/dist/api/railway.d.ts +3 -2
- package/dist/api/sdk-dispatch.d.ts +4 -4
- package/dist/api/sdk-routes.d.ts +2 -2
- package/dist/api/templates.d.ts +2 -2
- package/dist/api/types.d.ts +3 -2
- package/dist/capacity-provider.d.ts +117 -60
- package/dist/capacity-provider.js +215 -43
- package/dist/capacity.d.ts +2 -2
- package/dist/cli-tools.d.ts +1 -1
- package/dist/content-operations.d.ts +97 -0
- package/dist/content-operations.js +250 -0
- package/dist/content-store.d.ts +4 -4
- package/dist/control-plane-client.d.ts +13 -18
- package/dist/control-plane-client.js +8 -86
- package/dist/control-plane.d.ts +2 -2
- package/dist/copilot.d.ts +3 -0
- package/dist/copilot.js +16 -1
- package/dist/d1-http.d.ts +1 -1
- package/dist/d1-store.d.ts +32 -57
- package/dist/d1-store.js +115 -318
- package/dist/db/d1.d.ts +1 -232
- package/dist/db/index.d.ts +3 -3
- package/dist/db/market-schema.d.ts +49037 -11843
- package/dist/db/market-schema.js +1788 -50
- package/dist/db/node-sqlite.d.ts +1 -232
- package/dist/db/schema.d.ts +0 -462
- package/dist/db/schema.js +2 -22
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +0 -11
- package/dist/fixture-support.js +52 -4
- package/dist/governance.d.ts +133 -0
- package/dist/governance.js +285 -0
- package/dist/graph/build.d.ts +2 -2
- package/dist/graph/context-query-contracts.d.ts +1 -1
- package/dist/graph/dsl.d.ts +1 -1
- package/dist/graph/query.d.ts +4 -4
- package/dist/graph/ranking.d.ts +1 -1
- package/dist/graph/schema.d.ts +1 -1
- package/dist/graph.d.ts +11 -11
- package/dist/guarantees/index.d.ts +425 -0
- package/dist/guarantees/index.js +1146 -0
- package/dist/hosting/apps.d.ts +1 -1
- package/dist/hosting/builtins.d.ts +1 -1
- package/dist/hosting/builtins.js +9 -198
- package/dist/hosting/contracts.d.ts +3 -2
- package/dist/hosting/graph.d.ts +24 -21
- package/dist/hosting/graph.js +280 -294
- package/dist/hosting/index.d.ts +4 -4
- package/dist/index.d.ts +88 -74
- package/dist/index.js +81 -10
- package/dist/local-dev/managed-dev.d.ts +85 -0
- package/dist/local-dev/managed-dev.js +368 -0
- package/dist/managed-dependencies.d.ts +9 -0
- package/dist/managed-dependencies.js +10 -8
- package/dist/market-client.d.ts +265 -7
- package/dist/market-client.js +381 -9
- package/dist/model-registry.d.ts +1 -1
- package/dist/model-registry.js +19 -19
- package/dist/operations/agent-tools.d.ts +2 -11
- package/dist/operations/agent-tools.js +2 -14
- package/dist/operations/providers/default.d.ts +1 -1
- package/dist/operations/providers/default.js +20 -37
- package/dist/operations/repository-operations.d.ts +26 -2
- package/dist/operations/repository-operations.js +159 -1
- package/dist/operations/runtime.d.ts +3 -3
- package/dist/operations/services/bootstrap-runner.d.ts +1 -1
- package/dist/operations/services/build-warning-policy.d.ts +34 -0
- package/dist/operations/services/build-warning-policy.js +81 -82
- package/dist/operations/services/commit-message-provider.js +4 -4
- package/dist/operations/services/config-runtime.d.ts +73 -19
- package/dist/operations/services/config-runtime.js +276 -100
- package/dist/operations/services/deploy.d.ts +41 -622
- package/dist/operations/services/deploy.js +181 -213
- package/dist/operations/services/deployment-readiness.d.ts +1 -1
- package/dist/operations/services/deployment-readiness.js +38 -7
- package/dist/operations/services/git-remote-policy.js +12 -4
- package/dist/operations/services/git-runner.d.ts +66 -0
- package/dist/operations/services/git-runner.js +479 -0
- package/dist/operations/services/git-workflow.d.ts +43 -5
- package/dist/operations/services/git-workflow.js +242 -20
- package/dist/operations/services/github-actions-verification.d.ts +3 -1
- package/dist/operations/services/github-actions-verification.js +11 -3
- package/dist/operations/services/github-api.js +21 -5
- package/dist/operations/services/github-automation.d.ts +3 -3
- package/dist/operations/services/github-automation.js +15 -18
- package/dist/operations/services/github-credentials.js +2 -1
- package/dist/operations/services/hosted-service-checks.d.ts +19 -1
- package/dist/operations/services/hosted-service-checks.js +162 -11
- package/dist/operations/services/hosting-audit.d.ts +2 -2
- package/dist/operations/services/hosting-audit.js +7 -7
- package/dist/operations/services/hub-launch.d.ts +1 -1
- package/dist/operations/services/hub-provider-launch.d.ts +5 -5
- package/dist/operations/services/hub-provider-launch.js +19 -14
- package/dist/operations/services/live-hosted-service-checks.d.ts +2 -1
- package/dist/operations/services/live-hosted-service-checks.js +193 -80
- package/dist/operations/services/local-dev.js +0 -1
- package/dist/operations/services/managed-host-security.d.ts +1 -1
- package/dist/operations/services/managed-repositories.d.ts +37 -0
- package/dist/operations/services/managed-repositories.js +177 -0
- package/dist/operations/services/operations-runner-smoke.d.ts +3 -2
- package/dist/operations/services/operations-runner-smoke.js +22 -4
- package/dist/operations/services/package-adapters.d.ts +77 -2
- package/dist/operations/services/package-adapters.js +627 -56
- package/dist/operations/services/package-reference-policy.d.ts +12 -88
- package/dist/operations/services/package-reference-policy.js +81 -213
- package/dist/operations/services/project-host-operations.d.ts +4 -4
- package/dist/operations/services/project-platform.d.ts +19 -193
- package/dist/operations/services/project-platform.js +31 -103
- package/dist/operations/services/project-web-monitor.d.ts +2 -2
- package/dist/operations/services/project-web-monitor.js +97 -2
- package/dist/operations/services/railway-api.d.ts +36 -7
- package/dist/operations/services/railway-api.js +331 -100
- package/dist/operations/services/railway-deploy.d.ts +7 -95
- package/dist/operations/services/railway-deploy.js +334 -742
- package/dist/operations/services/release-history.js +3 -4
- package/dist/operations/services/release-proof-ledger.d.ts +13 -0
- package/dist/operations/services/release-proof-ledger.js +81 -0
- package/dist/operations/services/release-proof-planner.d.ts +48 -0
- package/dist/operations/services/release-proof-planner.js +195 -0
- package/dist/operations/services/release-proof-runner.d.ts +16 -0
- package/dist/operations/services/release-proof-runner.js +169 -0
- package/dist/operations/services/release-proof.d.ts +81 -0
- package/dist/operations/services/release-proof.js +79 -0
- package/dist/operations/services/repository-save-orchestrator.d.ts +6 -7
- package/dist/operations/services/repository-save-orchestrator.js +353 -174
- package/dist/operations/services/runtime-paths.d.ts +1 -3
- package/dist/operations/services/runtime-paths.js +0 -4
- package/dist/operations/services/runtime-tools.d.ts +1 -192
- package/dist/operations/services/runtime-tools.js +4 -444
- package/dist/operations/services/save-deploy-preflight.js +9 -1
- package/dist/operations/services/template-host-bindings.d.ts +2 -2
- package/dist/operations/services/template-host-bindings.js +3 -2
- package/dist/operations/services/template-registry.d.ts +6 -6
- package/dist/operations/services/template-registry.js +33 -14
- package/dist/operations/services/template-secret-sync.d.ts +3 -3
- package/dist/operations/services/workflow-timing.d.ts +25 -0
- package/dist/operations/services/workflow-timing.js +79 -0
- package/dist/operations/services/workspace-dependency-mode.js +41 -4
- package/dist/operations/services/workspace-preflight.js +8 -8
- package/dist/operations/services/workspace-save-script.d.ts +4 -4
- package/dist/operations/services/workspace-save.d.ts +1 -0
- package/dist/operations/services/workspace-save.js +41 -18
- package/dist/operations/services/workspace-tools.js +40 -6
- package/dist/operations-registry.d.ts +1 -1
- package/dist/operations-registry.js +5 -6
- package/dist/operations-types.d.ts +1 -1
- package/dist/operations.d.ts +17 -11
- package/dist/operations.js +57 -3
- package/dist/platform/book-export.js +2 -0
- package/dist/platform/books-data.d.ts +1 -1
- package/dist/platform/config.d.ts +27 -0
- package/dist/platform/config.js +100 -0
- package/dist/platform/content-runtime-source.d.ts +54 -0
- package/dist/platform/content-runtime-source.js +169 -0
- package/dist/platform/contracts.d.ts +6 -1
- package/dist/platform/deploy-config.d.ts +1 -1
- package/dist/platform/deploy-config.js +7 -1
- package/dist/platform/deploy-runtime.d.ts +1 -1
- package/dist/platform/desired-state.d.ts +63 -0
- package/dist/platform/desired-state.js +1021 -0
- package/dist/platform/env.yaml +414 -19
- package/dist/platform/environment.d.ts +2 -2
- package/dist/platform/environment.js +54 -22
- package/dist/platform/local-content-materialization.d.ts +9 -0
- package/dist/platform/local-content-materialization.js +212 -0
- package/dist/platform/plugin.d.ts +5 -5
- package/dist/platform/plugins/runtime.d.ts +3 -3
- package/dist/platform/plugins/runtime.js +62 -1
- package/dist/platform/plugins.d.ts +6 -6
- package/dist/platform/published-content-pipeline.d.ts +3 -3
- package/dist/platform/published-content-pipeline.js +5 -1
- package/dist/platform/published-content.d.ts +4 -3
- package/dist/platform/site-config-schema.d.ts +1 -0
- package/dist/platform/site-config-schema.js +4 -1
- package/dist/platform/tenant/runtime-config.d.ts +152 -2
- package/dist/platform/tenant-config.d.ts +1 -1
- package/dist/platform/utils/site-config-schema.d.ts +154 -0
- package/dist/platform/utils/site-config-schema.js +400 -461
- package/dist/platform-operation-store.d.ts +1 -1
- package/dist/plugin-default.d.ts +1 -1
- package/dist/plugin-default.js +27 -12
- package/dist/project-import.d.ts +91 -0
- package/dist/project-import.js +257 -0
- package/dist/project-workflow.d.ts +1 -1
- package/dist/reconcile/bootstrap-systems.d.ts +2 -2
- package/dist/reconcile/bootstrap-systems.js +3 -3
- package/dist/reconcile/builtin-adapters.d.ts +7 -1
- package/dist/reconcile/builtin-adapters.js +2924 -346
- package/dist/reconcile/contracts.d.ts +15 -2
- package/dist/reconcile/desired-state.d.ts +2 -193
- package/dist/reconcile/desired-state.js +24 -33
- package/dist/reconcile/engine.d.ts +60 -397
- package/dist/reconcile/engine.js +182 -18
- package/dist/reconcile/index.d.ts +10 -10
- package/dist/reconcile/live-acceptance.d.ts +22 -1
- package/dist/reconcile/live-acceptance.js +456 -44
- package/dist/reconcile/providers/docker-private.d.ts +54 -0
- package/dist/reconcile/providers/docker-private.js +101 -0
- package/dist/reconcile/providers/github-private.d.ts +65 -0
- package/dist/reconcile/providers/github-private.js +119 -0
- package/dist/reconcile/providers/local-private.d.ts +28 -0
- package/dist/reconcile/providers/local-private.js +75 -0
- package/dist/reconcile/providers/railway-iac.d.ts +66 -0
- package/dist/reconcile/providers/railway-iac.js +275 -0
- package/dist/reconcile/providers/release-private.d.ts +92 -0
- package/dist/reconcile/providers/release-private.js +152 -0
- package/dist/reconcile/registry.d.ts +2 -2
- package/dist/reconcile/registry.js +16 -1
- package/dist/reconcile/state.d.ts +1 -1
- package/dist/reconcile/state.js +3 -40
- package/dist/reconcile/units.d.ts +1 -1
- package/dist/reconcile/units.js +7 -1
- package/dist/remote.d.ts +6 -6
- package/dist/scenes/artifacts.d.ts +16 -0
- package/dist/scenes/artifacts.js +61 -0
- package/dist/scenes/auth.d.ts +2 -0
- package/dist/scenes/auth.js +28 -0
- package/dist/scenes/base-url.d.ts +12 -0
- package/dist/scenes/base-url.js +123 -0
- package/dist/scenes/builtin-plugins.d.ts +2 -0
- package/dist/scenes/builtin-plugins.js +447 -0
- package/dist/scenes/checkpoints.d.ts +14 -0
- package/dist/scenes/checkpoints.js +33 -0
- package/dist/scenes/device-matrix.d.ts +2 -0
- package/dist/scenes/device-matrix.js +97 -0
- package/dist/scenes/devices.d.ts +11 -0
- package/dist/scenes/devices.js +22 -0
- package/dist/scenes/diagnostics.d.ts +5 -0
- package/dist/scenes/diagnostics.js +22 -0
- package/dist/scenes/diagram-providers.d.ts +2 -0
- package/dist/scenes/diagram-providers.js +181 -0
- package/dist/scenes/diagram-validation.d.ts +22 -0
- package/dist/scenes/diagram-validation.js +74 -0
- package/dist/scenes/environment.d.ts +2 -0
- package/dist/scenes/environment.js +65 -0
- package/dist/scenes/evidence-bundle.d.ts +5 -0
- package/dist/scenes/evidence-bundle.js +42 -0
- package/dist/scenes/evidence-report.d.ts +10 -0
- package/dist/scenes/evidence-report.js +73 -0
- package/dist/scenes/evidence.d.ts +16 -0
- package/dist/scenes/evidence.js +277 -0
- package/dist/scenes/index.d.ts +48 -0
- package/dist/scenes/index.js +312 -0
- package/dist/scenes/inspect.d.ts +9 -0
- package/dist/scenes/inspect.js +64 -0
- package/dist/scenes/loader.d.ts +3 -0
- package/dist/scenes/loader.js +38 -0
- package/dist/scenes/logs.d.ts +2 -0
- package/dist/scenes/logs.js +47 -0
- package/dist/scenes/operations.d.ts +3 -0
- package/dist/scenes/operations.js +128 -0
- package/dist/scenes/phase0.d.ts +9 -0
- package/dist/scenes/phase0.js +637 -0
- package/dist/scenes/planner.d.ts +12 -0
- package/dist/scenes/planner.js +166 -0
- package/dist/scenes/playwright-adapter.d.ts +2 -0
- package/dist/scenes/playwright-adapter.js +63 -0
- package/dist/scenes/plugins.d.ts +5 -0
- package/dist/scenes/plugins.js +115 -0
- package/dist/scenes/progress.d.ts +17 -0
- package/dist/scenes/progress.js +24 -0
- package/dist/scenes/publish-destinations.d.ts +12 -0
- package/dist/scenes/publish-destinations.js +99 -0
- package/dist/scenes/publish-plan-report.d.ts +10 -0
- package/dist/scenes/publish-plan-report.js +70 -0
- package/dist/scenes/publish-plan.d.ts +15 -0
- package/dist/scenes/publish-plan.js +248 -0
- package/dist/scenes/publish-redaction.d.ts +16 -0
- package/dist/scenes/publish-redaction.js +148 -0
- package/dist/scenes/publish-release-record.d.ts +13 -0
- package/dist/scenes/publish-release-record.js +34 -0
- package/dist/scenes/publish-report.d.ts +10 -0
- package/dist/scenes/publish-report.js +67 -0
- package/dist/scenes/publish.d.ts +16 -0
- package/dist/scenes/publish.js +298 -0
- package/dist/scenes/registry.d.ts +12 -0
- package/dist/scenes/registry.js +65 -0
- package/dist/scenes/remotion-adapter.d.ts +3 -0
- package/dist/scenes/remotion-adapter.js +94 -0
- package/dist/scenes/remotion-composition-registry.d.ts +2 -0
- package/dist/scenes/remotion-composition-registry.js +12 -0
- package/dist/scenes/remotion-compositions.d.ts +15 -0
- package/dist/scenes/remotion-compositions.js +464 -0
- package/dist/scenes/remotion-diagrams.d.ts +9 -0
- package/dist/scenes/remotion-diagrams.js +188 -0
- package/dist/scenes/remotion-input.d.ts +13 -0
- package/dist/scenes/remotion-input.js +291 -0
- package/dist/scenes/remotion-visuals.d.ts +29 -0
- package/dist/scenes/remotion-visuals.js +198 -0
- package/dist/scenes/render-media-assets.d.ts +9 -0
- package/dist/scenes/render-media-assets.js +117 -0
- package/dist/scenes/render-report.d.ts +10 -0
- package/dist/scenes/render-report.js +27 -0
- package/dist/scenes/render.d.ts +2 -0
- package/dist/scenes/render.js +218 -0
- package/dist/scenes/reporter.d.ts +3 -0
- package/dist/scenes/reporter.js +101 -0
- package/dist/scenes/resume.d.ts +2 -0
- package/dist/scenes/resume.js +79 -0
- package/dist/scenes/runner.d.ts +2 -0
- package/dist/scenes/runner.js +674 -0
- package/dist/scenes/schema.d.ts +6 -0
- package/dist/scenes/schema.js +994 -0
- package/dist/scenes/seed.d.ts +2 -0
- package/dist/scenes/seed.js +83 -0
- package/dist/scenes/segments.d.ts +15 -0
- package/dist/scenes/segments.js +79 -0
- package/dist/scenes/selectors.d.ts +3 -0
- package/dist/scenes/selectors.js +21 -0
- package/dist/scenes/timeline.d.ts +11 -0
- package/dist/scenes/timeline.js +26 -0
- package/dist/scenes/timeouts.d.ts +6 -0
- package/dist/scenes/timeouts.js +17 -0
- package/dist/scenes/training-captions.d.ts +14 -0
- package/dist/scenes/training-captions.js +98 -0
- package/dist/scenes/training-report.d.ts +10 -0
- package/dist/scenes/training-report.js +78 -0
- package/dist/scenes/training-transcript.d.ts +24 -0
- package/dist/scenes/training-transcript.js +204 -0
- package/dist/scenes/training.d.ts +10 -0
- package/dist/scenes/training.js +134 -0
- package/dist/scenes/types.d.ts +1872 -0
- package/dist/scenes/types.js +8 -0
- package/dist/scenes/visual-audit-contact-sheets.d.ts +6 -0
- package/dist/scenes/visual-audit-contact-sheets.js +119 -0
- package/dist/scenes/visual-audit-fixtures.d.ts +25 -0
- package/dist/scenes/visual-audit-fixtures.js +210 -0
- package/dist/scenes/visual-audit-report.d.ts +11 -0
- package/dist/scenes/visual-audit-report.js +85 -0
- package/dist/scenes/visual-audit-review.d.ts +23 -0
- package/dist/scenes/visual-audit-review.js +829 -0
- package/dist/scenes/visual-audit-routes.d.ts +13 -0
- package/dist/scenes/visual-audit-routes.js +308 -0
- package/dist/scenes/visual-audit.d.ts +3 -0
- package/dist/scenes/visual-audit.js +596 -0
- package/dist/scripts/build-dist.js +154 -40
- package/dist/scripts/check-build-warnings.js +42 -53
- package/dist/scripts/cleanup-markdown.js +1 -1
- package/dist/scripts/cli-test-fixtures.js +24 -28
- package/dist/scripts/migrate-journey-csv-to-guarantees.js +140 -0
- package/dist/scripts/package-tools.js +12 -1
- package/dist/scripts/prepare.js +3 -7
- package/dist/scripts/release-verify.js +2 -5
- package/dist/scripts/run-fixture-astro-command.js +1 -1
- package/dist/scripts/template-catalog.test.js +68 -82
- package/dist/scripts/tenant-dev.js +0 -10
- package/dist/scripts/test-cloudflare-local.js +1 -1
- package/dist/scripts/test-scaffold.js +0 -1
- package/dist/scripts/test-smoke.js +3 -3
- package/dist/scripts/treeseed-build-dist.js +4 -7
- package/dist/scripts/treeseed-release-verify.js +1 -1
- package/dist/scripts/verify-driver.js +30 -21
- package/dist/scripts/workflow-commands.test.js +18 -27
- package/dist/scripts/workspace-command-e2e.js +1 -1
- package/dist/scripts/workspace-lint.js +6 -0
- package/dist/scripts/workspace-release-test.js +90 -1
- package/dist/scripts/workspace-test.js +6 -0
- package/dist/sdk-dispatch.d.ts +1 -1
- package/dist/sdk-dispatch.js +0 -16
- package/dist/sdk-fields.d.ts +1 -1
- package/dist/sdk-filters.d.ts +1 -1
- package/dist/sdk-types.d.ts +1184 -113
- package/dist/sdk-types.js +393 -0
- package/dist/sdk.d.ts +74 -79
- package/dist/sdk.js +27 -36
- package/dist/secrets-capability.d.ts +421 -0
- package/dist/secrets-capability.js +530 -0
- package/dist/seeds/normalize.js +1 -0
- package/dist/seeds/planner.js +38 -0
- package/dist/seeds/schema.js +293 -4
- package/dist/seeds/types.d.ts +60 -0
- package/dist/seeds/types.js +9 -1
- package/dist/service-credentials.d.ts +54 -0
- package/dist/service-credentials.js +112 -0
- package/dist/stores/cursor-store.d.ts +2 -2
- package/dist/stores/envelopes.d.ts +1 -1
- package/dist/stores/helpers.d.ts +1 -1
- package/dist/stores/lease-store.d.ts +2 -2
- package/dist/stores/message-store.d.ts +2 -2
- package/dist/stores/operational-store.d.ts +3 -26
- package/dist/stores/operational-store.js +0 -225
- package/dist/stores/project-workflow-store.d.ts +2 -2
- package/dist/stores/run-store.d.ts +2 -3
- package/dist/stores/run-store.js +0 -98
- package/dist/stores/subscription-store.d.ts +2 -3
- package/dist/stores/subscription-store.js +0 -100
- package/dist/template-catalog.d.ts +1 -1
- package/dist/template-launch-requirements.d.ts +2 -2
- package/dist/template-launch-ui.d.ts +2 -2
- package/dist/treedx/adapters.d.ts +6 -6
- package/dist/treedx/client.d.ts +1 -1
- package/dist/treedx/errors.d.ts +1 -1
- package/dist/treedx/federated-client.d.ts +3 -3
- package/dist/treedx/graph-adapter.d.ts +2 -2
- package/dist/treedx/index.d.ts +14 -14
- package/dist/treedx/market-integration.d.ts +1 -1
- package/dist/treedx/ports.d.ts +30 -30
- package/dist/treedx/query-adapter.d.ts +3 -3
- package/dist/treedx/registry-client.d.ts +2 -2
- package/dist/treedx/repository-adapter.d.ts +4 -4
- package/dist/treedx/repository-adapter.js +14 -3
- package/dist/treedx/sdk-integration.d.ts +3 -3
- package/dist/treedx/types.d.ts +2 -2
- package/dist/treedx/workspace-adapter.d.ts +2 -2
- package/dist/treedx-backends.d.ts +13 -11
- package/dist/treedx-backends.js +52 -50
- package/dist/treedx-client.d.ts +3 -0
- package/dist/treedx-client.js +10 -1
- package/dist/treeseed/services/compose.yml +2 -3
- package/dist/treeseed/template-catalog/catalog.fixture.json +4 -4
- package/dist/types/agents.d.ts +256 -0
- package/dist/types/agents.js +42 -1
- package/dist/verification.d.ts +1 -0
- package/dist/verification.js +143 -14
- package/dist/workflow/operations.d.ts +510 -559
- package/dist/workflow/operations.js +1891 -1342
- package/dist/workflow/policy.js +19 -7
- package/dist/workflow/runs.d.ts +9 -3
- package/dist/workflow/runs.js +66 -26
- package/dist/workflow/session.d.ts +4 -1
- package/dist/workflow/session.js +50 -10
- package/dist/workflow/worktrees.d.ts +6 -2
- package/dist/workflow/worktrees.js +98 -15
- package/dist/workflow-state.d.ts +5 -5
- package/dist/workflow-state.js +27 -17
- package/dist/workflow-support.d.ts +26 -25
- package/dist/workflow-support.js +27 -31
- package/dist/workflow.d.ts +46 -16
- package/dist/workflow.js +19 -7
- package/drizzle/d1/0000_treeseed_d1.sql +0 -18
- package/drizzle/market/0000_market_control_plane.sql +1793 -1499
- package/drizzle/market/0005_agent_capacity_coordination.sql +145 -0
- package/drizzle/market/0006_provider_assignment_lifecycle.sql +9 -0
- package/drizzle/market/0007_agent_architecture_gap_closure.sql +157 -0
- package/drizzle/market/0008_agent_capacity_operational_closure.sql +27 -0
- package/drizzle/market/0009_agent_capacity_runtime_hardening.sql +29 -0
- package/drizzle/market/0010_secrets_capability_persistence.sql +130 -0
- package/drizzle/market/0011_github_app_adapter.sql +47 -0
- package/drizzle/market/0012_workday_test_runs.sql +44 -0
- package/drizzle/market/0013_runtime_task_lifecycle.sql +39 -0
- package/drizzle/market/0014_governance_pipeline.sql +189 -0
- package/package.json +75 -18
- package/scripts/{verify-driver.mjs → verify-driver.ts} +19 -4
- package/templates/github/deploy-web.workflow.yml +12 -8
- package/dist/operations/services/mailpit-runtime.d.ts +0 -20
- package/dist/operations/services/mailpit-runtime.js +0 -67
- package/dist/operations/services/release-candidate.d.ts +0 -60
- package/dist/operations/services/release-candidate.js +0 -953
- package/dist/scripts/ensure-mailpit.js +0 -48
- package/dist/scripts/logs-mailpit.js +0 -2
- package/dist/scripts/run-ts.js +0 -45
- package/dist/scripts/stop-mailpit.js +0 -5
- package/dist/scripts/treeseed-run-ts.js +0 -45
|
@@ -0,0 +1,1600 @@
|
|
|
1
|
+
const AGENT_ASSIGNMENT_WORKSPACE_ACCESS_MODES = [
|
|
2
|
+
"context_only",
|
|
3
|
+
"workspace_write",
|
|
4
|
+
"brokered_workspace",
|
|
5
|
+
"full_workspace_no_credentials",
|
|
6
|
+
"trusted_direct"
|
|
7
|
+
];
|
|
8
|
+
function record(value) {
|
|
9
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
10
|
+
}
|
|
11
|
+
function stringList(value) {
|
|
12
|
+
return Array.isArray(value) ? value.map((entry) => String(entry ?? "").trim()).filter(Boolean) : [];
|
|
13
|
+
}
|
|
14
|
+
function arrayValue(value) {
|
|
15
|
+
return Array.isArray(value) ? value : [];
|
|
16
|
+
}
|
|
17
|
+
function isRecord(value) {
|
|
18
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
19
|
+
}
|
|
20
|
+
function uniqueStrings(values) {
|
|
21
|
+
return [...new Set(values.filter(Boolean))].sort((left, right) => left.localeCompare(right));
|
|
22
|
+
}
|
|
23
|
+
function numberOrNull(value) {
|
|
24
|
+
const number = Number(value);
|
|
25
|
+
return Number.isFinite(number) ? number : null;
|
|
26
|
+
}
|
|
27
|
+
function booleanDefault(value, fallback) {
|
|
28
|
+
return typeof value === "boolean" ? value : fallback;
|
|
29
|
+
}
|
|
30
|
+
function stringOrNull(value) {
|
|
31
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
32
|
+
return String(value);
|
|
33
|
+
}
|
|
34
|
+
function firstString(...values) {
|
|
35
|
+
for (const value of values) {
|
|
36
|
+
const normalized = stringOrNull(value);
|
|
37
|
+
if (normalized) return normalized;
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
function firstArray(...values) {
|
|
42
|
+
for (const value of values) {
|
|
43
|
+
if (Array.isArray(value)) return value;
|
|
44
|
+
}
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
function booleanOrNull(value) {
|
|
48
|
+
return typeof value === "boolean" ? value : null;
|
|
49
|
+
}
|
|
50
|
+
function pressureAllows(pressure) {
|
|
51
|
+
return pressure !== "exhausted" && pressure !== "throttled";
|
|
52
|
+
}
|
|
53
|
+
function resourceNeedKey(need) {
|
|
54
|
+
return [
|
|
55
|
+
need.kind,
|
|
56
|
+
uniqueStrings(need.operations).join("|"),
|
|
57
|
+
uniqueStrings(need.paths ?? []).join("|"),
|
|
58
|
+
need.required === false ? "optional" : "required"
|
|
59
|
+
].join(":");
|
|
60
|
+
}
|
|
61
|
+
function pushResourceNeed(target, need) {
|
|
62
|
+
const normalized = {
|
|
63
|
+
...need,
|
|
64
|
+
operations: uniqueStrings(need.operations),
|
|
65
|
+
paths: need.paths?.length ? uniqueStrings(need.paths) : void 0,
|
|
66
|
+
required: need.required ?? true,
|
|
67
|
+
metadata: need.metadata
|
|
68
|
+
};
|
|
69
|
+
if (!target.some((entry) => resourceNeedKey(entry) === resourceNeedKey(normalized))) {
|
|
70
|
+
target.push(normalized);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function handleResourceNeed(handle) {
|
|
74
|
+
const operations = uniqueStrings([
|
|
75
|
+
...stringList(handle.operations),
|
|
76
|
+
...stringList(handle.allowedOperations)
|
|
77
|
+
]);
|
|
78
|
+
if (handle.kind === "repository_access") {
|
|
79
|
+
const repository = handle;
|
|
80
|
+
return {
|
|
81
|
+
kind: "repository",
|
|
82
|
+
operations: operations.length ? operations : ["read"],
|
|
83
|
+
paths: stringList(repository.allowedPaths),
|
|
84
|
+
required: true,
|
|
85
|
+
metadata: {
|
|
86
|
+
handleId: handle.id,
|
|
87
|
+
provider: repository.provider ?? null
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (handle.kind === "treedx_workspace") {
|
|
92
|
+
const workspace = handle;
|
|
93
|
+
return {
|
|
94
|
+
kind: "treedx_workspace",
|
|
95
|
+
operations: operations.length ? operations : ["read"],
|
|
96
|
+
paths: stringList(workspace.allowedPaths),
|
|
97
|
+
required: true,
|
|
98
|
+
metadata: {
|
|
99
|
+
handleId: handle.id,
|
|
100
|
+
workspaceId: workspace.workspaceId ?? null
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
if (handle.kind === "workflow_operation") {
|
|
105
|
+
const workflow = handle;
|
|
106
|
+
return {
|
|
107
|
+
kind: "workflow",
|
|
108
|
+
operations: operations.length ? operations : ["dispatch_workflow"],
|
|
109
|
+
required: true,
|
|
110
|
+
metadata: {
|
|
111
|
+
handleId: handle.id,
|
|
112
|
+
operationId: workflow.operationId,
|
|
113
|
+
workflowFile: workflow.workflowFile
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
if (handle.kind === "secret_use") {
|
|
118
|
+
return {
|
|
119
|
+
kind: "secret",
|
|
120
|
+
operations: operations.length ? operations : ["use"],
|
|
121
|
+
required: true,
|
|
122
|
+
metadata: {
|
|
123
|
+
handleId: handle.id,
|
|
124
|
+
custodyMode: handle.custodyMode ?? null
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
function preferredCapabilitiesFromAgent(agent) {
|
|
131
|
+
const preferences = agent?.execution.providerProfile?.preferredLanes ?? [];
|
|
132
|
+
return uniqueStrings(preferences.flatMap((entry) => [
|
|
133
|
+
entry.provider,
|
|
134
|
+
entry.providerId,
|
|
135
|
+
entry.laneId,
|
|
136
|
+
entry.model,
|
|
137
|
+
entry.modelClass
|
|
138
|
+
].filter((value) => typeof value === "string" && value.trim().length > 0)));
|
|
139
|
+
}
|
|
140
|
+
function recordStringArray(source, key) {
|
|
141
|
+
return stringList(record(source)[key]);
|
|
142
|
+
}
|
|
143
|
+
function collectSupplyMetadataCapabilities(...sources) {
|
|
144
|
+
return uniqueStrings(sources.flatMap((source) => [
|
|
145
|
+
...recordStringArray(record(source).metadata, "capabilities"),
|
|
146
|
+
...recordStringArray(source, "capabilities")
|
|
147
|
+
]));
|
|
148
|
+
}
|
|
149
|
+
function collectSupplyMetadataAliases(...sources) {
|
|
150
|
+
return uniqueStrings(sources.flatMap((source) => [
|
|
151
|
+
...recordStringArray(record(source).metadata, "capabilityAliases"),
|
|
152
|
+
...recordStringArray(record(source).metadata, "aliases"),
|
|
153
|
+
...recordStringArray(source, "capabilityAliases"),
|
|
154
|
+
...recordStringArray(source, "aliases")
|
|
155
|
+
]));
|
|
156
|
+
}
|
|
157
|
+
function stableStringify(value) {
|
|
158
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
159
|
+
if (Array.isArray(value)) return `[${value.map((entry) => stableStringify(entry)).join(",")}]`;
|
|
160
|
+
const entries = Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`);
|
|
161
|
+
return `{${entries.join(",")}}`;
|
|
162
|
+
}
|
|
163
|
+
function computeDecisionScopeHash(scope) {
|
|
164
|
+
const text = stableStringify(scope ?? {});
|
|
165
|
+
let hash = 5381;
|
|
166
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
167
|
+
hash = (hash << 5) + hash ^ text.charCodeAt(index);
|
|
168
|
+
}
|
|
169
|
+
return `scope_${(hash >>> 0).toString(16).padStart(8, "0")}`;
|
|
170
|
+
}
|
|
171
|
+
function isDecisionReadyForActing(status) {
|
|
172
|
+
if (!status) return false;
|
|
173
|
+
const readiness = status.executionReadiness;
|
|
174
|
+
const planning = status.planningInputsStatus;
|
|
175
|
+
return (readiness === "ready" || readiness === "waived") && (planning === "complete" || planning === "waived");
|
|
176
|
+
}
|
|
177
|
+
function isPlanningInputOpen(request) {
|
|
178
|
+
return request.status === "requested" || request.status === "stale";
|
|
179
|
+
}
|
|
180
|
+
function isDecisionExecutionInputAccepted(input) {
|
|
181
|
+
return input.status === "accepted";
|
|
182
|
+
}
|
|
183
|
+
function isAgentCapacityPlanAccepted(plan) {
|
|
184
|
+
return Boolean(plan && ["accepted", "scheduled", "active"].includes(String(plan.status)));
|
|
185
|
+
}
|
|
186
|
+
function normalizeDecisionExecutionEstimate(input) {
|
|
187
|
+
const source = "input" in input && input.input && "input" in input.input ? input.input.input : record(input.input);
|
|
188
|
+
const metadata = record(input.metadata ?? input.metadata);
|
|
189
|
+
const estimate = record(source.estimate ?? metadata.estimate);
|
|
190
|
+
const capabilities = Array.isArray(source.requiredCapabilities) ? source.requiredCapabilities : Array.isArray(metadata.requiredCapabilities) ? metadata.requiredCapabilities : [];
|
|
191
|
+
const dependencies = Array.isArray(source.dependencies) ? source.dependencies : Array.isArray(metadata.dependencies) ? metadata.dependencies : [];
|
|
192
|
+
const blockers = Array.isArray(source.blockers) ? source.blockers : Array.isArray(metadata.blockers) ? metadata.blockers : [];
|
|
193
|
+
const assumptions = Array.isArray(source.assumptions) ? source.assumptions : Array.isArray(metadata.assumptions) ? metadata.assumptions : [];
|
|
194
|
+
const environmentNeeds = Array.isArray(source.environmentNeeds) ? source.environmentNeeds : Array.isArray(metadata.environmentNeeds) ? metadata.environmentNeeds : [];
|
|
195
|
+
const expectedCredits = numberOrNull(estimate.expectedCredits ?? estimate.credits ?? source.expectedCredits ?? metadata.expectedCredits) ?? 1;
|
|
196
|
+
const highCredits = Math.max(expectedCredits, numberOrNull(estimate.highCredits ?? estimate.p90Credits ?? source.highCredits ?? metadata.highCredits) ?? expectedCredits);
|
|
197
|
+
const confidence = numberOrNull(estimate.confidence ?? source.confidence ?? metadata.confidence);
|
|
198
|
+
return {
|
|
199
|
+
expectedCredits,
|
|
200
|
+
highCredits,
|
|
201
|
+
requiredCapabilities: capabilities.map(String).filter(Boolean),
|
|
202
|
+
dependencies: dependencies.map(String).filter(Boolean),
|
|
203
|
+
blockers: blockers.map(String).filter(Boolean),
|
|
204
|
+
assumptions: assumptions.map(String).filter(Boolean),
|
|
205
|
+
confidence,
|
|
206
|
+
environmentNeeds: environmentNeeds.map(String).filter(Boolean),
|
|
207
|
+
risk: record(source.risk ?? metadata.risk)
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function buildAgentCapacityPlanDraft(input) {
|
|
211
|
+
const timestamp = input.now ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
212
|
+
const accepted = input.executionInputs.filter(isDecisionExecutionInputAccepted);
|
|
213
|
+
const workUnits = accepted.map((entry, index) => {
|
|
214
|
+
const estimate = normalizeDecisionExecutionEstimate(entry);
|
|
215
|
+
const decisionInput = entry.input;
|
|
216
|
+
const capacityEnvelope = {
|
|
217
|
+
...decisionInput.capacity,
|
|
218
|
+
teamId: input.teamId,
|
|
219
|
+
projectId: input.projectId,
|
|
220
|
+
workDayId: decisionInput.capacity?.workDayId ?? input.workDayId ?? null,
|
|
221
|
+
allocationSetId: decisionInput.capacity?.allocationSetId ?? input.allocationSetId ?? null,
|
|
222
|
+
mode: normalizeAgentExecutionMode(decisionInput.mode, "acting"),
|
|
223
|
+
projectAgentClassId: entry.projectAgentClassId,
|
|
224
|
+
metadata: {
|
|
225
|
+
...decisionInput.capacity?.metadata ?? {},
|
|
226
|
+
capacityPlanId: input.id,
|
|
227
|
+
decisionExecutionInputId: entry.id
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
return {
|
|
231
|
+
id: `${input.id}:wu:${index + 1}`,
|
|
232
|
+
decisionExecutionInputId: entry.id,
|
|
233
|
+
decisionId: input.decisionId,
|
|
234
|
+
projectAgentClassId: entry.projectAgentClassId,
|
|
235
|
+
mode: normalizeAgentExecutionMode(entry.mode, "acting"),
|
|
236
|
+
taskId: decisionInput.taskId ?? null,
|
|
237
|
+
agentId: decisionInput.agentId ?? null,
|
|
238
|
+
handlerId: decisionInput.handlerId ?? null,
|
|
239
|
+
workDayId: capacityEnvelope.workDayId ?? null,
|
|
240
|
+
expectedCredits: estimate.expectedCredits,
|
|
241
|
+
highCredits: estimate.highCredits,
|
|
242
|
+
requiredCapabilities: estimate.requiredCapabilities,
|
|
243
|
+
dependencies: estimate.dependencies,
|
|
244
|
+
blockers: estimate.blockers,
|
|
245
|
+
risk: estimate.risk,
|
|
246
|
+
assumptions: estimate.assumptions,
|
|
247
|
+
confidence: estimate.confidence,
|
|
248
|
+
capacityEnvelope,
|
|
249
|
+
decisionInput: {
|
|
250
|
+
...decisionInput,
|
|
251
|
+
capacity: capacityEnvelope,
|
|
252
|
+
metadata: {
|
|
253
|
+
...decisionInput.metadata ?? {},
|
|
254
|
+
capacityPlanId: input.id,
|
|
255
|
+
decisionExecutionInputId: entry.id
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
metadata: entry.metadata ?? {}
|
|
259
|
+
};
|
|
260
|
+
});
|
|
261
|
+
const unique = (values) => [...new Set(values.filter(Boolean))].sort((left, right) => left.localeCompare(right));
|
|
262
|
+
return {
|
|
263
|
+
id: input.id,
|
|
264
|
+
teamId: input.teamId,
|
|
265
|
+
projectId: input.projectId,
|
|
266
|
+
decisionId: input.decisionId,
|
|
267
|
+
status: input.status ?? "draft",
|
|
268
|
+
scopeHash: input.scopeHash,
|
|
269
|
+
allocationSetId: input.allocationSetId ?? null,
|
|
270
|
+
workDayId: input.workDayId ?? null,
|
|
271
|
+
expectedCredits: workUnits.reduce((sum, unit) => sum + unit.expectedCredits, 0),
|
|
272
|
+
highCredits: workUnits.reduce((sum, unit) => sum + unit.highCredits, 0),
|
|
273
|
+
workUnits,
|
|
274
|
+
capabilityNeeds: unique(workUnits.flatMap((unit) => unit.requiredCapabilities)),
|
|
275
|
+
environmentNeeds: unique(accepted.flatMap((entry) => normalizeDecisionExecutionEstimate(entry).environmentNeeds)),
|
|
276
|
+
reserves: { highCredits: workUnits.reduce((sum, unit) => sum + unit.highCredits, 0) },
|
|
277
|
+
blockers: unique(workUnits.flatMap((unit) => unit.blockers)),
|
|
278
|
+
priorityRationale: typeof input.metadata?.priorityRationale === "string" ? input.metadata.priorityRationale : null,
|
|
279
|
+
review: {},
|
|
280
|
+
metadata: input.metadata ?? {},
|
|
281
|
+
acceptedAt: null,
|
|
282
|
+
scheduledAt: null,
|
|
283
|
+
createdAt: timestamp,
|
|
284
|
+
updatedAt: timestamp
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
function isProviderAssignmentCandidateEligible(candidate) {
|
|
288
|
+
if (!candidate.teamId || !candidate.projectId || !candidate.capacityProviderId || !candidate.projectAgentClassId) return false;
|
|
289
|
+
if (candidate.mode === "acting" && candidate.readiness && !isDecisionReadyForActing(candidate.readiness)) return false;
|
|
290
|
+
return candidate.capacityEnvelope.mode === candidate.mode && candidate.decisionInput.mode === candidate.mode;
|
|
291
|
+
}
|
|
292
|
+
function validateTreeDxProxyHandle(handle, expected) {
|
|
293
|
+
if (!handle) return null;
|
|
294
|
+
if (!handle.id && !handle.projectId) return null;
|
|
295
|
+
const access = evaluateTreeDxProxyHandleAccess(handle, {
|
|
296
|
+
teamId: expected.teamId ?? null,
|
|
297
|
+
projectId: expected.projectId,
|
|
298
|
+
assignmentId: expected.assignmentId ?? null
|
|
299
|
+
});
|
|
300
|
+
if (!access.ok) {
|
|
301
|
+
return createAgentKernelModeFallback(
|
|
302
|
+
"assignment_treedx_proxy_scope_invalid",
|
|
303
|
+
access.reason ?? "TreeDX proxy handle scope does not match the assignment.",
|
|
304
|
+
{ retryable: access.code === "treedx_proxy_handle_expired", metadata: access.metadata }
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
function hasSecretLikeKey(key) {
|
|
310
|
+
return /(^|[_-])(plaintext|token|passphrase|password|private[_-]?key|deploy[_-]?key|raw[_-]?secret|unencrypted|credential)([_-]|$)/iu.test(key) || ["secretValue", "rawSecret", "githubInstallationToken", "deployKey", "privateKey"].includes(key);
|
|
311
|
+
}
|
|
312
|
+
function findSecretLikePath(value, path = "$") {
|
|
313
|
+
if (Array.isArray(value)) {
|
|
314
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
315
|
+
const found = findSecretLikePath(value[index], `${path}[${index}]`);
|
|
316
|
+
if (found) return found;
|
|
317
|
+
}
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
if (!isRecord(value)) return null;
|
|
321
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
322
|
+
const nextPath = `${path}.${key}`;
|
|
323
|
+
if (hasSecretLikeKey(key)) return nextPath;
|
|
324
|
+
const found = findSecretLikePath(entry, nextPath);
|
|
325
|
+
if (found) return found;
|
|
326
|
+
}
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
function normalizeWorkspaceAccessMode(value) {
|
|
330
|
+
return AGENT_ASSIGNMENT_WORKSPACE_ACCESS_MODES.includes(value) ? value : "context_only";
|
|
331
|
+
}
|
|
332
|
+
function capabilityHandleArrays(handles) {
|
|
333
|
+
const source = record(handles);
|
|
334
|
+
return [
|
|
335
|
+
...arrayValue(source.repository),
|
|
336
|
+
...arrayValue(source.treeDx),
|
|
337
|
+
...arrayValue(source.workflowOperations),
|
|
338
|
+
...arrayValue(source.secrets)
|
|
339
|
+
].filter(isRecord);
|
|
340
|
+
}
|
|
341
|
+
function planningContentArtifactWriteAllowed(input) {
|
|
342
|
+
if (input.assignment.mode !== "planning") return false;
|
|
343
|
+
const metadata = record(input.assignment.metadata);
|
|
344
|
+
if (metadata.allowPlanningContentArtifacts !== true) return false;
|
|
345
|
+
const handle = record(input.handle);
|
|
346
|
+
const contentProxyHandle = input.handle.kind === "treedx_workspace" || input.handle.kind === "repository_access" && handle.provider === "treedx_proxy" && handle.credentialMode === "brokered";
|
|
347
|
+
if (!contentProxyHandle) return false;
|
|
348
|
+
const allowed = /* @__PURE__ */ new Set(["read", "write", "commit", "test", "files:read", "files:search", "files:write", "git:commit"]);
|
|
349
|
+
return input.operations.every((operation) => allowed.has(operation));
|
|
350
|
+
}
|
|
351
|
+
function redactedProviderAssignmentCapabilityHandles(handles) {
|
|
352
|
+
const source = record(handles);
|
|
353
|
+
const redact = (handle) => {
|
|
354
|
+
const next = { ...record(handle) };
|
|
355
|
+
for (const key of Object.keys(next)) {
|
|
356
|
+
if (hasSecretLikeKey(key)) delete next[key];
|
|
357
|
+
}
|
|
358
|
+
return next;
|
|
359
|
+
};
|
|
360
|
+
return {
|
|
361
|
+
workspaceAccessMode: normalizeWorkspaceAccessMode(source.workspaceAccessMode),
|
|
362
|
+
repository: arrayValue(source.repository).map(redact),
|
|
363
|
+
treeDx: arrayValue(source.treeDx).map(redact),
|
|
364
|
+
workflowOperations: arrayValue(source.workflowOperations).map(redact),
|
|
365
|
+
secrets: arrayValue(source.secrets).map(redact),
|
|
366
|
+
metadata: record(source.metadata)
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
function providerAssignmentCapabilityHandlesContainSecretMaterial(value) {
|
|
370
|
+
return Boolean(findSecretLikePath(value));
|
|
371
|
+
}
|
|
372
|
+
function validateProviderAssignmentCapabilityHandles(input) {
|
|
373
|
+
const assignment = input.assignment;
|
|
374
|
+
const handles = input.capabilityHandles ?? assignment.capabilityHandles ?? null;
|
|
375
|
+
if (!handles) return null;
|
|
376
|
+
const leakedPath = findSecretLikePath(handles);
|
|
377
|
+
if (leakedPath) {
|
|
378
|
+
return createAgentKernelModeFallback(
|
|
379
|
+
"assignment_capability_handle_secret_material",
|
|
380
|
+
`Assignment ${assignment.id} capability handles include secret-like material at ${leakedPath}.`,
|
|
381
|
+
{ retryable: false, metadata: { path: leakedPath } }
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
const workspaceAccessMode = normalizeWorkspaceAccessMode(record(handles).workspaceAccessMode);
|
|
385
|
+
const allHandles = capabilityHandleArrays(handles);
|
|
386
|
+
for (const handle of allHandles) {
|
|
387
|
+
if (!handle.id || !handle.kind) {
|
|
388
|
+
return createAgentKernelModeFallback(
|
|
389
|
+
"assignment_capability_handle_invalid",
|
|
390
|
+
`Assignment ${assignment.id} has an invalid capability handle.`,
|
|
391
|
+
{ retryable: false }
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
if (handle.teamId && handle.teamId !== assignment.teamId || handle.projectId && handle.projectId !== assignment.projectId || handle.assignmentId && handle.assignmentId !== assignment.id) {
|
|
395
|
+
return createAgentKernelModeFallback(
|
|
396
|
+
"assignment_capability_handle_invalid",
|
|
397
|
+
`Assignment ${assignment.id} capability handle ${handle.id} is scoped to a different assignment.`,
|
|
398
|
+
{ retryable: false, metadata: { handleId: handle.id, kind: handle.kind } }
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
if (handle.expiresAt && Date.parse(handle.expiresAt) <= (input.now ?? /* @__PURE__ */ new Date()).getTime()) {
|
|
402
|
+
return createAgentKernelModeFallback(
|
|
403
|
+
"assignment_capability_handle_invalid",
|
|
404
|
+
`Assignment ${assignment.id} capability handle ${handle.id} has expired.`,
|
|
405
|
+
{ retryable: true, metadata: { handleId: handle.id, kind: handle.kind } }
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
const operations = stringList(handle.operations);
|
|
409
|
+
const writeCapable = operations.some((operation) => ["write", "commit", "push", "release", "dispatch_workflow", "files:write", "git:commit"].includes(operation));
|
|
410
|
+
const planningContentWrite = writeCapable && planningContentArtifactWriteAllowed({ assignment, handle, operations });
|
|
411
|
+
if (writeCapable && assignment.mode !== "acting" && !planningContentWrite) {
|
|
412
|
+
return createAgentKernelModeFallback(
|
|
413
|
+
"assignment_capability_handle_write_not_ready",
|
|
414
|
+
`Assignment ${assignment.id} cannot receive write-capable capability handles outside acting mode.`,
|
|
415
|
+
{ retryable: false, metadata: { handleId: handle.id, kind: handle.kind, operations } }
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
if (writeCapable && !planningContentWrite && !hasAcceptedCapacityPlanProvenance({
|
|
419
|
+
assignment,
|
|
420
|
+
decisionInput: input.decisionInput ?? null,
|
|
421
|
+
capacityEnvelope: input.capacityEnvelope ?? null
|
|
422
|
+
})) {
|
|
423
|
+
return createAgentKernelModeFallback(
|
|
424
|
+
"assignment_capability_handle_write_not_ready",
|
|
425
|
+
`Assignment ${assignment.id} write-capable capability handles require accepted capacity-plan provenance.`,
|
|
426
|
+
{ retryable: true, metadata: { handleId: handle.id, kind: handle.kind } }
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
if (workspaceAccessMode === "context_only" && writeCapable) {
|
|
430
|
+
return createAgentKernelModeFallback(
|
|
431
|
+
"assignment_capability_handle_workspace_denied",
|
|
432
|
+
`Assignment ${assignment.id} context-only workspace mode cannot receive write-capable handles.`,
|
|
433
|
+
{ retryable: false, metadata: { handleId: handle.id, kind: handle.kind } }
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
if (handle.kind === "workflow_operation") {
|
|
437
|
+
const workflow = handle;
|
|
438
|
+
if (!workflow.operationId || !workflow.repository || !workflow.workflowFile) {
|
|
439
|
+
return createAgentKernelModeFallback(
|
|
440
|
+
"assignment_workflow_operation_denied",
|
|
441
|
+
`Assignment ${assignment.id} workflow operation handle ${handle.id} is missing operation scope.`,
|
|
442
|
+
{ retryable: false, metadata: { handleId: handle.id } }
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
if (!operations.includes("dispatch_workflow")) {
|
|
446
|
+
return createAgentKernelModeFallback(
|
|
447
|
+
"assignment_workflow_operation_denied",
|
|
448
|
+
`Assignment ${assignment.id} workflow operation handle ${handle.id} is not dispatch-capable.`,
|
|
449
|
+
{ retryable: false, metadata: { handleId: handle.id } }
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
function globLikePathMatches(pattern, candidate) {
|
|
457
|
+
const normalizedPattern = pattern.replace(/^\/+/, "");
|
|
458
|
+
const normalizedCandidate = candidate.replace(/^\/+/, "");
|
|
459
|
+
if (!normalizedPattern || normalizedPattern === "**" || normalizedPattern === "*") return true;
|
|
460
|
+
if (normalizedPattern.endsWith("/**")) {
|
|
461
|
+
const prefix = normalizedPattern.slice(0, -3);
|
|
462
|
+
return normalizedCandidate === prefix || normalizedCandidate.startsWith(`${prefix}/`);
|
|
463
|
+
}
|
|
464
|
+
if (normalizedPattern.endsWith("*")) {
|
|
465
|
+
return normalizedCandidate.startsWith(normalizedPattern.slice(0, -1));
|
|
466
|
+
}
|
|
467
|
+
return normalizedCandidate === normalizedPattern || normalizedCandidate.startsWith(`${normalizedPattern}/`);
|
|
468
|
+
}
|
|
469
|
+
function evaluateTreeDxProxyHandleAccess(handle, request) {
|
|
470
|
+
if (!handle?.id) return { ok: false, code: "treedx_proxy_handle_missing", reason: "TreeDX proxy handle is required." };
|
|
471
|
+
if (handle.status === "revoked" || handle.revokedAt) return { ok: false, code: "treedx_proxy_handle_revoked", reason: "TreeDX proxy handle has been revoked." };
|
|
472
|
+
if (handle.status === "expired") return { ok: false, code: "treedx_proxy_handle_expired", reason: "TreeDX proxy handle has expired." };
|
|
473
|
+
if (handle.projectId !== request.projectId || request.teamId && handle.teamId !== request.teamId) {
|
|
474
|
+
return { ok: false, code: "treedx_proxy_scope_mismatch", reason: "TreeDX proxy handle scope does not match the project.", metadata: { projectId: request.projectId, handleProjectId: handle.projectId } };
|
|
475
|
+
}
|
|
476
|
+
if (request.assignmentId && handle.assignmentId && handle.assignmentId !== request.assignmentId) {
|
|
477
|
+
return { ok: false, code: "treedx_proxy_assignment_mismatch", reason: "TreeDX proxy handle is bound to a different assignment.", metadata: { assignmentId: request.assignmentId, handleAssignmentId: handle.assignmentId } };
|
|
478
|
+
}
|
|
479
|
+
if (request.repositoryId && handle.repositoryId && handle.repositoryId !== request.repositoryId) {
|
|
480
|
+
return { ok: false, code: "treedx_proxy_repository_mismatch", reason: "TreeDX proxy handle is bound to a different repository.", metadata: { repositoryId: request.repositoryId, handleRepositoryId: handle.repositoryId } };
|
|
481
|
+
}
|
|
482
|
+
if (request.workspaceId && handle.workspaceId && handle.workspaceId !== request.workspaceId) {
|
|
483
|
+
return { ok: false, code: "treedx_proxy_workspace_mismatch", reason: "TreeDX proxy handle is bound to a different workspace.", metadata: { workspaceId: request.workspaceId, handleWorkspaceId: handle.workspaceId } };
|
|
484
|
+
}
|
|
485
|
+
if (handle.expiresAt && Date.parse(handle.expiresAt) <= (request.now ?? /* @__PURE__ */ new Date()).getTime()) {
|
|
486
|
+
return { ok: false, code: "treedx_proxy_handle_expired", reason: "TreeDX proxy handle has expired." };
|
|
487
|
+
}
|
|
488
|
+
if (handle.token && request.token && handle.token !== request.token) {
|
|
489
|
+
return { ok: false, code: "treedx_proxy_token_mismatch", reason: "TreeDX proxy handle token does not match." };
|
|
490
|
+
}
|
|
491
|
+
const operation = request.operation ? String(request.operation) : null;
|
|
492
|
+
const allowedOperations = Array.isArray(handle.allowedOperations) ? handle.allowedOperations.map(String) : [];
|
|
493
|
+
if (operation && allowedOperations.length && !allowedOperations.includes(operation) && !allowedOperations.includes("*")) {
|
|
494
|
+
return { ok: false, code: "treedx_proxy_operation_denied", reason: "TreeDX proxy handle does not allow this operation.", metadata: { operation, allowedOperations } };
|
|
495
|
+
}
|
|
496
|
+
const path = request.path ? String(request.path).replace(/^\/+/, "") : null;
|
|
497
|
+
const allowedPaths = Array.isArray(handle.allowedPaths) ? handle.allowedPaths.map(String).filter(Boolean) : [];
|
|
498
|
+
if (path && allowedPaths.length && !allowedPaths.some((pattern) => globLikePathMatches(pattern, path))) {
|
|
499
|
+
return { ok: false, code: "treedx_proxy_path_denied", reason: "TreeDX proxy handle does not allow this path.", metadata: { path, allowedPaths } };
|
|
500
|
+
}
|
|
501
|
+
return { ok: true };
|
|
502
|
+
}
|
|
503
|
+
function compileExecutionCapabilityDemand(input) {
|
|
504
|
+
const agentProfile = input.agent?.execution.providerProfile;
|
|
505
|
+
const decisionInput = record(input.decisionInput);
|
|
506
|
+
const decisionPayload = record(decisionInput.input);
|
|
507
|
+
const decisionMetadata = record(decisionInput.metadata);
|
|
508
|
+
const capacityMetadata = record(record(input.capacityEnvelope).metadata);
|
|
509
|
+
const workUnitMetadata = record(input.workUnit?.metadata);
|
|
510
|
+
const assignmentMetadata = record(input.assignment?.metadata);
|
|
511
|
+
const allowedOutputs = record(input.assignment?.allowedOutputs);
|
|
512
|
+
const allowedPaths = uniqueStrings([
|
|
513
|
+
...stringList(input.agent?.execution.allowedPaths),
|
|
514
|
+
...stringList(input.workPackage?.constraints.allowedPaths)
|
|
515
|
+
]);
|
|
516
|
+
const forbiddenPaths = uniqueStrings([
|
|
517
|
+
...stringList(input.agent?.execution.forbiddenPaths),
|
|
518
|
+
...stringList(input.workPackage?.constraints.forbiddenPaths)
|
|
519
|
+
]);
|
|
520
|
+
const required = uniqueStrings([
|
|
521
|
+
...stringList(agentProfile?.requiredCapabilities),
|
|
522
|
+
...stringList(input.projectAgentClass?.requiredCapabilities),
|
|
523
|
+
...stringList(decisionPayload.requiredCapabilities),
|
|
524
|
+
...stringList(decisionMetadata.requiredCapabilities),
|
|
525
|
+
...stringList(capacityMetadata.requiredCapabilities),
|
|
526
|
+
...stringList(input.workUnit?.requiredCapabilities),
|
|
527
|
+
...stringList(workUnitMetadata.requiredCapabilities),
|
|
528
|
+
...stringList(assignmentMetadata.requiredCapabilities),
|
|
529
|
+
...stringList(input.workPackage?.constraints.requiredCapabilities)
|
|
530
|
+
]);
|
|
531
|
+
const preferred = preferredCapabilitiesFromAgent(input.agent);
|
|
532
|
+
const outputTypes = uniqueStrings([
|
|
533
|
+
...stringList(input.agent?.outputs.messageTypes),
|
|
534
|
+
...stringList(input.agent?.outputs.modelMutations),
|
|
535
|
+
...stringList(allowedOutputs.types),
|
|
536
|
+
...(input.workPackage?.expectedOutputs ?? []).map((entry) => entry.type).filter(Boolean)
|
|
537
|
+
]);
|
|
538
|
+
const resourceNeeds = [];
|
|
539
|
+
for (const need of input.resourceNeeds ?? []) {
|
|
540
|
+
pushResourceNeed(resourceNeeds, need);
|
|
541
|
+
}
|
|
542
|
+
for (const handle of capabilityHandleArrays(input.assignment?.capabilityHandles)) {
|
|
543
|
+
const need = handleResourceNeed(handle);
|
|
544
|
+
if (need) pushResourceNeed(resourceNeeds, need);
|
|
545
|
+
}
|
|
546
|
+
const workspace = record(input.assignment?.workspaceContext);
|
|
547
|
+
if (workspace.externalIssue || workspace.externalIssueKey) {
|
|
548
|
+
pushResourceNeed(resourceNeeds, {
|
|
549
|
+
kind: "external_issue",
|
|
550
|
+
operations: ["read"],
|
|
551
|
+
required: true,
|
|
552
|
+
metadata: {
|
|
553
|
+
externalRef: String(workspace.externalIssueKey ?? workspace.externalIssue)
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
if (workspace.externalJob || workspace.externalJobId) {
|
|
558
|
+
pushResourceNeed(resourceNeeds, {
|
|
559
|
+
kind: "external_job",
|
|
560
|
+
operations: ["read"],
|
|
561
|
+
required: true,
|
|
562
|
+
metadata: {
|
|
563
|
+
externalRef: String(workspace.externalJobId ?? workspace.externalJob)
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
return {
|
|
568
|
+
required,
|
|
569
|
+
preferred,
|
|
570
|
+
mode: normalizeAgentExecutionMode(input.mode ?? input.assignment?.mode ?? input.workPackage?.constraints.mode ?? input.capacityEnvelope?.mode),
|
|
571
|
+
resourceNeeds,
|
|
572
|
+
outputTypes,
|
|
573
|
+
metadata: {
|
|
574
|
+
...input.metadata ?? {},
|
|
575
|
+
allowedPaths,
|
|
576
|
+
forbiddenPaths,
|
|
577
|
+
providerProfile: agentProfile ?? null
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
function compileExecutionCapabilitySupply(input) {
|
|
582
|
+
const executionProvider = input.executionProvider ?? null;
|
|
583
|
+
const availability = input.availabilitySession ?? null;
|
|
584
|
+
const observation = record(executionProvider?.latestObservation);
|
|
585
|
+
const config = record(executionProvider?.config);
|
|
586
|
+
const activeGrants = (input.grants ?? []).filter((grant) => grant.state === "active");
|
|
587
|
+
const pressure = input.pressure ?? (typeof record(availability?.runnerPressure).pressure === "string" ? record(availability?.runnerPressure).pressure : void 0) ?? (observation.throttleState === "exhausted" || observation.throttleState === "throttled" ? observation.throttleState : void 0) ?? "normal";
|
|
588
|
+
return {
|
|
589
|
+
capacityProviderId: input.capacityProviderId,
|
|
590
|
+
executionProviderId: input.executionProviderId ?? executionProvider?.id ?? input.descriptor?.id ?? input.capacityProviderId,
|
|
591
|
+
kind: input.kind ?? input.descriptor?.kind ?? executionProvider?.kind ?? "local_process",
|
|
592
|
+
capabilities: uniqueStrings([
|
|
593
|
+
...stringList(input.descriptor?.capabilities),
|
|
594
|
+
...executionProvider?.kind ? [executionProvider.kind] : [],
|
|
595
|
+
...collectSupplyMetadataCapabilities(executionProvider),
|
|
596
|
+
...stringList(availability?.capabilities),
|
|
597
|
+
...stringList(input.providerCapabilities),
|
|
598
|
+
...stringList(input.checkInCapabilities)
|
|
599
|
+
]),
|
|
600
|
+
aliases: uniqueStrings([
|
|
601
|
+
...stringList(input.descriptor?.capabilityAliases),
|
|
602
|
+
...collectSupplyMetadataAliases(executionProvider)
|
|
603
|
+
]),
|
|
604
|
+
grants: uniqueStrings([
|
|
605
|
+
...activeGrants.map((grant) => grant.id),
|
|
606
|
+
...stringList(config.grants)
|
|
607
|
+
]),
|
|
608
|
+
availability: availability ? {
|
|
609
|
+
sessionId: availability.id,
|
|
610
|
+
status: availability.status,
|
|
611
|
+
checkedInAt: availability.checkedInAt
|
|
612
|
+
} : void 0,
|
|
613
|
+
pressure,
|
|
614
|
+
maxConcurrentAssignments: input.descriptor?.maxConcurrentAssignments ?? executionProvider?.maxConcurrentWorkers ?? input.maxConcurrentAssignments ?? 1,
|
|
615
|
+
nativeUnit: input.descriptor?.nativeUnit ?? executionProvider?.nativeUnit ?? "assignment",
|
|
616
|
+
quotaVisibility: input.descriptor?.quotaVisibility ?? executionProvider?.quotaVisibility ?? "opaque",
|
|
617
|
+
metadata: {
|
|
618
|
+
...input.metadata ?? {},
|
|
619
|
+
activeGrantScopes: activeGrants.map((grant) => grant.grantScope)
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
function evaluateExecutionProviderEligibility(input) {
|
|
624
|
+
const requiredCapabilities = uniqueStrings(input.demand.required);
|
|
625
|
+
const preferredCapabilities = uniqueStrings(input.demand.preferred ?? []);
|
|
626
|
+
const availableCapabilities = uniqueStrings(input.supply.capabilities);
|
|
627
|
+
const aliasCapabilities = uniqueStrings(input.supply.aliases ?? []);
|
|
628
|
+
const covered = /* @__PURE__ */ new Set([...availableCapabilities, ...aliasCapabilities]);
|
|
629
|
+
const missingCapabilities = requiredCapabilities.filter((capability) => !covered.has(capability));
|
|
630
|
+
const gates = {
|
|
631
|
+
grantMatches: booleanDefault(input.gates?.grantMatches, true),
|
|
632
|
+
availabilityMatches: booleanDefault(input.gates?.availabilityMatches, true),
|
|
633
|
+
runnerPressureAllows: booleanDefault(input.gates?.runnerPressureAllows, pressureAllows(input.supply.pressure)),
|
|
634
|
+
budgetAllows: booleanDefault(input.gates?.budgetAllows, true),
|
|
635
|
+
readinessAllows: booleanDefault(input.gates?.readinessAllows, true),
|
|
636
|
+
capabilityHandlesCanBeIssued: booleanDefault(input.gates?.capabilityHandlesCanBeIssued, true)
|
|
637
|
+
};
|
|
638
|
+
const reasonCodes = [
|
|
639
|
+
...missingCapabilities.map((capability) => `missing_capability:${capability}`),
|
|
640
|
+
...gates.grantMatches ? [] : ["grant_mismatch"],
|
|
641
|
+
...gates.availabilityMatches ? [] : ["availability_mismatch"],
|
|
642
|
+
...gates.runnerPressureAllows ? [] : ["runner_pressure_blocked"],
|
|
643
|
+
...gates.budgetAllows ? [] : ["budget_blocked"],
|
|
644
|
+
...gates.readinessAllows ? [] : ["readiness_blocked"],
|
|
645
|
+
...gates.capabilityHandlesCanBeIssued ? [] : ["capability_handle_blocked"]
|
|
646
|
+
];
|
|
647
|
+
return {
|
|
648
|
+
eligible: missingCapabilities.length === 0 && Object.values(gates).every(Boolean),
|
|
649
|
+
requiredCapabilities,
|
|
650
|
+
preferredCapabilities,
|
|
651
|
+
availableCapabilities,
|
|
652
|
+
aliasCapabilities,
|
|
653
|
+
missingCapabilities,
|
|
654
|
+
reasonCodes,
|
|
655
|
+
gates,
|
|
656
|
+
metadata: input.gates?.metadata
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
function buildProviderAssignmentExplanation(input) {
|
|
660
|
+
return {
|
|
661
|
+
teamId: "",
|
|
662
|
+
assignmentId: "",
|
|
663
|
+
source: input.source,
|
|
664
|
+
sourceId: input.sourceId ?? null,
|
|
665
|
+
eligible: input.eligible,
|
|
666
|
+
reasons: input.reasons ?? [],
|
|
667
|
+
gates: input.gates ?? {},
|
|
668
|
+
allocationPolicyVersion: input.allocationPolicyVersion ?? null,
|
|
669
|
+
grantScope: input.grantScope ?? null,
|
|
670
|
+
metadata: input.metadata ?? {}
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
function buildExecutionProviderAssignmentExplanation(input) {
|
|
674
|
+
return buildProviderAssignmentExplanation({
|
|
675
|
+
source: input.source,
|
|
676
|
+
sourceId: input.sourceId,
|
|
677
|
+
eligible: input.eligibility.eligible,
|
|
678
|
+
reasons: input.eligibility.reasonCodes,
|
|
679
|
+
allocationPolicyVersion: input.allocationPolicyVersion ?? null,
|
|
680
|
+
grantScope: input.grantScope ?? null,
|
|
681
|
+
gates: {
|
|
682
|
+
requiredCapabilities: input.eligibility.requiredCapabilities,
|
|
683
|
+
preferredCapabilities: input.eligibility.preferredCapabilities,
|
|
684
|
+
availableCapabilities: input.eligibility.availableCapabilities,
|
|
685
|
+
aliasCapabilities: input.eligibility.aliasCapabilities,
|
|
686
|
+
missingCapabilities: input.eligibility.missingCapabilities,
|
|
687
|
+
selectedProvider: input.supply.capacityProviderId,
|
|
688
|
+
selectedExecutionProvider: input.supply.executionProviderId,
|
|
689
|
+
executionProviderKind: input.supply.kind,
|
|
690
|
+
grantId: input.grantId ?? null,
|
|
691
|
+
grantScope: input.grantScope ?? null,
|
|
692
|
+
readinessGate: input.readinessGate ?? null,
|
|
693
|
+
allocationBudgetGate: input.allocationBudgetGate ?? null,
|
|
694
|
+
capabilityHandleGate: input.capabilityHandleGate ?? null,
|
|
695
|
+
demand: input.demand,
|
|
696
|
+
supply: input.supply,
|
|
697
|
+
eligibility: input.eligibility
|
|
698
|
+
},
|
|
699
|
+
metadata: input.metadata
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
function visibilityGateRecord(input) {
|
|
703
|
+
const assignment = record(input.assignment);
|
|
704
|
+
const explicitExplanation = record(input.explanation);
|
|
705
|
+
const assignmentExplanation = record(assignment.explanation);
|
|
706
|
+
const assignmentMetadata = record(assignment.metadata);
|
|
707
|
+
const eligibility = record(assignmentMetadata.eligibility);
|
|
708
|
+
const explicitGates = record(explicitExplanation.gates);
|
|
709
|
+
const assignmentGates = record(assignmentExplanation.gates);
|
|
710
|
+
if (Object.keys(explicitGates).length > 0) return explicitGates;
|
|
711
|
+
if (Object.keys(assignmentGates).length > 0) return assignmentGates;
|
|
712
|
+
return record(eligibility.gates);
|
|
713
|
+
}
|
|
714
|
+
function visibilityReasonCodes(input) {
|
|
715
|
+
const eligibility = record(input.gates.eligibility);
|
|
716
|
+
const explicitExplanation = record(input.explanation);
|
|
717
|
+
const assignmentExplanation = record(record(input.assignment).explanation);
|
|
718
|
+
return uniqueStrings([
|
|
719
|
+
...stringList(eligibility.reasonCodes),
|
|
720
|
+
...stringList(explicitExplanation.reasons),
|
|
721
|
+
...stringList(assignmentExplanation.reasons)
|
|
722
|
+
]);
|
|
723
|
+
}
|
|
724
|
+
function visibilityCapabilityEligible(input) {
|
|
725
|
+
const eligibility = record(input.gates.eligibility);
|
|
726
|
+
return booleanOrNull(eligibility.eligible) ?? booleanOrNull(record(input.explanation).eligible) ?? booleanOrNull(record(record(input.assignment).explanation).eligible);
|
|
727
|
+
}
|
|
728
|
+
function summarizeExecutionProviderVisibility(input) {
|
|
729
|
+
const assignment = record(input.assignment);
|
|
730
|
+
const modeRun = record(input.modeRun);
|
|
731
|
+
const explanation = record(input.explanation);
|
|
732
|
+
const lifecycleOutput = record(assignment.lifecycleOutput);
|
|
733
|
+
const lifecycleMetadata = record(lifecycleOutput.metadata);
|
|
734
|
+
const modeOutputs = record(modeRun.outputs);
|
|
735
|
+
const modeOutputMetadata = record(modeOutputs.metadata);
|
|
736
|
+
const modeMetadata = record(modeRun.metadata);
|
|
737
|
+
const traceRefs = record(modeRun.traceRefs);
|
|
738
|
+
const gates = visibilityGateRecord({ assignment, explanation });
|
|
739
|
+
const supply = record(gates.supply);
|
|
740
|
+
const capabilitySupply = record(gates.capabilitySupply);
|
|
741
|
+
const eligibility = record(gates.eligibility);
|
|
742
|
+
const selectedExecutionProvider = firstString(
|
|
743
|
+
gates.selectedExecutionProvider,
|
|
744
|
+
supply.executionProviderId,
|
|
745
|
+
capabilitySupply.executionProviderId,
|
|
746
|
+
modeRun.executionProviderId,
|
|
747
|
+
assignment.executionProviderId
|
|
748
|
+
);
|
|
749
|
+
const executionProviderKind = firstString(
|
|
750
|
+
gates.executionProviderKind,
|
|
751
|
+
supply.kind,
|
|
752
|
+
capabilitySupply.kind,
|
|
753
|
+
assignment.executionProviderKind,
|
|
754
|
+
record(assignment.metadata).executionProviderKind,
|
|
755
|
+
modeOutputMetadata.provider,
|
|
756
|
+
modeMetadata.provider,
|
|
757
|
+
lifecycleMetadata.provider
|
|
758
|
+
);
|
|
759
|
+
return {
|
|
760
|
+
executionProviderId: firstString(
|
|
761
|
+
modeRun.executionProviderId,
|
|
762
|
+
assignment.executionProviderId,
|
|
763
|
+
selectedExecutionProvider
|
|
764
|
+
),
|
|
765
|
+
executionProviderKind,
|
|
766
|
+
adapterStatus: firstString(
|
|
767
|
+
modeOutputs.status,
|
|
768
|
+
modeOutputMetadata.executionStatus,
|
|
769
|
+
modeMetadata.executionStatus,
|
|
770
|
+
lifecycleMetadata.executionStatus,
|
|
771
|
+
lifecycleOutput.status,
|
|
772
|
+
assignment.status
|
|
773
|
+
),
|
|
774
|
+
externalRef: firstString(
|
|
775
|
+
modeOutputs.externalRef,
|
|
776
|
+
traceRefs.externalRef,
|
|
777
|
+
lifecycleOutput.externalRef,
|
|
778
|
+
lifecycleMetadata.externalRef
|
|
779
|
+
),
|
|
780
|
+
externalUrl: firstString(
|
|
781
|
+
modeOutputs.externalUrl,
|
|
782
|
+
traceRefs.externalUrl,
|
|
783
|
+
lifecycleOutput.externalUrl,
|
|
784
|
+
lifecycleMetadata.externalUrl
|
|
785
|
+
),
|
|
786
|
+
blockerReason: firstString(
|
|
787
|
+
modeRun.fallbackReason,
|
|
788
|
+
modeOutputs.blockerReason,
|
|
789
|
+
modeOutputMetadata.blockerReason,
|
|
790
|
+
lifecycleOutput.blockerReason,
|
|
791
|
+
lifecycleMetadata.blockerReason,
|
|
792
|
+
assignment.lifecycleReason,
|
|
793
|
+
assignment.lifecycleCode
|
|
794
|
+
),
|
|
795
|
+
usage: firstArray(
|
|
796
|
+
modeOutputs.usage,
|
|
797
|
+
record(record(modeRun.usageActual).nativeUsage).executionUsage,
|
|
798
|
+
record(modeRun.usageActual).executionUsage,
|
|
799
|
+
lifecycleOutput.usage,
|
|
800
|
+
lifecycleMetadata.usage
|
|
801
|
+
),
|
|
802
|
+
artifacts: firstArray(
|
|
803
|
+
modeOutputs.artifacts,
|
|
804
|
+
lifecycleOutput.artifacts,
|
|
805
|
+
lifecycleMetadata.artifacts
|
|
806
|
+
),
|
|
807
|
+
requiredCapabilities: uniqueStrings([
|
|
808
|
+
...stringList(gates.requiredCapabilities),
|
|
809
|
+
...stringList(record(eligibility).requiredCapabilities)
|
|
810
|
+
]),
|
|
811
|
+
preferredCapabilities: uniqueStrings([
|
|
812
|
+
...stringList(gates.preferredCapabilities),
|
|
813
|
+
...stringList(record(eligibility).preferredCapabilities)
|
|
814
|
+
]),
|
|
815
|
+
availableCapabilities: uniqueStrings([
|
|
816
|
+
...stringList(gates.availableCapabilities),
|
|
817
|
+
...stringList(record(eligibility).availableCapabilities),
|
|
818
|
+
...stringList(supply.capabilities),
|
|
819
|
+
...stringList(capabilitySupply.capabilities)
|
|
820
|
+
]),
|
|
821
|
+
aliasCapabilities: uniqueStrings([
|
|
822
|
+
...stringList(gates.aliasCapabilities),
|
|
823
|
+
...stringList(record(eligibility).aliasCapabilities),
|
|
824
|
+
...stringList(supply.aliases),
|
|
825
|
+
...stringList(capabilitySupply.aliases)
|
|
826
|
+
]),
|
|
827
|
+
missingCapabilities: uniqueStrings([
|
|
828
|
+
...stringList(gates.missingCapabilities),
|
|
829
|
+
...stringList(record(eligibility).missingCapabilities)
|
|
830
|
+
]),
|
|
831
|
+
selectedProvider: firstString(
|
|
832
|
+
gates.selectedProvider,
|
|
833
|
+
supply.capacityProviderId,
|
|
834
|
+
capabilitySupply.capacityProviderId,
|
|
835
|
+
assignment.capacityProviderId,
|
|
836
|
+
assignment.providerId
|
|
837
|
+
),
|
|
838
|
+
selectedExecutionProvider,
|
|
839
|
+
capabilityEligible: visibilityCapabilityEligible({ assignment, explanation, gates }),
|
|
840
|
+
reasonCodes: visibilityReasonCodes({ assignment, explanation, gates }),
|
|
841
|
+
metadata: {
|
|
842
|
+
sources: {
|
|
843
|
+
assignment: Object.keys(assignment).length > 0,
|
|
844
|
+
modeRun: Object.keys(modeRun).length > 0,
|
|
845
|
+
explanation: Object.keys(explanation).length > 0
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
function decorateExecutionProviderVisibility(recordInput, options = {}) {
|
|
851
|
+
return {
|
|
852
|
+
...recordInput,
|
|
853
|
+
executionVisibility: summarizeExecutionProviderVisibility({
|
|
854
|
+
assignment: options.modeRun ? null : recordInput,
|
|
855
|
+
modeRun: options.modeRun ?? null,
|
|
856
|
+
explanation: options.explanation ?? record(recordInput.explanation)
|
|
857
|
+
})
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
function evaluateFallbackQuota(input) {
|
|
861
|
+
const quota = Number(input.quota);
|
|
862
|
+
if (!Number.isFinite(quota) || quota < 0) return null;
|
|
863
|
+
const existing = Number(input.existingCount ?? 0);
|
|
864
|
+
if (existing < quota) return null;
|
|
865
|
+
return createAgentKernelModeFallback(
|
|
866
|
+
"assignment_fallback_quota_exceeded",
|
|
867
|
+
"Fallback output quota is exhausted for this project scope.",
|
|
868
|
+
{ retryable: true, metadata: { quota, existingCount: existing } }
|
|
869
|
+
);
|
|
870
|
+
}
|
|
871
|
+
function deriveAllocationSetFromCapacityGrants(input) {
|
|
872
|
+
const timestamp = input.now ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
873
|
+
return {
|
|
874
|
+
id: input.id,
|
|
875
|
+
teamId: input.teamId,
|
|
876
|
+
version: input.version ?? timestamp,
|
|
877
|
+
status: input.status ?? "draft",
|
|
878
|
+
effectiveFrom: null,
|
|
879
|
+
effectiveUntil: null,
|
|
880
|
+
policy: {
|
|
881
|
+
source: "capacity_grants",
|
|
882
|
+
grantCount: input.grants.length
|
|
883
|
+
},
|
|
884
|
+
slices: input.grants.map((grant) => ({
|
|
885
|
+
projectId: grant.projectId,
|
|
886
|
+
capacityProviderId: grant.capacityProviderId,
|
|
887
|
+
laneId: grant.laneId,
|
|
888
|
+
environment: grant.environment,
|
|
889
|
+
priorityWeight: grant.priorityWeight,
|
|
890
|
+
overflowPolicy: grant.overflowPolicy,
|
|
891
|
+
percent: grant.portfolioAllocationPercent ?? null,
|
|
892
|
+
dailyCreditLimit: grant.dailyCreditLimit,
|
|
893
|
+
monthlyCreditLimit: grant.monthlyCreditLimit,
|
|
894
|
+
metadata: grant.metadata ?? {}
|
|
895
|
+
})),
|
|
896
|
+
createdById: input.createdById ?? null,
|
|
897
|
+
activatedAt: null,
|
|
898
|
+
supersededById: null,
|
|
899
|
+
metadata: input.metadata ?? {},
|
|
900
|
+
createdAt: timestamp,
|
|
901
|
+
updatedAt: timestamp
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
function deriveProviderAvailabilitySession(input) {
|
|
905
|
+
const timestamp = input.now ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
906
|
+
return {
|
|
907
|
+
id: input.id,
|
|
908
|
+
teamId: input.provider.teamId ?? input.provider.ownerTeamId ?? "",
|
|
909
|
+
capacityProviderId: input.provider.id,
|
|
910
|
+
registrationId: input.registrationId ?? null,
|
|
911
|
+
environment: input.environment ?? null,
|
|
912
|
+
status: input.provider.status === "draining" ? "draining" : "open",
|
|
913
|
+
checkedInAt: timestamp,
|
|
914
|
+
availableFrom: timestamp,
|
|
915
|
+
availableUntil: null,
|
|
916
|
+
executionProviders: input.executionProviders ?? [],
|
|
917
|
+
capabilities: Array.isArray(input.provider.capabilities) ? input.provider.capabilities.map(String) : [],
|
|
918
|
+
grants: input.grants ?? [],
|
|
919
|
+
nativeLimits: record(input.provider.metadata?.nativeLimits),
|
|
920
|
+
runnerPressure: input.runnerPressure ?? record(input.provider.metadata?.lastHealth),
|
|
921
|
+
constraints: input.constraints ?? {},
|
|
922
|
+
metadata: input.metadata ?? {},
|
|
923
|
+
createdAt: timestamp,
|
|
924
|
+
updatedAt: timestamp,
|
|
925
|
+
closedAt: null
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
function deriveAgentCapacityPlanFromCapacityPlan(plan) {
|
|
929
|
+
const raw = record(plan);
|
|
930
|
+
const project = record(raw.project);
|
|
931
|
+
const capacity = record(raw.capacity ?? raw.summary);
|
|
932
|
+
const providers = Array.isArray(raw.providers) ? raw.providers : [];
|
|
933
|
+
return {
|
|
934
|
+
teamId: String(raw.teamId ?? project.teamId ?? ""),
|
|
935
|
+
projectId: String(raw.projectId ?? project.id ?? ""),
|
|
936
|
+
environment: String(raw.environment ?? "staging"),
|
|
937
|
+
allocationSetId: typeof raw.allocationSetId === "string" ? raw.allocationSetId : null,
|
|
938
|
+
workday: {
|
|
939
|
+
teamId: String(raw.teamId ?? project.teamId ?? ""),
|
|
940
|
+
projectId: String(raw.projectId ?? project.id ?? ""),
|
|
941
|
+
workDayId: typeof raw.workDayId === "string" ? raw.workDayId : null,
|
|
942
|
+
environment: String(raw.environment ?? "staging"),
|
|
943
|
+
availableCredits: numberOrNull(capacity.availableCredits),
|
|
944
|
+
reservedCredits: numberOrNull(capacity.reservedCredits),
|
|
945
|
+
consumedCredits: numberOrNull(capacity.consumedCredits),
|
|
946
|
+
metadata: capacity
|
|
947
|
+
},
|
|
948
|
+
assignableProviders: providers.map((provider) => {
|
|
949
|
+
const entry = record(provider);
|
|
950
|
+
return {
|
|
951
|
+
capacityProviderId: String(entry.id ?? entry.capacityProviderId ?? ""),
|
|
952
|
+
executionProviderId: typeof entry.executionProviderId === "string" ? entry.executionProviderId : null,
|
|
953
|
+
laneId: typeof entry.laneId === "string" ? entry.laneId : null,
|
|
954
|
+
availableCredits: numberOrNull(entry.availableCredits),
|
|
955
|
+
reasons: Array.isArray(entry.reasons) ? entry.reasons.map(String) : [],
|
|
956
|
+
metadata: entry
|
|
957
|
+
};
|
|
958
|
+
}).filter((provider) => provider.capacityProviderId),
|
|
959
|
+
legacyPlan: plan,
|
|
960
|
+
metadata: {
|
|
961
|
+
source: "legacy_capacity_plan"
|
|
962
|
+
}
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
function deriveAgentCapacityEnvelopeFromReservation(input) {
|
|
966
|
+
const reservation = input.reservation;
|
|
967
|
+
return {
|
|
968
|
+
teamId: reservation.teamId,
|
|
969
|
+
projectId: reservation.projectId,
|
|
970
|
+
workDayId: reservation.workDayId,
|
|
971
|
+
environment: input.environment ?? null,
|
|
972
|
+
allocationSetId: input.allocationSetId ?? null,
|
|
973
|
+
mode: input.mode,
|
|
974
|
+
projectAgentClassId: input.projectAgentClassId ?? null,
|
|
975
|
+
capacityProviderId: reservation.capacityProviderId,
|
|
976
|
+
executionProviderId: reservation.executionProviderId ?? null,
|
|
977
|
+
laneId: reservation.laneId,
|
|
978
|
+
reservationId: reservation.id,
|
|
979
|
+
availableCredits: null,
|
|
980
|
+
reservedCredits: reservation.reservedCredits,
|
|
981
|
+
consumedCredits: reservation.consumedCredits,
|
|
982
|
+
nativeUnit: reservation.nativeUnit ?? null,
|
|
983
|
+
reservedNativeAmount: reservation.reservedNativeAmount ?? null,
|
|
984
|
+
limits: input.limits ?? {},
|
|
985
|
+
metadata: {
|
|
986
|
+
...reservation.metadata ?? {},
|
|
987
|
+
...input.metadata ?? {}
|
|
988
|
+
}
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
function deriveModeRunUsageSettlement(actual) {
|
|
992
|
+
return {
|
|
993
|
+
taskUsageActualId: actual.id,
|
|
994
|
+
capacityLedgerEntryId: typeof actual.metadata?.capacityLedgerEntryId === "string" ? actual.metadata.capacityLedgerEntryId : null,
|
|
995
|
+
actualCredits: actual.actualCredits,
|
|
996
|
+
actualUsd: actual.actualUsd,
|
|
997
|
+
nativeUsage: record(actual.nativeUsage),
|
|
998
|
+
metadata: actual.metadata ?? {}
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
function isAgentExecutionMode(value) {
|
|
1002
|
+
return value === "planning" || value === "acting";
|
|
1003
|
+
}
|
|
1004
|
+
function normalizeAgentExecutionMode(value, fallback = "planning") {
|
|
1005
|
+
return isAgentExecutionMode(value) ? value : fallback;
|
|
1006
|
+
}
|
|
1007
|
+
function createAgentKernelModeFallback(code, reason, options = {}) {
|
|
1008
|
+
return {
|
|
1009
|
+
code,
|
|
1010
|
+
reason,
|
|
1011
|
+
retryable: options.retryable ?? true,
|
|
1012
|
+
metadata: options.metadata ?? {}
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
function isAgentModeAllowedForClass(mode, agentClass, profile) {
|
|
1016
|
+
if (agentClass?.status && agentClass.status !== "active") return false;
|
|
1017
|
+
const classModes = Array.isArray(agentClass?.allowedModes) ? agentClass.allowedModes : [];
|
|
1018
|
+
if (classModes.length && !classModes.includes(mode)) return false;
|
|
1019
|
+
const profileModes = Array.isArray(profile?.allowedModes) ? profile.allowedModes : [];
|
|
1020
|
+
if (profileModes.length && !profileModes.includes(mode)) return false;
|
|
1021
|
+
return true;
|
|
1022
|
+
}
|
|
1023
|
+
function deriveAgentCapacityEnvelopeFromAssignment(assignment) {
|
|
1024
|
+
const envelope = record(assignment.capacityEnvelope);
|
|
1025
|
+
return {
|
|
1026
|
+
teamId: String(envelope.teamId ?? assignment.teamId),
|
|
1027
|
+
projectId: String(envelope.projectId ?? assignment.projectId),
|
|
1028
|
+
workDayId: typeof envelope.workDayId === "string" ? envelope.workDayId : assignment.workDayId ?? null,
|
|
1029
|
+
environment: typeof envelope.environment === "string" ? envelope.environment : null,
|
|
1030
|
+
allocationSetId: typeof envelope.allocationSetId === "string" ? envelope.allocationSetId : assignment.allocationSetId ?? null,
|
|
1031
|
+
mode: normalizeAgentExecutionMode(envelope.mode ?? assignment.mode),
|
|
1032
|
+
projectAgentClassId: typeof envelope.projectAgentClassId === "string" ? envelope.projectAgentClassId : assignment.projectAgentClassId,
|
|
1033
|
+
capacityProviderId: typeof envelope.capacityProviderId === "string" ? envelope.capacityProviderId : assignment.capacityProviderId,
|
|
1034
|
+
executionProviderId: typeof envelope.executionProviderId === "string" ? envelope.executionProviderId : assignment.executionProviderId ?? null,
|
|
1035
|
+
laneId: typeof envelope.laneId === "string" ? envelope.laneId : null,
|
|
1036
|
+
reservationId: typeof envelope.reservationId === "string" ? envelope.reservationId : assignment.reservationId ?? null,
|
|
1037
|
+
nativeUnit: typeof envelope.nativeUnit === "string" ? envelope.nativeUnit : null,
|
|
1038
|
+
reservedNativeAmount: numberOrNull(envelope.reservedNativeAmount),
|
|
1039
|
+
availableCredits: numberOrNull(envelope.availableCredits),
|
|
1040
|
+
reservedCredits: numberOrNull(envelope.reservedCredits),
|
|
1041
|
+
consumedCredits: numberOrNull(envelope.consumedCredits),
|
|
1042
|
+
limits: record(envelope.limits),
|
|
1043
|
+
metadata: record(envelope.metadata)
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
function deriveDecisionExecutionInputFromAssignment(assignment) {
|
|
1047
|
+
const decision = record(assignment.decisionInput);
|
|
1048
|
+
return {
|
|
1049
|
+
teamId: String(decision.teamId ?? assignment.teamId),
|
|
1050
|
+
projectId: String(decision.projectId ?? assignment.projectId),
|
|
1051
|
+
projectAgentClassId: String(decision.projectAgentClassId ?? assignment.projectAgentClassId),
|
|
1052
|
+
mode: normalizeAgentExecutionMode(decision.mode ?? assignment.mode),
|
|
1053
|
+
taskId: typeof decision.taskId === "string" ? decision.taskId : assignment.taskId ?? null,
|
|
1054
|
+
workDayId: typeof decision.workDayId === "string" ? decision.workDayId : assignment.workDayId ?? null,
|
|
1055
|
+
agentId: typeof decision.agentId === "string" ? decision.agentId : assignment.agentId ?? null,
|
|
1056
|
+
handlerId: typeof decision.handlerId === "string" ? decision.handlerId : assignment.handlerId ?? null,
|
|
1057
|
+
capacity: deriveAgentCapacityEnvelopeFromAssignment(assignment),
|
|
1058
|
+
input: record(decision.input),
|
|
1059
|
+
metadata: record(decision.metadata)
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
function validateAgentKernelModeExecutionInput(input) {
|
|
1063
|
+
const assignment = input.assignment;
|
|
1064
|
+
const now = input.now instanceof Date ? input.now : new Date(input.now ?? Date.now());
|
|
1065
|
+
if (isProviderAssignmentLeaseExpired(assignment, now)) {
|
|
1066
|
+
return createAgentKernelModeFallback(
|
|
1067
|
+
"assignment_lease_expired",
|
|
1068
|
+
`Assignment ${assignment.id} lease expired before execution.`,
|
|
1069
|
+
{ retryable: true }
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
1072
|
+
if (!assignment.projectId || !assignment.agentId && !deriveDecisionExecutionInputFromAssignment(assignment).agentId) {
|
|
1073
|
+
return createAgentKernelModeFallback(
|
|
1074
|
+
"assignment_missing_project_or_agent",
|
|
1075
|
+
`Assignment ${assignment.id} is missing project or agent routing.`,
|
|
1076
|
+
{ retryable: false }
|
|
1077
|
+
);
|
|
1078
|
+
}
|
|
1079
|
+
const mode = normalizeAgentExecutionMode(assignment.mode);
|
|
1080
|
+
const profile = input.kernelProfile ?? input.projectAgentClass?.kernelProfile ?? null;
|
|
1081
|
+
if (!isAgentModeAllowedForClass(mode, input.projectAgentClass, profile)) {
|
|
1082
|
+
return createAgentKernelModeFallback(
|
|
1083
|
+
"assignment_mode_not_allowed",
|
|
1084
|
+
`Assignment ${assignment.id} mode ${mode} is not allowed for the project agent class.`,
|
|
1085
|
+
{ retryable: false }
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
const capacity = input.capacityEnvelope ?? deriveAgentCapacityEnvelopeFromAssignment(assignment);
|
|
1089
|
+
if (capacity.mode !== mode) {
|
|
1090
|
+
return createAgentKernelModeFallback(
|
|
1091
|
+
"assignment_missing_capacity_envelope",
|
|
1092
|
+
`Assignment ${assignment.id} capacity envelope does not match selected mode ${mode}.`,
|
|
1093
|
+
{ retryable: false }
|
|
1094
|
+
);
|
|
1095
|
+
}
|
|
1096
|
+
const decision = input.decisionInput ?? deriveDecisionExecutionInputFromAssignment(assignment);
|
|
1097
|
+
if (decision.mode !== mode || decision.projectId !== assignment.projectId) {
|
|
1098
|
+
return createAgentKernelModeFallback(
|
|
1099
|
+
"assignment_missing_decision_input",
|
|
1100
|
+
`Assignment ${assignment.id} decision input does not match assignment scope.`,
|
|
1101
|
+
{ retryable: false }
|
|
1102
|
+
);
|
|
1103
|
+
}
|
|
1104
|
+
if (mode === "acting" && input.readiness && !isDecisionReadyForActing(input.readiness)) {
|
|
1105
|
+
return createAgentKernelModeFallback(
|
|
1106
|
+
"assignment_decision_not_ready",
|
|
1107
|
+
`Assignment ${assignment.id} is not ready for acting execution.`,
|
|
1108
|
+
{ retryable: true, metadata: { readiness: input.readiness } }
|
|
1109
|
+
);
|
|
1110
|
+
}
|
|
1111
|
+
if (mode === "acting" && !input.readiness) {
|
|
1112
|
+
return createAgentKernelModeFallback(
|
|
1113
|
+
"assignment_decision_not_ready",
|
|
1114
|
+
`Assignment ${assignment.id} is missing decision readiness for acting execution.`,
|
|
1115
|
+
{ retryable: true }
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
1118
|
+
if (mode === "acting" && (!capacity.reservationId || Number(capacity.reservedCredits ?? 0) <= 0)) {
|
|
1119
|
+
return createAgentKernelModeFallback(
|
|
1120
|
+
"assignment_capacity_not_reserved",
|
|
1121
|
+
`Assignment ${assignment.id} is missing reserved capacity for acting execution.`,
|
|
1122
|
+
{ retryable: true, metadata: { reservationId: capacity.reservationId ?? null, reservedCredits: capacity.reservedCredits ?? null } }
|
|
1123
|
+
);
|
|
1124
|
+
}
|
|
1125
|
+
if (mode === "acting" && !hasAcceptedCapacityPlanProvenance({ assignment, decisionInput: decision, capacityEnvelope: capacity })) {
|
|
1126
|
+
return createAgentKernelModeFallback(
|
|
1127
|
+
"assignment_capacity_plan_not_accepted",
|
|
1128
|
+
`Assignment ${assignment.id} is missing accepted capacity-plan provenance for acting execution.`,
|
|
1129
|
+
{ retryable: true }
|
|
1130
|
+
);
|
|
1131
|
+
}
|
|
1132
|
+
const policyMaxAttempts = numberOrNull(record(input.kernelPolicy?.fallback).maxAttempts) ?? numberOrNull(record(input.projectAgentClass?.kernelPolicy?.fallback).maxAttempts) ?? numberOrNull(record(input.kernelProfile?.metadata).maxAttempts);
|
|
1133
|
+
if (policyMaxAttempts !== null && Number(assignment.attemptCount ?? 0) >= policyMaxAttempts) {
|
|
1134
|
+
return createAgentKernelModeFallback(
|
|
1135
|
+
"assignment_retry_policy_exceeded",
|
|
1136
|
+
`Assignment ${assignment.id} has exceeded kernel retry policy.`,
|
|
1137
|
+
{ retryable: false, metadata: { attemptCount: assignment.attemptCount ?? 0, maxAttempts: policyMaxAttempts } }
|
|
1138
|
+
);
|
|
1139
|
+
}
|
|
1140
|
+
const assignmentMetadata = record(assignment.metadata);
|
|
1141
|
+
const eligibility = record(assignmentMetadata.eligibility);
|
|
1142
|
+
const eligibilityGates = record(eligibility.gates);
|
|
1143
|
+
const explanationGates = record(record(assignment.explanation).gates);
|
|
1144
|
+
const explanationSupply = record(explanationGates.supply);
|
|
1145
|
+
const explanationCapabilitySupply = record(explanationGates.capabilitySupply);
|
|
1146
|
+
const demand = compileExecutionCapabilityDemand({
|
|
1147
|
+
projectAgentClass: input.projectAgentClass,
|
|
1148
|
+
decisionInput: decision,
|
|
1149
|
+
capacityEnvelope: capacity,
|
|
1150
|
+
assignment,
|
|
1151
|
+
mode
|
|
1152
|
+
});
|
|
1153
|
+
const requiredCapabilities = uniqueStrings([
|
|
1154
|
+
...demand.required,
|
|
1155
|
+
...stringList(input.projectAgentClass?.requiredCapabilities),
|
|
1156
|
+
...stringList(assignmentMetadata.requiredCapabilities),
|
|
1157
|
+
...stringList(record(decision.metadata).requiredCapabilities),
|
|
1158
|
+
...stringList(record(capacity.metadata).requiredCapabilities)
|
|
1159
|
+
]);
|
|
1160
|
+
const availableCapabilities = uniqueStrings([
|
|
1161
|
+
...stringList(eligibilityGates.availableCapabilities),
|
|
1162
|
+
...stringList(explanationGates.availableCapabilities),
|
|
1163
|
+
...stringList(explanationGates.aliasCapabilities),
|
|
1164
|
+
...stringList(explanationSupply.capabilities),
|
|
1165
|
+
...stringList(explanationSupply.aliases),
|
|
1166
|
+
...stringList(explanationCapabilitySupply.capabilities),
|
|
1167
|
+
...stringList(explanationCapabilitySupply.aliases),
|
|
1168
|
+
...stringList(assignmentMetadata.availableCapabilities)
|
|
1169
|
+
]);
|
|
1170
|
+
const missingCapabilities = requiredCapabilities.filter((capability) => !availableCapabilities.includes(capability));
|
|
1171
|
+
if (requiredCapabilities.length && (!availableCapabilities.length || missingCapabilities.length)) {
|
|
1172
|
+
return createAgentKernelModeFallback(
|
|
1173
|
+
"assignment_eligibility_capability_mismatch",
|
|
1174
|
+
`Assignment ${assignment.id} required capabilities were not covered by eligibility metadata.`,
|
|
1175
|
+
{ retryable: true, metadata: { requiredCapabilities, availableCapabilities, missingCapabilities, source: "execution_capability_eligibility" } }
|
|
1176
|
+
);
|
|
1177
|
+
}
|
|
1178
|
+
const capabilityHandleFallback = validateProviderAssignmentCapabilityHandles({
|
|
1179
|
+
assignment,
|
|
1180
|
+
decisionInput: decision,
|
|
1181
|
+
capacityEnvelope: capacity,
|
|
1182
|
+
now
|
|
1183
|
+
});
|
|
1184
|
+
if (capabilityHandleFallback) return capabilityHandleFallback;
|
|
1185
|
+
const proxyFallback = validateTreeDxProxyHandle(input.treedxProxyHandle ?? record(assignment.treedxProxyHandle), {
|
|
1186
|
+
teamId: assignment.teamId,
|
|
1187
|
+
projectId: assignment.projectId,
|
|
1188
|
+
assignmentId: assignment.id
|
|
1189
|
+
});
|
|
1190
|
+
if (proxyFallback) return proxyFallback;
|
|
1191
|
+
return null;
|
|
1192
|
+
}
|
|
1193
|
+
function selectAgentKernelModeDecision(observation) {
|
|
1194
|
+
const planningReady = Math.max(0, Number(observation.planningReady ?? 0));
|
|
1195
|
+
const actingReady = Math.max(0, Number(observation.actingReady ?? 0));
|
|
1196
|
+
const fallbackReady = Math.max(0, Number(observation.fallbackReady ?? 0));
|
|
1197
|
+
const planningBudget = Number(observation.planningBudgetCredits ?? 0);
|
|
1198
|
+
const actingBudget = Number(observation.actingBudgetCredits ?? 0);
|
|
1199
|
+
const hasPlanningBudget = !Number.isFinite(planningBudget) || planningBudget > 0;
|
|
1200
|
+
const hasActingBudget = !Number.isFinite(actingBudget) || actingBudget > 0;
|
|
1201
|
+
if (observation.modePreference === "planning" && planningReady > 0 && hasPlanningBudget) {
|
|
1202
|
+
return { kind: "mode", mode: "planning", reason: "preferred_planning_ready", metadata: observation.metadata ?? {} };
|
|
1203
|
+
}
|
|
1204
|
+
if (observation.modePreference === "acting" && actingReady > 0 && hasActingBudget) {
|
|
1205
|
+
return { kind: "mode", mode: "acting", reason: "preferred_acting_ready", metadata: observation.metadata ?? {} };
|
|
1206
|
+
}
|
|
1207
|
+
if (actingReady > 0 && hasActingBudget) {
|
|
1208
|
+
return { kind: "mode", mode: "acting", reason: "acting_queue_ready", metadata: observation.metadata ?? {} };
|
|
1209
|
+
}
|
|
1210
|
+
if (planningReady > 0 && hasPlanningBudget) {
|
|
1211
|
+
return { kind: "mode", mode: "planning", reason: "planning_queue_ready", metadata: observation.metadata ?? {} };
|
|
1212
|
+
}
|
|
1213
|
+
if (fallbackReady > 0) {
|
|
1214
|
+
return { kind: "fallback", mode: null, reason: "fallback_queue_ready", metadata: observation.metadata ?? {} };
|
|
1215
|
+
}
|
|
1216
|
+
return { kind: "idle", mode: null, reason: "no_eligible_work", metadata: observation.metadata ?? {} };
|
|
1217
|
+
}
|
|
1218
|
+
function validateAgentKernelOutputs(input) {
|
|
1219
|
+
const allowed = record(input.allowedOutputs);
|
|
1220
|
+
if (!Object.keys(allowed).length) return { ok: true };
|
|
1221
|
+
const outputs = record(input.outputs);
|
|
1222
|
+
const allowedStatuses = Array.isArray(allowed.statuses) ? allowed.statuses.map(String) : [];
|
|
1223
|
+
const status = typeof outputs.status === "string" ? outputs.status : null;
|
|
1224
|
+
if (allowedStatuses.length && (!status || !allowedStatuses.includes(status))) {
|
|
1225
|
+
return { ok: false, reason: `Output status ${status ?? "<missing>"} is not allowed for ${input.mode}.`, metadata: { status, allowedStatuses } };
|
|
1226
|
+
}
|
|
1227
|
+
const allowedTypes = Array.isArray(allowed.types) ? allowed.types.map(String) : [];
|
|
1228
|
+
const metadata = record(outputs.metadata);
|
|
1229
|
+
const outputType = typeof metadata.type === "string" ? metadata.type : typeof metadata.kind === "string" ? metadata.kind : null;
|
|
1230
|
+
if (allowedTypes.length && (!outputType || !allowedTypes.includes(outputType))) {
|
|
1231
|
+
return { ok: false, reason: `Output type ${outputType ?? "<missing>"} is not allowed for ${input.mode}.`, metadata: { outputType, allowedTypes } };
|
|
1232
|
+
}
|
|
1233
|
+
return { ok: true };
|
|
1234
|
+
}
|
|
1235
|
+
function treeDxProxyHeaders(handle) {
|
|
1236
|
+
const headers = {
|
|
1237
|
+
"x-treeseed-assignment-id": String(handle.assignmentId ?? ""),
|
|
1238
|
+
"x-treeseed-treedx-proxy-handle-id": String(handle.id ?? "")
|
|
1239
|
+
};
|
|
1240
|
+
if (handle.token) headers["x-treeseed-treedx-proxy-handle"] = handle.token;
|
|
1241
|
+
return Object.fromEntries(Object.entries(headers).filter(([, value]) => value));
|
|
1242
|
+
}
|
|
1243
|
+
function isAgentCapacityPlanStaleOrSuperseded(plan, currentScopeHash) {
|
|
1244
|
+
if (plan.status === "superseded") return true;
|
|
1245
|
+
return Boolean(currentScopeHash && plan.scopeHash && currentScopeHash !== plan.scopeHash);
|
|
1246
|
+
}
|
|
1247
|
+
function hasAcceptedCapacityPlanProvenance(input) {
|
|
1248
|
+
const assignmentMetadata = record(input.assignment?.metadata);
|
|
1249
|
+
const decisionMetadata = record(input.decisionInput?.metadata ?? record(input.assignment?.decisionInput).metadata);
|
|
1250
|
+
const capacityMetadata = record(input.capacityEnvelope?.metadata ?? record(input.assignment?.capacityEnvelope).metadata);
|
|
1251
|
+
const status = String(
|
|
1252
|
+
assignmentMetadata.capacityPlanStatus ?? decisionMetadata.capacityPlanStatus ?? capacityMetadata.capacityPlanStatus ?? ""
|
|
1253
|
+
);
|
|
1254
|
+
const capacityPlanId = assignmentMetadata.capacityPlanId ?? decisionMetadata.capacityPlanId ?? capacityMetadata.capacityPlanId;
|
|
1255
|
+
const synthesizedFrom = input.assignment?.synthesizedFrom ?? assignmentMetadata.synthesizedFrom ?? capacityMetadata.synthesizedFrom;
|
|
1256
|
+
return Boolean(capacityPlanId && (["accepted", "scheduled", "active"].includes(status) || synthesizedFrom === "capacity_plan"));
|
|
1257
|
+
}
|
|
1258
|
+
function isProviderAssignmentLeaseExpired(assignment, now = /* @__PURE__ */ new Date()) {
|
|
1259
|
+
if (!assignment.leaseExpiresAt) return false;
|
|
1260
|
+
const expiresAt = Date.parse(assignment.leaseExpiresAt);
|
|
1261
|
+
return Number.isFinite(expiresAt) && expiresAt <= now.getTime();
|
|
1262
|
+
}
|
|
1263
|
+
function isProviderAssignmentLeasable(assignment, now = /* @__PURE__ */ new Date()) {
|
|
1264
|
+
if (assignment.status === "pending" && assignment.leaseState === "unleased") return true;
|
|
1265
|
+
if (assignment.status === "returned" && assignment.leaseState === "released") return true;
|
|
1266
|
+
if (assignment.leaseState === "leased" && isProviderAssignmentLeaseExpired(assignment, now)) return true;
|
|
1267
|
+
return false;
|
|
1268
|
+
}
|
|
1269
|
+
const CAPACITY_RUNTIME_REASON_DETAILS = {
|
|
1270
|
+
provider_inactive: {
|
|
1271
|
+
title: "Provider is inactive",
|
|
1272
|
+
message: "The capacity provider is not currently eligible to receive work.",
|
|
1273
|
+
owner: "provider_operator",
|
|
1274
|
+
nextAction: "Start or reactivate the provider runtime and confirm it is checking in.",
|
|
1275
|
+
severity: "danger"
|
|
1276
|
+
},
|
|
1277
|
+
provider_session_not_open: {
|
|
1278
|
+
title: "Provider session is not open",
|
|
1279
|
+
message: "The provider has no open availability session for this assignment.",
|
|
1280
|
+
owner: "provider_operator",
|
|
1281
|
+
nextAction: "Open a provider session by running the provider manager check-in loop.",
|
|
1282
|
+
severity: "danger"
|
|
1283
|
+
},
|
|
1284
|
+
outside_availability_window: {
|
|
1285
|
+
title: "Outside availability window",
|
|
1286
|
+
message: "The provider checked in, but its availability window does not cover the current time.",
|
|
1287
|
+
owner: "provider_operator",
|
|
1288
|
+
nextAction: "Adjust the provider availability window or wait until the window opens.",
|
|
1289
|
+
severity: "warning"
|
|
1290
|
+
},
|
|
1291
|
+
missing_required_capability: {
|
|
1292
|
+
title: "Missing required capability",
|
|
1293
|
+
message: "No checked-in execution provider advertised all capabilities required by the assignment.",
|
|
1294
|
+
owner: "team_admin",
|
|
1295
|
+
nextAction: "Update provider grants/capabilities or assign the work to a provider that supports the required capability set.",
|
|
1296
|
+
severity: "danger"
|
|
1297
|
+
},
|
|
1298
|
+
missing_checked_in_grant: {
|
|
1299
|
+
title: "Grant was not checked in",
|
|
1300
|
+
message: "The provider did not present the grant needed for this project, class, or mode.",
|
|
1301
|
+
owner: "provider_operator",
|
|
1302
|
+
nextAction: "Refresh provider configuration and check in with the expected grants.",
|
|
1303
|
+
severity: "danger"
|
|
1304
|
+
},
|
|
1305
|
+
missing_active_grant: {
|
|
1306
|
+
title: "Grant is not active",
|
|
1307
|
+
message: "A matching grant exists but is not active for assignment leasing.",
|
|
1308
|
+
owner: "team_admin",
|
|
1309
|
+
nextAction: "Activate or replace the capacity grant before retrying the assignment.",
|
|
1310
|
+
severity: "danger"
|
|
1311
|
+
},
|
|
1312
|
+
workday_not_active: {
|
|
1313
|
+
title: "Workday is not active",
|
|
1314
|
+
message: "The assignment cannot lease because its workday envelope is not active.",
|
|
1315
|
+
owner: "team_admin",
|
|
1316
|
+
nextAction: "Start or resume the workday, or move the work to an active envelope.",
|
|
1317
|
+
severity: "warning"
|
|
1318
|
+
},
|
|
1319
|
+
decision_readiness_not_ready: {
|
|
1320
|
+
title: "Decision is not ready",
|
|
1321
|
+
message: "The underlying decision input has not reached execution readiness.",
|
|
1322
|
+
owner: "project",
|
|
1323
|
+
nextAction: "Resolve open questions, accept the proposal, or mark the decision readiness gate ready.",
|
|
1324
|
+
severity: "warning"
|
|
1325
|
+
},
|
|
1326
|
+
capacity_plan_not_ready: {
|
|
1327
|
+
title: "Capacity plan is not ready",
|
|
1328
|
+
message: "Acting work requires an accepted, scheduled, or active capacity plan.",
|
|
1329
|
+
owner: "project",
|
|
1330
|
+
nextAction: "Accept or schedule the capacity plan generated during planning.",
|
|
1331
|
+
severity: "warning"
|
|
1332
|
+
},
|
|
1333
|
+
runner_pressure_exhausted: {
|
|
1334
|
+
title: "Runner pressure exhausted",
|
|
1335
|
+
message: "The provider runner reported that local concurrency, quota, or pressure limits are exhausted.",
|
|
1336
|
+
owner: "provider_operator",
|
|
1337
|
+
nextAction: "Wait for active work to finish or increase provider-local runner capacity.",
|
|
1338
|
+
severity: "warning"
|
|
1339
|
+
},
|
|
1340
|
+
allocation_exhausted: {
|
|
1341
|
+
title: "Allocation exhausted",
|
|
1342
|
+
message: "The matching allocation set does not have enough remaining credits for this assignment.",
|
|
1343
|
+
owner: "team_admin",
|
|
1344
|
+
nextAction: "Increase allocation, change routing, or defer lower-priority work.",
|
|
1345
|
+
severity: "danger"
|
|
1346
|
+
},
|
|
1347
|
+
allocation_overrun_hold: {
|
|
1348
|
+
title: "Allocation overrun hold",
|
|
1349
|
+
message: "The assignment would exceed allocation policy and requires overrun approval.",
|
|
1350
|
+
owner: "team_admin",
|
|
1351
|
+
nextAction: "Approve the overrun or adjust the capacity plan before leasing.",
|
|
1352
|
+
severity: "warning"
|
|
1353
|
+
},
|
|
1354
|
+
treedx_proxy_handle_missing: {
|
|
1355
|
+
title: "TreeDX proxy handle missing",
|
|
1356
|
+
message: "Content-scoped work requires an assignment-scoped TreeDX proxy handle.",
|
|
1357
|
+
owner: "system",
|
|
1358
|
+
nextAction: "Regenerate the assignment after TreeDX workspace access is available.",
|
|
1359
|
+
severity: "danger"
|
|
1360
|
+
},
|
|
1361
|
+
treedx_proxy_scope_mismatch: {
|
|
1362
|
+
title: "TreeDX proxy scope mismatch",
|
|
1363
|
+
message: "The TreeDX proxy handle does not match the assignment project, workspace, or operation scope.",
|
|
1364
|
+
owner: "system",
|
|
1365
|
+
nextAction: "Issue a fresh scoped proxy handle for this assignment.",
|
|
1366
|
+
severity: "danger"
|
|
1367
|
+
},
|
|
1368
|
+
treedx_proxy_operation_denied: {
|
|
1369
|
+
title: "TreeDX operation denied",
|
|
1370
|
+
message: "The requested TreeDX operation is outside the handle scope.",
|
|
1371
|
+
owner: "project",
|
|
1372
|
+
nextAction: "Update the agent capability requirements or issue a handle with the required operation.",
|
|
1373
|
+
severity: "danger"
|
|
1374
|
+
},
|
|
1375
|
+
treedx_proxy_path_denied: {
|
|
1376
|
+
title: "TreeDX path denied",
|
|
1377
|
+
message: "The requested content path is outside the TreeDX handle path scope.",
|
|
1378
|
+
owner: "project",
|
|
1379
|
+
nextAction: "Constrain the work to allowed paths or update the approved path scope.",
|
|
1380
|
+
severity: "danger"
|
|
1381
|
+
},
|
|
1382
|
+
local_content_write_blocked: {
|
|
1383
|
+
title: "Local content write blocked",
|
|
1384
|
+
message: "Content writes must go through TreeDX when assignment workspace handles are expected.",
|
|
1385
|
+
owner: "project",
|
|
1386
|
+
nextAction: "Use TreeDX workspace write/commit tools for content, and reserve local files for code and artifacts.",
|
|
1387
|
+
severity: "danger"
|
|
1388
|
+
},
|
|
1389
|
+
treedx_workspace_required: {
|
|
1390
|
+
title: "TreeDX workspace required",
|
|
1391
|
+
message: "The assignment needs a TreeDX workspace before content mutation can begin.",
|
|
1392
|
+
owner: "system",
|
|
1393
|
+
nextAction: "Create or attach a TreeDX workspace and retry assignment synthesis.",
|
|
1394
|
+
severity: "danger"
|
|
1395
|
+
},
|
|
1396
|
+
execution_provider_prepare_rejected: {
|
|
1397
|
+
title: "Execution provider rejected preparation",
|
|
1398
|
+
message: "The selected execution provider could not prepare the work package.",
|
|
1399
|
+
owner: "provider_operator",
|
|
1400
|
+
nextAction: "Inspect provider readiness, auth, sandbox, and adapter diagnostics.",
|
|
1401
|
+
severity: "danger"
|
|
1402
|
+
},
|
|
1403
|
+
assignment_output_invalid: {
|
|
1404
|
+
title: "Assignment output invalid",
|
|
1405
|
+
message: "The agent completed with output that did not satisfy the assignment output contract.",
|
|
1406
|
+
owner: "project",
|
|
1407
|
+
nextAction: "Tighten the agent output contract or fix the handler/provider output mapping.",
|
|
1408
|
+
severity: "danger"
|
|
1409
|
+
}
|
|
1410
|
+
};
|
|
1411
|
+
function runtimeReasonDetails(code) {
|
|
1412
|
+
return CAPACITY_RUNTIME_REASON_DETAILS[code] ?? {
|
|
1413
|
+
title: code.split(/[_-]+/u).filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1)).join(" ") || "Runtime blocker",
|
|
1414
|
+
message: "The assignment reported a runtime blocker that does not yet have a specialized explanation.",
|
|
1415
|
+
owner: "system",
|
|
1416
|
+
nextAction: "Inspect the assignment explanation gates and provider runner logs.",
|
|
1417
|
+
severity: "warning"
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
function assignmentById(assignments) {
|
|
1421
|
+
return new Map(assignments.map((assignment) => [assignment.id, assignment]));
|
|
1422
|
+
}
|
|
1423
|
+
function summarizeCapacityRuntimeDiagnostics(input) {
|
|
1424
|
+
const assignments = input.assignments ?? [];
|
|
1425
|
+
const explanations = input.explanations ?? [];
|
|
1426
|
+
const byAssignment = assignmentById(assignments);
|
|
1427
|
+
const diagnostics = [];
|
|
1428
|
+
const addBlocker = (code, assignment, evidence = []) => {
|
|
1429
|
+
const details = runtimeReasonDetails(code);
|
|
1430
|
+
diagnostics.push({
|
|
1431
|
+
code,
|
|
1432
|
+
severity: details.severity,
|
|
1433
|
+
title: details.title,
|
|
1434
|
+
message: details.message,
|
|
1435
|
+
owner: details.owner,
|
|
1436
|
+
assignmentId: assignment?.id ?? null,
|
|
1437
|
+
projectId: assignment?.projectId ?? input.projectId,
|
|
1438
|
+
providerId: assignment?.capacityProviderId ?? null,
|
|
1439
|
+
nextAction: details.nextAction,
|
|
1440
|
+
evidence
|
|
1441
|
+
});
|
|
1442
|
+
};
|
|
1443
|
+
for (const explanation of explanations) {
|
|
1444
|
+
const assignment = byAssignment.get(explanation.assignmentId) ?? null;
|
|
1445
|
+
for (const reason of explanation.reasons ?? []) {
|
|
1446
|
+
addBlocker(reason, assignment, [
|
|
1447
|
+
{ label: "eligible", value: String(explanation.eligible) },
|
|
1448
|
+
{ label: "source", value: String(explanation.source ?? "unknown") }
|
|
1449
|
+
]);
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
for (const assignment of assignments) {
|
|
1453
|
+
if (assignment.lifecycleCode) {
|
|
1454
|
+
addBlocker(String(assignment.lifecycleCode), assignment, [
|
|
1455
|
+
{ label: "status", value: String(assignment.status) },
|
|
1456
|
+
{ label: "lease", value: String(assignment.leaseState) }
|
|
1457
|
+
]);
|
|
1458
|
+
}
|
|
1459
|
+
if (assignment.status === "failed") {
|
|
1460
|
+
addBlocker("assignment_failed", assignment, [
|
|
1461
|
+
{ label: "reason", value: String(assignment.lifecycleReason ?? "not recorded") }
|
|
1462
|
+
]);
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
const auditAssignmentIds = new Set((input.treeDxProxyAudit ?? []).map((audit) => String(audit.assignmentId ?? "")).filter(Boolean));
|
|
1466
|
+
for (const assignment of assignments) {
|
|
1467
|
+
const handle = record(assignment.treedxProxyHandle);
|
|
1468
|
+
if (handle.id && !auditAssignmentIds.has(assignment.id) && assignment.status !== "pending") {
|
|
1469
|
+
addBlocker("treedx_proxy_audit_missing", assignment, [
|
|
1470
|
+
{ label: "handle", value: String(handle.id) },
|
|
1471
|
+
{ label: "status", value: String(assignment.status) }
|
|
1472
|
+
]);
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
const terminalPhases = /* @__PURE__ */ new Set(["task_completed_actual_settlement", "reservation_released", "task_failed_refund"]);
|
|
1476
|
+
const assignmentLedger = /* @__PURE__ */ new Map();
|
|
1477
|
+
for (const entry of input.ledgerEntries ?? []) {
|
|
1478
|
+
const assignmentId = entry.assignmentId ?? null;
|
|
1479
|
+
if (!assignmentId) continue;
|
|
1480
|
+
assignmentLedger.set(assignmentId, [...assignmentLedger.get(assignmentId) ?? [], entry]);
|
|
1481
|
+
}
|
|
1482
|
+
for (const assignment of assignments) {
|
|
1483
|
+
if (["completed", "failed", "returned"].includes(String(assignment.status))) {
|
|
1484
|
+
const hasTerminal = (assignmentLedger.get(assignment.id) ?? []).some((entry) => terminalPhases.has(String(entry.phase)));
|
|
1485
|
+
if (!hasTerminal && assignment.reservationId) {
|
|
1486
|
+
addBlocker("settlement_missing", assignment, [
|
|
1487
|
+
{ label: "reservation", value: String(assignment.reservationId) },
|
|
1488
|
+
{ label: "status", value: String(assignment.status) }
|
|
1489
|
+
]);
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
const uniqueDiagnostics = Array.from(new Map(diagnostics.map((diagnostic) => [
|
|
1494
|
+
`${diagnostic.assignmentId ?? "global"}:${diagnostic.code}`,
|
|
1495
|
+
diagnostic
|
|
1496
|
+
])).values());
|
|
1497
|
+
return {
|
|
1498
|
+
projectId: input.projectId,
|
|
1499
|
+
teamId: input.teamId,
|
|
1500
|
+
generatedAt: input.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
1501
|
+
assignments,
|
|
1502
|
+
explanations,
|
|
1503
|
+
modeRuns: input.modeRuns ?? [],
|
|
1504
|
+
treeDxProxyAudit: input.treeDxProxyAudit ?? [],
|
|
1505
|
+
ledgerEntries: input.ledgerEntries ?? [],
|
|
1506
|
+
fallbackOutputs: input.fallbackOutputs ?? [],
|
|
1507
|
+
diagnostics: uniqueDiagnostics
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
1510
|
+
function validateCapacitySettlementInvariant(input) {
|
|
1511
|
+
const violations = [];
|
|
1512
|
+
const assignmentEntries = input.ledgerEntries.filter((entry) => !entry.assignmentId || entry.assignmentId === input.assignment.id);
|
|
1513
|
+
const byPhase = /* @__PURE__ */ new Map();
|
|
1514
|
+
for (const entry of assignmentEntries) {
|
|
1515
|
+
byPhase.set(String(entry.phase), [...byPhase.get(String(entry.phase)) ?? [], entry]);
|
|
1516
|
+
if (Number(entry.credits ?? 0) < 0) {
|
|
1517
|
+
violations.push({ code: "negative_consumed_credits", message: `Ledger entry ${entry.id} has negative credits.`, severity: "error" });
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
const completion = byPhase.get("task_completed_actual_settlement") ?? [];
|
|
1521
|
+
if (completion.length > 1) {
|
|
1522
|
+
violations.push({ code: "duplicate_completion_settlement", message: `Assignment ${input.assignment.id} has ${completion.length} completion settlement entries.`, severity: "error" });
|
|
1523
|
+
}
|
|
1524
|
+
const releases = byPhase.get("reservation_released") ?? [];
|
|
1525
|
+
if (input.reservation && releases.length) {
|
|
1526
|
+
const consumed = Math.max(...completion.map((entry) => Number(entry.credits ?? 0)), Number(input.reservation.consumedCredits ?? 0), 0);
|
|
1527
|
+
const reserved = Number(input.reservation.reservedCredits ?? 0);
|
|
1528
|
+
const released = releases.reduce((sum, entry) => sum + Number(entry.credits ?? 0), 0);
|
|
1529
|
+
if (released > Math.max(0, reserved - consumed) + 1e-6) {
|
|
1530
|
+
violations.push({ code: "reservation_release_exceeds_unused", message: `Released ${released} credits exceeds unused reservation ${Math.max(0, reserved - consumed)}.`, severity: "error" });
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
const refunds = byPhase.get("task_failed_refund") ?? [];
|
|
1534
|
+
if (input.reservation && refunds.reduce((sum, entry) => sum + Number(entry.credits ?? 0), 0) > Number(input.reservation.reservedCredits ?? 0) + 1e-6) {
|
|
1535
|
+
violations.push({ code: "refund_exceeds_reserved", message: "Failure refund exceeds reserved credits.", severity: "error" });
|
|
1536
|
+
}
|
|
1537
|
+
if (input.assignment.status === "completed" && completion.length === 0 && input.assignment.reservationId) {
|
|
1538
|
+
violations.push({ code: "completed_assignment_missing_settlement", message: "Completed assignment with a reservation has no completion settlement.", severity: "error" });
|
|
1539
|
+
}
|
|
1540
|
+
if (input.assignment.status === "completed") {
|
|
1541
|
+
const hasModeRun = Boolean(input.assignment.lifecycleOutput?.modeRunId ?? input.assignment.lifecycleOutput?.usageActualId ?? completion.some((entry) => entry.modeRunId));
|
|
1542
|
+
if (!hasModeRun) {
|
|
1543
|
+
violations.push({ code: "completed_assignment_missing_mode_run_or_usage", message: "Completed assignment does not link a mode run or usage actual.", severity: "warning" });
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
if (input.assignment.mode === "acting" && !hasAcceptedCapacityPlanProvenance({ assignment: input.assignment })) {
|
|
1547
|
+
violations.push({ code: "acting_assignment_missing_capacity_plan_provenance", message: "Acting assignment lacks accepted, scheduled, or active capacity-plan provenance.", severity: "error" });
|
|
1548
|
+
}
|
|
1549
|
+
const hasErrors = violations.some((violation) => violation.severity === "error");
|
|
1550
|
+
return {
|
|
1551
|
+
ok: violations.length === 0,
|
|
1552
|
+
status: hasErrors ? "fail" : violations.length ? "warning" : "pass",
|
|
1553
|
+
violations
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
export {
|
|
1557
|
+
AGENT_ASSIGNMENT_WORKSPACE_ACCESS_MODES,
|
|
1558
|
+
buildAgentCapacityPlanDraft,
|
|
1559
|
+
buildExecutionProviderAssignmentExplanation,
|
|
1560
|
+
buildProviderAssignmentExplanation,
|
|
1561
|
+
compileExecutionCapabilityDemand,
|
|
1562
|
+
compileExecutionCapabilitySupply,
|
|
1563
|
+
computeDecisionScopeHash,
|
|
1564
|
+
createAgentKernelModeFallback,
|
|
1565
|
+
decorateExecutionProviderVisibility,
|
|
1566
|
+
deriveAgentCapacityEnvelopeFromAssignment,
|
|
1567
|
+
deriveAgentCapacityEnvelopeFromReservation,
|
|
1568
|
+
deriveAgentCapacityPlanFromCapacityPlan,
|
|
1569
|
+
deriveAllocationSetFromCapacityGrants,
|
|
1570
|
+
deriveDecisionExecutionInputFromAssignment,
|
|
1571
|
+
deriveModeRunUsageSettlement,
|
|
1572
|
+
deriveProviderAvailabilitySession,
|
|
1573
|
+
evaluateExecutionProviderEligibility,
|
|
1574
|
+
evaluateFallbackQuota,
|
|
1575
|
+
evaluateTreeDxProxyHandleAccess,
|
|
1576
|
+
hasAcceptedCapacityPlanProvenance,
|
|
1577
|
+
isAgentCapacityPlanAccepted,
|
|
1578
|
+
isAgentCapacityPlanStaleOrSuperseded,
|
|
1579
|
+
isAgentExecutionMode,
|
|
1580
|
+
isAgentModeAllowedForClass,
|
|
1581
|
+
isDecisionExecutionInputAccepted,
|
|
1582
|
+
isDecisionReadyForActing,
|
|
1583
|
+
isPlanningInputOpen,
|
|
1584
|
+
isProviderAssignmentCandidateEligible,
|
|
1585
|
+
isProviderAssignmentLeasable,
|
|
1586
|
+
isProviderAssignmentLeaseExpired,
|
|
1587
|
+
normalizeAgentExecutionMode,
|
|
1588
|
+
normalizeDecisionExecutionEstimate,
|
|
1589
|
+
providerAssignmentCapabilityHandlesContainSecretMaterial,
|
|
1590
|
+
redactedProviderAssignmentCapabilityHandles,
|
|
1591
|
+
selectAgentKernelModeDecision,
|
|
1592
|
+
summarizeCapacityRuntimeDiagnostics,
|
|
1593
|
+
summarizeExecutionProviderVisibility,
|
|
1594
|
+
treeDxProxyHeaders,
|
|
1595
|
+
validateAgentKernelModeExecutionInput,
|
|
1596
|
+
validateAgentKernelOutputs,
|
|
1597
|
+
validateCapacitySettlementInvariant,
|
|
1598
|
+
validateProviderAssignmentCapabilityHandles,
|
|
1599
|
+
validateTreeDxProxyHandle
|
|
1600
|
+
};
|