agentix-cli 0.25.0 → 0.27.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 (269) hide show
  1. package/README.md +2 -0
  2. package/dist/{App-6Y7B3YQH.js → App-U52IPUK2.js} +2 -2
  3. package/dist/{ChatApp-KIEUJ2DS.js → ChatApp-XP4OMHPL.js} +2 -2
  4. package/dist/agent-V2CA4TXD.js +2 -0
  5. package/dist/article-fields-L4D2BOGY.js +2 -0
  6. package/dist/article-quality-44ONVGLN.js +3 -0
  7. package/dist/article-quality-44ONVGLN.js.map +1 -0
  8. package/dist/{audit-JU3HWISB.js → audit-4245CX6P.js} +2 -2
  9. package/dist/backfill-MTD2B62I.js +6 -0
  10. package/dist/backfill-MTD2B62I.js.map +1 -0
  11. package/dist/{board-dashboard-CQ5F4GPD.js → board-dashboard-TTMAYRLG.js} +2445 -519
  12. package/dist/board-dashboard-TTMAYRLG.js.map +1 -0
  13. package/dist/{builtin-FRKAQMFS.js → builtin-B67DMJ3M.js} +2 -2
  14. package/dist/calibration-ILS5OI44.js +2 -0
  15. package/dist/chunk-2T5W3ETS.js +5 -0
  16. package/dist/{chunk-FDMWOPEL.js.map → chunk-2T5W3ETS.js.map} +1 -1
  17. package/dist/chunk-3SCPRYJB.js +5 -0
  18. package/dist/chunk-3SCPRYJB.js.map +1 -0
  19. package/dist/chunk-4BR7D2OU.js +36 -0
  20. package/dist/chunk-4BR7D2OU.js.map +1 -0
  21. package/dist/{chunk-P4VWIVKN.js → chunk-52ZPELD2.js} +403 -29
  22. package/dist/chunk-52ZPELD2.js.map +1 -0
  23. package/dist/{chunk-5U35WAZF.js → chunk-77OV7346.js} +6 -6
  24. package/dist/chunk-77OV7346.js.map +1 -0
  25. package/dist/chunk-AJ5RMU7X.js +2 -0
  26. package/dist/chunk-AJ5RMU7X.js.map +1 -0
  27. package/dist/chunk-AJONZWLT.js +2 -0
  28. package/dist/chunk-AJONZWLT.js.map +1 -0
  29. package/dist/chunk-AX2PWSRT.js +114 -0
  30. package/dist/chunk-AX2PWSRT.js.map +1 -0
  31. package/dist/chunk-BH3GTCZ6.js +2 -0
  32. package/dist/chunk-BH3GTCZ6.js.map +1 -0
  33. package/dist/chunk-BPIRO4YD.js +126 -0
  34. package/dist/chunk-BPIRO4YD.js.map +1 -0
  35. package/dist/chunk-CJQQIRPD.js +13 -0
  36. package/dist/chunk-CJQQIRPD.js.map +1 -0
  37. package/dist/chunk-DCDQZR7I.js +24 -0
  38. package/dist/chunk-DCDQZR7I.js.map +1 -0
  39. package/dist/chunk-EVWEG3VD.js +3 -0
  40. package/dist/chunk-EVWEG3VD.js.map +1 -0
  41. package/dist/chunk-F4HZTZHW.js +2 -0
  42. package/dist/chunk-F4HZTZHW.js.map +1 -0
  43. package/dist/chunk-F6CNF7V3.js +25 -0
  44. package/dist/chunk-F6CNF7V3.js.map +1 -0
  45. package/dist/chunk-FIEJDZVC.js +121 -0
  46. package/dist/chunk-FIEJDZVC.js.map +1 -0
  47. package/dist/chunk-FIOB3MEX.js +2 -0
  48. package/dist/{chunk-H5D7ATXS.js → chunk-FKBGJYWQ.js} +5 -5
  49. package/dist/chunk-FKBGJYWQ.js.map +1 -0
  50. package/dist/chunk-GDWJUGB5.js +9 -0
  51. package/dist/chunk-GDWJUGB5.js.map +1 -0
  52. package/dist/{chunk-TRJTTBE2.js → chunk-IB3F7VBE.js} +7 -7
  53. package/dist/{chunk-TRJTTBE2.js.map → chunk-IB3F7VBE.js.map} +1 -1
  54. package/dist/chunk-IBUSGCLQ.js +109 -0
  55. package/dist/chunk-IBUSGCLQ.js.map +1 -0
  56. package/dist/{chunk-LDHPMJ7M.js → chunk-IWMW5432.js} +3 -3
  57. package/dist/chunk-IWMW5432.js.map +1 -0
  58. package/dist/chunk-K33GIBSE.js +2 -0
  59. package/dist/chunk-K33GIBSE.js.map +1 -0
  60. package/dist/chunk-L2IZPR6P.js +45 -0
  61. package/dist/chunk-L2IZPR6P.js.map +1 -0
  62. package/dist/chunk-L2WSPY6S.js +2 -0
  63. package/dist/chunk-L2WSPY6S.js.map +1 -0
  64. package/dist/chunk-LCIREYTD.js +54 -0
  65. package/dist/chunk-LCIREYTD.js.map +1 -0
  66. package/dist/chunk-LNRB5MCU.js +530 -0
  67. package/dist/chunk-LNRB5MCU.js.map +1 -0
  68. package/dist/chunk-OPHIXXGI.js +157 -0
  69. package/dist/chunk-OPHIXXGI.js.map +1 -0
  70. package/dist/{chunk-JTJIPNXY.js → chunk-PNWNVOEZ.js} +1 -1
  71. package/dist/chunk-PNWNVOEZ.js.map +1 -0
  72. package/dist/{chunk-LNXCJWOU.js → chunk-UFAGYX73.js} +45 -47
  73. package/dist/chunk-UFAGYX73.js.map +1 -0
  74. package/dist/{chunk-DJEZ2HVU.js → chunk-VDMYBPYB.js} +1 -1
  75. package/dist/chunk-VDMYBPYB.js.map +1 -0
  76. package/dist/chunk-VTE7L6LO.js +5 -0
  77. package/dist/{chunk-G57RZCXM.js.map → chunk-VTE7L6LO.js.map} +1 -1
  78. package/dist/chunk-W5C2GDNC.js +32 -0
  79. package/dist/{chunk-JP5IEXR7.js.map → chunk-W5C2GDNC.js.map} +1 -1
  80. package/dist/chunk-WPMX3VGI.js +3 -0
  81. package/dist/chunk-WPMX3VGI.js.map +1 -0
  82. package/dist/chunk-WZ72HX5C.js +1182 -0
  83. package/dist/chunk-WZ72HX5C.js.map +1 -0
  84. package/dist/chunk-ZTEU5JVQ.js +2 -0
  85. package/dist/chunk-ZTEU5JVQ.js.map +1 -0
  86. package/dist/{chunk-2WVX3LEC.js → chunk-ZVP6GB4G.js} +1 -1
  87. package/dist/chunk-ZVP6GB4G.js.map +1 -0
  88. package/dist/cli.js +1 -1
  89. package/dist/cli.js.map +1 -1
  90. package/dist/client-PX25MD47.js +2 -0
  91. package/dist/{compaction-NWEMOSHA.js → compaction-FD4R23HZ.js} +4 -4
  92. package/dist/config-HL5F36V4.js +2 -0
  93. package/dist/config-mutator-HBX53OMJ.js +2 -0
  94. package/dist/{conflict-detector-D7VIYPDI.js → conflict-detector-ZPOT3M2C.js} +2 -2
  95. package/dist/conflict-detector-ZPOT3M2C.js.map +1 -0
  96. package/dist/context-planner-AQHJMVY2.js +17 -0
  97. package/dist/context-planner-AQHJMVY2.js.map +1 -0
  98. package/dist/debug-3MEUO2P2.js +2 -0
  99. package/dist/{discover-XJXQXXIO.js → discover-D4VVRNAI.js} +2 -2
  100. package/dist/discover-D4VVRNAI.js.map +1 -0
  101. package/dist/doctor-PBAH6XL3.js +2 -0
  102. package/dist/editor-chat-OB7IQOB5.js +137 -0
  103. package/dist/editor-chat-OB7IQOB5.js.map +1 -0
  104. package/dist/entry-triage-LK34TSQD.js +2 -0
  105. package/dist/entry-triage-LK34TSQD.js.map +1 -0
  106. package/dist/facts-YVEJOM7O.js +22 -0
  107. package/dist/facts-YVEJOM7O.js.map +1 -0
  108. package/dist/heal-FVLBHYP7.js +2 -0
  109. package/dist/index.d.ts +1073 -65
  110. package/dist/index.js +13 -13
  111. package/dist/index.js.map +1 -1
  112. package/dist/{ingest-whatsapp-LZE62INA.js → ingest-whatsapp-NS4OWDOU.js} +2 -2
  113. package/dist/loader-4ZGTK7LD.js +2 -0
  114. package/dist/loader-BOKE5M5S.js +2 -0
  115. package/dist/memory-extract-F5ZHWY5E.js +2 -0
  116. package/dist/{migrate-v2-QO7SLKIM.js → migrate-v2-V4WN2VVM.js} +2 -2
  117. package/dist/ntfy-X7EPB3TG.js +4 -0
  118. package/dist/ntfy-X7EPB3TG.js.map +1 -0
  119. package/dist/processes-WU23U2OJ.js +2 -0
  120. package/dist/program-YGXVCV2Y.js +2 -0
  121. package/dist/{projects-api-KRME2YKZ.js → projects-api-QE6L65SK.js} +2 -2
  122. package/dist/{projects-mutate-3NHVG437.js → projects-mutate-MY7JTLSZ.js} +2 -2
  123. package/dist/providers-KO2DODIO.js +2 -0
  124. package/dist/query-ZKTFJMNE.js +2 -0
  125. package/dist/questions-4B4KSPXG.js +3 -0
  126. package/dist/questions-4B4KSPXG.js.map +1 -0
  127. package/dist/recipes-7S4MC5OJ.js +2 -0
  128. package/dist/registry-PSZDPFZK.js +2 -0
  129. package/dist/review-candidates-AEUXRQQC.js +4 -0
  130. package/dist/review-candidates-AEUXRQQC.js.map +1 -0
  131. package/dist/review-store-B4YE3EFK.js +4 -0
  132. package/dist/review-store-B4YE3EFK.js.map +1 -0
  133. package/dist/{rotation-memo-B3EXXSEH.js → rotation-memo-7T75BLEX.js} +2 -2
  134. package/dist/routing-4PV66BK6.js +2 -0
  135. package/dist/routing-4PV66BK6.js.map +1 -0
  136. package/dist/runner-YRBVNYTY.js +2 -0
  137. package/dist/scheduler-GZZAN6ZG.js +2 -0
  138. package/dist/seat-VZ7AVO27.js +2 -0
  139. package/dist/select-F5I5PO2O.js +2 -0
  140. package/dist/select-F5I5PO2O.js.map +1 -0
  141. package/dist/store-MCOA6N4F.js +2 -0
  142. package/dist/{subagent-WV7QKQ3L.js → subagent-2SJHSSJS.js} +2 -2
  143. package/dist/{surface-usage-PRL6Z4U3.js → surface-usage-NS74AXSE.js} +2 -2
  144. package/dist/timers-PCB4ZIBD.js +2 -0
  145. package/dist/triage-backtest-JNO7RKX7.js +2 -0
  146. package/dist/triage-backtest-JNO7RKX7.js.map +1 -0
  147. package/dist/types-H5ENIUIT.js +2 -0
  148. package/dist/types-OIFU3KSQ.js +2 -0
  149. package/dist/web/workflow-editor.global.js +1542 -0
  150. package/dist/web/workflow-editor.global.js.map +1 -0
  151. package/dist/{whatsapp-state-XMTCSRXU.js → whatsapp-state-6D6FHHD2.js} +2 -2
  152. package/dist/wiki-N5SMP3DT.js +2 -0
  153. package/dist/wiki-N5SMP3DT.js.map +1 -0
  154. package/dist/workflows/templates/branching.yaml +1 -1
  155. package/dist/{workspace-setup-TBQAHYHJ.js → workspace-setup-B3F3TUDE.js} +2 -2
  156. package/dist/workspace-setup-B3F3TUDE.js.map +1 -0
  157. package/package.json +1 -1
  158. package/dist/agent-QGZIAZMY.js +0 -2
  159. package/dist/architect-SEPWMWS4.js +0 -2
  160. package/dist/board-dashboard-CQ5F4GPD.js.map +0 -1
  161. package/dist/chunk-2WVX3LEC.js.map +0 -1
  162. package/dist/chunk-3WQFCIB6.js +0 -24
  163. package/dist/chunk-3WQFCIB6.js.map +0 -1
  164. package/dist/chunk-4YCH6IZV.js +0 -2
  165. package/dist/chunk-53GCFZPR.js +0 -116
  166. package/dist/chunk-53GCFZPR.js.map +0 -1
  167. package/dist/chunk-53NRX2NA.js +0 -2
  168. package/dist/chunk-53NRX2NA.js.map +0 -1
  169. package/dist/chunk-5U35WAZF.js.map +0 -1
  170. package/dist/chunk-BFXONUUN.js +0 -108
  171. package/dist/chunk-BFXONUUN.js.map +0 -1
  172. package/dist/chunk-BFZUG66X.js +0 -45
  173. package/dist/chunk-BFZUG66X.js.map +0 -1
  174. package/dist/chunk-DJEZ2HVU.js.map +0 -1
  175. package/dist/chunk-FDMWOPEL.js +0 -5
  176. package/dist/chunk-FDWOECJY.js +0 -54
  177. package/dist/chunk-FDWOECJY.js.map +0 -1
  178. package/dist/chunk-G57RZCXM.js +0 -5
  179. package/dist/chunk-H5D7ATXS.js.map +0 -1
  180. package/dist/chunk-HA3XI7Z2.js +0 -155
  181. package/dist/chunk-HA3XI7Z2.js.map +0 -1
  182. package/dist/chunk-HR4JSRZS.js +0 -3
  183. package/dist/chunk-HR4JSRZS.js.map +0 -1
  184. package/dist/chunk-ISEBLHWX.js +0 -118
  185. package/dist/chunk-ISEBLHWX.js.map +0 -1
  186. package/dist/chunk-JP5IEXR7.js +0 -32
  187. package/dist/chunk-JTJIPNXY.js.map +0 -1
  188. package/dist/chunk-JXWAGMI2.js +0 -13
  189. package/dist/chunk-JXWAGMI2.js.map +0 -1
  190. package/dist/chunk-KUCCW2VG.js +0 -431
  191. package/dist/chunk-KUCCW2VG.js.map +0 -1
  192. package/dist/chunk-LDHPMJ7M.js.map +0 -1
  193. package/dist/chunk-LNXCJWOU.js.map +0 -1
  194. package/dist/chunk-NGSIRE4X.js +0 -126
  195. package/dist/chunk-NGSIRE4X.js.map +0 -1
  196. package/dist/chunk-P4VWIVKN.js.map +0 -1
  197. package/dist/chunk-Q6YO7LYE.js +0 -47
  198. package/dist/chunk-Q6YO7LYE.js.map +0 -1
  199. package/dist/chunk-SR3X25TN.js +0 -3
  200. package/dist/chunk-SR3X25TN.js.map +0 -1
  201. package/dist/chunk-TMKLHTCG.js +0 -971
  202. package/dist/chunk-TMKLHTCG.js.map +0 -1
  203. package/dist/chunk-VJUTQAPJ.js +0 -2
  204. package/dist/chunk-VJUTQAPJ.js.map +0 -1
  205. package/dist/client-GGVVLOOF.js +0 -2
  206. package/dist/config-7FSZFRTQ.js +0 -2
  207. package/dist/config-mutator-POCOYYJ4.js +0 -2
  208. package/dist/conflict-detector-D7VIYPDI.js.map +0 -1
  209. package/dist/context-planner-45GEWKGL.js +0 -17
  210. package/dist/context-planner-45GEWKGL.js.map +0 -1
  211. package/dist/debug-N3B5NVJU.js +0 -2
  212. package/dist/discover-XJXQXXIO.js.map +0 -1
  213. package/dist/doctor-DHLBEKA4.js +0 -2
  214. package/dist/heal-HZ3EQD52.js +0 -2
  215. package/dist/loader-5CA3O5GM.js +0 -2
  216. package/dist/loader-6IHUCPVO.js +0 -2
  217. package/dist/memory-extract-AENFVVO5.js +0 -2
  218. package/dist/processes-UIC4XXA6.js +0 -2
  219. package/dist/program-WVAVJFND.js +0 -2
  220. package/dist/providers-L27BJ5A6.js +0 -2
  221. package/dist/query-HZYYRCLU.js +0 -2
  222. package/dist/recipes-EPLE2SNT.js +0 -2
  223. package/dist/registry-T36ZIAWV.js +0 -2
  224. package/dist/runner-BFYDWPUO.js +0 -2
  225. package/dist/scheduler-GH7TIKYS.js +0 -2
  226. package/dist/store-I3HEWVLQ.js +0 -2
  227. package/dist/timers-5LPZN55J.js +0 -2
  228. package/dist/types-VECTMYTO.js +0 -2
  229. package/dist/types-Y6ZCZ52Z.js +0 -2
  230. package/dist/wiki-M6YNRFC4.js +0 -2
  231. /package/dist/{App-6Y7B3YQH.js.map → App-U52IPUK2.js.map} +0 -0
  232. /package/dist/{ChatApp-KIEUJ2DS.js.map → ChatApp-XP4OMHPL.js.map} +0 -0
  233. /package/dist/{agent-QGZIAZMY.js.map → agent-V2CA4TXD.js.map} +0 -0
  234. /package/dist/{architect-SEPWMWS4.js.map → article-fields-L4D2BOGY.js.map} +0 -0
  235. /package/dist/{audit-JU3HWISB.js.map → audit-4245CX6P.js.map} +0 -0
  236. /package/dist/{builtin-FRKAQMFS.js.map → builtin-B67DMJ3M.js.map} +0 -0
  237. /package/dist/{chunk-4YCH6IZV.js.map → calibration-ILS5OI44.js.map} +0 -0
  238. /package/dist/{client-GGVVLOOF.js.map → chunk-FIOB3MEX.js.map} +0 -0
  239. /package/dist/{config-7FSZFRTQ.js.map → client-PX25MD47.js.map} +0 -0
  240. /package/dist/{compaction-NWEMOSHA.js.map → compaction-FD4R23HZ.js.map} +0 -0
  241. /package/dist/{config-mutator-POCOYYJ4.js.map → config-HL5F36V4.js.map} +0 -0
  242. /package/dist/{debug-N3B5NVJU.js.map → config-mutator-HBX53OMJ.js.map} +0 -0
  243. /package/dist/{doctor-DHLBEKA4.js.map → debug-3MEUO2P2.js.map} +0 -0
  244. /package/dist/{heal-HZ3EQD52.js.map → doctor-PBAH6XL3.js.map} +0 -0
  245. /package/dist/{loader-5CA3O5GM.js.map → heal-FVLBHYP7.js.map} +0 -0
  246. /package/dist/{ingest-whatsapp-LZE62INA.js.map → ingest-whatsapp-NS4OWDOU.js.map} +0 -0
  247. /package/dist/{loader-6IHUCPVO.js.map → loader-4ZGTK7LD.js.map} +0 -0
  248. /package/dist/{memory-extract-AENFVVO5.js.map → loader-BOKE5M5S.js.map} +0 -0
  249. /package/dist/{processes-UIC4XXA6.js.map → memory-extract-F5ZHWY5E.js.map} +0 -0
  250. /package/dist/{migrate-v2-QO7SLKIM.js.map → migrate-v2-V4WN2VVM.js.map} +0 -0
  251. /package/dist/{program-WVAVJFND.js.map → processes-WU23U2OJ.js.map} +0 -0
  252. /package/dist/{providers-L27BJ5A6.js.map → program-YGXVCV2Y.js.map} +0 -0
  253. /package/dist/{projects-api-KRME2YKZ.js.map → projects-api-QE6L65SK.js.map} +0 -0
  254. /package/dist/{projects-mutate-3NHVG437.js.map → projects-mutate-MY7JTLSZ.js.map} +0 -0
  255. /package/dist/{query-HZYYRCLU.js.map → providers-KO2DODIO.js.map} +0 -0
  256. /package/dist/{recipes-EPLE2SNT.js.map → query-ZKTFJMNE.js.map} +0 -0
  257. /package/dist/{registry-T36ZIAWV.js.map → recipes-7S4MC5OJ.js.map} +0 -0
  258. /package/dist/{runner-BFYDWPUO.js.map → registry-PSZDPFZK.js.map} +0 -0
  259. /package/dist/{rotation-memo-B3EXXSEH.js.map → rotation-memo-7T75BLEX.js.map} +0 -0
  260. /package/dist/{scheduler-GH7TIKYS.js.map → runner-YRBVNYTY.js.map} +0 -0
  261. /package/dist/{store-I3HEWVLQ.js.map → scheduler-GZZAN6ZG.js.map} +0 -0
  262. /package/dist/{surface-usage-PRL6Z4U3.js.map → seat-VZ7AVO27.js.map} +0 -0
  263. /package/dist/{timers-5LPZN55J.js.map → store-MCOA6N4F.js.map} +0 -0
  264. /package/dist/{subagent-WV7QKQ3L.js.map → subagent-2SJHSSJS.js.map} +0 -0
  265. /package/dist/{types-VECTMYTO.js.map → surface-usage-NS74AXSE.js.map} +0 -0
  266. /package/dist/{types-Y6ZCZ52Z.js.map → timers-PCB4ZIBD.js.map} +0 -0
  267. /package/dist/{wiki-M6YNRFC4.js.map → types-H5ENIUIT.js.map} +0 -0
  268. /package/dist/{workspace-setup-TBQAHYHJ.js.map → types-OIFU3KSQ.js.map} +0 -0
  269. /package/dist/{whatsapp-state-XMTCSRXU.js.map → whatsapp-state-6D6FHHD2.js.map} +0 -0
@@ -1,6 +1,6 @@
1
- import"./chunk-4YCH6IZV.js";import{createHash as C}from"crypto";function $(e,n){if(!e.enabled)return[];let t=n.listContacts(),d=n.listChats(),i=[],a=r=>r.replace(/^\+/,"").toLowerCase(),s=e.allowContacts.map(a),o=e.allowGroups.map(a),c=e.denyContacts.map(a),u=e.denyGroups.map(a),m=(r,g)=>{if(g.length===0)return!1;let f=a(r);return g.some(p=>f.includes(p)||p.includes(f))};for(let r of t){let g=`${r.phone}|${r.jid}`;if(m(g,c)||!m(g,s))continue;let f=d.find(p=>!p.isGroup&&p.jid===r.jid);i.push({kind:"contact",jid:r.jid,contact:r,chat:f})}for(let r of d)r.isGroup&&(m(r.jid,u)||m(r.jid,o)&&i.push({kind:"group",jid:r.jid,chat:r}));return i}var h=(e=new Date)=>e.toISOString().slice(0,10),l=(e=new Date)=>h(e).replace(/-/g,"");function w(e){return C("sha1").update(e).digest("hex").slice(0,10)}function R(e,n,t=new Date){let d=h(t),i=`wa-contact-${w(e.jid)}-${l(t)}`,s=[`# Contact: ${e.savedName||e.pushName||e.phone||e.jid}`,""];return s.push(`- **JID**: ${e.jid}`),e.phone&&s.push(`- **Phone**: +${e.phone}`),e.savedName&&s.push(`- **Saved name**: ${e.savedName}`),e.pushName&&e.pushName!==e.savedName&&s.push(`- **Push name**: ${e.pushName}`),e.status&&s.push(`- **Status**: ${e.status}`),e.updatedAt&&s.push(`- **Last observed**: ${e.updatedAt}`),{id:i,date:d,agentId:n,source:"whatsapp:contact",sourceContext:e.jid,content:s.join(`
1
+ import"./chunk-FIOB3MEX.js";import{createHash as C}from"crypto";function $(e,n){if(!e.enabled)return[];let t=n.listContacts(),d=n.listChats(),i=[],a=r=>r.replace(/^\+/,"").toLowerCase(),s=e.allowContacts.map(a),o=e.allowGroups.map(a),c=e.denyContacts.map(a),u=e.denyGroups.map(a),m=(r,g)=>{if(g.length===0)return!1;let f=a(r);return g.some(p=>f.includes(p)||p.includes(f))};for(let r of t){let g=`${r.phone}|${r.jid}`;if(m(g,c)||!m(g,s))continue;let f=d.find(p=>!p.isGroup&&p.jid===r.jid);i.push({kind:"contact",jid:r.jid,contact:r,chat:f})}for(let r of d)r.isGroup&&(m(r.jid,u)||m(r.jid,o)&&i.push({kind:"group",jid:r.jid,chat:r}));return i}var h=(e=new Date)=>e.toISOString().slice(0,10),l=(e=new Date)=>h(e).replace(/-/g,"");function w(e){return C("sha1").update(e).digest("hex").slice(0,10)}function R(e,n,t=new Date){let d=h(t),i=`wa-contact-${w(e.jid)}-${l(t)}`,s=[`# Contact: ${e.savedName||e.pushName||e.phone||e.jid}`,""];return s.push(`- **JID**: ${e.jid}`),e.phone&&s.push(`- **Phone**: +${e.phone}`),e.savedName&&s.push(`- **Saved name**: ${e.savedName}`),e.pushName&&e.pushName!==e.savedName&&s.push(`- **Push name**: ${e.pushName}`),e.status&&s.push(`- **Status**: ${e.status}`),e.updatedAt&&s.push(`- **Last observed**: ${e.updatedAt}`),{id:i,date:d,agentId:n,source:"whatsapp:contact",sourceContext:e.jid,content:s.join(`
2
2
  `),meta:{jid:e.jid,phone:e.phone,kind:"contact",profileHash:x(e)}}}function k(e,n,t=new Date){let d=h(t),i=`wa-group-meta-${w(e.jid)}-${l(t)}`,a=[`# Group: ${e.subject||"(unnamed)"}`,""];a.push(`- **JID**: ${e.jid}`),e.description&&a.push(`- **Description**: ${e.description}`),e.owner&&a.push(`- **Owner**: ${e.owner}`),a.push(`- **Members (${e.memberCount})**:`);let s=e.members.slice(0,50);for(let o of s){let c=o.admin?` (${o.admin})`:"";a.push(` - ${o.jid}${c}`)}return e.members.length>s.length&&a.push(` - \u2026and ${e.members.length-s.length} more`),{id:i,date:d,agentId:n,source:"whatsapp:group-meta",sourceContext:e.jid,content:a.join(`
3
3
  `),meta:{jid:e.jid,subject:e.subject,memberCount:e.memberCount,kind:"group-meta"}}}function D(e,n,t,d,i=new Date){if(t.length===0)return null;let a=t[t.length-1].id,s=h(i),o=`wa-dm-${w(e.jid)}-${l(i)}-${b(a)}`,c=n?.savedName||n?.pushName||e.name||e.jid,u=[`# Chat with ${c}`,""];u.push(`- **JID**: ${e.jid}`),n?.phone&&u.push(`- **Phone**: +${n.phone}`),u.push(`- **Messages**: ${t.length}`),u.push("");for(let m of t)u.push(y(m,c));return{id:o,date:s,agentId:d,source:"whatsapp:dm",sourceContext:e.jid,content:u.join(`
4
4
  `),meta:{jid:e.jid,kind:"dm",messageCount:t.length,oldestMsgTs:t[0]?.timestamp,newestMsgTs:t[t.length-1]?.timestamp,lastMsgId:a}}}function M(e,n,t,d,i=new Date){if(t.length===0)return null;let a=t[t.length-1].id,s=h(i),o=`wa-group-${w(e.jid)}-${l(i)}-${b(a)}`,c=[`# Group chat: ${n.subject||e.name}`,""];c.push(`- **JID**: ${e.jid}`),c.push(`- **Members**: ${n.memberCount}`),c.push(`- **Messages**: ${t.length}`),c.push("");for(let u of t)c.push(y(u,u.fromJid));return{id:o,date:s,agentId:d,source:"whatsapp:group",sourceContext:e.jid,content:c.join(`
5
5
  `),meta:{jid:e.jid,kind:"group",messageCount:t.length,lastMsgId:a}}}function y(e,n){let t=e.timestamp?G(e.timestamp):"",d=e.fromMe?"me":n,i=e.text;return!i&&e.media&&(e.media.kind==="image"?i=`[image${e.media.caption?`: ${e.media.caption}`:""}]`:e.media.kind==="audio"?i="[voice message]":e.media.kind==="video"?i=`[video${e.media.caption?`: ${e.media.caption}`:""}]`:e.media.kind==="document"?i=`[document: ${e.media.filename??"unnamed"}]`:e.media.kind==="sticker"&&(i="[sticker]")),`[${t}] ${d}: ${i}`}function G(e){try{return new Date(e*1e3).toISOString().replace("T"," ").slice(0,16)}catch{return String(e)}}function b(e){return e.replace(/[^A-Za-z0-9]/g,"").slice(-10)}function x(e){let n=C("sha1");return n.update(e.jid),n.update("|"),n.update(e.savedName||""),n.update("|"),n.update(e.pushName||""),n.update("|"),n.update(e.status||""),n.digest("hex").slice(0,10)}async function I(e){let n=e.now??new Date,t={scannedContacts:0,scannedGroups:0,wroteContacts:0,wroteGroups:0,wroteDmWindows:0,wroteGroupWindows:0,skippedUnchanged:0,errors:[]},d=[],i=s=>{if(e.dryRun){d.push(s);return}e.store.addEntry(s)};if(!e.source.isConnected()&&!e.dryRun)return t.errors.push({jid:"-",kind:"source",message:"WhatsApp socket not connected"}),{...t,dryRunEntries:e.dryRun?d:void 0};let a=$(e.config,e.source);a.length>e.config.throttle.maxChatsPerSweep&&(a=a.slice(0,e.config.throttle.maxChatsPerSweep));for(let s of a)try{if(s.kind==="contact"){t.scannedContacts++;let o=s.contact??await e.source.getContactProfile(s.jid);if(!o)continue;let c=R(o,e.agentId,n);if(i(c),t.wroteContacts++,e.config.mode==="messages"){let u=await e.source.getHistory(s.jid,{limit:e.config.messageCap}),m=j(u,e.config.historyDays,n);if(m.length>0&&s.chat){let r=D(s.chat,o,m,e.agentId,n);r&&(i(r),t.wroteDmWindows++)}}}else{t.scannedGroups++;let o=await e.source.getGroupMetadata(s.jid);if(!o)continue;let c=k(o,e.agentId,n);if(i(c),t.wroteGroups++,e.config.mode==="messages"&&s.chat){let u=await e.source.getHistory(s.jid,{limit:e.config.messageCap}),m=j(u,e.config.historyDays,n);if(m.length>0){let r=M(s.chat,o,m,e.agentId,n);r&&(i(r),t.wroteGroupWindows++)}}}}catch(o){t.errors.push({jid:s.jid,kind:s.kind,message:o?.message??String(o)})}return{...t,dryRunEntries:e.dryRun?d:void 0}}function j(e,n,t){let d=Math.floor((t.getTime()-n*864e5)/1e3);return e.filter(i=>!i.timestamp||i.timestamp>=d)}export{x as hashProfile,$ as resolveScope,I as runSweep,R as transformContact,D as transformDm,M as transformGroupMessages,k as transformGroupMeta};
6
- //# sourceMappingURL=ingest-whatsapp-LZE62INA.js.map
6
+ //# sourceMappingURL=ingest-whatsapp-NS4OWDOU.js.map
@@ -0,0 +1,2 @@
1
+ import{a,b}from"./chunk-WPMX3VGI.js";import"./chunk-L2WSPY6S.js";import"./chunk-FIOB3MEX.js";export{a as loadReferences,b as renderReferences};
2
+ //# sourceMappingURL=loader-4ZGTK7LD.js.map
@@ -0,0 +1,2 @@
1
+ import{a,b,c,d,e}from"./chunk-VDMYBPYB.js";import"./chunk-FIOB3MEX.js";export{e as getAutoInjectSkills,a as loadLocalSkills,d as matchSkillsToTask,c as parseSkillContent,b as parseSkillFile};
2
+ //# sourceMappingURL=loader-BOKE5M5S.js.map
@@ -0,0 +1,2 @@
1
+ import{a}from"./chunk-77OV7346.js";import"./chunk-FIOB3MEX.js";export{a as extractMemories};
2
+ //# sourceMappingURL=memory-extract-F5ZHWY5E.js.map
@@ -1,4 +1,4 @@
1
- import{b as g,d as l}from"./chunk-JTJIPNXY.js";import"./chunk-4YCH6IZV.js";import{readFileSync as m,writeFileSync as y,existsSync as v,copyFileSync as f}from"fs";import{join as _}from"path";var w=new Set(["code","ops","support","admin","knowledge","social","system"]),x=[{test:/\b(review[-_.]?(merge[-_.]?request|mr|pull[-_.]?request|pr))\b/i,category:"code",verb:"review.merge-request"},{test:/\breview[-_.]?(code|change|diff|snippet)\b/i,category:"code",verb:"review.code-change"},{test:/\b(fix|patch)[-_.]/i,category:"code",verb:"fix.bug"},{test:/\b(implement|build|add)[-_.]?(feature|cli|api|endpoint)\b/i,category:"code",verb:"implement.feature"},{test:/\bimplement[-_.]/i,category:"code",verb:"implement.feature"},{test:/\b(debug|investigate)[-_.]/i,category:"code",verb:"investigate.error"},{test:/\brefactor\b/i,category:"code",verb:"refactor.code"},{test:/\b(plan|spec|rfc|design)[-_.]/i,category:"code",verb:"spec.feature"},{test:/\b(handle|triage)[-_.]?(github|gitlab)?[-_.]?(issue|push|pr|mr|webhook|comment)\b/i,category:"code",verb:"triage.issue"},{test:/\btriage[-_.]?(issue|ticket)?\b/i,category:"code",verb:"triage.issue"},{test:/\banswer[-_.]?(code[-_.]?question|technical[-_.]?question)\b/i,category:"code",verb:"answer.code-question"},{test:/\b(commit|push|checkout|sync|merge)[-_.]/i,category:"code",verb:"refactor.code"},{test:/\bauthor[-_.]?(workflow|spec|rfc)\b/i,category:"code",verb:"spec.feature"},{test:/\bdeploy[-_.]?(staging|preview|qa|test)\b/i,category:"ops",verb:"deploy.staging"},{test:/\bdeploy[-_.]?(prod|production|live|release)\b/i,category:"ops",verb:"deploy.production"},{test:/\bdeploy[-_.]/i,category:"ops",verb:"deploy.staging"},{test:/\brollback\b/i,category:"ops",verb:"rollback.release"},{test:/\binvestigate[-_.]?incident\b/i,category:"ops",verb:"investigate.incident"},{test:/\b(monitor|status|health[-_.]?check|metrics)\b/i,category:"ops",verb:"monitor.system"},{test:/\b(restart|stop|start)[-_.]?(daemon|service|server)\b/i,category:"ops",verb:"monitor.system"},{test:/\brun[-_.]?(server|infra)?[-_.]?(security|audit)\b/i,category:"ops",verb:"audit.security"},{test:/\brotate[-_.]?(token|tokens|credential|credentials|key|cert|secret)s?\b/i,category:"ops",verb:"rotate.credential"},{test:/\baudit\b/i,category:"ops",verb:"audit.security"},{test:/\bsetup[-_.]/i,category:"ops",verb:"monitor.system"},{test:/\b(greet(ing)?|hello|hi[-_.]?there)\b/i,category:"support",verb:"chat.greeting"},{test:/\b(support[-_.]?request|help[-_.]?request|customer[-_.]?question)\b/i,category:"support",verb:"chat.support-request"},{test:/\b(casual|chitchat|smalltalk)\b/i,category:"support",verb:"chat.casual"},{test:/\b(query|ask|clarify|confirm)[-_.]/i,category:"support",verb:"answer.question"},{test:/\b(reply|respond)[-_.]/i,category:"support",verb:"route.request"},{test:/\banswer[-_.]?question\b/i,category:"support",verb:"answer.question"},{test:/\broute[-_.]?(request|message|to[-_.]?agent)\b/i,category:"support",verb:"route.request"},{test:/\bconfig[-_.]?(change|update|edit)?\b/i,category:"admin",verb:"config.change"},{test:/\bconfigure[-_.]/i,category:"admin",verb:"config.change"},{test:/\b(schedule|cron)[-_.]?(add|create|new)\b/i,category:"admin",verb:"schedule.add"},{test:/\b(reminder|remind)\b/i,category:"admin",verb:"schedule.add"},{test:/\b(schedule|cron)[-_.]?(remove|delete|disable)\b/i,category:"admin",verb:"schedule.remove"},{test:/\b(provide|assign|copy)[-_.]?(token|tokens|credential|credentials|cert|secret)s?\b/i,category:"admin",verb:"token.create"},{test:/\b(token[-_.]?(create|mint|issue)|mint[-_.]?token)\b/i,category:"admin",verb:"token.create"},{test:/\bagent[-_.]?(add|onboard|new|configure)\b/i,category:"admin",verb:"agent.add"},{test:/\bchannel[-_.]?(add|wire|new|configure)\b/i,category:"admin",verb:"channel.add"},{test:/\bauthorize[-_.]/i,category:"admin",verb:"config.change"},{test:/\bcorrect[-_.]/i,category:"admin",verb:"config.change"},{test:/\b(document|docs|readme|wiki[-_.]?write)\b/i,category:"knowledge",verb:"document.feature"},{test:/\bsummariz?e\b/i,category:"knowledge",verb:"summarize.thread"},{test:/\b(research|analyze|compare|investigate[-_.]?repo)\b/i,category:"knowledge",verb:"research.topic"},{test:/\bwiki[-_.]?(absorb|ingest|import|run)\b/i,category:"knowledge",verb:"wiki.absorb"},{test:/\bcompile[-_.]?(brief|report|digest)\b/i,category:"knowledge",verb:"summarize.thread"},{test:/\b(daily[-_.]?brief|brief[-_.]?daily|morning[-_.]?digest)\b/i,category:"social",verb:"brief.daily"},{test:/\b(draft[-_.]?(social|post|tweet|announcement|blog|article)|social[-_.]?post)\b/i,category:"social",verb:"draft.post"},{test:/\bcreate[-_.]?(blog|article|post)\b/i,category:"social",verb:"draft.post"},{test:/\bcompile[-_.]?(marketing|social)\b/i,category:"social",verb:"brief.daily"},{test:/\b(weekly[-_.]?report|report[-_.]?weekly|status[-_.]?report)\b/i,category:"social",verb:"report.weekly"},{test:/\b(classify[-_.]?intent|graph[-_.]?classify)\b/i,category:"system",verb:"classify.intent"}];function q(i,n={}){let c=n.log??console.error.bind(console,"[graph-migrate]"),s=i.baseDir,e={indexRemapped:0,indexSkippedV2:0,indexUnmapped:0,jsonlRemapped:0,jsonlSkippedV2:0,jsonlUnmapped:0,backups:[],patternHits:{}},b=_(s,"index.json");if(v(b)){let u=m(b,"utf-8"),d=l.parse(JSON.parse(u)),r={version:d.version,entries:{}};for(let[t,o]of Object.entries(d.entries)){let a=h(o.path,e.patternHits);if(a==="v2"){r.entries[t]=o,e.indexSkippedV2++;continue}if(a===null){r.entries[t]=o,e.indexUnmapped++;continue}r.entries[t]={...o,path:a,updatedAt:new Date().toISOString()},e.indexRemapped++}if(!n.dryRun){let t=`${b}.v1.bak.${Date.now()}.json`;f(b,t),e.backups.push(t),y(b,JSON.stringify(r,null,2),"utf-8")}}let p=_(s,"classifications.jsonl");if(v(p)){let d=m(p,"utf-8").split(`
1
+ import{b as g,d as l}from"./chunk-PNWNVOEZ.js";import"./chunk-FIOB3MEX.js";import{readFileSync as m,writeFileSync as y,existsSync as v,copyFileSync as f}from"fs";import{join as _}from"path";var w=new Set(["code","ops","support","admin","knowledge","social","system"]),x=[{test:/\b(review[-_.]?(merge[-_.]?request|mr|pull[-_.]?request|pr))\b/i,category:"code",verb:"review.merge-request"},{test:/\breview[-_.]?(code|change|diff|snippet)\b/i,category:"code",verb:"review.code-change"},{test:/\b(fix|patch)[-_.]/i,category:"code",verb:"fix.bug"},{test:/\b(implement|build|add)[-_.]?(feature|cli|api|endpoint)\b/i,category:"code",verb:"implement.feature"},{test:/\bimplement[-_.]/i,category:"code",verb:"implement.feature"},{test:/\b(debug|investigate)[-_.]/i,category:"code",verb:"investigate.error"},{test:/\brefactor\b/i,category:"code",verb:"refactor.code"},{test:/\b(plan|spec|rfc|design)[-_.]/i,category:"code",verb:"spec.feature"},{test:/\b(handle|triage)[-_.]?(github|gitlab)?[-_.]?(issue|push|pr|mr|webhook|comment)\b/i,category:"code",verb:"triage.issue"},{test:/\btriage[-_.]?(issue|ticket)?\b/i,category:"code",verb:"triage.issue"},{test:/\banswer[-_.]?(code[-_.]?question|technical[-_.]?question)\b/i,category:"code",verb:"answer.code-question"},{test:/\b(commit|push|checkout|sync|merge)[-_.]/i,category:"code",verb:"refactor.code"},{test:/\bauthor[-_.]?(workflow|spec|rfc)\b/i,category:"code",verb:"spec.feature"},{test:/\bdeploy[-_.]?(staging|preview|qa|test)\b/i,category:"ops",verb:"deploy.staging"},{test:/\bdeploy[-_.]?(prod|production|live|release)\b/i,category:"ops",verb:"deploy.production"},{test:/\bdeploy[-_.]/i,category:"ops",verb:"deploy.staging"},{test:/\brollback\b/i,category:"ops",verb:"rollback.release"},{test:/\binvestigate[-_.]?incident\b/i,category:"ops",verb:"investigate.incident"},{test:/\b(monitor|status|health[-_.]?check|metrics)\b/i,category:"ops",verb:"monitor.system"},{test:/\b(restart|stop|start)[-_.]?(daemon|service|server)\b/i,category:"ops",verb:"monitor.system"},{test:/\brun[-_.]?(server|infra)?[-_.]?(security|audit)\b/i,category:"ops",verb:"audit.security"},{test:/\brotate[-_.]?(token|tokens|credential|credentials|key|cert|secret)s?\b/i,category:"ops",verb:"rotate.credential"},{test:/\baudit\b/i,category:"ops",verb:"audit.security"},{test:/\bsetup[-_.]/i,category:"ops",verb:"monitor.system"},{test:/\b(greet(ing)?|hello|hi[-_.]?there)\b/i,category:"support",verb:"chat.greeting"},{test:/\b(support[-_.]?request|help[-_.]?request|customer[-_.]?question)\b/i,category:"support",verb:"chat.support-request"},{test:/\b(casual|chitchat|smalltalk)\b/i,category:"support",verb:"chat.casual"},{test:/\b(query|ask|clarify|confirm)[-_.]/i,category:"support",verb:"answer.question"},{test:/\b(reply|respond)[-_.]/i,category:"support",verb:"route.request"},{test:/\banswer[-_.]?question\b/i,category:"support",verb:"answer.question"},{test:/\broute[-_.]?(request|message|to[-_.]?agent)\b/i,category:"support",verb:"route.request"},{test:/\bconfig[-_.]?(change|update|edit)?\b/i,category:"admin",verb:"config.change"},{test:/\bconfigure[-_.]/i,category:"admin",verb:"config.change"},{test:/\b(schedule|cron)[-_.]?(add|create|new)\b/i,category:"admin",verb:"schedule.add"},{test:/\b(reminder|remind)\b/i,category:"admin",verb:"schedule.add"},{test:/\b(schedule|cron)[-_.]?(remove|delete|disable)\b/i,category:"admin",verb:"schedule.remove"},{test:/\b(provide|assign|copy)[-_.]?(token|tokens|credential|credentials|cert|secret)s?\b/i,category:"admin",verb:"token.create"},{test:/\b(token[-_.]?(create|mint|issue)|mint[-_.]?token)\b/i,category:"admin",verb:"token.create"},{test:/\bagent[-_.]?(add|onboard|new|configure)\b/i,category:"admin",verb:"agent.add"},{test:/\bchannel[-_.]?(add|wire|new|configure)\b/i,category:"admin",verb:"channel.add"},{test:/\bauthorize[-_.]/i,category:"admin",verb:"config.change"},{test:/\bcorrect[-_.]/i,category:"admin",verb:"config.change"},{test:/\b(document|docs|readme|wiki[-_.]?write)\b/i,category:"knowledge",verb:"document.feature"},{test:/\bsummariz?e\b/i,category:"knowledge",verb:"summarize.thread"},{test:/\b(research|analyze|compare|investigate[-_.]?repo)\b/i,category:"knowledge",verb:"research.topic"},{test:/\bwiki[-_.]?(absorb|ingest|import|run)\b/i,category:"knowledge",verb:"wiki.absorb"},{test:/\bcompile[-_.]?(brief|report|digest)\b/i,category:"knowledge",verb:"summarize.thread"},{test:/\b(daily[-_.]?brief|brief[-_.]?daily|morning[-_.]?digest)\b/i,category:"social",verb:"brief.daily"},{test:/\b(draft[-_.]?(social|post|tweet|announcement|blog|article)|social[-_.]?post)\b/i,category:"social",verb:"draft.post"},{test:/\bcreate[-_.]?(blog|article|post)\b/i,category:"social",verb:"draft.post"},{test:/\bcompile[-_.]?(marketing|social)\b/i,category:"social",verb:"brief.daily"},{test:/\b(weekly[-_.]?report|report[-_.]?weekly|status[-_.]?report)\b/i,category:"social",verb:"report.weekly"},{test:/\b(classify[-_.]?intent|graph[-_.]?classify)\b/i,category:"system",verb:"classify.intent"}];function q(i,n={}){let c=n.log??console.error.bind(console,"[graph-migrate]"),s=i.baseDir,e={indexRemapped:0,indexSkippedV2:0,indexUnmapped:0,jsonlRemapped:0,jsonlSkippedV2:0,jsonlUnmapped:0,backups:[],patternHits:{}},b=_(s,"index.json");if(v(b)){let u=m(b,"utf-8"),d=l.parse(JSON.parse(u)),r={version:d.version,entries:{}};for(let[t,o]of Object.entries(d.entries)){let a=h(o.path,e.patternHits);if(a==="v2"){r.entries[t]=o,e.indexSkippedV2++;continue}if(a===null){r.entries[t]=o,e.indexUnmapped++;continue}r.entries[t]={...o,path:a,updatedAt:new Date().toISOString()},e.indexRemapped++}if(!n.dryRun){let t=`${b}.v1.bak.${Date.now()}.json`;f(b,t),e.backups.push(t),y(b,JSON.stringify(r,null,2),"utf-8")}}let p=_(s,"classifications.jsonl");if(v(p)){let d=m(p,"utf-8").split(`
2
2
  `),r=[];for(let t of d){if(!t.trim()){r.push(t);continue}let o;try{o=g.parse(JSON.parse(t))}catch{r.push(t);continue}let a=h(o.path,e.patternHits);if(a==="v2"){e.jsonlSkippedV2++,r.push(t);continue}if(a===null){e.jsonlUnmapped++,r.push(t);continue}let k={...o,path:a,proposedAxes:{}};r.push(JSON.stringify(k)),e.jsonlRemapped++}if(!n.dryRun){let t=`${p}.v1.bak.${Date.now()}.jsonl`;f(p,t),e.backups.push(t),y(p,r.join(`
3
3
  `),"utf-8")}}return c(`index: ${e.indexRemapped} remapped, ${e.indexSkippedV2} already v2, ${e.indexUnmapped} unmapped`),c(`jsonl: ${e.jsonlRemapped} remapped, ${e.jsonlSkippedV2} already v2, ${e.jsonlUnmapped} unmapped`),n.dryRun?c("(dry-run \u2014 no files written)"):c(`backups: ${e.backups.join(", ")}`),e}function h(i,n){if(!Array.isArray(i)||i.length===0)return null;if(w.has(i[0]))return"v2";let c=`${i.join(" ")} ${i[i.length-1]??""}`;for(let s of x)if(s.test.test(c))return n[s.verb]=(n[s.verb]??0)+1,[s.category,s.verb];return null}export{q as migrateV2};
4
- //# sourceMappingURL=migrate-v2-QO7SLKIM.js.map
4
+ //# sourceMappingURL=migrate-v2-V4WN2VVM.js.map
@@ -0,0 +1,4 @@
1
+ import"./chunk-FIOB3MEX.js";function l(i){return/^[\x20-\x7E]*$/.test(i)?i:`=?UTF-8?B?${Buffer.from(i,"utf8").toString("base64")}?=`}function d(i){let t=i.split(`
2
+ `),n=(t[0]??"").trim(),e=t.slice(1).join(`
3
+ `).trim();return e?n.length===0||n.length>80?{body:i.trim()}:{title:n,body:e}:{body:i.trim()}}var f=class{name="ntfy";config;log;constructor(t,n=console.error.bind(console,"[ntfy]")){this.config=t,this.log=n}async start(){this.log(`ntfy: ready (${this.serverUrl()}/${this.config.topic})`)}async stop(){}onMessage(t){}serverUrl(){return(this.config.server||"https://ntfy.sh").replace(/\/+$/,"")}async send(t){let n=(t.chatId||"").trim(),e=n&&n!=="default"?n:this.config.topic;if(!e)throw new Error("ntfy: no topic (pass chatId, or set channels.ntfy.topic)");let c=d(t.text||""),g=c.title||this.config.defaultTitle||"AgentX",o={"Content-Type":"text/plain; charset=utf-8",Title:l(g),Priority:String(this.config.defaultPriority??3)};if(this.config.token&&(o.Authorization=`Bearer ${this.config.token}`),t.parseMode==="markdown"&&(o.Markdown="yes"),t.buttons?.length){let s=t.buttons.slice(0,3).map(a=>`view, ${a.label.replace(/[,;]/g," ")}, ${a.url}`).join("; ");o.Actions=l(s)}let r=await fetch(`${this.serverUrl()}/${encodeURIComponent(e)}`,{method:"POST",headers:o,body:c.body||t.text||""});if(!r.ok){let s=await r.text().catch(()=>"");throw new Error(`ntfy: ${r.status} ${s.slice(0,200)}`)}return(await r.json().catch(()=>null))?.id}};export{f as NtfyAdapter};
4
+ //# sourceMappingURL=ntfy-X7EPB3TG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/channels/ntfy.ts"],"sourcesContent":["import type { ChannelAdapter, IncomingMessage, OutgoingMessage } from \"./types\"\n\n// --- ntfy channel — push notifications to the operator's phone ---\n//\n// Outbound only. ntfy has an inbound side (SSE subscribe), but nothing here\n// wants it: a push notification is a one-way tap on the shoulder, and replies\n// come back through the channels that already carry conversation (Telegram,\n// WhatsApp) or through POST /ask when Siri asks a follow-up.\n//\n// Registering it as a ChannelAdapter rather than a bespoke notifier is what\n// makes it free everywhere: notifications.destination, POST /send, cron\n// failure pings, and the agent-facing `channel` action all funnel through\n// router.sendOutbound() and already know how to address a channel + chatId.\n//\n// Addressing: `chatId` is the ntfy topic. The literal `\"default\"` (and an\n// empty string) resolve to the configured topic instead. That indirection\n// exists so agents never have to carry the topic in their prompts — on the\n// public ntfy.sh the topic IS the credential, and every caller that funnels\n// through the router (POST /send, channel.reply) requires a non-empty chatId.\n\nexport interface NtfyConfig {\n /** Base URL of the ntfy server. Default is the public ntfy.sh. */\n server?: string\n /** Default topic when an OutgoingMessage carries no chatId. */\n topic: string\n /** Access token for protected topics (ntfy `Authorization: Bearer`). */\n token?: string\n /** 1 (min) .. 5 (max). ntfy's default is 3. */\n defaultPriority?: number\n /** Notification title. Per-message titles override it. */\n defaultTitle?: string\n}\n\n/**\n * ntfy requires header values to be ASCII. Titles and tags routinely aren't\n * (French accents, Arabic), so non-ASCII values go out as RFC 2047 encoded\n * words, which ntfy decodes. Pure-ASCII values are passed through untouched\n * so the common case stays readable in logs.\n */\nfunction encodeHeader(value: string): string {\n // eslint-disable-next-line no-control-regex\n if (/^[\\x20-\\x7E]*$/.test(value)) return value\n return `=?UTF-8?B?${Buffer.from(value, \"utf8\").toString(\"base64\")}?=`\n}\n\n/** First line of the body, when it reads like a title (short, no trailing\n * punctuation that implies a sentence continues). Lets an agent write a\n * single blob and still get a well-formed notification. */\nfunction splitTitle(text: string): { title?: string; body: string } {\n const lines = text.split(\"\\n\")\n const first = (lines[0] ?? \"\").trim()\n const rest = lines.slice(1).join(\"\\n\").trim()\n if (!rest) return { body: text.trim() }\n if (first.length === 0 || first.length > 80) return { body: text.trim() }\n return { title: first, body: rest }\n}\n\nexport class NtfyAdapter implements ChannelAdapter {\n readonly name = \"ntfy\"\n private config: NtfyConfig\n private log: (...args: unknown[]) => void\n\n constructor(config: NtfyConfig, log: (...args: unknown[]) => void = console.error.bind(console, \"[ntfy]\")) {\n this.config = config\n this.log = log\n }\n\n async start(): Promise<void> {\n this.log(`ntfy: ready (${this.serverUrl()}/${this.config.topic})`)\n }\n\n async stop(): Promise<void> {}\n\n /** No inbound side — the handler is accepted and never invoked, which\n * satisfies router.addChannel() without pretending to poll. */\n onMessage(_handler: (msg: IncomingMessage) => Promise<void>): void {}\n\n private serverUrl(): string {\n return (this.config.server || \"https://ntfy.sh\").replace(/\\/+$/, \"\")\n }\n\n async send(msg: OutgoingMessage): Promise<string | void> {\n const requested = (msg.chatId || \"\").trim()\n const topic = requested && requested !== \"default\" ? requested : this.config.topic\n if (!topic) throw new Error(\"ntfy: no topic (pass chatId, or set channels.ntfy.topic)\")\n\n const split = splitTitle(msg.text || \"\")\n const title = split.title || this.config.defaultTitle || \"AgentX\"\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"text/plain; charset=utf-8\",\n \"Title\": encodeHeader(title),\n \"Priority\": String(this.config.defaultPriority ?? 3),\n }\n if (this.config.token) headers[\"Authorization\"] = `Bearer ${this.config.token}`\n if (msg.parseMode === \"markdown\") headers[\"Markdown\"] = \"yes\"\n\n // URL buttons map onto ntfy's view actions, so an alert can carry\n // \"open the MR\" without the operator hunting for it.\n if (msg.buttons?.length) {\n const actions = msg.buttons\n .slice(0, 3) // ntfy caps at 3\n .map(b => `view, ${b.label.replace(/[,;]/g, \" \")}, ${b.url}`)\n .join(\"; \")\n headers[\"Actions\"] = encodeHeader(actions)\n }\n\n const res = await fetch(`${this.serverUrl()}/${encodeURIComponent(topic)}`, {\n method: \"POST\",\n headers,\n body: split.body || msg.text || \"\",\n })\n\n if (!res.ok) {\n const detail = await res.text().catch(() => \"\")\n throw new Error(`ntfy: ${res.status} ${detail.slice(0, 200)}`)\n }\n\n const json = await res.json().catch(() => null) as { id?: string } | null\n return json?.id\n }\n}\n"],"mappings":"4BAuCA,SAASA,EAAaC,EAAuB,CAE3C,MAAI,iBAAiB,KAAKA,CAAK,EAAUA,EAClC,aAAa,OAAO,KAAKA,EAAO,MAAM,EAAE,SAAS,QAAQ,KAClE,CAKA,SAASC,EAAWC,EAAgD,CAClE,IAAMC,EAAQD,EAAK,MAAM;AAAA,CAAI,EACvBE,GAASD,EAAM,CAAC,GAAK,IAAI,KAAK,EAC9BE,EAAOF,EAAM,MAAM,CAAC,EAAE,KAAK;AAAA,CAAI,EAAE,KAAK,EAC5C,OAAKE,EACDD,EAAM,SAAW,GAAKA,EAAM,OAAS,GAAW,CAAE,KAAMF,EAAK,KAAK,CAAE,EACjE,CAAE,MAAOE,EAAO,KAAMC,CAAK,EAFhB,CAAE,KAAMH,EAAK,KAAK,CAAE,CAGxC,CAEO,IAAMI,EAAN,KAA4C,CACxC,KAAO,OACR,OACA,IAER,YAAYC,EAAoBC,EAAoC,QAAQ,MAAM,KAAK,QAAS,QAAQ,EAAG,CACzG,KAAK,OAASD,EACd,KAAK,IAAMC,CACb,CAEA,MAAM,OAAuB,CAC3B,KAAK,IAAI,gBAAgB,KAAK,UAAU,KAAK,KAAK,OAAO,QAAQ,CACnE,CAEA,MAAM,MAAsB,CAAC,CAI7B,UAAUC,EAAyD,CAAC,CAE5D,WAAoB,CAC1B,OAAQ,KAAK,OAAO,QAAU,mBAAmB,QAAQ,OAAQ,EAAE,CACrE,CAEA,MAAM,KAAKC,EAA8C,CACvD,IAAMC,GAAaD,EAAI,QAAU,IAAI,KAAK,EACpCE,EAAQD,GAAaA,IAAc,UAAYA,EAAY,KAAK,OAAO,MAC7E,GAAI,CAACC,EAAO,MAAM,IAAI,MAAM,0DAA0D,EAEtF,IAAMC,EAAQZ,EAAWS,EAAI,MAAQ,EAAE,EACjCI,EAAQD,EAAM,OAAS,KAAK,OAAO,cAAgB,SAEnDE,EAAkC,CACtC,eAAgB,4BAChB,MAAShB,EAAae,CAAK,EAC3B,SAAY,OAAO,KAAK,OAAO,iBAAmB,CAAC,CACrD,EAMA,GALI,KAAK,OAAO,QAAOC,EAAQ,cAAmB,UAAU,KAAK,OAAO,SACpEL,EAAI,YAAc,aAAYK,EAAQ,SAAc,OAIpDL,EAAI,SAAS,OAAQ,CACvB,IAAMM,EAAUN,EAAI,QACjB,MAAM,EAAG,CAAC,EACV,IAAIO,GAAK,SAASA,EAAE,MAAM,QAAQ,QAAS,GAAG,MAAMA,EAAE,KAAK,EAC3D,KAAK,IAAI,EACZF,EAAQ,QAAahB,EAAaiB,CAAO,EAG3C,IAAME,EAAM,MAAM,MAAM,GAAG,KAAK,UAAU,KAAK,mBAAmBN,CAAK,IAAK,CAC1E,OAAQ,OACR,QAAAG,EACA,KAAMF,EAAM,MAAQH,EAAI,MAAQ,EAClC,CAAC,EAED,GAAI,CAACQ,EAAI,GAAI,CACX,IAAMC,EAAS,MAAMD,EAAI,KAAK,EAAE,MAAM,IAAM,EAAE,EAC9C,MAAM,IAAI,MAAM,SAASA,EAAI,UAAUC,EAAO,MAAM,EAAG,GAAG,GAAG,EAI/D,OADa,MAAMD,EAAI,KAAK,EAAE,MAAM,IAAM,IAAI,IACjC,EACf,CACF","names":["encodeHeader","value","splitTitle","text","lines","first","rest","NtfyAdapter","config","log","_handler","msg","requested","topic","split","title","headers","actions","b","res","detail"]}
@@ -0,0 +1,2 @@
1
+ import{a}from"./chunk-FKBGJYWQ.js";import"./chunk-52ZPELD2.js";import"./chunk-IWMW5432.js";import"./chunk-FIOB3MEX.js";export{a as renderProcessesPage};
2
+ //# sourceMappingURL=processes-WU23U2OJ.js.map
@@ -0,0 +1,2 @@
1
+ import{a}from"./chunk-LNRB5MCU.js";import"./chunk-VTE7L6LO.js";import"./chunk-UFAGYX73.js";import"./chunk-UCQ2N3KO.js";import"./chunk-52ZPELD2.js";import"./chunk-IB3F7VBE.js";import"./chunk-GV7S5BAX.js";import"./chunk-GSX4NK2N.js";import"./chunk-SZDACK6O.js";import"./chunk-WZ72HX5C.js";import"./chunk-F6CNF7V3.js";import"./chunk-DCDQZR7I.js";import"./chunk-IWMW5432.js";import"./chunk-ZVP6GB4G.js";import"./chunk-OPHIXXGI.js";import"./chunk-GDWJUGB5.js";import"./chunk-M4AMWP3H.js";import"./chunk-4BR7D2OU.js";import"./chunk-BPIRO4YD.js";import"./chunk-SFQUP3BP.js";import"./chunk-3SCPRYJB.js";import"./chunk-I3SIXTJO.js";import"./chunk-67Q4DCPS.js";import"./chunk-76Z4VHDX.js";import"./chunk-W4YR2RO2.js";import"./chunk-AFRHKA7B.js";import"./chunk-FIEJDZVC.js";import"./chunk-2T5W3ETS.js";import"./chunk-77OV7346.js";import"./chunk-VWTBHLMX.js";import"./chunk-IBUSGCLQ.js";import"./chunk-PNWNVOEZ.js";import"./chunk-64P4QHSZ.js";import"./chunk-L2IZPR6P.js";import"./chunk-AJ5RMU7X.js";import"./chunk-EVWEG3VD.js";import"./chunk-AX2PWSRT.js";import"./chunk-AJONZWLT.js";import"./chunk-F4HZTZHW.js";import"./chunk-ZTEU5JVQ.js";import"./chunk-QOYAX2FT.js";import"./chunk-WPMX3VGI.js";import"./chunk-K33GIBSE.js";import"./chunk-LCIREYTD.js";import"./chunk-L2WSPY6S.js";import"./chunk-VDMYBPYB.js";import"./chunk-DSYKYZMT.js";import"./chunk-CJQQIRPD.js";import"./chunk-BH3GTCZ6.js";import"./chunk-FIOB3MEX.js";export{a as buildProgram};
2
+ //# sourceMappingURL=program-YGXVCV2Y.js.map
@@ -1,2 +1,2 @@
1
- import{a as k}from"./chunk-2WVX3LEC.js";import"./chunk-4YCH6IZV.js";import{existsSync as v,readFileSync as M}from"fs";import{resolve as K}from"path";function W(i){let m=[],g=new Set,j=new Map;for(let t of i.rules.list())g.add(t.project),j.set(t.project,t);let u=new Map,R=i.workflowStore.list();for(let t of R){if(!t.project)continue;g.add(t.project),u.has(t.project)||u.set(t.project,[]);let n=(t.nodes||[]).find(o=>o?.type?.startsWith("trigger.")),s=n?n.config?.event??n.config?.source??n.type.replace("trigger.",""):void 0;u.get(t.project).push({id:t.id,title:t.title||t.id,status:t.status||"active",trigger:s})}let f=i.config.channels?.gitlab,d=i.config.channels?.github;for(let t of f?.routes??[])t.project&&t.project!=="*"&&g.add(t.project);for(let t of d?.routes??[])t.repo&&t.repo!=="*"&&g.add(t.repo);let w=(f?.agentMappings??[]).map(t=>({agentId:t.agentId,usernames:t.gitlabUsernames??[]})),I=(d?.agentMappings??[]).map(t=>({agentId:t.agentId,usernames:t.githubUsernames??[]})),b=K(i.cwd,".agentx/contacts.json"),h=[];if(v(b))try{let t=JSON.parse(M(b,"utf-8"));h=Array.isArray(t)?t:t.contacts??[]}catch(t){m.push(`failed to parse .agentx/contacts.json: ${t?.message??t}`)}let y=[];for(let t of Array.from(g).sort()){let n=j.get(t),s=[],o=[],P=(f?.routes??[]).filter(e=>e.project===t);P.length>0&&s.push("gitlab");let A=(d?.routes??[]).filter(e=>e.repo===t);A.length>0&&s.push("github"),s.length===0&&n&&s.push("gitlab");let a=new Set;if(n?.agent&&!a.has(n.agent)){a.add(n.agent);let e=w.find(r=>r.agentId===n.agent);o.push({agentId:n.agent,channel:s[0]??"gitlab",via:"rule.agent",gitlabUsername:e?.usernames[0]})}for(let e of P){if(a.has(e.agent))continue;a.add(e.agent);let r=w.find(p=>p.agentId===e.agent);o.push({agentId:e.agent,channel:"gitlab",via:"channels.gitlab.routes",gitlabUsername:r?.usernames[0]})}for(let e of A){let r=e.agent;if(a.has(r))continue;a.add(r);let p=I.find(U=>U.agentId===r);o.push({agentId:r,channel:"github",via:"channels.github.routes",gitlabUsername:p?.usernames[0]})}let S=h.filter(e=>e.project===t).map(e=>({id:e.id,name:e.name??e.id,channels:Object.keys(e.channels??{})})),c=new Map;for(let e of S)c.set(e.id,e);if(n?.contacts)for(let e of n.contacts){if(c.has(e))continue;let r=h.find(p=>p.id===e);r?c.set(e,{id:e,name:r.name??e,channels:Object.keys(r.channels??{})}):c.set(e,{id:e,name:e+" (unknown)",channels:[]})}let l={};n?.gitlab&&(l.gitlab=n.gitlab),n?.github&&(l.github=n.github),y.push({project:t,kind:n?k.inferKind(n):"other",displayName:n?.displayName,homeUrl:n?.homeUrl,channels:s,agents:o,workflows:u.get(t)??[],contacts:Array.from(c.values()),runbook:n?.runbook,rulePath:n?._path,clauses:l.gitlab||l.github?l:void 0})}return{projects:y,warnings:m}}export{W as computeProjectsAggregate};
2
- //# sourceMappingURL=projects-api-KRME2YKZ.js.map
1
+ import{a as k}from"./chunk-ZVP6GB4G.js";import"./chunk-FIOB3MEX.js";import{existsSync as v,readFileSync as M}from"fs";import{resolve as K}from"path";function W(i){let m=[],g=new Set,j=new Map;for(let t of i.rules.list())g.add(t.project),j.set(t.project,t);let u=new Map,R=i.workflowStore.list();for(let t of R){if(!t.project)continue;g.add(t.project),u.has(t.project)||u.set(t.project,[]);let n=(t.nodes||[]).find(o=>o?.type?.startsWith("trigger.")),s=n?n.config?.event??n.config?.source??n.type.replace("trigger.",""):void 0;u.get(t.project).push({id:t.id,title:t.title||t.id,status:t.status||"active",trigger:s})}let f=i.config.channels?.gitlab,d=i.config.channels?.github;for(let t of f?.routes??[])t.project&&t.project!=="*"&&g.add(t.project);for(let t of d?.routes??[])t.repo&&t.repo!=="*"&&g.add(t.repo);let w=(f?.agentMappings??[]).map(t=>({agentId:t.agentId,usernames:t.gitlabUsernames??[]})),I=(d?.agentMappings??[]).map(t=>({agentId:t.agentId,usernames:t.githubUsernames??[]})),b=K(i.cwd,".agentx/contacts.json"),h=[];if(v(b))try{let t=JSON.parse(M(b,"utf-8"));h=Array.isArray(t)?t:t.contacts??[]}catch(t){m.push(`failed to parse .agentx/contacts.json: ${t?.message??t}`)}let y=[];for(let t of Array.from(g).sort()){let n=j.get(t),s=[],o=[],P=(f?.routes??[]).filter(e=>e.project===t);P.length>0&&s.push("gitlab");let A=(d?.routes??[]).filter(e=>e.repo===t);A.length>0&&s.push("github"),s.length===0&&n&&s.push("gitlab");let a=new Set;if(n?.agent&&!a.has(n.agent)){a.add(n.agent);let e=w.find(r=>r.agentId===n.agent);o.push({agentId:n.agent,channel:s[0]??"gitlab",via:"rule.agent",gitlabUsername:e?.usernames[0]})}for(let e of P){if(a.has(e.agent))continue;a.add(e.agent);let r=w.find(p=>p.agentId===e.agent);o.push({agentId:e.agent,channel:"gitlab",via:"channels.gitlab.routes",gitlabUsername:r?.usernames[0]})}for(let e of A){let r=e.agent;if(a.has(r))continue;a.add(r);let p=I.find(U=>U.agentId===r);o.push({agentId:r,channel:"github",via:"channels.github.routes",gitlabUsername:p?.usernames[0]})}let S=h.filter(e=>e.project===t).map(e=>({id:e.id,name:e.name??e.id,channels:Object.keys(e.channels??{})})),c=new Map;for(let e of S)c.set(e.id,e);if(n?.contacts)for(let e of n.contacts){if(c.has(e))continue;let r=h.find(p=>p.id===e);r?c.set(e,{id:e,name:r.name??e,channels:Object.keys(r.channels??{})}):c.set(e,{id:e,name:e+" (unknown)",channels:[]})}let l={};n?.gitlab&&(l.gitlab=n.gitlab),n?.github&&(l.github=n.github),y.push({project:t,kind:n?k.inferKind(n):"other",displayName:n?.displayName,homeUrl:n?.homeUrl,channels:s,agents:o,workflows:u.get(t)??[],contacts:Array.from(c.values()),runbook:n?.runbook,rulePath:n?._path,clauses:l.gitlab||l.github?l:void 0})}return{projects:y,warnings:m}}export{W as computeProjectsAggregate};
2
+ //# sourceMappingURL=projects-api-QE6L65SK.js.map
@@ -1,7 +1,7 @@
1
- import"./chunk-4YCH6IZV.js";import{existsSync as m,mkdirSync as k,readFileSync as g,readdirSync as b,statSync as K,unlinkSync as P,writeFileSync as R,renameSync as x,copyFileSync as j}from"fs";import{dirname as $,extname as S,join as E,resolve as s}from"path";import u from"js-yaml";function I(t,e){return[{path:s(e,`${t}.yaml`),format:"yaml"},{path:s(e,`${t}.yml`),format:"yaml"},{path:s(e,`${t}.json`),format:"json"}].find(n=>m(n.path))??null}function A(t){return`${t}.bak.${Date.now()}`}function O(t,e){let r=t.split(`
1
+ import"./chunk-FIOB3MEX.js";import{existsSync as m,mkdirSync as k,readFileSync as g,readdirSync as b,statSync as K,unlinkSync as P,writeFileSync as R,renameSync as x,copyFileSync as j}from"fs";import{dirname as $,extname as S,join as E,resolve as s}from"path";import u from"js-yaml";function I(t,e){return[{path:s(e,`${t}.yaml`),format:"yaml"},{path:s(e,`${t}.yml`),format:"yaml"},{path:s(e,`${t}.json`),format:"json"}].find(n=>m(n.path))??null}function A(t){return`${t}.bak.${Date.now()}`}function O(t,e){let r=t.split(`
2
2
  `),n=r.findIndex(o=>/^project\s*:/.test(o));if(n>=0){if(e===null){let i=r[n];return r.splice(n,1),{text:r.join(`
3
3
  `),changed:i!==""}}let o=`project: ${e}`;return r[n]===o?{text:t,changed:!1}:(r[n]=o,{text:r.join(`
4
4
  `),changed:!0})}if(e===null)return{text:t,changed:!1};let c=r.findIndex(o=>/^id\s*:/.test(o)),a=`project: ${e}`;return c>=0?r.splice(c+1,0,a):r.unshift(a),{text:r.join(`
5
5
  `),changed:!0}}function M(t,e){let r=JSON.parse(t),n=r.project??null;if(e===null){if(n===null)return{text:t,changed:!1};delete r.project}else{if(n===e)return{text:t,changed:!1};r.project=e}return{text:JSON.stringify(r,null,2)+`
6
6
  `,changed:!0}}function p(t,e){let r=`${t}.tmp.${process.pid}.${Date.now()}`;R(r,e,"utf-8"),x(r,t)}var y=".agentx/projects";function h(t,e){let r=s(e,y);if(!m(r))return null;let n=[r];for(;n.length>0;){let c=n.pop(),a;try{a=b(c)}catch{continue}for(let o of a){let i=E(c,o),l;try{l=K(i)}catch{continue}if(l.isDirectory()){n.push(i);continue}let w=S(o).toLowerCase();if(!(w!==".yaml"&&w!==".yml"))try{if(u.load(g(i,"utf-8"))?.project===t)return i}catch{}}}return null}function N(t,e){let r=s(e,y),n=t.replace(/[^a-zA-Z0-9._/-]/g,"_");return s(r,n+".yaml")}function d(t){let e=h(t.projectKey,t.cwd);if(!e)throw new Error(`project rule for "${t.projectKey}" not found under ${y}`);let r=g(e,"utf-8"),n=u.load(r)??{};if(n.project!==t.projectKey)throw new Error(`rule file at ${e} project=${n.project} does not match requested ${t.projectKey}`);let c=t.mutator(structuredClone(n)),a=f(c),o=u.dump(a,{lineWidth:100,noRefs:!0,sortKeys:!1});if(o===r)return{projectKey:t.projectKey,path:e,backup:"",changed:!1};try{u.load(o)}catch(l){throw new Error(`refusing to write \u2014 YAML parse failed after mutation: ${l?.message??l}`)}let i=`${e}.bak.${Date.now()}`;return j(e,i),p(e,o),{projectKey:t.projectKey,path:e,backup:i,changed:!0}}function f(t){if(t!=null){if(typeof t=="string")return t===""?void 0:t;if(Array.isArray(t)){let e=t.map(f).filter(r=>r!==void 0);return e.length>0?e:void 0}if(typeof t=="object"){let e={},r=0;for(let[n,c]of Object.entries(t)){let a=f(c);a!==void 0&&(e[n]=a,r++)}return r>0?e:void 0}return t}}var C=["displayName","homeUrl","kind","runbook","agent"];function _(t){return d({projectKey:t.projectKey,cwd:t.cwd,mutator:e=>{for(let r of C){if(!(r in t.patch))continue;let n=t.patch[r];if(n===""||n===null)delete e[r];else if(typeof n=="string")if(r==="kind"){let c=["gitlab","github","jira","linear","other"];if(!c.includes(n))throw new Error(`invalid kind="${n}" \u2014 allowed: ${c.join(", ")}`);e.kind=n}else e[r]=n}return e}})}function v(t){return d({projectKey:t.projectKey,cwd:t.cwd,mutator:e=>{let r=Array.isArray(e.contacts)?e.contacts.slice():[];return r.includes(t.contactId)||r.push(t.contactId),e.contacts=r,e}})}function U(t){return d({projectKey:t.projectKey,cwd:t.cwd,mutator:e=>{let r=Array.isArray(e.contacts)?e.contacts.filter(n=>n!==t.contactId):[];return r.length>0?e.contacts=r:delete e.contacts,e}})}function H(t){return d({projectKey:t.projectKey,cwd:t.cwd,mutator:e=>(t.gitlab!==void 0&&(t.gitlab===null?delete e.gitlab:e.gitlab=t.gitlab),t.github!==void 0&&(t.github===null?delete e.github:e.github=t.github),e)})}function J(t){if(!/^[a-zA-Z0-9._-]+(\/[a-zA-Z0-9._-]+)*$/.test(t.projectKey))throw new Error("project key must be alphanumeric with optional / separators");if(h(t.projectKey,t.cwd))throw new Error(`project "${t.projectKey}" already exists`);let e=N(t.projectKey,t.cwd);k($(e),{recursive:!0});let r={project:t.projectKey,kind:t.kind};t.displayName&&(r.displayName=t.displayName),t.homeUrl&&(r.homeUrl=t.homeUrl),t.runbook&&(r.runbook=t.runbook),t.agent&&(r.agent=t.agent);let n=u.dump(r,{lineWidth:100,noRefs:!0,sortKeys:!1});return p(e,n),{path:e}}function z(t){let e=h(t.projectKey,t.cwd);if(!e)throw new Error(`project "${t.projectKey}" not found`);let r=`${e}.bak.${Date.now()}`;return j(e,r),P(e),{path:e,backup:r}}function Y(t){let e=s(t.cwd,".agentx/workflows"),r=I(t.workflowId,e);if(!r)throw new Error(`workflow "${t.workflowId}" not found in ${e}`);let n=g(r.path,"utf-8"),{text:c,changed:a}=r.format==="yaml"?O(n,t.projectKey):M(n,t.projectKey);if(r.format==="yaml")try{u.load(c)}catch(i){throw new Error(`refusing to write \u2014 YAML parse failed after mutation: ${i?.message??i}`)}else try{JSON.parse(c)}catch(i){throw new Error(`refusing to write \u2014 JSON parse failed after mutation: ${i?.message??i}`)}let o="";return a&&(o=A(r.path),j(r.path,o),p(r.path,c)),{workflowId:t.workflowId,path:r.path,format:r.format,backup:o,project:t.projectKey,changed:a}}export{J as createProjectRule,z as deleteProjectRule,v as linkContact,d as mutateProjectRule,_ as patchProjectHeader,H as setProjectClauses,Y as setWorkflowProject,U as unlinkContact};
7
- //# sourceMappingURL=projects-mutate-3NHVG437.js.map
7
+ //# sourceMappingURL=projects-mutate-MY7JTLSZ.js.map
@@ -0,0 +1,2 @@
1
+ import{l as a,m as b,n as c,o as d,p as e}from"./chunk-LCIREYTD.js";import"./chunk-L2WSPY6S.js";import"./chunk-FIOB3MEX.js";e();export{b as ClaudeCodeProvider,a as ClaudeProvider,c as OpenAIProvider,d as createProvider};
2
+ //# sourceMappingURL=providers-KO2DODIO.js.map
@@ -0,0 +1,2 @@
1
+ import{a}from"./chunk-L2IZPR6P.js";import"./chunk-AJ5RMU7X.js";import"./chunk-EVWEG3VD.js";import"./chunk-AX2PWSRT.js";import"./chunk-AJONZWLT.js";import"./chunk-F4HZTZHW.js";import"./chunk-ZTEU5JVQ.js";import"./chunk-QOYAX2FT.js";import"./chunk-LCIREYTD.js";import"./chunk-L2WSPY6S.js";import"./chunk-CJQQIRPD.js";import"./chunk-BH3GTCZ6.js";import"./chunk-FIOB3MEX.js";export{a as agenticQuery};
2
+ //# sourceMappingURL=query-ZKTFJMNE.js.map
@@ -0,0 +1,3 @@
1
+ import"./chunk-FIOB3MEX.js";import{createHash as l}from"crypto";import{existsSync as f,mkdirSync as c,readFileSync as g,writeFileSync as p}from"fs";import{dirname as h,resolve as m}from"path";function q(u,s,i,t){return l("sha1").update([u,s,i.toLowerCase().trim(),t??""].join("\0")).digest("hex").slice(0,12)}var d=class{file;constructor(s){this.file=m(s,"_questions.json")}get path(){return this.file}load(){if(!f(this.file))return{version:1,questions:[]};try{let s=JSON.parse(g(this.file,"utf-8"));return Array.isArray(s?.questions)?{version:1,questions:s.questions}:{version:1,questions:[]}}catch{return{version:1,questions:[]}}}save(s){c(h(this.file),{recursive:!0}),p(this.file,`${JSON.stringify(s,null,2)}
2
+ `)}list(s){let i=this.load().questions;return s?i.filter(t=>t.status===s):i}add(s){let i=this.load(),t=new Set(i.questions.map(n=>n.id)),r=0,e=0,o=new Date().toISOString();for(let n of s){let a=q(n.kind,n.agentId,n.subject,n.field);if(t.has(a)){e++;continue}t.add(a),i.questions.push({...n,id:a,status:"open",asked:o}),r++}return r>0&&this.save(i),{added:r,skipped:e}}resolve(s,i,t){let r=this.load(),e=r.questions.find(o=>o.id===s||o.id.startsWith(s));return e?(e.status=i,e.answered=new Date().toISOString(),t!==void 0&&(e.answer=t),this.save(r),e):null}};export{d as QuestionStore,q as questionId};
3
+ //# sourceMappingURL=questions-4B4KSPXG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/wiki/questions.ts"],"sourcesContent":["import { createHash } from \"crypto\"\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\"\nimport { dirname, resolve } from \"path\"\n\n// The gaps a lookup cannot close.\n//\n// Two things were producing gap signal and throwing it away. Absorb has\n// always emitted a `gaps` array — entities it referenced but has no\n// article for — and the command printed it and moved on, so the signal\n// lived in a cron log nobody reads. And the field grader can now name a\n// missing `role` or `ourOwner` that no system of record holds.\n//\n// Both are the same thing: a question only a person can answer. They\n// get one queue rather than two, because a second parallel store is\n// precisely what the wiki-as-source-of-truth decision rules out.\n//\n// Stored as JSON beside the other wiki sidecars (_index.json,\n// _backlinks.json), because this is derived state that is rebuilt and\n// rewritten, not knowledge. The knowledge is what lands in the article\n// once the question is answered.\n\nexport type QuestionStatus = \"open\" | \"answered\" | \"dismissed\"\n\nexport interface WikiQuestion {\n id: string\n /** \"field\" — a named gap in an existing article.\n * \"article\" — absorb referenced something with no article at all. */\n kind: \"field\" | \"article\"\n agentId: string\n /** Article path for a field question; empty for a missing article. */\n path: string\n /** Article title, or the referenced name for a missing article. */\n subject: string\n /** Required-field key, for `kind: \"field\"`. */\n field?: string\n /** What tier of the build this gap sits at, so asking can be ordered. */\n tier?: string\n question: string\n status: QuestionStatus\n asked: string\n answered?: string\n answer?: string\n}\n\nexport interface QuestionsFile {\n version: 1\n questions: WikiQuestion[]\n}\n\n/**\n * Stable across runs, so re-grading the same article does not ask the\n * same question twice and cannot resurrect one already dismissed.\n */\nexport function questionId(kind: string, agentId: string, subject: string, field?: string): string {\n return createHash(\"sha1\")\n .update([kind, agentId, subject.toLowerCase().trim(), field ?? \"\"].join(\"\\u0000\"))\n .digest(\"hex\")\n .slice(0, 12)\n}\n\nexport class QuestionStore {\n private readonly file: string\n\n constructor(wikiDir: string) {\n this.file = resolve(wikiDir, \"_questions.json\")\n }\n\n get path(): string { return this.file }\n\n load(): QuestionsFile {\n if (!existsSync(this.file)) return { version: 1, questions: [] }\n try {\n const parsed = JSON.parse(readFileSync(this.file, \"utf-8\")) as Partial<QuestionsFile>\n if (!Array.isArray(parsed?.questions)) return { version: 1, questions: [] }\n return { version: 1, questions: parsed.questions as WikiQuestion[] }\n } catch {\n // A corrupt queue must not stop an absorb. Worst case the\n // questions are asked again; they are idempotent by id.\n return { version: 1, questions: [] }\n }\n }\n\n private save(f: QuestionsFile): void {\n mkdirSync(dirname(this.file), { recursive: true })\n writeFileSync(this.file, `${JSON.stringify(f, null, 2)}\\n`)\n }\n\n list(status?: QuestionStatus): WikiQuestion[] {\n const all = this.load().questions\n return status ? all.filter((q) => q.status === status) : all\n }\n\n /**\n * Add questions that are not already on the queue.\n *\n * An existing id is left completely alone — including an answered or\n * dismissed one. Re-asking something a person has already dealt with\n * is how a queue becomes noise and then becomes ignored.\n */\n add(items: Array<Omit<WikiQuestion, \"id\" | \"status\" | \"asked\">>): { added: number; skipped: number } {\n const f = this.load()\n const seen = new Set(f.questions.map((q) => q.id))\n let added = 0, skipped = 0\n const now = new Date().toISOString()\n\n for (const item of items) {\n const id = questionId(item.kind, item.agentId, item.subject, item.field)\n if (seen.has(id)) { skipped++; continue }\n seen.add(id)\n f.questions.push({ ...item, id, status: \"open\", asked: now })\n added++\n }\n if (added > 0) this.save(f)\n return { added, skipped }\n }\n\n /** Returns the question so the caller can act on its subject/field. */\n resolve(id: string, status: \"answered\" | \"dismissed\", answer?: string): WikiQuestion | null {\n const f = this.load()\n const q = f.questions.find((x) => x.id === id || x.id.startsWith(id))\n if (!q) return null\n q.status = status\n q.answered = new Date().toISOString()\n if (answer !== undefined) q.answer = answer\n this.save(f)\n return q\n }\n}\n"],"mappings":"4BAAA,OAAS,cAAAA,MAAkB,SAC3B,OAAS,cAAAC,EAAY,aAAAC,EAAW,gBAAAC,EAAc,iBAAAC,MAAqB,KACnE,OAAS,WAAAC,EAAS,WAAAC,MAAe,OAmD1B,SAASC,EAAWC,EAAcC,EAAiBC,EAAiBC,EAAwB,CACjG,OAAOX,EAAW,MAAM,EACrB,OAAO,CAACQ,EAAMC,EAASC,EAAQ,YAAY,EAAE,KAAK,EAAGC,GAAS,EAAE,EAAE,KAAK,IAAQ,CAAC,EAChF,OAAO,KAAK,EACZ,MAAM,EAAG,EAAE,CAChB,CAEO,IAAMC,EAAN,KAAoB,CACR,KAEjB,YAAYC,EAAiB,CAC3B,KAAK,KAAOP,EAAQO,EAAS,iBAAiB,CAChD,CAEA,IAAI,MAAe,CAAE,OAAO,KAAK,IAAK,CAEtC,MAAsB,CACpB,GAAI,CAACZ,EAAW,KAAK,IAAI,EAAG,MAAO,CAAE,QAAS,EAAG,UAAW,CAAC,CAAE,EAC/D,GAAI,CACF,IAAMa,EAAS,KAAK,MAAMX,EAAa,KAAK,KAAM,OAAO,CAAC,EAC1D,OAAK,MAAM,QAAQW,GAAQ,SAAS,EAC7B,CAAE,QAAS,EAAG,UAAWA,EAAO,SAA4B,EADrB,CAAE,QAAS,EAAG,UAAW,CAAC,CAAE,CAE5E,MAAE,CAGA,MAAO,CAAE,QAAS,EAAG,UAAW,CAAC,CAAE,CACrC,CACF,CAEQ,KAAKC,EAAwB,CACnCb,EAAUG,EAAQ,KAAK,IAAI,EAAG,CAAE,UAAW,EAAK,CAAC,EACjDD,EAAc,KAAK,KAAM,GAAG,KAAK,UAAUW,EAAG,KAAM,CAAC;AAAA,CAAK,CAC5D,CAEA,KAAKC,EAAyC,CAC5C,IAAMC,EAAM,KAAK,KAAK,EAAE,UACxB,OAAOD,EAASC,EAAI,OAAQC,GAAMA,EAAE,SAAWF,CAAM,EAAIC,CAC3D,CASA,IAAIE,EAAiG,CACnG,IAAMJ,EAAI,KAAK,KAAK,EACdK,EAAO,IAAI,IAAIL,EAAE,UAAU,IAAKG,GAAMA,EAAE,EAAE,CAAC,EAC7CG,EAAQ,EAAGC,EAAU,EACnBC,EAAM,IAAI,KAAK,EAAE,YAAY,EAEnC,QAAWC,KAAQL,EAAO,CACxB,IAAMM,EAAKlB,EAAWiB,EAAK,KAAMA,EAAK,QAASA,EAAK,QAASA,EAAK,KAAK,EACvE,GAAIJ,EAAK,IAAIK,CAAE,EAAG,CAAEH,IAAW,SAC/BF,EAAK,IAAIK,CAAE,EACXV,EAAE,UAAU,KAAK,CAAE,GAAGS,EAAM,GAAAC,EAAI,OAAQ,OAAQ,MAAOF,CAAI,CAAC,EAC5DF,IAEF,OAAIA,EAAQ,GAAG,KAAK,KAAKN,CAAC,EACnB,CAAE,MAAAM,EAAO,QAAAC,CAAQ,CAC1B,CAGA,QAAQG,EAAYT,EAAkCU,EAAsC,CAC1F,IAAMX,EAAI,KAAK,KAAK,EACdG,EAAIH,EAAE,UAAU,KAAMY,GAAMA,EAAE,KAAOF,GAAME,EAAE,GAAG,WAAWF,CAAE,CAAC,EACpE,OAAKP,GACLA,EAAE,OAASF,EACXE,EAAE,SAAW,IAAI,KAAK,EAAE,YAAY,EAChCQ,IAAW,SAAWR,EAAE,OAASQ,GACrC,KAAK,KAAKX,CAAC,EACJG,GALQ,IAMjB,CACF","names":["createHash","existsSync","mkdirSync","readFileSync","writeFileSync","dirname","resolve","questionId","kind","agentId","subject","field","QuestionStore","wikiDir","parsed","f","status","all","q","items","seen","added","skipped","now","item","id","answer","x"]}
@@ -0,0 +1,2 @@
1
+ import{a,b}from"./chunk-K33GIBSE.js";import"./chunk-L2WSPY6S.js";import"./chunk-FIOB3MEX.js";export{a as loadRecipes,b as resolveRecipes};
2
+ //# sourceMappingURL=recipes-7S4MC5OJ.js.map
@@ -0,0 +1,2 @@
1
+ import{A as e,B as f,C as g,D as h,E as i,F as j,w as a,x as b,y as c,z as d}from"./chunk-OPHIXXGI.js";import"./chunk-GDWJUGB5.js";import"./chunk-M4AMWP3H.js";import"./chunk-4BR7D2OU.js";import"./chunk-67Q4DCPS.js";import"./chunk-W4YR2RO2.js";import"./chunk-AFRHKA7B.js";import"./chunk-FIEJDZVC.js";import"./chunk-2T5W3ETS.js";import"./chunk-77OV7346.js";import"./chunk-VWTBHLMX.js";import"./chunk-IBUSGCLQ.js";import"./chunk-PNWNVOEZ.js";import"./chunk-64P4QHSZ.js";import"./chunk-L2IZPR6P.js";import"./chunk-AJ5RMU7X.js";import"./chunk-EVWEG3VD.js";import"./chunk-AX2PWSRT.js";import"./chunk-AJONZWLT.js";import"./chunk-F4HZTZHW.js";import"./chunk-ZTEU5JVQ.js";import"./chunk-QOYAX2FT.js";import"./chunk-WPMX3VGI.js";import"./chunk-K33GIBSE.js";import"./chunk-LCIREYTD.js";import"./chunk-L2WSPY6S.js";import"./chunk-DSYKYZMT.js";import"./chunk-CJQQIRPD.js";import"./chunk-BH3GTCZ6.js";import"./chunk-FIOB3MEX.js";export{j as AgentRegistry,e as TRACE_STEP_SUMMARY_BYTES,c as buildEntryMeta,d as buildWikiContext,f as clipForTrace,i as emitTraceStepsFromStreamEvent,h as firstMissingRequiredTool,b as getGlobalRegistry,a as setGlobalRegistry,g as tallyToolUses};
2
+ //# sourceMappingURL=registry-PSZDPFZK.js.map
@@ -0,0 +1,4 @@
1
+ import{b}from"./chunk-DCDQZR7I.js";import"./chunk-4BR7D2OU.js";import"./chunk-EVWEG3VD.js";import"./chunk-AX2PWSRT.js";import"./chunk-AJONZWLT.js";import"./chunk-F4HZTZHW.js";import"./chunk-ZTEU5JVQ.js";import"./chunk-LCIREYTD.js";import"./chunk-L2WSPY6S.js";import"./chunk-CJQQIRPD.js";import"./chunk-BH3GTCZ6.js";import"./chunk-FIOB3MEX.js";import{createHash as A}from"crypto";var v="review:",I={decisions:"project",warnings:"feedback",friction:"feedback",context:"reference"};function E(t,n,r){let s=A("sha1").update(r.trim().toLowerCase()).digest("hex").slice(0,10);return`${v}${t}/${n}@${s}`}function F(t){if(!t.startsWith(v))return null;let n=t.slice(v.length),r=n.lastIndexOf("@"),s=n.lastIndexOf("/",r===-1?void 0:r);return r===-1||s===-1?null:{reviewId:n.slice(0,s),kind:n.slice(s+1,r),digest:n.slice(r+1)}}var C=t=>t.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"").slice(0,48)||"finding";function L(t,n){return`${t}|${b(n)}`}function K(t,n={}){let r=n.kinds??Object.keys(I),s=n.minLength??24,h=[],d=new Map,u=new Map,f=new Map,x=new Map;for(let e of t){let i;try{i=JSON.parse(e.result)}catch{continue}let o=e.updated_at?new Date(e.updated_at).toISOString():new Date().toISOString();for(let c of r){let l=I[c];if(!l)continue;let R=i[c];if(Array.isArray(R))for(let $ of R){let a=String($?.text??"").trim();if(a.length<s)continue;let g=E(e.id,c,a),O=g.slice(g.lastIndexOf("@")),w=`${c}${O}`,y=L(c,a),k=f.get(y)??new Set;if(k.add(e.session_id??e.id),f.set(y,k),d.has(w))continue;let _=String($?.evidence??"").trim(),S={occurrences:1,agentId:e.agent,key:`${e.agent}/${l}_${C(a)}`,stamp:g,memory:{name:C(a),type:l,description:a.slice(0,200),body:"",createdAt:o,updatedAt:o}};x.set(g,{text:a,evidence:_,agent:e.agent,source:e.source}),d.set(w,S),u.set(w,y),h.push(S)}}}for(let[e,i]of d){i.occurrences=f.get(u.get(e)??"")?.size??1;let o=x.get(i.stamp);i.memory.body=T(o,i.occurrences)}let m=new Map;for(let[e,i]of d){let o=u.get(e)??e,c=m.get(o)??[];c.push(i),m.set(o,c)}let M=[...m.values()].sort((e,i)=>i[0].occurrences-e[0].occurrences),p=[];for(let e=0;p.length<h.length;e++){let i=!1;for(let o of M)e<o.length&&(p.push(o[e]),i=!0);if(!i)break}return p}function T(t,n){let r=`${t.agent}${t.source?` via ${t.source}`:""}`,s=[];return n>1?(s.push(`Observed in ${n} separate sessions. Most recently stated as:`),s.push(`> ${t.text}`),s.push(`Recurring across ${n} sessions is what makes this worth recording \u2014 any single occurrence is session state, the repetition is the finding.`)):s.push(t.text),t.evidence&&s.push(`**Evidence:** ${t.evidence}`),s.push(`_From ${n>1?"session reviews":"a session review"} of ${r}._`),s.join(`
2
+
3
+ `)}export{I as PROMOTABLE_KINDS,v as REVIEW_STAMP_PREFIX,F as parseReviewStamp,E as reviewStamp,K as reviewsToCandidates};
4
+ //# sourceMappingURL=review-candidates-AEUXRQQC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/wiki/review-candidates.ts"],"sourcesContent":["import { createHash } from \"crypto\"\nimport type { MemoryType } from \"../agents/agent-memory\"\nimport { commitmentKey } from \"@/daemon/session-monitor\"\nimport type { MemoryCandidate } from \"./promote\"\n\n// Monitor reviews → wiki promotion candidates.\n//\n// The session monitor has been producing structured findings all along\n// and none of them have ever reached the wiki. 574 parsed reviews hold\n// 1,543 decisions, 1,511 warnings, 1,278 pieces of context, 1,216\n// actions and 1,106 friction notes — 6,654 items against a wiki of 784\n// articles. The single richest seam of institutional knowledge on the\n// fleet has been sitting in SQLite because nothing was pointed at it.\n//\n// It never reached absorb for a structural reason rather than an\n// oversight: absorb reads raw *conversation* entries, and a review is\n// not a conversation. It is a judgement already extracted from one, with\n// its evidence attached. Pushing reviews through absorb would pay a\n// writing model to re-derive what the monitor already derived.\n//\n// So they go through `wiki promote` instead, which exists for exactly\n// this shape — a durable, already-formed claim that needs judging for\n// fleet relevance and writing into a shared article. Reviews become\n// MemoryCandidates carrying a `review:` stamp, and inherit the ledger,\n// the idempotency and the judge without any of it being rebuilt.\n//\n// Only the kinds that describe what IS or WAS are eligible. `actions`\n// are excluded: an action is a thing somebody should do, it goes stale\n// the moment it is done or abandoned, and a wiki full of last month's\n// to-dos is worse than one without them. Those belong on the questions\n// queue if anywhere.\n\nexport const REVIEW_STAMP_PREFIX = \"review:\"\n\n/** Review kinds worth keeping, mapped to the memory type they promote as. */\nexport const PROMOTABLE_KINDS: Record<string, MemoryType> = {\n decisions: \"project\",\n warnings: \"feedback\",\n friction: \"feedback\",\n context: \"reference\",\n}\n\nexport interface ReviewRow {\n id: string\n /** The run being reviewed. The monitor re-reviews a session as it\n * progresses, so several rows share one session_id. */\n session_id?: string\n agent: string\n source?: string\n updated_at?: number\n /** The parsed Review JSON as stored. */\n result: string\n}\n\nexport interface ReviewItem {\n text: string\n evidence?: string\n}\n\n/**\n * Stable identity for one item inside one review.\n *\n * Keyed on the item's TEXT, not its index. The monitor re-reviews a\n * session as it progresses, so the same finding reappears at a\n * different position in a later review of the same run; indexing by\n * position would promote it again as something new. Hashing the text\n * means a re-stated finding is recognised and a genuinely reworded one\n * is treated as new, which is the right way round.\n */\nexport function reviewStamp(reviewId: string, kind: string, text: string): string {\n const digest = createHash(\"sha1\").update(text.trim().toLowerCase()).digest(\"hex\").slice(0, 10)\n return `${REVIEW_STAMP_PREFIX}${reviewId}/${kind}@${digest}`\n}\n\nexport function parseReviewStamp(s: string): { reviewId: string; kind: string; digest: string } | null {\n if (!s.startsWith(REVIEW_STAMP_PREFIX)) return null\n const rest = s.slice(REVIEW_STAMP_PREFIX.length)\n const at = rest.lastIndexOf(\"@\")\n const slash = rest.lastIndexOf(\"/\", at === -1 ? undefined : at)\n if (at === -1 || slash === -1) return null\n return { reviewId: rest.slice(0, slash), kind: rest.slice(slash + 1, at), digest: rest.slice(at + 1) }\n}\n\nconst slug = (s: string) =>\n s.toLowerCase().replace(/[^a-z0-9]+/g, \"-\").replace(/^-|-$/g, \"\").slice(0, 48) || \"finding\"\n\n/**\n * Turn stored reviews into promotion candidates.\n *\n * The evidence travels with the claim. A warning without it is an\n * assertion a reader has to take on faith, and the monitor already went\n * to the trouble of collecting it — dropping it here would make the\n * resulting article exactly the kind of unfalsifiable prose the wiki is\n * meant not to accumulate.\n */\nexport interface ReviewCandidate extends MemoryCandidate {\n /** How many DISTINCT SESSIONS produced substantially this finding.\n *\n * Distinct sessions, not review rows, and the difference decides\n * whether the number means anything. The monitor re-reviews a run as\n * it progresses, so counting rows made a single miner run that\n * restated one observation 24 times look like the fleet's most\n * recurrent finding. It ranked top, and the promotion judge threw it\n * out as \"a snapshot of pending operator items from one miner run\" —\n * correctly. Recurrence across sessions is evidence of a pattern;\n * recurrence within one is evidence of a long session. */\n occurrences: number\n}\n\n/**\n * Two keys, deliberately, because dedupe and ranking want opposite errors.\n *\n * IDENTITY is the exact text hash in the stamp. It must never merge two\n * findings that differ, because a merge drops one permanently — so it\n * errs towards treating a rewording as new.\n *\n * SIMILARITY, for counting recurrence, wants the opposite: monitor\n * findings are free text re-derived from scratch every session, so exact\n * matching almost never fires. On this corpus it found 3 recurrences in\n * 5,435 findings, which is no signal at all. `commitmentKey` is the\n * monitor's own answer to this, written after one leaked token produced\n * 18 separate \"rotate it\" actions — verb plus the entity acted on. Over-\n * merging here only inflates a rank; it cannot lose a finding.\n */\nfunction similarityKey(kind: string, text: string): string {\n return `${kind}|${commitmentKey(text)}`\n}\n\nexport function reviewsToCandidates(\n rows: ReviewRow[],\n opts: { kinds?: string[]; minLength?: number } = {},\n): ReviewCandidate[] {\n const kinds = opts.kinds ?? Object.keys(PROMOTABLE_KINDS)\n const minLength = opts.minLength ?? 24\n\n const out: ReviewCandidate[] = []\n // Emit once per exact wording…\n const byExact = new Map<string, ReviewCandidate>()\n const familyOf = new Map<string, string>()\n // …but count per family. Keeping these separate is the point: sharing\n // one map would make an over-merge drop a distinct finding instead of\n // merely inflating a rank.\n const familySessions = new Map<string, Set<string>>()\n const provenance = new Map<string, { text: string; evidence: string; agent: string; source?: string }>()\n\n for (const row of rows) {\n let review: Record<string, unknown>\n try { review = JSON.parse(row.result) as Record<string, unknown> } catch { continue }\n\n const when = row.updated_at ? new Date(row.updated_at).toISOString() : new Date().toISOString()\n\n for (const kind of kinds) {\n const type = PROMOTABLE_KINDS[kind]\n if (!type) continue\n const items = review[kind]\n if (!Array.isArray(items)) continue\n\n for (const raw of items as ReviewItem[]) {\n const text = String(raw?.text ?? \"\").trim()\n // Very short findings are almost always a restated status line\n // (\"deploy succeeded\"), which carries nothing an article wants.\n if (text.length < minLength) continue\n\n const stamp = reviewStamp(row.id, kind, text)\n // The same finding recurs across reviews of one run and across\n // runs. Promote the first occurrence, but count the rest: a\n // finding stated once is usually about that session, while one\n // stated a dozen times is a property of the fleet. That count is\n // the only durability signal this source offers for free, and\n // without it 5,000 candidates arrive in arbitrary order.\n const dedupe = stamp.slice(stamp.lastIndexOf(\"@\"))\n const key = `${kind}${dedupe}`\n\n // Count every occurrence, including restatements of something\n // already emitted — a finding said five different ways is one\n // recurring finding, and that is what deserves promoting first.\n const family = similarityKey(kind, text)\n const sessions = familySessions.get(family) ?? new Set<string>()\n sessions.add(row.session_id ?? row.id)\n familySessions.set(family, sessions)\n if (byExact.has(key)) continue\n\n const evidence = String(raw?.evidence ?? \"\").trim()\n const candidate: ReviewCandidate = {\n occurrences: 1,\n agentId: row.agent,\n key: `${row.agent}/${type}_${slug(text)}`,\n stamp,\n memory: {\n name: slug(text),\n type,\n description: text.slice(0, 200),\n // Body is filled in after the counting pass, because how a\n // finding should be framed depends on how often it recurred.\n body: \"\",\n createdAt: when,\n updatedAt: when,\n },\n }\n provenance.set(stamp, { text, evidence, agent: row.agent, source: row.source })\n byExact.set(key, candidate)\n familyOf.set(key, family)\n out.push(candidate)\n }\n }\n }\n\n for (const [key, candidate] of byExact) {\n candidate.occurrences = familySessions.get(familyOf.get(key) ?? \"\")?.size ?? 1\n const p = provenance.get(candidate.stamp)!\n candidate.memory.body = renderBody(p, candidate.occurrences)\n }\n\n // Recurrence decides which finding matters; diversity decides what a\n // capped run gets to see. Sorting on count alone put five rewordings\n // of the same 24× observation in the top five, spending the whole\n // nightly budget on one idea. So: one representative per family\n // first, in count order, then second representatives, and so on.\n // Nothing is dropped — the rewordings are still in the list, just\n // behind a first look at everything else.\n const byFamily = new Map<string, ReviewCandidate[]>()\n for (const [key, candidate] of byExact) {\n const family = familyOf.get(key) ?? key\n const list = byFamily.get(family) ?? []\n list.push(candidate)\n byFamily.set(family, list)\n }\n const families = [...byFamily.values()].sort((a, b) => b[0].occurrences - a[0].occurrences)\n\n const ordered: ReviewCandidate[] = []\n for (let round = 0; ordered.length < out.length; round++) {\n let placed = false\n for (const list of families) {\n if (round < list.length) { ordered.push(list[round]); placed = true }\n }\n if (!placed) break\n }\n return ordered\n}\n\n/**\n * How a finding is written decides whether it survives the judge.\n *\n * The monitor writes point-in-time observations, and the promotion\n * judge — correctly — rejects those: \"a transient OAuth expiry that\n * stalled #38\", \"issue-thread state, not a wiki fact\". Twelve\n * candidates across two runs, all skipped, every reason sound.\n *\n * But a finding seen in seven separate sessions is no longer a status\n * report about one of them; it is evidence of something that keeps\n * happening, and that IS durable. The observation was never the\n * knowledge — the repetition is. Saying so explicitly, with the session\n * count as the evidence, is the difference between offering the judge a\n * stale fact and offering it a pattern.\n *\n * A one-off stays phrased as what it is. Dressing a single observation\n * up as a pattern would just launder session state past the judge,\n * which is the failure this is trying to avoid, not a way around it.\n */\nfunction renderBody(\n p: { text: string; evidence: string; agent: string; source?: string },\n occurrences: number,\n): string {\n const origin = `${p.agent}${p.source ? ` via ${p.source}` : \"\"}`\n const parts: string[] = []\n\n if (occurrences > 1) {\n parts.push(`Observed in ${occurrences} separate sessions. Most recently stated as:`)\n parts.push(`> ${p.text}`)\n parts.push(\n `Recurring across ${occurrences} sessions is what makes this worth recording — ` +\n `any single occurrence is session state, the repetition is the finding.`,\n )\n } else {\n parts.push(p.text)\n }\n\n if (p.evidence) parts.push(`**Evidence:** ${p.evidence}`)\n parts.push(`_From ${occurrences > 1 ? \"session reviews\" : \"a session review\"} of ${origin}._`)\n return parts.join(\"\\n\\n\")\n}\n"],"mappings":"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","names":["createHash","REVIEW_STAMP_PREFIX","PROMOTABLE_KINDS","reviewStamp","reviewId","kind","text","digest","createHash","parseReviewStamp","s","rest","at","slash","slug","similarityKey","commitmentKey","reviewsToCandidates","rows","opts","kinds","minLength","out","byExact","familyOf","familySessions","provenance","row","review","when","type","items","raw","stamp","dedupe","key","family","sessions","evidence","candidate","p","renderBody","byFamily","list","families","a","b","ordered","round","placed","occurrences","origin","parts"]}
@@ -0,0 +1,4 @@
1
+ import"./chunk-FIOB3MEX.js";import{appendFileSync as p,existsSync as w,mkdirSync as y,readFileSync as v}from"fs";import{dirname as R,resolve as S}from"path";var d=class{file;constructor(n){this.file=S(n,"reviews.jsonl")}get path(){return this.file}load(){let n=new Map;if(!w(this.file))return n;let i;try{i=v(this.file,"utf-8")}catch{return n}for(let s of i.split(`
2
+ `)){let a=s.trim();if(a)try{let c=JSON.parse(a);c?.msgHash&&c?.verdict&&n.set(c.msgHash,c)}catch{}}return n}record(n){let i={...n,ts:n.ts??new Date().toISOString()};return y(R(this.file),{recursive:!0}),p(this.file,`${JSON.stringify(i)}
3
+ `),i}};function M(m,n,i={}){let s=i.n??20,a=i.strategy??"stratified",c=new Map;for(let e of m)e?.msgHash&&c.set(e.msgHash,e);let u=[...c.values()].filter(e=>!n.has(e.msgHash));if(u.length===0)return[];if(a==="uncertain")return[...u].sort((e,f)=>(e.confidence??2)-(f.confidence??2)).slice(0,s);let g=u.filter(e=>typeof e.confidence=="number"),b=u.filter(e=>typeof e.confidence!="number"),t=new Map;for(let e of g){let f=Math.min(9,Math.floor(e.confidence*10)),h=t.get(f)??[];h.push(e),t.set(f,h)}let r=[...t.keys()].sort((e,f)=>e-f),o=[],l=0;for(;o.length<s&&r.length>0;){let e=!1;for(let f of r){let h=t.get(f);if(l<h.length&&(o.push(h[l]),e=!0),o.length>=s)break}if(!e)break;l++}for(let e of b){if(o.length>=s)break;o.push(e)}return o}function C(m,n){let i=new Map;for(let t of m)t?.msgHash&&i.set(t.msgHash,t);let s=0,a=0,c=0,u=0,g=new Map;for(let[t,r]of n){let o=i.get(t);if(o){if(r.verdict==="correct")s++;else if(r.verdict==="wrong")a++;else{c++;continue}if(typeof o.confidence=="number"){u++;let l=Math.min(9,Math.floor(o.confidence*10)),e=g.get(l)??{n:0,correct:0};e.n++,r.verdict==="correct"&&e.correct++,g.set(l,e)}}}let b=s+a;return{classifications:i.size,reviewed:s+a+c,correct:s,wrong:a,unsure:c,accuracy:b?s/b:0,calibratable:u,byBin:[...g.entries()].sort((t,r)=>t[0]-r[0]).map(([t,r])=>({bin:`${(t/10).toFixed(1)}-${((t+1)/10).toFixed(1)}`,n:r.n,accuracy:r.n?r.correct/r.n:0}))}}export{d as ReviewStore,M as buildQueue,C as labelStats};
4
+ //# sourceMappingURL=review-store-B4YE3EFK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/graph/review-store.ts"],"sourcesContent":["import { appendFileSync, existsSync, mkdirSync, readFileSync } from \"fs\"\nimport { dirname, resolve } from \"path\"\n\n// Human verdicts on graph classifications.\n//\n// The classifier has recorded 8,903 classifications and not one of them\n// carries a human judgement. 8,871 say \"approved\", which sounds like\n// ground truth and is not: `status: \"approved\"` is set by the\n// classifier itself when the proposed path passes schema validation and\n// its nodes commit. Nothing rejects. `graph.autoApproveConfidence`\n// still defaults to 1.0 — permanently off — which is the codebase\n// stating plainly that it does not trust the number.\n//\n// So a decision seat here would produce a better-formed probability\n// against nothing to check it with, which is the one outcome the whole\n// calibration exercise exists to avoid. Labels first.\n//\n// Verdicts live in their own log rather than in classifications.jsonl.\n// approveClassification() appends a copy of the row with the status\n// flipped, so a human verdict written that way would be\n// indistinguishable from the classifier's own — the exact conflation\n// that produced 8,871 meaningless \"approved\" rows. Keeping them apart\n// means the join is explicit and the existing log stays what it is: a\n// record of what the classifier did.\n\nexport type Verdict = \"correct\" | \"wrong\" | \"unsure\"\n\nexport interface Review {\n msgHash: string\n verdict: Verdict\n /** For `wrong`: the path it should have been, when the reviewer knows. */\n correctPath?: string[]\n /** Free-text note, for the cases worth remembering. */\n note?: string\n reviewer: string\n ts: string\n}\n\nexport interface ClassificationRow {\n msgHash: string\n path: string[]\n leaf?: string\n confidence?: number\n preview?: string\n agentId?: string\n channel?: string\n source?: string\n status?: string\n ts?: string\n}\n\nexport class ReviewStore {\n private readonly file: string\n\n constructor(baseDir: string) {\n this.file = resolve(baseDir, \"reviews.jsonl\")\n }\n\n get path(): string { return this.file }\n\n /** Latest verdict per msgHash — a reviewer may change their mind. */\n load(): Map<string, Review> {\n const out = new Map<string, Review>()\n if (!existsSync(this.file)) return out\n let raw: string\n try { raw = readFileSync(this.file, \"utf-8\") } catch { return out }\n for (const line of raw.split(\"\\n\")) {\n const t = line.trim()\n if (!t) continue\n try {\n const r = JSON.parse(t) as Review\n if (r?.msgHash && r?.verdict) out.set(r.msgHash, r)\n } catch { /* skip corrupt line */ }\n }\n return out\n }\n\n record(review: Omit<Review, \"ts\"> & { ts?: string }): Review {\n const row: Review = { ...review, ts: review.ts ?? new Date().toISOString() }\n mkdirSync(dirname(this.file), { recursive: true })\n appendFileSync(this.file, `${JSON.stringify(row)}\\n`)\n return row\n }\n}\n\nexport type QueueStrategy = \"uncertain\" | \"stratified\"\n\n/**\n * What to put in front of a person next.\n *\n * `uncertain` is classic active learning: lowest confidence first,\n * because that is where a label changes the most. It finds errors fast\n * and it is the right mode when the question is \"is this thing broken\".\n *\n * It is the wrong mode for calibration, and that distinction matters\n * enough to be the default. Labelling only the bottom of the confidence\n * range leaves the top bins empty, and the top bins are exactly where\n * an overconfident model does its damage — a reliability diagram built\n * from uncertain-only labels cannot see the failure it exists to find.\n * `stratified` spreads the budget evenly across confidence deciles so\n * every bin gets evidence.\n */\nexport function buildQueue(\n rows: ClassificationRow[],\n reviewed: Set<string>,\n opts: { n?: number; strategy?: QueueStrategy } = {},\n): ClassificationRow[] {\n const n = opts.n ?? 20\n const strategy = opts.strategy ?? \"stratified\"\n\n // One row per message. The log is append-only and re-records a row on\n // every status change, so the same msgHash appears several times.\n const byHash = new Map<string, ClassificationRow>()\n for (const r of rows) if (r?.msgHash) byHash.set(r.msgHash, r)\n\n const pending = [...byHash.values()].filter((r) => !reviewed.has(r.msgHash))\n if (pending.length === 0) return []\n\n if (strategy === \"uncertain\") {\n // Rows with no confidence at all are cache hits; they carry no\n // number to be uncertain about, so they sort last here.\n return [...pending]\n .sort((a, b) => (a.confidence ?? 2) - (b.confidence ?? 2))\n .slice(0, n)\n }\n\n const scored = pending.filter((r) => typeof r.confidence === \"number\")\n const unscored = pending.filter((r) => typeof r.confidence !== \"number\")\n\n const bins = new Map<number, ClassificationRow[]>()\n for (const r of scored) {\n const bin = Math.min(9, Math.floor((r.confidence as number) * 10))\n const list = bins.get(bin) ?? []\n list.push(r)\n bins.set(bin, list)\n }\n\n // Round-robin across occupied bins so a sparse bin still contributes\n // and a crowded one cannot swallow the budget.\n const order = [...bins.keys()].sort((a, b) => a - b)\n const out: ClassificationRow[] = []\n let i = 0\n while (out.length < n && order.length > 0) {\n let placed = false\n for (const bin of order) {\n const list = bins.get(bin)!\n if (i < list.length) { out.push(list[i]); placed = true }\n if (out.length >= n) break\n }\n if (!placed) break\n i++\n }\n // Cache hits are still worth labelling for accuracy, just not for\n // calibration — they fill whatever budget is left.\n for (const r of unscored) {\n if (out.length >= n) break\n out.push(r)\n }\n return out\n}\n\nexport interface LabelStats {\n classifications: number\n reviewed: number\n correct: number\n wrong: number\n unsure: number\n /** Accuracy among decided verdicts; `unsure` is excluded rather than\n * counted either way. */\n accuracy: number\n /** Reviewed rows that carry a confidence, i.e. usable for calibration. */\n calibratable: number\n byBin: Array<{ bin: string; n: number; accuracy: number }>\n}\n\nexport function labelStats(rows: ClassificationRow[], reviews: Map<string, Review>): LabelStats {\n const byHash = new Map<string, ClassificationRow>()\n for (const r of rows) if (r?.msgHash) byHash.set(r.msgHash, r)\n\n let correct = 0, wrong = 0, unsure = 0, calibratable = 0\n const bins = new Map<number, { n: number; correct: number }>()\n\n for (const [hash, review] of reviews) {\n const row = byHash.get(hash)\n if (!row) continue\n if (review.verdict === \"correct\") correct++\n else if (review.verdict === \"wrong\") wrong++\n else { unsure++; continue }\n\n if (typeof row.confidence === \"number\") {\n calibratable++\n const bin = Math.min(9, Math.floor(row.confidence * 10))\n const b = bins.get(bin) ?? { n: 0, correct: 0 }\n b.n++\n if (review.verdict === \"correct\") b.correct++\n bins.set(bin, b)\n }\n }\n\n const decided = correct + wrong\n return {\n classifications: byHash.size,\n reviewed: correct + wrong + unsure,\n correct, wrong, unsure,\n accuracy: decided ? correct / decided : 0,\n calibratable,\n byBin: [...bins.entries()].sort((a, b) => a[0] - b[0]).map(([bin, b]) => ({\n bin: `${(bin / 10).toFixed(1)}-${((bin + 1) / 10).toFixed(1)}`,\n n: b.n,\n accuracy: b.n ? b.correct / b.n : 0,\n })),\n }\n}\n"],"mappings":"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","names":["appendFileSync","existsSync","mkdirSync","readFileSync","dirname","resolve","ReviewStore","baseDir","out","raw","line","t","r","review","row","buildQueue","rows","reviewed","opts","n","strategy","byHash","pending","a","b","scored","unscored","bins","bin","list","order","i","placed","labelStats","reviews","correct","wrong","unsure","calibratable","hash","decided"]}
@@ -1,4 +1,4 @@
1
- import{a as l,b as m}from"./chunk-HR4JSRZS.js";import"./chunk-4YCH6IZV.js";import{execFile as f}from"child_process";var g=3e4,w="haiku",M=`You are about to lose this working session due to context-size rotation. Before that happens, write a compact memo of what you should carry forward to your next session on the same conversation.
1
+ import{a as l,b as m}from"./chunk-EVWEG3VD.js";import"./chunk-FIOB3MEX.js";import{execFile as f}from"child_process";var g=3e4,w="haiku",M=`You are about to lose this working session due to context-size rotation. Before that happens, write a compact memo of what you should carry forward to your next session on the same conversation.
2
2
 
3
3
  Include only durable facts a future turn would otherwise have to rediscover:
4
4
  - Names, handles, agent ids, telegram/whatsapp/gitlab usernames
@@ -10,4 +10,4 @@ Include only durable facts a future turn would otherwise have to rediscover:
10
10
  Format: 3-12 short bullet points. Each \u2264120 chars. No prose, no preamble, no header. If the session was uninformative or just casual chat, reply with exactly the literal text "(no memo)".
11
11
 
12
12
  Begin the memo now.`;async function R(n,r){let c=Date.now();if(!r)return{memo:null,reason:"no-resume",durationMs:0};let i=["-p",M,"--output-format","json","--resume",r,"--model",w];n.permissionMode==="bypassPermissions"&&i.push("--dangerously-skip-permissions");let p=m(l(n.workspace));return new Promise(d=>{let a=!1,e=(t,s)=>{a||(a=!0,d({memo:t,reason:s,durationMs:Date.now()-c}))};f("claude",i,{cwd:n.workspace,env:p,timeout:g,maxBuffer:1024*1024},(t,s,b)=>{if(t)return t.killed?e(null,"timeout"):e(null,`error:${t.message.slice(0,80)}`);try{let u=JSON.parse(s||"{}"),o=typeof u.result=="string"?u.result.trim():"";if(!o||o==="(no memo)")return e(null,"empty");let h=o.length>4096?o.slice(0,4096)+"\u2026":o;return e(h,"ok")}catch{return e(null,"parse-error")}}).on("error",()=>e(null,"spawn-error"))})}export{R as extractRotationMemo};
13
- //# sourceMappingURL=rotation-memo-B3EXXSEH.js.map
13
+ //# sourceMappingURL=rotation-memo-7T75BLEX.js.map
@@ -0,0 +1,2 @@
1
+ import{v as l,w as p}from"./chunk-AX2PWSRT.js";import"./chunk-AJONZWLT.js";import{a as i,f as w}from"./chunk-F4HZTZHW.js";import"./chunk-ZTEU5JVQ.js";import"./chunk-LCIREYTD.js";import"./chunk-L2WSPY6S.js";import"./chunk-CJQQIRPD.js";import"./chunk-BH3GTCZ6.js";import"./chunk-FIOB3MEX.js";p();w();var u="task-tier";function g(e){return{task:m(e.message,2e3),agent:e.agent,channel:e.channel??null,continuesExistingSession:e.isFollowUp??!1,...e.toolsAvailable?{toolsAvailable:e.toolsAvailable}:{}}}var d={needsFlagship:i("This task needs the strongest available model to be done correctly.",{true:"Doing it well takes real reasoning: writing or reviewing code, diagnosing a failure, planning work across several steps, weighing a judgement, handling money, touching production or anything irreversible, following a long or ambiguous instruction, or work where being subtly wrong would not be noticed. Anything where a weaker model would produce something that LOOKS right belongs here.",false:"It is mechanical and its result is obvious on sight: acknowledging or confirming a message, restating or formatting something already written, a greeting, a status echo, reading one file and reporting what it says, a lookup with one right answer, a scheduled heartbeat with nothing to decide."})};function h(e){return e.needsFlagship.noul}function m(e,n){return e.length>n?`${e.slice(0,n)}\u2026`:e}var c=.2,T=60*60*1e3;async function F(e){let n=(r,o=null)=>({downgraded:!1,needsFlagship:o,reason:r});if(!e.cheapModel)return n("no cheaper model configured");if(e.isFollowUp){let r=e.sessionIdleMs??0,o=e.cacheTtlMs??T;if(r<o)return n(`follow-up on a warm cache (idle ${Math.round(r/1e3)}s) \u2014 swapping would cost more than it saves`)}let s;try{s=await l(u,g(e),d,{incumbent:{needsFlagship:1},features:{agent:e.agent,channel:e.channel??"unknown"}})}catch{return n("routing seat threw \u2014 keeping the flagship")}if(!s)return n("routing seat unavailable \u2014 keeping the flagship");let t=h(s.answers);if(s.mode!=="active")return n(`shadow \u2014 would ${t<(e.threshold??c)?"downgrade":"keep"} (${t.toFixed(2)})`,t);let a=e.threshold??c;return t>=a?n(`needs the flagship (${t.toFixed(2)})`,t):{model:e.cheapModel,downgraded:!0,needsFlagship:t,reason:`mechanical task (${t.toFixed(2)} below ${a}) \u2192 ${e.cheapModel}`}}export{F as routeTaskModel};
2
+ //# sourceMappingURL=routing-4PV66BK6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/agents/routing.ts","../src/decisions/seats/task-tier.ts"],"sourcesContent":["import { askSeat } from \"@/decisions/seat\"\nimport {\n TASK_TIER_SEAT,\n taskTierQuestions,\n taskTierState,\n needsFlagship,\n type TaskTierAnswers,\n type TaskTierInput,\n} from \"@/decisions/seats/task-tier\"\n\n// Picking a cheaper model for work that does not need an expensive one.\n//\n// The policy, in one line: keep the flagship unless the seat is ACTIVE and\n// confidently says the task is mechanical.\n//\n// Every branch that is not that leads to the flagship — seat off, seat in\n// shadow, backend unreachable, answer above the threshold, no cheap model\n// configured, a follow-up turn. That is not defensiveness for its own\n// sake. Downgrading is invisible in the output: the reply still arrives,\n// still reads fluently, and is simply worse in ways nobody checks. An\n// outage that silently made the whole fleet dumber would cost far more\n// than the tokens it saved, so the failure has to land on the expensive\n// side.\n//\n// Follow-ups are where this gets interesting, and the answer is\n// arithmetic rather than caution.\n//\n// Switching models mid-session is mechanically trivial — the session is a\n// transcript and the model is a per-invocation flag, which is exactly what\n// Claude Code's /model does. The cost is that the PROMPT CACHE IS PER\n// MODEL. Resuming on a different model means that model has no cache for\n// the transcript and reads all of it at full price.\n//\n// Published rates, opus-5 against haiku-4.5:\n//\n// opus cached read $0.50/M haiku uncached read $1.00/M\n// opus output $25.00/M haiku output $5.00/M\n//\n// So the swap costs 2x more on input and saves $20/M on output, and it is\n// cheaper only when\n//\n// session_tokens < 40 x reply_tokens\n//\n// A task worth downgrading is by definition one whose answer is short —\n// an acknowledgement, a status echo. Ten tokens of reply puts the\n// break-even at a 400-token session. On any real conversation the swap\n// LOSES, and it loses precisely on the traffic this seat exists to catch.\n//\n// That reverses once the cache is COLD. After the cache TTL there is no\n// cached read to give up: opus pays $5.00/M and haiku $1.00/M, and the\n// cheap model wins outright. So a follow-up is routable exactly when the\n// session has been idle long enough that nothing is cached any more.\n\n/** Below this P(needs the strongest model), a cheaper one will do.\n *\n * Low on purpose. This is not the point at which a small model becomes\n * adequate — it is the point past which being wrong is cheap, and the\n * asymmetry between \"saved a fraction of a cent\" and \"gave a subtly wrong\n * answer nobody caught\" is not close. */\nconst DOWNGRADE_BELOW = 0.2\n\n/** How long a cached prefix is assumed to live. Anthropic's default TTL is\n * five minutes with a one-hour option; an hour is assumed here because it\n * is the assumption whose error is cheap — see the note above. */\nconst CACHE_TTL_MS = 60 * 60 * 1000\n\nexport interface RouteResult {\n /** The model to use, or undefined to leave the agent's own default. */\n model?: string\n /** True when this call moved the task off the flagship. */\n downgraded: boolean\n /** P(needs the strongest model), or null when nothing was asked. */\n needsFlagship: number | null\n reason: string\n}\n\nexport interface RouteOptions extends TaskTierInput {\n /** The model to drop to. Usually from config; no downgrade without it. */\n cheapModel?: string | null\n threshold?: number\n /** Idle time on the session being resumed, or null when starting fresh.\n * Only consulted for follow-ups. */\n sessionIdleMs?: number | null\n /** How long a cached prefix is assumed to survive. */\n cacheTtlMs?: number\n}\n\n/**\n * Decide whether this task can run on a cheaper model.\n *\n * Never throws and never blocks: a routing decision must not be able to\n * stop a task from running.\n */\nexport async function routeTaskModel(opts: RouteOptions): Promise<RouteResult> {\n const keep = (reason: string, p: number | null = null): RouteResult => ({\n downgraded: false, needsFlagship: p, reason,\n })\n\n if (!opts.cheapModel) return keep(\"no cheaper model configured\")\n\n // A follow-up onto a WARM cache is the losing case above. Idle longer\n // than the TTL and there is nothing left to lose, so it is routable.\n //\n // One hour, not the five-minute default, because the assumption has to\n // be the one that fails safely: over-estimating how long the cache lives\n // means occasionally keeping the flagship when a swap would have been\n // free, while under-estimating means routinely paying double on input.\n if (opts.isFollowUp) {\n const idle = opts.sessionIdleMs ?? 0\n const ttl = opts.cacheTtlMs ?? CACHE_TTL_MS\n if (idle < ttl) {\n return keep(`follow-up on a warm cache (idle ${Math.round(idle / 1000)}s) — swapping would cost more than it saves`)\n }\n }\n\n let result\n try {\n result = await askSeat(\n TASK_TIER_SEAT,\n taskTierState(opts),\n taskTierQuestions,\n {\n // The incumbent is \"always flagship\", which is what this has to beat.\n incumbent: { needsFlagship: 1 },\n features: { agent: opts.agent, channel: opts.channel ?? \"unknown\" },\n },\n )\n } catch {\n return keep(\"routing seat threw — keeping the flagship\")\n }\n\n if (!result) return keep(\"routing seat unavailable — keeping the flagship\")\n\n const p = needsFlagship(result.answers as TaskTierAnswers)\n\n // Shadow records the answer and changes nothing. This is the whole\n // point of a soak: the rows accumulate against real traffic while the\n // incumbent stays authoritative, so promotion is a decision made on\n // evidence rather than on how good the idea sounded.\n if (result.mode !== \"active\") {\n return keep(`shadow — would ${p < (opts.threshold ?? DOWNGRADE_BELOW) ? \"downgrade\" : \"keep\"} (${p.toFixed(2)})`, p)\n }\n\n const threshold = opts.threshold ?? DOWNGRADE_BELOW\n if (p >= threshold) return keep(`needs the flagship (${p.toFixed(2)})`, p)\n\n return {\n model: opts.cheapModel,\n downgraded: true,\n needsFlagship: p,\n reason: `mechanical task (${p.toFixed(2)} below ${threshold}) → ${opts.cheapModel}`,\n }\n}\n","import { noul } from \"../questions\"\nimport type { AnswersFor, NoulAnswer, StateValue } from \"../types\"\n\n// Whether a task needs the flagship model.\n//\n// Every claude-code agent on both nodes runs claude-opus-5. Much of what\n// they actually do is not hard: acknowledging a message, reading a file\n// and saying what is in it, restating a status, routing a mention. Those\n// turns cost flagship rates for work a small model finishes correctly.\n//\n// A single Noul, not a Choice over model names, and that is deliberate.\n// The models available differ per node, per tier and per provider, and a\n// Choice whose options are a fleet's current model list is a decision that\n// silently changes meaning every time someone edits config. The question\n// that stays stable is about the TASK: does this need the strongest model\n// available. Mapping that answer onto a specific model is policy, and\n// policy belongs in code where it can be read.\n//\n// Direction of failure is the whole design. A seat is fail-open by\n// contract — askSeat returns null when it is off or unreachable — and here\n// \"open\" must mean KEEP THE FLAGSHIP. A missing answer that quietly\n// downgrades a model would turn an outage into silently worse work across\n// the fleet, which is far more expensive than the tokens it saved. So the\n// caller only ever downgrades on an explicit, confident \"no\".\n\nexport const TASK_TIER_SEAT = \"task-tier\"\n\nexport interface TaskTierInput {\n /** The task, in the words it arrived in. */\n message: string\n agent: string\n /** Where it came from — a cron heartbeat and a customer message are not\n * the same kind of work even when they read alike. */\n channel?: string | null\n /** True when this continues an existing session. A follow-up inherits\n * the difficulty of what it is following up on, and switching models\n * mid-conversation is its own hazard. */\n isFollowUp?: boolean\n /** Tools the agent could reach for. A task that will touch production\n * is not a cheap task however simply it is phrased. */\n toolsAvailable?: number\n}\n\nexport function taskTierState(input: TaskTierInput): StateValue {\n return {\n task: clip(input.message, 2000),\n agent: input.agent,\n channel: input.channel ?? null,\n continuesExistingSession: input.isFollowUp ?? false,\n ...(input.toolsAvailable ? { toolsAvailable: input.toolsAvailable } : {}),\n }\n}\n\nexport const taskTierQuestions = {\n needsFlagship: noul(\n \"This task needs the strongest available model to be done correctly.\",\n {\n true:\n \"Doing it well takes real reasoning: writing or reviewing code, diagnosing a failure, planning work across several steps, weighing a judgement, handling money, touching production or anything irreversible, following a long or ambiguous instruction, or work where being subtly wrong would not be noticed. Anything where a weaker model would produce something that LOOKS right belongs here.\",\n false:\n \"It is mechanical and its result is obvious on sight: acknowledging or confirming a message, restating or formatting something already written, a greeting, a status echo, reading one file and reporting what it says, a lookup with one right answer, a scheduled heartbeat with nothing to decide.\",\n },\n ),\n}\n\nexport type TaskTierAnswers = AnswersFor<typeof taskTierQuestions>\n\nexport function needsFlagship(answers: TaskTierAnswers): number {\n return (answers.needsFlagship as NoulAnswer).noul\n}\n\nfunction clip(s: string, max: number): string {\n return s.length > max ? `${s.slice(0, max)}…` : s\n}\n"],"mappings":"kSAAAA,ICAAC,IAyBO,IAAMC,EAAiB,YAkBvB,SAASC,EAAcC,EAAkC,CAC9D,MAAO,CACL,KAAMC,EAAKD,EAAM,QAAS,GAAI,EAC9B,MAAOA,EAAM,MACb,QAASA,EAAM,SAAW,KAC1B,yBAA0BA,EAAM,YAAc,GAC9C,GAAIA,EAAM,eAAiB,CAAE,eAAgBA,EAAM,cAAe,EAAI,CAAC,CACzE,CACF,CAEO,IAAME,EAAoB,CAC/B,cAAeC,EACb,sEACA,CACE,KACE,sYACF,MACE,sSACJ,CACF,CACF,EAIO,SAASC,EAAcC,EAAkC,CAC9D,OAAQA,EAAQ,cAA6B,IAC/C,CAEA,SAASJ,EAAKK,EAAWC,EAAqB,CAC5C,OAAOD,EAAE,OAASC,EAAM,GAAGD,EAAE,MAAM,EAAGC,CAAG,UAAOD,CAClD,CDdA,IAAME,EAAkB,GAKlBC,EAAe,GAAK,GAAK,IA6B/B,eAAsBC,EAAeC,EAA0C,CAC7E,IAAMC,EAAO,CAACC,EAAgBC,EAAmB,QAAuB,CACtE,WAAY,GAAO,cAAeA,EAAG,OAAAD,CACvC,GAEA,GAAI,CAACF,EAAK,WAAY,OAAOC,EAAK,6BAA6B,EAS/D,GAAID,EAAK,WAAY,CACnB,IAAMI,EAAOJ,EAAK,eAAiB,EAC7BK,EAAML,EAAK,YAAcF,EAC/B,GAAIM,EAAOC,EACT,OAAOJ,EAAK,mCAAmC,KAAK,MAAMG,EAAO,GAAI,mDAA8C,EAIvH,IAAIE,EACJ,GAAI,CACFA,EAAS,MAAMC,EACbC,EACAC,EAAcT,CAAI,EAClBU,EACA,CAEE,UAAW,CAAE,cAAe,CAAE,EAC9B,SAAU,CAAE,MAAOV,EAAK,MAAO,QAASA,EAAK,SAAW,SAAU,CACpE,CACF,CACF,MAAE,CACA,OAAOC,EAAK,gDAA2C,CACzD,CAEA,GAAI,CAACK,EAAQ,OAAOL,EAAK,sDAAiD,EAE1E,IAAME,EAAIQ,EAAcL,EAAO,OAA0B,EAMzD,GAAIA,EAAO,OAAS,SAClB,OAAOL,EAAK,uBAAkBE,GAAKH,EAAK,WAAaH,GAAmB,YAAc,WAAWM,EAAE,QAAQ,CAAC,KAAMA,CAAC,EAGrH,IAAMS,EAAYZ,EAAK,WAAaH,EACpC,OAAIM,GAAKS,EAAkBX,EAAK,uBAAuBE,EAAE,QAAQ,CAAC,KAAMA,CAAC,EAElE,CACL,MAAOH,EAAK,WACZ,WAAY,GACZ,cAAeG,EACf,OAAQ,oBAAoBA,EAAE,QAAQ,CAAC,WAAWS,aAAgBZ,EAAK,YACzE,CACF","names":["init_seat","init_questions","TASK_TIER_SEAT","taskTierState","input","clip","taskTierQuestions","noul","needsFlagship","answers","s","max","DOWNGRADE_BELOW","CACHE_TTL_MS","routeTaskModel","opts","keep","reason","p","idle","ttl","result","askSeat","TASK_TIER_SEAT","taskTierState","taskTierQuestions","needsFlagship","threshold"]}
@@ -0,0 +1,2 @@
1
+ import{a,b,c}from"./chunk-SZDACK6O.js";import"./chunk-FIOB3MEX.js";export{b as resolveInputs,c as runAction,a as template};
2
+ //# sourceMappingURL=runner-YRBVNYTY.js.map
@@ -0,0 +1,2 @@
1
+ import{g as a,h as b}from"./chunk-FIEJDZVC.js";import"./chunk-2T5W3ETS.js";import"./chunk-ZTEU5JVQ.js";import"./chunk-L2WSPY6S.js";import"./chunk-CJQQIRPD.js";import"./chunk-FIOB3MEX.js";export{b as CronScheduler,a as getNextCronDate};
2
+ //# sourceMappingURL=scheduler-GZZAN6ZG.js.map
@@ -0,0 +1,2 @@
1
+ import{o as a,p as b,q as c,r as d,s as e,t as f,u as g,v as h,w as i}from"./chunk-AX2PWSRT.js";import"./chunk-AJONZWLT.js";import"./chunk-F4HZTZHW.js";import"./chunk-ZTEU5JVQ.js";import"./chunk-LCIREYTD.js";import"./chunk-L2WSPY6S.js";import"./chunk-CJQQIRPD.js";import"./chunk-BH3GTCZ6.js";import"./chunk-FIOB3MEX.js";i();export{h as askSeat,a as configureDecisions,c as decisionsRuntime,f as getSeatMode,d as parseSeatMode,g as recordSeatOutcome,b as resetDecisionsRuntime,e as seatEnvVar};
2
+ //# sourceMappingURL=seat-VZ7AVO27.js.map
@@ -0,0 +1,2 @@
1
+ import{v as S,w}from"./chunk-AX2PWSRT.js";import"./chunk-AJONZWLT.js";import{a as g,b as h,f as R}from"./chunk-F4HZTZHW.js";import"./chunk-ZTEU5JVQ.js";import"./chunk-LCIREYTD.js";import"./chunk-L2WSPY6S.js";import{d as f}from"./chunk-VDMYBPYB.js";import"./chunk-CJQQIRPD.js";import"./chunk-BH3GTCZ6.js";import"./chunk-FIOB3MEX.js";w();R();var p="skill-select";function y(l){return{task:d(l.task,1200),...l.agent?{agent:l.agent}:{},skills:l.candidates.map(t=>({name:t.name,purpose:d(t.description,300),...t.category?{category:t.category}:{},...t.tags?.length?{tags:t.tags.slice(0,8)}:{}}))}}function b(l){let t={};for(let i of l)t[i.name]=d(i.description,300);return{anyRelevant:g("At least one of these skills is about the task at hand.",{true:"The task is the kind of work one of these skills exists for, and following it would change how the work is done \u2014 it names the same system, the same procedure or the same kind of artefact.",false:"None of them is about this task. They may share vocabulary with it, mention the same tools, or belong to the same project without bearing on what is being asked. Sharing words with a task is not being about it."}),best:h(t,"Which skill is this task most about?")}}function v(l){let t=l.anyRelevant,i=l.best,o=Object.entries(i.probabilities??{}).map(([e,s])=>({name:e,p:Number(s)||0})).sort((e,s)=>s.p-e.p);return{anyRelevant:t.noul,name:i.choice,confidence:i.confidence,ranked:o}}function d(l,t){return l.length>t?`${l.slice(0,t)}\u2026`:l}var x=.1,A=12;async function E(l,t,i={}){let o=i.minRelevant??.6,e=f(l,t).filter(n=>n.relevance>=x).slice(0,A),s=e.map(n=>({name:n.skill.frontmatter.name,lexical:n.relevance}));if(e.length===0)return{skill:null,anyRelevant:0,confidence:0,shortlist:s,judged:!1,reason:"no skill mentions anything in this task"};if(e.length===1)return{skill:e[0].skill,anyRelevant:e[0].relevance,confidence:e[0].relevance,shortlist:s,judged:!1,reason:`only ${e[0].skill.frontmatter.name} matched (${e[0].matchReason})`};let r=e.map(n=>({name:n.skill.frontmatter.name,description:n.skill.frontmatter.description,category:n.skill.frontmatter.category??null,tags:n.skill.frontmatter.tags})),m=await S(p,y({task:t,agent:i.agent??null,candidates:r}),b(r),{incumbent:{best:r[0].name},features:{agent:i.agent??"unknown",candidates:r.length}});if(!m)return{skill:e[0].skill,anyRelevant:e[0].relevance,confidence:e[0].relevance,shortlist:s,judged:!1,reason:`seat off \u2014 lexical top match (${e[0].matchReason})`};let a=v(m.answers),c=s.map(n=>({...n,p:a.ranked.find(k=>k.name===n.name)?.p??0})).sort((n,k)=>(k.p??0)-(n.p??0));if(a.anyRelevant<o)return{skill:null,anyRelevant:a.anyRelevant,confidence:a.confidence,shortlist:c,judged:!0,reason:`nothing on the shortlist is about this task (${a.anyRelevant.toFixed(2)} below ${o})`};let u=e.find(n=>n.skill.frontmatter.name===a.name);return u?{skill:u.skill,anyRelevant:a.anyRelevant,confidence:a.confidence,shortlist:c,judged:!0,reason:`${a.name} (confidence ${a.confidence.toFixed(2)})`}:{skill:null,anyRelevant:a.anyRelevant,confidence:a.confidence,shortlist:c,judged:!0,reason:`seat chose "${a.name}", which was not on the shortlist`}}export{E as pickSkillForTask};
2
+ //# sourceMappingURL=select-F5I5PO2O.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/agent/skills/select.ts","../src/decisions/seats/skill-select.ts"],"sourcesContent":["import { askSeat } from \"@/decisions/seat\"\nimport {\n SKILL_SELECT_SEAT,\n skillSelectQuestions,\n skillSelectState,\n toSkillSelection,\n type SkillCandidate,\n type SkillSelectAnswers,\n} from \"@/decisions/seats/skill-select\"\nimport { matchSkillsToTask } from \"./loader\"\nimport type { Skill } from \"./types\"\n\n// Choosing a skill for a task, in two stages.\n//\n// 1. matchSkillsToTask — lexical, free, no network. Narrows a hundred\n// skills to a handful. Good at recall, indifferent about precision.\n// 2. the skill-select seat — judgement over that handful.\n//\n// Same division as wiki-rerank, and for the same reason: the cheap stage\n// is the one that has to look at everything.\n//\n// Stage 1 only sees skills whose author wrote trigger patterns or tags, so\n// it is also the stage that limits what is reachable. SHORTLIST_FLOOR is\n// deliberately lower than the injection threshold the incumbent uses —\n// recall is stage one's job, and the seat is what says no.\n\n/** Below this the lexical matcher is reporting noise, but the bar is low\n * on purpose: a skill the shortlist never mentions cannot be chosen. */\nconst SHORTLIST_FLOOR = 0.1\n\n/** How many the seat is asked to judge. Beyond a dozen the request grows\n * without the answer improving — the lexical stage is not sorting well\n * enough for the twelfth candidate to be a contender. */\nconst SHORTLIST_SIZE = 12\n\nexport interface SkillPick {\n /** The chosen skill, or null when nothing applies. */\n skill: Skill | null\n /** P(any shortlisted skill is about this task). */\n anyRelevant: number\n confidence: number\n /** What the lexical stage put forward, best first. */\n shortlist: Array<{ name: string; lexical: number; p?: number }>\n /** True when the seat answered; false when this is the lexical result\n * alone, because a seat that is off must not look like a judgement. */\n judged: boolean\n reason: string\n}\n\n/**\n * Rank a workspace's skills against a task.\n *\n * Returns a pick, never applies it — whether a skill is loaded, suggested\n * or ignored is the caller's policy, and this deliberately has no opinion\n * about per-turn context injection.\n */\nexport async function pickSkillForTask(\n skills: Skill[],\n task: string,\n opts: { agent?: string | null; minRelevant?: number } = {},\n): Promise<SkillPick> {\n const minRelevant = opts.minRelevant ?? 0.6\n\n const lexical = matchSkillsToTask(skills, task)\n .filter((m) => m.relevance >= SHORTLIST_FLOOR)\n .slice(0, SHORTLIST_SIZE)\n\n const shortlist = lexical.map((m) => ({\n name: m.skill.frontmatter.name,\n lexical: m.relevance,\n }))\n\n if (lexical.length === 0) {\n return {\n skill: null, anyRelevant: 0, confidence: 0, shortlist, judged: false,\n reason: \"no skill mentions anything in this task\",\n }\n }\n\n // One candidate is not a choice. Still worth asking whether it applies,\n // but a Choice over a single option carries no information, so the\n // lexical score stands and the Noul is not paid for either.\n if (lexical.length === 1) {\n return {\n skill: lexical[0].skill, anyRelevant: lexical[0].relevance,\n confidence: lexical[0].relevance, shortlist, judged: false,\n reason: `only ${lexical[0].skill.frontmatter.name} matched (${lexical[0].matchReason})`,\n }\n }\n\n const candidates: SkillCandidate[] = lexical.map((m) => ({\n name: m.skill.frontmatter.name,\n description: m.skill.frontmatter.description,\n category: m.skill.frontmatter.category ?? null,\n tags: m.skill.frontmatter.tags,\n }))\n\n const result = await askSeat(\n SKILL_SELECT_SEAT,\n skillSelectState({ task, agent: opts.agent ?? null, candidates }),\n skillSelectQuestions(candidates),\n {\n // What the lexical matcher would have done, so agreement between the\n // two is measurable rather than assumed.\n incumbent: { best: candidates[0].name },\n features: { agent: opts.agent ?? \"unknown\", candidates: candidates.length },\n },\n )\n\n if (!result) {\n return {\n skill: lexical[0].skill, anyRelevant: lexical[0].relevance,\n confidence: lexical[0].relevance, shortlist, judged: false,\n reason: `seat off — lexical top match (${lexical[0].matchReason})`,\n }\n }\n\n const selection = toSkillSelection(result.answers as SkillSelectAnswers)\n const ranked = shortlist.map((s) => ({\n ...s,\n p: selection.ranked.find((r) => r.name === s.name)?.p ?? 0,\n })).sort((a, b) => (b.p ?? 0) - (a.p ?? 0))\n\n // Read anyRelevant FIRST. The Choice named something regardless.\n if (selection.anyRelevant < minRelevant) {\n return {\n skill: null, anyRelevant: selection.anyRelevant, confidence: selection.confidence,\n shortlist: ranked, judged: true,\n reason: `nothing on the shortlist is about this task (${selection.anyRelevant.toFixed(2)} below ${minRelevant})`,\n }\n }\n\n const chosen = lexical.find((m) => m.skill.frontmatter.name === selection.name)\n if (!chosen) {\n // Named something outside the option set. Refuse rather than guess.\n return {\n skill: null, anyRelevant: selection.anyRelevant, confidence: selection.confidence,\n shortlist: ranked, judged: true,\n reason: `seat chose \"${selection.name}\", which was not on the shortlist`,\n }\n }\n\n return {\n skill: chosen.skill, anyRelevant: selection.anyRelevant,\n confidence: selection.confidence, shortlist: ranked, judged: true,\n reason: `${selection.name} (confidence ${selection.confidence.toFixed(2)})`,\n }\n}\n","import { choice, noul } from \"../questions\"\nimport type { AnswersFor, ChoiceAnswer, NoulAnswer, StateValue } from \"../types\"\n\n// Which skill, if any, a task should start from.\n//\n// The incumbent is matchSkillsToTask() in agent/skills/loader.ts: regex\n// triggers at 0.9, tag overlap at 0.3 per tag capped at 0.8, shared words\n// over four characters at 0.2 each capped at 0.6, and anything over 0.1\n// gets used. Those numbers are not wrong so much as unexamined — they were\n// chosen to make a lexical score come out roughly sorted, and they answer\n// \"which skill shares the most words with this sentence\", which is a\n// different question from \"which skill is this task about\".\n//\n// The lexical matcher stays. It is free, it runs with no network, and it\n// is a perfectly good way to narrow a hundred skills down to a handful.\n// This seat re-ranks that handful, which is the same division of labour as\n// wiki-rerank: cheap recall first, judgement second.\n//\n// `anyRelevant` is read FIRST and is the more valuable of the two answers.\n// A Choice over skills always names one, and the failure that matters here\n// is not picking the wrong skill — it is loading a skill at all for a task\n// that needed none, which spends context on instructions that do not\n// apply and quietly steers the agent toward them.\n//\n// One thing this seat can do that the incumbent cannot: rank by what a\n// skill is FOR. The lexical path needs an author to have written trigger\n// patterns and tags by hand, and across ten agent workspaces and 101 skill\n// files, exactly zero have opted into auto-injection. A description is\n// something every skill already has.\n\nexport const SKILL_SELECT_SEAT = \"skill-select\"\n\n/** Enough to judge a skill by, small enough that a dozen fit in one\n * request. The instructions themselves are deliberately excluded — they\n * are thousands of tokens each and the description is what says what the\n * skill is for. */\nexport interface SkillCandidate {\n name: string\n description: string\n category?: string | null\n tags?: string[]\n}\n\nexport interface SkillSelectInput {\n /** The task, in the words it arrived in. */\n task: string\n /** Which agent is about to run it — a deploy skill means something\n * different to devops than to a content writer. */\n agent?: string | null\n candidates: SkillCandidate[]\n}\n\nexport function skillSelectState(input: SkillSelectInput): StateValue {\n return {\n task: clip(input.task, 1200),\n ...(input.agent ? { agent: input.agent } : {}),\n skills: input.candidates.map((c) => ({\n name: c.name,\n purpose: clip(c.description, 300),\n ...(c.category ? { category: c.category } : {}),\n ...(c.tags?.length ? { tags: c.tags.slice(0, 8) } : {}),\n })),\n }\n}\n\n/** Built per call — the option set IS the shortlist. */\nexport function skillSelectQuestions(candidates: SkillCandidate[]) {\n const criteria: Record<string, string> = {}\n for (const c of candidates) criteria[c.name] = clip(c.description, 300)\n\n return {\n anyRelevant: noul(\n \"At least one of these skills is about the task at hand.\",\n {\n true:\n \"The task is the kind of work one of these skills exists for, and following it would change how the work is done — it names the same system, the same procedure or the same kind of artefact.\",\n false:\n \"None of them is about this task. They may share vocabulary with it, mention the same tools, or belong to the same project without bearing on what is being asked. Sharing words with a task is not being about it.\",\n },\n ),\n best: choice(criteria, \"Which skill is this task most about?\"),\n }\n}\n\nexport type SkillSelectAnswers = AnswersFor<ReturnType<typeof skillSelectQuestions>>\n\nexport interface SkillSelection {\n /** P(any of the shortlist applies). Read before `name`. */\n anyRelevant: number\n name: string\n /** Derived from the shape of the probabilities, never self-reported. */\n confidence: number\n ranked: Array<{ name: string; p: number }>\n}\n\nexport function toSkillSelection(answers: SkillSelectAnswers): SkillSelection {\n const any = answers.anyRelevant as NoulAnswer\n const best = answers.best as ChoiceAnswer\n const ranked = Object.entries(best.probabilities ?? {})\n .map(([name, p]) => ({ name, p: Number(p) || 0 }))\n .sort((a, b) => b.p - a.p)\n return {\n anyRelevant: any.noul,\n name: best.choice,\n confidence: best.confidence,\n ranked,\n }\n}\n\nfunction clip(s: string, max: number): string {\n return s.length > max ? `${s.slice(0, max)}…` : s\n}\n"],"mappings":"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","names":["init_seat","init_questions","SKILL_SELECT_SEAT","skillSelectState","input","clip","c","skillSelectQuestions","candidates","criteria","noul","choice","toSkillSelection","answers","any","best","ranked","name","p","a","b","s","max","SHORTLIST_FLOOR","SHORTLIST_SIZE","pickSkillForTask","skills","task","opts","minRelevant","lexical","matchSkillsToTask","m","shortlist","candidates","result","askSeat","SKILL_SELECT_SEAT","skillSelectState","skillSelectQuestions","selection","toSkillSelection","ranked","s","r","a","b","chosen"]}
@@ -0,0 +1,2 @@
1
+ import{a}from"./chunk-GV7S5BAX.js";import"./chunk-GSX4NK2N.js";import"./chunk-FIOB3MEX.js";export{a as ActionStore};
2
+ //# sourceMappingURL=store-MCOA6N4F.js.map
@@ -1,3 +1,3 @@
1
- import"./chunk-4YCH6IZV.js";var g=class{registry;activeRuns=new Map;runCounter=0;log;depthMap=new Map;constructor(t,e=console.error.bind(console,"[subagent]")){this.registry=t,this.log=e}async spawn(t){let e=this.depthMap.get(t.parentAgentId)||0;if(e>=3)return{runId:"",targetAgentId:t.targetAgentId,success:!1,content:"",error:`Sub-agent spawn depth exceeded (max: 3). Cannot spawn "${t.targetAgentId}" from "${t.parentAgentId}".`,duration:0};let n=`sub-${++this.runCounter}-${Date.now().toString(36)}`,r={request:t,runId:n,startedAt:Date.now()};this.activeRuns.set(n,r),this.depthMap.set(t.targetAgentId,e+1),this.log(`Spawning sub-agent: ${t.targetAgentId} (parent: ${t.parentAgentId}, depth: ${e+1}, blocking: ${t.blocking??!1})`);let o={message:this.buildSubAgentPrompt(t),agentId:t.targetAgentId,context:{channel:"subagent",sender:`agent:${t.parentAgentId}`,chatId:`subagent:${n}`}},i=t.timeout??3e5;try{let a=await Promise.race([this.registry.execute(o),this.timeoutPromise(i,t.targetAgentId)]),s={runId:n,targetAgentId:t.targetAgentId,success:!a.error,content:a.content,error:a.error,duration:Date.now()-r.startedAt};return this.log(`Sub-agent ${t.targetAgentId} completed in ${s.duration}ms (${s.success?"success":"error"})`),s}catch(a){return{runId:n,targetAgentId:t.targetAgentId,success:!1,content:"",error:a.message,duration:Date.now()-r.startedAt}}finally{this.activeRuns.delete(n),this.depthMap.delete(t.targetAgentId)}}async spawnParallel(t){return Promise.all(t.map(e=>this.spawn(e)))}listActive(){return Array.from(this.activeRuns.values()).map(t=>({runId:t.runId,targetAgentId:t.request.targetAgentId,parentAgentId:t.request.parentAgentId,duration:Date.now()-t.startedAt}))}buildSubAgentPrompt(t){return[`[Sub-agent task from ${t.parentAgentId}]`,"You have been spawned as a background sub-agent to handle a specific task.","Focus on completing the task below and provide a clear, actionable response.","",t.prompt].join(`
1
+ import"./chunk-FIOB3MEX.js";var g=class{registry;activeRuns=new Map;runCounter=0;log;depthMap=new Map;constructor(t,e=console.error.bind(console,"[subagent]")){this.registry=t,this.log=e}async spawn(t){let e=this.depthMap.get(t.parentAgentId)||0;if(e>=3)return{runId:"",targetAgentId:t.targetAgentId,success:!1,content:"",error:`Sub-agent spawn depth exceeded (max: 3). Cannot spawn "${t.targetAgentId}" from "${t.parentAgentId}".`,duration:0};let n=`sub-${++this.runCounter}-${Date.now().toString(36)}`,r={request:t,runId:n,startedAt:Date.now()};this.activeRuns.set(n,r),this.depthMap.set(t.targetAgentId,e+1),this.log(`Spawning sub-agent: ${t.targetAgentId} (parent: ${t.parentAgentId}, depth: ${e+1}, blocking: ${t.blocking??!1})`);let o={message:this.buildSubAgentPrompt(t),agentId:t.targetAgentId,context:{channel:"subagent",sender:`agent:${t.parentAgentId}`,chatId:`subagent:${n}`}},i=t.timeout??3e5;try{let a=await Promise.race([this.registry.execute(o),this.timeoutPromise(i,t.targetAgentId)]),s={runId:n,targetAgentId:t.targetAgentId,success:!a.error,content:a.content,error:a.error,duration:Date.now()-r.startedAt};return this.log(`Sub-agent ${t.targetAgentId} completed in ${s.duration}ms (${s.success?"success":"error"})`),s}catch(a){return{runId:n,targetAgentId:t.targetAgentId,success:!1,content:"",error:a.message,duration:Date.now()-r.startedAt}}finally{this.activeRuns.delete(n),this.depthMap.delete(t.targetAgentId)}}async spawnParallel(t){return Promise.all(t.map(e=>this.spawn(e)))}listActive(){return Array.from(this.activeRuns.values()).map(t=>({runId:t.runId,targetAgentId:t.request.targetAgentId,parentAgentId:t.request.parentAgentId,duration:Date.now()-t.startedAt}))}buildSubAgentPrompt(t){return[`[Sub-agent task from ${t.parentAgentId}]`,"You have been spawned as a background sub-agent to handle a specific task.","Focus on completing the task below and provide a clear, actionable response.","",t.prompt].join(`
2
2
  `)}timeoutPromise(t,e){return new Promise((n,r)=>{setTimeout(()=>{r(new Error(`Sub-agent "${e}" timed out after ${Math.round(t/1e3)}s`))},t)})}};export{g as SubAgentManager};
3
- //# sourceMappingURL=subagent-WV7QKQ3L.js.map
3
+ //# sourceMappingURL=subagent-2SJHSSJS.js.map
@@ -1,2 +1,2 @@
1
- import{a,b,c,d}from"./chunk-UCQ2N3KO.js";import"./chunk-M4AMWP3H.js";import"./chunk-4YCH6IZV.js";export{c as commandPath,b as listSurfaceUse,a as recordSurfaceUse,d as shouldRecordCommand};
2
- //# sourceMappingURL=surface-usage-PRL6Z4U3.js.map
1
+ import{a,b,c,d}from"./chunk-UCQ2N3KO.js";import"./chunk-M4AMWP3H.js";import"./chunk-FIOB3MEX.js";export{c as commandPath,b as listSurfaceUse,a as recordSurfaceUse,d as shouldRecordCommand};
2
+ //# sourceMappingURL=surface-usage-NS74AXSE.js.map
@@ -0,0 +1,2 @@
1
+ import{a,b}from"./chunk-AFRHKA7B.js";import"./chunk-FIOB3MEX.js";export{b as TimerService,a as timerRecordSchema};
2
+ //# sourceMappingURL=timers-PCB4ZIBD.js.map
@@ -0,0 +1,2 @@
1
+ import"./chunk-FIOB3MEX.js";function m(t,l){let a=t.filter(n=>!l.has(n.id)).map(n=>String(n.date??"")).sort(),r=a[a.length-1]??"",i=[],o=0;for(let n of t){let s=!l.has(n.id);if(!s&&String(n.date??"")>=r){o++;continue}i.push({...n,absorbed:s})}return{labeled:i,backlog:o}}function v(t,l,a,r=Math.random){let i=t.filter(l),o=t.filter(c=>!l(c)),n=Math.min(a,t.length),s=Math.min(i.length,Math.floor(n/2)),d=Math.min(o.length,n-s);return[...f(i,s,r),...f(o,d,r)]}function f(t,l,a){let r=[...t];for(let i=r.length-1;i>0;i--){let o=Math.floor(a()*(i+1));[r[i],r[o]]=[r[o],r[i]]}return r.slice(0,l)}function w(t,l={}){let a=l.minType??.5,r=l.minWorth??.5,i=l.minDurability??1.5,o=t.filter(e=>e.entry.absorbed).length,n=t.length-o,s=t.filter(e=>e.entry.absorbed&&e.verdict.absorb).length,d=t.filter(e=>!e.entry.absorbed&&e.verdict.absorb).length,c=s+d,u=t.filter(e=>e.entry.absorbed&&!e.verdict.absorb),g={type:u.filter(e=>e.verdict.type==="none"||e.verdict.typeConfidence<a).length,worth:u.filter(e=>e.verdict.worthKeeping<r).length,durability:u.filter(e=>e.verdict.durability<i).length},b=t.filter(e=>e.entry.absorbed),p=e=>b.length?b.reduce((h,y)=>h+e(y),0)/b.length:0;return{n:t.length,positives:o,negatives:n,droppedBy:g,positiveMeans:{worth:p(e=>e.verdict.worthKeeping),durability:p(e=>e.verdict.durability),typeConfidence:p(e=>e.verdict.typeConfidence)},recall:o?s/o:0,precision:c?s/c:0,filtered:t.length?1-c/t.length:0,lostPositives:o-s,accuracy:t.length?(s+(n-d))/t.length:0}}function T(t,l,a){return t.map((r,i)=>({callId:`backtest-${i}`,seat:"entry-triage",ts:Date.now(),backend:a,model:l,structureMode:"backtest",question:"worthKeeping",type:"noul",predicted:r.verdict.worthKeeping>=.5?"true":"false",confidence:Math.max(r.verdict.worthKeeping,1-r.verdict.worthKeeping),probabilities:{true:r.verdict.worthKeeping,false:1-r.verdict.worthKeeping},truth:r.entry.absorbed?"true":"false",features:{source:r.entry.source??"",type:r.verdict.type},mode:"shadow",explored:!1}))}function k(t,l={}){let a=l.minRecall??.95,r={requireTyped:[!0,!1],type:[0,.2,.3,.4,.5],worth:[0,.1,.2,.3,.4,.5],durability:[0,.5,1,1.5]},i=[];for(let n of r.requireTyped)for(let s of r.type)for(let d of r.worth)for(let c of r.durability){let u=e=>(!n||e.verdict.type!=="none"&&e.verdict.typeConfidence>=s)&&e.verdict.worthKeeping>=d&&e.verdict.durability>=c,g=t.filter(e=>e.entry.absorbed),b=g.filter(u).length,p=t.filter(u).length;i.push({requireTyped:n,minType:s,minWorth:d,minDurability:c,recall:g.length?b/g.length:0,precision:p?b/p:0,filtered:t.length?1-p/t.length:0,lostPositives:g.length-b})}let o=i.filter(n=>n.recall>=a);return o.sort((n,s)=>s.filtered-n.filtered),{best:o[0]??null,points:i}}export{m as labelEntries,v as sample,w as scoreBacktest,k as sweepPolicies,T as toGradedRows};
2
+ //# sourceMappingURL=triage-backtest-JNO7RKX7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/wiki/triage-backtest.ts"],"sourcesContent":["import type { GradedRow } from \"@/decisions/store\"\nimport type { TriageVerdict } from \"@/decisions/seats/entry-triage\"\n\n// Grading the triage gate against what absorb actually did.\n//\n// The corpus is its own test set. Absorb walks entries oldest-first and\n// writes the ones it judges worth an article, recording the entry ids in\n// the article's `sources`. So for any entry older than the newest\n// absorbed one, we know the answer: absorbed is a yes, passed over is a\n// no. On this fleet that is 815 positives and 4,811 negatives, free,\n// already on disk — and no other classification site in agentx has\n// anything like it.\n//\n// The label is Sonnet's judgement rather than a human's, which bounds\n// what this can prove: a gate that scores perfectly has learned to\n// imitate absorb, not to be right. That is still the thing we need,\n// because the gate's whole job is to spare absorb the entries it would\n// have rejected anyway. Where it disagrees, the disagreement is worth\n// reading rather than assuming either side is correct.\n\nexport interface LabeledEntry {\n id: string\n date?: string\n source?: string\n sourceContext?: string\n content: string\n /** True when absorb wrote an article citing this entry. */\n absorbed: boolean\n}\n\n/**\n * Split entries into labelled examples and not-yet-seen backlog.\n *\n * An unabsorbed entry newer than the newest absorbed one has not been\n * offered to absorb yet, so it is unlabelled — counting it as a negative\n * would score the gate against a decision nobody has made.\n */\nexport function labelEntries(\n entries: Array<{ id: string; date?: string; source?: string; sourceContext?: string; content: string }>,\n unabsorbedIds: Set<string>,\n): { labeled: LabeledEntry[]; backlog: number } {\n const absorbedDates = entries\n .filter((e) => !unabsorbedIds.has(e.id))\n .map((e) => String(e.date ?? \"\"))\n .sort()\n const newest = absorbedDates[absorbedDates.length - 1] ?? \"\"\n\n const labeled: LabeledEntry[] = []\n let backlog = 0\n for (const e of entries) {\n const absorbed = !unabsorbedIds.has(e.id)\n if (!absorbed && String(e.date ?? \"\") >= newest) { backlog++; continue }\n labeled.push({ ...e, absorbed })\n }\n return { labeled, backlog }\n}\n\n/**\n * A stratified sample, because the classes are wildly unbalanced.\n *\n * 14% of entries were absorbed. A uniform sample of 400 would carry\n * ~58 positives, and every metric that matters — recall of the things\n * worth keeping, the threshold that preserves it — would rest on those\n * 58. Sampling each class separately buys a usable positive count at the\n * same total cost; the base rate is restored when reporting.\n */\nexport function sample<T>(\n items: T[],\n isPositive: (t: T) => boolean,\n n: number,\n rng: () => number = Math.random,\n): T[] {\n const pos = items.filter(isPositive)\n const neg = items.filter((t) => !isPositive(t))\n const want = Math.min(n, items.length)\n const wantPos = Math.min(pos.length, Math.floor(want / 2))\n const wantNeg = Math.min(neg.length, want - wantPos)\n return [...pick(pos, wantPos, rng), ...pick(neg, wantNeg, rng)]\n}\n\nfunction pick<T>(items: T[], n: number, rng: () => number): T[] {\n const copy = [...items]\n for (let i = copy.length - 1; i > 0; i--) {\n const j = Math.floor(rng() * (i + 1))\n ;[copy[i], copy[j]] = [copy[j], copy[i]]\n }\n return copy.slice(0, n)\n}\n\nexport interface TriageOutcome {\n entry: LabeledEntry\n verdict: TriageVerdict\n /** P(a wiki would cite this at all) — the signal aligned with the label. */\n citeWorthy?: number\n}\n\nexport interface BacktestReport {\n n: number\n positives: number\n negatives: number\n /** Absorbed entries the gate would have kept. The number that matters:\n * a dropped positive is knowledge destroyed, silently. */\n recall: number\n /** Of what the gate kept, the share absorb also kept. */\n precision: number\n /** Share of entries the gate would have spared the writing model. */\n filtered: number\n /** Positives the gate would have dropped. */\n lostPositives: number\n accuracy: number\n /** Of the positives the gate dropped, which condition rejected them.\n * A gate can fail three different ways and the fix differs for each,\n * so a single recall number is not actionable on its own. */\n droppedBy: { type: number; worth: number; durability: number }\n /** Mean signal values among positives, to see how far off the\n * thresholds actually are. */\n positiveMeans: { worth: number; durability: number; typeConfidence: number }\n}\n\nexport function scoreBacktest(\n outcomes: TriageOutcome[],\n policy: { minType?: number; minWorth?: number; minDurability?: number } = {},\n): BacktestReport {\n const minType = policy.minType ?? 0.5\n const minWorth = policy.minWorth ?? 0.5\n const minDurability = policy.minDurability ?? 1.5\n\n const positives = outcomes.filter((o) => o.entry.absorbed).length\n const negatives = outcomes.length - positives\n const keptPos = outcomes.filter((o) => o.entry.absorbed && o.verdict.absorb).length\n const keptNeg = outcomes.filter((o) => !o.entry.absorbed && o.verdict.absorb).length\n const kept = keptPos + keptNeg\n\n const lost = outcomes.filter((o) => o.entry.absorbed && !o.verdict.absorb)\n const droppedBy = {\n type: lost.filter((o) => o.verdict.type === \"none\" || o.verdict.typeConfidence < minType).length,\n worth: lost.filter((o) => o.verdict.worthKeeping < minWorth).length,\n durability: lost.filter((o) => o.verdict.durability < minDurability).length,\n }\n const pos = outcomes.filter((o) => o.entry.absorbed)\n const mean = (f: (o: TriageOutcome) => number) =>\n pos.length ? pos.reduce((a, o) => a + f(o), 0) / pos.length : 0\n\n return {\n n: outcomes.length,\n positives,\n negatives,\n droppedBy,\n positiveMeans: {\n worth: mean((o) => o.verdict.worthKeeping),\n durability: mean((o) => o.verdict.durability),\n typeConfidence: mean((o) => o.verdict.typeConfidence),\n },\n recall: positives ? keptPos / positives : 0,\n precision: kept ? keptPos / kept : 0,\n filtered: outcomes.length ? 1 - kept / outcomes.length : 0,\n lostPositives: positives - keptPos,\n accuracy: outcomes.length\n ? (keptPos + (negatives - keptNeg)) / outcomes.length\n : 0,\n }\n}\n\n/**\n * Rows for the shared calibration metrics.\n *\n * The scalar graded is P(worth keeping), not the gate's boolean: the\n * boolean is a policy over three signals and thresholds, and calibrating\n * a policy tells you about the thresholds rather than the model. The\n * probability is the thing the model actually produced.\n */\nexport function toGradedRows(outcomes: TriageOutcome[], model: string, backend: string): GradedRow[] {\n return outcomes.map((o, i) => ({\n callId: `backtest-${i}`,\n seat: \"entry-triage\",\n ts: Date.now(),\n backend,\n model,\n structureMode: \"backtest\",\n question: \"worthKeeping\",\n type: \"noul\" as const,\n predicted: o.verdict.worthKeeping >= 0.5 ? \"true\" : \"false\",\n confidence: Math.max(o.verdict.worthKeeping, 1 - o.verdict.worthKeeping),\n probabilities: { true: o.verdict.worthKeeping, false: 1 - o.verdict.worthKeeping },\n truth: o.entry.absorbed ? \"true\" : \"false\",\n features: { source: o.entry.source ?? \"\", type: o.verdict.type },\n mode: \"shadow\" as const,\n explored: false,\n }))\n}\n\nexport interface SweepPoint {\n /** Whether the policy required a non-`none` article type at all. */\n requireTyped: boolean\n minType: number\n minWorth: number\n minDurability: number\n recall: number\n precision: number\n filtered: number\n lostPositives: number\n}\n\n/**\n * Re-score already-graded outcomes under many policies.\n *\n * The model calls are the expensive part and they are already paid for;\n * the thresholds are free to vary afterwards. This is the difference\n * between \"the gate scored 25%\" and \"the gate scores 25% at these three\n * numbers, and here is what it scores at every other set\".\n *\n * Recall is the constraint, not the objective. A dropped positive is an\n * article that never gets written and nobody ever learns was missing,\n * whereas a kept negative costs one Sonnet read. The asymmetry is\n * enormous, so the sweep maximises filtering *subject to* a recall\n * floor rather than trading the two off.\n */\nexport function sweepPolicies(\n outcomes: TriageOutcome[],\n opts: { minRecall?: number } = {},\n): { best: SweepPoint | null; points: SweepPoint[] } {\n const floor = opts.minRecall ?? 0.95\n const grid = {\n // Whether a non-`none` type is required at all. The first sweep\n // hard-coded this to true and no policy could clear 45% recall,\n // which looked like a tuning failure and was really this one\n // condition: absorb frequently MERGES an entry into an existing\n // article, so an entry can be cited without being an article of its\n // own. \"What type is this?\" and \"did absorb use this?\" are different\n // questions, and only the second is the label.\n requireTyped: [true, false],\n type: [0, 0.2, 0.3, 0.4, 0.5],\n worth: [0, 0.1, 0.2, 0.3, 0.4, 0.5],\n durability: [0, 0.5, 1.0, 1.5],\n }\n\n const points: SweepPoint[] = []\n for (const requireTyped of grid.requireTyped) {\n for (const minType of grid.type) {\n for (const minWorth of grid.worth) {\n for (const minDurability of grid.durability) {\n const keep = (o: TriageOutcome) =>\n (!requireTyped || (o.verdict.type !== \"none\" && o.verdict.typeConfidence >= minType)) &&\n o.verdict.worthKeeping >= minWorth &&\n o.verdict.durability >= minDurability\n\n const pos = outcomes.filter((o) => o.entry.absorbed)\n const keptPos = pos.filter(keep).length\n const kept = outcomes.filter(keep).length\n points.push({\n requireTyped, minType, minWorth, minDurability,\n recall: pos.length ? keptPos / pos.length : 0,\n precision: kept ? keptPos / kept : 0,\n filtered: outcomes.length ? 1 - kept / outcomes.length : 0,\n lostPositives: pos.length - keptPos,\n })\n }\n }\n }\n }\n\n // Most filtering among the policies that clear the recall floor.\n const viable = points.filter((p) => p.recall >= floor)\n viable.sort((a, b) => b.filtered - a.filtered)\n return { best: viable[0] ?? null, points }\n}\n"],"mappings":"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","names":["labelEntries","entries","unabsorbedIds","absorbedDates","e","newest","labeled","backlog","absorbed","sample","items","isPositive","n","rng","pos","neg","t","want","wantPos","wantNeg","pick","copy","j","scoreBacktest","outcomes","policy","minType","minWorth","minDurability","positives","o","negatives","keptPos","keptNeg","kept","lost","droppedBy","mean","f","a","toGradedRows","model","backend","sweepPolicies","opts","floor","grid","points","requireTyped","keep","viable","p","b"]}
@@ -0,0 +1,2 @@
1
+ import{a,b,c,d,e}from"./chunk-GSX4NK2N.js";import"./chunk-FIOB3MEX.js";export{b as actionInputSchema,a as actionInputTypeSchema,e as actionSchema,d as httpActionSchema,c as shellActionSchema};
2
+ //# sourceMappingURL=types-H5ENIUIT.js.map
@@ -0,0 +1,2 @@
1
+ import{a,b}from"./chunk-64P4QHSZ.js";import"./chunk-FIOB3MEX.js";export{a as WIKI_ARTICLE_TYPES,b as isWikiArticleType};
2
+ //# sourceMappingURL=types-OIFU3KSQ.js.map