0sec-cli 0.23.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/0.js +66 -67
  2. package/README.md +37 -11
  3. package/chunks/{adapt-loop-V3JFWOKC.js → adapt-loop-TEJPXWFG.js} +4 -4
  4. package/chunks/{artifact-scraper-NYKUF2M5.js → artifact-scraper-AJZ3LOQY.js} +2 -2
  5. package/chunks/{assumption-mining-ZX3YVWEH.js → assumption-mining-EATDC4BB.js} +18 -18
  6. package/chunks/{backend-connections-H52NIVH2.js → backend-connections-QE4NASAK.js} +3 -3
  7. package/chunks/chunk-3XLEDCFX.js +566 -0
  8. package/chunks/{chunk-I3OCJ2OA.js → chunk-3XLFB46W.js} +5 -3
  9. package/chunks/{chunk-TPUJZ74W.js → chunk-4TUR47H5.js} +2 -2
  10. package/chunks/{chunk-3ZNI7F42.js → chunk-4YRXIVPZ.js} +3 -3
  11. package/chunks/{chunk-A5QO7N3S.js → chunk-52AWIBCT.js} +1 -1
  12. package/chunks/{chunk-TO35Y326.js → chunk-554D4AYM.js} +5 -2
  13. package/chunks/{chunk-KHPOSLCF.js → chunk-5FHFG5P2.js} +174 -710
  14. package/chunks/{chunk-LT7BFGXA.js → chunk-5POT5OYF.js} +10 -10
  15. package/chunks/chunk-62PGWN7Y.js +700 -0
  16. package/chunks/{chunk-EH2WTNY7.js → chunk-A3EKBUEM.js} +1424 -859
  17. package/chunks/{chunk-YYS2H33A.js → chunk-A6AZZ6XX.js} +2 -2
  18. package/chunks/{chunk-A2ADXAC5.js → chunk-AV5BF4V7.js} +1288 -2313
  19. package/chunks/{chunk-NI6QYQSK.js → chunk-BIJPMYFA.js} +6 -6
  20. package/chunks/{chunk-JEYIWMGL.js → chunk-CIMN2AO6.js} +1 -1
  21. package/chunks/{chunk-P7KDN5PK.js → chunk-CSPF47S3.js} +14 -14
  22. package/chunks/{chunk-F344NYUN.js → chunk-DNV3ZMPX.js} +1 -1
  23. package/chunks/{chunk-YAEJAZYW.js → chunk-E42XEJTK.js} +2 -2
  24. package/chunks/{chunk-VDOWARMJ.js → chunk-ESM447R6.js} +15 -1
  25. package/chunks/{chunk-UZUELPFW.js → chunk-EXTN4SQV.js} +1 -1
  26. package/chunks/{chunk-BF5FIK7H.js → chunk-EYBMTELO.js} +108 -24
  27. package/chunks/chunk-FEOPHBMY.js +56 -0
  28. package/chunks/{chunk-XORZUR2H.js → chunk-FKGFHLLS.js} +1 -1
  29. package/chunks/chunk-G2EJ3R5I.js +5198 -0
  30. package/chunks/chunk-GHEBZKUS.js +5417 -0
  31. package/chunks/chunk-GNR3APR6.js +295 -0
  32. package/chunks/{chunk-EJH2TNVH.js → chunk-H76JYUPE.js} +3 -3
  33. package/chunks/{chunk-WIYNYNL2.js → chunk-HRBOZBMC.js} +1 -1
  34. package/chunks/{chunk-62ENS6XO.js → chunk-IIBNQTOK.js} +3 -2
  35. package/chunks/{chunk-UELXHK2Y.js → chunk-IK27KMVV.js} +16 -5
  36. package/chunks/{chunk-O6Z4XPOM.js → chunk-J6G73WYF.js} +1 -1
  37. package/chunks/chunk-JSTLXPJS.js +284 -0
  38. package/chunks/{chunk-K55B4OH4.js → chunk-O3ADFN2A.js} +1 -1
  39. package/chunks/chunk-O7Q2G2KW.js +117 -0
  40. package/chunks/{chunk-HFDSIHTE.js → chunk-QEA2VHBD.js} +202 -30
  41. package/chunks/{chunk-ITES7BCY.js → chunk-RBOMXUYL.js} +3 -111
  42. package/chunks/{chunk-T7ZEVY5G.js → chunk-RCHAFSWS.js} +815 -41
  43. package/chunks/{chunk-W5VNCB57.js → chunk-RD4FKFWR.js} +1 -1
  44. package/chunks/{chunk-LVGZGG63.js → chunk-RPTO5FY2.js} +1 -1
  45. package/chunks/{chunk-LTEE2JWK.js → chunk-RZR2XXAK.js} +251 -8
  46. package/chunks/chunk-SGILXLR3.js +289 -0
  47. package/chunks/{chunk-QMICC5O2.js → chunk-TOAAYJVN.js} +2 -2
  48. package/chunks/{chunk-PIR6BNE7.js → chunk-TOFIGQ63.js} +2 -2
  49. package/chunks/{chunk-V2BWXPZP.js → chunk-TTDFBFNZ.js} +6 -6
  50. package/chunks/{chunk-WYNZFNMF.js → chunk-TZRHWP7R.js} +289 -10
  51. package/chunks/chunk-UHUW45S3.js +164 -0
  52. package/chunks/{chunk-NX4H6L4M.js → chunk-V4BD7GSP.js} +2 -2
  53. package/chunks/{chunk-4HGGNBKK.js → chunk-V5I5GAQH.js} +2593 -1563
  54. package/chunks/{chunk-MQJVU2GZ.js → chunk-W6IYTSZH.js} +79 -9
  55. package/chunks/{chunk-OLSKXTQZ.js → chunk-WUPN3D37.js} +22 -248
  56. package/chunks/{chunk-AAA4PRWK.js → chunk-Y7KV7WV6.js} +2 -2
  57. package/chunks/{chunk-Y4HJRUFE.js → chunk-YERUDEXT.js} +876 -817
  58. package/chunks/{chunk-65H3MRBF.js → chunk-YJU7QACE.js} +1 -1
  59. package/chunks/{chunk-3E54JYS4.js → chunk-YORL5S5V.js} +1 -1
  60. package/chunks/{chunk-CP6HANBE.js → chunk-YYOQADBB.js} +4 -3
  61. package/chunks/{remote-workflow-runtime-RGQ5ECMN.js → chunk-ZPFBQ3SD.js} +39 -8
  62. package/chunks/{codex-models-Y457ND6Q.js → codex-models-FQPCWRPC.js} +7 -7
  63. package/chunks/{commands-PBDAOZKA.js → commands-I5L3VD2A.js} +1603 -6945
  64. package/chunks/dashboard-B2AEGCFM.js +76 -0
  65. package/chunks/{db-TPSI552V.js → db-P5EIKMLP.js} +3 -3
  66. package/chunks/{deep-review-DKED6EN5.js → deep-review-I7RD5QSS.js} +34 -33
  67. package/chunks/{disclose-LDNKAVRH.js → disclose-PFBWESVJ.js} +4 -2
  68. package/chunks/{dist-7Q5A6TQM.js → dist-3SWWY5FZ.js} +18 -2
  69. package/chunks/{dist-4LGRIBOC.js → dist-AJU25UXA.js} +99 -64
  70. package/chunks/{dist-RLPNLRZD.js → dist-VK2M4HNW.js} +33 -1
  71. package/chunks/eval-runner-XVRX3R2Y.js +31 -0
  72. package/chunks/{exploit-agent-RMFYNG7H.js → exploit-agent-A4RTIJ4J.js} +1 -1
  73. package/chunks/{exploit-autoclimb-2A2PQI4E.js → exploit-autoclimb-ABMMBC3M.js} +1 -1
  74. package/chunks/{exploit-climb-DEJ232QN.js → exploit-climb-BQ2SVU63.js} +2 -2
  75. package/chunks/{fix-JGBW7XUN.js → fix-4YCLHFDT.js} +7 -7
  76. package/chunks/{harness-CBOLMUHB.js → harness-DHWULEIZ.js} +3 -3
  77. package/chunks/{hunt-scan-MIRMABY4.js → hunt-scan-DXV5H6YN.js} +16 -16
  78. package/chunks/{kernel-primitive-NAPDMJC7.js → kernel-primitive-SF4NALVR.js} +2 -2
  79. package/chunks/{kernel-vm-runner-PR5OSDQ2.js → kernel-vm-runner-WEBEZEIC.js} +2 -2
  80. package/chunks/local-engine-XUG7XRKM.js +21 -0
  81. package/chunks/managed-operation-SQFMBWHG.js +60 -0
  82. package/chunks/{memsafety-scan-RPS446CD.js → memsafety-scan-L2Z7E7TE.js} +2 -2
  83. package/chunks/{native-loop-CGOJMNHV.js → native-loop-ZNAELJGB.js} +14 -13
  84. package/chunks/orchestrate-YHFJXGID.js +63 -0
  85. package/chunks/{prepare-YVNENPNL.js → prepare-SODWQN6O.js} +5 -5
  86. package/chunks/provider-model-discovery-QGUGL3XW.js +277 -0
  87. package/chunks/remote-workflow-runtime-XNNGI2PG.js +12 -0
  88. package/chunks/{run-VH4OIDKF.js → run-5T4Z3Q2D.js} +902 -322
  89. package/chunks/{runtime-4TAJFVF7.js → runtime-HFFRUOPL.js} +2 -2
  90. package/chunks/{runtime-HWL6HG7A.js → runtime-MB4GEFBY.js} +7 -7
  91. package/chunks/{scan-stream-HJAZJONE.js → scan-stream-GXBJHCDF.js} +3 -2
  92. package/chunks/{scope-Q5PIX2IS.js → scope-M7L3CD6X.js} +2 -2
  93. package/chunks/service-plugins-BGS6QKSZ.js +64 -0
  94. package/chunks/{session-store-2FEIZGSV.js → session-store-K7I4GLNH.js} +2 -2
  95. package/chunks/{variant-candidates-TX5V6XOT.js → variant-candidates-2KSP5EHH.js} +7 -7
  96. package/chunks/{web-recon-prepass-6HOUGF7Q.js → web-recon-prepass-RAXXTEWH.js} +5 -5
  97. package/chunks/workflow-runtime-V26HIJO3.js +178 -0
  98. package/dashboard/assets/{abnfDiagram-VCTEODGH-BuvumFYR.js → abnfDiagram-VCTEODGH-CGCueOp5.js} +1 -1
  99. package/dashboard/assets/{arc-DZzWDuCG.js → arc-BjZaQcCc.js} +1 -1
  100. package/dashboard/assets/architecture-7GRP2DOG-hcbO-9zX.js +1 -0
  101. package/dashboard/assets/{architectureDiagram-5GKGNRK7-Bw5el_EL.js → architectureDiagram-5GKGNRK7-DReOXTSq.js} +1 -1
  102. package/dashboard/assets/{blockDiagram-I7D4REHJ-BjVLEApp.js → blockDiagram-I7D4REHJ-C-byT12_.js} +1 -1
  103. package/dashboard/assets/{c4Diagram-7LVT6UL2-B9nNzx2J.js → c4Diagram-7LVT6UL2-DoYjz-Uv.js} +1 -1
  104. package/dashboard/assets/channel-CUURBnpZ.js +1 -0
  105. package/dashboard/assets/{chunk-4HAMMTFA-y72Q95nW.js → chunk-4HAMMTFA-NBNp2zdt.js} +1 -1
  106. package/dashboard/assets/{chunk-75Z2AOVW-BOhrOGMO.js → chunk-75Z2AOVW-BHmkuP0B.js} +1 -1
  107. package/dashboard/assets/{chunk-DU6HZSFF-B0EhAaNb.js → chunk-DU6HZSFF-DgfLNaL2.js} +2 -2
  108. package/dashboard/assets/{chunk-F27PBJKO-BR-hiDvM.js → chunk-F27PBJKO-3wwNFUgZ.js} +1 -1
  109. package/dashboard/assets/{chunk-GMAD6QVW-H15vJYXg.js → chunk-GMAD6QVW-Btg7huZW.js} +1 -1
  110. package/dashboard/assets/{chunk-GVQU2GXP-BRndps0o.js → chunk-GVQU2GXP-ihCURK5O.js} +1 -1
  111. package/dashboard/assets/{chunk-IMKFNOWR-CIzC_OWx.js → chunk-IMKFNOWR-FsXj7slg.js} +1 -1
  112. package/dashboard/assets/{chunk-L3NEJ4N5-NR4P1Vko.js → chunk-L3NEJ4N5-DorFIxJY.js} +1 -1
  113. package/dashboard/assets/{chunk-OSK3NFVY-DtjTDf8S.js → chunk-OSK3NFVY-DKun_KfC.js} +1 -1
  114. package/dashboard/assets/{chunk-P2QGCYS3-CYT11Qjx.js → chunk-P2QGCYS3-XGgiU1Bz.js} +1 -1
  115. package/dashboard/assets/{chunk-POPQ4Y6H-Duj2Ygwu.js → chunk-POPQ4Y6H-BTXEnGhJ.js} +1 -1
  116. package/dashboard/assets/{chunk-PWAF6VOD-z4p5Tf_y.js → chunk-PWAF6VOD-DYZcGkTL.js} +1 -1
  117. package/dashboard/assets/{chunk-SHT3W25Y-DYeLYLS3.js → chunk-SHT3W25Y-C4WQfwf8.js} +1 -1
  118. package/dashboard/assets/{chunk-SVP7TREG-BKowxCVI.js → chunk-SVP7TREG-C4nCh0-1.js} +1 -1
  119. package/dashboard/assets/{chunk-TICWLB2K-GDAwEFlH.js → chunk-TICWLB2K-DSB-IiGc.js} +1 -1
  120. package/dashboard/assets/{chunk-XXDRQBXY-DRD79Mjx.js → chunk-XXDRQBXY-CS6fjUcj.js} +1 -1
  121. package/dashboard/assets/classDiagram-ZZMXUADV-CD1Buc26.js +1 -0
  122. package/dashboard/assets/classDiagram-v2-VYDZK3BY-CD1Buc26.js +1 -0
  123. package/dashboard/assets/{common-vnzd7UOF.js → common-BMdQBwfR.js} +1 -1
  124. package/dashboard/assets/{cose-bilkent-JH36ORCC-GqCLsRyf.js → cose-bilkent-JH36ORCC-CUjQHTML.js} +1 -1
  125. package/dashboard/assets/{cynefin-OW5HDTMX-CJN9YwWN.js → cynefin-OW5HDTMX-BC7zCtFq.js} +1 -1
  126. package/dashboard/assets/{cynefinDiagram-5FMLGOSQ-DVJHCYQY.js → cynefinDiagram-5FMLGOSQ-CgDOOY67.js} +1 -1
  127. package/dashboard/assets/{dagre-GXQ25YYZ-BMCrrJJR.js → dagre-GXQ25YYZ-Dqc8FS9x.js} +1 -1
  128. package/dashboard/assets/{diagram-S7CK7UJ4-CLYDdOEm.js → diagram-S7CK7UJ4-CrKqRXZb.js} +1 -1
  129. package/dashboard/assets/{diagram-UQ7AKVKN-CnPuAZK7.js → diagram-UQ7AKVKN-Cu6Es0-v.js} +1 -1
  130. package/dashboard/assets/{diagram-VSXAHHWV-Cr2I2DnG.js → diagram-VSXAHHWV-eK4d8FwS.js} +1 -1
  131. package/dashboard/assets/{diagram-VX7I27RA-_mGW1wpp.js → diagram-VX7I27RA-bFxeFKOc.js} +1 -1
  132. package/dashboard/assets/{diagram-Z3DM3KII-igybB0l9.js → diagram-Z3DM3KII-C0ClmnhC.js} +1 -1
  133. package/dashboard/assets/{dist-Bb7_-x5y.js → dist-BZh8Kb3O.js} +1 -1
  134. package/dashboard/assets/{ebnfDiagram-PWID7BFC-CVCICf2R.js → ebnfDiagram-PWID7BFC-CUqxFPzf.js} +1 -1
  135. package/dashboard/assets/{erDiagram-RLTQ6QDP-CRjnfgxj.js → erDiagram-RLTQ6QDP-CcYM7pLi.js} +1 -1
  136. package/dashboard/assets/eventmodeling-NTZA5JFV-D8ZNnPkV.js +1 -0
  137. package/dashboard/assets/flowDiagram-HODETNUW-pDb1AYm8.js +1 -0
  138. package/dashboard/assets/{ganttDiagram-EL5Y4UJY-DppD7sD7.js → ganttDiagram-EL5Y4UJY-UmZfbMyD.js} +1 -1
  139. package/dashboard/assets/{gitGraph-4MIJSDKK-Tw0vNnQc.js → gitGraph-4MIJSDKK-mvNUo0ZA.js} +1 -1
  140. package/dashboard/assets/{gitGraphDiagram-WWUBYQGX-Cpf96-z3.js → gitGraphDiagram-WWUBYQGX-CvzeCxVy.js} +1 -1
  141. package/dashboard/assets/{info-A6RAGUB7-DLyt9arx.js → info-A6RAGUB7-DChKqZMi.js} +1 -1
  142. package/dashboard/assets/{infoDiagram-27XIBGKW-B6_piSNS.js → infoDiagram-27XIBGKW-DjkpNtVW.js} +1 -1
  143. package/dashboard/assets/{ishikawaDiagram-5VMMS53U-DvMO0B0X.js → ishikawaDiagram-5VMMS53U-8FhjJaWT.js} +1 -1
  144. package/dashboard/assets/{journeyDiagram-3NMN7TZE-f7ZK98b6.js → journeyDiagram-3NMN7TZE-DyNJ755f.js} +1 -1
  145. package/dashboard/assets/{kanban-definition-UXKFOSKX-Dso0nAxL.js → kanban-definition-UXKFOSKX-_jCmtaY1.js} +1 -1
  146. package/dashboard/assets/katex-BdBmVSus.js +257 -0
  147. package/dashboard/assets/{line-VcKaFG0G.js → line-Bf0nxyWf.js} +1 -1
  148. package/dashboard/assets/{linear-D7YnpiWh.js → linear-Bl6JKbf4.js} +1 -1
  149. package/dashboard/assets/{mermaid-parser.core-DriL2hrV.js → mermaid-parser.core-Cc6sgCzs.js} +3 -3
  150. package/dashboard/assets/{mermaid.core-DJWYDh_P.js → mermaid.core-B4yCwiN-.js} +4 -4
  151. package/dashboard/assets/{mindmap-definition-YA3MSWOX-DZsoIeac.js → mindmap-definition-YA3MSWOX-D5DjWyXj.js} +1 -1
  152. package/dashboard/assets/operations-B7JAWOi6.js +130 -0
  153. package/dashboard/assets/operations-QjKT0Hwi.css +1 -0
  154. package/dashboard/assets/{packet-AYTQ26CC-BQBIXKxk.js → packet-AYTQ26CC-bQXLDhQC.js} +1 -1
  155. package/dashboard/assets/{pegDiagram-XKGWAZYB-B0-lslgW.js → pegDiagram-XKGWAZYB-Bf6a1vaf.js} +1 -1
  156. package/dashboard/assets/{pie-WAS4IAKB-DoY6dwQs.js → pie-WAS4IAKB-DfzoABIi.js} +1 -1
  157. package/dashboard/assets/{pieDiagram-E7YTZNPT-ElmdRjp5.js → pieDiagram-E7YTZNPT-D0L1qbch.js} +1 -1
  158. package/dashboard/assets/{quadrantDiagram-AXDQQJYC-Ci1h98Q2.js → quadrantDiagram-AXDQQJYC-CbFyy2Pe.js} +1 -1
  159. package/dashboard/assets/{radar-RG4KPBEZ-DHod2WgB.js → radar-RG4KPBEZ-CZ2pyeUT.js} +1 -1
  160. package/dashboard/assets/{railroad-74A4TZTK-90JxB4xV.js → railroad-74A4TZTK-CP4Jazfd.js} +1 -1
  161. package/dashboard/assets/railroad-abnf-HS5TGJTU-3MvEiHwD.js +1 -0
  162. package/dashboard/assets/railroad-ebnf-LZEXJU2U-BWto6cXE.js +1 -0
  163. package/dashboard/assets/railroad-peg-WCYAUIDC-DasEjbyf.js +1 -0
  164. package/dashboard/assets/{railroadDiagram-O6MQD6OU-k3190A6L.js → railroadDiagram-O6MQD6OU-t7Gp9g2R.js} +1 -1
  165. package/dashboard/assets/{requirementDiagram-BXWQKSXE-C3-eR7I5.js → requirementDiagram-BXWQKSXE-LYmqeBXi.js} +1 -1
  166. package/dashboard/assets/{sankeyDiagram-P5KCCOFB-Bc86pi8w.js → sankeyDiagram-P5KCCOFB-DnpfA_94.js} +1 -1
  167. package/dashboard/assets/{sequenceDiagram-WJ2MYXX4-Bv_MvEmz.js → sequenceDiagram-WJ2MYXX4-C8s3pTUE.js} +1 -1
  168. package/dashboard/assets/{src-D7Dqq5yP.js → src-CaNUT1AQ.js} +1 -1
  169. package/dashboard/assets/{stateDiagram-D77RDMKH-Bq_GVmeW.js → stateDiagram-D77RDMKH-BMzm6fK1.js} +1 -1
  170. package/dashboard/assets/stateDiagram-v2-MP3YSRHH-B3REMjpY.js +1 -0
  171. package/dashboard/assets/{swimlanes-42K2YHIH-q0iK-e0P.js → swimlanes-42K2YHIH-uJE05Tt_.js} +1 -1
  172. package/dashboard/assets/swimlanesDiagram-VR7AAH4N-CIaVMZEy.js +8 -0
  173. package/dashboard/assets/{timeline-definition-24CTP7MA-CNfF07hM.js → timeline-definition-24CTP7MA-BfLJMVeG.js} +1 -1
  174. package/dashboard/assets/{treeView-Q6P3EWNA-m629ePnS.js → treeView-Q6P3EWNA-ZOE2enu2.js} +1 -1
  175. package/dashboard/assets/{treemap-WGGIJYW6-D9Cb2ETv.js → treemap-WGGIJYW6-yIZoPBF5.js} +1 -1
  176. package/dashboard/assets/{vennDiagram-4TSXK5OY-v4dKF-FE.js → vennDiagram-4TSXK5OY-BYJRHEaL.js} +1 -1
  177. package/dashboard/assets/{wardley-WFR3VGLG-DkwH_nRe.js → wardley-WFR3VGLG-D5V-jLbB.js} +1 -1
  178. package/dashboard/assets/{wardleyDiagram-VM6X3IG4-3ImohVu4.js → wardleyDiagram-VM6X3IG4-DgPN4nSe.js} +1 -1
  179. package/dashboard/assets/{xychartDiagram-S5SC5T6Z-QJu3dm7K.js → xychartDiagram-S5SC5T6Z-BhkAMAOT.js} +1 -1
  180. package/dashboard/index.html +3 -3
  181. package/package.json +3 -2
  182. package/worker-assets/demo-target/README.md +15 -0
  183. package/worker-assets/demo-target/include/decoder.h +27 -0
  184. package/worker-assets/demo-target/patched-decoder.c +40 -0
  185. package/worker-assets/demo-target/src/decoder.c +50 -0
  186. package/worker-assets/demo-target/witness-65537x1.bin +0 -0
  187. package/chunks/chunk-3P3XXSIJ.js +0 -463
  188. package/chunks/chunk-EWXOG6D3.js +0 -1695
  189. package/chunks/chunk-FXR3BT2G.js +0 -226
  190. package/chunks/chunk-R35KKJA4.js +0 -90
  191. package/chunks/eval-runner-RZKDOQ2G.js +0 -30
  192. package/chunks/managed-operation-YNQQEA5V.js +0 -59
  193. package/chunks/orchestrate-5NK34H3W.js +0 -62
  194. package/chunks/service-plugins-QYFITODK.js +0 -63
  195. package/chunks/workflow-runtime-ZEEBJFQG.js +0 -64
  196. package/dashboard/assets/architecture-7GRP2DOG-DK9bv1Md.js +0 -1
  197. package/dashboard/assets/channel-Ck17eKdX.js +0 -1
  198. package/dashboard/assets/classDiagram-ZZMXUADV-BaeZA6LE.js +0 -1
  199. package/dashboard/assets/classDiagram-v2-VYDZK3BY-BaeZA6LE.js +0 -1
  200. package/dashboard/assets/eventmodeling-NTZA5JFV-po5oDydv.js +0 -1
  201. package/dashboard/assets/flowDiagram-HODETNUW-TSV9s7q4.js +0 -1
  202. package/dashboard/assets/katex-CXMH3UgJ.js +0 -257
  203. package/dashboard/assets/operations-BIaUSnYv.js +0 -121
  204. package/dashboard/assets/operations-BeCdkT4S.css +0 -1
  205. package/dashboard/assets/railroad-abnf-HS5TGJTU-BkXGl114.js +0 -1
  206. package/dashboard/assets/railroad-ebnf-LZEXJU2U-QVAERn2C.js +0 -1
  207. package/dashboard/assets/railroad-peg-WCYAUIDC-B1-PM01z.js +0 -1
  208. package/dashboard/assets/stateDiagram-v2-MP3YSRHH-z1XRaftz.js +0 -1
  209. package/dashboard/assets/swimlanesDiagram-VR7AAH4N-C9L6XBQj.js +0 -8
  210. package/dashboard/assets/zero-connect-BGDY0PMZ.webp +0 -0
  211. package/dashboard/assets/zero-curious-DovTLOs8.webp +0 -0
  212. package/dashboard/assets/zero-peek-Dzm3MT3X.png +0 -0
  213. package/dashboard/assets/zero-planning-CBeHDrG_.webp +0 -0
@@ -2,10 +2,13 @@
2
2
  import { createRequire as __0CreateRequire } from "node:module";
3
3
  const require = __0CreateRequire(import.meta.url);
4
4
  import {
5
+ ImpactAssessmentSchema,
6
+ compareFindingsByBusinessPriority,
7
+ getFindingPriority,
5
8
  homeStateDir,
6
9
  parseSecurityWorkflowInput,
7
10
  runStateDir
8
- } from "./chunk-LTEE2JWK.js";
11
+ } from "./chunk-RZR2XXAK.js";
9
12
  import {
10
13
  __export
11
14
  } from "./chunk-VCSFCHAJ.js";
@@ -153,24 +156,55 @@ function createWasmEngine(path) {
153
156
  const sqliteWasm = createRequire(import.meta.url)("node-sqlite3-wasm");
154
157
  const { Database: WasmDatabaseCtor } = sqliteWasm;
155
158
  const wasm = new WasmDatabaseCtor(path);
156
- const wrapStatement = (stmt) => ({
157
- run: (bind) => stmt.run(bind),
158
- get: (bind) => {
159
- const row = stmt.get(bind);
160
- return row == null ? void 0 : row;
161
- },
162
- all: (bind) => stmt.all(bind),
163
- values: (bind) => {
164
- const rows = stmt.all(bind);
165
- return rows.map((r) => Object.values(r));
166
- }
167
- });
159
+ const prepared = /* @__PURE__ */ new Set();
160
+ const wrapStatement = (sql) => {
161
+ let initial = wasm.prepare(sql);
162
+ prepared.add(initial);
163
+ const execute = (operation) => {
164
+ if (!wasm.isOpen)
165
+ throw new Error("Database already closed");
166
+ const statement = initial ?? wasm.prepare(sql);
167
+ initial = void 0;
168
+ prepared.add(statement);
169
+ let succeeded = false;
170
+ try {
171
+ const result = operation(statement);
172
+ succeeded = true;
173
+ return result;
174
+ } finally {
175
+ prepared.delete(statement);
176
+ try {
177
+ statement.finalize();
178
+ } catch (error) {
179
+ if (succeeded)
180
+ throw error;
181
+ }
182
+ }
183
+ };
184
+ return {
185
+ run: (bind) => execute((statement) => statement.run(bind)),
186
+ get: (bind) => execute((statement) => {
187
+ const row = statement.get(bind);
188
+ return row == null ? void 0 : row;
189
+ }),
190
+ all: (bind) => execute((statement) => statement.all(bind)),
191
+ values: (bind) => execute((statement) => {
192
+ const rows = statement.all(bind);
193
+ return rows.map((row) => Object.values(row));
194
+ })
195
+ };
196
+ };
168
197
  return {
169
- prepare: (sql) => wrapStatement(wasm.prepare(sql)),
198
+ prepare: (sql) => wrapStatement(sql),
170
199
  exec: (sql) => {
171
200
  wasm.exec(sql);
172
201
  },
173
- close: () => wasm.close(),
202
+ close: () => {
203
+ for (const statement of prepared)
204
+ statement.finalize();
205
+ prepared.clear();
206
+ wasm.close();
207
+ },
174
208
  get inTransaction() {
175
209
  return wasm.inTransaction;
176
210
  },
@@ -625,6 +659,144 @@ var trustGraphEdges = sqliteTable("trust_graph_edges", {
625
659
  index("idx_tge_relation").on(table.relation)
626
660
  ]);
627
661
 
662
+ // packages/db/dist/finding-priority.js
663
+ function* iterateLatestFindingMetadata(sqlite, opts = {}) {
664
+ let afterId = null;
665
+ while (true) {
666
+ const page = sqlite.prepare(`SELECT f.id,COALESCE(f.fingerprint,f.id) AS familyKey,f.timestamp,f.severity,f.category,f.status,f.triageStatus,f.cvssScore,
667
+ CASE WHEN length(CAST(f.impactAssessment AS BLOB)) <= ${MAX_ASSESSMENT_BYTES} THEN f.impactAssessment ELSE NULL END AS impactAssessment
668
+ FROM findings f WHERE (@afterId IS NULL OR f.id>@afterId) AND (@scanId IS NULL OR f.scanId=@scanId)
669
+ AND NOT EXISTS (SELECT 1 FROM findings newer WHERE COALESCE(newer.fingerprint,newer.id)=COALESCE(f.fingerprint,f.id)
670
+ AND (@scanId IS NULL OR newer.scanId=@scanId) AND (newer.timestamp>f.timestamp OR (newer.timestamp=f.timestamp AND newer.id>f.id)))
671
+ AND (@severity IS NULL OR f.severity=@severity) AND (@category IS NULL OR f.category=@category)
672
+ AND (@status IS NULL OR f.status=@status) AND (@triageStatus IS NULL OR f.triageStatus=@triageStatus)
673
+ ORDER BY f.id LIMIT ${PAGE_SIZE}`).all({
674
+ afterId,
675
+ scanId: opts.scanId ?? null,
676
+ severity: opts.severity ?? null,
677
+ category: opts.category ?? null,
678
+ status: opts.status ?? null,
679
+ triageStatus: opts.triageStatus ?? null
680
+ });
681
+ yield* page;
682
+ if (page.length < PAGE_SIZE)
683
+ return;
684
+ afterId = page[page.length - 1].id;
685
+ }
686
+ }
687
+ function getFindingFamilyMetadata(sqlite, key, opts = {}) {
688
+ const rows = sqlite.prepare(`SELECT scanId,COUNT(*) AS count FROM findings WHERE COALESCE(fingerprint,id)=@key
689
+ AND (@scanId IS NULL OR scanId=@scanId) GROUP BY scanId ORDER BY scanId`).all({ key, scanId: opts.scanId ?? null });
690
+ return { count: rows.reduce((sum, row) => sum + row.count, 0), scanIds: rows.map((row) => row.scanId) };
691
+ }
692
+ function compare(a, b) {
693
+ return b.businessPriorityRank - a.businessPriorityRank || compareFindingsByBusinessPriority(a, b) || b.timestamp - a.timestamp || b.id.localeCompare(a.id);
694
+ }
695
+ function retain(heap, item, limit2) {
696
+ if (heap.length < limit2) {
697
+ heap.push(item);
698
+ let index3 = heap.length - 1;
699
+ while (index3 > 0) {
700
+ const parent = Math.floor((index3 - 1) / 2);
701
+ if (compare(heap[index3], heap[parent]) <= 0)
702
+ break;
703
+ [heap[index3], heap[parent]] = [heap[parent], heap[index3]];
704
+ index3 = parent;
705
+ }
706
+ return;
707
+ }
708
+ if (compare(item, heap[0]) >= 0)
709
+ return;
710
+ heap[0] = item;
711
+ let index2 = 0;
712
+ while (true) {
713
+ let worst = index2;
714
+ for (const child of [index2 * 2 + 1, index2 * 2 + 2])
715
+ if (child < heap.length && compare(heap[child], heap[worst]) > 0)
716
+ worst = child;
717
+ if (worst === index2)
718
+ break;
719
+ [heap[index2], heap[worst]] = [heap[worst], heap[index2]];
720
+ index2 = worst;
721
+ }
722
+ }
723
+ var PAGE_SIZE = 500;
724
+ var MAX_ASSESSMENT_BYTES = 80 * 1024;
725
+ function rankedFindingRows(sqlite, opts, latestOnly) {
726
+ const limit2 = opts.limit ?? 100;
727
+ if (!Number.isSafeInteger(limit2) || limit2 < 0 || limit2 > 5e3)
728
+ throw new Error("Business-priority finding limit must be an integer between 0 and 5000.");
729
+ if (limit2 === 0)
730
+ return [];
731
+ const heap = [];
732
+ let afterId = null;
733
+ const statement = sqlite.prepare(`
734
+ SELECT f.id, COALESCE(f.fingerprint, f.id) AS familyKey, f.timestamp, f.severity, f.cvssScore,
735
+ CASE WHEN length(CAST(f.impactAssessment AS BLOB)) <= ${MAX_ASSESSMENT_BYTES} THEN f.impactAssessment ELSE NULL END AS impactAssessment
736
+ FROM findings f
737
+ WHERE (@afterId IS NULL OR f.id > @afterId)
738
+ AND (@scanId IS NULL OR f.scanId = @scanId)
739
+ AND (@latestOnly = 0 OR NOT EXISTS (SELECT 1 FROM findings newer
740
+ WHERE COALESCE(newer.fingerprint, newer.id) = COALESCE(f.fingerprint, f.id)
741
+ AND (@scanId IS NULL OR newer.scanId = @scanId)
742
+ AND (newer.timestamp > f.timestamp OR (newer.timestamp = f.timestamp AND newer.id > f.id))))
743
+ AND (@severity IS NULL OR f.severity = @severity)
744
+ AND (@category IS NULL OR f.category = @category)
745
+ AND (@status IS NULL OR f.status = @status)
746
+ AND (@triageStatus IS NULL OR f.triageStatus = @triageStatus)
747
+ ORDER BY f.id LIMIT ${PAGE_SIZE}`);
748
+ while (true) {
749
+ const page = statement.all({ latestOnly: latestOnly ? 1 : 0, afterId, scanId: opts.scanId ?? null, severity: opts.severity ?? null, category: opts.category ?? null, status: opts.status ?? null, triageStatus: opts.triageStatus ?? null });
750
+ for (const row of page) {
751
+ let impactAssessment;
752
+ try {
753
+ impactAssessment = row.impactAssessment ? JSON.parse(row.impactAssessment) : void 0;
754
+ } catch {
755
+ }
756
+ const { impactAssessment: _serialized, ...metadata } = row;
757
+ retain(heap, { ...metadata, businessPriorityRank: getFindingPriority({ impactAssessment }).rank }, limit2);
758
+ }
759
+ if (page.length < PAGE_SIZE)
760
+ break;
761
+ afterId = page[page.length - 1].id;
762
+ }
763
+ const selected = heap.sort(compare);
764
+ const rows = /* @__PURE__ */ new Map();
765
+ for (let offset = 0; offset < selected.length; offset += 300) {
766
+ const ids = selected.slice(offset, offset + 300).map((item) => item.id);
767
+ for (const row of sqlite.prepare(`SELECT * FROM findings WHERE id IN (${ids.map(() => "?").join(",")})`).all(...ids))
768
+ rows.set(row.id, row);
769
+ }
770
+ return selected.map((item) => rows.get(item.id));
771
+ }
772
+ function selectFindingsByBusinessPriority(sqlite, opts = {}) {
773
+ return sqlite.transaction(() => rankedFindingRows(sqlite, opts, false))();
774
+ }
775
+ function selectFindingFamiliesByBusinessPriority(sqlite, opts = {}) {
776
+ return sqlite.transaction(() => {
777
+ return rankedFindingRows(sqlite, opts, true).map((item) => {
778
+ const params = { key: item.fingerprint ?? item.id, scanId: opts.scanId ?? null };
779
+ const counts = sqlite.prepare(`SELECT COUNT(*) AS count, COUNT(DISTINCT scanId) AS scanCount FROM findings
780
+ WHERE COALESCE(fingerprint,id)=@key AND (@scanId IS NULL OR scanId=@scanId)`).get(params);
781
+ const verdictCounts = sqlite.prepare(`SELECT COUNT(*) AS total,
782
+ COALESCE(SUM(CASE WHEN v.verdict='TRUE_POSITIVE' THEN 1 ELSE 0 END),0) AS truePositive,
783
+ COALESCE(SUM(CASE WHEN v.verdict='FALSE_POSITIVE' THEN 1 ELSE 0 END),0) AS falsePositive,
784
+ COALESCE(SUM(CASE WHEN v.verdict NOT IN ('TRUE_POSITIVE','FALSE_POSITIVE') THEN 1 ELSE 0 END),0) AS unsure
785
+ FROM verdicts v JOIN findings f ON f.id=v.findingId
786
+ WHERE COALESCE(f.fingerprint,f.id)=@key AND (@scanId IS NULL OR f.scanId=@scanId)`).get(params);
787
+ const activeSessions = sqlite.prepare(`SELECT s.id,s.scanId,s.status,s.agentRole,s.createdAt,s.updatedAt FROM agent_sessions s
788
+ WHERE s.status='running' AND EXISTS (SELECT 1 FROM findings f WHERE f.scanId=s.scanId
789
+ AND COALESCE(f.fingerprint,f.id)=@key AND (@scanId IS NULL OR f.scanId=@scanId))
790
+ AND NOT EXISTS (SELECT 1 FROM agent_sessions newer WHERE newer.status='running' AND newer.agentRole=s.agentRole
791
+ AND (newer.updatedAt>s.updatedAt OR (newer.updatedAt=s.updatedAt AND newer.id>s.id))
792
+ AND EXISTS (SELECT 1 FROM findings f WHERE f.scanId=newer.scanId
793
+ AND COALESCE(f.fingerprint,f.id)=@key AND (@scanId IS NULL OR f.scanId=@scanId)))
794
+ ORDER BY s.agentRole`).all(params);
795
+ return { key: item.fingerprint ?? item.id, latest: item, ...counts, verdictCounts, activeSessions };
796
+ });
797
+ })();
798
+ }
799
+
628
800
  // packages/db/dist/database.js
629
801
  var DEFAULT_DB_DIR = homeStateDir();
630
802
  var DEFAULT_DB_PATH = join(DEFAULT_DB_DIR, "0.db");
@@ -763,10 +935,13 @@ function clearStaleLockIfAny(path) {
763
935
  }
764
936
  }
765
937
  var osecDB = class {
938
+ /** Resolved storage identity for scoped stores sharing this database. */
939
+ databasePath;
766
940
  sqlite;
767
941
  db;
768
942
  constructor(dbPath, options = {}) {
769
943
  const path = resolveOsecDbPath(dbPath);
944
+ this.databasePath = path;
770
945
  if (options.readOnly) {
771
946
  if (path !== ":memory:" && !existsSync2(path)) {
772
947
  throw new Error(`Database does not exist: ${path}`);
@@ -935,13 +1110,13 @@ var osecDB = class {
935
1110
  params.category = opts.category;
936
1111
  }
937
1112
  const whereSql = where.length > 0 ? `WHERE ${where.join(" AND ")}` : "";
938
- const limit = opts?.limit ?? 500;
1113
+ const limit2 = opts?.limit ?? 500;
939
1114
  const rows = this.sqlite.prepare(`SELECT id, scope, scope_value as scopeValue, category, pattern, reasoning,
940
1115
  created_at as createdAt, applied_count as appliedCount
941
1116
  FROM triage_memories
942
1117
  ${whereSql}
943
1118
  ORDER BY applied_count DESC, created_at DESC
944
- LIMIT ${limit}`).all(params);
1119
+ LIMIT ${limit2}`).all(params);
945
1120
  return rows;
946
1121
  }
947
1122
  deleteTriageMemory(id) {
@@ -1320,8 +1495,8 @@ var osecDB = class {
1320
1495
  getScan(scanId) {
1321
1496
  return this.db.select().from(scans).where(eq(scans.id, scanId)).get();
1322
1497
  }
1323
- listScans(limit = 20) {
1324
- return this.db.select().from(scans).orderBy(desc(scans.startedAt)).limit(limit).all();
1498
+ listScans(limit2 = 20) {
1499
+ return this.db.select().from(scans).orderBy(desc(scans.startedAt)).limit(limit2).all();
1325
1500
  }
1326
1501
  listScansByTarget(target, opts) {
1327
1502
  const query = this.db.select().from(scans).where(eq(scans.target, target)).orderBy(desc(scans.startedAt));
@@ -1475,6 +1650,24 @@ var osecDB = class {
1475
1650
  getLatestFindingForScan(scanId) {
1476
1651
  return this.db.select().from(findings).where(eq(findings.scanId, scanId)).orderBy(desc(findings.timestamp)).get();
1477
1652
  }
1653
+ /** Stable bounded keyset projection for read-only external findings APIs. */
1654
+ listFindingsApiPage(options) {
1655
+ if (!Number.isSafeInteger(options.limit) || options.limit < 1 || options.limit > 101)
1656
+ throw new Error("Finding API page limit must be between 1 and 101.");
1657
+ return this.sqlite.prepare(`SELECT id, scanId, timestamp FROM findings
1658
+ WHERE (@scanId IS NULL OR scanId = @scanId) AND (@severity IS NULL OR severity = @severity)
1659
+ AND (@status IS NULL OR status = @status) AND (@includeSuppressed = 1 OR COALESCE(triageStatus, 'new') != 'suppressed')
1660
+ AND (@beforeTimestamp IS NULL OR timestamp < @beforeTimestamp OR (timestamp = @beforeTimestamp AND id < @beforeId))
1661
+ ORDER BY timestamp DESC, id DESC LIMIT @limit`).all({
1662
+ limit: options.limit,
1663
+ scanId: options.scanId ?? null,
1664
+ severity: options.severity ?? null,
1665
+ status: options.status ?? null,
1666
+ includeSuppressed: options.includeSuppressed ? 1 : 0,
1667
+ beforeTimestamp: options.before?.timestamp ?? null,
1668
+ beforeId: options.before?.id ?? null
1669
+ });
1670
+ }
1478
1671
  listFindings(opts) {
1479
1672
  const conditions = [];
1480
1673
  if (opts?.scanId)
@@ -1493,6 +1686,21 @@ var osecDB = class {
1493
1686
  }
1494
1687
  return query.all();
1495
1688
  }
1689
+ /** Globally rank current family assessments before the body/row limit. Historical review counts remain intact. */
1690
+ listFindingFamiliesByBusinessPriority(opts) {
1691
+ return selectFindingFamiliesByBusinessPriority(this.sqlite, opts);
1692
+ }
1693
+ /** Bounded row-level native --all list; historical rows are independently ranked. */
1694
+ listFindingsByBusinessPriority(opts) {
1695
+ return selectFindingsByBusinessPriority(this.sqlite, opts);
1696
+ }
1697
+ /** Uncapped paged metadata for exact cross-database latest-family merging; no evidence bodies. */
1698
+ iterateLatestFindingMetadata(opts) {
1699
+ return iterateLatestFindingMetadata(this.sqlite, opts);
1700
+ }
1701
+ getFindingFamilyMetadata(key, opts) {
1702
+ return getFindingFamilyMetadata(this.sqlite, key, opts);
1703
+ }
1496
1704
  /** Alias for listFindings — backward compat with core agent tools */
1497
1705
  queryFindings(opts) {
1498
1706
  return this.listFindings(opts);
@@ -1503,6 +1711,12 @@ var osecDB = class {
1503
1711
  if (finding?.fingerprint)
1504
1712
  this.syncFindingGraph(finding.scanId, finding.fingerprint);
1505
1713
  }
1714
+ /** Operator business context is metadata; never changes verification or execution authority. */
1715
+ updateFindingImpactAssessment(findingId, assessment) {
1716
+ const parsed = assessment === null ? null : ImpactAssessmentSchema.parse(assessment);
1717
+ const result = this.db.update(findings).set({ impactAssessment: parsed === null ? null : JSON.stringify(parsed) }).where(eq(findings.id, findingId)).run();
1718
+ return result.changes > 0;
1719
+ }
1506
1720
  saveFindingPocExecution(findingId, execution) {
1507
1721
  const serialized = JSON.stringify(execution);
1508
1722
  this.db.update(findings).set({ pocExecution: serialized }).where(eq(findings.id, findingId)).run();
@@ -1687,7 +1901,7 @@ var osecDB = class {
1687
1901
  conditions.push(eq(pipelineEvents.eventType, opts.eventType));
1688
1902
  return this.db.select().from(pipelineEvents).where(and(...conditions)).orderBy(pipelineEvents.timestamp).all();
1689
1903
  }
1690
- listRecentEvents(limit = 50) {
1904
+ listRecentEvents(limit2 = 50) {
1691
1905
  return this.db.select({
1692
1906
  id: pipelineEvents.id,
1693
1907
  scanId: pipelineEvents.scanId,
@@ -1700,13 +1914,13 @@ var osecDB = class {
1700
1914
  source: pipelineEvents.source,
1701
1915
  payload: pipelineEvents.payload,
1702
1916
  timestamp: pipelineEvents.timestamp
1703
- }).from(pipelineEvents).innerJoin(scans, eq(pipelineEvents.scanId, scans.id)).leftJoin(findings, eq(pipelineEvents.findingId, findings.id)).orderBy(desc(pipelineEvents.timestamp)).limit(limit).all();
1917
+ }).from(pipelineEvents).innerJoin(scans, eq(pipelineEvents.scanId, scans.id)).leftJoin(findings, eq(pipelineEvents.findingId, findings.id)).orderBy(desc(pipelineEvents.timestamp)).limit(limit2).all();
1704
1918
  }
1705
1919
  /**
1706
1920
  * Read persisted events strictly after a durable `(timestamp, id)` cursor.
1707
1921
  * The UUID tie-breaker makes same-millisecond multi-process writes stable.
1708
1922
  */
1709
- listEventsAfter(cursor, limit = 250) {
1923
+ listEventsAfter(cursor, limit2 = 250) {
1710
1924
  const selectEvents = () => this.db.select({
1711
1925
  id: pipelineEvents.id,
1712
1926
  scanId: pipelineEvents.scanId,
@@ -1721,9 +1935,9 @@ var osecDB = class {
1721
1935
  timestamp: pipelineEvents.timestamp
1722
1936
  }).from(pipelineEvents).innerJoin(scans, eq(pipelineEvents.scanId, scans.id)).leftJoin(findings, eq(pipelineEvents.findingId, findings.id));
1723
1937
  if (!cursor) {
1724
- return selectEvents().orderBy(asc(pipelineEvents.timestamp), asc(pipelineEvents.id)).limit(limit).all();
1938
+ return selectEvents().orderBy(asc(pipelineEvents.timestamp), asc(pipelineEvents.id)).limit(limit2).all();
1725
1939
  }
1726
- return selectEvents().where(or(gt(pipelineEvents.timestamp, cursor.timestamp), and(eq(pipelineEvents.timestamp, cursor.timestamp), gt(pipelineEvents.id, cursor.id)))).orderBy(asc(pipelineEvents.timestamp), asc(pipelineEvents.id)).limit(limit).all();
1940
+ return selectEvents().where(or(gt(pipelineEvents.timestamp, cursor.timestamp), and(eq(pipelineEvents.timestamp, cursor.timestamp), gt(pipelineEvents.id, cursor.id)))).orderBy(asc(pipelineEvents.timestamp), asc(pipelineEvents.id)).limit(limit2).all();
1727
1941
  }
1728
1942
  // ── Agent Sessions (resumable state) ──
1729
1943
  saveSession(session) {
@@ -1795,8 +2009,8 @@ var osecDB = class {
1795
2009
  getCase(caseId) {
1796
2010
  return this.db.select().from(cases).where(eq(cases.id, caseId)).get();
1797
2011
  }
1798
- listCases(limit = 100) {
1799
- return this.db.select().from(cases).orderBy(desc(cases.updatedAt)).limit(limit).all();
2012
+ listCases(limit2 = 100) {
2013
+ return this.db.select().from(cases).orderBy(desc(cases.updatedAt)).limit(limit2).all();
1800
2014
  }
1801
2015
  upsertWorkItem(record) {
1802
2016
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -1932,8 +2146,8 @@ var osecDB = class {
1932
2146
  }
1933
2147
  }).run();
1934
2148
  }
1935
- listWorkers(limit = 50) {
1936
- return this.db.select().from(workers).orderBy(desc(workers.heartbeatAt)).limit(limit).all();
2149
+ listWorkers(limit2 = 50) {
2150
+ return this.db.select().from(workers).orderBy(desc(workers.heartbeatAt)).limit(limit2).all();
1937
2151
  }
1938
2152
  stopWorkersByLabel(label, exceptId) {
1939
2153
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -2035,8 +2249,8 @@ var osecDB = class {
2035
2249
  params.push(query.target);
2036
2250
  }
2037
2251
  const whereSql = where.length > 0 ? `WHERE ${where.join(" AND ")}` : "";
2038
- const limit = query.limit ?? 500;
2039
- return this.sqlite.prepare(`SELECT * FROM persistent_credentials ${whereSql} ORDER BY lastSeenAt DESC LIMIT ${limit}`).all(...params);
2252
+ const limit2 = query.limit ?? 500;
2253
+ return this.sqlite.prepare(`SELECT * FROM persistent_credentials ${whereSql} ORDER BY lastSeenAt DESC LIMIT ${limit2}`).all(...params);
2040
2254
  }
2041
2255
  // ── Trust graph edges (0#771) ──
2042
2256
  /**
@@ -2117,8 +2331,8 @@ var osecDB = class {
2117
2331
  params.push(query.relation);
2118
2332
  }
2119
2333
  const whereSql = where.length > 0 ? `WHERE ${where.join(" AND ")}` : "";
2120
- const limit = query.limit ?? 500;
2121
- return this.sqlite.prepare(`SELECT * FROM trust_graph_edges ${whereSql} ORDER BY updatedAt DESC LIMIT ${limit}`).all(...params);
2334
+ const limit2 = query.limit ?? 500;
2335
+ return this.sqlite.prepare(`SELECT * FROM trust_graph_edges ${whereSql} ORDER BY updatedAt DESC LIMIT ${limit2}`).all(...params);
2122
2336
  }
2123
2337
  // ── Utilities ──
2124
2338
  close() {
@@ -2485,6 +2699,7 @@ CREATE INDEX IF NOT EXISTS idx_findings_severity ON findings(severity);
2485
2699
  CREATE INDEX IF NOT EXISTS idx_findings_category ON findings(category);
2486
2700
  CREATE INDEX IF NOT EXISTS idx_findings_status ON findings(status);
2487
2701
  CREATE INDEX IF NOT EXISTS idx_findings_fingerprint ON findings(fingerprint);
2702
+ CREATE INDEX IF NOT EXISTS idx_findings_priority_latest ON findings(COALESCE(fingerprint,id),timestamp DESC,id DESC);
2488
2703
  CREATE INDEX IF NOT EXISTS idx_findings_triageStatus ON findings(triageStatus);
2489
2704
  CREATE INDEX IF NOT EXISTS idx_findings_workflowStatus ON findings(workflowStatus);
2490
2705
  CREATE INDEX IF NOT EXISTS idx_attack_results_scanId ON attack_results(scanId);
@@ -2616,7 +2831,447 @@ function writeOsecRunReport(storage, report) {
2616
2831
  }
2617
2832
 
2618
2833
  // packages/db/dist/security-workflows.js
2619
- import { randomUUID as randomUUID3 } from "node:crypto";
2834
+ import { createHash as createHash3, randomUUID as randomUUID4 } from "node:crypto";
2835
+
2836
+ // packages/db/dist/learning.js
2837
+ import { randomUUID as randomUUID3, createHash as createHash2 } from "node:crypto";
2838
+ import { realpathSync } from "node:fs";
2839
+ import { isAbsolute as isAbsolute2, resolve as resolve3 } from "node:path";
2840
+ import { homedir } from "node:os";
2841
+ var LearningStoreError = class extends Error {
2842
+ statusCode;
2843
+ constructor(message, statusCode = 400) {
2844
+ super(message);
2845
+ this.statusCode = statusCode;
2846
+ this.name = "LearningStoreError";
2847
+ }
2848
+ };
2849
+ function learningProjectId(target) {
2850
+ let identity = target.trim();
2851
+ const sourceTarget = identity.startsWith("source:");
2852
+ const path = sourceTarget ? identity.slice("source:".length).trim() : identity;
2853
+ const remote = /^[a-z][a-z0-9+.-]*:\/\//i.test(path);
2854
+ if (!remote && path && (sourceTarget || isAbsolute2(path) || /^\.{1,2}(?:\/|$)/.test(path) || path.startsWith("~/"))) {
2855
+ identity = resolve3(path.startsWith("~/") ? resolve3(homedir(), path.slice(2)) : path);
2856
+ try {
2857
+ identity = realpathSync(identity);
2858
+ } catch {
2859
+ }
2860
+ }
2861
+ return `sha256:${createHash2("sha256").update(identity).digest("hex")}`;
2862
+ }
2863
+ function learningArtifactDigest(proposal) {
2864
+ return `sha256:${createHash2("sha256").update(proposal).digest("hex")}`;
2865
+ }
2866
+ function fail(message, status = 400) {
2867
+ throw new LearningStoreError(message, status);
2868
+ }
2869
+ function bounded(value, name, max = 256) {
2870
+ if (typeof value !== "string" || !value.trim() || value.length > max || /\0/.test(value))
2871
+ fail(`Invalid ${name}.`);
2872
+ if (/-----BEGIN .*PRIVATE KEY|(?:api[_-]?key|authorization|password|access[_-]?token)\s*[:=]\s*\S+|\b(?:sk-|ghp_)[A-Za-z0-9_-]{16,}/i.test(value))
2873
+ fail(`Credential-like content in ${name}.`);
2874
+ }
2875
+ function refs(values, name) {
2876
+ if (!Array.isArray(values) || values.length > 64)
2877
+ fail(`Invalid ${name}.`);
2878
+ for (const value of values)
2879
+ bounded(value, name, 1024);
2880
+ }
2881
+ function sources(value) {
2882
+ if (!Array.isArray(value) || value.length > 64)
2883
+ fail("Invalid source links.");
2884
+ for (const item of value) {
2885
+ if (!item || typeof item !== "object" || Object.keys(item).some((key) => !["path", "hash"].includes(key)))
2886
+ fail("Invalid source link.");
2887
+ bounded(item.path, "source path", 4096);
2888
+ bounded(item.hash, "source hash");
2889
+ }
2890
+ }
2891
+ function limit(value) {
2892
+ return Number.isInteger(value) && value > 0 ? Math.min(value, 200) : 100;
2893
+ }
2894
+ function read(db, table, id) {
2895
+ const row = db.prepare(`SELECT body FROM ${table} WHERE id = ?`).all(id)[0];
2896
+ return row ? JSON.parse(row.body) : null;
2897
+ }
2898
+ function write(db, table, value) {
2899
+ db.prepare(`INSERT INTO ${table}(id,project_id,body,updated_at) VALUES(?,?,?,?) ON CONFLICT(id) DO UPDATE SET body=excluded.body,updated_at=excluded.updated_at`).run(value.id, value.projectId, JSON.stringify(value), value.updatedAt ?? value.createdAt);
2900
+ }
2901
+ function initializeLearningTables(db) {
2902
+ db.exec(`CREATE TABLE IF NOT EXISTS learning_events(id TEXT PRIMARY KEY,idempotency_key TEXT NOT NULL UNIQUE,project_id TEXT NOT NULL,body TEXT NOT NULL,updated_at TEXT NOT NULL);
2903
+ CREATE TABLE IF NOT EXISTS learning_knowledge(id TEXT PRIMARY KEY,project_id TEXT NOT NULL,body TEXT NOT NULL,updated_at TEXT NOT NULL);
2904
+ CREATE TABLE IF NOT EXISTS learning_candidates(id TEXT PRIMARY KEY,project_id TEXT NOT NULL,body TEXT NOT NULL,updated_at TEXT NOT NULL);
2905
+ CREATE TABLE IF NOT EXISTS learning_suggestion_keys(key TEXT PRIMARY KEY,candidate_id TEXT NOT NULL);
2906
+ CREATE TABLE IF NOT EXISTS learning_active(project_id TEXT NOT NULL,kind TEXT NOT NULL,target_id TEXT NOT NULL,candidate_id TEXT NOT NULL,PRIMARY KEY(project_id,kind,target_id));
2907
+ CREATE TABLE IF NOT EXISTS learning_work(id TEXT PRIMARY KEY,event_id TEXT NOT NULL,kind TEXT NOT NULL,status TEXT NOT NULL,attempts INTEGER NOT NULL DEFAULT 0,available_at INTEGER NOT NULL,lease_until INTEGER,claim_token TEXT,owner TEXT,UNIQUE(event_id,kind));
2908
+ CREATE INDEX IF NOT EXISTS learning_events_project ON learning_events(project_id,updated_at);
2909
+ CREATE INDEX IF NOT EXISTS learning_work_ready ON learning_work(status,available_at,lease_until);`);
2910
+ }
2911
+ function appendLearningEvent(db, input) {
2912
+ const allowed = ["idempotencyKey", "projectId", "kind", "outcome", "summary", "evidenceStrength", "workflowId", "workflowRevision", "executionId", "runId", "stepId", "sourceLinks", "evidenceRefs", "evidenceDigests"];
2913
+ if (!input || typeof input !== "object" || Object.keys(input).some((key) => !allowed.includes(key)))
2914
+ fail("Invalid learning event fields.");
2915
+ for (const field of ["idempotencyKey", "projectId", "kind", "outcome"])
2916
+ bounded(input[field], field);
2917
+ bounded(input.summary, "summary", 4e3);
2918
+ for (const field of ["workflowId", "executionId", "runId", "stepId"])
2919
+ if (input[field] !== void 0)
2920
+ bounded(input[field], field);
2921
+ if (input.workflowRevision !== void 0 && (!Number.isSafeInteger(input.workflowRevision) || input.workflowRevision < 1))
2922
+ fail("Invalid workflow revision.");
2923
+ if (input.evidenceStrength !== void 0 && !["operational", "hypothesis", "verified", "human-feedback"].includes(input.evidenceStrength))
2924
+ fail("Invalid evidence strength.");
2925
+ if (input.sourceLinks !== void 0)
2926
+ sources(input.sourceLinks);
2927
+ if (input.evidenceRefs !== void 0)
2928
+ refs(input.evidenceRefs, "evidence refs");
2929
+ if (input.evidenceDigests !== void 0)
2930
+ refs(input.evidenceDigests, "evidence digests");
2931
+ const existing = db.prepare("SELECT body FROM learning_events WHERE idempotency_key=?").all(input.idempotencyKey)[0];
2932
+ if (existing) {
2933
+ const event2 = JSON.parse(existing.body);
2934
+ const { id: _id, createdAt: _createdAt, ...original } = event2;
2935
+ const normalize = (v) => JSON.stringify(Object.fromEntries(Object.entries(v).filter(([, x]) => x !== void 0).sort(([a], [b]) => a.localeCompare(b))));
2936
+ if (normalize(original) !== normalize(input))
2937
+ fail("Idempotency key already describes a different event.", 409);
2938
+ return event2;
2939
+ }
2940
+ const event = { ...input, id: randomUUID3(), createdAt: (/* @__PURE__ */ new Date()).toISOString() };
2941
+ db.prepare("INSERT INTO learning_events(id,idempotency_key,project_id,body,updated_at) VALUES(?,?,?,?,?)").run(event.id, event.idempotencyKey, event.projectId, JSON.stringify(event), event.createdAt);
2942
+ return event;
2943
+ }
2944
+ function enqueueLearningWork(db, eventId, kind = "derive-knowledge") {
2945
+ bounded(eventId, "event ID");
2946
+ bounded(kind, "work kind");
2947
+ if (!read(db, "learning_events", eventId))
2948
+ fail("Learning event not found.", 404);
2949
+ db.prepare("INSERT OR IGNORE INTO learning_work(id,event_id,kind,status,available_at) VALUES(?,?,?,'pending',?)").run(randomUUID3(), eventId, kind, Date.now());
2950
+ return workRow(db.prepare("SELECT * FROM learning_work WHERE event_id=? AND kind=?").all(eventId, kind)[0]);
2951
+ }
2952
+ function workRow(row) {
2953
+ return { id: String(row.id), eventId: String(row.event_id), kind: String(row.kind), status: row.status, attempts: Number(row.attempts), availableAt: Number(row.available_at), leaseUntil: row.lease_until === null ? null : Number(row.lease_until), claimToken: row.claim_token === null ? null : String(row.claim_token), owner: row.owner === null ? null : String(row.owner) };
2954
+ }
2955
+ var LearningStore = class {
2956
+ #db;
2957
+ #ownsDatabase;
2958
+ constructor(dbPath) {
2959
+ this.#ownsDatabase = typeof dbPath !== "object";
2960
+ if (typeof dbPath === "object")
2961
+ this.#db = dbPath;
2962
+ else {
2963
+ const path = resolveOsecDbPath(dbPath);
2964
+ ensureDatabaseDirectory(path);
2965
+ this.#db = createShimmedDatabase(path);
2966
+ }
2967
+ this.#db.pragma("busy_timeout = 5000");
2968
+ initializeLearningTables(this.#db);
2969
+ }
2970
+ close() {
2971
+ if (this.#ownsDatabase)
2972
+ this.#db.close();
2973
+ }
2974
+ appendEvent(input) {
2975
+ return this.#db.transaction(() => appendLearningEvent(this.#db, input))();
2976
+ }
2977
+ recordExperience(input, kind = "derive-knowledge") {
2978
+ return this.#db.transaction(() => {
2979
+ const event = appendLearningEvent(this.#db, input);
2980
+ enqueueLearningWork(this.#db, event.id, kind);
2981
+ return event;
2982
+ })();
2983
+ }
2984
+ getEvent(id) {
2985
+ return read(this.#db, "learning_events", id);
2986
+ }
2987
+ #list(table, options = {}) {
2988
+ return this.#db.prepare(`SELECT body FROM ${table} WHERE (? IS NULL OR project_id=?) ORDER BY updated_at DESC,id LIMIT ?`).all(options.projectId ?? null, options.projectId ?? null, limit(options.limit)).map((row) => JSON.parse(row.body));
2989
+ }
2990
+ listEvents(options) {
2991
+ return this.#list("learning_events", options);
2992
+ }
2993
+ listKnowledge(options) {
2994
+ return this.#list("learning_knowledge", options);
2995
+ }
2996
+ getKnowledge(id) {
2997
+ return read(this.#db, "learning_knowledge", id);
2998
+ }
2999
+ #checkEvidence(projectId, ids) {
3000
+ refs(ids, "evidence event IDs");
3001
+ if (!ids.length)
3002
+ fail("Evidence is required.");
3003
+ for (const id of ids) {
3004
+ const event = this.getEvent(id);
3005
+ if (!event || event.projectId !== projectId)
3006
+ fail("Evidence must belong to the same project.");
3007
+ }
3008
+ }
3009
+ putKnowledge(input) {
3010
+ bounded(input.projectId, "project ID");
3011
+ bounded(input.summary, "knowledge summary", 4e3);
3012
+ sources(input.sourceLinks);
3013
+ this.#checkEvidence(input.projectId, input.evidenceEventIds);
3014
+ if (input.sourceLinks.length) {
3015
+ const fingerprint = (links) => JSON.stringify([...links].sort((a, b) => a.path.localeCompare(b.path) || a.hash.localeCompare(b.hash)));
3016
+ const matching = this.#db.prepare("SELECT body FROM learning_knowledge WHERE project_id=? AND json_extract(body,'$.summary')=? AND json_extract(body,'$.status')='disabled'").all(input.projectId, input.summary);
3017
+ for (const row of matching) {
3018
+ const prior = JSON.parse(row.body);
3019
+ if (fingerprint(prior.sourceLinks) === fingerprint(input.sourceLinks))
3020
+ return prior;
3021
+ }
3022
+ }
3023
+ const identity = `knowledge:${createHash2("sha256").update(JSON.stringify({ projectId: input.projectId, summary: input.summary, sourceLinks: [...input.sourceLinks].sort((a, b) => a.path.localeCompare(b.path)), evidenceEventIds: [...input.evidenceEventIds].sort() })).digest("hex")}`;
3024
+ const existing = this.getKnowledge(identity);
3025
+ if (existing)
3026
+ return existing;
3027
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3028
+ const entry = { projectId: input.projectId, summary: input.summary, sourceLinks: input.sourceLinks, evidenceEventIds: input.evidenceEventIds, id: identity, status: "current", createdAt: now, updatedAt: now };
3029
+ write(this.#db, "learning_knowledge", entry);
3030
+ return entry;
3031
+ }
3032
+ setKnowledgeStatus(id, status) {
3033
+ if (!["current", "stale", "disabled"].includes(status))
3034
+ fail("Invalid knowledge status.");
3035
+ const entry = this.getKnowledge(id);
3036
+ if (!entry)
3037
+ fail("Knowledge not found.", 404);
3038
+ if (status === "current" && entry.status !== "current")
3039
+ fail("Retain fresh evidence before reactivating knowledge.", 409);
3040
+ entry.status = status;
3041
+ entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3042
+ write(this.#db, "learning_knowledge", entry);
3043
+ return entry;
3044
+ }
3045
+ invalidateKnowledge(projectId, currentHashes) {
3046
+ bounded(projectId, "project ID");
3047
+ return this.#db.transaction(() => {
3048
+ let count = 0;
3049
+ const rows = this.#db.prepare("SELECT body FROM learning_knowledge WHERE project_id=?").all(projectId);
3050
+ for (const row of rows) {
3051
+ const entry = JSON.parse(row.body);
3052
+ if (entry.status === "current" && entry.sourceLinks.some((link) => !Object.hasOwn(currentHashes, link.path) || currentHashes[link.path] !== link.hash)) {
3053
+ this.setKnowledgeStatus(entry.id, "stale");
3054
+ count++;
3055
+ }
3056
+ }
3057
+ return count;
3058
+ })();
3059
+ }
3060
+ createCandidate(input) {
3061
+ for (const field of ["projectId", "kind", "targetId", "baseVersion"])
3062
+ bounded(input[field], field);
3063
+ bounded(input.proposal, "proposal", 16e3);
3064
+ this.#checkEvidence(input.projectId, input.evidenceEventIds);
3065
+ if (input.artifactDigest !== void 0 && input.artifactDigest !== learningArtifactDigest(input.proposal))
3066
+ fail("Candidate artifact digest mismatch.");
3067
+ if (input.registry) {
3068
+ for (const field of ["registryVersionId", "registryStorePath", "snapshotDigest"])
3069
+ bounded(input.registry[field], field, 4096);
3070
+ if (!["candidate", "canary", "active", "retired"].includes(input.registry.registryStatus))
3071
+ fail("Invalid registry status.");
3072
+ }
3073
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3074
+ const value = { projectId: input.projectId, kind: input.kind, targetId: input.targetId, baseVersion: input.baseVersion, proposal: input.proposal, evidenceEventIds: input.evidenceEventIds, artifactDigest: learningArtifactDigest(input.proposal), ...input.registry ? { registry: input.registry } : {}, id: randomUUID3(), status: "proposed", revision: 1, evaluations: [], createdAt: now, updatedAt: now };
3075
+ write(this.#db, "learning_candidates", value);
3076
+ return value;
3077
+ }
3078
+ createWorkflowSuggestion(input) {
3079
+ if (input.kind !== "workflow-restore" || input.registry)
3080
+ fail("Invalid workflow suggestion.");
3081
+ const key = learningArtifactDigest(JSON.stringify([input.projectId, input.targetId, input.baseVersion, input.proposal]));
3082
+ return this.#db.transaction(() => {
3083
+ if (this.#db.prepare("SELECT candidate_id FROM learning_suggestion_keys WHERE key=?").all(key).length)
3084
+ return null;
3085
+ const candidate = this.createCandidate(input);
3086
+ this.#db.prepare("INSERT INTO learning_suggestion_keys(key,candidate_id) VALUES(?,?)").run(key, candidate.id);
3087
+ return candidate;
3088
+ })();
3089
+ }
3090
+ getCandidate(id) {
3091
+ return read(this.#db, "learning_candidates", id);
3092
+ }
3093
+ listCandidates(options) {
3094
+ return this.#list("learning_candidates", options);
3095
+ }
3096
+ #candidate(id, revision) {
3097
+ const value = this.getCandidate(id);
3098
+ if (!value)
3099
+ fail("Candidate not found.", 404);
3100
+ if (value.revision !== revision)
3101
+ fail("Candidate changed. Reload before updating.", 409);
3102
+ return value;
3103
+ }
3104
+ #saveCandidate(value) {
3105
+ value.revision++;
3106
+ value.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3107
+ write(this.#db, "learning_candidates", value);
3108
+ return value;
3109
+ }
3110
+ transitionCandidate(id, revision, status) {
3111
+ return this.#db.transaction(() => {
3112
+ const value = this.#candidate(id, revision);
3113
+ const allowed = { proposed: ["evaluating", "rejected"], evaluating: ["rejected"], validated: ["rejected"] };
3114
+ if (!allowed[value.status]?.includes(status))
3115
+ fail("Invalid candidate transition.", 409);
3116
+ value.status = status;
3117
+ return this.#saveCandidate(value);
3118
+ })();
3119
+ }
3120
+ recordEvaluation(id, revision, input) {
3121
+ for (const field of ["suiteDigest", "baselineDigest", "candidateDigest"])
3122
+ bounded(input[field], field);
3123
+ if (input.evaluatorDigest !== void 0)
3124
+ bounded(input.evaluatorDigest, "evaluator digest");
3125
+ if (input.evaluationKind !== void 0 && !["output-fixture", "security-proof"].includes(input.evaluationKind))
3126
+ fail("Invalid evaluation kind.");
3127
+ if (input.outcome !== void 0 && !["passed", "failed", "inconclusive"].includes(input.outcome))
3128
+ fail("Invalid evaluation outcome.");
3129
+ if (input.outcome !== void 0 && input.passed !== (input.outcome === "passed"))
3130
+ fail("Contradictory evaluation outcome.");
3131
+ if (typeof input.passed !== "boolean" || !input.metrics || typeof input.metrics !== "object" || Array.isArray(input.metrics) || Object.keys(input.metrics).length > 32 || Object.entries(input.metrics).some(([key, value]) => key.length > 128 || typeof value !== "number" || !Number.isFinite(value)))
3132
+ fail("Invalid evaluation metrics.");
3133
+ return this.#db.transaction(() => {
3134
+ const value = this.#candidate(id, revision);
3135
+ if (value.evaluations.length >= 64)
3136
+ fail("Too many evaluation receipts.");
3137
+ if (value.status !== "evaluating")
3138
+ fail("Candidate is not evaluating.", 409);
3139
+ if (input.candidateDigest !== value.artifactDigest)
3140
+ fail("Evaluation does not match the candidate artifact.", 409);
3141
+ value.evaluations.push({ ...input.evaluatorDigest ? { evaluatorDigest: input.evaluatorDigest } : {}, ...input.evaluationKind ? { evaluationKind: input.evaluationKind } : {}, ...input.outcome ? { outcome: input.outcome } : {}, suiteDigest: input.suiteDigest, baselineDigest: input.baselineDigest, candidateDigest: input.candidateDigest, passed: input.passed, metrics: input.metrics, id: randomUUID3(), createdAt: (/* @__PURE__ */ new Date()).toISOString() });
3142
+ value.status = input.outcome === "inconclusive" ? "evaluating" : input.passed ? "validated" : "rejected";
3143
+ return this.#saveCandidate(value);
3144
+ })();
3145
+ }
3146
+ /** Reviewed workflow edit, distinct from evaluated security deployment. */
3147
+ markWorkflowSuggestionApplied(id, revision) {
3148
+ const value = this.#candidate(id, revision);
3149
+ if (value.kind !== "workflow-restore" || value.registry || value.status !== "proposed")
3150
+ fail("Suggestion is no longer available.", 409);
3151
+ value.status = "applied";
3152
+ return this.#saveCandidate(value);
3153
+ }
3154
+ getActiveCandidate(projectId, kind, targetId) {
3155
+ const row = this.#db.prepare("SELECT candidate_id FROM learning_active WHERE project_id=? AND kind=? AND target_id=?").all(projectId, kind, targetId)[0];
3156
+ return row ? this.getCandidate(row.candidate_id) : null;
3157
+ }
3158
+ activateCandidate(id, revision, expectedActiveId = null) {
3159
+ return this.#db.transaction(() => {
3160
+ const value = this.#candidate(id, revision);
3161
+ if (value.status !== "validated" || !value.evaluations.at(-1)?.passed)
3162
+ fail("Candidate needs a passing evaluation.", 409);
3163
+ const active = this.getActiveCandidate(value.projectId, value.kind, value.targetId);
3164
+ if ((active?.id ?? null) !== expectedActiveId)
3165
+ fail("Active version changed.", 409);
3166
+ if (active) {
3167
+ active.status = "retired";
3168
+ this.#saveCandidate(active);
3169
+ value.previousActiveId = active.id;
3170
+ }
3171
+ value.status = "active";
3172
+ this.#db.prepare("INSERT INTO learning_active(project_id,kind,target_id,candidate_id) VALUES(?,?,?,?) ON CONFLICT(project_id,kind,target_id) DO UPDATE SET candidate_id=excluded.candidate_id").run(value.projectId, value.kind, value.targetId, value.id);
3173
+ return this.#saveCandidate(value);
3174
+ })();
3175
+ }
3176
+ rollbackCandidate(id, revision) {
3177
+ return this.#db.transaction(() => {
3178
+ const value = this.#candidate(id, revision);
3179
+ if (value.status !== "active" || this.getActiveCandidate(value.projectId, value.kind, value.targetId)?.id !== id)
3180
+ fail("Candidate is not active.", 409);
3181
+ value.status = "retired";
3182
+ const previous = value.previousActiveId ? this.getCandidate(value.previousActiveId) : null;
3183
+ if (previous) {
3184
+ previous.status = "active";
3185
+ this.#saveCandidate(previous);
3186
+ this.#db.prepare("UPDATE learning_active SET candidate_id=? WHERE project_id=? AND kind=? AND target_id=?").run(previous.id, value.projectId, value.kind, value.targetId);
3187
+ } else
3188
+ this.#db.prepare("DELETE FROM learning_active WHERE project_id=? AND kind=? AND target_id=?").run(value.projectId, value.kind, value.targetId);
3189
+ return this.#saveCandidate(value);
3190
+ })();
3191
+ }
3192
+ enqueue(eventId, kind = "derive-knowledge") {
3193
+ return enqueueLearningWork(this.#db, eventId, kind);
3194
+ }
3195
+ claimWork(owner, leaseMs = 6e4, now = Date.now(), projectId) {
3196
+ bounded(owner, "worker owner");
3197
+ if (!Number.isSafeInteger(now) || !Number.isSafeInteger(leaseMs) || leaseMs < 100 || leaseMs > 36e5)
3198
+ fail("Invalid lease.");
3199
+ return this.#db.transaction(() => {
3200
+ const row = this.#db.prepare("SELECT id FROM learning_work WHERE ((status='pending' AND available_at<=?) OR (status='claimed' AND lease_until<=?)) AND (? IS NULL OR event_id IN (SELECT id FROM learning_events WHERE project_id=?)) ORDER BY available_at,id LIMIT 1").all(now, now, projectId ?? null, projectId ?? null)[0];
3201
+ if (!row)
3202
+ return null;
3203
+ this.#db.prepare("UPDATE learning_work SET status='claimed',owner=?,claim_token=?,lease_until=?,attempts=attempts+1 WHERE id=?").run(owner, randomUUID3(), now + leaseMs, row.id);
3204
+ return workRow(this.#db.prepare("SELECT * FROM learning_work WHERE id=?").all(row.id)[0]);
3205
+ })();
3206
+ }
3207
+ queueStatus(projectId) {
3208
+ const counts = { pending: 0, claimed: 0, completed: 0 };
3209
+ for (const row of this.#db.prepare("SELECT status,COUNT(*) AS count FROM learning_work WHERE (? IS NULL OR event_id IN (SELECT id FROM learning_events WHERE project_id=?)) GROUP BY status").all(projectId ?? null, projectId ?? null)) {
3210
+ const value = row;
3211
+ counts[value.status] = Number(value.count);
3212
+ }
3213
+ return counts;
3214
+ }
3215
+ /** Mirror a trusted external registry's existing lifecycle; this is not a promotion API. */
3216
+ mirrorRegistryCandidate(input, status, evaluations = []) {
3217
+ if (!["proposed", "canary", "active", "retired", "rejected", "validated", "evaluating"].includes(status))
3218
+ fail("Invalid registry mirror status.");
3219
+ const matching = { candidate: status === "rejected" ? "rejected" : "proposed", canary: "canary", active: "active", retired: "retired" };
3220
+ if (matching[input.registry.registryStatus] !== status)
3221
+ fail("Mirror status must match the registry.");
3222
+ if (evaluations.length > 64)
3223
+ fail("Too many evaluation receipts.");
3224
+ for (const receipt of evaluations) {
3225
+ if (receipt.evaluationKind !== "output-fixture")
3226
+ fail("External registry receipts are output fixtures.");
3227
+ for (const field of ["suiteDigest", "baselineDigest", "candidateDigest"])
3228
+ bounded(receipt[field], field);
3229
+ if (receipt.candidateDigest !== input.registry.snapshotDigest)
3230
+ fail("Registry receipt snapshot does not match.");
3231
+ if (typeof receipt.passed !== "boolean" || !receipt.metrics || typeof receipt.metrics !== "object" || Array.isArray(receipt.metrics) || Object.keys(receipt.metrics).length > 32 || Object.entries(receipt.metrics).some(([k, v]) => k.length > 128 || typeof v !== "number" || !Number.isFinite(v)))
3232
+ fail("Invalid mirror receipt.");
3233
+ }
3234
+ return this.#db.transaction(() => {
3235
+ const rows = this.#db.prepare("SELECT body FROM learning_candidates WHERE project_id=?").all(input.projectId);
3236
+ const existing = rows.map((row) => JSON.parse(row.body)).find((candidate) => candidate.registry?.registryStorePath === input.registry.registryStorePath && candidate.registry.registryVersionId === input.registry.registryVersionId);
3237
+ if (existing && (existing.proposal !== input.proposal || existing.registry?.snapshotDigest !== input.registry.snapshotDigest))
3238
+ fail("Immutable registry artifact changed.", 409);
3239
+ const value = existing ?? this.createCandidate(input);
3240
+ value.status = status;
3241
+ value.registry = input.registry;
3242
+ for (const receipt of evaluations)
3243
+ if (!value.evaluations.some((v) => v.suiteDigest === receipt.suiteDigest && v.candidateDigest === receipt.candidateDigest && v.baselineDigest === receipt.baselineDigest && v.passed === receipt.passed))
3244
+ value.evaluations.push({ ...receipt, id: randomUUID3(), createdAt: (/* @__PURE__ */ new Date()).toISOString() });
3245
+ return this.#saveCandidate(value);
3246
+ })();
3247
+ }
3248
+ #finishWork(id, token, status, delayMs, now) {
3249
+ if (!Number.isSafeInteger(delayMs) || delayMs < 0 || delayMs > 864e5 || !Number.isSafeInteger(now))
3250
+ fail("Invalid retry delay.");
3251
+ const result = this.#db.prepare("UPDATE learning_work SET status=?,available_at=?,owner=NULL,claim_token=NULL,lease_until=NULL WHERE id=? AND status='claimed' AND claim_token=? AND lease_until>?").run(status, now + delayMs, id, token, now);
3252
+ if (!result.changes)
3253
+ fail("Work lease expired or belongs to another worker.", 409);
3254
+ }
3255
+ completeWork(id, token, now = Date.now()) {
3256
+ this.#finishWork(id, token, "completed", 0, now);
3257
+ }
3258
+ retryWork(id, token, delayMs = 1e3, now = Date.now()) {
3259
+ this.#finishWork(id, token, "pending", delayMs, now);
3260
+ }
3261
+ };
3262
+
3263
+ // packages/db/dist/security-workflows.js
3264
+ function workflowRestorePolicy(workflow) {
3265
+ return JSON.stringify({
3266
+ target: workflow.target,
3267
+ template: workflow.template,
3268
+ edges: workflow.edges,
3269
+ nodes: workflow.nodes.map(({ label: _label, execution, ...node }) => ({
3270
+ ...node,
3271
+ ...execution ? { execution: { allowedAgentTools: execution.allowedAgentTools } } : {}
3272
+ }))
3273
+ });
3274
+ }
2620
3275
  var SecurityWorkflowStoreError = class extends Error {
2621
3276
  statusCode;
2622
3277
  constructor(message, statusCode) {
@@ -2629,17 +3284,39 @@ var activeStatuses = { queued: true, running: true };
2629
3284
  var statuses = { queued: true, running: true, completed: true, failed: true, cancelled: true, interrupted: true };
2630
3285
  var SecurityWorkflowStore = class {
2631
3286
  #db;
2632
- #runnerInstanceId = randomUUID3();
3287
+ #runnerInstanceId = randomUUID4();
2633
3288
  constructor(dbPath) {
2634
3289
  const path = resolveOsecDbPath(dbPath);
2635
3290
  ensureDatabaseDirectory(path);
2636
3291
  this.#db = createShimmedDatabase(path);
2637
3292
  this.#db.pragma("busy_timeout = 5000");
3293
+ initializeLearningTables(this.#db);
2638
3294
  this.#db.exec(`CREATE TABLE IF NOT EXISTS workflow_definitions (id TEXT PRIMARY KEY, revision INTEGER NOT NULL, definition_json TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL);
2639
3295
  CREATE TABLE IF NOT EXISTS workflow_executions (id TEXT PRIMARY KEY, workflow_id TEXT NOT NULL, workflow_revision INTEGER NOT NULL, session_id TEXT NOT NULL, status TEXT NOT NULL, execution_json TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL);
2640
3296
  CREATE INDEX IF NOT EXISTS workflow_executions_workflow ON workflow_executions(workflow_id, created_at);
2641
3297
  CREATE INDEX IF NOT EXISTS workflow_executions_session ON workflow_executions(session_id, created_at);`);
2642
3298
  this.#db.exec("CREATE TABLE IF NOT EXISTS workflow_execution_results (execution_id TEXT PRIMARY KEY, result_json TEXT NOT NULL);");
3299
+ this.#db.exec(`CREATE TABLE IF NOT EXISTS workflow_definition_versions (
3300
+ workflow_id TEXT NOT NULL, revision INTEGER NOT NULL, definition_json TEXT NOT NULL,
3301
+ parent_revision INTEGER, restored_from_revision INTEGER, digest TEXT NOT NULL, created_at TEXT NOT NULL,
3302
+ PRIMARY KEY (workflow_id, revision));`);
3303
+ this.#db.transaction(() => {
3304
+ for (const workflow of this.list()) {
3305
+ if (this.getVersion(workflow.id, workflow.revision))
3306
+ continue;
3307
+ this.#recordVersion(workflow, null, null, true);
3308
+ const snapshots = this.#db.prepare("SELECT execution_json FROM workflow_executions WHERE workflow_id = ? ORDER BY created_at, id").all(workflow.id);
3309
+ for (const row of snapshots) {
3310
+ const execution = JSON.parse(String(row.execution_json));
3311
+ if (execution.workflow.revision < workflow.revision)
3312
+ this.#recordVersion(execution.workflow, null, null, true);
3313
+ }
3314
+ }
3315
+ })();
3316
+ }
3317
+ /** Shares the control connection so learning and workflow transactions cannot race. */
3318
+ learningStore() {
3319
+ return new LearningStore(this.#db);
2643
3320
  }
2644
3321
  list() {
2645
3322
  return this.#db.prepare("SELECT definition_json FROM workflow_definitions ORDER BY updated_at DESC, id").all().map((row) => JSON.parse(String(row.definition_json)));
@@ -2648,6 +3325,65 @@ var SecurityWorkflowStore = class {
2648
3325
  const row = this.#db.prepare("SELECT definition_json FROM workflow_definitions WHERE id = ?").all(id)[0];
2649
3326
  return row ? JSON.parse(String(row.definition_json)) : null;
2650
3327
  }
3328
+ listVersions(id) {
3329
+ return this.#db.prepare("SELECT * FROM workflow_definition_versions WHERE workflow_id = ? ORDER BY revision DESC").all(id).map(readVersion);
3330
+ }
3331
+ getVersion(id, revision) {
3332
+ const row = this.#db.prepare("SELECT * FROM workflow_definition_versions WHERE workflow_id = ? AND revision = ?").all(id, revision)[0];
3333
+ return row ? readVersion(row) : null;
3334
+ }
3335
+ #recordVersion(workflow, parentRevision, restoredFromRevision, recover = false) {
3336
+ const json = JSON.stringify(workflow);
3337
+ this.#db.prepare(`${recover ? "INSERT OR IGNORE" : "INSERT"} INTO workflow_definition_versions (workflow_id, revision, definition_json, parent_revision, restored_from_revision, digest, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)`).run(workflow.id, workflow.revision, json, parentRevision, restoredFromRevision, createHash3("sha256").update(json).digest("hex"), workflow.updatedAt);
3338
+ }
3339
+ /** CAS restore: existing runs and reviewed schedule revisions remain pinned. */
3340
+ rollback(id, targetRevision, expectedRevision) {
3341
+ if (!Number.isInteger(targetRevision) || targetRevision < 1 || !Number.isInteger(expectedRevision) || expectedRevision < 1)
3342
+ throw new SecurityWorkflowStoreError("Invalid workflow revision.", 400);
3343
+ return this.#db.transaction(() => {
3344
+ const current = this.get(id);
3345
+ if (!current)
3346
+ throw new SecurityWorkflowStoreError("Workflow not found.", 404);
3347
+ if (current.revision !== expectedRevision)
3348
+ throw new SecurityWorkflowStoreError("Workflow changed. Reload before restoring.", 409);
3349
+ const previous = this.getVersion(id, targetRevision);
3350
+ if (!previous)
3351
+ throw new SecurityWorkflowStoreError("Workflow version not found.", 404);
3352
+ const workflow = { ...previous.definition, revision: current.revision + 1, createdAt: current.createdAt, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
3353
+ this.#db.prepare("UPDATE workflow_definitions SET revision = ?, definition_json = ?, updated_at = ? WHERE id = ? AND revision = ?").run(workflow.revision, JSON.stringify(workflow), workflow.updatedAt, id, expectedRevision);
3354
+ this.#recordVersion(workflow, current.revision, targetRevision);
3355
+ return workflow;
3356
+ })();
3357
+ }
3358
+ applyLearningRestore(candidateId, candidateRevision, expectedWorkflowRevision) {
3359
+ return this.#db.transaction(() => {
3360
+ const learning = this.learningStore();
3361
+ const candidate = learning.getCandidate(candidateId);
3362
+ if (!candidate || candidate.kind !== "workflow-restore" || candidate.status !== "proposed" || candidate.revision !== candidateRevision)
3363
+ throw new SecurityWorkflowStoreError("Suggestion changed. Reload before applying.", 409);
3364
+ const proposal = JSON.parse(candidate.proposal);
3365
+ const current = this.get(candidate.targetId);
3366
+ const previous = this.getVersion(candidate.targetId, proposal.restoreRevision)?.definition;
3367
+ if (!current || !previous || proposal.action !== "restore-workflow-instructions" || proposal.workflowId !== current.id || current.revision !== expectedWorkflowRevision || proposal.fromRevision !== current.revision || candidate.baseVersion !== String(current.revision) || candidate.projectId !== learningProjectId(current.target || current.id) || workflowRestorePolicy(current) !== workflowRestorePolicy(previous))
3368
+ throw new SecurityWorkflowStoreError("Workflow changed or its scope differs. Review it again.", 409);
3369
+ const updated = { ...previous, revision: current.revision + 1, createdAt: current.createdAt, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
3370
+ this.#db.prepare("UPDATE workflow_definitions SET revision = ?, definition_json = ?, updated_at = ? WHERE id = ? AND revision = ?").run(updated.revision, JSON.stringify(updated), updated.updatedAt, current.id, current.revision);
3371
+ this.#recordVersion(updated, current.revision, previous.revision);
3372
+ learning.markWorkflowSuggestionApplied(candidate.id, candidateRevision);
3373
+ appendLearningEvent(this.#db, {
3374
+ idempotencyKey: `workflow-suggestion-applied:${candidate.id}`,
3375
+ projectId: candidate.projectId,
3376
+ kind: "workflow-suggestion",
3377
+ outcome: "applied",
3378
+ summary: "Earlier workflow instructions restored after review.",
3379
+ evidenceStrength: "operational",
3380
+ workflowId: current.id,
3381
+ workflowRevision: updated.revision,
3382
+ evidenceRefs: [candidate.id]
3383
+ });
3384
+ return updated;
3385
+ })();
3386
+ }
2651
3387
  save(value) {
2652
3388
  let input;
2653
3389
  try {
@@ -2661,12 +3397,15 @@ var SecurityWorkflowStore = class {
2661
3397
  throw new SecurityWorkflowStoreError("Workflow not found.", 404);
2662
3398
  if (existing && input.revision !== existing.revision)
2663
3399
  throw new SecurityWorkflowStoreError("Workflow changed. Reload before saving.", 409);
3400
+ if (input.id && !existing && this.#db.prepare("SELECT 1 FROM workflow_definition_versions WHERE workflow_id = ? LIMIT 1").all(input.id).length > 0)
3401
+ throw new SecurityWorkflowStoreError("Deleted workflow IDs cannot be reused.", 409);
2664
3402
  const now = (/* @__PURE__ */ new Date()).toISOString();
2665
- const workflow = { ...input, id: input.id ?? randomUUID3(), revision: (existing?.revision ?? 0) + 1, createdAt: existing?.createdAt ?? now, updatedAt: now };
3403
+ const workflow = { ...input, id: input.id ?? randomUUID4(), revision: (existing?.revision ?? 0) + 1, createdAt: existing?.createdAt ?? now, updatedAt: now };
2666
3404
  if (existing)
2667
3405
  this.#db.prepare("UPDATE workflow_definitions SET revision = ?, definition_json = ?, updated_at = ? WHERE id = ?").run(workflow.revision, JSON.stringify(workflow), now, workflow.id);
2668
3406
  else
2669
3407
  this.#db.prepare("INSERT INTO workflow_definitions (id, revision, definition_json, created_at, updated_at) VALUES (?, ?, ?, ?, ?)").run(workflow.id, workflow.revision, JSON.stringify(workflow), now, now);
3408
+ this.#recordVersion(workflow, existing?.revision ?? null, null);
2670
3409
  return workflow;
2671
3410
  })();
2672
3411
  }
@@ -2691,7 +3430,7 @@ var SecurityWorkflowStore = class {
2691
3430
  if (revision !== void 0 && revision !== workflow.revision)
2692
3431
  throw new SecurityWorkflowStoreError("Workflow changed. Review it before running.", 409);
2693
3432
  const now = (/* @__PURE__ */ new Date()).toISOString();
2694
- const execution = { id: randomUUID3(), workflowId, workflowRevision: workflow.revision, workflow, sessionId, ownerPid: process.pid, runnerInstanceId: this.#runnerInstanceId, status: "queued", nodeResults: {}, createdAt: now, updatedAt: now };
3433
+ const execution = { id: randomUUID4(), workflowId, workflowRevision: workflow.revision, workflow, sessionId, ownerPid: process.pid, runnerInstanceId: this.#runnerInstanceId, status: "queued", nodeResults: {}, createdAt: now, updatedAt: now };
2695
3434
  this.#db.prepare("INSERT INTO workflow_executions (id, workflow_id, workflow_revision, session_id, status, execution_json, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)").run(execution.id, workflowId, workflow.revision, sessionId, execution.status, JSON.stringify(execution), now, now);
2696
3435
  return execution;
2697
3436
  })();
@@ -2702,8 +3441,8 @@ var SecurityWorkflowStore = class {
2702
3441
  throw new SecurityWorkflowStoreError("Invalid execution owner.", 400);
2703
3442
  const input = parseSecurityWorkflowInput(value);
2704
3443
  const now = (/* @__PURE__ */ new Date()).toISOString();
2705
- const workflow = { ...input, id: input.id ?? randomUUID3(), revision: input.revision ?? 1, createdAt: now, updatedAt: now };
2706
- const execution = { id: randomUUID3(), workflowId: workflow.id, workflowRevision: workflow.revision, workflow, sessionId, ownerPid: process.pid, runnerInstanceId: this.#runnerInstanceId, status: "queued", nodeResults: {}, createdAt: now, updatedAt: now };
3444
+ const workflow = { ...input, id: input.id ?? randomUUID4(), revision: input.revision ?? 1, createdAt: now, updatedAt: now };
3445
+ const execution = { id: randomUUID4(), workflowId: workflow.id, workflowRevision: workflow.revision, workflow, sessionId, ownerPid: process.pid, runnerInstanceId: this.#runnerInstanceId, status: "queued", nodeResults: {}, createdAt: now, updatedAt: now };
2707
3446
  this.#db.prepare("INSERT INTO workflow_executions (id, workflow_id, workflow_revision, session_id, status, execution_json, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)").run(execution.id, execution.workflowId, execution.workflowRevision, sessionId, execution.status, JSON.stringify(execution), now, now);
2708
3447
  return execution;
2709
3448
  }
@@ -2757,6 +3496,29 @@ var SecurityWorkflowStore = class {
2757
3496
  }
2758
3497
  const execution = { ...current, status: update.status ?? current.status, jobId: update.jobId ?? current.jobId, nodeResults: update.nodeResults ?? current.nodeResults, error: update.error ?? current.error, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
2759
3498
  this.#db.prepare("UPDATE workflow_executions SET status = ?, execution_json = ?, updated_at = ? WHERE id = ?").run(execution.status, JSON.stringify(execution), execution.updatedAt, id);
3499
+ const projectId = learningProjectId(execution.workflow.target || execution.workflowId);
3500
+ const observe = (kind, outcome, stepId) => {
3501
+ const event = appendLearningEvent(this.#db, {
3502
+ idempotencyKey: `workflow-terminal:${createHash3("sha256").update(JSON.stringify({ executionId: id, stepId: stepId ?? null, kind, outcome })).digest("hex")}`,
3503
+ projectId,
3504
+ kind,
3505
+ outcome,
3506
+ evidenceStrength: "operational",
3507
+ summary: stepId ? `Workflow step ${outcome}.` : `Workflow execution ${outcome}.`,
3508
+ executionId: id,
3509
+ workflowId: execution.workflowId,
3510
+ workflowRevision: execution.workflowRevision,
3511
+ ...stepId ? { stepId } : {}
3512
+ });
3513
+ enqueueLearningWork(this.#db, event.id);
3514
+ };
3515
+ const terminalNodes = /* @__PURE__ */ new Set(["completed", "failed", "cancelled", "interrupted", "skipped", "blocked"]);
3516
+ for (const [stepId, result] of Object.entries(execution.nodeResults)) {
3517
+ if (terminalNodes.has(result.status) && !terminalNodes.has(current.nodeResults[stepId]?.status ?? ""))
3518
+ observe("workflow-step", result.status, stepId);
3519
+ }
3520
+ if (!activeStatuses[execution.status])
3521
+ observe("workflow-run", execution.status);
2760
3522
  return execution;
2761
3523
  })();
2762
3524
  }
@@ -2789,9 +3551,13 @@ function processAlive(pid) {
2789
3551
  return !(error && typeof error === "object" && "code" in error && error.code === "ESRCH");
2790
3552
  }
2791
3553
  }
3554
+ function readVersion(value) {
3555
+ const row = value;
3556
+ return { workflowId: row.workflow_id, revision: row.revision, definition: JSON.parse(row.definition_json), parentRevision: row.parent_revision, restoredFromRevision: row.restored_from_revision, digest: row.digest, createdAt: row.created_at };
3557
+ }
2792
3558
 
2793
3559
  // packages/db/dist/workflow-triggers.js
2794
- import { randomUUID as randomUUID4 } from "node:crypto";
3560
+ import { randomUUID as randomUUID5 } from "node:crypto";
2795
3561
  var TRIGGER_INTERVAL_MS = { hourly: 36e5, daily: 864e5, weekly: 6048e5 };
2796
3562
  function nextWorkflowTriggerFire(startAt, cadence, now) {
2797
3563
  const anchor = Date.parse(startAt);
@@ -2819,7 +3585,7 @@ var WorkflowTriggerStore = class {
2819
3585
  }
2820
3586
  create(input, now = Date.now()) {
2821
3587
  const stamp = new Date(now).toISOString();
2822
- const trigger = { ...input, kind: "schedule", id: randomUUID4(), version: 1, nextFireAt: nextWorkflowTriggerFire(input.startAt, input.cadence, now), createdAt: stamp, updatedAt: stamp };
3588
+ const trigger = { ...input, kind: "schedule", id: randomUUID5(), version: 1, nextFireAt: nextWorkflowTriggerFire(input.startAt, input.cadence, now), createdAt: stamp, updatedAt: stamp };
2823
3589
  this.#db.prepare("INSERT INTO workflow_triggers(id,workflow_id,enabled,next_fire_at,active_claim,trigger_json) VALUES(?,?,?,?,NULL,?)").run(trigger.id, trigger.workflowId, trigger.enabled ? 1 : 0, trigger.nextFireAt, JSON.stringify(trigger));
2824
3590
  return trigger;
2825
3591
  }
@@ -2845,7 +3611,7 @@ var WorkflowTriggerStore = class {
2845
3611
  const current = this.get(id);
2846
3612
  if (!current?.enabled || current.activeClaim || Date.parse(current.nextFireAt) > now)
2847
3613
  return null;
2848
- const claim = randomUUID4();
3614
+ const claim = randomUUID5();
2849
3615
  const next = { ...current, version: (current.version ?? 1) + 1, activeClaim: claim, ownerPid: process.pid, nextFireAt: nextWorkflowTriggerFire(current.startAt, current.cadence, now), lastStatus: "starting", lastExecutionId: void 0, lastError: void 0, updatedAt: new Date(now).toISOString() };
2850
3616
  this.#db.prepare("UPDATE workflow_triggers SET active_claim=?, next_fire_at=?, trigger_json=? WHERE id=? AND enabled=1 AND active_claim IS NULL AND next_fire_at=? AND trigger_json=?").run(claim, next.nextFireAt, JSON.stringify(next), id, current.nextFireAt, JSON.stringify(current));
2851
3617
  return this.get(id)?.activeClaim === claim ? next : null;
@@ -2910,6 +3676,14 @@ export {
2910
3676
  resolveOsecRunStorage,
2911
3677
  listOsecRunDatabasePaths,
2912
3678
  writeOsecRunReport,
3679
+ LearningStoreError,
3680
+ learningProjectId,
3681
+ learningArtifactDigest,
3682
+ initializeLearningTables,
3683
+ appendLearningEvent,
3684
+ enqueueLearningWork,
3685
+ LearningStore,
3686
+ workflowRestorePolicy,
2913
3687
  SecurityWorkflowStoreError,
2914
3688
  SecurityWorkflowStore,
2915
3689
  TRIGGER_INTERVAL_MS,