@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.
package/cjs/index.cjs CHANGED
@@ -27260,6 +27260,10 @@ var init_symbolEdit = __esm({
27260
27260
  });
27261
27261
 
27262
27262
  // src/tools/fileTracker.js
27263
+ function normalizePath(filePath) {
27264
+ if (!filePath) return filePath;
27265
+ return (0, import_path6.resolve)(filePath);
27266
+ }
27263
27267
  function computeContentHash(content) {
27264
27268
  const normalized = (content || "").split("\n").map((l) => l.trimEnd()).join("\n");
27265
27269
  return (0, import_crypto2.createHash)("sha256").update(normalized).digest("hex").slice(0, 16);
@@ -27322,10 +27326,11 @@ var init_fileTracker = __esm({
27322
27326
  * @param {string} resolvedPath - Absolute path to the file
27323
27327
  */
27324
27328
  markFileSeen(resolvedPath) {
27325
- this._seenFiles.add(resolvedPath);
27326
- this._textEditCounts.set(resolvedPath, 0);
27329
+ const normalized = normalizePath(resolvedPath);
27330
+ this._seenFiles.add(normalized);
27331
+ this._textEditCounts.set(normalized, 0);
27327
27332
  if (this.debug) {
27328
- console.error(`[FileTracker] Marked as seen: ${resolvedPath}`);
27333
+ console.error(`[FileTracker] Marked as seen: ${normalized}`);
27329
27334
  }
27330
27335
  }
27331
27336
  /**
@@ -27334,7 +27339,7 @@ var init_fileTracker = __esm({
27334
27339
  * @returns {boolean}
27335
27340
  */
27336
27341
  isFileSeen(resolvedPath) {
27337
- return this._seenFiles.has(resolvedPath);
27342
+ return this._seenFiles.has(normalizePath(resolvedPath));
27338
27343
  }
27339
27344
  /**
27340
27345
  * Store a content hash for a symbol in a file.
@@ -27346,7 +27351,7 @@ var init_fileTracker = __esm({
27346
27351
  * @param {string} [source='extract'] - How the content was obtained
27347
27352
  */
27348
27353
  trackSymbolContent(resolvedPath, symbolName, code, startLine, endLine, source = "extract") {
27349
- const key = `${resolvedPath}#${symbolName}`;
27354
+ const key = `${normalizePath(resolvedPath)}#${symbolName}`;
27350
27355
  const contentHash = computeContentHash(code);
27351
27356
  this._contentRecords.set(key, {
27352
27357
  contentHash,
@@ -27367,7 +27372,7 @@ var init_fileTracker = __esm({
27367
27372
  * @returns {Object|null} The stored record or null
27368
27373
  */
27369
27374
  getSymbolRecord(resolvedPath, symbolName) {
27370
- return this._contentRecords.get(`${resolvedPath}#${symbolName}`) || null;
27375
+ return this._contentRecords.get(`${normalizePath(resolvedPath)}#${symbolName}`) || null;
27371
27376
  }
27372
27377
  /**
27373
27378
  * Check if a symbol's current content matches what was stored.
@@ -27377,7 +27382,7 @@ var init_fileTracker = __esm({
27377
27382
  * @returns {{ok: boolean, reason?: string, message?: string}}
27378
27383
  */
27379
27384
  checkSymbolContent(resolvedPath, symbolName, currentCode) {
27380
- const key = `${resolvedPath}#${symbolName}`;
27385
+ const key = `${normalizePath(resolvedPath)}#${symbolName}`;
27381
27386
  const record2 = this._contentRecords.get(key);
27382
27387
  if (!record2) {
27383
27388
  return { ok: true };
@@ -27454,7 +27459,7 @@ var init_fileTracker = __esm({
27454
27459
  * @returns {{ok: boolean, reason?: string, message?: string}}
27455
27460
  */
27456
27461
  checkBeforeEdit(resolvedPath) {
27457
- if (!this._seenFiles.has(resolvedPath)) {
27462
+ if (!this._seenFiles.has(normalizePath(resolvedPath))) {
27458
27463
  return {
27459
27464
  ok: false,
27460
27465
  reason: "untracked",
@@ -27469,8 +27474,9 @@ var init_fileTracker = __esm({
27469
27474
  * @param {string} resolvedPath - Absolute path to the file
27470
27475
  */
27471
27476
  async trackFileAfterWrite(resolvedPath) {
27472
- this._seenFiles.add(resolvedPath);
27473
- this.invalidateFileRecords(resolvedPath);
27477
+ const normalized = normalizePath(resolvedPath);
27478
+ this._seenFiles.add(normalized);
27479
+ this.invalidateFileRecords(normalized);
27474
27480
  }
27475
27481
  /**
27476
27482
  * Record a text-mode edit (old_string/new_string) to a file.
@@ -27478,10 +27484,11 @@ var init_fileTracker = __esm({
27478
27484
  * @param {string} resolvedPath - Absolute path to the file
27479
27485
  */
27480
27486
  recordTextEdit(resolvedPath) {
27481
- const count = (this._textEditCounts.get(resolvedPath) || 0) + 1;
27482
- this._textEditCounts.set(resolvedPath, count);
27487
+ const normalized = normalizePath(resolvedPath);
27488
+ const count = (this._textEditCounts.get(normalized) || 0) + 1;
27489
+ this._textEditCounts.set(normalized, count);
27483
27490
  if (this.debug) {
27484
- console.error(`[FileTracker] Text edit #${count} for ${resolvedPath}`);
27491
+ console.error(`[FileTracker] Text edit #${count} for ${normalized}`);
27485
27492
  }
27486
27493
  }
27487
27494
  /**
@@ -27490,7 +27497,7 @@ var init_fileTracker = __esm({
27490
27497
  * @returns {{ok: boolean, editCount?: number, message?: string}}
27491
27498
  */
27492
27499
  checkTextEditStaleness(resolvedPath) {
27493
- const count = this._textEditCounts.get(resolvedPath) || 0;
27500
+ const count = this._textEditCounts.get(normalizePath(resolvedPath)) || 0;
27494
27501
  if (count >= this.maxConsecutiveTextEdits) {
27495
27502
  return {
27496
27503
  ok: false,
@@ -27519,7 +27526,7 @@ var init_fileTracker = __esm({
27519
27526
  * @param {string} resolvedPath - Absolute path to the file
27520
27527
  */
27521
27528
  invalidateFileRecords(resolvedPath) {
27522
- const prefix = resolvedPath + "#";
27529
+ const prefix = normalizePath(resolvedPath) + "#";
27523
27530
  for (const key of this._contentRecords.keys()) {
27524
27531
  if (key.startsWith(prefix)) {
27525
27532
  this._contentRecords.delete(key);
@@ -27535,7 +27542,7 @@ var init_fileTracker = __esm({
27535
27542
  * @returns {boolean}
27536
27543
  */
27537
27544
  isTracked(resolvedPath) {
27538
- return this.isFileSeen(resolvedPath);
27545
+ return this.isFileSeen(normalizePath(resolvedPath));
27539
27546
  }
27540
27547
  /**
27541
27548
  * Clear all tracking state.
@@ -31588,7 +31595,7 @@ var init_esm3 = __esm({
31588
31595
  });
31589
31596
 
31590
31597
  // node_modules/path-scurry/dist/esm/index.js
31591
- var import_node_path, import_node_url, import_fs4, actualFS, import_promises, realpathSync2, defaultFS, fsFromOption, uncDriveRegexp, uncToDrive, eitherSep, UNKNOWN, IFIFO, IFCHR, IFDIR, IFBLK, IFREG, IFLNK, IFSOCK, IFMT, IFMT_UNKNOWN, READDIR_CALLED, LSTAT_CALLED, ENOTDIR, ENOENT, ENOREADLINK, ENOREALPATH, ENOCHILD, TYPEMASK, entToType, normalizeCache, normalize, normalizeNocaseCache, normalizeNocase, ResolveCache, ChildrenCache, setAsCwd, PathBase, PathWin32, PathPosix, PathScurryBase, PathScurryWin32, PathScurryPosix, PathScurryDarwin, Path, PathScurry;
31598
+ var import_node_path, import_node_url, import_fs4, actualFS, import_promises, realpathSync2, defaultFS, fsFromOption, uncDriveRegexp, uncToDrive, eitherSep, UNKNOWN, IFIFO, IFCHR, IFDIR, IFBLK, IFREG, IFLNK, IFSOCK, IFMT, IFMT_UNKNOWN, READDIR_CALLED, LSTAT_CALLED, ENOTDIR, ENOENT, ENOREADLINK, ENOREALPATH, ENOCHILD, TYPEMASK, entToType, normalizeCache, normalize2, normalizeNocaseCache, normalizeNocase, ResolveCache, ChildrenCache, setAsCwd, PathBase, PathWin32, PathPosix, PathScurryBase, PathScurryWin32, PathScurryPosix, PathScurryDarwin, Path, PathScurry;
31592
31599
  var init_esm4 = __esm({
31593
31600
  "node_modules/path-scurry/dist/esm/index.js"() {
31594
31601
  init_esm2();
@@ -31643,7 +31650,7 @@ var init_esm4 = __esm({
31643
31650
  TYPEMASK = 1023;
31644
31651
  entToType = (s) => s.isFile() ? IFREG : s.isDirectory() ? IFDIR : s.isSymbolicLink() ? IFLNK : s.isCharacterDevice() ? IFCHR : s.isBlockDevice() ? IFBLK : s.isSocket() ? IFSOCK : s.isFIFO() ? IFIFO : UNKNOWN;
31645
31652
  normalizeCache = /* @__PURE__ */ new Map();
31646
- normalize = (s) => {
31653
+ normalize2 = (s) => {
31647
31654
  const c = normalizeCache.get(s);
31648
31655
  if (c)
31649
31656
  return c;
@@ -31656,7 +31663,7 @@ var init_esm4 = __esm({
31656
31663
  const c = normalizeNocaseCache.get(s);
31657
31664
  if (c)
31658
31665
  return c;
31659
- const n = normalize(s.toLowerCase());
31666
+ const n = normalize2(s.toLowerCase());
31660
31667
  normalizeNocaseCache.set(s, n);
31661
31668
  return n;
31662
31669
  };
@@ -31823,7 +31830,7 @@ var init_esm4 = __esm({
31823
31830
  */
31824
31831
  constructor(name15, type = UNKNOWN, root2, roots, nocase, children, opts) {
31825
31832
  this.name = name15;
31826
- this.#matchName = nocase ? normalizeNocase(name15) : normalize(name15);
31833
+ this.#matchName = nocase ? normalizeNocase(name15) : normalize2(name15);
31827
31834
  this.#type = type & TYPEMASK;
31828
31835
  this.nocase = nocase;
31829
31836
  this.roots = roots;
@@ -31916,7 +31923,7 @@ var init_esm4 = __esm({
31916
31923
  return this.parent || this;
31917
31924
  }
31918
31925
  const children = this.children();
31919
- const name15 = this.nocase ? normalizeNocase(pathPart) : normalize(pathPart);
31926
+ const name15 = this.nocase ? normalizeNocase(pathPart) : normalize2(pathPart);
31920
31927
  for (const p of children) {
31921
31928
  if (p.#matchName === name15) {
31922
31929
  return p;
@@ -32161,7 +32168,7 @@ var init_esm4 = __esm({
32161
32168
  * directly.
32162
32169
  */
32163
32170
  isNamed(n) {
32164
- return !this.nocase ? this.#matchName === normalize(n) : this.#matchName === normalizeNocase(n);
32171
+ return !this.nocase ? this.#matchName === normalize2(n) : this.#matchName === normalizeNocase(n);
32165
32172
  }
32166
32173
  /**
32167
32174
  * Return the Path object corresponding to the target of a symbolic link.
@@ -32300,7 +32307,7 @@ var init_esm4 = __esm({
32300
32307
  #readdirMaybePromoteChild(e, c) {
32301
32308
  for (let p = c.provisional; p < c.length; p++) {
32302
32309
  const pchild = c[p];
32303
- const name15 = this.nocase ? normalizeNocase(e.name) : normalize(e.name);
32310
+ const name15 = this.nocase ? normalizeNocase(e.name) : normalize2(e.name);
32304
32311
  if (name15 !== pchild.#matchName) {
32305
32312
  continue;
32306
32313
  }
@@ -53617,7 +53624,7 @@ var init_graph_builder = __esm({
53617
53624
  applyLinkStyles() {
53618
53625
  if (!this.pendingLinkStyles.length || !this.edges.length)
53619
53626
  return;
53620
- const normalize3 = (s) => {
53627
+ const normalize4 = (s) => {
53621
53628
  const out = {};
53622
53629
  for (const [kRaw, vRaw] of Object.entries(s)) {
53623
53630
  const k = kRaw.trim().toLowerCase();
@@ -53638,7 +53645,7 @@ var init_graph_builder = __esm({
53638
53645
  return out;
53639
53646
  };
53640
53647
  for (const cmd of this.pendingLinkStyles) {
53641
- const style = normalize3(cmd.props);
53648
+ const style = normalize4(cmd.props);
53642
53649
  for (const idx of cmd.indices) {
53643
53650
  if (idx >= 0 && idx < this.edges.length) {
53644
53651
  const e = this.edges[idx];
@@ -60905,7 +60912,7 @@ var require_layout = __commonJS({
60905
60912
  "use strict";
60906
60913
  var _ = require_lodash2();
60907
60914
  var acyclic = require_acyclic();
60908
- var normalize3 = require_normalize();
60915
+ var normalize4 = require_normalize();
60909
60916
  var rank = require_rank();
60910
60917
  var normalizeRanks = require_util().normalizeRanks;
60911
60918
  var parentDummyChains = require_parent_dummy_chains();
@@ -60967,7 +60974,7 @@ var require_layout = __commonJS({
60967
60974
  removeEdgeLabelProxies(g);
60968
60975
  });
60969
60976
  time3(" normalize.run", function() {
60970
- normalize3.run(g);
60977
+ normalize4.run(g);
60971
60978
  });
60972
60979
  time3(" parentDummyChains", function() {
60973
60980
  parentDummyChains(g);
@@ -60994,7 +61001,7 @@ var require_layout = __commonJS({
60994
61001
  removeBorderNodes(g);
60995
61002
  });
60996
61003
  time3(" normalize.undo", function() {
60997
- normalize3.undo(g);
61004
+ normalize4.undo(g);
60998
61005
  });
60999
61006
  time3(" fixupEdgeLabelCoords", function() {
61000
61007
  fixupEdgeLabelCoords(g);
@@ -67365,8 +67372,8 @@ var require_resolve = __commonJS({
67365
67372
  }
67366
67373
  return count;
67367
67374
  }
67368
- function getFullPath(resolver, id = "", normalize3) {
67369
- if (normalize3 !== false)
67375
+ function getFullPath(resolver, id = "", normalize4) {
67376
+ if (normalize4 !== false)
67370
67377
  id = normalizeId(id);
67371
67378
  const p = resolver.parse(id);
67372
67379
  return _getFullPath(resolver, p);
@@ -68706,7 +68713,7 @@ var require_fast_uri = __commonJS({
68706
68713
  "use strict";
68707
68714
  var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils();
68708
68715
  var { SCHEMES, getSchemeHandler } = require_schemes();
68709
- function normalize3(uri, options) {
68716
+ function normalize4(uri, options) {
68710
68717
  if (typeof uri === "string") {
68711
68718
  uri = /** @type {T} */
68712
68719
  serialize(parse11(uri, options), options);
@@ -68942,7 +68949,7 @@ var require_fast_uri = __commonJS({
68942
68949
  }
68943
68950
  var fastUri = {
68944
68951
  SCHEMES,
68945
- normalize: normalize3,
68952
+ normalize: normalize4,
68946
68953
  resolve: resolve9,
68947
68954
  resolveComponent,
68948
68955
  equal,
@@ -73149,9 +73156,9 @@ If the solution is clear, you can jump to implementation right away. If not, ask
73149
73156
  - Do not add code comments unless the logic is genuinely complex and non-obvious.
73150
73157
 
73151
73158
  # Before Implementation
73152
- - Focus on high-level design patterns and system organization
73153
- - Identify architectural patterns and component relationships
73154
- - Evaluate system structure and suggest architectural improvements
73159
+ - 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.
73160
+ - Read neighboring files \u2014 understand naming conventions, error handling patterns, import style, and existing utilities before creating new ones.
73161
+ - Trace the call chain \u2014 follow how the code you're changing is called and what depends on it. Check interfaces, types, and consumers.
73155
73162
  - Focus on backward compatibility
73156
73163
  - Consider scalability, maintainability, and extensibility in your analysis
73157
73164
 
@@ -73176,6 +73183,20 @@ Before building or testing, determine the project's toolchain:
73176
73183
  - Read README for build/test instructions if the above are unclear
73177
73184
  - Common patterns: \`make build\`/\`make test\`, \`npm run build\`/\`npm test\`, \`cargo build\`/\`cargo test\`, \`go build ./...\`/\`go test ./...\`, \`python -m pytest\`
73178
73185
 
73186
+ # File Editing Rules
73187
+ 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.
73188
+
73189
+ 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.
73190
+
73191
+ Use the right tool:
73192
+ 1. To MODIFY existing code \u2192 \`edit\` tool (old_string \u2192 new_string, or start_line/end_line)
73193
+ 2. To CREATE a new file \u2192 \`create\` tool
73194
+ 3. To CHANGE multiple files at once \u2192 \`multi_edit\` tool
73195
+ 4. To READ code \u2192 \`extract\` or \`search\` tools
73196
+ 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.
73197
+
73198
+ 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.
73199
+
73179
73200
  # During Implementation
73180
73201
  - Always create a new branch before making changes to the codebase.
73181
73202
  - Fix problems at the root cause, not with surface-level patches. Prefer general solutions over special cases.
@@ -73202,6 +73223,22 @@ Before committing or creating a PR, run through this checklist:
73202
73223
 
73203
73224
  Do NOT skip verification. Do NOT proceed to PR creation with a broken build or failing tests.
73204
73225
 
73226
+ # Output Integrity
73227
+ Your final output MUST accurately reflect what ACTUALLY happened. Do NOT fabricate, hallucinate, or report aspirational results.
73228
+
73229
+ - 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.
73230
+ - 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.
73231
+ - Only list files you actually modified AND committed.
73232
+ - If you could not complete the task \u2014 ran out of iterations, tests failed, build broken, push rejected \u2014 report the real reason honestly.
73233
+
73234
+ NEVER claim success when:
73235
+ - You did not run \`git push\` successfully
73236
+ - Tests failed and you did not fix them
73237
+ - You hit the iteration limit before completing the work
73238
+ - You only analyzed/investigated but did not implement changes
73239
+
73240
+ A false success report is WORSE than an honest failure \u2014 it misleads the user into thinking work is done when it is not.
73241
+
73205
73242
  # GitHub Integration
73206
73243
  - Use the \`gh\` CLI for all GitHub operations: issues, pull requests, checks, releases.
73207
73244
  - To view issues or PRs: \`gh issue view <number>\`, \`gh pr view <number>\`.
@@ -97494,9 +97531,13 @@ var init_ProbeAgent = __esm({
97494
97531
  }
97495
97532
  return await this.fallbackManager.executeWithFallback(
97496
97533
  async (provider, model, config2) => {
97534
+ let fallbackModel = provider(model);
97535
+ if (this.concurrencyLimiter) {
97536
+ fallbackModel = _ProbeAgent._wrapModelWithLimiter(fallbackModel, this.concurrencyLimiter, this.debug);
97537
+ }
97497
97538
  const fallbackOptions = {
97498
97539
  ...options,
97499
- model: provider(model),
97540
+ model: fallbackModel,
97500
97541
  abortSignal: controller.signal
97501
97542
  };
97502
97543
  if (config2.provider !== "google" && fallbackOptions.tools) {
@@ -97524,6 +97565,132 @@ var init_ProbeAgent = __esm({
97524
97565
  }
97525
97566
  );
97526
97567
  }
97568
+ /**
97569
+ * Wrap a LanguageModelV1 model so each doStream/doGenerate call acquires and
97570
+ * releases a concurrency limiter slot. This gates individual LLM API calls
97571
+ * (seconds each) instead of entire multi-step agent sessions (minutes).
97572
+ *
97573
+ * @param {Object} model - LanguageModelV1 model instance
97574
+ * @param {Object} limiter - Concurrency limiter with acquire/release/getStats
97575
+ * @param {boolean} debug - Enable debug logging
97576
+ * @returns {Object} Wrapped model with per-call concurrency gating
97577
+ * @private
97578
+ */
97579
+ static _wrapModelWithLimiter(model, limiter, debug) {
97580
+ return new Proxy(model, {
97581
+ get(target, prop) {
97582
+ if (prop === "doStream") {
97583
+ return async function(...args) {
97584
+ await limiter.acquire(null);
97585
+ if (debug) {
97586
+ const stats = limiter.getStats();
97587
+ console.log(`[DEBUG] Acquired AI slot for LLM call (${stats.globalActive}/${stats.maxConcurrent}, queue: ${stats.queueSize})`);
97588
+ }
97589
+ try {
97590
+ const result = await target.doStream(...args);
97591
+ const originalStream = result.stream;
97592
+ const originalReader = originalStream.getReader();
97593
+ let released = false;
97594
+ const releaseOnce = () => {
97595
+ if (released) return;
97596
+ released = true;
97597
+ limiter.release(null);
97598
+ };
97599
+ const wrappedStream = new ReadableStream({
97600
+ async pull(controller) {
97601
+ try {
97602
+ const { done, value: value2 } = await originalReader.read();
97603
+ if (done) {
97604
+ controller.close();
97605
+ releaseOnce();
97606
+ if (debug) {
97607
+ const stats = limiter.getStats();
97608
+ console.log(`[DEBUG] Released AI slot after LLM stream complete (${stats.globalActive}/${stats.maxConcurrent})`);
97609
+ }
97610
+ } else {
97611
+ controller.enqueue(value2);
97612
+ }
97613
+ } catch (err) {
97614
+ releaseOnce();
97615
+ if (debug) {
97616
+ console.log(`[DEBUG] Released AI slot on LLM stream error`);
97617
+ }
97618
+ controller.error(err);
97619
+ }
97620
+ },
97621
+ cancel() {
97622
+ releaseOnce();
97623
+ if (debug) {
97624
+ console.log(`[DEBUG] Released AI slot on LLM stream cancel`);
97625
+ }
97626
+ originalReader.cancel();
97627
+ }
97628
+ });
97629
+ return { ...result, stream: wrappedStream };
97630
+ } catch (err) {
97631
+ limiter.release(null);
97632
+ if (debug) {
97633
+ console.log(`[DEBUG] Released AI slot on doStream error`);
97634
+ }
97635
+ throw err;
97636
+ }
97637
+ };
97638
+ }
97639
+ if (prop === "doGenerate") {
97640
+ return async function(...args) {
97641
+ await limiter.acquire(null);
97642
+ if (debug) {
97643
+ const stats = limiter.getStats();
97644
+ console.log(`[DEBUG] Acquired AI slot for LLM generate (${stats.globalActive}/${stats.maxConcurrent})`);
97645
+ }
97646
+ try {
97647
+ const result = await target.doGenerate(...args);
97648
+ return result;
97649
+ } finally {
97650
+ limiter.release(null);
97651
+ if (debug) {
97652
+ const stats = limiter.getStats();
97653
+ console.log(`[DEBUG] Released AI slot after LLM generate (${stats.globalActive}/${stats.maxConcurrent})`);
97654
+ }
97655
+ }
97656
+ };
97657
+ }
97658
+ const value = target[prop];
97659
+ return typeof value === "function" ? value.bind(target) : value;
97660
+ }
97661
+ });
97662
+ }
97663
+ /**
97664
+ * Wrap an engine stream result so its textStream async generator acquires
97665
+ * and releases a concurrency limiter slot. Acquire happens when iteration
97666
+ * begins; release happens in finally (completion, error, or break).
97667
+ *
97668
+ * @param {Object} result - Engine result with { textStream, usage, ... }
97669
+ * @param {Object} limiter - Concurrency limiter with acquire/release/getStats
97670
+ * @param {boolean} debug - Enable debug logging
97671
+ * @returns {Object} Result with wrapped textStream
97672
+ * @private
97673
+ */
97674
+ static _wrapEngineStreamWithLimiter(result, limiter, debug) {
97675
+ const originalStream = result.textStream;
97676
+ async function* gatedStream() {
97677
+ await limiter.acquire(null);
97678
+ if (debug) {
97679
+ const stats = limiter.getStats();
97680
+ console.log(`[DEBUG] Acquired AI slot for engine stream (${stats.globalActive}/${stats.maxConcurrent}, queue: ${stats.queueSize})`);
97681
+ }
97682
+ try {
97683
+ yield* originalStream;
97684
+ } finally {
97685
+ limiter.release(null);
97686
+ if (debug) {
97687
+ const stats = limiter.getStats();
97688
+ console.log(`[DEBUG] Released AI slot after engine stream (${stats.globalActive}/${stats.maxConcurrent})`);
97689
+ }
97690
+ }
97691
+ }
97692
+ return { ...result, textStream: gatedStream() };
97693
+ }
97527
97694
  /**
97528
97695
  * Execute streamText with retry and fallback support
97529
97696
  * @param {Object} options - streamText options
@@ -97532,12 +97699,8 @@ var init_ProbeAgent = __esm({
97532
97699
  */
97533
97700
  async streamTextWithRetryAndFallback(options) {
97534
97701
  const limiter = this.concurrencyLimiter;
97535
- if (limiter) {
97536
- await limiter.acquire(null);
97537
- if (this.debug) {
97538
- const stats = limiter.getStats();
97539
- console.log(`[DEBUG] Acquired global AI concurrency slot (${stats.globalActive}/${stats.maxConcurrent}, queue: ${stats.queueSize})`);
97540
- }
97702
+ if (limiter && options.model) {
97703
+ options = { ...options, model: _ProbeAgent._wrapModelWithLimiter(options.model, limiter, this.debug) };
97541
97704
  }
97542
97705
  const controller = new AbortController();
97543
97706
  const timeoutState = { timeoutId: null };
@@ -97565,6 +97728,9 @@ var init_ProbeAgent = __esm({
97565
97728
  if (useClaudeCode || useCodex) {
97566
97729
  try {
97567
97730
  result = await this._tryEngineStreamPath(options, controller, timeoutState);
97731
+ if (result && limiter) {
97732
+ result = _ProbeAgent._wrapEngineStreamWithLimiter(result, limiter, this.debug);
97733
+ }
97568
97734
  } catch (error40) {
97569
97735
  if (this.debug) {
97570
97736
  const engineType = useClaudeCode ? "Claude Code" : "Codex";
@@ -97575,41 +97741,7 @@ var init_ProbeAgent = __esm({
97575
97741
  if (!result) {
97576
97742
  result = await this._executeWithVercelProvider(options, controller);
97577
97743
  }
97578
- if (limiter && result.textStream) {
97579
- const originalStream = result.textStream;
97580
- const debug = this.debug;
97581
- const wrappedStream = (async function* () {
97582
- try {
97583
- for await (const chunk of originalStream) {
97584
- yield chunk;
97585
- }
97586
- } finally {
97587
- limiter.release(null);
97588
- if (debug) {
97589
- const stats = limiter.getStats();
97590
- console.log(`[DEBUG] Released global AI concurrency slot (${stats.globalActive}/${stats.maxConcurrent}, queue: ${stats.queueSize})`);
97591
- }
97592
- }
97593
- })();
97594
- return new Proxy(result, {
97595
- get(target, prop) {
97596
- if (prop === "textStream") return wrappedStream;
97597
- const value = target[prop];
97598
- return typeof value === "function" ? value.bind(target) : value;
97599
- }
97600
- });
97601
- } else if (limiter) {
97602
- limiter.release(null);
97603
- }
97604
97744
  return result;
97605
- } catch (error40) {
97606
- if (limiter) {
97607
- limiter.release(null);
97608
- if (this.debug) {
97609
- console.log(`[DEBUG] Released global AI concurrency slot on error`);
97610
- }
97611
- }
97612
- throw error40;
97613
97745
  } finally {
97614
97746
  if (timeoutState.timeoutId) {
97615
97747
  clearTimeout(timeoutState.timeoutId);
@@ -101404,7 +101536,7 @@ var init_vercel = __esm({
101404
101536
  name: "search",
101405
101537
  description: searchDelegate ? searchDelegateDescription : searchDescription,
101406
101538
  inputSchema: searchSchema,
101407
- execute: async ({ query: searchQuery, path: path9, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage }) => {
101539
+ execute: async ({ query: searchQuery, path: path9, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage, workingDirectory }) => {
101408
101540
  if (!exact && searchQuery) {
101409
101541
  const originalQuery = searchQuery;
101410
101542
  searchQuery = autoQuoteSearchTerms(searchQuery);
@@ -101413,18 +101545,19 @@ var init_vercel = __esm({
101413
101545
  }
101414
101546
  }
101415
101547
  const effectiveMaxTokens = paramMaxTokens || maxTokens;
101548
+ const effectiveSearchCwd = workingDirectory || options.cwd || ".";
101416
101549
  let searchPaths;
101417
101550
  if (path9) {
101418
- searchPaths = parseAndResolvePaths(path9, options.cwd);
101551
+ searchPaths = parseAndResolvePaths(path9, effectiveSearchCwd);
101419
101552
  }
101420
101553
  if (!searchPaths || searchPaths.length === 0) {
101421
- searchPaths = [options.cwd || "."];
101554
+ searchPaths = [effectiveSearchCwd];
101422
101555
  }
101423
101556
  const searchPath = searchPaths.join(" ");
101424
101557
  const searchOptions = {
101425
101558
  query: searchQuery,
101426
101559
  path: searchPath,
101427
- cwd: options.cwd,
101560
+ cwd: effectiveSearchCwd,
101428
101561
  // Working directory for resolving relative paths
101429
101562
  allowTests: allow_tests ?? true,
101430
101563
  exact,
@@ -101475,7 +101608,7 @@ var init_vercel = __esm({
101475
101608
  try {
101476
101609
  const result = maybeAnnotate(await runRawSearch());
101477
101610
  if (options.fileTracker && typeof result === "string") {
101478
- options.fileTracker.trackFilesFromOutput(result, options.cwd || ".").catch(() => {
101611
+ options.fileTracker.trackFilesFromOutput(result, effectiveSearchCwd).catch(() => {
101479
101612
  });
101480
101613
  }
101481
101614
  return result;
@@ -101528,7 +101661,7 @@ var init_vercel = __esm({
101528
101661
  }
101529
101662
  const fallbackResult = maybeAnnotate(await runRawSearch());
101530
101663
  if (options.fileTracker && typeof fallbackResult === "string") {
101531
- options.fileTracker.trackFilesFromOutput(fallbackResult, options.cwd || ".").catch(() => {
101664
+ options.fileTracker.trackFilesFromOutput(fallbackResult, effectiveSearchCwd).catch(() => {
101532
101665
  });
101533
101666
  }
101534
101667
  return fallbackResult;
@@ -101591,7 +101724,7 @@ var init_vercel = __esm({
101591
101724
  try {
101592
101725
  const fallbackResult2 = maybeAnnotate(await runRawSearch());
101593
101726
  if (options.fileTracker && typeof fallbackResult2 === "string") {
101594
- options.fileTracker.trackFilesFromOutput(fallbackResult2, options.cwd || ".").catch(() => {
101727
+ options.fileTracker.trackFilesFromOutput(fallbackResult2, effectiveSearchCwd).catch(() => {
101595
101728
  });
101596
101729
  }
101597
101730
  return fallbackResult2;
@@ -101645,9 +101778,9 @@ var init_vercel = __esm({
101645
101778
  name: "extract",
101646
101779
  description: extractDescription,
101647
101780
  inputSchema: extractSchema,
101648
- execute: async ({ targets, input_content, line, end_line, allow_tests, context_lines, format }) => {
101781
+ execute: async ({ targets, input_content, line, end_line, allow_tests, context_lines, format, workingDirectory }) => {
101649
101782
  try {
101650
- const effectiveCwd = options.cwd || ".";
101783
+ const effectiveCwd = workingDirectory || options.cwd || ".";
101651
101784
  if (debug) {
101652
101785
  if (targets) {
101653
101786
  console.error(`Executing extract with targets: "${targets}", cwd: "${effectiveCwd}", context lines: ${context_lines || 10}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@probelabs/probe",
3
- "version": "0.6.0-rc292",
3
+ "version": "0.6.0-rc294",
4
4
  "description": "Node.js wrapper for the probe code search tool",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",