@axiom-lattice/core 3.0.1 → 3.0.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/dist/index.mjs CHANGED
@@ -5905,12 +5905,26 @@ var VolumeFilesystem = class {
5905
5905
  return { error: String(err) };
5906
5906
  }
5907
5907
  }
5908
+ /** Delete an existing regular file from the mounted volume. */
5909
+ async delete(filePath) {
5910
+ if (!this.client.delete) {
5911
+ return { error: "Error: Backend does not support file deletion" };
5912
+ }
5913
+ try {
5914
+ await this.client.delete(filePath);
5915
+ return { path: filePath, filesUpdate: null };
5916
+ } catch (error) {
5917
+ const message = error instanceof Error ? error.message : String(error);
5918
+ return { error: `Error deleting file '${filePath}': ${message}` };
5919
+ }
5920
+ }
5908
5921
  edit(_filePath, _oldString, _newString, _replaceAll) {
5909
5922
  throw new Error("Not supported on volume backend");
5910
5923
  }
5911
5924
  };
5912
5925
 
5913
5926
  // src/sandbox_lattice/pathUtils.ts
5927
+ import { posix } from "path";
5914
5928
  function normalizeExternalSandboxPath(inputPath) {
5915
5929
  if (inputPath === "~" || inputPath === "~/") {
5916
5930
  return "/";
@@ -5923,6 +5937,60 @@ function normalizeExternalSandboxPath(inputPath) {
5923
5937
  }
5924
5938
  return `/${inputPath}`;
5925
5939
  }
5940
+ function normalizeDeleteSandboxPath(inputPath) {
5941
+ const normalized = normalizeExternalSandboxPath(inputPath);
5942
+ if (normalized.split("/").includes("..")) {
5943
+ throw new Error(`Path traversal denied: ${inputPath}`);
5944
+ }
5945
+ return normalized;
5946
+ }
5947
+ function resolveWorkspacePath(workspace, inputPath) {
5948
+ const root = posix.resolve("/", workspace);
5949
+ const normalizedInput = normalizeExternalSandboxPath(inputPath);
5950
+ if (normalizedInput.split("/").includes("..")) {
5951
+ throw new Error(`Path traversal denied: ${inputPath}`);
5952
+ }
5953
+ const alreadyInWorkspace = normalizedInput === root || normalizedInput.startsWith(`${root}/`);
5954
+ const resolved = alreadyInWorkspace ? posix.resolve(normalizedInput) : posix.resolve(root, `.${normalizedInput}`);
5955
+ const relative4 = posix.relative(root, resolved);
5956
+ if (relative4 === ".." || relative4.startsWith("../") || posix.isAbsolute(relative4)) {
5957
+ throw new Error(`Path traversal denied: ${inputPath}`);
5958
+ }
5959
+ return resolved;
5960
+ }
5961
+ function quotePosixShellArg(value) {
5962
+ return `'${value.replace(/'/g, `'"'"'`)}'`;
5963
+ }
5964
+ function buildRegularFileGuard(filePath, successCommand, containmentRoot) {
5965
+ const quotedPath = quotePosixShellArg(filePath);
5966
+ const commands = [`target=${quotedPath};`];
5967
+ if (containmentRoot !== void 0) {
5968
+ commands.push(
5969
+ `root=${quotePosixShellArg(containmentRoot)};`,
5970
+ `root_real=$(CDPATH= cd -P "$root" 2>/dev/null && pwd -P) || { printf '%s\\n' 'containment root not found' >&2; exit 5; };`,
5971
+ `case "$target" in /*) target_for_dir=$target ;; *) target_for_dir=./$target ;; esac;`,
5972
+ `parent=$(dirname "$target_for_dir") || exit 5;`,
5973
+ "base=${target_for_dir##*/};",
5974
+ `parent_real=$(CDPATH= cd -P "$parent" 2>/dev/null && pwd -P) || { printf '%s\\n' 'file parent not found' >&2; exit 5; };`,
5975
+ `case "$parent_real" in "$root_real"|"$root_real"/*) ;; *) printf '%s\\n' 'path outside containment root' >&2; exit 6 ;; esac;`,
5976
+ `CDPATH= cd -P "$parent_real" 2>/dev/null || exit 5;`,
5977
+ `target=./$base;`
5978
+ );
5979
+ }
5980
+ commands.push(
5981
+ `if [ -L "$target" ]; then printf '%s\\n' 'symlinks are not allowed' >&2; exit 2;`,
5982
+ `elif [ ! -e "$target" ]; then printf '%s\\n' 'file not found' >&2; exit 3;`,
5983
+ `elif [ ! -f "$target" ]; then printf '%s\\n' 'target is not a regular file' >&2; exit 4;`,
5984
+ `else ${successCommand}; fi`
5985
+ );
5986
+ return commands.join(" ");
5987
+ }
5988
+ function buildAssertRegularFileCommand(filePath, containmentRoot) {
5989
+ return buildRegularFileGuard(filePath, ":", containmentRoot);
5990
+ }
5991
+ function buildDeleteRegularFileCommand(filePath, containmentRoot) {
5992
+ return buildRegularFileGuard(filePath, 'rm -- "$target"', containmentRoot);
5993
+ }
5926
5994
 
5927
5995
  // src/sandbox_lattice/utils.ts
5928
5996
  import { createHash } from "crypto";
@@ -5971,7 +6039,8 @@ function stripPrefixClient(client, prefix) {
5971
6039
  write: (p, c) => client.write(strip(p), c),
5972
6040
  list: (p) => client.list(strip(p)),
5973
6041
  readRaw: (p) => client.readRaw(strip(p)),
5974
- writeRaw: (p, d) => client.writeRaw(strip(p), d)
6042
+ writeRaw: (p, d) => client.writeRaw(strip(p), d),
6043
+ ...client.delete ? { delete: (p) => client.delete(strip(p)) } : {}
5975
6044
  };
5976
6045
  }
5977
6046
  function computeSandboxName(config) {
@@ -7431,15 +7500,15 @@ function globSearchFiles(files, pattern, path8 = "/") {
7431
7500
  const effectivePattern = pattern;
7432
7501
  const matches = [];
7433
7502
  for (const [filePath, fileData] of Object.entries(filtered)) {
7434
- let relative3 = filePath.substring(normalizedPath.length);
7435
- if (relative3.startsWith("/")) {
7436
- relative3 = relative3.substring(1);
7503
+ let relative4 = filePath.substring(normalizedPath.length);
7504
+ if (relative4.startsWith("/")) {
7505
+ relative4 = relative4.substring(1);
7437
7506
  }
7438
- if (!relative3) {
7507
+ if (!relative4) {
7439
7508
  const parts = filePath.split("/");
7440
- relative3 = parts[parts.length - 1] || "";
7509
+ relative4 = parts[parts.length - 1] || "";
7441
7510
  }
7442
- if (micromatch.isMatch(relative3, effectivePattern, {
7511
+ if (micromatch.isMatch(relative4, effectivePattern, {
7443
7512
  dot: true,
7444
7513
  nobrace: false
7445
7514
  })) {
@@ -7593,9 +7662,9 @@ var StateBackend = class {
7593
7662
  if (!k.startsWith(normalizedPath)) {
7594
7663
  continue;
7595
7664
  }
7596
- const relative3 = k.substring(normalizedPath.length);
7597
- if (relative3.includes("/")) {
7598
- const subdirName = relative3.split("/")[0];
7665
+ const relative4 = k.substring(normalizedPath.length);
7666
+ if (relative4.includes("/")) {
7667
+ const subdirName = relative4.split("/")[0];
7599
7668
  subdirs.add(normalizedPath + subdirName + "/");
7600
7669
  continue;
7601
7670
  }
@@ -7691,6 +7760,17 @@ var StateBackend = class {
7691
7760
  occurrences
7692
7761
  };
7693
7762
  }
7763
+ /** Delete an existing file through a LangGraph state update. */
7764
+ delete(filePath) {
7765
+ const files = this.getFiles();
7766
+ if (!files[filePath]) {
7767
+ return { error: `Error: File '${filePath}' not found` };
7768
+ }
7769
+ return {
7770
+ path: filePath,
7771
+ filesUpdate: { [filePath]: null }
7772
+ };
7773
+ }
7694
7774
  /**
7695
7775
  * Structured search results or error string for invalid input.
7696
7776
  */
@@ -8078,12 +8158,14 @@ Path conventions:
8078
8158
  - read_file: read a file from the filesystem
8079
8159
  - write_file: write to a file in the filesystem
8080
8160
  - edit_file: edit a file in the filesystem
8161
+ - delete_file: permanently and irreversibly delete an existing regular file from the filesystem
8081
8162
  - glob: find files matching a pattern (e.g., "/project/**/*.py")
8082
8163
  - grep: search for text within files`;
8083
8164
  var LS_TOOL_DESCRIPTION = "List files and directories in a directory";
8084
8165
  var READ_FILE_TOOL_DESCRIPTION = "Read the contents of a file. For image files (png, jpg, gif, webp, bmp, svg), returns a visual description when the current model supports vision; otherwise returns an error suggesting a vision-capable model. For audio files (webm, wav, mp3, m4a, ogg, flac, aac, wma, opus, amr), transcribes the content using the default STT model; if none is registered, returns an error with registration instructions.";
8085
8166
  var WRITE_FILE_TOOL_DESCRIPTION = "Write content to a new file. Returns an error if the file already exists";
8086
8167
  var EDIT_FILE_TOOL_DESCRIPTION = "Edit a file by replacing a specific string with a new string";
8168
+ var DELETE_FILE_TOOL_DESCRIPTION = "Permanently and irreversibly delete an existing regular file. Directories and symbolic links are not allowed. If the target is ambiguous, use ask_user_to_clarify before deleting";
8087
8169
  var GLOB_TOOL_DESCRIPTION = "Find files matching a glob pattern (e.g., '**/*.py' for all Python files)";
8088
8170
  var GREP_TOOL_DESCRIPTION = "Search for a regex pattern in files. Returns matching files and line numbers";
8089
8171
  function createLsTool(backend, options) {
@@ -8292,6 +8374,48 @@ function createEditFileTool(backend, options) {
8292
8374
  }
8293
8375
  );
8294
8376
  }
8377
+ function createDeleteFileTool(backend, options) {
8378
+ const { customDescription } = options;
8379
+ return tool38(
8380
+ async (input, config) => {
8381
+ const toolConfig = config;
8382
+ const runConfig = toolConfig.configurable?.runConfig ?? {};
8383
+ const stateAndStore = {
8384
+ state: getCurrentTaskInput(config),
8385
+ store: toolConfig.store,
8386
+ ...runConfig
8387
+ };
8388
+ const resolvedBackend = await getBackend(backend, stateAndStore);
8389
+ const { file_path } = input;
8390
+ if (!resolvedBackend.delete) {
8391
+ return "Error: Backend does not support file deletion";
8392
+ }
8393
+ const result = await resolvedBackend.delete(file_path);
8394
+ if (result.error) {
8395
+ return result.error;
8396
+ }
8397
+ const message = new ToolMessage({
8398
+ content: `Successfully deleted '${file_path}'`,
8399
+ tool_call_id: toolConfig.toolCall?.id ?? "",
8400
+ name: "delete_file",
8401
+ metadata: result.metadata
8402
+ });
8403
+ if (result.filesUpdate) {
8404
+ return new Command({
8405
+ update: { files: result.filesUpdate, messages: [message] }
8406
+ });
8407
+ }
8408
+ return message;
8409
+ },
8410
+ {
8411
+ name: "delete_file",
8412
+ description: customDescription || DELETE_FILE_TOOL_DESCRIPTION,
8413
+ schema: z310.object({
8414
+ file_path: z310.string().describe("Absolute path to the file to delete")
8415
+ })
8416
+ }
8417
+ );
8418
+ }
8295
8419
  function createGlobTool(backend, options) {
8296
8420
  const { customDescription } = options;
8297
8421
  return tool38(
@@ -8383,6 +8507,9 @@ function createFilesystemMiddleware(options = {}) {
8383
8507
  createEditFileTool(backend, {
8384
8508
  customDescription: customToolDescriptions?.edit_file
8385
8509
  }),
8510
+ createDeleteFileTool(backend, {
8511
+ customDescription: customToolDescriptions?.delete_file
8512
+ }),
8386
8513
  createGlobTool(backend, {
8387
8514
  customDescription: customToolDescriptions?.glob
8388
8515
  }),
@@ -10962,6 +11089,19 @@ var SandboxFilesystem = class {
10962
11089
  return { error: `Error writing file '${filePath}': ${e.message}` };
10963
11090
  }
10964
11091
  }
11092
+ /** Delete an existing regular file in the sandbox. */
11093
+ async delete(filePath) {
11094
+ if (!this.sandbox.file.deleteFile) {
11095
+ return { error: "Error: Backend does not support file deletion" };
11096
+ }
11097
+ try {
11098
+ await this.sandbox.file.deleteFile(filePath);
11099
+ return { path: filePath, filesUpdate: null };
11100
+ } catch (error) {
11101
+ const message = error instanceof Error ? error.message : String(error);
11102
+ return { error: `Error deleting file '${filePath}': ${message}` };
11103
+ }
11104
+ }
10965
11105
  async edit(filePath, oldString, newString, replaceAll = false) {
10966
11106
  try {
10967
11107
  await this.sandbox.file.strReplaceEditor({
@@ -15651,9 +15791,9 @@ var StoreBackend = class {
15651
15791
  if (!itemKey.startsWith(normalizedPath)) {
15652
15792
  continue;
15653
15793
  }
15654
- const relative3 = itemKey.substring(normalizedPath.length);
15655
- if (relative3.includes("/")) {
15656
- const subdirName = relative3.split("/")[0];
15794
+ const relative4 = itemKey.substring(normalizedPath.length);
15795
+ if (relative4.includes("/")) {
15796
+ const subdirName = relative4.split("/")[0];
15657
15797
  subdirs.add(normalizedPath + subdirName + "/");
15658
15798
  continue;
15659
15799
  }
@@ -15760,6 +15900,22 @@ var StoreBackend = class {
15760
15900
  return { error: `Error: ${e.message}` };
15761
15901
  }
15762
15902
  }
15903
+ /** Delete an existing persistent file. */
15904
+ async delete(filePath) {
15905
+ try {
15906
+ const store = this.getStore();
15907
+ const namespace = this.getNamespace();
15908
+ const existing = await store.get(namespace, filePath);
15909
+ if (!existing) {
15910
+ return { error: `Error: File '${filePath}' not found` };
15911
+ }
15912
+ await store.delete(namespace, filePath);
15913
+ return { path: filePath, filesUpdate: null };
15914
+ } catch (error) {
15915
+ const message = error instanceof Error ? error.message : String(error);
15916
+ return { error: `Error deleting file '${filePath}': ${message}` };
15917
+ }
15918
+ }
15763
15919
  /**
15764
15920
  * Structured search results or error string for invalid input.
15765
15921
  */
@@ -15852,8 +16008,8 @@ var FilesystemBackend = class {
15852
16008
  throw new Error("Path traversal not allowed");
15853
16009
  }
15854
16010
  const full = path4.resolve(this.cwd, vpath.substring(1));
15855
- const relative3 = path4.relative(this.cwd, full);
15856
- if (relative3.startsWith("..") || path4.isAbsolute(relative3)) {
16011
+ const relative4 = path4.relative(this.cwd, full);
16012
+ if (relative4.startsWith("..") || path4.isAbsolute(relative4)) {
15857
16013
  throw new Error(`Path: ${full} outside root directory: ${this.cwd}`);
15858
16014
  }
15859
16015
  return full;
@@ -15867,6 +16023,31 @@ var FilesystemBackend = class {
15867
16023
  }
15868
16024
  return path4.resolve(this.cwd, target);
15869
16025
  }
16026
+ async assertVirtualParentContained(resolvedPath) {
16027
+ if (!this.virtualMode) {
16028
+ return;
16029
+ }
16030
+ const [rootPath, parentPath] = await Promise.all([
16031
+ fs2.realpath(this.cwd),
16032
+ fs2.realpath(path4.dirname(resolvedPath))
16033
+ ]);
16034
+ const relative4 = path4.relative(rootPath, parentPath);
16035
+ if (relative4 === ".." || relative4.startsWith(`..${path4.sep}`) || path4.isAbsolute(relative4)) {
16036
+ throw new Error(`Path: ${resolvedPath} outside root directory: ${this.cwd}`);
16037
+ }
16038
+ }
16039
+ validateDeleteTarget(filePath, stat4) {
16040
+ if (stat4.isSymbolicLink()) {
16041
+ return `Error: Cannot delete '${filePath}': symlinks are not allowed`;
16042
+ }
16043
+ if (stat4.isDirectory()) {
16044
+ return `Error: Cannot delete '${filePath}': target is a directory`;
16045
+ }
16046
+ if (!stat4.isFile()) {
16047
+ return `Error: Cannot delete '${filePath}': target is not a regular file`;
16048
+ }
16049
+ return void 0;
16050
+ }
15870
16051
  /**
15871
16052
  * List files and directories in the specified directory (non-recursive).
15872
16053
  *
@@ -16068,6 +16249,50 @@ var FilesystemBackend = class {
16068
16249
  return { error: `Error writing file '${filePath}': ${e.message}` };
16069
16250
  }
16070
16251
  }
16252
+ /** Delete an existing regular file without following symbolic links. */
16253
+ async delete(filePath) {
16254
+ let resolvedPath;
16255
+ try {
16256
+ resolvedPath = this.resolvePath(filePath);
16257
+ } catch (error) {
16258
+ const message = error instanceof Error ? error.message : String(error);
16259
+ return { error: `Error deleting file '${filePath}': ${message}` };
16260
+ }
16261
+ let stat4;
16262
+ try {
16263
+ stat4 = await fs2.lstat(resolvedPath);
16264
+ } catch (error) {
16265
+ if (error.code === "ENOENT") {
16266
+ return { error: `Error: File '${filePath}' not found` };
16267
+ }
16268
+ const message = error instanceof Error ? error.message : String(error);
16269
+ return { error: `Error deleting file '${filePath}': ${message}` };
16270
+ }
16271
+ const validationError = this.validateDeleteTarget(filePath, stat4);
16272
+ if (validationError) {
16273
+ return { error: validationError };
16274
+ }
16275
+ try {
16276
+ await this.assertVirtualParentContained(resolvedPath);
16277
+ const currentStat = await fs2.lstat(resolvedPath);
16278
+ const currentValidationError = this.validateDeleteTarget(filePath, currentStat);
16279
+ if (currentValidationError) {
16280
+ return { error: currentValidationError };
16281
+ }
16282
+ if (currentStat.dev !== stat4.dev || currentStat.ino !== stat4.ino) {
16283
+ return { error: `Error: Cannot delete '${filePath}': target changed during deletion` };
16284
+ }
16285
+ await this.assertVirtualParentContained(resolvedPath);
16286
+ await fs2.unlink(resolvedPath);
16287
+ return { path: filePath, filesUpdate: null };
16288
+ } catch (error) {
16289
+ if (error.code === "ENOENT") {
16290
+ return { error: `Error: File '${filePath}' not found` };
16291
+ }
16292
+ const message = error instanceof Error ? error.message : String(error);
16293
+ return { error: `Error deleting file '${filePath}': ${message}` };
16294
+ }
16295
+ }
16071
16296
  /**
16072
16297
  * Edit a file by replacing string occurrences.
16073
16298
  * Returns EditResult. External storage sets filesUpdate=null.
@@ -16192,9 +16417,9 @@ var FilesystemBackend = class {
16192
16417
  if (this.virtualMode) {
16193
16418
  try {
16194
16419
  const resolved = path4.resolve(ftext);
16195
- const relative3 = path4.relative(this.cwd, resolved);
16196
- if (relative3.startsWith("..")) continue;
16197
- const normalizedRelative = relative3.split(path4.sep).join("/");
16420
+ const relative4 = path4.relative(this.cwd, resolved);
16421
+ if (relative4.startsWith("..")) continue;
16422
+ const normalizedRelative = relative4.split(path4.sep).join("/");
16198
16423
  virtPath = "/" + normalizedRelative;
16199
16424
  } catch {
16200
16425
  continue;
@@ -16256,9 +16481,9 @@ var FilesystemBackend = class {
16256
16481
  let virtPath;
16257
16482
  if (this.virtualMode) {
16258
16483
  try {
16259
- const relative3 = path4.relative(this.cwd, fp);
16260
- if (relative3.startsWith("..")) continue;
16261
- const normalizedRelative = relative3.split(path4.sep).join("/");
16484
+ const relative4 = path4.relative(this.cwd, fp);
16485
+ if (relative4.startsWith("..")) continue;
16486
+ const normalizedRelative = relative4.split(path4.sep).join("/");
16262
16487
  virtPath = "/" + normalizedRelative;
16263
16488
  } catch {
16264
16489
  continue;
@@ -16509,6 +16734,14 @@ var CompositeBackend = class {
16509
16734
  const [backend, strippedKey] = this.getBackendAndKey(filePath);
16510
16735
  return await backend.write(strippedKey, content);
16511
16736
  }
16737
+ /** Delete a file, routing to the same backend selected for write and edit. */
16738
+ async delete(filePath) {
16739
+ const [backend, strippedKey] = this.getBackendAndKey(filePath);
16740
+ if (!backend.delete) {
16741
+ return { error: "Error: Backend does not support file deletion" };
16742
+ }
16743
+ return await backend.delete(strippedKey);
16744
+ }
16512
16745
  /**
16513
16746
  * Edit a file, routing to appropriate backend.
16514
16747
  *
@@ -16541,9 +16774,9 @@ var MemoryBackend = class {
16541
16774
  if (!k.startsWith(normalizedPath)) {
16542
16775
  continue;
16543
16776
  }
16544
- const relative3 = k.substring(normalizedPath.length);
16545
- if (relative3.includes("/")) {
16546
- const subdirName = relative3.split("/")[0];
16777
+ const relative4 = k.substring(normalizedPath.length);
16778
+ if (relative4.includes("/")) {
16779
+ const subdirName = relative4.split("/")[0];
16547
16780
  subdirs.add(normalizedPath + subdirName + "/");
16548
16781
  continue;
16549
16782
  }
@@ -16611,6 +16844,14 @@ var MemoryBackend = class {
16611
16844
  this.files.set(filePath, newFileData);
16612
16845
  return { path: filePath, filesUpdate: null, occurrences };
16613
16846
  }
16847
+ /** Delete an existing in-memory file. */
16848
+ delete(filePath) {
16849
+ if (!this.files.has(filePath)) {
16850
+ return { error: `Error: File '${filePath}' not found` };
16851
+ }
16852
+ this.files.delete(filePath);
16853
+ return { path: filePath, filesUpdate: null };
16854
+ }
16614
16855
  grepRaw(pattern, path8 = "/", glob = null) {
16615
16856
  const files = this.getFiles();
16616
16857
  return grepMatchesFromFiles(files, pattern, path8, glob);
@@ -23005,6 +23246,9 @@ var MicrosandboxRemoteInstance = class {
23005
23246
  }
23006
23247
  return Buffer.from(result.content ?? "");
23007
23248
  },
23249
+ deleteFile: async (file) => {
23250
+ await this.client.deleteFile(this.name, normalizeExternalSandboxPath(file));
23251
+ },
23008
23252
  deletePath: async (path8) => {
23009
23253
  const resolved = normalizeExternalSandboxPath(path8);
23010
23254
  await this.client.execCommand({
@@ -23111,6 +23355,12 @@ var MicrosandboxServiceClient = class {
23111
23355
  body: { sandboxName, path: path8, content }
23112
23356
  });
23113
23357
  }
23358
+ async deleteFile(sandboxName, path8) {
23359
+ return this.request("/api/files/delete", {
23360
+ method: "POST",
23361
+ body: { sandboxName, path: path8 }
23362
+ });
23363
+ }
23114
23364
  async listPath(sandboxName, path8, recursive) {
23115
23365
  return this.request("/api/files/list", {
23116
23366
  method: "POST",
@@ -23172,6 +23422,15 @@ var MicrosandboxServiceClient = class {
23172
23422
  }
23173
23423
  );
23174
23424
  }
23425
+ async volumeFsDelete(volumeName, path8) {
23426
+ await this.request(
23427
+ `/api/volumes/${encodeURIComponent(volumeName)}/fs/delete`,
23428
+ {
23429
+ method: "POST",
23430
+ body: { path: path8 }
23431
+ }
23432
+ );
23433
+ }
23175
23434
  async volumeFsList(volumeName, path8) {
23176
23435
  console.log(`[volumeFsList] volume=${volumeName} path="${path8}" url=POST /api/volumes/${encodeURIComponent(volumeName)}/fs/list`);
23177
23436
  const result = await this.request(
@@ -23303,7 +23562,10 @@ var MicrosandboxRemoteProvider = class {
23303
23562
  return new MicrosandboxRemoteInstance(name, this.client);
23304
23563
  })();
23305
23564
  this.creating.set(name, creation);
23306
- creation.finally(() => this.creating.delete(name));
23565
+ creation.then(
23566
+ () => this.creating.delete(name),
23567
+ () => this.creating.delete(name)
23568
+ );
23307
23569
  return creation;
23308
23570
  }
23309
23571
  async getSandbox(name) {
@@ -23326,6 +23588,7 @@ var MicrosandboxRemoteProvider = class {
23326
23588
  return {
23327
23589
  read: (path8) => this.client.volumeFsRead(volumeName, path8),
23328
23590
  write: (path8, content) => this.client.volumeFsWrite(volumeName, path8, content),
23591
+ delete: (path8) => this.client.volumeFsDelete(volumeName, path8),
23329
23592
  list: (path8) => this.client.volumeFsList(volumeName, path8),
23330
23593
  readRaw: (path8) => this.client.volumeFsDownload(volumeName, path8),
23331
23594
  writeRaw: (path8, data) => this.client.volumeFsUpload(volumeName, path8, data),
@@ -23503,6 +23766,22 @@ var RemoteSandboxInstance = class {
23503
23766
  const buffer2 = await result.body.arrayBuffer();
23504
23767
  return Buffer.from(buffer2);
23505
23768
  },
23769
+ deleteFile: async (file) => {
23770
+ const resolved = this.resolveDeletePath(file);
23771
+ const result = await this.client.shell.execCommand({
23772
+ command: buildDeleteRegularFileCommand(
23773
+ resolved,
23774
+ resolveWorkspacePath(this.workspace, "/")
23775
+ )
23776
+ });
23777
+ if (!result.ok) {
23778
+ throw new Error(`deleteFile failed: ${extractFetcherError(result.error)}`);
23779
+ }
23780
+ const exitCode = result.body.data?.exit_code ?? 0;
23781
+ if (exitCode !== 0) {
23782
+ throw new Error(`deleteFile failed: ${result.body.data?.output ?? `exit code ${exitCode}`}`);
23783
+ }
23784
+ },
23506
23785
  deletePath: async (path8) => {
23507
23786
  const resolved = this.resolvePath(path8);
23508
23787
  const result = await this.client.shell.execCommand({
@@ -23549,6 +23828,9 @@ var RemoteSandboxInstance = class {
23549
23828
  }
23550
23829
  return `${this.workspace}${file}`;
23551
23830
  }
23831
+ resolveDeletePath(file) {
23832
+ return resolveWorkspacePath(this.workspace, file);
23833
+ }
23552
23834
  async start() {
23553
23835
  }
23554
23836
  async stop() {
@@ -23668,6 +23950,19 @@ var RemoteSandboxProvider = class {
23668
23950
  }
23669
23951
  return `${workspace}/${p}`;
23670
23952
  };
23953
+ const resolveDelete = (p) => {
23954
+ if (!p || p === "/") {
23955
+ return resolveWorkspacePath(workspace, pathPrefix ?? "/");
23956
+ }
23957
+ if (p === workspace || p.startsWith(`${workspace}/`)) {
23958
+ return resolveWorkspacePath(workspace, p);
23959
+ }
23960
+ if (p.startsWith("/")) {
23961
+ return resolveWorkspacePath(workspace, p);
23962
+ }
23963
+ const prefixed = pathPrefix ? `/${pathPrefix.replace(/^\//, "")}/${p}` : p;
23964
+ return resolveWorkspacePath(workspace, prefixed);
23965
+ };
23671
23966
  return {
23672
23967
  read: async (path8) => {
23673
23968
  const resolved = resolve4(path8);
@@ -23684,6 +23979,24 @@ var RemoteSandboxProvider = class {
23684
23979
  throw new Error(`Volume write failed: ${extractFetcherError(result.error)}`);
23685
23980
  }
23686
23981
  },
23982
+ delete: async (path8) => {
23983
+ const resolved = resolveDelete(path8);
23984
+ const result = await this.client.shell.execCommand({
23985
+ command: buildDeleteRegularFileCommand(
23986
+ resolved,
23987
+ resolveWorkspacePath(workspace, "/")
23988
+ )
23989
+ });
23990
+ if (!result.ok) {
23991
+ throw new Error(`Volume delete failed: ${extractFetcherError(result.error)}`);
23992
+ }
23993
+ const exitCode = result.body.data?.exit_code ?? 0;
23994
+ if (exitCode !== 0) {
23995
+ throw new Error(
23996
+ `Volume delete failed: ${result.body.data?.output ?? `exit code ${exitCode}`}`
23997
+ );
23998
+ }
23999
+ },
23687
24000
  mkdir: async (path8) => {
23688
24001
  const resolved = resolve4(path8);
23689
24002
  const result = await this.client.shell.execCommand({
@@ -23802,6 +24115,20 @@ var E2BInstance = class {
23802
24115
  const data = await this.native.files.read(params.file, { format: "bytes" });
23803
24116
  return Buffer.isBuffer(data) ? data : Buffer.from(data);
23804
24117
  },
24118
+ deleteFile: async (file) => {
24119
+ const deletePath = normalizeDeleteSandboxPath(file);
24120
+ const info = await this.native.files.getInfo(deletePath);
24121
+ if (info.symlinkTarget) {
24122
+ throw new Error(`Cannot delete '${file}': symlinks are not allowed`);
24123
+ }
24124
+ if (info.type === "dir") {
24125
+ throw new Error(`Cannot delete '${file}': target is a directory`);
24126
+ }
24127
+ if (info.type !== "file") {
24128
+ throw new Error(`Cannot delete '${file}': target is not a regular file`);
24129
+ }
24130
+ await this.native.files.remove(deletePath);
24131
+ },
23805
24132
  deletePath: async (path8) => {
23806
24133
  await this.native.commands.run(`rm -rf "${path8}"`);
23807
24134
  },
@@ -23925,6 +24252,10 @@ function toRelativePath(inputPath) {
23925
24252
  const normalized = normalizeExternalSandboxPath(inputPath);
23926
24253
  return normalized === "/" ? "" : normalized.slice(1);
23927
24254
  }
24255
+ function toDeleteRelativePath(inputPath) {
24256
+ const normalized = normalizeDeleteSandboxPath(inputPath);
24257
+ return normalized === "/" ? "" : normalized.slice(1);
24258
+ }
23928
24259
  var DaytonaInstance = class {
23929
24260
  constructor(name, native) {
23930
24261
  this.native = native;
@@ -23985,6 +24316,18 @@ var DaytonaInstance = class {
23985
24316
  const buffer2 = await this.native.fs.downloadFile(toRelativePath(params.file));
23986
24317
  return Buffer.isBuffer(buffer2) ? buffer2 : Buffer.from(buffer2);
23987
24318
  },
24319
+ deleteFile: async (file) => {
24320
+ const relativePath = toDeleteRelativePath(file);
24321
+ const check = await this.native.process.executeCommand(
24322
+ buildAssertRegularFileCommand(relativePath, "."),
24323
+ void 0,
24324
+ void 0
24325
+ );
24326
+ if (check.exitCode !== 0) {
24327
+ throw new Error(check.result || `Cannot delete '${file}': target is not a regular file`);
24328
+ }
24329
+ await this.native.fs.deleteFile(relativePath, false);
24330
+ },
23988
24331
  deletePath: async (path8) => {
23989
24332
  await this.native.process.executeCommand(`rm -rf "${toRelativePath(path8)}"`, void 0, void 0);
23990
24333
  },
@@ -24248,10 +24591,21 @@ import * as fs4 from "fs/promises";
24248
24591
  import { execFile } from "child_process";
24249
24592
  import * as fs3 from "fs/promises";
24250
24593
  import * as path5 from "path";
24251
- import * as posix from "path/posix";
24594
+ import * as posix2 from "path/posix";
24252
24595
  import { promisify } from "util";
24253
24596
  var execFileAsync = promisify(execFile);
24254
24597
  var isWin = process.platform === "win32";
24598
+ function assertRegularDeleteTarget(file, stat4) {
24599
+ if (stat4.isSymbolicLink()) {
24600
+ throw new Error(`Cannot delete '${file}': symlinks are not allowed`);
24601
+ }
24602
+ if (stat4.isDirectory()) {
24603
+ throw new Error(`Cannot delete '${file}': target is a directory`);
24604
+ }
24605
+ if (!stat4.isFile()) {
24606
+ throw new Error(`Cannot delete '${file}': target is not a regular file`);
24607
+ }
24608
+ }
24255
24609
  var LocalSandboxInstance = class {
24256
24610
  constructor(name, rootDir) {
24257
24611
  this.file = {
@@ -24275,7 +24629,7 @@ var LocalSandboxInstance = class {
24275
24629
  const full = path5.join(hp, e.name);
24276
24630
  const stat4 = await fs3.stat(full).catch(() => null);
24277
24631
  files.push({
24278
- path: posix.join(targetPath, e.name),
24632
+ path: posix2.join(targetPath, e.name),
24279
24633
  is_dir: e.isDirectory(),
24280
24634
  size: stat4?.size ?? 0,
24281
24635
  modified_at: stat4?.mtime.toISOString()
@@ -24292,7 +24646,7 @@ var LocalSandboxInstance = class {
24292
24646
  );
24293
24647
  await this.walkDirFilter(hp, regex, results);
24294
24648
  const hpNorm = hp + path5.sep;
24295
- const toSandboxPath = (hostPath) => posix.join(targetPath, hostPath.slice(hpNorm.length).split(path5.sep).join("/"));
24649
+ const toSandboxPath = (hostPath) => posix2.join(targetPath, hostPath.slice(hpNorm.length).split(path5.sep).join("/"));
24296
24650
  return { files: results.map(toSandboxPath) };
24297
24651
  },
24298
24652
  searchInFile: async (file, regex) => {
@@ -24335,6 +24689,38 @@ var LocalSandboxInstance = class {
24335
24689
  const data = await fs3.readFile(this.hostPath(params.file));
24336
24690
  return data;
24337
24691
  },
24692
+ deleteFile: async (file) => {
24693
+ const hp = this.hostPath(file);
24694
+ let stat4;
24695
+ try {
24696
+ stat4 = await fs3.lstat(hp);
24697
+ } catch (error) {
24698
+ if (error.code === "ENOENT") {
24699
+ throw new Error(`File '${file}' not found`);
24700
+ }
24701
+ throw error;
24702
+ }
24703
+ assertRegularDeleteTarget(file, stat4);
24704
+ const [rootPath, parentPath] = await Promise.all([
24705
+ fs3.realpath(this.rootDir),
24706
+ fs3.realpath(path5.dirname(hp))
24707
+ ]);
24708
+ const relativeParent = path5.relative(rootPath, parentPath);
24709
+ if (relativeParent === ".." || relativeParent.startsWith(`..${path5.sep}`) || path5.isAbsolute(relativeParent)) {
24710
+ throw new Error(`Path traversal denied: ${file}`);
24711
+ }
24712
+ const currentStat = await fs3.lstat(hp);
24713
+ assertRegularDeleteTarget(file, currentStat);
24714
+ if (currentStat.dev !== stat4.dev || currentStat.ino !== stat4.ino) {
24715
+ throw new Error(`Cannot delete '${file}': target changed during deletion`);
24716
+ }
24717
+ const currentParentPath = await fs3.realpath(path5.dirname(hp));
24718
+ const currentRelativeParent = path5.relative(rootPath, currentParentPath);
24719
+ if (currentRelativeParent === ".." || currentRelativeParent.startsWith(`..${path5.sep}`) || path5.isAbsolute(currentRelativeParent)) {
24720
+ throw new Error(`Path traversal denied: ${file}`);
24721
+ }
24722
+ await fs3.unlink(hp);
24723
+ },
24338
24724
  deletePath: async (targetPath) => {
24339
24725
  await fs3.rm(this.hostPath(targetPath), { recursive: true, force: true });
24340
24726
  },
@@ -24411,7 +24797,7 @@ ${errOut}`.trim() : out.trim();
24411
24797
  }
24412
24798
  for (const e of entries) {
24413
24799
  const fullHost = path5.join(hostDir, e.name);
24414
- const fullSandbox = posix.join(sandboxDir, e.name);
24800
+ const fullSandbox = posix2.join(sandboxDir, e.name);
24415
24801
  try {
24416
24802
  const stat4 = await fs3.stat(fullHost);
24417
24803
  result.push({