@probelabs/probe 0.6.0-rc239 → 0.6.0-rc240

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.
@@ -49,6 +49,7 @@ import {
49
49
  delegateToolDefinition,
50
50
  analyzeAllToolDefinition,
51
51
  getExecutePlanToolDefinition,
52
+ getCleanupExecutePlanToolDefinition,
52
53
  bashToolDefinition,
53
54
  listFilesToolDefinition,
54
55
  searchFilesToolDefinition,
@@ -870,6 +871,10 @@ export class ProbeAgent {
870
871
  }
871
872
  if (this.enableExecutePlan && wrappedTools.executePlanToolInstance && isToolAllowed('execute_plan')) {
872
873
  this.toolImplementations.execute_plan = wrappedTools.executePlanToolInstance;
874
+ // cleanup_execute_plan is enabled together with execute_plan
875
+ if (wrappedTools.cleanupExecutePlanToolInstance && isToolAllowed('cleanup_execute_plan')) {
876
+ this.toolImplementations.cleanup_execute_plan = wrappedTools.cleanupExecutePlanToolInstance;
877
+ }
873
878
  } else if (wrappedTools.analyzeAllToolInstance && isToolAllowed('analyze_all')) {
874
879
  // analyze_all is fallback when execute_plan is not enabled
875
880
  this.toolImplementations.analyze_all = wrappedTools.analyzeAllToolInstance;
@@ -2582,6 +2587,10 @@ ${extractGuidance}
2582
2587
  if (isToolAllowed('listFiles')) dslFunctions.push('listFiles');
2583
2588
  if (this.enableBash && isToolAllowed('bash')) dslFunctions.push('bash');
2584
2589
  toolDefinitions += `${getExecutePlanToolDefinition(dslFunctions)}\n`;
2590
+ // cleanup_execute_plan is enabled together with execute_plan
2591
+ if (isToolAllowed('cleanup_execute_plan')) {
2592
+ toolDefinitions += `${getCleanupExecutePlanToolDefinition()}\n`;
2593
+ }
2585
2594
  } else if (isToolAllowed('analyze_all')) {
2586
2595
  // Fallback: only register analyze_all if execute_plan is not available
2587
2596
  toolDefinitions += `${analyzeAllToolDefinition}\n`;
@@ -2661,6 +2670,9 @@ The configuration is loaded from src/config.js lines 15-25 which contains the da
2661
2670
  }
2662
2671
  if (this.enableExecutePlan && isToolAllowed('execute_plan')) {
2663
2672
  availableToolsList += '- execute_plan: Execute a DSL program to orchestrate tool calls. ALWAYS use this for: questions containing "all"/"every"/"comprehensive"/"complete inventory", multi-topic analysis, open-ended discovery questions, or any task requiring full codebase coverage.\n';
2673
+ if (isToolAllowed('cleanup_execute_plan')) {
2674
+ availableToolsList += '- cleanup_execute_plan: Clean up output buffer and session store from previous execute_plan calls.\n';
2675
+ }
2664
2676
  } else if (isToolAllowed('analyze_all')) {
2665
2677
  availableToolsList += '- analyze_all: Process ALL data matching a query using map-reduce (for aggregate questions needing 100% coverage).\n';
2666
2678
  }
@@ -2891,8 +2903,10 @@ Follow these instructions carefully:
2891
2903
  // Track initial history length for storage
2892
2904
  const oldHistoryLength = this.history.length;
2893
2905
 
2894
- // Reset output buffer for this answer() call
2895
- if (this._outputBuffer) {
2906
+ // Reset output buffer for this answer() call — but NOT during schema correction recursion
2907
+ // When _schemaFormatted is true, this is a recursive call to fix JSON formatting,
2908
+ // and we must preserve the output buffer so the parent call can append it
2909
+ if (this._outputBuffer && !options?._schemaFormatted) {
2896
2910
  this._outputBuffer.items = [];
2897
2911
  }
2898
2912
 
@@ -3411,6 +3425,10 @@ Follow these instructions carefully:
3411
3425
  // Execute Plan tool (requires enableExecutePlan flag, supersedes analyze_all)
3412
3426
  if (this.enableExecutePlan && this.allowedTools.isEnabled('execute_plan')) {
3413
3427
  validTools.push('execute_plan');
3428
+ // cleanup_execute_plan is enabled together with execute_plan
3429
+ if (this.allowedTools.isEnabled('cleanup_execute_plan')) {
3430
+ validTools.push('cleanup_execute_plan');
3431
+ }
3414
3432
  } else if (this.allowedTools.isEnabled('analyze_all')) {
3415
3433
  validTools.push('analyze_all');
3416
3434
  }
@@ -9905,7 +9905,7 @@ function resolveTargetPath(target, cwd) {
9905
9905
  }
9906
9906
  return filePart + suffix;
9907
9907
  }
9908
- var searchSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, bashSchema, analyzeAllSchema, executePlanSchema, attemptCompletionSchema, searchToolDefinition, queryToolDefinition, extractToolDefinition, delegateToolDefinition, attemptCompletionToolDefinition, analyzeAllToolDefinition, bashToolDefinition, googleSearchToolDefinition, urlContextToolDefinition, searchDescription, queryDescription, extractDescription, delegateDescription, analyzeAllDescription, DEFAULT_VALID_TOOLS;
9908
+ var searchSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, bashSchema, analyzeAllSchema, executePlanSchema, cleanupExecutePlanSchema, attemptCompletionSchema, searchToolDefinition, queryToolDefinition, extractToolDefinition, delegateToolDefinition, attemptCompletionToolDefinition, analyzeAllToolDefinition, bashToolDefinition, googleSearchToolDefinition, urlContextToolDefinition, searchDescription, queryDescription, extractDescription, delegateDescription, analyzeAllDescription, DEFAULT_VALID_TOOLS;
9909
9909
  var init_common = __esm({
9910
9910
  "src/tools/common.js"() {
9911
9911
  "use strict";
@@ -9953,6 +9953,10 @@ var init_common = __esm({
9953
9953
  code: external_exports.string().min(1).describe("JavaScript DSL code to execute. All function calls look synchronous \u2014 do NOT use async/await. Use map(items, fn) for batch operations. Use LLM(instruction, data) for AI processing."),
9954
9954
  description: external_exports.string().optional().describe("Human-readable description of what this plan does, for logging.")
9955
9955
  });
9956
+ cleanupExecutePlanSchema = external_exports.object({
9957
+ clearOutputBuffer: external_exports.boolean().optional().default(true).describe("Clear the output buffer from previous execute_plan calls"),
9958
+ clearSessionStore: external_exports.boolean().optional().default(false).describe("Clear the session store (persisted data across execute_plan calls)")
9959
+ });
9956
9960
  attemptCompletionSchema = {
9957
9961
  // Custom validation that requires result parameter but allows direct XML response
9958
9962
  safeParse: (params) => {
@@ -10300,6 +10304,7 @@ Capabilities:
10300
10304
  "delegate",
10301
10305
  "analyze_all",
10302
10306
  "execute_plan",
10307
+ "cleanup_execute_plan",
10303
10308
  "listSkills",
10304
10309
  "useSkill",
10305
10310
  "listFiles",
@@ -29086,6 +29091,14 @@ Logs: ${result.logs.join(" | ")}` : "";
29086
29091
  "dsl.error": lastError.substring(0, 1e3)
29087
29092
  });
29088
29093
  }
29094
+ if (outputBuffer && outputBuffer.items && outputBuffer.items.length > 0) {
29095
+ const clearedChars = outputBuffer.items.reduce((sum, item) => sum + item.length, 0);
29096
+ outputBuffer.items = [];
29097
+ planSpan?.addEvent?.("dsl.auto_cleanup", {
29098
+ "cleanup.chars_cleared": clearedChars,
29099
+ "cleanup.reason": "all_retries_exhausted"
29100
+ });
29101
+ }
29089
29102
  finalOutput = `Plan execution failed after ${maxRetries} retries.
29090
29103
 
29091
29104
  Last error: ${lastError}`;
@@ -29098,6 +29111,9 @@ Last error: ${lastError}`;
29098
29111
  planSpan?.end?.();
29099
29112
  return finalOutput;
29100
29113
  } catch (e) {
29114
+ if (outputBuffer && outputBuffer.items && outputBuffer.items.length > 0) {
29115
+ outputBuffer.items = [];
29116
+ }
29101
29117
  planSpan?.setStatus?.("ERROR");
29102
29118
  planSpan?.addEvent?.("exception", {
29103
29119
  "exception.message": e.message,
@@ -29541,6 +29557,62 @@ output(table);
29541
29557
  return "Generated table with " + results.length + " items.";
29542
29558
  \`\`\``;
29543
29559
  }
29560
+ function createCleanupExecutePlanTool(options) {
29561
+ const { outputBuffer, sessionStore, tracer } = options;
29562
+ return tool4({
29563
+ description: "Clean up output buffer and session store from previous execute_plan calls. Use this when a previous execute_plan failed and left stale data, or before starting a fresh analysis.",
29564
+ parameters: cleanupExecutePlanSchema,
29565
+ execute: async ({ clearOutputBuffer = true, clearSessionStore = false }) => {
29566
+ const span = tracer?.createToolSpan?.("cleanup_execute_plan", {
29567
+ "cleanup.clear_output_buffer": clearOutputBuffer,
29568
+ "cleanup.clear_session_store": clearSessionStore
29569
+ }) || null;
29570
+ const results = [];
29571
+ try {
29572
+ if (clearOutputBuffer && outputBuffer) {
29573
+ const itemCount = outputBuffer.items?.length || 0;
29574
+ const charCount = outputBuffer.items?.reduce((sum, item) => sum + item.length, 0) || 0;
29575
+ outputBuffer.items = [];
29576
+ results.push(`Output buffer cleared (${itemCount} items, ${charCount} chars)`);
29577
+ }
29578
+ if (clearSessionStore && sessionStore) {
29579
+ const keyCount = Object.keys(sessionStore).length;
29580
+ for (const key of Object.keys(sessionStore)) {
29581
+ delete sessionStore[key];
29582
+ }
29583
+ results.push(`Session store cleared (${keyCount} keys)`);
29584
+ }
29585
+ const output = results.length > 0 ? `Cleanup complete:
29586
+ - ${results.join("\n- ")}` : "Nothing to clean up";
29587
+ span?.setAttributes?.({
29588
+ "cleanup.result": output,
29589
+ "cleanup.success": true
29590
+ });
29591
+ span?.setStatus?.("OK");
29592
+ span?.end?.();
29593
+ return output;
29594
+ } catch (e) {
29595
+ span?.setStatus?.("ERROR");
29596
+ span?.addEvent?.("exception", { "exception.message": e.message });
29597
+ span?.end?.();
29598
+ return `Cleanup failed: ${e.message}`;
29599
+ }
29600
+ }
29601
+ });
29602
+ }
29603
+ function getCleanupExecutePlanToolDefinition() {
29604
+ return `## cleanup_execute_plan
29605
+ Description: Clean up output buffer and session store from previous execute_plan calls. Use when a previous execute_plan failed and left stale data, or before starting a fresh analysis.
29606
+
29607
+ Parameters:
29608
+ - clearOutputBuffer: (optional, default: true) Clear the output buffer from previous execute_plan calls
29609
+ - clearSessionStore: (optional, default: false) Clear the session store (persisted data across execute_plan calls)
29610
+
29611
+ Example:
29612
+ <cleanup_execute_plan>
29613
+ <clearOutputBuffer>true</clearOutputBuffer>
29614
+ </cleanup_execute_plan>`;
29615
+ }
29544
29616
  var init_executePlan = __esm({
29545
29617
  "src/tools/executePlan.js"() {
29546
29618
  "use strict";
@@ -30336,6 +30408,13 @@ function createWrappedTools(baseTools) {
30336
30408
  baseTools.executePlanTool.execute
30337
30409
  );
30338
30410
  }
30411
+ if (baseTools.cleanupExecutePlanTool) {
30412
+ wrappedTools.cleanupExecutePlanToolInstance = wrapToolWithEmitter(
30413
+ baseTools.cleanupExecutePlanTool,
30414
+ "cleanup_execute_plan",
30415
+ baseTools.cleanupExecutePlanTool.execute
30416
+ );
30417
+ }
30339
30418
  if (baseTools.bashTool) {
30340
30419
  wrappedTools.bashToolInstance = wrapToolWithEmitter(
30341
30420
  baseTools.bashTool,
@@ -31245,6 +31324,9 @@ function createTools(configOptions) {
31245
31324
  }
31246
31325
  if (configOptions.enableExecutePlan && isToolAllowed("execute_plan")) {
31247
31326
  tools2.executePlanTool = createExecutePlanTool(configOptions);
31327
+ if (isToolAllowed("cleanup_execute_plan")) {
31328
+ tools2.cleanupExecutePlanTool = createCleanupExecutePlanTool(configOptions);
31329
+ }
31248
31330
  } else if (isToolAllowed("analyze_all")) {
31249
31331
  tools2.analyzeAllTool = analyzeAllTool(configOptions);
31250
31332
  }
@@ -68657,15 +68739,31 @@ function isSimpleTextWrapperSchema(schema) {
68657
68739
  return null;
68658
68740
  }
68659
68741
  const trimmed = schema.trim();
68660
- const simplePatterns = [
68661
- /^\{\s*["']?(\w+)["']?\s*:\s*["']?string["']?\s*\}$/i,
68662
- /^\{\s*["']?type["']?\s*:\s*["']?object["']?\s*,\s*["']?properties["']?\s*:\s*\{\s*["']?(\w+)["']?\s*:\s*\{\s*["']?type["']?\s*:\s*["']?string["']?\s*\}\s*\}\s*\}$/i
68663
- ];
68664
- for (const pattern of simplePatterns) {
68665
- const match2 = trimmed.match(pattern);
68666
- if (match2) {
68667
- return { fieldName: match2[1] };
68742
+ try {
68743
+ const parsed = JSON.parse(trimmed);
68744
+ if (typeof parsed !== "object" || parsed === null) {
68745
+ } else {
68746
+ const keys2 = Object.keys(parsed);
68747
+ if (keys2.length === 1 && parsed[keys2[0]] === "string") {
68748
+ return { fieldName: keys2[0] };
68749
+ }
68750
+ if (parsed.type === "object" && parsed.properties) {
68751
+ const propKeys = Object.keys(parsed.properties);
68752
+ if (propKeys.length === 1) {
68753
+ const prop = parsed.properties[propKeys[0]];
68754
+ if (prop && prop.type === "string") {
68755
+ return { fieldName: propKeys[0] };
68756
+ }
68757
+ }
68758
+ }
68759
+ return null;
68668
68760
  }
68761
+ } catch {
68762
+ }
68763
+ const simplePattern = /^\{\s*["']?(\w+)["']?\s*:\s*["']?string["']?\s*\}$/i;
68764
+ const match2 = trimmed.match(simplePattern);
68765
+ if (match2) {
68766
+ return { fieldName: match2[1] };
68669
68767
  }
68670
68768
  return null;
68671
68769
  }
@@ -81528,6 +81626,9 @@ var init_ProbeAgent = __esm({
81528
81626
  }
81529
81627
  if (this.enableExecutePlan && wrappedTools.executePlanToolInstance && isToolAllowed("execute_plan")) {
81530
81628
  this.toolImplementations.execute_plan = wrappedTools.executePlanToolInstance;
81629
+ if (wrappedTools.cleanupExecutePlanToolInstance && isToolAllowed("cleanup_execute_plan")) {
81630
+ this.toolImplementations.cleanup_execute_plan = wrappedTools.cleanupExecutePlanToolInstance;
81631
+ }
81531
81632
  } else if (wrappedTools.analyzeAllToolInstance && isToolAllowed("analyze_all")) {
81532
81633
  this.toolImplementations.analyze_all = wrappedTools.analyzeAllToolInstance;
81533
81634
  }
@@ -82901,6 +83002,10 @@ Workspace: ${this.allowedFolders.join(", ")}`;
82901
83002
  if (this.enableBash && isToolAllowed("bash")) dslFunctions.push("bash");
82902
83003
  toolDefinitions += `${getExecutePlanToolDefinition(dslFunctions)}
82903
83004
  `;
83005
+ if (isToolAllowed("cleanup_execute_plan")) {
83006
+ toolDefinitions += `${getCleanupExecutePlanToolDefinition()}
83007
+ `;
83008
+ }
82904
83009
  } else if (isToolAllowed("analyze_all")) {
82905
83010
  toolDefinitions += `${analyzeAllToolDefinition}
82906
83011
  `;
@@ -82976,6 +83081,9 @@ The configuration is loaded from src/config.js lines 15-25 which contains the da
82976
83081
  }
82977
83082
  if (this.enableExecutePlan && isToolAllowed("execute_plan")) {
82978
83083
  availableToolsList += '- execute_plan: Execute a DSL program to orchestrate tool calls. ALWAYS use this for: questions containing "all"/"every"/"comprehensive"/"complete inventory", multi-topic analysis, open-ended discovery questions, or any task requiring full codebase coverage.\n';
83084
+ if (isToolAllowed("cleanup_execute_plan")) {
83085
+ availableToolsList += "- cleanup_execute_plan: Clean up output buffer and session store from previous execute_plan calls.\n";
83086
+ }
82979
83087
  } else if (isToolAllowed("analyze_all")) {
82980
83088
  availableToolsList += "- analyze_all: Process ALL data matching a query using map-reduce (for aggregate questions needing 100% coverage).\n";
82981
83089
  }
@@ -83202,7 +83310,7 @@ You are working with a workspace. Available paths: ${workspaceDesc}
83202
83310
  }
83203
83311
  try {
83204
83312
  const oldHistoryLength = this.history.length;
83205
- if (this._outputBuffer) {
83313
+ if (this._outputBuffer && !options?._schemaFormatted) {
83206
83314
  this._outputBuffer.items = [];
83207
83315
  }
83208
83316
  if (this.enableTasks) {
@@ -83562,6 +83670,9 @@ You are working with a workspace. Available paths: ${workspaceDesc}
83562
83670
  }
83563
83671
  if (this.enableExecutePlan && this.allowedTools.isEnabled("execute_plan")) {
83564
83672
  validTools.push("execute_plan");
83673
+ if (this.allowedTools.isEnabled("cleanup_execute_plan")) {
83674
+ validTools.push("cleanup_execute_plan");
83675
+ }
83565
83676
  } else if (this.allowedTools.isEnabled("analyze_all")) {
83566
83677
  validTools.push("analyze_all");
83567
83678
  }
@@ -220,6 +220,15 @@ export function createWrappedTools(baseTools) {
220
220
  );
221
221
  }
222
222
 
223
+ // Wrap cleanup_execute_plan tool
224
+ if (baseTools.cleanupExecutePlanTool) {
225
+ wrappedTools.cleanupExecutePlanToolInstance = wrapToolWithEmitter(
226
+ baseTools.cleanupExecutePlanTool,
227
+ 'cleanup_execute_plan',
228
+ baseTools.cleanupExecutePlanTool.execute
229
+ );
230
+ }
231
+
223
232
  // Wrap bash tool
224
233
  if (baseTools.bashTool) {
225
234
  wrappedTools.bashToolInstance = wrapToolWithEmitter(
@@ -784,18 +784,42 @@ export function isSimpleTextWrapperSchema(schema) {
784
784
 
785
785
  const trimmed = schema.trim();
786
786
 
787
- // Match patterns like: {text: string}, {"text": "string"}, {response: string}, etc.
788
- // These are simple wrappers that just need a single text field
789
- const simplePatterns = [
790
- /^\{\s*["']?(\w+)["']?\s*:\s*["']?string["']?\s*\}$/i,
791
- /^\{\s*["']?type["']?\s*:\s*["']?object["']?\s*,\s*["']?properties["']?\s*:\s*\{\s*["']?(\w+)["']?\s*:\s*\{\s*["']?type["']?\s*:\s*["']?string["']?\s*\}\s*\}\s*\}$/i
792
- ];
787
+ // First, try parsing as JSON for full JSON Schema format
788
+ try {
789
+ const parsed = JSON.parse(trimmed);
790
+ if (typeof parsed !== 'object' || parsed === null) {
791
+ // Fall through to regex matching
792
+ } else {
793
+ // Shorthand JSON format: {"text": "string"} or {"fieldName": "string"}
794
+ const keys = Object.keys(parsed);
795
+ if (keys.length === 1 && parsed[keys[0]] === 'string') {
796
+ return { fieldName: keys[0] };
797
+ }
793
798
 
794
- for (const pattern of simplePatterns) {
795
- const match = trimmed.match(pattern);
796
- if (match) {
797
- return { fieldName: match[1] };
799
+ // Full JSON Schema format: {"type":"object","properties":{"text":{"type":"string",...}}}
800
+ // Handles schemas with "required", "description", and other extra fields
801
+ if (parsed.type === 'object' && parsed.properties) {
802
+ const propKeys = Object.keys(parsed.properties);
803
+ if (propKeys.length === 1) {
804
+ const prop = parsed.properties[propKeys[0]];
805
+ if (prop && prop.type === 'string') {
806
+ return { fieldName: propKeys[0] };
807
+ }
808
+ }
809
+ }
810
+
811
+ // Valid JSON but not a simple wrapper
812
+ return null;
798
813
  }
814
+ } catch {
815
+ // Not valid JSON, fall through to regex matching
816
+ }
817
+
818
+ // Fallback: regex matching for shorthand formats like {text: string}, {'text': 'string'}
819
+ const simplePattern = /^\{\s*["']?(\w+)["']?\s*:\s*["']?string["']?\s*\}$/i;
820
+ const match = trimmed.match(simplePattern);
821
+ if (match) {
822
+ return { fieldName: match[1] };
799
823
  }
800
824
 
801
825
  return null;
@@ -6,6 +6,7 @@ import {
6
6
  delegateTool,
7
7
  analyzeAllTool,
8
8
  createExecutePlanTool,
9
+ createCleanupExecutePlanTool,
9
10
  bashTool,
10
11
  editTool,
11
12
  createTool,
@@ -18,6 +19,7 @@ import {
18
19
  delegateSchema,
19
20
  analyzeAllSchema,
20
21
  executePlanSchema,
22
+ cleanupExecutePlanSchema,
21
23
  bashSchema,
22
24
  editSchema,
23
25
  createSchema,
@@ -27,6 +29,7 @@ import {
27
29
  delegateToolDefinition,
28
30
  analyzeAllToolDefinition,
29
31
  getExecutePlanToolDefinition,
32
+ getCleanupExecutePlanToolDefinition,
30
33
  bashToolDefinition,
31
34
  editToolDefinition,
32
35
  createToolDefinition,
@@ -63,6 +66,10 @@ export function createTools(configOptions) {
63
66
  }
64
67
  if (configOptions.enableExecutePlan && isToolAllowed('execute_plan')) {
65
68
  tools.executePlanTool = createExecutePlanTool(configOptions);
69
+ // cleanup_execute_plan is enabled together with execute_plan
70
+ if (isToolAllowed('cleanup_execute_plan')) {
71
+ tools.cleanupExecutePlanTool = createCleanupExecutePlanTool(configOptions);
72
+ }
66
73
  } else if (isToolAllowed('analyze_all')) {
67
74
  // analyze_all is fallback when execute_plan is not enabled
68
75
  tools.analyzeAllTool = analyzeAllTool(configOptions);
@@ -104,6 +111,7 @@ export {
104
111
  delegateSchema,
105
112
  analyzeAllSchema,
106
113
  executePlanSchema,
114
+ cleanupExecutePlanSchema,
107
115
  bashSchema,
108
116
  editSchema,
109
117
  createSchema,
@@ -114,6 +122,7 @@ export {
114
122
  delegateToolDefinition,
115
123
  analyzeAllToolDefinition,
116
124
  getExecutePlanToolDefinition,
125
+ getCleanupExecutePlanToolDefinition,
117
126
  bashToolDefinition,
118
127
  editToolDefinition,
119
128
  createToolDefinition,
package/build/index.js CHANGED
@@ -27,6 +27,7 @@ import {
27
27
  delegateSchema,
28
28
  analyzeAllSchema,
29
29
  executePlanSchema,
30
+ cleanupExecutePlanSchema,
30
31
  attemptCompletionSchema,
31
32
  bashSchema,
32
33
  searchToolDefinition,
@@ -47,7 +48,7 @@ import {
47
48
  createToolDefinition
48
49
  } from './tools/edit.js';
49
50
  import { searchTool, queryTool, extractTool, delegateTool, analyzeAllTool } from './tools/vercel.js';
50
- import { createExecutePlanTool, getExecutePlanToolDefinition } from './tools/executePlan.js';
51
+ import { createExecutePlanTool, getExecutePlanToolDefinition, createCleanupExecutePlanTool, getCleanupExecutePlanToolDefinition } from './tools/executePlan.js';
51
52
  import { bashTool } from './tools/bash.js';
52
53
  import { editTool, createTool } from './tools/edit.js';
53
54
  import { ProbeAgent } from './agent/ProbeAgent.js';
@@ -93,6 +94,7 @@ export {
93
94
  delegateTool,
94
95
  analyzeAllTool,
95
96
  createExecutePlanTool,
97
+ createCleanupExecutePlanTool,
96
98
  bashTool,
97
99
  editTool,
98
100
  createTool,
@@ -106,6 +108,7 @@ export {
106
108
  delegateSchema,
107
109
  analyzeAllSchema,
108
110
  executePlanSchema,
111
+ cleanupExecutePlanSchema,
109
112
  attemptCompletionSchema,
110
113
  bashSchema,
111
114
  editSchema,
@@ -117,6 +120,7 @@ export {
117
120
  delegateToolDefinition,
118
121
  analyzeAllToolDefinition,
119
122
  getExecutePlanToolDefinition,
123
+ getCleanupExecutePlanToolDefinition,
120
124
  attemptCompletionToolDefinition,
121
125
  bashToolDefinition,
122
126
  editToolDefinition,
@@ -59,6 +59,11 @@ export const executePlanSchema = z.object({
59
59
  description: z.string().optional().describe('Human-readable description of what this plan does, for logging.')
60
60
  });
61
61
 
62
+ export const cleanupExecutePlanSchema = z.object({
63
+ clearOutputBuffer: z.boolean().optional().default(true).describe('Clear the output buffer from previous execute_plan calls'),
64
+ clearSessionStore: z.boolean().optional().default(false).describe('Clear the session store (persisted data across execute_plan calls)')
65
+ });
66
+
62
67
  // Schema for the attempt_completion tool - flexible validation for direct XML response
63
68
  export const attemptCompletionSchema = {
64
69
  // Custom validation that requires result parameter but allows direct XML response
@@ -431,6 +436,7 @@ export const DEFAULT_VALID_TOOLS = [
431
436
  'delegate',
432
437
  'analyze_all',
433
438
  'execute_plan',
439
+ 'cleanup_execute_plan',
434
440
  'listSkills',
435
441
  'useSkill',
436
442
  'listFiles',
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import { tool } from 'ai';
9
- import { executePlanSchema, parseAndResolvePaths } from './common.js';
9
+ import { executePlanSchema, cleanupExecutePlanSchema, parseAndResolvePaths } from './common.js';
10
10
  import { createDSLRuntime } from '../agent/dsl/runtime.js';
11
11
  import { search } from '../search.js';
12
12
  import { query } from '../query.js';
@@ -347,7 +347,16 @@ RULES REMINDER:
347
347
  });
348
348
  }
349
349
 
350
- // All retries exhausted
350
+ // All retries exhausted — auto-cleanup output buffer to prevent stale data
351
+ if (outputBuffer && outputBuffer.items && outputBuffer.items.length > 0) {
352
+ const clearedChars = outputBuffer.items.reduce((sum, item) => sum + item.length, 0);
353
+ outputBuffer.items = [];
354
+ planSpan?.addEvent?.('dsl.auto_cleanup', {
355
+ 'cleanup.chars_cleared': clearedChars,
356
+ 'cleanup.reason': 'all_retries_exhausted',
357
+ });
358
+ }
359
+
351
360
  finalOutput = `Plan execution failed after ${maxRetries} retries.\n\nLast error: ${lastError}`;
352
361
  planSpan?.setAttributes?.({
353
362
  'dsl.result': 'all_retries_exhausted',
@@ -358,6 +367,11 @@ RULES REMINDER:
358
367
  planSpan?.end?.();
359
368
  return finalOutput;
360
369
  } catch (e) {
370
+ // Auto-cleanup output buffer on exception to prevent stale data
371
+ if (outputBuffer && outputBuffer.items && outputBuffer.items.length > 0) {
372
+ outputBuffer.items = [];
373
+ }
374
+
361
375
  planSpan?.setStatus?.('ERROR');
362
376
  planSpan?.addEvent?.('exception', {
363
377
  'exception.message': e.message,
@@ -808,3 +822,87 @@ output(table);
808
822
  return "Generated table with " + results.length + " items.";
809
823
  \`\`\``;
810
824
  }
825
+
826
+ /**
827
+ * Create the cleanup_execute_plan tool for the Vercel AI SDK.
828
+ *
829
+ * Cleans up output buffer and optionally session store from previous
830
+ * failed or interrupted execute_plan calls.
831
+ *
832
+ * @param {Object} options
833
+ * @param {Object} [options.outputBuffer] - Output buffer to clear
834
+ * @param {Object} [options.sessionStore] - Session store to clear
835
+ * @param {Object} [options.tracer] - OTEL tracer for tracing
836
+ * @returns {Object} Vercel AI SDK tool
837
+ */
838
+ export function createCleanupExecutePlanTool(options) {
839
+ const { outputBuffer, sessionStore, tracer } = options;
840
+
841
+ return tool({
842
+ description: 'Clean up output buffer and session store from previous execute_plan calls. ' +
843
+ 'Use this when a previous execute_plan failed and left stale data, or before starting a fresh analysis.',
844
+ parameters: cleanupExecutePlanSchema,
845
+ execute: async ({ clearOutputBuffer = true, clearSessionStore = false }) => {
846
+ const span = tracer?.createToolSpan?.('cleanup_execute_plan', {
847
+ 'cleanup.clear_output_buffer': clearOutputBuffer,
848
+ 'cleanup.clear_session_store': clearSessionStore,
849
+ }) || null;
850
+
851
+ const results = [];
852
+
853
+ try {
854
+ if (clearOutputBuffer && outputBuffer) {
855
+ const itemCount = outputBuffer.items?.length || 0;
856
+ const charCount = outputBuffer.items?.reduce((sum, item) => sum + item.length, 0) || 0;
857
+ outputBuffer.items = [];
858
+ results.push(`Output buffer cleared (${itemCount} items, ${charCount} chars)`);
859
+ }
860
+
861
+ if (clearSessionStore && sessionStore) {
862
+ const keyCount = Object.keys(sessionStore).length;
863
+ for (const key of Object.keys(sessionStore)) {
864
+ delete sessionStore[key];
865
+ }
866
+ results.push(`Session store cleared (${keyCount} keys)`);
867
+ }
868
+
869
+ const output = results.length > 0
870
+ ? `Cleanup complete:\n- ${results.join('\n- ')}`
871
+ : 'Nothing to clean up';
872
+
873
+ span?.setAttributes?.({
874
+ 'cleanup.result': output,
875
+ 'cleanup.success': true,
876
+ });
877
+ span?.setStatus?.('OK');
878
+ span?.end?.();
879
+
880
+ return output;
881
+ } catch (e) {
882
+ span?.setStatus?.('ERROR');
883
+ span?.addEvent?.('exception', { 'exception.message': e.message });
884
+ span?.end?.();
885
+ return `Cleanup failed: ${e.message}`;
886
+ }
887
+ },
888
+ });
889
+ }
890
+
891
+ /**
892
+ * XML tool definition for cleanup_execute_plan.
893
+ *
894
+ * @returns {string} Tool definition text
895
+ */
896
+ export function getCleanupExecutePlanToolDefinition() {
897
+ return `## cleanup_execute_plan
898
+ Description: Clean up output buffer and session store from previous execute_plan calls. Use when a previous execute_plan failed and left stale data, or before starting a fresh analysis.
899
+
900
+ Parameters:
901
+ - clearOutputBuffer: (optional, default: true) Clear the output buffer from previous execute_plan calls
902
+ - clearSessionStore: (optional, default: false) Clear the session store (persisted data across execute_plan calls)
903
+
904
+ Example:
905
+ <cleanup_execute_plan>
906
+ <clearOutputBuffer>true</clearOutputBuffer>
907
+ </cleanup_execute_plan>`;
908
+ }
@@ -11,8 +11,8 @@ export { editTool, createTool } from './edit.js';
11
11
  // Export LangChain tools
12
12
  export { createSearchTool, createQueryTool, createExtractTool } from './langchain.js';
13
13
 
14
- // Export execute_plan tool
15
- export { createExecutePlanTool, getExecutePlanToolDefinition } from './executePlan.js';
14
+ // Export execute_plan and cleanup_execute_plan tools
15
+ export { createExecutePlanTool, getExecutePlanToolDefinition, createCleanupExecutePlanTool, getCleanupExecutePlanToolDefinition } from './executePlan.js';
16
16
 
17
17
  // Export common schemas and utilities
18
18
  export {
@@ -22,6 +22,7 @@ export {
22
22
  delegateSchema,
23
23
  bashSchema,
24
24
  executePlanSchema,
25
+ cleanupExecutePlanSchema,
25
26
  delegateDescription,
26
27
  delegateToolDefinition,
27
28
  bashDescription,