@kindgi/api 0.1.4 → 0.1.5-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (507) 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/derive-agent-version.d.ts +7 -0
  21. package/dist/derive-agent-version.d.ts.map +1 -1
  22. package/dist/derive-agent-version.js +4 -0
  23. package/dist/derive-agent-version.js.map +1 -1
  24. package/dist/errors.d.ts.map +1 -1
  25. package/dist/errors.js +73 -7
  26. package/dist/errors.js.map +1 -1
  27. package/dist/eval-case-binding.d.ts +16 -4
  28. package/dist/eval-case-binding.d.ts.map +1 -1
  29. package/dist/eval-run-binding.d.ts +31 -0
  30. package/dist/eval-run-binding.d.ts.map +1 -1
  31. package/dist/eval-run-dispatcher.d.ts +7 -0
  32. package/dist/eval-run-dispatcher.d.ts.map +1 -1
  33. package/dist/eval-run-dispatcher.js.map +1 -1
  34. package/dist/eval-sample.d.ts +11 -0
  35. package/dist/eval-sample.d.ts.map +1 -0
  36. package/dist/eval-sample.js +34 -0
  37. package/dist/eval-sample.js.map +1 -0
  38. package/dist/eval-suite-binding.d.ts +13 -0
  39. package/dist/eval-suite-binding.d.ts.map +1 -1
  40. package/dist/eval-suite-binding.js.map +1 -1
  41. package/dist/flow-binding.d.ts +18 -0
  42. package/dist/flow-binding.d.ts.map +1 -1
  43. package/dist/flow-pins.d.ts.map +1 -1
  44. package/dist/flow-pins.js +18 -2
  45. package/dist/flow-pins.js.map +1 -1
  46. package/dist/gate.d.ts.map +1 -1
  47. package/dist/gate.js +25 -0
  48. package/dist/gate.js.map +1 -1
  49. package/dist/handler-binding.d.ts +32 -0
  50. package/dist/handler-binding.d.ts.map +1 -1
  51. package/dist/hitl-binding.d.ts +23 -0
  52. package/dist/hitl-binding.d.ts.map +1 -1
  53. package/dist/identity-directory-binding.d.ts +82 -4
  54. package/dist/identity-directory-binding.d.ts.map +1 -1
  55. package/dist/identity-provider-binding.d.ts +197 -17
  56. package/dist/identity-provider-binding.d.ts.map +1 -1
  57. package/dist/improvement-pass-binding.d.ts +196 -0
  58. package/dist/improvement-pass-binding.d.ts.map +1 -0
  59. package/dist/improvement-pass-binding.js +9 -0
  60. package/dist/improvement-pass-binding.js.map +1 -0
  61. package/dist/index.d.ts +35 -17
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +11 -3
  64. package/dist/index.js.map +1 -1
  65. package/dist/judged-dispatcher.d.ts +25 -4
  66. package/dist/judged-dispatcher.d.ts.map +1 -1
  67. package/dist/judged-dispatcher.js +43 -6
  68. package/dist/judged-dispatcher.js.map +1 -1
  69. package/dist/judgment-binding.d.ts +25 -1
  70. package/dist/judgment-binding.d.ts.map +1 -1
  71. package/dist/mcp-endpoint-binding.d.ts +7 -0
  72. package/dist/mcp-endpoint-binding.d.ts.map +1 -1
  73. package/dist/memory-binding.d.ts +124 -17
  74. package/dist/memory-binding.d.ts.map +1 -1
  75. package/dist/memory-erasure-binding.d.ts +145 -0
  76. package/dist/memory-erasure-binding.d.ts.map +1 -0
  77. package/dist/memory-erasure-binding.js +4 -0
  78. package/dist/memory-erasure-binding.js.map +1 -0
  79. package/dist/middleware/auth.d.ts +66 -13
  80. package/dist/middleware/auth.d.ts.map +1 -1
  81. package/dist/middleware/auth.js +150 -50
  82. package/dist/middleware/auth.js.map +1 -1
  83. package/dist/middleware/authorize.d.ts +6 -0
  84. package/dist/middleware/authorize.d.ts.map +1 -1
  85. package/dist/middleware/authorize.js +113 -4
  86. package/dist/middleware/authorize.js.map +1 -1
  87. package/dist/middleware/error-mapper.d.ts +5 -0
  88. package/dist/middleware/error-mapper.d.ts.map +1 -1
  89. package/dist/middleware/error-mapper.js +14 -1
  90. package/dist/middleware/error-mapper.js.map +1 -1
  91. package/dist/middleware/idempotency.d.ts +70 -5
  92. package/dist/middleware/idempotency.d.ts.map +1 -1
  93. package/dist/middleware/idempotency.js +174 -41
  94. package/dist/middleware/idempotency.js.map +1 -1
  95. package/dist/middleware/key-project.d.ts +17 -0
  96. package/dist/middleware/key-project.d.ts.map +1 -0
  97. package/dist/middleware/key-project.js +75 -0
  98. package/dist/middleware/key-project.js.map +1 -0
  99. package/dist/middleware/principal.d.ts.map +1 -1
  100. package/dist/middleware/principal.js +9 -5
  101. package/dist/middleware/principal.js.map +1 -1
  102. package/dist/middleware/project-ref.d.ts.map +1 -1
  103. package/dist/middleware/project-ref.js +3 -0
  104. package/dist/middleware/project-ref.js.map +1 -1
  105. package/dist/openapi/generate.d.ts.map +1 -1
  106. package/dist/openapi/generate.js +5 -2
  107. package/dist/openapi/generate.js.map +1 -1
  108. package/dist/openapi/operations.d.ts.map +1 -1
  109. package/dist/openapi/operations.js +918 -139
  110. package/dist/openapi/operations.js.map +1 -1
  111. package/dist/openapi/schemas.d.ts +90 -13
  112. package/dist/openapi/schemas.d.ts.map +1 -1
  113. package/dist/openapi/schemas.js +2081 -359
  114. package/dist/openapi/schemas.js.map +1 -1
  115. package/dist/person-grants-binding.d.ts +77 -0
  116. package/dist/person-grants-binding.d.ts.map +1 -0
  117. package/dist/person-grants-binding.js +4 -0
  118. package/dist/person-grants-binding.js.map +1 -0
  119. package/dist/proposal-service.d.ts +121 -0
  120. package/dist/proposal-service.d.ts.map +1 -0
  121. package/dist/proposal-service.js +447 -0
  122. package/dist/proposal-service.js.map +1 -0
  123. package/dist/proposal-status.d.ts +60 -0
  124. package/dist/proposal-status.d.ts.map +1 -0
  125. package/dist/proposal-status.js +109 -0
  126. package/dist/proposal-status.js.map +1 -0
  127. package/dist/provider-binding.d.ts +10 -0
  128. package/dist/provider-binding.d.ts.map +1 -1
  129. package/dist/publish-refused.d.ts +18 -3
  130. package/dist/publish-refused.d.ts.map +1 -1
  131. package/dist/publish-refused.js +18 -3
  132. package/dist/publish-refused.js.map +1 -1
  133. package/dist/routes/adapters.d.ts +2 -1
  134. package/dist/routes/adapters.d.ts.map +1 -1
  135. package/dist/routes/adapters.js +3 -1
  136. package/dist/routes/adapters.js.map +1 -1
  137. package/dist/routes/agent-releases.d.ts +55 -3
  138. package/dist/routes/agent-releases.d.ts.map +1 -1
  139. package/dist/routes/agent-releases.js +85 -55
  140. package/dist/routes/agent-releases.js.map +1 -1
  141. package/dist/routes/agents.d.ts +12 -1
  142. package/dist/routes/agents.d.ts.map +1 -1
  143. package/dist/routes/agents.js +100 -2
  144. package/dist/routes/agents.js.map +1 -1
  145. package/dist/routes/approvals.d.ts +15 -3
  146. package/dist/routes/approvals.d.ts.map +1 -1
  147. package/dist/routes/approvals.js +133 -122
  148. package/dist/routes/approvals.js.map +1 -1
  149. package/dist/routes/artifacts.d.ts +25 -3
  150. package/dist/routes/artifacts.d.ts.map +1 -1
  151. package/dist/routes/artifacts.js +116 -7
  152. package/dist/routes/artifacts.js.map +1 -1
  153. package/dist/routes/auth.d.ts +25 -1
  154. package/dist/routes/auth.d.ts.map +1 -1
  155. package/dist/routes/auth.js +655 -281
  156. package/dist/routes/auth.js.map +1 -1
  157. package/dist/routes/blocks.d.ts.map +1 -1
  158. package/dist/routes/blocks.js +2 -42
  159. package/dist/routes/blocks.js.map +1 -1
  160. package/dist/routes/capabilities.d.ts +2 -1
  161. package/dist/routes/capabilities.d.ts.map +1 -1
  162. package/dist/routes/capabilities.js +6 -1
  163. package/dist/routes/capabilities.js.map +1 -1
  164. package/dist/routes/compliance.d.ts +9 -10
  165. package/dist/routes/compliance.d.ts.map +1 -1
  166. package/dist/routes/compliance.js +42 -61
  167. package/dist/routes/compliance.js.map +1 -1
  168. package/dist/routes/conversations.d.ts +8 -1
  169. package/dist/routes/conversations.d.ts.map +1 -1
  170. package/dist/routes/conversations.js +87 -8
  171. package/dist/routes/conversations.js.map +1 -1
  172. package/dist/routes/cost.d.ts +7 -1
  173. package/dist/routes/cost.d.ts.map +1 -1
  174. package/dist/routes/cost.js +22 -3
  175. package/dist/routes/cost.js.map +1 -1
  176. package/dist/routes/denied.d.ts +11 -0
  177. package/dist/routes/denied.d.ts.map +1 -0
  178. package/dist/routes/denied.js +17 -0
  179. package/dist/routes/denied.js.map +1 -0
  180. package/dist/routes/deployments.d.ts +2 -1
  181. package/dist/routes/deployments.d.ts.map +1 -1
  182. package/dist/routes/deployments.js +44 -12
  183. package/dist/routes/deployments.js.map +1 -1
  184. package/dist/routes/eval-comparison.d.ts.map +1 -1
  185. package/dist/routes/eval-comparison.js +69 -1
  186. package/dist/routes/eval-comparison.js.map +1 -1
  187. package/dist/routes/eval-overrides.d.ts +18 -0
  188. package/dist/routes/eval-overrides.d.ts.map +1 -0
  189. package/dist/routes/eval-overrides.js +81 -0
  190. package/dist/routes/eval-overrides.js.map +1 -0
  191. package/dist/routes/eval-runs.d.ts +10 -1
  192. package/dist/routes/eval-runs.d.ts.map +1 -1
  193. package/dist/routes/eval-runs.js +63 -7
  194. package/dist/routes/eval-runs.js.map +1 -1
  195. package/dist/routes/eval-suites.d.ts.map +1 -1
  196. package/dist/routes/eval-suites.js +20 -1
  197. package/dist/routes/eval-suites.js.map +1 -1
  198. package/dist/routes/event-triggers.d.ts +5 -1
  199. package/dist/routes/event-triggers.d.ts.map +1 -1
  200. package/dist/routes/event-triggers.js +26 -3
  201. package/dist/routes/event-triggers.js.map +1 -1
  202. package/dist/routes/export-signing-keys.d.ts +12 -0
  203. package/dist/routes/export-signing-keys.d.ts.map +1 -0
  204. package/dist/routes/export-signing-keys.js +24 -0
  205. package/dist/routes/export-signing-keys.js.map +1 -0
  206. package/dist/routes/flows.d.ts.map +1 -1
  207. package/dist/routes/flows.js +9 -1
  208. package/dist/routes/flows.js.map +1 -1
  209. package/dist/routes/gate-policies.d.ts +4 -1
  210. package/dist/routes/gate-policies.d.ts.map +1 -1
  211. package/dist/routes/gate-policies.js +29 -5
  212. package/dist/routes/gate-policies.js.map +1 -1
  213. package/dist/routes/guardrails.d.ts +16 -14
  214. package/dist/routes/guardrails.d.ts.map +1 -1
  215. package/dist/routes/guardrails.js +39 -3
  216. package/dist/routes/guardrails.js.map +1 -1
  217. package/dist/routes/identity.d.ts +20 -4
  218. package/dist/routes/identity.d.ts.map +1 -1
  219. package/dist/routes/identity.js +246 -6
  220. package/dist/routes/identity.js.map +1 -1
  221. package/dist/routes/improvement-passes.d.ts +52 -0
  222. package/dist/routes/improvement-passes.d.ts.map +1 -0
  223. package/dist/routes/improvement-passes.js +449 -0
  224. package/dist/routes/improvement-passes.js.map +1 -0
  225. package/dist/routes/judged-suites.d.ts +50 -0
  226. package/dist/routes/judged-suites.d.ts.map +1 -1
  227. package/dist/routes/judged-suites.js +66 -27
  228. package/dist/routes/judged-suites.js.map +1 -1
  229. package/dist/routes/judgment-context.d.ts +18 -2
  230. package/dist/routes/judgment-context.d.ts.map +1 -1
  231. package/dist/routes/judgment-context.js +63 -5
  232. package/dist/routes/judgment-context.js.map +1 -1
  233. package/dist/routes/judgment-flow-context.d.ts.map +1 -1
  234. package/dist/routes/judgment-flow-context.js +14 -2
  235. package/dist/routes/judgment-flow-context.js.map +1 -1
  236. package/dist/routes/judgments.d.ts.map +1 -1
  237. package/dist/routes/judgments.js +8 -1
  238. package/dist/routes/judgments.js.map +1 -1
  239. package/dist/routes/mcp.d.ts.map +1 -1
  240. package/dist/routes/mcp.js +26 -1
  241. package/dist/routes/mcp.js.map +1 -1
  242. package/dist/routes/memory-access.d.ts +43 -0
  243. package/dist/routes/memory-access.d.ts.map +1 -0
  244. package/dist/routes/memory-access.js +129 -0
  245. package/dist/routes/memory-access.js.map +1 -0
  246. package/dist/routes/memory-erasures.d.ts +25 -0
  247. package/dist/routes/memory-erasures.d.ts.map +1 -0
  248. package/dist/routes/memory-erasures.js +214 -0
  249. package/dist/routes/memory-erasures.js.map +1 -0
  250. package/dist/routes/memory-parse.d.ts +38 -0
  251. package/dist/routes/memory-parse.d.ts.map +1 -0
  252. package/dist/routes/memory-parse.js +199 -0
  253. package/dist/routes/memory-parse.js.map +1 -0
  254. package/dist/routes/memory.d.ts +9 -4
  255. package/dist/routes/memory.d.ts.map +1 -1
  256. package/dist/routes/memory.js +226 -234
  257. package/dist/routes/memory.js.map +1 -1
  258. package/dist/routes/observations.d.ts +4 -1
  259. package/dist/routes/observations.d.ts.map +1 -1
  260. package/dist/routes/observations.js +8 -2
  261. package/dist/routes/observations.js.map +1 -1
  262. package/dist/routes/pagination.d.ts +7 -0
  263. package/dist/routes/pagination.d.ts.map +1 -1
  264. package/dist/routes/pagination.js +10 -0
  265. package/dist/routes/pagination.js.map +1 -1
  266. package/dist/routes/policies.d.ts +2 -1
  267. package/dist/routes/policies.d.ts.map +1 -1
  268. package/dist/routes/policies.js +3 -1
  269. package/dist/routes/policies.js.map +1 -1
  270. package/dist/routes/project-mismatch.d.ts +22 -0
  271. package/dist/routes/project-mismatch.d.ts.map +1 -0
  272. package/dist/routes/project-mismatch.js +34 -0
  273. package/dist/routes/project-mismatch.js.map +1 -0
  274. package/dist/routes/projects.d.ts +15 -4
  275. package/dist/routes/projects.d.ts.map +1 -1
  276. package/dist/routes/projects.js +100 -9
  277. package/dist/routes/projects.js.map +1 -1
  278. package/dist/routes/proposals.d.ts +25 -17
  279. package/dist/routes/proposals.d.ts.map +1 -1
  280. package/dist/routes/proposals.js +340 -507
  281. package/dist/routes/proposals.js.map +1 -1
  282. package/dist/routes/provenance.d.ts +17 -3
  283. package/dist/routes/provenance.d.ts.map +1 -1
  284. package/dist/routes/provenance.js +79 -103
  285. package/dist/routes/provenance.js.map +1 -1
  286. package/dist/routes/providers.d.ts +17 -1
  287. package/dist/routes/providers.d.ts.map +1 -1
  288. package/dist/routes/providers.js +110 -3
  289. package/dist/routes/providers.js.map +1 -1
  290. package/dist/routes/public-run-tokens.d.ts.map +1 -1
  291. package/dist/routes/public-run-tokens.js +3 -0
  292. package/dist/routes/public-run-tokens.js.map +1 -1
  293. package/dist/routes/runs.d.ts +10 -0
  294. package/dist/routes/runs.d.ts.map +1 -1
  295. package/dist/routes/runs.js +92 -42
  296. package/dist/routes/runs.js.map +1 -1
  297. package/dist/routes/schedules.d.ts +21 -13
  298. package/dist/routes/schedules.d.ts.map +1 -1
  299. package/dist/routes/schedules.js +416 -55
  300. package/dist/routes/schedules.js.map +1 -1
  301. package/dist/routes/segments.d.ts +2 -0
  302. package/dist/routes/segments.d.ts.map +1 -1
  303. package/dist/routes/segments.js +5 -0
  304. package/dist/routes/segments.js.map +1 -1
  305. package/dist/routes/service-accounts.d.ts +17 -0
  306. package/dist/routes/service-accounts.d.ts.map +1 -0
  307. package/dist/routes/service-accounts.js +212 -0
  308. package/dist/routes/service-accounts.js.map +1 -0
  309. package/dist/routes/sign-in-options.d.ts +50 -0
  310. package/dist/routes/sign-in-options.d.ts.map +1 -0
  311. package/dist/routes/sign-in-options.js +98 -0
  312. package/dist/routes/sign-in-options.js.map +1 -0
  313. package/dist/routes/signing-keys.d.ts +2 -1
  314. package/dist/routes/signing-keys.d.ts.map +1 -1
  315. package/dist/routes/signing-keys.js +3 -1
  316. package/dist/routes/signing-keys.js.map +1 -1
  317. package/dist/routes/tenant-access.d.ts +13 -0
  318. package/dist/routes/tenant-access.d.ts.map +1 -0
  319. package/dist/routes/tenant-access.js +26 -0
  320. package/dist/routes/tenant-access.js.map +1 -0
  321. package/dist/routes/tenant.d.ts.map +1 -1
  322. package/dist/routes/tenant.js +4 -0
  323. package/dist/routes/tenant.js.map +1 -1
  324. package/dist/routes/token-sign-in.d.ts +36 -0
  325. package/dist/routes/token-sign-in.d.ts.map +1 -0
  326. package/dist/routes/token-sign-in.js +101 -0
  327. package/dist/routes/token-sign-in.js.map +1 -0
  328. package/dist/routes/tokens.d.ts +15 -5
  329. package/dist/routes/tokens.d.ts.map +1 -1
  330. package/dist/routes/tokens.js +162 -43
  331. package/dist/routes/tokens.js.map +1 -1
  332. package/dist/routes/tools.d.ts.map +1 -1
  333. package/dist/routes/tools.js +9 -1
  334. package/dist/routes/tools.js.map +1 -1
  335. package/dist/routes/trigger-access.d.ts +29 -0
  336. package/dist/routes/trigger-access.d.ts.map +1 -0
  337. package/dist/routes/trigger-access.js +33 -0
  338. package/dist/routes/trigger-access.js.map +1 -0
  339. package/dist/routes/webhook-endpoints.d.ts +2 -1
  340. package/dist/routes/webhook-endpoints.d.ts.map +1 -1
  341. package/dist/routes/webhook-endpoints.js +11 -3
  342. package/dist/routes/webhook-endpoints.js.map +1 -1
  343. package/dist/routes/webhooks.d.ts +5 -1
  344. package/dist/routes/webhooks.d.ts.map +1 -1
  345. package/dist/routes/webhooks.js +25 -2
  346. package/dist/routes/webhooks.js.map +1 -1
  347. package/dist/run-failure.d.ts +21 -0
  348. package/dist/run-failure.d.ts.map +1 -0
  349. package/dist/run-failure.js +29 -0
  350. package/dist/run-failure.js.map +1 -0
  351. package/dist/service-account-binding.d.ts +108 -0
  352. package/dist/service-account-binding.d.ts.map +1 -0
  353. package/dist/service-account-binding.js +4 -0
  354. package/dist/service-account-binding.js.map +1 -0
  355. package/dist/session-store-binding.d.ts +61 -13
  356. package/dist/session-store-binding.d.ts.map +1 -1
  357. package/dist/signed-export.d.ts +89 -0
  358. package/dist/signed-export.d.ts.map +1 -0
  359. package/dist/signed-export.js +149 -0
  360. package/dist/signed-export.js.map +1 -0
  361. package/dist/supervisor-binding.d.ts +186 -413
  362. package/dist/supervisor-binding.d.ts.map +1 -1
  363. package/dist/testing/app-bindings.d.ts +33 -0
  364. package/dist/testing/app-bindings.d.ts.map +1 -0
  365. package/dist/testing/app-bindings.js +112 -0
  366. package/dist/testing/app-bindings.js.map +1 -0
  367. package/dist/testing/index.d.ts +11 -0
  368. package/dist/testing/index.d.ts.map +1 -0
  369. package/dist/testing/index.js +13 -0
  370. package/dist/testing/index.js.map +1 -0
  371. package/dist/testing/stub-binding.d.ts +34 -0
  372. package/dist/testing/stub-binding.d.ts.map +1 -0
  373. package/dist/testing/stub-binding.js +42 -0
  374. package/dist/testing/stub-binding.js.map +1 -0
  375. package/dist/testing/trigger-registry.d.ts +21 -0
  376. package/dist/testing/trigger-registry.d.ts.map +1 -0
  377. package/dist/testing/trigger-registry.js +206 -0
  378. package/dist/testing/trigger-registry.js.map +1 -0
  379. package/dist/token-admin.d.ts +46 -8
  380. package/dist/token-admin.d.ts.map +1 -1
  381. package/dist/token-admin.js.map +1 -1
  382. package/dist/tool-binding.d.ts +13 -0
  383. package/dist/tool-binding.d.ts.map +1 -1
  384. package/dist/trigger-binding.d.ts +2 -2
  385. package/dist/trigger-binding.d.ts.map +1 -1
  386. package/dist/trigger-binding.js +1 -1
  387. package/dist/trigger-binding.js.map +1 -1
  388. package/dist/types.d.ts +16 -0
  389. package/dist/types.d.ts.map +1 -1
  390. package/dist/webhook-endpoint-binding.d.ts +19 -3
  391. package/dist/webhook-endpoint-binding.d.ts.map +1 -1
  392. package/dist/webhook-endpoint-binding.js +1 -1
  393. package/dist/webhook-endpoint-binding.js.map +1 -1
  394. package/openapi.json +15385 -8788
  395. package/package.json +26 -22
  396. package/src/agent-binding.ts +14 -0
  397. package/src/agent-pins.ts +18 -2
  398. package/src/app.ts +366 -80
  399. package/src/block-publish.ts +181 -0
  400. package/src/caller.ts +62 -0
  401. package/src/capability-binding.ts +12 -0
  402. package/src/derive-agent-version.ts +9 -1
  403. package/src/errors.ts +73 -7
  404. package/src/eval-case-binding.ts +17 -1
  405. package/src/eval-run-binding.ts +31 -0
  406. package/src/eval-run-dispatcher.ts +7 -0
  407. package/src/eval-sample.ts +42 -0
  408. package/src/eval-suite-binding.ts +14 -0
  409. package/src/flow-binding.ts +19 -0
  410. package/src/flow-pins.ts +18 -2
  411. package/src/gate.ts +26 -0
  412. package/src/handler-binding.ts +36 -1
  413. package/src/hitl-binding.ts +24 -0
  414. package/src/identity-directory-binding.ts +85 -4
  415. package/src/identity-provider-binding.ts +216 -21
  416. package/src/improvement-pass-binding.ts +193 -0
  417. package/src/index.ts +153 -31
  418. package/src/judged-dispatcher.ts +78 -11
  419. package/src/judgment-binding.ts +25 -1
  420. package/src/mcp-endpoint-binding.ts +7 -0
  421. package/src/memory-binding.ts +121 -17
  422. package/src/memory-erasure-binding.ts +156 -0
  423. package/src/middleware/auth.ts +228 -62
  424. package/src/middleware/authorize.ts +139 -3
  425. package/src/middleware/error-mapper.ts +15 -1
  426. package/src/middleware/idempotency.ts +230 -43
  427. package/src/middleware/key-project.ts +86 -0
  428. package/src/middleware/principal.ts +9 -5
  429. package/src/middleware/project-ref.ts +3 -0
  430. package/src/openapi/generate.ts +11 -2
  431. package/src/openapi/operations.ts +1059 -142
  432. package/src/openapi/schemas.ts +2387 -469
  433. package/src/person-grants-binding.ts +76 -0
  434. package/src/proposal-service.ts +640 -0
  435. package/src/proposal-status.ts +176 -0
  436. package/src/provider-binding.ts +10 -0
  437. package/src/publish-refused.ts +38 -9
  438. package/src/routes/adapters.ts +4 -0
  439. package/src/routes/agent-releases.ts +121 -69
  440. package/src/routes/agents.ts +128 -1
  441. package/src/routes/approvals.ts +164 -154
  442. package/src/routes/artifacts.ts +181 -11
  443. package/src/routes/auth.ts +804 -336
  444. package/src/routes/blocks.ts +2 -53
  445. package/src/routes/capabilities.ts +10 -1
  446. package/src/routes/compliance.ts +60 -99
  447. package/src/routes/conversations.ts +101 -7
  448. package/src/routes/cost.ts +32 -3
  449. package/src/routes/denied.ts +28 -0
  450. package/src/routes/deployments.ts +62 -16
  451. package/src/routes/eval-comparison.ts +81 -2
  452. package/src/routes/eval-overrides.ts +108 -0
  453. package/src/routes/eval-runs.ts +86 -6
  454. package/src/routes/eval-suites.ts +25 -1
  455. package/src/routes/event-triggers.ts +27 -3
  456. package/src/routes/export-signing-keys.ts +31 -0
  457. package/src/routes/flows.ts +12 -1
  458. package/src/routes/gate-policies.ts +37 -6
  459. package/src/routes/guardrails.ts +61 -2
  460. package/src/routes/identity.ts +328 -10
  461. package/src/routes/improvement-passes.ts +541 -0
  462. package/src/routes/judged-suites.ts +113 -43
  463. package/src/routes/judgment-context.ts +76 -5
  464. package/src/routes/judgment-flow-context.ts +16 -2
  465. package/src/routes/judgments.ts +17 -3
  466. package/src/routes/mcp.ts +29 -1
  467. package/src/routes/memory-access.ts +179 -0
  468. package/src/routes/memory-erasures.ts +240 -0
  469. package/src/routes/memory-parse.ts +250 -0
  470. package/src/routes/memory.ts +244 -286
  471. package/src/routes/observations.ts +15 -2
  472. package/src/routes/pagination.ts +12 -0
  473. package/src/routes/policies.ts +7 -1
  474. package/src/routes/project-mismatch.ts +56 -0
  475. package/src/routes/projects.ts +107 -13
  476. package/src/routes/proposals.ts +370 -611
  477. package/src/routes/provenance.ts +99 -133
  478. package/src/routes/providers.ts +142 -3
  479. package/src/routes/public-run-tokens.ts +3 -0
  480. package/src/routes/runs.ts +119 -39
  481. package/src/routes/schedules.ts +472 -60
  482. package/src/routes/segments.ts +11 -0
  483. package/src/routes/service-accounts.ts +262 -0
  484. package/src/routes/sign-in-options.ts +168 -0
  485. package/src/routes/signing-keys.ts +7 -1
  486. package/src/routes/tenant-access.ts +37 -0
  487. package/src/routes/tenant.ts +6 -0
  488. package/src/routes/token-sign-in.ts +161 -0
  489. package/src/routes/tokens.ts +172 -46
  490. package/src/routes/tools.ts +12 -1
  491. package/src/routes/trigger-access.ts +72 -0
  492. package/src/routes/webhook-endpoints.ts +15 -3
  493. package/src/routes/webhooks.ts +26 -2
  494. package/src/run-failure.ts +42 -0
  495. package/src/service-account-binding.ts +113 -0
  496. package/src/session-store-binding.ts +65 -13
  497. package/src/signed-export.ts +250 -0
  498. package/src/supervisor-binding.ts +169 -409
  499. package/src/testing/app-bindings.ts +142 -0
  500. package/src/testing/index.ts +21 -0
  501. package/src/testing/stub-binding.ts +53 -0
  502. package/src/testing/trigger-registry.ts +259 -0
  503. package/src/token-admin.ts +40 -8
  504. package/src/tool-binding.ts +14 -0
  505. package/src/trigger-binding.ts +8 -1
  506. package/src/types.ts +16 -0
  507. package/src/webhook-endpoint-binding.ts +18 -3
@@ -4,31 +4,44 @@
4
4
  import type { Context, MiddlewareHandler } from 'hono';
5
5
  import { Hono } from 'hono';
6
6
 
7
- import { ref } from '@kindgi/authz';
8
7
  import type { ApiTokenId, ProjectId, TenantId } from '@kindgi/types';
9
8
 
9
+ import { callerPrincipal, callerRef, isTenantAdmin, principalToWire } from '../caller.js';
10
+
10
11
  import { statusFor, toWireError } from '../errors.js';
11
12
  import type { Authorizer } from '../middleware/authorize.js';
13
+ import { withholdFromReplay } from '../middleware/idempotency.js';
12
14
  import {
13
15
  API_TOKEN_ROLES,
14
16
  type ApiTokenRecord,
15
17
  type ApiTokenRole,
16
18
  type TokenAdmin,
19
+ type TokenPrincipal,
17
20
  } from '../token-admin.js';
18
21
  import type { AppEnv } from '../types.js';
19
22
  import { clampLimit, decodeCursor, encodeCursor } from './pagination.js';
20
23
 
21
24
  /**
22
- * API keys: mint, list, read, revoke. Every route is tenant-admin only:
23
- * `admin` on the tenant when authorization is on, otherwise the
24
- * `tenant-admin` scope on the caller's token.
25
+ * API keys: mint, list, read, revoke. A key acts for one principal (a
26
+ * person or a service account) with that principal's grants; its role is
27
+ * a ceiling under them and its project a narrowing.
25
28
  *
26
- * A caller can only give a new key what it holds itself: every
27
- * capability it grants must be one of its own.
29
+ * - **Your own keys:** a person, or a service account's key, may mint,
30
+ * list, read and revoke the keys that act for them.
31
+ * - **Someone else's:** only a tenant admin (`admin` on the tenant when
32
+ * authorization is on, otherwise the `tenant-admin` scope) mints keys
33
+ * for another principal, and sees and revokes every key. A tenant admin
34
+ * whose token names no principal mints, as before principals, keys
35
+ * that are their own service account.
36
+ * - **What a new key may hold:** an `admin` key needs a tenant admin
37
+ * minting it (and, for someone else, a principal that is one: the store
38
+ * refuses `role-exceeds-principal`). Every capability granted must be
39
+ * one the minter holds. A minter narrowed to a project mints only keys
40
+ * narrowed to the same project.
28
41
  */
29
42
  export function tokensRouter(admin: TokenAdmin, authorizer?: Authorizer): Hono<AppEnv> {
30
43
  const r = new Hono<AppEnv>();
31
- r.use('/*', requireTenantAdmin(authorizer));
44
+ r.use('/*', requireOwnerOrAdmin(authorizer));
32
45
 
33
46
  // ---------- POST / (mint) ----------
34
47
  r.post('/', async (c) => {
@@ -40,22 +53,15 @@ export function tokensRouter(admin: TokenAdmin, authorizer?: Authorizer): Hono<A
40
53
  const parsed = parseMintBody(body.value);
41
54
  if (parsed.kind === 'err') return fail(c, parsed.error, requestId);
42
55
 
43
- const held = new Set(c.get('capabilities') ?? []);
44
- const notHeld = parsed.value.capabilities.filter((cap) => !held.has(cap));
45
- if (notHeld.length > 0) {
46
- return fail(
47
- c,
48
- {
49
- code: 'permission-denied',
50
- message: `A key can only be given capabilities its minter holds; you don't hold: ${notHeld.join(', ')}`,
51
- },
52
- requestId,
53
- );
54
- }
56
+ const owner = keyOwner(c);
57
+ const principal = parsed.value.for ?? owner;
58
+ const refused = await mintRefusal(c, authorizer, owner, principal, parsed.value);
59
+ if (refused !== undefined) return fail(c, refused, requestId);
55
60
 
56
61
  const createdBy = callerRef(c);
57
62
  const minted = await admin.mint({
58
63
  tenantId,
64
+ ...(principal !== undefined && { principal }),
59
65
  role: parsed.value.role,
60
66
  capabilities: parsed.value.capabilities,
61
67
  ...(parsed.value.label !== undefined && { label: parsed.value.label }),
@@ -63,6 +69,11 @@ export function tokensRouter(admin: TokenAdmin, authorizer?: Authorizer): Hono<A
63
69
  ...(parsed.value.projectId !== undefined && { projectId: parsed.value.projectId }),
64
70
  ...(createdBy !== undefined && { createdBy }),
65
71
  });
72
+ if (!('token' in minted)) {
73
+ return fail(c, { code: minted.kind, message: minted.message }, requestId);
74
+ }
75
+ // The key's secret is shown once: an Idempotency-Key repeat doesn't get it.
76
+ withholdFromReplay(c);
66
77
  c.status(201);
67
78
  return c.json({ ...toWire(minted.record), token: minted.token });
68
79
  });
@@ -81,7 +92,16 @@ export function tokensRouter(admin: TokenAdmin, authorizer?: Authorizer): Hono<A
81
92
  }
82
93
  after = { createdAt: new Date(decoded.createdAt), tokenId: decoded.id as ApiTokenId };
83
94
  }
84
- const records = await admin.list({ tenantId, limit: limit + 1, ...(after && { after }) });
95
+ const filter = parsePrincipalQuery(c.req.query('principal'));
96
+ if (filter.kind === 'err') return fail(c, filter.error, requestId);
97
+ // An admin sees every key (or one principal's); anyone else, their own.
98
+ const principal = (await isTenantAdmin(c, authorizer)) ? filter.value : keyOwner(c);
99
+ const records = await admin.list({
100
+ tenantId,
101
+ limit: limit + 1,
102
+ ...(principal !== undefined && { principal }),
103
+ ...(after && { after }),
104
+ });
85
105
  const hasMore = records.length > limit;
86
106
  const page = hasMore ? records.slice(0, limit) : records;
87
107
  const last = page[page.length - 1];
@@ -101,9 +121,8 @@ export function tokensRouter(admin: TokenAdmin, authorizer?: Authorizer): Hono<A
101
121
  // ---------- GET /:tokenId ----------
102
122
  r.get('/:tokenId', async (c) => {
103
123
  const requestId = c.get('requestId');
104
- const tenantId = c.get('tenantId') as TenantId;
105
124
  const tokenId = c.req.param('tokenId') as ApiTokenId;
106
- const record = await admin.get({ tenantId, tokenId });
125
+ const record = await visibleKey(c, admin, authorizer, tokenId);
107
126
  if (record === undefined) return notFound(c, tokenId, requestId);
108
127
  return c.json(toWire(record));
109
128
  });
@@ -113,8 +132,16 @@ export function tokensRouter(admin: TokenAdmin, authorizer?: Authorizer): Hono<A
113
132
  const requestId = c.get('requestId');
114
133
  const tenantId = c.get('tenantId') as TenantId;
115
134
  const tokenId = c.req.param('tokenId') as ApiTokenId;
116
-
117
- const outcome = await admin.revoke({ tenantId, tokenId });
135
+ // Someone else's key reads as missing, as it does for `get`.
136
+ if ((await visibleKey(c, admin, authorizer, tokenId)) === undefined) {
137
+ return notFound(c, tokenId, requestId);
138
+ }
139
+ const revokedBy = callerRef(c);
140
+ const outcome = await admin.revoke({
141
+ tenantId,
142
+ tokenId,
143
+ ...(revokedBy !== undefined && { revokedBy }),
144
+ });
118
145
  if (outcome.kind === 'not-found') return notFound(c, tokenId, requestId);
119
146
  return c.json({ tokenId: tokenId as unknown as string, revoked: true });
120
147
  });
@@ -122,18 +149,10 @@ export function tokensRouter(admin: TokenAdmin, authorizer?: Authorizer): Hono<A
122
149
  return r;
123
150
  }
124
151
 
125
- function requireTenantAdmin(authorizer: Authorizer | undefined): MiddlewareHandler<AppEnv> {
126
- if (authorizer !== undefined) {
127
- return async (c, next) => {
128
- const tenantId = c.get('tenantId') as TenantId;
129
- const mw = authorizer.authorize('admin', async () =>
130
- ref('tenant', tenantId as unknown as string),
131
- );
132
- return mw(c, next);
133
- };
134
- }
152
+ /** A caller with keys of their own, or a tenant admin. */
153
+ function requireOwnerOrAdmin(authorizer: Authorizer | undefined): MiddlewareHandler<AppEnv> {
135
154
  return async (c, next) => {
136
- if (!(c.get('scopes') ?? []).includes('tenant-admin')) {
155
+ if (keyOwner(c) === undefined && !(await isTenantAdmin(c, authorizer))) {
137
156
  return fail(
138
157
  c,
139
158
  { code: 'permission-denied', message: 'Managing API keys needs a tenant admin' },
@@ -145,18 +164,80 @@ function requireTenantAdmin(authorizer: Authorizer | undefined): MiddlewareHandl
145
164
  };
146
165
  }
147
166
 
148
- /** The caller as a principal reference: `user:<id>` or `service_account:<tokenId>`. */
149
- function callerRef(c: Context<AppEnv>): string | undefined {
150
- const userId = c.get('userId');
151
- if (userId !== undefined) return `user:${userId as unknown as string}`;
152
- const tokenId = c.get('tokenId');
153
- if (tokenId !== undefined) return `service_account:${tokenId as unknown as string}`;
167
+ /**
168
+ * Whom the caller's own keys act for: the person, or the service account
169
+ * the caller's key acts for. A token naming neither (a key from before
170
+ * principals, a static token) has no keys of its own.
171
+ */
172
+ function keyOwner(c: Context<AppEnv>): TokenPrincipal | undefined {
173
+ const caller = callerPrincipal(c);
174
+ if (caller?.kind === 'service-account' && c.get('serviceAccountId') === undefined) {
175
+ return undefined;
176
+ }
177
+ return caller;
178
+ }
179
+
180
+ function samePrincipal(a: TokenPrincipal, b: TokenPrincipal | undefined): boolean {
181
+ if (b === undefined) return false;
182
+ if (a.kind === 'user') return b.kind === 'user' && b.userId === a.userId;
183
+ return b.kind === 'service-account' && b.serviceAccountId === a.serviceAccountId;
184
+ }
185
+
186
+ /** Why the route refuses this mint before the store sees it, if it does. */
187
+ async function mintRefusal(
188
+ c: Context<AppEnv>,
189
+ authorizer: Authorizer | undefined,
190
+ owner: TokenPrincipal | undefined,
191
+ principal: TokenPrincipal | undefined,
192
+ body: ParsedMintBody,
193
+ ): Promise<WireError | undefined> {
194
+ const forSomeoneElse = principal !== undefined && !samePrincipal(principal, owner);
195
+ if ((forSomeoneElse || body.role === 'admin') && !(await isTenantAdmin(c, authorizer))) {
196
+ return {
197
+ code: 'permission-denied',
198
+ message: forSomeoneElse
199
+ ? 'Only a tenant admin mints keys for someone else'
200
+ : 'Only a tenant admin mints an admin key',
201
+ };
202
+ }
203
+ const held = new Set(c.get('capabilities') ?? []);
204
+ const notHeld = body.capabilities.filter((cap) => !held.has(cap));
205
+ if (notHeld.length > 0) {
206
+ return {
207
+ code: 'permission-denied',
208
+ message: `A key can only be given capabilities its minter holds; you don't hold: ${notHeld.join(', ')}`,
209
+ };
210
+ }
211
+ const narrowedTo = c.get('tokenProjectId');
212
+ if (narrowedTo !== undefined && body.projectId !== (narrowedTo as unknown as ProjectId)) {
213
+ return {
214
+ code: 'key-project-mismatch',
215
+ message: `Your key is limited to project ${narrowedTo}: a key it mints must be limited to it too`,
216
+ };
217
+ }
154
218
  return undefined;
155
219
  }
156
220
 
221
+ /** A key the caller may see: any, for a tenant admin; otherwise only their own. */
222
+ async function visibleKey(
223
+ c: Context<AppEnv>,
224
+ admin: TokenAdmin,
225
+ authorizer: Authorizer | undefined,
226
+ tokenId: ApiTokenId,
227
+ ): Promise<ApiTokenRecord | undefined> {
228
+ const tenantId = c.get('tenantId') as TenantId;
229
+ const record = await admin.get({ tenantId, tokenId });
230
+ if (record === undefined) return undefined;
231
+ if (await isTenantAdmin(c, authorizer)) return record;
232
+ return record.principal !== undefined && samePrincipal(record.principal, keyOwner(c))
233
+ ? record
234
+ : undefined;
235
+ }
236
+
157
237
  function toWire(record: ApiTokenRecord): Record<string, unknown> {
158
238
  return {
159
239
  tokenId: record.tokenId as unknown as string,
240
+ ...(record.principal !== undefined && { principal: principalToWire(record.principal) }),
160
241
  role: record.role,
161
242
  capabilities: record.capabilities,
162
243
  ...(record.label !== undefined && { label: record.label }),
@@ -207,6 +288,7 @@ async function readJsonBody(
207
288
  }
208
289
 
209
290
  type ParsedMintBody = {
291
+ readonly for?: TokenPrincipal;
210
292
  readonly role: ApiTokenRole;
211
293
  readonly capabilities: readonly string[];
212
294
  readonly label?: string;
@@ -232,23 +314,29 @@ function parseMintBody(body: unknown): Parsed<ParsedMintBody> {
232
314
  if (role.kind === 'err') return role;
233
315
  const capabilities = parseCapabilities(b.capabilities);
234
316
  if (capabilities.kind === 'err') return capabilities;
317
+ const principal = parseFor(b.for);
318
+ if (principal.kind === 'err') return principal;
235
319
  const out: {
320
+ for?: TokenPrincipal;
236
321
  role: ApiTokenRole;
237
322
  capabilities: readonly string[];
238
323
  label?: string;
239
324
  expiresAt?: Date;
240
325
  projectId?: ProjectId;
241
- } = { role: role.value, capabilities: capabilities.value };
326
+ } = {
327
+ ...(principal.value !== undefined && { for: principal.value }),
328
+ role: role.value,
329
+ capabilities: capabilities.value,
330
+ };
242
331
 
243
332
  if (b.label !== undefined) {
244
333
  if (typeof b.label !== 'string') return badInput('`label` must be a string');
245
334
  out.label = b.label;
246
335
  }
247
336
  if (b.expiresAt !== undefined) {
248
- if (typeof b.expiresAt !== 'string') return badInput('`expiresAt` must be an ISO date string');
249
- const parsed = new Date(b.expiresAt);
250
- if (Number.isNaN(parsed.getTime())) return badInput('`expiresAt` is not a valid ISO date');
251
- out.expiresAt = parsed;
337
+ const expiresAt = parseExpiresAt(b.expiresAt);
338
+ if (expiresAt.kind === 'err') return expiresAt;
339
+ out.expiresAt = expiresAt.value;
252
340
  }
253
341
  if (b.projectId !== undefined) {
254
342
  if (typeof b.projectId !== 'string') {
@@ -259,6 +347,14 @@ function parseMintBody(body: unknown): Parsed<ParsedMintBody> {
259
347
  return { kind: 'ok', value: out };
260
348
  }
261
349
 
350
+ /** `expiresAt`: an ISO date string. */
351
+ function parseExpiresAt(raw: unknown): Parsed<Date> {
352
+ if (typeof raw !== 'string') return badInput('`expiresAt` must be an ISO date string');
353
+ const parsed = new Date(raw);
354
+ if (Number.isNaN(parsed.getTime())) return badInput('`expiresAt` is not a valid ISO date');
355
+ return { kind: 'ok', value: parsed };
356
+ }
357
+
262
358
  /** `role`: `admin` | `member`; `member` when absent (least privilege). */
263
359
  function parseRole(raw: unknown): Parsed<ApiTokenRole> {
264
360
  if (raw === undefined) return { kind: 'ok', value: 'member' };
@@ -283,3 +379,33 @@ function parseCapabilities(raw: unknown): Parsed<readonly string[]> {
283
379
  }
284
380
  return { kind: 'ok', value: out };
285
381
  }
382
+
383
+ /** `for`, when present: whom the key acts for. */
384
+ function parseFor(raw: unknown): Parsed<TokenPrincipal | undefined> {
385
+ return raw === undefined ? { kind: 'ok', value: undefined } : parsePrincipalBody(raw);
386
+ }
387
+
388
+ /** `for`: `{kind: 'user' | 'service-account', id}`. */
389
+ function parsePrincipalBody(raw: unknown): Parsed<TokenPrincipal> {
390
+ const b = (raw ?? {}) as { kind?: unknown; id?: unknown };
391
+ if (typeof b.id !== 'string' || b.id.length === 0) {
392
+ return badInput('`for.id` must be the id of a person or a service account');
393
+ }
394
+ if (b.kind === 'user') return { kind: 'ok', value: { kind: 'user', userId: b.id } };
395
+ if (b.kind === 'service-account') {
396
+ return { kind: 'ok', value: { kind: 'service-account', serviceAccountId: b.id } };
397
+ }
398
+ return badInput("`for.kind` must be 'user' or 'service-account'");
399
+ }
400
+
401
+ /** `?principal=user:<id>` or `?principal=service-account:<id>`: one principal's keys (admins). */
402
+ function parsePrincipalQuery(raw: string | undefined): Parsed<TokenPrincipal | undefined> {
403
+ if (raw === undefined || raw === '') return { kind: 'ok', value: undefined };
404
+ const colon = raw.indexOf(':');
405
+ const kind = raw.slice(0, colon);
406
+ const id = raw.slice(colon + 1);
407
+ if (colon <= 0 || id === '') {
408
+ return badInput('`principal` is `user:<id>` or `service-account:<id>`');
409
+ }
410
+ return parsePrincipalBody({ kind, id });
411
+ }
@@ -13,6 +13,7 @@ import { refuseWritesWhenReadOnly } from '../registry-read-only.js';
13
13
  import type { ToolRegistryBinding } from '../tool-binding.js';
14
14
  import type { AppEnv } from '../types.js';
15
15
  import { clampLimit } from './pagination.js';
16
+ import { projectMismatch } from './project-mismatch.js';
16
17
  import { parseScopeParams } from './scope-params.js';
17
18
 
18
19
  /**
@@ -127,8 +128,15 @@ export function toolsRouter(
127
128
  ...(scopeParsed.scope !== undefined && { scope: scopeParsed.scope }),
128
129
  ...(scopeParsed.inherit !== undefined && { inherit: scopeParsed.inherit }),
129
130
  });
131
+ // Only what the caller may read (T243 A), as `GET …/:id` asks.
132
+ const visible =
133
+ authorizer === undefined
134
+ ? page.data
135
+ : await authorizer.filterByCan(c, 'read', page.data, (a) =>
136
+ ref('tool', a.id as unknown as string),
137
+ );
130
138
  return c.json({
131
- data: page.data.map(serializeTool),
139
+ data: visible.map(serializeTool),
132
140
  hasMore: page.nextCursor !== undefined,
133
141
  ...(page.nextCursor !== undefined && { nextCursor: page.nextCursor as unknown as string }),
134
142
  });
@@ -329,6 +337,9 @@ export function toolsRouter(
329
337
  ),
330
338
  );
331
339
  }
340
+ if (outcome.kind === 'project-mismatch') {
341
+ return projectMismatch(c, 'tool', outcome.toolId as unknown as string, outcome.projectId);
342
+ }
332
343
  if (outcome.kind === 'project-not-found') {
333
344
  // Caller supplied a `projectId` that does not resolve within
334
345
  // this tenant. Distinct signal from `already-registered` so the
@@ -0,0 +1,72 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+
4
+ import type { Context } from 'hono';
5
+
6
+ import { type Action, ref } from '@kindgi/authz';
7
+ import type { TenantId, TriggerId } from '@kindgi/types';
8
+
9
+ import type { Authorizer } from '../middleware/authorize.js';
10
+ import type { TriggerRecord, TriggerRegistryBinding } from '../trigger-binding.js';
11
+ import type { AppEnv } from '../types.js';
12
+ import { deniedBy } from './denied.js';
13
+
14
+ /**
15
+ * Who may manage an event or webhook trigger (T243 A): the flow it fires.
16
+ * A trigger has no parent tuple of its own, so every check names its
17
+ * flow: `read` to see it; `write` to pause, resume or unregister it; and
18
+ * `write` plus `execute` to register it or change what it runs, since it
19
+ * starts runs of the flow as its registrant would. With no authorizer,
20
+ * nothing is checked.
21
+ */
22
+ export interface TriggerAccess {
23
+ /** Registering a trigger for `flowId`: the refusal, if any. */
24
+ onRegister(c: Context<AppEnv>, flowId: string): Promise<Response | undefined>;
25
+ /**
26
+ * A route on one trigger: the refusal, if any. A trigger that isn't
27
+ * there, or is of another kind, passes on to the handler's 404.
28
+ */
29
+ onTrigger(
30
+ c: Context<AppEnv>,
31
+ triggerId: TriggerId,
32
+ actions: readonly Action[],
33
+ ): Promise<Response | undefined>;
34
+ /** The rows of a list whose flow the caller may read. */
35
+ visible<T extends { readonly flowId: string }>(
36
+ c: Context<AppEnv>,
37
+ rows: readonly T[],
38
+ ): Promise<readonly T[]>;
39
+ }
40
+
41
+ export function triggerAccess(
42
+ binding: TriggerRegistryBinding,
43
+ kind: Exclude<TriggerRecord['kind'], 'cron'>,
44
+ authorizer: Authorizer | undefined,
45
+ ): TriggerAccess {
46
+ const onFlow = async (
47
+ c: Context<AppEnv>,
48
+ flowId: string,
49
+ actions: readonly Action[],
50
+ ): Promise<Response | undefined> => {
51
+ for (const action of actions) {
52
+ const refused = await deniedBy(authorizer, c, action, ref('flow', flowId));
53
+ if (refused !== undefined) return refused;
54
+ }
55
+ return undefined;
56
+ };
57
+ return {
58
+ onRegister: (c, flowId) => onFlow(c, flowId, ['write', 'execute']),
59
+ async onTrigger(c, triggerId, actions) {
60
+ if (authorizer === undefined) return undefined;
61
+ const tenantId = c.get('tenantId') as TenantId;
62
+ const rec = await binding.get({ tenantId, triggerId });
63
+ // A schedule's access is the schedules router's own (it may run an agent).
64
+ if (rec === null || rec.kind === 'cron' || rec.kind !== kind) return undefined;
65
+ return onFlow(c, rec.flowId, actions);
66
+ },
67
+ async visible(c, rows) {
68
+ if (authorizer === undefined) return rows;
69
+ return authorizer.filterByCan(c, 'read', rows, (row) => ref('flow', row.flowId));
70
+ },
71
+ };
72
+ }
@@ -14,6 +14,8 @@ import type {
14
14
  } from '@kindgi/types';
15
15
 
16
16
  import { statusFor, toWireError } from '../errors.js';
17
+ import type { Authorizer } from '../middleware/authorize.js';
18
+ import { withholdFromReplay } from '../middleware/idempotency.js';
17
19
  import type { AppEnv } from '../types.js';
18
20
  import {
19
21
  WEBHOOK_DELIVERY_STATUSES,
@@ -30,6 +32,7 @@ import {
30
32
  } from '../webhook-endpoint-binding.js';
31
33
  import { clampLimit } from './pagination.js';
32
34
  import { parseSecretRef } from './secret-ref.js';
35
+ import { tenantAdminAccess } from './tenant-access.js';
33
36
 
34
37
  const MAX_URL_LENGTH = 2048;
35
38
  const MAX_DESCRIPTION_LENGTH = 500;
@@ -41,8 +44,13 @@ const MAX_FLOW_ID_LENGTH = 200;
41
44
  * the platform sends signed events to, read their delivery log, redeliver
42
45
  * and send a test event. See `WebhookEndpointBinding`.
43
46
  */
44
- export function webhookEndpointsRouter(binding: WebhookEndpointBinding): Hono<AppEnv> {
47
+ export function webhookEndpointsRouter(
48
+ binding: WebhookEndpointBinding,
49
+ authorizer?: Authorizer,
50
+ ): Hono<AppEnv> {
45
51
  const r = new Hono<AppEnv>();
52
+ // Deliveries carry every project's runs: all of it is an admin's.
53
+ r.use('*', tenantAdminAccess(authorizer));
46
54
 
47
55
  // ---------- POST / (create) ----------
48
56
  r.post('/', async (c) => {
@@ -71,8 +79,12 @@ export function webhookEndpointsRouter(binding: WebhookEndpointBinding): Hono<Ap
71
79
  });
72
80
 
73
81
  // ---------- POST /generate-secret ----------
74
- // A strong signing secret to store before registering; nothing is kept.
75
- r.post('/generate-secret', (c) => c.json({ secret: generateWebhookSecret() }));
82
+ // A strong signing secret to store before registering; nothing is kept,
83
+ // and an Idempotency-Key repeat doesn't get it.
84
+ r.post('/generate-secret', (c) => {
85
+ withholdFromReplay(c);
86
+ return c.json({ secret: generateWebhookSecret() });
87
+ });
76
88
 
77
89
  // ---------- GET / (list) ----------
78
90
  r.get('/', async (c) => {
@@ -9,6 +9,7 @@ import type { Context } from 'hono';
9
9
  import type { Cursor, TenantId, TriggerId } from '@kindgi/types';
10
10
 
11
11
  import { statusFor, toWireError } from '../errors.js';
12
+ import type { Authorizer } from '../middleware/authorize.js';
12
13
  import type {
13
14
  RegisterWebhookTriggerInput,
14
15
  TriggerRegistryBinding,
@@ -17,6 +18,7 @@ import type {
17
18
  } from '../trigger-binding.js';
18
19
  import type { AppEnv } from '../types.js';
19
20
  import { clampLimit } from './pagination.js';
21
+ import { triggerAccess } from './trigger-access.js';
20
22
 
21
23
  /**
22
24
  * Webhook triggers — fire a flow run when an HMAC-signed request
@@ -33,9 +35,16 @@ import { clampLimit } from './pagination.js';
33
35
  * carries `hmacSecretName`, a handle into the tenant secrets store the
34
36
  * caller wrote to first via `POST /v1/secrets`. Rotation flows through
35
37
  * `POST /v1/secrets/:name/rotate` — trigger row untouched.
38
+ *
39
+ * With an authorizer (T243 A), the trigger's flow decides who may see or
40
+ * change it (`trigger-access.ts`).
36
41
  */
37
- export function webhooksRouter(binding: TriggerRegistryBinding): Hono<AppEnv> {
42
+ export function webhooksRouter(
43
+ binding: TriggerRegistryBinding,
44
+ authorizer?: Authorizer,
45
+ ): Hono<AppEnv> {
38
46
  const r = new Hono<AppEnv>();
47
+ const access = triggerAccess(binding, 'webhook', authorizer);
39
48
 
40
49
  // ---------- POST / (register) ----------
41
50
  r.post('/', async (c) => {
@@ -53,6 +62,9 @@ export function webhooksRouter(binding: TriggerRegistryBinding): Hono<AppEnv> {
53
62
  if (flowVersion.kind === 'err') return bad(c, requestId, flowVersion.message);
54
63
  if (hmacSecretName.kind === 'err') return bad(c, requestId, hmacSecretName.message);
55
64
 
65
+ const refused = await access.onRegister(c, flowId.value);
66
+ if (refused !== undefined) return refused;
67
+
56
68
  const rawConfig = (body.config ?? {}) as Record<string, unknown>;
57
69
  // Route mints the webhookId — a random UUID, returned so the caller
58
70
  // learns the receiver URL from the response.
@@ -100,7 +112,9 @@ export function webhooksRouter(binding: TriggerRegistryBinding): Hono<AppEnv> {
100
112
  ...(statusFilter !== undefined && { status: statusFilter }),
101
113
  });
102
114
  return c.json({
103
- data: page.data.map((row) => serializeWebhook(row as WebhookTriggerRecord)),
115
+ data: (await access.visible(c, page.data as readonly WebhookTriggerRecord[])).map(
116
+ serializeWebhook,
117
+ ),
104
118
  hasMore: page.nextCursor !== undefined,
105
119
  ...(page.nextCursor !== undefined && { nextCursor: page.nextCursor as unknown as string }),
106
120
  });
@@ -111,6 +125,8 @@ export function webhooksRouter(binding: TriggerRegistryBinding): Hono<AppEnv> {
111
125
  const requestId = c.get('requestId');
112
126
  const tenantId = c.get('tenantId') as TenantId;
113
127
  const triggerId = c.req.param('triggerId') as TriggerId;
128
+ const refused = await access.onTrigger(c, triggerId, ['read']);
129
+ if (refused !== undefined) return refused;
114
130
 
115
131
  const rec = await binding.get({ tenantId, triggerId });
116
132
  if (rec === null || rec.kind !== 'webhook') return notFound(c, requestId, triggerId);
@@ -122,6 +138,8 @@ export function webhooksRouter(binding: TriggerRegistryBinding): Hono<AppEnv> {
122
138
  const requestId = c.get('requestId');
123
139
  const tenantId = c.get('tenantId') as TenantId;
124
140
  const triggerId = c.req.param('triggerId') as TriggerId;
141
+ const refused = await access.onTrigger(c, triggerId, ['write', 'execute']);
142
+ if (refused !== undefined) return refused;
125
143
 
126
144
  const parsed = await parseJsonObject(c, requestId);
127
145
  if (parsed.kind === 'err') return parsed.response;
@@ -162,6 +180,8 @@ export function webhooksRouter(binding: TriggerRegistryBinding): Hono<AppEnv> {
162
180
  const requestId = c.get('requestId');
163
181
  const tenantId = c.get('tenantId') as TenantId;
164
182
  const triggerId = c.req.param('triggerId') as TriggerId;
183
+ const refused = await access.onTrigger(c, triggerId, ['write']);
184
+ if (refused !== undefined) return refused;
165
185
  const result = await binding.pause({ tenantId, triggerId });
166
186
  if (result.kind === 'err') return lifecycleError(c, requestId, result.error, triggerId);
167
187
  return c.json(serializeWebhook(result.value as WebhookTriggerRecord));
@@ -172,6 +192,8 @@ export function webhooksRouter(binding: TriggerRegistryBinding): Hono<AppEnv> {
172
192
  const requestId = c.get('requestId');
173
193
  const tenantId = c.get('tenantId') as TenantId;
174
194
  const triggerId = c.req.param('triggerId') as TriggerId;
195
+ const refused = await access.onTrigger(c, triggerId, ['write']);
196
+ if (refused !== undefined) return refused;
175
197
  const result = await binding.resume({ tenantId, triggerId });
176
198
  if (result.kind === 'err') return lifecycleError(c, requestId, result.error, triggerId);
177
199
  return c.json(serializeWebhook(result.value as WebhookTriggerRecord));
@@ -181,6 +203,8 @@ export function webhooksRouter(binding: TriggerRegistryBinding): Hono<AppEnv> {
181
203
  r.post('/:triggerId/unregister', async (c) => {
182
204
  const tenantId = c.get('tenantId') as TenantId;
183
205
  const triggerId = c.req.param('triggerId') as TriggerId;
206
+ const refused = await access.onTrigger(c, triggerId, ['write']);
207
+ if (refused !== undefined) return refused;
184
208
  const outcome = await binding.unregister({ tenantId, triggerId });
185
209
  return c.json({
186
210
  triggerId: triggerId as unknown as string,
@@ -0,0 +1,42 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+
4
+ import { parseFailureMessage } from '@kindgi/agents';
5
+
6
+ /** Why a run failed, on the wire (`Run.failure`). */
7
+ export interface RunFailure {
8
+ /** The error's own code (`budget-exceeded`, `capability-routing-failed`, …), or `run-failed`. */
9
+ readonly code: string;
10
+ readonly message: string;
11
+ /** What the error came from, when it says (e.g. the router's reasons, by provider). */
12
+ readonly cause?: unknown;
13
+ }
14
+
15
+ /**
16
+ * A failed run's error, so a caller needn't parse `failureMessage`. An
17
+ * agent turn's failure reads back as its typed error, the way
18
+ * `invokeAgent` reads a run (`parseFailureMessage`; with several joined,
19
+ * the first). Any other is `run-failed`, in the run's own words.
20
+ * Absent unless the run is `failed`.
21
+ */
22
+ export function runFailure(row: {
23
+ readonly runId: unknown;
24
+ readonly status: string;
25
+ readonly failureMessage?: string | null;
26
+ }): RunFailure | undefined {
27
+ if (row.status !== 'failed') return undefined;
28
+ const raw = row.failureMessage ?? undefined;
29
+ const error = parseFailureMessage(raw);
30
+ if (error !== undefined) {
31
+ const cause = (error as { readonly cause?: unknown }).cause;
32
+ return {
33
+ code: error.code,
34
+ message: error.message,
35
+ ...(cause !== undefined && cause !== null && { cause }),
36
+ };
37
+ }
38
+ return {
39
+ code: 'run-failed',
40
+ message: raw !== undefined && raw !== '' ? raw : `Run ${String(row.runId)} failed`,
41
+ };
42
+ }