@kindgi/api 0.1.4-rc.5 → 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 +92 -13
  112. package/dist/openapi/schemas.d.ts.map +1 -1
  113. package/dist/openapi/schemas.js +2122 -370
  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 +188 -8
  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 +15286 -8653
  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 +2438 -485
  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 +223 -8
  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
@@ -2,6 +2,7 @@
2
2
  // Copyright (C) 2026 Kindgi Inc.
3
3
 
4
4
  import type { Context, MiddlewareHandler, Next } from 'hono';
5
+ import { getCookie } from 'hono/cookie';
5
6
 
6
7
  import type { ReviewerRole } from '@kindgi/authz';
7
8
  import type { ApiTokenId, SessionId, TenantId, UserId } from '@kindgi/types';
@@ -49,11 +50,27 @@ export interface TokenResolution {
49
50
  */
50
51
  readonly userId?: UserId;
51
52
  /**
52
- * The id of a durable API key (`POST /v1/tokens`). The caller is then
53
- * the key's service account (`service_account:<tokenId>`), not a user;
54
- * the middleware surfaces it as `c.get('tokenId')`.
53
+ * The id of a durable API key (`POST /v1/tokens`), surfaced as
54
+ * `c.get('tokenId')`. The caller is the key's principal: `userId` for a
55
+ * person's key, `serviceAccountId` for a service account's. A key with
56
+ * neither (a store built before principals) is its own service account,
57
+ * `service_account:<tokenId>`.
55
58
  */
56
59
  readonly tokenId?: ApiTokenId;
60
+ /** The service account an API key acts for (`service_account:<id>`). */
61
+ readonly serviceAccountId?: string;
62
+ /**
63
+ * An API key's role: the most it may do, under its principal's grants.
64
+ * A `member` key is refused tenant-admin actions even when its principal
65
+ * is an admin.
66
+ */
67
+ readonly tokenRole?: 'admin' | 'member';
68
+ /**
69
+ * The project an API key is narrowed to. A request naming another
70
+ * project is refused (`key-project-mismatch`), and so is an `admin`
71
+ * action on the tenant.
72
+ */
73
+ readonly tokenProjectId?: string;
57
74
  /**
58
75
  * Optional session id. Populated by the session-token verifier path
59
76
  * (`kgi_sk_*` tokens) so `/v1/identity/whoami` and `/v1/auth/logout`
@@ -115,10 +132,45 @@ export interface TokenResolution {
115
132
  export const SESSION_TOKEN_PREFIX = 'kgi_sk_' as const;
116
133
 
117
134
  /**
118
- * Compose the on-wire session token from a `SessionId`. Session tokens
119
- * are opaque to clients — the shape below is a framework convention,
120
- * not part of the wire contract. Do NOT parse it in downstream code
121
- * outside the middleware.
135
+ * The cookie a browser carries its session token in, when the deployment
136
+ * turns cookie sessions on (`SessionConfig.cookie`). `__Host-`: Secure,
137
+ * Path=/, no Domain, so it's sent only to the origin that set it.
138
+ */
139
+ export const SESSION_COOKIE_NAME = '__Host-kindgi_session' as const;
140
+
141
+ /**
142
+ * Cookie sessions: where the middleware reads a browser's session token
143
+ * when the request has no `Authorization` header.
144
+ */
145
+ export interface SessionCookieOptions {
146
+ /** The cookie's name. Default `SESSION_COOKIE_NAME`. */
147
+ readonly name?: string;
148
+ /**
149
+ * The origins a cookie-authenticated request may come from (the
150
+ * console's origin, e.g. `https://kindgi.example.com`). An unsafe method
151
+ * (anything but GET, HEAD and OPTIONS) authenticated by the cookie must
152
+ * carry one of them in `Origin`, else 403 `csrf-origin-mismatch`; so
153
+ * must a request with no `Origin` at all. Bearer requests are unaffected.
154
+ */
155
+ readonly allowedOrigins: readonly string[];
156
+ /**
157
+ * Also accept an `Origin` naming the host the request was sent to (its
158
+ * `Host`, or `X-Forwarded-Host` behind a proxy that sets it): the
159
+ * console served from the runtime itself, wherever it's reached. For a
160
+ * deployment that doesn't know its public URL. A cross-site page can't
161
+ * forge `Origin`, nor add `X-Forwarded-Host` without a CORS preflight.
162
+ */
163
+ readonly sameOrigin?: boolean;
164
+ }
165
+
166
+ const SAFE_METHODS = new Set(['GET', 'HEAD', 'OPTIONS']);
167
+
168
+ /**
169
+ * The older session token, `kgi_sk_<sessionId>`, for a session store
170
+ * without `resolveToken`. A store with it mints its own token
171
+ * (`SessionCreateOutput.token`), and the routes hand that out instead.
172
+ * Session tokens are opaque to clients: never parse one outside the
173
+ * middleware or the store that minted it.
122
174
  */
123
175
  export function encodeSessionToken(sessionId: SessionId): string {
124
176
  return `${SESSION_TOKEN_PREFIX}${sessionId as unknown as string}`;
@@ -150,6 +202,12 @@ export interface BearerAuthMiddlewareOptions {
150
202
  * fewer writes but coarser inactivity enforcement. Defaults to 60s.
151
203
  */
152
204
  readonly touchThrottleMs?: number;
205
+ /**
206
+ * Read a session token from a cookie when the request carries no
207
+ * `Authorization` header (browser sessions). Only `kgi_sk_` tokens are
208
+ * taken from it, and only with a `sessionStore`.
209
+ */
210
+ readonly sessionCookie?: SessionCookieOptions;
153
211
  /**
154
212
  * Accept public run tokens (`kgi_pt_…`), verified against
155
213
  * `signingKey`, on the routes `isAllowed` admits (every other route
@@ -169,9 +227,9 @@ const DEFAULT_TOUCH_THROTTLE_MS = 60_000;
169
227
  * handlers.
170
228
  *
171
229
  * Two token flavors are accepted:
172
- * - `kgi_sk_<sessionId>` — framework-issued OAuth session tokens.
173
- * Verified via `sessionStore.get`; expiration + revocation are pulled
174
- * from the session row.
230
+ * - `kgi_sk_…` — session tokens. Verified by the session store
231
+ * (`resolveToken`, or `get` for an older store); expiration +
232
+ * revocation are pulled from the session row.
175
233
  * - anything else — caller-plugged static bearer tokens. Verified via
176
234
  * `resolveToken`. Byte-shape-identical with the session-less contract.
177
235
  */
@@ -186,36 +244,13 @@ export function bearerAuthMiddleware(
186
244
  // bucket is bounded by the number of active sessions the process
187
245
  // has recently observed.
188
246
  const touchBucket = new Map<string, number>();
189
- return async (c, next) => {
190
- const requestId = c.get('requestId') as string;
191
- const header = c.req.header('authorization');
192
- if (header === undefined || header.length === 0) {
193
- const body = toWireError(
194
- { code: 'auth-missing', message: 'Authorization header is required' },
195
- requestId,
196
- );
197
- return c.json(body, statusFor('auth-missing') as never);
198
- }
199
- const match = /^Bearer\s+(.+)$/i.exec(header);
200
- if (match === null) {
201
- const body = toWireError(
202
- {
203
- code: 'auth-missing',
204
- message: 'Authorization header must be "Bearer <token>"',
205
- },
206
- requestId,
207
- );
208
- return c.json(body, statusFor('auth-missing') as never);
209
- }
210
- const token = match[1]?.trim() ?? '';
211
- if (token.length === 0) {
212
- const body = toWireError(
213
- { code: 'auth-missing', message: 'Bearer token is empty' },
214
- requestId,
215
- );
216
- return c.json(body, statusFor('auth-missing') as never);
217
- }
218
-
247
+ // Everything after the token is found: the header's or the cookie's.
248
+ const authenticate = async (
249
+ c: Context,
250
+ next: Next,
251
+ token: string,
252
+ requestId: string,
253
+ ): Promise<Response | undefined> => {
219
254
  if (token.startsWith(PUBLIC_RUN_TOKEN_PREFIX)) {
220
255
  return authenticatePublicRunToken(c, next, token, requestId, options.publicRunTokens);
221
256
  }
@@ -297,6 +332,18 @@ export function bearerAuthMiddleware(
297
332
  if (resolution.tokenId !== undefined) {
298
333
  c.set('tokenId', resolution.tokenId);
299
334
  }
335
+ if (resolution.serviceAccountId !== undefined) {
336
+ c.set('serviceAccountId', resolution.serviceAccountId);
337
+ }
338
+ if (resolution.tokenRole !== undefined) {
339
+ c.set('tokenRole', resolution.tokenRole);
340
+ }
341
+ if (resolution.tokenProjectId !== undefined) {
342
+ c.set('tokenProjectId', resolution.tokenProjectId);
343
+ }
344
+ if (resolution.expiresAt !== undefined && !isSessionToken) {
345
+ c.set('tokenExpiresAt', resolution.expiresAt);
346
+ }
300
347
  if (resolution.sessionId !== undefined) {
301
348
  c.set('sessionId', resolution.sessionId);
302
349
  }
@@ -337,6 +384,45 @@ export function bearerAuthMiddleware(
337
384
  await next();
338
385
  return;
339
386
  };
387
+ return async (c, next) => {
388
+ const requestId = c.get('requestId') as string;
389
+ const header = c.req.header('authorization');
390
+ if (header === undefined || header.length === 0) {
391
+ const fromCookie = sessionCookieToken(c, sessionStore, options.sessionCookie);
392
+ if (fromCookie !== undefined) {
393
+ const refusal = csrfRefusal(c, options.sessionCookie as SessionCookieOptions, requestId);
394
+ if (refusal !== undefined) return refusal;
395
+ c.set('sessionCookieName', options.sessionCookie?.name ?? SESSION_COOKIE_NAME);
396
+ return authenticate(c, next, fromCookie, requestId);
397
+ }
398
+ const body = toWireError(
399
+ { code: 'auth-missing', message: 'Authorization header is required' },
400
+ requestId,
401
+ );
402
+ return c.json(body, statusFor('auth-missing') as never);
403
+ }
404
+ const match = /^Bearer\s+(.+)$/i.exec(header);
405
+ if (match === null) {
406
+ const body = toWireError(
407
+ {
408
+ code: 'auth-missing',
409
+ message: 'Authorization header must be "Bearer <token>"',
410
+ },
411
+ requestId,
412
+ );
413
+ return c.json(body, statusFor('auth-missing') as never);
414
+ }
415
+ const token = match[1]?.trim() ?? '';
416
+ if (token.length === 0) {
417
+ const body = toWireError(
418
+ { code: 'auth-missing', message: 'Bearer token is empty' },
419
+ requestId,
420
+ );
421
+ return c.json(body, statusFor('auth-missing') as never);
422
+ }
423
+
424
+ return authenticate(c, next, token, requestId);
425
+ };
340
426
  }
341
427
 
342
428
  /**
@@ -345,31 +431,20 @@ export function bearerAuthMiddleware(
345
431
  * scopes + expiration + revocation) that the store returns natively —
346
432
  * we don't want to squeeze that shape through the resolver contract.
347
433
  *
348
- * Session-token tenant scoping: the stored session is the source of
349
- * truth for `tenantId`. We look up ACROSS tenants here — the resolved
350
- * `tenantId` is whatever the session was written under, so everything
351
- * downstream is scoped to that tenant only. A stolen session token still
352
- * can't cross tenants because the session it points to belongs to
353
- * exactly one tenant.
434
+ * A store with `resolveToken` gets the whole token: it reads the tenant
435
+ * from it, looks only in that tenant and compares a hash, so nothing is
436
+ * read across tenants before the token is authenticated. The session it
437
+ * returns is authoritative for `tenantId`.
354
438
  *
355
- * Since `SessionStoreBinding.get` requires `tenantId`, the lookup is:
356
- * extract `sessionId` from the token, then call
357
- * `sessionStore.get({ tenantId: MULTI_TENANT_LOOKUP, sessionId })`.
358
- * Session-store implementations MUST recognize the `MULTI_TENANT_LOOKUP`
359
- * sentinel and skip the tenant filter; the `tenantId` on the stored
360
- * session is authoritative.
439
+ * A store without it gets the older lookup: the token's remainder is the
440
+ * session id, read with `get({ tenantId: MULTI_TENANT_LOOKUP, sessionId })`,
441
+ * which such stores must honor by skipping the tenant filter.
361
442
  */
362
443
  async function resolveSessionToken(
363
444
  sessionStore: SessionStoreBinding,
364
445
  token: string,
365
446
  ): Promise<TokenResolution | null> {
366
- const sessionIdStr = token.slice(SESSION_TOKEN_PREFIX.length);
367
- if (sessionIdStr.length === 0) return null;
368
- const sessionId = sessionIdStr as unknown as SessionId;
369
- const session = await sessionStore.get({
370
- tenantId: MULTI_TENANT_LOOKUP,
371
- sessionId,
372
- });
447
+ const session = await findSession(sessionStore, token);
373
448
  if (session === null) return null;
374
449
  return {
375
450
  tenantId: session.tenantId,
@@ -386,12 +461,93 @@ async function resolveSessionToken(
386
461
  };
387
462
  }
388
463
 
464
+ /**
465
+ * The session token in the request's cookie, when cookie sessions are on
466
+ * and the cookie holds a session token; `undefined` otherwise (an API
467
+ * key or any other token is never taken from a cookie).
468
+ */
469
+ function sessionCookieToken(
470
+ c: Context,
471
+ sessionStore: SessionStoreBinding | undefined,
472
+ cookie: SessionCookieOptions | undefined,
473
+ ): string | undefined {
474
+ if (cookie === undefined || sessionStore === undefined) return undefined;
475
+ const value = getCookie(c, cookie.name ?? SESSION_COOKIE_NAME)?.trim();
476
+ if (value === undefined || !value.startsWith(SESSION_TOKEN_PREFIX)) return undefined;
477
+ return value;
478
+ }
479
+
480
+ /**
481
+ * A cookie-authenticated unsafe request must come from an allowed origin:
482
+ * a browser always sends `Origin` on such requests, and a cross-site page
483
+ * can't forge it. A missing `Origin` is refused too.
484
+ */
485
+ function csrfRefusal(
486
+ c: Context,
487
+ cookie: SessionCookieOptions,
488
+ requestId: string,
489
+ ): Response | undefined {
490
+ if (SAFE_METHODS.has(c.req.method)) return undefined;
491
+ const origin = c.req.header('origin');
492
+ if (origin !== undefined && cookie.allowedOrigins.includes(origin)) return undefined;
493
+ if (origin !== undefined && cookie.sameOrigin === true && isSameHost(c, origin)) return undefined;
494
+ const body = toWireError(
495
+ {
496
+ code: 'csrf-origin-mismatch',
497
+ message:
498
+ origin === undefined
499
+ ? 'A request signed in by the session cookie must say where it comes from (Origin)'
500
+ : `A request signed in by the session cookie can't come from ${origin}`,
501
+ },
502
+ requestId,
503
+ );
504
+ return c.json(body, statusFor('csrf-origin-mismatch') as never);
505
+ }
506
+
507
+ /**
508
+ * Whether `origin` names the host this request was sent to, over https (or
509
+ * plain http on loopback, for local development). The session cookie is
510
+ * `__Host-` and Secure, so a browser holding it is on https: a plain-http
511
+ * page on the same host (an on-path attacker's) is not the same origin.
512
+ */
513
+ function isSameHost(c: Context, origin: string): boolean {
514
+ let host: string;
515
+ try {
516
+ const url = new URL(origin);
517
+ if (url.protocol !== 'https:' && !(url.protocol === 'http:' && isLoopback(url.hostname))) {
518
+ return false;
519
+ }
520
+ host = url.host;
521
+ } catch {
522
+ return false;
523
+ }
524
+ // The request's own URL carries its Host (the Node server builds it from it).
525
+ const sentTo = [
526
+ new URL(c.req.url).host,
527
+ c.req.header('host'),
528
+ c.req.header('x-forwarded-host')?.split(',')[0]?.trim(),
529
+ ];
530
+ return sentTo.some((h) => h !== undefined && h !== '' && h.toLowerCase() === host.toLowerCase());
531
+ }
532
+
533
+ async function findSession(sessionStore: SessionStoreBinding, token: string) {
534
+ if (sessionStore.resolveToken !== undefined) {
535
+ return sessionStore.resolveToken({ token });
536
+ }
537
+ const sessionIdStr = token.slice(SESSION_TOKEN_PREFIX.length);
538
+ if (sessionIdStr.length === 0) return null;
539
+ return sessionStore.get({
540
+ tenantId: MULTI_TENANT_LOOKUP,
541
+ sessionId: sessionIdStr as unknown as SessionId,
542
+ });
543
+ }
544
+
389
545
  /**
390
546
  * Sentinel `tenantId` value the middleware passes to
391
547
  * `SessionStoreBinding.get` when it needs to look up a session by its
392
- * id without knowing the tenant in advance. Session-store adapters
393
- * MUST honor this by skipping the tenant filter — the tenant is not
394
- * known until the session row has been read.
548
+ * id without knowing the tenant in advance: only for a store without
549
+ * `resolveToken`. Such stores MUST honor it by skipping the tenant
550
+ * filter (the tenant is not known until the session row has been read).
395
551
  */
396
552
  export const MULTI_TENANT_LOOKUP = '__kindgi_session_multi_tenant_lookup__' as unknown as TenantId;
397
553
 
@@ -436,3 +592,13 @@ async function authenticatePublicRunToken(
436
592
  await next();
437
593
  return undefined;
438
594
  }
595
+
596
+ /** `localhost`, `127.0.0.0/8` or `::1`. */
597
+ function isLoopback(hostname: string): boolean {
598
+ return (
599
+ hostname === 'localhost' ||
600
+ hostname === '[::1]' ||
601
+ hostname === '::1' ||
602
+ /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(hostname)
603
+ );
604
+ }
@@ -23,6 +23,8 @@ import {
23
23
  type Action,
24
24
  type AuthzCheckBinding,
25
25
  type Decision,
26
+ OBJECT_ACTIONS,
27
+ type ObjectType,
26
28
  type Principal,
27
29
  type ResourceRef,
28
30
  denyPayload,
@@ -57,9 +59,37 @@ export interface Authorizer {
57
59
  items: readonly T[],
58
60
  refFn: (item: T) => ResourceRef,
59
61
  ) => Promise<T[]>;
62
+ /**
63
+ * The ids of every object of `type` the caller may `action`
64
+ * (`AuthzCheckBinding.listObjects`); `undefined` when the binding
65
+ * can't list, so the caller falls back to `filterByCan`.
66
+ */
67
+ readonly listObjects?: (
68
+ c: Context<AppEnv>,
69
+ action: Action,
70
+ type: ResourceRef['type'],
71
+ ) => Promise<readonly string[] | undefined>;
60
72
  }
61
73
 
62
74
  export function createAuthorizer(binding: AuthzCheckBinding): Authorizer {
75
+ /**
76
+ * Whether a resource is inside the project the caller's API key is
77
+ * limited to (always, for a caller with no such key). The tenant, orgs,
78
+ * teams and projects are held by `keyCeilingDeny`; anything else must
79
+ * belong to the key's project, as the store says (`inProject`). Without
80
+ * that answer, the key reaches no other resource.
81
+ */
82
+ async function inKeyProject(
83
+ c: Context<AppEnv>,
84
+ principal: Principal,
85
+ resource: ResourceRef,
86
+ ): Promise<boolean> {
87
+ const keyProject = c.get('tokenProjectId');
88
+ if (keyProject === undefined || STRUCTURAL.has(resource.type)) return true;
89
+ if (binding.inProject === undefined) return false;
90
+ return binding.inProject(principal, resource, keyProject);
91
+ }
92
+
63
93
  async function checkInternal(
64
94
  c: Context<AppEnv>,
65
95
  action: Action,
@@ -79,6 +109,17 @@ export function createAuthorizer(binding: AuthzCheckBinding): Authorizer {
79
109
  },
80
110
  };
81
111
  }
112
+ const undefinedPair = undefinedRelation(action, resource);
113
+ if (undefinedPair !== undefined) return undefinedPair;
114
+ const ceiling = keyCeilingDeny(c, action, resource);
115
+ if (ceiling !== undefined) return ceiling;
116
+ if (!(await inKeyProject(c, principal, resource))) {
117
+ return keyDecision(
118
+ action,
119
+ resource,
120
+ `the API key is limited to project ${c.get('tokenProjectId')}`,
121
+ );
122
+ }
82
123
  const requestId = c.get('requestId');
83
124
  const ctx =
84
125
  typeof requestId === 'string' && requestId.length > 0
@@ -124,7 +165,19 @@ export function createAuthorizer(binding: AuthzCheckBinding): Authorizer {
124
165
  if (items.length === 0) return [];
125
166
  const principal = c.get('principal') as Principal | undefined;
126
167
  if (principal === undefined) return [];
127
- const refs = items.map(refFn);
168
+ // What the model can't answer, or the key itself rules out, never
169
+ // reaches the store.
170
+ const allowedByKey = await Promise.all(
171
+ items.map(
172
+ async (item) =>
173
+ undefinedRelation(action, refFn(item)) === undefined &&
174
+ keyCeilingDeny(c, action, refFn(item)) === undefined &&
175
+ (await inKeyProject(c, principal, refFn(item))),
176
+ ),
177
+ );
178
+ const open = items.filter((_, i) => allowedByKey[i]);
179
+ if (open.length === 0) return [];
180
+ const refs = open.map(refFn);
128
181
  const requestId = c.get('requestId');
129
182
  const ctx =
130
183
  typeof requestId === 'string' && requestId.length > 0
@@ -132,10 +185,93 @@ export function createAuthorizer(binding: AuthzCheckBinding): Authorizer {
132
185
  : undefined;
133
186
  const decisions = await binding.checkBatch(principal, action, refs, ctx);
134
187
  const out: (typeof items)[number][] = [];
135
- for (let i = 0; i < items.length; i++) {
136
- if (decisions[i]?.allowed) out.push(items[i] as (typeof items)[number]);
188
+ for (let i = 0; i < open.length; i++) {
189
+ if (decisions[i]?.allowed) out.push(open[i] as (typeof items)[number]);
137
190
  }
138
191
  return out;
139
192
  },
193
+ async listObjects(c, action, type) {
194
+ if (binding.listObjects === undefined) return undefined;
195
+ const principal = c.get('principal') as Principal | undefined;
196
+ if (principal === undefined) return [];
197
+ const requestId = c.get('requestId');
198
+ const ctx =
199
+ typeof requestId === 'string' && requestId.length > 0
200
+ ? { correlationId: requestId }
201
+ : undefined;
202
+ return binding.listObjects(principal, action, type, ctx);
203
+ },
140
204
  };
141
205
  }
206
+
207
+ /**
208
+ * A check must name a relation the authorization model defines for the
209
+ * resource's type (`OBJECT_ACTIONS`, kept equal to the runtime's model by
210
+ * its tests). Any other pair is a bug in the route: the PDP rejects it
211
+ * for everyone, the seed admin included, and only with authorization on,
212
+ * so it's refused here, naming the pair, and logged.
213
+ */
214
+ function undefinedRelation(action: Action, resource: ResourceRef): Decision | undefined {
215
+ const defined = OBJECT_ACTIONS[resource.type as ObjectType] as readonly Action[] | undefined;
216
+ if (defined?.includes(action) === true) return undefined;
217
+ const reason = `the authorization model has no \`${action}\` on \`${resource.type}\` (a check that can never pass: a bug in the route)`;
218
+ console.error(`[authz] ${reason}`);
219
+ return {
220
+ allowed: false,
221
+ failing: 'invalid-action',
222
+ reason,
223
+ evidence: {
224
+ action,
225
+ relation: '',
226
+ resource: `${resource.type}:${resource.id}`,
227
+ actorSubject: '',
228
+ },
229
+ };
230
+ }
231
+
232
+ /** Types a key's project limit is checked on directly, not through `inProject`. */
233
+ const STRUCTURAL: ReadonlySet<string> = new Set(['tenant', 'org', 'team', 'project']);
234
+
235
+ function keyDecision(action: Action, resource: ResourceRef, reason: string): Decision {
236
+ return {
237
+ allowed: false,
238
+ failing: 'scope',
239
+ reason,
240
+ evidence: {
241
+ action,
242
+ relation: '',
243
+ resource: `${resource.type}:${resource.id}`,
244
+ actorSubject: '',
245
+ },
246
+ };
247
+ }
248
+
249
+ /**
250
+ * What an API key itself rules out, before the principal's grants are
251
+ * asked: a key can do less than its principal, never more.
252
+ *
253
+ * - A `member` key takes no `admin` action on the tenant, even for a
254
+ * principal who is a tenant admin: a day-to-day key can't change keys,
255
+ * grants or policies. Below the tenant, its principal's roles hold: a
256
+ * project admin's member key administers that project.
257
+ * - A key limited to a project acts on no other project, and takes no
258
+ * `admin` action on the tenant, an org or a team. Other resources must
259
+ * be in its project (`inKeyProject`).
260
+ */
261
+ function keyCeilingDeny(
262
+ c: Context<AppEnv>,
263
+ action: Action,
264
+ resource: ResourceRef,
265
+ ): Decision | undefined {
266
+ if (action === 'admin' && resource.type === 'tenant' && c.get('tokenRole') === 'member') {
267
+ return keyDecision(action, resource, 'a member API key takes no admin action on the tenant');
268
+ }
269
+ const keyProject = c.get('tokenProjectId');
270
+ if (keyProject === undefined) return undefined;
271
+ const otherProject = resource.type === 'project' && resource.id !== keyProject;
272
+ const aboveIt =
273
+ resource.type !== 'project' && STRUCTURAL.has(resource.type) && action === 'admin';
274
+ return otherProject || aboveIt
275
+ ? keyDecision(action, resource, `the API key is limited to project ${keyProject}`)
276
+ : undefined;
277
+ }
@@ -22,6 +22,11 @@ import { statusFor, toWireError } from '../errors.js';
22
22
  *
23
23
  * The requestId embedded in the response body matches
24
24
  * `middleware/request-id.ts`, so a caller can correlate.
25
+ *
26
+ * A failed database query's message carries its SQL and its parameters
27
+ * (Drizzle's `Failed query: … params: …`), which can be a person's email or
28
+ * address: the body says only that a query failed, and the log keeps the
29
+ * whole error.
25
30
  */
26
31
  export function mapThrownError(cause: Error, c: Context): Response {
27
32
  if ('getResponse' in cause && typeof cause.getResponse === 'function') {
@@ -34,10 +39,19 @@ export function mapThrownError(cause: Error, c: Context): Response {
34
39
  const body = toWireError(
35
40
  {
36
41
  code: 'internal-server-error',
37
- message: cause.message.length > 0 ? cause.message : 'Unhandled server error',
42
+ message: isQueryError(cause)
43
+ ? 'A database query failed.'
44
+ : cause.message.length > 0
45
+ ? cause.message
46
+ : 'Unhandled server error',
38
47
  },
39
48
  requestId,
40
49
  );
41
50
  c.status(statusFor('internal-server-error') as never);
42
51
  return c.json(body);
43
52
  }
53
+
54
+ /** Drizzle's wrapper around a failed query, whose message is the SQL and its values. */
55
+ function isQueryError(cause: Error): boolean {
56
+ return cause.name === 'DrizzleQueryError' || cause.message.startsWith('Failed query: ');
57
+ }