@evident-ai/cli 3.0.1-dev.51bb855 → 3.0.1-dev.60e0bdb

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -490,6 +490,29 @@ var TelemetryEventTypes = {
490
490
  var MAX_FRAME_BYTES = 256 * 1024;
491
491
  var TUNNEL_DRAIN_PING_PATH = "/__evident/drain";
492
492
 
493
+ // ../../packages/types/src/logging/index.ts
494
+ var CORRELATION_ID_HEADER = "x-evident-correlation-id";
495
+ function log(level, event, fields) {
496
+ const method = level === "debug" ? "log" : level;
497
+ try {
498
+ console[method]("[evident]", JSON.stringify({ level, event, ...fields }));
499
+ } catch (err) {
500
+ console.error(
501
+ "[evident] log_serialize_failed",
502
+ event,
503
+ err instanceof Error ? err.message : String(err)
504
+ );
505
+ }
506
+ }
507
+ function stripQuery(url) {
508
+ try {
509
+ return new URL(url).pathname;
510
+ } catch {
511
+ const q = url.indexOf("?");
512
+ return q === -1 ? url : url.slice(0, q);
513
+ }
514
+ }
515
+
493
516
  // src/lib/telemetry.ts
494
517
  var CLI_VERSION = process.env.npm_package_version || "unknown";
495
518
  var eventBuffer = [];
@@ -1192,12 +1215,20 @@ var StreamForwarder = class {
1192
1215
  }
1193
1216
  async handleOpen(frame) {
1194
1217
  const { sid, method, path, headers, has_body } = frame;
1218
+ const correlationId = headers?.[CORRELATION_ID_HEADER];
1219
+ const startedAt = Date.now();
1195
1220
  if (path === TUNNEL_DRAIN_PING_PATH) {
1196
1221
  this.callbacks.onDrainPing?.();
1197
1222
  this.send({ type: "head", sid, status: 204, headers: {} });
1198
1223
  this.send({ type: "res_end", sid });
1199
1224
  return;
1200
1225
  }
1226
+ log("info", "agent_request", {
1227
+ correlation_id: correlationId,
1228
+ sid,
1229
+ method,
1230
+ path: stripQuery(path)
1231
+ });
1201
1232
  const ac = new AbortController();
1202
1233
  let bodyPromise;
1203
1234
  let pushBody;
@@ -1244,6 +1275,12 @@ var StreamForwarder = class {
1244
1275
  if (!STRIP_RES.has(key.toLowerCase())) resHeaders[key] = value;
1245
1276
  });
1246
1277
  this.send({ type: "head", sid, status: upstream.status, headers: resHeaders });
1278
+ log("info", "agent_response", {
1279
+ correlation_id: correlationId,
1280
+ sid,
1281
+ status: upstream.status,
1282
+ duration_ms: Date.now() - startedAt
1283
+ });
1247
1284
  this.callbacks.onHead?.(sid, upstream.status);
1248
1285
  try {
1249
1286
  if (upstream.body) {
@@ -2535,7 +2572,7 @@ async function getAgentInfo(agentId, authHeader) {
2535
2572
  // src/commands/run.ts
2536
2573
  var MAX_ACTIVITY_LOG_ENTRIES = 10;
2537
2574
  var CHANNEL_POLL_INTERVAL_MS = Number(process.env.EVIDENT_CHANNEL_POLL_INTERVAL_MS) || 2e3;
2538
- function log(state, message, isError = false) {
2575
+ function log2(state, message, isError = false) {
2539
2576
  if (state.json) {
2540
2577
  console.log(
2541
2578
  JSON.stringify({
@@ -2560,9 +2597,9 @@ function logActivity(state, entry) {
2560
2597
  }
2561
2598
  if (!state.interactive) {
2562
2599
  if (entry.type === "error") {
2563
- log(state, entry.error ?? "Unknown error", true);
2600
+ log2(state, entry.error ?? "Unknown error", true);
2564
2601
  } else if (entry.type === "info" && entry.message) {
2565
- log(state, entry.message);
2602
+ log2(state, entry.message);
2566
2603
  }
2567
2604
  }
2568
2605
  }
@@ -2648,6 +2685,7 @@ async function handleAuthError(state, error2) {
2648
2685
  }
2649
2686
  async function driveChannels(state, driver) {
2650
2687
  let idlePolls = 0;
2688
+ let lastSeenProxiedActivityAt = state.lastProxiedActivityAt;
2651
2689
  while (state.running) {
2652
2690
  if (state.connection?.reconnecting && state.connection.reconnectPromise) {
2653
2691
  logActivity(state, { type: "info", message: "Waiting for tunnel reconnection..." });
@@ -2657,7 +2695,9 @@ async function driveChannels(state, driver) {
2657
2695
  try {
2658
2696
  const processed = await driver.drainPending();
2659
2697
  state.messageCount += processed;
2660
- if (processed > 0 || driver.hasInFlightWatchers()) {
2698
+ const proxiedActivity = state.lastProxiedActivityAt !== lastSeenProxiedActivityAt;
2699
+ lastSeenProxiedActivityAt = state.lastProxiedActivityAt;
2700
+ if (processed > 0 || driver.hasInFlightWatchers() || proxiedActivity) {
2661
2701
  idlePolls = 0;
2662
2702
  if (processed > 0 && state.interactive) displayStatus(state);
2663
2703
  } else if (state.idleTimeout !== null) {
@@ -2709,7 +2749,7 @@ async function cleanup(state) {
2709
2749
  logActivity(state, { type: "info", message: "Stopped OpenCode process" });
2710
2750
  displayStatus(state);
2711
2751
  } else {
2712
- log(state, "Stopped OpenCode process");
2752
+ log2(state, "Stopped OpenCode process");
2713
2753
  }
2714
2754
  state.opencodeProcess = null;
2715
2755
  }
@@ -2732,10 +2772,11 @@ async function run(options) {
2732
2772
  running: true,
2733
2773
  activityLog: [],
2734
2774
  messageCount: 0,
2775
+ lastProxiedActivityAt: null,
2735
2776
  authHeader: ""
2736
2777
  };
2737
2778
  if (state.idleTimeout === null && (process.env.GITHUB_ACTIONS || process.env.CI)) {
2738
- log(
2779
+ log2(
2739
2780
  state,
2740
2781
  "Warning: No --idle-timeout set in CI environment. The runner will poll indefinitely until the job times out. Consider adding --idle-timeout 30 to avoid wasting runner minutes.",
2741
2782
  false
@@ -2746,7 +2787,7 @@ async function run(options) {
2746
2787
  logActivity(state, { type: "info", message: "Shutting down..." });
2747
2788
  displayStatus(state);
2748
2789
  } else {
2749
- log(state, "Shutting down...");
2790
+ log2(state, "Shutting down...");
2750
2791
  }
2751
2792
  await cleanup(state);
2752
2793
  await shutdownTelemetry();
@@ -2779,7 +2820,7 @@ async function run(options) {
2779
2820
  const resolved = await resolveAgentIdFromKey(state.authHeader);
2780
2821
  if (resolved.agent_id) {
2781
2822
  state.agentId = resolved.agent_id;
2782
- log(state, `Resolved agent ID from key: ${state.agentId}`);
2823
+ log2(state, `Resolved agent ID from key: ${state.agentId}`);
2783
2824
  if (state.interactive && !state.json) {
2784
2825
  logActivity(state, {
2785
2826
  type: "info",
@@ -2842,7 +2883,7 @@ async function run(options) {
2842
2883
  port: state.port,
2843
2884
  interactive: state.interactive,
2844
2885
  agentId: state.agentId,
2845
- log: (message) => log(state, message)
2886
+ log: (message) => log2(state, message)
2846
2887
  });
2847
2888
  state.port = oc.port;
2848
2889
  state.opencodeProcess = oc.process;
@@ -2852,7 +2893,7 @@ async function run(options) {
2852
2893
  ocSpinner?.succeed(`OpenCode running on port ${state.port}${version}`);
2853
2894
  const versionWarning = buildOpenCodeVersionWarning(state.opencodeVersion);
2854
2895
  if (versionWarning) {
2855
- log(state, versionWarning, false);
2896
+ log2(state, versionWarning, false);
2856
2897
  if (state.interactive && !state.json) {
2857
2898
  logActivity(state, { type: "info", message: versionWarning });
2858
2899
  }
@@ -2921,9 +2962,14 @@ async function run(options) {
2921
2962
  logActivity(state, { type: "error", error: error2 });
2922
2963
  if (state.interactive) displayStatus(state);
2923
2964
  },
2924
- // Web traffic is proxied transparently; only note opencode is live.
2965
+ // Web traffic is proxied transparently; note opencode is live and stamp
2966
+ // proxied activity so the idle loop treats interactive proxy use as work.
2967
+ // Fires per forwarded response head (incl. every SSE open) and excludes
2968
+ // the internal drain-ping, so an actively-used proxy keeps the timer
2969
+ // fresh while a lone idle SSE with no follow-up requests still ages out.
2925
2970
  onResponse: () => {
2926
2971
  state.opencodeConnected = true;
2972
+ state.lastProxiedActivityAt = Date.now();
2927
2973
  },
2928
2974
  // A channel message was queued and the api-worker pinged us over the
2929
2975
  // tunnel to drain immediately instead of waiting for the next poll tick.
@@ -2962,7 +3008,7 @@ async function run(options) {
2962
3008
  throw error2;
2963
3009
  }
2964
3010
  if (!interactive || state.json) {
2965
- log(state, "Driving channel messages...");
3011
+ log2(state, "Driving channel messages...");
2966
3012
  }
2967
3013
  await driveChannels(state, channelDriver);
2968
3014
  await cleanup(state);
@@ -2974,7 +3020,7 @@ async function run(options) {
2974
3020
  })
2975
3021
  );
2976
3022
  } else if (!interactive) {
2977
- log(state, `Completed. Processed ${state.messageCount} message(s).`);
3023
+ log2(state, `Completed. Processed ${state.messageCount} message(s).`);
2978
3024
  }
2979
3025
  await shutdownTelemetry();
2980
3026
  process.exit(0);