@probelabs/probe 0.6.0-rc209 → 0.6.0-rc211

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.
Files changed (38) hide show
  1. package/bin/binaries/probe-v0.6.0-rc211-aarch64-apple-darwin.tar.gz +0 -0
  2. package/bin/binaries/probe-v0.6.0-rc211-aarch64-unknown-linux-musl.tar.gz +0 -0
  3. package/bin/binaries/probe-v0.6.0-rc211-x86_64-apple-darwin.tar.gz +0 -0
  4. package/bin/binaries/probe-v0.6.0-rc211-x86_64-pc-windows-msvc.zip +0 -0
  5. package/bin/binaries/probe-v0.6.0-rc211-x86_64-unknown-linux-musl.tar.gz +0 -0
  6. package/build/agent/ProbeAgent.js +100 -7
  7. package/build/agent/bashCommandUtils.js +98 -12
  8. package/build/agent/bashPermissions.js +207 -1
  9. package/build/agent/index.js +911 -90
  10. package/build/agent/probeTool.js +11 -2
  11. package/build/agent/tools.js +8 -0
  12. package/build/delegate.js +11 -2
  13. package/build/index.js +6 -1
  14. package/build/search.js +2 -2
  15. package/build/tools/analyzeAll.js +624 -0
  16. package/build/tools/common.js +149 -85
  17. package/build/tools/langchain.js +1 -1
  18. package/build/tools/vercel.js +66 -4
  19. package/cjs/agent/ProbeAgent.cjs +9841 -6642
  20. package/cjs/index.cjs +9955 -6750
  21. package/package.json +1 -1
  22. package/src/agent/ProbeAgent.js +100 -7
  23. package/src/agent/bashCommandUtils.js +98 -12
  24. package/src/agent/bashPermissions.js +207 -1
  25. package/src/agent/probeTool.js +11 -2
  26. package/src/agent/tools.js +8 -0
  27. package/src/delegate.js +11 -2
  28. package/src/index.js +6 -1
  29. package/src/search.js +2 -2
  30. package/src/tools/analyzeAll.js +624 -0
  31. package/src/tools/common.js +149 -85
  32. package/src/tools/langchain.js +1 -1
  33. package/src/tools/vercel.js +66 -4
  34. package/bin/binaries/probe-v0.6.0-rc209-aarch64-apple-darwin.tar.gz +0 -0
  35. package/bin/binaries/probe-v0.6.0-rc209-aarch64-unknown-linux-musl.tar.gz +0 -0
  36. package/bin/binaries/probe-v0.6.0-rc209-x86_64-apple-darwin.tar.gz +0 -0
  37. package/bin/binaries/probe-v0.6.0-rc209-x86_64-pc-windows-msvc.zip +0 -0
  38. package/bin/binaries/probe-v0.6.0-rc209-x86_64-unknown-linux-musl.tar.gz +0 -0
@@ -3346,8 +3346,8 @@ async function search(options) {
3346
3346
  }
3347
3347
  }
3348
3348
  if (!options.maxTokens) {
3349
- options.maxTokens = 1e4;
3350
- cliArgs.push("--max-tokens", "10000");
3349
+ options.maxTokens = 2e4;
3350
+ cliArgs.push("--max-tokens", "20000");
3351
3351
  }
3352
3352
  if (!options.timeout) {
3353
3353
  options.timeout = 30;
@@ -3764,7 +3764,10 @@ async function delegate({
3764
3764
  disableTools = false,
3765
3765
  searchDelegate = void 0,
3766
3766
  schema = null,
3767
- enableTasks = false
3767
+ enableTasks = false,
3768
+ enableMcp = false,
3769
+ mcpConfig = null,
3770
+ mcpConfigPath = null
3768
3771
  }) {
3769
3772
  if (!task || typeof task !== "string") {
3770
3773
  throw new Error("Task parameter is required and must be a string");
@@ -3820,8 +3823,14 @@ async function delegate({
3820
3823
  allowedTools,
3821
3824
  disableTools,
3822
3825
  searchDelegate,
3823
- enableTasks
3826
+ enableTasks,
3824
3827
  // Inherit from parent (subagent gets isolated TaskManager)
3828
+ enableMcp,
3829
+ // Inherit from parent (subagent creates own MCPXmlBridge)
3830
+ mcpConfig,
3831
+ // Inherit from parent
3832
+ mcpConfigPath
3833
+ // Inherit from parent
3825
3834
  });
3826
3835
  if (debug) {
3827
3836
  console.error(`[DELEGATE] Created subagent with session ${sessionId}`);
@@ -4023,6 +4032,447 @@ var init_delegate = __esm({
4023
4032
  }
4024
4033
  });
4025
4034
 
4035
+ // src/tools/analyzeAll.js
4036
+ function estimateTokens(text) {
4037
+ if (!text) return 0;
4038
+ return Math.ceil(text.length / CHARS_PER_TOKEN);
4039
+ }
4040
+ function stripResultTags(text) {
4041
+ if (!text) return text;
4042
+ return text.replace(/^<result>\s*/i, "").replace(/\s*<\/result>$/i, "").trim();
4043
+ }
4044
+ function parsePlanningResult(planningResult) {
4045
+ const result = {
4046
+ searchQuery: null,
4047
+ aggregation: "summarize",
4048
+ extractionPrompt: null,
4049
+ reasoning: null
4050
+ };
4051
+ const queryMatch = planningResult.match(/SEARCH_QUERY:\s*(.+?)(?=\n[A-Z_]+:|$)/s);
4052
+ if (queryMatch) {
4053
+ result.searchQuery = queryMatch[1].trim();
4054
+ }
4055
+ const aggMatch = planningResult.match(/AGGREGATION:\s*(summarize|list_unique|count|group_by)/i);
4056
+ if (aggMatch) {
4057
+ result.aggregation = aggMatch[1].toLowerCase();
4058
+ }
4059
+ const extractMatch = planningResult.match(/EXTRACTION_PROMPT:\s*(.+?)(?=\n[A-Z_]+:|$)/s);
4060
+ if (extractMatch) {
4061
+ result.extractionPrompt = extractMatch[1].trim();
4062
+ }
4063
+ const reasoningMatch = planningResult.match(/REASONING:\s*(.+?)$/s);
4064
+ if (reasoningMatch) {
4065
+ result.reasoning = reasoningMatch[1].trim();
4066
+ }
4067
+ return result;
4068
+ }
4069
+ function chunkResults(searchResults, chunkSizeTokens) {
4070
+ const chunks = [];
4071
+ const chunkSizeChars = chunkSizeTokens * CHARS_PER_TOKEN;
4072
+ const fileBlocks = searchResults.split(/(?=^```)/m);
4073
+ let currentChunk = "";
4074
+ let currentTokens = 0;
4075
+ for (const block of fileBlocks) {
4076
+ const blockTokens = estimateTokens(block);
4077
+ if (blockTokens > chunkSizeTokens && currentChunk.length > 0) {
4078
+ chunks.push({
4079
+ id: chunks.length + 1,
4080
+ total: 0,
4081
+ content: currentChunk.trim(),
4082
+ estimatedTokens: currentTokens
4083
+ });
4084
+ currentChunk = "";
4085
+ currentTokens = 0;
4086
+ }
4087
+ if (currentTokens + blockTokens > chunkSizeTokens && currentChunk.length > 0) {
4088
+ chunks.push({
4089
+ id: chunks.length + 1,
4090
+ total: 0,
4091
+ content: currentChunk.trim(),
4092
+ estimatedTokens: currentTokens
4093
+ });
4094
+ currentChunk = "";
4095
+ currentTokens = 0;
4096
+ }
4097
+ currentChunk += block;
4098
+ currentTokens += blockTokens;
4099
+ }
4100
+ if (currentChunk.trim().length > 0) {
4101
+ chunks.push({
4102
+ id: chunks.length + 1,
4103
+ total: 0,
4104
+ content: currentChunk.trim(),
4105
+ estimatedTokens: currentTokens
4106
+ });
4107
+ }
4108
+ const totalChunks = chunks.length;
4109
+ for (const chunk of chunks) {
4110
+ chunk.total = totalChunks;
4111
+ }
4112
+ return chunks;
4113
+ }
4114
+ async function processChunk(chunk, extractionPrompt, options) {
4115
+ const task = `You are analyzing search results (chunk ${chunk.id} of ${chunk.total}).
4116
+
4117
+ Your task: ${extractionPrompt}
4118
+
4119
+ Search Results:
4120
+ ${chunk.content}
4121
+
4122
+ Instructions:
4123
+ - Extract ALL relevant information matching the analysis task
4124
+ - Be specific and include actual names, values, patterns found
4125
+ - Format as a structured list if multiple items found
4126
+ - If nothing relevant is found in this chunk, respond with "No relevant items found in this chunk."
4127
+ - Do NOT summarize the code - extract the specific information requested
4128
+ - IMPORTANT: When completing, always use the FULL format: <attempt_completion><result>YOUR ANSWER HERE</result></attempt_completion>
4129
+ - Do NOT use the shorthand <attempt_complete></attempt_complete> format`;
4130
+ try {
4131
+ const result = await delegate({
4132
+ task,
4133
+ debug: options.debug,
4134
+ parentSessionId: options.sessionId,
4135
+ path: options.path,
4136
+ allowedFolders: options.allowedFolders,
4137
+ provider: options.provider,
4138
+ model: options.model,
4139
+ tracer: options.tracer,
4140
+ enableBash: false,
4141
+ promptType: "code-researcher",
4142
+ allowedTools: ["extract"],
4143
+ maxIterations: 5,
4144
+ timeout: 120
4145
+ });
4146
+ return { chunk, result };
4147
+ } catch (error) {
4148
+ return { chunk, result: `Error processing chunk ${chunk.id}: ${error.message}` };
4149
+ }
4150
+ }
4151
+ async function processChunksParallel(chunks, extractionPrompt, maxWorkers, options) {
4152
+ const results = [];
4153
+ const queue = [...chunks];
4154
+ const active = /* @__PURE__ */ new Set();
4155
+ while (queue.length > 0 || active.size > 0) {
4156
+ while (active.size < maxWorkers && queue.length > 0) {
4157
+ const chunk = queue.shift();
4158
+ const promise = processChunk(chunk, extractionPrompt, options).then((result) => {
4159
+ active.delete(promise);
4160
+ return result;
4161
+ });
4162
+ active.add(promise);
4163
+ if (options.debug) {
4164
+ console.error(`[analyze_all] Started processing chunk ${chunk.id}/${chunk.total}`);
4165
+ }
4166
+ }
4167
+ if (active.size > 0) {
4168
+ const result = await Promise.race(active);
4169
+ results.push(result);
4170
+ if (options.debug) {
4171
+ console.error(`[analyze_all] Completed chunk ${result.chunk.id}/${result.chunk.total}`);
4172
+ }
4173
+ }
4174
+ }
4175
+ results.sort((a, b) => a.chunk.id - b.chunk.id);
4176
+ return results;
4177
+ }
4178
+ async function aggregateResults(chunkResults2, aggregation, extractionPrompt, options) {
4179
+ const meaningfulResults = chunkResults2.filter(
4180
+ (r) => r.result && !r.result.toLowerCase().includes("no relevant items found") && r.result.trim().length > 0
4181
+ );
4182
+ if (meaningfulResults.length === 0) {
4183
+ return "No relevant information found across all chunks.";
4184
+ }
4185
+ if (meaningfulResults.length === 1) {
4186
+ return stripResultTags(meaningfulResults[0].result);
4187
+ }
4188
+ const chunkSummaries = meaningfulResults.map((r) => `--- Chunk ${r.chunk.id} ---
4189
+ ${stripResultTags(r.result)}`).join("\n\n");
4190
+ const completionNote = `
4191
+
4192
+ IMPORTANT: When completing, always use the FULL format: <attempt_completion><result>YOUR ANSWER HERE</result></attempt_completion>`;
4193
+ const aggregationPrompts = {
4194
+ summarize: `Synthesize these analyses into a comprehensive summary. Combine related findings, remove redundancy, and present a coherent overview.
4195
+
4196
+ Original task: ${extractionPrompt}
4197
+
4198
+ Chunk analyses:
4199
+ ${chunkSummaries}
4200
+
4201
+ Provide a unified summary that captures all key findings.${completionNote}`,
4202
+ list_unique: `Combine these lists and remove duplicates. Create a single deduplicated list of all unique items found.
4203
+
4204
+ Original task: ${extractionPrompt}
4205
+
4206
+ Chunk analyses:
4207
+ ${chunkSummaries}
4208
+
4209
+ Return a deduplicated, organized list of all unique items. Group related items if helpful.${completionNote}`,
4210
+ count: `Count and aggregate the findings from these analyses. Provide total counts and breakdowns.
4211
+
4212
+ Original task: ${extractionPrompt}
4213
+
4214
+ Chunk analyses:
4215
+ ${chunkSummaries}
4216
+
4217
+ Provide accurate counts and a summary of all occurrences found.${completionNote}`,
4218
+ group_by: `Group and categorize all items from these analyses. Organize findings into logical categories.
4219
+
4220
+ Original task: ${extractionPrompt}
4221
+
4222
+ Chunk analyses:
4223
+ ${chunkSummaries}
4224
+
4225
+ Organize all findings into clear categories with items listed under each.${completionNote}`
4226
+ };
4227
+ const aggregationTask = aggregationPrompts[aggregation] || aggregationPrompts.summarize;
4228
+ try {
4229
+ const result = await delegate({
4230
+ task: aggregationTask,
4231
+ debug: options.debug,
4232
+ parentSessionId: options.sessionId,
4233
+ path: options.path,
4234
+ allowedFolders: options.allowedFolders,
4235
+ provider: options.provider,
4236
+ model: options.model,
4237
+ tracer: options.tracer,
4238
+ enableBash: false,
4239
+ promptType: "code-researcher",
4240
+ allowedTools: [],
4241
+ maxIterations: 5,
4242
+ timeout: 120
4243
+ });
4244
+ return result;
4245
+ } catch (error) {
4246
+ return `Aggregation failed (${error.message}). Raw results:
4247
+
4248
+ ${chunkSummaries}`;
4249
+ }
4250
+ }
4251
+ async function planAnalysis(question, path9, options) {
4252
+ if (options.debug) {
4253
+ console.error(`[analyze_all] Phase 1: Planning analysis strategy...`);
4254
+ }
4255
+ const planningTask = `Create an analysis plan for this question about a codebase:
4256
+
4257
+ "${question}"
4258
+
4259
+ Search scope: ${path9}
4260
+
4261
+ Use attempt_completion to output your plan in this EXACT format:
4262
+
4263
+ SEARCH_QUERY: <elasticsearch query using OR for multiple terms, quotes for exact phrases>
4264
+ AGGREGATION: <summarize | list_unique | count | group_by>
4265
+ EXTRACTION_PROMPT: <what to extract from each search result>
4266
+ REASONING: <brief explanation>
4267
+
4268
+ Example plan:
4269
+ SEARCH_QUERY: export OR function OR class OR tool
4270
+ AGGREGATION: list_unique
4271
+ EXTRACTION_PROMPT: Extract tool names and their purpose
4272
+ REASONING: Using list_unique to deduplicate tool definitions
4273
+
4274
+ IMPORTANT: Use attempt_completion immediately with your plan. Do NOT try to search or answer the question - just create the analysis plan.`;
4275
+ try {
4276
+ const result = await delegate({
4277
+ task: planningTask,
4278
+ debug: options.debug,
4279
+ parentSessionId: options.sessionId,
4280
+ path: path9,
4281
+ allowedFolders: [path9],
4282
+ provider: options.provider,
4283
+ model: options.model,
4284
+ tracer: options.tracer,
4285
+ enableBash: false,
4286
+ promptType: "code-researcher",
4287
+ allowedTools: [],
4288
+ // attempt_completion only (default tool)
4289
+ maxIterations: 3,
4290
+ timeout: 60
4291
+ });
4292
+ const plan = parsePlanningResult(stripResultTags(result));
4293
+ if (options.debug) {
4294
+ console.error(`[analyze_all] Planning complete:`);
4295
+ console.error(`[analyze_all] Search Query: ${plan.searchQuery}`);
4296
+ console.error(`[analyze_all] Aggregation: ${plan.aggregation}`);
4297
+ console.error(`[analyze_all] Extraction: ${plan.extractionPrompt?.substring(0, 100)}...`);
4298
+ }
4299
+ return plan;
4300
+ } catch (error) {
4301
+ throw new Error(`Planning phase failed: ${error.message}`);
4302
+ }
4303
+ }
4304
+ async function synthesizeAnswer(question, aggregatedData, plan, options) {
4305
+ if (options.debug) {
4306
+ console.error(`[analyze_all] Phase 3: Synthesizing final answer...`);
4307
+ }
4308
+ const synthesisTask = `You analyzed a codebase to answer this question:
4309
+
4310
+ "${question}"
4311
+
4312
+ Analysis Strategy Used:
4313
+ - Search Query: ${plan.searchQuery}
4314
+ - Aggregation Method: ${plan.aggregation}
4315
+ - Extraction Focus: ${plan.extractionPrompt}
4316
+
4317
+ Aggregated Analysis Results:
4318
+ ${aggregatedData}
4319
+
4320
+ Now provide a COMPREHENSIVE, DETAILED answer to the original question.
4321
+
4322
+ Your answer should:
4323
+ 1. **Directly answer the question** with a clear summary at the top
4324
+ 2. **Provide specific evidence** - include actual names, values, file locations where relevant
4325
+ 3. **Organize the information** logically (use categories, lists, or sections as appropriate)
4326
+ 4. **Note completeness** - mention if the analysis covered all relevant data or if there might be gaps
4327
+ 5. **Be thorough** - this is the final answer the user will see, make it complete and useful
4328
+
4329
+ Format your response as a well-structured document that fully answers: "${question}"
4330
+
4331
+ IMPORTANT: When completing, use the FULL format: <attempt_completion><result>YOUR ANSWER HERE</result></attempt_completion>`;
4332
+ try {
4333
+ const result = await delegate({
4334
+ task: synthesisTask,
4335
+ debug: options.debug,
4336
+ parentSessionId: options.sessionId,
4337
+ path: options.path,
4338
+ allowedFolders: options.allowedFolders,
4339
+ provider: options.provider,
4340
+ model: options.model,
4341
+ tracer: options.tracer,
4342
+ enableBash: false,
4343
+ promptType: "code-researcher",
4344
+ allowedTools: [],
4345
+ maxIterations: 5,
4346
+ timeout: 180
4347
+ });
4348
+ return stripResultTags(result);
4349
+ } catch (error) {
4350
+ return `Analysis Results for: "${question}"
4351
+
4352
+ ${aggregatedData}`;
4353
+ }
4354
+ }
4355
+ async function analyzeAll(options) {
4356
+ const {
4357
+ question,
4358
+ path: path9 = ".",
4359
+ sessionId,
4360
+ debug = false,
4361
+ cwd,
4362
+ allowedFolders,
4363
+ provider,
4364
+ model,
4365
+ tracer,
4366
+ chunkSizeTokens = DEFAULT_CHUNK_SIZE_TOKENS,
4367
+ maxChunks = MAX_CHUNKS
4368
+ } = options;
4369
+ if (!question) {
4370
+ throw new Error('The "question" parameter is required.');
4371
+ }
4372
+ const delegateOptions = {
4373
+ debug,
4374
+ sessionId,
4375
+ path: allowedFolders?.[0] || cwd || path9,
4376
+ allowedFolders,
4377
+ provider,
4378
+ model,
4379
+ tracer
4380
+ };
4381
+ if (debug) {
4382
+ console.error(`[analyze_all] Starting analysis`);
4383
+ console.error(`[analyze_all] Question: ${question}`);
4384
+ console.error(`[analyze_all] Path: ${path9}`);
4385
+ }
4386
+ const plan = await planAnalysis(question, path9, delegateOptions);
4387
+ if (!plan.searchQuery) {
4388
+ throw new Error("Planning phase failed to determine a search query.");
4389
+ }
4390
+ if (!plan.extractionPrompt) {
4391
+ throw new Error("Planning phase failed to determine an extraction prompt.");
4392
+ }
4393
+ if (debug) {
4394
+ console.error(`[analyze_all] Phase 2: Processing data with map-reduce...`);
4395
+ }
4396
+ const searchResults = await search({
4397
+ query: plan.searchQuery,
4398
+ path: path9,
4399
+ cwd,
4400
+ maxTokens: null,
4401
+ allowTests: true,
4402
+ session: sessionId
4403
+ });
4404
+ if (!searchResults || searchResults.trim().length === 0) {
4405
+ return `No data found matching the analysis plan for: "${question}"
4406
+
4407
+ Search query used: ${plan.searchQuery}
4408
+
4409
+ Try rephrasing your question or broadening the scope.`;
4410
+ }
4411
+ const totalTokens = estimateTokens(searchResults);
4412
+ if (debug) {
4413
+ console.error(`[analyze_all] Total search results: ~${totalTokens} tokens`);
4414
+ }
4415
+ let aggregatedData;
4416
+ if (totalTokens <= chunkSizeTokens) {
4417
+ if (debug) {
4418
+ console.error(`[analyze_all] Results fit in single chunk, processing directly`);
4419
+ }
4420
+ const singleChunk = {
4421
+ id: 1,
4422
+ total: 1,
4423
+ content: searchResults,
4424
+ estimatedTokens: totalTokens
4425
+ };
4426
+ const result = await processChunk(singleChunk, plan.extractionPrompt, delegateOptions);
4427
+ aggregatedData = stripResultTags(result.result);
4428
+ } else {
4429
+ const chunks = chunkResults(searchResults, chunkSizeTokens);
4430
+ if (debug) {
4431
+ console.error(`[analyze_all] Split into ${chunks.length} chunks`);
4432
+ }
4433
+ if (chunks.length > maxChunks) {
4434
+ console.error(`[analyze_all] Warning: Truncating from ${chunks.length} to ${maxChunks} chunks`);
4435
+ chunks.length = maxChunks;
4436
+ for (const chunk of chunks) {
4437
+ chunk.total = maxChunks;
4438
+ }
4439
+ }
4440
+ const chunkResultsProcessed = await processChunksParallel(
4441
+ chunks,
4442
+ plan.extractionPrompt,
4443
+ MAX_PARALLEL_WORKERS,
4444
+ delegateOptions
4445
+ );
4446
+ if (debug) {
4447
+ console.error(`[analyze_all] All ${chunks.length} chunks processed, starting aggregation`);
4448
+ }
4449
+ aggregatedData = await aggregateResults(
4450
+ chunkResultsProcessed,
4451
+ plan.aggregation,
4452
+ plan.extractionPrompt,
4453
+ delegateOptions
4454
+ );
4455
+ aggregatedData = stripResultTags(aggregatedData);
4456
+ }
4457
+ const finalAnswer = await synthesizeAnswer(question, aggregatedData, plan, delegateOptions);
4458
+ if (debug) {
4459
+ console.error(`[analyze_all] Analysis complete`);
4460
+ }
4461
+ return finalAnswer;
4462
+ }
4463
+ var DEFAULT_CHUNK_SIZE_TOKENS, MAX_PARALLEL_WORKERS, MAX_CHUNKS, CHARS_PER_TOKEN;
4464
+ var init_analyzeAll = __esm({
4465
+ "src/tools/analyzeAll.js"() {
4466
+ "use strict";
4467
+ init_search();
4468
+ init_delegate();
4469
+ DEFAULT_CHUNK_SIZE_TOKENS = 8e3;
4470
+ MAX_PARALLEL_WORKERS = 3;
4471
+ MAX_CHUNKS = 50;
4472
+ CHARS_PER_TOKEN = 4;
4473
+ }
4474
+ });
4475
+
4026
4476
  // node_modules/zod/v3/helpers/util.js
4027
4477
  var util, objectUtil, ZodParsedType, getParsedType;
4028
4478
  var init_util2 = __esm({
@@ -8895,6 +9345,7 @@ function getValidParamsForTool(toolName) {
8895
9345
  query: querySchema,
8896
9346
  extract: extractSchema,
8897
9347
  delegate: delegateSchema,
9348
+ analyze_all: analyzeAllSchema,
8898
9349
  listSkills: listSkillsSchema,
8899
9350
  useSkill: useSkillSchema,
8900
9351
  bash: bashSchema,
@@ -8919,76 +9370,84 @@ function getValidParamsForTool(toolName) {
8919
9370
  return [];
8920
9371
  }
8921
9372
  function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
8922
- for (const toolName of validTools) {
8923
- const openTag = `<${toolName}>`;
8924
- const closeTag = `</${toolName}>`;
8925
- const openIndex = xmlString.indexOf(openTag);
8926
- if (openIndex === -1) {
8927
- continue;
9373
+ let earliestToolName = null;
9374
+ let earliestOpenIndex = Infinity;
9375
+ for (const toolName2 of validTools) {
9376
+ const openTag2 = `<${toolName2}>`;
9377
+ const openIndex2 = xmlString.indexOf(openTag2);
9378
+ if (openIndex2 !== -1 && openIndex2 < earliestOpenIndex) {
9379
+ earliestOpenIndex = openIndex2;
9380
+ earliestToolName = toolName2;
8928
9381
  }
8929
- let closeIndex;
8930
- if (toolName === "attempt_completion") {
8931
- closeIndex = xmlString.lastIndexOf(closeTag);
8932
- if (closeIndex !== -1 && closeIndex <= openIndex + openTag.length) {
8933
- closeIndex = -1;
8934
- }
8935
- } else {
8936
- closeIndex = xmlString.indexOf(closeTag, openIndex + openTag.length);
9382
+ }
9383
+ if (earliestToolName === null) {
9384
+ return null;
9385
+ }
9386
+ const toolName = earliestToolName;
9387
+ const openTag = `<${toolName}>`;
9388
+ const closeTag = `</${toolName}>`;
9389
+ const openIndex = earliestOpenIndex;
9390
+ let closeIndex;
9391
+ if (toolName === "attempt_completion") {
9392
+ closeIndex = xmlString.lastIndexOf(closeTag);
9393
+ if (closeIndex !== -1 && closeIndex <= openIndex + openTag.length) {
9394
+ closeIndex = -1;
8937
9395
  }
8938
- let hasClosingTag = closeIndex !== -1;
8939
- if (closeIndex === -1) {
8940
- closeIndex = xmlString.length;
9396
+ } else {
9397
+ closeIndex = xmlString.indexOf(closeTag, openIndex + openTag.length);
9398
+ }
9399
+ let hasClosingTag = closeIndex !== -1;
9400
+ if (closeIndex === -1) {
9401
+ closeIndex = xmlString.length;
9402
+ }
9403
+ const innerContent = xmlString.substring(
9404
+ openIndex + openTag.length,
9405
+ closeIndex
9406
+ );
9407
+ const params = {};
9408
+ const validParams = getValidParamsForTool(toolName);
9409
+ for (const paramName of validParams) {
9410
+ const paramOpenTag = `<${paramName}>`;
9411
+ const paramCloseTag = `</${paramName}>`;
9412
+ const paramOpenIndex = innerContent.indexOf(paramOpenTag);
9413
+ if (paramOpenIndex === -1) {
9414
+ continue;
8941
9415
  }
8942
- const innerContent = xmlString.substring(
8943
- openIndex + openTag.length,
8944
- closeIndex
8945
- );
8946
- const params = {};
8947
- const validParams = getValidParamsForTool(toolName);
8948
- for (const paramName of validParams) {
8949
- const paramOpenTag = `<${paramName}>`;
8950
- const paramCloseTag = `</${paramName}>`;
8951
- const paramOpenIndex = innerContent.indexOf(paramOpenTag);
8952
- if (paramOpenIndex === -1) {
8953
- continue;
8954
- }
8955
- let paramCloseIndex = innerContent.indexOf(paramCloseTag, paramOpenIndex + paramOpenTag.length);
8956
- if (paramCloseIndex === -1) {
8957
- let nextTagIndex = innerContent.length;
8958
- for (const nextParam of validParams) {
8959
- const nextOpenTag = `<${nextParam}>`;
8960
- const nextIndex = innerContent.indexOf(nextOpenTag, paramOpenIndex + paramOpenTag.length);
8961
- if (nextIndex !== -1 && nextIndex < nextTagIndex) {
8962
- nextTagIndex = nextIndex;
8963
- }
9416
+ let paramCloseIndex = innerContent.indexOf(paramCloseTag, paramOpenIndex + paramOpenTag.length);
9417
+ if (paramCloseIndex === -1) {
9418
+ let nextTagIndex = innerContent.length;
9419
+ for (const nextParam of validParams) {
9420
+ const nextOpenTag = `<${nextParam}>`;
9421
+ const nextIndex = innerContent.indexOf(nextOpenTag, paramOpenIndex + paramOpenTag.length);
9422
+ if (nextIndex !== -1 && nextIndex < nextTagIndex) {
9423
+ nextTagIndex = nextIndex;
8964
9424
  }
8965
- paramCloseIndex = nextTagIndex;
8966
9425
  }
8967
- let paramValue = innerContent.substring(
8968
- paramOpenIndex + paramOpenTag.length,
8969
- paramCloseIndex
8970
- ).trim();
8971
- if (paramValue.toLowerCase() === "true") {
8972
- paramValue = true;
8973
- } else if (paramValue.toLowerCase() === "false") {
8974
- paramValue = false;
8975
- } else if (!isNaN(paramValue) && paramValue.trim() !== "") {
8976
- const num = Number(paramValue);
8977
- if (Number.isFinite(num)) {
8978
- paramValue = num;
8979
- }
8980
- }
8981
- params[paramName] = paramValue;
9426
+ paramCloseIndex = nextTagIndex;
8982
9427
  }
8983
- if (toolName === "attempt_completion") {
8984
- params["result"] = innerContent.trim();
8985
- if (params.command) {
8986
- delete params.command;
9428
+ let paramValue = innerContent.substring(
9429
+ paramOpenIndex + paramOpenTag.length,
9430
+ paramCloseIndex
9431
+ ).trim();
9432
+ if (paramValue.toLowerCase() === "true") {
9433
+ paramValue = true;
9434
+ } else if (paramValue.toLowerCase() === "false") {
9435
+ paramValue = false;
9436
+ } else if (!isNaN(paramValue) && paramValue.trim() !== "") {
9437
+ const num = Number(paramValue);
9438
+ if (Number.isFinite(num)) {
9439
+ paramValue = num;
8987
9440
  }
8988
9441
  }
8989
- return { toolName, params };
9442
+ params[paramName] = paramValue;
8990
9443
  }
8991
- return null;
9444
+ if (toolName === "attempt_completion") {
9445
+ params["result"] = innerContent.trim();
9446
+ if (params.command) {
9447
+ delete params.command;
9448
+ }
9449
+ }
9450
+ return { toolName, params };
8992
9451
  }
8993
9452
  function createMessagePreview(message, charsPerSide = 200) {
8994
9453
  if (message === null || message === void 0) {
@@ -9113,7 +9572,7 @@ function resolveTargetPath(target, cwd) {
9113
9572
  }
9114
9573
  return filePart + suffix;
9115
9574
  }
9116
- var searchSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, bashSchema, attemptCompletionSchema, searchToolDefinition, queryToolDefinition, extractToolDefinition, delegateToolDefinition, attemptCompletionToolDefinition, bashToolDefinition, searchDescription, queryDescription, extractDescription, delegateDescription, DEFAULT_VALID_TOOLS;
9575
+ var searchSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, bashSchema, analyzeAllSchema, attemptCompletionSchema, searchToolDefinition, queryToolDefinition, extractToolDefinition, delegateToolDefinition, attemptCompletionToolDefinition, analyzeAllToolDefinition, bashToolDefinition, searchDescription, queryDescription, extractDescription, delegateDescription, analyzeAllDescription, DEFAULT_VALID_TOOLS;
9117
9576
  var init_common = __esm({
9118
9577
  "src/tools/common.js"() {
9119
9578
  "use strict";
@@ -9150,6 +9609,10 @@ var init_common = __esm({
9150
9609
  timeout: external_exports.number().optional().describe("Command timeout in milliseconds (optional)"),
9151
9610
  env: external_exports.record(external_exports.string()).optional().describe("Additional environment variables (optional)")
9152
9611
  });
9612
+ analyzeAllSchema = external_exports.object({
9613
+ question: external_exports.string().min(1).describe('Free-form question to answer (e.g., "What features are customers using?", "List all API endpoints"). The AI will automatically plan the search strategy, process all matching data, and synthesize a comprehensive answer.'),
9614
+ path: external_exports.string().optional().default(".").describe("Directory path to search in")
9615
+ });
9153
9616
  attemptCompletionSchema = {
9154
9617
  // Custom validation that requires result parameter but allows direct XML response
9155
9618
  safeParse: (params) => {
@@ -9359,6 +9822,51 @@ Usage Example:
9359
9822
  <attempt_completion>
9360
9823
  I have refactored the search module according to the requirements and verified the tests pass. The module now uses the new BM25 ranking algorithm and has improved error handling.
9361
9824
  </attempt_completion>
9825
+ `;
9826
+ analyzeAllToolDefinition = `
9827
+ ## analyze_all
9828
+ Description: Intelligent bulk data analysis tool. Process ALL data matching your question using a 3-phase approach:
9829
+ 1. **PLANNING**: AI analyzes your question and determines the optimal search strategy
9830
+ 2. **PROCESSING**: Map-reduce processes all matching data in parallel chunks
9831
+ 3. **SYNTHESIS**: Comprehensive answer with evidence and organization
9832
+
9833
+ **Use this for questions requiring 100% data coverage:**
9834
+ - "What features are customers using?"
9835
+ - "List all API endpoints in the codebase"
9836
+ - "Summarize the error handling patterns"
9837
+ - "Count all TODO comments and their contexts"
9838
+
9839
+ **Do NOT use for:**
9840
+ - Simple searches where a sample is sufficient
9841
+ - Finding a specific function or class
9842
+ - Quick exploration (use search instead)
9843
+
9844
+ **WARNING:** Makes multiple LLM calls - slower and costlier than search.
9845
+
9846
+ Parameters:
9847
+ - question: (required) Free-form question to answer - the AI determines the best search strategy automatically
9848
+ - path: (optional) Directory to search in (default: current directory)
9849
+
9850
+ <examples>
9851
+
9852
+ User: What are all the different tools available in this codebase?
9853
+ <analyze_all>
9854
+ <question>What are all the different tools available in this codebase and what do they do?</question>
9855
+ <path>./src</path>
9856
+ </analyze_all>
9857
+
9858
+ User: I need to understand all the error handling patterns
9859
+ <analyze_all>
9860
+ <question>What error handling patterns are used throughout the codebase? Include examples.</question>
9861
+ </analyze_all>
9862
+
9863
+ User: Count and categorize all the environment variables
9864
+ <analyze_all>
9865
+ <question>What environment variables are used? Categorize them by purpose.</question>
9866
+ <path>./src</path>
9867
+ </analyze_all>
9868
+
9869
+ </examples>
9362
9870
  `;
9363
9871
  bashToolDefinition = `
9364
9872
  ## bash
@@ -9419,11 +9927,13 @@ User: Check system info
9419
9927
  queryDescription = "Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.";
9420
9928
  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.";
9421
9929
  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.";
9930
+ analyzeAllDescription = 'Answer questions that require analyzing ALL matching data in the codebase. Use for aggregate questions like "What features exist?", "List all API endpoints", "Count TODO comments". The AI automatically plans the search strategy, processes all results via map-reduce, and synthesizes a comprehensive answer. WARNING: Slower than search - only use when you need complete coverage.';
9422
9931
  DEFAULT_VALID_TOOLS = [
9423
9932
  "search",
9424
9933
  "query",
9425
9934
  "extract",
9426
9935
  "delegate",
9936
+ "analyze_all",
9427
9937
  "listSkills",
9428
9938
  "useSkill",
9429
9939
  "listFiles",
@@ -9541,7 +10051,7 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
9541
10051
  "Deduplicate targets. Do NOT explain or answer - ONLY return the JSON targets."
9542
10052
  ].join("\n");
9543
10053
  }
9544
- var CODE_SEARCH_SCHEMA, searchTool, queryTool, extractTool, delegateTool;
10054
+ var CODE_SEARCH_SCHEMA, searchTool, queryTool, extractTool, delegateTool, analyzeAllTool;
9545
10055
  var init_vercel = __esm({
9546
10056
  "src/tools/vercel.js"() {
9547
10057
  "use strict";
@@ -9549,6 +10059,7 @@ var init_vercel = __esm({
9549
10059
  init_query();
9550
10060
  init_extract();
9551
10061
  init_delegate();
10062
+ init_analyzeAll();
9552
10063
  init_common();
9553
10064
  init_error_types();
9554
10065
  CODE_SEARCH_SCHEMA = {
@@ -9566,7 +10077,7 @@ var init_vercel = __esm({
9566
10077
  searchTool = (options = {}) => {
9567
10078
  const {
9568
10079
  sessionId,
9569
- maxTokens = 1e4,
10080
+ maxTokens = 2e4,
9570
10081
  debug = false,
9571
10082
  outline = false,
9572
10083
  searchDelegate = false
@@ -9791,7 +10302,7 @@ var init_vercel = __esm({
9791
10302
  });
9792
10303
  };
9793
10304
  delegateTool = (options = {}) => {
9794
- const { debug = false, timeout = 300, cwd, allowedFolders, enableBash = false, bashConfig, architectureFileName } = options;
10305
+ const { debug = false, timeout = 300, cwd, allowedFolders, enableBash = false, bashConfig, architectureFileName, enableMcp = false, mcpConfig = null, mcpConfigPath = null } = options;
9795
10306
  return tool2({
9796
10307
  name: "delegate",
9797
10308
  description: delegateDescription,
@@ -9850,12 +10361,54 @@ var init_vercel = __esm({
9850
10361
  enableBash,
9851
10362
  bashConfig,
9852
10363
  architectureFileName,
9853
- searchDelegate
10364
+ searchDelegate,
10365
+ enableMcp,
10366
+ mcpConfig,
10367
+ mcpConfigPath
9854
10368
  });
9855
10369
  return result;
9856
10370
  }
9857
10371
  });
9858
10372
  };
10373
+ analyzeAllTool = (options = {}) => {
10374
+ const { sessionId, debug = false } = options;
10375
+ return tool2({
10376
+ name: "analyze_all",
10377
+ description: analyzeAllDescription,
10378
+ inputSchema: analyzeAllSchema,
10379
+ execute: async ({ question, path: path9 }) => {
10380
+ try {
10381
+ let searchPath = path9 || ".";
10382
+ if (path9 && options.cwd) {
10383
+ const resolvedPaths = parseAndResolvePaths(path9, options.cwd);
10384
+ if (resolvedPaths.length > 0) {
10385
+ searchPath = resolvedPaths[0];
10386
+ }
10387
+ }
10388
+ if (debug) {
10389
+ console.error(`[analyze_all] Starting analysis`);
10390
+ console.error(`[analyze_all] Question: ${question}`);
10391
+ console.error(`[analyze_all] Path: ${searchPath}`);
10392
+ }
10393
+ const result = await analyzeAll({
10394
+ question,
10395
+ path: searchPath,
10396
+ sessionId,
10397
+ debug,
10398
+ cwd: options.cwd,
10399
+ allowedFolders: options.allowedFolders,
10400
+ provider: options.provider,
10401
+ model: options.model,
10402
+ tracer: options.tracer
10403
+ });
10404
+ return result;
10405
+ } catch (error) {
10406
+ console.error("Error executing analyze_all:", error);
10407
+ return formatErrorForAI(error);
10408
+ }
10409
+ }
10410
+ });
10411
+ };
9859
10412
  }
9860
10413
  });
9861
10414
 
@@ -10377,6 +10930,38 @@ function parseSimpleCommand(command) {
10377
10930
  isComplex: false
10378
10931
  };
10379
10932
  }
10933
+ const stripQuotedContent = (str) => {
10934
+ let result = "";
10935
+ let inQuotes2 = false;
10936
+ let quoteChar2 = "";
10937
+ for (let i = 0; i < str.length; i++) {
10938
+ const char = str[i];
10939
+ const nextChar = str[i + 1];
10940
+ if (!inQuotes2 && char === "\\" && nextChar !== void 0) {
10941
+ i++;
10942
+ continue;
10943
+ }
10944
+ if (inQuotes2 && quoteChar2 === '"' && char === "\\" && nextChar !== void 0) {
10945
+ i++;
10946
+ continue;
10947
+ }
10948
+ if (!inQuotes2 && (char === '"' || char === "'")) {
10949
+ inQuotes2 = true;
10950
+ quoteChar2 = char;
10951
+ continue;
10952
+ }
10953
+ if (inQuotes2 && char === quoteChar2) {
10954
+ inQuotes2 = false;
10955
+ quoteChar2 = "";
10956
+ continue;
10957
+ }
10958
+ if (!inQuotes2) {
10959
+ result += char;
10960
+ }
10961
+ }
10962
+ return result;
10963
+ };
10964
+ const strippedForOperators = stripQuotedContent(trimmed);
10380
10965
  const complexPatterns = [
10381
10966
  /\|/,
10382
10967
  // Pipes
@@ -10402,7 +10987,7 @@ function parseSimpleCommand(command) {
10402
10987
  // Brace expansion like {a,b} or {1..10} (but not find {} placeholders)
10403
10988
  ];
10404
10989
  for (const pattern of complexPatterns) {
10405
- if (pattern.test(trimmed)) {
10990
+ if (pattern.test(strippedForOperators)) {
10406
10991
  return {
10407
10992
  success: false,
10408
10993
  error: "Complex shell commands with pipes, operators, or redirections are not supported for security reasons",
@@ -10417,17 +11002,17 @@ function parseSimpleCommand(command) {
10417
11002
  let current = "";
10418
11003
  let inQuotes = false;
10419
11004
  let quoteChar = "";
10420
- let escaped = false;
10421
11005
  for (let i = 0; i < trimmed.length; i++) {
10422
11006
  const char = trimmed[i];
10423
11007
  const nextChar = i + 1 < trimmed.length ? trimmed[i + 1] : "";
10424
- if (escaped) {
10425
- current += char;
10426
- escaped = false;
11008
+ if (!inQuotes && char === "\\" && nextChar) {
11009
+ current += nextChar;
11010
+ i++;
10427
11011
  continue;
10428
11012
  }
10429
- if (char === "\\" && !inQuotes) {
10430
- escaped = true;
11013
+ if (inQuotes && quoteChar === '"' && char === "\\" && nextChar) {
11014
+ current += nextChar;
11015
+ i++;
10431
11016
  continue;
10432
11017
  }
10433
11018
  if (!inQuotes && (char === '"' || char === "'")) {
@@ -10760,8 +11345,97 @@ var init_bashPermissions = __esm({
10760
11345
  });
10761
11346
  return result;
10762
11347
  }
11348
+ /**
11349
+ * Split a complex command into component commands by operators
11350
+ *
11351
+ * ## Escape Handling (Security-Critical)
11352
+ *
11353
+ * This function intentionally PRESERVES escape sequences (both backslash AND
11354
+ * escaped character) in the output. This is step 1 of a 2-step parsing process:
11355
+ *
11356
+ * 1. _splitComplexCommand: Splits by operators, PRESERVES escapes → `echo "test\" && b"`
11357
+ * 2. parseCommand: Interprets escapes in each component → args: ['test" && b']
11358
+ *
11359
+ * This differs from stripQuotedContent() in bashCommandUtils.js which REMOVES
11360
+ * escapes entirely (for operator detection only).
11361
+ *
11362
+ * The security rationale: if we stripped escapes here, `\"` would become `"`,
11363
+ * potentially causing incorrect quote boundary detection and allowing operator
11364
+ * injection. By preserving escapes, parseCommand() can correctly interpret them.
11365
+ *
11366
+ * See bashCommandUtils.js module header for the full escape handling architecture.
11367
+ *
11368
+ * @private
11369
+ * @param {string} command - Complex command to split
11370
+ * @returns {string[]} Array of component commands (with escapes preserved)
11371
+ */
11372
+ _splitComplexCommand(command) {
11373
+ const components = [];
11374
+ let current = "";
11375
+ let inQuotes = false;
11376
+ let quoteChar = "";
11377
+ let i = 0;
11378
+ while (i < command.length) {
11379
+ const char = command[i];
11380
+ const nextChar = command[i + 1] || "";
11381
+ if (!inQuotes && char === "\\") {
11382
+ current += char;
11383
+ if (nextChar) {
11384
+ current += nextChar;
11385
+ i += 2;
11386
+ } else {
11387
+ i++;
11388
+ }
11389
+ continue;
11390
+ }
11391
+ if (inQuotes && quoteChar === '"' && char === "\\" && nextChar) {
11392
+ current += char + nextChar;
11393
+ i += 2;
11394
+ continue;
11395
+ }
11396
+ if (!inQuotes && (char === '"' || char === "'")) {
11397
+ inQuotes = true;
11398
+ quoteChar = char;
11399
+ current += char;
11400
+ i++;
11401
+ continue;
11402
+ }
11403
+ if (inQuotes && char === quoteChar) {
11404
+ inQuotes = false;
11405
+ quoteChar = "";
11406
+ current += char;
11407
+ i++;
11408
+ continue;
11409
+ }
11410
+ if (!inQuotes) {
11411
+ if (char === "&" && nextChar === "&" || char === "|" && nextChar === "|") {
11412
+ if (current.trim()) {
11413
+ components.push(current.trim());
11414
+ }
11415
+ current = "";
11416
+ i += 2;
11417
+ continue;
11418
+ }
11419
+ if (char === "|") {
11420
+ if (current.trim()) {
11421
+ components.push(current.trim());
11422
+ }
11423
+ current = "";
11424
+ i++;
11425
+ continue;
11426
+ }
11427
+ }
11428
+ current += char;
11429
+ i++;
11430
+ }
11431
+ if (current.trim()) {
11432
+ components.push(current.trim());
11433
+ }
11434
+ return components;
11435
+ }
10763
11436
  /**
10764
11437
  * Check a complex command against complex patterns in allow/deny lists
11438
+ * Also supports auto-allowing commands where all components are individually allowed
10765
11439
  * @private
10766
11440
  * @param {string} command - Complex command to check
10767
11441
  * @returns {Object} Permission result
@@ -10816,6 +11490,85 @@ var init_bashPermissions = __esm({
10816
11490
  return result;
10817
11491
  }
10818
11492
  }
11493
+ const components = this._splitComplexCommand(command);
11494
+ if (this.debug) {
11495
+ console.log(`[BashPermissions] Checking ${components.length} command components: ${JSON.stringify(components)}`);
11496
+ }
11497
+ if (components.length > 1) {
11498
+ const componentResults = [];
11499
+ let allAllowed = true;
11500
+ let deniedComponent = null;
11501
+ let deniedReason = null;
11502
+ for (const component of components) {
11503
+ const parsed = parseCommand(component);
11504
+ if (parsed.error || parsed.isComplex) {
11505
+ if (this.debug) {
11506
+ console.log(`[BashPermissions] Component "${component}" is complex or has error: ${parsed.error}`);
11507
+ }
11508
+ allAllowed = false;
11509
+ deniedComponent = component;
11510
+ deniedReason = parsed.error || "Component contains nested complex constructs";
11511
+ break;
11512
+ }
11513
+ if (matchesAnyPattern(parsed, this.denyPatterns)) {
11514
+ if (this.debug) {
11515
+ console.log(`[BashPermissions] Component "${component}" matches deny pattern`);
11516
+ }
11517
+ allAllowed = false;
11518
+ deniedComponent = component;
11519
+ deniedReason = "Component matches deny pattern";
11520
+ break;
11521
+ }
11522
+ if (!matchesAnyPattern(parsed, this.allowPatterns)) {
11523
+ if (this.debug) {
11524
+ console.log(`[BashPermissions] Component "${component}" not in allow list`);
11525
+ }
11526
+ allAllowed = false;
11527
+ deniedComponent = component;
11528
+ deniedReason = "Component not in allow list";
11529
+ break;
11530
+ }
11531
+ componentResults.push({ component, parsed, allowed: true });
11532
+ }
11533
+ if (allAllowed) {
11534
+ if (this.debug) {
11535
+ console.log(`[BashPermissions] ALLOWED - all ${components.length} components passed individual checks`);
11536
+ }
11537
+ const result = {
11538
+ allowed: true,
11539
+ command,
11540
+ isComplex: true,
11541
+ allowedByComponents: true,
11542
+ components: componentResults
11543
+ };
11544
+ this.recordBashEvent("permission.allowed", {
11545
+ command,
11546
+ isComplex: true,
11547
+ allowedByComponents: true,
11548
+ componentCount: components.length
11549
+ });
11550
+ return result;
11551
+ } else {
11552
+ if (this.debug) {
11553
+ console.log(`[BashPermissions] DENIED - component "${deniedComponent}" failed: ${deniedReason}`);
11554
+ }
11555
+ const result = {
11556
+ allowed: false,
11557
+ reason: `Component "${deniedComponent}" not allowed: ${deniedReason}`,
11558
+ command,
11559
+ isComplex: true,
11560
+ failedComponent: deniedComponent
11561
+ };
11562
+ this.recordBashEvent("permission.denied", {
11563
+ command,
11564
+ reason: "component_not_allowed",
11565
+ failedComponent: deniedComponent,
11566
+ componentReason: deniedReason,
11567
+ isComplex: true
11568
+ });
11569
+ return result;
11570
+ }
11571
+ }
10819
11572
  if (this.debug) {
10820
11573
  console.log(`[BashPermissions] DENIED - no matching complex pattern found`);
10821
11574
  }
@@ -18565,6 +19318,13 @@ function createWrappedTools(baseTools) {
18565
19318
  baseTools.delegateTool.execute
18566
19319
  );
18567
19320
  }
19321
+ if (baseTools.analyzeAllTool) {
19322
+ wrappedTools.analyzeAllToolInstance = wrapToolWithEmitter(
19323
+ baseTools.analyzeAllTool,
19324
+ "analyze_all",
19325
+ baseTools.analyzeAllTool.execute
19326
+ );
19327
+ }
18568
19328
  if (baseTools.bashTool) {
18569
19329
  wrappedTools.bashToolInstance = wrapToolWithEmitter(
18570
19330
  baseTools.bashTool,
@@ -19471,6 +20231,9 @@ function createTools(configOptions) {
19471
20231
  if (configOptions.enableDelegate && isToolAllowed("delegate")) {
19472
20232
  tools2.delegateTool = delegateTool(configOptions);
19473
20233
  }
20234
+ if (isToolAllowed("analyze_all")) {
20235
+ tools2.analyzeAllTool = analyzeAllTool(configOptions);
20236
+ }
19474
20237
  if (configOptions.enableBash && isToolAllowed("bash")) {
19475
20238
  tools2.bashTool = bashTool(configOptions);
19476
20239
  }
@@ -67393,14 +68156,14 @@ function calculateCompactionStats(originalMessages, compactedMessages) {
67393
68156
  const compactedCount = compactedMessages.length;
67394
68157
  const removed = originalCount - compactedCount;
67395
68158
  const reductionPercent = originalCount > 0 ? (removed / originalCount * 100).toFixed(1) : 0;
67396
- const estimateTokens = (msgs) => {
68159
+ const estimateTokens2 = (msgs) => {
67397
68160
  return msgs.reduce((sum, msg) => {
67398
68161
  const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
67399
68162
  return sum + Math.ceil(content.length / 4);
67400
68163
  }, 0);
67401
68164
  };
67402
- const originalTokens = estimateTokens(originalMessages);
67403
- const compactedTokens = estimateTokens(compactedMessages);
68165
+ const originalTokens = estimateTokens2(originalMessages);
68166
+ const compactedTokens = estimateTokens2(compactedMessages);
67404
68167
  const tokensSaved = originalTokens - compactedTokens;
67405
68168
  return {
67406
68169
  originalCount,
@@ -67481,7 +68244,7 @@ async function truncateIfNeeded(content, tokenCounter, sessionId, maxTokens) {
67481
68244
  if (tokenCount <= limit) {
67482
68245
  return { truncated: false, content };
67483
68246
  }
67484
- const maxChars = limit * CHARS_PER_TOKEN;
68247
+ const maxChars = limit * CHARS_PER_TOKEN2;
67485
68248
  const truncatedContent = content.substring(0, maxChars);
67486
68249
  let tempFilePath = null;
67487
68250
  let fileError = null;
@@ -67520,12 +68283,12 @@ ${truncatedContent}
67520
68283
  error: fileError || void 0
67521
68284
  };
67522
68285
  }
67523
- var DEFAULT_MAX_OUTPUT_TOKENS, CHARS_PER_TOKEN;
68286
+ var DEFAULT_MAX_OUTPUT_TOKENS, CHARS_PER_TOKEN2;
67524
68287
  var init_outputTruncator = __esm({
67525
68288
  "src/agent/outputTruncator.js"() {
67526
68289
  "use strict";
67527
68290
  DEFAULT_MAX_OUTPUT_TOKENS = 2e4;
67528
- CHARS_PER_TOKEN = 4;
68291
+ CHARS_PER_TOKEN2 = 4;
67529
68292
  }
67530
68293
  });
67531
68294
 
@@ -69385,6 +70148,9 @@ var init_ProbeAgent = __esm({
69385
70148
  if (this.enableDelegate && wrappedTools.delegateToolInstance && isToolAllowed("delegate")) {
69386
70149
  this.toolImplementations.delegate = wrappedTools.delegateToolInstance;
69387
70150
  }
70151
+ if (wrappedTools.analyzeAllToolInstance && isToolAllowed("analyze_all")) {
70152
+ this.toolImplementations.analyze_all = wrappedTools.analyzeAllToolInstance;
70153
+ }
69388
70154
  if (isToolAllowed("listFiles")) {
69389
70155
  this.toolImplementations.listFiles = listFilesToolInstance;
69390
70156
  }
@@ -70584,6 +71350,10 @@ Workspace: ${this.allowedFolders.join(", ")}`;
70584
71350
  }
70585
71351
  if (this.enableDelegate && isToolAllowed("delegate")) {
70586
71352
  toolDefinitions += `${delegateToolDefinition}
71353
+ `;
71354
+ }
71355
+ if (isToolAllowed("analyze_all")) {
71356
+ toolDefinitions += `${analyzeAllToolDefinition}
70587
71357
  `;
70588
71358
  }
70589
71359
  let toolExamples = "";
@@ -70647,6 +71417,9 @@ The configuration is loaded from src/config.js lines 15-25 which contains the da
70647
71417
  if (this.enableDelegate && isToolAllowed("delegate")) {
70648
71418
  availableToolsList += "- delegate: Delegate big distinct tasks to specialized probe subagents.\n";
70649
71419
  }
71420
+ if (isToolAllowed("analyze_all")) {
71421
+ availableToolsList += "- analyze_all: Process ALL data matching a query using map-reduce (for aggregate questions needing 100% coverage).\n";
71422
+ }
70650
71423
  if (this.enableBash && isToolAllowed("bash")) {
70651
71424
  availableToolsList += "- bash: Execute bash commands for system operations.\n";
70652
71425
  }
@@ -71033,6 +71806,9 @@ You are working with a repository located at: ${searchDirectory}
71033
71806
  let lastFormatErrorType = null;
71034
71807
  let sameFormatErrorCount = 0;
71035
71808
  const MAX_REPEATED_FORMAT_ERRORS = 3;
71809
+ let lastNoToolResponse = null;
71810
+ let sameResponseCount = 0;
71811
+ const MAX_REPEATED_IDENTICAL_RESPONSES = 3;
71036
71812
  while (currentIteration < maxIterations && !completionAttempted) {
71037
71813
  currentIteration++;
71038
71814
  if (this.cancelled) throw new Error("Request was cancelled by the user");
@@ -71070,11 +71846,11 @@ You are working with a repository located at: ${searchDirectory}
71070
71846
  let maxResponseTokens = this.maxResponseTokens;
71071
71847
  if (!maxResponseTokens) {
71072
71848
  maxResponseTokens = 4e3;
71073
- if (this.model.includes("opus") || this.model.includes("sonnet") || this.model.startsWith("gpt-4-")) {
71849
+ if (this.model && this.model.includes("opus") || this.model && this.model.includes("sonnet") || this.model && this.model.startsWith("gpt-4-")) {
71074
71850
  maxResponseTokens = 8192;
71075
- } else if (this.model.startsWith("gpt-4o")) {
71851
+ } else if (this.model && this.model.startsWith("gpt-4o")) {
71076
71852
  maxResponseTokens = 8192;
71077
- } else if (this.model.startsWith("gemini")) {
71853
+ } else if (this.model && this.model.startsWith("gemini")) {
71078
71854
  maxResponseTokens = 32e3;
71079
71855
  }
71080
71856
  }
@@ -71514,6 +72290,26 @@ ${errorXml}
71514
72290
  }
71515
72291
  break;
71516
72292
  }
72293
+ if (lastNoToolResponse !== null && assistantResponseContent === lastNoToolResponse) {
72294
+ sameResponseCount++;
72295
+ if (sameResponseCount >= MAX_REPEATED_IDENTICAL_RESPONSES) {
72296
+ let cleanedResponse = assistantResponseContent;
72297
+ cleanedResponse = cleanedResponse.replace(/<thinking>[\s\S]*?<\/thinking>/gi, "").trim();
72298
+ cleanedResponse = cleanedResponse.replace(/<thinking>[\s\S]*$/gi, "").trim();
72299
+ const hasSubstantialContent = cleanedResponse.length > 50 && !cleanedResponse.includes("<api_call>") && !cleanedResponse.includes("<tool_name>") && !cleanedResponse.includes("<function>");
72300
+ if (hasSubstantialContent) {
72301
+ if (this.debug) {
72302
+ console.log(`[DEBUG] Same response repeated ${sameResponseCount} times - accepting as final answer (${cleanedResponse.length} chars)`);
72303
+ }
72304
+ finalResult = cleanedResponse;
72305
+ completionAttempted = true;
72306
+ break;
72307
+ }
72308
+ }
72309
+ } else {
72310
+ lastNoToolResponse = assistantResponseContent;
72311
+ sameResponseCount = 1;
72312
+ }
71517
72313
  currentMessages.push({ role: "assistant", content: assistantResponseContent });
71518
72314
  const unrecognizedTool = detectUnrecognizedToolCall(assistantResponseContent, validTools);
71519
72315
  let reminderContent;
@@ -71586,10 +72382,35 @@ Or if your previous response already contains a complete, direct answer (not a t
71586
72382
 
71587
72383
  Note: <attempt_complete></attempt_complete> reuses your PREVIOUS assistant message as the final answer. Only use this if that message was already a valid, complete response to the user's question.`;
71588
72384
  }
71589
- currentMessages.push({
71590
- role: "user",
71591
- content: reminderContent
71592
- });
72385
+ const prevUserMsgIndex = currentMessages.length - 2;
72386
+ const prevUserMsg = currentMessages[prevUserMsgIndex];
72387
+ const isExistingReminder = prevUserMsg && prevUserMsg.role === "user" && (prevUserMsg.content.includes("Please use one of the available tools") || prevUserMsg.content.includes("<tool_result>"));
72388
+ if (isExistingReminder && sameResponseCount > 1) {
72389
+ const prevAssistantIndex = prevUserMsgIndex - 1;
72390
+ const hasSystemMessage2 = currentMessages.length > 0 && currentMessages[0].role === "system";
72391
+ const minValidIndex = hasSystemMessage2 ? 1 : 0;
72392
+ const canSafelyRemove = prevAssistantIndex >= minValidIndex && currentMessages[prevAssistantIndex] && currentMessages[prevAssistantIndex].role === "assistant" && currentMessages.length - 2 >= (hasSystemMessage2 ? 2 : 1);
72393
+ if (canSafelyRemove) {
72394
+ currentMessages.splice(prevAssistantIndex, 2);
72395
+ if (this.debug) {
72396
+ console.log(`[DEBUG] Removed duplicate assistant+reminder pair (iteration ${currentIteration}, same response #${sameResponseCount})`);
72397
+ }
72398
+ } else if (this.debug) {
72399
+ console.log(`[DEBUG] Skipped deduplication: pattern validation failed (prevAssistantIndex=${prevAssistantIndex}, arrayLength=${currentMessages.length})`);
72400
+ }
72401
+ const iterationHint = `
72402
+
72403
+ (Attempt #${sameResponseCount}: Your previous ${sameResponseCount} responses were identical. If you have a complete answer, use <attempt_complete></attempt_complete> to finalize it.)`;
72404
+ currentMessages.push({
72405
+ role: "user",
72406
+ content: reminderContent + iterationHint
72407
+ });
72408
+ } else {
72409
+ currentMessages.push({
72410
+ role: "user",
72411
+ content: reminderContent
72412
+ });
72413
+ }
71593
72414
  if (this.debug) {
71594
72415
  if (unrecognizedTool) {
71595
72416
  console.log(`[DEBUG] Unrecognized tool '${unrecognizedTool}' used. Providing error feedback.`);