@botbotgo/agent-harness 0.0.28 → 0.0.30
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.29";
|
package/dist/package-version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export const AGENT_HARNESS_VERSION = "0.0.29";
|
|
@@ -6,9 +6,16 @@ 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 timeoutMs: number;
|
|
11
|
+
constructor(operation: string, timeoutMs: number);
|
|
12
|
+
}
|
|
9
13
|
export declare class AgentRuntimeAdapter {
|
|
10
14
|
private readonly options;
|
|
11
15
|
constructor(options?: RuntimeAdapterOptions);
|
|
16
|
+
private resolveBindingTimeout;
|
|
17
|
+
private withTimeout;
|
|
18
|
+
private iterateWithTimeout;
|
|
12
19
|
private materializeModelStream;
|
|
13
20
|
private createModelFallbackRunnable;
|
|
14
21
|
private applyStrictToolJsonInstruction;
|
|
@@ -32,4 +39,4 @@ export declare class AgentRuntimeAdapter {
|
|
|
32
39
|
invoke(binding: CompiledAgentBinding, input: MessageContent, threadId: string, runId: string, resumePayload?: unknown, history?: TranscriptMessage[]): Promise<RunResult>;
|
|
33
40
|
stream(binding: CompiledAgentBinding, input: MessageContent, threadId: string, history?: TranscriptMessage[]): AsyncGenerator<RuntimeStreamChunk | string>;
|
|
34
41
|
}
|
|
35
|
-
export { AgentRuntimeAdapter as RuntimeAdapter, AGENT_INTERRUPT_SENTINEL_PREFIX, AGENT_INTERRUPT_SENTINEL_PREFIX as INTERRUPT_SENTINEL_PREFIX, };
|
|
42
|
+
export { AgentRuntimeAdapter as RuntimeAdapter, AGENT_INTERRUPT_SENTINEL_PREFIX, AGENT_INTERRUPT_SENTINEL_PREFIX as INTERRUPT_SENTINEL_PREFIX, RuntimeOperationTimeoutError, };
|
|
@@ -12,20 +12,58 @@ 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
|
+
timeoutMs;
|
|
17
|
+
constructor(operation, timeoutMs) {
|
|
18
|
+
super(`${operation} timed out after ${timeoutMs}ms`);
|
|
19
|
+
this.timeoutMs = timeoutMs;
|
|
20
|
+
this.name = "RuntimeOperationTimeoutError";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
15
23
|
function asObject(value) {
|
|
16
24
|
return typeof value === "object" && value ? value : undefined;
|
|
17
25
|
}
|
|
26
|
+
function resolveTimeoutMs(value) {
|
|
27
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : undefined;
|
|
28
|
+
}
|
|
29
|
+
function isPlaceholderApiKey(value) {
|
|
30
|
+
return typeof value === "string" && value.trim().toLowerCase() === "dummy";
|
|
31
|
+
}
|
|
32
|
+
function buildAuthOmittingFetch(baseFetch = fetch) {
|
|
33
|
+
return async (input, init) => {
|
|
34
|
+
const sanitizedHeaders = new Headers(input instanceof Request ? input.headers : undefined);
|
|
35
|
+
const initHeaders = new Headers(init?.headers);
|
|
36
|
+
initHeaders.forEach((value, key) => {
|
|
37
|
+
sanitizedHeaders.set(key, value);
|
|
38
|
+
});
|
|
39
|
+
sanitizedHeaders.delete("authorization");
|
|
40
|
+
if (input instanceof Request) {
|
|
41
|
+
return baseFetch(new Request(input, {
|
|
42
|
+
...init,
|
|
43
|
+
headers: sanitizedHeaders,
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
return baseFetch(input, {
|
|
47
|
+
...init,
|
|
48
|
+
headers: sanitizedHeaders,
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
}
|
|
18
52
|
function normalizeOpenAICompatibleInit(init) {
|
|
19
53
|
const normalized = { ...init };
|
|
20
54
|
const configuration = asObject(init.configuration) ?? {};
|
|
21
55
|
const baseUrl = typeof init.baseUrl === "string" && init.baseUrl.trim() ? init.baseUrl.trim() : undefined;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
56
|
+
const omitAuthHeader = init.omitAuthHeader === true || isPlaceholderApiKey(init.apiKey);
|
|
57
|
+
const nextConfiguration = { ...configuration };
|
|
58
|
+
if (baseUrl && typeof nextConfiguration.baseURL !== "string") {
|
|
59
|
+
nextConfiguration.baseURL = baseUrl;
|
|
60
|
+
}
|
|
61
|
+
if (omitAuthHeader) {
|
|
62
|
+
nextConfiguration.fetch = buildAuthOmittingFetch(typeof configuration.fetch === "function" ? configuration.fetch : fetch);
|
|
27
63
|
}
|
|
64
|
+
normalized.configuration = nextConfiguration;
|
|
28
65
|
delete normalized.baseUrl;
|
|
66
|
+
delete normalized.omitAuthHeader;
|
|
29
67
|
return normalized;
|
|
30
68
|
}
|
|
31
69
|
function sanitizeToolNameForModel(name) {
|
|
@@ -86,6 +124,44 @@ export class AgentRuntimeAdapter {
|
|
|
86
124
|
constructor(options = {}) {
|
|
87
125
|
this.options = options;
|
|
88
126
|
}
|
|
127
|
+
resolveBindingTimeout(binding) {
|
|
128
|
+
return resolveTimeoutMs(binding.langchainAgentParams?.model.init.timeout ?? binding.deepAgentParams?.model.init.timeout);
|
|
129
|
+
}
|
|
130
|
+
async withTimeout(producer, timeoutMs, operation) {
|
|
131
|
+
if (!timeoutMs) {
|
|
132
|
+
return Promise.resolve(producer());
|
|
133
|
+
}
|
|
134
|
+
return new Promise((resolve, reject) => {
|
|
135
|
+
const timer = setTimeout(() => reject(new RuntimeOperationTimeoutError(operation, timeoutMs)), timeoutMs);
|
|
136
|
+
Promise.resolve(producer()).then((value) => {
|
|
137
|
+
clearTimeout(timer);
|
|
138
|
+
resolve(value);
|
|
139
|
+
}, (error) => {
|
|
140
|
+
clearTimeout(timer);
|
|
141
|
+
reject(error);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
async *iterateWithTimeout(iterable, timeoutMs, operation) {
|
|
146
|
+
const iterator = iterable[Symbol.asyncIterator]();
|
|
147
|
+
try {
|
|
148
|
+
for (;;) {
|
|
149
|
+
const next = await this.withTimeout(() => iterator.next(), timeoutMs, operation);
|
|
150
|
+
if (next.done) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
yield next.value;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
if (typeof iterator.return === "function") {
|
|
158
|
+
const returnResult = iterator.return();
|
|
159
|
+
if (returnResult && typeof returnResult.then === "function") {
|
|
160
|
+
void returnResult.catch(() => undefined);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
89
165
|
async materializeModelStream(streamFactory, input, config) {
|
|
90
166
|
const stream = await streamFactory(input, config);
|
|
91
167
|
let content = "";
|
|
@@ -166,7 +242,7 @@ export class AgentRuntimeAdapter {
|
|
|
166
242
|
if (!model?.invoke) {
|
|
167
243
|
return "";
|
|
168
244
|
}
|
|
169
|
-
const synthesized = await model.invoke([
|
|
245
|
+
const synthesized = await this.withTimeout(() => model.invoke([
|
|
170
246
|
{
|
|
171
247
|
role: "system",
|
|
172
248
|
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.",
|
|
@@ -175,7 +251,7 @@ export class AgentRuntimeAdapter {
|
|
|
175
251
|
role: "user",
|
|
176
252
|
content: `Original user request:\n${extractMessageText(input)}\n\nTool results:\n${toolContext}`,
|
|
177
253
|
},
|
|
178
|
-
]);
|
|
254
|
+
]), this.resolveBindingTimeout(binding), "deepagent synthesis invoke");
|
|
179
255
|
return sanitizeVisibleText(extractVisibleOutput(synthesized));
|
|
180
256
|
}
|
|
181
257
|
async resolveModel(model) {
|
|
@@ -394,7 +470,7 @@ export class AgentRuntimeAdapter {
|
|
|
394
470
|
let result;
|
|
395
471
|
try {
|
|
396
472
|
const runnable = await this.create(binding);
|
|
397
|
-
result = (await runnable.invoke(request, { configurable: { thread_id: threadId } }));
|
|
473
|
+
result = (await this.withTimeout(() => runnable.invoke(request, { configurable: { thread_id: threadId } }), this.resolveBindingTimeout(binding), "agent invoke"));
|
|
398
474
|
}
|
|
399
475
|
catch (error) {
|
|
400
476
|
if (resumePayload !== undefined || !isToolCallParseFailure(error)) {
|
|
@@ -402,7 +478,7 @@ export class AgentRuntimeAdapter {
|
|
|
402
478
|
}
|
|
403
479
|
const retriedBinding = this.applyStrictToolJsonInstruction(binding);
|
|
404
480
|
const runnable = await this.create(retriedBinding);
|
|
405
|
-
result = (await runnable.invoke({ messages: this.buildAgentMessages(history, input) }, { configurable: { thread_id: threadId } }));
|
|
481
|
+
result = (await this.withTimeout(() => runnable.invoke({ messages: this.buildAgentMessages(history, input) }, { configurable: { thread_id: threadId } }), this.resolveBindingTimeout(retriedBinding), "agent invoke"));
|
|
406
482
|
}
|
|
407
483
|
const interruptContent = Array.isArray(result.__interrupt__) && result.__interrupt__.length > 0 ? JSON.stringify(result.__interrupt__) : undefined;
|
|
408
484
|
const extractedOutput = extractVisibleOutput(result);
|
|
@@ -425,9 +501,11 @@ export class AgentRuntimeAdapter {
|
|
|
425
501
|
}
|
|
426
502
|
async *stream(binding, input, threadId, history = []) {
|
|
427
503
|
try {
|
|
504
|
+
const timeoutMs = this.resolveBindingTimeout(binding);
|
|
428
505
|
if (binding.langchainAgentParams) {
|
|
506
|
+
const langchainParams = binding.langchainAgentParams;
|
|
429
507
|
const resolvedModel = (await this.resolveModel(binding.langchainAgentParams.model));
|
|
430
|
-
const tools = this.resolveTools(
|
|
508
|
+
const tools = this.resolveTools(langchainParams.tools, binding);
|
|
431
509
|
const canUseDirectModelStream = tools.length === 0 || typeof resolvedModel.bindTools !== "function";
|
|
432
510
|
const model = canUseDirectModelStream
|
|
433
511
|
? resolvedModel
|
|
@@ -438,8 +516,8 @@ export class AgentRuntimeAdapter {
|
|
|
438
516
|
// agent loop and only adds an extra model round-trip before the runnable path.
|
|
439
517
|
if (canUseDirectModelStream && typeof model.stream === "function") {
|
|
440
518
|
let emitted = false;
|
|
441
|
-
const stream = await model.stream(this.buildRawModelMessages(
|
|
442
|
-
for await (const chunk of stream) {
|
|
519
|
+
const stream = await this.withTimeout(() => model.stream(this.buildRawModelMessages(langchainParams.systemPrompt, history, input)), timeoutMs, "model stream start");
|
|
520
|
+
for await (const chunk of this.iterateWithTimeout(stream, timeoutMs, "model stream")) {
|
|
443
521
|
const delta = readStreamDelta(chunk);
|
|
444
522
|
if (delta) {
|
|
445
523
|
emitted = true;
|
|
@@ -459,11 +537,11 @@ export class AgentRuntimeAdapter {
|
|
|
459
537
|
const runnable = await this.create(binding);
|
|
460
538
|
const request = { messages: this.buildAgentMessages(history, input) };
|
|
461
539
|
if (typeof runnable.streamEvents === "function") {
|
|
462
|
-
const events = await runnable.streamEvents(request, { configurable: { thread_id: threadId }, version: "v2" });
|
|
540
|
+
const events = await this.withTimeout(() => runnable.streamEvents(request, { configurable: { thread_id: threadId }, version: "v2" }), timeoutMs, "agent streamEvents start");
|
|
463
541
|
let terminalOutput = "";
|
|
464
542
|
const seenTerminalOutputs = new Set();
|
|
465
543
|
let lastStep = "";
|
|
466
|
-
for await (const event of events) {
|
|
544
|
+
for await (const event of this.iterateWithTimeout(events, timeoutMs, "agent streamEvents")) {
|
|
467
545
|
const interruptPayload = extractInterruptPayload(event);
|
|
468
546
|
if (interruptPayload) {
|
|
469
547
|
yield { kind: "interrupt", content: interruptPayload };
|
|
@@ -500,9 +578,9 @@ export class AgentRuntimeAdapter {
|
|
|
500
578
|
}
|
|
501
579
|
}
|
|
502
580
|
if (binding.langchainAgentParams && typeof runnable.stream === "function") {
|
|
503
|
-
const stream = await runnable.stream(request, { configurable: { thread_id: threadId } });
|
|
581
|
+
const stream = await this.withTimeout(() => runnable.stream(request, { configurable: { thread_id: threadId } }), timeoutMs, "agent stream start");
|
|
504
582
|
let emitted = false;
|
|
505
|
-
for await (const chunk of stream) {
|
|
583
|
+
for await (const chunk of this.iterateWithTimeout(stream, timeoutMs, "agent stream")) {
|
|
506
584
|
const delta = readStreamDelta(chunk);
|
|
507
585
|
if (delta) {
|
|
508
586
|
emitted = true;
|
|
@@ -539,4 +617,4 @@ export class AgentRuntimeAdapter {
|
|
|
539
617
|
}
|
|
540
618
|
}
|
|
541
619
|
}
|
|
542
|
-
export { AgentRuntimeAdapter as RuntimeAdapter, AGENT_INTERRUPT_SENTINEL_PREFIX, AGENT_INTERRUPT_SENTINEL_PREFIX as INTERRUPT_SENTINEL_PREFIX, };
|
|
620
|
+
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) {
|
|
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);
|