@kindgi/api 0.1.4 → 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 +90 -13
- package/dist/openapi/schemas.d.ts.map +1 -1
- package/dist/openapi/schemas.js +2081 -359
- 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 +110 -3
- 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 +15385 -8788
- 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 +2387 -469
- 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 +142 -3
- 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
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// Copyright (C) 2026 Kindgi Inc.
|
|
3
|
-
import { denyPayload, } from '@kindgi/authz';
|
|
3
|
+
import { OBJECT_ACTIONS, denyPayload, } from '@kindgi/authz';
|
|
4
4
|
import { toWireError } from '../errors.js';
|
|
5
5
|
export function createAuthorizer(binding) {
|
|
6
|
+
/**
|
|
7
|
+
* Whether a resource is inside the project the caller's API key is
|
|
8
|
+
* limited to (always, for a caller with no such key). The tenant, orgs,
|
|
9
|
+
* teams and projects are held by `keyCeilingDeny`; anything else must
|
|
10
|
+
* belong to the key's project, as the store says (`inProject`). Without
|
|
11
|
+
* that answer, the key reaches no other resource.
|
|
12
|
+
*/
|
|
13
|
+
async function inKeyProject(c, principal, resource) {
|
|
14
|
+
const keyProject = c.get('tokenProjectId');
|
|
15
|
+
if (keyProject === undefined || STRUCTURAL.has(resource.type))
|
|
16
|
+
return true;
|
|
17
|
+
if (binding.inProject === undefined)
|
|
18
|
+
return false;
|
|
19
|
+
return binding.inProject(principal, resource, keyProject);
|
|
20
|
+
}
|
|
6
21
|
async function checkInternal(c, action, resource) {
|
|
7
22
|
const principal = c.get('principal');
|
|
8
23
|
if (principal === undefined) {
|
|
@@ -18,6 +33,15 @@ export function createAuthorizer(binding) {
|
|
|
18
33
|
},
|
|
19
34
|
};
|
|
20
35
|
}
|
|
36
|
+
const undefinedPair = undefinedRelation(action, resource);
|
|
37
|
+
if (undefinedPair !== undefined)
|
|
38
|
+
return undefinedPair;
|
|
39
|
+
const ceiling = keyCeilingDeny(c, action, resource);
|
|
40
|
+
if (ceiling !== undefined)
|
|
41
|
+
return ceiling;
|
|
42
|
+
if (!(await inKeyProject(c, principal, resource))) {
|
|
43
|
+
return keyDecision(action, resource, `the API key is limited to project ${c.get('tokenProjectId')}`);
|
|
44
|
+
}
|
|
21
45
|
const requestId = c.get('requestId');
|
|
22
46
|
const ctx = typeof requestId === 'string' && requestId.length > 0
|
|
23
47
|
? { correlationId: requestId }
|
|
@@ -58,19 +82,104 @@ export function createAuthorizer(binding) {
|
|
|
58
82
|
const principal = c.get('principal');
|
|
59
83
|
if (principal === undefined)
|
|
60
84
|
return [];
|
|
61
|
-
|
|
85
|
+
// What the model can't answer, or the key itself rules out, never
|
|
86
|
+
// reaches the store.
|
|
87
|
+
const allowedByKey = await Promise.all(items.map(async (item) => undefinedRelation(action, refFn(item)) === undefined &&
|
|
88
|
+
keyCeilingDeny(c, action, refFn(item)) === undefined &&
|
|
89
|
+
(await inKeyProject(c, principal, refFn(item)))));
|
|
90
|
+
const open = items.filter((_, i) => allowedByKey[i]);
|
|
91
|
+
if (open.length === 0)
|
|
92
|
+
return [];
|
|
93
|
+
const refs = open.map(refFn);
|
|
62
94
|
const requestId = c.get('requestId');
|
|
63
95
|
const ctx = typeof requestId === 'string' && requestId.length > 0
|
|
64
96
|
? { correlationId: requestId }
|
|
65
97
|
: undefined;
|
|
66
98
|
const decisions = await binding.checkBatch(principal, action, refs, ctx);
|
|
67
99
|
const out = [];
|
|
68
|
-
for (let i = 0; i <
|
|
100
|
+
for (let i = 0; i < open.length; i++) {
|
|
69
101
|
if (decisions[i]?.allowed)
|
|
70
|
-
out.push(
|
|
102
|
+
out.push(open[i]);
|
|
71
103
|
}
|
|
72
104
|
return out;
|
|
73
105
|
},
|
|
106
|
+
async listObjects(c, action, type) {
|
|
107
|
+
if (binding.listObjects === undefined)
|
|
108
|
+
return undefined;
|
|
109
|
+
const principal = c.get('principal');
|
|
110
|
+
if (principal === undefined)
|
|
111
|
+
return [];
|
|
112
|
+
const requestId = c.get('requestId');
|
|
113
|
+
const ctx = typeof requestId === 'string' && requestId.length > 0
|
|
114
|
+
? { correlationId: requestId }
|
|
115
|
+
: undefined;
|
|
116
|
+
return binding.listObjects(principal, action, type, ctx);
|
|
117
|
+
},
|
|
74
118
|
};
|
|
75
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* A check must name a relation the authorization model defines for the
|
|
122
|
+
* resource's type (`OBJECT_ACTIONS`, kept equal to the runtime's model by
|
|
123
|
+
* its tests). Any other pair is a bug in the route: the PDP rejects it
|
|
124
|
+
* for everyone, the seed admin included, and only with authorization on,
|
|
125
|
+
* so it's refused here, naming the pair, and logged.
|
|
126
|
+
*/
|
|
127
|
+
function undefinedRelation(action, resource) {
|
|
128
|
+
const defined = OBJECT_ACTIONS[resource.type];
|
|
129
|
+
if (defined?.includes(action) === true)
|
|
130
|
+
return undefined;
|
|
131
|
+
const reason = `the authorization model has no \`${action}\` on \`${resource.type}\` (a check that can never pass: a bug in the route)`;
|
|
132
|
+
console.error(`[authz] ${reason}`);
|
|
133
|
+
return {
|
|
134
|
+
allowed: false,
|
|
135
|
+
failing: 'invalid-action',
|
|
136
|
+
reason,
|
|
137
|
+
evidence: {
|
|
138
|
+
action,
|
|
139
|
+
relation: '',
|
|
140
|
+
resource: `${resource.type}:${resource.id}`,
|
|
141
|
+
actorSubject: '',
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/** Types a key's project limit is checked on directly, not through `inProject`. */
|
|
146
|
+
const STRUCTURAL = new Set(['tenant', 'org', 'team', 'project']);
|
|
147
|
+
function keyDecision(action, resource, reason) {
|
|
148
|
+
return {
|
|
149
|
+
allowed: false,
|
|
150
|
+
failing: 'scope',
|
|
151
|
+
reason,
|
|
152
|
+
evidence: {
|
|
153
|
+
action,
|
|
154
|
+
relation: '',
|
|
155
|
+
resource: `${resource.type}:${resource.id}`,
|
|
156
|
+
actorSubject: '',
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* What an API key itself rules out, before the principal's grants are
|
|
162
|
+
* asked: a key can do less than its principal, never more.
|
|
163
|
+
*
|
|
164
|
+
* - A `member` key takes no `admin` action on the tenant, even for a
|
|
165
|
+
* principal who is a tenant admin: a day-to-day key can't change keys,
|
|
166
|
+
* grants or policies. Below the tenant, its principal's roles hold: a
|
|
167
|
+
* project admin's member key administers that project.
|
|
168
|
+
* - A key limited to a project acts on no other project, and takes no
|
|
169
|
+
* `admin` action on the tenant, an org or a team. Other resources must
|
|
170
|
+
* be in its project (`inKeyProject`).
|
|
171
|
+
*/
|
|
172
|
+
function keyCeilingDeny(c, action, resource) {
|
|
173
|
+
if (action === 'admin' && resource.type === 'tenant' && c.get('tokenRole') === 'member') {
|
|
174
|
+
return keyDecision(action, resource, 'a member API key takes no admin action on the tenant');
|
|
175
|
+
}
|
|
176
|
+
const keyProject = c.get('tokenProjectId');
|
|
177
|
+
if (keyProject === undefined)
|
|
178
|
+
return undefined;
|
|
179
|
+
const otherProject = resource.type === 'project' && resource.id !== keyProject;
|
|
180
|
+
const aboveIt = resource.type !== 'project' && STRUCTURAL.has(resource.type) && action === 'admin';
|
|
181
|
+
return otherProject || aboveIt
|
|
182
|
+
? keyDecision(action, resource, `the API key is limited to project ${keyProject}`)
|
|
183
|
+
: undefined;
|
|
184
|
+
}
|
|
76
185
|
//# sourceMappingURL=authorize.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorize.js","sourceRoot":"","sources":["../../src/middleware/authorize.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,iCAAiC;AAoBjC,OAAO,
|
|
1
|
+
{"version":3,"file":"authorize.js","sourceRoot":"","sources":["../../src/middleware/authorize.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,6 +15,11 @@ import type { Context } from 'hono';
|
|
|
15
15
|
*
|
|
16
16
|
* The requestId embedded in the response body matches
|
|
17
17
|
* `middleware/request-id.ts`, so a caller can correlate.
|
|
18
|
+
*
|
|
19
|
+
* A failed database query's message carries its SQL and its parameters
|
|
20
|
+
* (Drizzle's `Failed query: … params: …`), which can be a person's email or
|
|
21
|
+
* address: the body says only that a query failed, and the log keeps the
|
|
22
|
+
* whole error.
|
|
18
23
|
*/
|
|
19
24
|
export declare function mapThrownError(cause: Error, c: Context): Response;
|
|
20
25
|
//# sourceMappingURL=error-mapper.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-mapper.d.ts","sourceRoot":"","sources":["../../src/middleware/error-mapper.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAIpC
|
|
1
|
+
{"version":3,"file":"error-mapper.d.ts","sourceRoot":"","sources":["../../src/middleware/error-mapper.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAIpC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,GAAG,QAAQ,CAqBjE"}
|
|
@@ -17,6 +17,11 @@ import { statusFor, toWireError } from '../errors.js';
|
|
|
17
17
|
*
|
|
18
18
|
* The requestId embedded in the response body matches
|
|
19
19
|
* `middleware/request-id.ts`, so a caller can correlate.
|
|
20
|
+
*
|
|
21
|
+
* A failed database query's message carries its SQL and its parameters
|
|
22
|
+
* (Drizzle's `Failed query: … params: …`), which can be a person's email or
|
|
23
|
+
* address: the body says only that a query failed, and the log keeps the
|
|
24
|
+
* whole error.
|
|
20
25
|
*/
|
|
21
26
|
export function mapThrownError(cause, c) {
|
|
22
27
|
if ('getResponse' in cause && typeof cause.getResponse === 'function') {
|
|
@@ -28,9 +33,17 @@ export function mapThrownError(cause, c) {
|
|
|
28
33
|
c.get('log')?.error('unhandled error', { err: cause });
|
|
29
34
|
const body = toWireError({
|
|
30
35
|
code: 'internal-server-error',
|
|
31
|
-
message: cause
|
|
36
|
+
message: isQueryError(cause)
|
|
37
|
+
? 'A database query failed.'
|
|
38
|
+
: cause.message.length > 0
|
|
39
|
+
? cause.message
|
|
40
|
+
: 'Unhandled server error',
|
|
32
41
|
}, requestId);
|
|
33
42
|
c.status(statusFor('internal-server-error'));
|
|
34
43
|
return c.json(body);
|
|
35
44
|
}
|
|
45
|
+
/** Drizzle's wrapper around a failed query, whose message is the SQL and its values. */
|
|
46
|
+
function isQueryError(cause) {
|
|
47
|
+
return cause.name === 'DrizzleQueryError' || cause.message.startsWith('Failed query: ');
|
|
48
|
+
}
|
|
36
49
|
//# sourceMappingURL=error-mapper.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-mapper.js","sourceRoot":"","sources":["../../src/middleware/error-mapper.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,iCAAiC;AAKjC,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEtD
|
|
1
|
+
{"version":3,"file":"error-mapper.js","sourceRoot":"","sources":["../../src/middleware/error-mapper.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,iCAAiC;AAKjC,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,cAAc,CAAC,KAAY,EAAE,CAAU;IACrD,IAAI,aAAa,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,WAAW,KAAK,UAAU,EAAE,CAAC;QACtE,MAAM,GAAG,GAAI,KAAqC,CAAC,WAAW,EAAE,CAAC;QACjE,OAAO,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACtC,CAAC;IACD,MAAM,SAAS,GAAI,CAAC,CAAC,GAAG,CAAC,WAAW,CAAwB,IAAI,aAAa,CAAC;IAC9E,yEAAyE;IACxE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAwB,EAAE,KAAK,CAAC,iBAAiB,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/E,MAAM,IAAI,GAAG,WAAW,CACtB;QACE,IAAI,EAAE,uBAAuB;QAC7B,OAAO,EAAE,YAAY,CAAC,KAAK,CAAC;YAC1B,CAAC,CAAC,0BAA0B;YAC5B,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;gBACxB,CAAC,CAAC,KAAK,CAAC,OAAO;gBACf,CAAC,CAAC,wBAAwB;KAC/B,EACD,SAAS,CACV,CAAC;IACF,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,uBAAuB,CAAU,CAAC,CAAC;IACtD,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACtB,CAAC;AAED,wFAAwF;AACxF,SAAS,YAAY,CAAC,KAAY;IAChC,OAAO,KAAK,CAAC,IAAI,KAAK,mBAAmB,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;AAC1F,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { MiddlewareHandler } from 'hono';
|
|
1
|
+
import type { Context, MiddlewareHandler } from 'hono';
|
|
2
|
+
import type { AppEnv } from '../types.js';
|
|
2
3
|
/**
|
|
3
|
-
* Idempotency-key store: `(tenantId, route, key) → cached response`,
|
|
4
|
+
* Idempotency-key store: `(tenantId, caller, route, key) → cached response`,
|
|
4
5
|
* cached for `ttlMs` (default 24h per API-ROUTE-CONVENTIONS.md §3.1).
|
|
5
6
|
*
|
|
6
7
|
* Injected into the middleware so deployments can plug in a durable
|
|
@@ -10,15 +11,65 @@ import type { MiddlewareHandler } from 'hono';
|
|
|
10
11
|
*/
|
|
11
12
|
export interface IdempotencyStore {
|
|
12
13
|
get(key: string): Promise<StoredIdempotencyEntry | null>;
|
|
14
|
+
/** Store an answer. It replaces a hold on the key, never a stored answer: the first stays. */
|
|
13
15
|
set(key: string, entry: StoredIdempotencyEntry): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Hold a key while its request runs (T349), so a repeat sent meanwhile
|
|
18
|
+
* is answered 409 `idempotency-key-in-flight` instead of running the
|
|
19
|
+
* operation again. Optional: a store without holds holds nothing, and
|
|
20
|
+
* such a repeat runs again (the behaviour before 0.1.5).
|
|
21
|
+
*/
|
|
22
|
+
readonly holds?: IdempotencyHolds;
|
|
14
23
|
}
|
|
24
|
+
/** A store's holds: all three or none. */
|
|
25
|
+
export interface IdempotencyHolds {
|
|
26
|
+
/** Take the key for `hold.holder`, unless an unexpired hold or a stored answer has it. */
|
|
27
|
+
hold(key: string, hold: IdempotencyHold): Promise<IdempotencyHoldOutcome>;
|
|
28
|
+
/** Extend the holder's hold: its request still runs. */
|
|
29
|
+
renew(key: string, holder: string, expiresAt: number): Promise<void>;
|
|
30
|
+
/** Drop the holder's hold: its request ended with nothing to replay. */
|
|
31
|
+
release(key: string, holder: string): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
export interface IdempotencyHold {
|
|
34
|
+
/** Who holds it: one id per request. */
|
|
35
|
+
readonly holder: string;
|
|
36
|
+
readonly bodyHash: string;
|
|
37
|
+
readonly expiresAt: number;
|
|
38
|
+
}
|
|
39
|
+
export type IdempotencyHoldOutcome = {
|
|
40
|
+
readonly kind: 'held';
|
|
41
|
+
} | {
|
|
42
|
+
readonly kind: 'in-flight';
|
|
43
|
+
readonly bodyHash: string;
|
|
44
|
+
} | {
|
|
45
|
+
readonly kind: 'stored';
|
|
46
|
+
readonly entry: StoredIdempotencyEntry;
|
|
47
|
+
};
|
|
15
48
|
export interface StoredIdempotencyEntry {
|
|
16
49
|
readonly bodyHash: string;
|
|
17
50
|
readonly status: number;
|
|
18
51
|
readonly contentType: string;
|
|
52
|
+
/** The answer's body; empty when it's `withheld`. */
|
|
19
53
|
readonly bodyText: string;
|
|
20
54
|
readonly expiresAt: number;
|
|
55
|
+
/**
|
|
56
|
+
* The answer carried a secret (`withholdFromReplay`), so only that the
|
|
57
|
+
* request succeeded is kept: a repeat is answered 409
|
|
58
|
+
* `idempotency-key-replay-withheld`, never the secret. Optional: a store
|
|
59
|
+
* that doesn't keep it stores the empty body, and a repeat replays that.
|
|
60
|
+
*/
|
|
61
|
+
readonly withheld?: boolean;
|
|
62
|
+
/** When the answer was stored (ms since the epoch), for the withheld 409. */
|
|
63
|
+
readonly storedAt?: number;
|
|
21
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* For a route whose answer carries a secret (an API key, a session token,
|
|
67
|
+
* a signing secret): the Idempotency-Key middleware keeps that the request
|
|
68
|
+
* succeeded and when, never the answer. A repeat with the same key gets 409
|
|
69
|
+
* `idempotency-key-replay-withheld` instead of the secret, and instead of
|
|
70
|
+
* running again (which would make a second one). Call it before answering.
|
|
71
|
+
*/
|
|
72
|
+
export declare function withholdFromReplay(c: Context<AppEnv>): void;
|
|
22
73
|
/** Simple in-memory store with lazy expiry — swap for a DB store in prod. */
|
|
23
74
|
export declare function createInMemoryIdempotencyStore(): IdempotencyStore;
|
|
24
75
|
/**
|
|
@@ -36,12 +87,26 @@ export declare function createInMemoryIdempotencyStore(): IdempotencyStore;
|
|
|
36
87
|
* nothing, and a failure (5xx) may be transient: neither is stored, so
|
|
37
88
|
* a retry with the same key after fixing the cause runs again instead
|
|
38
89
|
* of replaying the old error.
|
|
90
|
+
* - With a store that holds keys (`store.holds`, T349): the key is held
|
|
91
|
+
* while the handler runs (for `holdMs`, renewed every third of it), so
|
|
92
|
+
* a repeat sent meanwhile gets 409 `idempotency-key-in-flight` with
|
|
93
|
+
* `Retry-After`, or `idempotency-key-body-mismatch` for another body,
|
|
94
|
+
* instead of running the operation again. The hold becomes the stored
|
|
95
|
+
* answer, or is released when nothing is stored. A crashed request's
|
|
96
|
+
* hold lapses within `holdMs`.
|
|
97
|
+
*
|
|
98
|
+
* The middleware sits AFTER auth so `tenantId` is available. The cache
|
|
99
|
+
* key is the tenant, the caller (`user:…`, `service_account:…`, else the
|
|
100
|
+
* key or session it came with), the route and the key: someone else in
|
|
101
|
+
* the tenant who sends the same key and body runs the request themselves,
|
|
102
|
+
* and never gets another caller's answer (T392).
|
|
39
103
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
104
|
+
* A route whose answer carries a secret calls `withholdFromReplay`: its
|
|
105
|
+
* answer isn't kept, only that it succeeded (status, when). A repeat with
|
|
106
|
+
* the same key and body gets 409 `idempotency-key-replay-withheld`.
|
|
43
107
|
*/
|
|
44
108
|
export declare function idempotencyMiddleware(store: IdempotencyStore, opts?: {
|
|
45
109
|
ttlMs?: number;
|
|
110
|
+
holdMs?: number;
|
|
46
111
|
}): MiddlewareHandler;
|
|
47
112
|
//# sourceMappingURL=idempotency.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../../src/middleware/idempotency.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../../src/middleware/idempotency.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAMvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAAC;IACzD,8FAA8F;IAC9F,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;CACnC;AAED,0CAA0C;AAC1C,MAAM,WAAW,gBAAgB;IAC/B,0FAA0F;IAC1F,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC1E,wDAAwD;IACxD,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,wEAAwE;IACxE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,eAAe;IAC9B,wCAAwC;IACxC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,MAAM,sBAAsB,GAC9B;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACzD;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,sBAAsB,CAAA;CAAE,CAAC;AAExE,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,CAE3D;AAED,6EAA6E;AAC7E,wBAAgB,8BAA8B,IAAI,gBAAgB,CAwCjE;AASD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,gBAAgB,EACvB,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAC7C,iBAAiB,CA8FnB"}
|
|
@@ -1,28 +1,71 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// Copyright (C) 2026 Kindgi Inc.
|
|
3
|
-
import { createHash } from 'node:crypto';
|
|
3
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
4
|
+
import { callerRef } from '../caller.js';
|
|
4
5
|
import { statusFor, toWireError } from '../errors.js';
|
|
6
|
+
/**
|
|
7
|
+
* For a route whose answer carries a secret (an API key, a session token,
|
|
8
|
+
* a signing secret): the Idempotency-Key middleware keeps that the request
|
|
9
|
+
* succeeded and when, never the answer. A repeat with the same key gets 409
|
|
10
|
+
* `idempotency-key-replay-withheld` instead of the secret, and instead of
|
|
11
|
+
* running again (which would make a second one). Call it before answering.
|
|
12
|
+
*/
|
|
13
|
+
export function withholdFromReplay(c) {
|
|
14
|
+
c.set('idempotencyWithhold', true);
|
|
15
|
+
}
|
|
5
16
|
/** Simple in-memory store with lazy expiry — swap for a DB store in prod. */
|
|
6
17
|
export function createInMemoryIdempotencyStore() {
|
|
7
18
|
const table = new Map();
|
|
19
|
+
const held = new Map();
|
|
20
|
+
const stored = (key) => {
|
|
21
|
+
const hit = table.get(key);
|
|
22
|
+
if (hit === undefined)
|
|
23
|
+
return null;
|
|
24
|
+
if (hit.expiresAt <= Date.now()) {
|
|
25
|
+
table.delete(key);
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
return hit;
|
|
29
|
+
};
|
|
8
30
|
return {
|
|
9
31
|
async get(key) {
|
|
10
|
-
|
|
11
|
-
if (hit === undefined)
|
|
12
|
-
return null;
|
|
13
|
-
if (hit.expiresAt <= Date.now()) {
|
|
14
|
-
table.delete(key);
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
return hit;
|
|
32
|
+
return stored(key);
|
|
18
33
|
},
|
|
19
34
|
async set(key, entry) {
|
|
20
|
-
|
|
35
|
+
held.delete(key);
|
|
36
|
+
if (stored(key) === null)
|
|
37
|
+
table.set(key, entry);
|
|
38
|
+
},
|
|
39
|
+
holds: {
|
|
40
|
+
async hold(key, hold) {
|
|
41
|
+
const entry = stored(key);
|
|
42
|
+
if (entry !== null)
|
|
43
|
+
return { kind: 'stored', entry };
|
|
44
|
+
const current = held.get(key);
|
|
45
|
+
if (current !== undefined && current.expiresAt > Date.now()) {
|
|
46
|
+
return { kind: 'in-flight', bodyHash: current.bodyHash };
|
|
47
|
+
}
|
|
48
|
+
held.set(key, hold);
|
|
49
|
+
return { kind: 'held' };
|
|
50
|
+
},
|
|
51
|
+
async renew(key, holder, expiresAt) {
|
|
52
|
+
const current = held.get(key);
|
|
53
|
+
if (current?.holder === holder)
|
|
54
|
+
held.set(key, { ...current, expiresAt });
|
|
55
|
+
},
|
|
56
|
+
async release(key, holder) {
|
|
57
|
+
if (held.get(key)?.holder === holder)
|
|
58
|
+
held.delete(key);
|
|
59
|
+
},
|
|
21
60
|
},
|
|
22
61
|
};
|
|
23
62
|
}
|
|
24
63
|
const MUTATING_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE']);
|
|
25
64
|
const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
|
|
65
|
+
/** How long a hold lasts unless renewed; renewed every third of it while the request runs. */
|
|
66
|
+
const DEFAULT_HOLD_MS = 30 * 1000;
|
|
67
|
+
/** What a repeat in flight is told to wait, in seconds. */
|
|
68
|
+
const IN_FLIGHT_RETRY_AFTER_S = 5;
|
|
26
69
|
/**
|
|
27
70
|
* Idempotency-Key middleware per API-ROUTE-CONVENTIONS.md §3.1.
|
|
28
71
|
*
|
|
@@ -38,13 +81,27 @@ const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
|
|
|
38
81
|
* nothing, and a failure (5xx) may be transient: neither is stored, so
|
|
39
82
|
* a retry with the same key after fixing the cause runs again instead
|
|
40
83
|
* of replaying the old error.
|
|
84
|
+
* - With a store that holds keys (`store.holds`, T349): the key is held
|
|
85
|
+
* while the handler runs (for `holdMs`, renewed every third of it), so
|
|
86
|
+
* a repeat sent meanwhile gets 409 `idempotency-key-in-flight` with
|
|
87
|
+
* `Retry-After`, or `idempotency-key-body-mismatch` for another body,
|
|
88
|
+
* instead of running the operation again. The hold becomes the stored
|
|
89
|
+
* answer, or is released when nothing is stored. A crashed request's
|
|
90
|
+
* hold lapses within `holdMs`.
|
|
91
|
+
*
|
|
92
|
+
* The middleware sits AFTER auth so `tenantId` is available. The cache
|
|
93
|
+
* key is the tenant, the caller (`user:…`, `service_account:…`, else the
|
|
94
|
+
* key or session it came with), the route and the key: someone else in
|
|
95
|
+
* the tenant who sends the same key and body runs the request themselves,
|
|
96
|
+
* and never gets another caller's answer (T392).
|
|
41
97
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
98
|
+
* A route whose answer carries a secret calls `withholdFromReplay`: its
|
|
99
|
+
* answer isn't kept, only that it succeeded (status, when). A repeat with
|
|
100
|
+
* the same key and body gets 409 `idempotency-key-replay-withheld`.
|
|
45
101
|
*/
|
|
46
102
|
export function idempotencyMiddleware(store, opts = {}) {
|
|
47
103
|
const ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;
|
|
104
|
+
const holdMs = opts.holdMs ?? DEFAULT_HOLD_MS;
|
|
48
105
|
return async (c, next) => {
|
|
49
106
|
if (!MUTATING_METHODS.has(c.req.method.toUpperCase())) {
|
|
50
107
|
return next();
|
|
@@ -62,43 +119,119 @@ export function idempotencyMiddleware(store, opts = {}) {
|
|
|
62
119
|
return next();
|
|
63
120
|
}
|
|
64
121
|
const bodyText = await readRawBody(c.req.raw);
|
|
65
|
-
const bodyHash =
|
|
66
|
-
const cacheKey = `${tenantId}|${c.req.method.toUpperCase()}:${c.req.path}|${rawKey}`;
|
|
122
|
+
const bodyHash = sha256Hex(bodyText);
|
|
123
|
+
const cacheKey = `${tenantId}|${callerKey(c)}|${c.req.method.toUpperCase()}:${c.req.path}|${rawKey}`;
|
|
67
124
|
const hit = await store.get(cacheKey);
|
|
68
|
-
if (hit !== null)
|
|
69
|
-
|
|
70
|
-
|
|
125
|
+
if (hit !== null)
|
|
126
|
+
return replay(c, hit, bodyHash, requestId, ttlMs);
|
|
127
|
+
const { holds } = store;
|
|
128
|
+
const holder = randomUUID();
|
|
129
|
+
if (holds !== undefined) {
|
|
130
|
+
const held = await holds.hold(cacheKey, {
|
|
131
|
+
holder,
|
|
132
|
+
bodyHash,
|
|
133
|
+
expiresAt: Date.now() + holdMs,
|
|
134
|
+
});
|
|
135
|
+
if (held.kind === 'stored')
|
|
136
|
+
return replay(c, held.entry, bodyHash, requestId, ttlMs);
|
|
137
|
+
if (held.kind === 'in-flight') {
|
|
138
|
+
if (held.bodyHash !== bodyHash)
|
|
139
|
+
return bodyMismatch(c, requestId);
|
|
140
|
+
c.header('Retry-After', String(IN_FLIGHT_RETRY_AFTER_S));
|
|
141
|
+
c.status(statusFor('idempotency-key-in-flight'));
|
|
71
142
|
return c.json(toWireError({
|
|
72
|
-
code: 'idempotency-key-
|
|
73
|
-
message:
|
|
143
|
+
code: 'idempotency-key-in-flight',
|
|
144
|
+
message: "A request with this Idempotency-Key is still running. Retry after it answers (Retry-After), and you'll get its answer.",
|
|
74
145
|
}, requestId));
|
|
75
146
|
}
|
|
76
|
-
return c.body(hit.bodyText, hit.status, {
|
|
77
|
-
'Content-Type': hit.contentType,
|
|
78
|
-
'X-Idempotent-Replay': 'true',
|
|
79
|
-
});
|
|
80
147
|
}
|
|
81
148
|
// Re-attach the buffered body so downstream handlers reading
|
|
82
149
|
// `c.req.json()` see the same bytes.
|
|
83
150
|
if (bodyText.length > 0)
|
|
84
151
|
rebindBody(c, bodyText);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
152
|
+
const renewal = holds === undefined
|
|
153
|
+
? undefined
|
|
154
|
+
: setInterval(() => {
|
|
155
|
+
holds.renew(cacheKey, holder, Date.now() + holdMs).catch(() => undefined);
|
|
156
|
+
}, Math.max(1, Math.floor(holdMs / 3)));
|
|
157
|
+
renewal?.unref?.();
|
|
158
|
+
let answered = false;
|
|
159
|
+
try {
|
|
160
|
+
await next();
|
|
161
|
+
const res = c.res;
|
|
162
|
+
// Only what took effect is replayed; see the doc above.
|
|
163
|
+
if (res.status >= 400)
|
|
164
|
+
return;
|
|
165
|
+
const withheld = c.get('idempotencyWithhold') === true;
|
|
166
|
+
const savedBody = withheld ? '' : await res.clone().text();
|
|
167
|
+
const contentType = res.headers.get('Content-Type') ?? 'application/octet-stream';
|
|
168
|
+
const now = Date.now();
|
|
169
|
+
await store.set(cacheKey, {
|
|
170
|
+
bodyHash,
|
|
171
|
+
status: res.status,
|
|
172
|
+
contentType,
|
|
173
|
+
bodyText: savedBody,
|
|
174
|
+
expiresAt: now + ttlMs,
|
|
175
|
+
...(withheld && { withheld: true }),
|
|
176
|
+
storedAt: now,
|
|
177
|
+
});
|
|
178
|
+
answered = true;
|
|
179
|
+
}
|
|
180
|
+
finally {
|
|
181
|
+
if (renewal !== undefined)
|
|
182
|
+
clearInterval(renewal);
|
|
183
|
+
// Nothing stored (a refusal, a failure, a throw): let a retry run.
|
|
184
|
+
if (!answered && holds !== undefined) {
|
|
185
|
+
await holds.release(cacheKey, holder).catch(() => undefined);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
100
188
|
};
|
|
101
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* Who the request comes from, for the cache key: the caller's principal
|
|
192
|
+
* (`user:…`, `service_account:…`; an API key with neither is its own
|
|
193
|
+
* service account), else the session it came with, else the credential
|
|
194
|
+
* itself (`token:` and the first 16 hex of its sha256: one bucket per
|
|
195
|
+
* credential, never a shared one, and nothing a reader could use).
|
|
196
|
+
*/
|
|
197
|
+
function callerKey(c) {
|
|
198
|
+
const ref = callerRef(c);
|
|
199
|
+
if (ref !== undefined)
|
|
200
|
+
return ref;
|
|
201
|
+
const sessionId = c.get('sessionId');
|
|
202
|
+
if (sessionId !== undefined)
|
|
203
|
+
return `session:${sessionId}`;
|
|
204
|
+
return `token:${sha256Hex(c.req.header('authorization') ?? '').slice(0, 16)}`;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* A stored answer, replayed; or the refusal for the same key with another
|
|
208
|
+
* body; or, for an answer that carried a secret, what succeeded and when.
|
|
209
|
+
*/
|
|
210
|
+
function replay(c, hit, bodyHash, requestId, ttlMs) {
|
|
211
|
+
if (hit.bodyHash !== bodyHash)
|
|
212
|
+
return bodyMismatch(c, requestId);
|
|
213
|
+
if (hit.withheld === true) {
|
|
214
|
+
const at = new Date(hit.storedAt ?? hit.expiresAt - ttlMs).toISOString();
|
|
215
|
+
c.status(statusFor('idempotency-key-replay-withheld'));
|
|
216
|
+
return c.json(toWireError({
|
|
217
|
+
code: 'idempotency-key-replay-withheld',
|
|
218
|
+
message: `The request with this Idempotency-Key succeeded (${hit.status}, at ${at}). Its answer carried a secret, which isn't kept, so it can't be sent again: read what it made (for an API key, \`GET /v1/tokens\`), or make a new one with a fresh key.`,
|
|
219
|
+
status: hit.status,
|
|
220
|
+
at,
|
|
221
|
+
}, requestId));
|
|
222
|
+
}
|
|
223
|
+
return c.body(hit.bodyText, hit.status, {
|
|
224
|
+
'Content-Type': hit.contentType,
|
|
225
|
+
'X-Idempotent-Replay': 'true',
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
function bodyMismatch(c, requestId) {
|
|
229
|
+
c.status(statusFor('idempotency-key-body-mismatch'));
|
|
230
|
+
return c.json(toWireError({
|
|
231
|
+
code: 'idempotency-key-body-mismatch',
|
|
232
|
+
message: 'Idempotency-Key was reused with a different request body. Use a fresh key or the original body.',
|
|
233
|
+
}, requestId));
|
|
234
|
+
}
|
|
102
235
|
async function readRawBody(req) {
|
|
103
236
|
const method = req.method.toUpperCase();
|
|
104
237
|
if (method === 'GET' || method === 'HEAD')
|
|
@@ -110,8 +243,8 @@ async function readRawBody(req) {
|
|
|
110
243
|
return '';
|
|
111
244
|
}
|
|
112
245
|
}
|
|
113
|
-
function
|
|
114
|
-
return createHash('sha256').update(
|
|
246
|
+
function sha256Hex(text) {
|
|
247
|
+
return createHash('sha256').update(text).digest('hex');
|
|
115
248
|
}
|
|
116
249
|
function rebindBody(c, body) {
|
|
117
250
|
const original = c.req.raw;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"idempotency.js","sourceRoot":"","sources":["../../src/middleware/idempotency.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,iCAAiC;AAEjC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"idempotency.js","sourceRoot":"","sources":["../../src/middleware/idempotency.ts"],"names":[],"mappings":"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"}
|