@botbotgo/agent-harness 0.0.95 → 0.0.97
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/README.md +1 -114
- package/README.zh.md +1 -70
- package/dist/api.d.ts +5 -5
- package/dist/config/workflows/langgraph-workflows.yaml +363 -111
- package/dist/config/workflows/runtime-profiles.yaml +94 -0
- package/dist/contracts/core.d.ts +9 -0
- package/dist/contracts/core.js +1 -0
- package/dist/contracts/runtime.d.ts +421 -0
- package/dist/contracts/runtime.js +1 -0
- package/dist/contracts/types.d.ts +3 -571
- package/dist/contracts/types.js +3 -1
- package/dist/contracts/workspace.d.ts +229 -0
- package/dist/contracts/workspace.js +1 -0
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/runtime/adapter/compat/deepagent-compat.d.ts +16 -0
- package/dist/runtime/adapter/compat/deepagent-compat.js +45 -0
- package/dist/runtime/adapter/compat/openai-compatible.d.ts +2 -0
- package/dist/runtime/adapter/compat/openai-compatible.js +43 -0
- package/dist/runtime/adapter/index.d.ts +15 -0
- package/dist/runtime/adapter/index.js +15 -0
- package/dist/runtime/adapter/langgraph/presets.js +165 -0
- package/dist/runtime/{langgraph-profiles.d.ts → adapter/langgraph/profiles.d.ts} +1 -1
- package/dist/runtime/adapter/langgraph/profiles.js +206 -0
- package/dist/runtime/adapter/model/invocation-request.d.ts +10 -0
- package/dist/runtime/adapter/model/invocation-request.js +46 -0
- package/dist/runtime/adapter/model/message-assembly.d.ts +6 -0
- package/dist/runtime/adapter/model/message-assembly.js +21 -0
- package/dist/runtime/adapter/model/model-providers.d.ts +2 -0
- package/dist/runtime/adapter/model/model-providers.js +27 -0
- package/dist/runtime/adapter/resilience.d.ts +12 -0
- package/dist/runtime/adapter/resilience.js +60 -0
- package/dist/runtime/{declared-middleware.d.ts → adapter/tool/declared-middleware.d.ts} +1 -1
- package/dist/runtime/adapter/tool/interrupt-policy.d.ts +8 -0
- package/dist/runtime/adapter/tool/interrupt-policy.js +34 -0
- package/dist/runtime/adapter/tool/provider-tool.d.ts +2 -0
- package/dist/runtime/adapter/tool/provider-tool.js +25 -0
- package/dist/runtime/adapter/tool/resolved-tool.d.ts +18 -0
- package/dist/runtime/adapter/tool/resolved-tool.js +62 -0
- package/dist/runtime/adapter/tool/tool-arguments.d.ts +7 -0
- package/dist/runtime/adapter/tool/tool-arguments.js +87 -0
- package/dist/runtime/{tool-hitl.d.ts → adapter/tool/tool-hitl.d.ts} +2 -2
- package/dist/runtime/adapter/tool/tool-name-mapping.d.ts +13 -0
- package/dist/runtime/adapter/tool/tool-name-mapping.js +101 -0
- package/dist/runtime/agent-runtime-adapter.d.ts +5 -20
- package/dist/runtime/agent-runtime-adapter.js +42 -544
- package/dist/runtime/checkpoint-maintenance.d.ts +1 -45
- package/dist/runtime/checkpoint-maintenance.js +1 -259
- package/dist/runtime/file-checkpoint-saver.d.ts +1 -20
- package/dist/runtime/file-checkpoint-saver.js +1 -106
- package/dist/runtime/{event-bus.d.ts → harness/events/event-bus.d.ts} +1 -1
- package/dist/runtime/{event-sink.d.ts → harness/events/event-sink.d.ts} +1 -1
- package/dist/runtime/{event-sink.js → harness/events/event-sink.js} +1 -1
- package/dist/runtime/harness/events/events.d.ts +23 -0
- package/dist/runtime/harness/events/events.js +61 -0
- package/dist/runtime/harness/events/streaming.d.ts +19 -0
- package/dist/runtime/harness/events/streaming.js +96 -0
- package/dist/runtime/harness/index.d.ts +16 -0
- package/dist/runtime/harness/index.js +16 -0
- package/dist/runtime/harness/run/helpers.d.ts +33 -0
- package/dist/runtime/harness/run/helpers.js +74 -0
- package/dist/runtime/harness/run/resources.d.ts +7 -0
- package/dist/runtime/harness/run/resources.js +58 -0
- package/dist/runtime/harness/run/resume.d.ts +6 -0
- package/dist/runtime/harness/run/resume.js +56 -0
- package/dist/runtime/harness/run/routing.d.ts +12 -0
- package/dist/runtime/harness/run/routing.js +47 -0
- package/dist/runtime/harness/run/run-lifecycle.d.ts +37 -0
- package/dist/runtime/harness/run/run-lifecycle.js +109 -0
- package/dist/runtime/harness/run/run-queue.d.ts +17 -0
- package/dist/runtime/harness/run/run-queue.js +43 -0
- package/dist/runtime/{health-monitor.d.ts → harness/system/health-monitor.d.ts} +3 -3
- package/dist/runtime/{health-monitor.js → harness/system/health-monitor.js} +2 -2
- package/dist/runtime/{inventory.d.ts → harness/system/inventory.d.ts} +2 -2
- package/dist/runtime/{inventory.js → harness/system/inventory.js} +4 -4
- package/dist/runtime/{policy-engine.d.ts → harness/system/policy-engine.d.ts} +1 -1
- package/dist/runtime/{policy-engine.js → harness/system/policy-engine.js} +1 -1
- package/dist/runtime/{skill-requirements.d.ts → harness/system/skill-requirements.d.ts} +1 -1
- package/dist/runtime/{skill-requirements.js → harness/system/skill-requirements.js} +1 -1
- package/dist/runtime/{thread-memory-sync.d.ts → harness/system/thread-memory-sync.d.ts} +2 -2
- package/dist/runtime/{thread-memory-sync.js → harness/system/thread-memory-sync.js} +1 -1
- package/dist/runtime/harness.d.ts +2 -7
- package/dist/runtime/harness.js +158 -477
- package/dist/runtime/index.d.ts +7 -7
- package/dist/runtime/index.js +7 -7
- package/dist/runtime/maintenance/checkpoint-maintenance.d.ts +45 -0
- package/dist/runtime/maintenance/checkpoint-maintenance.js +259 -0
- package/dist/runtime/maintenance/file-checkpoint-saver.d.ts +20 -0
- package/dist/runtime/maintenance/file-checkpoint-saver.js +106 -0
- package/dist/runtime/maintenance/index.d.ts +4 -0
- package/dist/runtime/maintenance/index.js +4 -0
- package/dist/runtime/{runtime-record-maintenance.d.ts → maintenance/runtime-record-maintenance.d.ts} +1 -1
- package/dist/runtime/{runtime-record-maintenance.js → maintenance/runtime-record-maintenance.js} +2 -2
- package/dist/runtime/maintenance/sqlite-maintained-checkpoint-saver.d.ts +9 -0
- package/dist/runtime/maintenance/sqlite-maintained-checkpoint-saver.js +39 -0
- package/dist/runtime/parsing/stream-event-parsing.d.ts +6 -0
- package/dist/runtime/parsing/stream-event-parsing.js +231 -0
- package/dist/runtime/sqlite-maintained-checkpoint-saver.d.ts +1 -9
- package/dist/runtime/sqlite-maintained-checkpoint-saver.js +1 -39
- package/dist/runtime/support/harness-support.d.ts +4 -4
- package/dist/runtime/support/harness-support.js +14 -3
- package/dist/runtime/support/runtime-factories.d.ts +1 -1
- package/dist/runtime/support/runtime-factories.js +1 -1
- package/dist/workspace/agent-binding-compiler.js +39 -3
- package/dist/workspace/object-loader.js +5 -1
- package/package.json +4 -4
- package/dist/runtime/langgraph-presets.js +0 -165
- package/dist/runtime/langgraph-profiles.js +0 -206
- /package/dist/runtime/{langgraph-presets.d.ts → adapter/langgraph/presets.d.ts} +0 -0
- /package/dist/runtime/{declared-middleware.js → adapter/tool/declared-middleware.js} +0 -0
- /package/dist/runtime/{tool-hitl.js → adapter/tool/tool-hitl.js} +0 -0
- /package/dist/runtime/{event-bus.js → harness/events/event-bus.js} +0 -0
- /package/dist/runtime/{store.d.ts → harness/system/store.d.ts} +0 -0
- /package/dist/runtime/{store.js → harness/system/store.js} +0 -0
|
@@ -1,32 +1,37 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import { Command, MemorySaver } from "@langchain/langgraph";
|
|
4
|
-
import { tool as createLangChainTool } from "@langchain/core/tools";
|
|
5
4
|
import { HumanMessage, ToolMessage } from "@langchain/core/messages";
|
|
6
5
|
import { DEFAULT_SUBAGENT_PROMPT, createDeepAgent, createMemoryMiddleware, createPatchToolCallsMiddleware, createSkillsMiddleware, createSummarizationMiddleware, createSubAgentMiddleware, FilesystemBackend, StateBackend, isSandboxBackend, } from "deepagents";
|
|
7
|
-
import {
|
|
8
|
-
import { tools as anthropicProviderTools } from "@langchain/anthropic";
|
|
9
|
-
import { ChatGoogle } from "@langchain/google";
|
|
10
|
-
import { ChatOllama } from "@langchain/ollama";
|
|
11
|
-
import { ChatOpenAI } from "@langchain/openai";
|
|
12
|
-
import { tools as openAIProviderTools } from "@langchain/openai";
|
|
13
|
-
import { createAgent, humanInTheLoopMiddleware, initChatModel } from "langchain";
|
|
6
|
+
import { createAgent, humanInTheLoopMiddleware } from "langchain";
|
|
14
7
|
import { z } from "zod";
|
|
15
|
-
import { extractEmptyAssistantMessageFailure, extractContentBlocks, extractOutputContent, extractReasoningText, extractToolFallbackContext, extractVisibleOutput,
|
|
16
|
-
import { computeIncrementalOutput, extractAgentStep, extractInterruptPayload, extractReasoningStreamOutput, extractStateStreamOutput, extractVisibleStreamOutput, extractTerminalStreamOutput, extractToolResult, normalizeTerminalOutputKey, readStreamDelta, } from "./parsing/stream-event-parsing.js";
|
|
17
|
-
import { wrapToolForExecution } from "./tool-hitl.js";
|
|
18
|
-
import { resolveDeclaredMiddleware } from "./declared-middleware.js";
|
|
19
|
-
import { extractMessageText
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
8
|
+
import { extractEmptyAssistantMessageFailure, extractContentBlocks, extractOutputContent, extractReasoningText, extractToolFallbackContext, extractVisibleOutput, isLikelyToolArgsObject, isToolCallParseFailure, STRICT_TOOL_JSON_INSTRUCTION, sanitizeVisibleText, tryParseJson, wrapResolvedModel, } from "./parsing/output-parsing.js";
|
|
9
|
+
import { computeIncrementalOutput, extractAgentStep, extractInterruptPayload, normalizeUpstreamRuntimeEvent, extractReasoningStreamOutput, extractStateStreamOutput, extractVisibleStreamOutput, extractTerminalStreamOutput, extractToolResult, normalizeTerminalOutputKey, readStreamDelta, } from "./parsing/stream-event-parsing.js";
|
|
10
|
+
import { wrapToolForExecution } from "./adapter/tool/tool-hitl.js";
|
|
11
|
+
import { resolveDeclaredMiddleware } from "./adapter/tool/declared-middleware.js";
|
|
12
|
+
import { extractMessageText } from "../utils/message-content.js";
|
|
13
|
+
import { applyDeepAgentDelegationPromptCompatibility, materializeDeepAgentSkillSourcePaths, } from "./adapter/compat/deepagent-compat.js";
|
|
14
|
+
import { buildToolNameMapping, createModelFacingToolNameLookupCandidates, resolveModelFacingToolName, } from "./adapter/tool/tool-name-mapping.js";
|
|
15
|
+
import { computeRemainingTimeoutMs, isRetryableProviderError, resolveBindingTimeout, resolveProviderRetryPolicy, resolveStreamIdleTimeout, } from "./adapter/resilience.js";
|
|
16
|
+
import { createResolvedModel } from "./adapter/model/model-providers.js";
|
|
17
|
+
import { buildInvocationRequest, buildSlashCommandSkillInstruction, } from "./adapter/model/invocation-request.js";
|
|
18
|
+
import { compileInterruptOn } from "./adapter/tool/interrupt-policy.js";
|
|
19
|
+
import { buildRawModelMessages, buildStateSnapshot } from "./adapter/model/message-assembly.js";
|
|
20
|
+
import { asRecord, asStructuredExecutableTool, hasCallableToolHandler, normalizeResolvedToolSchema, wrapResolvedToolWithModelFacingName, } from "./adapter/tool/resolved-tool.js";
|
|
21
|
+
import { instantiateProviderTool } from "./adapter/tool/provider-tool.js";
|
|
22
|
+
import { extractToolCallsFromResult, normalizeToolArgsForSchema, stringifyToolOutput } from "./adapter/tool/tool-arguments.js";
|
|
23
|
+
export { applyDeepAgentDelegationPromptCompatibility, materializeDeepAgentSkillSourcePaths, relativizeDeepAgentSkillSourcePaths, shouldRelaxDeepAgentDelegationPrompt, } from "./adapter/compat/deepagent-compat.js";
|
|
24
|
+
export { buildAuthOmittingFetch, normalizeOpenAICompatibleInit } from "./adapter/compat/openai-compatible.js";
|
|
25
|
+
export { buildToolNameMapping, createModelFacingToolNameCandidates, createModelFacingToolNameLookupCandidates, resolveModelFacingToolName, sanitizeToolNameForModel, } from "./adapter/tool/tool-name-mapping.js";
|
|
26
|
+
export { computeRemainingTimeoutMs, isRetryableProviderError, resolveBindingTimeout, resolveProviderRetryPolicy, resolveStreamIdleTimeout, resolveTimeoutMs, } from "./adapter/resilience.js";
|
|
27
|
+
import { getBindingAdapterKind, getBindingDeepAgentParams, getBindingAdapterConfig, getBindingInterruptCompatibilityRules, getBindingLangChainParams, getBindingLangGraphPreset, getBindingLangGraphWorkflow, getBindingMiddlewareConfigs, getBindingPrimaryModel, getBindingRuntimeModel, getBindingPrimaryTools, getBindingSystemPrompt, isDeepAgentBinding, isLangChainBinding, } from "./support/compiled-binding.js";
|
|
28
|
+
import { resolveLangGraphProfileWorkflow } from "./adapter/langgraph/profiles.js";
|
|
29
|
+
import { resolveLangGraphPresetWorkflow } from "./adapter/langgraph/presets.js";
|
|
24
30
|
const SUPPORTED_LANGGRAPH_WORKFLOW_NODE_KINDS = new Set(["llm", "agent", "tool", "approval", "condition"]);
|
|
25
31
|
function countConfiguredTools(binding) {
|
|
26
32
|
return getBindingPrimaryTools(binding).length;
|
|
27
33
|
}
|
|
28
34
|
const AGENT_INTERRUPT_SENTINEL_PREFIX = "__agent_harness_interrupt__:";
|
|
29
|
-
const MODEL_SAFE_TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
30
35
|
const UPSTREAM_BUILTIN_MIDDLEWARE_TOOL_NAMES = Object.freeze([
|
|
31
36
|
"write_todos",
|
|
32
37
|
"ls",
|
|
@@ -53,111 +58,9 @@ class RuntimeOperationTimeoutError extends Error {
|
|
|
53
58
|
function asObject(value) {
|
|
54
59
|
return typeof value === "object" && value ? value : undefined;
|
|
55
60
|
}
|
|
56
|
-
function resolveTimeoutMs(value) {
|
|
57
|
-
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : undefined;
|
|
58
|
-
}
|
|
59
|
-
function computeRemainingTimeoutMs(deadlineAt, fallbackTimeoutMs) {
|
|
60
|
-
if (!deadlineAt) {
|
|
61
|
-
return fallbackTimeoutMs;
|
|
62
|
-
}
|
|
63
|
-
const remaining = deadlineAt - Date.now();
|
|
64
|
-
if (remaining <= 0) {
|
|
65
|
-
return 0;
|
|
66
|
-
}
|
|
67
|
-
return fallbackTimeoutMs ? Math.min(fallbackTimeoutMs, remaining) : remaining;
|
|
68
|
-
}
|
|
69
61
|
function sleep(ms) {
|
|
70
62
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
71
63
|
}
|
|
72
|
-
function isPlaceholderApiKey(value) {
|
|
73
|
-
return typeof value === "string" && value.trim().toLowerCase() === "dummy";
|
|
74
|
-
}
|
|
75
|
-
export function relativizeDeepAgentSkillSourcePaths(workspaceRoot, skillPaths) {
|
|
76
|
-
if (!workspaceRoot || !skillPaths) {
|
|
77
|
-
return skillPaths;
|
|
78
|
-
}
|
|
79
|
-
return skillPaths.map((skillPath) => {
|
|
80
|
-
if (!path.isAbsolute(skillPath)) {
|
|
81
|
-
return skillPath;
|
|
82
|
-
}
|
|
83
|
-
const relative = path.relative(workspaceRoot, skillPath);
|
|
84
|
-
if (!relative || relative.startsWith("..")) {
|
|
85
|
-
return skillPath;
|
|
86
|
-
}
|
|
87
|
-
return relative.split(path.sep).join("/");
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
export async function materializeDeepAgentSkillSourcePaths(options) {
|
|
91
|
-
const { workspaceRoot, skillPaths } = options;
|
|
92
|
-
if (!skillPaths) {
|
|
93
|
-
return skillPaths;
|
|
94
|
-
}
|
|
95
|
-
return relativizeDeepAgentSkillSourcePaths(workspaceRoot, skillPaths) ?? skillPaths;
|
|
96
|
-
}
|
|
97
|
-
function buildAuthOmittingFetch(baseFetch = fetch) {
|
|
98
|
-
return async (input, init) => {
|
|
99
|
-
const sanitizedHeaders = new Headers(input instanceof Request ? input.headers : undefined);
|
|
100
|
-
const initHeaders = new Headers(init?.headers);
|
|
101
|
-
initHeaders.forEach((value, key) => {
|
|
102
|
-
sanitizedHeaders.set(key, value);
|
|
103
|
-
});
|
|
104
|
-
sanitizedHeaders.delete("authorization");
|
|
105
|
-
if (input instanceof Request) {
|
|
106
|
-
return baseFetch(new Request(input, {
|
|
107
|
-
...init,
|
|
108
|
-
headers: sanitizedHeaders,
|
|
109
|
-
}));
|
|
110
|
-
}
|
|
111
|
-
return baseFetch(input, {
|
|
112
|
-
...init,
|
|
113
|
-
headers: sanitizedHeaders,
|
|
114
|
-
});
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
function normalizeOpenAICompatibleInit(init) {
|
|
118
|
-
const normalized = { ...init };
|
|
119
|
-
const configuration = asObject(init.configuration) ?? {};
|
|
120
|
-
const baseUrl = typeof init.baseUrl === "string" && init.baseUrl.trim() ? init.baseUrl.trim() : undefined;
|
|
121
|
-
const omitAuthHeader = init.omitAuthHeader === true || isPlaceholderApiKey(init.apiKey);
|
|
122
|
-
const nextConfiguration = { ...configuration };
|
|
123
|
-
if (baseUrl && typeof nextConfiguration.baseURL !== "string") {
|
|
124
|
-
nextConfiguration.baseURL = baseUrl;
|
|
125
|
-
}
|
|
126
|
-
if (omitAuthHeader) {
|
|
127
|
-
nextConfiguration.fetch = buildAuthOmittingFetch(typeof configuration.fetch === "function" ? configuration.fetch : fetch);
|
|
128
|
-
}
|
|
129
|
-
normalized.configuration = nextConfiguration;
|
|
130
|
-
delete normalized.baseUrl;
|
|
131
|
-
delete normalized.omitAuthHeader;
|
|
132
|
-
return normalized;
|
|
133
|
-
}
|
|
134
|
-
function sanitizeToolNameForModel(name) {
|
|
135
|
-
const withoutNamespace = name.includes(".") ? name.split(".").at(-1) ?? name : name;
|
|
136
|
-
const sanitized = withoutNamespace
|
|
137
|
-
.replaceAll(".", "__")
|
|
138
|
-
.replace(/[^a-zA-Z0-9_-]+/g, "_")
|
|
139
|
-
.replace(/^_+|_+$/g, "");
|
|
140
|
-
return sanitized || "tool";
|
|
141
|
-
}
|
|
142
|
-
function buildToolNameMapping(tools) {
|
|
143
|
-
const originalToModelFacing = new Map();
|
|
144
|
-
const modelFacingToOriginal = new Map();
|
|
145
|
-
const seen = new Set();
|
|
146
|
-
for (const tool of tools) {
|
|
147
|
-
const originalName = tool.name;
|
|
148
|
-
const baseName = MODEL_SAFE_TOOL_NAME_PATTERN.test(originalName) ? originalName : sanitizeToolNameForModel(originalName);
|
|
149
|
-
let candidate = baseName;
|
|
150
|
-
let suffix = 2;
|
|
151
|
-
while (seen.has(candidate)) {
|
|
152
|
-
candidate = `${baseName}_${suffix}`;
|
|
153
|
-
suffix += 1;
|
|
154
|
-
}
|
|
155
|
-
seen.add(candidate);
|
|
156
|
-
originalToModelFacing.set(originalName, candidate);
|
|
157
|
-
modelFacingToOriginal.set(candidate, originalName);
|
|
158
|
-
}
|
|
159
|
-
return { originalToModelFacing, modelFacingToOriginal };
|
|
160
|
-
}
|
|
161
64
|
function hasConfiguredSubagentSupport(binding) {
|
|
162
65
|
const params = getBindingLangChainParams(binding);
|
|
163
66
|
if (!params) {
|
|
@@ -165,185 +68,15 @@ function hasConfiguredSubagentSupport(binding) {
|
|
|
165
68
|
}
|
|
166
69
|
return (params.subagents?.length ?? 0) > 0 || params.generalPurposeAgent === true || Boolean(params.taskDescription?.trim());
|
|
167
70
|
}
|
|
168
|
-
function isOpenAICompatibleGptOssModel(model) {
|
|
169
|
-
return model?.provider === "openai-compatible" && model.model.trim().toLowerCase().startsWith("gpt-oss");
|
|
170
|
-
}
|
|
171
|
-
export function shouldRelaxDeepAgentDelegationPrompt(model, params) {
|
|
172
|
-
if (!isOpenAICompatibleGptOssModel(model)) {
|
|
173
|
-
return false;
|
|
174
|
-
}
|
|
175
|
-
if ((params.subagents?.length ?? 0) === 0) {
|
|
176
|
-
return false;
|
|
177
|
-
}
|
|
178
|
-
return params.generalPurposeAgent === true || Boolean(params.taskDescription?.trim());
|
|
179
|
-
}
|
|
180
|
-
function applyDeepAgentDelegationPromptCompatibility(model, params) {
|
|
181
|
-
if (!shouldRelaxDeepAgentDelegationPrompt(model, params)) {
|
|
182
|
-
return params;
|
|
183
|
-
}
|
|
184
|
-
return {
|
|
185
|
-
...params,
|
|
186
|
-
generalPurposeAgent: undefined,
|
|
187
|
-
taskDescription: undefined,
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
71
|
function hasConfiguredMiddlewareKind(binding, kind) {
|
|
191
72
|
return getBindingMiddlewareConfigs(binding)?.some((entry) => entry.kind === kind) ?? false;
|
|
192
73
|
}
|
|
193
|
-
function instantiateProviderTool(compiledTool) {
|
|
194
|
-
const providerTool = asRecord(compiledTool.config?.providerTool);
|
|
195
|
-
const provider = typeof providerTool?.provider === "string" ? providerTool.provider.trim().toLowerCase() : "";
|
|
196
|
-
const toolName = typeof providerTool?.tool === "string" ? providerTool.tool.trim() : "";
|
|
197
|
-
const args = asRecord(providerTool?.args) ?? {};
|
|
198
|
-
if (!provider || !toolName) {
|
|
199
|
-
throw new Error(`Provider tool ${compiledTool.id} must define providerTool.provider and providerTool.tool`);
|
|
200
|
-
}
|
|
201
|
-
const registry = provider === "openai"
|
|
202
|
-
? openAIProviderTools
|
|
203
|
-
: provider === "anthropic"
|
|
204
|
-
? anthropicProviderTools
|
|
205
|
-
: undefined;
|
|
206
|
-
if (!registry) {
|
|
207
|
-
throw new Error(`Provider tool ${compiledTool.id} uses unsupported provider ${provider}`);
|
|
208
|
-
}
|
|
209
|
-
const factory = registry[toolName];
|
|
210
|
-
if (typeof factory !== "function") {
|
|
211
|
-
throw new Error(`Provider tool ${compiledTool.id} references unknown ${provider} tool ${toolName}`);
|
|
212
|
-
}
|
|
213
|
-
return factory(args);
|
|
214
|
-
}
|
|
215
|
-
function wrapResolvedToolWithModelFacingName(resolvedTool, modelFacingName) {
|
|
216
|
-
if (typeof resolvedTool !== "object" || resolvedTool === null) {
|
|
217
|
-
return resolvedTool;
|
|
218
|
-
}
|
|
219
|
-
return new Proxy(resolvedTool, {
|
|
220
|
-
get(target, prop, receiver) {
|
|
221
|
-
if (prop === "name") {
|
|
222
|
-
return modelFacingName;
|
|
223
|
-
}
|
|
224
|
-
return Reflect.get(target, prop, receiver);
|
|
225
|
-
},
|
|
226
|
-
getOwnPropertyDescriptor(target, prop) {
|
|
227
|
-
if (prop === "name") {
|
|
228
|
-
return {
|
|
229
|
-
configurable: true,
|
|
230
|
-
enumerable: true,
|
|
231
|
-
writable: false,
|
|
232
|
-
value: modelFacingName,
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
return Reflect.getOwnPropertyDescriptor(target, prop);
|
|
236
|
-
},
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
function hasCallableToolHandler(value) {
|
|
240
|
-
if (typeof value !== "object" || value === null) {
|
|
241
|
-
return false;
|
|
242
|
-
}
|
|
243
|
-
const typed = value;
|
|
244
|
-
return typeof typed.invoke === "function" || typeof typed.call === "function" || typeof typed.func === "function";
|
|
245
|
-
}
|
|
246
|
-
function asRecord(value) {
|
|
247
|
-
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
248
|
-
? { ...value }
|
|
249
|
-
: undefined;
|
|
250
|
-
}
|
|
251
|
-
function normalizeResolvedToolSchema(resolvedTool) {
|
|
252
|
-
const schema = resolvedTool.schema;
|
|
253
|
-
if (schema && typeof schema.parse === "function" && "_def" in schema) {
|
|
254
|
-
return resolvedTool.schema;
|
|
255
|
-
}
|
|
256
|
-
if (schema && (schema.type === "object" || typeof schema.properties === "object")) {
|
|
257
|
-
return schema;
|
|
258
|
-
}
|
|
259
|
-
return z.object({}).passthrough();
|
|
260
|
-
}
|
|
261
74
|
function isRecord(value) {
|
|
262
75
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
263
76
|
}
|
|
264
77
|
function isObject(value) {
|
|
265
78
|
return isRecord(value);
|
|
266
79
|
}
|
|
267
|
-
function stringifyToolOutput(output) {
|
|
268
|
-
if (typeof output === "string") {
|
|
269
|
-
return output;
|
|
270
|
-
}
|
|
271
|
-
try {
|
|
272
|
-
return JSON.stringify(output);
|
|
273
|
-
}
|
|
274
|
-
catch {
|
|
275
|
-
return `${String(output)}`;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
function normalizeToolArgsForSchema(args, schema) {
|
|
279
|
-
const schemaDef = isObject(schema) ? schema._def : undefined;
|
|
280
|
-
const shape = schemaDef
|
|
281
|
-
? isRecord(schemaDef.shape)
|
|
282
|
-
? schemaDef.shape
|
|
283
|
-
: typeof schemaDef.shape === "function"
|
|
284
|
-
? schemaDef.shape()
|
|
285
|
-
: undefined
|
|
286
|
-
: undefined;
|
|
287
|
-
if (!shape || !isRecord(shape)) {
|
|
288
|
-
return args;
|
|
289
|
-
}
|
|
290
|
-
const keys = Object.keys(shape);
|
|
291
|
-
if (keys.length !== 1) {
|
|
292
|
-
return args;
|
|
293
|
-
}
|
|
294
|
-
const [expectedKey] = keys;
|
|
295
|
-
if (expectedKey in args) {
|
|
296
|
-
return args;
|
|
297
|
-
}
|
|
298
|
-
const aliasesByExpected = {
|
|
299
|
-
city: ["location", "locality", "place"],
|
|
300
|
-
location: ["city", "city_name"],
|
|
301
|
-
};
|
|
302
|
-
const aliases = aliasesByExpected[expectedKey] ?? [];
|
|
303
|
-
const aliasKey = aliases.find((candidate) => candidate in args);
|
|
304
|
-
if (!aliasKey || !(aliasKey in args)) {
|
|
305
|
-
return args;
|
|
306
|
-
}
|
|
307
|
-
return {
|
|
308
|
-
...args,
|
|
309
|
-
[expectedKey]: args[aliasKey],
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
function extractToolCallsFromResult(result) {
|
|
313
|
-
const messages = isRecord(result) && Array.isArray(result.messages) ? result.messages : [];
|
|
314
|
-
const lastMessage = messages.at(-1);
|
|
315
|
-
if (!isObject(lastMessage)) {
|
|
316
|
-
return [];
|
|
317
|
-
}
|
|
318
|
-
const messageKwargs = isObject(lastMessage.kwargs) ? lastMessage.kwargs : undefined;
|
|
319
|
-
const rawToolCalls = Array.isArray(lastMessage.tool_calls)
|
|
320
|
-
? (lastMessage.tool_calls ?? [])
|
|
321
|
-
: Array.isArray(messageKwargs?.tool_calls)
|
|
322
|
-
? messageKwargs.tool_calls
|
|
323
|
-
: [];
|
|
324
|
-
return rawToolCalls
|
|
325
|
-
.map((toolCall) => {
|
|
326
|
-
if (!isObject(toolCall)) {
|
|
327
|
-
return null;
|
|
328
|
-
}
|
|
329
|
-
const functionPayload = isObject(toolCall.function) ? toolCall.function : undefined;
|
|
330
|
-
const name = typeof toolCall.name === "string"
|
|
331
|
-
? toolCall.name
|
|
332
|
-
: typeof functionPayload?.name === "string"
|
|
333
|
-
? functionPayload.name
|
|
334
|
-
: null;
|
|
335
|
-
if (!name) {
|
|
336
|
-
return null;
|
|
337
|
-
}
|
|
338
|
-
const rawArgs = salvageToolArgs(toolCall.args ?? functionPayload?.arguments) ?? {};
|
|
339
|
-
if (!isObject(rawArgs)) {
|
|
340
|
-
return null;
|
|
341
|
-
}
|
|
342
|
-
const id = typeof toolCall.id === "string" ? toolCall.id : undefined;
|
|
343
|
-
return { id, name, args: rawArgs };
|
|
344
|
-
})
|
|
345
|
-
.filter((item) => item !== null);
|
|
346
|
-
}
|
|
347
80
|
function truncateLines(lines, maxChars = 12_000) {
|
|
348
81
|
const joined = lines.join("\n");
|
|
349
82
|
if (joined.length <= maxChars) {
|
|
@@ -372,93 +105,6 @@ function summarizeBuiltinWriteTodosArgs(args) {
|
|
|
372
105
|
items,
|
|
373
106
|
};
|
|
374
107
|
}
|
|
375
|
-
function createModelFacingToolNameCandidates(toolName) {
|
|
376
|
-
const trimmed = toolName.trim();
|
|
377
|
-
if (!trimmed) {
|
|
378
|
-
return [];
|
|
379
|
-
}
|
|
380
|
-
const lastSegment = trimmed.split(".").at(-1) ?? "";
|
|
381
|
-
const candidates = new Set([
|
|
382
|
-
trimmed,
|
|
383
|
-
sanitizeToolNameForModel(trimmed),
|
|
384
|
-
lastSegment,
|
|
385
|
-
lastSegment.trim() ? sanitizeToolNameForModel(lastSegment) : "",
|
|
386
|
-
trimmed.replace(/[^a-zA-Z0-9_]+/g, ""),
|
|
387
|
-
]);
|
|
388
|
-
candidates.delete("");
|
|
389
|
-
return [...candidates];
|
|
390
|
-
}
|
|
391
|
-
function createModelFacingToolNameLookupCandidates(toolName) {
|
|
392
|
-
const trimmed = toolName.trim();
|
|
393
|
-
if (!trimmed) {
|
|
394
|
-
return [];
|
|
395
|
-
}
|
|
396
|
-
const candidates = new Set();
|
|
397
|
-
const add = (value) => {
|
|
398
|
-
for (const candidate of createModelFacingToolNameCandidates(value)) {
|
|
399
|
-
candidates.add(candidate);
|
|
400
|
-
}
|
|
401
|
-
};
|
|
402
|
-
const addSuffixStripped = (value) => {
|
|
403
|
-
const stripped = value.includes(".") ? value.split(".").at(-1) ?? value : value;
|
|
404
|
-
if (stripped !== value) {
|
|
405
|
-
add(stripped);
|
|
406
|
-
}
|
|
407
|
-
};
|
|
408
|
-
add(trimmed);
|
|
409
|
-
if (trimmed.startsWith("functions.")) {
|
|
410
|
-
const stripped = trimmed.substring("functions.".length);
|
|
411
|
-
add(stripped);
|
|
412
|
-
addSuffixStripped(stripped);
|
|
413
|
-
}
|
|
414
|
-
else if (trimmed.startsWith("function.")) {
|
|
415
|
-
const stripped = trimmed.substring("function.".length);
|
|
416
|
-
add(stripped);
|
|
417
|
-
addSuffixStripped(stripped);
|
|
418
|
-
}
|
|
419
|
-
else {
|
|
420
|
-
addSuffixStripped(trimmed);
|
|
421
|
-
}
|
|
422
|
-
return [...candidates];
|
|
423
|
-
}
|
|
424
|
-
function resolveModelFacingToolName(toolName, mapping, tools) {
|
|
425
|
-
const candidateNames = createModelFacingToolNameLookupCandidates(toolName);
|
|
426
|
-
const candidateSet = new Set(candidateNames);
|
|
427
|
-
for (const candidate of candidateNames) {
|
|
428
|
-
const mappedToolName = mapping.modelFacingToOriginal.get(candidate);
|
|
429
|
-
if (mappedToolName) {
|
|
430
|
-
return mappedToolName;
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
for (const candidate of candidateNames) {
|
|
434
|
-
const directMatch = tools.find((tool) => tool.name === candidate);
|
|
435
|
-
if (directMatch) {
|
|
436
|
-
return directMatch.name;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
const candidateMatches = tools.filter((tool) => {
|
|
440
|
-
const modelFacingCandidates = createModelFacingToolNameCandidates(tool.name);
|
|
441
|
-
return modelFacingCandidates.some((candidate) => candidateSet.has(candidate));
|
|
442
|
-
});
|
|
443
|
-
if (candidateMatches.length === 1) {
|
|
444
|
-
return candidateMatches[0].name;
|
|
445
|
-
}
|
|
446
|
-
return toolName;
|
|
447
|
-
}
|
|
448
|
-
function asStructuredExecutableTool(resolvedTool, modelFacingName, description) {
|
|
449
|
-
if (!hasCallableToolHandler(resolvedTool)) {
|
|
450
|
-
return resolvedTool;
|
|
451
|
-
}
|
|
452
|
-
const handler = resolvedTool.invoke ?? resolvedTool.call ?? resolvedTool.func;
|
|
453
|
-
if (typeof handler !== "function") {
|
|
454
|
-
return resolvedTool;
|
|
455
|
-
}
|
|
456
|
-
return createLangChainTool(async (input, config) => handler(input, config), {
|
|
457
|
-
name: modelFacingName,
|
|
458
|
-
description,
|
|
459
|
-
schema: normalizeResolvedToolSchema(resolvedTool),
|
|
460
|
-
});
|
|
461
|
-
}
|
|
462
108
|
export class AgentRuntimeAdapter {
|
|
463
109
|
options;
|
|
464
110
|
modelCache = new Map();
|
|
@@ -475,54 +121,8 @@ export class AgentRuntimeAdapter {
|
|
|
475
121
|
clientRef: model.clientRef,
|
|
476
122
|
});
|
|
477
123
|
}
|
|
478
|
-
resolveBindingTimeout(binding) {
|
|
479
|
-
return resolveTimeoutMs(getBindingModelInit(binding)?.timeout);
|
|
480
|
-
}
|
|
481
|
-
resolveStreamIdleTimeout(binding) {
|
|
482
|
-
const configuredIdleTimeout = resolveTimeoutMs(getBindingModelInit(binding)?.streamIdleTimeout);
|
|
483
|
-
if (configuredIdleTimeout) {
|
|
484
|
-
return configuredIdleTimeout;
|
|
485
|
-
}
|
|
486
|
-
const invokeTimeout = this.resolveBindingTimeout(binding);
|
|
487
|
-
if (invokeTimeout) {
|
|
488
|
-
return Math.min(invokeTimeout, 15_000);
|
|
489
|
-
}
|
|
490
|
-
return 15_000;
|
|
491
|
-
}
|
|
492
|
-
resolveProviderRetryPolicy(binding) {
|
|
493
|
-
const resilience = typeof binding.harnessRuntime.resilience === "object" && binding.harnessRuntime.resilience
|
|
494
|
-
? binding.harnessRuntime.resilience
|
|
495
|
-
: {};
|
|
496
|
-
const providerRetries = typeof resilience.providerRetries === "object" && resilience.providerRetries
|
|
497
|
-
? resilience.providerRetries
|
|
498
|
-
: {};
|
|
499
|
-
const maxAttempts = typeof providerRetries.maxAttempts === "number" &&
|
|
500
|
-
Number.isFinite(providerRetries.maxAttempts) &&
|
|
501
|
-
providerRetries.maxAttempts > 0
|
|
502
|
-
? Math.floor(providerRetries.maxAttempts)
|
|
503
|
-
: 2;
|
|
504
|
-
const backoffMs = typeof providerRetries.backoffMs === "number" &&
|
|
505
|
-
Number.isFinite(providerRetries.backoffMs) &&
|
|
506
|
-
providerRetries.backoffMs >= 0
|
|
507
|
-
? Math.floor(providerRetries.backoffMs)
|
|
508
|
-
: 1_000;
|
|
509
|
-
const retryableMessages = Array.isArray(providerRetries.retryableMessages)
|
|
510
|
-
? providerRetries.retryableMessages.filter((value) => typeof value === "string" && value.trim().length > 0)
|
|
511
|
-
: [];
|
|
512
|
-
return {
|
|
513
|
-
maxAttempts,
|
|
514
|
-
backoffMs,
|
|
515
|
-
retryableMessages,
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
isRetryableProviderError(binding, error) {
|
|
519
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
520
|
-
const normalized = message.toLowerCase();
|
|
521
|
-
const { retryableMessages } = this.resolveProviderRetryPolicy(binding);
|
|
522
|
-
return retryableMessages.some((candidate) => normalized.includes(candidate.toLowerCase()));
|
|
523
|
-
}
|
|
524
124
|
async invokeWithProviderRetry(binding, operation) {
|
|
525
|
-
const retryPolicy =
|
|
125
|
+
const retryPolicy = resolveProviderRetryPolicy(binding);
|
|
526
126
|
let lastError;
|
|
527
127
|
for (let attempt = 1; attempt <= retryPolicy.maxAttempts; attempt += 1) {
|
|
528
128
|
try {
|
|
@@ -530,7 +130,7 @@ export class AgentRuntimeAdapter {
|
|
|
530
130
|
}
|
|
531
131
|
catch (error) {
|
|
532
132
|
lastError = error;
|
|
533
|
-
if (attempt >= retryPolicy.maxAttempts || !
|
|
133
|
+
if (attempt >= retryPolicy.maxAttempts || !isRetryableProviderError(binding, error)) {
|
|
534
134
|
throw error;
|
|
535
135
|
}
|
|
536
136
|
if (retryPolicy.backoffMs > 0) {
|
|
@@ -668,25 +268,7 @@ export class AgentRuntimeAdapter {
|
|
|
668
268
|
return cached;
|
|
669
269
|
}
|
|
670
270
|
const pending = (async () => {
|
|
671
|
-
|
|
672
|
-
return wrapResolvedModel(await this.options.modelResolver(model.id));
|
|
673
|
-
}
|
|
674
|
-
if (model.provider === "ollama") {
|
|
675
|
-
return wrapResolvedModel(new ChatOllama({ model: model.model, ...model.init }));
|
|
676
|
-
}
|
|
677
|
-
if (model.provider === "openai-compatible") {
|
|
678
|
-
return wrapResolvedModel(new ChatOpenAI({ model: model.model, ...normalizeOpenAICompatibleInit(model.init) }));
|
|
679
|
-
}
|
|
680
|
-
if (model.provider === "openai") {
|
|
681
|
-
return wrapResolvedModel(new ChatOpenAI({ model: model.model, ...model.init }));
|
|
682
|
-
}
|
|
683
|
-
if (model.provider === "anthropic") {
|
|
684
|
-
return wrapResolvedModel(new ChatAnthropic({ model: model.model, ...model.init }));
|
|
685
|
-
}
|
|
686
|
-
if (model.provider === "google" || model.provider === "google-genai" || model.provider === "gemini") {
|
|
687
|
-
return wrapResolvedModel(new ChatGoogle({ model: model.model, ...model.init }));
|
|
688
|
-
}
|
|
689
|
-
return wrapResolvedModel(await initChatModel(model.model, { modelProvider: model.provider, ...model.init }));
|
|
271
|
+
return wrapResolvedModel(await createResolvedModel(model, this.options.modelResolver));
|
|
690
272
|
})();
|
|
691
273
|
this.modelCache.set(cacheKey, pending);
|
|
692
274
|
try {
|
|
@@ -700,69 +282,14 @@ export class AgentRuntimeAdapter {
|
|
|
700
282
|
buildToolNameMapping(tools) {
|
|
701
283
|
return buildToolNameMapping(tools);
|
|
702
284
|
}
|
|
703
|
-
buildAgentMessages(history, input) {
|
|
704
|
-
return [
|
|
705
|
-
...history.map((item) => ({ role: item.role, content: normalizeMessageContent(item.content) })),
|
|
706
|
-
{ role: "user", content: normalizeMessageContent(input) },
|
|
707
|
-
];
|
|
708
|
-
}
|
|
709
285
|
buildSlashCommandSkillInstruction(binding, input) {
|
|
710
|
-
|
|
711
|
-
const match = inputText.match(/^\/([a-z0-9]+(?:-[a-z0-9]+)*)(?:\s+([\s\S]*))?$/i);
|
|
712
|
-
if (!match) {
|
|
713
|
-
return undefined;
|
|
714
|
-
}
|
|
715
|
-
const invokedName = match[1].toLowerCase();
|
|
716
|
-
const argumentText = match[2]?.trim() ?? "";
|
|
717
|
-
const skillPaths = binding.deepAgentParams?.skills ?? binding.langchainAgentParams?.skills ?? [];
|
|
718
|
-
const matchedSkillPath = skillPaths.find((skillPath) => readSkillMetadata(skillPath).name.toLowerCase() === invokedName);
|
|
719
|
-
if (!matchedSkillPath) {
|
|
720
|
-
return undefined;
|
|
721
|
-
}
|
|
722
|
-
const metadata = readSkillMetadata(matchedSkillPath);
|
|
723
|
-
const skillQualifier = metadata.userInvocable === true ? "user-invocable skill" : "skill";
|
|
724
|
-
const dryRunHint = /\s--dry-run(?:\s|$)/.test(` ${argumentText} `)
|
|
725
|
-
? "This invocation includes `--dry-run`. Perform the real fetch or inspection steps needed for dry-run output. Do not return hypothetical or mock results."
|
|
726
|
-
: undefined;
|
|
727
|
-
return [
|
|
728
|
-
`This user message is an explicit command-style invocation of the ${skillQualifier} \`${metadata.name}\`.`,
|
|
729
|
-
`Read the skill file for \`${metadata.name}\` before taking action, then follow its documented phases and constraints exactly.`,
|
|
730
|
-
`You must use the \`${metadata.name}\` skill for this request and follow its documented workflow.`,
|
|
731
|
-
`Treat everything after \`/${metadata.name}\` as the skill argument string: ${argumentText ? JSON.stringify(argumentText) : '""'}.`,
|
|
732
|
-
"Do not answer with a generic explanation of what the skill would do. Execute the skill workflow using the available tools unless the skill instructions explicitly require confirmation before acting.",
|
|
733
|
-
dryRunHint,
|
|
734
|
-
].filter((line) => typeof line === "string" && line.length > 0).join("\n");
|
|
286
|
+
return buildSlashCommandSkillInstruction(binding, input);
|
|
735
287
|
}
|
|
736
288
|
buildInvocationRequest(binding, history, input, options = {}) {
|
|
737
|
-
|
|
738
|
-
const messages = this.buildAgentMessages(history, input);
|
|
739
|
-
return {
|
|
740
|
-
...(options.state ?? {}),
|
|
741
|
-
...(options.files ? { files: options.files } : {}),
|
|
742
|
-
messages: userInvocableInstruction
|
|
743
|
-
? [{ role: "system", content: userInvocableInstruction }, ...messages]
|
|
744
|
-
: messages,
|
|
745
|
-
};
|
|
746
|
-
}
|
|
747
|
-
buildStateSnapshot(result) {
|
|
748
|
-
const snapshot = { ...result };
|
|
749
|
-
delete snapshot.messages;
|
|
750
|
-
delete snapshot.__interrupt__;
|
|
751
|
-
delete snapshot.structuredResponse;
|
|
752
|
-
delete snapshot.files;
|
|
753
|
-
return Object.keys(snapshot).length > 0 ? snapshot : undefined;
|
|
289
|
+
return buildInvocationRequest(binding, history, input, options);
|
|
754
290
|
}
|
|
755
291
|
buildRawModelMessages(binding, systemPrompt, history, input) {
|
|
756
|
-
|
|
757
|
-
if (systemPrompt) {
|
|
758
|
-
messages.push({ role: "system", content: systemPrompt });
|
|
759
|
-
}
|
|
760
|
-
const userInvocableInstruction = this.buildSlashCommandSkillInstruction(binding, input);
|
|
761
|
-
if (userInvocableInstruction) {
|
|
762
|
-
messages.push({ role: "system", content: userInvocableInstruction });
|
|
763
|
-
}
|
|
764
|
-
messages.push(...this.buildAgentMessages(history, input));
|
|
765
|
-
return messages;
|
|
292
|
+
return buildRawModelMessages(binding, systemPrompt, history, input);
|
|
766
293
|
}
|
|
767
294
|
resolveTools(tools, binding) {
|
|
768
295
|
const resolved = this.options.toolResolver ? this.options.toolResolver(tools.map((tool) => tool.id), binding) : [];
|
|
@@ -781,38 +308,8 @@ export class AgentRuntimeAdapter {
|
|
|
781
308
|
return modelFacingName === compiledTool.name ? wrappedTool : wrapResolvedToolWithModelFacingName(wrappedTool, modelFacingName);
|
|
782
309
|
});
|
|
783
310
|
}
|
|
784
|
-
normalizeInterruptPolicy(rule) {
|
|
785
|
-
if (!rule)
|
|
786
|
-
return null;
|
|
787
|
-
if (rule === true)
|
|
788
|
-
return ["approve", "edit", "reject"];
|
|
789
|
-
const typed = rule;
|
|
790
|
-
if (Array.isArray(typed.allowedDecisions)) {
|
|
791
|
-
return typed.allowedDecisions.filter((item) => item === "approve" || item === "edit" || item === "reject");
|
|
792
|
-
}
|
|
793
|
-
const decisions = [];
|
|
794
|
-
if (typed.allowAccept !== false)
|
|
795
|
-
decisions.push("approve");
|
|
796
|
-
if (typed.allowEdit !== false)
|
|
797
|
-
decisions.push("edit");
|
|
798
|
-
if (typed.allowReject !== false)
|
|
799
|
-
decisions.push("reject");
|
|
800
|
-
return decisions.length > 0 ? decisions : null;
|
|
801
|
-
}
|
|
802
311
|
compileInterruptOn(tools, compatibilityRules) {
|
|
803
|
-
|
|
804
|
-
const compiled = new Map();
|
|
805
|
-
for (const [toolName, rule] of Object.entries(compatibilityRules ?? {})) {
|
|
806
|
-
const modelFacingName = toolNameMapping.originalToModelFacing.get(toolName) ?? toolName;
|
|
807
|
-
const allowedDecisions = this.normalizeInterruptPolicy(rule);
|
|
808
|
-
if (!allowedDecisions) {
|
|
809
|
-
compiled.delete(modelFacingName);
|
|
810
|
-
}
|
|
811
|
-
else {
|
|
812
|
-
compiled.set(modelFacingName, { allowedDecisions });
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
return compiled.size > 0 ? Object.fromEntries(compiled.entries()) : undefined;
|
|
312
|
+
return compileInterruptOn(tools, compatibilityRules);
|
|
816
313
|
}
|
|
817
314
|
resolveInterruptOn(binding) {
|
|
818
315
|
return this.compileInterruptOn(getBindingPrimaryTools(binding), getBindingInterruptCompatibilityRules(binding));
|
|
@@ -1333,13 +830,13 @@ export class AgentRuntimeAdapter {
|
|
|
1333
830
|
.map((edge) => this.normalizeLangGraphWorkflowEdge(edge))
|
|
1334
831
|
.filter((edge) => edge !== null);
|
|
1335
832
|
}
|
|
1336
|
-
shouldFollowLangGraphEdge(edge, state,
|
|
833
|
+
shouldFollowLangGraphEdge(edge, state, currentResult) {
|
|
1337
834
|
const condition = edge.when?.toLowerCase().trim();
|
|
1338
835
|
if (!condition || condition === "always") {
|
|
1339
836
|
return true;
|
|
1340
837
|
}
|
|
1341
838
|
const review = state.review?.toLowerCase() ?? "";
|
|
1342
|
-
const hasResult = Boolean(
|
|
839
|
+
const hasResult = Boolean(currentResult);
|
|
1343
840
|
switch (condition) {
|
|
1344
841
|
case "has_plan":
|
|
1345
842
|
return Boolean(state.plan?.trim());
|
|
@@ -1365,9 +862,9 @@ export class AgentRuntimeAdapter {
|
|
|
1365
862
|
return false;
|
|
1366
863
|
}
|
|
1367
864
|
}
|
|
1368
|
-
listLangGraphWorkflowNextNodes(workflow, nodeId, state,
|
|
865
|
+
listLangGraphWorkflowNextNodes(workflow, nodeId, state, currentResult) {
|
|
1369
866
|
return this.listLangGraphWorkflowEdges(workflow)
|
|
1370
|
-
.filter((edge) => edge.from === nodeId && this.shouldFollowLangGraphEdge(edge, state,
|
|
867
|
+
.filter((edge) => edge.from === nodeId && this.shouldFollowLangGraphEdge(edge, state, currentResult))
|
|
1371
868
|
.map((edge) => edge.to);
|
|
1372
869
|
}
|
|
1373
870
|
extractInvocationRequestText(request) {
|
|
@@ -1631,7 +1128,7 @@ export class AgentRuntimeAdapter {
|
|
|
1631
1128
|
`User request:\n${userInputText}`,
|
|
1632
1129
|
...(workflowState.plan ? ["", `Workflow plan:\n${workflowState.plan}`] : []),
|
|
1633
1130
|
...(workflowState.review ? ["", `Workflow review:\n${workflowState.review}`] : []),
|
|
1634
|
-
...(activeResult ? ["", `Current
|
|
1131
|
+
...(activeResult ? ["", `Current result:\n${extractVisibleOutput(activeResult) || JSON.stringify(activeResult, null, 2)}`] : []),
|
|
1635
1132
|
"",
|
|
1636
1133
|
"Complete the delegated subagent work and return concise results.",
|
|
1637
1134
|
].join("\n");
|
|
@@ -1746,7 +1243,7 @@ export class AgentRuntimeAdapter {
|
|
|
1746
1243
|
workflowState.review = await this.invokeWorkflowNodeModel(reviewerModel, reviewerPrompt, [
|
|
1747
1244
|
`User request:\n${userInputText}`,
|
|
1748
1245
|
"",
|
|
1749
|
-
`
|
|
1246
|
+
`Current result:\n${extractVisibleOutput(activeResult) || JSON.stringify(activeResult, null, 2)}`,
|
|
1750
1247
|
].join("\n"));
|
|
1751
1248
|
}
|
|
1752
1249
|
else if ((nodeRole === "finalizer" || nodeRole === "final") && activeResult) {
|
|
@@ -1988,7 +1485,7 @@ export class AgentRuntimeAdapter {
|
|
|
1988
1485
|
workflowState.review = await this.invokeWorkflowNodeModel(reviewerModel, reviewerPrompt, [
|
|
1989
1486
|
`User request:\n${userInputText}`,
|
|
1990
1487
|
"",
|
|
1991
|
-
`
|
|
1488
|
+
`Current result:\n${extractVisibleOutput(activeResult) || JSON.stringify(activeResult, null, 2)}`,
|
|
1992
1489
|
].join("\n"));
|
|
1993
1490
|
if (workflowState.review) {
|
|
1994
1491
|
yield { kind: "reasoning", content: workflowState.review };
|
|
@@ -2329,7 +1826,7 @@ export class AgentRuntimeAdapter {
|
|
|
2329
1826
|
const callRuntime = async (activeBinding, activeRequest) => {
|
|
2330
1827
|
return this.invokeWithProviderRetry(activeBinding, async () => {
|
|
2331
1828
|
const runnable = await this.create(activeBinding);
|
|
2332
|
-
return (await this.withTimeout(() => runnable.invoke(activeRequest, { configurable: { thread_id: threadId, run_id: runId }, ...(options.context ? { context: options.context } : {}) }),
|
|
1829
|
+
return (await this.withTimeout(() => runnable.invoke(activeRequest, { configurable: { thread_id: threadId, run_id: runId }, ...(options.context ? { context: options.context } : {}) }), resolveBindingTimeout(activeBinding), "agent invoke", "invoke"));
|
|
2333
1830
|
});
|
|
2334
1831
|
};
|
|
2335
1832
|
const callRuntimeWithToolParseRecovery = async (activeRequest) => {
|
|
@@ -2475,7 +1972,7 @@ export class AgentRuntimeAdapter {
|
|
|
2475
1972
|
...(outputContent !== undefined ? { outputContent } : {}),
|
|
2476
1973
|
...(contentBlocks.length > 0 ? { contentBlocks } : {}),
|
|
2477
1974
|
...(asRecord(result.files) ? { files: asRecord(result.files) } : {}),
|
|
2478
|
-
...(
|
|
1975
|
+
...(buildStateSnapshot(result) ? { stateSnapshot: buildStateSnapshot(result) } : {}),
|
|
2479
1976
|
upstreamResult: result,
|
|
2480
1977
|
},
|
|
2481
1978
|
};
|
|
@@ -2483,8 +1980,8 @@ export class AgentRuntimeAdapter {
|
|
|
2483
1980
|
async *stream(binding, input, threadId, history = [], options = {}) {
|
|
2484
1981
|
try {
|
|
2485
1982
|
const adapterKind = getBindingAdapterKind(binding);
|
|
2486
|
-
const invokeTimeoutMs =
|
|
2487
|
-
const streamIdleTimeoutMs =
|
|
1983
|
+
const invokeTimeoutMs = resolveBindingTimeout(binding);
|
|
1984
|
+
const streamIdleTimeoutMs = resolveStreamIdleTimeout(binding);
|
|
2488
1985
|
const streamDeadlineAt = invokeTimeoutMs ? Date.now() + invokeTimeoutMs : undefined;
|
|
2489
1986
|
const primaryTools = getBindingPrimaryTools(binding);
|
|
2490
1987
|
const toolNameMapping = this.buildToolNameMapping(primaryTools);
|
|
@@ -2541,6 +2038,7 @@ export class AgentRuntimeAdapter {
|
|
|
2541
2038
|
const seenTerminalOutputs = new Set();
|
|
2542
2039
|
let lastStep = "";
|
|
2543
2040
|
for await (const event of this.iterateWithTimeout(events, streamIdleTimeoutMs, "agent streamEvents", streamDeadlineAt, invokeTimeoutMs)) {
|
|
2041
|
+
yield { kind: "upstream-event", event: normalizeUpstreamRuntimeEvent(event) };
|
|
2544
2042
|
const interruptPayload = extractInterruptPayload(event);
|
|
2545
2043
|
if (interruptPayload) {
|
|
2546
2044
|
yield { kind: "interrupt", content: interruptPayload };
|