@vercel/factory 0.0.13 → 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 (458) hide show
  1. package/README.md +307 -1
  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 +77 -0
  14. package/dist/api.mjs +1087 -0
  15. package/dist/api.mjs.map +1 -0
  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 +44 -8
  29. package/dist/blob/index.mjs +159 -35
  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 -2
  92. package/dist/connectors/index.mjs +4 -26
  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 -41
  103. package/dist/connectors/slack.mjs +13 -69
  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 -1561
  124. package/dist/eve/index.mjs +244 -1063
  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 +250 -37
  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 -314
  175. package/dist/index.mjs +14 -1124
  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-DwCihQr4.mjs → schema/common.mjs} +7 -14
  303. package/dist/schema/common.mjs.map +1 -0
  304. package/dist/schema/communication.d.mts +52 -0
  305. package/dist/schema/communication.mjs +35 -0
  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 -40
  443. package/dist/approval-CwhTP1XB.mjs +0 -269
  444. package/dist/approval-CwhTP1XB.mjs.map +0 -1
  445. package/dist/change-CzEAoFVE.mjs +0 -620
  446. package/dist/change-CzEAoFVE.mjs.map +0 -1
  447. package/dist/dispatch-B77cuPxh.d.mts +0 -713
  448. package/dist/driver-BJutB0PE.d.mts +0 -34
  449. package/dist/driver-Bbd25rNM.mjs +0 -23
  450. package/dist/driver-Bbd25rNM.mjs.map +0 -1
  451. package/dist/factory-config-BJcjsPKl.mjs +0 -86
  452. package/dist/factory-config-BJcjsPKl.mjs.map +0 -1
  453. package/dist/factory-config-D2yU3G0c.d.mts +0 -58
  454. package/dist/github-DXsxboUL.d.mts +0 -271
  455. package/dist/id-DwCihQr4.mjs.map +0 -1
  456. package/dist/index-Cy4jfXh5.d.mts +0 -67
  457. package/dist/index.mjs.map +0 -1
  458. package/dist/signal-CHVEmzvI.d.mts +0 -59
@@ -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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch.mjs","names":[],"sources":["../src/dispatch.ts"],"sourcesContent":["import { budgetMonthOf, canReserve, computeMonthSpendUsd } from \"./budget\";\nimport type { AgentRouteBinding } from \"./agent-routes\";\nimport type { BudgetLimits, BudgetMonth } from \"./schema/budget\";\nimport type { DateClock } from \"./schema/common\";\nimport type { TaskId } from \"./schema/id\";\nimport type { ExecutionReference, Task } from \"./schema/task\";\nimport { routeKey } from \"./agent-routes\";\nimport { InvalidTransitionError } from \"./schema/transitions\";\nimport { RecordNotFoundError, VersionConflictError } from \"./store/driver\";\nimport type { FactoryStores } from \"./store/engine\";\n\n/** Minimal persistence capabilities required to gate and record one dispatch. */\nexport type DispatchStores = Pick<FactoryStores, \"graphs\" | \"receipts\" | \"tasks\">;\n\n/** The provider may have accepted the request. Keep this attempt for recovery using its original launch key. */\nexport class ExecutionLaunchPendingError extends Error {}\n\n/** Stable machine-readable reason a Task could not be dispatched. */\nexport type DispatchRefusalCode =\n | \"route_mismatch\"\n | \"awaiting_approval\"\n | \"approval_denied\"\n | \"dependencies_unsatisfied\"\n | \"dispatch_race\";\n\n/** Non-terminal dispatch refusal that identifies the gated Task and retryable reason. */\nexport class DispatchRefusedError extends Error {\n readonly taskId: TaskId;\n readonly code: DispatchRefusalCode;\n readonly reason: string;\n\n constructor(taskId: TaskId, code: DispatchRefusalCode, reason: string) {\n super(`dispatch of \"${taskId}\" refused: ${reason}`);\n this.name = \"DispatchRefusedError\";\n this.taskId = taskId;\n this.code = code;\n this.reason = reason;\n }\n}\n\n/**\n * An exhausted budget is definitive, not a retryable refusal: the task has\n * already been failed and receipted when this throws, so it never re-enters\n * the sweep. Operator retry recovers it once budget frees up.\n */\nexport class BudgetExhaustedError extends Error {\n readonly taskId: TaskId;\n readonly month: BudgetMonth;\n readonly spendUsd: number;\n\n constructor(taskId: TaskId, month: BudgetMonth, spendUsd: number) {\n super(`dispatch of \"${taskId}\" refused: budget for ${month} is exhausted at $${spendUsd}`);\n this.name = \"BudgetExhaustedError\";\n this.taskId = taskId;\n this.month = month;\n this.spendUsd = spendUsd;\n }\n}\n\n/** Factory-wide reservation limits and snapshot used to gate one dispatch. */\nexport interface DispatchBudget {\n limits: Pick<BudgetLimits, \"totalUsd\" | \"perTaskReservationUsd\">;\n /** Defaults to the current UTC month. */\n month?: BudgetMonth;\n now?: DateClock;\n /** A dispatcher-wide snapshot used to calculate spend without another list. */\n tasks?: readonly Task[];\n}\n\ninterface DispatchContext {\n /** How many launches to try before the task fails for good; a failure re-queues for the next sweep. */\n maxLaunchAttempts?: number;\n /** Reserve-before-execution policy for this dispatch. */\n budget?: DispatchBudget;\n}\n\n/** Exact provider launch or explicit workflow-owned execution for one Task dispatch. */\nexport type DispatchOptions = DispatchContext &\n (\n | {\n /** Required exact binding when the Task declares an agent route. */\n route?: AgentRouteBinding;\n /**\n * Starts the durable execution for the task (an Eve agent run). Runs after\n * the task has won the queued -> running transition. Its provider reference\n * is mandatory so recovery can always find an accepted run.\n */\n launch: (task: Task) => Promise<ExecutionReference>;\n workflowOwned?: never;\n }\n | {\n /** Marks work whose workflow advances the running Task without a provider session. */\n workflowOwned: true;\n route?: never;\n launch?: never;\n }\n );\n\n/** Task identity and exact launch mode supplied to one dispatch attempt. */\nexport type DispatchTaskInput = { readonly taskId: TaskId } & DispatchOptions;\n\n// A fresh Vercel project rejects its own agents' sessions until its identity\n// finishes provisioning; at one sweep per minute the budget must outlast that.\n/** Default launch-attempt allowance covering delayed identity provisioning. */\nexport const DEFAULT_MAX_LAUNCH_ATTEMPTS = 45;\n\n/**\n * The handshake between recorded work and execution: win the store-gated\n * transition (at most one dispatcher can succeed), then start the run. A\n * launch failure re-queues the task (bumping its attempt) until the attempt\n * limit, then fails it; a crash between the transition and the launch leaves\n * a running task with no execution, which the sweep reaps.\n */\nexport async function dispatchTask(\n stores: DispatchStores,\n input: DispatchTaskInput,\n): Promise<Task> {\n const { taskId } = input;\n const options: DispatchOptions = input;\n const task = await stores.tasks.get(taskId);\n if (task === null) {\n throw new RecordNotFoundError(\"tasks\", taskId);\n }\n const route = task.work.route;\n if (route !== undefined && options.workflowOwned === true) {\n throw new DispatchRefusedError(\n taskId,\n \"route_mismatch\",\n `agent route ${routeKey(route)} requires a launcher`,\n );\n }\n if (\n route !== undefined &&\n (options.route?.id !== route.id || options.route?.version !== route.version)\n ) {\n throw new DispatchRefusedError(\n taskId,\n \"route_mismatch\",\n `launch requires exact agent route ${routeKey(route)}`,\n );\n }\n if (task.approval === \"required\") {\n throw new DispatchRefusedError(taskId, \"awaiting_approval\", \"awaiting human approval\");\n }\n if (task.approval === \"denied\") {\n throw new DispatchRefusedError(taskId, \"approval_denied\", \"approval was denied\");\n }\n if (\n !(await stores.graphs.dependenciesSatisfied({\n rootTaskId: task.rootTaskId,\n taskId: task.id,\n }))\n ) {\n throw new DispatchRefusedError(\n taskId,\n \"dependencies_unsatisfied\",\n \"graph dependencies must all be succeeded before dispatch\",\n );\n }\n\n // The reservation gates queued -> running only; any other state falls\n // through to the transition below, which rejects it as invalid.\n let reservation: { reservedUsd: number; reservedAt: string } | undefined;\n if (options.budget !== undefined && task.state === \"queued\") {\n const budgetNow = (options.budget.now ?? (() => new Date()))().toISOString();\n const month = options.budget.month ?? budgetMonthOf(budgetNow);\n // This task's own retained reservation from a prior attempt is excluded,\n // because re-reserving overwrites it. Its settled cost stays counted:\n // that money was actually spent and a retry must not reopen it.\n const rows = (options.budget.tasks ?? (await stores.tasks.list())).map((row) =>\n row.id === taskId ? { ...row, reservedUsd: undefined } : row,\n );\n const spendUsd = computeMonthSpendUsd(rows, month);\n const { totalUsd, perTaskReservationUsd } = options.budget.limits;\n if (!canReserve(spendUsd, options.budget.limits)) {\n const reason = `budget exhausted: ${month} spend $${spendUsd} cannot fit a $${perTaskReservationUsd} reservation under $${totalUsd}`;\n // Fail first, so BudgetExhaustedError's contract (task already failed)\n // holds; losing the task to a concurrent dispatcher surfaces as a\n // refusal, never as a false exhaustion with a misleading receipt.\n try {\n await stores.tasks.transition(taskId, \"failed\", { reason, expectFrom: \"queued\" });\n } catch (error) {\n if (error instanceof InvalidTransitionError || error instanceof VersionConflictError) {\n throw new DispatchRefusedError(\n taskId,\n \"dispatch_race\",\n \"another dispatcher took the task\",\n );\n }\n throw error;\n }\n // Best-effort: the failed transition above already wrote its own\n // receipt carrying this reason, so losing this supplementary one\n // cannot lose the evidence.\n await stores.receipts\n .append({\n repositoryIds: task.repositoryIds,\n taskId,\n kind: task.kind,\n state: \"reservation_refused\",\n phase: \"budget\",\n reason,\n attempt: task.attempt,\n dedupeKey: `task:${taskId}:reservation_refused:${task.attempt}`,\n })\n .catch(() => undefined);\n throw new BudgetExhaustedError(taskId, month, spendUsd);\n }\n reservation = { reservedUsd: perTaskReservationUsd, reservedAt: budgetNow };\n }\n\n const running = await stores.tasks.transition(taskId, \"running\", {\n expectFrom: \"queued\",\n expectAttempt: task.attempt,\n ...(reservation === undefined\n ? {}\n : {\n patch: {\n reservedUsd: reservation.reservedUsd,\n reservedAt: reservation.reservedAt,\n },\n }),\n ...(task.attempt > 1 ? { phase: \"retry\" } : {}),\n });\n if (options.workflowOwned === true) return running;\n let execution: ExecutionReference;\n try {\n execution = await options.launch(running);\n } catch (error) {\n if (error instanceof ExecutionLaunchPendingError) throw error;\n const reason = (error instanceof Error ? error.message : String(error)).slice(0, 500);\n const limit = options.maxLaunchAttempts ?? DEFAULT_MAX_LAUNCH_ATTEMPTS;\n if (running.attempt < limit) {\n const retryReason = `launch failed (attempt ${running.attempt} of ${limit}), will retry: ${reason}`;\n await stores.tasks\n .transition(taskId, \"queued\", {\n reason: retryReason,\n phase: \"retry\",\n expectFrom: \"running\",\n expectAttempt: running.attempt,\n })\n .catch(() => undefined);\n } else {\n await stores.tasks\n .transition(taskId, \"failed\", {\n reason: `launch failed after ${running.attempt} attempts: ${reason}`,\n phase: \"retry\",\n expectFrom: \"running\",\n expectAttempt: running.attempt,\n })\n .catch(() => undefined);\n }\n throw error;\n }\n // Surface a failed binding so a durable caller can recover the same launch;\n // never report acceptance with an unrecorded or stale execution.\n return stores.tasks.recordExecution(taskId, {\n execution,\n expectAttempt: running.attempt,\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ //#region src/errors.d.ts
2
+ /** Returns an error message with supplied secrets and recognized credentials removed. */
3
+ declare function redactedErrorMessage(error: unknown, secrets?: readonly (string | undefined)[], fallback?: string): string;
4
+ /** Bounded, recursively redacted diagnostic details safe for structured reporting. */
5
+ interface RedactedErrorDetails {
6
+ name: string;
7
+ message: string;
8
+ stack?: string;
9
+ cause?: RedactedErrorDetails;
10
+ }
11
+ /** Builds bounded, recursively redacted diagnostic details from an unknown error. */
12
+ declare function redactedErrorDetails(error: unknown, secrets?: readonly (string | undefined)[]): RedactedErrorDetails;
13
+ //#endregion
14
+ export { RedactedErrorDetails, redactedErrorDetails, redactedErrorMessage };
15
+ //# sourceMappingURL=errors.d.mts.map
@@ -0,0 +1,38 @@
1
+ //#region src/errors.ts
2
+ const credentialPatterns = [
3
+ /github_pat_[A-Za-z0-9_]+/gu,
4
+ /gh[psoru]_[A-Za-z0-9_]+/gu,
5
+ /xox[baprs]-[A-Za-z0-9-]+/gu,
6
+ /Basic [A-Za-z0-9+/=]+/gu,
7
+ /Bearer [A-Za-z0-9._~+/-]+=*/giu,
8
+ /vercel_blob_rw_[A-Za-z0-9_]+/gu
9
+ ];
10
+ function errorMessage(error, fallback) {
11
+ return error instanceof Error ? error.message : fallback ?? String(error);
12
+ }
13
+ /** Returns an error message with supplied secrets and recognized credentials removed. */
14
+ function redactedErrorMessage(error, secrets = [], fallback) {
15
+ let message = errorMessage(error, fallback);
16
+ for (const secret of secrets) {
17
+ const value = secret?.trim();
18
+ if (value) message = message.replaceAll(value, "[REDACTED]");
19
+ }
20
+ for (const pattern of credentialPatterns) message = message.replace(pattern, (match) => match.startsWith("Basic ") ? "Basic [REDACTED]" : "[REDACTED]");
21
+ return message;
22
+ }
23
+ /** Builds bounded, recursively redacted diagnostic details from an unknown error. */
24
+ function redactedErrorDetails(error, secrets = []) {
25
+ return nestedRedactedErrorDetails(error, secrets, 0);
26
+ }
27
+ function nestedRedactedErrorDetails(error, secrets, depth) {
28
+ return {
29
+ name: error instanceof Error ? error.name : "UnknownError",
30
+ message: redactedErrorMessage(error, secrets, "Unknown error").slice(0, 2e3),
31
+ ...error instanceof Error && error.stack ? { stack: redactedErrorMessage(error.stack, secrets).slice(0, 8e3) } : {},
32
+ ...error instanceof Error && error.cause !== void 0 && depth < 2 ? { cause: nestedRedactedErrorDetails(error.cause, secrets, depth + 1) } : {}
33
+ };
34
+ }
35
+ //#endregion
36
+ export { redactedErrorDetails, redactedErrorMessage };
37
+
38
+ //# sourceMappingURL=errors.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["const credentialPatterns = [\n /github_pat_[A-Za-z0-9_]+/gu,\n /gh[psoru]_[A-Za-z0-9_]+/gu,\n /xox[baprs]-[A-Za-z0-9-]+/gu,\n /Basic [A-Za-z0-9+/=]+/gu,\n /Bearer [A-Za-z0-9._~+/-]+=*/giu,\n /vercel_blob_rw_[A-Za-z0-9_]+/gu,\n] as const;\n\nfunction errorMessage(error: unknown, fallback?: string): string {\n return error instanceof Error ? error.message : (fallback ?? String(error));\n}\n\n/** Returns an error message with supplied secrets and recognized credentials removed. */\nexport function redactedErrorMessage(\n error: unknown,\n secrets: readonly (string | undefined)[] = [],\n fallback?: string,\n): string {\n let message = errorMessage(error, fallback);\n for (const secret of secrets) {\n const value = secret?.trim();\n if (value) message = message.replaceAll(value, \"[REDACTED]\");\n }\n for (const pattern of credentialPatterns) {\n message = message.replace(pattern, (match) =>\n match.startsWith(\"Basic \") ? \"Basic [REDACTED]\" : \"[REDACTED]\",\n );\n }\n return message;\n}\n\n/** Bounded, recursively redacted diagnostic details safe for structured reporting. */\nexport interface RedactedErrorDetails {\n name: string;\n message: string;\n stack?: string;\n cause?: RedactedErrorDetails;\n}\n\n/** Builds bounded, recursively redacted diagnostic details from an unknown error. */\nexport function redactedErrorDetails(\n error: unknown,\n secrets: readonly (string | undefined)[] = [],\n): RedactedErrorDetails {\n return nestedRedactedErrorDetails(error, secrets, 0);\n}\n\nfunction nestedRedactedErrorDetails(\n error: unknown,\n secrets: readonly (string | undefined)[],\n depth: number,\n): RedactedErrorDetails {\n return {\n name: error instanceof Error ? error.name : \"UnknownError\",\n message: redactedErrorMessage(error, secrets, \"Unknown error\").slice(0, 2000),\n ...(error instanceof Error && error.stack\n ? { stack: redactedErrorMessage(error.stack, secrets).slice(0, 8000) }\n : {}),\n ...(error instanceof Error && error.cause !== undefined && depth < 2\n ? { cause: nestedRedactedErrorDetails(error.cause, secrets, depth + 1) }\n : {}),\n };\n}\n"],"mappings":";AAAA,MAAM,qBAAqB;CACzB;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAS,aAAa,OAAgB,UAA2B;CAC/D,OAAO,iBAAiB,QAAQ,MAAM,UAAW,YAAY,OAAO,KAAK;AAC3E;;AAGA,SAAgB,qBACd,OACA,UAA2C,CAAC,GAC5C,UACQ;CACR,IAAI,UAAU,aAAa,OAAO,QAAQ;CAC1C,KAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,QAAQ,QAAQ,KAAK;EAC3B,IAAI,OAAO,UAAU,QAAQ,WAAW,OAAO,YAAY;CAC7D;CACA,KAAK,MAAM,WAAW,oBACpB,UAAU,QAAQ,QAAQ,UAAU,UAClC,MAAM,WAAW,QAAQ,IAAI,qBAAqB,YACpD;CAEF,OAAO;AACT;;AAWA,SAAgB,qBACd,OACA,UAA2C,CAAC,GACtB;CACtB,OAAO,2BAA2B,OAAO,SAAS,CAAC;AACrD;AAEA,SAAS,2BACP,OACA,SACA,OACsB;CACtB,OAAO;EACL,MAAM,iBAAiB,QAAQ,MAAM,OAAO;EAC5C,SAAS,qBAAqB,OAAO,SAAS,eAAe,CAAC,CAAC,MAAM,GAAG,GAAI;EAC5E,GAAI,iBAAiB,SAAS,MAAM,QAChC,EAAE,OAAO,qBAAqB,MAAM,OAAO,OAAO,CAAC,CAAC,MAAM,GAAG,GAAI,EAAE,IACnE,CAAC;EACL,GAAI,iBAAiB,SAAS,MAAM,UAAU,KAAA,KAAa,QAAQ,IAC/D,EAAE,OAAO,2BAA2B,MAAM,OAAO,SAAS,QAAQ,CAAC,EAAE,IACrE,CAAC;CACP;AACF"}