@probelabs/probe 0.6.0-rc292 → 0.6.0-rc294

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.
@@ -27566,7 +27566,7 @@ var init_vercel = __esm({
27566
27566
  name: "search",
27567
27567
  description: searchDelegate ? searchDelegateDescription : searchDescription,
27568
27568
  inputSchema: searchSchema,
27569
- execute: async ({ query: searchQuery, path: path9, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage }) => {
27569
+ execute: async ({ query: searchQuery, path: path9, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage, workingDirectory }) => {
27570
27570
  if (!exact && searchQuery) {
27571
27571
  const originalQuery = searchQuery;
27572
27572
  searchQuery = autoQuoteSearchTerms(searchQuery);
@@ -27575,18 +27575,19 @@ var init_vercel = __esm({
27575
27575
  }
27576
27576
  }
27577
27577
  const effectiveMaxTokens = paramMaxTokens || maxTokens;
27578
+ const effectiveSearchCwd = workingDirectory || options.cwd || ".";
27578
27579
  let searchPaths;
27579
27580
  if (path9) {
27580
- searchPaths = parseAndResolvePaths(path9, options.cwd);
27581
+ searchPaths = parseAndResolvePaths(path9, effectiveSearchCwd);
27581
27582
  }
27582
27583
  if (!searchPaths || searchPaths.length === 0) {
27583
- searchPaths = [options.cwd || "."];
27584
+ searchPaths = [effectiveSearchCwd];
27584
27585
  }
27585
27586
  const searchPath = searchPaths.join(" ");
27586
27587
  const searchOptions = {
27587
27588
  query: searchQuery,
27588
27589
  path: searchPath,
27589
- cwd: options.cwd,
27590
+ cwd: effectiveSearchCwd,
27590
27591
  // Working directory for resolving relative paths
27591
27592
  allowTests: allow_tests ?? true,
27592
27593
  exact,
@@ -27637,7 +27638,7 @@ var init_vercel = __esm({
27637
27638
  try {
27638
27639
  const result = maybeAnnotate(await runRawSearch());
27639
27640
  if (options.fileTracker && typeof result === "string") {
27640
- options.fileTracker.trackFilesFromOutput(result, options.cwd || ".").catch(() => {
27641
+ options.fileTracker.trackFilesFromOutput(result, effectiveSearchCwd).catch(() => {
27641
27642
  });
27642
27643
  }
27643
27644
  return result;
@@ -27690,7 +27691,7 @@ var init_vercel = __esm({
27690
27691
  }
27691
27692
  const fallbackResult = maybeAnnotate(await runRawSearch());
27692
27693
  if (options.fileTracker && typeof fallbackResult === "string") {
27693
- options.fileTracker.trackFilesFromOutput(fallbackResult, options.cwd || ".").catch(() => {
27694
+ options.fileTracker.trackFilesFromOutput(fallbackResult, effectiveSearchCwd).catch(() => {
27694
27695
  });
27695
27696
  }
27696
27697
  return fallbackResult;
@@ -27753,7 +27754,7 @@ var init_vercel = __esm({
27753
27754
  try {
27754
27755
  const fallbackResult2 = maybeAnnotate(await runRawSearch());
27755
27756
  if (options.fileTracker && typeof fallbackResult2 === "string") {
27756
- options.fileTracker.trackFilesFromOutput(fallbackResult2, options.cwd || ".").catch(() => {
27757
+ options.fileTracker.trackFilesFromOutput(fallbackResult2, effectiveSearchCwd).catch(() => {
27757
27758
  });
27758
27759
  }
27759
27760
  return fallbackResult2;
@@ -27807,9 +27808,9 @@ var init_vercel = __esm({
27807
27808
  name: "extract",
27808
27809
  description: extractDescription,
27809
27810
  inputSchema: extractSchema,
27810
- execute: async ({ targets, input_content, line, end_line, allow_tests, context_lines, format }) => {
27811
+ execute: async ({ targets, input_content, line, end_line, allow_tests, context_lines, format, workingDirectory }) => {
27811
27812
  try {
27812
- const effectiveCwd = options.cwd || ".";
27813
+ const effectiveCwd = workingDirectory || options.cwd || ".";
27813
27814
  if (debug) {
27814
27815
  if (targets) {
27815
27816
  console.error(`Executing extract with targets: "${targets}", cwd: "${effectiveCwd}", context lines: ${context_lines || 10}`);
@@ -48638,6 +48639,10 @@ var init_file_lister = __esm({
48638
48639
  });
48639
48640
 
48640
48641
  // src/tools/fileTracker.js
48642
+ function normalizePath(filePath) {
48643
+ if (!filePath) return filePath;
48644
+ return (0, import_path10.resolve)(filePath);
48645
+ }
48641
48646
  function computeContentHash(content) {
48642
48647
  const normalized = (content || "").split("\n").map((l) => l.trimEnd()).join("\n");
48643
48648
  return (0, import_crypto3.createHash)("sha256").update(normalized).digest("hex").slice(0, 16);
@@ -48700,10 +48705,11 @@ var init_fileTracker = __esm({
48700
48705
  * @param {string} resolvedPath - Absolute path to the file
48701
48706
  */
48702
48707
  markFileSeen(resolvedPath) {
48703
- this._seenFiles.add(resolvedPath);
48704
- this._textEditCounts.set(resolvedPath, 0);
48708
+ const normalized = normalizePath(resolvedPath);
48709
+ this._seenFiles.add(normalized);
48710
+ this._textEditCounts.set(normalized, 0);
48705
48711
  if (this.debug) {
48706
- console.error(`[FileTracker] Marked as seen: ${resolvedPath}`);
48712
+ console.error(`[FileTracker] Marked as seen: ${normalized}`);
48707
48713
  }
48708
48714
  }
48709
48715
  /**
@@ -48712,7 +48718,7 @@ var init_fileTracker = __esm({
48712
48718
  * @returns {boolean}
48713
48719
  */
48714
48720
  isFileSeen(resolvedPath) {
48715
- return this._seenFiles.has(resolvedPath);
48721
+ return this._seenFiles.has(normalizePath(resolvedPath));
48716
48722
  }
48717
48723
  /**
48718
48724
  * Store a content hash for a symbol in a file.
@@ -48724,7 +48730,7 @@ var init_fileTracker = __esm({
48724
48730
  * @param {string} [source='extract'] - How the content was obtained
48725
48731
  */
48726
48732
  trackSymbolContent(resolvedPath, symbolName, code, startLine, endLine, source = "extract") {
48727
- const key = `${resolvedPath}#${symbolName}`;
48733
+ const key = `${normalizePath(resolvedPath)}#${symbolName}`;
48728
48734
  const contentHash = computeContentHash(code);
48729
48735
  this._contentRecords.set(key, {
48730
48736
  contentHash,
@@ -48745,7 +48751,7 @@ var init_fileTracker = __esm({
48745
48751
  * @returns {Object|null} The stored record or null
48746
48752
  */
48747
48753
  getSymbolRecord(resolvedPath, symbolName) {
48748
- return this._contentRecords.get(`${resolvedPath}#${symbolName}`) || null;
48754
+ return this._contentRecords.get(`${normalizePath(resolvedPath)}#${symbolName}`) || null;
48749
48755
  }
48750
48756
  /**
48751
48757
  * Check if a symbol's current content matches what was stored.
@@ -48755,7 +48761,7 @@ var init_fileTracker = __esm({
48755
48761
  * @returns {{ok: boolean, reason?: string, message?: string}}
48756
48762
  */
48757
48763
  checkSymbolContent(resolvedPath, symbolName, currentCode) {
48758
- const key = `${resolvedPath}#${symbolName}`;
48764
+ const key = `${normalizePath(resolvedPath)}#${symbolName}`;
48759
48765
  const record2 = this._contentRecords.get(key);
48760
48766
  if (!record2) {
48761
48767
  return { ok: true };
@@ -48832,7 +48838,7 @@ var init_fileTracker = __esm({
48832
48838
  * @returns {{ok: boolean, reason?: string, message?: string}}
48833
48839
  */
48834
48840
  checkBeforeEdit(resolvedPath) {
48835
- if (!this._seenFiles.has(resolvedPath)) {
48841
+ if (!this._seenFiles.has(normalizePath(resolvedPath))) {
48836
48842
  return {
48837
48843
  ok: false,
48838
48844
  reason: "untracked",
@@ -48847,8 +48853,9 @@ var init_fileTracker = __esm({
48847
48853
  * @param {string} resolvedPath - Absolute path to the file
48848
48854
  */
48849
48855
  async trackFileAfterWrite(resolvedPath) {
48850
- this._seenFiles.add(resolvedPath);
48851
- this.invalidateFileRecords(resolvedPath);
48856
+ const normalized = normalizePath(resolvedPath);
48857
+ this._seenFiles.add(normalized);
48858
+ this.invalidateFileRecords(normalized);
48852
48859
  }
48853
48860
  /**
48854
48861
  * Record a text-mode edit (old_string/new_string) to a file.
@@ -48856,10 +48863,11 @@ var init_fileTracker = __esm({
48856
48863
  * @param {string} resolvedPath - Absolute path to the file
48857
48864
  */
48858
48865
  recordTextEdit(resolvedPath) {
48859
- const count = (this._textEditCounts.get(resolvedPath) || 0) + 1;
48860
- this._textEditCounts.set(resolvedPath, count);
48866
+ const normalized = normalizePath(resolvedPath);
48867
+ const count = (this._textEditCounts.get(normalized) || 0) + 1;
48868
+ this._textEditCounts.set(normalized, count);
48861
48869
  if (this.debug) {
48862
- console.error(`[FileTracker] Text edit #${count} for ${resolvedPath}`);
48870
+ console.error(`[FileTracker] Text edit #${count} for ${normalized}`);
48863
48871
  }
48864
48872
  }
48865
48873
  /**
@@ -48868,7 +48876,7 @@ var init_fileTracker = __esm({
48868
48876
  * @returns {{ok: boolean, editCount?: number, message?: string}}
48869
48877
  */
48870
48878
  checkTextEditStaleness(resolvedPath) {
48871
- const count = this._textEditCounts.get(resolvedPath) || 0;
48879
+ const count = this._textEditCounts.get(normalizePath(resolvedPath)) || 0;
48872
48880
  if (count >= this.maxConsecutiveTextEdits) {
48873
48881
  return {
48874
48882
  ok: false,
@@ -48897,7 +48905,7 @@ var init_fileTracker = __esm({
48897
48905
  * @param {string} resolvedPath - Absolute path to the file
48898
48906
  */
48899
48907
  invalidateFileRecords(resolvedPath) {
48900
- const prefix = resolvedPath + "#";
48908
+ const prefix = normalizePath(resolvedPath) + "#";
48901
48909
  for (const key of this._contentRecords.keys()) {
48902
48910
  if (key.startsWith(prefix)) {
48903
48911
  this._contentRecords.delete(key);
@@ -48913,7 +48921,7 @@ var init_fileTracker = __esm({
48913
48921
  * @returns {boolean}
48914
48922
  */
48915
48923
  isTracked(resolvedPath) {
48916
- return this.isFileSeen(resolvedPath);
48924
+ return this.isFileSeen(normalizePath(resolvedPath));
48917
48925
  }
48918
48926
  /**
48919
48927
  * Clear all tracking state.
@@ -69026,7 +69034,7 @@ var init_graph_builder = __esm({
69026
69034
  applyLinkStyles() {
69027
69035
  if (!this.pendingLinkStyles.length || !this.edges.length)
69028
69036
  return;
69029
- const normalize3 = (s) => {
69037
+ const normalize4 = (s) => {
69030
69038
  const out = {};
69031
69039
  for (const [kRaw, vRaw] of Object.entries(s)) {
69032
69040
  const k = kRaw.trim().toLowerCase();
@@ -69047,7 +69055,7 @@ var init_graph_builder = __esm({
69047
69055
  return out;
69048
69056
  };
69049
69057
  for (const cmd of this.pendingLinkStyles) {
69050
- const style = normalize3(cmd.props);
69058
+ const style = normalize4(cmd.props);
69051
69059
  for (const idx of cmd.indices) {
69052
69060
  if (idx >= 0 && idx < this.edges.length) {
69053
69061
  const e = this.edges[idx];
@@ -76314,7 +76322,7 @@ var require_layout = __commonJS({
76314
76322
  "use strict";
76315
76323
  var _ = require_lodash2();
76316
76324
  var acyclic = require_acyclic();
76317
- var normalize3 = require_normalize();
76325
+ var normalize4 = require_normalize();
76318
76326
  var rank = require_rank();
76319
76327
  var normalizeRanks = require_util().normalizeRanks;
76320
76328
  var parentDummyChains = require_parent_dummy_chains();
@@ -76376,7 +76384,7 @@ var require_layout = __commonJS({
76376
76384
  removeEdgeLabelProxies(g);
76377
76385
  });
76378
76386
  time3(" normalize.run", function() {
76379
- normalize3.run(g);
76387
+ normalize4.run(g);
76380
76388
  });
76381
76389
  time3(" parentDummyChains", function() {
76382
76390
  parentDummyChains(g);
@@ -76403,7 +76411,7 @@ var require_layout = __commonJS({
76403
76411
  removeBorderNodes(g);
76404
76412
  });
76405
76413
  time3(" normalize.undo", function() {
76406
- normalize3.undo(g);
76414
+ normalize4.undo(g);
76407
76415
  });
76408
76416
  time3(" fixupEdgeLabelCoords", function() {
76409
76417
  fixupEdgeLabelCoords(g);
@@ -82774,8 +82782,8 @@ var require_resolve = __commonJS({
82774
82782
  }
82775
82783
  return count;
82776
82784
  }
82777
- function getFullPath(resolver, id = "", normalize3) {
82778
- if (normalize3 !== false)
82785
+ function getFullPath(resolver, id = "", normalize4) {
82786
+ if (normalize4 !== false)
82779
82787
  id = normalizeId(id);
82780
82788
  const p = resolver.parse(id);
82781
82789
  return _getFullPath(resolver, p);
@@ -84115,7 +84123,7 @@ var require_fast_uri = __commonJS({
84115
84123
  "use strict";
84116
84124
  var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils2();
84117
84125
  var { SCHEMES, getSchemeHandler } = require_schemes();
84118
- function normalize3(uri, options) {
84126
+ function normalize4(uri, options) {
84119
84127
  if (typeof uri === "string") {
84120
84128
  uri = /** @type {T} */
84121
84129
  serialize(parse11(uri, options), options);
@@ -84351,7 +84359,7 @@ var require_fast_uri = __commonJS({
84351
84359
  }
84352
84360
  var fastUri = {
84353
84361
  SCHEMES,
84354
- normalize: normalize3,
84362
+ normalize: normalize4,
84355
84363
  resolve: resolve9,
84356
84364
  resolveComponent,
84357
84365
  equal,
@@ -88558,9 +88566,9 @@ If the solution is clear, you can jump to implementation right away. If not, ask
88558
88566
  - Do not add code comments unless the logic is genuinely complex and non-obvious.
88559
88567
 
88560
88568
  # Before Implementation
88561
- - Focus on high-level design patterns and system organization
88562
- - Identify architectural patterns and component relationships
88563
- - Evaluate system structure and suggest architectural improvements
88569
+ - Read tests first \u2014 find existing test files for the module you're changing. They reveal expected behavior, edge cases, and the project's testing patterns.
88570
+ - Read neighboring files \u2014 understand naming conventions, error handling patterns, import style, and existing utilities before creating new ones.
88571
+ - Trace the call chain \u2014 follow how the code you're changing is called and what depends on it. Check interfaces, types, and consumers.
88564
88572
  - Focus on backward compatibility
88565
88573
  - Consider scalability, maintainability, and extensibility in your analysis
88566
88574
 
@@ -88585,6 +88593,20 @@ Before building or testing, determine the project's toolchain:
88585
88593
  - Read README for build/test instructions if the above are unclear
88586
88594
  - Common patterns: \`make build\`/\`make test\`, \`npm run build\`/\`npm test\`, \`cargo build\`/\`cargo test\`, \`go build ./...\`/\`go test ./...\`, \`python -m pytest\`
88587
88595
 
88596
+ # File Editing Rules
88597
+ You have access to the \`edit\`, \`create\`, and \`multi_edit\` tools for modifying files. You MUST use these tools for ALL code changes. They are purpose-built, atomic, and safe.
88598
+
88599
+ DO NOT use sed, awk, echo/cat redirection, or heredocs to modify source code. These commands cause real damage in practice: truncated lines, duplicate code blocks, broken syntax. Every bad edit wastes iterations on fix-up commits.
88600
+
88601
+ Use the right tool:
88602
+ 1. To MODIFY existing code \u2192 \`edit\` tool (old_string \u2192 new_string, or start_line/end_line)
88603
+ 2. To CREATE a new file \u2192 \`create\` tool
88604
+ 3. To CHANGE multiple files at once \u2192 \`multi_edit\` tool
88605
+ 4. To READ code \u2192 \`extract\` or \`search\` tools
88606
+ 5. If \`edit\` fails with "file has not been read yet" \u2192 use \`extract\` with the EXACT same file path you will pass to \`edit\`. Relative vs absolute path mismatch causes this error. Use the same path format consistently. If it still fails, use bash \`cat\` to read the file, then use \`create\` to write the entire modified file. Do NOT fall back to sed.
88607
+
88608
+ Bash is fine for: formatters (gofmt, prettier, black), build/test/lint commands, git operations, and read-only file inspection (cat, head, tail). sed/awk should ONLY be used for trivial non-code tasks (e.g., config file tweaks) where the replacement is a simple literal string swap.
88609
+
88588
88610
  # During Implementation
88589
88611
  - Always create a new branch before making changes to the codebase.
88590
88612
  - Fix problems at the root cause, not with surface-level patches. Prefer general solutions over special cases.
@@ -88611,6 +88633,22 @@ Before committing or creating a PR, run through this checklist:
88611
88633
 
88612
88634
  Do NOT skip verification. Do NOT proceed to PR creation with a broken build or failing tests.
88613
88635
 
88636
+ # Output Integrity
88637
+ Your final output MUST accurately reflect what ACTUALLY happened. Do NOT fabricate, hallucinate, or report aspirational results.
88638
+
88639
+ - Only report PR URLs you actually created or updated with \`gh pr create\` or \`git push\`. If you checked out an existing PR but did NOT push changes to it, do NOT claim you updated it.
88640
+ - Describe what you ACTUALLY DID, not what you planned or intended to do. If you ran out of iterations, say so. If tests failed, say so.
88641
+ - Only list files you actually modified AND committed.
88642
+ - If you could not complete the task \u2014 ran out of iterations, tests failed, build broken, push rejected \u2014 report the real reason honestly.
88643
+
88644
+ NEVER claim success when:
88645
+ - You did not run \`git push\` successfully
88646
+ - Tests failed and you did not fix them
88647
+ - You hit the iteration limit before completing the work
88648
+ - You only analyzed/investigated but did not implement changes
88649
+
88650
+ A false success report is WORSE than an honest failure \u2014 it misleads the user into thinking work is done when it is not.
88651
+
88614
88652
  # GitHub Integration
88615
88653
  - Use the \`gh\` CLI for all GitHub operations: issues, pull requests, checks, releases.
88616
88654
  - To view issues or PRs: \`gh issue view <number>\`, \`gh pr view <number>\`.
@@ -100582,9 +100620,13 @@ var init_ProbeAgent = __esm({
100582
100620
  }
100583
100621
  return await this.fallbackManager.executeWithFallback(
100584
100622
  async (provider, model, config2) => {
100623
+ let fallbackModel = provider(model);
100624
+ if (this.concurrencyLimiter) {
100625
+ fallbackModel = _ProbeAgent._wrapModelWithLimiter(fallbackModel, this.concurrencyLimiter, this.debug);
100626
+ }
100585
100627
  const fallbackOptions = {
100586
100628
  ...options,
100587
- model: provider(model),
100629
+ model: fallbackModel,
100588
100630
  abortSignal: controller.signal
100589
100631
  };
100590
100632
  if (config2.provider !== "google" && fallbackOptions.tools) {
@@ -100612,6 +100654,132 @@ var init_ProbeAgent = __esm({
100612
100654
  }
100613
100655
  );
100614
100656
  }
100657
+ /**
100658
+ * Wrap a LanguageModelV1 model so each doStream/doGenerate call acquires and
100659
+ * releases a concurrency limiter slot. This gates individual LLM API calls
100660
+ * (seconds each) instead of entire multi-step agent sessions (minutes).
100661
+ *
100662
+ * @param {Object} model - LanguageModelV1 model instance
100663
+ * @param {Object} limiter - Concurrency limiter with acquire/release/getStats
100664
+ * @param {boolean} debug - Enable debug logging
100665
+ * @returns {Object} Wrapped model with per-call concurrency gating
100666
+ * @private
100667
+ */
100668
+ static _wrapModelWithLimiter(model, limiter, debug) {
100669
+ return new Proxy(model, {
100670
+ get(target, prop) {
100671
+ if (prop === "doStream") {
100672
+ return async function(...args) {
100673
+ await limiter.acquire(null);
100674
+ if (debug) {
100675
+ const stats = limiter.getStats();
100676
+ console.log(`[DEBUG] Acquired AI slot for LLM call (${stats.globalActive}/${stats.maxConcurrent}, queue: ${stats.queueSize})`);
100677
+ }
100678
+ try {
100679
+ const result = await target.doStream(...args);
100680
+ const originalStream = result.stream;
100681
+ const originalReader = originalStream.getReader();
100682
+ let released = false;
100683
+ const releaseOnce = () => {
100684
+ if (released) return;
100685
+ released = true;
100686
+ limiter.release(null);
100687
+ };
100688
+ const wrappedStream = new ReadableStream({
100689
+ async pull(controller) {
100690
+ try {
100691
+ const { done, value: value2 } = await originalReader.read();
100692
+ if (done) {
100693
+ controller.close();
100694
+ releaseOnce();
100695
+ if (debug) {
100696
+ const stats = limiter.getStats();
100697
+ console.log(`[DEBUG] Released AI slot after LLM stream complete (${stats.globalActive}/${stats.maxConcurrent})`);
100698
+ }
100699
+ } else {
100700
+ controller.enqueue(value2);
100701
+ }
100702
+ } catch (err) {
100703
+ releaseOnce();
100704
+ if (debug) {
100705
+ console.log(`[DEBUG] Released AI slot on LLM stream error`);
100706
+ }
100707
+ controller.error(err);
100708
+ }
100709
+ },
100710
+ cancel() {
100711
+ releaseOnce();
100712
+ if (debug) {
100713
+ console.log(`[DEBUG] Released AI slot on LLM stream cancel`);
100714
+ }
100715
+ originalReader.cancel();
100716
+ }
100717
+ });
100718
+ return { ...result, stream: wrappedStream };
100719
+ } catch (err) {
100720
+ limiter.release(null);
100721
+ if (debug) {
100722
+ console.log(`[DEBUG] Released AI slot on doStream error`);
100723
+ }
100724
+ throw err;
100725
+ }
100726
+ };
100727
+ }
100728
+ if (prop === "doGenerate") {
100729
+ return async function(...args) {
100730
+ await limiter.acquire(null);
100731
+ if (debug) {
100732
+ const stats = limiter.getStats();
100733
+ console.log(`[DEBUG] Acquired AI slot for LLM generate (${stats.globalActive}/${stats.maxConcurrent})`);
100734
+ }
100735
+ try {
100736
+ const result = await target.doGenerate(...args);
100737
+ return result;
100738
+ } finally {
100739
+ limiter.release(null);
100740
+ if (debug) {
100741
+ const stats = limiter.getStats();
100742
+ console.log(`[DEBUG] Released AI slot after LLM generate (${stats.globalActive}/${stats.maxConcurrent})`);
100743
+ }
100744
+ }
100745
+ };
100746
+ }
100747
+ const value = target[prop];
100748
+ return typeof value === "function" ? value.bind(target) : value;
100749
+ }
100750
+ });
100751
+ }
100752
+ /**
100753
+ * Wrap an engine stream result so its textStream async generator acquires
100754
+ * and releases a concurrency limiter slot. Acquire happens when iteration
100755
+ * begins; release happens in finally (completion, error, or break).
100756
+ *
100757
+ * @param {Object} result - Engine result with { textStream, usage, ... }
100758
+ * @param {Object} limiter - Concurrency limiter with acquire/release/getStats
100759
+ * @param {boolean} debug - Enable debug logging
100760
+ * @returns {Object} Result with wrapped textStream
100761
+ * @private
100762
+ */
100763
+ static _wrapEngineStreamWithLimiter(result, limiter, debug) {
100764
+ const originalStream = result.textStream;
100765
+ async function* gatedStream() {
100766
+ await limiter.acquire(null);
100767
+ if (debug) {
100768
+ const stats = limiter.getStats();
100769
+ console.log(`[DEBUG] Acquired AI slot for engine stream (${stats.globalActive}/${stats.maxConcurrent}, queue: ${stats.queueSize})`);
100770
+ }
100771
+ try {
100772
+ yield* originalStream;
100773
+ } finally {
100774
+ limiter.release(null);
100775
+ if (debug) {
100776
+ const stats = limiter.getStats();
100777
+ console.log(`[DEBUG] Released AI slot after engine stream (${stats.globalActive}/${stats.maxConcurrent})`);
100778
+ }
100779
+ }
100780
+ }
100781
+ return { ...result, textStream: gatedStream() };
100782
+ }
100615
100783
  /**
100616
100784
  * Execute streamText with retry and fallback support
100617
100785
  * @param {Object} options - streamText options
@@ -100620,12 +100788,8 @@ var init_ProbeAgent = __esm({
100620
100788
  */
100621
100789
  async streamTextWithRetryAndFallback(options) {
100622
100790
  const limiter = this.concurrencyLimiter;
100623
- if (limiter) {
100624
- await limiter.acquire(null);
100625
- if (this.debug) {
100626
- const stats = limiter.getStats();
100627
- console.log(`[DEBUG] Acquired global AI concurrency slot (${stats.globalActive}/${stats.maxConcurrent}, queue: ${stats.queueSize})`);
100628
- }
100791
+ if (limiter && options.model) {
100792
+ options = { ...options, model: _ProbeAgent._wrapModelWithLimiter(options.model, limiter, this.debug) };
100629
100793
  }
100630
100794
  const controller = new AbortController();
100631
100795
  const timeoutState = { timeoutId: null };
@@ -100653,6 +100817,9 @@ var init_ProbeAgent = __esm({
100653
100817
  if (useClaudeCode || useCodex) {
100654
100818
  try {
100655
100819
  result = await this._tryEngineStreamPath(options, controller, timeoutState);
100820
+ if (result && limiter) {
100821
+ result = _ProbeAgent._wrapEngineStreamWithLimiter(result, limiter, this.debug);
100822
+ }
100656
100823
  } catch (error40) {
100657
100824
  if (this.debug) {
100658
100825
  const engineType = useClaudeCode ? "Claude Code" : "Codex";
@@ -100663,41 +100830,7 @@ var init_ProbeAgent = __esm({
100663
100830
  if (!result) {
100664
100831
  result = await this._executeWithVercelProvider(options, controller);
100665
100832
  }
100666
- if (limiter && result.textStream) {
100667
- const originalStream = result.textStream;
100668
- const debug = this.debug;
100669
- const wrappedStream = (async function* () {
100670
- try {
100671
- for await (const chunk of originalStream) {
100672
- yield chunk;
100673
- }
100674
- } finally {
100675
- limiter.release(null);
100676
- if (debug) {
100677
- const stats = limiter.getStats();
100678
- console.log(`[DEBUG] Released global AI concurrency slot (${stats.globalActive}/${stats.maxConcurrent}, queue: ${stats.queueSize})`);
100679
- }
100680
- }
100681
- })();
100682
- return new Proxy(result, {
100683
- get(target, prop) {
100684
- if (prop === "textStream") return wrappedStream;
100685
- const value = target[prop];
100686
- return typeof value === "function" ? value.bind(target) : value;
100687
- }
100688
- });
100689
- } else if (limiter) {
100690
- limiter.release(null);
100691
- }
100692
100833
  return result;
100693
- } catch (error40) {
100694
- if (limiter) {
100695
- limiter.release(null);
100696
- if (this.debug) {
100697
- console.log(`[DEBUG] Released global AI concurrency slot on error`);
100698
- }
100699
- }
100700
- throw error40;
100701
100834
  } finally {
100702
100835
  if (timeoutState.timeoutId) {
100703
100836
  clearTimeout(timeoutState.timeoutId);