@botbotgo/agent-harness 0.0.344 → 0.0.346

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.
@@ -176,6 +176,34 @@ export async function streamChatMessage(input) {
176
176
  }
177
177
  stderrWriteChain = enqueueChatWrite(input.stderr, input.stderrStream, stderrWriteChain, message);
178
178
  };
179
+ const emitProgressCommentary = (rawText, agentId) => {
180
+ const lines = rawText.split("\n").filter((line) => line.length > 0);
181
+ if (lines.length === 0) {
182
+ return;
183
+ }
184
+ const now = Date.now();
185
+ const linePrefix = `[${formatPerfClock(now)} +${formatElapsed(now)}]${formatAgentProgressLabel(agentId || latestAgentId)}`;
186
+ const serializedLines = lines
187
+ .map((line) => {
188
+ const signature = `${agentId || latestAgentId}|${line}`;
189
+ if (renderedTodoTransitionLines.has(signature)) {
190
+ return null;
191
+ }
192
+ renderedTodoTransitionLines.add(signature);
193
+ return `${linePrefix} ${line}\n`;
194
+ })
195
+ .filter((line) => line !== null);
196
+ if (serializedLines.length === 0) {
197
+ return;
198
+ }
199
+ if (input.requestEvents && input.liveRequestTree && !suppressRequestTreeRendering && lastRenderedRequestTree) {
200
+ liveRequestAnnotations.push(...serializedLines);
201
+ clearLiveRequestTree();
202
+ drawLiveRequestTree();
203
+ return;
204
+ }
205
+ writeChatStderr(serializedLines.join(""));
206
+ };
179
207
  const clearIdleProgressTimer = () => {
180
208
  if (idleProgressTimer) {
181
209
  clearTimeout(idleProgressTimer);
@@ -247,10 +275,11 @@ export async function streamChatMessage(input) {
247
275
  continue;
248
276
  }
249
277
  const text = `TODO ${label}: ${todo.content}.`;
250
- if (renderedTodoTransitionLines.has(text)) {
278
+ const signature = `${snapshot.agentId || latestAgentId || ""}|${text}`;
279
+ if (renderedTodoTransitionLines.has(signature)) {
251
280
  continue;
252
281
  }
253
- renderedTodoTransitionLines.add(text);
282
+ renderedTodoTransitionLines.add(signature);
254
283
  lines.push(`[${formatPerfClock(Date.now())} +${formatElapsed(Date.now())}]${formatAgentProgressLabel(snapshot.agentId)} ${text}\n`);
255
284
  }
256
285
  previousTodoStatuses = nextStatuses;
@@ -354,17 +383,7 @@ export async function streamChatMessage(input) {
354
383
  if (wroteContent || wroteRenderableBlocks) {
355
384
  return;
356
385
  }
357
- if (renderedTodoTransitionLines.has(delta.text)) {
358
- return;
359
- }
360
- const progressLine = `[${formatPerfClock(Date.now())} +${formatElapsed(Date.now())}]${formatAgentProgressLabel(delta.agentId)} ${delta.text}\n`;
361
- if (input.requestEvents && input.liveRequestTree && !suppressRequestTreeRendering && lastRenderedRequestTree) {
362
- liveRequestAnnotations.push(progressLine);
363
- clearLiveRequestTree();
364
- drawLiveRequestTree();
365
- return;
366
- }
367
- writeChatStderr(progressLine);
386
+ emitProgressCommentary(delta.text, delta.agentId);
368
387
  }
369
388
  },
370
389
  });
@@ -1,2 +1,2 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.344";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.346";
2
2
  export declare const AGENT_HARNESS_RELEASE_DATE = "2026-04-24";
@@ -1,2 +1,2 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.344";
1
+ export const AGENT_HARNESS_VERSION = "0.0.346";
2
2
  export const AGENT_HARNESS_RELEASE_DATE = "2026-04-24";
@@ -372,7 +372,7 @@ function summarizePlanState(planState) {
372
372
  const suffix = planState.items.length > items.length ? [` ... ${planState.items.length - items.length} more`] : [];
373
373
  return ["TODO", ...items, ...suffix].join("\n");
374
374
  }
375
- function summarizePlanStateTerminalTransitions(previousPlanState, nextPlanState) {
375
+ function summarizePlanStateTerminalTransitionEntries(previousPlanState, nextPlanState) {
376
376
  const previousByKey = new Map((previousPlanState?.items ?? []).map((item) => [normalizePlanItemKey(item), item]));
377
377
  const terminalLabel = (status) => {
378
378
  switch (status) {
@@ -395,9 +395,70 @@ function summarizePlanStateTerminalTransitions(previousPlanState, nextPlanState)
395
395
  if (previousStatus === item.status) {
396
396
  return [];
397
397
  }
398
- return [`TODO ${label}: ${item.content}.`];
398
+ return [{
399
+ key: normalizePlanItemKey(item),
400
+ text: `TODO ${label}: ${item.content}.`,
401
+ item,
402
+ }];
399
403
  });
400
404
  }
405
+ function buildPlanStateProgression(previousPlanState, nextPlanState) {
406
+ if (!previousPlanState) {
407
+ return [{ planState: nextPlanState }];
408
+ }
409
+ const terminalEntries = summarizePlanStateTerminalTransitionEntries(previousPlanState, nextPlanState);
410
+ if (terminalEntries.length === 0) {
411
+ return [{ planState: nextPlanState }];
412
+ }
413
+ const mergedPlanState = mergePartialPlanState(previousPlanState, nextPlanState);
414
+ const workingItems = [];
415
+ const workingItemByKey = new Map();
416
+ const previousItems = previousPlanState?.items ?? [];
417
+ const nextItems = mergedPlanState?.items ?? [];
418
+ for (const item of previousItems) {
419
+ workingItemByKey.set(normalizePlanItemKey(item), workingItems.length);
420
+ workingItems.push({ ...item });
421
+ }
422
+ for (const item of nextItems) {
423
+ const key = normalizePlanItemKey(item);
424
+ const index = workingItemByKey.get(key);
425
+ if (index === undefined) {
426
+ workingItemByKey.set(key, workingItems.length);
427
+ workingItems.push({ ...item });
428
+ }
429
+ }
430
+ const seenTransitionKeys = new Set();
431
+ const result = [];
432
+ for (const entry of terminalEntries) {
433
+ if (!entry.item || seenTransitionKeys.has(entry.key)) {
434
+ continue;
435
+ }
436
+ seenTransitionKeys.add(entry.key);
437
+ const index = workingItems.findIndex((item) => normalizePlanItemKey(item) === entry.key);
438
+ const nextItem = nextPlanState.items.find((item) => normalizePlanItemKey(item) === entry.key);
439
+ if (!nextItem) {
440
+ continue;
441
+ }
442
+ if (index >= 0) {
443
+ workingItems[index] = { ...workingItems[index], ...nextItem };
444
+ }
445
+ else {
446
+ workingItems.push({ ...nextItem });
447
+ }
448
+ result.push({
449
+ planState: {
450
+ ...nextPlanState,
451
+ items: workingItems.map((item) => ({ ...item })),
452
+ summary: recomputePlanSummary(workingItems),
453
+ },
454
+ commentary: entry.text,
455
+ });
456
+ }
457
+ if (result.length === 0) {
458
+ return [{ planState: nextPlanState }];
459
+ }
460
+ return result;
461
+ }
401
462
  function createSurfaceCommentary(surfaceItem) {
402
463
  const name = normalizeCommentaryText(surfaceItem.name);
403
464
  if (!name) {
@@ -656,13 +717,16 @@ export async function* streamHarnessRun(options) {
656
717
  planStateVersion = mergedPlanState.version;
657
718
  lastPlanStateSignature = signature;
658
719
  currentPlanState = mergedPlanState;
659
- for (const item of await emitPlanStateUpdate(options, currentAgentId, mergedPlanState)) {
660
- yield item;
720
+ const progression = buildPlanStateProgression(previousPlanState, currentPlanState);
721
+ for (const progressionStep of progression) {
722
+ for (const item of await emitPlanStateUpdate(options, currentAgentId, progressionStep.planState)) {
723
+ yield item;
724
+ }
725
+ if (progressionStep.commentary) {
726
+ yield* emitCommentary(progressionStep.commentary);
727
+ }
661
728
  }
662
- for (const commentary of summarizePlanStateTerminalTransitions(previousPlanState, mergedPlanState)) {
663
- yield* emitCommentary(commentary);
664
- }
665
- const commentary = summarizePlanState(mergedPlanState);
729
+ const commentary = summarizePlanState(currentPlanState);
666
730
  if (commentary) {
667
731
  yield* emitCommentary(commentary);
668
732
  }
@@ -818,13 +882,16 @@ export async function* streamHarnessRun(options) {
818
882
  const previousPlanState = currentPlanState;
819
883
  lastPlanStateSignature = signature;
820
884
  currentPlanState = mergedPlanState;
821
- for (const item of await emitPlanStateUpdate(options, currentAgentId, mergedPlanState)) {
822
- yield item;
885
+ const progression = buildPlanStateProgression(previousPlanState, currentPlanState);
886
+ for (const progressionStep of progression) {
887
+ for (const item of await emitPlanStateUpdate(options, currentAgentId, progressionStep.planState)) {
888
+ yield item;
889
+ }
890
+ if (progressionStep.commentary) {
891
+ yield* emitCommentary(progressionStep.commentary);
892
+ }
823
893
  }
824
- for (const commentary of summarizePlanStateTerminalTransitions(previousPlanState, mergedPlanState)) {
825
- yield* emitCommentary(commentary);
826
- }
827
- const commentary = summarizePlanState(mergedPlanState);
894
+ const commentary = summarizePlanState(currentPlanState);
828
895
  if (commentary) {
829
896
  yield* emitCommentary(commentary);
830
897
  }
@@ -841,13 +908,16 @@ export async function* streamHarnessRun(options) {
841
908
  planStateVersion = reconciledPlanState.version;
842
909
  lastPlanStateSignature = signature;
843
910
  currentPlanState = reconciledPlanState;
844
- for (const item of await emitPlanStateUpdate(options, currentAgentId, reconciledPlanState)) {
845
- yield item;
846
- }
847
- for (const commentary of summarizePlanStateTerminalTransitions(previousPlanState, reconciledPlanState)) {
848
- yield* emitCommentary(commentary);
911
+ const progression = buildPlanStateProgression(previousPlanState, currentPlanState);
912
+ for (const progressionStep of progression) {
913
+ for (const item of await emitPlanStateUpdate(options, currentAgentId, progressionStep.planState)) {
914
+ yield item;
915
+ }
916
+ if (progressionStep.commentary) {
917
+ yield* emitCommentary(progressionStep.commentary);
918
+ }
849
919
  }
850
- const commentary = summarizePlanState(reconciledPlanState);
920
+ const commentary = summarizePlanState(currentPlanState);
851
921
  if (commentary) {
852
922
  yield* emitCommentary(commentary);
853
923
  }
@@ -919,13 +989,16 @@ export async function* streamHarnessRun(options) {
919
989
  planStateVersion = mergedPlanState.version;
920
990
  lastPlanStateSignature = signature;
921
991
  currentPlanState = mergedPlanState;
922
- for (const item of await emitPlanStateUpdate(options, currentAgentId, mergedPlanState)) {
923
- yield item;
924
- }
925
- for (const commentary of summarizePlanStateTerminalTransitions(previousPlanState, mergedPlanState)) {
926
- yield* emitCommentary(commentary);
992
+ const progression = buildPlanStateProgression(previousPlanState, currentPlanState);
993
+ for (const progressionStep of progression) {
994
+ for (const item of await emitPlanStateUpdate(options, currentAgentId, progressionStep.planState)) {
995
+ yield item;
996
+ }
997
+ if (progressionStep.commentary) {
998
+ yield* emitCommentary(progressionStep.commentary);
999
+ }
927
1000
  }
928
- const commentary = summarizePlanState(mergedPlanState);
1001
+ const commentary = summarizePlanState(currentPlanState);
929
1002
  if (commentary) {
930
1003
  yield* emitCommentary(commentary);
931
1004
  }
@@ -941,13 +1014,16 @@ export async function* streamHarnessRun(options) {
941
1014
  planStateVersion = reconciledPlanState.version;
942
1015
  lastPlanStateSignature = signature;
943
1016
  currentPlanState = reconciledPlanState;
944
- for (const item of await emitPlanStateUpdate(options, currentAgentId, reconciledPlanState)) {
945
- yield item;
946
- }
947
- for (const commentary of summarizePlanStateTerminalTransitions(previousPlanState, reconciledPlanState)) {
948
- yield* emitCommentary(commentary);
1017
+ const progression = buildPlanStateProgression(previousPlanState, currentPlanState);
1018
+ for (const progressionStep of progression) {
1019
+ for (const item of await emitPlanStateUpdate(options, currentAgentId, progressionStep.planState)) {
1020
+ yield item;
1021
+ }
1022
+ if (progressionStep.commentary) {
1023
+ yield* emitCommentary(progressionStep.commentary);
1024
+ }
949
1025
  }
950
- const commentary = summarizePlanState(reconciledPlanState);
1026
+ const commentary = summarizePlanState(currentPlanState);
951
1027
  if (commentary) {
952
1028
  yield* emitCommentary(commentary);
953
1029
  }
@@ -963,13 +1039,16 @@ export async function* streamHarnessRun(options) {
963
1039
  planStateVersion = reconciledPlanState.version;
964
1040
  lastPlanStateSignature = signature;
965
1041
  currentPlanState = reconciledPlanState;
966
- for (const item of await emitPlanStateUpdate(options, currentAgentId, reconciledPlanState)) {
967
- yield item;
968
- }
969
- for (const commentary of summarizePlanStateTerminalTransitions(previousPlanState, reconciledPlanState)) {
970
- yield* emitCommentary(commentary);
1042
+ const progression = buildPlanStateProgression(previousPlanState, currentPlanState);
1043
+ for (const progressionStep of progression) {
1044
+ for (const item of await emitPlanStateUpdate(options, currentAgentId, progressionStep.planState)) {
1045
+ yield item;
1046
+ }
1047
+ if (progressionStep.commentary) {
1048
+ yield* emitCommentary(progressionStep.commentary);
1049
+ }
971
1050
  }
972
- const commentary = summarizePlanState(reconciledPlanState);
1051
+ const commentary = summarizePlanState(currentPlanState);
973
1052
  if (commentary) {
974
1053
  yield* emitCommentary(commentary);
975
1054
  }
@@ -1031,13 +1110,16 @@ export async function* streamHarnessRun(options) {
1031
1110
  planStateVersion = reconciledPlanState.version;
1032
1111
  lastPlanStateSignature = signature;
1033
1112
  currentPlanState = reconciledPlanState;
1034
- for (const item of await emitPlanStateUpdate(options, currentAgentId, reconciledPlanState)) {
1035
- yield item;
1036
- }
1037
- for (const commentary of summarizePlanStateTerminalTransitions(previousPlanState, reconciledPlanState)) {
1038
- yield* emitCommentary(commentary);
1113
+ const progression = buildPlanStateProgression(previousPlanState, currentPlanState);
1114
+ for (const progressionStep of progression) {
1115
+ for (const item of await emitPlanStateUpdate(options, currentAgentId, progressionStep.planState)) {
1116
+ yield item;
1117
+ }
1118
+ if (progressionStep.commentary) {
1119
+ yield* emitCommentary(progressionStep.commentary);
1120
+ }
1039
1121
  }
1040
- const commentary = summarizePlanState(reconciledPlanState);
1122
+ const commentary = summarizePlanState(currentPlanState);
1041
1123
  if (commentary) {
1042
1124
  yield* emitCommentary(commentary);
1043
1125
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.344",
3
+ "version": "0.0.346",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "license": "MIT",
6
6
  "type": "module",