@botbotgo/agent-harness 0.0.1 → 0.0.3

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.
@@ -14,6 +14,7 @@ export declare class AgentRuntimeAdapter {
14
14
  private applyStrictToolJsonInstruction;
15
15
  private synthesizeDeepAgentAnswer;
16
16
  private resolveModel;
17
+ private buildToolNameMapping;
17
18
  private resolveTools;
18
19
  private normalizeInterruptPolicy;
19
20
  private compileInterruptOn;
@@ -9,6 +9,7 @@ import { extractReasoningText, extractToolFallbackContext, extractVisibleOutput,
9
9
  import { extractAgentStep, extractInterruptPayload, extractReasoningStreamOutput, extractTerminalStreamOutput, extractToolResult, normalizeTerminalOutputKey, readStreamDelta, } from "./parsing/stream-event-parsing.js";
10
10
  import { wrapToolForExecution } from "./tool-hitl.js";
11
11
  const AGENT_INTERRUPT_SENTINEL_PREFIX = "__agent_harness_interrupt__:";
12
+ const MODEL_SAFE_TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
12
13
  function asObject(value) {
13
14
  return typeof value === "object" && value ? value : undefined;
14
15
  }
@@ -25,6 +26,56 @@ function normalizeOpenAICompatibleInit(init) {
25
26
  delete normalized.baseUrl;
26
27
  return normalized;
27
28
  }
29
+ function sanitizeToolNameForModel(name) {
30
+ const sanitized = name
31
+ .replaceAll(".", "__")
32
+ .replace(/[^a-zA-Z0-9_-]+/g, "_")
33
+ .replace(/^_+|_+$/g, "");
34
+ return sanitized || "tool";
35
+ }
36
+ function buildToolNameMapping(tools) {
37
+ const originalToModelFacing = new Map();
38
+ const modelFacingToOriginal = new Map();
39
+ const seen = new Set();
40
+ for (const tool of tools) {
41
+ const originalName = tool.name;
42
+ const baseName = MODEL_SAFE_TOOL_NAME_PATTERN.test(originalName) ? originalName : sanitizeToolNameForModel(originalName);
43
+ let candidate = baseName;
44
+ let suffix = 2;
45
+ while (seen.has(candidate)) {
46
+ candidate = `${baseName}_${suffix}`;
47
+ suffix += 1;
48
+ }
49
+ seen.add(candidate);
50
+ originalToModelFacing.set(originalName, candidate);
51
+ modelFacingToOriginal.set(candidate, originalName);
52
+ }
53
+ return { originalToModelFacing, modelFacingToOriginal };
54
+ }
55
+ function wrapResolvedToolWithModelFacingName(resolvedTool, modelFacingName) {
56
+ if (typeof resolvedTool !== "object" || resolvedTool === null) {
57
+ return resolvedTool;
58
+ }
59
+ return new Proxy(resolvedTool, {
60
+ get(target, prop, receiver) {
61
+ if (prop === "name") {
62
+ return modelFacingName;
63
+ }
64
+ return Reflect.get(target, prop, receiver);
65
+ },
66
+ getOwnPropertyDescriptor(target, prop) {
67
+ if (prop === "name") {
68
+ return {
69
+ configurable: true,
70
+ enumerable: true,
71
+ writable: false,
72
+ value: modelFacingName,
73
+ };
74
+ }
75
+ return Reflect.getOwnPropertyDescriptor(target, prop);
76
+ },
77
+ });
78
+ }
28
79
  export class AgentRuntimeAdapter {
29
80
  options;
30
81
  constructor(options = {}) {
@@ -143,11 +194,20 @@ export class AgentRuntimeAdapter {
143
194
  }
144
195
  return wrapResolvedModel(await initChatModel(model.model, { modelProvider: model.provider, ...model.init }));
145
196
  }
197
+ buildToolNameMapping(tools) {
198
+ return buildToolNameMapping(tools);
199
+ }
146
200
  resolveTools(tools, binding) {
147
201
  const resolved = this.options.toolResolver ? this.options.toolResolver(tools.map((tool) => tool.id), binding) : [];
202
+ const toolNameMapping = this.buildToolNameMapping(tools);
148
203
  return resolved.map((resolvedTool, index) => {
149
204
  const compiledTool = tools[index];
150
- return compiledTool ? wrapToolForExecution(resolvedTool, compiledTool) : resolvedTool;
205
+ if (!compiledTool) {
206
+ return resolvedTool;
207
+ }
208
+ const wrappedTool = wrapToolForExecution(resolvedTool, compiledTool);
209
+ const modelFacingName = toolNameMapping.originalToModelFacing.get(compiledTool.name) ?? compiledTool.name;
210
+ return modelFacingName === compiledTool.name ? wrappedTool : wrapResolvedToolWithModelFacingName(wrappedTool, modelFacingName);
151
211
  });
152
212
  }
153
213
  normalizeInterruptPolicy(rule) {
@@ -168,15 +228,17 @@ export class AgentRuntimeAdapter {
168
228
  decisions.push("reject");
169
229
  return decisions.length > 0 ? decisions : null;
170
230
  }
171
- compileInterruptOn(_tools, compatibilityRules) {
231
+ compileInterruptOn(tools, compatibilityRules) {
232
+ const toolNameMapping = this.buildToolNameMapping(tools);
172
233
  const compiled = new Map();
173
234
  for (const [toolName, rule] of Object.entries(compatibilityRules ?? {})) {
235
+ const modelFacingName = toolNameMapping.originalToModelFacing.get(toolName) ?? toolName;
174
236
  const allowedDecisions = this.normalizeInterruptPolicy(rule);
175
237
  if (!allowedDecisions) {
176
- compiled.delete(toolName);
238
+ compiled.delete(modelFacingName);
177
239
  }
178
240
  else {
179
- compiled.set(toolName, { allowedDecisions });
241
+ compiled.set(modelFacingName, { allowedDecisions });
180
242
  }
181
243
  }
182
244
  return compiled.size > 0 ? Object.fromEntries(compiled.entries()) : undefined;
@@ -1,4 +1,5 @@
1
1
  import type { RuntimeAdapterOptions, WorkspaceBundle } from "../contracts/types.js";
2
+ export declare function resolveLocalBuiltinsEntry(currentVendorDir: string, resolveInstalledEntry?: () => string | null): string;
2
3
  type BuiltinToolInfo = {
3
4
  builtinPath: string;
4
5
  backendOperation: string;
@@ -1,18 +1,33 @@
1
1
  import { existsSync } from "node:fs";
2
+ import { createRequire } from "node:module";
2
3
  import path from "node:path";
3
4
  import { readFile } from "node:fs/promises";
4
5
  import { fileURLToPath, pathToFileURL } from "node:url";
5
6
  import { ensureExternalSource, isExternalSourceLocator, parseExternalSourceLocator } from "./sources.js";
6
7
  const vendorDir = path.dirname(fileURLToPath(import.meta.url));
7
- const builtinsDist = path.resolve(vendorDir, "../../../builtins/dist/src/index.js");
8
- const builtinsSource = path.resolve(vendorDir, "../../../builtins/src/index.ts");
9
- function preferSourceWhenAvailable(distPath, sourcePath) {
10
- if (!existsSync(sourcePath)) {
11
- return distPath;
8
+ const require = createRequire(import.meta.url);
9
+ function installedBuiltinsEntry() {
10
+ try {
11
+ return require.resolve("@botbotgo/agent-harness-builtin");
12
12
  }
13
- return sourcePath;
13
+ catch {
14
+ return null;
15
+ }
16
+ }
17
+ export function resolveLocalBuiltinsEntry(currentVendorDir, resolveInstalledEntry = installedBuiltinsEntry) {
18
+ const candidates = [
19
+ path.resolve(currentVendorDir, "../../../builtins/dist/src/index.js"),
20
+ resolveInstalledEntry(),
21
+ path.resolve(currentVendorDir, "../../../builtins/src/index.ts"),
22
+ ].filter((candidate) => typeof candidate === "string" && candidate.length > 0);
23
+ for (const candidate of candidates) {
24
+ if (existsSync(candidate)) {
25
+ return candidate;
26
+ }
27
+ }
28
+ return candidates.at(-1) ?? path.resolve(currentVendorDir, "../../../builtins/src/index.ts");
14
29
  }
15
- const builtinsEntry = preferSourceWhenAvailable(builtinsDist, builtinsSource);
30
+ const builtinsEntry = resolveLocalBuiltinsEntry(vendorDir);
16
31
  const localBuiltins = await import(pathToFileURL(builtinsEntry).href);
17
32
  const remoteProviderCache = new Map();
18
33
  function resolvePackageEntry(packageRoot, pkg) {
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Agent Harness framework package",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
7
8
  "types": "./dist/index.d.ts",
8
9
  "files": [
9
10
  "dist"
@@ -19,11 +20,12 @@
19
20
  "exports": {
20
21
  ".": {
21
22
  "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.js",
22
24
  "default": "./dist/index.js"
23
25
  }
24
26
  },
25
27
  "dependencies": {
26
- "@botbotgo/agent-harness-builtin": "workspace:*"
28
+ "@botbotgo/agent-harness-builtin": "0.0.3"
27
29
  },
28
30
  "scripts": {
29
31
  "build": "rm -rf dist tsconfig.tsbuildinfo && tsc -p tsconfig.json && cp -R config dist/",