@agent-api/sdk 1.3.0 → 1.3.2

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog — @agent-api/sdk
2
2
 
3
+ ## 1.3.2
4
+
5
+ ### Added
6
+
7
+ - Added volume image asset helpers for normalizing private volume paths and checking supported image paths/content types.
8
+
9
+ ## 1.3.1
10
+
11
+ ### Fixed
12
+
13
+ - Made `local_workdir` grep behave like familiar grep: `path` may be omitted, a file path, or a directory subtree.
14
+ - Model-facing `local_workdir` execution errors now return structured `{ ok: false, error }` tool results instead of aborting agent runs.
15
+
3
16
  ## 1.3.0
4
17
 
5
18
  ### Added
package/dist/index.d.ts CHANGED
@@ -6,5 +6,6 @@ export * from "./types/index.js";
6
6
  export { functionCallOutputInput, pendingFunctionCalls, runLocalFunctionHandlers, } from "./local-functions.js";
7
7
  export type { LocalFunctionHandler, LocalFunctionHandlers } from "./local-functions.js";
8
8
  export { mergeTools, publicToolToRequestTool, resolvePresetTools, resolvePresetToolsFromCatalog, } from "./preset-tools.js";
9
+ export { isSupportedVolumeImageContentType, isSupportedVolumeImagePath, normalizeVolumeAssetPath, } from "./volume-assets.js";
9
10
  export type { PresetToolCatalogClient, ResolvePresetToolsOptions, ResolvePresetToolsResult, UnknownPresetToolBehavior, } from "./preset-tools.js";
10
11
  export { AuthResource, DeviceAuthFlowError, browserAuthSessionExpiresWithin } from "./resources/auth.js";
package/dist/index.js CHANGED
@@ -4,4 +4,5 @@ export { APIError, APIConnectionError, APIStatusError, AuthenticationError, BadR
4
4
  export * from "./types/index.js";
5
5
  export { functionCallOutputInput, pendingFunctionCalls, runLocalFunctionHandlers, } from "./local-functions.js";
6
6
  export { mergeTools, publicToolToRequestTool, resolvePresetTools, resolvePresetToolsFromCatalog, } from "./preset-tools.js";
7
+ export { isSupportedVolumeImageContentType, isSupportedVolumeImagePath, normalizeVolumeAssetPath, } from "./volume-assets.js";
7
8
  export { AuthResource, DeviceAuthFlowError, browserAuthSessionExpiresWithin } from "./resources/auth.js";
@@ -309,6 +309,7 @@ export declare class LocalFileStore {
309
309
  readLines(relativePath: string, params: LocalReadLinesParams): Promise<LocalFileLines>;
310
310
  patchLines(relativePath: string, params: LocalPatchLinesParams): Promise<LocalFileLinesPatch>;
311
311
  grep(params: LocalGrepParams): Promise<LocalGrepResponse>;
312
+ private grepCandidates;
312
313
  summarize(params?: LocalSummarizeParams): Promise<LocalSummary>;
313
314
  private walk;
314
315
  }
@@ -310,7 +310,7 @@ export class LocalFileStore {
310
310
  const maxFiles = positiveInt(params.maxFiles, 500);
311
311
  const maxBytesPerFile = positiveInt(params.maxBytesPerFile, 512 * 1024);
312
312
  const maxLineLength = positiveInt(params.maxLineLength, 500);
313
- const stats = await this.list(params.path ?? ".", { recursive: true, ignore: params.ignore });
313
+ const stats = await this.grepCandidates(params.path ?? ".", params.ignore);
314
314
  const matches = [];
315
315
  let filesScanned = 0;
316
316
  let scanTruncated = false;
@@ -348,6 +348,27 @@ export class LocalFileStore {
348
348
  }
349
349
  return { object: "list", matches, files_scanned: filesScanned, scan_truncated: scanTruncated };
350
350
  }
351
+ async grepCandidates(relativePath, ignore) {
352
+ const fullPath = this.resolvePath(relativePath);
353
+ const info = await stat(fullPath);
354
+ const portablePath = toPortablePath(path.relative(this.root, fullPath)) || ".";
355
+ if (ignored(portablePath, ignore)) {
356
+ return [];
357
+ }
358
+ if (info.isFile()) {
359
+ return [{
360
+ path: portablePath,
361
+ fullPath,
362
+ type: "file",
363
+ size: info.size,
364
+ modifiedAt: info.mtime,
365
+ }];
366
+ }
367
+ if (info.isDirectory()) {
368
+ return await this.list(relativePath, { recursive: true, ignore });
369
+ }
370
+ return [];
371
+ }
351
372
  async summarize(params = {}) {
352
373
  const maxFiles = positiveInt(params.maxFiles, 2000);
353
374
  const maxPreviews = positiveInt(params.maxPreviews, 20);
@@ -23,6 +23,7 @@ export declare class LocalWorkdirDriver {
23
23
  readonly accessMode: LocalWorkdirAccessMode;
24
24
  constructor(workdir: LocalWorkdir, options?: LocalWorkdirDriverOptions);
25
25
  dispatch(args: Record<string, unknown>): Promise<Record<string, unknown>>;
26
+ private dispatchUnsafe;
26
27
  requiresApproval(args: Record<string, unknown>): boolean;
27
28
  private dispatchApplyEdits;
28
29
  private dispatchWrite;
@@ -7,6 +7,15 @@ export class LocalWorkdirDriver {
7
7
  this.accessMode = options.accessMode ?? "approval";
8
8
  }
9
9
  async dispatch(args) {
10
+ const action = safeWorkdirAction(args);
11
+ try {
12
+ return await this.dispatchUnsafe(args);
13
+ }
14
+ catch (error) {
15
+ return localToolErrorResult(action, error);
16
+ }
17
+ }
18
+ async dispatchUnsafe(args) {
10
19
  const action = workdirAction(args);
11
20
  switch (action) {
12
21
  case "summarize":
@@ -45,7 +54,8 @@ export class LocalWorkdirDriver {
45
54
  if (this.accessMode === "full") {
46
55
  return false;
47
56
  }
48
- return mutatingLocalWorkdirActions.has(workdirAction(args));
57
+ const action = safeWorkdirAction(args);
58
+ return action !== "unknown" && mutatingLocalWorkdirActions.has(action);
49
59
  }
50
60
  async dispatchApplyEdits(args) {
51
61
  const edits = editsArg(args);
@@ -129,6 +139,7 @@ const mutatingLocalWorkdirActions = new Set([
129
139
  const localWorkdirToolDescription = [
130
140
  "Inspect and modify the selected local workdir through one model-facing primitive.",
131
141
  "Use action=list/search/grep/summarize/context to discover files, read/read_lines for file content, preview_edits before edits, and apply_edits/write/mkdir/delete only when mutation is intended.",
142
+ "grep searches file contents for a literal substring; path may be omitted, a file path, or a directory subtree.",
132
143
  "In approval mode, mutating actions return requires_approval with a safe preview instead of changing files. In full mode, mutating actions execute immediately.",
133
144
  "Paths are relative to the selected local workdir; never use absolute paths.",
134
145
  ].join(" ");
@@ -139,7 +150,7 @@ function localWorkdirToolParameters() {
139
150
  enum: localWorkdirActions,
140
151
  description: "Workdir operation. Prefer summarize/list/search/grep before reading or editing. Prefer read_lines and apply_edits for source changes.",
141
152
  },
142
- path: stringSchema("Relative path. File path for read/write/delete/edit actions; directory base for list/search/grep/summarize/context/snapshot."),
153
+ path: stringSchema("Relative path. File path for read/write/delete/edit actions. For grep, path may be a file or a directory subtree. For list/search/summarize/context/snapshot, path is a directory base."),
143
154
  query: stringSchema("Path/name query for search, or optional context query."),
144
155
  pattern: stringSchema("Literal text pattern for grep."),
145
156
  content: stringSchema("Text content for write."),
@@ -182,6 +193,10 @@ function workdirAction(args) {
182
193
  }
183
194
  return value;
184
195
  }
196
+ function safeWorkdirAction(args) {
197
+ const value = typeof args.action === "string" ? args.action.trim().toLowerCase() : "";
198
+ return localWorkdirActions.includes(value) ? value : "unknown";
199
+ }
185
200
  function summaryArgs(args) {
186
201
  return {
187
202
  path: optionalStringArg(args, "path"),
@@ -277,6 +292,27 @@ function localToolResult(action, value) {
277
292
  }
278
293
  return { ok: true, action, result };
279
294
  }
295
+ function localToolErrorResult(action, error) {
296
+ const details = errorDetails(error);
297
+ return {
298
+ ok: false,
299
+ action,
300
+ error: details.message,
301
+ code: details.code,
302
+ path: details.path,
303
+ };
304
+ }
305
+ function errorDetails(error) {
306
+ if (error instanceof Error) {
307
+ const record = error;
308
+ return {
309
+ code: typeof record.code === "string" ? record.code : undefined,
310
+ message: error.message || "local_workdir action failed",
311
+ path: typeof record.path === "string" ? record.path : undefined,
312
+ };
313
+ }
314
+ return { message: String(error || "local_workdir action failed") };
315
+ }
280
316
  function stringArg(args, key, alternateKey) {
281
317
  const direct = args[key] ?? (alternateKey ? args[alternateKey] : undefined);
282
318
  const fromOptions = args.options && typeof args.options === "object"
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "1.3.0";
2
- export declare const USER_AGENT = "@agent-api/sdk/1.3.0";
1
+ export declare const VERSION = "1.3.2";
2
+ export declare const USER_AGENT = "@agent-api/sdk/1.3.2";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
- export const VERSION = "1.3.0";
1
+ export const VERSION = "1.3.2";
2
2
  export const USER_AGENT = `@agent-api/sdk/${VERSION}`;
@@ -0,0 +1,3 @@
1
+ export declare function normalizeVolumeAssetPath(src: string | null | undefined): string;
2
+ export declare function isSupportedVolumeImagePath(src: string | null | undefined): boolean;
3
+ export declare function isSupportedVolumeImageContentType(contentType: string | null | undefined): boolean;
@@ -0,0 +1,37 @@
1
+ const SUPPORTED_VOLUME_IMAGE_EXTENSIONS = new Set([
2
+ ".avif",
3
+ ".bmp",
4
+ ".gif",
5
+ ".jpeg",
6
+ ".jpg",
7
+ ".png",
8
+ ".svg",
9
+ ".webp",
10
+ ]);
11
+ export function normalizeVolumeAssetPath(src) {
12
+ const value = src?.trim() ?? "";
13
+ if (!value)
14
+ return "";
15
+ if (isExternalAssetTarget(value))
16
+ return "";
17
+ let path = value.split("#", 1)[0]?.split("?", 1)[0]?.trim() ?? "";
18
+ path = path.replace(/^\/agent-volume\/+/i, "");
19
+ path = path.replace(/^\/+/, "");
20
+ path = path.replace(/\/+/g, "/");
21
+ if (!path || path === "." || path.includes(".."))
22
+ return "";
23
+ return path;
24
+ }
25
+ export function isSupportedVolumeImagePath(src) {
26
+ const path = normalizeVolumeAssetPath(src).toLowerCase();
27
+ if (!path)
28
+ return false;
29
+ const dot = path.lastIndexOf(".");
30
+ return dot >= 0 && SUPPORTED_VOLUME_IMAGE_EXTENSIONS.has(path.slice(dot));
31
+ }
32
+ export function isSupportedVolumeImageContentType(contentType) {
33
+ return (contentType ?? "").split(";", 1)[0]?.trim().toLowerCase().startsWith("image/") ?? false;
34
+ }
35
+ function isExternalAssetTarget(src) {
36
+ return /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(src);
37
+ }
package/dist-cjs/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.browserAuthSessionExpiresWithin = exports.DeviceAuthFlowError = exports.AuthResource = exports.resolvePresetToolsFromCatalog = exports.resolvePresetTools = exports.publicToolToRequestTool = exports.mergeTools = exports.runLocalFunctionHandlers = exports.pendingFunctionCalls = exports.functionCallOutputInput = exports.parseResponseError = exports.isRetryableStatus = exports.RateLimitError = exports.PermissionDeniedError = exports.NotFoundError = exports.InternalServerError = exports.BadRequestError = exports.AuthenticationError = exports.APIStatusError = exports.APIConnectionError = exports.APIError = exports.collectPage = exports.Page = exports.VERSION = exports.DEFAULT_TIMEOUT_MS = exports.DEFAULT_STREAM_TIMEOUT_MS = exports.DEFAULT_MAX_RETRIES = exports.AgentAPI = void 0;
17
+ exports.browserAuthSessionExpiresWithin = exports.DeviceAuthFlowError = exports.AuthResource = exports.normalizeVolumeAssetPath = exports.isSupportedVolumeImagePath = exports.isSupportedVolumeImageContentType = exports.resolvePresetToolsFromCatalog = exports.resolvePresetTools = exports.publicToolToRequestTool = exports.mergeTools = exports.runLocalFunctionHandlers = exports.pendingFunctionCalls = exports.functionCallOutputInput = exports.parseResponseError = exports.isRetryableStatus = exports.RateLimitError = exports.PermissionDeniedError = exports.NotFoundError = exports.InternalServerError = exports.BadRequestError = exports.AuthenticationError = exports.APIStatusError = exports.APIConnectionError = exports.APIError = exports.collectPage = exports.Page = exports.VERSION = exports.DEFAULT_TIMEOUT_MS = exports.DEFAULT_STREAM_TIMEOUT_MS = exports.DEFAULT_MAX_RETRIES = exports.AgentAPI = void 0;
18
18
  var client_js_1 = require("./client.js");
19
19
  Object.defineProperty(exports, "AgentAPI", { enumerable: true, get: function () { return client_js_1.AgentAPI; } });
20
20
  Object.defineProperty(exports, "DEFAULT_MAX_RETRIES", { enumerable: true, get: function () { return client_js_1.DEFAULT_MAX_RETRIES; } });
@@ -46,6 +46,10 @@ Object.defineProperty(exports, "mergeTools", { enumerable: true, get: function (
46
46
  Object.defineProperty(exports, "publicToolToRequestTool", { enumerable: true, get: function () { return preset_tools_js_1.publicToolToRequestTool; } });
47
47
  Object.defineProperty(exports, "resolvePresetTools", { enumerable: true, get: function () { return preset_tools_js_1.resolvePresetTools; } });
48
48
  Object.defineProperty(exports, "resolvePresetToolsFromCatalog", { enumerable: true, get: function () { return preset_tools_js_1.resolvePresetToolsFromCatalog; } });
49
+ var volume_assets_js_1 = require("./volume-assets.js");
50
+ Object.defineProperty(exports, "isSupportedVolumeImageContentType", { enumerable: true, get: function () { return volume_assets_js_1.isSupportedVolumeImageContentType; } });
51
+ Object.defineProperty(exports, "isSupportedVolumeImagePath", { enumerable: true, get: function () { return volume_assets_js_1.isSupportedVolumeImagePath; } });
52
+ Object.defineProperty(exports, "normalizeVolumeAssetPath", { enumerable: true, get: function () { return volume_assets_js_1.normalizeVolumeAssetPath; } });
49
53
  var auth_js_1 = require("./resources/auth.js");
50
54
  Object.defineProperty(exports, "AuthResource", { enumerable: true, get: function () { return auth_js_1.AuthResource; } });
51
55
  Object.defineProperty(exports, "DeviceAuthFlowError", { enumerable: true, get: function () { return auth_js_1.DeviceAuthFlowError; } });
@@ -325,7 +325,7 @@ class LocalFileStore {
325
325
  const maxFiles = positiveInt(params.maxFiles, 500);
326
326
  const maxBytesPerFile = positiveInt(params.maxBytesPerFile, 512 * 1024);
327
327
  const maxLineLength = positiveInt(params.maxLineLength, 500);
328
- const stats = await this.list(params.path ?? ".", { recursive: true, ignore: params.ignore });
328
+ const stats = await this.grepCandidates(params.path ?? ".", params.ignore);
329
329
  const matches = [];
330
330
  let filesScanned = 0;
331
331
  let scanTruncated = false;
@@ -363,6 +363,27 @@ class LocalFileStore {
363
363
  }
364
364
  return { object: "list", matches, files_scanned: filesScanned, scan_truncated: scanTruncated };
365
365
  }
366
+ async grepCandidates(relativePath, ignore) {
367
+ const fullPath = this.resolvePath(relativePath);
368
+ const info = await (0, promises_1.stat)(fullPath);
369
+ const portablePath = toPortablePath(node_path_1.default.relative(this.root, fullPath)) || ".";
370
+ if (ignored(portablePath, ignore)) {
371
+ return [];
372
+ }
373
+ if (info.isFile()) {
374
+ return [{
375
+ path: portablePath,
376
+ fullPath,
377
+ type: "file",
378
+ size: info.size,
379
+ modifiedAt: info.mtime,
380
+ }];
381
+ }
382
+ if (info.isDirectory()) {
383
+ return await this.list(relativePath, { recursive: true, ignore });
384
+ }
385
+ return [];
386
+ }
366
387
  async summarize(params = {}) {
367
388
  const maxFiles = positiveInt(params.maxFiles, 2000);
368
389
  const maxPreviews = positiveInt(params.maxPreviews, 20);
@@ -13,6 +13,15 @@ class LocalWorkdirDriver {
13
13
  this.accessMode = options.accessMode ?? "approval";
14
14
  }
15
15
  async dispatch(args) {
16
+ const action = safeWorkdirAction(args);
17
+ try {
18
+ return await this.dispatchUnsafe(args);
19
+ }
20
+ catch (error) {
21
+ return localToolErrorResult(action, error);
22
+ }
23
+ }
24
+ async dispatchUnsafe(args) {
16
25
  const action = workdirAction(args);
17
26
  switch (action) {
18
27
  case "summarize":
@@ -51,7 +60,8 @@ class LocalWorkdirDriver {
51
60
  if (this.accessMode === "full") {
52
61
  return false;
53
62
  }
54
- return mutatingLocalWorkdirActions.has(workdirAction(args));
63
+ const action = safeWorkdirAction(args);
64
+ return action !== "unknown" && mutatingLocalWorkdirActions.has(action);
55
65
  }
56
66
  async dispatchApplyEdits(args) {
57
67
  const edits = editsArg(args);
@@ -136,6 +146,7 @@ const mutatingLocalWorkdirActions = new Set([
136
146
  const localWorkdirToolDescription = [
137
147
  "Inspect and modify the selected local workdir through one model-facing primitive.",
138
148
  "Use action=list/search/grep/summarize/context to discover files, read/read_lines for file content, preview_edits before edits, and apply_edits/write/mkdir/delete only when mutation is intended.",
149
+ "grep searches file contents for a literal substring; path may be omitted, a file path, or a directory subtree.",
139
150
  "In approval mode, mutating actions return requires_approval with a safe preview instead of changing files. In full mode, mutating actions execute immediately.",
140
151
  "Paths are relative to the selected local workdir; never use absolute paths.",
141
152
  ].join(" ");
@@ -146,7 +157,7 @@ function localWorkdirToolParameters() {
146
157
  enum: localWorkdirActions,
147
158
  description: "Workdir operation. Prefer summarize/list/search/grep before reading or editing. Prefer read_lines and apply_edits for source changes.",
148
159
  },
149
- path: stringSchema("Relative path. File path for read/write/delete/edit actions; directory base for list/search/grep/summarize/context/snapshot."),
160
+ path: stringSchema("Relative path. File path for read/write/delete/edit actions. For grep, path may be a file or a directory subtree. For list/search/summarize/context/snapshot, path is a directory base."),
150
161
  query: stringSchema("Path/name query for search, or optional context query."),
151
162
  pattern: stringSchema("Literal text pattern for grep."),
152
163
  content: stringSchema("Text content for write."),
@@ -189,6 +200,10 @@ function workdirAction(args) {
189
200
  }
190
201
  return value;
191
202
  }
203
+ function safeWorkdirAction(args) {
204
+ const value = typeof args.action === "string" ? args.action.trim().toLowerCase() : "";
205
+ return localWorkdirActions.includes(value) ? value : "unknown";
206
+ }
192
207
  function summaryArgs(args) {
193
208
  return {
194
209
  path: optionalStringArg(args, "path"),
@@ -284,6 +299,27 @@ function localToolResult(action, value) {
284
299
  }
285
300
  return { ok: true, action, result };
286
301
  }
302
+ function localToolErrorResult(action, error) {
303
+ const details = errorDetails(error);
304
+ return {
305
+ ok: false,
306
+ action,
307
+ error: details.message,
308
+ code: details.code,
309
+ path: details.path,
310
+ };
311
+ }
312
+ function errorDetails(error) {
313
+ if (error instanceof Error) {
314
+ const record = error;
315
+ return {
316
+ code: typeof record.code === "string" ? record.code : undefined,
317
+ message: error.message || "local_workdir action failed",
318
+ path: typeof record.path === "string" ? record.path : undefined,
319
+ };
320
+ }
321
+ return { message: String(error || "local_workdir action failed") };
322
+ }
287
323
  function stringArg(args, key, alternateKey) {
288
324
  const direct = args[key] ?? (alternateKey ? args[alternateKey] : undefined);
289
325
  const fromOptions = args.options && typeof args.options === "object"
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.USER_AGENT = exports.VERSION = void 0;
4
- exports.VERSION = "1.3.0";
4
+ exports.VERSION = "1.3.2";
5
5
  exports.USER_AGENT = `@agent-api/sdk/${exports.VERSION}`;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeVolumeAssetPath = normalizeVolumeAssetPath;
4
+ exports.isSupportedVolumeImagePath = isSupportedVolumeImagePath;
5
+ exports.isSupportedVolumeImageContentType = isSupportedVolumeImageContentType;
6
+ const SUPPORTED_VOLUME_IMAGE_EXTENSIONS = new Set([
7
+ ".avif",
8
+ ".bmp",
9
+ ".gif",
10
+ ".jpeg",
11
+ ".jpg",
12
+ ".png",
13
+ ".svg",
14
+ ".webp",
15
+ ]);
16
+ function normalizeVolumeAssetPath(src) {
17
+ const value = src?.trim() ?? "";
18
+ if (!value)
19
+ return "";
20
+ if (isExternalAssetTarget(value))
21
+ return "";
22
+ let path = value.split("#", 1)[0]?.split("?", 1)[0]?.trim() ?? "";
23
+ path = path.replace(/^\/agent-volume\/+/i, "");
24
+ path = path.replace(/^\/+/, "");
25
+ path = path.replace(/\/+/g, "/");
26
+ if (!path || path === "." || path.includes(".."))
27
+ return "";
28
+ return path;
29
+ }
30
+ function isSupportedVolumeImagePath(src) {
31
+ const path = normalizeVolumeAssetPath(src).toLowerCase();
32
+ if (!path)
33
+ return false;
34
+ const dot = path.lastIndexOf(".");
35
+ return dot >= 0 && SUPPORTED_VOLUME_IMAGE_EXTENSIONS.has(path.slice(dot));
36
+ }
37
+ function isSupportedVolumeImageContentType(contentType) {
38
+ return (contentType ?? "").split(";", 1)[0]?.trim().toLowerCase().startsWith("image/") ?? false;
39
+ }
40
+ function isExternalAssetTarget(src) {
41
+ return /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(src);
42
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-api/sdk",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "Production JavaScript SDK for the Managed Agent API",
5
5
  "license": "MIT",
6
6
  "repository": {