@botbotgo/agent-harness 0.0.30 → 0.0.32
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.31";
|
package/dist/package-version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export const AGENT_HARNESS_VERSION = "0.0.31";
|
|
@@ -7,13 +7,16 @@ type RunnableLike = {
|
|
|
7
7
|
};
|
|
8
8
|
declare const AGENT_INTERRUPT_SENTINEL_PREFIX = "__agent_harness_interrupt__:";
|
|
9
9
|
declare class RuntimeOperationTimeoutError extends Error {
|
|
10
|
+
readonly operation: string;
|
|
10
11
|
readonly timeoutMs: number;
|
|
11
|
-
|
|
12
|
+
readonly stage: "stream" | "invoke";
|
|
13
|
+
constructor(operation: string, timeoutMs: number, stage?: "stream" | "invoke");
|
|
12
14
|
}
|
|
13
15
|
export declare class AgentRuntimeAdapter {
|
|
14
16
|
private readonly options;
|
|
15
17
|
constructor(options?: RuntimeAdapterOptions);
|
|
16
18
|
private resolveBindingTimeout;
|
|
19
|
+
private resolveStreamIdleTimeout;
|
|
17
20
|
private withTimeout;
|
|
18
21
|
private iterateWithTimeout;
|
|
19
22
|
private materializeModelStream;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { Command, MemorySaver } from "@langchain/langgraph";
|
|
2
|
+
import { tool as createLangChainTool } from "@langchain/core/tools";
|
|
2
3
|
import { createDeepAgent } from "deepagents";
|
|
3
4
|
import { ChatAnthropic } from "@langchain/anthropic";
|
|
4
5
|
import { ChatGoogle } from "@langchain/google";
|
|
5
6
|
import { ChatOllama } from "@langchain/ollama";
|
|
6
7
|
import { ChatOpenAI } from "@langchain/openai";
|
|
7
8
|
import { createAgent, humanInTheLoopMiddleware, initChatModel } from "langchain";
|
|
9
|
+
import { z } from "zod";
|
|
8
10
|
import { extractEmptyAssistantMessageFailure, extractReasoningText, extractToolFallbackContext, extractVisibleOutput, isLikelyToolArgsObject, isToolCallParseFailure, STRICT_TOOL_JSON_INSTRUCTION, sanitizeVisibleText, tryParseJson, wrapResolvedModel, } from "./parsing/output-parsing.js";
|
|
9
11
|
import { extractAgentStep, extractInterruptPayload, extractReasoningStreamOutput, extractTerminalStreamOutput, extractToolResult, normalizeTerminalOutputKey, readStreamDelta, } from "./parsing/stream-event-parsing.js";
|
|
10
12
|
import { wrapToolForExecution } from "./tool-hitl.js";
|
|
@@ -13,10 +15,14 @@ import { extractMessageText, normalizeMessageContent } from "../utils/message-co
|
|
|
13
15
|
const AGENT_INTERRUPT_SENTINEL_PREFIX = "__agent_harness_interrupt__:";
|
|
14
16
|
const MODEL_SAFE_TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
15
17
|
class RuntimeOperationTimeoutError extends Error {
|
|
18
|
+
operation;
|
|
16
19
|
timeoutMs;
|
|
17
|
-
|
|
20
|
+
stage;
|
|
21
|
+
constructor(operation, timeoutMs, stage = operation.includes("stream") ? "stream" : "invoke") {
|
|
18
22
|
super(`${operation} timed out after ${timeoutMs}ms`);
|
|
23
|
+
this.operation = operation;
|
|
19
24
|
this.timeoutMs = timeoutMs;
|
|
25
|
+
this.stage = stage;
|
|
20
26
|
this.name = "RuntimeOperationTimeoutError";
|
|
21
27
|
}
|
|
22
28
|
}
|
|
@@ -116,6 +122,37 @@ function wrapResolvedToolWithModelFacingName(resolvedTool, modelFacingName) {
|
|
|
116
122
|
},
|
|
117
123
|
});
|
|
118
124
|
}
|
|
125
|
+
function hasCallableToolHandler(value) {
|
|
126
|
+
if (typeof value !== "object" || value === null) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
const typed = value;
|
|
130
|
+
return typeof typed.invoke === "function" || typeof typed.call === "function" || typeof typed.func === "function";
|
|
131
|
+
}
|
|
132
|
+
function normalizeResolvedToolSchema(resolvedTool) {
|
|
133
|
+
const schema = resolvedTool.schema;
|
|
134
|
+
if (schema && typeof schema.parse === "function" && "_def" in schema) {
|
|
135
|
+
return resolvedTool.schema;
|
|
136
|
+
}
|
|
137
|
+
if (schema && (schema.type === "object" || typeof schema.properties === "object")) {
|
|
138
|
+
return schema;
|
|
139
|
+
}
|
|
140
|
+
return z.object({}).passthrough();
|
|
141
|
+
}
|
|
142
|
+
function asStructuredExecutableTool(resolvedTool, modelFacingName, description) {
|
|
143
|
+
if (!hasCallableToolHandler(resolvedTool)) {
|
|
144
|
+
return resolvedTool;
|
|
145
|
+
}
|
|
146
|
+
const handler = resolvedTool.invoke ?? resolvedTool.call ?? resolvedTool.func;
|
|
147
|
+
if (typeof handler !== "function") {
|
|
148
|
+
return resolvedTool;
|
|
149
|
+
}
|
|
150
|
+
return createLangChainTool(async (input, config) => handler(input, config), {
|
|
151
|
+
name: modelFacingName,
|
|
152
|
+
description,
|
|
153
|
+
schema: normalizeResolvedToolSchema(resolvedTool),
|
|
154
|
+
});
|
|
155
|
+
}
|
|
119
156
|
function countConfiguredTools(binding) {
|
|
120
157
|
return binding.langchainAgentParams?.tools.length ?? binding.deepAgentParams?.tools.length ?? 0;
|
|
121
158
|
}
|
|
@@ -127,12 +164,23 @@ export class AgentRuntimeAdapter {
|
|
|
127
164
|
resolveBindingTimeout(binding) {
|
|
128
165
|
return resolveTimeoutMs(binding.langchainAgentParams?.model.init.timeout ?? binding.deepAgentParams?.model.init.timeout);
|
|
129
166
|
}
|
|
130
|
-
|
|
167
|
+
resolveStreamIdleTimeout(binding) {
|
|
168
|
+
const configuredIdleTimeout = resolveTimeoutMs(binding.langchainAgentParams?.model.init.streamIdleTimeout ?? binding.deepAgentParams?.model.init.streamIdleTimeout);
|
|
169
|
+
if (configuredIdleTimeout) {
|
|
170
|
+
return configuredIdleTimeout;
|
|
171
|
+
}
|
|
172
|
+
const invokeTimeout = this.resolveBindingTimeout(binding);
|
|
173
|
+
if (invokeTimeout) {
|
|
174
|
+
return Math.min(invokeTimeout, 15_000);
|
|
175
|
+
}
|
|
176
|
+
return 15_000;
|
|
177
|
+
}
|
|
178
|
+
async withTimeout(producer, timeoutMs, operation, stage = operation.includes("stream") ? "stream" : "invoke") {
|
|
131
179
|
if (!timeoutMs) {
|
|
132
180
|
return Promise.resolve(producer());
|
|
133
181
|
}
|
|
134
182
|
return new Promise((resolve, reject) => {
|
|
135
|
-
const timer = setTimeout(() => reject(new RuntimeOperationTimeoutError(operation, timeoutMs)), timeoutMs);
|
|
183
|
+
const timer = setTimeout(() => reject(new RuntimeOperationTimeoutError(operation, timeoutMs, stage)), timeoutMs);
|
|
136
184
|
Promise.resolve(producer()).then((value) => {
|
|
137
185
|
clearTimeout(timer);
|
|
138
186
|
resolve(value);
|
|
@@ -146,7 +194,7 @@ export class AgentRuntimeAdapter {
|
|
|
146
194
|
const iterator = iterable[Symbol.asyncIterator]();
|
|
147
195
|
try {
|
|
148
196
|
for (;;) {
|
|
149
|
-
const next = await this.withTimeout(() => iterator.next(), timeoutMs, operation);
|
|
197
|
+
const next = await this.withTimeout(() => iterator.next(), timeoutMs, operation, "stream");
|
|
150
198
|
if (next.done) {
|
|
151
199
|
return;
|
|
152
200
|
}
|
|
@@ -251,7 +299,7 @@ export class AgentRuntimeAdapter {
|
|
|
251
299
|
role: "user",
|
|
252
300
|
content: `Original user request:\n${extractMessageText(input)}\n\nTool results:\n${toolContext}`,
|
|
253
301
|
},
|
|
254
|
-
]), this.resolveBindingTimeout(binding), "deepagent synthesis invoke");
|
|
302
|
+
]), this.resolveBindingTimeout(binding), "deepagent synthesis invoke", "invoke");
|
|
255
303
|
return sanitizeVisibleText(extractVisibleOutput(synthesized));
|
|
256
304
|
}
|
|
257
305
|
async resolveModel(model) {
|
|
@@ -302,6 +350,10 @@ export class AgentRuntimeAdapter {
|
|
|
302
350
|
}
|
|
303
351
|
const wrappedTool = wrapToolForExecution(resolvedTool, compiledTool);
|
|
304
352
|
const modelFacingName = toolNameMapping.originalToModelFacing.get(compiledTool.name) ?? compiledTool.name;
|
|
353
|
+
const structuredTool = asStructuredExecutableTool(wrappedTool, modelFacingName, compiledTool.description);
|
|
354
|
+
if (structuredTool !== wrappedTool) {
|
|
355
|
+
return structuredTool;
|
|
356
|
+
}
|
|
305
357
|
return modelFacingName === compiledTool.name ? wrappedTool : wrapResolvedToolWithModelFacingName(wrappedTool, modelFacingName);
|
|
306
358
|
});
|
|
307
359
|
}
|
|
@@ -470,7 +522,7 @@ export class AgentRuntimeAdapter {
|
|
|
470
522
|
let result;
|
|
471
523
|
try {
|
|
472
524
|
const runnable = await this.create(binding);
|
|
473
|
-
result = (await this.withTimeout(() => runnable.invoke(request, { configurable: { thread_id: threadId } }), this.resolveBindingTimeout(binding), "agent invoke"));
|
|
525
|
+
result = (await this.withTimeout(() => runnable.invoke(request, { configurable: { thread_id: threadId } }), this.resolveBindingTimeout(binding), "agent invoke", "invoke"));
|
|
474
526
|
}
|
|
475
527
|
catch (error) {
|
|
476
528
|
if (resumePayload !== undefined || !isToolCallParseFailure(error)) {
|
|
@@ -478,7 +530,7 @@ export class AgentRuntimeAdapter {
|
|
|
478
530
|
}
|
|
479
531
|
const retriedBinding = this.applyStrictToolJsonInstruction(binding);
|
|
480
532
|
const runnable = await this.create(retriedBinding);
|
|
481
|
-
result = (await this.withTimeout(() => runnable.invoke({ messages: this.buildAgentMessages(history, input) }, { configurable: { thread_id: threadId } }), this.resolveBindingTimeout(retriedBinding), "agent invoke"));
|
|
533
|
+
result = (await this.withTimeout(() => runnable.invoke({ messages: this.buildAgentMessages(history, input) }, { configurable: { thread_id: threadId } }), this.resolveBindingTimeout(retriedBinding), "agent invoke", "invoke"));
|
|
482
534
|
}
|
|
483
535
|
const interruptContent = Array.isArray(result.__interrupt__) && result.__interrupt__.length > 0 ? JSON.stringify(result.__interrupt__) : undefined;
|
|
484
536
|
const extractedOutput = extractVisibleOutput(result);
|
|
@@ -501,7 +553,8 @@ export class AgentRuntimeAdapter {
|
|
|
501
553
|
}
|
|
502
554
|
async *stream(binding, input, threadId, history = []) {
|
|
503
555
|
try {
|
|
504
|
-
const
|
|
556
|
+
const invokeTimeoutMs = this.resolveBindingTimeout(binding);
|
|
557
|
+
const streamIdleTimeoutMs = this.resolveStreamIdleTimeout(binding);
|
|
505
558
|
if (binding.langchainAgentParams) {
|
|
506
559
|
const langchainParams = binding.langchainAgentParams;
|
|
507
560
|
const resolvedModel = (await this.resolveModel(binding.langchainAgentParams.model));
|
|
@@ -516,8 +569,8 @@ export class AgentRuntimeAdapter {
|
|
|
516
569
|
// agent loop and only adds an extra model round-trip before the runnable path.
|
|
517
570
|
if (canUseDirectModelStream && typeof model.stream === "function") {
|
|
518
571
|
let emitted = false;
|
|
519
|
-
const stream = await this.withTimeout(() => model.stream(this.buildRawModelMessages(langchainParams.systemPrompt, history, input)),
|
|
520
|
-
for await (const chunk of this.iterateWithTimeout(stream,
|
|
572
|
+
const stream = await this.withTimeout(() => model.stream(this.buildRawModelMessages(langchainParams.systemPrompt, history, input)), invokeTimeoutMs, "model stream start", "stream");
|
|
573
|
+
for await (const chunk of this.iterateWithTimeout(stream, streamIdleTimeoutMs, "model stream")) {
|
|
521
574
|
const delta = readStreamDelta(chunk);
|
|
522
575
|
if (delta) {
|
|
523
576
|
emitted = true;
|
|
@@ -537,11 +590,11 @@ export class AgentRuntimeAdapter {
|
|
|
537
590
|
const runnable = await this.create(binding);
|
|
538
591
|
const request = { messages: this.buildAgentMessages(history, input) };
|
|
539
592
|
if (typeof runnable.streamEvents === "function") {
|
|
540
|
-
const events = await this.withTimeout(() => runnable.streamEvents(request, { configurable: { thread_id: threadId }, version: "v2" }),
|
|
593
|
+
const events = await this.withTimeout(() => runnable.streamEvents(request, { configurable: { thread_id: threadId }, version: "v2" }), invokeTimeoutMs, "agent streamEvents start", "stream");
|
|
541
594
|
let terminalOutput = "";
|
|
542
595
|
const seenTerminalOutputs = new Set();
|
|
543
596
|
let lastStep = "";
|
|
544
|
-
for await (const event of this.iterateWithTimeout(events,
|
|
597
|
+
for await (const event of this.iterateWithTimeout(events, streamIdleTimeoutMs, "agent streamEvents")) {
|
|
545
598
|
const interruptPayload = extractInterruptPayload(event);
|
|
546
599
|
if (interruptPayload) {
|
|
547
600
|
yield { kind: "interrupt", content: interruptPayload };
|
|
@@ -578,9 +631,9 @@ export class AgentRuntimeAdapter {
|
|
|
578
631
|
}
|
|
579
632
|
}
|
|
580
633
|
if (binding.langchainAgentParams && typeof runnable.stream === "function") {
|
|
581
|
-
const stream = await this.withTimeout(() => runnable.stream(request, { configurable: { thread_id: threadId } }),
|
|
634
|
+
const stream = await this.withTimeout(() => runnable.stream(request, { configurable: { thread_id: threadId } }), invokeTimeoutMs, "agent stream start", "stream");
|
|
582
635
|
let emitted = false;
|
|
583
|
-
for await (const chunk of this.iterateWithTimeout(stream,
|
|
636
|
+
for await (const chunk of this.iterateWithTimeout(stream, streamIdleTimeoutMs, "agent stream")) {
|
|
584
637
|
const delta = readStreamDelta(chunk);
|
|
585
638
|
if (delta) {
|
|
586
639
|
emitted = true;
|
package/dist/runtime/harness.js
CHANGED
|
@@ -628,7 +628,7 @@ export class AgentHarness {
|
|
|
628
628
|
}) };
|
|
629
629
|
return;
|
|
630
630
|
}
|
|
631
|
-
if (error instanceof RuntimeOperationTimeoutError) {
|
|
631
|
+
if (error instanceof RuntimeOperationTimeoutError && error.stage === "invoke") {
|
|
632
632
|
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 4, "failed", {
|
|
633
633
|
previousState: null,
|
|
634
634
|
error: error.message,
|