@kindgi/api 0.1.4-rc.0 → 0.1.4-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. package/dist/agent-binding.d.ts +20 -2
  2. package/dist/agent-binding.d.ts.map +1 -1
  3. package/dist/agent-pins.d.ts.map +1 -1
  4. package/dist/agent-pins.js +4 -1
  5. package/dist/agent-pins.js.map +1 -1
  6. package/dist/app.d.ts +8 -0
  7. package/dist/app.d.ts.map +1 -1
  8. package/dist/app.js +30 -5
  9. package/dist/app.js.map +1 -1
  10. package/dist/deploy-versions.d.ts +10 -5
  11. package/dist/deploy-versions.d.ts.map +1 -1
  12. package/dist/deploy-versions.js +4 -10
  13. package/dist/deploy-versions.js.map +1 -1
  14. package/dist/derive-agent-version.d.ts +9 -1
  15. package/dist/derive-agent-version.d.ts.map +1 -1
  16. package/dist/derive-agent-version.js +11 -1
  17. package/dist/derive-agent-version.js.map +1 -1
  18. package/dist/errors.d.ts.map +1 -1
  19. package/dist/errors.js +26 -0
  20. package/dist/errors.js.map +1 -1
  21. package/dist/eval-case-binding.d.ts +10 -0
  22. package/dist/eval-case-binding.d.ts.map +1 -1
  23. package/dist/eval-run-binding.d.ts +21 -3
  24. package/dist/eval-run-binding.d.ts.map +1 -1
  25. package/dist/eval-run-binding.js.map +1 -1
  26. package/dist/eval-run-dispatcher.d.ts +3 -0
  27. package/dist/eval-run-dispatcher.d.ts.map +1 -1
  28. package/dist/eval-run-dispatcher.js.map +1 -1
  29. package/dist/flow-binding.d.ts +8 -0
  30. package/dist/flow-binding.d.ts.map +1 -1
  31. package/dist/flow-pins.d.ts +20 -7
  32. package/dist/flow-pins.d.ts.map +1 -1
  33. package/dist/flow-pins.js +36 -11
  34. package/dist/flow-pins.js.map +1 -1
  35. package/dist/gate-policy-binding.d.ts +164 -0
  36. package/dist/gate-policy-binding.d.ts.map +1 -0
  37. package/dist/gate-policy-binding.js +12 -0
  38. package/dist/gate-policy-binding.js.map +1 -0
  39. package/dist/gate.d.ts +56 -0
  40. package/dist/gate.d.ts.map +1 -0
  41. package/dist/gate.js +359 -0
  42. package/dist/gate.js.map +1 -0
  43. package/dist/guardrail-binding.d.ts +8 -0
  44. package/dist/guardrail-binding.d.ts.map +1 -1
  45. package/dist/handler-binding.d.ts +17 -2
  46. package/dist/handler-binding.d.ts.map +1 -1
  47. package/dist/index.d.ts +11 -4
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +3 -1
  50. package/dist/index.js.map +1 -1
  51. package/dist/judged-dispatcher.d.ts +20 -1
  52. package/dist/judged-dispatcher.d.ts.map +1 -1
  53. package/dist/judged-dispatcher.js +57 -7
  54. package/dist/judged-dispatcher.js.map +1 -1
  55. package/dist/judgment-binding.d.ts +38 -0
  56. package/dist/judgment-binding.d.ts.map +1 -1
  57. package/dist/judgment-binding.js +19 -0
  58. package/dist/judgment-binding.js.map +1 -1
  59. package/dist/live-version-binding.d.ts +206 -0
  60. package/dist/live-version-binding.d.ts.map +1 -0
  61. package/dist/live-version-binding.js +4 -0
  62. package/dist/live-version-binding.js.map +1 -0
  63. package/dist/middleware/authorize.d.ts.map +1 -1
  64. package/dist/middleware/authorize.js +13 -2
  65. package/dist/middleware/authorize.js.map +1 -1
  66. package/dist/middleware/project-ref.d.ts +25 -0
  67. package/dist/middleware/project-ref.d.ts.map +1 -0
  68. package/dist/middleware/project-ref.js +72 -0
  69. package/dist/middleware/project-ref.js.map +1 -0
  70. package/dist/openapi/generate.d.ts.map +1 -1
  71. package/dist/openapi/generate.js +4 -0
  72. package/dist/openapi/generate.js.map +1 -1
  73. package/dist/openapi/operations.d.ts +6 -0
  74. package/dist/openapi/operations.d.ts.map +1 -1
  75. package/dist/openapi/operations.js +524 -27
  76. package/dist/openapi/operations.js.map +1 -1
  77. package/dist/openapi/schemas.d.ts +43 -0
  78. package/dist/openapi/schemas.d.ts.map +1 -1
  79. package/dist/openapi/schemas.js +1050 -7
  80. package/dist/openapi/schemas.js.map +1 -1
  81. package/dist/publish-refused.d.ts +18 -0
  82. package/dist/publish-refused.d.ts.map +1 -0
  83. package/dist/publish-refused.js +22 -0
  84. package/dist/publish-refused.js.map +1 -0
  85. package/dist/registry-read-only.d.ts +32 -0
  86. package/dist/registry-read-only.d.ts.map +1 -0
  87. package/dist/registry-read-only.js +22 -0
  88. package/dist/registry-read-only.js.map +1 -0
  89. package/dist/retention-binding.d.ts +29 -0
  90. package/dist/retention-binding.d.ts.map +1 -1
  91. package/dist/routes/agent-releases.d.ts +40 -0
  92. package/dist/routes/agent-releases.d.ts.map +1 -0
  93. package/dist/routes/agent-releases.js +462 -0
  94. package/dist/routes/agent-releases.js.map +1 -0
  95. package/dist/routes/agents.d.ts +7 -1
  96. package/dist/routes/agents.d.ts.map +1 -1
  97. package/dist/routes/agents.js +38 -4
  98. package/dist/routes/agents.js.map +1 -1
  99. package/dist/routes/approvals.d.ts.map +1 -1
  100. package/dist/routes/approvals.js +27 -9
  101. package/dist/routes/approvals.js.map +1 -1
  102. package/dist/routes/audit.d.ts.map +1 -1
  103. package/dist/routes/audit.js +7 -0
  104. package/dist/routes/audit.js.map +1 -1
  105. package/dist/routes/auth.js +1 -1
  106. package/dist/routes/auth.js.map +1 -1
  107. package/dist/routes/blocks.d.ts.map +1 -1
  108. package/dist/routes/blocks.js +37 -12
  109. package/dist/routes/blocks.js.map +1 -1
  110. package/dist/routes/conversations.d.ts +0 -7
  111. package/dist/routes/conversations.d.ts.map +1 -1
  112. package/dist/routes/conversations.js +19 -3
  113. package/dist/routes/conversations.js.map +1 -1
  114. package/dist/routes/deployments.d.ts +6 -0
  115. package/dist/routes/deployments.d.ts.map +1 -1
  116. package/dist/routes/deployments.js +49 -5
  117. package/dist/routes/deployments.js.map +1 -1
  118. package/dist/routes/env.d.ts.map +1 -1
  119. package/dist/routes/env.js +1 -0
  120. package/dist/routes/env.js.map +1 -1
  121. package/dist/routes/eval-comparison.d.ts +4 -2
  122. package/dist/routes/eval-comparison.d.ts.map +1 -1
  123. package/dist/routes/eval-comparison.js +59 -10
  124. package/dist/routes/eval-comparison.js.map +1 -1
  125. package/dist/routes/eval-runs.d.ts +7 -1
  126. package/dist/routes/eval-runs.d.ts.map +1 -1
  127. package/dist/routes/eval-runs.js +34 -3
  128. package/dist/routes/eval-runs.js.map +1 -1
  129. package/dist/routes/eval-versions.d.ts +25 -0
  130. package/dist/routes/eval-versions.d.ts.map +1 -0
  131. package/dist/routes/eval-versions.js +66 -0
  132. package/dist/routes/eval-versions.js.map +1 -0
  133. package/dist/routes/flows.d.ts +14 -7
  134. package/dist/routes/flows.d.ts.map +1 -1
  135. package/dist/routes/flows.js +14 -8
  136. package/dist/routes/flows.js.map +1 -1
  137. package/dist/routes/gate-policies.d.ts +19 -0
  138. package/dist/routes/gate-policies.d.ts.map +1 -0
  139. package/dist/routes/gate-policies.js +191 -0
  140. package/dist/routes/gate-policies.js.map +1 -0
  141. package/dist/routes/gate-policy-spec.d.ts +17 -0
  142. package/dist/routes/gate-policy-spec.d.ts.map +1 -0
  143. package/dist/routes/gate-policy-spec.js +192 -0
  144. package/dist/routes/gate-policy-spec.js.map +1 -0
  145. package/dist/routes/gate-policy-wire.d.ts +3 -0
  146. package/dist/routes/gate-policy-wire.d.ts.map +1 -0
  147. package/dist/routes/gate-policy-wire.js +18 -0
  148. package/dist/routes/gate-policy-wire.js.map +1 -0
  149. package/dist/routes/guardrails.d.ts.map +1 -1
  150. package/dist/routes/guardrails.js +4 -0
  151. package/dist/routes/guardrails.js.map +1 -1
  152. package/dist/routes/hierarchy-errors.d.ts +21 -0
  153. package/dist/routes/hierarchy-errors.d.ts.map +1 -1
  154. package/dist/routes/hierarchy-errors.js +21 -0
  155. package/dist/routes/hierarchy-errors.js.map +1 -1
  156. package/dist/routes/judged-suites.js +7 -0
  157. package/dist/routes/judged-suites.js.map +1 -1
  158. package/dist/routes/judgments.d.ts +4 -1
  159. package/dist/routes/judgments.d.ts.map +1 -1
  160. package/dist/routes/judgments.js +92 -19
  161. package/dist/routes/judgments.js.map +1 -1
  162. package/dist/routes/live-scope-wire.d.ts +32 -0
  163. package/dist/routes/live-scope-wire.d.ts.map +1 -0
  164. package/dist/routes/live-scope-wire.js +71 -0
  165. package/dist/routes/live-scope-wire.js.map +1 -0
  166. package/dist/routes/policies.d.ts +6 -3
  167. package/dist/routes/policies.d.ts.map +1 -1
  168. package/dist/routes/policies.js +55 -3
  169. package/dist/routes/policies.js.map +1 -1
  170. package/dist/routes/projects.d.ts.map +1 -1
  171. package/dist/routes/projects.js +59 -9
  172. package/dist/routes/projects.js.map +1 -1
  173. package/dist/routes/retention.d.ts.map +1 -1
  174. package/dist/routes/retention.js +18 -2
  175. package/dist/routes/retention.js.map +1 -1
  176. package/dist/routes/reviewers.d.ts +8 -1
  177. package/dist/routes/reviewers.d.ts.map +1 -1
  178. package/dist/routes/reviewers.js +20 -3
  179. package/dist/routes/reviewers.js.map +1 -1
  180. package/dist/routes/runs.d.ts.map +1 -1
  181. package/dist/routes/runs.js +23 -13
  182. package/dist/routes/runs.js.map +1 -1
  183. package/dist/routes/secrets.d.ts.map +1 -1
  184. package/dist/routes/secrets.js +2 -0
  185. package/dist/routes/secrets.js.map +1 -1
  186. package/dist/routes/segments.d.ts +17 -0
  187. package/dist/routes/segments.d.ts.map +1 -0
  188. package/dist/routes/segments.js +68 -0
  189. package/dist/routes/segments.js.map +1 -0
  190. package/dist/routes/teams.d.ts.map +1 -1
  191. package/dist/routes/teams.js +53 -8
  192. package/dist/routes/teams.js.map +1 -1
  193. package/dist/routes/tools.d.ts.map +1 -1
  194. package/dist/routes/tools.js +4 -0
  195. package/dist/routes/tools.js.map +1 -1
  196. package/dist/routes/uuid-param.d.ts +11 -0
  197. package/dist/routes/uuid-param.d.ts.map +1 -0
  198. package/dist/routes/uuid-param.js +19 -0
  199. package/dist/routes/uuid-param.js.map +1 -0
  200. package/dist/tool-binding.d.ts +8 -0
  201. package/dist/tool-binding.d.ts.map +1 -1
  202. package/openapi.json +6484 -2402
  203. package/package.json +21 -21
  204. package/src/agent-binding.ts +21 -2
  205. package/src/agent-pins.ts +3 -1
  206. package/src/app.ts +47 -4
  207. package/src/deploy-versions.ts +12 -15
  208. package/src/derive-agent-version.ts +12 -1
  209. package/src/errors.ts +26 -0
  210. package/src/eval-case-binding.ts +7 -0
  211. package/src/eval-run-binding.ts +31 -3
  212. package/src/eval-run-dispatcher.ts +3 -0
  213. package/src/flow-binding.ts +9 -0
  214. package/src/flow-pins.ts +49 -9
  215. package/src/gate-policy-binding.ts +171 -0
  216. package/src/gate.ts +467 -0
  217. package/src/guardrail-binding.ts +9 -0
  218. package/src/handler-binding.ts +17 -2
  219. package/src/index.ts +47 -1
  220. package/src/judged-dispatcher.ts +100 -9
  221. package/src/judgment-binding.ts +62 -0
  222. package/src/live-version-binding.ts +237 -0
  223. package/src/middleware/authorize.ts +18 -2
  224. package/src/middleware/project-ref.ts +88 -0
  225. package/src/openapi/generate.ts +5 -0
  226. package/src/openapi/operations.ts +664 -27
  227. package/src/openapi/schemas.ts +1175 -33
  228. package/src/publish-refused.ts +31 -0
  229. package/src/registry-read-only.ts +43 -0
  230. package/src/retention-binding.ts +30 -0
  231. package/src/routes/agent-releases.ts +581 -0
  232. package/src/routes/agents.ts +53 -3
  233. package/src/routes/approvals.ts +36 -8
  234. package/src/routes/audit.ts +10 -0
  235. package/src/routes/auth.ts +1 -1
  236. package/src/routes/blocks.ts +39 -14
  237. package/src/routes/conversations.ts +31 -3
  238. package/src/routes/deployments.ts +75 -5
  239. package/src/routes/env.ts +1 -0
  240. package/src/routes/eval-comparison.ts +59 -11
  241. package/src/routes/eval-runs.ts +46 -3
  242. package/src/routes/eval-versions.ts +110 -0
  243. package/src/routes/flows.ts +34 -11
  244. package/src/routes/gate-policies.ts +226 -0
  245. package/src/routes/gate-policy-spec.ts +229 -0
  246. package/src/routes/gate-policy-wire.ts +20 -0
  247. package/src/routes/guardrails.ts +7 -0
  248. package/src/routes/hierarchy-errors.ts +23 -0
  249. package/src/routes/judged-suites.ts +5 -0
  250. package/src/routes/judgments.ts +107 -23
  251. package/src/routes/live-scope-wire.ts +90 -0
  252. package/src/routes/policies.ts +61 -3
  253. package/src/routes/projects.ts +72 -9
  254. package/src/routes/retention.ts +21 -2
  255. package/src/routes/reviewers.ts +23 -4
  256. package/src/routes/runs.ts +34 -20
  257. package/src/routes/secrets.ts +2 -0
  258. package/src/routes/segments.ts +75 -0
  259. package/src/routes/teams.ts +66 -8
  260. package/src/routes/tools.ts +7 -0
  261. package/src/routes/uuid-param.ts +28 -0
  262. package/src/tool-binding.ts +9 -0
@@ -91,6 +91,13 @@ const RunReplaysQueryParam = {
91
91
  description: 'Replay runs (an eval run re-running a past run). `exclude` (default) leaves them out; `include` lists them with the other runs; `only` lists just them.',
92
92
  schema: { type: 'string', enum: ['exclude', 'include', 'only'], default: 'exclude' },
93
93
  };
94
+ const ConversationReplaysQueryParam = {
95
+ name: 'replays',
96
+ in: 'query',
97
+ required: false,
98
+ description: "Replay conversations (opened by a comparison's replay turn; `metadata.replayOf` names the run it replays). `exclude` (default) leaves them out; `include` lists them with the others; `only` lists just them.",
99
+ schema: { type: 'string', enum: ['exclude', 'include', 'only'], default: 'exclude' },
100
+ };
94
101
  const RunEvalRunIdQueryParam = {
95
102
  name: 'evalRunId',
96
103
  in: 'query',
@@ -98,6 +105,34 @@ const RunEvalRunIdQueryParam = {
98
105
  description: 'Only the replay runs of this eval run. Implies replays are included; cannot be combined with `replays=exclude`.',
99
106
  schema: { type: 'string', minLength: 1 },
100
107
  };
108
+ const LiveProjectQueryParam = {
109
+ name: 'projectId',
110
+ in: 'query',
111
+ required: false,
112
+ description: "The run's project; omit → only the agent's tenant-wide pin applies.",
113
+ schema: { type: 'string', format: 'uuid' },
114
+ };
115
+ const SegmentQueryParam = {
116
+ name: 'segment',
117
+ in: 'query',
118
+ required: false,
119
+ description: 'One step of the segment path, `key:value`; repeat it in order, coarse to fine.',
120
+ schema: { type: 'array', items: { type: 'string' } },
121
+ segmentPath: true,
122
+ };
123
+ const PromotionScopeKindQueryParam = {
124
+ name: 'scopeKind',
125
+ in: 'query',
126
+ required: false,
127
+ description: 'Only one scope: `tenant`, `org` or `project` (with `scopeId`), or `segment` (with `scopeId` and `segment`).',
128
+ schema: { type: 'string', enum: ['tenant', 'org', 'project', 'segment'] },
129
+ };
130
+ const PromotionIdPathParam = {
131
+ name: 'promotionId',
132
+ in: 'path',
133
+ required: true,
134
+ schema: { type: 'string', format: 'uuid' },
135
+ };
101
136
  const RunIncludeQueryParam = {
102
137
  name: 'include',
103
138
  in: 'query',
@@ -126,6 +161,20 @@ const LastEventIdParam = {
126
161
  description: 'Resume marker (`<runId>:<sequence>`). Server replays events with `sequence > lastSeen` before entering live-poll.',
127
162
  schema: { type: 'string' },
128
163
  };
164
+ const GatePolicyIdPathParam = {
165
+ name: 'policyId',
166
+ in: 'path',
167
+ required: true,
168
+ description: 'The gate policy id.',
169
+ schema: { type: 'string' },
170
+ };
171
+ const GatePolicyVersionPathParam = {
172
+ name: 'version',
173
+ in: 'path',
174
+ required: true,
175
+ description: 'A semver version of the gate policy.',
176
+ schema: { type: 'string' },
177
+ };
129
178
  const AgentIdPathParam = {
130
179
  name: 'agentId',
131
180
  in: 'path',
@@ -271,6 +320,34 @@ const SupervisorIdQueryParam = {
271
320
  required: false,
272
321
  schema: { type: 'string' },
273
322
  };
323
+ const ObservationAgentVersionQueryParam = {
324
+ name: 'agentVersion',
325
+ in: 'query',
326
+ required: false,
327
+ description: 'Only the observations of this agent version (with `agentId`).',
328
+ schema: { type: 'string' },
329
+ };
330
+ const ObservationConversationIdQueryParam = {
331
+ name: 'conversationId',
332
+ in: 'query',
333
+ required: false,
334
+ description: 'Only the observations of turns in this conversation.',
335
+ schema: { type: 'string' },
336
+ };
337
+ const ObservationSinceQueryParam = {
338
+ name: 'since',
339
+ in: 'query',
340
+ required: false,
341
+ description: 'Only the observations at or after this time (ISO 8601).',
342
+ schema: { type: 'string', format: 'date-time' },
343
+ };
344
+ const ObservationUntilQueryParam = {
345
+ name: 'until',
346
+ in: 'query',
347
+ required: false,
348
+ description: 'Only the observations at or before this time (ISO 8601).',
349
+ schema: { type: 'string', format: 'date-time' },
350
+ };
274
351
  const FactIdPathParam = {
275
352
  name: 'factId',
276
353
  in: 'path',
@@ -345,7 +422,7 @@ const ProvenanceAgentIdQueryParam = {
345
422
  name: 'agentId',
346
423
  in: 'query',
347
424
  required: false,
348
- description: 'Filter records to those with at least one DAG node whose `actor = <agentId>` (agent-driven turns tag their nodes with the agent id).',
425
+ description: "Only the records of this agent's turns, at any version: the agent the record's run names, as `GET /v1/runs?agentId=` matches it. Turns that ran before Kindgi 0.1.3 don't name their agent and aren't matched.",
349
426
  schema: { type: 'string' },
350
427
  };
351
428
  const ProvenanceCreatedAfterQueryParam = {
@@ -603,7 +680,7 @@ const PolicyKindFilterQueryParam = {
603
680
  name: 'kind',
604
681
  in: 'query',
605
682
  required: false,
606
- description: 'Filter to policies of a single kind. Values: `access-control | model-routing | adapter-allowlist | rate-limit | retention | compliance`.',
683
+ description: 'Filter to policies of a single kind. Values: `access-control | model-routing | adapter-allowlist | rate-limit | retention | compliance | tool-errors | hitl`.',
607
684
  schema: { $ref: '#/components/schemas/PolicyKind' },
608
685
  };
609
686
  const PolicyNameFilterQueryParam = {
@@ -613,6 +690,28 @@ const PolicyNameFilterQueryParam = {
613
690
  description: 'Prefix match on `Policy.id`. Dotted namespaces are the natural filter shape.',
614
691
  schema: { type: 'string' },
615
692
  };
693
+ // Admin plane — retention.
694
+ const RetentionDomainQueryParam = {
695
+ name: 'domain',
696
+ in: 'query',
697
+ required: false,
698
+ description: 'Only this domain. Absent: every domain.',
699
+ schema: { $ref: '#/components/schemas/RetentionDomain' },
700
+ };
701
+ const RetentionPastGraceOnlyQueryParam = {
702
+ name: 'pastGraceOnly',
703
+ in: 'query',
704
+ required: false,
705
+ description: '`true`: only rows past their grace, the ones a sweep would purge now.',
706
+ schema: { type: 'boolean', default: false },
707
+ };
708
+ const RetentionDomainPathParam = {
709
+ name: 'domain',
710
+ in: 'path',
711
+ required: true,
712
+ description: 'The domain to sweep (not `*`).',
713
+ schema: { $ref: '#/components/schemas/RetentionDomain' },
714
+ };
616
715
  // Admin plane — eval suites.
617
716
  const EvalSuiteIdPathParam = {
618
717
  name: 'suiteId',
@@ -887,8 +986,9 @@ export const OPERATIONS = [
887
986
  schema: ref('Run'),
888
987
  },
889
988
  ...CommonMutationErrors,
890
- '404': ErrorResponse('Agent or flow not found.'),
989
+ '404': ErrorResponse('Agent or flow not found; or `projectId` names no project of this tenant (`project-not-found`).'),
891
990
  '422': ErrorResponse('Guardrail violation or budget exceeded.'),
991
+ '400': ErrorResponse("Malformed request body, or the body's `projectId` isn't a project id (a UUID)."),
892
992
  },
893
993
  },
894
994
  {
@@ -1144,6 +1244,8 @@ export const OPERATIONS = [
1144
1244
  '201': { description: 'Token minted.', schema: ref('MintTokenResult') },
1145
1245
  ...CommonMutationErrors,
1146
1246
  '403': ErrorResponse('Not a tenant admin, or a capability the caller does not hold.'),
1247
+ '400': ErrorResponse("Malformed request body, or the body's `projectId` isn't a project id (a UUID)."),
1248
+ '404': ErrorResponse("The body's `projectId` names no project of this tenant (`project-not-found`)."),
1147
1249
  },
1148
1250
  },
1149
1251
  {
@@ -1326,6 +1428,7 @@ export const OPERATIONS = [
1326
1428
  responses: {
1327
1429
  '201': { description: 'Reviewer registered.', schema: ref('Reviewer') },
1328
1430
  ...CommonMutationErrors,
1431
+ '403': ErrorResponse('With authorization enforced, the caller is not an admin of the tenant.'),
1329
1432
  },
1330
1433
  },
1331
1434
  {
@@ -1342,6 +1445,7 @@ export const OPERATIONS = [
1342
1445
  '200': { description: 'Unregistered.', schema: ref('UnregisterReviewerResult') },
1343
1446
  ...CommonMutationErrors,
1344
1447
  '404': ErrorResponse('No reviewer with that id under this tenant.'),
1448
+ '403': ErrorResponse('With authorization enforced, the caller is not an admin of the tenant.'),
1345
1449
  },
1346
1450
  },
1347
1451
  {
@@ -1429,10 +1533,15 @@ export const OPERATIONS = [
1429
1533
  parameters: [AgentIdPathParam, IdempotencyKeyParam],
1430
1534
  requestBody: { required: true, schema: ref('DeriveAgentVersionBody') },
1431
1535
  responses: {
1536
+ '200': {
1537
+ description: 'An active version already holds this definition and these pins (the same swap derived before, or a deploy that registered it): that version, unchanged.',
1538
+ schema: ref('Agent'),
1539
+ },
1432
1540
  '201': { description: 'The derived agent version.', schema: ref('Agent') },
1433
1541
  ...CommonMutationErrors,
1434
- '400': ErrorResponse("`validation-failed`: `from` has no pins, a swap names a block it doesn't reference, or a version that isn't published, active or the right kind (see `details.issues`)."),
1435
- '404': ErrorResponse('`agent-not-found`: no agent at `from`.'),
1542
+ '409': ErrorResponse("Idempotency-Key was reused with a different body, or resource-state conflict. Or `registry-read-only`: this registry takes no writes (under `kindgi dev`, the pack's files are the source); the message says what to do instead."),
1543
+ '400': ErrorResponse("`validation-failed`: `from` has no pins, a swap names a block it doesn't reference, or a version that isn't published, active or the right kind (see `details.issues`); or `projectId` isn't a project id (a UUID)."),
1544
+ '404': ErrorResponse('`agent-not-found`: no agent at `from`; or `projectId` names no project of this tenant (`project-not-found`).'),
1436
1545
  },
1437
1546
  },
1438
1547
  {
@@ -1464,8 +1573,9 @@ export const OPERATIONS = [
1464
1573
  responses: {
1465
1574
  '201': { description: 'Agent published.', schema: ref('PublishAgentResult') },
1466
1575
  ...CommonMutationErrors,
1467
- '400': ErrorResponse('Validation failed (see `details.issues`).'),
1468
- '409': ErrorResponse('Agent already registered at that (id, version).'),
1576
+ '400': ErrorResponse("Validation failed (see `details.issues`); or `projectId` isn't a project id (a UUID)."),
1577
+ '409': ErrorResponse("Agent already registered at that (id, version). Or `registry-read-only`: this registry takes no writes (under `kindgi dev`, the pack's files are the source); the message says what to do instead."),
1578
+ '404': ErrorResponse("The body's `projectId` names no project of this tenant (`project-not-found`)."),
1469
1579
  },
1470
1580
  },
1471
1581
  {
@@ -1480,6 +1590,7 @@ export const OPERATIONS = [
1480
1590
  responses: {
1481
1591
  '200': { description: 'Unregistered.', schema: ref('UnregisterAgentResult') },
1482
1592
  ...CommonMutationErrors,
1593
+ '409': ErrorResponse("Idempotency-Key was reused with a different body, or resource-state conflict. Or `registry-read-only`: this registry takes no writes (under `kindgi dev`, the pack's files are the source); the message says what to do instead. Or `agent-version-live`: the version is the live version of the scopes in `details.scopes`; roll back, unpin, or promote another version there first."),
1483
1594
  '404': ErrorResponse('No agent at that (id, version) under this tenant.'),
1484
1595
  },
1485
1596
  },
@@ -1496,9 +1607,304 @@ export const OPERATIONS = [
1496
1607
  responses: {
1497
1608
  '200': { description: 'Reinstated.', schema: ref('ReinstateAgentVersionResult') },
1498
1609
  ...CommonMutationErrors,
1610
+ '409': ErrorResponse("Idempotency-Key was reused with a different body, or resource-state conflict. Or `registry-read-only`: this registry takes no writes (under `kindgi dev`, the pack's files are the source); the message says what to do instead."),
1499
1611
  '404': ErrorResponse('No agent at that (id, version) under this tenant.'),
1500
1612
  },
1501
1613
  },
1614
+ {
1615
+ method: 'get',
1616
+ honoPath: '/v1/agents/:agentId/live',
1617
+ openapiPath: '/v1/agents/{agentId}/live',
1618
+ operationId: 'agents.live.resolve',
1619
+ summary: 'The version a run would use',
1620
+ description: "Resolves the version a run of this agent would use for a project and segment path: the most specific live version (segment path, project, org, tenant), else the latest registered. A run that names its version, or a follow-up turn in a conversation, isn't resolved this way.",
1621
+ tags: ['agents'],
1622
+ security: 'bearer',
1623
+ parameters: [AgentIdPathParam, LiveProjectQueryParam, SegmentQueryParam],
1624
+ responses: {
1625
+ '200': { description: 'The resolved version.', schema: ref('LiveVersionResolution') },
1626
+ ...CommonAuthErrors,
1627
+ '400': ErrorResponse('A malformed project id or segment, or a segment without a project.'),
1628
+ '404': ErrorResponse('No agent with that id.'),
1629
+ },
1630
+ },
1631
+ {
1632
+ method: 'get',
1633
+ honoPath: '/v1/agents/:agentId/live-versions',
1634
+ openapiPath: '/v1/agents/{agentId}/live-versions',
1635
+ operationId: 'agents.live.list',
1636
+ summary: "List an agent's live versions",
1637
+ description: 'Every scope with a live version pinned, and the promotion that set it.',
1638
+ tags: ['agents'],
1639
+ security: 'bearer',
1640
+ parameters: [AgentIdPathParam],
1641
+ responses: {
1642
+ '200': { description: 'The pins.', schema: ref('LivePinList') },
1643
+ ...CommonAuthErrors,
1644
+ },
1645
+ },
1646
+ {
1647
+ method: 'post',
1648
+ honoPath: '/v1/agents/:agentId/promotions',
1649
+ openapiPath: '/v1/agents/{agentId}/promotions',
1650
+ operationId: 'agents.promotions.create',
1651
+ summary: 'Make a version live for a scope',
1652
+ description: "Pins `version` live for `scope`: runs in that scope that don't name a version use it, from the next run. Open conversations keep their version. The version must be registered and active. Every promotion is recorded, with who asked and why. Needs `promote` on the agent.\n\nWith a gate policy for the scope (`GET …/gate-policy`), the promotion is checked first against the comparison named by `evalRunId`: `201` promoted; `202` the gate passed and the policy wants a reviewer's approval (a HITL approval, subject `agent-promotion`; the live version moves once it's approved, if nothing changed meanwhile); `422 gate-failed` with `details.promotionId`, `details.policy` and every check in `details.checks`. A refused promotion is recorded too.",
1653
+ tags: ['agents'],
1654
+ security: 'bearer',
1655
+ parameters: [AgentIdPathParam, IdempotencyKeyParam],
1656
+ requestBody: { required: true, schema: ref('PromoteBody') },
1657
+ responses: {
1658
+ '201': { description: 'Promoted.', schema: ref('Promotion') },
1659
+ '202': {
1660
+ description: 'The gate passed; the promotion waits for an approval (`approvalId`).',
1661
+ schema: ref('Promotion'),
1662
+ },
1663
+ ...CommonMutationErrors,
1664
+ '404': ErrorResponse('The version is not registered, or was unregistered; or the eval run is not found.'),
1665
+ '409': ErrorResponse("`promotion-superseded`: the scope's live version changed while the gate ran; check again."),
1666
+ '422': ErrorResponse('`gate-failed`: the gate refused it. `details.checks` has every check; the refusal is recorded (`details.promotionId`).'),
1667
+ '501': ErrorResponse("`promotion-gate-unsupported`: a gate policy applies, and the deployment can't record a gated promotion."),
1668
+ },
1669
+ },
1670
+ {
1671
+ method: 'post',
1672
+ honoPath: '/v1/agents/:agentId/promotions/check',
1673
+ openapiPath: '/v1/agents/{agentId}/promotions/check',
1674
+ operationId: 'agents.promotions.check',
1675
+ summary: 'Check a promotion against its gate',
1676
+ description: 'What `POST …/promotions` with the same body would do, with nothing recorded: `would-promote`, `needs-approval` (with the approval it needs) or `gate-failed`, with every check. For a pipeline: evaluate, check, promote. Needs `read` on the agent.',
1677
+ tags: ['agents'],
1678
+ security: 'bearer',
1679
+ parameters: [AgentIdPathParam],
1680
+ requestBody: { required: true, schema: ref('PromoteBody') },
1681
+ responses: {
1682
+ '200': { description: "The gate's answer.", schema: ref('PromotionCheck') },
1683
+ ...CommonAuthErrors,
1684
+ '400': ErrorResponse('Malformed body, or the eval run is not a finished comparison.'),
1685
+ '404': ErrorResponse('The version, the agent or the eval run is not found.'),
1686
+ },
1687
+ },
1688
+ {
1689
+ method: 'get',
1690
+ honoPath: '/v1/agents/:agentId/gate-policy',
1691
+ openapiPath: '/v1/agents/{agentId}/gate-policy',
1692
+ operationId: 'agents.gatePolicy.resolve',
1693
+ summary: 'The gate policy for a scope',
1694
+ description: "The gate policy a promotion of the agent for the scope would be checked against: the most specific scope with an active policy (a segment path's longer prefixes first, then its project, the project's org, the tenant), at its latest active version. `policy: null` when none applies.",
1695
+ tags: ['agents'],
1696
+ security: 'bearer',
1697
+ parameters: [
1698
+ AgentIdPathParam,
1699
+ PromotionScopeKindQueryParam,
1700
+ ScopeIdQueryParam,
1701
+ SegmentQueryParam,
1702
+ ],
1703
+ responses: {
1704
+ '200': { description: 'The policy, or null.', schema: ref('GatePolicyResolution') },
1705
+ ...CommonAuthErrors,
1706
+ '400': ErrorResponse('Missing or malformed scope.'),
1707
+ },
1708
+ },
1709
+ {
1710
+ method: 'get',
1711
+ honoPath: '/v1/gate-policies',
1712
+ openapiPath: '/v1/gate-policies',
1713
+ operationId: 'gatePolicies.list',
1714
+ summary: 'List gate policies',
1715
+ description: "Each gate policy's latest active version. `agentId` narrows it to one agent's; `scopeKind` (with `scopeId` and `segment`) to one scope's.",
1716
+ tags: ['gate-policies'],
1717
+ security: 'bearer',
1718
+ parameters: [
1719
+ LimitQueryParam,
1720
+ CursorQueryParam,
1721
+ {
1722
+ name: 'agentId',
1723
+ in: 'query',
1724
+ required: false,
1725
+ description: 'Only the policies gating this agent.',
1726
+ schema: { type: 'string' },
1727
+ },
1728
+ PromotionScopeKindQueryParam,
1729
+ ScopeIdQueryParam,
1730
+ SegmentQueryParam,
1731
+ ],
1732
+ responses: {
1733
+ '200': { description: 'Page of gate policies.', schema: ref('GatePolicyPage') },
1734
+ ...CommonAuthErrors,
1735
+ '400': ErrorResponse('Malformed scope or cursor.'),
1736
+ },
1737
+ },
1738
+ {
1739
+ method: 'post',
1740
+ honoPath: '/v1/gate-policies',
1741
+ openapiPath: '/v1/gate-policies',
1742
+ operationId: 'gatePolicies.publish',
1743
+ summary: 'Publish a gate policy',
1744
+ description: "Registers a gate policy, or a new version of one: what a promotion of `agentId` for `scope` must show. One policy per agent and scope: another policy id for a scope that has one is refused with `409 gate-policy-scope-taken` (`details.heldBy` names it; publish a new version of that one instead), and a new version can't change the agent or scope (`409 gate-policy-scope-changed`). The `spec` is checked strictly: an unknown key is refused (`400 validation-failed`, `details.issues`). Needs `admin` on the tenant: whoever may promote can't loosen their own gate.",
1745
+ tags: ['gate-policies'],
1746
+ security: 'bearer',
1747
+ parameters: [IdempotencyKeyParam],
1748
+ requestBody: { required: true, schema: ref('PublishGatePolicyBody') },
1749
+ responses: {
1750
+ '201': { description: 'Gate policy published.', schema: ref('GatePolicy') },
1751
+ ...CommonMutationErrors,
1752
+ '400': ErrorResponse('Validation failed (see `details.issues`).'),
1753
+ '409': ErrorResponse('`gate-policy-already-registered`: that (id, version) exists. `gate-policy-scope-taken`: another policy gates the agent for the scope (`details.heldBy`). `gate-policy-scope-changed`: the version would change the agent or scope. `gate-policy-scope-unpinned`: nothing covering the scope is pinned, so a published version would go live there ungated; pin a version for the scope, or one above it, first.'),
1754
+ },
1755
+ },
1756
+ {
1757
+ method: 'get',
1758
+ honoPath: '/v1/gate-policies/:policyId',
1759
+ openapiPath: '/v1/gate-policies/{policyId}',
1760
+ operationId: 'gatePolicies.get',
1761
+ summary: 'Get a gate policy',
1762
+ description: "The policy's latest active version.",
1763
+ tags: ['gate-policies'],
1764
+ security: 'bearer',
1765
+ parameters: [GatePolicyIdPathParam],
1766
+ responses: {
1767
+ '200': { description: 'The gate policy.', schema: ref('GatePolicy') },
1768
+ ...CommonAuthErrors,
1769
+ '404': ErrorResponse('No active gate policy with that id.'),
1770
+ },
1771
+ },
1772
+ {
1773
+ method: 'get',
1774
+ honoPath: '/v1/gate-policies/:policyId/versions',
1775
+ openapiPath: '/v1/gate-policies/{policyId}/versions',
1776
+ operationId: 'gatePolicies.versions.list',
1777
+ summary: "List a gate policy's versions",
1778
+ description: 'Every version, oldest first, unregistered ones too (with `unregisteredAt`).',
1779
+ tags: ['gate-policies'],
1780
+ security: 'bearer',
1781
+ parameters: [GatePolicyIdPathParam],
1782
+ responses: {
1783
+ '200': { description: 'The versions.', schema: ref('GatePolicyPage') },
1784
+ ...CommonAuthErrors,
1785
+ '404': ErrorResponse('No gate policy with that id.'),
1786
+ },
1787
+ },
1788
+ {
1789
+ method: 'get',
1790
+ honoPath: '/v1/gate-policies/:policyId/versions/:version',
1791
+ openapiPath: '/v1/gate-policies/{policyId}/versions/{version}',
1792
+ operationId: 'gatePolicies.versions.get',
1793
+ summary: 'Get a gate policy version',
1794
+ tags: ['gate-policies'],
1795
+ security: 'bearer',
1796
+ parameters: [GatePolicyIdPathParam, GatePolicyVersionPathParam],
1797
+ responses: {
1798
+ '200': { description: 'The version.', schema: ref('GatePolicy') },
1799
+ ...CommonAuthErrors,
1800
+ '404': ErrorResponse('No such version.'),
1801
+ },
1802
+ },
1803
+ {
1804
+ method: 'post',
1805
+ honoPath: '/v1/gate-policies/:policyId/versions/:version/unregister',
1806
+ openapiPath: '/v1/gate-policies/{policyId}/versions/{version}/unregister',
1807
+ operationId: 'gatePolicies.versions.unregister',
1808
+ summary: 'Unregister a gate policy version',
1809
+ description: "The version stops applying; the policy's latest remaining active version applies, or, with none, the scope above's policy. Promotions keep the version they were checked against. Needs `admin` on the tenant.",
1810
+ tags: ['gate-policies'],
1811
+ security: 'bearer',
1812
+ parameters: [GatePolicyIdPathParam, GatePolicyVersionPathParam],
1813
+ responses: {
1814
+ '200': { description: 'The unregistered version.', schema: ref('GatePolicy') },
1815
+ ...CommonMutationErrors,
1816
+ '404': ErrorResponse('No such version.'),
1817
+ },
1818
+ },
1819
+ {
1820
+ method: 'post',
1821
+ honoPath: '/v1/gate-policies/:policyId/versions/:version/reinstate',
1822
+ openapiPath: '/v1/gate-policies/{policyId}/versions/{version}/reinstate',
1823
+ operationId: 'gatePolicies.versions.reinstate',
1824
+ summary: 'Reinstate a gate policy version',
1825
+ description: 'Needs `admin` on the tenant.',
1826
+ tags: ['gate-policies'],
1827
+ security: 'bearer',
1828
+ parameters: [GatePolicyIdPathParam, GatePolicyVersionPathParam],
1829
+ responses: {
1830
+ '200': { description: 'The reinstated version.', schema: ref('GatePolicy') },
1831
+ ...CommonMutationErrors,
1832
+ '404': ErrorResponse('No such version.'),
1833
+ },
1834
+ },
1835
+ {
1836
+ method: 'get',
1837
+ honoPath: '/v1/agents/:agentId/promotions',
1838
+ openapiPath: '/v1/agents/{agentId}/promotions',
1839
+ operationId: 'agents.promotions.list',
1840
+ summary: "List an agent's promotions",
1841
+ description: 'The history of live-version changes (promotions, rollbacks, unpins), newest first. `scopeKind` (`tenant`, `org`, `project`, `segment`) with `scopeId` and `segment` narrows it to one scope.',
1842
+ tags: ['agents'],
1843
+ security: 'bearer',
1844
+ parameters: [
1845
+ AgentIdPathParam,
1846
+ LimitQueryParam,
1847
+ CursorQueryParam,
1848
+ PromotionScopeKindQueryParam,
1849
+ ScopeIdQueryParam,
1850
+ SegmentQueryParam,
1851
+ ],
1852
+ responses: {
1853
+ '200': { description: 'Page of promotions.', schema: ref('PromotionPage') },
1854
+ ...CommonAuthErrors,
1855
+ '400': ErrorResponse('Malformed scope or cursor.'),
1856
+ },
1857
+ },
1858
+ {
1859
+ method: 'get',
1860
+ honoPath: '/v1/agents/:agentId/promotions/:promotionId',
1861
+ openapiPath: '/v1/agents/{agentId}/promotions/{promotionId}',
1862
+ operationId: 'agents.promotions.get',
1863
+ summary: 'Get a promotion',
1864
+ tags: ['agents'],
1865
+ security: 'bearer',
1866
+ parameters: [AgentIdPathParam, PromotionIdPathParam],
1867
+ responses: {
1868
+ '200': { description: 'The promotion.', schema: ref('Promotion') },
1869
+ ...CommonAuthErrors,
1870
+ '404': ErrorResponse('No such promotion for this agent.'),
1871
+ },
1872
+ },
1873
+ {
1874
+ method: 'post',
1875
+ honoPath: '/v1/agents/:agentId/live/rollback',
1876
+ openapiPath: '/v1/agents/{agentId}/live/rollback',
1877
+ operationId: 'agents.live.rollback',
1878
+ summary: 'Roll a scope back to its previous live version',
1879
+ description: "Back to the scope's previous live version, or `toVersion`. Recorded like a promotion. Needs `promote` on the agent.",
1880
+ tags: ['agents'],
1881
+ security: 'bearer',
1882
+ parameters: [AgentIdPathParam, IdempotencyKeyParam],
1883
+ requestBody: { required: true, schema: ref('RollbackBody') },
1884
+ responses: {
1885
+ '200': { description: 'Rolled back.', schema: ref('Promotion') },
1886
+ ...CommonMutationErrors,
1887
+ '404': ErrorResponse('`toVersion` is not registered, or was unregistered.'),
1888
+ '409': ErrorResponse('The scope has no pin, or no earlier version to go back to.'),
1889
+ },
1890
+ },
1891
+ {
1892
+ method: 'post',
1893
+ honoPath: '/v1/agents/:agentId/live/unpin',
1894
+ openapiPath: '/v1/agents/{agentId}/live/unpin',
1895
+ operationId: 'agents.live.unpin',
1896
+ summary: "Remove a scope's live version",
1897
+ description: "Removes the scope's own pin: its runs use the next scope up (and the latest when nothing is pinned). Recorded like a promotion. Needs `promote` on the agent.",
1898
+ tags: ['agents'],
1899
+ security: 'bearer',
1900
+ parameters: [AgentIdPathParam, IdempotencyKeyParam],
1901
+ requestBody: { required: true, schema: ref('UnpinBody') },
1902
+ responses: {
1903
+ '200': { description: 'Unpinned.', schema: ref('Promotion') },
1904
+ ...CommonMutationErrors,
1905
+ '409': ErrorResponse('`not-pinned`: the scope has no pin of its own. `gate-policy-needs-pin`: unpinning would leave a scope a gate policy applies to on the latest version, where publishing goes live ungated.'),
1906
+ },
1907
+ },
1502
1908
  // ---------- flows ----------
1503
1909
  {
1504
1910
  method: 'get',
@@ -1583,8 +1989,9 @@ export const OPERATIONS = [
1583
1989
  responses: {
1584
1990
  '201': { description: 'Flow published.', schema: ref('PublishFlowResult') },
1585
1991
  ...CommonMutationErrors,
1586
- '400': ErrorResponse('Validation failed (see `details.issues`).'),
1587
- '409': ErrorResponse('Flow already registered at that (id, version).'),
1992
+ '400': ErrorResponse("Validation failed (see `details.issues`); or `projectId` isn't a project id (a UUID)."),
1993
+ '409': ErrorResponse("Flow already registered at that (id, version). Or `registry-read-only`: this registry takes no writes (under `kindgi dev`, the pack's files are the source); the message says what to do instead."),
1994
+ '404': ErrorResponse("The body's `projectId` names no project of this tenant (`project-not-found`)."),
1588
1995
  },
1589
1996
  },
1590
1997
  {
@@ -1599,6 +2006,7 @@ export const OPERATIONS = [
1599
2006
  responses: {
1600
2007
  '200': { description: 'Unregistered.', schema: ref('UnregisterFlowResult') },
1601
2008
  ...CommonMutationErrors,
2009
+ '409': ErrorResponse("Idempotency-Key was reused with a different body, or resource-state conflict. Or `registry-read-only`: this registry takes no writes (under `kindgi dev`, the pack's files are the source); the message says what to do instead."),
1602
2010
  '404': ErrorResponse('No flow at that (id, version) under this tenant.'),
1603
2011
  },
1604
2012
  },
@@ -1615,6 +2023,7 @@ export const OPERATIONS = [
1615
2023
  responses: {
1616
2024
  '200': { description: 'Reinstated.', schema: ref('ReinstateFlowVersionResult') },
1617
2025
  ...CommonMutationErrors,
2026
+ '409': ErrorResponse("Idempotency-Key was reused with a different body, or resource-state conflict. Or `registry-read-only`: this registry takes no writes (under `kindgi dev`, the pack's files are the source); the message says what to do instead."),
1618
2027
  '404': ErrorResponse('No flow at that (id, version) under this tenant.'),
1619
2028
  },
1620
2029
  },
@@ -1704,8 +2113,9 @@ export const OPERATIONS = [
1704
2113
  responses: {
1705
2114
  '201': { description: 'Tool registered.', schema: ref('RegisterToolResult') },
1706
2115
  ...CommonMutationErrors,
1707
- '400': ErrorResponse('Validation failed (see `details.issues`).'),
1708
- '409': ErrorResponse('Tool already registered at that id.'),
2116
+ '400': ErrorResponse("Validation failed (see `details.issues`); or `projectId` isn't a project id (a UUID)."),
2117
+ '409': ErrorResponse("Tool already registered at that id. Or `registry-read-only`: this registry takes no writes (under `kindgi dev`, the pack's files are the source); the message says what to do instead."),
2118
+ '404': ErrorResponse("The body's `projectId` names no project of this tenant (`project-not-found`)."),
1709
2119
  },
1710
2120
  },
1711
2121
  {
@@ -1720,6 +2130,7 @@ export const OPERATIONS = [
1720
2130
  responses: {
1721
2131
  '200': { description: 'Unregistered.', schema: ref('UnregisterToolResult') },
1722
2132
  ...CommonMutationErrors,
2133
+ '409': ErrorResponse("Idempotency-Key was reused with a different body, or resource-state conflict. Or `registry-read-only`: this registry takes no writes (under `kindgi dev`, the pack's files are the source); the message says what to do instead."),
1723
2134
  '404': ErrorResponse('No tool at that (id, version) under this tenant.'),
1724
2135
  },
1725
2136
  },
@@ -1736,6 +2147,7 @@ export const OPERATIONS = [
1736
2147
  responses: {
1737
2148
  '200': { description: 'Reinstated.', schema: ref('ReinstateToolVersionResult') },
1738
2149
  ...CommonMutationErrors,
2150
+ '409': ErrorResponse("Idempotency-Key was reused with a different body, or resource-state conflict. Or `registry-read-only`: this registry takes no writes (under `kindgi dev`, the pack's files are the source); the message says what to do instead."),
1739
2151
  '404': ErrorResponse('No tool at that (id, version) under this tenant.'),
1740
2152
  },
1741
2153
  },
@@ -1792,8 +2204,9 @@ export const OPERATIONS = [
1792
2204
  responses: {
1793
2205
  '201': { description: 'Guardrail registered.', schema: ref('RegisterGuardrailResult') },
1794
2206
  ...CommonMutationErrors,
1795
- '400': ErrorResponse('Validation failed (see `details.issues`).'),
1796
- '409': ErrorResponse('Guardrail already registered at that id.'),
2207
+ '400': ErrorResponse("Validation failed (see `details.issues`); or `projectId` isn't a project id (a UUID)."),
2208
+ '409': ErrorResponse("Guardrail already registered at that id. Or `registry-read-only`: this registry takes no writes (under `kindgi dev`, the pack's files are the source); the message says what to do instead."),
2209
+ '404': ErrorResponse("The body's `projectId` names no project of this tenant (`project-not-found`)."),
1797
2210
  },
1798
2211
  },
1799
2212
  {
@@ -1808,6 +2221,7 @@ export const OPERATIONS = [
1808
2221
  responses: {
1809
2222
  '200': { description: 'Unregistered.', schema: ref('UnregisterGuardrailResult') },
1810
2223
  ...CommonMutationErrors,
2224
+ '409': ErrorResponse("Idempotency-Key was reused with a different body, or resource-state conflict. Or `registry-read-only`: this registry takes no writes (under `kindgi dev`, the pack's files are the source); the message says what to do instead."),
1811
2225
  '404': ErrorResponse('No guardrail with that id under this tenant.'),
1812
2226
  },
1813
2227
  },
@@ -1818,7 +2232,7 @@ export const OPERATIONS = [
1818
2232
  openapiPath: '/v1/conversations',
1819
2233
  operationId: 'conversations.list',
1820
2234
  summary: 'List conversations',
1821
- description: "Cursor-paginated. Fixed sort: `openedAt desc, id desc`. Filters: `?agentId=`, `?status=open|closed`, and `scopeKind`/`scopeId` for one project's conversations, or every project's in an org. Conversations from before Kindgi 0.1.3 have no project and are listed only without a scope.",
2235
+ description: "Cursor-paginated. Fixed sort: `openedAt desc, id desc`. Filters: `?agentId=`, `?status=open|closed`, `?replays=exclude|include|only` (default `exclude`: a comparison's replay conversations are left out), and `scopeKind`/`scopeId` for one project's conversations, or every project's in an org. Conversations from before Kindgi 0.1.3 have no project and are listed only without a scope.",
1822
2236
  tags: ['conversations'],
1823
2237
  security: 'bearer',
1824
2238
  parameters: [
@@ -1828,6 +2242,7 @@ export const OPERATIONS = [
1828
2242
  ScopeIdQueryParam,
1829
2243
  AgentIdQueryParam,
1830
2244
  ConversationStatusQueryParam,
2245
+ ConversationReplaysQueryParam,
1831
2246
  ],
1832
2247
  responses: {
1833
2248
  '200': {
@@ -1850,6 +2265,7 @@ export const OPERATIONS = [
1850
2265
  responses: {
1851
2266
  '200': { description: 'Conversation.', schema: ref('Conversation') },
1852
2267
  ...CommonAuthErrors,
2268
+ '400': ErrorResponse('`conversationId` is not a conversation id (a UUID).'),
1853
2269
  '404': ErrorResponse('No conversation with that id under this tenant.'),
1854
2270
  },
1855
2271
  },
@@ -1885,6 +2301,7 @@ export const OPERATIONS = [
1885
2301
  schema: ref('Conversation'),
1886
2302
  },
1887
2303
  ...CommonMutationErrors,
2304
+ '400': ErrorResponse('`conversationId` is not a conversation id (a UUID).'),
1888
2305
  '404': ErrorResponse('No conversation with that id under this tenant.'),
1889
2306
  },
1890
2307
  },
@@ -1904,7 +2321,7 @@ export const OPERATIONS = [
1904
2321
  schema: ref('ConversationMessageCollectionPage'),
1905
2322
  },
1906
2323
  ...CommonAuthErrors,
1907
- '400': ErrorResponse('Malformed cursor.'),
2324
+ '400': ErrorResponse('Malformed cursor, or `conversationId` is not a conversation id (a UUID).'),
1908
2325
  '404': ErrorResponse('No conversation with that id under this tenant.'),
1909
2326
  },
1910
2327
  },
@@ -2361,7 +2778,11 @@ export const OPERATIONS = [
2361
2778
  CursorQueryParam,
2362
2779
  ObservationStatusQueryParam,
2363
2780
  AgentIdQueryParam,
2781
+ ObservationAgentVersionQueryParam,
2364
2782
  SupervisorIdQueryParam,
2783
+ ObservationConversationIdQueryParam,
2784
+ ObservationSinceQueryParam,
2785
+ ObservationUntilQueryParam,
2365
2786
  ],
2366
2787
  responses: {
2367
2788
  '200': {
@@ -2509,7 +2930,7 @@ export const OPERATIONS = [
2509
2930
  '201': { description: 'Judgment recorded.', schema: ref('Judgment') },
2510
2931
  ...CommonMutationErrors,
2511
2932
  '400': ErrorResponse('Malformed body, or `item-not-found` (the pointer resolves to nothing in the output), or `judge-class-not-applicable`.'),
2512
- '403': ErrorResponse('`permission-denied`: not allowed to judge this run.'),
2933
+ '403': ErrorResponse('`permission-denied`: not allowed to judge this run. `judge-class-not-allowed`: the judge class is restricted (`assertableBy`) and the caller may not assert it.'),
2513
2934
  '404': ErrorResponse('`run-not-found`.'),
2514
2935
  '409': ErrorResponse('`run-not-finished`: the run has no output to judge yet.'),
2515
2936
  },
@@ -3044,7 +3465,7 @@ export const OPERATIONS = [
3044
3465
  openapiPath: '/v1/policies',
3045
3466
  operationId: 'policies.publish',
3046
3467
  summary: 'Publish a policy',
3047
- description: "Body is a full `Policy` — the server validates top-level shape (id, tenantId, semver version, kind ∈ closed enum, spec is an object). Deeper `spec` validation is the runtime consumer's responsibility per kind. Re-publishing an existing `(policyId, version)` returns `409 policy-already-registered`. A known kind that no runtime consumer applies yet (`access-control`, `adapter-allowlist`, `rate-limit`, `compliance`) is refused with `400 kind-not-applied` (`details.appliedKinds` lists the ones that are): publishing it would change nothing. Idempotency-Key applies (retries with the same key replay the original 201).",
3468
+ description: "Body is a full `Policy` — the server validates top-level shape (id, tenantId, semver version, kind ∈ closed enum, spec is an object), and `spec` for `tool-errors`, `hitl` and `retention` (a retention policy with an unknown domain or `mode: archive` is refused with `400 validation-failed`, naming the field). Other kinds' specs are their runtime consumer's to validate. Re-publishing an existing `(policyId, version)` returns `409 policy-already-registered`. A tenant has one retention policy per domain, plus one for `*`: a second policy id for a covered domain is refused with `409 policy-scope-taken` (`details.heldBy` names the policy that covers it; publish a new version of that one instead), and a new version can't move a policy to another domain (`409 policy-scope-changed`). A known kind that no runtime consumer applies yet (`access-control`, `adapter-allowlist`, `rate-limit`, `compliance`) is refused with `400 kind-not-applied` (`details.appliedKinds` lists the ones that are): publishing it would change nothing. Idempotency-Key applies (retries with the same key replay the original 201).",
3048
3469
  tags: ['policies'],
3049
3470
  security: 'bearer',
3050
3471
  parameters: [IdempotencyKeyParam],
@@ -3053,7 +3474,7 @@ export const OPERATIONS = [
3053
3474
  '201': { description: 'Policy published.', schema: ref('PublishPolicyResult') },
3054
3475
  ...CommonMutationErrors,
3055
3476
  '400': ErrorResponse('Validation failed (see `details.issues`), or `kind-not-applied`: no runtime consumer applies that kind yet.'),
3056
- '409': ErrorResponse('Policy already registered at that (id, version).'),
3477
+ '409': ErrorResponse('`policy-already-registered`: that (id, version) exists. `policy-scope-taken`: another policy covers the retention domain (`details.heldBy`). `policy-scope-changed`: the version would move the policy to another domain.'),
3057
3478
  },
3058
3479
  },
3059
3480
  {
@@ -3085,6 +3506,65 @@ export const OPERATIONS = [
3085
3506
  '200': { description: 'Reinstated.', schema: ref('ReinstatePolicyVersionResult') },
3086
3507
  ...CommonMutationErrors,
3087
3508
  '404': ErrorResponse('No policy at that (id, version) under this tenant.'),
3509
+ '409': ErrorResponse("`policy-scope-taken`: another policy now covers the version's retention domain (`details.heldBy`); unregister it first. `policy-scope-changed`: the version covers a different domain from the policy's other versions."),
3510
+ },
3511
+ },
3512
+ // ---------- retention (admin plane) ----------
3513
+ {
3514
+ method: 'get',
3515
+ honoPath: '/v1/retention/scheduled',
3516
+ openapiPath: '/v1/retention/scheduled',
3517
+ operationId: 'retention.scheduled',
3518
+ summary: 'List deleted rows scheduled for purging',
3519
+ description: "Tombstoned rows in every domain a retention policy covers, with when each is purged (`purgeAt`) and the policy that decides it. `domainsMissingAdapter` names the covered domains this deployment can't purge; `unpolicedDomains` the ones no policy covers, whose tombstones are kept; `conflicts` the domains two policies cover (stored before one policy per domain was enforced). `limit` caps the rows **per domain**; `hasMore` says some domain has more than it returned, and `nextCursor` (when the runtime can continue) is the `cursor` for the next page. Requires `admin` on the tenant.",
3520
+ tags: ['retention'],
3521
+ security: 'bearer',
3522
+ parameters: [
3523
+ RetentionDomainQueryParam,
3524
+ RetentionPastGraceOnlyQueryParam,
3525
+ LimitQueryParam,
3526
+ CursorQueryParam,
3527
+ ],
3528
+ responses: {
3529
+ '200': { description: 'Scheduled rows.', schema: ref('RetentionScheduledPage') },
3530
+ ...CommonAuthErrors,
3531
+ '403': ErrorResponse('Not a tenant admin.'),
3532
+ '400': ErrorResponse('Unknown `domain`.'),
3533
+ },
3534
+ },
3535
+ {
3536
+ method: 'post',
3537
+ honoPath: '/v1/retention/sweep',
3538
+ openapiPath: '/v1/retention/sweep',
3539
+ operationId: 'retention.sweep',
3540
+ summary: 'Purge the deleted rows past their grace',
3541
+ description: "Purges, for good, every tombstoned row past the grace of the retention policy that covers its domain (or only `domain`'s), up to `maxPerDomain` per domain; `remaining` counts what is left for the next call. Nothing sweeps on its own: call this (or `POST /v1/retention/sweep/{domain}`) from a schedule. A hold (`graceSeconds: -1`) keeps its domain's rows. Idempotent: a second call purges nothing new. Requires `admin` on the tenant.",
3542
+ tags: ['retention'],
3543
+ security: 'bearer',
3544
+ requestBody: { required: false, schema: ref('RetentionSweepBody') },
3545
+ responses: {
3546
+ '200': { description: 'What was purged, per domain.', schema: ref('RetentionSweepResult') },
3547
+ ...CommonMutationErrors,
3548
+ '403': ErrorResponse('Not a tenant admin.'),
3549
+ '400': ErrorResponse('Unknown `domain`, or `maxPerDomain` outside 1..10000.'),
3550
+ },
3551
+ },
3552
+ {
3553
+ method: 'post',
3554
+ honoPath: '/v1/retention/sweep/:domain',
3555
+ openapiPath: '/v1/retention/sweep/{domain}',
3556
+ operationId: 'retention.sweepDomain',
3557
+ summary: "Purge one domain's deleted rows past their grace",
3558
+ description: 'As `POST /v1/retention/sweep`, for one domain. Requires `admin` on the tenant.',
3559
+ tags: ['retention'],
3560
+ security: 'bearer',
3561
+ parameters: [RetentionDomainPathParam],
3562
+ requestBody: { required: false, schema: ref('RetentionSweepDomainBody') },
3563
+ responses: {
3564
+ '200': { description: 'What was purged.', schema: ref('RetentionSweepResult') },
3565
+ ...CommonMutationErrors,
3566
+ '403': ErrorResponse('Not a tenant admin.'),
3567
+ '400': ErrorResponse('Unknown `domain` (or `*`), or `maxPerDomain` outside 1..10000.'),
3088
3568
  },
3089
3569
  },
3090
3570
  // ---------- eval suites (admin plane) ----------
@@ -3175,8 +3655,9 @@ export const OPERATIONS = [
3175
3655
  responses: {
3176
3656
  '201': { description: 'Eval suite published.', schema: ref('PublishEvalSuiteResult') },
3177
3657
  ...CommonMutationErrors,
3178
- '400': ErrorResponse('Validation failed (see `details.issues`).'),
3658
+ '400': ErrorResponse("Validation failed (see `details.issues`); or `projectId` isn't a project id (a UUID)."),
3179
3659
  '409': ErrorResponse('Eval suite already registered at that (id, version).'),
3660
+ '404': ErrorResponse("The body's `projectId` names no project of this tenant (`project-not-found`)."),
3180
3661
  },
3181
3662
  },
3182
3663
  {
@@ -3193,10 +3674,11 @@ export const OPERATIONS = [
3193
3674
  responses: {
3194
3675
  '201': { description: 'Version published.', schema: ref('BuildJudgedSuiteResult') },
3195
3676
  ...CommonMutationErrors,
3196
- '400': ErrorResponse('Malformed body.'),
3677
+ '400': ErrorResponse("Malformed body; or `projectId` isn't a project id (a UUID)."),
3197
3678
  '403': ErrorResponse('`permission-denied`.'),
3198
3679
  '409': ErrorResponse('Eval suite already registered at that (id, version).'),
3199
3680
  '501': ErrorResponse('`test-sets-not-supported`: this deployment cannot build test sets.'),
3681
+ '404': ErrorResponse("The body's `projectId` names no project of this tenant (`project-not-found`)."),
3200
3682
  },
3201
3683
  },
3202
3684
  {
@@ -3336,9 +3818,10 @@ export const OPERATIONS = [
3336
3818
  responses: {
3337
3819
  '201': { description: 'Published.', schema: ref('PublishBlockResult') },
3338
3820
  ...CommonMutationErrors,
3339
- '400': ErrorResponse('`validation-failed` (see `details.issues`), or an unknown `projectId`.'),
3821
+ '400': ErrorResponse("`validation-failed` (see `details.issues`), or an unknown `projectId`; or `projectId` isn't a project id (a UUID)."),
3340
3822
  '403': ErrorResponse('`permission-denied`: no `write` on the project.'),
3341
3823
  '409': ErrorResponse('`block-already-registered` or `block-project-mismatch`.'),
3824
+ '404': ErrorResponse("The body's `projectId` names no project of this tenant (`project-not-found`)."),
3342
3825
  },
3343
3826
  },
3344
3827
  {
@@ -3389,8 +3872,8 @@ export const OPERATIONS = [
3389
3872
  responses: {
3390
3873
  '201': { description: 'Eval run started.', schema: ref('StartEvalRunResult') },
3391
3874
  ...CommonMutationErrors,
3392
- '400': ErrorResponse('Malformed body (both / neither of `agentRef` / `flowRef`, dispatcher-input-invalid).'),
3393
- '404': ErrorResponse('No eval suite with that id under this tenant.'),
3875
+ '400': ErrorResponse("Malformed body (both / neither of `agentRef` / `flowRef`, dispatcher-input-invalid); or `projectId` isn't a project id (a UUID)."),
3876
+ '404': ErrorResponse('No eval suite with that id under this tenant; or `projectId` names no project of this tenant (`project-not-found`).'),
3394
3877
  '422': ErrorResponse('No dispatcher registered for the suite kind.'),
3395
3878
  },
3396
3879
  },
@@ -3734,6 +4217,7 @@ export const OPERATIONS = [
3734
4217
  schema: ref('DeploymentRecord'),
3735
4218
  },
3736
4219
  ...CommonMutationErrors,
4220
+ '409': ErrorResponse("Idempotency-Key was reused with a different body, or resource-state conflict. Or `registry-read-only`: this registry takes no writes (under `kindgi dev`, the pack's files are the source); the message says what to do instead."),
3737
4221
  '400': ErrorResponse('Signature invalid, image unverifiable, or deployment-validation-failed with per-primitive `details[]`.'),
3738
4222
  '403': ErrorResponse("Signer key not on the tenant's trust list."),
3739
4223
  },
@@ -3943,7 +4427,7 @@ export const OPERATIONS = [
3943
4427
  openapiPath: '/v1/audit/authz',
3944
4428
  operationId: 'audit.authz.list',
3945
4429
  summary: 'List authz decision audit events',
3946
- description: 'Cursor-paginated read of `authz-decision` audit events for the tenant. Filters (all AND-composed): `?actorSubject=` / `?onBehalfOf=` / `?action=` / `?resource=` / `?outcome=` / `?runId=` / `?from=` / `?to=`. Admin@tenant only. Only mounted when `CreateAppInput.auditEvents` is wired.',
4430
+ description: 'Cursor-paginated read of `authz-decision` audit events for the tenant. Filters (all AND-composed): `?actorSubject=` / `?onBehalfOf=` / `?action=` / `?resource=` / `?outcome=` / `?runId=` / `?from=` / `?to=`. Oldest first; `?order=desc` for newest first. Admin@tenant only. Only mounted when `CreateAppInput.auditEvents` is wired.',
3947
4431
  tags: ['audit'],
3948
4432
  security: 'bearer',
3949
4433
  parameters: [
@@ -4005,6 +4489,13 @@ export const OPERATIONS = [
4005
4489
  description: 'ISO 8601 upper bound (inclusive) on `timestamp`.',
4006
4490
  schema: { type: 'string', format: 'date-time' },
4007
4491
  },
4492
+ {
4493
+ name: 'order',
4494
+ in: 'query',
4495
+ required: false,
4496
+ description: '`asc` (the default): oldest first. `desc`: newest first. `nextCursor` continues in the same order.',
4497
+ schema: { type: 'string', enum: ['asc', 'desc'] },
4498
+ },
4008
4499
  ],
4009
4500
  responses: {
4010
4501
  '200': {
@@ -4051,7 +4542,7 @@ export const OPERATIONS = [
4051
4542
  },
4052
4543
  },
4053
4544
  ...CommonAuthErrors,
4054
- '400': ErrorResponse('Malformed cursor, `from`, `to`, or `outcome`.'),
4545
+ '400': ErrorResponse('Malformed cursor, `from`, `to`, `outcome`, or `order`.'),
4055
4546
  },
4056
4547
  },
4057
4548
  // ---------- orgs ----------
@@ -4216,6 +4707,7 @@ export const OPERATIONS = [
4216
4707
  responses: {
4217
4708
  '201': { description: 'Team created.', schema: ref('CreateResourceResult') },
4218
4709
  ...CommonMutationErrors,
4710
+ '404': ErrorResponse('org-not-found: `orgId` names no org of the tenant (it never existed, or it was deleted).'),
4219
4711
  },
4220
4712
  },
4221
4713
  {
@@ -4263,7 +4755,7 @@ export const OPERATIONS = [
4263
4755
  responses: {
4264
4756
  '204': { description: 'Updated. No body.' },
4265
4757
  ...CommonMutationErrors,
4266
- '404': ErrorResponse('No team with that id under this tenant.'),
4758
+ '404': ErrorResponse('team-not-found: no team with that id under this tenant; or org-not-found: `orgId` names no org of the tenant (it never existed, or it was deleted).'),
4267
4759
  },
4268
4760
  },
4269
4761
  {
@@ -4372,6 +4864,7 @@ export const OPERATIONS = [
4372
4864
  responses: {
4373
4865
  '204': { description: 'Removed (or already absent). No body.' },
4374
4866
  ...CommonAuthErrors,
4867
+ '501': ErrorResponse("With authorization enforced, a runtime whose tenant-hierarchy binding can't change the membership together with its authorization tuple refuses with `authz-membership-unsupported`; nothing is changed."),
4375
4868
  },
4376
4869
  },
4377
4870
  {
@@ -4404,6 +4897,7 @@ export const OPERATIONS = [
4404
4897
  '204': { description: 'Role updated. No body.' },
4405
4898
  ...CommonMutationErrors,
4406
4899
  '404': ErrorResponse('No membership for that (team, user) pair, or no such team under this tenant.'),
4900
+ '501': ErrorResponse("With authorization enforced, a runtime whose tenant-hierarchy binding can't change the membership together with its authorization tuple refuses with `authz-membership-unsupported`; nothing is changed."),
4407
4901
  },
4408
4902
  },
4409
4903
  // ---------- projects ----------
@@ -4469,6 +4963,7 @@ export const OPERATIONS = [
4469
4963
  responses: {
4470
4964
  '201': { description: 'Project created.', schema: ref('CreateResourceResult') },
4471
4965
  ...CommonMutationErrors,
4966
+ '404': ErrorResponse('org-not-found: `orgId` names no org of the tenant (it never existed, or it was deleted).'),
4472
4967
  },
4473
4968
  },
4474
4969
  {
@@ -4517,7 +5012,7 @@ export const OPERATIONS = [
4517
5012
  responses: {
4518
5013
  '204': { description: 'Updated. No body.' },
4519
5014
  ...CommonMutationErrors,
4520
- '404': ErrorResponse('No project with that id under this tenant.'),
5015
+ '404': ErrorResponse('project-not-found: no project with that id under this tenant; or org-not-found: `orgId` names no org of the tenant (it never existed, or it was deleted).'),
4521
5016
  },
4522
5017
  },
4523
5018
  {
@@ -4629,6 +5124,7 @@ export const OPERATIONS = [
4629
5124
  responses: {
4630
5125
  '204': { description: 'Removed (or already absent). No body.' },
4631
5126
  ...CommonAuthErrors,
5127
+ '501': ErrorResponse("With authorization enforced, a runtime whose tenant-hierarchy binding can't change the membership together with its authorization tuple refuses with `authz-membership-unsupported`; nothing is changed."),
4632
5128
  },
4633
5129
  },
4634
5130
  {
@@ -4661,6 +5157,7 @@ export const OPERATIONS = [
4661
5157
  '204': { description: 'Role updated. No body.' },
4662
5158
  ...CommonMutationErrors,
4663
5159
  '404': ErrorResponse('No membership for that (project, user) pair, or no such project under this tenant.'),
5160
+ '501': ErrorResponse("With authorization enforced, a runtime whose tenant-hierarchy binding can't change the membership together with its authorization tuple refuses with `authz-membership-unsupported`; nothing is changed."),
4664
5161
  },
4665
5162
  },
4666
5163
  // ---------- tenant ----------