@botbotgo/agent-harness 0.0.342 → 0.0.344

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.
@@ -16,6 +16,7 @@ export declare function streamChatMessage(input: {
16
16
  colorRequestTree?: boolean;
17
17
  showToolResults?: boolean;
18
18
  showRunningState?: boolean;
19
+ idleProgressMs?: number;
19
20
  }): Promise<{
20
21
  sessionId?: string;
21
22
  requestId?: string;
@@ -3,6 +3,7 @@ import { countRenderedLines, renderChatRequestRunning, renderChatTextChunk, summ
3
3
  import { buildTerminalRequestSnapshot, buildTodoContinuationSignature, flattenRequestExecutionSteps, renderRequestEventContinuation, renderRequestSnapshotTree, renderRequestTodoContinuation, } from "./request-tree.js";
4
4
  export async function streamChatMessage(input) {
5
5
  const requestStartedAt = Date.now();
6
+ const idleProgressMs = input.idleProgressMs ?? 30_000;
6
7
  let firstSnapshotAt;
7
8
  let firstDataAt;
8
9
  let latestSnapshot;
@@ -27,6 +28,8 @@ export async function streamChatMessage(input) {
27
28
  const requestTreeRenderThrottleMs = 75;
28
29
  let suppressRequestTreeRendering = false;
29
30
  let lastStableRequestTreeKey;
31
+ let idleProgressTimer;
32
+ let lastRuntimeProgressAt = requestStartedAt;
30
33
  let stdoutWriteChain = Promise.resolve();
31
34
  let stderrWriteChain = Promise.resolve();
32
35
  const enqueueChatWrite = (sink, _stream, chain, message) => chain.then(async () => {
@@ -173,6 +176,27 @@ export async function streamChatMessage(input) {
173
176
  }
174
177
  stderrWriteChain = enqueueChatWrite(input.stderr, input.stderrStream, stderrWriteChain, message);
175
178
  };
179
+ const clearIdleProgressTimer = () => {
180
+ if (idleProgressTimer) {
181
+ clearTimeout(idleProgressTimer);
182
+ idleProgressTimer = undefined;
183
+ }
184
+ };
185
+ const scheduleIdleProgress = () => {
186
+ clearIdleProgressTimer();
187
+ if (idleProgressMs <= 0) {
188
+ return;
189
+ }
190
+ idleProgressTimer = setTimeout(() => {
191
+ const idleSeconds = Math.max(1, Math.round((Date.now() - lastRuntimeProgressAt) / 1000));
192
+ writeChatStderr(`[${formatPerfClock(Date.now())} +${formatElapsed(Date.now())}]${formatAgentProgressLabel(latestAgentId)} Still working; no runtime update for ${idleSeconds}s.\n`);
193
+ scheduleIdleProgress();
194
+ }, idleProgressMs);
195
+ };
196
+ const markRuntimeProgress = () => {
197
+ lastRuntimeProgressAt = Date.now();
198
+ scheduleIdleProgress();
199
+ };
176
200
  const suspendRequestTreeRendering = () => {
177
201
  if (!input.requestEvents || !input.liveRequestTree) {
178
202
  return;
@@ -258,6 +282,7 @@ export async function streamChatMessage(input) {
258
282
  ...(input.sessionId ? { sessionId: input.sessionId } : {}),
259
283
  input: input.message,
260
284
  eventListener(snapshot) {
285
+ markRuntimeProgress();
261
286
  latestSessionId = snapshot.sessionId || latestSessionId;
262
287
  latestRequestId = snapshot.requestId || latestRequestId;
263
288
  latestAgentId = snapshot.agentId || latestAgentId;
@@ -303,6 +328,7 @@ export async function streamChatMessage(input) {
303
328
  }
304
329
  },
305
330
  dataListener(delta) {
331
+ markRuntimeProgress();
306
332
  latestSessionId = delta.sessionId || latestSessionId;
307
333
  latestRequestId = delta.requestId || latestRequestId;
308
334
  firstDataAt ??= Date.now();
@@ -342,6 +368,7 @@ export async function streamChatMessage(input) {
342
368
  }
343
369
  },
344
370
  });
371
+ clearIdleProgressTimer();
345
372
  if (!result) {
346
373
  throw new Error("chat request completed without a terminal result");
347
374
  }
@@ -161,10 +161,12 @@ export function renderChatRuntimeFailure(output, modelInfo) {
161
161
  if (!normalized.includes("fetch failed") &&
162
162
  !normalized.includes("connection error") &&
163
163
  !normalized.includes("timed out") &&
164
+ !normalized.includes("error 524") &&
164
165
  !normalized.includes("404 page not found")) {
165
166
  return output;
166
167
  }
167
168
  const lines = [trimmed];
169
+ const isTimeoutStyleError = /(\berror 524\b|\btimeout\b|\btimed out\b)/i.test(trimmed);
168
170
  if (modelInfo?.provider || modelInfo?.model) {
169
171
  lines.push(`provider=${modelInfo?.provider ?? "unknown"}${modelInfo?.model ? ` model=${modelInfo.model}` : ""}`);
170
172
  }
@@ -178,6 +180,9 @@ export function renderChatRuntimeFailure(output, modelInfo) {
178
180
  if (hint) {
179
181
  lines.push(hint);
180
182
  }
183
+ if (isTimeoutStyleError) {
184
+ lines.push("Hint: this usually indicates the model endpoint dropped the connection before producing a full answer.");
185
+ }
181
186
  return lines.join("\n");
182
187
  }
183
188
  export async function probeChatWorkspace(input) {
package/dist/cli.js CHANGED
@@ -4,5 +4,5 @@ import { runCli, resolveInvokedCliHref } from "./cli/main.js";
4
4
  const invokedPath = resolveInvokedCliHref(process.argv[1]);
5
5
  if (import.meta.url === invokedPath) {
6
6
  const exitCode = await runCli(process.argv.slice(2));
7
- process.exitCode = exitCode;
7
+ process.exit(exitCode);
8
8
  }
@@ -187,6 +187,7 @@ export type CompiledSubAgent = {
187
187
  builtinTools?: {
188
188
  filesystem?: boolean;
189
189
  todos?: boolean;
190
+ modelExposed?: boolean | string[];
190
191
  };
191
192
  };
192
193
  export type CompiledAsyncSubAgent = {
@@ -197,6 +198,11 @@ export type CompiledAsyncSubAgent = {
197
198
  headers?: Record<string, string>;
198
199
  };
199
200
  export type CompiledDeepAgentSubAgent = CompiledSubAgent | CompiledAsyncSubAgent;
201
+ export type CompiledBuiltinToolsConfig = {
202
+ filesystem?: boolean;
203
+ todos?: boolean;
204
+ modelExposed?: boolean | string[];
205
+ };
200
206
  export type LangChainAgentParams = {
201
207
  model: CompiledModel;
202
208
  tools: CompiledTool[];
@@ -209,6 +215,7 @@ export type LangChainAgentParams = {
209
215
  version?: "v1" | "v2";
210
216
  name?: string;
211
217
  description: string;
218
+ builtinTools?: CompiledBuiltinToolsConfig;
212
219
  };
213
220
  export type DeepAgentParams = {
214
221
  model: CompiledModel;
@@ -225,10 +232,7 @@ export type DeepAgentParams = {
225
232
  memory: string[];
226
233
  skills: string[];
227
234
  interactionMode?: "stream" | "invoke";
228
- builtinTools?: {
229
- filesystem?: boolean;
230
- todos?: boolean;
231
- };
235
+ builtinTools?: CompiledBuiltinToolsConfig;
232
236
  };
233
237
  export type LegacyLangChainAgentParams = LangChainAgentParams & {
234
238
  passthrough?: Record<string, unknown>;
@@ -1,2 +1,2 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.342";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.344";
2
2
  export declare const AGENT_HARNESS_RELEASE_DATE = "2026-04-24";
@@ -1,2 +1,2 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.342";
1
+ export const AGENT_HARNESS_VERSION = "0.0.344";
2
2
  export const AGENT_HARNESS_RELEASE_DATE = "2026-04-24";
@@ -84,6 +84,19 @@ function hasMissingDelegatedExecutionEvidence(evidence) {
84
84
  function hasMissingDelegatedFindings(evidence) {
85
85
  return evidence.hasDelegatedAgentWithConfiguredTools && evidence.hasOnlyPlaceholderTaskCompletion;
86
86
  }
87
+ function isRuntimeFailureOutput(value) {
88
+ return value.trim().startsWith("runtime_error=");
89
+ }
90
+ function resolveStreamedRuntimeFailureRecoveryInstruction(output, evidence) {
91
+ if (!isRuntimeFailureOutput(output)) {
92
+ return null;
93
+ }
94
+ const hasExecutionEvidence = evidence.hasToolResultEvidence
95
+ || evidence.hasPlanStateEvidence
96
+ || evidence.hasOpenTaskDelegation
97
+ || evidence.hasDelegatedExecutionToolEvidence;
98
+ return hasExecutionEvidence ? null : EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION;
99
+ }
87
100
  function resolveDelegatedExecutionRecoveryInstruction(evidence) {
88
101
  if (hasMissingDelegatedFindings(evidence)
89
102
  || (evidence.hasOpenTaskDelegation
@@ -442,6 +455,9 @@ export async function* streamRuntimeExecution(options) {
442
455
  hasMissingDelegatedExecutionEvidence: hasMissingDelegatedExecutionEvidence(streamedExecutionEvidence),
443
456
  })
444
457
  : null;
458
+ const streamedRuntimeFailureRecoveryInstruction = projectionState.emittedOutput
459
+ ? resolveStreamedRuntimeFailureRecoveryInstruction(projectionState.emittedOutput, streamedExecutionEvidence)
460
+ : null;
445
461
  const missingPlanRecoveryInstruction = !hasUnresolvedExecution(streamedExecutionEvidence) && !delegatedExecutionRecoveryInstruction
446
462
  ? resolveMissingPlanRecoveryInstruction({
447
463
  request,
@@ -453,7 +469,10 @@ export async function* streamRuntimeExecution(options) {
453
469
  : null;
454
470
  const retryInstruction = !emittedUnsafeStreamSideEffects && sawRetrySafeInvalidToolSelectionError
455
471
  ? INVALID_TOOL_SELECTION_RECOVERY_INSTRUCTION
456
- : delegatedExecutionRecoveryInstruction ?? missingPlanRecoveryInstruction ?? executionWithoutToolEvidenceInstruction;
472
+ : delegatedExecutionRecoveryInstruction
473
+ ?? streamedRuntimeFailureRecoveryInstruction
474
+ ?? missingPlanRecoveryInstruction
475
+ ?? executionWithoutToolEvidenceInstruction;
457
476
  if (retryInstruction) {
458
477
  let retried;
459
478
  retried = await options.invoke(options.applyToolRecoveryInstruction(options.binding, retryInstruction), options.input, options.sessionId, options.runtimeOptions.requestId ?? options.sessionId, undefined, options.history, options.runtimeOptions);
@@ -32,6 +32,10 @@ export function resolveStreamIdleTimeout(binding) {
32
32
  }
33
33
  const BUILTIN_RETRYABLE_PROVIDER_MESSAGES = [
34
34
  "unexpected eof",
35
+ "other side closed",
36
+ "socket hang up",
37
+ "connection reset",
38
+ "econnreset",
35
39
  ];
36
40
  function isEmptyFinalAiMessageError(error) {
37
41
  const message = error instanceof Error ? error.message : String(error);
@@ -246,6 +246,7 @@ export declare const BUILTIN_MIDDLEWARE_TOOL_DESCRIPTORS: readonly [{
246
246
  export declare function filterBuiltinMiddlewareToolDescriptors(options?: {
247
247
  filesystem?: boolean;
248
248
  todos?: boolean;
249
+ modelExposed?: boolean | string[];
249
250
  }): Array<{
250
251
  name: string;
251
252
  description: string;
@@ -29,6 +29,16 @@ export const BUILTIN_MIDDLEWARE_TOOL_DESCRIPTORS = [
29
29
  { name: "task", description: "Delegate a bounded task to a subagent." },
30
30
  ];
31
31
  export function filterBuiltinMiddlewareToolDescriptors(options) {
32
+ const modelExposedNames = new Set([
33
+ "fetch_url",
34
+ "http_request",
35
+ "send_message",
36
+ "request_approval",
37
+ "schedule_task",
38
+ ]);
39
+ const allowedModelExposedNames = Array.isArray(options?.modelExposed)
40
+ ? new Set(options.modelExposed)
41
+ : undefined;
32
42
  return BUILTIN_MIDDLEWARE_TOOL_DESCRIPTORS.filter((descriptor) => {
33
43
  if (options?.todos === false
34
44
  && (descriptor.name === "write_todos" || descriptor.name === "read_todos")) {
@@ -49,6 +59,14 @@ export function filterBuiltinMiddlewareToolDescriptors(options) {
49
59
  ].includes(descriptor.name)) {
50
60
  return false;
51
61
  }
62
+ if (modelExposedNames.has(descriptor.name)) {
63
+ if (options?.modelExposed === false) {
64
+ return false;
65
+ }
66
+ if (allowedModelExposedNames && !allowedModelExposedNames.has(descriptor.name)) {
67
+ return false;
68
+ }
69
+ }
52
70
  return true;
53
71
  }).map((descriptor) => ({ ...descriptor }));
54
72
  }
@@ -360,6 +360,7 @@ export class AgentRuntimeAdapter {
360
360
  }),
361
361
  }),
362
362
  explicitToolNames: primaryTools.map((tool) => tool.name),
363
+ modelExposed: getBindingBuiltinToolsConfig(binding)?.modelExposed,
363
364
  });
364
365
  const resolvedMiddleware = await this.resolveMiddleware(binding, interruptOn, { sessionId: options.sessionId ?? options.legacySessionId });
365
366
  const resolvedCheckpointer = resolveRunnableCheckpointer(this.options, binding);
@@ -405,6 +406,7 @@ export class AgentRuntimeAdapter {
405
406
  }),
406
407
  }),
407
408
  explicitToolNames: primaryTools.map((tool) => tool.name),
409
+ modelExposed: getBindingBuiltinToolsConfig(binding)?.modelExposed,
408
410
  });
409
411
  const resolvedMiddleware = await this.resolveMiddleware(binding);
410
412
  const resolvedSubagents = await this.resolveDeepAgentSubagents(getBindingDeepAgentSubagents(binding), binding, { sessionId: options.sessionId ?? options.legacySessionId });
@@ -6,6 +6,7 @@ export declare const DEFAULT_DEEPAGENT_RECURSION_LIMIT = 100;
6
6
  export declare function materializeModelExposedBuiltinMiddlewareTools(input: {
7
7
  builtinTools: Map<string, ExecutableTool>;
8
8
  explicitToolNames?: string[];
9
+ modelExposed?: boolean | string[];
9
10
  }): unknown[];
10
11
  export declare function resolveRunnableCheckpointer(options: RuntimeAdapterOptions, binding: CompiledAgentBinding): unknown;
11
12
  export declare function resolveRunnableInterruptOn(binding: CompiledAgentBinding): Record<string, {
@@ -18,12 +18,19 @@ const MODEL_EXPOSED_BUILTIN_MIDDLEWARE_TOOL_NAMES = new Set([
18
18
  "schedule_task",
19
19
  ]);
20
20
  export function materializeModelExposedBuiltinMiddlewareTools(input) {
21
+ if (input.modelExposed === false) {
22
+ return [];
23
+ }
21
24
  const explicitToolNames = new Set(input.explicitToolNames ?? []);
25
+ const allowedToolNames = Array.isArray(input.modelExposed)
26
+ ? new Set(input.modelExposed)
27
+ : undefined;
22
28
  const tools = [];
23
29
  for (const [toolName, tool] of input.builtinTools.entries()) {
24
30
  if (explicitToolNames.has(toolName)
25
31
  || BUILTIN_MIDDLEWARE_ALIAS_TOOL_NAMES.has(toolName)
26
- || !MODEL_EXPOSED_BUILTIN_MIDDLEWARE_TOOL_NAMES.has(toolName)) {
32
+ || !MODEL_EXPOSED_BUILTIN_MIDDLEWARE_TOOL_NAMES.has(toolName)
33
+ || (allowedToolNames && !allowedToolNames.has(toolName))) {
27
34
  continue;
28
35
  }
29
36
  tools.push(asStructuredExecutableTool(tool, toolName, tool.name));
@@ -11,6 +11,13 @@ function asObject(value) {
11
11
  function readStringArray(value) {
12
12
  return Array.isArray(value) ? value.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
13
13
  }
14
+ const MODEL_EXPOSED_BUILTIN_TOOL_NAMES = new Set([
15
+ "fetch_url",
16
+ "http_request",
17
+ "send_message",
18
+ "request_approval",
19
+ "schedule_task",
20
+ ]);
14
21
  function getBindingSkillNames(binding) {
15
22
  if (!binding) {
16
23
  return [];
@@ -106,13 +113,20 @@ export function buildRequestRuntimeSnapshot(binding, options) {
106
113
  name: tool.name,
107
114
  description: tool.description,
108
115
  }));
116
+ const builtinToolsConfig = getBindingBuiltinToolsConfig(binding);
117
+ const builtinToolDescriptors = filterBuiltinMiddlewareToolDescriptors(builtinToolsConfig)
118
+ .filter((descriptor) => !declaredTools.some((tool) => tool.name === descriptor.name));
109
119
  const tools = isDeepAgentBinding(binding)
110
120
  ? [
111
121
  ...declaredTools,
112
- ...filterBuiltinMiddlewareToolDescriptors(getBindingBuiltinToolsConfig(binding))
113
- .filter((descriptor) => !declaredTools.some((tool) => tool.name === descriptor.name)),
122
+ ...builtinToolDescriptors,
114
123
  ]
115
- : declaredTools;
124
+ : builtinToolsConfig !== undefined
125
+ ? [
126
+ ...declaredTools,
127
+ ...builtinToolDescriptors.filter((descriptor) => MODEL_EXPOSED_BUILTIN_TOOL_NAMES.has(descriptor.name)),
128
+ ]
129
+ : declaredTools;
116
130
  return {
117
131
  agentId: binding.agent.id,
118
132
  ...(model ? {
@@ -332,7 +332,16 @@ function normalizeStreamChunk(chunk) {
332
332
  return { kind: "content", content: chunk.content ?? "" };
333
333
  }
334
334
  function normalizeCommentaryText(value) {
335
- return value.replace(/\s+/g, " ").trim();
335
+ return value
336
+ .split("\n")
337
+ .map((line) => {
338
+ const leading = /^\s*/.exec(line)?.[0] ?? "";
339
+ const body = line.slice(leading.length).replace(/[^\S\n]+/g, " ").trimEnd();
340
+ return `${leading}${body}`;
341
+ })
342
+ .join("\n")
343
+ .replace(/\n{3,}/g, "\n\n")
344
+ .trim();
336
345
  }
337
346
  function ensureCommentarySentence(value) {
338
347
  const normalized = normalizeCommentaryText(value);
@@ -359,9 +368,9 @@ function summarizePlanState(planState) {
359
368
  return "[ ]";
360
369
  }
361
370
  };
362
- const items = planState.items.slice(0, 6).map((item) => `${statusMarker(item.status)} ${item.content}`);
363
- const suffix = planState.items.length > items.length ? ` | +${planState.items.length - items.length} more` : "";
364
- return `TODO: ${items.join(" | ")}${suffix}`;
371
+ const items = planState.items.slice(0, 6).map((item) => ` ${statusMarker(item.status)} ${item.content}`);
372
+ const suffix = planState.items.length > items.length ? [` ... ${planState.items.length - items.length} more`] : [];
373
+ return ["TODO", ...items, ...suffix].join("\n");
365
374
  }
366
375
  function summarizePlanStateTerminalTransitions(previousPlanState, nextPlanState) {
367
376
  const previousByKey = new Map((previousPlanState?.items ?? []).map((item) => [normalizePlanItemKey(item), item]));
@@ -523,6 +532,45 @@ function createRuntimeMemoryRecallSteps(sessionId, requestId, items) {
523
532
  },
524
533
  ];
525
534
  }
535
+ function readObject(value) {
536
+ return typeof value === "object" && value !== null ? value : null;
537
+ }
538
+ function isFallbackSafeUpstreamStartupEvent(event, rootAgentId) {
539
+ const typed = readObject(event);
540
+ const eventName = typeof typed?.event === "string" ? typed.event : "";
541
+ if (eventName.startsWith("on_chat_model_") || eventName.startsWith("on_llm_")) {
542
+ return true;
543
+ }
544
+ if (!eventName.startsWith("on_chain_")) {
545
+ return false;
546
+ }
547
+ if (typed?.run_type === "tool") {
548
+ return false;
549
+ }
550
+ const name = typeof typed?.name === "string" ? typed.name : "";
551
+ const metadata = readObject(typed?.metadata);
552
+ const langgraphNode = typeof metadata?.langgraph_node === "string" ? metadata.langgraph_node : "";
553
+ const langchainAgentName = typeof metadata?.lc_agent_name === "string" ? metadata.lc_agent_name : "";
554
+ return (name === "__start__"
555
+ || langgraphNode === "__start__"
556
+ || langgraphNode === "model_request"
557
+ || name.endsWith(".before_agent")
558
+ || name.endsWith(".before_model")
559
+ || (name === rootAgentId && langchainAgentName === rootAgentId));
560
+ }
561
+ function planStateHasAnyItems(planState) {
562
+ return (planState?.items.length ?? 0) > 0;
563
+ }
564
+ function isStartupRuntimeFailureOutput(value) {
565
+ const normalized = value.trim().toLowerCase();
566
+ if (!normalized.startsWith("runtime_error=")) {
567
+ return false;
568
+ }
569
+ return ((normalized.includes("terminated") && normalized.includes("other side closed"))
570
+ || normalized.includes("socket hang up")
571
+ || normalized.includes("connection reset")
572
+ || normalized.includes("econnreset"));
573
+ }
526
574
  export async function* streamHarnessRun(options) {
527
575
  const priorHistoryPromise = Promise.resolve(options.isNewSession ? [] : undefined).then((historyHint) => historyHint ?? options.loadPriorHistory(options.sessionId, options.requestId));
528
576
  yield { type: "event", event: await options.requestCreatedEventPromise };
@@ -533,6 +581,7 @@ export async function* streamHarnessRun(options) {
533
581
  let emitted = false;
534
582
  let streamActivityObserved = false;
535
583
  let nonUpstreamStreamActivityObserved = false;
584
+ let observedOnlyFallbackSafeUpstreamEvents = true;
536
585
  let assistantOutput = "";
537
586
  let assistantOutputCameFromInvokeFallback = false;
538
587
  const bufferAssistantTextUntilCompletion = true;
@@ -589,6 +638,9 @@ export async function* streamHarnessRun(options) {
589
638
  streamActivityObserved = true;
590
639
  const normalizedChunk = normalizeStreamChunk(rawChunk);
591
640
  if (normalizedChunk.kind === "upstream-event") {
641
+ if (!isFallbackSafeUpstreamStartupEvent(normalizedChunk.event, options.selectedAgentId)) {
642
+ observedOnlyFallbackSafeUpstreamEvents = false;
643
+ }
592
644
  const upstreamPlanState = getPlanStateFromUpstreamEvent({
593
645
  sessionId: options.sessionId,
594
646
  requestId: options.requestId,
@@ -833,6 +885,15 @@ export async function* streamHarnessRun(options) {
833
885
  emitted = emitted || assistantOutput.length > 0;
834
886
  }
835
887
  currentPlanState = await refreshPlanStateFromPersistence(options, currentPlanState);
888
+ if (assistantOutput
889
+ && isStartupRuntimeFailureOutput(assistantOutput)
890
+ && executedToolResults.length === 0
891
+ && !sawSuccessfulToolResult
892
+ && observedOnlyFallbackSafeUpstreamEvents
893
+ && !planStateHasAnyItems(currentPlanState)) {
894
+ assistantOutput = "";
895
+ emitted = false;
896
+ }
836
897
  if (!assistantOutput) {
837
898
  const actual = await options.invokeWithHistory(options.binding, options.input, options.sessionId, options.requestId);
838
899
  if (Array.isArray(actual.contentBlocks) && actual.contentBlocks.length > 0) {
@@ -1019,8 +1080,12 @@ export async function* streamHarnessRun(options) {
1019
1080
  }
1020
1081
  const shouldRetryAfterStreamingCompatibilityError = !assistantOutput &&
1021
1082
  isOpenAICompatibleStreamingCompatibilityError(options.binding, error);
1022
- if ((emitted || streamActivityObserved)
1023
- && !shouldRetryAfterStreamingCompatibilityError) {
1083
+ const streamHadExecutionSideEffects = emitted
1084
+ || nonUpstreamStreamActivityObserved
1085
+ || sawSuccessfulToolResult
1086
+ || executedToolResults.length > 0
1087
+ || !observedOnlyFallbackSafeUpstreamEvents;
1088
+ if (streamHadExecutionSideEffects && !shouldRetryAfterStreamingCompatibilityError) {
1024
1089
  const runtimeFailure = renderRuntimeFailure(error);
1025
1090
  const detailedError = describeRuntimeError(error);
1026
1091
  yield {
@@ -132,6 +132,7 @@ export declare class AgentHarnessRuntime {
132
132
  private emit;
133
133
  private trackBackgroundTask;
134
134
  private scheduleBackgroundStartupTask;
135
+ private drainBackgroundTasksForClose;
135
136
  private resolveToolMcpServerTools;
136
137
  private loadPriorHistory;
137
138
  private loadRequestInput;
@@ -46,6 +46,7 @@ import { defaultRequestedAgentId, prepareRunStart } from "./harness/run/start-ru
46
46
  import { buildRequestInspectionRecord, buildSessionInspectionRecord, deleteSessionRecord, getPublicApproval, listPublicApprovals, } from "./harness/run/session-records.js";
47
47
  import { createKnowledgeModule } from "../knowledge/index.js";
48
48
  import { createProceduralMemoryManager, ProceduralMemoryFormationSync, readProceduralMemoryRuntimeConfig, } from "../knowledge/procedural/index.js";
49
+ const BACKGROUND_TASK_CLOSE_DRAIN_TIMEOUT_MS = 1_000;
49
50
  const ACTIVE_REQUEST_STATES = [
50
51
  "queued",
51
52
  "claimed",
@@ -945,6 +946,23 @@ export class AgentHarnessRuntime {
945
946
  scheduleBackgroundStartupTask(task) {
946
947
  this.trackBackgroundTask(task.then(() => undefined).catch(() => undefined));
947
948
  }
949
+ async drainBackgroundTasksForClose() {
950
+ const tasks = Array.from(this.backgroundTasks);
951
+ if (tasks.length === 0) {
952
+ return;
953
+ }
954
+ let timeoutHandle;
955
+ await Promise.race([
956
+ Promise.allSettled(tasks),
957
+ new Promise((resolve) => {
958
+ timeoutHandle = setTimeout(resolve, BACKGROUND_TASK_CLOSE_DRAIN_TIMEOUT_MS);
959
+ timeoutHandle.unref?.();
960
+ }),
961
+ ]);
962
+ if (timeoutHandle) {
963
+ clearTimeout(timeoutHandle);
964
+ }
965
+ }
948
966
  resolveToolMcpServerTools(agentId) {
949
967
  return resolveWorkspaceAgentTools({
950
968
  workspace: this.workspace,
@@ -1375,7 +1393,7 @@ export class AgentHarnessRuntime {
1375
1393
  this.unregisterMem0IngestionSync();
1376
1394
  this.unregisterRuntimeMemoryFormationSync();
1377
1395
  this.unregisterProceduralMemoryFormationSync();
1378
- await Promise.allSettled(Array.from(this.backgroundTasks));
1396
+ await this.drainBackgroundTasksForClose();
1379
1397
  await this.sessionMemorySync?.close();
1380
1398
  await this.runtimeMemorySync?.close();
1381
1399
  await this.mem0IngestionSync?.close();
@@ -1,4 +1,4 @@
1
- import type { CompiledAgentBinding, CompiledDeepAgentSubAgent, CompiledExecutionBinding, CompiledModel, CompiledSubAgent, CompiledTool, DeepAgentParams, LegacyDeepAgentParams, LegacyLangChainAgentParams, LangChainAgentParams } from "../../contracts/types.js";
1
+ import type { CompiledAgentBinding, CompiledBuiltinToolsConfig, CompiledDeepAgentSubAgent, CompiledExecutionBinding, CompiledModel, CompiledSubAgent, CompiledTool, DeepAgentParams, LegacyDeepAgentParams, LegacyLangChainAgentParams, LangChainAgentParams } from "../../contracts/types.js";
2
2
  export type BindingExecutionView = {
3
3
  adapterKind: string;
4
4
  langchainParams?: LegacyLangChainAgentParams;
@@ -32,10 +32,7 @@ export declare function getBindingGeneralPurposeAgent(binding: CompiledAgentBind
32
32
  export declare function getBindingTaskDescription(binding: CompiledAgentBinding): string | undefined;
33
33
  export declare function getBindingBackendConfig(binding: CompiledAgentBinding): Record<string, unknown> | undefined;
34
34
  export declare function getBindingInteractionMode(binding: CompiledAgentBinding): "stream" | "invoke" | undefined;
35
- export declare function getBindingBuiltinToolsConfig(binding: CompiledAgentBinding): {
36
- filesystem?: boolean;
37
- todos?: boolean;
38
- } | undefined;
35
+ export declare function getBindingBuiltinToolsConfig(binding: CompiledAgentBinding): CompiledBuiltinToolsConfig | undefined;
39
36
  export declare function getBindingFilesystemConfig(binding: CompiledAgentBinding): Record<string, unknown> | undefined;
40
37
  export declare function isLangChainBinding(binding: CompiledAgentBinding): boolean;
41
38
  export declare function isDeepAgentBinding(binding: CompiledAgentBinding): boolean;
@@ -463,6 +463,7 @@ export function compileBinding(workspaceRoot, agent, agents, referencedSubagentI
463
463
  : undefined,
464
464
  name: resolveAgentRuntimeName(agent),
465
465
  description: agent.description,
466
+ builtinTools: getAgentExecutionObject(agent, "builtinTools"),
466
467
  },
467
468
  };
468
469
  return attachLegacyExecutionAliases({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.342",
3
+ "version": "0.0.344",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "license": "MIT",
6
6
  "type": "module",