@botbotgo/agent-harness 0.0.420 → 0.0.422
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/adapter/flow/invocation-flow.d.ts +10 -0
- package/dist/runtime/adapter/flow/invocation-flow.js +2 -0
- package/dist/runtime/adapter/flow/invoke-runtime.d.ts +10 -0
- package/dist/runtime/adapter/flow/invoke-runtime.js +2 -0
- package/dist/runtime/adapter/flow/stream-runtime.d.ts +20 -0
- package/dist/runtime/adapter/flow/stream-runtime.js +201 -3
- package/dist/runtime/adapter/local-tool-invocation.d.ts +11 -1
- package/dist/runtime/adapter/local-tool-invocation.js +221 -9
- package/dist/runtime/adapter/middleware-assembly.js +294 -20
- package/dist/runtime/agent-runtime-adapter.js +657 -48
- package/dist/runtime/agent-runtime-assembly.js +1 -1
- package/dist/runtime/harness/run/inspection.js +9 -1
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.422";
|
|
2
2
|
export declare const AGENT_HARNESS_RELEASE_DATE = "2026-05-03";
|
package/dist/package-version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export const AGENT_HARNESS_VERSION = "0.0.422";
|
|
2
2
|
export const AGENT_HARNESS_RELEASE_DATE = "2026-05-03";
|
|
@@ -16,6 +16,16 @@ export declare function executeRequestInvocation(options: {
|
|
|
16
16
|
toolRuntimeContext?: Record<string, unknown>;
|
|
17
17
|
suppressInitialRequiredPlanInstruction?: boolean;
|
|
18
18
|
externalPlanEvidence?: boolean;
|
|
19
|
+
externalPlanEvidenceTool?: {
|
|
20
|
+
name: string;
|
|
21
|
+
args?: Record<string, unknown>;
|
|
22
|
+
id?: string;
|
|
23
|
+
};
|
|
24
|
+
externalPlanEvidenceTools?: Array<{
|
|
25
|
+
name: string;
|
|
26
|
+
args?: Record<string, unknown>;
|
|
27
|
+
id?: string;
|
|
28
|
+
}>;
|
|
19
29
|
};
|
|
20
30
|
resolveTools: (tools: CompiledTool[], binding?: CompiledAgentBinding) => unknown[];
|
|
21
31
|
getToolNameMapping: (binding: CompiledAgentBinding) => ToolNameMapping;
|
|
@@ -339,6 +339,8 @@ export async function executeRequestInvocation(options) {
|
|
|
339
339
|
callRuntimeWithToolParseRecovery: options.callRuntimeWithToolParseRecovery,
|
|
340
340
|
toolRuntimeContext: invokeOptions.toolRuntimeContext,
|
|
341
341
|
externalPlanEvidence: invokeOptions.externalPlanEvidence,
|
|
342
|
+
externalPlanEvidenceTool: invokeOptions.externalPlanEvidenceTool,
|
|
343
|
+
externalPlanEvidenceTools: invokeOptions.externalPlanEvidenceTools,
|
|
342
344
|
});
|
|
343
345
|
let localOrUpstreamInvocation = await invokeOnce(request);
|
|
344
346
|
if (options.resumePayload === undefined
|
|
@@ -18,6 +18,16 @@ export declare function invokeRuntimeWithLocalTools(options: {
|
|
|
18
18
|
callRuntimeWithToolParseRecovery: (request: unknown) => Promise<Record<string, unknown>>;
|
|
19
19
|
toolRuntimeContext?: Record<string, unknown>;
|
|
20
20
|
externalPlanEvidence?: boolean;
|
|
21
|
+
externalPlanEvidenceTool?: {
|
|
22
|
+
name: string;
|
|
23
|
+
args?: Record<string, unknown>;
|
|
24
|
+
id?: string;
|
|
25
|
+
};
|
|
26
|
+
externalPlanEvidenceTools?: Array<{
|
|
27
|
+
name: string;
|
|
28
|
+
args?: Record<string, unknown>;
|
|
29
|
+
id?: string;
|
|
30
|
+
}>;
|
|
21
31
|
}): Promise<{
|
|
22
32
|
result: Record<string, unknown>;
|
|
23
33
|
executedToolResults: ExecutedToolResult[];
|
|
@@ -16,5 +16,7 @@ export async function invokeRuntimeWithLocalTools(options) {
|
|
|
16
16
|
callRuntimeWithToolParseRecovery: options.callRuntimeWithToolParseRecovery,
|
|
17
17
|
toolRuntimeContext: options.toolRuntimeContext,
|
|
18
18
|
externalPlanEvidence: options.externalPlanEvidence,
|
|
19
|
+
externalPlanEvidenceTool: options.externalPlanEvidenceTool,
|
|
20
|
+
externalPlanEvidenceTools: options.externalPlanEvidenceTools,
|
|
19
21
|
});
|
|
20
22
|
}
|
|
@@ -23,6 +23,16 @@ export declare function streamRuntimeExecution(options: {
|
|
|
23
23
|
toolRuntimeContext?: Record<string, unknown>;
|
|
24
24
|
suppressInitialRequiredPlanInstruction?: boolean;
|
|
25
25
|
externalPlanEvidence?: boolean;
|
|
26
|
+
externalPlanEvidenceTool?: {
|
|
27
|
+
name: string;
|
|
28
|
+
args?: Record<string, unknown>;
|
|
29
|
+
id?: string;
|
|
30
|
+
};
|
|
31
|
+
externalPlanEvidenceTools?: Array<{
|
|
32
|
+
name: string;
|
|
33
|
+
args?: Record<string, unknown>;
|
|
34
|
+
id?: string;
|
|
35
|
+
}>;
|
|
26
36
|
};
|
|
27
37
|
primaryTools: CompiledTool[];
|
|
28
38
|
toolNameMapping: ToolNameMapping;
|
|
@@ -51,6 +61,16 @@ export declare function streamRuntimeExecution(options: {
|
|
|
51
61
|
toolRuntimeContext?: Record<string, unknown>;
|
|
52
62
|
suppressInitialRequiredPlanInstruction?: boolean;
|
|
53
63
|
externalPlanEvidence?: boolean;
|
|
64
|
+
externalPlanEvidenceTool?: {
|
|
65
|
+
name: string;
|
|
66
|
+
args?: Record<string, unknown>;
|
|
67
|
+
id?: string;
|
|
68
|
+
};
|
|
69
|
+
externalPlanEvidenceTools?: Array<{
|
|
70
|
+
name: string;
|
|
71
|
+
args?: Record<string, unknown>;
|
|
72
|
+
id?: string;
|
|
73
|
+
}>;
|
|
54
74
|
}) => Promise<{
|
|
55
75
|
output: string;
|
|
56
76
|
metadata?: Record<string, unknown>;
|
|
@@ -128,6 +128,182 @@ function hasIncompletePlanOutput(value) {
|
|
|
128
128
|
}
|
|
129
129
|
return null;
|
|
130
130
|
}
|
|
131
|
+
function extractInProgressTodoContents(value) {
|
|
132
|
+
if (typeof value !== "object" || value === null) {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
const typed = value;
|
|
136
|
+
const arrays = [typed.todos, typed.items];
|
|
137
|
+
const contents = [];
|
|
138
|
+
for (const candidate of arrays) {
|
|
139
|
+
if (!Array.isArray(candidate)) {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
for (const todo of candidate) {
|
|
143
|
+
if (typeof todo !== "object" || todo === null) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const item = todo;
|
|
147
|
+
if (item.status !== "in_progress") {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
const content = [item.content, item.description, item.title, item.name, item.text]
|
|
151
|
+
.find((value) => typeof value === "string" && value.trim().length > 0);
|
|
152
|
+
if (content) {
|
|
153
|
+
contents.push(content.trim());
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
for (const nested of [typed.summary, typed.update, typed.data, typed.output]) {
|
|
158
|
+
contents.push(...extractInProgressTodoContents(nested));
|
|
159
|
+
}
|
|
160
|
+
return [...new Set(contents)];
|
|
161
|
+
}
|
|
162
|
+
function extractTodoContentsForEvidenceResolution(value) {
|
|
163
|
+
const inProgress = extractTodoContentsByStatus(value, "in_progress");
|
|
164
|
+
if (inProgress.length > 0) {
|
|
165
|
+
return inProgress;
|
|
166
|
+
}
|
|
167
|
+
return extractTodoContentsByStatus(value, "pending");
|
|
168
|
+
}
|
|
169
|
+
function extractTodoContentsByStatus(value, status) {
|
|
170
|
+
if (Array.isArray(value)) {
|
|
171
|
+
return value.flatMap((item) => extractTodoContentsByStatus(item, status));
|
|
172
|
+
}
|
|
173
|
+
if (typeof value !== "object" || value === null) {
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
const typed = value;
|
|
177
|
+
const arrays = [typed.todos, typed.items];
|
|
178
|
+
const contents = [];
|
|
179
|
+
for (const array of arrays) {
|
|
180
|
+
if (!Array.isArray(array)) {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
for (const item of array) {
|
|
184
|
+
if (typeof item !== "object" || item === null) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
const typedItem = item;
|
|
188
|
+
if (typedItem.status !== status) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const content = [
|
|
192
|
+
typedItem.content,
|
|
193
|
+
typedItem.description,
|
|
194
|
+
typedItem.title,
|
|
195
|
+
typedItem.name,
|
|
196
|
+
typedItem.text,
|
|
197
|
+
].find((value) => typeof value === "string" && value.trim().length > 0);
|
|
198
|
+
if (content) {
|
|
199
|
+
contents.push(content.trim());
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
for (const nested of [typed.summary, typed.update, typed.data, typed.output]) {
|
|
204
|
+
contents.push(...extractTodoContentsByStatus(nested, status));
|
|
205
|
+
}
|
|
206
|
+
return [...new Set(contents)];
|
|
207
|
+
}
|
|
208
|
+
function buildRunCommittedTodoEvidenceInstruction(primaryTools, planToolOutput) {
|
|
209
|
+
const todoContents = extractInProgressTodoContents(planToolOutput);
|
|
210
|
+
const todoText = todoContents.length > 0
|
|
211
|
+
? todoContents.map((content, index) => `${index + 1}. ${content}`).join("\n")
|
|
212
|
+
: "(no in-progress todo content was readable)";
|
|
213
|
+
return [
|
|
214
|
+
buildRunEvidenceAfterPlanInstruction(primaryTools),
|
|
215
|
+
"",
|
|
216
|
+
"The completed write_todos result contains these in-progress evidence commitments:",
|
|
217
|
+
todoText,
|
|
218
|
+
"",
|
|
219
|
+
"Your next action must execute the non-planning tool named by the in-progress TODO. If every TODO is pending, execute the non-planning tool named by the first pending TODO. Do not call write_todos or read_todos again before that evidence tool returns.",
|
|
220
|
+
].join("\n");
|
|
221
|
+
}
|
|
222
|
+
function resolveCommittedPlanEvidenceTool(primaryTools, planToolOutput) {
|
|
223
|
+
return resolveCommittedPlanEvidenceTools(primaryTools, planToolOutput)[0];
|
|
224
|
+
}
|
|
225
|
+
function resolveCommittedPlanEvidenceTools(primaryTools, planToolOutput) {
|
|
226
|
+
const availableToolNames = primaryTools
|
|
227
|
+
.map(readPrimaryToolName)
|
|
228
|
+
.filter((name) => name.length > 0 && !isPlanToolName(name));
|
|
229
|
+
const todoContents = extractTodoContentsForEvidenceResolution(planToolOutput);
|
|
230
|
+
if (todoContents.length === 0) {
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
const toolsByName = new Map(primaryTools.map((tool) => [tool.name, tool]));
|
|
234
|
+
const resolved = [];
|
|
235
|
+
const seen = new Set();
|
|
236
|
+
for (const content of todoContents) {
|
|
237
|
+
const todoText = content.toLowerCase();
|
|
238
|
+
const matches = availableToolNames.filter((name) => todoText.includes(name.toLowerCase()));
|
|
239
|
+
const selectedNames = matches.length === 1
|
|
240
|
+
? [matches[0]]
|
|
241
|
+
: resolveBestScoredToolNames(availableToolNames, toolsByName, todoText);
|
|
242
|
+
for (const selectedName of selectedNames) {
|
|
243
|
+
if (seen.has(selectedName)) {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
seen.add(selectedName);
|
|
247
|
+
const matchedTool = toolsByName.get(selectedName);
|
|
248
|
+
const args = buildCommittedPlanEvidenceToolArgs(matchedTool, content);
|
|
249
|
+
resolved.push({
|
|
250
|
+
name: selectedName,
|
|
251
|
+
args,
|
|
252
|
+
id: `stream-committed-plan-evidence-tool-${resolved.length + 1}`,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return resolved;
|
|
257
|
+
}
|
|
258
|
+
function extractSelectionTokens(value) {
|
|
259
|
+
const tokens = new Set();
|
|
260
|
+
for (const match of value.matchAll(/[\p{L}\p{N}_-]+/gu)) {
|
|
261
|
+
const token = match[0].toLowerCase();
|
|
262
|
+
if (token.length >= 2) {
|
|
263
|
+
tokens.add(token);
|
|
264
|
+
}
|
|
265
|
+
for (const part of token.split(/[_-]+/u)) {
|
|
266
|
+
if (part.length >= 2) {
|
|
267
|
+
tokens.add(part);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return tokens;
|
|
272
|
+
}
|
|
273
|
+
function resolveBestScoredToolNames(availableToolNames, toolsByName, todoText) {
|
|
274
|
+
const requestTokens = extractSelectionTokens(todoText);
|
|
275
|
+
const scored = availableToolNames
|
|
276
|
+
.map((name) => {
|
|
277
|
+
const tool = toolsByName.get(name);
|
|
278
|
+
const toolTokens = extractSelectionTokens(`${name} ${tool?.description ?? ""}`);
|
|
279
|
+
const toolNameTokens = extractSelectionTokens(name);
|
|
280
|
+
let score = 0;
|
|
281
|
+
for (const token of requestTokens) {
|
|
282
|
+
if (toolNameTokens.has(token)) {
|
|
283
|
+
score += 10;
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
if (toolTokens.has(token)) {
|
|
287
|
+
score += token.length > 3 ? 2 : 1;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return { name, score };
|
|
291
|
+
})
|
|
292
|
+
.filter((item) => item.score > 0)
|
|
293
|
+
.sort((left, right) => right.score - left.score);
|
|
294
|
+
const topScore = scored[0]?.score ?? 0;
|
|
295
|
+
return topScore > 0 ? scored.filter((item) => item.score === topScore).map((item) => item.name) : [];
|
|
296
|
+
}
|
|
297
|
+
function buildCommittedPlanEvidenceToolArgs(tool, todoText) {
|
|
298
|
+
const properties = typeof tool?.modelSchema === "object" && tool.modelSchema !== null
|
|
299
|
+
? tool.modelSchema.properties
|
|
300
|
+
: undefined;
|
|
301
|
+
if (typeof properties !== "object" || properties === null) {
|
|
302
|
+
return {};
|
|
303
|
+
}
|
|
304
|
+
const queryLikeField = ["query", "question", "prompt", "input", "text"].find((field) => Object.prototype.hasOwnProperty.call(properties, field));
|
|
305
|
+
return queryLikeField ? { [queryLikeField]: todoText } : {};
|
|
306
|
+
}
|
|
131
307
|
function normalizePlanToolName(toolName) {
|
|
132
308
|
return typeof toolName === "string" ? toolName.trim().toLowerCase().replace(/[\s-]+/gu, "_") : "";
|
|
133
309
|
}
|
|
@@ -655,6 +831,7 @@ export async function* streamRuntimeExecution(options) {
|
|
|
655
831
|
let sawCompletedPlanToolResult = false;
|
|
656
832
|
let sawSuccessfulNonTodoToolResult = false;
|
|
657
833
|
let earlyStreamRecoveryInstruction = null;
|
|
834
|
+
let earlyStreamExternalPlanEvidenceTools;
|
|
658
835
|
let earlyStreamRecoverySuppressInitialPlan = false;
|
|
659
836
|
let completedPlanToolResultCount = 0;
|
|
660
837
|
for await (const event of options.iterateWithTimeout(events, options.streamIdleTimeoutMs, "agent streamEvents", options.streamDeadlineAt, options.invokeTimeoutMs)) {
|
|
@@ -677,12 +854,15 @@ export async function* streamRuntimeExecution(options) {
|
|
|
677
854
|
&& chunk.kind !== "content"
|
|
678
855
|
&& !(chunk.kind === "tool-result" && isPlanToolName(chunk.toolName))
|
|
679
856
|
&& !(chunk.kind === "tool-result" && chunk.isError === true && isRetrySafeInvalidToolSelectionError(chunk.output)));
|
|
857
|
+
const hadPriorPlanToolResult = completedPlanToolResultCount > 0;
|
|
680
858
|
const repeatedPlanToolResultBeforeEvidence = requiresPlanEvidence(options.binding)
|
|
681
859
|
&& !sawSuccessfulNonTodoToolResult
|
|
682
|
-
&&
|
|
860
|
+
&& hadPriorPlanToolResult
|
|
683
861
|
&& projectedChunks.some((chunk) => chunk.kind === "tool-result" && isPlanToolName(chunk.toolName));
|
|
684
862
|
if (repeatedPlanToolResultBeforeEvidence) {
|
|
685
|
-
|
|
863
|
+
const planToolResult = projectedChunks.find((chunk) => chunk.kind === "tool-result" && isPlanToolName(chunk.toolName) && chunk.isError !== true);
|
|
864
|
+
earlyStreamExternalPlanEvidenceTools = resolveCommittedPlanEvidenceTools(options.primaryTools, planToolResult?.kind === "tool-result" ? planToolResult.output : undefined);
|
|
865
|
+
earlyStreamRecoveryInstruction = buildRunCommittedTodoEvidenceInstruction(options.primaryTools, planToolResult?.kind === "tool-result" ? planToolResult.output : undefined);
|
|
686
866
|
earlyStreamRecoverySuppressInitialPlan = true;
|
|
687
867
|
break;
|
|
688
868
|
}
|
|
@@ -721,6 +901,18 @@ export async function* streamRuntimeExecution(options) {
|
|
|
721
901
|
}
|
|
722
902
|
yield chunk;
|
|
723
903
|
}
|
|
904
|
+
const eventContainsPlanToolResult = projectedChunks.some((chunk) => chunk.kind === "tool-result" && isPlanToolName(chunk.toolName) && chunk.isError !== true);
|
|
905
|
+
if (requiresPlanEvidence(options.binding)
|
|
906
|
+
&& eventContainsPlanToolResult
|
|
907
|
+
&& (hadPriorPlanToolResult
|
|
908
|
+
|| projectedChunks.some((chunk) => isCompletedPlanToolResultChunk(chunk)))
|
|
909
|
+
&& !sawSuccessfulNonTodoToolResult) {
|
|
910
|
+
const planToolResult = projectedChunks.find((chunk) => chunk.kind === "tool-result" && isPlanToolName(chunk.toolName) && chunk.isError !== true);
|
|
911
|
+
earlyStreamExternalPlanEvidenceTools = resolveCommittedPlanEvidenceTools(options.primaryTools, planToolResult?.kind === "tool-result" ? planToolResult.output : undefined);
|
|
912
|
+
earlyStreamRecoveryInstruction = buildRunCommittedTodoEvidenceInstruction(options.primaryTools, planToolResult?.kind === "tool-result" ? planToolResult.output : undefined);
|
|
913
|
+
earlyStreamRecoverySuppressInitialPlan = true;
|
|
914
|
+
break;
|
|
915
|
+
}
|
|
724
916
|
if (requiresPlanEvidence(options.binding) && sawCompletedPlanToolResult && sawSuccessfulNonTodoToolResult) {
|
|
725
917
|
if (hasUsefulVisibleSynthesis(projectionState.emittedOutput)) {
|
|
726
918
|
if (deferredStreamContent.length > 0) {
|
|
@@ -794,7 +986,13 @@ export async function* streamRuntimeExecution(options) {
|
|
|
794
986
|
}
|
|
795
987
|
if (earlyStreamRecoveryInstruction) {
|
|
796
988
|
const earlyRecoveryRuntimeOptions = earlyStreamRecoverySuppressInitialPlan
|
|
797
|
-
?
|
|
989
|
+
? {
|
|
990
|
+
...withSuppressedInitialRequiredPlanInstruction(options.runtimeOptions),
|
|
991
|
+
externalPlanEvidence: true,
|
|
992
|
+
...(earlyStreamExternalPlanEvidenceTools && earlyStreamExternalPlanEvidenceTools.length > 0
|
|
993
|
+
? { externalPlanEvidenceTools: earlyStreamExternalPlanEvidenceTools }
|
|
994
|
+
: {}),
|
|
995
|
+
}
|
|
798
996
|
: options.runtimeOptions;
|
|
799
997
|
const recovered = await options.invoke(options.applyToolRecoveryInstruction(options.binding, earlyStreamRecoveryInstruction), options.input, options.sessionId, options.runtimeOptions.requestId ?? options.sessionId, undefined, options.history, earlyRecoveryRuntimeOptions);
|
|
800
998
|
const recoveredToolResults = Array.isArray(recovered.metadata?.executedToolResults)
|
|
@@ -16,10 +16,20 @@ type LocalToolInvocationParams = {
|
|
|
16
16
|
callRuntimeWithToolParseRecovery: (request: unknown) => Promise<Record<string, unknown>>;
|
|
17
17
|
toolRuntimeContext?: Record<string, unknown>;
|
|
18
18
|
externalPlanEvidence?: boolean;
|
|
19
|
+
externalPlanEvidenceTool?: {
|
|
20
|
+
name: string;
|
|
21
|
+
args?: Record<string, unknown>;
|
|
22
|
+
id?: string;
|
|
23
|
+
};
|
|
24
|
+
externalPlanEvidenceTools?: Array<{
|
|
25
|
+
name: string;
|
|
26
|
+
args?: Record<string, unknown>;
|
|
27
|
+
id?: string;
|
|
28
|
+
}>;
|
|
19
29
|
};
|
|
20
30
|
type LocalToolInvocationResult = {
|
|
21
31
|
result: Record<string, unknown>;
|
|
22
32
|
executedToolResults: ExecutedToolResult[];
|
|
23
33
|
};
|
|
24
|
-
export declare function runLocalToolInvocationLoop({ binding, request, primaryTools, toolNameMapping, executableTools, builtinExecutableTools, callRuntimeWithToolParseRecovery, toolRuntimeContext, externalPlanEvidence, }: LocalToolInvocationParams): Promise<LocalToolInvocationResult>;
|
|
34
|
+
export declare function runLocalToolInvocationLoop({ binding, request, primaryTools, toolNameMapping, executableTools, builtinExecutableTools, callRuntimeWithToolParseRecovery, toolRuntimeContext, externalPlanEvidence, externalPlanEvidenceTool, externalPlanEvidenceTools, }: LocalToolInvocationParams): Promise<LocalToolInvocationResult>;
|
|
25
35
|
export {};
|
|
@@ -27,20 +27,57 @@ function stringifyRequestForToolSelection(request) {
|
|
|
27
27
|
return "";
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
+
function extractSelectionTokens(value) {
|
|
31
|
+
const tokens = new Set();
|
|
32
|
+
for (const match of value.matchAll(/[\p{L}\p{N}_-]+/gu)) {
|
|
33
|
+
const token = match[0].toLowerCase();
|
|
34
|
+
if (token.length >= 2) {
|
|
35
|
+
tokens.add(token);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
for (const match of value.matchAll(/[\p{Script=Han}]{2,}/gu)) {
|
|
39
|
+
const sequence = match[0];
|
|
40
|
+
for (let size = 2; size <= Math.min(4, sequence.length); size += 1) {
|
|
41
|
+
for (let index = 0; index <= sequence.length - size; index += 1) {
|
|
42
|
+
tokens.add(sequence.slice(index, index + size).toLowerCase());
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return tokens;
|
|
47
|
+
}
|
|
30
48
|
function prioritizeBootstrapEvidenceTools(primaryTools, request) {
|
|
31
49
|
const requestText = stringifyRequestForToolSelection(request);
|
|
50
|
+
const requestTokens = extractSelectionTokens(requestText);
|
|
32
51
|
const isFinanceRequest = /\b(?:stock|ticker|finance|market|valuation|quote)\b|股票|股价|行情|估值|财报/iu.test(requestText);
|
|
33
52
|
const evidenceTools = primaryTools
|
|
34
|
-
.map((tool) =>
|
|
35
|
-
|
|
53
|
+
.map((tool) => {
|
|
54
|
+
const name = typeof tool.name === "string" ? tool.name.trim() : "";
|
|
55
|
+
const description = typeof tool.description === "string" ? tool.description : "";
|
|
56
|
+
const toolTokens = extractSelectionTokens(`${name} ${description}`);
|
|
57
|
+
let score = 0;
|
|
58
|
+
for (const token of requestTokens) {
|
|
59
|
+
if (toolTokens.has(token)) {
|
|
60
|
+
score += token.length > 3 ? 2 : 1;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (requestText.toLowerCase().includes(name.toLowerCase())) {
|
|
64
|
+
score += 6;
|
|
65
|
+
}
|
|
66
|
+
return { name, score };
|
|
67
|
+
})
|
|
68
|
+
.filter((tool) => tool.name.length > 0 && !isPlanToolName(tool.name))
|
|
36
69
|
.sort((left, right) => {
|
|
70
|
+
if (right.score !== left.score) {
|
|
71
|
+
return right.score - left.score;
|
|
72
|
+
}
|
|
37
73
|
if (!isFinanceRequest) {
|
|
38
74
|
return 0;
|
|
39
75
|
}
|
|
40
|
-
const leftFinance = left.includes("finance") ? 0 : 1;
|
|
41
|
-
const rightFinance = right.includes("finance") ? 0 : 1;
|
|
76
|
+
const leftFinance = left.name.includes("finance") ? 0 : 1;
|
|
77
|
+
const rightFinance = right.name.includes("finance") ? 0 : 1;
|
|
42
78
|
return leftFinance - rightFinance;
|
|
43
|
-
})
|
|
79
|
+
})
|
|
80
|
+
.map((tool) => tool.name);
|
|
44
81
|
return evidenceTools.slice(0, 4);
|
|
45
82
|
}
|
|
46
83
|
function createBootstrapTodoPlan(primaryTools, request) {
|
|
@@ -90,6 +127,19 @@ function buildBootstrapPlanToolResult(primaryTools, request) {
|
|
|
90
127
|
})],
|
|
91
128
|
};
|
|
92
129
|
}
|
|
130
|
+
function buildExternalPlanEvidenceToolResult(tools) {
|
|
131
|
+
return {
|
|
132
|
+
messages: [{
|
|
133
|
+
content: "",
|
|
134
|
+
tool_calls: tools.map((tool, index) => ({
|
|
135
|
+
id: tool.id ?? `external-plan-evidence-${index + 1}-${Math.random().toString(36).slice(2, 10)}`,
|
|
136
|
+
name: tool.name,
|
|
137
|
+
args: tool.args ?? {},
|
|
138
|
+
type: "tool_call",
|
|
139
|
+
})),
|
|
140
|
+
}],
|
|
141
|
+
};
|
|
142
|
+
}
|
|
93
143
|
function readPlanStateSummary(output) {
|
|
94
144
|
if (typeof output !== "object" || output === null) {
|
|
95
145
|
return null;
|
|
@@ -210,6 +260,76 @@ function terminalToolErrorRecoveryInstruction(terminalText) {
|
|
|
210
260
|
function requiresPlanEvidence(binding) {
|
|
211
261
|
return binding.harnessRuntime.executionContract?.requiresPlan === true;
|
|
212
262
|
}
|
|
263
|
+
function resolveCommittedTodoEvidenceTool(executedToolResults, primaryTools) {
|
|
264
|
+
const availableTools = primaryTools
|
|
265
|
+
.filter((tool) => typeof tool.name === "string" && tool.name.length > 0 && !isPlanToolName(tool.name));
|
|
266
|
+
if (availableTools.length === 0) {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
for (let index = executedToolResults.length - 1; index >= 0; index -= 1) {
|
|
270
|
+
const result = executedToolResults[index];
|
|
271
|
+
if (!result || result.isError === true || !isPlanToolName(result.toolName)) {
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
const output = result.output;
|
|
275
|
+
const summary = typeof output === "object" && output !== null
|
|
276
|
+
? output.summary
|
|
277
|
+
: undefined;
|
|
278
|
+
const items = typeof summary === "object" && summary !== null && Array.isArray(summary.items)
|
|
279
|
+
? summary.items
|
|
280
|
+
: [];
|
|
281
|
+
const activeItems = items.filter((item) => item.status === "in_progress");
|
|
282
|
+
const candidateItems = activeItems.length > 0
|
|
283
|
+
? activeItems
|
|
284
|
+
: items.filter((item) => item.status === "pending").slice(0, 1);
|
|
285
|
+
for (const item of candidateItems) {
|
|
286
|
+
const content = [
|
|
287
|
+
item.content,
|
|
288
|
+
item.description,
|
|
289
|
+
item.title,
|
|
290
|
+
item.name,
|
|
291
|
+
item.text,
|
|
292
|
+
].filter((value) => typeof value === "string").join(" ").toLowerCase();
|
|
293
|
+
const matched = availableTools.map((tool) => tool.name).filter((toolName) => content.includes(toolName.toLowerCase()));
|
|
294
|
+
if (matched.length === 1) {
|
|
295
|
+
return {
|
|
296
|
+
name: matched[0],
|
|
297
|
+
args: {},
|
|
298
|
+
id: `todo-committed-evidence-${index}`,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
const requestTokens = extractSelectionTokens(content);
|
|
302
|
+
const scored = availableTools
|
|
303
|
+
.map((tool) => {
|
|
304
|
+
const toolTokens = extractSelectionTokens(`${tool.name} ${tool.description ?? ""}`);
|
|
305
|
+
let score = 0;
|
|
306
|
+
for (const token of requestTokens) {
|
|
307
|
+
if (toolTokens.has(token)) {
|
|
308
|
+
score += token.length > 3 ? 2 : 1;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return { name: tool.name, score };
|
|
312
|
+
})
|
|
313
|
+
.filter((item) => item.score > 0)
|
|
314
|
+
.sort((left, right) => right.score - left.score);
|
|
315
|
+
if (scored[0] && (!scored[1] || scored[0].score > scored[1].score)) {
|
|
316
|
+
return {
|
|
317
|
+
name: scored[0].name,
|
|
318
|
+
args: {},
|
|
319
|
+
id: `todo-committed-evidence-${index}`,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
if (matched.length === 1) {
|
|
323
|
+
return {
|
|
324
|
+
name: matched[0],
|
|
325
|
+
args: {},
|
|
326
|
+
id: `todo-committed-evidence-${index}`,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
213
333
|
function extractLatestUserInput(request) {
|
|
214
334
|
const typedRequest = request;
|
|
215
335
|
const messages = Array.isArray(typedRequest.messages) ? typedRequest.messages : [];
|
|
@@ -268,7 +388,7 @@ function summarizeResultMessages(result) {
|
|
|
268
388
|
};
|
|
269
389
|
});
|
|
270
390
|
}
|
|
271
|
-
export async function runLocalToolInvocationLoop({ binding, request, primaryTools, toolNameMapping, executableTools, builtinExecutableTools, callRuntimeWithToolParseRecovery, toolRuntimeContext, externalPlanEvidence, }) {
|
|
391
|
+
export async function runLocalToolInvocationLoop({ binding, request, primaryTools, toolNameMapping, executableTools, builtinExecutableTools, callRuntimeWithToolParseRecovery, toolRuntimeContext, externalPlanEvidence, externalPlanEvidenceTool, externalPlanEvidenceTools, }) {
|
|
272
392
|
const executedToolResults = [];
|
|
273
393
|
let activeRequest = request;
|
|
274
394
|
let currentMessages = Array.isArray(activeRequest.messages) ? [...activeRequest.messages] : [];
|
|
@@ -289,9 +409,39 @@ export async function runLocalToolInvocationLoop({ binding, request, primaryTool
|
|
|
289
409
|
}
|
|
290
410
|
for (let iteration = 0; iteration < maxToolIterations; iteration += 1) {
|
|
291
411
|
const isFinalIteration = iteration + 1 === maxToolIterations;
|
|
292
|
-
|
|
412
|
+
const externalPlanEvidenceToolCalls = externalPlanEvidenceTools && externalPlanEvidenceTools.length > 0
|
|
413
|
+
? externalPlanEvidenceTools
|
|
414
|
+
: externalPlanEvidenceTool
|
|
415
|
+
? [externalPlanEvidenceTool]
|
|
416
|
+
: [];
|
|
417
|
+
const shouldRunExternalPlanEvidenceTool = pendingResult === undefined
|
|
418
|
+
&& requiresPlanEvidence(binding)
|
|
419
|
+
&& externalPlanEvidence === true
|
|
420
|
+
&& externalPlanEvidenceToolCalls.length > 0
|
|
421
|
+
&& !hasNonTodoToolEvidence(executedToolResults);
|
|
422
|
+
const usedExternalPlanEvidenceToolThisIteration = shouldRunExternalPlanEvidenceTool;
|
|
423
|
+
result = pendingResult
|
|
424
|
+
?? (shouldRunExternalPlanEvidenceTool
|
|
425
|
+
? buildExternalPlanEvidenceToolResult(externalPlanEvidenceToolCalls)
|
|
426
|
+
: await callRuntimeWithToolParseRecovery(activeRequest));
|
|
293
427
|
pendingResult = undefined;
|
|
294
|
-
|
|
428
|
+
let toolCalls = extractToolCallsFromResult(result);
|
|
429
|
+
const committedTodoEvidenceTool = requiresPlanEvidence(binding)
|
|
430
|
+
&& hasPlanStateEvidence(executedToolResults, externalPlanEvidence)
|
|
431
|
+
&& !hasNonTodoToolEvidence(executedToolResults)
|
|
432
|
+
&& (externalPlanEvidenceTool !== undefined || !hasIncompleteExecutedPlan(executedToolResults, externalPlanEvidence))
|
|
433
|
+
&& (toolCalls.length === 0 || toolCalls.every((toolCall) => isPlanToolName(toolCall.name)))
|
|
434
|
+
? externalPlanEvidenceTool
|
|
435
|
+
? {
|
|
436
|
+
name: externalPlanEvidenceTool.name,
|
|
437
|
+
args: externalPlanEvidenceTool.args ?? {},
|
|
438
|
+
id: externalPlanEvidenceTool.id ?? "external-plan-evidence-tool",
|
|
439
|
+
}
|
|
440
|
+
: resolveCommittedTodoEvidenceTool(executedToolResults, primaryTools)
|
|
441
|
+
: null;
|
|
442
|
+
if (committedTodoEvidenceTool) {
|
|
443
|
+
toolCalls = [committedTodoEvidenceTool];
|
|
444
|
+
}
|
|
295
445
|
if (toolCalls.length === 0) {
|
|
296
446
|
const terminalText = sanitizeVisibleText(extractVisibleOutput(result) || "");
|
|
297
447
|
const hasIncompletePlanState = hasIncompleteExecutedPlan(executedToolResults, externalPlanEvidence);
|
|
@@ -415,7 +565,8 @@ export async function runLocalToolInvocationLoop({ binding, request, primaryTool
|
|
|
415
565
|
}
|
|
416
566
|
repeatedRecoveryWithoutProgress = 0;
|
|
417
567
|
repeatedPlanOnlyAfterPlan = 0;
|
|
418
|
-
const canReplayToolCalls =
|
|
568
|
+
const canReplayToolCalls = usedExternalPlanEvidenceToolThisIteration
|
|
569
|
+
|| canReplayToolCallsLocally(binding, toolCalls, primaryTools, toolNameMapping, executableTools, builtinExecutableTools);
|
|
419
570
|
debugLocalToolReplay({
|
|
420
571
|
toolCalls,
|
|
421
572
|
result,
|
|
@@ -502,6 +653,67 @@ export async function runLocalToolInvocationLoop({ binding, request, primaryTool
|
|
|
502
653
|
content: stringifyToolOutput(safeToolResult),
|
|
503
654
|
}));
|
|
504
655
|
}
|
|
656
|
+
const committedEvidenceTool = requiresPlanEvidence(binding)
|
|
657
|
+
&& !hadNonTodoEvidenceBeforeToolReplay
|
|
658
|
+
&& !hasNonTodoToolEvidence(executedToolResults)
|
|
659
|
+
&& !hasIncompleteExecutedPlan(executedToolResults, externalPlanEvidence)
|
|
660
|
+
? resolveCommittedTodoEvidenceTool(executedToolResults, primaryTools)
|
|
661
|
+
: null;
|
|
662
|
+
if (committedEvidenceTool) {
|
|
663
|
+
const resolvedToolName = resolveModelFacingToolName(committedEvidenceTool.name, toolNameMapping, primaryTools);
|
|
664
|
+
const executable = executableTools.get(committedEvidenceTool.name) ?? executableTools.get(resolvedToolName);
|
|
665
|
+
if (executable) {
|
|
666
|
+
const compiledTool = toolCatalog.get(committedEvidenceTool.name) ?? toolCatalog.get(resolvedToolName);
|
|
667
|
+
const normalizedArgs = normalizeToolArgsForSchema(committedEvidenceTool.args, executable.schema, undefined, {
|
|
668
|
+
latestUserInput,
|
|
669
|
+
});
|
|
670
|
+
const gateway = validateToolGatewayInput({
|
|
671
|
+
toolName: executable.name,
|
|
672
|
+
schema: executable.schema,
|
|
673
|
+
args: normalizedArgs,
|
|
674
|
+
requiresApproval: compiledTool ? toolRequiresRuntimeApproval(compiledTool) : false,
|
|
675
|
+
});
|
|
676
|
+
if (gateway.ok) {
|
|
677
|
+
const toolResult = toolRuntimeContext
|
|
678
|
+
? await executable.invoke(gateway.input, { toolRuntimeContext })
|
|
679
|
+
: await executable.invoke(gateway.input);
|
|
680
|
+
const memoryCandidates = compiledTool ? extractMemoryCandidatesFromToolOutput(compiledTool, toolResult) : [];
|
|
681
|
+
const safeToolResult = await maybePersistLargeToolOutput({
|
|
682
|
+
toolName: executable.name,
|
|
683
|
+
output: toolResult,
|
|
684
|
+
toolRuntimeContext,
|
|
685
|
+
});
|
|
686
|
+
executedToolResults.push({
|
|
687
|
+
toolName: executable.name,
|
|
688
|
+
output: safeToolResult,
|
|
689
|
+
...(memoryCandidates.length > 0 ? { memoryCandidates } : {}),
|
|
690
|
+
});
|
|
691
|
+
nextMessages.push(new ToolMessage({
|
|
692
|
+
name: executable.name,
|
|
693
|
+
tool_call_id: committedEvidenceTool.id,
|
|
694
|
+
content: stringifyToolOutput(safeToolResult),
|
|
695
|
+
}));
|
|
696
|
+
}
|
|
697
|
+
else {
|
|
698
|
+
executedToolResults.push({
|
|
699
|
+
toolName: executable.name,
|
|
700
|
+
output: gateway.error,
|
|
701
|
+
isError: true,
|
|
702
|
+
});
|
|
703
|
+
nextMessages.push(new ToolMessage({
|
|
704
|
+
name: executable.name,
|
|
705
|
+
tool_call_id: committedEvidenceTool.id,
|
|
706
|
+
content: stringifyToolOutput(gateway.error),
|
|
707
|
+
}));
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
if (usedExternalPlanEvidenceToolThisIteration && hasNonTodoToolEvidence(executedToolResults)) {
|
|
712
|
+
return {
|
|
713
|
+
result: buildDeterministicFinalFromToolEvidence(executedToolResults),
|
|
714
|
+
executedToolResults,
|
|
715
|
+
};
|
|
716
|
+
}
|
|
505
717
|
if (requiresPlanEvidence(binding)
|
|
506
718
|
&& toolCalls.length > 0
|
|
507
719
|
&& toolCalls.every((toolCall) => isPlanToolName(toolCall.name))
|