@botbotgo/agent-harness 0.0.29 → 0.0.31
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.30";
|
package/dist/package-version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export const AGENT_HARNESS_VERSION = "0.0.30";
|
|
@@ -6,9 +6,19 @@ type RunnableLike = {
|
|
|
6
6
|
streamEvents?: (input: unknown, config?: Record<string, unknown>) => Promise<AsyncIterable<unknown>>;
|
|
7
7
|
};
|
|
8
8
|
declare const AGENT_INTERRUPT_SENTINEL_PREFIX = "__agent_harness_interrupt__:";
|
|
9
|
+
declare class RuntimeOperationTimeoutError extends Error {
|
|
10
|
+
readonly operation: string;
|
|
11
|
+
readonly timeoutMs: number;
|
|
12
|
+
readonly stage: "stream" | "invoke";
|
|
13
|
+
constructor(operation: string, timeoutMs: number, stage?: "stream" | "invoke");
|
|
14
|
+
}
|
|
9
15
|
export declare class AgentRuntimeAdapter {
|
|
10
16
|
private readonly options;
|
|
11
17
|
constructor(options?: RuntimeAdapterOptions);
|
|
18
|
+
private resolveBindingTimeout;
|
|
19
|
+
private resolveStreamIdleTimeout;
|
|
20
|
+
private withTimeout;
|
|
21
|
+
private iterateWithTimeout;
|
|
12
22
|
private materializeModelStream;
|
|
13
23
|
private createModelFallbackRunnable;
|
|
14
24
|
private applyStrictToolJsonInstruction;
|
|
@@ -32,4 +42,4 @@ export declare class AgentRuntimeAdapter {
|
|
|
32
42
|
invoke(binding: CompiledAgentBinding, input: MessageContent, threadId: string, runId: string, resumePayload?: unknown, history?: TranscriptMessage[]): Promise<RunResult>;
|
|
33
43
|
stream(binding: CompiledAgentBinding, input: MessageContent, threadId: string, history?: TranscriptMessage[]): AsyncGenerator<RuntimeStreamChunk | string>;
|
|
34
44
|
}
|
|
35
|
-
export { AgentRuntimeAdapter as RuntimeAdapter, AGENT_INTERRUPT_SENTINEL_PREFIX, AGENT_INTERRUPT_SENTINEL_PREFIX as INTERRUPT_SENTINEL_PREFIX, };
|
|
45
|
+
export { AgentRuntimeAdapter as RuntimeAdapter, AGENT_INTERRUPT_SENTINEL_PREFIX, AGENT_INTERRUPT_SENTINEL_PREFIX as INTERRUPT_SENTINEL_PREFIX, RuntimeOperationTimeoutError, };
|
|
@@ -12,9 +12,24 @@ import { resolveDeclaredMiddleware } from "./declared-middleware.js";
|
|
|
12
12
|
import { extractMessageText, normalizeMessageContent } from "../utils/message-content.js";
|
|
13
13
|
const AGENT_INTERRUPT_SENTINEL_PREFIX = "__agent_harness_interrupt__:";
|
|
14
14
|
const MODEL_SAFE_TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
15
|
+
class RuntimeOperationTimeoutError extends Error {
|
|
16
|
+
operation;
|
|
17
|
+
timeoutMs;
|
|
18
|
+
stage;
|
|
19
|
+
constructor(operation, timeoutMs, stage = operation.includes("stream") ? "stream" : "invoke") {
|
|
20
|
+
super(`${operation} timed out after ${timeoutMs}ms`);
|
|
21
|
+
this.operation = operation;
|
|
22
|
+
this.timeoutMs = timeoutMs;
|
|
23
|
+
this.stage = stage;
|
|
24
|
+
this.name = "RuntimeOperationTimeoutError";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
15
27
|
function asObject(value) {
|
|
16
28
|
return typeof value === "object" && value ? value : undefined;
|
|
17
29
|
}
|
|
30
|
+
function resolveTimeoutMs(value) {
|
|
31
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : undefined;
|
|
32
|
+
}
|
|
18
33
|
function isPlaceholderApiKey(value) {
|
|
19
34
|
return typeof value === "string" && value.trim().toLowerCase() === "dummy";
|
|
20
35
|
}
|
|
@@ -113,6 +128,55 @@ export class AgentRuntimeAdapter {
|
|
|
113
128
|
constructor(options = {}) {
|
|
114
129
|
this.options = options;
|
|
115
130
|
}
|
|
131
|
+
resolveBindingTimeout(binding) {
|
|
132
|
+
return resolveTimeoutMs(binding.langchainAgentParams?.model.init.timeout ?? binding.deepAgentParams?.model.init.timeout);
|
|
133
|
+
}
|
|
134
|
+
resolveStreamIdleTimeout(binding) {
|
|
135
|
+
const configuredIdleTimeout = resolveTimeoutMs(binding.langchainAgentParams?.model.init.streamIdleTimeout ?? binding.deepAgentParams?.model.init.streamIdleTimeout);
|
|
136
|
+
if (configuredIdleTimeout) {
|
|
137
|
+
return configuredIdleTimeout;
|
|
138
|
+
}
|
|
139
|
+
const invokeTimeout = this.resolveBindingTimeout(binding);
|
|
140
|
+
if (invokeTimeout) {
|
|
141
|
+
return Math.min(invokeTimeout, 15_000);
|
|
142
|
+
}
|
|
143
|
+
return 15_000;
|
|
144
|
+
}
|
|
145
|
+
async withTimeout(producer, timeoutMs, operation, stage = operation.includes("stream") ? "stream" : "invoke") {
|
|
146
|
+
if (!timeoutMs) {
|
|
147
|
+
return Promise.resolve(producer());
|
|
148
|
+
}
|
|
149
|
+
return new Promise((resolve, reject) => {
|
|
150
|
+
const timer = setTimeout(() => reject(new RuntimeOperationTimeoutError(operation, timeoutMs, stage)), timeoutMs);
|
|
151
|
+
Promise.resolve(producer()).then((value) => {
|
|
152
|
+
clearTimeout(timer);
|
|
153
|
+
resolve(value);
|
|
154
|
+
}, (error) => {
|
|
155
|
+
clearTimeout(timer);
|
|
156
|
+
reject(error);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
async *iterateWithTimeout(iterable, timeoutMs, operation) {
|
|
161
|
+
const iterator = iterable[Symbol.asyncIterator]();
|
|
162
|
+
try {
|
|
163
|
+
for (;;) {
|
|
164
|
+
const next = await this.withTimeout(() => iterator.next(), timeoutMs, operation, "stream");
|
|
165
|
+
if (next.done) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
yield next.value;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
finally {
|
|
172
|
+
if (typeof iterator.return === "function") {
|
|
173
|
+
const returnResult = iterator.return();
|
|
174
|
+
if (returnResult && typeof returnResult.then === "function") {
|
|
175
|
+
void returnResult.catch(() => undefined);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
116
180
|
async materializeModelStream(streamFactory, input, config) {
|
|
117
181
|
const stream = await streamFactory(input, config);
|
|
118
182
|
let content = "";
|
|
@@ -193,7 +257,7 @@ export class AgentRuntimeAdapter {
|
|
|
193
257
|
if (!model?.invoke) {
|
|
194
258
|
return "";
|
|
195
259
|
}
|
|
196
|
-
const synthesized = await model.invoke([
|
|
260
|
+
const synthesized = await this.withTimeout(() => model.invoke([
|
|
197
261
|
{
|
|
198
262
|
role: "system",
|
|
199
263
|
content: "The previous agent run completed tool work but did not produce a final user-facing answer. Write the final answer now using the tool results provided. Do not expose internal state, tools, or reasoning.",
|
|
@@ -202,7 +266,7 @@ export class AgentRuntimeAdapter {
|
|
|
202
266
|
role: "user",
|
|
203
267
|
content: `Original user request:\n${extractMessageText(input)}\n\nTool results:\n${toolContext}`,
|
|
204
268
|
},
|
|
205
|
-
]);
|
|
269
|
+
]), this.resolveBindingTimeout(binding), "deepagent synthesis invoke", "invoke");
|
|
206
270
|
return sanitizeVisibleText(extractVisibleOutput(synthesized));
|
|
207
271
|
}
|
|
208
272
|
async resolveModel(model) {
|
|
@@ -421,7 +485,7 @@ export class AgentRuntimeAdapter {
|
|
|
421
485
|
let result;
|
|
422
486
|
try {
|
|
423
487
|
const runnable = await this.create(binding);
|
|
424
|
-
result = (await runnable.invoke(request, { configurable: { thread_id: threadId } }));
|
|
488
|
+
result = (await this.withTimeout(() => runnable.invoke(request, { configurable: { thread_id: threadId } }), this.resolveBindingTimeout(binding), "agent invoke", "invoke"));
|
|
425
489
|
}
|
|
426
490
|
catch (error) {
|
|
427
491
|
if (resumePayload !== undefined || !isToolCallParseFailure(error)) {
|
|
@@ -429,7 +493,7 @@ export class AgentRuntimeAdapter {
|
|
|
429
493
|
}
|
|
430
494
|
const retriedBinding = this.applyStrictToolJsonInstruction(binding);
|
|
431
495
|
const runnable = await this.create(retriedBinding);
|
|
432
|
-
result = (await runnable.invoke({ messages: this.buildAgentMessages(history, input) }, { configurable: { thread_id: threadId } }));
|
|
496
|
+
result = (await this.withTimeout(() => runnable.invoke({ messages: this.buildAgentMessages(history, input) }, { configurable: { thread_id: threadId } }), this.resolveBindingTimeout(retriedBinding), "agent invoke", "invoke"));
|
|
433
497
|
}
|
|
434
498
|
const interruptContent = Array.isArray(result.__interrupt__) && result.__interrupt__.length > 0 ? JSON.stringify(result.__interrupt__) : undefined;
|
|
435
499
|
const extractedOutput = extractVisibleOutput(result);
|
|
@@ -452,9 +516,12 @@ export class AgentRuntimeAdapter {
|
|
|
452
516
|
}
|
|
453
517
|
async *stream(binding, input, threadId, history = []) {
|
|
454
518
|
try {
|
|
519
|
+
const invokeTimeoutMs = this.resolveBindingTimeout(binding);
|
|
520
|
+
const streamIdleTimeoutMs = this.resolveStreamIdleTimeout(binding);
|
|
455
521
|
if (binding.langchainAgentParams) {
|
|
522
|
+
const langchainParams = binding.langchainAgentParams;
|
|
456
523
|
const resolvedModel = (await this.resolveModel(binding.langchainAgentParams.model));
|
|
457
|
-
const tools = this.resolveTools(
|
|
524
|
+
const tools = this.resolveTools(langchainParams.tools, binding);
|
|
458
525
|
const canUseDirectModelStream = tools.length === 0 || typeof resolvedModel.bindTools !== "function";
|
|
459
526
|
const model = canUseDirectModelStream
|
|
460
527
|
? resolvedModel
|
|
@@ -465,8 +532,8 @@ export class AgentRuntimeAdapter {
|
|
|
465
532
|
// agent loop and only adds an extra model round-trip before the runnable path.
|
|
466
533
|
if (canUseDirectModelStream && typeof model.stream === "function") {
|
|
467
534
|
let emitted = false;
|
|
468
|
-
const stream = await model.stream(this.buildRawModelMessages(
|
|
469
|
-
for await (const chunk of stream) {
|
|
535
|
+
const stream = await this.withTimeout(() => model.stream(this.buildRawModelMessages(langchainParams.systemPrompt, history, input)), invokeTimeoutMs, "model stream start", "stream");
|
|
536
|
+
for await (const chunk of this.iterateWithTimeout(stream, streamIdleTimeoutMs, "model stream")) {
|
|
470
537
|
const delta = readStreamDelta(chunk);
|
|
471
538
|
if (delta) {
|
|
472
539
|
emitted = true;
|
|
@@ -486,11 +553,11 @@ export class AgentRuntimeAdapter {
|
|
|
486
553
|
const runnable = await this.create(binding);
|
|
487
554
|
const request = { messages: this.buildAgentMessages(history, input) };
|
|
488
555
|
if (typeof runnable.streamEvents === "function") {
|
|
489
|
-
const events = await runnable.streamEvents(request, { configurable: { thread_id: threadId }, version: "v2" });
|
|
556
|
+
const events = await this.withTimeout(() => runnable.streamEvents(request, { configurable: { thread_id: threadId }, version: "v2" }), invokeTimeoutMs, "agent streamEvents start", "stream");
|
|
490
557
|
let terminalOutput = "";
|
|
491
558
|
const seenTerminalOutputs = new Set();
|
|
492
559
|
let lastStep = "";
|
|
493
|
-
for await (const event of events) {
|
|
560
|
+
for await (const event of this.iterateWithTimeout(events, streamIdleTimeoutMs, "agent streamEvents")) {
|
|
494
561
|
const interruptPayload = extractInterruptPayload(event);
|
|
495
562
|
if (interruptPayload) {
|
|
496
563
|
yield { kind: "interrupt", content: interruptPayload };
|
|
@@ -527,9 +594,9 @@ export class AgentRuntimeAdapter {
|
|
|
527
594
|
}
|
|
528
595
|
}
|
|
529
596
|
if (binding.langchainAgentParams && typeof runnable.stream === "function") {
|
|
530
|
-
const stream = await runnable.stream(request, { configurable: { thread_id: threadId } });
|
|
597
|
+
const stream = await this.withTimeout(() => runnable.stream(request, { configurable: { thread_id: threadId } }), invokeTimeoutMs, "agent stream start", "stream");
|
|
531
598
|
let emitted = false;
|
|
532
|
-
for await (const chunk of stream) {
|
|
599
|
+
for await (const chunk of this.iterateWithTimeout(stream, streamIdleTimeoutMs, "agent stream")) {
|
|
533
600
|
const delta = readStreamDelta(chunk);
|
|
534
601
|
if (delta) {
|
|
535
602
|
emitted = true;
|
|
@@ -566,4 +633,4 @@ export class AgentRuntimeAdapter {
|
|
|
566
633
|
}
|
|
567
634
|
}
|
|
568
635
|
}
|
|
569
|
-
export { AgentRuntimeAdapter as RuntimeAdapter, AGENT_INTERRUPT_SENTINEL_PREFIX, AGENT_INTERRUPT_SENTINEL_PREFIX as INTERRUPT_SENTINEL_PREFIX, };
|
|
636
|
+
export { AgentRuntimeAdapter as RuntimeAdapter, AGENT_INTERRUPT_SENTINEL_PREFIX, AGENT_INTERRUPT_SENTINEL_PREFIX as INTERRUPT_SENTINEL_PREFIX, RuntimeOperationTimeoutError, };
|
package/dist/runtime/harness.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AUTO_AGENT_ID } from "../contracts/types.js";
|
|
2
2
|
import { FilePersistence } from "../persistence/file-store.js";
|
|
3
3
|
import { createPersistentId } from "../utils/id.js";
|
|
4
|
-
import { AGENT_INTERRUPT_SENTINEL_PREFIX, AgentRuntimeAdapter } from "./agent-runtime-adapter.js";
|
|
4
|
+
import { AGENT_INTERRUPT_SENTINEL_PREFIX, AgentRuntimeAdapter, RuntimeOperationTimeoutError } from "./agent-runtime-adapter.js";
|
|
5
5
|
import { createResourceBackendResolver, createResourceToolResolver } from "../resource/resource.js";
|
|
6
6
|
import { EventBus } from "./event-bus.js";
|
|
7
7
|
import { PolicyEngine } from "./policy-engine.js";
|
|
@@ -628,6 +628,20 @@ export class AgentHarness {
|
|
|
628
628
|
}) };
|
|
629
629
|
return;
|
|
630
630
|
}
|
|
631
|
+
if (error instanceof RuntimeOperationTimeoutError && error.stage === "invoke") {
|
|
632
|
+
yield { type: "event", event: await this.setRunStateAndEmit(threadId, runId, 4, "failed", {
|
|
633
|
+
previousState: null,
|
|
634
|
+
error: error.message,
|
|
635
|
+
}) };
|
|
636
|
+
yield {
|
|
637
|
+
type: "content",
|
|
638
|
+
threadId,
|
|
639
|
+
runId,
|
|
640
|
+
agentId: selectedAgentId,
|
|
641
|
+
content: renderRuntimeFailure(error),
|
|
642
|
+
};
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
631
645
|
try {
|
|
632
646
|
const actual = await this.invokeWithHistory(binding, options.input, threadId, runId);
|
|
633
647
|
await this.appendAssistantMessage(threadId, runId, actual.output);
|