@probelabs/visor 0.1.183-ee → 0.1.184-ee

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 (241) hide show
  1. package/dist/agent-protocol/task-live-update-slack.d.ts +24 -0
  2. package/dist/agent-protocol/task-live-update-slack.d.ts.map +1 -0
  3. package/dist/agent-protocol/task-live-update-teams.d.ts +25 -0
  4. package/dist/agent-protocol/task-live-update-teams.d.ts.map +1 -0
  5. package/dist/agent-protocol/task-live-update-telegram.d.ts +25 -0
  6. package/dist/agent-protocol/task-live-update-telegram.d.ts.map +1 -0
  7. package/dist/agent-protocol/task-live-updates.d.ts +102 -0
  8. package/dist/agent-protocol/task-live-updates.d.ts.map +1 -0
  9. package/dist/agent-protocol/task-progress-tool.d.ts.map +1 -1
  10. package/dist/agent-protocol/task-trace-resolution.d.ts +11 -0
  11. package/dist/agent-protocol/task-trace-resolution.d.ts.map +1 -0
  12. package/dist/agent-protocol/trace-serializer.d.ts.map +1 -1
  13. package/dist/agent-protocol/track-execution.d.ts +8 -0
  14. package/dist/agent-protocol/track-execution.d.ts.map +1 -1
  15. package/dist/cli-main.d.ts.map +1 -1
  16. package/dist/cli.d.ts.map +1 -1
  17. package/dist/config.d.ts.map +1 -1
  18. package/dist/frontends/slack-frontend.d.ts +2 -0
  19. package/dist/frontends/slack-frontend.d.ts.map +1 -1
  20. package/dist/frontends/teams-frontend.d.ts.map +1 -1
  21. package/dist/frontends/telegram-frontend.d.ts.map +1 -1
  22. package/dist/generated/config-schema.d.ts +110 -6
  23. package/dist/generated/config-schema.d.ts.map +1 -1
  24. package/dist/index.js +2239 -261
  25. package/dist/logger.d.ts +4 -1
  26. package/dist/logger.d.ts.map +1 -1
  27. package/dist/mcp-job-manager.d.ts +70 -0
  28. package/dist/mcp-job-manager.d.ts.map +1 -0
  29. package/dist/mcp-server.d.ts +6 -0
  30. package/dist/mcp-server.d.ts.map +1 -1
  31. package/dist/runners/mcp-server-runner.d.ts +4 -0
  32. package/dist/runners/mcp-server-runner.d.ts.map +1 -1
  33. package/dist/runners/runner-factory.d.ts.map +1 -1
  34. package/dist/sdk/{a2a-frontend-5YDHFQXD.mjs → a2a-frontend-OI4OVSKC.mjs} +4 -4
  35. package/dist/sdk/check-provider-registry-RITJW67U.mjs +32 -0
  36. package/dist/sdk/check-provider-registry-ZZ6N4GDP.mjs +32 -0
  37. package/dist/sdk/{chunk-4BN2XI4X.mjs → chunk-4HIWZA6M.mjs} +2 -2
  38. package/dist/sdk/{chunk-RI4ONH5X.mjs → chunk-4MHHELVZ.mjs} +2 -2
  39. package/dist/sdk/{chunk-J27D43HS.mjs → chunk-4ZLYHSN4.mjs} +2 -2
  40. package/dist/sdk/{chunk-ZPYODGYA.mjs → chunk-6E625R3C.mjs} +19 -4
  41. package/dist/sdk/chunk-6E625R3C.mjs.map +1 -0
  42. package/dist/sdk/{chunk-GA2TYKSR.mjs → chunk-7XKHFRPN.mjs} +4 -4
  43. package/dist/sdk/{chunk-6C3R6E42.mjs → chunk-IY5PQ5EN.mjs} +30 -6
  44. package/dist/sdk/chunk-IY5PQ5EN.mjs.map +1 -0
  45. package/dist/sdk/{chunk-MFXPJUUE.mjs → chunk-PUHU6UY6.mjs} +4 -3
  46. package/dist/sdk/chunk-PUHU6UY6.mjs.map +1 -0
  47. package/dist/sdk/{chunk-XOAEKFKB.mjs → chunk-QLT42TX7.mjs} +2 -2
  48. package/dist/sdk/chunk-SRU5TFNY.mjs +620 -0
  49. package/dist/sdk/chunk-SRU5TFNY.mjs.map +1 -0
  50. package/dist/sdk/{chunk-P2K4VOMU.mjs → chunk-TSX3YS3F.mjs} +3 -3
  51. package/dist/sdk/{chunk-WKLJ57WF.mjs → chunk-UM7LGO2P.mjs} +6 -6
  52. package/dist/sdk/{chunk-7VTZDC2X.mjs → chunk-VPPBOKBQ.mjs} +2 -2
  53. package/dist/sdk/chunk-WZQMTD7W.mjs +33 -0
  54. package/dist/sdk/chunk-WZQMTD7W.mjs.map +1 -0
  55. package/dist/sdk/{chunk-ZOF5QT6U.mjs → chunk-YBXNG75V.mjs} +118 -10
  56. package/dist/sdk/chunk-YBXNG75V.mjs.map +1 -0
  57. package/dist/sdk/{chunk-UXB4XWEE.mjs → chunk-YYFSAAD6.mjs} +53 -51
  58. package/dist/sdk/chunk-YYFSAAD6.mjs.map +1 -0
  59. package/dist/sdk/{chunk-RHKPFJLG.mjs → chunk-ZNKL6ESZ.mjs} +2 -2
  60. package/dist/sdk/{chunk-IDL3AA3G.mjs → chunk-ZYDRR6PZ.mjs} +1150 -403
  61. package/dist/sdk/chunk-ZYDRR6PZ.mjs.map +1 -0
  62. package/dist/sdk/{command-executor-YNJOS77A.mjs → command-executor-LHUW77GR.mjs} +3 -3
  63. package/dist/sdk/{config-PCP6O6Y6.mjs → config-TVU5RWR5.mjs} +3 -3
  64. package/dist/sdk/{dist-3UGGEZB3.mjs → dist-PN5UHL6A.mjs} +429 -429
  65. package/dist/sdk/dist-PN5UHL6A.mjs.map +1 -0
  66. package/dist/sdk/{email-frontend-WSNADJPI.mjs → email-frontend-ECHQCFTR.mjs} +2 -2
  67. package/dist/sdk/{failure-condition-evaluator-IRFKTYZD.mjs → failure-condition-evaluator-USY3ISVA.mjs} +6 -6
  68. package/dist/sdk/{github-auth-BJQBLK2V.mjs → github-auth-UO4DMNCC.mjs} +2 -2
  69. package/dist/sdk/{github-frontend-DECYOBRN.mjs → github-frontend-2T5PWYD5.mjs} +6 -6
  70. package/dist/sdk/{host-CFM2ASDI.mjs → host-OJSMCLKK.mjs} +6 -6
  71. package/dist/sdk/{host-T4LNVU2H.mjs → host-RM6UJEPP.mjs} +7 -7
  72. package/dist/sdk/{knex-store-OEWSZEBY.mjs → knex-store-KCMFAMH5.mjs} +2 -2
  73. package/dist/sdk/{liquid-extensions-E3AKRX7P.mjs → liquid-extensions-KZIRR4OY.mjs} +4 -4
  74. package/dist/sdk/{loader-WRGI244P.mjs → loader-35YWX5UQ.mjs} +4 -4
  75. package/dist/sdk/{memory-store-OHUIXCWJ.mjs → memory-store-3JONK7AF.mjs} +3 -3
  76. package/dist/sdk/{opa-policy-engine-IVMCGVNA.mjs → opa-policy-engine-FY5D45YS.mjs} +2 -2
  77. package/dist/sdk/{prompt-state-LN57DQF3.mjs → prompt-state-VVJMONT3.mjs} +3 -3
  78. package/dist/sdk/{renderer-schema-BT2IXMLW.mjs → renderer-schema-JZRRU5CW.mjs} +2 -2
  79. package/dist/sdk/{routing-H2PQ57OA.mjs → routing-NNQQSLWA.mjs} +8 -8
  80. package/dist/sdk/{schedule-tool-2DPNSU63.mjs → schedule-tool-ADZ2ON4I.mjs} +15 -14
  81. package/dist/sdk/schedule-tool-EMNF3FPQ.mjs +38 -0
  82. package/dist/sdk/{schedule-tool-handler-NBEO46RV.mjs → schedule-tool-handler-3R3IC6VA.mjs} +15 -14
  83. package/dist/sdk/{schedule-tool-handler-KLHE2SOW.mjs → schedule-tool-handler-F76ZD2WU.mjs} +17 -16
  84. package/dist/sdk/sdk.d.mts +40 -0
  85. package/dist/sdk/sdk.d.ts +40 -0
  86. package/dist/sdk/sdk.js +1154 -176
  87. package/dist/sdk/sdk.js.map +1 -1
  88. package/dist/sdk/sdk.mjs +14 -13
  89. package/dist/sdk/sdk.mjs.map +1 -1
  90. package/dist/sdk/{slack-frontend-DF5VL4OF.mjs → slack-frontend-K3TPGERM.mjs} +65 -9
  91. package/dist/sdk/slack-frontend-K3TPGERM.mjs.map +1 -0
  92. package/dist/sdk/{task-evaluator-OVMG7S56.mjs → task-evaluator-H3BXC7SE.mjs} +3 -3
  93. package/dist/sdk/{teams-frontend-DNW5GZP3.mjs → teams-frontend-6RRW533K.mjs} +54 -1
  94. package/dist/sdk/teams-frontend-6RRW533K.mjs.map +1 -0
  95. package/dist/sdk/{telegram-frontend-GA7OLADB.mjs → telegram-frontend-5UA77YAT.mjs} +48 -1
  96. package/dist/sdk/telegram-frontend-5UA77YAT.mjs.map +1 -0
  97. package/dist/sdk/{trace-helpers-26ZCAE2V.mjs → trace-helpers-OMF4C24T.mjs} +3 -3
  98. package/dist/sdk/{trace-serializer-KKBJHM7J.mjs → trace-serializer-EBHTHZYT.mjs} +3 -3
  99. package/dist/sdk/track-execution-3CHMGEPH.mjs +249 -0
  100. package/dist/sdk/track-execution-3CHMGEPH.mjs.map +1 -0
  101. package/dist/sdk/{utcp-check-provider-WI3QZ3W6.mjs → utcp-check-provider-Q4PD5EP2.mjs} +5 -5
  102. package/dist/sdk/workflow-check-provider-TC2G33WF.mjs +32 -0
  103. package/dist/sdk/workflow-check-provider-U4UXZ5YS.mjs +32 -0
  104. package/dist/sdk/{workflow-registry-YCZ3FCJC.mjs → workflow-registry-K56UTX36.mjs} +3 -3
  105. package/dist/slack/client.d.ts +14 -0
  106. package/dist/slack/client.d.ts.map +1 -1
  107. package/dist/slack/socket-runner.d.ts.map +1 -1
  108. package/dist/teams/client.d.ts +16 -0
  109. package/dist/teams/client.d.ts.map +1 -1
  110. package/dist/teams/webhook-runner.d.ts.map +1 -1
  111. package/dist/telegram/client.d.ts +17 -0
  112. package/dist/telegram/client.d.ts.map +1 -1
  113. package/dist/telegram/polling-runner.d.ts.map +1 -1
  114. package/dist/types/cli.d.ts +2 -0
  115. package/dist/types/cli.d.ts.map +1 -1
  116. package/dist/types/config.d.ts +40 -0
  117. package/dist/types/config.d.ts.map +1 -1
  118. package/package.json +4 -3
  119. package/dist/generated/config-schema.json +0 -4027
  120. package/dist/sdk/a2a-frontend-6LWBIPMS.mjs +0 -1734
  121. package/dist/sdk/a2a-frontend-6LWBIPMS.mjs.map +0 -1
  122. package/dist/sdk/check-provider-registry-WSEVHJEV.mjs +0 -31
  123. package/dist/sdk/check-provider-registry-YRADEEQY.mjs +0 -31
  124. package/dist/sdk/chunk-34QX63WK.mjs +0 -244
  125. package/dist/sdk/chunk-34QX63WK.mjs.map +0 -1
  126. package/dist/sdk/chunk-54KOAC4W.mjs +0 -665
  127. package/dist/sdk/chunk-6C3R6E42.mjs.map +0 -1
  128. package/dist/sdk/chunk-7W5QCO4Y.mjs +0 -5943
  129. package/dist/sdk/chunk-7W5QCO4Y.mjs.map +0 -1
  130. package/dist/sdk/chunk-7XRSCOKE.mjs +0 -825
  131. package/dist/sdk/chunk-FT3I25QV.mjs +0 -251
  132. package/dist/sdk/chunk-FT3I25QV.mjs.map +0 -1
  133. package/dist/sdk/chunk-G7GSN3SK.mjs +0 -390
  134. package/dist/sdk/chunk-G7GSN3SK.mjs.map +0 -1
  135. package/dist/sdk/chunk-IDL3AA3G.mjs.map +0 -1
  136. package/dist/sdk/chunk-J27D43HS.mjs.map +0 -1
  137. package/dist/sdk/chunk-MEB2TTIE.mjs +0 -157
  138. package/dist/sdk/chunk-MEB2TTIE.mjs.map +0 -1
  139. package/dist/sdk/chunk-MFXPJUUE.mjs.map +0 -1
  140. package/dist/sdk/chunk-NPSLGKXB.mjs +0 -1502
  141. package/dist/sdk/chunk-P2K4VOMU.mjs.map +0 -1
  142. package/dist/sdk/chunk-PQWZ6NFL.mjs +0 -459
  143. package/dist/sdk/chunk-PQWZ6NFL.mjs.map +0 -1
  144. package/dist/sdk/chunk-S5FSRHMY.mjs +0 -139
  145. package/dist/sdk/chunk-S5FSRHMY.mjs.map +0 -1
  146. package/dist/sdk/chunk-TFUQ2D5L.mjs +0 -307
  147. package/dist/sdk/chunk-TFUQ2D5L.mjs.map +0 -1
  148. package/dist/sdk/chunk-UCMJJ3IM.mjs +0 -227
  149. package/dist/sdk/chunk-UCMJJ3IM.mjs.map +0 -1
  150. package/dist/sdk/chunk-UFHOIB3R.mjs +0 -482
  151. package/dist/sdk/chunk-UFHOIB3R.mjs.map +0 -1
  152. package/dist/sdk/chunk-UXB4XWEE.mjs.map +0 -1
  153. package/dist/sdk/chunk-V45TITKX.mjs +0 -739
  154. package/dist/sdk/chunk-V45TITKX.mjs.map +0 -1
  155. package/dist/sdk/chunk-WKLJ57WF.mjs.map +0 -1
  156. package/dist/sdk/chunk-ZOF5QT6U.mjs.map +0 -1
  157. package/dist/sdk/chunk-ZPYODGYA.mjs.map +0 -1
  158. package/dist/sdk/command-executor-3AHGIYQG.mjs +0 -14
  159. package/dist/sdk/config-JE4HKTWW.mjs +0 -16
  160. package/dist/sdk/dist-3UGGEZB3.mjs.map +0 -1
  161. package/dist/sdk/failure-condition-evaluator-H3PBFBYT.mjs +0 -18
  162. package/dist/sdk/github-auth-27SZGPEC.mjs +0 -196
  163. package/dist/sdk/github-auth-BJQBLK2V.mjs.map +0 -1
  164. package/dist/sdk/github-frontend-TZRBOQCN.mjs +0 -1394
  165. package/dist/sdk/github-frontend-TZRBOQCN.mjs.map +0 -1
  166. package/dist/sdk/lazy-otel-5NH4ZJJM.mjs +0 -24
  167. package/dist/sdk/liquid-extensions-P6KDYILF.mjs +0 -25
  168. package/dist/sdk/memory-store-K5N7MC7U.mjs +0 -12
  169. package/dist/sdk/metrics-JTOG2HNO.mjs +0 -41
  170. package/dist/sdk/prompt-state-YPICX7PI.mjs +0 -16
  171. package/dist/sdk/renderer-schema-KOIH75RZ.mjs +0 -51
  172. package/dist/sdk/renderer-schema-KOIH75RZ.mjs.map +0 -1
  173. package/dist/sdk/routing-JMZ7HDCC.mjs +0 -26
  174. package/dist/sdk/schedule-tool-4M45RK3E.mjs +0 -37
  175. package/dist/sdk/schedule-tool-4M45RK3E.mjs.map +0 -1
  176. package/dist/sdk/schedule-tool-handler-KLHE2SOW.mjs.map +0 -1
  177. package/dist/sdk/schedule-tool-handler-NBEO46RV.mjs.map +0 -1
  178. package/dist/sdk/slack-frontend-BPWXNIHE.mjs +0 -929
  179. package/dist/sdk/slack-frontend-BPWXNIHE.mjs.map +0 -1
  180. package/dist/sdk/slack-frontend-DF5VL4OF.mjs.map +0 -1
  181. package/dist/sdk/task-evaluator-GQYDOSGT.mjs +0 -1392
  182. package/dist/sdk/task-evaluator-GQYDOSGT.mjs.map +0 -1
  183. package/dist/sdk/teams-frontend-DNW5GZP3.mjs.map +0 -1
  184. package/dist/sdk/telegram-frontend-GA7OLADB.mjs.map +0 -1
  185. package/dist/sdk/trace-helpers-26ZCAE2V.mjs.map +0 -1
  186. package/dist/sdk/trace-helpers-XV5GAX5L.mjs +0 -29
  187. package/dist/sdk/trace-helpers-XV5GAX5L.mjs.map +0 -1
  188. package/dist/sdk/trace-serializer-KKBJHM7J.mjs.map +0 -1
  189. package/dist/sdk/track-execution-3EC24C2X.mjs +0 -163
  190. package/dist/sdk/track-execution-3EC24C2X.mjs.map +0 -1
  191. package/dist/sdk/track-execution-66RLL6QT.mjs +0 -143
  192. package/dist/sdk/track-execution-66RLL6QT.mjs.map +0 -1
  193. package/dist/sdk/utcp-check-provider-JLIYF5HH.mjs +0 -16
  194. package/dist/sdk/utcp-check-provider-JLIYF5HH.mjs.map +0 -1
  195. package/dist/sdk/utcp-check-provider-WI3QZ3W6.mjs.map +0 -1
  196. package/dist/sdk/workflow-check-provider-X2UREEH7.mjs +0 -31
  197. package/dist/sdk/workflow-check-provider-X2UREEH7.mjs.map +0 -1
  198. package/dist/sdk/workflow-check-provider-YXALZNAQ.mjs +0 -31
  199. package/dist/sdk/workflow-check-provider-YXALZNAQ.mjs.map +0 -1
  200. package/dist/sdk/workflow-registry-X2IPY35M.mjs +0 -12
  201. package/dist/sdk/workflow-registry-X2IPY35M.mjs.map +0 -1
  202. package/dist/sdk/workflow-registry-YCZ3FCJC.mjs.map +0 -1
  203. /package/dist/sdk/{a2a-frontend-5YDHFQXD.mjs.map → a2a-frontend-OI4OVSKC.mjs.map} +0 -0
  204. /package/dist/sdk/{check-provider-registry-WSEVHJEV.mjs.map → check-provider-registry-RITJW67U.mjs.map} +0 -0
  205. /package/dist/sdk/{check-provider-registry-YRADEEQY.mjs.map → check-provider-registry-ZZ6N4GDP.mjs.map} +0 -0
  206. /package/dist/sdk/{chunk-4BN2XI4X.mjs.map → chunk-4HIWZA6M.mjs.map} +0 -0
  207. /package/dist/sdk/{chunk-RI4ONH5X.mjs.map → chunk-4MHHELVZ.mjs.map} +0 -0
  208. /package/dist/sdk/{chunk-54KOAC4W.mjs.map → chunk-4ZLYHSN4.mjs.map} +0 -0
  209. /package/dist/sdk/{chunk-GA2TYKSR.mjs.map → chunk-7XKHFRPN.mjs.map} +0 -0
  210. /package/dist/sdk/{chunk-XOAEKFKB.mjs.map → chunk-QLT42TX7.mjs.map} +0 -0
  211. /package/dist/sdk/{chunk-7XRSCOKE.mjs.map → chunk-TSX3YS3F.mjs.map} +0 -0
  212. /package/dist/sdk/{chunk-NPSLGKXB.mjs.map → chunk-UM7LGO2P.mjs.map} +0 -0
  213. /package/dist/sdk/{chunk-7VTZDC2X.mjs.map → chunk-VPPBOKBQ.mjs.map} +0 -0
  214. /package/dist/sdk/{chunk-RHKPFJLG.mjs.map → chunk-ZNKL6ESZ.mjs.map} +0 -0
  215. /package/dist/sdk/{command-executor-3AHGIYQG.mjs.map → command-executor-LHUW77GR.mjs.map} +0 -0
  216. /package/dist/sdk/{command-executor-YNJOS77A.mjs.map → config-TVU5RWR5.mjs.map} +0 -0
  217. /package/dist/sdk/{email-frontend-WSNADJPI.mjs.map → email-frontend-ECHQCFTR.mjs.map} +0 -0
  218. /package/dist/sdk/{config-JE4HKTWW.mjs.map → failure-condition-evaluator-USY3ISVA.mjs.map} +0 -0
  219. /package/dist/sdk/{github-auth-27SZGPEC.mjs.map → github-auth-UO4DMNCC.mjs.map} +0 -0
  220. /package/dist/sdk/{github-frontend-DECYOBRN.mjs.map → github-frontend-2T5PWYD5.mjs.map} +0 -0
  221. /package/dist/sdk/{host-CFM2ASDI.mjs.map → host-OJSMCLKK.mjs.map} +0 -0
  222. /package/dist/sdk/{host-T4LNVU2H.mjs.map → host-RM6UJEPP.mjs.map} +0 -0
  223. /package/dist/sdk/{knex-store-OEWSZEBY.mjs.map → knex-store-KCMFAMH5.mjs.map} +0 -0
  224. /package/dist/sdk/{config-PCP6O6Y6.mjs.map → liquid-extensions-KZIRR4OY.mjs.map} +0 -0
  225. /package/dist/sdk/{loader-WRGI244P.mjs.map → loader-35YWX5UQ.mjs.map} +0 -0
  226. /package/dist/sdk/{failure-condition-evaluator-H3PBFBYT.mjs.map → memory-store-3JONK7AF.mjs.map} +0 -0
  227. /package/dist/sdk/{opa-policy-engine-IVMCGVNA.mjs.map → opa-policy-engine-FY5D45YS.mjs.map} +0 -0
  228. /package/dist/sdk/{failure-condition-evaluator-IRFKTYZD.mjs.map → prompt-state-VVJMONT3.mjs.map} +0 -0
  229. /package/dist/sdk/{renderer-schema-BT2IXMLW.mjs.map → renderer-schema-JZRRU5CW.mjs.map} +0 -0
  230. /package/dist/sdk/{lazy-otel-5NH4ZJJM.mjs.map → routing-NNQQSLWA.mjs.map} +0 -0
  231. /package/dist/sdk/{liquid-extensions-E3AKRX7P.mjs.map → schedule-tool-ADZ2ON4I.mjs.map} +0 -0
  232. /package/dist/sdk/{liquid-extensions-P6KDYILF.mjs.map → schedule-tool-EMNF3FPQ.mjs.map} +0 -0
  233. /package/dist/sdk/{memory-store-K5N7MC7U.mjs.map → schedule-tool-handler-3R3IC6VA.mjs.map} +0 -0
  234. /package/dist/sdk/{memory-store-OHUIXCWJ.mjs.map → schedule-tool-handler-F76ZD2WU.mjs.map} +0 -0
  235. /package/dist/sdk/{task-evaluator-OVMG7S56.mjs.map → task-evaluator-H3BXC7SE.mjs.map} +0 -0
  236. /package/dist/sdk/{metrics-JTOG2HNO.mjs.map → trace-helpers-OMF4C24T.mjs.map} +0 -0
  237. /package/dist/sdk/{prompt-state-LN57DQF3.mjs.map → trace-serializer-EBHTHZYT.mjs.map} +0 -0
  238. /package/dist/sdk/{prompt-state-YPICX7PI.mjs.map → utcp-check-provider-Q4PD5EP2.mjs.map} +0 -0
  239. /package/dist/sdk/{routing-H2PQ57OA.mjs.map → workflow-check-provider-TC2G33WF.mjs.map} +0 -0
  240. /package/dist/sdk/{routing-JMZ7HDCC.mjs.map → workflow-check-provider-U4UXZ5YS.mjs.map} +0 -0
  241. /package/dist/sdk/{schedule-tool-2DPNSU63.mjs.map → workflow-registry-K56UTX36.mjs.map} +0 -0
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- process.env.VISOR_VERSION = '0.1.183';
3
- process.env.PROBE_VERSION = '0.6.0-rc308';
4
- process.env.VISOR_COMMIT_SHA = '0840de72a184fe6e122d343e1628c34c92b88922';
5
- process.env.VISOR_COMMIT_SHORT = '0840de7';
2
+ process.env.VISOR_VERSION = '0.1.184';
3
+ process.env.PROBE_VERSION = '0.6.0-rc311';
4
+ process.env.VISOR_COMMIT_SHA = 'fa523b7486d2550f30d84ebab4c46da43fafecaf';
5
+ process.env.VISOR_COMMIT_SHORT = 'fa523b7';
6
6
  /******/ (() => { // webpackBootstrap
7
7
  /******/ var __webpack_modules__ = ({
8
8
 
@@ -293448,6 +293448,868 @@ async function evaluateAndStore(taskId, store, config) {
293448
293448
  }
293449
293449
 
293450
293450
 
293451
+ /***/ }),
293452
+
293453
+ /***/ 90535:
293454
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
293455
+
293456
+ "use strict";
293457
+
293458
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
293459
+ exports.SlackTaskLiveUpdateSink = void 0;
293460
+ const markdown_1 = __nccwpck_require__(34907);
293461
+ const logger_1 = __nccwpck_require__(86999);
293462
+ class SlackTaskLiveUpdateSink {
293463
+ slack;
293464
+ channel;
293465
+ threadTs;
293466
+ kind = 'slack';
293467
+ messageTs;
293468
+ constructor(slack, channel, threadTs, initialMessageTs) {
293469
+ this.slack = slack;
293470
+ this.channel = channel;
293471
+ this.threadTs = threadTs;
293472
+ this.messageTs = initialMessageTs;
293473
+ }
293474
+ async start() {
293475
+ logger_1.logger.debug(`[TaskLiveUpdates][Slack] Initialized live update sink for channel=${this.channel} thread=${this.threadTs}`);
293476
+ return null;
293477
+ }
293478
+ async update(text) {
293479
+ return this.publish(text, 'progress');
293480
+ }
293481
+ async complete(text) {
293482
+ return this.publish(text, 'final');
293483
+ }
293484
+ async fail(text) {
293485
+ return this.publish(text, 'final');
293486
+ }
293487
+ async publish(text, mode) {
293488
+ if (this.messageTs) {
293489
+ logger_1.logger.debug(`[TaskLiveUpdates][Slack] Updating existing message ts=${this.messageTs} in channel=${this.channel}`);
293490
+ const updated = await this.slack.chat.update({
293491
+ channel: this.channel,
293492
+ ts: this.messageTs,
293493
+ text: (0, markdown_1.formatSlackText)(text),
293494
+ });
293495
+ if (updated?.ok)
293496
+ return null;
293497
+ logger_1.logger.warn(`[TaskLiveUpdates][Slack] chat.update failed for ts=${this.messageTs} error=${updated?.error || 'unknown_error'}; falling back to chat.postMessage`);
293498
+ }
293499
+ logger_1.logger.info(`[TaskLiveUpdates][Slack] Posting live update message in channel=${this.channel} thread=${this.threadTs}`);
293500
+ const posted = await this.slack.chat.postMessage({
293501
+ channel: this.channel,
293502
+ thread_ts: this.threadTs,
293503
+ text: (0, markdown_1.formatSlackText)(text),
293504
+ });
293505
+ if (posted?.ok && posted.ts) {
293506
+ const previousTs = this.messageTs;
293507
+ this.messageTs = posted.ts;
293508
+ if (mode === 'final' && previousTs && previousTs !== posted.ts) {
293509
+ const deleted = await this.slack.chat.delete({
293510
+ channel: this.channel,
293511
+ ts: previousTs,
293512
+ });
293513
+ if (!deleted?.ok) {
293514
+ logger_1.logger.warn(`[TaskLiveUpdates][Slack] chat.delete failed for stale live update ts=${previousTs} error=${deleted?.error || 'unknown_error'}`);
293515
+ }
293516
+ else {
293517
+ logger_1.logger.info(`[TaskLiveUpdates][Slack] Removed stale live update message ts=${previousTs} after final fallback post`);
293518
+ }
293519
+ }
293520
+ return {
293521
+ ref: {
293522
+ slack_live_update_channel: this.channel,
293523
+ slack_live_update_ts: posted.ts,
293524
+ slack_live_update_thread_ts: this.threadTs,
293525
+ },
293526
+ };
293527
+ }
293528
+ return null;
293529
+ }
293530
+ }
293531
+ exports.SlackTaskLiveUpdateSink = SlackTaskLiveUpdateSink;
293532
+
293533
+
293534
+ /***/ }),
293535
+
293536
+ /***/ 82309:
293537
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
293538
+
293539
+ "use strict";
293540
+
293541
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
293542
+ exports.TeamsTaskLiveUpdateSink = void 0;
293543
+ const markdown_1 = __nccwpck_require__(37349);
293544
+ const logger_1 = __nccwpck_require__(86999);
293545
+ class TeamsTaskLiveUpdateSink {
293546
+ teams;
293547
+ conversationReference;
293548
+ replyToActivityId;
293549
+ kind = 'teams';
293550
+ activityId;
293551
+ constructor(teams, conversationReference, replyToActivityId, initialActivityId) {
293552
+ this.teams = teams;
293553
+ this.conversationReference = conversationReference;
293554
+ this.replyToActivityId = replyToActivityId;
293555
+ this.activityId = initialActivityId;
293556
+ }
293557
+ async start() {
293558
+ logger_1.logger.debug('[TaskLiveUpdates][Teams] Initialized live update sink');
293559
+ return null;
293560
+ }
293561
+ async update(text) {
293562
+ return this.publish(text, 'progress');
293563
+ }
293564
+ async complete(text) {
293565
+ return this.publish(text, 'final');
293566
+ }
293567
+ async fail(text) {
293568
+ return this.publish(text, 'final');
293569
+ }
293570
+ async publish(text, mode) {
293571
+ const formatted = (0, markdown_1.formatTeamsText)(text);
293572
+ if (this.activityId) {
293573
+ logger_1.logger.debug(`[TaskLiveUpdates][Teams] Updating existing activityId=${this.activityId}`);
293574
+ const updated = await this.teams.updateMessage({
293575
+ conversationReference: this.conversationReference,
293576
+ activityId: this.activityId,
293577
+ text: formatted,
293578
+ });
293579
+ if (updated?.ok)
293580
+ return null;
293581
+ logger_1.logger.warn(`[TaskLiveUpdates][Teams] updateMessage failed for activityId=${this.activityId} error=${updated?.error || 'unknown_error'}; falling back to sendMessage`);
293582
+ }
293583
+ logger_1.logger.info('[TaskLiveUpdates][Teams] Posting live update message');
293584
+ const posted = await this.teams.sendMessage({
293585
+ conversationReference: this.conversationReference,
293586
+ text: formatted,
293587
+ ...(this.replyToActivityId ? { replyToActivityId: this.replyToActivityId } : {}),
293588
+ });
293589
+ if (posted?.ok && posted.activityId) {
293590
+ const previousActivityId = this.activityId;
293591
+ this.activityId = posted.activityId;
293592
+ if (mode === 'final' && previousActivityId && previousActivityId !== posted.activityId) {
293593
+ const deleted = await this.teams.deleteMessage({
293594
+ conversationReference: this.conversationReference,
293595
+ activityId: previousActivityId,
293596
+ });
293597
+ if (!deleted) {
293598
+ logger_1.logger.warn(`[TaskLiveUpdates][Teams] deleteMessage failed for stale live update activityId=${previousActivityId}`);
293599
+ }
293600
+ else {
293601
+ logger_1.logger.info(`[TaskLiveUpdates][Teams] Removed stale live update activityId=${previousActivityId} after final fallback post`);
293602
+ }
293603
+ }
293604
+ return {
293605
+ ref: {
293606
+ teams_live_update_activity_id: posted.activityId,
293607
+ },
293608
+ };
293609
+ }
293610
+ return null;
293611
+ }
293612
+ }
293613
+ exports.TeamsTaskLiveUpdateSink = TeamsTaskLiveUpdateSink;
293614
+
293615
+
293616
+ /***/ }),
293617
+
293618
+ /***/ 71872:
293619
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
293620
+
293621
+ "use strict";
293622
+
293623
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
293624
+ exports.TelegramTaskLiveUpdateSink = void 0;
293625
+ const markdown_1 = __nccwpck_require__(21506);
293626
+ const logger_1 = __nccwpck_require__(86999);
293627
+ class TelegramTaskLiveUpdateSink {
293628
+ telegram;
293629
+ chatId;
293630
+ replyToMessageId;
293631
+ messageThreadId;
293632
+ kind = 'telegram';
293633
+ messageId;
293634
+ constructor(telegram, chatId, replyToMessageId, messageThreadId, initialMessageId) {
293635
+ this.telegram = telegram;
293636
+ this.chatId = chatId;
293637
+ this.replyToMessageId = replyToMessageId;
293638
+ this.messageThreadId = messageThreadId;
293639
+ this.messageId = initialMessageId;
293640
+ }
293641
+ async start() {
293642
+ logger_1.logger.debug(`[TaskLiveUpdates][Telegram] Initialized live update sink for chat=${this.chatId} reply_to=${this.replyToMessageId}`);
293643
+ return null;
293644
+ }
293645
+ async update(text) {
293646
+ return this.publish(text, 'progress');
293647
+ }
293648
+ async complete(text) {
293649
+ return this.publish(text, 'final');
293650
+ }
293651
+ async fail(text) {
293652
+ return this.publish(text, 'final');
293653
+ }
293654
+ async publish(text, mode) {
293655
+ const formatted = (0, markdown_1.formatTelegramText)(text);
293656
+ if (this.messageId) {
293657
+ logger_1.logger.debug(`[TaskLiveUpdates][Telegram] Updating existing message id=${this.messageId} in chat=${this.chatId}`);
293658
+ const updated = await this.telegram.editMessageText({
293659
+ chat_id: this.chatId,
293660
+ message_id: this.messageId,
293661
+ text: formatted,
293662
+ parse_mode: 'HTML',
293663
+ });
293664
+ if (updated?.ok)
293665
+ return null;
293666
+ logger_1.logger.warn(`[TaskLiveUpdates][Telegram] editMessageText failed for message_id=${this.messageId} error=${updated?.error || 'unknown_error'}; falling back to sendMessage`);
293667
+ }
293668
+ logger_1.logger.info(`[TaskLiveUpdates][Telegram] Posting live update message in chat=${this.chatId} reply_to=${this.replyToMessageId}`);
293669
+ const posted = await this.telegram.sendMessage({
293670
+ chat_id: this.chatId,
293671
+ text: formatted,
293672
+ parse_mode: 'HTML',
293673
+ reply_to_message_id: this.replyToMessageId,
293674
+ ...(this.messageThreadId ? { message_thread_id: this.messageThreadId } : {}),
293675
+ });
293676
+ if (posted?.ok && posted.message_id) {
293677
+ const previousMessageId = this.messageId;
293678
+ this.messageId = posted.message_id;
293679
+ if (mode === 'final' && previousMessageId && previousMessageId !== posted.message_id) {
293680
+ const deleted = await this.telegram.deleteMessage({
293681
+ chat_id: this.chatId,
293682
+ message_id: previousMessageId,
293683
+ });
293684
+ if (!deleted) {
293685
+ logger_1.logger.warn(`[TaskLiveUpdates][Telegram] deleteMessage failed for stale live update message_id=${previousMessageId}`);
293686
+ }
293687
+ else {
293688
+ logger_1.logger.info(`[TaskLiveUpdates][Telegram] Removed stale live update message id=${previousMessageId} after final fallback post`);
293689
+ }
293690
+ }
293691
+ return {
293692
+ ref: {
293693
+ telegram_live_update_chat_id: String(this.chatId),
293694
+ telegram_live_update_message_id: posted.message_id,
293695
+ ...(this.messageThreadId ? { telegram_live_update_thread_id: this.messageThreadId } : {}),
293696
+ },
293697
+ };
293698
+ }
293699
+ return null;
293700
+ }
293701
+ }
293702
+ exports.TelegramTaskLiveUpdateSink = TelegramTaskLiveUpdateSink;
293703
+
293704
+
293705
+ /***/ }),
293706
+
293707
+ /***/ 5893:
293708
+ /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
293709
+
293710
+ "use strict";
293711
+
293712
+ var __importDefault = (this && this.__importDefault) || function (mod) {
293713
+ return (mod && mod.__esModule) ? mod : { "default": mod };
293714
+ };
293715
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
293716
+ exports.TaskLiveUpdateManager = exports.DEFAULT_TASK_LIVE_UPDATE_PROMPT = exports.DEFAULT_TASK_LIVE_UPDATE_MODEL = exports.DEFAULT_TASK_LIVE_UPDATE_STALL_FALLBACK_SECONDS = exports.DEFAULT_TASK_LIVE_UPDATE_METADATA_REFRESH_SECONDS = exports.DEFAULT_TASK_LIVE_UPDATE_FIRST_UPDATE_DELAY_SECONDS = exports.DEFAULT_TASK_LIVE_UPDATE_MAX_TRACE_CHARS = exports.DEFAULT_TASK_LIVE_UPDATE_INTERVAL_SECONDS = void 0;
293717
+ exports.resolveTaskLiveUpdatesConfig = resolveTaskLiveUpdatesConfig;
293718
+ exports.isFrontendLiveUpdatesEnabled = isFrontendLiveUpdatesEnabled;
293719
+ exports.summarizeTaskProgress = summarizeTaskProgress;
293720
+ const crypto_1 = __importDefault(__nccwpck_require__(76982));
293721
+ const logger_1 = __nccwpck_require__(86999);
293722
+ const trace_serializer_1 = __nccwpck_require__(48666);
293723
+ exports.DEFAULT_TASK_LIVE_UPDATE_INTERVAL_SECONDS = 10;
293724
+ exports.DEFAULT_TASK_LIVE_UPDATE_MAX_TRACE_CHARS = 12_000;
293725
+ exports.DEFAULT_TASK_LIVE_UPDATE_FIRST_UPDATE_DELAY_SECONDS = 10;
293726
+ exports.DEFAULT_TASK_LIVE_UPDATE_METADATA_REFRESH_SECONDS = 5;
293727
+ exports.DEFAULT_TASK_LIVE_UPDATE_STALL_FALLBACK_SECONDS = 60;
293728
+ exports.DEFAULT_TASK_LIVE_UPDATE_MODEL = 'gemini-3.1-flash-lite-preview';
293729
+ const DEFAULT_TASK_LIVE_UPDATE_STALL_NOTICE = '_No new meaningful progress is visible yet. Some steps can stay quiet for up to 5 minutes before there is new news._';
293730
+ exports.DEFAULT_TASK_LIVE_UPDATE_PROMPT = `You are generating a short live progress update for a user while an AI task is still running.
293731
+
293732
+ This is NOT the final answer.
293733
+ Do NOT answer the user's original request.
293734
+ Do NOT write the final solution.
293735
+ Do NOT explain the topic in full.
293736
+ Do NOT provide a root cause, recommendation, summary of findings, or conclusion.
293737
+ Even if you think you already know the answer, do NOT give it here.
293738
+
293739
+ You will receive:
293740
+ - the user's original request
293741
+ - the previous progress update, if any
293742
+ - timing metadata for this run
293743
+ - the latest execution trace snapshot
293744
+
293745
+ Your job is to produce a concise status update that tells the user only:
293746
+ - the overall progress so far
293747
+ - the last meaningful action that finished
293748
+ - what the agent is doing right now
293749
+ - what it is likely waiting on, if anything
293750
+
293751
+ Rules:
293752
+ - This is a STATUS UPDATE, not the final answer
293753
+ - Never answer the user's request directly
293754
+ - Never switch into explanation mode
293755
+ - Never write a complete answer, even partially
293756
+ - Keep it short: exactly 4 short bullet points
293757
+ - Use the exact bullet labels below
293758
+ - Do NOT generate timing metadata lines
293759
+ - Do NOT generate task_id lines
293760
+ - Timing metadata is provided only so you understand task pace and recency
293761
+ - The system will append timing and task metadata separately
293762
+ - Prefer concrete progress over generic wording
293763
+ - Mention the most recent completed action before the current action
293764
+ - Do not claim completion unless the task is actually done
293765
+ - Do not mention internal implementation details unless they help explain the current work
293766
+ - Avoid repeating the previous update verbatim
293767
+ - Do not use code fences
293768
+ - Plain markdown text only
293769
+
293770
+ Required output format:
293771
+ - Progress: <very short overall progress statement, not a final answer>
293772
+ - Last done: <most recent completed action>
293773
+ - Now: <current action in progress>
293774
+ - Waiting on: <tool, search, model, user input, or "nothing blocking right now">
293775
+
293776
+ Trace interpretation rules:
293777
+ - Translate internal trace phases into user-meaningful progress, do not just repeat raw span names
293778
+ - If the trace shows routing or classify work, describe that as understanding the request and choosing the right path
293779
+ - If the trace shows setup-projects, build-config, or loading context, describe that as preparing the workspace or gathering context
293780
+ - If the trace shows search, extract, code-explorer, or file inspection, describe that as investigating the codebase
293781
+ - If the trace shows engineer work, edits, tests, PR creation, or command execution, describe that as implementing or verifying changes
293782
+ - If the trace shows completion prompts, final answer generation, summarization, final validation, or output rendering, describe that as validating findings and preparing the final response
293783
+ - If the trace shows waiting on a long AI request, delegate, tool call, or sandboxed child task, describe that as waiting for analysis or validation to finish
293784
+
293785
+ If you are tempted to answer the user's question, stop and convert that into:
293786
+ - what was learned so far
293787
+ - what was just completed
293788
+ - what is still being checked
293789
+
293790
+ Bad update example:
293791
+ - "API rate limiting works by..."
293792
+
293793
+ Good update example:
293794
+ - "Progress: identified the gateway components involved in rate limiting"
293795
+ - "Last done: found the middleware files and session manager entry points"
293796
+ - "Now: tracing the enforcement path through the gateway"
293797
+ - "Waiting on: search results for the limiter implementation details"`;
293798
+ function resolveTaskLiveUpdatesConfig(config) {
293799
+ if (!config)
293800
+ return null;
293801
+ if (config === true) {
293802
+ return {
293803
+ enabled: true,
293804
+ intervalSeconds: exports.DEFAULT_TASK_LIVE_UPDATE_INTERVAL_SECONDS,
293805
+ model: exports.DEFAULT_TASK_LIVE_UPDATE_MODEL,
293806
+ prompt: exports.DEFAULT_TASK_LIVE_UPDATE_PROMPT,
293807
+ initialMessage: '',
293808
+ maxTraceChars: exports.DEFAULT_TASK_LIVE_UPDATE_MAX_TRACE_CHARS,
293809
+ };
293810
+ }
293811
+ if (config.enabled === false)
293812
+ return null;
293813
+ return {
293814
+ enabled: true,
293815
+ intervalSeconds: Math.max(1, Math.floor(config.interval_seconds || exports.DEFAULT_TASK_LIVE_UPDATE_INTERVAL_SECONDS)),
293816
+ model: config.model || exports.DEFAULT_TASK_LIVE_UPDATE_MODEL,
293817
+ provider: config.provider,
293818
+ prompt: config.prompt || exports.DEFAULT_TASK_LIVE_UPDATE_PROMPT,
293819
+ initialMessage: config.initial_message || '',
293820
+ maxTraceChars: Math.max(1000, Math.floor(config.max_trace_chars || exports.DEFAULT_TASK_LIVE_UPDATE_MAX_TRACE_CHARS)),
293821
+ };
293822
+ }
293823
+ function isFrontendLiveUpdatesEnabled(config, frontend) {
293824
+ if (!config)
293825
+ return false;
293826
+ if (config === true)
293827
+ return true;
293828
+ if (config.enabled === false)
293829
+ return false;
293830
+ const frontendCfg = config.frontends?.[frontend];
293831
+ if (frontendCfg?.enabled === false)
293832
+ return false;
293833
+ return true;
293834
+ }
293835
+ class TaskLiveUpdateManager {
293836
+ ctx;
293837
+ deps;
293838
+ timer;
293839
+ firstTickTimer;
293840
+ metadataRefreshTimer;
293841
+ running = false;
293842
+ started = false;
293843
+ completed = false;
293844
+ startedAt = new Date();
293845
+ lastUpdateText;
293846
+ lastUpdateAt;
293847
+ lastTraceSnapshot;
293848
+ lastPostedMessage;
293849
+ lastStallFallbackAt;
293850
+ lastUpdateKind = 'semantic';
293851
+ lastSkillMetadata;
293852
+ constructor(ctx, deps) {
293853
+ this.ctx = ctx;
293854
+ this.deps = {
293855
+ summarizeProgress: deps?.summarizeProgress || summarizeTaskProgress,
293856
+ serializeTrace: deps?.serializeTrace || defaultSerializeTrace,
293857
+ extractSkillMetadata: deps?.extractSkillMetadata || extractTraceSkillMetadata,
293858
+ };
293859
+ }
293860
+ async start() {
293861
+ if (this.started)
293862
+ return;
293863
+ this.started = true;
293864
+ try {
293865
+ const started = await this.ctx.sink.start();
293866
+ this.recordSinkRef(started);
293867
+ logger_1.logger.info(`[TaskLiveUpdates] Started for task ${this.ctx.taskId}; first update in ${exports.DEFAULT_TASK_LIVE_UPDATE_FIRST_UPDATE_DELAY_SECONDS}s, interval=${this.ctx.config.intervalSeconds}s, provider=${this.ctx.config.provider || 'default'}, model=${this.ctx.config.model}`);
293868
+ }
293869
+ catch (err) {
293870
+ logger_1.logger.warn(`[TaskLiveUpdates] Failed to initialize live updates for task ${this.ctx.taskId}: ${err instanceof Error ? err.message : String(err)}`);
293871
+ }
293872
+ this.firstTickTimer = setTimeout(() => {
293873
+ void this.runFirstTick();
293874
+ }, exports.DEFAULT_TASK_LIVE_UPDATE_FIRST_UPDATE_DELAY_SECONDS * 1000);
293875
+ if (typeof this.firstTickTimer?.unref === 'function') {
293876
+ this.firstTickTimer.unref();
293877
+ }
293878
+ }
293879
+ async complete(finalText) {
293880
+ if (this.completed)
293881
+ return;
293882
+ this.completed = true;
293883
+ this.stop();
293884
+ try {
293885
+ logger_1.logger.info(`[TaskLiveUpdates] Publishing final success update for task ${this.ctx.taskId}`);
293886
+ const result = await this.ctx.sink.complete(this.decorateText(finalText));
293887
+ this.recordSinkRef(result);
293888
+ this.ctx.appendHistory?.(finalText, 'completed');
293889
+ }
293890
+ catch (err) {
293891
+ logger_1.logger.warn(`[TaskLiveUpdates] Failed to publish final update for task ${this.ctx.taskId}: ${err instanceof Error ? err.message : String(err)}`);
293892
+ }
293893
+ }
293894
+ async fail(finalText) {
293895
+ if (this.completed)
293896
+ return;
293897
+ this.completed = true;
293898
+ this.stop();
293899
+ try {
293900
+ logger_1.logger.info(`[TaskLiveUpdates] Publishing final failure update for task ${this.ctx.taskId}`);
293901
+ const result = await this.ctx.sink.fail(this.decorateText(finalText));
293902
+ this.recordSinkRef(result);
293903
+ this.ctx.appendHistory?.(finalText, 'failed');
293904
+ }
293905
+ catch (err) {
293906
+ logger_1.logger.warn(`[TaskLiveUpdates] Failed to publish failure update for task ${this.ctx.taskId}: ${err instanceof Error ? err.message : String(err)}`);
293907
+ }
293908
+ }
293909
+ stop() {
293910
+ if (this.firstTickTimer) {
293911
+ clearTimeout(this.firstTickTimer);
293912
+ this.firstTickTimer = undefined;
293913
+ }
293914
+ if (this.timer) {
293915
+ clearInterval(this.timer);
293916
+ this.timer = undefined;
293917
+ }
293918
+ if (this.metadataRefreshTimer) {
293919
+ clearInterval(this.metadataRefreshTimer);
293920
+ this.metadataRefreshTimer = undefined;
293921
+ }
293922
+ }
293923
+ async tick() {
293924
+ if (this.completed || this.running)
293925
+ return;
293926
+ const traceState = this.getTraceState();
293927
+ if (!traceState.traceRef && !traceState.traceId) {
293928
+ logger_1.logger.debug(`[TaskLiveUpdates] Skipping tick for task ${this.ctx.taskId}: no trace reference available yet`);
293929
+ return;
293930
+ }
293931
+ this.running = true;
293932
+ try {
293933
+ const traceRef = traceState.traceRef || traceState.traceId;
293934
+ const traceSnapshot = await this.deps.serializeTrace(traceRef, this.ctx.config.maxTraceChars, traceState.traceId);
293935
+ if (this.completed) {
293936
+ logger_1.logger.debug(`[TaskLiveUpdates] Aborting in-flight tick for task ${this.ctx.taskId}: task already completed`);
293937
+ return;
293938
+ }
293939
+ if (!traceSnapshot || traceSnapshot === '(no trace data available)') {
293940
+ logger_1.logger.debug(`[TaskLiveUpdates] Skipping tick for task ${this.ctx.taskId}: no trace data available yet (traceRef=${traceRef})`);
293941
+ return;
293942
+ }
293943
+ if (traceSnapshot === this.lastTraceSnapshot) {
293944
+ await this.maybePublishStallFallback(traceSnapshot, traceState.traceId);
293945
+ logger_1.logger.debug(`[TaskLiveUpdates] Skipping tick for task ${this.ctx.taskId}: trace snapshot unchanged`);
293946
+ return;
293947
+ }
293948
+ const summary = await this.deps.summarizeProgress({
293949
+ requestText: this.ctx.requestText,
293950
+ previousUpdate: this.lastUpdateText,
293951
+ traceSnapshot,
293952
+ config: this.ctx.config,
293953
+ startedAt: this.startedAt,
293954
+ now: new Date(),
293955
+ elapsedSeconds: Math.max(0, Math.floor((Date.now() - this.startedAt.getTime()) / 1000)),
293956
+ previousUpdateAt: this.lastUpdateAt,
293957
+ secondsSincePreviousUpdate: this.lastUpdateAt
293958
+ ? Math.max(0, Math.floor((Date.now() - this.lastUpdateAt.getTime()) / 1000))
293959
+ : undefined,
293960
+ });
293961
+ if (this.completed) {
293962
+ logger_1.logger.debug(`[TaskLiveUpdates] Aborting in-flight tick for task ${this.ctx.taskId}: task already completed after summarization`);
293963
+ return;
293964
+ }
293965
+ const cleaned = summary?.trim();
293966
+ if (!cleaned || cleaned === this.lastUpdateText) {
293967
+ await this.maybePublishStallFallback(traceSnapshot, traceState.traceId);
293968
+ logger_1.logger.debug(`[TaskLiveUpdates] Skipping tick for task ${this.ctx.taskId}: summary empty or unchanged`);
293969
+ this.lastTraceSnapshot = traceSnapshot;
293970
+ return;
293971
+ }
293972
+ logger_1.logger.info(`[TaskLiveUpdates] Publishing progress update for task ${this.ctx.taskId}: ${cleaned.slice(0, 160)}`);
293973
+ this.lastSkillMetadata = await this.deps.extractSkillMetadata(traceRef, traceState.traceId);
293974
+ this.lastUpdateKind = 'semantic';
293975
+ const message = this.decorateProgressText(cleaned, {
293976
+ elapsedSeconds: Math.max(0, Math.floor((Date.now() - this.startedAt.getTime()) / 1000)),
293977
+ previousUpdateAt: this.lastUpdateAt,
293978
+ secondsSincePreviousUpdate: this.lastUpdateAt
293979
+ ? Math.max(0, Math.floor((Date.now() - this.lastUpdateAt.getTime()) / 1000))
293980
+ : undefined,
293981
+ activatedSkills: this.lastSkillMetadata?.activatedSkills,
293982
+ }, traceState.traceId);
293983
+ const result = await this.ctx.sink.update(message);
293984
+ this.recordSinkRef(result);
293985
+ this.ctx.appendHistory?.(cleaned, 'progress');
293986
+ this.lastUpdateText = cleaned;
293987
+ this.lastUpdateAt = new Date();
293988
+ this.lastTraceSnapshot = traceSnapshot;
293989
+ this.lastPostedMessage = message;
293990
+ this.lastStallFallbackAt = undefined;
293991
+ }
293992
+ catch (err) {
293993
+ logger_1.logger.warn(`[TaskLiveUpdates] Progress update failed for task ${this.ctx.taskId}: ${err instanceof Error ? err.message : String(err)}`);
293994
+ }
293995
+ finally {
293996
+ this.running = false;
293997
+ }
293998
+ }
293999
+ async runFirstTick() {
294000
+ if (this.completed)
294001
+ return;
294002
+ logger_1.logger.debug(`[TaskLiveUpdates] Running first scheduled tick for task ${this.ctx.taskId}`);
294003
+ await this.tick();
294004
+ if (this.completed)
294005
+ return;
294006
+ this.timer = setInterval(() => {
294007
+ void this.tick();
294008
+ }, this.ctx.config.intervalSeconds * 1000);
294009
+ if (typeof this.timer?.unref === 'function') {
294010
+ this.timer.unref();
294011
+ }
294012
+ this.metadataRefreshTimer = setInterval(() => {
294013
+ void this.refreshProgressMetadata();
294014
+ }, exports.DEFAULT_TASK_LIVE_UPDATE_METADATA_REFRESH_SECONDS * 1000);
294015
+ if (typeof this.metadataRefreshTimer?.unref === 'function') {
294016
+ this.metadataRefreshTimer.unref();
294017
+ }
294018
+ }
294019
+ recordSinkRef(result) {
294020
+ if (result?.ref)
294021
+ this.ctx.onPostedRef?.(result.ref);
294022
+ }
294023
+ getTraceState() {
294024
+ const resolved = this.ctx.resolveTraceState?.();
294025
+ return {
294026
+ traceRef: resolved?.traceRef || this.ctx.traceRef,
294027
+ traceId: resolved?.traceId || this.ctx.traceId,
294028
+ };
294029
+ }
294030
+ decorateText(text, _traceId) {
294031
+ if (!this.ctx.includeTraceId)
294032
+ return text;
294033
+ if (text.includes(`task_id: ${this.ctx.taskId}`))
294034
+ return text;
294035
+ return `${text}\n\n\`task_id: ${this.ctx.taskId}\``;
294036
+ }
294037
+ decorateProgressText(text, timing, traceId) {
294038
+ const normalized = normalizeProgressSummary(text);
294039
+ const blocks = [
294040
+ '*Live Update*',
294041
+ '_Current task is still running. This message updates in place until the final answer is ready._',
294042
+ this.lastUpdateKind === 'stall' ? DEFAULT_TASK_LIVE_UPDATE_STALL_NOTICE : '',
294043
+ normalized,
294044
+ formatProgressMetadata(timing),
294045
+ ].filter(Boolean);
294046
+ return this.decorateText(blocks.join('\n\n'), traceId);
294047
+ }
294048
+ async refreshProgressMetadata() {
294049
+ if (this.completed || this.running || !this.lastUpdateText)
294050
+ return;
294051
+ const traceState = this.getTraceState();
294052
+ const message = this.decorateProgressText(this.lastUpdateText, {
294053
+ elapsedSeconds: Math.max(0, Math.floor((Date.now() - this.startedAt.getTime()) / 1000)),
294054
+ previousUpdateAt: this.lastUpdateAt,
294055
+ secondsSincePreviousUpdate: this.lastUpdateAt
294056
+ ? Math.max(0, Math.floor((Date.now() - this.lastUpdateAt.getTime()) / 1000))
294057
+ : undefined,
294058
+ activatedSkills: this.lastSkillMetadata?.activatedSkills,
294059
+ }, traceState.traceId);
294060
+ if (!message || message === this.lastPostedMessage)
294061
+ return;
294062
+ if (this.completed)
294063
+ return;
294064
+ try {
294065
+ logger_1.logger.debug(`[TaskLiveUpdates] Refreshing metadata-only live update for task ${this.ctx.taskId}`);
294066
+ const result = await this.ctx.sink.update(message);
294067
+ this.recordSinkRef(result);
294068
+ this.lastPostedMessage = message;
294069
+ }
294070
+ catch (err) {
294071
+ logger_1.logger.warn(`[TaskLiveUpdates] Metadata refresh failed for task ${this.ctx.taskId}: ${err instanceof Error ? err.message : String(err)}`);
294072
+ }
294073
+ }
294074
+ async maybePublishStallFallback(traceSnapshot, traceId) {
294075
+ if (this.completed)
294076
+ return;
294077
+ const now = new Date();
294078
+ if (this.lastStallFallbackAt &&
294079
+ now.getTime() - this.lastStallFallbackAt.getTime() <
294080
+ exports.DEFAULT_TASK_LIVE_UPDATE_STALL_FALLBACK_SECONDS * 1000) {
294081
+ return;
294082
+ }
294083
+ if (this.lastUpdateAt &&
294084
+ now.getTime() - this.lastUpdateAt.getTime() <
294085
+ exports.DEFAULT_TASK_LIVE_UPDATE_STALL_FALLBACK_SECONDS * 1000) {
294086
+ return;
294087
+ }
294088
+ this.lastSkillMetadata = await this.deps.extractSkillMetadata(this.getTraceState().traceRef || traceId || '', traceId);
294089
+ const fallback = buildStallFallbackSummary(traceSnapshot, this.lastUpdateText);
294090
+ const baseText = this.lastUpdateText || fallback;
294091
+ if (!baseText)
294092
+ return;
294093
+ logger_1.logger.info(`[TaskLiveUpdates] Publishing stall notice for task ${this.ctx.taskId}: ${baseText.slice(0, 160)}`);
294094
+ this.lastUpdateKind = 'stall';
294095
+ if (this.completed)
294096
+ return;
294097
+ const message = this.decorateProgressText(baseText, {
294098
+ elapsedSeconds: Math.max(0, Math.floor((Date.now() - this.startedAt.getTime()) / 1000)),
294099
+ previousUpdateAt: this.lastUpdateAt,
294100
+ secondsSincePreviousUpdate: this.lastUpdateAt
294101
+ ? Math.max(0, Math.floor((Date.now() - this.lastUpdateAt.getTime()) / 1000))
294102
+ : undefined,
294103
+ activatedSkills: this.lastSkillMetadata?.activatedSkills,
294104
+ }, traceId);
294105
+ if (message === this.lastPostedMessage) {
294106
+ this.lastStallFallbackAt = now;
294107
+ return;
294108
+ }
294109
+ const result = await this.ctx.sink.update(message);
294110
+ this.recordSinkRef(result);
294111
+ if (!this.lastUpdateText) {
294112
+ this.ctx.appendHistory?.(baseText, 'progress');
294113
+ this.lastUpdateText = baseText;
294114
+ this.lastUpdateAt = now;
294115
+ }
294116
+ this.lastPostedMessage = message;
294117
+ this.lastStallFallbackAt = now;
294118
+ }
294119
+ }
294120
+ exports.TaskLiveUpdateManager = TaskLiveUpdateManager;
294121
+ async function defaultSerializeTrace(traceRef, maxChars, traceId) {
294122
+ logger_1.logger.debug(`[TaskLiveUpdates] Serializing trace for progress update (traceRef=${traceRef}, traceId=${traceId || '-'}, sink=${process.env.VISOR_TELEMETRY_SINK || 'auto'})`);
294123
+ return (0, trace_serializer_1.serializeTraceForPrompt)(traceRef, maxChars, undefined, undefined, traceId);
294124
+ }
294125
+ async function summarizeTaskProgress(input) {
294126
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
294127
+ const { ProbeAgent } = __nccwpck_require__(83841);
294128
+ const agentOptions = {
294129
+ sessionId: `visor-task-progress-${Date.now()}-${crypto_1.default.randomUUID().slice(0, 8)}`,
294130
+ systemPrompt: input.config.prompt,
294131
+ maxIterations: 1,
294132
+ disableTools: true,
294133
+ };
294134
+ if (input.config.model)
294135
+ agentOptions.model = input.config.model;
294136
+ if (input.config.provider)
294137
+ agentOptions.provider = input.config.provider;
294138
+ const agent = new ProbeAgent(agentOptions);
294139
+ if (typeof agent.initialize === 'function') {
294140
+ await agent.initialize();
294141
+ }
294142
+ const userPrompt = [
294143
+ `<user_request>\n${input.requestText}\n</user_request>`,
294144
+ input.previousUpdate
294145
+ ? `<previous_update>\n${input.previousUpdate}\n</previous_update>`
294146
+ : '<previous_update>(none)</previous_update>',
294147
+ `<timing>\nstarted_at: ${input.startedAt.toISOString()}\nnow: ${input.now.toISOString()}\nelapsed: ${formatDuration(input.elapsedSeconds)}\nlast_update_at: ${input.previousUpdateAt ? input.previousUpdateAt.toISOString() : '(none)'}\ntime_since_last_update: ${input.previousUpdateAt ? formatDuration(input.secondsSincePreviousUpdate || 0) : '(none)'}\n</timing>`,
294148
+ `<execution_trace>\n${input.traceSnapshot}\n</execution_trace>`,
294149
+ ].join('\n\n');
294150
+ const response = await agent.answer(userPrompt);
294151
+ const cleaned = response
294152
+ .replace(/^```(?:markdown|md|text)?\s*\n?/i, '')
294153
+ .replace(/\n?```\s*$/m, '')
294154
+ .trim();
294155
+ return cleaned || null;
294156
+ }
294157
+ function formatDuration(totalSeconds) {
294158
+ const seconds = Math.max(0, Math.floor(totalSeconds));
294159
+ const hours = Math.floor(seconds / 3600);
294160
+ const minutes = Math.floor((seconds % 3600) / 60);
294161
+ const remainingSeconds = seconds % 60;
294162
+ const parts = [];
294163
+ if (hours > 0)
294164
+ parts.push(`${hours}h`);
294165
+ if (minutes > 0)
294166
+ parts.push(`${minutes}m`);
294167
+ if (remainingSeconds > 0 || parts.length === 0)
294168
+ parts.push(`${remainingSeconds}s`);
294169
+ return parts.join(' ');
294170
+ }
294171
+ function normalizeProgressSummary(text) {
294172
+ const wantedLabels = ['Progress', 'Last done', 'Now', 'Waiting on'];
294173
+ const lines = text
294174
+ .split(/\r?\n/)
294175
+ .map(line => line.trim())
294176
+ .filter(Boolean);
294177
+ const matched = new Map();
294178
+ for (const line of lines) {
294179
+ const match = line.match(/^-?\s*(Progress|Last done|Now|Waiting on|Timing)\s*:\s*(.+)$/i);
294180
+ if (!match)
294181
+ continue;
294182
+ const label = match[1].toLowerCase();
294183
+ if (label === 'timing')
294184
+ continue;
294185
+ const canonical = wantedLabels.find(item => item.toLowerCase() === label);
294186
+ if (canonical && !matched.has(canonical)) {
294187
+ matched.set(canonical, match[2].trim());
294188
+ }
294189
+ }
294190
+ if (matched.size === wantedLabels.length) {
294191
+ return wantedLabels.map(label => `- ${label}: ${matched.get(label)}`).join('\n');
294192
+ }
294193
+ const withoutTiming = lines.filter(line => !/^-?\s*(Timing|Metadata|Trace)\s*:/i.test(line));
294194
+ return withoutTiming.join('\n');
294195
+ }
294196
+ function formatProgressMetadata(timing) {
294197
+ const parts = [`elapsed ${formatDuration(timing.elapsedSeconds)}`];
294198
+ if (timing.previousUpdateAt) {
294199
+ parts.push(`previous update ${formatDuration(timing.secondsSincePreviousUpdate || 0)} ago`);
294200
+ parts.push(`at ${timing.previousUpdateAt.toISOString()}`);
294201
+ }
294202
+ else {
294203
+ parts.push('first live update');
294204
+ }
294205
+ if (timing.activatedSkills && timing.activatedSkills.length > 0) {
294206
+ parts.push(`activated skills ${formatSkillList(timing.activatedSkills)}`);
294207
+ }
294208
+ return `_Metadata: ${parts.join(' | ')}_`;
294209
+ }
294210
+ function formatSkillList(skills) {
294211
+ const normalized = dedupeStrings(skills);
294212
+ if (normalized.length <= 4)
294213
+ return normalized.join(', ');
294214
+ return `${normalized.slice(0, 4).join(', ')} +${normalized.length - 4} more`;
294215
+ }
294216
+ function dedupeStrings(values) {
294217
+ if (!Array.isArray(values))
294218
+ return [];
294219
+ return [...new Set(values.map(value => String(value || '').trim()).filter(Boolean))];
294220
+ }
294221
+ async function extractTraceSkillMetadata(traceRef, traceId) {
294222
+ if (!traceRef && !traceId)
294223
+ return undefined;
294224
+ try {
294225
+ const spans = await (0, trace_serializer_1.fetchTraceSpans)(traceId || traceRef);
294226
+ if (!spans.length)
294227
+ return undefined;
294228
+ const routeIntentSpan = spans.find(span => span.attributes['visor.check.id'] === 'route-intent');
294229
+ const buildConfigSpan = spans.find(span => span.attributes['visor.check.id'] === 'build-config');
294230
+ const classifySpan = spans.find(span => span.attributes['visor.check.id'] === 'classify');
294231
+ const routeIntentOutput = parseJsonAttribute(routeIntentSpan?.attributes['visor.check.output']);
294232
+ const classifyOutput = parseJsonAttribute(classifySpan?.attributes['visor.check.output']);
294233
+ const buildConfigOutput = parseJsonAttribute(buildConfigSpan?.attributes['visor.check.output']);
294234
+ const activatedSkills = dedupeStrings(Array.isArray(buildConfigOutput?.activated_skills)
294235
+ ? buildConfigOutput.activated_skills
294236
+ : undefined);
294237
+ const fallbackActivatedSkills = dedupeStrings((Array.isArray(routeIntentOutput?.skills) ? routeIntentOutput.skills : undefined) ||
294238
+ (Array.isArray(classifyOutput?.skills) ? classifyOutput.skills : undefined));
294239
+ const finalActivatedSkills = activatedSkills.length > 0 ? activatedSkills : fallbackActivatedSkills;
294240
+ if (!finalActivatedSkills.length)
294241
+ return undefined;
294242
+ return { activatedSkills: finalActivatedSkills };
294243
+ }
294244
+ catch (err) {
294245
+ logger_1.logger.debug(`[TaskLiveUpdates] Failed to extract skill metadata from trace: ${err instanceof Error ? err.message : String(err)}`);
294246
+ return undefined;
294247
+ }
294248
+ }
294249
+ function parseJsonAttribute(value) {
294250
+ if (typeof value !== 'string' || !value.trim())
294251
+ return undefined;
294252
+ try {
294253
+ const parsed = JSON.parse(value);
294254
+ return parsed && typeof parsed === 'object' ? parsed : undefined;
294255
+ }
294256
+ catch {
294257
+ return undefined;
294258
+ }
294259
+ }
294260
+ function buildStallFallbackSummary(traceSnapshot, previousUpdate) {
294261
+ const lastDone = extractProgressField(previousUpdate, 'Last done') ||
294262
+ 'continuing from the last completed analysis step';
294263
+ const lower = traceSnapshot.toLowerCase();
294264
+ let now = 'waiting for the current analysis step to finish';
294265
+ let waitingOn = 'the current analysis step to complete';
294266
+ if (lower.includes('search.delegate') ||
294267
+ lower.includes('tool: search') ||
294268
+ lower.includes('search(')) {
294269
+ now = 'running or waiting on the current code search step';
294270
+ waitingOn = 'search results and downstream analysis to finish';
294271
+ }
294272
+ else if (lower.includes('extract(') || lower.includes('tool: extract')) {
294273
+ now = 'extracting the relevant code or documentation context';
294274
+ waitingOn = 'the extract step to finish and be interpreted';
294275
+ }
294276
+ else if (lower.includes('engineer-task') || lower.includes('engineer')) {
294277
+ now = 'waiting on the current implementation or validation step';
294278
+ waitingOn = 'the engineer workflow to finish the current step';
294279
+ }
294280
+ else if (lower.includes('ai.request') ||
294281
+ lower.includes('gemini') ||
294282
+ lower.includes('claude') ||
294283
+ lower.includes('openai')) {
294284
+ now = 'waiting on the current model analysis step';
294285
+ waitingOn = 'the active model response to finish';
294286
+ }
294287
+ else if (lower.includes('bash(') || lower.includes('go test') || lower.includes('npm test')) {
294288
+ now = 'running or waiting on command-based validation';
294289
+ waitingOn = 'the current command or test run to finish';
294290
+ }
294291
+ else if (lower.includes('setup-projects') || lower.includes('build-config')) {
294292
+ now = 'preparing the workspace and loading the required context';
294293
+ waitingOn = 'workspace setup and context loading to finish';
294294
+ }
294295
+ return [
294296
+ '- Progress: still working through the same step; no new completed action yet',
294297
+ `- Last done: ${lastDone}`,
294298
+ `- Now: ${now}`,
294299
+ `- Waiting on: ${waitingOn}`,
294300
+ ].join('\n');
294301
+ }
294302
+ function extractProgressField(text, label) {
294303
+ if (!text)
294304
+ return undefined;
294305
+ const match = text.match(new RegExp(`(?:^|\\n)-?\\s*${escapeRegExp(label)}\\s*:\\s*(.+)$`, 'im'));
294306
+ return match?.[1]?.trim() || undefined;
294307
+ }
294308
+ function escapeRegExp(value) {
294309
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
294310
+ }
294311
+
294312
+
293451
294313
  /***/ }),
293452
294314
 
293453
294315
  /***/ 38570:
@@ -293503,6 +294365,7 @@ exports.getTaskProgressToolDefinition = getTaskProgressToolDefinition;
293503
294365
  exports.isTaskProgressTool = isTaskProgressTool;
293504
294366
  exports.handleTaskProgressAction = handleTaskProgressAction;
293505
294367
  const logger_1 = __nccwpck_require__(86999);
294368
+ const task_trace_resolution_1 = __nccwpck_require__(4018);
293506
294369
  // ---------------------------------------------------------------------------
293507
294370
  // Tool definition
293508
294371
  // ---------------------------------------------------------------------------
@@ -293627,8 +294490,9 @@ async function handleTrace(taskId, taskStore) {
293627
294490
  if (!task) {
293628
294491
  return { success: false, error: `Task not found: ${taskId}` };
293629
294492
  }
293630
- const traceId = task.metadata?.trace_id;
293631
- const traceFile = task.metadata?.trace_file;
294493
+ const resolvedTrace = await (0, task_trace_resolution_1.resolveTaskTraceReference)(task.metadata);
294494
+ const traceId = resolvedTrace.traceId;
294495
+ const traceFile = resolvedTrace.traceFile;
293632
294496
  if (!traceId && !traceFile) {
293633
294497
  // No trace available — return basic status info
293634
294498
  const trigger = task.metadata?.slack_trigger_text || '';
@@ -293648,17 +294512,12 @@ async function handleTrace(taskId, taskStore) {
293648
294512
  };
293649
294513
  }
293650
294514
  // Lazy-import the trace serializer to avoid circular dependencies
293651
- const { serializeTraceForPrompt, readTraceIdFromFile } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(48666)));
293652
- // Get trace ID from file if needed
293653
- let resolvedTraceId = traceId;
293654
- if (!resolvedTraceId && traceFile) {
293655
- resolvedTraceId = await readTraceIdFromFile(traceFile);
293656
- }
294515
+ const { serializeTraceForPrompt } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(48666)));
293657
294516
  // Get the task response for full output context
293658
294517
  const taskResponse = task.status?.message?.parts?.[0]?.text;
293659
294518
  // Serialize the trace — use generous char limit for AI consumption
293660
- const traceTree = await serializeTraceForPrompt(traceFile || resolvedTraceId || '', 8000, // generous limit so AI gets good context
293661
- undefined, taskResponse, resolvedTraceId || undefined);
294519
+ const traceTree = await serializeTraceForPrompt(resolvedTrace.primaryRef || '', 8000, // generous limit so AI gets good context
294520
+ undefined, taskResponse, traceId || undefined);
293662
294521
  const trigger = task.metadata?.slack_trigger_text || '';
293663
294522
  const elapsed = timeSince(task.created_at);
293664
294523
  const lines = [
@@ -294429,6 +295288,68 @@ class TaskStreamManager {
294429
295288
  exports.TaskStreamManager = TaskStreamManager;
294430
295289
 
294431
295290
 
295291
+ /***/ }),
295292
+
295293
+ /***/ 4018:
295294
+ /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
295295
+
295296
+ "use strict";
295297
+
295298
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
295299
+ if (k2 === undefined) k2 = k;
295300
+ var desc = Object.getOwnPropertyDescriptor(m, k);
295301
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
295302
+ desc = { enumerable: true, get: function() { return m[k]; } };
295303
+ }
295304
+ Object.defineProperty(o, k2, desc);
295305
+ }) : (function(o, m, k, k2) {
295306
+ if (k2 === undefined) k2 = k;
295307
+ o[k2] = m[k];
295308
+ }));
295309
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
295310
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
295311
+ }) : function(o, v) {
295312
+ o["default"] = v;
295313
+ });
295314
+ var __importStar = (this && this.__importStar) || (function () {
295315
+ var ownKeys = function(o) {
295316
+ ownKeys = Object.getOwnPropertyNames || function (o) {
295317
+ var ar = [];
295318
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
295319
+ return ar;
295320
+ };
295321
+ return ownKeys(o);
295322
+ };
295323
+ return function (mod) {
295324
+ if (mod && mod.__esModule) return mod;
295325
+ var result = {};
295326
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
295327
+ __setModuleDefault(result, mod);
295328
+ return result;
295329
+ };
295330
+ })();
295331
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
295332
+ exports.resolveTaskTraceReference = resolveTaskTraceReference;
295333
+ async function resolveTaskTraceReference(metadata) {
295334
+ const traceFile = metadata?.trace_file;
295335
+ let traceId = metadata?.trace_id;
295336
+ if (!traceId && traceFile) {
295337
+ try {
295338
+ const { readTraceIdFromFile } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(48666)));
295339
+ traceId = (await readTraceIdFromFile(traceFile)) || undefined;
295340
+ }
295341
+ catch {
295342
+ traceId = undefined;
295343
+ }
295344
+ }
295345
+ return {
295346
+ traceId,
295347
+ traceFile,
295348
+ primaryRef: traceId || traceFile || undefined,
295349
+ };
295350
+ }
295351
+
295352
+
294432
295353
  /***/ }),
294433
295354
 
294434
295355
  /***/ 5932:
@@ -296786,13 +297707,17 @@ fallbackTraceId) {
296786
297707
  const remoteTraceId = fallbackTraceId ||
296787
297708
  (!isFilePath ? traceIdOrPath : (await readTraceIdFromFile(traceIdOrPath)) || undefined);
296788
297709
  const preferLocalFirst = backendOrder[0] === 'file';
297710
+ logger_1.logger.debug(`[TraceSerializer] serializeTraceForPrompt ref=${traceIdOrPath} remoteTraceId=${remoteTraceId || '-'} backendOrder=${backendOrder.join('>')}`);
296789
297711
  if (preferLocalFirst && localTracePath) {
297712
+ logger_1.logger.debug(`[TraceSerializer] Trying local trace file first: ${localTracePath}`);
296790
297713
  spans = await fetchTraceSpans(localTracePath, { ...cfg, type: 'file' });
296791
297714
  }
296792
297715
  if (spans.length === 0 && remoteTraceId) {
297716
+ logger_1.logger.debug(`[TraceSerializer] Trying remote trace backends for trace_id=${remoteTraceId}`);
296793
297717
  spans = await fetchTraceSpans(remoteTraceId, cfg);
296794
297718
  }
296795
297719
  if (spans.length === 0 && localTracePath) {
297720
+ logger_1.logger.debug(`[TraceSerializer] Falling back to local trace file: ${localTracePath}`);
296796
297721
  spans = await fetchTraceSpans(localTracePath, { ...cfg, type: 'file' });
296797
297722
  }
296798
297723
  if (spans.length === 0) {
@@ -296889,6 +297814,9 @@ function renderSpanYaml(tree, allSpans, opts) {
296889
297814
  }
296890
297815
  function renderYamlNode(node, indent, lines, dedup, renderContext, fallbackIntent, fullOutput, maxLen, parentSpan) {
296891
297816
  if (shouldSkipLifecycleSpan(node.span, renderContext)) {
297817
+ for (const child of node.children) {
297818
+ renderYamlNode(child, indent, lines, dedup, renderContext, fallbackIntent, fullOutput, maxLen, parentSpan);
297819
+ }
296892
297820
  return;
296893
297821
  }
296894
297822
  const pad = ' '.repeat(indent);
@@ -297151,7 +298079,10 @@ function renderYamlOutput(rawOutput, pad, label, spanName, dedup, lines, fullOut
297151
298079
  // and {text: "..."} → render text inline
297152
298080
  if (typeof obj === 'object' && !Array.isArray(obj)) {
297153
298081
  const keys = Object.keys(obj);
297154
- if (keys.length === 1 && typeof obj[keys[0]] === 'object' && obj[keys[0]] !== null) {
298082
+ if (keys.length === 1 &&
298083
+ typeof obj[keys[0]] === 'object' &&
298084
+ obj[keys[0]] !== null &&
298085
+ !Array.isArray(obj[keys[0]])) {
297155
298086
  obj = obj[keys[0]]; // unwrap {answer: {...}} → {...}
297156
298087
  }
297157
298088
  // If single text key, render inline
@@ -297844,7 +298775,7 @@ function formatJsonPreview(obj, maxLen) {
297844
298775
  let len = 2; // for { }
297845
298776
  for (const [key, val] of Object.entries(obj)) {
297846
298777
  // Skip internal/verbose keys
297847
- if (key === 'raw' || key === 'skills' || key === 'tags')
298778
+ if (key === 'raw' || key === 'tags')
297848
298779
  continue;
297849
298780
  let valStr;
297850
298781
  if (val === null || val === undefined)
@@ -297865,8 +298796,15 @@ function formatJsonPreview(obj, maxLen) {
297865
298796
  .trim();
297866
298797
  valStr = `"${truncate(clean.split('\n')[0], Math.min(80, maxLen / 3))}"`;
297867
298798
  }
297868
- else if (Array.isArray(val))
297869
- valStr = `[${val.length}]`;
298799
+ else if (Array.isArray(val)) {
298800
+ if (val.every(item => typeof item === 'string') &&
298801
+ val.join(', ').length < Math.min(120, maxLen / 2)) {
298802
+ valStr = `[${val.join(', ')}]`;
298803
+ }
298804
+ else {
298805
+ valStr = `[${val.length}]`;
298806
+ }
298807
+ }
297870
298808
  else if (typeof val === 'object')
297871
298809
  valStr = `{${Object.keys(val).length} keys}`;
297872
298810
  else
@@ -298217,6 +299155,8 @@ const path_1 = __importDefault(__nccwpck_require__(16928));
298217
299155
  const logger_1 = __nccwpck_require__(86999);
298218
299156
  const lazy_otel_1 = __nccwpck_require__(21084);
298219
299157
  const instance_id_1 = __nccwpck_require__(89942);
299158
+ const task_live_updates_1 = __nccwpck_require__(5893);
299159
+ const task_trace_resolution_1 = __nccwpck_require__(4018);
298220
299160
  function getPackageVersion() {
298221
299161
  try {
298222
299162
  return (__nccwpck_require__(8330).version) || 'dev';
@@ -298269,106 +299209,189 @@ async function trackExecution(opts, executor) {
298269
299209
  },
298270
299210
  });
298271
299211
  const instanceId = (0, instance_id_1.getInstanceId)();
298272
- taskStore.updateTaskState(task.id, 'working');
298273
- taskStore.claimTask(task.id, instanceId);
298274
- logger_1.logger.info(`[TaskTracking] Task ${task.id} started (source=${source}, workflow=${workflowId || '-'}, instance=${instanceId})`);
298275
- // Heartbeat: periodically touch updated_at so stale-task detection
298276
- // can distinguish live tasks from orphans (works across nodes).
298277
- const HEARTBEAT_INTERVAL = 60_000; // 1 minute
298278
- const heartbeatTimer = setInterval(() => {
298279
- try {
298280
- taskStore.heartbeat(task.id);
298281
- }
298282
- catch {
298283
- // best-effort — don't crash the task over a heartbeat failure
298284
- }
298285
- }, HEARTBEAT_INTERVAL);
298286
- try {
298287
- const result = await executor();
298288
- // Now that execution is done, capture the trace ID from the active span.
298289
- // At task creation time the span may not have been active yet, so we
298290
- // update metadata post-execution to ensure trace_id is stored.
298291
- try {
298292
- const activeTraceId = lazy_otel_1.trace.getActiveSpan()?.spanContext().traceId;
298293
- const persistedTraceId = (activeTraceId && activeTraceId !== '' ? activeTraceId : undefined) ||
298294
- (await readTraceIdFromFallbackFile(traceFile));
298295
- if (persistedTraceId && !task.metadata?.trace_id) {
298296
- taskStore.updateMetadata(task.id, { trace_id: persistedTraceId });
299212
+ return await logger_1.logger.withTaskContext(task.id, async () => {
299213
+ taskStore.updateTaskState(task.id, 'working');
299214
+ taskStore.claimTask(task.id, instanceId);
299215
+ logger_1.logger.info(`[TaskTracking] Task ${task.id} started (source=${source}, workflow=${workflowId || '-'}, instance=${instanceId})`);
299216
+ const liveUpdateConfig = (0, task_live_updates_1.resolveTaskLiveUpdatesConfig)(opts.liveUpdates?.config);
299217
+ const initialTraceId = lazy_otel_1.trace.getActiveSpan()?.spanContext().traceId ||
299218
+ task.metadata?.trace_id;
299219
+ if (initialTraceId && !task.metadata?.trace_id) {
299220
+ try {
299221
+ taskStore.updateMetadata(task.id, { trace_id: initialTraceId });
298297
299222
  }
299223
+ catch {
299224
+ // best-effort only
299225
+ }
299226
+ }
299227
+ const initialResolvedTrace = await (0, task_trace_resolution_1.resolveTaskTraceReference)({
299228
+ trace_id: initialTraceId || task.metadata?.trace_id,
299229
+ trace_file: task.metadata?.trace_file,
299230
+ });
299231
+ const liveUpdateManager = liveUpdateConfig && opts.liveUpdates?.sink
299232
+ ? new task_live_updates_1.TaskLiveUpdateManager({
299233
+ taskId: task.id,
299234
+ requestText: messageText,
299235
+ traceRef: initialResolvedTrace.primaryRef,
299236
+ traceId: initialResolvedTrace.traceId,
299237
+ includeTraceId: opts.liveUpdates?.includeTraceId === true,
299238
+ sink: opts.liveUpdates.sink,
299239
+ config: liveUpdateConfig,
299240
+ resolveTraceState: () => {
299241
+ let current;
299242
+ try {
299243
+ current = taskStore.getTask(task.id);
299244
+ }
299245
+ catch {
299246
+ return {
299247
+ traceRef: initialResolvedTrace.primaryRef,
299248
+ traceId: initialResolvedTrace.traceId,
299249
+ };
299250
+ }
299251
+ return {
299252
+ traceRef: current?.metadata?.trace_id ||
299253
+ current?.metadata?.trace_file ||
299254
+ initialResolvedTrace.primaryRef,
299255
+ traceId: current?.metadata?.trace_id,
299256
+ };
299257
+ },
299258
+ onPostedRef: ref => {
299259
+ try {
299260
+ taskStore.updateMetadata(task.id, ref);
299261
+ }
299262
+ catch {
299263
+ // best-effort only
299264
+ }
299265
+ },
299266
+ appendHistory: (text, stage) => {
299267
+ try {
299268
+ taskStore.appendHistory(task.id, {
299269
+ message_id: crypto_1.default.randomUUID(),
299270
+ role: 'agent',
299271
+ parts: [{ text }],
299272
+ metadata: { kind: 'task_live_update', stage, source },
299273
+ });
299274
+ }
299275
+ catch {
299276
+ // best-effort only
299277
+ }
299278
+ },
299279
+ })
299280
+ : null;
299281
+ if (liveUpdateConfig && !opts.liveUpdates?.sink) {
299282
+ logger_1.logger.debug(`[TaskTracking] Live updates requested for task ${task.id} but no sink is available for source=${source}`);
298298
299283
  }
298299
- catch {
298300
- // best-effort don't fail the task over metadata
299284
+ else if (liveUpdateManager) {
299285
+ logger_1.logger.info(`[TaskTracking] Live updates enabled for task ${task.id} (source=${source})`);
298301
299286
  }
298302
- // Extract AI response text from the result.
298303
- // result.reviewSummary.history is keyed by checkId with arrays of outputs.
298304
- // We want the LAST check's text output (the final AI response), not the
298305
- // first (which is typically the intent router).
298306
- let responseText = 'Execution completed';
299287
+ // Heartbeat: periodically touch updated_at so stale-task detection
299288
+ // can distinguish live tasks from orphans (works across nodes).
299289
+ const HEARTBEAT_INTERVAL = 60_000; // 1 minute
299290
+ const heartbeatTimer = setInterval(() => {
299291
+ try {
299292
+ taskStore.heartbeat(task.id);
299293
+ }
299294
+ catch {
299295
+ // best-effort — don't crash the task over a heartbeat failure
299296
+ }
299297
+ }, HEARTBEAT_INTERVAL);
298307
299298
  try {
298308
- const history = result?.reviewSummary?.history;
298309
- if (history) {
298310
- const entries = Object.values(history);
298311
- // Iterate in reverse — last check output is the final response
298312
- for (let i = entries.length - 1; i >= 0; i--) {
298313
- const outputs = entries[i];
298314
- if (!Array.isArray(outputs))
298315
- continue;
298316
- // Within a check, look at the last output first too
298317
- for (let j = outputs.length - 1; j >= 0; j--) {
298318
- const text = outputs[j]?.text;
298319
- if (typeof text === 'string' && text.trim().length > 0) {
298320
- responseText = text.trim();
298321
- break;
299299
+ if (liveUpdateManager) {
299300
+ await liveUpdateManager.start();
299301
+ }
299302
+ const result = await executor();
299303
+ // Now that execution is done, capture the trace ID from the active span.
299304
+ // At task creation time the span may not have been active yet, so we
299305
+ // update metadata post-execution to ensure trace_id is stored.
299306
+ try {
299307
+ const activeTraceId = lazy_otel_1.trace.getActiveSpan()?.spanContext().traceId;
299308
+ const persistedTraceId = (activeTraceId && activeTraceId !== '' ? activeTraceId : undefined) ||
299309
+ (await readTraceIdFromFallbackFile(traceFile));
299310
+ if (persistedTraceId && !task.metadata?.trace_id) {
299311
+ taskStore.updateMetadata(task.id, { trace_id: persistedTraceId });
299312
+ }
299313
+ }
299314
+ catch {
299315
+ // best-effort — don't fail the task over metadata
299316
+ }
299317
+ // Extract AI response text from the result.
299318
+ // result.reviewSummary.history is keyed by checkId with arrays of outputs.
299319
+ // We want the LAST check's text output (the final AI response), not the
299320
+ // first (which is typically the intent router).
299321
+ let responseText = 'Execution completed';
299322
+ try {
299323
+ const history = result?.reviewSummary?.history;
299324
+ if (history) {
299325
+ const entries = Object.values(history);
299326
+ // Iterate in reverse — last check output is the final response
299327
+ for (let i = entries.length - 1; i >= 0; i--) {
299328
+ const outputs = entries[i];
299329
+ if (!Array.isArray(outputs))
299330
+ continue;
299331
+ // Within a check, look at the last output first too
299332
+ for (let j = outputs.length - 1; j >= 0; j--) {
299333
+ const text = outputs[j]?.text;
299334
+ if (typeof text === 'string' && text.trim().length > 0) {
299335
+ responseText = text.trim();
299336
+ break;
299337
+ }
298322
299338
  }
299339
+ if (responseText !== 'Execution completed')
299340
+ break;
298323
299341
  }
298324
- if (responseText !== 'Execution completed')
298325
- break;
298326
299342
  }
298327
299343
  }
299344
+ catch {
299345
+ // ignore extraction errors
299346
+ }
299347
+ const completedMsg = {
299348
+ message_id: crypto_1.default.randomUUID(),
299349
+ role: 'agent',
299350
+ parts: [{ text: responseText }],
299351
+ };
299352
+ try {
299353
+ taskStore.updateTaskState(task.id, 'completed', completedMsg);
299354
+ logger_1.logger.info(`[TaskTracking] Task ${task.id} completed`);
299355
+ }
299356
+ catch (stateErr) {
299357
+ // Another process (e.g. stale sweep) may have already marked this task as failed.
299358
+ // Log a warning but do NOT re-throw — the execution itself succeeded.
299359
+ logger_1.logger.warn(`[TaskTracking] Task ${task.id} completed but state transition failed: ${stateErr instanceof Error ? stateErr.message : stateErr}`);
299360
+ }
299361
+ // Fire-and-forget LLM evaluation (non-blocking)
299362
+ // Enabled via opts.autoEvaluate (from config.task_evaluate) or VISOR_TASK_EVALUATE env var
299363
+ if (opts.autoEvaluate || process.env.VISOR_TASK_EVALUATE === 'true') {
299364
+ scheduleEvaluation(task.id, taskStore);
299365
+ }
299366
+ if (liveUpdateManager) {
299367
+ await liveUpdateManager.complete(responseText);
299368
+ }
299369
+ return { task, result };
298328
299370
  }
298329
- catch {
298330
- // ignore extraction errors
298331
- }
298332
- const completedMsg = {
298333
- message_id: crypto_1.default.randomUUID(),
298334
- role: 'agent',
298335
- parts: [{ text: responseText }],
298336
- };
298337
- try {
298338
- taskStore.updateTaskState(task.id, 'completed', completedMsg);
298339
- logger_1.logger.info(`[TaskTracking] Task ${task.id} completed`);
298340
- }
298341
- catch (stateErr) {
298342
- // Another process (e.g. stale sweep) may have already marked this task as failed.
298343
- // Log a warning but do NOT re-throw — the execution itself succeeded.
298344
- logger_1.logger.warn(`[TaskTracking] Task ${task.id} completed but state transition failed: ${stateErr instanceof Error ? stateErr.message : stateErr}`);
298345
- }
298346
- // Fire-and-forget LLM evaluation (non-blocking)
298347
- // Enabled via opts.autoEvaluate (from config.task_evaluate) or VISOR_TASK_EVALUATE env var
298348
- if (opts.autoEvaluate || process.env.VISOR_TASK_EVALUATE === 'true') {
298349
- scheduleEvaluation(task.id, taskStore);
298350
- }
298351
- return { task, result };
298352
- }
298353
- catch (err) {
298354
- const errorText = err instanceof Error ? err.message : String(err);
298355
- const failMessage = {
298356
- message_id: crypto_1.default.randomUUID(),
298357
- role: 'agent',
298358
- parts: [{ text: errorText }],
298359
- };
298360
- try {
298361
- taskStore.updateTaskState(task.id, 'failed', failMessage);
298362
- logger_1.logger.info(`[TaskTracking] Task ${task.id} failed: ${errorText}`);
299371
+ catch (err) {
299372
+ const errorText = err instanceof Error ? err.message : String(err);
299373
+ const failMessage = {
299374
+ message_id: crypto_1.default.randomUUID(),
299375
+ role: 'agent',
299376
+ parts: [{ text: errorText }],
299377
+ };
299378
+ try {
299379
+ taskStore.updateTaskState(task.id, 'failed', failMessage);
299380
+ logger_1.logger.info(`[TaskTracking] Task ${task.id} failed: ${errorText}`);
299381
+ }
299382
+ catch {
299383
+ // ignore double-failure
299384
+ }
299385
+ if (liveUpdateManager) {
299386
+ await liveUpdateManager.fail(`:warning: ${errorText}`);
299387
+ }
299388
+ throw err; // re-throw to preserve original behavior
298363
299389
  }
298364
- catch {
298365
- // ignore double-failure
299390
+ finally {
299391
+ clearInterval(heartbeatTimer);
299392
+ liveUpdateManager?.stop();
298366
299393
  }
298367
- throw err; // re-throw to preserve original behavior
298368
- }
298369
- finally {
298370
- clearInterval(heartbeatTimer);
298371
- }
299394
+ });
298372
299395
  }
298373
299396
  // ---------------------------------------------------------------------------
298374
299397
  // Non-blocking auto-evaluation
@@ -298376,14 +299399,16 @@ async function trackExecution(opts, executor) {
298376
299399
  function scheduleEvaluation(taskId, taskStore) {
298377
299400
  // Delay slightly to let OTEL spans flush before we try to read the trace
298378
299401
  setTimeout(async () => {
298379
- try {
298380
- const { evaluateAndStore } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(53635)));
298381
- await evaluateAndStore(taskId, taskStore);
298382
- logger_1.logger.info(`[TaskEvaluator] Auto-evaluation completed for task ${taskId}`);
298383
- }
298384
- catch (err) {
298385
- logger_1.logger.warn(`[TaskEvaluator] Auto-evaluation failed for task ${taskId}: ${err instanceof Error ? err.message : err}`);
298386
- }
299402
+ await logger_1.logger.withTaskContext(taskId, async () => {
299403
+ try {
299404
+ const { evaluateAndStore } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(53635)));
299405
+ await evaluateAndStore(taskId, taskStore);
299406
+ logger_1.logger.info(`[TaskEvaluator] Auto-evaluation completed for task ${taskId}`);
299407
+ }
299408
+ catch (err) {
299409
+ logger_1.logger.warn(`[TaskEvaluator] Auto-evaluation failed for task ${taskId}: ${err instanceof Error ? err.message : err}`);
299410
+ }
299411
+ });
298387
299412
  }, 5000);
298388
299413
  }
298389
299414
 
@@ -302814,6 +303839,8 @@ async function main() {
302814
303839
  authTokenEnv: getArg('--auth-token-env'),
302815
303840
  tlsCert: getArg('--tls-cert'),
302816
303841
  tlsKey: getArg('--tls-key'),
303842
+ asyncMode: mcpArgs.includes('--async'),
303843
+ longPollTimeout: getArg('--poll-timeout') ? Number(getArg('--poll-timeout')) : undefined,
302817
303844
  };
302818
303845
  const { startMcpServer } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(93143)));
302819
303846
  await startMcpServer(mcpOptions);
@@ -304288,6 +305315,7 @@ class CLI {
304288
305315
  .option('--mcp', 'Enable MCP HTTP server runner (composable, requires --mcp-auth-token)')
304289
305316
  .option('--mcp-port <port>', 'Port for MCP HTTP server (default: 8080)', value => parseInt(value, 10))
304290
305317
  .option('--mcp-auth-token <token>', 'Bearer token for MCP HTTP server authentication')
305318
+ .option('--mcp-async', 'Enable async job mode for MCP (start_job/get_job pattern)')
304291
305319
  .option('-c, --check <type>', 'Specify check type (can be used multiple times)', this.collectChecks, [])
304292
305320
  .option('-o, --output <format>', 'Output format (table, json, markdown, sarif)', 'table')
304293
305321
  .option('--output-file <path>', 'Write formatted output to a file instead of stdout')
@@ -304428,6 +305456,7 @@ class CLI {
304428
305456
  mcp: Boolean(options.mcp),
304429
305457
  mcpPort: options.mcpPort,
304430
305458
  mcpAuthToken: options.mcpAuthToken,
305459
+ mcpAsync: Boolean(options.mcpAsync),
304431
305460
  tui: Boolean(options.tui),
304432
305461
  keepWorkspace: Boolean(options.keepWorkspace),
304433
305462
  workspacePath: options.workspacePath,
@@ -306362,6 +307391,7 @@ class ConfigManager {
306362
307391
  'sandbox',
306363
307392
  'sandbox_defaults',
306364
307393
  'policy',
307394
+ 'task_live_updates',
306365
307395
  // Workflow metadata fields used when configs are imported as reusable workflows
306366
307396
  'id',
306367
307397
  'name',
@@ -313538,6 +314568,8 @@ exports.SlackFrontend = void 0;
313538
314568
  const client_1 = __nccwpck_require__(67681);
313539
314569
  const markdown_1 = __nccwpck_require__(34907);
313540
314570
  const lazy_otel_1 = __nccwpck_require__(21084);
314571
+ const task_live_updates_1 = __nccwpck_require__(5893);
314572
+ const logger_1 = __nccwpck_require__(86999);
313541
314573
  class SlackFrontend {
313542
314574
  name = 'slack';
313543
314575
  subs = [];
@@ -313751,7 +314783,19 @@ class SlackFrontend {
313751
314783
  return false;
313752
314784
  }
313753
314785
  }
314786
+ isLiveTaskUpdatesMode(ctx) {
314787
+ try {
314788
+ if (!this.getInboundSlackPayload(ctx))
314789
+ return false;
314790
+ return (0, task_live_updates_1.isFrontendLiveUpdatesEnabled)(ctx.config?.task_live_updates, 'slack');
314791
+ }
314792
+ catch {
314793
+ return false;
314794
+ }
314795
+ }
313754
314796
  async maybePostError(ctx, title, message, checkId) {
314797
+ if (this.isLiveTaskUpdatesMode(ctx))
314798
+ return;
313755
314799
  if (this.errorNotified)
313756
314800
  return;
313757
314801
  return this.postErrorToSlack(ctx, title, message, checkId);
@@ -313761,6 +314805,8 @@ class SlackFrontend {
313761
314805
  * Used for fatal/shutdown errors that must always reach the user.
313762
314806
  */
313763
314807
  async forcePostError(ctx, title, message, checkId) {
314808
+ if (this.isLiveTaskUpdatesMode(ctx))
314809
+ return;
313764
314810
  return this.postErrorToSlack(ctx, title, message, checkId);
313765
314811
  }
313766
314812
  async postErrorToSlack(ctx, title, message, checkId) {
@@ -313784,9 +314830,9 @@ class SlackFrontend {
313784
314830
  if (message)
313785
314831
  text += `\n${message}`;
313786
314832
  if (this.isTelemetryEnabled(ctx)) {
313787
- const traceInfo = this.getTraceInfo() || this.cachedTraceInfo;
313788
- if (traceInfo?.traceId) {
313789
- text += `\n\n\`trace_id: ${traceInfo.traceId}\``;
314833
+ const suffix = this.getExecutionReferenceSuffix();
314834
+ if (suffix) {
314835
+ text += `\n\n${suffix}`;
313790
314836
  }
313791
314837
  }
313792
314838
  const formattedText = (0, markdown_1.formatSlackText)(text);
@@ -313821,6 +314867,8 @@ class SlackFrontend {
313821
314867
  }
313822
314868
  async maybePostExecutionFailure(ctx, checkId, result) {
313823
314869
  try {
314870
+ if (this.isLiveTaskUpdatesMode(ctx))
314871
+ return;
313824
314872
  if (this.errorNotified)
313825
314873
  return;
313826
314874
  const cfg = ctx.config || {};
@@ -313930,6 +314978,8 @@ class SlackFrontend {
313930
314978
  */
313931
314979
  async maybePostDirectReply(ctx, checkId, result) {
313932
314980
  try {
314981
+ if (this.isLiveTaskUpdatesMode(ctx))
314982
+ return;
313933
314983
  const cfg = ctx.config || {};
313934
314984
  const checkCfg = cfg.checks?.[checkId];
313935
314985
  if (!checkCfg)
@@ -314108,9 +315158,8 @@ class SlackFrontend {
314108
315158
  const telemetryEnabled = telemetryCfg === true ||
314109
315159
  (telemetryCfg && typeof telemetryCfg === 'object' && telemetryCfg.enabled === true);
314110
315160
  if (telemetryEnabled) {
314111
- const traceInfo = this.getTraceInfo() || this.cachedTraceInfo;
314112
- if (traceInfo?.traceId) {
314113
- const suffix = `\`trace_id: ${traceInfo.traceId}\``;
315161
+ const suffix = this.getExecutionReferenceSuffix();
315162
+ if (suffix) {
314114
315163
  decoratedText = `${decoratedText}\n\n${suffix}`;
314115
315164
  }
314116
315165
  }
@@ -314157,6 +315206,20 @@ class SlackFrontend {
314157
315206
  return null;
314158
315207
  }
314159
315208
  }
315209
+ getExecutionReferenceSuffix() {
315210
+ try {
315211
+ const taskId = logger_1.logger.getCurrentTaskId();
315212
+ if (taskId)
315213
+ return `\`task_id: ${taskId}\``;
315214
+ const traceInfo = this.getTraceInfo() || this.cachedTraceInfo;
315215
+ if (traceInfo?.traceId)
315216
+ return `\`trace_id: ${traceInfo.traceId}\``;
315217
+ return null;
315218
+ }
315219
+ catch {
315220
+ return null;
315221
+ }
315222
+ }
314160
315223
  }
314161
315224
  exports.SlackFrontend = SlackFrontend;
314162
315225
 
@@ -314172,6 +315235,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
314172
315235
  exports.TeamsFrontend = void 0;
314173
315236
  const client_1 = __nccwpck_require__(55391);
314174
315237
  const markdown_1 = __nccwpck_require__(37349);
315238
+ const task_live_updates_1 = __nccwpck_require__(5893);
314175
315239
  class TeamsFrontend {
314176
315240
  name = 'teams';
314177
315241
  subs = [];
@@ -314280,6 +315344,8 @@ class TeamsFrontend {
314280
315344
  return;
314281
315345
  if (checkCfg.criticality === 'internal')
314282
315346
  return;
315347
+ if ((0, task_live_updates_1.isFrontendLiveUpdatesEnabled)(cfg.task_live_updates, 'teams'))
315348
+ return;
314283
315349
  // For AI checks, only post simple schemas
314284
315350
  if (isAi) {
314285
315351
  const schema = checkCfg.schema;
@@ -314360,6 +315426,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
314360
315426
  exports.TelegramFrontend = void 0;
314361
315427
  const client_1 = __nccwpck_require__(68148);
314362
315428
  const markdown_1 = __nccwpck_require__(21506);
315429
+ const task_live_updates_1 = __nccwpck_require__(5893);
314363
315430
  class TelegramFrontend {
314364
315431
  name = 'telegram';
314365
315432
  subs = [];
@@ -314548,6 +315615,8 @@ class TelegramFrontend {
314548
315615
  return;
314549
315616
  if (checkCfg.criticality === 'internal')
314550
315617
  return;
315618
+ if ((0, task_live_updates_1.isFrontendLiveUpdatesEnabled)(cfg.task_live_updates, 'telegram'))
315619
+ return;
314551
315620
  // For AI checks, only post simple schemas
314552
315621
  if (isAi) {
314553
315622
  const schema = checkCfg.schema;
@@ -315039,6 +316108,17 @@ exports.configSchema = {
315039
316108
  ],
315040
316109
  description: 'Automatically evaluate completed tasks using an LLM judge. Requires task_tracking to be enabled. Runs asynchronously after task completion. Set to `true` for defaults, or provide an object to configure.',
315041
316110
  },
316111
+ task_live_updates: {
316112
+ anyOf: [
316113
+ {
316114
+ type: 'boolean',
316115
+ },
316116
+ {
316117
+ $ref: '#/definitions/TaskLiveUpdatesConfig',
316118
+ },
316119
+ ],
316120
+ description: 'Live progress updates for long-running tracked tasks on supported frontends.',
316121
+ },
315042
316122
  },
315043
316123
  required: ['version'],
315044
316124
  patternProperties: {
@@ -315910,7 +316990,7 @@ exports.configSchema = {
315910
316990
  description: 'Arguments/inputs for the workflow',
315911
316991
  },
315912
316992
  overrides: {
315913
- $ref: '#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-15521-30601-src_types_config.ts-0-61047%3E%3E',
316993
+ $ref: '#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-15521-30601-src_types_config.ts-0-62422%3E%3E',
315914
316994
  description: 'Override specific step configurations in the workflow',
315915
316995
  },
315916
316996
  output_mapping: {
@@ -315926,7 +317006,7 @@ exports.configSchema = {
315926
317006
  description: 'Config file path - alternative to workflow ID (loads a Visor config file as workflow)',
315927
317007
  },
315928
317008
  workflow_overrides: {
315929
- $ref: '#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-15521-30601-src_types_config.ts-0-61047%3E%3E',
317009
+ $ref: '#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-15521-30601-src_types_config.ts-0-62422%3E%3E',
315930
317010
  description: 'Alias for overrides - workflow step overrides (backward compatibility)',
315931
317011
  },
315932
317012
  ref: {
@@ -316654,7 +317734,7 @@ exports.configSchema = {
316654
317734
  description: 'Custom output name (defaults to workflow name)',
316655
317735
  },
316656
317736
  overrides: {
316657
- $ref: '#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-15521-30601-src_types_config.ts-0-61047%3E%3E',
317737
+ $ref: '#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-15521-30601-src_types_config.ts-0-62422%3E%3E',
316658
317738
  description: 'Step overrides',
316659
317739
  },
316660
317740
  output_mapping: {
@@ -316669,13 +317749,13 @@ exports.configSchema = {
316669
317749
  '^x-': {},
316670
317750
  },
316671
317751
  },
316672
- 'Record<string,Partial<interface-src_types_config.ts-15521-30601-src_types_config.ts-0-61047>>': {
317752
+ 'Record<string,Partial<interface-src_types_config.ts-15521-30601-src_types_config.ts-0-62422>>': {
316673
317753
  type: 'object',
316674
317754
  additionalProperties: {
316675
- $ref: '#/definitions/Partial%3Cinterface-src_types_config.ts-15521-30601-src_types_config.ts-0-61047%3E',
317755
+ $ref: '#/definitions/Partial%3Cinterface-src_types_config.ts-15521-30601-src_types_config.ts-0-62422%3E',
316676
317756
  },
316677
317757
  },
316678
- 'Partial<interface-src_types_config.ts-15521-30601-src_types_config.ts-0-61047>': {
317758
+ 'Partial<interface-src_types_config.ts-15521-30601-src_types_config.ts-0-62422>': {
316679
317759
  type: 'object',
316680
317760
  additionalProperties: false,
316681
317761
  },
@@ -318608,6 +319688,102 @@ exports.configSchema = {
318608
319688
  '^x-': {},
318609
319689
  },
318610
319690
  },
319691
+ TaskLiveUpdatesConfig: {
319692
+ type: 'object',
319693
+ properties: {
319694
+ enabled: {
319695
+ type: 'boolean',
319696
+ description: 'Enable live task progress updates (default: true when config object is present)',
319697
+ },
319698
+ interval_seconds: {
319699
+ type: 'number',
319700
+ description: 'Update interval in seconds (default: 10)',
319701
+ },
319702
+ model: {
319703
+ type: 'string',
319704
+ description: 'LLM model to use for progress summarization',
319705
+ },
319706
+ provider: {
319707
+ type: 'string',
319708
+ description: 'AI provider to use for progress summarization',
319709
+ },
319710
+ prompt: {
319711
+ type: 'string',
319712
+ description: 'Custom system prompt for the progress summarizer',
319713
+ },
319714
+ initial_message: {
319715
+ type: 'string',
319716
+ description: 'Immediate placeholder text posted before the first summary is generated',
319717
+ },
319718
+ max_trace_chars: {
319719
+ type: 'number',
319720
+ description: 'Maximum serialized trace characters passed into the summarizer (default: 12000)',
319721
+ },
319722
+ frontends: {
319723
+ type: 'object',
319724
+ properties: {
319725
+ slack: {
319726
+ type: 'object',
319727
+ properties: {
319728
+ enabled: {
319729
+ type: 'boolean',
319730
+ },
319731
+ },
319732
+ additionalProperties: false,
319733
+ patternProperties: {
319734
+ '^x-': {},
319735
+ },
319736
+ },
319737
+ telegram: {
319738
+ type: 'object',
319739
+ properties: {
319740
+ enabled: {
319741
+ type: 'boolean',
319742
+ },
319743
+ },
319744
+ additionalProperties: false,
319745
+ patternProperties: {
319746
+ '^x-': {},
319747
+ },
319748
+ },
319749
+ teams: {
319750
+ type: 'object',
319751
+ properties: {
319752
+ enabled: {
319753
+ type: 'boolean',
319754
+ },
319755
+ },
319756
+ additionalProperties: false,
319757
+ patternProperties: {
319758
+ '^x-': {},
319759
+ },
319760
+ },
319761
+ whatsapp: {
319762
+ type: 'object',
319763
+ properties: {
319764
+ enabled: {
319765
+ type: 'boolean',
319766
+ },
319767
+ },
319768
+ additionalProperties: false,
319769
+ patternProperties: {
319770
+ '^x-': {},
319771
+ },
319772
+ },
319773
+ },
319774
+ additionalProperties: false,
319775
+ description: 'Per-frontend enablement overrides',
319776
+ patternProperties: {
319777
+ '^x-': {},
319778
+ },
319779
+ },
319780
+ },
319781
+ additionalProperties: false,
319782
+ description: 'Live task progress updates for supported interactive frontends.\n\nWhen enabled, Visor posts a "working on it" placeholder and periodically updates the same message using a cheap internal LLM summary of the current execution trace. The final answer replaces that same message.',
319783
+ patternProperties: {
319784
+ '^x-': {},
319785
+ },
319786
+ },
318611
319787
  },
318612
319788
  };
318613
319789
  exports["default"] = exports.configSchema;
@@ -322522,6 +323698,7 @@ exports.configureLoggerFromCli = configureLoggerFromCli;
322522
323698
  * - Supports levels: silent < error < warn < info < verbose < debug
322523
323699
  * - Routes logs to stderr to keep stdout clean for machine-readable output
322524
323700
  */
323701
+ const node_async_hooks_1 = __nccwpck_require__(16698);
322525
323702
  const lazy_otel_1 = __nccwpck_require__(21084);
322526
323703
  function levelToNumber(level) {
322527
323704
  switch (level) {
@@ -322549,6 +323726,7 @@ const OTEL_SEVERITY = {
322549
323726
  debug: 5, // DEBUG
322550
323727
  };
322551
323728
  class Logger {
323729
+ taskContextStorage = new node_async_hooks_1.AsyncLocalStorage();
322552
323730
  level = 'info';
322553
323731
  isJsonLike = false;
322554
323732
  isTTY = typeof process !== 'undefined' ? !!process.stderr.isTTY : false;
@@ -322605,11 +323783,25 @@ class Logger {
322605
323783
  }
322606
323784
  return true;
322607
323785
  }
322608
- getTraceSuffix(msg) {
323786
+ withTaskContext(taskId, fn) {
323787
+ const current = this.taskContextStorage.getStore() || {};
323788
+ return this.taskContextStorage.run({ ...current, taskId }, fn);
323789
+ }
323790
+ getCurrentTaskId() {
323791
+ return this.taskContextStorage.getStore()?.taskId;
323792
+ }
323793
+ getContextSuffix(msg) {
322609
323794
  if (!msg)
322610
323795
  return '';
322611
- if (msg.includes('trace_id=') || msg.includes('trace_id:'))
323796
+ if (msg.includes('task_id=') ||
323797
+ msg.includes('task_id:') ||
323798
+ msg.includes('trace_id=') ||
323799
+ msg.includes('trace_id:')) {
322612
323800
  return '';
323801
+ }
323802
+ const taskId = this.getCurrentTaskId();
323803
+ if (taskId)
323804
+ return ` [task_id=${taskId}]`;
322613
323805
  try {
322614
323806
  const span = lazy_otel_1.trace.getSpan(lazy_otel_1.context.active()) || lazy_otel_1.trace.getActiveSpan();
322615
323807
  const ctx = span?.spanContext?.();
@@ -322639,12 +323831,14 @@ class Logger {
322639
323831
  return;
322640
323832
  const span = lazy_otel_1.trace.getSpan(lazy_otel_1.context.active()) || lazy_otel_1.trace.getActiveSpan();
322641
323833
  const spanCtx = span?.spanContext?.();
323834
+ const taskId = this.getCurrentTaskId();
322642
323835
  this.otelLogger.emit({
322643
323836
  severityNumber: OTEL_SEVERITY[level] || 9,
322644
323837
  severityText: level.toUpperCase(),
322645
323838
  body: msg,
322646
323839
  attributes: {
322647
323840
  'visor.logger': true,
323841
+ ...(taskId ? { task_id: taskId } : {}),
322648
323842
  ...(spanCtx?.traceId ? { trace_id: spanCtx.traceId, span_id: spanCtx.spanId } : {}),
322649
323843
  },
322650
323844
  });
@@ -322655,7 +323849,7 @@ class Logger {
322655
323849
  }
322656
323850
  write(msg, level) {
322657
323851
  // Always route to stderr to keep stdout clean for results
322658
- const suffix = this.getTraceSuffix(msg);
323852
+ const suffix = this.getContextSuffix(msg);
322659
323853
  const decoratedMsg = suffix ? `${msg}${suffix}` : msg;
322660
323854
  const lvl = level || 'info';
322661
323855
  // Emit to OTel Logs pipeline (non-blocking, best-effort)
@@ -322781,6 +323975,337 @@ function configureLoggerFromCli(options) {
322781
323975
  }
322782
323976
 
322783
323977
 
323978
+ /***/ }),
323979
+
323980
+ /***/ 70279:
323981
+ /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
323982
+
323983
+ "use strict";
323984
+
323985
+ /**
323986
+ * MCP Job Manager — async job pattern for long-running MCP tool calls.
323987
+ *
323988
+ * Claude Code and other MCP clients have timeout issues with long-running tools.
323989
+ * This module provides a `start_job` / `get_job` pattern: the tool call returns
323990
+ * immediately with a job_id, and the client polls `get_job` until done.
323991
+ *
323992
+ * This is a thin wrapper over the existing TaskStore — jobs are persisted as
323993
+ * regular Visor tasks (visible via `visor tasks list`, `visor tasks show`).
323994
+ */
323995
+ var __importDefault = (this && this.__importDefault) || function (mod) {
323996
+ return (mod && mod.__esModule) ? mod : { "default": mod };
323997
+ };
323998
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
323999
+ exports.JobManager = void 0;
324000
+ const crypto_1 = __importDefault(__nccwpck_require__(76982));
324001
+ const logger_1 = __nccwpck_require__(86999);
324002
+ /** Map TaskState → JobStatus for the MCP response. */
324003
+ function taskStateToJobStatus(state) {
324004
+ switch (state) {
324005
+ case 'submitted':
324006
+ return 'queued';
324007
+ case 'working':
324008
+ case 'input_required':
324009
+ case 'auth_required':
324010
+ return 'running';
324011
+ case 'completed':
324012
+ return 'completed';
324013
+ case 'failed':
324014
+ case 'canceled':
324015
+ case 'rejected':
324016
+ return 'failed';
324017
+ default:
324018
+ return 'running';
324019
+ }
324020
+ }
324021
+ /**
324022
+ * Build the standardized MCP job response from an AgentTask.
324023
+ */
324024
+ function buildResponse(task) {
324025
+ const status = taskStateToJobStatus(task.status.state);
324026
+ const done = status === 'completed' || status === 'failed';
324027
+ const polling = done
324028
+ ? { recommended_next_action: 'none', recommended_delay_seconds: 0 }
324029
+ : { recommended_next_action: 'get_job', recommended_delay_seconds: 0 };
324030
+ // Extract result text from task status message or artifacts
324031
+ let result = null;
324032
+ if (status === 'completed') {
324033
+ // The completed status message contains the AI response text
324034
+ const statusText = task.status.message?.parts?.[0]?.text;
324035
+ if (statusText) {
324036
+ result = statusText;
324037
+ }
324038
+ // Also check artifacts for richer output
324039
+ if (task.artifacts.length > 0) {
324040
+ const artifactTexts = task.artifacts
324041
+ .flatMap(a => a.parts)
324042
+ .filter(p => p.text)
324043
+ .map(p => p.text);
324044
+ if (artifactTexts.length > 0 && !result) {
324045
+ result = artifactTexts.join('\n');
324046
+ }
324047
+ }
324048
+ }
324049
+ // Extract error from failed status message
324050
+ let error = null;
324051
+ if (status === 'failed' && task.status.message) {
324052
+ const errorText = task.status.message.parts?.[0]?.text || 'Unknown error';
324053
+ error = {
324054
+ code: 'EXECUTION_ERROR',
324055
+ message: errorText,
324056
+ retryable: true,
324057
+ };
324058
+ }
324059
+ // Build progress
324060
+ let progress;
324061
+ switch (status) {
324062
+ case 'queued':
324063
+ progress = { percent: 0, step: 'queued', message: 'Job accepted and queued' };
324064
+ break;
324065
+ case 'running':
324066
+ progress = { percent: null, step: 'running', message: 'Job is executing' };
324067
+ break;
324068
+ case 'completed':
324069
+ progress = { percent: 100, step: 'completed', message: 'Job finished successfully' };
324070
+ break;
324071
+ case 'failed':
324072
+ progress = { percent: null, step: 'failed', message: 'The job failed before completion' };
324073
+ break;
324074
+ default:
324075
+ progress = { percent: null, step: status, message: `Job is ${status}` };
324076
+ }
324077
+ // Instructions for the model
324078
+ let userMessage;
324079
+ let nextInstruction;
324080
+ switch (status) {
324081
+ case 'queued':
324082
+ userMessage = 'The job has been queued.';
324083
+ nextInstruction =
324084
+ 'Call get_job with this job_id. It will wait up to 59 seconds for the result. If it returns with done still false, call get_job again.';
324085
+ break;
324086
+ case 'running':
324087
+ userMessage = 'The job is still running.';
324088
+ nextInstruction =
324089
+ 'Call get_job again with this job_id. It will wait up to 59 seconds for the result. If it returns with done still false, call get_job again.';
324090
+ break;
324091
+ case 'completed':
324092
+ userMessage = 'The result is ready.';
324093
+ nextInstruction = 'Use the result to answer the user.';
324094
+ break;
324095
+ case 'failed':
324096
+ userMessage = 'The job failed.';
324097
+ nextInstruction =
324098
+ 'Explain the failure briefly. If the user still wants the result, start a new job.';
324099
+ break;
324100
+ default:
324101
+ userMessage = `Job status: ${status}`;
324102
+ nextInstruction = 'Call get_job again after 10 seconds.';
324103
+ }
324104
+ return {
324105
+ job_id: task.id.slice(0, 8),
324106
+ status,
324107
+ done,
324108
+ progress,
324109
+ polling,
324110
+ result,
324111
+ error,
324112
+ user_message: userMessage,
324113
+ next_instruction_for_model: nextInstruction,
324114
+ };
324115
+ }
324116
+ /** Build an "expired/not found" response. */
324117
+ function buildExpiredResponse(jobId) {
324118
+ return {
324119
+ job_id: jobId,
324120
+ status: 'expired',
324121
+ done: true,
324122
+ progress: { percent: null, step: 'expired', message: 'This job is no longer available' },
324123
+ polling: { recommended_next_action: 'none', recommended_delay_seconds: 0 },
324124
+ result: null,
324125
+ error: { code: 'JOB_EXPIRED', message: 'The job is no longer available', retryable: true },
324126
+ user_message: 'The previous job expired.',
324127
+ next_instruction_for_model: 'If the user still wants the result, start a new job.',
324128
+ };
324129
+ }
324130
+ /**
324131
+ * Manages async jobs for MCP tool calls, backed by the existing TaskStore.
324132
+ *
324133
+ * Jobs created here are visible via `visor tasks list` and `visor tasks show`.
324134
+ */
324135
+ class JobManager {
324136
+ taskStore;
324137
+ longPollTimeoutMs;
324138
+ constructor(taskStore, opts) {
324139
+ this.taskStore = taskStore;
324140
+ this.longPollTimeoutMs = opts?.longPollTimeoutMs ?? 59_000;
324141
+ }
324142
+ /**
324143
+ * Start a new async job. Creates a task, runs the handler in the background,
324144
+ * and returns immediately with the task ID.
324145
+ */
324146
+ startJob(handler, opts) {
324147
+ // Create task via trackExecution pattern (inline, since we need non-blocking)
324148
+ const { getInstanceId } = __nccwpck_require__(89942);
324149
+ const requestMessage = {
324150
+ message_id: crypto_1.default.randomUUID(),
324151
+ role: 'user',
324152
+ parts: [{ text: opts.messageText }],
324153
+ };
324154
+ const task = this.taskStore.createTask({
324155
+ contextId: crypto_1.default.randomUUID(),
324156
+ requestMessage,
324157
+ workflowId: opts.workflowId,
324158
+ requestMetadata: {
324159
+ source: 'mcp',
324160
+ instance_id: getInstanceId(),
324161
+ async_job: true,
324162
+ ...opts.metadata,
324163
+ },
324164
+ });
324165
+ // Transition to working
324166
+ this.taskStore.updateTaskState(task.id, 'working');
324167
+ this.taskStore.claimTask(task.id, getInstanceId());
324168
+ logger_1.logger.info(`[MCP-AsyncJob] Job ${task.id.slice(0, 8)} started (workflow=${opts.workflowId || '-'})`);
324169
+ // Heartbeat timer
324170
+ const heartbeatTimer = setInterval(() => {
324171
+ try {
324172
+ this.taskStore.heartbeat(task.id);
324173
+ }
324174
+ catch {
324175
+ // best-effort
324176
+ }
324177
+ }, 60_000);
324178
+ if (heartbeatTimer.unref)
324179
+ heartbeatTimer.unref();
324180
+ // Run handler in background
324181
+ handler()
324182
+ .then(result => {
324183
+ clearInterval(heartbeatTimer);
324184
+ // Extract response text from the result (same logic as track-execution.ts)
324185
+ let responseText = 'Execution completed';
324186
+ try {
324187
+ // If result is an MCP content response, extract the text
324188
+ const content = result?.content;
324189
+ if (Array.isArray(content)) {
324190
+ const texts = content.filter((c) => c.type === 'text').map((c) => c.text);
324191
+ if (texts.length > 0)
324192
+ responseText = texts.join('\n');
324193
+ }
324194
+ else if (typeof result === 'string') {
324195
+ responseText = result;
324196
+ }
324197
+ else {
324198
+ // Try engine result format
324199
+ const history = result?.reviewSummary?.history;
324200
+ if (history) {
324201
+ const entries = Object.values(history);
324202
+ for (let i = entries.length - 1; i >= 0; i--) {
324203
+ const outputs = entries[i];
324204
+ if (!Array.isArray(outputs))
324205
+ continue;
324206
+ for (let j = outputs.length - 1; j >= 0; j--) {
324207
+ const text = outputs[j]?.text;
324208
+ if (typeof text === 'string' && text.trim().length > 0) {
324209
+ responseText = text.trim();
324210
+ break;
324211
+ }
324212
+ }
324213
+ if (responseText !== 'Execution completed')
324214
+ break;
324215
+ }
324216
+ }
324217
+ }
324218
+ }
324219
+ catch {
324220
+ // ignore extraction errors
324221
+ }
324222
+ const completedMsg = {
324223
+ message_id: crypto_1.default.randomUUID(),
324224
+ role: 'agent',
324225
+ parts: [{ text: responseText }],
324226
+ };
324227
+ try {
324228
+ this.taskStore.updateTaskState(task.id, 'completed', completedMsg);
324229
+ logger_1.logger.info(`[MCP-AsyncJob] Job ${task.id.slice(0, 8)} completed`);
324230
+ }
324231
+ catch (stateErr) {
324232
+ logger_1.logger.warn(`[MCP-AsyncJob] Job ${task.id.slice(0, 8)} completed but state transition failed: ${stateErr}`);
324233
+ }
324234
+ })
324235
+ .catch(err => {
324236
+ clearInterval(heartbeatTimer);
324237
+ const errorText = err instanceof Error ? err.message : String(err);
324238
+ const failMsg = {
324239
+ message_id: crypto_1.default.randomUUID(),
324240
+ role: 'agent',
324241
+ parts: [{ text: errorText }],
324242
+ };
324243
+ try {
324244
+ this.taskStore.updateTaskState(task.id, 'failed', failMsg);
324245
+ logger_1.logger.info(`[MCP-AsyncJob] Job ${task.id.slice(0, 8)} failed: ${errorText}`);
324246
+ }
324247
+ catch (stateErr) {
324248
+ logger_1.logger.warn(`[MCP-AsyncJob] Job ${task.id.slice(0, 8)} failed but could not update task state: ${stateErr}`);
324249
+ }
324250
+ });
324251
+ return buildResponse(task);
324252
+ }
324253
+ /**
324254
+ * Get the current state of a job by ID (supports both short and full IDs).
324255
+ *
324256
+ * Uses long polling: if the job is still running, waits up to 59 seconds
324257
+ * for it to finish before responding. Returns immediately if the job is
324258
+ * already in a terminal state (completed/failed).
324259
+ */
324260
+ async getJob(jobId) {
324261
+ const resolvedTask = this.resolveTask(jobId);
324262
+ if (!resolvedTask) {
324263
+ return buildExpiredResponse(jobId);
324264
+ }
324265
+ // If already done, return immediately
324266
+ const initialStatus = taskStateToJobStatus(resolvedTask.status.state);
324267
+ if (initialStatus === 'completed' || initialStatus === 'failed') {
324268
+ return buildResponse(resolvedTask);
324269
+ }
324270
+ // Long poll: check every 500ms for up to the configured timeout
324271
+ const POLL_INTERVAL_MS = 500;
324272
+ const MAX_WAIT_MS = this.longPollTimeoutMs;
324273
+ const deadline = Date.now() + MAX_WAIT_MS;
324274
+ while (Date.now() < deadline) {
324275
+ await new Promise(resolve => setTimeout(resolve, POLL_INTERVAL_MS));
324276
+ const task = this.resolveTask(jobId);
324277
+ if (!task) {
324278
+ return buildExpiredResponse(jobId);
324279
+ }
324280
+ const status = taskStateToJobStatus(task.status.state);
324281
+ if (status === 'completed' || status === 'failed') {
324282
+ return buildResponse(task);
324283
+ }
324284
+ }
324285
+ // Timeout — return current state (still running)
324286
+ const finalTask = this.resolveTask(jobId);
324287
+ if (!finalTask) {
324288
+ return buildExpiredResponse(jobId);
324289
+ }
324290
+ return buildResponse(finalTask);
324291
+ }
324292
+ /**
324293
+ * Resolve a task by full UUID or short ID prefix.
324294
+ */
324295
+ resolveTask(jobId) {
324296
+ // Try direct lookup first (full UUID)
324297
+ let task = this.taskStore.getTask(jobId);
324298
+ // If not found and looks like a short ID, search by prefix
324299
+ if (!task && jobId.length < 36) {
324300
+ const { tasks } = this.taskStore.listTasks({ limit: 200 });
324301
+ task = tasks.find(t => t.id.startsWith(jobId)) || null;
324302
+ }
324303
+ return task;
324304
+ }
324305
+ }
324306
+ exports.JobManager = JobManager;
324307
+
324308
+
322784
324309
  /***/ }),
322785
324310
 
322786
324311
  /***/ 93143:
@@ -323156,75 +324681,143 @@ async function executeWorkflow(args) {
323156
324681
  }
323157
324682
  }
323158
324683
  /**
323159
- * Zod schema for fixed workflow tool parameters (no workflow parameter).
324684
+ * Zod schema for fixed workflow tool parameters (no workflow parameter).
324685
+ */
324686
+ exports.FixedWorkflowSchema = zod_1.z.object({
324687
+ message: zod_1.z
324688
+ .string()
324689
+ .optional()
324690
+ .describe('Optional human input message for workflows that include human-input checks. ' +
324691
+ 'This is equivalent to the --message CLI argument. Use this to provide context, ' +
324692
+ 'instructions, or answers to prompts that the workflow may request.'),
324693
+ checks: zod_1.z
324694
+ .array(zod_1.z.string())
324695
+ .optional()
324696
+ .describe('Optional list of specific check IDs to run from the workflow. ' +
324697
+ 'If not provided, all checks defined in the workflow will be executed. ' +
324698
+ 'Check IDs are the keys defined under the "checks:" section in the workflow YAML.'),
324699
+ format: zod_1.z
324700
+ .enum(['json', 'markdown', 'table'])
324701
+ .optional()
324702
+ .default('json')
324703
+ .describe('Output format for the results. "json" (default) returns structured data suitable for ' +
324704
+ 'programmatic processing. "markdown" returns human-readable formatted output with ' +
324705
+ 'severity icons. "table" returns a simple text table format.'),
324706
+ });
324707
+ /**
324708
+ * Execute a fixed workflow and return formatted results.
324709
+ *
324710
+ * @param args - The tool arguments (without workflow)
324711
+ * @param workflowPath - The pre-resolved workflow path
324712
+ * @returns MCP tool result with content
324713
+ */
324714
+ async function executeFixedWorkflow(args, workflowPath) {
324715
+ try {
324716
+ // Build execution context with message for human-input checks
324717
+ const executionContext = {};
324718
+ if (args.message) {
324719
+ executionContext.cliMessage = args.message;
324720
+ }
324721
+ // Call runChecks() from SDK
324722
+ const result = await (0, sdk_1.runChecks)({
324723
+ configPath: workflowPath,
324724
+ checks: args.checks,
324725
+ output: { format: args.format || 'json' },
324726
+ executionContext,
324727
+ cwd: process.cwd(),
324728
+ });
324729
+ // Format output based on requested format
324730
+ const groupedResults = result;
324731
+ const formattedOutput = formatResults(groupedResults, args.format || 'json');
324732
+ return {
324733
+ content: [
324734
+ {
324735
+ type: 'text',
324736
+ text: formattedOutput,
324737
+ },
324738
+ ],
324739
+ };
324740
+ }
324741
+ catch (error) {
324742
+ const errorMessage = error instanceof Error ? error.message : String(error);
324743
+ return {
324744
+ content: [
324745
+ {
324746
+ type: 'text',
324747
+ text: `Error executing workflow: ${errorMessage}`,
324748
+ },
324749
+ ],
324750
+ isError: true,
324751
+ };
324752
+ }
324753
+ }
324754
+ /**
324755
+ * Get or create a TaskStore for async job mode.
324756
+ * If one is provided in options, use it. Otherwise create a SQLite-backed store.
323160
324757
  */
323161
- exports.FixedWorkflowSchema = zod_1.z.object({
323162
- message: zod_1.z
323163
- .string()
323164
- .optional()
323165
- .describe('Optional human input message for workflows that include human-input checks. ' +
323166
- 'This is equivalent to the --message CLI argument. Use this to provide context, ' +
323167
- 'instructions, or answers to prompts that the workflow may request.'),
323168
- checks: zod_1.z
323169
- .array(zod_1.z.string())
323170
- .optional()
323171
- .describe('Optional list of specific check IDs to run from the workflow. ' +
323172
- 'If not provided, all checks defined in the workflow will be executed. ' +
323173
- 'Check IDs are the keys defined under the "checks:" section in the workflow YAML.'),
323174
- format: zod_1.z
323175
- .enum(['json', 'markdown', 'table'])
323176
- .optional()
323177
- .default('json')
323178
- .describe('Output format for the results. "json" (default) returns structured data suitable for ' +
323179
- 'programmatic processing. "markdown" returns human-readable formatted output with ' +
323180
- 'severity icons. "table" returns a simple text table format.'),
323181
- });
324758
+ async function getOrCreateTaskStore(options) {
324759
+ if (options.taskStore)
324760
+ return options.taskStore;
324761
+ const { SqliteTaskStore } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(24711)));
324762
+ const store = new SqliteTaskStore();
324763
+ await store.initialize();
324764
+ return store;
324765
+ }
323182
324766
  /**
323183
- * Execute a fixed workflow and return formatted results.
324767
+ * Register async job tools (start_job / get_job) on an MCP server instance.
324768
+ * Used by both standalone and createHttpMcpServer when asyncMode is enabled.
323184
324769
  *
323185
- * @param args - The tool arguments (without workflow)
323186
- * @param workflowPath - The pre-resolved workflow path
323187
- * @returns MCP tool result with content
324770
+ * Requires a TaskStore for persistence jobs are stored as regular Visor tasks,
324771
+ * visible via `visor tasks list` and `visor tasks show`.
323188
324772
  */
323189
- async function executeFixedWorkflow(args, workflowPath) {
323190
- try {
323191
- // Build execution context with message for human-input checks
323192
- const executionContext = {};
323193
- if (args.message) {
323194
- executionContext.cliMessage = args.message;
323195
- }
323196
- // Call runChecks() from SDK
323197
- const result = await (0, sdk_1.runChecks)({
323198
- configPath: workflowPath,
323199
- checks: args.checks,
323200
- output: { format: args.format || 'json' },
323201
- executionContext,
323202
- cwd: process.cwd(),
324773
+ async function registerAsyncJobTools(server, resolvedWorkflowPath, toolName, taskStore, longPollTimeoutMs) {
324774
+ const { JobManager } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(70279)));
324775
+ const jobManager = new JobManager(taskStore, { longPollTimeoutMs });
324776
+ const startJobName = toolName === 'run_workflow' || toolName === 'send_message' ? 'start_job' : `start_${toolName}`;
324777
+ const getJobName = 'get_job';
324778
+ const workflowId = resolvedWorkflowPath
324779
+ ? path.basename(resolvedWorkflowPath, path.extname(resolvedWorkflowPath))
324780
+ : undefined;
324781
+ // Build start_job schema based on whether we have a fixed workflow
324782
+ if (resolvedWorkflowPath) {
324783
+ server.tool(startJobName, 'Start a long-running job. Returns immediately with a job_id. ' +
324784
+ 'You MUST then call get_job with this job_id. get_job uses long polling (waits up to 59s). ' +
324785
+ 'If done is still false, call get_job again.', {
324786
+ message: exports.FixedWorkflowSchema.shape.message,
324787
+ checks: exports.FixedWorkflowSchema.shape.checks,
324788
+ format: exports.FixedWorkflowSchema.shape.format,
324789
+ }, async (args) => {
324790
+ const response = jobManager.startJob(async () => executeFixedWorkflow(args, resolvedWorkflowPath), {
324791
+ messageText: args.message || `Run workflow: ${workflowId}`,
324792
+ workflowId,
324793
+ });
324794
+ return { content: [{ type: 'text', text: JSON.stringify(response, null, 2) }] };
323203
324795
  });
323204
- // Format output based on requested format
323205
- const groupedResults = result;
323206
- const formattedOutput = formatResults(groupedResults, args.format || 'json');
323207
- return {
323208
- content: [
323209
- {
323210
- type: 'text',
323211
- text: formattedOutput,
323212
- },
323213
- ],
323214
- };
323215
324796
  }
323216
- catch (error) {
323217
- const errorMessage = error instanceof Error ? error.message : String(error);
323218
- return {
323219
- content: [
323220
- {
323221
- type: 'text',
323222
- text: `Error executing workflow: ${errorMessage}`,
323223
- },
323224
- ],
323225
- isError: true,
323226
- };
324797
+ else {
324798
+ server.tool(startJobName, 'Start a long-running job. Returns immediately with a job_id. ' +
324799
+ 'You MUST then call get_job with this job_id. get_job uses long polling (waits up to 59s). ' +
324800
+ 'If done is still false, call get_job again.', {
324801
+ workflow: exports.RunWorkflowSchema.shape.workflow,
324802
+ message: exports.RunWorkflowSchema.shape.message,
324803
+ checks: exports.RunWorkflowSchema.shape.checks,
324804
+ format: exports.RunWorkflowSchema.shape.format,
324805
+ }, async (args) => {
324806
+ const response = jobManager.startJob(async () => executeWorkflow(args), {
324807
+ messageText: args.message || `Run workflow: ${args.workflow}`,
324808
+ workflowId: args.workflow,
324809
+ });
324810
+ return { content: [{ type: 'text', text: JSON.stringify(response, null, 2) }] };
324811
+ });
323227
324812
  }
324813
+ // Register get_job tool
324814
+ server.tool(getJobName, 'Check the status of a running job using long polling. Waits up to 59 seconds for the job to finish. ' +
324815
+ 'Returns immediately if the job is already done. If done is still false after the wait, call again.', {
324816
+ job_id: zod_1.z.string().describe('The job ID returned by start_job.'),
324817
+ }, async (args) => {
324818
+ const response = await jobManager.getJob(args.job_id);
324819
+ return { content: [{ type: 'text', text: JSON.stringify(response, null, 2) }] };
324820
+ });
323228
324821
  }
323229
324822
  /**
323230
324823
  * Validate a Bearer token from an HTTP request using timing-safe comparison.
@@ -323281,7 +324874,12 @@ async function createHttpMcpServer(options) {
323281
324874
  const server = new mcp_js_1.McpServer({ name: exports.SERVER_INFO.name, version: exports.SERVER_INFO.version }, { capabilities: { tools: {} } });
323282
324875
  const toolName = options.toolName || 'run_workflow';
323283
324876
  const toolDescription = options.toolDescription || exports.RUN_WORKFLOW_DESCRIPTION;
323284
- if (resolvedWorkflowPath) {
324877
+ if (options.asyncMode) {
324878
+ const taskStore = await getOrCreateTaskStore(options);
324879
+ const longPollMs = options.longPollTimeout ? options.longPollTimeout * 1000 : undefined;
324880
+ await registerAsyncJobTools(server, resolvedWorkflowPath, toolName, taskStore, longPollMs);
324881
+ }
324882
+ else if (resolvedWorkflowPath) {
323285
324883
  server.tool(toolName, toolDescription, {
323286
324884
  message: exports.FixedWorkflowSchema.shape.message,
323287
324885
  checks: exports.FixedWorkflowSchema.shape.checks,
@@ -323472,9 +325070,15 @@ async function startMcpServer(options = {}) {
323472
325070
  // Determine tool name and description
323473
325071
  const toolName = options.toolName || 'run_workflow';
323474
325072
  const toolDescription = options.toolDescription || exports.RUN_WORKFLOW_DESCRIPTION;
323475
- // Check if we're in fixed workflow mode
323476
- if (resolvedWorkflowPath) {
323477
- // Register the tool without workflow parameter
325073
+ // Register tools based on mode
325074
+ if (options.asyncMode) {
325075
+ const taskStore = await getOrCreateTaskStore(options);
325076
+ const longPollMs = options.longPollTimeout ? options.longPollTimeout * 1000 : undefined;
325077
+ await registerAsyncJobTools(server, resolvedWorkflowPath, toolName, taskStore, longPollMs);
325078
+ console.error(`Visor MCP server started in async mode${resolvedWorkflowPath ? ` with fixed workflow: ${resolvedWorkflowPath}` : ''}`);
325079
+ }
325080
+ else if (resolvedWorkflowPath) {
325081
+ // Fixed workflow mode - tool without workflow parameter
323478
325082
  server.tool(toolName, toolDescription, {
323479
325083
  message: exports.FixedWorkflowSchema.shape.message,
323480
325084
  checks: exports.FixedWorkflowSchema.shape.checks,
@@ -338660,9 +340264,9 @@ class WorkflowCheckProvider extends check_provider_interface_1.CheckProvider {
338660
340264
  }
338661
340265
  // Log final outputs
338662
340266
  const outputKeys = Object.keys(outputs);
338663
- const nullOutputs = outputKeys.filter(k => outputs[k] === null || outputs[k] === undefined);
338664
- if (nullOutputs.length > 0) {
338665
- logger_1.logger.warn(`[WorkflowProvider] Workflow '${workflow.id}' has null/undefined outputs: [${nullOutputs.join(', ')}]. ` +
340267
+ const undefinedOutputs = outputKeys.filter(k => outputs[k] === undefined);
340268
+ if (undefinedOutputs.length > 0) {
340269
+ logger_1.logger.warn(`[WorkflowProvider] Workflow '${workflow.id}' has undefined outputs: [${undefinedOutputs.join(', ')}]. ` +
338666
340270
  `This may indicate value_js expressions are not finding expected data.`);
338667
340271
  }
338668
340272
  // Propagate _rawOutput from any step — this carries DSL output() content
@@ -339781,16 +341385,58 @@ class McpServerRunner {
339781
341385
  'Send a message to the Visor assistant. The assistant will process your message ' +
339782
341386
  'through the configured workflow and return a response. Use this for conversations, ' +
339783
341387
  'questions, task requests, and any interaction with the assistant.';
339784
- mcpServer.tool(toolName, toolDescription, {
339785
- message: zod_1.z.string().describe('The message to send to the assistant.'),
339786
- session_id: zod_1.z
339787
- .string()
339788
- .optional()
339789
- .describe('Optional conversation session ID for maintaining context across messages. ' +
339790
- 'If omitted, a new session is created. Re-use the same session_id for follow-up messages.'),
339791
- }, async (args) => {
339792
- return this.handleMessage(args.message, args.session_id);
339793
- });
341388
+ if (this.options.asyncMode) {
341389
+ // Async job mode: register start_job and get_job instead of blocking tool
341390
+ // Requires a TaskStore — jobs are stored as regular Visor tasks
341391
+ if (!this.taskStore) {
341392
+ const { SqliteTaskStore } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(24711)));
341393
+ this.taskStore = new SqliteTaskStore();
341394
+ await this.taskStore.initialize();
341395
+ }
341396
+ const { JobManager } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(70279)));
341397
+ const longPollMs = this.options.longPollTimeout
341398
+ ? this.options.longPollTimeout * 1000
341399
+ : undefined;
341400
+ const jobManager = new JobManager(this.taskStore, { longPollTimeoutMs: longPollMs });
341401
+ const startJobName = toolName === 'send_message' ? 'start_job' : `start_${toolName}`;
341402
+ const allChecks = Object.keys(this.cfg.checks || {});
341403
+ mcpServer.tool(startJobName, 'Start a long-running job. Returns immediately with a job_id. ' +
341404
+ 'You MUST then call get_job with this job_id. get_job uses long polling (waits up to 59s). ' +
341405
+ 'If done is still false, call get_job again.', {
341406
+ message: zod_1.z.string().describe('The message to send to the assistant.'),
341407
+ session_id: zod_1.z
341408
+ .string()
341409
+ .optional()
341410
+ .describe('Optional conversation session ID for maintaining context across messages. ' +
341411
+ 'If omitted, a new session is created.'),
341412
+ }, async (args) => {
341413
+ const response = jobManager.startJob(async () => this.handleMessage(args.message, args.session_id), {
341414
+ messageText: args.message,
341415
+ workflowId: allChecks.join(','),
341416
+ });
341417
+ return { content: [{ type: 'text', text: JSON.stringify(response, null, 2) }] };
341418
+ });
341419
+ mcpServer.tool('get_job', 'Check the status of a running job using long polling. Waits up to 59 seconds for the job to finish. ' +
341420
+ 'Returns immediately if the job is already done. If done is still false after the wait, call again.', {
341421
+ job_id: zod_1.z.string().describe('The job ID returned by start_job.'),
341422
+ }, async (args) => {
341423
+ const response = await jobManager.getJob(args.job_id);
341424
+ return { content: [{ type: 'text', text: JSON.stringify(response, null, 2) }] };
341425
+ });
341426
+ console.error(`Visor MCP frontend started in async job mode`);
341427
+ }
341428
+ else {
341429
+ mcpServer.tool(toolName, toolDescription, {
341430
+ message: zod_1.z.string().describe('The message to send to the assistant.'),
341431
+ session_id: zod_1.z
341432
+ .string()
341433
+ .optional()
341434
+ .describe('Optional conversation session ID for maintaining context across messages. ' +
341435
+ 'If omitted, a new session is created. Re-use the same session_id for follow-up messages.'),
341436
+ }, async (args) => {
341437
+ return this.handleMessage(args.message, args.session_id);
341438
+ });
341439
+ }
339794
341440
  const { transports } = this;
339795
341441
  const handleRequest = async (req, res) => {
339796
341442
  try {
@@ -340814,6 +342460,11 @@ async function createRunner(name, engine, config, options) {
340814
342460
  tlsKey: mcpAny.tls_key || process.env.VISOR_MCP_TLS_KEY,
340815
342461
  toolName: mcpAny.tool_name || process.env.VISOR_MCP_TOOL_NAME,
340816
342462
  toolDescription: mcpAny.tool_description || process.env.VISOR_MCP_TOOL_DESCRIPTION,
342463
+ asyncMode: options.mcpAsync || mcpAny.async_mode || process.env.VISOR_MCP_ASYNC === 'true',
342464
+ longPollTimeout: mcpAny.long_poll_timeout ||
342465
+ (process.env.VISOR_MCP_POLL_TIMEOUT
342466
+ ? parseInt(process.env.VISOR_MCP_POLL_TIMEOUT)
342467
+ : undefined),
340817
342468
  });
340818
342469
  }
340819
342470
  default:
@@ -347987,6 +349638,26 @@ class SlackClient {
347987
349638
  };
347988
349639
  }
347989
349640
  },
349641
+ delete: async ({ channel, ts }) => {
349642
+ try {
349643
+ const resp = await this.api('chat.delete', { channel, ts });
349644
+ if (!resp || resp.ok !== true) {
349645
+ const err = (resp && resp.error) || 'unknown_error';
349646
+ console.warn(`Slack chat.delete failed (non-fatal): error=${err} channel=${channel} ts=${ts}`);
349647
+ return { ok: false, ts, error: err, data: resp };
349648
+ }
349649
+ return { ok: true, ts: resp.ts || ts, error: undefined, data: resp };
349650
+ }
349651
+ catch (e) {
349652
+ console.warn(`Slack chat.delete threw (non-fatal): channel=${channel} ts=${ts} error=${e instanceof Error ? e.message : String(e)}`);
349653
+ return {
349654
+ ok: false,
349655
+ ts,
349656
+ error: e instanceof Error ? e.message : String(e),
349657
+ data: undefined,
349658
+ };
349659
+ }
349660
+ },
347990
349661
  };
347991
349662
  async getBotUserId() {
347992
349663
  const resp = await this.api('auth.test', {});
@@ -350110,6 +351781,13 @@ class SlackSocketRunner {
350110
351781
  });
350111
351782
  if (this.taskStore) {
350112
351783
  const { trackExecution } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(7628)));
351784
+ const { isFrontendLiveUpdatesEnabled } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(5893)));
351785
+ const { SlackTaskLiveUpdateSink } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(90535)));
351786
+ const slackTelemetryCfg = cfgForRun?.slack?.telemetry ?? cfgForRun?.telemetry;
351787
+ const includeTraceId = slackTelemetryCfg === true ||
351788
+ (slackTelemetryCfg &&
351789
+ typeof slackTelemetryCfg === 'object' &&
351790
+ slackTelemetryCfg.enabled === true);
350113
351791
  await trackExecution({
350114
351792
  taskStore: this.taskStore,
350115
351793
  source: 'slack',
@@ -350122,6 +351800,14 @@ class SlackSocketRunner {
350122
351800
  slack_user: userId,
350123
351801
  slack_trigger_text: String(ev.text || '').slice(0, 500),
350124
351802
  },
351803
+ liveUpdates: {
351804
+ config: cfgForRun.task_live_updates,
351805
+ includeTraceId,
351806
+ sink: this.client &&
351807
+ isFrontendLiveUpdatesEnabled(cfgForRun.task_live_updates, 'slack')
351808
+ ? new SlackTaskLiveUpdateSink(this.client, channelId, threadTs)
351809
+ : undefined,
351810
+ },
350125
351811
  }, execFn);
350126
351812
  }
350127
351813
  else {
@@ -350322,6 +352008,13 @@ class SlackSocketRunner {
350322
352008
  });
350323
352009
  if (this.taskStore) {
350324
352010
  const { trackExecution } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(7628)));
352011
+ const { isFrontendLiveUpdatesEnabled } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(5893)));
352012
+ const { SlackTaskLiveUpdateSink } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(90535)));
352013
+ const slackTelemetryCfg = cfgForRun?.slack?.telemetry ?? cfgForRun?.telemetry;
352014
+ const includeTraceId = slackTelemetryCfg === true ||
352015
+ (slackTelemetryCfg &&
352016
+ typeof slackTelemetryCfg === 'object' &&
352017
+ slackTelemetryCfg.enabled === true);
350325
352018
  await trackExecution({
350326
352019
  taskStore: this.taskStore,
350327
352020
  source: 'slack',
@@ -350335,6 +352028,14 @@ class SlackSocketRunner {
350335
352028
  slack_trigger_text: String(ev.text || '').slice(0, 500),
350336
352029
  trigger_id: id,
350337
352030
  },
352031
+ liveUpdates: {
352032
+ config: cfgForRun.task_live_updates,
352033
+ includeTraceId,
352034
+ sink: this.client &&
352035
+ isFrontendLiveUpdatesEnabled(cfgForRun.task_live_updates, 'slack')
352036
+ ? new SlackTaskLiveUpdateSink(this.client, channel, threadTs || ts)
352037
+ : undefined,
352038
+ },
350338
352039
  }, triggerExecFn);
350339
352040
  }
350340
352041
  else {
@@ -361373,6 +363074,44 @@ class TeamsClient {
361373
363074
  }
361374
363075
  return { ok: true, activityId: lastActivityId };
361375
363076
  }
363077
+ /**
363078
+ * Update an existing bot message.
363079
+ * For safety, only supports single-activity payloads. Oversized content returns msg_too_long.
363080
+ */
363081
+ async updateMessage(opts) {
363082
+ const chunks = (0, markdown_1.chunkText)(opts.text, 28000);
363083
+ if (chunks.length > 1) {
363084
+ return { ok: false, error: 'msg_too_long' };
363085
+ }
363086
+ try {
363087
+ await this.adapter.continueConversationAsync(this.appId, opts.conversationReference, async (turnContext) => {
363088
+ const activity = botbuilder_1.MessageFactory.text(chunks[0] || '');
363089
+ activity.id = opts.activityId;
363090
+ await turnContext.updateActivity(activity);
363091
+ });
363092
+ return { ok: true, activityId: opts.activityId };
363093
+ }
363094
+ catch (err) {
363095
+ return {
363096
+ ok: false,
363097
+ error: err instanceof Error ? err.message : String(err),
363098
+ };
363099
+ }
363100
+ }
363101
+ /**
363102
+ * Delete a previously sent bot message.
363103
+ */
363104
+ async deleteMessage(opts) {
363105
+ try {
363106
+ await this.adapter.continueConversationAsync(this.appId, opts.conversationReference, async (turnContext) => {
363107
+ await turnContext.deleteActivity(opts.activityId);
363108
+ });
363109
+ return true;
363110
+ }
363111
+ catch {
363112
+ return false;
363113
+ }
363114
+ }
361376
363115
  }
361377
363116
  exports.TeamsClient = TeamsClient;
361378
363117
 
@@ -361726,6 +363465,8 @@ class TeamsWebhookRunner {
361726
363465
  });
361727
363466
  if (this.taskStore) {
361728
363467
  const { trackExecution } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(7628)));
363468
+ const { isFrontendLiveUpdatesEnabled } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(5893)));
363469
+ const { TeamsTaskLiveUpdateSink } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(82309)));
361729
363470
  await trackExecution({
361730
363471
  taskStore: this.taskStore,
361731
363472
  source: 'teams',
@@ -361737,6 +363478,12 @@ class TeamsWebhookRunner {
361737
363478
  teams_conversation_type: msg.conversationType,
361738
363479
  teams_from: msg.from.id,
361739
363480
  },
363481
+ liveUpdates: {
363482
+ config: cfgForRun.task_live_updates,
363483
+ sink: isFrontendLiveUpdatesEnabled(cfgForRun.task_live_updates, 'teams')
363484
+ ? new TeamsTaskLiveUpdateSink(this.client, msg.conversationReference, msg.activityId)
363485
+ : undefined,
363486
+ },
361740
363487
  }, execFn);
361741
363488
  }
361742
363489
  else {
@@ -361994,6 +363741,40 @@ class TelegramClient {
361994
363741
  return { ok: false, error: errMsg };
361995
363742
  }
361996
363743
  }
363744
+ /**
363745
+ * Edit an existing text message.
363746
+ * Returns ok=false if Telegram rejects the edit (e.g. message too old or unchanged).
363747
+ */
363748
+ async editMessageText(opts) {
363749
+ try {
363750
+ const params = {
363751
+ link_preview_options: { is_disabled: true },
363752
+ };
363753
+ if (opts.parse_mode)
363754
+ params.parse_mode = opts.parse_mode;
363755
+ const msg = await this.bot.api.editMessageText(opts.chat_id, opts.message_id, opts.text, params);
363756
+ const messageId = msg?.message_id || opts.message_id;
363757
+ return { ok: true, message_id: messageId };
363758
+ }
363759
+ catch (err) {
363760
+ const errMsg = err?.description || err?.message || String(err);
363761
+ console.warn(`Telegram editMessageText failed (non-fatal): ${errMsg}`);
363762
+ return { ok: false, error: errMsg };
363763
+ }
363764
+ }
363765
+ /**
363766
+ * Delete a message.
363767
+ */
363768
+ async deleteMessage(opts) {
363769
+ try {
363770
+ await this.bot.api.deleteMessage(opts.chat_id, opts.message_id);
363771
+ return true;
363772
+ }
363773
+ catch (err) {
363774
+ console.warn(`Telegram deleteMessage failed (non-fatal): ${err?.description || err?.message || String(err)}`);
363775
+ return false;
363776
+ }
363777
+ }
361997
363778
  /**
361998
363779
  * Send a document/file.
361999
363780
  */
@@ -362493,6 +364274,8 @@ class TelegramPollingRunner {
362493
364274
  });
362494
364275
  if (this.taskStore) {
362495
364276
  const { trackExecution } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(7628)));
364277
+ const { isFrontendLiveUpdatesEnabled } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(5893)));
364278
+ const { TelegramTaskLiveUpdateSink } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(71872)));
362496
364279
  await trackExecution({
362497
364280
  taskStore: this.taskStore,
362498
364281
  source: 'telegram',
@@ -362504,6 +364287,12 @@ class TelegramPollingRunner {
362504
364287
  telegram_chat_type: msg.chat.type,
362505
364288
  telegram_user: msg.from ? String(msg.from.id) : 'unknown',
362506
364289
  },
364290
+ liveUpdates: {
364291
+ config: cfgForRun.task_live_updates,
364292
+ sink: isFrontendLiveUpdatesEnabled(cfgForRun.task_live_updates, 'telegram')
364293
+ ? new TelegramTaskLiveUpdateSink(this.client, msg.chat.id, msg.message_id, msg.message_thread_id)
364294
+ : undefined,
364295
+ },
362507
364296
  }, execFn);
362508
364297
  }
362509
364298
  else {
@@ -381344,6 +383133,14 @@ module.exports = require("net");
381344
383133
 
381345
383134
  /***/ }),
381346
383135
 
383136
+ /***/ 16698:
383137
+ /***/ ((module) => {
383138
+
383139
+ "use strict";
383140
+ module.exports = require("node:async_hooks");
383141
+
383142
+ /***/ }),
383143
+
381347
383144
  /***/ 4573:
381348
383145
  /***/ ((module) => {
381349
383146
 
@@ -455959,6 +457756,70 @@ var init_extract = __esm({
455959
457756
  }
455960
457757
  });
455961
457758
 
457759
+ // src/symbols.js
457760
+ async function symbols(options) {
457761
+ if (!options) {
457762
+ throw new Error("Options object is required");
457763
+ }
457764
+ if (!options.files || !Array.isArray(options.files) || options.files.length === 0) {
457765
+ throw new Error("At least one file path is required");
457766
+ }
457767
+ const binaryPath = await getBinaryPath(options.binaryOptions || {});
457768
+ const cwd = await validateCwdPath(options.cwd);
457769
+ const args = ["symbols", "--format", "json"];
457770
+ if (options.allowTests) {
457771
+ args.push("--allow-tests");
457772
+ }
457773
+ for (const file2 of options.files) {
457774
+ args.push(escapeString(file2));
457775
+ }
457776
+ if (process.env.DEBUG === "1") {
457777
+ console.error(`
457778
+ Symbols: files="${options.files.join(", ")}" cwd="${cwd}"`);
457779
+ }
457780
+ return new Promise((resolve9, reject2) => {
457781
+ const childProcess = (0, import_child_process5.spawn)(binaryPath, args, {
457782
+ stdio: ["pipe", "pipe", "pipe"],
457783
+ cwd
457784
+ });
457785
+ let stdout = "";
457786
+ let stderr = "";
457787
+ childProcess.stdout.on("data", (data2) => {
457788
+ stdout += data2.toString();
457789
+ });
457790
+ childProcess.stderr.on("data", (data2) => {
457791
+ stderr += data2.toString();
457792
+ });
457793
+ childProcess.on("close", (code) => {
457794
+ if (stderr && process.env.DEBUG === "1") {
457795
+ console.error(`stderr: ${stderr}`);
457796
+ }
457797
+ if (code !== 0) {
457798
+ reject2(new Error(`Symbols command failed with exit code ${code}: ${stderr}`));
457799
+ return;
457800
+ }
457801
+ try {
457802
+ const result = JSON.parse(stdout);
457803
+ resolve9(result);
457804
+ } catch (error40) {
457805
+ reject2(new Error(`Failed to parse symbols output: ${error40.message}. Output: ${stdout.substring(0, 200)}`));
457806
+ }
457807
+ });
457808
+ childProcess.on("error", (error40) => {
457809
+ reject2(new Error(`Failed to spawn symbols process: ${error40.message}`));
457810
+ });
457811
+ });
457812
+ }
457813
+ var import_child_process5;
457814
+ var init_symbols = __esm({
457815
+ "src/symbols.js"() {
457816
+ "use strict";
457817
+ import_child_process5 = __nccwpck_require__(35317);
457818
+ init_utils();
457819
+ init_path_validation();
457820
+ }
457821
+ });
457822
+
455962
457823
  // src/grep.js
455963
457824
  async function grep(options) {
455964
457825
  if (!options || !options.pattern) {
@@ -456002,14 +457863,14 @@ async function grep(options) {
456002
457863
  throw new Error(`Grep failed: ${errorMessage}`);
456003
457864
  }
456004
457865
  }
456005
- var import_child_process5, import_util5, execFileAsync2, GREP_FLAG_MAP;
457866
+ var import_child_process6, import_util5, execFileAsync2, GREP_FLAG_MAP;
456006
457867
  var init_grep = __esm({
456007
457868
  "src/grep.js"() {
456008
457869
  "use strict";
456009
- import_child_process5 = __nccwpck_require__(35317);
457870
+ import_child_process6 = __nccwpck_require__(35317);
456010
457871
  import_util5 = __nccwpck_require__(39023);
456011
457872
  init_utils();
456012
- execFileAsync2 = (0, import_util5.promisify)(import_child_process5.execFile);
457873
+ execFileAsync2 = (0, import_util5.promisify)(import_child_process6.execFile);
456013
457874
  GREP_FLAG_MAP = {
456014
457875
  ignoreCase: "-i",
456015
457876
  lineNumbers: "-n",
@@ -481417,6 +483278,9 @@ function createTools(configOptions) {
481417
483278
  if (isToolAllowed("extract")) {
481418
483279
  tools2.extractTool = extractTool(configOptions);
481419
483280
  }
483281
+ if (isToolAllowed("symbols")) {
483282
+ tools2.symbolsTool = symbolsTool(configOptions);
483283
+ }
481420
483284
  if (configOptions.enableDelegate && isToolAllowed("delegate")) {
481421
483285
  tools2.delegateTool = delegateTool(configOptions);
481422
483286
  }
@@ -481585,7 +483449,7 @@ function resolveTargetPath(target, cwd) {
481585
483449
  }
481586
483450
  return filePart + suffix;
481587
483451
  }
481588
- var import_path6, searchDelegateSchema, searchSchema, searchAllSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, listFilesSchema, searchFilesSchema, readImageSchema, readMediaSchema, bashSchema, analyzeAllSchema, executePlanSchema, cleanupExecutePlanSchema, searchDescription, searchDelegateDescription, queryDescription, extractDescription, delegateDescription, bashDescription, analyzeAllDescription;
483452
+ var import_path6, searchDelegateSchema, searchSchema, searchAllSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, listFilesSchema, searchFilesSchema, readImageSchema, readMediaSchema, symbolsSchema, bashSchema, analyzeAllSchema, executePlanSchema, cleanupExecutePlanSchema, searchDescription, searchDelegateDescription, queryDescription, extractDescription, delegateDescription, bashDescription, analyzeAllDescription;
481589
483453
  var init_common = __esm({
481590
483454
  "src/tools/common.js"() {
481591
483455
  "use strict";
@@ -481644,6 +483508,9 @@ var init_common = __esm({
481644
483508
  readMediaSchema = external_exports2.object({
481645
483509
  path: external_exports2.string().describe("Path to the media file to read. Supports images (png, jpg, jpeg, webp, bmp, svg) and documents (pdf).")
481646
483510
  });
483511
+ symbolsSchema = external_exports2.object({
483512
+ file: external_exports2.string().describe("Path to the file to list symbols from. Returns a hierarchical tree of functions, classes, structs, constants, etc. with line numbers and nesting (e.g., methods inside classes/impl blocks).")
483513
+ });
481647
483514
  bashSchema = external_exports2.object({
481648
483515
  command: external_exports2.string().describe("The bash command to execute"),
481649
483516
  workingDirectory: external_exports2.string().optional().describe("Directory to execute the command in (optional)"),
@@ -489053,14 +490920,21 @@ function createWrappedTools(baseTools) {
489053
490920
  baseTools.multiEditTool.execute
489054
490921
  );
489055
490922
  }
490923
+ if (baseTools.symbolsTool) {
490924
+ wrappedTools.symbolsToolInstance = wrapToolWithEmitter(
490925
+ baseTools.symbolsTool,
490926
+ "symbols",
490927
+ baseTools.symbolsTool.execute
490928
+ );
490929
+ }
489056
490930
  return wrappedTools;
489057
490931
  }
489058
- var import_child_process6, import_util13, import_crypto3, import_events, import_fs6, import_fs7, import_path8, toolCallEmitter, activeToolExecutions, wrapToolWithEmitter, listFilesTool, searchFilesTool, listFilesToolInstance, searchFilesToolInstance;
490932
+ var import_child_process7, import_util13, import_crypto3, import_events, import_fs6, import_fs7, import_path8, toolCallEmitter, activeToolExecutions, wrapToolWithEmitter, listFilesTool, searchFilesTool, listFilesToolInstance, searchFilesToolInstance;
489059
490933
  var init_probeTool = __esm({
489060
490934
  "src/agent/probeTool.js"() {
489061
490935
  "use strict";
489062
490936
  init_index();
489063
- import_child_process6 = __nccwpck_require__(35317);
490937
+ import_child_process7 = __nccwpck_require__(35317);
489064
490938
  import_util13 = __nccwpck_require__(39023);
489065
490939
  import_crypto3 = __nccwpck_require__(76982);
489066
490940
  import_events = __nccwpck_require__(24434);
@@ -527710,6 +529584,7 @@ CRITICAL - ALWAYS search before answering:
527710
529584
  You must NEVER answer questions about the codebase from memory or general knowledge. ALWAYS use the search and extract tools first to find the actual code, then base your answer ONLY on what you found. Even if you think you know the answer, you MUST verify it against the actual code. Your answers must be grounded in code evidence, not assumptions.
527711
529585
 
527712
529586
  When exploring code:
529587
+ - Use the symbols tool to get a quick overview of a file's structure (functions, classes, constants) before diving into details with extract
527713
529588
  - Provide clear, concise explanations based on user request
527714
529589
  - Find and highlight the most relevant code snippets, if required
527715
529590
  - Trace function calls and data flow through the system \u2014 follow the FULL call chain, not just the entry point
@@ -527740,6 +529615,7 @@ You think like a code explorer \u2014 you understand that codebases have layers:
527740
529615
 
527741
529616
  When searching:
527742
529617
  - Search for the MAIN concept first, then think: "what RELATED subsystems would a real codebase have?"
529618
+ - Use symbols to get a file's table of contents (functions, classes, constants with line numbers) before extracting \u2014 it helps you pick the right symbols to extract
527743
529619
  - Use extract to READ the code you find \u2014 look for function calls, type references, and imports that point to OTHER relevant code
527744
529620
  - If you find middleware, check: are there org-level or tenant-level variants?
527745
529621
  - If you find algorithms, check: are there different storage backends?
@@ -527783,6 +529659,7 @@ If the solution is clear, you can jump to implementation right away. If not, ask
527783
529659
  - Do not add code comments unless the logic is genuinely complex and non-obvious.
527784
529660
 
527785
529661
  # Before Implementation
529662
+ - Use symbols to get a quick overview of file structure (functions, classes, constants with line numbers) before reading or editing files.
527786
529663
  - Read tests first \u2014 find existing test files for the module you're changing. They reveal expected behavior, edge cases, and the project's testing patterns.
527787
529664
  - Read neighboring files \u2014 understand naming conventions, error handling patterns, import style, and existing utilities before creating new ones.
527788
529665
  - Trace the call chain \u2014 follow how the code you're changing is called and what depends on it. Check interfaces, types, and consumers.
@@ -527819,7 +529696,7 @@ Use the right tool:
527819
529696
  1. To MODIFY existing code \u2192 \`edit\` tool (old_string \u2192 new_string, or start_line/end_line)
527820
529697
  2. To CREATE a new file \u2192 \`create\` tool
527821
529698
  3. To CHANGE multiple files at once \u2192 \`multi_edit\` tool
527822
- 4. To READ code \u2192 \`extract\` or \`search\` tools
529699
+ 4. To READ code \u2192 \`extract\` or \`search\` tools. Use \`symbols\` to get a file's table of contents (functions, classes, constants with line numbers) before extracting.
527823
529700
  5. If \`edit\` fails with "file has not been read yet" \u2192 use \`extract\` with the EXACT same file path you will pass to \`edit\`. Relative vs absolute path mismatch causes this error. Use the same path format consistently. If it still fails, use bash \`cat\` to read the file, then use \`create\` to write the entire modified file. Do NOT fall back to sed.
527824
529701
 
527825
529702
  Bash is fine for: formatters (gofmt, prettier, black), build/test/lint commands, git operations, and read-only file inspection (cat, head, tail). sed/awk should ONLY be used for trivial non-code tasks (e.g., config file tweaks) where the replacement is a simple literal string swap.
@@ -527831,6 +529708,8 @@ Bash is fine for: formatters (gofmt, prettier, black), build/test/lint commands,
527831
529708
  - Never expose secrets, API keys, or credentials in generated code. Never log sensitive information.
527832
529709
  - Do not surprise the user with unrequested changes. Do what was asked, including reasonable follow-up actions, but do not refactor surrounding code or add features that were not requested.
527833
529710
  - When editing files, keep edits focused and minimal. For changes spanning more than a few lines, prefer line-targeted editing (start_line/end_line) over text replacement (old_string) \u2014 it constrains scope and prevents accidental removal of adjacent content. Never include unrelated sections in an edit operation.
529711
+ - In line-targeted mode, omitting position means replace/update the addressed lines. To insert code near an existing line, you MUST use position="before" or position="after". Never express insertion as end_line < start_line.
529712
+ - To remove lines with line-targeted mode, target the exact start_line/end_line range and set new_string to the empty string "".
527834
529713
  - After every significant change, verify the project still builds and passes linting. Do not wait until the end to discover breakage.
527835
529714
 
527836
529715
  # Writing Tests
@@ -530273,6 +532152,7 @@ var require_stringify = __commonJS({
530273
532152
  nullStr: "null",
530274
532153
  simpleKeys: false,
530275
532154
  singleQuote: null,
532155
+ trailingComma: false,
530276
532156
  trueStr: "true",
530277
532157
  verifyAliasOrder: true
530278
532158
  }, doc.schema.toStringOptions, options);
@@ -530790,12 +532670,19 @@ ${indent}${line}` : "\n";
530790
532670
  if (comment)
530791
532671
  reqNewline = true;
530792
532672
  let str = stringify.stringify(item, itemCtx, () => comment = null);
530793
- if (i < items.length - 1)
532673
+ reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n"));
532674
+ if (i < items.length - 1) {
530794
532675
  str += ",";
532676
+ } else if (ctx.options.trailingComma) {
532677
+ if (ctx.options.lineWidth > 0) {
532678
+ reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth);
532679
+ }
532680
+ if (reqNewline) {
532681
+ str += ",";
532682
+ }
532683
+ }
530795
532684
  if (comment)
530796
532685
  str += stringifyComment.lineComment(str, itemIndent, commentString(comment));
530797
- if (!reqNewline && (lines.length > linesAtValue || str.includes("\n")))
530798
- reqNewline = true;
530799
532686
  lines.push(str);
530800
532687
  linesAtValue = lines.length;
530801
532688
  }
@@ -533809,17 +535696,22 @@ var require_compose_node = __commonJS({
533809
535696
  case "block-map":
533810
535697
  case "block-seq":
533811
535698
  case "flow-collection":
533812
- node = composeCollection.composeCollection(CN, ctx, token, props, onError);
533813
- if (anchor)
533814
- node.anchor = anchor.source.substring(1);
535699
+ try {
535700
+ node = composeCollection.composeCollection(CN, ctx, token, props, onError);
535701
+ if (anchor)
535702
+ node.anchor = anchor.source.substring(1);
535703
+ } catch (error40) {
535704
+ const message = error40 instanceof Error ? error40.message : String(error40);
535705
+ onError(token, "RESOURCE_EXHAUSTION", message);
535706
+ }
533815
535707
  break;
533816
535708
  default: {
533817
535709
  const message = token.type === "error" ? token.message : `Unsupported token (type: ${token.type})`;
533818
535710
  onError(token, "UNEXPECTED_TOKEN", message);
533819
- node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError);
533820
535711
  isSrcToken = false;
533821
535712
  }
533822
535713
  }
535714
+ node ?? (node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError));
533823
535715
  if (anchor && node.anchor === "")
533824
535716
  onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string");
533825
535717
  if (atKey && ctx.options.stringKeys && (!identity2.isScalar(node) || typeof node.value !== "string" || node.tag && node.tag !== "tag:yaml.org,2002:str")) {
@@ -548808,7 +550700,7 @@ async function executeBashCommand(command, options = {}) {
548808
550700
  [cmd, ...cmdArgs] = args;
548809
550701
  useShell = false;
548810
550702
  }
548811
- const child = (0, import_child_process7.spawn)(cmd, cmdArgs, {
550703
+ const child = (0, import_child_process8.spawn)(cmd, cmdArgs, {
548812
550704
  cwd,
548813
550705
  env: processEnv,
548814
550706
  stdio: ["ignore", "pipe", "pipe"],
@@ -549004,11 +550896,11 @@ function validateExecutionOptions(options = {}) {
549004
550896
  warnings
549005
550897
  };
549006
550898
  }
549007
- var import_child_process7, import_path13, import_fs10;
550899
+ var import_child_process8, import_path13, import_fs10;
549008
550900
  var init_bashExecutor = __esm({
549009
550901
  "src/agent/bashExecutor.js"() {
549010
550902
  "use strict";
549011
- import_child_process7 = __nccwpck_require__(35317);
550903
+ import_child_process8 = __nccwpck_require__(35317);
549012
550904
  import_path13 = __nccwpck_require__(16928);
549013
550905
  import_fs10 = __nccwpck_require__(79896);
549014
550906
  init_bashCommandUtils();
@@ -550534,7 +552426,7 @@ ${opts.schema}`;
550534
552426
  }
550535
552427
  }
550536
552428
  const toolCollector = [];
550537
- const proc2 = (0, import_child_process8.spawn)("sh", ["-c", shellCmd], {
552429
+ const proc2 = (0, import_child_process9.spawn)("sh", ["-c", shellCmd], {
550538
552430
  env: { ...process.env, FORCE_COLOR: "0" },
550539
552431
  stdio: ["ignore", "pipe", "pipe"]
550540
552432
  // Ignore stdin since echo handles it
@@ -550889,11 +552781,11 @@ function combinePrompts(systemPrompt, customPrompt, agent) {
550889
552781
  }
550890
552782
  return systemPrompt || "";
550891
552783
  }
550892
- var import_child_process8, import_crypto6, import_promises5, import_path15, import_os5, import_events3;
552784
+ var import_child_process9, import_crypto6, import_promises5, import_path15, import_os5, import_events3;
550893
552785
  var init_enhanced_claude_code = __esm({
550894
552786
  "src/agent/engines/enhanced-claude-code.js"() {
550895
552787
  "use strict";
550896
- import_child_process8 = __nccwpck_require__(35317);
552788
+ import_child_process9 = __nccwpck_require__(35317);
550897
552789
  import_crypto6 = __nccwpck_require__(76982);
550898
552790
  import_promises5 = __toESM(__nccwpck_require__(91943), 1);
550899
552791
  import_path15 = __toESM(__nccwpck_require__(16928), 1);
@@ -550935,7 +552827,7 @@ async function createCodexEngine(options = {}) {
550935
552827
  if (debug) {
550936
552828
  console.log("[DEBUG] Starting Codex MCP server...");
550937
552829
  }
550938
- const codexProcess = (0, import_child_process9.spawn)("codex", ["mcp-server"], {
552830
+ const codexProcess = (0, import_child_process10.spawn)("codex", ["mcp-server"], {
550939
552831
  stdio: ["pipe", "pipe", "pipe"]
550940
552832
  });
550941
552833
  let requestId = 0;
@@ -551167,11 +553059,11 @@ function combinePrompts2(systemPrompt, customPrompt, agent) {
551167
553059
  }
551168
553060
  return systemPrompt || "";
551169
553061
  }
551170
- var import_child_process9, import_crypto7, import_readline;
553062
+ var import_child_process10, import_crypto7, import_readline;
551171
553063
  var init_codex = __esm({
551172
553064
  "src/agent/engines/codex.js"() {
551173
553065
  "use strict";
551174
- import_child_process9 = __nccwpck_require__(35317);
553066
+ import_child_process10 = __nccwpck_require__(35317);
551175
553067
  import_crypto7 = __nccwpck_require__(76982);
551176
553068
  import_readline = __nccwpck_require__(23785);
551177
553069
  init_built_in_server();
@@ -551937,6 +553829,9 @@ var init_ProbeAgent = __esm({
551937
553829
  if (isToolAllowed("searchFiles")) {
551938
553830
  this.toolImplementations.searchFiles = searchFilesToolInstance;
551939
553831
  }
553832
+ if (wrappedTools.symbolsToolInstance && isToolAllowed("symbols")) {
553833
+ this.toolImplementations.symbols = wrappedTools.symbolsToolInstance;
553834
+ }
551940
553835
  if (this.enableSkills) {
551941
553836
  const registry2 = this._getSkillsRegistry();
551942
553837
  const { listSkillsToolInstance, useSkillToolInstance } = createSkillToolInstances({
@@ -552968,6 +554863,10 @@ var init_ProbeAgent = __esm({
552968
554863
  schema: searchFilesSchema,
552969
554864
  description: "Find files matching a glob pattern with recursive search capability."
552970
554865
  },
554866
+ symbols: {
554867
+ schema: symbolsSchema,
554868
+ description: "List all symbols (functions, classes, structs, constants, etc.) in a file. Returns a hierarchical tree with line numbers \u2014 like a table of contents for code."
554869
+ },
552971
554870
  readMedia: {
552972
554871
  schema: readMediaSchema,
552973
554872
  description: "Read and load a media file (image or PDF document) for AI analysis. Supports: png, jpg, jpeg, webp, bmp, svg, pdf."
@@ -553623,7 +555522,8 @@ ${this.architectureContext.content}
553623
555522
  ${searchToolDesc1}
553624
555523
  - extract: Extract specific code sections with context
553625
555524
  - listFiles: Browse directory contents
553626
- - searchFiles: Find files by name patterns`;
555525
+ - searchFiles: Find files by name patterns
555526
+ - symbols: List all symbols in a file (functions, classes, constants, etc.) with line numbers`;
553627
555527
  if (this.enableBash) {
553628
555528
  systemPrompt += `
553629
555529
  - bash: Execute bash commands for system operations (building, running tests, git, etc.). NEVER use bash for code exploration (no grep, cat, find, head, tail) \u2014 always use search and extract tools instead, they are faster and more accurate.`;
@@ -553678,7 +555578,8 @@ Workspace: ${this.allowedFolders.join(", ")}`;
553678
555578
  ${searchToolDesc2}
553679
555579
  - extract: Extract specific code sections with context
553680
555580
  - listFiles: Browse directory contents
553681
- - searchFiles: Find files by name patterns`;
555581
+ - searchFiles: Find files by name patterns
555582
+ - symbols: List all symbols in a file (functions, classes, constants, etc.) with line numbers`;
553682
555583
  if (this.enableBash) {
553683
555584
  systemPrompt += `
553684
555585
  - bash: Execute bash commands for system operations (building, running tests, git, etc.). NEVER use bash for code exploration (no grep, cat, find, head, tail) \u2014 always use search and extract tools instead, they are faster and more accurate.`;
@@ -553752,6 +555653,8 @@ Follow these instructions carefully:
553752
555653
  7. When modifying files, choose the appropriate tool:
553753
555654
  - Use 'edit' for all code modifications:
553754
555655
  * PREFERRED: Use start_line (and optionally end_line) for line-targeted editing \u2014 this is the safest and most precise approach.${this.hashLines ? ' Use the line:hash references from extract/search output (e.g. "42:ab") for integrity verification.' : ""} Always use extract first to see line numbers${this.hashLines ? " and hashes" : ""}, then edit by line reference.
555656
+ * In line-targeted mode, omitting position means replace/update the addressed lines. To insert new code near a line, you MUST set position="before" or position="after". Never express insertion as end_line < start_line.
555657
+ * To delete lines in line-targeted mode, target the exact start_line/end_line range and set new_string to the empty string "".
553755
555658
  * For editing inside large functions: first use extract with the symbol target (e.g. "file.js#myFunction") to see the function with line numbers${this.hashLines ? " and hashes" : ""}, then use start_line/end_line to surgically edit specific lines within it.
553756
555659
  * For rewriting entire functions/classes/methods, use the symbol parameter instead (no exact text matching needed).
553757
555660
  * FALLBACK ONLY: Use old_string + new_string for simple single-line changes where the text is unique. Copy old_string verbatim from the file. Keep old_string as small as possible.
@@ -557123,7 +559026,7 @@ The "searches" field helps the caller understand what was attempted.
557123
559026
  - Deduplicate files across groups.
557124
559027
  </output-rules>`;
557125
559028
  }
557126
- var import_ai5, import_fs12, CODE_SEARCH_SCHEMA, searchTool, queryTool, extractTool, delegateTool, analyzeAllTool;
559029
+ var import_ai5, import_fs12, CODE_SEARCH_SCHEMA, searchTool, queryTool, extractTool, delegateTool, analyzeAllTool, symbolsTool;
557127
559030
  var init_vercel = __esm({
557128
559031
  "src/tools/vercel.js"() {
557129
559032
  "use strict";
@@ -557131,6 +559034,7 @@ var init_vercel = __esm({
557131
559034
  init_search();
557132
559035
  init_query();
557133
559036
  init_extract();
559037
+ init_symbols();
557134
559038
  init_delegate();
557135
559039
  init_analyzeAll();
557136
559040
  init_common();
@@ -557853,6 +559757,36 @@ Do NOT search for analogies or loosely related concepts. If the feature does not
557853
559757
  }
557854
559758
  });
557855
559759
  };
559760
+ symbolsTool = (options = {}) => {
559761
+ return (0, import_ai5.tool)({
559762
+ name: "symbols",
559763
+ description: "List all symbols (functions, classes, structs, constants, etc.) in a file. Returns a hierarchical tree with line numbers \u2014 like a table of contents for code files.",
559764
+ inputSchema: symbolsSchema,
559765
+ execute: async ({ file: file2 }) => {
559766
+ try {
559767
+ let filePath = file2;
559768
+ if (options.cwd) {
559769
+ const resolvedPaths = parseAndResolvePaths(file2, options.cwd);
559770
+ if (resolvedPaths.length > 0) {
559771
+ filePath = resolvedPaths[0];
559772
+ }
559773
+ }
559774
+ const result = await symbols({
559775
+ files: [filePath],
559776
+ cwd: options.cwd,
559777
+ binaryOptions: options.binaryOptions
559778
+ });
559779
+ if (result && result.length > 0) {
559780
+ return JSON.stringify(result[0], null, 2);
559781
+ }
559782
+ return JSON.stringify({ file: file2, symbols: [] }, null, 2);
559783
+ } catch (error40) {
559784
+ console.error("Error executing symbols:", error40);
559785
+ return formatErrorForAI(error40);
559786
+ }
559787
+ }
559788
+ });
559789
+ };
557856
559790
  }
557857
559791
  });
557858
559792
 
@@ -558253,10 +560187,11 @@ async function handleLineEdit({ resolvedPath, file_path, start_line, end_line, n
558253
560187
  const startLine = startRef.line;
558254
560188
  const endLine = endRef ? endRef.line : startLine;
558255
560189
  if (endLine < startLine) {
558256
- return `Error editing file: end_line (${endLine}) must be >= start_line (${startLine}).`;
560190
+ const insertionHint = endLine === startLine - 1 ? ` This looks like an insertion between lines ${endLine} and ${startLine}. To insert there without replacing existing code, use start_line="${startLine}" with position="before" or start_line="${endLine}" with position="after".` : ' To insert new code, choose a single anchor line in start_line and set position to "before" or "after" instead of using an inverted range.';
560191
+ return `Error editing file: end_line (${endLine}) must be >= start_line (${startLine}). Omitting position means replace/update the addressed lines, not insert.${insertionHint} To delete lines, use new_string as the empty string "" with a valid non-inverted range.`;
558257
560192
  }
558258
560193
  if (position !== void 0 && position !== null && position !== "before" && position !== "after") {
558259
- return 'Error editing file: Invalid position - must be "before" or "after". Use position="before" to insert before the line, or position="after" to insert after it.';
560194
+ return 'Error editing file: Invalid position - must be "before" or "after". Use position="before" to insert before start_line, position="after" to insert after start_line, or omit position to replace/update the addressed lines.';
558260
560195
  }
558261
560196
  const content = await import_fs13.promises.readFile(resolvedPath, "utf-8");
558262
560197
  const fileLines = content.split("\n");
@@ -558313,7 +560248,7 @@ async function handleLineEdit({ resolvedPath, file_path, start_line, end_line, n
558313
560248
  return buildLineEditResponse(file_path, startLine, endLine, newLines.length, fileLines, startLine - 1, action, modifications);
558314
560249
  }
558315
560250
  }
558316
- var import_ai6, import_fs13, import_path17, import_fs14, editTool, createTool, multiEditTool, editSchema, createSchema, multiEditSchema, editDescription, createDescription, multiEditDescription, editToolDefinition, createToolDefinition, multiEditToolDefinition;
560251
+ var import_ai6, import_fs13, import_path17, import_fs14, lineTargetedModeGuidance, editTool, createTool, multiEditTool, editSchema, createSchema, multiEditSchema, editDescription, createDescription, multiEditDescription, editToolDefinition, createToolDefinition, multiEditToolDefinition;
558317
560252
  var init_edit = __esm({
558318
560253
  "src/tools/edit.js"() {
558319
560254
  "use strict";
@@ -558326,27 +560261,35 @@ var init_edit = __esm({
558326
560261
  init_symbolEdit();
558327
560262
  init_hashline();
558328
560263
  init_lineEditHeuristics();
560264
+ lineTargetedModeGuidance = `Line-targeted mode has three explicit behaviors:
560265
+ - Replace/update lines: provide start_line and optionally end_line, and omit position
560266
+ - Insert near a line: provide start_line and set position to "before" or "after"
560267
+ - Delete lines: provide start_line/end_line for the exact range and set new_string to empty string ""
560268
+
560269
+ Inverted ranges (end_line < start_line) are always invalid and are never treated as insertion.`;
558329
560270
  editTool = (options = {}) => {
558330
560271
  const { debug, allowedFolders, cwd, workspaceRoot } = parseFileToolOptions(options);
558331
560272
  return (0, import_ai6.tool)({
558332
560273
  name: "edit",
558333
- description: `Edit files using text replacement, AST-aware symbol operations, or line-targeted editing.
560274
+ description: `Edit files using text replacement, AST-aware symbol operations, or line-targeted line replacement/insertion/deletion.
558334
560275
 
558335
560276
  Modes:
558336
560277
  1. Text edit: Provide old_string + new_string to find and replace text (with fuzzy matching fallback)
558337
560278
  2. Symbol replace: Provide symbol + new_string to replace an entire function/class/method by name
558338
560279
  3. Symbol insert: Provide symbol + new_string + position to insert code before/after a symbol
558339
- 4. Line-targeted edit: Provide start_line + new_string to edit by line number (from extract/search output)
560280
+ 4. Line-targeted edit: Provide start_line + new_string to replace/update, insert, or delete lines by line number (from extract/search output)
560281
+
560282
+ ${lineTargetedModeGuidance}
558340
560283
 
558341
560284
  Parameters:
558342
560285
  - file_path: Path to the file to edit (absolute or relative)
558343
- - new_string: Replacement text or new code content
560286
+ - new_string: Replacement text or new code content. Use empty string "" to delete the targeted line range.
558344
560287
  - old_string: (optional) Text to find and replace. If omitted, symbol or start_line must be provided.
558345
560288
  - replace_all: (optional) Replace all occurrences (text mode only)
558346
560289
  - symbol: (optional) Symbol name for AST-aware editing (e.g. "myFunction", "MyClass.myMethod")
558347
- - position: (optional) "before" or "after" \u2014 insert code near a symbol or line instead of replacing it
558348
- - start_line: (optional) Line reference (e.g. "42" or "42:ab") for line-targeted editing
558349
- - end_line: (optional) End of line range, inclusive (e.g. "55" or "55:cd")`,
560290
+ - position: (optional) "before" or "after" \u2014 insert code near a symbol or line instead of replacing it. Omit position to replace/update.
560291
+ - start_line: (optional) Line reference (e.g. "42" or "42:ab") for line-targeted editing. With position, this is the insertion anchor. Without position, this is the first line to replace/update/delete.
560292
+ - end_line: (optional) End of line range, inclusive (e.g. "55" or "55:cd"). Must be >= start_line. Omit for single-line updates. Do not use inverted ranges for insertion.`,
558350
560293
  inputSchema: {
558351
560294
  type: "object",
558352
560295
  properties: {
@@ -558360,7 +560303,7 @@ Parameters:
558360
560303
  },
558361
560304
  new_string: {
558362
560305
  type: "string",
558363
- description: "Replacement text or new code content"
560306
+ description: 'Replacement text or new code content. Use empty string "" to delete the targeted line range.'
558364
560307
  },
558365
560308
  replace_all: {
558366
560309
  type: "boolean",
@@ -558374,15 +560317,15 @@ Parameters:
558374
560317
  position: {
558375
560318
  type: "string",
558376
560319
  enum: ["before", "after"],
558377
- description: "Insert before/after symbol or line (requires symbol or start_line, omit to replace)"
560320
+ description: "Insert before/after the target symbol or start_line anchor. Omit position to replace/update the addressed symbol or line range."
558378
560321
  },
558379
560322
  start_line: {
558380
560323
  type: "string",
558381
- description: 'Line reference for line-targeted editing (e.g. "42" or "42:ab" with hash)'
560324
+ description: 'Line reference for line-targeted editing (e.g. "42" or "42:ab" with hash). With position, this is the insertion anchor. Without position, this is the first line to replace/update/delete.'
558382
560325
  },
558383
560326
  end_line: {
558384
560327
  type: "string",
558385
- description: 'End of line range, inclusive (e.g. "55" or "55:cd"). Defaults to start_line.'
560328
+ description: 'Inclusive end of the line range to replace/delete (e.g. "55" or "55:cd"). Must be >= start_line. Defaults to start_line.'
558386
560329
  }
558387
560330
  },
558388
560331
  required: ["file_path", "new_string"]
@@ -558633,7 +560576,7 @@ Important:
558633
560576
  },
558634
560577
  new_string: {
558635
560578
  type: "string",
558636
- description: "Replacement text or new code content"
560579
+ description: 'Replacement text or new code content. Use empty string "" to delete the targeted line range.'
558637
560580
  },
558638
560581
  replace_all: {
558639
560582
  type: "boolean",
@@ -558646,15 +560589,15 @@ Important:
558646
560589
  position: {
558647
560590
  type: "string",
558648
560591
  enum: ["before", "after"],
558649
- description: "Insert before/after symbol or line (requires symbol or start_line, omit to replace)"
560592
+ description: "Insert before/after the target symbol or start_line anchor. Omit position to replace/update the addressed symbol or line range."
558650
560593
  },
558651
560594
  start_line: {
558652
560595
  type: "string",
558653
- description: 'Line reference for line-targeted editing (e.g. "42" or "42:ab" with hash)'
560596
+ description: 'Line reference for line-targeted editing (e.g. "42" or "42:ab" with hash). With position, this is the insertion anchor. Without position, this is the first line to replace/update/delete.'
558654
560597
  },
558655
560598
  end_line: {
558656
560599
  type: "string",
558657
- description: 'End of line range, inclusive (e.g. "55" or "55:cd"). Defaults to start_line.'
560600
+ description: 'Inclusive end of the line range to replace/delete (e.g. "55" or "55:cd"). Must be >= start_line. Defaults to start_line.'
558658
560601
  }
558659
560602
  },
558660
560603
  required: ["file_path", "new_string"]
@@ -558687,7 +560630,7 @@ Important:
558687
560630
  },
558688
560631
  required: ["edits"]
558689
560632
  };
558690
- editDescription = "Edit files using text replacement, AST-aware symbol operations, or line-targeted editing. Supports fuzzy matching for text edits and optional hash-based integrity verification for line edits.";
560633
+ editDescription = "Edit files using text replacement, AST-aware symbol operations, or line-targeted replacement/insertion/deletion. Supports fuzzy matching for text edits and optional hash-based integrity verification for line edits.";
558691
560634
  createDescription = "Create new files with specified content. Will create parent directories if needed.";
558692
560635
  multiEditDescription = "Apply multiple file edits in a single tool call. Accepts a JSON array of edit operations, each supporting the same modes as the edit tool.";
558693
560636
  editToolDefinition = `
@@ -558704,15 +560647,21 @@ Four editing modes \u2014 choose based on the scope of your change:
558704
560647
 
558705
560648
  4. **Line-targeted edit** (start_line + new_string): For precise edits using line numbers from extract/search output. Use start_line with a line number (e.g. "42") or line:hash (e.g. "42:ab") for integrity verification. Add end_line for multi-line ranges. Use position="before" or "after" to insert instead of replace.
558706
560649
 
560650
+ How line-targeted mode behaves:
560651
+ - Replace/update lines: provide start_line and optionally end_line, and omit position
560652
+ - Insert near a line: provide start_line and set position to "before" or "after"
560653
+ - Delete lines: provide start_line/end_line for the exact range and set new_string to empty string ""
560654
+ - Inverted ranges are invalid: end_line must be >= start_line and is never treated as insertion
560655
+
558707
560656
  Parameters:
558708
560657
  - file_path: (required) Path to the file to edit
558709
- - new_string: (required) Replacement text or new code content
560658
+ - new_string: (required) Replacement text or new code content. Use empty string "" to delete the targeted line range.
558710
560659
  - old_string: (optional) Text to find and replace \u2014 copy verbatim from the file, do not paraphrase or reformat
558711
560660
  - replace_all: (optional, default: false) Replace all occurrences of old_string (text mode only)
558712
560661
  - symbol: (optional) Name of a code symbol (e.g. "myFunction", "MyClass.myMethod") \u2014 must match a function, class, or method definition
558713
- - position: (optional) "before" or "after" \u2014 insert new_string near the symbol or line instead of replacing it
558714
- - start_line: (optional) Line reference for line-targeted editing (e.g. "42" or "42:ab")
558715
- - end_line: (optional) End of line range, inclusive (e.g. "55" or "55:cd"). Defaults to start_line.
560662
+ - position: (optional) "before" or "after" \u2014 insert new_string near the symbol or line instead of replacing it. Omit position to replace/update.
560663
+ - start_line: (optional) Line reference for line-targeted editing (e.g. "42" or "42:ab"). With position, this is the insertion anchor. Without position, this is the first line to replace/update/delete.
560664
+ - end_line: (optional) End of line range, inclusive (e.g. "55" or "55:cd"). Defaults to start_line. Must be >= start_line. Use it for replace/update/delete ranges, not insertion.
558716
560665
 
558717
560666
  Mode selection rules (priority order):
558718
560667
  - If symbol is provided, symbol mode is used (old_string and start_line are ignored)
@@ -558724,12 +560673,15 @@ When to use each mode:
558724
560673
  - Small edits (a line or a few lines): use text mode with old_string
558725
560674
  - Replacing entire functions/classes/methods: use symbol mode \u2014 no exact text matching needed
558726
560675
  - Editing specific lines from extract/search output: use line-targeted mode with start_line
560676
+ - Adding a new block near an existing line: use line-targeted mode with start_line plus position="before" or position="after"
560677
+ - Removing lines entirely: use line-targeted mode with start_line/end_line and new_string=""
558727
560678
  - Editing inside large functions without rewriting them entirely: first use extract with the symbol target (e.g. "file.js#myFunction") to see the function with line numbers, then use start_line/end_line to edit specific lines within it
558728
560679
 
558729
560680
  Error handling:
558730
560681
  - If an edit fails, read the error message carefully \u2014 it contains specific instructions for how to fix the call and retry
558731
560682
  - Common fixes: use 'search'/'extract' to get exact file content, add more context to old_string, switch between text and symbol modes
558732
560683
  - Line-targeted hash mismatch: the file changed since last read; the error provides updated line:hash references
560684
+ - Inverted line ranges are invalid; the error explains how to express insertion with position="before"/"after"
558733
560685
 
558734
560686
  Examples:
558735
560687
 
@@ -558783,6 +560735,22 @@ Line-targeted edit (replace a range of lines):
558783
560735
  return processItems(order.items);</new_string>
558784
560736
  </edit>
558785
560737
 
560738
+ Line-targeted edit (insert before a line without replacing it):
560739
+ <edit>
560740
+ <file_path>src/main.js</file_path>
560741
+ <start_line>42</start_line>
560742
+ <position>before</position>
560743
+ <new_string> logger.debug("starting process");</new_string>
560744
+ </edit>
560745
+
560746
+ Line-targeted edit (delete a range of lines):
560747
+ <edit>
560748
+ <file_path>src/main.js</file_path>
560749
+ <start_line>42</start_line>
560750
+ <end_line>45</end_line>
560751
+ <new_string></new_string>
560752
+ </edit>
560753
+
558786
560754
  Line-targeted edit with hash verification:
558787
560755
  <edit>
558788
560756
  <file_path>src/main.js</file_path>
@@ -559013,6 +560981,10 @@ You are Probe, a specialized code intelligence assistant. Your objective is to a
559013
560981
  * **Purpose:** Retrieve specific code blocks or entire files *after* \`search\` or \`query\` identifies the target.
559014
560982
  * **Syntax:** Optional \`#symbol\` (e.g., \`#MyClass\`), \`#Lstart-Lend\` (e.g., \`#L50-L75\`).
559015
560983
  * **Mandatory Argument:** \`path\` (specific file path, e.g., \`"src/utils/helpers.go"\`, or dependency file like \`"go:github.com/gin-gonic/gin/context.go"\`).
560984
+ * \`symbols\`
560985
+ * **Purpose:** List all symbols (functions, classes, structs, constants, etc.) in a file \u2014 a table of contents with line numbers and nesting.
560986
+ * **Syntax:** \`file\` (path to the file to list symbols from).
560987
+ * **Use When:** You need to understand a file's structure before extracting specific parts, or to find the right symbol name/line number for \`extract\`.
559016
560988
 
559017
560989
  [Examples]
559018
560990
 
@@ -559147,6 +561119,8 @@ __export(tools_exports, {
559147
561119
  searchFilesSchema: () => searchFilesSchema,
559148
561120
  searchSchema: () => searchSchema,
559149
561121
  searchTool: () => searchTool,
561122
+ symbolsSchema: () => symbolsSchema,
561123
+ symbolsTool: () => symbolsTool,
559150
561124
  tools: () => tools,
559151
561125
  useSkillSchema: () => useSkillSchema
559152
561126
  });
@@ -559270,16 +561244,16 @@ function shouldIgnore(filePath, ignorePatterns) {
559270
561244
  }
559271
561245
  return false;
559272
561246
  }
559273
- var import_fs15, import_path18, import_util14, import_child_process10, execAsync3;
561247
+ var import_fs15, import_path18, import_util14, import_child_process11, execAsync3;
559274
561248
  var init_file_lister = __esm({
559275
561249
  "src/utils/file-lister.js"() {
559276
561250
  "use strict";
559277
561251
  import_fs15 = __toESM(__nccwpck_require__(79896), 1);
559278
561252
  import_path18 = __toESM(__nccwpck_require__(16928), 1);
559279
561253
  import_util14 = __nccwpck_require__(39023);
559280
- import_child_process10 = __nccwpck_require__(35317);
561254
+ import_child_process11 = __nccwpck_require__(35317);
559281
561255
  init_symlink_utils();
559282
- execAsync3 = (0, import_util14.promisify)(import_child_process10.exec);
561256
+ execAsync3 = (0, import_util14.promisify)(import_child_process11.exec);
559283
561257
  }
559284
561258
  });
559285
561259
 
@@ -559355,6 +561329,9 @@ __export(index_exports, {
559355
561329
  searchSchema: () => searchSchema,
559356
561330
  searchTool: () => searchTool,
559357
561331
  setBinaryPath: () => setBinaryPath,
561332
+ symbols: () => symbols,
561333
+ symbolsSchema: () => symbolsSchema,
561334
+ symbolsTool: () => symbolsTool,
559358
561335
  taskSchema: () => taskSchema,
559359
561336
  taskSystemPrompt: () => taskSystemPrompt,
559360
561337
  tools: () => tools_exports,
@@ -559368,6 +561345,7 @@ var init_index = __esm({
559368
561345
  init_search();
559369
561346
  init_query();
559370
561347
  init_extract();
561348
+ init_symbols();
559371
561349
  init_grep();
559372
561350
  init_delegate();
559373
561351
  init_utils();
@@ -627561,7 +629539,7 @@ module.exports = /*#__PURE__*/JSON.parse('["aaa","aarp","abb","abbott","abbvie",
627561
629539
  /***/ ((module) => {
627562
629540
 
627563
629541
  "use strict";
627564
- module.exports = /*#__PURE__*/JSON.parse('{"name":"@probelabs/visor","version":"0.1.42","main":"dist/index.js","bin":{"visor":"./dist/index.js"},"exports":{".":{"require":"./dist/index.js","import":"./dist/index.js"},"./sdk":{"types":"./dist/sdk/sdk.d.ts","import":"./dist/sdk/sdk.mjs","require":"./dist/sdk/sdk.js"},"./cli":{"require":"./dist/index.js"}},"files":["dist/","defaults/","action.yml","README.md","LICENSE"],"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"scripts":{"build:cli":"ncc build src/index.ts -o dist && cp -r defaults dist/ && cp -r output dist/ && cp -r docs dist/ && cp -r examples dist/ && cp -r src/debug-visualizer/ui dist/debug-visualizer/ && node scripts/inject-version.js && echo \'#!/usr/bin/env node\' | cat - dist/index.js > temp && mv temp dist/index.js && chmod +x dist/index.js","build:sdk":"tsup src/sdk.ts --dts --sourcemap --format esm,cjs --out-dir dist/sdk","build":"./scripts/build-oss.sh","build:ee":"npm run build:cli && npm run build:sdk","test":"jest && npm run test:yaml","test:unit":"jest","prepublishOnly":"npm run build","test:watch":"jest --watch","test:coverage":"jest --coverage","test:ee":"jest --testPathPatterns=\'tests/ee\' --testPathIgnorePatterns=\'/node_modules/\' --no-coverage","test:manual:bash":"RUN_MANUAL_TESTS=true jest tests/manual/bash-config-manual.test.ts","lint":"eslint src tests --ext .ts","lint:fix":"eslint src tests --ext .ts --fix","format":"prettier --write src tests","format:check":"prettier --check src tests","clean":"","clean:traces":"node scripts/clean-traces.js","prebuild":"npm run clean && node scripts/generate-config-schema.js","pretest":"npm run clean:traces && node scripts/generate-config-schema.js && npm run build:cli","pretest:unit":"npm run clean:traces && node scripts/generate-config-schema.js && npm run build:cli","test:with-build":"npm run build:cli && jest","test:yaml":"node dist/index.js test --progress compact","test:yaml:parallel":"node dist/index.js test --progress compact --max-parallel 4","prepare":"husky","pre-commit":"lint-staged","deploy:site":"cd site && npx wrangler pages deploy . --project-name=visor-site --commit-dirty=true","deploy:worker":"npx wrangler deploy","deploy":"npm run deploy:site && npm run deploy:worker","publish:ee":"./scripts/publish-ee.sh","release":"./scripts/release.sh","release:patch":"./scripts/release.sh patch","release:minor":"./scripts/release.sh minor","release:major":"./scripts/release.sh major","release:prerelease":"./scripts/release.sh prerelease","docs:validate":"node scripts/validate-readme-links.js","workshop:setup":"npm install -D reveal-md@6.1.2","workshop:serve":"cd workshop && reveal-md slides.md -w","workshop:export":"reveal-md workshop/slides.md --static workshop/build","workshop:pdf":"reveal-md workshop/slides.md --print workshop/Visor-Workshop.pdf --print-size letter","workshop:pdf:ci":"reveal-md workshop/slides.md --print workshop/Visor-Workshop.pdf --print-size letter --puppeteer-launch-args=\\"--no-sandbox --disable-dev-shm-usage\\"","workshop:pdf:a4":"reveal-md workshop/slides.md --print workshop/Visor-Workshop-A4.pdf --print-size A4","workshop:build":"npm run workshop:export && npm run workshop:pdf","simulate:issue":"TS_NODE_TRANSPILE_ONLY=1 ts-node scripts/simulate-gh-run.ts --event issues --action opened --debug","simulate:comment":"TS_NODE_TRANSPILE_ONLY=1 ts-node scripts/simulate-gh-run.ts --event issue_comment --action created --debug"},"keywords":["code-review","ai","github-action","cli","pr-review","visor"],"author":"Probe Labs","license":"MIT","description":"AI workflow engine for code review, assistants, and automation — orchestrate checks, MCP tools, and AI providers with YAML-driven pipelines","repository":{"type":"git","url":"git+https://github.com/probelabs/visor.git"},"bugs":{"url":"https://github.com/probelabs/visor/issues"},"homepage":"https://github.com/probelabs/visor#readme","dependencies":{"@actions/core":"^1.11.1","@apidevtools/swagger-parser":"^12.1.0","@grammyjs/runner":"^2.0.3","@modelcontextprotocol/sdk":"^1.25.3","@nyariv/sandboxjs":"github:probelabs/SandboxJS#23c4bb611f7d05f3cb8c523917b5f57103e48108","@octokit/action":"^8.0.2","@octokit/auth-app":"^8.1.0","@octokit/core":"^7.0.3","@octokit/rest":"^22.0.0","@opentelemetry/api":"^1.9.0","@opentelemetry/api-logs":"^0.203.0","@opentelemetry/core":"^1.30.1","@opentelemetry/exporter-logs-otlp-http":"^0.203.0","@opentelemetry/exporter-metrics-otlp-http":"^0.203.0","@opentelemetry/exporter-trace-otlp-grpc":"^0.203.0","@opentelemetry/exporter-trace-otlp-http":"^0.203.0","@opentelemetry/instrumentation":"^0.203.0","@opentelemetry/resources":"^1.30.1","@opentelemetry/sdk-logs":"^0.203.0","@opentelemetry/sdk-metrics":"^1.30.1","@opentelemetry/sdk-node":"^0.203.0","@opentelemetry/sdk-trace-base":"^1.30.1","@opentelemetry/semantic-conventions":"^1.30.1","@probelabs/probe":"^0.6.0-rc308","@types/commander":"^2.12.0","@types/uuid":"^10.0.0","@utcp/file":"^1.1.0","@utcp/http":"^1.1.0","@utcp/sdk":"^1.1.0","@utcp/text":"^1.1.0","acorn":"^8.16.0","acorn-walk":"^8.3.5","ajv":"^8.17.1","ajv-formats":"^3.0.1","better-sqlite3":"^11.0.0","blessed":"^0.1.81","botbuilder":"^4.23.3","botframework-connector":"^4.23.3","cli-table3":"^0.6.5","commander":"^14.0.0","deepmerge":"^4.3.1","dotenv":"^17.2.3","grammy":"^1.41.1","ignore":"^7.0.5","imapflow":"^1.2.12","js-yaml":"^4.1.0","jsonpath-plus":"^10.4.0","liquidjs":"^10.21.1","mailparser":"^3.9.3","minimatch":"^10.2.2","node-cron":"^3.0.3","nodemailer":"^8.0.1","open":"^9.1.0","resend":"^6.9.3","simple-git":"^3.28.0","uuid":"^11.1.0","ws":"^8.18.3"},"optionalDependencies":{"@anthropic/claude-code-sdk":"npm:null@*","@open-policy-agent/opa-wasm":"^1.10.0","knex":"^3.1.0","mysql2":"^3.11.0","pg":"^8.13.0","tedious":"^19.0.0"},"devDependencies":{"@eslint/js":"^9.34.0","@kie/act-js":"^2.6.2","@kie/mock-github":"^2.0.1","@swc/core":"^1.13.2","@swc/jest":"^0.2.37","@types/better-sqlite3":"^7.6.0","@types/blessed":"^0.1.27","@types/jest":"^30.0.0","@types/js-yaml":"^4.0.9","@types/mailparser":"^3.4.6","@types/node":"^24.3.0","@types/node-cron":"^3.0.11","@types/nodemailer":"^7.0.11","@types/ws":"^8.18.1","@typescript-eslint/eslint-plugin":"^8.42.0","@typescript-eslint/parser":"^8.42.0","@vercel/ncc":"^0.38.4","eslint":"^9.34.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.4","husky":"^9.1.7","jest":"^30.1.3","lint-staged":"^16.1.6","prettier":"^3.6.2","reveal-md":"^6.1.2","ts-json-schema-generator":"^1.5.1","ts-node":"^10.9.2","tsup":"^8.5.0","typescript":"^5.9.2","wrangler":"^3.0.0"},"peerDependenciesMeta":{"@anthropic/claude-code-sdk":{"optional":true}},"directories":{"test":"tests"},"lint-staged":{"src/**/*.{ts,js}":["eslint --fix","prettier --write"],"tests/**/*.{ts,js}":["eslint --fix","prettier --write"],"*.{json,md,yml,yaml}":["prettier --write"]}}');
629542
+ module.exports = /*#__PURE__*/JSON.parse('{"name":"@probelabs/visor","version":"0.1.42","main":"dist/index.js","bin":{"visor":"./dist/index.js"},"exports":{".":{"require":"./dist/index.js","import":"./dist/index.js"},"./sdk":{"types":"./dist/sdk/sdk.d.ts","import":"./dist/sdk/sdk.mjs","require":"./dist/sdk/sdk.js"},"./cli":{"require":"./dist/index.js"}},"files":["dist/","defaults/","action.yml","README.md","LICENSE"],"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"scripts":{"build:cli":"ncc build src/index.ts -o dist && cp -r defaults dist/ && cp -r output dist/ && cp -r docs dist/ && cp -r examples dist/ && cp -r src/debug-visualizer/ui dist/debug-visualizer/ && node scripts/inject-version.js && echo \'#!/usr/bin/env node\' | cat - dist/index.js > temp && mv temp dist/index.js && chmod +x dist/index.js","build:sdk":"tsup src/sdk.ts --dts --sourcemap --format esm,cjs --out-dir dist/sdk","build":"npm run build:oss","build:oss":"./scripts/build-oss.sh","build:ee":"npm run build:cli && npm run build:sdk","test":"jest && npm run test:yaml","test:unit":"jest","prepublishOnly":"npm run build","test:watch":"jest --watch","test:coverage":"jest --coverage","test:ee":"jest --testPathPatterns=\'tests/ee\' --testPathIgnorePatterns=\'/node_modules/\' --no-coverage","test:manual:bash":"RUN_MANUAL_TESTS=true jest tests/manual/bash-config-manual.test.ts","lint":"eslint src tests --ext .ts","lint:fix":"eslint src tests --ext .ts --fix","format":"prettier --write src tests","format:check":"prettier --check src tests","clean":"","clean:traces":"node scripts/clean-traces.js","prebuild":"npm run clean && node scripts/generate-config-schema.js","pretest":"npm run clean:traces && node scripts/generate-config-schema.js && npm run build:cli","pretest:unit":"npm run clean:traces && node scripts/generate-config-schema.js && npm run build:cli","test:with-build":"npm run build:cli && jest","test:yaml":"node dist/index.js test --progress compact","test:yaml:parallel":"node dist/index.js test --progress compact --max-parallel 4","prepare":"husky","pre-commit":"lint-staged","deploy:site":"cd site && npx wrangler pages deploy . --project-name=visor-site --commit-dirty=true","deploy:worker":"npx wrangler deploy","deploy":"npm run deploy:site && npm run deploy:worker","publish:ee":"./scripts/publish-ee.sh","release":"./scripts/release.sh","release:patch":"./scripts/release.sh patch","release:minor":"./scripts/release.sh minor","release:major":"./scripts/release.sh major","release:prerelease":"./scripts/release.sh prerelease","docs:validate":"node scripts/validate-readme-links.js","workshop:setup":"npm install -D reveal-md@6.1.2","workshop:serve":"cd workshop && reveal-md slides.md -w","workshop:export":"reveal-md workshop/slides.md --static workshop/build","workshop:pdf":"reveal-md workshop/slides.md --print workshop/Visor-Workshop.pdf --print-size letter","workshop:pdf:ci":"reveal-md workshop/slides.md --print workshop/Visor-Workshop.pdf --print-size letter --puppeteer-launch-args=\\"--no-sandbox --disable-dev-shm-usage\\"","workshop:pdf:a4":"reveal-md workshop/slides.md --print workshop/Visor-Workshop-A4.pdf --print-size A4","workshop:build":"npm run workshop:export && npm run workshop:pdf","simulate:issue":"TS_NODE_TRANSPILE_ONLY=1 ts-node scripts/simulate-gh-run.ts --event issues --action opened --debug","simulate:comment":"TS_NODE_TRANSPILE_ONLY=1 ts-node scripts/simulate-gh-run.ts --event issue_comment --action created --debug"},"keywords":["code-review","ai","github-action","cli","pr-review","visor"],"author":"Probe Labs","license":"MIT","description":"AI workflow engine for code review, assistants, and automation — orchestrate checks, MCP tools, and AI providers with YAML-driven pipelines","repository":{"type":"git","url":"git+https://github.com/probelabs/visor.git"},"bugs":{"url":"https://github.com/probelabs/visor/issues"},"homepage":"https://github.com/probelabs/visor#readme","dependencies":{"@actions/core":"^1.11.1","@apidevtools/swagger-parser":"^12.1.0","@grammyjs/runner":"^2.0.3","@modelcontextprotocol/sdk":"^1.25.3","@nyariv/sandboxjs":"github:probelabs/SandboxJS#23c4bb611f7d05f3cb8c523917b5f57103e48108","@octokit/action":"^8.0.2","@octokit/auth-app":"^8.1.0","@octokit/core":"^7.0.3","@octokit/rest":"^22.0.0","@opentelemetry/api":"^1.9.0","@opentelemetry/api-logs":"^0.203.0","@opentelemetry/core":"^1.30.1","@opentelemetry/exporter-logs-otlp-http":"^0.203.0","@opentelemetry/exporter-metrics-otlp-http":"^0.203.0","@opentelemetry/exporter-trace-otlp-grpc":"^0.203.0","@opentelemetry/exporter-trace-otlp-http":"^0.203.0","@opentelemetry/instrumentation":"^0.203.0","@opentelemetry/resources":"^1.30.1","@opentelemetry/sdk-logs":"^0.203.0","@opentelemetry/sdk-metrics":"^1.30.1","@opentelemetry/sdk-node":"^0.203.0","@opentelemetry/sdk-trace-base":"^1.30.1","@opentelemetry/semantic-conventions":"^1.30.1","@probelabs/probe":"^0.6.0-rc311","@types/commander":"^2.12.0","@types/uuid":"^10.0.0","@utcp/file":"^1.1.0","@utcp/http":"^1.1.0","@utcp/sdk":"^1.1.0","@utcp/text":"^1.1.0","acorn":"^8.16.0","acorn-walk":"^8.3.5","ajv":"^8.17.1","ajv-formats":"^3.0.1","better-sqlite3":"^11.0.0","blessed":"^0.1.81","botbuilder":"^4.23.3","botframework-connector":"^4.23.3","cli-table3":"^0.6.5","commander":"^14.0.0","deepmerge":"^4.3.1","dotenv":"^17.2.3","grammy":"^1.41.1","ignore":"^7.0.5","imapflow":"^1.2.12","js-yaml":"^4.1.0","jsonpath-plus":"^10.4.0","liquidjs":"^10.21.1","mailparser":"^3.9.3","minimatch":"^10.2.2","node-cron":"^3.0.3","nodemailer":"^8.0.1","open":"^9.1.0","resend":"^6.9.3","simple-git":"^3.28.0","uuid":"^11.1.0","ws":"^8.18.3"},"optionalDependencies":{"@anthropic/claude-code-sdk":"npm:null@*","@open-policy-agent/opa-wasm":"^1.10.0","knex":"^3.1.0","mysql2":"^3.11.0","pg":"^8.13.0","tedious":"^19.0.0"},"devDependencies":{"@eslint/js":"^9.34.0","@kie/act-js":"^2.6.2","@kie/mock-github":"^2.0.1","@swc/core":"^1.13.2","@swc/jest":"^0.2.37","@types/better-sqlite3":"^7.6.0","@types/blessed":"^0.1.27","@types/jest":"^30.0.0","@types/js-yaml":"^4.0.9","@types/mailparser":"^3.4.6","@types/node":"^24.3.0","@types/node-cron":"^3.0.11","@types/nodemailer":"^7.0.11","@types/ws":"^8.18.1","@typescript-eslint/eslint-plugin":"^8.42.0","@typescript-eslint/parser":"^8.42.0","@vercel/ncc":"^0.38.4","eslint":"^9.34.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.4","husky":"^9.1.7","jest":"^30.1.3","lint-staged":"^16.1.6","prettier":"^3.6.2","reveal-md":"^6.1.2","ts-json-schema-generator":"^1.5.1","ts-node":"^10.9.2","tsup":"^8.5.0","typescript":"^5.9.2","wrangler":"^3.0.0"},"peerDependenciesMeta":{"@anthropic/claude-code-sdk":{"optional":true}},"directories":{"test":"tests"},"lint-staged":{"src/**/*.{ts,js}":["eslint --fix","prettier --write"],"tests/**/*.{ts,js}":["eslint --fix","prettier --write"],"*.{json,md,yml,yaml}":["prettier --write"]}}');
627565
629543
 
627566
629544
  /***/ })
627567
629545