Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/cli/print.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2849,7 +2849,7 @@ function runHeadlessStreaming(
}))
}
if (abortController) {
abortController.abort()
abortController.abort('interrupt')
}
suggestionState.abortController?.abort()
suggestionState.abortController = null
Expand Down
36 changes: 20 additions & 16 deletions src/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from './services/compact/autoCompact.js'
import { consumeCompactionRequest } from './utils/memoryPressure.js'
import { buildPostCompactMessages } from './services/compact/compact.js'
import type { PendingCacheEdits } from './services/compact/microCompact.js'
/* eslint-disable @typescript-eslint/no-require-imports */
const reactiveCompact = feature('REACTIVE_COMPACT')
? (require('./services/compact/reactiveCompact.js') as typeof import('./services/compact/reactiveCompact.js'))
Expand Down Expand Up @@ -177,7 +178,7 @@ function* yieldMissingToolResultBlocks(
* rules, ye will be punished with an entire day of debugging and hair pulling.
*/
const MAX_OUTPUT_TOKENS_RECOVERY_LIMIT = 3
const MAX_CONTINUATION_NUDGES = 3
const MAX_CONTINUATION_NUDGES = 20

function formatAutoCompactRetryDelay(delayMs: number): string {
const totalSeconds = Math.max(1, Math.ceil(delayMs / 1000))
Expand Down Expand Up @@ -500,21 +501,24 @@ async function* queryLoop(
queryCheckpoint('query_snip_end')
}

// Apply microcompact before autocompact
queryCheckpoint('query_microcompact_start')
const microcompactResult = await deps.microcompact(
messagesForQuery,
toolUseContext,
querySource,
)
messagesForQuery = microcompactResult.messages
// For cached microcompact (cache editing), defer boundary message until after
// the API response so we can use actual cache_deleted_input_tokens.
// Gated behind feature() so the string is eliminated from external builds.
const pendingCacheEdits = feature('CACHED_MICROCOMPACT')
? microcompactResult.compactionInfo?.pendingCacheEdits
: undefined
queryCheckpoint('query_microcompact_end')
// Only run microcompact if compaction is not explicitly disabled via settings
let pendingCacheEdits: PendingCacheEdits | undefined = undefined
if (getGlobalConfig().maxMessagesCompactionThreshold !== 'off') {
queryCheckpoint('query_microcompact_start')
const microcompactResult = await deps.microcompact(
messagesForQuery,
toolUseContext,
querySource,
)
messagesForQuery = microcompactResult.messages
pendingCacheEdits = feature('CACHED_MICROCOMPACT')
? microcompactResult.compactionInfo?.pendingCacheEdits
: undefined
queryCheckpoint('query_microcompact_end')
} else {
queryCheckpoint('query_microcompact_start')
queryCheckpoint('query_microcompact_end')
}

// Project the collapsed context view and maybe commit more collapses.
// Runs BEFORE autocompact so that if collapse gets us under the
Expand Down
20 changes: 12 additions & 8 deletions src/utils/continuation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import { tokenCountWithEstimation } from './tokens.js'

export const CONTINUATION_SIGNALS = [
// English: Action-transition phrases (requires intent + action)
/\bso now (i|let me|we) (need to|have to|should|must|will) (do|create|write|edit|update|fix|implement|add|run|check|make|build|set up|start|begin|apply|identify|inspect|analyze|review|search)\b/i,
/\bnow i('ll| will) (do|create|write|edit|update|fix|implement|add|run|check|make|build|set up|go|proceed|start|begin|apply|identify|inspect|analyze|review|search)\b/i,
/\bi (will|shall|now|need to|have to|must|should) (now )?(do|create|write|edit|update|fix|implement|add|run|check|make|build|set up|go|proceed|start|begin|apply|identify|inspect|analyze|review|search)\b/i,
/\blet me (go ahead and |now )?(do|create|write|edit|update|fix|implement|add|run|check|make|build|set up|proceed|start|begin|apply|update|create|identify|inspect|analyze|review|search|summarize)\b/i,
/\btime to (do|create|write|edit|update|fix|implement|add|run|check|make|build|get started|begin|start|inspect|analyze|review|search)\b/i,
/\b(moving on to|next step is to|starting to|proceeding to|applying (the|these) changes|inspecting|analyzing|reviewing|searching)\b/i,
/\bso now (i|let me|we) (need to|have to|should|must|will) (do|create|write|edit|update|fix|implement|add|run|check|make|build|set up|start|begin|apply|identify|inspect|analyze|review|search|process|download|upload|convert|compile|train|evaluate|test|continue|generate|extract|merge|deploy|install|configure|refactor|optimize)\b/i,
/\bnow i('ll| will) (do|create|write|edit|update|fix|implement|add|run|check|make|build|set up|go|proceed|start|begin|apply|identify|inspect|analyze|review|search|process|download|upload|convert|compile|train|evaluate|test|continue|generate|extract|merge|deploy|install|configure|refactor|optimize)\b/i,
/\bi (will|shall|now|need to|have to|must|should) (now )?(do|create|write|edit|update|fix|implement|add|run|check|make|build|set up|go|proceed|start|begin|apply|identify|inspect|analyze|review|search|process|download|upload|convert|compile|train|evaluate|test|continue|generate|extract|merge|deploy|install|configure|refactor|optimize)\b/i,
/\blet me (go ahead and |now )?(do|create|write|edit|update|fix|implement|add|run|check|make|build|set up|proceed|start|begin|apply|update|create|identify|inspect|analyze|review|search|summarize|process|download|upload|convert|compile|train|evaluate|test|continue|generate|extract|merge|deploy|install|configure|refactor|optimize)\b/i,
/\btime to (do|create|write|edit|update|fix|implement|add|run|check|make|build|get started|begin|start|inspect|analyze|review|search|process|download|upload|convert|compile|train|evaluate|test|continue|generate|extract|merge|deploy|install|configure|refactor|optimize)\b/i,
/\b(moving on to|next step is to|starting to|proceeding to|continuing with|applying (the|these) changes|inspecting|analyzing|reviewing|searching|processing|downloading|uploading|converting|compiling|training|evaluating|testing|extracting|merging|deploying|installing|configuring|refactoring|optimizing)\b/i,
// French: Support for common continuation phrasing (relaxed boundaries for accents and apostrophes)
/(^|\s)(je passe (à|au)|ensuite|l'étape suivante est de|je continue avec|au suivant|passons à|je reviens vers vous|je suis en train d'|je vais maintenant)(\s|$|[a-zà-ÿ])/i,
/(^|\s)(je (vais|dois|dois maintenant|vais maintenant) (faire|créer|écrire|modifier|ajouter|tester|vérifier|lancer|exécuter|procéder|démarrer|commencer|identifier|analyser|inspecter|revoir|chercher))(\s|$|[a-zà-ÿ])/i,
Expand Down Expand Up @@ -101,8 +101,9 @@ export function analyzeContinuationIntent(
if (hasTerminalPunctuation) {
const strongIntent = /\b(i (will|shall|need to|must|should|now)|let (me|us)|je (vais|reviens)|passons à|moving on to|next step is to)\b/i.test(lowerText) ||
/je suis en train d'/i.test(lowerText) || /◻/.test(lastText)
const presentProgressive = /\bnow \w+ing\b/i.test(lateText) || /\b\w+ing\s+(the|this|these|a|an|remaining|next|files|data|model)\b/i.test(lateText)
const endsWithColon = /:\s*$/.test(lastText)
if (strongIntent || endsWithColon) {
if (strongIntent || endsWithColon || presentProgressive) {
return { shouldNudge: true, reason: 'continuation_signal' }
}
} else {
Expand All @@ -111,7 +112,10 @@ export function analyzeContinuationIntent(
}

// 3. Completion Marker Guard (Final check for sound, completed messages)
if (COMPLETION_MARKERS.test(lowerText)) {
// Only block continuation if no continuation signal is present (prevents false
// positives when "complete" or "done" appears mid-sentence, e.g. "The download
// is complete. Now processing the files...")
if (COMPLETION_MARKERS.test(lowerText) && (hasLateContinuationSignal || !CONTINUATION_SIGNALS.some(re => re.test(lowerText)))) {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
return { shouldNudge: false }
}

Expand Down