@botbotgo/agent-harness 0.0.66 → 0.0.67
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/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/runtime/agent-runtime-adapter.d.ts +3 -0
- package/dist/runtime/agent-runtime-adapter.js +561 -15
- package/dist/runtime/harness.d.ts +1 -0
- package/dist/runtime/harness.js +44 -8
- package/dist/runtime/support/harness-support.js +10 -7
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.66";
|
package/dist/package-version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export const AGENT_HARNESS_VERSION = "0.0.66";
|
|
@@ -42,6 +42,9 @@ export declare class AgentRuntimeAdapter {
|
|
|
42
42
|
private compileInterruptOn;
|
|
43
43
|
private resolveInterruptOn;
|
|
44
44
|
private resolveFilesystemBackend;
|
|
45
|
+
private resolveBuiltinMiddlewareBackend;
|
|
46
|
+
private invokeBuiltinTaskTool;
|
|
47
|
+
private resolveBuiltinMiddlewareTools;
|
|
45
48
|
private resolveLangChainAutomaticMiddleware;
|
|
46
49
|
private resolveMiddleware;
|
|
47
50
|
private resolveCheckpointer;
|
|
@@ -3,7 +3,8 @@ import { existsSync, statSync } from "node:fs";
|
|
|
3
3
|
import { cp, mkdir, rm } from "node:fs/promises";
|
|
4
4
|
import { Command, MemorySaver } from "@langchain/langgraph";
|
|
5
5
|
import { tool as createLangChainTool } from "@langchain/core/tools";
|
|
6
|
-
import {
|
|
6
|
+
import { HumanMessage, ToolMessage } from "@langchain/core/messages";
|
|
7
|
+
import { DEFAULT_SUBAGENT_PROMPT, createDeepAgent, createMemoryMiddleware, createPatchToolCallsMiddleware, createSkillsMiddleware, createSummarizationMiddleware, createSubAgentMiddleware, FilesystemBackend, StateBackend, isSandboxBackend, } from "deepagents";
|
|
7
8
|
import { ChatAnthropic } from "@langchain/anthropic";
|
|
8
9
|
import { tools as anthropicProviderTools } from "@langchain/anthropic";
|
|
9
10
|
import { ChatGoogle } from "@langchain/google";
|
|
@@ -12,7 +13,7 @@ import { ChatOpenAI } from "@langchain/openai";
|
|
|
12
13
|
import { tools as openAIProviderTools } from "@langchain/openai";
|
|
13
14
|
import { createAgent, humanInTheLoopMiddleware, initChatModel } from "langchain";
|
|
14
15
|
import { z } from "zod";
|
|
15
|
-
import { extractEmptyAssistantMessageFailure, extractContentBlocks, extractOutputContent, extractReasoningText, extractToolFallbackContext, extractVisibleOutput, isLikelyToolArgsObject, isToolCallParseFailure, STRICT_TOOL_JSON_INSTRUCTION, sanitizeVisibleText, tryParseJson, wrapResolvedModel, } from "./parsing/output-parsing.js";
|
|
16
|
+
import { extractEmptyAssistantMessageFailure, extractContentBlocks, extractOutputContent, extractReasoningText, extractToolFallbackContext, extractVisibleOutput, salvageToolArgs, isLikelyToolArgsObject, isToolCallParseFailure, STRICT_TOOL_JSON_INSTRUCTION, sanitizeVisibleText, tryParseJson, wrapResolvedModel, } from "./parsing/output-parsing.js";
|
|
16
17
|
import { computeIncrementalOutput, extractAgentStep, extractInterruptPayload, extractReasoningStreamOutput, extractStateStreamOutput, extractVisibleStreamOutput, extractTerminalStreamOutput, extractToolResult, normalizeTerminalOutputKey, readStreamDelta, } from "./parsing/stream-event-parsing.js";
|
|
17
18
|
import { wrapToolForExecution } from "./tool-hitl.js";
|
|
18
19
|
import { resolveDeclaredMiddleware } from "./declared-middleware.js";
|
|
@@ -24,6 +25,17 @@ function countConfiguredTools(binding) {
|
|
|
24
25
|
}
|
|
25
26
|
const AGENT_INTERRUPT_SENTINEL_PREFIX = "__agent_harness_interrupt__:";
|
|
26
27
|
const MODEL_SAFE_TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
28
|
+
const UPSTREAM_BUILTIN_MIDDLEWARE_TOOL_NAMES = Object.freeze([
|
|
29
|
+
"write_todos",
|
|
30
|
+
"ls",
|
|
31
|
+
"read_file",
|
|
32
|
+
"write_file",
|
|
33
|
+
"edit_file",
|
|
34
|
+
"glob",
|
|
35
|
+
"grep",
|
|
36
|
+
"execute",
|
|
37
|
+
"task",
|
|
38
|
+
]);
|
|
27
39
|
class RuntimeOperationTimeoutError extends Error {
|
|
28
40
|
operation;
|
|
29
41
|
timeoutMs;
|
|
@@ -152,7 +164,8 @@ function normalizeOpenAICompatibleInit(init) {
|
|
|
152
164
|
return normalized;
|
|
153
165
|
}
|
|
154
166
|
function sanitizeToolNameForModel(name) {
|
|
155
|
-
const
|
|
167
|
+
const withoutNamespace = name.includes(".") ? name.split(".").at(-1) ?? name : name;
|
|
168
|
+
const sanitized = withoutNamespace
|
|
156
169
|
.replaceAll(".", "__")
|
|
157
170
|
.replace(/[^a-zA-Z0-9_-]+/g, "_")
|
|
158
171
|
.replace(/^_+|_+$/g, "");
|
|
@@ -252,6 +265,193 @@ function normalizeResolvedToolSchema(resolvedTool) {
|
|
|
252
265
|
}
|
|
253
266
|
return z.object({}).passthrough();
|
|
254
267
|
}
|
|
268
|
+
function isRecord(value) {
|
|
269
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
270
|
+
}
|
|
271
|
+
function isObject(value) {
|
|
272
|
+
return isRecord(value);
|
|
273
|
+
}
|
|
274
|
+
function stringifyToolOutput(output) {
|
|
275
|
+
if (typeof output === "string") {
|
|
276
|
+
return output;
|
|
277
|
+
}
|
|
278
|
+
try {
|
|
279
|
+
return JSON.stringify(output);
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
return `${String(output)}`;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
function normalizeToolArgsForSchema(args, schema) {
|
|
286
|
+
const schemaDef = isObject(schema) ? schema._def : undefined;
|
|
287
|
+
const shape = schemaDef
|
|
288
|
+
? isRecord(schemaDef.shape)
|
|
289
|
+
? schemaDef.shape
|
|
290
|
+
: typeof schemaDef.shape === "function"
|
|
291
|
+
? schemaDef.shape()
|
|
292
|
+
: undefined
|
|
293
|
+
: undefined;
|
|
294
|
+
if (!shape || !isRecord(shape)) {
|
|
295
|
+
return args;
|
|
296
|
+
}
|
|
297
|
+
const keys = Object.keys(shape);
|
|
298
|
+
if (keys.length !== 1) {
|
|
299
|
+
return args;
|
|
300
|
+
}
|
|
301
|
+
const [expectedKey] = keys;
|
|
302
|
+
if (expectedKey in args) {
|
|
303
|
+
return args;
|
|
304
|
+
}
|
|
305
|
+
const aliasesByExpected = {
|
|
306
|
+
city: ["location", "locality", "place"],
|
|
307
|
+
location: ["city", "city_name"],
|
|
308
|
+
};
|
|
309
|
+
const aliases = aliasesByExpected[expectedKey] ?? [];
|
|
310
|
+
const aliasKey = aliases.find((candidate) => candidate in args);
|
|
311
|
+
if (!aliasKey || !(aliasKey in args)) {
|
|
312
|
+
return args;
|
|
313
|
+
}
|
|
314
|
+
return {
|
|
315
|
+
...args,
|
|
316
|
+
[expectedKey]: args[aliasKey],
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
function extractToolCallsFromResult(result) {
|
|
320
|
+
const messages = isRecord(result) && Array.isArray(result.messages) ? result.messages : [];
|
|
321
|
+
const lastMessage = messages.at(-1);
|
|
322
|
+
if (!isObject(lastMessage)) {
|
|
323
|
+
return [];
|
|
324
|
+
}
|
|
325
|
+
const messageKwargs = isObject(lastMessage.kwargs) ? lastMessage.kwargs : undefined;
|
|
326
|
+
const rawToolCalls = Array.isArray(lastMessage.tool_calls)
|
|
327
|
+
? (lastMessage.tool_calls ?? [])
|
|
328
|
+
: Array.isArray(messageKwargs?.tool_calls)
|
|
329
|
+
? messageKwargs.tool_calls
|
|
330
|
+
: [];
|
|
331
|
+
return rawToolCalls
|
|
332
|
+
.map((toolCall) => {
|
|
333
|
+
if (!isObject(toolCall)) {
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
const functionPayload = isObject(toolCall.function) ? toolCall.function : undefined;
|
|
337
|
+
const name = typeof toolCall.name === "string"
|
|
338
|
+
? toolCall.name
|
|
339
|
+
: typeof functionPayload?.name === "string"
|
|
340
|
+
? functionPayload.name
|
|
341
|
+
: null;
|
|
342
|
+
if (!name) {
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
const rawArgs = salvageToolArgs(toolCall.args ?? functionPayload?.arguments) ?? {};
|
|
346
|
+
if (!isObject(rawArgs)) {
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
const id = typeof toolCall.id === "string" ? toolCall.id : undefined;
|
|
350
|
+
return { id, name, args: rawArgs };
|
|
351
|
+
})
|
|
352
|
+
.filter((item) => item !== null);
|
|
353
|
+
}
|
|
354
|
+
function truncateLines(lines, maxChars = 12_000) {
|
|
355
|
+
const joined = lines.join("\n");
|
|
356
|
+
if (joined.length <= maxChars) {
|
|
357
|
+
return joined;
|
|
358
|
+
}
|
|
359
|
+
return `${joined.slice(0, maxChars - 18)}\n...[truncated]`;
|
|
360
|
+
}
|
|
361
|
+
function summarizeBuiltinWriteTodosArgs(args) {
|
|
362
|
+
const todos = Array.isArray(args.todos) ? args.todos : [];
|
|
363
|
+
const items = todos.flatMap((todo) => {
|
|
364
|
+
if (!isObject(todo)) {
|
|
365
|
+
return [];
|
|
366
|
+
}
|
|
367
|
+
const content = typeof todo.content === "string" && todo.content.trim().length > 0
|
|
368
|
+
? todo.content.trim()
|
|
369
|
+
: typeof todo.description === "string" && todo.description.trim().length > 0
|
|
370
|
+
? todo.description.trim()
|
|
371
|
+
: "";
|
|
372
|
+
const status = typeof todo.status === "string" && todo.status.trim().length > 0 ? todo.status.trim() : "pending";
|
|
373
|
+
return content ? [{ content, status }] : [];
|
|
374
|
+
});
|
|
375
|
+
return {
|
|
376
|
+
total: items.length,
|
|
377
|
+
pending: items.filter((item) => item.status !== "completed").length,
|
|
378
|
+
completed: items.filter((item) => item.status === "completed").length,
|
|
379
|
+
items,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
function createModelFacingToolNameCandidates(toolName) {
|
|
383
|
+
const trimmed = toolName.trim();
|
|
384
|
+
if (!trimmed) {
|
|
385
|
+
return [];
|
|
386
|
+
}
|
|
387
|
+
const lastSegment = trimmed.split(".").at(-1) ?? "";
|
|
388
|
+
const candidates = new Set([
|
|
389
|
+
trimmed,
|
|
390
|
+
sanitizeToolNameForModel(trimmed),
|
|
391
|
+
lastSegment,
|
|
392
|
+
lastSegment.trim() ? sanitizeToolNameForModel(lastSegment) : "",
|
|
393
|
+
trimmed.replace(/[^a-zA-Z0-9_]+/g, ""),
|
|
394
|
+
]);
|
|
395
|
+
candidates.delete("");
|
|
396
|
+
return [...candidates];
|
|
397
|
+
}
|
|
398
|
+
function createModelFacingToolNameLookupCandidates(toolName) {
|
|
399
|
+
const trimmed = toolName.trim();
|
|
400
|
+
if (!trimmed) {
|
|
401
|
+
return [];
|
|
402
|
+
}
|
|
403
|
+
const candidates = new Set();
|
|
404
|
+
const add = (value) => {
|
|
405
|
+
for (const candidate of createModelFacingToolNameCandidates(value)) {
|
|
406
|
+
candidates.add(candidate);
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
const addSuffixStripped = (value) => {
|
|
410
|
+
const stripped = value.includes(".") ? value.split(".").at(-1) ?? value : value;
|
|
411
|
+
if (stripped !== value) {
|
|
412
|
+
add(stripped);
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
add(trimmed);
|
|
416
|
+
if (trimmed.startsWith("functions.")) {
|
|
417
|
+
const stripped = trimmed.substring("functions.".length);
|
|
418
|
+
add(stripped);
|
|
419
|
+
addSuffixStripped(stripped);
|
|
420
|
+
}
|
|
421
|
+
else if (trimmed.startsWith("function.")) {
|
|
422
|
+
const stripped = trimmed.substring("function.".length);
|
|
423
|
+
add(stripped);
|
|
424
|
+
addSuffixStripped(stripped);
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
addSuffixStripped(trimmed);
|
|
428
|
+
}
|
|
429
|
+
return [...candidates];
|
|
430
|
+
}
|
|
431
|
+
function resolveModelFacingToolName(toolName, mapping, tools) {
|
|
432
|
+
const candidateNames = createModelFacingToolNameLookupCandidates(toolName);
|
|
433
|
+
const candidateSet = new Set(candidateNames);
|
|
434
|
+
for (const candidate of candidateNames) {
|
|
435
|
+
const mappedToolName = mapping.modelFacingToOriginal.get(candidate);
|
|
436
|
+
if (mappedToolName) {
|
|
437
|
+
return mappedToolName;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
for (const candidate of candidateNames) {
|
|
441
|
+
const directMatch = tools.find((tool) => tool.name === candidate);
|
|
442
|
+
if (directMatch) {
|
|
443
|
+
return directMatch.name;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
const candidateMatches = tools.filter((tool) => {
|
|
447
|
+
const modelFacingCandidates = createModelFacingToolNameCandidates(tool.name);
|
|
448
|
+
return modelFacingCandidates.some((candidate) => candidateSet.has(candidate));
|
|
449
|
+
});
|
|
450
|
+
if (candidateMatches.length === 1) {
|
|
451
|
+
return candidateMatches[0].name;
|
|
452
|
+
}
|
|
453
|
+
return toolName;
|
|
454
|
+
}
|
|
255
455
|
function asStructuredExecutableTool(resolvedTool, modelFacingName, description) {
|
|
256
456
|
if (!hasCallableToolHandler(resolvedTool)) {
|
|
257
457
|
return resolvedTool;
|
|
@@ -589,6 +789,228 @@ export class AgentRuntimeAdapter {
|
|
|
589
789
|
: 10,
|
|
590
790
|
});
|
|
591
791
|
}
|
|
792
|
+
resolveBuiltinMiddlewareBackend(binding, options = {}) {
|
|
793
|
+
const runtimeState = {
|
|
794
|
+
...(options.state ?? {}),
|
|
795
|
+
...(isRecord(options.files) ? { files: options.files } : {}),
|
|
796
|
+
};
|
|
797
|
+
const runtimeLike = {
|
|
798
|
+
state: runtimeState,
|
|
799
|
+
store: this.options.storeResolver?.(binding),
|
|
800
|
+
};
|
|
801
|
+
const configuredBackend = isDeepAgentBinding(binding)
|
|
802
|
+
? this.options.backendResolver?.(binding)
|
|
803
|
+
: this.resolveFilesystemBackend(binding);
|
|
804
|
+
if (typeof configuredBackend === "function") {
|
|
805
|
+
return configuredBackend(runtimeLike);
|
|
806
|
+
}
|
|
807
|
+
if (configuredBackend) {
|
|
808
|
+
return configuredBackend;
|
|
809
|
+
}
|
|
810
|
+
return new StateBackend(runtimeLike);
|
|
811
|
+
}
|
|
812
|
+
async invokeBuiltinTaskTool(binding, input, options = {}) {
|
|
813
|
+
if (!isDeepAgentBinding(binding)) {
|
|
814
|
+
throw new Error("The built-in task tool is only available for deepagent bindings.");
|
|
815
|
+
}
|
|
816
|
+
const params = getBindingDeepAgentParams(binding);
|
|
817
|
+
if (!params) {
|
|
818
|
+
throw new Error(`Agent ${binding.agent.id} has no deepagent params`);
|
|
819
|
+
}
|
|
820
|
+
const typedInput = isObject(input) ? input : {};
|
|
821
|
+
const description = typeof typedInput.description === "string" ? typedInput.description : "";
|
|
822
|
+
const subagentType = typeof typedInput.subagent_type === "string" ? typedInput.subagent_type : "";
|
|
823
|
+
const builtinBackend = this.resolveBuiltinMiddlewareBackend(binding, options);
|
|
824
|
+
const resolvedSubagents = await this.resolveSubagents(params.subagents, binding);
|
|
825
|
+
const selectedSubagent = resolvedSubagents.find((subagent) => subagent.name === subagentType);
|
|
826
|
+
if (!selectedSubagent) {
|
|
827
|
+
const allowed = [
|
|
828
|
+
...resolvedSubagents.map((subagent) => subagent.name),
|
|
829
|
+
...(params.generalPurposeAgent ? ["general-purpose"] : []),
|
|
830
|
+
];
|
|
831
|
+
throw new Error(`Error: invoked agent of type ${subagentType}, the only allowed types are ${allowed.map((name) => `\`${name}\``).join(", ")}`);
|
|
832
|
+
}
|
|
833
|
+
const summarizationModel = selectedSubagent.model
|
|
834
|
+
? await this.resolveModel(selectedSubagent.model)
|
|
835
|
+
: await this.resolveModel(params.model);
|
|
836
|
+
const middleware = [
|
|
837
|
+
...(selectedSubagent.skills?.length
|
|
838
|
+
? [createSkillsMiddleware({ backend: builtinBackend, sources: selectedSubagent.skills })]
|
|
839
|
+
: []),
|
|
840
|
+
...(selectedSubagent.memory?.length
|
|
841
|
+
? [createMemoryMiddleware({ backend: builtinBackend, sources: selectedSubagent.memory })]
|
|
842
|
+
: []),
|
|
843
|
+
...(selectedSubagent.middleware ??
|
|
844
|
+
[
|
|
845
|
+
createPatchToolCallsMiddleware(),
|
|
846
|
+
createSummarizationMiddleware({
|
|
847
|
+
model: summarizationModel,
|
|
848
|
+
backend: builtinBackend,
|
|
849
|
+
}),
|
|
850
|
+
]),
|
|
851
|
+
...(selectedSubagent.interruptOn
|
|
852
|
+
? [humanInTheLoopMiddleware({
|
|
853
|
+
interruptOn: this.compileInterruptOn(selectedSubagent.tools ?? [], selectedSubagent.interruptOn),
|
|
854
|
+
})]
|
|
855
|
+
: []),
|
|
856
|
+
];
|
|
857
|
+
const runnable = createAgent({
|
|
858
|
+
model: (selectedSubagent.model ?? (await this.resolveModel(params.model))),
|
|
859
|
+
tools: (selectedSubagent.tools ?? this.resolveTools(params.tools, binding)),
|
|
860
|
+
systemPrompt: selectedSubagent.systemPrompt ?? DEFAULT_SUBAGENT_PROMPT,
|
|
861
|
+
middleware: middleware,
|
|
862
|
+
responseFormat: selectedSubagent.responseFormat,
|
|
863
|
+
contextSchema: selectedSubagent.contextSchema,
|
|
864
|
+
name: selectedSubagent.name,
|
|
865
|
+
description: selectedSubagent.description,
|
|
866
|
+
});
|
|
867
|
+
const result = await runnable.invoke({ messages: [new HumanMessage({ content: description })] }, { configurable: { thread_id: `${binding.agent.id}:builtin-task` }, ...(options.context ? { context: options.context } : {}) });
|
|
868
|
+
const visibleOutput = extractVisibleOutput(result);
|
|
869
|
+
const fallbackOutput = extractToolFallbackContext(result);
|
|
870
|
+
return visibleOutput || fallbackOutput || JSON.stringify(result);
|
|
871
|
+
}
|
|
872
|
+
async resolveBuiltinMiddlewareTools(binding, options = {}) {
|
|
873
|
+
const tools = new Map();
|
|
874
|
+
const backend = this.resolveBuiltinMiddlewareBackend(binding, options);
|
|
875
|
+
tools.set("write_todos", {
|
|
876
|
+
name: "write_todos",
|
|
877
|
+
schema: z.object({
|
|
878
|
+
todos: z.array(z.object({}).passthrough()).optional(),
|
|
879
|
+
}).passthrough(),
|
|
880
|
+
invoke: async (input) => {
|
|
881
|
+
const args = isObject(input) ? input : {};
|
|
882
|
+
const summary = summarizeBuiltinWriteTodosArgs(args);
|
|
883
|
+
return {
|
|
884
|
+
ok: true,
|
|
885
|
+
tool: "write_todos",
|
|
886
|
+
message: `Tracked ${summary.total} todo item(s).`,
|
|
887
|
+
summary,
|
|
888
|
+
};
|
|
889
|
+
},
|
|
890
|
+
});
|
|
891
|
+
tools.set("ls", {
|
|
892
|
+
name: "ls",
|
|
893
|
+
schema: z.object({ path: z.string().optional().default("/") }).passthrough(),
|
|
894
|
+
invoke: async (input) => {
|
|
895
|
+
const targetPath = isObject(input) && typeof input.path === "string" ? input.path : "/";
|
|
896
|
+
const infos = (await Promise.resolve(backend.lsInfo?.(targetPath))) ?? [];
|
|
897
|
+
if (infos.length === 0) {
|
|
898
|
+
return `No files found in ${targetPath}`;
|
|
899
|
+
}
|
|
900
|
+
return truncateLines(infos.map((info) => info.is_dir ? `${info.path} (directory)` : `${info.path}${info.size ? ` (${info.size} bytes)` : ""}`));
|
|
901
|
+
},
|
|
902
|
+
});
|
|
903
|
+
tools.set("read_file", {
|
|
904
|
+
name: "read_file",
|
|
905
|
+
schema: z.object({
|
|
906
|
+
file_path: z.string(),
|
|
907
|
+
offset: z.number().optional(),
|
|
908
|
+
limit: z.number().optional(),
|
|
909
|
+
}).passthrough(),
|
|
910
|
+
invoke: async (input) => {
|
|
911
|
+
const typed = isObject(input) ? input : {};
|
|
912
|
+
const filePath = typeof typed.file_path === "string" ? typed.file_path : "";
|
|
913
|
+
const offset = typeof typed.offset === "number" ? typed.offset : 0;
|
|
914
|
+
const limit = typeof typed.limit === "number" ? typed.limit : 500;
|
|
915
|
+
return Promise.resolve(backend.read?.(filePath, offset, limit)) ?? "";
|
|
916
|
+
},
|
|
917
|
+
});
|
|
918
|
+
tools.set("write_file", {
|
|
919
|
+
name: "write_file",
|
|
920
|
+
schema: z.object({ file_path: z.string(), content: z.string().optional() }).passthrough(),
|
|
921
|
+
invoke: async (input) => {
|
|
922
|
+
const typed = isObject(input) ? input : {};
|
|
923
|
+
const result = await Promise.resolve(backend.write?.(typeof typed.file_path === "string" ? typed.file_path : "", typeof typed.content === "string" ? typed.content : ""));
|
|
924
|
+
return result?.error ?? `Successfully wrote to '${result?.path ?? (typed.file_path ?? "")}'`;
|
|
925
|
+
},
|
|
926
|
+
});
|
|
927
|
+
tools.set("edit_file", {
|
|
928
|
+
name: "edit_file",
|
|
929
|
+
schema: z.object({
|
|
930
|
+
file_path: z.string(),
|
|
931
|
+
old_string: z.string(),
|
|
932
|
+
new_string: z.string(),
|
|
933
|
+
replace_all: z.boolean().optional(),
|
|
934
|
+
}).passthrough(),
|
|
935
|
+
invoke: async (input) => {
|
|
936
|
+
const typed = isObject(input) ? input : {};
|
|
937
|
+
const result = await Promise.resolve(backend.edit?.(typeof typed.file_path === "string" ? typed.file_path : "", typeof typed.old_string === "string" ? typed.old_string : "", typeof typed.new_string === "string" ? typed.new_string : "", typed.replace_all === true));
|
|
938
|
+
return result?.error ?? `Successfully replaced ${result?.occurrences ?? 0} occurrence(s) in '${result?.path ?? (typed.file_path ?? "")}'`;
|
|
939
|
+
},
|
|
940
|
+
});
|
|
941
|
+
tools.set("glob", {
|
|
942
|
+
name: "glob",
|
|
943
|
+
schema: z.object({ pattern: z.string(), path: z.string().optional().default("/") }).passthrough(),
|
|
944
|
+
invoke: async (input) => {
|
|
945
|
+
const typed = isObject(input) ? input : {};
|
|
946
|
+
const pattern = typeof typed.pattern === "string" ? typed.pattern : "";
|
|
947
|
+
const targetPath = typeof typed.path === "string" ? typed.path : "/";
|
|
948
|
+
const infos = (await Promise.resolve(backend.globInfo?.(pattern, targetPath))) ?? [];
|
|
949
|
+
if (infos.length === 0) {
|
|
950
|
+
return `No files found matching pattern '${pattern}'`;
|
|
951
|
+
}
|
|
952
|
+
return truncateLines(infos.map((info) => info.path));
|
|
953
|
+
},
|
|
954
|
+
});
|
|
955
|
+
tools.set("grep", {
|
|
956
|
+
name: "grep",
|
|
957
|
+
schema: z.object({
|
|
958
|
+
pattern: z.string(),
|
|
959
|
+
path: z.string().optional().default("/"),
|
|
960
|
+
glob: z.string().nullable().optional(),
|
|
961
|
+
}).passthrough(),
|
|
962
|
+
invoke: async (input) => {
|
|
963
|
+
const typed = isObject(input) ? input : {};
|
|
964
|
+
const result = await Promise.resolve(backend.grepRaw?.(typeof typed.pattern === "string" ? typed.pattern : "", typeof typed.path === "string" ? typed.path : "/", typeof typed.glob === "string" ? typed.glob : null));
|
|
965
|
+
if (typeof result === "string") {
|
|
966
|
+
return result;
|
|
967
|
+
}
|
|
968
|
+
if (!result || result.length === 0) {
|
|
969
|
+
return `No matches found for pattern '${typeof typed.pattern === "string" ? typed.pattern : ""}'`;
|
|
970
|
+
}
|
|
971
|
+
const lines = [];
|
|
972
|
+
let currentFile = "";
|
|
973
|
+
for (const match of result) {
|
|
974
|
+
if (match.path !== currentFile) {
|
|
975
|
+
currentFile = match.path;
|
|
976
|
+
lines.push(`\n${currentFile}:`);
|
|
977
|
+
}
|
|
978
|
+
lines.push(` ${match.line}: ${match.text}`);
|
|
979
|
+
}
|
|
980
|
+
return truncateLines(lines);
|
|
981
|
+
},
|
|
982
|
+
});
|
|
983
|
+
tools.set("execute", {
|
|
984
|
+
name: "execute",
|
|
985
|
+
schema: z.object({ command: z.string() }).passthrough(),
|
|
986
|
+
invoke: async (input) => {
|
|
987
|
+
if (!isSandboxBackend(backend) || typeof backend.execute !== "function") {
|
|
988
|
+
return "Error: Execution not available. This agent's backend does not support command execution (SandboxBackendProtocol).";
|
|
989
|
+
}
|
|
990
|
+
const typed = isObject(input) ? input : {};
|
|
991
|
+
const result = await Promise.resolve(backend.execute(typeof typed.command === "string" ? typed.command : ""));
|
|
992
|
+
const parts = [result.output];
|
|
993
|
+
if (result.exitCode !== null) {
|
|
994
|
+
parts.push(`\n[Command ${result.exitCode === 0 ? "succeeded" : "failed"} with exit code ${result.exitCode}]`);
|
|
995
|
+
}
|
|
996
|
+
if (result.truncated) {
|
|
997
|
+
parts.push("\n[Output was truncated due to size limits]");
|
|
998
|
+
}
|
|
999
|
+
return parts.join("");
|
|
1000
|
+
},
|
|
1001
|
+
});
|
|
1002
|
+
if (isDeepAgentBinding(binding)) {
|
|
1003
|
+
tools.set("task", {
|
|
1004
|
+
name: "task",
|
|
1005
|
+
schema: z.object({
|
|
1006
|
+
description: z.string(),
|
|
1007
|
+
subagent_type: z.string(),
|
|
1008
|
+
}).passthrough(),
|
|
1009
|
+
invoke: async (input) => this.invokeBuiltinTaskTool(binding, input, options),
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
return tools;
|
|
1013
|
+
}
|
|
592
1014
|
async resolveLangChainAutomaticMiddleware(binding) {
|
|
593
1015
|
const params = getBindingLangChainParams(binding);
|
|
594
1016
|
if (!params) {
|
|
@@ -781,17 +1203,117 @@ export class AgentRuntimeAdapter {
|
|
|
781
1203
|
? this.buildInvocationRequest(binding, history, input, options)
|
|
782
1204
|
: new Command({ resume: resumePayload });
|
|
783
1205
|
let result;
|
|
784
|
-
|
|
785
|
-
const runnable = await this.create(
|
|
786
|
-
|
|
1206
|
+
const callRuntime = async (activeBinding, activeRequest) => {
|
|
1207
|
+
const runnable = await this.create(activeBinding);
|
|
1208
|
+
return (await this.withTimeout(() => runnable.invoke(activeRequest, { configurable: { thread_id: threadId }, ...(options.context ? { context: options.context } : {}) }), this.resolveBindingTimeout(activeBinding), "agent invoke", "invoke"));
|
|
1209
|
+
};
|
|
1210
|
+
const callRuntimeWithToolParseRecovery = async (activeRequest) => {
|
|
1211
|
+
try {
|
|
1212
|
+
return await callRuntime(binding, activeRequest);
|
|
1213
|
+
}
|
|
1214
|
+
catch (error) {
|
|
1215
|
+
if (resumePayload !== undefined || !isToolCallParseFailure(error)) {
|
|
1216
|
+
throw error;
|
|
1217
|
+
}
|
|
1218
|
+
return callRuntime(this.applyStrictToolJsonInstruction(binding), activeRequest);
|
|
1219
|
+
}
|
|
1220
|
+
};
|
|
1221
|
+
const executedToolResults = [];
|
|
1222
|
+
if (resumePayload !== undefined) {
|
|
1223
|
+
result = await callRuntimeWithToolParseRecovery(request);
|
|
787
1224
|
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
1225
|
+
else {
|
|
1226
|
+
const primaryTools = getBindingPrimaryTools(binding);
|
|
1227
|
+
const defersToUpstreamHitlExecution = primaryTools.some((tool) => tool.hitl?.enabled === true);
|
|
1228
|
+
if (defersToUpstreamHitlExecution) {
|
|
1229
|
+
result = await callRuntimeWithToolParseRecovery(request);
|
|
1230
|
+
}
|
|
1231
|
+
else {
|
|
1232
|
+
const resolvedTools = this.resolveTools(primaryTools, binding);
|
|
1233
|
+
const toolNameMapping = this.buildToolNameMapping(primaryTools);
|
|
1234
|
+
const executableTools = new Map();
|
|
1235
|
+
const builtinExecutableTools = await this.resolveBuiltinMiddlewareTools(binding, options);
|
|
1236
|
+
for (let index = 0; index < primaryTools.length; index += 1) {
|
|
1237
|
+
const compiledTool = primaryTools[index];
|
|
1238
|
+
const resolvedTool = resolvedTools[index];
|
|
1239
|
+
if (!compiledTool || !resolvedTool || !hasCallableToolHandler(resolvedTool)) {
|
|
1240
|
+
continue;
|
|
1241
|
+
}
|
|
1242
|
+
const handler = async (toolInput) => {
|
|
1243
|
+
const callable = typeof resolvedTool.invoke === "function"
|
|
1244
|
+
? resolvedTool.invoke
|
|
1245
|
+
: typeof resolvedTool.call === "function"
|
|
1246
|
+
? resolvedTool.call
|
|
1247
|
+
: resolvedTool.func;
|
|
1248
|
+
if (!callable) {
|
|
1249
|
+
throw new Error(`Tool ${compiledTool.name} has no callable handler.`);
|
|
1250
|
+
}
|
|
1251
|
+
return Promise.resolve(callable.call(resolvedTool, toolInput, options.context ? { context: options.context } : undefined));
|
|
1252
|
+
};
|
|
1253
|
+
const modelFacingName = toolNameMapping.originalToModelFacing.get(compiledTool.name) ?? compiledTool.name;
|
|
1254
|
+
const normalizedSchema = normalizeResolvedToolSchema(resolvedTool);
|
|
1255
|
+
executableTools.set(modelFacingName, {
|
|
1256
|
+
name: compiledTool.name,
|
|
1257
|
+
schema: normalizedSchema,
|
|
1258
|
+
invoke: handler,
|
|
1259
|
+
});
|
|
1260
|
+
executableTools.set(compiledTool.name, {
|
|
1261
|
+
name: compiledTool.name,
|
|
1262
|
+
schema: normalizedSchema,
|
|
1263
|
+
invoke: handler,
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1266
|
+
let activeRequest = request;
|
|
1267
|
+
let currentMessages = Array.isArray(activeRequest.messages) ? [...activeRequest.messages] : [];
|
|
1268
|
+
const maxToolIterations = 8;
|
|
1269
|
+
for (let iteration = 0; iteration < maxToolIterations; iteration += 1) {
|
|
1270
|
+
result = await callRuntimeWithToolParseRecovery(activeRequest);
|
|
1271
|
+
const toolCalls = extractToolCallsFromResult(result);
|
|
1272
|
+
if (toolCalls.length === 0) {
|
|
1273
|
+
break;
|
|
1274
|
+
}
|
|
1275
|
+
if (iteration + 1 === maxToolIterations) {
|
|
1276
|
+
throw new Error(`Tool-calling loop exceeded the maximum of ${maxToolIterations} iterations`);
|
|
1277
|
+
}
|
|
1278
|
+
const resultMessages = result.messages;
|
|
1279
|
+
const nextMessages = Array.isArray(resultMessages)
|
|
1280
|
+
? [...resultMessages]
|
|
1281
|
+
: [...currentMessages];
|
|
1282
|
+
for (let toolIndex = 0; toolIndex < toolCalls.length; toolIndex += 1) {
|
|
1283
|
+
const toolCall = toolCalls[toolIndex];
|
|
1284
|
+
const resolvedToolName = resolveModelFacingToolName(toolCall.name, toolNameMapping, primaryTools);
|
|
1285
|
+
const executable = executableTools.get(toolCall.name) ?? executableTools.get(resolvedToolName);
|
|
1286
|
+
const builtinExecutable = builtinExecutableTools.get(toolCall.name) ??
|
|
1287
|
+
builtinExecutableTools.get(resolvedToolName) ??
|
|
1288
|
+
createModelFacingToolNameLookupCandidates(toolCall.name)
|
|
1289
|
+
.map((candidate) => builtinExecutableTools.get(candidate))
|
|
1290
|
+
.find((candidate) => candidate !== undefined);
|
|
1291
|
+
const activeExecutable = executable ?? builtinExecutable;
|
|
1292
|
+
if (!activeExecutable) {
|
|
1293
|
+
throw new Error(`Tool ${toolCall.name} is not configured for this agent.`);
|
|
1294
|
+
}
|
|
1295
|
+
const normalizedArgs = normalizeToolArgsForSchema(toolCall.args, activeExecutable.schema);
|
|
1296
|
+
const toolResult = await activeExecutable.invoke(normalizedArgs);
|
|
1297
|
+
executedToolResults.push({
|
|
1298
|
+
toolName: activeExecutable.name,
|
|
1299
|
+
output: toolResult,
|
|
1300
|
+
});
|
|
1301
|
+
nextMessages.push(new ToolMessage({
|
|
1302
|
+
name: activeExecutable.name,
|
|
1303
|
+
tool_call_id: toolCall.id ?? `tool-${iteration + 1}-${toolIndex + 1}`,
|
|
1304
|
+
content: stringifyToolOutput(toolResult),
|
|
1305
|
+
}));
|
|
1306
|
+
}
|
|
1307
|
+
currentMessages = nextMessages;
|
|
1308
|
+
activeRequest = {
|
|
1309
|
+
...activeRequest,
|
|
1310
|
+
messages: currentMessages,
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
791
1313
|
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
1314
|
+
}
|
|
1315
|
+
if (!result) {
|
|
1316
|
+
throw new Error("Agent invocation returned no result");
|
|
795
1317
|
}
|
|
796
1318
|
const interruptContent = Array.isArray(result.__interrupt__) && result.__interrupt__.length > 0 ? JSON.stringify(result.__interrupt__) : undefined;
|
|
797
1319
|
const extractedOutput = extractVisibleOutput(result);
|
|
@@ -819,6 +1341,7 @@ export class AgentRuntimeAdapter {
|
|
|
819
1341
|
...(contentBlocks.length > 0 ? { contentBlocks } : {}),
|
|
820
1342
|
...(structuredResponse !== undefined ? { structuredResponse } : {}),
|
|
821
1343
|
metadata: {
|
|
1344
|
+
...(executedToolResults.length > 0 ? { executedToolResults } : {}),
|
|
822
1345
|
...(structuredResponse !== undefined ? { structuredResponse } : {}),
|
|
823
1346
|
...(outputContent !== undefined ? { outputContent } : {}),
|
|
824
1347
|
...(contentBlocks.length > 0 ? { contentBlocks } : {}),
|
|
@@ -833,6 +1356,12 @@ export class AgentRuntimeAdapter {
|
|
|
833
1356
|
const invokeTimeoutMs = this.resolveBindingTimeout(binding);
|
|
834
1357
|
const streamIdleTimeoutMs = this.resolveStreamIdleTimeout(binding);
|
|
835
1358
|
const streamDeadlineAt = invokeTimeoutMs ? Date.now() + invokeTimeoutMs : undefined;
|
|
1359
|
+
const primaryTools = getBindingPrimaryTools(binding);
|
|
1360
|
+
const toolNameMapping = this.buildToolNameMapping(primaryTools);
|
|
1361
|
+
const primaryModel = getBindingPrimaryModel(binding);
|
|
1362
|
+
const forceInvokeFallback = isLangChainBinding(binding) &&
|
|
1363
|
+
primaryTools.length > 0 &&
|
|
1364
|
+
primaryModel?.provider === "openai-compatible";
|
|
836
1365
|
if (isLangChainBinding(binding)) {
|
|
837
1366
|
const langchainParams = getBindingLangChainParams(binding);
|
|
838
1367
|
const resolvedModel = (await this.resolveModel(langchainParams.model));
|
|
@@ -867,7 +1396,7 @@ export class AgentRuntimeAdapter {
|
|
|
867
1396
|
}
|
|
868
1397
|
const runnable = await this.create(binding);
|
|
869
1398
|
const request = this.buildInvocationRequest(binding, history, input, options);
|
|
870
|
-
if (typeof runnable.streamEvents === "function") {
|
|
1399
|
+
if (!forceInvokeFallback && typeof runnable.streamEvents === "function") {
|
|
871
1400
|
const events = await this.withTimeout(() => runnable.streamEvents(request, { configurable: { thread_id: threadId }, version: "v2", ...(options.context ? { context: options.context } : {}) }), computeRemainingTimeoutMs(streamDeadlineAt, invokeTimeoutMs), "agent streamEvents start", "stream");
|
|
872
1401
|
const allowVisibleStreamDeltas = isLangChainBinding(binding);
|
|
873
1402
|
let emittedOutput = "";
|
|
@@ -912,7 +1441,13 @@ export class AgentRuntimeAdapter {
|
|
|
912
1441
|
const toolResult = extractToolResult(event);
|
|
913
1442
|
if (toolResult) {
|
|
914
1443
|
emittedToolError = emittedToolError || toolResult.isError === true;
|
|
915
|
-
|
|
1444
|
+
const resolvedToolName = resolveModelFacingToolName(toolResult.toolName, toolNameMapping, primaryTools);
|
|
1445
|
+
yield {
|
|
1446
|
+
kind: "tool-result",
|
|
1447
|
+
toolName: resolvedToolName,
|
|
1448
|
+
output: toolResult.output,
|
|
1449
|
+
isError: toolResult.isError,
|
|
1450
|
+
};
|
|
916
1451
|
}
|
|
917
1452
|
const output = extractTerminalStreamOutput(event);
|
|
918
1453
|
if (output) {
|
|
@@ -934,7 +1469,7 @@ export class AgentRuntimeAdapter {
|
|
|
934
1469
|
return;
|
|
935
1470
|
}
|
|
936
1471
|
}
|
|
937
|
-
if (isLangChainBinding(binding) && typeof runnable.stream === "function") {
|
|
1472
|
+
if (!forceInvokeFallback && isLangChainBinding(binding) && typeof runnable.stream === "function") {
|
|
938
1473
|
const stream = await this.withTimeout(() => runnable.stream(request, { configurable: { thread_id: threadId } }), computeRemainingTimeoutMs(streamDeadlineAt, invokeTimeoutMs), "agent stream start", "stream");
|
|
939
1474
|
let emitted = false;
|
|
940
1475
|
for await (const chunk of this.iterateWithTimeout(stream, streamIdleTimeoutMs, "agent stream", streamDeadlineAt, invokeTimeoutMs)) {
|
|
@@ -954,6 +1489,17 @@ export class AgentRuntimeAdapter {
|
|
|
954
1489
|
}
|
|
955
1490
|
}
|
|
956
1491
|
const result = await this.invoke(binding, input, threadId, threadId);
|
|
1492
|
+
const executedToolResults = Array.isArray(result.metadata?.executedToolResults)
|
|
1493
|
+
? result.metadata.executedToolResults
|
|
1494
|
+
: [];
|
|
1495
|
+
for (const toolResult of executedToolResults) {
|
|
1496
|
+
yield {
|
|
1497
|
+
kind: "tool-result",
|
|
1498
|
+
toolName: toolResult.toolName,
|
|
1499
|
+
output: toolResult.output,
|
|
1500
|
+
isError: toolResult.isError,
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
957
1503
|
if (result.output) {
|
|
958
1504
|
yield { kind: "content", content: sanitizeVisibleText(result.output) };
|
|
959
1505
|
}
|
|
@@ -100,6 +100,7 @@ export declare class AgentHarnessRuntime {
|
|
|
100
100
|
run(options: RunOptions): Promise<RunResult>;
|
|
101
101
|
streamEvents(options: RunStartOptions): AsyncGenerator<HarnessStreamItem>;
|
|
102
102
|
resume(options: ResumeOptions): Promise<RunResult>;
|
|
103
|
+
private buildResumePayload;
|
|
103
104
|
restartConversation(options: RestartConversationOptions): Promise<RunResult & {
|
|
104
105
|
restart: Record<string, string>;
|
|
105
106
|
}>;
|
package/dist/runtime/harness.js
CHANGED
|
@@ -15,7 +15,7 @@ import { FileBackedStore } from "./store.js";
|
|
|
15
15
|
import { CheckpointMaintenanceLoop, discoverCheckpointMaintenanceTargets, readCheckpointMaintenanceConfig, } from "./checkpoint-maintenance.js";
|
|
16
16
|
import { extractMessageText, normalizeMessageContent } from "../utils/message-content.js";
|
|
17
17
|
import { createToolMcpServerFromTools, serveToolsOverStdioFromHarness } from "../mcp.js";
|
|
18
|
-
import { getBindingAdapterKind, getBindingPrimaryTools, getBindingStoreConfig } from "./support/compiled-binding.js";
|
|
18
|
+
import { getBindingAdapterKind, getBindingPrimaryTools, getBindingStoreConfig, isDeepAgentBinding } from "./support/compiled-binding.js";
|
|
19
19
|
import { describeWorkspaceInventory, listAgentSkills as listWorkspaceAgentSkills, } from "./inventory.js";
|
|
20
20
|
export class AgentHarnessRuntime {
|
|
21
21
|
workspace;
|
|
@@ -401,6 +401,9 @@ export class AgentHarnessRuntime {
|
|
|
401
401
|
const event = createHarnessEvent(threadId, runId, sequence, eventType, payload, source);
|
|
402
402
|
await this.persistence.appendEvent(event);
|
|
403
403
|
this.eventBus.publish(event);
|
|
404
|
+
if (this.threadMemorySync.shouldHandle(event)) {
|
|
405
|
+
await this.threadMemorySync.handleEvent(event);
|
|
406
|
+
}
|
|
404
407
|
return event;
|
|
405
408
|
}
|
|
406
409
|
async ensureThreadStarted(selectedAgentId, binding, input, existingThreadId) {
|
|
@@ -1066,6 +1069,7 @@ export class AgentHarnessRuntime {
|
|
|
1066
1069
|
if (!binding) {
|
|
1067
1070
|
throw new Error(`Unknown agent ${thread.agentId}`);
|
|
1068
1071
|
}
|
|
1072
|
+
const resumePayload = this.buildResumePayload(binding, approval, options);
|
|
1069
1073
|
await this.persistence.setRunState(threadId, runId, "resuming", `checkpoints/${threadId}/${runId}/cp-1`);
|
|
1070
1074
|
const releaseRunSlot = await this.acquireRunSlot(threadId, runId, "resuming");
|
|
1071
1075
|
try {
|
|
@@ -1073,9 +1077,7 @@ export class AgentHarnessRuntime {
|
|
|
1073
1077
|
kind: "approval-decision",
|
|
1074
1078
|
savedAt: new Date().toISOString(),
|
|
1075
1079
|
checkpointRef: `checkpoints/${threadId}/${runId}/cp-1`,
|
|
1076
|
-
resumePayload
|
|
1077
|
-
? { decision: "edit", editedInput: options.editedInput }
|
|
1078
|
-
: (options.decision ?? "approve"),
|
|
1080
|
+
resumePayload,
|
|
1079
1081
|
attempts: 0,
|
|
1080
1082
|
});
|
|
1081
1083
|
await this.emit(threadId, runId, 5, "run.resumed", {
|
|
@@ -1095,11 +1097,8 @@ export class AgentHarnessRuntime {
|
|
|
1095
1097
|
const history = await this.persistence.listThreadMessages(threadId);
|
|
1096
1098
|
const priorHistory = history.filter((message) => message.runId !== runId);
|
|
1097
1099
|
const runInput = await this.loadRunInput(threadId, runId);
|
|
1098
|
-
const resumeDecision = options.decision === "edit" && options.editedInput
|
|
1099
|
-
? { decision: "edit", editedInput: options.editedInput }
|
|
1100
|
-
: (options.decision ?? "approve");
|
|
1101
1100
|
try {
|
|
1102
|
-
const actual = await this.runtimeAdapter.invoke(binding, "", threadId, runId,
|
|
1101
|
+
const actual = await this.runtimeAdapter.invoke(binding, "", threadId, runId, resumePayload, priorHistory);
|
|
1103
1102
|
await this.persistence.clearRecoveryIntent(threadId, runId);
|
|
1104
1103
|
const finalized = await this.finalizeContinuedRun(threadId, runId, runInput, actual, {
|
|
1105
1104
|
previousState: "resuming",
|
|
@@ -1120,6 +1119,43 @@ export class AgentHarnessRuntime {
|
|
|
1120
1119
|
releaseRunSlot();
|
|
1121
1120
|
}
|
|
1122
1121
|
}
|
|
1122
|
+
buildResumePayload(binding, approval, options) {
|
|
1123
|
+
if (!isDeepAgentBinding(binding)) {
|
|
1124
|
+
return options.decision === "edit" && options.editedInput
|
|
1125
|
+
? { decision: "edit", editedInput: options.editedInput }
|
|
1126
|
+
: (options.decision ?? "approve");
|
|
1127
|
+
}
|
|
1128
|
+
const decisionType = options.decision ?? "approve";
|
|
1129
|
+
if (decisionType === "edit" && options.editedInput) {
|
|
1130
|
+
return {
|
|
1131
|
+
decisions: [
|
|
1132
|
+
{
|
|
1133
|
+
type: "edit",
|
|
1134
|
+
editedAction: {
|
|
1135
|
+
name: approval.toolName,
|
|
1136
|
+
args: options.editedInput,
|
|
1137
|
+
},
|
|
1138
|
+
},
|
|
1139
|
+
],
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
if (decisionType === "reject") {
|
|
1143
|
+
return {
|
|
1144
|
+
decisions: [
|
|
1145
|
+
{
|
|
1146
|
+
type: "reject",
|
|
1147
|
+
},
|
|
1148
|
+
],
|
|
1149
|
+
};
|
|
1150
|
+
}
|
|
1151
|
+
return {
|
|
1152
|
+
decisions: [
|
|
1153
|
+
{
|
|
1154
|
+
type: "approve",
|
|
1155
|
+
},
|
|
1156
|
+
],
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1123
1159
|
async restartConversation(options) {
|
|
1124
1160
|
const thread = await this.getSession(options.threadId);
|
|
1125
1161
|
if (!thread) {
|
|
@@ -130,23 +130,26 @@ export function createPendingApproval(threadId, runId, checkpointRef, input, int
|
|
|
130
130
|
}
|
|
131
131
|
export function inferRoutingBindings(workspace) {
|
|
132
132
|
const hostBindings = Array.from(workspace.bindings.values()).filter((binding) => binding.harnessRuntime.hostFacing);
|
|
133
|
-
const
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
-
const
|
|
137
|
-
const
|
|
133
|
+
const deepAgentHosts = hostBindings.filter((binding) => binding.agent.executionMode === "deepagent" || Boolean(binding.deepAgentParams));
|
|
134
|
+
const routingHosts = deepAgentHosts.length > 0 ? deepAgentHosts : hostBindings;
|
|
135
|
+
const researchBinding = routingHosts.find((binding) => binding.agent.id === "research-lite" || binding.agent.id === "research");
|
|
136
|
+
const directBinding = routingHosts.find((binding) => binding.agent.id === "direct");
|
|
137
|
+
const delegationHosts = routingHosts.filter((binding) => isDelegationCapableBinding(binding));
|
|
138
|
+
const lightweightHosts = routingHosts.filter((binding) => !isDelegationCapableBinding(binding));
|
|
139
|
+
const defaultOrchestratingHost = routingHosts.find((binding) => binding.agent.id === "orchestra") ??
|
|
138
140
|
delegationHosts.find((binding) => (binding.deepAgentParams?.subagents.length ?? 0) > 0) ??
|
|
139
141
|
delegationHosts[0];
|
|
140
142
|
const delegationPreferredSecondary = delegationHosts.find((binding) => (binding.deepAgentParams?.subagents.length ?? 0) > 0) ??
|
|
141
143
|
delegationHosts[0];
|
|
142
144
|
const genericLightweightHost = lightweightHosts.find((binding) => binding.agent.id !== researchBinding?.agent.id);
|
|
143
|
-
const primaryBinding = defaultOrchestratingHost ?? directBinding ?? genericLightweightHost ?? hostBindings[0];
|
|
145
|
+
const primaryBinding = defaultOrchestratingHost ?? directBinding ?? genericLightweightHost ?? routingHosts[0] ?? hostBindings[0];
|
|
144
146
|
const secondaryBinding = genericLightweightHost && genericLightweightHost.agent.id !== primaryBinding?.agent.id
|
|
145
147
|
? genericLightweightHost
|
|
146
148
|
: directBinding && directBinding.agent.id !== primaryBinding?.agent.id
|
|
147
149
|
? directBinding
|
|
148
150
|
: delegationPreferredSecondary && delegationPreferredSecondary.agent.id !== primaryBinding?.agent.id
|
|
149
151
|
? delegationPreferredSecondary
|
|
150
|
-
:
|
|
152
|
+
: routingHosts.find((binding) => binding.agent.id !== primaryBinding?.agent.id) ??
|
|
153
|
+
(deepAgentHosts.length > 0 ? undefined : hostBindings.find((binding) => binding.agent.id !== primaryBinding?.agent.id));
|
|
151
154
|
return { primaryBinding, secondaryBinding, researchBinding, hostBindings };
|
|
152
155
|
}
|