@deepagents/context 4.1.0 → 4.2.1

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 (42) hide show
  1. package/README.md +18 -13
  2. package/dist/browser.js.map +2 -2
  3. package/dist/index.js +576 -57
  4. package/dist/index.js.map +4 -4
  5. package/dist/lib/agent.d.ts.map +1 -1
  6. package/dist/lib/engine.d.ts +17 -3
  7. package/dist/lib/engine.d.ts.map +1 -1
  8. package/dist/lib/fragments/message/user.d.ts +12 -0
  9. package/dist/lib/fragments/message/user.d.ts.map +1 -1
  10. package/dist/lib/sandbox/agent-os-sandbox.d.ts +2 -2
  11. package/dist/lib/sandbox/agent-os-sandbox.d.ts.map +1 -1
  12. package/dist/lib/sandbox/apple-container-sandbox.d.ts +8 -0
  13. package/dist/lib/sandbox/apple-container-sandbox.d.ts.map +1 -1
  14. package/dist/lib/sandbox/bash-meta.d.ts +0 -3
  15. package/dist/lib/sandbox/bash-meta.d.ts.map +1 -1
  16. package/dist/lib/sandbox/bash-tool.d.ts +3 -3
  17. package/dist/lib/sandbox/bash-tool.d.ts.map +1 -1
  18. package/dist/lib/sandbox/container-engine.d.ts +2 -2
  19. package/dist/lib/sandbox/container-engine.d.ts.map +1 -1
  20. package/dist/lib/sandbox/container-sandbox.d.ts.map +1 -1
  21. package/dist/lib/sandbox/daytona-sandbox.d.ts +2 -2
  22. package/dist/lib/sandbox/daytona-sandbox.d.ts.map +1 -1
  23. package/dist/lib/sandbox/docker-sandbox.d.ts.map +1 -1
  24. package/dist/lib/sandbox/index.d.ts +1 -0
  25. package/dist/lib/sandbox/index.d.ts.map +1 -1
  26. package/dist/lib/sandbox/microsandbox-sandbox.d.ts +93 -0
  27. package/dist/lib/sandbox/microsandbox-sandbox.d.ts.map +1 -0
  28. package/dist/lib/sandbox/types.d.ts +19 -0
  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 +13 -1
  33. package/dist/lib/save/save-pipeline.d.ts.map +1 -1
  34. package/dist/lib/store/postgres.store.d.ts +2 -1
  35. package/dist/lib/store/postgres.store.d.ts.map +1 -1
  36. package/dist/lib/store/sqlite.store.d.ts +2 -1
  37. package/dist/lib/store/sqlite.store.d.ts.map +1 -1
  38. package/dist/lib/store/sqlserver.store.d.ts +2 -1
  39. package/dist/lib/store/sqlserver.store.d.ts.map +1 -1
  40. package/dist/lib/store/store.d.ts +17 -2
  41. package/dist/lib/store/store.d.ts.map +1 -1
  42. package/package.json +12 -8
package/dist/index.js CHANGED
@@ -1595,6 +1595,7 @@ var Agent = class _Agent {
1595
1595
  ),
1596
1596
  toolChoice: this.#options.toolChoice,
1597
1597
  onStepFinish: (step) => {
1598
+ if (!this.#options.logging) return;
1598
1599
  const toolCall2 = step.toolCalls.at(-1);
1599
1600
  if (toolCall2) {
1600
1601
  console.log(
@@ -1672,6 +1673,7 @@ var Agent = class _Agent {
1672
1673
  experimental_context: contextVariables,
1673
1674
  toolChoice: this.#options.toolChoice,
1674
1675
  onStepFinish: (step) => {
1676
+ if (!this.#options.logging) return;
1675
1677
  const toolCall2 = step.toolCalls.at(-1);
1676
1678
  if (toolCall2) {
1677
1679
  console.log(
@@ -1932,7 +1934,10 @@ function wrapToolsWithOutputReminders(tools, context) {
1932
1934
  execute: async (input, options) => {
1933
1935
  const result = await execute.call(toolDef, input, options);
1934
1936
  if (isAsyncIterable(result)) return result;
1935
- return context.applyToolOutputReminders(result);
1937
+ return context.applyToolOutputReminders(result, {
1938
+ toolName: name,
1939
+ input
1940
+ });
1936
1941
  },
1937
1942
  toModelOutput: (args) => {
1938
1943
  const project = (output) => originalToModelOutput ? originalToModelOutput({
@@ -2611,7 +2616,15 @@ async function evaluateFiredReminders(configs, whenCtx) {
2611
2616
  }
2612
2617
 
2613
2618
  // packages/context/src/lib/save/save-pipeline.ts
2614
- var SavePipeline = class {
2619
+ var HeadConflictError = class extends Error {
2620
+ constructor(chatId, headMessageId) {
2621
+ super(
2622
+ `Could not commit head "${headMessageId}" for chat "${chatId}": concurrent writers kept moving the branch head`
2623
+ );
2624
+ this.name = "HeadConflictError";
2625
+ }
2626
+ };
2627
+ var SavePipeline = class _SavePipeline {
2615
2628
  #engine;
2616
2629
  #pending;
2617
2630
  #shouldBranch = true;
@@ -2667,9 +2680,49 @@ var SavePipeline = class {
2667
2680
  );
2668
2681
  }
2669
2682
  await this.#engine.store.addMessages(messages);
2670
- await this.#engine.commitHead(parentId);
2683
+ await this.#commitHead(messages[0], parentId);
2671
2684
  return { headMessageId: parentId };
2672
2685
  }
2686
+ static #maxCommitAttempts = 5;
2687
+ /**
2688
+ * Advance the branch head to `newHead` through the store's CAS. A stale
2689
+ * cached head must never rewind the branch (that silently orphans turns
2690
+ * committed in between), so a rejected CAS re-reads the store head and
2691
+ * reconciles:
2692
+ * - the store head already descends from `newHead` (an in-place assistant
2693
+ * fill whose turn was built past): content is on the chain, keep the
2694
+ * store head;
2695
+ * - `newHead` descends from the store head: fast-forward, retry against it;
2696
+ * - true fork (another turn won the same head): graft this turn onto the
2697
+ * winning head by re-parenting the first pending message, then retry.
2698
+ * Grafting applies only to messages chained off the stale head — an
2699
+ * in-place fill keeps its original parent and its content already landed.
2700
+ */
2701
+ async #commitHead(first2, newHead) {
2702
+ let expected = this.#engine.getActiveBranch().headMessageId;
2703
+ for (let i = 0; i < _SavePipeline.#maxCommitAttempts; i++) {
2704
+ if (await this.#engine.commitHead(newHead, expected)) return;
2705
+ const fresh = (await this.#engine.refreshBranch()).headMessageId;
2706
+ if (fresh === null || fresh === expected) {
2707
+ expected = fresh;
2708
+ continue;
2709
+ }
2710
+ if (await this.#chainContains(fresh, newHead)) return;
2711
+ if (await this.#chainContains(newHead, fresh)) {
2712
+ expected = fresh;
2713
+ continue;
2714
+ }
2715
+ if (first2.parentId !== expected) return;
2716
+ await this.#engine.store.setMessageParent(first2.id, fresh);
2717
+ first2.parentId = fresh;
2718
+ expected = fresh;
2719
+ }
2720
+ throw new HeadConflictError(this.#engine.chatId, newHead);
2721
+ }
2722
+ async #chainContains(headId, messageId) {
2723
+ const chain = await this.#engine.store.getMessageChain(headId);
2724
+ return chain.some((message2) => message2.id === messageId);
2725
+ }
2673
2726
  };
2674
2727
 
2675
2728
  // packages/context/src/lib/store/sqlite.store.ts
@@ -2937,6 +2990,17 @@ var SqliteContextStore = class extends ContextStore {
2937
2990
  }
2938
2991
  });
2939
2992
  }
2993
+ async setMessageParent(messageId, parentId) {
2994
+ if (parentId === messageId) {
2995
+ throw new Error(`Message ${messageId} cannot be its own parent`);
2996
+ }
2997
+ const result = this.#stmt(
2998
+ "UPDATE messages SET parentId = ? WHERE id = ?"
2999
+ ).run(parentId, messageId);
3000
+ if (result.changes === 0) {
3001
+ throw new Error(`Message ${messageId} not found`);
3002
+ }
3003
+ }
2940
3004
  async getMessage(messageId) {
2941
3005
  const row = this.#stmt("SELECT * FROM messages WHERE id = ?").get(
2942
3006
  messageId
@@ -3043,11 +3107,11 @@ var SqliteContextStore = class extends ContextStore {
3043
3107
  this.#db.prepare("UPDATE branches SET isActive = 0 WHERE chatId = ?").run(chatId);
3044
3108
  this.#db.prepare("UPDATE branches SET isActive = 1 WHERE id = ?").run(branchId);
3045
3109
  }
3046
- async updateBranchHead(branchId, messageId) {
3047
- this.#stmt("UPDATE branches SET headMessageId = ? WHERE id = ?").run(
3048
- messageId,
3049
- branchId
3050
- );
3110
+ async updateBranchHead(branchId, messageId, expectedHeadMessageId) {
3111
+ const result = this.#stmt(
3112
+ "UPDATE branches SET headMessageId = ? WHERE id = ? AND headMessageId IS ?"
3113
+ ).run(messageId, branchId, expectedHeadMessageId);
3114
+ return result.changes > 0;
3051
3115
  }
3052
3116
  async listBranches(chatId) {
3053
3117
  const rows = this.#db.prepare(
@@ -3916,6 +3980,10 @@ var ContextEngine = class _ContextEngine {
3916
3980
  * return the (possibly wrapped) output.
3917
3981
  *
3918
3982
  * Called by the agent's tool wrapper right after each `execute()` resolves.
3983
+ * The wrapper passes the executing `call`, surfaced as
3984
+ * {@link WhenContext.executingTool}, so a predicate can gate on the live
3985
+ * tool's name/input/result — the only point at which the call being wrapped
3986
+ * is observable.
3919
3987
  * The store keeps the wrapped value with a host-only marker; the model-facing
3920
3988
  * projection strips that marker while preserving `result` + `systemReminder`.
3921
3989
  *
@@ -3924,17 +3992,22 @@ var ContextEngine = class _ContextEngine {
3924
3992
  * (e.g. asTool forks that set a pending user without saving), so the output
3925
3993
  * passes through untouched.
3926
3994
  */
3927
- async applyToolOutputReminders(output) {
3995
+ async applyToolOutputReminders(output, call) {
3928
3996
  const configs = this.#remindersFor("tool-output");
3929
3997
  if (configs.length === 0) return output;
3930
3998
  await this.#ensureInitialized();
3931
3999
  const chain = await this.#getChainContext();
3932
4000
  const currentMessage = chain.lastMessage;
3933
4001
  if (!currentMessage) return output;
3934
- const matched = await evaluateFiredReminders(
3935
- configs,
3936
- this.#buildWhenCtx(chain, currentMessage)
3937
- );
4002
+ const whenCtx = this.#buildWhenCtx(chain, currentMessage);
4003
+ if (call) {
4004
+ whenCtx.executingTool = {
4005
+ name: call.toolName,
4006
+ input: call.input,
4007
+ output
4008
+ };
4009
+ }
4010
+ const matched = await evaluateFiredReminders(configs, whenCtx);
3938
4011
  if (matched.length === 0) return output;
3939
4012
  return applyRemindersToToolOutput(
3940
4013
  output,
@@ -3949,12 +4022,29 @@ var ContextEngine = class _ContextEngine {
3949
4022
  id: this.#activeBranch.id,
3950
4023
  headMessageId: this.#activeBranch.headMessageId
3951
4024
  }),
3952
- commitHead: async (headMessageId) => {
3953
- await this.#store.updateBranchHead(
4025
+ commitHead: async (headMessageId, expectedHeadMessageId) => {
4026
+ const committed = await this.#store.updateBranchHead(
3954
4027
  this.#activeBranch.id,
3955
- headMessageId
4028
+ headMessageId,
4029
+ expectedHeadMessageId
4030
+ );
4031
+ if (committed) {
4032
+ this.#activeBranch.headMessageId = headMessageId;
4033
+ }
4034
+ return committed;
4035
+ },
4036
+ refreshBranch: async () => {
4037
+ const fresh = await this.#store.getBranch(
4038
+ this.#chatId,
4039
+ this.#activeBranch.name
3956
4040
  );
3957
- this.#activeBranch.headMessageId = headMessageId;
4041
+ if (!fresh) {
4042
+ throw new Error(
4043
+ `Branch "${this.#activeBranch.name}" not found for chat "${this.#chatId}"`
4044
+ );
4045
+ }
4046
+ this.#branch = fresh;
4047
+ return { id: fresh.id, headMessageId: fresh.headMessageId };
3958
4048
  },
3959
4049
  rewindForUpdate: (parentId) => this.#rewindForUpdate(parentId)
3960
4050
  };
@@ -4055,8 +4145,14 @@ var ContextEngine = class _ContextEngine {
4055
4145
  *
4056
4146
  * @example
4057
4147
  * ```ts
4058
- * context.set(user('What is 2 + 2?', { id: 'q1' }));
4059
- * context.set(assistant('The answer is 5.', { id: 'wrong' })); // Oops!
4148
+ * context.set(
4149
+ * user({
4150
+ * id: 'q1',
4151
+ * role: 'user',
4152
+ * parts: [{ type: 'text', text: 'What is 2 + 2?' }],
4153
+ * }),
4154
+ * );
4155
+ * context.set(assistantText('The answer is 5.', { id: 'wrong' })); // Oops!
4060
4156
  * await context.save();
4061
4157
  *
4062
4158
  * // Rewind to the question, creates new branch
@@ -4894,7 +4990,7 @@ async function chat(agent2, options = {}) {
4894
4990
  execute: async ({ writer }) => {
4895
4991
  writer.merge(uiStream);
4896
4992
  if (title) {
4897
- writer.write({ type: "data-chat-title", data: title });
4993
+ writer.write({ type: "data-chat-title", data: title, transient: true });
4898
4994
  }
4899
4995
  }
4900
4996
  });
@@ -6132,14 +6228,15 @@ function bindAbort(signal, onAbort) {
6132
6228
  }
6133
6229
  async function createAgentOsSandbox(options = {}) {
6134
6230
  const { AgentOs } = await importAgentOs();
6231
+ const { readiness, ...kernelOptions } = options;
6135
6232
  let os;
6136
6233
  try {
6137
- os = await AgentOs.create(options);
6234
+ os = await AgentOs.create(kernelOptions);
6138
6235
  } catch (error) {
6139
6236
  const err = error instanceof Error ? error : new Error(String(error));
6140
6237
  throw new AgentOsCreationError(err.message, err);
6141
6238
  }
6142
- return {
6239
+ const sandbox = {
6143
6240
  async executeCommand(command, { signal } = {}) {
6144
6241
  if (signal?.aborted) {
6145
6242
  return { stdout: "", stderr: "", exitCode: SIGKILL_EXIT_CODE };
@@ -6219,6 +6316,16 @@ async function createAgentOsSandbox(options = {}) {
6219
6316
  return this.dispose();
6220
6317
  }
6221
6318
  };
6319
+ if (readiness) {
6320
+ try {
6321
+ await readiness(sandbox);
6322
+ } catch (error) {
6323
+ await sandbox.dispose().catch(() => {
6324
+ });
6325
+ throw error;
6326
+ }
6327
+ }
6328
+ return sandbox;
6222
6329
  }
6223
6330
  async function useAgentOsSandbox(options, fn) {
6224
6331
  const sandbox = await createAgentOsSandbox(options);
@@ -6672,7 +6779,17 @@ var ContainerSandboxStrategy = class {
6672
6779
  await this.cleanupCreatedVolumesAfterFailure(error);
6673
6780
  throw error;
6674
6781
  }
6675
- return this.createSandboxMethods();
6782
+ const sandbox = this.createSandboxMethods();
6783
+ if (this.opts.readiness) {
6784
+ try {
6785
+ await this.opts.readiness(sandbox);
6786
+ } catch (error) {
6787
+ await sandbox.dispose().catch(() => {
6788
+ });
6789
+ throw error;
6790
+ }
6791
+ }
6792
+ return sandbox;
6676
6793
  }
6677
6794
  namedContainerId() {
6678
6795
  return `sandbox-${this.name}`;
@@ -7045,15 +7162,13 @@ function isAppleContainerfileOptions(opts) {
7045
7162
  }
7046
7163
  function buildMountArg(volume) {
7047
7164
  const readOnly = volume.readOnly !== false;
7048
- const parts = volume.type === "bind" ? [
7165
+ if (volume.type === "volume") {
7166
+ return `${volume.name}:${volume.containerPath}${readOnly ? ":ro" : ""}`;
7167
+ }
7168
+ const parts = [
7049
7169
  "type=bind",
7050
7170
  `source=${volume.hostPath}`,
7051
7171
  `target=${volume.containerPath}`
7052
- ] : [
7053
- // Named volumes mount as virtiofs; `container` has no `type=volume`.
7054
- "type=virtiofs",
7055
- `source=${volume.name}`,
7056
- `target=${volume.containerPath}`
7057
7172
  ];
7058
7173
  if (readOnly) {
7059
7174
  parts.push("readonly");
@@ -7073,8 +7188,9 @@ function safeParseArray(stdout) {
7073
7188
  }
7074
7189
  }
7075
7190
  function readContainerStatus(entry) {
7191
+ const raw = entry?.status;
7076
7192
  const status = String(
7077
- entry?.status ?? ""
7193
+ (typeof raw === "object" && raw !== null ? raw.state : raw) ?? ""
7078
7194
  ).toLowerCase();
7079
7195
  if (status === "running" || status === "booted") return "running";
7080
7196
  if (status === "stopped") return "stopped";
@@ -7104,7 +7220,10 @@ var appleEngine = {
7104
7220
  args.push("--env", `${key}=${value}`);
7105
7221
  }
7106
7222
  for (const volume of opts.volumes ?? []) {
7107
- args.push("--mount", buildMountArg(volume));
7223
+ args.push(
7224
+ volume.type === "volume" ? "--volume" : "--mount",
7225
+ buildMountArg(volume)
7226
+ );
7108
7227
  }
7109
7228
  args.push(image);
7110
7229
  if (opts.command === void 0) {
@@ -7131,7 +7250,6 @@ var appleEngine = {
7131
7250
  inspectArgs(containerId) {
7132
7251
  return ["inspect", containerId];
7133
7252
  },
7134
- mountArg: buildMountArg,
7135
7253
  parseStatus(stdout) {
7136
7254
  const entries = safeParseArray(stdout);
7137
7255
  if (entries.length === 0) return "absent";
@@ -7356,12 +7474,6 @@ function useBashMeta() {
7356
7474
  return {
7357
7475
  setHidden(patch) {
7358
7476
  state.hidden = { ...state.hidden, ...patch };
7359
- },
7360
- setReminder(text) {
7361
- state.reminder = text;
7362
- },
7363
- clearReminder() {
7364
- state.reminder = void 0;
7365
7477
  }
7366
7478
  };
7367
7479
  }
@@ -7571,14 +7683,8 @@ async function createBashTool(options) {
7571
7683
  const result = await originalExecute({ command }, execOptions);
7572
7684
  const state = readBashMeta();
7573
7685
  if (!state) return result;
7574
- const hasHidden = Object.keys(state.hidden).length > 0;
7575
- const hasReminder = state.reminder !== void 0;
7576
- if (!hasHidden && !hasReminder) return result;
7577
- return {
7578
- ...result,
7579
- ...hasHidden ? { meta: state.hidden } : {},
7580
- ...hasReminder ? { reminder: state.reminder } : {}
7581
- };
7686
+ if (Object.keys(state.hidden).length === 0) return result;
7687
+ return { ...result, meta: state.hidden };
7582
7688
  })
7583
7689
  );
7584
7690
  },
@@ -7752,10 +7858,20 @@ async function createDaytonaSandbox(client, options = {}) {
7752
7858
  } catch (error) {
7753
7859
  throw normalizeDaytonaError(error, sdk);
7754
7860
  }
7755
- return createDaytonaSandboxMethods({
7861
+ const backend = createDaytonaSandboxMethods({
7756
7862
  sandbox,
7757
7863
  commandTimeout: options.commandTimeout
7758
7864
  });
7865
+ if (options.readiness) {
7866
+ try {
7867
+ await options.readiness(backend);
7868
+ } catch (error) {
7869
+ await backend.dispose().catch(() => {
7870
+ });
7871
+ throw error;
7872
+ }
7873
+ }
7874
+ return backend;
7759
7875
  }
7760
7876
  var UNRECOVERABLE_SANDBOX_STATES = /* @__PURE__ */ new Set([
7761
7877
  "error",
@@ -8393,7 +8509,6 @@ var dockerEngine = {
8393
8509
  containerId
8394
8510
  ];
8395
8511
  },
8396
- mountArg: dockerMountArg,
8397
8512
  parseStatus(status) {
8398
8513
  return status === "running" ? "running" : "stopped";
8399
8514
  },
@@ -9233,6 +9348,359 @@ function formatVersion(version) {
9233
9348
  return /^[<>=!~]/.test(version) ? version : `==${version}`;
9234
9349
  }
9235
9350
 
9351
+ // packages/context/src/lib/sandbox/microsandbox-sandbox.ts
9352
+ import "bash-tool";
9353
+ import { randomUUID as randomUUID3 } from "node:crypto";
9354
+ var MICROSANDBOX_DEFAULT_DESTINATION = "/workspace";
9355
+ var MICROSANDBOX_DEFAULT_IMAGE = "alpine";
9356
+ var MICROSANDBOX_MAX_NAME_BYTES = 128;
9357
+ var COMMAND_TIMEOUT_EXIT_CODE = 124;
9358
+ var MicrosandboxSandboxError = class extends Error {
9359
+ constructor(message2, cause) {
9360
+ super(message2);
9361
+ this.name = "MicrosandboxSandboxError";
9362
+ this.cause = cause;
9363
+ }
9364
+ };
9365
+ var MicrosandboxNotAvailableError = class extends MicrosandboxSandboxError {
9366
+ constructor(cause) {
9367
+ super(
9368
+ "microsandbox is not installed or its runtime is unavailable. Install it with: npm install microsandbox (requires Node >= 22 and hardware virtualization: Apple silicon, Linux with KVM, or Windows with WHP)",
9369
+ cause
9370
+ );
9371
+ this.name = "MicrosandboxNotAvailableError";
9372
+ }
9373
+ };
9374
+ var MicrosandboxCreationError = class extends MicrosandboxSandboxError {
9375
+ constructor(message2, cause) {
9376
+ super(`Failed to create microsandbox: ${message2}`, cause);
9377
+ this.name = "MicrosandboxCreationError";
9378
+ }
9379
+ };
9380
+ var MicrosandboxCommandError = class extends MicrosandboxSandboxError {
9381
+ constructor(message2, cause) {
9382
+ super(message2, cause);
9383
+ this.name = "MicrosandboxCommandError";
9384
+ }
9385
+ };
9386
+ async function createMicrosandboxSandbox(options = {}) {
9387
+ validateMicrosandboxOptions(options);
9388
+ const sdk = await importMicrosandbox();
9389
+ const ephemeral = options.name === void 0;
9390
+ const name = options.name ?? `deepagents-msb-${randomUUID3()}`;
9391
+ const workdir = options.workdir ?? MICROSANDBOX_DEFAULT_DESTINATION;
9392
+ let vm;
9393
+ try {
9394
+ vm = await acquireSandbox(sdk, { ...options, name, ephemeral, workdir });
9395
+ await vm.fs().mkdir(workdir);
9396
+ } catch (error) {
9397
+ throw normalizeMicrosandboxError(error, sdk);
9398
+ }
9399
+ const backend = createMicrosandboxMethods({
9400
+ sdk,
9401
+ vm,
9402
+ name,
9403
+ ephemeral,
9404
+ commandTimeout: options.commandTimeout
9405
+ });
9406
+ if (options.readiness) {
9407
+ try {
9408
+ await options.readiness(backend);
9409
+ } catch (error) {
9410
+ await backend.dispose().catch(() => {
9411
+ });
9412
+ throw error;
9413
+ }
9414
+ }
9415
+ return backend;
9416
+ }
9417
+ async function importMicrosandbox() {
9418
+ try {
9419
+ return await import("microsandbox");
9420
+ } catch (error) {
9421
+ throw new MicrosandboxNotAvailableError(toError2(error));
9422
+ }
9423
+ }
9424
+ async function acquireSandbox(sdk, options) {
9425
+ if (options.ephemeral || options.replace) {
9426
+ return buildSandbox(sdk, options);
9427
+ }
9428
+ let handle;
9429
+ try {
9430
+ handle = await sdk.Sandbox.get(options.name);
9431
+ } catch (error) {
9432
+ if (error instanceof sdk.SandboxNotFoundError) {
9433
+ return createFreshSandbox(sdk, options);
9434
+ }
9435
+ throw error;
9436
+ }
9437
+ if (handle.status === "running") {
9438
+ return handle.connect();
9439
+ }
9440
+ if (handle.status === "draining") {
9441
+ await handle.waitUntilStopped();
9442
+ }
9443
+ try {
9444
+ return await handle.start();
9445
+ } catch {
9446
+ await sdk.Sandbox.remove(options.name).catch(() => {
9447
+ });
9448
+ return buildSandbox(sdk, options);
9449
+ }
9450
+ }
9451
+ async function createFreshSandbox(sdk, options) {
9452
+ try {
9453
+ return await buildSandbox(sdk, options);
9454
+ } catch (error) {
9455
+ if (error instanceof sdk.SandboxAlreadyExistsError) {
9456
+ const handle = await sdk.Sandbox.get(options.name);
9457
+ return handle.status === "running" ? handle.connect() : handle.start();
9458
+ }
9459
+ throw error;
9460
+ }
9461
+ }
9462
+ function buildSandbox(sdk, options) {
9463
+ let builder = sdk.Sandbox.builder(options.name).image(options.image ?? MICROSANDBOX_DEFAULT_IMAGE).patch((patch) => patch.mkdir(options.workdir)).workdir(options.workdir);
9464
+ if (options.ephemeral) builder = builder.ephemeral(true);
9465
+ if (options.replace) builder = builder.replace();
9466
+ if (options.cpus !== void 0) builder = builder.cpus(options.cpus);
9467
+ if (options.memory !== void 0) builder = builder.memory(options.memory);
9468
+ if (options.env) builder = builder.envs(options.env);
9469
+ if (options.configure) builder = options.configure(builder);
9470
+ return builder.create();
9471
+ }
9472
+ function normalizeMicrosandboxError(error, sdk) {
9473
+ const err = toError2(error);
9474
+ if (err instanceof sdk.LibkrunfwNotFoundError) {
9475
+ return new MicrosandboxNotAvailableError(err);
9476
+ }
9477
+ if (err instanceof sdk.MicrosandboxError) {
9478
+ return err;
9479
+ }
9480
+ return new MicrosandboxCreationError(err.message, err);
9481
+ }
9482
+ function validateMicrosandboxOptions(options) {
9483
+ if (options.name !== void 0 && Buffer.byteLength(options.name, "utf-8") > MICROSANDBOX_MAX_NAME_BYTES) {
9484
+ throw new MicrosandboxSandboxError(
9485
+ `Microsandbox names are limited to ${MICROSANDBOX_MAX_NAME_BYTES} UTF-8 bytes.`
9486
+ );
9487
+ }
9488
+ if (options.replace && options.name === void 0) {
9489
+ throw new MicrosandboxSandboxError(
9490
+ 'Microsandbox options can only include "replace" together with "name" \u2014 an unnamed sandbox is always created fresh.'
9491
+ );
9492
+ }
9493
+ }
9494
+ function createMicrosandboxMethods(args) {
9495
+ const { sdk, vm, name, ephemeral, commandTimeout } = args;
9496
+ const spawn7 = (command, options = {}) => {
9497
+ return spawnMicrosandboxProcess(sdk, vm, command, {
9498
+ ...options,
9499
+ commandTimeout
9500
+ });
9501
+ };
9502
+ return {
9503
+ // `executeCommand` lowers onto the same streaming pump as `spawn` so a
9504
+ // single code path supports real cancellation. Cooperative abort (abandon
9505
+ // the promise, Daytona-style) is not an option here: the SDK is an
9506
+ // in-process native binding, so an abandoned exec keeps the guest process
9507
+ // and its libuv handle alive.
9508
+ async executeCommand(command, options) {
9509
+ const proc = spawn7(command, { signal: options?.signal });
9510
+ const [stdout, stderr, info] = await Promise.all([
9511
+ readAllText(proc.stdout),
9512
+ readAllText(proc.stderr),
9513
+ proc.exit
9514
+ ]);
9515
+ if (info.signal === "SIGKILL") {
9516
+ return abortedCommandResult2();
9517
+ }
9518
+ return { stdout, stderr, exitCode: info.code ?? 1 };
9519
+ },
9520
+ spawn: spawn7,
9521
+ async readFile(path5) {
9522
+ try {
9523
+ return await vm.fs().readToString(path5);
9524
+ } catch (error) {
9525
+ throw new MicrosandboxCommandError(
9526
+ `Failed to read file "${path5}": ${toError2(error).message}`,
9527
+ toError2(error)
9528
+ );
9529
+ }
9530
+ },
9531
+ async writeFiles(files) {
9532
+ try {
9533
+ const fs2 = vm.fs();
9534
+ for (const dir of uniqueParentDirectories2(files.map((f) => f.path))) {
9535
+ await fs2.mkdir(dir);
9536
+ }
9537
+ for (const file of files) {
9538
+ await fs2.write(file.path, file.content);
9539
+ }
9540
+ } catch (error) {
9541
+ const err = toError2(error);
9542
+ throw new MicrosandboxCommandError(
9543
+ `Failed to write files: ${err.message}`,
9544
+ err
9545
+ );
9546
+ }
9547
+ },
9548
+ async dispose() {
9549
+ try {
9550
+ await vm.stop();
9551
+ } catch {
9552
+ }
9553
+ if (ephemeral) {
9554
+ await sdk.Sandbox.remove(name).catch(() => {
9555
+ });
9556
+ }
9557
+ },
9558
+ [Symbol.asyncDispose]() {
9559
+ return this.dispose();
9560
+ }
9561
+ };
9562
+ }
9563
+ function spawnMicrosandboxProcess(sdk, vm, command, options) {
9564
+ const stdout = createByteReadable();
9565
+ const stderr = createByteReadable();
9566
+ const exit = pumpExecStream({ sdk, vm, command, options, stdout, stderr });
9567
+ return { stdout: stdout.stream, stderr: stderr.stream, exit };
9568
+ }
9569
+ async function pumpExecStream(args) {
9570
+ const { sdk, vm, command, options, stdout, stderr } = args;
9571
+ const { signal } = options;
9572
+ let handle;
9573
+ let aborted = signal?.aborted ?? false;
9574
+ let timedOut = false;
9575
+ let timeoutTimer;
9576
+ const abort = () => {
9577
+ aborted = true;
9578
+ handle?.kill().catch(() => {
9579
+ });
9580
+ };
9581
+ if (aborted) {
9582
+ stdout.close();
9583
+ stderr.close();
9584
+ return abortedExitInfo2();
9585
+ }
9586
+ signal?.addEventListener("abort", abort, { once: true });
9587
+ try {
9588
+ handle = await vm.execStreamWith("sh", (builder) => {
9589
+ builder.args(["-lc", command]).stdinNull();
9590
+ if (options.cwd) builder.cwd(options.cwd);
9591
+ if (options.env) builder.envs(options.env);
9592
+ return builder;
9593
+ });
9594
+ if (aborted) {
9595
+ return abortedExitInfo2();
9596
+ }
9597
+ if (options.commandTimeout) {
9598
+ const startedHandle = handle;
9599
+ timeoutTimer = setTimeout(() => {
9600
+ timedOut = true;
9601
+ startedHandle.kill().catch(() => {
9602
+ });
9603
+ }, options.commandTimeout);
9604
+ timeoutTimer.unref();
9605
+ }
9606
+ let code = null;
9607
+ for await (const event of handle) {
9608
+ if (event.kind === "stdout") stdout.enqueue(event.data);
9609
+ else if (event.kind === "stderr") stderr.enqueue(event.data);
9610
+ else if (event.kind === "exited") code = event.code;
9611
+ }
9612
+ if (aborted) {
9613
+ return abortedExitInfo2();
9614
+ }
9615
+ if (timedOut) {
9616
+ stderr.enqueue(new TextEncoder().encode("Command timed out"));
9617
+ return { code: COMMAND_TIMEOUT_EXIT_CODE, signal: null, success: false };
9618
+ }
9619
+ return { code, signal: null, success: code === 0 };
9620
+ } catch (error) {
9621
+ if (aborted) {
9622
+ return abortedExitInfo2();
9623
+ }
9624
+ if (timedOut || error instanceof sdk.ExecTimeoutError) {
9625
+ stderr.enqueue(new TextEncoder().encode("Command timed out"));
9626
+ return { code: COMMAND_TIMEOUT_EXIT_CODE, signal: null, success: false };
9627
+ }
9628
+ const err = toError2(error);
9629
+ stdout.error(err);
9630
+ stderr.error(err);
9631
+ throw err;
9632
+ } finally {
9633
+ clearTimeout(timeoutTimer);
9634
+ signal?.removeEventListener("abort", abort);
9635
+ stdout.close();
9636
+ stderr.close();
9637
+ await handle?.[Symbol.asyncDispose]();
9638
+ }
9639
+ }
9640
+ function createByteReadable() {
9641
+ let controller;
9642
+ let closed = false;
9643
+ return {
9644
+ stream: new ReadableStream({
9645
+ start(streamController) {
9646
+ controller = streamController;
9647
+ },
9648
+ cancel() {
9649
+ closed = true;
9650
+ }
9651
+ }),
9652
+ enqueue(chunk) {
9653
+ if (closed || chunk.length === 0) return;
9654
+ controller?.enqueue(chunk);
9655
+ },
9656
+ close() {
9657
+ if (closed) return;
9658
+ closed = true;
9659
+ controller?.close();
9660
+ },
9661
+ error(error) {
9662
+ if (closed) return;
9663
+ closed = true;
9664
+ controller?.error(error);
9665
+ }
9666
+ };
9667
+ }
9668
+ async function readAllText(stream) {
9669
+ const decoder2 = new TextDecoder();
9670
+ let text = "";
9671
+ for await (const chunk of stream) {
9672
+ text += decoder2.decode(chunk, { stream: true });
9673
+ }
9674
+ return text + decoder2.decode();
9675
+ }
9676
+ function abortedCommandResult2() {
9677
+ return {
9678
+ stdout: "",
9679
+ stderr: "Command aborted",
9680
+ exitCode: 1
9681
+ };
9682
+ }
9683
+ function abortedExitInfo2() {
9684
+ return {
9685
+ code: null,
9686
+ signal: "SIGKILL",
9687
+ success: false
9688
+ };
9689
+ }
9690
+ function uniqueParentDirectories2(paths) {
9691
+ const dirs = /* @__PURE__ */ new Set();
9692
+ for (const path5 of paths) {
9693
+ const index = path5.lastIndexOf("/");
9694
+ if (index > 0) {
9695
+ dirs.add(path5.slice(0, index));
9696
+ }
9697
+ }
9698
+ return [...dirs];
9699
+ }
9700
+ function toError2(error) {
9701
+ return error instanceof Error ? error : new Error(String(error));
9702
+ }
9703
+
9236
9704
  // packages/context/src/lib/sandbox/subcommand.ts
9237
9705
  import {
9238
9706
  defineCommand as defineCommand2,
@@ -9314,7 +9782,7 @@ async function createVirtualSandbox(options) {
9314
9782
  env: options.env,
9315
9783
  customCommands: options.customCommands
9316
9784
  });
9317
- return {
9785
+ const sandbox = {
9318
9786
  async executeCommand(command, options2) {
9319
9787
  const result = await bash.exec(
9320
9788
  command,
@@ -9343,6 +9811,16 @@ async function createVirtualSandbox(options) {
9343
9811
  return this.dispose();
9344
9812
  }
9345
9813
  };
9814
+ if (options.readiness) {
9815
+ try {
9816
+ await options.readiness(sandbox);
9817
+ } catch (error) {
9818
+ await sandbox.dispose().catch(() => {
9819
+ });
9820
+ throw error;
9821
+ }
9822
+ }
9823
+ return sandbox;
9346
9824
  }
9347
9825
 
9348
9826
  // packages/context/src/lib/skills/fragments.ts
@@ -9806,6 +10284,18 @@ var PostgresContextStore = class _PostgresContextStore extends ContextStore {
9806
10284
  );
9807
10285
  });
9808
10286
  }
10287
+ async setMessageParent(messageId, parentId) {
10288
+ if (parentId === messageId) {
10289
+ throw new Error(`Message ${messageId} cannot be its own parent`);
10290
+ }
10291
+ const rows = await this.#query(
10292
+ `UPDATE ${this.#t("messages")} SET parentId = $1 WHERE id = $2 RETURNING id`,
10293
+ [parentId, messageId]
10294
+ );
10295
+ if (rows.length === 0) {
10296
+ throw new Error(`Message ${messageId} not found`);
10297
+ }
10298
+ }
9809
10299
  async getMessage(messageId) {
9810
10300
  const rows = await this.#query(`SELECT * FROM ${this.#t("messages")} WHERE id = $1`, [messageId]);
9811
10301
  if (rows.length === 0) {
@@ -9928,11 +10418,14 @@ var PostgresContextStore = class _PostgresContextStore extends ContextStore {
9928
10418
  );
9929
10419
  });
9930
10420
  }
9931
- async updateBranchHead(branchId, messageId) {
9932
- await this.#query(
9933
- `UPDATE ${this.#t("branches")} SET headMessageId = $1 WHERE id = $2`,
9934
- [messageId, branchId]
10421
+ async updateBranchHead(branchId, messageId, expectedHeadMessageId) {
10422
+ const rows = await this.#query(
10423
+ `UPDATE ${this.#t("branches")} SET headMessageId = $1
10424
+ WHERE id = $2 AND headMessageId IS NOT DISTINCT FROM $3
10425
+ RETURNING id`,
10426
+ [messageId, branchId, expectedHeadMessageId]
9935
10427
  );
10428
+ return rows.length > 0;
9936
10429
  }
9937
10430
  async listBranches(chatId) {
9938
10431
  const branches = await this.#query(
@@ -10595,6 +11088,20 @@ var SqlServerContextStore = class _SqlServerContextStore extends ContextStore {
10595
11088
  `);
10596
11089
  });
10597
11090
  }
11091
+ async setMessageParent(messageId, parentId) {
11092
+ if (parentId === messageId) {
11093
+ throw new Error(`Message ${messageId} cannot be its own parent`);
11094
+ }
11095
+ const rows = await this.#query(
11096
+ `UPDATE ${this.#t("messages")} SET parentId = @p0
11097
+ OUTPUT INSERTED.id
11098
+ WHERE id = @p1`,
11099
+ [parentId, messageId]
11100
+ );
11101
+ if (rows.length === 0) {
11102
+ throw new Error(`Message ${messageId} not found`);
11103
+ }
11104
+ }
10598
11105
  async getMessage(messageId) {
10599
11106
  const rows = await this.#query(`SELECT * FROM ${this.#t("messages")} WHERE id = @p0`, [messageId]);
10600
11107
  if (rows.length === 0) {
@@ -10720,11 +11227,15 @@ var SqlServerContextStore = class _SqlServerContextStore extends ContextStore {
10720
11227
  );
10721
11228
  });
10722
11229
  }
10723
- async updateBranchHead(branchId, messageId) {
10724
- await this.#query(
10725
- `UPDATE ${this.#t("branches")} SET headMessageId = @p0 WHERE id = @p1`,
10726
- [messageId, branchId]
11230
+ async updateBranchHead(branchId, messageId, expectedHeadMessageId) {
11231
+ const rows = await this.#query(
11232
+ `UPDATE ${this.#t("branches")} SET headMessageId = @p0
11233
+ OUTPUT INSERTED.id
11234
+ WHERE id = @p1
11235
+ AND (headMessageId = @p2 OR (headMessageId IS NULL AND @p2 IS NULL))`,
11236
+ [messageId, branchId, expectedHeadMessageId]
10727
11237
  );
11238
+ return rows.length > 0;
10728
11239
  }
10729
11240
  async listBranches(chatId) {
10730
11241
  const branches = await this.#query(
@@ -12576,12 +13087,18 @@ export {
12576
13087
  FunctionResolver,
12577
13088
  GeneratorResolver,
12578
13089
  GithubReleaseInstaller,
13090
+ HeadConflictError,
12579
13091
  InMemoryContextStore,
12580
13092
  InstallError,
12581
13093
  Installer,
12582
13094
  IterableResolver,
12583
13095
  LOCALE_METADATA_KEY,
13096
+ MICROSANDBOX_DEFAULT_DESTINATION,
12584
13097
  MarkdownRenderer,
13098
+ MicrosandboxCommandError,
13099
+ MicrosandboxCreationError,
13100
+ MicrosandboxNotAvailableError,
13101
+ MicrosandboxSandboxError,
12585
13102
  MissingRuntimeError,
12586
13103
  ModelsRegistry,
12587
13104
  NpmInstaller,
@@ -12618,6 +13135,7 @@ export {
12618
13135
  analogy,
12619
13136
  and,
12620
13137
  anyToolCalled,
13138
+ appleEngine,
12621
13139
  applyInlineReminder,
12622
13140
  applyPartReminder,
12623
13141
  applyReminderToMessage,
@@ -12647,6 +13165,7 @@ export {
12647
13165
  createDaytonaSandbox,
12648
13166
  createDockerSandbox,
12649
13167
  createInstallerContext,
13168
+ createMicrosandboxSandbox,
12650
13169
  createRepairToolCall,
12651
13170
  createVirtualSandbox,
12652
13171
  dateReminder,