@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/bin/binaries/{probe-v0.6.0-rc292-aarch64-apple-darwin.tar.gz → probe-v0.6.0-rc294-aarch64-apple-darwin.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc292-aarch64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc294-aarch64-unknown-linux-musl.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc292-x86_64-apple-darwin.tar.gz → probe-v0.6.0-rc294-x86_64-apple-darwin.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc292-x86_64-pc-windows-msvc.zip → probe-v0.6.0-rc294-x86_64-pc-windows-msvc.zip} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc292-x86_64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc294-x86_64-unknown-linux-musl.tar.gz} +0 -0
- package/build/agent/ProbeAgent.js +156 -48
- package/build/agent/shared/prompts.js +33 -3
- package/build/tools/fileTracker.js +33 -17
- package/build/tools/vercel.js +13 -10
- package/cjs/agent/ProbeAgent.cjs +211 -78
- package/cjs/index.cjs +218 -85
- package/package.json +1 -1
- package/src/agent/ProbeAgent.js +156 -48
- package/src/agent/shared/prompts.js +33 -3
- package/src/tools/fileTracker.js +33 -17
- package/src/tools/vercel.js +13 -10
package/cjs/agent/ProbeAgent.cjs
CHANGED
|
@@ -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,
|
|
27581
|
+
searchPaths = parseAndResolvePaths(path9, effectiveSearchCwd);
|
|
27581
27582
|
}
|
|
27582
27583
|
if (!searchPaths || searchPaths.length === 0) {
|
|
27583
|
-
searchPaths = [
|
|
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:
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
-
|
|
48704
|
-
this.
|
|
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: ${
|
|
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
|
-
|
|
48851
|
-
this.
|
|
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
|
|
48860
|
-
this._textEditCounts.
|
|
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 ${
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 = "",
|
|
82778
|
-
if (
|
|
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
|
|
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:
|
|
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
|
-
-
|
|
88562
|
-
-
|
|
88563
|
-
-
|
|
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:
|
|
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
|
-
|
|
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);
|