@deepagents/context 6.1.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -3
- 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 +1411 -514
- 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 +17 -18
- 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 +1 -1
- package/dist/lib/sandbox/agent-os-sandbox.d.ts.map +1 -1
- package/dist/lib/sandbox/apple-container-sandbox.d.ts +3 -3
- 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 +1 -0
- 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 +10 -3
- 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/shell-quote.d.ts +1 -1
- package/dist/lib/sandbox/strace/index.d.ts +3 -3
- package/dist/lib/sandbox/strace/index.d.ts.map +1 -1
- package/dist/lib/sandbox/strace/index.js +2 -2
- package/dist/lib/sandbox/strace/index.js.map +2 -2
- package/dist/lib/sandbox/types.d.ts +24 -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;
|
|
@@ -840,6 +847,7 @@ import { getErrorMessage } from "@ai-sdk/provider";
|
|
|
840
847
|
import {
|
|
841
848
|
convertToModelMessages as convertToModelMessages2,
|
|
842
849
|
generateId as generateId5,
|
|
850
|
+
isStaticToolUIPart as isStaticToolUIPart2,
|
|
843
851
|
validateUIMessages
|
|
844
852
|
} from "ai";
|
|
845
853
|
|
|
@@ -1436,13 +1444,13 @@ var TomlRenderer = class extends ContextRenderer {
|
|
|
1436
1444
|
const entries = this.#renderObjectEntries(obj, newPath);
|
|
1437
1445
|
return ["", `[${newPath.join(".")}]`, ...entries].join("\n");
|
|
1438
1446
|
}
|
|
1439
|
-
#renderObjectEntries(obj,
|
|
1447
|
+
#renderObjectEntries(obj, path6) {
|
|
1440
1448
|
return Object.entries(obj).map(([key, value]) => {
|
|
1441
1449
|
if (value == null) {
|
|
1442
1450
|
return "";
|
|
1443
1451
|
}
|
|
1444
1452
|
if (isFragmentObject(value)) {
|
|
1445
|
-
const newPath = [...
|
|
1453
|
+
const newPath = [...path6, key];
|
|
1446
1454
|
const entries = this.#renderObjectEntries(value, newPath);
|
|
1447
1455
|
return ["", `[${newPath.join(".")}]`, ...entries].join("\n");
|
|
1448
1456
|
}
|
|
@@ -3025,30 +3033,30 @@ var FragmentLoaderResolver = class {
|
|
|
3025
3033
|
fragments.map((f) => this.#walkFragment(f, ctx, 0, /* @__PURE__ */ new Set(), f.name))
|
|
3026
3034
|
);
|
|
3027
3035
|
}
|
|
3028
|
-
async #walkFragment(fragment2, ctx, depth, ancestors,
|
|
3036
|
+
async #walkFragment(fragment2, ctx, depth, ancestors, path6) {
|
|
3029
3037
|
if (!("data" in fragment2)) return;
|
|
3030
3038
|
fragment2.data = await this.#walkData(
|
|
3031
3039
|
fragment2.data,
|
|
3032
3040
|
ctx,
|
|
3033
3041
|
depth,
|
|
3034
3042
|
ancestors,
|
|
3035
|
-
|
|
3043
|
+
path6
|
|
3036
3044
|
);
|
|
3037
3045
|
}
|
|
3038
|
-
async #walkData(value, ctx, depth, ancestors,
|
|
3046
|
+
async #walkData(value, ctx, depth, ancestors, path6) {
|
|
3039
3047
|
if (ctx.signal?.aborted) {
|
|
3040
3048
|
throw ctx.signal.reason ?? new Error("Resolver aborted");
|
|
3041
3049
|
}
|
|
3042
3050
|
if (depth > this.#maxDepth) {
|
|
3043
3051
|
throw new Error(
|
|
3044
|
-
`Resolver recursion exceeded maxDepth=${this.#maxDepth} at fragment '${
|
|
3052
|
+
`Resolver recursion exceeded maxDepth=${this.#maxDepth} at fragment '${path6}'`
|
|
3045
3053
|
);
|
|
3046
3054
|
}
|
|
3047
3055
|
const handler = this.#resolvers.find((r) => r.canResolve(value));
|
|
3048
3056
|
if (handler) {
|
|
3049
3057
|
if (handler.requiresSandbox && !ctx.sandbox) {
|
|
3050
3058
|
throw new Error(
|
|
3051
|
-
`Fragment '${
|
|
3059
|
+
`Fragment '${path6}' is dispatched to ${handler.name}, which requires a sandbox; none was provided`
|
|
3052
3060
|
);
|
|
3053
3061
|
}
|
|
3054
3062
|
let resolved;
|
|
@@ -3059,11 +3067,11 @@ var FragmentLoaderResolver = class {
|
|
|
3059
3067
|
);
|
|
3060
3068
|
} catch (cause) {
|
|
3061
3069
|
throw new Error(
|
|
3062
|
-
`Async fragment '${
|
|
3070
|
+
`Async fragment '${path6}' failed in ${handler.name}: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
3063
3071
|
{ cause }
|
|
3064
3072
|
);
|
|
3065
3073
|
}
|
|
3066
|
-
return this.#walkData(resolved, ctx, depth + 1, ancestors,
|
|
3074
|
+
return this.#walkData(resolved, ctx, depth + 1, ancestors, path6);
|
|
3067
3075
|
}
|
|
3068
3076
|
if (value === null || value === void 0) {
|
|
3069
3077
|
return value;
|
|
@@ -3072,7 +3080,7 @@ var FragmentLoaderResolver = class {
|
|
|
3072
3080
|
if (ancestors.has(value)) return void 0;
|
|
3073
3081
|
const childAncestors = new Set(ancestors);
|
|
3074
3082
|
childAncestors.add(value);
|
|
3075
|
-
const childPath = `${
|
|
3083
|
+
const childPath = `${path6}.${value.name}`;
|
|
3076
3084
|
await this.#walkFragment(
|
|
3077
3085
|
value,
|
|
3078
3086
|
ctx,
|
|
@@ -3088,7 +3096,7 @@ var FragmentLoaderResolver = class {
|
|
|
3088
3096
|
childAncestors.add(value);
|
|
3089
3097
|
return Promise.all(
|
|
3090
3098
|
value.map(
|
|
3091
|
-
(item) => this.#walkData(item, ctx, depth, childAncestors,
|
|
3099
|
+
(item) => this.#walkData(item, ctx, depth, childAncestors, path6)
|
|
3092
3100
|
)
|
|
3093
3101
|
);
|
|
3094
3102
|
}
|
|
@@ -3100,7 +3108,7 @@ var FragmentLoaderResolver = class {
|
|
|
3100
3108
|
const entries = Object.entries(value);
|
|
3101
3109
|
const resolvedValues = await Promise.all(
|
|
3102
3110
|
entries.map(
|
|
3103
|
-
([, v]) => this.#walkData(v, ctx, depth, childAncestors,
|
|
3111
|
+
([, v]) => this.#walkData(v, ctx, depth, childAncestors, path6)
|
|
3104
3112
|
)
|
|
3105
3113
|
);
|
|
3106
3114
|
for (let i = 0; i < entries.length; i += 1) {
|
|
@@ -3959,6 +3967,58 @@ function toolOutcomesFromStep(content) {
|
|
|
3959
3967
|
}
|
|
3960
3968
|
return outcomes;
|
|
3961
3969
|
}
|
|
3970
|
+
function terminalToolPartsFromStep(content) {
|
|
3971
|
+
return content.flatMap((part) => {
|
|
3972
|
+
if (part.type === "tool-result" && part.dynamic !== true) {
|
|
3973
|
+
return [
|
|
3974
|
+
{
|
|
3975
|
+
type: `tool-${part.toolName}`,
|
|
3976
|
+
toolCallId: part.toolCallId,
|
|
3977
|
+
state: "output-available",
|
|
3978
|
+
input: part.input,
|
|
3979
|
+
output: part.output === void 0 ? null : part.output
|
|
3980
|
+
}
|
|
3981
|
+
];
|
|
3982
|
+
}
|
|
3983
|
+
if (part.type === "tool-error" && part.dynamic !== true) {
|
|
3984
|
+
return [
|
|
3985
|
+
{
|
|
3986
|
+
type: `tool-${part.toolName}`,
|
|
3987
|
+
toolCallId: part.toolCallId,
|
|
3988
|
+
state: "output-error",
|
|
3989
|
+
input: part.input,
|
|
3990
|
+
errorText: getErrorMessage(part.error)
|
|
3991
|
+
}
|
|
3992
|
+
];
|
|
3993
|
+
}
|
|
3994
|
+
if (part.type === "tool-approval-response" && part.approved === false && part.toolCall.dynamic !== true) {
|
|
3995
|
+
return [
|
|
3996
|
+
{
|
|
3997
|
+
type: `tool-${part.toolCall.toolName}`,
|
|
3998
|
+
toolCallId: part.toolCall.toolCallId,
|
|
3999
|
+
state: "output-denied",
|
|
4000
|
+
input: part.toolCall.input,
|
|
4001
|
+
approval: {
|
|
4002
|
+
id: part.approvalId,
|
|
4003
|
+
approved: false,
|
|
4004
|
+
...part.reason === void 0 ? {} : { reason: part.reason }
|
|
4005
|
+
}
|
|
4006
|
+
}
|
|
4007
|
+
];
|
|
4008
|
+
}
|
|
4009
|
+
return [];
|
|
4010
|
+
});
|
|
4011
|
+
}
|
|
4012
|
+
function mergeTerminalToolParts(message2, liveParts) {
|
|
4013
|
+
const remaining = new Map(liveParts);
|
|
4014
|
+
const parts = message2.parts.map((part) => {
|
|
4015
|
+
if (!isStaticToolUIPart2(part)) return part;
|
|
4016
|
+
remaining.delete(part.toolCallId);
|
|
4017
|
+
return part;
|
|
4018
|
+
});
|
|
4019
|
+
parts.push(...remaining.values());
|
|
4020
|
+
return { ...message2, parts };
|
|
4021
|
+
}
|
|
3962
4022
|
var ContextEngine = class _ContextEngine {
|
|
3963
4023
|
/** Non-message fragments (role, hints, etc.) - not persisted in graph */
|
|
3964
4024
|
#fragments = [];
|
|
@@ -4037,6 +4097,7 @@ var ContextEngine = class _ContextEngine {
|
|
|
4037
4097
|
);
|
|
4038
4098
|
}
|
|
4039
4099
|
this.#branch = branch;
|
|
4100
|
+
this.#branchName = branch.name;
|
|
4040
4101
|
this.#initialized = true;
|
|
4041
4102
|
}
|
|
4042
4103
|
/**
|
|
@@ -4371,12 +4432,24 @@ var ContextEngine = class _ContextEngine {
|
|
|
4371
4432
|
const enableSteer = options.steer ?? true;
|
|
4372
4433
|
const session = {
|
|
4373
4434
|
firedOnceIds: /* @__PURE__ */ new Set(),
|
|
4374
|
-
|
|
4435
|
+
terminalToolParts: /* @__PURE__ */ new Map(),
|
|
4436
|
+
segments: [],
|
|
4437
|
+
initialStepStarts: null,
|
|
4438
|
+
stepOffset: 0,
|
|
4439
|
+
lastStepNumber: null
|
|
4375
4440
|
};
|
|
4376
4441
|
this.#currentReminderSession = session;
|
|
4377
4442
|
return async ({ steps, stepNumber, messages }) => {
|
|
4443
|
+
if (stepNumber === 0 && session.lastStepNumber !== null) {
|
|
4444
|
+
session.stepOffset += session.lastStepNumber + 1;
|
|
4445
|
+
}
|
|
4446
|
+
session.lastStepNumber = stepNumber;
|
|
4447
|
+
const completedSteps = session.stepOffset + stepNumber;
|
|
4378
4448
|
const priorStep = stepNumber >= 1 ? steps[stepNumber - 1] : void 0;
|
|
4379
4449
|
const hasSafeBoundary = (priorStep?.content?.length ?? 0) > 0;
|
|
4450
|
+
for (const part of terminalToolPartsFromStep(priorStep?.content ?? [])) {
|
|
4451
|
+
session.terminalToolParts.set(part.toolCallId, part);
|
|
4452
|
+
}
|
|
4380
4453
|
const canFire = enableSteer && hasSafeBoundary;
|
|
4381
4454
|
const steerConfigs = canFire ? this.#remindersFor("steer") : [];
|
|
4382
4455
|
const outcomes = toolOutcomesFromStep(steps.at(-1)?.content ?? []);
|
|
@@ -4435,18 +4508,24 @@ var ContextEngine = class _ContextEngine {
|
|
|
4435
4508
|
"createPrepareStep: expected an assistant message at chain head."
|
|
4436
4509
|
);
|
|
4437
4510
|
}
|
|
4438
|
-
const currentSegmentId =
|
|
4511
|
+
const currentSegmentId = head.id;
|
|
4439
4512
|
const currentSegment = await this.#store.getMessage(currentSegmentId);
|
|
4440
4513
|
if (!currentSegment || currentSegment.name !== "assistant") {
|
|
4441
4514
|
throw new Error(
|
|
4442
4515
|
"createPrepareStep: expected the open assistant segment to be persisted."
|
|
4443
4516
|
);
|
|
4444
4517
|
}
|
|
4445
|
-
|
|
4518
|
+
requireUIMessage(
|
|
4446
4519
|
currentSegment.data,
|
|
4447
4520
|
`Stored assistant message "${currentSegment.id}"`
|
|
4448
4521
|
);
|
|
4449
4522
|
const nextSegmentId = generateId5();
|
|
4523
|
+
if (session.segments.length === 0) {
|
|
4524
|
+
session.segments.push({
|
|
4525
|
+
id: currentSegmentId,
|
|
4526
|
+
startStep: 0
|
|
4527
|
+
});
|
|
4528
|
+
}
|
|
4450
4529
|
for (const input of inputs) this.set(user(input));
|
|
4451
4530
|
this.set(
|
|
4452
4531
|
assistant({
|
|
@@ -4456,8 +4535,11 @@ var ContextEngine = class _ContextEngine {
|
|
|
4456
4535
|
})
|
|
4457
4536
|
);
|
|
4458
4537
|
await this.save({ branch: false });
|
|
4459
|
-
session.
|
|
4460
|
-
|
|
4538
|
+
session.segments.push({
|
|
4539
|
+
id: nextSegmentId,
|
|
4540
|
+
startStep: completedSteps
|
|
4541
|
+
});
|
|
4542
|
+
session.terminalToolParts.clear();
|
|
4461
4543
|
return {
|
|
4462
4544
|
messages: [...messages, ...inputModel]
|
|
4463
4545
|
};
|
|
@@ -4482,14 +4564,81 @@ var ContextEngine = class _ContextEngine {
|
|
|
4482
4564
|
);
|
|
4483
4565
|
}
|
|
4484
4566
|
const session = this.#currentReminderSession;
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4567
|
+
let id = head.id;
|
|
4568
|
+
let parts = message2.parts;
|
|
4569
|
+
if (session) {
|
|
4570
|
+
if (session.initialStepStarts === null) {
|
|
4571
|
+
const initialId = session.segments[0]?.id ?? head.id;
|
|
4572
|
+
const initial = await this.#store.getMessage(initialId);
|
|
4573
|
+
if (!initial || initial.name !== "assistant") {
|
|
4574
|
+
throw new Error(
|
|
4575
|
+
`writeAssistantSegment: initial segment "${initialId}" was not found`
|
|
4576
|
+
);
|
|
4577
|
+
}
|
|
4578
|
+
session.initialStepStarts = requireUIMessage(
|
|
4579
|
+
initial.data,
|
|
4580
|
+
`Stored assistant message "${initial.id}"`
|
|
4581
|
+
).parts.filter((part) => part.type === "step-start").length;
|
|
4582
|
+
}
|
|
4583
|
+
const streamStepStarts = message2.parts.flatMap(
|
|
4584
|
+
(part, index) => part.type === "step-start" ? [index] : []
|
|
4585
|
+
);
|
|
4586
|
+
const completedSteps = streamStepStarts.length - session.initialStepStarts;
|
|
4587
|
+
if (completedSteps < 0) {
|
|
4588
|
+
throw new Error(
|
|
4589
|
+
"writeAssistantSegment: response lost an existing step boundary"
|
|
4590
|
+
);
|
|
4591
|
+
}
|
|
4592
|
+
const segmentIndex = session.segments.findLastIndex(
|
|
4593
|
+
(candidate) => candidate.startStep < completedSteps
|
|
4594
|
+
);
|
|
4595
|
+
if (segmentIndex >= 0) {
|
|
4596
|
+
const segment = session.segments[segmentIndex];
|
|
4597
|
+
id = segment.id;
|
|
4598
|
+
if (segmentIndex > 0) {
|
|
4599
|
+
const start = streamStepStarts[session.initialStepStarts + segment.startStep];
|
|
4600
|
+
if (start === void 0) {
|
|
4601
|
+
throw new Error(
|
|
4602
|
+
`writeAssistantSegment: response is missing step ${segment.startStep + 1}`
|
|
4603
|
+
);
|
|
4604
|
+
}
|
|
4605
|
+
parts = message2.parts.slice(start);
|
|
4606
|
+
}
|
|
4607
|
+
} else {
|
|
4608
|
+
const lastSegment = session.segments.at(-1);
|
|
4609
|
+
if (lastSegment) {
|
|
4610
|
+
id = lastSegment.id;
|
|
4611
|
+
parts = [];
|
|
4612
|
+
}
|
|
4613
|
+
}
|
|
4614
|
+
}
|
|
4615
|
+
const fragment2 = assistant({
|
|
4616
|
+
...message2,
|
|
4617
|
+
id,
|
|
4618
|
+
parts
|
|
4619
|
+
});
|
|
4620
|
+
if (!session) {
|
|
4621
|
+
this.set(fragment2);
|
|
4622
|
+
await this.save({ branch: false });
|
|
4623
|
+
return;
|
|
4624
|
+
}
|
|
4625
|
+
const stored = await this.#store.getMessage(id);
|
|
4626
|
+
if (!stored) {
|
|
4627
|
+
throw new Error(
|
|
4628
|
+
`writeAssistantSegment: reserved segment "${id}" was not found`
|
|
4629
|
+
);
|
|
4630
|
+
}
|
|
4631
|
+
if (!fragment2.codec) {
|
|
4632
|
+
throw new Error("writeAssistantSegment: assistant codec is missing");
|
|
4633
|
+
}
|
|
4634
|
+
await this.#store.addMessages([
|
|
4635
|
+
{
|
|
4636
|
+
...stored,
|
|
4637
|
+
name: fragment2.name,
|
|
4638
|
+
type: fragment2.type,
|
|
4639
|
+
data: fragment2.codec.encode()
|
|
4640
|
+
}
|
|
4641
|
+
]);
|
|
4493
4642
|
}
|
|
4494
4643
|
#remindersFor(target) {
|
|
4495
4644
|
return this.#fragments.filter(isConditionalReminder).map((fragment2) => fragment2.metadata.reminder).filter((config) => config.target === target);
|
|
@@ -4590,8 +4739,16 @@ var ContextEngine = class _ContextEngine {
|
|
|
4590
4739
|
"steer reminders require a user message earlier in the turn"
|
|
4591
4740
|
);
|
|
4592
4741
|
}
|
|
4742
|
+
const lastAssistantMessage = chain.lastAssistantMessage;
|
|
4743
|
+
if (!lastAssistantMessage) {
|
|
4744
|
+
throw new Error("steer reminders require a completed assistant step");
|
|
4745
|
+
}
|
|
4593
4746
|
return {
|
|
4594
4747
|
...this.#buildWhenCtx(chain, currentMessage),
|
|
4748
|
+
lastAssistantMessage: mergeTerminalToolParts(
|
|
4749
|
+
lastAssistantMessage,
|
|
4750
|
+
session.terminalToolParts
|
|
4751
|
+
),
|
|
4595
4752
|
sandbox,
|
|
4596
4753
|
firedOnceIds: /* @__PURE__ */ new Set([...chain.firedOnceIds, ...session.firedOnceIds])
|
|
4597
4754
|
};
|
|
@@ -4998,10 +5155,10 @@ var ContextEngine = class _ContextEngine {
|
|
|
4998
5155
|
for (const fragment2 of this.#fragments) {
|
|
4999
5156
|
const skills2 = fragment2.metadata?.skills;
|
|
5000
5157
|
if (fragment2.name === "available_skills" && Array.isArray(skills2) && skills2.every(isAvailableSkill)) {
|
|
5001
|
-
return skills2.map(({ name, description, path:
|
|
5158
|
+
return skills2.map(({ name, description, path: path6 }) => ({
|
|
5002
5159
|
name,
|
|
5003
5160
|
description,
|
|
5004
|
-
path:
|
|
5161
|
+
path: path6
|
|
5005
5162
|
}));
|
|
5006
5163
|
}
|
|
5007
5164
|
}
|
|
@@ -5489,7 +5646,7 @@ var TitleGenerator = class {
|
|
|
5489
5646
|
context,
|
|
5490
5647
|
model: options.model,
|
|
5491
5648
|
schema: titleSchema
|
|
5492
|
-
}).generate({
|
|
5649
|
+
}).generate({ abortSignal: options.abortSignal });
|
|
5493
5650
|
if (!title) {
|
|
5494
5651
|
throw new Error("Title generation returned an empty string.");
|
|
5495
5652
|
}
|
|
@@ -5524,7 +5681,8 @@ var defaultChatMessageMetadata = ({
|
|
|
5524
5681
|
}
|
|
5525
5682
|
return void 0;
|
|
5526
5683
|
};
|
|
5527
|
-
async function chat(agent2,
|
|
5684
|
+
async function chat(agent2, ...args) {
|
|
5685
|
+
const [options] = args;
|
|
5528
5686
|
const context = agent2.context;
|
|
5529
5687
|
if (!context) {
|
|
5530
5688
|
throw new Error(
|
|
@@ -5539,32 +5697,11 @@ async function chat(agent2, options = {}) {
|
|
|
5539
5697
|
}
|
|
5540
5698
|
const initialAssistantMsgId = head.id;
|
|
5541
5699
|
const uiMessages = await context.getMessages();
|
|
5542
|
-
|
|
5543
|
-
const [title, result] = await Promise.all([
|
|
5544
|
-
makeTitle({
|
|
5545
|
-
context,
|
|
5546
|
-
model: agent2.model,
|
|
5547
|
-
generateTitle: options.generateTitle,
|
|
5548
|
-
abortSignal: options.abortSignal
|
|
5549
|
-
}),
|
|
5550
|
-
agent2.stream(streamContextVariables, {
|
|
5551
|
-
transform: options.transform,
|
|
5552
|
-
abortSignal: options.abortSignal
|
|
5553
|
-
})
|
|
5554
|
-
]);
|
|
5555
|
-
const uiStream = result.toUIMessageStream({
|
|
5556
|
-
onError: options.onError ?? formatChatError,
|
|
5557
|
-
sendStart: true,
|
|
5558
|
-
sendFinish: true,
|
|
5559
|
-
sendReasoning: true,
|
|
5560
|
-
sendSources: true,
|
|
5561
|
-
originalMessages: uiMessages,
|
|
5562
|
-
generateMessageId: () => initialAssistantMsgId,
|
|
5563
|
-
messageMetadata: options.messageMetadata ?? defaultChatMessageMetadata
|
|
5564
|
-
});
|
|
5700
|
+
let result;
|
|
5565
5701
|
return createUIMessageStream2({
|
|
5566
5702
|
originalMessages: uiMessages,
|
|
5567
5703
|
generateId: () => initialAssistantMsgId,
|
|
5704
|
+
onError: options?.onError ?? formatChatError,
|
|
5568
5705
|
onStepEnd: async ({ responseMessage }) => {
|
|
5569
5706
|
await context.writeAssistantSegment(responseMessage);
|
|
5570
5707
|
},
|
|
@@ -5573,7 +5710,7 @@ async function chat(agent2, options = {}) {
|
|
|
5573
5710
|
if (isAborted) {
|
|
5574
5711
|
message2 = { ...message2, parts: sanitizeAbortedParts(message2.parts) };
|
|
5575
5712
|
}
|
|
5576
|
-
const finalMetadata = await options
|
|
5713
|
+
const finalMetadata = await options?.finalAssistantMetadata?.(message2);
|
|
5577
5714
|
const mergedMetadata = {
|
|
5578
5715
|
...message2.metadata ?? {},
|
|
5579
5716
|
...finalMetadata ?? {}
|
|
@@ -5582,13 +5719,35 @@ async function chat(agent2, options = {}) {
|
|
|
5582
5719
|
message2 = { ...message2, metadata: mergedMetadata };
|
|
5583
5720
|
}
|
|
5584
5721
|
await context.writeAssistantSegment(message2);
|
|
5585
|
-
const usage = await result
|
|
5722
|
+
const usage = await result?.usage;
|
|
5586
5723
|
if (usage !== void 0) {
|
|
5587
5724
|
await context.trackUsage(usage);
|
|
5588
5725
|
}
|
|
5589
5726
|
},
|
|
5590
5727
|
execute: async ({ writer }) => {
|
|
5591
|
-
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
|
+
);
|
|
5592
5751
|
if (title) {
|
|
5593
5752
|
writer.write({ type: "data-chat-title", data: title, transient: true });
|
|
5594
5753
|
}
|
|
@@ -5634,7 +5793,7 @@ function formatChatError(error) {
|
|
|
5634
5793
|
console.error("Upstream API call failed:", error);
|
|
5635
5794
|
return `Upstream API call failed with status ${error.statusCode}: ${error.message}`;
|
|
5636
5795
|
}
|
|
5637
|
-
return JSON.stringify(error);
|
|
5796
|
+
return error instanceof Error ? "An error occurred." : JSON.stringify(error);
|
|
5638
5797
|
}
|
|
5639
5798
|
async function makeTitle(options) {
|
|
5640
5799
|
const titler = new TitleGenerator({ context: options.context });
|
|
@@ -5930,10 +6089,14 @@ function fromFragment(fragment2, options) {
|
|
|
5930
6089
|
}
|
|
5931
6090
|
|
|
5932
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
|
|
5933
6096
|
import dedent from "dedent";
|
|
5934
6097
|
import z3 from "zod";
|
|
5935
6098
|
var PLAN_FILE_PATH = "/workspace/.deepagents/plan.json";
|
|
5936
|
-
var
|
|
6099
|
+
var PLAN_FOUNDATION_INSTRUCTIONS = dedent`
|
|
5937
6100
|
You own the plan. The user is not required to provide a structured objective, success criteria, constraints, or task breakdown.
|
|
5938
6101
|
|
|
5939
6102
|
Build and maintain the plan from the best available evidence, in this order:
|
|
@@ -5957,28 +6120,11 @@ var PLAN_INSTRUCTIONS = dedent`
|
|
|
5957
6120
|
|
|
5958
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.
|
|
5959
6122
|
|
|
5960
|
-
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.
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
- successCriteria: [{ id, text, basis, source, evidence: [{ summary, source }] }];
|
|
5964
|
-
- constraints and assumptions: [{ text, basis, source }];
|
|
5965
|
-
- tasks: [{ id, title, status, blockedBy, evidence }], where status is pending, in_progress, or completed;
|
|
5966
|
-
- lastReview, when present: { revision, decision, summary }.
|
|
5967
|
-
|
|
5968
|
-
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.
|
|
5969
|
-
|
|
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`
|
|
5970
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.
|
|
5971
|
-
|
|
5972
|
-
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.
|
|
5973
6127
|
`;
|
|
5974
|
-
var REVIEW_REMINDER = [
|
|
5975
|
-
"Re-read the current plan and consider the evidence gathered since the previous review.",
|
|
5976
|
-
"",
|
|
5977
|
-
"Is the current plan still valid given the latest evidence?",
|
|
5978
|
-
"",
|
|
5979
|
-
"If yes, continue. If not, revise it before taking another action.",
|
|
5980
|
-
"Before claiming completion, verify the success criteria against evidence."
|
|
5981
|
-
].join("\n");
|
|
5982
6128
|
var nonEmptyString = z3.string().trim().min(1);
|
|
5983
6129
|
var statementBasis = z3.enum([
|
|
5984
6130
|
"explicit_requirement",
|
|
@@ -5986,40 +6132,24 @@ var statementBasis = z3.enum([
|
|
|
5986
6132
|
"inference",
|
|
5987
6133
|
"assumption"
|
|
5988
6134
|
]);
|
|
5989
|
-
var statement = z3.strictObject({
|
|
5990
|
-
text: nonEmptyString,
|
|
5991
|
-
basis: statementBasis,
|
|
5992
|
-
source: nonEmptyString
|
|
5993
|
-
});
|
|
5994
6135
|
var evidence = z3.strictObject({
|
|
5995
6136
|
summary: nonEmptyString,
|
|
5996
6137
|
source: nonEmptyString
|
|
5997
6138
|
});
|
|
5998
|
-
var successCriterion = statement.extend({
|
|
5999
|
-
id: nonEmptyString,
|
|
6000
|
-
evidence: z3.array(evidence)
|
|
6001
|
-
});
|
|
6002
|
-
var task = z3.strictObject({
|
|
6003
|
-
id: nonEmptyString,
|
|
6004
|
-
title: nonEmptyString,
|
|
6005
|
-
status: z3.enum(["pending", "in_progress", "completed"]),
|
|
6006
|
-
blockedBy: z3.array(nonEmptyString),
|
|
6007
|
-
evidence: z3.array(evidence)
|
|
6008
|
-
});
|
|
6009
6139
|
var lastReview = z3.strictObject({
|
|
6010
6140
|
revision: z3.number().int().positive(),
|
|
6011
6141
|
decision: z3.enum(["continue", "revise", "replace", "complete", "blocked"]),
|
|
6012
6142
|
summary: nonEmptyString
|
|
6013
6143
|
});
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
}
|
|
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
|
+
}
|
|
6023
6153
|
function uniqueIds(items, label) {
|
|
6024
6154
|
const byId = /* @__PURE__ */ new Map();
|
|
6025
6155
|
for (const item of items) {
|
|
@@ -6030,23 +6160,22 @@ function uniqueIds(items, label) {
|
|
|
6030
6160
|
}
|
|
6031
6161
|
return byId;
|
|
6032
6162
|
}
|
|
6033
|
-
function
|
|
6034
|
-
uniqueIds(
|
|
6035
|
-
const
|
|
6036
|
-
const
|
|
6037
|
-
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) {
|
|
6038
6167
|
const blockers = /* @__PURE__ */ new Set();
|
|
6039
6168
|
for (const blocker of item.blockedBy) {
|
|
6040
6169
|
if (blocker === item.id) {
|
|
6041
|
-
throw new Error(
|
|
6170
|
+
throw new Error(`${label} "${item.id}" cannot block itself`);
|
|
6042
6171
|
}
|
|
6043
|
-
if (!
|
|
6172
|
+
if (!byId.has(blocker)) {
|
|
6044
6173
|
throw new Error(
|
|
6045
|
-
|
|
6174
|
+
`${label} "${item.id}" references missing blocker "${blocker}"`
|
|
6046
6175
|
);
|
|
6047
6176
|
}
|
|
6048
6177
|
if (blockers.has(blocker)) {
|
|
6049
|
-
throw new Error(
|
|
6178
|
+
throw new Error(`${label} "${item.id}" repeats blocker "${blocker}"`);
|
|
6050
6179
|
}
|
|
6051
6180
|
blockers.add(blocker);
|
|
6052
6181
|
blocks.get(blocker)?.push(item.id);
|
|
@@ -6065,12 +6194,112 @@ function validatePlan(state) {
|
|
|
6065
6194
|
}
|
|
6066
6195
|
visiting.add(id);
|
|
6067
6196
|
stack.push(id);
|
|
6068
|
-
for (const blocker of
|
|
6197
|
+
for (const blocker of byId.get(id)?.blockedBy ?? []) visit(blocker);
|
|
6069
6198
|
stack.pop();
|
|
6070
6199
|
visiting.delete(id);
|
|
6071
6200
|
visited.add(id);
|
|
6072
6201
|
};
|
|
6073
|
-
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");
|
|
6074
6303
|
for (const item of state.tasks) {
|
|
6075
6304
|
const incompleteBlockers = item.blockedBy.filter(
|
|
6076
6305
|
(id) => tasks.get(id)?.status !== "completed"
|
|
@@ -6096,37 +6325,19 @@ function validatePlan(state) {
|
|
|
6096
6325
|
}
|
|
6097
6326
|
return blocks;
|
|
6098
6327
|
}
|
|
6099
|
-
function
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
parsed = JSON.parse(raw);
|
|
6103
|
-
} catch (error) {
|
|
6104
|
-
const message2 = error instanceof Error ? error.message : String(error);
|
|
6105
|
-
throw new Error(`invalid JSON: ${message2}`, { cause: error });
|
|
6106
|
-
}
|
|
6107
|
-
const result = planState.safeParse(parsed);
|
|
6108
|
-
if (!result.success) {
|
|
6109
|
-
const issues = result.error.issues.slice(0, 5).map(({ path: path3, message: message2 }) => `${path3.join(".") || "plan"}: ${message2}`);
|
|
6110
|
-
const remaining = result.error.issues.length - issues.length;
|
|
6111
|
-
throw new Error(
|
|
6112
|
-
`schema errors: ${issues.join("; ")}${remaining > 0 ? `; ${remaining} more` : ""}`
|
|
6113
|
-
);
|
|
6114
|
-
}
|
|
6115
|
-
return { state: result.data, blocks: validatePlan(result.data) };
|
|
6116
|
-
}
|
|
6117
|
-
function lineItems(items) {
|
|
6118
|
-
return items.length > 0 ? items.map((item) => `- ${item}`) : ["- none"];
|
|
6119
|
-
}
|
|
6120
|
-
function latestEvidence(items) {
|
|
6121
|
-
const latest = items.at(-1);
|
|
6122
|
-
return latest ? ` Evidence: ${latest.summary} (${latest.source})` : "";
|
|
6328
|
+
function parseExecutionPlan(value) {
|
|
6329
|
+
const state = parsePlanSchema(executionPlanState, value);
|
|
6330
|
+
return { state, blocks: validateExecutionPlan(state) };
|
|
6123
6331
|
}
|
|
6124
6332
|
function formatTask(item, blocks) {
|
|
6125
6333
|
const blocked = blocks.get(item.id) ?? [];
|
|
6126
6334
|
const suffix = blocked.length > 0 ? `; blocks ${blocked.join(", ")}` : "";
|
|
6127
6335
|
return `${item.id}: ${item.title}${suffix}${latestEvidence(item.evidence)}`;
|
|
6128
6336
|
}
|
|
6129
|
-
function
|
|
6337
|
+
function formatExecutionPlanReview({
|
|
6338
|
+
state,
|
|
6339
|
+
blocks
|
|
6340
|
+
}) {
|
|
6130
6341
|
const tasks = new Map(state.tasks.map((item) => [item.id, item]));
|
|
6131
6342
|
const active = state.tasks.filter(({ status }) => status === "in_progress");
|
|
6132
6343
|
const ready = state.tasks.filter(
|
|
@@ -6181,45 +6392,23 @@ function formatPlanReview(state, blocks) {
|
|
|
6181
6392
|
`Last review: ${state.lastReview.decision} at revision ${state.lastReview.revision} \u2014 ${state.lastReview.summary}`
|
|
6182
6393
|
] : [],
|
|
6183
6394
|
"",
|
|
6184
|
-
|
|
6395
|
+
EXECUTION_REVIEW_REMINDER
|
|
6185
6396
|
].join("\n");
|
|
6186
6397
|
}
|
|
6187
|
-
function errorMessage(error) {
|
|
6188
|
-
const message2 = error instanceof Error ? error.message : String(error);
|
|
6189
|
-
return message2.length > 500 ? `${message2.slice(0, 497)}...` : message2;
|
|
6190
|
-
}
|
|
6191
|
-
async function resolvePlanReview(sandbox) {
|
|
6192
|
-
let raw;
|
|
6193
|
-
try {
|
|
6194
|
-
raw = await sandbox.sandbox.readFile(PLAN_FILE_PATH);
|
|
6195
|
-
} catch (error) {
|
|
6196
|
-
return [
|
|
6197
|
-
`The plan file at ${PLAN_FILE_PATH} could not be read: ${errorMessage(error)}`,
|
|
6198
|
-
"Create or repair it before taking another action.",
|
|
6199
|
-
"",
|
|
6200
|
-
REVIEW_REMINDER
|
|
6201
|
-
].join("\n");
|
|
6202
|
-
}
|
|
6203
|
-
try {
|
|
6204
|
-
const { state, blocks } = parsePlan(raw);
|
|
6205
|
-
return formatPlanReview(state, blocks);
|
|
6206
|
-
} catch (error) {
|
|
6207
|
-
return [
|
|
6208
|
-
`The plan file at ${PLAN_FILE_PATH} is invalid: ${errorMessage(error)}`,
|
|
6209
|
-
"Repair the plan before taking another action.",
|
|
6210
|
-
"",
|
|
6211
|
-
REVIEW_REMINDER
|
|
6212
|
-
].join("\n");
|
|
6213
|
-
}
|
|
6214
|
-
}
|
|
6215
6398
|
function instructions() {
|
|
6216
|
-
return fragment(
|
|
6399
|
+
return fragment(
|
|
6400
|
+
"plan_instructions",
|
|
6401
|
+
PLAN_FOUNDATION_INSTRUCTIONS,
|
|
6402
|
+
EXECUTION_PLAN_INSTRUCTIONS,
|
|
6403
|
+
PLAN_REVIEW_INSTRUCTIONS
|
|
6404
|
+
);
|
|
6217
6405
|
}
|
|
6218
6406
|
function review(options) {
|
|
6219
|
-
return
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
|
|
6407
|
+
return createPlanReview(
|
|
6408
|
+
options,
|
|
6409
|
+
(value) => formatExecutionPlanReview(parseExecutionPlan(value)),
|
|
6410
|
+
EXECUTION_REVIEW_REMINDER
|
|
6411
|
+
);
|
|
6223
6412
|
}
|
|
6224
6413
|
var plan = { path: PLAN_FILE_PATH, instructions, review };
|
|
6225
6414
|
|
|
@@ -6347,67 +6536,304 @@ function selfCritique(checks = DEFAULT_SELF_CRITIQUE_CHECKS) {
|
|
|
6347
6536
|
});
|
|
6348
6537
|
}
|
|
6349
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
|
+
|
|
6350
6597
|
// packages/context/src/lib/fragments/socratic.ts
|
|
6351
6598
|
function socraticPrompting() {
|
|
6352
|
-
return [
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6399
|
-
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6599
|
+
return [role(SOCRATIC_ROLE), socraticInquiry()];
|
|
6600
|
+
}
|
|
6601
|
+
|
|
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"
|
|
6677
|
+
);
|
|
6678
|
+
if (item.status !== "open" && incompleteBlockers.length > 0) {
|
|
6679
|
+
throw new Error(
|
|
6680
|
+
`inquiry "${item.id}" is ${item.status} while blocked by ${incompleteBlockers.join(", ")}`
|
|
6681
|
+
);
|
|
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:"`
|
|
6692
|
+
);
|
|
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
|
+
);
|
|
6410
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 };
|
|
6411
6837
|
|
|
6412
6838
|
// packages/context/src/lib/guardrails/error-recovery.guardrail.ts
|
|
6413
6839
|
import chalk3 from "chalk";
|
|
@@ -6519,7 +6945,15 @@ function render(tag, ...fragments) {
|
|
|
6519
6945
|
|
|
6520
6946
|
// packages/context/src/lib/sandbox/agent-os-sandbox.ts
|
|
6521
6947
|
import { PassThrough, Readable } from "node:stream";
|
|
6948
|
+
|
|
6949
|
+
// packages/context/src/lib/sandbox/read-file.ts
|
|
6522
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
|
|
6523
6957
|
var AgentOsSandboxError = class extends Error {
|
|
6524
6958
|
constructor(message2) {
|
|
6525
6959
|
super(message2);
|
|
@@ -6557,7 +6991,7 @@ function startKernelProcess(os, command, options) {
|
|
|
6557
6991
|
const stderr = new PassThrough();
|
|
6558
6992
|
const onStdout = (chunk) => stdout.write(chunk);
|
|
6559
6993
|
const onStderr = (chunk) => stderr.write(chunk);
|
|
6560
|
-
const { pid } = os.spawn("
|
|
6994
|
+
const { pid } = os.spawn("bash", ["-lc", command], {
|
|
6561
6995
|
...options,
|
|
6562
6996
|
onStdout,
|
|
6563
6997
|
onStderr
|
|
@@ -6610,6 +7044,25 @@ async function createAgentOsSandbox(options = {}) {
|
|
|
6610
7044
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
6611
7045
|
throw new AgentOsCreationError(err.message, err);
|
|
6612
7046
|
}
|
|
7047
|
+
try {
|
|
7048
|
+
const probe = startKernelProcess(os, ":", {});
|
|
7049
|
+
const [, stderr, exitCode] = await Promise.all([
|
|
7050
|
+
readAll(probe.stdout),
|
|
7051
|
+
readAll(probe.stderr),
|
|
7052
|
+
probe.exit
|
|
7053
|
+
]);
|
|
7054
|
+
if (exitCode !== 0) {
|
|
7055
|
+
throw new Error(stderr.trim() || `exit code ${exitCode}`);
|
|
7056
|
+
}
|
|
7057
|
+
} catch (error) {
|
|
7058
|
+
await os.dispose().catch(() => {
|
|
7059
|
+
});
|
|
7060
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
7061
|
+
throw new AgentOsCreationError(
|
|
7062
|
+
`Bash is required to execute sandbox commands but could not be started: ${err.message}`,
|
|
7063
|
+
err
|
|
7064
|
+
);
|
|
7065
|
+
}
|
|
6613
7066
|
const sandbox = {
|
|
6614
7067
|
async executeCommand(command, { signal } = {}) {
|
|
6615
7068
|
if (signal?.aborted) {
|
|
@@ -6657,16 +7110,19 @@ async function createAgentOsSandbox(options = {}) {
|
|
|
6657
7110
|
exit
|
|
6658
7111
|
};
|
|
6659
7112
|
},
|
|
6660
|
-
async readFile(
|
|
7113
|
+
async readFile(path6, options2) {
|
|
6661
7114
|
try {
|
|
6662
|
-
const bytes = await os.readFile(
|
|
6663
|
-
return
|
|
7115
|
+
const bytes = await os.readFile(path6);
|
|
7116
|
+
return readFileContent(bytes, options2);
|
|
6664
7117
|
} catch (error) {
|
|
6665
7118
|
throw new Error(
|
|
6666
|
-
`Failed to read file "${
|
|
7119
|
+
`Failed to read file "${path6}": ${error instanceof Error ? error.message : String(error)}`
|
|
6667
7120
|
);
|
|
6668
7121
|
}
|
|
6669
7122
|
},
|
|
7123
|
+
async exists(path6) {
|
|
7124
|
+
return os.exists(path6);
|
|
7125
|
+
},
|
|
6670
7126
|
async writeFiles(files) {
|
|
6671
7127
|
const results = await os.writeFiles(
|
|
6672
7128
|
files.map((f) => ({
|
|
@@ -6853,9 +7309,9 @@ function toSandboxProcess(child, abortSignal) {
|
|
|
6853
7309
|
};
|
|
6854
7310
|
}
|
|
6855
7311
|
var BASE64_WRITE_CHUNK = 32768;
|
|
6856
|
-
function base64WriteCommands(
|
|
7312
|
+
function base64WriteCommands(path6, content) {
|
|
6857
7313
|
const base64 = Buffer.from(content).toString("base64");
|
|
6858
|
-
const quotedPath = shellQuote(
|
|
7314
|
+
const quotedPath = shellQuote(path6);
|
|
6859
7315
|
const commands = [];
|
|
6860
7316
|
for (let offset = 0; offset < base64.length; offset += BASE64_WRITE_CHUNK) {
|
|
6861
7317
|
const chunk = base64.slice(offset, offset + BASE64_WRITE_CHUNK);
|
|
@@ -6869,8 +7325,11 @@ function base64WriteCommands(path3, content) {
|
|
|
6869
7325
|
}
|
|
6870
7326
|
return commands;
|
|
6871
7327
|
}
|
|
6872
|
-
function base64ReadCommand(
|
|
6873
|
-
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`;
|
|
6874
7333
|
}
|
|
6875
7334
|
|
|
6876
7335
|
// packages/context/src/lib/sandbox/installers/installer.ts
|
|
@@ -7143,6 +7602,7 @@ var ContainerSandboxStrategy = class {
|
|
|
7143
7602
|
await this.engine.ensureWorkdir(this.context.containerId, this.workdir);
|
|
7144
7603
|
await this.configure();
|
|
7145
7604
|
}
|
|
7605
|
+
await this.assertBashAvailable();
|
|
7146
7606
|
} catch (error) {
|
|
7147
7607
|
if (acquired && !acquired.attached) {
|
|
7148
7608
|
await this.stopContainer(acquired.containerId);
|
|
@@ -7375,7 +7835,7 @@ var ContainerSandboxStrategy = class {
|
|
|
7375
7835
|
const volumes = [...this.createdVolumes].reverse();
|
|
7376
7836
|
for (const volume of volumes) {
|
|
7377
7837
|
try {
|
|
7378
|
-
await
|
|
7838
|
+
await this.engine.removeVolume(volume);
|
|
7379
7839
|
this.createdVolumes.delete(volume);
|
|
7380
7840
|
} catch (error) {
|
|
7381
7841
|
const reason = this.engineErrorMessage(error);
|
|
@@ -7441,6 +7901,15 @@ var ContainerSandboxStrategy = class {
|
|
|
7441
7901
|
};
|
|
7442
7902
|
}
|
|
7443
7903
|
}
|
|
7904
|
+
async assertBashAvailable() {
|
|
7905
|
+
const result = await this.exec(":");
|
|
7906
|
+
if (result.exitCode === 0) return;
|
|
7907
|
+
const detail = result.stderr.trim() || result.stdout.trim();
|
|
7908
|
+
throw this.engine.errors.generic(
|
|
7909
|
+
`Bash is required to execute sandbox commands but could not be started${detail ? `: ${detail}` : "."}`,
|
|
7910
|
+
this.context.containerId
|
|
7911
|
+
);
|
|
7912
|
+
}
|
|
7444
7913
|
spawnProcess(command, options) {
|
|
7445
7914
|
const child = childSpawn(
|
|
7446
7915
|
this.engine.cli,
|
|
@@ -7453,12 +7922,18 @@ var ContainerSandboxStrategy = class {
|
|
|
7453
7922
|
const sandbox = {
|
|
7454
7923
|
executeCommand: async (command, options) => this.exec(command, options),
|
|
7455
7924
|
spawn: (command, options) => this.spawnProcess(command, options),
|
|
7456
|
-
readFile: async (
|
|
7457
|
-
const result = await sandbox.executeCommand(base64ReadCommand(
|
|
7925
|
+
readFile: async (path6, options) => {
|
|
7926
|
+
const result = await sandbox.executeCommand(base64ReadCommand(path6));
|
|
7458
7927
|
if (result.exitCode !== 0) {
|
|
7459
|
-
throw new Error(`Failed to read file "${
|
|
7928
|
+
throw new Error(`Failed to read file "${path6}": ${result.stderr}`);
|
|
7460
7929
|
}
|
|
7461
|
-
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}`);
|
|
7462
7937
|
},
|
|
7463
7938
|
writeFiles: async (files) => {
|
|
7464
7939
|
for (const file of files) {
|
|
@@ -7632,7 +8107,7 @@ var appleEngine = {
|
|
|
7632
8107
|
flags.push("--env", `${key}=${value}`);
|
|
7633
8108
|
}
|
|
7634
8109
|
}
|
|
7635
|
-
return ["exec", ...flags, containerId, "
|
|
8110
|
+
return ["exec", ...flags, containerId, "bash", "-lc", command];
|
|
7636
8111
|
},
|
|
7637
8112
|
inspectArgs(containerId) {
|
|
7638
8113
|
return ["inspect", containerId];
|
|
@@ -7645,6 +8120,9 @@ var appleEngine = {
|
|
|
7645
8120
|
volumeCreateArgs(volume) {
|
|
7646
8121
|
return ["volume", "create", volume.name];
|
|
7647
8122
|
},
|
|
8123
|
+
async removeVolume(name) {
|
|
8124
|
+
await spawn3(CLI, ["volume", "rm", name]);
|
|
8125
|
+
},
|
|
7648
8126
|
errorMessage: getCliErrorMessage,
|
|
7649
8127
|
isServiceDown(message2) {
|
|
7650
8128
|
return /apiserver|not running|connection refused|could not connect|xpc/i.test(
|
|
@@ -7661,9 +8139,15 @@ var appleEngine = {
|
|
|
7661
8139
|
return /already exists/i.test(message2);
|
|
7662
8140
|
},
|
|
7663
8141
|
async ensureWorkdir(containerId, workdir) {
|
|
7664
|
-
await spawn3(CLI, [
|
|
8142
|
+
await spawn3(CLI, [
|
|
8143
|
+
"exec",
|
|
8144
|
+
containerId,
|
|
8145
|
+
"bash",
|
|
8146
|
+
"-lc",
|
|
8147
|
+
`mkdir -p ${shellQuote(workdir)}`
|
|
8148
|
+
]);
|
|
7665
8149
|
},
|
|
7666
|
-
defaultImage: "docker.io/library/
|
|
8150
|
+
defaultImage: "docker.io/library/bash:5.3-alpine3.24",
|
|
7667
8151
|
createInstallerContext: createAppleInstallerContext,
|
|
7668
8152
|
async imageExists(tag) {
|
|
7669
8153
|
try {
|
|
@@ -7850,13 +8334,200 @@ var BashException = class extends Error {
|
|
|
7850
8334
|
};
|
|
7851
8335
|
|
|
7852
8336
|
// packages/context/src/lib/sandbox/bash-tool.ts
|
|
7853
|
-
import { tool as
|
|
8337
|
+
import { tool as tool3 } from "ai";
|
|
7854
8338
|
import fg from "fast-glob";
|
|
7855
8339
|
import { BashTransformPipeline, TeePlugin } from "just-bash";
|
|
7856
8340
|
import { readFile } from "node:fs/promises";
|
|
7857
|
-
import * as
|
|
7858
|
-
import
|
|
8341
|
+
import * as path4 from "node:path";
|
|
8342
|
+
import z7 from "zod";
|
|
7859
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
|
|
7860
8531
|
var DEFAULT_DESTINATION = "/workspace";
|
|
7861
8532
|
var DEFAULT_MAX_FILES = 1e3;
|
|
7862
8533
|
var DEFAULT_MAX_OUTPUT_LENGTH = 3e4;
|
|
@@ -7948,8 +8619,9 @@ function withBashExceptionCatch(sandbox) {
|
|
|
7948
8619
|
throw error;
|
|
7949
8620
|
}
|
|
7950
8621
|
},
|
|
7951
|
-
readFile: (filePath) => sandbox.readFile(filePath),
|
|
8622
|
+
readFile: (filePath, options) => sandbox.readFile(filePath, options),
|
|
7952
8623
|
writeFiles: (files) => sandbox.writeFiles(files),
|
|
8624
|
+
exists: (filePath) => sandbox.exists(filePath),
|
|
7953
8625
|
dispose: () => sandbox.dispose(),
|
|
7954
8626
|
[Symbol.asyncDispose]() {
|
|
7955
8627
|
return decorated.dispose();
|
|
@@ -7979,7 +8651,7 @@ async function listInitialFiles(options) {
|
|
|
7979
8651
|
const files = /* @__PURE__ */ new Map();
|
|
7980
8652
|
if (options.uploadDirectory) {
|
|
7981
8653
|
const { source, include = "**/*" } = options.uploadDirectory;
|
|
7982
|
-
const absoluteSource =
|
|
8654
|
+
const absoluteSource = path4.resolve(source);
|
|
7983
8655
|
const uploaded = await fg(include, {
|
|
7984
8656
|
cwd: absoluteSource,
|
|
7985
8657
|
dot: true,
|
|
@@ -7989,7 +8661,7 @@ async function listInitialFiles(options) {
|
|
|
7989
8661
|
for (const relativePath of uploaded) {
|
|
7990
8662
|
files.set(relativePath, {
|
|
7991
8663
|
path: relativePath,
|
|
7992
|
-
sourcePath:
|
|
8664
|
+
sourcePath: path4.join(absoluteSource, relativePath)
|
|
7993
8665
|
});
|
|
7994
8666
|
}
|
|
7995
8667
|
}
|
|
@@ -8008,7 +8680,7 @@ async function uploadInitialFiles(sandbox, destination, files) {
|
|
|
8008
8680
|
for (let index = 0; index < files.length; index += WRITE_BATCH_SIZE) {
|
|
8009
8681
|
const batch = await Promise.all(
|
|
8010
8682
|
files.slice(index, index + WRITE_BATCH_SIZE).map(async (file) => ({
|
|
8011
|
-
path:
|
|
8683
|
+
path: path4.posix.join(destination, file.path),
|
|
8012
8684
|
content: "content" in file ? file.content : await readFile(file.sourcePath)
|
|
8013
8685
|
}))
|
|
8014
8686
|
);
|
|
@@ -8028,12 +8700,12 @@ async function createToolPrompt(sandbox, filenames, customPrompt) {
|
|
|
8028
8700
|
const lines = [
|
|
8029
8701
|
`Available tools: ${[...available].sort().join(", ")}, and more`
|
|
8030
8702
|
];
|
|
8031
|
-
const
|
|
8703
|
+
const formats2 = new Set(
|
|
8032
8704
|
filenames.map(
|
|
8033
|
-
(filename) => EXTENSION_FORMATS[
|
|
8705
|
+
(filename) => EXTENSION_FORMATS[path4.extname(filename).toLowerCase()]
|
|
8034
8706
|
).filter((format) => format !== void 0)
|
|
8035
8707
|
);
|
|
8036
|
-
for (const format of
|
|
8708
|
+
for (const format of formats2) {
|
|
8037
8709
|
const tools = FORMAT_TOOLS[format].filter((name) => available.has(name));
|
|
8038
8710
|
if (tools.length > 0) {
|
|
8039
8711
|
lines.push(`For ${FORMAT_LABELS[format]}: ${tools.join(", ")}`);
|
|
@@ -8093,7 +8765,7 @@ async function createBashTool(options) {
|
|
|
8093
8765
|
);
|
|
8094
8766
|
const extraInstructions = [options.extraInstructions, REASONING_INSTRUCTION].filter(Boolean).join("\n\n");
|
|
8095
8767
|
const experimentalTeeTransform = options.experimentalTeeTransform ?? false;
|
|
8096
|
-
const bash =
|
|
8768
|
+
const bash = tool3({
|
|
8097
8769
|
description: createBashDescription({
|
|
8098
8770
|
destination,
|
|
8099
8771
|
filenames: initialFiles.map((file) => file.path),
|
|
@@ -8101,9 +8773,9 @@ async function createBashTool(options) {
|
|
|
8101
8773
|
extraInstructions,
|
|
8102
8774
|
experimentalTeeTransform
|
|
8103
8775
|
}),
|
|
8104
|
-
inputSchema:
|
|
8105
|
-
command:
|
|
8106
|
-
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")
|
|
8107
8779
|
}),
|
|
8108
8780
|
execute: async ({ command: originalCommand }, executionOptions) => {
|
|
8109
8781
|
let command = originalCommand;
|
|
@@ -8143,28 +8815,18 @@ async function createBashTool(options) {
|
|
|
8143
8815
|
return Object.keys(meta).length > 0 ? { ...result, meta } : result;
|
|
8144
8816
|
}
|
|
8145
8817
|
});
|
|
8146
|
-
const readFile2 =
|
|
8147
|
-
|
|
8148
|
-
inputSchema: z4.object({
|
|
8149
|
-
path: z4.string().describe("The path to the file to read")
|
|
8150
|
-
}),
|
|
8151
|
-
execute: async ({ path: filePath }) => ({
|
|
8152
|
-
content: await sandbox.readFile(
|
|
8153
|
-
path.posix.resolve(destination, filePath)
|
|
8154
|
-
)
|
|
8155
|
-
})
|
|
8156
|
-
});
|
|
8157
|
-
const writeFile2 = tool2({
|
|
8818
|
+
const readFile2 = createReadFileTool({ sandbox, destination });
|
|
8819
|
+
const writeFile2 = tool3({
|
|
8158
8820
|
description: "Write content to a file in the sandbox. Creates parent directories if needed.",
|
|
8159
|
-
inputSchema:
|
|
8160
|
-
path:
|
|
8161
|
-
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")
|
|
8162
8824
|
}),
|
|
8163
8825
|
execute: async ({ path: filePath, content }) => {
|
|
8164
8826
|
try {
|
|
8165
8827
|
await sandbox.writeFiles([
|
|
8166
8828
|
{
|
|
8167
|
-
path:
|
|
8829
|
+
path: path4.posix.resolve(destination, filePath),
|
|
8168
8830
|
content
|
|
8169
8831
|
}
|
|
8170
8832
|
]);
|
|
@@ -8183,7 +8845,7 @@ async function createBashTool(options) {
|
|
|
8183
8845
|
import { existsSync as existsSync2 } from "fs";
|
|
8184
8846
|
import { defineCommand } from "just-bash";
|
|
8185
8847
|
import spawn4 from "nano-spawn";
|
|
8186
|
-
import * as
|
|
8848
|
+
import * as path5 from "path";
|
|
8187
8849
|
function createBinaryBridges(...binaries) {
|
|
8188
8850
|
return binaries.map((input) => {
|
|
8189
8851
|
const config = typeof input === "string" ? { name: input } : input;
|
|
@@ -8205,11 +8867,11 @@ function createBinaryBridges(...binaries) {
|
|
|
8205
8867
|
if (arg.startsWith("-")) {
|
|
8206
8868
|
return arg;
|
|
8207
8869
|
}
|
|
8208
|
-
const hasExtension =
|
|
8209
|
-
const hasPathSep = arg.includes(
|
|
8870
|
+
const hasExtension = path5.extname(arg) !== "";
|
|
8871
|
+
const hasPathSep = arg.includes(path5.sep) || arg.includes("/");
|
|
8210
8872
|
const isRelative = arg.startsWith(".");
|
|
8211
8873
|
if (hasExtension || hasPathSep || isRelative) {
|
|
8212
|
-
return
|
|
8874
|
+
return path5.resolve(realCwd, arg);
|
|
8213
8875
|
}
|
|
8214
8876
|
return arg;
|
|
8215
8877
|
});
|
|
@@ -8262,7 +8924,7 @@ function resolveRealCwd(ctx) {
|
|
|
8262
8924
|
const fs = ctx.fs;
|
|
8263
8925
|
let realCwd;
|
|
8264
8926
|
if (fs.root) {
|
|
8265
|
-
realCwd =
|
|
8927
|
+
realCwd = path5.join(fs.root, ctx.cwd);
|
|
8266
8928
|
} else if (typeof fs.getMountPoint === "function" && typeof fs.toRealPath === "function") {
|
|
8267
8929
|
const real = fs.toRealPath(ctx.cwd);
|
|
8268
8930
|
realCwd = real ?? process.cwd();
|
|
@@ -8313,7 +8975,9 @@ async function createDaytonaSandbox(client, options = {}) {
|
|
|
8313
8975
|
} catch (error) {
|
|
8314
8976
|
throw normalizeDaytonaError(error, sdk);
|
|
8315
8977
|
}
|
|
8978
|
+
await assertDaytonaBash(sandbox, options.commandTimeout);
|
|
8316
8979
|
const backend = createDaytonaSandboxMethods({
|
|
8980
|
+
sdk,
|
|
8317
8981
|
sandbox,
|
|
8318
8982
|
commandTimeout: options.commandTimeout
|
|
8319
8983
|
});
|
|
@@ -8328,6 +8992,24 @@ async function createDaytonaSandbox(client, options = {}) {
|
|
|
8328
8992
|
}
|
|
8329
8993
|
return backend;
|
|
8330
8994
|
}
|
|
8995
|
+
async function assertDaytonaBash(sandbox, commandTimeout) {
|
|
8996
|
+
try {
|
|
8997
|
+
const result = await sandbox.process.executeCommand(
|
|
8998
|
+
"bash -lc ':'",
|
|
8999
|
+
void 0,
|
|
9000
|
+
void 0,
|
|
9001
|
+
commandTimeout
|
|
9002
|
+
);
|
|
9003
|
+
if (result.exitCode === void 0 || result.exitCode === 0) return;
|
|
9004
|
+
throw new Error(result.result || `exit code ${result.exitCode}`);
|
|
9005
|
+
} catch (error) {
|
|
9006
|
+
const err = toError(error);
|
|
9007
|
+
throw new DaytonaCreationError(
|
|
9008
|
+
`Bash is required to execute sandbox commands but could not be started: ${err.message}`,
|
|
9009
|
+
err
|
|
9010
|
+
);
|
|
9011
|
+
}
|
|
9012
|
+
}
|
|
8331
9013
|
var UNRECOVERABLE_SANDBOX_STATES = /* @__PURE__ */ new Set([
|
|
8332
9014
|
"error",
|
|
8333
9015
|
"build_failed",
|
|
@@ -8449,7 +9131,7 @@ function validateDaytonaOptions(options) {
|
|
|
8449
9131
|
}
|
|
8450
9132
|
}
|
|
8451
9133
|
function createDaytonaSandboxMethods(args) {
|
|
8452
|
-
const { sandbox, commandTimeout } = args;
|
|
9134
|
+
const { sdk, sandbox, commandTimeout } = args;
|
|
8453
9135
|
const executeCommand = async (command, options) => {
|
|
8454
9136
|
if (options?.signal?.aborted) {
|
|
8455
9137
|
return abortedCommandResult();
|
|
@@ -8464,7 +9146,7 @@ function createDaytonaSandboxMethods(args) {
|
|
|
8464
9146
|
return abortedCommandResult();
|
|
8465
9147
|
}
|
|
8466
9148
|
const response = await sandbox.process.executeCommand(
|
|
8467
|
-
command
|
|
9149
|
+
`bash -lc ${shellQuote(command)}`,
|
|
8468
9150
|
void 0,
|
|
8469
9151
|
void 0,
|
|
8470
9152
|
commandTimeout
|
|
@@ -8495,13 +9177,25 @@ function createDaytonaSandboxMethods(args) {
|
|
|
8495
9177
|
commandTimeout
|
|
8496
9178
|
});
|
|
8497
9179
|
},
|
|
8498
|
-
async readFile(
|
|
9180
|
+
async readFile(path6, options) {
|
|
8499
9181
|
try {
|
|
8500
|
-
const bytes = await sandbox.fs.downloadFile(
|
|
8501
|
-
return
|
|
9182
|
+
const bytes = await sandbox.fs.downloadFile(path6);
|
|
9183
|
+
return readFileContent(bytes, options);
|
|
9184
|
+
} catch (error) {
|
|
9185
|
+
throw new DaytonaCommandError(
|
|
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;
|
|
8502
9195
|
} catch (error) {
|
|
9196
|
+
if (error instanceof sdk.DaytonaNotFoundError) return false;
|
|
8503
9197
|
throw new DaytonaCommandError(
|
|
8504
|
-
`Failed to
|
|
9198
|
+
`Failed to check "${path6}": ${toError(error).message}`,
|
|
8505
9199
|
toError(error)
|
|
8506
9200
|
);
|
|
8507
9201
|
}
|
|
@@ -8706,13 +9400,13 @@ function buildSessionCommand(command, options) {
|
|
|
8706
9400
|
const env = options.env ?? {};
|
|
8707
9401
|
const entries = Object.entries(env);
|
|
8708
9402
|
if (entries.length === 0) {
|
|
8709
|
-
return `
|
|
9403
|
+
return `bash -lc ${shellQuote(`${cwdPrefix}${command}`)}`;
|
|
8710
9404
|
}
|
|
8711
9405
|
for (const [key] of entries) {
|
|
8712
9406
|
validateEnvKey(key);
|
|
8713
9407
|
}
|
|
8714
9408
|
const exports = entries.map(([key, value]) => `export ${key}=${shellQuote(value)}`).join("; ");
|
|
8715
|
-
return `
|
|
9409
|
+
return `bash -lc ${shellQuote(`${exports}; ${cwdPrefix}${command}`)}`;
|
|
8716
9410
|
}
|
|
8717
9411
|
function validateEnvKey(key) {
|
|
8718
9412
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
@@ -8740,10 +9434,10 @@ function abortedExitInfo() {
|
|
|
8740
9434
|
}
|
|
8741
9435
|
function uniqueParentDirectories(paths) {
|
|
8742
9436
|
const dirs = /* @__PURE__ */ new Set();
|
|
8743
|
-
for (const
|
|
8744
|
-
const index =
|
|
9437
|
+
for (const path6 of paths) {
|
|
9438
|
+
const index = path6.lastIndexOf("/");
|
|
8745
9439
|
if (index > 0) {
|
|
8746
|
-
dirs.add(
|
|
9440
|
+
dirs.add(path6.slice(0, index));
|
|
8747
9441
|
}
|
|
8748
9442
|
}
|
|
8749
9443
|
return [...dirs];
|
|
@@ -8764,8 +9458,9 @@ function toError(error) {
|
|
|
8764
9458
|
// packages/context/src/lib/sandbox/docker-sandbox.ts
|
|
8765
9459
|
import spawn5 from "nano-spawn";
|
|
8766
9460
|
import { spawn as childSpawn3 } from "node:child_process";
|
|
8767
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
9461
|
+
import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
|
|
8768
9462
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
9463
|
+
import { setTimeout as delay2 } from "node:timers/promises";
|
|
8769
9464
|
function isDockerfileOptions(opts) {
|
|
8770
9465
|
return "dockerfile" in opts;
|
|
8771
9466
|
}
|
|
@@ -8952,7 +9647,7 @@ var dockerEngine = {
|
|
|
8952
9647
|
flags.push("-e", `${key}=${value}`);
|
|
8953
9648
|
}
|
|
8954
9649
|
}
|
|
8955
|
-
return ["exec", ...flags, containerId, "
|
|
9650
|
+
return ["exec", ...flags, containerId, "bash", "-lc", command];
|
|
8956
9651
|
},
|
|
8957
9652
|
inspectArgs(containerId) {
|
|
8958
9653
|
return [
|
|
@@ -8981,6 +9676,20 @@ var dockerEngine = {
|
|
|
8981
9676
|
const err = error;
|
|
8982
9677
|
return err.stderr || err.stdout || err.message || String(error);
|
|
8983
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
|
+
},
|
|
8984
9693
|
isServiceDown(message2) {
|
|
8985
9694
|
return message2.includes("Cannot connect") || message2.includes("docker daemon");
|
|
8986
9695
|
},
|
|
@@ -8995,7 +9704,7 @@ var dockerEngine = {
|
|
|
8995
9704
|
},
|
|
8996
9705
|
async ensureWorkdir() {
|
|
8997
9706
|
},
|
|
8998
|
-
defaultImage: "
|
|
9707
|
+
defaultImage: "bash:5.3-alpine3.24",
|
|
8999
9708
|
createInstallerContext,
|
|
9000
9709
|
async imageExists(tag) {
|
|
9001
9710
|
try {
|
|
@@ -9050,14 +9759,190 @@ function buildDockerExecFlags(options) {
|
|
|
9050
9759
|
}
|
|
9051
9760
|
return flags;
|
|
9052
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
|
+
};
|
|
9053
9936
|
var ComposeStrategy = class extends ContainerSandboxStrategy {
|
|
9054
9937
|
projectName;
|
|
9055
9938
|
composeFile;
|
|
9056
9939
|
service;
|
|
9940
|
+
commandTimeout;
|
|
9057
9941
|
constructor(args) {
|
|
9058
9942
|
super({ resources: args.resources }, dockerEngine);
|
|
9059
9943
|
this.composeFile = args.compose;
|
|
9060
9944
|
this.service = args.service;
|
|
9945
|
+
this.commandTimeout = args.commandTimeout;
|
|
9061
9946
|
this.projectName = this.computeProjectName();
|
|
9062
9947
|
}
|
|
9063
9948
|
computeProjectName() {
|
|
@@ -9093,36 +9978,23 @@ var ComposeStrategy = class extends ContainerSandboxStrategy {
|
|
|
9093
9978
|
async configure() {
|
|
9094
9979
|
}
|
|
9095
9980
|
async exec(command, options) {
|
|
9096
|
-
|
|
9097
|
-
|
|
9098
|
-
|
|
9099
|
-
|
|
9100
|
-
|
|
9101
|
-
|
|
9102
|
-
this.composeFile,
|
|
9103
|
-
"-p",
|
|
9104
|
-
this.projectName,
|
|
9105
|
-
"exec",
|
|
9106
|
-
"-T",
|
|
9107
|
-
this.service,
|
|
9108
|
-
"sh",
|
|
9109
|
-
"-c",
|
|
9110
|
-
command
|
|
9111
|
-
],
|
|
9112
|
-
{ signal: options?.signal }
|
|
9113
|
-
);
|
|
9114
|
-
return { stdout: result.stdout, stderr: result.stderr, exitCode: 0 };
|
|
9115
|
-
} catch (error) {
|
|
9116
|
-
const err = error;
|
|
9117
|
-
return {
|
|
9118
|
-
stdout: err.stdout || "",
|
|
9119
|
-
stderr: err.stderr || err.message || "",
|
|
9120
|
-
exitCode: err.exitCode ?? 1
|
|
9121
|
-
};
|
|
9122
|
-
}
|
|
9981
|
+
return executeDockerCommand(
|
|
9982
|
+
(value, spawnOptions) => this.execArgs(value, spawnOptions),
|
|
9983
|
+
command,
|
|
9984
|
+
options,
|
|
9985
|
+
this.commandTimeout
|
|
9986
|
+
);
|
|
9123
9987
|
}
|
|
9124
9988
|
spawnProcess(command, options) {
|
|
9125
|
-
|
|
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 [
|
|
9126
9998
|
"compose",
|
|
9127
9999
|
"-f",
|
|
9128
10000
|
this.composeFile,
|
|
@@ -9132,11 +10004,10 @@ var ComposeStrategy = class extends ContainerSandboxStrategy {
|
|
|
9132
10004
|
"-T",
|
|
9133
10005
|
...buildDockerExecFlags(options),
|
|
9134
10006
|
this.service,
|
|
9135
|
-
"
|
|
9136
|
-
"-
|
|
10007
|
+
"bash",
|
|
10008
|
+
"-lc",
|
|
9137
10009
|
command
|
|
9138
|
-
]
|
|
9139
|
-
return toSandboxProcess(child, options?.signal);
|
|
10010
|
+
];
|
|
9140
10011
|
}
|
|
9141
10012
|
async stopContainer(_containerId) {
|
|
9142
10013
|
try {
|
|
@@ -9157,18 +10028,19 @@ async function createDockerSandbox(options = {}) {
|
|
|
9157
10028
|
return new ComposeStrategy({
|
|
9158
10029
|
compose: options.compose,
|
|
9159
10030
|
service: options.service,
|
|
9160
|
-
resources: options.resources
|
|
10031
|
+
resources: options.resources,
|
|
10032
|
+
commandTimeout: options.commandTimeout
|
|
9161
10033
|
}).create();
|
|
9162
10034
|
}
|
|
9163
10035
|
if (isDockerfileOptions(options)) {
|
|
9164
|
-
return new
|
|
10036
|
+
return new DockerContainerfileStrategy(options, dockerEngine, {
|
|
9165
10037
|
dockerfile: options.dockerfile,
|
|
9166
10038
|
context: options.context ?? ".",
|
|
9167
10039
|
showBuildLogs: options.showBuildLogs ?? false,
|
|
9168
10040
|
identity: options.platform
|
|
9169
10041
|
}).create();
|
|
9170
10042
|
}
|
|
9171
|
-
return new
|
|
10043
|
+
return new DockerRuntimeStrategy(options, dockerEngine, {
|
|
9172
10044
|
image: options.image ?? dockerEngine.defaultImage,
|
|
9173
10045
|
installers: options.installers ?? []
|
|
9174
10046
|
}).create();
|
|
@@ -9183,7 +10055,7 @@ async function useSandbox(options, fn) {
|
|
|
9183
10055
|
}
|
|
9184
10056
|
|
|
9185
10057
|
// packages/context/src/lib/sandbox/strace/file-changes.ts
|
|
9186
|
-
import { randomUUID as
|
|
10058
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
9187
10059
|
import { posix as posix3 } from "node:path";
|
|
9188
10060
|
|
|
9189
10061
|
// packages/context/src/lib/sandbox/strace/index.ts
|
|
@@ -9191,10 +10063,10 @@ import spawn6 from "nano-spawn";
|
|
|
9191
10063
|
import { posix as posix2 } from "node:path";
|
|
9192
10064
|
var STRACE_FLAGS = "-f -y -qq -e trace=%file,write,pwrite64,writev";
|
|
9193
10065
|
function buildStraceCommand(command, traceFile, traceDir) {
|
|
9194
|
-
return `mkdir -p ${shellQuote(traceDir)} 2>/dev/null; strace ${STRACE_FLAGS} -o ${shellQuote(traceFile)} --
|
|
10066
|
+
return `mkdir -p ${shellQuote(traceDir)} 2>/dev/null; strace ${STRACE_FLAGS} -o ${shellQuote(traceFile)} -- bash -lc ${shellQuote(command)}`;
|
|
9195
10067
|
}
|
|
9196
10068
|
function buildTracedCommand(command, traceFile, traceDir, sentinel) {
|
|
9197
|
-
return `mkdir -p ${shellQuote(traceDir)} 2>/dev/null; strace ${STRACE_FLAGS} -o ${shellQuote(traceFile)} --
|
|
10069
|
+
return `mkdir -p ${shellQuote(traceDir)} 2>/dev/null; strace ${STRACE_FLAGS} -o ${shellQuote(traceFile)} -- bash -lc ${shellQuote(command)}; __dat_rc=$?; printf '\\n%s\\n' ${shellQuote(sentinel)}; base64 ${shellQuote(traceFile)} 2>/dev/null; rm -f ${shellQuote(traceFile)} 2>/dev/null; exit $__dat_rc`;
|
|
9198
10070
|
}
|
|
9199
10071
|
function splitTracedOutput(stdout, sentinel) {
|
|
9200
10072
|
const marker = `
|
|
@@ -9305,8 +10177,8 @@ function fdPath(args) {
|
|
|
9305
10177
|
function openFlags(args) {
|
|
9306
10178
|
return args.match(/\bO_[A-Z_]+(?:\|O_[A-Z_]+)*/)?.[0] ?? "";
|
|
9307
10179
|
}
|
|
9308
|
-
function matchesGlobs(
|
|
9309
|
-
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));
|
|
9310
10182
|
}
|
|
9311
10183
|
function parseStraceTrace(raw, options) {
|
|
9312
10184
|
const { include, exclude, traceDir, traceFile } = options;
|
|
@@ -9314,12 +10186,12 @@ function parseStraceTrace(raw, options) {
|
|
|
9314
10186
|
const state = /* @__PURE__ */ new Map();
|
|
9315
10187
|
const renames = [];
|
|
9316
10188
|
const resolve3 = (dir, p) => posix2.normalize(p.startsWith("/") ? p : posix2.join(dir, p));
|
|
9317
|
-
const write = (
|
|
9318
|
-
state.set(
|
|
10189
|
+
const write = (path6) => {
|
|
10190
|
+
state.set(path6, "write");
|
|
9319
10191
|
};
|
|
9320
|
-
const remove = (
|
|
9321
|
-
if (state.get(
|
|
9322
|
-
else state.set(
|
|
10192
|
+
const remove = (path6) => {
|
|
10193
|
+
if (state.get(path6) === "write") state.delete(path6);
|
|
10194
|
+
else state.set(path6, "delete");
|
|
9323
10195
|
};
|
|
9324
10196
|
for (const line of stitchLines(raw)) {
|
|
9325
10197
|
const call = parseCall(line);
|
|
@@ -9366,13 +10238,13 @@ function parseStraceTrace(raw, options) {
|
|
|
9366
10238
|
if (p) write(resolve3(dir, p));
|
|
9367
10239
|
}
|
|
9368
10240
|
}
|
|
9369
|
-
const keep = (
|
|
9370
|
-
if (traceFile &&
|
|
9371
|
-
if (traceDir && (
|
|
10241
|
+
const keep = (path6) => {
|
|
10242
|
+
if (traceFile && path6 === traceFile) return false;
|
|
10243
|
+
if (traceDir && (path6 === traceDir || path6.startsWith(`${traceDir}/`))) {
|
|
9372
10244
|
return false;
|
|
9373
10245
|
}
|
|
9374
|
-
if (/^\/(proc|sys|dev)(\/|$)/.test(
|
|
9375
|
-
return matchesGlobs(
|
|
10246
|
+
if (/^\/(proc|sys|dev)(\/|$)/.test(path6)) return false;
|
|
10247
|
+
return matchesGlobs(path6, include, exclude);
|
|
9376
10248
|
};
|
|
9377
10249
|
const changes = [];
|
|
9378
10250
|
const emittedRenameTargets = /* @__PURE__ */ new Set();
|
|
@@ -9383,9 +10255,9 @@ function parseStraceTrace(raw, options) {
|
|
|
9383
10255
|
emittedRenameTargets.add(to);
|
|
9384
10256
|
}
|
|
9385
10257
|
const tail = [];
|
|
9386
|
-
for (const [
|
|
9387
|
-
if (emittedRenameTargets.has(
|
|
9388
|
-
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 });
|
|
9389
10261
|
}
|
|
9390
10262
|
tail.sort((a, b) => {
|
|
9391
10263
|
if (a.path < b.path) return -1;
|
|
@@ -9413,13 +10285,12 @@ async function withStraceFileChanges(sandbox, options) {
|
|
|
9413
10285
|
const onError = options.onError ?? warnOnFileChangesError;
|
|
9414
10286
|
const traceDir = options.traceDir ?? DEFAULT_TRACE_DIR;
|
|
9415
10287
|
const innerExecute = sandbox.executeCommand.bind(sandbox);
|
|
9416
|
-
const innerReadFile = sandbox.readFile.bind(sandbox);
|
|
9417
10288
|
const innerWriteFiles = sandbox.writeFiles.bind(sandbox);
|
|
9418
10289
|
const removeTrace = (traceFile) => void innerExecute(`rm -f ${shellQuote(traceFile)}`).catch(() => {
|
|
9419
10290
|
});
|
|
9420
10291
|
const readChanges = async (traceFile) => {
|
|
9421
10292
|
try {
|
|
9422
|
-
return parseStraceTrace(await
|
|
10293
|
+
return parseStraceTrace(await sandbox.readFile(traceFile), {
|
|
9423
10294
|
include,
|
|
9424
10295
|
exclude,
|
|
9425
10296
|
traceFile,
|
|
@@ -9442,8 +10313,8 @@ async function withStraceFileChanges(sandbox, options) {
|
|
|
9442
10313
|
};
|
|
9443
10314
|
const decorated = {
|
|
9444
10315
|
async executeCommand(command, execOptions) {
|
|
9445
|
-
const traceFile = `${traceDir}/${
|
|
9446
|
-
const sentinel = `__dat_trace_${
|
|
10316
|
+
const traceFile = `${traceDir}/${randomUUID3()}.strace`;
|
|
10317
|
+
const sentinel = `__dat_trace_${randomUUID3()}__`;
|
|
9447
10318
|
const wrapped = buildTracedCommand(
|
|
9448
10319
|
command,
|
|
9449
10320
|
traceFile,
|
|
@@ -9483,7 +10354,8 @@ async function withStraceFileChanges(sandbox, options) {
|
|
|
9483
10354
|
}
|
|
9484
10355
|
return result;
|
|
9485
10356
|
},
|
|
9486
|
-
readFile:
|
|
10357
|
+
readFile: (path6, options2) => sandbox.readFile(path6, options2),
|
|
10358
|
+
exists: (path6) => sandbox.exists(path6),
|
|
9487
10359
|
// The writeFile tool mutates the filesystem outside strace's view, so
|
|
9488
10360
|
// observe it directly: synthesize a `write` change per file (under the
|
|
9489
10361
|
// observation root) and run it through onFileChanges. A throw propagates to
|
|
@@ -9493,7 +10365,7 @@ async function withStraceFileChanges(sandbox, options) {
|
|
|
9493
10365
|
writeFiles: async (files) => {
|
|
9494
10366
|
await innerWriteFiles(files);
|
|
9495
10367
|
const now = Date.now();
|
|
9496
|
-
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 }));
|
|
9497
10369
|
if (changes.length) await onFileChanges?.(changes);
|
|
9498
10370
|
},
|
|
9499
10371
|
dispose: async () => {
|
|
@@ -9508,7 +10380,7 @@ async function withStraceFileChanges(sandbox, options) {
|
|
|
9508
10380
|
if (sandbox.spawn) {
|
|
9509
10381
|
const innerSpawn = sandbox.spawn.bind(sandbox);
|
|
9510
10382
|
decorated.spawn = (command, spawnOptions) => {
|
|
9511
|
-
const traceFile = `${traceDir}/${
|
|
10383
|
+
const traceFile = `${traceDir}/${randomUUID3()}.strace`;
|
|
9512
10384
|
const child = innerSpawn(
|
|
9513
10385
|
buildStraceCommand(command, traceFile, traceDir),
|
|
9514
10386
|
spawnOptions
|
|
@@ -9815,9 +10687,9 @@ function formatVersion(version) {
|
|
|
9815
10687
|
}
|
|
9816
10688
|
|
|
9817
10689
|
// packages/context/src/lib/sandbox/microsandbox-sandbox.ts
|
|
9818
|
-
import { randomUUID as
|
|
10690
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
9819
10691
|
var MICROSANDBOX_DEFAULT_DESTINATION = "/workspace";
|
|
9820
|
-
var MICROSANDBOX_DEFAULT_IMAGE = "
|
|
10692
|
+
var MICROSANDBOX_DEFAULT_IMAGE = "bash:5.3-alpine3.24";
|
|
9821
10693
|
var MICROSANDBOX_MAX_NAME_BYTES = 128;
|
|
9822
10694
|
var COMMAND_TIMEOUT_EXIT_CODE = 124;
|
|
9823
10695
|
var MicrosandboxSandboxError = class extends Error {
|
|
@@ -9852,13 +10724,29 @@ async function createMicrosandboxSandbox(options = {}) {
|
|
|
9852
10724
|
validateMicrosandboxOptions(options);
|
|
9853
10725
|
const sdk = await importMicrosandbox();
|
|
9854
10726
|
const ephemeral = options.name === void 0;
|
|
9855
|
-
const name = options.name ?? `deepagents-msb-${
|
|
10727
|
+
const name = options.name ?? `deepagents-msb-${randomUUID4()}`;
|
|
9856
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
|
+
};
|
|
9857
10735
|
let vm;
|
|
9858
10736
|
try {
|
|
9859
|
-
|
|
10737
|
+
const builder = sandboxBuilder(options.replace === true);
|
|
10738
|
+
vm = await (ephemeral || options.replace ? builder.create() : builder.connectOrCreate());
|
|
10739
|
+
} catch (error) {
|
|
10740
|
+
throw normalizeMicrosandboxError(error, sdk);
|
|
10741
|
+
}
|
|
10742
|
+
try {
|
|
9860
10743
|
await vm.fs().mkdir(workdir);
|
|
10744
|
+
await assertMicrosandboxBash(vm);
|
|
9861
10745
|
} catch (error) {
|
|
10746
|
+
await vm[Symbol.asyncDispose]().catch(() => {
|
|
10747
|
+
});
|
|
10748
|
+
if (ephemeral) await sdk.Sandbox.remove(name).catch(() => {
|
|
10749
|
+
});
|
|
9862
10750
|
throw normalizeMicrosandboxError(error, sdk);
|
|
9863
10751
|
}
|
|
9864
10752
|
const backend = createMicrosandboxMethods({
|
|
@@ -9866,7 +10754,8 @@ async function createMicrosandboxSandbox(options = {}) {
|
|
|
9866
10754
|
vm,
|
|
9867
10755
|
name,
|
|
9868
10756
|
ephemeral,
|
|
9869
|
-
commandTimeout: options.commandTimeout
|
|
10757
|
+
commandTimeout: options.commandTimeout,
|
|
10758
|
+
reconnect: ephemeral ? void 0 : () => sandboxBuilder(false).connectOrCreate()
|
|
9870
10759
|
});
|
|
9871
10760
|
if (options.readiness) {
|
|
9872
10761
|
try {
|
|
@@ -9886,56 +10775,22 @@ async function importMicrosandbox() {
|
|
|
9886
10775
|
throw new MicrosandboxNotAvailableError(toError2(error));
|
|
9887
10776
|
}
|
|
9888
10777
|
}
|
|
9889
|
-
async function
|
|
9890
|
-
if (options.ephemeral || options.replace) {
|
|
9891
|
-
return buildSandbox(sdk, options);
|
|
9892
|
-
}
|
|
9893
|
-
let handle;
|
|
9894
|
-
try {
|
|
9895
|
-
handle = await sdk.Sandbox.get(options.name);
|
|
9896
|
-
} catch (error) {
|
|
9897
|
-
if (error instanceof sdk.SandboxNotFoundError) {
|
|
9898
|
-
return createFreshSandbox(sdk, options);
|
|
9899
|
-
}
|
|
9900
|
-
throw error;
|
|
9901
|
-
}
|
|
9902
|
-
if (handle.status === "running") {
|
|
9903
|
-
return handle.connect();
|
|
9904
|
-
}
|
|
9905
|
-
if (handle.status === "draining") {
|
|
9906
|
-
await handle.waitUntilStopped();
|
|
9907
|
-
}
|
|
9908
|
-
try {
|
|
9909
|
-
return await handle.start();
|
|
9910
|
-
} catch {
|
|
9911
|
-
await sdk.Sandbox.remove(options.name).catch(() => {
|
|
9912
|
-
});
|
|
9913
|
-
return buildSandbox(sdk, options);
|
|
9914
|
-
}
|
|
9915
|
-
}
|
|
9916
|
-
async function createFreshSandbox(sdk, options) {
|
|
10778
|
+
async function assertMicrosandboxBash(vm) {
|
|
9917
10779
|
try {
|
|
9918
|
-
|
|
10780
|
+
const result = await vm.exec("bash", ["-lc", ":"]);
|
|
10781
|
+
if (result.code === 0) return;
|
|
10782
|
+
throw new Error(result.stderr() || `exit code ${result.code}`);
|
|
9919
10783
|
} catch (error) {
|
|
9920
|
-
|
|
9921
|
-
|
|
9922
|
-
|
|
9923
|
-
|
|
9924
|
-
|
|
10784
|
+
const err = toError2(error);
|
|
10785
|
+
throw new MicrosandboxCreationError(
|
|
10786
|
+
`Bash is required to execute sandbox commands but could not be started: ${err.message}`,
|
|
10787
|
+
err
|
|
10788
|
+
);
|
|
9925
10789
|
}
|
|
9926
10790
|
}
|
|
9927
|
-
function buildSandbox(sdk, options) {
|
|
9928
|
-
let builder = sdk.Sandbox.builder(options.name).image(options.image ?? MICROSANDBOX_DEFAULT_IMAGE).patch((patch) => patch.mkdir(options.workdir)).workdir(options.workdir);
|
|
9929
|
-
if (options.ephemeral) builder = builder.ephemeral(true);
|
|
9930
|
-
if (options.replace) builder = builder.replace();
|
|
9931
|
-
if (options.cpus !== void 0) builder = builder.cpus(options.cpus);
|
|
9932
|
-
if (options.memory !== void 0) builder = builder.memory(options.memory);
|
|
9933
|
-
if (options.env) builder = builder.envs(options.env);
|
|
9934
|
-
if (options.configure) builder = options.configure(builder);
|
|
9935
|
-
return builder.create();
|
|
9936
|
-
}
|
|
9937
10791
|
function normalizeMicrosandboxError(error, sdk) {
|
|
9938
10792
|
const err = toError2(error);
|
|
10793
|
+
if (err instanceof MicrosandboxSandboxError) return err;
|
|
9939
10794
|
if (err instanceof sdk.LibkrunfwNotFoundError) {
|
|
9940
10795
|
return new MicrosandboxNotAvailableError(err);
|
|
9941
10796
|
}
|
|
@@ -9957,9 +10812,25 @@ function validateMicrosandboxOptions(options) {
|
|
|
9957
10812
|
}
|
|
9958
10813
|
}
|
|
9959
10814
|
function createMicrosandboxMethods(args) {
|
|
9960
|
-
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
|
+
};
|
|
9961
10832
|
const spawn7 = (command, options = {}) => {
|
|
9962
|
-
return spawnMicrosandboxProcess(sdk,
|
|
10833
|
+
return spawnMicrosandboxProcess(sdk, run, command, {
|
|
9963
10834
|
...options,
|
|
9964
10835
|
commandTimeout
|
|
9965
10836
|
});
|
|
@@ -9983,25 +10854,38 @@ function createMicrosandboxMethods(args) {
|
|
|
9983
10854
|
return { stdout, stderr, exitCode: info.code ?? 1 };
|
|
9984
10855
|
},
|
|
9985
10856
|
spawn: spawn7,
|
|
9986
|
-
async readFile(
|
|
10857
|
+
async readFile(path6, options) {
|
|
9987
10858
|
try {
|
|
9988
|
-
|
|
10859
|
+
const bytes = await run((vm2) => vm2.fs().read(path6));
|
|
10860
|
+
return readFileContent(bytes, options);
|
|
9989
10861
|
} catch (error) {
|
|
9990
10862
|
throw new MicrosandboxCommandError(
|
|
9991
|
-
`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}`,
|
|
9992
10874
|
toError2(error)
|
|
9993
10875
|
);
|
|
9994
10876
|
}
|
|
9995
10877
|
},
|
|
9996
10878
|
async writeFiles(files) {
|
|
9997
10879
|
try {
|
|
9998
|
-
|
|
9999
|
-
|
|
10000
|
-
|
|
10001
|
-
|
|
10002
|
-
|
|
10003
|
-
|
|
10004
|
-
|
|
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
|
+
});
|
|
10005
10889
|
} catch (error) {
|
|
10006
10890
|
const err = toError2(error);
|
|
10007
10891
|
throw new MicrosandboxCommandError(
|
|
@@ -10011,10 +10895,7 @@ function createMicrosandboxMethods(args) {
|
|
|
10011
10895
|
}
|
|
10012
10896
|
},
|
|
10013
10897
|
async dispose() {
|
|
10014
|
-
|
|
10015
|
-
await vm.stop();
|
|
10016
|
-
} catch {
|
|
10017
|
-
}
|
|
10898
|
+
await vm[Symbol.asyncDispose]();
|
|
10018
10899
|
if (ephemeral) {
|
|
10019
10900
|
await sdk.Sandbox.remove(name).catch(() => {
|
|
10020
10901
|
});
|
|
@@ -10025,14 +10906,14 @@ function createMicrosandboxMethods(args) {
|
|
|
10025
10906
|
}
|
|
10026
10907
|
};
|
|
10027
10908
|
}
|
|
10028
|
-
function spawnMicrosandboxProcess(sdk,
|
|
10909
|
+
function spawnMicrosandboxProcess(sdk, run, command, options) {
|
|
10029
10910
|
const stdout = createByteReadable();
|
|
10030
10911
|
const stderr = createByteReadable();
|
|
10031
|
-
const exit = pumpExecStream({ sdk,
|
|
10912
|
+
const exit = pumpExecStream({ sdk, run, command, options, stdout, stderr });
|
|
10032
10913
|
return { stdout: stdout.stream, stderr: stderr.stream, exit };
|
|
10033
10914
|
}
|
|
10034
10915
|
async function pumpExecStream(args) {
|
|
10035
|
-
const { sdk,
|
|
10916
|
+
const { sdk, run, command, options, stdout, stderr } = args;
|
|
10036
10917
|
const { signal } = options;
|
|
10037
10918
|
let handle;
|
|
10038
10919
|
let aborted = signal?.aborted ?? false;
|
|
@@ -10050,12 +10931,14 @@ async function pumpExecStream(args) {
|
|
|
10050
10931
|
}
|
|
10051
10932
|
signal?.addEventListener("abort", abort, { once: true });
|
|
10052
10933
|
try {
|
|
10053
|
-
handle = await
|
|
10054
|
-
|
|
10055
|
-
|
|
10056
|
-
|
|
10057
|
-
|
|
10058
|
-
|
|
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
|
+
);
|
|
10059
10942
|
if (aborted) {
|
|
10060
10943
|
return abortedExitInfo2();
|
|
10061
10944
|
}
|
|
@@ -10154,10 +11037,10 @@ function abortedExitInfo2() {
|
|
|
10154
11037
|
}
|
|
10155
11038
|
function uniqueParentDirectories2(paths) {
|
|
10156
11039
|
const dirs = /* @__PURE__ */ new Set();
|
|
10157
|
-
for (const
|
|
10158
|
-
const index =
|
|
11040
|
+
for (const path6 of paths) {
|
|
11041
|
+
const index = path6.lastIndexOf("/");
|
|
10159
11042
|
if (index > 0) {
|
|
10160
|
-
dirs.add(
|
|
11043
|
+
dirs.add(path6.slice(0, index));
|
|
10161
11044
|
}
|
|
10162
11045
|
}
|
|
10163
11046
|
return [...dirs];
|
|
@@ -10243,13 +11126,15 @@ import {
|
|
|
10243
11126
|
Bash
|
|
10244
11127
|
} from "just-bash";
|
|
10245
11128
|
async function createVirtualSandbox(options) {
|
|
11129
|
+
const { fs } = options;
|
|
10246
11130
|
const bash = new Bash({
|
|
10247
|
-
fs
|
|
11131
|
+
fs,
|
|
10248
11132
|
cwd: options.cwd,
|
|
10249
11133
|
env: options.env,
|
|
10250
11134
|
javascript: options.javascript,
|
|
10251
11135
|
customCommands: options.customCommands
|
|
10252
11136
|
});
|
|
11137
|
+
const resolve3 = (path6) => fs.resolvePath(bash.getCwd(), path6);
|
|
10253
11138
|
const sandbox = {
|
|
10254
11139
|
async executeCommand(command, options2) {
|
|
10255
11140
|
const result = await bash.exec(
|
|
@@ -10262,17 +11147,20 @@ async function createVirtualSandbox(options) {
|
|
|
10262
11147
|
exitCode: result.exitCode
|
|
10263
11148
|
};
|
|
10264
11149
|
},
|
|
10265
|
-
async readFile(
|
|
10266
|
-
return
|
|
11150
|
+
async readFile(path6, readOptions) {
|
|
11151
|
+
return readFileContent(
|
|
11152
|
+
await fs.readFileBuffer(resolve3(path6)),
|
|
11153
|
+
readOptions
|
|
11154
|
+
);
|
|
10267
11155
|
},
|
|
10268
11156
|
async writeFiles(files) {
|
|
10269
11157
|
for (const file of files) {
|
|
10270
|
-
await
|
|
10271
|
-
file.path,
|
|
10272
|
-
typeof file.content === "string" ? file.content : Buffer.from(file.content).toString("utf-8")
|
|
10273
|
-
);
|
|
11158
|
+
await fs.writeFile(resolve3(file.path), file.content);
|
|
10274
11159
|
}
|
|
10275
11160
|
},
|
|
11161
|
+
async exists(path6) {
|
|
11162
|
+
return fs.exists(resolve3(path6));
|
|
11163
|
+
},
|
|
10276
11164
|
async dispose() {
|
|
10277
11165
|
},
|
|
10278
11166
|
[Symbol.asyncDispose]() {
|
|
@@ -10292,12 +11180,12 @@ async function createVirtualSandbox(options) {
|
|
|
10292
11180
|
}
|
|
10293
11181
|
|
|
10294
11182
|
// packages/context/src/lib/skills/fragments.ts
|
|
10295
|
-
import
|
|
11183
|
+
import dedent4 from "dedent";
|
|
10296
11184
|
function skills(availableSkills) {
|
|
10297
|
-
const skillSnapshot = availableSkills.map(({ name, description, path:
|
|
11185
|
+
const skillSnapshot = availableSkills.map(({ name, description, path: path6 }) => ({
|
|
10298
11186
|
name,
|
|
10299
11187
|
description,
|
|
10300
|
-
path:
|
|
11188
|
+
path: path6
|
|
10301
11189
|
}));
|
|
10302
11190
|
if (skillSnapshot.length === 0) {
|
|
10303
11191
|
return {
|
|
@@ -10323,7 +11211,7 @@ function skills(availableSkills) {
|
|
|
10323
11211
|
metadata: { skills: skillSnapshot }
|
|
10324
11212
|
};
|
|
10325
11213
|
}
|
|
10326
|
-
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.
|
|
10327
11215
|
|
|
10328
11216
|
### How to use skills
|
|
10329
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.
|
|
@@ -12045,6 +12933,7 @@ async function persistedWriter(options) {
|
|
|
12045
12933
|
let seq = 0;
|
|
12046
12934
|
let buffer = [];
|
|
12047
12935
|
let failedByErrorChunk = false;
|
|
12936
|
+
let abortedByChunk = false;
|
|
12048
12937
|
async function flush() {
|
|
12049
12938
|
if (buffer.length === 0) return;
|
|
12050
12939
|
const batch = buffer;
|
|
@@ -12070,11 +12959,14 @@ async function persistedWriter(options) {
|
|
|
12070
12959
|
}
|
|
12071
12960
|
}
|
|
12072
12961
|
async function persistChunk(chunk) {
|
|
12962
|
+
if (chunk.data.type === "abort") {
|
|
12963
|
+
abortedByChunk = true;
|
|
12964
|
+
}
|
|
12073
12965
|
if (strategy === "immediate") {
|
|
12074
12966
|
await appendBatch([chunk]);
|
|
12075
12967
|
} else {
|
|
12076
12968
|
buffer.push(chunk);
|
|
12077
|
-
if (buffer.length >= flushSize) {
|
|
12969
|
+
if (buffer.length >= flushSize || chunk.data.type.startsWith("data-")) {
|
|
12078
12970
|
await flush();
|
|
12079
12971
|
}
|
|
12080
12972
|
}
|
|
@@ -12102,7 +12994,10 @@ async function persistedWriter(options) {
|
|
|
12102
12994
|
async complete() {
|
|
12103
12995
|
await flush();
|
|
12104
12996
|
if (failedByErrorChunk) return;
|
|
12105
|
-
await store.updateStreamStatus(
|
|
12997
|
+
await store.updateStreamStatus(
|
|
12998
|
+
streamId,
|
|
12999
|
+
abortedByChunk ? "cancelled" : "completed"
|
|
13000
|
+
);
|
|
12106
13001
|
},
|
|
12107
13002
|
async fail(error) {
|
|
12108
13003
|
await flush();
|
|
@@ -12116,7 +13011,7 @@ async function persistedWriter(options) {
|
|
|
12116
13011
|
}
|
|
12117
13012
|
|
|
12118
13013
|
// packages/context/src/lib/stream/polling-change-source.ts
|
|
12119
|
-
import { setTimeout as
|
|
13014
|
+
import { setTimeout as delay3 } from "node:timers/promises";
|
|
12120
13015
|
|
|
12121
13016
|
// packages/context/src/lib/stream/polling-policy.ts
|
|
12122
13017
|
var DEFAULT_WATCH_POLLING = {
|
|
@@ -12161,7 +13056,7 @@ function nextAdaptivePollingDelay(state) {
|
|
|
12161
13056
|
state.config.minMs,
|
|
12162
13057
|
state.config.maxMs
|
|
12163
13058
|
);
|
|
12164
|
-
const
|
|
13059
|
+
const delay4 = applyJitter(
|
|
12165
13060
|
current,
|
|
12166
13061
|
state.config.jitterRatio,
|
|
12167
13062
|
state.config.minMs,
|
|
@@ -12172,7 +13067,7 @@ function nextAdaptivePollingDelay(state) {
|
|
|
12172
13067
|
state.config.minMs,
|
|
12173
13068
|
state.config.maxMs
|
|
12174
13069
|
);
|
|
12175
|
-
return
|
|
13070
|
+
return delay4;
|
|
12176
13071
|
}
|
|
12177
13072
|
function normalizeAdaptivePolling(polling, fallback) {
|
|
12178
13073
|
const merged = {
|
|
@@ -12272,7 +13167,7 @@ function isTerminal(status) {
|
|
|
12272
13167
|
return status !== "queued" && status !== "running";
|
|
12273
13168
|
}
|
|
12274
13169
|
function waitForDelay(ms, signal) {
|
|
12275
|
-
return
|
|
13170
|
+
return delay3(ms, true, { signal }).catch((err) => {
|
|
12276
13171
|
if (signal.aborted) return false;
|
|
12277
13172
|
throw err;
|
|
12278
13173
|
});
|
|
@@ -13975,6 +14870,7 @@ export {
|
|
|
13975
14870
|
preference,
|
|
13976
14871
|
principle,
|
|
13977
14872
|
quirk,
|
|
14873
|
+
readFileContent,
|
|
13978
14874
|
reasoningFramework,
|
|
13979
14875
|
reminder,
|
|
13980
14876
|
render,
|
|
@@ -13991,6 +14887,7 @@ export {
|
|
|
13991
14887
|
shellQuote,
|
|
13992
14888
|
skills,
|
|
13993
14889
|
skillsReminder,
|
|
14890
|
+
socraticPlan,
|
|
13994
14891
|
socraticPrompting,
|
|
13995
14892
|
soul,
|
|
13996
14893
|
stop,
|