atom-agent 1.5.0 → 1.5.1

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/telemetry.js CHANGED
@@ -55,7 +55,7 @@ import { randomUUID } from "node:crypto";
55
55
  import { fileURLToPath } from "node:url";
56
56
  import { scrubSecrets } from "./policy.js";
57
57
  import { atomDir } from "./auth.js";
58
- export const TELEMETRY_VERSION = 1;
58
+ export const TELEMETRY_VERSION = 2;
59
59
  export const TELEMETRY_DIRNAME = "telemetry";
60
60
  export const TELEMETRY_SESSIONS_DIRNAME = "sessions";
61
61
  export const TELEMETRY_DASHBOARD_FILENAME = "dashboard.html";
@@ -414,7 +414,8 @@ function isRecord(value) {
414
414
  function validateTelemetrySession(data) {
415
415
  if (!isRecord(data))
416
416
  return null;
417
- if (data["version"] !== TELEMETRY_VERSION)
417
+ const ver = data["version"];
418
+ if (ver !== TELEMETRY_VERSION && ver !== 1)
418
419
  return null;
419
420
  const sessionId = data["sessionId"];
420
421
  const startedAt = data["startedAt"];
@@ -427,6 +428,17 @@ function validateTelemetrySession(data) {
427
428
  return null;
428
429
  const subagents = Array.isArray(data["subagents"]) ? data["subagents"] : [];
429
430
  const events = Array.isArray(data["events"]) ? data["events"] : [];
431
+ // v1 → v2 migration: inputTruncated defaults from preview length, loop
432
+ // gains additive phase fields only when the writer reported them.
433
+ const migratedTurns = turns.map((t) => {
434
+ const rec = t;
435
+ if (typeof rec["inputTruncated"] !== "boolean") {
436
+ const preview = typeof rec["inputPreview"] === "string" ? rec["inputPreview"] : "";
437
+ const chars = typeof rec["inputChars"] === "number" ? rec["inputChars"] : preview.length;
438
+ rec["inputTruncated"] = chars > preview.length;
439
+ }
440
+ return t;
441
+ });
430
442
  return {
431
443
  version: TELEMETRY_VERSION,
432
444
  sessionId,
@@ -436,7 +448,7 @@ function validateTelemetrySession(data) {
436
448
  project: typeof data["project"] === "string" ? data["project"] : null,
437
449
  provider: typeof data["provider"] === "string" ? data["provider"] : "unknown",
438
450
  model: typeof data["model"] === "string" ? data["model"] : "unknown",
439
- turns: turns,
451
+ turns: migratedTurns,
440
452
  subagents,
441
453
  events,
442
454
  compactionUsage: isRecord(data["compactionUsage"]) ? data["compactionUsage"] : {},
@@ -704,6 +716,7 @@ export class TelemetryRecorder {
704
716
  durationMs: null,
705
717
  inputPreview: preview.preview,
706
718
  inputChars: typeof input === "string" ? input.length : 0,
719
+ inputTruncated: preview.truncated,
707
720
  provider: meta.provider,
708
721
  model: meta.model,
709
722
  effort: meta.effort,
@@ -798,7 +811,9 @@ export class TelemetryRecorder {
798
811
  : 0,
799
812
  usage: clean,
800
813
  usageReported: info.usageReported === true && clean !== undefined,
801
- reasoningLabel: typeof info.reasoningLabel === "string" ? info.reasoningLabel : undefined,
814
+ reasoningLabel: typeof info.reasoningLabel === "string" && info.reasoningLabel.trim().length > 2
815
+ ? info.reasoningLabel
816
+ : undefined,
802
817
  toolCallCount: typeof info.toolCallCount === "number" ? info.toolCallCount : 0,
803
818
  finishReason: info.finishReason === "tool_calls" || info.finishReason === "error" ? info.finishReason : "final",
804
819
  error: typeof info.error === "string" ? info.error.slice(0, 500) : undefined,
@@ -895,6 +910,26 @@ export class TelemetryRecorder {
895
910
  if (bMs !== undefined)
896
911
  loop.bottleneckMs = bMs;
897
912
  }
913
+ const slowest = s["slowestModel"];
914
+ const sName = typeof slowest?.["id"] === "string" ? slowest["id"] : undefined;
915
+ const sMs = num(slowest?.["durationMs"]) ?? undefined;
916
+ if (sName && sName.length > 0) {
917
+ loop.slowestModelName = sName.slice(0, 80);
918
+ if (sMs !== undefined)
919
+ loop.slowestModelMs = sMs;
920
+ }
921
+ const modelTotal = num(s["modelTotalMs"]) ?? undefined;
922
+ if (modelTotal !== undefined)
923
+ loop.modelTotalMs = modelTotal;
924
+ const toolTotal = num(s["toolTotalMs"]) ?? undefined;
925
+ if (toolTotal !== undefined)
926
+ loop.toolTotalMs = toolTotal;
927
+ const dom = s["dominantPhase"];
928
+ if (dom === "model" || dom === "tool")
929
+ loop.dominantPhase = dom;
930
+ else if (modelTotal !== undefined || toolTotal !== undefined) {
931
+ loop.dominantPhase = (modelTotal ?? 0) >= (toolTotal ?? 0) ? "model" : "tool";
932
+ }
898
933
  turn.loop = loop;
899
934
  }
900
935
  catch {
@@ -1031,7 +1066,21 @@ export class TelemetryRecorder {
1031
1066
  : outcome === "cancelled"
1032
1067
  ? "(cancelled)"
1033
1068
  : "(failed)";
1069
+ // Preserve any streamed partial for post-mortem instead of nulling
1070
+ // the reply outright; dashboard renders partial distinctly.
1071
+ if (typeof replyOrError === "string" && replyOrError.length > 0) {
1072
+ const scrubbed = this.scrub(replyOrError);
1073
+ turn.partialReplyPreview = truncatePreview(scrubbed, TELEMETRY_INPUT_PREVIEW_CHARS).preview;
1074
+ }
1075
+ else {
1076
+ turn.partialReplyPreview = null;
1077
+ }
1034
1078
  turn.replyPreview = null;
1079
+ // Timeline entry for failed turns only: cancellations are
1080
+ // user-initiated noise, and the turn record already keeps the error.
1081
+ if (outcome === "failed") {
1082
+ this.recordEvent("info", `turn ${turn.id} ${outcome}: ${(turn.error ?? "").slice(0, 200)}`);
1083
+ }
1035
1084
  }
1036
1085
  else {
1037
1086
  const scrubbed = this.scrub(typeof replyOrError === "string" ? replyOrError : "");