agent-session-inspector 0.1.7 → 0.1.9

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 (96) hide show
  1. package/VERSION +1 -1
  2. package/app/.next/BUILD_ID +1 -1
  3. package/app/.next/app-path-routes-manifest.json +1 -0
  4. package/app/.next/build-manifest.json +3 -3
  5. package/app/.next/prerender-manifest.json +3 -3
  6. package/app/.next/required-server-files.json +1 -1
  7. package/app/.next/routes-manifest.json +9 -0
  8. package/app/.next/server/app/_global-error.html +1 -1
  9. package/app/.next/server/app/_global-error.rsc +1 -1
  10. package/app/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  11. package/app/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  12. package/app/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/app/.next/server/app/_not-found/page.js.nft.json +1 -1
  14. package/app/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  15. package/app/.next/server/app/_not-found.html +1 -1
  16. package/app/.next/server/app/_not-found.rsc +14 -14
  17. package/app/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
  18. package/app/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +13 -13
  19. package/app/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  20. package/app/.next/server/app/api/analytics/route.js +2 -1
  21. package/app/.next/server/app/api/analytics/route.js.nft.json +1 -1
  22. package/app/.next/server/app/api/logs/route.js +2 -1
  23. package/app/.next/server/app/api/logs/route.js.nft.json +1 -1
  24. package/app/.next/server/app/api/pricing/route.js +2 -1
  25. package/app/.next/server/app/api/pricing/route.js.nft.json +1 -1
  26. package/app/.next/server/app/api/providers/route.js +2 -1
  27. package/app/.next/server/app/api/providers/route.js.nft.json +1 -1
  28. package/app/.next/server/app/api/search/route.js +3 -2
  29. package/app/.next/server/app/api/search/route.js.nft.json +1 -1
  30. package/app/.next/server/app/api/sessions/[provider]/[id]/record/route/app-paths-manifest.json +3 -0
  31. package/app/.next/server/app/api/sessions/[provider]/[id]/record/route/build-manifest.json +12 -0
  32. package/app/.next/server/app/api/sessions/[provider]/[id]/record/route/server-reference-manifest.json +4 -0
  33. package/app/.next/server/app/api/sessions/[provider]/[id]/record/route.js +8 -0
  34. package/app/.next/server/app/api/sessions/[provider]/[id]/record/route.js.map +5 -0
  35. package/app/.next/server/app/api/sessions/[provider]/[id]/record/route.js.nft.json +1 -0
  36. package/app/.next/server/app/api/sessions/[provider]/[id]/record/route_client-reference-manifest.js +3 -0
  37. package/app/.next/server/app/api/sessions/[provider]/[id]/reveal/route.js +2 -1
  38. package/app/.next/server/app/api/sessions/[provider]/[id]/reveal/route.js.nft.json +1 -1
  39. package/app/.next/server/app/api/sessions/[provider]/[id]/route.js +2 -1
  40. package/app/.next/server/app/api/sessions/[provider]/[id]/route.js.nft.json +1 -1
  41. package/app/.next/server/app/api/sessions/route.js +2 -1
  42. package/app/.next/server/app/api/sessions/route.js.nft.json +1 -1
  43. package/app/.next/server/app/index.html +1 -1
  44. package/app/.next/server/app/index.rsc +16 -16
  45. package/app/.next/server/app/index.segments/__PAGE__.segment.rsc +15 -15
  46. package/app/.next/server/app/index.segments/_full.segment.rsc +16 -16
  47. package/app/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  48. package/app/.next/server/app/page.js.nft.json +1 -1
  49. package/app/.next/server/app/page_client-reference-manifest.js +1 -1
  50. package/app/.next/server/app/sessions/[provider]/[id]/page.js.nft.json +1 -1
  51. package/app/.next/server/app/sessions/[provider]/[id]/page_client-reference-manifest.js +1 -1
  52. package/app/.next/server/app-paths-manifest.json +1 -0
  53. package/app/.next/server/chunks/1oeh_server_app_api_sessions_[provider]_[id]_record_route_actions_13lvgh0.js +3 -0
  54. package/app/.next/server/chunks/[externals]__0xll52b._.js +3 -0
  55. package/app/.next/server/chunks/[root-of-the-server]__014br-h._.js +3 -0
  56. package/app/.next/server/chunks/[root-of-the-server]__03bee2l._.js +3 -0
  57. package/app/.next/server/chunks/[root-of-the-server]__093lyxv._.js +3 -0
  58. package/app/.next/server/chunks/[root-of-the-server]__0co499t._.js +3 -0
  59. package/app/.next/server/chunks/[root-of-the-server]__0ne74op._.js +3 -0
  60. package/app/.next/server/chunks/[root-of-the-server]__0p_hyq8._.js +3 -0
  61. package/app/.next/server/chunks/[root-of-the-server]__0ryrg2h._.js +3 -0
  62. package/app/.next/server/chunks/[root-of-the-server]__0uv5tnc._.js +3 -0
  63. package/app/.next/server/chunks/[root-of-the-server]__0yxic1z._.js +3 -3
  64. package/app/.next/server/chunks/[root-of-the-server]__1lfc-ga._.js +3 -0
  65. package/app/.next/server/chunks/[root-of-the-server]__1n_sepq._.js +3 -0
  66. package/app/.next/server/chunks/ssr/_0f8nmc3._.js +1 -1
  67. package/app/.next/server/chunks/ssr/_0o294sf._.js +1 -1
  68. package/app/.next/server/chunks/ssr/_0wfo2z0._.js +1 -1
  69. package/app/.next/server/chunks/ssr/src_app_sessions_[provider]_[id]_page_tsx_0uf635h._.js +3 -3
  70. package/app/.next/server/middleware-build-manifest.js +3 -3
  71. package/app/.next/server/pages/404.html +1 -1
  72. package/app/.next/server/pages/500.html +1 -1
  73. package/app/.next/server/server-reference-manifest.js +1 -1
  74. package/app/.next/server/server-reference-manifest.json +1 -1
  75. package/app/.next/static/chunks/{33valh9_p24ah.js → 0f_9fwik8mxzl.js} +1 -1
  76. package/app/.next/static/chunks/{1fe2mxakoweia.js → 18c6__lhilfeo.js} +1 -1
  77. package/app/.next/static/chunks/28k1og85stfuo.js +6 -0
  78. package/app/.next/static/chunks/3-49ck9kf466m.css +1 -0
  79. package/app/package.json +1 -1
  80. package/app/scripts/demo-data.mjs +3 -1
  81. package/app/server.js +1 -1
  82. package/cli/cli.mjs +351 -126
  83. package/package.json +1 -1
  84. package/app/.next/server/chunks/[root-of-the-server]__0dyq0rc._.js +0 -3
  85. package/app/.next/server/chunks/[root-of-the-server]__0kgkqhn._.js +0 -3
  86. package/app/.next/server/chunks/[root-of-the-server]__10p8y9z._.js +0 -3
  87. package/app/.next/server/chunks/[root-of-the-server]__1cceere._.js +0 -3
  88. package/app/.next/server/chunks/[root-of-the-server]__1eclfq8._.js +0 -3
  89. package/app/.next/server/chunks/[root-of-the-server]__1lz20wm._.js +0 -3
  90. package/app/.next/server/chunks/[root-of-the-server]__1zdh1mi._.js +0 -3
  91. package/app/.next/server/chunks/[root-of-the-server]__20epvou._.js +0 -3
  92. package/app/.next/static/chunks/0slbmtcht4v2w.css +0 -1
  93. package/app/.next/static/chunks/279fmcw99x0e9.js +0 -6
  94. /package/app/.next/static/{ji6govOHS2spXukBPOyFL → 7UozMfIH3Lk8P6-UWWQXF}/_buildManifest.js +0 -0
  95. /package/app/.next/static/{ji6govOHS2spXukBPOyFL → 7UozMfIH3Lk8P6-UWWQXF}/_clientMiddlewareManifest.js +0 -0
  96. /package/app/.next/static/{ji6govOHS2spXukBPOyFL → 7UozMfIH3Lk8P6-UWWQXF}/_ssgManifest.js +0 -0
package/cli/cli.mjs CHANGED
@@ -3329,6 +3329,10 @@ function readJsonl(filePath) {
3329
3329
  }
3330
3330
  return out;
3331
3331
  }
3332
+ function readJsonlRecord(filePath, index) {
3333
+ if (!/^\d+$/.test(index)) return null;
3334
+ return readJsonl(filePath)[Number(index)] ?? null;
3335
+ }
3332
3336
  function walkFiles(dir, match, maxDepth = 6) {
3333
3337
  if (maxDepth < 0) return [];
3334
3338
  const out = [];
@@ -3377,12 +3381,16 @@ function findGitRoot(cwd) {
3377
3381
  gitRoots.set(cwd, root);
3378
3382
  return root;
3379
3383
  }
3384
+ function runtimePlatform() {
3385
+ return globalThis.process?.platform ?? "linux";
3386
+ }
3380
3387
  function appDataDir(appName) {
3381
3388
  const home = os.homedir();
3382
- if (process.platform === "darwin") {
3389
+ const platform = runtimePlatform();
3390
+ if (platform === "darwin") {
3383
3391
  return path.join(home, "Library", "Application Support", appName);
3384
3392
  }
3385
- if (process.platform === "win32") {
3393
+ if (platform === "win32") {
3386
3394
  return path.join(process.env.APPDATA ?? path.join(home, "AppData", "Roaming"), appName);
3387
3395
  }
3388
3396
  return path.join(process.env.XDG_CONFIG_HOME ?? path.join(home, ".config"), appName);
@@ -3399,6 +3407,86 @@ function capText(value) {
3399
3407
  if (text.length <= TEXT_CAP) return { text, chars: text.length, truncated: false };
3400
3408
  return { text: `${text.slice(0, TEXT_CAP)}\u2026`, chars: text.length, truncated: true };
3401
3409
  }
3410
+ var TEXT_PARTS = /^(text|input_text|output_text)$/;
3411
+ var DATA_URI = /^data:([^;,]+)?(?:;[^,]*)?,/;
3412
+ function base64Bytes(data) {
3413
+ const padding = data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0;
3414
+ return Math.max(0, Math.floor(data.length * 3 / 4) - padding);
3415
+ }
3416
+ function imageOf(part) {
3417
+ if (part.type === "image") {
3418
+ const source = part.source ?? {};
3419
+ return {
3420
+ mediaType: source.media_type,
3421
+ bytes: typeof source.data === "string" ? base64Bytes(source.data) : void 0
3422
+ };
3423
+ }
3424
+ if (part.type === "input_image" || part.type === "image_url") {
3425
+ const url = part.image_url;
3426
+ const href = typeof url === "string" ? url : url?.url ?? "";
3427
+ const uri = DATA_URI.exec(href);
3428
+ if (!uri) return {};
3429
+ return { mediaType: uri[1], bytes: base64Bytes(href.slice(uri[0].length)) };
3430
+ }
3431
+ return null;
3432
+ }
3433
+ function indentJson(text) {
3434
+ const trimmed = text.trim();
3435
+ if (!/^[{[]/.test(trimmed) || !/[}\]]$/.test(trimmed)) return null;
3436
+ const parsed = parseJson(trimmed);
3437
+ return parsed && typeof parsed === "object" ? JSON.stringify(parsed, null, 2) : null;
3438
+ }
3439
+ function toolResult(raw) {
3440
+ const images = [];
3441
+ let format = "text";
3442
+ let text;
3443
+ let chars;
3444
+ if (typeof raw === "string" || raw == null) {
3445
+ text = raw ?? "";
3446
+ chars = text.length;
3447
+ } else if (Array.isArray(raw)) {
3448
+ const texts = [];
3449
+ for (const part of raw) {
3450
+ if (typeof part === "string") {
3451
+ texts.push(part);
3452
+ continue;
3453
+ }
3454
+ const p = part ?? {};
3455
+ const image = imageOf(p);
3456
+ if (image) images.push(image);
3457
+ else if (typeof p.text === "string" && (!p.type || TEXT_PARTS.test(String(p.type)))) texts.push(p.text);
3458
+ else if (p.type === "tool_reference") texts.push(`[tool reference: ${String(p.tool_name ?? "")}]`);
3459
+ else texts.push(JSON.stringify(p));
3460
+ }
3461
+ text = texts.join("\n");
3462
+ chars = text.length;
3463
+ } else {
3464
+ text = JSON.stringify(raw, null, 2);
3465
+ chars = JSON.stringify(raw).length;
3466
+ format = "json";
3467
+ }
3468
+ if (format === "text") {
3469
+ const indented = indentJson(text);
3470
+ if (indented !== null) {
3471
+ text = indented;
3472
+ format = "json";
3473
+ }
3474
+ }
3475
+ const capped = capText(text);
3476
+ return {
3477
+ result: {
3478
+ content: capped.text,
3479
+ format,
3480
+ ...images.length > 0 ? { images } : {}
3481
+ },
3482
+ resultChars: chars,
3483
+ truncated: capped.truncated
3484
+ };
3485
+ }
3486
+ function argumentChars(args) {
3487
+ if (typeof args === "string") return args.length;
3488
+ return args == null ? 0 : JSON.stringify(args)?.length ?? 0;
3489
+ }
3402
3490
  function isoFromMs(ms) {
3403
3491
  return typeof ms === "number" && Number.isFinite(ms) && ms > 0 ? new Date(ms).toISOString() : "";
3404
3492
  }
@@ -3978,9 +4066,18 @@ function normalize(raw) {
3978
4066
  const data = { ...raw.data };
3979
4067
  if (raw.type === "tool.execution_complete" || raw.type === "external_tool.completed") {
3980
4068
  const result = data.result;
3981
- data.resultChars = String(result?.content ?? "").length;
4069
+ const error = data.error?.message;
4070
+ const shaped = toolResult(result?.content ?? error);
4071
+ const detail = result?.detailedContent;
4072
+ if (typeof detail === "string" && detail.trim() && detail !== result?.content) {
4073
+ shaped.result.detail = capText(detail).text;
4074
+ }
4075
+ Object.assign(data, shaped);
3982
4076
  data.toolName = data.toolName ?? data.name;
3983
4077
  }
4078
+ if (raw.type === "tool.execution_start" || raw.type === "external_tool.requested") {
4079
+ data.argumentChars = argumentChars(data.arguments);
4080
+ }
3984
4081
  if (raw.type === "session.shutdown") {
3985
4082
  const usage = usageFromShutdown(data);
3986
4083
  data.sessionTotals = {
@@ -4000,7 +4097,7 @@ function normalize(raw) {
4000
4097
  }
4001
4098
  function readEvents(id) {
4002
4099
  const eventsPath = path3.join(SESSION_STATE_DIR, id, "events.jsonl");
4003
- const events = readJsonl(eventsPath).filter((e) => e && typeof e.type === "string").map(normalize);
4100
+ const events = readJsonl(eventsPath).map((e, index) => e && typeof e.type === "string" ? { ...e, source: String(index) } : null).filter((e) => e !== null).map(normalize);
4004
4101
  const lastShutdown = events.findLast((e) => e.type === "session.shutdown");
4005
4102
  if (lastShutdown) {
4006
4103
  lastShutdown.data.billedUsage = billedUsageFromShutdown(lastShutdown.data);
@@ -4085,6 +4182,10 @@ var copilotProvider = {
4085
4182
  storagePath: sessionDir
4086
4183
  };
4087
4184
  },
4185
+ getRecord(id, source) {
4186
+ if (id.includes("/") || id.includes("..")) return null;
4187
+ return readJsonlRecord(path3.join(SESSION_STATE_DIR, id, "events.jsonl"), source);
4188
+ },
4088
4189
  listLogs() {
4089
4190
  return safeReaddir(LOGS_DIR).filter((f) => f.endsWith(".log")).map((name) => {
4090
4191
  const p = path3.join(LOGS_DIR, name);
@@ -4139,7 +4240,8 @@ var ATTACHMENT_LABELS = {
4139
4240
  command_permissions: "Command permissions",
4140
4241
  remote_session_change: "Remote session",
4141
4242
  file: "Attached file",
4142
- image: "Image"
4243
+ image: "Image",
4244
+ synthetic: "Injected context"
4143
4245
  };
4144
4246
  function joinText(value) {
4145
4247
  if (Array.isArray(value)) return value.map((v) => joinText(v)).join("\n");
@@ -4159,6 +4261,7 @@ function describeAttachment(attachment) {
4159
4261
  case "total_tokens_reminder":
4160
4262
  case "batching_reminder_sent":
4161
4263
  case "silent_turn_reminder":
4264
+ case "synthetic":
4162
4265
  case "model":
4163
4266
  body = joinText(attachment.text);
4164
4267
  break;
@@ -4170,7 +4273,9 @@ function describeAttachment(attachment) {
4170
4273
  body = joinText(attachment.snapshot);
4171
4274
  break;
4172
4275
  case "session_context":
4173
- body = Object.values(attachment.context ?? {}).map((v) => joinText(v)).join("\n\n");
4276
+ body = Object.values(
4277
+ attachment.context ?? {}
4278
+ ).map((v) => joinText(v)).join("\n\n");
4174
4279
  break;
4175
4280
  case "deferred_tools_record": {
4176
4281
  const entries = attachment.entries ?? [];
@@ -4267,7 +4372,9 @@ function systemPromptOf(attachment) {
4267
4372
  return sections.filter(Boolean).join("\n\n");
4268
4373
  }
4269
4374
  function describeToolDefinitions(tools, chars) {
4270
- const names = tools.map((tool) => String(tool?.name ?? "tool"));
4375
+ const names = tools.map(
4376
+ (tool) => String(tool?.name ?? "tool")
4377
+ );
4271
4378
  return {
4272
4379
  attachmentType: "tool_definitions",
4273
4380
  label: ATTACHMENT_LABELS.tool_definitions,
@@ -4281,8 +4388,10 @@ function describeToolDefinitions(tools, chars) {
4281
4388
  }
4282
4389
  function instructionFilesText(attachment) {
4283
4390
  const files = attachment.files ?? [];
4284
- return files.map((file) => `# ${file.path ?? "instructions"}${file.type ? ` (${file.type})` : ""}
4285
- ${file.content ?? ""}`).join("\n\n");
4391
+ return files.map(
4392
+ (file) => `# ${file.path ?? "instructions"}${file.type ? ` (${file.type})` : ""}
4393
+ ${file.content ?? ""}`
4394
+ ).join("\n\n");
4286
4395
  }
4287
4396
  function parseClaudeTranscript(filePath, provider = "claude", id = path4.basename(filePath, ".jsonl")) {
4288
4397
  const records = readJsonl(filePath);
@@ -4298,16 +4407,19 @@ function parseClaudeTranscript(filePath, provider = "claude", id = path4.basenam
4298
4407
  let seq2 = 0;
4299
4408
  const hasOwnRecords = records.some((rec) => rec.sessionId === id);
4300
4409
  let inherited = false;
4410
+ let recordIndex = 0;
4301
4411
  const push = (timestamp2, type2, data, uuid, parentUuid) => {
4302
4412
  events.push({
4303
4413
  type: type2,
4304
4414
  data: inherited ? { ...data, inherited: true } : data,
4305
4415
  id: uuid ? `${uuid}:${seq2++}` : `claude-${seq2++}`,
4306
4416
  timestamp: timestamp2,
4307
- parentId: parentUuid ?? null
4417
+ parentId: parentUuid ?? null,
4418
+ source: String(recordIndex)
4308
4419
  });
4309
4420
  };
4310
- for (const rec of records) {
4421
+ for (const [index, rec] of records.entries()) {
4422
+ recordIndex = index;
4311
4423
  const ts = rec.timestamp ?? lastTimestamp;
4312
4424
  if (rec.timestamp) {
4313
4425
  if (!firstTimestamp) firstTimestamp = rec.timestamp;
@@ -4338,16 +4450,28 @@ function parseClaudeTranscript(filePath, provider = "claude", id = path4.basenam
4338
4450
  continue;
4339
4451
  case "mode":
4340
4452
  case "permission-mode":
4341
- push(ts, "session.mode_change", {
4342
- mode: rec.mode ?? rec.permissionMode
4343
- }, rec.uuid, rec.parentUuid);
4453
+ push(
4454
+ ts,
4455
+ "session.mode_change",
4456
+ {
4457
+ mode: rec.mode ?? rec.permissionMode
4458
+ },
4459
+ rec.uuid,
4460
+ rec.parentUuid
4461
+ );
4344
4462
  continue;
4345
4463
  case "cost-state":
4346
- push(ts, "session.usage_checkpoint", {
4347
- totalCostUSD: rec.totalCostUSD,
4348
- apiDurationMs: rec.totalAPIDuration,
4349
- sessionTotals: { apiDurationMs: rec.totalAPIDuration ?? 0 }
4350
- }, rec.uuid, rec.parentUuid);
4464
+ push(
4465
+ ts,
4466
+ "session.usage_checkpoint",
4467
+ {
4468
+ totalCostUSD: rec.totalCostUSD,
4469
+ apiDurationMs: rec.totalAPIDuration,
4470
+ sessionTotals: { apiDurationMs: rec.totalAPIDuration ?? 0 }
4471
+ },
4472
+ rec.uuid,
4473
+ rec.parentUuid
4474
+ );
4351
4475
  continue;
4352
4476
  case "attachment": {
4353
4477
  const attachment = rec.attachment ?? {};
@@ -4361,12 +4485,18 @@ function parseClaudeTranscript(filePath, provider = "claude", id = path4.basenam
4361
4485
 
4362
4486
  ${prompt}` : prompt
4363
4487
  );
4364
- push(ts, "system.message", {
4365
- role: "system_prompt",
4366
- content: text,
4367
- charLength: chars,
4368
- truncated
4369
- }, rec.uuid, rec.parentUuid);
4488
+ push(
4489
+ ts,
4490
+ "system.message",
4491
+ {
4492
+ role: "system_prompt",
4493
+ content: text,
4494
+ charLength: chars,
4495
+ truncated
4496
+ },
4497
+ rec.uuid,
4498
+ rec.parentUuid
4499
+ );
4370
4500
  }
4371
4501
  if (Array.isArray(attachment.tools)) {
4372
4502
  const serialized = JSON.stringify(attachment.tools);
@@ -4384,60 +4514,126 @@ ${prompt}` : prompt
4384
4514
  continue;
4385
4515
  }
4386
4516
  if (attachment.type === "instructions") {
4387
- const { text, chars, truncated } = capText(instructionFilesText(attachment));
4388
- push(ts, "system.message", {
4389
- role: "instructions",
4390
- content: text,
4391
- charLength: chars,
4392
- truncated
4393
- }, rec.uuid, rec.parentUuid);
4517
+ const { text, chars, truncated } = capText(
4518
+ instructionFilesText(attachment)
4519
+ );
4520
+ push(
4521
+ ts,
4522
+ "system.message",
4523
+ {
4524
+ role: "instructions",
4525
+ content: text,
4526
+ charLength: chars,
4527
+ truncated
4528
+ },
4529
+ rec.uuid,
4530
+ rec.parentUuid
4531
+ );
4394
4532
  continue;
4395
4533
  }
4396
- push(ts, "context.attachment", describeAttachment(attachment), rec.uuid, rec.parentUuid);
4534
+ push(
4535
+ ts,
4536
+ "context.attachment",
4537
+ describeAttachment(attachment),
4538
+ rec.uuid,
4539
+ rec.parentUuid
4540
+ );
4397
4541
  continue;
4398
4542
  }
4399
4543
  case "system": {
4400
4544
  if (rec.subtype === "api_error") {
4401
- push(ts, "session.error", {
4402
- message: rec.error?.message ?? "API error",
4403
- detail: rec.error?.formatted ?? "",
4404
- level: rec.level,
4405
- error: rec.error
4406
- }, rec.uuid, rec.parentUuid);
4545
+ push(
4546
+ ts,
4547
+ "session.error",
4548
+ {
4549
+ message: rec.error?.message ?? "API error",
4550
+ detail: rec.error?.formatted ?? "",
4551
+ level: rec.level,
4552
+ error: rec.error
4553
+ },
4554
+ rec.uuid,
4555
+ rec.parentUuid
4556
+ );
4407
4557
  } else if (rec.subtype === "stop_hook_summary") {
4408
- push(ts, "hook.end", {
4409
- hookType: "Stop",
4410
- hookCount: rec.hookCount,
4411
- success: (rec.hookErrors?.length ?? 0) === 0
4412
- }, rec.uuid, rec.parentUuid);
4558
+ push(
4559
+ ts,
4560
+ "hook.end",
4561
+ {
4562
+ hookType: "Stop",
4563
+ hookCount: rec.hookCount,
4564
+ success: (rec.hookErrors?.length ?? 0) === 0
4565
+ },
4566
+ rec.uuid,
4567
+ rec.parentUuid
4568
+ );
4413
4569
  } else if (rec.subtype === "compact_boundary") {
4414
- push(ts, "session.compaction_start", {
4415
- trigger: rec.compactMetadata?.trigger,
4416
- preTokens: rec.compactMetadata?.preTokens
4417
- }, rec.uuid, rec.parentUuid);
4570
+ push(
4571
+ ts,
4572
+ "session.compaction_start",
4573
+ {
4574
+ trigger: rec.compactMetadata?.trigger,
4575
+ preTokens: rec.compactMetadata?.preTokens
4576
+ },
4577
+ rec.uuid,
4578
+ rec.parentUuid
4579
+ );
4418
4580
  } else if (rec.subtype === "turn_duration") {
4419
- push(ts, "assistant.turn_end", {
4420
- durationMs: rec.durationMs
4421
- }, rec.uuid, rec.parentUuid);
4581
+ push(
4582
+ ts,
4583
+ "assistant.turn_end",
4584
+ {
4585
+ durationMs: rec.durationMs
4586
+ },
4587
+ rec.uuid,
4588
+ rec.parentUuid
4589
+ );
4422
4590
  } else {
4423
4591
  const { text, chars, truncated } = capText(rec.content);
4424
- push(ts, "system.message", {
4425
- role: rec.subtype ?? "system",
4426
- content: text,
4427
- charLength: chars,
4428
- truncated
4429
- }, rec.uuid, rec.parentUuid);
4592
+ push(
4593
+ ts,
4594
+ "system.message",
4595
+ {
4596
+ role: rec.subtype ?? "system",
4597
+ content: text,
4598
+ charLength: chars,
4599
+ truncated
4600
+ },
4601
+ rec.uuid,
4602
+ rec.parentUuid
4603
+ );
4430
4604
  }
4431
4605
  continue;
4432
4606
  }
4433
4607
  case "user": {
4434
4608
  const content = rec.message?.content;
4435
4609
  const pushText = (raw) => {
4610
+ if (rec.isMeta === true) {
4611
+ push(
4612
+ ts,
4613
+ "context.attachment",
4614
+ describeAttachment({ type: "synthetic", text: raw }),
4615
+ rec.uuid,
4616
+ rec.parentUuid
4617
+ );
4618
+ return;
4619
+ }
4436
4620
  const { text } = capText(raw);
4437
4621
  if (INTERRUPTED.test(text)) {
4438
- push(ts, "session.turn_aborted", { reason: "interrupted", message: text }, rec.uuid, rec.parentUuid);
4622
+ push(
4623
+ ts,
4624
+ "session.turn_aborted",
4625
+ { reason: "interrupted", message: text },
4626
+ rec.uuid,
4627
+ rec.parentUuid
4628
+ );
4439
4629
  } else {
4440
- push(ts, "user.message", { content: text }, rec.uuid, rec.parentUuid);
4630
+ push(
4631
+ ts,
4632
+ "user.message",
4633
+ { content: text },
4634
+ rec.uuid,
4635
+ rec.parentUuid
4636
+ );
4441
4637
  }
4442
4638
  };
4443
4639
  if (typeof content === "string") {
@@ -4448,10 +4644,6 @@ ${prompt}` : prompt
4448
4644
  if (block.type === "text") {
4449
4645
  pushText(block.text);
4450
4646
  } else if (block.type === "tool_result") {
4451
- const raw = block.content ?? rec.toolUseResult;
4452
- const { text, chars, truncated } = capText(
4453
- typeof raw === "string" ? raw : raw
4454
- );
4455
4647
  const toolName = toolNames.get(block.tool_use_id ?? "") ?? "tool";
4456
4648
  push(
4457
4649
  ts,
@@ -4460,15 +4652,19 @@ ${prompt}` : prompt
4460
4652
  toolName,
4461
4653
  toolCallId: block.tool_use_id,
4462
4654
  success: block.is_error !== true,
4463
- resultChars: chars,
4464
- truncated,
4465
- result: { content: text }
4655
+ ...toolResult(block.content ?? rec.toolUseResult)
4466
4656
  },
4467
4657
  rec.uuid,
4468
4658
  rec.parentUuid
4469
4659
  );
4470
4660
  } else if (block.type === "image") {
4471
- push(ts, "context.attachment", describeAttachment({ type: "image" }), rec.uuid, rec.parentUuid);
4661
+ push(
4662
+ ts,
4663
+ "context.attachment",
4664
+ describeAttachment({ type: "image" }),
4665
+ rec.uuid,
4666
+ rec.parentUuid
4667
+ );
4472
4668
  }
4473
4669
  }
4474
4670
  continue;
@@ -4476,7 +4672,8 @@ ${prompt}` : prompt
4476
4672
  case "assistant": {
4477
4673
  const message = rec.message;
4478
4674
  if (!message) continue;
4479
- if (message.model && message.model !== "<synthetic>") meta.model = message.model;
4675
+ if (message.model && message.model !== "<synthetic>")
4676
+ meta.model = message.model;
4480
4677
  const usage = message.usage;
4481
4678
  const messageKey = message.id ?? rec.requestId ?? rec.uuid ?? "";
4482
4679
  let usageData = {};
@@ -4507,20 +4704,32 @@ ${prompt}` : prompt
4507
4704
  };
4508
4705
  for (const block of blocks) {
4509
4706
  if (block.type === "text") {
4510
- push(ts, "assistant.message", {
4511
- content: capText(block.text).text,
4512
- model: message.model,
4513
- effort: rec.effort,
4514
- requestId: rec.requestId,
4515
- ...attachUsage()
4516
- }, rec.uuid, rec.parentUuid);
4707
+ push(
4708
+ ts,
4709
+ "assistant.message",
4710
+ {
4711
+ content: capText(block.text).text,
4712
+ model: message.model,
4713
+ effort: rec.effort,
4714
+ requestId: rec.requestId,
4715
+ ...attachUsage()
4716
+ },
4717
+ rec.uuid,
4718
+ rec.parentUuid
4719
+ );
4517
4720
  } else if (block.type === "thinking") {
4518
- push(ts, "assistant.thinking", {
4519
- content: capText(block.thinking).text,
4520
- charLength: (block.thinking ?? "").length,
4521
- model: message.model,
4522
- ...attachUsage()
4523
- }, rec.uuid, rec.parentUuid);
4721
+ push(
4722
+ ts,
4723
+ "assistant.thinking",
4724
+ {
4725
+ content: capText(block.thinking).text,
4726
+ charLength: (block.thinking ?? "").length,
4727
+ model: message.model,
4728
+ ...attachUsage()
4729
+ },
4730
+ rec.uuid,
4731
+ rec.parentUuid
4732
+ );
4524
4733
  } else if (block.type === "tool_use") {
4525
4734
  const name = block.name ?? "tool";
4526
4735
  if (block.id) toolNames.set(block.id, name);
@@ -4535,6 +4744,7 @@ ${prompt}` : prompt
4535
4744
  toolName: name,
4536
4745
  toolCallId: block.id,
4537
4746
  arguments: block.input ?? {},
4747
+ argumentChars: argumentChars(block.input),
4538
4748
  model: message.model,
4539
4749
  ...attachUsage()
4540
4750
  },
@@ -4544,11 +4754,17 @@ ${prompt}` : prompt
4544
4754
  }
4545
4755
  }
4546
4756
  if (!usageAttached && Object.keys(usageData).length > 0) {
4547
- push(ts, "assistant.message", {
4548
- content: "",
4549
- model: message.model,
4550
- ...usageData
4551
- }, rec.uuid, rec.parentUuid);
4757
+ push(
4758
+ ts,
4759
+ "assistant.message",
4760
+ {
4761
+ content: "",
4762
+ model: message.model,
4763
+ ...usageData
4764
+ },
4765
+ rec.uuid,
4766
+ rec.parentUuid
4767
+ );
4552
4768
  }
4553
4769
  continue;
4554
4770
  }
@@ -4576,7 +4792,8 @@ ${prompt}` : prompt
4576
4792
  }
4577
4793
  function parseFile(filePath, projectDir) {
4578
4794
  const parsed = parseClaudeTranscript(filePath);
4579
- if (!parsed.meta.cwd) parsed.meta.cwd = decodeProjectDir(path4.basename(projectDir));
4795
+ if (!parsed.meta.cwd)
4796
+ parsed.meta.cwd = decodeProjectDir(path4.basename(projectDir));
4580
4797
  return parsed;
4581
4798
  }
4582
4799
  var summarize = createFileCache((filePath) => {
@@ -4640,6 +4857,10 @@ var claudeProvider = {
4640
4857
  storagePath: found.filePath
4641
4858
  };
4642
4859
  },
4860
+ getRecord(id, source) {
4861
+ const found = findSessionFile(id);
4862
+ return found ? readJsonlRecord(found.filePath, source) : null;
4863
+ },
4643
4864
  listLogs() {
4644
4865
  return [];
4645
4866
  },
@@ -4703,16 +4924,19 @@ function parseFile2(filePath) {
4703
4924
  let billedOutput = 0;
4704
4925
  let localModel = false;
4705
4926
  const toolNames = /* @__PURE__ */ new Map();
4927
+ let lineIndex = 0;
4706
4928
  const push = (timestamp2, type2, data) => {
4707
4929
  events.push({
4708
4930
  type: type2,
4709
4931
  data,
4710
4932
  id: `codex-${seq2++}`,
4711
4933
  timestamp: timestamp2,
4712
- parentId: null
4934
+ parentId: null,
4935
+ source: String(lineIndex)
4713
4936
  });
4714
4937
  };
4715
- for (const line of lines) {
4938
+ for (const [index, line] of lines.entries()) {
4939
+ lineIndex = index;
4716
4940
  const ts = line.timestamp ?? lastTimestamp;
4717
4941
  if (line.timestamp) {
4718
4942
  if (!firstTimestamp) firstTimestamp = line.timestamp;
@@ -4916,14 +5140,14 @@ function parseFile2(filePath) {
4916
5140
  break;
4917
5141
  case "mcp_tool_call_end": {
4918
5142
  const invocation = p.invocation;
4919
- const capped = capText(p.result);
5143
+ const outcome = p.result;
4920
5144
  push(ts, "external_tool.completed", {
4921
5145
  toolName: invocation ? `${invocation.server}__${invocation.tool}` : "mcp tool",
4922
5146
  toolCallId: p.call_id,
4923
5147
  arguments: invocation?.arguments,
4924
- resultChars: capped.chars,
4925
- success: Boolean(p.result?.Ok),
4926
- result: { content: capped.text }
5148
+ argumentChars: argumentChars(invocation?.arguments),
5149
+ success: Boolean(outcome?.Ok),
5150
+ ...toolResult(outcome?.Ok?.content ?? outcome?.Ok ?? outcome?.Err ?? p.result)
4927
5151
  });
4928
5152
  break;
4929
5153
  }
@@ -5019,21 +5243,20 @@ function parseFile2(filePath) {
5019
5243
  toolName,
5020
5244
  toolCallId: p.call_id,
5021
5245
  status: p.status,
5022
- arguments: args
5246
+ arguments: args,
5247
+ argumentChars: argumentChars(rawArgs)
5023
5248
  });
5024
5249
  break;
5025
5250
  }
5026
5251
  case "custom_tool_call_output":
5027
5252
  case "function_call_output":
5028
5253
  case "local_shell_call_output": {
5029
- const capped = capText(flattenContent(p.output));
5254
+ const output = toolResult(p.output);
5030
5255
  push(ts, "tool.execution_complete", {
5031
5256
  toolName: toolNames.get(String(p.call_id)) ?? "exec",
5032
5257
  toolCallId: p.call_id,
5033
- success: !FAILED_EXIT.test(capped.text),
5034
- resultChars: capped.chars,
5035
- truncated: capped.truncated,
5036
- result: { content: capped.text }
5258
+ success: !FAILED_EXIT.test(output.result.content),
5259
+ ...output
5037
5260
  });
5038
5261
  break;
5039
5262
  }
@@ -5107,6 +5330,11 @@ var codexProvider = {
5107
5330
  storagePath: filePath
5108
5331
  };
5109
5332
  },
5333
+ getRecord(id, source) {
5334
+ if (id.includes("/") || id.includes("..")) return null;
5335
+ const filePath = rolloutFiles().find((f) => path5.basename(f).endsWith(`-${id}.jsonl`));
5336
+ return filePath ? readJsonlRecord(filePath, source) : null;
5337
+ },
5110
5338
  listLogs() {
5111
5339
  return [];
5112
5340
  },
@@ -5243,25 +5471,24 @@ function parseFile3(filePath) {
5243
5471
  const detail = part.toolSpecificData;
5244
5472
  if (call?.at && call.at > at) at = call.at;
5245
5473
  const toolName = call?.name ?? part.toolId ?? "tool";
5474
+ const args = parseJson(call?.arguments) ?? (detail?.kind === "terminal" ? { command: detail.commandLine?.original } : detail?.kind === "subagent" ? { description: detail.description, agent: detail.agentName, prompt: detail.prompt } : { description: textOf(part.invocationMessage) });
5246
5475
  push(at, "tool.execution_start", {
5247
5476
  toolName,
5248
5477
  toolCallId: callId,
5249
- arguments: parseJson(call?.arguments) ?? (detail?.kind === "terminal" ? { command: detail.commandLine?.original } : detail?.kind === "subagent" ? { description: detail.description, agent: detail.agentName, prompt: detail.prompt } : { description: textOf(part.invocationMessage) }),
5478
+ arguments: args,
5479
+ argumentChars: argumentChars(call?.arguments ?? args),
5250
5480
  model: requestModel
5251
5481
  }, requestId);
5252
5482
  const finishedAt = detail?.terminalCommandState?.timestamp;
5253
5483
  if (finishedAt && finishedAt > at) at = finishedAt;
5254
5484
  const exitCode = detail?.terminalCommandState?.exitCode;
5255
- const { text, chars, truncated } = capText(
5256
- callId && results.get(callId) || detail?.result || detail?.terminalCommandOutput?.text?.replace(ANSI, "") || textOf(part.pastTenseMessage)
5257
- );
5258
5485
  push(at, "tool.execution_complete", {
5259
5486
  toolName,
5260
5487
  toolCallId: callId,
5261
5488
  success: typeof exitCode !== "number" || exitCode === 0,
5262
- resultChars: chars,
5263
- truncated,
5264
- result: { content: text }
5489
+ ...toolResult(
5490
+ callId && results.get(callId) || detail?.result || detail?.terminalCommandOutput?.text?.replace(ANSI, "") || textOf(part.pastTenseMessage)
5491
+ )
5265
5492
  }, requestId);
5266
5493
  } else if (EDIT_PARTS.has(part.kind)) {
5267
5494
  const file = part.uri?.fsPath ?? part.uri?.path;
@@ -5499,6 +5726,10 @@ var coworkProvider = {
5499
5726
  storagePath: session.descriptorPath.slice(0, -".json".length)
5500
5727
  };
5501
5728
  },
5729
+ getRecord(id, source) {
5730
+ const session = sessions().find((s) => s.id === id);
5731
+ return session ? readJsonlRecord(session.transcript, source) : null;
5732
+ },
5502
5733
  listLogs() {
5503
5734
  return [];
5504
5735
  },
@@ -5652,20 +5883,16 @@ function parse(session, messages, parts) {
5652
5883
  toolName,
5653
5884
  toolCallId: part.callID,
5654
5885
  arguments: args,
5886
+ argumentChars: argumentChars(state.input),
5655
5887
  model: messageModel,
5656
5888
  ...takeUsage()
5657
5889
  }, row.id);
5658
5890
  if (state.status !== "completed" && state.status !== "error") break;
5659
- const { text, chars, truncated } = capText(
5660
- state.status === "error" ? state.error : state.output
5661
- );
5662
5891
  push(isoFromMs(state.time?.end) || ts, "tool.execution_complete", {
5663
5892
  toolName,
5664
5893
  toolCallId: part.callID,
5665
5894
  success: state.status === "completed",
5666
- resultChars: chars,
5667
- truncated,
5668
- result: { content: text }
5895
+ ...toolResult(state.status === "error" ? state.error : state.output)
5669
5896
  }, row.id);
5670
5897
  break;
5671
5898
  }
@@ -5872,18 +6099,16 @@ function parse2(session, messages) {
5872
6099
  toolName,
5873
6100
  toolCallId,
5874
6101
  arguments: args,
6102
+ argumentChars: argumentChars(call.function?.arguments),
5875
6103
  model: session.model
5876
6104
  });
5877
6105
  }
5878
6106
  } else if (message.role === "tool") {
5879
- const { text, chars, truncated } = capText(content);
5880
6107
  push(ts, "tool.execution_complete", {
5881
6108
  toolName: message.tool_name ?? toolNames.get(message.tool_call_id ?? "") ?? "tool",
5882
6109
  toolCallId: message.tool_call_id,
5883
6110
  success: !toolFailed(content),
5884
- resultChars: chars,
5885
- truncated,
5886
- result: { content: text }
6111
+ ...toolResult(content)
5887
6112
  });
5888
6113
  } else if (content) {
5889
6114
  const { text, chars, truncated } = capText(content);
@@ -6076,7 +6301,7 @@ function parseFile4(filePath) {
6076
6301
  let lastTimestamp = metadata.startTime ?? "";
6077
6302
  const push = (timestamp2, type2, data, parentId) => {
6078
6303
  if (timestamp2 > lastTimestamp) lastTimestamp = timestamp2;
6079
- events.push({ type: type2, data, id: `${parentId}:${events.length}`, timestamp: timestamp2, parentId });
6304
+ events.push({ type: type2, data, id: `${parentId}:${events.length}`, timestamp: timestamp2, parentId, source: parentId });
6080
6305
  };
6081
6306
  for (const message of messages) {
6082
6307
  const ts = message.timestamp ?? lastTimestamp;
@@ -6149,20 +6374,18 @@ function parseFile4(filePath) {
6149
6374
  toolName,
6150
6375
  toolCallId: call.id,
6151
6376
  arguments: args,
6377
+ argumentChars: argumentChars(call.args),
6152
6378
  model: message.model,
6153
6379
  ...takeUsage()
6154
6380
  }, message.id);
6155
6381
  if (!FINISHED.test(call.status ?? "")) continue;
6156
- const result = capText(toolOutput(call));
6157
6382
  push(callTime, "tool.execution_complete", {
6158
6383
  toolName,
6159
6384
  toolCallId: call.id,
6160
6385
  // The user turning a call down is not the tool failing.
6161
6386
  success: call.status !== "error",
6162
6387
  cancelled: call.status === "cancelled" || void 0,
6163
- resultChars: result.chars,
6164
- truncated: result.truncated,
6165
- result: { content: result.text }
6388
+ ...toolResult(toolOutput(call))
6166
6389
  }, message.id);
6167
6390
  }
6168
6391
  if (Object.keys(usage).length > 0) {
@@ -6246,6 +6469,10 @@ var geminiProvider = {
6246
6469
  storagePath: filePath
6247
6470
  };
6248
6471
  },
6472
+ getRecord(id, source) {
6473
+ const filePath = listAll3().find(({ meta }) => meta.id === id)?.filePath;
6474
+ return filePath && replay(filePath).messages.find((message) => message.id === source) || null;
6475
+ },
6249
6476
  listLogs() {
6250
6477
  return [];
6251
6478
  },
@@ -6330,20 +6557,18 @@ function parseComposer(composer, bubbles) {
6330
6557
  toolName: tool.name,
6331
6558
  toolCallId: tool.toolCallId,
6332
6559
  arguments: args,
6560
+ argumentChars: argumentChars(tool.rawArgs || tool.params),
6333
6561
  model,
6334
6562
  ...takeUsage()
6335
6563
  }, id);
6336
6564
  if (FINISHED2.test(tool.status ?? "")) {
6337
- const result = capText(tool.result);
6338
6565
  push(isoFromMs(bubble.completedAtMs) || ts, "tool.execution_complete", {
6339
6566
  toolName: tool.name,
6340
6567
  toolCallId: tool.toolCallId,
6341
6568
  // The user turning a call down is not the tool failing.
6342
6569
  success: tool.status !== "error",
6343
6570
  cancelled: tool.status === "cancelled" || void 0,
6344
- resultChars: result.chars,
6345
- truncated: result.truncated,
6346
- result: { content: result.text }
6571
+ ...toolResult(tool.result)
6347
6572
  }, id);
6348
6573
  }
6349
6574
  }
@@ -6478,7 +6703,7 @@ function parseTranscript2(filePath) {
6478
6703
  const value = args[key];
6479
6704
  if (typeof value === "string" && EDIT_TOOLS5.test(toolName)) files.add(value);
6480
6705
  }
6481
- push("tool.execution_start", { toolName, arguments: args }, index);
6706
+ push("tool.execution_start", { toolName, arguments: args, argumentChars: argumentChars(part.input) }, index);
6482
6707
  continue;
6483
6708
  }
6484
6709
  if (part.type !== "text" || !part.text?.trim()) continue;