Skip to content

Correct CLI --continue --session syntax for persisting sessions is malfunctioning. #11680

Closed as not planned

Description

@davidbernat

Description

Howdy OpenCoders. This is an easy one, and most likely either a corrupted opencode functionality or a badly configured configuration from my stupider days.

The question is how to persist a session across multiple prompts from my using the CLI.
The docs indicate that the proper syntax requires --continue and --session.

Caveats:

  • if the --session flag is not used the opencode platform will find the most recent session and continue that
  • the first message to opencode can force create any session as long as that session_id starts with "ses_"
    -- this is particularly useful when driving new opencode sessions from external uuid constraints.

Anyway. The syntax is simple so that just needs double checking.
As you can see from the terminal logs below, the opencode run is definitely not persisting across calls.
What should we do?
Let's double check, and debug together.
PS these operations are being conducted from my LLC so I hope no spoliating factors are langoliering my logerinos!

Image

.
.
.
.
You can see from this shorter exchange below that the session persistence is definitely malfunctioning.

Image

Thanks.
Jazzed about testing out the new Code-Reviewer agent.

Activity

  1. github-actions commented on Feb 1, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    Feel free to ignore if none of these address your specific case.

  2. justfortheloveof commented on Feb 1, 2026

    @justfortheloveof
    Contributor
    Image (I don't know if it believing I sent 4 message prior to the last one is because the model sucks at math or perhaps opencode injected a user message or it's counting the system prompt?.. anyhow!)

    The above is with v1.1.48 - I wonder what version you're running.
    Notice I was using one of -c (2nd and 3rd messages) or -s (4th message).


    Also found this:

    const sessionID = await (async () => {
    if (args.continue) {
    const result = await sdk.session.list()
    return result.data?.find((s) => !s.parentID)?.id
    }
    if (args.session) return args.session

    where if you pass -c, -s is ignored (regardless of the short or long form).


    Perhaps they should be made mutually exclusive to avoid confusion.

  3. davidbernat commented on Feb 2, 2026

    @davidbernat
    Author

    Thanks for inspecting the code. I did as well, and I agree with you.
    Using --continue will deploy the most recent session_id, regardless of whether the --session flag has been used with a different session_id. The --continue flag takes precedence with the most recent session_id.

    I also read the logic to MessageV2.toModelMessages() which is responsible for constructing the "context" for the next request to the LLM. It is unfortunately much more nuanced with hard won adaptations than simply reconstructing linear parts of the individual logs from opencode run --format json.

    export function toModelMessages(input: WithParts[], model: Provider.Model): ModelMessage[] {

    I read through to see how much I would want to replicate, and also had OpenCode inspect the OpenCode repository and as usual congratulations; when the software works it works. I do wish OpenCode offered a command line utility for reconstructing this information from the multitude of logs directly. It is more complicated than it is accessible to the average public, which makes me really wishful how best to offline manage the construction of contexts from the opencode run --format json outputs themselves.

    For those who want more information:

    Image Image

    Context Construction Process Flow

    Step 1: Message Collection and Filtering

    In SessionPrompt.loop() around Line 276:

    let msgs = await MessageV2.filterCompacted(MessageV2.stream(sessionID))

    Step 2: Last Message Analysis (Lines 278-293)

    Finds the most recent user and assistant messages to understand conversation state.

    Step 3: Reminder Injection (Lines 519-596)

    The insertReminders() function adds system reminders and plan mode instructions.

    Step 4: Tool Resolution (Lines 560-568)

    The resolveTools() function determines which tools are available for the LLM.

    Step 5: Message Processing (Lines 600-619)

    CRITICAL CONTEXT BUILDING CODE:

    const result = await processor.process({
      user: lastUser,
      agent,
      abort,
      sessionID,
      system: [...(await SystemPrompt.environment(model)), ...(await InstructionPrompt.system())],
      messages: [
        ...MessageV2.toModelMessages(sessionMessages, model),  // <- KEY CONVERSION
        ...(isLastStep ? [{ role: "assistant" as const, content: MAX_STEPS }] : []),
      ],
      tools,
      model,
    })

    Step 6: Message to Model Format Conversion

    The most important context conversion happens in MessageV2.toModelMessages() (Lines 345-500 in message-v2.ts):

    • Processes each message part (text, file, tool, reasoning)
    • Converts internal message format to AI SDK's ModelMessage format
    • Handles tool calls, attachments, and content transformation
    • Filters out ignored parts and manages compaction state

    Step 7: System Prompt Assembly (Lines 5-50 in system.ts)

    Environment context includes:

    • Working directory
    • Git repository status
    • Platform information
    • Current date
    • File tree (limited to 200 files)

    Step 8: Context Overflow Management

    When context exceeds limits, SessionCompaction.isOverflow() triggers compaction:

    • Summarizes old messages
    • Prunes tool call outputs
    • Maintains conversation context within model limits

    Key Plugin Hooks for Context Modification

    1. experimental.chat.messages.transform (Line 598 in prompt.ts): Allows plugins to modify messages before LLM call
    2. experimental.chat.system.transform (Line 358 in llm.ts): Allows plugins to modify system prompts
    3. experimental.session.compacting (Line 134 in compaction.ts): Allows plugins to influence context compaction

    Context Construction Logic Summary

    The context construction process follows this sequence:

    1. Message Collection → Gather conversation history with filtering
    2. Message Analysis → Identify relevant user/assistant messages
    3. Tool Resolution → Determine available tools based on permissions
    4. System Prompt Building → Add environment context and provider instructions
    5. Message Format Conversion → Convert internal MessageV2 format to ModelMessage format
    6. Plugin Transformations → Apply plugin-based modifications
    7. Context Assembly → Combine system prompts, conversation history, and tools
    8. LLM Call → Send constructed context to the language model
    9. Overflow Management → Compact context if it exceeds model limits

    The MessageV2.toModelMessages() function is the most critical piece for understanding how message objects are converted into LLM context, as it handles all the complex logic for transforming internal message parts into the final format that language models can understand.

  4. davidbernat commented on Mar 13, 2026

    @davidbernat
    Author

    This ticket can be closed, yes?
    The result of the analysis is:

    1. --continue overrides --session when both are present. this should be made clear in the documentation
    2. the internal logs => context function await MessageV2.filterCompacted is not well documented and not in the scope of this ticket

    Thanks.

  5. assigned and unassigned on Apr 26, 2026
  6. gosuto-inzasheru commented on Apr 29, 2026

    @gosuto-inzasheru

    Related edge case: when using --continue and there is no prior session in the directory, opencode returns a non-fatal JSON error instead of gracefully starting a fresh session:

    {
      "data": {
        "sessionID": "dummy"
      },
      "error": [
        {
          "origin": "string",
          "code": "invalid_format",
          "format": "starts_with",
          "prefix": "ses",
          "path": ["sessionID"],
          "message": "Invalid string: must start with \"ses\""
        }
      ],
      "success": false
    }

    The process does not exit with a non-zero code, so a shell fallback like opencode --continue || opencode never triggers the second branch. Expected behavior: --continue with no existing session should either silently start a new session, or exit non-zero so callers can handle it.

    Reproducible with opencode --model <model> --continue in a directory with no prior opencode sessions. Running v1.14.29 on macOS.

  7. removed
    bugSomething isn't working
    on May 3, 2026
  8. github-actions commented on Jul 3, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions