@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.
- package/defaults/code-talk.yaml +80 -14
- package/defaults/engineer.yaml +33 -15
- package/defaults/skills/code-explorer.yaml +5 -0
- package/dist/agent-protocol/a2a-frontend.d.ts +10 -0
- package/dist/agent-protocol/a2a-frontend.d.ts.map +1 -1
- package/dist/agent-protocol/task-evaluator.d.ts +52 -0
- package/dist/agent-protocol/task-evaluator.d.ts.map +1 -0
- package/dist/agent-protocol/task-store.d.ts +5 -3
- package/dist/agent-protocol/task-store.d.ts.map +1 -1
- package/dist/agent-protocol/tasks-cli-handler.d.ts.map +1 -1
- package/dist/agent-protocol/tasks-tui.d.ts +34 -0
- package/dist/agent-protocol/tasks-tui.d.ts.map +1 -0
- package/dist/agent-protocol/trace-serializer.d.ts +90 -0
- package/dist/agent-protocol/trace-serializer.d.ts.map +1 -0
- package/dist/agent-protocol/track-execution.d.ts +2 -0
- package/dist/agent-protocol/track-execution.d.ts.map +1 -1
- package/dist/cli-main.d.ts.map +1 -1
- package/dist/defaults/code-talk.yaml +80 -14
- package/dist/defaults/engineer.yaml +33 -15
- package/dist/defaults/skills/code-explorer.yaml +5 -0
- package/dist/docs/commands.md +57 -14
- package/dist/docs/configuration.md +2 -0
- package/dist/docs/guides/graceful-restart.md +178 -0
- package/dist/docs/observability.md +69 -0
- package/dist/docs/production-deployment.md +17 -0
- package/dist/email/polling-runner.d.ts +4 -0
- package/dist/email/polling-runner.d.ts.map +1 -1
- package/dist/generated/config-schema.d.ts +70 -6
- package/dist/generated/config-schema.d.ts.map +1 -1
- package/dist/generated/config-schema.json +73 -6
- package/dist/index.js +5006 -886
- package/dist/output/traces/{run-2026-03-17T13-58-29-402Z.ndjson → run-2026-03-18T19-02-50-465Z.ndjson} +84 -84
- package/dist/{traces/run-2026-03-17T13-59-10-403Z.ndjson → output/traces/run-2026-03-18T19-03-30-428Z.ndjson} +2037 -2037
- package/dist/providers/mcp-custom-sse-server.d.ts +4 -0
- package/dist/providers/mcp-custom-sse-server.d.ts.map +1 -1
- package/dist/runners/graceful-restart.d.ts +46 -0
- package/dist/runners/graceful-restart.d.ts.map +1 -0
- package/dist/runners/mcp-server-runner.d.ts +12 -0
- package/dist/runners/mcp-server-runner.d.ts.map +1 -1
- package/dist/runners/runner-factory.d.ts.map +1 -1
- package/dist/runners/runner-host.d.ts +12 -0
- package/dist/runners/runner-host.d.ts.map +1 -1
- package/dist/runners/runner.d.ts +12 -0
- package/dist/runners/runner.d.ts.map +1 -1
- package/dist/sdk/{a2a-frontend-IWOUJOIZ.mjs → a2a-frontend-4LP3MLTS.mjs} +47 -5
- package/dist/sdk/a2a-frontend-4LP3MLTS.mjs.map +1 -0
- package/dist/sdk/a2a-frontend-5J3UNFY4.mjs +1718 -0
- package/dist/sdk/a2a-frontend-5J3UNFY4.mjs.map +1 -0
- package/dist/sdk/{a2a-frontend-BDACLGMA.mjs → a2a-frontend-MU5EO2HZ.mjs} +35 -1
- package/dist/sdk/a2a-frontend-MU5EO2HZ.mjs.map +1 -0
- package/dist/sdk/{check-provider-registry-4YKTEDKF.mjs → check-provider-registry-MHXQGUNN.mjs} +7 -7
- package/dist/sdk/{check-provider-registry-4YFVBGYU.mjs → check-provider-registry-RRWCXSTG.mjs} +3 -3
- package/dist/sdk/{check-provider-registry-67ZLGDDQ.mjs → check-provider-registry-Y33CRFVD.mjs} +7 -7
- package/dist/sdk/{chunk-DGIH6EX3.mjs → chunk-4AXAVXG5.mjs} +151 -281
- package/dist/sdk/chunk-4AXAVXG5.mjs.map +1 -0
- package/dist/sdk/{chunk-VMVIM4JB.mjs → chunk-4I3TJ7UJ.mjs} +37 -7
- package/dist/sdk/chunk-4I3TJ7UJ.mjs.map +1 -0
- package/dist/sdk/{chunk-VXC2XNQJ.mjs → chunk-5J3DNRF7.mjs} +3 -3
- package/dist/sdk/{chunk-7YZSSO4X.mjs → chunk-6DPPP7LD.mjs} +10 -10
- package/dist/sdk/chunk-7ERVRLDV.mjs +296 -0
- package/dist/sdk/chunk-7ERVRLDV.mjs.map +1 -0
- package/dist/sdk/{chunk-4DVP6KVC.mjs → chunk-7Z2WHX2J.mjs} +71 -30
- package/dist/sdk/chunk-7Z2WHX2J.mjs.map +1 -0
- package/dist/sdk/chunk-ANUT54HW.mjs +1502 -0
- package/dist/sdk/chunk-ANUT54HW.mjs.map +1 -0
- package/dist/sdk/{chunk-J73GEFPT.mjs → chunk-DHETLQIX.mjs} +2 -2
- package/dist/sdk/{chunk-QGBASDYP.mjs → chunk-JCOSKBMP.mjs} +71 -30
- package/dist/sdk/chunk-JCOSKBMP.mjs.map +1 -0
- package/dist/sdk/chunk-MK7ONH47.mjs +739 -0
- package/dist/sdk/chunk-MK7ONH47.mjs.map +1 -0
- package/dist/sdk/chunk-QXT47ZHR.mjs +390 -0
- package/dist/sdk/chunk-QXT47ZHR.mjs.map +1 -0
- package/dist/sdk/chunk-V75NEIXL.mjs +296 -0
- package/dist/sdk/chunk-V75NEIXL.mjs.map +1 -0
- package/dist/sdk/chunk-ZOF5QT6U.mjs +5943 -0
- package/dist/sdk/chunk-ZOF5QT6U.mjs.map +1 -0
- package/dist/sdk/{config-TSA5FUOM.mjs → config-2STD74CJ.mjs} +2 -2
- package/dist/sdk/config-JE4HKTWW.mjs +16 -0
- package/dist/sdk/{failure-condition-evaluator-HTPB5FYW.mjs → failure-condition-evaluator-5DZYMCGW.mjs} +4 -4
- package/dist/sdk/failure-condition-evaluator-R6DCDJAV.mjs +18 -0
- package/dist/sdk/{github-frontend-3SDFCCKI.mjs → github-frontend-3PSCKPAJ.mjs} +4 -4
- package/dist/sdk/github-frontend-L3F5JXPJ.mjs +1394 -0
- package/dist/sdk/github-frontend-L3F5JXPJ.mjs.map +1 -0
- package/dist/sdk/{host-QE4L7UXE.mjs → host-54CHV2LW.mjs} +3 -3
- package/dist/sdk/{host-VBBSLUWG.mjs → host-WAU6CT42.mjs} +3 -3
- package/dist/sdk/{host-CVH2CSHM.mjs → host-X5ZZCEWN.mjs} +2 -2
- package/dist/sdk/{routing-YVMTKFDZ.mjs → routing-CVQT4KHX.mjs} +5 -5
- package/dist/sdk/routing-EBAE5SSO.mjs +26 -0
- package/dist/sdk/{schedule-tool-Z5VG67JK.mjs → schedule-tool-POY3CDZL.mjs} +7 -7
- package/dist/sdk/{schedule-tool-ADUXTCY7.mjs → schedule-tool-R2OAATUS.mjs} +7 -7
- package/dist/sdk/{schedule-tool-ZMX3Y7LF.mjs → schedule-tool-Z6QYL2B3.mjs} +3 -3
- package/dist/sdk/{schedule-tool-handler-N7UNABOA.mjs → schedule-tool-handler-J4NUETJ6.mjs} +3 -3
- package/dist/sdk/{schedule-tool-handler-PCERK6ZZ.mjs → schedule-tool-handler-JMAKHPI7.mjs} +7 -7
- package/dist/sdk/{schedule-tool-handler-QOJVFRB4.mjs → schedule-tool-handler-MWFUIQKR.mjs} +7 -7
- package/dist/sdk/sdk.d.mts +33 -0
- package/dist/sdk/sdk.d.ts +33 -0
- package/dist/sdk/sdk.js +2058 -342
- package/dist/sdk/sdk.js.map +1 -1
- package/dist/sdk/sdk.mjs +6 -6
- package/dist/sdk/task-evaluator-HLNXKKVV.mjs +1278 -0
- package/dist/sdk/task-evaluator-HLNXKKVV.mjs.map +1 -0
- package/dist/sdk/{trace-helpers-KXDOJWBL.mjs → trace-helpers-HL5FBX65.mjs} +3 -3
- package/dist/sdk/trace-helpers-WJXYVV4S.mjs +29 -0
- package/dist/sdk/trace-helpers-WJXYVV4S.mjs.map +1 -0
- package/dist/sdk/trace-reader-ZY77OFNM.mjs +266 -0
- package/dist/sdk/trace-reader-ZY77OFNM.mjs.map +1 -0
- package/dist/sdk/track-execution-MKIQXP2C.mjs +136 -0
- package/dist/sdk/track-execution-MKIQXP2C.mjs.map +1 -0
- package/dist/sdk/track-execution-YUXQ6WQH.mjs +136 -0
- package/dist/sdk/track-execution-YUXQ6WQH.mjs.map +1 -0
- package/dist/sdk/{workflow-check-provider-NTHC5ZBF.mjs → workflow-check-provider-SE5I7EMA.mjs} +7 -7
- package/dist/sdk/workflow-check-provider-SE5I7EMA.mjs.map +1 -0
- package/dist/sdk/{workflow-check-provider-SRIMWKLQ.mjs → workflow-check-provider-VKYGI5GK.mjs} +3 -3
- package/dist/sdk/workflow-check-provider-VKYGI5GK.mjs.map +1 -0
- package/dist/sdk/{workflow-check-provider-CJXW2Z4F.mjs → workflow-check-provider-YDGZRI3Z.mjs} +7 -7
- package/dist/sdk/workflow-check-provider-YDGZRI3Z.mjs.map +1 -0
- package/dist/slack/socket-runner.d.ts +12 -0
- package/dist/slack/socket-runner.d.ts.map +1 -1
- package/dist/teams/webhook-runner.d.ts +4 -0
- package/dist/teams/webhook-runner.d.ts.map +1 -1
- package/dist/telegram/polling-runner.d.ts +2 -0
- package/dist/telegram/polling-runner.d.ts.map +1 -1
- package/dist/traces/{run-2026-03-17T13-58-29-402Z.ndjson → run-2026-03-18T19-02-50-465Z.ndjson} +84 -84
- package/dist/{output/traces/run-2026-03-17T13-59-10-403Z.ndjson → traces/run-2026-03-18T19-03-30-428Z.ndjson} +2037 -2037
- package/dist/types/config.d.ts +33 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/whatsapp/webhook-runner.d.ts +4 -0
- package/dist/whatsapp/webhook-runner.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/sdk/a2a-frontend-BDACLGMA.mjs.map +0 -1
- package/dist/sdk/a2a-frontend-IWOUJOIZ.mjs.map +0 -1
- package/dist/sdk/chunk-4DVP6KVC.mjs.map +0 -1
- package/dist/sdk/chunk-DGIH6EX3.mjs.map +0 -1
- package/dist/sdk/chunk-QGBASDYP.mjs.map +0 -1
- package/dist/sdk/chunk-VMVIM4JB.mjs.map +0 -1
- /package/dist/sdk/{check-provider-registry-4YFVBGYU.mjs.map → check-provider-registry-MHXQGUNN.mjs.map} +0 -0
- /package/dist/sdk/{check-provider-registry-4YKTEDKF.mjs.map → check-provider-registry-RRWCXSTG.mjs.map} +0 -0
- /package/dist/sdk/{check-provider-registry-67ZLGDDQ.mjs.map → check-provider-registry-Y33CRFVD.mjs.map} +0 -0
- /package/dist/sdk/{chunk-VXC2XNQJ.mjs.map → chunk-5J3DNRF7.mjs.map} +0 -0
- /package/dist/sdk/{chunk-7YZSSO4X.mjs.map → chunk-6DPPP7LD.mjs.map} +0 -0
- /package/dist/sdk/{chunk-J73GEFPT.mjs.map → chunk-DHETLQIX.mjs.map} +0 -0
- /package/dist/sdk/{config-TSA5FUOM.mjs.map → config-2STD74CJ.mjs.map} +0 -0
- /package/dist/sdk/{failure-condition-evaluator-HTPB5FYW.mjs.map → config-JE4HKTWW.mjs.map} +0 -0
- /package/dist/sdk/{routing-YVMTKFDZ.mjs.map → failure-condition-evaluator-5DZYMCGW.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-ADUXTCY7.mjs.map → failure-condition-evaluator-R6DCDJAV.mjs.map} +0 -0
- /package/dist/sdk/{github-frontend-3SDFCCKI.mjs.map → github-frontend-3PSCKPAJ.mjs.map} +0 -0
- /package/dist/sdk/{host-CVH2CSHM.mjs.map → host-54CHV2LW.mjs.map} +0 -0
- /package/dist/sdk/{host-QE4L7UXE.mjs.map → host-WAU6CT42.mjs.map} +0 -0
- /package/dist/sdk/{host-VBBSLUWG.mjs.map → host-X5ZZCEWN.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-Z5VG67JK.mjs.map → routing-CVQT4KHX.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-ZMX3Y7LF.mjs.map → routing-EBAE5SSO.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-N7UNABOA.mjs.map → schedule-tool-POY3CDZL.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-PCERK6ZZ.mjs.map → schedule-tool-R2OAATUS.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-QOJVFRB4.mjs.map → schedule-tool-Z6QYL2B3.mjs.map} +0 -0
- /package/dist/sdk/{trace-helpers-KXDOJWBL.mjs.map → schedule-tool-handler-J4NUETJ6.mjs.map} +0 -0
- /package/dist/sdk/{workflow-check-provider-CJXW2Z4F.mjs.map → schedule-tool-handler-JMAKHPI7.mjs.map} +0 -0
- /package/dist/sdk/{workflow-check-provider-NTHC5ZBF.mjs.map → schedule-tool-handler-MWFUIQKR.mjs.map} +0 -0
- /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.
|
|
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-
|
|
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
|
|
1289
|
-
const
|
|
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 =
|
|
1293
|
-
if (!
|
|
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 ||
|
|
1297
|
-
if (!
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
|
1960
|
-
if (state.forwardRunGuards.has(
|
|
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(
|
|
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
|
|
3942
|
-
if (!
|
|
3943
|
-
const parts =
|
|
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,
|
|
4063
|
-
if (!obj || !
|
|
4064
|
-
const parts =
|
|
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
|
|
7838
|
-
const
|
|
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 =
|
|
7848
|
-
templateContent = await
|
|
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
|
-
|
|
7854
|
+
path31.join(__dirname, "output", sanitized, "template.liquid"),
|
|
7855
7855
|
// bundled: dist/output/
|
|
7856
|
-
|
|
7856
|
+
path31.join(__dirname, "..", "..", "output", sanitized, "template.liquid"),
|
|
7857
7857
|
// source: output/
|
|
7858
|
-
|
|
7858
|
+
path31.join(process.cwd(), "output", sanitized, "template.liquid"),
|
|
7859
7859
|
// fallback: cwd/output/
|
|
7860
|
-
|
|
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
|
|
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
|
|
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 (
|
|
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
|
|
9814
|
-
const
|
|
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 ||
|
|
9929
|
-
if (!
|
|
9930
|
-
|
|
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 =
|
|
9932
|
+
const debugFile = path31.join(
|
|
9933
9933
|
debugArtifactsDir,
|
|
9934
9934
|
`prompt-${_checkName || "unknown"}-${timestamp}.json`
|
|
9935
9935
|
);
|
|
9936
|
-
|
|
9937
|
-
const readableFile =
|
|
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
|
-
|
|
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
|
|
9980
|
-
const
|
|
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 ||
|
|
9992
|
-
const sessionBase =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
10041
|
-
const
|
|
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 ||
|
|
10044
|
-
const responseFile =
|
|
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
|
-
|
|
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
|
|
10094
|
-
if (
|
|
10095
|
-
const stats =
|
|
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
|
|
10357
|
-
const
|
|
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 =
|
|
10432
|
-
|
|
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 ||
|
|
10435
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path31.join(process.cwd(), "debug-artifacts");
|
|
10436
10436
|
try {
|
|
10437
|
-
const base =
|
|
10437
|
+
const base = path31.join(
|
|
10438
10438
|
debugArtifactsDir,
|
|
10439
10439
|
`prompt-${_checkName || "unknown"}-${timestamp}`
|
|
10440
10440
|
);
|
|
10441
|
-
|
|
10442
|
-
|
|
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
|
|
10493
|
-
const
|
|
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 ||
|
|
10505
|
-
const sessionBase =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
10554
|
-
const
|
|
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 ||
|
|
10557
|
-
const responseFile =
|
|
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
|
-
|
|
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
|
|
10609
|
-
if (
|
|
10610
|
-
const stats =
|
|
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
|
|
10649
|
-
const
|
|
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")) && !
|
|
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 =
|
|
10667
|
+
const schemaPath = path31.resolve(process.cwd(), schema);
|
|
10668
10668
|
log(`\u{1F4CB} Loading custom schema from file: ${schemaPath}`);
|
|
10669
|
-
const schemaContent = await
|
|
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
|
-
|
|
10683
|
+
path31.join(__dirname, "output", sanitizedSchemaName, "schema.json"),
|
|
10684
10684
|
// Historical fallback when src/output was inadvertently bundled as output1/
|
|
10685
|
-
|
|
10685
|
+
path31.join(__dirname, "output1", sanitizedSchemaName, "schema.json"),
|
|
10686
10686
|
// Local dev (repo root)
|
|
10687
|
-
|
|
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
|
|
10691
|
+
const schemaContent = await fs29.readFile(schemaPath, "utf-8");
|
|
10692
10692
|
return schemaContent.trim();
|
|
10693
10693
|
} catch {
|
|
10694
10694
|
}
|
|
10695
10695
|
}
|
|
10696
|
-
const distPath =
|
|
10697
|
-
const distAltPath =
|
|
10698
|
-
const cwdPath =
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
20470
|
-
const
|
|
20536
|
+
const path31 = require("path");
|
|
20537
|
+
const fs29 = require("fs");
|
|
20471
20538
|
const yaml6 = require("js-yaml");
|
|
20472
|
-
const resolved =
|
|
20473
|
-
if (!
|
|
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 =
|
|
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 =
|
|
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 =
|
|
20510
|
-
const name = loaded.name || `Workflow from ${
|
|
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:
|
|
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:
|
|
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:
|
|
23591
|
+
message_id: import_crypto6.default.randomUUID(),
|
|
21981
23592
|
role: "agent",
|
|
21982
23593
|
parts: [{ text: responseText }]
|
|
21983
23594
|
};
|
|
21984
|
-
|
|
21985
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
24157
|
-
|
|
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 =
|
|
24402
|
-
const cwd =
|
|
24403
|
-
const normalizedResolved =
|
|
24404
|
-
const cwdPrefix = cwd.endsWith(
|
|
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 (
|
|
24411
|
-
const realPath =
|
|
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 (!
|
|
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 =
|
|
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 =
|
|
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 ||
|
|
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
|
-
|
|
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.
|
|
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
|
|
27603
|
+
const fs29 = require("fs").promises;
|
|
25930
27604
|
try {
|
|
25931
|
-
const stat2 = await
|
|
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
|
|
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
|
-
|
|
28347
|
-
|
|
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 && !
|
|
28475
|
-
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 &&
|
|
28481
|
-
const stats =
|
|
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 =
|
|
28712
|
-
if (parentDir && !
|
|
28713
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
32208
|
-
|
|
33881
|
+
if (path31 && !path31.startsWith("/")) {
|
|
33882
|
+
path31 = `/${path31}`;
|
|
32209
33883
|
}
|
|
32210
|
-
url = new URL(origin +
|
|
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(
|
|
33825
|
-
if (typeof
|
|
35498
|
+
module2.exports = function basename5(path31) {
|
|
35499
|
+
if (typeof path31 !== "string") {
|
|
33826
35500
|
return "";
|
|
33827
35501
|
}
|
|
33828
|
-
for (var i =
|
|
33829
|
-
switch (
|
|
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
|
-
|
|
33834
|
-
return
|
|
35507
|
+
path31 = path31.slice(i + 1);
|
|
35508
|
+
return path31 === ".." || path31 === "." ? "" : path31;
|
|
33835
35509
|
}
|
|
33836
35510
|
}
|
|
33837
|
-
return
|
|
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
|
|
36515
|
+
var crypto8;
|
|
34842
36516
|
try {
|
|
34843
|
-
|
|
36517
|
+
crypto8 = require("crypto");
|
|
34844
36518
|
const possibleRelevantHashes = ["sha256", "sha384", "sha512"];
|
|
34845
|
-
supportedHashes =
|
|
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 (
|
|
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 =
|
|
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
|
|
36485
|
-
random = (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:
|
|
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
|
|
38556
|
+
if (typeof path31 !== "string") {
|
|
36883
38557
|
throw new InvalidArgumentError("path must be a string");
|
|
36884
|
-
} else if (
|
|
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(
|
|
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(
|
|
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
|
|
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 =
|
|
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:
|
|
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} ${
|
|
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:
|
|
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] =
|
|
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(
|
|
41601
|
-
if (typeof
|
|
41602
|
-
return
|
|
43274
|
+
function safeUrl(path31) {
|
|
43275
|
+
if (typeof path31 !== "string") {
|
|
43276
|
+
return path31;
|
|
41603
43277
|
}
|
|
41604
|
-
const pathSegments =
|
|
43278
|
+
const pathSegments = path31.split("?");
|
|
41605
43279
|
if (pathSegments.length !== 2) {
|
|
41606
|
-
return
|
|
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:
|
|
41613
|
-
const pathMatch = matchValue(mockDispatch2.path,
|
|
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:
|
|
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:
|
|
43342
|
+
const { path: path31, method, body, headers, query } = opts;
|
|
41669
43343
|
return {
|
|
41670
|
-
path:
|
|
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:
|
|
43793
|
+
({ method, path: path31, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
|
|
42120
43794
|
Method: method,
|
|
42121
43795
|
Origin: origin,
|
|
42122
|
-
Path:
|
|
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(
|
|
46744
|
-
for (const char of
|
|
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
|
|
49215
|
+
var crypto8;
|
|
47542
49216
|
try {
|
|
47543
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
49345
|
+
var crypto8;
|
|
47672
49346
|
try {
|
|
47673
|
-
|
|
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 =
|
|
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
|
|
50098
|
+
let path31 = opts.path;
|
|
48425
50099
|
if (!opts.path.startsWith("/")) {
|
|
48426
|
-
|
|
50100
|
+
path31 = `/${path31}`;
|
|
48427
50101
|
}
|
|
48428
|
-
url = new URL(util.parseOrigin(url).origin +
|
|
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 =
|
|
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 =
|
|
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
|
|
51109
|
+
var readline3, activePrompt, waiters;
|
|
49436
51110
|
var init_interactive_prompt = __esm({
|
|
49437
51111
|
"src/utils/interactive-prompt.ts"() {
|
|
49438
51112
|
"use strict";
|
|
49439
|
-
|
|
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
|
|
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
|
-
|
|
49666
|
-
|
|
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 =
|
|
49841
|
-
const normalizedPath =
|
|
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 +
|
|
51517
|
+
if (!normalizedPath.startsWith(cwd + path23.sep) && normalizedPath !== cwd) {
|
|
49844
51518
|
return null;
|
|
49845
51519
|
}
|
|
49846
51520
|
try {
|
|
49847
|
-
await
|
|
49848
|
-
const stats = await
|
|
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
|
|
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
|
|
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
|
-
|
|
52683
|
+
fs22 = __toESM(require("fs"));
|
|
51010
52684
|
fsp = __toESM(require("fs/promises"));
|
|
51011
|
-
|
|
51012
|
-
|
|
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 ||
|
|
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 (!
|
|
51065
|
-
|
|
52738
|
+
if (!fs22.existsSync(reposDir)) {
|
|
52739
|
+
fs22.mkdirSync(reposDir, { recursive: true });
|
|
51066
52740
|
logger.debug(`Created repos directory: ${reposDir}`);
|
|
51067
52741
|
}
|
|
51068
|
-
if (!
|
|
51069
|
-
|
|
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
|
|
52748
|
+
return path24.join(this.config.base_path, "repos");
|
|
51075
52749
|
}
|
|
51076
52750
|
getWorktreesDir() {
|
|
51077
|
-
return
|
|
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 =
|
|
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 =
|
|
51103
|
-
if (
|
|
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 ||
|
|
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 (
|
|
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 (
|
|
53291
|
+
if (fs22.existsSync(worktree_path)) {
|
|
51618
53292
|
logger.debug(`Manually removing worktree directory`);
|
|
51619
|
-
|
|
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 (
|
|
51625
|
-
|
|
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
|
-
|
|
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 =
|
|
51653
|
-
const pathToRead =
|
|
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 =
|
|
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 (!
|
|
53344
|
+
if (!fs22.existsSync(worktreesDir)) {
|
|
51671
53345
|
return [];
|
|
51672
53346
|
}
|
|
51673
|
-
const entries =
|
|
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 =
|
|
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 =
|
|
51810
|
-
if (!
|
|
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
|
|
54445
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
56126
|
+
path31.join(__dirname, "..", "..", "output", sanitized, "schema.json"),
|
|
54453
56127
|
// When running from a checkout with output/ folder copied to CWD
|
|
54454
|
-
|
|
56128
|
+
path31.join(process.cwd(), "output", sanitized, "schema.json"),
|
|
54455
56129
|
// Fallback: cwd/dist/output/
|
|
54456
|
-
|
|
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
|
|
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
|
|
56915
|
-
const
|
|
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 =
|
|
56925
|
-
templateContent = await
|
|
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
|
-
|
|
58605
|
+
path31.join(__dirname, "output", sanitized, "template.liquid"),
|
|
56932
58606
|
// bundled: dist/output/
|
|
56933
|
-
|
|
58607
|
+
path31.join(__dirname, "..", "..", "output", sanitized, "template.liquid"),
|
|
56934
58608
|
// source (from state-machine/states)
|
|
56935
|
-
|
|
58609
|
+
path31.join(__dirname, "..", "..", "..", "output", sanitized, "template.liquid"),
|
|
56936
58610
|
// source (alternate)
|
|
56937
|
-
|
|
58611
|
+
path31.join(process.cwd(), "output", sanitized, "template.liquid"),
|
|
56938
58612
|
// fallback: cwd/output/
|
|
56939
|
-
|
|
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
|
|
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,
|
|
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
|
-
|
|
57565
|
-
|
|
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 =
|
|
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 =
|
|
57597
|
-
const resolvedWorkingDir =
|
|
59270
|
+
const gitignorePath = path25.resolve(this.workingDirectory, ".gitignore");
|
|
59271
|
+
const resolvedWorkingDir = path25.resolve(this.workingDirectory);
|
|
57598
59272
|
try {
|
|
57599
|
-
const relativePath =
|
|
57600
|
-
if (relativePath.startsWith("..") ||
|
|
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 (
|
|
57611
|
-
const rawContent =
|
|
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,
|
|
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
|
-
|
|
57649
|
-
|
|
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 =
|
|
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" &&
|
|
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" &&
|
|
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" &&
|
|
59637
|
+
if (status === "added" && fs24.existsSync(filePath)) {
|
|
57964
59638
|
try {
|
|
57965
|
-
const stats =
|
|
59639
|
+
const stats = fs24.statSync(filePath);
|
|
57966
59640
|
if (stats.isFile() && stats.size < 1024 * 1024) {
|
|
57967
59641
|
if (includeContext) {
|
|
57968
|
-
content =
|
|
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 :
|
|
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,
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
58217
|
-
const normalizedRoot =
|
|
59890
|
+
const normalizedOriginal = path27.resolve(this.originalPath);
|
|
59891
|
+
const normalizedRoot = path27.resolve(gitRoot);
|
|
58218
59892
|
if (normalizedOriginal !== normalizedRoot) {
|
|
58219
|
-
subdirOffset =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
58440
|
-
const gitFilePath =
|
|
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
|
|
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,
|
|
64234
|
+
constructor(method, path31) {
|
|
62561
64235
|
this.method = method;
|
|
62562
|
-
this.path =
|
|
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
|
|
71998
|
+
var import_crypto7, EmailClient;
|
|
70325
71999
|
var init_client3 = __esm({
|
|
70326
72000
|
"src/email/client.ts"() {
|
|
70327
72001
|
"use strict";
|
|
70328
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
72825
|
+
var import_crypto8, WhatsAppClient;
|
|
71152
72826
|
var init_client4 = __esm({
|
|
71153
72827
|
"src/whatsapp/client.ts"() {
|
|
71154
72828
|
"use strict";
|
|
71155
|
-
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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 =
|
|
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:
|
|
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:
|
|
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
|
|
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
|
-
|
|
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 ??
|
|
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
|
|
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
|
-
|
|
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 ??
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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,
|
|
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
|
-
|
|
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:
|
|
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 ??
|
|
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 ??
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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
|
-
|
|
73544
|
-
|
|
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 ||
|
|
73933
|
-
|
|
73934
|
-
const filePath =
|
|
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
|
|
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
|
|
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
|
|
74102
|
-
const raw = await
|
|
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
|
/**
|