MCPcopy Create free account
hub / github.com/QodeXcli/QodeX / buildInitialMessages

Method buildInitialMessages

src/agent/loop.ts:367–634  ·  view source on GitHub ↗

Build the initial system message with full context.

(
    userPrompt: string,
    mode: ToolExecutionMode['mode'],
    modelId: string,
    /** Sub-agent role name. Affects system prompt selection. */
    role?: string,
    /** Tools actually available to this run; if provided, drives the "Available tools:"
     *  line so a restricted sub-agent doesn't think it has tools the registry filters out. */
    allowedTools?: string[],
  )

Source from the content-addressed store, hash-verified

365 // messages.session_id carries a FK to sessions.id, so the sub-agent's FIRST
366 // recordTurn would fail with "FOREIGN KEY constraint failed", killing every
367 // delegation instantly. Create the parent row up front (idempotent).
368 getSessionStore().ensureSession(opts.sessionId, this.cwd, modelUsed);
369
370 // Role-specific tool restriction (allow-list). Built-in policy:
371 // - vision role: only vision_analyze + read-only browser/file/web tools
372 // - subagent role: everything except `task` (no recursion) — handled by mode=subagent
373 const roleConfig = (this.config as any).roles?.[role] as { allowedTools?: string[] } | undefined;
374 let allowedTools = roleConfig?.allowedTools;
375 if (!allowedTools && role === 'vision') {
376 // Sensible default for vision role: it should ANALYZE images, not refactor code.
377 // Keep it focused — read-only inspection + the vision tool.
378 allowedTools = [
379 'vision_analyze',
380 'read_file', 'ls', 'glob', 'grep',
381 'browser_navigate', 'browser_screenshot', 'browser_get_text',
382 'browser_console', 'browser_wait_for', 'browser_close',
383 'web_fetch',
384 ];
385 } else if (!allowedTools && role === 'scout') {
386 // Scout role: read-only reconnaissance for the `gather` tool. Collects data/
387 // context for the parent to decide on — must NEVER mutate. Restricted to
388 // read-only inspection tools (any missing one just degrades gracefully).
389 allowedTools = [
390 'read_file', 'ls', 'glob', 'grep', 'semantic_search',
391 'project_overview', 'explain_codebase', 'data_flow', 'analyze_impact', 'find_dead_code',
392 'git_status', 'git_diff', 'git_log',
393 'db_schema', 'db_query', 'openapi_digest', 'backend_routemap',
394 'web_search', 'web_fetch', 'media_probe',
395 'project_recall', 'recall',
396 ];
397 }
398
399 // Build a fresh message stack — sub-agent has NO prior context. The system
400 // prompt is selected per-role; built-ins (subagent, vision) have crafted defaults.
401 // Custom roles can override via config.roles.<name>.systemPrompt.
402 //
403 // CRITICAL: we pass `allowedTools` so the system prompt lists ONLY the tools the
404 // sub-agent can actually call. Small/quantized models will hallucinate they don't
405 // have web_search if it isn't named in prose — see Sub-Agent persona fix.
406 const initialMessages = await this.buildInitialMessages(prompt, 'subagent', dispatchModel.model, role, allowedTools);
407
408 for await (const event of this.run(initialMessages, opts.sessionId, {
409 mode: { mode: 'subagent', allowedTools },
410 signal: opts.signal,
411 // run() reads `maxIterationsOverride` (not `maxIterations`) to cap the child's
412 // budget — passing the wrong key silently left every sub-agent on the parent's
413 // full iteration budget. Pass BOTH so the cap actually applies.
414 maxIterationsOverride: opts.maxIterations,
415 maxIterations: opts.maxIterations,
416 modelOverride: { provider: dispatchModel.provider, model: dispatchModel.model },
417 // A sub-agent runs unattended: it has no interactive user to answer a
418 // permission prompt. Without an askUser, any tool that prompts would call
419 // `ctx.askUser` === undefined and crash the whole delegation. Supply a
420 // conservative auto-decline so a gated tool degrades to a tool-level refusal
421 // (which the child can adapt to) instead of killing the run.
422 askUser: async () => 'no',
423 } as any)) {
424 if (event.type === 'tool_call_start') toolCallsRun += 1;

Callers 4

runHeadlessFunction · 0.95
runTurnMethod · 0.95
runSubagentMethod · 0.95
AppFunction · 0.80

Calls 15

classifyForPromptMethod · 0.95
extractAttachedDirFunction · 0.85
detectProjectInfoFunction · 0.85
loadProjectRulesFunction · 0.85
buildDirectoryTreeFunction · 0.85
getGitBranchFunction · 0.85
detectProjectSignalsFunction · 0.85
loadTrellisContextFunction · 0.85
resolveMemoryModeFunction · 0.85
getSessionStoreFunction · 0.85
selectInjectedFactsFunction · 0.85
getBuiltinRolePromptFunction · 0.85

Tested by

no test coverage detected