@probelabs/visor 0.1.181 → 0.1.182

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 (158) hide show
  1. package/defaults/code-talk.yaml +80 -14
  2. package/defaults/engineer.yaml +33 -15
  3. package/defaults/skills/code-explorer.yaml +5 -0
  4. package/dist/agent-protocol/a2a-frontend.d.ts +10 -0
  5. package/dist/agent-protocol/a2a-frontend.d.ts.map +1 -1
  6. package/dist/agent-protocol/task-evaluator.d.ts +52 -0
  7. package/dist/agent-protocol/task-evaluator.d.ts.map +1 -0
  8. package/dist/agent-protocol/task-store.d.ts +5 -3
  9. package/dist/agent-protocol/task-store.d.ts.map +1 -1
  10. package/dist/agent-protocol/tasks-cli-handler.d.ts.map +1 -1
  11. package/dist/agent-protocol/tasks-tui.d.ts +34 -0
  12. package/dist/agent-protocol/tasks-tui.d.ts.map +1 -0
  13. package/dist/agent-protocol/trace-serializer.d.ts +90 -0
  14. package/dist/agent-protocol/trace-serializer.d.ts.map +1 -0
  15. package/dist/agent-protocol/track-execution.d.ts +2 -0
  16. package/dist/agent-protocol/track-execution.d.ts.map +1 -1
  17. package/dist/cli-main.d.ts.map +1 -1
  18. package/dist/defaults/code-talk.yaml +80 -14
  19. package/dist/defaults/engineer.yaml +33 -15
  20. package/dist/defaults/skills/code-explorer.yaml +5 -0
  21. package/dist/docs/commands.md +57 -14
  22. package/dist/docs/configuration.md +2 -0
  23. package/dist/docs/guides/graceful-restart.md +178 -0
  24. package/dist/docs/observability.md +69 -0
  25. package/dist/docs/production-deployment.md +17 -0
  26. package/dist/email/polling-runner.d.ts +4 -0
  27. package/dist/email/polling-runner.d.ts.map +1 -1
  28. package/dist/generated/config-schema.d.ts +70 -6
  29. package/dist/generated/config-schema.d.ts.map +1 -1
  30. package/dist/generated/config-schema.json +73 -6
  31. package/dist/index.js +5006 -886
  32. package/dist/output/traces/{run-2026-03-17T13-58-29-402Z.ndjson → run-2026-03-18T19-02-50-465Z.ndjson} +84 -84
  33. package/dist/{traces/run-2026-03-17T13-59-10-403Z.ndjson → output/traces/run-2026-03-18T19-03-30-428Z.ndjson} +2037 -2037
  34. package/dist/providers/mcp-custom-sse-server.d.ts +4 -0
  35. package/dist/providers/mcp-custom-sse-server.d.ts.map +1 -1
  36. package/dist/runners/graceful-restart.d.ts +46 -0
  37. package/dist/runners/graceful-restart.d.ts.map +1 -0
  38. package/dist/runners/mcp-server-runner.d.ts +12 -0
  39. package/dist/runners/mcp-server-runner.d.ts.map +1 -1
  40. package/dist/runners/runner-factory.d.ts.map +1 -1
  41. package/dist/runners/runner-host.d.ts +12 -0
  42. package/dist/runners/runner-host.d.ts.map +1 -1
  43. package/dist/runners/runner.d.ts +12 -0
  44. package/dist/runners/runner.d.ts.map +1 -1
  45. package/dist/sdk/{a2a-frontend-IWOUJOIZ.mjs → a2a-frontend-4LP3MLTS.mjs} +47 -5
  46. package/dist/sdk/a2a-frontend-4LP3MLTS.mjs.map +1 -0
  47. package/dist/sdk/a2a-frontend-5J3UNFY4.mjs +1718 -0
  48. package/dist/sdk/a2a-frontend-5J3UNFY4.mjs.map +1 -0
  49. package/dist/sdk/{a2a-frontend-BDACLGMA.mjs → a2a-frontend-MU5EO2HZ.mjs} +35 -1
  50. package/dist/sdk/a2a-frontend-MU5EO2HZ.mjs.map +1 -0
  51. package/dist/sdk/{check-provider-registry-4YKTEDKF.mjs → check-provider-registry-MHXQGUNN.mjs} +7 -7
  52. package/dist/sdk/{check-provider-registry-4YFVBGYU.mjs → check-provider-registry-RRWCXSTG.mjs} +3 -3
  53. package/dist/sdk/{check-provider-registry-67ZLGDDQ.mjs → check-provider-registry-Y33CRFVD.mjs} +7 -7
  54. package/dist/sdk/{chunk-DGIH6EX3.mjs → chunk-4AXAVXG5.mjs} +151 -281
  55. package/dist/sdk/chunk-4AXAVXG5.mjs.map +1 -0
  56. package/dist/sdk/{chunk-VMVIM4JB.mjs → chunk-4I3TJ7UJ.mjs} +37 -7
  57. package/dist/sdk/chunk-4I3TJ7UJ.mjs.map +1 -0
  58. package/dist/sdk/{chunk-VXC2XNQJ.mjs → chunk-5J3DNRF7.mjs} +3 -3
  59. package/dist/sdk/{chunk-7YZSSO4X.mjs → chunk-6DPPP7LD.mjs} +10 -10
  60. package/dist/sdk/chunk-7ERVRLDV.mjs +296 -0
  61. package/dist/sdk/chunk-7ERVRLDV.mjs.map +1 -0
  62. package/dist/sdk/{chunk-4DVP6KVC.mjs → chunk-7Z2WHX2J.mjs} +71 -30
  63. package/dist/sdk/chunk-7Z2WHX2J.mjs.map +1 -0
  64. package/dist/sdk/chunk-ANUT54HW.mjs +1502 -0
  65. package/dist/sdk/chunk-ANUT54HW.mjs.map +1 -0
  66. package/dist/sdk/{chunk-J73GEFPT.mjs → chunk-DHETLQIX.mjs} +2 -2
  67. package/dist/sdk/{chunk-QGBASDYP.mjs → chunk-JCOSKBMP.mjs} +71 -30
  68. package/dist/sdk/chunk-JCOSKBMP.mjs.map +1 -0
  69. package/dist/sdk/chunk-MK7ONH47.mjs +739 -0
  70. package/dist/sdk/chunk-MK7ONH47.mjs.map +1 -0
  71. package/dist/sdk/chunk-QXT47ZHR.mjs +390 -0
  72. package/dist/sdk/chunk-QXT47ZHR.mjs.map +1 -0
  73. package/dist/sdk/chunk-V75NEIXL.mjs +296 -0
  74. package/dist/sdk/chunk-V75NEIXL.mjs.map +1 -0
  75. package/dist/sdk/chunk-ZOF5QT6U.mjs +5943 -0
  76. package/dist/sdk/chunk-ZOF5QT6U.mjs.map +1 -0
  77. package/dist/sdk/{config-TSA5FUOM.mjs → config-2STD74CJ.mjs} +2 -2
  78. package/dist/sdk/config-JE4HKTWW.mjs +16 -0
  79. package/dist/sdk/{failure-condition-evaluator-HTPB5FYW.mjs → failure-condition-evaluator-5DZYMCGW.mjs} +4 -4
  80. package/dist/sdk/failure-condition-evaluator-R6DCDJAV.mjs +18 -0
  81. package/dist/sdk/{github-frontend-3SDFCCKI.mjs → github-frontend-3PSCKPAJ.mjs} +4 -4
  82. package/dist/sdk/github-frontend-L3F5JXPJ.mjs +1394 -0
  83. package/dist/sdk/github-frontend-L3F5JXPJ.mjs.map +1 -0
  84. package/dist/sdk/{host-QE4L7UXE.mjs → host-54CHV2LW.mjs} +3 -3
  85. package/dist/sdk/{host-VBBSLUWG.mjs → host-WAU6CT42.mjs} +3 -3
  86. package/dist/sdk/{host-CVH2CSHM.mjs → host-X5ZZCEWN.mjs} +2 -2
  87. package/dist/sdk/{routing-YVMTKFDZ.mjs → routing-CVQT4KHX.mjs} +5 -5
  88. package/dist/sdk/routing-EBAE5SSO.mjs +26 -0
  89. package/dist/sdk/{schedule-tool-Z5VG67JK.mjs → schedule-tool-POY3CDZL.mjs} +7 -7
  90. package/dist/sdk/{schedule-tool-ADUXTCY7.mjs → schedule-tool-R2OAATUS.mjs} +7 -7
  91. package/dist/sdk/{schedule-tool-ZMX3Y7LF.mjs → schedule-tool-Z6QYL2B3.mjs} +3 -3
  92. package/dist/sdk/{schedule-tool-handler-N7UNABOA.mjs → schedule-tool-handler-J4NUETJ6.mjs} +3 -3
  93. package/dist/sdk/{schedule-tool-handler-PCERK6ZZ.mjs → schedule-tool-handler-JMAKHPI7.mjs} +7 -7
  94. package/dist/sdk/{schedule-tool-handler-QOJVFRB4.mjs → schedule-tool-handler-MWFUIQKR.mjs} +7 -7
  95. package/dist/sdk/sdk.d.mts +33 -0
  96. package/dist/sdk/sdk.d.ts +33 -0
  97. package/dist/sdk/sdk.js +2058 -342
  98. package/dist/sdk/sdk.js.map +1 -1
  99. package/dist/sdk/sdk.mjs +6 -6
  100. package/dist/sdk/task-evaluator-HLNXKKVV.mjs +1278 -0
  101. package/dist/sdk/task-evaluator-HLNXKKVV.mjs.map +1 -0
  102. package/dist/sdk/{trace-helpers-KXDOJWBL.mjs → trace-helpers-HL5FBX65.mjs} +3 -3
  103. package/dist/sdk/trace-helpers-WJXYVV4S.mjs +29 -0
  104. package/dist/sdk/trace-helpers-WJXYVV4S.mjs.map +1 -0
  105. package/dist/sdk/trace-reader-ZY77OFNM.mjs +266 -0
  106. package/dist/sdk/trace-reader-ZY77OFNM.mjs.map +1 -0
  107. package/dist/sdk/track-execution-MKIQXP2C.mjs +136 -0
  108. package/dist/sdk/track-execution-MKIQXP2C.mjs.map +1 -0
  109. package/dist/sdk/track-execution-YUXQ6WQH.mjs +136 -0
  110. package/dist/sdk/track-execution-YUXQ6WQH.mjs.map +1 -0
  111. package/dist/sdk/{workflow-check-provider-NTHC5ZBF.mjs → workflow-check-provider-SE5I7EMA.mjs} +7 -7
  112. package/dist/sdk/workflow-check-provider-SE5I7EMA.mjs.map +1 -0
  113. package/dist/sdk/{workflow-check-provider-SRIMWKLQ.mjs → workflow-check-provider-VKYGI5GK.mjs} +3 -3
  114. package/dist/sdk/workflow-check-provider-VKYGI5GK.mjs.map +1 -0
  115. package/dist/sdk/{workflow-check-provider-CJXW2Z4F.mjs → workflow-check-provider-YDGZRI3Z.mjs} +7 -7
  116. package/dist/sdk/workflow-check-provider-YDGZRI3Z.mjs.map +1 -0
  117. package/dist/slack/socket-runner.d.ts +12 -0
  118. package/dist/slack/socket-runner.d.ts.map +1 -1
  119. package/dist/teams/webhook-runner.d.ts +4 -0
  120. package/dist/teams/webhook-runner.d.ts.map +1 -1
  121. package/dist/telegram/polling-runner.d.ts +2 -0
  122. package/dist/telegram/polling-runner.d.ts.map +1 -1
  123. package/dist/traces/{run-2026-03-17T13-58-29-402Z.ndjson → run-2026-03-18T19-02-50-465Z.ndjson} +84 -84
  124. package/dist/{output/traces/run-2026-03-17T13-59-10-403Z.ndjson → traces/run-2026-03-18T19-03-30-428Z.ndjson} +2037 -2037
  125. package/dist/types/config.d.ts +33 -0
  126. package/dist/types/config.d.ts.map +1 -1
  127. package/dist/whatsapp/webhook-runner.d.ts +4 -0
  128. package/dist/whatsapp/webhook-runner.d.ts.map +1 -1
  129. package/package.json +2 -2
  130. package/dist/sdk/a2a-frontend-BDACLGMA.mjs.map +0 -1
  131. package/dist/sdk/a2a-frontend-IWOUJOIZ.mjs.map +0 -1
  132. package/dist/sdk/chunk-4DVP6KVC.mjs.map +0 -1
  133. package/dist/sdk/chunk-DGIH6EX3.mjs.map +0 -1
  134. package/dist/sdk/chunk-QGBASDYP.mjs.map +0 -1
  135. package/dist/sdk/chunk-VMVIM4JB.mjs.map +0 -1
  136. /package/dist/sdk/{check-provider-registry-4YFVBGYU.mjs.map → check-provider-registry-MHXQGUNN.mjs.map} +0 -0
  137. /package/dist/sdk/{check-provider-registry-4YKTEDKF.mjs.map → check-provider-registry-RRWCXSTG.mjs.map} +0 -0
  138. /package/dist/sdk/{check-provider-registry-67ZLGDDQ.mjs.map → check-provider-registry-Y33CRFVD.mjs.map} +0 -0
  139. /package/dist/sdk/{chunk-VXC2XNQJ.mjs.map → chunk-5J3DNRF7.mjs.map} +0 -0
  140. /package/dist/sdk/{chunk-7YZSSO4X.mjs.map → chunk-6DPPP7LD.mjs.map} +0 -0
  141. /package/dist/sdk/{chunk-J73GEFPT.mjs.map → chunk-DHETLQIX.mjs.map} +0 -0
  142. /package/dist/sdk/{config-TSA5FUOM.mjs.map → config-2STD74CJ.mjs.map} +0 -0
  143. /package/dist/sdk/{failure-condition-evaluator-HTPB5FYW.mjs.map → config-JE4HKTWW.mjs.map} +0 -0
  144. /package/dist/sdk/{routing-YVMTKFDZ.mjs.map → failure-condition-evaluator-5DZYMCGW.mjs.map} +0 -0
  145. /package/dist/sdk/{schedule-tool-ADUXTCY7.mjs.map → failure-condition-evaluator-R6DCDJAV.mjs.map} +0 -0
  146. /package/dist/sdk/{github-frontend-3SDFCCKI.mjs.map → github-frontend-3PSCKPAJ.mjs.map} +0 -0
  147. /package/dist/sdk/{host-CVH2CSHM.mjs.map → host-54CHV2LW.mjs.map} +0 -0
  148. /package/dist/sdk/{host-QE4L7UXE.mjs.map → host-WAU6CT42.mjs.map} +0 -0
  149. /package/dist/sdk/{host-VBBSLUWG.mjs.map → host-X5ZZCEWN.mjs.map} +0 -0
  150. /package/dist/sdk/{schedule-tool-Z5VG67JK.mjs.map → routing-CVQT4KHX.mjs.map} +0 -0
  151. /package/dist/sdk/{schedule-tool-ZMX3Y7LF.mjs.map → routing-EBAE5SSO.mjs.map} +0 -0
  152. /package/dist/sdk/{schedule-tool-handler-N7UNABOA.mjs.map → schedule-tool-POY3CDZL.mjs.map} +0 -0
  153. /package/dist/sdk/{schedule-tool-handler-PCERK6ZZ.mjs.map → schedule-tool-R2OAATUS.mjs.map} +0 -0
  154. /package/dist/sdk/{schedule-tool-handler-QOJVFRB4.mjs.map → schedule-tool-Z6QYL2B3.mjs.map} +0 -0
  155. /package/dist/sdk/{trace-helpers-KXDOJWBL.mjs.map → schedule-tool-handler-J4NUETJ6.mjs.map} +0 -0
  156. /package/dist/sdk/{workflow-check-provider-CJXW2Z4F.mjs.map → schedule-tool-handler-JMAKHPI7.mjs.map} +0 -0
  157. /package/dist/sdk/{workflow-check-provider-NTHC5ZBF.mjs.map → schedule-tool-handler-MWFUIQKR.mjs.map} +0 -0
  158. /package/dist/sdk/{workflow-check-provider-SRIMWKLQ.mjs.map → trace-helpers-HL5FBX65.mjs.map} +0 -0
package/dist/sdk/sdk.js CHANGED
@@ -704,7 +704,7 @@ var require_package = __commonJS({
704
704
  "package.json"(exports2, module2) {
705
705
  module2.exports = {
706
706
  name: "@probelabs/visor",
707
- version: "0.1.181",
707
+ version: "0.1.182",
708
708
  main: "dist/index.js",
709
709
  bin: {
710
710
  visor: "./dist/index.js"
@@ -823,7 +823,7 @@ var require_package = __commonJS({
823
823
  "@opentelemetry/sdk-node": "^0.203.0",
824
824
  "@opentelemetry/sdk-trace-base": "^1.30.1",
825
825
  "@opentelemetry/semantic-conventions": "^1.30.1",
826
- "@probelabs/probe": "^0.6.0-rc297",
826
+ "@probelabs/probe": "^0.6.0-rc300",
827
827
  "@types/commander": "^2.12.0",
828
828
  "@types/uuid": "^10.0.0",
829
829
  "@utcp/file": "^1.1.0",
@@ -1285,19 +1285,19 @@ function __getOrCreateNdjsonPath() {
1285
1285
  try {
1286
1286
  if (process.env.VISOR_TELEMETRY_SINK && process.env.VISOR_TELEMETRY_SINK !== "file")
1287
1287
  return null;
1288
- const path30 = require("path");
1289
- const fs27 = require("fs");
1288
+ const path31 = require("path");
1289
+ const fs29 = require("fs");
1290
1290
  if (process.env.VISOR_FALLBACK_TRACE_FILE) {
1291
1291
  __ndjsonPath = process.env.VISOR_FALLBACK_TRACE_FILE;
1292
- const dir = path30.dirname(__ndjsonPath);
1293
- if (!fs27.existsSync(dir)) fs27.mkdirSync(dir, { recursive: true });
1292
+ const dir = path31.dirname(__ndjsonPath);
1293
+ if (!fs29.existsSync(dir)) fs29.mkdirSync(dir, { recursive: true });
1294
1294
  return __ndjsonPath;
1295
1295
  }
1296
- const outDir = process.env.VISOR_TRACE_DIR || path30.join(process.cwd(), "output", "traces");
1297
- if (!fs27.existsSync(outDir)) fs27.mkdirSync(outDir, { recursive: true });
1296
+ const outDir = process.env.VISOR_TRACE_DIR || path31.join(process.cwd(), "output", "traces");
1297
+ if (!fs29.existsSync(outDir)) fs29.mkdirSync(outDir, { recursive: true });
1298
1298
  if (!__ndjsonPath) {
1299
1299
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1300
- __ndjsonPath = path30.join(outDir, `${ts}.ndjson`);
1300
+ __ndjsonPath = path31.join(outDir, `${ts}.ndjson`);
1301
1301
  }
1302
1302
  return __ndjsonPath;
1303
1303
  } catch {
@@ -1306,11 +1306,11 @@ function __getOrCreateNdjsonPath() {
1306
1306
  }
1307
1307
  function _appendRunMarker() {
1308
1308
  try {
1309
- const fs27 = require("fs");
1309
+ const fs29 = require("fs");
1310
1310
  const p = __getOrCreateNdjsonPath();
1311
1311
  if (!p) return;
1312
1312
  const line = { name: "visor.run", attributes: { started: true } };
1313
- fs27.appendFileSync(p, JSON.stringify(line) + "\n", "utf8");
1313
+ fs29.appendFileSync(p, JSON.stringify(line) + "\n", "utf8");
1314
1314
  } catch {
1315
1315
  }
1316
1316
  }
@@ -1956,14 +1956,14 @@ async function handleWavePlanning(context2, state, transition) {
1956
1956
  for (const request of forwardRunRequests) {
1957
1957
  const { target, gotoEvent } = request;
1958
1958
  const scopeKey = request.scope && Array.isArray(request.scope) ? JSON.stringify(request.scope) : "root";
1959
- const dedupeKey = `${target}:${gotoEvent || "default"}:${state.wave}:${scopeKey}`;
1960
- if (state.forwardRunGuards.has(dedupeKey)) {
1959
+ const dedupeKey2 = `${target}:${gotoEvent || "default"}:${state.wave}:${scopeKey}`;
1960
+ if (state.forwardRunGuards.has(dedupeKey2)) {
1961
1961
  if (context2.debug) {
1962
1962
  logger.info(`[WavePlanning] Skipping duplicate forward run: ${target}`);
1963
1963
  }
1964
1964
  continue;
1965
1965
  }
1966
- state.forwardRunGuards.add(dedupeKey);
1966
+ state.forwardRunGuards.add(dedupeKey2);
1967
1967
  checksToRun.add(target);
1968
1968
  try {
1969
1969
  const scope = request.scope;
@@ -3938,9 +3938,9 @@ function configureLiquidWithExtensions(liquid) {
3938
3938
  });
3939
3939
  liquid.registerFilter("get", (obj, pathExpr) => {
3940
3940
  if (obj == null) return void 0;
3941
- const path30 = typeof pathExpr === "string" ? pathExpr : String(pathExpr || "");
3942
- if (!path30) return obj;
3943
- const parts = path30.split(".");
3941
+ const path31 = typeof pathExpr === "string" ? pathExpr : String(pathExpr || "");
3942
+ if (!path31) return obj;
3943
+ const parts = path31.split(".");
3944
3944
  let cur = obj;
3945
3945
  for (const p of parts) {
3946
3946
  if (cur == null) return void 0;
@@ -4059,9 +4059,9 @@ function configureLiquidWithExtensions(liquid) {
4059
4059
  }
4060
4060
  }
4061
4061
  const defaultRole = typeof rolesCfg.default === "string" && rolesCfg.default.trim() ? rolesCfg.default.trim() : void 0;
4062
- const getNested = (obj, path30) => {
4063
- if (!obj || !path30) return void 0;
4064
- const parts = path30.split(".");
4062
+ const getNested = (obj, path31) => {
4063
+ if (!obj || !path31) return void 0;
4064
+ const parts = path31.split(".");
4065
4065
  let cur = obj;
4066
4066
  for (const p of parts) {
4067
4067
  if (cur == null) return void 0;
@@ -7834,8 +7834,8 @@ var init_dependency_gating = __esm({
7834
7834
  async function renderTemplateContent(checkId, checkConfig, reviewSummary) {
7835
7835
  try {
7836
7836
  const { createExtendedLiquid: createExtendedLiquid2 } = await Promise.resolve().then(() => (init_liquid_extensions(), liquid_extensions_exports));
7837
- const fs27 = await import("fs/promises");
7838
- const path30 = await import("path");
7837
+ const fs29 = await import("fs/promises");
7838
+ const path31 = await import("path");
7839
7839
  const schemaRaw = checkConfig.schema || "plain";
7840
7840
  const schema = typeof schemaRaw === "string" ? schemaRaw : "code-review";
7841
7841
  let templateContent;
@@ -7844,25 +7844,25 @@ async function renderTemplateContent(checkId, checkConfig, reviewSummary) {
7844
7844
  logger.debug(`[TemplateRenderer] Using inline template for ${checkId}`);
7845
7845
  } else if (checkConfig.template && checkConfig.template.file) {
7846
7846
  const file = String(checkConfig.template.file);
7847
- const resolved = path30.resolve(process.cwd(), file);
7848
- templateContent = await fs27.readFile(resolved, "utf-8");
7847
+ const resolved = path31.resolve(process.cwd(), file);
7848
+ templateContent = await fs29.readFile(resolved, "utf-8");
7849
7849
  logger.debug(`[TemplateRenderer] Using template file for ${checkId}: ${resolved}`);
7850
7850
  } else if (schema && schema !== "plain") {
7851
7851
  const sanitized = String(schema).replace(/[^a-zA-Z0-9-]/g, "");
7852
7852
  if (sanitized) {
7853
7853
  const candidatePaths = [
7854
- path30.join(__dirname, "output", sanitized, "template.liquid"),
7854
+ path31.join(__dirname, "output", sanitized, "template.liquid"),
7855
7855
  // bundled: dist/output/
7856
- path30.join(__dirname, "..", "..", "output", sanitized, "template.liquid"),
7856
+ path31.join(__dirname, "..", "..", "output", sanitized, "template.liquid"),
7857
7857
  // source: output/
7858
- path30.join(process.cwd(), "output", sanitized, "template.liquid"),
7858
+ path31.join(process.cwd(), "output", sanitized, "template.liquid"),
7859
7859
  // fallback: cwd/output/
7860
- path30.join(process.cwd(), "dist", "output", sanitized, "template.liquid")
7860
+ path31.join(process.cwd(), "dist", "output", sanitized, "template.liquid")
7861
7861
  // fallback: cwd/dist/output/
7862
7862
  ];
7863
7863
  for (const p of candidatePaths) {
7864
7864
  try {
7865
- templateContent = await fs27.readFile(p, "utf-8");
7865
+ templateContent = await fs29.readFile(p, "utf-8");
7866
7866
  if (templateContent) {
7867
7867
  logger.debug(`[TemplateRenderer] Using schema template for ${checkId}: ${p}`);
7868
7868
  break;
@@ -8284,7 +8284,7 @@ async function processDiffWithOutline(diffContent) {
8284
8284
  }
8285
8285
  try {
8286
8286
  const originalProbePath = process.env.PROBE_PATH;
8287
- const fs27 = require("fs");
8287
+ const fs29 = require("fs");
8288
8288
  const possiblePaths = [
8289
8289
  // Relative to current working directory (most common in production)
8290
8290
  path6.join(process.cwd(), "node_modules/@probelabs/probe/bin/probe-binary"),
@@ -8295,7 +8295,7 @@ async function processDiffWithOutline(diffContent) {
8295
8295
  ];
8296
8296
  let probeBinaryPath;
8297
8297
  for (const candidatePath of possiblePaths) {
8298
- if (fs27.existsSync(candidatePath)) {
8298
+ if (fs29.existsSync(candidatePath)) {
8299
8299
  probeBinaryPath = candidatePath;
8300
8300
  break;
8301
8301
  }
@@ -9810,8 +9810,8 @@ ${schemaString}`);
9810
9810
  }
9811
9811
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
9812
9812
  try {
9813
- const fs27 = require("fs");
9814
- const path30 = require("path");
9813
+ const fs29 = require("fs");
9814
+ const path31 = require("path");
9815
9815
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
9816
9816
  const provider = this.config.provider || "auto";
9817
9817
  const model = this.config.model || "default";
@@ -9925,20 +9925,20 @@ ${"=".repeat(60)}
9925
9925
  `;
9926
9926
  readableVersion += `${"=".repeat(60)}
9927
9927
  `;
9928
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path30.join(process.cwd(), "debug-artifacts");
9929
- if (!fs27.existsSync(debugArtifactsDir)) {
9930
- fs27.mkdirSync(debugArtifactsDir, { recursive: true });
9928
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path31.join(process.cwd(), "debug-artifacts");
9929
+ if (!fs29.existsSync(debugArtifactsDir)) {
9930
+ fs29.mkdirSync(debugArtifactsDir, { recursive: true });
9931
9931
  }
9932
- const debugFile = path30.join(
9932
+ const debugFile = path31.join(
9933
9933
  debugArtifactsDir,
9934
9934
  `prompt-${_checkName || "unknown"}-${timestamp}.json`
9935
9935
  );
9936
- fs27.writeFileSync(debugFile, debugJson, "utf-8");
9937
- const readableFile = path30.join(
9936
+ fs29.writeFileSync(debugFile, debugJson, "utf-8");
9937
+ const readableFile = path31.join(
9938
9938
  debugArtifactsDir,
9939
9939
  `prompt-${_checkName || "unknown"}-${timestamp}.txt`
9940
9940
  );
9941
- fs27.writeFileSync(readableFile, readableVersion, "utf-8");
9941
+ fs29.writeFileSync(readableFile, readableVersion, "utf-8");
9942
9942
  log(`
9943
9943
  \u{1F4BE} Full debug info saved to:`);
9944
9944
  log(` JSON: ${debugFile}`);
@@ -9976,8 +9976,8 @@ ${"=".repeat(60)}
9976
9976
  log(`\u{1F4E4} Response length: ${response.length} characters`);
9977
9977
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
9978
9978
  try {
9979
- const fs27 = require("fs");
9980
- const path30 = require("path");
9979
+ const fs29 = require("fs");
9980
+ const path31 = require("path");
9981
9981
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
9982
9982
  const agentAny2 = agent;
9983
9983
  let fullHistory = [];
@@ -9988,8 +9988,8 @@ ${"=".repeat(60)}
9988
9988
  } else if (agentAny2._messages) {
9989
9989
  fullHistory = agentAny2._messages;
9990
9990
  }
9991
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path30.join(process.cwd(), "debug-artifacts");
9992
- const sessionBase = path30.join(
9991
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path31.join(process.cwd(), "debug-artifacts");
9992
+ const sessionBase = path31.join(
9993
9993
  debugArtifactsDir,
9994
9994
  `session-${_checkName || "unknown"}-${timestamp}`
9995
9995
  );
@@ -10001,7 +10001,7 @@ ${"=".repeat(60)}
10001
10001
  schema: effectiveSchema,
10002
10002
  totalMessages: fullHistory.length
10003
10003
  };
10004
- fs27.writeFileSync(sessionBase + ".json", JSON.stringify(sessionData, null, 2), "utf-8");
10004
+ fs29.writeFileSync(sessionBase + ".json", JSON.stringify(sessionData, null, 2), "utf-8");
10005
10005
  let readable = `=============================================================
10006
10006
  `;
10007
10007
  readable += `COMPLETE AI SESSION HISTORY (AFTER RESPONSE)
@@ -10028,7 +10028,7 @@ ${"=".repeat(60)}
10028
10028
  `;
10029
10029
  readable += content + "\n";
10030
10030
  });
10031
- fs27.writeFileSync(sessionBase + ".summary.txt", readable, "utf-8");
10031
+ fs29.writeFileSync(sessionBase + ".summary.txt", readable, "utf-8");
10032
10032
  log(`\u{1F4BE} Complete session history saved:`);
10033
10033
  log(` - Contains ALL ${fullHistory.length} messages (prompts + responses)`);
10034
10034
  } catch (error) {
@@ -10037,11 +10037,11 @@ ${"=".repeat(60)}
10037
10037
  }
10038
10038
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
10039
10039
  try {
10040
- const fs27 = require("fs");
10041
- const path30 = require("path");
10040
+ const fs29 = require("fs");
10041
+ const path31 = require("path");
10042
10042
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
10043
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path30.join(process.cwd(), "debug-artifacts");
10044
- const responseFile = path30.join(
10043
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path31.join(process.cwd(), "debug-artifacts");
10044
+ const responseFile = path31.join(
10045
10045
  debugArtifactsDir,
10046
10046
  `response-${_checkName || "unknown"}-${timestamp}.txt`
10047
10047
  );
@@ -10074,7 +10074,7 @@ ${"=".repeat(60)}
10074
10074
  `;
10075
10075
  responseContent += `${"=".repeat(60)}
10076
10076
  `;
10077
- fs27.writeFileSync(responseFile, responseContent, "utf-8");
10077
+ fs29.writeFileSync(responseFile, responseContent, "utf-8");
10078
10078
  log(`\u{1F4BE} Response saved to: ${responseFile}`);
10079
10079
  } catch (error) {
10080
10080
  log(`\u26A0\uFE0F Could not save response file: ${error}`);
@@ -10090,9 +10090,9 @@ ${"=".repeat(60)}
10090
10090
  await agentAny._telemetryConfig.shutdown();
10091
10091
  log(`\u{1F4CA} OpenTelemetry trace saved to: ${agentAny._traceFilePath}`);
10092
10092
  if (process.env.GITHUB_ACTIONS) {
10093
- const fs27 = require("fs");
10094
- if (fs27.existsSync(agentAny._traceFilePath)) {
10095
- const stats = fs27.statSync(agentAny._traceFilePath);
10093
+ const fs29 = require("fs");
10094
+ if (fs29.existsSync(agentAny._traceFilePath)) {
10095
+ const stats = fs29.statSync(agentAny._traceFilePath);
10096
10096
  console.log(
10097
10097
  `::notice title=AI Trace Saved::${agentAny._traceFilePath} (${stats.size} bytes)`
10098
10098
  );
@@ -10353,8 +10353,8 @@ ${schemaString}`);
10353
10353
  const model = this.config.model || "default";
10354
10354
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
10355
10355
  try {
10356
- const fs27 = require("fs");
10357
- const path30 = require("path");
10356
+ const fs29 = require("fs");
10357
+ const path31 = require("path");
10358
10358
  const os2 = require("os");
10359
10359
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
10360
10360
  const debugData = {
@@ -10428,18 +10428,18 @@ ${"=".repeat(60)}
10428
10428
  readableVersion += `${"=".repeat(60)}
10429
10429
  `;
10430
10430
  const tempDir = os2.tmpdir();
10431
- const promptFile = path30.join(tempDir, `visor-prompt-${timestamp}.txt`);
10432
- fs27.writeFileSync(promptFile, prompt, "utf-8");
10431
+ const promptFile = path31.join(tempDir, `visor-prompt-${timestamp}.txt`);
10432
+ fs29.writeFileSync(promptFile, prompt, "utf-8");
10433
10433
  log(`
10434
10434
  \u{1F4BE} Prompt saved to: ${promptFile}`);
10435
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path30.join(process.cwd(), "debug-artifacts");
10435
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path31.join(process.cwd(), "debug-artifacts");
10436
10436
  try {
10437
- const base = path30.join(
10437
+ const base = path31.join(
10438
10438
  debugArtifactsDir,
10439
10439
  `prompt-${_checkName || "unknown"}-${timestamp}`
10440
10440
  );
10441
- fs27.writeFileSync(base + ".json", debugJson, "utf-8");
10442
- fs27.writeFileSync(base + ".summary.txt", readableVersion, "utf-8");
10441
+ fs29.writeFileSync(base + ".json", debugJson, "utf-8");
10442
+ fs29.writeFileSync(base + ".summary.txt", readableVersion, "utf-8");
10443
10443
  log(`
10444
10444
  \u{1F4BE} Full debug info saved to directory: ${debugArtifactsDir}`);
10445
10445
  } catch {
@@ -10489,8 +10489,8 @@ $ ${cliCommand}
10489
10489
  log(`\u{1F4E4} Response length: ${response.length} characters`);
10490
10490
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
10491
10491
  try {
10492
- const fs27 = require("fs");
10493
- const path30 = require("path");
10492
+ const fs29 = require("fs");
10493
+ const path31 = require("path");
10494
10494
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
10495
10495
  const agentAny = agent;
10496
10496
  let fullHistory = [];
@@ -10501,8 +10501,8 @@ $ ${cliCommand}
10501
10501
  } else if (agentAny._messages) {
10502
10502
  fullHistory = agentAny._messages;
10503
10503
  }
10504
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path30.join(process.cwd(), "debug-artifacts");
10505
- const sessionBase = path30.join(
10504
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path31.join(process.cwd(), "debug-artifacts");
10505
+ const sessionBase = path31.join(
10506
10506
  debugArtifactsDir,
10507
10507
  `session-${_checkName || "unknown"}-${timestamp}`
10508
10508
  );
@@ -10514,7 +10514,7 @@ $ ${cliCommand}
10514
10514
  schema: effectiveSchema,
10515
10515
  totalMessages: fullHistory.length
10516
10516
  };
10517
- fs27.writeFileSync(sessionBase + ".json", JSON.stringify(sessionData, null, 2), "utf-8");
10517
+ fs29.writeFileSync(sessionBase + ".json", JSON.stringify(sessionData, null, 2), "utf-8");
10518
10518
  let readable = `=============================================================
10519
10519
  `;
10520
10520
  readable += `COMPLETE AI SESSION HISTORY (AFTER RESPONSE)
@@ -10541,7 +10541,7 @@ ${"=".repeat(60)}
10541
10541
  `;
10542
10542
  readable += content + "\n";
10543
10543
  });
10544
- fs27.writeFileSync(sessionBase + ".summary.txt", readable, "utf-8");
10544
+ fs29.writeFileSync(sessionBase + ".summary.txt", readable, "utf-8");
10545
10545
  log(`\u{1F4BE} Complete session history saved:`);
10546
10546
  log(` - Contains ALL ${fullHistory.length} messages (prompts + responses)`);
10547
10547
  } catch (error) {
@@ -10550,11 +10550,11 @@ ${"=".repeat(60)}
10550
10550
  }
10551
10551
  if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
10552
10552
  try {
10553
- const fs27 = require("fs");
10554
- const path30 = require("path");
10553
+ const fs29 = require("fs");
10554
+ const path31 = require("path");
10555
10555
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
10556
- const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path30.join(process.cwd(), "debug-artifacts");
10557
- const responseFile = path30.join(
10556
+ const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path31.join(process.cwd(), "debug-artifacts");
10557
+ const responseFile = path31.join(
10558
10558
  debugArtifactsDir,
10559
10559
  `response-${_checkName || "unknown"}-${timestamp}.txt`
10560
10560
  );
@@ -10587,7 +10587,7 @@ ${"=".repeat(60)}
10587
10587
  `;
10588
10588
  responseContent += `${"=".repeat(60)}
10589
10589
  `;
10590
- fs27.writeFileSync(responseFile, responseContent, "utf-8");
10590
+ fs29.writeFileSync(responseFile, responseContent, "utf-8");
10591
10591
  log(`\u{1F4BE} Response saved to: ${responseFile}`);
10592
10592
  } catch (error) {
10593
10593
  log(`\u26A0\uFE0F Could not save response file: ${error}`);
@@ -10605,9 +10605,9 @@ ${"=".repeat(60)}
10605
10605
  await telemetry.shutdown();
10606
10606
  log(`\u{1F4CA} OpenTelemetry trace saved to: ${traceFilePath}`);
10607
10607
  if (process.env.GITHUB_ACTIONS) {
10608
- const fs27 = require("fs");
10609
- if (fs27.existsSync(traceFilePath)) {
10610
- const stats = fs27.statSync(traceFilePath);
10608
+ const fs29 = require("fs");
10609
+ if (fs29.existsSync(traceFilePath)) {
10610
+ const stats = fs29.statSync(traceFilePath);
10611
10611
  console.log(
10612
10612
  `::notice title=AI Trace Saved::OpenTelemetry trace file size: ${stats.size} bytes`
10613
10613
  );
@@ -10645,8 +10645,8 @@ ${"=".repeat(60)}
10645
10645
  * Load schema content from schema files or inline definitions
10646
10646
  */
10647
10647
  async loadSchemaContent(schema) {
10648
- const fs27 = require("fs").promises;
10649
- const path30 = require("path");
10648
+ const fs29 = require("fs").promises;
10649
+ const path31 = require("path");
10650
10650
  if (typeof schema === "object" && schema !== null) {
10651
10651
  log("\u{1F4CB} Using inline schema object from configuration");
10652
10652
  return JSON.stringify(schema);
@@ -10659,14 +10659,14 @@ ${"=".repeat(60)}
10659
10659
  }
10660
10660
  } catch {
10661
10661
  }
10662
- if ((schema.startsWith("./") || schema.includes(".json")) && !path30.isAbsolute(schema)) {
10662
+ if ((schema.startsWith("./") || schema.includes(".json")) && !path31.isAbsolute(schema)) {
10663
10663
  if (schema.includes("..") || schema.includes("\0")) {
10664
10664
  throw new Error("Invalid schema path: path traversal not allowed");
10665
10665
  }
10666
10666
  try {
10667
- const schemaPath = path30.resolve(process.cwd(), schema);
10667
+ const schemaPath = path31.resolve(process.cwd(), schema);
10668
10668
  log(`\u{1F4CB} Loading custom schema from file: ${schemaPath}`);
10669
- const schemaContent = await fs27.readFile(schemaPath, "utf-8");
10669
+ const schemaContent = await fs29.readFile(schemaPath, "utf-8");
10670
10670
  return schemaContent.trim();
10671
10671
  } catch (error) {
10672
10672
  throw new Error(
@@ -10680,22 +10680,22 @@ ${"=".repeat(60)}
10680
10680
  }
10681
10681
  const candidatePaths = [
10682
10682
  // GitHub Action bundle location
10683
- path30.join(__dirname, "output", sanitizedSchemaName, "schema.json"),
10683
+ path31.join(__dirname, "output", sanitizedSchemaName, "schema.json"),
10684
10684
  // Historical fallback when src/output was inadvertently bundled as output1/
10685
- path30.join(__dirname, "output1", sanitizedSchemaName, "schema.json"),
10685
+ path31.join(__dirname, "output1", sanitizedSchemaName, "schema.json"),
10686
10686
  // Local dev (repo root)
10687
- path30.join(process.cwd(), "output", sanitizedSchemaName, "schema.json")
10687
+ path31.join(process.cwd(), "output", sanitizedSchemaName, "schema.json")
10688
10688
  ];
10689
10689
  for (const schemaPath of candidatePaths) {
10690
10690
  try {
10691
- const schemaContent = await fs27.readFile(schemaPath, "utf-8");
10691
+ const schemaContent = await fs29.readFile(schemaPath, "utf-8");
10692
10692
  return schemaContent.trim();
10693
10693
  } catch {
10694
10694
  }
10695
10695
  }
10696
- const distPath = path30.join(__dirname, "output", sanitizedSchemaName, "schema.json");
10697
- const distAltPath = path30.join(__dirname, "output1", sanitizedSchemaName, "schema.json");
10698
- const cwdPath = path30.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
10696
+ const distPath = path31.join(__dirname, "output", sanitizedSchemaName, "schema.json");
10697
+ const distAltPath = path31.join(__dirname, "output1", sanitizedSchemaName, "schema.json");
10698
+ const cwdPath = path31.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
10699
10699
  throw new Error(
10700
10700
  `Failed to load schema '${sanitizedSchemaName}'. Tried: ${distPath}, ${distAltPath}, and ${cwdPath}. Ensure build copies 'output/' into dist (build:cli), or provide a custom schema file/path.`
10701
10701
  );
@@ -14409,6 +14409,21 @@ var init_config_schema = __esm({
14409
14409
  task_tracking: {
14410
14410
  type: "boolean",
14411
14411
  description: "Enable cross-frontend task tracking (default: false). When true, all workflow executions (CLI, Slack, TUI, Scheduler) are recorded in a shared SQLite TaskStore visible via `visor tasks`."
14412
+ },
14413
+ graceful_restart: {
14414
+ $ref: "#/definitions/GracefulRestartConfig",
14415
+ description: "Graceful restart configuration"
14416
+ },
14417
+ task_evaluate: {
14418
+ anyOf: [
14419
+ {
14420
+ type: "boolean"
14421
+ },
14422
+ {
14423
+ $ref: "#/definitions/TaskEvaluateConfig"
14424
+ }
14425
+ ],
14426
+ 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."
14412
14427
  }
14413
14428
  },
14414
14429
  required: ["version"],
@@ -15281,7 +15296,7 @@ var init_config_schema = __esm({
15281
15296
  description: "Arguments/inputs for the workflow"
15282
15297
  },
15283
15298
  overrides: {
15284
- $ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-15521-30601-src_types_config.ts-0-59584%3E%3E",
15299
+ $ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-15521-30601-src_types_config.ts-0-61047%3E%3E",
15285
15300
  description: "Override specific step configurations in the workflow"
15286
15301
  },
15287
15302
  output_mapping: {
@@ -15297,7 +15312,7 @@ var init_config_schema = __esm({
15297
15312
  description: "Config file path - alternative to workflow ID (loads a Visor config file as workflow)"
15298
15313
  },
15299
15314
  workflow_overrides: {
15300
- $ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-15521-30601-src_types_config.ts-0-59584%3E%3E",
15315
+ $ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-15521-30601-src_types_config.ts-0-61047%3E%3E",
15301
15316
  description: "Alias for overrides - workflow step overrides (backward compatibility)"
15302
15317
  },
15303
15318
  ref: {
@@ -16025,7 +16040,7 @@ var init_config_schema = __esm({
16025
16040
  description: "Custom output name (defaults to workflow name)"
16026
16041
  },
16027
16042
  overrides: {
16028
- $ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-15521-30601-src_types_config.ts-0-59584%3E%3E",
16043
+ $ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-15521-30601-src_types_config.ts-0-61047%3E%3E",
16029
16044
  description: "Step overrides"
16030
16045
  },
16031
16046
  output_mapping: {
@@ -16040,13 +16055,13 @@ var init_config_schema = __esm({
16040
16055
  "^x-": {}
16041
16056
  }
16042
16057
  },
16043
- "Record<string,Partial<interface-src_types_config.ts-15521-30601-src_types_config.ts-0-59584>>": {
16058
+ "Record<string,Partial<interface-src_types_config.ts-15521-30601-src_types_config.ts-0-61047>>": {
16044
16059
  type: "object",
16045
16060
  additionalProperties: {
16046
- $ref: "#/definitions/Partial%3Cinterface-src_types_config.ts-15521-30601-src_types_config.ts-0-59584%3E"
16061
+ $ref: "#/definitions/Partial%3Cinterface-src_types_config.ts-15521-30601-src_types_config.ts-0-61047%3E"
16047
16062
  }
16048
16063
  },
16049
- "Partial<interface-src_types_config.ts-15521-30601-src_types_config.ts-0-59584>": {
16064
+ "Partial<interface-src_types_config.ts-15521-30601-src_types_config.ts-0-61047>": {
16050
16065
  type: "object",
16051
16066
  additionalProperties: false
16052
16067
  },
@@ -17926,6 +17941,58 @@ var init_config_schema = __esm({
17926
17941
  patternProperties: {
17927
17942
  "^x-": {}
17928
17943
  }
17944
+ },
17945
+ GracefulRestartConfig: {
17946
+ type: "object",
17947
+ properties: {
17948
+ drain_timeout_ms: {
17949
+ type: "number",
17950
+ description: "Max time in ms to wait for in-flight work to complete. 0 = unlimited (default)."
17951
+ },
17952
+ child_ready_timeout_ms: {
17953
+ type: "number",
17954
+ description: "Max time in ms to wait for the new child process to become ready. Default: 15000."
17955
+ },
17956
+ notify_users: {
17957
+ type: "boolean",
17958
+ description: 'Send "restarting" messages to active conversations. Default: true.'
17959
+ },
17960
+ restart_command: {
17961
+ type: "string",
17962
+ description: "Override the command used to spawn the new process. If not set, auto-detects: npx re-invokes npx, direct execution re-spawns same binary."
17963
+ }
17964
+ },
17965
+ additionalProperties: false,
17966
+ description: "Graceful restart configuration",
17967
+ patternProperties: {
17968
+ "^x-": {}
17969
+ }
17970
+ },
17971
+ TaskEvaluateConfig: {
17972
+ type: "object",
17973
+ properties: {
17974
+ enabled: {
17975
+ type: "boolean",
17976
+ description: "Enable auto-evaluation (default: true when config object is present)"
17977
+ },
17978
+ model: {
17979
+ type: "string",
17980
+ description: 'LLM model to use for evaluation (e.g. "gemini-2.5-flash", "claude-sonnet-4-5")'
17981
+ },
17982
+ provider: {
17983
+ type: "string",
17984
+ description: "AI provider: google, openai, anthropic"
17985
+ },
17986
+ prompt: {
17987
+ type: "string",
17988
+ description: "Custom system prompt for the evaluator (overrides the default evaluation prompt)"
17989
+ }
17990
+ },
17991
+ additionalProperties: false,
17992
+ description: "Configuration for automatic task evaluation via LLM judge.",
17993
+ patternProperties: {
17994
+ "^x-": {}
17995
+ }
17929
17996
  }
17930
17997
  }
17931
17998
  };
@@ -20466,17 +20533,17 @@ var init_workflow_check_provider = __esm({
20466
20533
  * so it can be executed by the state machine as a nested workflow.
20467
20534
  */
20468
20535
  async loadWorkflowFromConfigPath(sourcePath, baseDir) {
20469
- const path30 = require("path");
20470
- const fs27 = require("fs");
20536
+ const path31 = require("path");
20537
+ const fs29 = require("fs");
20471
20538
  const yaml6 = require("js-yaml");
20472
- const resolved = path30.isAbsolute(sourcePath) ? sourcePath : path30.resolve(baseDir, sourcePath);
20473
- if (!fs27.existsSync(resolved)) {
20539
+ const resolved = path31.isAbsolute(sourcePath) ? sourcePath : path31.resolve(baseDir, sourcePath);
20540
+ if (!fs29.existsSync(resolved)) {
20474
20541
  throw new Error(`Workflow config not found at: ${resolved}`);
20475
20542
  }
20476
- const rawContent = fs27.readFileSync(resolved, "utf8");
20543
+ const rawContent = fs29.readFileSync(resolved, "utf8");
20477
20544
  const rawData = yaml6.load(rawContent);
20478
20545
  if (rawData.imports && Array.isArray(rawData.imports)) {
20479
- const configDir = path30.dirname(resolved);
20546
+ const configDir = path31.dirname(resolved);
20480
20547
  for (const source of rawData.imports) {
20481
20548
  const results = await this.registry.import(source, {
20482
20549
  basePath: configDir,
@@ -20506,8 +20573,8 @@ ${errors}`);
20506
20573
  if (!steps || Object.keys(steps).length === 0) {
20507
20574
  throw new Error(`Config '${resolved}' does not contain any steps to execute as a workflow`);
20508
20575
  }
20509
- const id = path30.basename(resolved).replace(/\.(ya?ml)$/i, "");
20510
- const name = loaded.name || `Workflow from ${path30.basename(resolved)}`;
20576
+ const id = path31.basename(resolved).replace(/\.(ya?ml)$/i, "");
20577
+ const name = loaded.name || `Workflow from ${path31.basename(resolved)}`;
20511
20578
  const workflowDef = {
20512
20579
  id,
20513
20580
  name,
@@ -21923,28 +21990,1572 @@ var init_github_auth = __esm({
21923
21990
  }
21924
21991
  });
21925
21992
 
21993
+ // src/debug-visualizer/trace-reader.ts
21994
+ var trace_reader_exports = {};
21995
+ __export(trace_reader_exports, {
21996
+ buildExecutionTree: () => buildExecutionTree,
21997
+ computeTimeline: () => computeTimeline,
21998
+ extractStateSnapshots: () => extractStateSnapshots,
21999
+ parseNDJSONTrace: () => parseNDJSONTrace
22000
+ });
22001
+ async function parseNDJSONTrace(filePath) {
22002
+ const spans = [];
22003
+ let lineNumber = 0;
22004
+ const fileStream = fs14.createReadStream(filePath);
22005
+ const rl = readline.createInterface({
22006
+ input: fileStream,
22007
+ crlfDelay: Infinity
22008
+ });
22009
+ for await (const line of rl) {
22010
+ lineNumber++;
22011
+ if (!line.trim()) {
22012
+ continue;
22013
+ }
22014
+ try {
22015
+ const rawSpan = JSON.parse(line);
22016
+ const processedSpan = processRawSpan(rawSpan);
22017
+ spans.push(processedSpan);
22018
+ } catch (error) {
22019
+ console.warn(`[trace-reader] Malformed JSON at line ${lineNumber}: ${error}`);
22020
+ }
22021
+ }
22022
+ if (spans.length === 0) {
22023
+ throw new Error("No valid spans found in trace file");
22024
+ }
22025
+ const tree = buildExecutionTree(spans);
22026
+ const snapshots = extractStateSnapshots(spans);
22027
+ const timeline = computeTimeline(spans);
22028
+ const sortedSpans = [...spans].sort((a, b) => compareTimeValues(a.startTime, b.startTime));
22029
+ const firstSpan = sortedSpans[0];
22030
+ const lastSpan = sortedSpans[sortedSpans.length - 1];
22031
+ const startTimeMs = timeValueToMillis(firstSpan.startTime);
22032
+ const endTimeMs = timeValueToMillis(lastSpan.endTime);
22033
+ return {
22034
+ runId: tree.checkId,
22035
+ traceId: firstSpan.traceId,
22036
+ spans,
22037
+ tree,
22038
+ timeline,
22039
+ snapshots,
22040
+ metadata: {
22041
+ startTime: timeValueToISO(firstSpan.startTime),
22042
+ endTime: timeValueToISO(lastSpan.endTime),
22043
+ duration: endTimeMs - startTimeMs,
22044
+ totalSpans: spans.length,
22045
+ totalSnapshots: snapshots.length
22046
+ }
22047
+ };
22048
+ }
22049
+ function processRawSpan(rawSpan) {
22050
+ const traceId = rawSpan.traceId || "";
22051
+ const spanId = rawSpan.spanId || "";
22052
+ const parentSpanId = rawSpan.parentSpanId || void 0;
22053
+ const name = rawSpan.name || "unknown";
22054
+ const startTime = rawSpan.startTime || [0, 0];
22055
+ const endTime = rawSpan.endTime || rawSpan.startTime || [0, 0];
22056
+ const startMs = timeValueToMillis(startTime);
22057
+ const endMs = timeValueToMillis(endTime);
22058
+ const duration = endMs - startMs;
22059
+ const attributes = rawSpan.attributes || {};
22060
+ const events = (rawSpan.events || []).map((evt) => ({
22061
+ name: evt.name || "unknown",
22062
+ time: evt.time || [0, 0],
22063
+ timestamp: evt.timestamp || timeValueToISO(evt.time || [0, 0]),
22064
+ attributes: evt.attributes || {}
22065
+ }));
22066
+ const status = rawSpan.status?.code === 2 ? "error" : "ok";
22067
+ return {
22068
+ traceId,
22069
+ spanId,
22070
+ parentSpanId,
22071
+ name,
22072
+ startTime,
22073
+ endTime,
22074
+ duration,
22075
+ attributes,
22076
+ events,
22077
+ status
22078
+ };
22079
+ }
22080
+ function buildExecutionTree(spans) {
22081
+ const nodeMap = /* @__PURE__ */ new Map();
22082
+ for (const span of spans) {
22083
+ const node = createExecutionNode(span);
22084
+ nodeMap.set(span.spanId, node);
22085
+ }
22086
+ let rootNode;
22087
+ for (const span of spans) {
22088
+ const node = nodeMap.get(span.spanId);
22089
+ if (!span.parentSpanId) {
22090
+ rootNode = node;
22091
+ } else {
22092
+ const parent = nodeMap.get(span.parentSpanId);
22093
+ if (parent) {
22094
+ parent.children.push(node);
22095
+ } else {
22096
+ console.warn(`[trace-reader] Orphaned span: ${span.spanId} (parent: ${span.parentSpanId})`);
22097
+ }
22098
+ }
22099
+ }
22100
+ if (!rootNode) {
22101
+ console.warn("[trace-reader] No root span found, creating synthetic root");
22102
+ rootNode = {
22103
+ checkId: "synthetic-root",
22104
+ type: "run",
22105
+ status: "completed",
22106
+ children: Array.from(nodeMap.values()).filter((n) => !n.span.parentSpanId),
22107
+ span: spans[0],
22108
+ // Use first span as placeholder
22109
+ state: {}
22110
+ };
22111
+ }
22112
+ return rootNode;
22113
+ }
22114
+ function createExecutionNode(span) {
22115
+ const attrs = span.attributes;
22116
+ const checkId = attrs["visor.check.id"] || attrs["visor.run.id"] || span.spanId;
22117
+ let type = "unknown";
22118
+ if (span.name === "visor.run") {
22119
+ type = "run";
22120
+ } else if (span.name === "visor.check") {
22121
+ type = "check";
22122
+ } else if (span.name.startsWith("visor.provider.")) {
22123
+ type = "provider";
22124
+ }
22125
+ let status = "completed";
22126
+ if (span.status === "error") {
22127
+ status = "error";
22128
+ } else if (attrs["visor.check.skipped"] === true) {
22129
+ status = "skipped";
22130
+ }
22131
+ const state = {};
22132
+ if (attrs["visor.check.input.context"]) {
22133
+ try {
22134
+ state.inputContext = JSON.parse(attrs["visor.check.input.context"]);
22135
+ } catch {
22136
+ state.inputContext = attrs["visor.check.input.context"];
22137
+ }
22138
+ }
22139
+ if (attrs["visor.check.output"]) {
22140
+ try {
22141
+ state.output = JSON.parse(attrs["visor.check.output"]);
22142
+ } catch {
22143
+ state.output = attrs["visor.check.output"];
22144
+ }
22145
+ }
22146
+ if (span.status === "error" || attrs["visor.check.error"]) {
22147
+ state.errors = [attrs["visor.check.error"] || "Unknown error"];
22148
+ }
22149
+ state.metadata = {
22150
+ type: attrs["visor.check.type"],
22151
+ duration: span.duration,
22152
+ provider: attrs["visor.provider.type"]
22153
+ };
22154
+ return {
22155
+ checkId,
22156
+ type,
22157
+ status,
22158
+ children: [],
22159
+ span,
22160
+ state
22161
+ };
22162
+ }
22163
+ function extractStateSnapshots(spans) {
22164
+ const snapshots = [];
22165
+ for (const span of spans) {
22166
+ for (const event of span.events) {
22167
+ if (event.name === "state.snapshot") {
22168
+ const attrs = event.attributes || {};
22169
+ const snapshot = {
22170
+ checkId: attrs["visor.snapshot.check_id"] || span.attributes["visor.check.id"] || "unknown",
22171
+ timestamp: attrs["visor.snapshot.timestamp"] || event.timestamp || timeValueToISO(event.time),
22172
+ timestampNanos: event.time,
22173
+ outputs: parseJSON(attrs["visor.snapshot.outputs"], {}),
22174
+ memory: parseJSON(attrs["visor.snapshot.memory"], {})
22175
+ };
22176
+ snapshots.push(snapshot);
22177
+ }
22178
+ }
22179
+ }
22180
+ snapshots.sort((a, b) => compareTimeValues(a.timestampNanos, b.timestampNanos));
22181
+ return snapshots;
22182
+ }
22183
+ function computeTimeline(spans) {
22184
+ const events = [];
22185
+ for (const span of spans) {
22186
+ const checkId = span.attributes["visor.check.id"] || span.spanId;
22187
+ events.push({
22188
+ type: "check.started",
22189
+ checkId,
22190
+ timestamp: timeValueToISO(span.startTime),
22191
+ timestampNanos: span.startTime,
22192
+ metadata: {
22193
+ name: span.name,
22194
+ type: span.attributes["visor.check.type"]
22195
+ }
22196
+ });
22197
+ events.push({
22198
+ type: span.status === "error" ? "check.failed" : "check.completed",
22199
+ checkId,
22200
+ timestamp: timeValueToISO(span.endTime),
22201
+ timestampNanos: span.endTime,
22202
+ duration: span.duration,
22203
+ status: span.status,
22204
+ metadata: {
22205
+ name: span.name
22206
+ }
22207
+ });
22208
+ for (const evt of span.events) {
22209
+ events.push({
22210
+ type: evt.name === "state.snapshot" ? "state.snapshot" : "event",
22211
+ checkId: evt.attributes?.["visor.snapshot.check_id"] || checkId,
22212
+ timestamp: evt.timestamp || timeValueToISO(evt.time),
22213
+ timestampNanos: evt.time,
22214
+ metadata: {
22215
+ eventName: evt.name,
22216
+ attributes: evt.attributes
22217
+ }
22218
+ });
22219
+ }
22220
+ }
22221
+ events.sort((a, b) => compareTimeValues(a.timestampNanos, b.timestampNanos));
22222
+ return events;
22223
+ }
22224
+ function timeValueToMillis(timeValue) {
22225
+ const [seconds, nanos] = timeValue;
22226
+ return seconds * 1e3 + nanos / 1e6;
22227
+ }
22228
+ function timeValueToISO(timeValue) {
22229
+ const millis = timeValueToMillis(timeValue);
22230
+ return new Date(millis).toISOString();
22231
+ }
22232
+ function compareTimeValues(a, b) {
22233
+ if (a[0] !== b[0]) {
22234
+ return a[0] - b[0];
22235
+ }
22236
+ return a[1] - b[1];
22237
+ }
22238
+ function parseJSON(value, defaultValue) {
22239
+ if (typeof value !== "string") {
22240
+ return defaultValue;
22241
+ }
22242
+ try {
22243
+ return JSON.parse(value);
22244
+ } catch {
22245
+ return defaultValue;
22246
+ }
22247
+ }
22248
+ var fs14, readline;
22249
+ var init_trace_reader = __esm({
22250
+ "src/debug-visualizer/trace-reader.ts"() {
22251
+ "use strict";
22252
+ fs14 = __toESM(require("fs"));
22253
+ readline = __toESM(require("readline"));
22254
+ }
22255
+ });
22256
+
22257
+ // src/agent-protocol/trace-serializer.ts
22258
+ function resolveBackendConfig(overrides) {
22259
+ const explicit = process.env.VISOR_TRACE_BACKEND;
22260
+ return {
22261
+ type: overrides?.type || explicit || "auto",
22262
+ grafanaUrl: overrides?.grafanaUrl || process.env.GRAFANA_URL,
22263
+ grafanaDatasourceId: overrides?.grafanaDatasourceId || process.env.GRAFANA_TEMPO_DATASOURCE_ID,
22264
+ jaegerUrl: overrides?.jaegerUrl || process.env.JAEGER_URL,
22265
+ traceDir: overrides?.traceDir || process.env.VISOR_TRACE_DIR || "output/traces",
22266
+ authToken: overrides?.authToken || process.env.GRAFANA_TOKEN
22267
+ };
22268
+ }
22269
+ function parseOTLPResponse(data) {
22270
+ const spans = [];
22271
+ if (data.batches) {
22272
+ for (const batch of data.batches) {
22273
+ for (const ss of batch.scopeSpans || []) {
22274
+ for (const s of ss.spans || []) {
22275
+ spans.push(normalizeOTLPSpan(s));
22276
+ }
22277
+ }
22278
+ }
22279
+ return spans;
22280
+ }
22281
+ if (data.data && Array.isArray(data.data)) {
22282
+ for (const trace2 of data.data) {
22283
+ for (const s of trace2.spans || []) {
22284
+ spans.push(normalizeJaegerSpan(s, trace2.traceID));
22285
+ }
22286
+ }
22287
+ return spans;
22288
+ }
22289
+ return spans;
22290
+ }
22291
+ function normalizeOTLPSpan(s) {
22292
+ const startNs = parseInt(s.startTimeUnixNano || "0", 10);
22293
+ const endNs = parseInt(s.endTimeUnixNano || "0", 10);
22294
+ const traceId = decodeOTLPId(s.traceId);
22295
+ const spanId = decodeOTLPId(s.spanId);
22296
+ const parentSpanId = s.parentSpanId ? decodeOTLPId(s.parentSpanId) : void 0;
22297
+ const attributes = {};
22298
+ for (const attr of s.attributes || []) {
22299
+ const val = attr.value;
22300
+ if (val.stringValue !== void 0) attributes[attr.key] = val.stringValue;
22301
+ else if (val.intValue !== void 0) attributes[attr.key] = parseInt(val.intValue, 10);
22302
+ else if (val.boolValue !== void 0) attributes[attr.key] = val.boolValue;
22303
+ else if (val.doubleValue !== void 0) attributes[attr.key] = val.doubleValue;
22304
+ }
22305
+ const events = [];
22306
+ for (const evt of s.events || []) {
22307
+ const evtAttrs = {};
22308
+ for (const a of evt.attributes || []) {
22309
+ const v = a.value;
22310
+ if (v.stringValue !== void 0) evtAttrs[a.key] = v.stringValue;
22311
+ else if (v.intValue !== void 0) evtAttrs[a.key] = parseInt(v.intValue, 10);
22312
+ }
22313
+ events.push({ name: evt.name, attributes: evtAttrs });
22314
+ }
22315
+ return {
22316
+ traceId,
22317
+ spanId,
22318
+ parentSpanId,
22319
+ name: s.name || "unknown",
22320
+ startTimeMs: startNs / 1e6,
22321
+ endTimeMs: endNs / 1e6,
22322
+ durationMs: (endNs - startNs) / 1e6,
22323
+ attributes,
22324
+ events,
22325
+ status: s.status?.code === 2 ? "error" : "ok"
22326
+ };
22327
+ }
22328
+ function normalizeJaegerSpan(s, traceId) {
22329
+ const attributes = {};
22330
+ for (const tag of s.tags || []) {
22331
+ attributes[tag.key] = tag.value;
22332
+ }
22333
+ const events = [];
22334
+ for (const log2 of s.logs || []) {
22335
+ const evtAttrs = {};
22336
+ for (const f of log2.fields || []) evtAttrs[f.key] = f.value;
22337
+ events.push({ name: evtAttrs["event"] || "log", attributes: evtAttrs });
22338
+ }
22339
+ const startUs = s.startTime || 0;
22340
+ const durationUs = s.duration || 0;
22341
+ return {
22342
+ traceId,
22343
+ spanId: s.spanID,
22344
+ parentSpanId: s.references?.find((r) => r.refType === "CHILD_OF")?.spanID,
22345
+ name: s.operationName || "unknown",
22346
+ startTimeMs: startUs / 1e3,
22347
+ endTimeMs: (startUs + durationUs) / 1e3,
22348
+ durationMs: durationUs / 1e3,
22349
+ attributes,
22350
+ events,
22351
+ status: attributes["otel.status_code"] === "ERROR" || attributes["error"] === true ? "error" : "ok"
22352
+ };
22353
+ }
22354
+ function decodeOTLPId(id) {
22355
+ if (!id) return "";
22356
+ if (/^[0-9a-f]+$/i.test(id)) return id.toLowerCase();
22357
+ try {
22358
+ return Buffer.from(id, "base64").toString("hex");
22359
+ } catch {
22360
+ return id;
22361
+ }
22362
+ }
22363
+ function parseLocalNDJSONSpans(spans) {
22364
+ return spans.map((s) => {
22365
+ const startMs = timeValueToMs(s.startTime || [0, 0]);
22366
+ const endMs = timeValueToMs(s.endTime || s.startTime || [0, 0]);
22367
+ const events = (s.events || []).map((e) => ({
22368
+ name: e.name,
22369
+ attributes: e.attributes || {}
22370
+ }));
22371
+ return {
22372
+ traceId: s.traceId || "",
22373
+ spanId: s.spanId || "",
22374
+ parentSpanId: s.parentSpanId || void 0,
22375
+ name: s.name || "unknown",
22376
+ startTimeMs: startMs,
22377
+ endTimeMs: endMs,
22378
+ durationMs: endMs - startMs,
22379
+ attributes: s.attributes || {},
22380
+ events,
22381
+ status: s.status?.code === 2 ? "error" : "ok"
22382
+ };
22383
+ });
22384
+ }
22385
+ function timeValueToMs(tv) {
22386
+ return tv[0] * 1e3 + tv[1] / 1e6;
22387
+ }
22388
+ async function fetchTraceSpans(traceId, config) {
22389
+ const cfg = resolveBackendConfig(config);
22390
+ const tryGrafana = cfg.type === "grafana" || cfg.type === "auto";
22391
+ const tryJaeger = cfg.type === "jaeger" || cfg.type === "auto";
22392
+ const tryFile = cfg.type === "file" || cfg.type === "auto";
22393
+ if (tryGrafana) {
22394
+ const spans = await fetchFromGrafanaTempo(traceId, cfg);
22395
+ if (spans && spans.length > 0) return spans;
22396
+ }
22397
+ if (tryJaeger) {
22398
+ const spans = await fetchFromJaeger(traceId, cfg);
22399
+ if (spans && spans.length > 0) return spans;
22400
+ }
22401
+ if (tryFile) {
22402
+ const spans = await fetchFromLocalFiles(traceId, cfg);
22403
+ if (spans && spans.length > 0) return spans;
22404
+ }
22405
+ return [];
22406
+ }
22407
+ async function fetchFromGrafanaTempo(traceId, cfg) {
22408
+ let grafanaUrl = cfg.grafanaUrl;
22409
+ if (!grafanaUrl) {
22410
+ const otlpEndpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
22411
+ if (otlpEndpoint) {
22412
+ const url = new URL(otlpEndpoint);
22413
+ const host = url.hostname;
22414
+ for (const port of ["3000", "8001", "80"]) {
22415
+ try {
22416
+ const testUrl = `http://${host}:${port}/api/health`;
22417
+ const resp = await httpGet(testUrl, cfg.authToken, 2e3);
22418
+ if (resp && resp.includes('"database"')) {
22419
+ grafanaUrl = `http://${host}:${port}`;
22420
+ break;
22421
+ }
22422
+ } catch {
22423
+ }
22424
+ }
22425
+ }
22426
+ }
22427
+ if (!grafanaUrl) return null;
22428
+ try {
22429
+ let dsId = cfg.grafanaDatasourceId;
22430
+ if (!dsId) {
22431
+ const dsResp = await httpGet(`${grafanaUrl}/api/datasources`, cfg.authToken);
22432
+ if (dsResp) {
22433
+ const datasources = JSON.parse(dsResp);
22434
+ const tempo = datasources.find((d) => d.type === "tempo");
22435
+ if (tempo) dsId = tempo.id;
22436
+ }
22437
+ }
22438
+ if (!dsId) return null;
22439
+ const traceUrl = `${grafanaUrl}/api/datasources/proxy/${dsId}/api/traces/${traceId}`;
22440
+ const resp = await httpGet(traceUrl, cfg.authToken);
22441
+ if (!resp) return null;
22442
+ const data = JSON.parse(resp);
22443
+ return parseOTLPResponse(data);
22444
+ } catch (err) {
22445
+ logger.debug(`[TraceSerializer] Grafana Tempo fetch failed: ${err}`);
22446
+ return null;
22447
+ }
22448
+ }
22449
+ async function fetchFromJaeger(traceId, cfg) {
22450
+ let jaegerUrl = cfg.jaegerUrl;
22451
+ if (!jaegerUrl) {
22452
+ const otlpEndpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
22453
+ const host = otlpEndpoint ? new URL(otlpEndpoint).hostname : "localhost";
22454
+ for (const port of ["16686"]) {
22455
+ try {
22456
+ const testUrl = `http://${host}:${port}/api/services`;
22457
+ const resp = await httpGet(testUrl, void 0, 2e3);
22458
+ if (resp && resp.includes('"data"')) {
22459
+ jaegerUrl = `http://${host}:${port}`;
22460
+ break;
22461
+ }
22462
+ } catch {
22463
+ }
22464
+ }
22465
+ }
22466
+ if (!jaegerUrl) return null;
22467
+ try {
22468
+ const traceUrl = `${jaegerUrl}/api/traces/${traceId}`;
22469
+ const resp = await httpGet(traceUrl, cfg.authToken);
22470
+ if (!resp) return null;
22471
+ const data = JSON.parse(resp);
22472
+ return parseOTLPResponse(data);
22473
+ } catch (err) {
22474
+ logger.debug(`[TraceSerializer] Jaeger fetch failed: ${err}`);
22475
+ return null;
22476
+ }
22477
+ }
22478
+ async function fetchFromLocalFiles(traceId, cfg) {
22479
+ const traceFile = await findTraceFile(traceId, cfg.traceDir);
22480
+ if (!traceFile) return null;
22481
+ try {
22482
+ const { parseNDJSONTrace: parseNDJSONTrace2 } = await Promise.resolve().then(() => (init_trace_reader(), trace_reader_exports));
22483
+ const trace2 = await parseNDJSONTrace2(traceFile);
22484
+ return parseLocalNDJSONSpans(trace2.spans);
22485
+ } catch (err) {
22486
+ logger.debug(`[TraceSerializer] Local file parse failed: ${err}`);
22487
+ return null;
22488
+ }
22489
+ }
22490
+ async function httpGet(url, authToken, timeoutMs) {
22491
+ try {
22492
+ const controller = new AbortController();
22493
+ const timeout = setTimeout(() => controller.abort(), timeoutMs || 1e4);
22494
+ const headers = {};
22495
+ if (authToken) headers["Authorization"] = `Bearer ${authToken}`;
22496
+ const resp = await fetch(url, {
22497
+ signal: controller.signal,
22498
+ headers
22499
+ });
22500
+ clearTimeout(timeout);
22501
+ if (!resp.ok) return null;
22502
+ return await resp.text();
22503
+ } catch {
22504
+ return null;
22505
+ }
22506
+ }
22507
+ async function findTraceFile(traceId, traceDir) {
22508
+ const dir = traceDir || process.env.VISOR_TRACE_DIR || "output/traces";
22509
+ if (!fs15.existsSync(dir)) return null;
22510
+ const files = fs15.readdirSync(dir).filter((f) => f.endsWith(".ndjson"));
22511
+ for (const file of files) {
22512
+ const filePath = path17.join(dir, file);
22513
+ try {
22514
+ const firstLine = await readFirstLine(filePath);
22515
+ if (!firstLine) continue;
22516
+ const parsed = JSON.parse(firstLine);
22517
+ if (parsed.traceId === traceId) return filePath;
22518
+ } catch {
22519
+ }
22520
+ }
22521
+ return null;
22522
+ }
22523
+ async function readFirstLine(filePath) {
22524
+ return new Promise((resolve17, reject) => {
22525
+ const stream = fs15.createReadStream(filePath, { encoding: "utf-8" });
22526
+ const rl = readline2.createInterface({ input: stream, crlfDelay: Infinity });
22527
+ let resolved = false;
22528
+ rl.on("line", (line) => {
22529
+ if (!resolved) {
22530
+ resolved = true;
22531
+ rl.close();
22532
+ stream.destroy();
22533
+ resolve17(line.trim() || null);
22534
+ }
22535
+ });
22536
+ rl.on("close", () => {
22537
+ if (!resolved) resolve17(null);
22538
+ });
22539
+ rl.on("error", reject);
22540
+ });
22541
+ }
22542
+ function isNoiseSpan(span) {
22543
+ return NOISE_SPAN_NAMES.has(span.name);
22544
+ }
22545
+ function isWrapperSpan(span) {
22546
+ return WRAPPER_SPAN_NAMES.has(span.name);
22547
+ }
22548
+ function buildSpanTree(spans) {
22549
+ const filtered = spans.filter((s) => !isNoiseSpan(s));
22550
+ const nodeMap = /* @__PURE__ */ new Map();
22551
+ for (const span of filtered) {
22552
+ nodeMap.set(span.spanId, { span, children: [] });
22553
+ }
22554
+ let root;
22555
+ for (const span of filtered) {
22556
+ const node = nodeMap.get(span.spanId);
22557
+ if (!span.parentSpanId) {
22558
+ root = node;
22559
+ } else {
22560
+ let parentId = span.parentSpanId;
22561
+ while (parentId && !nodeMap.has(parentId)) {
22562
+ const parentSpan = spans.find((s) => s.spanId === parentId);
22563
+ parentId = parentSpan?.parentSpanId;
22564
+ }
22565
+ if (parentId) {
22566
+ const parent = nodeMap.get(parentId);
22567
+ if (parent) parent.children.push(node);
22568
+ } else if (!root) {
22569
+ root = node;
22570
+ }
22571
+ }
22572
+ }
22573
+ if (!root) {
22574
+ const sorted = [...nodeMap.values()].sort((a, b) => b.span.durationMs - a.span.durationMs);
22575
+ root = sorted[0] || { span: filtered[0], children: [] };
22576
+ }
22577
+ const sortChildren = (node) => {
22578
+ node.children.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);
22579
+ node.children.forEach(sortChildren);
22580
+ };
22581
+ sortChildren(root);
22582
+ const unwrap = (node) => {
22583
+ node.children = node.children.map(unwrap);
22584
+ const newChildren = [];
22585
+ for (const child of node.children) {
22586
+ if (isWrapperSpan(child.span)) {
22587
+ newChildren.push(...child.children);
22588
+ } else {
22589
+ newChildren.push(child);
22590
+ }
22591
+ }
22592
+ node.children = newChildren;
22593
+ return node;
22594
+ };
22595
+ unwrap(root);
22596
+ const removeDelegateEchos = (node) => {
22597
+ const hasDelegateChild = node.children.some((c) => c.span.name === "search.delegate");
22598
+ if (hasDelegateChild) {
22599
+ node.children = node.children.filter((c) => {
22600
+ if (c.span.name !== "probe.event.tool.result") return true;
22601
+ const toolName = c.span.attributes["tool.name"];
22602
+ return toolName !== "search";
22603
+ });
22604
+ }
22605
+ node.children.forEach(removeDelegateEchos);
22606
+ };
22607
+ removeDelegateEchos(root);
22608
+ return root;
22609
+ }
22610
+ function dedupeKey(text) {
22611
+ return text.replace(/\s+/g, " ").trim().slice(0, 100).toLowerCase();
22612
+ }
22613
+ function dedupeOrRegister(ctx, kind, text, spanName) {
22614
+ if (!text || text.length < 20) return null;
22615
+ const key = dedupeKey(text);
22616
+ if (!key) return null;
22617
+ const map = ctx[kind];
22618
+ const existing = map.get(key);
22619
+ if (existing && existing !== spanName) {
22620
+ return existing;
22621
+ }
22622
+ const otherMap = kind === "outputs" ? ctx.intents : ctx.outputs;
22623
+ const crossRef = otherMap.get(key);
22624
+ if (crossRef && crossRef !== spanName) {
22625
+ map.set(key, spanName);
22626
+ return crossRef;
22627
+ }
22628
+ map.set(key, spanName);
22629
+ return null;
22630
+ }
22631
+ async function serializeTraceForPrompt(traceIdOrPath, maxChars, backendConfig, taskResponse) {
22632
+ let spans;
22633
+ if (traceIdOrPath.includes("/") || traceIdOrPath.endsWith(".ndjson")) {
22634
+ const { parseNDJSONTrace: parseNDJSONTrace2 } = await Promise.resolve().then(() => (init_trace_reader(), trace_reader_exports));
22635
+ const trace2 = await parseNDJSONTrace2(traceIdOrPath);
22636
+ spans = parseLocalNDJSONSpans(trace2.spans);
22637
+ } else {
22638
+ spans = await fetchTraceSpans(traceIdOrPath, backendConfig);
22639
+ }
22640
+ if (spans.length === 0) {
22641
+ return "(no trace data available)";
22642
+ }
22643
+ const tree = buildSpanTree(spans);
22644
+ const routeIntentTopic = extractRouteIntentTopic(spans);
22645
+ const fullOutput = (maxChars ?? 4e3) > 1e5;
22646
+ return renderSpanYaml(tree, spans, {
22647
+ maxChars: maxChars ?? 4e3,
22648
+ fallbackIntent: routeIntentTopic,
22649
+ fullOutput,
22650
+ taskResponse
22651
+ });
22652
+ }
22653
+ function renderSpanYaml(tree, allSpans, opts) {
22654
+ const fullOutput = opts?.fullOutput ?? false;
22655
+ const maxLen = fullOutput ? 1e5 : 120;
22656
+ const dedup = { outputs: /* @__PURE__ */ new Map(), intents: /* @__PURE__ */ new Map() };
22657
+ const lines = [];
22658
+ renderYamlNode(tree, 0, lines, dedup, opts?.fallbackIntent, fullOutput, maxLen);
22659
+ if (opts?.taskResponse) {
22660
+ while (lines.length > 0 && /^\s*output:\s*=\s*\S+/.test(lines[lines.length - 1])) {
22661
+ lines.pop();
22662
+ }
22663
+ const ml = fullOutput ? 1e5 : 500;
22664
+ const text = opts.taskResponse.replace(/\*\*/g, "").replace(/`/g, "").trim();
22665
+ if (fullOutput) {
22666
+ lines.push(" response: |");
22667
+ for (const line of text.split("\n")) {
22668
+ lines.push(` ${line}`);
22669
+ }
22670
+ } else {
22671
+ const truncated = truncate(text.replace(/\n/g, " "), ml);
22672
+ lines.push(` response: ${truncated}`);
22673
+ if (text.length > ml) {
22674
+ lines.push(" # use --full for complete response");
22675
+ }
22676
+ }
22677
+ }
22678
+ return lines.join("\n");
22679
+ }
22680
+ function renderYamlNode(node, indent, lines, dedup, fallbackIntent, fullOutput, maxLen, parentSpan) {
22681
+ const pad = " ".repeat(indent);
22682
+ const attrs = node.span.attributes;
22683
+ const duration = formatDurationMs(node.span.durationMs);
22684
+ const name = node.span.name;
22685
+ const ml = maxLen ?? 120;
22686
+ const parentCheckId = parentSpan?.attributes["visor.check.id"];
22687
+ const parentCheckName = parentCheckId ? String(parentCheckId).replace(/^visor\.check\./, "") : void 0;
22688
+ const displayName = name === "ai.request" && parentCheckName ? parentCheckName : String(attrs["visor.check.id"] || name).replace(/^visor\.check\./, "");
22689
+ const toolName = attrs["tool.name"] || attrs["visor.tool.name"];
22690
+ if (toolName) {
22691
+ const toolInput = extractToolInput(String(toolName), attrs);
22692
+ const toolResultLen = attrs["tool.result.length"] || attrs["tool.result.count"];
22693
+ const tn = String(toolName);
22694
+ const isSearchTool = tn === "search" || tn === "searchCode" || tn === "search_code";
22695
+ const numLen = toolResultLen ? Number(toolResultLen) : -1;
22696
+ const noResults = isSearchTool && numLen >= 0 && numLen < 500;
22697
+ const resultSize = noResults ? " \u2192 no results" : toolResultLen ? ` \u2192 ${formatSize(numLen)}` : "";
22698
+ const successMark = attrs["tool.success"] === false ? " \u2717" : "";
22699
+ lines.push(`${pad}- ${tn}(${toolInput})${resultSize}${successMark}`);
22700
+ return;
22701
+ }
22702
+ if (name === "search.delegate") {
22703
+ const query = attrs["search.query"] || "";
22704
+ lines.push(`${pad}search.delegate("${truncate(String(query), 80)}") \u2014 ${duration}:`);
22705
+ for (const child of node.children) {
22706
+ renderYamlNode(
22707
+ child,
22708
+ indent + 1,
22709
+ lines,
22710
+ dedup,
22711
+ fallbackIntent,
22712
+ fullOutput,
22713
+ maxLen,
22714
+ node.span
22715
+ );
22716
+ }
22717
+ return;
22718
+ }
22719
+ if (name === "ai.request") {
22720
+ const model = attrs["ai.model"] || attrs["gen_ai.request.model"] || "?";
22721
+ const tokensIn = attrs["ai.input_length"] || attrs["gen_ai.usage.input_tokens"] || "";
22722
+ const tokensOut = attrs["gen_ai.usage.output_tokens"] || "";
22723
+ const tokenParts = [];
22724
+ if (tokensIn) tokenParts.push(`${tokensIn} in`);
22725
+ if (tokensOut) tokenParts.push(`${tokensOut} out`);
22726
+ const tokenStr = tokenParts.length > 0 ? ` \u2014 ${tokenParts.join(", ")}` : "";
22727
+ const hasChildren2 = node.children.length > 0;
22728
+ lines.push(`${pad}ai: ${model} \u2014 ${duration}${tokenStr}${hasChildren2 ? ":" : ""}`);
22729
+ const aiInput = String(attrs["ai.input"] || "");
22730
+ let intent = extractAIIntent(aiInput, ml);
22731
+ if (!intent && parentSpan) {
22732
+ const promptPreview = String(
22733
+ parentSpan.attributes["visor.provider.request.prompt.preview"] || ""
22734
+ );
22735
+ if (promptPreview) intent = extractAIIntent(promptPreview, ml);
22736
+ if (!intent) {
22737
+ const inputOutputs = String(parentSpan.attributes["visor.check.input.outputs"] || "");
22738
+ if (inputOutputs) {
22739
+ try {
22740
+ const o = JSON.parse(inputOutputs);
22741
+ const t = o["route-intent"]?.topic;
22742
+ if (t) intent = truncate(String(t), ml);
22743
+ } catch {
22744
+ }
22745
+ }
22746
+ }
22747
+ }
22748
+ if (!intent && fallbackIntent && parentSpan?.name !== "search.delegate") {
22749
+ intent = fallbackIntent;
22750
+ }
22751
+ if (intent) {
22752
+ const intentRef = dedupeOrRegister(dedup, "intents", intent, displayName);
22753
+ if (intentRef) {
22754
+ lines.push(`${pad} intent: = ${intentRef}`);
22755
+ } else {
22756
+ lines.push(`${pad} intent: ${intent}`);
22757
+ }
22758
+ }
22759
+ for (const child of node.children) {
22760
+ renderYamlNode(
22761
+ child,
22762
+ indent + 1,
22763
+ lines,
22764
+ dedup,
22765
+ fallbackIntent,
22766
+ fullOutput,
22767
+ maxLen,
22768
+ node.span
22769
+ );
22770
+ }
22771
+ if (parentSpan) {
22772
+ const checkOutput = String(parentSpan.attributes["visor.check.output"] || "");
22773
+ if (checkOutput) {
22774
+ renderYamlOutput(
22775
+ checkOutput,
22776
+ `${pad} `,
22777
+ "output",
22778
+ displayName,
22779
+ dedup,
22780
+ lines,
22781
+ fullOutput,
22782
+ ml
22783
+ );
22784
+ }
22785
+ }
22786
+ return;
22787
+ }
22788
+ if (name === "visor.run") {
22789
+ const source = attrs["visor.run.source"] || "";
22790
+ const visorVersion = attrs["visor.version"] || "";
22791
+ const probeVersion = attrs["probe.version"] || "";
22792
+ const slackUser = attrs["slack.user_id"] || "";
22793
+ lines.push(`${pad}visor.run:`);
22794
+ lines.push(`${pad} trace_id: ${node.span.traceId}`);
22795
+ if (visorVersion) lines.push(`${pad} visor: ${visorVersion}`);
22796
+ if (probeVersion) lines.push(`${pad} probe: ${probeVersion}`);
22797
+ if (source) lines.push(`${pad} source: ${source}`);
22798
+ if (slackUser) lines.push(`${pad} slack_user: ${slackUser}`);
22799
+ lines.push(`${pad} duration: ${duration}`);
22800
+ for (const child of node.children) {
22801
+ renderYamlNode(
22802
+ child,
22803
+ indent + 1,
22804
+ lines,
22805
+ dedup,
22806
+ fallbackIntent,
22807
+ fullOutput,
22808
+ maxLen,
22809
+ node.span
22810
+ );
22811
+ }
22812
+ return;
22813
+ }
22814
+ const checkId = attrs["visor.check.id"];
22815
+ const checkType = attrs["visor.check.type"];
22816
+ if (checkId || name.startsWith("visor.check.")) {
22817
+ const cleanName = String(checkId || name).replace(/^visor\.check\./, "");
22818
+ const errMark2 = node.span.status === "error" ? " \u2717" : "";
22819
+ lines.push(`${pad}${cleanName}:${errMark2}`);
22820
+ if (checkType) lines.push(`${pad} type: ${checkType}`);
22821
+ lines.push(`${pad} duration: ${duration}`);
22822
+ const inputContext = String(attrs["visor.check.input.context"] || "");
22823
+ const inputOutputs = String(attrs["visor.check.input.outputs"] || "");
22824
+ const question = extractQuestionFromContext(inputContext, inputOutputs);
22825
+ if (question || inputOutputs && inputOutputs !== "{}") {
22826
+ renderYamlInput(inputOutputs, `${pad} `, lines, fullOutput, ml, question);
22827
+ }
22828
+ for (const child of node.children) {
22829
+ renderYamlNode(
22830
+ child,
22831
+ indent + 1,
22832
+ lines,
22833
+ dedup,
22834
+ fallbackIntent,
22835
+ fullOutput,
22836
+ maxLen,
22837
+ node.span
22838
+ );
22839
+ }
22840
+ const hasDirectAiChild = node.children.some((c) => c.span.name === "ai.request");
22841
+ if (!hasDirectAiChild) {
22842
+ const output = String(attrs["visor.check.output"] || "");
22843
+ if (output) {
22844
+ renderYamlOutput(output, `${pad} `, "output", cleanName, dedup, lines, fullOutput, ml);
22845
+ }
22846
+ }
22847
+ return;
22848
+ }
22849
+ const errMark = node.span.status === "error" ? " \u2717" : "";
22850
+ const hasChildren = node.children.length > 0;
22851
+ lines.push(`${pad}${name} \u2014 ${duration}${errMark}${hasChildren ? ":" : ""}`);
22852
+ for (const child of node.children) {
22853
+ renderYamlNode(child, indent + 1, lines, dedup, fallbackIntent, fullOutput, maxLen, node.span);
22854
+ }
22855
+ }
22856
+ function renderYamlOutput(rawOutput, pad, label, spanName, dedup, lines, fullOutput, maxLen) {
22857
+ const ml = maxLen ?? 120;
22858
+ let obj;
22859
+ try {
22860
+ obj = JSON.parse(rawOutput);
22861
+ } catch {
22862
+ obj = parseTruncatedJson(rawOutput);
22863
+ if (!obj || typeof obj !== "object") return;
22864
+ }
22865
+ if (typeof obj === "object" && !Array.isArray(obj)) {
22866
+ const keys = Object.keys(obj);
22867
+ if (keys.length === 1 && typeof obj[keys[0]] === "object" && obj[keys[0]] !== null) {
22868
+ obj = obj[keys[0]];
22869
+ }
22870
+ const objKeys = Object.keys(obj);
22871
+ if (objKeys.length === 1 && objKeys[0] === "text" && typeof obj.text === "string") {
22872
+ const text = obj.text.replace(/\*\*/g, "").replace(/`/g, "").trim();
22873
+ const flat = text.replace(/\n/g, " ");
22874
+ const preview2 = fullOutput ? flat : truncate(flat, ml);
22875
+ const ref2 = dedupeOrRegister(dedup, "outputs", truncate(flat, 100), spanName);
22876
+ if (ref2) {
22877
+ lines.push(`${pad}${label}: = ${ref2}`);
22878
+ } else {
22879
+ lines.push(`${pad}${label}: ${preview2}`);
22880
+ }
22881
+ return;
22882
+ }
22883
+ }
22884
+ const preview = formatJsonPreview(obj, 200);
22885
+ if (!preview) return;
22886
+ const ref = dedupeOrRegister(dedup, "outputs", preview, spanName);
22887
+ if (ref) {
22888
+ lines.push(`${pad}${label}: = ${ref}`);
22889
+ return;
22890
+ }
22891
+ renderYamlValue(obj, pad, label, lines, fullOutput, ml);
22892
+ }
22893
+ function renderYamlValue(val, pad, key, lines, fullOutput, maxLen, depth) {
22894
+ const ml = maxLen ?? 120;
22895
+ const d = depth ?? 0;
22896
+ if (val === null || val === void 0) return;
22897
+ if (typeof val === "boolean" || typeof val === "number") {
22898
+ lines.push(`${pad}${key}: ${val}`);
22899
+ return;
22900
+ }
22901
+ if (typeof val === "string") {
22902
+ if (val.startsWith("{") || val.startsWith("[")) return;
22903
+ const clean = val.replace(/\*\*/g, "").replace(/`/g, "").trim();
22904
+ if (fullOutput && clean.length > 100 && clean.includes("\n")) {
22905
+ lines.push(`${pad}${key}: |`);
22906
+ for (const line of clean.split("\n").slice(0, fullOutput ? 500 : 5)) {
22907
+ lines.push(`${pad} ${line}`);
22908
+ }
22909
+ } else {
22910
+ const flat = clean.replace(/\n/g, " ");
22911
+ const truncVal = fullOutput ? flat : truncate(flat, ml);
22912
+ lines.push(`${pad}${key}: ${truncVal}`);
22913
+ }
22914
+ return;
22915
+ }
22916
+ if (Array.isArray(val)) {
22917
+ if (val.length === 0) {
22918
+ lines.push(`${pad}${key}: []`);
22919
+ return;
22920
+ }
22921
+ const maxItems = fullOutput ? 20 : 3;
22922
+ lines.push(`${pad}${key}:`);
22923
+ for (let i = 0; i < Math.min(val.length, maxItems); i++) {
22924
+ const item = val[i];
22925
+ if (typeof item === "object" && item !== null) {
22926
+ const entries = Object.entries(item).filter(
22927
+ ([k]) => k !== "raw" && k !== "skills" && k !== "tags"
22928
+ );
22929
+ if (entries.length > 0) {
22930
+ const [firstKey, firstVal] = entries[0];
22931
+ if (firstVal === null || firstVal === void 0 || typeof firstVal !== "object") {
22932
+ const sv = typeof firstVal === "string" ? fullOutput ? firstVal.split("\n")[0] : truncate(firstVal.split("\n")[0], ml) : String(firstVal ?? "");
22933
+ lines.push(`${pad} - ${firstKey}: ${sv}`);
22934
+ } else {
22935
+ lines.push(`${pad} - ${firstKey}:`);
22936
+ for (const [ck, cv] of Object.entries(firstVal)) {
22937
+ if (ck === "raw" || ck === "skills" || ck === "tags") continue;
22938
+ renderYamlValue(cv, `${pad} `, ck, lines, fullOutput, ml, d + 2);
22939
+ }
22940
+ }
22941
+ for (let j = 1; j < entries.length; j++) {
22942
+ const [k, v] = entries[j];
22943
+ renderYamlValue(v, `${pad} `, k, lines, fullOutput, ml, d + 1);
22944
+ }
22945
+ }
22946
+ } else {
22947
+ lines.push(`${pad} - ${truncate(String(item), ml)}`);
22948
+ }
22949
+ }
22950
+ if (val.length > maxItems) {
22951
+ lines.push(`${pad} # ... ${val.length - maxItems} more`);
22952
+ }
22953
+ return;
22954
+ }
22955
+ if (typeof val === "object") {
22956
+ if (d > 3) {
22957
+ const keys = Object.keys(val);
22958
+ lines.push(`${pad}${key}: {${keys.slice(0, 4).join(", ")}${keys.length > 4 ? ", ..." : ""}}`);
22959
+ return;
22960
+ }
22961
+ lines.push(`${pad}${key}:`);
22962
+ for (const [k, v] of Object.entries(val)) {
22963
+ if (k === "raw" || k === "skills" || k === "tags") continue;
22964
+ renderYamlValue(v, `${pad} `, k, lines, fullOutput, ml, d + 1);
22965
+ }
22966
+ }
22967
+ }
22968
+ function extractQuestionFromContext(contextStr, inputOutputsStr) {
22969
+ if (!contextStr) return void 0;
22970
+ try {
22971
+ const ctx = JSON.parse(contextStr);
22972
+ const outputs = ctx.outputs || {};
22973
+ const routeIntent = outputs["route-intent"];
22974
+ if (routeIntent) {
22975
+ const topic = routeIntent.topic || routeIntent.intent || routeIntent.question;
22976
+ if (topic && typeof topic === "string") return topic;
22977
+ if (typeof routeIntent === "string") return routeIntent;
22978
+ }
22979
+ const args = ctx.args || {};
22980
+ if (args.topic && typeof args.topic === "string") return args.topic;
22981
+ if (args.question && typeof args.question === "string") return args.question;
22982
+ if (args.intent && typeof args.intent === "string") return args.intent;
22983
+ for (const key of Object.keys(outputs)) {
22984
+ const val = outputs[key];
22985
+ if (typeof val === "object" && val !== null) {
22986
+ if (val.topic && typeof val.topic === "string") {
22987
+ try {
22988
+ const depOutputs = JSON.parse(inputOutputsStr);
22989
+ if (depOutputs[key]) continue;
22990
+ } catch {
22991
+ }
22992
+ return val.topic;
22993
+ }
22994
+ }
22995
+ }
22996
+ } catch {
22997
+ const topicMatch = contextStr.match(/"topic"\s*:\s*"([^"]+)"/);
22998
+ if (topicMatch) return topicMatch[1];
22999
+ }
23000
+ return void 0;
23001
+ }
23002
+ function renderYamlInput(inputOutputsStr, pad, lines, fullOutput, maxLen, question) {
23003
+ const ml = maxLen ?? 120;
23004
+ if (question) {
23005
+ lines.push(`${pad}input: ${truncate(question, fullOutput ? 1e5 : ml)}`);
23006
+ }
23007
+ try {
23008
+ const inputs = JSON.parse(inputOutputsStr);
23009
+ if (typeof inputs !== "object" || inputs === null) return;
23010
+ const keys = Object.keys(inputs);
23011
+ if (keys.length === 0) return;
23012
+ if (!question) lines.push(`${pad}input:`);
23013
+ for (const key of keys) {
23014
+ const val = inputs[key];
23015
+ if (typeof val === "object" && val !== null) {
23016
+ renderYamlValue(val, `${pad} `, key, lines, fullOutput, ml, 0);
23017
+ } else {
23018
+ lines.push(`${pad} ${key}: ${truncate(String(val), ml)}`);
23019
+ }
23020
+ }
23021
+ } catch {
23022
+ }
23023
+ }
23024
+ function extractToolInput(toolName, attrs) {
23025
+ const result = String(attrs["tool.result"] || "");
23026
+ const explicitInput = String(attrs["tool.input"] || "");
23027
+ if (explicitInput) return truncate(explicitInput, 80);
23028
+ switch (toolName) {
23029
+ case "search": {
23030
+ const patMatch = result.match(/Pattern: (.+)/);
23031
+ const pathMatch = result.match(/Path: \S+\/([^\s/]+)\s/);
23032
+ const pattern = patMatch ? patMatch[1].trim() : "";
23033
+ const inPath = pathMatch ? pathMatch[1] : "";
23034
+ if (pattern && inPath) return `"${truncate(pattern, 50)}" in ${inPath}`;
23035
+ if (pattern) return `"${truncate(pattern, 60)}"`;
23036
+ return "";
23037
+ }
23038
+ case "extract": {
23039
+ const fileMatch = result.match(/Files to extract:\n\s*(\S+)/);
23040
+ if (fileMatch) {
23041
+ const filePath = fileMatch[1];
23042
+ const parts = filePath.split("/");
23043
+ return parts.length > 2 ? parts.slice(-2).join("/") : parts[parts.length - 1];
23044
+ }
23045
+ return "";
23046
+ }
23047
+ case "listFiles": {
23048
+ const pathMatch = result.match(/^(\S+):/);
23049
+ if (pathMatch) {
23050
+ const parts = pathMatch[1].split("/");
23051
+ return parts[parts.length - 1] || "";
23052
+ }
23053
+ return "";
23054
+ }
23055
+ default:
23056
+ return truncate(explicitInput, 60);
23057
+ }
23058
+ }
23059
+ function extractAIIntent(input, maxLen = 150) {
23060
+ if (!input || input.length < 20) return "";
23061
+ const qMatch = input.match(/<question>([\s\S]*?)<\/question>/);
23062
+ if (qMatch) return truncate(qMatch[1].trim(), maxLen);
23063
+ const crMatch = input.match(/## Current Request\s*\n(?:User: )?(.+)/);
23064
+ if (crMatch) return truncate(crMatch[1].trim(), maxLen);
23065
+ const userMatch = input.match(/(?:^|\n)User: (.+)/);
23066
+ if (userMatch) return truncate(userMatch[1].trim(), maxLen);
23067
+ const pmMatch = input.match(/Primary message[^:]*:\s*\n(.+)/);
23068
+ if (pmMatch) return truncate(pmMatch[1].trim(), maxLen);
23069
+ return "";
23070
+ }
23071
+ function formatJsonPreview(obj, maxLen) {
23072
+ if (obj === null || obj === void 0) return "";
23073
+ if (typeof obj !== "object") return truncate(String(obj), maxLen);
23074
+ if (Array.isArray(obj)) {
23075
+ if (obj.length === 0) return "[]";
23076
+ const first = typeof obj[0] === "object" && obj[0] !== null ? obj[0].project_id || obj[0].id || obj[0].name || Object.keys(obj[0])[0] || "..." : String(obj[0]);
23077
+ return `[${obj.length}] ${truncate(String(first), 30)}${obj.length > 1 ? ", ..." : ""}`;
23078
+ }
23079
+ const parts = [];
23080
+ let len = 2;
23081
+ for (const [key, val] of Object.entries(obj)) {
23082
+ if (key === "raw" || key === "skills" || key === "tags") continue;
23083
+ let valStr;
23084
+ if (val === null || val === void 0) continue;
23085
+ if (typeof val === "boolean") valStr = String(val);
23086
+ else if (typeof val === "number") valStr = String(val);
23087
+ else if (typeof val === "string") {
23088
+ if (val.startsWith("{") || val.startsWith("[")) continue;
23089
+ const clean = val.replace(/\*\*/g, "").replace(/^#+\s*/gm, "").replace(/`/g, "").trim();
23090
+ valStr = `"${truncate(clean.split("\n")[0], Math.min(80, maxLen / 3))}"`;
23091
+ } else if (Array.isArray(val)) valStr = `[${val.length}]`;
23092
+ else if (typeof val === "object") valStr = `{${Object.keys(val).length} keys}`;
23093
+ else valStr = "...";
23094
+ const part = `${key}: ${valStr}`;
23095
+ if (len + part.length + 2 > maxLen) {
23096
+ parts.push("...");
23097
+ break;
23098
+ }
23099
+ parts.push(part);
23100
+ len += part.length + 2;
23101
+ }
23102
+ return `{${parts.join(", ")}}`;
23103
+ }
23104
+ function parseTruncatedJson(input) {
23105
+ let pos = 0;
23106
+ const len = input.length;
23107
+ function skipWhitespace() {
23108
+ while (pos < len && " \n\r".includes(input[pos])) pos++;
23109
+ }
23110
+ function parseString() {
23111
+ if (input[pos] !== '"') return "";
23112
+ pos++;
23113
+ let result = "";
23114
+ while (pos < len) {
23115
+ const ch = input[pos];
23116
+ if (ch === "\\" && pos + 1 < len) {
23117
+ const next = input[pos + 1];
23118
+ if (next === "n") {
23119
+ result += "\n";
23120
+ pos += 2;
23121
+ continue;
23122
+ }
23123
+ if (next === "t") {
23124
+ result += " ";
23125
+ pos += 2;
23126
+ continue;
23127
+ }
23128
+ if (next === '"') {
23129
+ result += '"';
23130
+ pos += 2;
23131
+ continue;
23132
+ }
23133
+ if (next === "\\") {
23134
+ result += "\\";
23135
+ pos += 2;
23136
+ continue;
23137
+ }
23138
+ result += next;
23139
+ pos += 2;
23140
+ continue;
23141
+ }
23142
+ if (ch === '"') {
23143
+ pos++;
23144
+ return result;
23145
+ }
23146
+ result += ch;
23147
+ pos++;
23148
+ }
23149
+ return result;
23150
+ }
23151
+ function parseNumber() {
23152
+ const start = pos;
23153
+ if (input[pos] === "-") pos++;
23154
+ while (pos < len && input[pos] >= "0" && input[pos] <= "9") pos++;
23155
+ if (pos < len && input[pos] === ".") {
23156
+ pos++;
23157
+ while (pos < len && input[pos] >= "0" && input[pos] <= "9") pos++;
23158
+ }
23159
+ return Number(input.slice(start, pos));
23160
+ }
23161
+ function parseValue() {
23162
+ skipWhitespace();
23163
+ if (pos >= len) return void 0;
23164
+ const ch = input[pos];
23165
+ if (ch === '"') return parseString();
23166
+ if (ch === "{") return parseObject();
23167
+ if (ch === "[") return parseArray();
23168
+ if (ch === "t" && input.slice(pos, pos + 4) === "true") {
23169
+ pos += 4;
23170
+ return true;
23171
+ }
23172
+ if (ch === "f" && input.slice(pos, pos + 5) === "false") {
23173
+ pos += 5;
23174
+ return false;
23175
+ }
23176
+ if (ch === "n" && input.slice(pos, pos + 4) === "null") {
23177
+ pos += 4;
23178
+ return null;
23179
+ }
23180
+ if (ch === "-" || ch >= "0" && ch <= "9") return parseNumber();
23181
+ return void 0;
23182
+ }
23183
+ function parseObject() {
23184
+ const obj = {};
23185
+ pos++;
23186
+ skipWhitespace();
23187
+ while (pos < len && input[pos] !== "}") {
23188
+ skipWhitespace();
23189
+ if (pos >= len || input[pos] !== '"') break;
23190
+ const key = parseString();
23191
+ skipWhitespace();
23192
+ if (pos >= len || input[pos] !== ":") {
23193
+ obj[key] = void 0;
23194
+ break;
23195
+ }
23196
+ pos++;
23197
+ const val = parseValue();
23198
+ if (val !== void 0) obj[key] = val;
23199
+ skipWhitespace();
23200
+ if (pos < len && input[pos] === ",") pos++;
23201
+ }
23202
+ if (pos < len && input[pos] === "}") pos++;
23203
+ return obj;
23204
+ }
23205
+ function parseArray() {
23206
+ const arr = [];
23207
+ pos++;
23208
+ skipWhitespace();
23209
+ while (pos < len && input[pos] !== "]") {
23210
+ const val = parseValue();
23211
+ if (val !== void 0) arr.push(val);
23212
+ else break;
23213
+ skipWhitespace();
23214
+ if (pos < len && input[pos] === ",") pos++;
23215
+ skipWhitespace();
23216
+ }
23217
+ if (pos < len && input[pos] === "]") pos++;
23218
+ return arr;
23219
+ }
23220
+ return parseValue();
23221
+ }
23222
+ function extractRouteIntentTopic(spans) {
23223
+ const riSpan = spans.find((s) => s.attributes["visor.check.id"] === "route-intent");
23224
+ if (riSpan) {
23225
+ const output = String(riSpan.attributes["visor.check.output"] || "");
23226
+ if (output) {
23227
+ try {
23228
+ const obj = JSON.parse(output);
23229
+ if (obj.topic) return truncate(String(obj.topic), 150);
23230
+ } catch {
23231
+ }
23232
+ }
23233
+ }
23234
+ const classifySpan = spans.find((s) => s.attributes["visor.check.id"] === "classify");
23235
+ if (classifySpan) {
23236
+ const output = String(classifySpan.attributes["visor.check.output"] || "");
23237
+ if (output) {
23238
+ try {
23239
+ const obj = JSON.parse(output);
23240
+ if (obj.topic) return truncate(String(obj.topic), 150);
23241
+ } catch {
23242
+ }
23243
+ }
23244
+ }
23245
+ return void 0;
23246
+ }
23247
+ function formatSize(chars) {
23248
+ if (chars < 1e3) return `${chars} chars`;
23249
+ return `${(chars / 1e3).toFixed(1)}k chars`;
23250
+ }
23251
+ function formatDurationMs(ms) {
23252
+ if (ms < 0) return "0ms";
23253
+ if (ms < 1e3) return `${Math.round(ms)}ms`;
23254
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
23255
+ const mins = Math.floor(ms / 6e4);
23256
+ const secs = Math.round(ms % 6e4 / 1e3);
23257
+ return `${mins}m ${secs}s`;
23258
+ }
23259
+ function truncate(str, max) {
23260
+ if (typeof str !== "string") return "";
23261
+ if (str.length <= max) return str;
23262
+ const tail = Math.min(100, Math.floor(max / 3));
23263
+ const head = max - tail - 19;
23264
+ if (head < 10) return str.slice(0, max - 3) + "...";
23265
+ return str.slice(0, head) + " ...[truncated]... " + str.slice(-tail);
23266
+ }
23267
+ var fs15, readline2, path17, NOISE_SPAN_NAMES, WRAPPER_SPAN_NAMES;
23268
+ var init_trace_serializer = __esm({
23269
+ "src/agent-protocol/trace-serializer.ts"() {
23270
+ "use strict";
23271
+ fs15 = __toESM(require("fs"));
23272
+ readline2 = __toESM(require("readline"));
23273
+ path17 = __toESM(require("path"));
23274
+ init_logger();
23275
+ NOISE_SPAN_NAMES = /* @__PURE__ */ new Set([
23276
+ "engine.state.init",
23277
+ "engine.state.waveplanning",
23278
+ "engine.state.planready",
23279
+ "visor.sandbox.stopAll"
23280
+ ]);
23281
+ WRAPPER_SPAN_NAMES = /* @__PURE__ */ new Set([
23282
+ "engine.state.leveldispatch",
23283
+ "visor.ai_check",
23284
+ "probe.delegation"
23285
+ ]);
23286
+ }
23287
+ });
23288
+
23289
+ // src/agent-protocol/task-evaluator.ts
23290
+ var task_evaluator_exports = {};
23291
+ __export(task_evaluator_exports, {
23292
+ DEFAULT_EVALUATION_PROMPT: () => DEFAULT_EVALUATION_PROMPT,
23293
+ evaluateAndStore: () => evaluateAndStore,
23294
+ evaluateTask: () => evaluateTask
23295
+ });
23296
+ function buildEvaluationSchema(includeExecution) {
23297
+ const schema = {
23298
+ type: "object",
23299
+ required: ["response_quality", "overall_rating", "summary"],
23300
+ properties: {
23301
+ response_quality: {
23302
+ type: "object",
23303
+ required: ["rating", "category", "relevance", "completeness", "actionable", "reasoning"],
23304
+ properties: {
23305
+ rating: { type: "integer", minimum: 1, maximum: 5 },
23306
+ category: {
23307
+ type: "string",
23308
+ enum: ["excellent", "good", "adequate", "poor", "off-topic", "error"]
23309
+ },
23310
+ relevance: { type: "boolean" },
23311
+ completeness: { type: "boolean" },
23312
+ actionable: { type: "boolean" },
23313
+ reasoning: { type: "string" }
23314
+ }
23315
+ },
23316
+ overall_rating: { type: "integer", minimum: 1, maximum: 5 },
23317
+ summary: { type: "string" }
23318
+ }
23319
+ };
23320
+ if (includeExecution) {
23321
+ schema.required.push("execution_quality");
23322
+ schema.properties.execution_quality = {
23323
+ type: "object",
23324
+ required: ["rating", "category", "reasoning"],
23325
+ properties: {
23326
+ rating: { type: "integer", minimum: 1, maximum: 5 },
23327
+ category: { type: "string", enum: ["efficient", "adequate", "wasteful", "error"] },
23328
+ unnecessary_tool_calls: { type: "integer" },
23329
+ reasoning: { type: "string" }
23330
+ }
23331
+ };
23332
+ }
23333
+ return schema;
23334
+ }
23335
+ async function evaluateTask(taskId, store, config) {
23336
+ const { rows } = store.listTasksRaw({ limit: 500 });
23337
+ const match = rows.find((r) => r.id === taskId || r.id.startsWith(taskId));
23338
+ if (!match) {
23339
+ throw new Error(`Task not found: ${taskId}`);
23340
+ }
23341
+ const fullTask = store.getTask(match.id);
23342
+ if (!fullTask) {
23343
+ throw new Error(`Task data not found: ${match.id}`);
23344
+ }
23345
+ const requestText = match.request_message || "No request text available";
23346
+ let responseText = "No response available";
23347
+ if (fullTask.status?.message) {
23348
+ const parts = fullTask.status.message.parts ?? [];
23349
+ const textPart = parts.find((p) => typeof p.text === "string");
23350
+ if (textPart) {
23351
+ responseText = textPart.text;
23352
+ }
23353
+ }
23354
+ if (fullTask.status.state === "failed" && responseText === "No response available") {
23355
+ return {
23356
+ response_quality: {
23357
+ rating: 1,
23358
+ category: "error",
23359
+ relevance: false,
23360
+ completeness: false,
23361
+ actionable: false,
23362
+ reasoning: "Task failed without producing a response."
23363
+ },
23364
+ overall_rating: 1,
23365
+ summary: "Task failed without producing a response."
23366
+ };
23367
+ }
23368
+ let traceTree;
23369
+ const traceId = match.metadata?.trace_id;
23370
+ const traceFile = match.metadata?.trace_file;
23371
+ if (traceFile || traceId) {
23372
+ try {
23373
+ const traceRef = traceFile || traceId;
23374
+ traceTree = await serializeTraceForPrompt(
23375
+ traceRef,
23376
+ 1e6,
23377
+ { traceDir: config?.traceDir },
23378
+ responseText !== "No response available" ? responseText : void 0
23379
+ );
23380
+ if (traceTree === "(no trace data available)") {
23381
+ traceTree = void 0;
23382
+ }
23383
+ } catch (err) {
23384
+ logger.debug(
23385
+ `[TaskEvaluator] Failed to load trace: ${err instanceof Error ? err.message : err}`
23386
+ );
23387
+ }
23388
+ }
23389
+ const systemPrompt = config?.prompt || process.env.VISOR_EVAL_PROMPT || DEFAULT_EVALUATION_PROMPT;
23390
+ const hasTrace = !!traceTree;
23391
+ let userPrompt;
23392
+ if (traceTree) {
23393
+ userPrompt = `<user_request>
23394
+ ${requestText}
23395
+ </user_request>
23396
+
23397
+ <execution_trace>
23398
+ ${traceTree}
23399
+ </execution_trace>`;
23400
+ } else {
23401
+ userPrompt = `<user_request>
23402
+ ${requestText}
23403
+ </user_request>
23404
+
23405
+ <agent_response>
23406
+ ${responseText}
23407
+ </agent_response>`;
23408
+ }
23409
+ const { ProbeAgent: ProbeAgent2 } = require("@probelabs/probe");
23410
+ const model = config?.model || process.env.VISOR_EVAL_MODEL || process.env.VISOR_JUDGE_MODEL || void 0;
23411
+ const provider = config?.provider || process.env.VISOR_EVAL_PROVIDER || void 0;
23412
+ const agentOptions = {
23413
+ sessionId: `visor-task-eval-${Date.now()}`,
23414
+ systemPrompt,
23415
+ maxIterations: 1,
23416
+ disableTools: true
23417
+ };
23418
+ if (model) agentOptions.model = model;
23419
+ if (provider) agentOptions.provider = provider;
23420
+ if (config?.apiKey) {
23421
+ const envKey = provider === "openai" ? "OPENAI_API_KEY" : provider === "anthropic" ? "ANTHROPIC_API_KEY" : "GOOGLE_API_KEY";
23422
+ process.env[envKey] = config.apiKey;
23423
+ }
23424
+ const agent = new ProbeAgent2(agentOptions);
23425
+ if (typeof agent.initialize === "function") {
23426
+ await agent.initialize();
23427
+ }
23428
+ const jsonSchema = buildEvaluationSchema(hasTrace);
23429
+ const schemaStr = JSON.stringify(jsonSchema);
23430
+ const response = await agent.answer(userPrompt, void 0, { schema: schemaStr });
23431
+ let result;
23432
+ try {
23433
+ const cleaned = response.replace(/^```(?:json)?\s*\n?/m, "").replace(/\n?```\s*$/m, "").trim();
23434
+ result = JSON.parse(cleaned);
23435
+ } catch {
23436
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
23437
+ if (jsonMatch) {
23438
+ result = JSON.parse(jsonMatch[0]);
23439
+ } else {
23440
+ throw new Error(`Failed to parse evaluation response as JSON: ${response.slice(0, 200)}`);
23441
+ }
23442
+ }
23443
+ return result;
23444
+ }
23445
+ async function evaluateAndStore(taskId, store, config) {
23446
+ const result = await evaluateTask(taskId, store, config);
23447
+ const { rows } = store.listTasksRaw({ limit: 500 });
23448
+ const match = rows.find((r) => r.id === taskId || r.id.startsWith(taskId));
23449
+ if (match) {
23450
+ store.addArtifact(match.id, {
23451
+ artifact_id: import_crypto5.default.randomUUID(),
23452
+ name: "evaluation",
23453
+ parts: [{ text: JSON.stringify(result), media_type: "application/json" }]
23454
+ });
23455
+ }
23456
+ return result;
23457
+ }
23458
+ var import_crypto5, DEFAULT_EVALUATION_PROMPT;
23459
+ var init_task_evaluator = __esm({
23460
+ "src/agent-protocol/task-evaluator.ts"() {
23461
+ "use strict";
23462
+ import_crypto5 = __toESM(require("crypto"));
23463
+ init_logger();
23464
+ init_trace_serializer();
23465
+ DEFAULT_EVALUATION_PROMPT = `You are a task response quality evaluator for an AI agent system called Visor.
23466
+
23467
+ You will receive the user's original request and an execution trace inside <execution_trace> tags. The trace is a YAML-formatted view of the entire agent execution, including the final response. When no trace is available, the agent response is provided directly.
23468
+
23469
+ ## How to Read the Execution Trace
23470
+
23471
+ The trace is a tree of spans representing the agent's execution pipeline:
23472
+
23473
+ **Top-level: \`visor.run\`** \u2014 The root span with metadata:
23474
+ - \`trace_id\`: Unique execution identifier
23475
+ - \`visor\` / \`probe\`: Software versions
23476
+ - \`source\`: Where the request came from (e.g., "slack", "cli")
23477
+ - \`duration\`: Total wall-clock time
23478
+
23479
+ **Checks** \u2014 Named processing steps (e.g., \`route-intent\`, \`explore-code\`, \`generate-response\`):
23480
+ - \`type\`: "ai" (LLM-powered), "script" (deterministic), or "workflow" (sub-pipeline)
23481
+ - \`duration\`: How long this step took
23482
+ - \`input\`: What was passed to this check \u2014 may include an \`intent\` (the user's question as understood by the router) and dependency outputs
23483
+ - \`output\`: The check's result \u2014 may be structured JSON or plain text
23484
+
23485
+ **AI blocks** (\`ai: model-name\`) \u2014 Individual LLM calls within checks:
23486
+ - Shows model used, duration, and token counts (input/output)
23487
+ - \`intent\`: The question or instruction sent to the LLM
23488
+
23489
+ **Tool calls** \u2014 Listed as \`- toolName(input) \u2192 size\`:
23490
+ - \`search("query" in repo)\`: Code search. "\u2192 no results" means nothing was found; otherwise shows result size
23491
+ - \`extract(file/path)\`: File content extraction with result size
23492
+ - \`listFiles(dir)\`: Directory listing
23493
+ - \`bash()\`: Shell command execution
23494
+
23495
+ **Delegations** (\`search.delegate("query")\`) \u2014 Sub-agent searches:
23496
+ - Contains their own AI blocks and tool calls
23497
+ - Used for complex multi-step code exploration
23498
+
23499
+ **The \`response\` field** at the end of the trace is the final answer sent back to the user. This is the primary output to evaluate.
23500
+
23501
+ **Symbols:**
23502
+ - \`\u2717\` marks failed/error spans
23503
+ - \`= check-name\` means output is identical to that check's output (deduplication)
23504
+
23505
+ ## Evaluation Criteria
23506
+
23507
+ **Response Quality** (1-5):
23508
+ - **Relevance**: Does the response directly address what the user asked? A response about the wrong topic or that misunderstands the question scores low.
23509
+ - **Completeness**: Does it fully answer the question? Partial answers, missing key details, or surface-level responses score lower.
23510
+ - **Actionable**: Can the user act on this information? Vague or generic advice scores lower than specific, concrete answers with code references.
23511
+ - Rating: 5=excellent (thorough, specific, directly useful), 4=good (answers well but minor gaps), 3=adequate (addresses question but lacks depth), 2=poor (partially relevant or very incomplete), 1=off-topic or error
23512
+
23513
+ **Execution Quality** (1-5, only when trace is provided):
23514
+ - **Efficiency**: Were tool calls necessary and well-targeted? Good search queries that find results on the first try score high.
23515
+ - **Redundancy**: Were there duplicate searches, unnecessary re-searches with slightly different queries, or tools called for information already available?
23516
+ - **Delegation quality**: Were search delegations productive? Did they explore relevant code paths?
23517
+ - **Token usage**: Was input context kept reasonable, or did the agent load excessive amounts of code?
23518
+ - Rating: 5=efficient (minimal, targeted tool use), 4=adequate (minor redundancy), 3=some waste (noticeable unnecessary calls), 2=wasteful (many redundant searches or delegations), 1=error/broken execution
23519
+
23520
+ **Overall Rating** (1-5): Weighted combination \u2014 response quality matters most, execution quality is secondary. A perfect response from a wasteful execution still scores 3-4 overall.
23521
+
23522
+ You MUST respond with valid JSON matching the provided schema. Be specific in your reasoning \u2014 reference actual check names, tool calls, or response content.`;
23523
+ }
23524
+ });
23525
+
21926
23526
  // src/agent-protocol/track-execution.ts
21927
23527
  var track_execution_exports = {};
21928
23528
  __export(track_execution_exports, {
21929
23529
  trackExecution: () => trackExecution
21930
23530
  });
23531
+ function getPackageVersion() {
23532
+ try {
23533
+ return require_package()?.version || "dev";
23534
+ } catch {
23535
+ return "dev";
23536
+ }
23537
+ }
21931
23538
  async function trackExecution(opts, executor) {
21932
23539
  const { taskStore, source, configPath, metadata, messageText } = opts;
21933
23540
  const configName = configPath ? import_path12.default.basename(configPath, import_path12.default.extname(configPath)) : void 0;
21934
23541
  const workflowId = configName && opts.workflowId ? `${configName}#${opts.workflowId}` : opts.workflowId;
21935
23542
  const requestMessage = {
21936
- message_id: import_crypto5.default.randomUUID(),
23543
+ message_id: import_crypto6.default.randomUUID(),
21937
23544
  role: "user",
21938
23545
  parts: [{ text: messageText }]
21939
23546
  };
23547
+ const traceFile = process.env.VISOR_FALLBACK_TRACE_FILE || void 0;
21940
23548
  const task = taskStore.createTask({
21941
- contextId: import_crypto5.default.randomUUID(),
23549
+ contextId: import_crypto6.default.randomUUID(),
21942
23550
  requestMessage,
21943
23551
  workflowId,
21944
23552
  requestMetadata: {
21945
23553
  source,
21946
23554
  instance_id: getInstanceId(),
23555
+ visor_version: process.env.VISOR_VERSION || getPackageVersion(),
23556
+ ...process.env.VISOR_COMMIT_SHORT ? { visor_commit: process.env.VISOR_COMMIT_SHORT } : {},
21947
23557
  trace_id: trace.getActiveSpan()?.spanContext().traceId || void 0,
23558
+ ...traceFile ? { trace_file: traceFile } : {},
21948
23559
  ...metadata
21949
23560
  }
21950
23561
  });
@@ -21977,17 +23588,26 @@ async function trackExecution(opts, executor) {
21977
23588
  } catch {
21978
23589
  }
21979
23590
  const completedMsg = {
21980
- message_id: import_crypto5.default.randomUUID(),
23591
+ message_id: import_crypto6.default.randomUUID(),
21981
23592
  role: "agent",
21982
23593
  parts: [{ text: responseText }]
21983
23594
  };
21984
- taskStore.updateTaskState(task.id, "completed", completedMsg);
21985
- logger.info(`[TaskTracking] Task ${task.id} completed`);
23595
+ try {
23596
+ taskStore.updateTaskState(task.id, "completed", completedMsg);
23597
+ logger.info(`[TaskTracking] Task ${task.id} completed`);
23598
+ } catch (stateErr) {
23599
+ logger.warn(
23600
+ `[TaskTracking] Task ${task.id} completed but state transition failed: ${stateErr instanceof Error ? stateErr.message : stateErr}`
23601
+ );
23602
+ }
23603
+ if (opts.autoEvaluate || process.env.VISOR_TASK_EVALUATE === "true") {
23604
+ scheduleEvaluation(task.id, taskStore);
23605
+ }
21986
23606
  return { task, result };
21987
23607
  } catch (err) {
21988
23608
  const errorText = err instanceof Error ? err.message : String(err);
21989
23609
  const failMessage = {
21990
- message_id: import_crypto5.default.randomUUID(),
23610
+ message_id: import_crypto6.default.randomUUID(),
21991
23611
  role: "agent",
21992
23612
  parts: [{ text: errorText }]
21993
23613
  };
@@ -21999,11 +23619,24 @@ async function trackExecution(opts, executor) {
21999
23619
  throw err;
22000
23620
  }
22001
23621
  }
22002
- var import_crypto5, import_path12;
23622
+ function scheduleEvaluation(taskId, taskStore) {
23623
+ setTimeout(async () => {
23624
+ try {
23625
+ const { evaluateAndStore: evaluateAndStore2 } = await Promise.resolve().then(() => (init_task_evaluator(), task_evaluator_exports));
23626
+ await evaluateAndStore2(taskId, taskStore);
23627
+ logger.info(`[TaskEvaluator] Auto-evaluation completed for task ${taskId}`);
23628
+ } catch (err) {
23629
+ logger.warn(
23630
+ `[TaskEvaluator] Auto-evaluation failed for task ${taskId}: ${err instanceof Error ? err.message : err}`
23631
+ );
23632
+ }
23633
+ }, 5e3);
23634
+ }
23635
+ var import_crypto6, import_path12;
22003
23636
  var init_track_execution = __esm({
22004
23637
  "src/agent-protocol/track-execution.ts"() {
22005
23638
  "use strict";
22006
- import_crypto5 = __toESM(require("crypto"));
23639
+ import_crypto6 = __toESM(require("crypto"));
22007
23640
  import_path12 = __toESM(require("path"));
22008
23641
  init_logger();
22009
23642
  init_lazy_otel();
@@ -24143,7 +25776,7 @@ var utcp_check_provider_exports = {};
24143
25776
  __export(utcp_check_provider_exports, {
24144
25777
  UtcpCheckProvider: () => UtcpCheckProvider
24145
25778
  });
24146
- var fs14, path18, UtcpCheckProvider;
25779
+ var fs16, path19, UtcpCheckProvider;
24147
25780
  var init_utcp_check_provider = __esm({
24148
25781
  "src/providers/utcp-check-provider.ts"() {
24149
25782
  "use strict";
@@ -24153,8 +25786,8 @@ var init_utcp_check_provider = __esm({
24153
25786
  init_sandbox();
24154
25787
  init_env_resolver();
24155
25788
  init_issue_normalizer();
24156
- fs14 = __toESM(require("fs"));
24157
- path18 = __toESM(require("path"));
25789
+ fs16 = __toESM(require("fs"));
25790
+ path19 = __toESM(require("path"));
24158
25791
  UtcpCheckProvider = class _UtcpCheckProvider extends CheckProvider {
24159
25792
  liquid;
24160
25793
  sandbox;
@@ -24398,29 +26031,29 @@ var init_utcp_check_provider = __esm({
24398
26031
  if (manual.includes("\0")) {
24399
26032
  throw new Error("Invalid UTCP manual path: null bytes are not allowed");
24400
26033
  }
24401
- const resolvedPath = path18.resolve(manual);
24402
- const cwd = path18.resolve(process.cwd());
24403
- const normalizedResolved = path18.normalize(resolvedPath);
24404
- const cwdPrefix = cwd.endsWith(path18.sep) ? cwd : cwd + path18.sep;
26034
+ const resolvedPath = path19.resolve(manual);
26035
+ const cwd = path19.resolve(process.cwd());
26036
+ const normalizedResolved = path19.normalize(resolvedPath);
26037
+ const cwdPrefix = cwd.endsWith(path19.sep) ? cwd : cwd + path19.sep;
24405
26038
  if (normalizedResolved !== cwd && !normalizedResolved.startsWith(cwdPrefix)) {
24406
26039
  throw new Error(
24407
26040
  `Path traversal detected: "${manual}" resolves outside the project directory. UTCP manual paths must be within the project directory.`
24408
26041
  );
24409
26042
  }
24410
- if (fs14.existsSync(resolvedPath)) {
24411
- const realPath = fs14.realpathSync(resolvedPath);
26043
+ if (fs16.existsSync(resolvedPath)) {
26044
+ const realPath = fs16.realpathSync(resolvedPath);
24412
26045
  if (realPath !== cwd && !realPath.startsWith(cwdPrefix)) {
24413
26046
  throw new Error(
24414
26047
  `Symlink traversal detected: "${manual}" points outside the project directory via symlink.`
24415
26048
  );
24416
26049
  }
24417
26050
  }
24418
- if (!fs14.existsSync(resolvedPath)) {
26051
+ if (!fs16.existsSync(resolvedPath)) {
24419
26052
  throw new Error(`UTCP manual file not found: ${resolvedPath}`);
24420
26053
  }
24421
26054
  let content;
24422
26055
  try {
24423
- content = fs14.readFileSync(resolvedPath, "utf-8");
26056
+ content = fs16.readFileSync(resolvedPath, "utf-8");
24424
26057
  } catch (err) {
24425
26058
  throw new Error(
24426
26059
  `Failed to read UTCP manual file: ${resolvedPath}: ${err instanceof Error ? err.message : "Unknown error"}`
@@ -24436,7 +26069,7 @@ var init_utcp_check_provider = __esm({
24436
26069
  }
24437
26070
  if (parsed.call_template_type) {
24438
26071
  if (!parsed.name) {
24439
- parsed.name = path18.basename(resolvedPath, path18.extname(resolvedPath));
26072
+ parsed.name = path19.basename(resolvedPath, path19.extname(resolvedPath));
24440
26073
  }
24441
26074
  return parsed;
24442
26075
  }
@@ -24446,7 +26079,7 @@ var init_utcp_check_provider = __esm({
24446
26079
  logger.debug("UTCP @utcp/file plugin not available, attempting direct parse");
24447
26080
  }
24448
26081
  return {
24449
- name: parsed.name || path18.basename(resolvedPath, path18.extname(resolvedPath)),
26082
+ name: parsed.name || path19.basename(resolvedPath, path19.extname(resolvedPath)),
24450
26083
  call_template_type: "file",
24451
26084
  file_path: resolvedPath,
24452
26085
  allowed_communication_protocols: ["file", "http", "https"]
@@ -24638,6 +26271,7 @@ var init_mcp_custom_sse_server = __esm({
24638
26271
  workflowContext;
24639
26272
  keepaliveInterval = null;
24640
26273
  activeToolCalls = 0;
26274
+ inFlightToolCalls = /* @__PURE__ */ new Set();
24641
26275
  lastActivityAt = Date.now();
24642
26276
  gracefulStopRequested = false;
24643
26277
  static KEEPALIVE_INTERVAL_MS = 3e4;
@@ -24768,6 +26402,53 @@ var init_mcp_custom_sse_server = __esm({
24768
26402
  this.keepaliveInterval = null;
24769
26403
  }
24770
26404
  }
26405
+ beginToolCall(toolName, workspace) {
26406
+ if (workspace) {
26407
+ workspace.acquire();
26408
+ }
26409
+ const toolCall = {
26410
+ toolName,
26411
+ startedAt: Date.now(),
26412
+ workspace,
26413
+ counted: true,
26414
+ released: false
26415
+ };
26416
+ this.inFlightToolCalls.add(toolCall);
26417
+ this.activeToolCalls++;
26418
+ this.lastActivityAt = Date.now();
26419
+ return toolCall;
26420
+ }
26421
+ finalizeToolCall(toolCall, reason) {
26422
+ if (toolCall.counted) {
26423
+ this.activeToolCalls = Math.max(0, this.activeToolCalls - 1);
26424
+ toolCall.counted = false;
26425
+ }
26426
+ if (toolCall.workspace && !toolCall.released) {
26427
+ toolCall.workspace.release();
26428
+ toolCall.released = true;
26429
+ }
26430
+ this.inFlightToolCalls.delete(toolCall);
26431
+ this.lastActivityAt = Date.now();
26432
+ if (reason === "forced-stop") {
26433
+ logger.warn(
26434
+ `[CustomToolsSSEServer:${this.sessionId}] Force-released stranded tool call '${toolCall.toolName}' after ${Date.now() - toolCall.startedAt}ms`
26435
+ );
26436
+ }
26437
+ }
26438
+ forceDrainToolCalls() {
26439
+ if (this.inFlightToolCalls.size === 0) {
26440
+ if (this.activeToolCalls > 0) {
26441
+ logger.warn(
26442
+ `[CustomToolsSSEServer:${this.sessionId}] Resetting active tool counter with no in-flight records (${this.activeToolCalls})`
26443
+ );
26444
+ this.activeToolCalls = 0;
26445
+ }
26446
+ return;
26447
+ }
26448
+ for (const toolCall of Array.from(this.inFlightToolCalls)) {
26449
+ this.finalizeToolCall(toolCall, "forced-stop");
26450
+ }
26451
+ }
24771
26452
  /**
24772
26453
  * Stop the server and cleanup resources
24773
26454
  */
@@ -24798,6 +26479,7 @@ var init_mcp_custom_sse_server = __esm({
24798
26479
  logger.warn(
24799
26480
  `[CustomToolsSSEServer:${this.sessionId}] Drain timeout reached; stopping with ${this.activeToolCalls} active tool call(s)`
24800
26481
  );
26482
+ this.forceDrainToolCalls();
24801
26483
  }
24802
26484
  }
24803
26485
  if (this.debug) {
@@ -25182,11 +26864,7 @@ var init_mcp_custom_sse_server = __esm({
25182
26864
  */
25183
26865
  async handleToolCall(id, toolName, args) {
25184
26866
  const workspace = this.workflowContext?.workspace;
25185
- if (workspace) {
25186
- workspace.acquire();
25187
- }
25188
- this.activeToolCalls++;
25189
- this.lastActivityAt = Date.now();
26867
+ const toolCall = this.beginToolCall(toolName, workspace);
25190
26868
  try {
25191
26869
  if (this.debug) {
25192
26870
  logger.debug(
@@ -25473,11 +27151,7 @@ var init_mcp_custom_sse_server = __esm({
25473
27151
  }
25474
27152
  };
25475
27153
  } finally {
25476
- this.activeToolCalls = Math.max(0, this.activeToolCalls - 1);
25477
- this.lastActivityAt = Date.now();
25478
- if (workspace) {
25479
- workspace.release();
25480
- }
27154
+ this.finalizeToolCall(toolCall, "completed");
25481
27155
  }
25482
27156
  }
25483
27157
  /**
@@ -25926,9 +27600,9 @@ var init_ai_check_provider = __esm({
25926
27600
  } else {
25927
27601
  resolvedPath = import_path13.default.resolve(process.cwd(), str);
25928
27602
  }
25929
- const fs27 = require("fs").promises;
27603
+ const fs29 = require("fs").promises;
25930
27604
  try {
25931
- const stat2 = await fs27.stat(resolvedPath);
27605
+ const stat2 = await fs29.stat(resolvedPath);
25932
27606
  return stat2.isFile();
25933
27607
  } catch {
25934
27608
  return hasFileExtension && (isRelativePath || isAbsolutePath || hasPathSeparators);
@@ -28331,7 +30005,7 @@ var init_oauth2_token_cache = __esm({
28331
30005
  });
28332
30006
 
28333
30007
  // src/providers/http-client-provider.ts
28334
- var fs17, path20, HttpClientProvider;
30008
+ var fs19, path21, HttpClientProvider;
28335
30009
  var init_http_client_provider = __esm({
28336
30010
  "src/providers/http-client-provider.ts"() {
28337
30011
  "use strict";
@@ -28343,8 +30017,8 @@ var init_http_client_provider = __esm({
28343
30017
  init_oauth2_token_cache();
28344
30018
  init_logger();
28345
30019
  init_rate_limiter();
28346
- fs17 = __toESM(require("fs"));
28347
- path20 = __toESM(require("path"));
30020
+ fs19 = __toESM(require("fs"));
30021
+ path21 = __toESM(require("path"));
28348
30022
  HttpClientProvider = class extends CheckProvider {
28349
30023
  liquid;
28350
30024
  sandbox;
@@ -28471,14 +30145,14 @@ var init_http_client_provider = __esm({
28471
30145
  const parentContext = context2?._parentContext;
28472
30146
  const workingDirectory = parentContext?.workingDirectory;
28473
30147
  const workspaceEnabled = parentContext?.workspace?.isEnabled?.();
28474
- if (workspaceEnabled && workingDirectory && !path20.isAbsolute(resolvedOutputFile)) {
28475
- resolvedOutputFile = path20.join(workingDirectory, resolvedOutputFile);
30148
+ if (workspaceEnabled && workingDirectory && !path21.isAbsolute(resolvedOutputFile)) {
30149
+ resolvedOutputFile = path21.join(workingDirectory, resolvedOutputFile);
28476
30150
  logger.debug(
28477
30151
  `[http_client] Resolved relative output_file to workspace: ${resolvedOutputFile}`
28478
30152
  );
28479
30153
  }
28480
- if (skipIfExists && fs17.existsSync(resolvedOutputFile)) {
28481
- const stats = fs17.statSync(resolvedOutputFile);
30154
+ if (skipIfExists && fs19.existsSync(resolvedOutputFile)) {
30155
+ const stats = fs19.statSync(resolvedOutputFile);
28482
30156
  logger.verbose(`[http_client] File cached: ${resolvedOutputFile} (${stats.size} bytes)`);
28483
30157
  return {
28484
30158
  issues: [],
@@ -28708,13 +30382,13 @@ var init_http_client_provider = __esm({
28708
30382
  ]
28709
30383
  };
28710
30384
  }
28711
- const parentDir = path20.dirname(outputFile);
28712
- if (parentDir && !fs17.existsSync(parentDir)) {
28713
- fs17.mkdirSync(parentDir, { recursive: true });
30385
+ const parentDir = path21.dirname(outputFile);
30386
+ if (parentDir && !fs19.existsSync(parentDir)) {
30387
+ fs19.mkdirSync(parentDir, { recursive: true });
28714
30388
  }
28715
30389
  const arrayBuffer = await response.arrayBuffer();
28716
30390
  const buffer = Buffer.from(arrayBuffer);
28717
- fs17.writeFileSync(outputFile, buffer);
30391
+ fs19.writeFileSync(outputFile, buffer);
28718
30392
  const contentType = response.headers.get("content-type") || "application/octet-stream";
28719
30393
  logger.verbose(`[http_client] Downloaded: ${outputFile} (${buffer.length} bytes)`);
28720
30394
  return {
@@ -32200,14 +33874,14 @@ var require_util = __commonJS({
32200
33874
  }
32201
33875
  const port = url.port != null ? url.port : url.protocol === "https:" ? 443 : 80;
32202
33876
  let origin = url.origin != null ? url.origin : `${url.protocol}//${url.hostname}:${port}`;
32203
- let path30 = url.path != null ? url.path : `${url.pathname || ""}${url.search || ""}`;
33877
+ let path31 = url.path != null ? url.path : `${url.pathname || ""}${url.search || ""}`;
32204
33878
  if (origin.endsWith("/")) {
32205
33879
  origin = origin.substring(0, origin.length - 1);
32206
33880
  }
32207
- if (path30 && !path30.startsWith("/")) {
32208
- path30 = `/${path30}`;
33881
+ if (path31 && !path31.startsWith("/")) {
33882
+ path31 = `/${path31}`;
32209
33883
  }
32210
- url = new URL(origin + path30);
33884
+ url = new URL(origin + path31);
32211
33885
  }
32212
33886
  return url;
32213
33887
  }
@@ -33821,20 +35495,20 @@ var require_parseParams = __commonJS({
33821
35495
  var require_basename = __commonJS({
33822
35496
  "node_modules/@fastify/busboy/lib/utils/basename.js"(exports2, module2) {
33823
35497
  "use strict";
33824
- module2.exports = function basename5(path30) {
33825
- if (typeof path30 !== "string") {
35498
+ module2.exports = function basename5(path31) {
35499
+ if (typeof path31 !== "string") {
33826
35500
  return "";
33827
35501
  }
33828
- for (var i = path30.length - 1; i >= 0; --i) {
33829
- switch (path30.charCodeAt(i)) {
35502
+ for (var i = path31.length - 1; i >= 0; --i) {
35503
+ switch (path31.charCodeAt(i)) {
33830
35504
  case 47:
33831
35505
  // '/'
33832
35506
  case 92:
33833
- path30 = path30.slice(i + 1);
33834
- return path30 === ".." || path30 === "." ? "" : path30;
35507
+ path31 = path31.slice(i + 1);
35508
+ return path31 === ".." || path31 === "." ? "" : path31;
33835
35509
  }
33836
35510
  }
33837
- return path30 === ".." || path30 === "." ? "" : path30;
35511
+ return path31 === ".." || path31 === "." ? "" : path31;
33838
35512
  };
33839
35513
  }
33840
35514
  });
@@ -34838,11 +36512,11 @@ var require_util2 = __commonJS({
34838
36512
  var assert = require("assert");
34839
36513
  var { isUint8Array } = require("util/types");
34840
36514
  var supportedHashes = [];
34841
- var crypto7;
36515
+ var crypto8;
34842
36516
  try {
34843
- crypto7 = require("crypto");
36517
+ crypto8 = require("crypto");
34844
36518
  const possibleRelevantHashes = ["sha256", "sha384", "sha512"];
34845
- supportedHashes = crypto7.getHashes().filter((hash) => possibleRelevantHashes.includes(hash));
36519
+ supportedHashes = crypto8.getHashes().filter((hash) => possibleRelevantHashes.includes(hash));
34846
36520
  } catch {
34847
36521
  }
34848
36522
  function responseURL(response) {
@@ -35119,7 +36793,7 @@ var require_util2 = __commonJS({
35119
36793
  }
35120
36794
  }
35121
36795
  function bytesMatch(bytes, metadataList) {
35122
- if (crypto7 === void 0) {
36796
+ if (crypto8 === void 0) {
35123
36797
  return true;
35124
36798
  }
35125
36799
  const parsedMetadata = parseMetadata(metadataList);
@@ -35134,7 +36808,7 @@ var require_util2 = __commonJS({
35134
36808
  for (const item of metadata) {
35135
36809
  const algorithm = item.algo;
35136
36810
  const expectedValue = item.hash;
35137
- let actualValue = crypto7.createHash(algorithm).update(bytes).digest("base64");
36811
+ let actualValue = crypto8.createHash(algorithm).update(bytes).digest("base64");
35138
36812
  if (actualValue[actualValue.length - 1] === "=") {
35139
36813
  if (actualValue[actualValue.length - 2] === "=") {
35140
36814
  actualValue = actualValue.slice(0, -2);
@@ -36481,8 +38155,8 @@ var require_body = __commonJS({
36481
38155
  var { parseMIMEType, serializeAMimeType } = require_dataURL();
36482
38156
  var random;
36483
38157
  try {
36484
- const crypto7 = require("crypto");
36485
- random = (max) => crypto7.randomInt(0, max);
38158
+ const crypto8 = require("crypto");
38159
+ random = (max) => crypto8.randomInt(0, max);
36486
38160
  } catch {
36487
38161
  random = (max) => Math.floor(Math.random(max));
36488
38162
  }
@@ -36865,7 +38539,7 @@ var require_request = __commonJS({
36865
38539
  }
36866
38540
  var Request2 = class _Request {
36867
38541
  constructor(origin, {
36868
- path: path30,
38542
+ path: path31,
36869
38543
  method,
36870
38544
  body,
36871
38545
  headers,
@@ -36879,11 +38553,11 @@ var require_request = __commonJS({
36879
38553
  throwOnError,
36880
38554
  expectContinue
36881
38555
  }, handler) {
36882
- if (typeof path30 !== "string") {
38556
+ if (typeof path31 !== "string") {
36883
38557
  throw new InvalidArgumentError("path must be a string");
36884
- } else if (path30[0] !== "/" && !(path30.startsWith("http://") || path30.startsWith("https://")) && method !== "CONNECT") {
38558
+ } else if (path31[0] !== "/" && !(path31.startsWith("http://") || path31.startsWith("https://")) && method !== "CONNECT") {
36885
38559
  throw new InvalidArgumentError("path must be an absolute URL or start with a slash");
36886
- } else if (invalidPathRegex.exec(path30) !== null) {
38560
+ } else if (invalidPathRegex.exec(path31) !== null) {
36887
38561
  throw new InvalidArgumentError("invalid request path");
36888
38562
  }
36889
38563
  if (typeof method !== "string") {
@@ -36946,7 +38620,7 @@ var require_request = __commonJS({
36946
38620
  this.completed = false;
36947
38621
  this.aborted = false;
36948
38622
  this.upgrade = upgrade || null;
36949
- this.path = query ? util.buildURL(path30, query) : path30;
38623
+ this.path = query ? util.buildURL(path31, query) : path31;
36950
38624
  this.origin = origin;
36951
38625
  this.idempotent = idempotent == null ? method === "HEAD" || method === "GET" : idempotent;
36952
38626
  this.blocking = blocking == null ? false : blocking;
@@ -37954,9 +39628,9 @@ var require_RedirectHandler = __commonJS({
37954
39628
  return this.handler.onHeaders(statusCode, headers, resume, statusText);
37955
39629
  }
37956
39630
  const { origin, pathname, search } = util.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin)));
37957
- const path30 = search ? `${pathname}${search}` : pathname;
39631
+ const path31 = search ? `${pathname}${search}` : pathname;
37958
39632
  this.opts.headers = cleanRequestHeaders(this.opts.headers, statusCode === 303, this.opts.origin !== origin);
37959
- this.opts.path = path30;
39633
+ this.opts.path = path31;
37960
39634
  this.opts.origin = origin;
37961
39635
  this.opts.maxRedirections = 0;
37962
39636
  this.opts.query = null;
@@ -39198,7 +40872,7 @@ var require_client = __commonJS({
39198
40872
  writeH2(client, client[kHTTP2Session], request);
39199
40873
  return;
39200
40874
  }
39201
- const { body, method, path: path30, host, upgrade, headers, blocking, reset } = request;
40875
+ const { body, method, path: path31, host, upgrade, headers, blocking, reset } = request;
39202
40876
  const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH";
39203
40877
  if (body && typeof body.read === "function") {
39204
40878
  body.read(0);
@@ -39248,7 +40922,7 @@ var require_client = __commonJS({
39248
40922
  if (blocking) {
39249
40923
  socket[kBlocking] = true;
39250
40924
  }
39251
- let header = `${method} ${path30} HTTP/1.1\r
40925
+ let header = `${method} ${path31} HTTP/1.1\r
39252
40926
  `;
39253
40927
  if (typeof host === "string") {
39254
40928
  header += `host: ${host}\r
@@ -39311,7 +40985,7 @@ upgrade: ${upgrade}\r
39311
40985
  return true;
39312
40986
  }
39313
40987
  function writeH2(client, session, request) {
39314
- const { body, method, path: path30, host, upgrade, expectContinue, signal, headers: reqHeaders } = request;
40988
+ const { body, method, path: path31, host, upgrade, expectContinue, signal, headers: reqHeaders } = request;
39315
40989
  let headers;
39316
40990
  if (typeof reqHeaders === "string") headers = Request2[kHTTP2CopyHeaders](reqHeaders.trim());
39317
40991
  else headers = reqHeaders;
@@ -39354,7 +41028,7 @@ upgrade: ${upgrade}\r
39354
41028
  });
39355
41029
  return true;
39356
41030
  }
39357
- headers[HTTP2_HEADER_PATH] = path30;
41031
+ headers[HTTP2_HEADER_PATH] = path31;
39358
41032
  headers[HTTP2_HEADER_SCHEME] = "https";
39359
41033
  const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH";
39360
41034
  if (body && typeof body.read === "function") {
@@ -41597,20 +43271,20 @@ var require_mock_utils = __commonJS({
41597
43271
  }
41598
43272
  return true;
41599
43273
  }
41600
- function safeUrl(path30) {
41601
- if (typeof path30 !== "string") {
41602
- return path30;
43274
+ function safeUrl(path31) {
43275
+ if (typeof path31 !== "string") {
43276
+ return path31;
41603
43277
  }
41604
- const pathSegments = path30.split("?");
43278
+ const pathSegments = path31.split("?");
41605
43279
  if (pathSegments.length !== 2) {
41606
- return path30;
43280
+ return path31;
41607
43281
  }
41608
43282
  const qp = new URLSearchParams(pathSegments.pop());
41609
43283
  qp.sort();
41610
43284
  return [...pathSegments, qp.toString()].join("?");
41611
43285
  }
41612
- function matchKey(mockDispatch2, { path: path30, method, body, headers }) {
41613
- const pathMatch = matchValue(mockDispatch2.path, path30);
43286
+ function matchKey(mockDispatch2, { path: path31, method, body, headers }) {
43287
+ const pathMatch = matchValue(mockDispatch2.path, path31);
41614
43288
  const methodMatch = matchValue(mockDispatch2.method, method);
41615
43289
  const bodyMatch = typeof mockDispatch2.body !== "undefined" ? matchValue(mockDispatch2.body, body) : true;
41616
43290
  const headersMatch = matchHeaders(mockDispatch2, headers);
@@ -41628,7 +43302,7 @@ var require_mock_utils = __commonJS({
41628
43302
  function getMockDispatch(mockDispatches, key) {
41629
43303
  const basePath = key.query ? buildURL(key.path, key.query) : key.path;
41630
43304
  const resolvedPath = typeof basePath === "string" ? safeUrl(basePath) : basePath;
41631
- let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path30 }) => matchValue(safeUrl(path30), resolvedPath));
43305
+ let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path31 }) => matchValue(safeUrl(path31), resolvedPath));
41632
43306
  if (matchedMockDispatches.length === 0) {
41633
43307
  throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`);
41634
43308
  }
@@ -41665,9 +43339,9 @@ var require_mock_utils = __commonJS({
41665
43339
  }
41666
43340
  }
41667
43341
  function buildKey(opts) {
41668
- const { path: path30, method, body, headers, query } = opts;
43342
+ const { path: path31, method, body, headers, query } = opts;
41669
43343
  return {
41670
- path: path30,
43344
+ path: path31,
41671
43345
  method,
41672
43346
  body,
41673
43347
  headers,
@@ -42116,10 +43790,10 @@ var require_pending_interceptors_formatter = __commonJS({
42116
43790
  }
42117
43791
  format(pendingInterceptors) {
42118
43792
  const withPrettyHeaders = pendingInterceptors.map(
42119
- ({ method, path: path30, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
43793
+ ({ method, path: path31, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
42120
43794
  Method: method,
42121
43795
  Origin: origin,
42122
- Path: path30,
43796
+ Path: path31,
42123
43797
  "Status code": statusCode,
42124
43798
  Persistent: persist ? "\u2705" : "\u274C",
42125
43799
  Invocations: timesInvoked,
@@ -46740,8 +48414,8 @@ var require_util6 = __commonJS({
46740
48414
  }
46741
48415
  }
46742
48416
  }
46743
- function validateCookiePath(path30) {
46744
- for (const char of path30) {
48417
+ function validateCookiePath(path31) {
48418
+ for (const char of path31) {
46745
48419
  const code = char.charCodeAt(0);
46746
48420
  if (code < 33 || char === ";") {
46747
48421
  throw new Error("Invalid cookie path");
@@ -47538,9 +49212,9 @@ var require_connection = __commonJS({
47538
49212
  channels.open = diagnosticsChannel.channel("undici:websocket:open");
47539
49213
  channels.close = diagnosticsChannel.channel("undici:websocket:close");
47540
49214
  channels.socketError = diagnosticsChannel.channel("undici:websocket:socket_error");
47541
- var crypto7;
49215
+ var crypto8;
47542
49216
  try {
47543
- crypto7 = require("crypto");
49217
+ crypto8 = require("crypto");
47544
49218
  } catch {
47545
49219
  }
47546
49220
  function establishWebSocketConnection(url, protocols, ws, onEstablish, options) {
@@ -47559,7 +49233,7 @@ var require_connection = __commonJS({
47559
49233
  const headersList = new Headers(options.headers)[kHeadersList];
47560
49234
  request.headersList = headersList;
47561
49235
  }
47562
- const keyValue = crypto7.randomBytes(16).toString("base64");
49236
+ const keyValue = crypto8.randomBytes(16).toString("base64");
47563
49237
  request.headersList.append("sec-websocket-key", keyValue);
47564
49238
  request.headersList.append("sec-websocket-version", "13");
47565
49239
  for (const protocol of protocols) {
@@ -47588,7 +49262,7 @@ var require_connection = __commonJS({
47588
49262
  return;
47589
49263
  }
47590
49264
  const secWSAccept = response.headersList.get("Sec-WebSocket-Accept");
47591
- const digest = crypto7.createHash("sha1").update(keyValue + uid).digest("base64");
49265
+ const digest = crypto8.createHash("sha1").update(keyValue + uid).digest("base64");
47592
49266
  if (secWSAccept !== digest) {
47593
49267
  failWebsocketConnection(ws, "Incorrect hash received in Sec-WebSocket-Accept header.");
47594
49268
  return;
@@ -47668,9 +49342,9 @@ var require_frame = __commonJS({
47668
49342
  "node_modules/undici/lib/websocket/frame.js"(exports2, module2) {
47669
49343
  "use strict";
47670
49344
  var { maxUnsigned16Bit } = require_constants5();
47671
- var crypto7;
49345
+ var crypto8;
47672
49346
  try {
47673
- crypto7 = require("crypto");
49347
+ crypto8 = require("crypto");
47674
49348
  } catch {
47675
49349
  }
47676
49350
  var WebsocketFrameSend = class {
@@ -47679,7 +49353,7 @@ var require_frame = __commonJS({
47679
49353
  */
47680
49354
  constructor(data) {
47681
49355
  this.frameData = data;
47682
- this.maskKey = crypto7.randomBytes(4);
49356
+ this.maskKey = crypto8.randomBytes(4);
47683
49357
  }
47684
49358
  createFrame(opcode) {
47685
49359
  const bodyLength = this.frameData?.byteLength ?? 0;
@@ -48421,11 +50095,11 @@ var require_undici = __commonJS({
48421
50095
  if (typeof opts.path !== "string") {
48422
50096
  throw new InvalidArgumentError("invalid opts.path");
48423
50097
  }
48424
- let path30 = opts.path;
50098
+ let path31 = opts.path;
48425
50099
  if (!opts.path.startsWith("/")) {
48426
- path30 = `/${path30}`;
50100
+ path31 = `/${path31}`;
48427
50101
  }
48428
- url = new URL(util.parseOrigin(url).origin + path30);
50102
+ url = new URL(util.parseOrigin(url).origin + path31);
48429
50103
  } else {
48430
50104
  if (!opts) {
48431
50105
  opts = typeof url === "object" ? url : {};
@@ -49300,7 +50974,7 @@ async function interactivePrompt(options) {
49300
50974
  } catch {
49301
50975
  }
49302
50976
  if (multiline) {
49303
- rl = readline.createInterface({
50977
+ rl = readline3.createInterface({
49304
50978
  input: process.stdin,
49305
50979
  output: process.stdout,
49306
50980
  terminal: true
@@ -49413,7 +51087,7 @@ async function interactivePrompt(options) {
49413
51087
  }
49414
51088
  async function simplePrompt(prompt) {
49415
51089
  return new Promise((resolve17) => {
49416
- const rl = readline.createInterface({
51090
+ const rl = readline3.createInterface({
49417
51091
  input: process.stdin,
49418
51092
  output: process.stdout
49419
51093
  });
@@ -49432,11 +51106,11 @@ async function simplePrompt(prompt) {
49432
51106
  });
49433
51107
  });
49434
51108
  }
49435
- var readline, activePrompt, waiters;
51109
+ var readline3, activePrompt, waiters;
49436
51110
  var init_interactive_prompt = __esm({
49437
51111
  "src/utils/interactive-prompt.ts"() {
49438
51112
  "use strict";
49439
- readline = __toESM(require("readline"));
51113
+ readline3 = __toESM(require("readline"));
49440
51114
  activePrompt = false;
49441
51115
  waiters = [];
49442
51116
  }
@@ -49653,7 +51327,7 @@ var init_stdin_reader = __esm({
49653
51327
  });
49654
51328
 
49655
51329
  // src/providers/human-input-check-provider.ts
49656
- var fs19, path22, HumanInputCheckProvider;
51330
+ var fs21, path23, HumanInputCheckProvider;
49657
51331
  var init_human_input_check_provider = __esm({
49658
51332
  "src/providers/human-input-check-provider.ts"() {
49659
51333
  "use strict";
@@ -49662,8 +51336,8 @@ var init_human_input_check_provider = __esm({
49662
51336
  init_prompt_state();
49663
51337
  init_liquid_extensions();
49664
51338
  init_stdin_reader();
49665
- fs19 = __toESM(require("fs"));
49666
- path22 = __toESM(require("path"));
51339
+ fs21 = __toESM(require("fs"));
51340
+ path23 = __toESM(require("path"));
49667
51341
  HumanInputCheckProvider = class _HumanInputCheckProvider extends CheckProvider {
49668
51342
  liquid;
49669
51343
  /**
@@ -49837,19 +51511,19 @@ var init_human_input_check_provider = __esm({
49837
51511
  */
49838
51512
  async tryReadFile(filePath) {
49839
51513
  try {
49840
- const absolutePath = path22.isAbsolute(filePath) ? filePath : path22.resolve(process.cwd(), filePath);
49841
- const normalizedPath = path22.normalize(absolutePath);
51514
+ const absolutePath = path23.isAbsolute(filePath) ? filePath : path23.resolve(process.cwd(), filePath);
51515
+ const normalizedPath = path23.normalize(absolutePath);
49842
51516
  const cwd = process.cwd();
49843
- if (!normalizedPath.startsWith(cwd + path22.sep) && normalizedPath !== cwd) {
51517
+ if (!normalizedPath.startsWith(cwd + path23.sep) && normalizedPath !== cwd) {
49844
51518
  return null;
49845
51519
  }
49846
51520
  try {
49847
- await fs19.promises.access(normalizedPath, fs19.constants.R_OK);
49848
- const stats = await fs19.promises.stat(normalizedPath);
51521
+ await fs21.promises.access(normalizedPath, fs21.constants.R_OK);
51522
+ const stats = await fs21.promises.stat(normalizedPath);
49849
51523
  if (!stats.isFile()) {
49850
51524
  return null;
49851
51525
  }
49852
- const content = await fs19.promises.readFile(normalizedPath, "utf-8");
51526
+ const content = await fs21.promises.readFile(normalizedPath, "utf-8");
49853
51527
  return content.trim();
49854
51528
  } catch {
49855
51529
  return null;
@@ -51002,14 +52676,14 @@ var init_script_check_provider = __esm({
51002
52676
  });
51003
52677
 
51004
52678
  // src/utils/worktree-manager.ts
51005
- var fs20, fsp, path23, crypto2, WorktreeManager, worktreeManager;
52679
+ var fs22, fsp, path24, crypto3, WorktreeManager, worktreeManager;
51006
52680
  var init_worktree_manager = __esm({
51007
52681
  "src/utils/worktree-manager.ts"() {
51008
52682
  "use strict";
51009
- fs20 = __toESM(require("fs"));
52683
+ fs22 = __toESM(require("fs"));
51010
52684
  fsp = __toESM(require("fs/promises"));
51011
- path23 = __toESM(require("path"));
51012
- crypto2 = __toESM(require("crypto"));
52685
+ path24 = __toESM(require("path"));
52686
+ crypto3 = __toESM(require("crypto"));
51013
52687
  init_command_executor();
51014
52688
  init_logger();
51015
52689
  WorktreeManager = class _WorktreeManager {
@@ -51024,7 +52698,7 @@ var init_worktree_manager = __esm({
51024
52698
  } catch {
51025
52699
  cwd = "/tmp";
51026
52700
  }
51027
- const defaultBasePath = process.env.VISOR_WORKTREE_PATH || path23.join(cwd, ".visor", "worktrees");
52701
+ const defaultBasePath = process.env.VISOR_WORKTREE_PATH || path24.join(cwd, ".visor", "worktrees");
51028
52702
  this.config = {
51029
52703
  enabled: true,
51030
52704
  base_path: defaultBasePath,
@@ -51061,20 +52735,20 @@ var init_worktree_manager = __esm({
51061
52735
  }
51062
52736
  const reposDir = this.getReposDir();
51063
52737
  const worktreesDir = this.getWorktreesDir();
51064
- if (!fs20.existsSync(reposDir)) {
51065
- fs20.mkdirSync(reposDir, { recursive: true });
52738
+ if (!fs22.existsSync(reposDir)) {
52739
+ fs22.mkdirSync(reposDir, { recursive: true });
51066
52740
  logger.debug(`Created repos directory: ${reposDir}`);
51067
52741
  }
51068
- if (!fs20.existsSync(worktreesDir)) {
51069
- fs20.mkdirSync(worktreesDir, { recursive: true });
52742
+ if (!fs22.existsSync(worktreesDir)) {
52743
+ fs22.mkdirSync(worktreesDir, { recursive: true });
51070
52744
  logger.debug(`Created worktrees directory: ${worktreesDir}`);
51071
52745
  }
51072
52746
  }
51073
52747
  getReposDir() {
51074
- return path23.join(this.config.base_path, "repos");
52748
+ return path24.join(this.config.base_path, "repos");
51075
52749
  }
51076
52750
  getWorktreesDir() {
51077
- return path23.join(this.config.base_path, "worktrees");
52751
+ return path24.join(this.config.base_path, "worktrees");
51078
52752
  }
51079
52753
  /**
51080
52754
  * Generate a worktree ID based on repository, ref, and session.
@@ -51090,7 +52764,7 @@ var init_worktree_manager = __esm({
51090
52764
  const sanitizedRepo = repository.replace(/[^a-zA-Z0-9-]/g, "-");
51091
52765
  const sanitizedRef = ref.replace(/[^a-zA-Z0-9-]/g, "-");
51092
52766
  const hashInput = sessionId ? `${repository}:${ref}:${sessionId}` : `${repository}:${ref}`;
51093
- const hash = crypto2.createHash("md5").update(hashInput).digest("hex").substring(0, 8);
52767
+ const hash = crypto3.createHash("md5").update(hashInput).digest("hex").substring(0, 8);
51094
52768
  return `${sanitizedRepo}-${sanitizedRef}-${hash}`;
51095
52769
  }
51096
52770
  /**
@@ -51099,8 +52773,8 @@ var init_worktree_manager = __esm({
51099
52773
  async getOrCreateBareRepo(repository, repoUrl, _token, fetchDepth, cloneTimeoutMs) {
51100
52774
  const reposDir = this.getReposDir();
51101
52775
  const repoName = repository.replace(/\//g, "-");
51102
- const bareRepoPath = path23.join(reposDir, `${repoName}.git`);
51103
- if (fs20.existsSync(bareRepoPath)) {
52776
+ const bareRepoPath = path24.join(reposDir, `${repoName}.git`);
52777
+ if (fs22.existsSync(bareRepoPath)) {
51104
52778
  logger.debug(`Bare repository already exists: ${bareRepoPath}`);
51105
52779
  const verifyResult = await this.verifyBareRepoRemote(bareRepoPath, repoUrl);
51106
52780
  if (verifyResult === "timeout") {
@@ -51269,12 +52943,12 @@ var init_worktree_manager = __esm({
51269
52943
  options.cloneTimeoutMs
51270
52944
  );
51271
52945
  const worktreeId = this.generateWorktreeId(repository, ref, options.sessionId);
51272
- let worktreePath = options.workingDirectory || path23.join(this.getWorktreesDir(), worktreeId);
52946
+ let worktreePath = options.workingDirectory || path24.join(this.getWorktreesDir(), worktreeId);
51273
52947
  if (options.workingDirectory) {
51274
52948
  worktreePath = this.validatePath(options.workingDirectory);
51275
52949
  }
51276
52950
  let refreshFailedNeedsRecreate = false;
51277
- if (fs20.existsSync(worktreePath)) {
52951
+ if (fs22.existsSync(worktreePath)) {
51278
52952
  logger.debug(`Worktree already exists: ${worktreePath}`);
51279
52953
  const metadata2 = await this.loadMetadata(worktreePath);
51280
52954
  if (metadata2) {
@@ -51614,15 +53288,15 @@ var init_worktree_manager = __esm({
51614
53288
  const result = await this.executeGitCommand(removeCmd, { timeout: 3e4 });
51615
53289
  if (result.exitCode !== 0) {
51616
53290
  logger.warn(`Failed to remove worktree via git: ${result.stderr}`);
51617
- if (fs20.existsSync(worktree_path)) {
53291
+ if (fs22.existsSync(worktree_path)) {
51618
53292
  logger.debug(`Manually removing worktree directory`);
51619
- fs20.rmSync(worktree_path, { recursive: true, force: true });
53293
+ fs22.rmSync(worktree_path, { recursive: true, force: true });
51620
53294
  }
51621
53295
  }
51622
53296
  const metadataPath = this.getMetadataPath(worktree_path);
51623
53297
  try {
51624
- if (fs20.existsSync(metadataPath)) {
51625
- fs20.unlinkSync(metadataPath);
53298
+ if (fs22.existsSync(metadataPath)) {
53299
+ fs22.unlinkSync(metadataPath);
51626
53300
  }
51627
53301
  } catch {
51628
53302
  }
@@ -51642,20 +53316,20 @@ var init_worktree_manager = __esm({
51642
53316
  */
51643
53317
  async saveMetadata(worktreePath, metadata) {
51644
53318
  const metadataPath = this.getMetadataPath(worktreePath);
51645
- fs20.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2), "utf8");
53319
+ fs22.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2), "utf8");
51646
53320
  }
51647
53321
  /**
51648
53322
  * Load worktree metadata
51649
53323
  */
51650
53324
  async loadMetadata(worktreePath) {
51651
53325
  const metadataPath = this.getMetadataPath(worktreePath);
51652
- const legacyPath = path23.join(worktreePath, ".visor-metadata.json");
51653
- const pathToRead = fs20.existsSync(metadataPath) ? metadataPath : fs20.existsSync(legacyPath) ? legacyPath : null;
53326
+ const legacyPath = path24.join(worktreePath, ".visor-metadata.json");
53327
+ const pathToRead = fs22.existsSync(metadataPath) ? metadataPath : fs22.existsSync(legacyPath) ? legacyPath : null;
51654
53328
  if (!pathToRead) {
51655
53329
  return null;
51656
53330
  }
51657
53331
  try {
51658
- const content = fs20.readFileSync(pathToRead, "utf8");
53332
+ const content = fs22.readFileSync(pathToRead, "utf8");
51659
53333
  return JSON.parse(content);
51660
53334
  } catch (error) {
51661
53335
  logger.warn(`Failed to load metadata: ${error}`);
@@ -51667,14 +53341,14 @@ var init_worktree_manager = __esm({
51667
53341
  */
51668
53342
  async listWorktrees() {
51669
53343
  const worktreesDir = this.getWorktreesDir();
51670
- if (!fs20.existsSync(worktreesDir)) {
53344
+ if (!fs22.existsSync(worktreesDir)) {
51671
53345
  return [];
51672
53346
  }
51673
- const entries = fs20.readdirSync(worktreesDir, { withFileTypes: true });
53347
+ const entries = fs22.readdirSync(worktreesDir, { withFileTypes: true });
51674
53348
  const worktrees = [];
51675
53349
  for (const entry of entries) {
51676
53350
  if (!entry.isDirectory()) continue;
51677
- const worktreePath = path23.join(worktreesDir, entry.name);
53351
+ const worktreePath = path24.join(worktreesDir, entry.name);
51678
53352
  const metadata = await this.loadMetadata(worktreePath);
51679
53353
  if (metadata) {
51680
53354
  worktrees.push({
@@ -51806,8 +53480,8 @@ var init_worktree_manager = __esm({
51806
53480
  * Validate path to prevent directory traversal
51807
53481
  */
51808
53482
  validatePath(userPath) {
51809
- const resolvedPath = path23.resolve(userPath);
51810
- if (!path23.isAbsolute(resolvedPath)) {
53483
+ const resolvedPath = path24.resolve(userPath);
53484
+ if (!path24.isAbsolute(resolvedPath)) {
51811
53485
  throw new Error("Path must be absolute");
51812
53486
  }
51813
53487
  const sensitivePatterns = [
@@ -54441,23 +56115,23 @@ __export(renderer_schema_exports, {
54441
56115
  });
54442
56116
  async function loadRendererSchema(name) {
54443
56117
  try {
54444
- const fs27 = await import("fs/promises");
54445
- const path30 = await import("path");
56118
+ const fs29 = await import("fs/promises");
56119
+ const path31 = await import("path");
54446
56120
  const sanitized = String(name).replace(/[^a-zA-Z0-9-]/g, "");
54447
56121
  if (!sanitized) return void 0;
54448
56122
  const candidates = [
54449
56123
  // When bundled with ncc, __dirname is dist/ and output/ is at dist/output/
54450
- path30.join(__dirname, "output", sanitized, "schema.json"),
56124
+ path31.join(__dirname, "output", sanitized, "schema.json"),
54451
56125
  // When running from source, __dirname is src/state-machine/dispatch/ and output/ is at output/
54452
- path30.join(__dirname, "..", "..", "output", sanitized, "schema.json"),
56126
+ path31.join(__dirname, "..", "..", "output", sanitized, "schema.json"),
54453
56127
  // When running from a checkout with output/ folder copied to CWD
54454
- path30.join(process.cwd(), "output", sanitized, "schema.json"),
56128
+ path31.join(process.cwd(), "output", sanitized, "schema.json"),
54455
56129
  // Fallback: cwd/dist/output/
54456
- path30.join(process.cwd(), "dist", "output", sanitized, "schema.json")
56130
+ path31.join(process.cwd(), "dist", "output", sanitized, "schema.json")
54457
56131
  ];
54458
56132
  for (const p of candidates) {
54459
56133
  try {
54460
- const raw = await fs27.readFile(p, "utf-8");
56134
+ const raw = await fs29.readFile(p, "utf-8");
54461
56135
  return JSON.parse(raw);
54462
56136
  } catch {
54463
56137
  }
@@ -56911,8 +58585,8 @@ function updateStats2(results, state, isForEachIteration = false) {
56911
58585
  async function renderTemplateContent2(checkId, checkConfig, reviewSummary) {
56912
58586
  try {
56913
58587
  const { createExtendedLiquid: createExtendedLiquid2 } = await Promise.resolve().then(() => (init_liquid_extensions(), liquid_extensions_exports));
56914
- const fs27 = await import("fs/promises");
56915
- const path30 = await import("path");
58588
+ const fs29 = await import("fs/promises");
58589
+ const path31 = await import("path");
56916
58590
  const schemaRaw = checkConfig.schema || "plain";
56917
58591
  const schema = typeof schemaRaw === "string" && !schemaRaw.includes("{{") && !schemaRaw.includes("{%") ? schemaRaw : typeof schemaRaw === "object" ? "code-review" : "plain";
56918
58592
  let templateContent;
@@ -56921,27 +58595,27 @@ async function renderTemplateContent2(checkId, checkConfig, reviewSummary) {
56921
58595
  logger.debug(`[LevelDispatch] Using inline template for ${checkId}`);
56922
58596
  } else if (checkConfig.template && checkConfig.template.file) {
56923
58597
  const file = String(checkConfig.template.file);
56924
- const resolved = path30.resolve(process.cwd(), file);
56925
- templateContent = await fs27.readFile(resolved, "utf-8");
58598
+ const resolved = path31.resolve(process.cwd(), file);
58599
+ templateContent = await fs29.readFile(resolved, "utf-8");
56926
58600
  logger.debug(`[LevelDispatch] Using template file for ${checkId}: ${resolved}`);
56927
58601
  } else if (schema && schema !== "plain") {
56928
58602
  const sanitized = String(schema).replace(/[^a-zA-Z0-9-]/g, "");
56929
58603
  if (sanitized) {
56930
58604
  const candidatePaths = [
56931
- path30.join(__dirname, "output", sanitized, "template.liquid"),
58605
+ path31.join(__dirname, "output", sanitized, "template.liquid"),
56932
58606
  // bundled: dist/output/
56933
- path30.join(__dirname, "..", "..", "output", sanitized, "template.liquid"),
58607
+ path31.join(__dirname, "..", "..", "output", sanitized, "template.liquid"),
56934
58608
  // source (from state-machine/states)
56935
- path30.join(__dirname, "..", "..", "..", "output", sanitized, "template.liquid"),
58609
+ path31.join(__dirname, "..", "..", "..", "output", sanitized, "template.liquid"),
56936
58610
  // source (alternate)
56937
- path30.join(process.cwd(), "output", sanitized, "template.liquid"),
58611
+ path31.join(process.cwd(), "output", sanitized, "template.liquid"),
56938
58612
  // fallback: cwd/output/
56939
- path30.join(process.cwd(), "dist", "output", sanitized, "template.liquid")
58613
+ path31.join(process.cwd(), "dist", "output", sanitized, "template.liquid")
56940
58614
  // fallback: cwd/dist/output/
56941
58615
  ];
56942
58616
  for (const p of candidatePaths) {
56943
58617
  try {
56944
- templateContent = await fs27.readFile(p, "utf-8");
58618
+ templateContent = await fs29.readFile(p, "utf-8");
56945
58619
  if (templateContent) {
56946
58620
  logger.debug(`[LevelDispatch] Using schema template for ${checkId}: ${p}`);
56947
58621
  break;
@@ -57556,13 +59230,13 @@ var init_runner = __esm({
57556
59230
  });
57557
59231
 
57558
59232
  // src/utils/file-exclusion.ts
57559
- var import_ignore, fs21, path24, DEFAULT_EXCLUSION_PATTERNS, FileExclusionHelper;
59233
+ var import_ignore, fs23, path25, DEFAULT_EXCLUSION_PATTERNS, FileExclusionHelper;
57560
59234
  var init_file_exclusion = __esm({
57561
59235
  "src/utils/file-exclusion.ts"() {
57562
59236
  "use strict";
57563
59237
  import_ignore = __toESM(require("ignore"));
57564
- fs21 = __toESM(require("fs"));
57565
- path24 = __toESM(require("path"));
59238
+ fs23 = __toESM(require("fs"));
59239
+ path25 = __toESM(require("path"));
57566
59240
  DEFAULT_EXCLUSION_PATTERNS = [
57567
59241
  "dist/",
57568
59242
  "build/",
@@ -57581,7 +59255,7 @@ var init_file_exclusion = __esm({
57581
59255
  * @param additionalPatterns - Additional patterns to include (optional, defaults to common build artifacts)
57582
59256
  */
57583
59257
  constructor(workingDirectory = process.cwd(), additionalPatterns = DEFAULT_EXCLUSION_PATTERNS) {
57584
- const normalizedPath = path24.resolve(workingDirectory);
59258
+ const normalizedPath = path25.resolve(workingDirectory);
57585
59259
  if (normalizedPath.includes("\0")) {
57586
59260
  throw new Error("Invalid workingDirectory: contains null bytes");
57587
59261
  }
@@ -57593,11 +59267,11 @@ var init_file_exclusion = __esm({
57593
59267
  * @param additionalPatterns - Additional patterns to add to gitignore rules
57594
59268
  */
57595
59269
  loadGitignore(additionalPatterns) {
57596
- const gitignorePath = path24.resolve(this.workingDirectory, ".gitignore");
57597
- const resolvedWorkingDir = path24.resolve(this.workingDirectory);
59270
+ const gitignorePath = path25.resolve(this.workingDirectory, ".gitignore");
59271
+ const resolvedWorkingDir = path25.resolve(this.workingDirectory);
57598
59272
  try {
57599
- const relativePath = path24.relative(resolvedWorkingDir, gitignorePath);
57600
- if (relativePath.startsWith("..") || path24.isAbsolute(relativePath)) {
59273
+ const relativePath = path25.relative(resolvedWorkingDir, gitignorePath);
59274
+ if (relativePath.startsWith("..") || path25.isAbsolute(relativePath)) {
57601
59275
  throw new Error("Invalid gitignore path: path traversal detected");
57602
59276
  }
57603
59277
  if (relativePath !== ".gitignore") {
@@ -57607,8 +59281,8 @@ var init_file_exclusion = __esm({
57607
59281
  if (additionalPatterns && additionalPatterns.length > 0) {
57608
59282
  this.gitignore.add(additionalPatterns);
57609
59283
  }
57610
- if (fs21.existsSync(gitignorePath)) {
57611
- const rawContent = fs21.readFileSync(gitignorePath, "utf8");
59284
+ if (fs23.existsSync(gitignorePath)) {
59285
+ const rawContent = fs23.readFileSync(gitignorePath, "utf8");
57612
59286
  const gitignoreContent = rawContent.replace(/[\r\n]+/g, "\n").replace(/[\x00-\x09\x0B-\x1F\x7F]/g, "").split("\n").filter((line) => line.length < 1e3).join("\n").trim();
57613
59287
  this.gitignore.add(gitignoreContent);
57614
59288
  if (process.env.VISOR_DEBUG === "true") {
@@ -57640,13 +59314,13 @@ var git_repository_analyzer_exports = {};
57640
59314
  __export(git_repository_analyzer_exports, {
57641
59315
  GitRepositoryAnalyzer: () => GitRepositoryAnalyzer
57642
59316
  });
57643
- var import_simple_git2, path25, fs22, MAX_PATCH_SIZE, GitRepositoryAnalyzer;
59317
+ var import_simple_git2, path26, fs24, MAX_PATCH_SIZE, GitRepositoryAnalyzer;
57644
59318
  var init_git_repository_analyzer = __esm({
57645
59319
  "src/git-repository-analyzer.ts"() {
57646
59320
  "use strict";
57647
59321
  import_simple_git2 = require("simple-git");
57648
- path25 = __toESM(require("path"));
57649
- fs22 = __toESM(require("fs"));
59322
+ path26 = __toESM(require("path"));
59323
+ fs24 = __toESM(require("fs"));
57650
59324
  init_file_exclusion();
57651
59325
  MAX_PATCH_SIZE = 50 * 1024;
57652
59326
  GitRepositoryAnalyzer = class {
@@ -57835,7 +59509,7 @@ ${file.patch}`).join("\n\n");
57835
59509
  console.error(`\u23ED\uFE0F Skipping excluded file: ${file}`);
57836
59510
  continue;
57837
59511
  }
57838
- const filePath = path25.join(this.cwd, file);
59512
+ const filePath = path26.join(this.cwd, file);
57839
59513
  const fileChange = await this.analyzeFileChange(file, status2, filePath, includeContext);
57840
59514
  changes.push(fileChange);
57841
59515
  }
@@ -57942,7 +59616,7 @@ ${file.patch}`).join("\n\n");
57942
59616
  let content;
57943
59617
  let truncated = false;
57944
59618
  try {
57945
- if (includeContext && status !== "added" && fs22.existsSync(filePath)) {
59619
+ if (includeContext && status !== "added" && fs24.existsSync(filePath)) {
57946
59620
  const diff = await this.git.diff(["--", filename]).catch(() => "");
57947
59621
  if (diff) {
57948
59622
  const result = this.truncatePatch(diff, filename);
@@ -57952,7 +59626,7 @@ ${file.patch}`).join("\n\n");
57952
59626
  additions = lines.filter((line) => line.startsWith("+")).length;
57953
59627
  deletions = lines.filter((line) => line.startsWith("-")).length;
57954
59628
  }
57955
- } else if (status !== "added" && fs22.existsSync(filePath)) {
59629
+ } else if (status !== "added" && fs24.existsSync(filePath)) {
57956
59630
  const diff = await this.git.diff(["--", filename]).catch(() => "");
57957
59631
  if (diff) {
57958
59632
  const lines = diff.split("\n");
@@ -57960,17 +59634,17 @@ ${file.patch}`).join("\n\n");
57960
59634
  deletions = lines.filter((line) => line.startsWith("-")).length;
57961
59635
  }
57962
59636
  }
57963
- if (status === "added" && fs22.existsSync(filePath)) {
59637
+ if (status === "added" && fs24.existsSync(filePath)) {
57964
59638
  try {
57965
- const stats = fs22.statSync(filePath);
59639
+ const stats = fs24.statSync(filePath);
57966
59640
  if (stats.isFile() && stats.size < 1024 * 1024) {
57967
59641
  if (includeContext) {
57968
- content = fs22.readFileSync(filePath, "utf8");
59642
+ content = fs24.readFileSync(filePath, "utf8");
57969
59643
  const result = this.truncatePatch(content, filename);
57970
59644
  patch = result.patch;
57971
59645
  truncated = result.truncated;
57972
59646
  }
57973
- const fileContent = includeContext ? content : fs22.readFileSync(filePath, "utf8");
59647
+ const fileContent = includeContext ? content : fs24.readFileSync(filePath, "utf8");
57974
59648
  additions = fileContent.split("\n").length;
57975
59649
  }
57976
59650
  } catch {
@@ -58061,12 +59735,12 @@ function shellEscape(str) {
58061
59735
  function sanitizePathComponent(name) {
58062
59736
  return name.replace(/\.\./g, "").replace(/[\/\\]/g, "-").replace(/^\.+/, "").trim() || "unnamed";
58063
59737
  }
58064
- var fsp2, path26, WorkspaceManager;
59738
+ var fsp2, path27, WorkspaceManager;
58065
59739
  var init_workspace_manager = __esm({
58066
59740
  "src/utils/workspace-manager.ts"() {
58067
59741
  "use strict";
58068
59742
  fsp2 = __toESM(require("fs/promises"));
58069
- path26 = __toESM(require("path"));
59743
+ path27 = __toESM(require("path"));
58070
59744
  init_command_executor();
58071
59745
  init_logger();
58072
59746
  WorkspaceManager = class _WorkspaceManager {
@@ -58100,7 +59774,7 @@ var init_workspace_manager = __esm({
58100
59774
  };
58101
59775
  this.basePath = this.config.basePath;
58102
59776
  const workspaceDirName = sanitizePathComponent(this.config.name || this.sessionId);
58103
- this.workspacePath = path26.join(this.basePath, workspaceDirName);
59777
+ this.workspacePath = path27.join(this.basePath, workspaceDirName);
58104
59778
  }
58105
59779
  /**
58106
59780
  * Get or create a WorkspaceManager instance for a session
@@ -58195,7 +59869,7 @@ var init_workspace_manager = __esm({
58195
59869
  configuredMainProjectName || this.extractProjectName(this.originalPath)
58196
59870
  );
58197
59871
  this.usedNames.add(mainProjectName);
58198
- let mainProjectPath = path26.join(this.workspacePath, mainProjectName);
59872
+ let mainProjectPath = path27.join(this.workspacePath, mainProjectName);
58199
59873
  const isGitRepo = await this.isGitRepository(this.originalPath);
58200
59874
  if (isGitRepo) {
58201
59875
  try {
@@ -58213,10 +59887,10 @@ var init_workspace_manager = __esm({
58213
59887
  );
58214
59888
  if (gitRootResult.exitCode === 0) {
58215
59889
  const gitRoot = gitRootResult.stdout.trim();
58216
- const normalizedOriginal = path26.resolve(this.originalPath);
58217
- const normalizedRoot = path26.resolve(gitRoot);
59890
+ const normalizedOriginal = path27.resolve(this.originalPath);
59891
+ const normalizedRoot = path27.resolve(gitRoot);
58218
59892
  if (normalizedOriginal !== normalizedRoot) {
58219
- subdirOffset = path26.relative(normalizedRoot, normalizedOriginal);
59893
+ subdirOffset = path27.relative(normalizedRoot, normalizedOriginal);
58220
59894
  logger.info(`[Workspace] Original path is a subdirectory of git repo: ${subdirOffset}`);
58221
59895
  }
58222
59896
  }
@@ -58256,14 +59930,14 @@ var init_workspace_manager = __esm({
58256
59930
  }
58257
59931
  const worktreeRootPath = mainProjectPath;
58258
59932
  if (subdirOffset) {
58259
- mainProjectPath = path26.join(mainProjectPath, subdirOffset);
59933
+ mainProjectPath = path27.join(mainProjectPath, subdirOffset);
58260
59934
  logger.info(`[Workspace] Adjusted main project path to subdirectory: ${mainProjectPath}`);
58261
59935
  const subdirExists = await this.pathExists(mainProjectPath);
58262
59936
  if (!subdirExists) {
58263
59937
  logger.warn(
58264
59938
  `[Workspace] Subdirectory '${subdirOffset}' not found in worktree \u2014 falling back to worktree root`
58265
59939
  );
58266
- mainProjectPath = path26.join(this.workspacePath, mainProjectName);
59940
+ mainProjectPath = path27.join(this.workspacePath, mainProjectName);
58267
59941
  }
58268
59942
  }
58269
59943
  try {
@@ -58318,7 +59992,7 @@ var init_workspace_manager = __esm({
58318
59992
  let projectName = sanitizePathComponent(description || this.extractRepoName(repository));
58319
59993
  projectName = this.getUniqueName(projectName);
58320
59994
  this.usedNames.add(projectName);
58321
- const workspacePath = path26.join(this.workspacePath, projectName);
59995
+ const workspacePath = path27.join(this.workspacePath, projectName);
58322
59996
  await fsp2.rm(workspacePath, { recursive: true, force: true });
58323
59997
  try {
58324
59998
  await fsp2.symlink(worktreePath, workspacePath);
@@ -58428,7 +60102,7 @@ var init_workspace_manager = __esm({
58428
60102
  const now = Date.now();
58429
60103
  for (const entry of entries) {
58430
60104
  if (!entry.isDirectory()) continue;
58431
- const dirPath = path26.join(basePath, entry.name);
60105
+ const dirPath = path27.join(basePath, entry.name);
58432
60106
  try {
58433
60107
  const stat2 = await fsp2.stat(dirPath);
58434
60108
  if (now - stat2.mtimeMs > maxAgeMs) {
@@ -58436,8 +60110,8 @@ var init_workspace_manager = __esm({
58436
60110
  const subdirs = await fsp2.readdir(dirPath, { withFileTypes: true });
58437
60111
  for (const sub of subdirs) {
58438
60112
  if (!sub.isDirectory()) continue;
58439
- const subPath = path26.join(dirPath, sub.name);
58440
- const gitFilePath = path26.join(subPath, ".git");
60113
+ const subPath = path27.join(dirPath, sub.name);
60114
+ const gitFilePath = path27.join(subPath, ".git");
58441
60115
  try {
58442
60116
  const gitContent = await fsp2.readFile(gitFilePath, "utf-8");
58443
60117
  const match = gitContent.match(/gitdir:\s*(.+)/);
@@ -58683,7 +60357,7 @@ var init_workspace_manager = __esm({
58683
60357
  * Extract project name from path
58684
60358
  */
58685
60359
  extractProjectName(dirPath) {
58686
- return path26.basename(dirPath);
60360
+ return path27.basename(dirPath);
58687
60361
  }
58688
60362
  /**
58689
60363
  * Extract repository name from owner/repo format
@@ -62557,9 +64231,9 @@ var require_request3 = __commonJS({
62557
64231
  HttpMethod2["PATCH"] = "PATCH";
62558
64232
  })(HttpMethod = exports2.HttpMethod || (exports2.HttpMethod = {}));
62559
64233
  var SvixRequest = class {
62560
- constructor(method, path30) {
64234
+ constructor(method, path31) {
62561
64235
  this.method = method;
62562
- this.path = path30;
64236
+ this.path = path31;
62563
64237
  this.queryParams = {};
62564
64238
  this.headerParams = {};
62565
64239
  }
@@ -70321,11 +71995,11 @@ var require_dist2 = __commonJS({
70321
71995
  });
70322
71996
 
70323
71997
  // src/email/client.ts
70324
- var import_crypto6, EmailClient;
71998
+ var import_crypto7, EmailClient;
70325
71999
  var init_client3 = __esm({
70326
72000
  "src/email/client.ts"() {
70327
72001
  "use strict";
70328
- import_crypto6 = require("crypto");
72002
+ import_crypto7 = require("crypto");
70329
72003
  EmailClient = class {
70330
72004
  receiveBackend;
70331
72005
  sendBackend;
@@ -70430,7 +72104,7 @@ var init_client3 = __esm({
70430
72104
  const messages = [];
70431
72105
  for await (const msg of this.imapClient.fetch({ seen: false }, { source: true, uid: true })) {
70432
72106
  const parsed = await simpleParser(msg.source);
70433
- const messageId = parsed.messageId || `<${(0, import_crypto6.randomUUID)()}@visor>`;
72107
+ const messageId = parsed.messageId || `<${(0, import_crypto7.randomUUID)()}@visor>`;
70434
72108
  const inReplyTo = typeof parsed.inReplyTo === "string" ? parsed.inReplyTo : void 0;
70435
72109
  const references = parsed.references ? Array.isArray(parsed.references) ? parsed.references : [parsed.references] : void 0;
70436
72110
  messages.push({
@@ -70571,7 +72245,7 @@ var init_client3 = __esm({
70571
72245
  // ─── Send ───
70572
72246
  /** Send an email via configured backend (SMTP or Resend) */
70573
72247
  async sendEmail(opts) {
70574
- const messageId = opts.messageId || `<${(0, import_crypto6.randomUUID)()}@visor>`;
72248
+ const messageId = opts.messageId || `<${(0, import_crypto7.randomUUID)()}@visor>`;
70575
72249
  if (this.sendBackend === "resend") {
70576
72250
  return this.sendViaResend(opts, messageId);
70577
72251
  }
@@ -70654,7 +72328,7 @@ var init_client3 = __esm({
70654
72328
  }
70655
72329
  /** Generate a deterministic thread ID from a Message-ID chain */
70656
72330
  static deriveThreadId(rootMessageId) {
70657
- return (0, import_crypto6.createHash)("sha256").update(rootMessageId).digest("hex").slice(0, 16);
72331
+ return (0, import_crypto7.createHash)("sha256").update(rootMessageId).digest("hex").slice(0, 16);
70658
72332
  }
70659
72333
  };
70660
72334
  }
@@ -71148,11 +72822,11 @@ var init_markdown4 = __esm({
71148
72822
  });
71149
72823
 
71150
72824
  // src/whatsapp/client.ts
71151
- var import_crypto7, WhatsAppClient;
72825
+ var import_crypto8, WhatsAppClient;
71152
72826
  var init_client4 = __esm({
71153
72827
  "src/whatsapp/client.ts"() {
71154
72828
  "use strict";
71155
- import_crypto7 = require("crypto");
72829
+ import_crypto8 = require("crypto");
71156
72830
  init_markdown4();
71157
72831
  WhatsAppClient = class {
71158
72832
  accessToken;
@@ -71258,7 +72932,7 @@ var init_client4 = __esm({
71258
72932
  verifyWebhookSignature(rawBody, signatureHeader) {
71259
72933
  if (!this.appSecret) return true;
71260
72934
  if (!signatureHeader) return false;
71261
- const expected = "sha256=" + (0, import_crypto7.createHmac)("sha256", this.appSecret).update(rawBody).digest("hex");
72935
+ const expected = "sha256=" + (0, import_crypto8.createHmac)("sha256", this.appSecret).update(rawBody).digest("hex");
71262
72936
  return signatureHeader === expected;
71263
72937
  }
71264
72938
  /**
@@ -71762,13 +73436,13 @@ function taskRowToAgentTask(row) {
71762
73436
  workflow_id: row.workflow_id ?? void 0
71763
73437
  };
71764
73438
  }
71765
- var import_path16, import_fs12, import_crypto8, SqliteTaskStore;
73439
+ var import_path16, import_fs12, import_crypto9, SqliteTaskStore;
71766
73440
  var init_task_store = __esm({
71767
73441
  "src/agent-protocol/task-store.ts"() {
71768
73442
  "use strict";
71769
73443
  import_path16 = __toESM(require("path"));
71770
73444
  import_fs12 = __toESM(require("fs"));
71771
- import_crypto8 = __toESM(require("crypto"));
73445
+ import_crypto9 = __toESM(require("crypto"));
71772
73446
  init_logger();
71773
73447
  init_types();
71774
73448
  init_state_transitions();
@@ -71849,7 +73523,7 @@ var init_task_store = __esm({
71849
73523
  // -------------------------------------------------------------------------
71850
73524
  createTask(params) {
71851
73525
  const db = this.getDb();
71852
- const id = import_crypto8.default.randomUUID();
73526
+ const id = import_crypto9.default.randomUUID();
71853
73527
  const now = nowISO();
71854
73528
  const contextId = this.resolveContextId(params.requestMessage, params.contextId);
71855
73529
  const expiresAt = params.expiresAt ?? null;
@@ -72065,19 +73739,27 @@ var init_task_store = __esm({
72065
73739
  // -------------------------------------------------------------------------
72066
73740
  // Cleanup
72067
73741
  // -------------------------------------------------------------------------
72068
- failStaleTasks(reason) {
73742
+ failStaleTasks(reason, claimedBy) {
72069
73743
  const db = this.getDb();
72070
73744
  const now = nowISO();
72071
73745
  const msg = reason || "Process terminated while task was running";
72072
73746
  const statusMessage = JSON.stringify({
72073
- message_id: import_crypto8.default.randomUUID(),
73747
+ message_id: import_crypto9.default.randomUUID(),
72074
73748
  role: "agent",
72075
73749
  parts: [{ text: msg }]
72076
73750
  });
73751
+ if (claimedBy) {
73752
+ const result2 = db.prepare(
73753
+ `UPDATE agent_tasks
73754
+ SET state = 'failed', updated_at = ?, status_message = ?
73755
+ WHERE state = 'working' AND claimed_by = ?`
73756
+ ).run(now, statusMessage, claimedBy);
73757
+ return result2.changes;
73758
+ }
72077
73759
  const result = db.prepare(
72078
73760
  `UPDATE agent_tasks
72079
73761
  SET state = 'failed', updated_at = ?, status_message = ?
72080
- WHERE state = 'working'`
73762
+ WHERE state = 'working' AND claimed_by IS NULL`
72081
73763
  ).run(now, statusMessage);
72082
73764
  return result.changes;
72083
73765
  }
@@ -72087,7 +73769,7 @@ var init_task_store = __esm({
72087
73769
  const cutoff = new Date(Date.now() - olderThanMs).toISOString();
72088
73770
  const msg = reason || "Task exceeded maximum working duration";
72089
73771
  const statusMessage = JSON.stringify({
72090
- message_id: import_crypto8.default.randomUUID(),
73772
+ message_id: import_crypto9.default.randomUUID(),
72091
73773
  role: "agent",
72092
73774
  parts: [{ text: msg }]
72093
73775
  });
@@ -72256,11 +73938,11 @@ var init_task_stream_manager = __esm({
72256
73938
  });
72257
73939
 
72258
73940
  // src/agent-protocol/push-notification-manager.ts
72259
- var import_crypto9, PushNotificationManager;
73941
+ var import_crypto10, PushNotificationManager;
72260
73942
  var init_push_notification_manager = __esm({
72261
73943
  "src/agent-protocol/push-notification-manager.ts"() {
72262
73944
  "use strict";
72263
- import_crypto9 = __toESM(require("crypto"));
73945
+ import_crypto10 = __toESM(require("crypto"));
72264
73946
  init_logger();
72265
73947
  PushNotificationManager = class {
72266
73948
  db = null;
@@ -72305,7 +73987,7 @@ var init_push_notification_manager = __esm({
72305
73987
  // -------------------------------------------------------------------------
72306
73988
  create(config) {
72307
73989
  const db = this.getDb();
72308
- const id = config.id ?? import_crypto9.default.randomUUID();
73990
+ const id = config.id ?? import_crypto10.default.randomUUID();
72309
73991
  const now = (/* @__PURE__ */ new Date()).toISOString();
72310
73992
  db.prepare(
72311
73993
  `INSERT INTO agent_push_configs (id, task_id, url, token, auth_scheme, auth_credentials, created_at)
@@ -72403,11 +74085,11 @@ var init_push_notification_manager = __esm({
72403
74085
  });
72404
74086
 
72405
74087
  // src/agent-protocol/task-queue.ts
72406
- var import_crypto10, DEFAULT_CONFIG, TaskQueue;
74088
+ var import_crypto11, DEFAULT_CONFIG, TaskQueue;
72407
74089
  var init_task_queue = __esm({
72408
74090
  "src/agent-protocol/task-queue.ts"() {
72409
74091
  "use strict";
72410
- import_crypto10 = __toESM(require("crypto"));
74092
+ import_crypto11 = __toESM(require("crypto"));
72411
74093
  init_logger();
72412
74094
  init_trace_helpers();
72413
74095
  DEFAULT_CONFIG = {
@@ -72420,7 +74102,7 @@ var init_task_queue = __esm({
72420
74102
  this.taskStore = taskStore;
72421
74103
  this.executor = executor;
72422
74104
  this.config = { ...DEFAULT_CONFIG, ...config };
72423
- this.workerId = workerId ?? import_crypto10.default.randomUUID();
74105
+ this.workerId = workerId ?? import_crypto11.default.randomUUID();
72424
74106
  }
72425
74107
  running = false;
72426
74108
  timer = null;
@@ -72494,7 +74176,7 @@ var init_task_queue = __esm({
72494
74176
  }
72495
74177
  if (result.success) {
72496
74178
  const completedMsg = {
72497
- message_id: import_crypto10.default.randomUUID(),
74179
+ message_id: import_crypto11.default.randomUUID(),
72498
74180
  role: "agent",
72499
74181
  parts: [
72500
74182
  {
@@ -72506,7 +74188,7 @@ var init_task_queue = __esm({
72506
74188
  this.taskStore.updateTaskState(task.id, "completed", completedMsg);
72507
74189
  } else {
72508
74190
  this.taskStore.updateTaskState(task.id, "failed", {
72509
- message_id: import_crypto10.default.randomUUID(),
74191
+ message_id: import_crypto11.default.randomUUID(),
72510
74192
  role: "agent",
72511
74193
  parts: [{ text: result.error ?? "Task execution failed" }]
72512
74194
  });
@@ -72517,7 +74199,7 @@ var init_task_queue = __esm({
72517
74199
  );
72518
74200
  try {
72519
74201
  this.taskStore.updateTaskState(task.id, "failed", {
72520
- message_id: import_crypto10.default.randomUUID(),
74202
+ message_id: import_crypto11.default.randomUUID(),
72521
74203
  role: "agent",
72522
74204
  parts: [{ text: err instanceof Error ? err.message : "Unknown error" }]
72523
74205
  });
@@ -72571,7 +74253,7 @@ function timingSafeEqual(a, b) {
72571
74253
  if (!a || !b) return false;
72572
74254
  if (a.length !== b.length) return false;
72573
74255
  try {
72574
- return import_crypto11.default.timingSafeEqual(Buffer.from(a), Buffer.from(b));
74256
+ return import_crypto12.default.timingSafeEqual(Buffer.from(a), Buffer.from(b));
72575
74257
  } catch {
72576
74258
  return false;
72577
74259
  }
@@ -72645,7 +74327,7 @@ function resultToArtifacts(checkResults) {
72645
74327
  }
72646
74328
  if (parts.length > 0) {
72647
74329
  artifacts.push({
72648
- artifact_id: import_crypto11.default.randomUUID(),
74330
+ artifact_id: import_crypto12.default.randomUUID(),
72649
74331
  name: checkId,
72650
74332
  description: `Output from check: ${checkId}`,
72651
74333
  parts
@@ -72654,14 +74336,14 @@ function resultToArtifacts(checkResults) {
72654
74336
  }
72655
74337
  return artifacts;
72656
74338
  }
72657
- var import_http2, import_https, import_fs13, import_crypto11, A2AFrontend;
74339
+ var import_http2, import_https, import_fs13, import_crypto12, A2AFrontend;
72658
74340
  var init_a2a_frontend = __esm({
72659
74341
  "src/agent-protocol/a2a-frontend.ts"() {
72660
74342
  "use strict";
72661
74343
  import_http2 = __toESM(require("http"));
72662
74344
  import_https = __toESM(require("https"));
72663
74345
  import_fs13 = __toESM(require("fs"));
72664
- import_crypto11 = __toESM(require("crypto"));
74346
+ import_crypto12 = __toESM(require("crypto"));
72665
74347
  init_logger();
72666
74348
  init_task_store();
72667
74349
  init_types();
@@ -72755,7 +74437,7 @@ var init_a2a_frontend = __esm({
72755
74437
  if (!taskId) return;
72756
74438
  try {
72757
74439
  const statusMessage = {
72758
- message_id: import_crypto11.default.randomUUID(),
74440
+ message_id: import_crypto12.default.randomUUID(),
72759
74441
  role: "agent",
72760
74442
  parts: [{ text: envelope.payload?.prompt ?? "Agent requires input" }]
72761
74443
  };
@@ -72807,6 +74489,40 @@ var init_a2a_frontend = __esm({
72807
74489
  }
72808
74490
  }
72809
74491
  }
74492
+ /**
74493
+ * Stop listening for new connections and free the port.
74494
+ * In-flight tasks continue processing.
74495
+ */
74496
+ async stopListening() {
74497
+ if (this.server) {
74498
+ const srv = this.server;
74499
+ if (typeof srv.closeAllConnections === "function") {
74500
+ srv.closeAllConnections();
74501
+ }
74502
+ await new Promise((resolve17) => srv.close(() => resolve17()));
74503
+ this.server = null;
74504
+ }
74505
+ }
74506
+ /**
74507
+ * Drain: stop accepting new tasks, wait for in-flight tasks to complete.
74508
+ * @param timeoutMs - Max wait time. 0 = unlimited (default).
74509
+ */
74510
+ async drain(timeoutMs = 0) {
74511
+ await this.stopListening();
74512
+ if (this.taskQueue) {
74513
+ this.taskQueue.stop();
74514
+ const startedAt = Date.now();
74515
+ while (this.taskQueue.getActiveCount() > 0) {
74516
+ if (timeoutMs > 0 && Date.now() - startedAt >= timeoutMs) {
74517
+ break;
74518
+ }
74519
+ await new Promise((resolve17) => setTimeout(resolve17, 500));
74520
+ }
74521
+ this.taskQueue = null;
74522
+ }
74523
+ this.stopCleanupSweep();
74524
+ this.streamManager.shutdown();
74525
+ }
72810
74526
  async stop() {
72811
74527
  this.stopCleanupSweep();
72812
74528
  if (this.taskQueue) {
@@ -72927,7 +74643,7 @@ var init_a2a_frontend = __esm({
72927
74643
  const response = await this.handleFollowUpMessage(existingTaskId, body);
72928
74644
  return sendJson(res, 200, response);
72929
74645
  }
72930
- const contextId = body.message.context_id ?? import_crypto11.default.randomUUID();
74646
+ const contextId = body.message.context_id ?? import_crypto12.default.randomUUID();
72931
74647
  const workflowId = resolveWorkflow(body, this.config);
72932
74648
  const blocking = body.configuration?.blocking ?? false;
72933
74649
  await withActiveSpan(
@@ -73040,7 +74756,7 @@ var init_a2a_frontend = __esm({
73040
74756
  if (!body.message?.parts?.length) {
73041
74757
  throw new InvalidRequestError("Message must contain at least one part");
73042
74758
  }
73043
- const contextId = body.message.context_id ?? import_crypto11.default.randomUUID();
74759
+ const contextId = body.message.context_id ?? import_crypto12.default.randomUUID();
73044
74760
  const workflowId = resolveWorkflow(body, this.config);
73045
74761
  const task = this.taskStore.createTask({
73046
74762
  contextId,
@@ -73174,7 +74890,7 @@ var init_a2a_frontend = __esm({
73174
74890
  await this.executeTaskViaEngine(task, message);
73175
74891
  } else {
73176
74892
  const agentResponse = {
73177
- message_id: import_crypto11.default.randomUUID(),
74893
+ message_id: import_crypto12.default.randomUUID(),
73178
74894
  role: "agent",
73179
74895
  parts: [{ text: `Task ${task.id} received and processed.`, media_type: "text/markdown" }]
73180
74896
  };
@@ -73186,7 +74902,7 @@ var init_a2a_frontend = __esm({
73186
74902
  logger.error(`[A2A] Task ${task.id} execution failed: ${errorMsg}`);
73187
74903
  try {
73188
74904
  const failMessage = {
73189
- message_id: import_crypto11.default.randomUUID(),
74905
+ message_id: import_crypto12.default.randomUUID(),
73190
74906
  role: "agent",
73191
74907
  parts: [{ text: errorMsg }]
73192
74908
  };
@@ -73229,7 +74945,7 @@ ${issueText}`, media_type: "text/markdown" });
73229
74945
  }
73230
74946
  if (summaryParts.length > 0) {
73231
74947
  artifacts.push({
73232
- artifact_id: import_crypto11.default.randomUUID(),
74948
+ artifact_id: import_crypto12.default.randomUUID(),
73233
74949
  name: "review-summary",
73234
74950
  description: "Review summary with issues found",
73235
74951
  parts: summaryParts
@@ -73249,7 +74965,7 @@ ${issueText}`, media_type: "text/markdown" });
73249
74965
  }
73250
74966
  if (parts.length > 0) {
73251
74967
  artifacts.push({
73252
- artifact_id: import_crypto11.default.randomUUID(),
74968
+ artifact_id: import_crypto12.default.randomUUID(),
73253
74969
  name: cr.checkName ?? groupName,
73254
74970
  description: `Output from check: ${cr.checkName ?? groupName}`,
73255
74971
  parts
@@ -73260,7 +74976,7 @@ ${issueText}`, media_type: "text/markdown" });
73260
74976
  }
73261
74977
  if (artifacts.length === 0) {
73262
74978
  artifacts.push({
73263
- artifact_id: import_crypto11.default.randomUUID(),
74979
+ artifact_id: import_crypto12.default.randomUUID(),
73264
74980
  name: "result",
73265
74981
  description: "Execution result",
73266
74982
  parts: [
@@ -73282,7 +74998,7 @@ ${issueText}`, media_type: "text/markdown" });
73282
74998
  );
73283
74999
  }
73284
75000
  const agentResponse = {
73285
- message_id: import_crypto11.default.randomUUID(),
75001
+ message_id: import_crypto12.default.randomUUID(),
73286
75002
  role: "agent",
73287
75003
  parts: [
73288
75004
  {
@@ -73335,7 +75051,7 @@ ${issueText}`, media_type: "text/markdown" });
73335
75051
  }
73336
75052
  if (parts.length === 0) return null;
73337
75053
  return {
73338
- artifact_id: import_crypto11.default.randomUUID(),
75054
+ artifact_id: import_crypto12.default.randomUUID(),
73339
75055
  name: p.checkId ?? "check-result",
73340
75056
  parts
73341
75057
  };
@@ -73533,15 +75249,15 @@ function serializeRunState(state) {
73533
75249
  ])
73534
75250
  };
73535
75251
  }
73536
- var path29, fs26, StateMachineExecutionEngine;
75252
+ var path30, fs28, StateMachineExecutionEngine;
73537
75253
  var init_state_machine_execution_engine = __esm({
73538
75254
  "src/state-machine-execution-engine.ts"() {
73539
75255
  "use strict";
73540
75256
  init_runner();
73541
75257
  init_logger();
73542
75258
  init_sandbox_manager();
73543
- path29 = __toESM(require("path"));
73544
- fs26 = __toESM(require("fs"));
75259
+ path30 = __toESM(require("path"));
75260
+ fs28 = __toESM(require("fs"));
73545
75261
  StateMachineExecutionEngine = class _StateMachineExecutionEngine {
73546
75262
  workingDirectory;
73547
75263
  executionContext;
@@ -73929,9 +75645,9 @@ var init_state_machine_execution_engine = __esm({
73929
75645
  }
73930
75646
  const checkId = String(ev?.checkId || "unknown");
73931
75647
  const threadKey = ev?.threadKey || (channel && threadTs ? `${channel}:${threadTs}` : "session");
73932
- const baseDir = process.env.VISOR_SNAPSHOT_DIR || path29.resolve(process.cwd(), ".visor", "snapshots");
73933
- fs26.mkdirSync(baseDir, { recursive: true });
73934
- const filePath = path29.join(baseDir, `${threadKey}-${checkId}.json`);
75648
+ const baseDir = process.env.VISOR_SNAPSHOT_DIR || path30.resolve(process.cwd(), ".visor", "snapshots");
75649
+ fs28.mkdirSync(baseDir, { recursive: true });
75650
+ const filePath = path30.join(baseDir, `${threadKey}-${checkId}.json`);
73935
75651
  await this.saveSnapshotToFile(filePath);
73936
75652
  logger.info(`[Snapshot] Saved run snapshot: ${filePath}`);
73937
75653
  try {
@@ -74072,7 +75788,7 @@ var init_state_machine_execution_engine = __esm({
74072
75788
  * Does not include secrets. Intended for debugging and future resume support.
74073
75789
  */
74074
75790
  async saveSnapshotToFile(filePath) {
74075
- const fs27 = await import("fs/promises");
75791
+ const fs29 = await import("fs/promises");
74076
75792
  const ctx = this._lastContext;
74077
75793
  const runner = this._lastRunner;
74078
75794
  if (!ctx || !runner) {
@@ -74092,14 +75808,14 @@ var init_state_machine_execution_engine = __esm({
74092
75808
  journal: entries,
74093
75809
  requestedChecks: ctx.requestedChecks || []
74094
75810
  };
74095
- await fs27.writeFile(filePath, JSON.stringify(payload, null, 2), "utf8");
75811
+ await fs29.writeFile(filePath, JSON.stringify(payload, null, 2), "utf8");
74096
75812
  }
74097
75813
  /**
74098
75814
  * Load a snapshot JSON from file and return it. Resume support can build on this.
74099
75815
  */
74100
75816
  async loadSnapshotFromFile(filePath) {
74101
- const fs27 = await import("fs/promises");
74102
- const raw = await fs27.readFile(filePath, "utf8");
75817
+ const fs29 = await import("fs/promises");
75818
+ const raw = await fs29.readFile(filePath, "utf8");
74103
75819
  return JSON.parse(raw);
74104
75820
  }
74105
75821
  /**