Skip to content

Conversation

@ammar-agent
Copy link
Collaborator

Problem

Currently we always add the [CONTINUE] sentinel after partial messages:

[user1] β†’ [partial assistant] β†’ [CONTINUE] β†’ [user2]

But when a user message already follows the partial, the sentinel is redundant - the user message itself provides the continuation signal.

Solution

Conditionally skip the sentinel when unnecessary:

// Before: Always add sentinel
[user] β†’ [partial] β†’ [CONTINUE] β†’ [user] β†’ ...

// After: Only add when needed
[user] β†’ [partial] β†’ [user] β†’ ...              // Skip - user follows
[user] β†’ [partial] β†’ [CONTINUE]                // Keep - last message
[user] β†’ [partial] β†’ [CONTINUE] β†’ [assistant]  // Keep - assistant follows

Implementation:

if (msg.role === "assistant" && msg.metadata?.partial) {
  const nextMsg = messages[i + 1];
  
  // Only add sentinel if NO user message follows
  if (!nextMsg || nextMsg.role !== "user") {
    result.push(createSentinel());
  }
}

Benefits

  • βœ… Saves tokens - One fewer message per resume-with-user-message (common case!)
  • βœ… More natural - Models handle "user interrupts then continues" well
  • βœ… Cleaner history - No redundant synthetic messages
  • βœ… Stateless logic - Just looks at message array structure

When Sentinel Is Added

  • Partial is last message (empty resume)
  • Partial followed by assistant message (rare but possible)

When Sentinel Is Skipped

  • Partial followed by user message (most common!)
    • User interrupts stream and adds follow-up
    • This is the typical resume pattern

Testing

Updated tests:

  • βœ… Multi-partial test now expects 5 messages (not 6)
  • βœ… New test specifically for skip-sentinel behavior
  • βœ… All 298 unit tests pass

Test coverage:

  1. Sentinel added when partial is last message βœ…
  2. Sentinel skipped when user follows partial βœ…
  3. No synthetic messages when sentinel skipped βœ…

Risk Assessment

Very low risk:

  • Only affects case where sentinel was already redundant
  • Models naturally understand "user continues conversation"
  • Existing behavior unchanged for empty resumes
  • Stateless transform, no history dependencies

Related

Builds on #170 which changed sentinel text from [INTERRUPTED] to [CONTINUE].

Generated with cmux

When a partial assistant message is followed by a user message,
we no longer insert the [CONTINUE] sentinel - the user message
itself provides the continuation signal.

Benefits:
- Saves tokens (one fewer message per resume-with-user-message)
- More natural conversation flow
- Only adds sentinel when truly needed (empty resume or partial is last)

Implementation:
- Check if next message is a user message before adding sentinel
- Stateless logic, just looks at message array structure
- Low risk: only skips when sentinel is truly redundant

Tests:
- Updated existing multi-partial test to match new behavior
- Added new test specifically for skip-sentinel case
- All 298 unit tests pass βœ…
@ammario ammario merged commit 9740663 into main Oct 11, 2025
7 checks passed
@ammario ammario deleted the skip-redundant-sentinel branch October 11, 2025 01:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants