@probelabs/probe 0.6.0-rc250 → 0.6.0-rc252
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/bin/binaries/probe-v0.6.0-rc252-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc252-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc252-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc252-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc252-x86_64-unknown-linux-musl.tar.gz +0 -0
- package/build/agent/ProbeAgent.js +48 -24
- package/build/agent/index.js +54 -27
- package/build/tools/executePlan.js +39 -7
- package/cjs/agent/ProbeAgent.cjs +54 -27
- package/cjs/index.cjs +54 -27
- package/package.json +1 -1
- package/src/agent/ProbeAgent.js +48 -24
- package/src/tools/executePlan.js +39 -7
- package/bin/binaries/probe-v0.6.0-rc250-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc250-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc250-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc250-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc250-x86_64-unknown-linux-musl.tar.gz +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -819,6 +819,11 @@ export class ProbeAgent {
|
|
|
819
819
|
// reset at the start of each answer() call
|
|
820
820
|
this._outputBuffer = { items: [] };
|
|
821
821
|
|
|
822
|
+
// Separate accumulator for extracted RAW_OUTPUT blocks from tool results.
|
|
823
|
+
// This is distinct from _outputBuffer to prevent the cycle where:
|
|
824
|
+
// formatSuccess wraps → extract re-adds → next execute_plan re-wraps (issue #438)
|
|
825
|
+
this._extractedRawBlocks = [];
|
|
826
|
+
|
|
822
827
|
const configOptions = {
|
|
823
828
|
sessionId: this.sessionId,
|
|
824
829
|
debug: this.debug,
|
|
@@ -2910,6 +2915,8 @@ Follow these instructions carefully:
|
|
|
2910
2915
|
// Both must preserve the output buffer so the parent call can append it.
|
|
2911
2916
|
if (this._outputBuffer && !options?._schemaFormatted && !options?._completionPromptProcessed) {
|
|
2912
2917
|
this._outputBuffer.items = [];
|
|
2918
|
+
// Also reset the extracted blocks accumulator (issue #438)
|
|
2919
|
+
this._extractedRawBlocks = [];
|
|
2913
2920
|
}
|
|
2914
2921
|
|
|
2915
2922
|
// START CHECKPOINT: Initialize task management for this request
|
|
@@ -3629,15 +3636,17 @@ Follow these instructions carefully:
|
|
|
3629
3636
|
|
|
3630
3637
|
let toolResultContent = typeof executionResult === 'string' ? executionResult : JSON.stringify(executionResult, null, 2);
|
|
3631
3638
|
|
|
3632
|
-
// Extract raw output blocks
|
|
3639
|
+
// Extract raw output blocks from tool result (before truncation)
|
|
3633
3640
|
// This prevents LLM from processing/hallucinating large structured output from execute_plan
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
+
// Push to _extractedRawBlocks (NOT _outputBuffer) to prevent the cycle where:
|
|
3642
|
+
// formatSuccess wraps → extract re-adds → next execute_plan re-wraps (issue #438)
|
|
3643
|
+
const { cleanedContent, extractedBlocks } = extractRawOutputBlocks(toolResultContent);
|
|
3644
|
+
if (extractedBlocks.length > 0) {
|
|
3645
|
+
toolResultContent = cleanedContent;
|
|
3646
|
+
// Accumulate extracted blocks separately from DSL output() buffer
|
|
3647
|
+
this._extractedRawBlocks.push(...extractedBlocks);
|
|
3648
|
+
if (this.debug) {
|
|
3649
|
+
console.log(`[DEBUG] Extracted ${extractedBlocks.length} raw output blocks (${extractedBlocks.reduce((sum, b) => sum + b.length, 0)} chars) from tool result`);
|
|
3641
3650
|
}
|
|
3642
3651
|
}
|
|
3643
3652
|
|
|
@@ -3887,15 +3896,17 @@ Follow these instructions carefully:
|
|
|
3887
3896
|
toolResultContent = toolResultContent.split(wsPrefix).join('');
|
|
3888
3897
|
}
|
|
3889
3898
|
|
|
3890
|
-
// Extract raw output blocks
|
|
3899
|
+
// Extract raw output blocks from tool result (before truncation)
|
|
3891
3900
|
// This prevents LLM from processing/hallucinating large structured output from execute_plan
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3901
|
+
// Push to _extractedRawBlocks (NOT _outputBuffer) to prevent the cycle where:
|
|
3902
|
+
// formatSuccess wraps → extract re-adds → next execute_plan re-wraps (issue #438)
|
|
3903
|
+
const { cleanedContent, extractedBlocks } = extractRawOutputBlocks(toolResultContent);
|
|
3904
|
+
if (extractedBlocks.length > 0) {
|
|
3905
|
+
toolResultContent = cleanedContent;
|
|
3906
|
+
// Accumulate extracted blocks separately from DSL output() buffer
|
|
3907
|
+
this._extractedRawBlocks.push(...extractedBlocks);
|
|
3908
|
+
if (this.debug) {
|
|
3909
|
+
console.log(`[DEBUG] Extracted ${extractedBlocks.length} raw output blocks (${extractedBlocks.reduce((sum, b) => sum + b.length, 0)} chars) from tool result`);
|
|
3899
3910
|
}
|
|
3900
3911
|
}
|
|
3901
3912
|
|
|
@@ -4314,16 +4325,18 @@ After reviewing, provide your final answer using attempt_completion.`;
|
|
|
4314
4325
|
|
|
4315
4326
|
// Make a follow-up call with the completion prompt
|
|
4316
4327
|
// Pass _completionPromptProcessed to prevent infinite loops
|
|
4317
|
-
// Save output
|
|
4328
|
+
// Save output buffers — the recursive answer() must not destroy DSL output() content
|
|
4318
4329
|
const savedOutputItems = this._outputBuffer ? [...this._outputBuffer.items] : [];
|
|
4330
|
+
const savedExtractedBlocks = this._extractedRawBlocks ? [...this._extractedRawBlocks] : [];
|
|
4319
4331
|
const completionResult = await this.answer(completionPromptMessage, [], {
|
|
4320
4332
|
...options,
|
|
4321
4333
|
_completionPromptProcessed: true
|
|
4322
4334
|
});
|
|
4323
|
-
// Restore output
|
|
4335
|
+
// Restore output buffers so the parent call can append them to the final result
|
|
4324
4336
|
if (this._outputBuffer) {
|
|
4325
4337
|
this._outputBuffer.items = savedOutputItems;
|
|
4326
4338
|
}
|
|
4339
|
+
this._extractedRawBlocks = savedExtractedBlocks;
|
|
4327
4340
|
|
|
4328
4341
|
// Update finalResult with the result from the completion prompt
|
|
4329
4342
|
finalResult = completionResult;
|
|
@@ -4383,7 +4396,8 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
4383
4396
|
// Call answer recursively with _schemaFormatted flag to prevent infinite loop
|
|
4384
4397
|
finalResult = await this.answer(schemaPrompt, [], {
|
|
4385
4398
|
...options,
|
|
4386
|
-
_schemaFormatted: true
|
|
4399
|
+
_schemaFormatted: true,
|
|
4400
|
+
_completionPromptProcessed: true // Prevent cascading completion prompts in retry calls
|
|
4387
4401
|
});
|
|
4388
4402
|
|
|
4389
4403
|
// Step 2: Validate and fix Mermaid diagrams if present (BEFORE cleaning schema)
|
|
@@ -4642,7 +4656,8 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
4642
4656
|
finalResult = await this.answer(schemaDefinitionPrompt, [], {
|
|
4643
4657
|
...options,
|
|
4644
4658
|
_schemaFormatted: true,
|
|
4645
|
-
_skipValidation: true // Skip validation in recursive correction calls to prevent loops
|
|
4659
|
+
_skipValidation: true, // Skip validation in recursive correction calls to prevent loops
|
|
4660
|
+
_completionPromptProcessed: true // Prevent cascading completion prompts in retry calls
|
|
4646
4661
|
});
|
|
4647
4662
|
finalResult = cleanSchemaResponse(finalResult);
|
|
4648
4663
|
validation = validateJsonResponse(finalResult);
|
|
@@ -4702,7 +4717,8 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
4702
4717
|
...options,
|
|
4703
4718
|
_schemaFormatted: true,
|
|
4704
4719
|
_skipValidation: true, // Skip validation in recursive correction calls to prevent loops
|
|
4705
|
-
_disableTools: true
|
|
4720
|
+
_disableTools: true, // Only allow attempt_completion - prevent AI from using search/query tools
|
|
4721
|
+
_completionPromptProcessed: true // Prevent cascading completion prompts in retry calls
|
|
4706
4722
|
});
|
|
4707
4723
|
finalResult = cleanSchemaResponse(finalResult);
|
|
4708
4724
|
|
|
@@ -4787,8 +4803,15 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
4787
4803
|
}
|
|
4788
4804
|
|
|
4789
4805
|
// Append DSL output buffer directly to response (bypasses LLM rewriting)
|
|
4790
|
-
|
|
4791
|
-
|
|
4806
|
+
// Skip during _completionPromptProcessed — only the parent answer() should append the buffer.
|
|
4807
|
+
// Combine _outputBuffer (from DSL output() calls) and _extractedRawBlocks (from tool results)
|
|
4808
|
+
// Using separate accumulators prevents the cycle described in issue #438.
|
|
4809
|
+
const allOutputItems = [
|
|
4810
|
+
...(this._outputBuffer?.items || []),
|
|
4811
|
+
...(this._extractedRawBlocks || [])
|
|
4812
|
+
];
|
|
4813
|
+
if (allOutputItems.length > 0 && !options._schemaFormatted && !options._completionPromptProcessed) {
|
|
4814
|
+
const outputContent = allOutputItems.join('\n\n');
|
|
4792
4815
|
if (options.schema) {
|
|
4793
4816
|
// Schema response — the finalResult is JSON. Wrap output in RAW_OUTPUT
|
|
4794
4817
|
// delimiters so clients (visor, etc.) can extract and propagate the
|
|
@@ -4801,9 +4824,10 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
4801
4824
|
options.onStream('\n\n' + outputContent);
|
|
4802
4825
|
}
|
|
4803
4826
|
if (this.debug) {
|
|
4804
|
-
console.log(`[DEBUG] Appended ${
|
|
4827
|
+
console.log(`[DEBUG] Appended ${allOutputItems.length} output items (${outputContent.length} chars) to final result${options.schema ? ' (with RAW_OUTPUT delimiters)' : ''}`);
|
|
4805
4828
|
}
|
|
4806
4829
|
this._outputBuffer.items = [];
|
|
4830
|
+
this._extractedRawBlocks = [];
|
|
4807
4831
|
}
|
|
4808
4832
|
|
|
4809
4833
|
return finalResult;
|
package/build/agent/index.js
CHANGED
|
@@ -28993,8 +28993,13 @@ function stripCodeWrapping(code) {
|
|
|
28993
28993
|
s = decodeHtmlEntities(s);
|
|
28994
28994
|
return s.trim();
|
|
28995
28995
|
}
|
|
28996
|
-
function
|
|
28997
|
-
const
|
|
28996
|
+
function generatePlanSessionId(baseSessionId) {
|
|
28997
|
+
const uniquePart = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID().slice(0, 8) : `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
28998
|
+
return `${baseSessionId || "plan"}-${uniquePart}`;
|
|
28999
|
+
}
|
|
29000
|
+
function buildToolImplementations(configOptions, planSessionId) {
|
|
29001
|
+
const { cwd } = configOptions;
|
|
29002
|
+
const sessionId = planSessionId || configOptions.sessionId;
|
|
28998
29003
|
const tools2 = {};
|
|
28999
29004
|
tools2.search = {
|
|
29000
29005
|
execute: async (params) => {
|
|
@@ -29180,7 +29185,7 @@ function createExecutePlanTool(options) {
|
|
|
29180
29185
|
const isMcpToolAllowed = options.isMcpToolAllowed || (() => true);
|
|
29181
29186
|
let cachedMcpBridge = null;
|
|
29182
29187
|
let runtime = null;
|
|
29183
|
-
function buildRuntime() {
|
|
29188
|
+
function buildRuntime(planSessionId) {
|
|
29184
29189
|
const currentMcpBridge = getMcpBridge();
|
|
29185
29190
|
const currentMcpTools = getMcpTools();
|
|
29186
29191
|
const filteredMcpTools = {};
|
|
@@ -29202,7 +29207,7 @@ function createExecutePlanTool(options) {
|
|
|
29202
29207
|
} else {
|
|
29203
29208
|
llmCallFn = llmCallFn || buildLLMCall(options);
|
|
29204
29209
|
runtimeOptions = {
|
|
29205
|
-
toolImplementations: buildToolImplementations(options),
|
|
29210
|
+
toolImplementations: buildToolImplementations(options, planSessionId),
|
|
29206
29211
|
llmCall: llmCallFn,
|
|
29207
29212
|
mcpBridge: currentMcpBridge,
|
|
29208
29213
|
mcpTools: filteredMcpTools,
|
|
@@ -29230,12 +29235,15 @@ function createExecutePlanTool(options) {
|
|
|
29230
29235
|
description: "Execute a JavaScript DSL program to orchestrate tool calls. Use for batch processing, paginated APIs, multi-step workflows where intermediate data is large. Write simple synchronous-looking code \u2014 do NOT use async/await.",
|
|
29231
29236
|
parameters: executePlanSchema,
|
|
29232
29237
|
execute: async ({ code, description }) => {
|
|
29238
|
+
const planSessionId = generatePlanSessionId(options.sessionId);
|
|
29233
29239
|
const planSpan = tracer?.createToolSpan?.("execute_plan", {
|
|
29234
29240
|
"dsl.description": description || "",
|
|
29235
29241
|
"dsl.code_length": code.length,
|
|
29236
29242
|
"dsl.code": code,
|
|
29237
|
-
"dsl.max_retries": maxRetries
|
|
29243
|
+
"dsl.max_retries": maxRetries,
|
|
29244
|
+
"dsl.plan_session_id": planSessionId
|
|
29238
29245
|
}) || null;
|
|
29246
|
+
const planRuntime = buildRuntime(planSessionId);
|
|
29239
29247
|
let currentCode = stripCodeWrapping(code);
|
|
29240
29248
|
let lastError = null;
|
|
29241
29249
|
let finalOutput;
|
|
@@ -29291,7 +29299,7 @@ Original error: ${lastError}`;
|
|
|
29291
29299
|
return finalOutput;
|
|
29292
29300
|
}
|
|
29293
29301
|
}
|
|
29294
|
-
const result = await
|
|
29302
|
+
const result = await planRuntime.execute(currentCode, description);
|
|
29295
29303
|
if (result.status === "success") {
|
|
29296
29304
|
finalOutput = formatSuccess(result, description, attempt, outputBuffer);
|
|
29297
29305
|
planSpan?.setAttributes?.({
|
|
@@ -29389,8 +29397,14 @@ ${userLogs.join("\n")}
|
|
|
29389
29397
|
}
|
|
29390
29398
|
}
|
|
29391
29399
|
const resultValue = result.result;
|
|
29400
|
+
const hasOutputBufferContent = outputBuffer && outputBuffer.items && outputBuffer.items.length > 0;
|
|
29392
29401
|
if (resultValue === void 0 || resultValue === null) {
|
|
29393
|
-
|
|
29402
|
+
if (hasOutputBufferContent) {
|
|
29403
|
+
const totalChars = outputBuffer.items.reduce((sum, item) => sum + item.length, 0);
|
|
29404
|
+
output += `Plan completed successfully. Output captured (${totalChars} chars) via output() and will be included in the final response.`;
|
|
29405
|
+
} else {
|
|
29406
|
+
output += "Plan completed (no return value).";
|
|
29407
|
+
}
|
|
29394
29408
|
} else if (typeof resultValue === "string") {
|
|
29395
29409
|
output += `Result:
|
|
29396
29410
|
${resultValue}`;
|
|
@@ -81832,6 +81846,7 @@ var init_ProbeAgent = __esm({
|
|
|
81832
81846
|
initializeTools() {
|
|
81833
81847
|
const isToolAllowed = (toolName) => this.allowedTools.isEnabled(toolName);
|
|
81834
81848
|
this._outputBuffer = { items: [] };
|
|
81849
|
+
this._extractedRawBlocks = [];
|
|
81835
81850
|
const configOptions = {
|
|
81836
81851
|
sessionId: this.sessionId,
|
|
81837
81852
|
debug: this.debug,
|
|
@@ -83566,6 +83581,7 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
83566
83581
|
const oldHistoryLength = this.history.length;
|
|
83567
83582
|
if (this._outputBuffer && !options?._schemaFormatted && !options?._completionPromptProcessed) {
|
|
83568
83583
|
this._outputBuffer.items = [];
|
|
83584
|
+
this._extractedRawBlocks = [];
|
|
83569
83585
|
}
|
|
83570
83586
|
if (this.enableTasks) {
|
|
83571
83587
|
try {
|
|
@@ -84065,13 +84081,12 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
84065
84081
|
}
|
|
84066
84082
|
const executionResult = await this.mcpBridge.mcpTools[toolName].execute(params);
|
|
84067
84083
|
let toolResultContent = typeof executionResult === "string" ? executionResult : JSON.stringify(executionResult, null, 2);
|
|
84068
|
-
|
|
84069
|
-
|
|
84070
|
-
|
|
84071
|
-
|
|
84072
|
-
|
|
84073
|
-
|
|
84074
|
-
}
|
|
84084
|
+
const { cleanedContent, extractedBlocks } = extractRawOutputBlocks(toolResultContent);
|
|
84085
|
+
if (extractedBlocks.length > 0) {
|
|
84086
|
+
toolResultContent = cleanedContent;
|
|
84087
|
+
this._extractedRawBlocks.push(...extractedBlocks);
|
|
84088
|
+
if (this.debug) {
|
|
84089
|
+
console.log(`[DEBUG] Extracted ${extractedBlocks.length} raw output blocks (${extractedBlocks.reduce((sum, b) => sum + b.length, 0)} chars) from tool result`);
|
|
84075
84090
|
}
|
|
84076
84091
|
}
|
|
84077
84092
|
try {
|
|
@@ -84280,13 +84295,12 @@ ${errorXml}
|
|
|
84280
84295
|
const wsPrefix = this.workspaceRoot.endsWith(sep5) ? this.workspaceRoot : this.workspaceRoot + sep5;
|
|
84281
84296
|
toolResultContent = toolResultContent.split(wsPrefix).join("");
|
|
84282
84297
|
}
|
|
84283
|
-
|
|
84284
|
-
|
|
84285
|
-
|
|
84286
|
-
|
|
84287
|
-
|
|
84288
|
-
|
|
84289
|
-
}
|
|
84298
|
+
const { cleanedContent, extractedBlocks } = extractRawOutputBlocks(toolResultContent);
|
|
84299
|
+
if (extractedBlocks.length > 0) {
|
|
84300
|
+
toolResultContent = cleanedContent;
|
|
84301
|
+
this._extractedRawBlocks.push(...extractedBlocks);
|
|
84302
|
+
if (this.debug) {
|
|
84303
|
+
console.log(`[DEBUG] Extracted ${extractedBlocks.length} raw output blocks (${extractedBlocks.reduce((sum, b) => sum + b.length, 0)} chars) from tool result`);
|
|
84290
84304
|
}
|
|
84291
84305
|
}
|
|
84292
84306
|
try {
|
|
@@ -84595,6 +84609,7 @@ ${finalResult}
|
|
|
84595
84609
|
|
|
84596
84610
|
After reviewing, provide your final answer using attempt_completion.`;
|
|
84597
84611
|
const savedOutputItems = this._outputBuffer ? [...this._outputBuffer.items] : [];
|
|
84612
|
+
const savedExtractedBlocks = this._extractedRawBlocks ? [...this._extractedRawBlocks] : [];
|
|
84598
84613
|
const completionResult = await this.answer(completionPromptMessage, [], {
|
|
84599
84614
|
...options,
|
|
84600
84615
|
_completionPromptProcessed: true
|
|
@@ -84602,6 +84617,7 @@ After reviewing, provide your final answer using attempt_completion.`;
|
|
|
84602
84617
|
if (this._outputBuffer) {
|
|
84603
84618
|
this._outputBuffer.items = savedOutputItems;
|
|
84604
84619
|
}
|
|
84620
|
+
this._extractedRawBlocks = savedExtractedBlocks;
|
|
84605
84621
|
finalResult = completionResult;
|
|
84606
84622
|
if (this.debug) {
|
|
84607
84623
|
console.log(`[DEBUG] Completion prompt finished. New result length: ${finalResult?.length || 0}`);
|
|
@@ -84647,7 +84663,9 @@ NOT: {"type": "object", "properties": {"name": {"type": "string"}}}
|
|
|
84647
84663
|
Convert your previous response content into actual JSON data that follows this schema structure.`;
|
|
84648
84664
|
finalResult = await this.answer(schemaPrompt, [], {
|
|
84649
84665
|
...options,
|
|
84650
|
-
_schemaFormatted: true
|
|
84666
|
+
_schemaFormatted: true,
|
|
84667
|
+
_completionPromptProcessed: true
|
|
84668
|
+
// Prevent cascading completion prompts in retry calls
|
|
84651
84669
|
});
|
|
84652
84670
|
if (!this.disableMermaidValidation) {
|
|
84653
84671
|
try {
|
|
@@ -84851,8 +84869,10 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
84851
84869
|
finalResult = await this.answer(schemaDefinitionPrompt, [], {
|
|
84852
84870
|
...options,
|
|
84853
84871
|
_schemaFormatted: true,
|
|
84854
|
-
_skipValidation: true
|
|
84872
|
+
_skipValidation: true,
|
|
84855
84873
|
// Skip validation in recursive correction calls to prevent loops
|
|
84874
|
+
_completionPromptProcessed: true
|
|
84875
|
+
// Prevent cascading completion prompts in retry calls
|
|
84856
84876
|
});
|
|
84857
84877
|
finalResult = cleanSchemaResponse(finalResult);
|
|
84858
84878
|
validation = validateJsonResponse(finalResult);
|
|
@@ -84905,8 +84925,10 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
84905
84925
|
_schemaFormatted: true,
|
|
84906
84926
|
_skipValidation: true,
|
|
84907
84927
|
// Skip validation in recursive correction calls to prevent loops
|
|
84908
|
-
_disableTools: true
|
|
84928
|
+
_disableTools: true,
|
|
84909
84929
|
// Only allow attempt_completion - prevent AI from using search/query tools
|
|
84930
|
+
_completionPromptProcessed: true
|
|
84931
|
+
// Prevent cascading completion prompts in retry calls
|
|
84910
84932
|
});
|
|
84911
84933
|
finalResult = cleanSchemaResponse(finalResult);
|
|
84912
84934
|
validation = validateJsonResponse(finalResult, { debug: this.debug });
|
|
@@ -84976,8 +84998,12 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
84976
84998
|
console.log(`[DEBUG] Removed thinking tags from final result`);
|
|
84977
84999
|
}
|
|
84978
85000
|
}
|
|
84979
|
-
|
|
84980
|
-
|
|
85001
|
+
const allOutputItems = [
|
|
85002
|
+
...this._outputBuffer?.items || [],
|
|
85003
|
+
...this._extractedRawBlocks || []
|
|
85004
|
+
];
|
|
85005
|
+
if (allOutputItems.length > 0 && !options._schemaFormatted && !options._completionPromptProcessed) {
|
|
85006
|
+
const outputContent = allOutputItems.join("\n\n");
|
|
84981
85007
|
if (options.schema) {
|
|
84982
85008
|
finalResult = (finalResult || "") + "\n<<<RAW_OUTPUT>>>\n" + outputContent + "\n<<<END_RAW_OUTPUT>>>";
|
|
84983
85009
|
} else {
|
|
@@ -84987,9 +85013,10 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
84987
85013
|
options.onStream("\n\n" + outputContent);
|
|
84988
85014
|
}
|
|
84989
85015
|
if (this.debug) {
|
|
84990
|
-
console.log(`[DEBUG] Appended ${
|
|
85016
|
+
console.log(`[DEBUG] Appended ${allOutputItems.length} output items (${outputContent.length} chars) to final result${options.schema ? " (with RAW_OUTPUT delimiters)" : ""}`);
|
|
84991
85017
|
}
|
|
84992
85018
|
this._outputBuffer.items = [];
|
|
85019
|
+
this._extractedRawBlocks = [];
|
|
84993
85020
|
}
|
|
84994
85021
|
return finalResult;
|
|
84995
85022
|
} catch (error) {
|
|
@@ -65,14 +65,28 @@ function stripCodeWrapping(code) {
|
|
|
65
65
|
return s.trim();
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Generate a unique session ID for this execute_plan invocation.
|
|
70
|
+
* Uses crypto.randomUUID if available, falls back to timestamp + random.
|
|
71
|
+
*/
|
|
72
|
+
function generatePlanSessionId(baseSessionId) {
|
|
73
|
+
const uniquePart = typeof crypto !== 'undefined' && crypto.randomUUID
|
|
74
|
+
? crypto.randomUUID().slice(0, 8)
|
|
75
|
+
: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
76
|
+
return `${baseSessionId || 'plan'}-${uniquePart}`;
|
|
77
|
+
}
|
|
78
|
+
|
|
68
79
|
/**
|
|
69
80
|
* Build DSL-compatible tool implementations from the agent's configOptions.
|
|
70
81
|
*
|
|
71
82
|
* @param {Object} configOptions - Agent config (sessionId, cwd, provider, model, etc.)
|
|
83
|
+
* @param {string} [planSessionId] - Unique session ID for this execute_plan invocation
|
|
72
84
|
* @returns {Object} toolImplementations for createDSLRuntime
|
|
73
85
|
*/
|
|
74
|
-
function buildToolImplementations(configOptions) {
|
|
75
|
-
const {
|
|
86
|
+
function buildToolImplementations(configOptions, planSessionId) {
|
|
87
|
+
const { cwd } = configOptions;
|
|
88
|
+
// Use planSessionId for isolated pagination per execute_plan, fall back to global sessionId
|
|
89
|
+
const sessionId = planSessionId || configOptions.sessionId;
|
|
76
90
|
const tools = {};
|
|
77
91
|
|
|
78
92
|
tools.search = {
|
|
@@ -311,9 +325,11 @@ export function createExecutePlanTool(options) {
|
|
|
311
325
|
|
|
312
326
|
/**
|
|
313
327
|
* Build or rebuild the DSL runtime.
|
|
314
|
-
* Called
|
|
328
|
+
* Called for each execute() invocation with a unique planSessionId.
|
|
329
|
+
*
|
|
330
|
+
* @param {string} [planSessionId] - Unique session ID for this execute_plan invocation
|
|
315
331
|
*/
|
|
316
|
-
function buildRuntime() {
|
|
332
|
+
function buildRuntime(planSessionId) {
|
|
317
333
|
const currentMcpBridge = getMcpBridge();
|
|
318
334
|
const currentMcpTools = getMcpTools();
|
|
319
335
|
|
|
@@ -340,7 +356,7 @@ export function createExecutePlanTool(options) {
|
|
|
340
356
|
// Agent configOptions — build everything from the agent's config
|
|
341
357
|
llmCallFn = llmCallFn || buildLLMCall(options);
|
|
342
358
|
runtimeOptions = {
|
|
343
|
-
toolImplementations: buildToolImplementations(options),
|
|
359
|
+
toolImplementations: buildToolImplementations(options, planSessionId),
|
|
344
360
|
llmCall: llmCallFn,
|
|
345
361
|
mcpBridge: currentMcpBridge,
|
|
346
362
|
mcpTools: filteredMcpTools,
|
|
@@ -360,6 +376,7 @@ export function createExecutePlanTool(options) {
|
|
|
360
376
|
|
|
361
377
|
/**
|
|
362
378
|
* Get or rebuild the runtime if MCP state has changed.
|
|
379
|
+
* @deprecated Use buildRuntime(planSessionId) directly for unique sessions per execution
|
|
363
380
|
*/
|
|
364
381
|
function getRuntime() {
|
|
365
382
|
const currentMcpBridge = getMcpBridge();
|
|
@@ -378,14 +395,22 @@ export function createExecutePlanTool(options) {
|
|
|
378
395
|
'Write simple synchronous-looking code — do NOT use async/await.',
|
|
379
396
|
parameters: executePlanSchema,
|
|
380
397
|
execute: async ({ code, description }) => {
|
|
398
|
+
// Generate a unique session ID for this execute_plan invocation
|
|
399
|
+
// This ensures search pagination is isolated per execute_plan call
|
|
400
|
+
const planSessionId = generatePlanSessionId(options.sessionId);
|
|
401
|
+
|
|
381
402
|
// Create top-level OTEL span for the entire execute_plan invocation
|
|
382
403
|
const planSpan = tracer?.createToolSpan?.('execute_plan', {
|
|
383
404
|
'dsl.description': description || '',
|
|
384
405
|
'dsl.code_length': code.length,
|
|
385
406
|
'dsl.code': code,
|
|
386
407
|
'dsl.max_retries': maxRetries,
|
|
408
|
+
'dsl.plan_session_id': planSessionId,
|
|
387
409
|
}) || null;
|
|
388
410
|
|
|
411
|
+
// Build runtime with the unique planSessionId for isolated search pagination
|
|
412
|
+
const planRuntime = buildRuntime(planSessionId);
|
|
413
|
+
|
|
389
414
|
// Strip XML tags and markdown fences LLMs sometimes wrap code in
|
|
390
415
|
let currentCode = stripCodeWrapping(code);
|
|
391
416
|
let lastError = null;
|
|
@@ -446,7 +471,7 @@ RULES REMINDER:
|
|
|
446
471
|
}
|
|
447
472
|
}
|
|
448
473
|
|
|
449
|
-
const result = await
|
|
474
|
+
const result = await planRuntime.execute(currentCode, description);
|
|
450
475
|
|
|
451
476
|
if (result.status === 'success') {
|
|
452
477
|
finalOutput = formatSuccess(result, description, attempt, outputBuffer);
|
|
@@ -574,8 +599,15 @@ function formatSuccess(result, description, attempt, outputBuffer) {
|
|
|
574
599
|
|
|
575
600
|
// Format the result value
|
|
576
601
|
const resultValue = result.result;
|
|
602
|
+
const hasOutputBufferContent = outputBuffer && outputBuffer.items && outputBuffer.items.length > 0;
|
|
577
603
|
if (resultValue === undefined || resultValue === null) {
|
|
578
|
-
|
|
604
|
+
if (hasOutputBufferContent) {
|
|
605
|
+
// output() was used but no return statement — tell LLM the script succeeded
|
|
606
|
+
const totalChars = outputBuffer.items.reduce((sum, item) => sum + item.length, 0);
|
|
607
|
+
output += `Plan completed successfully. Output captured (${totalChars} chars) via output() and will be included in the final response.`;
|
|
608
|
+
} else {
|
|
609
|
+
output += 'Plan completed (no return value).';
|
|
610
|
+
}
|
|
579
611
|
} else if (typeof resultValue === 'string') {
|
|
580
612
|
output += `Result:\n${resultValue}`;
|
|
581
613
|
} else {
|
package/cjs/agent/ProbeAgent.cjs
CHANGED
|
@@ -56126,8 +56126,13 @@ function stripCodeWrapping(code) {
|
|
|
56126
56126
|
s4 = decodeHtmlEntities(s4);
|
|
56127
56127
|
return s4.trim();
|
|
56128
56128
|
}
|
|
56129
|
-
function
|
|
56130
|
-
const
|
|
56129
|
+
function generatePlanSessionId(baseSessionId) {
|
|
56130
|
+
const uniquePart = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID().slice(0, 8) : `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
56131
|
+
return `${baseSessionId || "plan"}-${uniquePart}`;
|
|
56132
|
+
}
|
|
56133
|
+
function buildToolImplementations(configOptions, planSessionId) {
|
|
56134
|
+
const { cwd } = configOptions;
|
|
56135
|
+
const sessionId = planSessionId || configOptions.sessionId;
|
|
56131
56136
|
const tools2 = {};
|
|
56132
56137
|
tools2.search = {
|
|
56133
56138
|
execute: async (params) => {
|
|
@@ -56313,7 +56318,7 @@ function createExecutePlanTool(options) {
|
|
|
56313
56318
|
const isMcpToolAllowed = options.isMcpToolAllowed || (() => true);
|
|
56314
56319
|
let cachedMcpBridge = null;
|
|
56315
56320
|
let runtime = null;
|
|
56316
|
-
function buildRuntime() {
|
|
56321
|
+
function buildRuntime(planSessionId) {
|
|
56317
56322
|
const currentMcpBridge = getMcpBridge();
|
|
56318
56323
|
const currentMcpTools = getMcpTools();
|
|
56319
56324
|
const filteredMcpTools = {};
|
|
@@ -56335,7 +56340,7 @@ function createExecutePlanTool(options) {
|
|
|
56335
56340
|
} else {
|
|
56336
56341
|
llmCallFn = llmCallFn || buildLLMCall(options);
|
|
56337
56342
|
runtimeOptions = {
|
|
56338
|
-
toolImplementations: buildToolImplementations(options),
|
|
56343
|
+
toolImplementations: buildToolImplementations(options, planSessionId),
|
|
56339
56344
|
llmCall: llmCallFn,
|
|
56340
56345
|
mcpBridge: currentMcpBridge,
|
|
56341
56346
|
mcpTools: filteredMcpTools,
|
|
@@ -56363,12 +56368,15 @@ function createExecutePlanTool(options) {
|
|
|
56363
56368
|
description: "Execute a JavaScript DSL program to orchestrate tool calls. Use for batch processing, paginated APIs, multi-step workflows where intermediate data is large. Write simple synchronous-looking code \u2014 do NOT use async/await.",
|
|
56364
56369
|
parameters: executePlanSchema,
|
|
56365
56370
|
execute: async ({ code, description }) => {
|
|
56371
|
+
const planSessionId = generatePlanSessionId(options.sessionId);
|
|
56366
56372
|
const planSpan = tracer?.createToolSpan?.("execute_plan", {
|
|
56367
56373
|
"dsl.description": description || "",
|
|
56368
56374
|
"dsl.code_length": code.length,
|
|
56369
56375
|
"dsl.code": code,
|
|
56370
|
-
"dsl.max_retries": maxRetries
|
|
56376
|
+
"dsl.max_retries": maxRetries,
|
|
56377
|
+
"dsl.plan_session_id": planSessionId
|
|
56371
56378
|
}) || null;
|
|
56379
|
+
const planRuntime = buildRuntime(planSessionId);
|
|
56372
56380
|
let currentCode = stripCodeWrapping(code);
|
|
56373
56381
|
let lastError = null;
|
|
56374
56382
|
let finalOutput;
|
|
@@ -56424,7 +56432,7 @@ Original error: ${lastError}`;
|
|
|
56424
56432
|
return finalOutput;
|
|
56425
56433
|
}
|
|
56426
56434
|
}
|
|
56427
|
-
const result = await
|
|
56435
|
+
const result = await planRuntime.execute(currentCode, description);
|
|
56428
56436
|
if (result.status === "success") {
|
|
56429
56437
|
finalOutput = formatSuccess(result, description, attempt, outputBuffer);
|
|
56430
56438
|
planSpan?.setAttributes?.({
|
|
@@ -56522,8 +56530,14 @@ ${userLogs.join("\n")}
|
|
|
56522
56530
|
}
|
|
56523
56531
|
}
|
|
56524
56532
|
const resultValue = result.result;
|
|
56533
|
+
const hasOutputBufferContent = outputBuffer && outputBuffer.items && outputBuffer.items.length > 0;
|
|
56525
56534
|
if (resultValue === void 0 || resultValue === null) {
|
|
56526
|
-
|
|
56535
|
+
if (hasOutputBufferContent) {
|
|
56536
|
+
const totalChars = outputBuffer.items.reduce((sum, item) => sum + item.length, 0);
|
|
56537
|
+
output += `Plan completed successfully. Output captured (${totalChars} chars) via output() and will be included in the final response.`;
|
|
56538
|
+
} else {
|
|
56539
|
+
output += "Plan completed (no return value).";
|
|
56540
|
+
}
|
|
56527
56541
|
} else if (typeof resultValue === "string") {
|
|
56528
56542
|
output += `Result:
|
|
56529
56543
|
${resultValue}`;
|
|
@@ -108531,6 +108545,7 @@ var init_ProbeAgent = __esm({
|
|
|
108531
108545
|
initializeTools() {
|
|
108532
108546
|
const isToolAllowed = (toolName) => this.allowedTools.isEnabled(toolName);
|
|
108533
108547
|
this._outputBuffer = { items: [] };
|
|
108548
|
+
this._extractedRawBlocks = [];
|
|
108534
108549
|
const configOptions = {
|
|
108535
108550
|
sessionId: this.sessionId,
|
|
108536
108551
|
debug: this.debug,
|
|
@@ -110265,6 +110280,7 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
110265
110280
|
const oldHistoryLength = this.history.length;
|
|
110266
110281
|
if (this._outputBuffer && !options?._schemaFormatted && !options?._completionPromptProcessed) {
|
|
110267
110282
|
this._outputBuffer.items = [];
|
|
110283
|
+
this._extractedRawBlocks = [];
|
|
110268
110284
|
}
|
|
110269
110285
|
if (this.enableTasks) {
|
|
110270
110286
|
try {
|
|
@@ -110764,13 +110780,12 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
110764
110780
|
}
|
|
110765
110781
|
const executionResult = await this.mcpBridge.mcpTools[toolName].execute(params);
|
|
110766
110782
|
let toolResultContent = typeof executionResult === "string" ? executionResult : JSON.stringify(executionResult, null, 2);
|
|
110767
|
-
|
|
110768
|
-
|
|
110769
|
-
|
|
110770
|
-
|
|
110771
|
-
|
|
110772
|
-
|
|
110773
|
-
}
|
|
110783
|
+
const { cleanedContent, extractedBlocks } = extractRawOutputBlocks(toolResultContent);
|
|
110784
|
+
if (extractedBlocks.length > 0) {
|
|
110785
|
+
toolResultContent = cleanedContent;
|
|
110786
|
+
this._extractedRawBlocks.push(...extractedBlocks);
|
|
110787
|
+
if (this.debug) {
|
|
110788
|
+
console.log(`[DEBUG] Extracted ${extractedBlocks.length} raw output blocks (${extractedBlocks.reduce((sum, b4) => sum + b4.length, 0)} chars) from tool result`);
|
|
110774
110789
|
}
|
|
110775
110790
|
}
|
|
110776
110791
|
try {
|
|
@@ -110979,13 +110994,12 @@ ${errorXml}
|
|
|
110979
110994
|
const wsPrefix = this.workspaceRoot.endsWith(import_path17.sep) ? this.workspaceRoot : this.workspaceRoot + import_path17.sep;
|
|
110980
110995
|
toolResultContent = toolResultContent.split(wsPrefix).join("");
|
|
110981
110996
|
}
|
|
110982
|
-
|
|
110983
|
-
|
|
110984
|
-
|
|
110985
|
-
|
|
110986
|
-
|
|
110987
|
-
|
|
110988
|
-
}
|
|
110997
|
+
const { cleanedContent, extractedBlocks } = extractRawOutputBlocks(toolResultContent);
|
|
110998
|
+
if (extractedBlocks.length > 0) {
|
|
110999
|
+
toolResultContent = cleanedContent;
|
|
111000
|
+
this._extractedRawBlocks.push(...extractedBlocks);
|
|
111001
|
+
if (this.debug) {
|
|
111002
|
+
console.log(`[DEBUG] Extracted ${extractedBlocks.length} raw output blocks (${extractedBlocks.reduce((sum, b4) => sum + b4.length, 0)} chars) from tool result`);
|
|
110989
111003
|
}
|
|
110990
111004
|
}
|
|
110991
111005
|
try {
|
|
@@ -111294,6 +111308,7 @@ ${finalResult}
|
|
|
111294
111308
|
|
|
111295
111309
|
After reviewing, provide your final answer using attempt_completion.`;
|
|
111296
111310
|
const savedOutputItems = this._outputBuffer ? [...this._outputBuffer.items] : [];
|
|
111311
|
+
const savedExtractedBlocks = this._extractedRawBlocks ? [...this._extractedRawBlocks] : [];
|
|
111297
111312
|
const completionResult = await this.answer(completionPromptMessage, [], {
|
|
111298
111313
|
...options,
|
|
111299
111314
|
_completionPromptProcessed: true
|
|
@@ -111301,6 +111316,7 @@ After reviewing, provide your final answer using attempt_completion.`;
|
|
|
111301
111316
|
if (this._outputBuffer) {
|
|
111302
111317
|
this._outputBuffer.items = savedOutputItems;
|
|
111303
111318
|
}
|
|
111319
|
+
this._extractedRawBlocks = savedExtractedBlocks;
|
|
111304
111320
|
finalResult = completionResult;
|
|
111305
111321
|
if (this.debug) {
|
|
111306
111322
|
console.log(`[DEBUG] Completion prompt finished. New result length: ${finalResult?.length || 0}`);
|
|
@@ -111346,7 +111362,9 @@ NOT: {"type": "object", "properties": {"name": {"type": "string"}}}
|
|
|
111346
111362
|
Convert your previous response content into actual JSON data that follows this schema structure.`;
|
|
111347
111363
|
finalResult = await this.answer(schemaPrompt, [], {
|
|
111348
111364
|
...options,
|
|
111349
|
-
_schemaFormatted: true
|
|
111365
|
+
_schemaFormatted: true,
|
|
111366
|
+
_completionPromptProcessed: true
|
|
111367
|
+
// Prevent cascading completion prompts in retry calls
|
|
111350
111368
|
});
|
|
111351
111369
|
if (!this.disableMermaidValidation) {
|
|
111352
111370
|
try {
|
|
@@ -111550,8 +111568,10 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
111550
111568
|
finalResult = await this.answer(schemaDefinitionPrompt, [], {
|
|
111551
111569
|
...options,
|
|
111552
111570
|
_schemaFormatted: true,
|
|
111553
|
-
_skipValidation: true
|
|
111571
|
+
_skipValidation: true,
|
|
111554
111572
|
// Skip validation in recursive correction calls to prevent loops
|
|
111573
|
+
_completionPromptProcessed: true
|
|
111574
|
+
// Prevent cascading completion prompts in retry calls
|
|
111555
111575
|
});
|
|
111556
111576
|
finalResult = cleanSchemaResponse(finalResult);
|
|
111557
111577
|
validation = validateJsonResponse(finalResult);
|
|
@@ -111604,8 +111624,10 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
111604
111624
|
_schemaFormatted: true,
|
|
111605
111625
|
_skipValidation: true,
|
|
111606
111626
|
// Skip validation in recursive correction calls to prevent loops
|
|
111607
|
-
_disableTools: true
|
|
111627
|
+
_disableTools: true,
|
|
111608
111628
|
// Only allow attempt_completion - prevent AI from using search/query tools
|
|
111629
|
+
_completionPromptProcessed: true
|
|
111630
|
+
// Prevent cascading completion prompts in retry calls
|
|
111609
111631
|
});
|
|
111610
111632
|
finalResult = cleanSchemaResponse(finalResult);
|
|
111611
111633
|
validation = validateJsonResponse(finalResult, { debug: this.debug });
|
|
@@ -111675,8 +111697,12 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
111675
111697
|
console.log(`[DEBUG] Removed thinking tags from final result`);
|
|
111676
111698
|
}
|
|
111677
111699
|
}
|
|
111678
|
-
|
|
111679
|
-
|
|
111700
|
+
const allOutputItems = [
|
|
111701
|
+
...this._outputBuffer?.items || [],
|
|
111702
|
+
...this._extractedRawBlocks || []
|
|
111703
|
+
];
|
|
111704
|
+
if (allOutputItems.length > 0 && !options._schemaFormatted && !options._completionPromptProcessed) {
|
|
111705
|
+
const outputContent = allOutputItems.join("\n\n");
|
|
111680
111706
|
if (options.schema) {
|
|
111681
111707
|
finalResult = (finalResult || "") + "\n<<<RAW_OUTPUT>>>\n" + outputContent + "\n<<<END_RAW_OUTPUT>>>";
|
|
111682
111708
|
} else {
|
|
@@ -111686,9 +111712,10 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
111686
111712
|
options.onStream("\n\n" + outputContent);
|
|
111687
111713
|
}
|
|
111688
111714
|
if (this.debug) {
|
|
111689
|
-
console.log(`[DEBUG] Appended ${
|
|
111715
|
+
console.log(`[DEBUG] Appended ${allOutputItems.length} output items (${outputContent.length} chars) to final result${options.schema ? " (with RAW_OUTPUT delimiters)" : ""}`);
|
|
111690
111716
|
}
|
|
111691
111717
|
this._outputBuffer.items = [];
|
|
111718
|
+
this._extractedRawBlocks = [];
|
|
111692
111719
|
}
|
|
111693
111720
|
return finalResult;
|
|
111694
111721
|
} catch (error2) {
|
package/cjs/index.cjs
CHANGED
|
@@ -103899,8 +103899,13 @@ function stripCodeWrapping(code) {
|
|
|
103899
103899
|
s4 = decodeHtmlEntities2(s4);
|
|
103900
103900
|
return s4.trim();
|
|
103901
103901
|
}
|
|
103902
|
-
function
|
|
103903
|
-
const
|
|
103902
|
+
function generatePlanSessionId(baseSessionId) {
|
|
103903
|
+
const uniquePart = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID().slice(0, 8) : `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
103904
|
+
return `${baseSessionId || "plan"}-${uniquePart}`;
|
|
103905
|
+
}
|
|
103906
|
+
function buildToolImplementations(configOptions, planSessionId) {
|
|
103907
|
+
const { cwd } = configOptions;
|
|
103908
|
+
const sessionId = planSessionId || configOptions.sessionId;
|
|
103904
103909
|
const tools2 = {};
|
|
103905
103910
|
tools2.search = {
|
|
103906
103911
|
execute: async (params) => {
|
|
@@ -104086,7 +104091,7 @@ function createExecutePlanTool(options) {
|
|
|
104086
104091
|
const isMcpToolAllowed = options.isMcpToolAllowed || (() => true);
|
|
104087
104092
|
let cachedMcpBridge = null;
|
|
104088
104093
|
let runtime = null;
|
|
104089
|
-
function buildRuntime() {
|
|
104094
|
+
function buildRuntime(planSessionId) {
|
|
104090
104095
|
const currentMcpBridge = getMcpBridge();
|
|
104091
104096
|
const currentMcpTools = getMcpTools();
|
|
104092
104097
|
const filteredMcpTools = {};
|
|
@@ -104108,7 +104113,7 @@ function createExecutePlanTool(options) {
|
|
|
104108
104113
|
} else {
|
|
104109
104114
|
llmCallFn = llmCallFn || buildLLMCall(options);
|
|
104110
104115
|
runtimeOptions = {
|
|
104111
|
-
toolImplementations: buildToolImplementations(options),
|
|
104116
|
+
toolImplementations: buildToolImplementations(options, planSessionId),
|
|
104112
104117
|
llmCall: llmCallFn,
|
|
104113
104118
|
mcpBridge: currentMcpBridge,
|
|
104114
104119
|
mcpTools: filteredMcpTools,
|
|
@@ -104136,12 +104141,15 @@ function createExecutePlanTool(options) {
|
|
|
104136
104141
|
description: "Execute a JavaScript DSL program to orchestrate tool calls. Use for batch processing, paginated APIs, multi-step workflows where intermediate data is large. Write simple synchronous-looking code \u2014 do NOT use async/await.",
|
|
104137
104142
|
parameters: executePlanSchema,
|
|
104138
104143
|
execute: async ({ code, description }) => {
|
|
104144
|
+
const planSessionId = generatePlanSessionId(options.sessionId);
|
|
104139
104145
|
const planSpan = tracer?.createToolSpan?.("execute_plan", {
|
|
104140
104146
|
"dsl.description": description || "",
|
|
104141
104147
|
"dsl.code_length": code.length,
|
|
104142
104148
|
"dsl.code": code,
|
|
104143
|
-
"dsl.max_retries": maxRetries
|
|
104149
|
+
"dsl.max_retries": maxRetries,
|
|
104150
|
+
"dsl.plan_session_id": planSessionId
|
|
104144
104151
|
}) || null;
|
|
104152
|
+
const planRuntime = buildRuntime(planSessionId);
|
|
104145
104153
|
let currentCode = stripCodeWrapping(code);
|
|
104146
104154
|
let lastError = null;
|
|
104147
104155
|
let finalOutput;
|
|
@@ -104197,7 +104205,7 @@ Original error: ${lastError}`;
|
|
|
104197
104205
|
return finalOutput;
|
|
104198
104206
|
}
|
|
104199
104207
|
}
|
|
104200
|
-
const result = await
|
|
104208
|
+
const result = await planRuntime.execute(currentCode, description);
|
|
104201
104209
|
if (result.status === "success") {
|
|
104202
104210
|
finalOutput = formatSuccess(result, description, attempt, outputBuffer);
|
|
104203
104211
|
planSpan?.setAttributes?.({
|
|
@@ -104295,8 +104303,14 @@ ${userLogs.join("\n")}
|
|
|
104295
104303
|
}
|
|
104296
104304
|
}
|
|
104297
104305
|
const resultValue = result.result;
|
|
104306
|
+
const hasOutputBufferContent = outputBuffer && outputBuffer.items && outputBuffer.items.length > 0;
|
|
104298
104307
|
if (resultValue === void 0 || resultValue === null) {
|
|
104299
|
-
|
|
104308
|
+
if (hasOutputBufferContent) {
|
|
104309
|
+
const totalChars = outputBuffer.items.reduce((sum, item) => sum + item.length, 0);
|
|
104310
|
+
output += `Plan completed successfully. Output captured (${totalChars} chars) via output() and will be included in the final response.`;
|
|
104311
|
+
} else {
|
|
104312
|
+
output += "Plan completed (no return value).";
|
|
104313
|
+
}
|
|
104300
104314
|
} else if (typeof resultValue === "string") {
|
|
104301
104315
|
output += `Result:
|
|
104302
104316
|
${resultValue}`;
|
|
@@ -106884,6 +106898,7 @@ var init_ProbeAgent = __esm({
|
|
|
106884
106898
|
initializeTools() {
|
|
106885
106899
|
const isToolAllowed = (toolName) => this.allowedTools.isEnabled(toolName);
|
|
106886
106900
|
this._outputBuffer = { items: [] };
|
|
106901
|
+
this._extractedRawBlocks = [];
|
|
106887
106902
|
const configOptions = {
|
|
106888
106903
|
sessionId: this.sessionId,
|
|
106889
106904
|
debug: this.debug,
|
|
@@ -108618,6 +108633,7 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
108618
108633
|
const oldHistoryLength = this.history.length;
|
|
108619
108634
|
if (this._outputBuffer && !options?._schemaFormatted && !options?._completionPromptProcessed) {
|
|
108620
108635
|
this._outputBuffer.items = [];
|
|
108636
|
+
this._extractedRawBlocks = [];
|
|
108621
108637
|
}
|
|
108622
108638
|
if (this.enableTasks) {
|
|
108623
108639
|
try {
|
|
@@ -109117,13 +109133,12 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
109117
109133
|
}
|
|
109118
109134
|
const executionResult = await this.mcpBridge.mcpTools[toolName].execute(params);
|
|
109119
109135
|
let toolResultContent = typeof executionResult === "string" ? executionResult : JSON.stringify(executionResult, null, 2);
|
|
109120
|
-
|
|
109121
|
-
|
|
109122
|
-
|
|
109123
|
-
|
|
109124
|
-
|
|
109125
|
-
|
|
109126
|
-
}
|
|
109136
|
+
const { cleanedContent, extractedBlocks } = extractRawOutputBlocks(toolResultContent);
|
|
109137
|
+
if (extractedBlocks.length > 0) {
|
|
109138
|
+
toolResultContent = cleanedContent;
|
|
109139
|
+
this._extractedRawBlocks.push(...extractedBlocks);
|
|
109140
|
+
if (this.debug) {
|
|
109141
|
+
console.log(`[DEBUG] Extracted ${extractedBlocks.length} raw output blocks (${extractedBlocks.reduce((sum, b4) => sum + b4.length, 0)} chars) from tool result`);
|
|
109127
109142
|
}
|
|
109128
109143
|
}
|
|
109129
109144
|
try {
|
|
@@ -109332,13 +109347,12 @@ ${errorXml}
|
|
|
109332
109347
|
const wsPrefix = this.workspaceRoot.endsWith(import_path15.sep) ? this.workspaceRoot : this.workspaceRoot + import_path15.sep;
|
|
109333
109348
|
toolResultContent = toolResultContent.split(wsPrefix).join("");
|
|
109334
109349
|
}
|
|
109335
|
-
|
|
109336
|
-
|
|
109337
|
-
|
|
109338
|
-
|
|
109339
|
-
|
|
109340
|
-
|
|
109341
|
-
}
|
|
109350
|
+
const { cleanedContent, extractedBlocks } = extractRawOutputBlocks(toolResultContent);
|
|
109351
|
+
if (extractedBlocks.length > 0) {
|
|
109352
|
+
toolResultContent = cleanedContent;
|
|
109353
|
+
this._extractedRawBlocks.push(...extractedBlocks);
|
|
109354
|
+
if (this.debug) {
|
|
109355
|
+
console.log(`[DEBUG] Extracted ${extractedBlocks.length} raw output blocks (${extractedBlocks.reduce((sum, b4) => sum + b4.length, 0)} chars) from tool result`);
|
|
109342
109356
|
}
|
|
109343
109357
|
}
|
|
109344
109358
|
try {
|
|
@@ -109647,6 +109661,7 @@ ${finalResult}
|
|
|
109647
109661
|
|
|
109648
109662
|
After reviewing, provide your final answer using attempt_completion.`;
|
|
109649
109663
|
const savedOutputItems = this._outputBuffer ? [...this._outputBuffer.items] : [];
|
|
109664
|
+
const savedExtractedBlocks = this._extractedRawBlocks ? [...this._extractedRawBlocks] : [];
|
|
109650
109665
|
const completionResult = await this.answer(completionPromptMessage, [], {
|
|
109651
109666
|
...options,
|
|
109652
109667
|
_completionPromptProcessed: true
|
|
@@ -109654,6 +109669,7 @@ After reviewing, provide your final answer using attempt_completion.`;
|
|
|
109654
109669
|
if (this._outputBuffer) {
|
|
109655
109670
|
this._outputBuffer.items = savedOutputItems;
|
|
109656
109671
|
}
|
|
109672
|
+
this._extractedRawBlocks = savedExtractedBlocks;
|
|
109657
109673
|
finalResult = completionResult;
|
|
109658
109674
|
if (this.debug) {
|
|
109659
109675
|
console.log(`[DEBUG] Completion prompt finished. New result length: ${finalResult?.length || 0}`);
|
|
@@ -109699,7 +109715,9 @@ NOT: {"type": "object", "properties": {"name": {"type": "string"}}}
|
|
|
109699
109715
|
Convert your previous response content into actual JSON data that follows this schema structure.`;
|
|
109700
109716
|
finalResult = await this.answer(schemaPrompt, [], {
|
|
109701
109717
|
...options,
|
|
109702
|
-
_schemaFormatted: true
|
|
109718
|
+
_schemaFormatted: true,
|
|
109719
|
+
_completionPromptProcessed: true
|
|
109720
|
+
// Prevent cascading completion prompts in retry calls
|
|
109703
109721
|
});
|
|
109704
109722
|
if (!this.disableMermaidValidation) {
|
|
109705
109723
|
try {
|
|
@@ -109903,8 +109921,10 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
109903
109921
|
finalResult = await this.answer(schemaDefinitionPrompt, [], {
|
|
109904
109922
|
...options,
|
|
109905
109923
|
_schemaFormatted: true,
|
|
109906
|
-
_skipValidation: true
|
|
109924
|
+
_skipValidation: true,
|
|
109907
109925
|
// Skip validation in recursive correction calls to prevent loops
|
|
109926
|
+
_completionPromptProcessed: true
|
|
109927
|
+
// Prevent cascading completion prompts in retry calls
|
|
109908
109928
|
});
|
|
109909
109929
|
finalResult = cleanSchemaResponse(finalResult);
|
|
109910
109930
|
validation = validateJsonResponse(finalResult);
|
|
@@ -109957,8 +109977,10 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
109957
109977
|
_schemaFormatted: true,
|
|
109958
109978
|
_skipValidation: true,
|
|
109959
109979
|
// Skip validation in recursive correction calls to prevent loops
|
|
109960
|
-
_disableTools: true
|
|
109980
|
+
_disableTools: true,
|
|
109961
109981
|
// Only allow attempt_completion - prevent AI from using search/query tools
|
|
109982
|
+
_completionPromptProcessed: true
|
|
109983
|
+
// Prevent cascading completion prompts in retry calls
|
|
109962
109984
|
});
|
|
109963
109985
|
finalResult = cleanSchemaResponse(finalResult);
|
|
109964
109986
|
validation = validateJsonResponse(finalResult, { debug: this.debug });
|
|
@@ -110028,8 +110050,12 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
110028
110050
|
console.log(`[DEBUG] Removed thinking tags from final result`);
|
|
110029
110051
|
}
|
|
110030
110052
|
}
|
|
110031
|
-
|
|
110032
|
-
|
|
110053
|
+
const allOutputItems = [
|
|
110054
|
+
...this._outputBuffer?.items || [],
|
|
110055
|
+
...this._extractedRawBlocks || []
|
|
110056
|
+
];
|
|
110057
|
+
if (allOutputItems.length > 0 && !options._schemaFormatted && !options._completionPromptProcessed) {
|
|
110058
|
+
const outputContent = allOutputItems.join("\n\n");
|
|
110033
110059
|
if (options.schema) {
|
|
110034
110060
|
finalResult = (finalResult || "") + "\n<<<RAW_OUTPUT>>>\n" + outputContent + "\n<<<END_RAW_OUTPUT>>>";
|
|
110035
110061
|
} else {
|
|
@@ -110039,9 +110065,10 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
110039
110065
|
options.onStream("\n\n" + outputContent);
|
|
110040
110066
|
}
|
|
110041
110067
|
if (this.debug) {
|
|
110042
|
-
console.log(`[DEBUG] Appended ${
|
|
110068
|
+
console.log(`[DEBUG] Appended ${allOutputItems.length} output items (${outputContent.length} chars) to final result${options.schema ? " (with RAW_OUTPUT delimiters)" : ""}`);
|
|
110043
110069
|
}
|
|
110044
110070
|
this._outputBuffer.items = [];
|
|
110071
|
+
this._extractedRawBlocks = [];
|
|
110045
110072
|
}
|
|
110046
110073
|
return finalResult;
|
|
110047
110074
|
} catch (error2) {
|
package/package.json
CHANGED
package/src/agent/ProbeAgent.js
CHANGED
|
@@ -819,6 +819,11 @@ export class ProbeAgent {
|
|
|
819
819
|
// reset at the start of each answer() call
|
|
820
820
|
this._outputBuffer = { items: [] };
|
|
821
821
|
|
|
822
|
+
// Separate accumulator for extracted RAW_OUTPUT blocks from tool results.
|
|
823
|
+
// This is distinct from _outputBuffer to prevent the cycle where:
|
|
824
|
+
// formatSuccess wraps → extract re-adds → next execute_plan re-wraps (issue #438)
|
|
825
|
+
this._extractedRawBlocks = [];
|
|
826
|
+
|
|
822
827
|
const configOptions = {
|
|
823
828
|
sessionId: this.sessionId,
|
|
824
829
|
debug: this.debug,
|
|
@@ -2910,6 +2915,8 @@ Follow these instructions carefully:
|
|
|
2910
2915
|
// Both must preserve the output buffer so the parent call can append it.
|
|
2911
2916
|
if (this._outputBuffer && !options?._schemaFormatted && !options?._completionPromptProcessed) {
|
|
2912
2917
|
this._outputBuffer.items = [];
|
|
2918
|
+
// Also reset the extracted blocks accumulator (issue #438)
|
|
2919
|
+
this._extractedRawBlocks = [];
|
|
2913
2920
|
}
|
|
2914
2921
|
|
|
2915
2922
|
// START CHECKPOINT: Initialize task management for this request
|
|
@@ -3629,15 +3636,17 @@ Follow these instructions carefully:
|
|
|
3629
3636
|
|
|
3630
3637
|
let toolResultContent = typeof executionResult === 'string' ? executionResult : JSON.stringify(executionResult, null, 2);
|
|
3631
3638
|
|
|
3632
|
-
// Extract raw output blocks
|
|
3639
|
+
// Extract raw output blocks from tool result (before truncation)
|
|
3633
3640
|
// This prevents LLM from processing/hallucinating large structured output from execute_plan
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
+
// Push to _extractedRawBlocks (NOT _outputBuffer) to prevent the cycle where:
|
|
3642
|
+
// formatSuccess wraps → extract re-adds → next execute_plan re-wraps (issue #438)
|
|
3643
|
+
const { cleanedContent, extractedBlocks } = extractRawOutputBlocks(toolResultContent);
|
|
3644
|
+
if (extractedBlocks.length > 0) {
|
|
3645
|
+
toolResultContent = cleanedContent;
|
|
3646
|
+
// Accumulate extracted blocks separately from DSL output() buffer
|
|
3647
|
+
this._extractedRawBlocks.push(...extractedBlocks);
|
|
3648
|
+
if (this.debug) {
|
|
3649
|
+
console.log(`[DEBUG] Extracted ${extractedBlocks.length} raw output blocks (${extractedBlocks.reduce((sum, b) => sum + b.length, 0)} chars) from tool result`);
|
|
3641
3650
|
}
|
|
3642
3651
|
}
|
|
3643
3652
|
|
|
@@ -3887,15 +3896,17 @@ Follow these instructions carefully:
|
|
|
3887
3896
|
toolResultContent = toolResultContent.split(wsPrefix).join('');
|
|
3888
3897
|
}
|
|
3889
3898
|
|
|
3890
|
-
// Extract raw output blocks
|
|
3899
|
+
// Extract raw output blocks from tool result (before truncation)
|
|
3891
3900
|
// This prevents LLM from processing/hallucinating large structured output from execute_plan
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3901
|
+
// Push to _extractedRawBlocks (NOT _outputBuffer) to prevent the cycle where:
|
|
3902
|
+
// formatSuccess wraps → extract re-adds → next execute_plan re-wraps (issue #438)
|
|
3903
|
+
const { cleanedContent, extractedBlocks } = extractRawOutputBlocks(toolResultContent);
|
|
3904
|
+
if (extractedBlocks.length > 0) {
|
|
3905
|
+
toolResultContent = cleanedContent;
|
|
3906
|
+
// Accumulate extracted blocks separately from DSL output() buffer
|
|
3907
|
+
this._extractedRawBlocks.push(...extractedBlocks);
|
|
3908
|
+
if (this.debug) {
|
|
3909
|
+
console.log(`[DEBUG] Extracted ${extractedBlocks.length} raw output blocks (${extractedBlocks.reduce((sum, b) => sum + b.length, 0)} chars) from tool result`);
|
|
3899
3910
|
}
|
|
3900
3911
|
}
|
|
3901
3912
|
|
|
@@ -4314,16 +4325,18 @@ After reviewing, provide your final answer using attempt_completion.`;
|
|
|
4314
4325
|
|
|
4315
4326
|
// Make a follow-up call with the completion prompt
|
|
4316
4327
|
// Pass _completionPromptProcessed to prevent infinite loops
|
|
4317
|
-
// Save output
|
|
4328
|
+
// Save output buffers — the recursive answer() must not destroy DSL output() content
|
|
4318
4329
|
const savedOutputItems = this._outputBuffer ? [...this._outputBuffer.items] : [];
|
|
4330
|
+
const savedExtractedBlocks = this._extractedRawBlocks ? [...this._extractedRawBlocks] : [];
|
|
4319
4331
|
const completionResult = await this.answer(completionPromptMessage, [], {
|
|
4320
4332
|
...options,
|
|
4321
4333
|
_completionPromptProcessed: true
|
|
4322
4334
|
});
|
|
4323
|
-
// Restore output
|
|
4335
|
+
// Restore output buffers so the parent call can append them to the final result
|
|
4324
4336
|
if (this._outputBuffer) {
|
|
4325
4337
|
this._outputBuffer.items = savedOutputItems;
|
|
4326
4338
|
}
|
|
4339
|
+
this._extractedRawBlocks = savedExtractedBlocks;
|
|
4327
4340
|
|
|
4328
4341
|
// Update finalResult with the result from the completion prompt
|
|
4329
4342
|
finalResult = completionResult;
|
|
@@ -4383,7 +4396,8 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
4383
4396
|
// Call answer recursively with _schemaFormatted flag to prevent infinite loop
|
|
4384
4397
|
finalResult = await this.answer(schemaPrompt, [], {
|
|
4385
4398
|
...options,
|
|
4386
|
-
_schemaFormatted: true
|
|
4399
|
+
_schemaFormatted: true,
|
|
4400
|
+
_completionPromptProcessed: true // Prevent cascading completion prompts in retry calls
|
|
4387
4401
|
});
|
|
4388
4402
|
|
|
4389
4403
|
// Step 2: Validate and fix Mermaid diagrams if present (BEFORE cleaning schema)
|
|
@@ -4642,7 +4656,8 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
4642
4656
|
finalResult = await this.answer(schemaDefinitionPrompt, [], {
|
|
4643
4657
|
...options,
|
|
4644
4658
|
_schemaFormatted: true,
|
|
4645
|
-
_skipValidation: true // Skip validation in recursive correction calls to prevent loops
|
|
4659
|
+
_skipValidation: true, // Skip validation in recursive correction calls to prevent loops
|
|
4660
|
+
_completionPromptProcessed: true // Prevent cascading completion prompts in retry calls
|
|
4646
4661
|
});
|
|
4647
4662
|
finalResult = cleanSchemaResponse(finalResult);
|
|
4648
4663
|
validation = validateJsonResponse(finalResult);
|
|
@@ -4702,7 +4717,8 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
4702
4717
|
...options,
|
|
4703
4718
|
_schemaFormatted: true,
|
|
4704
4719
|
_skipValidation: true, // Skip validation in recursive correction calls to prevent loops
|
|
4705
|
-
_disableTools: true
|
|
4720
|
+
_disableTools: true, // Only allow attempt_completion - prevent AI from using search/query tools
|
|
4721
|
+
_completionPromptProcessed: true // Prevent cascading completion prompts in retry calls
|
|
4706
4722
|
});
|
|
4707
4723
|
finalResult = cleanSchemaResponse(finalResult);
|
|
4708
4724
|
|
|
@@ -4787,8 +4803,15 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
4787
4803
|
}
|
|
4788
4804
|
|
|
4789
4805
|
// Append DSL output buffer directly to response (bypasses LLM rewriting)
|
|
4790
|
-
|
|
4791
|
-
|
|
4806
|
+
// Skip during _completionPromptProcessed — only the parent answer() should append the buffer.
|
|
4807
|
+
// Combine _outputBuffer (from DSL output() calls) and _extractedRawBlocks (from tool results)
|
|
4808
|
+
// Using separate accumulators prevents the cycle described in issue #438.
|
|
4809
|
+
const allOutputItems = [
|
|
4810
|
+
...(this._outputBuffer?.items || []),
|
|
4811
|
+
...(this._extractedRawBlocks || [])
|
|
4812
|
+
];
|
|
4813
|
+
if (allOutputItems.length > 0 && !options._schemaFormatted && !options._completionPromptProcessed) {
|
|
4814
|
+
const outputContent = allOutputItems.join('\n\n');
|
|
4792
4815
|
if (options.schema) {
|
|
4793
4816
|
// Schema response — the finalResult is JSON. Wrap output in RAW_OUTPUT
|
|
4794
4817
|
// delimiters so clients (visor, etc.) can extract and propagate the
|
|
@@ -4801,9 +4824,10 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
4801
4824
|
options.onStream('\n\n' + outputContent);
|
|
4802
4825
|
}
|
|
4803
4826
|
if (this.debug) {
|
|
4804
|
-
console.log(`[DEBUG] Appended ${
|
|
4827
|
+
console.log(`[DEBUG] Appended ${allOutputItems.length} output items (${outputContent.length} chars) to final result${options.schema ? ' (with RAW_OUTPUT delimiters)' : ''}`);
|
|
4805
4828
|
}
|
|
4806
4829
|
this._outputBuffer.items = [];
|
|
4830
|
+
this._extractedRawBlocks = [];
|
|
4807
4831
|
}
|
|
4808
4832
|
|
|
4809
4833
|
return finalResult;
|
package/src/tools/executePlan.js
CHANGED
|
@@ -65,14 +65,28 @@ function stripCodeWrapping(code) {
|
|
|
65
65
|
return s.trim();
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Generate a unique session ID for this execute_plan invocation.
|
|
70
|
+
* Uses crypto.randomUUID if available, falls back to timestamp + random.
|
|
71
|
+
*/
|
|
72
|
+
function generatePlanSessionId(baseSessionId) {
|
|
73
|
+
const uniquePart = typeof crypto !== 'undefined' && crypto.randomUUID
|
|
74
|
+
? crypto.randomUUID().slice(0, 8)
|
|
75
|
+
: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
76
|
+
return `${baseSessionId || 'plan'}-${uniquePart}`;
|
|
77
|
+
}
|
|
78
|
+
|
|
68
79
|
/**
|
|
69
80
|
* Build DSL-compatible tool implementations from the agent's configOptions.
|
|
70
81
|
*
|
|
71
82
|
* @param {Object} configOptions - Agent config (sessionId, cwd, provider, model, etc.)
|
|
83
|
+
* @param {string} [planSessionId] - Unique session ID for this execute_plan invocation
|
|
72
84
|
* @returns {Object} toolImplementations for createDSLRuntime
|
|
73
85
|
*/
|
|
74
|
-
function buildToolImplementations(configOptions) {
|
|
75
|
-
const {
|
|
86
|
+
function buildToolImplementations(configOptions, planSessionId) {
|
|
87
|
+
const { cwd } = configOptions;
|
|
88
|
+
// Use planSessionId for isolated pagination per execute_plan, fall back to global sessionId
|
|
89
|
+
const sessionId = planSessionId || configOptions.sessionId;
|
|
76
90
|
const tools = {};
|
|
77
91
|
|
|
78
92
|
tools.search = {
|
|
@@ -311,9 +325,11 @@ export function createExecutePlanTool(options) {
|
|
|
311
325
|
|
|
312
326
|
/**
|
|
313
327
|
* Build or rebuild the DSL runtime.
|
|
314
|
-
* Called
|
|
328
|
+
* Called for each execute() invocation with a unique planSessionId.
|
|
329
|
+
*
|
|
330
|
+
* @param {string} [planSessionId] - Unique session ID for this execute_plan invocation
|
|
315
331
|
*/
|
|
316
|
-
function buildRuntime() {
|
|
332
|
+
function buildRuntime(planSessionId) {
|
|
317
333
|
const currentMcpBridge = getMcpBridge();
|
|
318
334
|
const currentMcpTools = getMcpTools();
|
|
319
335
|
|
|
@@ -340,7 +356,7 @@ export function createExecutePlanTool(options) {
|
|
|
340
356
|
// Agent configOptions — build everything from the agent's config
|
|
341
357
|
llmCallFn = llmCallFn || buildLLMCall(options);
|
|
342
358
|
runtimeOptions = {
|
|
343
|
-
toolImplementations: buildToolImplementations(options),
|
|
359
|
+
toolImplementations: buildToolImplementations(options, planSessionId),
|
|
344
360
|
llmCall: llmCallFn,
|
|
345
361
|
mcpBridge: currentMcpBridge,
|
|
346
362
|
mcpTools: filteredMcpTools,
|
|
@@ -360,6 +376,7 @@ export function createExecutePlanTool(options) {
|
|
|
360
376
|
|
|
361
377
|
/**
|
|
362
378
|
* Get or rebuild the runtime if MCP state has changed.
|
|
379
|
+
* @deprecated Use buildRuntime(planSessionId) directly for unique sessions per execution
|
|
363
380
|
*/
|
|
364
381
|
function getRuntime() {
|
|
365
382
|
const currentMcpBridge = getMcpBridge();
|
|
@@ -378,14 +395,22 @@ export function createExecutePlanTool(options) {
|
|
|
378
395
|
'Write simple synchronous-looking code — do NOT use async/await.',
|
|
379
396
|
parameters: executePlanSchema,
|
|
380
397
|
execute: async ({ code, description }) => {
|
|
398
|
+
// Generate a unique session ID for this execute_plan invocation
|
|
399
|
+
// This ensures search pagination is isolated per execute_plan call
|
|
400
|
+
const planSessionId = generatePlanSessionId(options.sessionId);
|
|
401
|
+
|
|
381
402
|
// Create top-level OTEL span for the entire execute_plan invocation
|
|
382
403
|
const planSpan = tracer?.createToolSpan?.('execute_plan', {
|
|
383
404
|
'dsl.description': description || '',
|
|
384
405
|
'dsl.code_length': code.length,
|
|
385
406
|
'dsl.code': code,
|
|
386
407
|
'dsl.max_retries': maxRetries,
|
|
408
|
+
'dsl.plan_session_id': planSessionId,
|
|
387
409
|
}) || null;
|
|
388
410
|
|
|
411
|
+
// Build runtime with the unique planSessionId for isolated search pagination
|
|
412
|
+
const planRuntime = buildRuntime(planSessionId);
|
|
413
|
+
|
|
389
414
|
// Strip XML tags and markdown fences LLMs sometimes wrap code in
|
|
390
415
|
let currentCode = stripCodeWrapping(code);
|
|
391
416
|
let lastError = null;
|
|
@@ -446,7 +471,7 @@ RULES REMINDER:
|
|
|
446
471
|
}
|
|
447
472
|
}
|
|
448
473
|
|
|
449
|
-
const result = await
|
|
474
|
+
const result = await planRuntime.execute(currentCode, description);
|
|
450
475
|
|
|
451
476
|
if (result.status === 'success') {
|
|
452
477
|
finalOutput = formatSuccess(result, description, attempt, outputBuffer);
|
|
@@ -574,8 +599,15 @@ function formatSuccess(result, description, attempt, outputBuffer) {
|
|
|
574
599
|
|
|
575
600
|
// Format the result value
|
|
576
601
|
const resultValue = result.result;
|
|
602
|
+
const hasOutputBufferContent = outputBuffer && outputBuffer.items && outputBuffer.items.length > 0;
|
|
577
603
|
if (resultValue === undefined || resultValue === null) {
|
|
578
|
-
|
|
604
|
+
if (hasOutputBufferContent) {
|
|
605
|
+
// output() was used but no return statement — tell LLM the script succeeded
|
|
606
|
+
const totalChars = outputBuffer.items.reduce((sum, item) => sum + item.length, 0);
|
|
607
|
+
output += `Plan completed successfully. Output captured (${totalChars} chars) via output() and will be included in the final response.`;
|
|
608
|
+
} else {
|
|
609
|
+
output += 'Plan completed (no return value).';
|
|
610
|
+
}
|
|
579
611
|
} else if (typeof resultValue === 'string') {
|
|
580
612
|
output += `Result:\n${resultValue}`;
|
|
581
613
|
} else {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|