@probelabs/probe 0.6.0-rc302 → 0.6.0-rc304
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-rc302-aarch64-apple-darwin.tar.gz → probe-v0.6.0-rc304-aarch64-apple-darwin.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc302-aarch64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc304-aarch64-unknown-linux-musl.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc302-x86_64-apple-darwin.tar.gz → probe-v0.6.0-rc304-x86_64-apple-darwin.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc302-x86_64-pc-windows-msvc.zip → probe-v0.6.0-rc304-x86_64-pc-windows-msvc.zip} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc302-x86_64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc304-x86_64-unknown-linux-musl.tar.gz} +0 -0
- package/build/agent/FallbackManager.js +3 -57
- package/build/agent/ProbeAgent.js +48 -62
- package/build/delegate.js +15 -4
- package/build/tools/common.js +16 -1
- package/build/tools/vercel.js +448 -209
- package/build/utils/provider.js +106 -0
- package/cjs/agent/ProbeAgent.cjs +1078 -305
- package/cjs/index.cjs +529 -303
- package/package.json +1 -1
- package/src/agent/FallbackManager.js +3 -57
- package/src/agent/ProbeAgent.js +48 -62
- package/src/delegate.js +15 -4
- package/src/tools/common.js +16 -1
- package/src/tools/vercel.js +448 -209
- package/src/utils/provider.js +106 -0
package/cjs/agent/ProbeAgent.cjs
CHANGED
|
@@ -22274,6 +22274,87 @@ var init_dist3 = __esm({
|
|
|
22274
22274
|
}
|
|
22275
22275
|
});
|
|
22276
22276
|
|
|
22277
|
+
// src/utils/provider.js
|
|
22278
|
+
function createProviderInstance(config2) {
|
|
22279
|
+
switch (config2.provider) {
|
|
22280
|
+
case "anthropic":
|
|
22281
|
+
return (0, import_anthropic.createAnthropic)({
|
|
22282
|
+
apiKey: config2.apiKey,
|
|
22283
|
+
...config2.baseURL && { baseURL: config2.baseURL }
|
|
22284
|
+
});
|
|
22285
|
+
case "openai":
|
|
22286
|
+
return (0, import_openai.createOpenAI)({
|
|
22287
|
+
compatibility: "strict",
|
|
22288
|
+
apiKey: config2.apiKey,
|
|
22289
|
+
...config2.baseURL && { baseURL: config2.baseURL }
|
|
22290
|
+
});
|
|
22291
|
+
case "google":
|
|
22292
|
+
return (0, import_google.createGoogleGenerativeAI)({
|
|
22293
|
+
apiKey: config2.apiKey,
|
|
22294
|
+
...config2.baseURL && { baseURL: config2.baseURL }
|
|
22295
|
+
});
|
|
22296
|
+
case "bedrock": {
|
|
22297
|
+
const bedrockConfig = {};
|
|
22298
|
+
if (config2.apiKey) {
|
|
22299
|
+
bedrockConfig.apiKey = config2.apiKey;
|
|
22300
|
+
} else if (config2.accessKeyId && config2.secretAccessKey) {
|
|
22301
|
+
bedrockConfig.accessKeyId = config2.accessKeyId;
|
|
22302
|
+
bedrockConfig.secretAccessKey = config2.secretAccessKey;
|
|
22303
|
+
if (config2.sessionToken) {
|
|
22304
|
+
bedrockConfig.sessionToken = config2.sessionToken;
|
|
22305
|
+
}
|
|
22306
|
+
}
|
|
22307
|
+
if (config2.region) bedrockConfig.region = config2.region;
|
|
22308
|
+
if (config2.baseURL) bedrockConfig.baseURL = config2.baseURL;
|
|
22309
|
+
return createAmazonBedrock(bedrockConfig);
|
|
22310
|
+
}
|
|
22311
|
+
default:
|
|
22312
|
+
throw new Error(`Unknown provider "${config2.provider}"`);
|
|
22313
|
+
}
|
|
22314
|
+
}
|
|
22315
|
+
function resolveApiKey(providerName) {
|
|
22316
|
+
switch (providerName) {
|
|
22317
|
+
case "anthropic":
|
|
22318
|
+
return process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN;
|
|
22319
|
+
case "openai":
|
|
22320
|
+
return process.env.OPENAI_API_KEY;
|
|
22321
|
+
case "google":
|
|
22322
|
+
return process.env.GOOGLE_GENERATIVE_AI_API_KEY || process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY;
|
|
22323
|
+
case "bedrock":
|
|
22324
|
+
return process.env.AWS_BEDROCK_API_KEY;
|
|
22325
|
+
default:
|
|
22326
|
+
return void 0;
|
|
22327
|
+
}
|
|
22328
|
+
}
|
|
22329
|
+
async function createLanguageModel(providerName, modelName) {
|
|
22330
|
+
if (!providerName) return null;
|
|
22331
|
+
const resolvedModel = modelName || DEFAULT_MODELS[providerName];
|
|
22332
|
+
if (!resolvedModel) return null;
|
|
22333
|
+
try {
|
|
22334
|
+
const apiKey = resolveApiKey(providerName);
|
|
22335
|
+
const provider = createProviderInstance({ provider: providerName, ...apiKey ? { apiKey } : {} });
|
|
22336
|
+
return provider(resolvedModel);
|
|
22337
|
+
} catch {
|
|
22338
|
+
return null;
|
|
22339
|
+
}
|
|
22340
|
+
}
|
|
22341
|
+
var import_anthropic, import_openai, import_google, DEFAULT_MODELS;
|
|
22342
|
+
var init_provider = __esm({
|
|
22343
|
+
"src/utils/provider.js"() {
|
|
22344
|
+
"use strict";
|
|
22345
|
+
import_anthropic = require("@ai-sdk/anthropic");
|
|
22346
|
+
import_openai = require("@ai-sdk/openai");
|
|
22347
|
+
import_google = require("@ai-sdk/google");
|
|
22348
|
+
init_dist3();
|
|
22349
|
+
DEFAULT_MODELS = {
|
|
22350
|
+
anthropic: "claude-sonnet-4-6",
|
|
22351
|
+
openai: "gpt-5.2",
|
|
22352
|
+
google: "gemini-2.5-flash",
|
|
22353
|
+
bedrock: "anthropic.claude-sonnet-4-6"
|
|
22354
|
+
};
|
|
22355
|
+
}
|
|
22356
|
+
});
|
|
22357
|
+
|
|
22277
22358
|
// node_modules/gpt-tokenizer/esm/bpeRanks/o200k_base.js
|
|
22278
22359
|
var c0, c1, bpe, o200k_base_default;
|
|
22279
22360
|
var init_o200k_base = __esm({
|
|
@@ -24014,16 +24095,128 @@ var init_tokenCounter = __esm({
|
|
|
24014
24095
|
});
|
|
24015
24096
|
|
|
24016
24097
|
// src/agent/otelLogBridge.js
|
|
24017
|
-
|
|
24098
|
+
function getOtelApi() {
|
|
24099
|
+
if (otelApiAttempted) return otelApi;
|
|
24100
|
+
otelApiAttempted = true;
|
|
24101
|
+
try {
|
|
24102
|
+
otelApi = (function(name15) {
|
|
24103
|
+
return _require(name15);
|
|
24104
|
+
})("@opentelemetry/api");
|
|
24105
|
+
} catch {
|
|
24106
|
+
}
|
|
24107
|
+
return otelApi;
|
|
24108
|
+
}
|
|
24109
|
+
function getOtelLogger() {
|
|
24110
|
+
if (otelLoggerAttempted) return otelLogger;
|
|
24111
|
+
otelLoggerAttempted = true;
|
|
24112
|
+
try {
|
|
24113
|
+
const { logs } = (function(name15) {
|
|
24114
|
+
return _require(name15);
|
|
24115
|
+
})("@opentelemetry/api-logs");
|
|
24116
|
+
otelLogger = logs.getLogger("probe-agent");
|
|
24117
|
+
} catch {
|
|
24118
|
+
}
|
|
24119
|
+
return otelLogger;
|
|
24120
|
+
}
|
|
24121
|
+
function getTraceSuffix() {
|
|
24122
|
+
try {
|
|
24123
|
+
const api2 = getOtelApi();
|
|
24124
|
+
if (!api2) return "";
|
|
24125
|
+
const span = api2.trace.getSpan(api2.context.active());
|
|
24126
|
+
const ctx = span?.spanContext?.();
|
|
24127
|
+
if (!ctx?.traceId) return "";
|
|
24128
|
+
return ` [trace_id=${ctx.traceId} span_id=${ctx.spanId}]`;
|
|
24129
|
+
} catch {
|
|
24130
|
+
return "";
|
|
24131
|
+
}
|
|
24132
|
+
}
|
|
24133
|
+
function emitOtelLog(msg, level) {
|
|
24134
|
+
try {
|
|
24135
|
+
const logger = getOtelLogger();
|
|
24136
|
+
if (!logger) return;
|
|
24137
|
+
const api2 = getOtelApi();
|
|
24138
|
+
let traceId, spanId;
|
|
24139
|
+
if (api2) {
|
|
24140
|
+
const span = api2.trace.getSpan(api2.context.active());
|
|
24141
|
+
const ctx = span?.spanContext?.();
|
|
24142
|
+
if (ctx?.traceId) {
|
|
24143
|
+
traceId = ctx.traceId;
|
|
24144
|
+
spanId = ctx.spanId;
|
|
24145
|
+
}
|
|
24146
|
+
}
|
|
24147
|
+
logger.emit({
|
|
24148
|
+
severityNumber: OTEL_SEVERITY[level] || 9,
|
|
24149
|
+
severityText: level.toUpperCase(),
|
|
24150
|
+
body: msg,
|
|
24151
|
+
attributes: {
|
|
24152
|
+
"probe.logger": true,
|
|
24153
|
+
...traceId ? { trace_id: traceId, span_id: spanId } : {}
|
|
24154
|
+
}
|
|
24155
|
+
});
|
|
24156
|
+
} catch {
|
|
24157
|
+
}
|
|
24158
|
+
}
|
|
24159
|
+
function patchConsole() {
|
|
24160
|
+
if (patched) return;
|
|
24161
|
+
const methods = ["log", "info", "warn", "error"];
|
|
24162
|
+
const c = globalThis.console;
|
|
24163
|
+
for (const m of methods) {
|
|
24164
|
+
const orig = c[m].bind(c);
|
|
24165
|
+
originals[m] = orig;
|
|
24166
|
+
c[m] = (...args) => {
|
|
24167
|
+
const msgParts = args.map(
|
|
24168
|
+
(a) => typeof a === "string" ? a : a instanceof Error ? a.message : JSON.stringify(a)
|
|
24169
|
+
);
|
|
24170
|
+
const msg = msgParts.join(" ");
|
|
24171
|
+
emitOtelLog(msg, m === "log" ? "log" : m);
|
|
24172
|
+
const suffix = getTraceSuffix();
|
|
24173
|
+
if (suffix) {
|
|
24174
|
+
if (typeof args[0] === "string") {
|
|
24175
|
+
args[0] = args[0] + suffix;
|
|
24176
|
+
} else {
|
|
24177
|
+
args.push(suffix);
|
|
24178
|
+
}
|
|
24179
|
+
}
|
|
24180
|
+
return orig(...args);
|
|
24181
|
+
};
|
|
24182
|
+
}
|
|
24183
|
+
patched = true;
|
|
24184
|
+
}
|
|
24185
|
+
var import_module, _require, OTEL_SEVERITY, patched, originals, otelApi, otelApiAttempted, otelLogger, otelLoggerAttempted;
|
|
24018
24186
|
var init_otelLogBridge = __esm({
|
|
24019
24187
|
"src/agent/otelLogBridge.js"() {
|
|
24020
24188
|
"use strict";
|
|
24021
24189
|
import_module = require("module");
|
|
24022
24190
|
_require = (0, import_module.createRequire)("file:///");
|
|
24191
|
+
OTEL_SEVERITY = {
|
|
24192
|
+
log: 9,
|
|
24193
|
+
// INFO
|
|
24194
|
+
info: 9,
|
|
24195
|
+
// INFO
|
|
24196
|
+
warn: 13,
|
|
24197
|
+
// WARN
|
|
24198
|
+
error: 17,
|
|
24199
|
+
// ERROR
|
|
24200
|
+
debug: 5
|
|
24201
|
+
// DEBUG
|
|
24202
|
+
};
|
|
24203
|
+
patched = false;
|
|
24204
|
+
originals = {};
|
|
24205
|
+
otelApi = null;
|
|
24206
|
+
otelApiAttempted = false;
|
|
24207
|
+
otelLogger = null;
|
|
24208
|
+
otelLoggerAttempted = false;
|
|
24023
24209
|
}
|
|
24024
24210
|
});
|
|
24025
24211
|
|
|
24026
24212
|
// src/agent/simpleTelemetry.js
|
|
24213
|
+
var simpleTelemetry_exports = {};
|
|
24214
|
+
__export(simpleTelemetry_exports, {
|
|
24215
|
+
SimpleAppTracer: () => SimpleAppTracer,
|
|
24216
|
+
SimpleTelemetry: () => SimpleTelemetry,
|
|
24217
|
+
initializeSimpleTelemetryFromOptions: () => initializeSimpleTelemetryFromOptions,
|
|
24218
|
+
truncateForSpan: () => truncateForSpan
|
|
24219
|
+
});
|
|
24027
24220
|
function truncateForSpan(text, maxLen = 4096) {
|
|
24028
24221
|
if (!text || text.length <= maxLen) return text || "";
|
|
24029
24222
|
const half = Math.floor((maxLen - 40) / 2);
|
|
@@ -24032,13 +24225,455 @@ function truncateForSpan(text, maxLen = 4096) {
|
|
|
24032
24225
|
... [${omitted} chars omitted] ...
|
|
24033
24226
|
` + text.substring(text.length - half);
|
|
24034
24227
|
}
|
|
24035
|
-
|
|
24228
|
+
function initializeSimpleTelemetryFromOptions(options) {
|
|
24229
|
+
const telemetry = new SimpleTelemetry({
|
|
24230
|
+
serviceName: "probe-agent",
|
|
24231
|
+
enableFile: options.traceFile !== void 0,
|
|
24232
|
+
enableConsole: options.traceConsole,
|
|
24233
|
+
filePath: options.traceFile || "./traces.jsonl"
|
|
24234
|
+
});
|
|
24235
|
+
patchConsole();
|
|
24236
|
+
return telemetry;
|
|
24237
|
+
}
|
|
24238
|
+
var import_fs, import_path, SimpleTelemetry, SimpleAppTracer;
|
|
24036
24239
|
var init_simpleTelemetry = __esm({
|
|
24037
24240
|
"src/agent/simpleTelemetry.js"() {
|
|
24038
24241
|
"use strict";
|
|
24039
24242
|
import_fs = require("fs");
|
|
24040
24243
|
import_path = require("path");
|
|
24041
24244
|
init_otelLogBridge();
|
|
24245
|
+
SimpleTelemetry = class {
|
|
24246
|
+
constructor(options = {}) {
|
|
24247
|
+
this.serviceName = options.serviceName || "probe-agent";
|
|
24248
|
+
this.enableFile = options.enableFile || false;
|
|
24249
|
+
this.enableConsole = options.enableConsole || false;
|
|
24250
|
+
this.filePath = options.filePath || "./traces.jsonl";
|
|
24251
|
+
this.stream = null;
|
|
24252
|
+
if (this.enableFile) {
|
|
24253
|
+
this.initializeFileExporter();
|
|
24254
|
+
}
|
|
24255
|
+
}
|
|
24256
|
+
initializeFileExporter() {
|
|
24257
|
+
try {
|
|
24258
|
+
const dir = (0, import_path.dirname)(this.filePath);
|
|
24259
|
+
if (!(0, import_fs.existsSync)(dir)) {
|
|
24260
|
+
(0, import_fs.mkdirSync)(dir, { recursive: true });
|
|
24261
|
+
}
|
|
24262
|
+
this.stream = (0, import_fs.createWriteStream)(this.filePath, { flags: "a" });
|
|
24263
|
+
this.stream.on("error", (error40) => {
|
|
24264
|
+
console.error(`[SimpleTelemetry] Stream error: ${error40.message}`);
|
|
24265
|
+
});
|
|
24266
|
+
console.log(`[SimpleTelemetry] File exporter initialized: ${this.filePath}`);
|
|
24267
|
+
} catch (error40) {
|
|
24268
|
+
console.error(`[SimpleTelemetry] Failed to initialize file exporter: ${error40.message}`);
|
|
24269
|
+
}
|
|
24270
|
+
}
|
|
24271
|
+
createSpan(name15, attributes = {}) {
|
|
24272
|
+
const span = {
|
|
24273
|
+
traceId: this.generateTraceId(),
|
|
24274
|
+
spanId: this.generateSpanId(),
|
|
24275
|
+
name: name15,
|
|
24276
|
+
startTime: Date.now(),
|
|
24277
|
+
attributes: { ...attributes, service: this.serviceName },
|
|
24278
|
+
events: [],
|
|
24279
|
+
status: "OK"
|
|
24280
|
+
};
|
|
24281
|
+
return {
|
|
24282
|
+
...span,
|
|
24283
|
+
addEvent: (eventName, eventAttributes = {}) => {
|
|
24284
|
+
span.events.push({
|
|
24285
|
+
name: eventName,
|
|
24286
|
+
time: Date.now(),
|
|
24287
|
+
attributes: eventAttributes
|
|
24288
|
+
});
|
|
24289
|
+
},
|
|
24290
|
+
setAttributes: (attrs) => {
|
|
24291
|
+
Object.assign(span.attributes, attrs);
|
|
24292
|
+
},
|
|
24293
|
+
setStatus: (status) => {
|
|
24294
|
+
span.status = status;
|
|
24295
|
+
},
|
|
24296
|
+
end: () => {
|
|
24297
|
+
span.endTime = Date.now();
|
|
24298
|
+
span.duration = span.endTime - span.startTime;
|
|
24299
|
+
this.exportSpan(span);
|
|
24300
|
+
}
|
|
24301
|
+
};
|
|
24302
|
+
}
|
|
24303
|
+
exportSpan(span) {
|
|
24304
|
+
const spanData = {
|
|
24305
|
+
...span,
|
|
24306
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
24307
|
+
};
|
|
24308
|
+
if (this.enableConsole) {
|
|
24309
|
+
console.log("[Trace]", JSON.stringify(spanData, null, 2));
|
|
24310
|
+
}
|
|
24311
|
+
if (this.enableFile && this.stream) {
|
|
24312
|
+
this.stream.write(JSON.stringify(spanData) + "\n");
|
|
24313
|
+
}
|
|
24314
|
+
}
|
|
24315
|
+
generateTraceId() {
|
|
24316
|
+
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
|
|
24317
|
+
}
|
|
24318
|
+
generateSpanId() {
|
|
24319
|
+
return Math.random().toString(36).substring(2, 10);
|
|
24320
|
+
}
|
|
24321
|
+
async flush() {
|
|
24322
|
+
if (this.stream) {
|
|
24323
|
+
return new Promise((resolve9) => {
|
|
24324
|
+
this.stream.once("drain", resolve9);
|
|
24325
|
+
if (!this.stream.writableNeedDrain) {
|
|
24326
|
+
resolve9();
|
|
24327
|
+
}
|
|
24328
|
+
});
|
|
24329
|
+
}
|
|
24330
|
+
}
|
|
24331
|
+
async shutdown() {
|
|
24332
|
+
if (this.stream) {
|
|
24333
|
+
return new Promise((resolve9) => {
|
|
24334
|
+
this.stream.end(() => {
|
|
24335
|
+
console.log(`[SimpleTelemetry] File stream closed: ${this.filePath}`);
|
|
24336
|
+
resolve9();
|
|
24337
|
+
});
|
|
24338
|
+
});
|
|
24339
|
+
}
|
|
24340
|
+
}
|
|
24341
|
+
};
|
|
24342
|
+
SimpleAppTracer = class {
|
|
24343
|
+
constructor(telemetry, sessionId = null) {
|
|
24344
|
+
this.telemetry = telemetry;
|
|
24345
|
+
this.sessionId = sessionId || this.generateSessionId();
|
|
24346
|
+
}
|
|
24347
|
+
generateSessionId() {
|
|
24348
|
+
return Math.random().toString(36).substring(2, 15);
|
|
24349
|
+
}
|
|
24350
|
+
isEnabled() {
|
|
24351
|
+
return this.telemetry !== null;
|
|
24352
|
+
}
|
|
24353
|
+
createSessionSpan(attributes = {}) {
|
|
24354
|
+
if (!this.isEnabled()) return null;
|
|
24355
|
+
return this.telemetry.createSpan("agent.session", {
|
|
24356
|
+
"session.id": this.sessionId,
|
|
24357
|
+
...attributes
|
|
24358
|
+
});
|
|
24359
|
+
}
|
|
24360
|
+
createAISpan(modelName, provider, attributes = {}) {
|
|
24361
|
+
if (!this.isEnabled()) return null;
|
|
24362
|
+
return this.telemetry.createSpan("ai.request", {
|
|
24363
|
+
"ai.model": modelName,
|
|
24364
|
+
"ai.provider": provider,
|
|
24365
|
+
"session.id": this.sessionId,
|
|
24366
|
+
...attributes
|
|
24367
|
+
});
|
|
24368
|
+
}
|
|
24369
|
+
createToolSpan(toolName, attributes = {}) {
|
|
24370
|
+
if (!this.isEnabled()) return null;
|
|
24371
|
+
return this.telemetry.createSpan("tool.call", {
|
|
24372
|
+
"tool.name": toolName,
|
|
24373
|
+
"session.id": this.sessionId,
|
|
24374
|
+
...attributes
|
|
24375
|
+
});
|
|
24376
|
+
}
|
|
24377
|
+
addEvent(name15, attributes = {}) {
|
|
24378
|
+
if (this.telemetry && this.telemetry.enableConsole) {
|
|
24379
|
+
console.log("[Event]", name15, attributes);
|
|
24380
|
+
}
|
|
24381
|
+
}
|
|
24382
|
+
/**
|
|
24383
|
+
* Record a generic event (used by completionPrompt and other features)
|
|
24384
|
+
*/
|
|
24385
|
+
// visor-disable: SimpleAppTracer uses this.sessionId because it's a per-session instance. AppTracer extracts from attributes because it's a singleton managing multiple sessions. Different architectures require different approaches.
|
|
24386
|
+
recordEvent(name15, attributes = {}) {
|
|
24387
|
+
if (!this.isEnabled()) return;
|
|
24388
|
+
this.addEvent(name15, {
|
|
24389
|
+
"session.id": this.sessionId,
|
|
24390
|
+
...attributes
|
|
24391
|
+
});
|
|
24392
|
+
}
|
|
24393
|
+
/**
|
|
24394
|
+
* Record delegation events
|
|
24395
|
+
*/
|
|
24396
|
+
recordDelegationEvent(eventType, data2 = {}) {
|
|
24397
|
+
if (!this.isEnabled()) return;
|
|
24398
|
+
this.addEvent(`delegation.${eventType}`, {
|
|
24399
|
+
"session.id": this.sessionId,
|
|
24400
|
+
...data2
|
|
24401
|
+
});
|
|
24402
|
+
}
|
|
24403
|
+
/**
|
|
24404
|
+
* Record JSON validation events
|
|
24405
|
+
*/
|
|
24406
|
+
recordJsonValidationEvent(eventType, data2 = {}) {
|
|
24407
|
+
if (!this.isEnabled()) return;
|
|
24408
|
+
this.addEvent(`json_validation.${eventType}`, {
|
|
24409
|
+
"session.id": this.sessionId,
|
|
24410
|
+
...data2
|
|
24411
|
+
});
|
|
24412
|
+
}
|
|
24413
|
+
/**
|
|
24414
|
+
* Record Mermaid validation events
|
|
24415
|
+
*/
|
|
24416
|
+
recordMermaidValidationEvent(eventType, data2 = {}) {
|
|
24417
|
+
if (!this.isEnabled()) return;
|
|
24418
|
+
this.addEvent(`mermaid_validation.${eventType}`, {
|
|
24419
|
+
"session.id": this.sessionId,
|
|
24420
|
+
...data2
|
|
24421
|
+
});
|
|
24422
|
+
}
|
|
24423
|
+
/**
|
|
24424
|
+
* Record task management events
|
|
24425
|
+
*/
|
|
24426
|
+
recordTaskEvent(eventType, data2 = {}) {
|
|
24427
|
+
if (!this.isEnabled()) return;
|
|
24428
|
+
this.addEvent(`task.${eventType}`, {
|
|
24429
|
+
"session.id": this.sessionId,
|
|
24430
|
+
...data2
|
|
24431
|
+
});
|
|
24432
|
+
}
|
|
24433
|
+
/**
|
|
24434
|
+
* Record MCP (Model Context Protocol) events
|
|
24435
|
+
* Tracks server connections, tool discovery, method filtering, and tool execution
|
|
24436
|
+
*/
|
|
24437
|
+
recordMcpEvent(eventType, data2 = {}) {
|
|
24438
|
+
if (!this.isEnabled()) return;
|
|
24439
|
+
this.addEvent(`mcp.${eventType}`, {
|
|
24440
|
+
"session.id": this.sessionId,
|
|
24441
|
+
...data2
|
|
24442
|
+
});
|
|
24443
|
+
}
|
|
24444
|
+
/**
|
|
24445
|
+
* Record bash tool events
|
|
24446
|
+
* Tracks command permission checks, allowed/denied commands, and execution
|
|
24447
|
+
*/
|
|
24448
|
+
recordBashEvent(eventType, data2 = {}) {
|
|
24449
|
+
if (!this.isEnabled()) return;
|
|
24450
|
+
this.addEvent(`bash.${eventType}`, {
|
|
24451
|
+
"session.id": this.sessionId,
|
|
24452
|
+
...data2
|
|
24453
|
+
});
|
|
24454
|
+
}
|
|
24455
|
+
setAttributes(attributes) {
|
|
24456
|
+
if (this.telemetry && this.telemetry.enableConsole) {
|
|
24457
|
+
console.log("[Attributes]", attributes);
|
|
24458
|
+
}
|
|
24459
|
+
}
|
|
24460
|
+
/**
|
|
24461
|
+
* Hash content for deduplication/comparison purposes
|
|
24462
|
+
* @param {string} content - The content to hash
|
|
24463
|
+
* @returns {string} - Hex string hash
|
|
24464
|
+
*/
|
|
24465
|
+
hashContent(content) {
|
|
24466
|
+
let hash2 = 0;
|
|
24467
|
+
const len = Math.min(content.length, 1e3);
|
|
24468
|
+
for (let i = 0; i < len; i++) {
|
|
24469
|
+
hash2 = (hash2 << 5) - hash2 + content.charCodeAt(i);
|
|
24470
|
+
hash2 |= 0;
|
|
24471
|
+
}
|
|
24472
|
+
return hash2.toString(16);
|
|
24473
|
+
}
|
|
24474
|
+
/**
|
|
24475
|
+
* Record a conversation turn (assistant response or tool result)
|
|
24476
|
+
* @param {string} role - The role (assistant, tool_result)
|
|
24477
|
+
* @param {string} content - The turn content
|
|
24478
|
+
* @param {Object} metadata - Additional metadata
|
|
24479
|
+
*/
|
|
24480
|
+
recordConversationTurn(role, content, metadata = {}) {
|
|
24481
|
+
if (!this.isEnabled()) return;
|
|
24482
|
+
this.addEvent(`conversation.turn.${role}`, {
|
|
24483
|
+
"session.id": this.sessionId,
|
|
24484
|
+
"conversation.role": role,
|
|
24485
|
+
"conversation.content": content.substring(0, 1e4),
|
|
24486
|
+
"conversation.content.length": content.length,
|
|
24487
|
+
"conversation.content.hash": this.hashContent(content),
|
|
24488
|
+
...metadata
|
|
24489
|
+
});
|
|
24490
|
+
}
|
|
24491
|
+
/**
|
|
24492
|
+
* Record error events with classification
|
|
24493
|
+
* @param {string} errorType - The type of error (wrapped_tool, unrecognized_tool, no_tool_call, circuit_breaker, etc.)
|
|
24494
|
+
* @param {Object} errorDetails - Error details including message, stack, context
|
|
24495
|
+
*/
|
|
24496
|
+
recordErrorEvent(errorType, errorDetails = {}) {
|
|
24497
|
+
if (!this.isEnabled()) return;
|
|
24498
|
+
this.addEvent(`error.${errorType}`, {
|
|
24499
|
+
"session.id": this.sessionId,
|
|
24500
|
+
"error.type": errorType,
|
|
24501
|
+
"error.message": errorDetails.message?.substring(0, 1e3) || null,
|
|
24502
|
+
"error.stack": errorDetails.stack?.substring(0, 2e3) || null,
|
|
24503
|
+
"error.recoverable": errorDetails.recoverable ?? true,
|
|
24504
|
+
"error.context": JSON.stringify(errorDetails.context || {}).substring(0, 1e3),
|
|
24505
|
+
...Object.fromEntries(
|
|
24506
|
+
Object.entries(errorDetails).filter(([k]) => !["message", "stack", "context", "recoverable"].includes(k)).map(([k, v]) => [`error.${k}`, v])
|
|
24507
|
+
)
|
|
24508
|
+
});
|
|
24509
|
+
}
|
|
24510
|
+
/**
|
|
24511
|
+
* Record AI thinking/reasoning content
|
|
24512
|
+
* @param {string} thinkingContent - The thinking content from AI response
|
|
24513
|
+
* @param {Object} metadata - Additional metadata
|
|
24514
|
+
*/
|
|
24515
|
+
recordThinkingContent(thinkingContent, metadata = {}) {
|
|
24516
|
+
if (!this.isEnabled() || !thinkingContent) return;
|
|
24517
|
+
this.addEvent("ai.thinking", {
|
|
24518
|
+
"session.id": this.sessionId,
|
|
24519
|
+
"ai.thinking.content": thinkingContent.substring(0, 5e4),
|
|
24520
|
+
"ai.thinking.length": thinkingContent.length,
|
|
24521
|
+
"ai.thinking.hash": this.hashContent(thinkingContent),
|
|
24522
|
+
...metadata
|
|
24523
|
+
});
|
|
24524
|
+
}
|
|
24525
|
+
/**
|
|
24526
|
+
* Record AI tool call decision
|
|
24527
|
+
* @param {string} toolName - The tool name AI decided to call
|
|
24528
|
+
* @param {Object} params - The parameters AI provided
|
|
24529
|
+
* @param {Object} metadata - Additional metadata
|
|
24530
|
+
*/
|
|
24531
|
+
recordToolDecision(toolName, params, metadata = {}) {
|
|
24532
|
+
if (!this.isEnabled()) return;
|
|
24533
|
+
this.addEvent("ai.tool_decision", {
|
|
24534
|
+
"session.id": this.sessionId,
|
|
24535
|
+
"ai.tool_decision.name": toolName,
|
|
24536
|
+
"ai.tool_decision.params": JSON.stringify(params || {}).substring(0, 2e3),
|
|
24537
|
+
...metadata
|
|
24538
|
+
});
|
|
24539
|
+
}
|
|
24540
|
+
/**
|
|
24541
|
+
* Record tool result after execution
|
|
24542
|
+
* @param {string} toolName - The tool that was executed
|
|
24543
|
+
* @param {string|Object} result - The tool result
|
|
24544
|
+
* @param {boolean} success - Whether the tool succeeded
|
|
24545
|
+
* @param {number} durationMs - Execution duration in milliseconds
|
|
24546
|
+
* @param {Object} metadata - Additional metadata
|
|
24547
|
+
*/
|
|
24548
|
+
recordToolResult(toolName, result, success2, durationMs, metadata = {}) {
|
|
24549
|
+
if (!this.isEnabled()) return;
|
|
24550
|
+
const resultStr = typeof result === "string" ? result : JSON.stringify(result);
|
|
24551
|
+
this.addEvent("tool.result", {
|
|
24552
|
+
"session.id": this.sessionId,
|
|
24553
|
+
"tool.name": toolName,
|
|
24554
|
+
"tool.result": resultStr.substring(0, 1e4),
|
|
24555
|
+
"tool.result.length": resultStr.length,
|
|
24556
|
+
"tool.result.hash": this.hashContent(resultStr),
|
|
24557
|
+
"tool.duration_ms": durationMs,
|
|
24558
|
+
"tool.success": success2,
|
|
24559
|
+
...metadata
|
|
24560
|
+
});
|
|
24561
|
+
}
|
|
24562
|
+
/**
|
|
24563
|
+
* Record MCP tool execution start
|
|
24564
|
+
* @param {string} toolName - MCP tool name
|
|
24565
|
+
* @param {string} serverName - MCP server name
|
|
24566
|
+
* @param {Object} params - Tool parameters
|
|
24567
|
+
* @param {Object} metadata - Additional metadata
|
|
24568
|
+
*/
|
|
24569
|
+
recordMcpToolStart(toolName, serverName, params, metadata = {}) {
|
|
24570
|
+
if (!this.isEnabled()) return;
|
|
24571
|
+
this.addEvent("mcp.tool.start", {
|
|
24572
|
+
"session.id": this.sessionId,
|
|
24573
|
+
"mcp.tool.name": toolName,
|
|
24574
|
+
"mcp.tool.server": serverName || "unknown",
|
|
24575
|
+
"mcp.tool.params": JSON.stringify(params || {}).substring(0, 2e3),
|
|
24576
|
+
...metadata
|
|
24577
|
+
});
|
|
24578
|
+
}
|
|
24579
|
+
/**
|
|
24580
|
+
* Record MCP tool execution end
|
|
24581
|
+
* @param {string} toolName - MCP tool name
|
|
24582
|
+
* @param {string} serverName - MCP server name
|
|
24583
|
+
* @param {string|Object} result - Tool result
|
|
24584
|
+
* @param {boolean} success - Whether succeeded
|
|
24585
|
+
* @param {number} durationMs - Execution duration
|
|
24586
|
+
* @param {string} errorMessage - Error message if failed
|
|
24587
|
+
* @param {Object} metadata - Additional metadata
|
|
24588
|
+
*/
|
|
24589
|
+
recordMcpToolEnd(toolName, serverName, result, success2, durationMs, errorMessage = null, metadata = {}) {
|
|
24590
|
+
if (!this.isEnabled()) return;
|
|
24591
|
+
const resultStr = typeof result === "string" ? result : JSON.stringify(result || "");
|
|
24592
|
+
this.addEvent("mcp.tool.end", {
|
|
24593
|
+
"session.id": this.sessionId,
|
|
24594
|
+
"mcp.tool.name": toolName,
|
|
24595
|
+
"mcp.tool.server": serverName || "unknown",
|
|
24596
|
+
"mcp.tool.result": resultStr.substring(0, 1e4),
|
|
24597
|
+
"mcp.tool.result.length": resultStr.length,
|
|
24598
|
+
"mcp.tool.duration_ms": durationMs,
|
|
24599
|
+
"mcp.tool.success": success2,
|
|
24600
|
+
"mcp.tool.error": errorMessage,
|
|
24601
|
+
...metadata
|
|
24602
|
+
});
|
|
24603
|
+
}
|
|
24604
|
+
/**
|
|
24605
|
+
* Record iteration lifecycle event
|
|
24606
|
+
* @param {string} eventType - start or end
|
|
24607
|
+
* @param {number} iteration - Iteration number
|
|
24608
|
+
* @param {Object} data - Additional data
|
|
24609
|
+
*/
|
|
24610
|
+
recordIterationEvent(eventType, iteration, data2 = {}) {
|
|
24611
|
+
if (!this.isEnabled()) return;
|
|
24612
|
+
this.addEvent(`iteration.${eventType}`, {
|
|
24613
|
+
"session.id": this.sessionId,
|
|
24614
|
+
"iteration": iteration,
|
|
24615
|
+
...data2
|
|
24616
|
+
});
|
|
24617
|
+
}
|
|
24618
|
+
/**
|
|
24619
|
+
* Record per-turn token breakdown
|
|
24620
|
+
* @param {number} iteration - Iteration number
|
|
24621
|
+
* @param {Object} tokenData - Token metrics
|
|
24622
|
+
*/
|
|
24623
|
+
recordTokenTurn(iteration, tokenData = {}) {
|
|
24624
|
+
if (!this.isEnabled()) return;
|
|
24625
|
+
this.addEvent("tokens.turn", {
|
|
24626
|
+
"session.id": this.sessionId,
|
|
24627
|
+
"iteration": iteration,
|
|
24628
|
+
"tokens.input": tokenData.inputTokens || 0,
|
|
24629
|
+
"tokens.output": tokenData.outputTokens || 0,
|
|
24630
|
+
"tokens.total": (tokenData.inputTokens || 0) + (tokenData.outputTokens || 0),
|
|
24631
|
+
"tokens.cache_read": tokenData.cacheReadTokens || 0,
|
|
24632
|
+
"tokens.cache_write": tokenData.cacheWriteTokens || 0,
|
|
24633
|
+
"tokens.context_used": tokenData.contextTokens || 0,
|
|
24634
|
+
"tokens.context_remaining": tokenData.maxContextTokens ? tokenData.maxContextTokens - (tokenData.contextTokens || 0) : null
|
|
24635
|
+
});
|
|
24636
|
+
}
|
|
24637
|
+
async withSpan(spanName, fn, attributes = {}, onResult = null) {
|
|
24638
|
+
if (!this.isEnabled()) {
|
|
24639
|
+
return fn();
|
|
24640
|
+
}
|
|
24641
|
+
const span = this.telemetry.createSpan(spanName, {
|
|
24642
|
+
"session.id": this.sessionId,
|
|
24643
|
+
...attributes
|
|
24644
|
+
});
|
|
24645
|
+
try {
|
|
24646
|
+
const result = await fn();
|
|
24647
|
+
span.setStatus("OK");
|
|
24648
|
+
if (onResult) {
|
|
24649
|
+
try {
|
|
24650
|
+
onResult(span, result);
|
|
24651
|
+
} catch (_) {
|
|
24652
|
+
}
|
|
24653
|
+
}
|
|
24654
|
+
return result;
|
|
24655
|
+
} catch (error40) {
|
|
24656
|
+
span.setStatus("ERROR");
|
|
24657
|
+
span.addEvent("exception", {
|
|
24658
|
+
"exception.message": error40.message,
|
|
24659
|
+
"exception.stack": error40.stack
|
|
24660
|
+
});
|
|
24661
|
+
throw error40;
|
|
24662
|
+
} finally {
|
|
24663
|
+
span.end();
|
|
24664
|
+
}
|
|
24665
|
+
}
|
|
24666
|
+
async flush() {
|
|
24667
|
+
if (this.telemetry) {
|
|
24668
|
+
await this.telemetry.flush();
|
|
24669
|
+
}
|
|
24670
|
+
}
|
|
24671
|
+
async shutdown() {
|
|
24672
|
+
if (this.telemetry) {
|
|
24673
|
+
await this.telemetry.shutdown();
|
|
24674
|
+
}
|
|
24675
|
+
}
|
|
24676
|
+
};
|
|
24042
24677
|
}
|
|
24043
24678
|
});
|
|
24044
24679
|
|
|
@@ -26267,6 +26902,7 @@ async function delegate({
|
|
|
26267
26902
|
});
|
|
26268
26903
|
let parentAbortHandler;
|
|
26269
26904
|
let parentAbortHardCancelId = null;
|
|
26905
|
+
let raceSettled = false;
|
|
26270
26906
|
const parentAbortPromise = new Promise((_, reject2) => {
|
|
26271
26907
|
if (parentAbortSignal) {
|
|
26272
26908
|
if (parentAbortSignal.aborted) {
|
|
@@ -26275,6 +26911,7 @@ async function delegate({
|
|
|
26275
26911
|
return;
|
|
26276
26912
|
}
|
|
26277
26913
|
parentAbortHandler = () => {
|
|
26914
|
+
if (raceSettled) return;
|
|
26278
26915
|
subagent.triggerGracefulWindDown();
|
|
26279
26916
|
if (debug) {
|
|
26280
26917
|
console.error(`[DELEGATE] Parent abort signal received \u2014 triggered graceful wind-down on subagent ${sessionId}`);
|
|
@@ -26287,6 +26924,7 @@ async function delegate({
|
|
|
26287
26924
|
});
|
|
26288
26925
|
}
|
|
26289
26926
|
parentAbortHardCancelId = setTimeout(() => {
|
|
26927
|
+
if (raceSettled) return;
|
|
26290
26928
|
if (debug) {
|
|
26291
26929
|
console.error(`[DELEGATE] Graceful wind-down deadline expired \u2014 hard cancelling subagent ${sessionId}`);
|
|
26292
26930
|
}
|
|
@@ -26312,6 +26950,7 @@ async function delegate({
|
|
|
26312
26950
|
try {
|
|
26313
26951
|
response = await Promise.race(racers);
|
|
26314
26952
|
} finally {
|
|
26953
|
+
raceSettled = true;
|
|
26315
26954
|
if (parentAbortHandler && parentAbortSignal) {
|
|
26316
26955
|
parentAbortSignal.removeEventListener("abort", parentAbortHandler);
|
|
26317
26956
|
}
|
|
@@ -26352,10 +26991,12 @@ async function delegate({
|
|
|
26352
26991
|
"delegation.success": true
|
|
26353
26992
|
});
|
|
26354
26993
|
if (delegationSpan) {
|
|
26994
|
+
const { truncateForSpan: truncateForSpan2 } = await Promise.resolve().then(() => (init_simpleTelemetry(), simpleTelemetry_exports));
|
|
26355
26995
|
delegationSpan.setAttributes({
|
|
26356
26996
|
"delegation.result.success": true,
|
|
26357
26997
|
"delegation.result.response_length": response.length,
|
|
26358
|
-
"delegation.result.duration_ms": duration3
|
|
26998
|
+
"delegation.result.duration_ms": duration3,
|
|
26999
|
+
"delegation.result": truncateForSpan2(response, 4096)
|
|
26359
27000
|
});
|
|
26360
27001
|
delegationSpan.setStatus({ code: 1 });
|
|
26361
27002
|
delegationSpan.end();
|
|
@@ -26408,9 +27049,13 @@ var init_delegate = __esm({
|
|
|
26408
27049
|
init_ProbeAgent();
|
|
26409
27050
|
DelegationManager = class {
|
|
26410
27051
|
constructor(options = {}) {
|
|
26411
|
-
|
|
26412
|
-
|
|
26413
|
-
|
|
27052
|
+
const parseSafe = (val, fallback) => {
|
|
27053
|
+
const n = parseInt(val, 10);
|
|
27054
|
+
return Number.isNaN(n) ? fallback : n;
|
|
27055
|
+
};
|
|
27056
|
+
this.maxConcurrent = options.maxConcurrent ?? parseSafe(process.env.MAX_CONCURRENT_DELEGATIONS, 3);
|
|
27057
|
+
this.maxPerSession = options.maxPerSession ?? parseSafe(process.env.MAX_DELEGATIONS_PER_SESSION, 10);
|
|
27058
|
+
this.defaultQueueTimeout = options.queueTimeout ?? parseSafe(process.env.DELEGATION_QUEUE_TIMEOUT, 6e4);
|
|
26414
27059
|
this.sessionDelegations = /* @__PURE__ */ new Map();
|
|
26415
27060
|
this.globalActive = 0;
|
|
26416
27061
|
this.waitQueue = [];
|
|
@@ -27248,12 +27893,16 @@ function resolveTargetPath(target, cwd) {
|
|
|
27248
27893
|
}
|
|
27249
27894
|
return filePart + suffix;
|
|
27250
27895
|
}
|
|
27251
|
-
var import_path6, searchSchema, searchAllSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, listFilesSchema, searchFilesSchema, readImageSchema, bashSchema, analyzeAllSchema, executePlanSchema, cleanupExecutePlanSchema, searchDescription, searchDelegateDescription, queryDescription, extractDescription, delegateDescription, analyzeAllDescription;
|
|
27896
|
+
var import_path6, searchDelegateSchema, searchSchema, searchAllSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, listFilesSchema, searchFilesSchema, readImageSchema, bashSchema, analyzeAllSchema, executePlanSchema, cleanupExecutePlanSchema, searchDescription, searchDelegateDescription, queryDescription, extractDescription, delegateDescription, analyzeAllDescription;
|
|
27252
27897
|
var init_common = __esm({
|
|
27253
27898
|
"src/tools/common.js"() {
|
|
27254
27899
|
"use strict";
|
|
27255
27900
|
init_zod();
|
|
27256
27901
|
import_path6 = require("path");
|
|
27902
|
+
searchDelegateSchema = external_exports2.object({
|
|
27903
|
+
query: external_exports2.string().describe('Natural language question about the code (e.g., "How does authentication work?", "Where is the rate limiting middleware?"). Do NOT use keyword syntax \u2014 just describe what you are looking for in plain English. A subagent will handle keyword searches for you.'),
|
|
27904
|
+
path: external_exports2.string().optional().default(".").describe("Path to search in.")
|
|
27905
|
+
});
|
|
27257
27906
|
searchSchema = external_exports2.object({
|
|
27258
27907
|
query: external_exports2.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."),
|
|
27259
27908
|
path: external_exports2.string().optional().default(".").describe('Path to search in. For dependencies use "go:github.com/owner/repo", "js:package_name", or "rust:cargo_name" etc.'),
|
|
@@ -27319,7 +27968,17 @@ var init_common = __esm({
|
|
|
27319
27968
|
clearSessionStore: external_exports2.boolean().optional().default(false).describe("Clear the session store (persisted data across execute_plan calls)")
|
|
27320
27969
|
});
|
|
27321
27970
|
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.';
|
|
27322
|
-
searchDelegateDescription =
|
|
27971
|
+
searchDelegateDescription = `Find where relevant code is located by asking a natural language question. A subagent searches the codebase and returns file locations grouped by relevance, with reasons explaining why each group matters. Use extract() to read the actual code from the returned locations.
|
|
27972
|
+
|
|
27973
|
+
Returns JSON: { "confidence": "high|medium|low", "groups": [{ "reason": "why these files matter", "files": ["path#Symbol", ...] }] }
|
|
27974
|
+
|
|
27975
|
+
IMPORTANT \u2014 each call spawns a subagent (expensive, takes minutes). Be deliberate:
|
|
27976
|
+
- Ask plain English questions about WHERE code is, NOT keyword queries. Good: "How are user sessions extracted from cookies?" Bad: "ctxGetSession OR GetSession"
|
|
27977
|
+
- Each call should explore a DIFFERENT ANGLE of the problem. Don't rephrase \u2014 reframe:
|
|
27978
|
+
Good: 1) "How are sessions extracted from HTTP requests?" 2) "What middleware runs before route handlers?" 3) "How is the session cookie parsed and validated?"
|
|
27979
|
+
Bad: 1) "How does session extraction work?" 2) "Where is the session extracted?" 3) "Find session extraction code" \u2190 same question reworded
|
|
27980
|
+
- If a search returned no useful results, ask about a DIFFERENT part of the system. Think: what upstream/downstream component touches this?
|
|
27981
|
+
- After getting results, use extract() to read the files you need \u2014 search only locates, extract reads.`;
|
|
27323
27982
|
queryDescription = "Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.";
|
|
27324
27983
|
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.";
|
|
27325
27984
|
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.";
|
|
@@ -27443,6 +28102,72 @@ function autoQuoteSearchTerms(query2) {
|
|
|
27443
28102
|
});
|
|
27444
28103
|
return result.join(" ");
|
|
27445
28104
|
}
|
|
28105
|
+
async function checkDelegateDedup(newQuery, previousQueries, model, debug) {
|
|
28106
|
+
if (!model || previousQueries.length === 0) {
|
|
28107
|
+
return { action: "allow", reason: "no previous queries" };
|
|
28108
|
+
}
|
|
28109
|
+
const previousList = previousQueries.map((q, i) => {
|
|
28110
|
+
let line = `${i + 1}. "${q.query}" (path: ${q.path}, found results: ${q.hadResults})`;
|
|
28111
|
+
if (q.reason) line += `
|
|
28112
|
+
Outcome: ${q.reason}`;
|
|
28113
|
+
if (q.groups && q.groups.length > 0) {
|
|
28114
|
+
line += `
|
|
28115
|
+
Found: ${q.groups.map((g) => g.reason).join("; ")}`;
|
|
28116
|
+
}
|
|
28117
|
+
return line;
|
|
28118
|
+
}).join("\n");
|
|
28119
|
+
try {
|
|
28120
|
+
const result = await (0, import_ai.generateText)({
|
|
28121
|
+
model,
|
|
28122
|
+
maxTokens: 150,
|
|
28123
|
+
temperature: 0,
|
|
28124
|
+
prompt: `You decide if a code search query is redundant given previous queries in the same session.
|
|
28125
|
+
|
|
28126
|
+
PREVIOUS QUERIES:
|
|
28127
|
+
${previousList}
|
|
28128
|
+
|
|
28129
|
+
NEW QUERY: "${newQuery}"
|
|
28130
|
+
|
|
28131
|
+
Respond with exactly one line: ACTION|REASON
|
|
28132
|
+
For rewrites: rewrite|REASON|REWRITTEN_QUERY
|
|
28133
|
+
|
|
28134
|
+
BLOCK when:
|
|
28135
|
+
- Same concept, different phrasing: "find X" / "definition of X" / "where is X" / "X implementation" \u2192 all the same
|
|
28136
|
+
- Synonym or narrower term of a previous query: "dedup" \u2192 "duplicate" \u2192 "unique" \u2192 all the same concept
|
|
28137
|
+
- Single generic word that's just a synonym of a previous failed query
|
|
28138
|
+
- Query is trying to brute-force the same concept with different keywords after previous failures
|
|
28139
|
+
|
|
28140
|
+
REWRITE when:
|
|
28141
|
+
- Previous query was too narrow and failed, new query targets the same goal but could use a FUNDAMENTALLY different search strategy (e.g. searching for a caller instead of the function name, or searching the config/registration site instead of the implementation)
|
|
28142
|
+
- Previous query found WRONG results (e.g. found "FallbackManager" when looking for "dedup logic") \u2014 rewrite to target the actual concept more precisely using implementation-level terms
|
|
28143
|
+
|
|
28144
|
+
ALLOW only when:
|
|
28145
|
+
- The new query targets a COMPLETELY DIFFERENT feature, module, or subsystem \u2014 not just a different word for the same thing
|
|
28146
|
+
|
|
28147
|
+
Only BLOCK when you are CERTAIN the queries target the same concept. When uncertain, ALLOW \u2014 a missed dedup is cheaper than blocking a valid search.
|
|
28148
|
+
|
|
28149
|
+
Examples:
|
|
28150
|
+
- Prev: "wrapToolWithEmitter" \u2192 New: "definition of wrapToolWithEmitter" \u2192 block|Same symbol
|
|
28151
|
+
- Prev: "search dedup" (no results) \u2192 New: "dedup" \u2192 block|Synonym of failed query
|
|
28152
|
+
- Prev: "dedup" (no results) \u2192 New: "duplicate" \u2192 block|Synonym of failed query
|
|
28153
|
+
- Prev: "dedup" (no results) \u2192 New: "unique" \u2192 block|Synonym of failed query
|
|
28154
|
+
- Prev: "auth middleware" \u2192 New: "rate limiting" \u2192 allow|Different subsystem
|
|
28155
|
+
- Prev: "search dedup" (no results) \u2192 New: "previousSearches Map" \u2192 rewrite|Searching for implementation detail instead of concept|previousSearches OR searchKey`
|
|
28156
|
+
});
|
|
28157
|
+
const line = result.text.trim().split("\n")[0];
|
|
28158
|
+
const parts = line.split("|");
|
|
28159
|
+
const action = (parts[0] || "").toLowerCase().trim();
|
|
28160
|
+
if (action === "block") {
|
|
28161
|
+
return { action: "block", reason: parts[1]?.trim() || "duplicate query" };
|
|
28162
|
+
} else if (action === "rewrite" && parts[2]) {
|
|
28163
|
+
return { action: "rewrite", reason: parts[1]?.trim() || "refined query", rewritten: parts[2].trim() };
|
|
28164
|
+
}
|
|
28165
|
+
return { action: "allow", reason: parts[1]?.trim() || "new concept" };
|
|
28166
|
+
} catch (err) {
|
|
28167
|
+
if (debug) console.error("[DEDUP-LLM] Error:", err.message);
|
|
28168
|
+
return { action: "allow", reason: "dedup check failed, allowing" };
|
|
28169
|
+
}
|
|
28170
|
+
}
|
|
27446
28171
|
function normalizeTargets(targets) {
|
|
27447
28172
|
if (!Array.isArray(targets)) return [];
|
|
27448
28173
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -27505,8 +28230,8 @@ function fallbackTargetsFromText(text) {
|
|
|
27505
28230
|
}
|
|
27506
28231
|
return candidates;
|
|
27507
28232
|
}
|
|
27508
|
-
function
|
|
27509
|
-
if (!rawResponse || typeof rawResponse !== "string") return
|
|
28233
|
+
function parseDelegatedResponse(rawResponse) {
|
|
28234
|
+
if (!rawResponse || typeof rawResponse !== "string") return null;
|
|
27510
28235
|
const trimmed = rawResponse.trim();
|
|
27511
28236
|
const tryParse = (text) => {
|
|
27512
28237
|
try {
|
|
@@ -27523,14 +28248,37 @@ function parseDelegatedTargets(rawResponse) {
|
|
|
27523
28248
|
}
|
|
27524
28249
|
}
|
|
27525
28250
|
if (parsed) {
|
|
27526
|
-
if (Array.isArray(parsed)) {
|
|
27527
|
-
return
|
|
28251
|
+
if (Array.isArray(parsed.groups)) {
|
|
28252
|
+
return {
|
|
28253
|
+
confidence: parsed.confidence || "medium",
|
|
28254
|
+
reason: parsed.reason || "",
|
|
28255
|
+
groups: parsed.groups.map((g) => ({
|
|
28256
|
+
reason: g.reason || "",
|
|
28257
|
+
files: normalizeTargets(g.files || [])
|
|
28258
|
+
})).filter((g) => g.files.length > 0),
|
|
28259
|
+
searches: Array.isArray(parsed.searches) ? parsed.searches : []
|
|
28260
|
+
};
|
|
27528
28261
|
}
|
|
27529
28262
|
if (Array.isArray(parsed.targets)) {
|
|
27530
|
-
|
|
28263
|
+
const files2 = normalizeTargets(parsed.targets);
|
|
28264
|
+
if (files2.length > 0) {
|
|
28265
|
+
return { confidence: "medium", reason: "", groups: [{ reason: "Search results", files: files2 }], searches: [] };
|
|
28266
|
+
}
|
|
28267
|
+
return null;
|
|
27531
28268
|
}
|
|
28269
|
+
if (Array.isArray(parsed)) {
|
|
28270
|
+
const files2 = normalizeTargets(parsed);
|
|
28271
|
+
if (files2.length > 0) {
|
|
28272
|
+
return { confidence: "medium", reason: "", groups: [{ reason: "Search results", files: files2 }], searches: [] };
|
|
28273
|
+
}
|
|
28274
|
+
return null;
|
|
28275
|
+
}
|
|
28276
|
+
}
|
|
28277
|
+
const files = normalizeTargets(fallbackTargetsFromText(trimmed));
|
|
28278
|
+
if (files.length > 0) {
|
|
28279
|
+
return { confidence: "low", reason: "", groups: [{ reason: "Search results", files }], searches: [] };
|
|
27532
28280
|
}
|
|
27533
|
-
return
|
|
28281
|
+
return null;
|
|
27534
28282
|
}
|
|
27535
28283
|
function splitTargetSuffix(target) {
|
|
27536
28284
|
const searchStart = target.length > 2 && target[1] === ":" && /[a-zA-Z]/.test(target[0]) ? 2 : 0;
|
|
@@ -27544,129 +28292,78 @@ function splitTargetSuffix(target) {
|
|
|
27544
28292
|
return { filePart: target, suffix: "" };
|
|
27545
28293
|
}
|
|
27546
28294
|
function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, allowTests }) {
|
|
27547
|
-
return
|
|
27548
|
-
|
|
27549
|
-
|
|
27550
|
-
|
|
27551
|
-
|
|
27552
|
-
|
|
27553
|
-
|
|
27554
|
-
|
|
27555
|
-
|
|
27556
|
-
|
|
27557
|
-
|
|
27558
|
-
|
|
27559
|
-
|
|
27560
|
-
|
|
27561
|
-
|
|
27562
|
-
|
|
27563
|
-
|
|
27564
|
-
|
|
27565
|
-
|
|
27566
|
-
|
|
27567
|
-
|
|
27568
|
-
|
|
27569
|
-
|
|
27570
|
-
|
|
27571
|
-
|
|
27572
|
-
|
|
27573
|
-
|
|
27574
|
-
|
|
27575
|
-
|
|
27576
|
-
|
|
27577
|
-
|
|
27578
|
-
|
|
27579
|
-
|
|
27580
|
-
|
|
27581
|
-
|
|
27582
|
-
|
|
27583
|
-
|
|
27584
|
-
|
|
27585
|
-
|
|
27586
|
-
|
|
27587
|
-
|
|
27588
|
-
|
|
27589
|
-
|
|
27590
|
-
|
|
27591
|
-
|
|
27592
|
-
|
|
27593
|
-
|
|
27594
|
-
|
|
27595
|
-
|
|
27596
|
-
|
|
27597
|
-
|
|
27598
|
-
|
|
27599
|
-
|
|
27600
|
-
|
|
27601
|
-
|
|
27602
|
-
|
|
27603
|
-
|
|
27604
|
-
|
|
27605
|
-
|
|
27606
|
-
|
|
27607
|
-
|
|
27608
|
-
|
|
27609
|
-
|
|
27610
|
-
|
|
27611
|
-
|
|
27612
|
-
|
|
27613
|
-
|
|
27614
|
-
|
|
27615
|
-
|
|
27616
|
-
|
|
27617
|
-
|
|
27618
|
-
|
|
27619
|
-
"- Common programming keywords are filtered as stopwords when unquoted: function, class, return, new, struct, impl, var, let, const, etc.",
|
|
27620
|
-
'- Avoid searching for these alone \u2014 combine with a specific term (e.g., "middleware function" is fine, "function" alone is too generic).',
|
|
27621
|
-
'- To bypass stopword filtering: wrap terms in quotes ("return", "struct") or set exact=true. Both disable stemming and splitting too.',
|
|
27622
|
-
'- camelCase terms are split: getUserData becomes "get", "user", "data" \u2014 so one search covers all naming styles.',
|
|
27623
|
-
'- Do NOT search for full function signatures like "func (r *Type) Method(args)". Just search for the method name with exact=true.',
|
|
27624
|
-
'- Do NOT search for file names (e.g., "sliding_log.go"). Use listFiles to discover files by name.',
|
|
27625
|
-
"",
|
|
27626
|
-
"PAGINATION:",
|
|
27627
|
-
"- Search results are paginated (~20k tokens per page).",
|
|
27628
|
-
"- If your search returned relevant files, call the same query with nextPage=true to check for more.",
|
|
27629
|
-
'- Keep paginating while results stay relevant. Stop when results are off-topic or "All results retrieved".',
|
|
27630
|
-
"",
|
|
27631
|
-
"WHEN TO STOP:",
|
|
27632
|
-
"- After you have explored the main concept AND related subsystems.",
|
|
27633
|
-
"- Once you have 5-15 targets covering different aspects of the query.",
|
|
27634
|
-
'- If you get a "DUPLICATE SEARCH BLOCKED" message, do NOT rephrase the same query \u2014 try a FUNDAMENTALLY different approach:',
|
|
27635
|
-
" * Switch between exact=true and exact=false",
|
|
27636
|
-
" * Search for a broader term and filter results manually",
|
|
27637
|
-
" * Use listFiles to browse the directory structure directly",
|
|
27638
|
-
" * Look for related/surrounding patterns instead of the exact string",
|
|
27639
|
-
"- If 2-3 genuinely different search approaches fail, STOP and report what you tried and why it failed.",
|
|
27640
|
-
" Do NOT keep trying variations of the same failing concept.",
|
|
27641
|
-
"",
|
|
27642
|
-
"Strategy:",
|
|
27643
|
-
"1. Analyze the query \u2014 identify key concepts, then brainstorm SYNONYMS and alternative terms for each.",
|
|
27644
|
-
' Code naming often differs from the concept: "authentication" \u2192 verify, credentials, login, auth;',
|
|
27645
|
-
' "rate limiting" \u2192 throttle, quota, limiter, bucket; "error handling" \u2192 catch, recover, panic.',
|
|
27646
|
-
" Think about what a developer would NAME the function/struct/variable, not just the concept.",
|
|
27647
|
-
"2. Run INDEPENDENT searches in PARALLEL \u2014 search for the main concept AND synonyms simultaneously.",
|
|
27648
|
-
" After each search, check if results are relevant. If yes, call nextPage=true for more results.",
|
|
27649
|
-
`3. Combine related symbols into OR searches: '"symbolA" OR "symbolB"' finds files with either.`,
|
|
27650
|
-
"4. For known symbol names use exact=true. For concepts use default (exact=false).",
|
|
27651
|
-
"5. After your first round of searches, READ the extracted code and look for connected code:",
|
|
27652
|
-
" - Function calls to other important functions \u2192 include those targets.",
|
|
27653
|
-
" - Type references and imports \u2192 include type definitions.",
|
|
27654
|
-
" - Registered handlers/middleware \u2192 include all registered items.",
|
|
27655
|
-
"6. If a search returns results, use extract to verify relevance. Run multiple extracts in parallel too.",
|
|
27656
|
-
"7. If a search returns NO results, the term does not exist. Do NOT retry with variations. Move on.",
|
|
27657
|
-
"8. Once you have enough targets (typically 5-15), output your final JSON answer immediately.",
|
|
27658
|
-
"",
|
|
27659
|
-
`Query: ${searchQuery}`,
|
|
27660
|
-
`Search path(s): ${searchPath}`,
|
|
27661
|
-
`Options: exact=${exact ? "true" : "false"}, language=${language || "auto"}, allow_tests=${allowTests ? "true" : "false"}.`,
|
|
27662
|
-
"",
|
|
27663
|
-
'Return ONLY valid JSON: {"targets": ["path/to/file.ext#Symbol", "path/to/file.ext:line", "path/to/file.ext:start-end"]}',
|
|
27664
|
-
'IMPORTANT: Use ABSOLUTE file paths in targets (e.g., "/full/path/to/file.ext#Symbol"). If you only have relative paths, make them relative to the search path above.',
|
|
27665
|
-
"Prefer #Symbol when a function/class name is clear; otherwise use line numbers.",
|
|
27666
|
-
"Deduplicate targets. Do NOT explain or answer - ONLY return the JSON targets.",
|
|
27667
|
-
"",
|
|
27668
|
-
"Remember: if your search returned relevant results, use nextPage=true to check for more before outputting."
|
|
27669
|
-
].join("\n");
|
|
28295
|
+
return `<role>
|
|
28296
|
+
You are a code-location subagent. Your job is to find WHERE relevant code lives for the given question.
|
|
28297
|
+
You are NOT answering the question \u2014 you are finding the code locations that would help answer it.
|
|
28298
|
+
</role>
|
|
28299
|
+
|
|
28300
|
+
<task>
|
|
28301
|
+
<question>${searchQuery}</question>
|
|
28302
|
+
<search-path>${searchPath}</search-path>
|
|
28303
|
+
<options language="${language || "auto"}" allow_tests="${allowTests ? "true" : "false"}" />
|
|
28304
|
+
</task>
|
|
28305
|
+
|
|
28306
|
+
<tools>
|
|
28307
|
+
<tool name="search">
|
|
28308
|
+
Find code matching keywords or patterns. Results are paginated \u2014 use nextPage=true when results are relevant to get more.
|
|
28309
|
+
</tool>
|
|
28310
|
+
<tool name="extract">
|
|
28311
|
+
Read code to verify a file is actually relevant before including it.
|
|
28312
|
+
</tool>
|
|
28313
|
+
<tool name="listFiles">
|
|
28314
|
+
Browse directory structure to discover where code might live.
|
|
28315
|
+
</tool>
|
|
28316
|
+
</tools>
|
|
28317
|
+
|
|
28318
|
+
<search-engine-behavior>
|
|
28319
|
+
- Probe handles stemming, case-insensitive matching, and camelCase/snake_case splitting automatically.
|
|
28320
|
+
- "allowed_ips" ALREADY matches "AllowedIPs", "allowedIps", etc. Do NOT try case/style variations.
|
|
28321
|
+
- NEVER repeat the same search query \u2014 you will get the same results.
|
|
28322
|
+
- If a search returns no results at workspace root, the term does not exist. Move on.
|
|
28323
|
+
- If a search returns no results in a subfolder, try the workspace root or a different directory.
|
|
28324
|
+
- Use exact=true for known symbol names. Use default for conceptual/exploratory queries.
|
|
28325
|
+
- Combine related symbols with OR: "SymbolA" OR "SymbolB" finds files with either.
|
|
28326
|
+
- Run INDEPENDENT searches in PARALLEL \u2014 do not wait between unrelated searches.
|
|
28327
|
+
</search-engine-behavior>
|
|
28328
|
+
|
|
28329
|
+
<strategy>
|
|
28330
|
+
1. Analyze the question \u2014 identify key concepts and brainstorm what a developer would NAME the relevant code.
|
|
28331
|
+
2. Start your first search with the FULL search-path provided above. Do NOT narrow to a subdirectory on first try \u2014 the code may live anywhere in the tree.
|
|
28332
|
+
3. Search for the main concept and synonyms in parallel.
|
|
28333
|
+
4. Use extract to verify relevance \u2014 skim the code to confirm it ACTUALLY relates to the question.
|
|
28334
|
+
5. Follow the trail: if you find a function, look for its callers, type definitions, and registered handlers.
|
|
28335
|
+
6. Group your findings by WHY they are relevant (not by how you found them).
|
|
28336
|
+
</strategy>
|
|
28337
|
+
|
|
28338
|
+
<relevance-filtering priority="critical">
|
|
28339
|
+
- Only include files you have VERIFIED are relevant by reading them with extract.
|
|
28340
|
+
- Do NOT include files just because they matched a keyword \u2014 confirm the match is meaningful.
|
|
28341
|
+
- A file that mentions "session" in a comment is NOT relevant to "How do sessions work?" \u2014 look for the actual implementation.
|
|
28342
|
+
- Fewer verified-relevant files are far more valuable than many unverified keyword matches.
|
|
28343
|
+
- If a file is tangentially related but not core to the question, leave it out.
|
|
28344
|
+
- If NO files are truly relevant, return EMPTY groups with confidence "low". An honest empty result is far better than a wrong result. Never fill groups with loosely related files just to have something.
|
|
28345
|
+
</relevance-filtering>
|
|
28346
|
+
|
|
28347
|
+
<stop-conditions>
|
|
28348
|
+
- Once you have found locations covering the main concept and related subsystems.
|
|
28349
|
+
- If 2-3 different search approaches fail, stop and report what you have.
|
|
28350
|
+
- Do NOT keep trying quote/syntax variations of the same failing keyword.
|
|
28351
|
+
</stop-conditions>
|
|
28352
|
+
|
|
28353
|
+
<on-iteration-limit>
|
|
28354
|
+
If you run out of tool iterations, you MUST still output your JSON response with whatever you found so far.
|
|
28355
|
+
Set confidence to "low" if your search was incomplete.
|
|
28356
|
+
Include ALL files you verified as relevant, even if coverage is partial.
|
|
28357
|
+
The "searches" field helps the caller understand what was attempted.
|
|
28358
|
+
</on-iteration-limit>
|
|
28359
|
+
|
|
28360
|
+
<output-rules>
|
|
28361
|
+
- Return ONLY valid JSON matching the schema. No markdown, no explanation.
|
|
28362
|
+
- ONLY include files you have verified are relevant. No noise.
|
|
28363
|
+
- Group files by RELEVANCE to the question, not by search query.
|
|
28364
|
+
- Use ABSOLUTE file paths. Prefer #Symbol for functions/classes; otherwise use line ranges.
|
|
28365
|
+
- Deduplicate files across groups.
|
|
28366
|
+
</output-rules>`;
|
|
27670
28367
|
}
|
|
27671
28368
|
var import_ai, import_fs5, CODE_SEARCH_SCHEMA, searchTool, queryTool, extractTool, delegateTool, analyzeAllTool;
|
|
27672
28369
|
var init_vercel = __esm({
|
|
@@ -27682,17 +28379,54 @@ var init_vercel = __esm({
|
|
|
27682
28379
|
import_fs5 = require("fs");
|
|
27683
28380
|
init_error_types();
|
|
27684
28381
|
init_hashline();
|
|
28382
|
+
init_provider();
|
|
27685
28383
|
init_simpleTelemetry();
|
|
27686
28384
|
CODE_SEARCH_SCHEMA = {
|
|
27687
28385
|
type: "object",
|
|
27688
28386
|
properties: {
|
|
27689
|
-
|
|
28387
|
+
confidence: {
|
|
28388
|
+
type: "string",
|
|
28389
|
+
enum: ["high", "medium", "low"],
|
|
28390
|
+
description: "How confident you are that these locations answer the question."
|
|
28391
|
+
},
|
|
28392
|
+
reason: {
|
|
28393
|
+
type: "string",
|
|
28394
|
+
description: "Brief explanation of confidence level \u2014 what was found, partially found, or not found."
|
|
28395
|
+
},
|
|
28396
|
+
groups: {
|
|
27690
28397
|
type: "array",
|
|
27691
|
-
items: {
|
|
27692
|
-
|
|
28398
|
+
items: {
|
|
28399
|
+
type: "object",
|
|
28400
|
+
properties: {
|
|
28401
|
+
reason: {
|
|
28402
|
+
type: "string",
|
|
28403
|
+
description: "Why these files are relevant \u2014 what aspect of the question they address (not how the code works)."
|
|
28404
|
+
},
|
|
28405
|
+
files: {
|
|
28406
|
+
type: "array",
|
|
28407
|
+
items: { type: "string" },
|
|
28408
|
+
description: 'File targets like "path/to/file.ext#Symbol" or "path/to/file.ext:10-20".'
|
|
28409
|
+
}
|
|
28410
|
+
},
|
|
28411
|
+
required: ["reason", "files"]
|
|
28412
|
+
},
|
|
28413
|
+
description: "Groups of related files, each with a reason explaining why they matter."
|
|
28414
|
+
},
|
|
28415
|
+
searches: {
|
|
28416
|
+
type: "array",
|
|
28417
|
+
items: {
|
|
28418
|
+
type: "object",
|
|
28419
|
+
properties: {
|
|
28420
|
+
query: { type: "string", description: "The search query used." },
|
|
28421
|
+
path: { type: "string", description: "The path searched in." },
|
|
28422
|
+
had_results: { type: "boolean", description: "Whether the search returned any results." }
|
|
28423
|
+
},
|
|
28424
|
+
required: ["query", "path", "had_results"]
|
|
28425
|
+
},
|
|
28426
|
+
description: "All search queries executed during this session, with their paths and outcomes."
|
|
27693
28427
|
}
|
|
27694
28428
|
},
|
|
27695
|
-
required: ["
|
|
28429
|
+
required: ["confidence", "reason", "groups", "searches"],
|
|
27696
28430
|
additionalProperties: false
|
|
27697
28431
|
};
|
|
27698
28432
|
searchTool = (options = {}) => {
|
|
@@ -27713,11 +28447,20 @@ var init_vercel = __esm({
|
|
|
27713
28447
|
const previousSearches = /* @__PURE__ */ new Map();
|
|
27714
28448
|
const dupBlockCounts = /* @__PURE__ */ new Map();
|
|
27715
28449
|
const paginationCounts = /* @__PURE__ */ new Map();
|
|
28450
|
+
let consecutiveNoResults = 0;
|
|
28451
|
+
const MAX_CONSECUTIVE_NO_RESULTS = 4;
|
|
28452
|
+
const failedConcepts = /* @__PURE__ */ new Map();
|
|
27716
28453
|
const MAX_PAGES_PER_QUERY = 3;
|
|
28454
|
+
const previousDelegations = [];
|
|
28455
|
+
let cachedDedupModel = void 0;
|
|
28456
|
+
function normalizeQueryConcept(query2) {
|
|
28457
|
+
if (!query2) return "";
|
|
28458
|
+
return query2.replace(/^["']|["']$/g, "").replace(/^(definition\s+of|implementation\s+of|usage\s+of|find|where\s+is|how\s+does|locate|show\s+me|get|look\s+for)\s+/i, "").replace(/^["']|["']$/g, "").replace(/\./g, "").replace(/[_\-\s]+/g, "").toLowerCase().trim();
|
|
28459
|
+
}
|
|
27717
28460
|
return (0, import_ai.tool)({
|
|
27718
28461
|
name: "search",
|
|
27719
28462
|
description: searchDelegate ? searchDelegateDescription : searchDescription,
|
|
27720
|
-
inputSchema: searchSchema,
|
|
28463
|
+
inputSchema: searchDelegate ? searchDelegateSchema : searchSchema,
|
|
27721
28464
|
execute: async ({ query: searchQuery, path: path9, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage, workingDirectory }) => {
|
|
27722
28465
|
if (!exact && searchQuery) {
|
|
27723
28466
|
const originalQuery = searchQuery;
|
|
@@ -27762,7 +28505,8 @@ var init_vercel = __esm({
|
|
|
27762
28505
|
return await search(searchOptions);
|
|
27763
28506
|
};
|
|
27764
28507
|
if (!searchDelegate) {
|
|
27765
|
-
const searchKey = `${searchPath}::${searchQuery}::${exact || false}`;
|
|
28508
|
+
const searchKey = `${searchPath}::${searchQuery}::${exact || false}::${language || ""}`;
|
|
28509
|
+
let circuitBreakerWarning = "";
|
|
27766
28510
|
if (!nextPage) {
|
|
27767
28511
|
if (previousSearches.has(searchKey)) {
|
|
27768
28512
|
const blockCount = (dupBlockCounts.get(searchKey) || 0) + 1;
|
|
@@ -27782,6 +28526,35 @@ var init_vercel = __esm({
|
|
|
27782
28526
|
}
|
|
27783
28527
|
previousSearches.set(searchKey, { hadResults: false });
|
|
27784
28528
|
paginationCounts.set(searchKey, 0);
|
|
28529
|
+
const normalizedKey = `${searchPath}::${normalizeQueryConcept(searchQuery)}`;
|
|
28530
|
+
if (failedConcepts.has(normalizedKey) && failedConcepts.get(normalizedKey) >= 2) {
|
|
28531
|
+
const conceptCount = failedConcepts.get(normalizedKey) + 1;
|
|
28532
|
+
failedConcepts.set(normalizedKey, conceptCount);
|
|
28533
|
+
if (debug) {
|
|
28534
|
+
console.error(`[CONCEPT-DEDUP] Blocked variation of failed concept (${conceptCount}x): "${searchQuery}" normalized to "${normalizeQueryConcept(searchQuery)}"`);
|
|
28535
|
+
}
|
|
28536
|
+
const isSubfolder = path9 && path9 !== effectiveSearchCwd && path9 !== ".";
|
|
28537
|
+
const scopeHint = isSubfolder ? `
|
|
28538
|
+
- Try searching from the workspace root (omit the path parameter) \u2014 the term may exist in a different directory` : `
|
|
28539
|
+
- The term does not exist in this codebase at any path`;
|
|
28540
|
+
return `CONCEPT ALREADY FAILED (${conceptCount} variations tried). You already searched for "${normalizeQueryConcept(searchQuery)}" with different quoting/syntax in this path and got NO results each time. Changing quotes, adding "func" prefix, or switching to method syntax will NOT change the results.
|
|
28541
|
+
|
|
28542
|
+
Change your strategy:${scopeHint}
|
|
28543
|
+
- Use extract on a file you ALREADY found to read actual code and discover real function/type names
|
|
28544
|
+
- Use listFiles to browse directories and find what functions actually exist
|
|
28545
|
+
- Search for a BROADER concept (e.g., instead of "ctxGetData", try "context" or "middleware data access")
|
|
28546
|
+
- If you have enough information from prior searches, provide your final answer NOW`;
|
|
28547
|
+
}
|
|
28548
|
+
if (consecutiveNoResults >= MAX_CONSECUTIVE_NO_RESULTS) {
|
|
28549
|
+
if (debug) {
|
|
28550
|
+
console.error(`[CIRCUIT-BREAKER] ${consecutiveNoResults} consecutive no-result searches, warning: "${searchQuery}"`);
|
|
28551
|
+
}
|
|
28552
|
+
const isSubfolderCB = path9 && path9 !== effectiveSearchCwd && path9 !== ".";
|
|
28553
|
+
const cbScopeHint = isSubfolderCB ? ` You have been searching in "${path9}" \u2014 consider searching from the workspace root or a different directory.` : "";
|
|
28554
|
+
circuitBreakerWarning = `
|
|
28555
|
+
|
|
28556
|
+
\u26A0\uFE0F CIRCUIT BREAKER: Your last ${consecutiveNoResults} searches ALL returned no results.${cbScopeHint} You MUST change your approach: use extract on files you already found, use listFiles to browse directories, or provide your final answer. Guessing names will not help.`;
|
|
28557
|
+
}
|
|
27785
28558
|
} else {
|
|
27786
28559
|
const pageCount = (paginationCounts.get(searchKey) || 0) + 1;
|
|
27787
28560
|
paginationCounts.set(searchKey, pageCount);
|
|
@@ -27795,10 +28568,24 @@ var init_vercel = __esm({
|
|
|
27795
28568
|
try {
|
|
27796
28569
|
const result = maybeAnnotate(await runRawSearch());
|
|
27797
28570
|
if (typeof result === "string" && result.includes("No results found")) {
|
|
28571
|
+
consecutiveNoResults++;
|
|
28572
|
+
const normalizedKey = `${searchPath}::${normalizeQueryConcept(searchQuery)}`;
|
|
28573
|
+
failedConcepts.set(normalizedKey, (failedConcepts.get(normalizedKey) || 0) + 1);
|
|
28574
|
+
if (debug) {
|
|
28575
|
+
console.error(`[NO-RESULTS] consecutiveNoResults=${consecutiveNoResults}, concept "${normalizeQueryConcept(searchQuery)}" failed ${failedConcepts.get(normalizedKey)}x`);
|
|
28576
|
+
}
|
|
27798
28577
|
if (/^[A-Z]+-\d+$/.test(searchQuery.trim()) || /^[A-Z]+-\d+$/.test(searchQuery.replace(/"/g, "").trim())) {
|
|
27799
|
-
return result + "\n\n\u26A0\uFE0F Your query looks like a ticket/issue ID (e.g., JIRA-1234). Ticket IDs are rarely present in source code. Search for the technical concepts described in the ticket instead (e.g., function names, error messages, variable names).";
|
|
28578
|
+
return result + "\n\n\u26A0\uFE0F Your query looks like a ticket/issue ID (e.g., JIRA-1234). Ticket IDs are rarely present in source code. Search for the technical concepts described in the ticket instead (e.g., function names, error messages, variable names)." + circuitBreakerWarning;
|
|
28579
|
+
}
|
|
28580
|
+
if (consecutiveNoResults >= MAX_CONSECUTIVE_NO_RESULTS - 1 && !circuitBreakerWarning) {
|
|
28581
|
+
const isSubfolderWarn = path9 && path9 !== effectiveSearchCwd && path9 !== ".";
|
|
28582
|
+
const warnScopeHint = isSubfolderWarn ? ` You are searching in "${path9}" \u2014 consider searching from the workspace root or a different directory.` : "";
|
|
28583
|
+
return result + `
|
|
28584
|
+
|
|
28585
|
+
\u26A0\uFE0F WARNING: ${consecutiveNoResults} consecutive searches returned no results.${warnScopeHint} Before your next action: use extract on a file you already found to read actual code, or use listFiles to discover what functions really exist. One more failed search will trigger the circuit breaker.`;
|
|
27800
28586
|
}
|
|
27801
28587
|
} else if (typeof result === "string") {
|
|
28588
|
+
consecutiveNoResults = 0;
|
|
27802
28589
|
const entry = previousSearches.get(searchKey);
|
|
27803
28590
|
if (entry) entry.hadResults = true;
|
|
27804
28591
|
}
|
|
@@ -27806,7 +28593,7 @@ var init_vercel = __esm({
|
|
|
27806
28593
|
options.fileTracker.trackFilesFromOutput(result, effectiveSearchCwd).catch(() => {
|
|
27807
28594
|
});
|
|
27808
28595
|
}
|
|
27809
|
-
return result;
|
|
28596
|
+
return typeof result === "string" ? result + circuitBreakerWarning : result;
|
|
27810
28597
|
} catch (error40) {
|
|
27811
28598
|
console.error("Error executing search command:", error40);
|
|
27812
28599
|
const formatted = formatErrorForAI(error40);
|
|
@@ -27816,12 +28603,58 @@ var init_vercel = __esm({
|
|
|
27816
28603
|
return formatted;
|
|
27817
28604
|
}
|
|
27818
28605
|
}
|
|
28606
|
+
const delegatePath = searchPath || "";
|
|
28607
|
+
let effectiveQuery = searchQuery;
|
|
28608
|
+
if (previousDelegations.length > 0) {
|
|
28609
|
+
if (cachedDedupModel === void 0) {
|
|
28610
|
+
const dedupProvider = options.searchDelegateProvider || process.env.PROBE_SEARCH_DELEGATE_PROVIDER || options.provider || process.env.FORCE_PROVIDER || null;
|
|
28611
|
+
const dedupModelName = options.searchDelegateModel || process.env.PROBE_SEARCH_DELEGATE_MODEL || options.model || process.env.MODEL_NAME || null;
|
|
28612
|
+
if (debug) {
|
|
28613
|
+
console.error(`[DEDUP-LLM] Creating model: provider=${dedupProvider}, model=${dedupModelName}`);
|
|
28614
|
+
}
|
|
28615
|
+
cachedDedupModel = await createLanguageModel(dedupProvider, dedupModelName);
|
|
28616
|
+
if (debug) {
|
|
28617
|
+
console.error(`[DEDUP-LLM] Model created: ${cachedDedupModel ? "success" : "null"}`);
|
|
28618
|
+
}
|
|
28619
|
+
}
|
|
28620
|
+
const dedupSpanAttrs = {
|
|
28621
|
+
"dedup.query": searchQuery,
|
|
28622
|
+
"dedup.previous_count": String(previousDelegations.length),
|
|
28623
|
+
"dedup.previous_queries": previousDelegations.map((d) => d.query).join(" | ")
|
|
28624
|
+
};
|
|
28625
|
+
const dedup = options.tracer?.withSpan ? await options.tracer.withSpan("search.delegate.dedup", async () => {
|
|
28626
|
+
return await checkDelegateDedup(searchQuery, previousDelegations, cachedDedupModel, debug);
|
|
28627
|
+
}, dedupSpanAttrs, (span, result) => {
|
|
28628
|
+
span.setAttributes({
|
|
28629
|
+
"dedup.action": result.action,
|
|
28630
|
+
"dedup.reason": result.reason || "",
|
|
28631
|
+
"dedup.rewritten": result.rewritten || ""
|
|
28632
|
+
});
|
|
28633
|
+
}) : await checkDelegateDedup(searchQuery, previousDelegations, cachedDedupModel, debug);
|
|
28634
|
+
if (debug) {
|
|
28635
|
+
console.error(`[DEDUP-LLM] Query: "${searchQuery}" \u2192 ${dedup.action}: ${dedup.reason}${dedup.rewritten ? ` \u2192 "${dedup.rewritten}"` : ""}`);
|
|
28636
|
+
}
|
|
28637
|
+
if (dedup.action === "block") {
|
|
28638
|
+
const prevQueries = previousDelegations.map((d) => `"${d.query}"`).join(", ");
|
|
28639
|
+
return `DELEGATE BLOCKED: "${searchQuery}" is semantically duplicate of previous delegation(s) [${prevQueries}]. ${dedup.reason}
|
|
28640
|
+
|
|
28641
|
+
Do NOT re-delegate the same concept. Use extract() on files already found, or synthesize your answer from existing results.`;
|
|
28642
|
+
}
|
|
28643
|
+
if (dedup.action === "rewrite" && dedup.rewritten) {
|
|
28644
|
+
effectiveQuery = dedup.rewritten;
|
|
28645
|
+
if (debug) {
|
|
28646
|
+
console.error(`[DEDUP-LLM] Rewritten query: "${searchQuery}" \u2192 "${effectiveQuery}"`);
|
|
28647
|
+
}
|
|
28648
|
+
}
|
|
28649
|
+
}
|
|
28650
|
+
const delegationRecord = { query: effectiveQuery, path: delegatePath, hadResults: false };
|
|
28651
|
+
previousDelegations.push(delegationRecord);
|
|
27819
28652
|
try {
|
|
27820
28653
|
if (debug) {
|
|
27821
|
-
console.error(`Delegating search with query: "${
|
|
28654
|
+
console.error(`Delegating search with query: "${effectiveQuery}", path: "${searchPath}"${effectiveQuery !== searchQuery ? ` (rewritten from: "${searchQuery}")` : ""}`);
|
|
27822
28655
|
}
|
|
27823
28656
|
const delegateTask = buildSearchDelegateTask({
|
|
27824
|
-
searchQuery,
|
|
28657
|
+
searchQuery: effectiveQuery,
|
|
27825
28658
|
searchPath,
|
|
27826
28659
|
exact,
|
|
27827
28660
|
language,
|
|
@@ -27848,18 +28681,33 @@ var init_vercel = __esm({
|
|
|
27848
28681
|
});
|
|
27849
28682
|
const delegateResult = options.tracer?.withSpan ? await options.tracer.withSpan("search.delegate", runDelegation, {
|
|
27850
28683
|
"search.query": searchQuery,
|
|
27851
|
-
"search.path": searchPath
|
|
28684
|
+
"search.path": searchPath,
|
|
28685
|
+
...effectiveQuery !== searchQuery ? { "search.query.rewritten": effectiveQuery } : {}
|
|
27852
28686
|
}, (span, result) => {
|
|
27853
|
-
const text = typeof result === "string" ? result : "";
|
|
28687
|
+
const text = typeof result === "string" ? result : JSON.stringify(result) || "";
|
|
28688
|
+
if (debug) console.error(`[search-delegate] onResult: type=${typeof result}, length=${text.length}`);
|
|
27854
28689
|
span.setAttributes({
|
|
27855
28690
|
"search.delegate.output": truncateForSpan(text),
|
|
27856
|
-
"search.delegate.output_length": text.length
|
|
28691
|
+
"search.delegate.output_length": String(text.length)
|
|
27857
28692
|
});
|
|
27858
28693
|
}) : await runDelegation();
|
|
27859
|
-
const
|
|
27860
|
-
if (
|
|
28694
|
+
const structured = parseDelegatedResponse(delegateResult);
|
|
28695
|
+
if (delegationRecord && structured) {
|
|
28696
|
+
delegationRecord.hadResults = structured.groups.length > 0;
|
|
28697
|
+
delegationRecord.reason = structured.reason || "";
|
|
28698
|
+
delegationRecord.groups = structured.groups.map((g) => ({ reason: g.reason }));
|
|
28699
|
+
}
|
|
28700
|
+
if (!structured || structured.groups.length === 0) {
|
|
28701
|
+
if (structured && structured.confidence === "low" && structured.reason) {
|
|
28702
|
+
if (debug) {
|
|
28703
|
+
console.error(`Delegated search explicitly found nothing: ${structured.reason}`);
|
|
28704
|
+
}
|
|
28705
|
+
return `NOT FOUND: The search delegate thoroughly searched for "${searchQuery}" and concluded: ${structured.reason}
|
|
28706
|
+
|
|
28707
|
+
Do NOT search for analogies or loosely related concepts. If the feature does not exist in the codebase, say so in your final answer.`;
|
|
28708
|
+
}
|
|
27861
28709
|
if (debug) {
|
|
27862
|
-
console.error("Delegated search returned no
|
|
28710
|
+
console.error("Delegated search returned no results; falling back to raw search");
|
|
27863
28711
|
}
|
|
27864
28712
|
const fallbackResult = maybeAnnotate(await runRawSearch());
|
|
27865
28713
|
if (options.fileTracker && typeof fallbackResult === "string") {
|
|
@@ -27870,57 +28718,35 @@ var init_vercel = __esm({
|
|
|
27870
28718
|
}
|
|
27871
28719
|
const delegateBase = options.allowedFolders?.[0] || options.cwd || ".";
|
|
27872
28720
|
const resolutionBase = searchPaths[0] || options.cwd || ".";
|
|
27873
|
-
const
|
|
27874
|
-
const
|
|
27875
|
-
|
|
27876
|
-
|
|
27877
|
-
|
|
27878
|
-
|
|
27879
|
-
|
|
27880
|
-
|
|
27881
|
-
|
|
27882
|
-
|
|
27883
|
-
|
|
27884
|
-
|
|
27885
|
-
|
|
27886
|
-
if ((0, import_fs5.existsSync)(candidate)) {
|
|
27887
|
-
validatedTargets.push(candidate + suffix);
|
|
27888
|
-
if (debug) console.error(`[search-delegate] Fixed doubled path segment: ${filePart} \u2192 ${candidate}`);
|
|
27889
|
-
fixed = true;
|
|
27890
|
-
break;
|
|
28721
|
+
const wsPrefix = resolutionBase.endsWith("/") ? resolutionBase : resolutionBase + "/";
|
|
28722
|
+
for (const group of structured.groups) {
|
|
28723
|
+
group.files = group.files.map((target) => resolveTargetPath(target, delegateBase)).map((target) => {
|
|
28724
|
+
const { filePart, suffix } = splitTargetSuffix(target);
|
|
28725
|
+
if ((0, import_fs5.existsSync)(filePart)) return target;
|
|
28726
|
+
const parts = filePart.split("/").filter(Boolean);
|
|
28727
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
28728
|
+
if (parts[i] === parts[i + 1]) {
|
|
28729
|
+
const candidate = "/" + [...parts.slice(0, i), ...parts.slice(i + 1)].join("/");
|
|
28730
|
+
if ((0, import_fs5.existsSync)(candidate)) {
|
|
28731
|
+
if (debug) console.error(`[search-delegate] Fixed doubled path: ${filePart} \u2192 ${candidate}`);
|
|
28732
|
+
return candidate + suffix;
|
|
28733
|
+
}
|
|
27891
28734
|
}
|
|
27892
28735
|
}
|
|
27893
|
-
|
|
27894
|
-
|
|
27895
|
-
|
|
27896
|
-
|
|
27897
|
-
|
|
27898
|
-
|
|
27899
|
-
|
|
27900
|
-
|
|
27901
|
-
if (debug) console.error(`[search-delegate] Resolved with alt base: ${filePart} \u2192 ${altFile}`);
|
|
27902
|
-
fixed = true;
|
|
27903
|
-
break;
|
|
28736
|
+
for (const altBase of [resolutionBase, options.cwd].filter(Boolean)) {
|
|
28737
|
+
if (altBase === delegateBase) continue;
|
|
28738
|
+
const altResolved = resolveTargetPath(target, altBase);
|
|
28739
|
+
const { filePart: altFile } = splitTargetSuffix(altResolved);
|
|
28740
|
+
if ((0, import_fs5.existsSync)(altFile)) {
|
|
28741
|
+
if (debug) console.error(`[search-delegate] Resolved with alt base: ${filePart} \u2192 ${altFile}`);
|
|
28742
|
+
return altResolved;
|
|
28743
|
+
}
|
|
27904
28744
|
}
|
|
27905
|
-
|
|
27906
|
-
|
|
27907
|
-
|
|
27908
|
-
validatedTargets.push(target);
|
|
28745
|
+
if (debug) console.error(`[search-delegate] Warning: target may not exist: ${filePart}`);
|
|
28746
|
+
return target;
|
|
28747
|
+
}).map((target) => target.split(wsPrefix).join(""));
|
|
27909
28748
|
}
|
|
27910
|
-
|
|
27911
|
-
files: validatedTargets,
|
|
27912
|
-
cwd: resolutionBase,
|
|
27913
|
-
allowTests: allow_tests ?? true
|
|
27914
|
-
};
|
|
27915
|
-
if (outline) {
|
|
27916
|
-
extractOptions.format = "xml";
|
|
27917
|
-
}
|
|
27918
|
-
const extractResult = await extract(extractOptions);
|
|
27919
|
-
if (resolutionBase && typeof extractResult === "string") {
|
|
27920
|
-
const wsPrefix = resolutionBase.endsWith("/") ? resolutionBase : resolutionBase + "/";
|
|
27921
|
-
return maybeAnnotate(extractResult.split(wsPrefix).join(""));
|
|
27922
|
-
}
|
|
27923
|
-
return maybeAnnotate(extractResult);
|
|
28749
|
+
return JSON.stringify(structured, null, 2);
|
|
27924
28750
|
} catch (error40) {
|
|
27925
28751
|
console.error("Delegated search failed, falling back to raw search:", error40);
|
|
27926
28752
|
try {
|
|
@@ -98047,14 +98873,11 @@ function buildFallbackProvidersFromEnv(options = {}) {
|
|
|
98047
98873
|
}
|
|
98048
98874
|
return providers;
|
|
98049
98875
|
}
|
|
98050
|
-
var
|
|
98876
|
+
var FALLBACK_STRATEGIES, DEFAULT_MODELS2, FallbackManager;
|
|
98051
98877
|
var init_FallbackManager = __esm({
|
|
98052
98878
|
"src/agent/FallbackManager.js"() {
|
|
98053
98879
|
"use strict";
|
|
98054
|
-
|
|
98055
|
-
import_openai = require("@ai-sdk/openai");
|
|
98056
|
-
import_google = require("@ai-sdk/google");
|
|
98057
|
-
init_dist3();
|
|
98880
|
+
init_provider();
|
|
98058
98881
|
FALLBACK_STRATEGIES = {
|
|
98059
98882
|
SAME_MODEL: "same-model",
|
|
98060
98883
|
// Try same model on different providers
|
|
@@ -98065,12 +98888,7 @@ var init_FallbackManager = __esm({
|
|
|
98065
98888
|
CUSTOM: "custom"
|
|
98066
98889
|
// Use custom provider list
|
|
98067
98890
|
};
|
|
98068
|
-
|
|
98069
|
-
anthropic: "claude-sonnet-4-6",
|
|
98070
|
-
openai: "gpt-5.2",
|
|
98071
|
-
google: "gemini-2.5-flash",
|
|
98072
|
-
bedrock: "anthropic.claude-sonnet-4-6"
|
|
98073
|
-
};
|
|
98891
|
+
DEFAULT_MODELS2 = DEFAULT_MODELS;
|
|
98074
98892
|
FallbackManager = class {
|
|
98075
98893
|
/**
|
|
98076
98894
|
* Create a new FallbackManager
|
|
@@ -98143,45 +98961,7 @@ var init_FallbackManager = __esm({
|
|
|
98143
98961
|
*/
|
|
98144
98962
|
_createProviderInstance(config2) {
|
|
98145
98963
|
try {
|
|
98146
|
-
|
|
98147
|
-
case "anthropic":
|
|
98148
|
-
return (0, import_anthropic.createAnthropic)({
|
|
98149
|
-
apiKey: config2.apiKey,
|
|
98150
|
-
...config2.baseURL && { baseURL: config2.baseURL }
|
|
98151
|
-
});
|
|
98152
|
-
case "openai":
|
|
98153
|
-
return (0, import_openai.createOpenAI)({
|
|
98154
|
-
compatibility: "strict",
|
|
98155
|
-
apiKey: config2.apiKey,
|
|
98156
|
-
...config2.baseURL && { baseURL: config2.baseURL }
|
|
98157
|
-
});
|
|
98158
|
-
case "google":
|
|
98159
|
-
return (0, import_google.createGoogleGenerativeAI)({
|
|
98160
|
-
apiKey: config2.apiKey,
|
|
98161
|
-
...config2.baseURL && { baseURL: config2.baseURL }
|
|
98162
|
-
});
|
|
98163
|
-
case "bedrock": {
|
|
98164
|
-
const bedrockConfig = {};
|
|
98165
|
-
if (config2.apiKey) {
|
|
98166
|
-
bedrockConfig.apiKey = config2.apiKey;
|
|
98167
|
-
} else if (config2.accessKeyId && config2.secretAccessKey) {
|
|
98168
|
-
bedrockConfig.accessKeyId = config2.accessKeyId;
|
|
98169
|
-
bedrockConfig.secretAccessKey = config2.secretAccessKey;
|
|
98170
|
-
if (config2.sessionToken) {
|
|
98171
|
-
bedrockConfig.sessionToken = config2.sessionToken;
|
|
98172
|
-
}
|
|
98173
|
-
}
|
|
98174
|
-
if (config2.region) {
|
|
98175
|
-
bedrockConfig.region = config2.region;
|
|
98176
|
-
}
|
|
98177
|
-
if (config2.baseURL) {
|
|
98178
|
-
bedrockConfig.baseURL = config2.baseURL;
|
|
98179
|
-
}
|
|
98180
|
-
return createAmazonBedrock(bedrockConfig);
|
|
98181
|
-
}
|
|
98182
|
-
default:
|
|
98183
|
-
throw new Error(`FallbackManager: Unknown provider "${config2.provider}"`);
|
|
98184
|
-
}
|
|
98964
|
+
return createProviderInstance(config2);
|
|
98185
98965
|
} catch (error40) {
|
|
98186
98966
|
const providerName = this._getProviderDisplayName(config2);
|
|
98187
98967
|
throw new Error(`Failed to create provider instance for ${providerName}: ${error40.message}`);
|
|
@@ -98194,7 +98974,7 @@ var init_FallbackManager = __esm({
|
|
|
98194
98974
|
* @private
|
|
98195
98975
|
*/
|
|
98196
98976
|
_getModelName(config2) {
|
|
98197
|
-
return config2.model ||
|
|
98977
|
+
return config2.model || DEFAULT_MODELS2[config2.provider];
|
|
98198
98978
|
}
|
|
98199
98979
|
/**
|
|
98200
98980
|
* Get provider display name for logging
|
|
@@ -100111,14 +100891,11 @@ function debugLogToolResults(toolResults) {
|
|
|
100111
100891
|
console.log(`[DEBUG] tool: ${tr.toolName} | args: ${debugTruncate(argsStr)} | result: ${debugTruncate(resultStr)}`);
|
|
100112
100892
|
}
|
|
100113
100893
|
}
|
|
100114
|
-
var import_dotenv2,
|
|
100894
|
+
var import_dotenv2, import_ai6, import_crypto9, import_events4, import_fs15, import_promises6, import_path18, ENGINE_ACTIVITY_TIMEOUT_DEFAULT, ENGINE_ACTIVITY_TIMEOUT_MIN, ENGINE_ACTIVITY_TIMEOUT_MAX, MAX_TOOL_ITERATIONS, MAX_HISTORY_MESSAGES, MAX_IMAGE_FILE_SIZE, ProbeAgent;
|
|
100115
100895
|
var init_ProbeAgent = __esm({
|
|
100116
100896
|
"src/agent/ProbeAgent.js"() {
|
|
100117
100897
|
import_dotenv2 = __toESM(require_main(), 1);
|
|
100118
|
-
|
|
100119
|
-
import_openai2 = require("@ai-sdk/openai");
|
|
100120
|
-
import_google2 = require("@ai-sdk/google");
|
|
100121
|
-
init_dist3();
|
|
100898
|
+
init_provider();
|
|
100122
100899
|
import_ai6 = require("ai");
|
|
100123
100900
|
import_crypto9 = require("crypto");
|
|
100124
100901
|
import_events4 = require("events");
|
|
@@ -101357,11 +102134,8 @@ var init_ProbeAgent = __esm({
|
|
|
101357
102134
|
* Initialize Anthropic model
|
|
101358
102135
|
*/
|
|
101359
102136
|
initializeAnthropicModel(apiKey, apiUrl, modelName) {
|
|
101360
|
-
this.provider = (
|
|
101361
|
-
|
|
101362
|
-
...apiUrl && { baseURL: apiUrl }
|
|
101363
|
-
});
|
|
101364
|
-
this.model = modelName || "claude-sonnet-4-6";
|
|
102137
|
+
this.provider = createProviderInstance({ provider: "anthropic", apiKey, ...apiUrl && { baseURL: apiUrl } });
|
|
102138
|
+
this.model = modelName || DEFAULT_MODELS.anthropic;
|
|
101365
102139
|
this.apiType = "anthropic";
|
|
101366
102140
|
if (this.debug) {
|
|
101367
102141
|
console.log(`Using Anthropic API with model: ${this.model}${apiUrl ? ` (URL: ${apiUrl})` : ""}`);
|
|
@@ -101371,12 +102145,8 @@ var init_ProbeAgent = __esm({
|
|
|
101371
102145
|
* Initialize OpenAI model
|
|
101372
102146
|
*/
|
|
101373
102147
|
initializeOpenAIModel(apiKey, apiUrl, modelName) {
|
|
101374
|
-
this.provider = (
|
|
101375
|
-
|
|
101376
|
-
apiKey,
|
|
101377
|
-
...apiUrl && { baseURL: apiUrl }
|
|
101378
|
-
});
|
|
101379
|
-
this.model = modelName || "gpt-5.2";
|
|
102148
|
+
this.provider = createProviderInstance({ provider: "openai", apiKey, ...apiUrl && { baseURL: apiUrl } });
|
|
102149
|
+
this.model = modelName || DEFAULT_MODELS.openai;
|
|
101380
102150
|
this.apiType = "openai";
|
|
101381
102151
|
if (this.debug) {
|
|
101382
102152
|
console.log(`Using OpenAI API with model: ${this.model}${apiUrl ? ` (URL: ${apiUrl})` : ""}`);
|
|
@@ -101386,10 +102156,7 @@ var init_ProbeAgent = __esm({
|
|
|
101386
102156
|
* Initialize Google model
|
|
101387
102157
|
*/
|
|
101388
102158
|
initializeGoogleModel(apiKey, apiUrl, modelName) {
|
|
101389
|
-
this.provider = (
|
|
101390
|
-
apiKey,
|
|
101391
|
-
...apiUrl && { baseURL: apiUrl }
|
|
101392
|
-
});
|
|
102159
|
+
this.provider = createProviderInstance({ provider: "google", apiKey, ...apiUrl && { baseURL: apiUrl } });
|
|
101393
102160
|
this.model = modelName || "gemini-2.5-pro";
|
|
101394
102161
|
this.apiType = "google";
|
|
101395
102162
|
if (this.debug) {
|
|
@@ -101833,24 +102600,16 @@ var init_ProbeAgent = __esm({
|
|
|
101833
102600
|
* Initialize AWS Bedrock model
|
|
101834
102601
|
*/
|
|
101835
102602
|
initializeBedrockModel(accessKeyId, secretAccessKey, region, sessionToken, apiKey, baseURL, modelName) {
|
|
101836
|
-
|
|
101837
|
-
|
|
101838
|
-
|
|
101839
|
-
|
|
101840
|
-
|
|
101841
|
-
|
|
101842
|
-
|
|
101843
|
-
|
|
101844
|
-
|
|
101845
|
-
|
|
101846
|
-
if (region) {
|
|
101847
|
-
config2.region = region;
|
|
101848
|
-
}
|
|
101849
|
-
if (baseURL) {
|
|
101850
|
-
config2.baseURL = baseURL;
|
|
101851
|
-
}
|
|
101852
|
-
this.provider = createAmazonBedrock(config2);
|
|
101853
|
-
this.model = modelName || "anthropic.claude-sonnet-4-6";
|
|
102603
|
+
this.provider = createProviderInstance({
|
|
102604
|
+
provider: "bedrock",
|
|
102605
|
+
apiKey,
|
|
102606
|
+
accessKeyId,
|
|
102607
|
+
secretAccessKey,
|
|
102608
|
+
sessionToken,
|
|
102609
|
+
region,
|
|
102610
|
+
baseURL
|
|
102611
|
+
});
|
|
102612
|
+
this.model = modelName || DEFAULT_MODELS.bedrock;
|
|
101854
102613
|
this.apiType = "bedrock";
|
|
101855
102614
|
if (this.debug) {
|
|
101856
102615
|
const authMethod = apiKey ? "API Key" : "AWS Credentials";
|
|
@@ -102433,7 +103192,7 @@ ${this.architectureContext.content}
|
|
|
102433
103192
|
} else {
|
|
102434
103193
|
systemPrompt += predefinedPrompts["code-explorer"] + "\n\n";
|
|
102435
103194
|
}
|
|
102436
|
-
const searchToolDesc1 = this.searchDelegate ? '- search: Ask natural language questions to find code (e.g., "How does authentication work?").
|
|
103195
|
+
const searchToolDesc1 = this.searchDelegate ? '- search: Ask natural language questions to find code locations (e.g., "How does authentication work?"). Returns structured JSON with file locations grouped by relevance. Use extract() on the returned files to read the actual code. 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.";
|
|
102437
103196
|
systemPrompt += `You have access to powerful code search and analysis tools through MCP:
|
|
102438
103197
|
${searchToolDesc1}
|
|
102439
103198
|
- extract: Extract specific code sections with context
|
|
@@ -102443,8 +103202,8 @@ ${searchToolDesc1}
|
|
|
102443
103202
|
systemPrompt += `
|
|
102444
103203
|
- bash: Execute bash commands for system operations (building, running tests, git, etc.). NEVER use bash for code exploration (no grep, cat, find, head, tail) \u2014 always use search and extract tools instead, they are faster and more accurate.`;
|
|
102445
103204
|
}
|
|
102446
|
-
const searchGuidance1 = this.searchDelegate ? "1. Start with search \u2014 ask a question about what you want to understand. It returns
|
|
102447
|
-
const extractGuidance1 = this.searchDelegate ?
|
|
103205
|
+
const searchGuidance1 = this.searchDelegate ? "1. Start with search \u2014 ask a question about what you want to understand. It returns file locations grouped by relevance (JSON with confidence and groups)." : "1. Start with search to find relevant code patterns. One search per concept is usually enough \u2014 probe handles stemming and case variations.";
|
|
103206
|
+
const extractGuidance1 = this.searchDelegate ? '2. Use extract on the file locations returned by search to read the actual code. Each group has a "reason" explaining why those files matter.' : "2. Use extract to get detailed context when needed";
|
|
102448
103207
|
systemPrompt += `
|
|
102449
103208
|
|
|
102450
103209
|
When exploring code:
|
|
@@ -102488,7 +103247,7 @@ Workspace: ${this.allowedFolders.join(", ")}`;
|
|
|
102488
103247
|
} else {
|
|
102489
103248
|
systemPrompt += predefinedPrompts["code-explorer"] + "\n\n";
|
|
102490
103249
|
}
|
|
102491
|
-
const searchToolDesc2 = this.searchDelegate ? '- search: Ask natural language questions to find code (e.g., "How does authentication work?").
|
|
103250
|
+
const searchToolDesc2 = this.searchDelegate ? '- search: Ask natural language questions to find code locations (e.g., "How does authentication work?"). Returns structured JSON with file locations grouped by relevance. Use extract() on the returned files to read the actual code. 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.";
|
|
102492
103251
|
systemPrompt += `You have access to powerful code search and analysis tools through MCP:
|
|
102493
103252
|
${searchToolDesc2}
|
|
102494
103253
|
- extract: Extract specific code sections with context
|
|
@@ -102498,8 +103257,8 @@ ${searchToolDesc2}
|
|
|
102498
103257
|
systemPrompt += `
|
|
102499
103258
|
- bash: Execute bash commands for system operations (building, running tests, git, etc.). NEVER use bash for code exploration (no grep, cat, find, head, tail) \u2014 always use search and extract tools instead, they are faster and more accurate.`;
|
|
102500
103259
|
}
|
|
102501
|
-
const searchGuidance2 = this.searchDelegate ? "1. Start with search \u2014 ask a question about what you want to understand. It returns
|
|
102502
|
-
const extractGuidance2 = this.searchDelegate ?
|
|
103260
|
+
const searchGuidance2 = this.searchDelegate ? "1. Start with search \u2014 ask a question about what you want to understand. It returns file locations grouped by relevance (JSON with confidence and groups)." : "1. Start with search to find relevant code patterns. One search per concept is usually enough \u2014 probe handles stemming and case variations.";
|
|
103261
|
+
const extractGuidance2 = this.searchDelegate ? '2. Use extract on the file locations returned by search to read the actual code. Each group has a "reason" explaining why those files matter.' : "2. Use extract to get detailed context when needed";
|
|
102503
103262
|
systemPrompt += `
|
|
102504
103263
|
|
|
102505
103264
|
When exploring code:
|
|
@@ -102559,10 +103318,10 @@ Workspace: ${this.allowedFolders.join(", ")}`;
|
|
|
102559
103318
|
Follow these instructions carefully:
|
|
102560
103319
|
1. Analyze the user's request.
|
|
102561
103320
|
2. Use the available tools step-by-step to fulfill the request.
|
|
102562
|
-
3. You MUST use the search tool before answering ANY code-related question. NEVER answer from memory or general knowledge \u2014 your answers must be grounded in actual code found via search/extract.${this.searchDelegate ? " Ask natural language questions \u2014 the search subagent handles keyword formulation and returns
|
|
103321
|
+
3. You MUST use the search tool before answering ANY code-related question. NEVER answer from memory or general knowledge \u2014 your answers must be grounded in actual code found via search/extract.${this.searchDelegate ? " Ask natural language questions \u2014 the search subagent handles keyword formulation and returns file locations grouped by relevance. Then use extract() on those locations to read the actual code." : " Search handles stemming and case variations automatically \u2014 do NOT try keyword variations manually. Read full files only if really necessary."}
|
|
102563
103322
|
4. Ensure to get really deep and understand the full picture before answering. Follow call chains \u2014 if function A calls B, search for B too. Look for related subsystems (e.g., if asked about rate limiting, also check for quota, throttling, smoothing).
|
|
102564
103323
|
5. Once the task is fully completed, provide your final answer directly as text. Always cite specific files and line numbers as evidence. Do NOT output planning or thinking text \u2014 go straight to the answer.
|
|
102565
|
-
6. ${this.searchDelegate ?
|
|
103324
|
+
6. ${this.searchDelegate ? 'Ask clear, specific questions when searching. Each search should target a distinct concept or question. NEVER re-search the same concept with different phrasing \u2014 if you already searched for "wrapToolWithEmitter", do NOT search again for "definition of wrapToolWithEmitter" or "how wrapToolWithEmitter works". Use extract() on the files already found instead. Limit yourself to one search per distinct concept. When formulating queries, describe WHAT you are looking for, not WHERE \u2014 the search agent will search the full codebase. Do NOT include file names or class names in the query unless that IS the concept (e.g., say "search dedup logic" not "search dedup ProbeAgent").' : "Prefer concise and focused search queries. Use specific keywords and phrases to narrow down results."}
|
|
102566
103325
|
7. NEVER use bash for code exploration (no grep, cat, find, head, tail, awk, sed) \u2014 always use search and extract tools instead. Bash is only for system operations like building, running tests, or git commands.${this.allowEdit ? `
|
|
102567
103326
|
7. When modifying files, choose the appropriate tool:
|
|
102568
103327
|
- Use 'edit' for all code modifications:
|
|
@@ -103289,9 +104048,11 @@ Provide your BEST answer NOW using the information you have already gathered. Do
|
|
|
103289
104048
|
const searchesTried = _toolCallLog.filter((tc) => tc.name === "search").map((tc) => `"${tc.args.query || ""}"${tc.args.exact ? " (exact)" : ""}`).filter((v, i, a) => a.indexOf(v) === i);
|
|
103290
104049
|
const searchSummary = searchesTried.length > 0 ? `
|
|
103291
104050
|
Searches attempted: ${searchesTried.join(", ")}` : "";
|
|
104051
|
+
const isCodeSearcher = this.promptType === "code-searcher";
|
|
104052
|
+
const lastIterMessage = isCodeSearcher ? `\u26A0\uFE0F LAST ITERATION \u2014 you are out of tool calls. Output your JSON response NOW with whatever files you have verified so far. Set confidence to "low" if your search was incomplete. Include the "searches" array listing all search queries you made with their paths and outcomes.${searchSummary}` : `\u26A0\uFE0F LAST ITERATION \u2014 you are out of tool calls. Provide your BEST answer NOW with the information gathered so far. If you could not find what was requested, explain exactly what you searched for and why it did not work, so the caller can try a different approach.${searchSummary}`;
|
|
103292
104053
|
return {
|
|
103293
104054
|
toolChoice: "none",
|
|
103294
|
-
userMessage:
|
|
104055
|
+
userMessage: lastIterMessage
|
|
103295
104056
|
};
|
|
103296
104057
|
}
|
|
103297
104058
|
if (steps.length >= 2) {
|
|
@@ -103826,29 +104587,41 @@ Be thorough \u2014 this is the user's only response. Include all useful informat
|
|
|
103826
104587
|
if (!finalResult || finalResult === DEFAULT_MAX_ITER_MSG) {
|
|
103827
104588
|
try {
|
|
103828
104589
|
const searchQueries = [];
|
|
104590
|
+
const searchDetails = [];
|
|
103829
104591
|
const toolCounts = {};
|
|
103830
104592
|
for (const tc of _toolCallLog) {
|
|
103831
104593
|
toolCounts[tc.name] = (toolCounts[tc.name] || 0) + 1;
|
|
103832
104594
|
if (tc.name === "search") {
|
|
103833
104595
|
const q = tc.args.query || "";
|
|
104596
|
+
const p = tc.args.path || ".";
|
|
103834
104597
|
const exact = tc.args.exact ? " (exact)" : "";
|
|
103835
104598
|
searchQueries.push(`"${q}"${exact}`);
|
|
104599
|
+
searchDetails.push({ query: q, path: p, had_results: false });
|
|
103836
104600
|
}
|
|
103837
104601
|
}
|
|
103838
104602
|
const toolBreakdown = Object.entries(toolCounts).map(([name15, count]) => `${name15}: ${count}x`).join(", ");
|
|
103839
104603
|
const uniqueSearches = [...new Set(searchQueries)];
|
|
103840
|
-
|
|
104604
|
+
if (this.promptType === "code-searcher") {
|
|
104605
|
+
finalResult = JSON.stringify({
|
|
104606
|
+
confidence: "low",
|
|
104607
|
+
reason: "Search incomplete \u2014 iteration limit reached",
|
|
104608
|
+
groups: [],
|
|
104609
|
+
searches: searchDetails
|
|
104610
|
+
});
|
|
104611
|
+
} else {
|
|
104612
|
+
let summary = `I was unable to complete your request after ${currentIteration} tool iterations.
|
|
103841
104613
|
|
|
103842
104614
|
`;
|
|
103843
|
-
|
|
104615
|
+
summary += `Tool calls made: ${toolBreakdown || "none"}
|
|
103844
104616
|
`;
|
|
103845
|
-
|
|
103846
|
-
|
|
104617
|
+
if (uniqueSearches.length > 0) {
|
|
104618
|
+
summary += `Search queries tried: ${uniqueSearches.join(", ")}
|
|
103847
104619
|
`;
|
|
103848
|
-
|
|
103849
|
-
|
|
104620
|
+
}
|
|
104621
|
+
summary += `
|
|
103850
104622
|
The search approach may be fundamentally wrong for this query. Consider: using exact=true for literal string matching, using bash/grep for pattern-based file searches, or trying a completely different strategy instead of repeating similar searches.`;
|
|
103851
|
-
|
|
104623
|
+
finalResult = summary;
|
|
104624
|
+
}
|
|
103852
104625
|
} catch {
|
|
103853
104626
|
finalResult = DEFAULT_MAX_ITER_MSG;
|
|
103854
104627
|
}
|