@probelabs/probe 0.6.0-rc200 → 0.6.0-rc202

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 (46) hide show
  1. package/README.md +31 -1
  2. package/bin/binaries/probe-v0.6.0-rc202-aarch64-apple-darwin.tar.gz +0 -0
  3. package/bin/binaries/probe-v0.6.0-rc202-aarch64-unknown-linux-musl.tar.gz +0 -0
  4. package/bin/binaries/probe-v0.6.0-rc202-x86_64-apple-darwin.tar.gz +0 -0
  5. package/bin/binaries/probe-v0.6.0-rc202-x86_64-pc-windows-msvc.zip +0 -0
  6. package/bin/binaries/probe-v0.6.0-rc202-x86_64-unknown-linux-musl.tar.gz +0 -0
  7. package/build/agent/ProbeAgent.d.ts +19 -1
  8. package/build/agent/ProbeAgent.js +310 -14
  9. package/build/agent/index.js +8615 -394
  10. package/build/agent/probeTool.js +2 -2
  11. package/build/agent/schemaUtils.js +37 -18
  12. package/build/agent/shared/prompts.js +17 -0
  13. package/build/agent/skills/formatting.js +23 -0
  14. package/build/agent/skills/parser.js +162 -0
  15. package/build/agent/skills/registry.js +185 -0
  16. package/build/agent/skills/tools.js +65 -0
  17. package/build/agent/tools.js +44 -0
  18. package/build/delegate.js +27 -7
  19. package/build/tools/common.js +17 -4
  20. package/build/tools/system-message.js +4 -4
  21. package/build/tools/vercel.js +243 -36
  22. package/cjs/agent/ProbeAgent.cjs +8769 -583
  23. package/cjs/index.cjs +8794 -608
  24. package/index.d.ts +16 -0
  25. package/package.json +2 -1
  26. package/scripts/postinstall.js +10 -4
  27. package/src/agent/ProbeAgent.d.ts +19 -1
  28. package/src/agent/ProbeAgent.js +310 -14
  29. package/src/agent/index.js +21 -1
  30. package/src/agent/probeTool.js +2 -2
  31. package/src/agent/schemaUtils.js +37 -18
  32. package/src/agent/shared/prompts.js +17 -0
  33. package/src/agent/skills/formatting.js +23 -0
  34. package/src/agent/skills/parser.js +162 -0
  35. package/src/agent/skills/registry.js +185 -0
  36. package/src/agent/skills/tools.js +65 -0
  37. package/src/agent/tools.js +44 -0
  38. package/src/delegate.js +27 -7
  39. package/src/tools/common.js +17 -4
  40. package/src/tools/system-message.js +4 -4
  41. package/src/tools/vercel.js +243 -36
  42. package/bin/binaries/probe-v0.6.0-rc200-aarch64-apple-darwin.tar.gz +0 -0
  43. package/bin/binaries/probe-v0.6.0-rc200-aarch64-unknown-linux-musl.tar.gz +0 -0
  44. package/bin/binaries/probe-v0.6.0-rc200-x86_64-apple-darwin.tar.gz +0 -0
  45. package/bin/binaries/probe-v0.6.0-rc200-x86_64-pc-windows-msvc.zip +0 -0
  46. package/bin/binaries/probe-v0.6.0-rc200-x86_64-unknown-linux-musl.tar.gz +0 -0
package/build/delegate.js CHANGED
@@ -173,11 +173,18 @@ const delegationManager = new DelegationManager();
173
173
  * @param {number} [options.maxIterations=30] - Maximum tool iterations allowed
174
174
  * @param {string} [options.parentSessionId=null] - Parent session ID for tracking
175
175
  * @param {string} [options.path] - Search directory path (inherited from parent)
176
+ * @param {string[]} [options.allowedFolders] - Allowed folders (inherited from parent)
176
177
  * @param {string} [options.provider] - AI provider (inherited from parent)
177
178
  * @param {string} [options.model] - AI model (inherited from parent)
178
179
  * @param {Object} [options.tracer=null] - Telemetry tracer instance
179
180
  * @param {boolean} [options.enableBash=false] - Enable bash tool (inherited from parent)
180
181
  * @param {Object} [options.bashConfig] - Bash configuration (inherited from parent)
182
+ * @param {string} [options.architectureFileName] - Architecture context filename to embed from repo root
183
+ * @param {string} [options.promptType='code-researcher'] - Prompt type for the subagent
184
+ * @param {Array<string>|null} [options.allowedTools] - Allowed tools for the subagent (null = default)
185
+ * @param {boolean} [options.disableTools=false] - Disable all tools for the subagent
186
+ * @param {boolean} [options.searchDelegate] - Use delegated search in the subagent
187
+ * @param {Object|string} [options.schema] - Optional JSON schema to enforce response format
181
188
  * @returns {Promise<string>} The response from the delegate agent
182
189
  */
183
190
  export async function delegate({
@@ -189,10 +196,17 @@ export async function delegate({
189
196
  tracer = null,
190
197
  parentSessionId = null,
191
198
  path = null,
199
+ allowedFolders = null,
192
200
  provider = null,
193
201
  model = null,
194
202
  enableBash = false,
195
- bashConfig = null
203
+ bashConfig = null,
204
+ architectureFileName = null,
205
+ promptType = 'code-researcher',
206
+ allowedTools = null,
207
+ disableTools = false,
208
+ searchDelegate = undefined,
209
+ schema = null
196
210
  }) {
197
211
  if (!task || typeof task !== 'string') {
198
212
  throw new Error('Task parameter is required and must be a string');
@@ -226,7 +240,7 @@ export async function delegate({
226
240
  console.error(`[DELEGATE] Remaining iterations for subagent: ${remainingIterations}`);
227
241
  console.error(`[DELEGATE] Timeout configured: ${timeout} seconds`);
228
242
  console.error(`[DELEGATE] Global active delegations: ${stats.globalActive}/${stats.maxConcurrent}`);
229
- console.error(`[DELEGATE] Using ProbeAgent SDK with code-researcher prompt`);
243
+ console.error(`[DELEGATE] Using ProbeAgent SDK with ${promptType} prompt`);
230
244
  }
231
245
  // Create a new ProbeAgent instance for the delegated task
232
246
  // IMPORTANT: We pass both path and cwd set to the same value (workspace root)
@@ -234,24 +248,29 @@ export async function delegate({
234
248
  // affect the subagent's path resolution - subagents always work from workspace root.
235
249
  const subagent = new ProbeAgent({
236
250
  sessionId,
237
- promptType: 'code-researcher', // Clean prompt, not inherited from parent
238
- enableDelegate: false, // Explicitly disable delegation to prevent recursion
251
+ promptType, // Clean prompt, not inherited from parent
252
+ enableDelegate: false, // Explicitly disable delegation to prevent recursion
239
253
  disableMermaidValidation: true, // Faster processing
240
254
  disableJsonValidation: true, // Simpler responses
241
255
  maxIterations: remainingIterations,
242
256
  debug,
243
257
  tracer,
244
258
  path, // Workspace root (from delegateTool)
259
+ allowedFolders, // Inherit allowed folders to keep architecture context root consistent
245
260
  cwd: path, // Explicitly set cwd to workspace root to prevent path doubling
246
261
  provider, // Inherit from parent
247
262
  model, // Inherit from parent
248
263
  enableBash, // Inherit from parent
249
- bashConfig // Inherit from parent
264
+ bashConfig, // Inherit from parent
265
+ architectureFileName,
266
+ allowedTools,
267
+ disableTools,
268
+ searchDelegate
250
269
  });
251
270
 
252
271
  if (debug) {
253
272
  console.error(`[DELEGATE] Created subagent with session ${sessionId}`);
254
- console.error(`[DELEGATE] Subagent config: promptType=code-researcher, enableDelegate=false, maxIterations=${remainingIterations}`);
273
+ console.error(`[DELEGATE] Subagent config: promptType=${promptType}, enableDelegate=false, maxIterations=${remainingIterations}`);
255
274
  }
256
275
 
257
276
  // Set up timeout with proper cleanup
@@ -269,7 +288,8 @@ export async function delegate({
269
288
  });
270
289
 
271
290
  // Execute the task with timeout
272
- const answerPromise = subagent.answer(task);
291
+ const answerOptions = schema ? { schema } : undefined;
292
+ const answerPromise = answerOptions ? subagent.answer(task, [], answerOptions) : subagent.answer(task);
273
293
  const response = await Promise.race([answerPromise, timeoutPromise]);
274
294
 
275
295
  // Clear timeout immediately after race completes to prevent memory leak
@@ -30,6 +30,14 @@ export const delegateSchema = z.object({
30
30
  task: z.string().describe('The task to delegate to a subagent. Be specific about what needs to be accomplished.')
31
31
  });
32
32
 
33
+ export const listSkillsSchema = z.object({
34
+ filter: z.string().optional().describe('Optional substring filter to match skill names or descriptions.')
35
+ });
36
+
37
+ export const useSkillSchema = z.object({
38
+ name: z.string().describe('Skill name to load and activate.')
39
+ });
40
+
33
41
  export const bashSchema = z.object({
34
42
  command: z.string().describe('The bash command to execute'),
35
43
  workingDirectory: z.string().optional().describe('Directory to execute the command in (optional)'),
@@ -103,7 +111,8 @@ export const attemptCompletionSchema = {
103
111
 
104
112
  export const searchToolDefinition = `
105
113
  ## search
106
- Description: Search code in the repository using Elasticsearch query syntax (except field based queries, e.g. "filename:..." NOT supported).
114
+ Description: Search code in the repository. You may provide a free-form question about the code or a concise Elasticsearch-style keyword query (field based queries, e.g. "filename:..." NOT supported).
115
+ Note: This tool may internally use a dedicated search subagent when search delegation is enabled. This is separate from the "delegate" tool and does not require an explicit delegate call.
107
116
 
108
117
  You need to focus on main keywords when constructing the query, and always use elastic search syntax like OR AND and brackets to group keywords.
109
118
 
@@ -113,7 +122,7 @@ You need to focus on main keywords when constructing the query, and always use e
113
122
  - Once data is returned, it's cached and won't return on next runs (this is expected behavior)
114
123
 
115
124
  Parameters:
116
- - query: (required) Search query with Elasticsearch syntax. Use quotes for exact matches ("functionName"), AND/OR for boolean logic, - for negation, + for important terms.
125
+ - query: (required) Search query. Free-form questions are accepted, but for best results prefer Elasticsearch-style syntax with quotes for exact matches ("functionName"), AND/OR for boolean logic, - for negation, + for important terms.
117
126
  - path: (optional, default: '.') Path to search in. All dependencies located in /dep folder, under language sub folders, like this: "/dep/go/github.com/owner/repo", "/dep/js/package_name", or "/dep/rust/cargo_name" etc.
118
127
 
119
128
  **Workflow:** Always start with search, then use extract for detailed context when needed.
@@ -319,7 +328,7 @@ User: Check system info
319
328
  </examples>
320
329
  `;
321
330
 
322
- export const searchDescription = 'Search code in the repository using Elasticsearch-like query syntax. Use this tool first for any code-related questions.';
331
+ export const 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.';
323
332
  export const queryDescription = 'Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.';
324
333
  export const 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.';
325
334
  export const 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.';
@@ -332,6 +341,8 @@ export const DEFAULT_VALID_TOOLS = [
332
341
  'query',
333
342
  'extract',
334
343
  'delegate',
344
+ 'listSkills',
345
+ 'useSkill',
335
346
  'listFiles',
336
347
  'searchFiles',
337
348
  'implement',
@@ -366,6 +377,8 @@ function getValidParamsForTool(toolName) {
366
377
  query: querySchema,
367
378
  extract: extractSchema,
368
379
  delegate: delegateSchema,
380
+ listSkills: listSkillsSchema,
381
+ useSkill: useSkillSchema,
369
382
  bash: bashSchema,
370
383
  attempt_completion: attemptCompletionSchema,
371
384
  edit: editSchema,
@@ -628,4 +641,4 @@ export function resolveTargetPath(target, cwd) {
628
641
  }
629
642
 
630
643
  return filePart + suffix;
631
- }
644
+ }
@@ -11,7 +11,7 @@ You are Probe, a specialized code intelligence assistant. Your objective is to a
11
11
  1. **Tool-First Always:** Immediately use tools for any code-related query. Do not guess or use general knowledge.
12
12
  2. **Mandatory Path:** ALL tool calls (\`search\`, \`query\`, \`extract\`) MUST include the \`path\` argument. Use \`"."\` for the whole project, specific directories/files (e.g., \`"src/api"\`, \`"pkg/utils/helpers.py"\`), or dependency syntax (e.g., \`"go:github.com/gin-gonic/gin"\`, \`"js:@ai-sdk/anthropic"\`, \`"rust:serde"\`).
13
13
  3. **Start with \`search\`:**
14
- * **Keywords are Key:** Formulate queries like you would in Elasticsearch. Use specific keywords, boolean operators (\`AND\`, \`OR\`, \`NOT\`), and exact phrases (\`""\`). This is NOT a simple text search.
14
+ * **Free-form is OK, keywords are better:** You may ask a free-form question about the code, but for best results use Elasticsearch-style keywords, boolean operators (\`AND\`, \`OR\`, \`NOT\`), and exact phrases (\`""\`).
15
15
  * **Iterate if Needed:** If initial results are too broad or insufficient, **repeat the exact same \`search\` query** to get the next page of results (pagination). Reuse the \`sessionID\` if provided by the previous identical search. If results are irrelevant, refine the keywords (add terms, use \`NOT\`, try synonyms).
16
16
  4. **Analyze & Refine:** Review \`search\` results (snippets, file paths).
17
17
  * Use \`query\` if you need code based on *structure* (AST patterns) within specific files/directories identified by \`search\`.
@@ -23,8 +23,8 @@ You are Probe, a specialized code intelligence assistant. Your objective is to a
23
23
 
24
24
  * \`search\`
25
25
  * **Purpose:** Find relevant code snippets/files using keyword-based search (like Elasticsearch). Locate named symbols. Search project code or dependencies.
26
- * **Syntax:** \`query\` (Elasticsearch-like string: keywords, \`AND\`, \`OR\`, \`NOT\`, \`""\` exact phrases), \`path\` (Mandatory: \`"."\`, \`"path/to/dir"\`, \`"path/to/file.ext"\`, \`"go:pkg"\`, \`"js:npm_module"\`, \`"rust:crate"\`), \`exact\` (Optional: Set to \`true\` for case-insensitive exact matching without tokenization).
27
- * **Features:** Returns snippets/paths. Supports pagination (repeat query). Caching via \`sessionID\` (reuse if returned). Use \`exact\` flag when you need precise matching of terms.
26
+ * **Syntax:** \`query\` (free-form question or Elasticsearch-like keywords: \`AND\`, \`OR\`, \`NOT\`, \`""\` exact phrases), \`path\` (Mandatory: \`"."\`, \`"path/to/dir"\`, \`"path/to/file.ext"\`, \`"go:pkg"\`, \`"js:npm_module"\`, \`"rust:crate"\`), \`exact\` (Optional: Set to \`true\` for case-insensitive exact matching without tokenization).
27
+ * **Features:** Returns snippets/paths. Supports pagination (repeat query). Caching via \`sessionID\` (reuse if returned). Use \`exact\` flag when you need precise matching of terms. This tool may internally delegate code discovery when configured; this is not the same as the \`delegate\` tool and requires no explicit call.
28
28
  * \`query\`
29
29
  * **Purpose:** Find code by its *structure* (AST patterns) within specific files/directories, typically after \`search\`.
30
30
  * **Syntax:** \`pattern\` (ast-grep pattern), \`language\` (e.g., "go", "python").
@@ -124,4 +124,4 @@ For GitHub-compatible mermaid diagrams, avoid single quotes and parentheses in n
124
124
  - **requirement**: system requirements, traceability
125
125
  - **packet**: network protocols, data packets
126
126
  - **zenuml**: UML sequence diagrams
127
- </mermaid-instructions>`
127
+ </mermaid-instructions>`
@@ -10,6 +10,125 @@ import { extract } from '../extract.js';
10
10
  import { delegate } from '../delegate.js';
11
11
  import { searchSchema, querySchema, extractSchema, delegateSchema, searchDescription, queryDescription, extractDescription, delegateDescription, parseTargets, parseAndResolvePaths, resolveTargetPath } from './common.js';
12
12
 
13
+ const CODE_SEARCH_SCHEMA = {
14
+ type: 'object',
15
+ properties: {
16
+ targets: {
17
+ type: 'array',
18
+ items: { type: 'string' },
19
+ description: 'List of file targets like "path/to/file.ext#Symbol" or "path/to/file.ext:line" or "path/to/file.ext:start-end".'
20
+ }
21
+ },
22
+ required: ['targets'],
23
+ additionalProperties: false
24
+ };
25
+
26
+ function normalizeTargets(targets) {
27
+ if (!Array.isArray(targets)) return [];
28
+ const seen = new Set();
29
+ const normalized = [];
30
+
31
+ for (const target of targets) {
32
+ if (typeof target !== 'string') continue;
33
+ const trimmed = target.trim();
34
+ if (!trimmed || seen.has(trimmed)) continue;
35
+ seen.add(trimmed);
36
+ normalized.push(trimmed);
37
+ }
38
+
39
+ return normalized;
40
+ }
41
+
42
+ function extractJsonSnippet(text) {
43
+ const jsonBlockMatch = text.match(/```json\s*([\s\S]*?)```/i);
44
+ if (jsonBlockMatch) {
45
+ return jsonBlockMatch[1].trim();
46
+ }
47
+
48
+ const anyBlockMatch = text.match(/```\s*([\s\S]*?)```/);
49
+ if (anyBlockMatch) {
50
+ return anyBlockMatch[1].trim();
51
+ }
52
+
53
+ const firstBrace = text.indexOf('{');
54
+ const lastBrace = text.lastIndexOf('}');
55
+ if (firstBrace !== -1 && lastBrace > firstBrace) {
56
+ return text.slice(firstBrace, lastBrace + 1);
57
+ }
58
+
59
+ const firstBracket = text.indexOf('[');
60
+ const lastBracket = text.lastIndexOf(']');
61
+ if (firstBracket !== -1 && lastBracket > firstBracket) {
62
+ return text.slice(firstBracket, lastBracket + 1);
63
+ }
64
+
65
+ return null;
66
+ }
67
+
68
+ function fallbackTargetsFromText(text) {
69
+ const candidates = [];
70
+ const lines = text.split(/\r?\n/);
71
+
72
+ for (const line of lines) {
73
+ let cleaned = line.trim();
74
+ if (!cleaned) continue;
75
+ cleaned = cleaned.replace(/^[-*•\d.)\s]+/, '').trim();
76
+ if (!cleaned) continue;
77
+ const token = cleaned.split(/\s+/)[0];
78
+ if (/[#:]|[/\\]|\\./.test(token)) {
79
+ candidates.push(token);
80
+ }
81
+ }
82
+
83
+ return candidates;
84
+ }
85
+
86
+ function parseDelegatedTargets(rawResponse) {
87
+ if (!rawResponse || typeof rawResponse !== 'string') return [];
88
+ const trimmed = rawResponse.trim();
89
+
90
+ const tryParse = (text) => {
91
+ try {
92
+ return JSON.parse(text);
93
+ } catch {
94
+ return null;
95
+ }
96
+ };
97
+
98
+ let parsed = tryParse(trimmed);
99
+ if (!parsed) {
100
+ const snippet = extractJsonSnippet(trimmed);
101
+ if (snippet) {
102
+ parsed = tryParse(snippet);
103
+ }
104
+ }
105
+
106
+ if (parsed) {
107
+ if (Array.isArray(parsed)) {
108
+ return normalizeTargets(parsed);
109
+ }
110
+ if (Array.isArray(parsed.targets)) {
111
+ return normalizeTargets(parsed.targets);
112
+ }
113
+ }
114
+
115
+ return normalizeTargets(fallbackTargetsFromText(trimmed));
116
+ }
117
+
118
+ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, allowTests }) {
119
+ return [
120
+ 'You are a code-search subagent. Your ONLY job is to return ALL relevant code locations.',
121
+ 'Use ONLY the search tool. Do NOT answer the question or explain anything.',
122
+ 'Return ONLY valid JSON with this shape: {"targets": ["path/to/file.ext#Symbol", "path/to/file.ext:line", "path/to/file.ext:start-end"]}.',
123
+ 'Prefer #Symbol when a function/class name is clear; otherwise use line numbers.',
124
+ `Search query: ${searchQuery}`,
125
+ `Search path(s): ${searchPath}`,
126
+ `Options: exact=${exact ? 'true' : 'false'}, language=${language || 'auto'}, allow_tests=${allowTests ? 'true' : 'false'}.`,
127
+ 'Run additional searches only if needed to capture all relevant locations.',
128
+ 'Deduplicate targets.'
129
+ ].join('\n');
130
+ }
131
+
13
132
  /**
14
133
  * Search tool generator
15
134
  *
@@ -20,58 +139,138 @@ import { searchSchema, querySchema, extractSchema, delegateSchema, searchDescrip
20
139
  * @returns {Object} Configured search tool
21
140
  */
22
141
  export const searchTool = (options = {}) => {
23
- const { sessionId, maxTokens = 10000, debug = false, outline = false } = options;
142
+ const {
143
+ sessionId,
144
+ maxTokens = 10000,
145
+ debug = false,
146
+ outline = false,
147
+ searchDelegate = false
148
+ } = options;
24
149
 
25
150
  return tool({
26
151
  name: 'search',
27
- description: searchDescription,
152
+ description: searchDelegate
153
+ ? `${searchDescription} (delegates code search to a subagent and returns extracted code blocks)`
154
+ : searchDescription,
28
155
  inputSchema: searchSchema,
29
156
  execute: async ({ query: searchQuery, path, allow_tests, exact, maxTokens: paramMaxTokens, language }) => {
30
- try {
31
- // Use parameter maxTokens if provided, otherwise use the default
32
- const effectiveMaxTokens = paramMaxTokens || maxTokens;
157
+ // Use parameter maxTokens if provided, otherwise use the default
158
+ const effectiveMaxTokens = paramMaxTokens || maxTokens;
33
159
 
34
- // Parse and resolve paths (supports comma-separated and relative paths)
35
- let searchPaths;
36
- if (path) {
37
- searchPaths = parseAndResolvePaths(path, options.cwd);
38
- }
160
+ // Parse and resolve paths (supports comma-separated and relative paths)
161
+ let searchPaths;
162
+ if (path) {
163
+ searchPaths = parseAndResolvePaths(path, options.cwd);
164
+ }
39
165
 
40
- // Default to cwd or '.' if no paths provided
41
- if (!searchPaths || searchPaths.length === 0) {
42
- searchPaths = [options.cwd || '.'];
43
- }
166
+ // Default to cwd or '.' if no paths provided
167
+ if (!searchPaths || searchPaths.length === 0) {
168
+ searchPaths = [options.cwd || '.'];
169
+ }
44
170
 
45
- // Join paths with space for CLI (probe search supports multiple paths)
46
- const searchPath = searchPaths.join(' ');
171
+ // Join paths with space for CLI (probe search supports multiple paths)
172
+ const searchPath = searchPaths.join(' ');
173
+
174
+ const searchOptions = {
175
+ query: searchQuery,
176
+ path: searchPath,
177
+ cwd: options.cwd, // Working directory for resolving relative paths
178
+ allowTests: allow_tests ?? true,
179
+ exact,
180
+ json: false,
181
+ maxTokens: effectiveMaxTokens,
182
+ session: sessionId, // Pass session ID if provided
183
+ language // Pass language parameter if provided
184
+ };
185
+
186
+ // Add outline format if enabled
187
+ if (outline) {
188
+ searchOptions.format = 'outline-xml';
189
+ }
47
190
 
191
+ const runRawSearch = async () => {
48
192
  if (debug) {
49
193
  console.error(`Executing search with query: "${searchQuery}", path: "${searchPath}", exact: ${exact ? 'true' : 'false'}, language: ${language || 'all'}, session: ${sessionId || 'none'}`);
50
194
  }
195
+ return await search(searchOptions);
196
+ };
197
+
198
+ if (!searchDelegate) {
199
+ try {
200
+ return await runRawSearch();
201
+ } catch (error) {
202
+ console.error('Error executing search command:', error);
203
+ return `Error executing search command: ${error.message}`;
204
+ }
205
+ }
51
206
 
52
- const searchOptions = {
53
- query: searchQuery,
54
- path: searchPath,
55
- cwd: options.cwd, // Working directory for resolving relative paths
56
- allowTests: allow_tests ?? true,
207
+ try {
208
+ if (debug) {
209
+ console.error(`Delegating search with query: "${searchQuery}", path: "${searchPath}"`);
210
+ }
211
+
212
+ const delegateTask = buildSearchDelegateTask({
213
+ searchQuery,
214
+ searchPath,
57
215
  exact,
58
- json: false,
59
- maxTokens: effectiveMaxTokens,
60
- session: sessionId, // Pass session ID if provided
61
- language // Pass language parameter if provided
216
+ language,
217
+ allowTests: allow_tests ?? true
218
+ });
219
+
220
+ const runDelegation = () => delegate({
221
+ task: delegateTask,
222
+ debug,
223
+ parentSessionId: sessionId,
224
+ path: options.allowedFolders?.[0] || options.cwd || '.',
225
+ allowedFolders: options.allowedFolders,
226
+ provider: options.provider || null,
227
+ model: options.model || null,
228
+ tracer: options.tracer || null,
229
+ enableBash: false,
230
+ bashConfig: null,
231
+ architectureFileName: options.architectureFileName || null,
232
+ promptType: 'code-searcher',
233
+ allowedTools: ['search', 'attempt_completion'],
234
+ searchDelegate: false,
235
+ schema: CODE_SEARCH_SCHEMA
236
+ });
237
+
238
+ const delegateResult = options.tracer?.withSpan
239
+ ? await options.tracer.withSpan('search.delegate', runDelegation, {
240
+ 'search.query': searchQuery,
241
+ 'search.path': searchPath
242
+ })
243
+ : await runDelegation();
244
+
245
+ const targets = parseDelegatedTargets(delegateResult);
246
+ if (!targets.length) {
247
+ if (debug) {
248
+ console.error('Delegated search returned no targets; falling back to raw search');
249
+ }
250
+ return await runRawSearch();
251
+ }
252
+
253
+ const effectiveCwd = options.cwd || '.';
254
+ const resolvedTargets = targets.map(target => resolveTargetPath(target, effectiveCwd));
255
+ const extractOptions = {
256
+ files: resolvedTargets,
257
+ cwd: effectiveCwd,
258
+ allowTests: allow_tests ?? true
62
259
  };
63
260
 
64
- // Add outline format if enabled
65
261
  if (outline) {
66
- searchOptions.format = 'outline-xml';
262
+ extractOptions.format = 'xml';
67
263
  }
68
264
 
69
- const results = await search(searchOptions);
70
-
71
- return results;
265
+ return await extract(extractOptions);
72
266
  } catch (error) {
73
- console.error('Error executing search command:', error);
74
- return `Error executing search command: ${error.message}`;
267
+ console.error('Delegated search failed, falling back to raw search:', error);
268
+ try {
269
+ return await runRawSearch();
270
+ } catch (fallbackError) {
271
+ console.error('Error executing search command:', fallbackError);
272
+ return `Error executing search command: ${fallbackError.message}`;
273
+ }
75
274
  }
76
275
  }
77
276
  });
@@ -250,16 +449,17 @@ export const extractTool = (options = {}) => {
250
449
  * @param {string[]} [options.allowedFolders] - Allowed folders for workspace isolation
251
450
  * @param {boolean} [options.enableBash=false] - Enable bash tool for sub-agents
252
451
  * @param {Object} [options.bashConfig] - Bash configuration (allow/deny patterns)
452
+ * @param {string} [options.architectureFileName] - Architecture context filename to embed from repo root
253
453
  * @returns {Object} Configured delegate tool
254
454
  */
255
455
  export const delegateTool = (options = {}) => {
256
- const { debug = false, timeout = 300, cwd, allowedFolders, enableBash = false, bashConfig } = options;
456
+ const { debug = false, timeout = 300, cwd, allowedFolders, enableBash = false, bashConfig, architectureFileName } = options;
257
457
 
258
458
  return tool({
259
459
  name: 'delegate',
260
460
  description: delegateDescription,
261
461
  inputSchema: delegateSchema,
262
- execute: async ({ task, currentIteration, maxIterations, parentSessionId, path, provider, model, tracer }) => {
462
+ execute: async ({ task, currentIteration, maxIterations, parentSessionId, path, provider, model, tracer, searchDelegate }) => {
263
463
  // Validate required parameters - throw errors for consistency
264
464
  if (!task || typeof task !== 'string') {
265
465
  throw new Error('Task parameter is required and must be a non-empty string');
@@ -295,6 +495,10 @@ export const delegateTool = (options = {}) => {
295
495
  throw new TypeError('model must be a string, null, or undefined');
296
496
  }
297
497
 
498
+ if (searchDelegate !== undefined && typeof searchDelegate !== 'boolean') {
499
+ throw new TypeError('searchDelegate must be a boolean if provided');
500
+ }
501
+
298
502
  // Determine the path to pass to the subagent
299
503
  // NOTE: Delegation intentionally uses DIFFERENT priority than other tools.
300
504
  //
@@ -331,14 +535,17 @@ export const delegateTool = (options = {}) => {
331
535
  maxIterations: maxIterations || 30,
332
536
  parentSessionId,
333
537
  path: effectivePath,
538
+ allowedFolders,
334
539
  provider,
335
540
  model,
336
541
  tracer,
337
542
  enableBash,
338
- bashConfig
543
+ bashConfig,
544
+ architectureFileName,
545
+ searchDelegate
339
546
  });
340
547
 
341
548
  return result;
342
549
  }
343
550
  });
344
- };
551
+ };