@kindgi/api 0.1.4 → 0.1.5

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 (516) hide show
  1. package/dist/agent-binding.d.ts +13 -0
  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 +18 -2
  5. package/dist/agent-pins.js.map +1 -1
  6. package/dist/app.d.ts +110 -17
  7. package/dist/app.d.ts.map +1 -1
  8. package/dist/app.js +163 -35
  9. package/dist/app.js.map +1 -1
  10. package/dist/block-publish.d.ts +56 -0
  11. package/dist/block-publish.d.ts.map +1 -0
  12. package/dist/block-publish.js +132 -0
  13. package/dist/block-publish.js.map +1 -0
  14. package/dist/caller.d.ts +24 -0
  15. package/dist/caller.d.ts.map +1 -0
  16. package/dist/caller.js +47 -0
  17. package/dist/caller.js.map +1 -0
  18. package/dist/capability-binding.d.ts +11 -0
  19. package/dist/capability-binding.d.ts.map +1 -1
  20. package/dist/cost-binding.d.ts +19 -0
  21. package/dist/cost-binding.d.ts.map +1 -1
  22. package/dist/cost-binding.js.map +1 -1
  23. package/dist/derive-agent-version.d.ts +7 -0
  24. package/dist/derive-agent-version.d.ts.map +1 -1
  25. package/dist/derive-agent-version.js +4 -0
  26. package/dist/derive-agent-version.js.map +1 -1
  27. package/dist/errors.d.ts.map +1 -1
  28. package/dist/errors.js +75 -7
  29. package/dist/errors.js.map +1 -1
  30. package/dist/eval-case-binding.d.ts +16 -4
  31. package/dist/eval-case-binding.d.ts.map +1 -1
  32. package/dist/eval-run-binding.d.ts +31 -0
  33. package/dist/eval-run-binding.d.ts.map +1 -1
  34. package/dist/eval-run-dispatcher.d.ts +7 -0
  35. package/dist/eval-run-dispatcher.d.ts.map +1 -1
  36. package/dist/eval-run-dispatcher.js.map +1 -1
  37. package/dist/eval-sample.d.ts +11 -0
  38. package/dist/eval-sample.d.ts.map +1 -0
  39. package/dist/eval-sample.js +34 -0
  40. package/dist/eval-sample.js.map +1 -0
  41. package/dist/eval-suite-binding.d.ts +13 -0
  42. package/dist/eval-suite-binding.d.ts.map +1 -1
  43. package/dist/eval-suite-binding.js.map +1 -1
  44. package/dist/flow-binding.d.ts +18 -0
  45. package/dist/flow-binding.d.ts.map +1 -1
  46. package/dist/flow-pins.d.ts.map +1 -1
  47. package/dist/flow-pins.js +18 -2
  48. package/dist/flow-pins.js.map +1 -1
  49. package/dist/gate.d.ts.map +1 -1
  50. package/dist/gate.js +25 -0
  51. package/dist/gate.js.map +1 -1
  52. package/dist/handler-binding.d.ts +32 -0
  53. package/dist/handler-binding.d.ts.map +1 -1
  54. package/dist/hitl-binding.d.ts +23 -0
  55. package/dist/hitl-binding.d.ts.map +1 -1
  56. package/dist/identity-directory-binding.d.ts +82 -4
  57. package/dist/identity-directory-binding.d.ts.map +1 -1
  58. package/dist/identity-provider-binding.d.ts +197 -17
  59. package/dist/identity-provider-binding.d.ts.map +1 -1
  60. package/dist/improvement-pass-binding.d.ts +196 -0
  61. package/dist/improvement-pass-binding.d.ts.map +1 -0
  62. package/dist/improvement-pass-binding.js +9 -0
  63. package/dist/improvement-pass-binding.js.map +1 -0
  64. package/dist/index.d.ts +35 -17
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +11 -3
  67. package/dist/index.js.map +1 -1
  68. package/dist/judged-dispatcher.d.ts +25 -4
  69. package/dist/judged-dispatcher.d.ts.map +1 -1
  70. package/dist/judged-dispatcher.js +43 -6
  71. package/dist/judged-dispatcher.js.map +1 -1
  72. package/dist/judgment-binding.d.ts +25 -1
  73. package/dist/judgment-binding.d.ts.map +1 -1
  74. package/dist/mcp-endpoint-binding.d.ts +7 -0
  75. package/dist/mcp-endpoint-binding.d.ts.map +1 -1
  76. package/dist/memory-binding.d.ts +124 -17
  77. package/dist/memory-binding.d.ts.map +1 -1
  78. package/dist/memory-erasure-binding.d.ts +145 -0
  79. package/dist/memory-erasure-binding.d.ts.map +1 -0
  80. package/dist/memory-erasure-binding.js +4 -0
  81. package/dist/memory-erasure-binding.js.map +1 -0
  82. package/dist/middleware/auth.d.ts +66 -13
  83. package/dist/middleware/auth.d.ts.map +1 -1
  84. package/dist/middleware/auth.js +150 -50
  85. package/dist/middleware/auth.js.map +1 -1
  86. package/dist/middleware/authorize.d.ts +9 -0
  87. package/dist/middleware/authorize.d.ts.map +1 -1
  88. package/dist/middleware/authorize.js +122 -4
  89. package/dist/middleware/authorize.js.map +1 -1
  90. package/dist/middleware/error-mapper.d.ts +5 -0
  91. package/dist/middleware/error-mapper.d.ts.map +1 -1
  92. package/dist/middleware/error-mapper.js +14 -1
  93. package/dist/middleware/error-mapper.js.map +1 -1
  94. package/dist/middleware/idempotency.d.ts +70 -5
  95. package/dist/middleware/idempotency.d.ts.map +1 -1
  96. package/dist/middleware/idempotency.js +174 -41
  97. package/dist/middleware/idempotency.js.map +1 -1
  98. package/dist/middleware/key-project.d.ts +17 -0
  99. package/dist/middleware/key-project.d.ts.map +1 -0
  100. package/dist/middleware/key-project.js +75 -0
  101. package/dist/middleware/key-project.js.map +1 -0
  102. package/dist/middleware/principal.d.ts.map +1 -1
  103. package/dist/middleware/principal.js +9 -5
  104. package/dist/middleware/principal.js.map +1 -1
  105. package/dist/middleware/project-ref.d.ts.map +1 -1
  106. package/dist/middleware/project-ref.js +3 -0
  107. package/dist/middleware/project-ref.js.map +1 -1
  108. package/dist/openapi/generate.d.ts.map +1 -1
  109. package/dist/openapi/generate.js +5 -2
  110. package/dist/openapi/generate.js.map +1 -1
  111. package/dist/openapi/operations.d.ts.map +1 -1
  112. package/dist/openapi/operations.js +919 -140
  113. package/dist/openapi/operations.js.map +1 -1
  114. package/dist/openapi/schemas.d.ts +90 -13
  115. package/dist/openapi/schemas.d.ts.map +1 -1
  116. package/dist/openapi/schemas.js +2081 -359
  117. package/dist/openapi/schemas.js.map +1 -1
  118. package/dist/person-grants-binding.d.ts +77 -0
  119. package/dist/person-grants-binding.d.ts.map +1 -0
  120. package/dist/person-grants-binding.js +4 -0
  121. package/dist/person-grants-binding.js.map +1 -0
  122. package/dist/proposal-service.d.ts +121 -0
  123. package/dist/proposal-service.d.ts.map +1 -0
  124. package/dist/proposal-service.js +447 -0
  125. package/dist/proposal-service.js.map +1 -0
  126. package/dist/proposal-status.d.ts +60 -0
  127. package/dist/proposal-status.d.ts.map +1 -0
  128. package/dist/proposal-status.js +109 -0
  129. package/dist/proposal-status.js.map +1 -0
  130. package/dist/provider-binding.d.ts +10 -0
  131. package/dist/provider-binding.d.ts.map +1 -1
  132. package/dist/publish-refused.d.ts +28 -3
  133. package/dist/publish-refused.d.ts.map +1 -1
  134. package/dist/publish-refused.js +24 -3
  135. package/dist/publish-refused.js.map +1 -1
  136. package/dist/routes/adapters.d.ts +2 -1
  137. package/dist/routes/adapters.d.ts.map +1 -1
  138. package/dist/routes/adapters.js +3 -1
  139. package/dist/routes/adapters.js.map +1 -1
  140. package/dist/routes/agent-releases.d.ts +55 -3
  141. package/dist/routes/agent-releases.d.ts.map +1 -1
  142. package/dist/routes/agent-releases.js +85 -55
  143. package/dist/routes/agent-releases.js.map +1 -1
  144. package/dist/routes/agents.d.ts +12 -1
  145. package/dist/routes/agents.d.ts.map +1 -1
  146. package/dist/routes/agents.js +100 -2
  147. package/dist/routes/agents.js.map +1 -1
  148. package/dist/routes/approvals.d.ts +15 -3
  149. package/dist/routes/approvals.d.ts.map +1 -1
  150. package/dist/routes/approvals.js +133 -122
  151. package/dist/routes/approvals.js.map +1 -1
  152. package/dist/routes/artifacts.d.ts +25 -3
  153. package/dist/routes/artifacts.d.ts.map +1 -1
  154. package/dist/routes/artifacts.js +116 -7
  155. package/dist/routes/artifacts.js.map +1 -1
  156. package/dist/routes/auth.d.ts +25 -1
  157. package/dist/routes/auth.d.ts.map +1 -1
  158. package/dist/routes/auth.js +655 -281
  159. package/dist/routes/auth.js.map +1 -1
  160. package/dist/routes/blocks.d.ts.map +1 -1
  161. package/dist/routes/blocks.js +2 -42
  162. package/dist/routes/blocks.js.map +1 -1
  163. package/dist/routes/capabilities.d.ts +2 -1
  164. package/dist/routes/capabilities.d.ts.map +1 -1
  165. package/dist/routes/capabilities.js +6 -1
  166. package/dist/routes/capabilities.js.map +1 -1
  167. package/dist/routes/compliance.d.ts +9 -10
  168. package/dist/routes/compliance.d.ts.map +1 -1
  169. package/dist/routes/compliance.js +42 -61
  170. package/dist/routes/compliance.js.map +1 -1
  171. package/dist/routes/conversations.d.ts +8 -1
  172. package/dist/routes/conversations.d.ts.map +1 -1
  173. package/dist/routes/conversations.js +87 -8
  174. package/dist/routes/conversations.js.map +1 -1
  175. package/dist/routes/cost.d.ts +12 -1
  176. package/dist/routes/cost.d.ts.map +1 -1
  177. package/dist/routes/cost.js +74 -3
  178. package/dist/routes/cost.js.map +1 -1
  179. package/dist/routes/denied.d.ts +11 -0
  180. package/dist/routes/denied.d.ts.map +1 -0
  181. package/dist/routes/denied.js +17 -0
  182. package/dist/routes/denied.js.map +1 -0
  183. package/dist/routes/deployments.d.ts +10 -1
  184. package/dist/routes/deployments.d.ts.map +1 -1
  185. package/dist/routes/deployments.js +171 -13
  186. package/dist/routes/deployments.js.map +1 -1
  187. package/dist/routes/eval-comparison.d.ts.map +1 -1
  188. package/dist/routes/eval-comparison.js +69 -1
  189. package/dist/routes/eval-comparison.js.map +1 -1
  190. package/dist/routes/eval-overrides.d.ts +18 -0
  191. package/dist/routes/eval-overrides.d.ts.map +1 -0
  192. package/dist/routes/eval-overrides.js +81 -0
  193. package/dist/routes/eval-overrides.js.map +1 -0
  194. package/dist/routes/eval-runs.d.ts +10 -1
  195. package/dist/routes/eval-runs.d.ts.map +1 -1
  196. package/dist/routes/eval-runs.js +66 -7
  197. package/dist/routes/eval-runs.js.map +1 -1
  198. package/dist/routes/eval-suites.d.ts.map +1 -1
  199. package/dist/routes/eval-suites.js +28 -2
  200. package/dist/routes/eval-suites.js.map +1 -1
  201. package/dist/routes/event-triggers.d.ts +5 -1
  202. package/dist/routes/event-triggers.d.ts.map +1 -1
  203. package/dist/routes/event-triggers.js +26 -3
  204. package/dist/routes/event-triggers.js.map +1 -1
  205. package/dist/routes/export-signing-keys.d.ts +12 -0
  206. package/dist/routes/export-signing-keys.d.ts.map +1 -0
  207. package/dist/routes/export-signing-keys.js +24 -0
  208. package/dist/routes/export-signing-keys.js.map +1 -0
  209. package/dist/routes/flows.d.ts.map +1 -1
  210. package/dist/routes/flows.js +9 -1
  211. package/dist/routes/flows.js.map +1 -1
  212. package/dist/routes/gate-policies.d.ts +4 -1
  213. package/dist/routes/gate-policies.d.ts.map +1 -1
  214. package/dist/routes/gate-policies.js +29 -5
  215. package/dist/routes/gate-policies.js.map +1 -1
  216. package/dist/routes/guardrails.d.ts +16 -14
  217. package/dist/routes/guardrails.d.ts.map +1 -1
  218. package/dist/routes/guardrails.js +39 -3
  219. package/dist/routes/guardrails.js.map +1 -1
  220. package/dist/routes/identity.d.ts +20 -4
  221. package/dist/routes/identity.d.ts.map +1 -1
  222. package/dist/routes/identity.js +246 -6
  223. package/dist/routes/identity.js.map +1 -1
  224. package/dist/routes/improvement-passes.d.ts +52 -0
  225. package/dist/routes/improvement-passes.d.ts.map +1 -0
  226. package/dist/routes/improvement-passes.js +449 -0
  227. package/dist/routes/improvement-passes.js.map +1 -0
  228. package/dist/routes/judged-suites.d.ts +50 -0
  229. package/dist/routes/judged-suites.d.ts.map +1 -1
  230. package/dist/routes/judged-suites.js +66 -27
  231. package/dist/routes/judged-suites.js.map +1 -1
  232. package/dist/routes/judgment-context.d.ts +18 -2
  233. package/dist/routes/judgment-context.d.ts.map +1 -1
  234. package/dist/routes/judgment-context.js +63 -5
  235. package/dist/routes/judgment-context.js.map +1 -1
  236. package/dist/routes/judgment-flow-context.d.ts.map +1 -1
  237. package/dist/routes/judgment-flow-context.js +14 -2
  238. package/dist/routes/judgment-flow-context.js.map +1 -1
  239. package/dist/routes/judgments.d.ts.map +1 -1
  240. package/dist/routes/judgments.js +8 -1
  241. package/dist/routes/judgments.js.map +1 -1
  242. package/dist/routes/mcp.d.ts.map +1 -1
  243. package/dist/routes/mcp.js +26 -1
  244. package/dist/routes/mcp.js.map +1 -1
  245. package/dist/routes/memory-access.d.ts +43 -0
  246. package/dist/routes/memory-access.d.ts.map +1 -0
  247. package/dist/routes/memory-access.js +114 -0
  248. package/dist/routes/memory-access.js.map +1 -0
  249. package/dist/routes/memory-erasures.d.ts +25 -0
  250. package/dist/routes/memory-erasures.d.ts.map +1 -0
  251. package/dist/routes/memory-erasures.js +214 -0
  252. package/dist/routes/memory-erasures.js.map +1 -0
  253. package/dist/routes/memory-parse.d.ts +38 -0
  254. package/dist/routes/memory-parse.d.ts.map +1 -0
  255. package/dist/routes/memory-parse.js +199 -0
  256. package/dist/routes/memory-parse.js.map +1 -0
  257. package/dist/routes/memory.d.ts +9 -4
  258. package/dist/routes/memory.d.ts.map +1 -1
  259. package/dist/routes/memory.js +226 -234
  260. package/dist/routes/memory.js.map +1 -1
  261. package/dist/routes/observations.d.ts +4 -1
  262. package/dist/routes/observations.d.ts.map +1 -1
  263. package/dist/routes/observations.js +8 -2
  264. package/dist/routes/observations.js.map +1 -1
  265. package/dist/routes/pagination.d.ts +7 -0
  266. package/dist/routes/pagination.d.ts.map +1 -1
  267. package/dist/routes/pagination.js +10 -0
  268. package/dist/routes/pagination.js.map +1 -1
  269. package/dist/routes/policies.d.ts +2 -1
  270. package/dist/routes/policies.d.ts.map +1 -1
  271. package/dist/routes/policies.js +3 -1
  272. package/dist/routes/policies.js.map +1 -1
  273. package/dist/routes/project-mismatch.d.ts +22 -0
  274. package/dist/routes/project-mismatch.d.ts.map +1 -0
  275. package/dist/routes/project-mismatch.js +34 -0
  276. package/dist/routes/project-mismatch.js.map +1 -0
  277. package/dist/routes/projects.d.ts +15 -4
  278. package/dist/routes/projects.d.ts.map +1 -1
  279. package/dist/routes/projects.js +100 -9
  280. package/dist/routes/projects.js.map +1 -1
  281. package/dist/routes/proposals.d.ts +25 -17
  282. package/dist/routes/proposals.d.ts.map +1 -1
  283. package/dist/routes/proposals.js +340 -507
  284. package/dist/routes/proposals.js.map +1 -1
  285. package/dist/routes/provenance.d.ts +17 -3
  286. package/dist/routes/provenance.d.ts.map +1 -1
  287. package/dist/routes/provenance.js +79 -103
  288. package/dist/routes/provenance.js.map +1 -1
  289. package/dist/routes/providers.d.ts +17 -1
  290. package/dist/routes/providers.d.ts.map +1 -1
  291. package/dist/routes/providers.js +110 -3
  292. package/dist/routes/providers.js.map +1 -1
  293. package/dist/routes/public-run-tokens.d.ts.map +1 -1
  294. package/dist/routes/public-run-tokens.js +3 -0
  295. package/dist/routes/public-run-tokens.js.map +1 -1
  296. package/dist/routes/readable-projects.d.ts +26 -0
  297. package/dist/routes/readable-projects.d.ts.map +1 -0
  298. package/dist/routes/readable-projects.js +39 -0
  299. package/dist/routes/readable-projects.js.map +1 -0
  300. package/dist/routes/runs.d.ts +10 -0
  301. package/dist/routes/runs.d.ts.map +1 -1
  302. package/dist/routes/runs.js +92 -42
  303. package/dist/routes/runs.js.map +1 -1
  304. package/dist/routes/schedules.d.ts +21 -13
  305. package/dist/routes/schedules.d.ts.map +1 -1
  306. package/dist/routes/schedules.js +416 -55
  307. package/dist/routes/schedules.js.map +1 -1
  308. package/dist/routes/segments.d.ts +2 -0
  309. package/dist/routes/segments.d.ts.map +1 -1
  310. package/dist/routes/segments.js +5 -0
  311. package/dist/routes/segments.js.map +1 -1
  312. package/dist/routes/service-accounts.d.ts +17 -0
  313. package/dist/routes/service-accounts.d.ts.map +1 -0
  314. package/dist/routes/service-accounts.js +212 -0
  315. package/dist/routes/service-accounts.js.map +1 -0
  316. package/dist/routes/sign-in-options.d.ts +50 -0
  317. package/dist/routes/sign-in-options.d.ts.map +1 -0
  318. package/dist/routes/sign-in-options.js +98 -0
  319. package/dist/routes/sign-in-options.js.map +1 -0
  320. package/dist/routes/signing-keys.d.ts +2 -1
  321. package/dist/routes/signing-keys.d.ts.map +1 -1
  322. package/dist/routes/signing-keys.js +3 -1
  323. package/dist/routes/signing-keys.js.map +1 -1
  324. package/dist/routes/tenant-access.d.ts +13 -0
  325. package/dist/routes/tenant-access.d.ts.map +1 -0
  326. package/dist/routes/tenant-access.js +26 -0
  327. package/dist/routes/tenant-access.js.map +1 -0
  328. package/dist/routes/tenant.d.ts.map +1 -1
  329. package/dist/routes/tenant.js +4 -0
  330. package/dist/routes/tenant.js.map +1 -1
  331. package/dist/routes/token-sign-in.d.ts +36 -0
  332. package/dist/routes/token-sign-in.d.ts.map +1 -0
  333. package/dist/routes/token-sign-in.js +101 -0
  334. package/dist/routes/token-sign-in.js.map +1 -0
  335. package/dist/routes/tokens.d.ts +15 -5
  336. package/dist/routes/tokens.d.ts.map +1 -1
  337. package/dist/routes/tokens.js +162 -43
  338. package/dist/routes/tokens.js.map +1 -1
  339. package/dist/routes/tools.d.ts.map +1 -1
  340. package/dist/routes/tools.js +9 -1
  341. package/dist/routes/tools.js.map +1 -1
  342. package/dist/routes/trigger-access.d.ts +29 -0
  343. package/dist/routes/trigger-access.d.ts.map +1 -0
  344. package/dist/routes/trigger-access.js +33 -0
  345. package/dist/routes/trigger-access.js.map +1 -0
  346. package/dist/routes/webhook-endpoints.d.ts +2 -1
  347. package/dist/routes/webhook-endpoints.d.ts.map +1 -1
  348. package/dist/routes/webhook-endpoints.js +11 -3
  349. package/dist/routes/webhook-endpoints.js.map +1 -1
  350. package/dist/routes/webhooks.d.ts +5 -1
  351. package/dist/routes/webhooks.d.ts.map +1 -1
  352. package/dist/routes/webhooks.js +25 -2
  353. package/dist/routes/webhooks.js.map +1 -1
  354. package/dist/run-failure.d.ts +21 -0
  355. package/dist/run-failure.d.ts.map +1 -0
  356. package/dist/run-failure.js +29 -0
  357. package/dist/run-failure.js.map +1 -0
  358. package/dist/service-account-binding.d.ts +108 -0
  359. package/dist/service-account-binding.d.ts.map +1 -0
  360. package/dist/service-account-binding.js +4 -0
  361. package/dist/service-account-binding.js.map +1 -0
  362. package/dist/session-store-binding.d.ts +61 -13
  363. package/dist/session-store-binding.d.ts.map +1 -1
  364. package/dist/signed-export.d.ts +89 -0
  365. package/dist/signed-export.d.ts.map +1 -0
  366. package/dist/signed-export.js +149 -0
  367. package/dist/signed-export.js.map +1 -0
  368. package/dist/supervisor-binding.d.ts +186 -413
  369. package/dist/supervisor-binding.d.ts.map +1 -1
  370. package/dist/testing/app-bindings.d.ts +33 -0
  371. package/dist/testing/app-bindings.d.ts.map +1 -0
  372. package/dist/testing/app-bindings.js +112 -0
  373. package/dist/testing/app-bindings.js.map +1 -0
  374. package/dist/testing/index.d.ts +11 -0
  375. package/dist/testing/index.d.ts.map +1 -0
  376. package/dist/testing/index.js +13 -0
  377. package/dist/testing/index.js.map +1 -0
  378. package/dist/testing/stub-binding.d.ts +34 -0
  379. package/dist/testing/stub-binding.d.ts.map +1 -0
  380. package/dist/testing/stub-binding.js +42 -0
  381. package/dist/testing/stub-binding.js.map +1 -0
  382. package/dist/testing/trigger-registry.d.ts +21 -0
  383. package/dist/testing/trigger-registry.d.ts.map +1 -0
  384. package/dist/testing/trigger-registry.js +206 -0
  385. package/dist/testing/trigger-registry.js.map +1 -0
  386. package/dist/token-admin.d.ts +46 -8
  387. package/dist/token-admin.d.ts.map +1 -1
  388. package/dist/token-admin.js.map +1 -1
  389. package/dist/tool-binding.d.ts +13 -0
  390. package/dist/tool-binding.d.ts.map +1 -1
  391. package/dist/trigger-binding.d.ts +2 -2
  392. package/dist/trigger-binding.d.ts.map +1 -1
  393. package/dist/trigger-binding.js +1 -1
  394. package/dist/trigger-binding.js.map +1 -1
  395. package/dist/types.d.ts +16 -0
  396. package/dist/types.d.ts.map +1 -1
  397. package/dist/webhook-endpoint-binding.d.ts +19 -3
  398. package/dist/webhook-endpoint-binding.d.ts.map +1 -1
  399. package/dist/webhook-endpoint-binding.js +1 -1
  400. package/dist/webhook-endpoint-binding.js.map +1 -1
  401. package/openapi.json +15387 -8789
  402. package/package.json +26 -22
  403. package/src/agent-binding.ts +14 -0
  404. package/src/agent-pins.ts +18 -2
  405. package/src/app.ts +366 -80
  406. package/src/block-publish.ts +181 -0
  407. package/src/caller.ts +62 -0
  408. package/src/capability-binding.ts +12 -0
  409. package/src/cost-binding.ts +19 -0
  410. package/src/derive-agent-version.ts +9 -1
  411. package/src/errors.ts +75 -7
  412. package/src/eval-case-binding.ts +17 -1
  413. package/src/eval-run-binding.ts +31 -0
  414. package/src/eval-run-dispatcher.ts +7 -0
  415. package/src/eval-sample.ts +42 -0
  416. package/src/eval-suite-binding.ts +14 -0
  417. package/src/flow-binding.ts +19 -0
  418. package/src/flow-pins.ts +18 -2
  419. package/src/gate.ts +26 -0
  420. package/src/handler-binding.ts +36 -1
  421. package/src/hitl-binding.ts +24 -0
  422. package/src/identity-directory-binding.ts +85 -4
  423. package/src/identity-provider-binding.ts +216 -21
  424. package/src/improvement-pass-binding.ts +193 -0
  425. package/src/index.ts +153 -31
  426. package/src/judged-dispatcher.ts +78 -11
  427. package/src/judgment-binding.ts +25 -1
  428. package/src/mcp-endpoint-binding.ts +7 -0
  429. package/src/memory-binding.ts +121 -17
  430. package/src/memory-erasure-binding.ts +156 -0
  431. package/src/middleware/auth.ts +228 -62
  432. package/src/middleware/authorize.ts +155 -3
  433. package/src/middleware/error-mapper.ts +15 -1
  434. package/src/middleware/idempotency.ts +230 -43
  435. package/src/middleware/key-project.ts +86 -0
  436. package/src/middleware/principal.ts +9 -5
  437. package/src/middleware/project-ref.ts +3 -0
  438. package/src/openapi/generate.ts +11 -2
  439. package/src/openapi/operations.ts +1060 -143
  440. package/src/openapi/schemas.ts +2387 -469
  441. package/src/person-grants-binding.ts +76 -0
  442. package/src/proposal-service.ts +640 -0
  443. package/src/proposal-status.ts +176 -0
  444. package/src/provider-binding.ts +10 -0
  445. package/src/publish-refused.ts +56 -9
  446. package/src/routes/adapters.ts +4 -0
  447. package/src/routes/agent-releases.ts +121 -69
  448. package/src/routes/agents.ts +128 -1
  449. package/src/routes/approvals.ts +164 -154
  450. package/src/routes/artifacts.ts +181 -11
  451. package/src/routes/auth.ts +804 -336
  452. package/src/routes/blocks.ts +2 -53
  453. package/src/routes/capabilities.ts +10 -1
  454. package/src/routes/compliance.ts +60 -99
  455. package/src/routes/conversations.ts +101 -7
  456. package/src/routes/cost.ts +110 -4
  457. package/src/routes/denied.ts +28 -0
  458. package/src/routes/deployments.ts +210 -17
  459. package/src/routes/eval-comparison.ts +81 -2
  460. package/src/routes/eval-overrides.ts +108 -0
  461. package/src/routes/eval-runs.ts +89 -6
  462. package/src/routes/eval-suites.ts +32 -2
  463. package/src/routes/event-triggers.ts +27 -3
  464. package/src/routes/export-signing-keys.ts +31 -0
  465. package/src/routes/flows.ts +12 -1
  466. package/src/routes/gate-policies.ts +37 -6
  467. package/src/routes/guardrails.ts +61 -2
  468. package/src/routes/identity.ts +328 -10
  469. package/src/routes/improvement-passes.ts +541 -0
  470. package/src/routes/judged-suites.ts +113 -43
  471. package/src/routes/judgment-context.ts +76 -5
  472. package/src/routes/judgment-flow-context.ts +16 -2
  473. package/src/routes/judgments.ts +17 -3
  474. package/src/routes/mcp.ts +29 -1
  475. package/src/routes/memory-access.ts +160 -0
  476. package/src/routes/memory-erasures.ts +240 -0
  477. package/src/routes/memory-parse.ts +250 -0
  478. package/src/routes/memory.ts +244 -286
  479. package/src/routes/observations.ts +15 -2
  480. package/src/routes/pagination.ts +12 -0
  481. package/src/routes/policies.ts +7 -1
  482. package/src/routes/project-mismatch.ts +56 -0
  483. package/src/routes/projects.ts +107 -13
  484. package/src/routes/proposals.ts +370 -611
  485. package/src/routes/provenance.ts +99 -133
  486. package/src/routes/providers.ts +142 -3
  487. package/src/routes/public-run-tokens.ts +3 -0
  488. package/src/routes/readable-projects.ts +66 -0
  489. package/src/routes/runs.ts +119 -39
  490. package/src/routes/schedules.ts +472 -60
  491. package/src/routes/segments.ts +11 -0
  492. package/src/routes/service-accounts.ts +262 -0
  493. package/src/routes/sign-in-options.ts +168 -0
  494. package/src/routes/signing-keys.ts +7 -1
  495. package/src/routes/tenant-access.ts +37 -0
  496. package/src/routes/tenant.ts +6 -0
  497. package/src/routes/token-sign-in.ts +161 -0
  498. package/src/routes/tokens.ts +172 -46
  499. package/src/routes/tools.ts +12 -1
  500. package/src/routes/trigger-access.ts +72 -0
  501. package/src/routes/webhook-endpoints.ts +15 -3
  502. package/src/routes/webhooks.ts +26 -2
  503. package/src/run-failure.ts +42 -0
  504. package/src/service-account-binding.ts +113 -0
  505. package/src/session-store-binding.ts +65 -13
  506. package/src/signed-export.ts +250 -0
  507. package/src/supervisor-binding.ts +169 -409
  508. package/src/testing/app-bindings.ts +142 -0
  509. package/src/testing/index.ts +21 -0
  510. package/src/testing/stub-binding.ts +53 -0
  511. package/src/testing/trigger-registry.ts +259 -0
  512. package/src/token-admin.ts +40 -8
  513. package/src/tool-binding.ts +14 -0
  514. package/src/trigger-binding.ts +8 -1
  515. package/src/types.ts +16 -0
  516. package/src/webhook-endpoint-binding.ts +18 -3
@@ -1,16 +1,18 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // Copyright (C) 2026 Kindgi Inc.
3
3
 
4
- import { createHash } from 'node:crypto';
4
+ import { createHash, randomUUID } from 'node:crypto';
5
5
 
6
- import type { MiddlewareHandler } from 'hono';
6
+ import type { Context, MiddlewareHandler } from 'hono';
7
7
 
8
8
  import type { TenantId } from '@kindgi/types';
9
9
 
10
+ import { callerRef } from '../caller.js';
10
11
  import { statusFor, toWireError } from '../errors.js';
12
+ import type { AppEnv } from '../types.js';
11
13
 
12
14
  /**
13
- * Idempotency-key store: `(tenantId, route, key) → cached response`,
15
+ * Idempotency-key store: `(tenantId, caller, route, key) → cached response`,
14
16
  * cached for `ttlMs` (default 24h per API-ROUTE-CONVENTIONS.md §3.1).
15
17
  *
16
18
  * Injected into the middleware so deployments can plug in a durable
@@ -20,38 +22,117 @@ import { statusFor, toWireError } from '../errors.js';
20
22
  */
21
23
  export interface IdempotencyStore {
22
24
  get(key: string): Promise<StoredIdempotencyEntry | null>;
25
+ /** Store an answer. It replaces a hold on the key, never a stored answer: the first stays. */
23
26
  set(key: string, entry: StoredIdempotencyEntry): Promise<void>;
27
+ /**
28
+ * Hold a key while its request runs (T349), so a repeat sent meanwhile
29
+ * is answered 409 `idempotency-key-in-flight` instead of running the
30
+ * operation again. Optional: a store without holds holds nothing, and
31
+ * such a repeat runs again (the behaviour before 0.1.5).
32
+ */
33
+ readonly holds?: IdempotencyHolds;
24
34
  }
25
35
 
36
+ /** A store's holds: all three or none. */
37
+ export interface IdempotencyHolds {
38
+ /** Take the key for `hold.holder`, unless an unexpired hold or a stored answer has it. */
39
+ hold(key: string, hold: IdempotencyHold): Promise<IdempotencyHoldOutcome>;
40
+ /** Extend the holder's hold: its request still runs. */
41
+ renew(key: string, holder: string, expiresAt: number): Promise<void>;
42
+ /** Drop the holder's hold: its request ended with nothing to replay. */
43
+ release(key: string, holder: string): Promise<void>;
44
+ }
45
+
46
+ export interface IdempotencyHold {
47
+ /** Who holds it: one id per request. */
48
+ readonly holder: string;
49
+ readonly bodyHash: string;
50
+ readonly expiresAt: number;
51
+ }
52
+
53
+ export type IdempotencyHoldOutcome =
54
+ | { readonly kind: 'held' }
55
+ | { readonly kind: 'in-flight'; readonly bodyHash: string }
56
+ | { readonly kind: 'stored'; readonly entry: StoredIdempotencyEntry };
57
+
26
58
  export interface StoredIdempotencyEntry {
27
59
  readonly bodyHash: string;
28
60
  readonly status: number;
29
61
  readonly contentType: string;
62
+ /** The answer's body; empty when it's `withheld`. */
30
63
  readonly bodyText: string;
31
64
  readonly expiresAt: number;
65
+ /**
66
+ * The answer carried a secret (`withholdFromReplay`), so only that the
67
+ * request succeeded is kept: a repeat is answered 409
68
+ * `idempotency-key-replay-withheld`, never the secret. Optional: a store
69
+ * that doesn't keep it stores the empty body, and a repeat replays that.
70
+ */
71
+ readonly withheld?: boolean;
72
+ /** When the answer was stored (ms since the epoch), for the withheld 409. */
73
+ readonly storedAt?: number;
74
+ }
75
+
76
+ /**
77
+ * For a route whose answer carries a secret (an API key, a session token,
78
+ * a signing secret): the Idempotency-Key middleware keeps that the request
79
+ * succeeded and when, never the answer. A repeat with the same key gets 409
80
+ * `idempotency-key-replay-withheld` instead of the secret, and instead of
81
+ * running again (which would make a second one). Call it before answering.
82
+ */
83
+ export function withholdFromReplay(c: Context<AppEnv>): void {
84
+ c.set('idempotencyWithhold', true);
32
85
  }
33
86
 
34
87
  /** Simple in-memory store with lazy expiry — swap for a DB store in prod. */
35
88
  export function createInMemoryIdempotencyStore(): IdempotencyStore {
36
89
  const table = new Map<string, StoredIdempotencyEntry>();
90
+ const held = new Map<string, IdempotencyHold>();
91
+ const stored = (key: string): StoredIdempotencyEntry | null => {
92
+ const hit = table.get(key);
93
+ if (hit === undefined) return null;
94
+ if (hit.expiresAt <= Date.now()) {
95
+ table.delete(key);
96
+ return null;
97
+ }
98
+ return hit;
99
+ };
37
100
  return {
38
101
  async get(key) {
39
- const hit = table.get(key);
40
- if (hit === undefined) return null;
41
- if (hit.expiresAt <= Date.now()) {
42
- table.delete(key);
43
- return null;
44
- }
45
- return hit;
102
+ return stored(key);
46
103
  },
47
104
  async set(key, entry) {
48
- table.set(key, entry);
105
+ held.delete(key);
106
+ if (stored(key) === null) table.set(key, entry);
107
+ },
108
+ holds: {
109
+ async hold(key, hold) {
110
+ const entry = stored(key);
111
+ if (entry !== null) return { kind: 'stored', entry };
112
+ const current = held.get(key);
113
+ if (current !== undefined && current.expiresAt > Date.now()) {
114
+ return { kind: 'in-flight', bodyHash: current.bodyHash };
115
+ }
116
+ held.set(key, hold);
117
+ return { kind: 'held' };
118
+ },
119
+ async renew(key, holder, expiresAt) {
120
+ const current = held.get(key);
121
+ if (current?.holder === holder) held.set(key, { ...current, expiresAt });
122
+ },
123
+ async release(key, holder) {
124
+ if (held.get(key)?.holder === holder) held.delete(key);
125
+ },
49
126
  },
50
127
  };
51
128
  }
52
129
 
53
130
  const MUTATING_METHODS: ReadonlySet<string> = new Set(['POST', 'PUT', 'PATCH', 'DELETE']);
54
131
  const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
132
+ /** How long a hold lasts unless renewed; renewed every third of it while the request runs. */
133
+ const DEFAULT_HOLD_MS = 30 * 1000;
134
+ /** What a repeat in flight is told to wait, in seconds. */
135
+ const IN_FLIGHT_RETRY_AFTER_S = 5;
55
136
 
56
137
  /**
57
138
  * Idempotency-Key middleware per API-ROUTE-CONVENTIONS.md §3.1.
@@ -68,16 +149,30 @@ const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
68
149
  * nothing, and a failure (5xx) may be transient: neither is stored, so
69
150
  * a retry with the same key after fixing the cause runs again instead
70
151
  * of replaying the old error.
152
+ * - With a store that holds keys (`store.holds`, T349): the key is held
153
+ * while the handler runs (for `holdMs`, renewed every third of it), so
154
+ * a repeat sent meanwhile gets 409 `idempotency-key-in-flight` with
155
+ * `Retry-After`, or `idempotency-key-body-mismatch` for another body,
156
+ * instead of running the operation again. The hold becomes the stored
157
+ * answer, or is released when nothing is stored. A crashed request's
158
+ * hold lapses within `holdMs`.
159
+ *
160
+ * The middleware sits AFTER auth so `tenantId` is available. The cache
161
+ * key is the tenant, the caller (`user:…`, `service_account:…`, else the
162
+ * key or session it came with), the route and the key: someone else in
163
+ * the tenant who sends the same key and body runs the request themselves,
164
+ * and never gets another caller's answer (T392).
71
165
  *
72
- * The middleware sits AFTER auth so `tenantId` is available; the
73
- * cache key is namespaced by tenant so callers can't collide across
74
- * tenants.
166
+ * A route whose answer carries a secret calls `withholdFromReplay`: its
167
+ * answer isn't kept, only that it succeeded (status, when). A repeat with
168
+ * the same key and body gets 409 `idempotency-key-replay-withheld`.
75
169
  */
76
170
  export function idempotencyMiddleware(
77
171
  store: IdempotencyStore,
78
- opts: { ttlMs?: number } = {},
172
+ opts: { ttlMs?: number; holdMs?: number } = {},
79
173
  ): MiddlewareHandler {
80
174
  const ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;
175
+ const holdMs = opts.holdMs ?? DEFAULT_HOLD_MS;
81
176
  return async (c, next) => {
82
177
  if (!MUTATING_METHODS.has(c.req.method.toUpperCase())) {
83
178
  return next();
@@ -96,51 +191,143 @@ export function idempotencyMiddleware(
96
191
  }
97
192
 
98
193
  const bodyText = await readRawBody(c.req.raw);
99
- const bodyHash = hashBody(bodyText);
100
- const cacheKey = `${tenantId}|${c.req.method.toUpperCase()}:${c.req.path}|${rawKey}`;
194
+ const bodyHash = sha256Hex(bodyText);
195
+ const cacheKey = `${tenantId}|${callerKey(c as Context<AppEnv>)}|${c.req.method.toUpperCase()}:${c.req.path}|${rawKey}`;
101
196
  const hit = await store.get(cacheKey);
197
+ if (hit !== null) return replay(c, hit, bodyHash, requestId, ttlMs);
102
198
 
103
- if (hit !== null) {
104
- if (hit.bodyHash !== bodyHash) {
105
- c.status(statusFor('idempotency-key-body-mismatch') as never);
199
+ const { holds } = store;
200
+ const holder = randomUUID();
201
+ if (holds !== undefined) {
202
+ const held = await holds.hold(cacheKey, {
203
+ holder,
204
+ bodyHash,
205
+ expiresAt: Date.now() + holdMs,
206
+ });
207
+ if (held.kind === 'stored') return replay(c, held.entry, bodyHash, requestId, ttlMs);
208
+ if (held.kind === 'in-flight') {
209
+ if (held.bodyHash !== bodyHash) return bodyMismatch(c, requestId);
210
+ c.header('Retry-After', String(IN_FLIGHT_RETRY_AFTER_S));
211
+ c.status(statusFor('idempotency-key-in-flight') as never);
106
212
  return c.json(
107
213
  toWireError(
108
214
  {
109
- code: 'idempotency-key-body-mismatch',
215
+ code: 'idempotency-key-in-flight',
110
216
  message:
111
- 'Idempotency-Key was reused with a different request body. Use a fresh key or the original body.',
217
+ "A request with this Idempotency-Key is still running. Retry after it answers (Retry-After), and you'll get its answer.",
112
218
  },
113
219
  requestId,
114
220
  ),
115
221
  );
116
222
  }
117
- return c.body(hit.bodyText, hit.status as never, {
118
- 'Content-Type': hit.contentType,
119
- 'X-Idempotent-Replay': 'true',
120
- });
121
223
  }
122
224
 
123
225
  // Re-attach the buffered body so downstream handlers reading
124
226
  // `c.req.json()` see the same bytes.
125
227
  if (bodyText.length > 0) rebindBody(c, bodyText);
126
- await next();
127
-
128
- const res = c.res;
129
- // Only what took effect is replayed; see the doc above.
130
- if (res.status >= 400) return;
131
- const savedBody = await res.clone().text();
132
- const contentType = res.headers.get('Content-Type') ?? 'application/octet-stream';
133
- await store.set(cacheKey, {
134
- bodyHash,
135
- status: res.status,
136
- contentType,
137
- bodyText: savedBody,
138
- expiresAt: Date.now() + ttlMs,
139
- });
140
- return;
228
+ const renewal =
229
+ holds === undefined
230
+ ? undefined
231
+ : setInterval(
232
+ () => {
233
+ holds.renew(cacheKey, holder, Date.now() + holdMs).catch(() => undefined);
234
+ },
235
+ Math.max(1, Math.floor(holdMs / 3)),
236
+ );
237
+ renewal?.unref?.();
238
+ let answered = false;
239
+ try {
240
+ await next();
241
+
242
+ const res = c.res;
243
+ // Only what took effect is replayed; see the doc above.
244
+ if (res.status >= 400) return;
245
+ const withheld = (c as Context<AppEnv>).get('idempotencyWithhold') === true;
246
+ const savedBody = withheld ? '' : await res.clone().text();
247
+ const contentType = res.headers.get('Content-Type') ?? 'application/octet-stream';
248
+ const now = Date.now();
249
+ await store.set(cacheKey, {
250
+ bodyHash,
251
+ status: res.status,
252
+ contentType,
253
+ bodyText: savedBody,
254
+ expiresAt: now + ttlMs,
255
+ ...(withheld && { withheld: true }),
256
+ storedAt: now,
257
+ });
258
+ answered = true;
259
+ } finally {
260
+ if (renewal !== undefined) clearInterval(renewal);
261
+ // Nothing stored (a refusal, a failure, a throw): let a retry run.
262
+ if (!answered && holds !== undefined) {
263
+ await holds.release(cacheKey, holder).catch(() => undefined);
264
+ }
265
+ }
141
266
  };
142
267
  }
143
268
 
269
+ /**
270
+ * Who the request comes from, for the cache key: the caller's principal
271
+ * (`user:…`, `service_account:…`; an API key with neither is its own
272
+ * service account), else the session it came with, else the credential
273
+ * itself (`token:` and the first 16 hex of its sha256: one bucket per
274
+ * credential, never a shared one, and nothing a reader could use).
275
+ */
276
+ function callerKey(c: Context<AppEnv>): string {
277
+ const ref = callerRef(c);
278
+ if (ref !== undefined) return ref;
279
+ const sessionId = c.get('sessionId');
280
+ if (sessionId !== undefined) return `session:${sessionId as unknown as string}`;
281
+ return `token:${sha256Hex(c.req.header('authorization') ?? '').slice(0, 16)}`;
282
+ }
283
+
284
+ /**
285
+ * A stored answer, replayed; or the refusal for the same key with another
286
+ * body; or, for an answer that carried a secret, what succeeded and when.
287
+ */
288
+ function replay(
289
+ c: Context,
290
+ hit: StoredIdempotencyEntry,
291
+ bodyHash: string,
292
+ requestId: string,
293
+ ttlMs: number,
294
+ ) {
295
+ if (hit.bodyHash !== bodyHash) return bodyMismatch(c, requestId);
296
+ if (hit.withheld === true) {
297
+ const at = new Date(hit.storedAt ?? hit.expiresAt - ttlMs).toISOString();
298
+ c.status(statusFor('idempotency-key-replay-withheld') as never);
299
+ return c.json(
300
+ toWireError(
301
+ {
302
+ code: 'idempotency-key-replay-withheld',
303
+ message: `The request with this Idempotency-Key succeeded (${hit.status}, at ${at}). Its answer carried a secret, which isn't kept, so it can't be sent again: read what it made (for an API key, \`GET /v1/tokens\`), or make a new one with a fresh key.`,
304
+ status: hit.status,
305
+ at,
306
+ },
307
+ requestId,
308
+ ),
309
+ );
310
+ }
311
+ return c.body(hit.bodyText, hit.status as never, {
312
+ 'Content-Type': hit.contentType,
313
+ 'X-Idempotent-Replay': 'true',
314
+ });
315
+ }
316
+
317
+ function bodyMismatch(c: Context, requestId: string) {
318
+ c.status(statusFor('idempotency-key-body-mismatch') as never);
319
+ return c.json(
320
+ toWireError(
321
+ {
322
+ code: 'idempotency-key-body-mismatch',
323
+ message:
324
+ 'Idempotency-Key was reused with a different request body. Use a fresh key or the original body.',
325
+ },
326
+ requestId,
327
+ ),
328
+ );
329
+ }
330
+
144
331
  async function readRawBody(req: Request): Promise<string> {
145
332
  const method = req.method.toUpperCase();
146
333
  if (method === 'GET' || method === 'HEAD') return '';
@@ -151,8 +338,8 @@ async function readRawBody(req: Request): Promise<string> {
151
338
  }
152
339
  }
153
340
 
154
- function hashBody(body: string): string {
155
- return createHash('sha256').update(body).digest('hex');
341
+ function sha256Hex(text: string): string {
342
+ return createHash('sha256').update(text).digest('hex');
156
343
  }
157
344
 
158
345
  function rebindBody(c: { req: { raw: Request } }, body: string): void {
@@ -0,0 +1,86 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+
4
+ import type { MiddlewareHandler } from 'hono';
5
+
6
+ import { statusFor, toWireError } from '../errors.js';
7
+ import type { AppEnv } from '../types.js';
8
+
9
+ const PROJECT_PATH_RE = /\/projects\/([^/]+)/;
10
+ const WRITE_METHODS: ReadonlySet<string> = new Set(['POST', 'PUT', 'PATCH']);
11
+
12
+ /**
13
+ * A key limited to a project (`projectId` at mint) is refused, `403
14
+ * key-project-mismatch`, on a request that names another project in any
15
+ * of the places a request names one:
16
+ *
17
+ * - the path: `/projects/<id>/…`;
18
+ * - the query: `?projectId=<id>`, or `?scopeKind=project&scopeId=<id>`;
19
+ * - a JSON write body: `projectId`, or `scope.projectId`.
20
+ *
21
+ * Requests from any other caller pass untouched. What the key may do in
22
+ * its own project is the authorizer's to decide, under the principal's
23
+ * grants.
24
+ */
25
+ export function refuseOtherProjectForKey(): MiddlewareHandler<AppEnv> {
26
+ return async (c, next) => {
27
+ const keyProject = c.get('tokenProjectId');
28
+ if (keyProject === undefined) return next();
29
+ for (const named of await projectsNamed(c.req)) {
30
+ if (named !== keyProject) {
31
+ c.status(statusFor('key-project-mismatch') as never);
32
+ return c.json(
33
+ toWireError(
34
+ {
35
+ code: 'key-project-mismatch',
36
+ message: `This API key is limited to project ${keyProject}; the request names project ${named}`,
37
+ keyProjectId: keyProject,
38
+ projectId: named,
39
+ },
40
+ c.get('requestId'),
41
+ ),
42
+ );
43
+ }
44
+ }
45
+ return next();
46
+ };
47
+ }
48
+
49
+ type RequestLike = {
50
+ readonly path: string;
51
+ readonly method: string;
52
+ query(name: string): string | undefined;
53
+ json(): Promise<unknown>;
54
+ };
55
+
56
+ /** Every project id the request names, in path, query and body. */
57
+ async function projectsNamed(req: RequestLike): Promise<string[]> {
58
+ const named: string[] = [];
59
+ const inPath = PROJECT_PATH_RE.exec(req.path)?.[1];
60
+ if (inPath !== undefined) named.push(decodeURIComponent(inPath));
61
+ const inQuery = req.query('projectId');
62
+ if (inQuery !== undefined && inQuery !== '') named.push(inQuery);
63
+ const scopeId = req.query('scopeId');
64
+ if (req.query('scopeKind') === 'project' && scopeId !== undefined) named.push(scopeId);
65
+ if (WRITE_METHODS.has(req.method)) named.push(...(await projectsInBody(req)));
66
+ return named;
67
+ }
68
+
69
+ /** `projectId` and `scope.projectId` in a JSON body; none when it isn't JSON. */
70
+ async function projectsInBody(req: RequestLike): Promise<string[]> {
71
+ let body: unknown;
72
+ try {
73
+ body = await req.json();
74
+ } catch {
75
+ return [];
76
+ }
77
+ if (body === null || typeof body !== 'object') return [];
78
+ const b = body as { readonly projectId?: unknown; readonly scope?: unknown };
79
+ const named: string[] = [];
80
+ if (typeof b.projectId === 'string') named.push(b.projectId);
81
+ const scope = b.scope as { readonly projectId?: unknown } | null | undefined;
82
+ if (scope !== null && typeof scope === 'object' && typeof scope.projectId === 'string') {
83
+ named.push(scope.projectId);
84
+ }
85
+ return named;
86
+ }
@@ -28,6 +28,7 @@ export function principalMiddleware(): MiddlewareHandler<AppEnv> {
28
28
  const userId = c.get('userId');
29
29
  const tokenId = c.get('tokenId');
30
30
  const sessionId = c.get('sessionId');
31
+ const keyServiceAccount = c.get('serviceAccountId');
31
32
 
32
33
  if (tenantId === undefined) {
33
34
  // Bearer middleware didn't populate — either the route is
@@ -37,12 +38,15 @@ export function principalMiddleware(): MiddlewareHandler<AppEnv> {
37
38
  return next();
38
39
  }
39
40
 
40
- // A user when the token names one; otherwise a service account: a
41
- // durable API key's id, or else the session id (which uniquely
42
- // identifies the token record). With none of them, leave `principal`
43
- // unset; `authorize()` treats that as "no principal" and 403s cleanly.
41
+ // A user when the token names one; otherwise a service account: the
42
+ // one an API key acts for, else the key's own id (a store built before
43
+ // principals), else the session id (which uniquely identifies the
44
+ // token record). With none of them, leave `principal` unset;
45
+ // `authorize()` treats that as "no principal" and 403s cleanly.
44
46
  const serviceAccountId =
45
- (tokenId as unknown as string | undefined) ?? (sessionId as unknown as string | undefined);
47
+ keyServiceAccount ??
48
+ (tokenId as unknown as string | undefined) ??
49
+ (sessionId as unknown as string | undefined);
46
50
  if (userId === undefined && serviceAccountId === undefined) {
47
51
  return next();
48
52
  }
@@ -33,6 +33,9 @@ export const PROJECT_REF_ROUTES: readonly string[] = [
33
33
  '/eval-suites/:suiteId/versions/from-judgments',
34
34
  '/eval-suites/:suiteId/runs',
35
35
  '/blocks',
36
+ '/schedules',
37
+ '/service-accounts/:serviceAccountId/grant',
38
+ '/service-accounts/:serviceAccountId/ungrant',
36
39
  ];
37
40
 
38
41
  /**
@@ -49,6 +49,8 @@ const DEFAULT_SERVERS: readonly OpenApiServer[] = [
49
49
  const TAG_DESCRIPTIONS: Readonly<Record<string, string>> = {
50
50
  system: 'Health + spec discovery.',
51
51
  runs: 'Run lifecycle (start, list, get, cancel, resume, journal, stream).',
52
+ 'export-signing-keys':
53
+ 'The public keys this deployment signs its exports with (audit bundles, provenance, compliance evidence): what a verifier pins.',
52
54
  'signing-keys':
53
55
  'The deployment trust list: the public keys whose signatures `POST /v1/deployments` accepts. Revoked keys stay readable for audit.',
54
56
  tokens:
@@ -69,7 +71,7 @@ const TAG_DESCRIPTIONS: Readonly<Record<string, string>> = {
69
71
  proposals:
70
72
  'Supervisor fix-proposal lifecycle (draft, dry-run, submit-review, apply, rollback, withdraw — caller-plugged binding wraps the supervisor).',
71
73
  provenance:
72
- 'Causal DAG readback + signed exports (list, get, export — signing is caller-plugged via `SigningKeyBinding`).',
74
+ "Causal DAG readback + signed exports (list, get, export), signed with the deployment's export key.",
73
75
  artifacts:
74
76
  'Blob storage (list metadata, multipart upload, streaming download, HEAD, delete). Caller-plugged via `BlobStorageBinding` (from `@kindgi/blob-binding`).',
75
77
  capabilities:
@@ -100,7 +102,7 @@ const TAG_DESCRIPTIONS: Readonly<Record<string, string>> = {
100
102
  deployments:
101
103
  'Signed pack deployments (register, list, get). The single wire surface that lands a signed OCI image + index.json into the platform. Register runs a six-step atomic transaction: Ed25519 signature verify over the canonical `{imageDigest, artifactVersion, indexHash, tenantId, publishedAt}` envelope, tenant-scoped trust-list check via `SigningKeyRegistryBinding`, image pullability + `/app/index.json` sha256 match via `ImageRegistryBinding`, per-primitive manifest validation, registry upserts (tools + guardrails + agents + flows), append-only deployment ledger row via `DeploymentBinding`. All-or-nothing rollback on any failure; digest-based idempotency (redeploying the same image returns the existing record). Records are immutable — rollback = re-register the previous digest.',
102
104
  compliance:
103
- 'Compliance-evidence readback + signed exports (list, get, export). Reads are a classification lens over `AuditEventBinding`; only classifier-marked `exportable` kinds appear on the wire. Signed export is caller-plugged via `SigningKeyBinding` — deployments without a signing key get `404 signing-not-configured`. Bundle envelope matches `provenance.export` + audit-bundle byte-for-byte so verifiers reuse one `verifyEd25519` wrapper across all three surfaces. Redaction happens when evidence is generated, not at the export boundary — records are stored already redacted.',
105
+ 'Compliance-evidence readback + signed exports (list, get, export). Reads are a classification lens over `AuditEventBinding`; only classifier-marked `exportable` kinds appear on the wire. A signed export is signed with the export signing key of the deployment (`exportSigning`); a deployment without one answers `404 signing-not-configured`. It is the same envelope as the audit-bundle and provenance exports, so one verifier reads all three. Redaction happens when evidence is generated, not at the export boundary — records are stored already redacted.',
104
106
  audit:
105
107
  'PDP decision audit stream (list). Every route-level authz `check()` emits one `AuditEvent` (kind `authz-decision`) via the `AuditEventBinding`; this route pages through them, tenant-scoped, with `?actorSubject=` / `?onBehalfOf=` / `?action=` / `?resource=` / `?outcome=` / `?runId=` / `?from=` / `?to=` filters. Admin@tenant only. This view is a filtered projection of the tenant audit events.',
106
108
  orgs: 'Multi-tenant hierarchy — Org CRUD (list, create, get, patch, delete). Optional structural subdivision within a tenant; small tenants ignore Orgs entirely. Caller-plugged via `OrgBinding` from `@kindgi/platform`.',
@@ -111,6 +113,8 @@ const TAG_DESCRIPTIONS: Readonly<Record<string, string>> = {
111
113
  tenant:
112
114
  'Sovereignty boundary readback + tenant-scoped config (get tenant; list + upsert entries routed to the env / secrets bindings). There is no `/v1/tenants` collection — the caller\'s tenant is implicit from the bearer token. `PATCH /config` routes writes to `SecretBinding` when `sensitive: true` OR `kind: "secret"`; else to `EnvBinding`. `GET /config` merges both bindings at tenant scope; secret values are ALWAYS redacted here. The `/config` sub-routes mount when at least one of `envBinding` / `secretsBinding` is wired. Prefer `/v1/env/*` + `/v1/secrets/*` for new callers.',
113
115
  env: 'Non-sensitive per-env values (`/v1/env/*`). Every route requires `envName` + `scopeKind` (+ `scopeId` for org / project); `value` is present on every read path (env is non-sensitive by definition). Writes require the `env:write` capability. Caller-plugged via `EnvBinding` (`@kindgi/env-inmemory` is an in-memory implementation for development and tests).',
116
+ schedules:
117
+ "Run an agent or a flow on a schedule: a cron expression in a timezone. Each occurrence is one fire, which starts one run as the schedule's owner (re-checked at every fire), through the same path as `POST /v1/runs`; a run a schedule started names it (`Run.trigger`). After a gap, `catchUp` runs once for the latest missed occurrence (or skips them), never once per missed one; `overlap` skips an occurrence while the previous run is still going. `GET …/fires` is the schedule's history; `run-now` fires it outside the schedule. Mounted when the deployment fires schedules.",
114
118
  'webhook-endpoints':
115
119
  'Outbound webhooks: endpoints the platform sends signed events to (`run.finished` when a top-level run completes, fails or is cancelled), their delivery log, redelivery and a test event. Signed in the Standard Webhooks format with a secret the endpoint references by name (`secretRef`); delivered at least once, so receivers deduplicate on `webhook-id`. Event bodies are under `webhooks`. Not to be confused with `/v1/webhooks`, which are inbound triggers. Caller-plugged via `WebhookEndpointBinding`.',
116
120
  secrets:
@@ -212,6 +216,11 @@ function buildOutboundWebhooks(): Record<string, unknown> {
212
216
  'A top-level run completed, failed or was cancelled',
213
217
  'RunFinishedEvent',
214
218
  ),
219
+ 'improvement-pass.finished': event(
220
+ 'improvement-pass.finished',
221
+ 'An improvement pass completed, failed or was cancelled',
222
+ 'ImprovementPassFinishedEvent',
223
+ ),
215
224
  'webhook.test': event(
216
225
  'webhook.test',
217
226
  'A test event sent on request (`POST /v1/webhook-endpoints/{endpointId}/test`)',