@probelabs/probe 0.6.0-rc251 → 0.6.0-rc253

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.
@@ -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 { sessionId, cwd } = configOptions;
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 lazily on first execute() and when MCP bridge changes.
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 getRuntime().execute(currentCode, description);
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
- output += 'Plan completed (no return value).';
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 {