@botbotgo/agent-harness 0.0.228 → 0.0.229

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.
@@ -1 +1 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.227";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.228";
@@ -1 +1 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.227";
1
+ export const AGENT_HARNESS_VERSION = "0.0.228";
@@ -5,7 +5,7 @@ import path from "node:path";
5
5
  import { stat } from "node:fs/promises";
6
6
  import { readFile } from "node:fs/promises";
7
7
  import { fileURLToPath, pathToFileURL } from "node:url";
8
- import { CompositeBackend, LocalShellBackend, StateBackend, StoreBackend } from "deepagents";
8
+ import { CompositeBackend, LangSmithSandbox, LocalShellBackend, StateBackend, StoreBackend } from "deepagents";
9
9
  import { getBindingBackendConfig, getBindingExecutionView, getBindingPrimaryModel } from "../runtime/support/compiled-binding.js";
10
10
  import { resolveCompiledEmbeddingModelRef } from "../runtime/support/embedding-models.js";
11
11
  import { createRuntimeEnv } from "../runtime/support/runtime-env.js";
@@ -133,7 +133,60 @@ class CompatibleCompositeBackend {
133
133
  return this.composite.downloadFiles(paths);
134
134
  }
135
135
  }
136
- const INLINE_BACKEND_ERROR = 'Unsupported DeepAgent backend kind "%s". Supported inline kinds: LocalShellBackend, VfsSandbox, StateBackend, StoreBackend, CompositeBackend.';
136
+ function omitKind(config) {
137
+ const { kind: _kind, ...rest } = config ?? {};
138
+ return rest;
139
+ }
140
+ class LazyLangSmithSandbox {
141
+ config;
142
+ sandboxPromise = null;
143
+ constructor(config) {
144
+ this.config = config;
145
+ }
146
+ get id() {
147
+ return "langsmith-pending";
148
+ }
149
+ get isRunning() {
150
+ return this.sandboxPromise !== null;
151
+ }
152
+ getSandbox() {
153
+ if (!this.sandboxPromise) {
154
+ this.sandboxPromise = LangSmithSandbox.create(omitKind(this.config));
155
+ }
156
+ return this.sandboxPromise;
157
+ }
158
+ async read(filePath, offset, limit) {
159
+ return (await this.getSandbox()).read(filePath, offset, limit);
160
+ }
161
+ async edit(filePath, oldString, newString, replaceAll) {
162
+ return (await this.getSandbox()).edit(filePath, oldString, newString, replaceAll);
163
+ }
164
+ async ls(dirPath) {
165
+ return (await this.getSandbox()).ls(dirPath);
166
+ }
167
+ async glob(pattern, searchPath) {
168
+ return (await this.getSandbox()).glob(pattern, searchPath);
169
+ }
170
+ async write(filePath, content) {
171
+ return (await this.getSandbox()).write(filePath, content);
172
+ }
173
+ async execute(command) {
174
+ return (await this.getSandbox()).execute(command);
175
+ }
176
+ async uploadFiles(files) {
177
+ return (await this.getSandbox()).uploadFiles(files);
178
+ }
179
+ async downloadFiles(paths) {
180
+ return (await this.getSandbox()).downloadFiles(paths);
181
+ }
182
+ async close() {
183
+ if (!this.sandboxPromise) {
184
+ return;
185
+ }
186
+ await (await this.sandboxPromise).close();
187
+ }
188
+ }
189
+ const INLINE_BACKEND_ERROR = 'Unsupported DeepAgent backend kind "%s". Supported inline kinds: LocalShellBackend, VfsSandbox, StateBackend, StoreBackend, CompositeBackend, LangSmithSandbox.';
137
190
  function unsupportedInlineBackend(kind) {
138
191
  throw new Error(INLINE_BACKEND_ERROR.replace("%s", kind));
139
192
  }
@@ -172,6 +225,8 @@ function createInlineBackendInstance(workspaceRoot, kind, config, runtimeLike) {
172
225
  return new StateBackend(runtimeLike);
173
226
  case "StoreBackend":
174
227
  return new StoreBackend(runtimeLike);
228
+ case "LangSmithSandbox":
229
+ return new LazyLangSmithSandbox(config);
175
230
  default:
176
231
  return unsupportedInlineBackend(kind);
177
232
  }
@@ -193,6 +248,8 @@ function createInlineBackendResolver(workspace) {
193
248
  return createInlineBackendInstance(workspace.workspaceRoot, "StateBackend", backendConfig, runtimeLike);
194
249
  case "StoreBackend":
195
250
  return createInlineBackendInstance(workspace.workspaceRoot, "StoreBackend", backendConfig, runtimeLike);
251
+ case "LangSmithSandbox":
252
+ return createInlineBackendInstance(workspace.workspaceRoot, "LangSmithSandbox", backendConfig, runtimeLike);
196
253
  case "CompositeBackend": {
197
254
  const stateConfig = typeof backendConfig.state === "object" && backendConfig.state
198
255
  ? backendConfig.state
@@ -8,7 +8,28 @@ export type BuiltinMiddlewareBackend = {
8
8
  is_dir?: boolean;
9
9
  size?: number;
10
10
  }>;
11
- read?: (filePath: string, offset?: number, limit?: number) => Promise<string> | string;
11
+ ls?: (path: string) => Promise<{
12
+ files?: Array<{
13
+ path: string;
14
+ isDir?: boolean;
15
+ size?: number;
16
+ }>;
17
+ error?: string;
18
+ }> | {
19
+ files?: Array<{
20
+ path: string;
21
+ isDir?: boolean;
22
+ size?: number;
23
+ }>;
24
+ error?: string;
25
+ };
26
+ read?: (filePath: string, offset?: number, limit?: number) => Promise<string | {
27
+ content?: string | Uint8Array;
28
+ error?: string;
29
+ }> | string | {
30
+ content?: string | Uint8Array;
31
+ error?: string;
32
+ };
12
33
  write?: (filePath: string, content: string) => Promise<{
13
34
  error?: string;
14
35
  path?: string;
@@ -34,6 +55,17 @@ export type BuiltinMiddlewareBackend = {
34
55
  }>> | Array<{
35
56
  path: string;
36
57
  }>;
58
+ glob?: (pattern: string, path?: string) => Promise<{
59
+ files?: Array<{
60
+ path: string;
61
+ }>;
62
+ error?: string;
63
+ }> | {
64
+ files?: Array<{
65
+ path: string;
66
+ }>;
67
+ error?: string;
68
+ };
37
69
  grepRaw?: (pattern: string, path?: string | null, glob?: string | null) => Promise<Array<{
38
70
  path: string;
39
71
  line: number;
@@ -43,6 +75,27 @@ export type BuiltinMiddlewareBackend = {
43
75
  line: number;
44
76
  text: string;
45
77
  }> | string;
78
+ grep?: (pattern: string, path?: string | null, glob?: string | null) => Promise<{
79
+ matches?: Array<{
80
+ path: string;
81
+ lineNumber?: number;
82
+ line_number?: number;
83
+ content?: string;
84
+ line?: number;
85
+ text?: string;
86
+ }>;
87
+ error?: string;
88
+ }> | {
89
+ matches?: Array<{
90
+ path: string;
91
+ lineNumber?: number;
92
+ line_number?: number;
93
+ content?: string;
94
+ line?: number;
95
+ text?: string;
96
+ }>;
97
+ error?: string;
98
+ };
46
99
  execute?: (command: string) => Promise<{
47
100
  output: string;
48
101
  exitCode: number | null;
@@ -2,6 +2,15 @@ import { z } from "zod";
2
2
  import { isSandboxBackend } from "deepagents";
3
3
  import { isRecord } from "../../../utils/object.js";
4
4
  import { summarizeBuiltinWriteTodosArgs, truncateLines } from "../runtime-adapter-support.js";
5
+ function toDisplayContent(content) {
6
+ if (typeof content === "string") {
7
+ return content;
8
+ }
9
+ if (content instanceof Uint8Array) {
10
+ return new TextDecoder().decode(content);
11
+ }
12
+ return "";
13
+ }
5
14
  export async function createBuiltinMiddlewareTools(backend, options) {
6
15
  const tools = new Map();
7
16
  tools.set("write_todos", {
@@ -25,7 +34,14 @@ export async function createBuiltinMiddlewareTools(backend, options) {
25
34
  schema: z.object({ path: z.string().optional().default("/") }).passthrough(),
26
35
  invoke: async (input) => {
27
36
  const targetPath = isRecord(input) && typeof input.path === "string" ? input.path : "/";
28
- const infos = (await Promise.resolve(backend.lsInfo?.(targetPath))) ?? [];
37
+ const legacyInfos = (await Promise.resolve(backend.lsInfo?.(targetPath))) ?? [];
38
+ const infos = legacyInfos.length > 0
39
+ ? legacyInfos
40
+ : ((await Promise.resolve(backend.ls?.(targetPath)))?.files ?? []).map((info) => ({
41
+ path: info.path,
42
+ is_dir: info.isDir,
43
+ size: info.size,
44
+ }));
29
45
  if (infos.length === 0) {
30
46
  return `No files found in ${targetPath}`;
31
47
  }
@@ -44,7 +60,11 @@ export async function createBuiltinMiddlewareTools(backend, options) {
44
60
  const filePath = typeof typed.file_path === "string" ? typed.file_path : "";
45
61
  const offset = typeof typed.offset === "number" ? typed.offset : 0;
46
62
  const limit = typeof typed.limit === "number" ? typed.limit : 500;
47
- return Promise.resolve(backend.read?.(filePath, offset, limit)) ?? "";
63
+ const result = await Promise.resolve(backend.read?.(filePath, offset, limit));
64
+ if (typeof result === "string") {
65
+ return result;
66
+ }
67
+ return toDisplayContent(result?.content);
48
68
  },
49
69
  });
50
70
  tools.set("write_file", {
@@ -77,7 +97,10 @@ export async function createBuiltinMiddlewareTools(backend, options) {
77
97
  const typed = isRecord(input) ? input : {};
78
98
  const pattern = typeof typed.pattern === "string" ? typed.pattern : "";
79
99
  const targetPath = typeof typed.path === "string" ? typed.path : "/";
80
- const infos = (await Promise.resolve(backend.globInfo?.(pattern, targetPath))) ?? [];
100
+ const legacyInfos = (await Promise.resolve(backend.globInfo?.(pattern, targetPath))) ?? [];
101
+ const infos = legacyInfos.length > 0
102
+ ? legacyInfos
103
+ : ((await Promise.resolve(backend.glob?.(pattern, targetPath)))?.files ?? []);
81
104
  if (infos.length === 0) {
82
105
  return `No files found matching pattern '${pattern}'`;
83
106
  }
@@ -93,16 +116,23 @@ export async function createBuiltinMiddlewareTools(backend, options) {
93
116
  }).passthrough(),
94
117
  invoke: async (input) => {
95
118
  const typed = isRecord(input) ? input : {};
96
- const result = await Promise.resolve(backend.grepRaw?.(typeof typed.pattern === "string" ? typed.pattern : "", typeof typed.path === "string" ? typed.path : "/", typeof typed.glob === "string" ? typed.glob : null));
97
- if (typeof result === "string") {
98
- return result;
119
+ const legacyResult = await Promise.resolve(backend.grepRaw?.(typeof typed.pattern === "string" ? typed.pattern : "", typeof typed.path === "string" ? typed.path : "/", typeof typed.glob === "string" ? typed.glob : null));
120
+ const structuredResult = await Promise.resolve(backend.grep?.(typeof typed.pattern === "string" ? typed.pattern : "", typeof typed.path === "string" ? typed.path : "/", typeof typed.glob === "string" ? typed.glob : null));
121
+ const normalizedStructuredMatches = structuredResult?.matches?.map((match) => ({
122
+ path: match.path,
123
+ line: match.lineNumber ?? match.line_number ?? match.line ?? 0,
124
+ text: match.content ?? match.text ?? "",
125
+ }));
126
+ const normalizedResult = legacyResult ?? normalizedStructuredMatches;
127
+ if (typeof normalizedResult === "string") {
128
+ return normalizedResult;
99
129
  }
100
- if (!result || result.length === 0) {
130
+ if (!normalizedResult || normalizedResult.length === 0) {
101
131
  return `No matches found for pattern '${typeof typed.pattern === "string" ? typed.pattern : ""}'`;
102
132
  }
103
133
  const lines = [];
104
134
  let currentFile = "";
105
- for (const match of result) {
135
+ for (const match of normalizedResult) {
106
136
  if (match.path !== currentFile) {
107
137
  currentFile = match.path;
108
138
  lines.push(`\n${currentFile}:`);
@@ -1,5 +1,5 @@
1
1
  import { anthropicPromptCachingMiddleware, contextEditingMiddleware, dynamicSystemPromptMiddleware, humanInTheLoopMiddleware, llmToolSelectorMiddleware, modelCallLimitMiddleware, modelFallbackMiddleware, modelRetryMiddleware, openAIModerationMiddleware, piiMiddleware, piiRedactionMiddleware, summarizationMiddleware, todoListMiddleware, toolCallLimitMiddleware, toolEmulatorMiddleware, toolRetryMiddleware, } from "langchain";
2
- import { createFilesystemMiddleware, createPatchToolCallsMiddleware, createSummarizationMiddleware as createDeepAgentSummarizationMiddleware, } from "deepagents";
2
+ import { createCompletionCallbackMiddleware, createFilesystemMiddleware, createPatchToolCallsMiddleware, createSummarizationMiddleware as createDeepAgentSummarizationMiddleware, } from "deepagents";
3
3
  function asMiddlewareConfig(value) {
4
4
  return typeof value === "object" && value !== null && !Array.isArray(value) ? { ...value } : null;
5
5
  }
@@ -84,6 +84,7 @@ async function createFilesystemDeclarativeMiddleware({ config, resolveFilesystem
84
84
  return createFilesystemMiddleware(runtimeConfig);
85
85
  }
86
86
  export const DECLARATIVE_MIDDLEWARE_REGISTRY = {
87
+ completionCallback: async ({ config }) => createCompletionCallbackMiddleware(config),
87
88
  filesystem: createFilesystemDeclarativeMiddleware,
88
89
  patchToolCalls: async () => createPatchToolCallsMiddleware(),
89
90
  summarization: createBindingAwareSummarizationMiddleware,
@@ -39,6 +39,9 @@ function validateMiddlewareConfig(agent) {
39
39
  if (kind === "humanInTheLoop" && typeof typed.interruptOn !== "object") {
40
40
  throw new Error(`Agent ${agent.id} humanInTheLoop middleware requires interruptOn`);
41
41
  }
42
+ if (kind === "completionCallback" && typeof typed.callbackGraphId !== "string") {
43
+ throw new Error(`Agent ${agent.id} completionCallback middleware requires callbackGraphId`);
44
+ }
42
45
  if (kind === "filesystem" && agent.executionMode === "deepagent") {
43
46
  throw new Error(`Agent ${agent.id} cannot use filesystem middleware with DeepAgents; configure filesystem behavior through the deepagent backend instead`);
44
47
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.228",
3
+ "version": "0.0.229",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "license": "MIT",
6
6
  "type": "module",