@probelabs/probe 0.6.0-rc278 → 0.6.0-rc280
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-rc280-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc280-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc280-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc280-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc280-x86_64-unknown-linux-musl.tar.gz +0 -0
- package/build/agent/ProbeAgent.js +114 -28
- package/build/agent/dsl/environment.js +1 -0
- package/build/agent/index.js +248 -87
- package/build/delegate.js +62 -23
- package/build/downloader.js +28 -25
- package/build/tools/analyzeAll.js +10 -10
- package/build/tools/common.js +4 -3
- package/build/tools/vercel.js +72 -10
- package/cjs/agent/ProbeAgent.cjs +251 -88
- package/cjs/index.cjs +248 -87
- package/package.json +1 -1
- package/src/agent/ProbeAgent.js +114 -28
- package/src/agent/dsl/environment.js +1 -0
- package/src/delegate.js +62 -23
- package/src/downloader.js +28 -25
- package/src/tools/analyzeAll.js +10 -10
- package/src/tools/common.js +4 -3
- package/src/tools/vercel.js +72 -10
- package/bin/binaries/probe-v0.6.0-rc278-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc278-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc278-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc278-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc278-x86_64-unknown-linux-musl.tar.gz +0 -0
package/build/agent/index.js
CHANGED
|
@@ -2293,9 +2293,7 @@ async function acquireFileLock(lockPath, version2) {
|
|
|
2293
2293
|
};
|
|
2294
2294
|
try {
|
|
2295
2295
|
await fs3.writeFile(lockPath, JSON.stringify(lockData), { flag: "wx" });
|
|
2296
|
-
|
|
2297
|
-
console.log(`Acquired file lock: ${lockPath}`);
|
|
2298
|
-
}
|
|
2296
|
+
console.log(`Acquired file lock: ${lockPath}`);
|
|
2299
2297
|
return true;
|
|
2300
2298
|
} catch (error) {
|
|
2301
2299
|
if (error.code === "EEXIST") {
|
|
@@ -2303,15 +2301,11 @@ async function acquireFileLock(lockPath, version2) {
|
|
|
2303
2301
|
const existingLock = JSON.parse(await fs3.readFile(lockPath, "utf-8"));
|
|
2304
2302
|
const lockAge = Date.now() - existingLock.timestamp;
|
|
2305
2303
|
if (lockAge > LOCK_TIMEOUT_MS) {
|
|
2306
|
-
|
|
2307
|
-
console.log(`Removing stale lock file (age: ${Math.round(lockAge / 1e3)}s, pid: ${existingLock.pid})`);
|
|
2308
|
-
}
|
|
2304
|
+
console.log(`Removing stale lock file (age: ${Math.round(lockAge / 1e3)}s, pid: ${existingLock.pid})`);
|
|
2309
2305
|
await fs3.remove(lockPath);
|
|
2310
2306
|
return false;
|
|
2311
2307
|
}
|
|
2312
|
-
|
|
2313
|
-
console.log(`Download in progress by process ${existingLock.pid}, waiting...`);
|
|
2314
|
-
}
|
|
2308
|
+
console.log(`Download in progress by process ${existingLock.pid}, waiting...`);
|
|
2315
2309
|
return false;
|
|
2316
2310
|
} catch (readError) {
|
|
2317
2311
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
@@ -2352,36 +2346,36 @@ async function releaseFileLock(lockPath) {
|
|
|
2352
2346
|
}
|
|
2353
2347
|
async function waitForFileLock(lockPath, binaryPath) {
|
|
2354
2348
|
const startTime = Date.now();
|
|
2349
|
+
let lastStatusTime = startTime;
|
|
2350
|
+
console.log(`Waiting for file lock to clear: ${lockPath}`);
|
|
2355
2351
|
while (Date.now() - startTime < MAX_LOCK_WAIT_MS) {
|
|
2356
2352
|
if (await fs3.pathExists(binaryPath)) {
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
}
|
|
2353
|
+
const waitedSeconds = Math.round((Date.now() - startTime) / 1e3);
|
|
2354
|
+
console.log(`Binary now available at ${binaryPath}, download completed by another process (waited ${waitedSeconds}s)`);
|
|
2360
2355
|
return true;
|
|
2361
2356
|
}
|
|
2362
2357
|
const lockExists = await fs3.pathExists(lockPath);
|
|
2363
2358
|
if (!lockExists) {
|
|
2364
|
-
|
|
2365
|
-
console.log(`Lock file removed but binary not found - download may have failed`);
|
|
2366
|
-
}
|
|
2359
|
+
console.log(`Lock file removed but binary not found - download may have failed`);
|
|
2367
2360
|
return false;
|
|
2368
2361
|
}
|
|
2369
2362
|
try {
|
|
2370
2363
|
const lockData = JSON.parse(await fs3.readFile(lockPath, "utf-8"));
|
|
2371
2364
|
const lockAge = Date.now() - lockData.timestamp;
|
|
2372
2365
|
if (lockAge > LOCK_TIMEOUT_MS) {
|
|
2373
|
-
|
|
2374
|
-
console.log(`Lock expired (age: ${Math.round(lockAge / 1e3)}s), will retry download`);
|
|
2375
|
-
}
|
|
2366
|
+
console.log(`Lock expired (age: ${Math.round(lockAge / 1e3)}s), will retry download`);
|
|
2376
2367
|
return false;
|
|
2377
2368
|
}
|
|
2378
2369
|
} catch {
|
|
2379
2370
|
}
|
|
2371
|
+
if (Date.now() - lastStatusTime >= 15e3) {
|
|
2372
|
+
const elapsedSeconds = Math.round((Date.now() - startTime) / 1e3);
|
|
2373
|
+
console.log(`Still waiting for file lock (${elapsedSeconds}s/${MAX_LOCK_WAIT_MS / 1e3}s max)`);
|
|
2374
|
+
lastStatusTime = Date.now();
|
|
2375
|
+
}
|
|
2380
2376
|
await new Promise((resolve9) => setTimeout(resolve9, LOCK_POLL_INTERVAL_MS));
|
|
2381
2377
|
}
|
|
2382
|
-
|
|
2383
|
-
console.log(`Timeout waiting for file lock`);
|
|
2384
|
-
}
|
|
2378
|
+
console.log(`Timeout waiting for file lock after ${MAX_LOCK_WAIT_MS / 1e3}s`);
|
|
2385
2379
|
return false;
|
|
2386
2380
|
}
|
|
2387
2381
|
async function withDownloadLock(version2, downloadFn) {
|
|
@@ -2395,9 +2389,7 @@ async function withDownloadLock(version2, downloadFn) {
|
|
|
2395
2389
|
}
|
|
2396
2390
|
downloadLocks.delete(lockKey);
|
|
2397
2391
|
} else {
|
|
2398
|
-
|
|
2399
|
-
console.log(`Download already in progress in this process for version ${lockKey}, waiting...`);
|
|
2400
|
-
}
|
|
2392
|
+
console.log(`Download already in progress in this process for version ${lockKey}, waiting...`);
|
|
2401
2393
|
try {
|
|
2402
2394
|
return await lock.promise;
|
|
2403
2395
|
} catch (error) {
|
|
@@ -2407,10 +2399,16 @@ async function withDownloadLock(version2, downloadFn) {
|
|
|
2407
2399
|
}
|
|
2408
2400
|
}
|
|
2409
2401
|
}
|
|
2402
|
+
let timeoutId = null;
|
|
2410
2403
|
const downloadPromise = Promise.race([
|
|
2411
2404
|
downloadFn(),
|
|
2412
2405
|
new Promise(
|
|
2413
|
-
(_, reject2) =>
|
|
2406
|
+
(_, reject2) => {
|
|
2407
|
+
timeoutId = setTimeout(() => reject2(new Error(`Download timeout after ${LOCK_TIMEOUT_MS / 1e3}s`)), LOCK_TIMEOUT_MS);
|
|
2408
|
+
if (timeoutId.unref) {
|
|
2409
|
+
timeoutId.unref();
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2414
2412
|
)
|
|
2415
2413
|
]);
|
|
2416
2414
|
downloadLocks.set(lockKey, {
|
|
@@ -2421,6 +2419,9 @@ async function withDownloadLock(version2, downloadFn) {
|
|
|
2421
2419
|
const result = await downloadPromise;
|
|
2422
2420
|
return result;
|
|
2423
2421
|
} finally {
|
|
2422
|
+
if (timeoutId) {
|
|
2423
|
+
clearTimeout(timeoutId);
|
|
2424
|
+
}
|
|
2424
2425
|
downloadLocks.delete(lockKey);
|
|
2425
2426
|
}
|
|
2426
2427
|
}
|
|
@@ -3862,12 +3863,17 @@ async function delegate({
|
|
|
3862
3863
|
mcpConfigPath = null,
|
|
3863
3864
|
delegationManager = null,
|
|
3864
3865
|
// Optional per-instance manager, falls back to default singleton
|
|
3865
|
-
concurrencyLimiter = null
|
|
3866
|
+
concurrencyLimiter = null,
|
|
3866
3867
|
// Optional global AI concurrency limiter
|
|
3868
|
+
parentAbortSignal = null
|
|
3869
|
+
// Optional AbortSignal from parent to cancel this delegation
|
|
3867
3870
|
}) {
|
|
3868
3871
|
if (!task || typeof task !== "string") {
|
|
3869
3872
|
throw new Error("Task parameter is required and must be a string");
|
|
3870
3873
|
}
|
|
3874
|
+
if (parentAbortSignal?.aborted) {
|
|
3875
|
+
throw new Error("Delegation cancelled: parent operation was aborted");
|
|
3876
|
+
}
|
|
3871
3877
|
const hasExplicitTimeout = Object.prototype.hasOwnProperty.call(arguments?.[0] ?? {}, "timeout");
|
|
3872
3878
|
if (!hasExplicitTimeout) {
|
|
3873
3879
|
const envTimeoutMs = parseInt(process.env.DELEGATION_TIMEOUT_MS || "", 10);
|
|
@@ -3952,12 +3958,37 @@ async function delegate({
|
|
|
3952
3958
|
}
|
|
3953
3959
|
const timeoutPromise = new Promise((_, reject2) => {
|
|
3954
3960
|
timeoutId = setTimeout(() => {
|
|
3961
|
+
subagent.cancel();
|
|
3955
3962
|
reject2(new Error(`Delegation timed out after ${timeout} seconds`));
|
|
3956
3963
|
}, timeout * 1e3);
|
|
3957
3964
|
});
|
|
3965
|
+
let parentAbortHandler;
|
|
3966
|
+
const parentAbortPromise = new Promise((_, reject2) => {
|
|
3967
|
+
if (parentAbortSignal) {
|
|
3968
|
+
if (parentAbortSignal.aborted) {
|
|
3969
|
+
subagent.cancel();
|
|
3970
|
+
reject2(new Error("Delegation cancelled: parent operation was aborted"));
|
|
3971
|
+
return;
|
|
3972
|
+
}
|
|
3973
|
+
parentAbortHandler = () => {
|
|
3974
|
+
subagent.cancel();
|
|
3975
|
+
reject2(new Error("Delegation cancelled: parent operation was aborted"));
|
|
3976
|
+
};
|
|
3977
|
+
parentAbortSignal.addEventListener("abort", parentAbortHandler, { once: true });
|
|
3978
|
+
}
|
|
3979
|
+
});
|
|
3958
3980
|
const answerOptions = schema ? { schema } : void 0;
|
|
3959
3981
|
const answerPromise = answerOptions ? subagent.answer(task, [], answerOptions) : subagent.answer(task);
|
|
3960
|
-
const
|
|
3982
|
+
const racers = [answerPromise, timeoutPromise];
|
|
3983
|
+
if (parentAbortSignal) racers.push(parentAbortPromise);
|
|
3984
|
+
let response;
|
|
3985
|
+
try {
|
|
3986
|
+
response = await Promise.race(racers);
|
|
3987
|
+
} finally {
|
|
3988
|
+
if (parentAbortHandler && parentAbortSignal) {
|
|
3989
|
+
parentAbortSignal.removeEventListener("abort", parentAbortHandler);
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3961
3992
|
if (timeoutId !== null) {
|
|
3962
3993
|
clearTimeout(timeoutId);
|
|
3963
3994
|
timeoutId = null;
|
|
@@ -4112,10 +4143,9 @@ var init_delegate = __esm({
|
|
|
4112
4143
|
if (this.tryAcquire(parentSessionId)) {
|
|
4113
4144
|
return true;
|
|
4114
4145
|
}
|
|
4115
|
-
|
|
4116
|
-
console.error(`[DelegationManager] Slot unavailable (${this.globalActive}/${this.maxConcurrent}), queuing... (queue size: ${this.waitQueue.length}, timeout: ${effectiveTimeout}ms)`);
|
|
4117
|
-
}
|
|
4146
|
+
console.error(`[DelegationManager] Slot unavailable (${this.globalActive}/${this.maxConcurrent}), queuing... (queue size: ${this.waitQueue.length + 1}, timeout: ${effectiveTimeout}ms)`);
|
|
4118
4147
|
return new Promise((resolve9, reject2) => {
|
|
4148
|
+
const queuedAt = Date.now();
|
|
4119
4149
|
const entry = {
|
|
4120
4150
|
resolve: null,
|
|
4121
4151
|
// Will be wrapped below
|
|
@@ -4123,20 +4153,23 @@ var init_delegate = __esm({
|
|
|
4123
4153
|
// Will be wrapped below
|
|
4124
4154
|
parentSessionId,
|
|
4125
4155
|
debug,
|
|
4126
|
-
queuedAt
|
|
4127
|
-
timeoutId: null
|
|
4156
|
+
queuedAt,
|
|
4157
|
+
timeoutId: null,
|
|
4158
|
+
reminderId: null
|
|
4128
4159
|
};
|
|
4129
4160
|
let settled = false;
|
|
4130
4161
|
entry.resolve = (value) => {
|
|
4131
4162
|
if (settled) return;
|
|
4132
4163
|
settled = true;
|
|
4133
4164
|
if (entry.timeoutId) clearTimeout(entry.timeoutId);
|
|
4165
|
+
if (entry.reminderId) clearInterval(entry.reminderId);
|
|
4134
4166
|
resolve9(value);
|
|
4135
4167
|
};
|
|
4136
4168
|
entry.reject = (error) => {
|
|
4137
4169
|
if (settled) return;
|
|
4138
4170
|
settled = true;
|
|
4139
4171
|
if (entry.timeoutId) clearTimeout(entry.timeoutId);
|
|
4172
|
+
if (entry.reminderId) clearInterval(entry.reminderId);
|
|
4140
4173
|
reject2(error);
|
|
4141
4174
|
};
|
|
4142
4175
|
if (effectiveTimeout > 0) {
|
|
@@ -4148,6 +4181,13 @@ var init_delegate = __esm({
|
|
|
4148
4181
|
entry.reject(new Error(`Delegation queue timeout: waited ${effectiveTimeout}ms for an available slot`));
|
|
4149
4182
|
}, effectiveTimeout);
|
|
4150
4183
|
}
|
|
4184
|
+
entry.reminderId = setInterval(() => {
|
|
4185
|
+
const waitedSeconds = Math.round((Date.now() - queuedAt) / 1e3);
|
|
4186
|
+
console.error(`[DelegationManager] Still waiting for slot (${waitedSeconds}s). ${this.globalActive}/${this.maxConcurrent} active, ${this.waitQueue.length} queued.`);
|
|
4187
|
+
}, 15e3);
|
|
4188
|
+
if (entry.reminderId.unref) {
|
|
4189
|
+
entry.reminderId.unref();
|
|
4190
|
+
}
|
|
4151
4191
|
this.waitQueue.push(entry);
|
|
4152
4192
|
});
|
|
4153
4193
|
}
|
|
@@ -4186,18 +4226,14 @@ var init_delegate = __esm({
|
|
|
4186
4226
|
const sessionData = this.sessionDelegations.get(parentSessionId);
|
|
4187
4227
|
const sessionCount = sessionData?.count || 0;
|
|
4188
4228
|
if (sessionCount >= this.maxPerSession) {
|
|
4189
|
-
|
|
4190
|
-
console.error(`[DelegationManager] Session limit (${this.maxPerSession}) reached for queued item, rejecting`);
|
|
4191
|
-
}
|
|
4229
|
+
console.error(`[DelegationManager] Session limit (${this.maxPerSession}) reached for queued item, rejecting`);
|
|
4192
4230
|
toReject.push({ reject: reject2, error: new Error(`Maximum delegations per session (${this.maxPerSession}) reached for session ${parentSessionId}`) });
|
|
4193
4231
|
continue;
|
|
4194
4232
|
}
|
|
4195
4233
|
}
|
|
4196
4234
|
this._incrementCounters(parentSessionId);
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
console.error(`[DelegationManager] Granted slot from queue (waited ${waitTime}ms). Active: ${this.globalActive}/${this.maxConcurrent}`);
|
|
4200
|
-
}
|
|
4235
|
+
const waitTime = Date.now() - queuedAt;
|
|
4236
|
+
console.error(`[DelegationManager] Granted slot from queue (waited ${waitTime}ms). Active: ${this.globalActive}/${this.maxConcurrent}`);
|
|
4201
4237
|
toResolve.push(resolve9);
|
|
4202
4238
|
}
|
|
4203
4239
|
if (toResolve.length > 0 || toReject.length > 0) {
|
|
@@ -4247,6 +4283,9 @@ var init_delegate = __esm({
|
|
|
4247
4283
|
if (entry.timeoutId) {
|
|
4248
4284
|
clearTimeout(entry.timeoutId);
|
|
4249
4285
|
}
|
|
4286
|
+
if (entry.reminderId) {
|
|
4287
|
+
clearInterval(entry.reminderId);
|
|
4288
|
+
}
|
|
4250
4289
|
if (entry.reject) {
|
|
4251
4290
|
entry.reject(new Error("DelegationManager was cleaned up"));
|
|
4252
4291
|
}
|
|
@@ -4369,8 +4408,9 @@ Instructions:
|
|
|
4369
4408
|
promptType: "code-researcher",
|
|
4370
4409
|
allowedTools: ["extract"],
|
|
4371
4410
|
maxIterations: 5,
|
|
4372
|
-
delegationManager: options.delegationManager
|
|
4411
|
+
delegationManager: options.delegationManager,
|
|
4373
4412
|
// Per-instance delegation limits
|
|
4413
|
+
parentAbortSignal: options.parentAbortSignal || null
|
|
4374
4414
|
// timeout removed - inherit default from delegate (300s)
|
|
4375
4415
|
});
|
|
4376
4416
|
return { chunk, result };
|
|
@@ -4390,16 +4430,12 @@ async function processChunksParallel(chunks, extractionPrompt, maxWorkers, optio
|
|
|
4390
4430
|
return result;
|
|
4391
4431
|
});
|
|
4392
4432
|
active.add(promise);
|
|
4393
|
-
|
|
4394
|
-
console.error(`[analyze_all] Started processing chunk ${chunk.id}/${chunk.total}`);
|
|
4395
|
-
}
|
|
4433
|
+
console.error(`[analyze_all] Started processing chunk ${chunk.id}/${chunk.total}`);
|
|
4396
4434
|
}
|
|
4397
4435
|
if (active.size > 0) {
|
|
4398
4436
|
const result = await Promise.race(active);
|
|
4399
4437
|
results.push(result);
|
|
4400
|
-
|
|
4401
|
-
console.error(`[analyze_all] Completed chunk ${result.chunk.id}/${result.chunk.total}`);
|
|
4402
|
-
}
|
|
4438
|
+
console.error(`[analyze_all] Completed chunk ${result.chunk.id}/${result.chunk.total}`);
|
|
4403
4439
|
}
|
|
4404
4440
|
}
|
|
4405
4441
|
results.sort((a, b) => a.chunk.id - b.chunk.id);
|
|
@@ -4469,8 +4505,9 @@ Organize all findings into clear categories with items listed under each.${compl
|
|
|
4469
4505
|
promptType: "code-researcher",
|
|
4470
4506
|
allowedTools: [],
|
|
4471
4507
|
maxIterations: 5,
|
|
4472
|
-
delegationManager: options.delegationManager
|
|
4508
|
+
delegationManager: options.delegationManager,
|
|
4473
4509
|
// Per-instance delegation limits
|
|
4510
|
+
parentAbortSignal: options.parentAbortSignal || null
|
|
4474
4511
|
// timeout removed - inherit default from delegate (300s)
|
|
4475
4512
|
});
|
|
4476
4513
|
return result;
|
|
@@ -4534,8 +4571,9 @@ CRITICAL: Do NOT guess keywords. Actually run searches and see what returns resu
|
|
|
4534
4571
|
promptType: "code-researcher",
|
|
4535
4572
|
// Full tool access for exploration and experimentation
|
|
4536
4573
|
maxIterations: 15,
|
|
4537
|
-
delegationManager: options.delegationManager
|
|
4574
|
+
delegationManager: options.delegationManager,
|
|
4538
4575
|
// Per-instance delegation limits
|
|
4576
|
+
parentAbortSignal: options.parentAbortSignal || null
|
|
4539
4577
|
// timeout removed - inherit default from delegate (300s)
|
|
4540
4578
|
});
|
|
4541
4579
|
const plan = parsePlanningResult(stripResultTags(result));
|
|
@@ -4592,8 +4630,9 @@ When done, use the attempt_completion tool with your answer as the result.`;
|
|
|
4592
4630
|
promptType: "code-researcher",
|
|
4593
4631
|
allowedTools: [],
|
|
4594
4632
|
maxIterations: 5,
|
|
4595
|
-
delegationManager: options.delegationManager
|
|
4633
|
+
delegationManager: options.delegationManager,
|
|
4596
4634
|
// Per-instance delegation limits
|
|
4635
|
+
parentAbortSignal: options.parentAbortSignal || null
|
|
4597
4636
|
// timeout removed - inherit default from delegate (300s)
|
|
4598
4637
|
});
|
|
4599
4638
|
return stripResultTags(result);
|
|
@@ -8890,13 +8929,13 @@ function resolveTargetPath(target, cwd) {
|
|
|
8890
8929
|
}
|
|
8891
8930
|
return filePart + suffix;
|
|
8892
8931
|
}
|
|
8893
|
-
var searchSchema, searchAllSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, listFilesSchema, searchFilesSchema, readImageSchema, bashSchema, analyzeAllSchema, executePlanSchema, cleanupExecutePlanSchema, searchDescription, queryDescription, extractDescription, delegateDescription, analyzeAllDescription;
|
|
8932
|
+
var searchSchema, searchAllSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, listFilesSchema, searchFilesSchema, readImageSchema, bashSchema, analyzeAllSchema, executePlanSchema, cleanupExecutePlanSchema, searchDescription, searchDelegateDescription, queryDescription, extractDescription, delegateDescription, analyzeAllDescription;
|
|
8894
8933
|
var init_common = __esm({
|
|
8895
8934
|
"src/tools/common.js"() {
|
|
8896
8935
|
"use strict";
|
|
8897
8936
|
init_zod();
|
|
8898
8937
|
searchSchema = external_exports.object({
|
|
8899
|
-
query: external_exports.string().describe("Search query
|
|
8938
|
+
query: external_exports.string().describe("Search query \u2014 natural language questions or Elasticsearch-style keywords both work. For keywords: use quotes for exact phrases, AND/OR for boolean logic, - for negation. Probe handles stemming and camelCase/snake_case splitting automatically, so do NOT try case or style variations of the same keyword."),
|
|
8900
8939
|
path: external_exports.string().optional().default(".").describe('Path to search in. For dependencies use "go:github.com/owner/repo", "js:package_name", or "rust:cargo_name" etc.'),
|
|
8901
8940
|
exact: external_exports.boolean().optional().default(false).describe('Default (false) enables stemming and keyword splitting for exploratory search - "getUserData" matches "get", "user", "data", etc. Set true for precise symbol lookup where "getUserData" matches only "getUserData". Use true when you know the exact symbol name.'),
|
|
8902
8941
|
maxTokens: external_exports.number().nullable().optional().describe("Maximum tokens to return. Default is 20000. Set to null for unlimited results."),
|
|
@@ -8904,7 +8943,7 @@ var init_common = __esm({
|
|
|
8904
8943
|
nextPage: external_exports.boolean().optional().default(false).describe("Set to true when requesting the next page of results. Requires passing the same session ID from the previous search output.")
|
|
8905
8944
|
});
|
|
8906
8945
|
searchAllSchema = external_exports.object({
|
|
8907
|
-
query: external_exports.string().describe("Search query
|
|
8946
|
+
query: external_exports.string().describe("Search query \u2014 natural language questions or Elasticsearch-style keywords both work. For keywords: use quotes for exact phrases, AND/OR for boolean logic, - for negation. Probe handles stemming and camelCase/snake_case splitting automatically, so do NOT try case or style variations of the same keyword."),
|
|
8908
8947
|
path: external_exports.string().optional().default(".").describe("Path to search in."),
|
|
8909
8948
|
exact: external_exports.boolean().optional().default(false).describe("Use exact matching instead of stemming."),
|
|
8910
8949
|
maxTokensPerPage: external_exports.number().optional().default(2e4).describe("Tokens per page when paginating. Default 20000."),
|
|
@@ -8959,7 +8998,8 @@ var init_common = __esm({
|
|
|
8959
8998
|
clearOutputBuffer: external_exports.boolean().optional().default(true).describe("Clear the output buffer from previous execute_plan calls"),
|
|
8960
8999
|
clearSessionStore: external_exports.boolean().optional().default(false).describe("Clear the session store (persisted data across execute_plan calls)")
|
|
8961
9000
|
});
|
|
8962
|
-
searchDescription =
|
|
9001
|
+
searchDescription = 'Search code in the repository. Free-form questions are accepted, but Elasticsearch-style keyword queries work best. Use this tool first for any code-related questions. NOTE: By default, search handles stemming, case-insensitive matching, and camelCase/snake_case splitting automatically \u2014 do NOT manually try keyword variations like "getAllUsers" then "get_all_users" then "GetAllUsers". One search covers all variations.';
|
|
9002
|
+
searchDelegateDescription = 'Search code in the repository by asking a question. Accepts natural language questions (e.g., "How does authentication work?", "Where is the user validation logic?"). A specialized subagent breaks down your question into targeted keyword searches and returns extracted code blocks. Do NOT formulate keyword queries yourself \u2014 just ask the question naturally.';
|
|
8963
9003
|
queryDescription = "Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.";
|
|
8964
9004
|
extractDescription = "Extract code blocks from files based on file paths and optional line numbers. Use this tool to see complete context after finding relevant files. Line numbers from output can be used with edit start_line/end_line for precise editing.";
|
|
8965
9005
|
delegateDescription = "Automatically delegate big distinct tasks to specialized probe subagents within the agentic loop. Used by AI agents to break down complex requests into focused, parallel tasks.";
|
|
@@ -9144,11 +9184,41 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
|
|
|
9144
9184
|
"- extract: Verify code snippets to ensure targets are actually relevant before including them.",
|
|
9145
9185
|
"- listFiles: Understand directory structure to find where relevant code might live.",
|
|
9146
9186
|
"",
|
|
9147
|
-
"
|
|
9187
|
+
"CRITICAL - How probe search works (do NOT ignore):",
|
|
9188
|
+
"- By default (exact=false), probe ALREADY handles stemming, case-insensitive matching, and camelCase/snake_case splitting.",
|
|
9189
|
+
'- Searching "allowed_ips" ALREADY matches "AllowedIPs", "allowedIps", "allowed_ips", etc. Do NOT manually try case/style variations.',
|
|
9190
|
+
'- Searching "getUserData" ALREADY matches "get", "user", "data" and their variations.',
|
|
9191
|
+
"- NEVER repeat the same search query \u2014 you will get the same results.",
|
|
9192
|
+
"- NEVER search trivial variations of the same keyword (e.g., AllowedIPs then allowedIps then allowed_ips). This is wasteful \u2014 probe handles it.",
|
|
9193
|
+
"- If a search returns no results, the term likely does not exist in that path. Try a genuinely DIFFERENT keyword or concept, not a variation.",
|
|
9194
|
+
"- If 2-3 consecutive searches return no results for a concept, STOP searching for it and move on.",
|
|
9195
|
+
"",
|
|
9196
|
+
"GOOD search strategy (do this):",
|
|
9197
|
+
' Query: "How does authentication work and how are sessions managed?"',
|
|
9198
|
+
' \u2192 search "authentication" \u2192 search "session management" (two different concepts)',
|
|
9199
|
+
' Query: "Find the IP allowlist middleware"',
|
|
9200
|
+
' \u2192 search "allowlist middleware" (one search, probe handles IP/ip/Ip variations)',
|
|
9201
|
+
' Query: "How does BM25 scoring work with SIMD optimization?"',
|
|
9202
|
+
' \u2192 search "BM25 scoring" \u2192 search "SIMD optimization" (two different concepts)',
|
|
9203
|
+
"",
|
|
9204
|
+
"BAD search strategy (never do this):",
|
|
9205
|
+
' \u2192 search "AllowedIPs" \u2192 search "allowedIps" \u2192 search "allowed_ips" (WRONG: these are trivial case variations, probe handles them)',
|
|
9206
|
+
' \u2192 search "CIDR" \u2192 search "cidr" \u2192 search "Cidr" \u2192 search "*cidr*" (WRONG: same keyword repeated with variations)',
|
|
9207
|
+
' \u2192 search "error handling" \u2192 search "error handling" \u2192 search "error handling" (WRONG: repeating exact same query)',
|
|
9208
|
+
"",
|
|
9209
|
+
"Keyword tips:",
|
|
9210
|
+
"- Common programming keywords are filtered as stopwords when unquoted: function, class, return, new, struct, impl, var, let, const, etc.",
|
|
9211
|
+
'- Avoid searching for these alone \u2014 combine with a specific term (e.g., "middleware function" is fine, "function" alone is too generic).',
|
|
9212
|
+
'- To bypass stopword filtering: wrap terms in quotes ("return", "struct") or set exact=true. Both disable stemming and splitting too.',
|
|
9213
|
+
"- Multiple words without operators use OR logic: foo bar = foo OR bar. Use AND explicitly if you need both: foo AND bar.",
|
|
9214
|
+
'- camelCase terms are split: getUserData becomes "get", "user", "data" \u2014 so one search covers all naming styles.',
|
|
9215
|
+
"",
|
|
9216
|
+
"Strategy:",
|
|
9148
9217
|
"1. Analyze the query - identify key concepts, entities, and relationships",
|
|
9149
|
-
|
|
9150
|
-
"3.
|
|
9151
|
-
"4.
|
|
9218
|
+
"2. Run ONE focused search per concept with the most natural keyword. Trust probe to handle variations.",
|
|
9219
|
+
"3. If a search returns results, use extract to verify relevance",
|
|
9220
|
+
"4. Only try a different keyword if the first one returned irrelevant results (not if it returned no results \u2014 that means the concept is absent)",
|
|
9221
|
+
"5. Combine all relevant targets in your final response",
|
|
9152
9222
|
"",
|
|
9153
9223
|
`Query: ${searchQuery}`,
|
|
9154
9224
|
`Search path(s): ${searchPath}`,
|
|
@@ -9199,9 +9269,12 @@ var init_vercel = __esm({
|
|
|
9199
9269
|
}
|
|
9200
9270
|
return result;
|
|
9201
9271
|
};
|
|
9272
|
+
const previousSearches = /* @__PURE__ */ new Set();
|
|
9273
|
+
const paginationCounts = /* @__PURE__ */ new Map();
|
|
9274
|
+
const MAX_PAGES_PER_QUERY = 3;
|
|
9202
9275
|
return tool({
|
|
9203
9276
|
name: "search",
|
|
9204
|
-
description: searchDelegate ?
|
|
9277
|
+
description: searchDelegate ? searchDelegateDescription : searchDescription,
|
|
9205
9278
|
inputSchema: searchSchema,
|
|
9206
9279
|
execute: async ({ query: searchQuery, path: path9, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage }) => {
|
|
9207
9280
|
const effectiveMaxTokens = paramMaxTokens || maxTokens;
|
|
@@ -9239,6 +9312,26 @@ var init_vercel = __esm({
|
|
|
9239
9312
|
return await search(searchOptions);
|
|
9240
9313
|
};
|
|
9241
9314
|
if (!searchDelegate) {
|
|
9315
|
+
const searchKey = `${searchQuery}::${searchPath}::${exact || false}`;
|
|
9316
|
+
if (!nextPage) {
|
|
9317
|
+
if (previousSearches.has(searchKey)) {
|
|
9318
|
+
if (debug) {
|
|
9319
|
+
console.error(`[DEDUP] Blocked duplicate search: "${searchQuery}" in "${searchPath}"`);
|
|
9320
|
+
}
|
|
9321
|
+
return "DUPLICATE SEARCH BLOCKED: You already searched for this exact query in this path. Do NOT repeat the same search. If you need more results, set nextPage=true with the session ID from the previous search. Otherwise, try a genuinely different keyword, use extract to examine results you already found, or use attempt_completion if you have enough information.";
|
|
9322
|
+
}
|
|
9323
|
+
previousSearches.add(searchKey);
|
|
9324
|
+
paginationCounts.set(searchKey, 0);
|
|
9325
|
+
} else {
|
|
9326
|
+
const pageCount = (paginationCounts.get(searchKey) || 0) + 1;
|
|
9327
|
+
paginationCounts.set(searchKey, pageCount);
|
|
9328
|
+
if (pageCount > MAX_PAGES_PER_QUERY) {
|
|
9329
|
+
if (debug) {
|
|
9330
|
+
console.error(`[DEDUP] Blocked excessive pagination (page ${pageCount}/${MAX_PAGES_PER_QUERY}): "${searchQuery}" in "${searchPath}"`);
|
|
9331
|
+
}
|
|
9332
|
+
return `PAGINATION LIMIT REACHED: You have already retrieved ${MAX_PAGES_PER_QUERY} pages of results for this query. You have enough results \u2014 use extract to examine specific files, or use attempt_completion to return your findings.`;
|
|
9333
|
+
}
|
|
9334
|
+
}
|
|
9242
9335
|
try {
|
|
9243
9336
|
const result = maybeAnnotate(await runRawSearch());
|
|
9244
9337
|
if (options.fileTracker && typeof result === "string") {
|
|
@@ -9277,7 +9370,8 @@ var init_vercel = __esm({
|
|
|
9277
9370
|
promptType: "code-searcher",
|
|
9278
9371
|
allowedTools: ["search", "extract", "listFiles", "attempt_completion"],
|
|
9279
9372
|
searchDelegate: false,
|
|
9280
|
-
schema: CODE_SEARCH_SCHEMA
|
|
9373
|
+
schema: CODE_SEARCH_SCHEMA,
|
|
9374
|
+
parentAbortSignal: options.parentAbortSignal || null
|
|
9281
9375
|
});
|
|
9282
9376
|
const delegateResult = options.tracer?.withSpan ? await options.tracer.withSpan("search.delegate", runDelegation, {
|
|
9283
9377
|
"search.query": searchQuery,
|
|
@@ -9491,7 +9585,7 @@ var init_vercel = __esm({
|
|
|
9491
9585
|
name: "delegate",
|
|
9492
9586
|
description: delegateDescription,
|
|
9493
9587
|
inputSchema: delegateSchema,
|
|
9494
|
-
execute: async ({ task, currentIteration, maxIterations, parentSessionId, path: path9, provider, model, tracer, searchDelegate }) => {
|
|
9588
|
+
execute: async ({ task, currentIteration, maxIterations, parentSessionId, path: path9, provider, model, tracer, searchDelegate, parentAbortSignal }) => {
|
|
9495
9589
|
if (!task || typeof task !== "string") {
|
|
9496
9590
|
throw new Error("Task parameter is required and must be a non-empty string");
|
|
9497
9591
|
}
|
|
@@ -9549,8 +9643,9 @@ var init_vercel = __esm({
|
|
|
9549
9643
|
enableMcp,
|
|
9550
9644
|
mcpConfig,
|
|
9551
9645
|
mcpConfigPath,
|
|
9552
|
-
delegationManager
|
|
9646
|
+
delegationManager,
|
|
9553
9647
|
// Per-instance delegation limits
|
|
9648
|
+
parentAbortSignal
|
|
9554
9649
|
});
|
|
9555
9650
|
return result;
|
|
9556
9651
|
}
|
|
@@ -9588,8 +9683,9 @@ var init_vercel = __esm({
|
|
|
9588
9683
|
provider: options.provider,
|
|
9589
9684
|
model: options.model,
|
|
9590
9685
|
tracer: options.tracer,
|
|
9591
|
-
delegationManager
|
|
9686
|
+
delegationManager,
|
|
9592
9687
|
// Per-instance delegation limits
|
|
9688
|
+
parentAbortSignal: options.parentAbortSignal || null
|
|
9593
9689
|
});
|
|
9594
9690
|
return result;
|
|
9595
9691
|
} catch (error) {
|
|
@@ -22106,6 +22202,7 @@ function generateSandboxGlobals(options) {
|
|
|
22106
22202
|
executing.add(p);
|
|
22107
22203
|
results.push(p);
|
|
22108
22204
|
if (executing.size >= mapConcurrency) {
|
|
22205
|
+
console.error(`[map] Concurrency limit reached (${executing.size}/${mapConcurrency}), waiting for a slot...`);
|
|
22109
22206
|
await Promise.race(executing);
|
|
22110
22207
|
}
|
|
22111
22208
|
}
|
|
@@ -81768,7 +81865,9 @@ __export(ProbeAgent_exports, {
|
|
|
81768
81865
|
ENGINE_ACTIVITY_TIMEOUT_DEFAULT: () => ENGINE_ACTIVITY_TIMEOUT_DEFAULT,
|
|
81769
81866
|
ENGINE_ACTIVITY_TIMEOUT_MAX: () => ENGINE_ACTIVITY_TIMEOUT_MAX,
|
|
81770
81867
|
ENGINE_ACTIVITY_TIMEOUT_MIN: () => ENGINE_ACTIVITY_TIMEOUT_MIN,
|
|
81771
|
-
ProbeAgent: () => ProbeAgent
|
|
81868
|
+
ProbeAgent: () => ProbeAgent,
|
|
81869
|
+
debugLogToolResults: () => debugLogToolResults,
|
|
81870
|
+
debugTruncate: () => debugTruncate
|
|
81772
81871
|
});
|
|
81773
81872
|
import dotenv2 from "dotenv";
|
|
81774
81873
|
import { createAnthropic as createAnthropic2 } from "@ai-sdk/anthropic";
|
|
@@ -81781,6 +81880,19 @@ import { EventEmitter as EventEmitter5 } from "events";
|
|
|
81781
81880
|
import { existsSync as existsSync7 } from "fs";
|
|
81782
81881
|
import { readFile as readFile3, stat, readdir as readdir3 } from "fs/promises";
|
|
81783
81882
|
import { resolve as resolve7, isAbsolute as isAbsolute6, dirname as dirname5, basename, normalize as normalize2, sep as sep5 } from "path";
|
|
81883
|
+
function debugTruncate(s, limit = 200) {
|
|
81884
|
+
if (s.length <= limit) return s;
|
|
81885
|
+
const half = Math.floor(limit / 2);
|
|
81886
|
+
return s.substring(0, half) + ` ... [${s.length} chars] ... ` + s.substring(s.length - half);
|
|
81887
|
+
}
|
|
81888
|
+
function debugLogToolResults(toolResults) {
|
|
81889
|
+
if (!toolResults || toolResults.length === 0) return;
|
|
81890
|
+
for (const tr of toolResults) {
|
|
81891
|
+
const argsStr = JSON.stringify(tr.args || {});
|
|
81892
|
+
const resultStr = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result || "");
|
|
81893
|
+
console.log(`[DEBUG] tool: ${tr.toolName} | args: ${debugTruncate(argsStr)} | result: ${debugTruncate(resultStr)}`);
|
|
81894
|
+
}
|
|
81895
|
+
}
|
|
81784
81896
|
var ENGINE_ACTIVITY_TIMEOUT_DEFAULT, ENGINE_ACTIVITY_TIMEOUT_MIN, ENGINE_ACTIVITY_TIMEOUT_MAX, MAX_TOOL_ITERATIONS, MAX_HISTORY_MESSAGES, MAX_IMAGE_FILE_SIZE, ProbeAgent;
|
|
81785
81897
|
var init_ProbeAgent = __esm({
|
|
81786
81898
|
"src/agent/ProbeAgent.js"() {
|
|
@@ -81889,6 +82001,7 @@ var init_ProbeAgent = __esm({
|
|
|
81889
82001
|
this.enableExecutePlan = !!options.enableExecutePlan;
|
|
81890
82002
|
this.debug = options.debug || process.env.DEBUG === "1";
|
|
81891
82003
|
this.cancelled = false;
|
|
82004
|
+
this._abortController = new AbortController();
|
|
81892
82005
|
this.tracer = options.tracer || null;
|
|
81893
82006
|
this.outline = !!options.outline;
|
|
81894
82007
|
this.searchDelegate = options.searchDelegate !== void 0 ? !!options.searchDelegate : true;
|
|
@@ -81917,6 +82030,7 @@ var init_ProbeAgent = __esm({
|
|
|
81917
82030
|
this.completionPrompt = options.completionPrompt || null;
|
|
81918
82031
|
this.thinkingEffort = options.thinkingEffort || null;
|
|
81919
82032
|
const effectiveAllowedTools = options.disableTools ? [] : options.allowedTools;
|
|
82033
|
+
this._rawAllowedTools = options.allowedTools;
|
|
81920
82034
|
this.allowedTools = this._parseAllowedTools(effectiveAllowedTools);
|
|
81921
82035
|
this.storageAdapter = options.storageAdapter || new InMemoryStorageAdapter();
|
|
81922
82036
|
this.hooks = new HookManager();
|
|
@@ -82059,6 +82173,16 @@ var init_ProbeAgent = __esm({
|
|
|
82059
82173
|
_filterMcpTools(mcpToolNames) {
|
|
82060
82174
|
return mcpToolNames.filter((toolName) => this._isMcpToolAllowed(toolName));
|
|
82061
82175
|
}
|
|
82176
|
+
/**
|
|
82177
|
+
* Check if query tool was explicitly listed in allowedTools (not via wildcard).
|
|
82178
|
+
* Query (ast-grep) is excluded by default because models struggle with AST pattern syntax.
|
|
82179
|
+
* @returns {boolean}
|
|
82180
|
+
* @private
|
|
82181
|
+
*/
|
|
82182
|
+
_isQueryExplicitlyAllowed() {
|
|
82183
|
+
if (!this._rawAllowedTools) return false;
|
|
82184
|
+
return Array.isArray(this._rawAllowedTools) && this._rawAllowedTools.includes("query");
|
|
82185
|
+
}
|
|
82062
82186
|
/**
|
|
82063
82187
|
* Check if tracer is AppTracer (expects sessionId as first param) vs SimpleAppTracer
|
|
82064
82188
|
* @returns {boolean} - True if tracer is AppTracer style (requires sessionId)
|
|
@@ -82334,6 +82458,8 @@ var init_ProbeAgent = __esm({
|
|
|
82334
82458
|
searchDelegateModel: this.searchDelegateModel,
|
|
82335
82459
|
delegationManager: this.delegationManager,
|
|
82336
82460
|
// Per-instance delegation limits
|
|
82461
|
+
parentAbortSignal: this._abortController.signal,
|
|
82462
|
+
// Propagate cancellation to delegations
|
|
82337
82463
|
outputBuffer: this._outputBuffer,
|
|
82338
82464
|
concurrencyLimiter: this.concurrencyLimiter,
|
|
82339
82465
|
// Global AI concurrency limiter
|
|
@@ -82350,7 +82476,7 @@ var init_ProbeAgent = __esm({
|
|
|
82350
82476
|
if (wrappedTools.searchToolInstance && isToolAllowed("search")) {
|
|
82351
82477
|
this.toolImplementations.search = wrappedTools.searchToolInstance;
|
|
82352
82478
|
}
|
|
82353
|
-
if (wrappedTools.queryToolInstance && isToolAllowed("query")) {
|
|
82479
|
+
if (wrappedTools.queryToolInstance && isToolAllowed("query") && this._isQueryExplicitlyAllowed()) {
|
|
82354
82480
|
this.toolImplementations.query = wrappedTools.queryToolInstance;
|
|
82355
82481
|
}
|
|
82356
82482
|
if (wrappedTools.extractToolInstance && isToolAllowed("extract")) {
|
|
@@ -82781,6 +82907,15 @@ var init_ProbeAgent = __esm({
|
|
|
82781
82907
|
}
|
|
82782
82908
|
const controller = new AbortController();
|
|
82783
82909
|
const timeoutState = { timeoutId: null };
|
|
82910
|
+
if (this._abortController.signal.aborted) {
|
|
82911
|
+
controller.abort();
|
|
82912
|
+
} else {
|
|
82913
|
+
const onAgentAbort = () => controller.abort();
|
|
82914
|
+
this._abortController.signal.addEventListener("abort", onAgentAbort, { once: true });
|
|
82915
|
+
controller.signal.addEventListener("abort", () => {
|
|
82916
|
+
this._abortController.signal.removeEventListener("abort", onAgentAbort);
|
|
82917
|
+
}, { once: true });
|
|
82918
|
+
}
|
|
82784
82919
|
if (this.maxOperationTimeout && this.maxOperationTimeout > 0) {
|
|
82785
82920
|
timeoutState.timeoutId = setTimeout(() => {
|
|
82786
82921
|
controller.abort();
|
|
@@ -83084,7 +83219,8 @@ var init_ProbeAgent = __esm({
|
|
|
83084
83219
|
allowEdit: this.allowEdit,
|
|
83085
83220
|
allowedTools: allowedToolsForDelegate,
|
|
83086
83221
|
debug: this.debug,
|
|
83087
|
-
tracer: this.tracer
|
|
83222
|
+
tracer: this.tracer,
|
|
83223
|
+
parentAbortSignal: this._abortController.signal
|
|
83088
83224
|
};
|
|
83089
83225
|
if (this.debug) {
|
|
83090
83226
|
console.log(`[DEBUG] Executing delegate tool`);
|
|
@@ -83279,12 +83415,13 @@ var init_ProbeAgent = __esm({
|
|
|
83279
83415
|
const toolMap = {
|
|
83280
83416
|
search: {
|
|
83281
83417
|
schema: searchSchema,
|
|
83282
|
-
description: "Search code in the repository using keyword queries with Elasticsearch syntax."
|
|
83283
|
-
},
|
|
83284
|
-
query: {
|
|
83285
|
-
schema: querySchema,
|
|
83286
|
-
description: "Search code using ast-grep structural pattern matching."
|
|
83418
|
+
description: this.searchDelegate ? "Search code in the repository by asking a question. Accepts natural language questions \u2014 a subagent breaks them into targeted keyword searches and returns extracted code blocks. Do NOT formulate keyword queries yourself." : "Search code in the repository using keyword queries with Elasticsearch syntax. Handles stemming, case-insensitive matching, and camelCase/snake_case splitting automatically \u2014 do NOT try keyword variations manually."
|
|
83287
83419
|
},
|
|
83420
|
+
// query tool (ast-grep) removed from AI-facing tools — models struggle with pattern syntax
|
|
83421
|
+
// query: {
|
|
83422
|
+
// schema: querySchema,
|
|
83423
|
+
// description: 'Search code using ast-grep structural pattern matching.'
|
|
83424
|
+
// },
|
|
83288
83425
|
extract: {
|
|
83289
83426
|
schema: extractSchema,
|
|
83290
83427
|
description: "Extract code blocks from files based on file paths and optional line numbers."
|
|
@@ -83952,25 +84089,27 @@ ${this.architectureContext.content}
|
|
|
83952
84089
|
} else {
|
|
83953
84090
|
systemPrompt += predefinedPrompts["code-explorer"] + "\n\n";
|
|
83954
84091
|
}
|
|
84092
|
+
const searchToolDesc1 = this.searchDelegate ? '- search: Ask natural language questions to find code (e.g., "How does authentication work?"). A subagent handles keyword searches and returns extracted code blocks. Do NOT formulate keyword queries \u2014 just ask questions.' : "- search: Find code patterns using keyword queries with Elasticsearch syntax. Handles stemming and case variations automatically \u2014 do NOT try manual keyword variations.";
|
|
83955
84093
|
systemPrompt += `You have access to powerful code search and analysis tools through MCP:
|
|
83956
|
-
|
|
84094
|
+
${searchToolDesc1}
|
|
83957
84095
|
- extract: Extract specific code sections with context
|
|
83958
|
-
- query: Use AST patterns for structural code matching
|
|
83959
84096
|
- listFiles: Browse directory contents
|
|
83960
84097
|
- searchFiles: Find files by name patterns`;
|
|
83961
84098
|
if (this.enableBash) {
|
|
83962
84099
|
systemPrompt += `
|
|
83963
84100
|
- bash: Execute bash commands for system operations`;
|
|
83964
84101
|
}
|
|
83965
|
-
const
|
|
83966
|
-
const
|
|
84102
|
+
const searchGuidance1 = this.searchDelegate ? "1. Start with search \u2014 ask a question about what you want to understand. It returns extracted code blocks directly." : "1. Start with search to find relevant code patterns. One search per concept is usually enough \u2014 probe handles stemming and case variations.";
|
|
84103
|
+
const extractGuidance1 = this.searchDelegate ? "2. Use extract only if you need more context or a full file" : "2. Use extract to get detailed context when needed";
|
|
83967
84104
|
systemPrompt += `
|
|
83968
84105
|
|
|
83969
84106
|
When exploring code:
|
|
83970
|
-
${
|
|
83971
|
-
${
|
|
84107
|
+
${searchGuidance1}
|
|
84108
|
+
${extractGuidance1}
|
|
83972
84109
|
3. Prefer focused, specific searches over broad queries
|
|
83973
|
-
4.
|
|
84110
|
+
4. Do NOT repeat the same search or try trivial keyword variations \u2014 probe handles stemming and case variations automatically
|
|
84111
|
+
5. If 2-3 consecutive searches return no results for a concept, stop searching for it \u2014 the term likely does not exist in that codebase
|
|
84112
|
+
6. Combine multiple tools to build complete understanding`;
|
|
83974
84113
|
if (this.allowedFolders && this.allowedFolders.length > 0) {
|
|
83975
84114
|
systemPrompt += `
|
|
83976
84115
|
|
|
@@ -84005,25 +84144,27 @@ Workspace: ${this.allowedFolders.join(", ")}`;
|
|
|
84005
84144
|
} else {
|
|
84006
84145
|
systemPrompt += predefinedPrompts["code-explorer"] + "\n\n";
|
|
84007
84146
|
}
|
|
84147
|
+
const searchToolDesc2 = this.searchDelegate ? '- search: Ask natural language questions to find code (e.g., "How does authentication work?"). A subagent handles keyword searches and returns extracted code blocks. Do NOT formulate keyword queries \u2014 just ask questions.' : "- search: Find code patterns using keyword queries with Elasticsearch syntax. Handles stemming and case variations automatically \u2014 do NOT try manual keyword variations.";
|
|
84008
84148
|
systemPrompt += `You have access to powerful code search and analysis tools through MCP:
|
|
84009
|
-
|
|
84149
|
+
${searchToolDesc2}
|
|
84010
84150
|
- extract: Extract specific code sections with context
|
|
84011
|
-
- query: Use AST patterns for structural code matching
|
|
84012
84151
|
- listFiles: Browse directory contents
|
|
84013
84152
|
- searchFiles: Find files by name patterns`;
|
|
84014
84153
|
if (this.enableBash) {
|
|
84015
84154
|
systemPrompt += `
|
|
84016
84155
|
- bash: Execute bash commands for system operations`;
|
|
84017
84156
|
}
|
|
84018
|
-
const
|
|
84019
|
-
const
|
|
84157
|
+
const searchGuidance2 = this.searchDelegate ? "1. Start with search \u2014 ask a question about what you want to understand. It returns extracted code blocks directly." : "1. Start with search to find relevant code patterns. One search per concept is usually enough \u2014 probe handles stemming and case variations.";
|
|
84158
|
+
const extractGuidance2 = this.searchDelegate ? "2. Use extract only if you need more context or a full file" : "2. Use extract to get detailed context when needed";
|
|
84020
84159
|
systemPrompt += `
|
|
84021
84160
|
|
|
84022
84161
|
When exploring code:
|
|
84023
|
-
${
|
|
84024
|
-
${
|
|
84162
|
+
${searchGuidance2}
|
|
84163
|
+
${extractGuidance2}
|
|
84025
84164
|
3. Prefer focused, specific searches over broad queries
|
|
84026
|
-
4.
|
|
84165
|
+
4. Do NOT repeat the same search or try trivial keyword variations \u2014 probe handles stemming and case variations automatically
|
|
84166
|
+
5. If 2-3 consecutive searches return no results for a concept, stop searching for it \u2014 the term likely does not exist in that codebase
|
|
84167
|
+
6. Combine multiple tools to build complete understanding`;
|
|
84027
84168
|
if (this.allowedFolders && this.allowedFolders.length > 0) {
|
|
84028
84169
|
systemPrompt += `
|
|
84029
84170
|
|
|
@@ -84074,10 +84215,10 @@ Workspace: ${this.allowedFolders.join(", ")}`;
|
|
|
84074
84215
|
Follow these instructions carefully:
|
|
84075
84216
|
1. Analyze the user's request.
|
|
84076
84217
|
2. Use the available tools step-by-step to fulfill the request.
|
|
84077
|
-
3. You should always prefer the search tool for code-related questions.${this.searchDelegate ? "
|
|
84218
|
+
3. You should always prefer the search tool for code-related questions.${this.searchDelegate ? " Ask natural language questions \u2014 the search subagent handles keyword formulation and returns extracted code blocks. Use extract only to expand context or read full files." : " Search handles stemming and case variations automatically \u2014 do NOT try keyword variations manually. Read full files only if really necessary."}
|
|
84078
84219
|
4. Ensure to get really deep and understand the full picture before answering.
|
|
84079
84220
|
5. Once the task is fully completed, use the attempt_completion tool to provide the final result.
|
|
84080
|
-
6. Prefer concise and focused search queries. Use specific keywords and phrases to narrow down results
|
|
84221
|
+
6. ${this.searchDelegate ? "Ask clear, specific questions when searching. Each search should target a distinct concept or question." : "Prefer concise and focused search queries. Use specific keywords and phrases to narrow down results."}${this.allowEdit ? `
|
|
84081
84222
|
7. When modifying files, choose the appropriate tool:
|
|
84082
84223
|
- Use 'edit' for all code modifications:
|
|
84083
84224
|
* PREFERRED: Use start_line (and optionally end_line) for line-targeted editing \u2014 this is the safest and most precise approach.${this.hashLines ? ' Use the line:hash references from extract/search output (e.g. "42:ab") for integrity verification.' : ""} Always use extract first to see line numbers${this.hashLines ? " and hashes" : ""}, then edit by line reference.
|
|
@@ -84401,6 +84542,10 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
84401
84542
|
completionResult = result;
|
|
84402
84543
|
completionAttempted = true;
|
|
84403
84544
|
}, toolContext);
|
|
84545
|
+
if (this.debug) {
|
|
84546
|
+
const toolNames = Object.keys(tools2);
|
|
84547
|
+
console.log(`[DEBUG] Agent tools registered (${toolNames.length}): ${toolNames.join(", ")}`);
|
|
84548
|
+
}
|
|
84404
84549
|
let maxResponseTokens = this.maxResponseTokens;
|
|
84405
84550
|
if (!maxResponseTokens) {
|
|
84406
84551
|
maxResponseTokens = 4e3;
|
|
@@ -84440,6 +84585,7 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
84440
84585
|
}
|
|
84441
84586
|
if (this.debug) {
|
|
84442
84587
|
console.log(`[DEBUG] Step ${currentIteration}/${maxIterations} finished (reason: ${finishReason}, tools: ${toolResults?.length || 0})`);
|
|
84588
|
+
debugLogToolResults(toolResults);
|
|
84443
84589
|
}
|
|
84444
84590
|
}
|
|
84445
84591
|
};
|
|
@@ -84596,6 +84742,7 @@ Double-check your response based on the criteria above. If everything looks good
|
|
|
84596
84742
|
}
|
|
84597
84743
|
if (this.debug) {
|
|
84598
84744
|
console.log(`[DEBUG] Completion prompt step finished (reason: ${finishReason}, tools: ${toolResults?.length || 0})`);
|
|
84745
|
+
debugLogToolResults(toolResults);
|
|
84599
84746
|
}
|
|
84600
84747
|
}
|
|
84601
84748
|
};
|
|
@@ -85306,6 +85453,9 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
85306
85453
|
* Clean up resources (including MCP connections)
|
|
85307
85454
|
*/
|
|
85308
85455
|
async cleanup() {
|
|
85456
|
+
if (!this._abortController.signal.aborted) {
|
|
85457
|
+
this._abortController.abort();
|
|
85458
|
+
}
|
|
85309
85459
|
if (this.mcpBridge) {
|
|
85310
85460
|
try {
|
|
85311
85461
|
await this.mcpBridge.cleanup();
|
|
@@ -85329,14 +85479,25 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
85329
85479
|
this.clearHistory();
|
|
85330
85480
|
}
|
|
85331
85481
|
/**
|
|
85332
|
-
* Cancel the current request
|
|
85482
|
+
* Cancel the current request and all in-flight delegations.
|
|
85483
|
+
* Aborts the internal AbortController so streamText, subagents,
|
|
85484
|
+
* and any code checking the signal will stop.
|
|
85333
85485
|
*/
|
|
85334
85486
|
cancel() {
|
|
85335
85487
|
this.cancelled = true;
|
|
85488
|
+
this._abortController.abort();
|
|
85336
85489
|
if (this.debug) {
|
|
85337
85490
|
console.log(`[DEBUG] Agent cancelled for session ${this.sessionId}`);
|
|
85338
85491
|
}
|
|
85339
85492
|
}
|
|
85493
|
+
/**
|
|
85494
|
+
* Get the abort signal for this agent.
|
|
85495
|
+
* Delegations and subagents should check this signal.
|
|
85496
|
+
* @returns {AbortSignal}
|
|
85497
|
+
*/
|
|
85498
|
+
get abortSignal() {
|
|
85499
|
+
return this._abortController.signal;
|
|
85500
|
+
}
|
|
85340
85501
|
};
|
|
85341
85502
|
}
|
|
85342
85503
|
});
|