@probelabs/visor 0.1.170-ee → 0.1.171

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/defaults/code-talk.yaml +39 -238
  2. package/defaults/intent-router.yaml +1 -0
  3. package/dist/agent-protocol/task-store.d.ts +5 -0
  4. package/dist/agent-protocol/task-store.d.ts.map +1 -1
  5. package/dist/agent-protocol/tasks-cli-handler.d.ts.map +1 -1
  6. package/dist/agent-protocol/track-execution.d.ts +34 -0
  7. package/dist/agent-protocol/track-execution.d.ts.map +1 -0
  8. package/dist/cli-main.d.ts.map +1 -1
  9. package/dist/cli.d.ts.map +1 -1
  10. package/dist/defaults/code-talk.yaml +39 -238
  11. package/dist/defaults/intent-router.yaml +1 -0
  12. package/dist/frontends/host.d.ts +2 -0
  13. package/dist/frontends/host.d.ts.map +1 -1
  14. package/dist/generated/config-schema.d.ts +10 -6
  15. package/dist/generated/config-schema.d.ts.map +1 -1
  16. package/dist/generated/config-schema.json +10 -6
  17. package/dist/index.js +717 -1933
  18. package/dist/output/traces/run-2026-03-08T07-55-35-120Z.ndjson +138 -0
  19. package/dist/output/traces/run-2026-03-08T07-56-13-035Z.ndjson +2266 -0
  20. package/dist/scheduler/scheduler.d.ts +4 -0
  21. package/dist/scheduler/scheduler.d.ts.map +1 -1
  22. package/dist/sdk/{a2a-frontend-YTXQGUDH.mjs → a2a-frontend-FGJ3UBHX.mjs} +20 -3
  23. package/dist/sdk/a2a-frontend-FGJ3UBHX.mjs.map +1 -0
  24. package/dist/sdk/a2a-frontend-GUEGI5SX.mjs +1622 -0
  25. package/dist/sdk/a2a-frontend-GUEGI5SX.mjs.map +1 -0
  26. package/dist/sdk/{check-provider-registry-T5FWS4SW.mjs → check-provider-registry-PVTV5G5R.mjs} +7 -7
  27. package/dist/sdk/{check-provider-registry-Q2OVYSBJ.mjs → check-provider-registry-YTI4PU5F.mjs} +7 -7
  28. package/dist/sdk/check-provider-registry-ZUU7KSKR.mjs +30 -0
  29. package/dist/sdk/{chunk-46P7AYHG.mjs → chunk-2VDUNKIP.mjs} +63 -24
  30. package/dist/sdk/chunk-2VDUNKIP.mjs.map +1 -0
  31. package/dist/sdk/{chunk-FTUGQP5L.mjs → chunk-5SBX4KLG.mjs} +2 -2
  32. package/dist/sdk/chunk-6FDBLSGV.mjs +739 -0
  33. package/dist/sdk/chunk-6FDBLSGV.mjs.map +1 -0
  34. package/dist/sdk/{chunk-2CNT2EB3.mjs → chunk-6FXVWL6M.mjs} +3 -3
  35. package/dist/sdk/{chunk-KFKHU6CM.mjs → chunk-6VVXKXTI.mjs} +19 -2
  36. package/dist/sdk/chunk-6VVXKXTI.mjs.map +1 -0
  37. package/dist/sdk/{chunk-SVBF7Y2R.mjs → chunk-A2YVTICA.mjs} +11 -7
  38. package/dist/sdk/chunk-A2YVTICA.mjs.map +1 -0
  39. package/dist/sdk/chunk-AJK3FAA2.mjs +1502 -0
  40. package/dist/sdk/chunk-AJK3FAA2.mjs.map +1 -0
  41. package/dist/sdk/{chunk-DLO46M5M.mjs → chunk-CXA3WUOB.mjs} +62 -23
  42. package/dist/sdk/chunk-CXA3WUOB.mjs.map +1 -0
  43. package/dist/sdk/{chunk-62PXPI6Q.mjs → chunk-GGNR347O.mjs} +8 -2
  44. package/dist/sdk/chunk-GGNR347O.mjs.map +1 -0
  45. package/dist/sdk/chunk-O72J3ORS.mjs +449 -0
  46. package/dist/sdk/chunk-O72J3ORS.mjs.map +1 -0
  47. package/dist/sdk/chunk-XUQSI5SR.mjs +44810 -0
  48. package/dist/sdk/chunk-XUQSI5SR.mjs.map +1 -0
  49. package/dist/sdk/{config-IHECYTNT.mjs → config-6GWD673K.mjs} +2 -2
  50. package/dist/sdk/{failure-condition-evaluator-NJO6DSL4.mjs → failure-condition-evaluator-5HRNHZCC.mjs} +4 -3
  51. package/dist/sdk/failure-condition-evaluator-EFMCQVAK.mjs +18 -0
  52. package/dist/sdk/{github-frontend-WPTKI4AY.mjs → github-frontend-XG55VJ4R.mjs} +7 -7
  53. package/dist/sdk/github-frontend-ZZRU6P43.mjs +1386 -0
  54. package/dist/sdk/github-frontend-ZZRU6P43.mjs.map +1 -0
  55. package/dist/sdk/{host-6HV5FMD7.mjs → host-A4GGQVEN.mjs} +3 -3
  56. package/dist/sdk/host-A4GGQVEN.mjs.map +1 -0
  57. package/dist/sdk/{host-RATJKJW5.mjs → host-A7UNRBQU.mjs} +3 -3
  58. package/dist/sdk/host-A7UNRBQU.mjs.map +1 -0
  59. package/dist/sdk/{routing-RWZEXSRZ.mjs → routing-AMRQYI7J.mjs} +5 -4
  60. package/dist/sdk/routing-BVEHVZHK.mjs +26 -0
  61. package/dist/sdk/{schedule-tool-VI5IUMEL.mjs → schedule-tool-4MTFIHCA.mjs} +7 -7
  62. package/dist/sdk/{schedule-tool-JCKV47FU.mjs → schedule-tool-DGVJDHJM.mjs} +7 -7
  63. package/dist/sdk/schedule-tool-PHSF5U2B.mjs +36 -0
  64. package/dist/sdk/{schedule-tool-handler-PIXYVVJY.mjs → schedule-tool-handler-EFNCZNS7.mjs} +7 -7
  65. package/dist/sdk/{schedule-tool-handler-ZDAD6SWM.mjs → schedule-tool-handler-LMXQ4BZQ.mjs} +7 -7
  66. package/dist/sdk/schedule-tool-handler-LMXQ4BZQ.mjs.map +1 -0
  67. package/dist/sdk/schedule-tool-handler-XLCSBU3E.mjs +40 -0
  68. package/dist/sdk/schedule-tool-handler-XLCSBU3E.mjs.map +1 -0
  69. package/dist/sdk/sdk.d.mts +4 -0
  70. package/dist/sdk/sdk.d.ts +4 -0
  71. package/dist/sdk/sdk.js +552 -1782
  72. package/dist/sdk/sdk.js.map +1 -1
  73. package/dist/sdk/sdk.mjs +6 -6
  74. package/dist/sdk/{trace-helpers-ZYN23GBG.mjs → trace-helpers-4ZBZWH5W.mjs} +3 -2
  75. package/dist/sdk/trace-helpers-4ZBZWH5W.mjs.map +1 -0
  76. package/dist/sdk/trace-helpers-P5L4COO4.mjs +26 -0
  77. package/dist/sdk/trace-helpers-P5L4COO4.mjs.map +1 -0
  78. package/dist/sdk/track-execution-VWLQIGY7.mjs +82 -0
  79. package/dist/sdk/track-execution-VWLQIGY7.mjs.map +1 -0
  80. package/dist/sdk/{workflow-check-provider-OAOD3A5U.mjs → workflow-check-provider-KQNLEQEY.mjs} +7 -7
  81. package/dist/sdk/workflow-check-provider-KQNLEQEY.mjs.map +1 -0
  82. package/dist/sdk/{workflow-check-provider-W5FKQU5G.mjs → workflow-check-provider-WLMTCFRA.mjs} +7 -7
  83. package/dist/sdk/workflow-check-provider-WLMTCFRA.mjs.map +1 -0
  84. package/dist/sdk/workflow-check-provider-X5EMAJUZ.mjs +30 -0
  85. package/dist/sdk/workflow-check-provider-X5EMAJUZ.mjs.map +1 -0
  86. package/dist/slack/socket-runner.d.ts +4 -0
  87. package/dist/slack/socket-runner.d.ts.map +1 -1
  88. package/dist/telemetry/trace-helpers.d.ts.map +1 -1
  89. package/dist/traces/run-2026-03-08T07-55-35-120Z.ndjson +138 -0
  90. package/dist/traces/run-2026-03-08T07-56-13-035Z.ndjson +2266 -0
  91. package/dist/tui/chat-runner.d.ts +4 -0
  92. package/dist/tui/chat-runner.d.ts.map +1 -1
  93. package/dist/types/cli.d.ts +2 -0
  94. package/dist/types/cli.d.ts.map +1 -1
  95. package/dist/types/config.d.ts +4 -0
  96. package/dist/types/config.d.ts.map +1 -1
  97. package/dist/utils/instance-id.d.ts +9 -0
  98. package/dist/utils/instance-id.d.ts.map +1 -0
  99. package/package.json +2 -2
  100. package/dist/sdk/a2a-frontend-YTXQGUDH.mjs.map +0 -1
  101. package/dist/sdk/chunk-46P7AYHG.mjs.map +0 -1
  102. package/dist/sdk/chunk-62PXPI6Q.mjs.map +0 -1
  103. package/dist/sdk/chunk-DLO46M5M.mjs.map +0 -1
  104. package/dist/sdk/chunk-KFKHU6CM.mjs.map +0 -1
  105. package/dist/sdk/chunk-SVBF7Y2R.mjs.map +0 -1
  106. package/dist/sdk/host-6HV5FMD7.mjs.map +0 -1
  107. package/dist/sdk/host-RATJKJW5.mjs.map +0 -1
  108. package/dist/sdk/knex-store-CRORFJE6.mjs +0 -527
  109. package/dist/sdk/knex-store-CRORFJE6.mjs.map +0 -1
  110. package/dist/sdk/loader-NJCF7DUS.mjs +0 -89
  111. package/dist/sdk/loader-NJCF7DUS.mjs.map +0 -1
  112. package/dist/sdk/opa-policy-engine-S2S2ULEI.mjs +0 -655
  113. package/dist/sdk/opa-policy-engine-S2S2ULEI.mjs.map +0 -1
  114. package/dist/sdk/validator-XTZJZZJH.mjs +0 -134
  115. package/dist/sdk/validator-XTZJZZJH.mjs.map +0 -1
  116. /package/dist/sdk/{check-provider-registry-Q2OVYSBJ.mjs.map → check-provider-registry-PVTV5G5R.mjs.map} +0 -0
  117. /package/dist/sdk/{check-provider-registry-T5FWS4SW.mjs.map → check-provider-registry-YTI4PU5F.mjs.map} +0 -0
  118. /package/dist/sdk/{config-IHECYTNT.mjs.map → check-provider-registry-ZUU7KSKR.mjs.map} +0 -0
  119. /package/dist/sdk/{chunk-FTUGQP5L.mjs.map → chunk-5SBX4KLG.mjs.map} +0 -0
  120. /package/dist/sdk/{chunk-2CNT2EB3.mjs.map → chunk-6FXVWL6M.mjs.map} +0 -0
  121. /package/dist/sdk/{failure-condition-evaluator-NJO6DSL4.mjs.map → config-6GWD673K.mjs.map} +0 -0
  122. /package/dist/sdk/{routing-RWZEXSRZ.mjs.map → failure-condition-evaluator-5HRNHZCC.mjs.map} +0 -0
  123. /package/dist/sdk/{schedule-tool-JCKV47FU.mjs.map → failure-condition-evaluator-EFMCQVAK.mjs.map} +0 -0
  124. /package/dist/sdk/{github-frontend-WPTKI4AY.mjs.map → github-frontend-XG55VJ4R.mjs.map} +0 -0
  125. /package/dist/sdk/{schedule-tool-VI5IUMEL.mjs.map → routing-AMRQYI7J.mjs.map} +0 -0
  126. /package/dist/sdk/{schedule-tool-handler-PIXYVVJY.mjs.map → routing-BVEHVZHK.mjs.map} +0 -0
  127. /package/dist/sdk/{schedule-tool-handler-ZDAD6SWM.mjs.map → schedule-tool-4MTFIHCA.mjs.map} +0 -0
  128. /package/dist/sdk/{trace-helpers-ZYN23GBG.mjs.map → schedule-tool-DGVJDHJM.mjs.map} +0 -0
  129. /package/dist/sdk/{workflow-check-provider-OAOD3A5U.mjs.map → schedule-tool-PHSF5U2B.mjs.map} +0 -0
  130. /package/dist/sdk/{workflow-check-provider-W5FKQU5G.mjs.map → schedule-tool-handler-EFNCZNS7.mjs.map} +0 -0
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- process.env.VISOR_VERSION = '0.1.170';
3
- process.env.PROBE_VERSION = '0.6.0-rc286';
4
- process.env.VISOR_COMMIT_SHA = '3e49bfdee61154013b28eba096637ac60cdd08ac';
5
- process.env.VISOR_COMMIT_SHORT = '3e49bfd';
2
+ process.env.VISOR_VERSION = '0.1.171';
3
+ process.env.PROBE_VERSION = '0.6.0-rc290';
4
+ process.env.VISOR_COMMIT_SHA = '12d9273ce614376592cf397b6c874dcbfa220b8b';
5
+ process.env.VISOR_COMMIT_SHORT = '12d9273ce';
6
6
  /******/ (() => { // webpackBootstrap
7
7
  /******/ var __webpack_modules__ = ({
8
8
 
@@ -168110,6 +168110,13 @@ class SqliteTaskStore {
168110
168110
  messageText = textPart?.text ?? '';
168111
168111
  }
168112
168112
  catch { }
168113
+ let source = '-';
168114
+ let metadata = {};
168115
+ try {
168116
+ metadata = JSON.parse(r.request_metadata || '{}');
168117
+ source = metadata.source || '-';
168118
+ }
168119
+ catch { }
168113
168120
  return {
168114
168121
  id: r.id,
168115
168122
  context_id: r.context_id,
@@ -168121,6 +168128,8 @@ class SqliteTaskStore {
168121
168128
  workflow_id: r.workflow_id,
168122
168129
  run_id: r.run_id,
168123
168130
  request_message: messageText,
168131
+ source,
168132
+ metadata,
168124
168133
  };
168125
168134
  });
168126
168135
  return { rows, total };
@@ -168139,6 +168148,11 @@ class SqliteTaskStore {
168139
168148
  SET state = ?, updated_at = ?, status_message = ?
168140
168149
  WHERE id = ?`).run(newState, now, statusMessage ? JSON.stringify(statusMessage) : null, taskId);
168141
168150
  }
168151
+ claimTask(taskId, workerId) {
168152
+ const db = this.getDb();
168153
+ const now = nowISO();
168154
+ db.prepare(`UPDATE agent_tasks SET claimed_by = ?, claimed_at = ?, updated_at = ? WHERE id = ?`).run(workerId, now, now, taskId);
168155
+ }
168142
168156
  addArtifact(taskId, artifact) {
168143
168157
  const db = this.getDb();
168144
168158
  const row = db.prepare('SELECT artifacts FROM agent_tasks WHERE id = ?').get(taskId);
@@ -168396,6 +168410,7 @@ exports.handleTasksCommand = handleTasksCommand;
168396
168410
  * visor tasks help - Show usage
168397
168411
  */
168398
168412
  const logger_1 = __nccwpck_require__(86999);
168413
+ const instance_id_1 = __nccwpck_require__(89942);
168399
168414
  const task_store_1 = __nccwpck_require__(24711);
168400
168415
  const state_transitions_1 = __nccwpck_require__(68612);
168401
168416
  // ---------------------------------------------------------------------------
@@ -168477,22 +168492,34 @@ function buildFilter(flags) {
168477
168492
  // ---------------------------------------------------------------------------
168478
168493
  // Table formatting
168479
168494
  // ---------------------------------------------------------------------------
168495
+ function formatMeta(meta) {
168496
+ const parts = [];
168497
+ if (meta.slack_user)
168498
+ parts.push(`user:${meta.slack_user}`);
168499
+ if (meta.slack_channel)
168500
+ parts.push(`ch:${meta.slack_channel}`);
168501
+ if (meta.trace_id)
168502
+ parts.push(`trace:${String(meta.trace_id).slice(0, 8)}`);
168503
+ if (meta.schedule_id)
168504
+ parts.push(`sched:${meta.schedule_id}`);
168505
+ return parts.join(' ') || '-';
168506
+ }
168480
168507
  function formatTable(rows, total) {
168481
168508
  if (rows.length === 0)
168482
168509
  return 'No tasks found.';
168483
- const header = ['ID', 'State', 'Agent', 'Duration', 'Worker', 'Input'];
168510
+ const header = ['ID', 'Source', 'State', 'Workflow', 'Duration', 'Instance', 'Meta', 'Input'];
168484
168511
  const data = rows.map(r => {
168485
- const duration = r.state === 'working' && r.claimed_at
168486
- ? formatDuration(r.claimed_at)
168487
- : r.state === 'submitted'
168488
- ? formatDuration(r.created_at)
168489
- : formatDuration(r.created_at, r.updated_at);
168490
- const worker = r.claimed_by ? r.claimed_by.slice(0, 8) : '-';
168491
- const agent = r.workflow_id || '-';
168512
+ const duration = (0, state_transitions_1.isTerminalState)(r.state)
168513
+ ? formatDuration(r.created_at, r.updated_at)
168514
+ : formatDuration(r.claimed_at || r.created_at);
168515
+ const instance = r.claimed_by || '-';
168516
+ const workflow = r.workflow_id || '-';
168517
+ const source = r.source || '-';
168518
+ const meta = formatMeta(r.metadata);
168492
168519
  const input = r.request_message.length > 60
168493
168520
  ? r.request_message.slice(0, 57) + '...'
168494
168521
  : r.request_message || '-';
168495
- return [r.id.slice(0, 8), r.state, agent, duration, worker, input];
168522
+ return [r.id.slice(0, 8), source, r.state, workflow, duration, instance, meta, input];
168496
168523
  });
168497
168524
  const widths = header.map((h, i) => Math.max(h.length, ...data.map(row => row[i].length)));
168498
168525
  const sep = widths.map(w => '-'.repeat(w)).join(' ');
@@ -168505,19 +168532,19 @@ function formatTable(rows, total) {
168505
168532
  function formatMarkdown(rows, total) {
168506
168533
  if (rows.length === 0)
168507
168534
  return 'No tasks found.';
168508
- const header = ['ID', 'State', 'Agent', 'Duration', 'Worker', 'Input'];
168535
+ const header = ['ID', 'Source', 'State', 'Workflow', 'Duration', 'Instance', 'Meta', 'Input'];
168509
168536
  const data = rows.map(r => {
168510
- const duration = r.state === 'working' && r.claimed_at
168511
- ? formatDuration(r.claimed_at)
168512
- : r.state === 'submitted'
168513
- ? formatDuration(r.created_at)
168514
- : formatDuration(r.created_at, r.updated_at);
168515
- const worker = r.claimed_by ? r.claimed_by.slice(0, 8) : '-';
168516
- const agent = r.workflow_id || '-';
168537
+ const duration = (0, state_transitions_1.isTerminalState)(r.state)
168538
+ ? formatDuration(r.created_at, r.updated_at)
168539
+ : formatDuration(r.claimed_at || r.created_at);
168540
+ const instance = r.claimed_by || '-';
168541
+ const workflow = r.workflow_id || '-';
168542
+ const source = r.source || '-';
168543
+ const meta = formatMeta(r.metadata);
168517
168544
  const input = r.request_message.length > 60
168518
168545
  ? r.request_message.slice(0, 57) + '...'
168519
168546
  : r.request_message || '-';
168520
- return [r.id.slice(0, 8), r.state, agent, duration, worker, input];
168547
+ return [r.id.slice(0, 8), source, r.state, workflow, duration, instance, meta, input];
168521
168548
  });
168522
168549
  const lines = [
168523
168550
  '| ' + header.join(' | ') + ' |',
@@ -168534,16 +168561,19 @@ function formatMarkdown(rows, total) {
168534
168561
  async function handleList(flags) {
168535
168562
  const filter = buildFilter(flags);
168536
168563
  const output = typeof flags.output === 'string' ? flags.output : 'table';
168564
+ const instanceId = (0, instance_id_1.getInstanceId)();
168537
168565
  const render = async () => {
168538
168566
  await withTaskStore(async (store) => {
168539
168567
  const { rows, total } = store.listTasksRaw(filter);
168540
168568
  if (output === 'json') {
168541
- console.log(JSON.stringify({ tasks: rows, total }, null, 2));
168569
+ console.log(JSON.stringify({ instance_id: instanceId, tasks: rows, total }, null, 2));
168542
168570
  }
168543
168571
  else if (output === 'markdown') {
168572
+ console.log(`Instance: ${instanceId}\n`);
168544
168573
  console.log(formatMarkdown(rows, total));
168545
168574
  }
168546
168575
  else {
168576
+ console.log(`Instance: ${instanceId}\n`);
168547
168577
  console.log(formatTable(rows, total));
168548
168578
  }
168549
168579
  });
@@ -168551,7 +168581,7 @@ async function handleList(flags) {
168551
168581
  if (flags.watch) {
168552
168582
  const watchRender = async () => {
168553
168583
  process.stdout.write('\x1Bc'); // clear terminal
168554
- console.log(`visor tasks list (watching, Ctrl+C to exit)\n`);
168584
+ console.log(`visor tasks list (instance: ${instanceId}, watching, Ctrl+C to exit)\n`);
168555
168585
  try {
168556
168586
  await render();
168557
168587
  }
@@ -168657,17 +168687,67 @@ async function handleCancel(positional, _flags) {
168657
168687
  });
168658
168688
  }
168659
168689
  // ---------------------------------------------------------------------------
168690
+ // Subcommand: show
168691
+ // ---------------------------------------------------------------------------
168692
+ async function handleShow(positional, flags) {
168693
+ const taskId = positional[0];
168694
+ if (!taskId) {
168695
+ console.error('Usage: visor tasks show <task-id>');
168696
+ process.exitCode = 1;
168697
+ return;
168698
+ }
168699
+ const output = typeof flags.output === 'string' ? flags.output : 'table';
168700
+ await withTaskStore(async (store) => {
168701
+ // Try to find by prefix match
168702
+ const { rows } = store.listTasksRaw({ limit: 200 });
168703
+ const match = rows.find(r => r.id.startsWith(taskId));
168704
+ if (!match) {
168705
+ console.error(`Task not found: ${taskId}`);
168706
+ process.exitCode = 1;
168707
+ return;
168708
+ }
168709
+ if (output === 'json') {
168710
+ console.log(JSON.stringify(match, null, 2));
168711
+ return;
168712
+ }
168713
+ const duration = (0, state_transitions_1.isTerminalState)(match.state)
168714
+ ? formatDuration(match.created_at, match.updated_at)
168715
+ : formatDuration(match.claimed_at || match.created_at);
168716
+ console.log(`Task: ${match.id}`);
168717
+ console.log(`State: ${match.state}`);
168718
+ console.log(`Source: ${match.source}`);
168719
+ console.log(`Workflow: ${match.workflow_id || '-'}`);
168720
+ console.log(`Instance: ${match.claimed_by || '-'}`);
168721
+ console.log(`Duration: ${duration}`);
168722
+ console.log(`Created: ${match.created_at}`);
168723
+ console.log(`Updated: ${match.updated_at}`);
168724
+ if (match.run_id)
168725
+ console.log(`Run ID: ${match.run_id}`);
168726
+ console.log(`Input: ${match.request_message}`);
168727
+ // Show metadata
168728
+ const meta = match.metadata;
168729
+ const metaKeys = Object.keys(meta).filter(k => k !== 'source');
168730
+ if (metaKeys.length > 0) {
168731
+ console.log(`\nMetadata:`);
168732
+ for (const key of metaKeys) {
168733
+ console.log(` ${key}: ${JSON.stringify(meta[key])}`);
168734
+ }
168735
+ }
168736
+ });
168737
+ }
168738
+ // ---------------------------------------------------------------------------
168660
168739
  // Help
168661
168740
  // ---------------------------------------------------------------------------
168662
168741
  function printHelp() {
168663
168742
  console.log(`
168664
- Visor Tasks - Monitor and manage A2A agent tasks
168743
+ Visor Tasks - Monitor and manage agent tasks
168665
168744
 
168666
168745
  USAGE:
168667
168746
  visor tasks [command] [options]
168668
168747
 
168669
168748
  COMMANDS:
168670
168749
  list List tasks (default)
168750
+ show <task-id> Show task details (supports prefix match)
168671
168751
  stats Queue summary statistics
168672
168752
  cancel <task-id> Cancel a task
168673
168753
  help Show this help
@@ -168681,6 +168761,7 @@ OPTIONS:
168681
168761
 
168682
168762
  EXAMPLES:
168683
168763
  visor tasks List all tasks
168764
+ visor tasks show abc123 Show full task details
168684
168765
  visor tasks list --state working Show only working tasks
168685
168766
  visor tasks list --agent security-review Show tasks for a specific agent
168686
168767
  visor tasks list --output json JSON output
@@ -168704,6 +168785,9 @@ async function handleTasksCommand(argv) {
168704
168785
  case 'list':
168705
168786
  await handleList(flags);
168706
168787
  break;
168788
+ case 'show':
168789
+ await handleShow(positional, flags);
168790
+ break;
168707
168791
  case 'stats':
168708
168792
  await handleStats(flags);
168709
168793
  break;
@@ -168730,6 +168814,93 @@ async function handleTasksCommand(argv) {
168730
168814
  }
168731
168815
 
168732
168816
 
168817
+ /***/ }),
168818
+
168819
+ /***/ 7628:
168820
+ /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
168821
+
168822
+ "use strict";
168823
+
168824
+ /**
168825
+ * Shared execution tracking helper.
168826
+ *
168827
+ * Wraps any engine execution call with task lifecycle management:
168828
+ * creates a task in 'submitted' state, transitions to 'working',
168829
+ * runs the executor, then sets terminal state (completed/failed).
168830
+ *
168831
+ * Used by CLI, Slack, TUI, and Scheduler frontends when task_tracking is enabled.
168832
+ */
168833
+ var __importDefault = (this && this.__importDefault) || function (mod) {
168834
+ return (mod && mod.__esModule) ? mod : { "default": mod };
168835
+ };
168836
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
168837
+ exports.trackExecution = trackExecution;
168838
+ const crypto_1 = __importDefault(__nccwpck_require__(76982));
168839
+ const path_1 = __importDefault(__nccwpck_require__(16928));
168840
+ const logger_1 = __nccwpck_require__(86999);
168841
+ const lazy_otel_1 = __nccwpck_require__(21084);
168842
+ const instance_id_1 = __nccwpck_require__(89942);
168843
+ /**
168844
+ * Wrap an engine execution call with task lifecycle tracking.
168845
+ *
168846
+ * Creates a task, transitions to 'working', runs the executor,
168847
+ * then sets terminal state. Returns both the task and the original result.
168848
+ * Re-throws any executor error after marking the task as failed.
168849
+ */
168850
+ async function trackExecution(opts, executor) {
168851
+ const { taskStore, source, configPath, metadata, messageText } = opts;
168852
+ const configName = configPath ? path_1.default.basename(configPath, path_1.default.extname(configPath)) : undefined;
168853
+ const workflowId = configName && opts.workflowId ? `${configName}#${opts.workflowId}` : opts.workflowId;
168854
+ const requestMessage = {
168855
+ message_id: crypto_1.default.randomUUID(),
168856
+ role: 'user',
168857
+ parts: [{ text: messageText }],
168858
+ };
168859
+ const task = taskStore.createTask({
168860
+ contextId: crypto_1.default.randomUUID(),
168861
+ requestMessage,
168862
+ workflowId,
168863
+ requestMetadata: {
168864
+ source,
168865
+ instance_id: (0, instance_id_1.getInstanceId)(),
168866
+ trace_id: lazy_otel_1.trace.getActiveSpan()?.spanContext().traceId || undefined,
168867
+ ...metadata,
168868
+ },
168869
+ });
168870
+ const instanceId = (0, instance_id_1.getInstanceId)();
168871
+ taskStore.updateTaskState(task.id, 'working');
168872
+ taskStore.claimTask(task.id, instanceId);
168873
+ logger_1.logger.info(`[TaskTracking] Task ${task.id} started (source=${source}, workflow=${workflowId || '-'}, instance=${instanceId})`);
168874
+ try {
168875
+ const result = await executor();
168876
+ const completedMsg = {
168877
+ message_id: crypto_1.default.randomUUID(),
168878
+ role: 'agent',
168879
+ parts: [{ text: 'Execution completed' }],
168880
+ };
168881
+ taskStore.updateTaskState(task.id, 'completed', completedMsg);
168882
+ logger_1.logger.info(`[TaskTracking] Task ${task.id} completed`);
168883
+ return { task, result };
168884
+ }
168885
+ catch (err) {
168886
+ const errorText = err instanceof Error ? err.message : String(err);
168887
+ const failMessage = {
168888
+ message_id: crypto_1.default.randomUUID(),
168889
+ role: 'agent',
168890
+ parts: [{ text: errorText }],
168891
+ };
168892
+ try {
168893
+ taskStore.updateTaskState(task.id, 'failed', failMessage);
168894
+ logger_1.logger.info(`[TaskTracking] Task ${task.id} failed: ${errorText}`);
168895
+ }
168896
+ catch {
168897
+ // ignore double-failure
168898
+ }
168899
+ throw err; // re-throw to preserve original behavior
168900
+ }
168901
+ }
168902
+
168903
+
168733
168904
  /***/ }),
168734
168905
 
168735
168906
  /***/ 43137:
@@ -171591,7 +171762,7 @@ async function handleDumpPolicyInput(checkId, argv) {
171591
171762
  let PolicyInputBuilder;
171592
171763
  try {
171593
171764
  // @ts-ignore — enterprise/ may not exist in OSS builds (caught at runtime)
171594
- const mod = await Promise.resolve().then(() => __importStar(__nccwpck_require__(17117)));
171765
+ const mod = await Promise.resolve().then(() => __importStar(__nccwpck_require__(71370)));
171595
171766
  PolicyInputBuilder = mod.PolicyInputBuilder;
171596
171767
  }
171597
171768
  catch {
@@ -172627,6 +172798,20 @@ async function main() {
172627
172798
  catch (err) {
172628
172799
  logger_1.logger.debug(`Startup snapshot failed: ${err}`);
172629
172800
  }
172801
+ // ---- Shared Task Store (cross-frontend tracking) ----
172802
+ let sharedTaskStore = null;
172803
+ const taskTrackingEnabled = options.taskTracking || config.task_tracking === true;
172804
+ if (taskTrackingEnabled) {
172805
+ try {
172806
+ const { SqliteTaskStore } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(24711)));
172807
+ sharedTaskStore = new SqliteTaskStore();
172808
+ await sharedTaskStore.initialize();
172809
+ logger_1.logger.info('[TaskTracking] Shared task store initialized');
172810
+ }
172811
+ catch (err) {
172812
+ logger_1.logger.warn(`[TaskTracking] Failed to initialize task store: ${err instanceof Error ? err.message : err}`);
172813
+ }
172814
+ }
172630
172815
  // ---- A2A Agent Protocol Server ----
172631
172816
  let a2aFrontendInstance = null;
172632
172817
  let sharedEngine = null;
@@ -172640,7 +172825,7 @@ async function main() {
172640
172825
  process.exit(1);
172641
172826
  }
172642
172827
  const engine = new SMEngine();
172643
- const frontend = new A2AFrontend(agentConfig);
172828
+ const frontend = new A2AFrontend(agentConfig, sharedTaskStore ?? undefined);
172644
172829
  frontend.setEngine(engine);
172645
172830
  frontend.setVisorConfig(config);
172646
172831
  const eventBus = new EventBus();
@@ -172717,6 +172902,8 @@ async function main() {
172717
172902
  threads,
172718
172903
  channel_allowlist: allow,
172719
172904
  });
172905
+ if (sharedTaskStore)
172906
+ runner.setTaskStore(sharedTaskStore, options.configPath);
172720
172907
  await runner.start();
172721
172908
  console.log('✅ Slack Socket Mode is running. Press Ctrl+C to exit.');
172722
172909
  // Start config file watcher if --watch is enabled
@@ -172773,6 +172960,12 @@ async function main() {
172773
172960
  }
172774
172961
  }
172775
172962
  await runner.stop();
172963
+ if (sharedTaskStore) {
172964
+ try {
172965
+ await sharedTaskStore.shutdown();
172966
+ }
172967
+ catch { }
172968
+ }
172776
172969
  process.exit(0);
172777
172970
  };
172778
172971
  process.on('SIGINT', sig => {
@@ -173144,12 +173337,27 @@ async function main() {
173144
173337
  conversation: tuiConversation,
173145
173338
  });
173146
173339
  // Execute workflow
173147
- const rerunResult = await (0, trace_helpers_1.withActiveSpan)('visor.run', {
173340
+ const tuiExecFn = () => (0, trace_helpers_1.withActiveSpan)('visor.run', {
173148
173341
  ...(0, trace_helpers_1.getVisorRunAttributes)(),
173149
173342
  'visor.run.checks_configured': checksToRun.length,
173150
173343
  'visor.run.source': 'tui-rerun',
173151
173344
  }, async () => rerunEngine.executeGroupedChecks(prInfo, checksToRun, options.timeout, config, options.output, options.debug || false, options.maxParallelism, options.failFast, tagFilter, async () => { } // No pause gate for TUI re-runs
173152
173345
  ));
173346
+ let rerunResult;
173347
+ if (sharedTaskStore) {
173348
+ const { trackExecution } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(7628)));
173349
+ const tracked = await trackExecution({
173350
+ taskStore: sharedTaskStore,
173351
+ source: 'tui',
173352
+ workflowId: checksToRun.join(','),
173353
+ configPath: options.configPath,
173354
+ messageText: message,
173355
+ }, tuiExecFn);
173356
+ rerunResult = tracked.result;
173357
+ }
173358
+ else {
173359
+ rerunResult = await tuiExecFn();
173360
+ }
173153
173361
  // Show any errors and the last result in the chat
173154
173362
  if (rerunResult?.results) {
173155
173363
  const allRerunResults = Object.values(rerunResult.results).flat();
@@ -173251,7 +173459,21 @@ async function main() {
173251
173459
  process.exit(0);
173252
173460
  }
173253
173461
  else {
173254
- executionResult = await (0, trace_helpers_1.withActiveSpan)('visor.run', { ...(0, trace_helpers_1.getVisorRunAttributes)(), 'visor.run.checks_configured': checksToRun.length }, async () => engine.executeGroupedChecks(prInfo, checksToRun, options.timeout, config, options.output, options.debug || false, options.maxParallelism, options.failFast, tagFilter, pauseGate));
173462
+ const cliExecFn = () => (0, trace_helpers_1.withActiveSpan)('visor.run', { ...(0, trace_helpers_1.getVisorRunAttributes)(), 'visor.run.checks_configured': checksToRun.length }, async () => engine.executeGroupedChecks(prInfo, checksToRun, options.timeout, config, options.output, options.debug || false, options.maxParallelism, options.failFast, tagFilter, pauseGate));
173463
+ if (sharedTaskStore) {
173464
+ const { trackExecution } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(7628)));
173465
+ const tracked = await trackExecution({
173466
+ taskStore: sharedTaskStore,
173467
+ source: 'cli',
173468
+ workflowId: checksToRun.join(','),
173469
+ configPath: options.configPath,
173470
+ messageText: options.message || `CLI run: ${checksToRun.join(', ')}`,
173471
+ }, cliExecFn);
173472
+ executionResult = tracked.result;
173473
+ }
173474
+ else {
173475
+ executionResult = await cliExecFn();
173476
+ }
173255
173477
  }
173256
173478
  // Extract results and statistics from the execution result
173257
173479
  const { results: groupedResults, statistics: executionStatistics } = executionResult;
@@ -173443,6 +173665,12 @@ async function main() {
173443
173665
  return;
173444
173666
  }
173445
173667
  // Normal exit path (no debug server)
173668
+ if (sharedTaskStore) {
173669
+ try {
173670
+ await sharedTaskStore.shutdown();
173671
+ }
173672
+ catch { }
173673
+ }
173446
173674
  try {
173447
173675
  await (0, fallback_ndjson_1.flushNdjson)();
173448
173676
  }
@@ -173652,6 +173880,7 @@ class CLI {
173652
173880
  .option('--workspace-name <name>', 'Workspace directory name (overrides VISOR_WORKSPACE_NAME)')
173653
173881
  .option('--workspace-project-name <name>', 'Main project folder name inside workspace (overrides VISOR_WORKSPACE_PROJECT)')
173654
173882
  .option('--watch', 'Watch config file for changes and reload automatically (requires --config)')
173883
+ .option('--task-tracking', 'Enable cross-frontend task tracking (all executions recorded in visor tasks)')
173655
173884
  .option('--github-token <token>', 'GitHub token for API operations (env: GITHUB_TOKEN)')
173656
173885
  .option('--github-app-id <id>', 'GitHub App ID (env: GITHUB_APP_ID)')
173657
173886
  .option('--github-private-key <key>', 'GitHub App private key, PEM content or file path (env: GITHUB_APP_PRIVATE_KEY)')
@@ -173713,6 +173942,7 @@ class CLI {
173713
173942
  .option('--workspace-name <name>', 'Workspace directory name (overrides VISOR_WORKSPACE_NAME)')
173714
173943
  .option('--workspace-project-name <name>', 'Main project folder name inside workspace (overrides VISOR_WORKSPACE_PROJECT)')
173715
173944
  .option('--watch', 'Watch config file for changes and reload automatically (requires --config)')
173945
+ .option('--task-tracking', 'Enable cross-frontend task tracking (all executions recorded in visor tasks)')
173716
173946
  .option('--github-token <token>', 'GitHub token for API operations (env: GITHUB_TOKEN)')
173717
173947
  .option('--github-app-id <id>', 'GitHub App ID (env: GITHUB_APP_ID)')
173718
173948
  .option('--github-private-key <key>', 'GitHub App private key, PEM content or file path (env: GITHUB_APP_PRIVATE_KEY)')
@@ -173787,6 +174017,7 @@ class CLI {
173787
174017
  workspaceName: options.workspaceName,
173788
174018
  workspaceProjectName: options.workspaceProjectName,
173789
174019
  watch: Boolean(options.watch),
174020
+ taskTracking: Boolean(options.taskTracking),
173790
174021
  githubToken: options.githubToken,
173791
174022
  githubAppId: options.githubAppId,
173792
174023
  githubPrivateKey: options.githubPrivateKey,
@@ -177560,1810 +177791,6 @@ class DependencyResolver {
177560
177791
  exports.DependencyResolver = DependencyResolver;
177561
177792
 
177562
177793
 
177563
- /***/ }),
177564
-
177565
- /***/ 50069:
177566
- /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
177567
-
177568
- "use strict";
177569
-
177570
- /**
177571
- * Copyright (c) ProbeLabs. All rights reserved.
177572
- * Licensed under the Elastic License 2.0; you may not use this file except
177573
- * in compliance with the Elastic License 2.0.
177574
- */
177575
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
177576
- if (k2 === undefined) k2 = k;
177577
- var desc = Object.getOwnPropertyDescriptor(m, k);
177578
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
177579
- desc = { enumerable: true, get: function() { return m[k]; } };
177580
- }
177581
- Object.defineProperty(o, k2, desc);
177582
- }) : (function(o, m, k, k2) {
177583
- if (k2 === undefined) k2 = k;
177584
- o[k2] = m[k];
177585
- }));
177586
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
177587
- Object.defineProperty(o, "default", { enumerable: true, value: v });
177588
- }) : function(o, v) {
177589
- o["default"] = v;
177590
- });
177591
- var __importStar = (this && this.__importStar) || (function () {
177592
- var ownKeys = function(o) {
177593
- ownKeys = Object.getOwnPropertyNames || function (o) {
177594
- var ar = [];
177595
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
177596
- return ar;
177597
- };
177598
- return ownKeys(o);
177599
- };
177600
- return function (mod) {
177601
- if (mod && mod.__esModule) return mod;
177602
- var result = {};
177603
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
177604
- __setModuleDefault(result, mod);
177605
- return result;
177606
- };
177607
- })();
177608
- Object.defineProperty(exports, "__esModule", ({ value: true }));
177609
- exports.LicenseValidator = void 0;
177610
- const crypto = __importStar(__nccwpck_require__(76982));
177611
- const fs = __importStar(__nccwpck_require__(79896));
177612
- const path = __importStar(__nccwpck_require__(16928));
177613
- class LicenseValidator {
177614
- /** Ed25519 public key for license verification (PEM format). */
177615
- static PUBLIC_KEY = '-----BEGIN PUBLIC KEY-----\n' +
177616
- 'MCowBQYDK2VwAyEAI/Zd08EFmgIdrDm/HXd0l3/5GBt7R1PrdvhdmEXhJlU=\n' +
177617
- '-----END PUBLIC KEY-----\n';
177618
- cache = null;
177619
- static CACHE_TTL = 5 * 60 * 1000; // 5 minutes
177620
- static GRACE_PERIOD = 72 * 3600 * 1000; // 72 hours after expiry
177621
- /**
177622
- * Load and validate license from environment or file.
177623
- *
177624
- * Resolution order:
177625
- * 1. VISOR_LICENSE env var (JWT string)
177626
- * 2. VISOR_LICENSE_FILE env var (path to file)
177627
- * 3. .visor-license in project root (cwd)
177628
- * 4. .visor-license in ~/.config/visor/
177629
- */
177630
- async loadAndValidate() {
177631
- // Return cached result if still fresh
177632
- if (this.cache && Date.now() - this.cache.validatedAt < LicenseValidator.CACHE_TTL) {
177633
- return this.cache.payload;
177634
- }
177635
- const token = this.resolveToken();
177636
- if (!token)
177637
- return null;
177638
- const payload = this.verifyAndDecode(token);
177639
- if (!payload)
177640
- return null;
177641
- this.cache = { payload, validatedAt: Date.now() };
177642
- return payload;
177643
- }
177644
- /** Check if a specific feature is licensed */
177645
- hasFeature(feature) {
177646
- if (!this.cache)
177647
- return false;
177648
- return this.cache.payload.features.includes(feature);
177649
- }
177650
- /** Check if license is valid (with grace period) */
177651
- isValid() {
177652
- if (!this.cache)
177653
- return false;
177654
- const now = Date.now();
177655
- const expiryMs = this.cache.payload.exp * 1000;
177656
- return now < expiryMs + LicenseValidator.GRACE_PERIOD;
177657
- }
177658
- /** Check if the license is within its grace period (expired but still valid) */
177659
- isInGracePeriod() {
177660
- if (!this.cache)
177661
- return false;
177662
- const now = Date.now();
177663
- const expiryMs = this.cache.payload.exp * 1000;
177664
- return now >= expiryMs && now < expiryMs + LicenseValidator.GRACE_PERIOD;
177665
- }
177666
- resolveToken() {
177667
- // 1. Direct env var
177668
- if (process.env.VISOR_LICENSE) {
177669
- return process.env.VISOR_LICENSE.trim();
177670
- }
177671
- // 2. File path from env (validate against path traversal)
177672
- if (process.env.VISOR_LICENSE_FILE) {
177673
- // path.resolve() produces an absolute path with all '..' segments resolved,
177674
- // so a separate resolved.includes('..') check is unnecessary.
177675
- const resolved = path.resolve(process.env.VISOR_LICENSE_FILE);
177676
- const home = process.env.HOME || process.env.USERPROFILE || '';
177677
- const allowedPrefixes = [path.normalize(process.cwd())];
177678
- if (home)
177679
- allowedPrefixes.push(path.normalize(path.join(home, '.config', 'visor')));
177680
- // Resolve symlinks so an attacker cannot create a symlink inside an
177681
- // allowed prefix that points to an arbitrary file outside it.
177682
- let realPath;
177683
- try {
177684
- realPath = fs.realpathSync(resolved);
177685
- }
177686
- catch {
177687
- return null; // File doesn't exist or isn't accessible
177688
- }
177689
- const isSafe = allowedPrefixes.some(prefix => realPath === prefix || realPath.startsWith(prefix + path.sep));
177690
- if (!isSafe)
177691
- return null;
177692
- return this.readFile(realPath);
177693
- }
177694
- // 3. .visor-license in cwd
177695
- const cwdPath = path.join(process.cwd(), '.visor-license');
177696
- const cwdToken = this.readFile(cwdPath);
177697
- if (cwdToken)
177698
- return cwdToken;
177699
- // 4. ~/.config/visor/.visor-license
177700
- const home = process.env.HOME || process.env.USERPROFILE || '';
177701
- if (home) {
177702
- const configPath = path.join(home, '.config', 'visor', '.visor-license');
177703
- const configToken = this.readFile(configPath);
177704
- if (configToken)
177705
- return configToken;
177706
- }
177707
- return null;
177708
- }
177709
- readFile(filePath) {
177710
- try {
177711
- return fs.readFileSync(filePath, 'utf-8').trim();
177712
- }
177713
- catch {
177714
- return null;
177715
- }
177716
- }
177717
- verifyAndDecode(token) {
177718
- try {
177719
- const parts = token.split('.');
177720
- if (parts.length !== 3)
177721
- return null;
177722
- const [headerB64, payloadB64, signatureB64] = parts;
177723
- // Decode header to verify algorithm
177724
- const header = JSON.parse(Buffer.from(headerB64, 'base64url').toString());
177725
- if (header.alg !== 'EdDSA')
177726
- return null;
177727
- // Verify signature
177728
- const data = `${headerB64}.${payloadB64}`;
177729
- const signature = Buffer.from(signatureB64, 'base64url');
177730
- const publicKey = crypto.createPublicKey(LicenseValidator.PUBLIC_KEY);
177731
- // Validate that the loaded public key is actually Ed25519 (OID 1.3.101.112).
177732
- // This prevents algorithm-confusion attacks if the embedded key were ever
177733
- // swapped to a different type.
177734
- if (publicKey.asymmetricKeyType !== 'ed25519') {
177735
- return null;
177736
- }
177737
- // Ed25519 verification: algorithm must be null because EdDSA performs its
177738
- // own internal hashing (SHA-512) — passing a digest algorithm here would
177739
- // cause Node.js to throw. The key type is validated above.
177740
- const isValid = crypto.verify(null, Buffer.from(data), publicKey, signature);
177741
- if (!isValid)
177742
- return null;
177743
- // Decode payload
177744
- const payload = JSON.parse(Buffer.from(payloadB64, 'base64url').toString());
177745
- // Validate required fields
177746
- if (!payload.org ||
177747
- !Array.isArray(payload.features) ||
177748
- typeof payload.exp !== 'number' ||
177749
- typeof payload.iat !== 'number' ||
177750
- !payload.sub) {
177751
- return null;
177752
- }
177753
- // Check expiry (with grace period)
177754
- const now = Date.now();
177755
- const expiryMs = payload.exp * 1000;
177756
- if (now >= expiryMs + LicenseValidator.GRACE_PERIOD) {
177757
- return null;
177758
- }
177759
- return payload;
177760
- }
177761
- catch {
177762
- return null;
177763
- }
177764
- }
177765
- }
177766
- exports.LicenseValidator = LicenseValidator;
177767
-
177768
-
177769
- /***/ }),
177770
-
177771
- /***/ 87068:
177772
- /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
177773
-
177774
- "use strict";
177775
-
177776
- /**
177777
- * Copyright (c) ProbeLabs. All rights reserved.
177778
- * Licensed under the Elastic License 2.0; you may not use this file except
177779
- * in compliance with the Elastic License 2.0.
177780
- */
177781
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
177782
- if (k2 === undefined) k2 = k;
177783
- var desc = Object.getOwnPropertyDescriptor(m, k);
177784
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
177785
- desc = { enumerable: true, get: function() { return m[k]; } };
177786
- }
177787
- Object.defineProperty(o, k2, desc);
177788
- }) : (function(o, m, k, k2) {
177789
- if (k2 === undefined) k2 = k;
177790
- o[k2] = m[k];
177791
- }));
177792
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
177793
- Object.defineProperty(o, "default", { enumerable: true, value: v });
177794
- }) : function(o, v) {
177795
- o["default"] = v;
177796
- });
177797
- var __importStar = (this && this.__importStar) || (function () {
177798
- var ownKeys = function(o) {
177799
- ownKeys = Object.getOwnPropertyNames || function (o) {
177800
- var ar = [];
177801
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
177802
- return ar;
177803
- };
177804
- return ownKeys(o);
177805
- };
177806
- return function (mod) {
177807
- if (mod && mod.__esModule) return mod;
177808
- var result = {};
177809
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
177810
- __setModuleDefault(result, mod);
177811
- return result;
177812
- };
177813
- })();
177814
- Object.defineProperty(exports, "__esModule", ({ value: true }));
177815
- exports.loadEnterprisePolicyEngine = loadEnterprisePolicyEngine;
177816
- exports.loadEnterpriseStoreBackend = loadEnterpriseStoreBackend;
177817
- const default_engine_1 = __nccwpck_require__(93866);
177818
- /**
177819
- * Load the enterprise policy engine if licensed, otherwise return the default no-op engine.
177820
- *
177821
- * This is the sole import boundary between OSS and enterprise code. Core code
177822
- * must only import from this module (via dynamic `await import()`), never from
177823
- * individual enterprise submodules.
177824
- */
177825
- async function loadEnterprisePolicyEngine(config) {
177826
- try {
177827
- const { LicenseValidator } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(50069)));
177828
- const validator = new LicenseValidator();
177829
- const license = await validator.loadAndValidate();
177830
- if (!license || !validator.hasFeature('policy')) {
177831
- return new default_engine_1.DefaultPolicyEngine();
177832
- }
177833
- if (validator.isInGracePeriod()) {
177834
- // eslint-disable-next-line no-console
177835
- console.warn('[visor:enterprise] License has expired but is within the 72-hour grace period. ' +
177836
- 'Please renew your license.');
177837
- }
177838
- const { OpaPolicyEngine } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(39530)));
177839
- const engine = new OpaPolicyEngine(config);
177840
- await engine.initialize(config);
177841
- return engine;
177842
- }
177843
- catch (err) {
177844
- // Enterprise code not available or initialization failed
177845
- const msg = err instanceof Error ? err.message : String(err);
177846
- try {
177847
- const { logger } = __nccwpck_require__(86999);
177848
- logger.warn(`[PolicyEngine] Enterprise policy init failed, falling back to default: ${msg}`);
177849
- }
177850
- catch {
177851
- // silent
177852
- }
177853
- return new default_engine_1.DefaultPolicyEngine();
177854
- }
177855
- }
177856
- /**
177857
- * Load the enterprise schedule store backend if licensed.
177858
- *
177859
- * @param driver Database driver ('postgresql', 'mysql', or 'mssql')
177860
- * @param storageConfig Storage configuration with connection details
177861
- * @param haConfig Optional HA configuration
177862
- * @throws Error if enterprise license is not available or missing 'scheduler-sql' feature
177863
- */
177864
- async function loadEnterpriseStoreBackend(driver, storageConfig, haConfig) {
177865
- const { LicenseValidator } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(50069)));
177866
- const validator = new LicenseValidator();
177867
- const license = await validator.loadAndValidate();
177868
- if (!license || !validator.hasFeature('scheduler-sql')) {
177869
- throw new Error(`The ${driver} schedule storage driver requires a Visor Enterprise license ` +
177870
- `with the 'scheduler-sql' feature. Please upgrade or use driver: 'sqlite' (default).`);
177871
- }
177872
- if (validator.isInGracePeriod()) {
177873
- // eslint-disable-next-line no-console
177874
- console.warn('[visor:enterprise] License has expired but is within the 72-hour grace period. ' +
177875
- 'Please renew your license.');
177876
- }
177877
- const { KnexStoreBackend } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(63737)));
177878
- return new KnexStoreBackend(driver, storageConfig, haConfig);
177879
- }
177880
-
177881
-
177882
- /***/ }),
177883
-
177884
- /***/ 628:
177885
- /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
177886
-
177887
- "use strict";
177888
-
177889
- /**
177890
- * Copyright (c) ProbeLabs. All rights reserved.
177891
- * Licensed under the Elastic License 2.0; you may not use this file except
177892
- * in compliance with the Elastic License 2.0.
177893
- */
177894
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
177895
- if (k2 === undefined) k2 = k;
177896
- var desc = Object.getOwnPropertyDescriptor(m, k);
177897
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
177898
- desc = { enumerable: true, get: function() { return m[k]; } };
177899
- }
177900
- Object.defineProperty(o, k2, desc);
177901
- }) : (function(o, m, k, k2) {
177902
- if (k2 === undefined) k2 = k;
177903
- o[k2] = m[k];
177904
- }));
177905
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
177906
- Object.defineProperty(o, "default", { enumerable: true, value: v });
177907
- }) : function(o, v) {
177908
- o["default"] = v;
177909
- });
177910
- var __importStar = (this && this.__importStar) || (function () {
177911
- var ownKeys = function(o) {
177912
- ownKeys = Object.getOwnPropertyNames || function (o) {
177913
- var ar = [];
177914
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
177915
- return ar;
177916
- };
177917
- return ownKeys(o);
177918
- };
177919
- return function (mod) {
177920
- if (mod && mod.__esModule) return mod;
177921
- var result = {};
177922
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
177923
- __setModuleDefault(result, mod);
177924
- return result;
177925
- };
177926
- })();
177927
- Object.defineProperty(exports, "__esModule", ({ value: true }));
177928
- exports.OpaCompiler = void 0;
177929
- const fs = __importStar(__nccwpck_require__(79896));
177930
- const path = __importStar(__nccwpck_require__(16928));
177931
- const os = __importStar(__nccwpck_require__(70857));
177932
- const crypto = __importStar(__nccwpck_require__(76982));
177933
- const child_process_1 = __nccwpck_require__(35317);
177934
- /**
177935
- * OPA Rego Compiler - compiles .rego policy files to WASM bundles using the `opa` CLI.
177936
- *
177937
- * Handles:
177938
- * - Resolving input paths to WASM bytes (direct .wasm, directory with policy.wasm, or .rego files)
177939
- * - Compiling .rego files to WASM via `opa build`
177940
- * - Caching compiled bundles based on content hashes
177941
- * - Extracting policy.wasm from OPA tar.gz bundles
177942
- *
177943
- * Requires:
177944
- * - `opa` CLI on PATH (only when auto-compiling .rego files)
177945
- */
177946
- class OpaCompiler {
177947
- static CACHE_DIR = path.join(os.tmpdir(), 'visor-opa-cache');
177948
- /**
177949
- * Resolve the input paths to WASM bytes.
177950
- *
177951
- * Strategy:
177952
- * 1. If any path is a .wasm file, read it directly
177953
- * 2. If a directory contains policy.wasm, read it
177954
- * 3. Otherwise, collect all .rego files and auto-compile via `opa build`
177955
- */
177956
- async resolveWasmBytes(paths) {
177957
- // Collect .rego files and check for existing .wasm
177958
- const regoFiles = [];
177959
- for (const p of paths) {
177960
- const resolved = path.resolve(p);
177961
- // Reject paths containing '..' after resolution (path traversal)
177962
- if (path.normalize(resolved).includes('..')) {
177963
- throw new Error(`Policy path contains traversal sequences: ${p}`);
177964
- }
177965
- // Direct .wasm file
177966
- if (resolved.endsWith('.wasm') && fs.existsSync(resolved)) {
177967
- return fs.readFileSync(resolved);
177968
- }
177969
- if (!fs.existsSync(resolved))
177970
- continue;
177971
- const stat = fs.statSync(resolved);
177972
- if (stat.isDirectory()) {
177973
- // Check for pre-compiled policy.wasm in directory
177974
- const wasmCandidate = path.join(resolved, 'policy.wasm');
177975
- if (fs.existsSync(wasmCandidate)) {
177976
- return fs.readFileSync(wasmCandidate);
177977
- }
177978
- // Collect all .rego files from directory
177979
- const files = fs.readdirSync(resolved);
177980
- for (const f of files) {
177981
- if (f.endsWith('.rego')) {
177982
- regoFiles.push(path.join(resolved, f));
177983
- }
177984
- }
177985
- }
177986
- else if (resolved.endsWith('.rego')) {
177987
- regoFiles.push(resolved);
177988
- }
177989
- }
177990
- if (regoFiles.length === 0) {
177991
- throw new Error(`OPA WASM evaluator: no .wasm bundle or .rego files found in: ${paths.join(', ')}`);
177992
- }
177993
- // Auto-compile .rego -> .wasm
177994
- return this.compileRego(regoFiles);
177995
- }
177996
- /**
177997
- * Auto-compile .rego files to a WASM bundle using the `opa` CLI.
177998
- *
177999
- * Caches the compiled bundle based on a content hash of all input .rego files
178000
- * so subsequent runs skip compilation if policies haven't changed.
178001
- */
178002
- compileRego(regoFiles) {
178003
- // Check that `opa` CLI is available
178004
- try {
178005
- (0, child_process_1.execFileSync)('opa', ['version'], { stdio: 'pipe' });
178006
- }
178007
- catch {
178008
- throw new Error('OPA CLI (`opa`) not found on PATH. Install it from https://www.openpolicyagent.org/docs/latest/#running-opa\n' +
178009
- 'Or pre-compile your .rego files: opa build -t wasm -e visor -o bundle.tar.gz ' +
178010
- regoFiles.join(' '));
178011
- }
178012
- // Compute content hash for cache key
178013
- const hash = crypto.createHash('sha256');
178014
- for (const f of regoFiles.sort()) {
178015
- hash.update(fs.readFileSync(f));
178016
- hash.update(f); // include filename for disambiguation
178017
- }
178018
- const cacheKey = hash.digest('hex').slice(0, 16);
178019
- const cacheDir = OpaCompiler.CACHE_DIR;
178020
- const cachedWasm = path.join(cacheDir, `${cacheKey}.wasm`);
178021
- // Return cached bundle if still valid
178022
- if (fs.existsSync(cachedWasm)) {
178023
- return fs.readFileSync(cachedWasm);
178024
- }
178025
- // Compile to WASM via opa build
178026
- fs.mkdirSync(cacheDir, { recursive: true });
178027
- const bundleTar = path.join(cacheDir, `${cacheKey}-bundle.tar.gz`);
178028
- try {
178029
- const args = [
178030
- 'build',
178031
- '-t',
178032
- 'wasm',
178033
- '-e',
178034
- 'visor', // entrypoint: the visor package tree
178035
- '-o',
178036
- bundleTar,
178037
- ...regoFiles,
178038
- ];
178039
- (0, child_process_1.execFileSync)('opa', args, {
178040
- stdio: 'pipe',
178041
- timeout: 30000,
178042
- });
178043
- }
178044
- catch (err) {
178045
- const stderr = err?.stderr?.toString() || '';
178046
- throw new Error(`Failed to compile .rego files to WASM:\n${stderr}\n` +
178047
- 'Ensure your .rego files are valid and the `opa` CLI is installed.');
178048
- }
178049
- // Extract policy.wasm from the tar.gz bundle
178050
- // OPA bundles are tar.gz with /policy.wasm inside
178051
- try {
178052
- (0, child_process_1.execFileSync)('tar', ['-xzf', bundleTar, '-C', cacheDir, '/policy.wasm'], {
178053
- stdio: 'pipe',
178054
- });
178055
- const extractedWasm = path.join(cacheDir, 'policy.wasm');
178056
- if (fs.existsSync(extractedWasm)) {
178057
- // Move to cache-key named file
178058
- fs.renameSync(extractedWasm, cachedWasm);
178059
- }
178060
- }
178061
- catch {
178062
- // Some tar implementations don't like leading /
178063
- try {
178064
- (0, child_process_1.execFileSync)('tar', ['-xzf', bundleTar, '-C', cacheDir, 'policy.wasm'], {
178065
- stdio: 'pipe',
178066
- });
178067
- const extractedWasm = path.join(cacheDir, 'policy.wasm');
178068
- if (fs.existsSync(extractedWasm)) {
178069
- fs.renameSync(extractedWasm, cachedWasm);
178070
- }
178071
- }
178072
- catch (err2) {
178073
- throw new Error(`Failed to extract policy.wasm from OPA bundle: ${err2?.message || err2}`);
178074
- }
178075
- }
178076
- // Clean up tar
178077
- try {
178078
- fs.unlinkSync(bundleTar);
178079
- }
178080
- catch { }
178081
- if (!fs.existsSync(cachedWasm)) {
178082
- throw new Error('OPA build succeeded but policy.wasm was not found in the bundle');
178083
- }
178084
- return fs.readFileSync(cachedWasm);
178085
- }
178086
- }
178087
- exports.OpaCompiler = OpaCompiler;
178088
-
178089
-
178090
- /***/ }),
178091
-
178092
- /***/ 44693:
178093
- /***/ ((__unused_webpack_module, exports) => {
178094
-
178095
- "use strict";
178096
-
178097
- /**
178098
- * Copyright (c) ProbeLabs. All rights reserved.
178099
- * Licensed under the Elastic License 2.0; you may not use this file except
178100
- * in compliance with the Elastic License 2.0.
178101
- */
178102
- Object.defineProperty(exports, "__esModule", ({ value: true }));
178103
- exports.OpaHttpEvaluator = void 0;
178104
- /**
178105
- * OPA HTTP Evaluator - evaluates policies via an external OPA server's REST API.
178106
- *
178107
- * Uses the built-in `fetch` API (Node 18+), so no extra dependencies are needed.
178108
- */
178109
- class OpaHttpEvaluator {
178110
- baseUrl;
178111
- timeout;
178112
- constructor(baseUrl, timeout = 5000) {
178113
- // Validate URL format and protocol
178114
- let parsed;
178115
- try {
178116
- parsed = new URL(baseUrl);
178117
- }
178118
- catch {
178119
- throw new Error(`OPA HTTP evaluator: invalid URL: ${baseUrl}`);
178120
- }
178121
- if (!['http:', 'https:'].includes(parsed.protocol)) {
178122
- throw new Error(`OPA HTTP evaluator: url must use http:// or https:// protocol, got: ${baseUrl}`);
178123
- }
178124
- // Block cloud metadata, loopback, link-local, and private network addresses
178125
- const hostname = parsed.hostname;
178126
- if (this.isBlockedHostname(hostname)) {
178127
- throw new Error(`OPA HTTP evaluator: url must not point to internal, loopback, or private network addresses`);
178128
- }
178129
- // Normalize: strip trailing slash
178130
- this.baseUrl = baseUrl.replace(/\/+$/, '');
178131
- this.timeout = timeout;
178132
- }
178133
- /**
178134
- * Check if a hostname is blocked due to SSRF concerns.
178135
- *
178136
- * Blocks:
178137
- * - Loopback addresses (127.x.x.x, localhost, 0.0.0.0, ::1)
178138
- * - Link-local addresses (169.254.x.x)
178139
- * - Private networks (10.x.x.x, 172.16-31.x.x, 192.168.x.x)
178140
- * - IPv6 unique local addresses (fd00::/8)
178141
- * - Cloud metadata services (*.internal)
178142
- */
178143
- isBlockedHostname(hostname) {
178144
- if (!hostname)
178145
- return true; // block empty hostnames
178146
- // Normalize hostname: lowercase and remove brackets for IPv6
178147
- const normalized = hostname.toLowerCase().replace(/^\[|\]$/g, '');
178148
- // Block .internal domains (cloud metadata services)
178149
- if (normalized === 'metadata.google.internal' || normalized.endsWith('.internal')) {
178150
- return true;
178151
- }
178152
- // Block localhost variants
178153
- if (normalized === 'localhost' || normalized === 'localhost.localdomain') {
178154
- return true;
178155
- }
178156
- // Block IPv6 loopback
178157
- if (normalized === '::1' || normalized === '0:0:0:0:0:0:0:1') {
178158
- return true;
178159
- }
178160
- // Check IPv4 patterns
178161
- const ipv4Pattern = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
178162
- const ipv4Match = normalized.match(ipv4Pattern);
178163
- if (ipv4Match) {
178164
- const octets = ipv4Match.slice(1, 5).map(Number);
178165
- // Validate octets are in range [0, 255]
178166
- if (octets.some(octet => octet > 255)) {
178167
- return false;
178168
- }
178169
- const [a, b] = octets;
178170
- // Block loopback: 127.0.0.0/8
178171
- if (a === 127) {
178172
- return true;
178173
- }
178174
- // Block 0.0.0.0/8 (this host)
178175
- if (a === 0) {
178176
- return true;
178177
- }
178178
- // Block link-local: 169.254.0.0/16
178179
- if (a === 169 && b === 254) {
178180
- return true;
178181
- }
178182
- // Block private networks
178183
- // 10.0.0.0/8
178184
- if (a === 10) {
178185
- return true;
178186
- }
178187
- // 172.16.0.0/12 (172.16.x.x through 172.31.x.x)
178188
- if (a === 172 && b >= 16 && b <= 31) {
178189
- return true;
178190
- }
178191
- // 192.168.0.0/16
178192
- if (a === 192 && b === 168) {
178193
- return true;
178194
- }
178195
- }
178196
- // Check IPv6 patterns
178197
- // Block unique local addresses: fd00::/8
178198
- if (normalized.startsWith('fd') || normalized.startsWith('fc')) {
178199
- return true;
178200
- }
178201
- // Block link-local: fe80::/10
178202
- if (normalized.startsWith('fe80:')) {
178203
- return true;
178204
- }
178205
- return false;
178206
- }
178207
- /**
178208
- * Evaluate a policy rule against an input document via OPA REST API.
178209
- *
178210
- * @param input - The input document to evaluate
178211
- * @param rulePath - OPA rule path (e.g., 'visor/check/execute')
178212
- * @returns The result object from OPA, or undefined on error
178213
- */
178214
- async evaluate(input, rulePath) {
178215
- // OPA Data API: POST /v1/data/<path>
178216
- const encodedPath = rulePath
178217
- .split('/')
178218
- .map(s => encodeURIComponent(s))
178219
- .join('/');
178220
- const url = `${this.baseUrl}/v1/data/${encodedPath}`;
178221
- const controller = new AbortController();
178222
- const timer = setTimeout(() => controller.abort(), this.timeout);
178223
- try {
178224
- const response = await fetch(url, {
178225
- method: 'POST',
178226
- headers: { 'Content-Type': 'application/json' },
178227
- body: JSON.stringify({ input }),
178228
- signal: controller.signal,
178229
- });
178230
- if (!response.ok) {
178231
- throw new Error(`OPA HTTP ${response.status}: ${response.statusText}`);
178232
- }
178233
- let body;
178234
- try {
178235
- body = await response.json();
178236
- }
178237
- catch (jsonErr) {
178238
- throw new Error(`OPA HTTP evaluator: failed to parse JSON response: ${jsonErr instanceof Error ? jsonErr.message : String(jsonErr)}`);
178239
- }
178240
- // OPA returns { result: { ... } }
178241
- return body?.result;
178242
- }
178243
- finally {
178244
- clearTimeout(timer);
178245
- }
178246
- }
178247
- async shutdown() {
178248
- // No persistent connections to close
178249
- }
178250
- }
178251
- exports.OpaHttpEvaluator = OpaHttpEvaluator;
178252
-
178253
-
178254
- /***/ }),
178255
-
178256
- /***/ 39530:
178257
- /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
178258
-
178259
- "use strict";
178260
-
178261
- /**
178262
- * Copyright (c) ProbeLabs. All rights reserved.
178263
- * Licensed under the Elastic License 2.0; you may not use this file except
178264
- * in compliance with the Elastic License 2.0.
178265
- */
178266
- Object.defineProperty(exports, "__esModule", ({ value: true }));
178267
- exports.OpaPolicyEngine = void 0;
178268
- const opa_wasm_evaluator_1 = __nccwpck_require__(8613);
178269
- const opa_http_evaluator_1 = __nccwpck_require__(44693);
178270
- const policy_input_builder_1 = __nccwpck_require__(17117);
178271
- /**
178272
- * Enterprise OPA Policy Engine.
178273
- *
178274
- * Wraps both WASM (local) and HTTP (remote) OPA evaluators behind the
178275
- * OSS PolicyEngine interface. All OPA input building and role resolution
178276
- * is handled internally — the OSS call sites pass only plain types.
178277
- */
178278
- class OpaPolicyEngine {
178279
- evaluator = null;
178280
- fallback;
178281
- timeout;
178282
- config;
178283
- inputBuilder = null;
178284
- logger = null;
178285
- constructor(config) {
178286
- this.config = config;
178287
- this.fallback = config.fallback || 'deny';
178288
- this.timeout = config.timeout || 5000;
178289
- }
178290
- async initialize(config) {
178291
- // Resolve logger once at initialization
178292
- try {
178293
- this.logger = (__nccwpck_require__(86999).logger);
178294
- }
178295
- catch {
178296
- // logger not available in this context
178297
- }
178298
- // Build actor/repo context from environment (available at engine init time)
178299
- const actor = {
178300
- authorAssociation: process.env.VISOR_AUTHOR_ASSOCIATION,
178301
- login: process.env.VISOR_AUTHOR_LOGIN || process.env.GITHUB_ACTOR,
178302
- isLocalMode: !process.env.GITHUB_ACTIONS,
178303
- };
178304
- const repo = {
178305
- owner: process.env.GITHUB_REPOSITORY_OWNER,
178306
- name: process.env.GITHUB_REPOSITORY?.split('/')[1],
178307
- branch: process.env.GITHUB_HEAD_REF,
178308
- baseBranch: process.env.GITHUB_BASE_REF,
178309
- event: process.env.GITHUB_EVENT_NAME,
178310
- };
178311
- const prNum = process.env.GITHUB_PR_NUMBER
178312
- ? parseInt(process.env.GITHUB_PR_NUMBER, 10)
178313
- : undefined;
178314
- const pullRequest = {
178315
- number: prNum !== undefined && Number.isFinite(prNum) ? prNum : undefined,
178316
- };
178317
- this.inputBuilder = new policy_input_builder_1.PolicyInputBuilder(config, actor, repo, pullRequest);
178318
- if (config.engine === 'local') {
178319
- if (!config.rules) {
178320
- throw new Error('OPA local mode requires `policy.rules` path to .wasm or .rego files');
178321
- }
178322
- const wasm = new opa_wasm_evaluator_1.OpaWasmEvaluator();
178323
- await wasm.initialize(config.rules);
178324
- if (config.data) {
178325
- wasm.loadData(config.data);
178326
- }
178327
- this.evaluator = wasm;
178328
- }
178329
- else if (config.engine === 'remote') {
178330
- if (!config.url) {
178331
- throw new Error('OPA remote mode requires `policy.url` pointing to OPA server');
178332
- }
178333
- this.evaluator = new opa_http_evaluator_1.OpaHttpEvaluator(config.url, this.timeout);
178334
- }
178335
- else {
178336
- this.evaluator = null;
178337
- }
178338
- }
178339
- /**
178340
- * Update actor/repo/PR context (e.g., after PR info becomes available).
178341
- * Called by the enterprise loader when engine context is enriched.
178342
- */
178343
- setActorContext(actor, repo, pullRequest) {
178344
- this.inputBuilder = new policy_input_builder_1.PolicyInputBuilder(this.config, actor, repo, pullRequest);
178345
- }
178346
- async evaluateCheckExecution(checkId, checkConfig) {
178347
- if (!this.evaluator || !this.inputBuilder)
178348
- return { allowed: true };
178349
- const cfg = checkConfig && typeof checkConfig === 'object'
178350
- ? checkConfig
178351
- : {};
178352
- const policyOverride = cfg.policy;
178353
- const input = this.inputBuilder.forCheckExecution({
178354
- id: checkId,
178355
- type: cfg.type || 'ai',
178356
- group: cfg.group,
178357
- tags: cfg.tags,
178358
- criticality: cfg.criticality,
178359
- sandbox: cfg.sandbox,
178360
- policy: policyOverride,
178361
- });
178362
- return this.doEvaluate(input, this.resolveRulePath('check.execute', policyOverride?.rule));
178363
- }
178364
- async evaluateToolInvocation(serverName, methodName, transport) {
178365
- if (!this.evaluator || !this.inputBuilder)
178366
- return { allowed: true };
178367
- const input = this.inputBuilder.forToolInvocation(serverName, methodName, transport);
178368
- return this.doEvaluate(input, 'visor/tool/invoke');
178369
- }
178370
- async evaluateCapabilities(checkId, capabilities) {
178371
- if (!this.evaluator || !this.inputBuilder)
178372
- return { allowed: true };
178373
- const input = this.inputBuilder.forCapabilityResolve(checkId, capabilities);
178374
- return this.doEvaluate(input, 'visor/capability/resolve');
178375
- }
178376
- async shutdown() {
178377
- if (this.evaluator && 'shutdown' in this.evaluator) {
178378
- await this.evaluator.shutdown();
178379
- }
178380
- this.evaluator = null;
178381
- this.inputBuilder = null;
178382
- }
178383
- resolveRulePath(defaultScope, override) {
178384
- if (override) {
178385
- return override.startsWith('visor/') ? override : `visor/${override}`;
178386
- }
178387
- return `visor/${defaultScope.replace(/\./g, '/')}`;
178388
- }
178389
- async doEvaluate(input, rulePath) {
178390
- try {
178391
- this.logger?.debug(`[PolicyEngine] Evaluating ${rulePath}`, JSON.stringify(input));
178392
- let timer;
178393
- const timeoutPromise = new Promise((_resolve, reject) => {
178394
- timer = setTimeout(() => reject(new Error('policy evaluation timeout')), this.timeout);
178395
- });
178396
- try {
178397
- const result = await Promise.race([this.rawEvaluate(input, rulePath), timeoutPromise]);
178398
- const decision = this.parseDecision(result);
178399
- // In warn mode, override denied decisions to allowed but flag as warn
178400
- if (!decision.allowed && this.fallback === 'warn') {
178401
- decision.allowed = true;
178402
- decision.warn = true;
178403
- decision.reason = `audit: ${decision.reason || 'policy denied'}`;
178404
- }
178405
- this.logger?.debug(`[PolicyEngine] Decision for ${rulePath}: allowed=${decision.allowed}, warn=${decision.warn || false}, reason=${decision.reason || 'none'}`);
178406
- return decision;
178407
- }
178408
- finally {
178409
- if (timer)
178410
- clearTimeout(timer);
178411
- }
178412
- }
178413
- catch (err) {
178414
- const msg = err instanceof Error ? err.message : String(err);
178415
- this.logger?.warn(`[PolicyEngine] Evaluation failed for ${rulePath}: ${msg}`);
178416
- return {
178417
- allowed: this.fallback === 'allow' || this.fallback === 'warn',
178418
- warn: this.fallback === 'warn' ? true : undefined,
178419
- reason: `policy evaluation failed, fallback=${this.fallback}`,
178420
- };
178421
- }
178422
- }
178423
- async rawEvaluate(input, rulePath) {
178424
- if (this.evaluator instanceof opa_wasm_evaluator_1.OpaWasmEvaluator) {
178425
- const result = await this.evaluator.evaluate(input);
178426
- // WASM compiled with `-e visor` entrypoint returns the full visor package tree.
178427
- // Navigate to the specific rule subtree using rulePath segments.
178428
- // e.g., 'visor/check/execute' → result.check.execute
178429
- return this.navigateWasmResult(result, rulePath);
178430
- }
178431
- return this.evaluator.evaluate(input, rulePath);
178432
- }
178433
- /**
178434
- * Navigate nested OPA WASM result tree to reach the specific rule's output.
178435
- * The WASM entrypoint `-e visor` means the result root IS the visor package,
178436
- * so we strip the `visor/` prefix and walk the remaining segments.
178437
- */
178438
- navigateWasmResult(result, rulePath) {
178439
- if (!result || typeof result !== 'object')
178440
- return result;
178441
- // Strip the 'visor/' prefix (matches our compilation entrypoint)
178442
- const segments = rulePath.replace(/^visor\//, '').split('/');
178443
- let current = result;
178444
- for (const seg of segments) {
178445
- if (current && typeof current === 'object' && seg in current) {
178446
- current = current[seg];
178447
- }
178448
- else {
178449
- return undefined; // path not found in result tree
178450
- }
178451
- }
178452
- return current;
178453
- }
178454
- parseDecision(result) {
178455
- if (result === undefined || result === null) {
178456
- return {
178457
- allowed: this.fallback === 'allow' || this.fallback === 'warn',
178458
- warn: this.fallback === 'warn' ? true : undefined,
178459
- reason: this.fallback === 'warn' ? 'audit: no policy result' : 'no policy result',
178460
- };
178461
- }
178462
- const allowed = result.allowed !== false;
178463
- const decision = {
178464
- allowed,
178465
- reason: result.reason,
178466
- };
178467
- if (result.capabilities) {
178468
- decision.capabilities = result.capabilities;
178469
- }
178470
- return decision;
178471
- }
178472
- }
178473
- exports.OpaPolicyEngine = OpaPolicyEngine;
178474
-
178475
-
178476
- /***/ }),
178477
-
178478
- /***/ 8613:
178479
- /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
178480
-
178481
- "use strict";
178482
-
178483
- /**
178484
- * Copyright (c) ProbeLabs. All rights reserved.
178485
- * Licensed under the Elastic License 2.0; you may not use this file except
178486
- * in compliance with the Elastic License 2.0.
178487
- */
178488
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
178489
- if (k2 === undefined) k2 = k;
178490
- var desc = Object.getOwnPropertyDescriptor(m, k);
178491
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
178492
- desc = { enumerable: true, get: function() { return m[k]; } };
178493
- }
178494
- Object.defineProperty(o, k2, desc);
178495
- }) : (function(o, m, k, k2) {
178496
- if (k2 === undefined) k2 = k;
178497
- o[k2] = m[k];
178498
- }));
178499
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
178500
- Object.defineProperty(o, "default", { enumerable: true, value: v });
178501
- }) : function(o, v) {
178502
- o["default"] = v;
178503
- });
178504
- var __importStar = (this && this.__importStar) || (function () {
178505
- var ownKeys = function(o) {
178506
- ownKeys = Object.getOwnPropertyNames || function (o) {
178507
- var ar = [];
178508
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
178509
- return ar;
178510
- };
178511
- return ownKeys(o);
178512
- };
178513
- return function (mod) {
178514
- if (mod && mod.__esModule) return mod;
178515
- var result = {};
178516
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
178517
- __setModuleDefault(result, mod);
178518
- return result;
178519
- };
178520
- })();
178521
- Object.defineProperty(exports, "__esModule", ({ value: true }));
178522
- exports.OpaWasmEvaluator = void 0;
178523
- const fs = __importStar(__nccwpck_require__(79896));
178524
- const path = __importStar(__nccwpck_require__(16928));
178525
- const opa_compiler_1 = __nccwpck_require__(628);
178526
- /**
178527
- * OPA WASM Evaluator - loads and evaluates OPA policies locally.
178528
- *
178529
- * Supports three input formats:
178530
- * 1. Pre-compiled `.wasm` bundle — loaded directly (fastest startup)
178531
- * 2. `.rego` files or directory — auto-compiled to WASM via `opa build` CLI
178532
- * 3. Directory with `policy.wasm` inside — loaded directly
178533
- *
178534
- * Compilation and caching of .rego files is delegated to {@link OpaCompiler}.
178535
- *
178536
- * Requires:
178537
- * - `@open-policy-agent/opa-wasm` npm package (optional dep)
178538
- * - `opa` CLI on PATH (only when auto-compiling .rego files)
178539
- */
178540
- class OpaWasmEvaluator {
178541
- policy = null;
178542
- dataDocument = {};
178543
- compiler = new opa_compiler_1.OpaCompiler();
178544
- async initialize(rulesPath) {
178545
- const paths = Array.isArray(rulesPath) ? rulesPath : [rulesPath];
178546
- const wasmBytes = await this.compiler.resolveWasmBytes(paths);
178547
- try {
178548
- // Use createRequire to load the optional dep at runtime without ncc bundling it.
178549
- // `new Function('id', 'return require(id)')` fails in ncc bundles because
178550
- // `require` is not in the `new Function` scope. `createRequire` works correctly
178551
- // because it creates a real Node.js require rooted at the given path.
178552
- // eslint-disable-next-line @typescript-eslint/no-var-requires
178553
- const { createRequire } = __nccwpck_require__(73339);
178554
- const runtimeRequire = createRequire(__filename);
178555
- const opaWasm = runtimeRequire('@open-policy-agent/opa-wasm');
178556
- const loadPolicy = opaWasm.loadPolicy || opaWasm.default?.loadPolicy;
178557
- if (!loadPolicy) {
178558
- throw new Error('loadPolicy not found in @open-policy-agent/opa-wasm');
178559
- }
178560
- this.policy = await loadPolicy(wasmBytes);
178561
- }
178562
- catch (err) {
178563
- if (err?.code === 'MODULE_NOT_FOUND' || err?.code === 'ERR_MODULE_NOT_FOUND') {
178564
- throw new Error('OPA WASM evaluator requires @open-policy-agent/opa-wasm. ' +
178565
- 'Install it with: npm install @open-policy-agent/opa-wasm');
178566
- }
178567
- throw err;
178568
- }
178569
- }
178570
- /**
178571
- * Load external data from a JSON file to use as the OPA data document.
178572
- * The loaded data will be passed to `policy.setData()` during evaluation,
178573
- * making it available in Rego via `data.<key>`.
178574
- */
178575
- loadData(dataPath) {
178576
- const resolved = path.resolve(dataPath);
178577
- if (path.normalize(resolved).includes('..')) {
178578
- throw new Error(`Data path contains traversal sequences: ${dataPath}`);
178579
- }
178580
- if (!fs.existsSync(resolved)) {
178581
- throw new Error(`OPA data file not found: ${resolved}`);
178582
- }
178583
- const stat = fs.statSync(resolved);
178584
- if (stat.size > 10 * 1024 * 1024) {
178585
- throw new Error(`OPA data file exceeds 10MB limit: ${resolved} (${stat.size} bytes)`);
178586
- }
178587
- const raw = fs.readFileSync(resolved, 'utf-8');
178588
- try {
178589
- const parsed = JSON.parse(raw);
178590
- if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
178591
- throw new Error('OPA data file must contain a JSON object (not an array or primitive)');
178592
- }
178593
- this.dataDocument = parsed;
178594
- }
178595
- catch (err) {
178596
- if (err.message.startsWith('OPA data file must')) {
178597
- throw err;
178598
- }
178599
- throw new Error(`Failed to parse OPA data file ${resolved}: ${err.message}`);
178600
- }
178601
- }
178602
- async evaluate(input) {
178603
- if (!this.policy) {
178604
- throw new Error('OPA WASM evaluator not initialized');
178605
- }
178606
- this.policy.setData(this.dataDocument);
178607
- const resultSet = this.policy.evaluate(input);
178608
- if (Array.isArray(resultSet) && resultSet.length > 0) {
178609
- return resultSet[0].result;
178610
- }
178611
- return undefined;
178612
- }
178613
- async shutdown() {
178614
- if (this.policy) {
178615
- // opa-wasm policy objects may have a close/free method for WASM cleanup
178616
- if (typeof this.policy.close === 'function') {
178617
- try {
178618
- this.policy.close();
178619
- }
178620
- catch { }
178621
- }
178622
- else if (typeof this.policy.free === 'function') {
178623
- try {
178624
- this.policy.free();
178625
- }
178626
- catch { }
178627
- }
178628
- }
178629
- this.policy = null;
178630
- }
178631
- }
178632
- exports.OpaWasmEvaluator = OpaWasmEvaluator;
178633
-
178634
-
178635
- /***/ }),
178636
-
178637
- /***/ 17117:
178638
- /***/ ((__unused_webpack_module, exports) => {
178639
-
178640
- "use strict";
178641
-
178642
- /**
178643
- * Copyright (c) ProbeLabs. All rights reserved.
178644
- * Licensed under the Elastic License 2.0; you may not use this file except
178645
- * in compliance with the Elastic License 2.0.
178646
- */
178647
- Object.defineProperty(exports, "__esModule", ({ value: true }));
178648
- exports.PolicyInputBuilder = void 0;
178649
- /**
178650
- * Builds OPA-compatible input documents from engine context.
178651
- *
178652
- * Resolves actor roles from the `policy.roles` config section by matching
178653
- * the actor's authorAssociation and login against role definitions.
178654
- */
178655
- class PolicyInputBuilder {
178656
- roles;
178657
- actor;
178658
- repository;
178659
- pullRequest;
178660
- constructor(policyConfig, actor, repository, pullRequest) {
178661
- this.roles = policyConfig.roles || {};
178662
- this.actor = actor;
178663
- this.repository = repository;
178664
- this.pullRequest = pullRequest;
178665
- }
178666
- /** Resolve which roles apply to the current actor. */
178667
- resolveRoles() {
178668
- const matched = [];
178669
- for (const [roleName, roleConfig] of Object.entries(this.roles)) {
178670
- let identityMatch = false;
178671
- if (roleConfig.author_association &&
178672
- this.actor.authorAssociation &&
178673
- roleConfig.author_association.includes(this.actor.authorAssociation)) {
178674
- identityMatch = true;
178675
- }
178676
- if (!identityMatch &&
178677
- roleConfig.users &&
178678
- this.actor.login &&
178679
- roleConfig.users.includes(this.actor.login)) {
178680
- identityMatch = true;
178681
- }
178682
- // Slack user ID match
178683
- if (!identityMatch &&
178684
- roleConfig.slack_users &&
178685
- this.actor.slack?.userId &&
178686
- roleConfig.slack_users.includes(this.actor.slack.userId)) {
178687
- identityMatch = true;
178688
- }
178689
- // Email match (case-insensitive)
178690
- if (!identityMatch && roleConfig.emails && this.actor.slack?.email) {
178691
- const actorEmail = this.actor.slack.email.toLowerCase();
178692
- if (roleConfig.emails.some(e => e.toLowerCase() === actorEmail)) {
178693
- identityMatch = true;
178694
- }
178695
- }
178696
- // Note: teams-based role resolution requires GitHub API access (read:org scope)
178697
- // and is not yet implemented. If configured, the role will not match via teams.
178698
- if (!identityMatch)
178699
- continue;
178700
- // slack_channels gate: if set, the role only applies when triggered from one of these channels
178701
- if (roleConfig.slack_channels && roleConfig.slack_channels.length > 0) {
178702
- if (!this.actor.slack?.channelId ||
178703
- !roleConfig.slack_channels.includes(this.actor.slack.channelId)) {
178704
- continue;
178705
- }
178706
- }
178707
- matched.push(roleName);
178708
- }
178709
- return matched;
178710
- }
178711
- buildActor() {
178712
- return {
178713
- authorAssociation: this.actor.authorAssociation,
178714
- login: this.actor.login,
178715
- roles: this.resolveRoles(),
178716
- isLocalMode: this.actor.isLocalMode,
178717
- ...(this.actor.slack && { slack: this.actor.slack }),
178718
- };
178719
- }
178720
- forCheckExecution(check) {
178721
- return {
178722
- scope: 'check.execute',
178723
- check: {
178724
- id: check.id,
178725
- type: check.type,
178726
- group: check.group,
178727
- tags: check.tags,
178728
- criticality: check.criticality,
178729
- sandbox: check.sandbox,
178730
- policy: check.policy,
178731
- },
178732
- actor: this.buildActor(),
178733
- repository: this.repository,
178734
- pullRequest: this.pullRequest,
178735
- };
178736
- }
178737
- forToolInvocation(serverName, methodName, transport) {
178738
- return {
178739
- scope: 'tool.invoke',
178740
- tool: { serverName, methodName, transport },
178741
- actor: this.buildActor(),
178742
- repository: this.repository,
178743
- pullRequest: this.pullRequest,
178744
- };
178745
- }
178746
- forCapabilityResolve(checkId, capabilities) {
178747
- return {
178748
- scope: 'capability.resolve',
178749
- check: { id: checkId, type: 'ai' },
178750
- capability: capabilities,
178751
- actor: this.buildActor(),
178752
- repository: this.repository,
178753
- pullRequest: this.pullRequest,
178754
- };
178755
- }
178756
- }
178757
- exports.PolicyInputBuilder = PolicyInputBuilder;
178758
-
178759
-
178760
- /***/ }),
178761
-
178762
- /***/ 63737:
178763
- /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
178764
-
178765
- "use strict";
178766
-
178767
- /**
178768
- * Copyright (c) ProbeLabs. All rights reserved.
178769
- * Licensed under the Elastic License 2.0; you may not use this file except
178770
- * in compliance with the Elastic License 2.0.
178771
- */
178772
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
178773
- if (k2 === undefined) k2 = k;
178774
- var desc = Object.getOwnPropertyDescriptor(m, k);
178775
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
178776
- desc = { enumerable: true, get: function() { return m[k]; } };
178777
- }
178778
- Object.defineProperty(o, k2, desc);
178779
- }) : (function(o, m, k, k2) {
178780
- if (k2 === undefined) k2 = k;
178781
- o[k2] = m[k];
178782
- }));
178783
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
178784
- Object.defineProperty(o, "default", { enumerable: true, value: v });
178785
- }) : function(o, v) {
178786
- o["default"] = v;
178787
- });
178788
- var __importStar = (this && this.__importStar) || (function () {
178789
- var ownKeys = function(o) {
178790
- ownKeys = Object.getOwnPropertyNames || function (o) {
178791
- var ar = [];
178792
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
178793
- return ar;
178794
- };
178795
- return ownKeys(o);
178796
- };
178797
- return function (mod) {
178798
- if (mod && mod.__esModule) return mod;
178799
- var result = {};
178800
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
178801
- __setModuleDefault(result, mod);
178802
- return result;
178803
- };
178804
- })();
178805
- Object.defineProperty(exports, "__esModule", ({ value: true }));
178806
- exports.KnexStoreBackend = void 0;
178807
- /**
178808
- * Knex-backed schedule store for PostgreSQL, MySQL, and MSSQL (Enterprise)
178809
- *
178810
- * Uses Knex query builder for database-agnostic SQL. Same schema as SQLite backend
178811
- * but with real distributed locking via row-level claims (claimed_by/claimed_at/lock_token).
178812
- */
178813
- const fs = __importStar(__nccwpck_require__(79896));
178814
- const path = __importStar(__nccwpck_require__(16928));
178815
- const uuid_1 = __nccwpck_require__(31914);
178816
- const logger_1 = __nccwpck_require__(86999);
178817
- function toNum(val) {
178818
- if (val === null || val === undefined)
178819
- return undefined;
178820
- return typeof val === 'string' ? parseInt(val, 10) : val;
178821
- }
178822
- function safeJsonParse(value) {
178823
- if (!value)
178824
- return undefined;
178825
- try {
178826
- return JSON.parse(value);
178827
- }
178828
- catch {
178829
- return undefined;
178830
- }
178831
- }
178832
- function fromTriggerRow(row) {
178833
- return {
178834
- id: row.id,
178835
- creatorId: row.creator_id,
178836
- creatorContext: row.creator_context ?? undefined,
178837
- creatorName: row.creator_name ?? undefined,
178838
- description: row.description ?? undefined,
178839
- channels: safeJsonParse(row.channels),
178840
- fromUsers: safeJsonParse(row.from_users),
178841
- fromBots: row.from_bots === true || row.from_bots === 1,
178842
- contains: safeJsonParse(row.contains),
178843
- matchPattern: row.match_pattern ?? undefined,
178844
- threads: row.threads,
178845
- workflow: row.workflow,
178846
- inputs: safeJsonParse(row.inputs),
178847
- outputContext: safeJsonParse(row.output_context),
178848
- status: row.status,
178849
- enabled: row.enabled === true || row.enabled === 1,
178850
- createdAt: toNum(row.created_at),
178851
- };
178852
- }
178853
- function toTriggerInsertRow(trigger) {
178854
- return {
178855
- id: trigger.id,
178856
- creator_id: trigger.creatorId,
178857
- creator_context: trigger.creatorContext ?? null,
178858
- creator_name: trigger.creatorName ?? null,
178859
- description: trigger.description ?? null,
178860
- channels: trigger.channels ? JSON.stringify(trigger.channels) : null,
178861
- from_users: trigger.fromUsers ? JSON.stringify(trigger.fromUsers) : null,
178862
- from_bots: trigger.fromBots,
178863
- contains: trigger.contains ? JSON.stringify(trigger.contains) : null,
178864
- match_pattern: trigger.matchPattern ?? null,
178865
- threads: trigger.threads,
178866
- workflow: trigger.workflow,
178867
- inputs: trigger.inputs ? JSON.stringify(trigger.inputs) : null,
178868
- output_context: trigger.outputContext ? JSON.stringify(trigger.outputContext) : null,
178869
- status: trigger.status,
178870
- enabled: trigger.enabled,
178871
- created_at: trigger.createdAt,
178872
- };
178873
- }
178874
- function fromDbRow(row) {
178875
- return {
178876
- id: row.id,
178877
- creatorId: row.creator_id,
178878
- creatorContext: row.creator_context ?? undefined,
178879
- creatorName: row.creator_name ?? undefined,
178880
- timezone: row.timezone,
178881
- schedule: row.schedule_expr,
178882
- runAt: toNum(row.run_at),
178883
- isRecurring: row.is_recurring === true || row.is_recurring === 1,
178884
- originalExpression: row.original_expression,
178885
- workflow: row.workflow ?? undefined,
178886
- workflowInputs: safeJsonParse(row.workflow_inputs),
178887
- outputContext: safeJsonParse(row.output_context),
178888
- status: row.status,
178889
- createdAt: toNum(row.created_at),
178890
- lastRunAt: toNum(row.last_run_at),
178891
- nextRunAt: toNum(row.next_run_at),
178892
- runCount: row.run_count,
178893
- failureCount: row.failure_count,
178894
- lastError: row.last_error ?? undefined,
178895
- previousResponse: row.previous_response ?? undefined,
178896
- };
178897
- }
178898
- function toInsertRow(schedule) {
178899
- return {
178900
- id: schedule.id,
178901
- creator_id: schedule.creatorId,
178902
- creator_context: schedule.creatorContext ?? null,
178903
- creator_name: schedule.creatorName ?? null,
178904
- timezone: schedule.timezone,
178905
- schedule_expr: schedule.schedule,
178906
- run_at: schedule.runAt ?? null,
178907
- is_recurring: schedule.isRecurring,
178908
- original_expression: schedule.originalExpression,
178909
- workflow: schedule.workflow ?? null,
178910
- workflow_inputs: schedule.workflowInputs ? JSON.stringify(schedule.workflowInputs) : null,
178911
- output_context: schedule.outputContext ? JSON.stringify(schedule.outputContext) : null,
178912
- status: schedule.status,
178913
- created_at: schedule.createdAt,
178914
- last_run_at: schedule.lastRunAt ?? null,
178915
- next_run_at: schedule.nextRunAt ?? null,
178916
- run_count: schedule.runCount,
178917
- failure_count: schedule.failureCount,
178918
- last_error: schedule.lastError ?? null,
178919
- previous_response: schedule.previousResponse ?? null,
178920
- };
178921
- }
178922
- /**
178923
- * Enterprise Knex-backed store for PostgreSQL, MySQL, and MSSQL
178924
- */
178925
- class KnexStoreBackend {
178926
- knex = null;
178927
- driver;
178928
- connection;
178929
- constructor(driver, storageConfig, _haConfig) {
178930
- this.driver = driver;
178931
- this.connection = (storageConfig.connection || {});
178932
- }
178933
- async initialize() {
178934
- // Load knex dynamically
178935
- const { createRequire } = __nccwpck_require__(73339);
178936
- const runtimeRequire = createRequire(__filename);
178937
- let knexFactory;
178938
- try {
178939
- knexFactory = runtimeRequire('knex');
178940
- }
178941
- catch (err) {
178942
- const code = err?.code;
178943
- if (code === 'MODULE_NOT_FOUND' || code === 'ERR_MODULE_NOT_FOUND') {
178944
- throw new Error('knex is required for PostgreSQL/MySQL/MSSQL schedule storage. ' +
178945
- 'Install it with: npm install knex');
178946
- }
178947
- throw err;
178948
- }
178949
- const clientMap = {
178950
- postgresql: 'pg',
178951
- mysql: 'mysql2',
178952
- mssql: 'tedious',
178953
- };
178954
- const client = clientMap[this.driver];
178955
- // Build connection config
178956
- let connection;
178957
- if (this.connection.connection_string) {
178958
- connection = this.connection.connection_string;
178959
- }
178960
- else if (this.driver === 'mssql') {
178961
- connection = this.buildMssqlConnection();
178962
- }
178963
- else {
178964
- connection = this.buildStandardConnection();
178965
- }
178966
- this.knex = knexFactory({
178967
- client,
178968
- connection,
178969
- pool: {
178970
- min: this.connection.pool?.min ?? 0,
178971
- max: this.connection.pool?.max ?? 10,
178972
- },
178973
- });
178974
- // Run schema migration
178975
- await this.migrateSchema();
178976
- logger_1.logger.info(`[KnexStore] Initialized (${this.driver})`);
178977
- }
178978
- buildStandardConnection() {
178979
- return {
178980
- host: this.connection.host || 'localhost',
178981
- port: this.connection.port,
178982
- database: this.connection.database || 'visor',
178983
- user: this.connection.user,
178984
- password: this.connection.password,
178985
- ssl: this.resolveSslConfig(),
178986
- };
178987
- }
178988
- buildMssqlConnection() {
178989
- const ssl = this.connection.ssl;
178990
- const sslEnabled = ssl === true || (typeof ssl === 'object' && ssl.enabled !== false);
178991
- return {
178992
- server: this.connection.host || 'localhost',
178993
- port: this.connection.port,
178994
- database: this.connection.database || 'visor',
178995
- user: this.connection.user,
178996
- password: this.connection.password,
178997
- options: {
178998
- encrypt: sslEnabled,
178999
- trustServerCertificate: typeof ssl === 'object' ? ssl.reject_unauthorized === false : !sslEnabled,
179000
- },
179001
- };
179002
- }
179003
- resolveSslConfig() {
179004
- const ssl = this.connection.ssl;
179005
- if (ssl === false || ssl === undefined)
179006
- return false;
179007
- if (ssl === true)
179008
- return { rejectUnauthorized: true };
179009
- // Object config
179010
- if (ssl.enabled === false)
179011
- return false;
179012
- const result = {
179013
- rejectUnauthorized: ssl.reject_unauthorized !== false,
179014
- };
179015
- if (ssl.ca) {
179016
- const caPath = this.validateSslPath(ssl.ca, 'CA certificate');
179017
- result.ca = fs.readFileSync(caPath, 'utf8');
179018
- }
179019
- if (ssl.cert) {
179020
- const certPath = this.validateSslPath(ssl.cert, 'client certificate');
179021
- result.cert = fs.readFileSync(certPath, 'utf8');
179022
- }
179023
- if (ssl.key) {
179024
- const keyPath = this.validateSslPath(ssl.key, 'client key');
179025
- result.key = fs.readFileSync(keyPath, 'utf8');
179026
- }
179027
- return result;
179028
- }
179029
- validateSslPath(filePath, label) {
179030
- const resolved = path.resolve(filePath);
179031
- if (resolved !== path.normalize(resolved)) {
179032
- throw new Error(`SSL ${label} path contains invalid sequences: ${filePath}`);
179033
- }
179034
- if (!fs.existsSync(resolved)) {
179035
- throw new Error(`SSL ${label} not found: ${filePath}`);
179036
- }
179037
- return resolved;
179038
- }
179039
- async shutdown() {
179040
- if (this.knex) {
179041
- await this.knex.destroy();
179042
- this.knex = null;
179043
- }
179044
- }
179045
- async migrateSchema() {
179046
- const knex = this.getKnex();
179047
- const exists = await knex.schema.hasTable('schedules');
179048
- if (!exists) {
179049
- await knex.schema.createTable('schedules', table => {
179050
- table.string('id', 36).primary();
179051
- table.string('creator_id', 255).notNullable().index();
179052
- table.string('creator_context', 255);
179053
- table.string('creator_name', 255);
179054
- table.string('timezone', 64).notNullable().defaultTo('UTC');
179055
- table.string('schedule_expr', 255);
179056
- table.bigInteger('run_at');
179057
- table.boolean('is_recurring').notNullable();
179058
- table.text('original_expression');
179059
- table.string('workflow', 255);
179060
- table.text('workflow_inputs');
179061
- table.text('output_context');
179062
- table.string('status', 20).notNullable().index();
179063
- table.bigInteger('created_at').notNullable();
179064
- table.bigInteger('last_run_at');
179065
- table.bigInteger('next_run_at');
179066
- table.integer('run_count').notNullable().defaultTo(0);
179067
- table.integer('failure_count').notNullable().defaultTo(0);
179068
- table.text('last_error');
179069
- table.text('previous_response');
179070
- table.index(['status', 'next_run_at']);
179071
- });
179072
- }
179073
- // Create message_triggers table
179074
- const triggersExist = await knex.schema.hasTable('message_triggers');
179075
- if (!triggersExist) {
179076
- await knex.schema.createTable('message_triggers', table => {
179077
- table.string('id', 36).primary();
179078
- table.string('creator_id', 255).notNullable().index();
179079
- table.string('creator_context', 255);
179080
- table.string('creator_name', 255);
179081
- table.text('description');
179082
- table.text('channels'); // JSON array
179083
- table.text('from_users'); // JSON array
179084
- table.boolean('from_bots').notNullable().defaultTo(false);
179085
- table.text('contains'); // JSON array
179086
- table.text('match_pattern');
179087
- table.string('threads', 20).notNullable().defaultTo('any');
179088
- table.string('workflow', 255).notNullable();
179089
- table.text('inputs'); // JSON
179090
- table.text('output_context'); // JSON
179091
- table.string('status', 20).notNullable().defaultTo('active').index();
179092
- table.boolean('enabled').notNullable().defaultTo(true);
179093
- table.bigInteger('created_at').notNullable();
179094
- });
179095
- }
179096
- // Create scheduler_locks table for distributed locking
179097
- const locksExist = await knex.schema.hasTable('scheduler_locks');
179098
- if (!locksExist) {
179099
- await knex.schema.createTable('scheduler_locks', table => {
179100
- table.string('lock_id', 255).primary();
179101
- table.string('node_id', 255).notNullable();
179102
- table.string('lock_token', 36).notNullable();
179103
- table.bigInteger('acquired_at').notNullable();
179104
- table.bigInteger('expires_at').notNullable();
179105
- });
179106
- }
179107
- }
179108
- getKnex() {
179109
- if (!this.knex) {
179110
- throw new Error('[KnexStore] Not initialized. Call initialize() first.');
179111
- }
179112
- return this.knex;
179113
- }
179114
- // --- CRUD ---
179115
- async create(schedule) {
179116
- const knex = this.getKnex();
179117
- const newSchedule = {
179118
- ...schedule,
179119
- id: (0, uuid_1.v4)(),
179120
- createdAt: Date.now(),
179121
- runCount: 0,
179122
- failureCount: 0,
179123
- status: 'active',
179124
- };
179125
- await knex('schedules').insert(toInsertRow(newSchedule));
179126
- logger_1.logger.info(`[KnexStore] Created schedule ${newSchedule.id} for user ${newSchedule.creatorId}`);
179127
- return newSchedule;
179128
- }
179129
- async importSchedule(schedule) {
179130
- const knex = this.getKnex();
179131
- const existing = await knex('schedules').where('id', schedule.id).first();
179132
- if (existing)
179133
- return; // Already imported (idempotent)
179134
- await knex('schedules').insert(toInsertRow(schedule));
179135
- }
179136
- async get(id) {
179137
- const knex = this.getKnex();
179138
- const row = await knex('schedules').where('id', id).first();
179139
- return row ? fromDbRow(row) : undefined;
179140
- }
179141
- async update(id, patch) {
179142
- const knex = this.getKnex();
179143
- const existing = await knex('schedules').where('id', id).first();
179144
- if (!existing)
179145
- return undefined;
179146
- const current = fromDbRow(existing);
179147
- const updated = { ...current, ...patch, id: current.id };
179148
- const row = toInsertRow(updated);
179149
- // Remove id from update (PK cannot change)
179150
- delete row.id;
179151
- await knex('schedules').where('id', id).update(row);
179152
- return updated;
179153
- }
179154
- async delete(id) {
179155
- const knex = this.getKnex();
179156
- const deleted = await knex('schedules').where('id', id).del();
179157
- if (deleted > 0) {
179158
- logger_1.logger.info(`[KnexStore] Deleted schedule ${id}`);
179159
- return true;
179160
- }
179161
- return false;
179162
- }
179163
- // --- Queries ---
179164
- async getByCreator(creatorId) {
179165
- const knex = this.getKnex();
179166
- const rows = await knex('schedules').where('creator_id', creatorId);
179167
- return rows.map((r) => fromDbRow(r));
179168
- }
179169
- async getActiveSchedules() {
179170
- const knex = this.getKnex();
179171
- const rows = await knex('schedules').where('status', 'active');
179172
- return rows.map((r) => fromDbRow(r));
179173
- }
179174
- async getDueSchedules(now) {
179175
- const ts = now ?? Date.now();
179176
- const knex = this.getKnex();
179177
- // MSSQL uses 1/0 for booleans
179178
- const bFalse = this.driver === 'mssql' ? 0 : false;
179179
- const bTrue = this.driver === 'mssql' ? 1 : true;
179180
- const rows = await knex('schedules')
179181
- .where('status', 'active')
179182
- .andWhere(function () {
179183
- this.where(function () {
179184
- this.where('is_recurring', bFalse)
179185
- .whereNotNull('run_at')
179186
- .where('run_at', '<=', ts);
179187
- }).orWhere(function () {
179188
- this.where('is_recurring', bTrue)
179189
- .whereNotNull('next_run_at')
179190
- .where('next_run_at', '<=', ts);
179191
- });
179192
- });
179193
- return rows.map((r) => fromDbRow(r));
179194
- }
179195
- async findByWorkflow(creatorId, workflowName) {
179196
- const knex = this.getKnex();
179197
- const escaped = workflowName.toLowerCase().replace(/[%_\\]/g, '\\$&');
179198
- const pattern = `%${escaped}%`;
179199
- const rows = await knex('schedules')
179200
- .where('creator_id', creatorId)
179201
- .where('status', 'active')
179202
- .whereRaw("LOWER(workflow) LIKE ? ESCAPE '\\'", [pattern]);
179203
- return rows.map((r) => fromDbRow(r));
179204
- }
179205
- async getAll() {
179206
- const knex = this.getKnex();
179207
- const rows = await knex('schedules');
179208
- return rows.map((r) => fromDbRow(r));
179209
- }
179210
- async getStats() {
179211
- const knex = this.getKnex();
179212
- // MSSQL uses 1/0 for booleans; PostgreSQL/MySQL accept both true/1
179213
- const boolTrue = this.driver === 'mssql' ? '1' : 'true';
179214
- const boolFalse = this.driver === 'mssql' ? '0' : 'false';
179215
- const result = await knex('schedules')
179216
- .select(knex.raw('COUNT(*) as total'), knex.raw("SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) as active"), knex.raw("SUM(CASE WHEN status = 'paused' THEN 1 ELSE 0 END) as paused"), knex.raw("SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed"), knex.raw("SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed"), knex.raw(`SUM(CASE WHEN is_recurring = ${boolTrue} THEN 1 ELSE 0 END) as recurring`), knex.raw(`SUM(CASE WHEN is_recurring = ${boolFalse} THEN 1 ELSE 0 END) as one_time`))
179217
- .first();
179218
- return {
179219
- total: Number(result.total) || 0,
179220
- active: Number(result.active) || 0,
179221
- paused: Number(result.paused) || 0,
179222
- completed: Number(result.completed) || 0,
179223
- failed: Number(result.failed) || 0,
179224
- recurring: Number(result.recurring) || 0,
179225
- oneTime: Number(result.one_time) || 0,
179226
- };
179227
- }
179228
- async validateLimits(creatorId, isRecurring, limits) {
179229
- const knex = this.getKnex();
179230
- if (limits.maxGlobal) {
179231
- const result = await knex('schedules').count('* as cnt').first();
179232
- if (Number(result?.cnt) >= limits.maxGlobal) {
179233
- throw new Error(`Global schedule limit reached (${limits.maxGlobal})`);
179234
- }
179235
- }
179236
- if (limits.maxPerUser) {
179237
- const result = await knex('schedules')
179238
- .where('creator_id', creatorId)
179239
- .count('* as cnt')
179240
- .first();
179241
- if (Number(result?.cnt) >= limits.maxPerUser) {
179242
- throw new Error(`You have reached the maximum number of schedules (${limits.maxPerUser})`);
179243
- }
179244
- }
179245
- if (isRecurring && limits.maxRecurringPerUser) {
179246
- const bTrue = this.driver === 'mssql' ? 1 : true;
179247
- const result = await knex('schedules')
179248
- .where('creator_id', creatorId)
179249
- .where('is_recurring', bTrue)
179250
- .count('* as cnt')
179251
- .first();
179252
- if (Number(result?.cnt) >= limits.maxRecurringPerUser) {
179253
- throw new Error(`You have reached the maximum number of recurring schedules (${limits.maxRecurringPerUser})`);
179254
- }
179255
- }
179256
- }
179257
- // --- HA Distributed Locking (via scheduler_locks table) ---
179258
- async tryAcquireLock(lockId, nodeId, ttlSeconds) {
179259
- const knex = this.getKnex();
179260
- const now = Date.now();
179261
- const expiresAt = now + ttlSeconds * 1000;
179262
- const token = (0, uuid_1.v4)();
179263
- // Step 1: Try to claim an existing expired lock
179264
- const updated = await knex('scheduler_locks')
179265
- .where('lock_id', lockId)
179266
- .where('expires_at', '<', now)
179267
- .update({
179268
- node_id: nodeId,
179269
- lock_token: token,
179270
- acquired_at: now,
179271
- expires_at: expiresAt,
179272
- });
179273
- if (updated > 0)
179274
- return token;
179275
- // Step 2: Try to INSERT a new lock row
179276
- try {
179277
- await knex('scheduler_locks').insert({
179278
- lock_id: lockId,
179279
- node_id: nodeId,
179280
- lock_token: token,
179281
- acquired_at: now,
179282
- expires_at: expiresAt,
179283
- });
179284
- return token;
179285
- }
179286
- catch {
179287
- // Unique constraint violation — another node holds the lock
179288
- return null;
179289
- }
179290
- }
179291
- async releaseLock(lockId, lockToken) {
179292
- const knex = this.getKnex();
179293
- await knex('scheduler_locks').where('lock_id', lockId).where('lock_token', lockToken).del();
179294
- }
179295
- async renewLock(lockId, lockToken, ttlSeconds) {
179296
- const knex = this.getKnex();
179297
- const now = Date.now();
179298
- const expiresAt = now + ttlSeconds * 1000;
179299
- const updated = await knex('scheduler_locks')
179300
- .where('lock_id', lockId)
179301
- .where('lock_token', lockToken)
179302
- .update({ acquired_at: now, expires_at: expiresAt });
179303
- return updated > 0;
179304
- }
179305
- async flush() {
179306
- // No-op for server-based backends
179307
- }
179308
- // --- Message Trigger CRUD ---
179309
- async createTrigger(trigger) {
179310
- const knex = this.getKnex();
179311
- const newTrigger = {
179312
- ...trigger,
179313
- id: (0, uuid_1.v4)(),
179314
- createdAt: Date.now(),
179315
- };
179316
- await knex('message_triggers').insert(toTriggerInsertRow(newTrigger));
179317
- logger_1.logger.info(`[KnexStore] Created trigger ${newTrigger.id} for user ${newTrigger.creatorId}`);
179318
- return newTrigger;
179319
- }
179320
- async getTrigger(id) {
179321
- const knex = this.getKnex();
179322
- const row = await knex('message_triggers').where('id', id).first();
179323
- return row ? fromTriggerRow(row) : undefined;
179324
- }
179325
- async updateTrigger(id, patch) {
179326
- const knex = this.getKnex();
179327
- const existing = await knex('message_triggers').where('id', id).first();
179328
- if (!existing)
179329
- return undefined;
179330
- const current = fromTriggerRow(existing);
179331
- const updated = {
179332
- ...current,
179333
- ...patch,
179334
- id: current.id,
179335
- createdAt: current.createdAt,
179336
- };
179337
- const row = toTriggerInsertRow(updated);
179338
- delete row.id;
179339
- await knex('message_triggers').where('id', id).update(row);
179340
- return updated;
179341
- }
179342
- async deleteTrigger(id) {
179343
- const knex = this.getKnex();
179344
- const deleted = await knex('message_triggers').where('id', id).del();
179345
- if (deleted > 0) {
179346
- logger_1.logger.info(`[KnexStore] Deleted trigger ${id}`);
179347
- return true;
179348
- }
179349
- return false;
179350
- }
179351
- async getTriggersByCreator(creatorId) {
179352
- const knex = this.getKnex();
179353
- const rows = await knex('message_triggers').where('creator_id', creatorId);
179354
- return rows.map((r) => fromTriggerRow(r));
179355
- }
179356
- async getActiveTriggers() {
179357
- const knex = this.getKnex();
179358
- const rows = await knex('message_triggers')
179359
- .where('status', 'active')
179360
- .where('enabled', this.driver === 'mssql' ? 1 : true);
179361
- return rows.map((r) => fromTriggerRow(r));
179362
- }
179363
- }
179364
- exports.KnexStoreBackend = KnexStoreBackend;
179365
-
179366
-
179367
177794
  /***/ }),
179368
177795
 
179369
177796
  /***/ 83864:
@@ -181773,6 +180200,10 @@ exports.configSchema = {
181773
180200
  $ref: '#/definitions/AgentProtocolConfig',
181774
180201
  description: 'Agent protocol (A2A) server configuration',
181775
180202
  },
180203
+ task_tracking: {
180204
+ type: 'boolean',
180205
+ 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`.',
180206
+ },
181776
180207
  },
181777
180208
  required: ['version'],
181778
180209
  patternProperties: {
@@ -182508,7 +180939,7 @@ exports.configSchema = {
182508
180939
  description: 'Arguments/inputs for the workflow',
182509
180940
  },
182510
180941
  overrides: {
182511
- $ref: '#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-13844-28438-src_types_config.ts-0-55681%3E%3E',
180942
+ $ref: '#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-13844-28438-src_types_config.ts-0-55916%3E%3E',
182512
180943
  description: 'Override specific step configurations in the workflow',
182513
180944
  },
182514
180945
  output_mapping: {
@@ -182524,7 +180955,7 @@ exports.configSchema = {
182524
180955
  description: 'Config file path - alternative to workflow ID (loads a Visor config file as workflow)',
182525
180956
  },
182526
180957
  workflow_overrides: {
182527
- $ref: '#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-13844-28438-src_types_config.ts-0-55681%3E%3E',
180958
+ $ref: '#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-13844-28438-src_types_config.ts-0-55916%3E%3E',
182528
180959
  description: 'Alias for overrides - workflow step overrides (backward compatibility)',
182529
180960
  },
182530
180961
  ref: {
@@ -183222,7 +181653,7 @@ exports.configSchema = {
183222
181653
  description: 'Custom output name (defaults to workflow name)',
183223
181654
  },
183224
181655
  overrides: {
183225
- $ref: '#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-13844-28438-src_types_config.ts-0-55681%3E%3E',
181656
+ $ref: '#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-13844-28438-src_types_config.ts-0-55916%3E%3E',
183226
181657
  description: 'Step overrides',
183227
181658
  },
183228
181659
  output_mapping: {
@@ -183237,13 +181668,13 @@ exports.configSchema = {
183237
181668
  '^x-': {},
183238
181669
  },
183239
181670
  },
183240
- 'Record<string,Partial<interface-src_types_config.ts-13844-28438-src_types_config.ts-0-55681>>': {
181671
+ 'Record<string,Partial<interface-src_types_config.ts-13844-28438-src_types_config.ts-0-55916>>': {
183241
181672
  type: 'object',
183242
181673
  additionalProperties: {
183243
- $ref: '#/definitions/Partial%3Cinterface-src_types_config.ts-13844-28438-src_types_config.ts-0-55681%3E',
181674
+ $ref: '#/definitions/Partial%3Cinterface-src_types_config.ts-13844-28438-src_types_config.ts-0-55916%3E',
183244
181675
  },
183245
181676
  },
183246
- 'Partial<interface-src_types_config.ts-13844-28438-src_types_config.ts-0-55681>': {
181677
+ 'Partial<interface-src_types_config.ts-13844-28438-src_types_config.ts-0-55916>': {
183247
181678
  type: 'object',
183248
181679
  additionalProperties: false,
183249
181680
  },
@@ -190673,35 +189104,6 @@ class OutputFormatters {
190673
189104
  exports.OutputFormatters = OutputFormatters;
190674
189105
 
190675
189106
 
190676
- /***/ }),
190677
-
190678
- /***/ 93866:
190679
- /***/ ((__unused_webpack_module, exports) => {
190680
-
190681
- "use strict";
190682
-
190683
- Object.defineProperty(exports, "__esModule", ({ value: true }));
190684
- exports.DefaultPolicyEngine = void 0;
190685
- /**
190686
- * Default (no-op) policy engine — always allows everything.
190687
- * Used when no enterprise license is present or policy is disabled.
190688
- */
190689
- class DefaultPolicyEngine {
190690
- async initialize(_config) { }
190691
- async evaluateCheckExecution(_checkId, _checkConfig) {
190692
- return { allowed: true };
190693
- }
190694
- async evaluateToolInvocation(_serverName, _methodName, _transport) {
190695
- return { allowed: true };
190696
- }
190697
- async evaluateCapabilities(_checkId, _capabilities) {
190698
- return { allowed: true };
190699
- }
190700
- async shutdown() { }
190701
- }
190702
- exports.DefaultPolicyEngine = DefaultPolicyEngine;
190703
-
190704
-
190705
189107
  /***/ }),
190706
189108
 
190707
189109
  /***/ 96611:
@@ -208064,6 +206466,8 @@ class Scheduler {
208064
206466
  outputAdapters = new Map();
208065
206467
  executionContext = {};
208066
206468
  contextEnricher;
206469
+ taskStore;
206470
+ configPath;
208067
206471
  // HA fields
208068
206472
  haConfig;
208069
206473
  nodeId;
@@ -208089,6 +206493,11 @@ class Scheduler {
208089
206493
  setEngine(engine) {
208090
206494
  this.engine = engine;
208091
206495
  }
206496
+ /** Set shared task store for execution tracking. */
206497
+ setTaskStore(store, configPath) {
206498
+ this.taskStore = store;
206499
+ this.configPath = configPath;
206500
+ }
208092
206501
  /**
208093
206502
  * Set the execution context (e.g., Slack client) for workflow executions
208094
206503
  */
@@ -208676,7 +207085,7 @@ class Scheduler {
208676
207085
  // Use common preparation helper
208677
207086
  const { engine: runEngine, config: cfgForRun } = this.prepareExecution(schedule);
208678
207087
  // Execute the workflow
208679
- await runEngine.executeChecks({
207088
+ const schedExecFn = () => runEngine.executeChecks({
208680
207089
  checks: checksToRun,
208681
207090
  showDetails: true,
208682
207091
  outputFormat: 'json',
@@ -208685,6 +207094,20 @@ class Scheduler {
208685
207094
  debug: process.env.VISOR_DEBUG === 'true',
208686
207095
  inputs: schedule.workflowInputs,
208687
207096
  });
207097
+ if (this.taskStore) {
207098
+ const { trackExecution } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(7628)));
207099
+ await trackExecution({
207100
+ taskStore: this.taskStore,
207101
+ source: 'scheduler',
207102
+ workflowId: schedule.workflow,
207103
+ configPath: this.configPath,
207104
+ messageText: `Scheduled: ${schedule.workflow} (${schedule.id})`,
207105
+ metadata: { schedule_id: schedule.id, is_recurring: schedule.isRecurring },
207106
+ }, schedExecFn);
207107
+ }
207108
+ else {
207109
+ await schedExecFn();
207110
+ }
208688
207111
  return { message: 'Workflow completed', workflow: schedule.workflow };
208689
207112
  }
208690
207113
  /**
@@ -208797,7 +207220,7 @@ Please provide an updated response based on the reminder above. You may referenc
208797
207220
  const { engine: runEngine, config: cfgForRun, responseRef, } = this.prepareExecution(schedule, reminderText);
208798
207221
  try {
208799
207222
  // Execute ALL checks - let the visor engine route based on config
208800
- await runEngine.executeChecks({
207223
+ const reminderExecFn = () => runEngine.executeChecks({
208801
207224
  checks: allChecks,
208802
207225
  showDetails: true,
208803
207226
  outputFormat: 'json',
@@ -208805,6 +207228,20 @@ Please provide an updated response based on the reminder above. You may referenc
208805
207228
  webhookContext: { webhookData, eventType: 'schedule' },
208806
207229
  debug: process.env.VISOR_DEBUG === 'true',
208807
207230
  });
207231
+ if (this.taskStore) {
207232
+ const { trackExecution } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(7628)));
207233
+ await trackExecution({
207234
+ taskStore: this.taskStore,
207235
+ source: 'scheduler',
207236
+ workflowId: schedule.workflow,
207237
+ configPath: this.configPath,
207238
+ messageText: reminderText || `Reminder: ${schedule.id}`,
207239
+ metadata: { schedule_id: schedule.id, is_recurring: schedule.isRecurring },
207240
+ }, reminderExecFn);
207241
+ }
207242
+ else {
207243
+ await reminderExecFn();
207244
+ }
208808
207245
  // The visor pipeline handles output via frontends (Slack, etc.)
208809
207246
  // We return success - the actual response was posted by the pipeline
208810
207247
  // Save captured response for recurring reminders (previousResponse feature)
@@ -211705,6 +210142,8 @@ class SlackSocketRunner {
211705
210142
  heartbeatTimer;
211706
210143
  lastPong = 0;
211707
210144
  closing = false; // prevent duplicate reconnects
210145
+ taskStore;
210146
+ configPath;
211708
210147
  constructor(engine, cfg, opts) {
211709
210148
  const app = opts.appToken || process.env.SLACK_APP_TOKEN || '';
211710
210149
  if (!app)
@@ -211727,6 +210166,11 @@ class SlackSocketRunner {
211727
210166
  this.cfg = cfg;
211728
210167
  this.initMessageTriggersFromConfig();
211729
210168
  }
210169
+ /** Set shared task store for execution tracking. */
210170
+ setTaskStore(store, configPath) {
210171
+ this.taskStore = store;
210172
+ this.configPath = configPath;
210173
+ }
211730
210174
  /** Hot-swap the config used for future requests (does not affect in-flight ones). */
211731
210175
  updateConfig(cfg) {
211732
210176
  this.cfg = cfg;
@@ -211845,6 +210289,8 @@ class SlackSocketRunner {
211845
210289
  defaultTimezone: schedulerCfg?.default_timezone,
211846
210290
  });
211847
210291
  this.genericScheduler.setEngine(this.engine);
210292
+ if (this.taskStore)
210293
+ this.genericScheduler.setTaskStore(this.taskStore, this.configPath);
211848
210294
  // Pass Slack client to scheduler so it can inject into workflow executions
211849
210295
  this.genericScheduler.setExecutionContext({
211850
210296
  slack: this.client,
@@ -212400,7 +210846,7 @@ class SlackSocketRunner {
212400
210846
  }
212401
210847
  }
212402
210848
  // Cold run (no snapshot)
212403
- await runEngine.executeChecks({
210849
+ const execFn = () => runEngine.executeChecks({
212404
210850
  checks: allChecks,
212405
210851
  showDetails: true,
212406
210852
  outputFormat: 'json',
@@ -212408,6 +210854,24 @@ class SlackSocketRunner {
212408
210854
  webhookContext: { webhookData: map, eventType: 'manual' },
212409
210855
  debug: process.env.VISOR_DEBUG === 'true',
212410
210856
  });
210857
+ if (this.taskStore) {
210858
+ const { trackExecution } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(7628)));
210859
+ await trackExecution({
210860
+ taskStore: this.taskStore,
210861
+ source: 'slack',
210862
+ workflowId: allChecks.join(','),
210863
+ configPath: this.configPath,
210864
+ messageText: String(ev.text || 'Slack message'),
210865
+ metadata: {
210866
+ slack_channel: channelId,
210867
+ slack_thread_ts: threadTs,
210868
+ slack_user: userId,
210869
+ },
210870
+ }, execFn);
210871
+ }
210872
+ else {
210873
+ await execFn();
210874
+ }
212411
210875
  });
212412
210876
  }
212413
210877
  finally {
@@ -212540,7 +211004,7 @@ class SlackSocketRunner {
212540
211004
  'slack.thread_ts': threadTs || ts,
212541
211005
  'slack.user_id': user,
212542
211006
  }, async () => {
212543
- await runEngine.executeChecks({
211007
+ const triggerExecFn = () => runEngine.executeChecks({
212544
211008
  checks: checksToRun,
212545
211009
  showDetails: true,
212546
211010
  outputFormat: 'json',
@@ -212549,6 +211013,25 @@ class SlackSocketRunner {
212549
211013
  debug: process.env.VISOR_DEBUG === 'true',
212550
211014
  inputs: trigger.inputs,
212551
211015
  });
211016
+ if (this.taskStore) {
211017
+ const { trackExecution } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(7628)));
211018
+ await trackExecution({
211019
+ taskStore: this.taskStore,
211020
+ source: 'slack',
211021
+ workflowId: trigger.workflow || checksToRun.join(','),
211022
+ configPath: this.configPath,
211023
+ messageText: String(ev.text || `Trigger: ${id}`),
211024
+ metadata: {
211025
+ slack_channel: channel,
211026
+ slack_thread_ts: threadTs || ts,
211027
+ slack_user: user,
211028
+ trigger_id: id,
211029
+ },
211030
+ }, triggerExecFn);
211031
+ }
211032
+ else {
211033
+ await triggerExecFn();
211034
+ }
212552
211035
  });
212553
211036
  logger_1.logger.info(`[SlackSocket] Message trigger '${id}' workflow completed`);
212554
211037
  }
@@ -213400,7 +211883,7 @@ class StateMachineExecutionEngine {
213400
211883
  try {
213401
211884
  logger_1.logger.debug(`[PolicyEngine] Loading enterprise policy engine (engine=${configWithTagFilter.policy.engine})`);
213402
211885
  // @ts-ignore — enterprise/ may not exist in OSS builds (caught at runtime)
213403
- const { loadEnterprisePolicyEngine } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(87068)));
211886
+ const { loadEnterprisePolicyEngine } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(7065)));
213404
211887
  context.policyEngine = await loadEnterprisePolicyEngine(configWithTagFilter.policy);
213405
211888
  logger_1.logger.debug(`[PolicyEngine] Initialized: ${context.policyEngine?.constructor?.name || 'unknown'}`);
213406
211889
  }
@@ -223711,7 +222194,7 @@ async function initTelemetry(opts = {}) {
223711
222194
  const path = __nccwpck_require__(16928);
223712
222195
  const outDir = opts.file?.dir ||
223713
222196
  process.env.VISOR_TRACE_DIR ||
223714
- path.join(process.cwd(), 'output', 'traces');
222197
+ __nccwpck_require__.ab + "traces";
223715
222198
  fs.mkdirSync(outDir, { recursive: true });
223716
222199
  const ts = new Date().toISOString().replace(/[:.]/g, '-');
223717
222200
  process.env.VISOR_FALLBACK_TRACE_FILE = path.join(outDir, `run-${ts}.ndjson`);
@@ -223916,7 +222399,7 @@ async function shutdownTelemetry() {
223916
222399
  if (process.env.VISOR_TRACE_REPORT === 'true') {
223917
222400
  const fs = __nccwpck_require__(79896);
223918
222401
  const path = __nccwpck_require__(16928);
223919
- const outDir = process.env.VISOR_TRACE_DIR || path.join(process.cwd(), 'output', 'traces');
222402
+ const outDir = process.env.VISOR_TRACE_DIR || __nccwpck_require__.ab + "traces";
223920
222403
  if (!fs.existsSync(outDir))
223921
222404
  fs.mkdirSync(outDir, { recursive: true });
223922
222405
  const ts = new Date().toISOString().replace(/[:.]/g, '-');
@@ -224289,6 +222772,7 @@ exports.getVisorRunAttributes = getVisorRunAttributes;
224289
222772
  exports.__getOrCreateNdjsonPath = __getOrCreateNdjsonPath;
224290
222773
  exports._appendRunMarker = _appendRunMarker;
224291
222774
  const lazy_otel_1 = __nccwpck_require__(21084);
222775
+ const instance_id_1 = __nccwpck_require__(89942);
224292
222776
  function getTracer() {
224293
222777
  return lazy_otel_1.trace.getTracer('visor');
224294
222778
  }
@@ -224396,6 +222880,7 @@ function getVisorRunAttributes() {
224396
222880
  if (commitFull) {
224397
222881
  attrs['visor.commit.sha'] = commitFull;
224398
222882
  }
222883
+ attrs['visor.instance_id'] = (0, instance_id_1.getInstanceId)();
224399
222884
  return attrs;
224400
222885
  }
224401
222886
  // Internal helper for tests: write a minimal run marker to NDJSON when using file sink
@@ -224415,7 +222900,7 @@ function __getOrCreateNdjsonPath() {
224415
222900
  fs.mkdirSync(dir, { recursive: true });
224416
222901
  return __ndjsonPath;
224417
222902
  }
224418
- const outDir = process.env.VISOR_TRACE_DIR || path.join(process.cwd(), 'output', 'traces');
222903
+ const outDir = process.env.VISOR_TRACE_DIR || __nccwpck_require__.ab + "traces";
224419
222904
  if (!fs.existsSync(outDir))
224420
222905
  fs.mkdirSync(outDir, { recursive: true });
224421
222906
  if (!__ndjsonPath) {
@@ -228908,10 +227393,43 @@ function validateTestsDoc(doc) {
228908
227393
  /***/ }),
228909
227394
 
228910
227395
  /***/ 20881:
228911
- /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
227396
+ /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
228912
227397
 
228913
227398
  "use strict";
228914
227399
 
227400
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
227401
+ if (k2 === undefined) k2 = k;
227402
+ var desc = Object.getOwnPropertyDescriptor(m, k);
227403
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
227404
+ desc = { enumerable: true, get: function() { return m[k]; } };
227405
+ }
227406
+ Object.defineProperty(o, k2, desc);
227407
+ }) : (function(o, m, k, k2) {
227408
+ if (k2 === undefined) k2 = k;
227409
+ o[k2] = m[k];
227410
+ }));
227411
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
227412
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
227413
+ }) : function(o, v) {
227414
+ o["default"] = v;
227415
+ });
227416
+ var __importStar = (this && this.__importStar) || (function () {
227417
+ var ownKeys = function(o) {
227418
+ ownKeys = Object.getOwnPropertyNames || function (o) {
227419
+ var ar = [];
227420
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
227421
+ return ar;
227422
+ };
227423
+ return ownKeys(o);
227424
+ };
227425
+ return function (mod) {
227426
+ if (mod && mod.__esModule) return mod;
227427
+ var result = {};
227428
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
227429
+ __setModuleDefault(result, mod);
227430
+ return result;
227431
+ };
227432
+ })();
228915
227433
  Object.defineProperty(exports, "__esModule", ({ value: true }));
228916
227434
  exports.TuiChatRunner = void 0;
228917
227435
  exports.startChatTUI = startChatTUI;
@@ -228939,6 +227457,8 @@ class TuiChatRunner {
228939
227457
  messageCounter = 0;
228940
227458
  isRunning = false;
228941
227459
  currentExecution;
227460
+ taskStore;
227461
+ configPath;
228942
227462
  constructor(config) {
228943
227463
  this.cfg = config.config;
228944
227464
  this.stateManager = config.stateManager ?? new chat_state_1.ChatStateManager();
@@ -228947,6 +227467,11 @@ class TuiChatRunner {
228947
227467
  // Set as global state manager
228948
227468
  (0, chat_state_1.setChatStateManager)(this.stateManager);
228949
227469
  }
227470
+ /** Set shared task store for execution tracking. */
227471
+ setTaskStore(store, configPath) {
227472
+ this.taskStore = store;
227473
+ this.configPath = configPath;
227474
+ }
228950
227475
  async start() {
228951
227476
  // Create ChatTUI
228952
227477
  this.chatTui = new chat_tui_1.ChatTUI({
@@ -229093,16 +227618,28 @@ class TuiChatRunner {
229093
227618
  'tui.message_id': messageId,
229094
227619
  }, async () => {
229095
227620
  try {
229096
- await runEngine.executeChecks({
227621
+ const tuiExecFn = () => runEngine.executeChecks({
229097
227622
  checks: allChecks,
229098
227623
  showDetails: true,
229099
227624
  outputFormat: 'json',
229100
227625
  config: cfgForRun,
229101
227626
  webhookContext: { webhookData, eventType: 'manual' },
229102
227627
  debug: this.debug,
229103
- // Pass conversation directly in options for TUI mode
229104
227628
  conversation: ctx.conversation,
229105
227629
  });
227630
+ if (this.taskStore) {
227631
+ const { trackExecution } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(7628)));
227632
+ await trackExecution({
227633
+ taskStore: this.taskStore,
227634
+ source: 'tui',
227635
+ workflowId: allChecks.join(','),
227636
+ configPath: this.configPath,
227637
+ messageText: message,
227638
+ }, tuiExecFn);
227639
+ }
227640
+ else {
227641
+ await tuiExecFn();
227642
+ }
229106
227643
  }
229107
227644
  catch (error) {
229108
227645
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -234087,6 +232624,32 @@ function generateShortHumanId() {
234087
232624
  }
234088
232625
 
234089
232626
 
232627
+ /***/ }),
232628
+
232629
+ /***/ 89942:
232630
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
232631
+
232632
+ "use strict";
232633
+
232634
+ /**
232635
+ * Visor instance ID — unique per process lifetime.
232636
+ *
232637
+ * Used to identify which visor instance created/owns a task,
232638
+ * especially in multi-node environments.
232639
+ */
232640
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
232641
+ exports.getInstanceId = getInstanceId;
232642
+ const human_id_1 = __nccwpck_require__(30920);
232643
+ let _instanceId;
232644
+ /** Get or generate the visor instance ID for this process. */
232645
+ function getInstanceId() {
232646
+ if (!_instanceId) {
232647
+ _instanceId = (0, human_id_1.generateHumanId)();
232648
+ }
232649
+ return _instanceId;
232650
+ }
232651
+
232652
+
234090
232653
  /***/ }),
234091
232654
 
234092
232655
  /***/ 91784:
@@ -238793,6 +237356,22 @@ class WorkflowRegistry {
238793
237356
  exports.WorkflowRegistry = WorkflowRegistry;
238794
237357
 
238795
237358
 
237359
+ /***/ }),
237360
+
237361
+ /***/ 7065:
237362
+ /***/ ((module) => {
237363
+
237364
+ module.exports = eval("require")("./enterprise/loader");
237365
+
237366
+
237367
+ /***/ }),
237368
+
237369
+ /***/ 71370:
237370
+ /***/ ((module) => {
237371
+
237372
+ module.exports = eval("require")("./enterprise/policy/policy-input-builder");
237373
+
237374
+
238796
237375
  /***/ }),
238797
237376
 
238798
237377
  /***/ 18327:
@@ -263029,7 +261608,8 @@ var init_search = __esm({
263029
261608
  session: "--session",
263030
261609
  timeout: "--timeout",
263031
261610
  language: "--language",
263032
- format: "--format"
261611
+ format: "--format",
261612
+ lsp: "--lsp"
263033
261613
  };
263034
261614
  }
263035
261615
  });
@@ -263265,7 +261845,8 @@ var init_extract = __esm({
263265
261845
  allowTests: "--allow-tests",
263266
261846
  contextLines: "--context",
263267
261847
  format: "--format",
263268
- inputFile: "--input-file"
261848
+ inputFile: "--input-file",
261849
+ lsp: "--lsp"
263269
261850
  };
263270
261851
  }
263271
261852
  });
@@ -288142,7 +286723,14 @@ function parseTargets(targets) {
288142
286723
  }
288143
286724
  function parseAndResolvePaths(pathStr, cwd) {
288144
286725
  if (!pathStr) return [];
288145
- const paths = pathStr.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
286726
+ let paths = pathStr.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
286727
+ paths = paths.flatMap((p) => {
286728
+ if (!/\s/.test(p)) return [p];
286729
+ const parts = p.split(/\s+/).filter(Boolean);
286730
+ if (parts.length <= 1) return [p];
286731
+ const allLookLikePaths = parts.every((part) => /[/\\]/.test(part) || /\.\w+/.test(part));
286732
+ return allLookLikePaths ? parts : [p];
286733
+ });
288146
286734
  return paths.map((p) => {
288147
286735
  if ((0, import_path5.isAbsolute)(p)) {
288148
286736
  return p;
@@ -307805,6 +306393,7 @@ var init_parser2 = __esm({
307805
306393
  { ALT: () => this.CONSUME(Identifier) },
307806
306394
  { ALT: () => this.CONSUME(Text) },
307807
306395
  { ALT: () => this.CONSUME(NumberLiteral) },
306396
+ { ALT: () => this.CONSUME(ColorValue) },
307808
306397
  // Note: RoundOpen and RoundClose (parentheses) are NOT allowed in unquoted labels
307809
306398
  // to match Mermaid's behavior - use quoted labels like ["text (with parens)"] instead
307810
306399
  // Allow HTML-like tags (e.g., <br/>) inside labels
@@ -307896,6 +306485,7 @@ var init_parser2 = __esm({
307896
306485
  { ALT: () => this.CONSUME(Identifier) },
307897
306486
  { ALT: () => this.CONSUME(Text) },
307898
306487
  { ALT: () => this.CONSUME(NumberLiteral) },
306488
+ { ALT: () => this.CONSUME(ColorValue) },
307899
306489
  // Allow HTML-like angle brackets and slashes for <br/>, <i>, etc.
307900
306490
  { ALT: () => this.CONSUME(AngleLess) },
307901
306491
  { ALT: () => this.CONSUME(AngleOpen) },
@@ -309071,13 +307661,24 @@ function mapFlowchartParserError(err, text) {
309071
307661
  length: len
309072
307662
  };
309073
307663
  }
309074
- if (tokType === "QuotedString" || tokType === "SquareOpen" || tokType === "SquareClose") {
307664
+ if (tokType === "QuotedString" || tokType === "SquareOpen" || tokType === "SquareClose" || tokType === "DiamondOpen" || tokType === "DiamondClose") {
309075
307665
  const context = err?.context;
309076
307666
  const inLinkRule = context?.ruleStack?.includes("linkTextInline") || context?.ruleStack?.includes("link") || false;
309077
307667
  const lineContent = allLines[Math.max(0, line - 1)] || "";
309078
307668
  const beforeQuote = lineContent.slice(0, Math.max(0, column - 1));
309079
307669
  const hasLinkBefore = beforeQuote.match(/--\s*$|==\s*$|-\.\s*$|-\.-\s*$|\[\s*$/);
309080
307670
  if (inLinkRule || hasLinkBefore) {
307671
+ if (tokType === "DiamondOpen" || tokType === "DiamondClose") {
307672
+ return {
307673
+ line,
307674
+ column,
307675
+ severity: "error",
307676
+ code: "FL-EDGE-LABEL-CURLY-IN-PIPES",
307677
+ message: "Curly braces { } are not supported inside pipe-delimited edge labels.",
307678
+ hint: "Use HTML entities &#123; and &#125; inside |...|, e.g., --|Resolve $&#123;VAR&#125;|-->",
307679
+ length: len
307680
+ };
307681
+ }
309081
307682
  if (tokType === "SquareOpen" || tokType === "SquareClose") {
309082
307683
  return {
309083
307684
  line,
@@ -309139,6 +307740,17 @@ function mapFlowchartParserError(err, text) {
309139
307740
  length: len
309140
307741
  };
309141
307742
  }
307743
+ if (tokType === "DiamondOpen" || tokType === "DiamondClose") {
307744
+ return {
307745
+ line,
307746
+ column,
307747
+ severity: "error",
307748
+ code: "FL-LABEL-CURLY-IN-UNQUOTED",
307749
+ message: "Curly braces are not supported inside unquoted node labels.",
307750
+ hint: "Use &#123; and &#125; for literal braces, e.g., C[Substitute &#123;params&#125;].",
307751
+ length: len
307752
+ };
307753
+ }
309142
307754
  {
309143
307755
  const caret0 = Math.max(0, column - 1);
309144
307756
  const openIdx = lineStr.lastIndexOf("[", caret0);
@@ -309180,9 +307792,20 @@ function mapFlowchartParserError(err, text) {
309180
307792
  length: len
309181
307793
  };
309182
307794
  }
307795
+ if (seg.includes("{") || seg.includes("}")) {
307796
+ return {
307797
+ line,
307798
+ column,
307799
+ severity: "error",
307800
+ code: "FL-LABEL-CURLY-IN-UNQUOTED",
307801
+ message: "Curly braces are not supported inside unquoted node labels.",
307802
+ hint: "Use &#123; and &#125; for literal braces, e.g., C[Substitute &#123;params&#125;].",
307803
+ length: len
307804
+ };
307805
+ }
309183
307806
  }
309184
307807
  }
309185
- if (tokType === "QuotedString" || tokType === "SquareOpen" || tokType === "SquareClose") {
307808
+ if (tokType === "QuotedString") {
309186
307809
  return {
309187
307810
  line,
309188
307811
  column,
@@ -309193,6 +307816,17 @@ function mapFlowchartParserError(err, text) {
309193
307816
  length: len
309194
307817
  };
309195
307818
  }
307819
+ if (tokType === "SquareOpen" || tokType === "SquareClose") {
307820
+ return {
307821
+ line,
307822
+ column,
307823
+ severity: "error",
307824
+ code: "FL-LABEL-BRACKET-IN-UNQUOTED",
307825
+ message: "Square brackets are not supported inside unquoted node labels.",
307826
+ hint: 'Use &#91; and &#93; for literal brackets or wrap the label in quotes, e.g., C["bind_paths[]"]',
307827
+ length: len
307828
+ };
307829
+ }
309196
307830
  const q = findInnerQuoteIssue("[");
309197
307831
  if (q?.kind === "escaped") {
309198
307832
  return { line, column: q.column, severity: "error", code: "FL-LABEL-ESCAPED-QUOTE", message: 'Escaped quotes (\\") in node labels are not supported by Mermaid. Use &quot; instead.', hint: 'Prefer "He said &quot;Hi&quot;".', length: 2 };
@@ -309203,7 +307837,7 @@ function mapFlowchartParserError(err, text) {
309203
307837
  return { line, column, severity: "error", code: "FL-NODE-UNCLOSED-BRACKET", message: "Unclosed '['. Add a matching ']' before the arrow or newline.", hint: "Example: A[Label] --> B", length: 1 };
309204
307838
  }
309205
307839
  if (expecting(err, "RoundClose")) {
309206
- if (tokType === "QuotedString" || tokType === "SquareOpen" || tokType === "SquareClose") {
307840
+ if (tokType === "QuotedString") {
309207
307841
  return {
309208
307842
  line,
309209
307843
  column,
@@ -309214,6 +307848,17 @@ function mapFlowchartParserError(err, text) {
309214
307848
  length: len
309215
307849
  };
309216
307850
  }
307851
+ if (tokType === "SquareOpen" || tokType === "SquareClose") {
307852
+ return {
307853
+ line,
307854
+ column,
307855
+ severity: "error",
307856
+ code: "FL-LABEL-BRACKET-IN-UNQUOTED",
307857
+ message: "Square brackets are not supported inside unquoted node labels.",
307858
+ hint: 'Use &#91; and &#93; for literal brackets or wrap the label in quotes, e.g., C["bind_paths[]"]',
307859
+ length: len
307860
+ };
307861
+ }
309217
307862
  {
309218
307863
  const caret0 = Math.max(0, column - 1);
309219
307864
  const openIdx = lineStr.lastIndexOf("(", caret0);
@@ -309242,7 +307887,7 @@ function mapFlowchartParserError(err, text) {
309242
307887
  return { line, column, severity: "error", code: "FL-NODE-UNCLOSED-BRACKET", message: "Unclosed '('. Add a matching ')'.", hint: "Example: B(Label)", length: 1 };
309243
307888
  }
309244
307889
  if (expecting(err, "DiamondClose")) {
309245
- if (tokType === "QuotedString" || tokType === "SquareOpen" || tokType === "SquareClose") {
307890
+ if (tokType === "QuotedString") {
309246
307891
  return {
309247
307892
  line,
309248
307893
  column,
@@ -309253,6 +307898,17 @@ function mapFlowchartParserError(err, text) {
309253
307898
  length: len
309254
307899
  };
309255
307900
  }
307901
+ if (tokType === "SquareOpen" || tokType === "SquareClose") {
307902
+ return {
307903
+ line,
307904
+ column,
307905
+ severity: "error",
307906
+ code: "FL-LABEL-BRACKET-IN-UNQUOTED",
307907
+ message: "Square brackets are not supported inside unquoted node labels.",
307908
+ hint: 'Use &#91; and &#93; for literal brackets or wrap the label in quotes, e.g., C["bind_paths[]"]',
307909
+ length: len
307910
+ };
307911
+ }
309256
307912
  {
309257
307913
  const caret0 = Math.max(0, column - 1);
309258
307914
  const openIdx = lineStr.lastIndexOf("{", caret0);
@@ -310095,7 +308751,7 @@ function validateFlowchart(text, options = {}) {
310095
308751
  const byLine = /* @__PURE__ */ new Map();
310096
308752
  const collect = (arr) => {
310097
308753
  for (const e of arr || []) {
310098
- if (e && (e.code === "FL-LABEL-PARENS-UNQUOTED" || e.code === "FL-LABEL-AT-IN-UNQUOTED" || e.code === "FL-LABEL-QUOTE-IN-UNQUOTED" || e.code === "FL-LABEL-SLASH-UNQUOTED")) {
308754
+ if (e && (e.code === "FL-LABEL-PARENS-UNQUOTED" || e.code === "FL-LABEL-AT-IN-UNQUOTED" || e.code === "FL-LABEL-QUOTE-IN-UNQUOTED" || e.code === "FL-LABEL-SLASH-UNQUOTED" || e.code === "FL-LABEL-CURLY-IN-UNQUOTED" || e.code === "FL-LABEL-BRACKET-IN-UNQUOTED")) {
310099
308755
  const ln = e.line ?? 0;
310100
308756
  const col = e.column ?? 1;
310101
308757
  const list = byLine.get(ln) || [];
@@ -310177,6 +308833,9 @@ function validateFlowchart(text, options = {}) {
310177
308833
  const hasParens = seg.includes("(") || seg.includes(")");
310178
308834
  const hasAt = seg.includes("@");
310179
308835
  const hasQuote = seg.includes('"');
308836
+ const hasCurly = seg.includes("{") || seg.includes("}");
308837
+ const hasBracket = seg.includes("[") || seg.includes("]");
308838
+ const isDoubleSquare = raw.slice(i, i + 2) === "[[" && raw.slice(j - 2, j) === "]]";
310180
308839
  const isSingleQuoted = /^'[^]*'$/.test(trimmed);
310181
308840
  const hasLeadingSlash = lsp === "/" || lsp === "\\";
310182
308841
  if (!covered && !isQuoted && !isParenWrapped && hasParens) {
@@ -310194,6 +308853,16 @@ function validateFlowchart(text, options = {}) {
310194
308853
  existing.push({ start: startCol, end: endCol });
310195
308854
  byLine.set(ln, existing);
310196
308855
  }
308856
+ if (!covered && !isQuoted && !isSlashPair && hasCurly) {
308857
+ errs.push({ line: ln, column: startCol, severity: "error", code: "FL-LABEL-CURLY-IN-UNQUOTED", message: "Curly braces are not supported inside unquoted node labels.", hint: "Use &#123; and &#125; for literal braces, e.g., C[Substitute &#123;params&#125;]." });
308858
+ existing.push({ start: startCol, end: endCol });
308859
+ byLine.set(ln, existing);
308860
+ }
308861
+ if (!covered && !isQuoted && !isSlashPair && !isDoubleSquare && hasBracket) {
308862
+ errs.push({ line: ln, column: startCol, severity: "error", code: "FL-LABEL-BRACKET-IN-UNQUOTED", message: "Square brackets are not supported inside unquoted node labels.", hint: 'Use &#91; and &#93; for literal brackets or wrap the label in quotes, e.g., C["bind_paths[]"]' });
308863
+ existing.push({ start: startCol, end: endCol });
308864
+ byLine.set(ln, existing);
308865
+ }
310197
308866
  if (!covered && !isQuoted && !isSlashPair && hasQuote && !isSingleQuoted) {
310198
308867
  errs.push({ line: ln, column: startCol, severity: "error", code: "FL-LABEL-QUOTE-IN-UNQUOTED", message: "Quotes are not allowed inside unquoted node labels. Use &quot; for quotes or wrap the entire label in quotes.", hint: 'Example: C["HTML Output: data-trigger-visibility=&quot;true&quot;"]' });
310199
308868
  existing.push({ start: startCol, end: endCol });
@@ -312554,7 +311223,7 @@ function computeFixes(text, errors, level = "safe") {
312554
311223
  }
312555
311224
  continue;
312556
311225
  }
312557
- if (is("FL-EDGE-LABEL-BRACKET", e)) {
311226
+ if (is("FL-EDGE-LABEL-BRACKET", e) || is("FL-EDGE-LABEL-CURLY-IN-PIPES", e)) {
312558
311227
  const lineText = lineTextAt(text, e.line);
312559
311228
  const firstBar = lineText.indexOf("|");
312560
311229
  const secondBar = firstBar >= 0 ? lineText.indexOf("|", firstBar + 1) : -1;
@@ -312562,7 +311231,8 @@ function computeFixes(text, errors, level = "safe") {
312562
311231
  const before = lineText.slice(0, firstBar + 1);
312563
311232
  const label = lineText.slice(firstBar + 1, secondBar);
312564
311233
  const after = lineText.slice(secondBar);
312565
- const fixedLabel = label.replace(/\[/g, "&#91;").replace(/\]/g, "&#93;");
311234
+ let fixedLabel = label.replace(/\[/g, "&#91;").replace(/\]/g, "&#93;");
311235
+ fixedLabel = fixedLabel.replace(/\{/g, "&#123;").replace(/\}/g, "&#125;");
312566
311236
  const fixedLine = before + fixedLabel + after;
312567
311237
  const finalLine = fixedLine.replace(/\[([^\]]*)\]/g, (m, seg) => "[" + String(seg).replace(/`/g, "") + "]");
312568
311238
  edits.push({ start: { line: e.line, column: 1 }, end: { line: e.line, column: lineText.length + 1 }, newText: finalLine });
@@ -313202,7 +311872,7 @@ function computeFixes(text, errors, level = "safe") {
313202
311872
  }
313203
311873
  continue;
313204
311874
  }
313205
- if (is("FL-LABEL-PARENS-UNQUOTED", e) || is("FL-LABEL-AT-IN-UNQUOTED", e) || is("FL-LABEL-SLASH-UNQUOTED", e)) {
311875
+ if (is("FL-LABEL-PARENS-UNQUOTED", e) || is("FL-LABEL-AT-IN-UNQUOTED", e) || is("FL-LABEL-SLASH-UNQUOTED", e) || is("FL-LABEL-CURLY-IN-UNQUOTED", e) || is("FL-LABEL-BRACKET-IN-UNQUOTED", e)) {
313206
311876
  if (level === "safe" || level === "all") {
313207
311877
  if (patchedLines.has(e.line))
313208
311878
  continue;
@@ -313243,7 +311913,7 @@ function computeFixes(text, errors, level = "safe") {
313243
311913
  if (openIdx === -1)
313244
311914
  break;
313245
311915
  const contentStart = openIdx + shape.open.length;
313246
- const closeIdx = shape.open === "(" && shape.close === ")" ? findMatchingCloser(lineText, openIdx, shape.open, shape.close) : lineText.indexOf(shape.close, contentStart);
311916
+ const closeIdx = shape.open === "(" && shape.close === ")" || shape.open === "[" && shape.close === "]" ? findMatchingCloser(lineText, openIdx, shape.open, shape.close) : lineText.indexOf(shape.close, contentStart);
313247
311917
  if (closeIdx === -1)
313248
311918
  break;
313249
311919
  if (openIdx <= caret0 && caret0 < closeIdx) {
@@ -313263,11 +311933,21 @@ function computeFixes(text, errors, level = "safe") {
313263
311933
  const isSlashPair = (l, r) => l === "/" && r === "/" || l === "\\" && r === "\\" || l === "/" && r === "\\" || l === "\\" && r === "/";
313264
311934
  const isParallelogramShape = core.length >= 2 && isSlashPair(left, right);
313265
311935
  let replaced;
313266
- if (!isParallelogramShape) {
313267
- const escaped = inner.replace(/`/g, "").replace(/\"/g, "&quot;").replace(/"/g, "&quot;");
311936
+ if (is("FL-LABEL-BRACKET-IN-UNQUOTED", e) && !isParallelogramShape) {
311937
+ const hasOtherHazards = /[(){}@]/.test(inner) || inner.includes('"') || inner.includes('\\"');
311938
+ if (hasOtherHazards) {
311939
+ const escaped = inner.replace(/`/g, "").replace(/\\"/g, "&quot;").replace(/"/g, "&quot;");
311940
+ replaced = '"' + escaped + '"';
311941
+ } else {
311942
+ replaced = inner.replace(/\[/g, "&#91;").replace(/\]/g, "&#93;");
311943
+ }
311944
+ } else if (is("FL-LABEL-CURLY-IN-UNQUOTED", e)) {
311945
+ replaced = inner.replace(/\{/g, "&#123;").replace(/\}/g, "&#125;");
311946
+ } else if (!isParallelogramShape) {
311947
+ const escaped = inner.replace(/`/g, "").replace(/\\"/g, "&quot;").replace(/"/g, "&quot;");
313268
311948
  replaced = '"' + escaped + '"';
313269
311949
  } else {
313270
- replaced = inner.replace(/`/g, "").replace(/\(/g, "&#40;").replace(/\)/g, "&#41;").replace(/\"/g, "&quot;").replace(/"/g, "&quot;");
311950
+ replaced = inner.replace(/`/g, "").replace(/\(/g, "&#40;").replace(/\)/g, "&#41;").replace(/\[/g, "&#91;").replace(/\]/g, "&#93;").replace(/\\"/g, "&quot;").replace(/"/g, "&quot;");
313271
311951
  }
313272
311952
  if (replaced !== inner) {
313273
311953
  edits.push({ start: { line: e.line, column: contentStart + 1 }, end: { line: e.line, column: closeIdx + 1 }, newText: replaced });
@@ -334056,26 +332736,46 @@ var init_prompts = __esm({
334056
332736
  CRITICAL - You are READ-ONLY:
334057
332737
  You must NEVER create, modify, delete, or write files. You are strictly an exploration and analysis tool. If asked to make changes, implement features, fix bugs, or modify a PR, refuse and explain that file modifications must be done by the engineer tool \u2014 your role is only to investigate code and answer questions. Do not attempt workarounds using bash commands (echo, cat, tee, sed, etc.) to write files.
334058
332738
 
332739
+ CRITICAL - ALWAYS search before answering:
332740
+ You must NEVER answer questions about the codebase from memory or general knowledge. ALWAYS use the search and extract tools first to find the actual code, then base your answer ONLY on what you found. Even if you think you know the answer, you MUST verify it against the actual code. Your answers must be grounded in code evidence, not assumptions.
332741
+
334059
332742
  When exploring code:
334060
332743
  - Provide clear, concise explanations based on user request
334061
332744
  - Find and highlight the most relevant code snippets, if required
334062
- - Trace function calls and data flow through the system
332745
+ - Trace function calls and data flow through the system \u2014 follow the FULL call chain, not just the entry point
334063
332746
  - Try to understand the user's intent and provide relevant information
334064
332747
  - Understand high level picture
334065
332748
  - Balance detail with clarity in your explanations
332749
+ - Search using SYNONYMS and alternative terms \u2014 code naming often differs from the concept name (e.g., "authentication" might be named verify_credentials, check_token, validate_session)
332750
+ - When you find a key function, look at what it CALLS and what CALLS it to discover the complete picture
332751
+ - Before answering, ask yourself: "Did I cover all the major components? Are there related subsystems I missed?" If yes, do one more search round.
334066
332752
 
334067
332753
  When providing answers:
332754
+ - Be EXHAUSTIVE: cover ALL components you discovered, not just the main ones. If you found 10 related files, discuss all 10, not just the top 3. Users want the complete picture.
332755
+ - After drafting your answer, do a self-check: "What did I find in my searches that I haven't mentioned yet?" Add any missing components.
332756
+ - Include data structures, configuration options, and error handling \u2014 not just the happy path.
334068
332757
  - Always include a "References" section at the end of your response
334069
332758
  - List all relevant source code locations you found during exploration
334070
332759
  - Use the format: file_path:line_number or file_path#symbol_name
334071
332760
  - Group references by file when multiple locations are from the same file
334072
332761
  - Include brief descriptions of what each reference contains`,
334073
- "code-searcher": `You are ProbeChat Code Searcher, a specialized AI assistant focused ONLY on locating relevant code. Your sole job is to find and return ALL relevant code locations. Do NOT answer questions or explain anything.
332762
+ "code-searcher": `You are ProbeChat Code Explorer & Searcher. Your job is to EXPLORE the codebase to find ALL relevant code locations for the query, then return them as JSON targets.
332763
+
332764
+ You think like a code explorer \u2014 you understand that codebases have layers:
332765
+ - Core implementations (algorithms, data structures)
332766
+ - Middleware/integration layers (request handlers, interceptors)
332767
+ - Configuration and storage backends
332768
+ - Scoping mechanisms (per-user, per-org, per-tenant, global)
332769
+ - Supporting utilities and helpers
334074
332770
 
334075
332771
  When searching:
334076
- - Use only the search tool
334077
- - Run additional searches only if needed to capture all relevant locations
334078
- - Prefer specific, focused queries
332772
+ - Search for the MAIN concept first, then think: "what RELATED subsystems would a real codebase have?"
332773
+ - Use extract to READ the code you find \u2014 look for function calls, type references, and imports that point to OTHER relevant code
332774
+ - If you find middleware, check: are there org-level or tenant-level variants?
332775
+ - If you find algorithms, check: are there different storage backends?
332776
+ - Search results are paginated \u2014 if results look relevant, call nextPage=true to check for more files
332777
+ - Stop paginating when results become irrelevant or you see "All results retrieved"
332778
+ - Search using SYNONYMS \u2014 code naming differs from concepts (e.g., "rate limiting" \u2192 throttle, quota, limiter, bucket)
334079
332779
 
334080
332780
  Output format (MANDATORY):
334081
332781
  - Return ONLY valid JSON with a single top-level key: "targets"
@@ -334085,7 +332785,8 @@ Output format (MANDATORY):
334085
332785
  - "path/to/file.ext:line"
334086
332786
  - "path/to/file.ext:start-end"
334087
332787
  - Prefer #SymbolName when a function/class name is clear; otherwise use line numbers
334088
- - Deduplicate targets and keep them concise`,
332788
+ - Deduplicate targets and keep them concise
332789
+ - Aim for 5-15 targets covering ALL aspects of the query`,
334089
332790
  "architect": `You are ProbeChat Architect, a specialized AI assistant focused on software architecture and design. Your primary function is to help users understand, analyze, and design software systems using the provided code analysis tools.
334090
332791
 
334091
332792
  When analyzing code:
@@ -359739,9 +358440,9 @@ Workspace: ${this.allowedFolders.join(", ")}`;
359739
358440
  Follow these instructions carefully:
359740
358441
  1. Analyze the user's request.
359741
358442
  2. Use the available tools step-by-step to fulfill the request.
359742
- 3. You should always prefer the search tool for code-related questions.${this.searchDelegate ? " Ask natural language questions \u2014 the search subagent handles keyword formulation and returns extracted code blocks. Use extract only to expand context or read full files." : " Search handles stemming and case variations automatically \u2014 do NOT try keyword variations manually. Read full files only if really necessary."}
359743
- 4. Ensure to get really deep and understand the full picture before answering.
359744
- 5. Once the task is fully completed, provide your final answer directly as text.
358443
+ 3. You MUST use the search tool before answering ANY code-related question. NEVER answer from memory or general knowledge \u2014 your answers must be grounded in actual code found via search/extract.${this.searchDelegate ? " Ask natural language questions \u2014 the search subagent handles keyword formulation and returns extracted code blocks. Use extract only to expand context or read full files." : " Search handles stemming and case variations automatically \u2014 do NOT try keyword variations manually. Read full files only if really necessary."}
358444
+ 4. Ensure to get really deep and understand the full picture before answering. Follow call chains \u2014 if function A calls B, search for B too. Look for related subsystems (e.g., if asked about rate limiting, also check for quota, throttling, smoothing).
358445
+ 5. Once the task is fully completed, provide your final answer directly as text. Always cite specific files and line numbers as evidence. Do NOT output planning or thinking text \u2014 go straight to the answer.
359745
358446
  6. ${this.searchDelegate ? "Ask clear, specific questions when searching. Each search should target a distinct concept or question." : "Prefer concise and focused search queries. Use specific keywords and phrases to narrow down results."}
359746
358447
  7. NEVER use bash for code exploration (no grep, cat, find, head, tail, awk, sed) \u2014 always use search and extract tools instead. Bash is only for system operations like building, running tests, or git commands.${this.allowEdit ? `
359747
358448
  7. When modifying files, choose the appropriate tool:
@@ -360127,6 +358828,22 @@ You are working with a workspace. Available paths: ${workspaceDesc}
360127
358828
  if (recentTexts.every((t) => t && t === recentTexts[0])) return true;
360128
358829
  if (recentTexts.every((t) => detectStuckResponse(t))) return true;
360129
358830
  }
358831
+ if (steps.length >= 3) {
358832
+ const last3 = steps.slice(-3);
358833
+ const allHaveTools = last3.every((s) => s.toolCalls?.length === 1);
358834
+ if (allHaveTools) {
358835
+ const signatures = last3.map((s) => {
358836
+ const tc = s.toolCalls[0];
358837
+ return `${tc.toolName}::${JSON.stringify(tc.args ?? tc.input)}`;
358838
+ });
358839
+ if (signatures[0] === signatures[1] && signatures[1] === signatures[2]) {
358840
+ if (this.debug) {
358841
+ console.log(`[DEBUG] Circuit breaker: 3 consecutive identical tool calls detected (${last3[0].toolCalls[0].toolName}), forcing stop`);
358842
+ }
358843
+ return true;
358844
+ }
358845
+ }
358846
+ }
360130
358847
  return false;
360131
358848
  },
360132
358849
  prepareStep: ({ steps, stepNumber }) => {
@@ -360135,6 +358852,22 @@ You are working with a workspace. Available paths: ${workspaceDesc}
360135
358852
  toolChoice: "none"
360136
358853
  };
360137
358854
  }
358855
+ if (steps.length >= 2) {
358856
+ const last2 = steps.slice(-2);
358857
+ if (last2.every((s) => s.toolCalls?.length === 1)) {
358858
+ const tc1 = last2[0].toolCalls[0];
358859
+ const tc2 = last2[1].toolCalls[0];
358860
+ const sig1 = `${tc1.toolName}::${JSON.stringify(tc1.args ?? tc1.input)}`;
358861
+ const sig2 = `${tc2.toolName}::${JSON.stringify(tc2.args ?? tc2.input)}`;
358862
+ if (sig1 === sig2) {
358863
+ if (this.debug) {
358864
+ console.log(`[DEBUG] prepareStep: 2 consecutive identical tool calls (${tc1.toolName}), forcing toolChoice=none`);
358865
+ console.log(`[DEBUG] sig: ${sig1.substring(0, 200)}`);
358866
+ }
358867
+ return { toolChoice: "none" };
358868
+ }
358869
+ }
358870
+ }
360138
358871
  const lastStep = steps[steps.length - 1];
360139
358872
  const modelJustStopped = lastStep?.finishReason === "stop" && (!lastStep?.toolCalls || lastStep.toolCalls.length === 0);
360140
358873
  if (modelJustStopped) {
@@ -360165,7 +358898,9 @@ ${resultToReview}
360165
358898
 
360166
358899
  Double-check your response based on the criteria above. If everything looks good, respond with your previous answer exactly as-is. If something needs to be fixed or is missing, do it now, then respond with the COMPLETE updated answer (everything you did in total, not just the fix).`;
360167
358900
  return {
360168
- userMessage: completionPromptMessage
358901
+ userMessage: completionPromptMessage,
358902
+ toolChoice: "none"
358903
+ // Force text-only review — no tool calls
360169
358904
  };
360170
358905
  }
360171
358906
  }
@@ -360207,7 +358942,11 @@ Double-check your response based on the criteria above. If everything looks good
360207
358942
  options.onStream(text);
360208
358943
  }
360209
358944
  if (this.debug) {
360210
- console.log(`[DEBUG] Step ${currentIteration}/${maxIterations} finished (reason: ${finishReason}, tools: ${toolResults?.length || 0})`);
358945
+ const toolSummary = toolCalls?.length ? toolCalls.map((tc) => {
358946
+ const args = tc.args ? JSON.stringify(tc.args) : "";
358947
+ return args ? `${tc.toolName}(${debugTruncate(args, 120)})` : tc.toolName;
358948
+ }).join(", ") : "none";
358949
+ console.log(`[DEBUG] Step ${currentIteration}/${maxIterations} finished (reason: ${finishReason}, tools: [${toolSummary}])`);
360211
358950
  if (text) {
360212
358951
  console.log(`[DEBUG] model text: ${debugTruncate(text)}`);
360213
358952
  }
@@ -360240,9 +358979,15 @@ Double-check your response based on the criteria above. If everything looks good
360240
358979
  }
360241
358980
  const executeAIRequest = async () => {
360242
358981
  const result = await this.streamTextWithRetryAndFallback(streamOptions);
360243
- const finalText = await result.text;
358982
+ const steps = await result.steps;
358983
+ let finalText;
358984
+ if (steps && steps.length > 1) {
358985
+ const lastStepText = steps[steps.length - 1].text;
358986
+ finalText = lastStepText || await result.text;
358987
+ } else {
358988
+ finalText = await result.text;
358989
+ }
360244
358990
  if (this.debug) {
360245
- const steps = await result.steps;
360246
358991
  console.log(`[DEBUG] streamText completed: ${steps?.length || 0} steps, finalText=${finalText?.length || 0} chars`);
360247
358992
  }
360248
358993
  const usage = await result.usage;
@@ -360312,12 +359057,12 @@ ${finalResult}
360312
359057
 
360313
359058
  Double-check your response based on the criteria above. If everything looks good, respond with your previous answer exactly as-is. If something needs to be fixed or is missing, do it now, then respond with the COMPLETE updated answer (everything you did in total, not just the fix).`;
360314
359059
  currentMessages.push({ role: "user", content: completionPromptMessage });
360315
- const completionMaxIterations = 5;
360316
359060
  const completionStreamOptions = {
360317
359061
  model: this.provider ? this.provider(this.model) : this.model,
360318
359062
  messages: this.prepareMessagesWithImages(currentMessages),
360319
359063
  tools: tools2,
360320
- stopWhen: (0, import_ai4.stepCountIs)(completionMaxIterations),
359064
+ toolChoice: "none",
359065
+ // Force text-only response — no tool calls during review
360321
359066
  maxTokens: maxResponseTokens,
360322
359067
  temperature: 0.3,
360323
359068
  onStepFinish: ({ toolResults, text, finishReason, usage }) => {
@@ -361986,11 +360731,9 @@ function autoQuoteSearchTerms(query2) {
361986
360731
  const result = tokens.map((token) => {
361987
360732
  if (token.startsWith('"')) return token;
361988
360733
  if (operators.has(token)) return token;
361989
- const hasUpper = /[A-Z]/.test(token);
361990
- const hasLower = /[a-z]/.test(token);
361991
360734
  const hasUnderscore = token.includes("_");
361992
- const hasMixedCase = hasUpper && hasLower;
361993
- if (hasMixedCase || hasUnderscore) {
360735
+ const hasCaseTransition = /[a-z][A-Z]/.test(token) || /[A-Z]{2,}[a-z]/.test(token);
360736
+ if (hasCaseTransition || hasUnderscore) {
361994
360737
  return `"${token}"`;
361995
360738
  }
361996
360739
  return token;
@@ -362005,11 +360748,24 @@ function normalizeTargets(targets) {
362005
360748
  if (typeof target !== "string") continue;
362006
360749
  const trimmed = target.trim();
362007
360750
  if (!trimmed || seen.has(trimmed)) continue;
362008
- seen.add(trimmed);
362009
- normalized.push(trimmed);
360751
+ const subTargets = splitSpaceSeparatedPaths(trimmed);
360752
+ for (const sub of subTargets) {
360753
+ if (!seen.has(sub)) {
360754
+ seen.add(sub);
360755
+ normalized.push(sub);
360756
+ }
360757
+ }
362010
360758
  }
362011
360759
  return normalized;
362012
360760
  }
360761
+ function splitSpaceSeparatedPaths(target) {
360762
+ if (!/\s/.test(target)) return [target];
360763
+ const parts = target.split(/\s+/).filter(Boolean);
360764
+ if (parts.length <= 1) return [target];
360765
+ const allLookLikePaths = parts.every((p) => /[/\\]/.test(p) || /\.\w+/.test(p));
360766
+ if (allLookLikePaths) return parts;
360767
+ return [target];
360768
+ }
362013
360769
  function extractJsonSnippet(text) {
362014
360770
  const jsonBlockMatch = text.match(/```json\s*([\s\S]*?)```/i);
362015
360771
  if (jsonBlockMatch) {
@@ -362092,7 +360848,7 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
362092
360848
  "Break down complex queries into multiple searches to cover all aspects.",
362093
360849
  "",
362094
360850
  "Available tools:",
362095
- "- search: Find code matching keywords or patterns. Run multiple searches for different aspects of complex queries.",
360851
+ "- search: Find code matching keywords or patterns. Results are paginated \u2014 use nextPage=true when results are relevant to get more. Run multiple searches for different aspects.",
362096
360852
  "- extract: Verify code snippets to ensure targets are actually relevant before including them.",
362097
360853
  "- listFiles: Understand directory structure to find where relevant code might live.",
362098
360854
  "",
@@ -362113,13 +360869,14 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
362113
360869
  "",
362114
360870
  "Combining searches with OR:",
362115
360871
  '- Multiple unquoted words use OR logic: rate limit matches files containing EITHER "rate" OR "limit".',
362116
- `- For known symbol names, quote each term to prevent splitting: '"limitDRL" "limitRedis"' matches either exact symbol.`,
360872
+ `- IMPORTANT: Multiple quoted terms use AND logic by default: '"RateLimit" "middleware"' requires BOTH in the same file.`,
360873
+ `- To search for ANY of several quoted symbols, use the explicit OR operator: '"ForwardMessage" OR "SessionLimiter"'.`,
362117
360874
  '- Without quotes, camelCase like limitDRL gets split into "limit" + "DRL" \u2014 not what you want for symbol lookup.',
362118
360875
  "- Use OR to search for multiple related symbols in ONE search instead of separate searches.",
362119
360876
  "- This is much faster than running separate searches sequentially.",
362120
- `- Example: search '"ForwardMessage" "SessionLimiter"' finds files with either exact symbol in one call.`,
362121
- `- Example: search '"limitDRL" "doRollingWindowWrite"' finds both rate limiting functions at once.`,
362122
- '- Use AND only when you need both terms to appear in the same file: "rate AND limit".',
360877
+ `- Example: search '"ForwardMessage" OR "SessionLimiter"' finds files with either exact symbol in one call.`,
360878
+ `- Example: search '"limitDRL" OR "doRollingWindowWrite"' finds both rate limiting functions at once.`,
360879
+ "- Use AND (or just put quoted terms together) when you need both terms in the same file.",
362123
360880
  "",
362124
360881
  "Parallel tool calls:",
362125
360882
  "- When you need to search for INDEPENDENT concepts, call multiple search tools IN PARALLEL (same response).",
@@ -362133,10 +360890,10 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
362133
360890
  ' Query: "Find the IP allowlist middleware"',
362134
360891
  ' \u2192 search "allowlist middleware" (one search, probe handles IP/ip/Ip variations)',
362135
360892
  ' Query: "Find ForwardMessage and SessionLimiter"',
362136
- ` \u2192 search '"ForwardMessage" "SessionLimiter"' (one OR search finds both exact symbols)`,
360893
+ ` \u2192 search '"ForwardMessage" OR "SessionLimiter"' (one OR search finds both exact symbols)`,
362137
360894
  ' OR: search exact=true "ForwardMessage" + search exact=true "SessionLimiter" IN PARALLEL',
362138
360895
  ' Query: "Find limitDRL and limitRedis functions"',
362139
- ` \u2192 search '"limitDRL" "limitRedis"' (one OR search, quoted to prevent camelCase splitting)`,
360896
+ ` \u2192 search '"limitDRL" OR "limitRedis"' (one OR search, quoted to prevent camelCase splitting)`,
362140
360897
  ' Query: "Find ThrottleRetryLimit usage"',
362141
360898
  ' \u2192 search exact=true "ThrottleRetryLimit" (one search, if no results the symbol does not exist \u2014 stop)',
362142
360899
  ' Query: "How does BM25 scoring work with SIMD optimization?"',
@@ -362144,7 +360901,7 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
362144
360901
  "",
362145
360902
  "BAD search strategy (never do this):",
362146
360903
  ' \u2192 search "AllowedIPs" \u2192 search "allowedIps" \u2192 search "allowed_ips" (WRONG: case/style variations, probe handles them)',
362147
- ` \u2192 search "limitDRL" \u2192 search "LimitDRL" (WRONG: case variation \u2014 combine with OR: '"limitDRL" "limitRedis"')`,
360904
+ ` \u2192 search "limitDRL" \u2192 search "LimitDRL" (WRONG: case variation \u2014 combine with OR: '"limitDRL" OR "limitRedis"')`,
362148
360905
  ' \u2192 search "throttle_retry_limit" after searching "ThrottleRetryLimit" (WRONG: snake_case variation, probe handles it)',
362149
360906
  ' \u2192 search "ThrottleRetryLimit" path=tyk \u2192 search "ThrottleRetryLimit" path=gateway \u2192 search "ThrottleRetryLimit" path=apidef (WRONG: same query on different paths \u2014 probe searches recursively)',
362150
360907
  ' \u2192 search "func (k *RateLimitAndQuotaCheck) handleRateLimitFailure" (WRONG: do not search full function signatures, just use exact=true "handleRateLimitFailure")',
@@ -362157,15 +360914,34 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
362157
360914
  '- To bypass stopword filtering: wrap terms in quotes ("return", "struct") or set exact=true. Both disable stemming and splitting too.',
362158
360915
  '- camelCase terms are split: getUserData becomes "get", "user", "data" \u2014 so one search covers all naming styles.',
362159
360916
  '- Do NOT search for full function signatures like "func (r *Type) Method(args)". Just search for the method name with exact=true.',
360917
+ '- Do NOT search for file names (e.g., "sliding_log.go"). Use listFiles to discover files by name.',
360918
+ "",
360919
+ "PAGINATION:",
360920
+ "- Search results are paginated (~20k tokens per page).",
360921
+ "- If your search returned relevant files, call the same query with nextPage=true to check for more.",
360922
+ '- Keep paginating while results stay relevant. Stop when results are off-topic or "All results retrieved".',
360923
+ "",
360924
+ "WHEN TO STOP:",
360925
+ "- After you have explored the main concept AND related subsystems.",
360926
+ "- Once you have 5-15 targets covering different aspects of the query.",
360927
+ '- If you get a "DUPLICATE SEARCH BLOCKED" message, move on.',
362160
360928
  "",
362161
360929
  "Strategy:",
362162
- "1. Analyze the query - identify key concepts and group related symbols",
362163
- `2. Combine related symbols into OR searches: '"symbolA" "symbolB"' finds files with either (quote to prevent splitting)`,
362164
- "3. Run INDEPENDENT searches in PARALLEL \u2014 do not wait for one to finish before starting another",
360930
+ "1. Analyze the query \u2014 identify key concepts, then brainstorm SYNONYMS and alternative terms for each.",
360931
+ ' Code naming often differs from the concept: "authentication" \u2192 verify, credentials, login, auth;',
360932
+ ' "rate limiting" \u2192 throttle, quota, limiter, bucket; "error handling" \u2192 catch, recover, panic.',
360933
+ " Think about what a developer would NAME the function/struct/variable, not just the concept.",
360934
+ "2. Run INDEPENDENT searches in PARALLEL \u2014 search for the main concept AND synonyms simultaneously.",
360935
+ " After each search, check if results are relevant. If yes, call nextPage=true for more results.",
360936
+ `3. Combine related symbols into OR searches: '"symbolA" OR "symbolB"' finds files with either.`,
362165
360937
  "4. For known symbol names use exact=true. For concepts use default (exact=false).",
362166
- "5. If a search returns results, use extract to verify relevance. Run multiple extracts in parallel too.",
362167
- "6. If a search returns NO results, the term does not exist. Do NOT retry with variations, different paths, or longer strings. Move on.",
362168
- "7. Combine all relevant targets in your final response",
360938
+ "5. After your first round of searches, READ the extracted code and look for connected code:",
360939
+ " - Function calls to other important functions \u2192 include those targets.",
360940
+ " - Type references and imports \u2192 include type definitions.",
360941
+ " - Registered handlers/middleware \u2192 include all registered items.",
360942
+ "6. If a search returns results, use extract to verify relevance. Run multiple extracts in parallel too.",
360943
+ "7. If a search returns NO results, the term does not exist. Do NOT retry with variations. Move on.",
360944
+ "8. Once you have enough targets (typically 5-15), output your final JSON answer immediately.",
362169
360945
  "",
362170
360946
  `Query: ${searchQuery}`,
362171
360947
  `Search path(s): ${searchPath}`,
@@ -362174,7 +360950,9 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
362174
360950
  'Return ONLY valid JSON: {"targets": ["path/to/file.ext#Symbol", "path/to/file.ext:line", "path/to/file.ext:start-end"]}',
362175
360951
  'IMPORTANT: Use ABSOLUTE file paths in targets (e.g., "/full/path/to/file.ext#Symbol"). If you only have relative paths, make them relative to the search path above.',
362176
360952
  "Prefer #Symbol when a function/class name is clear; otherwise use line numbers.",
362177
- "Deduplicate targets. Do NOT explain or answer - ONLY return the JSON targets."
360953
+ "Deduplicate targets. Do NOT explain or answer - ONLY return the JSON targets.",
360954
+ "",
360955
+ "Remember: if your search returned relevant results, use nextPage=true to check for more before outputting."
362178
360956
  ].join("\n");
362179
360957
  }
362180
360958
  var import_ai5, import_fs11, CODE_SEARCH_SCHEMA, searchTool, queryTool, extractTool, delegateTool, analyzeAllTool;
@@ -362219,6 +360997,7 @@ var init_vercel = __esm({
362219
360997
  return result;
362220
360998
  };
362221
360999
  const previousSearches = /* @__PURE__ */ new Set();
361000
+ let consecutiveDupBlocks = 0;
362222
361001
  const paginationCounts = /* @__PURE__ */ new Map();
362223
361002
  const MAX_PAGES_PER_QUERY = 3;
362224
361003
  return (0, import_ai5.tool)({
@@ -362271,12 +361050,17 @@ var init_vercel = __esm({
362271
361050
  const searchKey = `${searchQuery}::${exact || false}`;
362272
361051
  if (!nextPage) {
362273
361052
  if (previousSearches.has(searchKey)) {
361053
+ consecutiveDupBlocks++;
362274
361054
  if (debug) {
362275
- console.error(`[DEDUP] Blocked duplicate search: "${searchQuery}" (path: "${searchPath}")`);
361055
+ console.error(`[DEDUP] Blocked duplicate search (${consecutiveDupBlocks}x): "${searchQuery}" (path: "${searchPath}")`);
361056
+ }
361057
+ if (consecutiveDupBlocks >= 3) {
361058
+ return "STOP. You have been blocked " + consecutiveDupBlocks + " times for repeating searches. You MUST output your final JSON answer NOW with whatever targets you have found. Do NOT call any more tools.";
362276
361059
  }
362277
- return "DUPLICATE SEARCH BLOCKED: You already searched for this exact query. Changing the path does NOT give different results \u2014 probe searches recursively. Do NOT repeat the same search. Try a genuinely different keyword, use extract to examine results you already found, or provide your final answer if you have enough information.";
361060
+ return "DUPLICATE SEARCH BLOCKED (" + consecutiveDupBlocks + "x). You already searched for this. Do NOT repeat \u2014 probe searches recursively across all paths. Either: (1) use extract on results you already found, (2) try a COMPLETELY different keyword, or (3) output your final answer NOW.";
362278
361061
  }
362279
361062
  previousSearches.add(searchKey);
361063
+ consecutiveDupBlocks = 0;
362280
361064
  paginationCounts.set(searchKey, 0);
362281
361065
  } else {
362282
361066
  const pageCount = (paginationCounts.get(searchKey) || 0) + 1;
@@ -399867,7 +398651,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"100":"Continue","101":"Switching Pro
399867
398651
  /***/ ((module) => {
399868
398652
 
399869
398653
  "use strict";
399870
- module.exports = /*#__PURE__*/JSON.parse('{"name":"@probelabs/visor","version":"0.1.42","main":"dist/index.js","bin":{"visor":"./dist/index.js"},"exports":{".":{"require":"./dist/index.js","import":"./dist/index.js"},"./sdk":{"types":"./dist/sdk/sdk.d.ts","import":"./dist/sdk/sdk.mjs","require":"./dist/sdk/sdk.js"},"./cli":{"require":"./dist/index.js"}},"files":["dist/","defaults/","action.yml","README.md","LICENSE"],"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"scripts":{"build:cli":"ncc build src/index.ts -o dist && cp -r defaults dist/ && cp -r output dist/ && cp -r docs dist/ && cp -r examples dist/ && cp -r src/debug-visualizer/ui dist/debug-visualizer/ && node scripts/inject-version.js && echo \'#!/usr/bin/env node\' | cat - dist/index.js > temp && mv temp dist/index.js && chmod +x dist/index.js","build:sdk":"tsup src/sdk.ts --dts --sourcemap --format esm,cjs --out-dir dist/sdk","build":"./scripts/build-oss.sh","build:ee":"npm run build:cli && npm run build:sdk","test":"jest && npm run test:yaml","test:unit":"jest","prepublishOnly":"npm run build","test:watch":"jest --watch","test:coverage":"jest --coverage","test:ee":"jest --testPathPatterns=\'tests/ee\' --testPathIgnorePatterns=\'/node_modules/\' --no-coverage","test:manual:bash":"RUN_MANUAL_TESTS=true jest tests/manual/bash-config-manual.test.ts","lint":"eslint src tests --ext .ts","lint:fix":"eslint src tests --ext .ts --fix","format":"prettier --write src tests","format:check":"prettier --check src tests","clean":"","clean:traces":"node scripts/clean-traces.js","prebuild":"npm run clean && node scripts/generate-config-schema.js","pretest":"npm run clean:traces && node scripts/generate-config-schema.js && npm run build:cli","pretest:unit":"npm run clean:traces && node scripts/generate-config-schema.js && npm run build:cli","test:with-build":"npm run build:cli && jest","test:yaml":"node dist/index.js test --progress compact","test:yaml:parallel":"node dist/index.js test --progress compact --max-parallel 4","prepare":"husky","pre-commit":"lint-staged","deploy:site":"cd site && npx wrangler pages deploy . --project-name=visor-site --commit-dirty=true","deploy:worker":"npx wrangler deploy","deploy":"npm run deploy:site && npm run deploy:worker","publish:ee":"./scripts/publish-ee.sh","release":"./scripts/release.sh","release:patch":"./scripts/release.sh patch","release:minor":"./scripts/release.sh minor","release:major":"./scripts/release.sh major","release:prerelease":"./scripts/release.sh prerelease","docs:validate":"node scripts/validate-readme-links.js","workshop:setup":"npm install -D reveal-md@6.1.2","workshop:serve":"cd workshop && reveal-md slides.md -w","workshop:export":"reveal-md workshop/slides.md --static workshop/build","workshop:pdf":"reveal-md workshop/slides.md --print workshop/Visor-Workshop.pdf --print-size letter","workshop:pdf:ci":"reveal-md workshop/slides.md --print workshop/Visor-Workshop.pdf --print-size letter --puppeteer-launch-args=\\"--no-sandbox --disable-dev-shm-usage\\"","workshop:pdf:a4":"reveal-md workshop/slides.md --print workshop/Visor-Workshop-A4.pdf --print-size A4","workshop:build":"npm run workshop:export && npm run workshop:pdf","simulate:issue":"TS_NODE_TRANSPILE_ONLY=1 ts-node scripts/simulate-gh-run.ts --event issues --action opened --debug","simulate:comment":"TS_NODE_TRANSPILE_ONLY=1 ts-node scripts/simulate-gh-run.ts --event issue_comment --action created --debug"},"keywords":["code-review","ai","github-action","cli","pr-review","visor"],"author":"Probe Labs","license":"MIT","description":"AI workflow engine for code review, assistants, and automation — orchestrate checks, MCP tools, and AI providers with YAML-driven pipelines","repository":{"type":"git","url":"git+https://github.com/probelabs/visor.git"},"bugs":{"url":"https://github.com/probelabs/visor/issues"},"homepage":"https://github.com/probelabs/visor#readme","dependencies":{"@actions/core":"^1.11.1","@apidevtools/swagger-parser":"^12.1.0","@modelcontextprotocol/sdk":"^1.25.3","@nyariv/sandboxjs":"github:probelabs/SandboxJS#f1c13b8eee98734a8ea024061eada4aa9a9ff2e9","@octokit/action":"^8.0.2","@octokit/auth-app":"^8.1.0","@octokit/core":"^7.0.3","@octokit/rest":"^22.0.0","@opentelemetry/api":"^1.9.0","@opentelemetry/core":"^1.30.1","@opentelemetry/exporter-trace-otlp-grpc":"^0.203.0","@opentelemetry/exporter-trace-otlp-http":"^0.203.0","@opentelemetry/instrumentation":"^0.203.0","@opentelemetry/resources":"^1.30.1","@opentelemetry/sdk-metrics":"^1.30.1","@opentelemetry/sdk-node":"^0.203.0","@opentelemetry/sdk-trace-base":"^1.30.1","@opentelemetry/semantic-conventions":"^1.30.1","@probelabs/probe":"^0.6.0-rc286","@types/commander":"^2.12.0","@types/uuid":"^10.0.0","acorn":"^8.16.0","acorn-walk":"^8.3.5","ajv":"^8.17.1","ajv-formats":"^3.0.1","better-sqlite3":"^11.0.0","blessed":"^0.1.81","cli-table3":"^0.6.5","commander":"^14.0.0","deepmerge":"^4.3.1","dotenv":"^17.2.3","ignore":"^7.0.5","js-yaml":"^4.1.0","jsonpath-plus":"^10.4.0","liquidjs":"^10.21.1","minimatch":"^10.2.2","node-cron":"^3.0.3","open":"^9.1.0","simple-git":"^3.28.0","uuid":"^11.1.0","ws":"^8.18.3"},"optionalDependencies":{"@anthropic/claude-code-sdk":"npm:null@*","@open-policy-agent/opa-wasm":"^1.10.0","knex":"^3.1.0","mysql2":"^3.11.0","pg":"^8.13.0","tedious":"^19.0.0"},"devDependencies":{"@eslint/js":"^9.34.0","@kie/act-js":"^2.6.2","@kie/mock-github":"^2.0.1","@swc/core":"^1.13.2","@swc/jest":"^0.2.37","@types/better-sqlite3":"^7.6.0","@types/blessed":"^0.1.27","@types/jest":"^30.0.0","@types/js-yaml":"^4.0.9","@types/node":"^24.3.0","@types/node-cron":"^3.0.11","@types/ws":"^8.18.1","@typescript-eslint/eslint-plugin":"^8.42.0","@typescript-eslint/parser":"^8.42.0","@vercel/ncc":"^0.38.4","eslint":"^9.34.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.4","husky":"^9.1.7","jest":"^30.1.3","lint-staged":"^16.1.6","prettier":"^3.6.2","reveal-md":"^6.1.2","ts-json-schema-generator":"^1.5.1","ts-node":"^10.9.2","tsup":"^8.5.0","typescript":"^5.9.2","wrangler":"^3.0.0"},"peerDependenciesMeta":{"@anthropic/claude-code-sdk":{"optional":true}},"directories":{"test":"tests"},"lint-staged":{"src/**/*.{ts,js}":["eslint --fix","prettier --write"],"tests/**/*.{ts,js}":["eslint --fix","prettier --write"],"*.{json,md,yml,yaml}":["prettier --write"]}}');
398654
+ module.exports = /*#__PURE__*/JSON.parse('{"name":"@probelabs/visor","version":"0.1.171","main":"dist/index.js","bin":{"visor":"./dist/index.js"},"exports":{".":{"require":"./dist/index.js","import":"./dist/index.js"},"./sdk":{"types":"./dist/sdk/sdk.d.ts","import":"./dist/sdk/sdk.mjs","require":"./dist/sdk/sdk.js"},"./cli":{"require":"./dist/index.js"}},"files":["dist/","defaults/","action.yml","README.md","LICENSE"],"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"scripts":{"build:cli":"ncc build src/index.ts -o dist && cp -r defaults dist/ && cp -r output dist/ && cp -r docs dist/ && cp -r examples dist/ && cp -r src/debug-visualizer/ui dist/debug-visualizer/ && node scripts/inject-version.js && echo \'#!/usr/bin/env node\' | cat - dist/index.js > temp && mv temp dist/index.js && chmod +x dist/index.js","build:sdk":"tsup src/sdk.ts --dts --sourcemap --format esm,cjs --out-dir dist/sdk","build":"./scripts/build-oss.sh","build:ee":"npm run build:cli && npm run build:sdk","test":"jest && npm run test:yaml","test:unit":"jest","prepublishOnly":"npm run build","test:watch":"jest --watch","test:coverage":"jest --coverage","test:ee":"jest --testPathPatterns=\'tests/ee\' --testPathIgnorePatterns=\'/node_modules/\' --no-coverage","test:manual:bash":"RUN_MANUAL_TESTS=true jest tests/manual/bash-config-manual.test.ts","lint":"eslint src tests --ext .ts","lint:fix":"eslint src tests --ext .ts --fix","format":"prettier --write src tests","format:check":"prettier --check src tests","clean":"","clean:traces":"node scripts/clean-traces.js","prebuild":"npm run clean && node scripts/generate-config-schema.js","pretest":"npm run clean:traces && node scripts/generate-config-schema.js && npm run build:cli","pretest:unit":"npm run clean:traces && node scripts/generate-config-schema.js && npm run build:cli","test:with-build":"npm run build:cli && jest","test:yaml":"node dist/index.js test --progress compact","test:yaml:parallel":"node dist/index.js test --progress compact --max-parallel 4","prepare":"husky","pre-commit":"lint-staged","deploy:site":"cd site && npx wrangler pages deploy . --project-name=visor-site --commit-dirty=true","deploy:worker":"npx wrangler deploy","deploy":"npm run deploy:site && npm run deploy:worker","publish:ee":"./scripts/publish-ee.sh","release":"./scripts/release.sh","release:patch":"./scripts/release.sh patch","release:minor":"./scripts/release.sh minor","release:major":"./scripts/release.sh major","release:prerelease":"./scripts/release.sh prerelease","docs:validate":"node scripts/validate-readme-links.js","workshop:setup":"npm install -D reveal-md@6.1.2","workshop:serve":"cd workshop && reveal-md slides.md -w","workshop:export":"reveal-md workshop/slides.md --static workshop/build","workshop:pdf":"reveal-md workshop/slides.md --print workshop/Visor-Workshop.pdf --print-size letter","workshop:pdf:ci":"reveal-md workshop/slides.md --print workshop/Visor-Workshop.pdf --print-size letter --puppeteer-launch-args=\\"--no-sandbox --disable-dev-shm-usage\\"","workshop:pdf:a4":"reveal-md workshop/slides.md --print workshop/Visor-Workshop-A4.pdf --print-size A4","workshop:build":"npm run workshop:export && npm run workshop:pdf","simulate:issue":"TS_NODE_TRANSPILE_ONLY=1 ts-node scripts/simulate-gh-run.ts --event issues --action opened --debug","simulate:comment":"TS_NODE_TRANSPILE_ONLY=1 ts-node scripts/simulate-gh-run.ts --event issue_comment --action created --debug"},"keywords":["code-review","ai","github-action","cli","pr-review","visor"],"author":"Probe Labs","license":"MIT","description":"AI workflow engine for code review, assistants, and automation — orchestrate checks, MCP tools, and AI providers with YAML-driven pipelines","repository":{"type":"git","url":"git+https://github.com/probelabs/visor.git"},"bugs":{"url":"https://github.com/probelabs/visor/issues"},"homepage":"https://github.com/probelabs/visor#readme","dependencies":{"@actions/core":"^1.11.1","@apidevtools/swagger-parser":"^12.1.0","@modelcontextprotocol/sdk":"^1.25.3","@nyariv/sandboxjs":"github:probelabs/SandboxJS#f1c13b8eee98734a8ea024061eada4aa9a9ff2e9","@octokit/action":"^8.0.2","@octokit/auth-app":"^8.1.0","@octokit/core":"^7.0.3","@octokit/rest":"^22.0.0","@opentelemetry/api":"^1.9.0","@opentelemetry/core":"^1.30.1","@opentelemetry/exporter-trace-otlp-grpc":"^0.203.0","@opentelemetry/exporter-trace-otlp-http":"^0.203.0","@opentelemetry/instrumentation":"^0.203.0","@opentelemetry/resources":"^1.30.1","@opentelemetry/sdk-metrics":"^1.30.1","@opentelemetry/sdk-node":"^0.203.0","@opentelemetry/sdk-trace-base":"^1.30.1","@opentelemetry/semantic-conventions":"^1.30.1","@probelabs/probe":"^0.6.0-rc290","@types/commander":"^2.12.0","@types/uuid":"^10.0.0","acorn":"^8.16.0","acorn-walk":"^8.3.5","ajv":"^8.17.1","ajv-formats":"^3.0.1","better-sqlite3":"^11.0.0","blessed":"^0.1.81","cli-table3":"^0.6.5","commander":"^14.0.0","deepmerge":"^4.3.1","dotenv":"^17.2.3","ignore":"^7.0.5","js-yaml":"^4.1.0","jsonpath-plus":"^10.4.0","liquidjs":"^10.21.1","minimatch":"^10.2.2","node-cron":"^3.0.3","open":"^9.1.0","simple-git":"^3.28.0","uuid":"^11.1.0","ws":"^8.18.3"},"optionalDependencies":{"@anthropic/claude-code-sdk":"npm:null@*","@open-policy-agent/opa-wasm":"^1.10.0","knex":"^3.1.0","mysql2":"^3.11.0","pg":"^8.13.0","tedious":"^19.0.0"},"devDependencies":{"@eslint/js":"^9.34.0","@kie/act-js":"^2.6.2","@kie/mock-github":"^2.0.1","@swc/core":"^1.13.2","@swc/jest":"^0.2.37","@types/better-sqlite3":"^7.6.0","@types/blessed":"^0.1.27","@types/jest":"^30.0.0","@types/js-yaml":"^4.0.9","@types/node":"^24.3.0","@types/node-cron":"^3.0.11","@types/ws":"^8.18.1","@typescript-eslint/eslint-plugin":"^8.42.0","@typescript-eslint/parser":"^8.42.0","@vercel/ncc":"^0.38.4","eslint":"^9.34.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.4","husky":"^9.1.7","jest":"^30.1.3","lint-staged":"^16.1.6","prettier":"^3.6.2","reveal-md":"^6.1.2","ts-json-schema-generator":"^1.5.1","ts-node":"^10.9.2","tsup":"^8.5.0","typescript":"^5.9.2","wrangler":"^3.0.0"},"peerDependenciesMeta":{"@anthropic/claude-code-sdk":{"optional":true}},"directories":{"test":"tests"},"lint-staged":{"src/**/*.{ts,js}":["eslint --fix","prettier --write"],"tests/**/*.{ts,js}":["eslint --fix","prettier --write"],"*.{json,md,yml,yaml}":["prettier --write"]}}');
399871
398655
 
399872
398656
  /***/ })
399873
398657