@botbotgo/agent-harness 0.0.34 → 0.0.36

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.
@@ -174,6 +174,7 @@ export type CompiledAgentBinding = {
174
174
  deepAgentParams?: DeepAgentParams;
175
175
  harnessRuntime: {
176
176
  runRoot: string;
177
+ workspaceRoot?: string;
177
178
  hostFacing: boolean;
178
179
  checkpointer?: Record<string, unknown>;
179
180
  store?: Record<string, unknown>;
@@ -1 +1 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.33";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.35";
@@ -1 +1 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.33";
1
+ export const AGENT_HARNESS_VERSION = "0.0.35";
@@ -367,7 +367,7 @@ export class AgentRuntimeAdapter {
367
367
  if (!compiledTool) {
368
368
  return resolvedTool;
369
369
  }
370
- const wrappedTool = wrapToolForExecution(resolvedTool, compiledTool);
370
+ const wrappedTool = wrapToolForExecution(resolvedTool, compiledTool, binding);
371
371
  const modelFacingName = toolNameMapping.originalToModelFacing.get(compiledTool.name) ?? compiledTool.name;
372
372
  const structuredTool = asStructuredExecutableTool(wrappedTool, modelFacingName, compiledTool.description);
373
373
  if (structuredTool !== wrappedTool) {
@@ -1,5 +1,6 @@
1
1
  import type { CompiledTool } from "../contracts/types.js";
2
+ import type { CompiledAgentBinding } from "../contracts/types.js";
2
3
  type InterruptFn = <I = unknown, R = unknown>(value: I) => R;
3
4
  export declare function wrapToolForHumanInTheLoop<T>(resolvedTool: T, compiledTool: CompiledTool, interruptFn?: InterruptFn): T;
4
- export declare function wrapToolForExecution<T>(resolvedTool: T, compiledTool: CompiledTool, interruptFn?: InterruptFn): T;
5
+ export declare function wrapToolForExecution<T>(resolvedTool: T, compiledTool: CompiledTool, binding?: CompiledAgentBinding, interruptFn?: InterruptFn): T;
5
6
  export {};
@@ -1,5 +1,8 @@
1
1
  import { interrupt } from "@langchain/langgraph";
2
+ import path from "node:path";
2
3
  const DEDUPED_REMOTE_TOOL_NAMES = new Set(["builtin.fetch_url", "builtin.web_search"]);
4
+ const PATH_LIKE_INPUT_KEYS = new Set(["path", "root", "dir", "directory", "cwd"]);
5
+ const ROOT_SCOPING_INPUT_KEYS = new Set(["root", "dir", "directory", "cwd"]);
3
6
  function toInputPreview(input) {
4
7
  if (typeof input === "object" && input && !Array.isArray(input)) {
5
8
  return input;
@@ -103,6 +106,56 @@ function wrapToolWithDedupe(resolvedTool, compiledTool) {
103
106
  },
104
107
  });
105
108
  }
106
- export function wrapToolForExecution(resolvedTool, compiledTool, interruptFn = interrupt) {
107
- return wrapToolWithDedupe(wrapToolForHumanInTheLoop(resolvedTool, compiledTool, interruptFn), compiledTool);
109
+ function shouldConstrainWorkspacePaths(compiledTool) {
110
+ const normalized = `${compiledTool.name} ${compiledTool.description}`.toLowerCase();
111
+ if (/(web[_ .-]?search|fetch[_ .-]?url|https?:\/\/|\burl\b)/.test(normalized)) {
112
+ return false;
113
+ }
114
+ return /(workspace|repo|repository|sandbox|filesystem|file|directory|folder|path|memory|source[- ]control|code search|context search|rag|index|grep|glob|read|write|edit)/.test(normalized);
115
+ }
116
+ function guardWorkspaceBoundToolInput(input, compiledTool, binding) {
117
+ const workspaceRoot = binding?.harnessRuntime.workspaceRoot;
118
+ if (!workspaceRoot || typeof input !== "object" || !input || Array.isArray(input) || !shouldConstrainWorkspacePaths(compiledTool)) {
119
+ return input;
120
+ }
121
+ const record = { ...input };
122
+ for (const [key, value] of Object.entries(record)) {
123
+ if (!PATH_LIKE_INPUT_KEYS.has(key) || typeof value !== "string") {
124
+ continue;
125
+ }
126
+ const candidate = value.trim();
127
+ if (!candidate || /^[a-z]+:\/\//i.test(candidate)) {
128
+ continue;
129
+ }
130
+ const absolute = path.resolve(workspaceRoot, candidate);
131
+ const relative = path.relative(workspaceRoot, absolute);
132
+ if (relative.startsWith("..") || path.isAbsolute(relative)) {
133
+ if (ROOT_SCOPING_INPUT_KEYS.has(key)) {
134
+ record[key] = workspaceRoot;
135
+ continue;
136
+ }
137
+ throw new Error(`Tool ${compiledTool.name} input ${key} resolves outside the workspace root`);
138
+ }
139
+ }
140
+ return record;
141
+ }
142
+ export function wrapToolForExecution(resolvedTool, compiledTool, binding, interruptFn = interrupt) {
143
+ const guardedTool = typeof resolvedTool === "object" && resolvedTool !== null
144
+ ? new Proxy(resolvedTool, {
145
+ get(obj, prop, receiver) {
146
+ const value = Reflect.get(obj, prop, receiver);
147
+ if (prop === "invoke" && typeof value === "function") {
148
+ return (input, config) => obj.invoke(guardWorkspaceBoundToolInput(input, compiledTool, binding), config);
149
+ }
150
+ if (prop === "call" && typeof value === "function") {
151
+ return (input, config) => obj.call(guardWorkspaceBoundToolInput(input, compiledTool, binding), config);
152
+ }
153
+ if (prop === "func" && typeof value === "function") {
154
+ return (input, config) => obj.func(guardWorkspaceBoundToolInput(input, compiledTool, binding), config);
155
+ }
156
+ return value;
157
+ },
158
+ })
159
+ : resolvedTool;
160
+ return wrapToolWithDedupe(wrapToolForHumanInTheLoop(guardedTool, compiledTool, interruptFn), compiledTool);
108
161
  }
@@ -154,6 +154,7 @@ export function compileBinding(workspaceRoot, agent, agents, referencedSubagentI
154
154
  agent,
155
155
  harnessRuntime: {
156
156
  runRoot,
157
+ workspaceRoot,
157
158
  hostFacing: !internalSubagent,
158
159
  ...(checkpointer ? { checkpointer: checkpointer.config } : {}),
159
160
  ...(store ? { store: store.config } : {}),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.34",
3
+ "version": "0.0.36",
4
4
  "description": "Agent Harness framework package",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.2",