@deepagents/context 6.1.2 → 7.0.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 (71) hide show
  1. package/README.md +26 -1
  2. package/dist/browser.js +9 -2
  3. package/dist/browser.js.map +2 -2
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +1187 -516
  7. package/dist/index.js.map +4 -4
  8. package/dist/lib/agent.d.ts +78 -37
  9. package/dist/lib/agent.d.ts.map +1 -1
  10. package/dist/lib/chat.d.ts +16 -17
  11. package/dist/lib/chat.d.ts.map +1 -1
  12. package/dist/lib/engine.d.ts.map +1 -1
  13. package/dist/lib/fragments/plan-shared.d.ts +46 -0
  14. package/dist/lib/fragments/plan-shared.d.ts.map +1 -0
  15. package/dist/lib/fragments/plan.d.ts.map +1 -1
  16. package/dist/lib/fragments/socratic-inquiry.d.ts +4 -0
  17. package/dist/lib/fragments/socratic-inquiry.d.ts.map +1 -0
  18. package/dist/lib/fragments/socratic-plan.d.ts +13 -0
  19. package/dist/lib/fragments/socratic-plan.d.ts.map +1 -0
  20. package/dist/lib/fragments/socratic.d.ts +2 -5
  21. package/dist/lib/fragments/socratic.d.ts.map +1 -1
  22. package/dist/lib/sandbox/agent-os-sandbox.d.ts.map +1 -1
  23. package/dist/lib/sandbox/apple-container-sandbox.d.ts.map +1 -1
  24. package/dist/lib/sandbox/bash-tool.d.ts +1 -1
  25. package/dist/lib/sandbox/bash-tool.d.ts.map +1 -1
  26. package/dist/lib/sandbox/cli-process.d.ts +6 -0
  27. package/dist/lib/sandbox/cli-process.d.ts.map +1 -1
  28. package/dist/lib/sandbox/container-engine.d.ts +1 -0
  29. package/dist/lib/sandbox/container-engine.d.ts.map +1 -1
  30. package/dist/lib/sandbox/container-sandbox.d.ts.map +1 -1
  31. package/dist/lib/sandbox/daytona-sandbox.d.ts.map +1 -1
  32. package/dist/lib/sandbox/docker-sandbox.d.ts +7 -0
  33. package/dist/lib/sandbox/docker-sandbox.d.ts.map +1 -1
  34. package/dist/lib/sandbox/index.d.ts +1 -0
  35. package/dist/lib/sandbox/index.d.ts.map +1 -1
  36. package/dist/lib/sandbox/microsandbox-sandbox.d.ts +13 -17
  37. package/dist/lib/sandbox/microsandbox-sandbox.d.ts.map +1 -1
  38. package/dist/lib/sandbox/read-file-tool/binary.d.ts +13 -0
  39. package/dist/lib/sandbox/read-file-tool/binary.d.ts.map +1 -0
  40. package/dist/lib/sandbox/read-file-tool/format.d.ts +17 -0
  41. package/dist/lib/sandbox/read-file-tool/format.d.ts.map +1 -0
  42. package/dist/lib/sandbox/read-file-tool/image.d.ts +4 -0
  43. package/dist/lib/sandbox/read-file-tool/image.d.ts.map +1 -0
  44. package/dist/lib/sandbox/read-file-tool/index.d.ts +6 -0
  45. package/dist/lib/sandbox/read-file-tool/index.d.ts.map +1 -0
  46. package/dist/lib/sandbox/read-file-tool/ooxml.d.ts +4 -0
  47. package/dist/lib/sandbox/read-file-tool/ooxml.d.ts.map +1 -0
  48. package/dist/lib/sandbox/read-file-tool/pdf.d.ts +4 -0
  49. package/dist/lib/sandbox/read-file-tool/pdf.d.ts.map +1 -0
  50. package/dist/lib/sandbox/read-file-tool/text.d.ts +10 -0
  51. package/dist/lib/sandbox/read-file-tool/text.d.ts.map +1 -0
  52. package/dist/lib/sandbox/read-file.d.ts +8 -0
  53. package/dist/lib/sandbox/read-file.d.ts.map +1 -0
  54. package/dist/lib/sandbox/types.d.ts +19 -5
  55. package/dist/lib/sandbox/types.d.ts.map +1 -1
  56. package/dist/lib/sandbox/virtual-sandbox.d.ts.map +1 -1
  57. package/dist/lib/stream-buffer.d.ts +1 -1
  58. package/dist/lib/stream-buffer.d.ts.map +1 -1
  59. package/dist/lib/telemetry/console-telemetry.d.ts +0 -1
  60. package/dist/lib/telemetry/console-telemetry.d.ts.map +1 -1
  61. package/dist/lib/telemetry/file/file-telemetry.d.ts +5 -3
  62. package/dist/lib/telemetry/file/file-telemetry.d.ts.map +1 -1
  63. package/dist/lib/telemetry/file/index.js +16 -11
  64. package/dist/lib/telemetry/file/index.js.map +2 -2
  65. package/dist/lib/telemetry/index.js +3 -4
  66. package/dist/lib/telemetry/index.js.map +2 -2
  67. package/dist/lib/telemetry/safe-serialize.d.ts +2 -2
  68. package/dist/lib/telemetry/safe-serialize.d.ts.map +1 -1
  69. package/dist/lib/tracing/index.js +4 -4
  70. package/dist/lib/tracing/index.js.map +2 -2
  71. package/package.json +9 -8
package/dist/index.js CHANGED
@@ -313,23 +313,23 @@ import {
313
313
  } from "ai";
314
314
  import chalk from "chalk";
315
315
  function createRepairToolCall(model, abortSignal) {
316
- return async ({ toolCall: toolCall2, tools, inputSchema, error }) => {
316
+ return async ({ toolCall: toolCall2, tools, inputSchema: inputSchema2, error }) => {
317
317
  if (NoSuchToolError.isInstance(error)) {
318
318
  return null;
319
319
  }
320
320
  console.log(
321
321
  `Debug: ${chalk.yellow("RepairingToolCall")}: ${toolCall2.toolName}`
322
322
  );
323
- const tool3 = tools[toolCall2.toolName];
323
+ const tool4 = tools[toolCall2.toolName];
324
324
  const { output } = await generateText({
325
325
  abortSignal,
326
326
  model,
327
- output: Output.object({ schema: tool3.inputSchema }),
327
+ output: Output.object({ schema: tool4.inputSchema }),
328
328
  prompt: [
329
329
  `The model tried to call the tool "${toolCall2.toolName}" with the following inputs:`,
330
330
  JSON.stringify(toolCall2.input),
331
331
  `The tool accepts the following schema:`,
332
- JSON.stringify(inputSchema(toolCall2)),
332
+ JSON.stringify(inputSchema2(toolCall2)),
333
333
  "Please fix the inputs."
334
334
  ].join("\n")
335
335
  });
@@ -348,16 +348,15 @@ var Agent = class _Agent {
348
348
  sandbox;
349
349
  constructor(options) {
350
350
  this.#options = options;
351
- this.tools = withHostOnlyToolMetadata({
352
- ...options.sandbox.tools,
353
- ...options.tools || {}
354
- });
351
+ this.tools = withHostOnlyToolMetadata(
352
+ Object.assign({}, options.sandbox.tools, options.tools)
353
+ );
355
354
  this.context = options.context;
356
355
  this.model = options.model;
357
356
  this.sandbox = options.sandbox;
358
357
  this.#guardrails = options.guardrails || [];
359
358
  }
360
- async generate(contextVariables, config) {
359
+ async generate(...[options]) {
361
360
  if (!this.#options.context) {
362
361
  throw new Error(`Agent ${this.#options.name} is missing a context.`);
363
362
  }
@@ -369,9 +368,10 @@ var Agent = class _Agent {
369
368
  sandbox: this.#options.sandbox
370
369
  });
371
370
  return generateText2({
372
- abortSignal: config?.abortSignal,
371
+ abortSignal: options?.abortSignal,
373
372
  providerOptions: this.#options.providerOptions,
374
373
  telemetry: this.#options.telemetry,
374
+ runtimeContext: this.#options.runtimeContext,
375
375
  model: this.#options.model,
376
376
  instructions: systemPrompt,
377
377
  messages: await convertToModelMessages(messages, {
@@ -379,16 +379,18 @@ var Agent = class _Agent {
379
379
  tools: this.tools
380
380
  }),
381
381
  stopWhen: this.#options.stopWhen ?? DEFAULT_STOP_WHEN,
382
- prepareStep: this.#options.context.createPrepareStep({
383
- steer: false,
384
- sandbox: this.#options.sandbox
385
- }),
382
+ prepareStep: this.#withPrepareStep(
383
+ this.#options.context.createPrepareStep({
384
+ steer: false,
385
+ sandbox: this.#options.sandbox
386
+ })
387
+ ),
386
388
  tools: this.tools,
387
- runtimeContext: contextVariables,
388
- toolsContext: createToolsContext(this.tools, contextVariables),
389
+ toolsContext: options?.toolsContext,
390
+ experimental_toolCallers: this.#options.experimental_toolCallers,
389
391
  repairToolCall: createRepairToolCall(
390
392
  this.#options.model,
391
- config?.abortSignal
393
+ options?.abortSignal
392
394
  ),
393
395
  toolChoice: this.#options.toolChoice
394
396
  });
@@ -401,7 +403,7 @@ var Agent = class _Agent {
401
403
  *
402
404
  * @example
403
405
  * ```typescript
404
- * const stream = await agent.stream({});
406
+ * const stream = await agent.stream();
405
407
  *
406
408
  * // With guardrails - use toUIMessageStream for protection
407
409
  * await printer.readableStream(stream.toUIMessageStream());
@@ -410,20 +412,22 @@ var Agent = class _Agent {
410
412
  * await printer.stdout(stream);
411
413
  * ```
412
414
  */
413
- async stream(contextVariables, config) {
415
+ async stream(...[options]) {
414
416
  if (!this.#options.context) {
415
417
  throw new Error(`Agent ${this.#options.name} is missing a context.`);
416
418
  }
417
419
  if (!this.#options.model) {
418
420
  throw new Error(`Agent ${this.#options.name} is missing a model.`);
419
421
  }
420
- const prepareStep = this.#options.context.createPrepareStep({
421
- additionalInput: this.#options.prepareStepInput,
422
- sandbox: this.#options.sandbox
423
- });
422
+ const prepareStep = this.#withPrepareStep(
423
+ this.#options.context.createPrepareStep({
424
+ additionalInput: this.#options.prepareStepInput,
425
+ sandbox: this.#options.sandbox
426
+ })
427
+ );
424
428
  const result = await this.#createRawStream(
425
- contextVariables,
426
- config,
429
+ options?.toolsContext,
430
+ options,
427
431
  prepareStep
428
432
  );
429
433
  if (this.#guardrails.length === 0) {
@@ -431,15 +435,27 @@ var Agent = class _Agent {
431
435
  }
432
436
  return this.#wrapWithGuardrails(
433
437
  result,
434
- contextVariables,
435
- config,
438
+ options?.toolsContext,
439
+ options,
436
440
  prepareStep
437
441
  );
438
442
  }
443
+ #withPrepareStep(prepareContext) {
444
+ const prepareStep = this.#options.prepareStep;
445
+ if (!prepareStep) return prepareContext;
446
+ return async (input) => {
447
+ const context = await prepareContext(input);
448
+ const overrides = await prepareStep({
449
+ ...input,
450
+ ...context
451
+ });
452
+ return { ...context, ...overrides };
453
+ };
454
+ }
439
455
  /**
440
456
  * Create a raw stream without guardrail processing.
441
457
  */
442
- async #createRawStream(contextVariables, config, prepareStep) {
458
+ async #createRawStream(toolsContext, config, prepareStep) {
443
459
  const context = this.#options.context;
444
460
  if (!context) {
445
461
  throw new Error(`Agent ${this.#options.name} is missing a context.`);
@@ -454,8 +470,13 @@ var Agent = class _Agent {
454
470
  });
455
471
  return streamText({
456
472
  abortSignal: config?.abortSignal,
473
+ onStart: config?.onStart,
474
+ onStepStart: config?.onStepStart,
475
+ onStepEnd: config?.onStepEnd,
476
+ include: config?.include,
457
477
  providerOptions: this.#options.providerOptions,
458
478
  telemetry: this.#options.telemetry,
479
+ runtimeContext: this.#options.runtimeContext,
459
480
  model,
460
481
  instructions: systemPrompt,
461
482
  messages: await convertToModelMessages(messages, {
@@ -464,11 +485,15 @@ var Agent = class _Agent {
464
485
  }),
465
486
  repairToolCall: createRepairToolCall(model, config?.abortSignal),
466
487
  stopWhen: this.#options.stopWhen ?? DEFAULT_STOP_WHEN,
467
- prepareStep: prepareStep ?? context.createPrepareStep({ sandbox: this.#options.sandbox }),
488
+ // Streaming erases the tool-set type; the callback still receives this.tools.
489
+ prepareStep: prepareStep ?? context.createPrepareStep({
490
+ sandbox: this.#options.sandbox
491
+ }),
468
492
  experimental_transform: config?.transform ?? smoothStream(),
469
493
  tools: this.tools,
470
- runtimeContext: contextVariables,
471
- toolsContext: createToolsContext(this.tools, contextVariables),
494
+ // Generic wrappers cannot reduce AI SDK's conditional context or caller maps.
495
+ toolsContext,
496
+ experimental_toolCallers: this.#options.experimental_toolCallers,
472
497
  toolChoice: this.#options.toolChoice
473
498
  });
474
499
  }
@@ -480,7 +505,7 @@ var Agent = class _Agent {
480
505
  * 2. A finish-step is emitted, triggering onStepEnd to persist the self-correction
481
506
  * 3. A new stream is started and the model continues from the correction
482
507
  */
483
- #wrapWithGuardrails(result, contextVariables, config, prepareStep) {
508
+ #wrapWithGuardrails(result, toolsContext, config, prepareStep) {
484
509
  const maxRetries = config?.maxRetries ?? this.#options.maxGuardrailRetries ?? 3;
485
510
  const context = this.#options.context;
486
511
  if (!context) {
@@ -570,7 +595,7 @@ var Agent = class _Agent {
570
595
  writer.write({ type: "finish-step" });
571
596
  await stepSaved.promise;
572
597
  currentResult = await this.#createRawStream(
573
- contextVariables,
598
+ toolsContext,
574
599
  config,
575
600
  prepareStep
576
601
  );
@@ -614,21 +639,10 @@ var Agent = class _Agent {
614
639
  ${output}
615
640
  </OutputInstructions>` : input;
616
641
  ctx.set(user(prompt));
617
- const sub = agent({
618
- name: this.#options.name,
619
- sandbox: this.#options.sandbox,
620
- model: this.model,
621
- context: ctx,
622
- tools: this.#options.tools,
623
- providerOptions: this.#options.providerOptions,
624
- telemetry: this.#options.telemetry
642
+ const result = await this.clone({ context: ctx }).generate({
643
+ toolsContext: options.context,
644
+ abortSignal: options.abortSignal
625
645
  });
626
- const result = await sub.generate(
627
- {},
628
- {
629
- abortSignal: options.abortSignal
630
- }
631
- );
632
646
  if (props?.outputExtractor) {
633
647
  return await props.outputExtractor(result);
634
648
  }
@@ -721,9 +735,9 @@ function agent(options) {
721
735
  return new Agent(options);
722
736
  }
723
737
  function structuredOutput(options) {
724
- const tools = options.tools ? withHostOnlyToolMetadata(options.tools) : void 0;
738
+ const tools = withHostOnlyToolMetadata({ ...options.tools });
725
739
  return {
726
- async generate(contextVariables, config) {
740
+ async generate(...[callOptions]) {
727
741
  if (!options.context) {
728
742
  throw new Error(`structuredOutput is missing a context.`);
729
743
  }
@@ -735,7 +749,7 @@ function structuredOutput(options) {
735
749
  sandbox: options.sandbox
736
750
  });
737
751
  const result = await generateText2({
738
- abortSignal: config?.abortSignal,
752
+ abortSignal: callOptions?.abortSignal,
739
753
  providerOptions: options.providerOptions,
740
754
  telemetry: options.telemetry,
741
755
  model: options.model,
@@ -747,16 +761,15 @@ function structuredOutput(options) {
747
761
  stopWhen: DEFAULT_STOP_WHEN,
748
762
  repairToolCall: createRepairToolCall(
749
763
  options.model,
750
- config?.abortSignal
764
+ callOptions?.abortSignal
751
765
  ),
752
- runtimeContext: contextVariables,
753
- toolsContext: createToolsContext(tools ?? {}, contextVariables),
766
+ toolsContext: callOptions?.toolsContext,
754
767
  output: Output2.object({ schema: options.schema }),
755
768
  tools
756
769
  });
757
770
  return result.output;
758
771
  },
759
- async stream(contextVariables, config) {
772
+ async stream(...[callOptions]) {
760
773
  if (!options.context) {
761
774
  throw new Error(`structuredOutput is missing a context.`);
762
775
  }
@@ -768,34 +781,28 @@ function structuredOutput(options) {
768
781
  sandbox: options.sandbox
769
782
  });
770
783
  return streamText({
771
- abortSignal: config?.abortSignal,
784
+ abortSignal: callOptions?.abortSignal,
772
785
  providerOptions: options.providerOptions,
773
786
  telemetry: options.telemetry,
774
787
  model: options.model,
775
788
  instructions: systemPrompt,
776
789
  repairToolCall: createRepairToolCall(
777
790
  options.model,
778
- config?.abortSignal
791
+ callOptions?.abortSignal
779
792
  ),
780
793
  messages: await convertToModelMessages(messages, {
781
794
  ignoreIncompleteToolCalls: true,
782
795
  tools
783
796
  }),
784
797
  stopWhen: DEFAULT_STOP_WHEN,
785
- experimental_transform: config?.transform ?? smoothStream(),
786
- runtimeContext: contextVariables,
787
- toolsContext: createToolsContext(tools ?? {}, contextVariables),
798
+ experimental_transform: callOptions?.transform ?? smoothStream(),
799
+ toolsContext: callOptions?.toolsContext,
788
800
  output: Output2.object({ schema: options.schema }),
789
801
  tools
790
802
  });
791
803
  }
792
804
  };
793
805
  }
794
- function createToolsContext(tools, context) {
795
- return Object.fromEntries(
796
- Object.keys(tools).map((toolName) => [toolName, context])
797
- );
798
- }
799
806
  function chunkId(chunk) {
800
807
  if (typeof chunk !== "object" || chunk === null || !("id" in chunk)) {
801
808
  return void 0;
@@ -1437,13 +1444,13 @@ var TomlRenderer = class extends ContextRenderer {
1437
1444
  const entries = this.#renderObjectEntries(obj, newPath);
1438
1445
  return ["", `[${newPath.join(".")}]`, ...entries].join("\n");
1439
1446
  }
1440
- #renderObjectEntries(obj, path3) {
1447
+ #renderObjectEntries(obj, path6) {
1441
1448
  return Object.entries(obj).map(([key, value]) => {
1442
1449
  if (value == null) {
1443
1450
  return "";
1444
1451
  }
1445
1452
  if (isFragmentObject(value)) {
1446
- const newPath = [...path3, key];
1453
+ const newPath = [...path6, key];
1447
1454
  const entries = this.#renderObjectEntries(value, newPath);
1448
1455
  return ["", `[${newPath.join(".")}]`, ...entries].join("\n");
1449
1456
  }
@@ -3026,30 +3033,30 @@ var FragmentLoaderResolver = class {
3026
3033
  fragments.map((f) => this.#walkFragment(f, ctx, 0, /* @__PURE__ */ new Set(), f.name))
3027
3034
  );
3028
3035
  }
3029
- async #walkFragment(fragment2, ctx, depth, ancestors, path3) {
3036
+ async #walkFragment(fragment2, ctx, depth, ancestors, path6) {
3030
3037
  if (!("data" in fragment2)) return;
3031
3038
  fragment2.data = await this.#walkData(
3032
3039
  fragment2.data,
3033
3040
  ctx,
3034
3041
  depth,
3035
3042
  ancestors,
3036
- path3
3043
+ path6
3037
3044
  );
3038
3045
  }
3039
- async #walkData(value, ctx, depth, ancestors, path3) {
3046
+ async #walkData(value, ctx, depth, ancestors, path6) {
3040
3047
  if (ctx.signal?.aborted) {
3041
3048
  throw ctx.signal.reason ?? new Error("Resolver aborted");
3042
3049
  }
3043
3050
  if (depth > this.#maxDepth) {
3044
3051
  throw new Error(
3045
- `Resolver recursion exceeded maxDepth=${this.#maxDepth} at fragment '${path3}'`
3052
+ `Resolver recursion exceeded maxDepth=${this.#maxDepth} at fragment '${path6}'`
3046
3053
  );
3047
3054
  }
3048
3055
  const handler = this.#resolvers.find((r) => r.canResolve(value));
3049
3056
  if (handler) {
3050
3057
  if (handler.requiresSandbox && !ctx.sandbox) {
3051
3058
  throw new Error(
3052
- `Fragment '${path3}' is dispatched to ${handler.name}, which requires a sandbox; none was provided`
3059
+ `Fragment '${path6}' is dispatched to ${handler.name}, which requires a sandbox; none was provided`
3053
3060
  );
3054
3061
  }
3055
3062
  let resolved;
@@ -3060,11 +3067,11 @@ var FragmentLoaderResolver = class {
3060
3067
  );
3061
3068
  } catch (cause) {
3062
3069
  throw new Error(
3063
- `Async fragment '${path3}' failed in ${handler.name}: ${cause instanceof Error ? cause.message : String(cause)}`,
3070
+ `Async fragment '${path6}' failed in ${handler.name}: ${cause instanceof Error ? cause.message : String(cause)}`,
3064
3071
  { cause }
3065
3072
  );
3066
3073
  }
3067
- return this.#walkData(resolved, ctx, depth + 1, ancestors, path3);
3074
+ return this.#walkData(resolved, ctx, depth + 1, ancestors, path6);
3068
3075
  }
3069
3076
  if (value === null || value === void 0) {
3070
3077
  return value;
@@ -3073,7 +3080,7 @@ var FragmentLoaderResolver = class {
3073
3080
  if (ancestors.has(value)) return void 0;
3074
3081
  const childAncestors = new Set(ancestors);
3075
3082
  childAncestors.add(value);
3076
- const childPath = `${path3}.${value.name}`;
3083
+ const childPath = `${path6}.${value.name}`;
3077
3084
  await this.#walkFragment(
3078
3085
  value,
3079
3086
  ctx,
@@ -3089,7 +3096,7 @@ var FragmentLoaderResolver = class {
3089
3096
  childAncestors.add(value);
3090
3097
  return Promise.all(
3091
3098
  value.map(
3092
- (item) => this.#walkData(item, ctx, depth, childAncestors, path3)
3099
+ (item) => this.#walkData(item, ctx, depth, childAncestors, path6)
3093
3100
  )
3094
3101
  );
3095
3102
  }
@@ -3101,7 +3108,7 @@ var FragmentLoaderResolver = class {
3101
3108
  const entries = Object.entries(value);
3102
3109
  const resolvedValues = await Promise.all(
3103
3110
  entries.map(
3104
- ([, v]) => this.#walkData(v, ctx, depth, childAncestors, path3)
3111
+ ([, v]) => this.#walkData(v, ctx, depth, childAncestors, path6)
3105
3112
  )
3106
3113
  );
3107
3114
  for (let i = 0; i < entries.length; i += 1) {
@@ -4090,6 +4097,7 @@ var ContextEngine = class _ContextEngine {
4090
4097
  );
4091
4098
  }
4092
4099
  this.#branch = branch;
4100
+ this.#branchName = branch.name;
4093
4101
  this.#initialized = true;
4094
4102
  }
4095
4103
  /**
@@ -5147,10 +5155,10 @@ var ContextEngine = class _ContextEngine {
5147
5155
  for (const fragment2 of this.#fragments) {
5148
5156
  const skills2 = fragment2.metadata?.skills;
5149
5157
  if (fragment2.name === "available_skills" && Array.isArray(skills2) && skills2.every(isAvailableSkill)) {
5150
- return skills2.map(({ name, description, path: path3 }) => ({
5158
+ return skills2.map(({ name, description, path: path6 }) => ({
5151
5159
  name,
5152
5160
  description,
5153
- path: path3
5161
+ path: path6
5154
5162
  }));
5155
5163
  }
5156
5164
  }
@@ -5638,7 +5646,7 @@ var TitleGenerator = class {
5638
5646
  context,
5639
5647
  model: options.model,
5640
5648
  schema: titleSchema
5641
- }).generate({}, { abortSignal: options.abortSignal });
5649
+ }).generate({ abortSignal: options.abortSignal });
5642
5650
  if (!title) {
5643
5651
  throw new Error("Title generation returned an empty string.");
5644
5652
  }
@@ -5673,7 +5681,8 @@ var defaultChatMessageMetadata = ({
5673
5681
  }
5674
5682
  return void 0;
5675
5683
  };
5676
- async function chat(agent2, options = {}) {
5684
+ async function chat(agent2, ...args) {
5685
+ const [options] = args;
5677
5686
  const context = agent2.context;
5678
5687
  if (!context) {
5679
5688
  throw new Error(
@@ -5688,32 +5697,11 @@ async function chat(agent2, options = {}) {
5688
5697
  }
5689
5698
  const initialAssistantMsgId = head.id;
5690
5699
  const uiMessages = await context.getMessages();
5691
- const streamContextVariables = options.contextVariables === void 0 ? {} : options.contextVariables;
5692
- const [title, result] = await Promise.all([
5693
- makeTitle({
5694
- context,
5695
- model: agent2.model,
5696
- generateTitle: options.generateTitle,
5697
- abortSignal: options.abortSignal
5698
- }),
5699
- agent2.stream(streamContextVariables, {
5700
- transform: options.transform,
5701
- abortSignal: options.abortSignal
5702
- })
5703
- ]);
5704
- const uiStream = result.toUIMessageStream({
5705
- onError: options.onError ?? formatChatError,
5706
- sendStart: true,
5707
- sendFinish: true,
5708
- sendReasoning: true,
5709
- sendSources: true,
5710
- originalMessages: uiMessages,
5711
- generateMessageId: () => initialAssistantMsgId,
5712
- messageMetadata: options.messageMetadata ?? defaultChatMessageMetadata
5713
- });
5700
+ let result;
5714
5701
  return createUIMessageStream2({
5715
5702
  originalMessages: uiMessages,
5716
5703
  generateId: () => initialAssistantMsgId,
5704
+ onError: options?.onError ?? formatChatError,
5717
5705
  onStepEnd: async ({ responseMessage }) => {
5718
5706
  await context.writeAssistantSegment(responseMessage);
5719
5707
  },
@@ -5722,7 +5710,7 @@ async function chat(agent2, options = {}) {
5722
5710
  if (isAborted) {
5723
5711
  message2 = { ...message2, parts: sanitizeAbortedParts(message2.parts) };
5724
5712
  }
5725
- const finalMetadata = await options.finalAssistantMetadata?.(message2);
5713
+ const finalMetadata = await options?.finalAssistantMetadata?.(message2);
5726
5714
  const mergedMetadata = {
5727
5715
  ...message2.metadata ?? {},
5728
5716
  ...finalMetadata ?? {}
@@ -5731,13 +5719,35 @@ async function chat(agent2, options = {}) {
5731
5719
  message2 = { ...message2, metadata: mergedMetadata };
5732
5720
  }
5733
5721
  await context.writeAssistantSegment(message2);
5734
- const usage = await result.usage;
5722
+ const usage = await result?.usage;
5735
5723
  if (usage !== void 0) {
5736
5724
  await context.trackUsage(usage);
5737
5725
  }
5738
5726
  },
5739
5727
  execute: async ({ writer }) => {
5740
- writer.merge(uiStream);
5728
+ writer.write({ type: "start", messageId: initialAssistantMsgId });
5729
+ options?.onStream?.(writer);
5730
+ const [title, streamed] = await Promise.all([
5731
+ makeTitle({
5732
+ context,
5733
+ model: agent2.model,
5734
+ generateTitle: options?.generateTitle,
5735
+ abortSignal: options?.abortSignal
5736
+ }),
5737
+ agent2.stream(...args)
5738
+ ]);
5739
+ result = streamed;
5740
+ writer.merge(
5741
+ result.toUIMessageStream({
5742
+ onError: options?.onError ?? formatChatError,
5743
+ sendStart: false,
5744
+ sendReasoning: true,
5745
+ sendSources: true,
5746
+ originalMessages: uiMessages,
5747
+ generateMessageId: () => initialAssistantMsgId,
5748
+ messageMetadata: options?.messageMetadata ?? defaultChatMessageMetadata
5749
+ })
5750
+ );
5741
5751
  if (title) {
5742
5752
  writer.write({ type: "data-chat-title", data: title, transient: true });
5743
5753
  }
@@ -5783,7 +5793,7 @@ function formatChatError(error) {
5783
5793
  console.error("Upstream API call failed:", error);
5784
5794
  return `Upstream API call failed with status ${error.statusCode}: ${error.message}`;
5785
5795
  }
5786
- return JSON.stringify(error);
5796
+ return error instanceof Error ? "An error occurred." : JSON.stringify(error);
5787
5797
  }
5788
5798
  async function makeTitle(options) {
5789
5799
  const titler = new TitleGenerator({ context: options.context });
@@ -6079,10 +6089,14 @@ function fromFragment(fragment2, options) {
6079
6089
  }
6080
6090
 
6081
6091
  // packages/context/src/lib/fragments/plan.ts
6092
+ import dedent2 from "dedent";
6093
+ import z4 from "zod";
6094
+
6095
+ // packages/context/src/lib/fragments/plan-shared.ts
6082
6096
  import dedent from "dedent";
6083
6097
  import z3 from "zod";
6084
6098
  var PLAN_FILE_PATH = "/workspace/.deepagents/plan.json";
6085
- var PLAN_INSTRUCTIONS = dedent`
6099
+ var PLAN_FOUNDATION_INSTRUCTIONS = dedent`
6086
6100
  You own the plan. The user is not required to provide a structured objective, success criteria, constraints, or task breakdown.
6087
6101
 
6088
6102
  Build and maintain the plan from the best available evidence, in this order:
@@ -6106,28 +6120,11 @@ var PLAN_INSTRUCTIONS = dedent`
6106
6120
 
6107
6121
  Never silently promote an assumption into a requirement or constraint. Ask the user only when an unresolved choice materially changes the outcome, cannot be answered from the workspace or runtime, or requires authority beyond the request. Do not ask merely because the user did not provide structured plan fields.
6108
6122
 
6109
- Maintain the authoritative plan as JSON at ${PLAN_FILE_PATH}. Read the latest file before changing it and write the complete document in one operation. The plan has this shape:
6110
- - revision: a positive integer, incremented whenever the objective, criteria, constraints, assumptions, tasks, or dependencies change; recording lastReview alone does not increment it;
6111
- - objective: { text, basis, source };
6112
- - successCriteria: [{ id, text, basis, source, evidence: [{ summary, source }] }];
6113
- - constraints and assumptions: [{ text, basis, source }];
6114
- - tasks: [{ id, title, status, blockedBy, evidence }], where status is pending, in_progress, or completed;
6115
- - lastReview, when present: { revision, decision, summary }.
6116
-
6117
- Use basis values explicit_requirement, discovered_constraint, inference, or assumption. Store only blockedBy dependency edges. Never store blocks, readiness, or a blocked task status; those are derived when the plan is read. A task may be in_progress only when every blocker is completed, and a completed task must include concrete evidence.
6118
-
6123
+ Maintain the authoritative plan as JSON at ${PLAN_FILE_PATH}. Read the latest file before changing it and write the complete document in one operation.
6124
+ `;
6125
+ var PLAN_REVIEW_INSTRUCTIONS = dedent`
6119
6126
  Whenever plan review fires, record lastReview before continuing. Its revision is the plan revision that was evaluated. If the decision is revise or replace, increment the plan revision when applying the resulting changes while preserving the evaluated revision in lastReview.
6120
-
6121
- Revise the plan when new evidence invalidates its objective, criteria, constraints, assumptions, sequencing, or dependencies. Before recording a complete decision or claiming completion, map every success criterion to concrete evidence. Completed task statuses alone do not prove completion.
6122
6127
  `;
6123
- var REVIEW_REMINDER = [
6124
- "Re-read the current plan and consider the evidence gathered since the previous review.",
6125
- "",
6126
- "Is the current plan still valid given the latest evidence?",
6127
- "",
6128
- "If yes, continue. If not, revise it before taking another action.",
6129
- "Before claiming completion, verify the success criteria against evidence."
6130
- ].join("\n");
6131
6128
  var nonEmptyString = z3.string().trim().min(1);
6132
6129
  var statementBasis = z3.enum([
6133
6130
  "explicit_requirement",
@@ -6135,40 +6132,24 @@ var statementBasis = z3.enum([
6135
6132
  "inference",
6136
6133
  "assumption"
6137
6134
  ]);
6138
- var statement = z3.strictObject({
6139
- text: nonEmptyString,
6140
- basis: statementBasis,
6141
- source: nonEmptyString
6142
- });
6143
6135
  var evidence = z3.strictObject({
6144
6136
  summary: nonEmptyString,
6145
6137
  source: nonEmptyString
6146
6138
  });
6147
- var successCriterion = statement.extend({
6148
- id: nonEmptyString,
6149
- evidence: z3.array(evidence)
6150
- });
6151
- var task = z3.strictObject({
6152
- id: nonEmptyString,
6153
- title: nonEmptyString,
6154
- status: z3.enum(["pending", "in_progress", "completed"]),
6155
- blockedBy: z3.array(nonEmptyString),
6156
- evidence: z3.array(evidence)
6157
- });
6158
6139
  var lastReview = z3.strictObject({
6159
6140
  revision: z3.number().int().positive(),
6160
6141
  decision: z3.enum(["continue", "revise", "replace", "complete", "blocked"]),
6161
6142
  summary: nonEmptyString
6162
6143
  });
6163
- var planState = z3.strictObject({
6164
- revision: z3.number().int().positive(),
6165
- objective: statement,
6166
- successCriteria: z3.array(successCriterion).min(1),
6167
- constraints: z3.array(statement),
6168
- assumptions: z3.array(statement),
6169
- tasks: z3.array(task),
6170
- lastReview: lastReview.optional()
6171
- });
6144
+ function parsePlanSchema(schema, value) {
6145
+ const result = schema.safeParse(value);
6146
+ if (result.success) return result.data;
6147
+ const issues = result.error.issues.slice(0, 5).map(({ path: path6, message: message2 }) => `${path6.join(".") || "plan"}: ${message2}`);
6148
+ const remaining = result.error.issues.length - issues.length;
6149
+ throw new Error(
6150
+ `schema errors: ${issues.join("; ")}${remaining > 0 ? `; ${remaining} more` : ""}`
6151
+ );
6152
+ }
6172
6153
  function uniqueIds(items, label) {
6173
6154
  const byId = /* @__PURE__ */ new Map();
6174
6155
  for (const item of items) {
@@ -6179,23 +6160,22 @@ function uniqueIds(items, label) {
6179
6160
  }
6180
6161
  return byId;
6181
6162
  }
6182
- function validatePlan(state) {
6183
- uniqueIds(state.successCriteria, "success criterion");
6184
- const tasks = uniqueIds(state.tasks, "task");
6185
- const blocks = new Map(state.tasks.map(({ id }) => [id, []]));
6186
- for (const item of state.tasks) {
6163
+ function validateDependencies(items, label) {
6164
+ const byId = uniqueIds(items, label);
6165
+ const blocks = new Map(items.map(({ id }) => [id, []]));
6166
+ for (const item of items) {
6187
6167
  const blockers = /* @__PURE__ */ new Set();
6188
6168
  for (const blocker of item.blockedBy) {
6189
6169
  if (blocker === item.id) {
6190
- throw new Error(`task "${item.id}" cannot block itself`);
6170
+ throw new Error(`${label} "${item.id}" cannot block itself`);
6191
6171
  }
6192
- if (!tasks.has(blocker)) {
6172
+ if (!byId.has(blocker)) {
6193
6173
  throw new Error(
6194
- `task "${item.id}" references missing blocker "${blocker}"`
6174
+ `${label} "${item.id}" references missing blocker "${blocker}"`
6195
6175
  );
6196
6176
  }
6197
6177
  if (blockers.has(blocker)) {
6198
- throw new Error(`task "${item.id}" repeats blocker "${blocker}"`);
6178
+ throw new Error(`${label} "${item.id}" repeats blocker "${blocker}"`);
6199
6179
  }
6200
6180
  blockers.add(blocker);
6201
6181
  blocks.get(blocker)?.push(item.id);
@@ -6214,12 +6194,112 @@ function validatePlan(state) {
6214
6194
  }
6215
6195
  visiting.add(id);
6216
6196
  stack.push(id);
6217
- for (const blocker of tasks.get(id)?.blockedBy ?? []) visit(blocker);
6197
+ for (const blocker of byId.get(id)?.blockedBy ?? []) visit(blocker);
6218
6198
  stack.pop();
6219
6199
  visiting.delete(id);
6220
6200
  visited.add(id);
6221
6201
  };
6222
- for (const { id } of state.tasks) visit(id);
6202
+ for (const { id } of items) visit(id);
6203
+ return { byId, blocks };
6204
+ }
6205
+ function lineItems(items) {
6206
+ return items.length > 0 ? items.map((item) => `- ${item}`) : ["- none"];
6207
+ }
6208
+ function latestEvidence(items) {
6209
+ const latest = items.at(-1);
6210
+ return latest ? ` Evidence: ${latest.summary} (${latest.source})` : "";
6211
+ }
6212
+ function errorMessage(error) {
6213
+ const message2 = error instanceof Error ? error.message : String(error);
6214
+ return message2.length > 500 ? `${message2.slice(0, 497)}...` : message2;
6215
+ }
6216
+ function parsePlanJson(raw) {
6217
+ try {
6218
+ return JSON.parse(raw);
6219
+ } catch (error) {
6220
+ const message2 = error instanceof Error ? error.message : String(error);
6221
+ throw new Error(`invalid JSON: ${message2}`, { cause: error });
6222
+ }
6223
+ }
6224
+ function createPlanReview(options, formatPlanReview, reviewReminder) {
6225
+ return reminder(
6226
+ async ({ sandbox }) => {
6227
+ let raw;
6228
+ try {
6229
+ raw = await sandbox.sandbox.readFile(PLAN_FILE_PATH);
6230
+ } catch (error) {
6231
+ return [
6232
+ `The plan file at ${PLAN_FILE_PATH} could not be read: ${errorMessage(error)}`,
6233
+ "Create or repair it before taking another action.",
6234
+ "",
6235
+ reviewReminder
6236
+ ].join("\n");
6237
+ }
6238
+ try {
6239
+ return formatPlanReview(parsePlanJson(raw));
6240
+ } catch (error) {
6241
+ return [
6242
+ `The plan file at ${PLAN_FILE_PATH} is invalid: ${errorMessage(error)}`,
6243
+ "Repair the plan before taking another action.",
6244
+ "",
6245
+ reviewReminder
6246
+ ].join("\n");
6247
+ }
6248
+ },
6249
+ { when: options.when, target: "steer" }
6250
+ );
6251
+ }
6252
+
6253
+ // packages/context/src/lib/fragments/plan.ts
6254
+ var EXECUTION_PLAN_INSTRUCTIONS = dedent2`
6255
+ Use an execution plan with this shape:
6256
+ - revision: a positive integer, incremented whenever the objective, criteria, constraints, assumptions, tasks, or dependencies change; recording lastReview alone does not increment it;
6257
+ - objective: { text, basis, source };
6258
+ - successCriteria: [{ id, text, basis, source, evidence: [{ summary, source }] }];
6259
+ - constraints and assumptions: [{ text, basis, source }];
6260
+ - tasks: [{ id, title, status, blockedBy, evidence }], where status is pending, in_progress, or completed;
6261
+ - lastReview, when present: { revision, decision, summary }.
6262
+
6263
+ Use basis values explicit_requirement, discovered_constraint, inference, or assumption. Store only blockedBy dependency edges. Never store blocks, readiness, or a blocked task status; those are derived when the plan is read. A task may be in_progress only when every blocker is completed, and a completed task must include concrete evidence.
6264
+
6265
+ Revise the plan when new evidence invalidates its objective, criteria, constraints, assumptions, sequencing, or dependencies. Before recording a complete decision or claiming completion, map every success criterion to concrete evidence. Completed task statuses alone do not prove completion.
6266
+ `;
6267
+ var EXECUTION_REVIEW_REMINDER = [
6268
+ "Re-read the current plan and consider the evidence gathered since the previous review.",
6269
+ "",
6270
+ "Is the current plan still valid given the latest evidence?",
6271
+ "",
6272
+ "If yes, continue. If not, revise it before taking another action.",
6273
+ "Before claiming completion, verify the success criteria against evidence."
6274
+ ].join("\n");
6275
+ var statement = z4.strictObject({
6276
+ text: nonEmptyString,
6277
+ basis: statementBasis,
6278
+ source: nonEmptyString
6279
+ });
6280
+ var successCriterion = statement.extend({
6281
+ id: nonEmptyString,
6282
+ evidence: z4.array(evidence)
6283
+ });
6284
+ var task = z4.strictObject({
6285
+ id: nonEmptyString,
6286
+ title: nonEmptyString,
6287
+ status: z4.enum(["pending", "in_progress", "completed"]),
6288
+ blockedBy: z4.array(nonEmptyString),
6289
+ evidence: z4.array(evidence)
6290
+ });
6291
+ var executionPlanState = z4.strictObject({
6292
+ revision: z4.number().int().positive(),
6293
+ objective: statement,
6294
+ successCriteria: z4.array(successCriterion).min(1),
6295
+ constraints: z4.array(statement),
6296
+ assumptions: z4.array(statement),
6297
+ tasks: z4.array(task),
6298
+ lastReview: lastReview.optional()
6299
+ });
6300
+ function validateExecutionPlan(state) {
6301
+ uniqueIds(state.successCriteria, "success criterion");
6302
+ const { byId: tasks, blocks } = validateDependencies(state.tasks, "task");
6223
6303
  for (const item of state.tasks) {
6224
6304
  const incompleteBlockers = item.blockedBy.filter(
6225
6305
  (id) => tasks.get(id)?.status !== "completed"
@@ -6245,37 +6325,19 @@ function validatePlan(state) {
6245
6325
  }
6246
6326
  return blocks;
6247
6327
  }
6248
- function parsePlan(raw) {
6249
- let parsed;
6250
- try {
6251
- parsed = JSON.parse(raw);
6252
- } catch (error) {
6253
- const message2 = error instanceof Error ? error.message : String(error);
6254
- throw new Error(`invalid JSON: ${message2}`, { cause: error });
6255
- }
6256
- const result = planState.safeParse(parsed);
6257
- if (!result.success) {
6258
- const issues = result.error.issues.slice(0, 5).map(({ path: path3, message: message2 }) => `${path3.join(".") || "plan"}: ${message2}`);
6259
- const remaining = result.error.issues.length - issues.length;
6260
- throw new Error(
6261
- `schema errors: ${issues.join("; ")}${remaining > 0 ? `; ${remaining} more` : ""}`
6262
- );
6263
- }
6264
- return { state: result.data, blocks: validatePlan(result.data) };
6265
- }
6266
- function lineItems(items) {
6267
- return items.length > 0 ? items.map((item) => `- ${item}`) : ["- none"];
6268
- }
6269
- function latestEvidence(items) {
6270
- const latest = items.at(-1);
6271
- return latest ? ` Evidence: ${latest.summary} (${latest.source})` : "";
6328
+ function parseExecutionPlan(value) {
6329
+ const state = parsePlanSchema(executionPlanState, value);
6330
+ return { state, blocks: validateExecutionPlan(state) };
6272
6331
  }
6273
6332
  function formatTask(item, blocks) {
6274
6333
  const blocked = blocks.get(item.id) ?? [];
6275
6334
  const suffix = blocked.length > 0 ? `; blocks ${blocked.join(", ")}` : "";
6276
6335
  return `${item.id}: ${item.title}${suffix}${latestEvidence(item.evidence)}`;
6277
6336
  }
6278
- function formatPlanReview(state, blocks) {
6337
+ function formatExecutionPlanReview({
6338
+ state,
6339
+ blocks
6340
+ }) {
6279
6341
  const tasks = new Map(state.tasks.map((item) => [item.id, item]));
6280
6342
  const active = state.tasks.filter(({ status }) => status === "in_progress");
6281
6343
  const ready = state.tasks.filter(
@@ -6330,45 +6392,23 @@ function formatPlanReview(state, blocks) {
6330
6392
  `Last review: ${state.lastReview.decision} at revision ${state.lastReview.revision} \u2014 ${state.lastReview.summary}`
6331
6393
  ] : [],
6332
6394
  "",
6333
- REVIEW_REMINDER
6395
+ EXECUTION_REVIEW_REMINDER
6334
6396
  ].join("\n");
6335
6397
  }
6336
- function errorMessage(error) {
6337
- const message2 = error instanceof Error ? error.message : String(error);
6338
- return message2.length > 500 ? `${message2.slice(0, 497)}...` : message2;
6339
- }
6340
- async function resolvePlanReview(sandbox) {
6341
- let raw;
6342
- try {
6343
- raw = await sandbox.sandbox.readFile(PLAN_FILE_PATH);
6344
- } catch (error) {
6345
- return [
6346
- `The plan file at ${PLAN_FILE_PATH} could not be read: ${errorMessage(error)}`,
6347
- "Create or repair it before taking another action.",
6348
- "",
6349
- REVIEW_REMINDER
6350
- ].join("\n");
6351
- }
6352
- try {
6353
- const { state, blocks } = parsePlan(raw);
6354
- return formatPlanReview(state, blocks);
6355
- } catch (error) {
6356
- return [
6357
- `The plan file at ${PLAN_FILE_PATH} is invalid: ${errorMessage(error)}`,
6358
- "Repair the plan before taking another action.",
6359
- "",
6360
- REVIEW_REMINDER
6361
- ].join("\n");
6362
- }
6363
- }
6364
6398
  function instructions() {
6365
- return fragment("plan_instructions", PLAN_INSTRUCTIONS);
6399
+ return fragment(
6400
+ "plan_instructions",
6401
+ PLAN_FOUNDATION_INSTRUCTIONS,
6402
+ EXECUTION_PLAN_INSTRUCTIONS,
6403
+ PLAN_REVIEW_INSTRUCTIONS
6404
+ );
6366
6405
  }
6367
6406
  function review(options) {
6368
- return reminder(({ sandbox }) => resolvePlanReview(sandbox), {
6369
- when: options.when,
6370
- target: "steer"
6371
- });
6407
+ return createPlanReview(
6408
+ options,
6409
+ (value) => formatExecutionPlanReview(parseExecutionPlan(value)),
6410
+ EXECUTION_REVIEW_REMINDER
6411
+ );
6372
6412
  }
6373
6413
  var plan = { path: PLAN_FILE_PATH, instructions, review };
6374
6414
 
@@ -6496,95 +6536,332 @@ function selfCritique(checks = DEFAULT_SELF_CRITIQUE_CHECKS) {
6496
6536
  });
6497
6537
  }
6498
6538
 
6539
+ // packages/context/src/lib/fragments/socratic-inquiry.ts
6540
+ var SOCRATIC_ROLE = "You are a deep, methodical thinker. For substantive work, build understanding through explicit questions and evidence before executing. Scale the inquiry to the task instead of manufacturing ceremony.";
6541
+ function socraticInquiry() {
6542
+ return fragment(
6543
+ "socratic_inquiry",
6544
+ hint(
6545
+ "Use Socratic inquiry when uncertainty, tradeoffs, or quality judgment matter. Skip Socratic inquiry for mechanical tasks with one obvious path, or when the user explicitly says to proceed directly, unless another active contract explicitly requires it."
6546
+ ),
6547
+ principle({
6548
+ title: "Interrogate the domain",
6549
+ description: "Surface the principles that separate excellent work from merely plausible output before choosing an approach.",
6550
+ policies: [
6551
+ "Ask what the user or audience actually needs from the result.",
6552
+ "Ask which frameworks, mental models, and quality criteria govern this domain.",
6553
+ "Ask which common failure modes would produce convincing but wrong results.",
6554
+ "Explore the logical, practical, user-specific, and risk dimensions that materially affect the outcome."
6555
+ ]
6556
+ }),
6557
+ principle({
6558
+ title: "Apply to the specific case",
6559
+ description: "Translate the domain principles into questions about the actual request, source, runtime, and constraints.",
6560
+ policies: [
6561
+ "Ask which evidence in this case changes which general principles matter most.",
6562
+ "Ask which assumptions remain unresolved and what observation could answer them.",
6563
+ "Ask what an expert would inspect, test, or rule out before committing to an answer."
6564
+ ]
6565
+ }),
6566
+ principle({
6567
+ title: "Bridge and execute",
6568
+ description: "Synthesize the answers into the concrete action before producing the result.",
6569
+ policies: [
6570
+ 'State "Applying to this task:" and connect the inquiry answers to the chosen action.',
6571
+ "Execute from that synthesis rather than reverting to a generic answer or checklist.",
6572
+ "Verify the result against the criteria discovered during inquiry."
6573
+ ]
6574
+ }),
6575
+ principle({
6576
+ title: "Adaptive and evolving inquiry",
6577
+ description: "Match inquiry depth to task complexity and let evidence determine follow-up questions.",
6578
+ policies: [
6579
+ "Use about 2 focused questions for light work, 3-4 for medium work, and 5-7 for heavy work.",
6580
+ "Do not precompute every follow-up question; answers should create, revise, or eliminate later questions.",
6581
+ "Retain questions and answers that explain consequential decisions."
6582
+ ]
6583
+ }),
6584
+ workflow({
6585
+ task: "Socratic reasoning process",
6586
+ steps: [
6587
+ "Interrogate the domain and answer the foundational questions.",
6588
+ "Apply those answers to the specific request and available evidence.",
6589
+ "Add or revise follow-up questions based on what the answers changed.",
6590
+ 'Bridge the answers into the concrete action with "Applying to this task:".',
6591
+ "Execute and verify the result against the discovered criteria."
6592
+ ]
6593
+ })
6594
+ );
6595
+ }
6596
+
6499
6597
  // packages/context/src/lib/fragments/socratic.ts
6500
6598
  function socraticPrompting() {
6501
- return [
6502
- role(
6503
- "You are a deep, methodical thinker. Before producing any output, you reason through the problem by asking and answering foundational questions. You never treat a request as a simple task to complete \u2014 you first build understanding through inquiry, then apply that understanding to produce high-quality output."
6504
- ),
6505
- fragment(
6506
- "socratic_prompting",
6507
- hint(
6508
- "When given a task, do not jump straight to producing output. Instead, decompose the task into foundational questions that, once answered, will make the output significantly better. Answer those questions first, then synthesize your answers into the final output."
6509
- ),
6510
- principle({
6511
- title: "Question-first decomposition",
6512
- description: 'Every task hides assumptions about what "good" looks like. Surface those assumptions by asking what makes the output effective before attempting to produce it.',
6513
- policies: [
6514
- 'Ask "What makes X effective/compelling/useful?" before producing X.',
6515
- "Identify the criteria, frameworks, or principles that govern quality for this type of output.",
6516
- "Do not skip this step even when the task seems straightforward \u2014 obvious tasks often have non-obvious quality dimensions."
6517
- ]
6518
- }),
6519
- principle({
6520
- title: "Multi-dimensional inquiry",
6521
- description: "Explore the problem from multiple angles \u2014 emotional, logical, practical, audience-specific \u2014 through targeted questions.",
6522
- policies: [
6523
- "Ask about the audience: Who is this for? What do they care about?",
6524
- "Ask about constraints: What must this include or avoid?",
6525
- "Ask about effectiveness: What separates great output from mediocre output in this domain?",
6526
- "Ask about structure: How should this be organized for maximum impact?"
6527
- ]
6528
- }),
6529
- principle({
6530
- title: "Framework discovery before application",
6531
- description: "Build or recall a relevant framework by reasoning through questions, then explicitly apply that framework to the specific task.",
6532
- policies: [
6533
- "First derive the framework: What principles govern this type of work?",
6534
- 'Then apply it: "Now, using these principles, produce the specific output."',
6535
- "The framework should emerge from your reasoning, not from a template."
6536
- ]
6537
- }),
6538
- workflow({
6539
- task: "Socratic reasoning process",
6540
- steps: [
6541
- "Identify the core task and desired output type.",
6542
- "Formulate 2-5 foundational questions about what makes this output effective.",
6543
- "Answer each question, drawing on relevant knowledge and frameworks.",
6544
- "Synthesize answers into a coherent set of principles or criteria.",
6545
- "Apply the discovered framework to produce the specific output.",
6546
- "Verify the output satisfies the criteria you identified."
6547
- ]
6548
- }),
6549
- example({
6550
- question: "How should I prompt for a value proposition for my AI analytics tool?",
6551
- answer: "What makes a value proposition compelling to B2B buyers? What emotional and logical triggers should it hit? Now apply that framework to an AI analytics tool that helps teams make faster data-driven decisions."
6552
- }),
6553
- example({
6554
- question: "How should I prompt for a 30-day LinkedIn content calendar for B2B SaaS?",
6555
- answer: "What types of LinkedIn content generate the most engagement in B2B SaaS? What posting frequency avoids audience fatigue? How should topics build on each other? Now design a 30-day calendar using these principles."
6556
- })
6557
- )
6558
- ];
6599
+ return [role(SOCRATIC_ROLE), socraticInquiry()];
6559
6600
  }
6560
6601
 
6561
- // packages/context/src/lib/guardrails/error-recovery.guardrail.ts
6562
- import chalk3 from "chalk";
6563
- var errorRecoveryGuardrail = {
6564
- id: "error-recovery",
6565
- name: "API Error Recovery",
6566
- handle: (part, context) => {
6567
- if (part.type !== "error") {
6568
- return pass(part);
6569
- }
6570
- const errorText = part.errorText || "";
6571
- const prefix = chalk3.bold.magenta("[ErrorRecovery]");
6572
- console.log(
6573
- `${prefix} ${chalk3.red("Caught error:")} ${chalk3.dim(errorText.slice(0, 150))}`
6602
+ // packages/context/src/lib/fragments/socratic-plan.ts
6603
+ import dedent3 from "dedent";
6604
+ import z5 from "zod";
6605
+ var SOCRATIC_PLAN_INSTRUCTIONS = dedent3`
6606
+ Use a Socratic plan as an evolving question-and-answer inquiry, not an execution task list whose titles merely end in question marks. It has this shape:
6607
+ The only top-level keys are revision, objective, successCriteria, constraints, assumptions, inquiries, and lastReview.
6608
+ - revision: a positive integer, incremented whenever a question, answer, dependency, or review-driven conclusion changes; recording lastReview alone does not increment it;
6609
+ - objective: one question entry;
6610
+ - successCriteria: question entries with an id;
6611
+ - constraints: question entries;
6612
+ - assumptions: question entries;
6613
+ - inquiries: question entries with id, phase, status, and blockedBy;
6614
+ - lastReview: null before the first review, then { revision, decision, summary, nextQuestionId }.
6615
+
6616
+ A question entry is { question, basis, source, answer }. Its answer is null until answered, then { text, basis, source, evidence: [{ summary, source }] }. Use basis values explicit_requirement, discovered_constraint, inference, or assumption for every basis field. Every question must end with a question mark and include an answer field. Never store an answer as a bare string. Use phase values domain, case, or bridge. Use inquiry status values open, answering, or answered. Store only blockedBy dependencies; readiness and reverse edges are derived. An inquiry may be answering only after every blocker is answered. An answered inquiry must have an evidence-backed answer. An open inquiry must have answer: null. nextQuestionId records the inquiry selected by that review; use null when the review selects none.
6617
+
6618
+ After reading any required skill or workspace instructions, write the initial inquiry before substantive investigation. Begin with the governing objective question and verification questions. Create an initial inquiry proportional to complexity: about 2 questions for light work, 3-4 for medium work, and 5-7 for heavy work. Do not precompute every follow-up question. After each answer, ask what it changed, then add, revise, or remove later questions. Preserve consequential questions and answers as the decision log.
6619
+
6620
+ Domain questions discover governing principles, relevant frameworks, and common failure modes. Case questions apply them to the actual source, runtime, constraints, and evidence. A bridge answer must begin with "Applying to this task:" and state the concrete action implied by the earlier answers. Execute from that bridge, then answer the success questions with verification evidence.
6621
+
6622
+ Before recording a complete decision, every objective, success, constraint, assumption, and inquiry question must have an evidence-backed answer. Completion means the answers collectively establish the requested outcome; the presence of answered questions alone does not prove completion. Do not return a final answer until lastReview records a complete decision at the current revision with nextQuestionId: null.
6623
+ `;
6624
+ var SOCRATIC_REVIEW_REMINDER = [
6625
+ "Re-read the current Socratic plan and the evidence gathered since the previous review.",
6626
+ "",
6627
+ "What did the latest evidence answer?",
6628
+ "Does that answer invalidate another answer, assumption, dependency, or question?",
6629
+ "Which unanswered question would reduce the most consequential uncertainty next?",
6630
+ "",
6631
+ "Record the review and its nextQuestionId, then revise the inquiry before continuing when needed.",
6632
+ "Before claiming completion, verify that every required question has an evidence-backed answer and that the answers establish the requested outcome."
6633
+ ].join("\n");
6634
+ var questionText = nonEmptyString.refine((text) => text.endsWith("?"), {
6635
+ message: "must end with a question mark"
6636
+ });
6637
+ var answer = z5.strictObject({
6638
+ text: nonEmptyString,
6639
+ basis: statementBasis,
6640
+ source: nonEmptyString,
6641
+ evidence: z5.array(evidence).min(1)
6642
+ });
6643
+ var question = z5.strictObject({
6644
+ question: questionText,
6645
+ basis: statementBasis,
6646
+ source: nonEmptyString,
6647
+ answer: answer.nullable()
6648
+ });
6649
+ var successQuestion = question.extend({ id: nonEmptyString });
6650
+ var inquiry = question.extend({
6651
+ id: nonEmptyString,
6652
+ phase: z5.enum(["domain", "case", "bridge"]),
6653
+ status: z5.enum(["open", "answering", "answered"]),
6654
+ blockedBy: z5.array(nonEmptyString)
6655
+ });
6656
+ var socraticLastReview = lastReview.extend({
6657
+ nextQuestionId: nonEmptyString.nullable()
6658
+ });
6659
+ var socraticPlanState = z5.strictObject({
6660
+ revision: z5.number().int().positive(),
6661
+ objective: question,
6662
+ successCriteria: z5.array(successQuestion).min(1),
6663
+ constraints: z5.array(question),
6664
+ assumptions: z5.array(question),
6665
+ inquiries: z5.array(inquiry).min(1),
6666
+ lastReview: socraticLastReview.nullable().optional()
6667
+ });
6668
+ function validateSocraticPlan(state) {
6669
+ uniqueIds(state.successCriteria, "success question");
6670
+ const { byId: inquiries, blocks } = validateDependencies(
6671
+ state.inquiries,
6672
+ "inquiry"
6673
+ );
6674
+ for (const item of state.inquiries) {
6675
+ const incompleteBlockers = item.blockedBy.filter(
6676
+ (id) => inquiries.get(id)?.status !== "answered"
6574
6677
  );
6575
- const logAndFail = (pattern, feedback) => {
6576
- console.log(
6577
- `${prefix} ${chalk3.yellow("Pattern:")} ${chalk3.cyan(pattern)}`
6678
+ if (item.status !== "open" && incompleteBlockers.length > 0) {
6679
+ throw new Error(
6680
+ `inquiry "${item.id}" is ${item.status} while blocked by ${incompleteBlockers.join(", ")}`
6578
6681
  );
6579
- console.log(
6580
- `${prefix} ${chalk3.green("Feedback:")} ${chalk3.dim(feedback.slice(0, 80))}...`
6682
+ }
6683
+ if (item.status === "open" && item.answer) {
6684
+ throw new Error(`open inquiry "${item.id}" already has an answer`);
6685
+ }
6686
+ if (item.status === "answered" && !item.answer) {
6687
+ throw new Error(`inquiry "${item.id}" is answered without an answer`);
6688
+ }
6689
+ if (item.phase === "bridge" && item.answer && !item.answer.text.startsWith("Applying to this task:")) {
6690
+ throw new Error(
6691
+ `bridge inquiry "${item.id}" answer must start with "Applying to this task:"`
6581
6692
  );
6582
- return fail(feedback);
6583
- };
6584
- if (errorText.includes("Tool choice is none")) {
6585
- if (context.availableTools.length > 0) {
6586
- return logAndFail(
6587
- "Tool choice is none",
6693
+ }
6694
+ }
6695
+ if (state.lastReview && state.lastReview.revision > state.revision) {
6696
+ throw new Error(
6697
+ `last review references future revision ${state.lastReview.revision}`
6698
+ );
6699
+ }
6700
+ const nextQuestionId = state.lastReview?.nextQuestionId;
6701
+ if (nextQuestionId) {
6702
+ const next = inquiries.get(nextQuestionId);
6703
+ if (!next) {
6704
+ throw new Error(
6705
+ `last review references missing next question "${nextQuestionId}"`
6706
+ );
6707
+ }
6708
+ if (state.lastReview?.revision === state.revision) {
6709
+ if (next.status === "answered") {
6710
+ throw new Error(
6711
+ `last review next question "${nextQuestionId}" is already answered`
6712
+ );
6713
+ }
6714
+ const incompleteBlockers = next.blockedBy.filter(
6715
+ (id) => inquiries.get(id)?.status !== "answered"
6716
+ );
6717
+ if (incompleteBlockers.length > 0) {
6718
+ throw new Error(
6719
+ `last review next question "${nextQuestionId}" is blocked by ${incompleteBlockers.join(", ")}`
6720
+ );
6721
+ }
6722
+ }
6723
+ }
6724
+ if (state.lastReview?.decision === "complete") {
6725
+ if (state.lastReview.revision !== state.revision) {
6726
+ throw new Error(
6727
+ "a complete Socratic review must reference the current revision"
6728
+ );
6729
+ }
6730
+ const unansweredStatements = [
6731
+ state.objective,
6732
+ ...state.successCriteria,
6733
+ ...state.constraints,
6734
+ ...state.assumptions
6735
+ ].some(({ answer: answer2 }) => answer2 === null);
6736
+ if (unansweredStatements || state.inquiries.some(({ status }) => status !== "answered")) {
6737
+ throw new Error(
6738
+ "a complete Socratic review requires an answer for every required question"
6739
+ );
6740
+ }
6741
+ if (state.lastReview.nextQuestionId !== null) {
6742
+ throw new Error("a complete Socratic review cannot name a next question");
6743
+ }
6744
+ }
6745
+ return blocks;
6746
+ }
6747
+ function parseSocraticPlan(value) {
6748
+ const state = parsePlanSchema(socraticPlanState, value);
6749
+ return { state, blocks: validateSocraticPlan(state) };
6750
+ }
6751
+ function formatAnswer(answer2) {
6752
+ return answer2 ? `Answer: ${answer2.text} [${answer2.basis}; ${answer2.source}]${latestEvidence(answer2.evidence)}` : "Answer: unanswered";
6753
+ }
6754
+ function formatQuestion(item) {
6755
+ return `${item.question} [${item.basis}; ${item.source}]
6756
+ ${formatAnswer(item.answer)}`;
6757
+ }
6758
+ function formatInquiry(item, blocks) {
6759
+ const blocked = blocks.get(item.id) ?? [];
6760
+ const suffix = blocked.length > 0 ? `; blocks ${blocked.join(", ")}` : "";
6761
+ return `${item.id} [${item.phase}]: ${item.question} [${item.basis}; ${item.source}]${suffix}
6762
+ ${formatAnswer(item.answer)}`;
6763
+ }
6764
+ function formatSocraticPlanReview({
6765
+ state,
6766
+ blocks
6767
+ }) {
6768
+ const inquiries = new Map(state.inquiries.map((item) => [item.id, item]));
6769
+ const active = state.inquiries.filter(({ status }) => status === "answering");
6770
+ const ready = state.inquiries.filter(
6771
+ ({ status, blockedBy }) => status === "open" && blockedBy.every((id) => inquiries.get(id)?.status === "answered")
6772
+ );
6773
+ const waiting = state.inquiries.filter(
6774
+ ({ status, blockedBy }) => status === "open" && blockedBy.some((id) => inquiries.get(id)?.status !== "answered")
6775
+ );
6776
+ const answered = state.inquiries.filter(
6777
+ ({ status }) => status === "answered"
6778
+ );
6779
+ return [
6780
+ `Current Socratic plan (revision ${state.revision})`,
6781
+ `Governing question: ${formatQuestion(state.objective)}`,
6782
+ "",
6783
+ "Success questions:",
6784
+ ...lineItems(
6785
+ state.successCriteria.map(
6786
+ (item) => `${item.id}: ${formatQuestion(item)}`
6787
+ )
6788
+ ),
6789
+ "",
6790
+ "Constraint questions:",
6791
+ ...lineItems(state.constraints.map(formatQuestion)),
6792
+ "",
6793
+ "Assumption questions:",
6794
+ ...lineItems(state.assumptions.map(formatQuestion)),
6795
+ "",
6796
+ "Active inquiries:",
6797
+ ...lineItems(active.map((item) => formatInquiry(item, blocks))),
6798
+ "",
6799
+ "Ready inquiries:",
6800
+ ...lineItems(ready.map((item) => formatInquiry(item, blocks))),
6801
+ "",
6802
+ "Waiting inquiries:",
6803
+ ...lineItems(
6804
+ waiting.map(
6805
+ (item) => `${formatInquiry(item, blocks)}; waiting for ${item.blockedBy.filter((id) => inquiries.get(id)?.status !== "answered").join(", ")}`
6806
+ )
6807
+ ),
6808
+ "",
6809
+ "Answered inquiries:",
6810
+ ...lineItems(answered.map((item) => formatInquiry(item, blocks))),
6811
+ ...state.lastReview ? [
6812
+ "",
6813
+ `Last review: ${state.lastReview.decision} at revision ${state.lastReview.revision} \u2014 ${state.lastReview.summary}`,
6814
+ `Next question: ${state.lastReview.nextQuestionId ?? "none"}`
6815
+ ] : [],
6816
+ "",
6817
+ SOCRATIC_REVIEW_REMINDER
6818
+ ].join("\n");
6819
+ }
6820
+ function instructions2() {
6821
+ return fragment(
6822
+ "plan_instructions",
6823
+ PLAN_FOUNDATION_INSTRUCTIONS,
6824
+ socraticInquiry(),
6825
+ SOCRATIC_PLAN_INSTRUCTIONS,
6826
+ PLAN_REVIEW_INSTRUCTIONS
6827
+ );
6828
+ }
6829
+ function review2(options) {
6830
+ return createPlanReview(
6831
+ options,
6832
+ (value) => formatSocraticPlanReview(parseSocraticPlan(value)),
6833
+ SOCRATIC_REVIEW_REMINDER
6834
+ );
6835
+ }
6836
+ var socraticPlan = { path: PLAN_FILE_PATH, instructions: instructions2, review: review2 };
6837
+
6838
+ // packages/context/src/lib/guardrails/error-recovery.guardrail.ts
6839
+ import chalk3 from "chalk";
6840
+ var errorRecoveryGuardrail = {
6841
+ id: "error-recovery",
6842
+ name: "API Error Recovery",
6843
+ handle: (part, context) => {
6844
+ if (part.type !== "error") {
6845
+ return pass(part);
6846
+ }
6847
+ const errorText = part.errorText || "";
6848
+ const prefix = chalk3.bold.magenta("[ErrorRecovery]");
6849
+ console.log(
6850
+ `${prefix} ${chalk3.red("Caught error:")} ${chalk3.dim(errorText.slice(0, 150))}`
6851
+ );
6852
+ const logAndFail = (pattern, feedback) => {
6853
+ console.log(
6854
+ `${prefix} ${chalk3.yellow("Pattern:")} ${chalk3.cyan(pattern)}`
6855
+ );
6856
+ console.log(
6857
+ `${prefix} ${chalk3.green("Feedback:")} ${chalk3.dim(feedback.slice(0, 80))}...`
6858
+ );
6859
+ return fail(feedback);
6860
+ };
6861
+ if (errorText.includes("Tool choice is none")) {
6862
+ if (context.availableTools.length > 0) {
6863
+ return logAndFail(
6864
+ "Tool choice is none",
6588
6865
  `I tried to call a tool that doesn't exist. Available tools: ${context.availableTools.join(", ")}. Let me use one of these instead.`
6589
6866
  );
6590
6867
  }
@@ -6668,7 +6945,15 @@ function render(tag, ...fragments) {
6668
6945
 
6669
6946
  // packages/context/src/lib/sandbox/agent-os-sandbox.ts
6670
6947
  import { PassThrough, Readable } from "node:stream";
6948
+
6949
+ // packages/context/src/lib/sandbox/read-file.ts
6671
6950
  var decoder = new TextDecoder();
6951
+ function readFileContent(bytes, options) {
6952
+ const content = options?.encoding === "binary" ? bytes : decoder.decode(bytes);
6953
+ return content;
6954
+ }
6955
+
6956
+ // packages/context/src/lib/sandbox/agent-os-sandbox.ts
6672
6957
  var AgentOsSandboxError = class extends Error {
6673
6958
  constructor(message2) {
6674
6959
  super(message2);
@@ -6825,16 +7110,19 @@ async function createAgentOsSandbox(options = {}) {
6825
7110
  exit
6826
7111
  };
6827
7112
  },
6828
- async readFile(path3) {
7113
+ async readFile(path6, options2) {
6829
7114
  try {
6830
- const bytes = await os.readFile(path3);
6831
- return decoder.decode(bytes);
7115
+ const bytes = await os.readFile(path6);
7116
+ return readFileContent(bytes, options2);
6832
7117
  } catch (error) {
6833
7118
  throw new Error(
6834
- `Failed to read file "${path3}": ${error instanceof Error ? error.message : String(error)}`
7119
+ `Failed to read file "${path6}": ${error instanceof Error ? error.message : String(error)}`
6835
7120
  );
6836
7121
  }
6837
7122
  },
7123
+ async exists(path6) {
7124
+ return os.exists(path6);
7125
+ },
6838
7126
  async writeFiles(files) {
6839
7127
  const results = await os.writeFiles(
6840
7128
  files.map((f) => ({
@@ -7021,9 +7309,9 @@ function toSandboxProcess(child, abortSignal) {
7021
7309
  };
7022
7310
  }
7023
7311
  var BASE64_WRITE_CHUNK = 32768;
7024
- function base64WriteCommands(path3, content) {
7312
+ function base64WriteCommands(path6, content) {
7025
7313
  const base64 = Buffer.from(content).toString("base64");
7026
- const quotedPath = shellQuote(path3);
7314
+ const quotedPath = shellQuote(path6);
7027
7315
  const commands = [];
7028
7316
  for (let offset = 0; offset < base64.length; offset += BASE64_WRITE_CHUNK) {
7029
7317
  const chunk = base64.slice(offset, offset + BASE64_WRITE_CHUNK);
@@ -7037,8 +7325,11 @@ function base64WriteCommands(path3, content) {
7037
7325
  }
7038
7326
  return commands;
7039
7327
  }
7040
- function base64ReadCommand(path3) {
7041
- return `base64 ${shellQuote(path3)}`;
7328
+ function base64ReadCommand(path6) {
7329
+ return `base64 ${shellQuote(path6)}`;
7330
+ }
7331
+ function existsCommand(path6) {
7332
+ return `test -e ${shellQuote(path6)} && printf true || printf false`;
7042
7333
  }
7043
7334
 
7044
7335
  // packages/context/src/lib/sandbox/installers/installer.ts
@@ -7544,7 +7835,7 @@ var ContainerSandboxStrategy = class {
7544
7835
  const volumes = [...this.createdVolumes].reverse();
7545
7836
  for (const volume of volumes) {
7546
7837
  try {
7547
- await spawn2(this.engine.cli, ["volume", "rm", volume]);
7838
+ await this.engine.removeVolume(volume);
7548
7839
  this.createdVolumes.delete(volume);
7549
7840
  } catch (error) {
7550
7841
  const reason = this.engineErrorMessage(error);
@@ -7631,12 +7922,18 @@ var ContainerSandboxStrategy = class {
7631
7922
  const sandbox = {
7632
7923
  executeCommand: async (command, options) => this.exec(command, options),
7633
7924
  spawn: (command, options) => this.spawnProcess(command, options),
7634
- readFile: async (path3) => {
7635
- const result = await sandbox.executeCommand(base64ReadCommand(path3));
7925
+ readFile: async (path6, options) => {
7926
+ const result = await sandbox.executeCommand(base64ReadCommand(path6));
7636
7927
  if (result.exitCode !== 0) {
7637
- throw new Error(`Failed to read file "${path3}": ${result.stderr}`);
7928
+ throw new Error(`Failed to read file "${path6}": ${result.stderr}`);
7638
7929
  }
7639
- return Buffer.from(result.stdout, "base64").toString("utf-8");
7930
+ return readFileContent(Buffer.from(result.stdout, "base64"), options);
7931
+ },
7932
+ exists: async (path6) => {
7933
+ const result = await sandbox.executeCommand(existsCommand(path6));
7934
+ if (result.stdout === "true") return true;
7935
+ if (result.stdout === "false") return false;
7936
+ throw new Error(`Failed to check "${path6}": ${result.stderr}`);
7640
7937
  },
7641
7938
  writeFiles: async (files) => {
7642
7939
  for (const file of files) {
@@ -7823,6 +8120,9 @@ var appleEngine = {
7823
8120
  volumeCreateArgs(volume) {
7824
8121
  return ["volume", "create", volume.name];
7825
8122
  },
8123
+ async removeVolume(name) {
8124
+ await spawn3(CLI, ["volume", "rm", name]);
8125
+ },
7826
8126
  errorMessage: getCliErrorMessage,
7827
8127
  isServiceDown(message2) {
7828
8128
  return /apiserver|not running|connection refused|could not connect|xpc/i.test(
@@ -8034,13 +8334,200 @@ var BashException = class extends Error {
8034
8334
  };
8035
8335
 
8036
8336
  // packages/context/src/lib/sandbox/bash-tool.ts
8037
- import { tool as tool2 } from "ai";
8337
+ import { tool as tool3 } from "ai";
8038
8338
  import fg from "fast-glob";
8039
8339
  import { BashTransformPipeline, TeePlugin } from "just-bash";
8040
8340
  import { readFile } from "node:fs/promises";
8041
- import * as path from "node:path";
8042
- import z4 from "zod";
8341
+ import * as path4 from "node:path";
8342
+ import z7 from "zod";
8043
8343
  import { withHostOnlyToolMetadata as withHostOnlyToolMetadata2 } from "@deepagents/agent";
8344
+
8345
+ // packages/context/src/lib/sandbox/read-file-tool/index.ts
8346
+ import { tool as tool2 } from "ai";
8347
+ import path3 from "node:path";
8348
+ import z6 from "zod";
8349
+
8350
+ // packages/context/src/lib/sandbox/read-file-tool/binary.ts
8351
+ import { detectMediaType } from "@ai-sdk/provider-utils";
8352
+ import path from "node:path";
8353
+ var SNIFF_BYTES = 8 * 1024;
8354
+ var NUL = 0;
8355
+ var mediaKinds = /* @__PURE__ */ new Set(["image", "video", "audio"]);
8356
+ var binaryFileFormat = {
8357
+ read({ bytes, path: filePath, options }) {
8358
+ options.abortSignal?.throwIfAborted();
8359
+ const mediaType = detectMediaType({ data: bytes });
8360
+ const kind = mediaType?.split("/")[0];
8361
+ const binary = kind !== void 0 && mediaKinds.has(kind) || bytes.subarray(0, SNIFF_BYTES).includes(NUL);
8362
+ if (!binary) return void 0;
8363
+ return {
8364
+ type: "error-text",
8365
+ value: `${path.posix.basename(filePath)} is a binary ${mediaType ?? "file"} that readFile cannot display. Convert it in the sandbox first and read the result: HEIC stills with \`heif-dec in.heic out.jpg\`, video frames with \`ffmpeg -i in.mov -frames:v 1 frame.jpg\`, audio through a transcript or metadata tool. UTF-16 text counts as binary too; re-encode it as UTF-8.`
8366
+ };
8367
+ }
8368
+ };
8369
+
8370
+ // packages/context/src/lib/sandbox/read-file-tool/format.ts
8371
+ import path2 from "node:path";
8372
+ function formatBytes(bytes) {
8373
+ return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
8374
+ }
8375
+ function attachment(input, {
8376
+ label,
8377
+ maxBytes,
8378
+ mediaType
8379
+ }) {
8380
+ input.options.abortSignal?.throwIfAborted();
8381
+ if (input.bytes.byteLength > maxBytes) {
8382
+ return {
8383
+ type: "error-text",
8384
+ value: `${label} exceeds ${formatBytes(maxBytes)} limit (got ${formatBytes(input.bytes.byteLength)})`
8385
+ };
8386
+ }
8387
+ return {
8388
+ type: "content",
8389
+ value: [
8390
+ {
8391
+ type: "file",
8392
+ data: {
8393
+ type: "data",
8394
+ data: Buffer.from(input.bytes).toString("base64")
8395
+ },
8396
+ mediaType,
8397
+ filename: path2.posix.basename(input.path)
8398
+ }
8399
+ ]
8400
+ };
8401
+ }
8402
+
8403
+ // packages/context/src/lib/sandbox/read-file-tool/image.ts
8404
+ import { detectMediaType as detectMediaType2 } from "@ai-sdk/provider-utils";
8405
+ var IMAGE_MAX_BYTES = 5 * 1024 * 1024;
8406
+ var supportedImageMediaTypes = /* @__PURE__ */ new Set([
8407
+ "image/png",
8408
+ "image/jpeg",
8409
+ "image/gif",
8410
+ "image/webp"
8411
+ ]);
8412
+ var imageFileFormat = {
8413
+ read(input) {
8414
+ const mediaType = detectMediaType2({
8415
+ data: input.bytes,
8416
+ topLevelType: "image"
8417
+ });
8418
+ return mediaType && supportedImageMediaTypes.has(mediaType) ? attachment(input, {
8419
+ label: "Image",
8420
+ maxBytes: IMAGE_MAX_BYTES,
8421
+ mediaType
8422
+ }) : void 0;
8423
+ }
8424
+ };
8425
+
8426
+ // packages/context/src/lib/sandbox/read-file-tool/ooxml.ts
8427
+ import { extname } from "node:path";
8428
+ var OFFICE_MAX_BYTES = 50 * 1024 * 1024;
8429
+ var mediaTypeByExtension = {
8430
+ ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
8431
+ ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
8432
+ ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
8433
+ };
8434
+ function hasZipSignature(bytes) {
8435
+ return bytes[0] === 80 && bytes[1] === 75 && bytes[2] === 3 && bytes[3] === 4;
8436
+ }
8437
+ var ooxmlFileFormat = {
8438
+ read(input) {
8439
+ if (!hasZipSignature(input.bytes)) return void 0;
8440
+ const mediaType = mediaTypeByExtension[extname(input.path).toLowerCase()];
8441
+ return mediaType ? attachment(input, {
8442
+ label: "Office file",
8443
+ maxBytes: OFFICE_MAX_BYTES,
8444
+ mediaType
8445
+ }) : void 0;
8446
+ }
8447
+ };
8448
+
8449
+ // packages/context/src/lib/sandbox/read-file-tool/pdf.ts
8450
+ import { detectMediaType as detectMediaType3 } from "@ai-sdk/provider-utils";
8451
+ var PDF_MAX_BYTES = 32 * 1024 * 1024;
8452
+ var pdfFileFormat = {
8453
+ read(input) {
8454
+ return detectMediaType3({
8455
+ data: input.bytes,
8456
+ topLevelType: "application"
8457
+ }) === "application/pdf" ? attachment(input, {
8458
+ label: "PDF",
8459
+ maxBytes: PDF_MAX_BYTES,
8460
+ mediaType: "application/pdf"
8461
+ }) : void 0;
8462
+ }
8463
+ };
8464
+
8465
+ // packages/context/src/lib/sandbox/read-file-tool/text.ts
8466
+ var TEXT_MAX_BYTES = 10 * 1024 * 1024;
8467
+ var textFileFormat = {
8468
+ read({ bytes, limit, offset, options }) {
8469
+ options.abortSignal?.throwIfAborted();
8470
+ if (offset === void 0 && limit === void 0 && bytes.byteLength > TEXT_MAX_BYTES) {
8471
+ return {
8472
+ type: "error-text",
8473
+ value: `Text file exceeds ${formatBytes(TEXT_MAX_BYTES)} limit (got ${formatBytes(bytes.byteLength)}). Use offset/limit to slice.`
8474
+ };
8475
+ }
8476
+ const content = readFileContent(bytes, { encoding: "utf-8" });
8477
+ if (offset === void 0 && limit === void 0) {
8478
+ return { type: "text", value: content };
8479
+ }
8480
+ const start = (offset ?? 1) - 1;
8481
+ const end = limit === void 0 ? void 0 : start + limit;
8482
+ return {
8483
+ type: "text",
8484
+ value: content.split(/\r\n|\n|\r/u).slice(start, end).join("\n")
8485
+ };
8486
+ }
8487
+ };
8488
+
8489
+ // packages/context/src/lib/sandbox/read-file-tool/index.ts
8490
+ var formats = [
8491
+ imageFileFormat,
8492
+ pdfFileFormat,
8493
+ ooxmlFileFormat,
8494
+ binaryFileFormat,
8495
+ textFileFormat
8496
+ ];
8497
+ var inputSchema = z6.strictObject({
8498
+ path: z6.string().min(1).describe("The path to the file to read"),
8499
+ offset: z6.number().int().min(1).optional().describe("1-based line number to start at for text files"),
8500
+ limit: z6.number().int().min(1).optional().describe("Number of text lines to return from offset")
8501
+ });
8502
+ function createReadFileTool({
8503
+ sandbox,
8504
+ destination
8505
+ }) {
8506
+ return tool2({
8507
+ description: "Read a sandbox file. Supports text, PNG, JPEG, GIF, WebP, PDF, XLSX, DOCX, and PPTX; other binary files such as HEIC, video, and audio are refused with conversion guidance. Use offset/limit with large text files.",
8508
+ inputExamples: [
8509
+ { input: { path: "README.md" } },
8510
+ { input: { path: "logs/trace.jsonl", offset: 1, limit: 200 } }
8511
+ ],
8512
+ inputSchema,
8513
+ execute: async (input, options) => {
8514
+ options.abortSignal?.throwIfAborted();
8515
+ const bytes = await sandbox.readFile(
8516
+ path3.posix.resolve(destination, input.path),
8517
+ { encoding: "binary" }
8518
+ );
8519
+ options.abortSignal?.throwIfAborted();
8520
+ for (const format of formats) {
8521
+ const result = format.read({ ...input, bytes, options });
8522
+ if (result !== void 0) return result;
8523
+ }
8524
+ throw new Error(`No file format accepted ${input.path}`);
8525
+ },
8526
+ toModelOutput: ({ output }) => output
8527
+ });
8528
+ }
8529
+
8530
+ // packages/context/src/lib/sandbox/bash-tool.ts
8044
8531
  var DEFAULT_DESTINATION = "/workspace";
8045
8532
  var DEFAULT_MAX_FILES = 1e3;
8046
8533
  var DEFAULT_MAX_OUTPUT_LENGTH = 3e4;
@@ -8132,8 +8619,9 @@ function withBashExceptionCatch(sandbox) {
8132
8619
  throw error;
8133
8620
  }
8134
8621
  },
8135
- readFile: (filePath) => sandbox.readFile(filePath),
8622
+ readFile: (filePath, options) => sandbox.readFile(filePath, options),
8136
8623
  writeFiles: (files) => sandbox.writeFiles(files),
8624
+ exists: (filePath) => sandbox.exists(filePath),
8137
8625
  dispose: () => sandbox.dispose(),
8138
8626
  [Symbol.asyncDispose]() {
8139
8627
  return decorated.dispose();
@@ -8163,7 +8651,7 @@ async function listInitialFiles(options) {
8163
8651
  const files = /* @__PURE__ */ new Map();
8164
8652
  if (options.uploadDirectory) {
8165
8653
  const { source, include = "**/*" } = options.uploadDirectory;
8166
- const absoluteSource = path.resolve(source);
8654
+ const absoluteSource = path4.resolve(source);
8167
8655
  const uploaded = await fg(include, {
8168
8656
  cwd: absoluteSource,
8169
8657
  dot: true,
@@ -8173,7 +8661,7 @@ async function listInitialFiles(options) {
8173
8661
  for (const relativePath of uploaded) {
8174
8662
  files.set(relativePath, {
8175
8663
  path: relativePath,
8176
- sourcePath: path.join(absoluteSource, relativePath)
8664
+ sourcePath: path4.join(absoluteSource, relativePath)
8177
8665
  });
8178
8666
  }
8179
8667
  }
@@ -8192,7 +8680,7 @@ async function uploadInitialFiles(sandbox, destination, files) {
8192
8680
  for (let index = 0; index < files.length; index += WRITE_BATCH_SIZE) {
8193
8681
  const batch = await Promise.all(
8194
8682
  files.slice(index, index + WRITE_BATCH_SIZE).map(async (file) => ({
8195
- path: path.posix.join(destination, file.path),
8683
+ path: path4.posix.join(destination, file.path),
8196
8684
  content: "content" in file ? file.content : await readFile(file.sourcePath)
8197
8685
  }))
8198
8686
  );
@@ -8212,12 +8700,12 @@ async function createToolPrompt(sandbox, filenames, customPrompt) {
8212
8700
  const lines = [
8213
8701
  `Available tools: ${[...available].sort().join(", ")}, and more`
8214
8702
  ];
8215
- const formats = new Set(
8703
+ const formats2 = new Set(
8216
8704
  filenames.map(
8217
- (filename) => EXTENSION_FORMATS[path.extname(filename).toLowerCase()]
8705
+ (filename) => EXTENSION_FORMATS[path4.extname(filename).toLowerCase()]
8218
8706
  ).filter((format) => format !== void 0)
8219
8707
  );
8220
- for (const format of formats) {
8708
+ for (const format of formats2) {
8221
8709
  const tools = FORMAT_TOOLS[format].filter((name) => available.has(name));
8222
8710
  if (tools.length > 0) {
8223
8711
  lines.push(`For ${FORMAT_LABELS[format]}: ${tools.join(", ")}`);
@@ -8277,7 +8765,7 @@ async function createBashTool(options) {
8277
8765
  );
8278
8766
  const extraInstructions = [options.extraInstructions, REASONING_INSTRUCTION].filter(Boolean).join("\n\n");
8279
8767
  const experimentalTeeTransform = options.experimentalTeeTransform ?? false;
8280
- const bash = tool2({
8768
+ const bash = tool3({
8281
8769
  description: createBashDescription({
8282
8770
  destination,
8283
8771
  filenames: initialFiles.map((file) => file.path),
@@ -8285,9 +8773,9 @@ async function createBashTool(options) {
8285
8773
  extraInstructions,
8286
8774
  experimentalTeeTransform
8287
8775
  }),
8288
- inputSchema: z4.object({
8289
- command: z4.string().describe("The bash command to execute"),
8290
- reasoning: z4.string().trim().min(1).describe("Brief reason for executing this command")
8776
+ inputSchema: z7.object({
8777
+ command: z7.string().describe("The bash command to execute"),
8778
+ reasoning: z7.string().trim().min(1).describe("Brief reason for executing this command")
8291
8779
  }),
8292
8780
  execute: async ({ command: originalCommand }, executionOptions) => {
8293
8781
  let command = originalCommand;
@@ -8327,28 +8815,18 @@ async function createBashTool(options) {
8327
8815
  return Object.keys(meta).length > 0 ? { ...result, meta } : result;
8328
8816
  }
8329
8817
  });
8330
- const readFile2 = tool2({
8331
- description: "Read the contents of a file from the sandbox.",
8332
- inputSchema: z4.object({
8333
- path: z4.string().describe("The path to the file to read")
8334
- }),
8335
- execute: async ({ path: filePath }) => ({
8336
- content: await sandbox.readFile(
8337
- path.posix.resolve(destination, filePath)
8338
- )
8339
- })
8340
- });
8341
- const writeFile2 = tool2({
8818
+ const readFile2 = createReadFileTool({ sandbox, destination });
8819
+ const writeFile2 = tool3({
8342
8820
  description: "Write content to a file in the sandbox. Creates parent directories if needed.",
8343
- inputSchema: z4.object({
8344
- path: z4.string().describe("The path where the file should be written"),
8345
- content: z4.string().describe("The content to write to the file")
8821
+ inputSchema: z7.object({
8822
+ path: z7.string().describe("The path where the file should be written"),
8823
+ content: z7.string().describe("The content to write to the file")
8346
8824
  }),
8347
8825
  execute: async ({ path: filePath, content }) => {
8348
8826
  try {
8349
8827
  await sandbox.writeFiles([
8350
8828
  {
8351
- path: path.posix.resolve(destination, filePath),
8829
+ path: path4.posix.resolve(destination, filePath),
8352
8830
  content
8353
8831
  }
8354
8832
  ]);
@@ -8367,7 +8845,7 @@ async function createBashTool(options) {
8367
8845
  import { existsSync as existsSync2 } from "fs";
8368
8846
  import { defineCommand } from "just-bash";
8369
8847
  import spawn4 from "nano-spawn";
8370
- import * as path2 from "path";
8848
+ import * as path5 from "path";
8371
8849
  function createBinaryBridges(...binaries) {
8372
8850
  return binaries.map((input) => {
8373
8851
  const config = typeof input === "string" ? { name: input } : input;
@@ -8389,11 +8867,11 @@ function createBinaryBridges(...binaries) {
8389
8867
  if (arg.startsWith("-")) {
8390
8868
  return arg;
8391
8869
  }
8392
- const hasExtension = path2.extname(arg) !== "";
8393
- const hasPathSep = arg.includes(path2.sep) || arg.includes("/");
8870
+ const hasExtension = path5.extname(arg) !== "";
8871
+ const hasPathSep = arg.includes(path5.sep) || arg.includes("/");
8394
8872
  const isRelative = arg.startsWith(".");
8395
8873
  if (hasExtension || hasPathSep || isRelative) {
8396
- return path2.resolve(realCwd, arg);
8874
+ return path5.resolve(realCwd, arg);
8397
8875
  }
8398
8876
  return arg;
8399
8877
  });
@@ -8446,7 +8924,7 @@ function resolveRealCwd(ctx) {
8446
8924
  const fs = ctx.fs;
8447
8925
  let realCwd;
8448
8926
  if (fs.root) {
8449
- realCwd = path2.join(fs.root, ctx.cwd);
8927
+ realCwd = path5.join(fs.root, ctx.cwd);
8450
8928
  } else if (typeof fs.getMountPoint === "function" && typeof fs.toRealPath === "function") {
8451
8929
  const real = fs.toRealPath(ctx.cwd);
8452
8930
  realCwd = real ?? process.cwd();
@@ -8499,6 +8977,7 @@ async function createDaytonaSandbox(client, options = {}) {
8499
8977
  }
8500
8978
  await assertDaytonaBash(sandbox, options.commandTimeout);
8501
8979
  const backend = createDaytonaSandboxMethods({
8980
+ sdk,
8502
8981
  sandbox,
8503
8982
  commandTimeout: options.commandTimeout
8504
8983
  });
@@ -8652,7 +9131,7 @@ function validateDaytonaOptions(options) {
8652
9131
  }
8653
9132
  }
8654
9133
  function createDaytonaSandboxMethods(args) {
8655
- const { sandbox, commandTimeout } = args;
9134
+ const { sdk, sandbox, commandTimeout } = args;
8656
9135
  const executeCommand = async (command, options) => {
8657
9136
  if (options?.signal?.aborted) {
8658
9137
  return abortedCommandResult();
@@ -8698,13 +9177,25 @@ function createDaytonaSandboxMethods(args) {
8698
9177
  commandTimeout
8699
9178
  });
8700
9179
  },
8701
- async readFile(path3) {
9180
+ async readFile(path6, options) {
8702
9181
  try {
8703
- const bytes = await sandbox.fs.downloadFile(path3);
8704
- return Buffer.from(bytes).toString("utf-8");
9182
+ const bytes = await sandbox.fs.downloadFile(path6);
9183
+ return readFileContent(bytes, options);
8705
9184
  } catch (error) {
8706
9185
  throw new DaytonaCommandError(
8707
- `Failed to read file "${path3}": ${toError(error).message}`,
9186
+ `Failed to read file "${path6}": ${toError(error).message}`,
9187
+ toError(error)
9188
+ );
9189
+ }
9190
+ },
9191
+ async exists(path6) {
9192
+ try {
9193
+ await sandbox.fs.getFileDetails(path6);
9194
+ return true;
9195
+ } catch (error) {
9196
+ if (error instanceof sdk.DaytonaNotFoundError) return false;
9197
+ throw new DaytonaCommandError(
9198
+ `Failed to check "${path6}": ${toError(error).message}`,
8708
9199
  toError(error)
8709
9200
  );
8710
9201
  }
@@ -8943,10 +9434,10 @@ function abortedExitInfo() {
8943
9434
  }
8944
9435
  function uniqueParentDirectories(paths) {
8945
9436
  const dirs = /* @__PURE__ */ new Set();
8946
- for (const path3 of paths) {
8947
- const index = path3.lastIndexOf("/");
9437
+ for (const path6 of paths) {
9438
+ const index = path6.lastIndexOf("/");
8948
9439
  if (index > 0) {
8949
- dirs.add(path3.slice(0, index));
9440
+ dirs.add(path6.slice(0, index));
8950
9441
  }
8951
9442
  }
8952
9443
  return [...dirs];
@@ -8967,8 +9458,9 @@ function toError(error) {
8967
9458
  // packages/context/src/lib/sandbox/docker-sandbox.ts
8968
9459
  import spawn5 from "nano-spawn";
8969
9460
  import { spawn as childSpawn3 } from "node:child_process";
8970
- import { createHash as createHash2 } from "node:crypto";
9461
+ import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
8971
9462
  import { readFileSync as readFileSync2 } from "node:fs";
9463
+ import { setTimeout as delay2 } from "node:timers/promises";
8972
9464
  function isDockerfileOptions(opts) {
8973
9465
  return "dockerfile" in opts;
8974
9466
  }
@@ -9184,6 +9676,20 @@ var dockerEngine = {
9184
9676
  const err = error;
9185
9677
  return err.stderr || err.stdout || err.message || String(error);
9186
9678
  },
9679
+ async removeVolume(name) {
9680
+ const deadline = performance.now() + 3e4;
9681
+ while (true) {
9682
+ try {
9683
+ await spawn5("docker", ["volume", "rm", name]);
9684
+ return;
9685
+ } catch (error) {
9686
+ if (!this.errorMessage(error).includes("volume is in use") || performance.now() >= deadline) {
9687
+ throw error;
9688
+ }
9689
+ await delay2(25);
9690
+ }
9691
+ }
9692
+ },
9187
9693
  isServiceDown(message2) {
9188
9694
  return message2.includes("Cannot connect") || message2.includes("docker daemon");
9189
9695
  },
@@ -9253,14 +9759,190 @@ function buildDockerExecFlags(options) {
9253
9759
  }
9254
9760
  return flags;
9255
9761
  }
9762
+ function managedDockerCommand(command, controlPath) {
9763
+ return [
9764
+ "set -m",
9765
+ `control=${shellQuote(controlPath)}`,
9766
+ ': > "$control" || exit 1',
9767
+ `trap 'rm -f "$control"' EXIT`,
9768
+ `bash -lc ${shellQuote(command)} &`,
9769
+ "pid=$!",
9770
+ `printf '%s\\n' "$pid" > "$control"`,
9771
+ 'wait "$pid"',
9772
+ 'exit "$?"'
9773
+ ].join("\n");
9774
+ }
9775
+ async function waitForDockerExecPid(execArgs, controlPath, localExit) {
9776
+ const exited = localExit.then(
9777
+ () => true,
9778
+ () => true
9779
+ );
9780
+ const readPid = `if IFS= read -r pid < ${shellQuote(controlPath)}; then printf '%s' "$pid"; fi`;
9781
+ while (true) {
9782
+ const result = await spawn5("docker", execArgs(readPid));
9783
+ if (result.stdout) return Number(result.stdout);
9784
+ if (await Promise.race([exited, delay2(10).then(() => false)])) {
9785
+ return void 0;
9786
+ }
9787
+ }
9788
+ }
9789
+ async function terminateDockerExec(execArgs, controlPath, localExit) {
9790
+ const pid = await waitForDockerExecPid(execArgs, controlPath, localExit);
9791
+ if (pid === void 0) return false;
9792
+ const result = await spawn5(
9793
+ "docker",
9794
+ execArgs(
9795
+ [
9796
+ `pid=${pid}`,
9797
+ `IFS= read -r control_pid < ${shellQuote(controlPath)} || exit 0`,
9798
+ '[ "$control_pid" = "$pid" ] || exit 0',
9799
+ 'kill -KILL -- "-$pid" 2>/dev/null || exit 0',
9800
+ "group_alive() {",
9801
+ " local stat rest state pgrp",
9802
+ " for stat_path in /proc/[0-9]*/stat; do",
9803
+ ' IFS= read -r stat < "$stat_path" || continue',
9804
+ " rest=${stat##*) }",
9805
+ ' read -r state _ pgrp _ <<< "$rest"',
9806
+ ' if [ "$pgrp" = "$pid" ] && [ "$state" != Z ]; then return 0; fi',
9807
+ " done",
9808
+ " return 1",
9809
+ "}",
9810
+ "while group_alive; do sleep 0.01; done",
9811
+ "printf terminated"
9812
+ ].join("\n")
9813
+ )
9814
+ );
9815
+ return result.stdout === "terminated";
9816
+ }
9817
+ function spawnDockerProcess(execArgs, command, options = {}, commandTimeout) {
9818
+ if (!commandTimeout && options.signal === void 0) {
9819
+ return toSandboxProcess(
9820
+ childSpawn3("docker", execArgs(command, options)),
9821
+ void 0
9822
+ );
9823
+ }
9824
+ const controlPath = `/dev/shm/deepagents-exec-${randomUUID2()}.pid`;
9825
+ const localProcess = toSandboxProcess(
9826
+ childSpawn3(
9827
+ "docker",
9828
+ execArgs(managedDockerCommand(command, controlPath), options)
9829
+ ),
9830
+ void 0
9831
+ );
9832
+ let termination;
9833
+ const terminate = (reason) => {
9834
+ termination ??= {
9835
+ reason,
9836
+ confirmed: terminateDockerExec(execArgs, controlPath, localProcess.exit)
9837
+ };
9838
+ };
9839
+ const onAbort = () => terminate("abort");
9840
+ if (options.signal?.aborted) onAbort();
9841
+ else options.signal?.addEventListener("abort", onAbort, { once: true });
9842
+ let timeout;
9843
+ if (commandTimeout) {
9844
+ timeout = setTimeout(() => terminate("timeout"), commandTimeout);
9845
+ timeout.unref();
9846
+ }
9847
+ return {
9848
+ stdout: localProcess.stdout,
9849
+ stderr: localProcess.stderr,
9850
+ exit: localProcess.exit.then(async (info) => {
9851
+ const activeTermination = termination;
9852
+ if (!activeTermination || !await activeTermination.confirmed) {
9853
+ return info;
9854
+ }
9855
+ return activeTermination.reason === "timeout" ? { code: 124, signal: null, success: false } : { code: null, signal: "SIGKILL", success: false };
9856
+ }).finally(() => {
9857
+ clearTimeout(timeout);
9858
+ options.signal?.removeEventListener("abort", onAbort);
9859
+ })
9860
+ };
9861
+ }
9862
+ async function executeDockerCommand(execArgs, command, options = {}, commandTimeout) {
9863
+ if (!commandTimeout && options.signal === void 0) {
9864
+ try {
9865
+ const result2 = await spawn5("docker", execArgs(command));
9866
+ return { stdout: result2.stdout, stderr: result2.stderr, exitCode: 0 };
9867
+ } catch (error) {
9868
+ const err = error;
9869
+ return {
9870
+ stdout: err.stdout || "",
9871
+ stderr: err.stderr || err.message || "",
9872
+ exitCode: err.exitCode ?? 1
9873
+ };
9874
+ }
9875
+ }
9876
+ const process2 = spawnDockerProcess(
9877
+ execArgs,
9878
+ command,
9879
+ { signal: options.signal },
9880
+ commandTimeout
9881
+ );
9882
+ const [stdout, stderr, info] = await Promise.all([
9883
+ new Response(process2.stdout).text(),
9884
+ new Response(process2.stderr).text(),
9885
+ process2.exit
9886
+ ]);
9887
+ const result = {
9888
+ stdout: stdout.replace(/\r?\n$/, ""),
9889
+ stderr: stderr.replace(/\r?\n$/, "")
9890
+ };
9891
+ if (info.signal === "SIGKILL") {
9892
+ return {
9893
+ ...result,
9894
+ stderr: result.stderr || "Command aborted",
9895
+ exitCode: 1
9896
+ };
9897
+ }
9898
+ return { ...result, exitCode: info.code ?? 1 };
9899
+ }
9900
+ var DockerRuntimeStrategy = class extends RuntimeStrategy {
9901
+ exec(command, options) {
9902
+ return executeDockerCommand(
9903
+ (value, spawnOptions) => this.engine.execArgs(this.context.containerId, value, spawnOptions),
9904
+ command,
9905
+ options,
9906
+ this.opts.commandTimeout
9907
+ );
9908
+ }
9909
+ spawnProcess(command, options) {
9910
+ return spawnDockerProcess(
9911
+ (value, spawnOptions) => this.engine.execArgs(this.context.containerId, value, spawnOptions),
9912
+ command,
9913
+ options,
9914
+ this.opts.commandTimeout
9915
+ );
9916
+ }
9917
+ };
9918
+ var DockerContainerfileStrategy = class extends ContainerfileStrategy {
9919
+ exec(command, options) {
9920
+ return executeDockerCommand(
9921
+ (value, spawnOptions) => this.engine.execArgs(this.context.containerId, value, spawnOptions),
9922
+ command,
9923
+ options,
9924
+ this.opts.commandTimeout
9925
+ );
9926
+ }
9927
+ spawnProcess(command, options) {
9928
+ return spawnDockerProcess(
9929
+ (value, spawnOptions) => this.engine.execArgs(this.context.containerId, value, spawnOptions),
9930
+ command,
9931
+ options,
9932
+ this.opts.commandTimeout
9933
+ );
9934
+ }
9935
+ };
9256
9936
  var ComposeStrategy = class extends ContainerSandboxStrategy {
9257
9937
  projectName;
9258
9938
  composeFile;
9259
9939
  service;
9940
+ commandTimeout;
9260
9941
  constructor(args) {
9261
9942
  super({ resources: args.resources }, dockerEngine);
9262
9943
  this.composeFile = args.compose;
9263
9944
  this.service = args.service;
9945
+ this.commandTimeout = args.commandTimeout;
9264
9946
  this.projectName = this.computeProjectName();
9265
9947
  }
9266
9948
  computeProjectName() {
@@ -9296,36 +9978,23 @@ var ComposeStrategy = class extends ContainerSandboxStrategy {
9296
9978
  async configure() {
9297
9979
  }
9298
9980
  async exec(command, options) {
9299
- try {
9300
- const result = await spawn5(
9301
- "docker",
9302
- [
9303
- "compose",
9304
- "-f",
9305
- this.composeFile,
9306
- "-p",
9307
- this.projectName,
9308
- "exec",
9309
- "-T",
9310
- this.service,
9311
- "bash",
9312
- "-lc",
9313
- command
9314
- ],
9315
- { signal: options?.signal }
9316
- );
9317
- return { stdout: result.stdout, stderr: result.stderr, exitCode: 0 };
9318
- } catch (error) {
9319
- const err = error;
9320
- return {
9321
- stdout: err.stdout || "",
9322
- stderr: err.stderr || err.message || "",
9323
- exitCode: err.exitCode ?? 1
9324
- };
9325
- }
9981
+ return executeDockerCommand(
9982
+ (value, spawnOptions) => this.execArgs(value, spawnOptions),
9983
+ command,
9984
+ options,
9985
+ this.commandTimeout
9986
+ );
9326
9987
  }
9327
9988
  spawnProcess(command, options) {
9328
- const child = childSpawn3("docker", [
9989
+ return spawnDockerProcess(
9990
+ (value, spawnOptions) => this.execArgs(value, spawnOptions),
9991
+ command,
9992
+ options,
9993
+ this.commandTimeout
9994
+ );
9995
+ }
9996
+ execArgs(command, options) {
9997
+ return [
9329
9998
  "compose",
9330
9999
  "-f",
9331
10000
  this.composeFile,
@@ -9338,8 +10007,7 @@ var ComposeStrategy = class extends ContainerSandboxStrategy {
9338
10007
  "bash",
9339
10008
  "-lc",
9340
10009
  command
9341
- ]);
9342
- return toSandboxProcess(child, options?.signal);
10010
+ ];
9343
10011
  }
9344
10012
  async stopContainer(_containerId) {
9345
10013
  try {
@@ -9360,18 +10028,19 @@ async function createDockerSandbox(options = {}) {
9360
10028
  return new ComposeStrategy({
9361
10029
  compose: options.compose,
9362
10030
  service: options.service,
9363
- resources: options.resources
10031
+ resources: options.resources,
10032
+ commandTimeout: options.commandTimeout
9364
10033
  }).create();
9365
10034
  }
9366
10035
  if (isDockerfileOptions(options)) {
9367
- return new ContainerfileStrategy(options, dockerEngine, {
10036
+ return new DockerContainerfileStrategy(options, dockerEngine, {
9368
10037
  dockerfile: options.dockerfile,
9369
10038
  context: options.context ?? ".",
9370
10039
  showBuildLogs: options.showBuildLogs ?? false,
9371
10040
  identity: options.platform
9372
10041
  }).create();
9373
10042
  }
9374
- return new RuntimeStrategy(options, dockerEngine, {
10043
+ return new DockerRuntimeStrategy(options, dockerEngine, {
9375
10044
  image: options.image ?? dockerEngine.defaultImage,
9376
10045
  installers: options.installers ?? []
9377
10046
  }).create();
@@ -9386,7 +10055,7 @@ async function useSandbox(options, fn) {
9386
10055
  }
9387
10056
 
9388
10057
  // packages/context/src/lib/sandbox/strace/file-changes.ts
9389
- import { randomUUID as randomUUID2 } from "node:crypto";
10058
+ import { randomUUID as randomUUID3 } from "node:crypto";
9390
10059
  import { posix as posix3 } from "node:path";
9391
10060
 
9392
10061
  // packages/context/src/lib/sandbox/strace/index.ts
@@ -9508,8 +10177,8 @@ function fdPath(args) {
9508
10177
  function openFlags(args) {
9509
10178
  return args.match(/\bO_[A-Z_]+(?:\|O_[A-Z_]+)*/)?.[0] ?? "";
9510
10179
  }
9511
- function matchesGlobs(path3, include, exclude) {
9512
- return include.some((glob) => posix2.matchesGlob(path3, glob)) && !exclude?.some((glob) => posix2.matchesGlob(path3, glob));
10180
+ function matchesGlobs(path6, include, exclude) {
10181
+ return include.some((glob) => posix2.matchesGlob(path6, glob)) && !exclude?.some((glob) => posix2.matchesGlob(path6, glob));
9513
10182
  }
9514
10183
  function parseStraceTrace(raw, options) {
9515
10184
  const { include, exclude, traceDir, traceFile } = options;
@@ -9517,12 +10186,12 @@ function parseStraceTrace(raw, options) {
9517
10186
  const state = /* @__PURE__ */ new Map();
9518
10187
  const renames = [];
9519
10188
  const resolve3 = (dir, p) => posix2.normalize(p.startsWith("/") ? p : posix2.join(dir, p));
9520
- const write = (path3) => {
9521
- state.set(path3, "write");
10189
+ const write = (path6) => {
10190
+ state.set(path6, "write");
9522
10191
  };
9523
- const remove = (path3) => {
9524
- if (state.get(path3) === "write") state.delete(path3);
9525
- else state.set(path3, "delete");
10192
+ const remove = (path6) => {
10193
+ if (state.get(path6) === "write") state.delete(path6);
10194
+ else state.set(path6, "delete");
9526
10195
  };
9527
10196
  for (const line of stitchLines(raw)) {
9528
10197
  const call = parseCall(line);
@@ -9569,13 +10238,13 @@ function parseStraceTrace(raw, options) {
9569
10238
  if (p) write(resolve3(dir, p));
9570
10239
  }
9571
10240
  }
9572
- const keep = (path3) => {
9573
- if (traceFile && path3 === traceFile) return false;
9574
- if (traceDir && (path3 === traceDir || path3.startsWith(`${traceDir}/`))) {
10241
+ const keep = (path6) => {
10242
+ if (traceFile && path6 === traceFile) return false;
10243
+ if (traceDir && (path6 === traceDir || path6.startsWith(`${traceDir}/`))) {
9575
10244
  return false;
9576
10245
  }
9577
- if (/^\/(proc|sys|dev)(\/|$)/.test(path3)) return false;
9578
- return matchesGlobs(path3, include, exclude);
10246
+ if (/^\/(proc|sys|dev)(\/|$)/.test(path6)) return false;
10247
+ return matchesGlobs(path6, include, exclude);
9579
10248
  };
9580
10249
  const changes = [];
9581
10250
  const emittedRenameTargets = /* @__PURE__ */ new Set();
@@ -9586,9 +10255,9 @@ function parseStraceTrace(raw, options) {
9586
10255
  emittedRenameTargets.add(to);
9587
10256
  }
9588
10257
  const tail = [];
9589
- for (const [path3, op] of state) {
9590
- if (emittedRenameTargets.has(path3)) continue;
9591
- if (keep(path3)) tail.push({ op, path: path3, timestamp: now });
10258
+ for (const [path6, op] of state) {
10259
+ if (emittedRenameTargets.has(path6)) continue;
10260
+ if (keep(path6)) tail.push({ op, path: path6, timestamp: now });
9592
10261
  }
9593
10262
  tail.sort((a, b) => {
9594
10263
  if (a.path < b.path) return -1;
@@ -9616,13 +10285,12 @@ async function withStraceFileChanges(sandbox, options) {
9616
10285
  const onError = options.onError ?? warnOnFileChangesError;
9617
10286
  const traceDir = options.traceDir ?? DEFAULT_TRACE_DIR;
9618
10287
  const innerExecute = sandbox.executeCommand.bind(sandbox);
9619
- const innerReadFile = sandbox.readFile.bind(sandbox);
9620
10288
  const innerWriteFiles = sandbox.writeFiles.bind(sandbox);
9621
10289
  const removeTrace = (traceFile) => void innerExecute(`rm -f ${shellQuote(traceFile)}`).catch(() => {
9622
10290
  });
9623
10291
  const readChanges = async (traceFile) => {
9624
10292
  try {
9625
- return parseStraceTrace(await innerReadFile(traceFile), {
10293
+ return parseStraceTrace(await sandbox.readFile(traceFile), {
9626
10294
  include,
9627
10295
  exclude,
9628
10296
  traceFile,
@@ -9645,8 +10313,8 @@ async function withStraceFileChanges(sandbox, options) {
9645
10313
  };
9646
10314
  const decorated = {
9647
10315
  async executeCommand(command, execOptions) {
9648
- const traceFile = `${traceDir}/${randomUUID2()}.strace`;
9649
- const sentinel = `__dat_trace_${randomUUID2()}__`;
10316
+ const traceFile = `${traceDir}/${randomUUID3()}.strace`;
10317
+ const sentinel = `__dat_trace_${randomUUID3()}__`;
9650
10318
  const wrapped = buildTracedCommand(
9651
10319
  command,
9652
10320
  traceFile,
@@ -9686,7 +10354,8 @@ async function withStraceFileChanges(sandbox, options) {
9686
10354
  }
9687
10355
  return result;
9688
10356
  },
9689
- readFile: innerReadFile,
10357
+ readFile: (path6, options2) => sandbox.readFile(path6, options2),
10358
+ exists: (path6) => sandbox.exists(path6),
9690
10359
  // The writeFile tool mutates the filesystem outside strace's view, so
9691
10360
  // observe it directly: synthesize a `write` change per file (under the
9692
10361
  // observation root) and run it through onFileChanges. A throw propagates to
@@ -9696,7 +10365,7 @@ async function withStraceFileChanges(sandbox, options) {
9696
10365
  writeFiles: async (files) => {
9697
10366
  await innerWriteFiles(files);
9698
10367
  const now = Date.now();
9699
- const changes = files.map((f) => posix3.normalize(f.path)).filter((path3) => matchesGlobs(path3, include, exclude)).map((path3) => ({ op: "write", path: path3, timestamp: now }));
10368
+ const changes = files.map((f) => posix3.normalize(f.path)).filter((path6) => matchesGlobs(path6, include, exclude)).map((path6) => ({ op: "write", path: path6, timestamp: now }));
9700
10369
  if (changes.length) await onFileChanges?.(changes);
9701
10370
  },
9702
10371
  dispose: async () => {
@@ -9711,7 +10380,7 @@ async function withStraceFileChanges(sandbox, options) {
9711
10380
  if (sandbox.spawn) {
9712
10381
  const innerSpawn = sandbox.spawn.bind(sandbox);
9713
10382
  decorated.spawn = (command, spawnOptions) => {
9714
- const traceFile = `${traceDir}/${randomUUID2()}.strace`;
10383
+ const traceFile = `${traceDir}/${randomUUID3()}.strace`;
9715
10384
  const child = innerSpawn(
9716
10385
  buildStraceCommand(command, traceFile, traceDir),
9717
10386
  spawnOptions
@@ -10018,7 +10687,7 @@ function formatVersion(version) {
10018
10687
  }
10019
10688
 
10020
10689
  // packages/context/src/lib/sandbox/microsandbox-sandbox.ts
10021
- import { randomUUID as randomUUID3 } from "node:crypto";
10690
+ import { randomUUID as randomUUID4 } from "node:crypto";
10022
10691
  var MICROSANDBOX_DEFAULT_DESTINATION = "/workspace";
10023
10692
  var MICROSANDBOX_DEFAULT_IMAGE = "bash:5.3-alpine3.24";
10024
10693
  var MICROSANDBOX_MAX_NAME_BYTES = 128;
@@ -10055,11 +10724,18 @@ async function createMicrosandboxSandbox(options = {}) {
10055
10724
  validateMicrosandboxOptions(options);
10056
10725
  const sdk = await importMicrosandbox();
10057
10726
  const ephemeral = options.name === void 0;
10058
- const name = options.name ?? `deepagents-msb-${randomUUID3()}`;
10727
+ const name = options.name ?? `deepagents-msb-${randomUUID4()}`;
10059
10728
  const workdir = options.workdir ?? MICROSANDBOX_DEFAULT_DESTINATION;
10729
+ const sandboxBuilder = (replace) => {
10730
+ let builder = sdk.Sandbox.builder(name).image(MICROSANDBOX_DEFAULT_IMAGE).patch((patch) => patch.mkdir(workdir));
10731
+ if (options.configure) builder = options.configure(builder);
10732
+ builder = builder.workdir(workdir).ephemeral(ephemeral).shell("bash");
10733
+ return replace ? builder.replace() : builder;
10734
+ };
10060
10735
  let vm;
10061
10736
  try {
10062
- vm = await acquireSandbox(sdk, { ...options, name, ephemeral, workdir });
10737
+ const builder = sandboxBuilder(options.replace === true);
10738
+ vm = await (ephemeral || options.replace ? builder.create() : builder.connectOrCreate());
10063
10739
  } catch (error) {
10064
10740
  throw normalizeMicrosandboxError(error, sdk);
10065
10741
  }
@@ -10067,7 +10743,7 @@ async function createMicrosandboxSandbox(options = {}) {
10067
10743
  await vm.fs().mkdir(workdir);
10068
10744
  await assertMicrosandboxBash(vm);
10069
10745
  } catch (error) {
10070
- await vm.stop().catch(() => {
10746
+ await vm[Symbol.asyncDispose]().catch(() => {
10071
10747
  });
10072
10748
  if (ephemeral) await sdk.Sandbox.remove(name).catch(() => {
10073
10749
  });
@@ -10078,7 +10754,8 @@ async function createMicrosandboxSandbox(options = {}) {
10078
10754
  vm,
10079
10755
  name,
10080
10756
  ephemeral,
10081
- commandTimeout: options.commandTimeout
10757
+ commandTimeout: options.commandTimeout,
10758
+ reconnect: ephemeral ? void 0 : () => sandboxBuilder(false).connectOrCreate()
10082
10759
  });
10083
10760
  if (options.readiness) {
10084
10761
  try {
@@ -10098,54 +10775,6 @@ async function importMicrosandbox() {
10098
10775
  throw new MicrosandboxNotAvailableError(toError2(error));
10099
10776
  }
10100
10777
  }
10101
- async function acquireSandbox(sdk, options) {
10102
- if (options.ephemeral || options.replace) {
10103
- return buildSandbox(sdk, options);
10104
- }
10105
- let handle;
10106
- try {
10107
- handle = await sdk.Sandbox.get(options.name);
10108
- } catch (error) {
10109
- if (error instanceof sdk.SandboxNotFoundError) {
10110
- return createFreshSandbox(sdk, options);
10111
- }
10112
- throw error;
10113
- }
10114
- if (handle.status === "running") {
10115
- return handle.connect();
10116
- }
10117
- if (handle.status === "draining") {
10118
- await handle.waitUntilStopped();
10119
- }
10120
- try {
10121
- return await handle.start();
10122
- } catch {
10123
- await sdk.Sandbox.remove(options.name).catch(() => {
10124
- });
10125
- return buildSandbox(sdk, options);
10126
- }
10127
- }
10128
- async function createFreshSandbox(sdk, options) {
10129
- try {
10130
- return await buildSandbox(sdk, options);
10131
- } catch (error) {
10132
- if (error instanceof sdk.SandboxAlreadyExistsError) {
10133
- const handle = await sdk.Sandbox.get(options.name);
10134
- return handle.status === "running" ? handle.connect() : handle.start();
10135
- }
10136
- throw error;
10137
- }
10138
- }
10139
- function buildSandbox(sdk, options) {
10140
- let builder = sdk.Sandbox.builder(options.name).image(options.image ?? MICROSANDBOX_DEFAULT_IMAGE).patch((patch) => patch.mkdir(options.workdir)).workdir(options.workdir);
10141
- if (options.ephemeral) builder = builder.ephemeral(true);
10142
- if (options.replace) builder = builder.replace();
10143
- if (options.cpus !== void 0) builder = builder.cpus(options.cpus);
10144
- if (options.memory !== void 0) builder = builder.memory(options.memory);
10145
- if (options.env) builder = builder.envs(options.env);
10146
- if (options.configure) builder = options.configure(builder);
10147
- return builder.shell("bash").create();
10148
- }
10149
10778
  async function assertMicrosandboxBash(vm) {
10150
10779
  try {
10151
10780
  const result = await vm.exec("bash", ["-lc", ":"]);
@@ -10183,9 +10812,25 @@ function validateMicrosandboxOptions(options) {
10183
10812
  }
10184
10813
  }
10185
10814
  function createMicrosandboxMethods(args) {
10186
- const { sdk, vm, name, ephemeral, commandTimeout } = args;
10815
+ const { sdk, name, ephemeral, commandTimeout, reconnect } = args;
10816
+ let vm = args.vm;
10817
+ let reconnecting;
10818
+ const run = async (operation) => {
10819
+ try {
10820
+ return await operation(vm);
10821
+ } catch (error) {
10822
+ if (!reconnect || !(error instanceof sdk.SandboxNotRunningError)) {
10823
+ throw error;
10824
+ }
10825
+ reconnecting ??= reconnect().finally(() => {
10826
+ reconnecting = void 0;
10827
+ });
10828
+ vm = await reconnecting;
10829
+ return operation(vm);
10830
+ }
10831
+ };
10187
10832
  const spawn7 = (command, options = {}) => {
10188
- return spawnMicrosandboxProcess(sdk, vm, command, {
10833
+ return spawnMicrosandboxProcess(sdk, run, command, {
10189
10834
  ...options,
10190
10835
  commandTimeout
10191
10836
  });
@@ -10209,25 +10854,38 @@ function createMicrosandboxMethods(args) {
10209
10854
  return { stdout, stderr, exitCode: info.code ?? 1 };
10210
10855
  },
10211
10856
  spawn: spawn7,
10212
- async readFile(path3) {
10857
+ async readFile(path6, options) {
10213
10858
  try {
10214
- return await vm.fs().readToString(path3);
10859
+ const bytes = await run((vm2) => vm2.fs().read(path6));
10860
+ return readFileContent(bytes, options);
10215
10861
  } catch (error) {
10216
10862
  throw new MicrosandboxCommandError(
10217
- `Failed to read file "${path3}": ${toError2(error).message}`,
10863
+ `Failed to read file "${path6}": ${toError2(error).message}`,
10864
+ toError2(error)
10865
+ );
10866
+ }
10867
+ },
10868
+ async exists(path6) {
10869
+ try {
10870
+ return await run((vm2) => vm2.fs().exists(path6));
10871
+ } catch (error) {
10872
+ throw new MicrosandboxCommandError(
10873
+ `Failed to check "${path6}": ${toError2(error).message}`,
10218
10874
  toError2(error)
10219
10875
  );
10220
10876
  }
10221
10877
  },
10222
10878
  async writeFiles(files) {
10223
10879
  try {
10224
- const fs = vm.fs();
10225
- for (const dir of uniqueParentDirectories2(files.map((f) => f.path))) {
10226
- await fs.mkdir(dir);
10227
- }
10228
- for (const file of files) {
10229
- await fs.write(file.path, file.content);
10230
- }
10880
+ await run(async (vm2) => {
10881
+ const fs = vm2.fs();
10882
+ for (const dir of uniqueParentDirectories2(files.map((f) => f.path))) {
10883
+ await fs.mkdir(dir);
10884
+ }
10885
+ for (const file of files) {
10886
+ await fs.write(file.path, file.content);
10887
+ }
10888
+ });
10231
10889
  } catch (error) {
10232
10890
  const err = toError2(error);
10233
10891
  throw new MicrosandboxCommandError(
@@ -10237,10 +10895,7 @@ function createMicrosandboxMethods(args) {
10237
10895
  }
10238
10896
  },
10239
10897
  async dispose() {
10240
- try {
10241
- await vm.stop();
10242
- } catch {
10243
- }
10898
+ await vm[Symbol.asyncDispose]();
10244
10899
  if (ephemeral) {
10245
10900
  await sdk.Sandbox.remove(name).catch(() => {
10246
10901
  });
@@ -10251,14 +10906,14 @@ function createMicrosandboxMethods(args) {
10251
10906
  }
10252
10907
  };
10253
10908
  }
10254
- function spawnMicrosandboxProcess(sdk, vm, command, options) {
10909
+ function spawnMicrosandboxProcess(sdk, run, command, options) {
10255
10910
  const stdout = createByteReadable();
10256
10911
  const stderr = createByteReadable();
10257
- const exit = pumpExecStream({ sdk, vm, command, options, stdout, stderr });
10912
+ const exit = pumpExecStream({ sdk, run, command, options, stdout, stderr });
10258
10913
  return { stdout: stdout.stream, stderr: stderr.stream, exit };
10259
10914
  }
10260
10915
  async function pumpExecStream(args) {
10261
- const { sdk, vm, command, options, stdout, stderr } = args;
10916
+ const { sdk, run, command, options, stdout, stderr } = args;
10262
10917
  const { signal } = options;
10263
10918
  let handle;
10264
10919
  let aborted = signal?.aborted ?? false;
@@ -10276,12 +10931,14 @@ async function pumpExecStream(args) {
10276
10931
  }
10277
10932
  signal?.addEventListener("abort", abort, { once: true });
10278
10933
  try {
10279
- handle = await vm.execStreamWith("bash", (builder) => {
10280
- builder.args(["-lc", command]).stdinNull();
10281
- if (options.cwd) builder.cwd(options.cwd);
10282
- if (options.env) builder.envs(options.env);
10283
- return builder;
10284
- });
10934
+ handle = await run(
10935
+ (vm) => vm.execStreamWith("bash", (builder) => {
10936
+ builder.args(["-lc", command]).stdinNull();
10937
+ if (options.cwd) builder.cwd(options.cwd);
10938
+ if (options.env) builder.envs(options.env);
10939
+ return builder;
10940
+ })
10941
+ );
10285
10942
  if (aborted) {
10286
10943
  return abortedExitInfo2();
10287
10944
  }
@@ -10380,10 +11037,10 @@ function abortedExitInfo2() {
10380
11037
  }
10381
11038
  function uniqueParentDirectories2(paths) {
10382
11039
  const dirs = /* @__PURE__ */ new Set();
10383
- for (const path3 of paths) {
10384
- const index = path3.lastIndexOf("/");
11040
+ for (const path6 of paths) {
11041
+ const index = path6.lastIndexOf("/");
10385
11042
  if (index > 0) {
10386
- dirs.add(path3.slice(0, index));
11043
+ dirs.add(path6.slice(0, index));
10387
11044
  }
10388
11045
  }
10389
11046
  return [...dirs];
@@ -10469,13 +11126,15 @@ import {
10469
11126
  Bash
10470
11127
  } from "just-bash";
10471
11128
  async function createVirtualSandbox(options) {
11129
+ const { fs } = options;
10472
11130
  const bash = new Bash({
10473
- fs: options.fs,
11131
+ fs,
10474
11132
  cwd: options.cwd,
10475
11133
  env: options.env,
10476
11134
  javascript: options.javascript,
10477
11135
  customCommands: options.customCommands
10478
11136
  });
11137
+ const resolve3 = (path6) => fs.resolvePath(bash.getCwd(), path6);
10479
11138
  const sandbox = {
10480
11139
  async executeCommand(command, options2) {
10481
11140
  const result = await bash.exec(
@@ -10488,17 +11147,20 @@ async function createVirtualSandbox(options) {
10488
11147
  exitCode: result.exitCode
10489
11148
  };
10490
11149
  },
10491
- async readFile(path3) {
10492
- return bash.readFile(path3);
11150
+ async readFile(path6, readOptions) {
11151
+ return readFileContent(
11152
+ await fs.readFileBuffer(resolve3(path6)),
11153
+ readOptions
11154
+ );
10493
11155
  },
10494
11156
  async writeFiles(files) {
10495
11157
  for (const file of files) {
10496
- await bash.writeFile(
10497
- file.path,
10498
- typeof file.content === "string" ? file.content : Buffer.from(file.content).toString("utf-8")
10499
- );
11158
+ await fs.writeFile(resolve3(file.path), file.content);
10500
11159
  }
10501
11160
  },
11161
+ async exists(path6) {
11162
+ return fs.exists(resolve3(path6));
11163
+ },
10502
11164
  async dispose() {
10503
11165
  },
10504
11166
  [Symbol.asyncDispose]() {
@@ -10518,12 +11180,12 @@ async function createVirtualSandbox(options) {
10518
11180
  }
10519
11181
 
10520
11182
  // packages/context/src/lib/skills/fragments.ts
10521
- import dedent2 from "dedent";
11183
+ import dedent4 from "dedent";
10522
11184
  function skills(availableSkills) {
10523
- const skillSnapshot = availableSkills.map(({ name, description, path: path3 }) => ({
11185
+ const skillSnapshot = availableSkills.map(({ name, description, path: path6 }) => ({
10524
11186
  name,
10525
11187
  description,
10526
- path: path3
11188
+ path: path6
10527
11189
  }));
10528
11190
  if (skillSnapshot.length === 0) {
10529
11191
  return {
@@ -10549,7 +11211,7 @@ function skills(availableSkills) {
10549
11211
  metadata: { skills: skillSnapshot }
10550
11212
  };
10551
11213
  }
10552
- var SKILLS_INSTRUCTIONS = dedent2`A skill is a set of local instructions to follow that is stored in a \`SKILL.md\` file. Below is the list of skills that can be used. Each entry includes a name, description, and file path so you can open the source for full instructions when using a specific skill.
11214
+ var SKILLS_INSTRUCTIONS = dedent4`A skill is a set of local instructions to follow that is stored in a \`SKILL.md\` file. Below is the list of skills that can be used. Each entry includes a name, description, and file path so you can open the source for full instructions when using a specific skill.
10553
11215
 
10554
11216
  ### How to use skills
10555
11217
  - Discovery: The list below shows the skills available in this session (name + description + file path). Skill bodies live on disk at the listed paths.
@@ -12271,6 +12933,7 @@ async function persistedWriter(options) {
12271
12933
  let seq = 0;
12272
12934
  let buffer = [];
12273
12935
  let failedByErrorChunk = false;
12936
+ let abortedByChunk = false;
12274
12937
  async function flush() {
12275
12938
  if (buffer.length === 0) return;
12276
12939
  const batch = buffer;
@@ -12296,11 +12959,14 @@ async function persistedWriter(options) {
12296
12959
  }
12297
12960
  }
12298
12961
  async function persistChunk(chunk) {
12962
+ if (chunk.data.type === "abort") {
12963
+ abortedByChunk = true;
12964
+ }
12299
12965
  if (strategy === "immediate") {
12300
12966
  await appendBatch([chunk]);
12301
12967
  } else {
12302
12968
  buffer.push(chunk);
12303
- if (buffer.length >= flushSize) {
12969
+ if (buffer.length >= flushSize || chunk.data.type.startsWith("data-")) {
12304
12970
  await flush();
12305
12971
  }
12306
12972
  }
@@ -12328,7 +12994,10 @@ async function persistedWriter(options) {
12328
12994
  async complete() {
12329
12995
  await flush();
12330
12996
  if (failedByErrorChunk) return;
12331
- await store.updateStreamStatus(streamId, "completed");
12997
+ await store.updateStreamStatus(
12998
+ streamId,
12999
+ abortedByChunk ? "cancelled" : "completed"
13000
+ );
12332
13001
  },
12333
13002
  async fail(error) {
12334
13003
  await flush();
@@ -12342,7 +13011,7 @@ async function persistedWriter(options) {
12342
13011
  }
12343
13012
 
12344
13013
  // packages/context/src/lib/stream/polling-change-source.ts
12345
- import { setTimeout as delay2 } from "node:timers/promises";
13014
+ import { setTimeout as delay3 } from "node:timers/promises";
12346
13015
 
12347
13016
  // packages/context/src/lib/stream/polling-policy.ts
12348
13017
  var DEFAULT_WATCH_POLLING = {
@@ -12387,7 +13056,7 @@ function nextAdaptivePollingDelay(state) {
12387
13056
  state.config.minMs,
12388
13057
  state.config.maxMs
12389
13058
  );
12390
- const delay3 = applyJitter(
13059
+ const delay4 = applyJitter(
12391
13060
  current,
12392
13061
  state.config.jitterRatio,
12393
13062
  state.config.minMs,
@@ -12398,7 +13067,7 @@ function nextAdaptivePollingDelay(state) {
12398
13067
  state.config.minMs,
12399
13068
  state.config.maxMs
12400
13069
  );
12401
- return delay3;
13070
+ return delay4;
12402
13071
  }
12403
13072
  function normalizeAdaptivePolling(polling, fallback) {
12404
13073
  const merged = {
@@ -12498,7 +13167,7 @@ function isTerminal(status) {
12498
13167
  return status !== "queued" && status !== "running";
12499
13168
  }
12500
13169
  function waitForDelay(ms, signal) {
12501
- return delay2(ms, true, { signal }).catch((err) => {
13170
+ return delay3(ms, true, { signal }).catch((err) => {
12502
13171
  if (signal.aborted) return false;
12503
13172
  throw err;
12504
13173
  });
@@ -14201,6 +14870,7 @@ export {
14201
14870
  preference,
14202
14871
  principle,
14203
14872
  quirk,
14873
+ readFileContent,
14204
14874
  reasoningFramework,
14205
14875
  reminder,
14206
14876
  render,
@@ -14217,6 +14887,7 @@ export {
14217
14887
  shellQuote,
14218
14888
  skills,
14219
14889
  skillsReminder,
14890
+ socraticPlan,
14220
14891
  socraticPrompting,
14221
14892
  soul,
14222
14893
  stop,