@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.
- package/README.md +26 -1
- package/dist/browser.js +9 -2
- package/dist/browser.js.map +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1187 -516
- package/dist/index.js.map +4 -4
- package/dist/lib/agent.d.ts +78 -37
- package/dist/lib/agent.d.ts.map +1 -1
- package/dist/lib/chat.d.ts +16 -17
- package/dist/lib/chat.d.ts.map +1 -1
- package/dist/lib/engine.d.ts.map +1 -1
- package/dist/lib/fragments/plan-shared.d.ts +46 -0
- package/dist/lib/fragments/plan-shared.d.ts.map +1 -0
- package/dist/lib/fragments/plan.d.ts.map +1 -1
- package/dist/lib/fragments/socratic-inquiry.d.ts +4 -0
- package/dist/lib/fragments/socratic-inquiry.d.ts.map +1 -0
- package/dist/lib/fragments/socratic-plan.d.ts +13 -0
- package/dist/lib/fragments/socratic-plan.d.ts.map +1 -0
- package/dist/lib/fragments/socratic.d.ts +2 -5
- package/dist/lib/fragments/socratic.d.ts.map +1 -1
- package/dist/lib/sandbox/agent-os-sandbox.d.ts.map +1 -1
- package/dist/lib/sandbox/apple-container-sandbox.d.ts.map +1 -1
- package/dist/lib/sandbox/bash-tool.d.ts +1 -1
- package/dist/lib/sandbox/bash-tool.d.ts.map +1 -1
- package/dist/lib/sandbox/cli-process.d.ts +6 -0
- package/dist/lib/sandbox/cli-process.d.ts.map +1 -1
- package/dist/lib/sandbox/container-engine.d.ts +1 -0
- package/dist/lib/sandbox/container-engine.d.ts.map +1 -1
- package/dist/lib/sandbox/container-sandbox.d.ts.map +1 -1
- package/dist/lib/sandbox/daytona-sandbox.d.ts.map +1 -1
- package/dist/lib/sandbox/docker-sandbox.d.ts +7 -0
- package/dist/lib/sandbox/docker-sandbox.d.ts.map +1 -1
- package/dist/lib/sandbox/index.d.ts +1 -0
- package/dist/lib/sandbox/index.d.ts.map +1 -1
- package/dist/lib/sandbox/microsandbox-sandbox.d.ts +13 -17
- package/dist/lib/sandbox/microsandbox-sandbox.d.ts.map +1 -1
- package/dist/lib/sandbox/read-file-tool/binary.d.ts +13 -0
- package/dist/lib/sandbox/read-file-tool/binary.d.ts.map +1 -0
- package/dist/lib/sandbox/read-file-tool/format.d.ts +17 -0
- package/dist/lib/sandbox/read-file-tool/format.d.ts.map +1 -0
- package/dist/lib/sandbox/read-file-tool/image.d.ts +4 -0
- package/dist/lib/sandbox/read-file-tool/image.d.ts.map +1 -0
- package/dist/lib/sandbox/read-file-tool/index.d.ts +6 -0
- package/dist/lib/sandbox/read-file-tool/index.d.ts.map +1 -0
- package/dist/lib/sandbox/read-file-tool/ooxml.d.ts +4 -0
- package/dist/lib/sandbox/read-file-tool/ooxml.d.ts.map +1 -0
- package/dist/lib/sandbox/read-file-tool/pdf.d.ts +4 -0
- package/dist/lib/sandbox/read-file-tool/pdf.d.ts.map +1 -0
- package/dist/lib/sandbox/read-file-tool/text.d.ts +10 -0
- package/dist/lib/sandbox/read-file-tool/text.d.ts.map +1 -0
- package/dist/lib/sandbox/read-file.d.ts +8 -0
- package/dist/lib/sandbox/read-file.d.ts.map +1 -0
- package/dist/lib/sandbox/types.d.ts +19 -5
- package/dist/lib/sandbox/types.d.ts.map +1 -1
- package/dist/lib/sandbox/virtual-sandbox.d.ts.map +1 -1
- package/dist/lib/stream-buffer.d.ts +1 -1
- package/dist/lib/stream-buffer.d.ts.map +1 -1
- package/dist/lib/telemetry/console-telemetry.d.ts +0 -1
- package/dist/lib/telemetry/console-telemetry.d.ts.map +1 -1
- package/dist/lib/telemetry/file/file-telemetry.d.ts +5 -3
- package/dist/lib/telemetry/file/file-telemetry.d.ts.map +1 -1
- package/dist/lib/telemetry/file/index.js +16 -11
- package/dist/lib/telemetry/file/index.js.map +2 -2
- package/dist/lib/telemetry/index.js +3 -4
- package/dist/lib/telemetry/index.js.map +2 -2
- package/dist/lib/telemetry/safe-serialize.d.ts +2 -2
- package/dist/lib/telemetry/safe-serialize.d.ts.map +1 -1
- package/dist/lib/tracing/index.js +4 -4
- package/dist/lib/tracing/index.js.map +2 -2
- 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
|
|
323
|
+
const tool4 = tools[toolCall2.toolName];
|
|
324
324
|
const { output } = await generateText({
|
|
325
325
|
abortSignal,
|
|
326
326
|
model,
|
|
327
|
-
output: Output.object({ schema:
|
|
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(
|
|
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
|
-
|
|
353
|
-
|
|
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(
|
|
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:
|
|
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.#
|
|
383
|
-
|
|
384
|
-
|
|
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
|
-
|
|
388
|
-
|
|
389
|
+
toolsContext: options?.toolsContext,
|
|
390
|
+
experimental_toolCallers: this.#options.experimental_toolCallers,
|
|
389
391
|
repairToolCall: createRepairToolCall(
|
|
390
392
|
this.#options.model,
|
|
391
|
-
|
|
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(
|
|
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.#
|
|
421
|
-
|
|
422
|
-
|
|
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
|
-
|
|
426
|
-
|
|
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
|
-
|
|
435
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
471
|
-
toolsContext
|
|
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,
|
|
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
|
-
|
|
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
|
|
618
|
-
|
|
619
|
-
|
|
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 =
|
|
738
|
+
const tools = withHostOnlyToolMetadata({ ...options.tools });
|
|
725
739
|
return {
|
|
726
|
-
async generate(
|
|
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:
|
|
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
|
-
|
|
764
|
+
callOptions?.abortSignal
|
|
751
765
|
),
|
|
752
|
-
|
|
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(
|
|
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:
|
|
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
|
-
|
|
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:
|
|
786
|
-
|
|
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,
|
|
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 = [...
|
|
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,
|
|
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
|
-
|
|
3043
|
+
path6
|
|
3037
3044
|
);
|
|
3038
3045
|
}
|
|
3039
|
-
async #walkData(value, ctx, depth, ancestors,
|
|
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 '${
|
|
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 '${
|
|
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 '${
|
|
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,
|
|
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 = `${
|
|
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,
|
|
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,
|
|
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:
|
|
5158
|
+
return skills2.map(({ name, description, path: path6 }) => ({
|
|
5151
5159
|
name,
|
|
5152
5160
|
description,
|
|
5153
|
-
path:
|
|
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({
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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.
|
|
6110
|
-
|
|
6111
|
-
|
|
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
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
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
|
|
6183
|
-
uniqueIds(
|
|
6184
|
-
const
|
|
6185
|
-
const
|
|
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(
|
|
6170
|
+
throw new Error(`${label} "${item.id}" cannot block itself`);
|
|
6191
6171
|
}
|
|
6192
|
-
if (!
|
|
6172
|
+
if (!byId.has(blocker)) {
|
|
6193
6173
|
throw new Error(
|
|
6194
|
-
|
|
6174
|
+
`${label} "${item.id}" references missing blocker "${blocker}"`
|
|
6195
6175
|
);
|
|
6196
6176
|
}
|
|
6197
6177
|
if (blockers.has(blocker)) {
|
|
6198
|
-
throw new Error(
|
|
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
|
|
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
|
|
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
|
|
6249
|
-
|
|
6250
|
-
|
|
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
|
|
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
|
-
|
|
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(
|
|
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
|
|
6369
|
-
|
|
6370
|
-
|
|
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/
|
|
6562
|
-
import
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
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
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
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
|
-
|
|
6580
|
-
|
|
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
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
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(
|
|
7113
|
+
async readFile(path6, options2) {
|
|
6829
7114
|
try {
|
|
6830
|
-
const bytes = await os.readFile(
|
|
6831
|
-
return
|
|
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 "${
|
|
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(
|
|
7312
|
+
function base64WriteCommands(path6, content) {
|
|
7025
7313
|
const base64 = Buffer.from(content).toString("base64");
|
|
7026
|
-
const quotedPath = shellQuote(
|
|
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(
|
|
7041
|
-
return `base64 ${shellQuote(
|
|
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
|
|
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 (
|
|
7635
|
-
const result = await sandbox.executeCommand(base64ReadCommand(
|
|
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 "${
|
|
7928
|
+
throw new Error(`Failed to read file "${path6}": ${result.stderr}`);
|
|
7638
7929
|
}
|
|
7639
|
-
return Buffer.from(result.stdout, "base64")
|
|
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
|
|
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
|
|
8042
|
-
import
|
|
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 =
|
|
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:
|
|
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:
|
|
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
|
|
8703
|
+
const formats2 = new Set(
|
|
8216
8704
|
filenames.map(
|
|
8217
|
-
(filename) => EXTENSION_FORMATS[
|
|
8705
|
+
(filename) => EXTENSION_FORMATS[path4.extname(filename).toLowerCase()]
|
|
8218
8706
|
).filter((format) => format !== void 0)
|
|
8219
8707
|
);
|
|
8220
|
-
for (const format of
|
|
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 =
|
|
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:
|
|
8289
|
-
command:
|
|
8290
|
-
reasoning:
|
|
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 =
|
|
8331
|
-
|
|
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:
|
|
8344
|
-
path:
|
|
8345
|
-
content:
|
|
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:
|
|
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
|
|
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 =
|
|
8393
|
-
const hasPathSep = 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
|
|
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 =
|
|
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(
|
|
9180
|
+
async readFile(path6, options) {
|
|
8702
9181
|
try {
|
|
8703
|
-
const bytes = await sandbox.fs.downloadFile(
|
|
8704
|
-
return
|
|
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 "${
|
|
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
|
|
8947
|
-
const index =
|
|
9437
|
+
for (const path6 of paths) {
|
|
9438
|
+
const index = path6.lastIndexOf("/");
|
|
8948
9439
|
if (index > 0) {
|
|
8949
|
-
dirs.add(
|
|
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
|
-
|
|
9300
|
-
|
|
9301
|
-
|
|
9302
|
-
|
|
9303
|
-
|
|
9304
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
9512
|
-
return include.some((glob) => posix2.matchesGlob(
|
|
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 = (
|
|
9521
|
-
state.set(
|
|
10189
|
+
const write = (path6) => {
|
|
10190
|
+
state.set(path6, "write");
|
|
9522
10191
|
};
|
|
9523
|
-
const remove = (
|
|
9524
|
-
if (state.get(
|
|
9525
|
-
else state.set(
|
|
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 = (
|
|
9573
|
-
if (traceFile &&
|
|
9574
|
-
if (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(
|
|
9578
|
-
return matchesGlobs(
|
|
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 [
|
|
9590
|
-
if (emittedRenameTargets.has(
|
|
9591
|
-
if (keep(
|
|
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
|
|
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}/${
|
|
9649
|
-
const sentinel = `__dat_trace_${
|
|
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:
|
|
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((
|
|
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}/${
|
|
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
|
|
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-${
|
|
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
|
-
|
|
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.
|
|
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,
|
|
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,
|
|
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(
|
|
10857
|
+
async readFile(path6, options) {
|
|
10213
10858
|
try {
|
|
10214
|
-
|
|
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 "${
|
|
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
|
-
|
|
10225
|
-
|
|
10226
|
-
|
|
10227
|
-
|
|
10228
|
-
|
|
10229
|
-
|
|
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
|
-
|
|
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,
|
|
10909
|
+
function spawnMicrosandboxProcess(sdk, run, command, options) {
|
|
10255
10910
|
const stdout = createByteReadable();
|
|
10256
10911
|
const stderr = createByteReadable();
|
|
10257
|
-
const exit = pumpExecStream({ sdk,
|
|
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,
|
|
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
|
|
10280
|
-
|
|
10281
|
-
|
|
10282
|
-
|
|
10283
|
-
|
|
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
|
|
10384
|
-
const index =
|
|
11040
|
+
for (const path6 of paths) {
|
|
11041
|
+
const index = path6.lastIndexOf("/");
|
|
10385
11042
|
if (index > 0) {
|
|
10386
|
-
dirs.add(
|
|
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
|
|
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(
|
|
10492
|
-
return
|
|
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
|
|
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
|
|
11183
|
+
import dedent4 from "dedent";
|
|
10522
11184
|
function skills(availableSkills) {
|
|
10523
|
-
const skillSnapshot = availableSkills.map(({ name, description, path:
|
|
11185
|
+
const skillSnapshot = availableSkills.map(({ name, description, path: path6 }) => ({
|
|
10524
11186
|
name,
|
|
10525
11187
|
description,
|
|
10526
|
-
path:
|
|
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 =
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|