@kindgi/api 0.1.4 → 0.1.5-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (507) hide show
  1. package/dist/agent-binding.d.ts +13 -0
  2. package/dist/agent-binding.d.ts.map +1 -1
  3. package/dist/agent-pins.d.ts.map +1 -1
  4. package/dist/agent-pins.js +18 -2
  5. package/dist/agent-pins.js.map +1 -1
  6. package/dist/app.d.ts +110 -17
  7. package/dist/app.d.ts.map +1 -1
  8. package/dist/app.js +163 -35
  9. package/dist/app.js.map +1 -1
  10. package/dist/block-publish.d.ts +56 -0
  11. package/dist/block-publish.d.ts.map +1 -0
  12. package/dist/block-publish.js +132 -0
  13. package/dist/block-publish.js.map +1 -0
  14. package/dist/caller.d.ts +24 -0
  15. package/dist/caller.d.ts.map +1 -0
  16. package/dist/caller.js +47 -0
  17. package/dist/caller.js.map +1 -0
  18. package/dist/capability-binding.d.ts +11 -0
  19. package/dist/capability-binding.d.ts.map +1 -1
  20. package/dist/derive-agent-version.d.ts +7 -0
  21. package/dist/derive-agent-version.d.ts.map +1 -1
  22. package/dist/derive-agent-version.js +4 -0
  23. package/dist/derive-agent-version.js.map +1 -1
  24. package/dist/errors.d.ts.map +1 -1
  25. package/dist/errors.js +73 -7
  26. package/dist/errors.js.map +1 -1
  27. package/dist/eval-case-binding.d.ts +16 -4
  28. package/dist/eval-case-binding.d.ts.map +1 -1
  29. package/dist/eval-run-binding.d.ts +31 -0
  30. package/dist/eval-run-binding.d.ts.map +1 -1
  31. package/dist/eval-run-dispatcher.d.ts +7 -0
  32. package/dist/eval-run-dispatcher.d.ts.map +1 -1
  33. package/dist/eval-run-dispatcher.js.map +1 -1
  34. package/dist/eval-sample.d.ts +11 -0
  35. package/dist/eval-sample.d.ts.map +1 -0
  36. package/dist/eval-sample.js +34 -0
  37. package/dist/eval-sample.js.map +1 -0
  38. package/dist/eval-suite-binding.d.ts +13 -0
  39. package/dist/eval-suite-binding.d.ts.map +1 -1
  40. package/dist/eval-suite-binding.js.map +1 -1
  41. package/dist/flow-binding.d.ts +18 -0
  42. package/dist/flow-binding.d.ts.map +1 -1
  43. package/dist/flow-pins.d.ts.map +1 -1
  44. package/dist/flow-pins.js +18 -2
  45. package/dist/flow-pins.js.map +1 -1
  46. package/dist/gate.d.ts.map +1 -1
  47. package/dist/gate.js +25 -0
  48. package/dist/gate.js.map +1 -1
  49. package/dist/handler-binding.d.ts +32 -0
  50. package/dist/handler-binding.d.ts.map +1 -1
  51. package/dist/hitl-binding.d.ts +23 -0
  52. package/dist/hitl-binding.d.ts.map +1 -1
  53. package/dist/identity-directory-binding.d.ts +82 -4
  54. package/dist/identity-directory-binding.d.ts.map +1 -1
  55. package/dist/identity-provider-binding.d.ts +197 -17
  56. package/dist/identity-provider-binding.d.ts.map +1 -1
  57. package/dist/improvement-pass-binding.d.ts +196 -0
  58. package/dist/improvement-pass-binding.d.ts.map +1 -0
  59. package/dist/improvement-pass-binding.js +9 -0
  60. package/dist/improvement-pass-binding.js.map +1 -0
  61. package/dist/index.d.ts +35 -17
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +11 -3
  64. package/dist/index.js.map +1 -1
  65. package/dist/judged-dispatcher.d.ts +25 -4
  66. package/dist/judged-dispatcher.d.ts.map +1 -1
  67. package/dist/judged-dispatcher.js +43 -6
  68. package/dist/judged-dispatcher.js.map +1 -1
  69. package/dist/judgment-binding.d.ts +25 -1
  70. package/dist/judgment-binding.d.ts.map +1 -1
  71. package/dist/mcp-endpoint-binding.d.ts +7 -0
  72. package/dist/mcp-endpoint-binding.d.ts.map +1 -1
  73. package/dist/memory-binding.d.ts +124 -17
  74. package/dist/memory-binding.d.ts.map +1 -1
  75. package/dist/memory-erasure-binding.d.ts +145 -0
  76. package/dist/memory-erasure-binding.d.ts.map +1 -0
  77. package/dist/memory-erasure-binding.js +4 -0
  78. package/dist/memory-erasure-binding.js.map +1 -0
  79. package/dist/middleware/auth.d.ts +66 -13
  80. package/dist/middleware/auth.d.ts.map +1 -1
  81. package/dist/middleware/auth.js +150 -50
  82. package/dist/middleware/auth.js.map +1 -1
  83. package/dist/middleware/authorize.d.ts +6 -0
  84. package/dist/middleware/authorize.d.ts.map +1 -1
  85. package/dist/middleware/authorize.js +113 -4
  86. package/dist/middleware/authorize.js.map +1 -1
  87. package/dist/middleware/error-mapper.d.ts +5 -0
  88. package/dist/middleware/error-mapper.d.ts.map +1 -1
  89. package/dist/middleware/error-mapper.js +14 -1
  90. package/dist/middleware/error-mapper.js.map +1 -1
  91. package/dist/middleware/idempotency.d.ts +70 -5
  92. package/dist/middleware/idempotency.d.ts.map +1 -1
  93. package/dist/middleware/idempotency.js +174 -41
  94. package/dist/middleware/idempotency.js.map +1 -1
  95. package/dist/middleware/key-project.d.ts +17 -0
  96. package/dist/middleware/key-project.d.ts.map +1 -0
  97. package/dist/middleware/key-project.js +75 -0
  98. package/dist/middleware/key-project.js.map +1 -0
  99. package/dist/middleware/principal.d.ts.map +1 -1
  100. package/dist/middleware/principal.js +9 -5
  101. package/dist/middleware/principal.js.map +1 -1
  102. package/dist/middleware/project-ref.d.ts.map +1 -1
  103. package/dist/middleware/project-ref.js +3 -0
  104. package/dist/middleware/project-ref.js.map +1 -1
  105. package/dist/openapi/generate.d.ts.map +1 -1
  106. package/dist/openapi/generate.js +5 -2
  107. package/dist/openapi/generate.js.map +1 -1
  108. package/dist/openapi/operations.d.ts.map +1 -1
  109. package/dist/openapi/operations.js +918 -139
  110. package/dist/openapi/operations.js.map +1 -1
  111. package/dist/openapi/schemas.d.ts +90 -13
  112. package/dist/openapi/schemas.d.ts.map +1 -1
  113. package/dist/openapi/schemas.js +2081 -359
  114. package/dist/openapi/schemas.js.map +1 -1
  115. package/dist/person-grants-binding.d.ts +77 -0
  116. package/dist/person-grants-binding.d.ts.map +1 -0
  117. package/dist/person-grants-binding.js +4 -0
  118. package/dist/person-grants-binding.js.map +1 -0
  119. package/dist/proposal-service.d.ts +121 -0
  120. package/dist/proposal-service.d.ts.map +1 -0
  121. package/dist/proposal-service.js +447 -0
  122. package/dist/proposal-service.js.map +1 -0
  123. package/dist/proposal-status.d.ts +60 -0
  124. package/dist/proposal-status.d.ts.map +1 -0
  125. package/dist/proposal-status.js +109 -0
  126. package/dist/proposal-status.js.map +1 -0
  127. package/dist/provider-binding.d.ts +10 -0
  128. package/dist/provider-binding.d.ts.map +1 -1
  129. package/dist/publish-refused.d.ts +18 -3
  130. package/dist/publish-refused.d.ts.map +1 -1
  131. package/dist/publish-refused.js +18 -3
  132. package/dist/publish-refused.js.map +1 -1
  133. package/dist/routes/adapters.d.ts +2 -1
  134. package/dist/routes/adapters.d.ts.map +1 -1
  135. package/dist/routes/adapters.js +3 -1
  136. package/dist/routes/adapters.js.map +1 -1
  137. package/dist/routes/agent-releases.d.ts +55 -3
  138. package/dist/routes/agent-releases.d.ts.map +1 -1
  139. package/dist/routes/agent-releases.js +85 -55
  140. package/dist/routes/agent-releases.js.map +1 -1
  141. package/dist/routes/agents.d.ts +12 -1
  142. package/dist/routes/agents.d.ts.map +1 -1
  143. package/dist/routes/agents.js +100 -2
  144. package/dist/routes/agents.js.map +1 -1
  145. package/dist/routes/approvals.d.ts +15 -3
  146. package/dist/routes/approvals.d.ts.map +1 -1
  147. package/dist/routes/approvals.js +133 -122
  148. package/dist/routes/approvals.js.map +1 -1
  149. package/dist/routes/artifacts.d.ts +25 -3
  150. package/dist/routes/artifacts.d.ts.map +1 -1
  151. package/dist/routes/artifacts.js +116 -7
  152. package/dist/routes/artifacts.js.map +1 -1
  153. package/dist/routes/auth.d.ts +25 -1
  154. package/dist/routes/auth.d.ts.map +1 -1
  155. package/dist/routes/auth.js +655 -281
  156. package/dist/routes/auth.js.map +1 -1
  157. package/dist/routes/blocks.d.ts.map +1 -1
  158. package/dist/routes/blocks.js +2 -42
  159. package/dist/routes/blocks.js.map +1 -1
  160. package/dist/routes/capabilities.d.ts +2 -1
  161. package/dist/routes/capabilities.d.ts.map +1 -1
  162. package/dist/routes/capabilities.js +6 -1
  163. package/dist/routes/capabilities.js.map +1 -1
  164. package/dist/routes/compliance.d.ts +9 -10
  165. package/dist/routes/compliance.d.ts.map +1 -1
  166. package/dist/routes/compliance.js +42 -61
  167. package/dist/routes/compliance.js.map +1 -1
  168. package/dist/routes/conversations.d.ts +8 -1
  169. package/dist/routes/conversations.d.ts.map +1 -1
  170. package/dist/routes/conversations.js +87 -8
  171. package/dist/routes/conversations.js.map +1 -1
  172. package/dist/routes/cost.d.ts +7 -1
  173. package/dist/routes/cost.d.ts.map +1 -1
  174. package/dist/routes/cost.js +22 -3
  175. package/dist/routes/cost.js.map +1 -1
  176. package/dist/routes/denied.d.ts +11 -0
  177. package/dist/routes/denied.d.ts.map +1 -0
  178. package/dist/routes/denied.js +17 -0
  179. package/dist/routes/denied.js.map +1 -0
  180. package/dist/routes/deployments.d.ts +2 -1
  181. package/dist/routes/deployments.d.ts.map +1 -1
  182. package/dist/routes/deployments.js +44 -12
  183. package/dist/routes/deployments.js.map +1 -1
  184. package/dist/routes/eval-comparison.d.ts.map +1 -1
  185. package/dist/routes/eval-comparison.js +69 -1
  186. package/dist/routes/eval-comparison.js.map +1 -1
  187. package/dist/routes/eval-overrides.d.ts +18 -0
  188. package/dist/routes/eval-overrides.d.ts.map +1 -0
  189. package/dist/routes/eval-overrides.js +81 -0
  190. package/dist/routes/eval-overrides.js.map +1 -0
  191. package/dist/routes/eval-runs.d.ts +10 -1
  192. package/dist/routes/eval-runs.d.ts.map +1 -1
  193. package/dist/routes/eval-runs.js +63 -7
  194. package/dist/routes/eval-runs.js.map +1 -1
  195. package/dist/routes/eval-suites.d.ts.map +1 -1
  196. package/dist/routes/eval-suites.js +20 -1
  197. package/dist/routes/eval-suites.js.map +1 -1
  198. package/dist/routes/event-triggers.d.ts +5 -1
  199. package/dist/routes/event-triggers.d.ts.map +1 -1
  200. package/dist/routes/event-triggers.js +26 -3
  201. package/dist/routes/event-triggers.js.map +1 -1
  202. package/dist/routes/export-signing-keys.d.ts +12 -0
  203. package/dist/routes/export-signing-keys.d.ts.map +1 -0
  204. package/dist/routes/export-signing-keys.js +24 -0
  205. package/dist/routes/export-signing-keys.js.map +1 -0
  206. package/dist/routes/flows.d.ts.map +1 -1
  207. package/dist/routes/flows.js +9 -1
  208. package/dist/routes/flows.js.map +1 -1
  209. package/dist/routes/gate-policies.d.ts +4 -1
  210. package/dist/routes/gate-policies.d.ts.map +1 -1
  211. package/dist/routes/gate-policies.js +29 -5
  212. package/dist/routes/gate-policies.js.map +1 -1
  213. package/dist/routes/guardrails.d.ts +16 -14
  214. package/dist/routes/guardrails.d.ts.map +1 -1
  215. package/dist/routes/guardrails.js +39 -3
  216. package/dist/routes/guardrails.js.map +1 -1
  217. package/dist/routes/identity.d.ts +20 -4
  218. package/dist/routes/identity.d.ts.map +1 -1
  219. package/dist/routes/identity.js +246 -6
  220. package/dist/routes/identity.js.map +1 -1
  221. package/dist/routes/improvement-passes.d.ts +52 -0
  222. package/dist/routes/improvement-passes.d.ts.map +1 -0
  223. package/dist/routes/improvement-passes.js +449 -0
  224. package/dist/routes/improvement-passes.js.map +1 -0
  225. package/dist/routes/judged-suites.d.ts +50 -0
  226. package/dist/routes/judged-suites.d.ts.map +1 -1
  227. package/dist/routes/judged-suites.js +66 -27
  228. package/dist/routes/judged-suites.js.map +1 -1
  229. package/dist/routes/judgment-context.d.ts +18 -2
  230. package/dist/routes/judgment-context.d.ts.map +1 -1
  231. package/dist/routes/judgment-context.js +63 -5
  232. package/dist/routes/judgment-context.js.map +1 -1
  233. package/dist/routes/judgment-flow-context.d.ts.map +1 -1
  234. package/dist/routes/judgment-flow-context.js +14 -2
  235. package/dist/routes/judgment-flow-context.js.map +1 -1
  236. package/dist/routes/judgments.d.ts.map +1 -1
  237. package/dist/routes/judgments.js +8 -1
  238. package/dist/routes/judgments.js.map +1 -1
  239. package/dist/routes/mcp.d.ts.map +1 -1
  240. package/dist/routes/mcp.js +26 -1
  241. package/dist/routes/mcp.js.map +1 -1
  242. package/dist/routes/memory-access.d.ts +43 -0
  243. package/dist/routes/memory-access.d.ts.map +1 -0
  244. package/dist/routes/memory-access.js +129 -0
  245. package/dist/routes/memory-access.js.map +1 -0
  246. package/dist/routes/memory-erasures.d.ts +25 -0
  247. package/dist/routes/memory-erasures.d.ts.map +1 -0
  248. package/dist/routes/memory-erasures.js +214 -0
  249. package/dist/routes/memory-erasures.js.map +1 -0
  250. package/dist/routes/memory-parse.d.ts +38 -0
  251. package/dist/routes/memory-parse.d.ts.map +1 -0
  252. package/dist/routes/memory-parse.js +199 -0
  253. package/dist/routes/memory-parse.js.map +1 -0
  254. package/dist/routes/memory.d.ts +9 -4
  255. package/dist/routes/memory.d.ts.map +1 -1
  256. package/dist/routes/memory.js +226 -234
  257. package/dist/routes/memory.js.map +1 -1
  258. package/dist/routes/observations.d.ts +4 -1
  259. package/dist/routes/observations.d.ts.map +1 -1
  260. package/dist/routes/observations.js +8 -2
  261. package/dist/routes/observations.js.map +1 -1
  262. package/dist/routes/pagination.d.ts +7 -0
  263. package/dist/routes/pagination.d.ts.map +1 -1
  264. package/dist/routes/pagination.js +10 -0
  265. package/dist/routes/pagination.js.map +1 -1
  266. package/dist/routes/policies.d.ts +2 -1
  267. package/dist/routes/policies.d.ts.map +1 -1
  268. package/dist/routes/policies.js +3 -1
  269. package/dist/routes/policies.js.map +1 -1
  270. package/dist/routes/project-mismatch.d.ts +22 -0
  271. package/dist/routes/project-mismatch.d.ts.map +1 -0
  272. package/dist/routes/project-mismatch.js +34 -0
  273. package/dist/routes/project-mismatch.js.map +1 -0
  274. package/dist/routes/projects.d.ts +15 -4
  275. package/dist/routes/projects.d.ts.map +1 -1
  276. package/dist/routes/projects.js +100 -9
  277. package/dist/routes/projects.js.map +1 -1
  278. package/dist/routes/proposals.d.ts +25 -17
  279. package/dist/routes/proposals.d.ts.map +1 -1
  280. package/dist/routes/proposals.js +340 -507
  281. package/dist/routes/proposals.js.map +1 -1
  282. package/dist/routes/provenance.d.ts +17 -3
  283. package/dist/routes/provenance.d.ts.map +1 -1
  284. package/dist/routes/provenance.js +79 -103
  285. package/dist/routes/provenance.js.map +1 -1
  286. package/dist/routes/providers.d.ts +17 -1
  287. package/dist/routes/providers.d.ts.map +1 -1
  288. package/dist/routes/providers.js +110 -3
  289. package/dist/routes/providers.js.map +1 -1
  290. package/dist/routes/public-run-tokens.d.ts.map +1 -1
  291. package/dist/routes/public-run-tokens.js +3 -0
  292. package/dist/routes/public-run-tokens.js.map +1 -1
  293. package/dist/routes/runs.d.ts +10 -0
  294. package/dist/routes/runs.d.ts.map +1 -1
  295. package/dist/routes/runs.js +92 -42
  296. package/dist/routes/runs.js.map +1 -1
  297. package/dist/routes/schedules.d.ts +21 -13
  298. package/dist/routes/schedules.d.ts.map +1 -1
  299. package/dist/routes/schedules.js +416 -55
  300. package/dist/routes/schedules.js.map +1 -1
  301. package/dist/routes/segments.d.ts +2 -0
  302. package/dist/routes/segments.d.ts.map +1 -1
  303. package/dist/routes/segments.js +5 -0
  304. package/dist/routes/segments.js.map +1 -1
  305. package/dist/routes/service-accounts.d.ts +17 -0
  306. package/dist/routes/service-accounts.d.ts.map +1 -0
  307. package/dist/routes/service-accounts.js +212 -0
  308. package/dist/routes/service-accounts.js.map +1 -0
  309. package/dist/routes/sign-in-options.d.ts +50 -0
  310. package/dist/routes/sign-in-options.d.ts.map +1 -0
  311. package/dist/routes/sign-in-options.js +98 -0
  312. package/dist/routes/sign-in-options.js.map +1 -0
  313. package/dist/routes/signing-keys.d.ts +2 -1
  314. package/dist/routes/signing-keys.d.ts.map +1 -1
  315. package/dist/routes/signing-keys.js +3 -1
  316. package/dist/routes/signing-keys.js.map +1 -1
  317. package/dist/routes/tenant-access.d.ts +13 -0
  318. package/dist/routes/tenant-access.d.ts.map +1 -0
  319. package/dist/routes/tenant-access.js +26 -0
  320. package/dist/routes/tenant-access.js.map +1 -0
  321. package/dist/routes/tenant.d.ts.map +1 -1
  322. package/dist/routes/tenant.js +4 -0
  323. package/dist/routes/tenant.js.map +1 -1
  324. package/dist/routes/token-sign-in.d.ts +36 -0
  325. package/dist/routes/token-sign-in.d.ts.map +1 -0
  326. package/dist/routes/token-sign-in.js +101 -0
  327. package/dist/routes/token-sign-in.js.map +1 -0
  328. package/dist/routes/tokens.d.ts +15 -5
  329. package/dist/routes/tokens.d.ts.map +1 -1
  330. package/dist/routes/tokens.js +162 -43
  331. package/dist/routes/tokens.js.map +1 -1
  332. package/dist/routes/tools.d.ts.map +1 -1
  333. package/dist/routes/tools.js +9 -1
  334. package/dist/routes/tools.js.map +1 -1
  335. package/dist/routes/trigger-access.d.ts +29 -0
  336. package/dist/routes/trigger-access.d.ts.map +1 -0
  337. package/dist/routes/trigger-access.js +33 -0
  338. package/dist/routes/trigger-access.js.map +1 -0
  339. package/dist/routes/webhook-endpoints.d.ts +2 -1
  340. package/dist/routes/webhook-endpoints.d.ts.map +1 -1
  341. package/dist/routes/webhook-endpoints.js +11 -3
  342. package/dist/routes/webhook-endpoints.js.map +1 -1
  343. package/dist/routes/webhooks.d.ts +5 -1
  344. package/dist/routes/webhooks.d.ts.map +1 -1
  345. package/dist/routes/webhooks.js +25 -2
  346. package/dist/routes/webhooks.js.map +1 -1
  347. package/dist/run-failure.d.ts +21 -0
  348. package/dist/run-failure.d.ts.map +1 -0
  349. package/dist/run-failure.js +29 -0
  350. package/dist/run-failure.js.map +1 -0
  351. package/dist/service-account-binding.d.ts +108 -0
  352. package/dist/service-account-binding.d.ts.map +1 -0
  353. package/dist/service-account-binding.js +4 -0
  354. package/dist/service-account-binding.js.map +1 -0
  355. package/dist/session-store-binding.d.ts +61 -13
  356. package/dist/session-store-binding.d.ts.map +1 -1
  357. package/dist/signed-export.d.ts +89 -0
  358. package/dist/signed-export.d.ts.map +1 -0
  359. package/dist/signed-export.js +149 -0
  360. package/dist/signed-export.js.map +1 -0
  361. package/dist/supervisor-binding.d.ts +186 -413
  362. package/dist/supervisor-binding.d.ts.map +1 -1
  363. package/dist/testing/app-bindings.d.ts +33 -0
  364. package/dist/testing/app-bindings.d.ts.map +1 -0
  365. package/dist/testing/app-bindings.js +112 -0
  366. package/dist/testing/app-bindings.js.map +1 -0
  367. package/dist/testing/index.d.ts +11 -0
  368. package/dist/testing/index.d.ts.map +1 -0
  369. package/dist/testing/index.js +13 -0
  370. package/dist/testing/index.js.map +1 -0
  371. package/dist/testing/stub-binding.d.ts +34 -0
  372. package/dist/testing/stub-binding.d.ts.map +1 -0
  373. package/dist/testing/stub-binding.js +42 -0
  374. package/dist/testing/stub-binding.js.map +1 -0
  375. package/dist/testing/trigger-registry.d.ts +21 -0
  376. package/dist/testing/trigger-registry.d.ts.map +1 -0
  377. package/dist/testing/trigger-registry.js +206 -0
  378. package/dist/testing/trigger-registry.js.map +1 -0
  379. package/dist/token-admin.d.ts +46 -8
  380. package/dist/token-admin.d.ts.map +1 -1
  381. package/dist/token-admin.js.map +1 -1
  382. package/dist/tool-binding.d.ts +13 -0
  383. package/dist/tool-binding.d.ts.map +1 -1
  384. package/dist/trigger-binding.d.ts +2 -2
  385. package/dist/trigger-binding.d.ts.map +1 -1
  386. package/dist/trigger-binding.js +1 -1
  387. package/dist/trigger-binding.js.map +1 -1
  388. package/dist/types.d.ts +16 -0
  389. package/dist/types.d.ts.map +1 -1
  390. package/dist/webhook-endpoint-binding.d.ts +19 -3
  391. package/dist/webhook-endpoint-binding.d.ts.map +1 -1
  392. package/dist/webhook-endpoint-binding.js +1 -1
  393. package/dist/webhook-endpoint-binding.js.map +1 -1
  394. package/openapi.json +15385 -8788
  395. package/package.json +26 -22
  396. package/src/agent-binding.ts +14 -0
  397. package/src/agent-pins.ts +18 -2
  398. package/src/app.ts +366 -80
  399. package/src/block-publish.ts +181 -0
  400. package/src/caller.ts +62 -0
  401. package/src/capability-binding.ts +12 -0
  402. package/src/derive-agent-version.ts +9 -1
  403. package/src/errors.ts +73 -7
  404. package/src/eval-case-binding.ts +17 -1
  405. package/src/eval-run-binding.ts +31 -0
  406. package/src/eval-run-dispatcher.ts +7 -0
  407. package/src/eval-sample.ts +42 -0
  408. package/src/eval-suite-binding.ts +14 -0
  409. package/src/flow-binding.ts +19 -0
  410. package/src/flow-pins.ts +18 -2
  411. package/src/gate.ts +26 -0
  412. package/src/handler-binding.ts +36 -1
  413. package/src/hitl-binding.ts +24 -0
  414. package/src/identity-directory-binding.ts +85 -4
  415. package/src/identity-provider-binding.ts +216 -21
  416. package/src/improvement-pass-binding.ts +193 -0
  417. package/src/index.ts +153 -31
  418. package/src/judged-dispatcher.ts +78 -11
  419. package/src/judgment-binding.ts +25 -1
  420. package/src/mcp-endpoint-binding.ts +7 -0
  421. package/src/memory-binding.ts +121 -17
  422. package/src/memory-erasure-binding.ts +156 -0
  423. package/src/middleware/auth.ts +228 -62
  424. package/src/middleware/authorize.ts +139 -3
  425. package/src/middleware/error-mapper.ts +15 -1
  426. package/src/middleware/idempotency.ts +230 -43
  427. package/src/middleware/key-project.ts +86 -0
  428. package/src/middleware/principal.ts +9 -5
  429. package/src/middleware/project-ref.ts +3 -0
  430. package/src/openapi/generate.ts +11 -2
  431. package/src/openapi/operations.ts +1059 -142
  432. package/src/openapi/schemas.ts +2387 -469
  433. package/src/person-grants-binding.ts +76 -0
  434. package/src/proposal-service.ts +640 -0
  435. package/src/proposal-status.ts +176 -0
  436. package/src/provider-binding.ts +10 -0
  437. package/src/publish-refused.ts +38 -9
  438. package/src/routes/adapters.ts +4 -0
  439. package/src/routes/agent-releases.ts +121 -69
  440. package/src/routes/agents.ts +128 -1
  441. package/src/routes/approvals.ts +164 -154
  442. package/src/routes/artifacts.ts +181 -11
  443. package/src/routes/auth.ts +804 -336
  444. package/src/routes/blocks.ts +2 -53
  445. package/src/routes/capabilities.ts +10 -1
  446. package/src/routes/compliance.ts +60 -99
  447. package/src/routes/conversations.ts +101 -7
  448. package/src/routes/cost.ts +32 -3
  449. package/src/routes/denied.ts +28 -0
  450. package/src/routes/deployments.ts +62 -16
  451. package/src/routes/eval-comparison.ts +81 -2
  452. package/src/routes/eval-overrides.ts +108 -0
  453. package/src/routes/eval-runs.ts +86 -6
  454. package/src/routes/eval-suites.ts +25 -1
  455. package/src/routes/event-triggers.ts +27 -3
  456. package/src/routes/export-signing-keys.ts +31 -0
  457. package/src/routes/flows.ts +12 -1
  458. package/src/routes/gate-policies.ts +37 -6
  459. package/src/routes/guardrails.ts +61 -2
  460. package/src/routes/identity.ts +328 -10
  461. package/src/routes/improvement-passes.ts +541 -0
  462. package/src/routes/judged-suites.ts +113 -43
  463. package/src/routes/judgment-context.ts +76 -5
  464. package/src/routes/judgment-flow-context.ts +16 -2
  465. package/src/routes/judgments.ts +17 -3
  466. package/src/routes/mcp.ts +29 -1
  467. package/src/routes/memory-access.ts +179 -0
  468. package/src/routes/memory-erasures.ts +240 -0
  469. package/src/routes/memory-parse.ts +250 -0
  470. package/src/routes/memory.ts +244 -286
  471. package/src/routes/observations.ts +15 -2
  472. package/src/routes/pagination.ts +12 -0
  473. package/src/routes/policies.ts +7 -1
  474. package/src/routes/project-mismatch.ts +56 -0
  475. package/src/routes/projects.ts +107 -13
  476. package/src/routes/proposals.ts +370 -611
  477. package/src/routes/provenance.ts +99 -133
  478. package/src/routes/providers.ts +142 -3
  479. package/src/routes/public-run-tokens.ts +3 -0
  480. package/src/routes/runs.ts +119 -39
  481. package/src/routes/schedules.ts +472 -60
  482. package/src/routes/segments.ts +11 -0
  483. package/src/routes/service-accounts.ts +262 -0
  484. package/src/routes/sign-in-options.ts +168 -0
  485. package/src/routes/signing-keys.ts +7 -1
  486. package/src/routes/tenant-access.ts +37 -0
  487. package/src/routes/tenant.ts +6 -0
  488. package/src/routes/token-sign-in.ts +161 -0
  489. package/src/routes/tokens.ts +172 -46
  490. package/src/routes/tools.ts +12 -1
  491. package/src/routes/trigger-access.ts +72 -0
  492. package/src/routes/webhook-endpoints.ts +15 -3
  493. package/src/routes/webhooks.ts +26 -2
  494. package/src/run-failure.ts +42 -0
  495. package/src/service-account-binding.ts +113 -0
  496. package/src/session-store-binding.ts +65 -13
  497. package/src/signed-export.ts +250 -0
  498. package/src/supervisor-binding.ts +169 -409
  499. package/src/testing/app-bindings.ts +142 -0
  500. package/src/testing/index.ts +21 -0
  501. package/src/testing/stub-binding.ts +53 -0
  502. package/src/testing/trigger-registry.ts +259 -0
  503. package/src/token-admin.ts +40 -8
  504. package/src/tool-binding.ts +14 -0
  505. package/src/trigger-binding.ts +8 -1
  506. package/src/types.ts +16 -0
  507. package/src/webhook-endpoint-binding.ts +18 -3
@@ -0,0 +1,640 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+
4
+ import { createHash } from 'node:crypto';
5
+
6
+ import type { AgentId } from '@kindgi/agents';
7
+ import { tuplesForCreate } from '@kindgi/authz';
8
+ import type { ProjectBinding } from '@kindgi/platform';
9
+ import { canonicalize } from '@kindgi/schema';
10
+ import type { LiveScope, RunId, Semver, TenantId, Timestamp, UserId } from '@kindgi/types';
11
+
12
+ import type { AgentRegistryBinding, AgentVersionRecord } from './agent-binding.js';
13
+ import type { BlockRecord, BlockRegistryBinding } from './block-binding.js';
14
+ import { type BlockEdit, blockEditDefinition, publishBlockEdit } from './block-publish.js';
15
+ import { deriveAgentVersion } from './derive-agent-version.js';
16
+ import type { EvalComparison, EvalRunBinding, EvalRunStartOutcome } from './eval-run-binding.js';
17
+ import type { GateApproval } from './gate.js';
18
+ import type {
19
+ AgentReleaseBindings,
20
+ LivePin,
21
+ Promotion,
22
+ PromotionActor,
23
+ } from './live-version-binding.js';
24
+ import {
25
+ type FixProposalStatus,
26
+ PROPOSAL_ACTIONS,
27
+ type ProposalAction,
28
+ type ProposalEvaluationOutcome,
29
+ evaluationOutcome,
30
+ proposalActionAllowed,
31
+ proposalStatus,
32
+ } from './proposal-status.js';
33
+ import { type PromotionRequestOutcome, requestPromotion } from './routes/agent-releases.js';
34
+ import { liveScopeToWire } from './routes/live-scope-wire.js';
35
+ import type {
36
+ ProposalDrafter,
37
+ ProposalEvidence,
38
+ ProposalObjective,
39
+ ProposalStep,
40
+ ProposalTier,
41
+ ProposedChange,
42
+ StoredProposal,
43
+ SupervisorBinding,
44
+ } from './supervisor-binding.js';
45
+
46
+ /**
47
+ * The improvement-proposal lifecycle, from the bindings it runs on: what
48
+ * `/v1/proposals` serves, and what a runtime's improvement pass drafts
49
+ * and evaluates its proposal with. No authorization here: the route
50
+ * checks the caller; a pass acts as the runtime. Each call answers an
51
+ * outcome; an error carries the wire code the route answers with.
52
+ */
53
+ export interface ProposalServiceDeps {
54
+ readonly store: Pick<
55
+ SupervisorBinding,
56
+ 'listProposals' | 'getProposal' | 'createProposal' | 'recordProposal'
57
+ >;
58
+ readonly agents: AgentRegistryBinding;
59
+ readonly blocks: BlockRegistryBinding;
60
+ readonly evalRuns: EvalRunBinding;
61
+ readonly releases: AgentReleaseBindings;
62
+ readonly projects?: ProjectBinding;
63
+ }
64
+
65
+ /** An error with the code the API answers it with (`statusFor`), and its details. */
66
+ export type ProposalServiceError = { readonly code: string; readonly message: string } & Readonly<
67
+ Record<string, unknown>
68
+ >;
69
+
70
+ export type ProposalOutcome<T> =
71
+ | { readonly kind: 'ok'; readonly value: T }
72
+ | { readonly kind: 'err'; readonly error: ProposalServiceError };
73
+
74
+ /** What a proposal's ids resolve to now, and the status that follows. */
75
+ export interface ProposalFacts {
76
+ readonly status: FixProposalStatus;
77
+ readonly evaluation?: ProposalEvaluationOutcome | null;
78
+ readonly promotion?: Promotion | null;
79
+ /** For a promoted proposal: whether its version still serves the scope. */
80
+ readonly liveNow?: boolean;
81
+ }
82
+
83
+ export interface DraftProposalInput {
84
+ readonly tenantId: TenantId;
85
+ readonly agentId: string;
86
+ readonly fromVersion: string;
87
+ readonly scope: LiveScope;
88
+ readonly tier: ProposalTier;
89
+ readonly blockId: string;
90
+ readonly content: ProposedChange['change']['content'];
91
+ readonly hypothesis: string;
92
+ readonly evidence?: ProposalEvidence;
93
+ readonly drafter: ProposalDrafter;
94
+ }
95
+
96
+ export interface EvaluateProposalInput {
97
+ readonly tenantId: TenantId;
98
+ readonly proposal: StoredProposal;
99
+ readonly suiteId: string;
100
+ readonly objective: ProposalObjective;
101
+ readonly comparison?: EvalComparison;
102
+ /** Who it's done for: what the derived version records as `by`. */
103
+ readonly actor: PromotionActor;
104
+ }
105
+
106
+ /**
107
+ * What a promotion that a drafter (not a person) asks for needs even when
108
+ * the scope's policy asks for none: a reviewer (K2). Any reviewer may
109
+ * approve: the drafter, not a person, requested it.
110
+ */
111
+ export const DRAFTED_PROPOSAL_APPROVAL: GateApproval = {
112
+ role: 'standard',
113
+ count: 1,
114
+ separateApprover: false,
115
+ };
116
+
117
+ export function createProposalService(deps: ProposalServiceDeps) {
118
+ const err = (error: ProposalServiceError) => ({ kind: 'err' as const, error });
119
+
120
+ /** A reader of proposal facts for one call: each agent's pins are read once. */
121
+ function facts(tenantId: TenantId): (p: StoredProposal) => Promise<ProposalFacts> {
122
+ const pins = new Map<string, Promise<readonly LivePin[]>>();
123
+ const pinsOf = (agentId: string): Promise<readonly LivePin[]> => {
124
+ const known = pins.get(agentId);
125
+ if (known !== undefined) return known;
126
+ const found = deps.releases.live.list({ tenantId, agentId });
127
+ pins.set(agentId, found);
128
+ return found;
129
+ };
130
+ return async (p) => {
131
+ const promotion =
132
+ p.promotionId === undefined
133
+ ? undefined
134
+ : await deps.releases.promotions.get(tenantId, p.promotionId);
135
+ let evaluation: ProposalEvaluationOutcome | null | undefined;
136
+ if (p.evaluation !== undefined) {
137
+ const run = await deps.evalRuns.get({ tenantId, runId: p.evaluation.evalRunId as RunId });
138
+ evaluation = run === null ? null : evaluationOutcome(p.evaluation.objective, run);
139
+ }
140
+ const status = proposalStatus(p, promotion, evaluation);
141
+ let liveNow: boolean | undefined;
142
+ if (status === 'promoted' && promotion !== undefined && promotion !== null) {
143
+ const all = await pinsOf(p.agentId as unknown as string);
144
+ liveNow = all.some((pin) => pin.promotionId === promotion.id);
145
+ }
146
+ return {
147
+ status,
148
+ ...(evaluation !== undefined && { evaluation }),
149
+ ...(promotion !== undefined && { promotion }),
150
+ ...(liveNow !== undefined && { liveNow }),
151
+ };
152
+ };
153
+ }
154
+
155
+ /** The 409 when the proposal's status doesn't allow `action`. */
156
+ async function refuseStatus(
157
+ action: ProposalAction,
158
+ proposal: StoredProposal,
159
+ seen?: ProposalFacts,
160
+ ): Promise<ProposalServiceError | undefined> {
161
+ const f = seen ?? (await facts(proposal.tenantId)(proposal));
162
+ if (proposalActionAllowed(action, f.status)) return undefined;
163
+ return invalidTransition(
164
+ proposal,
165
+ f.status,
166
+ `it's ${f.status}, and ${action} needs one of: ${PROPOSAL_ACTIONS[action].join(', ')}.`,
167
+ );
168
+ }
169
+
170
+ /** Record a step; the 409 when another call recorded one first. */
171
+ async function record(
172
+ proposal: StoredProposal,
173
+ step: ProposalStep,
174
+ ): Promise<ProposalOutcome<StoredProposal>> {
175
+ const outcome = await deps.store.recordProposal({
176
+ tenantId: proposal.tenantId,
177
+ proposalId: proposal.id,
178
+ expectRevision: proposal.revision,
179
+ step,
180
+ });
181
+ if (outcome.kind === 'ok') return { kind: 'ok', value: outcome.proposal };
182
+ if (outcome.kind === 'not-found')
183
+ return err(proposalNotFound(proposal.id as unknown as string));
184
+ return err({
185
+ code: 'proposal-invalid-state-transition',
186
+ message: `Proposal ${proposal.id as unknown as string} changed while this ran: read it again`,
187
+ proposalId: proposal.id as unknown as string,
188
+ });
189
+ }
190
+
191
+ /** Publish the block version and derive the agent version a proposal is evaluated as. */
192
+ async function makeCandidate(
193
+ proposal: StoredProposal,
194
+ actor: PromotionActor,
195
+ ): Promise<ProposalOutcome<NonNullable<StoredProposal['candidate']>>> {
196
+ const { tenantId } = proposal;
197
+ const { blockId, fromVersion } = proposal.change;
198
+ const from = await deps.blocks.getVersion({ tenantId, blockId, version: fromVersion });
199
+ if (from === null) {
200
+ return err(validationFailed(`Block "${blockId}" version ${fromVersion} is gone`, []));
201
+ }
202
+ const published = await publishBlockEdit({
203
+ blocks: deps.blocks,
204
+ tenantId,
205
+ from,
206
+ edit: editOf(proposal.tier, proposal.change.content),
207
+ });
208
+ if (published.kind === 'invalid') {
209
+ return err(
210
+ validationFailed(
211
+ `The proposal's content can't be published as block "${blockId}"`,
212
+ published.issues,
213
+ ),
214
+ );
215
+ }
216
+ const pinKind = proposal.tier === 'settings-block' ? 'settings' : 'prompts';
217
+ const userId = actor.kind === 'user' ? (actor.id as UserId) : undefined;
218
+ const derived = await deriveAgentVersion({
219
+ agents: deps.agents,
220
+ blocks: deps.blocks,
221
+ tenantId,
222
+ agentId: proposal.agentId,
223
+ from: proposal.fromVersion,
224
+ swaps: { [pinKind]: { [blockId]: published.version } },
225
+ label: `proposal ${proposal.id as unknown as string}`,
226
+ by: `${actor.kind}:${actor.id}`,
227
+ proposalId: proposal.id as unknown as string,
228
+ tuplesFor: (projectId) => (id) =>
229
+ tuplesForCreate(
230
+ { kind: 'agent', id: id as AgentId, tenantId, projectId },
231
+ userId ?? ('00000000-0000-0000-0000-000000000000' as UserId),
232
+ ),
233
+ });
234
+ if (derived.kind !== 'ok' && derived.kind !== 'reused') {
235
+ return err(
236
+ validationFailed(
237
+ derived.kind === 'invalid'
238
+ ? `Can't derive a version from ${proposal.fromVersion}`
239
+ : `Can't derive a version from ${proposal.fromVersion} (${derived.kind})`,
240
+ derived.kind === 'invalid' ? derived.issues : [],
241
+ ),
242
+ );
243
+ }
244
+ const { agent } = derived;
245
+ if (agent.pinsDigest === undefined) throw new Error('a derived version is pinned');
246
+ return {
247
+ kind: 'ok',
248
+ value: {
249
+ agentVersion: agent.version as unknown as string,
250
+ blockVersion: published.version,
251
+ pinsDigest: agent.pinsDigest,
252
+ },
253
+ };
254
+ }
255
+
256
+ return {
257
+ facts,
258
+
259
+ /**
260
+ * Store a proposal: new content for a block `fromVersion` pins, checked
261
+ * as publishing that block version would be. `deduped` when the same
262
+ * change from the same version for the same scope is open already.
263
+ */
264
+ async draft(
265
+ input: DraftProposalInput,
266
+ ): Promise<ProposalOutcome<{ proposal: StoredProposal; deduped: boolean }>> {
267
+ const { tenantId } = input;
268
+ const from = await deps.agents.getVersion({
269
+ tenantId,
270
+ agentId: input.agentId as AgentId,
271
+ version: input.fromVersion as Semver,
272
+ });
273
+ if (from === null || from.unregisteredAt !== undefined) {
274
+ return err({
275
+ code: 'agent-version-not-found',
276
+ message: `${input.agentId} has no active version ${input.fromVersion}`,
277
+ });
278
+ }
279
+ const pinned = await pinnedBlock(deps.blocks, tenantId, from, input.tier, input.blockId);
280
+ if (pinned.kind === 'invalid') return err(validationFailed(pinned.message, pinned.issues));
281
+ const edit = editOf(input.tier, input.content);
282
+ const checked = await blockEditDefinition(
283
+ { blocks: deps.blocks, tenantId, from: pinned.block, edit },
284
+ pinned.block.version,
285
+ );
286
+ if (checked.kind === 'invalid') {
287
+ return err(
288
+ validationFailed(
289
+ `The new content isn't a valid version of block "${input.blockId}"`,
290
+ checked.issues,
291
+ ),
292
+ );
293
+ }
294
+ if (sameContent(pinned.block, edit)) {
295
+ return err(
296
+ validationFailed(`The proposal doesn't change block "${input.blockId}"`, [
297
+ {
298
+ path: '/change/content',
299
+ message: `equals version ${pinned.block.version}, which ${input.fromVersion} pins`,
300
+ },
301
+ ]),
302
+ );
303
+ }
304
+ const change = {
305
+ blockId: input.blockId,
306
+ fromVersion: pinned.block.version,
307
+ content: input.content,
308
+ } as ProposedChange['change'];
309
+ const outcome = await deps.store.createProposal({
310
+ tenantId,
311
+ ...(from.projectId !== undefined && { projectId: from.projectId }),
312
+ agentId: input.agentId as AgentId,
313
+ fromVersion: input.fromVersion,
314
+ scope: input.scope,
315
+ tier: input.tier,
316
+ change,
317
+ hypothesis: input.hypothesis,
318
+ ...(input.evidence !== undefined && { evidence: input.evidence }),
319
+ drafter: input.drafter,
320
+ fingerprint: fingerprintOf(input, change),
321
+ });
322
+ return {
323
+ kind: 'ok',
324
+ value: { proposal: outcome.proposal, deduped: outcome.kind === 'dedup' },
325
+ };
326
+ },
327
+
328
+ /**
329
+ * Publish the proposal's block version and derive its agent version
330
+ * (once: an evaluation again reuses them), then start the comparison.
331
+ * Needs a writable agent registry and a tenant-wide live version.
332
+ */
333
+ async evaluate(input: EvaluateProposalInput): Promise<ProposalOutcome<StoredProposal>> {
334
+ const { tenantId } = input;
335
+ let proposal = input.proposal;
336
+ const agentId = proposal.agentId as unknown as string;
337
+ const refused = await refuseStatus('evaluate', proposal);
338
+ if (refused !== undefined) return err(refused);
339
+ // Evaluating derives an agent version: not into a registry that takes no writes.
340
+ if (deps.agents.readOnly !== undefined) {
341
+ return err({ code: 'registry-read-only', message: deps.agents.readOnly.reason });
342
+ }
343
+ // A version promoted nowhere is still the agent's latest, and the
344
+ // latest serves every scope nothing is pinned for.
345
+ if ((await deps.releases.live.resolve({ tenantId, agentId })) === null) {
346
+ return err({
347
+ code: 'proposal-needs-pin',
348
+ message: `${agentId} has no live version for the whole tenant, so a new version of it would serve every scope nothing is pinned for. Promote its current version for the tenant first (scope { "kind": "tenant" }), then evaluate.`,
349
+ });
350
+ }
351
+ if (proposal.candidate === undefined) {
352
+ const made = await makeCandidate(proposal, input.actor);
353
+ if (made.kind === 'err') return made;
354
+ const recorded = await record(proposal, { kind: 'candidate', candidate: made.value });
355
+ if (recorded.kind === 'err') return recorded;
356
+ proposal = recorded.value;
357
+ }
358
+ const candidate = proposal.candidate;
359
+ if (candidate === undefined) throw new Error('a recorded candidate is on the proposal');
360
+ const version = await deps.agents.getVersion({
361
+ tenantId,
362
+ agentId: proposal.agentId,
363
+ version: candidate.agentVersion as Semver,
364
+ });
365
+ if (
366
+ version === null ||
367
+ version.unregisteredAt !== undefined ||
368
+ version.projectId === undefined
369
+ ) {
370
+ return err({
371
+ code: 'agent-version-not-found',
372
+ message: `The candidate ${agentId} ${candidate.agentVersion} is unregistered: withdraw this proposal and draft it again`,
373
+ });
374
+ }
375
+ const started = await deps.evalRuns.start({
376
+ tenantId,
377
+ projectId: version.projectId,
378
+ suiteId: input.suiteId,
379
+ agentRef: { agentId: proposal.agentId, version: candidate.agentVersion as Semver },
380
+ correlationId: `proposal:${proposal.id as unknown as string}`,
381
+ ...(input.comparison !== undefined && { comparison: input.comparison }),
382
+ });
383
+ if (started.kind !== 'ok') return err(startError(started));
384
+ return record(proposal, {
385
+ kind: 'evaluation',
386
+ evaluation: {
387
+ evalRunId: started.runId as unknown as string,
388
+ suiteId: input.suiteId,
389
+ objective: input.objective,
390
+ startedAt: new Date().toISOString() as Timestamp,
391
+ },
392
+ });
393
+ },
394
+
395
+ /**
396
+ * A gated promotion of the candidate for the proposal's scope, with its
397
+ * evaluation's comparison. A drafter's proposal always waits for a
398
+ * reviewer (K2). `promotion` is the request's outcome, as promotions
399
+ * answer it; the proposal records its promotion once one is recorded.
400
+ */
401
+ async request(input: {
402
+ readonly tenantId: TenantId;
403
+ readonly proposal: StoredProposal;
404
+ readonly actor: PromotionActor;
405
+ readonly reason?: string;
406
+ }): Promise<ProposalOutcome<{ proposal: StoredProposal; promotion: PromotionRequestOutcome }>> {
407
+ const { tenantId, proposal } = input;
408
+ const refused = await refuseStatus('request', proposal);
409
+ if (refused !== undefined) return err(refused);
410
+ const { candidate, evaluation } = proposal;
411
+ if (candidate === undefined || evaluation === undefined) {
412
+ throw new Error('an evaluated proposal has a candidate and an evaluation');
413
+ }
414
+ const promotion = await requestPromotion(
415
+ deps.agents,
416
+ deps.releases,
417
+ {
418
+ evalRuns: deps.evalRuns,
419
+ ...(deps.projects !== undefined && { projects: deps.projects }),
420
+ },
421
+ {
422
+ tenantId,
423
+ agentId: proposal.agentId as unknown as string,
424
+ version: candidate.agentVersion as Semver,
425
+ scope: proposal.scope,
426
+ requestedBy: input.actor,
427
+ reason: input.reason ?? `Improvement proposal ${proposal.id as unknown as string}`,
428
+ evalRunId: evaluation.evalRunId,
429
+ },
430
+ proposal.drafter.kind === 'person' ? {} : { requireApproval: DRAFTED_PROPOSAL_APPROVAL },
431
+ );
432
+ if (promotion.kind !== 'ok') return { kind: 'ok', value: { proposal, promotion } };
433
+ const recorded = await record(proposal, {
434
+ kind: 'promotion',
435
+ promotionId: promotion.promotion.id,
436
+ });
437
+ if (recorded.kind === 'err') return recorded;
438
+ return { kind: 'ok', value: { proposal: recorded.value, promotion } };
439
+ },
440
+
441
+ /** The scope back on its own pin before the proposal's promotion (or unpinned). */
442
+ async rollback(input: {
443
+ readonly tenantId: TenantId;
444
+ readonly proposal: StoredProposal;
445
+ readonly actor: PromotionActor;
446
+ readonly reason?: string;
447
+ }): Promise<ProposalOutcome<StoredProposal>> {
448
+ const { tenantId, proposal } = input;
449
+ const seen = await facts(tenantId)(proposal);
450
+ const refused = await refuseStatus('rollback', proposal, seen);
451
+ if (refused !== undefined) return err(refused);
452
+ const promotion = seen.promotion;
453
+ if (promotion === null || promotion === undefined || seen.liveNow !== true) {
454
+ return err(
455
+ invalidTransition(
456
+ proposal,
457
+ seen.status,
458
+ "its version doesn't serve the scope anymore (the scope was promoted, rolled back or unpinned since), so there's nothing of it to roll back.",
459
+ ),
460
+ );
461
+ }
462
+ const request = {
463
+ tenantId,
464
+ agentId: proposal.agentId as unknown as string,
465
+ scope: proposal.scope,
466
+ requestedBy: input.actor,
467
+ reason:
468
+ input.reason ?? `Rolled back improvement proposal ${proposal.id as unknown as string}`,
469
+ };
470
+ // Back to the scope's own pin before; with none, it falls back to the scope above.
471
+ const outcome =
472
+ promotion.fromVersion === null
473
+ ? await deps.releases.promotions.unpin(request)
474
+ : await deps.releases.promotions.rollback({
475
+ ...request,
476
+ toVersion: promotion.fromVersion,
477
+ });
478
+ if (outcome.kind === 'err') return err({ ...outcome.error });
479
+ return record(proposal, {
480
+ kind: 'rolled-back',
481
+ rolledBack: {
482
+ at: new Date().toISOString() as Timestamp,
483
+ promotionId: outcome.value.id,
484
+ by: `${input.actor.kind}:${input.actor.id}`,
485
+ ...(input.reason !== undefined && { reason: input.reason }),
486
+ },
487
+ });
488
+ },
489
+
490
+ async withdraw(input: {
491
+ readonly tenantId: TenantId;
492
+ readonly proposal: StoredProposal;
493
+ readonly actor: PromotionActor;
494
+ readonly reason: string;
495
+ }): Promise<ProposalOutcome<StoredProposal>> {
496
+ const refused = await refuseStatus('withdraw', input.proposal);
497
+ if (refused !== undefined) return err(refused);
498
+ return record(input.proposal, {
499
+ kind: 'withdrawn',
500
+ withdrawn: {
501
+ at: new Date().toISOString() as Timestamp,
502
+ by: `${input.actor.kind}:${input.actor.id}`,
503
+ reason: input.reason,
504
+ },
505
+ });
506
+ },
507
+ };
508
+ }
509
+
510
+ export type ProposalService = ReturnType<typeof createProposalService>;
511
+
512
+ // ---------- helpers ----------
513
+
514
+ export function proposalNotFound(proposalId: string): ProposalServiceError {
515
+ return { code: 'proposal-not-found', message: `No proposal ${proposalId}`, proposalId };
516
+ }
517
+
518
+ function invalidTransition(
519
+ proposal: StoredProposal,
520
+ status: FixProposalStatus,
521
+ why: string,
522
+ ): ProposalServiceError {
523
+ return {
524
+ code: 'proposal-invalid-state-transition',
525
+ message: `Proposal ${proposal.id as unknown as string}: ${why}`,
526
+ proposalId: proposal.id as unknown as string,
527
+ status,
528
+ };
529
+ }
530
+
531
+ function validationFailed(
532
+ message: string,
533
+ issues: readonly { readonly path: string; readonly message: string }[],
534
+ ): ProposalServiceError {
535
+ return {
536
+ code: 'validation-failed',
537
+ message,
538
+ issues: issues as unknown as Record<string, unknown>[],
539
+ };
540
+ }
541
+
542
+ function startError(outcome: Exclude<EvalRunStartOutcome, { kind: 'ok' }>): ProposalServiceError {
543
+ switch (outcome.kind) {
544
+ case 'suite-not-found':
545
+ return {
546
+ code: 'eval-suite-not-found',
547
+ message: `No eval suite registered with id "${outcome.suiteId}"`,
548
+ suiteId: outcome.suiteId,
549
+ };
550
+ case 'dispatcher-not-registered':
551
+ return {
552
+ code: 'dispatcher-not-registered',
553
+ message: `No eval-run dispatcher registered for kind "${outcome.evalKind}"`,
554
+ };
555
+ case 'dispatcher-input-invalid':
556
+ return { code: 'dispatcher-input-invalid', message: outcome.message };
557
+ case 'project-not-found':
558
+ return {
559
+ code: 'bad-input',
560
+ message: `The candidate's project "${outcome.projectId as unknown as string}" isn't in this tenant`,
561
+ };
562
+ }
563
+ }
564
+
565
+ function editOf(tier: ProposalTier, content: ProposedChange['change']['content']): BlockEdit {
566
+ return tier === 'settings-block'
567
+ ? { kind: 'settings', values: (content as { values: Record<string, unknown> }).values }
568
+ : { kind: 'prompt', template: (content as { template: string }).template };
569
+ }
570
+
571
+ function sameContent(pinned: BlockRecord, edit: BlockEdit): boolean {
572
+ if (pinned.kind === 'settings' && edit.kind === 'settings') {
573
+ return canonicalize(pinned.content.values) === canonicalize(edit.values);
574
+ }
575
+ return pinned.kind === 'prompt' && edit.kind === 'prompt'
576
+ ? pinned.content.template === edit.template
577
+ : false;
578
+ }
579
+
580
+ type PinnedBlock =
581
+ | { readonly kind: 'ok'; readonly block: BlockRecord }
582
+ | {
583
+ readonly kind: 'invalid';
584
+ readonly message: string;
585
+ readonly issues: readonly { readonly path: string; readonly message: string }[];
586
+ };
587
+
588
+ /** The block version `from` pins for the tier, or why there's none to change. */
589
+ async function pinnedBlock(
590
+ blocks: BlockRegistryBinding,
591
+ tenantId: TenantId,
592
+ from: AgentVersionRecord,
593
+ tier: ProposalTier,
594
+ blockId: string,
595
+ ): Promise<PinnedBlock> {
596
+ const kind = tier === 'settings-block' ? 'settings' : 'prompts';
597
+ const what = tier === 'settings-block' ? 'settings' : 'prompt';
598
+ const named = `${from.id as unknown as string} ${from.version as unknown as string}`;
599
+ if (from.pins === undefined) {
600
+ return {
601
+ kind: 'invalid',
602
+ message: `${named} has no pins (it was published before pins); publish it again to pin it`,
603
+ issues: [],
604
+ };
605
+ }
606
+ const version = from.pins[kind][blockId];
607
+ if (version === undefined) {
608
+ return {
609
+ kind: 'invalid',
610
+ message: `${named} doesn't use ${what} block "${blockId}"`,
611
+ issues: [
612
+ {
613
+ path: '/change/blockId',
614
+ message: `not a ${what} block this version pins; adding a block is a code change`,
615
+ },
616
+ ],
617
+ };
618
+ }
619
+ const block = await blocks.getVersion({ tenantId, blockId, version });
620
+ if (block === null) {
621
+ return {
622
+ kind: 'invalid',
623
+ message: `Block "${blockId}" version ${version}, which ${named} pins, isn't in the block store`,
624
+ issues: [],
625
+ };
626
+ }
627
+ return { kind: 'ok', block };
628
+ }
629
+
630
+ /** The same change, from the same version, for the same scope, is one proposal. */
631
+ function fingerprintOf(input: DraftProposalInput, change: ProposedChange['change']): string {
632
+ const canonical = canonicalize({
633
+ agentId: input.agentId,
634
+ fromVersion: input.fromVersion,
635
+ scope: liveScopeToWire(input.scope),
636
+ tier: input.tier,
637
+ change,
638
+ });
639
+ return `sha256:${createHash('sha256').update(canonical, 'utf8').digest('hex')}`;
640
+ }