( params: QueryParams, consumedCommandUuids: string[], consumedAutonomyCommands: QueuedCommand[], )
| 391 | } |
| 392 | |
| 393 | async function* queryLoop( |
| 394 | params: QueryParams, |
| 395 | consumedCommandUuids: string[], |
| 396 | consumedAutonomyCommands: QueuedCommand[], |
| 397 | ): AsyncGenerator< |
| 398 | | StreamEvent |
| 399 | | RequestStartEvent |
| 400 | | Message |
| 401 | | TombstoneMessage |
| 402 | | ToolUseSummaryMessage, |
| 403 | Terminal |
| 404 | > { |
| 405 | // Immutable params — never reassigned during the query loop. |
| 406 | const { |
| 407 | systemPrompt, |
| 408 | userContext, |
| 409 | systemContext, |
| 410 | canUseTool, |
| 411 | fallbackModel, |
| 412 | querySource, |
| 413 | maxTurns, |
| 414 | skipCacheWrite, |
| 415 | } = params |
| 416 | const deps = params.deps ?? productionDeps() |
| 417 | |
| 418 | // Mutable cross-iteration state. The loop body destructures this at the top |
| 419 | // of each iteration so reads stay bare-name (`messages`, `toolUseContext`). |
| 420 | // Continue sites write `state = { ... }` instead of 9 separate assignments. |
| 421 | let state: State = { |
| 422 | messages: params.messages, |
| 423 | toolUseContext: params.toolUseContext, |
| 424 | maxOutputTokensOverride: params.maxOutputTokensOverride, |
| 425 | autoCompactTracking: undefined, |
| 426 | stopHookActive: undefined, |
| 427 | maxOutputTokensRecoveryCount: 0, |
| 428 | hasAttemptedReactiveCompact: false, |
| 429 | turnCount: 1, |
| 430 | pendingToolUseSummary: undefined, |
| 431 | transition: undefined, |
| 432 | } |
| 433 | const budgetTracker = feature('TOKEN_BUDGET') ? createBudgetTracker() : null |
| 434 | |
| 435 | // task_budget.remaining tracking across compaction boundaries. Undefined |
| 436 | // until first compact fires — while context is uncompacted the server can |
| 437 | // see the full history and handles the countdown from {total} itself (see |
| 438 | // api/api/sampling/prompt/renderer.py:292). After a compact, the server sees |
| 439 | // only the summary and would under-count spend; remaining tells it the |
| 440 | // pre-compact final window that got summarized away. Cumulative across |
| 441 | // multiple compacts: each subtracts the final context at that compact's |
| 442 | // trigger point. Loop-local (not on State) to avoid touching the 7 continue |
| 443 | // sites. |
| 444 | let taskBudgetRemaining: number | undefined |
| 445 | |
| 446 | // Snapshot immutable env/statsig/session state once at entry. See QueryConfig |
| 447 | // for what's included and why feature() gates are intentionally excluded. |
| 448 | const config = buildQueryConfig() |
| 449 | |
| 450 | // Fired once per user turn — the prompt is invariant across loop iterations, |
no test coverage detected