@koda-sl/baker-bridge 0.154.0-dev.db7cf2f5d → 0.154.0-dev.eb1bb1bf7

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 (74) hide show
  1. package/README.md +35 -3
  2. package/dist/hono/agent-session.d.ts +0 -1
  3. package/dist/hono/agent-session.d.ts.map +1 -1
  4. package/dist/hono/agent-session.js +0 -7
  5. package/dist/hono/agent-session.js.map +1 -1
  6. package/dist/hono/agent-session.test.js +1 -7
  7. package/dist/hono/agent-session.test.js.map +1 -1
  8. package/dist/hono/agent.d.ts.map +1 -1
  9. package/dist/hono/agent.js +10 -10
  10. package/dist/hono/agent.js.map +1 -1
  11. package/dist/hono/agent.test.js +24 -0
  12. package/dist/hono/agent.test.js.map +1 -1
  13. package/dist/hono/bridge-job.d.ts +20 -1
  14. package/dist/hono/bridge-job.d.ts.map +1 -1
  15. package/dist/hono/bridge-job.js +71 -6
  16. package/dist/hono/bridge-job.js.map +1 -1
  17. package/dist/hono/bridge-job.test.js +102 -0
  18. package/dist/hono/bridge-job.test.js.map +1 -1
  19. package/dist/hono/convex.d.ts +2 -1
  20. package/dist/hono/convex.d.ts.map +1 -1
  21. package/dist/hono/convex.js +4 -0
  22. package/dist/hono/convex.js.map +1 -1
  23. package/dist/hono/main-sync.d.ts +7 -0
  24. package/dist/hono/main-sync.d.ts.map +1 -1
  25. package/dist/hono/main-sync.js +39 -0
  26. package/dist/hono/main-sync.js.map +1 -1
  27. package/dist/hono/main-sync.test.js +29 -1
  28. package/dist/hono/main-sync.test.js.map +1 -1
  29. package/dist/hono/publish-handoff.d.ts +12 -1
  30. package/dist/hono/publish-handoff.d.ts.map +1 -1
  31. package/dist/hono/publish-handoff.js +86 -0
  32. package/dist/hono/publish-handoff.js.map +1 -1
  33. package/dist/hono/publish-handoff.test.js +76 -1
  34. package/dist/hono/publish-handoff.test.js.map +1 -1
  35. package/dist/hono/relay.d.ts +10 -1
  36. package/dist/hono/relay.d.ts.map +1 -1
  37. package/dist/hono/relay.js +4 -0
  38. package/dist/hono/relay.js.map +1 -1
  39. package/dist/hono/relay.test.js +11 -0
  40. package/dist/hono/relay.test.js.map +1 -1
  41. package/dist/hono/served-model.d.ts +23 -0
  42. package/dist/hono/served-model.d.ts.map +1 -0
  43. package/dist/hono/served-model.js +82 -0
  44. package/dist/hono/served-model.js.map +1 -0
  45. package/dist/hono/served-model.test.d.ts +2 -0
  46. package/dist/hono/served-model.test.d.ts.map +1 -0
  47. package/dist/hono/served-model.test.js +29 -0
  48. package/dist/hono/served-model.test.js.map +1 -0
  49. package/dist/hono/session-continuity.d.ts +24 -0
  50. package/dist/hono/session-continuity.d.ts.map +1 -1
  51. package/dist/hono/session-continuity.integration.test.js +201 -5
  52. package/dist/hono/session-continuity.integration.test.js.map +1 -1
  53. package/dist/hono/session-continuity.js +94 -7
  54. package/dist/hono/session-continuity.js.map +1 -1
  55. package/dist/hono/session-store.d.ts +2 -0
  56. package/dist/hono/session-store.d.ts.map +1 -1
  57. package/dist/hono/session-store.js +6 -0
  58. package/dist/hono/session-store.js.map +1 -1
  59. package/dist/hono/session-usage-baseline.d.ts +1 -0
  60. package/dist/hono/session-usage-baseline.d.ts.map +1 -1
  61. package/dist/hono/session-usage-baseline.js +17 -4
  62. package/dist/hono/session-usage-baseline.js.map +1 -1
  63. package/dist/hono/turn-events.d.ts +1 -1
  64. package/dist/hono/turn-events.d.ts.map +1 -1
  65. package/dist/hono/turn-events.js.map +1 -1
  66. package/dist/hono/types.d.ts +26 -0
  67. package/dist/hono/types.d.ts.map +1 -1
  68. package/dist/review/digest.d.ts +35 -0
  69. package/dist/review/digest.d.ts.map +1 -1
  70. package/dist/review/digest.js +171 -27
  71. package/dist/review/digest.js.map +1 -1
  72. package/dist/review/digest.test.js +130 -1
  73. package/dist/review/digest.test.js.map +1 -1
  74. package/package.json +1 -1
@@ -15,6 +15,7 @@
15
15
  * it could NOT find, so missing evidence is a fact before the model starts
16
16
  * rather than a silence it fills in.
17
17
  */
18
+ import { createHash } from "node:crypto";
18
19
  import { imageSize } from "image-size";
19
20
  import { languageMismatch, marketerWords, messageLanguage } from "./reply-language.js";
20
21
  /** Parse a `.jsonl` body, dropping lines that are not JSON rather than failing. */
@@ -289,6 +290,49 @@ function clip(text, max = TRUNCATE) {
289
290
  function currentTurn(acc) {
290
291
  return acc.turns[acc.turns.length - 1];
291
292
  }
293
+ /** One conversation inside a thread: the main agent, or one subagent. */
294
+ function conversationKey(threadId, agentId) {
295
+ return `${threadId ?? ""}\u0000${agentId ?? ""}`;
296
+ }
297
+ function isSubagentLine(line) {
298
+ return line.isSidechain === true || line.isSubagent === true;
299
+ }
300
+ /**
301
+ * The turn a line belongs to.
302
+ *
303
+ * A subagent's own file is read after the whole session file it was spawned
304
+ * in, so the turn open by then is that session's last one. Charged to it, a
305
+ * fan-out in turn 1 was listed under the re-render turn 2 — its refusals, its
306
+ * denials and its parent's closing recap as their intent. A subagent belongs to
307
+ * the last turn of its thread that began before the prompt that started it, or
308
+ * before the later prompt that continued it.
309
+ */
310
+ function turnIndexOf(acc, line) {
311
+ const current = currentTurn(acc)?.index;
312
+ if (!isSubagentLine(line)) {
313
+ return current;
314
+ }
315
+ const key = line.agentId === undefined ? undefined : conversationKey(line.threadId, line.agentId);
316
+ const known = key === undefined ? undefined : acc.subagentTurns.get(key);
317
+ if (known !== undefined) {
318
+ return known;
319
+ }
320
+ const at = stampOf(line.timestamp);
321
+ if (at === undefined) {
322
+ return current;
323
+ }
324
+ let spawned;
325
+ for (const turn of acc.turns) {
326
+ const began = stampOf(turn.timestamp);
327
+ if (turn.threadId === line.threadId && began !== undefined && began <= at) {
328
+ spawned = turn.index;
329
+ }
330
+ }
331
+ if (key !== undefined && spawned !== undefined) {
332
+ acc.subagentTurns.set(key, spawned);
333
+ }
334
+ return spawned ?? current;
335
+ }
292
336
  /**
293
337
  * Close the open turn at the latest line that still belongs to it.
294
338
  *
@@ -302,7 +346,8 @@ function currentTurn(acc) {
302
346
  */
303
347
  function noteTurnEnd(acc, line) {
304
348
  const at = stampOf(line.timestamp);
305
- const turn = currentTurn(acc);
349
+ const index = turnIndexOf(acc, line);
350
+ const turn = index === undefined ? undefined : acc.turns[index];
306
351
  if (at === undefined || turn === undefined) {
307
352
  return;
308
353
  }
@@ -430,16 +475,22 @@ function absorbFailure(acc, call, block) {
430
475
  }
431
476
  acc.failureMessages.set(call.toolUseId, clip(message, 400));
432
477
  const guard = namedGuard(message);
478
+ // A subagent's words, never the turn's: the turn's are its parent's.
479
+ const intent = call.isSidechain
480
+ ? acc.subagentWords.get(conversationKey(call.threadId, call.agentId))
481
+ : turn?.assistantText || undefined;
433
482
  acc.guards.push({
434
483
  turn: call.turn,
435
484
  ...(call.threadId === undefined ? {} : { threadId: call.threadId }),
436
485
  toolUseId: call.toolUseId,
437
486
  tool: call.tool,
438
487
  attempted: call.summary,
488
+ inputKey: call.inputKey,
439
489
  message: clip(message, 400),
440
490
  refusal: call.failureClass === "refused",
441
491
  ...(guard === undefined ? {} : { guard }),
442
- ...(turn?.assistantText ? { intent: clip(turn.assistantText, 300) } : {}),
492
+ ...(intent ? { intent: clip(intent, 300) } : {}),
493
+ ...(call.isSidechain && call.agentId !== undefined ? { agentId: call.agentId } : {}),
443
494
  repeats: 1,
444
495
  });
445
496
  }
@@ -490,6 +541,11 @@ function absorbUserLine(acc, line) {
490
541
  absorbToolResults(acc, line);
491
542
  const spoken = raw.replace(SYSTEM_REMINDER, "").trim();
492
543
  const isToolResult = blocks(line).some((block) => block.type === "tool_result");
544
+ if (isSubagentLine(line) && !isToolResult && spoken.length > 0) {
545
+ // A prompt to a subagent: its spawn, or a later turn continuing it (`SendMessage`
546
+ // keeps the id), so the turn it belongs to is read again from this line.
547
+ acc.subagentTurns.delete(conversationKey(line.threadId, line.agentId));
548
+ }
493
549
  if (line.isSidechain || line.isSubagent || isToolResult || spoken.length === 0) {
494
550
  return;
495
551
  }
@@ -509,6 +565,7 @@ function absorbUserLine(acc, line) {
509
565
  outputTokens: 0,
510
566
  cacheReadTokens: 0,
511
567
  cacheWriteTokens: 0,
568
+ subagent: { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0 },
512
569
  denials: 0,
513
570
  compacted: false,
514
571
  });
@@ -535,21 +592,25 @@ function absorbRequestUsage(acc, line, turnAt) {
535
592
  acc.countedRequests.add(requestId);
536
593
  }
537
594
  const usage = line.message?.usage ?? {};
595
+ const subagent = isSubagentLine(line);
538
596
  const request = {
539
597
  turn: turnAt,
540
598
  ...(line.message?.model === undefined ? {} : { model: line.message.model }),
599
+ subagent,
600
+ ...(subagent && line.agentId !== undefined ? { agentId: line.agentId } : {}),
541
601
  inputTokens: usage.input_tokens ?? 0,
542
602
  outputTokens: usage.output_tokens ?? 0,
543
603
  cacheReadTokens: usage.cache_read_input_tokens ?? 0,
544
604
  cacheWriteTokens: usage.cache_creation_input_tokens ?? 0,
545
605
  };
546
606
  acc.requests.push(request);
547
- const turn = currentTurn(acc);
607
+ const turn = acc.turns[turnAt];
548
608
  if (turn) {
549
- turn.inputTokens += request.inputTokens;
550
- turn.outputTokens += request.outputTokens;
551
- turn.cacheReadTokens += request.cacheReadTokens;
552
- turn.cacheWriteTokens += request.cacheWriteTokens;
609
+ const usage = subagent ? turn.subagent : turn;
610
+ usage.inputTokens += request.inputTokens;
611
+ usage.outputTokens += request.outputTokens;
612
+ usage.cacheReadTokens += request.cacheReadTokens;
613
+ usage.cacheWriteTokens += request.cacheWriteTokens;
553
614
  }
554
615
  }
555
616
  function buildToolCall(input) {
@@ -561,6 +622,10 @@ function buildToolCall(input) {
561
622
  toolUseId: input.block.id ?? `${input.turnAt}:${input.index}`,
562
623
  tool,
563
624
  summary: clip(summarizeInput(tool, input.block.input), 400),
625
+ inputKey: createHash("sha256")
626
+ .update(JSON.stringify(input.block.input ?? {}))
627
+ .digest("hex")
628
+ .slice(0, 16),
564
629
  ...(bakerVerb ? { bakerVerb } : {}),
565
630
  resultBytes: 0,
566
631
  resultTokens: 0,
@@ -608,12 +673,16 @@ function absorbToolUses(acc, line, turnAt, isSidechain) {
608
673
  return uses.length;
609
674
  }
610
675
  function absorbAssistantLine(acc, line) {
611
- const turnAt = currentTurn(acc)?.index ?? 0;
676
+ const turnAt = turnIndexOf(acc, line) ?? 0;
612
677
  absorbRequestUsage(acc, line, turnAt);
613
- const isSidechain = line.isSidechain === true || line.isSubagent === true;
678
+ const isSidechain = isSubagentLine(line);
614
679
  const uses = absorbToolUses(acc, line, turnAt, isSidechain);
615
680
  const text = textOf(line);
616
- if (text.trim().length === 0 || isSidechain) {
681
+ if (text.trim().length === 0) {
682
+ return;
683
+ }
684
+ if (isSidechain) {
685
+ acc.subagentWords.set(conversationKey(line.threadId, line.agentId), text);
617
686
  return;
618
687
  }
619
688
  const turn = currentTurn(acc);
@@ -698,7 +767,7 @@ function attributeGuardFollowUps(guards, calls) {
698
767
  .find((call) => call.threadId === refused.threadId &&
699
768
  call.isSidechain === refused.isSidechain &&
700
769
  call.agentId === refused.agentId &&
701
- call.summary !== guard.attempted);
770
+ call.inputKey !== refused.inputKey);
702
771
  if (after) {
703
772
  guard.nextAction = `${after.tool}: ${clip(after.summary, 160)}`;
704
773
  }
@@ -715,11 +784,11 @@ function attributeGuardFollowUps(guards, calls) {
715
784
  function countGuardRepeats(guards) {
716
785
  const counts = new Map();
717
786
  for (const guard of guards) {
718
- const key = `${guard.tool}\u0000${guard.attempted}\u0000${guard.guard ?? guard.message}`;
787
+ const key = `${guard.tool}\u0000${guard.inputKey}\u0000${guard.guard ?? guard.message}`;
719
788
  counts.set(key, (counts.get(key) ?? 0) + 1);
720
789
  }
721
790
  for (const guard of guards) {
722
- const key = `${guard.tool}\u0000${guard.attempted}\u0000${guard.guard ?? guard.message}`;
791
+ const key = `${guard.tool}\u0000${guard.inputKey}\u0000${guard.guard ?? guard.message}`;
723
792
  guard.repeats = counts.get(key) ?? 1;
724
793
  }
725
794
  }
@@ -739,6 +808,8 @@ export function buildTranscriptDigest(lines) {
739
808
  marketerLanguage: new Map(),
740
809
  batches: new Map(),
741
810
  answeredAt: new Map(),
811
+ subagentTurns: new Map(),
812
+ subagentWords: new Map(),
742
813
  };
743
814
  for (const line of lines) {
744
815
  const openTurns = acc.turns.length;
@@ -847,7 +918,7 @@ function aggregateFailures(calls, messages) {
847
918
  function findFlakyCalls(calls) {
848
919
  const groups = new Map();
849
920
  for (const call of calls) {
850
- const key = `${call.tool}\u0000${call.summary}`;
921
+ const key = `${call.tool}\u0000${call.inputKey}`;
851
922
  groups.set(key, [...(groups.get(key) ?? []), call]);
852
923
  }
853
924
  const out = [];
@@ -877,7 +948,18 @@ function findFlakyCalls(calls) {
877
948
  * and this is what says where in the Session to look.
878
949
  */
879
950
  function emptyThread(threadId) {
880
- return { threadId, turns: 0, toolCalls: 0, resultBytes: 0, resultTokens: 0, errors: 0, denials: 0, workingMs: 0 };
951
+ return {
952
+ threadId,
953
+ turns: 0,
954
+ toolCalls: 0,
955
+ resultBytes: 0,
956
+ resultTokens: 0,
957
+ subagentToolCalls: 0,
958
+ subagentResultTokens: 0,
959
+ errors: 0,
960
+ denials: 0,
961
+ workingMs: 0,
962
+ };
881
963
  }
882
964
  /** Milliseconds parsed off an ISO timestamp, or nothing. */
883
965
  function stampOf(value) {
@@ -914,6 +996,8 @@ function aggregateByThread(turns, calls, stamps) {
914
996
  entry.toolCalls += 1;
915
997
  entry.resultBytes += call.resultBytes;
916
998
  entry.resultTokens += call.resultTokens;
999
+ entry.subagentToolCalls += call.isSidechain ? 1 : 0;
1000
+ entry.subagentResultTokens += call.isSidechain ? call.resultTokens : 0;
917
1001
  entry.errors += call.isError ? 1 : 0;
918
1002
  }
919
1003
  for (const [threadId, values] of stamps) {
@@ -930,14 +1014,18 @@ function aggregateByTool(calls) {
930
1014
  const entry = map.get(call.tool) ?? {
931
1015
  tool: call.tool,
932
1016
  calls: 0,
1017
+ subagentCalls: 0,
933
1018
  totalBytes: 0,
934
1019
  totalTokens: 0,
1020
+ subagentTokens: 0,
935
1021
  maxBytes: 0,
936
1022
  errors: 0,
937
1023
  };
938
1024
  entry.calls += 1;
1025
+ entry.subagentCalls += call.isSidechain ? 1 : 0;
939
1026
  entry.totalBytes += call.resultBytes;
940
1027
  entry.totalTokens += call.resultTokens;
1028
+ entry.subagentTokens += call.isSidechain ? call.resultTokens : 0;
941
1029
  entry.maxBytes = Math.max(entry.maxBytes, call.resultBytes);
942
1030
  entry.errors += call.isError ? 1 : 0;
943
1031
  map.set(call.tool, entry);
@@ -974,7 +1062,7 @@ function aggregateByVerb(calls) {
974
1062
  function findRepeats(calls) {
975
1063
  const seen = new Map();
976
1064
  for (const call of calls) {
977
- const key = `${call.tool}\u0000${call.summary}`;
1065
+ const key = `${call.tool}\u0000${call.inputKey}`;
978
1066
  const entry = seen.get(key);
979
1067
  if (entry) {
980
1068
  entry.extra += 1;
@@ -1141,12 +1229,25 @@ export function renderTurns(digest) {
1141
1229
  "",
1142
1230
  digest.threads.length === 0
1143
1231
  ? "No thread could be attributed — the transcripts carried no thread id."
1144
- : table(["thread", "turns", "tool calls", "result bytes", "~result tokens", "errors", "denials", "running"], digest.threads.map((thread) => [
1232
+ : table([
1233
+ "thread",
1234
+ "turns",
1235
+ "tool calls",
1236
+ "subagent calls",
1237
+ "result bytes",
1238
+ "~main result tokens",
1239
+ "~subagent result tokens",
1240
+ "errors",
1241
+ "denials",
1242
+ "running",
1243
+ ], digest.threads.map((thread) => [
1145
1244
  thread.threadId,
1146
1245
  thread.turns,
1147
1246
  thread.toolCalls,
1247
+ thread.subagentToolCalls,
1148
1248
  thread.resultBytes,
1149
- thread.resultTokens,
1249
+ thread.resultTokens - thread.subagentResultTokens,
1250
+ thread.subagentResultTokens,
1150
1251
  thread.errors,
1151
1252
  thread.denials,
1152
1253
  `${Math.round(thread.workingMs / 1000)}s`,
@@ -1154,7 +1255,25 @@ export function renderTurns(digest) {
1154
1255
  "",
1155
1256
  "## Every turn",
1156
1257
  "",
1157
- table(["#", "thread", "at", "user", "assistant", "tools", "in", "out", "cache read", "denials", "compacted"], digest.turns.map((turn) => [
1258
+ "`in`, `out` and `cache read` are the main agent's context; `subagent` columns",
1259
+ "are what that turn's subagents spent, which never reached it.",
1260
+ "",
1261
+ table([
1262
+ "#",
1263
+ "thread",
1264
+ "at",
1265
+ "user",
1266
+ "assistant",
1267
+ "tools",
1268
+ "in",
1269
+ "out",
1270
+ "cache read",
1271
+ "subagent in",
1272
+ "subagent out",
1273
+ "subagent cache read",
1274
+ "denials",
1275
+ "compacted",
1276
+ ], digest.turns.map((turn) => [
1158
1277
  turn.index,
1159
1278
  turn.threadId ?? "",
1160
1279
  turn.timestamp ?? "",
@@ -1164,23 +1283,36 @@ export function renderTurns(digest) {
1164
1283
  turn.inputTokens,
1165
1284
  turn.outputTokens,
1166
1285
  turn.cacheReadTokens,
1286
+ turn.subagent.inputTokens,
1287
+ turn.subagent.outputTokens,
1288
+ turn.subagent.cacheReadTokens,
1167
1289
  turn.denials,
1168
1290
  turn.compacted ? "yes" : "",
1169
1291
  ])),
1170
1292
  "",
1171
1293
  ].join("\n");
1172
1294
  }
1295
+ /** Who made a call: the main agent, or the subagent by its id. */
1296
+ function agentOf(call) {
1297
+ return call.isSidechain ? `subagent ${call.agentId ?? "(unnamed)"}` : "main";
1298
+ }
1173
1299
  export function renderTools(digest) {
1174
1300
  const sections = [
1175
1301
  "# Tool calls",
1176
1302
  "",
1177
1303
  "## Per tool",
1178
1304
  "",
1179
- table(["tool", "calls", "total bytes", "~total tokens", "max bytes", "errors"], digest.byTool.map((entry) => [
1305
+ "`main` is what the agent the marketer talks to called; `subagent` ran in a",
1306
+ "subagent, and its results never reached the main agent's context.",
1307
+ "",
1308
+ table(["tool", "calls", "main", "subagent", "total bytes", "~main tokens", "~subagent tokens", "max bytes", "errors"], digest.byTool.map((entry) => [
1180
1309
  entry.tool,
1181
1310
  entry.calls,
1311
+ entry.calls - entry.subagentCalls,
1312
+ entry.subagentCalls,
1182
1313
  entry.totalBytes,
1183
- entry.totalTokens,
1314
+ entry.totalTokens - entry.subagentTokens,
1315
+ entry.subagentTokens,
1184
1316
  entry.maxBytes,
1185
1317
  entry.errors,
1186
1318
  ])),
@@ -1208,7 +1340,14 @@ export function renderTools(digest) {
1208
1340
  "",
1209
1341
  "## Ten largest single results (by what the model received)",
1210
1342
  "",
1211
- table(["turn", "tool", "input", "bytes", "~tokens"], digest.largestResults.map((call) => [call.turn, call.tool, call.summary, call.resultBytes, call.resultTokens])),
1343
+ table(["turn", "agent", "tool", "input", "bytes", "~tokens"], digest.largestResults.map((call) => [
1344
+ call.turn,
1345
+ agentOf(call),
1346
+ call.tool,
1347
+ call.summary,
1348
+ call.resultBytes,
1349
+ call.resultTokens,
1350
+ ])),
1212
1351
  "",
1213
1352
  "## Introspection calls (`baker schema`, `--help`)",
1214
1353
  "",
@@ -1279,8 +1418,9 @@ export function renderFailures(digest) {
1279
1418
  "",
1280
1419
  digest.slowest.length === 0
1281
1420
  ? "No call carried both a request and a result timestamp, so nothing here can be timed."
1282
- : table(["turn", "tool", "took", "failed", "input"], digest.slowest.map((call) => [
1421
+ : table(["turn", "agent", "tool", "took", "failed", "input"], digest.slowest.map((call) => [
1283
1422
  call.turn,
1423
+ agentOf(call),
1284
1424
  call.tool,
1285
1425
  secs(call.durationMs ?? 0),
1286
1426
  call.isError ? (call.failureClass ?? "yes") : "",
@@ -1304,6 +1444,9 @@ export function renderHooks(digest) {
1304
1444
  "",
1305
1445
  ].join("\n");
1306
1446
  }
1447
+ function guardAgent(guard) {
1448
+ return guard.agentId === undefined ? "" : ` · subagent ${guard.agentId}`;
1449
+ }
1307
1450
  export function renderGuards(digest) {
1308
1451
  const refusals = digest.guards.filter((guard) => guard.refusal);
1309
1452
  const errors = digest.guards.filter((guard) => !guard.refusal);
@@ -1323,13 +1466,14 @@ export function renderGuards(digest) {
1323
1466
  : [
1324
1467
  `${refusals.length} refusals.`,
1325
1468
  "",
1326
- "`Intent` is the agent's own words in that turn — what it thought it",
1327
- "was doing. It is the transcript's only answer to why it went there;",
1469
+ "`Intent` is the refused agent's own words — the main agent's in that",
1470
+ "turn, or the subagent's own when a subagent was refused: what it thought",
1471
+ "it was doing. It is the transcript's only answer to why it went there;",
1328
1472
  "the rest of the answer is in whatever doc, skill, hint or example",
1329
1473
  "pointed it that way, which is yours to go and open.",
1330
1474
  "",
1331
1475
  ...refusals.map((guard) => [
1332
- `### Turn ${guard.turn} · ${guard.tool}${guard.guard ? ` · ${guard.guard}` : ""}${guard.repeats > 1 ? ` · refused ${guard.repeats}× this Session` : ""}`,
1476
+ `### Turn ${guard.turn}${guardAgent(guard)} · ${guard.tool}${guard.guard ? ` · ${guard.guard}` : ""}${guard.repeats > 1 ? ` · refused ${guard.repeats}× this Session` : ""}`,
1333
1477
  "",
1334
1478
  `Attempted: \`${guard.attempted}\``,
1335
1479
  "",
@@ -1348,7 +1492,7 @@ export function renderGuards(digest) {
1348
1492
  ? "No tool returned an error."
1349
1493
  : errors
1350
1494
  .map((guard) => [
1351
- `### Turn ${guard.turn} · ${guard.tool}${guard.repeats > 1 ? ` · ${guard.repeats}×` : ""}`,
1495
+ `### Turn ${guard.turn}${guardAgent(guard)} · ${guard.tool}${guard.repeats > 1 ? ` · ${guard.repeats}×` : ""}`,
1352
1496
  "",
1353
1497
  `Attempted: \`${guard.attempted}\``,
1354
1498
  "",