@kindgi/api 0.0.0-bootstrap.0 → 0.1.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.
Files changed (505) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +216 -1
  3. package/dist/adapter-binding.d.ts +177 -0
  4. package/dist/adapter-binding.d.ts.map +1 -0
  5. package/dist/adapter-binding.js +12 -0
  6. package/dist/adapter-binding.js.map +1 -0
  7. package/dist/agent-binding.d.ts +207 -0
  8. package/dist/agent-binding.d.ts.map +1 -0
  9. package/dist/agent-binding.js +4 -0
  10. package/dist/agent-binding.js.map +1 -0
  11. package/dist/app.d.ts +770 -0
  12. package/dist/app.d.ts.map +1 -0
  13. package/dist/app.js +396 -0
  14. package/dist/app.js.map +1 -0
  15. package/dist/capability-binding.d.ts +76 -0
  16. package/dist/capability-binding.d.ts.map +1 -0
  17. package/dist/capability-binding.js +4 -0
  18. package/dist/capability-binding.js.map +1 -0
  19. package/dist/cost-binding.d.ts +175 -0
  20. package/dist/cost-binding.d.ts.map +1 -0
  21. package/dist/cost-binding.js +13 -0
  22. package/dist/cost-binding.js.map +1 -0
  23. package/dist/deployment-binding.d.ts +201 -0
  24. package/dist/deployment-binding.d.ts.map +1 -0
  25. package/dist/deployment-binding.js +4 -0
  26. package/dist/deployment-binding.js.map +1 -0
  27. package/dist/env-binding.d.ts +132 -0
  28. package/dist/env-binding.d.ts.map +1 -0
  29. package/dist/env-binding.js +4 -0
  30. package/dist/env-binding.js.map +1 -0
  31. package/dist/errors.d.ts +37 -0
  32. package/dist/errors.d.ts.map +1 -0
  33. package/dist/errors.js +264 -0
  34. package/dist/errors.js.map +1 -0
  35. package/dist/eval-run-binding.d.ts +196 -0
  36. package/dist/eval-run-binding.d.ts.map +1 -0
  37. package/dist/eval-run-binding.js +16 -0
  38. package/dist/eval-run-binding.js.map +1 -0
  39. package/dist/eval-run-dispatcher.d.ts +131 -0
  40. package/dist/eval-run-dispatcher.d.ts.map +1 -0
  41. package/dist/eval-run-dispatcher.js +368 -0
  42. package/dist/eval-run-dispatcher.js.map +1 -0
  43. package/dist/eval-suite-binding.d.ts +227 -0
  44. package/dist/eval-suite-binding.d.ts.map +1 -0
  45. package/dist/eval-suite-binding.js +24 -0
  46. package/dist/eval-suite-binding.js.map +1 -0
  47. package/dist/event-bus-binding.d.ts +107 -0
  48. package/dist/event-bus-binding.d.ts.map +1 -0
  49. package/dist/event-bus-binding.js +5 -0
  50. package/dist/event-bus-binding.js.map +1 -0
  51. package/dist/flow-binding.d.ts +194 -0
  52. package/dist/flow-binding.d.ts.map +1 -0
  53. package/dist/flow-binding.js +4 -0
  54. package/dist/flow-binding.js.map +1 -0
  55. package/dist/guardrail-binding.d.ts +138 -0
  56. package/dist/guardrail-binding.d.ts.map +1 -0
  57. package/dist/guardrail-binding.js +4 -0
  58. package/dist/guardrail-binding.js.map +1 -0
  59. package/dist/handler-binding.d.ts +126 -0
  60. package/dist/handler-binding.d.ts.map +1 -0
  61. package/dist/handler-binding.js +4 -0
  62. package/dist/handler-binding.js.map +1 -0
  63. package/dist/hitl-binding.d.ts +115 -0
  64. package/dist/hitl-binding.d.ts.map +1 -0
  65. package/dist/hitl-binding.js +4 -0
  66. package/dist/hitl-binding.js.map +1 -0
  67. package/dist/identity-directory-binding.d.ts +116 -0
  68. package/dist/identity-directory-binding.d.ts.map +1 -0
  69. package/dist/identity-directory-binding.js +4 -0
  70. package/dist/identity-directory-binding.js.map +1 -0
  71. package/dist/identity-provider-binding.d.ts +184 -0
  72. package/dist/identity-provider-binding.d.ts.map +1 -0
  73. package/dist/identity-provider-binding.js +4 -0
  74. package/dist/identity-provider-binding.js.map +1 -0
  75. package/dist/image-registry-binding.d.ts +165 -0
  76. package/dist/image-registry-binding.d.ts.map +1 -0
  77. package/dist/image-registry-binding.js +4 -0
  78. package/dist/image-registry-binding.js.map +1 -0
  79. package/dist/index.d.ts +72 -0
  80. package/dist/index.d.ts.map +1 -0
  81. package/dist/index.js +26 -0
  82. package/dist/index.js.map +1 -0
  83. package/dist/key-management-binding.d.ts +101 -0
  84. package/dist/key-management-binding.d.ts.map +1 -0
  85. package/dist/key-management-binding.js +4 -0
  86. package/dist/key-management-binding.js.map +1 -0
  87. package/dist/mcp-endpoint-binding.d.ts +312 -0
  88. package/dist/mcp-endpoint-binding.d.ts.map +1 -0
  89. package/dist/mcp-endpoint-binding.js +12 -0
  90. package/dist/mcp-endpoint-binding.js.map +1 -0
  91. package/dist/memory-binding.d.ts +209 -0
  92. package/dist/memory-binding.d.ts.map +1 -0
  93. package/dist/memory-binding.js +4 -0
  94. package/dist/memory-binding.js.map +1 -0
  95. package/dist/middleware/auth.d.ts +168 -0
  96. package/dist/middleware/auth.d.ts.map +1 -0
  97. package/dist/middleware/auth.js +247 -0
  98. package/dist/middleware/auth.js.map +1 -0
  99. package/dist/middleware/authorize.d.ts +26 -0
  100. package/dist/middleware/authorize.d.ts.map +1 -0
  101. package/dist/middleware/authorize.js +65 -0
  102. package/dist/middleware/authorize.js.map +1 -0
  103. package/dist/middleware/error-mapper.d.ts +13 -0
  104. package/dist/middleware/error-mapper.d.ts.map +1 -0
  105. package/dist/middleware/error-mapper.js +32 -0
  106. package/dist/middleware/error-mapper.js.map +1 -0
  107. package/dist/middleware/idempotency.d.ts +43 -0
  108. package/dist/middleware/idempotency.d.ts.map +1 -0
  109. package/dist/middleware/idempotency.js +118 -0
  110. package/dist/middleware/idempotency.js.map +1 -0
  111. package/dist/middleware/principal.d.ts +4 -0
  112. package/dist/middleware/principal.d.ts.map +1 -0
  113. package/dist/middleware/principal.js +42 -0
  114. package/dist/middleware/principal.js.map +1 -0
  115. package/dist/middleware/public-run-routes.d.ts +16 -0
  116. package/dist/middleware/public-run-routes.d.ts.map +1 -0
  117. package/dist/middleware/public-run-routes.js +43 -0
  118. package/dist/middleware/public-run-routes.js.map +1 -0
  119. package/dist/middleware/request-id.d.ts +14 -0
  120. package/dist/middleware/request-id.d.ts.map +1 -0
  121. package/dist/middleware/request-id.js +24 -0
  122. package/dist/middleware/request-id.js.map +1 -0
  123. package/dist/middleware/sigv4-verify.d.ts +64 -0
  124. package/dist/middleware/sigv4-verify.d.ts.map +1 -0
  125. package/dist/middleware/sigv4-verify.js +286 -0
  126. package/dist/middleware/sigv4-verify.js.map +1 -0
  127. package/dist/middleware/sigv4.d.ts +25 -0
  128. package/dist/middleware/sigv4.d.ts.map +1 -0
  129. package/dist/middleware/sigv4.js +120 -0
  130. package/dist/middleware/sigv4.js.map +1 -0
  131. package/dist/openapi/generate.d.ts +15 -0
  132. package/dist/openapi/generate.d.ts.map +1 -0
  133. package/dist/openapi/generate.js +241 -0
  134. package/dist/openapi/generate.js.map +1 -0
  135. package/dist/openapi/operations.d.ts +70 -0
  136. package/dist/openapi/operations.d.ts.map +1 -0
  137. package/dist/openapi/operations.js +5051 -0
  138. package/dist/openapi/operations.js.map +1 -0
  139. package/dist/openapi/schemas.d.ts +590 -0
  140. package/dist/openapi/schemas.d.ts.map +1 -0
  141. package/dist/openapi/schemas.js +5820 -0
  142. package/dist/openapi/schemas.js.map +1 -0
  143. package/dist/provenance-binding.d.ts +116 -0
  144. package/dist/provenance-binding.d.ts.map +1 -0
  145. package/dist/provenance-binding.js +31 -0
  146. package/dist/provenance-binding.js.map +1 -0
  147. package/dist/provider-binding.d.ts +174 -0
  148. package/dist/provider-binding.d.ts.map +1 -0
  149. package/dist/provider-binding.js +4 -0
  150. package/dist/provider-binding.js.map +1 -0
  151. package/dist/public-run-token.d.ts +97 -0
  152. package/dist/public-run-token.d.ts.map +1 -0
  153. package/dist/public-run-token.js +167 -0
  154. package/dist/public-run-token.js.map +1 -0
  155. package/dist/retention-binding.d.ts +69 -0
  156. package/dist/retention-binding.d.ts.map +1 -0
  157. package/dist/retention-binding.js +4 -0
  158. package/dist/retention-binding.js.map +1 -0
  159. package/dist/reviewer-binding.d.ts +84 -0
  160. package/dist/reviewer-binding.d.ts.map +1 -0
  161. package/dist/reviewer-binding.js +4 -0
  162. package/dist/reviewer-binding.js.map +1 -0
  163. package/dist/rotation-status-store.d.ts +96 -0
  164. package/dist/rotation-status-store.d.ts.map +1 -0
  165. package/dist/rotation-status-store.js +106 -0
  166. package/dist/rotation-status-store.js.map +1 -0
  167. package/dist/routes/adapters.d.ts +29 -0
  168. package/dist/routes/adapters.d.ts.map +1 -0
  169. package/dist/routes/adapters.js +230 -0
  170. package/dist/routes/adapters.js.map +1 -0
  171. package/dist/routes/agents.d.ts +17 -0
  172. package/dist/routes/agents.d.ts.map +1 -0
  173. package/dist/routes/agents.js +342 -0
  174. package/dist/routes/agents.js.map +1 -0
  175. package/dist/routes/approvals.d.ts +37 -0
  176. package/dist/routes/approvals.d.ts.map +1 -0
  177. package/dist/routes/approvals.js +551 -0
  178. package/dist/routes/approvals.js.map +1 -0
  179. package/dist/routes/artifacts.d.ts +25 -0
  180. package/dist/routes/artifacts.d.ts.map +1 -0
  181. package/dist/routes/artifacts.js +265 -0
  182. package/dist/routes/artifacts.js.map +1 -0
  183. package/dist/routes/audit.d.ts +6 -0
  184. package/dist/routes/audit.d.ts.map +1 -0
  185. package/dist/routes/audit.js +162 -0
  186. package/dist/routes/audit.js.map +1 -0
  187. package/dist/routes/auth.d.ts +47 -0
  188. package/dist/routes/auth.d.ts.map +1 -0
  189. package/dist/routes/auth.js +630 -0
  190. package/dist/routes/auth.js.map +1 -0
  191. package/dist/routes/capabilities.d.ts +14 -0
  192. package/dist/routes/capabilities.d.ts.map +1 -0
  193. package/dist/routes/capabilities.js +62 -0
  194. package/dist/routes/capabilities.js.map +1 -0
  195. package/dist/routes/compliance.d.ts +37 -0
  196. package/dist/routes/compliance.d.ts.map +1 -0
  197. package/dist/routes/compliance.js +321 -0
  198. package/dist/routes/compliance.js.map +1 -0
  199. package/dist/routes/conversations.d.ts +13 -0
  200. package/dist/routes/conversations.d.ts.map +1 -0
  201. package/dist/routes/conversations.js +295 -0
  202. package/dist/routes/conversations.js.map +1 -0
  203. package/dist/routes/cost.d.ts +24 -0
  204. package/dist/routes/cost.d.ts.map +1 -0
  205. package/dist/routes/cost.js +243 -0
  206. package/dist/routes/cost.js.map +1 -0
  207. package/dist/routes/deployments.d.ts +122 -0
  208. package/dist/routes/deployments.d.ts.map +1 -0
  209. package/dist/routes/deployments.js +1036 -0
  210. package/dist/routes/deployments.js.map +1 -0
  211. package/dist/routes/env.d.ts +62 -0
  212. package/dist/routes/env.d.ts.map +1 -0
  213. package/dist/routes/env.js +311 -0
  214. package/dist/routes/env.js.map +1 -0
  215. package/dist/routes/eval-runs.d.ts +28 -0
  216. package/dist/routes/eval-runs.d.ts.map +1 -0
  217. package/dist/routes/eval-runs.js +479 -0
  218. package/dist/routes/eval-runs.js.map +1 -0
  219. package/dist/routes/eval-suites.d.ts +24 -0
  220. package/dist/routes/eval-suites.d.ts.map +1 -0
  221. package/dist/routes/eval-suites.js +381 -0
  222. package/dist/routes/eval-suites.js.map +1 -0
  223. package/dist/routes/event-triggers.d.ts +12 -0
  224. package/dist/routes/event-triggers.d.ts.map +1 -0
  225. package/dist/routes/event-triggers.js +218 -0
  226. package/dist/routes/event-triggers.js.map +1 -0
  227. package/dist/routes/flows.d.ts +23 -0
  228. package/dist/routes/flows.d.ts.map +1 -0
  229. package/dist/routes/flows.js +344 -0
  230. package/dist/routes/flows.js.map +1 -0
  231. package/dist/routes/guardrails.d.ts +33 -0
  232. package/dist/routes/guardrails.d.ts.map +1 -0
  233. package/dist/routes/guardrails.js +236 -0
  234. package/dist/routes/guardrails.js.map +1 -0
  235. package/dist/routes/identity.d.ts +39 -0
  236. package/dist/routes/identity.d.ts.map +1 -0
  237. package/dist/routes/identity.js +138 -0
  238. package/dist/routes/identity.js.map +1 -0
  239. package/dist/routes/mcp.d.ts +23 -0
  240. package/dist/routes/mcp.d.ts.map +1 -0
  241. package/dist/routes/mcp.js +727 -0
  242. package/dist/routes/mcp.js.map +1 -0
  243. package/dist/routes/memory.d.ts +16 -0
  244. package/dist/routes/memory.d.ts.map +1 -0
  245. package/dist/routes/memory.js +353 -0
  246. package/dist/routes/memory.js.map +1 -0
  247. package/dist/routes/observations.d.ts +19 -0
  248. package/dist/routes/observations.d.ts.map +1 -0
  249. package/dist/routes/observations.js +135 -0
  250. package/dist/routes/observations.js.map +1 -0
  251. package/dist/routes/orgs.d.ts +24 -0
  252. package/dist/routes/orgs.d.ts.map +1 -0
  253. package/dist/routes/orgs.js +208 -0
  254. package/dist/routes/orgs.js.map +1 -0
  255. package/dist/routes/pagination.d.ts +18 -0
  256. package/dist/routes/pagination.d.ts.map +1 -0
  257. package/dist/routes/pagination.js +35 -0
  258. package/dist/routes/pagination.js.map +1 -0
  259. package/dist/routes/policies.d.ts +23 -0
  260. package/dist/routes/policies.d.ts.map +1 -0
  261. package/dist/routes/policies.js +314 -0
  262. package/dist/routes/policies.js.map +1 -0
  263. package/dist/routes/projects.d.ts +45 -0
  264. package/dist/routes/projects.d.ts.map +1 -0
  265. package/dist/routes/projects.js +485 -0
  266. package/dist/routes/projects.js.map +1 -0
  267. package/dist/routes/proposals.d.ts +24 -0
  268. package/dist/routes/proposals.d.ts.map +1 -0
  269. package/dist/routes/proposals.js +581 -0
  270. package/dist/routes/proposals.js.map +1 -0
  271. package/dist/routes/provenance.d.ts +34 -0
  272. package/dist/routes/provenance.d.ts.map +1 -0
  273. package/dist/routes/provenance.js +274 -0
  274. package/dist/routes/provenance.js.map +1 -0
  275. package/dist/routes/providers.d.ts +32 -0
  276. package/dist/routes/providers.d.ts.map +1 -0
  277. package/dist/routes/providers.js +490 -0
  278. package/dist/routes/providers.js.map +1 -0
  279. package/dist/routes/public-run-tokens.d.ts +17 -0
  280. package/dist/routes/public-run-tokens.d.ts.map +1 -0
  281. package/dist/routes/public-run-tokens.js +69 -0
  282. package/dist/routes/public-run-tokens.js.map +1 -0
  283. package/dist/routes/retention.d.ts +19 -0
  284. package/dist/routes/retention.d.ts.map +1 -0
  285. package/dist/routes/retention.js +152 -0
  286. package/dist/routes/retention.js.map +1 -0
  287. package/dist/routes/reviewers.d.ts +5 -0
  288. package/dist/routes/reviewers.d.ts.map +1 -0
  289. package/dist/routes/reviewers.js +161 -0
  290. package/dist/routes/reviewers.js.map +1 -0
  291. package/dist/routes/runs.d.ts +34 -0
  292. package/dist/routes/runs.d.ts.map +1 -0
  293. package/dist/routes/runs.js +773 -0
  294. package/dist/routes/runs.js.map +1 -0
  295. package/dist/routes/s3.d.ts +31 -0
  296. package/dist/routes/s3.d.ts.map +1 -0
  297. package/dist/routes/s3.js +433 -0
  298. package/dist/routes/s3.js.map +1 -0
  299. package/dist/routes/schedules.d.ts +20 -0
  300. package/dist/routes/schedules.d.ts.map +1 -0
  301. package/dist/routes/schedules.js +234 -0
  302. package/dist/routes/schedules.js.map +1 -0
  303. package/dist/routes/scope-params.d.ts +57 -0
  304. package/dist/routes/scope-params.d.ts.map +1 -0
  305. package/dist/routes/scope-params.js +105 -0
  306. package/dist/routes/scope-params.js.map +1 -0
  307. package/dist/routes/secret-ref.d.ts +20 -0
  308. package/dist/routes/secret-ref.d.ts.map +1 -0
  309. package/dist/routes/secret-ref.js +42 -0
  310. package/dist/routes/secret-ref.js.map +1 -0
  311. package/dist/routes/secrets.d.ts +53 -0
  312. package/dist/routes/secrets.d.ts.map +1 -0
  313. package/dist/routes/secrets.js +715 -0
  314. package/dist/routes/secrets.js.map +1 -0
  315. package/dist/routes/signing-keys.d.ts +23 -0
  316. package/dist/routes/signing-keys.d.ts.map +1 -0
  317. package/dist/routes/signing-keys.js +212 -0
  318. package/dist/routes/signing-keys.js.map +1 -0
  319. package/dist/routes/sse.d.ts +58 -0
  320. package/dist/routes/sse.d.ts.map +1 -0
  321. package/dist/routes/sse.js +92 -0
  322. package/dist/routes/sse.js.map +1 -0
  323. package/dist/routes/teams.d.ts +30 -0
  324. package/dist/routes/teams.d.ts.map +1 -0
  325. package/dist/routes/teams.js +443 -0
  326. package/dist/routes/teams.js.map +1 -0
  327. package/dist/routes/tenant.d.ts +57 -0
  328. package/dist/routes/tenant.d.ts.map +1 -0
  329. package/dist/routes/tenant.js +352 -0
  330. package/dist/routes/tenant.js.map +1 -0
  331. package/dist/routes/tokens.d.ts +14 -0
  332. package/dist/routes/tokens.d.ts.map +1 -0
  333. package/dist/routes/tokens.js +231 -0
  334. package/dist/routes/tokens.js.map +1 -0
  335. package/dist/routes/tools.d.ts +33 -0
  336. package/dist/routes/tools.d.ts.map +1 -0
  337. package/dist/routes/tools.js +380 -0
  338. package/dist/routes/tools.js.map +1 -0
  339. package/dist/routes/webhook-endpoints.d.ts +10 -0
  340. package/dist/routes/webhook-endpoints.d.ts.map +1 -0
  341. package/dist/routes/webhook-endpoints.js +407 -0
  342. package/dist/routes/webhook-endpoints.js.map +1 -0
  343. package/dist/routes/webhooks.d.ts +21 -0
  344. package/dist/routes/webhooks.d.ts.map +1 -0
  345. package/dist/routes/webhooks.js +228 -0
  346. package/dist/routes/webhooks.js.map +1 -0
  347. package/dist/s3-credential-binding.d.ts +54 -0
  348. package/dist/s3-credential-binding.d.ts.map +1 -0
  349. package/dist/s3-credential-binding.js +4 -0
  350. package/dist/s3-credential-binding.js.map +1 -0
  351. package/dist/secrets-binding.d.ts +281 -0
  352. package/dist/secrets-binding.d.ts.map +1 -0
  353. package/dist/secrets-binding.js +10 -0
  354. package/dist/secrets-binding.js.map +1 -0
  355. package/dist/secrets-provider-binding.d.ts +164 -0
  356. package/dist/secrets-provider-binding.d.ts.map +1 -0
  357. package/dist/secrets-provider-binding.js +4 -0
  358. package/dist/secrets-provider-binding.js.map +1 -0
  359. package/dist/session-store-binding.d.ts +148 -0
  360. package/dist/session-store-binding.d.ts.map +1 -0
  361. package/dist/session-store-binding.js +4 -0
  362. package/dist/session-store-binding.js.map +1 -0
  363. package/dist/signing-key-binding.d.ts +210 -0
  364. package/dist/signing-key-binding.d.ts.map +1 -0
  365. package/dist/signing-key-binding.js +4 -0
  366. package/dist/signing-key-binding.js.map +1 -0
  367. package/dist/state-store-binding.d.ts +53 -0
  368. package/dist/state-store-binding.d.ts.map +1 -0
  369. package/dist/state-store-binding.js +38 -0
  370. package/dist/state-store-binding.js.map +1 -0
  371. package/dist/supervisor-binding.d.ts +526 -0
  372. package/dist/supervisor-binding.d.ts.map +1 -0
  373. package/dist/supervisor-binding.js +4 -0
  374. package/dist/supervisor-binding.js.map +1 -0
  375. package/dist/tenant-host-access.d.ts +41 -0
  376. package/dist/tenant-host-access.d.ts.map +1 -0
  377. package/dist/tenant-host-access.js +42 -0
  378. package/dist/tenant-host-access.js.map +1 -0
  379. package/dist/token-admin.d.ts +99 -0
  380. package/dist/token-admin.d.ts.map +1 -0
  381. package/dist/token-admin.js +4 -0
  382. package/dist/token-admin.js.map +1 -0
  383. package/dist/tool-binding.d.ts +247 -0
  384. package/dist/tool-binding.d.ts.map +1 -0
  385. package/dist/tool-binding.js +4 -0
  386. package/dist/tool-binding.js.map +1 -0
  387. package/dist/trigger-binding.d.ts +13 -0
  388. package/dist/trigger-binding.d.ts.map +1 -0
  389. package/dist/trigger-binding.js +4 -0
  390. package/dist/trigger-binding.js.map +1 -0
  391. package/dist/types.d.ts +71 -0
  392. package/dist/types.d.ts.map +1 -0
  393. package/dist/types.js +4 -0
  394. package/dist/types.js.map +1 -0
  395. package/dist/webhook-endpoint-binding.d.ts +230 -0
  396. package/dist/webhook-endpoint-binding.d.ts.map +1 -0
  397. package/dist/webhook-endpoint-binding.js +12 -0
  398. package/dist/webhook-endpoint-binding.js.map +1 -0
  399. package/dist/xml.d.ts +79 -0
  400. package/dist/xml.d.ts.map +1 -0
  401. package/dist/xml.js +115 -0
  402. package/dist/xml.js.map +1 -0
  403. package/openapi.json +29659 -0
  404. package/package.json +76 -4
  405. package/scripts/emit-openapi.mjs +44 -0
  406. package/src/adapter-binding.ts +204 -0
  407. package/src/agent-binding.ts +231 -0
  408. package/src/app.ts +1285 -0
  409. package/src/capability-binding.ts +83 -0
  410. package/src/cost-binding.ts +213 -0
  411. package/src/deployment-binding.ts +225 -0
  412. package/src/env-binding.ts +140 -0
  413. package/src/errors.ts +291 -0
  414. package/src/eval-run-binding.ts +209 -0
  415. package/src/eval-run-dispatcher.ts +497 -0
  416. package/src/eval-suite-binding.ts +257 -0
  417. package/src/event-bus-binding.ts +127 -0
  418. package/src/flow-binding.ts +213 -0
  419. package/src/guardrail-binding.ts +151 -0
  420. package/src/handler-binding.ts +132 -0
  421. package/src/hitl-binding.ts +172 -0
  422. package/src/identity-directory-binding.ts +128 -0
  423. package/src/identity-provider-binding.ts +203 -0
  424. package/src/image-registry-binding.ts +193 -0
  425. package/src/index.ts +606 -0
  426. package/src/key-management-binding.ts +91 -0
  427. package/src/mcp-endpoint-binding.ts +311 -0
  428. package/src/memory-binding.ts +239 -0
  429. package/src/middleware/auth.ts +437 -0
  430. package/src/middleware/authorize.ts +125 -0
  431. package/src/middleware/error-mapper.ts +37 -0
  432. package/src/middleware/idempotency.ts +160 -0
  433. package/src/middleware/principal.ts +64 -0
  434. package/src/middleware/public-run-routes.ts +50 -0
  435. package/src/middleware/request-id.ts +28 -0
  436. package/src/middleware/sigv4-verify.ts +374 -0
  437. package/src/middleware/sigv4.ts +165 -0
  438. package/src/openapi/generate.ts +307 -0
  439. package/src/openapi/operations.ts +5445 -0
  440. package/src/openapi/schemas.ts +6363 -0
  441. package/src/provenance-binding.ts +169 -0
  442. package/src/provider-binding.ts +196 -0
  443. package/src/public-run-token.ts +263 -0
  444. package/src/retention-binding.ts +78 -0
  445. package/src/reviewer-binding.ts +99 -0
  446. package/src/rotation-status-store.ts +209 -0
  447. package/src/routes/adapters.ts +319 -0
  448. package/src/routes/agents.ts +424 -0
  449. package/src/routes/approvals.ts +759 -0
  450. package/src/routes/artifacts.ts +317 -0
  451. package/src/routes/audit.ts +219 -0
  452. package/src/routes/auth.ts +806 -0
  453. package/src/routes/capabilities.ts +79 -0
  454. package/src/routes/compliance.ts +482 -0
  455. package/src/routes/conversations.ts +369 -0
  456. package/src/routes/cost.ts +327 -0
  457. package/src/routes/deployments.ts +1493 -0
  458. package/src/routes/env.ts +429 -0
  459. package/src/routes/eval-runs.ts +631 -0
  460. package/src/routes/eval-suites.ts +490 -0
  461. package/src/routes/event-triggers.ts +283 -0
  462. package/src/routes/flows.ts +423 -0
  463. package/src/routes/guardrails.ts +320 -0
  464. package/src/routes/identity.ts +203 -0
  465. package/src/routes/mcp.ts +940 -0
  466. package/src/routes/memory.ts +430 -0
  467. package/src/routes/observations.ts +175 -0
  468. package/src/routes/orgs.ts +273 -0
  469. package/src/routes/pagination.ts +51 -0
  470. package/src/routes/policies.ts +413 -0
  471. package/src/routes/projects.ts +634 -0
  472. package/src/routes/proposals.ts +719 -0
  473. package/src/routes/provenance.ts +406 -0
  474. package/src/routes/providers.ts +624 -0
  475. package/src/routes/public-run-tokens.ts +104 -0
  476. package/src/routes/retention.ts +193 -0
  477. package/src/routes/reviewers.ts +209 -0
  478. package/src/routes/runs.ts +981 -0
  479. package/src/routes/s3.ts +519 -0
  480. package/src/routes/schedules.ts +300 -0
  481. package/src/routes/scope-params.ts +133 -0
  482. package/src/routes/secret-ref.ts +52 -0
  483. package/src/routes/secrets.ts +975 -0
  484. package/src/routes/signing-keys.ts +259 -0
  485. package/src/routes/sse.ts +148 -0
  486. package/src/routes/teams.ts +584 -0
  487. package/src/routes/tenant.ts +530 -0
  488. package/src/routes/tokens.ts +285 -0
  489. package/src/routes/tools.ts +491 -0
  490. package/src/routes/webhook-endpoints.ts +534 -0
  491. package/src/routes/webhooks.ts +296 -0
  492. package/src/s3-credential-binding.ts +58 -0
  493. package/src/secrets-binding.ts +308 -0
  494. package/src/secrets-provider-binding.ts +167 -0
  495. package/src/session-store-binding.ts +163 -0
  496. package/src/signing-key-binding.ts +235 -0
  497. package/src/state-store-binding.ts +84 -0
  498. package/src/supervisor-binding.ts +554 -0
  499. package/src/tenant-host-access.ts +62 -0
  500. package/src/token-admin.ts +104 -0
  501. package/src/tool-binding.ts +274 -0
  502. package/src/trigger-binding.ts +38 -0
  503. package/src/types.ts +74 -0
  504. package/src/webhook-endpoint-binding.ts +246 -0
  505. package/src/xml.ts +196 -0
@@ -0,0 +1,759 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+
4
+ import { Hono } from 'hono';
5
+
6
+ import type { ConversationBinding } from '@kindgi/agents';
7
+ import { REVIEWER_ROLE_RANK, type ReviewerRole } from '@kindgi/authz';
8
+ import { serializePublicKeyPem, signEd25519 } from '@kindgi/crypto';
9
+ import type { SigningKeyBinding } from '@kindgi/crypto';
10
+ import type { RunBinding } from '@kindgi/runtime';
11
+ import { canonicalize } from '@kindgi/schema';
12
+ import type {
13
+ ApprovalId,
14
+ ConversationId,
15
+ Cursor,
16
+ ReviewerId,
17
+ RunId,
18
+ SigningKeyId,
19
+ TenantId,
20
+ Timestamp,
21
+ UserId,
22
+ } from '@kindgi/types';
23
+
24
+ import { statusFor, toWireError } from '../errors.js';
25
+ import type { RunHandlerBinding } from '../handler-binding.js';
26
+ import type { Approval, ApprovalStatus, HitlBinding, ReviewDecisionKind } from '../hitl-binding.js';
27
+ import type { ReviewerBinding } from '../reviewer-binding.js';
28
+ import type { AppEnv } from '../types.js';
29
+ import { clampLimit } from './pagination.js';
30
+
31
+ const APPROVAL_STATUSES: ReadonlySet<ApprovalStatus> = new Set([
32
+ 'pending',
33
+ 'assigned',
34
+ 'in_review',
35
+ 'approved',
36
+ 'rejected',
37
+ 'escalated',
38
+ 'expired',
39
+ 'withdrawn',
40
+ ]);
41
+
42
+ const DECISION_KINDS: ReadonlySet<ReviewDecisionKind> = new Set([
43
+ 'approve',
44
+ 'reject',
45
+ 'escalate',
46
+ 'withdraw',
47
+ ]);
48
+
49
+ const ROLE_VALUES: ReadonlySet<ReviewerRole> = new Set(['standard', 'senior', 'admin']);
50
+
51
+ /**
52
+ * Terminal approval statuses — the ones an audit bundle can be exported for.
53
+ * An `expired` approval has no recorded decision.
54
+ */
55
+ const DECIDED_STATUSES: ReadonlySet<ApprovalStatus> = new Set([
56
+ 'approved',
57
+ 'rejected',
58
+ 'escalated',
59
+ 'expired',
60
+ 'withdrawn',
61
+ ]);
62
+
63
+ /** Audit-bundle body schema version — bump when the bundle wire shape changes. */
64
+ const AUDIT_BUNDLE_SCHEMA_VERSION = 1;
65
+
66
+ export interface ApprovalsRouterOptions {
67
+ readonly signingKey?: SigningKeyBinding;
68
+ /**
69
+ * When supplied, the POST /:approvalId/complete handler
70
+ * invokes `runHandler.resumeRun(runId)` INLINE after `completeToken`
71
+ * succeeds — the reviewer's HTTP response reflects the resumed run's
72
+ * new terminal (or re-suspended) state. Absent = the route does
73
+ * `completeToken` only; something else (a background job in the
74
+ * deployment, or a later `POST /v1/runs/:runId/resume`) must drive
75
+ * the resume.
76
+ */
77
+ readonly runHandler?: RunHandlerBinding;
78
+ }
79
+
80
+ /**
81
+ * Approvals resource routes.
82
+ *
83
+ * Reviewer-role scoping: routes require `c.get('reviewerRole')` to be
84
+ * set by the auth middleware (see `TokenResolution.reviewerRole`).
85
+ * Tokens without a reviewer role get `403 permission-denied` on every
86
+ * approvals route — this surface is reviewer-only.
87
+ *
88
+ * Visibility filter: a caller with role `R` sees approvals whose
89
+ * `requiredRole` rank ≤ their rank (standard < senior < admin). The
90
+ * scoping is applied post-fetch on the returned rows — pushing the
91
+ * filter into the storage query would require a role-set predicate on
92
+ * `listApprovals`.
93
+ */
94
+ export function approvalsRouter(
95
+ conversationBinding: ConversationBinding,
96
+ reviewerBinding: ReviewerBinding,
97
+ hitlBinding: HitlBinding,
98
+ runBinding: RunBinding,
99
+ options: ApprovalsRouterOptions = {},
100
+ ): Hono<AppEnv> {
101
+ const r = new Hono<AppEnv>();
102
+ const signingKey = options.signingKey;
103
+ const runHandler = options.runHandler;
104
+
105
+ // ---------- role gate for the whole resource ----------
106
+ r.use('*', async (c, next) => {
107
+ const requestId = c.get('requestId');
108
+ const role = c.get('reviewerRole');
109
+ if (role === undefined) {
110
+ c.status(statusFor('permission-denied') as never);
111
+ return c.json(
112
+ toWireError(
113
+ {
114
+ code: 'permission-denied',
115
+ message:
116
+ 'This token was not provisioned with a reviewer role; approvals surface is reviewer-only.',
117
+ },
118
+ requestId,
119
+ ),
120
+ );
121
+ }
122
+ await next();
123
+ return;
124
+ });
125
+
126
+ // ---------- GET / (list, cursor-paginated, role-scoped) ----------
127
+ r.get('/', async (c) => {
128
+ const requestId = c.get('requestId');
129
+ const tenantId = c.get('tenantId') as TenantId;
130
+ const role = c.get('reviewerRole') as ReviewerRole;
131
+ const limit = clampLimit(c.req.query('limit'));
132
+
133
+ const statusRaw = c.req.query('status');
134
+ let statusFilter: ApprovalStatus | undefined;
135
+ if (statusRaw !== undefined && statusRaw.length > 0) {
136
+ if (!APPROVAL_STATUSES.has(statusRaw as ApprovalStatus)) {
137
+ c.status(statusFor('bad-input') as never);
138
+ return c.json(
139
+ toWireError(
140
+ { code: 'bad-input', message: `Unknown \`status\` value: ${statusRaw}` },
141
+ requestId,
142
+ ),
143
+ );
144
+ }
145
+ statusFilter = statusRaw as ApprovalStatus;
146
+ }
147
+
148
+ const requiredRoleRaw = c.req.query('requiredRole');
149
+ let requiredRoleFilter: ReviewerRole | undefined;
150
+ if (requiredRoleRaw !== undefined && requiredRoleRaw.length > 0) {
151
+ if (!ROLE_VALUES.has(requiredRoleRaw as ReviewerRole)) {
152
+ c.status(statusFor('bad-input') as never);
153
+ return c.json(
154
+ toWireError(
155
+ {
156
+ code: 'bad-input',
157
+ message: `Unknown \`requiredRole\` value: ${requiredRoleRaw}`,
158
+ },
159
+ requestId,
160
+ ),
161
+ );
162
+ }
163
+ requiredRoleFilter = requiredRoleRaw as ReviewerRole;
164
+ // Refuse a filter that asks for a tier the caller cannot decide —
165
+ // preserves the "reviewer never sees above their tier" guardrail
166
+ // regardless of what the caller passes.
167
+ if (REVIEWER_ROLE_RANK[requiredRoleFilter] > REVIEWER_ROLE_RANK[role]) {
168
+ c.status(statusFor('permission-denied') as never);
169
+ return c.json(
170
+ toWireError(
171
+ {
172
+ code: 'permission-denied',
173
+ message: `Role ${role} cannot query approvals scoped to ${requiredRoleFilter}.`,
174
+ },
175
+ requestId,
176
+ ),
177
+ );
178
+ }
179
+ }
180
+
181
+ const createdAfterRaw = c.req.query('createdAfter');
182
+ let createdAfterIso: string | undefined;
183
+ if (createdAfterRaw !== undefined && createdAfterRaw.length > 0) {
184
+ const parsed = new Date(createdAfterRaw);
185
+ if (Number.isNaN(parsed.getTime())) {
186
+ c.status(statusFor('bad-input') as never);
187
+ return c.json(
188
+ toWireError(
189
+ { code: 'bad-input', message: '`createdAfter` must be an ISO date string' },
190
+ requestId,
191
+ ),
192
+ );
193
+ }
194
+ createdAfterIso = parsed.toISOString();
195
+ }
196
+
197
+ const cursor = c.req.query('cursor');
198
+ if (cursor !== undefined && cursor.length > 0) {
199
+ const parsed = new Date(cursor);
200
+ if (Number.isNaN(parsed.getTime())) {
201
+ c.status(statusFor('bad-input') as never);
202
+ return c.json(
203
+ toWireError({ code: 'bad-input', message: '`cursor` is malformed' }, requestId),
204
+ );
205
+ }
206
+ }
207
+
208
+ // `listApprovals` takes an ISO-timestamp cursor. Over-fetch up to 4×
209
+ // the page size (capped at 500) so a page still fills when
210
+ // role-scoping filters rows out; the extra rows (or the binding's
211
+ // own cursor) also tell us `hasMore`.
212
+ const HITL_LIMIT_CAP = Math.min(limit * 4, 500);
213
+ const listInput = {
214
+ tenantId,
215
+ limit: HITL_LIMIT_CAP,
216
+ ...(statusFilter !== undefined && { status: statusFilter }),
217
+ ...(requiredRoleFilter !== undefined && { requiredRole: requiredRoleFilter }),
218
+ ...(createdAfterIso !== undefined && { since: createdAfterIso as unknown as Timestamp }),
219
+ ...(cursor !== undefined && cursor.length > 0 && { cursor: cursor as Cursor }),
220
+ };
221
+ const listed = await hitlBinding.listApprovals(listInput);
222
+ if (listed.kind === 'err') {
223
+ c.status(statusFor(listed.error.code) as never);
224
+ return c.json(toWireError(listed.error as never, requestId));
225
+ }
226
+ const roleRank = REVIEWER_ROLE_RANK[role];
227
+ const visible = listed.value.approvals.filter(
228
+ (a) => REVIEWER_ROLE_RANK[a.requiredRole] <= roleRank,
229
+ );
230
+ const page = visible.slice(0, limit);
231
+ const hasMore = visible.length > limit || listed.value.nextCursor !== undefined;
232
+ const last = page[page.length - 1];
233
+ const nextCursor =
234
+ hasMore && last !== undefined ? (last.createdAt as unknown as string) : undefined;
235
+ return c.json({
236
+ data: page.map(serializeApproval),
237
+ hasMore,
238
+ ...(nextCursor !== undefined && { nextCursor }),
239
+ });
240
+ });
241
+
242
+ // ---------- GET /:approvalId (role-scoped) ----------
243
+ r.get('/:approvalId', async (c) => {
244
+ const requestId = c.get('requestId');
245
+ const tenantId = c.get('tenantId') as TenantId;
246
+ const role = c.get('reviewerRole') as ReviewerRole;
247
+ const approvalId = c.req.param('approvalId') as ApprovalId;
248
+
249
+ const got = await hitlBinding.getApproval(tenantId, approvalId);
250
+ if (got.kind === 'err') {
251
+ c.status(statusFor(got.error.code) as never);
252
+ return c.json(toWireError(got.error as never, requestId));
253
+ }
254
+ const approval = got.value;
255
+ // Out-of-scope reads are 404 (per API-ROUTE-CONVENTIONS.md §2.4 —
256
+ // avoid leaking existence across role tiers).
257
+ if (REVIEWER_ROLE_RANK[approval.requiredRole] > REVIEWER_ROLE_RANK[role]) {
258
+ c.status(statusFor('approval-not-found') as never);
259
+ return c.json(
260
+ toWireError(
261
+ {
262
+ code: 'approval-not-found',
263
+ message: `No approval with id ${approvalId as unknown as string}`,
264
+ approvalId: approvalId as unknown as string,
265
+ },
266
+ requestId,
267
+ ),
268
+ );
269
+ }
270
+ return c.json(serializeApproval(approval));
271
+ });
272
+
273
+ // ---------- POST /:approvalId/complete ----------
274
+ r.post('/:approvalId/complete', async (c) => {
275
+ const requestId = c.get('requestId');
276
+ const tenantId = c.get('tenantId') as TenantId;
277
+ const role = c.get('reviewerRole') as ReviewerRole;
278
+ const userId = c.get('userId');
279
+ const approvalId = c.req.param('approvalId') as ApprovalId;
280
+
281
+ if (userId === undefined) {
282
+ c.status(statusFor('permission-denied') as never);
283
+ return c.json(
284
+ toWireError(
285
+ {
286
+ code: 'permission-denied',
287
+ message:
288
+ 'Token has a reviewer role but no user identity — cannot resolve reviewer for decision.',
289
+ },
290
+ requestId,
291
+ ),
292
+ );
293
+ }
294
+
295
+ let body: unknown;
296
+ try {
297
+ body = await c.req.json();
298
+ } catch {
299
+ c.status(statusFor('bad-input') as never);
300
+ return c.json(
301
+ toWireError({ code: 'bad-input', message: 'Request body must be valid JSON' }, requestId),
302
+ );
303
+ }
304
+ const parsed = parseCompleteBody(body);
305
+ if (parsed.kind === 'err') {
306
+ c.status(statusFor(parsed.error.code) as never);
307
+ return c.json(toWireError(parsed.error, requestId));
308
+ }
309
+
310
+ const found = await hitlBinding.getApproval(tenantId, approvalId);
311
+ if (found.kind === 'err') {
312
+ c.status(statusFor(found.error.code) as never);
313
+ return c.json(toWireError(found.error as never, requestId));
314
+ }
315
+ const approval = found.value;
316
+ if (REVIEWER_ROLE_RANK[approval.requiredRole] > REVIEWER_ROLE_RANK[role]) {
317
+ c.status(statusFor('approval-not-found') as never);
318
+ return c.json(
319
+ toWireError(
320
+ {
321
+ code: 'approval-not-found',
322
+ message: `No approval with id ${approvalId as unknown as string}`,
323
+ approvalId: approvalId as unknown as string,
324
+ },
325
+ requestId,
326
+ ),
327
+ );
328
+ }
329
+
330
+ const reviewerId = await reviewerBinding.resolveReviewer({
331
+ tenantId,
332
+ userId: userId as UserId,
333
+ });
334
+ if (reviewerId === null) {
335
+ c.status(statusFor('permission-denied') as never);
336
+ return c.json(
337
+ toWireError(
338
+ {
339
+ code: 'permission-denied',
340
+ message: 'No reviewer row registered for this user under this tenant.',
341
+ },
342
+ requestId,
343
+ ),
344
+ );
345
+ }
346
+
347
+ const submitted = await hitlBinding.submitReview({
348
+ tenantId,
349
+ approvalId,
350
+ reviewerId: reviewerId as ReviewerId,
351
+ decision: parsed.value.decision,
352
+ ...(parsed.value.rationale !== undefined && { rationale: parsed.value.rationale }),
353
+ });
354
+ if (submitted.kind === 'err') {
355
+ c.status(statusFor(submitted.error.code) as never);
356
+ return c.json(toWireError(submitted.error as never, requestId));
357
+ }
358
+
359
+ // If the approval carries a waitpoint reference AND the decision is
360
+ // a terminal accept/reject, resolve the run's waitpoint so the
361
+ // suspended run resumes. Escalate/withdraw don't resume — the run
362
+ // stays suspended until the new (escalated) approval terminates,
363
+ // or the run is cancelled explicitly.
364
+ //
365
+ // Resume value shape: `{ decided, rationale? }` — the shape the
366
+ // agent's approval gate consumes. The caller CAN supply an explicit
367
+ // `value` in the wire body to override it, when the resume payload
368
+ // must carry more than the decision.
369
+ let waitpointResolved = false;
370
+ if (
371
+ approval.waitTokenId !== undefined &&
372
+ approval.provenanceRef?.runId !== undefined &&
373
+ (parsed.value.decision === 'approve' || parsed.value.decision === 'reject')
374
+ ) {
375
+ const resumeValue =
376
+ parsed.value.value !== undefined
377
+ ? parsed.value.value
378
+ : {
379
+ decided: parsed.value.decision as 'approve' | 'reject',
380
+ ...(parsed.value.rationale !== undefined && { rationale: parsed.value.rationale }),
381
+ };
382
+ const resolved = await runBinding.completeToken(
383
+ tenantId,
384
+ approval.provenanceRef.runId as RunId,
385
+ approval.waitTokenId,
386
+ resumeValue,
387
+ );
388
+ if (resolved.kind === 'err') {
389
+ c.status(statusFor(resolved.error.code) as never);
390
+ return c.json(toWireError(resolved.error as never, requestId));
391
+ }
392
+ waitpointResolved = true;
393
+
394
+ // Inline resume — when a runHandler is wired,
395
+ // drive resumeRun synchronously in the same request. The runtime
396
+ // replays from the journal; the parked run reaches its next
397
+ // terminal state (or re-suspends on a subsequent gate). Reviewer
398
+ // gets the new state in the same HTTP response.
399
+ //
400
+ // Failure here is intentionally soft: `completeToken` already
401
+ // journaled `wait.resumed`. The approval-complete surface reports
402
+ // the decision as successful; a run that stayed suspended because
403
+ // the inline resume failed can be resumed later.
404
+ if (runHandler !== undefined) {
405
+ try {
406
+ await runHandler.resumeRun({
407
+ tenantId,
408
+ runId: approval.provenanceRef.runId as RunId,
409
+ });
410
+ } catch {
411
+ // Soft-fail — the decision is durable; the run can be resumed later.
412
+ }
413
+ }
414
+ }
415
+
416
+ const result = submitted.value;
417
+ return c.json({
418
+ kind: result.kind,
419
+ approval: serializeApproval(result.approval),
420
+ decision: serializeDecision(result.decision),
421
+ ...(result.kind === 'escalated' && {
422
+ nextApproval: serializeApproval(result.nextApproval),
423
+ }),
424
+ waitpointResolved,
425
+ });
426
+ });
427
+
428
+ // ---------- POST /:approvalId/audit-bundle (signed bundle) ----------
429
+ r.post('/:approvalId/audit-bundle', async (c) => {
430
+ const requestId = c.get('requestId');
431
+ const tenantId = c.get('tenantId') as TenantId;
432
+ const role = c.get('reviewerRole') as ReviewerRole;
433
+ const approvalId = c.req.param('approvalId') as ApprovalId;
434
+
435
+ if (signingKey === undefined) {
436
+ c.status(statusFor('signing-not-configured') as never);
437
+ return c.json(
438
+ toWireError(
439
+ {
440
+ code: 'signing-not-configured',
441
+ message:
442
+ 'This deployment does not have a `signingKey` binding mounted; signed audit bundles are unavailable.',
443
+ },
444
+ requestId,
445
+ ),
446
+ );
447
+ }
448
+
449
+ let body: unknown;
450
+ try {
451
+ body = await c.req.json();
452
+ } catch {
453
+ c.status(statusFor('bad-input') as never);
454
+ return c.json(
455
+ toWireError({ code: 'bad-input', message: 'Request body must be valid JSON' }, requestId),
456
+ );
457
+ }
458
+ if (body === null || typeof body !== 'object' || Array.isArray(body)) {
459
+ c.status(statusFor('bad-input') as never);
460
+ return c.json(
461
+ toWireError({ code: 'bad-input', message: 'Request body must be an object' }, requestId),
462
+ );
463
+ }
464
+ const parsed = parseAuditBundleBody(body as Record<string, unknown>);
465
+ if (parsed.kind === 'err') {
466
+ c.status(statusFor('bad-input') as never);
467
+ return c.json(toWireError({ code: 'bad-input', message: parsed.message }, requestId));
468
+ }
469
+ const { signingKeyId, includeMessages } = parsed.value;
470
+
471
+ // 1. Load the approval + enforce role scoping (404 for out-of-scope).
472
+ const found = await hitlBinding.getApproval(tenantId, approvalId);
473
+ if (found.kind === 'err') {
474
+ c.status(statusFor(found.error.code) as never);
475
+ return c.json(toWireError(found.error as never, requestId));
476
+ }
477
+ const approval = found.value;
478
+ if (REVIEWER_ROLE_RANK[approval.requiredRole] > REVIEWER_ROLE_RANK[role]) {
479
+ c.status(statusFor('approval-not-found') as never);
480
+ return c.json(
481
+ toWireError(
482
+ {
483
+ code: 'approval-not-found',
484
+ message: `No approval with id ${approvalId as unknown as string}`,
485
+ approvalId: approvalId as unknown as string,
486
+ },
487
+ requestId,
488
+ ),
489
+ );
490
+ }
491
+
492
+ // 2. Refuse to sign a bundle for a still-pending approval — bundles
493
+ // are only meaningful post-decision.
494
+ if (!DECIDED_STATUSES.has(approval.status)) {
495
+ c.status(statusFor('approval-not-decided') as never);
496
+ return c.json(
497
+ toWireError(
498
+ {
499
+ code: 'approval-not-decided',
500
+ message: `Approval ${approvalId as unknown as string} is still ${approval.status}; audit bundles are only exportable after a terminal decision.`,
501
+ approvalId: approvalId as unknown as string,
502
+ status: approval.status,
503
+ },
504
+ requestId,
505
+ ),
506
+ );
507
+ }
508
+
509
+ // 3. Load the decision row (may be absent when the approval expired
510
+ // without human interaction — status 'expired'). Bundle carries
511
+ // `decision: null` in that case.
512
+ const decisionQuery = await hitlBinding.loadReviewDecision(tenantId, approvalId);
513
+ if (decisionQuery.kind === 'err') {
514
+ c.status(statusFor('persistence-error') as never);
515
+ return c.json(
516
+ toWireError(
517
+ {
518
+ code: 'persistence-error',
519
+ message: `Failed to load review decision: ${decisionQuery.error.message}`,
520
+ },
521
+ requestId,
522
+ ),
523
+ );
524
+ }
525
+ const decisionRow = decisionQuery.value;
526
+
527
+ // 4. Optionally hydrate messages tied to the approval's run.
528
+ let messages: readonly unknown[] | undefined;
529
+ if (includeMessages && approval.provenanceRef?.runId !== undefined) {
530
+ const msgResult = await conversationBinding.readMessages({
531
+ tenantId,
532
+ conversationId: approval.provenanceRef.runId as unknown as ConversationId,
533
+ });
534
+ messages = msgResult.kind === 'ok' ? msgResult.value : [];
535
+ } else if (includeMessages) {
536
+ messages = [];
537
+ }
538
+
539
+ // 5. Look up the private key material.
540
+ const privateKey = signingKey.getPrivateKey(signingKeyId);
541
+ if (privateKey === null) {
542
+ c.status(statusFor('signing-key-not-found') as never);
543
+ return c.json(
544
+ toWireError(
545
+ {
546
+ code: 'signing-key-not-found',
547
+ message: `No signing key registered with id "${signingKeyId as unknown as string}"`,
548
+ signingKeyId: signingKeyId as unknown as string,
549
+ },
550
+ requestId,
551
+ ),
552
+ );
553
+ }
554
+ const publicKeyRaw = signingKey.getPublicKey(signingKeyId);
555
+ if (publicKeyRaw === null) {
556
+ c.status(statusFor('signing-key-not-found') as never);
557
+ return c.json(
558
+ toWireError(
559
+ {
560
+ code: 'signing-key-not-found',
561
+ message: `Signing key "${signingKeyId as unknown as string}" resolved a private key but no public key`,
562
+ signingKeyId: signingKeyId as unknown as string,
563
+ },
564
+ requestId,
565
+ ),
566
+ );
567
+ }
568
+
569
+ // 6. Build the canonical bundle body.
570
+ const guardrailResults = extractGuardrailResults(approval.context);
571
+ const bundleBody: Record<string, unknown> = {
572
+ bundleVersion: AUDIT_BUNDLE_SCHEMA_VERSION,
573
+ approvalId: approval.id as unknown as string,
574
+ tenantId: approval.tenantId as unknown as string,
575
+ subjectKind: approval.subjectKind,
576
+ subjectRef: approval.subjectRef,
577
+ requiredRole: approval.requiredRole,
578
+ status: approval.status,
579
+ createdAt: approval.createdAt as unknown as string,
580
+ ...(approval.decidedAt !== undefined && {
581
+ decidedAt: approval.decidedAt as unknown as string,
582
+ }),
583
+ decision:
584
+ decisionRow !== null
585
+ ? {
586
+ kind: decisionRow.decision,
587
+ reviewerId: decisionRow.reviewerId,
588
+ reviewerRoleAtDecision: decisionRow.reviewerRoleAtDecision,
589
+ decidedAt: decisionRow.decidedAt as unknown as string,
590
+ ...(decisionRow.rationale !== undefined && { rationale: decisionRow.rationale }),
591
+ }
592
+ : null,
593
+ evidence: {
594
+ ...(guardrailResults !== undefined && { guardrailResults }),
595
+ ...(messages !== undefined && { messages }),
596
+ },
597
+ exportedAt: new Date().toISOString(),
598
+ };
599
+ const canonicalBundleBytes = new TextEncoder().encode(canonicalize(bundleBody));
600
+
601
+ // 7. Sign.
602
+ const signResult = signEd25519(privateKey, canonicalBundleBytes);
603
+ if (signResult.kind === 'err') {
604
+ c.status(statusFor('export-key-error') as never);
605
+ return c.json(
606
+ toWireError(
607
+ {
608
+ code: 'export-key-error',
609
+ message: `Failed to sign audit bundle: ${signResult.error.message}`,
610
+ },
611
+ requestId,
612
+ ),
613
+ );
614
+ }
615
+
616
+ return c.json({
617
+ approvalId: approvalId as unknown as string,
618
+ bundle: Buffer.from(canonicalBundleBytes).toString('base64'),
619
+ bundleSchemaVersion: AUDIT_BUNDLE_SCHEMA_VERSION,
620
+ algorithm: 'ed25519' as const,
621
+ signingKeyId: signingKeyId as unknown as string,
622
+ signature: Buffer.from(signResult.value).toString('base64'),
623
+ publicKey: serializePublicKeyPem(publicKeyRaw),
624
+ canonicalization: 'sorted-key-json' as const,
625
+ exportedAt: new Date().toISOString() as unknown as Timestamp,
626
+ });
627
+ });
628
+
629
+ return r;
630
+ }
631
+
632
+ // Extract guardrail results out of the approval's `context` envelope. HITL
633
+ // stores kind-specific context opaquely; agent-turn approvals populate
634
+ // `context.guardrailResults` when guardrail violations trigger the approval.
635
+ // Absent-or-mismatched → return undefined so the bundle omits the field.
636
+ function extractGuardrailResults(
637
+ context: Readonly<Record<string, unknown>> | undefined,
638
+ ): readonly unknown[] | undefined {
639
+ if (context === undefined) return undefined;
640
+ const raw = (context as Record<string, unknown>).guardrailResults;
641
+ return Array.isArray(raw) ? (raw as readonly unknown[]) : undefined;
642
+ }
643
+
644
+ function serializeApproval(a: Approval): Record<string, unknown> {
645
+ return {
646
+ id: a.id,
647
+ tenantId: a.tenantId,
648
+ projectId: a.projectId,
649
+ subjectKind: a.subjectKind,
650
+ subjectRef: a.subjectRef,
651
+ requiredRole: a.requiredRole,
652
+ status: a.status,
653
+ assignedTo: a.assignedTo,
654
+ batchKey: a.batchKey,
655
+ title: a.title,
656
+ description: a.description,
657
+ context: a.context,
658
+ provenanceRef: a.provenanceRef,
659
+ waitTokenId: a.waitTokenId,
660
+ createdAt: a.createdAt,
661
+ updatedAt: a.updatedAt,
662
+ decidedAt: a.decidedAt,
663
+ expiresAt: a.expiresAt,
664
+ };
665
+ }
666
+
667
+ function serializeDecision(d: {
668
+ readonly id: string;
669
+ readonly approvalId: unknown;
670
+ readonly reviewerId: unknown;
671
+ readonly decision: string;
672
+ readonly rationale?: string;
673
+ readonly reviewerRoleAtDecision: string;
674
+ readonly decidedAt: string;
675
+ }): Record<string, unknown> {
676
+ return {
677
+ id: d.id,
678
+ approvalId: d.approvalId,
679
+ reviewerId: d.reviewerId,
680
+ decision: d.decision,
681
+ ...(d.rationale !== undefined && { rationale: d.rationale }),
682
+ reviewerRoleAtDecision: d.reviewerRoleAtDecision,
683
+ decidedAt: d.decidedAt,
684
+ };
685
+ }
686
+
687
+ type ParsedComplete = {
688
+ readonly decision: ReviewDecisionKind;
689
+ readonly rationale?: string;
690
+ readonly value?: unknown;
691
+ };
692
+
693
+ function parseCompleteBody(
694
+ body: unknown,
695
+ ):
696
+ | { kind: 'ok'; value: ParsedComplete }
697
+ | { kind: 'err'; error: { code: string; message: string } } {
698
+ if (body === null || typeof body !== 'object') {
699
+ return {
700
+ kind: 'err',
701
+ error: { code: 'bad-input', message: 'Request body must be an object' },
702
+ };
703
+ }
704
+ const b = body as Record<string, unknown>;
705
+ const decision = b.decision;
706
+ if (typeof decision !== 'string' || !DECISION_KINDS.has(decision as ReviewDecisionKind)) {
707
+ return {
708
+ kind: 'err',
709
+ error: {
710
+ code: 'bad-input',
711
+ message: '`decision` must be one of: approve, reject, escalate, withdraw',
712
+ },
713
+ };
714
+ }
715
+ const rationale = b.rationale;
716
+ if (rationale !== undefined && typeof rationale !== 'string') {
717
+ return {
718
+ kind: 'err',
719
+ error: { code: 'bad-input', message: '`rationale` must be a string when supplied' },
720
+ };
721
+ }
722
+ const out: ParsedComplete = {
723
+ decision: decision as ReviewDecisionKind,
724
+ ...(rationale !== undefined && { rationale }),
725
+ ...('value' in b && { value: b.value }),
726
+ };
727
+ return { kind: 'ok', value: out };
728
+ }
729
+
730
+ interface ParsedAuditBundle {
731
+ readonly signingKeyId: SigningKeyId;
732
+ readonly includeMessages: boolean;
733
+ }
734
+
735
+ function parseAuditBundleBody(
736
+ body: Record<string, unknown>,
737
+ ):
738
+ | { readonly kind: 'ok'; readonly value: ParsedAuditBundle }
739
+ | { readonly kind: 'err'; readonly message: string } {
740
+ const raw = body.signingKeyId;
741
+ if (typeof raw !== 'string' || raw.length === 0) {
742
+ return { kind: 'err', message: 'Field `signingKeyId` must be a non-empty string' };
743
+ }
744
+ let includeMessages = false;
745
+ if ('includeMessages' in body) {
746
+ const im = body.includeMessages;
747
+ if (typeof im !== 'boolean') {
748
+ return { kind: 'err', message: 'Field `includeMessages` must be a boolean when present' };
749
+ }
750
+ includeMessages = im;
751
+ }
752
+ return {
753
+ kind: 'ok',
754
+ value: {
755
+ signingKeyId: raw as SigningKeyId,
756
+ includeMessages,
757
+ },
758
+ };
759
+ }