| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- /**
- * Shared helpers for building the API cache-key prefix (systemPrompt,
- * userContext, systemContext) for query() calls.
- *
- * Lives in its own file because it imports from context.ts and
- * constants/prompts.ts, which are high in the dependency graph. Putting
- * these imports in systemPrompt.ts or sideQuestion.ts (both reachable
- * from commands.ts) would create cycles. Only entrypoint-layer files
- * import from here (QueryEngine.ts, cli/print.ts).
- */
- import type { Command } from '../commands.js'
- import { getSystemPrompt } from '../constants/prompts.js'
- import { getSystemContext, getUserContext } from '../context.js'
- import type { MCPServerConnection } from '../services/mcp/types.js'
- import type { AppState } from '../state/AppStateStore.js'
- import type { Tools, ToolUseContext } from '../Tool.js'
- import type { AgentDefinition } from '../tools/AgentTool/loadAgentsDir.js'
- import type { Message } from '../types/message.js'
- import { createAbortController } from './abortController.js'
- import type { FileStateCache } from './fileStateCache.js'
- import type { CacheSafeParams } from './forkedAgent.js'
- import { getMainLoopModel } from './model/model.js'
- import { asSystemPrompt } from './systemPromptType.js'
- import {
- shouldEnableThinkingByDefault,
- type ThinkingConfig,
- } from './thinking.js'
- /**
- * Fetch the three context pieces that form the API cache-key prefix:
- * systemPrompt parts, userContext, systemContext.
- *
- * When customSystemPrompt is set, the default getSystemPrompt build and
- * getSystemContext are skipped — the custom prompt replaces the default
- * entirely, and systemContext would be appended to a default that isn't
- * being used.
- *
- * Callers assemble the final systemPrompt from defaultSystemPrompt (or
- * customSystemPrompt) + optional extras + appendSystemPrompt. QueryEngine
- * injects coordinator userContext and memory-mechanics prompt on top;
- * sideQuestion's fallback uses the base result directly.
- */
- export async function fetchSystemPromptParts({
- tools,
- mainLoopModel,
- additionalWorkingDirectories,
- mcpClients,
- customSystemPrompt,
- }: {
- tools: Tools
- mainLoopModel: string
- additionalWorkingDirectories: string[]
- mcpClients: MCPServerConnection[]
- customSystemPrompt: string | undefined
- }): Promise<{
- defaultSystemPrompt: string[]
- userContext: { [k: string]: string }
- systemContext: { [k: string]: string }
- }> {
- const [defaultSystemPrompt, userContext, systemContext] = await Promise.all([
- customSystemPrompt !== undefined
- ? Promise.resolve([])
- : getSystemPrompt(
- tools,
- mainLoopModel,
- additionalWorkingDirectories,
- mcpClients,
- ),
- getUserContext(),
- customSystemPrompt !== undefined ? Promise.resolve({}) : getSystemContext(),
- ])
- return { defaultSystemPrompt, userContext, systemContext }
- }
- /**
- * Build CacheSafeParams from raw inputs when getLastCacheSafeParams() is null.
- *
- * Used by the SDK side_question handler (print.ts) on resume before a turn
- * completes — there's no stopHooks snapshot yet. Mirrors the system prompt
- * assembly in QueryEngine.ts:ask() so the rebuilt prefix matches what the
- * main loop will send, preserving the cache hit in the common case.
- *
- * May still miss the cache if the main loop applies extras this path doesn't
- * know about (coordinator mode, memory-mechanics prompt). That's acceptable —
- * the alternative is returning null and failing the side question entirely.
- */
- export async function buildSideQuestionFallbackParams({
- tools,
- commands,
- mcpClients,
- messages,
- readFileState,
- getAppState,
- setAppState,
- customSystemPrompt,
- appendSystemPrompt,
- thinkingConfig,
- agents,
- }: {
- tools: Tools
- commands: Command[]
- mcpClients: MCPServerConnection[]
- messages: Message[]
- readFileState: FileStateCache
- getAppState: () => AppState
- setAppState: (f: (prev: AppState) => AppState) => void
- customSystemPrompt: string | undefined
- appendSystemPrompt: string | undefined
- thinkingConfig: ThinkingConfig | undefined
- agents: AgentDefinition[]
- }): Promise<CacheSafeParams> {
- const mainLoopModel = getMainLoopModel()
- const appState = getAppState()
- const { defaultSystemPrompt, userContext, systemContext } =
- await fetchSystemPromptParts({
- tools,
- mainLoopModel,
- additionalWorkingDirectories: Array.from(
- appState.toolPermissionContext.additionalWorkingDirectories.keys(),
- ),
- mcpClients,
- customSystemPrompt,
- })
- const systemPrompt = asSystemPrompt([
- ...(customSystemPrompt !== undefined
- ? [customSystemPrompt]
- : defaultSystemPrompt),
- ...(appendSystemPrompt ? [appendSystemPrompt] : []),
- ])
- // Strip in-progress assistant message (stop_reason === null) — same guard
- // as btw.tsx. The SDK can fire side_question mid-turn.
- const last = messages.at(-1)
- const forkContextMessages =
- last?.type === 'assistant' && last.message.stop_reason === null
- ? messages.slice(0, -1)
- : messages
- const toolUseContext: ToolUseContext = {
- options: {
- commands,
- debug: false,
- mainLoopModel,
- tools,
- verbose: false,
- thinkingConfig:
- thinkingConfig ??
- (shouldEnableThinkingByDefault() !== false
- ? { type: 'adaptive' }
- : { type: 'disabled' }),
- mcpClients,
- mcpResources: {},
- isNonInteractiveSession: true,
- agentDefinitions: { activeAgents: agents, allAgents: [] },
- customSystemPrompt,
- appendSystemPrompt,
- },
- abortController: createAbortController(),
- readFileState,
- getAppState,
- setAppState,
- messages: forkContextMessages,
- setInProgressToolUseIDs: () => {},
- setResponseLength: () => {},
- updateFileHistoryState: () => {},
- updateAttributionState: () => {},
- }
- return {
- systemPrompt,
- userContext,
- systemContext,
- toolUseContext,
- forkContextMessages,
- }
- }
|