@botbotgo/agent-harness 0.0.376 → 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.
package/dist/cli/chat-stream.js
CHANGED
|
@@ -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.
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.378";
|
|
2
2
|
export declare const AGENT_HARNESS_RELEASE_DATE = "2026-04-30";
|
package/dist/package-version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export const AGENT_HARNESS_VERSION = "0.0.378";
|
|
2
2
|
export const AGENT_HARNESS_RELEASE_DATE = "2026-04-30";
|
|
@@ -900,56 +900,58 @@ export class AgentRuntimeAdapter {
|
|
|
900
900
|
"User request:",
|
|
901
901
|
requestText,
|
|
902
902
|
].filter(Boolean).join("\n\n");
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
requestId,
|
|
910
|
-
context: options.context,
|
|
911
|
-
toolRuntimeContext: this.buildFunctionToolRuntimeContext(binding, {
|
|
912
|
-
...options,
|
|
903
|
+
if (!selection) {
|
|
904
|
+
const model = await this.resolveModel(primaryModel);
|
|
905
|
+
if (typeof model.invoke !== "function") {
|
|
906
|
+
return null;
|
|
907
|
+
}
|
|
908
|
+
const invokeRouter = async (activePrompt, operationName) => this.invokeWithProviderRetry(binding, () => this.withTimeout(() => model.invoke(activePrompt, resolveLangChainInvocationConfig(binding, {
|
|
913
909
|
sessionId,
|
|
914
910
|
requestId,
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
911
|
+
context: options.context,
|
|
912
|
+
toolRuntimeContext: this.buildFunctionToolRuntimeContext(binding, {
|
|
913
|
+
...options,
|
|
914
|
+
sessionId,
|
|
915
|
+
requestId,
|
|
916
|
+
}),
|
|
917
|
+
})), resolveBindingTimeout(binding), operationName, "invoke"));
|
|
918
|
+
const routerPrompts = [
|
|
920
919
|
prompt,
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
920
|
+
[
|
|
921
|
+
prompt,
|
|
922
|
+
"Your previous router output was invalid.",
|
|
923
|
+
"Return only one JSON object now. Do not include prose, markdown, labels, or tool-call wrappers.",
|
|
924
|
+
].join("\n\n"),
|
|
925
|
+
[
|
|
926
|
+
primaryModel.init?.think === false ? "/no_think" : "",
|
|
927
|
+
"Select one subagent from this exact list:",
|
|
928
|
+
Array.from(subagentNames).join(", "),
|
|
929
|
+
"Return JSON only:",
|
|
930
|
+
"{\"subagent_type\":\"<one exact listed name>\"}",
|
|
931
|
+
"User request:",
|
|
932
|
+
requestText,
|
|
933
|
+
].filter(Boolean).join("\n\n"),
|
|
934
|
+
[
|
|
935
|
+
primaryModel.init?.think === false ? "/no_think" : "",
|
|
936
|
+
"JSON only. Pick a listed subagent or refuse.",
|
|
937
|
+
"Listed subagents:",
|
|
938
|
+
Array.from(subagentNames).join(", "),
|
|
939
|
+
"Allowed outputs:",
|
|
940
|
+
"{\"subagent_type\":\"<listed name>\"}",
|
|
941
|
+
"{\"status\":\"refused\",\"reason\":\"No configured subagent can handle the request.\"}",
|
|
942
|
+
"Request:",
|
|
943
|
+
requestText,
|
|
944
|
+
].filter(Boolean).join("\n\n"),
|
|
945
|
+
];
|
|
946
|
+
let previousRawText = "";
|
|
947
|
+
for (let index = 0; index < routerPrompts.length && !selection; index += 1) {
|
|
948
|
+
const activePrompt = index <= 1 || !previousRawText
|
|
949
|
+
? routerPrompts[index]
|
|
950
|
+
: [routerPrompts[index], "Previous output:", previousRawText].join("\n\n");
|
|
951
|
+
const raw = await invokeRouter(activePrompt, index === 0 ? "delegation router invoke" : `delegation router retry invoke ${index}`);
|
|
952
|
+
previousRawText = readModelText(raw);
|
|
953
|
+
selection = parseCompactRouterSelection(previousRawText, subagentNames);
|
|
954
|
+
}
|
|
953
955
|
}
|
|
954
956
|
if (selection?.refusedReason) {
|
|
955
957
|
return {
|
|
@@ -1266,56 +1268,58 @@ export class AgentRuntimeAdapter {
|
|
|
1266
1268
|
"User request:",
|
|
1267
1269
|
requestText,
|
|
1268
1270
|
].filter(Boolean).join("\n\n");
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
requestId,
|
|
1276
|
-
context: options.context,
|
|
1277
|
-
toolRuntimeContext: this.buildFunctionToolRuntimeContext(binding, {
|
|
1278
|
-
...options,
|
|
1271
|
+
if (!selection) {
|
|
1272
|
+
const model = await this.resolveModel(primaryModel);
|
|
1273
|
+
if (typeof model.invoke !== "function") {
|
|
1274
|
+
return null;
|
|
1275
|
+
}
|
|
1276
|
+
const invokeRouter = async (activePrompt, operationName) => this.invokeWithProviderRetry(binding, () => this.withTimeout(() => model.invoke(activePrompt, resolveLangChainInvocationConfig(binding, {
|
|
1279
1277
|
sessionId,
|
|
1280
1278
|
requestId,
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1279
|
+
context: options.context,
|
|
1280
|
+
toolRuntimeContext: this.buildFunctionToolRuntimeContext(binding, {
|
|
1281
|
+
...options,
|
|
1282
|
+
sessionId,
|
|
1283
|
+
requestId,
|
|
1284
|
+
}),
|
|
1285
|
+
})), resolveBindingTimeout(binding), operationName, "invoke"));
|
|
1286
|
+
const routerPrompts = [
|
|
1286
1287
|
prompt,
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1288
|
+
[
|
|
1289
|
+
prompt,
|
|
1290
|
+
"Your previous router output was invalid.",
|
|
1291
|
+
"Return only one JSON object now. Do not include prose, markdown, labels, or tool-call wrappers.",
|
|
1292
|
+
].join("\n\n"),
|
|
1293
|
+
[
|
|
1294
|
+
primaryModel.init?.think === false ? "/no_think" : "",
|
|
1295
|
+
"Select one subagent from this exact list:",
|
|
1296
|
+
Array.from(subagentNames).join(", "),
|
|
1297
|
+
"Return JSON only:",
|
|
1298
|
+
"{\"subagent_type\":\"<one exact listed name>\"}",
|
|
1299
|
+
"User request:",
|
|
1300
|
+
requestText,
|
|
1301
|
+
].filter(Boolean).join("\n\n"),
|
|
1302
|
+
[
|
|
1303
|
+
primaryModel.init?.think === false ? "/no_think" : "",
|
|
1304
|
+
"JSON only. Pick a listed subagent or refuse.",
|
|
1305
|
+
"Listed subagents:",
|
|
1306
|
+
Array.from(subagentNames).join(", "),
|
|
1307
|
+
"Allowed outputs:",
|
|
1308
|
+
"{\"subagent_type\":\"<listed name>\"}",
|
|
1309
|
+
"{\"status\":\"refused\",\"reason\":\"No configured subagent can handle the request.\"}",
|
|
1310
|
+
"Request:",
|
|
1311
|
+
requestText,
|
|
1312
|
+
].filter(Boolean).join("\n\n"),
|
|
1313
|
+
];
|
|
1314
|
+
let previousRawText = "";
|
|
1315
|
+
for (let index = 0; index < routerPrompts.length && !selection; index += 1) {
|
|
1316
|
+
const activePrompt = index <= 1 || !previousRawText
|
|
1317
|
+
? routerPrompts[index]
|
|
1318
|
+
: [routerPrompts[index], "Previous output:", previousRawText].join("\n\n");
|
|
1319
|
+
const raw = await invokeRouter(activePrompt, index === 0 ? "delegation router invoke" : `delegation router retry invoke ${index}`);
|
|
1320
|
+
previousRawText = readModelText(raw);
|
|
1321
|
+
selection = parseCompactRouterSelection(previousRawText, subagentNames);
|
|
1322
|
+
}
|
|
1319
1323
|
}
|
|
1320
1324
|
if (selection?.refusedReason) {
|
|
1321
1325
|
return {
|
|
@@ -1356,61 +1360,87 @@ export class AgentRuntimeAdapter {
|
|
|
1356
1360
|
agentId: selectedBinding.agent.id,
|
|
1357
1361
|
};
|
|
1358
1362
|
const childRequestId = `${requestId}:${subagentType}`;
|
|
1359
|
-
const
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
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 };
|
|
1385
1392
|
}
|
|
1386
|
-
yield { ...chunk, agentId: chunk.agentId ?? selectedBinding.agent.id };
|
|
1387
1393
|
}
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
return {
|
|
1392
|
-
toolOutput: output,
|
|
1393
|
-
delegatedSubagentType: subagentType,
|
|
1394
|
-
delegatedResult: {
|
|
1394
|
+
catch (error) {
|
|
1395
|
+
output = error instanceof Error ? error.message : String(error);
|
|
1396
|
+
return {
|
|
1395
1397
|
sessionId,
|
|
1396
|
-
requestId: childRequestId
|
|
1398
|
+
requestId: `${childRequestId}${requestIdSuffix}`,
|
|
1397
1399
|
agentId: selectedBinding.agent.id,
|
|
1398
1400
|
state: "failed",
|
|
1399
1401
|
output,
|
|
1400
1402
|
finalMessageText: output,
|
|
1401
1403
|
metadata: { executedToolResults },
|
|
1402
|
-
}
|
|
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,
|
|
1403
1442
|
};
|
|
1404
1443
|
}
|
|
1405
|
-
const delegatedResult = {
|
|
1406
|
-
sessionId,
|
|
1407
|
-
requestId: childRequestId,
|
|
1408
|
-
agentId: selectedBinding.agent.id,
|
|
1409
|
-
state: "completed",
|
|
1410
|
-
output: sanitizeVisibleText(output),
|
|
1411
|
-
finalMessageText: sanitizeVisibleText(output),
|
|
1412
|
-
metadata: { executedToolResults },
|
|
1413
|
-
};
|
|
1414
1444
|
return {
|
|
1415
1445
|
toolOutput: resolveDelegatedResultOutput(delegatedResult),
|
|
1416
1446
|
delegatedSubagentType: subagentType,
|