@probelabs/probe 0.6.0-rc277 → 0.6.0-rc279

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.
@@ -104,7 +104,6 @@ import {
104
104
  TaskManager,
105
105
  createTaskTool,
106
106
  taskSystemPrompt,
107
- taskGuidancePrompt,
108
107
  createTaskCompletionBlockedMessage
109
108
  } from './tasks/index.js';
110
109
  import { z } from 'zod';
@@ -126,6 +125,27 @@ const MAX_HISTORY_MESSAGES = 100;
126
125
  // Maximum image file size (20MB) to prevent OOM attacks
127
126
  const MAX_IMAGE_FILE_SIZE = 20 * 1024 * 1024;
128
127
 
128
+ /**
129
+ * Truncate a string for debug logging, showing first and last portion.
130
+ */
131
+ export function debugTruncate(s, limit = 200) {
132
+ if (s.length <= limit) return s;
133
+ const half = Math.floor(limit / 2);
134
+ return s.substring(0, half) + ` ... [${s.length} chars] ... ` + s.substring(s.length - half);
135
+ }
136
+
137
+ /**
138
+ * Log tool results details for debug output.
139
+ */
140
+ export function debugLogToolResults(toolResults) {
141
+ if (!toolResults || toolResults.length === 0) return;
142
+ for (const tr of toolResults) {
143
+ const argsStr = JSON.stringify(tr.args || {});
144
+ const resultStr = typeof tr.result === 'string' ? tr.result : JSON.stringify(tr.result || '');
145
+ console.log(`[DEBUG] tool: ${tr.toolName} | args: ${debugTruncate(argsStr)} | result: ${debugTruncate(resultStr)}`);
146
+ }
147
+ }
148
+
129
149
  /**
130
150
  * ProbeAgent class to handle AI interactions with code search capabilities
131
151
  */
@@ -194,6 +214,7 @@ export class ProbeAgent {
194
214
  this.enableExecutePlan = !!options.enableExecutePlan;
195
215
  this.debug = options.debug || process.env.DEBUG === '1';
196
216
  this.cancelled = false;
217
+ this._abortController = new AbortController();
197
218
  this.tracer = options.tracer || null;
198
219
  this.outline = !!options.outline;
199
220
  this.searchDelegate = options.searchDelegate !== undefined ? !!options.searchDelegate : true;
@@ -793,6 +814,7 @@ export class ProbeAgent {
793
814
  searchDelegateProvider: this.searchDelegateProvider,
794
815
  searchDelegateModel: this.searchDelegateModel,
795
816
  delegationManager: this.delegationManager, // Per-instance delegation limits
817
+ parentAbortSignal: this._abortController.signal, // Propagate cancellation to delegations
796
818
  outputBuffer: this._outputBuffer,
797
819
  concurrencyLimiter: this.concurrencyLimiter, // Global AI concurrency limiter
798
820
  isToolAllowed,
@@ -1363,6 +1385,19 @@ export class ProbeAgent {
1363
1385
  const controller = new AbortController();
1364
1386
  const timeoutState = { timeoutId: null };
1365
1387
 
1388
+ // Link agent-level abort to this operation's controller
1389
+ // so that cancel() / cleanup() stops the current streamText call
1390
+ if (this._abortController.signal.aborted) {
1391
+ controller.abort();
1392
+ } else {
1393
+ const onAgentAbort = () => controller.abort();
1394
+ this._abortController.signal.addEventListener('abort', onAgentAbort, { once: true });
1395
+ // Clean up listener when this controller aborts (from any source)
1396
+ controller.signal.addEventListener('abort', () => {
1397
+ this._abortController.signal.removeEventListener('abort', onAgentAbort);
1398
+ }, { once: true });
1399
+ }
1400
+
1366
1401
  // Set up overall operation timeout (default 5 minutes)
1367
1402
  if (this.maxOperationTimeout && this.maxOperationTimeout > 0) {
1368
1403
  timeoutState.timeoutId = setTimeout(() => {
@@ -1730,7 +1765,8 @@ export class ProbeAgent {
1730
1765
  allowEdit: this.allowEdit,
1731
1766
  allowedTools: allowedToolsForDelegate,
1732
1767
  debug: this.debug,
1733
- tracer: this.tracer
1768
+ tracer: this.tracer,
1769
+ parentAbortSignal: this._abortController.signal
1734
1770
  };
1735
1771
 
1736
1772
  if (this.debug) {
@@ -3153,14 +3189,6 @@ Follow these instructions carefully:
3153
3189
  // Create user message with optional image support
3154
3190
  let userMessage = { role: 'user', content: message.trim() };
3155
3191
 
3156
- // START CHECKPOINT: Inject task guidance if tasks are enabled
3157
- if (this.enableTasks) {
3158
- userMessage.content = userMessage.content + '\n\n' + taskGuidancePrompt;
3159
- if (this.debug) {
3160
- console.log('[DEBUG] Task guidance injected into user message');
3161
- }
3162
- }
3163
-
3164
3192
  // If schema is provided, prepend JSON format requirement to user message
3165
3193
  if (options.schema && !options._schemaFormatted) {
3166
3194
  const schemaInstructions = generateSchemaInstructions(options.schema, { debug: this.debug });
@@ -3378,6 +3406,11 @@ Follow these instructions carefully:
3378
3406
  completionAttempted = true;
3379
3407
  }, toolContext);
3380
3408
 
3409
+ if (this.debug) {
3410
+ const toolNames = Object.keys(tools);
3411
+ console.log(`[DEBUG] Agent tools registered (${toolNames.length}): ${toolNames.join(', ')}`);
3412
+ }
3413
+
3381
3414
  let maxResponseTokens = this.maxResponseTokens;
3382
3415
  if (!maxResponseTokens) {
3383
3416
  maxResponseTokens = 4000;
@@ -3427,6 +3460,7 @@ Follow these instructions carefully:
3427
3460
 
3428
3461
  if (this.debug) {
3429
3462
  console.log(`[DEBUG] Step ${currentIteration}/${maxIterations} finished (reason: ${finishReason}, tools: ${toolResults?.length || 0})`);
3463
+ debugLogToolResults(toolResults);
3430
3464
  }
3431
3465
  }
3432
3466
  };
@@ -3638,6 +3672,7 @@ Double-check your response based on the criteria above. If everything looks good
3638
3672
  }
3639
3673
  if (this.debug) {
3640
3674
  console.log(`[DEBUG] Completion prompt step finished (reason: ${finishReason}, tools: ${toolResults?.length || 0})`);
3675
+ debugLogToolResults(toolResults);
3641
3676
  }
3642
3677
  }
3643
3678
  };
@@ -4554,6 +4589,11 @@ Convert your previous response content into actual JSON data that follows this s
4554
4589
  * Clean up resources (including MCP connections)
4555
4590
  */
4556
4591
  async cleanup() {
4592
+ // Abort any in-flight operations (delegations, streaming, etc.)
4593
+ if (!this._abortController.signal.aborted) {
4594
+ this._abortController.abort();
4595
+ }
4596
+
4557
4597
  // Clean up MCP bridge
4558
4598
  if (this.mcpBridge) {
4559
4599
  try {
@@ -4583,12 +4623,24 @@ Convert your previous response content into actual JSON data that follows this s
4583
4623
  }
4584
4624
 
4585
4625
  /**
4586
- * Cancel the current request
4626
+ * Cancel the current request and all in-flight delegations.
4627
+ * Aborts the internal AbortController so streamText, subagents,
4628
+ * and any code checking the signal will stop.
4587
4629
  */
4588
4630
  cancel() {
4589
4631
  this.cancelled = true;
4632
+ this._abortController.abort();
4590
4633
  if (this.debug) {
4591
4634
  console.log(`[DEBUG] Agent cancelled for session ${this.sessionId}`);
4592
4635
  }
4593
4636
  }
4637
+
4638
+ /**
4639
+ * Get the abort signal for this agent.
4640
+ * Delegations and subagents should check this signal.
4641
+ * @returns {AbortSignal}
4642
+ */
4643
+ get abortSignal() {
4644
+ return this._abortController.signal;
4645
+ }
4594
4646
  }
@@ -3862,12 +3862,17 @@ async function delegate({
3862
3862
  mcpConfigPath = null,
3863
3863
  delegationManager = null,
3864
3864
  // Optional per-instance manager, falls back to default singleton
3865
- concurrencyLimiter = null
3865
+ concurrencyLimiter = null,
3866
3866
  // Optional global AI concurrency limiter
3867
+ parentAbortSignal = null
3868
+ // Optional AbortSignal from parent to cancel this delegation
3867
3869
  }) {
3868
3870
  if (!task || typeof task !== "string") {
3869
3871
  throw new Error("Task parameter is required and must be a string");
3870
3872
  }
3873
+ if (parentAbortSignal?.aborted) {
3874
+ throw new Error("Delegation cancelled: parent operation was aborted");
3875
+ }
3871
3876
  const hasExplicitTimeout = Object.prototype.hasOwnProperty.call(arguments?.[0] ?? {}, "timeout");
3872
3877
  if (!hasExplicitTimeout) {
3873
3878
  const envTimeoutMs = parseInt(process.env.DELEGATION_TIMEOUT_MS || "", 10);
@@ -3952,12 +3957,37 @@ async function delegate({
3952
3957
  }
3953
3958
  const timeoutPromise = new Promise((_, reject2) => {
3954
3959
  timeoutId = setTimeout(() => {
3960
+ subagent.cancel();
3955
3961
  reject2(new Error(`Delegation timed out after ${timeout} seconds`));
3956
3962
  }, timeout * 1e3);
3957
3963
  });
3964
+ let parentAbortHandler;
3965
+ const parentAbortPromise = new Promise((_, reject2) => {
3966
+ if (parentAbortSignal) {
3967
+ if (parentAbortSignal.aborted) {
3968
+ subagent.cancel();
3969
+ reject2(new Error("Delegation cancelled: parent operation was aborted"));
3970
+ return;
3971
+ }
3972
+ parentAbortHandler = () => {
3973
+ subagent.cancel();
3974
+ reject2(new Error("Delegation cancelled: parent operation was aborted"));
3975
+ };
3976
+ parentAbortSignal.addEventListener("abort", parentAbortHandler, { once: true });
3977
+ }
3978
+ });
3958
3979
  const answerOptions = schema ? { schema } : void 0;
3959
3980
  const answerPromise = answerOptions ? subagent.answer(task, [], answerOptions) : subagent.answer(task);
3960
- const response = await Promise.race([answerPromise, timeoutPromise]);
3981
+ const racers = [answerPromise, timeoutPromise];
3982
+ if (parentAbortSignal) racers.push(parentAbortPromise);
3983
+ let response;
3984
+ try {
3985
+ response = await Promise.race(racers);
3986
+ } finally {
3987
+ if (parentAbortHandler && parentAbortSignal) {
3988
+ parentAbortSignal.removeEventListener("abort", parentAbortHandler);
3989
+ }
3990
+ }
3961
3991
  if (timeoutId !== null) {
3962
3992
  clearTimeout(timeoutId);
3963
3993
  timeoutId = null;
@@ -4369,8 +4399,9 @@ Instructions:
4369
4399
  promptType: "code-researcher",
4370
4400
  allowedTools: ["extract"],
4371
4401
  maxIterations: 5,
4372
- delegationManager: options.delegationManager
4402
+ delegationManager: options.delegationManager,
4373
4403
  // Per-instance delegation limits
4404
+ parentAbortSignal: options.parentAbortSignal || null
4374
4405
  // timeout removed - inherit default from delegate (300s)
4375
4406
  });
4376
4407
  return { chunk, result };
@@ -4469,8 +4500,9 @@ Organize all findings into clear categories with items listed under each.${compl
4469
4500
  promptType: "code-researcher",
4470
4501
  allowedTools: [],
4471
4502
  maxIterations: 5,
4472
- delegationManager: options.delegationManager
4503
+ delegationManager: options.delegationManager,
4473
4504
  // Per-instance delegation limits
4505
+ parentAbortSignal: options.parentAbortSignal || null
4474
4506
  // timeout removed - inherit default from delegate (300s)
4475
4507
  });
4476
4508
  return result;
@@ -4534,8 +4566,9 @@ CRITICAL: Do NOT guess keywords. Actually run searches and see what returns resu
4534
4566
  promptType: "code-researcher",
4535
4567
  // Full tool access for exploration and experimentation
4536
4568
  maxIterations: 15,
4537
- delegationManager: options.delegationManager
4569
+ delegationManager: options.delegationManager,
4538
4570
  // Per-instance delegation limits
4571
+ parentAbortSignal: options.parentAbortSignal || null
4539
4572
  // timeout removed - inherit default from delegate (300s)
4540
4573
  });
4541
4574
  const plan = parsePlanningResult(stripResultTags(result));
@@ -4592,8 +4625,9 @@ When done, use the attempt_completion tool with your answer as the result.`;
4592
4625
  promptType: "code-researcher",
4593
4626
  allowedTools: [],
4594
4627
  maxIterations: 5,
4595
- delegationManager: options.delegationManager
4628
+ delegationManager: options.delegationManager,
4596
4629
  // Per-instance delegation limits
4630
+ parentAbortSignal: options.parentAbortSignal || null
4597
4631
  // timeout removed - inherit default from delegate (300s)
4598
4632
  });
4599
4633
  return stripResultTags(result);
@@ -9277,7 +9311,8 @@ var init_vercel = __esm({
9277
9311
  promptType: "code-searcher",
9278
9312
  allowedTools: ["search", "extract", "listFiles", "attempt_completion"],
9279
9313
  searchDelegate: false,
9280
- schema: CODE_SEARCH_SCHEMA
9314
+ schema: CODE_SEARCH_SCHEMA,
9315
+ parentAbortSignal: options.parentAbortSignal || null
9281
9316
  });
9282
9317
  const delegateResult = options.tracer?.withSpan ? await options.tracer.withSpan("search.delegate", runDelegation, {
9283
9318
  "search.query": searchQuery,
@@ -9491,7 +9526,7 @@ var init_vercel = __esm({
9491
9526
  name: "delegate",
9492
9527
  description: delegateDescription,
9493
9528
  inputSchema: delegateSchema,
9494
- execute: async ({ task, currentIteration, maxIterations, parentSessionId, path: path9, provider, model, tracer, searchDelegate }) => {
9529
+ execute: async ({ task, currentIteration, maxIterations, parentSessionId, path: path9, provider, model, tracer, searchDelegate, parentAbortSignal }) => {
9495
9530
  if (!task || typeof task !== "string") {
9496
9531
  throw new Error("Task parameter is required and must be a non-empty string");
9497
9532
  }
@@ -9549,8 +9584,9 @@ var init_vercel = __esm({
9549
9584
  enableMcp,
9550
9585
  mcpConfig,
9551
9586
  mcpConfigPath,
9552
- delegationManager
9587
+ delegationManager,
9553
9588
  // Per-instance delegation limits
9589
+ parentAbortSignal
9554
9590
  });
9555
9591
  return result;
9556
9592
  }
@@ -9588,8 +9624,9 @@ var init_vercel = __esm({
9588
9624
  provider: options.provider,
9589
9625
  model: options.model,
9590
9626
  tracer: options.tracer,
9591
- delegationManager
9627
+ delegationManager,
9592
9628
  // Per-instance delegation limits
9629
+ parentAbortSignal: options.parentAbortSignal || null
9593
9630
  });
9594
9631
  return result;
9595
9632
  } catch (error) {
@@ -31932,7 +31969,7 @@ ${taskManager.formatTasksForPrompt()}`;
31932
31969
  }
31933
31970
  };
31934
31971
  }
31935
- var taskItemSchema, taskSchema, taskSystemPrompt, taskGuidancePrompt;
31972
+ var taskItemSchema, taskSchema, taskSystemPrompt;
31936
31973
  var init_taskTool = __esm({
31937
31974
  "src/agent/tasks/taskTool.js"() {
31938
31975
  "use strict";
@@ -31993,11 +32030,6 @@ Tasks = logical units of work, not files or steps.
31993
32030
  - Circular dependencies are rejected
31994
32031
  - attempt_completion is blocked while tasks remain unresolved
31995
32032
  `;
31996
- taskGuidancePrompt = `Does this request have MULTIPLE DISTINCT GOALS?
31997
- - "Do A AND B AND C" (multiple goals) \u2192 Create tasks for each goal
31998
- - "Investigate/explain/find X" (single goal) \u2192 Skip tasks, just answer directly
31999
- Multiple internal steps for ONE goal = NO tasks needed.
32000
- If creating tasks, use the task tool with action="create" first.`;
32001
32033
  }
32002
32034
  });
32003
32035
 
@@ -70753,8 +70785,20 @@ If the solution is clear, you can jump to implementation right away. If not, ask
70753
70785
  - Check imports and existing utilities before creating new helpers \u2014 the project may already have what you need.
70754
70786
 
70755
70787
  # Task Planning
70756
- - If the task tool is available, use it to break complex work into milestones before starting implementation.
70757
- - Stay flexible \u2014 if your understanding changes mid-task, add, remove, or reorganize tasks as needed. The plan should serve you, not constrain you.
70788
+ When the request has **multiple distinct goals** (e.g. "Fix bug A AND add feature B"), use the task tool to track them:
70789
+ - Call the task tool with action="create" and a tasks array. Each task must have an "id" field.
70790
+ - Update task status to "in_progress" when starting and "completed" when done.
70791
+ - All tasks must be completed or cancelled before calling attempt_completion.
70792
+ - Stay flexible \u2014 add, remove, or reorganize tasks as your understanding changes.
70793
+
70794
+ Do NOT create tasks for single-goal requests, even complex ones. Multiple internal steps for one goal (search, read, analyze, implement) do not need tasks.
70795
+
70796
+ # Discovering Project Commands
70797
+ Before building or testing, determine the project's toolchain:
70798
+ - Check for Makefile, package.json (scripts), Cargo.toml, go.mod, pyproject.toml, or similar
70799
+ - Look for CI config (.github/workflows/, .gitlab-ci.yml) to see what commands CI runs
70800
+ - Read README for build/test instructions if the above are unclear
70801
+ - Common patterns: \`make build\`/\`make test\`, \`npm run build\`/\`npm test\`, \`cargo build\`/\`cargo test\`, \`go build ./...\`/\`go test ./...\`, \`python -m pytest\`
70758
70802
 
70759
70803
  # During Implementation
70760
70804
  - Always create a new branch before making changes to the codebase.
@@ -70765,12 +70809,22 @@ If the solution is clear, you can jump to implementation right away. If not, ask
70765
70809
  - When editing files, keep edits focused and minimal. For changes spanning more than a few lines, prefer line-targeted editing (start_line/end_line) over text replacement (old_string) \u2014 it constrains scope and prevents accidental removal of adjacent content. Never include unrelated sections in an edit operation.
70766
70810
  - After every significant change, verify the project still builds and passes linting. Do not wait until the end to discover breakage.
70767
70811
 
70768
- # After Implementation
70769
- - Verify the project builds successfully. If it doesn't, fix the build before moving on.
70770
- - Run lint and typecheck commands if known for the project. Fix any new warnings or errors you introduced.
70771
- - Add tests for any new or changed functionality. Tests must cover the main path and important edge cases.
70772
- - Run the project's full test suite. If any tests fail (including pre-existing ones you may have broken), fix them before finishing.
70773
- - When the task is done, respond to the user with a concise summary of what was implemented, what files were changed, and any relevant details. Include links (e.g. pull request URL) so the user has everything they need.
70812
+ # Writing Tests
70813
+ Every change must include tests. Before writing them:
70814
+ - Find existing test files for the module you changed \u2014 look in \`tests/\`, \`__tests__/\`, \`*_test.go\`, \`*.test.js\`, \`*.spec.ts\`, or co-located test modules (\`#[cfg(test)]\` in Rust).
70815
+ - Read those tests to understand the project's testing patterns: framework, assertion style, mocking approach, file naming, test organization.
70816
+ - Prefer extending an existing test file over creating a new one when your change is in the same module.
70817
+ - Write tests that cover the main path and important edge cases. Include a failing-input test when relevant.
70818
+ - When fixing a bug, write a failing test first that reproduces the bug, then fix the code to make it pass.
70819
+
70820
+ # Verify Changes
70821
+ Before committing or creating a PR, run through this checklist:
70822
+ 1. **Build** \u2014 run the project-appropriate build command (go build, npm run build, cargo build, make, etc.). Fix any compilation errors.
70823
+ 2. **Lint & typecheck** \u2014 run linter/formatter if the project has one (eslint, clippy, golangci-lint, etc.). Fix any new warnings.
70824
+ 3. **Test** \u2014 run the full test suite (go test ./..., npm test, cargo test, make test, pytest, etc.). Fix any failures, including pre-existing tests you may have broken.
70825
+ 4. **Review** \u2014 re-read your diff. Ensure no debug code, no unrelated changes, no secrets, no missing files.
70826
+
70827
+ Do NOT skip verification. Do NOT proceed to PR creation with a broken build or failing tests.
70774
70828
 
70775
70829
  # GitHub Integration
70776
70830
  - Use the \`gh\` CLI for all GitHub operations: issues, pull requests, checks, releases.
@@ -81751,7 +81805,9 @@ __export(ProbeAgent_exports, {
81751
81805
  ENGINE_ACTIVITY_TIMEOUT_DEFAULT: () => ENGINE_ACTIVITY_TIMEOUT_DEFAULT,
81752
81806
  ENGINE_ACTIVITY_TIMEOUT_MAX: () => ENGINE_ACTIVITY_TIMEOUT_MAX,
81753
81807
  ENGINE_ACTIVITY_TIMEOUT_MIN: () => ENGINE_ACTIVITY_TIMEOUT_MIN,
81754
- ProbeAgent: () => ProbeAgent
81808
+ ProbeAgent: () => ProbeAgent,
81809
+ debugLogToolResults: () => debugLogToolResults,
81810
+ debugTruncate: () => debugTruncate
81755
81811
  });
81756
81812
  import dotenv2 from "dotenv";
81757
81813
  import { createAnthropic as createAnthropic2 } from "@ai-sdk/anthropic";
@@ -81764,6 +81820,19 @@ import { EventEmitter as EventEmitter5 } from "events";
81764
81820
  import { existsSync as existsSync7 } from "fs";
81765
81821
  import { readFile as readFile3, stat, readdir as readdir3 } from "fs/promises";
81766
81822
  import { resolve as resolve7, isAbsolute as isAbsolute6, dirname as dirname5, basename, normalize as normalize2, sep as sep5 } from "path";
81823
+ function debugTruncate(s, limit = 200) {
81824
+ if (s.length <= limit) return s;
81825
+ const half = Math.floor(limit / 2);
81826
+ return s.substring(0, half) + ` ... [${s.length} chars] ... ` + s.substring(s.length - half);
81827
+ }
81828
+ function debugLogToolResults(toolResults) {
81829
+ if (!toolResults || toolResults.length === 0) return;
81830
+ for (const tr of toolResults) {
81831
+ const argsStr = JSON.stringify(tr.args || {});
81832
+ const resultStr = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result || "");
81833
+ console.log(`[DEBUG] tool: ${tr.toolName} | args: ${debugTruncate(argsStr)} | result: ${debugTruncate(resultStr)}`);
81834
+ }
81835
+ }
81767
81836
  var ENGINE_ACTIVITY_TIMEOUT_DEFAULT, ENGINE_ACTIVITY_TIMEOUT_MIN, ENGINE_ACTIVITY_TIMEOUT_MAX, MAX_TOOL_ITERATIONS, MAX_HISTORY_MESSAGES, MAX_IMAGE_FILE_SIZE, ProbeAgent;
81768
81837
  var init_ProbeAgent = __esm({
81769
81838
  "src/agent/ProbeAgent.js"() {
@@ -81872,6 +81941,7 @@ var init_ProbeAgent = __esm({
81872
81941
  this.enableExecutePlan = !!options.enableExecutePlan;
81873
81942
  this.debug = options.debug || process.env.DEBUG === "1";
81874
81943
  this.cancelled = false;
81944
+ this._abortController = new AbortController();
81875
81945
  this.tracer = options.tracer || null;
81876
81946
  this.outline = !!options.outline;
81877
81947
  this.searchDelegate = options.searchDelegate !== void 0 ? !!options.searchDelegate : true;
@@ -82317,6 +82387,8 @@ var init_ProbeAgent = __esm({
82317
82387
  searchDelegateModel: this.searchDelegateModel,
82318
82388
  delegationManager: this.delegationManager,
82319
82389
  // Per-instance delegation limits
82390
+ parentAbortSignal: this._abortController.signal,
82391
+ // Propagate cancellation to delegations
82320
82392
  outputBuffer: this._outputBuffer,
82321
82393
  concurrencyLimiter: this.concurrencyLimiter,
82322
82394
  // Global AI concurrency limiter
@@ -82764,6 +82836,15 @@ var init_ProbeAgent = __esm({
82764
82836
  }
82765
82837
  const controller = new AbortController();
82766
82838
  const timeoutState = { timeoutId: null };
82839
+ if (this._abortController.signal.aborted) {
82840
+ controller.abort();
82841
+ } else {
82842
+ const onAgentAbort = () => controller.abort();
82843
+ this._abortController.signal.addEventListener("abort", onAgentAbort, { once: true });
82844
+ controller.signal.addEventListener("abort", () => {
82845
+ this._abortController.signal.removeEventListener("abort", onAgentAbort);
82846
+ }, { once: true });
82847
+ }
82767
82848
  if (this.maxOperationTimeout && this.maxOperationTimeout > 0) {
82768
82849
  timeoutState.timeoutId = setTimeout(() => {
82769
82850
  controller.abort();
@@ -83067,7 +83148,8 @@ var init_ProbeAgent = __esm({
83067
83148
  allowEdit: this.allowEdit,
83068
83149
  allowedTools: allowedToolsForDelegate,
83069
83150
  debug: this.debug,
83070
- tracer: this.tracer
83151
+ tracer: this.tracer,
83152
+ parentAbortSignal: this._abortController.signal
83071
83153
  };
83072
83154
  if (this.debug) {
83073
83155
  console.log(`[DEBUG] Executing delegate tool`);
@@ -84229,12 +84311,6 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84229
84311
  });
84230
84312
  const systemMessage = await this.getSystemMessage();
84231
84313
  let userMessage = { role: "user", content: message.trim() };
84232
- if (this.enableTasks) {
84233
- userMessage.content = userMessage.content + "\n\n" + taskGuidancePrompt;
84234
- if (this.debug) {
84235
- console.log("[DEBUG] Task guidance injected into user message");
84236
- }
84237
- }
84238
84314
  if (options.schema && !options._schemaFormatted) {
84239
84315
  const schemaInstructions = generateSchemaInstructions(options.schema, { debug: this.debug });
84240
84316
  userMessage.content = message.trim() + schemaInstructions;
@@ -84390,6 +84466,10 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84390
84466
  completionResult = result;
84391
84467
  completionAttempted = true;
84392
84468
  }, toolContext);
84469
+ if (this.debug) {
84470
+ const toolNames = Object.keys(tools2);
84471
+ console.log(`[DEBUG] Agent tools registered (${toolNames.length}): ${toolNames.join(", ")}`);
84472
+ }
84393
84473
  let maxResponseTokens = this.maxResponseTokens;
84394
84474
  if (!maxResponseTokens) {
84395
84475
  maxResponseTokens = 4e3;
@@ -84429,6 +84509,7 @@ You are working with a workspace. Available paths: ${workspaceDesc}
84429
84509
  }
84430
84510
  if (this.debug) {
84431
84511
  console.log(`[DEBUG] Step ${currentIteration}/${maxIterations} finished (reason: ${finishReason}, tools: ${toolResults?.length || 0})`);
84512
+ debugLogToolResults(toolResults);
84432
84513
  }
84433
84514
  }
84434
84515
  };
@@ -84585,6 +84666,7 @@ Double-check your response based on the criteria above. If everything looks good
84585
84666
  }
84586
84667
  if (this.debug) {
84587
84668
  console.log(`[DEBUG] Completion prompt step finished (reason: ${finishReason}, tools: ${toolResults?.length || 0})`);
84669
+ debugLogToolResults(toolResults);
84588
84670
  }
84589
84671
  }
84590
84672
  };
@@ -85295,6 +85377,9 @@ Convert your previous response content into actual JSON data that follows this s
85295
85377
  * Clean up resources (including MCP connections)
85296
85378
  */
85297
85379
  async cleanup() {
85380
+ if (!this._abortController.signal.aborted) {
85381
+ this._abortController.abort();
85382
+ }
85298
85383
  if (this.mcpBridge) {
85299
85384
  try {
85300
85385
  await this.mcpBridge.cleanup();
@@ -85318,14 +85403,25 @@ Convert your previous response content into actual JSON data that follows this s
85318
85403
  this.clearHistory();
85319
85404
  }
85320
85405
  /**
85321
- * Cancel the current request
85406
+ * Cancel the current request and all in-flight delegations.
85407
+ * Aborts the internal AbortController so streamText, subagents,
85408
+ * and any code checking the signal will stop.
85322
85409
  */
85323
85410
  cancel() {
85324
85411
  this.cancelled = true;
85412
+ this._abortController.abort();
85325
85413
  if (this.debug) {
85326
85414
  console.log(`[DEBUG] Agent cancelled for session ${this.sessionId}`);
85327
85415
  }
85328
85416
  }
85417
+ /**
85418
+ * Get the abort signal for this agent.
85419
+ * Delegations and subagents should check this signal.
85420
+ * @returns {AbortSignal}
85421
+ */
85422
+ get abortSignal() {
85423
+ return this._abortController.signal;
85424
+ }
85329
85425
  };
85330
85426
  }
85331
85427
  });
@@ -81,8 +81,20 @@ If the solution is clear, you can jump to implementation right away. If not, ask
81
81
  - Check imports and existing utilities before creating new helpers — the project may already have what you need.
82
82
 
83
83
  # Task Planning
84
- - If the task tool is available, use it to break complex work into milestones before starting implementation.
85
- - Stay flexible if your understanding changes mid-task, add, remove, or reorganize tasks as needed. The plan should serve you, not constrain you.
84
+ When the request has **multiple distinct goals** (e.g. "Fix bug A AND add feature B"), use the task tool to track them:
85
+ - Call the task tool with action="create" and a tasks array. Each task must have an "id" field.
86
+ - Update task status to "in_progress" when starting and "completed" when done.
87
+ - All tasks must be completed or cancelled before calling attempt_completion.
88
+ - Stay flexible — add, remove, or reorganize tasks as your understanding changes.
89
+
90
+ Do NOT create tasks for single-goal requests, even complex ones. Multiple internal steps for one goal (search, read, analyze, implement) do not need tasks.
91
+
92
+ # Discovering Project Commands
93
+ Before building or testing, determine the project's toolchain:
94
+ - Check for Makefile, package.json (scripts), Cargo.toml, go.mod, pyproject.toml, or similar
95
+ - Look for CI config (.github/workflows/, .gitlab-ci.yml) to see what commands CI runs
96
+ - Read README for build/test instructions if the above are unclear
97
+ - Common patterns: \`make build\`/\`make test\`, \`npm run build\`/\`npm test\`, \`cargo build\`/\`cargo test\`, \`go build ./...\`/\`go test ./...\`, \`python -m pytest\`
86
98
 
87
99
  # During Implementation
88
100
  - Always create a new branch before making changes to the codebase.
@@ -93,12 +105,22 @@ If the solution is clear, you can jump to implementation right away. If not, ask
93
105
  - When editing files, keep edits focused and minimal. For changes spanning more than a few lines, prefer line-targeted editing (start_line/end_line) over text replacement (old_string) — it constrains scope and prevents accidental removal of adjacent content. Never include unrelated sections in an edit operation.
94
106
  - After every significant change, verify the project still builds and passes linting. Do not wait until the end to discover breakage.
95
107
 
96
- # After Implementation
97
- - Verify the project builds successfully. If it doesn't, fix the build before moving on.
98
- - Run lint and typecheck commands if known for the project. Fix any new warnings or errors you introduced.
99
- - Add tests for any new or changed functionality. Tests must cover the main path and important edge cases.
100
- - Run the project's full test suite. If any tests fail (including pre-existing ones you may have broken), fix them before finishing.
101
- - When the task is done, respond to the user with a concise summary of what was implemented, what files were changed, and any relevant details. Include links (e.g. pull request URL) so the user has everything they need.
108
+ # Writing Tests
109
+ Every change must include tests. Before writing them:
110
+ - Find existing test files for the module you changed look in \`tests/\`, \`__tests__/\`, \`*_test.go\`, \`*.test.js\`, \`*.spec.ts\`, or co-located test modules (\`#[cfg(test)]\` in Rust).
111
+ - Read those tests to understand the project's testing patterns: framework, assertion style, mocking approach, file naming, test organization.
112
+ - Prefer extending an existing test file over creating a new one when your change is in the same module.
113
+ - Write tests that cover the main path and important edge cases. Include a failing-input test when relevant.
114
+ - When fixing a bug, write a failing test first that reproduces the bug, then fix the code to make it pass.
115
+
116
+ # Verify Changes
117
+ Before committing or creating a PR, run through this checklist:
118
+ 1. **Build** — run the project-appropriate build command (go build, npm run build, cargo build, make, etc.). Fix any compilation errors.
119
+ 2. **Lint & typecheck** — run linter/formatter if the project has one (eslint, clippy, golangci-lint, etc.). Fix any new warnings.
120
+ 3. **Test** — run the full test suite (go test ./..., npm test, cargo test, make test, pytest, etc.). Fix any failures, including pre-existing tests you may have broken.
121
+ 4. **Review** — re-read your diff. Ensure no debug code, no unrelated changes, no secrets, no missing files.
122
+
123
+ Do NOT skip verification. Do NOT proceed to PR creation with a broken build or failing tests.
102
124
 
103
125
  # GitHub Integration
104
126
  - Use the \`gh\` CLI for all GitHub operations: issues, pull requests, checks, releases.