@botbotgo/agent-harness 0.0.418 → 0.0.420

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.
Files changed (28) hide show
  1. package/dist/cli/chat-interactive.js +1 -1
  2. package/dist/cli/chat-stream.js +9 -1
  3. package/dist/package-version.d.ts +2 -2
  4. package/dist/package-version.js +2 -2
  5. package/dist/runtime/adapter/compat/openai-compatible.js +12 -0
  6. package/dist/runtime/adapter/flow/invocation-flow.d.ts +2 -0
  7. package/dist/runtime/adapter/flow/invocation-flow.js +13 -5
  8. package/dist/runtime/adapter/flow/invoke-runtime.d.ts +1 -0
  9. package/dist/runtime/adapter/flow/invoke-runtime.js +1 -0
  10. package/dist/runtime/adapter/flow/stream-runtime.d.ts +4 -0
  11. package/dist/runtime/adapter/flow/stream-runtime.js +177 -14
  12. package/dist/runtime/adapter/invocation-result.js +17 -6
  13. package/dist/runtime/adapter/local-tool-invocation.d.ts +2 -1
  14. package/dist/runtime/adapter/local-tool-invocation.js +268 -21
  15. package/dist/runtime/adapter/model/model-providers.js +269 -58
  16. package/dist/runtime/adapter/model/prompted-json-tool-call-capture.d.ts +9 -0
  17. package/dist/runtime/adapter/model/prompted-json-tool-call-capture.js +40 -0
  18. package/dist/runtime/adapter/runtime-adapter-support.js +58 -12
  19. package/dist/runtime/adapter/runtime-shell.js +3 -2
  20. package/dist/runtime/adapter/stream-event-projection.js +22 -5
  21. package/dist/runtime/adapter/tool/tool-arguments.js +157 -67
  22. package/dist/runtime/adapter/tool/tool-replay.js +0 -4
  23. package/dist/runtime/agent-runtime-adapter.d.ts +3 -0
  24. package/dist/runtime/agent-runtime-adapter.js +217 -73
  25. package/dist/runtime/harness/run/stream-run.js +31 -3
  26. package/dist/runtime/parsing/output-tool-args.js +108 -0
  27. package/dist/workspace/resource-compilers.js +17 -4
  28. package/package.json +1 -1
@@ -382,6 +382,24 @@ function updateDelegationState(state, event, countConfiguredToolsForAgentId) {
382
382
  }
383
383
  }
384
384
  }
385
+ function normalizePlanToolName(toolName) {
386
+ return typeof toolName === "string" ? toolName.trim().toLowerCase().replace(/[\s-]+/gu, "_") : "";
387
+ }
388
+ function isPlanToolName(toolName) {
389
+ const normalized = normalizePlanToolName(toolName);
390
+ return normalized === "write_todos"
391
+ || normalized === "read_todos"
392
+ || normalized === "tool_call_write_todos"
393
+ || normalized === "tool_call_read_todos"
394
+ || normalized === "call_write_todos"
395
+ || normalized === "call_read_todos";
396
+ }
397
+ function isWriteTodosToolName(toolName) {
398
+ const normalized = normalizePlanToolName(toolName);
399
+ return normalized === "write_todos"
400
+ || normalized === "tool_call_write_todos"
401
+ || normalized === "call_write_todos";
402
+ }
385
403
  function extractTodoToolStart(event) {
386
404
  if (typeof event !== "object" || event === null) {
387
405
  return null;
@@ -391,11 +409,11 @@ function extractTodoToolStart(event) {
391
409
  const runType = typeof typed.run_type === "string" ? typed.run_type : "";
392
410
  const toolName = typeof typed.name === "string" ? typed.name : "";
393
411
  const isToolStart = eventName === "on_tool_start" || (eventName === "on_chain_start" && runType === "tool");
394
- if (!isToolStart || (toolName !== "write_todos" && toolName !== "read_todos")) {
412
+ if (!isToolStart || !isPlanToolName(toolName)) {
395
413
  return null;
396
414
  }
397
415
  const input = unwrapPossibleToolInput(typed.data?.input);
398
- if (toolName === "write_todos" && typeof input === "object" && input !== null && !Array.isArray(input)) {
416
+ if (isWriteTodosToolName(toolName) && typeof input === "object" && input !== null && !Array.isArray(input)) {
399
417
  const summary = summarizeBuiltinWriteTodosArgs(input);
400
418
  if (summary.summary.total === 0) {
401
419
  throw new Error("Error invoking tool 'write_todos' with kwargs {\"todos\":[]} with error: Error: Initial write_todos call cannot use an empty todo list. Send the concrete task steps with both content and status.");
@@ -472,7 +490,7 @@ export function projectRuntimeStreamEvent(params) {
472
490
  });
473
491
  }
474
492
  if (toolResult) {
475
- const isTodoTool = toolResult.toolName === "write_todos" || toolResult.toolName === "read_todos";
493
+ const isTodoTool = isPlanToolName(toolResult.toolName);
476
494
  const salvagedTaskErrorFindings = toolResult.toolName === "task"
477
495
  && toolResult.isError === true
478
496
  && !!state.lastCompletedTaskDelegationFindings
@@ -485,8 +503,7 @@ export function projectRuntimeStreamEvent(params) {
485
503
  : toolResult.isError === true;
486
504
  const isSuccessfulTaskResult = toolResult.toolName === "task" && effectiveToolIsError !== true;
487
505
  const isDelegatedExecutionTool = (isDelegatedAgentEvent || state.openToolCapableTaskDelegations > 0)
488
- && toolResult.toolName !== "write_todos"
489
- && toolResult.toolName !== "read_todos"
506
+ && !isPlanToolName(toolResult.toolName)
490
507
  && toolResult.toolName !== "task";
491
508
  if (isDelegatedExecutionTool && toolResult.isError !== true) {
492
509
  recordDelegatedFindings(state, toolResult.output, "tool");
@@ -2,6 +2,7 @@ import { salvageToolArgs } from "../../parsing/output-parsing.js";
2
2
  import { salvageJsonToolCalls } from "../../parsing/output-tool-args.js";
3
3
  import { isRecord } from "../../../utils/object.js";
4
4
  import { extractExplicitResourceReferences, hasExplicitResourceReference } from "../../harness/system/runtime-memory-policy.js";
5
+ import { readCapturedPromptedJsonToolCalls } from "../model/prompted-json-tool-call-capture.js";
5
6
  function isObject(value) {
6
7
  return isRecord(value);
7
8
  }
@@ -109,6 +110,71 @@ function fillLatestUserInputForQueryLikeFields(args, shape, latestUserInput) {
109
110
  }
110
111
  return next;
111
112
  }
113
+ function mapCommonArgumentAliases(args, shape) {
114
+ let next = args;
115
+ if ("args" in shape && !("args" in next) && Array.isArray(next.argv)) {
116
+ next = { ...next, args: next.argv };
117
+ }
118
+ if ("arguments" in shape && !("arguments" in next) && Array.isArray(next.args)) {
119
+ next = { ...next, arguments: next.args };
120
+ }
121
+ return next;
122
+ }
123
+ function schemaPartExpectsArray(schemaPart) {
124
+ if (!isObject(schemaPart)) {
125
+ return false;
126
+ }
127
+ if (schemaPart.type === "array") {
128
+ return true;
129
+ }
130
+ const def = schemaPart._def;
131
+ if (def?.typeName === "ZodArray" || def?.type === "array") {
132
+ return true;
133
+ }
134
+ return schemaPartExpectsArray(def?.innerType);
135
+ }
136
+ function mapStringArrayFields(args, shape) {
137
+ let next = args;
138
+ for (const [key, schemaPart] of Object.entries(shape)) {
139
+ const value = next[key];
140
+ if (typeof value !== "string") {
141
+ continue;
142
+ }
143
+ const raw = value.trim();
144
+ if (!raw) {
145
+ continue;
146
+ }
147
+ const looksLikeDelimitedList = /[,;\n]/u.test(raw) && /(?:s|list|items|areas|paths|files)$/iu.test(key);
148
+ if (!schemaPartExpectsArray(schemaPart) && !looksLikeDelimitedList) {
149
+ continue;
150
+ }
151
+ const items = raw
152
+ .split(/[,;\n]/u)
153
+ .map((item) => item.trim())
154
+ .filter(Boolean);
155
+ if (items.length > 0) {
156
+ next = { ...next, [key]: items };
157
+ }
158
+ }
159
+ return next;
160
+ }
161
+ function mapDelimitedListLikeArgs(args) {
162
+ let next = args;
163
+ for (const [key, value] of Object.entries(args)) {
164
+ if (typeof value !== "string") {
165
+ continue;
166
+ }
167
+ const raw = value.trim();
168
+ if (!/[,;\n]/u.test(raw) || !/(?:s|list|items|areas|paths|files)$/iu.test(key)) {
169
+ continue;
170
+ }
171
+ const items = raw.split(/[,;\n]/u).map((item) => item.trim()).filter(Boolean);
172
+ if (items.length > 0) {
173
+ next = { ...next, [key]: items };
174
+ }
175
+ }
176
+ return next;
177
+ }
112
178
  export function normalizeToolArgsForSchema(args, schema, rawArgsInput, options = {}) {
113
179
  const schemaDef = isObject(schema) ? schema._def : undefined;
114
180
  const zodShape = schemaDef
@@ -123,17 +189,18 @@ export function normalizeToolArgsForSchema(args, schema, rawArgsInput, options =
123
189
  : undefined;
124
190
  const shape = zodShape && isRecord(zodShape) ? zodShape : jsonShape;
125
191
  if (!shape || !isRecord(shape)) {
126
- return args;
192
+ return mapDelimitedListLikeArgs(args);
127
193
  }
194
+ const aliasMappedArgs = mapStringArrayFields(mapCommonArgumentAliases(args, shape), shape);
128
195
  const keys = Object.keys(shape);
129
196
  if (keys.length !== 1) {
130
- return fillLatestUserInputForQueryLikeFields(args, shape, options.latestUserInput);
197
+ return fillLatestUserInputForQueryLikeFields(aliasMappedArgs, shape, options.latestUserInput);
131
198
  }
132
199
  const [expectedKey] = keys;
133
- if (expectedKey in args) {
134
- return args;
200
+ if (expectedKey in aliasMappedArgs) {
201
+ return aliasMappedArgs;
135
202
  }
136
- const scalarMappedArgs = mapSingleFieldScalarArg(args, expectedKey, rawArgsInput);
203
+ const scalarMappedArgs = mapSingleFieldScalarArg(aliasMappedArgs, expectedKey, rawArgsInput);
137
204
  if (expectedKey in scalarMappedArgs) {
138
205
  return scalarMappedArgs;
139
206
  }
@@ -154,67 +221,90 @@ export function normalizeToolArgsForSchema(args, schema, rawArgsInput, options =
154
221
  return genericScalarMappedArgs;
155
222
  }
156
223
  export function extractToolCallsFromResult(result) {
224
+ const capturedToolCalls = readCapturedPromptedJsonToolCalls(result);
225
+ if (capturedToolCalls.length > 0) {
226
+ return [capturedToolCalls[capturedToolCalls.length - 1]];
227
+ }
157
228
  const messages = isRecord(result) && Array.isArray(result.messages) ? result.messages : [];
158
- const lastMessage = messages.at(-1);
159
- if (!isObject(lastMessage)) {
160
- return [];
161
- }
162
- const messageKwargs = isObject(lastMessage.kwargs) ? lastMessage.kwargs : undefined;
163
- const rawToolCalls = Array.isArray(lastMessage.tool_calls)
164
- ? (lastMessage.tool_calls ?? [])
165
- : Array.isArray(messageKwargs?.tool_calls)
166
- ? messageKwargs.tool_calls
167
- : [];
168
- const extracted = rawToolCalls
169
- .map((toolCall) => {
170
- if (!isObject(toolCall)) {
171
- return null;
172
- }
173
- const functionPayload = isObject(toolCall.function) ? toolCall.function : undefined;
174
- const name = typeof toolCall.name === "string"
175
- ? toolCall.name
176
- : typeof functionPayload?.name === "string"
177
- ? functionPayload.name
178
- : null;
179
- if (!name) {
180
- return null;
181
- }
182
- const rawArgsInput = toolCall.args ?? functionPayload?.arguments;
183
- const rawArgs = salvageToolArgs(rawArgsInput) ?? {};
184
- if (!isObject(rawArgs)) {
185
- return null;
186
- }
187
- const id = typeof toolCall.id === "string" ? toolCall.id : undefined;
188
- return { id, name, args: rawArgs, rawArgsInput };
189
- })
190
- .filter((item) => item !== null);
191
- if (extracted.length > 0) {
192
- return extracted;
193
- }
194
- const directRole = typeof lastMessage.role === "string"
195
- ? lastMessage.role.trim().toLowerCase()
196
- : undefined;
197
- const messageType = typeof lastMessage._getType === "function"
198
- ? String(lastMessage._getType())
199
- : undefined;
200
- const constructorType = Array.isArray(lastMessage.id)
201
- ? lastMessage.id.at(-1)
202
- : undefined;
203
- if (directRole === "tool" || messageType === "tool" || constructorType === "ToolMessage") {
204
- return [];
205
- }
206
- const content = typeof lastMessage.content === "string"
207
- ? lastMessage.content
208
- : typeof messageKwargs?.content === "string"
209
- ? messageKwargs.content
210
- : "";
211
- if (!content.trim()) {
212
- return [];
213
- }
214
- return salvageJsonToolCalls(content).map((toolCall, index) => ({
215
- id: `salvaged-json-${index + 1}`,
216
- name: toolCall.name,
217
- args: toolCall.args,
218
- rawArgsInput: content,
219
- }));
229
+ const answeredToolCallIds = new Set();
230
+ for (const message of messages) {
231
+ if (!isObject(message)) {
232
+ continue;
233
+ }
234
+ const id = message.tool_call_id;
235
+ if (typeof id === "string" && id.length > 0) {
236
+ answeredToolCallIds.add(id);
237
+ }
238
+ }
239
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
240
+ const message = messages[index];
241
+ if (!isObject(message)) {
242
+ continue;
243
+ }
244
+ const messageKwargs = isObject(message.kwargs) ? message.kwargs : undefined;
245
+ const rawToolCalls = Array.isArray(message.tool_calls)
246
+ ? (message.tool_calls ?? [])
247
+ : Array.isArray(messageKwargs?.tool_calls)
248
+ ? messageKwargs.tool_calls
249
+ : [];
250
+ const extracted = rawToolCalls
251
+ .map((toolCall) => {
252
+ if (!isObject(toolCall)) {
253
+ return null;
254
+ }
255
+ const functionPayload = isObject(toolCall.function) ? toolCall.function : undefined;
256
+ const name = typeof toolCall.name === "string"
257
+ ? toolCall.name
258
+ : typeof functionPayload?.name === "string"
259
+ ? functionPayload.name
260
+ : null;
261
+ if (!name) {
262
+ return null;
263
+ }
264
+ const rawArgsInput = toolCall.args ?? functionPayload?.arguments;
265
+ const rawArgs = salvageToolArgs(rawArgsInput) ?? {};
266
+ if (!isObject(rawArgs)) {
267
+ return null;
268
+ }
269
+ const id = typeof toolCall.id === "string" ? toolCall.id : undefined;
270
+ if (id && answeredToolCallIds.has(id)) {
271
+ return null;
272
+ }
273
+ return { id, name, args: rawArgs, rawArgsInput };
274
+ })
275
+ .filter((item) => item !== null);
276
+ if (extracted.length > 0) {
277
+ return extracted;
278
+ }
279
+ const directRole = typeof message.role === "string"
280
+ ? message.role.trim().toLowerCase()
281
+ : undefined;
282
+ const messageType = typeof message._getType === "function"
283
+ ? String(message._getType())
284
+ : undefined;
285
+ const constructorType = Array.isArray(message.id)
286
+ ? message.id.at(-1)
287
+ : undefined;
288
+ if (directRole === "tool" || messageType === "tool" || constructorType === "ToolMessage") {
289
+ continue;
290
+ }
291
+ const content = typeof message.content === "string"
292
+ ? message.content
293
+ : typeof messageKwargs?.content === "string"
294
+ ? messageKwargs.content
295
+ : "";
296
+ if (!content.trim()) {
297
+ continue;
298
+ }
299
+ const salvaged = salvageJsonToolCalls(content);
300
+ if (salvaged.length > 0) {
301
+ return salvaged.map((toolCall, salvageIndex) => ({
302
+ id: `salvaged-json-${salvageIndex + 1}`,
303
+ name: toolCall.name,
304
+ args: toolCall.args,
305
+ rawArgsInput: content,
306
+ }));
307
+ }
308
+ }
309
+ return [];
220
310
  }
@@ -12,10 +12,6 @@ export function canReplayToolCallsLocally(binding, toolCalls, primaryTools, tool
12
12
  if (resolvedToolName === "task" || toolCall.name === "task") {
13
13
  return false;
14
14
  }
15
- const executable = executableTools.get(toolCall.name) ?? executableTools.get(resolvedToolName);
16
- if (executable) {
17
- return false;
18
- }
19
15
  const builtinExecutable = builtinExecutableTools.get(toolCall.name) ??
20
16
  builtinExecutableTools.get(resolvedToolName) ??
21
17
  createModelFacingToolNameLookupCandidates(toolCall.name)
@@ -59,6 +59,9 @@ export declare class AgentRuntimeAdapter {
59
59
  state?: Record<string, unknown>;
60
60
  files?: Record<string, unknown>;
61
61
  memoryContext?: string;
62
+ toolRuntimeContext?: Record<string, unknown>;
63
+ suppressInitialRequiredPlanInstruction?: boolean;
64
+ externalPlanEvidence?: boolean;
62
65
  }): Promise<RequestResult>;
63
66
  private tryDelegateWithCompactRouter;
64
67
  private buildCompactDelegationReport;