@probelabs/probe 0.6.0-rc239 → 0.6.0-rc241

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/bin/binaries/probe-v0.6.0-rc241-aarch64-apple-darwin.tar.gz +0 -0
  2. package/bin/binaries/{probe-v0.6.0-rc239-aarch64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc241-aarch64-unknown-linux-musl.tar.gz} +0 -0
  3. package/bin/binaries/probe-v0.6.0-rc241-x86_64-apple-darwin.tar.gz +0 -0
  4. package/bin/binaries/probe-v0.6.0-rc241-x86_64-pc-windows-msvc.zip +0 -0
  5. package/bin/binaries/probe-v0.6.0-rc241-x86_64-unknown-linux-musl.tar.gz +0 -0
  6. package/build/agent/ProbeAgent.js +20 -2
  7. package/build/agent/dsl/validator.js +99 -8
  8. package/build/agent/index.js +213 -22
  9. package/build/agent/probeTool.js +9 -0
  10. package/build/agent/schemaUtils.js +34 -10
  11. package/build/agent/tools.js +9 -0
  12. package/build/index.js +5 -1
  13. package/build/tools/common.js +6 -0
  14. package/build/tools/executePlan.js +136 -2
  15. package/build/tools/index.js +3 -2
  16. package/cjs/agent/ProbeAgent.cjs +213 -22
  17. package/cjs/index.cjs +219 -19
  18. package/package.json +1 -1
  19. package/src/agent/ProbeAgent.js +20 -2
  20. package/src/agent/dsl/validator.js +99 -8
  21. package/src/agent/probeTool.js +9 -0
  22. package/src/agent/schemaUtils.js +34 -10
  23. package/src/agent/tools.js +9 -0
  24. package/src/index.js +5 -1
  25. package/src/tools/common.js +6 -0
  26. package/src/tools/executePlan.js +136 -2
  27. package/src/tools/index.js +3 -2
  28. package/bin/binaries/probe-v0.6.0-rc239-aarch64-apple-darwin.tar.gz +0 -0
  29. package/bin/binaries/probe-v0.6.0-rc239-x86_64-apple-darwin.tar.gz +0 -0
  30. package/bin/binaries/probe-v0.6.0-rc239-x86_64-pc-windows-msvc.zip +0 -0
  31. package/bin/binaries/probe-v0.6.0-rc239-x86_64-unknown-linux-musl.tar.gz +0 -0
package/cjs/index.cjs CHANGED
@@ -38518,7 +38518,7 @@ function resolveTargetPath(target, cwd) {
38518
38518
  }
38519
38519
  return filePart + suffix;
38520
38520
  }
38521
- var import_path6, searchSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, bashSchema, analyzeAllSchema, executePlanSchema, attemptCompletionSchema, searchToolDefinition, queryToolDefinition, extractToolDefinition, delegateToolDefinition, attemptCompletionToolDefinition, analyzeAllToolDefinition, bashToolDefinition, googleSearchToolDefinition, urlContextToolDefinition, searchDescription, queryDescription, extractDescription, delegateDescription, bashDescription, analyzeAllDescription, DEFAULT_VALID_TOOLS;
38521
+ var import_path6, searchSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, bashSchema, analyzeAllSchema, executePlanSchema, cleanupExecutePlanSchema, attemptCompletionSchema, searchToolDefinition, queryToolDefinition, extractToolDefinition, delegateToolDefinition, attemptCompletionToolDefinition, analyzeAllToolDefinition, bashToolDefinition, googleSearchToolDefinition, urlContextToolDefinition, searchDescription, queryDescription, extractDescription, delegateDescription, bashDescription, analyzeAllDescription, DEFAULT_VALID_TOOLS;
38522
38522
  var init_common2 = __esm({
38523
38523
  "src/tools/common.js"() {
38524
38524
  "use strict";
@@ -38567,6 +38567,10 @@ var init_common2 = __esm({
38567
38567
  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."),
38568
38568
  description: external_exports.string().optional().describe("Human-readable description of what this plan does, for logging.")
38569
38569
  });
38570
+ cleanupExecutePlanSchema = external_exports.object({
38571
+ clearOutputBuffer: external_exports.boolean().optional().default(true).describe("Clear the output buffer from previous execute_plan calls"),
38572
+ clearSessionStore: external_exports.boolean().optional().default(false).describe("Clear the session store (persisted data across execute_plan calls)")
38573
+ });
38570
38574
  attemptCompletionSchema = {
38571
38575
  // Custom validation that requires result parameter but allows direct XML response
38572
38576
  safeParse: (params) => {
@@ -38915,6 +38919,7 @@ Capabilities:
38915
38919
  "delegate",
38916
38920
  "analyze_all",
38917
38921
  "execute_plan",
38922
+ "cleanup_execute_plan",
38918
38923
  "listSkills",
38919
38924
  "useSkill",
38920
38925
  "listFiles",
@@ -39560,6 +39565,9 @@ function createTools(configOptions) {
39560
39565
  }
39561
39566
  if (configOptions.enableExecutePlan && isToolAllowed("execute_plan")) {
39562
39567
  tools2.executePlanTool = createExecutePlanTool(configOptions);
39568
+ if (isToolAllowed("cleanup_execute_plan")) {
39569
+ tools2.cleanupExecutePlanTool = createCleanupExecutePlanTool(configOptions);
39570
+ }
39563
39571
  } else if (isToolAllowed("analyze_all")) {
39564
39572
  tools2.analyzeAllTool = analyzeAllTool(configOptions);
39565
39573
  }
@@ -46478,6 +46486,13 @@ function createWrappedTools(baseTools) {
46478
46486
  baseTools.executePlanTool.execute
46479
46487
  );
46480
46488
  }
46489
+ if (baseTools.cleanupExecutePlanTool) {
46490
+ wrappedTools.cleanupExecutePlanToolInstance = wrapToolWithEmitter(
46491
+ baseTools.cleanupExecutePlanTool,
46492
+ "cleanup_execute_plan",
46493
+ baseTools.cleanupExecutePlanTool.execute
46494
+ );
46495
+ }
46481
46496
  if (baseTools.bashTool) {
46482
46497
  wrappedTools.bashToolInstance = wrapToolWithEmitter(
46483
46498
  baseTools.bashTool,
@@ -83956,15 +83971,31 @@ function isSimpleTextWrapperSchema(schema) {
83956
83971
  return null;
83957
83972
  }
83958
83973
  const trimmed = schema.trim();
83959
- const simplePatterns = [
83960
- /^\{\s*["']?(\w+)["']?\s*:\s*["']?string["']?\s*\}$/i,
83961
- /^\{\s*["']?type["']?\s*:\s*["']?object["']?\s*,\s*["']?properties["']?\s*:\s*\{\s*["']?(\w+)["']?\s*:\s*\{\s*["']?type["']?\s*:\s*["']?string["']?\s*\}\s*\}\s*\}$/i
83962
- ];
83963
- for (const pattern of simplePatterns) {
83964
- const match2 = trimmed.match(pattern);
83965
- if (match2) {
83966
- return { fieldName: match2[1] };
83974
+ try {
83975
+ const parsed = JSON.parse(trimmed);
83976
+ if (typeof parsed !== "object" || parsed === null) {
83977
+ } else {
83978
+ const keys2 = Object.keys(parsed);
83979
+ if (keys2.length === 1 && parsed[keys2[0]] === "string") {
83980
+ return { fieldName: keys2[0] };
83981
+ }
83982
+ if (parsed.type === "object" && parsed.properties) {
83983
+ const propKeys = Object.keys(parsed.properties);
83984
+ if (propKeys.length === 1) {
83985
+ const prop = parsed.properties[propKeys[0]];
83986
+ if (prop && prop.type === "string") {
83987
+ return { fieldName: propKeys[0] };
83988
+ }
83989
+ }
83990
+ }
83991
+ return null;
83967
83992
  }
83993
+ } catch {
83994
+ }
83995
+ const simplePattern = /^\{\s*["']?(\w+)["']?\s*:\s*["']?string["']?\s*\}$/i;
83996
+ const match2 = trimmed.match(simplePattern);
83997
+ if (match2) {
83998
+ return { fieldName: match2[1] };
83968
83999
  }
83969
84000
  return null;
83970
84001
  }
@@ -96826,6 +96857,9 @@ var init_ProbeAgent = __esm({
96826
96857
  }
96827
96858
  if (this.enableExecutePlan && wrappedTools.executePlanToolInstance && isToolAllowed("execute_plan")) {
96828
96859
  this.toolImplementations.execute_plan = wrappedTools.executePlanToolInstance;
96860
+ if (wrappedTools.cleanupExecutePlanToolInstance && isToolAllowed("cleanup_execute_plan")) {
96861
+ this.toolImplementations.cleanup_execute_plan = wrappedTools.cleanupExecutePlanToolInstance;
96862
+ }
96829
96863
  } else if (wrappedTools.analyzeAllToolInstance && isToolAllowed("analyze_all")) {
96830
96864
  this.toolImplementations.analyze_all = wrappedTools.analyzeAllToolInstance;
96831
96865
  }
@@ -98199,6 +98233,10 @@ Workspace: ${this.allowedFolders.join(", ")}`;
98199
98233
  if (this.enableBash && isToolAllowed("bash")) dslFunctions.push("bash");
98200
98234
  toolDefinitions += `${getExecutePlanToolDefinition(dslFunctions)}
98201
98235
  `;
98236
+ if (isToolAllowed("cleanup_execute_plan")) {
98237
+ toolDefinitions += `${getCleanupExecutePlanToolDefinition()}
98238
+ `;
98239
+ }
98202
98240
  } else if (isToolAllowed("analyze_all")) {
98203
98241
  toolDefinitions += `${analyzeAllToolDefinition}
98204
98242
  `;
@@ -98274,6 +98312,9 @@ The configuration is loaded from src/config.js lines 15-25 which contains the da
98274
98312
  }
98275
98313
  if (this.enableExecutePlan && isToolAllowed("execute_plan")) {
98276
98314
  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';
98315
+ if (isToolAllowed("cleanup_execute_plan")) {
98316
+ availableToolsList += "- cleanup_execute_plan: Clean up output buffer and session store from previous execute_plan calls.\n";
98317
+ }
98277
98318
  } else if (isToolAllowed("analyze_all")) {
98278
98319
  availableToolsList += "- analyze_all: Process ALL data matching a query using map-reduce (for aggregate questions needing 100% coverage).\n";
98279
98320
  }
@@ -98500,7 +98541,7 @@ You are working with a workspace. Available paths: ${workspaceDesc}
98500
98541
  }
98501
98542
  try {
98502
98543
  const oldHistoryLength = this.history.length;
98503
- if (this._outputBuffer) {
98544
+ if (this._outputBuffer && !options?._schemaFormatted) {
98504
98545
  this._outputBuffer.items = [];
98505
98546
  }
98506
98547
  if (this.enableTasks) {
@@ -98860,6 +98901,9 @@ You are working with a workspace. Available paths: ${workspaceDesc}
98860
98901
  }
98861
98902
  if (this.enableExecutePlan && this.allowedTools.isEnabled("execute_plan")) {
98862
98903
  validTools.push("execute_plan");
98904
+ if (this.allowedTools.isEnabled("cleanup_execute_plan")) {
98905
+ validTools.push("cleanup_execute_plan");
98906
+ }
98863
98907
  } else if (this.allowedTools.isEnabled("analyze_all")) {
98864
98908
  validTools.push("analyze_all");
98865
98909
  }
@@ -112340,6 +112384,50 @@ var init_walk = __esm({
112340
112384
  });
112341
112385
 
112342
112386
  // src/agent/dsl/validator.js
112387
+ function offsetToLineColumn(code, offset2) {
112388
+ const lines = code.split("\n");
112389
+ let pos = 0;
112390
+ for (let i5 = 0; i5 < lines.length; i5++) {
112391
+ const lineLength = lines[i5].length + 1;
112392
+ if (pos + lineLength > offset2) {
112393
+ return { line: i5 + 1, column: offset2 - pos + 1 };
112394
+ }
112395
+ pos += lineLength;
112396
+ }
112397
+ return { line: lines.length, column: 1 };
112398
+ }
112399
+ function generateErrorSnippet(code, line, column, contextLines = 2) {
112400
+ const lines = code.split("\n");
112401
+ const startLine = Math.max(0, line - 1 - contextLines);
112402
+ const endLine = Math.min(lines.length, line + contextLines);
112403
+ const snippetLines = [];
112404
+ const lineNumWidth = String(endLine).length;
112405
+ for (let i5 = startLine; i5 < endLine; i5++) {
112406
+ const lineNum = String(i5 + 1).padStart(lineNumWidth, " ");
112407
+ const marker15 = i5 + 1 === line ? ">" : " ";
112408
+ snippetLines.push(`${marker15} ${lineNum} | ${lines[i5]}`);
112409
+ if (i5 + 1 === line) {
112410
+ const padding = " ".repeat(lineNumWidth + 4);
112411
+ const arrow = " ".repeat(Math.max(0, column - 1)) + "^";
112412
+ snippetLines.push(`${padding}${arrow}`);
112413
+ }
112414
+ }
112415
+ return snippetLines.join("\n");
112416
+ }
112417
+ function formatErrorWithSnippet(message, code, offset2 = -1, line = 0, column = 0) {
112418
+ if (offset2 >= 0) {
112419
+ const loc = offsetToLineColumn(code, offset2);
112420
+ line = loc.line;
112421
+ column = loc.column;
112422
+ }
112423
+ if (line <= 0) {
112424
+ return message;
112425
+ }
112426
+ const snippet = generateErrorSnippet(code, line, column);
112427
+ return `${message}
112428
+
112429
+ ${snippet}`;
112430
+ }
112343
112431
  function validateDSL(code) {
112344
112432
  const errors = [];
112345
112433
  let ast;
@@ -112347,40 +112435,54 @@ function validateDSL(code) {
112347
112435
  ast = parse8(code, {
112348
112436
  ecmaVersion: 2022,
112349
112437
  sourceType: "script",
112350
- allowReturnOutsideFunction: true
112438
+ allowReturnOutsideFunction: true,
112439
+ locations: true
112440
+ // Enable location tracking for better error messages
112351
112441
  });
112352
112442
  } catch (e5) {
112353
- return { valid: false, errors: [`Syntax error: ${e5.message}`] };
112443
+ const line = e5.loc?.line || 0;
112444
+ const column = e5.loc?.column ? e5.loc.column + 1 : 0;
112445
+ const formattedError = formatErrorWithSnippet(
112446
+ `Syntax error: ${e5.message}`,
112447
+ code,
112448
+ -1,
112449
+ line,
112450
+ column
112451
+ );
112452
+ return { valid: false, errors: [formattedError] };
112354
112453
  }
112454
+ const addError = (message, position) => {
112455
+ errors.push(formatErrorWithSnippet(message, code, position));
112456
+ };
112355
112457
  full(ast, (node) => {
112356
112458
  if (!ALLOWED_NODE_TYPES.has(node.type)) {
112357
- errors.push(`Blocked node type: ${node.type} at position ${node.start}`);
112459
+ addError(`Blocked node type: ${node.type}`, node.start);
112358
112460
  return;
112359
112461
  }
112360
112462
  if ((node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression") && node.async) {
112361
- errors.push(`Async functions are not allowed at position ${node.start}. Write synchronous code \u2014 the runtime handles async.`);
112463
+ addError(`Async functions are not allowed. Write synchronous code \u2014 the runtime handles async.`, node.start);
112362
112464
  }
112363
112465
  if (node.type === "FunctionExpression" && node.generator) {
112364
- errors.push(`Generator functions are not allowed at position ${node.start}`);
112466
+ addError(`Generator functions are not allowed`, node.start);
112365
112467
  }
112366
112468
  if (node.type === "Identifier" && BLOCKED_IDENTIFIERS.has(node.name)) {
112367
- errors.push(`Blocked identifier: '${node.name}' at position ${node.start}`);
112469
+ addError(`Blocked identifier: '${node.name}'`, node.start);
112368
112470
  }
112369
112471
  if (node.type === "MemberExpression" && !node.computed) {
112370
112472
  if (node.property.type === "Identifier" && BLOCKED_PROPERTIES.has(node.property.name)) {
112371
- errors.push(`Blocked property access: '.${node.property.name}' at position ${node.property.start}`);
112473
+ addError(`Blocked property access: '.${node.property.name}'`, node.property.start);
112372
112474
  }
112373
112475
  }
112374
112476
  if (node.type === "MemberExpression" && node.computed) {
112375
112477
  if (node.property.type === "Literal" && typeof node.property.value === "string") {
112376
112478
  if (BLOCKED_PROPERTIES.has(node.property.value) || BLOCKED_IDENTIFIERS.has(node.property.value)) {
112377
- errors.push(`Blocked computed property access: '["${node.property.value}"]' at position ${node.property.start}`);
112479
+ addError(`Blocked computed property access: '["${node.property.value}"]'`, node.property.start);
112378
112480
  }
112379
112481
  }
112380
112482
  }
112381
112483
  if (node.type === "VariableDeclarator" && node.id.type === "Identifier") {
112382
112484
  if (BLOCKED_IDENTIFIERS.has(node.id.name)) {
112383
- errors.push(`Cannot declare variable with blocked name: '${node.id.name}' at position ${node.id.start}`);
112485
+ addError(`Cannot declare variable with blocked name: '${node.id.name}'`, node.id.start);
112384
112486
  }
112385
112487
  }
112386
112488
  });
@@ -113011,10 +113113,32 @@ var init_runtime = __esm({
113011
113113
  });
113012
113114
 
113013
113115
  // src/tools/executePlan.js
113116
+ function decodeHtmlEntities2(str) {
113117
+ const entities = {
113118
+ "&amp;": "&",
113119
+ "&lt;": "<",
113120
+ "&gt;": ">",
113121
+ "&quot;": '"',
113122
+ "&apos;": "'",
113123
+ "&#39;": "'",
113124
+ "&#x27;": "'"
113125
+ };
113126
+ let result = str.replace(/&(?:amp|lt|gt|quot|apos|#39|#x27);/gi, (match2) => {
113127
+ return entities[match2.toLowerCase()] || match2;
113128
+ });
113129
+ result = result.replace(/&#(\d+);/g, (match2, dec) => {
113130
+ return String.fromCharCode(parseInt(dec, 10));
113131
+ });
113132
+ result = result.replace(/&#x([0-9a-f]+);/gi, (match2, hex) => {
113133
+ return String.fromCharCode(parseInt(hex, 16));
113134
+ });
113135
+ return result;
113136
+ }
113014
113137
  function stripCodeWrapping(code) {
113015
113138
  let s5 = String(code || "");
113016
113139
  s5 = s5.replace(/^```(?:javascript|js)?\n?/gm, "").replace(/```$/gm, "");
113017
113140
  s5 = s5.replace(/<\/?(?:execute_plan|code)>/g, "");
113141
+ s5 = decodeHtmlEntities2(s5);
113018
113142
  return s5.trim();
113019
113143
  }
113020
113144
  function buildToolImplementations(configOptions) {
@@ -113266,6 +113390,14 @@ Logs: ${result.logs.join(" | ")}` : "";
113266
113390
  "dsl.error": lastError.substring(0, 1e3)
113267
113391
  });
113268
113392
  }
113393
+ if (outputBuffer && outputBuffer.items && outputBuffer.items.length > 0) {
113394
+ const clearedChars = outputBuffer.items.reduce((sum, item) => sum + item.length, 0);
113395
+ outputBuffer.items = [];
113396
+ planSpan?.addEvent?.("dsl.auto_cleanup", {
113397
+ "cleanup.chars_cleared": clearedChars,
113398
+ "cleanup.reason": "all_retries_exhausted"
113399
+ });
113400
+ }
113269
113401
  finalOutput = `Plan execution failed after ${maxRetries} retries.
113270
113402
 
113271
113403
  Last error: ${lastError}`;
@@ -113278,6 +113410,9 @@ Last error: ${lastError}`;
113278
113410
  planSpan?.end?.();
113279
113411
  return finalOutput;
113280
113412
  } catch (e5) {
113413
+ if (outputBuffer && outputBuffer.items && outputBuffer.items.length > 0) {
113414
+ outputBuffer.items = [];
113415
+ }
113281
113416
  planSpan?.setStatus?.("ERROR");
113282
113417
  planSpan?.addEvent?.("exception", {
113283
113418
  "exception.message": e5.message,
@@ -113721,6 +113856,62 @@ output(table);
113721
113856
  return "Generated table with " + results.length + " items.";
113722
113857
  \`\`\``;
113723
113858
  }
113859
+ function createCleanupExecutePlanTool(options) {
113860
+ const { outputBuffer, sessionStore, tracer } = options;
113861
+ return (0, import_ai6.tool)({
113862
+ 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.",
113863
+ parameters: cleanupExecutePlanSchema,
113864
+ execute: async ({ clearOutputBuffer = true, clearSessionStore = false }) => {
113865
+ const span = tracer?.createToolSpan?.("cleanup_execute_plan", {
113866
+ "cleanup.clear_output_buffer": clearOutputBuffer,
113867
+ "cleanup.clear_session_store": clearSessionStore
113868
+ }) || null;
113869
+ const results = [];
113870
+ try {
113871
+ if (clearOutputBuffer && outputBuffer) {
113872
+ const itemCount = outputBuffer.items?.length || 0;
113873
+ const charCount = outputBuffer.items?.reduce((sum, item) => sum + item.length, 0) || 0;
113874
+ outputBuffer.items = [];
113875
+ results.push(`Output buffer cleared (${itemCount} items, ${charCount} chars)`);
113876
+ }
113877
+ if (clearSessionStore && sessionStore) {
113878
+ const keyCount = Object.keys(sessionStore).length;
113879
+ for (const key of Object.keys(sessionStore)) {
113880
+ delete sessionStore[key];
113881
+ }
113882
+ results.push(`Session store cleared (${keyCount} keys)`);
113883
+ }
113884
+ const output = results.length > 0 ? `Cleanup complete:
113885
+ - ${results.join("\n- ")}` : "Nothing to clean up";
113886
+ span?.setAttributes?.({
113887
+ "cleanup.result": output,
113888
+ "cleanup.success": true
113889
+ });
113890
+ span?.setStatus?.("OK");
113891
+ span?.end?.();
113892
+ return output;
113893
+ } catch (e5) {
113894
+ span?.setStatus?.("ERROR");
113895
+ span?.addEvent?.("exception", { "exception.message": e5.message });
113896
+ span?.end?.();
113897
+ return `Cleanup failed: ${e5.message}`;
113898
+ }
113899
+ }
113900
+ });
113901
+ }
113902
+ function getCleanupExecutePlanToolDefinition() {
113903
+ return `## cleanup_execute_plan
113904
+ 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.
113905
+
113906
+ Parameters:
113907
+ - clearOutputBuffer: (optional, default: true) Clear the output buffer from previous execute_plan calls
113908
+ - clearSessionStore: (optional, default: false) Clear the session store (persisted data across execute_plan calls)
113909
+
113910
+ Example:
113911
+ <cleanup_execute_plan>
113912
+ <clearOutputBuffer>true</clearOutputBuffer>
113913
+ </cleanup_execute_plan>`;
113914
+ }
113724
113915
  var import_ai6;
113725
113916
  var init_executePlan = __esm({
113726
113917
  "src/tools/executePlan.js"() {
@@ -113879,6 +114070,8 @@ __export(tools_exports, {
113879
114070
  bashTool: () => bashTool,
113880
114071
  bashToolDefinition: () => bashToolDefinition,
113881
114072
  buildToolTagPattern: () => buildToolTagPattern,
114073
+ cleanupExecutePlanSchema: () => cleanupExecutePlanSchema,
114074
+ createCleanupExecutePlanTool: () => createCleanupExecutePlanTool,
113882
114075
  createDescription: () => createDescription,
113883
114076
  createExecutePlanTool: () => createExecutePlanTool,
113884
114077
  createExtractTool: () => createExtractTool,
@@ -113898,6 +114091,7 @@ __export(tools_exports, {
113898
114091
  executePlanSchema: () => executePlanSchema,
113899
114092
  extractSchema: () => extractSchema,
113900
114093
  extractTool: () => extractTool,
114094
+ getCleanupExecutePlanToolDefinition: () => getCleanupExecutePlanToolDefinition,
113901
114095
  getExecutePlanToolDefinition: () => getExecutePlanToolDefinition,
113902
114096
  parseAndResolvePaths: () => parseAndResolvePaths,
113903
114097
  querySchema: () => querySchema,
@@ -114522,6 +114716,8 @@ __export(index_exports, {
114522
114716
  bashSchema: () => bashSchema,
114523
114717
  bashTool: () => bashTool,
114524
114718
  bashToolDefinition: () => bashToolDefinition,
114719
+ cleanupExecutePlanSchema: () => cleanupExecutePlanSchema,
114720
+ createCleanupExecutePlanTool: () => createCleanupExecutePlanTool,
114525
114721
  createExecutePlanTool: () => createExecutePlanTool,
114526
114722
  createSchema: () => createSchema,
114527
114723
  createTaskTool: () => createTaskTool,
@@ -114540,6 +114736,7 @@ __export(index_exports, {
114540
114736
  extractTool: () => extractTool,
114541
114737
  extractToolDefinition: () => extractToolDefinition,
114542
114738
  getBinaryPath: () => getBinaryPath,
114739
+ getCleanupExecutePlanToolDefinition: () => getCleanupExecutePlanToolDefinition,
114543
114740
  getExecutePlanToolDefinition: () => getExecutePlanToolDefinition,
114544
114741
  googleSearchToolDefinition: () => googleSearchToolDefinition,
114545
114742
  grep: () => grep,
@@ -114612,6 +114809,8 @@ init_index();
114612
114809
  bashSchema,
114613
114810
  bashTool,
114614
114811
  bashToolDefinition,
114812
+ cleanupExecutePlanSchema,
114813
+ createCleanupExecutePlanTool,
114615
114814
  createExecutePlanTool,
114616
114815
  createSchema,
114617
114816
  createTaskTool,
@@ -114630,6 +114829,7 @@ init_index();
114630
114829
  extractTool,
114631
114830
  extractToolDefinition,
114632
114831
  getBinaryPath,
114832
+ getCleanupExecutePlanToolDefinition,
114633
114833
  getExecutePlanToolDefinition,
114634
114834
  googleSearchToolDefinition,
114635
114835
  grep,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@probelabs/probe",
3
- "version": "0.6.0-rc239",
3
+ "version": "0.6.0-rc241",
4
4
  "description": "Node.js wrapper for the probe code search tool",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
@@ -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
  }
@@ -9,6 +9,81 @@
9
9
  import * as acorn from 'acorn';
10
10
  import * as walk from 'acorn-walk';
11
11
 
12
+ /**
13
+ * Convert a character offset to line and column numbers.
14
+ * @param {string} code - The source code
15
+ * @param {number} offset - Character offset
16
+ * @returns {{ line: number, column: number }}
17
+ */
18
+ function offsetToLineColumn(code, offset) {
19
+ const lines = code.split('\n');
20
+ let pos = 0;
21
+ for (let i = 0; i < lines.length; i++) {
22
+ const lineLength = lines[i].length + 1; // +1 for newline
23
+ if (pos + lineLength > offset) {
24
+ return { line: i + 1, column: offset - pos + 1 };
25
+ }
26
+ pos += lineLength;
27
+ }
28
+ return { line: lines.length, column: 1 };
29
+ }
30
+
31
+ /**
32
+ * Generate a code snippet with an arrow pointing to the error location.
33
+ * @param {string} code - The source code
34
+ * @param {number} line - Line number (1-based)
35
+ * @param {number} column - Column number (1-based)
36
+ * @param {number} contextLines - Number of lines to show before/after (default: 2)
37
+ * @returns {string}
38
+ */
39
+ function generateErrorSnippet(code, line, column, contextLines = 2) {
40
+ const lines = code.split('\n');
41
+ const startLine = Math.max(0, line - 1 - contextLines);
42
+ const endLine = Math.min(lines.length, line + contextLines);
43
+
44
+ const snippetLines = [];
45
+ const lineNumWidth = String(endLine).length;
46
+
47
+ for (let i = startLine; i < endLine; i++) {
48
+ const lineNum = String(i + 1).padStart(lineNumWidth, ' ');
49
+ const marker = (i + 1 === line) ? '>' : ' ';
50
+ snippetLines.push(`${marker} ${lineNum} | ${lines[i]}`);
51
+
52
+ // Add arrow line for the error line
53
+ if (i + 1 === line) {
54
+ const padding = ' '.repeat(lineNumWidth + 4); // " 123 | " prefix
55
+ const arrow = ' '.repeat(Math.max(0, column - 1)) + '^';
56
+ snippetLines.push(`${padding}${arrow}`);
57
+ }
58
+ }
59
+
60
+ return snippetLines.join('\n');
61
+ }
62
+
63
+ /**
64
+ * Format an error message with code snippet.
65
+ * @param {string} message - The error message
66
+ * @param {string} code - The source code
67
+ * @param {number} offset - Character offset (optional, use -1 if line/column provided)
68
+ * @param {number} line - Line number (optional)
69
+ * @param {number} column - Column number (optional)
70
+ * @returns {string}
71
+ */
72
+ function formatErrorWithSnippet(message, code, offset = -1, line = 0, column = 0) {
73
+ if (offset >= 0) {
74
+ const loc = offsetToLineColumn(code, offset);
75
+ line = loc.line;
76
+ column = loc.column;
77
+ }
78
+
79
+ if (line <= 0) {
80
+ return message;
81
+ }
82
+
83
+ const snippet = generateErrorSnippet(code, line, column);
84
+ return `${message}\n\n${snippet}`;
85
+ }
86
+
12
87
  // Node types the LLM is allowed to generate
13
88
  const ALLOWED_NODE_TYPES = new Set([
14
89
  'Program',
@@ -102,16 +177,32 @@ export function validateDSL(code) {
102
177
  ecmaVersion: 2022,
103
178
  sourceType: 'script',
104
179
  allowReturnOutsideFunction: true,
180
+ locations: true, // Enable location tracking for better error messages
105
181
  });
106
182
  } catch (e) {
107
- return { valid: false, errors: [`Syntax error: ${e.message}`] };
183
+ // Acorn errors have loc property with line/column
184
+ const line = e.loc?.line || 0;
185
+ const column = e.loc?.column ? e.loc.column + 1 : 0; // Acorn column is 0-based
186
+ const formattedError = formatErrorWithSnippet(
187
+ `Syntax error: ${e.message}`,
188
+ code,
189
+ -1,
190
+ line,
191
+ column
192
+ );
193
+ return { valid: false, errors: [formattedError] };
108
194
  }
109
195
 
196
+ // Helper to add error with code snippet
197
+ const addError = (message, position) => {
198
+ errors.push(formatErrorWithSnippet(message, code, position));
199
+ };
200
+
110
201
  // Step 2: Walk every node and validate
111
202
  walk.full(ast, (node) => {
112
203
  // Check node type against whitelist
113
204
  if (!ALLOWED_NODE_TYPES.has(node.type)) {
114
- errors.push(`Blocked node type: ${node.type} at position ${node.start}`);
205
+ addError(`Blocked node type: ${node.type}`, node.start);
115
206
  return;
116
207
  }
117
208
 
@@ -121,7 +212,7 @@ export function validateDSL(code) {
121
212
  node.type === 'FunctionExpression') &&
122
213
  node.async
123
214
  ) {
124
- errors.push(`Async functions are not allowed at position ${node.start}. Write synchronous code — the runtime handles async.`);
215
+ addError(`Async functions are not allowed. Write synchronous code — the runtime handles async.`, node.start);
125
216
  }
126
217
 
127
218
  // Block generator functions
@@ -129,19 +220,19 @@ export function validateDSL(code) {
129
220
  (node.type === 'FunctionExpression') &&
130
221
  node.generator
131
222
  ) {
132
- errors.push(`Generator functions are not allowed at position ${node.start}`);
223
+ addError(`Generator functions are not allowed`, node.start);
133
224
  }
134
225
 
135
226
 
136
227
  // Check identifiers against blocklist
137
228
  if (node.type === 'Identifier' && BLOCKED_IDENTIFIERS.has(node.name)) {
138
- errors.push(`Blocked identifier: '${node.name}' at position ${node.start}`);
229
+ addError(`Blocked identifier: '${node.name}'`, node.start);
139
230
  }
140
231
 
141
232
  // Check member expressions for blocked properties
142
233
  if (node.type === 'MemberExpression' && !node.computed) {
143
234
  if (node.property.type === 'Identifier' && BLOCKED_PROPERTIES.has(node.property.name)) {
144
- errors.push(`Blocked property access: '.${node.property.name}' at position ${node.property.start}`);
235
+ addError(`Blocked property access: '.${node.property.name}'`, node.property.start);
145
236
  }
146
237
  }
147
238
 
@@ -149,7 +240,7 @@ export function validateDSL(code) {
149
240
  if (node.type === 'MemberExpression' && node.computed) {
150
241
  if (node.property.type === 'Literal' && typeof node.property.value === 'string') {
151
242
  if (BLOCKED_PROPERTIES.has(node.property.value) || BLOCKED_IDENTIFIERS.has(node.property.value)) {
152
- errors.push(`Blocked computed property access: '["${node.property.value}"]' at position ${node.property.start}`);
243
+ addError(`Blocked computed property access: '["${node.property.value}"]'`, node.property.start);
153
244
  }
154
245
  }
155
246
  }
@@ -157,7 +248,7 @@ export function validateDSL(code) {
157
248
  // Block variable declarations named with blocked identifiers
158
249
  if (node.type === 'VariableDeclarator' && node.id.type === 'Identifier') {
159
250
  if (BLOCKED_IDENTIFIERS.has(node.id.name)) {
160
- errors.push(`Cannot declare variable with blocked name: '${node.id.name}' at position ${node.id.start}`);
251
+ addError(`Cannot declare variable with blocked name: '${node.id.name}'`, node.id.start);
161
252
  }
162
253
  }
163
254
  });