@deepagents/context 1.0.0 → 2.2.0

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.
Files changed (51) hide show
  1. package/README.md +18 -14
  2. package/dist/browser.js +37 -2
  3. package/dist/browser.js.map +3 -3
  4. package/dist/index.js +1564 -454
  5. package/dist/index.js.map +4 -4
  6. package/dist/lib/guardrail.d.ts +2 -6
  7. package/dist/lib/guardrail.d.ts.map +1 -1
  8. package/dist/lib/sandbox/abort.d.ts +24 -0
  9. package/dist/lib/sandbox/abort.d.ts.map +1 -0
  10. package/dist/lib/sandbox/agent-os-sandbox.d.ts +8 -7
  11. package/dist/lib/sandbox/agent-os-sandbox.d.ts.map +1 -1
  12. package/dist/lib/sandbox/bash-tool.d.ts +29 -16
  13. package/dist/lib/sandbox/bash-tool.d.ts.map +1 -1
  14. package/dist/lib/sandbox/daytona-sandbox.d.ts +63 -0
  15. package/dist/lib/sandbox/daytona-sandbox.d.ts.map +1 -0
  16. package/dist/lib/sandbox/docker-sandbox-errors.d.ts +28 -7
  17. package/dist/lib/sandbox/docker-sandbox-errors.d.ts.map +1 -1
  18. package/dist/lib/sandbox/docker-sandbox.d.ts +84 -19
  19. package/dist/lib/sandbox/docker-sandbox.d.ts.map +1 -1
  20. package/dist/lib/sandbox/file-events.d.ts +33 -38
  21. package/dist/lib/sandbox/file-events.d.ts.map +1 -1
  22. package/dist/lib/sandbox/index.d.ts +2 -1
  23. package/dist/lib/sandbox/index.d.ts.map +1 -1
  24. package/dist/lib/sandbox/installers/bin.d.ts +40 -0
  25. package/dist/lib/sandbox/installers/bin.d.ts.map +1 -0
  26. package/dist/lib/sandbox/installers/index.d.ts +1 -0
  27. package/dist/lib/sandbox/installers/index.d.ts.map +1 -1
  28. package/dist/lib/sandbox/types.d.ts +51 -6
  29. package/dist/lib/sandbox/types.d.ts.map +1 -1
  30. package/dist/lib/sandbox/virtual-sandbox.d.ts +2 -2
  31. package/dist/lib/sandbox/virtual-sandbox.d.ts.map +1 -1
  32. package/dist/lib/save/save-pipeline.d.ts.map +1 -1
  33. package/dist/lib/skills/fragments.d.ts +2 -2
  34. package/dist/lib/store/sqlite.store.d.ts +1 -0
  35. package/dist/lib/store/sqlite.store.d.ts.map +1 -1
  36. package/dist/lib/store/store.d.ts +8 -0
  37. package/dist/lib/store/store.d.ts.map +1 -1
  38. package/dist/lib/stream/postgres.stream-store.d.ts.map +1 -1
  39. package/dist/lib/stream/sqlite.stream-store.d.ts.map +1 -1
  40. package/dist/lib/stream/stream-manager.d.ts +1 -1
  41. package/dist/lib/stream/stream-manager.d.ts.map +1 -1
  42. package/dist/lib/stream/stream-store.d.ts +8 -1
  43. package/dist/lib/stream/stream-store.d.ts.map +1 -1
  44. package/dist/lib/stream/types.d.ts +7 -0
  45. package/dist/lib/stream/types.d.ts.map +1 -0
  46. package/dist/lib/stream-buffer.d.ts.map +1 -1
  47. package/package.json +8 -12
  48. package/dist/lib/sandbox/container-tool.d.ts +0 -188
  49. package/dist/lib/sandbox/container-tool.d.ts.map +0 -1
  50. package/dist/lib/sandboxes/openai.d.ts +0 -6
  51. package/dist/lib/sandboxes/openai.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -2623,6 +2623,8 @@ var SavePipeline = class {
2623
2623
  async persist() {
2624
2624
  let parentId = this.#engine.getActiveBranch().headMessageId;
2625
2625
  const now = Date.now();
2626
+ const messages = [];
2627
+ const pendingMessagesById = /* @__PURE__ */ new Map();
2626
2628
  for (const fragment2 of this.#pending) {
2627
2629
  if (!fragment2.codec) {
2628
2630
  throw new Error(`Fragment "${fragment2.name}" is missing codec.`);
@@ -2630,7 +2632,7 @@ var SavePipeline = class {
2630
2632
  const msgId = fragment2.id ?? crypto.randomUUID();
2631
2633
  let msgParentId = parentId;
2632
2634
  if (!this.#shouldBranch && msgId === parentId) {
2633
- const existing = await this.#engine.store.getMessage(msgId);
2635
+ const existing = pendingMessagesById.get(msgId) ?? await this.#engine.store.getMessage(msgId);
2634
2636
  if (existing) msgParentId = existing.parentId;
2635
2637
  }
2636
2638
  const messageData = {
@@ -2642,7 +2644,8 @@ var SavePipeline = class {
2642
2644
  data: fragment2.codec.encode(),
2643
2645
  createdAt: now
2644
2646
  };
2645
- await this.#engine.store.addMessage(messageData);
2647
+ messages.push(messageData);
2648
+ pendingMessagesById.set(messageData.id, messageData);
2646
2649
  parentId = messageData.id;
2647
2650
  }
2648
2651
  if (parentId === null) {
@@ -2650,6 +2653,7 @@ var SavePipeline = class {
2650
2653
  "Pipeline persisted no messages but pending was not empty"
2651
2654
  );
2652
2655
  }
2656
+ await this.#engine.store.addMessages(messages);
2653
2657
  await this.#engine.commitHead(parentId);
2654
2658
  return { headMessageId: parentId };
2655
2659
  }
@@ -2765,6 +2769,18 @@ var ddl_sqlite_default = "-- Context Store DDL for SQLite\n-- This schema implem
2765
2769
 
2766
2770
  // packages/context/src/lib/store/store.ts
2767
2771
  var ContextStore = class {
2772
+ /**
2773
+ * Add multiple messages to the graph.
2774
+ *
2775
+ * Stores can override this for atomic/batched persistence. The default keeps
2776
+ * the single-message contract for implementations that do not need a custom
2777
+ * batch path.
2778
+ */
2779
+ async addMessages(messages) {
2780
+ for (const message2 of messages) {
2781
+ await this.addMessage(message2);
2782
+ }
2783
+ }
2768
2784
  };
2769
2785
 
2770
2786
  // packages/context/src/lib/store/sqlite.store.ts
@@ -2972,7 +2988,7 @@ var SqliteContextStore = class extends ContextStore {
2972
2988
  // ==========================================================================
2973
2989
  // Message Operations (Graph Nodes)
2974
2990
  // ==========================================================================
2975
- async addMessage(message2) {
2991
+ #insertMessage(message2) {
2976
2992
  if (message2.parentId === message2.id) {
2977
2993
  throw new Error(`Message ${message2.id} cannot be its own parent`);
2978
2994
  }
@@ -2999,6 +3015,17 @@ var SqliteContextStore = class extends ContextStore {
2999
3015
  VALUES (?, ?, ?, ?)`
3000
3016
  ).run(message2.id, message2.chatId, message2.name, content);
3001
3017
  }
3018
+ async addMessage(message2) {
3019
+ this.#insertMessage(message2);
3020
+ }
3021
+ async addMessages(messages) {
3022
+ if (messages.length === 0) return;
3023
+ this.#useTransaction(() => {
3024
+ for (const message2 of messages) {
3025
+ this.#insertMessage(message2);
3026
+ }
3027
+ });
3028
+ }
3002
3029
  async getMessage(messageId) {
3003
3030
  const row = this.#stmt("SELECT * FROM messages WHERE id = ?").get(
3004
3031
  messageId
@@ -4697,7 +4724,7 @@ async function chat(agent2, options = {}) {
4697
4724
  if (isAborted) {
4698
4725
  normalizedMessage.parts = sanitizeAbortedParts(normalizedMessage.parts);
4699
4726
  }
4700
- const drained = sandbox.drainFileEvents?.() ?? [];
4727
+ const drained = sandbox.drainFileEvents();
4701
4728
  const fileEvents = isAborted ? [] : drained;
4702
4729
  const finalMetadata = await options.finalAssistantMetadata?.(normalizedMessage);
4703
4730
  const mergedMetadata = {
@@ -5454,15 +5481,15 @@ function dateReminder(options) {
5454
5481
  const now = /* @__PURE__ */ new Date();
5455
5482
  const currentDate = formatDateKey(now, tz);
5456
5483
  const currentDay = formatDayOfWeek(now, tz);
5457
- let diff = "";
5484
+ let diff2 = "";
5458
5485
  if (ctx.lastMessageAt !== void 0) {
5459
5486
  const prev = new Date(ctx.lastMessageAt);
5460
- diff = formatDiff([
5487
+ diff2 = formatDiff([
5461
5488
  diffLine("date", formatDateKey(prev, tz), currentDate),
5462
5489
  diffLine("day of week", formatDayOfWeek(prev, tz), currentDay)
5463
5490
  ]);
5464
5491
  }
5465
- return `${diff}Date: ${currentDate}
5492
+ return `${diff2}Date: ${currentDate}
5466
5493
  Day of Week: ${currentDay}`;
5467
5494
  },
5468
5495
  { when: dayChanged(options), asPart: false }
@@ -5474,14 +5501,14 @@ function timeReminder(options) {
5474
5501
  const tz = resolveTz(options, ctx);
5475
5502
  const now = /* @__PURE__ */ new Date();
5476
5503
  const currentTime = formatTime(now, tz);
5477
- let diff = "";
5504
+ let diff2 = "";
5478
5505
  if (ctx.lastMessageAt !== void 0) {
5479
5506
  const prev = new Date(ctx.lastMessageAt);
5480
- diff = formatDiff([
5507
+ diff2 = formatDiff([
5481
5508
  diffLine("hour", formatHour(prev, tz), formatHour(now, tz))
5482
5509
  ]);
5483
5510
  }
5484
- return `${diff}Time: ${currentTime}`;
5511
+ return `${diff2}Time: ${currentTime}`;
5485
5512
  },
5486
5513
  { when: hourChanged(options), asPart: false }
5487
5514
  );
@@ -5492,14 +5519,14 @@ function monthReminder(options) {
5492
5519
  const tz = resolveTz(options, ctx);
5493
5520
  const now = /* @__PURE__ */ new Date();
5494
5521
  const currentMonth = formatMonthName(now, tz);
5495
- let diff = "";
5522
+ let diff2 = "";
5496
5523
  if (ctx.lastMessageAt !== void 0) {
5497
5524
  const prev = new Date(ctx.lastMessageAt);
5498
- diff = formatDiff([
5525
+ diff2 = formatDiff([
5499
5526
  diffLine("month", formatMonthName(prev, tz), currentMonth)
5500
5527
  ]);
5501
5528
  }
5502
- return `${diff}Month: ${currentMonth}`;
5529
+ return `${diff2}Month: ${currentMonth}`;
5503
5530
  },
5504
5531
  { when: monthChanged(options), asPart: false }
5505
5532
  );
@@ -5510,14 +5537,14 @@ function yearReminder(options) {
5510
5537
  const tz = resolveTz(options, ctx);
5511
5538
  const now = /* @__PURE__ */ new Date();
5512
5539
  const currentYear = formatYear(now, tz);
5513
- let diff = "";
5540
+ let diff2 = "";
5514
5541
  if (ctx.lastMessageAt !== void 0) {
5515
5542
  const prev = new Date(ctx.lastMessageAt);
5516
- diff = formatDiff([
5543
+ diff2 = formatDiff([
5517
5544
  diffLine("year", formatYear(prev, tz), currentYear)
5518
5545
  ]);
5519
5546
  }
5520
- return `${diff}Year: ${currentYear}`;
5547
+ return `${diff2}Year: ${currentYear}`;
5521
5548
  },
5522
5549
  { when: yearChanged(options), asPart: false }
5523
5550
  );
@@ -5528,14 +5555,14 @@ function seasonReminder(options) {
5528
5555
  const tz = resolveTz(options, ctx);
5529
5556
  const now = /* @__PURE__ */ new Date();
5530
5557
  const currentSeason = getSeason(getMonthIndex(now, tz));
5531
- let diff = "";
5558
+ let diff2 = "";
5532
5559
  if (ctx.lastMessageAt !== void 0) {
5533
5560
  const prev = new Date(ctx.lastMessageAt);
5534
- diff = formatDiff([
5561
+ diff2 = formatDiff([
5535
5562
  diffLine("season", getSeason(getMonthIndex(prev, tz)), currentSeason)
5536
5563
  ]);
5537
5564
  }
5538
- return `${diff}Season: ${currentSeason}`;
5565
+ return `${diff2}Season: ${currentSeason}`;
5539
5566
  },
5540
5567
  { when: seasonChanged(options), asPart: false }
5541
5568
  );
@@ -5553,11 +5580,11 @@ function localeReminder() {
5553
5580
  const current = getLocaleFromMessage(ctx.currentMessage);
5554
5581
  if (!current) return "";
5555
5582
  const last = getLocaleFromMessage(ctx.lastMessage);
5556
- const diff = last ? formatDiff([
5583
+ const diff2 = last ? formatDiff([
5557
5584
  diffLine("language", last.language, current.language),
5558
5585
  diffLine("timezone", last.timeZone, current.timeZone)
5559
5586
  ]) : "";
5560
- return `${diff}Language: ${current.language}
5587
+ return `${diff2}Language: ${current.language}
5561
5588
  Timezone: ${current.timeZone}`;
5562
5589
  },
5563
5590
  { when: whenFn, asPart: false }
@@ -5829,9 +5856,37 @@ function render(tag, ...fragments) {
5829
5856
  return renderer.render([wrapped]);
5830
5857
  }
5831
5858
 
5859
+ // packages/context/src/lib/sandbox/abort.ts
5860
+ import { AsyncLocalStorage } from "node:async_hooks";
5861
+ var ambientAbortSignal = new AsyncLocalStorage();
5862
+ function runWithAbortSignal(signal, fn) {
5863
+ return ambientAbortSignal.run(signal, fn);
5864
+ }
5865
+ function withAbortSignal(sandbox) {
5866
+ const decorated = {
5867
+ ...sandbox,
5868
+ async executeCommand(command, options) {
5869
+ const signal = options?.signal ?? ambientAbortSignal.getStore();
5870
+ return sandbox.executeCommand(
5871
+ command,
5872
+ signal ? { ...options, signal } : options
5873
+ );
5874
+ }
5875
+ };
5876
+ if (sandbox.spawn) {
5877
+ const innerSpawn = sandbox.spawn.bind(sandbox);
5878
+ decorated.spawn = (command, options) => {
5879
+ const signal = options?.signal ?? ambientAbortSignal.getStore();
5880
+ return innerSpawn(command, signal ? { ...options, signal } : options);
5881
+ };
5882
+ }
5883
+ return decorated;
5884
+ }
5885
+
5832
5886
  // packages/context/src/lib/sandbox/agent-os-sandbox.ts
5833
5887
  import "bash-tool";
5834
- var textDecoder = new TextDecoder();
5888
+ import { PassThrough, Readable } from "node:stream";
5889
+ var decoder = new TextDecoder();
5835
5890
  var AgentOsSandboxError = class extends Error {
5836
5891
  constructor(message2) {
5837
5892
  super(message2);
@@ -5863,6 +5918,49 @@ async function importAgentOs() {
5863
5918
  );
5864
5919
  }
5865
5920
  }
5921
+ var SIGKILL_EXIT_CODE = 9;
5922
+ function startKernelProcess(os, command, options) {
5923
+ const { pid } = os.spawn("sh", ["-c", command], options);
5924
+ const stdout = new PassThrough();
5925
+ const stderr = new PassThrough();
5926
+ const unsubOut = os.onProcessStdout(pid, (chunk) => stdout.write(chunk));
5927
+ const unsubErr = os.onProcessStderr(pid, (chunk) => stderr.write(chunk));
5928
+ let killSignalled = false;
5929
+ const exit = os.waitProcess(pid).finally(() => {
5930
+ unsubOut();
5931
+ unsubErr();
5932
+ stdout.end();
5933
+ stderr.end();
5934
+ });
5935
+ return {
5936
+ pid,
5937
+ stdout,
5938
+ stderr,
5939
+ exit,
5940
+ kill: () => {
5941
+ if (killSignalled) return;
5942
+ killSignalled = true;
5943
+ os.killProcess(pid);
5944
+ },
5945
+ wasKilled: (exitCode) => killSignalled && exitCode === SIGKILL_EXIT_CODE
5946
+ };
5947
+ }
5948
+ async function readAll(stream) {
5949
+ const chunks = [];
5950
+ for await (const chunk of stream) chunks.push(chunk);
5951
+ return Buffer.concat(chunks).toString("utf8");
5952
+ }
5953
+ function bindAbort(signal, onAbort) {
5954
+ if (!signal) return () => {
5955
+ };
5956
+ if (signal.aborted) {
5957
+ onAbort();
5958
+ return () => {
5959
+ };
5960
+ }
5961
+ signal.addEventListener("abort", onAbort, { once: true });
5962
+ return () => signal.removeEventListener("abort", onAbort);
5963
+ }
5866
5964
  async function createAgentOsSandbox(options = {}) {
5867
5965
  const { AgentOs } = await importAgentOs();
5868
5966
  let os;
@@ -5873,27 +5971,56 @@ async function createAgentOsSandbox(options = {}) {
5873
5971
  throw new AgentOsCreationError(err.message, err);
5874
5972
  }
5875
5973
  return {
5876
- async executeCommand(command) {
5974
+ async executeCommand(command, { signal } = {}) {
5975
+ if (signal?.aborted) {
5976
+ return { stdout: "", stderr: "", exitCode: SIGKILL_EXIT_CODE };
5977
+ }
5978
+ const proc = startKernelProcess(os, command, {});
5979
+ const unbind = bindAbort(signal, proc.kill);
5877
5980
  try {
5878
- const result = await os.exec(command);
5981
+ const [stdout, stderr, exitCode] = await Promise.all([
5982
+ readAll(proc.stdout),
5983
+ readAll(proc.stderr),
5984
+ proc.exit
5985
+ ]);
5986
+ return { stdout, stderr, exitCode };
5987
+ } finally {
5988
+ unbind();
5989
+ }
5990
+ },
5991
+ spawn(command, { signal, env, cwd } = {}) {
5992
+ if (signal?.aborted) {
5993
+ const empty = () => new ReadableStream({ start: (c) => c.close() });
5879
5994
  return {
5880
- stdout: result.stdout,
5881
- stderr: result.stderr,
5882
- exitCode: result.exitCode
5995
+ stdout: empty(),
5996
+ stderr: empty(),
5997
+ exit: Promise.resolve({
5998
+ code: null,
5999
+ signal: "SIGKILL",
6000
+ success: false
6001
+ })
5883
6002
  };
5884
- } catch (error) {
5885
- const err = error;
6003
+ }
6004
+ const proc = startKernelProcess(os, command, { env, cwd });
6005
+ const unbind = bindAbort(signal, proc.kill);
6006
+ const exit = proc.exit.then((code) => {
6007
+ const killed = proc.wasKilled(code);
5886
6008
  return {
5887
- stdout: err.stdout || "",
5888
- stderr: err.stderr || err.message || "",
5889
- exitCode: err.exitCode ?? 1
6009
+ code: killed ? null : code,
6010
+ signal: killed ? "SIGKILL" : null,
6011
+ success: !killed && code === 0
5890
6012
  };
5891
- }
6013
+ }).finally(unbind);
6014
+ return {
6015
+ stdout: Readable.toWeb(proc.stdout),
6016
+ stderr: Readable.toWeb(proc.stderr),
6017
+ exit
6018
+ };
5892
6019
  },
5893
6020
  async readFile(path5) {
5894
6021
  try {
5895
6022
  const bytes = await os.readFile(path5);
5896
- return textDecoder.decode(bytes);
6023
+ return decoder.decode(bytes);
5897
6024
  } catch (error) {
5898
6025
  throw new Error(
5899
6026
  `Failed to read file "${path5}": ${error instanceof Error ? error.message : String(error)}`
@@ -5935,8 +6062,8 @@ var BashException = class extends Error {
5935
6062
  };
5936
6063
 
5937
6064
  // packages/context/src/lib/sandbox/bash-meta.ts
5938
- import { AsyncLocalStorage } from "node:async_hooks";
5939
- var store = new AsyncLocalStorage();
6065
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
6066
+ var store = new AsyncLocalStorage2();
5940
6067
  function runWithBashMeta(fn) {
5941
6068
  return store.run({ hidden: {} }, fn);
5942
6069
  }
@@ -5966,6 +6093,160 @@ import {
5966
6093
  } from "bash-tool";
5967
6094
  import z3 from "zod";
5968
6095
 
6096
+ // packages/context/src/lib/sandbox/file-events.ts
6097
+ var SnapshotFailedError = class extends Error {
6098
+ stderr;
6099
+ constructor(message2, stderr) {
6100
+ super(message2);
6101
+ this.name = "SnapshotFailedError";
6102
+ this.stderr = stderr;
6103
+ }
6104
+ };
6105
+ function shellQuote(value) {
6106
+ return `'${value.replace(/'/g, `'\\''`)}'`;
6107
+ }
6108
+ async function snapshot(execute, destination) {
6109
+ const probe = await execute(`[ -d ${shellQuote(destination)} ]`);
6110
+ if (probe.exitCode !== 0) return /* @__PURE__ */ new Map();
6111
+ const list = await execute(
6112
+ `find ${shellQuote(destination)} -type f -print0 | while IFS= read -r -d '' p; do sha256sum "$p"; done`
6113
+ );
6114
+ if (list.exitCode !== 0) {
6115
+ throw new SnapshotFailedError(
6116
+ `snapshot failed for ${destination}`,
6117
+ list.stderr
6118
+ );
6119
+ }
6120
+ const snap = /* @__PURE__ */ new Map();
6121
+ if (!list.stdout) return snap;
6122
+ for (const line of list.stdout.split("\n")) {
6123
+ if (line.length < 66) continue;
6124
+ snap.set(line.slice(66), line.slice(0, 64));
6125
+ }
6126
+ return snap;
6127
+ }
6128
+ function lazyReadable(innerPromise) {
6129
+ let reader = null;
6130
+ return new ReadableStream({
6131
+ async pull(controller) {
6132
+ if (!reader) {
6133
+ const inner = await innerPromise;
6134
+ reader = inner.getReader();
6135
+ }
6136
+ const { done, value } = await reader.read();
6137
+ if (done) controller.close();
6138
+ else controller.enqueue(value);
6139
+ },
6140
+ async cancel(reason) {
6141
+ if (reader) {
6142
+ await reader.cancel(reason);
6143
+ } else {
6144
+ const inner = await innerPromise;
6145
+ await inner.cancel(reason);
6146
+ }
6147
+ }
6148
+ });
6149
+ }
6150
+ function diff(before, after) {
6151
+ const events = [];
6152
+ const ts = Date.now();
6153
+ for (const [path5, hash] of after) {
6154
+ const prior = before.get(path5);
6155
+ if (prior === void 0) {
6156
+ events.push({ path: path5, op: "write", timestamp: ts });
6157
+ } else if (prior !== hash) {
6158
+ events.push({ path: path5, op: "modify", timestamp: ts });
6159
+ }
6160
+ }
6161
+ for (const path5 of before.keys()) {
6162
+ if (!after.has(path5)) {
6163
+ events.push({ path: path5, op: "delete", timestamp: ts });
6164
+ }
6165
+ }
6166
+ return events;
6167
+ }
6168
+ function observeSandboxFileEvents(sandbox, options) {
6169
+ const { destination } = options;
6170
+ if (!destination) {
6171
+ throw new Error("observeSandboxFileEvents: destination is required");
6172
+ }
6173
+ const innerExecute = sandbox.executeCommand.bind(sandbox);
6174
+ const innerReadFile = sandbox.readFile.bind(sandbox);
6175
+ const innerWriteFiles = sandbox.writeFiles.bind(sandbox);
6176
+ const buffer = [];
6177
+ const observe = async (fn) => {
6178
+ const before = await snapshot(innerExecute, destination);
6179
+ const takeAfter = async () => {
6180
+ const after = await snapshot(innerExecute, destination);
6181
+ buffer.push(...diff(before, after));
6182
+ };
6183
+ try {
6184
+ const result = await fn();
6185
+ await takeAfter();
6186
+ return result;
6187
+ } catch (err) {
6188
+ await takeAfter().catch(() => {
6189
+ });
6190
+ throw err;
6191
+ }
6192
+ };
6193
+ const decorated = {
6194
+ async executeCommand(command, options2) {
6195
+ return observe(() => innerExecute(command, options2));
6196
+ },
6197
+ async readFile(path5) {
6198
+ const content = await innerReadFile(path5);
6199
+ buffer.push({ path: path5, op: "read", timestamp: Date.now() });
6200
+ return content;
6201
+ },
6202
+ async writeFiles(files) {
6203
+ await observe(() => innerWriteFiles(files));
6204
+ },
6205
+ dispose: sandbox.dispose.bind(sandbox)
6206
+ };
6207
+ if (sandbox.spawn) {
6208
+ const innerSpawn = sandbox.spawn.bind(sandbox);
6209
+ decorated.spawn = (command, options2) => {
6210
+ const started = (async () => {
6211
+ const before = await snapshot(innerExecute, destination).catch(
6212
+ () => /* @__PURE__ */ new Map()
6213
+ );
6214
+ const child = innerSpawn(command, options2);
6215
+ return { before, child };
6216
+ })();
6217
+ const exit = (async () => {
6218
+ const { before, child } = await started;
6219
+ try {
6220
+ return await child.exit;
6221
+ } finally {
6222
+ try {
6223
+ const after = await snapshot(innerExecute, destination);
6224
+ buffer.push(...diff(before, after));
6225
+ } catch {
6226
+ }
6227
+ }
6228
+ })();
6229
+ const stdoutPromise = started.then((s) => s.child.stdout);
6230
+ const stderrPromise = started.then((s) => s.child.stderr);
6231
+ stdoutPromise.catch(() => {
6232
+ });
6233
+ stderrPromise.catch(() => {
6234
+ });
6235
+ return {
6236
+ stdout: lazyReadable(stdoutPromise),
6237
+ stderr: lazyReadable(stderrPromise),
6238
+ exit
6239
+ };
6240
+ };
6241
+ }
6242
+ return {
6243
+ sandbox: decorated,
6244
+ drain() {
6245
+ return buffer.splice(0, buffer.length);
6246
+ }
6247
+ };
6248
+ }
6249
+
5969
6250
  // packages/context/src/lib/sandbox/upload-skills.ts
5970
6251
  import * as path3 from "node:path";
5971
6252
 
@@ -6111,22 +6392,38 @@ async function uploadSkills(sandbox, inputs) {
6111
6392
 
6112
6393
  // packages/context/src/lib/sandbox/bash-tool.ts
6113
6394
  var REASONING_INSTRUCTION = 'Every bash tool call must include a brief non-empty "reasoning" input explaining why the command is needed.';
6114
- async function createBashTool(options = {}) {
6115
- const { skills: skillInputs = [], extraInstructions, ...rest } = options;
6395
+ function withBashExceptionCatch(sandbox) {
6396
+ return {
6397
+ ...sandbox,
6398
+ async executeCommand(command, options) {
6399
+ try {
6400
+ return await sandbox.executeCommand(command, options);
6401
+ } catch (err) {
6402
+ if (err instanceof BashException) return err.format();
6403
+ throw err;
6404
+ }
6405
+ }
6406
+ };
6407
+ }
6408
+ async function createBashTool(options) {
6409
+ const {
6410
+ skills: skillInputs = [],
6411
+ extraInstructions,
6412
+ sandbox: backend,
6413
+ destination,
6414
+ ...rest
6415
+ } = options;
6416
+ const observer = observeSandboxFileEvents(backend, {
6417
+ destination: destination ?? "/workspace"
6418
+ });
6419
+ const sandbox = withAbortSignal(withBashExceptionCatch(observer.sandbox));
6116
6420
  const combinedInstructions = [extraInstructions, REASONING_INSTRUCTION].filter(Boolean).join("\n\n");
6117
6421
  const toolkit = await externalCreateBashTool({
6118
6422
  ...rest,
6423
+ sandbox,
6424
+ destination,
6119
6425
  extraInstructions: combinedInstructions
6120
6426
  });
6121
- const innerExecute = toolkit.sandbox.executeCommand.bind(toolkit.sandbox);
6122
- toolkit.sandbox.executeCommand = async (cmd) => {
6123
- try {
6124
- return await innerExecute(cmd);
6125
- } catch (err) {
6126
- if (err instanceof BashException) return err.format();
6127
- throw err;
6128
- }
6129
- };
6130
6427
  const upstreamBash = toolkit.bash;
6131
6428
  const originalExecute = upstreamBash.execute;
6132
6429
  const toolBuilder = tool2;
@@ -6141,197 +6438,43 @@ async function createBashTool(options = {}) {
6141
6438
  if (!originalExecute) {
6142
6439
  throw new Error("bash tool execution is not available");
6143
6440
  }
6144
- return runWithBashMeta(async () => {
6145
- let result;
6146
- try {
6147
- result = await originalExecute({ command }, execOptions);
6148
- } catch (err) {
6149
- if (err instanceof BashException) {
6150
- result = err.format();
6151
- } else {
6152
- throw err;
6153
- }
6154
- }
6155
- const state = readBashMeta();
6156
- if (!state) return result;
6157
- const hasHidden = Object.keys(state.hidden).length > 0;
6158
- const hasReminder = state.reminder !== void 0;
6159
- if (!hasHidden && !hasReminder) return result;
6160
- return {
6161
- ...result,
6162
- ...hasHidden ? { meta: state.hidden } : {},
6163
- ...hasReminder ? { reminder: state.reminder } : {}
6164
- };
6165
- });
6441
+ const { abortSignal } = execOptions;
6442
+ return runWithAbortSignal(
6443
+ abortSignal,
6444
+ () => runWithBashMeta(async () => {
6445
+ const result = await originalExecute({ command }, execOptions);
6446
+ const state = readBashMeta();
6447
+ if (!state) return result;
6448
+ const hasHidden = Object.keys(state.hidden).length > 0;
6449
+ const hasReminder = state.reminder !== void 0;
6450
+ if (!hasHidden && !hasReminder) return result;
6451
+ return {
6452
+ ...result,
6453
+ ...hasHidden ? { meta: state.hidden } : {},
6454
+ ...hasReminder ? { reminder: state.reminder } : {}
6455
+ };
6456
+ })
6457
+ );
6166
6458
  },
6167
6459
  toModelOutput: ({ output }) => {
6168
6460
  if (typeof output !== "object" || output === null) {
6169
6461
  return { type: "json", value: output };
6170
6462
  }
6171
- const record = output;
6172
- if (!("meta" in record)) {
6173
- return { type: "json", value: record };
6174
- }
6175
- const { meta: _meta, ...visible } = record;
6463
+ const { meta: _meta, ...visible } = output;
6176
6464
  return { type: "json", value: visible };
6177
6465
  }
6178
6466
  });
6179
- const skills2 = await uploadSkills(toolkit.sandbox, skillInputs);
6467
+ const skills2 = await uploadSkills(sandbox, skillInputs);
6180
6468
  return {
6181
6469
  ...toolkit,
6470
+ sandbox,
6182
6471
  bash,
6183
6472
  tools: { ...toolkit.tools, bash },
6184
- skills: skills2
6473
+ skills: skills2,
6474
+ drainFileEvents: () => observer.drain()
6185
6475
  };
6186
6476
  }
6187
6477
 
6188
- // packages/context/src/lib/sandbox/file-events.ts
6189
- var ObservedFs = class {
6190
- #base;
6191
- #events = [];
6192
- readdirWithFileTypes;
6193
- constructor(base) {
6194
- this.#base = base;
6195
- if (base.readdirWithFileTypes) {
6196
- this.readdirWithFileTypes = (path5) => base.readdirWithFileTypes(path5);
6197
- }
6198
- }
6199
- drain() {
6200
- const events = this.#events;
6201
- this.#events = [];
6202
- return events;
6203
- }
6204
- #record(op, path5) {
6205
- this.#events.push({ path: path5, op, timestamp: Date.now() });
6206
- }
6207
- async readFile(path5, options) {
6208
- const content = await this.#base.readFile(path5, options);
6209
- this.#record("read", path5);
6210
- return content;
6211
- }
6212
- async readFileBuffer(path5) {
6213
- const content = await this.#base.readFileBuffer(path5);
6214
- this.#record("read", path5);
6215
- return content;
6216
- }
6217
- async writeFile(path5, content, options) {
6218
- const existed = await this.#base.exists(path5);
6219
- await this.#base.writeFile(path5, content, options);
6220
- this.#record(existed ? "modify" : "write", path5);
6221
- }
6222
- async appendFile(path5, content, options) {
6223
- const existed = await this.#base.exists(path5);
6224
- await this.#base.appendFile(path5, content, options);
6225
- this.#record(existed ? "modify" : "write", path5);
6226
- }
6227
- async rm(path5, options) {
6228
- const toRecord = options?.recursive ? await this.#walk(path5, { includeRoot: true }) : await this.#base.exists(path5) ? [path5] : [];
6229
- await this.#base.rm(path5, options);
6230
- for (const p of toRecord) {
6231
- this.#record("delete", p);
6232
- }
6233
- }
6234
- async cp(src, dest, options) {
6235
- if (options?.recursive) {
6236
- const sources = await this.#walk(src, { includeRoot: false });
6237
- const destChecks = await Promise.all(
6238
- sources.map(async (srcFile) => {
6239
- const relative3 = srcFile.slice(src.length);
6240
- const destFile = this.#joinPath(dest, relative3);
6241
- return {
6242
- srcFile,
6243
- destFile,
6244
- existed: await this.#base.exists(destFile)
6245
- };
6246
- })
6247
- );
6248
- await this.#base.cp(src, dest, options);
6249
- for (const { srcFile, destFile, existed } of destChecks) {
6250
- this.#record("read", srcFile);
6251
- this.#record(existed ? "modify" : "write", destFile);
6252
- }
6253
- return;
6254
- }
6255
- const destExisted = await this.#base.exists(dest);
6256
- await this.#base.cp(src, dest, options);
6257
- this.#record("read", src);
6258
- this.#record(destExisted ? "modify" : "write", dest);
6259
- }
6260
- async #walk(root, { includeRoot }) {
6261
- if (!await this.#base.exists(root)) return [];
6262
- const stat = await this.#base.stat(root);
6263
- if (!stat.isDirectory) return [root];
6264
- const out = [];
6265
- const visit = async (dir) => {
6266
- const entries = await this.#base.readdir(dir);
6267
- for (const name of entries) {
6268
- const child = this.#joinPath(dir, name);
6269
- const childStat = await this.#base.stat(child);
6270
- if (childStat.isDirectory) {
6271
- await visit(child);
6272
- } else {
6273
- out.push(child);
6274
- }
6275
- }
6276
- };
6277
- await visit(root);
6278
- if (includeRoot) out.push(root);
6279
- return out;
6280
- }
6281
- #joinPath(a, b) {
6282
- if (!b) return a;
6283
- if (b.startsWith("/")) return `${a.replace(/\/$/, "")}${b}`;
6284
- return a.endsWith("/") ? `${a}${b}` : `${a}/${b}`;
6285
- }
6286
- async mv(src, dest) {
6287
- const destExisted = await this.#base.exists(dest);
6288
- await this.#base.mv(src, dest);
6289
- this.#record("delete", src);
6290
- this.#record(destExisted ? "modify" : "write", dest);
6291
- }
6292
- async symlink(target, linkPath) {
6293
- await this.#base.symlink(target, linkPath);
6294
- this.#record("write", linkPath);
6295
- }
6296
- async link(existingPath, newPath) {
6297
- await this.#base.link(existingPath, newPath);
6298
- this.#record("write", newPath);
6299
- }
6300
- mkdir(path5, options) {
6301
- return this.#base.mkdir(path5, options);
6302
- }
6303
- exists(path5) {
6304
- return this.#base.exists(path5);
6305
- }
6306
- stat(path5) {
6307
- return this.#base.stat(path5);
6308
- }
6309
- lstat(path5) {
6310
- return this.#base.lstat(path5);
6311
- }
6312
- readdir(path5) {
6313
- return this.#base.readdir(path5);
6314
- }
6315
- readlink(path5) {
6316
- return this.#base.readlink(path5);
6317
- }
6318
- realpath(path5) {
6319
- return this.#base.realpath(path5);
6320
- }
6321
- chmod(path5, mode) {
6322
- return this.#base.chmod(path5, mode);
6323
- }
6324
- utimes(path5, atime, mtime) {
6325
- return this.#base.utimes(path5, atime, mtime);
6326
- }
6327
- resolvePath(base, path5) {
6328
- return this.#base.resolvePath(base, path5);
6329
- }
6330
- getAllPaths() {
6331
- return this.#base.getAllPaths();
6332
- }
6333
- };
6334
-
6335
6478
  // packages/context/src/lib/sandbox/binary-bridges.ts
6336
6479
  import { existsSync as existsSync2 } from "fs";
6337
6480
  import { defineCommand } from "just-bash";
@@ -6428,113 +6571,647 @@ function resolveRealCwd(ctx) {
6428
6571
  return realCwd;
6429
6572
  }
6430
6573
 
6431
- // packages/context/src/lib/sandbox/docker-sandbox.ts
6574
+ // packages/context/src/lib/sandbox/daytona-sandbox.ts
6432
6575
  import "bash-tool";
6433
- import spawn3 from "nano-spawn";
6434
- import { createHash } from "node:crypto";
6435
- import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
6436
-
6437
- // packages/context/src/lib/sandbox/docker-sandbox-errors.ts
6438
- var DockerSandboxError = class extends Error {
6439
- containerId;
6440
- constructor(message2, containerId) {
6576
+ import { randomUUID } from "node:crypto";
6577
+ import { createRequire } from "node:module";
6578
+ var DAYTONA_DEFAULT_DESTINATION = "/home/daytona";
6579
+ var DAYTONA_EXIT_POLL_INTERVAL_MS = 250;
6580
+ var DAYTONA_EXIT_POLL_TIMEOUT_MS = 3e4;
6581
+ var requireOptional = createRequire(import.meta.url);
6582
+ var DaytonaSandboxError = class extends Error {
6583
+ constructor(message2, cause) {
6441
6584
  super(message2);
6442
- this.name = "DockerSandboxError";
6443
- this.containerId = containerId;
6444
- }
6445
- };
6446
- var DockerNotAvailableError = class extends DockerSandboxError {
6447
- constructor() {
6448
- super("Docker is not available. Ensure Docker daemon is running.");
6449
- this.name = "DockerNotAvailableError";
6450
- }
6451
- };
6452
- var ContainerCreationError = class extends DockerSandboxError {
6453
- image;
6454
- cause;
6455
- constructor(message2, image, cause) {
6456
- super(`Failed to create container from image "${image}": ${message2}`);
6457
- this.name = "ContainerCreationError";
6458
- this.image = image;
6585
+ this.name = "DaytonaSandboxError";
6459
6586
  this.cause = cause;
6460
6587
  }
6461
6588
  };
6462
- var PackageInstallError = class extends DockerSandboxError {
6463
- packages;
6464
- image;
6465
- packageManager;
6466
- stderr;
6467
- constructor(packages, image, packageManager, stderr, containerId) {
6589
+ var DaytonaNotAvailableError = class extends DaytonaSandboxError {
6590
+ constructor(cause) {
6468
6591
  super(
6469
- `Package installation failed for [${packages.join(", ")}] using ${packageManager} on ${image}: ${stderr}`,
6470
- containerId
6592
+ "@daytona/sdk is not installed. Install it with: npm install @daytona/sdk",
6593
+ cause
6471
6594
  );
6472
- this.name = "PackageInstallError";
6473
- this.packages = packages;
6474
- this.image = image;
6475
- this.packageManager = packageManager;
6476
- this.stderr = stderr;
6595
+ this.name = "DaytonaNotAvailableError";
6477
6596
  }
6478
6597
  };
6479
- var InstallError = class extends DockerSandboxError {
6480
- target;
6481
- source;
6482
- reason;
6483
- url;
6484
- constructor(opts) {
6485
- const where = opts.url ? `${opts.source} (${opts.url})` : opts.source;
6486
- super(
6487
- `Failed to install "${opts.target}" via ${where}: ${opts.reason}`,
6488
- opts.containerId
6489
- );
6490
- this.name = "InstallError";
6491
- this.target = opts.target;
6492
- this.source = opts.source;
6493
- this.reason = opts.reason;
6494
- this.url = opts.url;
6598
+ var DaytonaCreationError = class extends DaytonaSandboxError {
6599
+ constructor(message2, cause) {
6600
+ super(`Failed to create Daytona sandbox: ${message2}`, cause);
6601
+ this.name = "DaytonaCreationError";
6495
6602
  }
6496
6603
  };
6497
- var MissingRuntimeError = class extends DockerSandboxError {
6498
- runtime;
6499
- required;
6500
- constructor(runtime, required, details, containerId) {
6501
- const base = `Required runtime "${runtime}" is not installed (needs: ${required.join(", ")}).`;
6502
- super(details ? `${base} ${details}` : base, containerId);
6503
- this.name = "MissingRuntimeError";
6504
- this.runtime = runtime;
6505
- this.required = required;
6604
+ var DaytonaCommandError = class extends DaytonaSandboxError {
6605
+ constructor(message2, cause) {
6606
+ super(message2, cause);
6607
+ this.name = "DaytonaCommandError";
6506
6608
  }
6507
6609
  };
6508
- var MountPathError = class extends DockerSandboxError {
6509
- hostPath;
6510
- containerPath;
6511
- constructor(hostPath, containerPath) {
6512
- super(
6513
- `Mount path does not exist on host: "${hostPath}" -> "${containerPath}"`
6514
- );
6515
- this.name = "MountPathError";
6516
- this.hostPath = hostPath;
6517
- this.containerPath = containerPath;
6610
+ async function createDaytonaSandbox(options = {}) {
6611
+ validateDaytonaOptions(options);
6612
+ const { Daytona } = await importDaytonaSdk();
6613
+ let client;
6614
+ try {
6615
+ client = new Daytona(createDaytonaConfig(options));
6616
+ } catch (error) {
6617
+ const err = toError(error);
6618
+ throw new DaytonaCreationError(err.message, err);
6518
6619
  }
6519
- };
6520
- var DockerfileBuildError = class extends DockerSandboxError {
6521
- stderr;
6522
- constructor(stderr) {
6523
- super(`Dockerfile build failed: ${stderr}`);
6524
- this.name = "DockerfileBuildError";
6525
- this.stderr = stderr;
6620
+ const attached = options.sandboxId !== void 0;
6621
+ const deleteOnDispose = options.deleteOnDispose ?? !attached;
6622
+ let sandbox;
6623
+ try {
6624
+ sandbox = attached ? await client.get(options.sandboxId) : await client.create(createDaytonaParams(options), {
6625
+ timeout: options.createTimeout,
6626
+ onSnapshotCreateLogs: options.onSnapshotCreateLogs
6627
+ });
6628
+ if (attached && sandbox.state && sandbox.state !== "started") {
6629
+ await sandbox.start?.(options.startTimeout ?? options.createTimeout);
6630
+ }
6631
+ } catch (error) {
6632
+ const err = toError(error);
6633
+ throw new DaytonaCreationError(err.message, err);
6526
6634
  }
6527
- };
6528
- var ComposeStartError = class extends DockerSandboxError {
6529
- composeFile;
6530
- stderr;
6531
- constructor(composeFile, stderr) {
6532
- super(`Docker Compose failed to start: ${stderr}`);
6533
- this.name = "ComposeStartError";
6534
- this.composeFile = composeFile;
6535
- this.stderr = stderr;
6635
+ return createDaytonaSandboxMethods({
6636
+ client,
6637
+ sandbox,
6638
+ commandTimeout: options.commandTimeout,
6639
+ deleteOnDispose,
6640
+ deleteTimeout: options.deleteTimeout
6641
+ });
6642
+ }
6643
+ async function importDaytonaSdk() {
6644
+ try {
6645
+ const mod = requireOptional("@daytona/sdk");
6646
+ if (typeof mod.Daytona !== "function") {
6647
+ throw new DaytonaSandboxError(
6648
+ "@daytona/sdk did not export a Daytona constructor"
6649
+ );
6650
+ }
6651
+ return { Daytona: mod.Daytona };
6652
+ } catch (error) {
6653
+ if (error instanceof DaytonaSandboxError) {
6654
+ throw error;
6655
+ }
6656
+ const err = toError(error);
6657
+ if (isMissingDaytonaSdk(err)) {
6658
+ throw new DaytonaNotAvailableError(err);
6659
+ }
6660
+ throw err;
6536
6661
  }
6537
- };
6662
+ }
6663
+ function createDaytonaConfig(options) {
6664
+ return compactObject({
6665
+ apiKey: options.apiKey,
6666
+ jwtToken: options.jwtToken,
6667
+ organizationId: options.organizationId,
6668
+ apiUrl: options.apiUrl,
6669
+ target: options.target,
6670
+ otelEnabled: options.otelEnabled,
6671
+ _experimental: options.experimental
6672
+ });
6673
+ }
6674
+ function createDaytonaParams(options) {
6675
+ const base = compactObject({
6676
+ name: options.name,
6677
+ user: options.user,
6678
+ language: options.language,
6679
+ envVars: options.envVars,
6680
+ labels: options.labels,
6681
+ public: options.public,
6682
+ autoStopInterval: options.autoStopInterval,
6683
+ autoArchiveInterval: options.autoArchiveInterval,
6684
+ autoDeleteInterval: options.autoDeleteInterval,
6685
+ volumes: options.volumes,
6686
+ networkBlockAll: options.networkBlockAll,
6687
+ networkAllowList: options.networkAllowList,
6688
+ ephemeral: options.ephemeral
6689
+ });
6690
+ if (options.image) {
6691
+ return compactObject({
6692
+ ...base,
6693
+ image: options.image,
6694
+ resources: options.resources
6695
+ });
6696
+ }
6697
+ const params = compactObject({
6698
+ ...base,
6699
+ snapshot: options.snapshot
6700
+ });
6701
+ return Object.keys(params).length > 0 ? params : void 0;
6702
+ }
6703
+ function validateDaytonaOptions(options) {
6704
+ if (options.image && options.snapshot) {
6705
+ throw new DaytonaSandboxError(
6706
+ 'Daytona sandbox options cannot include both "image" and "snapshot". Choose one environment source.'
6707
+ );
6708
+ }
6709
+ if (options.resources && !options.image) {
6710
+ throw new DaytonaSandboxError(
6711
+ 'Daytona sandbox options can only include "resources" when creating from "image". The Daytona SDK does not apply resources during default or snapshot creation.'
6712
+ );
6713
+ }
6714
+ if (!options.sandboxId) {
6715
+ return;
6716
+ }
6717
+ const creationOnlyFields = [
6718
+ "name",
6719
+ "user",
6720
+ "snapshot",
6721
+ "image",
6722
+ "language",
6723
+ "envVars",
6724
+ "labels",
6725
+ "public",
6726
+ "resources",
6727
+ "volumes",
6728
+ "networkAllowList",
6729
+ "networkBlockAll",
6730
+ "autoStopInterval",
6731
+ "autoArchiveInterval",
6732
+ "autoDeleteInterval",
6733
+ "ephemeral",
6734
+ "onSnapshotCreateLogs"
6735
+ ];
6736
+ const present = creationOnlyFields.filter((field) => {
6737
+ return options[field] !== void 0;
6738
+ });
6739
+ if (present.length > 0) {
6740
+ throw new DaytonaSandboxError(
6741
+ `Daytona sandbox options cannot combine "sandboxId" with creation options: ${present.join(", ")}`
6742
+ );
6743
+ }
6744
+ }
6745
+ function createDaytonaSandboxMethods(args) {
6746
+ const { client, sandbox, commandTimeout, deleteOnDispose, deleteTimeout } = args;
6747
+ let disposed = false;
6748
+ const executeCommand = async (command, options) => {
6749
+ if (options?.signal?.aborted) {
6750
+ return abortedCommandResult();
6751
+ }
6752
+ let aborted = false;
6753
+ const abort = () => {
6754
+ aborted = true;
6755
+ };
6756
+ options?.signal?.addEventListener("abort", abort, { once: true });
6757
+ try {
6758
+ if (aborted || options?.signal?.aborted) {
6759
+ return abortedCommandResult();
6760
+ }
6761
+ const response = await sandbox.process.executeCommand(
6762
+ command,
6763
+ void 0,
6764
+ void 0,
6765
+ commandTimeout
6766
+ );
6767
+ if (aborted) return abortedCommandResult();
6768
+ return {
6769
+ stdout: response.result ?? response.artifacts?.stdout ?? "",
6770
+ stderr: "",
6771
+ exitCode: response.exitCode ?? 0
6772
+ };
6773
+ } catch (error) {
6774
+ if (aborted) return abortedCommandResult();
6775
+ const err = error;
6776
+ return {
6777
+ stdout: err.stdout ?? "",
6778
+ stderr: err.stderr ?? err.message ?? String(error),
6779
+ exitCode: err.exitCode ?? 1
6780
+ };
6781
+ } finally {
6782
+ options?.signal?.removeEventListener("abort", abort);
6783
+ }
6784
+ };
6785
+ return {
6786
+ executeCommand,
6787
+ spawn(command, options) {
6788
+ return spawnDaytonaProcess(sandbox, command, {
6789
+ ...options,
6790
+ commandTimeout
6791
+ });
6792
+ },
6793
+ async readFile(path5) {
6794
+ try {
6795
+ const bytes = await sandbox.fs.downloadFile(path5);
6796
+ return Buffer.from(bytes).toString("utf-8");
6797
+ } catch (error) {
6798
+ throw new DaytonaCommandError(
6799
+ `Failed to read file "${path5}": ${toError(error).message}`,
6800
+ toError(error)
6801
+ );
6802
+ }
6803
+ },
6804
+ async writeFiles(files) {
6805
+ try {
6806
+ for (const dir of uniqueParentDirectories(files.map((f) => f.path))) {
6807
+ const result = await executeCommand(`mkdir -p ${shellQuote2(dir)}`);
6808
+ if (result.exitCode !== 0) {
6809
+ throw new DaytonaCommandError(
6810
+ `Failed to create directory "${dir}": ${result.stderr}`
6811
+ );
6812
+ }
6813
+ }
6814
+ await sandbox.fs.uploadFiles(
6815
+ files.map((file) => ({
6816
+ source: Buffer.from(file.content),
6817
+ destination: file.path
6818
+ }))
6819
+ );
6820
+ } catch (error) {
6821
+ if (error instanceof DaytonaSandboxError) throw error;
6822
+ const err = toError(error);
6823
+ throw new DaytonaCommandError(
6824
+ `Failed to write files: ${err.message}`,
6825
+ err
6826
+ );
6827
+ }
6828
+ },
6829
+ async dispose() {
6830
+ if (disposed) return;
6831
+ disposed = true;
6832
+ try {
6833
+ if (deleteOnDispose) {
6834
+ if (sandbox.delete) await sandbox.delete(deleteTimeout);
6835
+ else if (client.delete) await client.delete(sandbox, deleteTimeout);
6836
+ }
6837
+ } finally {
6838
+ await client[Symbol.asyncDispose]?.().catch(() => {
6839
+ });
6840
+ }
6841
+ }
6842
+ };
6843
+ }
6844
+ function spawnDaytonaProcess(sandbox, command, options = {}) {
6845
+ const sessionId = createSessionId("spawn");
6846
+ const stdout = createTextReadable();
6847
+ const stderr = createTextReadable();
6848
+ const exit = runSpawnedSession({
6849
+ sandbox,
6850
+ sessionId,
6851
+ command: buildSessionCommand(command, options),
6852
+ signal: options.signal,
6853
+ commandTimeout: options.commandTimeout,
6854
+ stdout,
6855
+ stderr
6856
+ });
6857
+ return {
6858
+ stdout: stdout.stream,
6859
+ stderr: stderr.stream,
6860
+ exit
6861
+ };
6862
+ }
6863
+ async function runSpawnedSession(args) {
6864
+ const {
6865
+ sandbox,
6866
+ sessionId,
6867
+ command,
6868
+ signal,
6869
+ commandTimeout,
6870
+ stdout,
6871
+ stderr
6872
+ } = args;
6873
+ let sessionCreated = false;
6874
+ let aborted = signal?.aborted ?? false;
6875
+ let resolveAbort;
6876
+ const abortPromise = new Promise((resolve4) => {
6877
+ resolveAbort = () => resolve4("aborted");
6878
+ });
6879
+ const abort = () => {
6880
+ aborted = true;
6881
+ resolveAbort?.();
6882
+ if (sessionCreated) {
6883
+ sandbox.process.deleteSession(sessionId).catch(() => {
6884
+ });
6885
+ }
6886
+ };
6887
+ if (signal) {
6888
+ if (signal.aborted) abort();
6889
+ else signal.addEventListener("abort", abort, { once: true });
6890
+ }
6891
+ try {
6892
+ await sandbox.process.createSession(sessionId);
6893
+ sessionCreated = true;
6894
+ if (aborted) return abortedExitInfo();
6895
+ const response = await sandbox.process.executeSessionCommand(
6896
+ sessionId,
6897
+ {
6898
+ command,
6899
+ runAsync: true,
6900
+ suppressInputEcho: true
6901
+ },
6902
+ commandTimeout
6903
+ );
6904
+ const commandId = response.cmdId;
6905
+ if (!commandId) {
6906
+ throw new DaytonaCommandError(
6907
+ "Daytona did not return a command id for the spawned session command."
6908
+ );
6909
+ }
6910
+ if (aborted) return abortedExitInfo();
6911
+ const logsTask = sandbox.process.getSessionCommandLogs(
6912
+ sessionId,
6913
+ commandId,
6914
+ (chunk) => stdout.enqueue(chunk),
6915
+ (chunk) => stderr.enqueue(chunk)
6916
+ );
6917
+ logsTask.catch(() => {
6918
+ });
6919
+ const winner = await Promise.race([
6920
+ logsTask.then(() => "logs"),
6921
+ abortPromise
6922
+ ]);
6923
+ if (winner === "aborted") {
6924
+ await sandbox.process.deleteSession(sessionId).catch(() => {
6925
+ });
6926
+ return abortedExitInfo();
6927
+ }
6928
+ await logsTask;
6929
+ const code = await waitForSessionCommandExitCode({
6930
+ sandbox,
6931
+ sessionId,
6932
+ commandId,
6933
+ timeoutMs: sessionExitPollTimeoutMs(commandTimeout),
6934
+ isAborted: () => aborted
6935
+ });
6936
+ return { code, signal: null, success: code === 0 };
6937
+ } catch (error) {
6938
+ if (aborted) return abortedExitInfo();
6939
+ const err = toError(error);
6940
+ stdout.error(err);
6941
+ stderr.error(err);
6942
+ throw err;
6943
+ } finally {
6944
+ if (signal) {
6945
+ signal.removeEventListener("abort", abort);
6946
+ }
6947
+ stdout.close();
6948
+ stderr.close();
6949
+ if (sessionCreated) {
6950
+ await sandbox.process.deleteSession(sessionId).catch(() => {
6951
+ });
6952
+ }
6953
+ }
6954
+ }
6955
+ async function waitForSessionCommandExitCode(args) {
6956
+ const { sandbox, sessionId, commandId, timeoutMs, isAborted } = args;
6957
+ const deadline = Date.now() + timeoutMs;
6958
+ while (true) {
6959
+ if (isAborted()) throw new DaytonaCommandError("Daytona command aborted.");
6960
+ const info = await sandbox.process.getSessionCommand(sessionId, commandId);
6961
+ if (typeof info.exitCode === "number") {
6962
+ return info.exitCode;
6963
+ }
6964
+ if (Date.now() >= deadline) {
6965
+ throw new DaytonaCommandError(
6966
+ `Daytona session command "${commandId}" logs closed before an exit code became available.`
6967
+ );
6968
+ }
6969
+ await delay(DAYTONA_EXIT_POLL_INTERVAL_MS);
6970
+ }
6971
+ }
6972
+ function sessionExitPollTimeoutMs(commandTimeout) {
6973
+ if (commandTimeout === void 0 || commandTimeout === 0) {
6974
+ return DAYTONA_EXIT_POLL_TIMEOUT_MS;
6975
+ }
6976
+ return Math.max(commandTimeout * 1e3, DAYTONA_EXIT_POLL_TIMEOUT_MS);
6977
+ }
6978
+ function delay(ms) {
6979
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
6980
+ }
6981
+ function createTextReadable() {
6982
+ const encoder = new TextEncoder();
6983
+ let controller;
6984
+ let closed = false;
6985
+ return {
6986
+ stream: new ReadableStream({
6987
+ start(streamController) {
6988
+ controller = streamController;
6989
+ }
6990
+ }),
6991
+ enqueue(chunk) {
6992
+ if (closed || !chunk) return;
6993
+ controller?.enqueue(encoder.encode(chunk));
6994
+ },
6995
+ close() {
6996
+ if (closed) return;
6997
+ closed = true;
6998
+ controller?.close();
6999
+ },
7000
+ error(error) {
7001
+ if (closed) return;
7002
+ closed = true;
7003
+ controller?.error(error);
7004
+ }
7005
+ };
7006
+ }
7007
+ function buildSessionCommand(command, options) {
7008
+ const cwdPrefix = options.cwd ? `cd ${shellQuote2(options.cwd)} && ` : "";
7009
+ const env = options.env ?? {};
7010
+ const entries = Object.entries(env);
7011
+ if (entries.length === 0) {
7012
+ return `sh -lc ${shellQuote2(`${cwdPrefix}${command}`)}`;
7013
+ }
7014
+ for (const [key] of entries) {
7015
+ validateEnvKey(key);
7016
+ }
7017
+ const exports = entries.map(([key, value]) => `export ${key}=${shellQuote2(value)}`).join("; ");
7018
+ return `sh -lc ${shellQuote2(`${exports}; ${cwdPrefix}${command}`)}`;
7019
+ }
7020
+ function validateEnvKey(key) {
7021
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
7022
+ throw new DaytonaSandboxError(
7023
+ `Invalid environment variable key: "${key}". Use shell-compatible environment variable names.`
7024
+ );
7025
+ }
7026
+ }
7027
+ function createSessionId(kind) {
7028
+ return `deepagents-${kind}-${randomUUID()}`;
7029
+ }
7030
+ function abortedCommandResult() {
7031
+ return {
7032
+ stdout: "",
7033
+ stderr: "Command aborted",
7034
+ exitCode: 1
7035
+ };
7036
+ }
7037
+ function abortedExitInfo() {
7038
+ return {
7039
+ code: null,
7040
+ signal: "SIGKILL",
7041
+ success: false
7042
+ };
7043
+ }
7044
+ function shellQuote2(value) {
7045
+ return `'${value.replace(/'/g, `'\\''`)}'`;
7046
+ }
7047
+ function uniqueParentDirectories(paths) {
7048
+ const dirs = /* @__PURE__ */ new Set();
7049
+ for (const path5 of paths) {
7050
+ const index = path5.lastIndexOf("/");
7051
+ if (index > 0) {
7052
+ dirs.add(path5.slice(0, index));
7053
+ }
7054
+ }
7055
+ return [...dirs];
7056
+ }
7057
+ function compactObject(input) {
7058
+ const output = {};
7059
+ for (const [key, value] of Object.entries(input)) {
7060
+ if (value !== void 0) {
7061
+ output[key] = value;
7062
+ }
7063
+ }
7064
+ return output;
7065
+ }
7066
+ function isMissingDaytonaSdk(error) {
7067
+ const maybeCode = error.code;
7068
+ return maybeCode === "ERR_MODULE_NOT_FOUND" || maybeCode === "MODULE_NOT_FOUND" || error.message.includes("@daytona/sdk");
7069
+ }
7070
+ function toError(error) {
7071
+ return error instanceof Error ? error : new Error(String(error));
7072
+ }
7073
+
7074
+ // packages/context/src/lib/sandbox/docker-sandbox.ts
7075
+ import "bash-tool";
7076
+ import spawn3 from "nano-spawn";
7077
+ import { spawn as childSpawn } from "node:child_process";
7078
+ import { createHash } from "node:crypto";
7079
+ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
7080
+ import { Readable as Readable2 } from "node:stream";
7081
+
7082
+ // packages/context/src/lib/sandbox/docker-sandbox-errors.ts
7083
+ var DockerSandboxError = class extends Error {
7084
+ containerId;
7085
+ constructor(message2, containerId) {
7086
+ super(message2);
7087
+ this.name = "DockerSandboxError";
7088
+ this.containerId = containerId;
7089
+ }
7090
+ };
7091
+ var DockerNotAvailableError = class extends DockerSandboxError {
7092
+ constructor() {
7093
+ super("Docker is not available. Ensure Docker daemon is running.");
7094
+ this.name = "DockerNotAvailableError";
7095
+ }
7096
+ };
7097
+ var ContainerCreationError = class extends DockerSandboxError {
7098
+ image;
7099
+ cause;
7100
+ constructor(message2, image, cause) {
7101
+ super(`Failed to create container from image "${image}": ${message2}`);
7102
+ this.name = "ContainerCreationError";
7103
+ this.image = image;
7104
+ this.cause = cause;
7105
+ }
7106
+ };
7107
+ var PackageInstallError = class extends DockerSandboxError {
7108
+ packages;
7109
+ image;
7110
+ packageManager;
7111
+ stderr;
7112
+ constructor(packages, image, packageManager, stderr, containerId) {
7113
+ super(
7114
+ `Package installation failed for [${packages.join(", ")}] using ${packageManager} on ${image}: ${stderr}`,
7115
+ containerId
7116
+ );
7117
+ this.name = "PackageInstallError";
7118
+ this.packages = packages;
7119
+ this.image = image;
7120
+ this.packageManager = packageManager;
7121
+ this.stderr = stderr;
7122
+ }
7123
+ };
7124
+ var InstallError = class extends DockerSandboxError {
7125
+ target;
7126
+ source;
7127
+ reason;
7128
+ url;
7129
+ constructor(opts) {
7130
+ const where = opts.url ? `${opts.source} (${opts.url})` : opts.source;
7131
+ super(
7132
+ `Failed to install "${opts.target}" via ${where}: ${opts.reason}`,
7133
+ opts.containerId
7134
+ );
7135
+ this.name = "InstallError";
7136
+ this.target = opts.target;
7137
+ this.source = opts.source;
7138
+ this.reason = opts.reason;
7139
+ this.url = opts.url;
7140
+ }
7141
+ };
7142
+ var MissingRuntimeError = class extends DockerSandboxError {
7143
+ runtime;
7144
+ required;
7145
+ constructor(runtime, required, details, containerId) {
7146
+ const base = `Required runtime "${runtime}" is not installed (needs: ${required.join(", ")}).`;
7147
+ super(details ? `${base} ${details}` : base, containerId);
7148
+ this.name = "MissingRuntimeError";
7149
+ this.runtime = runtime;
7150
+ this.required = required;
7151
+ }
7152
+ };
7153
+ var VolumePathError = class extends DockerSandboxError {
7154
+ source;
7155
+ containerPath;
7156
+ reason;
7157
+ constructor(source, containerPath, reason) {
7158
+ super(
7159
+ `Invalid Docker volume path "${source}" -> "${containerPath}": ${reason}`
7160
+ );
7161
+ this.name = "VolumePathError";
7162
+ this.source = source;
7163
+ this.containerPath = containerPath;
7164
+ this.reason = reason;
7165
+ }
7166
+ };
7167
+ var VolumeInspectError = class extends DockerSandboxError {
7168
+ volume;
7169
+ reason;
7170
+ constructor(volume, reason) {
7171
+ super(`Failed to inspect Docker volume "${volume}": ${reason}`);
7172
+ this.name = "VolumeInspectError";
7173
+ this.volume = volume;
7174
+ this.reason = reason;
7175
+ }
7176
+ };
7177
+ var VolumeCreateError = class extends DockerSandboxError {
7178
+ volume;
7179
+ reason;
7180
+ constructor(volume, reason) {
7181
+ super(`Failed to create Docker volume "${volume}": ${reason}`);
7182
+ this.name = "VolumeCreateError";
7183
+ this.volume = volume;
7184
+ this.reason = reason;
7185
+ }
7186
+ };
7187
+ var VolumeRemoveError = class extends DockerSandboxError {
7188
+ volume;
7189
+ reason;
7190
+ constructor(volume, reason) {
7191
+ super(`Failed to remove Docker volume "${volume}": ${reason}`);
7192
+ this.name = "VolumeRemoveError";
7193
+ this.volume = volume;
7194
+ this.reason = reason;
7195
+ }
7196
+ };
7197
+ var DockerfileBuildError = class extends DockerSandboxError {
7198
+ stderr;
7199
+ constructor(stderr) {
7200
+ super(`Dockerfile build failed: ${stderr}`);
7201
+ this.name = "DockerfileBuildError";
7202
+ this.stderr = stderr;
7203
+ }
7204
+ };
7205
+ var ComposeStartError = class extends DockerSandboxError {
7206
+ composeFile;
7207
+ stderr;
7208
+ constructor(composeFile, stderr) {
7209
+ super(`Docker Compose failed to start: ${stderr}`);
7210
+ this.name = "ComposeStartError";
7211
+ this.composeFile = composeFile;
7212
+ this.stderr = stderr;
7213
+ }
7214
+ };
6538
7215
 
6539
7216
  // packages/context/src/lib/sandbox/installers/installer.ts
6540
7217
  import "bash-tool";
@@ -6547,7 +7224,7 @@ function isDebianBased(image) {
6547
7224
  const debianPatterns = ["debian", "ubuntu", "node", "python"];
6548
7225
  return debianPatterns.some((pattern) => lower.includes(pattern));
6549
7226
  }
6550
- function shellQuote(s) {
7227
+ function shellQuote3(s) {
6551
7228
  return `'${s.replace(/'/g, `'\\''`)}'`;
6552
7229
  }
6553
7230
  function createInstallerContext(containerId, image) {
@@ -6595,7 +7272,7 @@ function createInstallerContext(containerId, image) {
6595
7272
  };
6596
7273
  const installPackages = async (packages) => {
6597
7274
  if (packages.length === 0) return;
6598
- const quoted = packages.map(shellQuote).join(" ");
7275
+ const quoted = packages.map(shellQuote3).join(" ");
6599
7276
  let cmd;
6600
7277
  if (packageManager === "apt-get") {
6601
7278
  cmd = aptUpdated ? `apt-get install -y ${quoted}` : `apt-get update && apt-get install -y ${quoted}`;
@@ -6617,7 +7294,7 @@ function createInstallerContext(containerId, image) {
6617
7294
  const ensureTool = async (checkName, installName) => {
6618
7295
  const cacheKey = installName ?? checkName;
6619
7296
  if (ensuredTools.has(cacheKey)) return;
6620
- const check = await exec(`which ${shellQuote(checkName)}`);
7297
+ const check = await exec(`which ${shellQuote3(checkName)}`);
6621
7298
  if (check.exitCode === 0) {
6622
7299
  ensuredTools.add(cacheKey);
6623
7300
  return;
@@ -6643,44 +7320,216 @@ function isDockerfileOptions(opts) {
6643
7320
  function isComposeOptions(opts) {
6644
7321
  return "compose" in opts;
6645
7322
  }
7323
+ var CONTAINER_NAME_PATTERN = /^[A-Za-z0-9_.-]+$/;
6646
7324
  var DockerSandboxStrategy = class {
6647
7325
  context;
6648
- mounts;
7326
+ volumes;
6649
7327
  resources;
6650
7328
  env;
7329
+ name;
7330
+ createdVolumes = /* @__PURE__ */ new Set();
6651
7331
  constructor(args = {}) {
6652
- const { mounts = [], resources = {}, env = {} } = args;
7332
+ const { volumes = [], resources = {}, env = {}, name } = args;
6653
7333
  for (const key of Object.keys(env)) {
6654
- if (key.length === 0 || key.includes("=")) {
6655
- throw new DockerSandboxError(
6656
- `Invalid environment variable key: "${key}"`
6657
- );
6658
- }
7334
+ validateEnvKey2(key);
6659
7335
  }
6660
- this.mounts = mounts;
7336
+ if (name !== void 0 && !CONTAINER_NAME_PATTERN.test(name)) {
7337
+ throw new DockerSandboxError(
7338
+ `Invalid container name: "${name}". Use only letters, numbers, underscore, period, or hyphen. The "sandbox-" prefix is added automatically.`
7339
+ );
7340
+ }
7341
+ this.volumes = volumes;
6661
7342
  this.resources = resources;
6662
7343
  this.env = env;
7344
+ this.name = name;
6663
7345
  }
6664
7346
  async create() {
6665
- this.validateMounts();
6666
7347
  const image = await this.getImage();
6667
- const containerId = await this.startContainer(image);
6668
- this.context = { containerId, image };
7348
+ let acquired;
6669
7349
  try {
6670
- await this.configure();
7350
+ acquired = await this.acquireContainer(image);
7351
+ this.context = { containerId: acquired.containerId, image };
7352
+ if (!acquired.attached) {
7353
+ await this.configure();
7354
+ }
6671
7355
  } catch (error) {
6672
- await this.stopContainer(containerId);
7356
+ if (acquired && !acquired.attached) {
7357
+ await this.stopContainer(acquired.containerId);
7358
+ }
7359
+ await this.cleanupCreatedVolumesAfterFailure(error);
6673
7360
  throw error;
6674
7361
  }
6675
7362
  return this.createSandboxMethods();
6676
7363
  }
6677
- validateMounts() {
6678
- for (const mount of this.mounts) {
6679
- if (!existsSync3(mount.hostPath)) {
6680
- throw new MountPathError(mount.hostPath, mount.containerPath);
7364
+ namedContainerId() {
7365
+ return `sandbox-${this.name}`;
7366
+ }
7367
+ defaultContainerId() {
7368
+ return `sandbox-${crypto.randomUUID().slice(0, 8)}`;
7369
+ }
7370
+ async acquireContainer(image) {
7371
+ if (!this.name) {
7372
+ const containerId2 = this.defaultContainerId();
7373
+ await this.prepareVolumes();
7374
+ await this.startContainer(image, containerId2);
7375
+ return { containerId: containerId2, attached: false };
7376
+ }
7377
+ const containerId = this.namedContainerId();
7378
+ const probe = await this.inspectContainer(containerId);
7379
+ if (probe === "running") {
7380
+ return { containerId, attached: true };
7381
+ }
7382
+ if (probe === "stopped") {
7383
+ await this.startStoppedContainer(containerId, image);
7384
+ return { containerId, attached: true };
7385
+ }
7386
+ await this.prepareVolumes();
7387
+ try {
7388
+ await this.startContainer(image, containerId);
7389
+ return { containerId, attached: false };
7390
+ } catch (error) {
7391
+ if (error instanceof ContainerCreationError && this.isNameConflictError(error.message)) {
7392
+ await this.cleanupCreatedVolumes();
7393
+ const raced = await this.inspectContainer(containerId);
7394
+ if (raced === "running") {
7395
+ return { containerId, attached: true };
7396
+ }
7397
+ if (raced === "stopped") {
7398
+ await this.startStoppedContainer(containerId, image);
7399
+ return { containerId, attached: true };
7400
+ }
6681
7401
  }
7402
+ throw error;
6682
7403
  }
6683
7404
  }
7405
+ async startStoppedContainer(containerId, image) {
7406
+ try {
7407
+ await spawn3("docker", ["start", containerId]);
7408
+ } catch (error) {
7409
+ const message2 = this.getDockerErrorMessage(error);
7410
+ if (this.isDockerUnavailableError(message2)) {
7411
+ throw new DockerNotAvailableError();
7412
+ }
7413
+ throw new ContainerCreationError(message2, image, error);
7414
+ }
7415
+ }
7416
+ async inspectContainer(containerId) {
7417
+ try {
7418
+ const result = await spawn3("docker", [
7419
+ "container",
7420
+ "inspect",
7421
+ "--format",
7422
+ "{{.State.Status}}",
7423
+ containerId
7424
+ ]);
7425
+ const status = result.stdout.trim();
7426
+ return status === "running" ? "running" : "stopped";
7427
+ } catch (error) {
7428
+ const message2 = this.getDockerErrorMessage(error);
7429
+ if (this.isDockerUnavailableError(message2)) {
7430
+ throw new DockerNotAvailableError();
7431
+ }
7432
+ if (this.isMissingContainerError(message2)) {
7433
+ return "absent";
7434
+ }
7435
+ throw new DockerSandboxError(
7436
+ `Failed to inspect container "${containerId}": ${message2}`
7437
+ );
7438
+ }
7439
+ }
7440
+ isMissingContainerError(message2) {
7441
+ return message2.toLowerCase().includes("no such container");
7442
+ }
7443
+ isNameConflictError(message2) {
7444
+ return message2.toLowerCase().includes("is already in use by container");
7445
+ }
7446
+ async prepareVolumes() {
7447
+ this.validateVolumes();
7448
+ for (const volume of this.volumes) {
7449
+ if (volume.type !== "volume") {
7450
+ continue;
7451
+ }
7452
+ const lifecycle = volume.lifecycle ?? "external";
7453
+ if (lifecycle === "external") {
7454
+ await this.inspectVolume(volume.name);
7455
+ continue;
7456
+ }
7457
+ const exists = await this.volumeExists(volume.name);
7458
+ if (exists) {
7459
+ throw new VolumeCreateError(
7460
+ volume.name,
7461
+ "managed volume already exists"
7462
+ );
7463
+ }
7464
+ await this.createVolume(volume);
7465
+ if (volume.removeOnDispose !== false) {
7466
+ this.createdVolumes.add(volume.name);
7467
+ }
7468
+ }
7469
+ }
7470
+ validateVolumes() {
7471
+ const containerPaths = /* @__PURE__ */ new Set();
7472
+ for (const volume of this.volumes) {
7473
+ const source = volume.type === "bind" ? volume.hostPath : volume.name;
7474
+ if (!volume.containerPath.startsWith("/")) {
7475
+ throw new VolumePathError(
7476
+ source,
7477
+ volume.containerPath,
7478
+ "containerPath must be absolute"
7479
+ );
7480
+ }
7481
+ this.validateMountValue("containerPath", volume.containerPath, volume);
7482
+ if (containerPaths.has(volume.containerPath)) {
7483
+ throw new VolumePathError(
7484
+ source,
7485
+ volume.containerPath,
7486
+ "containerPath must be unique"
7487
+ );
7488
+ }
7489
+ containerPaths.add(volume.containerPath);
7490
+ if (volume.type === "bind") {
7491
+ this.validateMountValue("hostPath", volume.hostPath, volume);
7492
+ if (!existsSync3(volume.hostPath)) {
7493
+ throw new VolumePathError(
7494
+ volume.hostPath,
7495
+ volume.containerPath,
7496
+ "hostPath does not exist on host"
7497
+ );
7498
+ }
7499
+ continue;
7500
+ }
7501
+ if (!/^[A-Za-z0-9][A-Za-z0-9_.-]*$/.test(volume.name)) {
7502
+ throw new VolumePathError(
7503
+ volume.name,
7504
+ volume.containerPath,
7505
+ "volume name must start with an alphanumeric character and contain only letters, numbers, underscore, period, or hyphen"
7506
+ );
7507
+ }
7508
+ if (volume.subPath) {
7509
+ this.validateMountValue("subPath", volume.subPath, volume);
7510
+ }
7511
+ if ((volume.lifecycle ?? "external") === "external") {
7512
+ if (volume.driver || volume.driverOptions) {
7513
+ throw new VolumePathError(
7514
+ volume.name,
7515
+ volume.containerPath,
7516
+ 'driver and driverOptions require lifecycle "managed"'
7517
+ );
7518
+ }
7519
+ }
7520
+ }
7521
+ }
7522
+ validateMountValue(field, value, volume) {
7523
+ if (!value.includes(",")) {
7524
+ return;
7525
+ }
7526
+ const source = volume.type === "bind" ? volume.hostPath : volume.name;
7527
+ throw new VolumePathError(
7528
+ source,
7529
+ volume.containerPath,
7530
+ `${field} must not contain commas`
7531
+ );
7532
+ }
6684
7533
  buildDockerArgs(image, containerId) {
6685
7534
  const { memory = "1g", cpus = 2 } = this.resources;
6686
7535
  const args = [
@@ -6697,15 +7546,106 @@ var DockerSandboxStrategy = class {
6697
7546
  for (const [key, value] of Object.entries(this.env)) {
6698
7547
  args.push("-e", `${key}=${value}`);
6699
7548
  }
6700
- for (const mount of this.mounts) {
6701
- const mode = mount.readOnly !== false ? "ro" : "rw";
6702
- args.push("-v", `${mount.hostPath}:${mount.containerPath}:${mode}`);
7549
+ for (const volume of this.volumes) {
7550
+ args.push("--mount", this.buildVolumeMountArg(volume));
6703
7551
  }
6704
7552
  args.push(image, "tail", "-f", "/dev/null");
6705
7553
  return args;
6706
7554
  }
6707
- async startContainer(image) {
6708
- const containerId = `sandbox-${crypto.randomUUID().slice(0, 8)}`;
7555
+ buildVolumeMountArg(volume) {
7556
+ const readOnly = volume.readOnly !== false;
7557
+ const parts = volume.type === "bind" ? ["type=bind", `src=${volume.hostPath}`, `dst=${volume.containerPath}`] : [
7558
+ "type=volume",
7559
+ `src=${volume.name}`,
7560
+ `dst=${volume.containerPath}`,
7561
+ ...volume.subPath ? [`volume-subpath=${volume.subPath}`] : [],
7562
+ ...volume.noCopy ? ["volume-nocopy"] : []
7563
+ ];
7564
+ if (readOnly) {
7565
+ parts.push("readonly");
7566
+ }
7567
+ return parts.join(",");
7568
+ }
7569
+ async inspectVolume(name) {
7570
+ try {
7571
+ await spawn3("docker", ["volume", "inspect", name]);
7572
+ } catch (error) {
7573
+ const reason = this.getDockerErrorMessage(error);
7574
+ if (this.isDockerUnavailableError(reason)) {
7575
+ throw new DockerNotAvailableError();
7576
+ }
7577
+ throw new VolumeInspectError(name, reason);
7578
+ }
7579
+ }
7580
+ async volumeExists(name) {
7581
+ try {
7582
+ await this.inspectVolume(name);
7583
+ return true;
7584
+ } catch (error) {
7585
+ if (error instanceof VolumeInspectError) {
7586
+ if (this.isMissingVolumeInspectError(error.reason)) {
7587
+ return false;
7588
+ }
7589
+ throw error;
7590
+ }
7591
+ throw error;
7592
+ }
7593
+ }
7594
+ async createVolume(volume) {
7595
+ const args = ["volume", "create"];
7596
+ if (volume.driver) {
7597
+ args.push("--driver", volume.driver);
7598
+ }
7599
+ for (const [key, value] of Object.entries(volume.driverOptions ?? {})) {
7600
+ args.push("--opt", `${key}=${value}`);
7601
+ }
7602
+ args.push(volume.name);
7603
+ try {
7604
+ await spawn3("docker", args);
7605
+ } catch (error) {
7606
+ const reason = this.getDockerErrorMessage(error);
7607
+ if (this.isDockerUnavailableError(reason)) {
7608
+ throw new DockerNotAvailableError();
7609
+ }
7610
+ throw new VolumeCreateError(volume.name, reason);
7611
+ }
7612
+ }
7613
+ async cleanupCreatedVolumes() {
7614
+ const volumes = [...this.createdVolumes].reverse();
7615
+ for (const volume of volumes) {
7616
+ try {
7617
+ await spawn3("docker", ["volume", "rm", volume]);
7618
+ this.createdVolumes.delete(volume);
7619
+ } catch (error) {
7620
+ const reason = this.getDockerErrorMessage(error);
7621
+ if (this.isDockerUnavailableError(reason)) {
7622
+ throw new DockerNotAvailableError();
7623
+ }
7624
+ throw new VolumeRemoveError(volume, reason);
7625
+ }
7626
+ }
7627
+ }
7628
+ async cleanupCreatedVolumesAfterFailure(originalError) {
7629
+ try {
7630
+ await this.cleanupCreatedVolumes();
7631
+ } catch (cleanupError) {
7632
+ if (originalError instanceof Error) {
7633
+ const original = originalError;
7634
+ original.suppressed = [...original.suppressed ?? [], cleanupError];
7635
+ }
7636
+ }
7637
+ }
7638
+ getDockerErrorMessage(error) {
7639
+ const err = error;
7640
+ return err.stderr || err.stdout || err.message || String(error);
7641
+ }
7642
+ isDockerUnavailableError(message2) {
7643
+ return message2.includes("Cannot connect") || message2.includes("docker daemon");
7644
+ }
7645
+ isMissingVolumeInspectError(message2) {
7646
+ return message2.toLowerCase().includes("no such volume");
7647
+ }
7648
+ async startContainer(image, containerId) {
6709
7649
  const args = this.buildDockerArgs(image, containerId);
6710
7650
  try {
6711
7651
  await spawn3("docker", args);
@@ -6714,9 +7654,12 @@ var DockerSandboxStrategy = class {
6714
7654
  if (err.message?.includes("Cannot connect") || err.message?.includes("docker daemon") || err.stderr?.includes("Cannot connect")) {
6715
7655
  throw new DockerNotAvailableError();
6716
7656
  }
6717
- throw new ContainerCreationError(err.message || String(err), image, err);
7657
+ throw new ContainerCreationError(
7658
+ this.getDockerErrorMessage(err),
7659
+ image,
7660
+ err
7661
+ );
6718
7662
  }
6719
- return containerId;
6720
7663
  }
6721
7664
  async stopContainer(containerId) {
6722
7665
  try {
@@ -6724,15 +7667,13 @@ var DockerSandboxStrategy = class {
6724
7667
  } catch {
6725
7668
  }
6726
7669
  }
6727
- async exec(command) {
7670
+ async exec(command, options) {
6728
7671
  try {
6729
- const result = await spawn3("docker", [
6730
- "exec",
6731
- this.context.containerId,
6732
- "sh",
6733
- "-c",
6734
- command
6735
- ]);
7672
+ const result = await spawn3(
7673
+ "docker",
7674
+ ["exec", this.context.containerId, "sh", "-c", command],
7675
+ { signal: options?.signal }
7676
+ );
6736
7677
  return {
6737
7678
  stdout: result.stdout,
6738
7679
  stderr: result.stderr,
@@ -6747,12 +7688,22 @@ var DockerSandboxStrategy = class {
6747
7688
  };
6748
7689
  }
6749
7690
  }
7691
+ spawnProcess(command, options) {
7692
+ const child = childSpawn("docker", [
7693
+ "exec",
7694
+ ...buildDockerExecFlags(options),
7695
+ this.context.containerId,
7696
+ "sh",
7697
+ "-c",
7698
+ command
7699
+ ]);
7700
+ return toSandboxProcess(child, options?.signal);
7701
+ }
6750
7702
  createSandboxMethods() {
6751
7703
  const { containerId } = this.context;
6752
7704
  const sandbox = {
6753
- executeCommand: async (command) => {
6754
- return this.exec(command);
6755
- },
7705
+ executeCommand: async (command, options) => this.exec(command, options),
7706
+ spawn: (command, options) => this.spawnProcess(command, options),
6756
7707
  readFile: async (path5) => {
6757
7708
  const result = await sandbox.executeCommand(`base64 "${path5}"`);
6758
7709
  if (result.exitCode !== 0) {
@@ -6779,16 +7730,74 @@ var DockerSandboxStrategy = class {
6779
7730
  },
6780
7731
  dispose: async () => {
6781
7732
  await this.stopContainer(containerId);
7733
+ await this.cleanupCreatedVolumes();
6782
7734
  }
6783
7735
  };
6784
7736
  return sandbox;
6785
7737
  }
6786
7738
  };
7739
+ function validateEnvKey2(key) {
7740
+ if (key.length === 0 || key.includes("=")) {
7741
+ throw new DockerSandboxError(`Invalid environment variable key: "${key}"`);
7742
+ }
7743
+ }
7744
+ function buildDockerExecFlags(options) {
7745
+ const flags = [];
7746
+ if (options?.cwd) {
7747
+ flags.push("-w", options.cwd);
7748
+ }
7749
+ if (options?.env) {
7750
+ for (const [key, value] of Object.entries(options.env)) {
7751
+ validateEnvKey2(key);
7752
+ flags.push("-e", `${key}=${value}`);
7753
+ }
7754
+ }
7755
+ return flags;
7756
+ }
7757
+ function toSandboxProcess(child, abortSignal) {
7758
+ if (!child.stdout || !child.stderr) {
7759
+ child.kill("SIGKILL");
7760
+ throw new DockerSandboxError("docker exec child missing stdio streams");
7761
+ }
7762
+ const onAbort = abortSignal ? () => child.kill("SIGKILL") : void 0;
7763
+ if (abortSignal && onAbort) {
7764
+ if (abortSignal.aborted) onAbort();
7765
+ else abortSignal.addEventListener("abort", onAbort, { once: true });
7766
+ }
7767
+ return {
7768
+ stdout: Readable2.toWeb(child.stdout),
7769
+ stderr: Readable2.toWeb(child.stderr),
7770
+ exit: new Promise((resolve4, reject) => {
7771
+ const settle = () => {
7772
+ child.removeListener("exit", onExitEvent);
7773
+ child.removeListener("error", onError);
7774
+ if (abortSignal && onAbort) {
7775
+ abortSignal.removeEventListener("abort", onAbort);
7776
+ }
7777
+ };
7778
+ const onError = (err) => {
7779
+ settle();
7780
+ reject(err);
7781
+ };
7782
+ const onExitEvent = (code, exitSignal) => {
7783
+ settle();
7784
+ resolve4({ code, signal: exitSignal, success: code === 0 });
7785
+ };
7786
+ child.on("exit", onExitEvent);
7787
+ child.on("error", onError);
7788
+ })
7789
+ };
7790
+ }
6787
7791
  var RuntimeStrategy = class extends DockerSandboxStrategy {
6788
7792
  image;
6789
7793
  installers;
6790
7794
  constructor(args = {}) {
6791
- super({ mounts: args.mounts, resources: args.resources, env: args.env });
7795
+ super({
7796
+ volumes: args.volumes,
7797
+ resources: args.resources,
7798
+ env: args.env,
7799
+ name: args.name
7800
+ });
6792
7801
  this.image = args.image ?? "alpine:latest";
6793
7802
  this.installers = args.installers ?? [];
6794
7803
  }
@@ -6807,7 +7816,12 @@ var DockerfileStrategy = class extends DockerSandboxStrategy {
6807
7816
  dockerfile;
6808
7817
  dockerContext;
6809
7818
  constructor(args) {
6810
- super({ mounts: args.mounts, resources: args.resources, env: args.env });
7819
+ super({
7820
+ volumes: args.volumes,
7821
+ resources: args.resources,
7822
+ env: args.env,
7823
+ name: args.name
7824
+ });
6811
7825
  this.dockerfile = args.dockerfile;
6812
7826
  this.dockerContext = args.context ?? ".";
6813
7827
  this.imageTag = this.computeImageTag();
@@ -6876,7 +7890,7 @@ var ComposeStrategy = class extends DockerSandboxStrategy {
6876
7890
  async getImage() {
6877
7891
  return "";
6878
7892
  }
6879
- async startContainer(_image) {
7893
+ async startContainer(_image, _containerId) {
6880
7894
  try {
6881
7895
  await spawn3("docker", [
6882
7896
  "compose",
@@ -6894,25 +7908,31 @@ var ComposeStrategy = class extends DockerSandboxStrategy {
6894
7908
  }
6895
7909
  throw new ComposeStartError(this.composeFile, err.stderr || err.message);
6896
7910
  }
7911
+ }
7912
+ defaultContainerId() {
6897
7913
  return this.projectName;
6898
7914
  }
6899
7915
  async configure() {
6900
7916
  }
6901
- async exec(command) {
7917
+ async exec(command, options) {
6902
7918
  try {
6903
- const result = await spawn3("docker", [
6904
- "compose",
6905
- "-f",
6906
- this.composeFile,
6907
- "-p",
6908
- this.projectName,
6909
- "exec",
6910
- "-T",
6911
- this.service,
6912
- "sh",
6913
- "-c",
6914
- command
6915
- ]);
7919
+ const result = await spawn3(
7920
+ "docker",
7921
+ [
7922
+ "compose",
7923
+ "-f",
7924
+ this.composeFile,
7925
+ "-p",
7926
+ this.projectName,
7927
+ "exec",
7928
+ "-T",
7929
+ this.service,
7930
+ "sh",
7931
+ "-c",
7932
+ command
7933
+ ],
7934
+ { signal: options?.signal }
7935
+ );
6916
7936
  return { stdout: result.stdout, stderr: result.stderr, exitCode: 0 };
6917
7937
  } catch (error) {
6918
7938
  const err = error;
@@ -6923,6 +7943,23 @@ var ComposeStrategy = class extends DockerSandboxStrategy {
6923
7943
  };
6924
7944
  }
6925
7945
  }
7946
+ spawnProcess(command, options) {
7947
+ const child = childSpawn("docker", [
7948
+ "compose",
7949
+ "-f",
7950
+ this.composeFile,
7951
+ "-p",
7952
+ this.projectName,
7953
+ "exec",
7954
+ "-T",
7955
+ ...buildDockerExecFlags(options),
7956
+ this.service,
7957
+ "sh",
7958
+ "-c",
7959
+ command
7960
+ ]);
7961
+ return toSandboxProcess(child, options?.signal);
7962
+ }
6926
7963
  async stopContainer(_containerId) {
6927
7964
  try {
6928
7965
  await spawn3("docker", [
@@ -6949,17 +7986,19 @@ async function createDockerSandbox(options = {}) {
6949
7986
  strategy = new DockerfileStrategy({
6950
7987
  dockerfile: options.dockerfile,
6951
7988
  context: options.context,
6952
- mounts: options.mounts,
7989
+ volumes: options.volumes,
6953
7990
  resources: options.resources,
6954
- env: options.env
7991
+ env: options.env,
7992
+ name: options.name
6955
7993
  });
6956
7994
  } else {
6957
7995
  strategy = new RuntimeStrategy({
6958
7996
  image: options.image,
6959
7997
  installers: options.installers,
6960
- mounts: options.mounts,
7998
+ volumes: options.volumes,
6961
7999
  resources: options.resources,
6962
- env: options.env
8000
+ env: options.env,
8001
+ name: options.name
6963
8002
  });
6964
8003
  }
6965
8004
  return strategy.create();
@@ -6973,57 +8012,6 @@ async function useSandbox(options, fn) {
6973
8012
  }
6974
8013
  }
6975
8014
 
6976
- // packages/context/src/lib/sandbox/container-tool.ts
6977
- async function createContainerTool(options = {}) {
6978
- let sandboxOptions;
6979
- let bashOptions;
6980
- let skillInputs = [];
6981
- if (isComposeOptions(options)) {
6982
- const { compose, service, resources, skills: skills2 = [], ...rest } = options;
6983
- sandboxOptions = { compose, service, resources };
6984
- bashOptions = rest;
6985
- skillInputs = skills2;
6986
- } else if (isDockerfileOptions(options)) {
6987
- const {
6988
- dockerfile,
6989
- context,
6990
- mounts,
6991
- resources,
6992
- env,
6993
- skills: skills2 = [],
6994
- ...rest
6995
- } = options;
6996
- sandboxOptions = { dockerfile, context, mounts, resources, env };
6997
- bashOptions = rest;
6998
- skillInputs = skills2;
6999
- } else {
7000
- const {
7001
- image,
7002
- installers,
7003
- mounts,
7004
- resources,
7005
- env,
7006
- skills: skills2 = [],
7007
- ...rest
7008
- } = options;
7009
- sandboxOptions = { image, installers, mounts, resources, env };
7010
- bashOptions = rest;
7011
- skillInputs = skills2;
7012
- }
7013
- const sandbox = await createDockerSandbox(sandboxOptions);
7014
- const toolkit = await createBashTool({
7015
- ...bashOptions,
7016
- sandbox,
7017
- skills: skillInputs
7018
- });
7019
- return {
7020
- bash: toolkit.bash,
7021
- tools: toolkit.tools,
7022
- sandbox,
7023
- skills: toolkit.skills
7024
- };
7025
- }
7026
-
7027
8015
  // packages/context/src/lib/sandbox/installers/package-manager.ts
7028
8016
  var PackageInstaller = class extends Installer {
7029
8017
  kind;
@@ -7096,9 +8084,9 @@ async function downloadAndInstall(ctx, name, url, binaryPath, source = "url") {
7096
8084
  function buildTarGzInstallCmd(name, url, binaryPathInArchive) {
7097
8085
  return `
7098
8086
  set -e
7099
- NAME=${shellQuote(name)}
7100
- URL=${shellQuote(url)}
7101
- BIN_IN_ARCHIVE=${shellQuote(binaryPathInArchive)}
8087
+ NAME=${shellQuote3(name)}
8088
+ URL=${shellQuote3(url)}
8089
+ BIN_IN_ARCHIVE=${shellQuote3(binaryPathInArchive)}
7102
8090
  TMPDIR=$(mktemp -d)
7103
8091
  cd "$TMPDIR"
7104
8092
  curl -fsSL "$URL" -o archive.tar.gz
@@ -7117,8 +8105,8 @@ function buildTarGzInstallCmd(name, url, binaryPathInArchive) {
7117
8105
  }
7118
8106
  function buildRawInstallCmd(name, url) {
7119
8107
  return `
7120
- NAME=${shellQuote(name)}
7121
- URL=${shellQuote(url)}
8108
+ NAME=${shellQuote3(name)}
8109
+ URL=${shellQuote3(url)}
7122
8110
  curl -fsSL "$URL" -o "/usr/local/bin/$NAME"
7123
8111
  chmod +x "/usr/local/bin/$NAME"
7124
8112
  `;
@@ -7171,6 +8159,59 @@ async function ensureNodeRuntime(ctx, ensure) {
7171
8159
  }
7172
8160
  }
7173
8161
 
8162
+ // packages/context/src/lib/sandbox/installers/bin.ts
8163
+ import { basename, posix } from "node:path";
8164
+ var NOT_FOUND_EXIT = 11;
8165
+ var CHMOD_FAILED_EXIT = 12;
8166
+ var BinInstaller = class extends Installer {
8167
+ kind;
8168
+ binary;
8169
+ options;
8170
+ #name;
8171
+ #target;
8172
+ constructor(binary, options = {}) {
8173
+ super();
8174
+ this.binary = binary;
8175
+ this.options = options;
8176
+ this.#name = resolveName(binary, options);
8177
+ this.#target = options.target ?? posix.join("/usr/local/bin", this.#name);
8178
+ this.kind = `bin:${this.#name}`;
8179
+ }
8180
+ async install(ctx) {
8181
+ const b = shellQuote3(this.binary);
8182
+ const t = shellQuote3(this.#target);
8183
+ const dir = shellQuote3(posix.dirname(this.#target));
8184
+ const result = await ctx.exec(
8185
+ `test -f ${b} || { echo "binary not found at ${this.binary}" >&2; exit ${NOT_FOUND_EXIT}; }; if ! test -x ${b}; then chmod +x ${b} || exit ${CHMOD_FAILED_EXIT}; fi; mkdir -p ${dir} && ln -sf ${b} ${t}`
8186
+ );
8187
+ if (result.exitCode === 0) return;
8188
+ throw new InstallError({
8189
+ target: this.#name,
8190
+ source: "bin",
8191
+ reason: explainFailure(result.exitCode, result.stderr, this.binary),
8192
+ containerId: ctx.containerId
8193
+ });
8194
+ }
8195
+ };
8196
+ function bin(binary, options) {
8197
+ return new BinInstaller(binary, options);
8198
+ }
8199
+ function explainFailure(exitCode, stderr, binary) {
8200
+ if (exitCode === NOT_FOUND_EXIT) {
8201
+ return `binary not found at ${binary}`;
8202
+ }
8203
+ if (exitCode === CHMOD_FAILED_EXIT && /read-only file system/i.test(stderr)) {
8204
+ return `${binary} is not executable and the bind mount is read-only \u2014 run \`chmod +x\` on the host, or mount with \`readOnly: false\`. (${stderr.trim()})`;
8205
+ }
8206
+ return stderr || `bin installer failed with exit code ${exitCode}`;
8207
+ }
8208
+ function resolveName(binary, options) {
8209
+ if (options.name) return options.name;
8210
+ const base = basename(binary);
8211
+ const dot = base.lastIndexOf(".");
8212
+ return dot > 0 ? base.slice(0, dot) : base;
8213
+ }
8214
+
7174
8215
  // packages/context/src/lib/sandbox/installers/pip.ts
7175
8216
  var PYTHON_BINARIES = ["python3", "pip3"];
7176
8217
  var PipInstaller = class extends Installer {
@@ -7330,8 +8371,11 @@ async function createVirtualSandbox(options) {
7330
8371
  customCommands: options.customCommands
7331
8372
  });
7332
8373
  return {
7333
- async executeCommand(command) {
7334
- const result = await bash.exec(command);
8374
+ async executeCommand(command, options2) {
8375
+ const result = await bash.exec(
8376
+ command,
8377
+ options2?.signal ? { signal: options2.signal } : void 0
8378
+ );
7335
8379
  return {
7336
8380
  stdout: result.stdout,
7337
8381
  stderr: result.stderr,
@@ -7348,6 +8392,8 @@ async function createVirtualSandbox(options) {
7348
8392
  typeof file.content === "string" ? file.content : Buffer.from(file.content).toString("utf-8")
7349
8393
  );
7350
8394
  }
8395
+ },
8396
+ async dispose() {
7351
8397
  }
7352
8398
  };
7353
8399
  }
@@ -7429,7 +8475,7 @@ function soul() {
7429
8475
  }
7430
8476
 
7431
8477
  // packages/context/src/lib/store/postgres.store.ts
7432
- import { createRequire } from "node:module";
8478
+ import { createRequire as createRequire2 } from "node:module";
7433
8479
 
7434
8480
  // packages/context/src/lib/store/ddl.postgres.ts
7435
8481
  function storeDDL(schema) {
@@ -7541,7 +8587,7 @@ var PostgresContextStore = class _PostgresContextStore extends ContextStore {
7541
8587
  }
7542
8588
  static #requirePg() {
7543
8589
  try {
7544
- const require2 = createRequire(import.meta.url);
8590
+ const require2 = createRequire2(import.meta.url);
7545
8591
  return require2("pg");
7546
8592
  } catch {
7547
8593
  throw new Error(
@@ -8137,7 +9183,7 @@ var PostgresContextStore = class _PostgresContextStore extends ContextStore {
8137
9183
  };
8138
9184
 
8139
9185
  // packages/context/src/lib/store/sqlserver.store.ts
8140
- import { createRequire as createRequire2 } from "node:module";
9186
+ import { createRequire as createRequire3 } from "node:module";
8141
9187
 
8142
9188
  // packages/context/src/lib/store/ddl.sqlserver.ts
8143
9189
  function storeDDL2(schema) {
@@ -8279,7 +9325,7 @@ var SqlServerContextStore = class _SqlServerContextStore extends ContextStore {
8279
9325
  }
8280
9326
  static #requireMssql() {
8281
9327
  try {
8282
- const require2 = createRequire2(import.meta.url);
9328
+ const require2 = createRequire3(import.meta.url);
8283
9329
  return require2("mssql");
8284
9330
  } catch {
8285
9331
  throw new Error(
@@ -8989,11 +10035,12 @@ async function persistedWriter(options) {
8989
10035
  } = options;
8990
10036
  let seq = 0;
8991
10037
  let buffer = [];
10038
+ let failedByErrorChunk = false;
8992
10039
  async function flush() {
8993
10040
  if (buffer.length === 0) return;
8994
10041
  const batch = buffer;
8995
10042
  buffer = [];
8996
- await store2.appendChunks(batch);
10043
+ await appendBatch(batch);
8997
10044
  }
8998
10045
  function makeChunk(part) {
8999
10046
  return {
@@ -9003,9 +10050,19 @@ async function persistedWriter(options) {
9003
10050
  createdAt: Date.now()
9004
10051
  };
9005
10052
  }
10053
+ function isStreamErrorPart(part) {
10054
+ return part.type === "error";
10055
+ }
10056
+ async function appendBatch(batch) {
10057
+ const hasErrorPart = !failedByErrorChunk && batch.map((chunk) => chunk.data).some(isStreamErrorPart);
10058
+ await store2.appendChunks(batch);
10059
+ if (hasErrorPart) {
10060
+ failedByErrorChunk = true;
10061
+ }
10062
+ }
9006
10063
  async function persistChunk(chunk) {
9007
10064
  if (strategy === "immediate") {
9008
- await store2.appendChunks([chunk]);
10065
+ await appendBatch([chunk]);
9009
10066
  } else {
9010
10067
  buffer.push(chunk);
9011
10068
  if (buffer.length >= flushSize) {
@@ -9035,10 +10092,12 @@ async function persistedWriter(options) {
9035
10092
  flush,
9036
10093
  async complete() {
9037
10094
  await flush();
10095
+ if (failedByErrorChunk) return;
9038
10096
  await store2.updateStreamStatus(streamId, "completed");
9039
10097
  },
9040
10098
  async fail(error) {
9041
10099
  await flush();
10100
+ if (failedByErrorChunk) return;
9042
10101
  await store2.updateStreamStatus(streamId, "failed", { error });
9043
10102
  },
9044
10103
  async cleanup() {
@@ -9048,7 +10107,7 @@ async function persistedWriter(options) {
9048
10107
  }
9049
10108
 
9050
10109
  // packages/context/src/lib/stream/polling-change-source.ts
9051
- import { setTimeout as delay } from "node:timers/promises";
10110
+ import { setTimeout as delay2 } from "node:timers/promises";
9052
10111
 
9053
10112
  // packages/context/src/lib/stream/polling-policy.ts
9054
10113
  var DEFAULT_WATCH_POLLING = {
@@ -9093,7 +10152,7 @@ function nextAdaptivePollingDelay(state) {
9093
10152
  state.config.minMs,
9094
10153
  state.config.maxMs
9095
10154
  );
9096
- const delay2 = applyJitter(
10155
+ const delay3 = applyJitter(
9097
10156
  current,
9098
10157
  state.config.jitterRatio,
9099
10158
  state.config.minMs,
@@ -9104,7 +10163,7 @@ function nextAdaptivePollingDelay(state) {
9104
10163
  state.config.minMs,
9105
10164
  state.config.maxMs
9106
10165
  );
9107
- return delay2;
10166
+ return delay3;
9108
10167
  }
9109
10168
  function normalizeAdaptivePolling(polling, fallback) {
9110
10169
  const merged = {
@@ -9204,7 +10263,7 @@ function isTerminal(status) {
9204
10263
  return status !== "queued" && status !== "running";
9205
10264
  }
9206
10265
  function waitForDelay(ms, signal) {
9207
- return delay(ms, true, { signal }).catch((err) => {
10266
+ return delay2(ms, true, { signal }).catch((err) => {
9208
10267
  if (signal.aborted) return false;
9209
10268
  throw err;
9210
10269
  });
@@ -9256,7 +10315,7 @@ function asStaticWordPartText(parts, options = {}) {
9256
10315
  }
9257
10316
 
9258
10317
  // packages/context/src/lib/stream/postgres-notify-change-source.ts
9259
- import { createRequire as createRequire3 } from "node:module";
10318
+ import { createRequire as createRequire4 } from "node:module";
9260
10319
 
9261
10320
  // packages/context/src/lib/stream/ddl.stream.postgres-notify.ts
9262
10321
  var DEFAULT_POSTGRES_STREAM_CHANGES_CHANNEL = "deepagents_stream_changes";
@@ -9358,7 +10417,7 @@ var PostgresNotifyChangeSource = class _PostgresNotifyChangeSource {
9358
10417
  }
9359
10418
  static #requirePg() {
9360
10419
  try {
9361
- const require2 = createRequire3(import.meta.url);
10420
+ const require2 = createRequire4(import.meta.url);
9362
10421
  return require2("pg");
9363
10422
  } catch {
9364
10423
  throw new Error(
@@ -9613,7 +10672,7 @@ function assertIdentifier(value, label) {
9613
10672
  }
9614
10673
 
9615
10674
  // packages/context/src/lib/stream/postgres.stream-store.ts
9616
- import { createRequire as createRequire4 } from "node:module";
10675
+ import { createRequire as createRequire5 } from "node:module";
9617
10676
 
9618
10677
  // packages/context/src/lib/stream/ddl.stream.postgres.ts
9619
10678
  function postgresStreamDDL(schema) {
@@ -9649,6 +10708,15 @@ CREATE INDEX IF NOT EXISTS "idx_${schema}_streams_status_created_at_id"
9649
10708
  }
9650
10709
 
9651
10710
  // packages/context/src/lib/stream/stream-store.ts
10711
+ function collectStreamFailures(chunks) {
10712
+ const failures = /* @__PURE__ */ new Map();
10713
+ for (const chunk of chunks) {
10714
+ if (chunk.data.type === "error" && !failures.has(chunk.streamId)) {
10715
+ failures.set(chunk.streamId, chunk.data.errorText);
10716
+ }
10717
+ }
10718
+ return [...failures].map(([streamId, error]) => ({ streamId, error }));
10719
+ }
9652
10720
  var StreamStore = class {
9653
10721
  async listRunningStreamIds() {
9654
10722
  return this.listStreamIds({ status: "running" });
@@ -9678,7 +10746,7 @@ var PostgresStreamStore = class _PostgresStreamStore extends StreamStore {
9678
10746
  }
9679
10747
  static #requirePg() {
9680
10748
  try {
9681
- const require2 = createRequire4(import.meta.url);
10749
+ const require2 = createRequire5(import.meta.url);
9682
10750
  return require2("pg");
9683
10751
  } catch {
9684
10752
  throw new Error(
@@ -9832,13 +10900,31 @@ var PostgresStreamStore = class _PostgresStreamStore extends StreamStore {
9832
10900
  data: chunk.data,
9833
10901
  created_at: chunk.createdAt
9834
10902
  }));
9835
- await this.#query(
9836
- `INSERT INTO ${this.#t("stream_chunks")} (stream_id, seq, data, created_at)
10903
+ const params = [JSON.stringify(rows)];
10904
+ const insertSql = `INSERT INTO ${this.#t("stream_chunks")} (stream_id, seq, data, created_at)
9837
10905
  SELECT stream_id, seq, data, created_at
9838
10906
  FROM jsonb_to_recordset($1::jsonb)
9839
- AS rows(stream_id TEXT, seq INTEGER, data JSONB, created_at BIGINT)`,
9840
- [JSON.stringify(rows)]
9841
- );
10907
+ AS rows(stream_id TEXT, seq INTEGER, data JSONB, created_at BIGINT)`;
10908
+ const failures = collectStreamFailures(chunks);
10909
+ if (failures.length === 0) {
10910
+ await this.#query(insertSql, params);
10911
+ return;
10912
+ }
10913
+ await this.#useTransaction(async (client) => {
10914
+ await client.query(insertSql, params);
10915
+ const failedAt = Date.now();
10916
+ for (const failure of failures) {
10917
+ const result = await client.query(
10918
+ `UPDATE ${this.#t("streams")}
10919
+ SET status = $1, finished_at = $2, error = $3
10920
+ WHERE id = $4`,
10921
+ ["failed", failedAt, failure.error, failure.streamId]
10922
+ );
10923
+ if (result.rowCount !== 1) {
10924
+ throw new Error(`Stream "${failure.streamId}" not found`);
10925
+ }
10926
+ }
10927
+ });
9842
10928
  }
9843
10929
  async getChunks(streamId, fromSeq, limit) {
9844
10930
  const params = [streamId];
@@ -10085,6 +11171,7 @@ var SqliteStreamStore = class extends StreamStore {
10085
11171
  }
10086
11172
  async appendChunks(chunks) {
10087
11173
  if (chunks.length === 0) return;
11174
+ const failures = collectStreamFailures(chunks);
10088
11175
  this.#db.exec("BEGIN TRANSACTION");
10089
11176
  try {
10090
11177
  for (const chunk of chunks) {
@@ -10098,6 +11185,15 @@ var SqliteStreamStore = class extends StreamStore {
10098
11185
  chunk.createdAt
10099
11186
  );
10100
11187
  }
11188
+ const failedAt = Date.now();
11189
+ for (const failure of failures) {
11190
+ const result = this.#stmt(
11191
+ "UPDATE streams SET status = ?, finishedAt = ?, error = ? WHERE id = ?"
11192
+ ).run("failed", failedAt, failure.error, failure.streamId);
11193
+ if (result.changes !== 1) {
11194
+ throw new Error(`Stream "${failure.streamId}" not found`);
11195
+ }
11196
+ }
10101
11197
  this.#db.exec("COMMIT");
10102
11198
  } catch (error) {
10103
11199
  this.#db.exec("ROLLBACK");
@@ -10499,14 +11595,20 @@ export {
10499
11595
  AsyncResolver,
10500
11596
  BM25Classifier,
10501
11597
  BashException,
11598
+ BinInstaller,
10502
11599
  ComposeStartError,
10503
11600
  ComposeStrategy,
10504
11601
  ContainerCreationError,
10505
11602
  ContextEngine,
10506
11603
  ContextRenderer,
10507
11604
  ContextStore,
11605
+ DAYTONA_DEFAULT_DESTINATION,
10508
11606
  DEFAULT_CANCEL_POLLING,
10509
11607
  DEFAULT_WATCH_POLLING,
11608
+ DaytonaCommandError,
11609
+ DaytonaCreationError,
11610
+ DaytonaNotAvailableError,
11611
+ DaytonaSandboxError,
10510
11612
  DockerNotAvailableError,
10511
11613
  DockerSandboxError,
10512
11614
  DockerSandboxStrategy,
@@ -10524,9 +11626,7 @@ export {
10524
11626
  MarkdownRenderer,
10525
11627
  MissingRuntimeError,
10526
11628
  ModelsRegistry,
10527
- MountPathError,
10528
11629
  NpmInstaller,
10529
- ObservedFs,
10530
11630
  PackageInstallError,
10531
11631
  PackageInstaller,
10532
11632
  PipInstaller,
@@ -10536,6 +11636,7 @@ export {
10536
11636
  PostgresStreamStore,
10537
11637
  PromiseResolver,
10538
11638
  RuntimeStrategy,
11639
+ SnapshotFailedError,
10539
11640
  SqlServerContextStore,
10540
11641
  SqliteContextStore,
10541
11642
  SqliteStreamStore,
@@ -10545,6 +11646,10 @@ export {
10545
11646
  TomlRenderer,
10546
11647
  ToonRenderer,
10547
11648
  UrlBinaryInstaller,
11649
+ VolumeCreateError,
11650
+ VolumeInspectError,
11651
+ VolumePathError,
11652
+ VolumeRemoveError,
10548
11653
  XmlRenderer,
10549
11654
  addUsage,
10550
11655
  advisorPreamble,
@@ -10565,11 +11670,13 @@ export {
10565
11670
  assertCountSpec,
10566
11671
  assistant,
10567
11672
  assistantText,
11673
+ bin,
10568
11674
  buildSubcommandRepair,
10569
11675
  chat,
10570
11676
  checkCount,
10571
11677
  clarification,
10572
11678
  classifies,
11679
+ collectStreamFailures,
10573
11680
  contentIncludes,
10574
11681
  contentMatches,
10575
11682
  contentPattern,
@@ -10578,7 +11685,7 @@ export {
10578
11685
  createAgentOsSandbox,
10579
11686
  createBashTool,
10580
11687
  createBinaryBridges,
10581
- createContainerTool,
11688
+ createDaytonaSandbox,
10582
11689
  createDockerSandbox,
10583
11690
  createInstallerContext,
10584
11691
  createRepairToolCall,
@@ -10639,6 +11746,7 @@ export {
10639
11746
  not,
10640
11747
  npm,
10641
11748
  nullUsage,
11749
+ observeSandboxFileEvents,
10642
11750
  once,
10643
11751
  or,
10644
11752
  parseFrontmatter,
@@ -10663,11 +11771,12 @@ export {
10663
11771
  resolveTz,
10664
11772
  role,
10665
11773
  runGuardrailChain,
11774
+ runWithAbortSignal,
10666
11775
  runWithBashMeta,
10667
11776
  seasonChanged,
10668
11777
  seasonReminder,
10669
11778
  selfCritique,
10670
- shellQuote,
11779
+ shellQuote3 as shellQuote,
10671
11780
  skills,
10672
11781
  skillsReminder,
10673
11782
  socraticPrompting,
@@ -10696,6 +11805,7 @@ export {
10696
11805
  user,
10697
11806
  visualizeGraph,
10698
11807
  weekChanged,
11808
+ withAbortSignal,
10699
11809
  withinLastN,
10700
11810
  workflow,
10701
11811
  yearChanged,