@kindgi/api 0.1.4-rc.5 → 0.1.5-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-binding.d.ts +13 -0
- package/dist/agent-binding.d.ts.map +1 -1
- package/dist/agent-pins.d.ts.map +1 -1
- package/dist/agent-pins.js +18 -2
- package/dist/agent-pins.js.map +1 -1
- package/dist/app.d.ts +110 -17
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +163 -35
- package/dist/app.js.map +1 -1
- package/dist/block-publish.d.ts +56 -0
- package/dist/block-publish.d.ts.map +1 -0
- package/dist/block-publish.js +132 -0
- package/dist/block-publish.js.map +1 -0
- package/dist/caller.d.ts +24 -0
- package/dist/caller.d.ts.map +1 -0
- package/dist/caller.js +47 -0
- package/dist/caller.js.map +1 -0
- package/dist/capability-binding.d.ts +11 -0
- package/dist/capability-binding.d.ts.map +1 -1
- package/dist/derive-agent-version.d.ts +7 -0
- package/dist/derive-agent-version.d.ts.map +1 -1
- package/dist/derive-agent-version.js +4 -0
- package/dist/derive-agent-version.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +73 -7
- package/dist/errors.js.map +1 -1
- package/dist/eval-case-binding.d.ts +16 -4
- package/dist/eval-case-binding.d.ts.map +1 -1
- package/dist/eval-run-binding.d.ts +31 -0
- package/dist/eval-run-binding.d.ts.map +1 -1
- package/dist/eval-run-dispatcher.d.ts +7 -0
- package/dist/eval-run-dispatcher.d.ts.map +1 -1
- package/dist/eval-run-dispatcher.js.map +1 -1
- package/dist/eval-sample.d.ts +11 -0
- package/dist/eval-sample.d.ts.map +1 -0
- package/dist/eval-sample.js +34 -0
- package/dist/eval-sample.js.map +1 -0
- package/dist/eval-suite-binding.d.ts +13 -0
- package/dist/eval-suite-binding.d.ts.map +1 -1
- package/dist/eval-suite-binding.js.map +1 -1
- package/dist/flow-binding.d.ts +18 -0
- package/dist/flow-binding.d.ts.map +1 -1
- package/dist/flow-pins.d.ts.map +1 -1
- package/dist/flow-pins.js +18 -2
- package/dist/flow-pins.js.map +1 -1
- package/dist/gate.d.ts.map +1 -1
- package/dist/gate.js +25 -0
- package/dist/gate.js.map +1 -1
- package/dist/handler-binding.d.ts +32 -0
- package/dist/handler-binding.d.ts.map +1 -1
- package/dist/hitl-binding.d.ts +23 -0
- package/dist/hitl-binding.d.ts.map +1 -1
- package/dist/identity-directory-binding.d.ts +82 -4
- package/dist/identity-directory-binding.d.ts.map +1 -1
- package/dist/identity-provider-binding.d.ts +197 -17
- package/dist/identity-provider-binding.d.ts.map +1 -1
- package/dist/improvement-pass-binding.d.ts +196 -0
- package/dist/improvement-pass-binding.d.ts.map +1 -0
- package/dist/improvement-pass-binding.js +9 -0
- package/dist/improvement-pass-binding.js.map +1 -0
- package/dist/index.d.ts +35 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/dist/judged-dispatcher.d.ts +25 -4
- package/dist/judged-dispatcher.d.ts.map +1 -1
- package/dist/judged-dispatcher.js +43 -6
- package/dist/judged-dispatcher.js.map +1 -1
- package/dist/judgment-binding.d.ts +25 -1
- package/dist/judgment-binding.d.ts.map +1 -1
- package/dist/mcp-endpoint-binding.d.ts +7 -0
- package/dist/mcp-endpoint-binding.d.ts.map +1 -1
- package/dist/memory-binding.d.ts +124 -17
- package/dist/memory-binding.d.ts.map +1 -1
- package/dist/memory-erasure-binding.d.ts +145 -0
- package/dist/memory-erasure-binding.d.ts.map +1 -0
- package/dist/memory-erasure-binding.js +4 -0
- package/dist/memory-erasure-binding.js.map +1 -0
- package/dist/middleware/auth.d.ts +66 -13
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +150 -50
- package/dist/middleware/auth.js.map +1 -1
- package/dist/middleware/authorize.d.ts +6 -0
- package/dist/middleware/authorize.d.ts.map +1 -1
- package/dist/middleware/authorize.js +113 -4
- package/dist/middleware/authorize.js.map +1 -1
- package/dist/middleware/error-mapper.d.ts +5 -0
- package/dist/middleware/error-mapper.d.ts.map +1 -1
- package/dist/middleware/error-mapper.js +14 -1
- package/dist/middleware/error-mapper.js.map +1 -1
- package/dist/middleware/idempotency.d.ts +70 -5
- package/dist/middleware/idempotency.d.ts.map +1 -1
- package/dist/middleware/idempotency.js +174 -41
- package/dist/middleware/idempotency.js.map +1 -1
- package/dist/middleware/key-project.d.ts +17 -0
- package/dist/middleware/key-project.d.ts.map +1 -0
- package/dist/middleware/key-project.js +75 -0
- package/dist/middleware/key-project.js.map +1 -0
- package/dist/middleware/principal.d.ts.map +1 -1
- package/dist/middleware/principal.js +9 -5
- package/dist/middleware/principal.js.map +1 -1
- package/dist/middleware/project-ref.d.ts.map +1 -1
- package/dist/middleware/project-ref.js +3 -0
- package/dist/middleware/project-ref.js.map +1 -1
- package/dist/openapi/generate.d.ts.map +1 -1
- package/dist/openapi/generate.js +5 -2
- package/dist/openapi/generate.js.map +1 -1
- package/dist/openapi/operations.d.ts.map +1 -1
- package/dist/openapi/operations.js +918 -139
- package/dist/openapi/operations.js.map +1 -1
- package/dist/openapi/schemas.d.ts +92 -13
- package/dist/openapi/schemas.d.ts.map +1 -1
- package/dist/openapi/schemas.js +2122 -370
- package/dist/openapi/schemas.js.map +1 -1
- package/dist/person-grants-binding.d.ts +77 -0
- package/dist/person-grants-binding.d.ts.map +1 -0
- package/dist/person-grants-binding.js +4 -0
- package/dist/person-grants-binding.js.map +1 -0
- package/dist/proposal-service.d.ts +121 -0
- package/dist/proposal-service.d.ts.map +1 -0
- package/dist/proposal-service.js +447 -0
- package/dist/proposal-service.js.map +1 -0
- package/dist/proposal-status.d.ts +60 -0
- package/dist/proposal-status.d.ts.map +1 -0
- package/dist/proposal-status.js +109 -0
- package/dist/proposal-status.js.map +1 -0
- package/dist/provider-binding.d.ts +10 -0
- package/dist/provider-binding.d.ts.map +1 -1
- package/dist/publish-refused.d.ts +18 -3
- package/dist/publish-refused.d.ts.map +1 -1
- package/dist/publish-refused.js +18 -3
- package/dist/publish-refused.js.map +1 -1
- package/dist/routes/adapters.d.ts +2 -1
- package/dist/routes/adapters.d.ts.map +1 -1
- package/dist/routes/adapters.js +3 -1
- package/dist/routes/adapters.js.map +1 -1
- package/dist/routes/agent-releases.d.ts +55 -3
- package/dist/routes/agent-releases.d.ts.map +1 -1
- package/dist/routes/agent-releases.js +85 -55
- package/dist/routes/agent-releases.js.map +1 -1
- package/dist/routes/agents.d.ts +12 -1
- package/dist/routes/agents.d.ts.map +1 -1
- package/dist/routes/agents.js +100 -2
- package/dist/routes/agents.js.map +1 -1
- package/dist/routes/approvals.d.ts +15 -3
- package/dist/routes/approvals.d.ts.map +1 -1
- package/dist/routes/approvals.js +133 -122
- package/dist/routes/approvals.js.map +1 -1
- package/dist/routes/artifacts.d.ts +25 -3
- package/dist/routes/artifacts.d.ts.map +1 -1
- package/dist/routes/artifacts.js +116 -7
- package/dist/routes/artifacts.js.map +1 -1
- package/dist/routes/auth.d.ts +25 -1
- package/dist/routes/auth.d.ts.map +1 -1
- package/dist/routes/auth.js +655 -281
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/blocks.d.ts.map +1 -1
- package/dist/routes/blocks.js +2 -42
- package/dist/routes/blocks.js.map +1 -1
- package/dist/routes/capabilities.d.ts +2 -1
- package/dist/routes/capabilities.d.ts.map +1 -1
- package/dist/routes/capabilities.js +6 -1
- package/dist/routes/capabilities.js.map +1 -1
- package/dist/routes/compliance.d.ts +9 -10
- package/dist/routes/compliance.d.ts.map +1 -1
- package/dist/routes/compliance.js +42 -61
- package/dist/routes/compliance.js.map +1 -1
- package/dist/routes/conversations.d.ts +8 -1
- package/dist/routes/conversations.d.ts.map +1 -1
- package/dist/routes/conversations.js +87 -8
- package/dist/routes/conversations.js.map +1 -1
- package/dist/routes/cost.d.ts +7 -1
- package/dist/routes/cost.d.ts.map +1 -1
- package/dist/routes/cost.js +22 -3
- package/dist/routes/cost.js.map +1 -1
- package/dist/routes/denied.d.ts +11 -0
- package/dist/routes/denied.d.ts.map +1 -0
- package/dist/routes/denied.js +17 -0
- package/dist/routes/denied.js.map +1 -0
- package/dist/routes/deployments.d.ts +2 -1
- package/dist/routes/deployments.d.ts.map +1 -1
- package/dist/routes/deployments.js +44 -12
- package/dist/routes/deployments.js.map +1 -1
- package/dist/routes/eval-comparison.d.ts.map +1 -1
- package/dist/routes/eval-comparison.js +69 -1
- package/dist/routes/eval-comparison.js.map +1 -1
- package/dist/routes/eval-overrides.d.ts +18 -0
- package/dist/routes/eval-overrides.d.ts.map +1 -0
- package/dist/routes/eval-overrides.js +81 -0
- package/dist/routes/eval-overrides.js.map +1 -0
- package/dist/routes/eval-runs.d.ts +10 -1
- package/dist/routes/eval-runs.d.ts.map +1 -1
- package/dist/routes/eval-runs.js +63 -7
- package/dist/routes/eval-runs.js.map +1 -1
- package/dist/routes/eval-suites.d.ts.map +1 -1
- package/dist/routes/eval-suites.js +20 -1
- package/dist/routes/eval-suites.js.map +1 -1
- package/dist/routes/event-triggers.d.ts +5 -1
- package/dist/routes/event-triggers.d.ts.map +1 -1
- package/dist/routes/event-triggers.js +26 -3
- package/dist/routes/event-triggers.js.map +1 -1
- package/dist/routes/export-signing-keys.d.ts +12 -0
- package/dist/routes/export-signing-keys.d.ts.map +1 -0
- package/dist/routes/export-signing-keys.js +24 -0
- package/dist/routes/export-signing-keys.js.map +1 -0
- package/dist/routes/flows.d.ts.map +1 -1
- package/dist/routes/flows.js +9 -1
- package/dist/routes/flows.js.map +1 -1
- package/dist/routes/gate-policies.d.ts +4 -1
- package/dist/routes/gate-policies.d.ts.map +1 -1
- package/dist/routes/gate-policies.js +29 -5
- package/dist/routes/gate-policies.js.map +1 -1
- package/dist/routes/guardrails.d.ts +16 -14
- package/dist/routes/guardrails.d.ts.map +1 -1
- package/dist/routes/guardrails.js +39 -3
- package/dist/routes/guardrails.js.map +1 -1
- package/dist/routes/identity.d.ts +20 -4
- package/dist/routes/identity.d.ts.map +1 -1
- package/dist/routes/identity.js +246 -6
- package/dist/routes/identity.js.map +1 -1
- package/dist/routes/improvement-passes.d.ts +52 -0
- package/dist/routes/improvement-passes.d.ts.map +1 -0
- package/dist/routes/improvement-passes.js +449 -0
- package/dist/routes/improvement-passes.js.map +1 -0
- package/dist/routes/judged-suites.d.ts +50 -0
- package/dist/routes/judged-suites.d.ts.map +1 -1
- package/dist/routes/judged-suites.js +66 -27
- package/dist/routes/judged-suites.js.map +1 -1
- package/dist/routes/judgment-context.d.ts +18 -2
- package/dist/routes/judgment-context.d.ts.map +1 -1
- package/dist/routes/judgment-context.js +63 -5
- package/dist/routes/judgment-context.js.map +1 -1
- package/dist/routes/judgment-flow-context.d.ts.map +1 -1
- package/dist/routes/judgment-flow-context.js +14 -2
- package/dist/routes/judgment-flow-context.js.map +1 -1
- package/dist/routes/judgments.d.ts.map +1 -1
- package/dist/routes/judgments.js +8 -1
- package/dist/routes/judgments.js.map +1 -1
- package/dist/routes/mcp.d.ts.map +1 -1
- package/dist/routes/mcp.js +26 -1
- package/dist/routes/mcp.js.map +1 -1
- package/dist/routes/memory-access.d.ts +43 -0
- package/dist/routes/memory-access.d.ts.map +1 -0
- package/dist/routes/memory-access.js +129 -0
- package/dist/routes/memory-access.js.map +1 -0
- package/dist/routes/memory-erasures.d.ts +25 -0
- package/dist/routes/memory-erasures.d.ts.map +1 -0
- package/dist/routes/memory-erasures.js +214 -0
- package/dist/routes/memory-erasures.js.map +1 -0
- package/dist/routes/memory-parse.d.ts +38 -0
- package/dist/routes/memory-parse.d.ts.map +1 -0
- package/dist/routes/memory-parse.js +199 -0
- package/dist/routes/memory-parse.js.map +1 -0
- package/dist/routes/memory.d.ts +9 -4
- package/dist/routes/memory.d.ts.map +1 -1
- package/dist/routes/memory.js +226 -234
- package/dist/routes/memory.js.map +1 -1
- package/dist/routes/observations.d.ts +4 -1
- package/dist/routes/observations.d.ts.map +1 -1
- package/dist/routes/observations.js +8 -2
- package/dist/routes/observations.js.map +1 -1
- package/dist/routes/pagination.d.ts +7 -0
- package/dist/routes/pagination.d.ts.map +1 -1
- package/dist/routes/pagination.js +10 -0
- package/dist/routes/pagination.js.map +1 -1
- package/dist/routes/policies.d.ts +2 -1
- package/dist/routes/policies.d.ts.map +1 -1
- package/dist/routes/policies.js +3 -1
- package/dist/routes/policies.js.map +1 -1
- package/dist/routes/project-mismatch.d.ts +22 -0
- package/dist/routes/project-mismatch.d.ts.map +1 -0
- package/dist/routes/project-mismatch.js +34 -0
- package/dist/routes/project-mismatch.js.map +1 -0
- package/dist/routes/projects.d.ts +15 -4
- package/dist/routes/projects.d.ts.map +1 -1
- package/dist/routes/projects.js +100 -9
- package/dist/routes/projects.js.map +1 -1
- package/dist/routes/proposals.d.ts +25 -17
- package/dist/routes/proposals.d.ts.map +1 -1
- package/dist/routes/proposals.js +340 -507
- package/dist/routes/proposals.js.map +1 -1
- package/dist/routes/provenance.d.ts +17 -3
- package/dist/routes/provenance.d.ts.map +1 -1
- package/dist/routes/provenance.js +79 -103
- package/dist/routes/provenance.js.map +1 -1
- package/dist/routes/providers.d.ts +17 -1
- package/dist/routes/providers.d.ts.map +1 -1
- package/dist/routes/providers.js +188 -8
- package/dist/routes/providers.js.map +1 -1
- package/dist/routes/public-run-tokens.d.ts.map +1 -1
- package/dist/routes/public-run-tokens.js +3 -0
- package/dist/routes/public-run-tokens.js.map +1 -1
- package/dist/routes/runs.d.ts +10 -0
- package/dist/routes/runs.d.ts.map +1 -1
- package/dist/routes/runs.js +92 -42
- package/dist/routes/runs.js.map +1 -1
- package/dist/routes/schedules.d.ts +21 -13
- package/dist/routes/schedules.d.ts.map +1 -1
- package/dist/routes/schedules.js +416 -55
- package/dist/routes/schedules.js.map +1 -1
- package/dist/routes/segments.d.ts +2 -0
- package/dist/routes/segments.d.ts.map +1 -1
- package/dist/routes/segments.js +5 -0
- package/dist/routes/segments.js.map +1 -1
- package/dist/routes/service-accounts.d.ts +17 -0
- package/dist/routes/service-accounts.d.ts.map +1 -0
- package/dist/routes/service-accounts.js +212 -0
- package/dist/routes/service-accounts.js.map +1 -0
- package/dist/routes/sign-in-options.d.ts +50 -0
- package/dist/routes/sign-in-options.d.ts.map +1 -0
- package/dist/routes/sign-in-options.js +98 -0
- package/dist/routes/sign-in-options.js.map +1 -0
- package/dist/routes/signing-keys.d.ts +2 -1
- package/dist/routes/signing-keys.d.ts.map +1 -1
- package/dist/routes/signing-keys.js +3 -1
- package/dist/routes/signing-keys.js.map +1 -1
- package/dist/routes/tenant-access.d.ts +13 -0
- package/dist/routes/tenant-access.d.ts.map +1 -0
- package/dist/routes/tenant-access.js +26 -0
- package/dist/routes/tenant-access.js.map +1 -0
- package/dist/routes/tenant.d.ts.map +1 -1
- package/dist/routes/tenant.js +4 -0
- package/dist/routes/tenant.js.map +1 -1
- package/dist/routes/token-sign-in.d.ts +36 -0
- package/dist/routes/token-sign-in.d.ts.map +1 -0
- package/dist/routes/token-sign-in.js +101 -0
- package/dist/routes/token-sign-in.js.map +1 -0
- package/dist/routes/tokens.d.ts +15 -5
- package/dist/routes/tokens.d.ts.map +1 -1
- package/dist/routes/tokens.js +162 -43
- package/dist/routes/tokens.js.map +1 -1
- package/dist/routes/tools.d.ts.map +1 -1
- package/dist/routes/tools.js +9 -1
- package/dist/routes/tools.js.map +1 -1
- package/dist/routes/trigger-access.d.ts +29 -0
- package/dist/routes/trigger-access.d.ts.map +1 -0
- package/dist/routes/trigger-access.js +33 -0
- package/dist/routes/trigger-access.js.map +1 -0
- package/dist/routes/webhook-endpoints.d.ts +2 -1
- package/dist/routes/webhook-endpoints.d.ts.map +1 -1
- package/dist/routes/webhook-endpoints.js +11 -3
- package/dist/routes/webhook-endpoints.js.map +1 -1
- package/dist/routes/webhooks.d.ts +5 -1
- package/dist/routes/webhooks.d.ts.map +1 -1
- package/dist/routes/webhooks.js +25 -2
- package/dist/routes/webhooks.js.map +1 -1
- package/dist/run-failure.d.ts +21 -0
- package/dist/run-failure.d.ts.map +1 -0
- package/dist/run-failure.js +29 -0
- package/dist/run-failure.js.map +1 -0
- package/dist/service-account-binding.d.ts +108 -0
- package/dist/service-account-binding.d.ts.map +1 -0
- package/dist/service-account-binding.js +4 -0
- package/dist/service-account-binding.js.map +1 -0
- package/dist/session-store-binding.d.ts +61 -13
- package/dist/session-store-binding.d.ts.map +1 -1
- package/dist/signed-export.d.ts +89 -0
- package/dist/signed-export.d.ts.map +1 -0
- package/dist/signed-export.js +149 -0
- package/dist/signed-export.js.map +1 -0
- package/dist/supervisor-binding.d.ts +186 -413
- package/dist/supervisor-binding.d.ts.map +1 -1
- package/dist/testing/app-bindings.d.ts +33 -0
- package/dist/testing/app-bindings.d.ts.map +1 -0
- package/dist/testing/app-bindings.js +112 -0
- package/dist/testing/app-bindings.js.map +1 -0
- package/dist/testing/index.d.ts +11 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +13 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/stub-binding.d.ts +34 -0
- package/dist/testing/stub-binding.d.ts.map +1 -0
- package/dist/testing/stub-binding.js +42 -0
- package/dist/testing/stub-binding.js.map +1 -0
- package/dist/testing/trigger-registry.d.ts +21 -0
- package/dist/testing/trigger-registry.d.ts.map +1 -0
- package/dist/testing/trigger-registry.js +206 -0
- package/dist/testing/trigger-registry.js.map +1 -0
- package/dist/token-admin.d.ts +46 -8
- package/dist/token-admin.d.ts.map +1 -1
- package/dist/token-admin.js.map +1 -1
- package/dist/tool-binding.d.ts +13 -0
- package/dist/tool-binding.d.ts.map +1 -1
- package/dist/trigger-binding.d.ts +2 -2
- package/dist/trigger-binding.d.ts.map +1 -1
- package/dist/trigger-binding.js +1 -1
- package/dist/trigger-binding.js.map +1 -1
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/webhook-endpoint-binding.d.ts +19 -3
- package/dist/webhook-endpoint-binding.d.ts.map +1 -1
- package/dist/webhook-endpoint-binding.js +1 -1
- package/dist/webhook-endpoint-binding.js.map +1 -1
- package/openapi.json +15286 -8653
- package/package.json +26 -22
- package/src/agent-binding.ts +14 -0
- package/src/agent-pins.ts +18 -2
- package/src/app.ts +366 -80
- package/src/block-publish.ts +181 -0
- package/src/caller.ts +62 -0
- package/src/capability-binding.ts +12 -0
- package/src/derive-agent-version.ts +9 -1
- package/src/errors.ts +73 -7
- package/src/eval-case-binding.ts +17 -1
- package/src/eval-run-binding.ts +31 -0
- package/src/eval-run-dispatcher.ts +7 -0
- package/src/eval-sample.ts +42 -0
- package/src/eval-suite-binding.ts +14 -0
- package/src/flow-binding.ts +19 -0
- package/src/flow-pins.ts +18 -2
- package/src/gate.ts +26 -0
- package/src/handler-binding.ts +36 -1
- package/src/hitl-binding.ts +24 -0
- package/src/identity-directory-binding.ts +85 -4
- package/src/identity-provider-binding.ts +216 -21
- package/src/improvement-pass-binding.ts +193 -0
- package/src/index.ts +153 -31
- package/src/judged-dispatcher.ts +78 -11
- package/src/judgment-binding.ts +25 -1
- package/src/mcp-endpoint-binding.ts +7 -0
- package/src/memory-binding.ts +121 -17
- package/src/memory-erasure-binding.ts +156 -0
- package/src/middleware/auth.ts +228 -62
- package/src/middleware/authorize.ts +139 -3
- package/src/middleware/error-mapper.ts +15 -1
- package/src/middleware/idempotency.ts +230 -43
- package/src/middleware/key-project.ts +86 -0
- package/src/middleware/principal.ts +9 -5
- package/src/middleware/project-ref.ts +3 -0
- package/src/openapi/generate.ts +11 -2
- package/src/openapi/operations.ts +1059 -142
- package/src/openapi/schemas.ts +2438 -485
- package/src/person-grants-binding.ts +76 -0
- package/src/proposal-service.ts +640 -0
- package/src/proposal-status.ts +176 -0
- package/src/provider-binding.ts +10 -0
- package/src/publish-refused.ts +38 -9
- package/src/routes/adapters.ts +4 -0
- package/src/routes/agent-releases.ts +121 -69
- package/src/routes/agents.ts +128 -1
- package/src/routes/approvals.ts +164 -154
- package/src/routes/artifacts.ts +181 -11
- package/src/routes/auth.ts +804 -336
- package/src/routes/blocks.ts +2 -53
- package/src/routes/capabilities.ts +10 -1
- package/src/routes/compliance.ts +60 -99
- package/src/routes/conversations.ts +101 -7
- package/src/routes/cost.ts +32 -3
- package/src/routes/denied.ts +28 -0
- package/src/routes/deployments.ts +62 -16
- package/src/routes/eval-comparison.ts +81 -2
- package/src/routes/eval-overrides.ts +108 -0
- package/src/routes/eval-runs.ts +86 -6
- package/src/routes/eval-suites.ts +25 -1
- package/src/routes/event-triggers.ts +27 -3
- package/src/routes/export-signing-keys.ts +31 -0
- package/src/routes/flows.ts +12 -1
- package/src/routes/gate-policies.ts +37 -6
- package/src/routes/guardrails.ts +61 -2
- package/src/routes/identity.ts +328 -10
- package/src/routes/improvement-passes.ts +541 -0
- package/src/routes/judged-suites.ts +113 -43
- package/src/routes/judgment-context.ts +76 -5
- package/src/routes/judgment-flow-context.ts +16 -2
- package/src/routes/judgments.ts +17 -3
- package/src/routes/mcp.ts +29 -1
- package/src/routes/memory-access.ts +179 -0
- package/src/routes/memory-erasures.ts +240 -0
- package/src/routes/memory-parse.ts +250 -0
- package/src/routes/memory.ts +244 -286
- package/src/routes/observations.ts +15 -2
- package/src/routes/pagination.ts +12 -0
- package/src/routes/policies.ts +7 -1
- package/src/routes/project-mismatch.ts +56 -0
- package/src/routes/projects.ts +107 -13
- package/src/routes/proposals.ts +370 -611
- package/src/routes/provenance.ts +99 -133
- package/src/routes/providers.ts +223 -8
- package/src/routes/public-run-tokens.ts +3 -0
- package/src/routes/runs.ts +119 -39
- package/src/routes/schedules.ts +472 -60
- package/src/routes/segments.ts +11 -0
- package/src/routes/service-accounts.ts +262 -0
- package/src/routes/sign-in-options.ts +168 -0
- package/src/routes/signing-keys.ts +7 -1
- package/src/routes/tenant-access.ts +37 -0
- package/src/routes/tenant.ts +6 -0
- package/src/routes/token-sign-in.ts +161 -0
- package/src/routes/tokens.ts +172 -46
- package/src/routes/tools.ts +12 -1
- package/src/routes/trigger-access.ts +72 -0
- package/src/routes/webhook-endpoints.ts +15 -3
- package/src/routes/webhooks.ts +26 -2
- package/src/run-failure.ts +42 -0
- package/src/service-account-binding.ts +113 -0
- package/src/session-store-binding.ts +65 -13
- package/src/signed-export.ts +250 -0
- package/src/supervisor-binding.ts +169 -409
- package/src/testing/app-bindings.ts +142 -0
- package/src/testing/index.ts +21 -0
- package/src/testing/stub-binding.ts +53 -0
- package/src/testing/trigger-registry.ts +259 -0
- package/src/token-admin.ts +40 -8
- package/src/tool-binding.ts +14 -0
- package/src/trigger-binding.ts +8 -1
- package/src/types.ts +16 -0
- package/src/webhook-endpoint-binding.ts +18 -3
package/src/routes/proposals.ts
CHANGED
|
@@ -3,157 +3,159 @@
|
|
|
3
3
|
|
|
4
4
|
import { type Context, Hono } from 'hono';
|
|
5
5
|
|
|
6
|
-
import type {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
FixProposalId,
|
|
10
|
-
SupervisorId,
|
|
11
|
-
TenantId,
|
|
12
|
-
Timestamp,
|
|
13
|
-
} from '@kindgi/types';
|
|
6
|
+
import type { AgentId } from '@kindgi/agents';
|
|
7
|
+
import { type Action, ref } from '@kindgi/authz';
|
|
8
|
+
import type { Cursor, FixProposalId, TenantId } from '@kindgi/types';
|
|
14
9
|
|
|
15
10
|
import { statusFor, toWireError } from '../errors.js';
|
|
11
|
+
import type { EvalComparison } from '../eval-run-binding.js';
|
|
12
|
+
import type { Authorizer } from '../middleware/authorize.js';
|
|
13
|
+
import {
|
|
14
|
+
type ProposalFacts,
|
|
15
|
+
type ProposalService,
|
|
16
|
+
type ProposalServiceDeps,
|
|
17
|
+
type ProposalServiceError,
|
|
18
|
+
createProposalService,
|
|
19
|
+
proposalNotFound,
|
|
20
|
+
} from '../proposal-service.js';
|
|
21
|
+
import {
|
|
22
|
+
FIX_PROPOSAL_STATUSES,
|
|
23
|
+
type FixProposalStatus,
|
|
24
|
+
type ProposalEvaluationOutcome,
|
|
25
|
+
} from '../proposal-status.js';
|
|
16
26
|
import type {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
PassCriterion,
|
|
20
|
-
PatternRef,
|
|
27
|
+
ProposalObjective,
|
|
28
|
+
ProposalTier,
|
|
21
29
|
ProposedChange,
|
|
22
|
-
|
|
30
|
+
StoredProposal,
|
|
23
31
|
} from '../supervisor-binding.js';
|
|
24
32
|
import type { AppEnv } from '../types.js';
|
|
33
|
+
import { actorOf, promotionResponse, scopeFromQuery } from './agent-releases.js';
|
|
34
|
+
import { parseComparison } from './eval-comparison.js';
|
|
35
|
+
import { liveScopeToWire, parseLiveScopeBody } from './live-scope-wire.js';
|
|
25
36
|
import { clampLimit } from './pagination.js';
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
'rejected',
|
|
36
|
-
'applied',
|
|
37
|
-
'rolled-back',
|
|
38
|
-
'withdrawn',
|
|
39
|
-
]);
|
|
40
|
-
|
|
41
|
-
const PROPOSAL_TIERS: ReadonlySet<ProposedChange['tier']> = new Set([
|
|
42
|
-
'prompt',
|
|
43
|
-
'retrieval',
|
|
44
|
-
'tool-config',
|
|
45
|
-
]);
|
|
46
|
-
|
|
47
|
-
const REVIEWER_ROLES: ReadonlySet<'standard' | 'senior' | 'admin'> = new Set([
|
|
48
|
-
'standard',
|
|
49
|
-
'senior',
|
|
50
|
-
'admin',
|
|
51
|
-
]);
|
|
37
|
+
|
|
38
|
+
/** What the proposal lifecycle runs on besides its own store. */
|
|
39
|
+
export type ProposalsRouteDeps = Omit<ProposalServiceDeps, 'store'>;
|
|
40
|
+
|
|
41
|
+
const TIERS: readonly ProposalTier[] = ['settings-block', 'prompt-block'];
|
|
42
|
+
const OBJECTIVES: readonly ProposalObjective[] = ['weightedYesShare', 'weightedPrecisionAtK'];
|
|
43
|
+
const MAX_TEXT = 2000;
|
|
44
|
+
|
|
45
|
+
type Ctx = Context<AppEnv>;
|
|
52
46
|
|
|
53
47
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
48
|
+
* Improvement proposals (`/v1/proposals`): a change to one data block an
|
|
49
|
+
* agent version pins (new settings values, or a new prompt template),
|
|
50
|
+
* for one live scope, taken through the same comparison, gate and
|
|
51
|
+
* promotion as any other version (`createProposalService`).
|
|
57
52
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
53
|
+
* GET / list (statuses derived): by agent, tier, status and live scope
|
|
54
|
+
* GET /:id one
|
|
55
|
+
* POST / a hand-written proposal (a draft)
|
|
56
|
+
* POST /:id/evaluate publish the block version and derive the agent
|
|
57
|
+
* version (inert until promoted), then compare it
|
|
58
|
+
* on a test set (202)
|
|
59
|
+
* POST /:id/request a gated promotion of the candidate for the scope
|
|
60
|
+
* (201 promoted, 202 in review, 422 gate-failed)
|
|
61
|
+
* POST /:id/rollback the scope goes back to what served it before
|
|
62
|
+
* POST /:id/withdraw closed by a person
|
|
61
63
|
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* the runtime won't accept (e.g., apply on a proposal still in
|
|
67
|
-
* `dry-running`) surface as `409 proposal-invalid-state-transition`.
|
|
68
|
-
* This is a caller/state conflict per `docs/API-ROUTE-CONVENTIONS.md`
|
|
69
|
-
* §4.3 — the request was well-formed but the resource is not in
|
|
70
|
-
* the required state.
|
|
64
|
+
* Authorized on the proposal's agent: reading is `read`, drafting,
|
|
65
|
+
* evaluating and withdrawing are `publish` (they make versions), and
|
|
66
|
+
* requesting and rolling back are `promote`. A proposal the caller can't
|
|
67
|
+
* read answers 404, as one that doesn't exist.
|
|
71
68
|
*/
|
|
72
|
-
export function proposalsRouter(
|
|
69
|
+
export function proposalsRouter(
|
|
70
|
+
store: ProposalServiceDeps['store'],
|
|
71
|
+
deps: ProposalsRouteDeps,
|
|
72
|
+
authorizer?: Authorizer,
|
|
73
|
+
mount?: (r: Hono<AppEnv>, service: ProposalService) => void,
|
|
74
|
+
): Hono<AppEnv> {
|
|
73
75
|
const r = new Hono<AppEnv>();
|
|
76
|
+
const service = createProposalService({ ...deps, store });
|
|
77
|
+
// Routes mounted before `/:proposalId` (an improvement pass's `improve`).
|
|
78
|
+
mount?.(r, service);
|
|
79
|
+
|
|
80
|
+
/** `undefined` when allowed; else the 403. */
|
|
81
|
+
async function deny(c: Ctx, action: Action, agentId: string) {
|
|
82
|
+
if (authorizer === undefined) return undefined;
|
|
83
|
+
const decision = await authorizer.check(c, action, ref('agent', agentId));
|
|
84
|
+
if (decision.allowed) return undefined;
|
|
85
|
+
return fail(c, 'permission-denied', {
|
|
86
|
+
code: 'permission-denied',
|
|
87
|
+
message: `Permission denied: this needs ${action} on agent "${agentId}" (${decision.reason})`,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
74
90
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
),
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
await next();
|
|
93
|
-
return;
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// ---------- GET / (list, cursor-paginated) ----------
|
|
97
|
-
r.get('/', async (c) => {
|
|
98
|
-
const requestId = c.get('requestId');
|
|
99
|
-
const tenantId = c.get('tenantId') as TenantId;
|
|
100
|
-
const supervisorId = c.req.header('x-supervisor-id') as SupervisorId;
|
|
101
|
-
const limit = clampLimit(c.req.query('limit'));
|
|
102
|
-
|
|
103
|
-
const cursorRaw = c.req.query('cursor');
|
|
104
|
-
const statusRaw = c.req.query('status');
|
|
105
|
-
const agentIdRaw = c.req.query('agentId');
|
|
106
|
-
const tierRaw = c.req.query('tier');
|
|
107
|
-
|
|
108
|
-
let status: FixProposalStatus | undefined;
|
|
109
|
-
if (statusRaw !== undefined && statusRaw.length > 0) {
|
|
110
|
-
if (!FIX_PROPOSAL_STATUSES.has(statusRaw as FixProposalStatus)) {
|
|
111
|
-
c.status(statusFor('bad-input') as never);
|
|
112
|
-
return c.json(
|
|
113
|
-
toWireError(
|
|
114
|
-
{ code: 'bad-input', message: `Unknown \`status\` value: ${statusRaw}` },
|
|
115
|
-
requestId,
|
|
116
|
-
),
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
status = statusRaw as FixProposalStatus;
|
|
120
|
-
}
|
|
91
|
+
/** The proposal, if it exists and the caller may read it (else the 404). */
|
|
92
|
+
async function load(c: Ctx): Promise<StoredProposal | Response> {
|
|
93
|
+
const proposalId = c.req.param('proposalId') ?? '';
|
|
94
|
+
const stored = await store.getProposal({
|
|
95
|
+
tenantId: c.get('tenantId') as TenantId,
|
|
96
|
+
proposalId: proposalId as FixProposalId,
|
|
97
|
+
});
|
|
98
|
+
const hidden =
|
|
99
|
+
stored !== null &&
|
|
100
|
+
authorizer !== undefined &&
|
|
101
|
+
!(await authorizer.can(c, 'read', ref('agent', stored.agentId as unknown as string)));
|
|
102
|
+
if (stored === null || hidden) return failWith(c, proposalNotFound(proposalId));
|
|
103
|
+
return stored;
|
|
104
|
+
}
|
|
121
105
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
toWireError(
|
|
128
|
-
{ code: 'bad-input', message: `Unknown \`tier\` value: ${tierRaw}` },
|
|
129
|
-
requestId,
|
|
130
|
-
),
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
tier = tierRaw as ProposedChange['tier'];
|
|
134
|
-
}
|
|
106
|
+
async function answer(c: Ctx, stored: StoredProposal, status: 200 | 201 | 202 = 200) {
|
|
107
|
+
const f = await service.facts(c.get('tenantId') as TenantId)(stored);
|
|
108
|
+
c.status(status);
|
|
109
|
+
return c.json(serialize(stored, f));
|
|
110
|
+
}
|
|
135
111
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
112
|
+
/** Read the body and the proposal, and check the caller may `action` it. */
|
|
113
|
+
async function prepare(
|
|
114
|
+
c: Ctx,
|
|
115
|
+
action: Action,
|
|
116
|
+
): Promise<{ body: Record<string, unknown>; proposal: StoredProposal } | Response> {
|
|
117
|
+
const body = await readBody(c);
|
|
118
|
+
if (body === undefined) return badInput(c, 'Request body must be a JSON object');
|
|
119
|
+
const proposal = await load(c);
|
|
120
|
+
if (proposal instanceof Response) return proposal;
|
|
121
|
+
const denied = await deny(c, action, proposal.agentId as unknown as string);
|
|
122
|
+
return denied ?? { body, proposal };
|
|
123
|
+
}
|
|
143
124
|
|
|
144
|
-
|
|
125
|
+
// ---------- GET / ----------
|
|
126
|
+
r.get('/', async (c) => {
|
|
127
|
+
const tenantId = c.get('tenantId') as TenantId;
|
|
128
|
+
const status = c.req.query('status');
|
|
129
|
+
if (status !== undefined && !FIX_PROPOSAL_STATUSES.includes(status as FixProposalStatus)) {
|
|
130
|
+
return badInput(c, `Unknown \`status\`: ${status}`);
|
|
131
|
+
}
|
|
132
|
+
const tier = c.req.query('tier');
|
|
133
|
+
if (tier !== undefined && !TIERS.includes(tier as ProposalTier)) {
|
|
134
|
+
return badInput(c, `Unknown \`tier\`: ${tier} (one of ${TIERS.join(', ')})`);
|
|
135
|
+
}
|
|
136
|
+
const scope = scopeFromQuery((n) => c.req.query(n), c.req.queries('segment') ?? []);
|
|
137
|
+
if (scope.kind === 'err') return badInput(c, scope.message);
|
|
138
|
+
const agentId = c.req.query('agentId');
|
|
139
|
+
const cursor = c.req.query('cursor');
|
|
140
|
+
const page = await store.listProposals({
|
|
145
141
|
tenantId,
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
...(
|
|
149
|
-
...(
|
|
150
|
-
...(
|
|
151
|
-
...(tier !== undefined && { tier }),
|
|
152
|
-
...(scopeParsed.scope !== undefined && { scope: scopeParsed.scope }),
|
|
153
|
-
...(scopeParsed.inherit !== undefined && { inherit: scopeParsed.inherit }),
|
|
142
|
+
limit: clampLimit(c.req.query('limit')),
|
|
143
|
+
...(cursor !== undefined && cursor !== '' && { cursor: cursor as Cursor }),
|
|
144
|
+
...(agentId !== undefined && agentId !== '' && { agentId: agentId as AgentId }),
|
|
145
|
+
...(tier !== undefined && { tier: tier as ProposalTier }),
|
|
146
|
+
...(scope.scope !== undefined && { liveScope: scope.scope }),
|
|
154
147
|
});
|
|
148
|
+
const readable =
|
|
149
|
+
authorizer === undefined
|
|
150
|
+
? page.data
|
|
151
|
+
: await authorizer.filterByCan(c, 'read', page.data, (p) =>
|
|
152
|
+
ref('agent', p.agentId as unknown as string),
|
|
153
|
+
);
|
|
154
|
+
const read = service.facts(tenantId);
|
|
155
|
+
const rows = await Promise.all(readable.map(async (p) => serialize(p, await read(p))));
|
|
155
156
|
return c.json({
|
|
156
|
-
|
|
157
|
+
// A status filter applies after deriving, so a page can hold fewer rows than `limit`.
|
|
158
|
+
data: status === undefined ? rows : rows.filter((row) => row.status === status),
|
|
157
159
|
hasMore: page.nextCursor !== undefined,
|
|
158
160
|
...(page.nextCursor !== undefined && { nextCursor: page.nextCursor as unknown as string }),
|
|
159
161
|
});
|
|
@@ -161,559 +163,316 @@ export function proposalsRouter(binding: SupervisorBinding): Hono<AppEnv> {
|
|
|
161
163
|
|
|
162
164
|
// ---------- GET /:proposalId ----------
|
|
163
165
|
r.get('/:proposalId', async (c) => {
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
const proposalId = c.req.param('proposalId') as FixProposalId;
|
|
168
|
-
|
|
169
|
-
const proposal = await binding.getProposal({ tenantId, supervisorId, proposalId });
|
|
170
|
-
if (proposal === null) {
|
|
171
|
-
return proposalNotFound(c, requestId, proposalId);
|
|
172
|
-
}
|
|
173
|
-
return c.json(serializeProposal(proposal));
|
|
166
|
+
const stored = await load(c);
|
|
167
|
+
if (stored instanceof Response) return stored;
|
|
168
|
+
return answer(c, stored);
|
|
174
169
|
});
|
|
175
170
|
|
|
176
|
-
// ---------- POST / (
|
|
171
|
+
// ---------- POST / (a hand-written proposal) ----------
|
|
177
172
|
r.post('/', async (c) => {
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
agentId: parsed.value.agentId,
|
|
197
|
-
agentVersion: parsed.value.agentVersion,
|
|
198
|
-
tier: parsed.value.tier,
|
|
199
|
-
change: parsed.value.change,
|
|
200
|
-
patternRefs: parsed.value.patternRefs,
|
|
201
|
-
hypothesis: parsed.value.hypothesis,
|
|
202
|
-
proposerRuleId: parsed.value.proposerRuleId,
|
|
173
|
+
const body = await readBody(c);
|
|
174
|
+
if (body === undefined) return badInput(c, 'Request body must be a JSON object');
|
|
175
|
+
const parsed = parseCreateBody(body);
|
|
176
|
+
if (typeof parsed === 'string') return badInput(c, parsed);
|
|
177
|
+
const denied = await deny(c, 'publish', parsed.agentId);
|
|
178
|
+
if (denied !== undefined) return denied;
|
|
179
|
+
const actor = actorOf(c);
|
|
180
|
+
const outcome = await service.draft({
|
|
181
|
+
tenantId: c.get('tenantId') as TenantId,
|
|
182
|
+
agentId: parsed.agentId,
|
|
183
|
+
fromVersion: parsed.fromVersion,
|
|
184
|
+
scope: parsed.scope,
|
|
185
|
+
tier: parsed.tier,
|
|
186
|
+
blockId: parsed.blockId,
|
|
187
|
+
content: parsed.content,
|
|
188
|
+
hypothesis: parsed.hypothesis,
|
|
189
|
+
...(parsed.judgmentIds !== undefined && { evidence: { judgmentIds: parsed.judgmentIds } }),
|
|
190
|
+
drafter: { kind: 'person', by: `${actor.kind}:${actor.id}` },
|
|
203
191
|
});
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
// consumers that care can distinguish (mirrors the
|
|
207
|
-
// X-Idempotent-Replay pattern from the idempotency middleware).
|
|
208
|
-
if (outcome.kind === 'dedup') {
|
|
192
|
+
if (outcome.kind === 'err') return failWith(c, outcome.error);
|
|
193
|
+
if (outcome.value.deduped) {
|
|
209
194
|
c.header('X-Proposal-Deduped', 'true');
|
|
195
|
+
return answer(c, outcome.value.proposal);
|
|
210
196
|
}
|
|
211
|
-
c.
|
|
212
|
-
return c.json(serializeProposal(outcome.proposal));
|
|
197
|
+
return answer(c, outcome.value.proposal, 201);
|
|
213
198
|
});
|
|
214
199
|
|
|
215
|
-
// ---------- POST /:proposalId/
|
|
216
|
-
r.post('/:proposalId/
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
return badInput(c, requestId, parsed.message);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const outcome = await binding.dryRunProposal({
|
|
234
|
-
tenantId,
|
|
235
|
-
supervisorId,
|
|
236
|
-
proposalId,
|
|
237
|
-
datasetId: parsed.value.datasetId,
|
|
238
|
-
datasetVersion: parsed.value.datasetVersion,
|
|
239
|
-
criterion: parsed.value.criterion,
|
|
240
|
-
});
|
|
241
|
-
if (outcome.kind === 'not-found') {
|
|
242
|
-
return proposalNotFound(c, requestId, proposalId);
|
|
243
|
-
}
|
|
244
|
-
if (outcome.kind === 'invalid-transition') {
|
|
245
|
-
return invalidTransition(c, requestId, outcome.proposalId, outcome.from, outcome.to);
|
|
246
|
-
}
|
|
247
|
-
if (outcome.kind === 'runtime-error') {
|
|
248
|
-
return runtimeError(c, requestId, outcome.code, outcome.message);
|
|
249
|
-
}
|
|
250
|
-
return c.json({
|
|
251
|
-
proposal: serializeProposal(outcome.proposal),
|
|
252
|
-
passed: outcome.passed,
|
|
253
|
-
});
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
// ---------- POST /:proposalId/submit-review ----------
|
|
257
|
-
r.post('/:proposalId/submit-review', async (c) => {
|
|
258
|
-
const requestId = c.get('requestId');
|
|
259
|
-
const tenantId = c.get('tenantId') as TenantId;
|
|
260
|
-
const supervisorId = c.req.header('x-supervisor-id') as SupervisorId;
|
|
261
|
-
const proposalId = c.req.param('proposalId') as FixProposalId;
|
|
262
|
-
|
|
263
|
-
// Body is optional — every field on submit-review is an override.
|
|
264
|
-
let body: unknown = {};
|
|
265
|
-
try {
|
|
266
|
-
const raw = await c.req.text();
|
|
267
|
-
if (raw.length > 0) body = JSON.parse(raw);
|
|
268
|
-
} catch {
|
|
269
|
-
return badInput(c, requestId, 'Request body must be valid JSON');
|
|
270
|
-
}
|
|
271
|
-
const parsed = parseSubmitReviewBody(body);
|
|
272
|
-
if (parsed.kind === 'err') {
|
|
273
|
-
return badInput(c, requestId, parsed.message);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
const outcome = await binding.submitReview({
|
|
277
|
-
tenantId,
|
|
278
|
-
supervisorId,
|
|
279
|
-
proposalId,
|
|
280
|
-
...(parsed.value.requiredRole !== undefined && { requiredRole: parsed.value.requiredRole }),
|
|
281
|
-
...(parsed.value.expiresAt !== undefined && { expiresAt: parsed.value.expiresAt }),
|
|
282
|
-
});
|
|
283
|
-
if (outcome.kind === 'not-found') {
|
|
284
|
-
return proposalNotFound(c, requestId, proposalId);
|
|
285
|
-
}
|
|
286
|
-
if (outcome.kind === 'invalid-transition') {
|
|
287
|
-
return invalidTransition(c, requestId, outcome.proposalId, outcome.from, outcome.to);
|
|
288
|
-
}
|
|
289
|
-
if (outcome.kind === 'ground-layer-violation') {
|
|
290
|
-
c.status(statusFor('ground-layer-violation') as never);
|
|
291
|
-
return c.json(
|
|
292
|
-
toWireError(
|
|
293
|
-
{
|
|
294
|
-
code: 'ground-layer-violation',
|
|
295
|
-
message: `Proposal ${outcome.proposalId as unknown as string} violated ground guardrail ${outcome.guardrailId}: ${outcome.reason}`,
|
|
296
|
-
proposalId: outcome.proposalId as unknown as string,
|
|
297
|
-
guardrailId: outcome.guardrailId,
|
|
298
|
-
reason: outcome.reason,
|
|
299
|
-
},
|
|
300
|
-
requestId,
|
|
301
|
-
),
|
|
302
|
-
);
|
|
303
|
-
}
|
|
304
|
-
if (outcome.kind === 'runtime-error') {
|
|
305
|
-
return runtimeError(c, requestId, outcome.code, outcome.message);
|
|
306
|
-
}
|
|
307
|
-
return c.json({
|
|
308
|
-
proposal: serializeProposal(outcome.proposal),
|
|
309
|
-
approvalId: outcome.approvalId as unknown as string,
|
|
310
|
-
metaFix: outcome.metaFix,
|
|
200
|
+
// ---------- POST /:proposalId/evaluate ----------
|
|
201
|
+
r.post('/:proposalId/evaluate', async (c) => {
|
|
202
|
+
const body = await readBody(c);
|
|
203
|
+
if (body === undefined) return badInput(c, 'Request body must be a JSON object');
|
|
204
|
+
const parsed = parseEvaluateBody(body);
|
|
205
|
+
if (typeof parsed === 'string') return badInput(c, parsed);
|
|
206
|
+
const ready = await prepare(c, 'publish');
|
|
207
|
+
if (ready instanceof Response) return ready;
|
|
208
|
+
const outcome = await service.evaluate({
|
|
209
|
+
tenantId: c.get('tenantId') as TenantId,
|
|
210
|
+
proposal: ready.proposal,
|
|
211
|
+
suiteId: parsed.suiteId,
|
|
212
|
+
objective: parsed.objective,
|
|
213
|
+
...(parsed.comparison !== undefined && { comparison: parsed.comparison }),
|
|
214
|
+
actor: actorOf(c),
|
|
311
215
|
});
|
|
216
|
+
if (outcome.kind === 'err') return failWith(c, outcome.error);
|
|
217
|
+
return answer(c, outcome.value, 202);
|
|
312
218
|
});
|
|
313
219
|
|
|
314
|
-
// ---------- POST /:proposalId/
|
|
315
|
-
r.post('/:proposalId/
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
} catch {
|
|
326
|
-
return badInput(c, requestId, 'Request body must be valid JSON');
|
|
327
|
-
}
|
|
328
|
-
const parsed = parseApplyBody(body);
|
|
329
|
-
if (parsed.kind === 'err') {
|
|
330
|
-
return badInput(c, requestId, parsed.message);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
const outcome = await binding.applyProposal({
|
|
334
|
-
tenantId,
|
|
335
|
-
supervisorId,
|
|
336
|
-
proposalId,
|
|
337
|
-
...(parsed.value.newVersion !== undefined && { newVersion: parsed.value.newVersion }),
|
|
220
|
+
// ---------- POST /:proposalId/request ----------
|
|
221
|
+
r.post('/:proposalId/request', async (c) => {
|
|
222
|
+
const ready = await prepare(c, 'promote');
|
|
223
|
+
if (ready instanceof Response) return ready;
|
|
224
|
+
const reason = optionalText(ready.body, 'reason');
|
|
225
|
+
if (reason.kind === 'err') return badInput(c, reason.message);
|
|
226
|
+
const outcome = await service.request({
|
|
227
|
+
tenantId: c.get('tenantId') as TenantId,
|
|
228
|
+
proposal: ready.proposal,
|
|
229
|
+
actor: actorOf(c),
|
|
230
|
+
...(reason.value !== undefined && { reason: reason.value }),
|
|
338
231
|
});
|
|
339
|
-
if (outcome.kind === '
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
if (
|
|
343
|
-
return
|
|
232
|
+
if (outcome.kind === 'err') return failWith(c, outcome.error);
|
|
233
|
+
const { proposal, promotion } = outcome.value;
|
|
234
|
+
if (promotion.kind !== 'ok') return promotionResponse(c, promotion);
|
|
235
|
+
if (promotion.promotion.status === 'refused') {
|
|
236
|
+
return promotionResponse(c, promotion, { proposalId: proposal.id as unknown as string });
|
|
344
237
|
}
|
|
345
|
-
|
|
346
|
-
return runtimeError(c, requestId, outcome.code, outcome.message);
|
|
347
|
-
}
|
|
348
|
-
return c.json({
|
|
349
|
-
proposalId: outcome.proposalId as unknown as string,
|
|
350
|
-
appliedVersion: outcome.appliedVersion,
|
|
351
|
-
appliedAt: outcome.appliedAt as unknown as string,
|
|
352
|
-
});
|
|
238
|
+
return answer(c, proposal, promotion.promotion.status === 'pending-approval' ? 202 : 201);
|
|
353
239
|
});
|
|
354
240
|
|
|
355
241
|
// ---------- POST /:proposalId/rollback ----------
|
|
356
242
|
r.post('/:proposalId/rollback', async (c) => {
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
return badInput(c, requestId, 'Request body must be valid JSON');
|
|
367
|
-
}
|
|
368
|
-
const parsed = parseReasonBody(body);
|
|
369
|
-
if (parsed.kind === 'err') {
|
|
370
|
-
return badInput(c, requestId, parsed.message);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
const outcome = await binding.rollbackProposal({
|
|
374
|
-
tenantId,
|
|
375
|
-
supervisorId,
|
|
376
|
-
proposalId,
|
|
377
|
-
reason: parsed.value.reason,
|
|
378
|
-
});
|
|
379
|
-
if (outcome.kind === 'not-found') {
|
|
380
|
-
return proposalNotFound(c, requestId, proposalId);
|
|
381
|
-
}
|
|
382
|
-
if (outcome.kind === 'invalid-transition') {
|
|
383
|
-
return invalidTransition(c, requestId, outcome.proposalId, outcome.from, outcome.to);
|
|
384
|
-
}
|
|
385
|
-
if (outcome.kind === 'runtime-error') {
|
|
386
|
-
return runtimeError(c, requestId, outcome.code, outcome.message);
|
|
387
|
-
}
|
|
388
|
-
return c.json({
|
|
389
|
-
proposalId: outcome.proposalId as unknown as string,
|
|
390
|
-
rolledBackAt: outcome.rolledBackAt as unknown as string,
|
|
243
|
+
const ready = await prepare(c, 'promote');
|
|
244
|
+
if (ready instanceof Response) return ready;
|
|
245
|
+
const reason = optionalText(ready.body, 'reason');
|
|
246
|
+
if (reason.kind === 'err') return badInput(c, reason.message);
|
|
247
|
+
const outcome = await service.rollback({
|
|
248
|
+
tenantId: c.get('tenantId') as TenantId,
|
|
249
|
+
proposal: ready.proposal,
|
|
250
|
+
actor: actorOf(c),
|
|
251
|
+
...(reason.value !== undefined && { reason: reason.value }),
|
|
391
252
|
});
|
|
253
|
+
if (outcome.kind === 'err') return failWith(c, outcome.error);
|
|
254
|
+
return answer(c, outcome.value);
|
|
392
255
|
});
|
|
393
256
|
|
|
394
257
|
// ---------- POST /:proposalId/withdraw ----------
|
|
395
258
|
r.post('/:proposalId/withdraw', async (c) => {
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
if (parsed.kind === 'err') {
|
|
409
|
-
return badInput(c, requestId, parsed.message);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
const outcome = await binding.withdrawProposal({
|
|
413
|
-
tenantId,
|
|
414
|
-
supervisorId,
|
|
415
|
-
proposalId,
|
|
416
|
-
reason: parsed.value.reason,
|
|
259
|
+
const ready = await prepare(c, 'publish');
|
|
260
|
+
if (ready instanceof Response) return ready;
|
|
261
|
+
const reason = optionalText(ready.body, 'reason');
|
|
262
|
+
if (reason.kind === 'err') return badInput(c, reason.message);
|
|
263
|
+
if (reason.value === undefined) {
|
|
264
|
+
return badInput(c, '`reason` is required: why it was withdrawn');
|
|
265
|
+
}
|
|
266
|
+
const outcome = await service.withdraw({
|
|
267
|
+
tenantId: c.get('tenantId') as TenantId,
|
|
268
|
+
proposal: ready.proposal,
|
|
269
|
+
actor: actorOf(c),
|
|
270
|
+
reason: reason.value,
|
|
417
271
|
});
|
|
418
|
-
if (outcome.kind === '
|
|
419
|
-
|
|
420
|
-
}
|
|
421
|
-
if (outcome.kind === 'invalid-transition') {
|
|
422
|
-
return invalidTransition(c, requestId, outcome.proposalId, outcome.from, outcome.to);
|
|
423
|
-
}
|
|
424
|
-
if (outcome.kind === 'runtime-error') {
|
|
425
|
-
return runtimeError(c, requestId, outcome.code, outcome.message);
|
|
426
|
-
}
|
|
427
|
-
return c.json(serializeProposal(outcome.proposal));
|
|
272
|
+
if (outcome.kind === 'err') return failWith(c, outcome.error);
|
|
273
|
+
return answer(c, outcome.value);
|
|
428
274
|
});
|
|
429
275
|
|
|
430
276
|
return r;
|
|
431
277
|
}
|
|
432
278
|
|
|
433
|
-
//
|
|
279
|
+
// ---------- wire ----------
|
|
434
280
|
|
|
435
|
-
function
|
|
281
|
+
function evaluationWire(
|
|
282
|
+
stored: NonNullable<StoredProposal['evaluation']>,
|
|
283
|
+
seen: ProposalEvaluationOutcome | null | undefined,
|
|
284
|
+
): Record<string, unknown> {
|
|
285
|
+
if (seen === undefined || seen === null) return { ...stored };
|
|
286
|
+
return {
|
|
287
|
+
...stored,
|
|
288
|
+
runStatus: seen.runStatus,
|
|
289
|
+
...(seen.baseline !== undefined && { baseline: seen.baseline }),
|
|
290
|
+
...(seen.candidate !== undefined && { candidate: seen.candidate }),
|
|
291
|
+
...(seen.delta !== undefined && { delta: seen.delta }),
|
|
292
|
+
...(seen.spread !== undefined && { spread: seen.spread }),
|
|
293
|
+
...(seen.cases !== undefined && { cases: seen.cases }),
|
|
294
|
+
...(seen.better !== undefined && { better: seen.better }),
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export function serialize(p: StoredProposal, f: ProposalFacts): Record<string, unknown> {
|
|
299
|
+
const promotion = f.promotion ?? undefined;
|
|
436
300
|
return {
|
|
437
301
|
id: p.id as unknown as string,
|
|
438
|
-
tenantId: p.tenantId as unknown as string,
|
|
439
|
-
supervisorId: p.supervisorId as unknown as string,
|
|
440
302
|
agentId: p.agentId as unknown as string,
|
|
441
|
-
|
|
303
|
+
fromVersion: p.fromVersion,
|
|
304
|
+
scope: liveScopeToWire(p.scope),
|
|
442
305
|
tier: p.tier,
|
|
443
306
|
change: p.change,
|
|
444
|
-
patternRefs: p.patternRefs,
|
|
445
307
|
hypothesis: p.hypothesis,
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
...(p.
|
|
450
|
-
...(p.
|
|
451
|
-
|
|
308
|
+
...(p.evidence !== undefined && { evidence: p.evidence }),
|
|
309
|
+
drafter: p.drafter,
|
|
310
|
+
status: f.status,
|
|
311
|
+
...(p.candidate !== undefined && { candidate: p.candidate }),
|
|
312
|
+
...(p.evaluation !== undefined && { evaluation: evaluationWire(p.evaluation, f.evaluation) }),
|
|
313
|
+
...(promotion !== undefined && {
|
|
314
|
+
promotion: {
|
|
315
|
+
id: promotion.id,
|
|
316
|
+
status: promotion.status ?? 'promoted',
|
|
317
|
+
...(promotion.approvalId !== undefined && { approvalId: promotion.approvalId }),
|
|
318
|
+
...(f.liveNow !== undefined && { liveNow: f.liveNow }),
|
|
319
|
+
},
|
|
452
320
|
}),
|
|
453
|
-
...(p.
|
|
454
|
-
...(p.
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
updatedAt: p.updatedAt as unknown as string,
|
|
458
|
-
...(p.resolvedAt !== undefined && { resolvedAt: p.resolvedAt as unknown as string }),
|
|
321
|
+
...(p.rolledBack !== undefined && { rolledBack: p.rolledBack }),
|
|
322
|
+
...(p.withdrawn !== undefined && { withdrawn: p.withdrawn }),
|
|
323
|
+
createdAt: p.createdAt,
|
|
324
|
+
updatedAt: p.updatedAt,
|
|
459
325
|
};
|
|
460
326
|
}
|
|
461
327
|
|
|
462
|
-
//
|
|
328
|
+
// ---------- parsing ----------
|
|
463
329
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
330
|
+
interface CreateBody {
|
|
331
|
+
readonly agentId: string;
|
|
332
|
+
readonly fromVersion: string;
|
|
333
|
+
readonly scope: StoredProposal['scope'];
|
|
334
|
+
readonly tier: ProposalTier;
|
|
335
|
+
readonly blockId: string;
|
|
336
|
+
readonly content: ProposedChange['change']['content'];
|
|
337
|
+
readonly hypothesis: string;
|
|
338
|
+
readonly judgmentIds?: readonly string[];
|
|
469
339
|
}
|
|
470
340
|
|
|
471
|
-
function
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
{
|
|
476
|
-
code: 'proposal-not-found',
|
|
477
|
-
message: `No proposal with id "${proposalId as unknown as string}"`,
|
|
478
|
-
proposalId: proposalId as unknown as string,
|
|
479
|
-
},
|
|
480
|
-
requestId,
|
|
481
|
-
),
|
|
482
|
-
);
|
|
341
|
+
function obj(value: unknown): Record<string, unknown> | undefined {
|
|
342
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
343
|
+
? (value as Record<string, unknown>)
|
|
344
|
+
: undefined;
|
|
483
345
|
}
|
|
484
346
|
|
|
485
|
-
function
|
|
486
|
-
|
|
487
|
-
requestId: string,
|
|
488
|
-
proposalId: FixProposalId,
|
|
489
|
-
from: FixProposalStatus,
|
|
490
|
-
to: FixProposalStatus,
|
|
491
|
-
): Response {
|
|
492
|
-
c.status(statusFor('proposal-invalid-state-transition') as never);
|
|
493
|
-
return c.json(
|
|
494
|
-
toWireError(
|
|
495
|
-
{
|
|
496
|
-
code: 'proposal-invalid-state-transition',
|
|
497
|
-
message: `Cannot transition proposal ${proposalId as unknown as string} from ${from} to ${to}`,
|
|
498
|
-
proposalId: proposalId as unknown as string,
|
|
499
|
-
from,
|
|
500
|
-
to,
|
|
501
|
-
},
|
|
502
|
-
requestId,
|
|
503
|
-
),
|
|
504
|
-
);
|
|
347
|
+
function nonEmpty(value: unknown): value is string {
|
|
348
|
+
return typeof value === 'string' && value.trim() !== '' && value.length <= MAX_TEXT;
|
|
505
349
|
}
|
|
506
350
|
|
|
507
|
-
function
|
|
508
|
-
|
|
509
|
-
|
|
351
|
+
function parseContent(tier: ProposalTier, raw: unknown): string | undefined {
|
|
352
|
+
const content = obj(raw);
|
|
353
|
+
const keys = content === undefined ? [] : Object.keys(content);
|
|
354
|
+
if (tier === 'settings-block') {
|
|
355
|
+
return content !== undefined && obj(content.values) !== undefined && keys.length === 1
|
|
356
|
+
? undefined
|
|
357
|
+
: '`change.content` for a settings block is { values: { … } } (the schema carries over)';
|
|
358
|
+
}
|
|
359
|
+
return content !== undefined && typeof content.template === 'string' && keys.length === 1
|
|
360
|
+
? undefined
|
|
361
|
+
: '`change.content` for a prompt block is { template } (the parameters carry over)';
|
|
510
362
|
}
|
|
511
363
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
readonly change: ProposedChange['change'];
|
|
523
|
-
readonly patternRefs: readonly PatternRef[];
|
|
524
|
-
readonly hypothesis: string;
|
|
525
|
-
readonly proposerRuleId: string;
|
|
364
|
+
function parseEvidence(raw: unknown): { judgmentIds?: string[] } | string {
|
|
365
|
+
if (raw === undefined) return {};
|
|
366
|
+
const evidence = obj(raw);
|
|
367
|
+
const ids = evidence?.judgmentIds;
|
|
368
|
+
const valid =
|
|
369
|
+
evidence !== undefined &&
|
|
370
|
+
Object.keys(evidence).every((k) => k === 'judgmentIds') &&
|
|
371
|
+
(ids === undefined || (Array.isArray(ids) && ids.every((id) => nonEmpty(id))));
|
|
372
|
+
if (!valid) return '`evidence` is { judgmentIds?: string[] }';
|
|
373
|
+
return ids === undefined ? {} : { judgmentIds: ids as string[] };
|
|
526
374
|
}
|
|
527
375
|
|
|
528
|
-
function
|
|
529
|
-
if (
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
const b = body as Record<string, unknown>;
|
|
533
|
-
const agentId = b.agentId;
|
|
534
|
-
if (typeof agentId !== 'string' || agentId.length === 0) {
|
|
535
|
-
return { kind: 'err', message: 'Field `agentId` must be a non-empty string' };
|
|
536
|
-
}
|
|
537
|
-
const agentVersion = b.agentVersion;
|
|
538
|
-
if (typeof agentVersion !== 'string' || agentVersion.length === 0) {
|
|
539
|
-
return { kind: 'err', message: 'Field `agentVersion` must be a non-empty string' };
|
|
540
|
-
}
|
|
541
|
-
const tier = b.tier;
|
|
542
|
-
if (typeof tier !== 'string' || !PROPOSAL_TIERS.has(tier as ProposedChange['tier'])) {
|
|
543
|
-
return {
|
|
544
|
-
kind: 'err',
|
|
545
|
-
message: 'Field `tier` must be one of "prompt", "retrieval", "tool-config"',
|
|
546
|
-
};
|
|
547
|
-
}
|
|
548
|
-
const change = b.change;
|
|
549
|
-
if (change === null || typeof change !== 'object') {
|
|
550
|
-
return { kind: 'err', message: 'Field `change` must be an object' };
|
|
376
|
+
function parseCreateBody(b: Record<string, unknown>): CreateBody | string {
|
|
377
|
+
if (!nonEmpty(b.agentId)) return '`agentId` is required';
|
|
378
|
+
if (!nonEmpty(b.fromVersion)) {
|
|
379
|
+
return '`fromVersion` is required: the agent version the change applies to';
|
|
551
380
|
}
|
|
552
|
-
const
|
|
553
|
-
if (
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
const
|
|
557
|
-
if (
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
if (
|
|
562
|
-
return
|
|
381
|
+
const scope = parseLiveScopeBody(b.scope);
|
|
382
|
+
if (scope.kind === 'err') return scope.message;
|
|
383
|
+
if (!TIERS.includes(b.tier as ProposalTier)) return `\`tier\` must be one of ${TIERS.join(', ')}`;
|
|
384
|
+
const tier = b.tier as ProposalTier;
|
|
385
|
+
const change = obj(b.change);
|
|
386
|
+
if (change === undefined) return '`change` must be an object: { blockId, content }';
|
|
387
|
+
if (!nonEmpty(change.blockId)) return '`change.blockId` is required';
|
|
388
|
+
const contentProblem = parseContent(tier, change.content);
|
|
389
|
+
if (contentProblem !== undefined) return contentProblem;
|
|
390
|
+
if (!nonEmpty(b.hypothesis)) {
|
|
391
|
+
return '`hypothesis` is required: what the change should improve, and why';
|
|
563
392
|
}
|
|
393
|
+
const evidence = parseEvidence(b.evidence);
|
|
394
|
+
if (typeof evidence === 'string') return evidence;
|
|
564
395
|
return {
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
proposerRuleId,
|
|
574
|
-
},
|
|
396
|
+
agentId: b.agentId,
|
|
397
|
+
fromVersion: b.fromVersion,
|
|
398
|
+
scope: scope.scope,
|
|
399
|
+
tier,
|
|
400
|
+
blockId: change.blockId,
|
|
401
|
+
content: change.content as ProposedChange['change']['content'],
|
|
402
|
+
hypothesis: b.hypothesis,
|
|
403
|
+
...evidence,
|
|
575
404
|
};
|
|
576
405
|
}
|
|
577
406
|
|
|
578
|
-
interface
|
|
579
|
-
readonly
|
|
580
|
-
readonly
|
|
581
|
-
readonly
|
|
407
|
+
interface EvaluateBody {
|
|
408
|
+
readonly suiteId: string;
|
|
409
|
+
readonly objective: ProposalObjective;
|
|
410
|
+
readonly comparison?: EvalComparison;
|
|
582
411
|
}
|
|
583
412
|
|
|
584
|
-
function
|
|
585
|
-
if (
|
|
586
|
-
return
|
|
587
|
-
}
|
|
588
|
-
const b = body as Record<string, unknown>;
|
|
589
|
-
const datasetId = b.datasetId;
|
|
590
|
-
if (typeof datasetId !== 'string' || datasetId.length === 0) {
|
|
591
|
-
return { kind: 'err', message: 'Field `datasetId` must be a non-empty string' };
|
|
413
|
+
function parseEvaluateBody(b: Record<string, unknown>): EvaluateBody | string {
|
|
414
|
+
if (!nonEmpty(b.suiteId)) {
|
|
415
|
+
return '`suiteId` is required: the test set (a judged eval suite) to compare on';
|
|
592
416
|
}
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
return { kind: 'err', message: 'Field `datasetVersion` must be a non-empty string' };
|
|
417
|
+
if (b.objective !== undefined && !OBJECTIVES.includes(b.objective as ProposalObjective)) {
|
|
418
|
+
return `\`objective\` must be one of ${OBJECTIVES.join(', ')}`;
|
|
596
419
|
}
|
|
597
|
-
const
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
const minPassRate = cr.minPassRate;
|
|
604
|
-
if (typeof minPassRate !== 'number' || !Number.isFinite(minPassRate)) {
|
|
605
|
-
return {
|
|
606
|
-
kind: 'err',
|
|
607
|
-
message: 'Field `criterion.minPassRate` must be a finite number for kind "min-pass-rate"',
|
|
608
|
-
};
|
|
609
|
-
}
|
|
610
|
-
return {
|
|
611
|
-
kind: 'ok',
|
|
612
|
-
value: { datasetId, datasetVersion, criterion: { kind: 'min-pass-rate', minPassRate } },
|
|
613
|
-
};
|
|
614
|
-
}
|
|
615
|
-
if (cr.kind === 'strict-improvement') {
|
|
616
|
-
const baseline = cr.baselinePassRate;
|
|
617
|
-
const minDelta = cr.minDelta;
|
|
618
|
-
if (typeof baseline !== 'number' || !Number.isFinite(baseline)) {
|
|
619
|
-
return {
|
|
620
|
-
kind: 'err',
|
|
621
|
-
message:
|
|
622
|
-
'Field `criterion.baselinePassRate` must be a finite number for kind "strict-improvement"',
|
|
623
|
-
};
|
|
624
|
-
}
|
|
625
|
-
if (typeof minDelta !== 'number' || !Number.isFinite(minDelta)) {
|
|
626
|
-
return {
|
|
627
|
-
kind: 'err',
|
|
628
|
-
message: 'Field `criterion.minDelta` must be a finite number for kind "strict-improvement"',
|
|
629
|
-
};
|
|
630
|
-
}
|
|
631
|
-
return {
|
|
632
|
-
kind: 'ok',
|
|
633
|
-
value: {
|
|
634
|
-
datasetId,
|
|
635
|
-
datasetVersion,
|
|
636
|
-
criterion: { kind: 'strict-improvement', baselinePassRate: baseline, minDelta },
|
|
637
|
-
},
|
|
638
|
-
};
|
|
420
|
+
const extra = Object.keys(b).filter(
|
|
421
|
+
(k) =>
|
|
422
|
+
!['suiteId', 'objective', 'reads', 'repetitions', 'k', 'classWeights', 'sample'].includes(k),
|
|
423
|
+
);
|
|
424
|
+
if (extra.length > 0) {
|
|
425
|
+
return `\`${extra[0]}\` isn't a field of an evaluation: { suiteId, objective?, reads?, repetitions?, k?, classWeights?, sample? }`;
|
|
639
426
|
}
|
|
427
|
+
const comparison = parseComparison(b);
|
|
428
|
+
if (comparison.kind === 'err') return comparison.message;
|
|
640
429
|
return {
|
|
641
|
-
|
|
642
|
-
|
|
430
|
+
suiteId: b.suiteId,
|
|
431
|
+
objective: (b.objective as ProposalObjective | undefined) ?? 'weightedYesShare',
|
|
432
|
+
...(comparison.value !== undefined && { comparison: comparison.value }),
|
|
643
433
|
};
|
|
644
434
|
}
|
|
645
435
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
const b = body as Record<string, unknown>;
|
|
656
|
-
const out: {
|
|
657
|
-
requiredRole?: 'standard' | 'senior' | 'admin';
|
|
658
|
-
expiresAt?: Timestamp;
|
|
659
|
-
} = {};
|
|
660
|
-
const requiredRole = b.requiredRole;
|
|
661
|
-
if (requiredRole !== undefined) {
|
|
662
|
-
if (
|
|
663
|
-
typeof requiredRole !== 'string' ||
|
|
664
|
-
!REVIEWER_ROLES.has(requiredRole as 'standard' | 'senior' | 'admin')
|
|
665
|
-
) {
|
|
666
|
-
return {
|
|
436
|
+
function optionalText(
|
|
437
|
+
body: Record<string, unknown>,
|
|
438
|
+
field: string,
|
|
439
|
+
): { kind: 'ok'; value?: string } | { kind: 'err'; message: string } {
|
|
440
|
+
const v = body[field];
|
|
441
|
+
if (v === undefined) return { kind: 'ok' };
|
|
442
|
+
return nonEmpty(v)
|
|
443
|
+
? { kind: 'ok', value: v }
|
|
444
|
+
: {
|
|
667
445
|
kind: 'err',
|
|
668
|
-
message:
|
|
446
|
+
message: `\`${field}\` must be a non-empty string (at most ${MAX_TEXT} characters)`,
|
|
669
447
|
};
|
|
670
|
-
}
|
|
671
|
-
out.requiredRole = requiredRole as 'standard' | 'senior' | 'admin';
|
|
672
|
-
}
|
|
673
|
-
const expiresAt = b.expiresAt;
|
|
674
|
-
if (expiresAt !== undefined) {
|
|
675
|
-
if (typeof expiresAt !== 'string' || Number.isNaN(new Date(expiresAt).getTime())) {
|
|
676
|
-
return { kind: 'err', message: 'Field `expiresAt` must be an ISO date string' };
|
|
677
|
-
}
|
|
678
|
-
out.expiresAt = expiresAt as Timestamp;
|
|
679
|
-
}
|
|
680
|
-
return { kind: 'ok', value: out };
|
|
681
448
|
}
|
|
682
449
|
|
|
683
|
-
|
|
684
|
-
|
|
450
|
+
/** The body as an object; `{}` when empty, `undefined` when it isn't a JSON object. */
|
|
451
|
+
async function readBody(c: Ctx): Promise<Record<string, unknown> | undefined> {
|
|
452
|
+
const text = await c.req.text();
|
|
453
|
+
if (text.trim() === '') return {};
|
|
454
|
+
try {
|
|
455
|
+
return obj(JSON.parse(text));
|
|
456
|
+
} catch {
|
|
457
|
+
return undefined;
|
|
458
|
+
}
|
|
685
459
|
}
|
|
686
460
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
kind: 'err',
|
|
697
|
-
message: 'Field `newVersion` must be a non-empty string when present',
|
|
698
|
-
};
|
|
699
|
-
}
|
|
700
|
-
return { kind: 'ok', value: { newVersion } };
|
|
701
|
-
}
|
|
702
|
-
return { kind: 'ok', value: {} };
|
|
461
|
+
// ---------- helpers ----------
|
|
462
|
+
|
|
463
|
+
function fail(
|
|
464
|
+
c: Ctx,
|
|
465
|
+
code: string,
|
|
466
|
+
error: { code: string; message: string } & Record<string, unknown>,
|
|
467
|
+
) {
|
|
468
|
+
c.status(statusFor(code) as never);
|
|
469
|
+
return c.json(toWireError(error, c.get('requestId')));
|
|
703
470
|
}
|
|
704
471
|
|
|
705
|
-
|
|
706
|
-
|
|
472
|
+
function badInput(c: Ctx, message: string) {
|
|
473
|
+
return fail(c, 'bad-input', { code: 'bad-input', message });
|
|
707
474
|
}
|
|
708
475
|
|
|
709
|
-
function
|
|
710
|
-
|
|
711
|
-
return { kind: 'err', message: 'Request body must be an object' };
|
|
712
|
-
}
|
|
713
|
-
const b = body as Record<string, unknown>;
|
|
714
|
-
const reason = b.reason;
|
|
715
|
-
if (typeof reason !== 'string' || reason.length === 0) {
|
|
716
|
-
return { kind: 'err', message: 'Field `reason` must be a non-empty string' };
|
|
717
|
-
}
|
|
718
|
-
return { kind: 'ok', value: { reason } };
|
|
476
|
+
function failWith(c: Ctx, error: ProposalServiceError) {
|
|
477
|
+
return fail(c, error.code, error);
|
|
719
478
|
}
|