@vercel/factory 0.0.14 → 0.0.15

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 (467) hide show
  1. package/README.md +306 -0
  2. package/dist/agent-routes.d.mts +69 -0
  3. package/dist/agent-routes.mjs +64 -0
  4. package/dist/agent-routes.mjs.map +1 -0
  5. package/dist/api-contracts.d.mts +1749 -0
  6. package/dist/api-contracts.mjs +118 -0
  7. package/dist/api-contracts.mjs.map +1 -0
  8. package/dist/api-images.d.mts +30 -0
  9. package/dist/api-images.mjs +28 -0
  10. package/dist/api-images.mjs.map +1 -0
  11. package/dist/api-task-graph.mjs +57 -0
  12. package/dist/api-task-graph.mjs.map +1 -0
  13. package/dist/api.d.mts +68 -216
  14. package/dist/api.mjs +872 -97
  15. package/dist/api.mjs.map +1 -1
  16. package/dist/approval-access.d.mts +39 -0
  17. package/dist/approval-access.mjs +62 -0
  18. package/dist/approval-access.mjs.map +1 -0
  19. package/dist/approval-contracts.d.mts +692 -0
  20. package/dist/approval-contracts.mjs +27 -0
  21. package/dist/approval-contracts.mjs.map +1 -0
  22. package/dist/approval.d.mts +49 -0
  23. package/dist/approval.mjs +39 -0
  24. package/dist/approval.mjs.map +1 -0
  25. package/dist/blob/artifacts.d.mts +37 -0
  26. package/dist/blob/artifacts.mjs +45 -0
  27. package/dist/blob/artifacts.mjs.map +1 -0
  28. package/dist/blob/index.d.mts +41 -6
  29. package/dist/blob/index.mjs +158 -40
  30. package/dist/blob/index.mjs.map +1 -1
  31. package/dist/budget.d.mts +108 -0
  32. package/dist/budget.mjs +141 -0
  33. package/dist/budget.mjs.map +1 -0
  34. package/dist/budgets.d.mts +4 -0
  35. package/dist/budgets.mjs +4 -0
  36. package/dist/build-factory.d.mts +6 -4
  37. package/dist/build-factory.mjs +2 -1
  38. package/dist/build-factory.mjs.map +1 -1
  39. package/dist/change-verification/brief.d.mts +9 -0
  40. package/dist/change-verification/brief.mjs +13 -0
  41. package/dist/change-verification/brief.mjs.map +1 -0
  42. package/dist/change-verification/dispatch.d.mts +24 -0
  43. package/dist/change-verification/dispatch.mjs +45 -0
  44. package/dist/change-verification/dispatch.mjs.map +1 -0
  45. package/dist/change-verification/eve-tool.d.mts +60 -0
  46. package/dist/change-verification/eve-tool.mjs +101 -0
  47. package/dist/change-verification/eve-tool.mjs.map +1 -0
  48. package/dist/changes/eve-record-change.d.mts +56 -0
  49. package/dist/changes/eve-record-change.mjs +97 -0
  50. package/dist/changes/eve-record-change.mjs.map +1 -0
  51. package/dist/changes/presentation.d.mts +14 -0
  52. package/dist/changes.d.mts +23 -0
  53. package/dist/changes.mjs +29 -0
  54. package/dist/changes.mjs.map +1 -0
  55. package/dist/client-events.d.mts +18 -0
  56. package/dist/client-events.mjs +41 -0
  57. package/dist/client-events.mjs.map +1 -0
  58. package/dist/client-stream.d.mts +28 -0
  59. package/dist/client-stream.mjs +204 -0
  60. package/dist/client-stream.mjs.map +1 -0
  61. package/dist/client-transcript.d.mts +41 -0
  62. package/dist/client-transcript.mjs +229 -0
  63. package/dist/client-transcript.mjs.map +1 -0
  64. package/dist/client.d.mts +234 -0
  65. package/dist/client.mjs +258 -0
  66. package/dist/client.mjs.map +1 -0
  67. package/dist/code-review/contracts.d.mts +26 -0
  68. package/dist/code-review/eve-post-review.d.mts +70 -0
  69. package/dist/code-review/eve-post-review.mjs +183 -0
  70. package/dist/code-review/eve-post-review.mjs.map +1 -0
  71. package/dist/code-review/eve-review-comments.d.mts +64 -0
  72. package/dist/code-review/eve-review-comments.mjs +159 -0
  73. package/dist/code-review/eve-review-comments.mjs.map +1 -0
  74. package/dist/code-review/github-contracts.d.mts +10 -0
  75. package/dist/code-review/github-reporter.d.mts +44 -0
  76. package/dist/code-review/github-reporter.mjs +260 -0
  77. package/dist/code-review/github-reporter.mjs.map +1 -0
  78. package/dist/code-review/preparation.d.mts +34 -0
  79. package/dist/code-review/preparation.mjs +23 -0
  80. package/dist/code-review/preparation.mjs.map +1 -0
  81. package/dist/code-review/stall.d.mts +17 -0
  82. package/dist/code-review/stall.mjs +30 -0
  83. package/dist/code-review/stall.mjs.map +1 -0
  84. package/dist/code-review.d.mts +6 -0
  85. package/dist/code-review.mjs +5 -0
  86. package/dist/communication.d.mts +9 -0
  87. package/dist/communication.mjs +9 -0
  88. package/dist/connectors/github.d.mts +26 -0
  89. package/dist/connectors/github.mjs +55 -0
  90. package/dist/connectors/github.mjs.map +1 -0
  91. package/dist/connectors/index.d.mts +35 -3
  92. package/dist/connectors/index.mjs +3 -52
  93. package/dist/connectors/index.mjs.map +1 -1
  94. package/dist/connectors/outbox.d.mts +34 -0
  95. package/dist/connectors/outbox.mjs +50 -0
  96. package/dist/connectors/outbox.mjs.map +1 -0
  97. package/dist/connectors/slack-approval-blocks.mjs +136 -0
  98. package/dist/connectors/slack-approval-blocks.mjs.map +1 -0
  99. package/dist/connectors/slack-approvals.d.mts +29 -0
  100. package/dist/connectors/slack-approvals.mjs +346 -0
  101. package/dist/connectors/slack-approvals.mjs.map +1 -0
  102. package/dist/connectors/slack.d.mts +35 -52
  103. package/dist/connectors/slack.mjs +13 -85
  104. package/dist/connectors/slack.mjs.map +1 -1
  105. package/dist/deepsec/eve-tool.d.mts +110 -0
  106. package/dist/deepsec/eve-tool.mjs +164 -0
  107. package/dist/deepsec/eve-tool.mjs.map +1 -0
  108. package/dist/deepsec/finding.d.mts +84 -0
  109. package/dist/deepsec/finding.mjs +56 -0
  110. package/dist/deepsec/finding.mjs.map +1 -0
  111. package/dist/deepsec.d.mts +3 -0
  112. package/dist/delivery-metrics/metrics.d.mts +93 -0
  113. package/dist/delivery-metrics/metrics.mjs +200 -0
  114. package/dist/delivery-metrics/metrics.mjs.map +1 -0
  115. package/dist/delivery-metrics.d.mts +2 -0
  116. package/dist/delivery-metrics.mjs +2 -0
  117. package/dist/dispatch.d.mts +81 -0
  118. package/dist/dispatch.mjs +140 -0
  119. package/dist/dispatch.mjs.map +1 -0
  120. package/dist/errors.d.mts +15 -0
  121. package/dist/errors.mjs +38 -0
  122. package/dist/errors.mjs.map +1 -0
  123. package/dist/eve/index.d.mts +143 -1271
  124. package/dist/eve/index.mjs +233 -1065
  125. package/dist/eve/index.mjs.map +1 -1
  126. package/dist/eve/invoke.d.mts +144 -0
  127. package/dist/eve/invoke.mjs +241 -0
  128. package/dist/eve/invoke.mjs.map +1 -0
  129. package/dist/eve/session-client.d.mts +68 -0
  130. package/dist/eve/session-client.mjs +131 -0
  131. package/dist/eve/session-client.mjs.map +1 -0
  132. package/dist/eve/task-execution.mjs +18 -0
  133. package/dist/eve/task-execution.mjs.map +1 -0
  134. package/dist/eve/task-session.d.mts +87 -0
  135. package/dist/eve/task-session.mjs +203 -0
  136. package/dist/eve/task-session.mjs.map +1 -0
  137. package/dist/eve/transcript.d.mts +37 -0
  138. package/dist/eve/transcript.mjs +65 -0
  139. package/dist/eve/transcript.mjs.map +1 -0
  140. package/dist/execution-public.d.mts +8 -0
  141. package/dist/execution-public.mjs +7 -0
  142. package/dist/execution.d.mts +75 -0
  143. package/dist/execution.mjs +153 -0
  144. package/dist/execution.mjs.map +1 -0
  145. package/dist/finding-remediation/admission.d.mts +51 -0
  146. package/dist/finding-remediation/admission.mjs +87 -0
  147. package/dist/finding-remediation/admission.mjs.map +1 -0
  148. package/dist/finding-remediation/brief.d.mts +14 -0
  149. package/dist/finding-remediation/brief.mjs +25 -0
  150. package/dist/finding-remediation/brief.mjs.map +1 -0
  151. package/dist/finding-remediation/eve-tool.d.mts +29 -0
  152. package/dist/finding-remediation/eve-tool.mjs +34 -0
  153. package/dist/finding-remediation/eve-tool.mjs.map +1 -0
  154. package/dist/findings/fingerprint.d.mts +14 -0
  155. package/dist/findings/fingerprint.mjs +28 -0
  156. package/dist/findings/fingerprint.mjs.map +1 -0
  157. package/dist/findings.d.mts +6 -0
  158. package/dist/findings.mjs +6 -0
  159. package/dist/github-app.d.mts +54 -0
  160. package/dist/github-app.mjs +121 -0
  161. package/dist/github-app.mjs.map +1 -0
  162. package/dist/github-publication.d.mts +198 -0
  163. package/dist/github-publication.mjs +429 -0
  164. package/dist/github-publication.mjs.map +1 -0
  165. package/dist/github-transfer.d.mts +72 -0
  166. package/dist/github-transfer.mjs +125 -0
  167. package/dist/github-transfer.mjs.map +1 -0
  168. package/dist/github.d.mts +406 -2
  169. package/dist/github.mjs +239 -40
  170. package/dist/github.mjs.map +1 -1
  171. package/dist/image-input.d.mts +51 -0
  172. package/dist/image-input.mjs +63 -0
  173. package/dist/image-input.mjs.map +1 -0
  174. package/dist/index.d.mts +14 -484
  175. package/dist/index.mjs +14 -522
  176. package/dist/intake-contracts.d.mts +56 -0
  177. package/dist/intake-contracts.mjs +33 -0
  178. package/dist/intake-contracts.mjs.map +1 -0
  179. package/dist/integrations/deepsec.d.mts +4 -0
  180. package/dist/integrations/deepsec.mjs +3 -0
  181. package/dist/integrations/github.d.mts +11 -0
  182. package/dist/integrations/github.mjs +9 -0
  183. package/dist/integrations/slack.d.mts +3 -0
  184. package/dist/integrations/slack.mjs +3 -0
  185. package/dist/integrations/vercel.d.mts +3 -0
  186. package/dist/integrations/vercel.mjs +3 -0
  187. package/dist/merge-resolution/contracts.d.mts +17 -0
  188. package/dist/merge-resolution/contracts.mjs +19 -0
  189. package/dist/merge-resolution/contracts.mjs.map +1 -0
  190. package/dist/merge-resolution/eve-tools.d.mts +46 -0
  191. package/dist/merge-resolution/eve-tools.mjs +117 -0
  192. package/dist/merge-resolution/eve-tools.mjs.map +1 -0
  193. package/dist/multi-repository-changes/coordination.d.mts +49 -0
  194. package/dist/multi-repository-changes/coordination.mjs +115 -0
  195. package/dist/multi-repository-changes/coordination.mjs.map +1 -0
  196. package/dist/planning/cancellation.mjs +82 -0
  197. package/dist/planning/cancellation.mjs.map +1 -0
  198. package/dist/planning/configuration.d.mts +23 -0
  199. package/dist/planning/configuration.mjs +56 -0
  200. package/dist/planning/configuration.mjs.map +1 -0
  201. package/dist/planning/contracts.d.mts +136 -0
  202. package/dist/planning/contracts.mjs +42 -0
  203. package/dist/planning/contracts.mjs.map +1 -0
  204. package/dist/planning/dependencies.mjs +44 -0
  205. package/dist/planning/dependencies.mjs.map +1 -0
  206. package/dist/planning/effects.mjs +122 -0
  207. package/dist/planning/effects.mjs.map +1 -0
  208. package/dist/planning/reconcile.mjs +273 -0
  209. package/dist/planning/reconcile.mjs.map +1 -0
  210. package/dist/planning/runtime.d.mts +27 -0
  211. package/dist/planning/runtime.mjs +25 -0
  212. package/dist/planning/runtime.mjs.map +1 -0
  213. package/dist/planning/state.mjs +92 -0
  214. package/dist/planning/state.mjs.map +1 -0
  215. package/dist/planning/turns.mjs +262 -0
  216. package/dist/planning/turns.mjs.map +1 -0
  217. package/dist/planning/validation.mjs +106 -0
  218. package/dist/planning/validation.mjs.map +1 -0
  219. package/dist/postgres/index.d.mts +7 -1
  220. package/dist/postgres/index.mjs +7 -3
  221. package/dist/postgres/index.mjs.map +1 -1
  222. package/dist/presets/software-development/approval.d.mts +14 -0
  223. package/dist/presets/software-development/approval.mjs +21 -0
  224. package/dist/presets/software-development/approval.mjs.map +1 -0
  225. package/dist/presets/software-development/deepsec-brief.d.mts +8 -0
  226. package/dist/presets/software-development/deepsec-brief.mjs +14 -0
  227. package/dist/presets/software-development/deepsec-brief.mjs.map +1 -0
  228. package/dist/presets/software-development/dispatch.d.mts +53 -0
  229. package/dist/presets/software-development/dispatch.mjs +66 -0
  230. package/dist/presets/software-development/dispatch.mjs.map +1 -0
  231. package/dist/presets/software-development/eve-delegation-policy.d.mts +7 -0
  232. package/dist/presets/software-development/eve-delegation-policy.mjs +34 -0
  233. package/dist/presets/software-development/eve-delegation-policy.mjs.map +1 -0
  234. package/dist/presets/software-development/eve.d.mts +18 -0
  235. package/dist/presets/software-development/eve.mjs +19 -0
  236. package/dist/presets/software-development/eve.mjs.map +1 -0
  237. package/dist/presets/software-development/finish-task-policy.d.mts +7 -0
  238. package/dist/presets/software-development/finish-task-policy.mjs +9 -0
  239. package/dist/presets/software-development/finish-task-policy.mjs.map +1 -0
  240. package/dist/presets/software-development/recovery.d.mts +32 -0
  241. package/dist/presets/software-development/recovery.mjs +120 -0
  242. package/dist/presets/software-development/recovery.mjs.map +1 -0
  243. package/dist/presets/software-development/task-communication.d.mts +23 -0
  244. package/dist/presets/software-development/task-communication.mjs +120 -0
  245. package/dist/presets/software-development/task-communication.mjs.map +1 -0
  246. package/dist/presets/software-development.d.mts +9 -0
  247. package/dist/presets/software-development.mjs +9 -0
  248. package/dist/pull-requests/contracts.d.mts +72 -0
  249. package/dist/pull-requests/eve-fetch.d.mts +57 -0
  250. package/dist/pull-requests/eve-fetch.mjs +151 -0
  251. package/dist/pull-requests/eve-fetch.mjs.map +1 -0
  252. package/dist/pull-requests/github-label-manager.d.mts +27 -0
  253. package/dist/pull-requests/github-label-manager.mjs +40 -0
  254. package/dist/pull-requests/github-label-manager.mjs.map +1 -0
  255. package/dist/pull-requests/github-publisher.d.mts +53 -0
  256. package/dist/pull-requests/github-publisher.mjs +345 -0
  257. package/dist/pull-requests/github-publisher.mjs.map +1 -0
  258. package/dist/pull-requests.d.mts +5 -0
  259. package/dist/pull-requests.mjs +4 -0
  260. package/dist/receipts.d.mts +4 -0
  261. package/dist/receipts.mjs +3 -0
  262. package/dist/repositories.d.mts +18 -0
  263. package/dist/repositories.mjs +33 -0
  264. package/dist/repositories.mjs.map +1 -0
  265. package/dist/sandbox/command.d.mts +18 -0
  266. package/dist/sandbox/execution-abort.d.mts +16 -0
  267. package/dist/sandbox/execution-abort.mjs +49 -0
  268. package/dist/sandbox/execution-abort.mjs.map +1 -0
  269. package/dist/sandbox/execution-environment.d.mts +25 -0
  270. package/dist/sandbox/execution-run-ledger.d.mts +115 -0
  271. package/dist/sandbox/execution-run-ledger.mjs +209 -0
  272. package/dist/sandbox/execution-run-ledger.mjs.map +1 -0
  273. package/dist/sandbox/execution.d.mts +54 -0
  274. package/dist/sandbox/execution.mjs +228 -0
  275. package/dist/sandbox/execution.mjs.map +1 -0
  276. package/dist/sandbox/harness-runner.d.mts +79 -0
  277. package/dist/sandbox/harness-runner.mjs +162 -0
  278. package/dist/sandbox/harness-runner.mjs.map +1 -0
  279. package/dist/sandbox/index.d.mts +9 -0
  280. package/dist/sandbox/index.mjs +7 -0
  281. package/dist/sandbox/task-environment.d.mts +50 -0
  282. package/dist/sandbox/task-environment.mjs +140 -0
  283. package/dist/sandbox/task-environment.mjs.map +1 -0
  284. package/dist/sandbox/workspace.d.mts +29 -0
  285. package/dist/sandbox/workspace.mjs +28 -0
  286. package/dist/sandbox/workspace.mjs.map +1 -0
  287. package/dist/schema/agent-route.d.mts +22 -0
  288. package/dist/schema/agent-route.mjs +15 -0
  289. package/dist/schema/agent-route.mjs.map +1 -0
  290. package/dist/schema/answer.d.mts +23 -0
  291. package/dist/schema/answer.mjs +21 -0
  292. package/dist/schema/answer.mjs.map +1 -0
  293. package/dist/schema/budget.d.mts +38 -0
  294. package/dist/schema/budget.mjs +30 -0
  295. package/dist/schema/budget.mjs.map +1 -0
  296. package/dist/schema/catalog.mjs +72 -0
  297. package/dist/schema/catalog.mjs.map +1 -0
  298. package/dist/schema/change.d.mts +117 -0
  299. package/dist/schema/change.mjs +68 -0
  300. package/dist/schema/change.mjs.map +1 -0
  301. package/dist/schema/common.d.mts +33 -0
  302. package/dist/{id-BLIkCVue.mjs → schema/common.mjs} +7 -16
  303. package/dist/schema/common.mjs.map +1 -0
  304. package/dist/schema/communication.d.mts +52 -0
  305. package/dist/{communication-Kd9nU5RP.mjs → schema/communication.mjs} +8 -3
  306. package/dist/schema/communication.mjs.map +1 -0
  307. package/dist/schema/execution.d.mts +101 -0
  308. package/dist/schema/execution.mjs +86 -0
  309. package/dist/schema/execution.mjs.map +1 -0
  310. package/dist/schema/factory-config.d.mts +95 -0
  311. package/dist/schema/factory-config.mjs +60 -0
  312. package/dist/schema/factory-config.mjs.map +1 -0
  313. package/dist/schema/finding.d.mts +125 -0
  314. package/dist/schema/finding.mjs +67 -0
  315. package/dist/schema/finding.mjs.map +1 -0
  316. package/dist/schema/id.d.mts +43 -0
  317. package/dist/schema/id.mjs +32 -0
  318. package/dist/schema/id.mjs.map +1 -0
  319. package/dist/schema/outbox.d.mts +240 -0
  320. package/dist/schema/outbox.mjs +53 -0
  321. package/dist/schema/outbox.mjs.map +1 -0
  322. package/dist/schema/question-route.mjs +16 -0
  323. package/dist/schema/question-route.mjs.map +1 -0
  324. package/dist/schema/receipt.d.mts +81 -0
  325. package/dist/schema/receipt.mjs +94 -0
  326. package/dist/schema/receipt.mjs.map +1 -0
  327. package/dist/schema/repository.d.mts +26 -0
  328. package/dist/schema/repository.mjs +25 -0
  329. package/dist/schema/repository.mjs.map +1 -0
  330. package/dist/schema/session.d.mts +135 -0
  331. package/dist/schema/session.mjs +53 -0
  332. package/dist/schema/session.mjs.map +1 -0
  333. package/dist/schema/signal.d.mts +105 -0
  334. package/dist/schema/signal.mjs +81 -0
  335. package/dist/schema/signal.mjs.map +1 -0
  336. package/dist/schema/stored-session.mjs +10 -0
  337. package/dist/schema/stored-session.mjs.map +1 -0
  338. package/dist/schema/stored-task.mjs +20 -0
  339. package/dist/schema/stored-task.mjs.map +1 -0
  340. package/dist/schema/task-effect.d.mts +39 -0
  341. package/dist/schema/task-effect.mjs +29 -0
  342. package/dist/schema/task-effect.mjs.map +1 -0
  343. package/dist/schema/task-graph.d.mts +3134 -0
  344. package/dist/schema/task-graph.mjs +97 -0
  345. package/dist/schema/task-graph.mjs.map +1 -0
  346. package/dist/schema/task-message.d.mts +254 -0
  347. package/dist/schema/task-message.mjs +189 -0
  348. package/dist/schema/task-message.mjs.map +1 -0
  349. package/dist/schema/task.d.mts +272 -0
  350. package/dist/schema/task.mjs +199 -0
  351. package/dist/schema/task.mjs.map +1 -0
  352. package/dist/schema/transcript.d.mts +170 -0
  353. package/dist/schema/transcript.mjs +86 -0
  354. package/dist/schema/transcript.mjs.map +1 -0
  355. package/dist/schema/transitions.d.mts +50 -0
  356. package/dist/schema/transitions.mjs +303 -0
  357. package/dist/schema/transitions.mjs.map +1 -0
  358. package/dist/schema/work.d.mts +101 -0
  359. package/dist/schema/work.mjs +102 -0
  360. package/dist/schema/work.mjs.map +1 -0
  361. package/dist/signal-triage.d.mts +20 -0
  362. package/dist/signal-triage.mjs +85 -0
  363. package/dist/signal-triage.mjs.map +1 -0
  364. package/dist/signals.d.mts +19 -0
  365. package/dist/signals.mjs +19 -0
  366. package/dist/signals.mjs.map +1 -0
  367. package/dist/stall.d.mts +38 -0
  368. package/dist/stall.mjs +79 -0
  369. package/dist/stall.mjs.map +1 -0
  370. package/dist/storage.d.mts +4 -0
  371. package/dist/storage.mjs +4 -0
  372. package/dist/store/driver.d.mts +65 -0
  373. package/dist/store/driver.mjs +42 -0
  374. package/dist/store/driver.mjs.map +1 -0
  375. package/dist/store/engine.d.mts +485 -0
  376. package/dist/store/engine.mjs +2175 -0
  377. package/dist/store/engine.mjs.map +1 -0
  378. package/dist/store/memory.d.mts +7 -0
  379. package/dist/store/memory.mjs +52 -0
  380. package/dist/store/memory.mjs.map +1 -0
  381. package/dist/store/task-effects.d.mts +38 -0
  382. package/dist/store/task-effects.mjs +118 -0
  383. package/dist/store/task-effects.mjs.map +1 -0
  384. package/dist/store/task-graphs.d.mts +60 -0
  385. package/dist/store/task-graphs.mjs +212 -0
  386. package/dist/store/task-graphs.mjs.map +1 -0
  387. package/dist/store/task-messages.d.mts +136 -0
  388. package/dist/store/task-messages.mjs +475 -0
  389. package/dist/store/task-messages.mjs.map +1 -0
  390. package/dist/store/task-work.d.mts +65 -0
  391. package/dist/store/task-work.mjs +134 -0
  392. package/dist/store/task-work.mjs.map +1 -0
  393. package/dist/store/work-notifications.d.mts +19 -0
  394. package/dist/store/work-notifications.mjs +48 -0
  395. package/dist/store/work-notifications.mjs.map +1 -0
  396. package/dist/sweep.d.mts +57 -0
  397. package/dist/sweep.mjs +143 -0
  398. package/dist/sweep.mjs.map +1 -0
  399. package/dist/task-communication.d.mts +35 -0
  400. package/dist/task-communication.mjs +47 -0
  401. package/dist/task-communication.mjs.map +1 -0
  402. package/dist/task-graph-view.d.mts +264 -0
  403. package/dist/task-graph-view.mjs +17 -0
  404. package/dist/task-graph-view.mjs.map +1 -0
  405. package/dist/task-message-processing.d.mts +60 -0
  406. package/dist/task-message-processing.mjs +200 -0
  407. package/dist/task-message-processing.mjs.map +1 -0
  408. package/dist/task-protocols.d.mts +80 -0
  409. package/dist/task-protocols.mjs +47 -0
  410. package/dist/task-protocols.mjs.map +1 -0
  411. package/dist/tasks.d.mts +17 -0
  412. package/dist/tasks.mjs +17 -0
  413. package/dist/vercel-git.d.mts +21 -0
  414. package/dist/vercel-git.mjs +112 -0
  415. package/dist/vercel-git.mjs.map +1 -0
  416. package/dist/vercel.d.mts +33 -2
  417. package/dist/vercel.mjs +48 -3
  418. package/dist/vercel.mjs.map +1 -1
  419. package/dist/work-triage/eve-tool.d.mts +29 -0
  420. package/dist/work-triage/eve-tool.mjs +32 -0
  421. package/dist/work-triage/eve-tool.mjs.map +1 -0
  422. package/dist/work-triage/find-existing-work.d.mts +32 -0
  423. package/dist/work-triage/find-existing-work.mjs +31 -0
  424. package/dist/work-triage/find-existing-work.mjs.map +1 -0
  425. package/dist/work-triage/github.d.mts +15 -0
  426. package/dist/work-triage/github.mjs +14 -0
  427. package/dist/work-triage/github.mjs.map +1 -0
  428. package/dist/work-triage.d.mts +3 -0
  429. package/dist/work-triage.mjs +3 -0
  430. package/dist/workflows.d.mts +48 -0
  431. package/dist/workflows.mjs +86 -0
  432. package/dist/workflows.mjs.map +1 -0
  433. package/dist/workspace-diff-assembly.d.mts +33 -0
  434. package/dist/workspace-diff-assembly.mjs +95 -0
  435. package/dist/workspace-diff-assembly.mjs.map +1 -0
  436. package/dist/workspace-diff.d.mts +90 -0
  437. package/dist/workspace-diff.mjs +61 -0
  438. package/dist/workspace-diff.mjs.map +1 -0
  439. package/dist/workspace-snapshot.d.mts +26 -0
  440. package/dist/workspace-snapshot.mjs +80 -0
  441. package/dist/workspace-snapshot.mjs.map +1 -0
  442. package/package.json +129 -44
  443. package/dist/approval-JX2Px9IC.mjs +0 -132
  444. package/dist/approval-JX2Px9IC.mjs.map +0 -1
  445. package/dist/budget-DzSiIA4t.mjs +0 -42
  446. package/dist/budget-DzSiIA4t.mjs.map +0 -1
  447. package/dist/change-D2U6v7Ek.mjs +0 -554
  448. package/dist/change-D2U6v7Ek.mjs.map +0 -1
  449. package/dist/communication-Kd9nU5RP.mjs.map +0 -1
  450. package/dist/coordination-Cq08hb1l.mjs +0 -76
  451. package/dist/coordination-Cq08hb1l.mjs.map +0 -1
  452. package/dist/dispatch-BaZS3PT8.d.mts +0 -76
  453. package/dist/driver-BC0Mt5KG.d.mts +0 -34
  454. package/dist/driver-Bbd25rNM.mjs +0 -23
  455. package/dist/driver-Bbd25rNM.mjs.map +0 -1
  456. package/dist/engine-DFHU2Pe-.d.mts +0 -781
  457. package/dist/engine-WaUM2cGA.mjs +0 -1068
  458. package/dist/engine-WaUM2cGA.mjs.map +0 -1
  459. package/dist/factory-config-BoRXOw43.mjs +0 -50
  460. package/dist/factory-config-BoRXOw43.mjs.map +0 -1
  461. package/dist/factory-config-D2yU3G0c.d.mts +0 -58
  462. package/dist/github-C5HNBN6p.d.mts +0 -279
  463. package/dist/id-BLIkCVue.mjs.map +0 -1
  464. package/dist/index-Bw6hggoH.d.mts +0 -40
  465. package/dist/index.mjs.map +0 -1
  466. package/dist/task-CcOyOJ3k.mjs +0 -154
  467. package/dist/task-CcOyOJ3k.mjs.map +0 -1
@@ -0,0 +1,56 @@
1
+ import { findingSchema } from "../schema/finding.mjs";
2
+ import { z } from "zod";
3
+ //#region src/deepsec/finding.ts
4
+ /** Deepsec's exported finding shape, as far as Factory depends on it. */
5
+ const deepsecFindingSchema = z.looseObject({
6
+ title: z.string().min(1),
7
+ description: z.string().min(1),
8
+ severity: z.string().min(1),
9
+ metadata: z.looseObject({
10
+ filePath: z.string().min(1).optional(),
11
+ lineNumbers: z.array(z.number().int().min(1)).readonly().optional(),
12
+ vulnSlug: z.string().min(1).optional(),
13
+ confidence: z.string().optional(),
14
+ runId: z.string().optional()
15
+ }).optional()
16
+ }).readonly();
17
+ const deepsecFindingsSchema = z.array(deepsecFindingSchema).readonly();
18
+ /** Deepsec export document accepted by the optional scanner adapter. */
19
+ const deepsecExportSchema = z.union([deepsecFindingsSchema, z.looseObject({ findings: deepsecFindingsSchema }).transform((value) => value.findings)]);
20
+ const priorityBySeverity = {
21
+ CRITICAL: "P0",
22
+ HIGH: "P1",
23
+ MEDIUM: "P2",
24
+ HIGH_BUG: "P2",
25
+ BUG: "P3",
26
+ LOW: "P3"
27
+ };
28
+ /** Validate and convert one Deepsec finding into Factory's normalized Finding contract. */
29
+ function findingFromDeepsec(input) {
30
+ const raw = deepsecFindingSchema.parse(input);
31
+ const meta = raw.metadata ?? {};
32
+ const confidence = (meta.confidence ?? "").toLowerCase();
33
+ const line = meta.lineNumbers?.[0];
34
+ const endLine = meta.lineNumbers === void 0 ? void 0 : Math.max(...meta.lineNumbers);
35
+ return findingSchema.parse({
36
+ title: raw.title.replace(/^\[[A-Z_]+\]\s*/u, "").slice(0, 200),
37
+ body: raw.description.slice(0, 8e3),
38
+ ...meta.filePath === void 0 ? {} : { path: meta.filePath.slice(0, 500) },
39
+ ...line === void 0 ? {} : { line },
40
+ ...endLine === void 0 || endLine === line ? {} : { endLine },
41
+ priority: priorityBySeverity[raw.severity.toUpperCase()] ?? "P3",
42
+ category: "security",
43
+ verification: {
44
+ level: confidence === "high" ? "supported" : "unverified",
45
+ method: "static_analysis",
46
+ summary: `deepsec ${meta.vulnSlug ?? "finding"} with ${confidence || "unknown"} confidence${meta.runId === void 0 ? "" : ` (run ${meta.runId})`}.`,
47
+ limitations: "AI-assisted static analysis of the checkout; not reproduced at runtime."
48
+ },
49
+ tool: "deepsec",
50
+ ...meta.vulnSlug === void 0 ? {} : { ruleId: meta.vulnSlug }
51
+ });
52
+ }
53
+ //#endregion
54
+ export { deepsecExportSchema, deepsecFindingSchema, findingFromDeepsec };
55
+
56
+ //# sourceMappingURL=finding.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding.mjs","names":[],"sources":["../../src/deepsec/finding.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { findingSchema, type Finding, type FindingPriority } from \"../schema/finding\";\n\n/** Deepsec's exported finding shape, as far as Factory depends on it. */\nexport const deepsecFindingSchema = z\n .looseObject({\n title: z.string().min(1),\n description: z.string().min(1),\n severity: z.string().min(1),\n metadata: z\n .looseObject({\n filePath: z.string().min(1).optional(),\n lineNumbers: z.array(z.number().int().min(1)).readonly().optional(),\n vulnSlug: z.string().min(1).optional(),\n confidence: z.string().optional(),\n runId: z.string().optional(),\n })\n .optional(),\n })\n .readonly();\n\nconst deepsecFindingsSchema = z.array(deepsecFindingSchema).readonly();\n\n/** Deepsec export document accepted by the optional scanner adapter. */\nexport const deepsecExportSchema = z.union([\n deepsecFindingsSchema,\n z.looseObject({ findings: deepsecFindingsSchema }).transform((value) => value.findings),\n]);\n\n/** Provider payload accepted for one Deepsec finding before normalization. */\nexport type DeepsecFindingInput = z.input<typeof deepsecFindingSchema>;\n\n/** Parsed Deepsec finding payload used by the normalization adapter. */\nexport type DeepsecFinding = z.output<typeof deepsecFindingSchema>;\n\n/** Provider export document accepted before its wrapper is normalized. */\nexport type DeepsecExportInput = z.input<typeof deepsecExportSchema>;\n\n/** Parsed list of Deepsec findings produced by the export schema. */\nexport type DeepsecExport = z.output<typeof deepsecExportSchema>;\n\nconst priorityBySeverity: Record<string, FindingPriority> = {\n CRITICAL: \"P0\",\n HIGH: \"P1\",\n MEDIUM: \"P2\",\n HIGH_BUG: \"P2\",\n BUG: \"P3\",\n LOW: \"P3\",\n};\n\n/** Validate and convert one Deepsec finding into Factory's normalized Finding contract. */\nexport function findingFromDeepsec(input: DeepsecFindingInput): Finding {\n const raw = deepsecFindingSchema.parse(input);\n const meta = raw.metadata ?? {};\n const confidence = (meta.confidence ?? \"\").toLowerCase();\n const line = meta.lineNumbers?.[0];\n const endLine = meta.lineNumbers === undefined ? undefined : Math.max(...meta.lineNumbers);\n return findingSchema.parse({\n title: raw.title.replace(/^\\[[A-Z_]+\\]\\s*/u, \"\").slice(0, 200),\n body: raw.description.slice(0, 8000),\n ...(meta.filePath === undefined ? {} : { path: meta.filePath.slice(0, 500) }),\n ...(line === undefined ? {} : { line }),\n ...(endLine === undefined || endLine === line ? {} : { endLine }),\n priority: priorityBySeverity[raw.severity.toUpperCase()] ?? \"P3\",\n category: \"security\",\n verification: {\n level: confidence === \"high\" ? \"supported\" : \"unverified\",\n method: \"static_analysis\",\n summary: `deepsec ${meta.vulnSlug ?? \"finding\"} with ${confidence || \"unknown\"} confidence${meta.runId === undefined ? \"\" : ` (run ${meta.runId})`}.`,\n limitations: \"AI-assisted static analysis of the checkout; not reproduced at runtime.\",\n },\n tool: \"deepsec\",\n ...(meta.vulnSlug === undefined ? {} : { ruleId: meta.vulnSlug }),\n });\n}\n"],"mappings":";;;;AAIA,MAAa,uBAAuB,EACjC,YAAY;CACX,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;CACvB,aAAa,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;CAC7B,UAAU,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;CAC1B,UAAU,EACP,YAAY;EACX,UAAU,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;EACrC,aAAa,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;EAClE,UAAU,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;EACrC,YAAY,EAAE,OAAO,CAAC,CAAC,SAAS;EAChC,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS;CAC7B,CAAC,CAAC,CACD,SAAS;AACd,CAAC,CAAC,CACD,SAAS;AAEZ,MAAM,wBAAwB,EAAE,MAAM,oBAAoB,CAAC,CAAC,SAAS;;AAGrE,MAAa,sBAAsB,EAAE,MAAM,CACzC,uBACA,EAAE,YAAY,EAAE,UAAU,sBAAsB,CAAC,CAAC,CAAC,WAAW,UAAU,MAAM,QAAQ,CACxF,CAAC;AAcD,MAAM,qBAAsD;CAC1D,UAAU;CACV,MAAM;CACN,QAAQ;CACR,UAAU;CACV,KAAK;CACL,KAAK;AACP;;AAGA,SAAgB,mBAAmB,OAAqC;CACtE,MAAM,MAAM,qBAAqB,MAAM,KAAK;CAC5C,MAAM,OAAO,IAAI,YAAY,CAAC;CAC9B,MAAM,cAAc,KAAK,cAAc,GAAA,CAAI,YAAY;CACvD,MAAM,OAAO,KAAK,cAAc;CAChC,MAAM,UAAU,KAAK,gBAAgB,KAAA,IAAY,KAAA,IAAY,KAAK,IAAI,GAAG,KAAK,WAAW;CACzF,OAAO,cAAc,MAAM;EACzB,OAAO,IAAI,MAAM,QAAQ,oBAAoB,EAAE,CAAC,CAAC,MAAM,GAAG,GAAG;EAC7D,MAAM,IAAI,YAAY,MAAM,GAAG,GAAI;EACnC,GAAI,KAAK,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,MAAM,GAAG,GAAG,EAAE;EAC3E,GAAI,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,KAAK;EACrC,GAAI,YAAY,KAAA,KAAa,YAAY,OAAO,CAAC,IAAI,EAAE,QAAQ;EAC/D,UAAU,mBAAmB,IAAI,SAAS,YAAY,MAAM;EAC5D,UAAU;EACV,cAAc;GACZ,OAAO,eAAe,SAAS,cAAc;GAC7C,QAAQ;GACR,SAAS,WAAW,KAAK,YAAY,UAAU,QAAQ,cAAc,UAAU,aAAa,KAAK,UAAU,KAAA,IAAY,KAAK,SAAS,KAAK,MAAM,GAAG;GACnJ,aAAa;EACf;EACA,MAAM;EACN,GAAI,KAAK,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,KAAK,SAAS;CACjE,CAAC;AACH"}
@@ -0,0 +1,3 @@
1
+ import { DeepsecExport, DeepsecExportInput, DeepsecFinding, DeepsecFindingInput, deepsecExportSchema, deepsecFindingSchema, findingFromDeepsec } from "./deepsec/finding.mjs";
2
+ import { DeepsecAgent, DeepsecCommandResult, DeepsecCredentialCommandInput, DeepsecScanFailure, DeepsecScanFailureCode, DeepsecScanFinding, DeepsecScanInput, DeepsecScanResult, DeepsecScanSuccess, DeepsecScanTool, DeepsecScanToolOptions, DeepsecScanToolStores, DeepsecThinkingLevel, createDeepsecScanTool, deepsecScanInputSchema } from "./deepsec/eve-tool.mjs";
3
+ export { type DeepsecAgent, type DeepsecCommandResult, type DeepsecCredentialCommandInput, type DeepsecExport, type DeepsecExportInput, type DeepsecFinding, type DeepsecFindingInput, type DeepsecScanFailure, type DeepsecScanFailureCode, type DeepsecScanFinding, type DeepsecScanInput, type DeepsecScanResult, type DeepsecScanSuccess, type DeepsecScanTool, type DeepsecScanToolOptions, type DeepsecScanToolStores, type DeepsecThinkingLevel, createDeepsecScanTool, deepsecExportSchema, deepsecFindingSchema, deepsecScanInputSchema, findingFromDeepsec };
@@ -0,0 +1,93 @@
1
+ import { Signal } from "../schema/signal.mjs";
2
+ import { Task } from "../schema/task.mjs";
3
+ import { TaskGraph } from "../schema/task-graph.mjs";
4
+ import { Change, ChangeLevel } from "../schema/change.mjs";
5
+ import { Receipt } from "../schema/receipt.mjs";
6
+ //#region src/delivery-metrics/metrics.d.ts
7
+ /**
8
+ * The derived review bucket from the default risk x confidence table. Derived
9
+ * at read time, never stored; "blocked" is a bucket, not a change state.
10
+ */
11
+ type ChangeReviewBucket = "stamp" | "review" | "blocked";
12
+ /** Risk and confidence inputs used to derive a human-review bucket. */
13
+ interface ChangeReviewFactors {
14
+ risk: ChangeLevel;
15
+ confidence: ChangeLevel;
16
+ }
17
+ /** Derives the default human-review bucket from a change's risk and confidence. */
18
+ declare function changeReviewBucket(input: ChangeReviewFactors): ChangeReviewBucket;
19
+ /** Validated ledger rows used to derive Factory metrics and health. */
20
+ interface DeliveryLedgerRows {
21
+ changes: readonly Change[];
22
+ signals: readonly Signal[];
23
+ receipts: readonly Receipt[];
24
+ tasks: readonly Task[];
25
+ /** Validated graph snapshots keyed by rootTaskId. Missing snapshots make health yellow, never runnable. */
26
+ graphs?: ReadonlyMap<string, TaskGraph>;
27
+ }
28
+ /** Options controlling ledger metric evaluation. */
29
+ interface ComputeLedgerMetricsOptions {
30
+ /** Evaluation time; defaults to the current time. */
31
+ now?: Date;
32
+ /** Days a landed change must hold without a rollback. Default 7. */
33
+ holdDays?: number;
34
+ }
35
+ /** Product metrics derived deterministically from ledger rows. */
36
+ interface DeliveryMetrics {
37
+ /**
38
+ * Changes a human merged that reached deployed (rolled_out for high risk)
39
+ * and did not roll back within the hold window. Includes provisional ones.
40
+ */
41
+ landed: number;
42
+ /** Landed changes still inside the hold window; the number may drop. */
43
+ provisional: number;
44
+ /** landed / (landed + rolled back + closed after ready). Null when nothing decided. */
45
+ stickRate: number | null;
46
+ /**
47
+ * Not computable from ledger rows: needs a record of human commits pushed
48
+ * to the pull request after ready, which nothing writes today.
49
+ */
50
+ stampedClean: null;
51
+ /** Median ms from the ready receipt to the merged receipt, per review bucket. */
52
+ humanTimeMs: {
53
+ stamp: number | null;
54
+ review: number | null;
55
+ };
56
+ /** Sum of settled task cost over landed changes / landed. Null until budgets settle. */
57
+ costPerLandedUsd: number | null;
58
+ /** Signals triage turned away plus changes closed in verification. */
59
+ filtered: number;
60
+ }
61
+ /**
62
+ * The six product metrics from ledger rows alone. One rule governs them: a
63
+ * change counts only after a human merged it and it stayed. Activity that
64
+ * never landed is not a result. Compute over rows already filtered to the
65
+ * period and repositories you are reporting on.
66
+ */
67
+ declare function computeLedgerMetrics(rows: DeliveryLedgerRows, options?: ComputeLedgerMetricsOptions): DeliveryMetrics;
68
+ /** Overall repository health severity derived from operational evidence. */
69
+ type RepositoryHealthColor = "green" | "yellow" | "red";
70
+ /** Options controlling repository health evaluation. */
71
+ interface ComputeRepositoryHealthOptions {
72
+ now?: Date;
73
+ /** Open-change cap from factory configuration; omitted disables that check. */
74
+ maxOpenChanges?: number;
75
+ /** Minutes an eligible executable task may remain queued before health turns yellow. Default 5. */
76
+ maxQueuedMinutes?: number;
77
+ }
78
+ /** Explainable repository health derived from ledger and graph state. */
79
+ interface RepositoryHealth {
80
+ readonly color: RepositoryHealthColor;
81
+ /** Every rule that fired, so the color is explainable from rows. */
82
+ readonly reasons: readonly string[];
83
+ }
84
+ /**
85
+ * Green, yellow, or red from rows, never from a model. Budget threshold and
86
+ * onboarding rules are not evaluated here yet: budget notices live on the
87
+ * budgets collection (not in these rows), and onboarding investigations are
88
+ * not recorded.
89
+ */
90
+ declare function computeRepositoryHealth(rows: DeliveryLedgerRows, options?: ComputeRepositoryHealthOptions): RepositoryHealth;
91
+ //#endregion
92
+ export { ChangeReviewBucket, ChangeReviewFactors, ComputeLedgerMetricsOptions, ComputeRepositoryHealthOptions, DeliveryLedgerRows, DeliveryMetrics, RepositoryHealth, RepositoryHealthColor, changeReviewBucket, computeLedgerMetrics, computeRepositoryHealth };
93
+ //# sourceMappingURL=metrics.d.mts.map
@@ -0,0 +1,200 @@
1
+ //#region src/delivery-metrics/metrics.ts
2
+ const bucketTable = {
3
+ low: {
4
+ low: "review",
5
+ medium: "stamp",
6
+ high: "stamp"
7
+ },
8
+ medium: {
9
+ low: "review",
10
+ medium: "review",
11
+ high: "stamp"
12
+ },
13
+ high: {
14
+ low: "blocked",
15
+ medium: "review",
16
+ high: "review"
17
+ }
18
+ };
19
+ /** Derives the default human-review bucket from a change's risk and confidence. */
20
+ function changeReviewBucket(input) {
21
+ return bucketTable[input.risk][input.confidence];
22
+ }
23
+ function collectFacts(rows) {
24
+ const byChange = /* @__PURE__ */ new Map();
25
+ for (const receipt of rows.receipts) {
26
+ if (receipt.kind !== "change" || receipt.changeId === void 0) continue;
27
+ const list = byChange.get(receipt.changeId) ?? [];
28
+ list.push(receipt);
29
+ byChange.set(receipt.changeId, list);
30
+ }
31
+ const at = (receipts, state) => {
32
+ const found = receipts?.find((receipt) => receipt.state === state);
33
+ return found === void 0 ? null : Date.parse(found.recordedAt);
34
+ };
35
+ return rows.changes.map((change) => {
36
+ const receipts = byChange.get(change.id);
37
+ const requiredState = change.risk === "high" ? "rolled_out" : "deployed";
38
+ return {
39
+ change,
40
+ readyAt: at(receipts, "ready"),
41
+ mergedAt: at(receipts, "merged"),
42
+ reachedAt: at(receipts, requiredState),
43
+ rolledBackAt: at(receipts, "rolled_back")
44
+ };
45
+ });
46
+ }
47
+ /** True for a change a human saw as ready and then closed unmerged. */
48
+ function closedAfterReady(facts) {
49
+ return facts.change.state === "closed" && (facts.readyAt !== null || facts.change.decidedBy !== void 0);
50
+ }
51
+ function median(values) {
52
+ if (values.length === 0) return null;
53
+ const sorted = [...values].sort((a, b) => a - b);
54
+ const mid = Math.floor(sorted.length / 2);
55
+ return sorted.length % 2 === 1 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
56
+ }
57
+ /**
58
+ * The six product metrics from ledger rows alone. One rule governs them: a
59
+ * change counts only after a human merged it and it stayed. Activity that
60
+ * never landed is not a result. Compute over rows already filtered to the
61
+ * period and repositories you are reporting on.
62
+ */
63
+ function computeLedgerMetrics(rows, options = {}) {
64
+ const now = (options.now ?? /* @__PURE__ */ new Date()).getTime();
65
+ const holdMs = (options.holdDays ?? 7) * 24 * 60 * 60 * 1e3;
66
+ const facts = collectFacts(rows);
67
+ let landed = 0;
68
+ let provisional = 0;
69
+ let rolledBack = 0;
70
+ let closedAfterReadyCount = 0;
71
+ let closedInVerification = 0;
72
+ const landedChangeIds = /* @__PURE__ */ new Set();
73
+ for (const fact of facts) {
74
+ const { change } = fact;
75
+ if (fact.rolledBackAt !== null) {
76
+ rolledBack += 1;
77
+ continue;
78
+ }
79
+ if (change.state === "closed") {
80
+ if (closedAfterReady(fact)) closedAfterReadyCount += 1;
81
+ else closedInVerification += 1;
82
+ continue;
83
+ }
84
+ if (change.decidedBy !== void 0 && fact.reachedAt !== null) {
85
+ landed += 1;
86
+ landedChangeIds.add(change.id);
87
+ if (now - fact.reachedAt < holdMs) provisional += 1;
88
+ }
89
+ }
90
+ const stickDenominator = landed + rolledBack + closedAfterReadyCount;
91
+ const humanTimes = {
92
+ stamp: [],
93
+ review: []
94
+ };
95
+ for (const fact of facts) {
96
+ if (fact.readyAt === null || fact.mergedAt === null) continue;
97
+ const bucket = changeReviewBucket({
98
+ risk: fact.change.risk,
99
+ confidence: fact.change.confidence
100
+ });
101
+ if (bucket === "blocked") continue;
102
+ humanTimes[bucket].push(fact.mergedAt - fact.readyAt);
103
+ }
104
+ let settledOverLanded = 0;
105
+ let sawSettledCost = false;
106
+ for (const task of rows.tasks) {
107
+ if (task.settledUsd === void 0) continue;
108
+ if (task.kind === "code_change" && task.changeId !== void 0 && landedChangeIds.has(task.changeId)) {
109
+ settledOverLanded += task.settledUsd;
110
+ sawSettledCost = true;
111
+ }
112
+ }
113
+ const rejectedSignals = rows.signals.filter((signal) => signal.outcome !== "pending" && signal.outcome !== "accepted").length;
114
+ return {
115
+ landed,
116
+ provisional,
117
+ stickRate: stickDenominator === 0 ? null : landed / stickDenominator,
118
+ stampedClean: null,
119
+ humanTimeMs: {
120
+ stamp: median(humanTimes.stamp),
121
+ review: median(humanTimes.review)
122
+ },
123
+ costPerLandedUsd: sawSettledCost && landed > 0 ? settledOverLanded / landed : null,
124
+ filtered: rejectedSignals + closedInVerification
125
+ };
126
+ }
127
+ const openChangeStates = /* @__PURE__ */ new Set(["verifying", "ready"]);
128
+ function matchesGraphNode(task, node) {
129
+ return task.id === node.taskId && task.rootTaskId === node.admission.rootTaskId && task.parentTaskId === node.parentTaskId && task.repositoryIds.length === node.admission.repositoryIds.length && task.repositoryIds.every((id, index) => id === node.admission.repositoryIds[index]);
130
+ }
131
+ function dependencyStatus(task, tasks, graphs) {
132
+ const graph = graphs?.get(task.rootTaskId);
133
+ const node = graph?.nodes.find((entry) => entry.taskId === task.id);
134
+ if (!graph || graph.rootTaskId !== task.rootTaskId || !node || !matchesGraphNode(task, node)) return "unknown";
135
+ let ready = true;
136
+ for (const id of node.dependencies) {
137
+ const dependency = tasks.get(id);
138
+ const dependencyNode = graph.nodes.find((entry) => entry.taskId === id);
139
+ if (!dependency || !dependencyNode || !matchesGraphNode(dependency, dependencyNode)) return "unknown";
140
+ if (dependency.state !== "succeeded") ready = false;
141
+ }
142
+ return ready ? "ready" : "waiting";
143
+ }
144
+ /**
145
+ * Green, yellow, or red from rows, never from a model. Budget threshold and
146
+ * onboarding rules are not evaluated here yet: budget notices live on the
147
+ * budgets collection (not in these rows), and onboarding investigations are
148
+ * not recorded.
149
+ */
150
+ function computeRepositoryHealth(rows, options = {}) {
151
+ const now = (options.now ?? /* @__PURE__ */ new Date()).getTime();
152
+ const dayMs = 864e5;
153
+ const red = [];
154
+ const yellow = [];
155
+ const tasksById = new Map(rows.tasks.map((task) => [task.id, task]));
156
+ for (const receipt of rows.receipts) {
157
+ if (now - Date.parse(receipt.recordedAt) >= dayMs) continue;
158
+ if (receipt.state === "rolled_back") red.push(`rollback in the last 24h (${receipt.receiptId})`);
159
+ if (receipt.state === "cancelled" && receipt.reason?.toLowerCase().includes("kill")) red.push(`kill in the last 24h (${receipt.receiptId})`);
160
+ }
161
+ const openQuestions = rows.tasks.filter((task) => task.kind === "question" && task.state === "needs_human").length;
162
+ if (openQuestions > 0) yellow.push(`${openQuestions} open question(s)`);
163
+ const needsHuman = rows.tasks.filter((task) => task.kind !== "question" && task.state === "needs_human").length;
164
+ if (needsHuman > 0) yellow.push(`${needsHuman} task(s) waiting on a human`);
165
+ const maxQueuedMinutes = options.maxQueuedMinutes ?? 5;
166
+ let staleQueued = 0;
167
+ let unknownDependencies = 0;
168
+ for (const task of rows.tasks) {
169
+ const status = dependencyStatus(task, tasksById, rows.graphs);
170
+ if (status === "unknown") unknownDependencies += 1;
171
+ if (status === "ready" && task.state === "queued" && task.approval !== "required" && task.approval !== "denied" && now - Date.parse(task.updatedAt) >= maxQueuedMinutes * 60 * 1e3) staleQueued += 1;
172
+ }
173
+ if (unknownDependencies > 0) yellow.push(`${unknownDependencies} task(s) with missing or inconsistent graph/dependency snapshots`);
174
+ if (staleQueued > 0) yellow.push(`${staleQueued} eligible task(s) queued for at least ${maxQueuedMinutes} minutes`);
175
+ const blocked = rows.changes.filter((change) => openChangeStates.has(change.state) && changeReviewBucket({
176
+ risk: change.risk,
177
+ confidence: change.confidence
178
+ }) === "blocked").length;
179
+ if (blocked > 0) yellow.push(`${blocked} change(s) in the blocked bucket`);
180
+ if (options.maxOpenChanges !== void 0) {
181
+ const open = rows.changes.filter((change) => openChangeStates.has(change.state)).length;
182
+ if (open >= options.maxOpenChanges) yellow.push(`open changes at the cap (${open}/${options.maxOpenChanges})`);
183
+ }
184
+ if (red.length > 0) return {
185
+ color: "red",
186
+ reasons: red
187
+ };
188
+ if (yellow.length > 0) return {
189
+ color: "yellow",
190
+ reasons: yellow
191
+ };
192
+ return {
193
+ color: "green",
194
+ reasons: []
195
+ };
196
+ }
197
+ //#endregion
198
+ export { changeReviewBucket, computeLedgerMetrics, computeRepositoryHealth };
199
+
200
+ //# sourceMappingURL=metrics.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.mjs","names":[],"sources":["../../src/delivery-metrics/metrics.ts"],"sourcesContent":["import type { Change, ChangeLevel } from \"../schema/change\";\nimport type { Receipt } from \"../schema/receipt\";\nimport type { Signal } from \"../schema/signal\";\nimport type { Task } from \"../schema/task\";\nimport type { TaskGraph, TaskGraphNode } from \"../schema/task-graph\";\n\n/**\n * The derived review bucket from the default risk x confidence table. Derived\n * at read time, never stored; \"blocked\" is a bucket, not a change state.\n */\nexport type ChangeReviewBucket = \"stamp\" | \"review\" | \"blocked\";\n\nconst bucketTable: Record<ChangeLevel, Record<ChangeLevel, ChangeReviewBucket>> = {\n low: { low: \"review\", medium: \"stamp\", high: \"stamp\" },\n medium: { low: \"review\", medium: \"review\", high: \"stamp\" },\n high: { low: \"blocked\", medium: \"review\", high: \"review\" },\n};\n\n/** Risk and confidence inputs used to derive a human-review bucket. */\nexport interface ChangeReviewFactors {\n risk: ChangeLevel;\n confidence: ChangeLevel;\n}\n\n/** Derives the default human-review bucket from a change's risk and confidence. */\nexport function changeReviewBucket(input: ChangeReviewFactors): ChangeReviewBucket {\n return bucketTable[input.risk][input.confidence];\n}\n\n/** Validated ledger rows used to derive Factory metrics and health. */\nexport interface DeliveryLedgerRows {\n changes: readonly Change[];\n signals: readonly Signal[];\n receipts: readonly Receipt[];\n tasks: readonly Task[];\n /** Validated graph snapshots keyed by rootTaskId. Missing snapshots make health yellow, never runnable. */\n graphs?: ReadonlyMap<string, TaskGraph>;\n}\n\n/** Options controlling ledger metric evaluation. */\nexport interface ComputeLedgerMetricsOptions {\n /** Evaluation time; defaults to the current time. */\n now?: Date;\n /** Days a landed change must hold without a rollback. Default 7. */\n holdDays?: number;\n}\n\n/** Product metrics derived deterministically from ledger rows. */\nexport interface DeliveryMetrics {\n /**\n * Changes a human merged that reached deployed (rolled_out for high risk)\n * and did not roll back within the hold window. Includes provisional ones.\n */\n landed: number;\n /** Landed changes still inside the hold window; the number may drop. */\n provisional: number;\n /** landed / (landed + rolled back + closed after ready). Null when nothing decided. */\n stickRate: number | null;\n /**\n * Not computable from ledger rows: needs a record of human commits pushed\n * to the pull request after ready, which nothing writes today.\n */\n stampedClean: null;\n /** Median ms from the ready receipt to the merged receipt, per review bucket. */\n humanTimeMs: { stamp: number | null; review: number | null };\n /** Sum of settled task cost over landed changes / landed. Null until budgets settle. */\n costPerLandedUsd: number | null;\n /** Signals triage turned away plus changes closed in verification. */\n filtered: number;\n}\n\ninterface ChangeFacts {\n change: Change;\n readyAt: number | null;\n mergedAt: number | null;\n reachedAt: number | null;\n rolledBackAt: number | null;\n}\n\nfunction collectFacts(rows: DeliveryLedgerRows): ChangeFacts[] {\n const byChange = new Map<string, Receipt[]>();\n for (const receipt of rows.receipts) {\n if (receipt.kind !== \"change\" || receipt.changeId === undefined) continue;\n const list = byChange.get(receipt.changeId) ?? [];\n list.push(receipt);\n byChange.set(receipt.changeId, list);\n }\n const at = (receipts: Receipt[] | undefined, state: string): number | null => {\n const found = receipts?.find((receipt) => receipt.state === state);\n return found === undefined ? null : Date.parse(found.recordedAt);\n };\n return rows.changes.map((change) => {\n const receipts = byChange.get(change.id);\n const requiredState = change.risk === \"high\" ? \"rolled_out\" : \"deployed\";\n return {\n change,\n readyAt: at(receipts, \"ready\"),\n mergedAt: at(receipts, \"merged\"),\n reachedAt: at(receipts, requiredState),\n rolledBackAt: at(receipts, \"rolled_back\"),\n };\n });\n}\n\n/** True for a change a human saw as ready and then closed unmerged. */\nfunction closedAfterReady(facts: ChangeFacts): boolean {\n return (\n facts.change.state === \"closed\" &&\n (facts.readyAt !== null || facts.change.decidedBy !== undefined)\n );\n}\n\nfunction median(values: number[]): number | null {\n if (values.length === 0) return null;\n const sorted = [...values].sort((a, b) => a - b);\n const mid = Math.floor(sorted.length / 2);\n return sorted.length % 2 === 1 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;\n}\n\n/**\n * The six product metrics from ledger rows alone. One rule governs them: a\n * change counts only after a human merged it and it stayed. Activity that\n * never landed is not a result. Compute over rows already filtered to the\n * period and repositories you are reporting on.\n */\nexport function computeLedgerMetrics(\n rows: DeliveryLedgerRows,\n options: ComputeLedgerMetricsOptions = {},\n): DeliveryMetrics {\n const now = (options.now ?? new Date()).getTime();\n const holdMs = (options.holdDays ?? 7) * 24 * 60 * 60 * 1000;\n const facts = collectFacts(rows);\n\n let landed = 0;\n let provisional = 0;\n let rolledBack = 0;\n let closedAfterReadyCount = 0;\n let closedInVerification = 0;\n const landedChangeIds = new Set<string>();\n\n for (const fact of facts) {\n const { change } = fact;\n if (fact.rolledBackAt !== null) {\n rolledBack += 1;\n continue;\n }\n if (change.state === \"closed\") {\n if (closedAfterReady(fact)) closedAfterReadyCount += 1;\n else closedInVerification += 1;\n continue;\n }\n if (change.decidedBy !== undefined && fact.reachedAt !== null) {\n landed += 1;\n landedChangeIds.add(change.id);\n if (now - fact.reachedAt < holdMs) provisional += 1;\n }\n }\n\n const stickDenominator = landed + rolledBack + closedAfterReadyCount;\n const humanTimes: Record<\"stamp\" | \"review\", number[]> = { stamp: [], review: [] };\n for (const fact of facts) {\n if (fact.readyAt === null || fact.mergedAt === null) continue;\n const bucket = changeReviewBucket({\n risk: fact.change.risk,\n confidence: fact.change.confidence,\n });\n if (bucket === \"blocked\") continue;\n humanTimes[bucket].push(fact.mergedAt - fact.readyAt);\n }\n\n let settledOverLanded = 0;\n let sawSettledCost = false;\n for (const task of rows.tasks) {\n if (task.settledUsd === undefined) continue;\n if (\n task.kind === \"code_change\" &&\n task.changeId !== undefined &&\n landedChangeIds.has(task.changeId)\n ) {\n settledOverLanded += task.settledUsd;\n sawSettledCost = true;\n }\n }\n\n const rejectedSignals = rows.signals.filter(\n (signal) => signal.outcome !== \"pending\" && signal.outcome !== \"accepted\",\n ).length;\n\n return {\n landed,\n provisional,\n stickRate: stickDenominator === 0 ? null : landed / stickDenominator,\n stampedClean: null,\n humanTimeMs: { stamp: median(humanTimes.stamp), review: median(humanTimes.review) },\n costPerLandedUsd: sawSettledCost && landed > 0 ? settledOverLanded / landed : null,\n filtered: rejectedSignals + closedInVerification,\n };\n}\n\n/** Overall repository health severity derived from operational evidence. */\nexport type RepositoryHealthColor = \"green\" | \"yellow\" | \"red\";\n\n/** Options controlling repository health evaluation. */\nexport interface ComputeRepositoryHealthOptions {\n now?: Date;\n /** Open-change cap from factory configuration; omitted disables that check. */\n maxOpenChanges?: number;\n /** Minutes an eligible executable task may remain queued before health turns yellow. Default 5. */\n maxQueuedMinutes?: number;\n}\n\n/** Explainable repository health derived from ledger and graph state. */\nexport interface RepositoryHealth {\n readonly color: RepositoryHealthColor;\n /** Every rule that fired, so the color is explainable from rows. */\n readonly reasons: readonly string[];\n}\n\nconst openChangeStates = new Set([\"verifying\", \"ready\"]);\n\nfunction matchesGraphNode(task: Task, node: TaskGraphNode): boolean {\n return (\n task.id === node.taskId &&\n task.rootTaskId === node.admission.rootTaskId &&\n task.parentTaskId === node.parentTaskId &&\n task.repositoryIds.length === node.admission.repositoryIds.length &&\n task.repositoryIds.every((id, index) => id === node.admission.repositoryIds[index])\n );\n}\n\nfunction dependencyStatus(\n task: Task,\n tasks: ReadonlyMap<string, Task>,\n graphs: DeliveryLedgerRows[\"graphs\"],\n): \"ready\" | \"waiting\" | \"unknown\" {\n const graph = graphs?.get(task.rootTaskId);\n const node = graph?.nodes.find((entry) => entry.taskId === task.id);\n if (!graph || graph.rootTaskId !== task.rootTaskId || !node || !matchesGraphNode(task, node)) {\n return \"unknown\";\n }\n let ready = true;\n for (const id of node.dependencies) {\n const dependency = tasks.get(id);\n const dependencyNode = graph.nodes.find((entry) => entry.taskId === id);\n if (!dependency || !dependencyNode || !matchesGraphNode(dependency, dependencyNode))\n return \"unknown\";\n if (dependency.state !== \"succeeded\") ready = false;\n }\n return ready ? \"ready\" : \"waiting\";\n}\n\n/**\n * Green, yellow, or red from rows, never from a model. Budget threshold and\n * onboarding rules are not evaluated here yet: budget notices live on the\n * budgets collection (not in these rows), and onboarding investigations are\n * not recorded.\n */\nexport function computeRepositoryHealth(\n rows: DeliveryLedgerRows,\n options: ComputeRepositoryHealthOptions = {},\n): RepositoryHealth {\n const now = (options.now ?? new Date()).getTime();\n const dayMs = 24 * 60 * 60 * 1000;\n const red: string[] = [];\n const yellow: string[] = [];\n const tasksById = new Map(rows.tasks.map((task) => [task.id, task]));\n\n for (const receipt of rows.receipts) {\n if (now - Date.parse(receipt.recordedAt) >= dayMs) continue;\n if (receipt.state === \"rolled_back\")\n red.push(`rollback in the last 24h (${receipt.receiptId})`);\n if (receipt.state === \"cancelled\" && receipt.reason?.toLowerCase().includes(\"kill\")) {\n red.push(`kill in the last 24h (${receipt.receiptId})`);\n }\n }\n\n const openQuestions = rows.tasks.filter(\n (task) => task.kind === \"question\" && task.state === \"needs_human\",\n ).length;\n if (openQuestions > 0) yellow.push(`${openQuestions} open question(s)`);\n\n const needsHuman = rows.tasks.filter(\n (task) => task.kind !== \"question\" && task.state === \"needs_human\",\n ).length;\n if (needsHuman > 0) yellow.push(`${needsHuman} task(s) waiting on a human`);\n\n const maxQueuedMinutes = options.maxQueuedMinutes ?? 5;\n let staleQueued = 0;\n let unknownDependencies = 0;\n for (const task of rows.tasks) {\n const status = dependencyStatus(task, tasksById, rows.graphs);\n if (status === \"unknown\") unknownDependencies += 1;\n if (\n status === \"ready\" &&\n task.state === \"queued\" &&\n task.approval !== \"required\" &&\n task.approval !== \"denied\" &&\n now - Date.parse(task.updatedAt) >= maxQueuedMinutes * 60 * 1000\n )\n staleQueued += 1;\n }\n if (unknownDependencies > 0) {\n yellow.push(\n `${unknownDependencies} task(s) with missing or inconsistent graph/dependency snapshots`,\n );\n }\n if (staleQueued > 0) {\n yellow.push(`${staleQueued} eligible task(s) queued for at least ${maxQueuedMinutes} minutes`);\n }\n\n const blocked = rows.changes.filter(\n (change) =>\n openChangeStates.has(change.state) &&\n changeReviewBucket({ risk: change.risk, confidence: change.confidence }) === \"blocked\",\n ).length;\n if (blocked > 0) yellow.push(`${blocked} change(s) in the blocked bucket`);\n\n if (options.maxOpenChanges !== undefined) {\n const open = rows.changes.filter((change) => openChangeStates.has(change.state)).length;\n if (open >= options.maxOpenChanges) {\n yellow.push(`open changes at the cap (${open}/${options.maxOpenChanges})`);\n }\n }\n\n if (red.length > 0) return { color: \"red\", reasons: red };\n if (yellow.length > 0) return { color: \"yellow\", reasons: yellow };\n return { color: \"green\", reasons: [] };\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { ChangeReviewBucket, ComputeLedgerMetricsOptions, ComputeRepositoryHealthOptions, DeliveryLedgerRows, DeliveryMetrics, RepositoryHealth, RepositoryHealthColor, changeReviewBucket, computeLedgerMetrics, computeRepositoryHealth } from "./delivery-metrics/metrics.mjs";
2
+ export { type ChangeReviewBucket, type ComputeLedgerMetricsOptions, type ComputeRepositoryHealthOptions, type DeliveryLedgerRows, type DeliveryMetrics, type RepositoryHealth, type RepositoryHealthColor, changeReviewBucket, computeLedgerMetrics, computeRepositoryHealth };
@@ -0,0 +1,2 @@
1
+ import { changeReviewBucket, computeLedgerMetrics, computeRepositoryHealth } from "./delivery-metrics/metrics.mjs";
2
+ export { changeReviewBucket, computeLedgerMetrics, computeRepositoryHealth };
@@ -0,0 +1,81 @@
1
+ import { TaskId } from "./schema/id.mjs";
2
+ import { AgentRouteBinding } from "./schema/agent-route.mjs";
3
+ import { ExecutionReference, Task } from "./schema/task.mjs";
4
+ import "./agent-routes.mjs";
5
+ import { DateClock } from "./schema/common.mjs";
6
+ import { BudgetLimits, BudgetMonth } from "./schema/budget.mjs";
7
+ import { FactoryStores } from "./store/engine.mjs";
8
+ //#region src/dispatch.d.ts
9
+ /** Minimal persistence capabilities required to gate and record one dispatch. */
10
+ type DispatchStores = Pick<FactoryStores, "graphs" | "receipts" | "tasks">;
11
+ /** The provider may have accepted the request. Keep this attempt for recovery using its original launch key. */
12
+ declare class ExecutionLaunchPendingError extends Error {}
13
+ /** Stable machine-readable reason a Task could not be dispatched. */
14
+ type DispatchRefusalCode = "route_mismatch" | "awaiting_approval" | "approval_denied" | "dependencies_unsatisfied" | "dispatch_race";
15
+ /** Non-terminal dispatch refusal that identifies the gated Task and retryable reason. */
16
+ declare class DispatchRefusedError extends Error {
17
+ readonly taskId: TaskId;
18
+ readonly code: DispatchRefusalCode;
19
+ readonly reason: string;
20
+ constructor(taskId: TaskId, code: DispatchRefusalCode, reason: string);
21
+ }
22
+ /**
23
+ * An exhausted budget is definitive, not a retryable refusal: the task has
24
+ * already been failed and receipted when this throws, so it never re-enters
25
+ * the sweep. Operator retry recovers it once budget frees up.
26
+ */
27
+ declare class BudgetExhaustedError extends Error {
28
+ readonly taskId: TaskId;
29
+ readonly month: BudgetMonth;
30
+ readonly spendUsd: number;
31
+ constructor(taskId: TaskId, month: BudgetMonth, spendUsd: number);
32
+ }
33
+ /** Factory-wide reservation limits and snapshot used to gate one dispatch. */
34
+ interface DispatchBudget {
35
+ limits: Pick<BudgetLimits, "totalUsd" | "perTaskReservationUsd">;
36
+ /** Defaults to the current UTC month. */
37
+ month?: BudgetMonth;
38
+ now?: DateClock;
39
+ /** A dispatcher-wide snapshot used to calculate spend without another list. */
40
+ tasks?: readonly Task[];
41
+ }
42
+ interface DispatchContext {
43
+ /** How many launches to try before the task fails for good; a failure re-queues for the next sweep. */
44
+ maxLaunchAttempts?: number;
45
+ /** Reserve-before-execution policy for this dispatch. */
46
+ budget?: DispatchBudget;
47
+ }
48
+ /** Exact provider launch or explicit workflow-owned execution for one Task dispatch. */
49
+ type DispatchOptions = DispatchContext & ({
50
+ /** Required exact binding when the Task declares an agent route. */
51
+ route?: AgentRouteBinding;
52
+ /**
53
+ * Starts the durable execution for the task (an Eve agent run). Runs after
54
+ * the task has won the queued -> running transition. Its provider reference
55
+ * is mandatory so recovery can always find an accepted run.
56
+ */
57
+ launch: (task: Task) => Promise<ExecutionReference>;
58
+ workflowOwned?: never;
59
+ } | {
60
+ /** Marks work whose workflow advances the running Task without a provider session. */
61
+ workflowOwned: true;
62
+ route?: never;
63
+ launch?: never;
64
+ });
65
+ /** Task identity and exact launch mode supplied to one dispatch attempt. */
66
+ type DispatchTaskInput = {
67
+ readonly taskId: TaskId;
68
+ } & DispatchOptions;
69
+ /** Default launch-attempt allowance covering delayed identity provisioning. */
70
+ declare const DEFAULT_MAX_LAUNCH_ATTEMPTS = 45;
71
+ /**
72
+ * The handshake between recorded work and execution: win the store-gated
73
+ * transition (at most one dispatcher can succeed), then start the run. A
74
+ * launch failure re-queues the task (bumping its attempt) until the attempt
75
+ * limit, then fails it; a crash between the transition and the launch leaves
76
+ * a running task with no execution, which the sweep reaps.
77
+ */
78
+ declare function dispatchTask(stores: DispatchStores, input: DispatchTaskInput): Promise<Task>;
79
+ //#endregion
80
+ export { BudgetExhaustedError, DEFAULT_MAX_LAUNCH_ATTEMPTS, DispatchBudget, DispatchOptions, DispatchRefusalCode, DispatchRefusedError, DispatchStores, DispatchTaskInput, ExecutionLaunchPendingError, dispatchTask };
81
+ //# sourceMappingURL=dispatch.d.mts.map
@@ -0,0 +1,140 @@
1
+ import { budgetMonthOf, canReserve, computeMonthSpendUsd } from "./budget.mjs";
2
+ import { InvalidTransitionError } from "./schema/transitions.mjs";
3
+ import { routeKey } from "./agent-routes.mjs";
4
+ import { RecordNotFoundError, VersionConflictError } from "./store/driver.mjs";
5
+ //#region src/dispatch.ts
6
+ /** The provider may have accepted the request. Keep this attempt for recovery using its original launch key. */
7
+ var ExecutionLaunchPendingError = class extends Error {};
8
+ /** Non-terminal dispatch refusal that identifies the gated Task and retryable reason. */
9
+ var DispatchRefusedError = class extends Error {
10
+ taskId;
11
+ code;
12
+ reason;
13
+ constructor(taskId, code, reason) {
14
+ super(`dispatch of "${taskId}" refused: ${reason}`);
15
+ this.name = "DispatchRefusedError";
16
+ this.taskId = taskId;
17
+ this.code = code;
18
+ this.reason = reason;
19
+ }
20
+ };
21
+ /**
22
+ * An exhausted budget is definitive, not a retryable refusal: the task has
23
+ * already been failed and receipted when this throws, so it never re-enters
24
+ * the sweep. Operator retry recovers it once budget frees up.
25
+ */
26
+ var BudgetExhaustedError = class extends Error {
27
+ taskId;
28
+ month;
29
+ spendUsd;
30
+ constructor(taskId, month, spendUsd) {
31
+ super(`dispatch of "${taskId}" refused: budget for ${month} is exhausted at $${spendUsd}`);
32
+ this.name = "BudgetExhaustedError";
33
+ this.taskId = taskId;
34
+ this.month = month;
35
+ this.spendUsd = spendUsd;
36
+ }
37
+ };
38
+ /** Default launch-attempt allowance covering delayed identity provisioning. */
39
+ const DEFAULT_MAX_LAUNCH_ATTEMPTS = 45;
40
+ /**
41
+ * The handshake between recorded work and execution: win the store-gated
42
+ * transition (at most one dispatcher can succeed), then start the run. A
43
+ * launch failure re-queues the task (bumping its attempt) until the attempt
44
+ * limit, then fails it; a crash between the transition and the launch leaves
45
+ * a running task with no execution, which the sweep reaps.
46
+ */
47
+ async function dispatchTask(stores, input) {
48
+ const { taskId } = input;
49
+ const options = input;
50
+ const task = await stores.tasks.get(taskId);
51
+ if (task === null) throw new RecordNotFoundError("tasks", taskId);
52
+ const route = task.work.route;
53
+ if (route !== void 0 && options.workflowOwned === true) throw new DispatchRefusedError(taskId, "route_mismatch", `agent route ${routeKey(route)} requires a launcher`);
54
+ if (route !== void 0 && (options.route?.id !== route.id || options.route?.version !== route.version)) throw new DispatchRefusedError(taskId, "route_mismatch", `launch requires exact agent route ${routeKey(route)}`);
55
+ if (task.approval === "required") throw new DispatchRefusedError(taskId, "awaiting_approval", "awaiting human approval");
56
+ if (task.approval === "denied") throw new DispatchRefusedError(taskId, "approval_denied", "approval was denied");
57
+ if (!await stores.graphs.dependenciesSatisfied({
58
+ rootTaskId: task.rootTaskId,
59
+ taskId: task.id
60
+ })) throw new DispatchRefusedError(taskId, "dependencies_unsatisfied", "graph dependencies must all be succeeded before dispatch");
61
+ let reservation;
62
+ if (options.budget !== void 0 && task.state === "queued") {
63
+ const budgetNow = (options.budget.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
64
+ const month = options.budget.month ?? budgetMonthOf(budgetNow);
65
+ const rows = (options.budget.tasks ?? await stores.tasks.list()).map((row) => row.id === taskId ? {
66
+ ...row,
67
+ reservedUsd: void 0
68
+ } : row);
69
+ const spendUsd = computeMonthSpendUsd(rows, month);
70
+ const { totalUsd, perTaskReservationUsd } = options.budget.limits;
71
+ if (!canReserve(spendUsd, options.budget.limits)) {
72
+ const reason = `budget exhausted: ${month} spend $${spendUsd} cannot fit a $${perTaskReservationUsd} reservation under $${totalUsd}`;
73
+ try {
74
+ await stores.tasks.transition(taskId, "failed", {
75
+ reason,
76
+ expectFrom: "queued"
77
+ });
78
+ } catch (error) {
79
+ if (error instanceof InvalidTransitionError || error instanceof VersionConflictError) throw new DispatchRefusedError(taskId, "dispatch_race", "another dispatcher took the task");
80
+ throw error;
81
+ }
82
+ await stores.receipts.append({
83
+ repositoryIds: task.repositoryIds,
84
+ taskId,
85
+ kind: task.kind,
86
+ state: "reservation_refused",
87
+ phase: "budget",
88
+ reason,
89
+ attempt: task.attempt,
90
+ dedupeKey: `task:${taskId}:reservation_refused:${task.attempt}`
91
+ }).catch(() => void 0);
92
+ throw new BudgetExhaustedError(taskId, month, spendUsd);
93
+ }
94
+ reservation = {
95
+ reservedUsd: perTaskReservationUsd,
96
+ reservedAt: budgetNow
97
+ };
98
+ }
99
+ const running = await stores.tasks.transition(taskId, "running", {
100
+ expectFrom: "queued",
101
+ expectAttempt: task.attempt,
102
+ ...reservation === void 0 ? {} : { patch: {
103
+ reservedUsd: reservation.reservedUsd,
104
+ reservedAt: reservation.reservedAt
105
+ } },
106
+ ...task.attempt > 1 ? { phase: "retry" } : {}
107
+ });
108
+ if (options.workflowOwned === true) return running;
109
+ let execution;
110
+ try {
111
+ execution = await options.launch(running);
112
+ } catch (error) {
113
+ if (error instanceof ExecutionLaunchPendingError) throw error;
114
+ const reason = (error instanceof Error ? error.message : String(error)).slice(0, 500);
115
+ const limit = options.maxLaunchAttempts ?? 45;
116
+ if (running.attempt < limit) {
117
+ const retryReason = `launch failed (attempt ${running.attempt} of ${limit}), will retry: ${reason}`;
118
+ await stores.tasks.transition(taskId, "queued", {
119
+ reason: retryReason,
120
+ phase: "retry",
121
+ expectFrom: "running",
122
+ expectAttempt: running.attempt
123
+ }).catch(() => void 0);
124
+ } else await stores.tasks.transition(taskId, "failed", {
125
+ reason: `launch failed after ${running.attempt} attempts: ${reason}`,
126
+ phase: "retry",
127
+ expectFrom: "running",
128
+ expectAttempt: running.attempt
129
+ }).catch(() => void 0);
130
+ throw error;
131
+ }
132
+ return stores.tasks.recordExecution(taskId, {
133
+ execution,
134
+ expectAttempt: running.attempt
135
+ });
136
+ }
137
+ //#endregion
138
+ export { BudgetExhaustedError, DEFAULT_MAX_LAUNCH_ATTEMPTS, DispatchRefusedError, ExecutionLaunchPendingError, dispatchTask };
139
+
140
+ //# sourceMappingURL=dispatch.mjs.map