@botbotgo/agent-harness 0.0.311 → 0.0.313

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.310";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.312";
@@ -1 +1 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.310";
1
+ export const AGENT_HARNESS_VERSION = "0.0.312";
@@ -100,6 +100,45 @@ export function truncateLines(lines, maxChars = 12000) {
100
100
  return truncateText(lines.join("\n"), maxChars);
101
101
  }
102
102
 
103
+ export function shellQuote(value) {
104
+ return `'${String(value).replace(/'/g, `'\"'\"'`)}'`;
105
+ }
106
+
107
+ export async function tryExecuteShallowDirectoryListing(backend, targetPath) {
108
+ if (typeof backend?.execute !== "function" || process.platform === "win32") {
109
+ return undefined;
110
+ }
111
+
112
+ const quotedPath = shellQuote(targetPath);
113
+ const command = [
114
+ "set -f",
115
+ `dir=${quotedPath}`,
116
+ 'if [ ! -d "$dir" ]; then exit 0; fi',
117
+ "count=0",
118
+ 'for entry in "$dir"/* "$dir"/.[!.]* "$dir"/..?*; do',
119
+ ' [ -e "$entry" ] || continue',
120
+ ' if [ -d "$entry" ]; then',
121
+ ' printf \'%s (directory)\\n\' "$entry"',
122
+ " else",
123
+ ' printf \'%s\\n\' "$entry"',
124
+ " fi",
125
+ " count=$((count + 1))",
126
+ ' if [ "$count" -ge 400 ]; then',
127
+ " printf '...[truncated]\\n'",
128
+ " break",
129
+ " fi",
130
+ "done",
131
+ ].join("; ");
132
+
133
+ try {
134
+ const result = await backend.execute(command);
135
+ const output = typeof result?.output === "string" ? result.output.trim() : "";
136
+ return output.length > 0 ? output : undefined;
137
+ } catch {
138
+ return undefined;
139
+ }
140
+ }
141
+
103
142
  export function formatHttpResponse(result) {
104
143
  if (result?.error) {
105
144
  return result.error;
@@ -1,5 +1,5 @@
1
1
  import { tool } from "@botbotgo/agent-harness/tools";
2
- import { defineSchema, formatListEntries, getBackend, normalizeWorkspacePath, optionalString, truncateLines } from "./_runtime_tool_helpers.mjs";
2
+ import { defineSchema, formatListEntries, getBackend, normalizeWorkspacePath, optionalString, tryExecuteShallowDirectoryListing } from "./_runtime_tool_helpers.mjs";
3
3
 
4
4
  export const list_files = tool({
5
5
  description: "List files in a workspace directory.",
@@ -9,6 +9,10 @@ export const list_files = tool({
9
9
  async invoke(input, context = {}) {
10
10
  const backend = getBackend(context);
11
11
  const targetPath = normalizeWorkspacePath(context, input.path, ".");
12
+ const shallowListing = await tryExecuteShallowDirectoryListing(backend, targetPath);
13
+ if (typeof shallowListing === "string") {
14
+ return shallowListing;
15
+ }
12
16
  const legacyInfos = typeof backend.lsInfo === "function" ? await backend.lsInfo(targetPath) : [];
13
17
  const infos = legacyInfos.length > 0
14
18
  ? legacyInfos
@@ -36,6 +36,45 @@ function toDisplayContent(content) {
36
36
  function notAvailable(toolName, capability) {
37
37
  return `Error: ${toolName} is not available. This backend does not support ${capability}.`;
38
38
  }
39
+ function shellQuote(value) {
40
+ return `'${value.replace(/'/g, `'\"'\"'`)}'`;
41
+ }
42
+ async function tryExecuteShallowDirectoryListing(backend, targetPath) {
43
+ if (typeof backend.execute !== "function" || process.platform === "win32") {
44
+ return undefined;
45
+ }
46
+ const quotedPath = shellQuote(targetPath);
47
+ const command = [
48
+ "set -f",
49
+ `dir=${quotedPath}`,
50
+ 'if [ ! -d "$dir" ]; then exit 0; fi',
51
+ "count=0",
52
+ 'for entry in "$dir"/* "$dir"/.[!.]* "$dir"/..?*; do',
53
+ ' [ -e "$entry" ] || continue',
54
+ ' if [ -d "$entry" ]; then',
55
+ ' printf \'%s (directory)\\n\' \"$entry\"',
56
+ " else",
57
+ ' printf \'%s\\n\' \"$entry\"',
58
+ " fi",
59
+ " count=$((count + 1))",
60
+ ' if [ "$count" -ge 400 ]; then',
61
+ " printf '...[truncated]\\n'",
62
+ " break",
63
+ " fi",
64
+ "done",
65
+ ].join("; ");
66
+ try {
67
+ const result = await Promise.resolve(backend.execute(command));
68
+ const output = typeof result?.output === "string" ? result.output.trim() : "";
69
+ if (!output) {
70
+ return undefined;
71
+ }
72
+ return output;
73
+ }
74
+ catch {
75
+ return undefined;
76
+ }
77
+ }
39
78
  function resolveWorkspaceRoot(backend) {
40
79
  const typed = backend;
41
80
  if (typeof typed.rootDir === "string" && typed.rootDir.trim().length > 0) {
@@ -163,6 +202,10 @@ export async function createBuiltinMiddlewareTools(backend, options) {
163
202
  schema: z.object({ path: z.string().optional().default("/") }).passthrough(),
164
203
  invoke: async (input) => {
165
204
  const targetPath = normalizeWorkspacePathOrThrow(pathScopedBackend, isRecord(input) && typeof input.path === "string" ? input.path : "/");
205
+ const shallowListing = await tryExecuteShallowDirectoryListing(pathScopedBackend, targetPath);
206
+ if (typeof shallowListing === "string") {
207
+ return shallowListing.length > 0 ? shallowListing : `No files found in ${targetPath}`;
208
+ }
166
209
  const legacyInfos = (await Promise.resolve(backend.lsInfo?.(targetPath))) ?? [];
167
210
  const infos = legacyInfos.length > 0
168
211
  ? legacyInfos
@@ -1,4 +1,5 @@
1
- import { readFileSync } from "node:fs";
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { fileURLToPath } from "node:url";
2
3
  const bundledTextCache = new Map();
3
4
  export function readBundledText(relativePath) {
4
5
  const normalized = relativePath.replace(/^\.?\//, "");
@@ -6,10 +7,23 @@ export function readBundledText(relativePath) {
6
7
  if (cached !== undefined) {
7
8
  return cached;
8
9
  }
9
- const value = readFileSync(new URL(`../../resources/${normalized}`, import.meta.url), "utf8");
10
+ const resourceUrl = resolveBundledResourceUrl(normalized);
11
+ const value = readFileSync(resourceUrl, "utf8");
10
12
  bundledTextCache.set(normalized, value);
11
13
  return value;
12
14
  }
15
+ function resolveBundledResourceUrl(relativePath) {
16
+ const candidates = [
17
+ new URL(`../resources/${relativePath}`, import.meta.url),
18
+ new URL(`../../resources/${relativePath}`, import.meta.url),
19
+ ];
20
+ for (const candidate of candidates) {
21
+ if (existsSync(fileURLToPath(candidate))) {
22
+ return candidate;
23
+ }
24
+ }
25
+ return candidates[0];
26
+ }
13
27
  export function renderTemplateText(template, values) {
14
28
  return template
15
29
  .replace(/\{\{([a-zA-Z0-9_]+)\}\}/g, (_match, key) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.311",
3
+ "version": "0.0.313",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "license": "MIT",
6
6
  "type": "module",