@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
@@ -0,0 +1,179 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+
4
+ /**
5
+ * Who can see and change which memory, for a caller of `/v1/memory`.
6
+ *
7
+ * Reads: the containers the caller may read (`MemoryReaders`), worked out
8
+ * here from the principal and handed to the binding, which applies them
9
+ * inside its query. A tenant admin (or a deployment without
10
+ * authorization) reads everything; anyone else reads tenant-wide facts,
11
+ * the projects and orgs they may read, their own user facts, and every
12
+ * end user's and conversation's facts in the projects they may write (an
13
+ * app's own credential acting for its users).
14
+ *
15
+ * Writes: what the fact's scope names decides the check.
16
+ */
17
+
18
+ import type { Context } from 'hono';
19
+
20
+ import { type Principal, ref } from '@kindgi/authz';
21
+ import type { MemoryReaders, MemoryScope } from '@kindgi/memory';
22
+ import type { ProjectBinding } from '@kindgi/platform';
23
+ import type { OrgId, ProjectId, TenantId, UserId } from '@kindgi/types';
24
+
25
+ import type { Authorizer } from '../middleware/authorize.js';
26
+ import type { AppEnv } from '../types.js';
27
+
28
+ export interface MemoryAccessDeps {
29
+ readonly authorizer?: Authorizer;
30
+ /** The tenant's projects, to check one by one when the authorizer can't list them. */
31
+ readonly projects?: Pick<ProjectBinding, 'list'>;
32
+ }
33
+
34
+ const PROJECT_PAGE = 200;
35
+
36
+ /** The caller's user id, when the caller is a Kindgi user. */
37
+ function callerUserId(c: Context<AppEnv>): UserId | undefined {
38
+ const actor = (c.get('principal') as Principal | undefined)?.actor;
39
+ return actor?.kind === 'user' ? (actor.id as UserId) : undefined;
40
+ }
41
+
42
+ /** `user:<id>` or `service:<id>`: who did it, for a record. */
43
+ export function callerRef(c: Context<AppEnv>): string {
44
+ const actor = (c.get('principal') as Principal | undefined)?.actor;
45
+ if (actor === undefined) return 'service:unknown';
46
+ return `${actor.kind === 'user' ? 'user' : 'service'}:${actor.id}`;
47
+ }
48
+
49
+ /** Who asserts a fact the caller writes. */
50
+ export function callerAttribution(c: Context<AppEnv>): {
51
+ readonly kind: 'user' | 'service';
52
+ readonly id: string;
53
+ } {
54
+ const actor = (c.get('principal') as Principal | undefined)?.actor;
55
+ if (actor === undefined) return { kind: 'service', id: 'unknown' };
56
+ return { kind: actor.kind === 'user' ? 'user' : 'service', id: actor.id };
57
+ }
58
+
59
+ async function isTenantAdmin(c: Context<AppEnv>, authorizer: Authorizer): Promise<boolean> {
60
+ return authorizer.can(c, 'admin', ref('tenant', c.get('tenantId') as unknown as string));
61
+ }
62
+
63
+ type ProjectRow = { readonly id: ProjectId; readonly orgId?: OrgId };
64
+
65
+ /** The tenant's projects, every page. */
66
+ async function allProjects(c: Context<AppEnv>, deps: MemoryAccessDeps): Promise<ProjectRow[]> {
67
+ if (deps.projects === undefined) return [];
68
+ const tenantId = c.get('tenantId') as TenantId;
69
+ const all: ProjectRow[] = [];
70
+ let cursor: Parameters<ProjectBinding['list']>[1]['cursor'];
71
+ for (;;) {
72
+ const page = await deps.projects.list(tenantId, {
73
+ limit: PROJECT_PAGE,
74
+ ...(cursor !== undefined && { cursor }),
75
+ });
76
+ all.push(
77
+ ...page.items.map((p) => ({ id: p.id, ...(p.orgId !== undefined && { orgId: p.orgId }) })),
78
+ );
79
+ if (page.nextCursor === undefined) break;
80
+ cursor = page.nextCursor;
81
+ }
82
+ return all;
83
+ }
84
+
85
+ /**
86
+ * The projects the caller may `action`, and the orgs whose org-wide facts
87
+ * it reads: listed by the authorizer (`read` on the org), else the
88
+ * projects checked one by one and the orgs of those it may read.
89
+ */
90
+ async function readerContainers(
91
+ c: Context<AppEnv>,
92
+ deps: MemoryAccessDeps & { readonly authorizer: Authorizer },
93
+ ): Promise<{ readable: ProjectId[]; writable: ProjectId[]; orgs: OrgId[] }> {
94
+ const { authorizer } = deps;
95
+ const [readable, writable, orgs] = await Promise.all([
96
+ authorizer.listObjects?.(c, 'read', 'project'),
97
+ authorizer.listObjects?.(c, 'write', 'project'),
98
+ authorizer.listObjects?.(c, 'read', 'org'),
99
+ ]);
100
+ if (readable !== undefined && writable !== undefined && orgs !== undefined) {
101
+ return {
102
+ readable: readable.map((id) => id as ProjectId),
103
+ writable: writable.map((id) => id as ProjectId),
104
+ orgs: orgs.map((id) => id as OrgId),
105
+ };
106
+ }
107
+ const all = await allProjects(c, deps);
108
+ const projectRef = (p: ProjectRow) => ref('project', p.id as unknown as string);
109
+ const [canRead, canWrite] = await Promise.all([
110
+ authorizer.filterByCan(c, 'read', all, projectRef),
111
+ authorizer.filterByCan(c, 'write', all, projectRef),
112
+ ]);
113
+ return {
114
+ readable: canRead.map((p) => p.id),
115
+ writable: canWrite.map((p) => p.id),
116
+ orgs: [...new Set(canRead.flatMap((p) => (p.orgId !== undefined ? [p.orgId] : [])))],
117
+ };
118
+ }
119
+
120
+ /** What the caller may read. */
121
+ export async function memoryReadersFor(
122
+ c: Context<AppEnv>,
123
+ deps: MemoryAccessDeps,
124
+ ): Promise<MemoryReaders> {
125
+ const { authorizer } = deps;
126
+ // Without authorization (a development deployment), the one credential reads it all.
127
+ if (authorizer === undefined) return { all: true };
128
+ if (await isTenantAdmin(c, authorizer)) return { all: true };
129
+ const { readable, writable, orgs } = await readerContainers(c, { ...deps, authorizer });
130
+ const user = callerUserId(c);
131
+ return {
132
+ projectIds: readable,
133
+ orgIds: orgs,
134
+ ...(user !== undefined && { userIds: [user] }),
135
+ onBehalfOfProjectIds: writable,
136
+ };
137
+ }
138
+
139
+ /**
140
+ * Why the caller may not write a fact in `scope`, if they may not:
141
+ * - a tenant-wide fact needs `admin` on the tenant;
142
+ * - an org-wide fact, `admin` on the org;
143
+ * - a project's fact (an end user's or a conversation's included),
144
+ * `write` on the project;
145
+ * - a conversation's fact outside a project, `write` on the conversation;
146
+ * - a user's fact, being that user (or a tenant admin).
147
+ */
148
+ export async function memoryWriteProblem(
149
+ c: Context<AppEnv>,
150
+ deps: MemoryAccessDeps,
151
+ scope: MemoryScope,
152
+ ): Promise<string | undefined> {
153
+ const { authorizer } = deps;
154
+ if (authorizer === undefined || (await isTenantAdmin(c, authorizer))) return undefined;
155
+ if (scope.userId !== undefined && scope.userId !== callerUserId(c)) {
156
+ return "Only that user (or a tenant admin) can write a user's memory.";
157
+ }
158
+ if (scope.projectId !== undefined) {
159
+ return (await authorizer.can(c, 'write', ref('project', scope.projectId as unknown as string)))
160
+ ? undefined
161
+ : `Writing memory in project "${scope.projectId as unknown as string}" needs write on it.`;
162
+ }
163
+ if (scope.threadId !== undefined) {
164
+ return (await authorizer.can(
165
+ c,
166
+ 'write',
167
+ ref('conversation', scope.threadId as unknown as string),
168
+ ))
169
+ ? undefined
170
+ : `Writing a conversation's memory needs write on conversation "${scope.threadId as unknown as string}".`;
171
+ }
172
+ if (scope.orgId !== undefined) {
173
+ return (await authorizer.can(c, 'admin', ref('org', scope.orgId as unknown as string)))
174
+ ? undefined
175
+ : `Writing org-wide memory needs admin on org "${scope.orgId as unknown as string}".`;
176
+ }
177
+ if (scope.userId !== undefined) return undefined;
178
+ return 'Writing tenant-wide memory needs admin on the tenant.';
179
+ }
@@ -0,0 +1,240 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+
4
+ import { type Context, Hono } from 'hono';
5
+
6
+ import { ref } from '@kindgi/authz';
7
+ import type { TenantId } from '@kindgi/types';
8
+
9
+ import { statusFor, toWireError } from '../errors.js';
10
+ import type {
11
+ MemoryErasureBinding,
12
+ MemoryErasureLedgerEntry,
13
+ MemoryErasureSelector,
14
+ MemoryErasureSelectorKind,
15
+ } from '../memory-erasure-binding.js';
16
+ import type { Authorizer } from '../middleware/authorize.js';
17
+ import type { AppEnv } from '../types.js';
18
+ import { callerRef } from './memory-access.js';
19
+ import { clampLimit } from './pagination.js';
20
+
21
+ /**
22
+ * Erasing a person's words (T273 M-5):
23
+ *
24
+ * - `POST /v1/memory/erasures` start one (202); `409 legal-hold`
25
+ * when a fact it reaches is held
26
+ * - `GET /v1/memory/erasures` newest first, in pages
27
+ * - `GET /v1/memory/erasures/export` the whole ledger, content-free,
28
+ * to keep off-box
29
+ * - `GET /v1/memory/erasures/:id` one, and how far it got
30
+ * - `POST /v1/memory/erasures/:id/resume` try again now; `force`: stop
31
+ * waiting for a shared flow's run
32
+ * - `POST /v1/memory/erasures/replay` after a backup restore: the
33
+ * exported ledger back, its
34
+ * erasures run again
35
+ *
36
+ * PEP: a tenant admin only, for every selector (`admin` on
37
+ * `tenant:<tenantId>`), as retention; a service account only through an
38
+ * explicit tenant-admin grant.
39
+ */
40
+ export function memoryErasuresRouter(
41
+ binding: MemoryErasureBinding,
42
+ authorizer?: Authorizer,
43
+ ): Hono<AppEnv> {
44
+ const r = new Hono<AppEnv>();
45
+
46
+ if (authorizer !== undefined) {
47
+ r.use('/*', async (c, next) => {
48
+ const tenantId = c.get('tenantId') as TenantId;
49
+ const mw = authorizer.authorize('admin', async () =>
50
+ ref('tenant', tenantId as unknown as string),
51
+ );
52
+ return mw(c, next);
53
+ });
54
+ }
55
+
56
+ r.post('/', async (c) => {
57
+ const tenantId = c.get('tenantId') as TenantId;
58
+ const body = await jsonBody(c);
59
+ const selector = parseSelector(body);
60
+ if (typeof selector === 'string') return fail(c, 'bad-input', selector);
61
+ const outcome = await binding.create({ tenantId, selector, requestedBy: callerRef(c) });
62
+ if (outcome.kind === 'refused') {
63
+ const { code, message, factIds } = outcome.refusal;
64
+ return fail(c, code, message, factIds !== undefined ? { factIds: [...factIds] } : {});
65
+ }
66
+ c.status(202);
67
+ return c.json({
68
+ ...outcome.erasure,
69
+ ...(outcome.warnings.length > 0 && { warnings: outcome.warnings }),
70
+ });
71
+ });
72
+
73
+ r.get('/', async (c) => {
74
+ const tenantId = c.get('tenantId') as TenantId;
75
+ const cursor = c.req.query('cursor');
76
+ const page = await binding.list(tenantId, {
77
+ limit: clampLimit(c.req.query('limit')),
78
+ ...(cursor !== undefined && cursor !== '' && { cursor }),
79
+ });
80
+ if (page === undefined) return fail(c, 'bad-input', '`cursor` is not one this list issued');
81
+ return c.json({
82
+ data: page.data,
83
+ hasMore: page.nextCursor !== undefined,
84
+ ...(page.nextCursor !== undefined && { nextCursor: page.nextCursor }),
85
+ });
86
+ });
87
+
88
+ r.get('/export', async (c) => {
89
+ const tenantId = c.get('tenantId') as TenantId;
90
+ return c.json({ data: await binding.exportLedger(tenantId) });
91
+ });
92
+
93
+ r.post('/replay', async (c) => {
94
+ const tenantId = c.get('tenantId') as TenantId;
95
+ const body = await jsonBody(c);
96
+ const entries = parseLedger(body);
97
+ if (typeof entries === 'string') return fail(c, 'bad-input', entries);
98
+ return c.json(await binding.replay(tenantId, entries));
99
+ });
100
+
101
+ r.post('/:erasureId/resume', async (c) => {
102
+ const tenantId = c.get('tenantId') as TenantId;
103
+ const id = c.req.param('erasureId');
104
+ const body = (await jsonBody(c)) ?? {};
105
+ const force = (body as { force?: unknown }).force;
106
+ if (
107
+ typeof body !== 'object' ||
108
+ Array.isArray(body) ||
109
+ (force !== undefined && typeof force !== 'boolean')
110
+ ) {
111
+ return fail(c, 'bad-input', 'The body is `{force?: boolean}`');
112
+ }
113
+ const erasure = UUID.test(id)
114
+ ? await binding.resume(tenantId, id, { ...(force === true && { force: true }) })
115
+ : undefined;
116
+ if (erasure === undefined) return fail(c, 'not-found', `No erasure ${id}`);
117
+ return c.json(erasure);
118
+ });
119
+
120
+ r.get('/:erasureId', async (c) => {
121
+ const tenantId = c.get('tenantId') as TenantId;
122
+ const id = c.req.param('erasureId');
123
+ const erasure = UUID.test(id) ? await binding.get(tenantId, id) : undefined;
124
+ if (erasure === undefined) return fail(c, 'not-found', `No erasure ${id}`);
125
+ return c.json(erasure);
126
+ });
127
+
128
+ return r;
129
+ }
130
+
131
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
132
+ const SUBJECT_KINDS = ['participant', 'external'] as const;
133
+ const SELECTOR_KINDS: readonly MemoryErasureSelectorKind[] = [
134
+ 'fact',
135
+ 'participant',
136
+ 'external',
137
+ 'conversation',
138
+ ];
139
+ const STATUSES = ['pending', 'running', 'waiting-on-run', 'completed', 'failed'] as const;
140
+ const MAX_ID = 256;
141
+ const MAX_LEDGER = 10_000;
142
+
143
+ function fail(
144
+ c: Context<AppEnv>,
145
+ code: string,
146
+ message: string,
147
+ extra: Readonly<Record<string, unknown>> = {},
148
+ ) {
149
+ c.status(statusFor(code as never) as never);
150
+ return c.json(toWireError({ code, message, ...extra }, c.get('requestId')));
151
+ }
152
+
153
+ async function jsonBody(c: Context<AppEnv>): Promise<unknown> {
154
+ try {
155
+ return await c.req.json();
156
+ } catch {
157
+ return undefined;
158
+ }
159
+ }
160
+
161
+ const isId = (v: unknown): v is string =>
162
+ typeof v === 'string' && v.trim() !== '' && v.length <= MAX_ID;
163
+
164
+ /** Exactly one of `factId`, `subject` or `conversationId`; or why not. */
165
+ function parseSelector(body: unknown): MemoryErasureSelector | string {
166
+ if (body === null || typeof body !== 'object' || Array.isArray(body)) {
167
+ return 'The body must be an object naming one of `factId`, `subject` or `conversationId`';
168
+ }
169
+ const b = body as Record<string, unknown>;
170
+ const named = ['factId', 'subject', 'conversationId'].filter((k) => b[k] !== undefined);
171
+ if (named.length !== 1) {
172
+ return 'Name exactly one of `factId`, `subject` (`{kind, id}`) or `conversationId`';
173
+ }
174
+ const unknown = Object.keys(b).filter(
175
+ (k) => !['factId', 'subject', 'conversationId'].includes(k),
176
+ );
177
+ if (unknown.length > 0) return `Unknown field \`${unknown[0]}\``;
178
+ if (b.factId !== undefined) {
179
+ return isId(b.factId) ? { factId: b.factId } : '`factId` must be a non-empty string';
180
+ }
181
+ if (b.conversationId !== undefined) {
182
+ return isId(b.conversationId)
183
+ ? { conversationId: b.conversationId }
184
+ : '`conversationId` must be a non-empty string';
185
+ }
186
+ const s = b.subject as { kind?: unknown; id?: unknown } | null;
187
+ if (s === null || typeof s !== 'object') return '`subject` must be `{kind, id}`';
188
+ if (s.kind === 'user') {
189
+ return "Erasing a Kindgi user isn't offered: `subject.kind` must be `participant` (an app's end user) or `external`";
190
+ }
191
+ if (!SUBJECT_KINDS.includes(s.kind as (typeof SUBJECT_KINDS)[number])) {
192
+ return '`subject.kind` must be `participant` or `external`';
193
+ }
194
+ if (!isId(s.id)) return '`subject.id` must be a non-empty string';
195
+ return { subject: { kind: s.kind as (typeof SUBJECT_KINDS)[number], id: s.id } };
196
+ }
197
+
198
+ /** `{erasures: MemoryErasureLedgerEntry[]}`, as `GET /export` gave it; or why not. */
199
+ function parseLedger(body: unknown): MemoryErasureLedgerEntry[] | string {
200
+ const list = (body as { erasures?: unknown } | null)?.erasures;
201
+ if (!Array.isArray(list)) return 'The body must be `{erasures: [...]}`, as the export gave them';
202
+ if (list.length > MAX_LEDGER) return `At most ${MAX_LEDGER} erasures per replay`;
203
+ const out: MemoryErasureLedgerEntry[] = [];
204
+ for (const [i, raw] of list.entries()) {
205
+ const e = raw as Record<string, unknown> | null;
206
+ const at = `erasures[${i}]`;
207
+ if (e === null || typeof e !== 'object') return `${at} must be an object`;
208
+ if (typeof e.id !== 'string' || !UUID.test(e.id)) return `${at}.id must be a uuid`;
209
+ if (!SELECTOR_KINDS.includes(e.selectorKind as MemoryErasureSelectorKind)) {
210
+ return `${at}.selectorKind must be one of ${SELECTOR_KINDS.join(', ')}`;
211
+ }
212
+ if (
213
+ e.selectorHmac !== undefined &&
214
+ (typeof e.selectorHmac !== 'string' || !/^[0-9a-f]{64}$/.test(e.selectorHmac))
215
+ ) {
216
+ return `${at}.selectorHmac must be 64 hex characters`;
217
+ }
218
+ if (e.keyId !== undefined && !isId(e.keyId)) return `${at}.keyId must be a string`;
219
+ if (!isId(e.requestedBy)) return `${at}.requestedBy must be a string`;
220
+ if (!STATUSES.includes(e.status as (typeof STATUSES)[number])) {
221
+ return `${at}.status must be one of ${STATUSES.join(', ')}`;
222
+ }
223
+ for (const k of ['createdAt', 'completedAt'] as const) {
224
+ const v = e[k];
225
+ if (v === undefined && k === 'completedAt') continue;
226
+ if (typeof v !== 'string' || Number.isNaN(Date.parse(v))) return `${at}.${k} must be a time`;
227
+ }
228
+ out.push({
229
+ id: e.id,
230
+ selectorKind: e.selectorKind as MemoryErasureSelectorKind,
231
+ ...(typeof e.selectorHmac === 'string' && { selectorHmac: e.selectorHmac }),
232
+ ...(typeof e.keyId === 'string' && { keyId: e.keyId }),
233
+ requestedBy: e.requestedBy as string,
234
+ status: e.status as (typeof STATUSES)[number],
235
+ createdAt: e.createdAt as string,
236
+ ...(typeof e.completedAt === 'string' && { completedAt: e.completedAt }),
237
+ });
238
+ }
239
+ return out;
240
+ }
@@ -0,0 +1,250 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+
4
+ /** Request parsing for `/v1/memory`: fact writes, supersedes, retrieve intents and query values. */
5
+
6
+ import type { FactSubject, MemoryScope, Retention } from '@kindgi/memory';
7
+ import type { Timestamp } from '@kindgi/types';
8
+
9
+ import type { MemoryRetrieveIntent } from '../memory-binding.js';
10
+
11
+ export type ValidationResult<T> =
12
+ | { readonly kind: 'ok'; readonly value: T }
13
+ | { readonly kind: 'err'; readonly message: string };
14
+
15
+ const ok = <T>(value: T): ValidationResult<T> => ({ kind: 'ok', value });
16
+ const err = <T>(message: string): ValidationResult<T> => ({ kind: 'err', message });
17
+
18
+ const MAX_SUBJECTS = 20;
19
+
20
+ export function parseScopeParam(raw: string): Partial<MemoryScope> | null {
21
+ try {
22
+ const decoded = JSON.parse(raw) as unknown;
23
+ if (decoded === null || typeof decoded !== 'object' || Array.isArray(decoded)) return null;
24
+ return decoded as Partial<MemoryScope>;
25
+ } catch {
26
+ return null;
27
+ }
28
+ }
29
+
30
+ /** An ISO 8601 time, as a query value or a body field. */
31
+ export function parseTime(raw: unknown, field: string): ValidationResult<Timestamp | undefined> {
32
+ if (raw === undefined) return ok(undefined);
33
+ if (typeof raw !== 'string' || Number.isNaN(Date.parse(raw))) {
34
+ return err(`\`${field}\` must be an ISO 8601 time`);
35
+ }
36
+ return ok(new Date(raw).toISOString() as Timestamp);
37
+ }
38
+
39
+ /** A revision number: a whole number of 1 or more. */
40
+ export function parseVersion(raw: unknown, field: string): ValidationResult<number | undefined> {
41
+ if (raw === undefined) return ok(undefined);
42
+ const n = typeof raw === 'string' && raw.trim() !== '' ? Number(raw) : raw;
43
+ if (typeof n !== 'number' || !Number.isInteger(n) || n < 1) {
44
+ return err(`\`${field}\` must be a revision number (1 or more)`);
45
+ }
46
+ return ok(n);
47
+ }
48
+
49
+ function parseScope(raw: unknown): ValidationResult<MemoryScope> {
50
+ if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
51
+ return err('Field `scope` must be an object');
52
+ }
53
+ const scope = raw as Record<string, unknown>;
54
+ if (typeof scope.tenantId !== 'string') return err('Field `scope.tenantId` must be a string');
55
+ if (
56
+ scope.participantId !== undefined &&
57
+ (typeof scope.participantId !== 'string' || scope.participantId.length === 0)
58
+ ) {
59
+ return err('Field `scope.participantId` must be a non-empty string');
60
+ }
61
+ if (scope.participantId !== undefined && scope.projectId === undefined) {
62
+ return err(
63
+ "`scope.participantId` needs `scope.projectId`: an app's end users belong to a project",
64
+ );
65
+ }
66
+ return ok(scope as unknown as MemoryScope);
67
+ }
68
+
69
+ function parseRetention(raw: unknown): ValidationResult<Retention | undefined> {
70
+ if (raw === undefined) return ok(undefined);
71
+ if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
72
+ return err('Field `retention` must be an object when present');
73
+ }
74
+ const r = raw as Record<string, unknown>;
75
+ const keepUntil = parseTime(r.keepUntil, 'retention.keepUntil');
76
+ if (keepUntil.kind === 'err') return keepUntil;
77
+ if (
78
+ r.keepDays !== undefined &&
79
+ (typeof r.keepDays !== 'number' || !Number.isInteger(r.keepDays) || r.keepDays < 1)
80
+ ) {
81
+ return err('`retention.keepDays` must be a whole number of days (1 or more)');
82
+ }
83
+ if (r.legalHold !== undefined && typeof r.legalHold !== 'boolean') {
84
+ return err('`retention.legalHold` must be true or false');
85
+ }
86
+ return ok(r as Retention);
87
+ }
88
+
89
+ function parseSubjects(raw: unknown): ValidationResult<readonly FactSubject[] | undefined> {
90
+ if (raw === undefined) return ok(undefined);
91
+ const shape =
92
+ "Field `subjects` is a list of whom the fact is about: [{ kind: 'participant' | 'user' | 'external', id }]";
93
+ if (!Array.isArray(raw) || raw.length > MAX_SUBJECTS) {
94
+ return err(`${shape}, at most ${MAX_SUBJECTS}`);
95
+ }
96
+ for (const s of raw) {
97
+ const o = s as Record<string, unknown> | null;
98
+ if (
99
+ o === null ||
100
+ typeof o !== 'object' ||
101
+ !['participant', 'user', 'external'].includes(o.kind as string) ||
102
+ typeof o.id !== 'string' ||
103
+ o.id.length === 0 ||
104
+ Object.keys(o).length !== 2
105
+ ) {
106
+ return err(shape);
107
+ }
108
+ }
109
+ return ok(raw as FactSubject[]);
110
+ }
111
+
112
+ /** The fields a write and a supersede share: retention, subjects and the times. */
113
+ export interface ValidatedFactFields {
114
+ readonly retention?: Retention;
115
+ readonly subjects?: readonly FactSubject[];
116
+ readonly validFrom?: Timestamp;
117
+ readonly validUntil?: Timestamp;
118
+ readonly observedAt?: Timestamp;
119
+ }
120
+
121
+ function parseFactFields(body: Record<string, unknown>): ValidationResult<ValidatedFactFields> {
122
+ const retention = parseRetention(body.retention);
123
+ if (retention.kind === 'err') return retention;
124
+ const subjects = parseSubjects(body.subjects);
125
+ if (subjects.kind === 'err') return subjects;
126
+ const times: Record<string, Timestamp> = {};
127
+ for (const field of ['validFrom', 'validUntil', 'observedAt'] as const) {
128
+ const t = parseTime(body[field], field);
129
+ if (t.kind === 'err') return t;
130
+ if (t.value !== undefined) times[field] = t.value;
131
+ }
132
+ if (
133
+ times.validFrom !== undefined &&
134
+ times.validUntil !== undefined &&
135
+ times.validFrom > times.validUntil
136
+ ) {
137
+ return err('`validFrom` must not be after `validUntil`');
138
+ }
139
+ return ok({
140
+ ...(retention.value !== undefined && { retention: retention.value }),
141
+ ...(subjects.value !== undefined && { subjects: subjects.value }),
142
+ ...times,
143
+ });
144
+ }
145
+
146
+ export interface ValidatedWriteFactBody extends ValidatedFactFields {
147
+ readonly type: string;
148
+ readonly scope: MemoryScope;
149
+ readonly content: unknown;
150
+ readonly contentHash?: string;
151
+ }
152
+
153
+ export function validateWriteFactBody(
154
+ body: Record<string, unknown>,
155
+ ): ValidationResult<ValidatedWriteFactBody> {
156
+ const type = body.type;
157
+ if (typeof type !== 'string' || type.length === 0) {
158
+ return err('Field `type` must be a non-empty string');
159
+ }
160
+ const scope = parseScope(body.scope);
161
+ if (scope.kind === 'err') return scope;
162
+ if (!('content' in body)) return err('Field `content` is required');
163
+ const rawContentHash = body.contentHash;
164
+ if (
165
+ rawContentHash !== undefined &&
166
+ (typeof rawContentHash !== 'string' || rawContentHash.length === 0)
167
+ ) {
168
+ return err('Field `contentHash` must be a non-empty string when present');
169
+ }
170
+ const fields = parseFactFields(body);
171
+ if (fields.kind === 'err') return fields;
172
+ return ok({
173
+ type,
174
+ scope: scope.value,
175
+ content: body.content,
176
+ ...(typeof rawContentHash === 'string' && { contentHash: rawContentHash }),
177
+ ...fields.value,
178
+ });
179
+ }
180
+
181
+ export interface ValidatedSupersedeBody extends ValidatedFactFields {
182
+ readonly content: unknown;
183
+ readonly expectVersion?: number;
184
+ }
185
+
186
+ export function validateSupersedeBody(
187
+ body: Record<string, unknown>,
188
+ ): ValidationResult<ValidatedSupersedeBody> {
189
+ if (!('content' in body)) {
190
+ return err("Field `content` is required: the fact's next revision");
191
+ }
192
+ const expectVersion = parseVersion(body.expectVersion, 'expectVersion');
193
+ if (expectVersion.kind === 'err') return expectVersion;
194
+ const fields = parseFactFields(body);
195
+ if (fields.kind === 'err') return fields;
196
+ return ok({
197
+ content: body.content,
198
+ ...(expectVersion.value !== undefined && { expectVersion: expectVersion.value }),
199
+ ...fields.value,
200
+ });
201
+ }
202
+
203
+ export function validateRetrieveIntent(
204
+ body: Record<string, unknown>,
205
+ ): ValidationResult<MemoryRetrieveIntent> {
206
+ const mode = body.mode;
207
+ if (mode !== 'list' && mode !== 'keyword' && mode !== 'semantic' && mode !== 'both') {
208
+ return err('Field `mode` must be one of "list", "keyword", "semantic", "both"');
209
+ }
210
+ const rawQuery = body.query;
211
+ if (rawQuery !== undefined && typeof rawQuery !== 'string') {
212
+ return err('Field `query` must be a string when present');
213
+ }
214
+ if ((mode === 'keyword' || mode === 'semantic' || mode === 'both') && rawQuery === undefined) {
215
+ return err(`Field \`query\` is required when \`mode\` is "${mode}"`);
216
+ }
217
+ const rawType = body.type;
218
+ if (rawType !== undefined && (typeof rawType !== 'string' || rawType.length === 0)) {
219
+ return err('Field `type` must be a non-empty string when present');
220
+ }
221
+ const rawScope = body.scope;
222
+ if (
223
+ rawScope !== undefined &&
224
+ (rawScope === null || typeof rawScope !== 'object' || Array.isArray(rawScope))
225
+ ) {
226
+ return err('Field `scope` must be an object when present');
227
+ }
228
+ const rawLimit = body.limit;
229
+ if (
230
+ rawLimit !== undefined &&
231
+ (typeof rawLimit !== 'number' || !Number.isFinite(rawLimit) || rawLimit < 1)
232
+ ) {
233
+ return err('Field `limit` must be a positive number when present');
234
+ }
235
+ const rawEmbeddingModel = body.embeddingModel;
236
+ if (
237
+ rawEmbeddingModel !== undefined &&
238
+ (typeof rawEmbeddingModel !== 'string' || rawEmbeddingModel.length === 0)
239
+ ) {
240
+ return err('Field `embeddingModel` must be a non-empty string when present');
241
+ }
242
+ return ok({
243
+ mode,
244
+ ...(typeof rawQuery === 'string' && { query: rawQuery }),
245
+ ...(typeof rawType === 'string' && { type: rawType }),
246
+ ...(rawScope !== undefined && { scope: rawScope as Partial<MemoryScope> }),
247
+ ...(typeof rawLimit === 'number' && { limit: Math.floor(rawLimit) }),
248
+ ...(typeof rawEmbeddingModel === 'string' && { embeddingModel: rawEmbeddingModel }),
249
+ });
250
+ }