@kindgi/api 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (516) hide show
  1. package/dist/agent-binding.d.ts +13 -0
  2. package/dist/agent-binding.d.ts.map +1 -1
  3. package/dist/agent-pins.d.ts.map +1 -1
  4. package/dist/agent-pins.js +18 -2
  5. package/dist/agent-pins.js.map +1 -1
  6. package/dist/app.d.ts +110 -17
  7. package/dist/app.d.ts.map +1 -1
  8. package/dist/app.js +163 -35
  9. package/dist/app.js.map +1 -1
  10. package/dist/block-publish.d.ts +56 -0
  11. package/dist/block-publish.d.ts.map +1 -0
  12. package/dist/block-publish.js +132 -0
  13. package/dist/block-publish.js.map +1 -0
  14. package/dist/caller.d.ts +24 -0
  15. package/dist/caller.d.ts.map +1 -0
  16. package/dist/caller.js +47 -0
  17. package/dist/caller.js.map +1 -0
  18. package/dist/capability-binding.d.ts +11 -0
  19. package/dist/capability-binding.d.ts.map +1 -1
  20. package/dist/cost-binding.d.ts +19 -0
  21. package/dist/cost-binding.d.ts.map +1 -1
  22. package/dist/cost-binding.js.map +1 -1
  23. package/dist/derive-agent-version.d.ts +7 -0
  24. package/dist/derive-agent-version.d.ts.map +1 -1
  25. package/dist/derive-agent-version.js +4 -0
  26. package/dist/derive-agent-version.js.map +1 -1
  27. package/dist/errors.d.ts.map +1 -1
  28. package/dist/errors.js +75 -7
  29. package/dist/errors.js.map +1 -1
  30. package/dist/eval-case-binding.d.ts +16 -4
  31. package/dist/eval-case-binding.d.ts.map +1 -1
  32. package/dist/eval-run-binding.d.ts +31 -0
  33. package/dist/eval-run-binding.d.ts.map +1 -1
  34. package/dist/eval-run-dispatcher.d.ts +7 -0
  35. package/dist/eval-run-dispatcher.d.ts.map +1 -1
  36. package/dist/eval-run-dispatcher.js.map +1 -1
  37. package/dist/eval-sample.d.ts +11 -0
  38. package/dist/eval-sample.d.ts.map +1 -0
  39. package/dist/eval-sample.js +34 -0
  40. package/dist/eval-sample.js.map +1 -0
  41. package/dist/eval-suite-binding.d.ts +13 -0
  42. package/dist/eval-suite-binding.d.ts.map +1 -1
  43. package/dist/eval-suite-binding.js.map +1 -1
  44. package/dist/flow-binding.d.ts +18 -0
  45. package/dist/flow-binding.d.ts.map +1 -1
  46. package/dist/flow-pins.d.ts.map +1 -1
  47. package/dist/flow-pins.js +18 -2
  48. package/dist/flow-pins.js.map +1 -1
  49. package/dist/gate.d.ts.map +1 -1
  50. package/dist/gate.js +25 -0
  51. package/dist/gate.js.map +1 -1
  52. package/dist/handler-binding.d.ts +32 -0
  53. package/dist/handler-binding.d.ts.map +1 -1
  54. package/dist/hitl-binding.d.ts +23 -0
  55. package/dist/hitl-binding.d.ts.map +1 -1
  56. package/dist/identity-directory-binding.d.ts +82 -4
  57. package/dist/identity-directory-binding.d.ts.map +1 -1
  58. package/dist/identity-provider-binding.d.ts +197 -17
  59. package/dist/identity-provider-binding.d.ts.map +1 -1
  60. package/dist/improvement-pass-binding.d.ts +196 -0
  61. package/dist/improvement-pass-binding.d.ts.map +1 -0
  62. package/dist/improvement-pass-binding.js +9 -0
  63. package/dist/improvement-pass-binding.js.map +1 -0
  64. package/dist/index.d.ts +35 -17
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +11 -3
  67. package/dist/index.js.map +1 -1
  68. package/dist/judged-dispatcher.d.ts +25 -4
  69. package/dist/judged-dispatcher.d.ts.map +1 -1
  70. package/dist/judged-dispatcher.js +43 -6
  71. package/dist/judged-dispatcher.js.map +1 -1
  72. package/dist/judgment-binding.d.ts +25 -1
  73. package/dist/judgment-binding.d.ts.map +1 -1
  74. package/dist/mcp-endpoint-binding.d.ts +7 -0
  75. package/dist/mcp-endpoint-binding.d.ts.map +1 -1
  76. package/dist/memory-binding.d.ts +124 -17
  77. package/dist/memory-binding.d.ts.map +1 -1
  78. package/dist/memory-erasure-binding.d.ts +145 -0
  79. package/dist/memory-erasure-binding.d.ts.map +1 -0
  80. package/dist/memory-erasure-binding.js +4 -0
  81. package/dist/memory-erasure-binding.js.map +1 -0
  82. package/dist/middleware/auth.d.ts +66 -13
  83. package/dist/middleware/auth.d.ts.map +1 -1
  84. package/dist/middleware/auth.js +150 -50
  85. package/dist/middleware/auth.js.map +1 -1
  86. package/dist/middleware/authorize.d.ts +9 -0
  87. package/dist/middleware/authorize.d.ts.map +1 -1
  88. package/dist/middleware/authorize.js +122 -4
  89. package/dist/middleware/authorize.js.map +1 -1
  90. package/dist/middleware/error-mapper.d.ts +5 -0
  91. package/dist/middleware/error-mapper.d.ts.map +1 -1
  92. package/dist/middleware/error-mapper.js +14 -1
  93. package/dist/middleware/error-mapper.js.map +1 -1
  94. package/dist/middleware/idempotency.d.ts +70 -5
  95. package/dist/middleware/idempotency.d.ts.map +1 -1
  96. package/dist/middleware/idempotency.js +174 -41
  97. package/dist/middleware/idempotency.js.map +1 -1
  98. package/dist/middleware/key-project.d.ts +17 -0
  99. package/dist/middleware/key-project.d.ts.map +1 -0
  100. package/dist/middleware/key-project.js +75 -0
  101. package/dist/middleware/key-project.js.map +1 -0
  102. package/dist/middleware/principal.d.ts.map +1 -1
  103. package/dist/middleware/principal.js +9 -5
  104. package/dist/middleware/principal.js.map +1 -1
  105. package/dist/middleware/project-ref.d.ts.map +1 -1
  106. package/dist/middleware/project-ref.js +3 -0
  107. package/dist/middleware/project-ref.js.map +1 -1
  108. package/dist/openapi/generate.d.ts.map +1 -1
  109. package/dist/openapi/generate.js +5 -2
  110. package/dist/openapi/generate.js.map +1 -1
  111. package/dist/openapi/operations.d.ts.map +1 -1
  112. package/dist/openapi/operations.js +919 -140
  113. package/dist/openapi/operations.js.map +1 -1
  114. package/dist/openapi/schemas.d.ts +90 -13
  115. package/dist/openapi/schemas.d.ts.map +1 -1
  116. package/dist/openapi/schemas.js +2081 -359
  117. package/dist/openapi/schemas.js.map +1 -1
  118. package/dist/person-grants-binding.d.ts +77 -0
  119. package/dist/person-grants-binding.d.ts.map +1 -0
  120. package/dist/person-grants-binding.js +4 -0
  121. package/dist/person-grants-binding.js.map +1 -0
  122. package/dist/proposal-service.d.ts +121 -0
  123. package/dist/proposal-service.d.ts.map +1 -0
  124. package/dist/proposal-service.js +447 -0
  125. package/dist/proposal-service.js.map +1 -0
  126. package/dist/proposal-status.d.ts +60 -0
  127. package/dist/proposal-status.d.ts.map +1 -0
  128. package/dist/proposal-status.js +109 -0
  129. package/dist/proposal-status.js.map +1 -0
  130. package/dist/provider-binding.d.ts +10 -0
  131. package/dist/provider-binding.d.ts.map +1 -1
  132. package/dist/publish-refused.d.ts +28 -3
  133. package/dist/publish-refused.d.ts.map +1 -1
  134. package/dist/publish-refused.js +24 -3
  135. package/dist/publish-refused.js.map +1 -1
  136. package/dist/routes/adapters.d.ts +2 -1
  137. package/dist/routes/adapters.d.ts.map +1 -1
  138. package/dist/routes/adapters.js +3 -1
  139. package/dist/routes/adapters.js.map +1 -1
  140. package/dist/routes/agent-releases.d.ts +55 -3
  141. package/dist/routes/agent-releases.d.ts.map +1 -1
  142. package/dist/routes/agent-releases.js +85 -55
  143. package/dist/routes/agent-releases.js.map +1 -1
  144. package/dist/routes/agents.d.ts +12 -1
  145. package/dist/routes/agents.d.ts.map +1 -1
  146. package/dist/routes/agents.js +100 -2
  147. package/dist/routes/agents.js.map +1 -1
  148. package/dist/routes/approvals.d.ts +15 -3
  149. package/dist/routes/approvals.d.ts.map +1 -1
  150. package/dist/routes/approvals.js +133 -122
  151. package/dist/routes/approvals.js.map +1 -1
  152. package/dist/routes/artifacts.d.ts +25 -3
  153. package/dist/routes/artifacts.d.ts.map +1 -1
  154. package/dist/routes/artifacts.js +116 -7
  155. package/dist/routes/artifacts.js.map +1 -1
  156. package/dist/routes/auth.d.ts +25 -1
  157. package/dist/routes/auth.d.ts.map +1 -1
  158. package/dist/routes/auth.js +655 -281
  159. package/dist/routes/auth.js.map +1 -1
  160. package/dist/routes/blocks.d.ts.map +1 -1
  161. package/dist/routes/blocks.js +2 -42
  162. package/dist/routes/blocks.js.map +1 -1
  163. package/dist/routes/capabilities.d.ts +2 -1
  164. package/dist/routes/capabilities.d.ts.map +1 -1
  165. package/dist/routes/capabilities.js +6 -1
  166. package/dist/routes/capabilities.js.map +1 -1
  167. package/dist/routes/compliance.d.ts +9 -10
  168. package/dist/routes/compliance.d.ts.map +1 -1
  169. package/dist/routes/compliance.js +42 -61
  170. package/dist/routes/compliance.js.map +1 -1
  171. package/dist/routes/conversations.d.ts +8 -1
  172. package/dist/routes/conversations.d.ts.map +1 -1
  173. package/dist/routes/conversations.js +87 -8
  174. package/dist/routes/conversations.js.map +1 -1
  175. package/dist/routes/cost.d.ts +12 -1
  176. package/dist/routes/cost.d.ts.map +1 -1
  177. package/dist/routes/cost.js +74 -3
  178. package/dist/routes/cost.js.map +1 -1
  179. package/dist/routes/denied.d.ts +11 -0
  180. package/dist/routes/denied.d.ts.map +1 -0
  181. package/dist/routes/denied.js +17 -0
  182. package/dist/routes/denied.js.map +1 -0
  183. package/dist/routes/deployments.d.ts +10 -1
  184. package/dist/routes/deployments.d.ts.map +1 -1
  185. package/dist/routes/deployments.js +171 -13
  186. package/dist/routes/deployments.js.map +1 -1
  187. package/dist/routes/eval-comparison.d.ts.map +1 -1
  188. package/dist/routes/eval-comparison.js +69 -1
  189. package/dist/routes/eval-comparison.js.map +1 -1
  190. package/dist/routes/eval-overrides.d.ts +18 -0
  191. package/dist/routes/eval-overrides.d.ts.map +1 -0
  192. package/dist/routes/eval-overrides.js +81 -0
  193. package/dist/routes/eval-overrides.js.map +1 -0
  194. package/dist/routes/eval-runs.d.ts +10 -1
  195. package/dist/routes/eval-runs.d.ts.map +1 -1
  196. package/dist/routes/eval-runs.js +66 -7
  197. package/dist/routes/eval-runs.js.map +1 -1
  198. package/dist/routes/eval-suites.d.ts.map +1 -1
  199. package/dist/routes/eval-suites.js +28 -2
  200. package/dist/routes/eval-suites.js.map +1 -1
  201. package/dist/routes/event-triggers.d.ts +5 -1
  202. package/dist/routes/event-triggers.d.ts.map +1 -1
  203. package/dist/routes/event-triggers.js +26 -3
  204. package/dist/routes/event-triggers.js.map +1 -1
  205. package/dist/routes/export-signing-keys.d.ts +12 -0
  206. package/dist/routes/export-signing-keys.d.ts.map +1 -0
  207. package/dist/routes/export-signing-keys.js +24 -0
  208. package/dist/routes/export-signing-keys.js.map +1 -0
  209. package/dist/routes/flows.d.ts.map +1 -1
  210. package/dist/routes/flows.js +9 -1
  211. package/dist/routes/flows.js.map +1 -1
  212. package/dist/routes/gate-policies.d.ts +4 -1
  213. package/dist/routes/gate-policies.d.ts.map +1 -1
  214. package/dist/routes/gate-policies.js +29 -5
  215. package/dist/routes/gate-policies.js.map +1 -1
  216. package/dist/routes/guardrails.d.ts +16 -14
  217. package/dist/routes/guardrails.d.ts.map +1 -1
  218. package/dist/routes/guardrails.js +39 -3
  219. package/dist/routes/guardrails.js.map +1 -1
  220. package/dist/routes/identity.d.ts +20 -4
  221. package/dist/routes/identity.d.ts.map +1 -1
  222. package/dist/routes/identity.js +246 -6
  223. package/dist/routes/identity.js.map +1 -1
  224. package/dist/routes/improvement-passes.d.ts +52 -0
  225. package/dist/routes/improvement-passes.d.ts.map +1 -0
  226. package/dist/routes/improvement-passes.js +449 -0
  227. package/dist/routes/improvement-passes.js.map +1 -0
  228. package/dist/routes/judged-suites.d.ts +50 -0
  229. package/dist/routes/judged-suites.d.ts.map +1 -1
  230. package/dist/routes/judged-suites.js +66 -27
  231. package/dist/routes/judged-suites.js.map +1 -1
  232. package/dist/routes/judgment-context.d.ts +18 -2
  233. package/dist/routes/judgment-context.d.ts.map +1 -1
  234. package/dist/routes/judgment-context.js +63 -5
  235. package/dist/routes/judgment-context.js.map +1 -1
  236. package/dist/routes/judgment-flow-context.d.ts.map +1 -1
  237. package/dist/routes/judgment-flow-context.js +14 -2
  238. package/dist/routes/judgment-flow-context.js.map +1 -1
  239. package/dist/routes/judgments.d.ts.map +1 -1
  240. package/dist/routes/judgments.js +8 -1
  241. package/dist/routes/judgments.js.map +1 -1
  242. package/dist/routes/mcp.d.ts.map +1 -1
  243. package/dist/routes/mcp.js +26 -1
  244. package/dist/routes/mcp.js.map +1 -1
  245. package/dist/routes/memory-access.d.ts +43 -0
  246. package/dist/routes/memory-access.d.ts.map +1 -0
  247. package/dist/routes/memory-access.js +114 -0
  248. package/dist/routes/memory-access.js.map +1 -0
  249. package/dist/routes/memory-erasures.d.ts +25 -0
  250. package/dist/routes/memory-erasures.d.ts.map +1 -0
  251. package/dist/routes/memory-erasures.js +214 -0
  252. package/dist/routes/memory-erasures.js.map +1 -0
  253. package/dist/routes/memory-parse.d.ts +38 -0
  254. package/dist/routes/memory-parse.d.ts.map +1 -0
  255. package/dist/routes/memory-parse.js +199 -0
  256. package/dist/routes/memory-parse.js.map +1 -0
  257. package/dist/routes/memory.d.ts +9 -4
  258. package/dist/routes/memory.d.ts.map +1 -1
  259. package/dist/routes/memory.js +226 -234
  260. package/dist/routes/memory.js.map +1 -1
  261. package/dist/routes/observations.d.ts +4 -1
  262. package/dist/routes/observations.d.ts.map +1 -1
  263. package/dist/routes/observations.js +8 -2
  264. package/dist/routes/observations.js.map +1 -1
  265. package/dist/routes/pagination.d.ts +7 -0
  266. package/dist/routes/pagination.d.ts.map +1 -1
  267. package/dist/routes/pagination.js +10 -0
  268. package/dist/routes/pagination.js.map +1 -1
  269. package/dist/routes/policies.d.ts +2 -1
  270. package/dist/routes/policies.d.ts.map +1 -1
  271. package/dist/routes/policies.js +3 -1
  272. package/dist/routes/policies.js.map +1 -1
  273. package/dist/routes/project-mismatch.d.ts +22 -0
  274. package/dist/routes/project-mismatch.d.ts.map +1 -0
  275. package/dist/routes/project-mismatch.js +34 -0
  276. package/dist/routes/project-mismatch.js.map +1 -0
  277. package/dist/routes/projects.d.ts +15 -4
  278. package/dist/routes/projects.d.ts.map +1 -1
  279. package/dist/routes/projects.js +100 -9
  280. package/dist/routes/projects.js.map +1 -1
  281. package/dist/routes/proposals.d.ts +25 -17
  282. package/dist/routes/proposals.d.ts.map +1 -1
  283. package/dist/routes/proposals.js +340 -507
  284. package/dist/routes/proposals.js.map +1 -1
  285. package/dist/routes/provenance.d.ts +17 -3
  286. package/dist/routes/provenance.d.ts.map +1 -1
  287. package/dist/routes/provenance.js +79 -103
  288. package/dist/routes/provenance.js.map +1 -1
  289. package/dist/routes/providers.d.ts +17 -1
  290. package/dist/routes/providers.d.ts.map +1 -1
  291. package/dist/routes/providers.js +110 -3
  292. package/dist/routes/providers.js.map +1 -1
  293. package/dist/routes/public-run-tokens.d.ts.map +1 -1
  294. package/dist/routes/public-run-tokens.js +3 -0
  295. package/dist/routes/public-run-tokens.js.map +1 -1
  296. package/dist/routes/readable-projects.d.ts +26 -0
  297. package/dist/routes/readable-projects.d.ts.map +1 -0
  298. package/dist/routes/readable-projects.js +39 -0
  299. package/dist/routes/readable-projects.js.map +1 -0
  300. package/dist/routes/runs.d.ts +10 -0
  301. package/dist/routes/runs.d.ts.map +1 -1
  302. package/dist/routes/runs.js +92 -42
  303. package/dist/routes/runs.js.map +1 -1
  304. package/dist/routes/schedules.d.ts +21 -13
  305. package/dist/routes/schedules.d.ts.map +1 -1
  306. package/dist/routes/schedules.js +416 -55
  307. package/dist/routes/schedules.js.map +1 -1
  308. package/dist/routes/segments.d.ts +2 -0
  309. package/dist/routes/segments.d.ts.map +1 -1
  310. package/dist/routes/segments.js +5 -0
  311. package/dist/routes/segments.js.map +1 -1
  312. package/dist/routes/service-accounts.d.ts +17 -0
  313. package/dist/routes/service-accounts.d.ts.map +1 -0
  314. package/dist/routes/service-accounts.js +212 -0
  315. package/dist/routes/service-accounts.js.map +1 -0
  316. package/dist/routes/sign-in-options.d.ts +50 -0
  317. package/dist/routes/sign-in-options.d.ts.map +1 -0
  318. package/dist/routes/sign-in-options.js +98 -0
  319. package/dist/routes/sign-in-options.js.map +1 -0
  320. package/dist/routes/signing-keys.d.ts +2 -1
  321. package/dist/routes/signing-keys.d.ts.map +1 -1
  322. package/dist/routes/signing-keys.js +3 -1
  323. package/dist/routes/signing-keys.js.map +1 -1
  324. package/dist/routes/tenant-access.d.ts +13 -0
  325. package/dist/routes/tenant-access.d.ts.map +1 -0
  326. package/dist/routes/tenant-access.js +26 -0
  327. package/dist/routes/tenant-access.js.map +1 -0
  328. package/dist/routes/tenant.d.ts.map +1 -1
  329. package/dist/routes/tenant.js +4 -0
  330. package/dist/routes/tenant.js.map +1 -1
  331. package/dist/routes/token-sign-in.d.ts +36 -0
  332. package/dist/routes/token-sign-in.d.ts.map +1 -0
  333. package/dist/routes/token-sign-in.js +101 -0
  334. package/dist/routes/token-sign-in.js.map +1 -0
  335. package/dist/routes/tokens.d.ts +15 -5
  336. package/dist/routes/tokens.d.ts.map +1 -1
  337. package/dist/routes/tokens.js +162 -43
  338. package/dist/routes/tokens.js.map +1 -1
  339. package/dist/routes/tools.d.ts.map +1 -1
  340. package/dist/routes/tools.js +9 -1
  341. package/dist/routes/tools.js.map +1 -1
  342. package/dist/routes/trigger-access.d.ts +29 -0
  343. package/dist/routes/trigger-access.d.ts.map +1 -0
  344. package/dist/routes/trigger-access.js +33 -0
  345. package/dist/routes/trigger-access.js.map +1 -0
  346. package/dist/routes/webhook-endpoints.d.ts +2 -1
  347. package/dist/routes/webhook-endpoints.d.ts.map +1 -1
  348. package/dist/routes/webhook-endpoints.js +11 -3
  349. package/dist/routes/webhook-endpoints.js.map +1 -1
  350. package/dist/routes/webhooks.d.ts +5 -1
  351. package/dist/routes/webhooks.d.ts.map +1 -1
  352. package/dist/routes/webhooks.js +25 -2
  353. package/dist/routes/webhooks.js.map +1 -1
  354. package/dist/run-failure.d.ts +21 -0
  355. package/dist/run-failure.d.ts.map +1 -0
  356. package/dist/run-failure.js +29 -0
  357. package/dist/run-failure.js.map +1 -0
  358. package/dist/service-account-binding.d.ts +108 -0
  359. package/dist/service-account-binding.d.ts.map +1 -0
  360. package/dist/service-account-binding.js +4 -0
  361. package/dist/service-account-binding.js.map +1 -0
  362. package/dist/session-store-binding.d.ts +61 -13
  363. package/dist/session-store-binding.d.ts.map +1 -1
  364. package/dist/signed-export.d.ts +89 -0
  365. package/dist/signed-export.d.ts.map +1 -0
  366. package/dist/signed-export.js +149 -0
  367. package/dist/signed-export.js.map +1 -0
  368. package/dist/supervisor-binding.d.ts +186 -413
  369. package/dist/supervisor-binding.d.ts.map +1 -1
  370. package/dist/testing/app-bindings.d.ts +33 -0
  371. package/dist/testing/app-bindings.d.ts.map +1 -0
  372. package/dist/testing/app-bindings.js +112 -0
  373. package/dist/testing/app-bindings.js.map +1 -0
  374. package/dist/testing/index.d.ts +11 -0
  375. package/dist/testing/index.d.ts.map +1 -0
  376. package/dist/testing/index.js +13 -0
  377. package/dist/testing/index.js.map +1 -0
  378. package/dist/testing/stub-binding.d.ts +34 -0
  379. package/dist/testing/stub-binding.d.ts.map +1 -0
  380. package/dist/testing/stub-binding.js +42 -0
  381. package/dist/testing/stub-binding.js.map +1 -0
  382. package/dist/testing/trigger-registry.d.ts +21 -0
  383. package/dist/testing/trigger-registry.d.ts.map +1 -0
  384. package/dist/testing/trigger-registry.js +206 -0
  385. package/dist/testing/trigger-registry.js.map +1 -0
  386. package/dist/token-admin.d.ts +46 -8
  387. package/dist/token-admin.d.ts.map +1 -1
  388. package/dist/token-admin.js.map +1 -1
  389. package/dist/tool-binding.d.ts +13 -0
  390. package/dist/tool-binding.d.ts.map +1 -1
  391. package/dist/trigger-binding.d.ts +2 -2
  392. package/dist/trigger-binding.d.ts.map +1 -1
  393. package/dist/trigger-binding.js +1 -1
  394. package/dist/trigger-binding.js.map +1 -1
  395. package/dist/types.d.ts +16 -0
  396. package/dist/types.d.ts.map +1 -1
  397. package/dist/webhook-endpoint-binding.d.ts +19 -3
  398. package/dist/webhook-endpoint-binding.d.ts.map +1 -1
  399. package/dist/webhook-endpoint-binding.js +1 -1
  400. package/dist/webhook-endpoint-binding.js.map +1 -1
  401. package/openapi.json +15387 -8789
  402. package/package.json +26 -22
  403. package/src/agent-binding.ts +14 -0
  404. package/src/agent-pins.ts +18 -2
  405. package/src/app.ts +366 -80
  406. package/src/block-publish.ts +181 -0
  407. package/src/caller.ts +62 -0
  408. package/src/capability-binding.ts +12 -0
  409. package/src/cost-binding.ts +19 -0
  410. package/src/derive-agent-version.ts +9 -1
  411. package/src/errors.ts +75 -7
  412. package/src/eval-case-binding.ts +17 -1
  413. package/src/eval-run-binding.ts +31 -0
  414. package/src/eval-run-dispatcher.ts +7 -0
  415. package/src/eval-sample.ts +42 -0
  416. package/src/eval-suite-binding.ts +14 -0
  417. package/src/flow-binding.ts +19 -0
  418. package/src/flow-pins.ts +18 -2
  419. package/src/gate.ts +26 -0
  420. package/src/handler-binding.ts +36 -1
  421. package/src/hitl-binding.ts +24 -0
  422. package/src/identity-directory-binding.ts +85 -4
  423. package/src/identity-provider-binding.ts +216 -21
  424. package/src/improvement-pass-binding.ts +193 -0
  425. package/src/index.ts +153 -31
  426. package/src/judged-dispatcher.ts +78 -11
  427. package/src/judgment-binding.ts +25 -1
  428. package/src/mcp-endpoint-binding.ts +7 -0
  429. package/src/memory-binding.ts +121 -17
  430. package/src/memory-erasure-binding.ts +156 -0
  431. package/src/middleware/auth.ts +228 -62
  432. package/src/middleware/authorize.ts +155 -3
  433. package/src/middleware/error-mapper.ts +15 -1
  434. package/src/middleware/idempotency.ts +230 -43
  435. package/src/middleware/key-project.ts +86 -0
  436. package/src/middleware/principal.ts +9 -5
  437. package/src/middleware/project-ref.ts +3 -0
  438. package/src/openapi/generate.ts +11 -2
  439. package/src/openapi/operations.ts +1060 -143
  440. package/src/openapi/schemas.ts +2387 -469
  441. package/src/person-grants-binding.ts +76 -0
  442. package/src/proposal-service.ts +640 -0
  443. package/src/proposal-status.ts +176 -0
  444. package/src/provider-binding.ts +10 -0
  445. package/src/publish-refused.ts +56 -9
  446. package/src/routes/adapters.ts +4 -0
  447. package/src/routes/agent-releases.ts +121 -69
  448. package/src/routes/agents.ts +128 -1
  449. package/src/routes/approvals.ts +164 -154
  450. package/src/routes/artifacts.ts +181 -11
  451. package/src/routes/auth.ts +804 -336
  452. package/src/routes/blocks.ts +2 -53
  453. package/src/routes/capabilities.ts +10 -1
  454. package/src/routes/compliance.ts +60 -99
  455. package/src/routes/conversations.ts +101 -7
  456. package/src/routes/cost.ts +110 -4
  457. package/src/routes/denied.ts +28 -0
  458. package/src/routes/deployments.ts +210 -17
  459. package/src/routes/eval-comparison.ts +81 -2
  460. package/src/routes/eval-overrides.ts +108 -0
  461. package/src/routes/eval-runs.ts +89 -6
  462. package/src/routes/eval-suites.ts +32 -2
  463. package/src/routes/event-triggers.ts +27 -3
  464. package/src/routes/export-signing-keys.ts +31 -0
  465. package/src/routes/flows.ts +12 -1
  466. package/src/routes/gate-policies.ts +37 -6
  467. package/src/routes/guardrails.ts +61 -2
  468. package/src/routes/identity.ts +328 -10
  469. package/src/routes/improvement-passes.ts +541 -0
  470. package/src/routes/judged-suites.ts +113 -43
  471. package/src/routes/judgment-context.ts +76 -5
  472. package/src/routes/judgment-flow-context.ts +16 -2
  473. package/src/routes/judgments.ts +17 -3
  474. package/src/routes/mcp.ts +29 -1
  475. package/src/routes/memory-access.ts +160 -0
  476. package/src/routes/memory-erasures.ts +240 -0
  477. package/src/routes/memory-parse.ts +250 -0
  478. package/src/routes/memory.ts +244 -286
  479. package/src/routes/observations.ts +15 -2
  480. package/src/routes/pagination.ts +12 -0
  481. package/src/routes/policies.ts +7 -1
  482. package/src/routes/project-mismatch.ts +56 -0
  483. package/src/routes/projects.ts +107 -13
  484. package/src/routes/proposals.ts +370 -611
  485. package/src/routes/provenance.ts +99 -133
  486. package/src/routes/providers.ts +142 -3
  487. package/src/routes/public-run-tokens.ts +3 -0
  488. package/src/routes/readable-projects.ts +66 -0
  489. package/src/routes/runs.ts +119 -39
  490. package/src/routes/schedules.ts +472 -60
  491. package/src/routes/segments.ts +11 -0
  492. package/src/routes/service-accounts.ts +262 -0
  493. package/src/routes/sign-in-options.ts +168 -0
  494. package/src/routes/signing-keys.ts +7 -1
  495. package/src/routes/tenant-access.ts +37 -0
  496. package/src/routes/tenant.ts +6 -0
  497. package/src/routes/token-sign-in.ts +161 -0
  498. package/src/routes/tokens.ts +172 -46
  499. package/src/routes/tools.ts +12 -1
  500. package/src/routes/trigger-access.ts +72 -0
  501. package/src/routes/webhook-endpoints.ts +15 -3
  502. package/src/routes/webhooks.ts +26 -2
  503. package/src/run-failure.ts +42 -0
  504. package/src/service-account-binding.ts +113 -0
  505. package/src/session-store-binding.ts +65 -13
  506. package/src/signed-export.ts +250 -0
  507. package/src/supervisor-binding.ts +169 -409
  508. package/src/testing/app-bindings.ts +142 -0
  509. package/src/testing/index.ts +21 -0
  510. package/src/testing/stub-binding.ts +53 -0
  511. package/src/testing/trigger-registry.ts +259 -0
  512. package/src/token-admin.ts +40 -8
  513. package/src/tool-binding.ts +14 -0
  514. package/src/trigger-binding.ts +8 -1
  515. package/src/types.ts +16 -0
  516. package/src/webhook-endpoint-binding.ts +18 -3
@@ -4,7 +4,13 @@
4
4
  import { Hono } from 'hono';
5
5
 
6
6
  import { type Principal, ref, tuplesForCreate } from '@kindgi/authz';
7
- import { type Guardrail, validateGuardrailSpec } from '@kindgi/guardrails';
7
+ import {
8
+ type Guardrail,
9
+ type GuardrailConfigProblem,
10
+ createCheckRegistry,
11
+ describeGuardrailConfigProblems,
12
+ validateGuardrailSpec,
13
+ } from '@kindgi/guardrails';
8
14
  import type { Cursor, GuardrailId, ProjectId, TenantId, UserId } from '@kindgi/types';
9
15
 
10
16
  import { statusFor, toWireError } from '../errors.js';
@@ -28,6 +34,9 @@ import { parseScopeParams } from './scope-params.js';
28
34
  * guardrails whose check code is already bundled server-side. Check
29
35
  * code is not uploaded over this surface.
30
36
  */
37
+ /** The built-in checks, for the config of a guardrail naming one. */
38
+ const builtInChecks = createCheckRegistry();
39
+
31
40
  /**
32
41
  * Optional side-effect callback fired after a successful write
33
42
  * (register, unregister) to a guardrail. Lets an in-process
@@ -42,10 +51,26 @@ export type GuardrailWriteHook = (params: {
42
51
  readonly kind: 'register' | 'unregister';
43
52
  }) => void | Promise<void>;
44
53
 
54
+ /**
55
+ * Checks a guardrail being registered against the check it names: every
56
+ * way its `config` breaks that check's `configSchema` (a pack check's,
57
+ * from its deployment), via `guardrailConfigProblems`. Any problem
58
+ * refuses the registration with `422 guardrail-config-invalid`
59
+ * (`details.issues`). Absent, or nothing to check against (an unknown
60
+ * check, a check without a schema): no problems. A guardrail naming a
61
+ * built-in check is checked by the route itself, against the built-in's
62
+ * schema, whatever the runtime passes here.
63
+ */
64
+ export type GuardrailConfigCheck = (input: {
65
+ readonly tenantId: TenantId;
66
+ readonly guardrail: Guardrail;
67
+ }) => Promise<readonly GuardrailConfigProblem[]>;
68
+
45
69
  export function guardrailsRouter(
46
70
  binding: GuardrailRegistryBinding,
47
71
  authorizer?: Authorizer,
48
72
  onWrite?: GuardrailWriteHook,
73
+ checkConfig?: GuardrailConfigCheck,
49
74
  ): Hono<AppEnv> {
50
75
  const r = new Hono<AppEnv>();
51
76
  // A read-only registry (under `kindgi dev`, the pack's files) refuses
@@ -127,8 +152,15 @@ export function guardrailsRouter(
127
152
  ...(scopeParsed.scope !== undefined && { scope: scopeParsed.scope }),
128
153
  ...(scopeParsed.inherit !== undefined && { inherit: scopeParsed.inherit }),
129
154
  });
155
+ // Only what the caller may read (T243 A), as `GET …/:id` asks.
156
+ const visible =
157
+ authorizer === undefined
158
+ ? page.data
159
+ : await authorizer.filterByCan(c, 'read', page.data, (a) =>
160
+ ref('guardrail', a.id as unknown as string),
161
+ );
130
162
  return c.json({
131
- data: page.data.map(serializeGuardrail),
163
+ data: visible.map(serializeGuardrail),
132
164
  hasMore: page.nextCursor !== undefined,
133
165
  ...(page.nextCursor !== undefined && { nextCursor: page.nextCursor as unknown as string }),
134
166
  });
@@ -210,6 +242,33 @@ export function guardrailsRouter(
210
242
  );
211
243
  }
212
244
 
245
+ // A built-in check's config is the route's to check (its schema ships with
246
+ // `@kindgi/guardrails`); any other check's, the runtime's.
247
+ const builtIn = builtInChecks.get(validated.value.check);
248
+ const problems =
249
+ builtIn?.configProblems !== undefined
250
+ ? builtIn.configProblems(validated.value.config)
251
+ : await checkConfig?.({ tenantId, guardrail: validated.value });
252
+ if (problems !== undefined && problems.length > 0) {
253
+ c.status(statusFor('guardrail-config-invalid') as never);
254
+ return c.json(
255
+ toWireError(
256
+ {
257
+ code: 'guardrail-config-invalid',
258
+ message: describeGuardrailConfigProblems(
259
+ {
260
+ guardrailId: validated.value.id as unknown as string,
261
+ check: validated.value.check,
262
+ },
263
+ problems,
264
+ ),
265
+ issues: problems as unknown as Record<string, unknown>[],
266
+ },
267
+ requestId,
268
+ ),
269
+ );
270
+ }
271
+
213
272
  const principal = c.get('principal') as Principal | undefined;
214
273
  const creatorUserId =
215
274
  principal?.actor.kind === 'user' ? (principal.actor.id as UserId) : undefined;
@@ -1,16 +1,25 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // Copyright (C) 2026 Kindgi Inc.
3
3
 
4
+ import type { Context } from 'hono';
4
5
  import { Hono } from 'hono';
5
6
 
6
7
  import type { Cursor, SessionId, TenantId, UserId } from '@kindgi/types';
7
8
 
9
+ import { callerPrincipal, callerRef, isTenantAdmin, principalToWire } from '../caller.js';
8
10
  import { statusFor, toWireError } from '../errors.js';
9
11
  import type {
10
12
  IdentityDirectoryBinding,
11
13
  SessionSummary,
12
14
  UserRecord,
13
15
  } from '../identity-directory-binding.js';
16
+ import type { Authorizer } from '../middleware/authorize.js';
17
+ import type {
18
+ PersonGrant,
19
+ PersonGrantError,
20
+ PersonGrants,
21
+ PersonGrantsBinding,
22
+ } from '../person-grants-binding.js';
14
23
  import type { ReviewerBinding } from '../reviewer-binding.js';
15
24
  import { callerReviewerRole } from '../reviewer-role.js';
16
25
  import type { SessionStoreBinding } from '../session-store-binding.js';
@@ -24,12 +33,16 @@ import { clampLimit } from './pagination.js';
24
33
  * `whoami` endpoint, so clients have exactly one whoami endpoint to
25
34
  * consume.
26
35
  *
27
- * Five routes:
36
+ * Six routes:
28
37
  * - `GET /v1/identity/whoami` (self — always mounted)
29
- * - `GET /v1/identity/users` (cursor-paginated list)
30
- * - `GET /v1/identity/users/:userId` (get)
31
- * - `GET /v1/identity/users/:userId/sessions` (active sessions)
38
+ * - `GET /v1/identity/users` (cursor-paginated list; tenant admins)
39
+ * - `POST /v1/identity/users` (add a person; admin, when the directory can)
40
+ * - `GET /v1/identity/users/:userId` (get; a tenant admin, or your own)
41
+ * - `GET /v1/identity/users/:userId/sessions` (active sessions; a tenant admin, or your own)
32
42
  * - `POST /v1/identity/users/:userId/revoke-sessions` (admin op)
43
+ * - `GET /v1/identity/users/:userId/grants` (admin, or your own)
44
+ * - `POST /v1/identity/users/:userId/grant|ungrant` (tenant admin, tenant admins only)
45
+ * - `POST /v1/identity/users/:userId/unregister` (remove a person; tenant admins, when the directory can)
33
46
  *
34
47
  * `directory` is optional: deployments without an `IdentityDirectoryBinding`
35
48
  * still get `whoami` (returning the minimal `{ tenantId, scopes, ... }` shape
@@ -55,10 +68,20 @@ export interface IdentityRouterOptions {
55
68
  * routes resolve it.
56
69
  */
57
70
  readonly reviewerBinding?: ReviewerBinding;
71
+ /**
72
+ * Optional. Decides who is a tenant admin for `POST /users`; without
73
+ * one, the `tenant-admin` scope does.
74
+ */
75
+ readonly authorizer?: Authorizer;
76
+ /**
77
+ * Optional. A person's grants: the read, and tenant admin given or
78
+ * taken. Without it, those routes answer `501 person-grants-unsupported`.
79
+ */
80
+ readonly personGrants?: PersonGrantsBinding;
58
81
  }
59
82
 
60
83
  export function identityRouter(options: IdentityRouterOptions = {}): Hono<AppEnv> {
61
- const { directory, sessionStore, reviewerBinding } = options;
84
+ const { directory, sessionStore, reviewerBinding, authorizer, personGrants } = options;
62
85
  const r = new Hono<AppEnv>();
63
86
 
64
87
  // ---------- GET / whoami ----------
@@ -76,7 +99,21 @@ export function identityRouter(options: IdentityRouterOptions = {}): Hono<AppEnv
76
99
  if (sessionId !== undefined) body.sessionId = sessionId;
77
100
  if (providerId !== undefined) body.providerId = providerId;
78
101
  body.scopes = scopes;
102
+ // Whether the caller is a tenant admin, as the admin routes decide it:
103
+ // what a console shows admin pages by (scopes are what a credential may
104
+ // do; admin is a role, from the authorizer when there is one). whoami
105
+ // is how a console starts, so an authorization store that can't answer
106
+ // leaves the field out (a console then reads `scopes`) instead of
107
+ // failing the whole answer.
108
+ try {
109
+ body.tenantAdmin = await isTenantAdmin(c, authorizer);
110
+ } catch (cause) {
111
+ c.get('log').warn(
112
+ `whoami: couldn't tell whether the caller is a tenant admin, so tenantAdmin is left out: ${cause instanceof Error ? cause.message : String(cause)}`,
113
+ );
114
+ }
79
115
  if (reviewerRole !== undefined) body.reviewerRole = reviewerRole;
116
+ Object.assign(body, keyFacts(c));
80
117
 
81
118
  if (userId !== undefined) {
82
119
  body.userId = userId;
@@ -99,18 +136,25 @@ export function identityRouter(options: IdentityRouterOptions = {}): Hono<AppEnv
99
136
  return r;
100
137
  }
101
138
 
102
- // ---------- GET /users (list) ----------
139
+ // ---------- GET /users (list; tenant admins) ----------
140
+ // The people of a tenant, with their emails: a tenant admin's to read.
141
+ // Anyone else adds a person to a project by their email or id.
103
142
  r.get('/users', async (c) => {
143
+ if (!(await isTenantAdmin(c, authorizer))) {
144
+ return denied(c, "Only a tenant admin lists the tenant's people");
145
+ }
104
146
  const tenantId = c.get('tenantId') as TenantId;
105
147
  const limit = clampLimit(c.req.query('limit'));
106
148
  const cursorRaw = c.req.query('cursor');
107
149
  const queryRaw = c.req.query('query');
150
+ const includeUnregistered = c.req.query('includeUnregistered') === 'true';
108
151
 
109
152
  const page = await directory.listUsers({
110
153
  tenantId,
111
154
  limit,
112
155
  ...(cursorRaw !== undefined && cursorRaw.length > 0 && { cursor: cursorRaw as Cursor }),
113
156
  ...(queryRaw !== undefined && queryRaw.length > 0 && { query: queryRaw }),
157
+ ...(includeUnregistered && { includeUnregistered: true }),
114
158
  });
115
159
  return c.json({
116
160
  data: page.data.map(serializeUser),
@@ -119,11 +163,57 @@ export function identityRouter(options: IdentityRouterOptions = {}): Hono<AppEnv
119
163
  });
120
164
  });
121
165
 
122
- // ---------- GET /users/:userId (get) ----------
166
+ // ---------- POST /users (add a person; tenant admins) ----------
167
+ const createUser = directory.createUser?.bind(directory);
168
+ if (createUser !== undefined) {
169
+ r.post('/users', async (c) => {
170
+ const requestId = c.get('requestId');
171
+ if (!(await isTenantAdmin(c, authorizer))) {
172
+ c.status(statusFor('permission-denied') as never);
173
+ return c.json(
174
+ toWireError(
175
+ { code: 'permission-denied', message: 'Only a tenant admin adds people' },
176
+ requestId,
177
+ ),
178
+ );
179
+ }
180
+ const parsed = parseCreateUser(await c.req.json().catch(() => undefined));
181
+ if (typeof parsed === 'string') {
182
+ c.status(statusFor('bad-input') as never);
183
+ return c.json(toWireError({ code: 'bad-input', message: parsed }, requestId));
184
+ }
185
+ const createdBy = callerRef(c);
186
+ const outcome = await createUser({
187
+ tenantId: c.get('tenantId') as TenantId,
188
+ ...parsed,
189
+ ...(createdBy !== undefined && { createdBy }),
190
+ });
191
+ if (outcome.kind === 'email-taken') {
192
+ c.status(statusFor('identity-user-email-taken') as never);
193
+ return c.json(
194
+ toWireError(
195
+ {
196
+ code: 'identity-user-email-taken',
197
+ message: 'Another person of this tenant has that email',
198
+ userId: outcome.userId as unknown as string,
199
+ },
200
+ requestId,
201
+ ),
202
+ );
203
+ }
204
+ c.status(201);
205
+ return c.json(serializeUser(outcome.user));
206
+ });
207
+ }
208
+
209
+ // ---------- GET /users/:userId (get; a tenant admin, or your own) ----------
123
210
  r.get('/users/:userId', async (c) => {
124
211
  const requestId = c.get('requestId');
125
212
  const tenantId = c.get('tenantId') as TenantId;
126
213
  const userId = c.req.param('userId') as UserId;
214
+ if (!isOwn(c, userId) && !(await isTenantAdmin(c, authorizer))) {
215
+ return denied(c, "Only a tenant admin reads someone else's record");
216
+ }
127
217
 
128
218
  const user = await directory.getUser({ tenantId, userId });
129
219
  if (user === null) {
@@ -142,10 +232,13 @@ export function identityRouter(options: IdentityRouterOptions = {}): Hono<AppEnv
142
232
  return c.json(serializeUser(user));
143
233
  });
144
234
 
145
- // ---------- GET /users/:userId/sessions (list active sessions) ----------
235
+ // ---------- GET /users/:userId/sessions (a tenant admin, or your own) ----------
146
236
  r.get('/users/:userId/sessions', async (c) => {
147
237
  const tenantId = c.get('tenantId') as TenantId;
148
238
  const userId = c.req.param('userId') as UserId;
239
+ if (!isOwn(c, userId) && !(await isTenantAdmin(c, authorizer))) {
240
+ return denied(c, "Only a tenant admin reads someone else's sessions");
241
+ }
149
242
 
150
243
  const page = await directory.listSessions({ tenantId, userId });
151
244
  return c.json({
@@ -155,15 +248,84 @@ export function identityRouter(options: IdentityRouterOptions = {}): Hono<AppEnv
155
248
  });
156
249
  });
157
250
 
158
- // ---------- POST /users/:userId/revoke-sessions (admin) ----------
251
+ // ---------- POST /users/:userId/unregister (remove a person; tenant admins) ----------
252
+ const unregisterUser = directory.unregisterUser?.bind(directory);
253
+ if (unregisterUser !== undefined) {
254
+ r.post('/users/:userId/unregister', async (c) => {
255
+ const requestId = c.get('requestId');
256
+ const userId = c.req.param('userId') as UserId;
257
+ if (!(await isTenantAdmin(c, authorizer))) {
258
+ c.status(statusFor('permission-denied') as never);
259
+ return c.json(
260
+ toWireError(
261
+ { code: 'permission-denied', message: 'Only a tenant admin removes people' },
262
+ requestId,
263
+ ),
264
+ );
265
+ }
266
+ const unregisteredBy = callerRef(c);
267
+ const outcome = await unregisterUser({
268
+ tenantId: c.get('tenantId') as TenantId,
269
+ userId,
270
+ ...(unregisteredBy !== undefined && { unregisteredBy }),
271
+ });
272
+ if (outcome.kind === 'not-found') {
273
+ c.status(statusFor('identity-user-not-found') as never);
274
+ return c.json(
275
+ toWireError(
276
+ {
277
+ code: 'identity-user-not-found',
278
+ message: `No user with id "${userId as unknown as string}" under this tenant`,
279
+ userId: userId as unknown as string,
280
+ },
281
+ requestId,
282
+ ),
283
+ );
284
+ }
285
+ if (outcome.kind === 'refused') {
286
+ const code =
287
+ outcome.reason === 'last-tenant-admin'
288
+ ? 'last-tenant-admin'
289
+ : 'identity-user-unregister-refused';
290
+ c.status(statusFor(code) as never);
291
+ return c.json(
292
+ toWireError(
293
+ {
294
+ code,
295
+ message: outcome.message,
296
+ reason: outcome.reason,
297
+ userId: userId as unknown as string,
298
+ },
299
+ requestId,
300
+ ),
301
+ );
302
+ }
303
+ return c.json({
304
+ user: serializeUser(outcome.user),
305
+ keysRevoked: outcome.keysRevoked,
306
+ sessionsRevoked: outcome.sessionsRevoked,
307
+ grantsRemoved: outcome.grantsRemoved,
308
+ });
309
+ });
310
+ }
311
+
312
+ // ---------- POST /users/:userId/revoke-sessions (admin, or your own) ----------
159
313
  r.post('/users/:userId/revoke-sessions', async (c) => {
160
314
  const requestId = c.get('requestId');
161
315
  const tenantId = c.get('tenantId') as TenantId;
162
316
  const userId = c.req.param('userId') as UserId;
317
+ if (!isOwn(c, userId) && !(await isTenantAdmin(c, authorizer))) {
318
+ return denied(c, "Only a tenant admin revokes someone else's sessions");
319
+ }
163
320
 
164
321
  let outcome: Awaited<ReturnType<IdentityDirectoryBinding['revokeAllSessions']>>;
165
322
  try {
166
- outcome = await directory.revokeAllSessions({ tenantId, userId });
323
+ const revokedBy = callerRef(c);
324
+ outcome = await directory.revokeAllSessions({
325
+ tenantId,
326
+ userId,
327
+ ...(revokedBy !== undefined && { revokedBy }),
328
+ });
167
329
  } catch (err) {
168
330
  c.status(statusFor('identity-revoke-failed') as never);
169
331
  return c.json(
@@ -183,9 +345,123 @@ export function identityRouter(options: IdentityRouterOptions = {}): Hono<AppEnv
183
345
  });
184
346
  });
185
347
 
348
+ mountPersonGrants(r, personGrants, authorizer);
186
349
  return r;
187
350
  }
188
351
 
352
+ const unsupported = (c: Context<AppEnv>) => {
353
+ c.status(statusFor('person-grants-unsupported') as never);
354
+ return c.json(
355
+ toWireError(
356
+ {
357
+ code: 'person-grants-unsupported',
358
+ message:
359
+ "This runtime doesn't read or write a person's grants: it runs without an authorization store",
360
+ },
361
+ c.get('requestId'),
362
+ ),
363
+ );
364
+ };
365
+
366
+ /** Whether `userId` is the caller's own. */
367
+ const isOwn = (c: Context<AppEnv>, userId: string) =>
368
+ (c.get('userId') as unknown as string | undefined) === userId;
369
+
370
+ const denied = (c: Context<AppEnv>, message: string) => {
371
+ c.status(statusFor('permission-denied') as never);
372
+ return c.json(toWireError({ code: 'permission-denied', message }, c.get('requestId')));
373
+ };
374
+
375
+ /**
376
+ * `GET /users/:userId/grants` (a tenant admin, or the person), and
377
+ * `POST /users/:userId/grant|ungrant` (tenant admins): `{kind:
378
+ * 'tenant-admin'}`, written before the call answers.
379
+ */
380
+ function mountPersonGrants(
381
+ r: Hono<AppEnv>,
382
+ binding: PersonGrantsBinding | undefined,
383
+ authorizer: Authorizer | undefined,
384
+ ): void {
385
+ r.get('/users/:userId/grants', async (c) => {
386
+ const userId = c.req.param('userId');
387
+ if (!isOwn(c, userId) && !(await isTenantAdmin(c, authorizer))) {
388
+ return denied(c, "Only a tenant admin reads someone else's grants");
389
+ }
390
+ if (binding === undefined) return unsupported(c);
391
+ const grants = await binding.read({ tenantId: c.get('tenantId') as TenantId, userId });
392
+ if (grants === null) return notFound(c, userId);
393
+ return c.json(serializePersonGrants(grants));
394
+ });
395
+
396
+ for (const action of ['grant', 'ungrant'] as const) {
397
+ r.post(`/users/:userId/${action}`, async (c) => {
398
+ if (!(await isTenantAdmin(c, authorizer))) {
399
+ return denied(c, "Only a tenant admin changes a person's grants");
400
+ }
401
+ const grant = parsePersonGrant(await c.req.json().catch(() => undefined));
402
+ if (typeof grant === 'string') {
403
+ c.status(statusFor('bad-input') as never);
404
+ return c.json(toWireError({ code: 'bad-input', message: grant }, c.get('requestId')));
405
+ }
406
+ if (binding === undefined) return unsupported(c);
407
+ const by = callerRef(c);
408
+ const result = await binding[action]({
409
+ tenantId: c.get('tenantId') as TenantId,
410
+ userId: c.req.param('userId'),
411
+ grant,
412
+ ...(by !== undefined && { by }),
413
+ });
414
+ if (result.kind === 'err') return grantError(c, result.error);
415
+ return c.json(serializePersonGrants(result.value));
416
+ });
417
+ }
418
+ }
419
+
420
+ function notFound(c: Context<AppEnv>, userId: string) {
421
+ c.status(statusFor('identity-user-not-found') as never);
422
+ return c.json(
423
+ toWireError(
424
+ {
425
+ code: 'identity-user-not-found',
426
+ message: `No user with id "${userId}" under this tenant`,
427
+ userId,
428
+ },
429
+ c.get('requestId'),
430
+ ),
431
+ );
432
+ }
433
+
434
+ function grantError(c: Context<AppEnv>, error: PersonGrantError) {
435
+ c.status(statusFor(error.code) as never);
436
+ return c.json(toWireError({ code: error.code, message: error.message }, c.get('requestId')));
437
+ }
438
+
439
+ /** `{kind: 'tenant-admin'}`, or why not. */
440
+ function parsePersonGrant(raw: unknown): PersonGrant | string {
441
+ const b = raw as { kind?: unknown } | null | undefined;
442
+ if (
443
+ typeof b !== 'object' ||
444
+ b === null ||
445
+ Array.isArray(b) ||
446
+ b.kind !== 'tenant-admin' ||
447
+ Object.keys(b).length !== 1
448
+ ) {
449
+ return "The body must be { kind: 'tenant-admin' }: a person's project and team roles have their own routes (`/v1/projects/{projectId}/memberships`, `/v1/teams/{teamId}/memberships`)";
450
+ }
451
+ return { kind: 'tenant-admin' };
452
+ }
453
+
454
+ function serializePersonGrants(g: PersonGrants): Record<string, unknown> {
455
+ return {
456
+ userId: g.userId,
457
+ ...(g.tenantAdmin !== undefined && { tenantAdmin: g.tenantAdmin }),
458
+ ...(g.tenantMember !== undefined && { tenantMember: g.tenantMember }),
459
+ projects: g.projects.map((p) => ({ projectId: p.projectId, role: p.role })),
460
+ teams: g.teams.map((t) => ({ teamId: t.teamId, role: t.role })),
461
+ ...(g.reviewer !== undefined && { reviewer: { role: g.reviewer.role } }),
462
+ };
463
+ }
464
+
189
465
  function serializeUser(u: UserRecord): Record<string, unknown> {
190
466
  return {
191
467
  userId: u.userId as unknown as string,
@@ -194,6 +470,9 @@ function serializeUser(u: UserRecord): Record<string, unknown> {
194
470
  ...(u.displayName !== undefined && { displayName: u.displayName }),
195
471
  createdAt: u.createdAt as unknown as string,
196
472
  ...(u.lastActiveAt !== undefined && { lastActiveAt: u.lastActiveAt as unknown as string }),
473
+ ...(u.unregisteredAt !== undefined && {
474
+ unregisteredAt: u.unregisteredAt as unknown as string,
475
+ }),
197
476
  ...(u.metadata !== undefined && { metadata: u.metadata }),
198
477
  };
199
478
  }
@@ -209,3 +488,42 @@ function serializeSession(s: SessionSummary): Record<string, unknown> {
209
488
  ...(s.revokedAt !== undefined && { revokedAt: s.revokedAt as unknown as string }),
210
489
  };
211
490
  }
491
+
492
+ /**
493
+ * What an API key adds to whoami: whom it acts for, its id, and what it's
494
+ * limited to, so a client can say "you're acme-ci, limited to project X".
495
+ */
496
+ function keyFacts(c: Context<AppEnv>): Record<string, unknown> {
497
+ const principal = callerPrincipal(c);
498
+ const tokenId = c.get('tokenId') as string | undefined;
499
+ const role = c.get('tokenRole');
500
+ const projectId = c.get('tokenProjectId');
501
+ return {
502
+ ...(principal !== undefined && { principal: principalToWire(principal) }),
503
+ ...(tokenId !== undefined && { tokenId }),
504
+ ...(role !== undefined && { role }),
505
+ ...(projectId !== undefined && { projectId }),
506
+ };
507
+ }
508
+
509
+ const MAX_NAME_LENGTH = 200;
510
+
511
+ /** `{displayName, primaryEmail?}`, or why not. */
512
+ function parseCreateUser(raw: unknown): { displayName: string; primaryEmail?: string } | string {
513
+ if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
514
+ return 'Request body must be a JSON object';
515
+ }
516
+ const b = raw as { displayName?: unknown; primaryEmail?: unknown };
517
+ if (
518
+ typeof b.displayName !== 'string' ||
519
+ b.displayName.trim() === '' ||
520
+ b.displayName.length > MAX_NAME_LENGTH
521
+ ) {
522
+ return `\`displayName\` must be a non-empty string of at most ${MAX_NAME_LENGTH} characters`;
523
+ }
524
+ if (b.primaryEmail === undefined) return { displayName: b.displayName.trim() };
525
+ if (typeof b.primaryEmail !== 'string' || !/^[^@\s]+@[^@\s]+$/.test(b.primaryEmail)) {
526
+ return '`primaryEmail` must be an email address';
527
+ }
528
+ return { displayName: b.displayName.trim(), primaryEmail: b.primaryEmail };
529
+ }