agentix-cli 0.26.0 → 0.28.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 (300) hide show
  1. package/README.md +67 -118
  2. package/apps/mac-helper/Sources/AgentXHelper/AXTree.swift +159 -0
  3. package/apps/mac-helper/Sources/AgentXHelper/Brand.swift +63 -0
  4. package/apps/mac-helper/Sources/AgentXHelper/Focus.swift +97 -0
  5. package/apps/mac-helper/Sources/AgentXHelper/HUD.swift +179 -0
  6. package/apps/mac-helper/Sources/AgentXHelper/OCR.swift +117 -0
  7. package/apps/mac-helper/Sources/AgentXHelper/Pointer.swift +309 -0
  8. package/apps/mac-helper/Sources/AgentXHelper/Screens.swift +67 -0
  9. package/apps/mac-helper/Sources/AgentXHelper/Typer.swift +185 -0
  10. package/apps/mac-helper/Sources/AgentXHelper/Vision.swift +201 -0
  11. package/apps/mac-helper/Sources/AgentXHelper/main.swift +265 -0
  12. package/apps/mac-helper/build.sh +39 -0
  13. package/apps/mac-voice/Resources/Info.plist +18 -0
  14. package/apps/mac-voice/Sources/AgentXVoice/AgentClient.swift +108 -0
  15. package/apps/mac-voice/Sources/AgentXVoice/Brand.swift +123 -0
  16. package/apps/mac-voice/Sources/AgentXVoice/Config.swift +68 -0
  17. package/apps/mac-voice/Sources/AgentXVoice/Hold.swift +74 -0
  18. package/apps/mac-voice/Sources/AgentXVoice/Hotkey.swift +76 -0
  19. package/apps/mac-voice/Sources/AgentXVoice/Markdown.swift +140 -0
  20. package/apps/mac-voice/Sources/AgentXVoice/Panel.swift +275 -0
  21. package/apps/mac-voice/Sources/AgentXVoice/Progress.swift +147 -0
  22. package/apps/mac-voice/Sources/AgentXVoice/Recorder.swift +139 -0
  23. package/apps/mac-voice/Sources/AgentXVoice/ResultCard.swift +162 -0
  24. package/apps/mac-voice/Sources/AgentXVoice/SmartPaste.swift +72 -0
  25. package/apps/mac-voice/Sources/AgentXVoice/Speech.swift +192 -0
  26. package/apps/mac-voice/Sources/AgentXVoice/StepLabel.swift +159 -0
  27. package/apps/mac-voice/Sources/AgentXVoice/main.swift +380 -0
  28. package/apps/mac-voice/build.sh +32 -0
  29. package/dist/{App-BPNHIGUX.js → App-CKPH3HHF.js} +2 -2
  30. package/dist/{ChatApp-6XSSZB4U.js → ChatApp-MX7IG4K2.js} +2 -2
  31. package/dist/agent-2U574KBZ.js +2 -0
  32. package/dist/article-fields-L4D2BOGY.js +2 -0
  33. package/dist/article-quality-44ONVGLN.js +3 -0
  34. package/dist/article-quality-44ONVGLN.js.map +1 -0
  35. package/dist/{audit-JU3HWISB.js → audit-4245CX6P.js} +2 -2
  36. package/dist/backfill-MTD2B62I.js +6 -0
  37. package/dist/backfill-MTD2B62I.js.map +1 -0
  38. package/dist/{board-dashboard-M3372FLO.js → board-dashboard-QR7CVTKA.js} +1411 -453
  39. package/dist/board-dashboard-QR7CVTKA.js.map +1 -0
  40. package/dist/{builtin-FRKAQMFS.js → builtin-B67DMJ3M.js} +2 -2
  41. package/dist/calibration-ILS5OI44.js +2 -0
  42. package/dist/{chunk-V3U4OF7L.js → chunk-3LYM22IV.js} +46 -48
  43. package/dist/chunk-3LYM22IV.js.map +1 -0
  44. package/dist/chunk-3SCPRYJB.js +5 -0
  45. package/dist/chunk-3SCPRYJB.js.map +1 -0
  46. package/dist/chunk-4BR7D2OU.js +36 -0
  47. package/dist/chunk-4BR7D2OU.js.map +1 -0
  48. package/dist/{chunk-P4VWIVKN.js → chunk-52ZPELD2.js} +403 -29
  49. package/dist/chunk-52ZPELD2.js.map +1 -0
  50. package/dist/chunk-5IA2CYHN.js +5 -0
  51. package/dist/{chunk-O6YPEOQF.js.map → chunk-5IA2CYHN.js.map} +1 -1
  52. package/dist/chunk-64CAIB3C.js +9 -0
  53. package/dist/chunk-64CAIB3C.js.map +1 -0
  54. package/dist/{chunk-5U35WAZF.js → chunk-77OV7346.js} +6 -6
  55. package/dist/chunk-77OV7346.js.map +1 -0
  56. package/dist/chunk-AJ5RMU7X.js +2 -0
  57. package/dist/chunk-AJ5RMU7X.js.map +1 -0
  58. package/dist/chunk-AJONZWLT.js +2 -0
  59. package/dist/chunk-AJONZWLT.js.map +1 -0
  60. package/dist/chunk-BH3GTCZ6.js +2 -0
  61. package/dist/chunk-BH3GTCZ6.js.map +1 -0
  62. package/dist/chunk-E3UJHQGN.js +1189 -0
  63. package/dist/chunk-E3UJHQGN.js.map +1 -0
  64. package/dist/chunk-EVWEG3VD.js +3 -0
  65. package/dist/chunk-EVWEG3VD.js.map +1 -0
  66. package/dist/chunk-F4HZTZHW.js +2 -0
  67. package/dist/chunk-F4HZTZHW.js.map +1 -0
  68. package/dist/chunk-F6CNF7V3.js +25 -0
  69. package/dist/chunk-F6CNF7V3.js.map +1 -0
  70. package/dist/chunk-FIOB3MEX.js +2 -0
  71. package/dist/{chunk-H5D7ATXS.js → chunk-FKBGJYWQ.js} +5 -5
  72. package/dist/chunk-FKBGJYWQ.js.map +1 -0
  73. package/dist/{chunk-BVGJSPW4.js → chunk-G2HMRDUQ.js} +7 -7
  74. package/dist/chunk-G2HMRDUQ.js.map +1 -0
  75. package/dist/chunk-H2QQU2XV.js +109 -0
  76. package/dist/chunk-H2QQU2XV.js.map +1 -0
  77. package/dist/{chunk-LDHPMJ7M.js → chunk-IWMW5432.js} +3 -3
  78. package/dist/chunk-IWMW5432.js.map +1 -0
  79. package/dist/chunk-K33GIBSE.js +2 -0
  80. package/dist/chunk-K33GIBSE.js.map +1 -0
  81. package/dist/chunk-L2WSPY6S.js +2 -0
  82. package/dist/chunk-L2WSPY6S.js.map +1 -0
  83. package/dist/chunk-LCIREYTD.js +54 -0
  84. package/dist/chunk-LCIREYTD.js.map +1 -0
  85. package/dist/chunk-MHIMGM4G.js +126 -0
  86. package/dist/chunk-MHIMGM4G.js.map +1 -0
  87. package/dist/chunk-P5ZBBEPL.js +13 -0
  88. package/dist/chunk-P5ZBBEPL.js.map +1 -0
  89. package/dist/{chunk-JTJIPNXY.js → chunk-PNWNVOEZ.js} +1 -1
  90. package/dist/chunk-PNWNVOEZ.js.map +1 -0
  91. package/dist/chunk-QHTQPBHU.js +45 -0
  92. package/dist/chunk-QHTQPBHU.js.map +1 -0
  93. package/dist/chunk-S2MNL5V6.js +2 -0
  94. package/dist/chunk-S2MNL5V6.js.map +1 -0
  95. package/dist/chunk-SCTIUFL4.js +5 -0
  96. package/dist/{chunk-K2CAPYES.js.map → chunk-SCTIUFL4.js.map} +1 -1
  97. package/dist/chunk-ULLAE6LE.js +157 -0
  98. package/dist/chunk-ULLAE6LE.js.map +1 -0
  99. package/dist/{chunk-DJEZ2HVU.js → chunk-VDMYBPYB.js} +1 -1
  100. package/dist/chunk-VDMYBPYB.js.map +1 -0
  101. package/dist/chunk-VIH7ARPF.js +547 -0
  102. package/dist/chunk-VIH7ARPF.js.map +1 -0
  103. package/dist/chunk-W5C2GDNC.js +32 -0
  104. package/dist/{chunk-JP5IEXR7.js.map → chunk-W5C2GDNC.js.map} +1 -1
  105. package/dist/chunk-WPMX3VGI.js +3 -0
  106. package/dist/chunk-WPMX3VGI.js.map +1 -0
  107. package/dist/chunk-YLBERT7N.js +24 -0
  108. package/dist/chunk-YLBERT7N.js.map +1 -0
  109. package/dist/chunk-YLGIZRHV.js +114 -0
  110. package/dist/chunk-YLGIZRHV.js.map +1 -0
  111. package/dist/chunk-ZMYGXHPT.js +121 -0
  112. package/dist/chunk-ZMYGXHPT.js.map +1 -0
  113. package/dist/chunk-ZTEU5JVQ.js +2 -0
  114. package/dist/chunk-ZTEU5JVQ.js.map +1 -0
  115. package/dist/{chunk-2WVX3LEC.js → chunk-ZVP6GB4G.js} +1 -1
  116. package/dist/chunk-ZVP6GB4G.js.map +1 -0
  117. package/dist/cli.js +1 -1
  118. package/dist/cli.js.map +1 -1
  119. package/dist/client-ZLSXGPTV.js +2 -0
  120. package/dist/{compaction-NWEMOSHA.js → compaction-FD4R23HZ.js} +4 -4
  121. package/dist/config-HMW3EBZL.js +2 -0
  122. package/dist/config-mutator-4SZ5YEVD.js +2 -0
  123. package/dist/{conflict-detector-D7VIYPDI.js → conflict-detector-ZPOT3M2C.js} +2 -2
  124. package/dist/conflict-detector-ZPOT3M2C.js.map +1 -0
  125. package/dist/context-planner-AQHJMVY2.js +17 -0
  126. package/dist/context-planner-AQHJMVY2.js.map +1 -0
  127. package/dist/debug-3MEUO2P2.js +2 -0
  128. package/dist/{discover-XJXQXXIO.js → discover-D4VVRNAI.js} +2 -2
  129. package/dist/discover-D4VVRNAI.js.map +1 -0
  130. package/dist/doctor-LRYV3QLK.js +2 -0
  131. package/dist/editor-chat-OB7IQOB5.js +137 -0
  132. package/dist/editor-chat-OB7IQOB5.js.map +1 -0
  133. package/dist/entry-triage-LK34TSQD.js +2 -0
  134. package/dist/entry-triage-LK34TSQD.js.map +1 -0
  135. package/dist/facts-YVEJOM7O.js +22 -0
  136. package/dist/facts-YVEJOM7O.js.map +1 -0
  137. package/dist/heal-FVLBHYP7.js +2 -0
  138. package/dist/index.d.ts +1040 -64
  139. package/dist/index.js +12 -19
  140. package/dist/index.js.map +1 -1
  141. package/dist/{ingest-whatsapp-LZE62INA.js → ingest-whatsapp-NS4OWDOU.js} +2 -2
  142. package/dist/loader-4ZGTK7LD.js +2 -0
  143. package/dist/loader-BOKE5M5S.js +2 -0
  144. package/dist/memory-extract-F5ZHWY5E.js +2 -0
  145. package/dist/{migrate-v2-QO7SLKIM.js → migrate-v2-V4WN2VVM.js} +2 -2
  146. package/dist/ntfy-X7EPB3TG.js +4 -0
  147. package/dist/ntfy-X7EPB3TG.js.map +1 -0
  148. package/dist/processes-WU23U2OJ.js +2 -0
  149. package/dist/program-GSZ5AYAM.js +2 -0
  150. package/dist/{projects-api-KRME2YKZ.js → projects-api-QE6L65SK.js} +2 -2
  151. package/dist/{projects-mutate-3NHVG437.js → projects-mutate-MY7JTLSZ.js} +2 -2
  152. package/dist/providers-KO2DODIO.js +2 -0
  153. package/dist/query-SQDHE4HV.js +2 -0
  154. package/dist/questions-4B4KSPXG.js +3 -0
  155. package/dist/questions-4B4KSPXG.js.map +1 -0
  156. package/dist/recipes-7S4MC5OJ.js +2 -0
  157. package/dist/registry-LE6FWYZC.js +2 -0
  158. package/dist/review-candidates-RQU5KNZM.js +4 -0
  159. package/dist/review-candidates-RQU5KNZM.js.map +1 -0
  160. package/dist/review-store-B4YE3EFK.js +4 -0
  161. package/dist/review-store-B4YE3EFK.js.map +1 -0
  162. package/dist/{rotation-memo-B3EXXSEH.js → rotation-memo-7T75BLEX.js} +2 -2
  163. package/dist/routing-ILERJXB6.js +2 -0
  164. package/dist/runner-YRBVNYTY.js +2 -0
  165. package/dist/scheduler-BFM3EINS.js +2 -0
  166. package/dist/seat-7WM7II2B.js +2 -0
  167. package/dist/select-X4KVMOP5.js +2 -0
  168. package/dist/select-X4KVMOP5.js.map +1 -0
  169. package/dist/store-MCOA6N4F.js +2 -0
  170. package/dist/{subagent-WV7QKQ3L.js → subagent-2SJHSSJS.js} +2 -2
  171. package/dist/{surface-usage-PRL6Z4U3.js → surface-usage-NS74AXSE.js} +2 -2
  172. package/dist/timers-PCB4ZIBD.js +2 -0
  173. package/dist/triage-backtest-JNO7RKX7.js +2 -0
  174. package/dist/triage-backtest-JNO7RKX7.js.map +1 -0
  175. package/dist/types-H5ENIUIT.js +2 -0
  176. package/dist/types-OIFU3KSQ.js +2 -0
  177. package/dist/types-OIFU3KSQ.js.map +1 -0
  178. package/dist/web/workflow-editor.global.js +1542 -0
  179. package/dist/web/workflow-editor.global.js.map +1 -0
  180. package/dist/{whatsapp-state-XMTCSRXU.js → whatsapp-state-6D6FHHD2.js} +2 -2
  181. package/dist/wiki-2AUFSIKO.js +2 -0
  182. package/dist/wiki-2AUFSIKO.js.map +1 -0
  183. package/dist/workflows/templates/branching.yaml +1 -1
  184. package/dist/{workspace-setup-TBQAHYHJ.js → workspace-setup-B3F3TUDE.js} +2 -2
  185. package/dist/workspace-setup-B3F3TUDE.js.map +1 -0
  186. package/package.json +14 -3
  187. package/scripts/postinstall.mjs +80 -0
  188. package/dist/agent-5QMITZT6.js +0 -2
  189. package/dist/architect-SEPWMWS4.js +0 -2
  190. package/dist/board-dashboard-M3372FLO.js.map +0 -1
  191. package/dist/chunk-2WVX3LEC.js.map +0 -1
  192. package/dist/chunk-3WQFCIB6.js +0 -24
  193. package/dist/chunk-3WQFCIB6.js.map +0 -1
  194. package/dist/chunk-4YCH6IZV.js +0 -2
  195. package/dist/chunk-53NRX2NA.js +0 -2
  196. package/dist/chunk-53NRX2NA.js.map +0 -1
  197. package/dist/chunk-5U35WAZF.js.map +0 -1
  198. package/dist/chunk-BFXONUUN.js +0 -108
  199. package/dist/chunk-BFXONUUN.js.map +0 -1
  200. package/dist/chunk-BFZUG66X.js +0 -45
  201. package/dist/chunk-BFZUG66X.js.map +0 -1
  202. package/dist/chunk-BTHHSY7D.js +0 -13
  203. package/dist/chunk-BTHHSY7D.js.map +0 -1
  204. package/dist/chunk-BVGJSPW4.js.map +0 -1
  205. package/dist/chunk-D46VZ7K7.js +0 -116
  206. package/dist/chunk-D46VZ7K7.js.map +0 -1
  207. package/dist/chunk-DJEZ2HVU.js.map +0 -1
  208. package/dist/chunk-E5CXCI33.js +0 -155
  209. package/dist/chunk-E5CXCI33.js.map +0 -1
  210. package/dist/chunk-FDWOECJY.js +0 -54
  211. package/dist/chunk-FDWOECJY.js.map +0 -1
  212. package/dist/chunk-H5D7ATXS.js.map +0 -1
  213. package/dist/chunk-HR4JSRZS.js +0 -3
  214. package/dist/chunk-HR4JSRZS.js.map +0 -1
  215. package/dist/chunk-ISEBLHWX.js +0 -118
  216. package/dist/chunk-ISEBLHWX.js.map +0 -1
  217. package/dist/chunk-JP5IEXR7.js +0 -32
  218. package/dist/chunk-JTJIPNXY.js.map +0 -1
  219. package/dist/chunk-K2CAPYES.js +0 -5
  220. package/dist/chunk-LDHPMJ7M.js.map +0 -1
  221. package/dist/chunk-METYYZKQ.js +0 -1115
  222. package/dist/chunk-METYYZKQ.js.map +0 -1
  223. package/dist/chunk-MTAPOCGA.js +0 -47
  224. package/dist/chunk-MTAPOCGA.js.map +0 -1
  225. package/dist/chunk-O6YPEOQF.js +0 -5
  226. package/dist/chunk-P4VWIVKN.js.map +0 -1
  227. package/dist/chunk-SR3X25TN.js +0 -3
  228. package/dist/chunk-SR3X25TN.js.map +0 -1
  229. package/dist/chunk-UHE5FCPB.js +0 -431
  230. package/dist/chunk-UHE5FCPB.js.map +0 -1
  231. package/dist/chunk-V3U4OF7L.js.map +0 -1
  232. package/dist/chunk-VJUTQAPJ.js +0 -2
  233. package/dist/chunk-VJUTQAPJ.js.map +0 -1
  234. package/dist/chunk-VT5XYFDD.js +0 -126
  235. package/dist/chunk-VT5XYFDD.js.map +0 -1
  236. package/dist/client-HGKAC6VP.js +0 -2
  237. package/dist/config-A4RWEJHV.js +0 -2
  238. package/dist/config-mutator-MNSFUN7D.js +0 -2
  239. package/dist/conflict-detector-D7VIYPDI.js.map +0 -1
  240. package/dist/context-planner-45GEWKGL.js +0 -17
  241. package/dist/context-planner-45GEWKGL.js.map +0 -1
  242. package/dist/debug-N3B5NVJU.js +0 -2
  243. package/dist/discover-XJXQXXIO.js.map +0 -1
  244. package/dist/doctor-3Q36ZFE6.js +0 -2
  245. package/dist/heal-HZ3EQD52.js +0 -2
  246. package/dist/loader-5CA3O5GM.js +0 -2
  247. package/dist/loader-6IHUCPVO.js +0 -2
  248. package/dist/memory-extract-AENFVVO5.js +0 -2
  249. package/dist/processes-UIC4XXA6.js +0 -2
  250. package/dist/program-SAUNRCCD.js +0 -2
  251. package/dist/providers-L27BJ5A6.js +0 -2
  252. package/dist/query-HZYYRCLU.js +0 -2
  253. package/dist/recipes-EPLE2SNT.js +0 -2
  254. package/dist/registry-HK3WQ7BS.js +0 -2
  255. package/dist/runner-BFYDWPUO.js +0 -2
  256. package/dist/scheduler-6T2SU5ST.js +0 -2
  257. package/dist/store-I3HEWVLQ.js +0 -2
  258. package/dist/timers-5LPZN55J.js +0 -2
  259. package/dist/types-VECTMYTO.js +0 -2
  260. package/dist/types-Y6ZCZ52Z.js +0 -2
  261. package/dist/wiki-M6YNRFC4.js +0 -2
  262. /package/dist/{App-BPNHIGUX.js.map → App-CKPH3HHF.js.map} +0 -0
  263. /package/dist/{ChatApp-6XSSZB4U.js.map → ChatApp-MX7IG4K2.js.map} +0 -0
  264. /package/dist/{agent-5QMITZT6.js.map → agent-2U574KBZ.js.map} +0 -0
  265. /package/dist/{architect-SEPWMWS4.js.map → article-fields-L4D2BOGY.js.map} +0 -0
  266. /package/dist/{audit-JU3HWISB.js.map → audit-4245CX6P.js.map} +0 -0
  267. /package/dist/{builtin-FRKAQMFS.js.map → builtin-B67DMJ3M.js.map} +0 -0
  268. /package/dist/{chunk-4YCH6IZV.js.map → calibration-ILS5OI44.js.map} +0 -0
  269. /package/dist/{client-HGKAC6VP.js.map → chunk-FIOB3MEX.js.map} +0 -0
  270. /package/dist/{config-A4RWEJHV.js.map → client-ZLSXGPTV.js.map} +0 -0
  271. /package/dist/{compaction-NWEMOSHA.js.map → compaction-FD4R23HZ.js.map} +0 -0
  272. /package/dist/{config-mutator-MNSFUN7D.js.map → config-HMW3EBZL.js.map} +0 -0
  273. /package/dist/{debug-N3B5NVJU.js.map → config-mutator-4SZ5YEVD.js.map} +0 -0
  274. /package/dist/{doctor-3Q36ZFE6.js.map → debug-3MEUO2P2.js.map} +0 -0
  275. /package/dist/{heal-HZ3EQD52.js.map → doctor-LRYV3QLK.js.map} +0 -0
  276. /package/dist/{loader-5CA3O5GM.js.map → heal-FVLBHYP7.js.map} +0 -0
  277. /package/dist/{ingest-whatsapp-LZE62INA.js.map → ingest-whatsapp-NS4OWDOU.js.map} +0 -0
  278. /package/dist/{loader-6IHUCPVO.js.map → loader-4ZGTK7LD.js.map} +0 -0
  279. /package/dist/{memory-extract-AENFVVO5.js.map → loader-BOKE5M5S.js.map} +0 -0
  280. /package/dist/{processes-UIC4XXA6.js.map → memory-extract-F5ZHWY5E.js.map} +0 -0
  281. /package/dist/{migrate-v2-QO7SLKIM.js.map → migrate-v2-V4WN2VVM.js.map} +0 -0
  282. /package/dist/{program-SAUNRCCD.js.map → processes-WU23U2OJ.js.map} +0 -0
  283. /package/dist/{providers-L27BJ5A6.js.map → program-GSZ5AYAM.js.map} +0 -0
  284. /package/dist/{projects-api-KRME2YKZ.js.map → projects-api-QE6L65SK.js.map} +0 -0
  285. /package/dist/{projects-mutate-3NHVG437.js.map → projects-mutate-MY7JTLSZ.js.map} +0 -0
  286. /package/dist/{query-HZYYRCLU.js.map → providers-KO2DODIO.js.map} +0 -0
  287. /package/dist/{recipes-EPLE2SNT.js.map → query-SQDHE4HV.js.map} +0 -0
  288. /package/dist/{registry-HK3WQ7BS.js.map → recipes-7S4MC5OJ.js.map} +0 -0
  289. /package/dist/{runner-BFYDWPUO.js.map → registry-LE6FWYZC.js.map} +0 -0
  290. /package/dist/{rotation-memo-B3EXXSEH.js.map → rotation-memo-7T75BLEX.js.map} +0 -0
  291. /package/dist/{scheduler-6T2SU5ST.js.map → routing-ILERJXB6.js.map} +0 -0
  292. /package/dist/{store-I3HEWVLQ.js.map → runner-YRBVNYTY.js.map} +0 -0
  293. /package/dist/{surface-usage-PRL6Z4U3.js.map → scheduler-BFM3EINS.js.map} +0 -0
  294. /package/dist/{timers-5LPZN55J.js.map → seat-7WM7II2B.js.map} +0 -0
  295. /package/dist/{types-VECTMYTO.js.map → store-MCOA6N4F.js.map} +0 -0
  296. /package/dist/{subagent-WV7QKQ3L.js.map → subagent-2SJHSSJS.js.map} +0 -0
  297. /package/dist/{types-Y6ZCZ52Z.js.map → surface-usage-NS74AXSE.js.map} +0 -0
  298. /package/dist/{wiki-M6YNRFC4.js.map → timers-PCB4ZIBD.js.map} +0 -0
  299. /package/dist/{workspace-setup-TBQAHYHJ.js.map → types-H5ENIUIT.js.map} +0 -0
  300. /package/dist/{whatsapp-state-XMTCSRXU.js.map → whatsapp-state-6D6FHHD2.js.map} +0 -0
@@ -0,0 +1,380 @@
1
+ import AppKit
2
+ import Carbon.HIToolbox
3
+
4
+ /// AgentX Voice — push-to-talk widget over the daemon's /ask endpoint.
5
+ ///
6
+ /// Hold ⌥Space, speak, release. Transcribe (ElevenLabs, falling back to
7
+ /// on-device mlx-whisper), POST to /ask, speak the answer (ElevenLabs,
8
+ /// falling back to `say`).
9
+ ///
10
+ /// Deliberately has NO computer-use capability in this slice. The point is
11
+ /// to find out whether talking to an agent this way is actually pleasant
12
+ /// before building the part that can click things.
13
+ /// Main-actor isolated in full. An NSApplicationDelegate touches AppKit in
14
+ /// every method, and the crash that prompted this was UI state reached from
15
+ /// a background queue. Isolating the whole class turns that from a runtime
16
+ /// heap corruption into a compile error.
17
+ @MainActor
18
+ final class App: NSObject, NSApplicationDelegate {
19
+ private let panel = Panel()
20
+ private let card = ResultCard()
21
+ private let recorder = Recorder()
22
+ private var hotkey: Hotkey?
23
+ private var pasteHotkey: Hotkey?
24
+ private var busy = false
25
+ private var progress: Progress?
26
+ private var ticker: Timer?
27
+ private var startedAt = Date()
28
+ private var lastStep = "Thinking…"
29
+ private var spokenSteps = Set<String>()
30
+ private var lastSpokeAt = Date.distantPast
31
+
32
+ /// ⌘⌥V. Everything except the hotkey lives in `agentx paste`.
33
+ private func smartPaste() {
34
+ guard !busy else { return }
35
+ busy = true
36
+ panel.render(.working("Pasting…", 0))
37
+ SmartPaste.run { [weak self] result in
38
+ guard let self else { return }
39
+ self.busy = false
40
+ guard let result else {
41
+ self.panel.render(.error("Smart paste unavailable"))
42
+ return
43
+ }
44
+ // Quiet when nothing changed: "pasted as copied" is the common
45
+ // case and does not deserve an announcement.
46
+ if let what = SmartPaste.summary(result) {
47
+ self.panel.render(.working(what, 0))
48
+ }
49
+ self.panel.render(.idle)
50
+ }
51
+ }
52
+
53
+ /// Only one widget, however it was started.
54
+ ///
55
+ /// Two pills were sitting on screen at once: the installed copy in
56
+ /// /Applications and a freshly built one from the source tree. Same
57
+ /// bundle identifier, different paths, so macOS is happy to run both —
58
+ /// and the older one keeps rendering an older UI, which looks exactly
59
+ /// like a bug in the new build.
60
+ ///
61
+ /// The NEWEST wins. After an upgrade or a rebuild the thing you just
62
+ /// made is the one you want; a stale copy showing yesterday's
63
+ /// behaviour has no claim to the screen.
64
+ private func retireOlderInstances() {
65
+ let me = ProcessInfo.processInfo.processIdentifier
66
+ let mine = Bundle.main.bundleIdentifier
67
+ for app in NSWorkspace.shared.runningApplications {
68
+ guard app.bundleIdentifier == mine,
69
+ app.processIdentifier != me else { continue }
70
+ Log.info("retiring an older instance (pid \(app.processIdentifier))")
71
+ if !app.terminate() { app.forceTerminate() }
72
+ }
73
+ }
74
+
75
+ func applicationDidFinishLaunching(_ note: Notification) {
76
+ NSApp.setActivationPolicy(.accessory)
77
+ retireOlderInstances()
78
+ panel.orderFrontRegardless()
79
+
80
+ hotkey = Hotkey(
81
+ id: 1,
82
+ onPress: { [weak self] in self?.startListening() },
83
+ onRelease: { [weak self] in self?.stopAndSend() })
84
+ hotkey?.register()
85
+ panel.onClick = { [weak self] in self?.toggleListening() }
86
+
87
+ // Turning the hold OFF is the moment anything that piled up
88
+ // becomes welcome. Without this the queue is a hole rather than a
89
+ // delay — the daemon's watcher only notices SYSTEM Focus ending,
90
+ // and this switch is not that.
91
+ panel.onHoldChanged = { [weak self] nowOn in
92
+ guard let self else { return }
93
+ self.panel.render(.idle)
94
+ if !nowOn { Hold.flushHeld() }
95
+ Log.info(nowOn ? "notifications held" : "notifications delivering")
96
+ }
97
+
98
+ // ⌘⌥V: reshape the clipboard for wherever the caret is, then paste.
99
+ // Fires on RELEASE so the modifiers are up before cmd-V is sent —
100
+ // pressing it while ⌘⌥ are still held produces a different chord
101
+ // in the destination app.
102
+ pasteHotkey = Hotkey(
103
+ id: 2,
104
+ onPress: {},
105
+ onRelease: { [weak self] in self?.smartPaste() })
106
+ pasteHotkey?.register(keyCode: UInt32(kVK_ANSI_V),
107
+ modifiers: UInt32(cmdKey | optionKey))
108
+
109
+ recorder.requestPermission { [weak self] granted in
110
+ Task { @MainActor in
111
+ guard let self else { return }
112
+ if !granted { self.panel.render(.error("Microphone denied")) }
113
+ else { Log.info("ready — hold ⌥Space to talk (agent: \(Config.agentID))") }
114
+ }
115
+ }
116
+ }
117
+
118
+ // --- Hands-free listening ---
119
+ //
120
+ // Holding a chord is fine for one question and wrong for a
121
+ // conversation: the reply arrives, you want to say one more thing, and
122
+ // you have to find ⌥Space again while the assistant sits idle. So the
123
+ // microphone reopens by itself after every answer, and closes again on
124
+ // its own if you say nothing.
125
+ //
126
+ // Ending on SILENCE rather than a timer is the whole trick. A fixed
127
+ // window either cuts people off mid-sentence or leaves the mic open
128
+ // staring at them; the only honest signal that a sentence has finished
129
+ // is the room going quiet.
130
+
131
+ /// RMS above which the microphone is hearing a voice rather than a room.
132
+ private let speechLevel: Float = 0.02
133
+ /// Quiet this long after speech ends the turn.
134
+ private let endSilence: TimeInterval = 1.2
135
+ /// How long an unprompted follow-up window waits before giving up.
136
+ private let followUpPatience: TimeInterval = 4.0
137
+ /// How long a clicked session waits for you to start talking.
138
+ private let clickPatience: TimeInterval = 8.0
139
+
140
+ private var listenPoll: Timer?
141
+ private var heardSpeech = false
142
+ private var quietSince: Date?
143
+ private var openedAt = Date()
144
+ private var patience: TimeInterval = 8.0
145
+ /// True when nothing was said and the window should close in silence.
146
+ private var silentClose = false
147
+
148
+ /// Open the microphone with no key held.
149
+ private func listenHandsFree(followUp: Bool) {
150
+ guard !busy, !recorder.isRecording else { return }
151
+ do {
152
+ try recorder.start()
153
+ } catch {
154
+ panel.render(.error(error.localizedDescription))
155
+ return
156
+ }
157
+ heardSpeech = false
158
+ quietSince = nil
159
+ openedAt = Date()
160
+ patience = followUp ? followUpPatience : clickPatience
161
+ silentClose = followUp
162
+ panel.render(.listening)
163
+
164
+ listenPoll?.invalidate()
165
+ // Timer fires on the run loop but its closure is not main-actor
166
+ // isolated, and pollLevel touches UI. Hopping explicitly keeps it
167
+ // correct under Swift 6 rather than relying on the timer happening
168
+ // to run where we want it.
169
+ listenPoll = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { _ in
170
+ Task { @MainActor [weak self] in self?.pollLevel() }
171
+ }
172
+ }
173
+
174
+ private func pollLevel() {
175
+ guard recorder.isRecording else { stopPolling(); return }
176
+ let level = recorder.level
177
+
178
+ if level > speechLevel {
179
+ heardSpeech = true
180
+ quietSince = nil
181
+ return
182
+ }
183
+ if heardSpeech {
184
+ // Speech has stopped. Give it a beat before deciding the
185
+ // sentence is over — people pause inside sentences.
186
+ let since = quietSince ?? Date()
187
+ quietSince = since
188
+ if Date().timeIntervalSince(since) >= endSilence {
189
+ stopPolling()
190
+ stopAndSend()
191
+ }
192
+ return
193
+ }
194
+ // Nothing said yet. Close quietly rather than making the person
195
+ // dismiss a window they did not ask for.
196
+ if Date().timeIntervalSince(openedAt) >= patience {
197
+ stopPolling()
198
+ _ = recorder.stop()
199
+ panel.render(silentClose ? .idle : .error("Didn't catch that"))
200
+ if !silentClose { resetSoon() }
201
+ }
202
+ }
203
+
204
+ private func stopPolling() {
205
+ listenPoll?.invalidate()
206
+ listenPoll = nil
207
+ }
208
+
209
+ /// The pill was clicked: start if idle, finish early if already listening.
210
+ private func toggleListening() {
211
+ if recorder.isRecording {
212
+ stopPolling()
213
+ stopAndSend()
214
+ return
215
+ }
216
+ listenHandsFree(followUp: false)
217
+ }
218
+
219
+ private func startListening() {
220
+ // A held key overrides any hands-free window that is open, so the
221
+ // two ways of talking never fight over the microphone.
222
+ stopPolling()
223
+ // A turn already in flight must not be interrupted by a stray
224
+ // keypress; the answer is still coming and will be spoken.
225
+ guard !busy, !recorder.isRecording else { return }
226
+ do {
227
+ try recorder.start()
228
+ panel.render(.listening)
229
+ } catch {
230
+ panel.render(.error(error.localizedDescription))
231
+ }
232
+ }
233
+
234
+ private func stopAndSend() {
235
+ guard recorder.isRecording else { return }
236
+ guard let wav = recorder.stop() else {
237
+ panel.render(.error("Too short — hold while speaking"))
238
+ resetSoon()
239
+ return
240
+ }
241
+ busy = true
242
+ panel.render(.thinking)
243
+
244
+ Task { @MainActor in
245
+ do {
246
+ let heard = try await Speech.transcribe(wav: wav)
247
+ guard !heard.isEmpty else {
248
+ panel.render(.error("Didn't catch that"))
249
+ busy = false; resetSoon(); return
250
+ }
251
+ Log.info("heard: \(heard)")
252
+ beginNarration()
253
+
254
+ let answer = try await AgentClient.ask(heard)
255
+ endNarration()
256
+ Log.info("answer: \(answer.text)")
257
+ // Show BEFORE speaking, but only when there is something
258
+ // the speech cannot deliver — a link, an image, or more
259
+ // text than was read aloud. A card that opens on every
260
+ // "Ok." teaches you to ignore it.
261
+ if ResultCard.isWorthShowing(spoken: answer.text, written: answer.written,
262
+ buttons: answer.buttons, imageURL: answer.imageURL) {
263
+ card.show(spoken: answer.text, written: answer.written,
264
+ buttons: answer.buttons, imageURL: answer.imageURL)
265
+ } else {
266
+ card.orderOut(nil)
267
+ }
268
+ // Scroll the sentence being spoken, so it can be read as
269
+ // well as heard — and re-read after, which speech cannot do.
270
+ panel.render(.saying(answer.text))
271
+ await Speech.speak(answer.text)
272
+ panel.render(.idle)
273
+ // Leave the microphone open for a moment. Say nothing and
274
+ // it closes itself; start talking and the conversation
275
+ // simply continues.
276
+ busy = false
277
+ listenHandsFree(followUp: true)
278
+ } catch {
279
+ endNarration()
280
+ Log.warn("turn failed: \(error.localizedDescription)")
281
+ panel.render(.error(short(error.localizedDescription)))
282
+ // Say it aloud too — a voice assistant that fails only in
283
+ // a 230px label has failed silently for anyone not looking.
284
+ await Speech.speak("Sorry, that didn't work.")
285
+ resetSoon()
286
+ }
287
+ busy = false
288
+ }
289
+ }
290
+
291
+ /// Narrate the wait using what the daemon says it is actually doing.
292
+ ///
293
+ /// Two sources, because neither alone is enough: `task:step` events
294
+ /// say what the agent is doing but arrive irregularly, and a 1-second
295
+ /// ticker proves the thing is still alive between them. Together they
296
+ /// answer both "what is it doing" and "is it stuck".
297
+ private func beginNarration() {
298
+ startedAt = Date()
299
+ lastStep = "Thinking…"
300
+ spokenSteps.removeAll()
301
+ lastSpokeAt = Date()
302
+ panel.render(.working(lastStep, 0))
303
+
304
+ // Progress delivers on its own serial queue; hop to main before
305
+ // touching any view.
306
+ progress = Progress(agentID: Config.agentID) { [weak self] step in
307
+ Task { @MainActor in
308
+ guard let self, self.busy else { return }
309
+ self.lastStep = step
310
+ Log.info("step: \(step)")
311
+ self.panel.render(.working(step, Int(Date().timeIntervalSince(self.startedAt))))
312
+ self.speakStepIfDue(step)
313
+ }
314
+ }
315
+ progress?.start()
316
+
317
+ ticker = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
318
+ Task { @MainActor in
319
+ guard let self, self.busy else { return }
320
+ self.panel.render(.working(self.lastStep, Int(Date().timeIntervalSince(self.startedAt))))
321
+ }
322
+ }
323
+ }
324
+
325
+ private func endNarration() {
326
+ ticker?.invalidate(); ticker = nil
327
+ progress?.stop(); progress = nil
328
+ }
329
+
330
+ /// Say what it is doing, sparingly.
331
+ ///
332
+ /// Silence during a long turn reads as a broken assistant, and a
333
+ /// visible label does not help someone who asked by voice precisely
334
+ /// because they were not looking at the screen. But narrating every
335
+ /// step would talk over itself and be worse than silence — so: nothing
336
+ /// for the first stretch, then at most one short line every 15
337
+ /// seconds, and never the same step twice.
338
+ private func speakStepIfDue(_ step: String) {
339
+ let elapsed = Date().timeIntervalSince(startedAt)
340
+ guard elapsed > 12 else { return }
341
+ guard Date().timeIntervalSince(lastSpokeAt) > 15 else { return }
342
+
343
+ // The step is "Tool: detail". Both halves go to the daemon, which
344
+ // decides whether it is worth saying and in what words. Reading the
345
+ // detail aloud directly is what produced "still working, osascript
346
+ // tell application Calendar".
347
+ let parts = step.split(separator: ":", maxSplits: 1).map(String.init)
348
+ let tool = parts.first?.trimmingCharacters(in: .whitespaces) ?? step
349
+ let detail = parts.count > 1 ? parts[1].trimmingCharacters(in: .whitespaces) : ""
350
+
351
+ // Reserve the slot before awaiting, or two steps arriving together
352
+ // both pass the interval check and talk over each other.
353
+ lastSpokeAt = Date()
354
+ Task { @MainActor in
355
+ guard let phrase = await AgentClient.phrase(
356
+ tool: tool, detail: detail, elapsed: Int(elapsed)) else { return }
357
+ guard !self.spokenSteps.contains(phrase) else { return }
358
+ self.spokenSteps.insert(phrase)
359
+ await Speech.speak(phrase.prefix(1).capitalized + phrase.dropFirst() + ".")
360
+ }
361
+ }
362
+
363
+ private func short(_ s: String) -> String {
364
+ s.count > 40 ? String(s.prefix(38)) + "…" : s
365
+ }
366
+
367
+ private func resetSoon() {
368
+ DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in
369
+ MainActor.assumeIsolated {
370
+ guard let self, !self.recorder.isRecording, !self.busy else { return }
371
+ self.panel.render(.idle)
372
+ }
373
+ }
374
+ }
375
+ }
376
+
377
+ let app = NSApplication.shared
378
+ let delegate = MainActor.assumeIsolated { App() }
379
+ app.delegate = delegate
380
+ app.run()
@@ -0,0 +1,32 @@
1
+ #!/bin/bash
2
+ # Build AgentX Voice into a .app bundle.
3
+ #
4
+ # swiftc directly, not SwiftPM: a CommandLineTools-only install ships a
5
+ # broken PackageDescription ManifestAPI, and this target has no
6
+ # dependencies, so the package manager buys nothing here.
7
+ #
8
+ # The .app wrapper is not cosmetic — macOS grants microphone access to
9
+ # bundles with an Info.plist, so a bare executable can never get a TCC
10
+ # prompt and will fail with an inscrutable silence instead.
11
+ set -euo pipefail
12
+ cd "$(dirname "$0")"
13
+
14
+ APP="build/AgentX Voice.app"
15
+ BIN="$APP/Contents/MacOS"
16
+ rm -rf build && mkdir -p "$BIN" "$APP/Contents/Resources"
17
+
18
+ swiftc -O \
19
+ -o "$BIN/AgentXVoice" \
20
+ Sources/AgentXVoice/*.swift \
21
+ -framework AppKit -framework AVFoundation -framework Carbon \
22
+ -target arm64-apple-macosx14.0
23
+
24
+ cp Resources/Info.plist "$APP/Contents/Info.plist"
25
+
26
+ # Ad-hoc signature. Unsigned bundles get a fresh TCC identity on every
27
+ # rebuild, so macOS re-asks for the microphone every single launch.
28
+ codesign --force --sign - --identifier tn.acme.agentx.voice "$APP" 2>/dev/null \
29
+ || echo "warning: codesign failed; expect repeated microphone prompts"
30
+
31
+ echo "built: $APP"
32
+ echo "run: open \"$APP\" (or ./build/AgentX\\ Voice.app/Contents/MacOS/AgentXVoice for logs)"
@@ -1,8 +1,8 @@
1
- import{a as F,b as L,c as U,d as N,e as O,f as H,g as j}from"./chunk-U2CKL755.js";import{b as B,c as D,d as A,e as P,g as M}from"./chunk-O6YPEOQF.js";import"./chunk-BTHHSY7D.js";import"./chunk-4YCH6IZV.js";import{useEffect as R,useReducer as V,useRef as G}from"react";import{Box as i,Text as o,useApp as W,useInput as J}from"ink";import{randomUUID as z}from"crypto";async function*q(e){let r=[];e.type&&r.push(`type=${encodeURIComponent(e.type)}`);let t=`${e.baseUrl.replace(/\/+$/,"")}/events${r.length?`?${r.join("&")}`:""}`,n={Accept:"text/event-stream"};e.token&&(n.Authorization=`Bearer ${e.token}`);let s=await fetch(t,{headers:n,signal:e.signal});if(!s.ok||!s.body)throw new Error(`SSE connect failed: HTTP ${s.status}`);let u=s.body.getReader(),p=new TextDecoder,y="";try{for(;;){let{done:f,value:l}=await u.read();if(f)return;y+=p.decode(l,{stream:!0});let d;for(;(d=y.indexOf(`
1
+ import{a as F,b as L,c as U,d as N,e as O,f as H,g as j}from"./chunk-U2CKL755.js";import{b as B,c as D,d as A,e as P,g as M}from"./chunk-5IA2CYHN.js";import"./chunk-P5ZBBEPL.js";import"./chunk-FIOB3MEX.js";import{useEffect as R,useReducer as V,useRef as G}from"react";import{Box as i,Text as o,useApp as W,useInput as J}from"ink";import{randomUUID as z}from"crypto";async function*q(e){let r=[];e.type&&r.push(`type=${encodeURIComponent(e.type)}`);let t=`${e.baseUrl.replace(/\/+$/,"")}/events${r.length?`?${r.join("&")}`:""}`,n={Accept:"text/event-stream"};e.token&&(n.Authorization=`Bearer ${e.token}`);let s=await fetch(t,{headers:n,signal:e.signal});if(!s.ok||!s.body)throw new Error(`SSE connect failed: HTTP ${s.status}`);let u=s.body.getReader(),p=new TextDecoder,y="";try{for(;;){let{done:f,value:l}=await u.read();if(f)return;y+=p.decode(l,{stream:!0});let d;for(;(d=y.indexOf(`
2
2
 
3
3
  `))>=0;){let h=y.slice(0,d);y=y.slice(d+2);let m=X(h);m&&(yield m)}}}finally{try{u.releaseLock()}catch{}}}function X(e){let r="message",t="";for(let s of e.split(`
4
4
  `))s.startsWith("event: ")?r=s.slice(7):s.startsWith("data: ")&&(t=s.slice(6));if(!t)return null;let n;try{n=JSON.parse(t)}catch{n=t}return{event:r,data:n}}import{jsx as a,jsxs as c}from"react/jsx-runtime";function $(e,r){return r<=0||e<0?0:e>=r?r-1:e}function E(){return`tui:${z().slice(0,12)}`}function k(e,r){let t=[...e,r];return t.length>50&&t.splice(0,t.length-50),t}function Y(e,r){switch(r.type){case"agents":return{...e,agents:r.data,cursor:{...e.cursor,agents:$(e.cursor.agents,r.data.length)}};case"processes":return{...e,processes:r.data,cursor:{...e.cursor,processes:$(e.cursor.processes,r.data.length)}};case"crons":return{...e,crons:r.data};case"event":{let t=[r.row,...e.events];t.length>200&&(t.length=200);let n=e.channels;return r.channelTally&&(n=new Map(e.channels),n.set(r.channelTally.channel,r.channelTally)),{...e,events:t,channels:n}}case"connError":return{...e,connError:r.error};case"tick":return{...e,lastTick:Date.now()};case"focus":return{...e,focus:r.pane};case"cursor":{let t=r.pane==="agents"?e.agents.length:r.pane==="processes"?e.processes.length:e.events.length;return{...e,cursor:{...e.cursor,[r.pane]:$(e.cursor[r.pane]+r.delta,t)}}}case"bottomRight":return{...e,bottomRight:r.pane};case"chatStart":return{...e,chat:{active:!0,agentId:r.agentId,chatId:E(),history:[],text:"",status:"idle",error:null}};case"chatExit":return{...e,chat:{...e.chat,active:!1}};case"chatClear":return{...e,chat:{...e.chat,chatId:E(),history:[],text:"",status:"idle",error:null}};case"chatText":return{...e,chat:{...e.chat,text:r.text}};case"chatSubmitStart":return{...e,chat:{...e.chat,history:k(e.chat.history,r.you),text:"",status:"sending",error:null}};case"chatSubmitDone":return{...e,chat:{...e.chat,history:k(e.chat.history,r.reply),status:"idle",error:null}};case"chatSubmitError":return{...e,chat:{...e.chat,history:k(e.chat.history,{role:"error",text:r.error,at:Date.now()}),status:"idle",error:r.error}};case"chatStreamStart":{let t=k(e.chat.history,r.you),n=k(t,{role:"agent",text:"",at:Date.now(),streaming:!0,tools:[]});return{...e,chat:{...e.chat,history:n,text:"",status:"sending",error:null}}}case"chatStreamDelta":{let t=e.chat.history.slice(),n=t[t.length-1];return n?.role==="agent"&&n.streaming&&(t[t.length-1]={...n,text:n.text+r.text}),{...e,chat:{...e.chat,history:t}}}case"chatStreamThinking":{let t=e.chat.history.slice(),n=t[t.length-1];return n?.role==="agent"&&n.streaming&&(t[t.length-1]={...n,thinking:(n.thinking??"")+r.text}),{...e,chat:{...e.chat,history:t}}}case"chatStreamTool":{let t=e.chat.history.slice(),n=t[t.length-1];return n?.role==="agent"&&n.streaming&&(t[t.length-1]={...n,tools:[...n.tools??[],{name:r.name,arg:r.arg,error:r.error}]}),{...e,chat:{...e.chat,history:t}}}case"chatStreamEnd":{let t=e.chat.history.slice(),n=t[t.length-1];return n?.role==="agent"&&n.streaming&&(t[t.length-1]=r.error?{...n,streaming:!1,role:"error",text:r.error}:{...n,streaming:!1,elapsedMs:r.elapsedMs,outTokens:r.outTokens}),{...e,chat:{...e.chat,history:t,status:"idle",error:r.error??null}}}case"toast":return{...e,toast:{text:r.text,color:r.color,at:Date.now()}};case"clearToast":return{...e,toast:null}}}var _={agents:[],processes:[],crons:[],events:[],channels:new Map,connError:null,lastTick:0,focus:"agents",cursor:{agents:0,processes:0,events:0},bottomRight:"crons",chat:{active:!1,agentId:null,chatId:E(),history:[],text:"",status:"idle",error:null},toast:null};function be({conn:e,pollMs:r=3e3}){let[t,n]=V(Y,_),{exit:s}=W(),u=G(null),p=G(null),y=t.agents.map(l=>l.id),f=t.chat.active?L(t.chat.text,y,process.cwd()):null;return R(()=>{if(!t.toast)return;let l=setTimeout(()=>n({type:"clearToast"}),3500);return()=>clearTimeout(l)},[t.toast]),J((l,d)=>{if(t.chat.active){if(d.escape){n({type:"chatExit"});return}if(d.ctrl&&l==="l"){n({type:"chatClear"});return}if(d.tab){let m=U(t.chat.text,p,y,process.cwd());m!==null&&n({type:"chatText",text:m});return}if(d.backspace||d.delete){p.current=null,n({type:"chatText",text:t.chat.text.slice(0,-1)});return}if(d.ctrl||d.meta)return;let h=N(l??"",d.return,t.chat.text);if(h.kind==="paste"||h.kind==="type"){p.current=null,n({type:"chatText",text:t.chat.text+h.text});return}if(h.kind==="continue"){n({type:"chatText",text:h.buffer});return}if(h.kind==="submit"){let m=h.text.trim(),g=t.chat.agentId;if(!m||!g||t.chat.status==="sending"){n({type:"chatText",text:""});return}p.current=null;let b=Date.now(),w=t.chat.chatId;n({type:"chatStreamStart",you:{role:"you",text:m,at:b}}),(async()=>{try{let C=await M(e,g,m,{channel:"tui",chatId:w,onText:x=>n({type:"chatStreamDelta",text:x}),onThinking:x=>n({type:"chatStreamThinking",text:x}),onTool:x=>{x.status==="start"&&x.name?n({type:"chatStreamTool",name:x.name,arg:x.arg}):x.status==="result"&&x.error&&n({type:"chatStreamTool",name:x.name??"tool",error:!0})}});n({type:"chatStreamEnd",elapsedMs:Date.now()-b,outTokens:C.usage?.outputTokens,error:C.error})}catch(C){n({type:"chatStreamEnd",elapsedMs:Date.now()-b,error:C?.message||String(C)})}})();return}return}if(l==="q"||d.ctrl&&l==="c"){u.current?.abort(),s();return}if(d.tab){let h=["agents","processes","events"],m=h.indexOf(t.focus);n({type:"focus",pane:h[(m+1)%h.length]});return}if(l==="a"){n({type:"focus",pane:"agents"});return}if(l==="p"){n({type:"focus",pane:"processes"});return}if(l==="e"){n({type:"focus",pane:"events"});return}if(l==="c"){n({type:"bottomRight",pane:t.bottomRight==="crons"?"channels":"crons"});return}if(d.upArrow){n({type:"cursor",pane:t.focus,delta:-1});return}if(d.downArrow){n({type:"cursor",pane:t.focus,delta:1});return}if(d.return&&t.focus==="agents"){let h=t.agents[t.cursor.agents];if(!h){n({type:"toast",text:"no agent selected",color:"yellow"});return}n({type:"chatStart",agentId:h.id});return}if(l==="k"&&t.focus==="processes"){let h=t.processes[t.cursor.processes];if(!h){n({type:"toast",text:"no process selected",color:"yellow"});return}(async()=>{try{await P(e,h.key,"tui"),n({type:"toast",text:`killed ${h.key.agentId}/${h.key.channel}/${h.key.chatId}`,color:"green"})}catch(m){n({type:"toast",text:`kill failed: ${m?.message||m}`,color:"red"})}})();return}}),R(()=>{let l=new AbortController;u.current=l;let d=!1,h=async()=>{try{let[g,b,w]=await Promise.all([B(e,l.signal).catch(()=>[]),D(e,l.signal).catch(()=>[]),A(e,l.signal).catch(()=>[])]);if(d)return;n({type:"agents",data:g}),n({type:"processes",data:b}),n({type:"crons",data:w}),n({type:"tick"}),n({type:"connError",error:null})}catch(g){d||n({type:"connError",error:g?.message||String(g)})}};h();let m=setInterval(h,r);return()=>{d=!0,clearInterval(m),l.abort()}},[e.baseUrl,e.token,r]),R(()=>{let l=!1,d=new AbortController,h=new Map;return(async()=>{let m=500;for(;!l;){try{for await(let g of q({baseUrl:e.baseUrl,token:e.token,signal:d.signal})){let b=ae(g),w;if(g.event==="channel"&&typeof g.data?.channel=="string"){let S=h.get(g.data.channel)??{channel:g.data.channel,inbound:0,outbound:0,lastAt:0,lastPreview:""},C=S.inbound+(g.data.direction==="in"?1:0),x=S.outbound+(g.data.direction==="out"?1:0);w={channel:g.data.channel,inbound:C,outbound:x,lastAt:b.at,lastPreview:(g.data.textPreview||"").slice(0,60)},h.set(g.data.channel,w)}n({type:"event",row:b,channelTally:w}),m=500}}catch(g){if(l)return;n({type:"connError",error:`events: ${g?.message||g}`})}await new Promise(g=>setTimeout(g,m)),m=Math.min(m*2,1e4)}})(),()=>{l=!0,d.abort()}},[e.baseUrl,e.token]),c(i,{flexDirection:"column",width:"100%",children:[a(K,{conn:e,lastTick:t.lastTick,connError:t.connError,chat:t.chat}),c(i,{flexDirection:"row",flexGrow:1,children:[c(i,{flexDirection:"column",width:"50%",children:[a(v,{title:"AGENTS",focused:!t.chat.active&&t.focus==="agents",children:a(Z,{rows:t.agents,cursor:t.cursor.agents,focused:!t.chat.active&&t.focus==="agents",talkingTo:t.chat.active?t.chat.agentId:null})}),a(v,{title:"PROCESSES",focused:!t.chat.active&&t.focus==="processes",children:a(ee,{rows:t.processes,cursor:t.cursor.processes,focused:!t.chat.active&&t.focus==="processes"})})]}),t.chat.active?a(oe,{chat:t.chat,suggest:f,agents:t.agents}):c(i,{flexDirection:"column",width:"50%",children:[a(v,{title:"LIVE EVENTS",focused:t.focus==="events",children:a(ne,{rows:t.events,cursor:t.cursor.events,focused:t.focus==="events"})}),a(v,{title:t.bottomRight==="crons"?"CRONS / SCHEDULES":"CHANNELS",focused:!1,children:t.bottomRight==="crons"?a(te,{rows:t.crons}):a(re,{rows:Array.from(t.channels.values())})})]})]}),a(Q,{focus:t.focus,bottomRight:t.bottomRight,chat:t.chat,toast:t.toast})]})}function K({conn:e,lastTick:r,connError:t,chat:n}){let s=t?c(o,{color:"red",children:["\u25CF ",t]}):r===0?a(o,{color:"yellow",children:"\u25CF connecting\u2026"}):a(o,{color:"green",children:"\u25CF live"}),u=n.active?c(o,{color:"green",bold:!0,children:["CHAT @",n.agentId]}):a(o,{bold:!0,children:"agentx tui"});return c(i,{paddingX:1,justifyContent:"space-between",children:[a(i,{children:u}),a(o,{dimColor:!0,children:e.baseUrl}),a(i,{children:s})]})}function Q({focus:e,bottomRight:r,chat:t,toast:n}){let s=t.active?["[Enter] send","[Esc] exit chat","[Ctrl-L] new session"]:[`[Tab/a/p/e] focus (now: ${e})`,e==="agents"?"[Enter] chat":null,e==="processes"?"[k] kill":null,`[c] ${r==="crons"?"\u2192channels":"\u2192crons"}`,"[\u2191/\u2193] move","[q] quit"].filter(Boolean);return c(i,{paddingX:1,borderStyle:"single",borderTop:!0,borderBottom:!1,borderLeft:!1,borderRight:!1,justifyContent:"space-between",children:[a(o,{dimColor:!0,children:s.join(" \xB7 ")}),n?a(o,{color:n.color,children:n.text}):a(o,{children:" "})]})}function v({title:e,focused:r,children:t}){return c(i,{flexDirection:"column",width:"100%",borderStyle:r?"double":"single",borderColor:r?"cyan":void 0,paddingX:1,children:[c(o,{bold:!0,color:r?"cyan":"white",children:[e,r?" \u25CF":""]}),a(i,{flexDirection:"column",marginTop:1,children:t})]})}function I(e,r){return e&&r?{inverse:!0}:{}}function Z({rows:e,cursor:r,focused:t,talkingTo:n}){return e.length===0?a(o,{dimColor:!0,children:"no agents"}):a(i,{flexDirection:"column",children:e.slice(0,12).map((s,u)=>{let p=I(t,u===r),y=s.errors>0?"red":s.active>0?"yellow":"green",f=s.id===n;return c(i,{children:[a(o,{...p,color:y,children:"\u25CF"}),c(o,{...p,children:[" ",T(s.name,14)]}),c(o,{...p,children:[T(s.tier,6)," "]}),c(o,{...p,children:[s.active,"/",s.total]}),s.errors>0?c(o,{...p,color:"red",children:[" err=",s.errors]}):null,f?a(o,{color:"green",children:" \u2194 chatting"}):null]},s.id)})})}function ee({rows:e,cursor:r,focused:t}){if(e.length===0)return a(o,{dimColor:!0,children:"no live processes"});let n=Date.now();return a(i,{flexDirection:"column",children:e.slice(0,12).map((s,u)=>{let p=I(t,u===r),y=Math.max(0,Math.round((n-(s.lastTurnAt||s.spawnedAt))/1e3)),f=s.state==="dead"?"red":s.state==="warm-hot"?"green":"yellow";return c(i,{children:[a(o,{...p,color:f,children:T(s.state,10)}),a(o,{...p,children:T(s.key.agentId,12)}),a(o,{...p,children:T(s.key.channel,10)}),c(o,{...p,children:[s.turnCount,"t ",y,"s"]})]},`${s.key.agentId}-${s.key.channel}-${s.key.chatId}-${u}`)})})}function te({rows:e}){return e.length===0?a(o,{dimColor:!0,children:"no crons"}):a(i,{flexDirection:"column",children:e.slice(0,12).map(r=>{let t=r.enabled?r.consecutiveErrors>0?a(o,{color:"red",children:"err"}):a(o,{color:"green",children:"ok"}):a(o,{dimColor:!0,children:"off"}),n=r.nextRun?new Date(r.nextRun).toLocaleTimeString():"\u2014";return c(i,{children:[a(o,{children:T(r.id,16)}),a(o,{dimColor:!0,children:T(r.schedule,12)}),a(o,{children:T(n,10)}),a(i,{children:t})]},r.id)})})}function re({rows:e}){if(e.length===0)return a(o,{dimColor:!0,children:"(waiting for channel events\u2026)"});let r=[...e].sort((t,n)=>n.lastAt-t.lastAt);return a(i,{flexDirection:"column",children:r.slice(0,12).map(t=>c(i,{children:[a(o,{children:T(t.channel,12)}),c(o,{color:"cyan",children:["\u2190",T(String(t.inbound),4)]}),c(o,{color:"magenta",children:["\u2192",T(String(t.outbound),4)]}),a(o,{dimColor:!0,children:t.lastPreview})]},t.channel))})}function ne({rows:e,cursor:r,focused:t}){return e.length===0?a(o,{dimColor:!0,children:"(waiting for events\u2026)"}):a(i,{flexDirection:"column",children:e.slice(0,18).map((n,s)=>{let u=I(t,s===r);return c(i,{children:[a(o,{...u,dimColor:!0,children:new Date(n.at).toLocaleTimeString().padEnd(10)}),a(o,{...u,color:ce(n.kind),children:T(n.kind,8)}),a(o,{...u,children:n.line})]},`${n.at}-${s}`)})})}function oe({chat:e,suggest:r,agents:t}){let n=e.history.slice(-14),s=t.find(f=>f.id===e.agentId),u=H(s?.tier??"",s?.model),p=e.status==="sending"?[...e.history].reverse().find(f=>f.role==="agent"&&f.streaming):void 0,y=e.status==="sending"?a(O,{startedAt:p?.at??Date.now(),phase:p?.text?"responding":"thinking",indent:""}):e.error?c(o,{color:"red",children:["\u25CF ",e.error]}):a(o,{dimColor:!0,children:"\u25CF ready"});return c(i,{flexDirection:"column",width:"50%",borderStyle:"double",borderColor:"green",paddingX:1,children:[c(i,{justifyContent:"space-between",children:[c(o,{bold:!0,color:"green",children:["CHAT \xB7 @",e.agentId]}),a(i,{children:y})]}),c(o,{children:[c(o,{color:u.color,bold:!0,children:[u.glyph," ",u.label]}),s?.model?c(o,{dimColor:!0,children:[" ",j(s.model)]}):null,c(o,{dimColor:!0,children:[" \xB7 ",e.chatId]})]}),a(i,{flexDirection:"column",flexGrow:1,marginTop:1,children:n.length===0?a(o,{dimColor:!0,children:"(type a message and press Enter \u2014 Esc exits, Ctrl-L starts fresh)"}):n.map((f,l)=>a(se,{turn:f,agentId:e.agentId},`${f.at}-${l}`))}),r?c(o,{dimColor:!0,children:[a(o,{color:"cyan",children:"\u21B9"})," ",r.items.slice(0,5).map((f,l)=>l===0?a(o,{color:"cyan",children:f},l):c(o,{children:[" ",f]},l))]}):null,c(i,{children:[a(o,{color:"cyan",children:"you \u203A "}),a(o,{children:e.text}),a(o,{inverse:!0,children:" "})]})]})}function se({turn:e,agentId:r}){if(e.role==="you")return c(i,{flexDirection:"column",marginBottom:1,children:[a(o,{color:"cyan",children:"you"}),e.text.split(`
5
5
  `).map((s,u)=>c(o,{children:[" ",s]},u))]});if(e.role==="error")return c(i,{flexDirection:"column",marginBottom:1,children:[a(o,{color:"red",children:"error"}),c(o,{color:"red",children:[" ",e.text]})]});let t=e.elapsedMs!=null?` \xB7 ${(e.elapsedMs/1e3).toFixed(1)}s${e.outTokens!=null?`, ${e.outTokens} tok`:""}`:e.streaming?" \xB7 \u2026":"",n=F(e.text,58,{balance:e.streaming});return c(i,{flexDirection:"column",marginBottom:1,children:[c(o,{color:"green",children:["@",r??"agent",a(o,{dimColor:!0,children:t})]}),e.thinking?e.thinking.split(`
6
6
  `).filter(s=>s.trim()).slice(-2).map((s,u)=>c(o,{dimColor:!0,italic:!0,children:[" ",s]},`th${u}`)):null,(e.tools??[]).map((s,u)=>c(o,{color:s.error?"red":"green",children:[" \u25CF ",c(o,{bold:!s.error,dimColor:s.error,children:[s.name,s.arg?c(o,{dimColor:!0,children:["(",s.arg,")"]}):null,s.error?" failed":""]})]},`tool${u}`)),n.split(`
7
7
  `).slice(0,14).map((s,u)=>c(o,{children:[" ",s]},u))]})}function ae(e){let r=e.data??{},t=typeof r.at=="string"||typeof r.at=="number"?new Date(r.at).getTime():Date.now(),n="";switch(e.event){case"run":n=`${r.phase??""} ${r.workflowId??""} ${(r.runId??"").slice(0,8)} ${r.nodeId??""}`.trim();break;case"task":n=`${r.phase??""} ${r.workflowId??""} ${Array.isArray(r.assignedTo)?r.assignedTo.join(","):""} ${r.title?`"${r.title}"`:""}`.trim();break;case"signal":n=`${r.name??""} (${r.scope??""}) ${r.workflowId??""}`.trim();break;case"mesh":n=`${r.peer??""} ${r.healthy?"healthy":"lost"} ${r.delta??""}`.trim();break;case"channel":{n=`${r.direction==="in"?"\u2190":"\u2192"} ${r.channel??""}:${r.chatId??""} ${r.textPreview??""}`.trim();break}case"status":n=`node=${r.node??""} agents=${r.agents??0} active=${(r.active??[]).length}`;break;default:n=typeof r=="string"?r:JSON.stringify(r).slice(0,80)}return{at:Number.isFinite(t)?t:Date.now(),kind:e.event,line:n}}function ce(e){switch(e){case"run":return"cyan";case"task":return"magenta";case"signal":return"green";case"mesh":return"blue";case"channel":return"yellow";default:return"white"}}function T(e,r){let t=(e??"").toString();return t.length>=r?t.slice(0,r-1)+" ":t+" ".repeat(r-t.length)}export{be as App};
8
- //# sourceMappingURL=App-BPNHIGUX.js.map
8
+ //# sourceMappingURL=App-CKPH3HHF.js.map
@@ -1,7 +1,7 @@
1
- import{a as Y,b as Z,c as _,d as ee,e as te,f as ne,g as re}from"./chunk-U2CKL755.js";import{b as Q,g as X}from"./chunk-O6YPEOQF.js";import"./chunk-BTHHSY7D.js";import"./chunk-4YCH6IZV.js";import{useCallback as oe,useMemo as H,useRef as P,useState as k}from"react";import{Box as g,Text as t,useApp as he,useInput as xe,useStdout as Te}from"ink";import{randomUUID as se}from"crypto";var m={accent:"cyan",agent:"green",working:"magenta",error:"red",muted:"gray"},q="\u25C7 agentx",F="\u258C";import{jsx as c,jsxs as o}from"react/jsx-runtime";var ie="/help /agent <id> /agents /clear /who /exit",be=[{cmd:"/agent",desc:"switch the target agent (/agent <id>)"},{cmd:"/agents",desc:"list registered agents"},{cmd:"/clear",desc:"start a fresh session"},{cmd:"/who",desc:"show agent + chat id"},{cmd:"/help",desc:"list commands"},{cmd:"/exit",desc:"quit the chat"}];function Pe({conn:n,agentId:p,channel:f,chatId:a,agents:h}){let{exit:w}=he(),{stdout:O}=Te(),V=Math.max(40,Math.min((O?.columns||80)-4,100)),[z,U]=k([]),[$,S]=k(null),[T,le]=k(p),[A,ae]=k(h),[E,ue]=k(a),[y,x]=k(""),[J,de]=k(""),W=P([]),u=P(-1),M=P(null),R=P(null),v=$?.live??!1,j=H(()=>Z(y,A.map(s=>s.id),process.cwd()),[y,A]),N=H(()=>{let s=/^\/(\w*)$/.exec(y);if(!s)return null;let e=be.filter(r=>r.cmd.slice(1).startsWith(s[1].toLowerCase()));return e.length?e:null},[y]),b=s=>de(s),K=oe(s=>{let e=se().slice(0,8),r=Date.now(),i=new AbortController;R.current=i;let d={id:e,you:s,agentId:T,segs:[],live:!0,startedAt:r};S({...d});let L=()=>S({...d,segs:d.segs.map(C=>({...C}))});(async()=>{try{let C=await X(n,T,s,{channel:f,chatId:E,signal:i.signal,onText:l=>{let I=d.segs[d.segs.length-1];I&&I.type==="text"?I.content=(I.content??"")+l:d.segs.push({type:"text",content:l}),L()},onThinking:l=>{d.thinking=(d.thinking??"")+l,L()},onTool:l=>{l.status==="start"&&l.name?(d.segs.push({type:"tool",name:l.name,arg:l.arg}),L()):l.status==="result"&&l.error&&(d.segs.push({type:"tool",name:l.name??"tool",error:!0}),L())}});U(l=>[...l,{...d,live:!1,elapsedMs:Date.now()-r,outTokens:C.usage?.outputTokens,error:C.error}]),S(null)}catch(C){let l=i.signal.aborted||C?.name==="AbortError";U(I=>[...I,{...d,live:!1,elapsedMs:Date.now()-r,interrupted:l,error:l?void 0:C?.message||String(C)}]),S(null)}finally{R.current===i&&(R.current=null)}})()},[n,T,f,E]),me=oe(s=>{let e=s.trim();if(e){if(W.current.push(e),u.current=-1,e==="/exit"||e==="/quit"){w();return}if(e==="/help"){b(ie);return}if(e==="/who"){b(`@${T} \xB7 chatId=${E} \xB7 channel=${f}`);return}if(e==="/clear"){let r=`chat-cli:${se().slice(0,12)}`;ue(r),U([]),b(`fresh chatId=${r} (warm process dropped)`);return}if(e==="/agents"){Q(n).then(r=>{ae(r),b(r.map(i=>i.id===T?`[${i.id}]`:i.id).join(" "))}).catch(r=>b(`failed to list agents: ${r?.message||r}`));return}if(e.startsWith("/agent ")){let r=e.slice(7).replace(/^@/,"").trim();if(!r){b("usage: /agent <id>");return}if(!A.find(i=>i.id===r)){b(`unknown agent: @${r}`);return}le(r),b(`now talking to @${r}`);return}if(e.startsWith("/")){b(`unknown command \xB7 ${ie}`);return}K(e)}},[n,T,A,f,E,w,K]);xe((s,e)=>{if(e.ctrl&&s==="c"){w();return}if(e.escape){if(v&&R.current){R.current.abort();return}w();return}if(e.tab){if(N){x(N[0].cmd+" ");return}let i=_(y,M,A.map(d=>d.id),process.cwd());i!==null&&(x(i),u.current=-1);return}if(e.upArrow){let i=W.current;if(!i.length)return;u.current=u.current<0?i.length-1:Math.max(0,u.current-1),x(i[u.current]??"");return}if(e.downArrow){let i=W.current;if(u.current<0)return;if(u.current=u.current+1,u.current>=i.length){u.current=-1,x("");return}x(i[u.current]??"");return}if(e.backspace||e.delete){M.current=null,x(i=>i.slice(0,-1));return}if(e.ctrl||e.meta)return;let r=ee(s??"",e.return,y);switch(r.kind){case"paste":M.current=null,x(i=>i+r.text),u.current=-1;return;case"type":M.current=null,x(i=>i+r.text),u.current=-1;return;case"continue":x(r.buffer);return;case"submit":if(v)return;M.current=null,x(""),me(r.text);return;case"none":return}});let G=v?m.working:m.accent,ge=y.length?y.split(`
1
+ import{a as Y,b as Z,c as _,d as ee,e as te,f as ne,g as re}from"./chunk-U2CKL755.js";import{b as Q,g as X}from"./chunk-5IA2CYHN.js";import"./chunk-P5ZBBEPL.js";import"./chunk-FIOB3MEX.js";import{useCallback as oe,useMemo as H,useRef as P,useState as k}from"react";import{Box as g,Text as t,useApp as he,useInput as xe,useStdout as Te}from"ink";import{randomUUID as se}from"crypto";var m={accent:"cyan",agent:"green",working:"magenta",error:"red",muted:"gray"},q="\u25C7 agentx",F="\u258C";import{jsx as c,jsxs as o}from"react/jsx-runtime";var ie="/help /agent <id> /agents /clear /who /exit",be=[{cmd:"/agent",desc:"switch the target agent (/agent <id>)"},{cmd:"/agents",desc:"list registered agents"},{cmd:"/clear",desc:"start a fresh session"},{cmd:"/who",desc:"show agent + chat id"},{cmd:"/help",desc:"list commands"},{cmd:"/exit",desc:"quit the chat"}];function Pe({conn:n,agentId:p,channel:f,chatId:a,agents:h}){let{exit:w}=he(),{stdout:O}=Te(),V=Math.max(40,Math.min((O?.columns||80)-4,100)),[z,U]=k([]),[$,S]=k(null),[T,le]=k(p),[A,ae]=k(h),[E,ue]=k(a),[y,x]=k(""),[J,de]=k(""),W=P([]),u=P(-1),M=P(null),R=P(null),v=$?.live??!1,j=H(()=>Z(y,A.map(s=>s.id),process.cwd()),[y,A]),N=H(()=>{let s=/^\/(\w*)$/.exec(y);if(!s)return null;let e=be.filter(r=>r.cmd.slice(1).startsWith(s[1].toLowerCase()));return e.length?e:null},[y]),b=s=>de(s),K=oe(s=>{let e=se().slice(0,8),r=Date.now(),i=new AbortController;R.current=i;let d={id:e,you:s,agentId:T,segs:[],live:!0,startedAt:r};S({...d});let L=()=>S({...d,segs:d.segs.map(C=>({...C}))});(async()=>{try{let C=await X(n,T,s,{channel:f,chatId:E,signal:i.signal,onText:l=>{let I=d.segs[d.segs.length-1];I&&I.type==="text"?I.content=(I.content??"")+l:d.segs.push({type:"text",content:l}),L()},onThinking:l=>{d.thinking=(d.thinking??"")+l,L()},onTool:l=>{l.status==="start"&&l.name?(d.segs.push({type:"tool",name:l.name,arg:l.arg}),L()):l.status==="result"&&l.error&&(d.segs.push({type:"tool",name:l.name??"tool",error:!0}),L())}});U(l=>[...l,{...d,live:!1,elapsedMs:Date.now()-r,outTokens:C.usage?.outputTokens,error:C.error}]),S(null)}catch(C){let l=i.signal.aborted||C?.name==="AbortError";U(I=>[...I,{...d,live:!1,elapsedMs:Date.now()-r,interrupted:l,error:l?void 0:C?.message||String(C)}]),S(null)}finally{R.current===i&&(R.current=null)}})()},[n,T,f,E]),me=oe(s=>{let e=s.trim();if(e){if(W.current.push(e),u.current=-1,e==="/exit"||e==="/quit"){w();return}if(e==="/help"){b(ie);return}if(e==="/who"){b(`@${T} \xB7 chatId=${E} \xB7 channel=${f}`);return}if(e==="/clear"){let r=`chat-cli:${se().slice(0,12)}`;ue(r),U([]),b(`fresh chatId=${r} (warm process dropped)`);return}if(e==="/agents"){Q(n).then(r=>{ae(r),b(r.map(i=>i.id===T?`[${i.id}]`:i.id).join(" "))}).catch(r=>b(`failed to list agents: ${r?.message||r}`));return}if(e.startsWith("/agent ")){let r=e.slice(7).replace(/^@/,"").trim();if(!r){b("usage: /agent <id>");return}if(!A.find(i=>i.id===r)){b(`unknown agent: @${r}`);return}le(r),b(`now talking to @${r}`);return}if(e.startsWith("/")){b(`unknown command \xB7 ${ie}`);return}K(e)}},[n,T,A,f,E,w,K]);xe((s,e)=>{if(e.ctrl&&s==="c"){w();return}if(e.escape){if(v&&R.current){R.current.abort();return}w();return}if(e.tab){if(N){x(N[0].cmd+" ");return}let i=_(y,M,A.map(d=>d.id),process.cwd());i!==null&&(x(i),u.current=-1);return}if(e.upArrow){let i=W.current;if(!i.length)return;u.current=u.current<0?i.length-1:Math.max(0,u.current-1),x(i[u.current]??"");return}if(e.downArrow){let i=W.current;if(u.current<0)return;if(u.current=u.current+1,u.current>=i.length){u.current=-1,x("");return}x(i[u.current]??"");return}if(e.backspace||e.delete){M.current=null,x(i=>i.slice(0,-1));return}if(e.ctrl||e.meta)return;let r=ee(s??"",e.return,y);switch(r.kind){case"paste":M.current=null,x(i=>i+r.text),u.current=-1;return;case"type":M.current=null,x(i=>i+r.text),u.current=-1;return;case"continue":x(r.buffer);return;case"submit":if(v)return;M.current=null,x(""),me(r.text);return;case"none":return}});let G=v?m.working:m.accent,ge=y.length?y.split(`
2
2
  `):[""],B=A.find(s=>s.id===T),D=ne(B?.tier??"",B?.model),pe=O?.rows||24,fe=z.slice(-40);return o(g,{flexDirection:"column",height:pe,children:[o(g,{flexDirection:"column",flexGrow:1,justifyContent:"flex-end",overflow:"hidden",children:[z.length===0&&!$?o(g,{flexDirection:"column",children:[o(t,{children:[c(t,{color:m.accent,bold:!0,children:q}),c(t,{dimColor:!0,children:" \u2014 chat with "}),o(t,{color:m.accent,children:["@",T]}),c(t,{dimColor:!0,children:" via "}),o(t,{color:D.color,bold:!0,children:[D.glyph," ",D.label]}),B?.model?o(t,{dimColor:!0,children:[" (",B.model,")"]}):null]}),c(t,{dimColor:!0,children:"streaming \xB7 markdown \xB7 tools \xB7 type a message, or / for commands"})]}):null,fe.map(s=>c(ce,{turn:s,width:V},s.id)),$?c(ce,{turn:$,width:V}):null]}),N?c(g,{flexDirection:"column",marginTop:1,children:N.map((s,e)=>o(g,{children:[c(t,{color:e===0?m.accent:m.muted,bold:e===0,children:s.cmd.padEnd(10)}),c(t,{dimColor:!0,children:s.desc})]},s.cmd))}):j?o(g,{marginTop:1,children:[c(t,{color:m.accent,children:"\u21B9 "}),j.items.map((s,e)=>o(t,{color:e===0?m.accent:m.muted,children:[s,e<j.items.length-1?" ":""]},e))]}):J?c(g,{marginTop:1,children:c(t,{dimColor:!0,children:J})}):null,c(g,{borderStyle:"single",borderColor:G,borderLeft:!1,borderRight:!1,borderDimColor:!v,width:"100%",children:c(g,{flexDirection:"column",width:"100%",children:ge.map((s,e,r)=>o(g,{children:[c(t,{color:G,bold:!0,children:e===0?"\u203A ":" "}),c(t,{children:s}),e===r.length-1&&!v?c(t,{color:G,children:"\u258F"}):null]},e))})}),o(g,{justifyContent:"space-between",children:[o(t,{children:[c(t,{color:m.accent,bold:!0,children:q}),o(t,{dimColor:!0,children:[" \xB7 @",T," \xB7 "]}),o(t,{color:D.color,bold:!0,children:[D.glyph," ",D.label]}),B?.model?o(t,{dimColor:!0,children:[" ",re(B.model)]}):null]}),o(t,{dimColor:!0,children:["\u21B5 send / cmds @ mention esc ",v?"stop":"exit"]})]})]})}function ce({turn:n,width:p}){let f=H(()=>ye(n,p),[n,p]),a=n.segs.some(h=>h.type==="text"&&h.content);return o(g,{flexDirection:"column",marginBottom:1,children:[o(t,{color:m.accent,bold:!0,children:[F," you"]}),n.you.split(`
3
3
  `).map((h,w)=>o(t,{color:"white",children:[" ",h]},`u${w}`)),o(t,{color:m.agent,bold:!0,children:[F," ",n.agentId??"agent"]}),n.thinking?c(g,{flexDirection:"column",children:we(n.thinking,n.live?3:2).map((h,w)=>o(t,{dimColor:!0,italic:!0,children:[" ",h]},`th${w}`))}):null,f,n.live?c(te,{startedAt:n.startedAt,phase:a?"responding":"thinking",hint:"esc to interrupt"}):n.interrupted?o(t,{dimColor:!0,children:[" \u2298 interrupted \xB7 ",(n.elapsedMs/1e3).toFixed(1),"s"]}):n.error?o(t,{color:"red",children:[" \u2717 ",n.error]}):n.elapsedMs!=null?o(t,{dimColor:!0,children:[" \xB7 ",(n.elapsedMs/1e3).toFixed(1),"s",n.outTokens!=null?`, ${n.outTokens} tok`:""]}):null]})}function we(n,p){return n.split(`
4
4
  `).filter(a=>a.trim()).slice(-p)}function ye(n,p){let f=[];return n.segs.forEach((a,h)=>{a.type==="tool"?f.push(o(t,{color:a.error?"red":"green",children:[" ","\u25CF ",o(t,{bold:!a.error,dimColor:a.error,children:[a.name,a.arg?o(t,{dimColor:!0,children:["(",a.arg,")"]}):null,a.error?" failed":""]})]},h)):a.content&&f.push(c(t,{children:Ce(Y(a.content,p,{balance:n.live}))},h))}),f}function Ce(n){return n.split(`
5
5
  `).map(p=>p.length?" "+p:p).join(`
6
6
  `)}export{Pe as ChatApp};
7
- //# sourceMappingURL=ChatApp-6XSSZB4U.js.map
7
+ //# sourceMappingURL=ChatApp-MX7IG4K2.js.map
@@ -0,0 +1,2 @@
1
+ import{A as a,B as b,C as c}from"./chunk-MHIMGM4G.js";import"./chunk-SFQUP3BP.js";import"./chunk-QOYAX2FT.js";import"./chunk-LCIREYTD.js";import"./chunk-L2WSPY6S.js";import"./chunk-VDMYBPYB.js";import"./chunk-DSYKYZMT.js";import"./chunk-FIOB3MEX.js";export{a as createAgentContext,b as generate,c as generateStream};
2
+ //# sourceMappingURL=agent-2U574KBZ.js.map
@@ -0,0 +1,2 @@
1
+ import{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r}from"./chunk-3SCPRYJB.js";import"./chunk-F4HZTZHW.js";import"./chunk-FIOB3MEX.js";export{a as ARTICLE_FIELDS_SEAT,h as BACKFILLABLE,i as BACKFILL_ORDER,j as FIELD_SOURCES,g as IDENTITY_SLOTS,f as REQUIRED_FIELDS,b as TIERS,d as TIER_MEANING,c as TIER_RANK,n as articleFieldsState,q as fieldChecklistMarkdown,m as fieldQuestions,l as fieldsByTier,k as fieldsFor,e as isCritical,p as nextActions,o as reportFields,r as tierLadderMarkdown};
2
+ //# sourceMappingURL=article-fields-L4D2BOGY.js.map
@@ -0,0 +1,3 @@
1
+ import{a as s,b as n,c as a,f as h}from"./chunk-F4HZTZHW.js";import"./chunk-FIOB3MEX.js";h();var b="article-quality",d={identityFirst:s("Does the article state what the subject is before recounting what happened to it?"),hasIdentifiers:s("Does the article contain a concrete identifier \u2014 a phone number, address, URL, path, account or handle?"),standsAlone:s("Could a reader who has never seen the source material act on this article without going back to it?"),completeness:a(["A log of events; the subject itself is barely described","Describes the subject loosely, but a reader would still have to ask basics","Covers the subject, with one or two facts a reader would expect and not find","A reader could act on this without asking anything further"],"How completely does this article describe its subject?")};function p(e){let t={none:"Nothing important is missing",purpose:"Does not say what the subject is for, or why it matters to us",identifiers:"Describes things by name where a concrete value was available",relationships:"Does not connect the subject to the projects or people around it","too-narrative":"Mostly a chronology of events rather than a description"};return(e==="person"||e==="place"||e==="project")&&(t["contact-details"]="No way to reach, locate or access the subject"),(e==="person"||e==="project")&&(t["role-or-org"]="Does not say what the subject's role is, or what it belongs to"),t}function y(e){return{...d,biggestGap:n(p(e),"What is most conspicuously missing from this article?")}}function w(e,t=12e3){let o=String(e.body??"").trim();return{title:e.title,type:e.type??null,tags:e.tags?.length?e.tags.join(", "):null,body:o.length>t?`${o.slice(0,t)}
2
+ \u2026[truncated]`:o,truncated:o.length>t}}function f(e,t={}){let o=e.completeness.score,c=e.identityFirst.noul,l=e.hasIdentifiers.noul,r=e.standsAlone.noul,u=e.biggestGap.choice,i=t.minCompleteness??2;return{completeness:o,identityFirst:c,hasIdentifiers:l,standsAlone:r,biggestGap:u,adequate:o>=i&&r>=.5,needsRework:o<i||r<.5}}export{b as ARTICLE_QUALITY_SEAT,d as articleQualityQuestions,y as articleQualityQuestionsFor,w as articleQualityState,p as gapCriteria,f as gradeArticle};
3
+ //# sourceMappingURL=article-quality-44ONVGLN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/decisions/seats/article-quality.ts"],"sourcesContent":["import { choice, noul, score } from \"../questions\"\nimport type { AnswersFor, ChoiceAnswer, NoulAnswer, ScoreAnswer, StateValue } from \"../types\"\n\n// Grading what absorb wrote.\n//\n// The failure that motivated this is not a bad article, it is a\n// plausible one. A client contact's page recorded the invoice thread in\n// detail — two dated exchanges, the hourly rate, the instalment — and\n// under \"How to reach him\" said \"WhatsApp, in Arabic\". The channel, not\n// the number. It reads well and cannot answer either question a person\n// article exists for: who is this, and how do I reach them.\n//\n// That is invisible to every check absorb already runs. The JSON parsed,\n// the type was valid, the wikilinks resolved, the length was in range.\n// Nothing in the pipeline asks whether the article is USEFUL, because\n// that is a judgement, and judgements were expensive until they weren't.\n//\n// Deliberately type-agnostic. A person needs a phone number and a place\n// needs a hostname, but both fail the same way — recounting history\n// while omitting identity — and one rubric over the whole corpus is\n// comparable in a way seven type-specific ones are not.\n\nexport const ARTICLE_QUALITY_SEAT = \"article-quality\"\n\nexport const articleQualityQuestions = {\n identityFirst: noul(\n \"Does the article state what the subject is before recounting what happened to it?\",\n ),\n hasIdentifiers: noul(\n \"Does the article contain a concrete identifier — a phone number, address, URL, path, account or handle?\",\n ),\n standsAlone: noul(\n \"Could a reader who has never seen the source material act on this article without going back to it?\",\n ),\n completeness: score(\n [\n \"A log of events; the subject itself is barely described\",\n \"Describes the subject loosely, but a reader would still have to ask basics\",\n \"Covers the subject, with one or two facts a reader would expect and not find\",\n \"A reader could act on this without asking anything further\",\n ],\n \"How completely does this article describe its subject?\",\n ),\n}\n\n/**\n * The gap options, narrowed to what can apply to this type.\n *\n * A Choice must return something. The first version offered\n * \"contact-details\" to every article and it won on concept pages about\n * invoice registers and bank downloaders — you do not phone a register.\n * With one strongly-worded attractor in the set, the model picks it\n * rather than the weaker option that actually fits, so the answer said\n * more about the option list than the article.\n *\n * Excluding inapplicable options is not a thumb on the scale. It is\n * declining to ask a question the article cannot answer.\n */\nexport function gapCriteria(type?: string): Record<string, string> {\n const base: Record<string, string> = {\n none: \"Nothing important is missing\",\n purpose: \"Does not say what the subject is for, or why it matters to us\",\n identifiers: \"Describes things by name where a concrete value was available\",\n relationships: \"Does not connect the subject to the projects or people around it\",\n \"too-narrative\": \"Mostly a chronology of events rather than a description\",\n }\n // Only a thing you contact or visit can be missing a way to reach it.\n if (type === \"person\" || type === \"place\" || type === \"project\") {\n base[\"contact-details\"] = \"No way to reach, locate or access the subject\"\n }\n if (type === \"person\" || type === \"project\") {\n base[\"role-or-org\"] = \"Does not say what the subject's role is, or what it belongs to\"\n }\n return base\n}\n\n/** Questions for one article. The gap options depend on its type. */\nexport function articleQualityQuestionsFor(type?: string) {\n return {\n ...articleQualityQuestions,\n biggestGap: choice(gapCriteria(type), \"What is most conspicuously missing from this article?\"),\n }\n}\n\nexport type ArticleQualityAnswers = AnswersFor<ReturnType<typeof articleQualityQuestionsFor>>\n\nexport interface ArticleForReview {\n path: string\n title: string\n type?: string\n tags?: string[]\n body: string\n}\n\n/** The whole article, because the judgement is about what is absent —\n * and a truncated article is missing things for the wrong reason. */\nexport function articleQualityState(a: ArticleForReview, maxChars = 12_000): StateValue {\n const body = String(a.body ?? \"\").trim()\n return {\n title: a.title,\n type: a.type ?? null,\n tags: a.tags?.length ? a.tags.join(\", \") : null,\n body: body.length > maxChars ? `${body.slice(0, maxChars)}\\n…[truncated]` : body,\n truncated: body.length > maxChars,\n }\n}\n\nexport interface QualityVerdict {\n completeness: number\n identityFirst: number\n hasIdentifiers: number\n standsAlone: number\n biggestGap: string\n /** Good enough to leave alone. */\n adequate: boolean\n /** Worth queueing for a rewrite pass. */\n needsRework: boolean\n}\n\nexport function gradeArticle(\n answers: ArticleQualityAnswers,\n opts: { minCompleteness?: number } = {},\n): QualityVerdict {\n const completeness = (answers.completeness as ScoreAnswer).score\n const identityFirst = (answers.identityFirst as NoulAnswer).noul\n const hasIdentifiers = (answers.hasIdentifiers as NoulAnswer).noul\n const standsAlone = (answers.standsAlone as NoulAnswer).noul\n const gap = (answers.biggestGap as ChoiceAnswer).choice\n const floor = opts.minCompleteness ?? 2\n\n return {\n completeness,\n identityFirst,\n hasIdentifiers,\n standsAlone,\n biggestGap: gap,\n // standsAlone is the one that decides it. An article can open with a\n // tidy identity line and still send the reader back to the source.\n adequate: completeness >= floor && standsAlone >= 0.5,\n needsRework: completeness < floor || standsAlone < 0.5,\n }\n}\n"],"mappings":"yFAAAA,IAsBO,IAAMC,EAAuB,kBAEvBC,EAA0B,CACrC,cAAeC,EACb,mFACF,EACA,eAAgBA,EACd,8GACF,EACA,YAAaA,EACX,qGACF,EACA,aAAcC,EACZ,CACE,0DACA,6EACA,+EACA,4DACF,EACA,wDACF,CACF,EAeO,SAASC,EAAYC,EAAuC,CACjE,IAAMC,EAA+B,CACnC,KAAM,+BACN,QAAS,gEACT,YAAa,gEACb,cAAe,mEACf,gBAAiB,yDACnB,EAEA,OAAID,IAAS,UAAYA,IAAS,SAAWA,IAAS,aACpDC,EAAK,iBAAiB,EAAI,kDAExBD,IAAS,UAAYA,IAAS,aAChCC,EAAK,aAAa,EAAI,kEAEjBA,CACT,CAGO,SAASC,EAA2BF,EAAe,CACxD,MAAO,CACL,GAAGJ,EACH,WAAYO,EAAOJ,EAAYC,CAAI,EAAG,uDAAuD,CAC/F,CACF,CAcO,SAASI,EAAoBC,EAAqBC,EAAW,KAAoB,CACtF,IAAMC,EAAO,OAAOF,EAAE,MAAQ,EAAE,EAAE,KAAK,EACvC,MAAO,CACL,MAAOA,EAAE,MACT,KAAMA,EAAE,MAAQ,KAChB,KAAMA,EAAE,MAAM,OAASA,EAAE,KAAK,KAAK,IAAI,EAAI,KAC3C,KAAME,EAAK,OAASD,EAAW,GAAGC,EAAK,MAAM,EAAGD,CAAQ;AAAA,mBAAoBC,EAC5E,UAAWA,EAAK,OAASD,CAC3B,CACF,CAcO,SAASE,EACdC,EACAC,EAAqC,CAAC,EACtB,CAChB,IAAMC,EAAgBF,EAAQ,aAA6B,MACrDG,EAAiBH,EAAQ,cAA6B,KACtDI,EAAkBJ,EAAQ,eAA8B,KACxDK,EAAeL,EAAQ,YAA2B,KAClDM,EAAON,EAAQ,WAA4B,OAC3CO,EAAQN,EAAK,iBAAmB,EAEtC,MAAO,CACL,aAAAC,EACA,cAAAC,EACA,eAAAC,EACA,YAAAC,EACA,WAAYC,EAGZ,SAAUJ,GAAgBK,GAASF,GAAe,GAClD,YAAaH,EAAeK,GAASF,EAAc,EACrD,CACF","names":["init_questions","ARTICLE_QUALITY_SEAT","articleQualityQuestions","noul","score","gapCriteria","type","base","articleQualityQuestionsFor","choice","articleQualityState","a","maxChars","body","gradeArticle","answers","opts","completeness","identityFirst","hasIdentifiers","standsAlone","gap","floor"]}
@@ -1,3 +1,3 @@
1
- import"./chunk-4YCH6IZV.js";function h(e,t,o,n){let r=[],s=e.frontmatter,f=e.instructions??"",l="PASS";if(s.references?.length)for(let i of s.references)m(i,t)||(l="FAILING",r.push(`references: "${i}" does not resolve in the registry`));if(s.delegatesTo?.length)for(let i of s.delegatesTo)n.has(i)||(l="FAILING",r.push(`delegatesTo: "${i}" has no installed SKILL.md`));k(f)&&(l!=="FAILING"&&(l="REVIEW"),r.push("body contains raw `python3 ~/scripts/...` inside a code block \u2014 delegate to the relevant skill instead"));let d=["deploy","ssh","email","gitlab","ops"],p=(s.category||"").toLowerCase(),I=(s.tags||[]).map(i=>i.toLowerCase());(d.includes(p)||I.some(i=>d.includes(i))||/\b(deploy|ssh|gitlab|hotmail|server|host)\b/i.test(s.description||""))&&(!s.references||s.references.length===0)&&(l==="PASS"&&(l="REVIEW"),r.push("infrastructure-flavored skill has no `references:` frontmatter \u2014 facts should be cited from the registry"));let c=f.match(/\b\d{1,3}(?:\.\d{1,3}){3}\b/g);if(c&&c.length>0){let i=new Set([...t.byId.values()].flatMap(a=>Object.values(a.fields).filter(g=>typeof g=="string"))),u=c.filter(a=>!i.has(a));u.length>0&&(l==="PASS"&&(l="REVIEW"),r.push(`body contains raw IP(s) not in the registry: ${[...new Set(u)].slice(0,3).join(", ")}`))}return{skill:e,name:s.name,verdict:l,reasons:r}}function S(e,t){let o=new Set;for(let r of e.recipes)for(let s of r.skills)o.add(s);let n=[];for(let r of o)t.has(r)||n.push({skill:null,name:r,verdict:"FAILING",reasons:["required by a recipe, but no SKILL.md is installed"]});return n}function A(e){let t=new Set(e.skills.map(n=>n.frontmatter.name));return[...e.skills.map(n=>h(n,e.references,e.recipes,t)),...S(e.recipes,t)]}function k(e){let t=!1;for(let o of e.split(`
1
+ import"./chunk-FIOB3MEX.js";function h(e,t,o,n){let r=[],s=e.frontmatter,f=e.instructions??"",l="PASS";if(s.references?.length)for(let i of s.references)m(i,t)||(l="FAILING",r.push(`references: "${i}" does not resolve in the registry`));if(s.delegatesTo?.length)for(let i of s.delegatesTo)n.has(i)||(l="FAILING",r.push(`delegatesTo: "${i}" has no installed SKILL.md`));k(f)&&(l!=="FAILING"&&(l="REVIEW"),r.push("body contains raw `python3 ~/scripts/...` inside a code block \u2014 delegate to the relevant skill instead"));let d=["deploy","ssh","email","gitlab","ops"],p=(s.category||"").toLowerCase(),I=(s.tags||[]).map(i=>i.toLowerCase());(d.includes(p)||I.some(i=>d.includes(i))||/\b(deploy|ssh|gitlab|hotmail|server|host)\b/i.test(s.description||""))&&(!s.references||s.references.length===0)&&(l==="PASS"&&(l="REVIEW"),r.push("infrastructure-flavored skill has no `references:` frontmatter \u2014 facts should be cited from the registry"));let c=f.match(/\b\d{1,3}(?:\.\d{1,3}){3}\b/g);if(c&&c.length>0){let i=new Set([...t.byId.values()].flatMap(a=>Object.values(a.fields).filter(g=>typeof g=="string"))),u=c.filter(a=>!i.has(a));u.length>0&&(l==="PASS"&&(l="REVIEW"),r.push(`body contains raw IP(s) not in the registry: ${[...new Set(u)].slice(0,3).join(", ")}`))}return{skill:e,name:s.name,verdict:l,reasons:r}}function S(e,t){let o=new Set;for(let r of e.recipes)for(let s of r.skills)o.add(s);let n=[];for(let r of o)t.has(r)||n.push({skill:null,name:r,verdict:"FAILING",reasons:["required by a recipe, but no SKILL.md is installed"]});return n}function A(e){let t=new Set(e.skills.map(n=>n.frontmatter.name));return[...e.skills.map(n=>h(n,e.references,e.recipes,t)),...S(e.recipes,t)]}function k(e){let t=!1;for(let o of e.split(`
2
2
  `)){if(/^\s*```/.test(o)){t=!t;continue}if(t&&/python3\s+~\/scripts\//.test(o))return!0}return!1}function m(e,t){if(e.endsWith(".*")){let o=e.slice(0,-2);for(let n of t.byId.keys())if(n===o||n.startsWith(`${o}.`))return!0;return!1}return t.byId.has(e)}export{A as auditAll,S as auditMissingSkills,h as auditSkill};
3
- //# sourceMappingURL=audit-JU3HWISB.js.map
3
+ //# sourceMappingURL=audit-4245CX6P.js.map
@@ -0,0 +1,6 @@
1
+ import{g as d,h as u,i as g}from"./chunk-3SCPRYJB.js";import"./chunk-F4HZTZHW.js";import"./chunk-FIOB3MEX.js";function h(c,f){let i=[];for(let n of f){let t=c.fields[n];t?.value?.trim()&&i.push(`${t.value} (${n}, via ${t.source})`)}return i.length>0?i.join("; "):null}var p="## Identity";function E(c,f,i){let n=d[f];if(!n)return null;let t=c.split(`
2
+ `),r=`- **${n.heading}:** ${i}`,l=t.findIndex(e=>e.trim().toLowerCase()===p.toLowerCase()),o=t.length;if(l>=0){for(let e=l+1;e<t.length;e++)if(/^##\s/.test(t[e])){o=e;break}}if(l>=0){for(let e=l+1;e<o;e++){let a=t[e].match(/^\s*-\s*\*\*([^*]+)\*\*\s*:?/);if(!(!a||!n.match.test(a[1])))return t[e].includes(i)?null:(t[e]=r,{content:t.join(`
3
+ `),replaced:!0})}return t.splice(o,0,r),{content:t.join(`
4
+ `),replaced:!1}}let s=t.length;for(let e=0;e<t.length;e++)if(/^##\s/.test(t[e])){s=e;break}return t.splice(s,0,p,"",r,""),{content:t.join(`
5
+ `),replaced:!1}}function I(c,f,i){let n=c,t=[];for(let r of g){if(!i.includes(r))continue;let l=u[r],o=h(f,l.from);if(!o)continue;let s=E(n,r,o);s&&(n=s.content,t.push({field:r,heading:l.heading,value:o,replaced:s.replaced}))}return{content:n,edits:t}}export{I as backfillArticle,E as patchIdentityField,h as renderValue};
6
+ //# sourceMappingURL=backfill-MTD2B62I.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/wiki/backfill.ts"],"sourcesContent":["import { BACKFILLABLE, BACKFILL_ORDER, IDENTITY_SLOTS } from \"@/decisions/seats/article-fields\"\nimport type { MergedEntity } from \"./facts\"\n\n// Writing resolved identifiers back into articles that are missing them.\n//\n// `wiki gaps` reports that a person article has no contact value and\n// that wacli can supply one. Reporting is where it stopped, so the\n// corpus kept a list of things nobody was going to do by hand. This\n// closes that loop for the one class of gap where closing it needs no\n// judgement: a contact value is a string a system of record handed us,\n// and copying it is the whole operation.\n//\n// Everything else stays a report. A project's status, why a decision was\n// made, who owns a relationship — no lookup returns those, and a\n// confident guess written into the source of truth is worse than an\n// acknowledged gap.\n//\n// All edits go through WikiStore.writeArticle, which versions the prior\n// content into `_versions/` first, so every backfill is reversible.\n\nexport interface FieldEdit {\n field: string\n heading: string\n value: string\n /** Replaced an existing bullet rather than adding one. */\n replaced: boolean\n}\n\nexport interface BackfillResult {\n content: string\n edits: FieldEdit[]\n}\n\n/**\n * Render one identity bullet's value from resolved facts.\n *\n * Several facts can feed one field — a person may have a number, an\n * address and a handle — and all of them belong in the article. Each\n * carries the source it came from, because a reader who finds two\n * numbers needs to know which system said what.\n */\nexport function renderValue(\n entity: MergedEntity,\n from: string[],\n): string | null {\n const parts: string[] = []\n for (const key of from) {\n const f = entity.fields[key]\n if (!f?.value?.trim()) continue\n parts.push(`${f.value} (${key}, via ${f.source})`)\n }\n return parts.length > 0 ? parts.join(\"; \") : null\n}\n\nconst IDENTITY_HEADING = \"## Identity\"\n\n/**\n * Replace or insert one identity bullet.\n *\n * Matching is by keyword rather than by exact heading because the\n * headings were written by a model from the prompt's field labels and\n * vary — \"Contact identifiers\", \"How to reach him\", \"Contact details\".\n * Rewriting them all to a canonical form would be a bigger edit than\n * the fact being added, so the patcher meets the article where it is.\n */\nexport function patchIdentityField(\n content: string,\n field: string,\n value: string,\n): { content: string; replaced: boolean } | null {\n // The wider map: a human answer may fill any identity slot, while\n // only BACKFILLABLE fields may be written without being asked.\n const spec = IDENTITY_SLOTS[field]\n if (!spec) return null\n\n const lines = content.split(\"\\n\")\n const bullet = `- **${spec.heading}:** ${value}`\n\n // Find the Identity section's bounds.\n const start = lines.findIndex((l) => l.trim().toLowerCase() === IDENTITY_HEADING.toLowerCase())\n let end = lines.length\n if (start >= 0) {\n for (let i = start + 1; i < lines.length; i++) {\n if (/^##\\s/.test(lines[i])) { end = i; break }\n }\n }\n\n if (start >= 0) {\n for (let i = start + 1; i < end; i++) {\n const m = lines[i].match(/^\\s*-\\s*\\*\\*([^*]+)\\*\\*\\s*:?/)\n if (!m || !spec.match.test(m[1])) continue\n // Already carries this exact value — leave it alone so the\n // backfill is idempotent and re-running writes no new version.\n if (lines[i].includes(value)) return null\n lines[i] = bullet\n return { content: lines.join(\"\\n\"), replaced: true }\n }\n // Section exists, field does not.\n lines.splice(end, 0, bullet)\n return { content: lines.join(\"\\n\"), replaced: false }\n }\n\n // No Identity section at all — articles written before the tier work.\n // It goes after the opening paragraph, which is the definition, so\n // identity still follows \"what this is\" rather than displacing it.\n let insertAt = lines.length\n for (let i = 0; i < lines.length; i++) {\n if (/^##\\s/.test(lines[i])) { insertAt = i; break }\n }\n lines.splice(insertAt, 0, IDENTITY_HEADING, \"\", bullet, \"\")\n return { content: lines.join(\"\\n\"), replaced: false }\n}\n\n/**\n * Apply every backfillable field the facts can answer.\n *\n * Only fields named in `missing` are touched. A field the grader found\n * present is left exactly as written, even when a source could supply\n * a different value — the article may carry a better-sourced fact than\n * the directory does, and overwriting it would make the wiki a mirror\n * of the systems rather than a record that can correct them.\n */\nexport function backfillArticle(\n content: string,\n entity: MergedEntity,\n missing: string[],\n): BackfillResult {\n let out = content\n const edits: FieldEdit[] = []\n\n for (const field of BACKFILL_ORDER) {\n if (!missing.includes(field)) continue\n const spec = BACKFILLABLE[field]\n const value = renderValue(entity, spec.from)\n if (!value) continue\n const patched = patchIdentityField(out, field, value)\n if (!patched) continue\n out = patched.content\n edits.push({ field, heading: spec.heading, value, replaced: patched.replaced })\n }\n\n return { content: out, edits }\n}\n"],"mappings":"8GAyCO,SAASA,EACdC,EACAC,EACe,CACf,IAAMC,EAAkB,CAAC,EACzB,QAAWC,KAAOF,EAAM,CACtB,IAAMG,EAAIJ,EAAO,OAAOG,CAAG,EACtBC,GAAG,OAAO,KAAK,GACpBF,EAAM,KAAK,GAAGE,EAAE,UAAUD,UAAYC,EAAE,SAAS,EAEnD,OAAOF,EAAM,OAAS,EAAIA,EAAM,KAAK,IAAI,EAAI,IAC/C,CAEA,IAAMG,EAAmB,cAWlB,SAASC,EACdC,EACAC,EACAC,EAC+C,CAG/C,IAAMC,EAAOC,EAAeH,CAAK,EACjC,GAAI,CAACE,EAAM,OAAO,KAElB,IAAME,EAAQL,EAAQ,MAAM;AAAA,CAAI,EAC1BM,EAAS,OAAOH,EAAK,cAAcD,IAGnCK,EAAQF,EAAM,UAAWG,GAAMA,EAAE,KAAK,EAAE,YAAY,IAAMV,EAAiB,YAAY,CAAC,EAC1FW,EAAMJ,EAAM,OAChB,GAAIE,GAAS,GACX,QAASG,EAAIH,EAAQ,EAAGG,EAAIL,EAAM,OAAQK,IACxC,GAAI,QAAQ,KAAKL,EAAMK,CAAC,CAAC,EAAG,CAAED,EAAMC,EAAG,OAI3C,GAAIH,GAAS,EAAG,CACd,QAASG,EAAIH,EAAQ,EAAGG,EAAID,EAAKC,IAAK,CACpC,IAAMC,EAAIN,EAAMK,CAAC,EAAE,MAAM,8BAA8B,EACvD,GAAI,GAACC,GAAK,CAACR,EAAK,MAAM,KAAKQ,EAAE,CAAC,CAAC,GAG/B,OAAIN,EAAMK,CAAC,EAAE,SAASR,CAAK,EAAU,MACrCG,EAAMK,CAAC,EAAIJ,EACJ,CAAE,QAASD,EAAM,KAAK;AAAA,CAAI,EAAG,SAAU,EAAK,GAGrD,OAAAA,EAAM,OAAOI,EAAK,EAAGH,CAAM,EACpB,CAAE,QAASD,EAAM,KAAK;AAAA,CAAI,EAAG,SAAU,EAAM,EAMtD,IAAIO,EAAWP,EAAM,OACrB,QAASK,EAAI,EAAGA,EAAIL,EAAM,OAAQK,IAChC,GAAI,QAAQ,KAAKL,EAAMK,CAAC,CAAC,EAAG,CAAEE,EAAWF,EAAG,MAE9C,OAAAL,EAAM,OAAOO,EAAU,EAAGd,EAAkB,GAAIQ,EAAQ,EAAE,EACnD,CAAE,QAASD,EAAM,KAAK;AAAA,CAAI,EAAG,SAAU,EAAM,CACtD,CAWO,SAASQ,EACdb,EACAP,EACAqB,EACgB,CAChB,IAAIC,EAAMf,EACJgB,EAAqB,CAAC,EAE5B,QAAWf,KAASgB,EAAgB,CAClC,GAAI,CAACH,EAAQ,SAASb,CAAK,EAAG,SAC9B,IAAME,EAAOe,EAAajB,CAAK,EACzBC,EAAQV,EAAYC,EAAQU,EAAK,IAAI,EAC3C,GAAI,CAACD,EAAO,SACZ,IAAMiB,EAAUpB,EAAmBgB,EAAKd,EAAOC,CAAK,EAC/CiB,IACLJ,EAAMI,EAAQ,QACdH,EAAM,KAAK,CAAE,MAAAf,EAAO,QAASE,EAAK,QAAS,MAAAD,EAAO,SAAUiB,EAAQ,QAAS,CAAC,GAGhF,MAAO,CAAE,QAASJ,EAAK,MAAAC,CAAM,CAC/B","names":["renderValue","entity","from","parts","key","f","IDENTITY_HEADING","patchIdentityField","content","field","value","spec","IDENTITY_SLOTS","lines","bullet","start","l","end","i","m","insertAt","backfillArticle","missing","out","edits","BACKFILL_ORDER","BACKFILLABLE","patched"]}