@botbotgo/agent-harness 0.0.377 → 0.0.378

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.
@@ -20,6 +20,8 @@ export async function streamChatMessage(input) {
20
20
  let announcedRunningState = false;
21
21
  let requestTreeVisible = false;
22
22
  let requestTreePersisted = false;
23
+ let todoPanelVisible = false;
24
+ let lastRenderedTodoPanelLineCount = 0;
23
25
  let liveRequestAnnotations = [];
24
26
  let persistedRequestTreeEventCount = 0;
25
27
  let persistedRequestTreeTodoSignature = "";
@@ -155,6 +157,7 @@ export async function streamChatMessage(input) {
155
157
  stdoutWriteChain = enqueueChatWrite(input.stdout, input.stdoutStream, barrier, message);
156
158
  };
157
159
  const writeChatStdout = (message) => {
160
+ todoPanelVisible = false;
158
161
  if (input.requestEvents && input.liveRequestTree && !suppressRequestTreeRendering) {
159
162
  clearLiveRequestTree();
160
163
  enqueueOrderedStdout(message);
@@ -168,6 +171,7 @@ export async function streamChatMessage(input) {
168
171
  stdoutWriteChain = enqueueChatWrite(input.stdout, input.stdoutStream, stdoutWriteChain, message);
169
172
  };
170
173
  const writeChatStderr = (message) => {
174
+ todoPanelVisible = false;
171
175
  if (input.requestEvents && input.liveRequestTree && !suppressRequestTreeRendering) {
172
176
  clearLiveRequestTree();
173
177
  stderrWriteChain = enqueueChatWrite(input.stderr, input.stderrStream, stderrWriteChain, message);
@@ -176,6 +180,61 @@ export async function streamChatMessage(input) {
176
180
  }
177
181
  stderrWriteChain = enqueueChatWrite(input.stderr, input.stderrStream, stderrWriteChain, message);
178
182
  };
183
+ const parseTerminalTodoPanel = (rawText) => {
184
+ const lines = rawText.split("\n");
185
+ if (lines[0]?.trim() !== "TODO") {
186
+ return null;
187
+ }
188
+ const items = lines.slice(1).map((line) => {
189
+ const match = line.match(/^\s*\[([ x~!-])\]\s+(.+?)\s*$/);
190
+ if (!match) {
191
+ return null;
192
+ }
193
+ const marker = match[1];
194
+ const status = marker === "x"
195
+ ? "completed"
196
+ : marker === "~"
197
+ ? "in_progress"
198
+ : marker === "!"
199
+ ? "failed"
200
+ : marker === "-"
201
+ ? "cancelled"
202
+ : "pending";
203
+ return {
204
+ marker: marker === " " ? " " : marker,
205
+ content: match[2],
206
+ status,
207
+ };
208
+ }).filter((item) => item !== null);
209
+ return items.length > 0 ? items : null;
210
+ };
211
+ const renderTerminalTodoPanel = (items, agentId) => {
212
+ const now = Date.now();
213
+ const completed = items.filter((item) => item.status === "completed").length;
214
+ const inProgress = items.filter((item) => item.status === "in_progress").length;
215
+ const failed = items.filter((item) => item.status === "failed").length;
216
+ const cancelled = items.filter((item) => item.status === "cancelled").length;
217
+ const pending = items.filter((item) => item.status === "pending").length;
218
+ const summary = [
219
+ `${completed}/${items.length} done`,
220
+ `${inProgress} active`,
221
+ `${pending} pending`,
222
+ failed > 0 ? `${failed} failed` : null,
223
+ cancelled > 0 ? `${cancelled} cancelled` : null,
224
+ ].filter((part) => part !== null).join(" | ");
225
+ const header = `[${formatPerfClock(now)} +${formatElapsed(now)}]${formatAgentProgressLabel(agentId || latestAgentId)} TODO Burn Down | ${summary}`;
226
+ const rows = items.map((item) => ` [${item.marker}] ${item.content}`);
227
+ return `${header}\n${rows.join("\n")}\n`;
228
+ };
229
+ const drawTerminalTodoPanel = (items, agentId) => {
230
+ const panel = renderTerminalTodoPanel(items, agentId);
231
+ const clearPreviousPanel = todoPanelVisible && lastRenderedTodoPanelLineCount > 0
232
+ ? `\x1b[${lastRenderedTodoPanelLineCount}F\x1b[0J`
233
+ : "";
234
+ stderrWriteChain = enqueueChatWrite(input.stderr, input.stderrStream, stderrWriteChain, `${clearPreviousPanel}${panel}`);
235
+ lastRenderedTodoPanelLineCount = countRenderedLines(panel);
236
+ todoPanelVisible = true;
237
+ };
179
238
  const emitProgressCommentary = (rawText, agentId) => {
180
239
  const lines = rawText.split("\n").filter((line) => line.length > 0);
181
240
  if (lines.length === 0) {
@@ -393,6 +452,11 @@ export async function streamChatMessage(input) {
393
452
  if (wroteContent || wroteRenderableBlocks) {
394
453
  return;
395
454
  }
455
+ const todoItems = !input.requestEvents ? parseTerminalTodoPanel(delta.text) : null;
456
+ if (todoItems) {
457
+ drawTerminalTodoPanel(todoItems, delta.agentId);
458
+ return;
459
+ }
396
460
  emitProgressCommentary(delta.text, delta.agentId);
397
461
  }
398
462
  },
@@ -1,2 +1,2 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.377";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.378";
2
2
  export declare const AGENT_HARNESS_RELEASE_DATE = "2026-04-30";
@@ -1,2 +1,2 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.377";
1
+ export const AGENT_HARNESS_VERSION = "0.0.378";
2
2
  export const AGENT_HARNESS_RELEASE_DATE = "2026-04-30";
@@ -1360,61 +1360,87 @@ export class AgentRuntimeAdapter {
1360
1360
  agentId: selectedBinding.agent.id,
1361
1361
  };
1362
1362
  const childRequestId = `${requestId}:${subagentType}`;
1363
- const executedToolResults = [];
1364
- let output = "";
1365
- try {
1366
- for await (const chunk of this.stream(selectedBinding, requestText, sessionId, [], {
1367
- context: options.context,
1368
- state: options.state,
1369
- files: options.files,
1370
- requestId: childRequestId,
1371
- memoryContext: options.memoryContext,
1372
- profiling: options.profiling,
1373
- toolRuntimeContext: options.toolRuntimeContext,
1374
- })) {
1375
- if (typeof chunk === "string") {
1376
- output += chunk;
1377
- continue;
1378
- }
1379
- if (chunk.kind === "content") {
1380
- output += chunk.content ?? "";
1381
- continue;
1382
- }
1383
- if (chunk.kind === "tool-result") {
1384
- executedToolResults.push({
1385
- toolName: chunk.toolName,
1386
- output: chunk.output,
1387
- ...(chunk.isError !== undefined ? { isError: chunk.isError } : {}),
1388
- });
1363
+ const runDelegatedStreamAttempt = async function* (text, requestIdSuffix = "") {
1364
+ const executedToolResults = [];
1365
+ let output = "";
1366
+ try {
1367
+ for await (const chunk of this.stream(selectedBinding, text, sessionId, [], {
1368
+ context: options.context,
1369
+ state: options.state,
1370
+ files: options.files,
1371
+ requestId: `${childRequestId}${requestIdSuffix}`,
1372
+ memoryContext: options.memoryContext,
1373
+ profiling: options.profiling,
1374
+ toolRuntimeContext: options.toolRuntimeContext,
1375
+ })) {
1376
+ if (typeof chunk === "string") {
1377
+ output += chunk;
1378
+ continue;
1379
+ }
1380
+ if (chunk.kind === "content") {
1381
+ output += chunk.content ?? "";
1382
+ continue;
1383
+ }
1384
+ if (chunk.kind === "tool-result") {
1385
+ executedToolResults.push({
1386
+ toolName: chunk.toolName,
1387
+ output: chunk.output,
1388
+ ...(chunk.isError !== undefined ? { isError: chunk.isError } : {}),
1389
+ });
1390
+ }
1391
+ yield { ...chunk, agentId: chunk.agentId ?? selectedBinding.agent.id };
1389
1392
  }
1390
- yield { ...chunk, agentId: chunk.agentId ?? selectedBinding.agent.id };
1391
1393
  }
1392
- }
1393
- catch (error) {
1394
- output = error instanceof Error ? error.message : String(error);
1395
- return {
1396
- toolOutput: output,
1397
- delegatedSubagentType: subagentType,
1398
- delegatedResult: {
1394
+ catch (error) {
1395
+ output = error instanceof Error ? error.message : String(error);
1396
+ return {
1399
1397
  sessionId,
1400
- requestId: childRequestId,
1398
+ requestId: `${childRequestId}${requestIdSuffix}`,
1401
1399
  agentId: selectedBinding.agent.id,
1402
1400
  state: "failed",
1403
1401
  output,
1404
1402
  finalMessageText: output,
1405
1403
  metadata: { executedToolResults },
1406
- },
1404
+ };
1405
+ }
1406
+ return {
1407
+ sessionId,
1408
+ requestId: `${childRequestId}${requestIdSuffix}`,
1409
+ agentId: selectedBinding.agent.id,
1410
+ state: "completed",
1411
+ output: sanitizeVisibleText(output),
1412
+ finalMessageText: sanitizeVisibleText(output),
1413
+ metadata: { executedToolResults },
1414
+ };
1415
+ }.bind(this);
1416
+ let delegatedResult = yield* runDelegatedStreamAttempt(requestText);
1417
+ const targetRequiresExecutionToolEvidence = getBindingPrimaryTools(selectedBinding).length > 0;
1418
+ if (selectedBinding.harnessRuntime.executionContract?.requiresPlan === true
1419
+ && !hasDelegatedPlanEvidence(delegatedResult)) {
1420
+ delegatedResult = yield* runDelegatedStreamAttempt([requestText, DELEGATED_PLAN_EVIDENCE_RETRY_INSTRUCTION].filter(Boolean).join("\n\n"), ":plan-evidence-retry");
1421
+ }
1422
+ if (targetRequiresExecutionToolEvidence && !hasDelegatedExecutionToolEvidence(delegatedResult)) {
1423
+ delegatedResult = yield* runDelegatedStreamAttempt([requestText, EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION].filter(Boolean).join("\n\n"), ":tool-evidence-retry");
1424
+ }
1425
+ if (selectedBinding.harnessRuntime.executionContract?.requiresPlan === true
1426
+ && !hasDelegatedPlanEvidence(delegatedResult)) {
1427
+ const output = "runtime_error=Delegated agent ended before producing required plan evidence.";
1428
+ delegatedResult = {
1429
+ ...delegatedResult,
1430
+ state: "failed",
1431
+ output,
1432
+ finalMessageText: output,
1433
+ };
1434
+ }
1435
+ if (targetRequiresExecutionToolEvidence && !hasDelegatedExecutionToolEvidence(delegatedResult)) {
1436
+ const output = `runtime_error=Delegated agent ${selectedBinding.agent.id} completed without tool execution evidence.`;
1437
+ delegatedResult = {
1438
+ ...delegatedResult,
1439
+ state: "failed",
1440
+ output,
1441
+ finalMessageText: output,
1407
1442
  };
1408
1443
  }
1409
- const delegatedResult = {
1410
- sessionId,
1411
- requestId: childRequestId,
1412
- agentId: selectedBinding.agent.id,
1413
- state: "completed",
1414
- output: sanitizeVisibleText(output),
1415
- finalMessageText: sanitizeVisibleText(output),
1416
- metadata: { executedToolResults },
1417
- };
1418
1444
  return {
1419
1445
  toolOutput: resolveDelegatedResultOutput(delegatedResult),
1420
1446
  delegatedSubagentType: subagentType,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.377",
3
+ "version": "0.0.378",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "license": "MIT",
6
6
  "type": "module",