diff --git a/src/__tests__/bugfixes.test.ts b/src/__tests__/bugfixes.test.ts index 8924fb00e5..5862506a14 100644 --- a/src/__tests__/bugfixes.test.ts +++ b/src/__tests__/bugfixes.test.ts @@ -135,6 +135,27 @@ describe('Agent loop continuation nudge', () => { expect(analyzeContinuationIntent("Here is the code:\n```typescript\nfunction test() {").shouldNudge).toBe(true) }) + test('non-Claude continuation verbs and mid-text completion markers (issue #1707)', async () => { + const { analyzeContinuationIntent } = await import('../utils/continuation.js') + + // Mid-text completion marker must NOT suppress a later continuation intent. + // The completion-marker guard now only inspects the final sentence. + expect(analyzeContinuationIntent("The download is complete. Now I will install the dependencies").shouldNudge).toBe(true) + expect(analyzeContinuationIntent("Step one finished. I will now deploy the service").shouldNudge).toBe(true) + + // Action verbs common to non-Claude models, gated behind intent framing. + expect(analyzeContinuationIntent("Let me compile the project").shouldNudge).toBe(true) + expect(analyzeContinuationIntent("I will now train the model").shouldNudge).toBe(true) + + // Unpunctuated present-progressive action reads as in-progress work. + expect(analyzeContinuationIntent("Now downloading the dataset").shouldNudge).toBe(true) + + // A genuinely completed message whose LAST sentence is a completion marker + // must still be suppressed, even if an action gerund appears earlier. + expect(analyzeContinuationIntent("Finished installing the packages. All set.").shouldNudge).toBe(false) + expect(analyzeContinuationIntent("Downloaded the files and the migration is complete.").shouldNudge).toBe(false) + }) + test('nudge creates a meta user message to continue', async () => { const content = await file('query.ts').text() diff --git a/src/utils/continuation.ts b/src/utils/continuation.ts index bee357bc36..34e0c2fa9c 100644 --- a/src/utils/continuation.ts +++ b/src/utils/continuation.ts @@ -13,6 +13,15 @@ export const CONTINUATION_SIGNALS = [ /\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, + // English: Action verbs common to non-Claude models, gated behind explicit + // first-person intent framing so they only fire as a continuation signal + // (e.g. "I will now deploy the service", "Let me install the deps"). + /\b(i (will|'ll|shall|need to|have to|must|should|am going to|am about to)( now)?|let me( now)?|now i('ll| will)?)\s+(process|download|upload|compile|train|evaluate|extract|merge|deploy|install|configure|optimize|refactor|generate|fetch|validate|parse|scan|test)\b/i, + // English: Present-progressive action statements signal in-progress work. + // These intentionally do NOT override terminal punctuation downstream, so a + // punctuated completion ("Finished installing.") is still caught by the + // completion-marker guard; only an unpunctuated/truncated action nudges. + /\b(processing|downloading|uploading|compiling|training|evaluating|extracting|merging|deploying|installing|configuring|optimizing|refactoring|generating|fetching|validating|parsing|scanning)\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, @@ -111,7 +120,12 @@ export function analyzeContinuationIntent( } // 3. Completion Marker Guard (Final check for sound, completed messages) - if (COMPLETION_MARKERS.test(lowerText)) { + // Only treat a completion marker as terminal when it appears in the LAST + // sentence. A marker buried mid-text ("The download is complete. Now + // installing the deps") must not suppress a later continuation intent — + // this is the common failure mode for non-Claude models (#1707). + const lastSentence = lowerText.split(/(?<=[.!?])\s+/).pop() ?? lowerText + if (COMPLETION_MARKERS.test(lastSentence)) { return { shouldNudge: false } }