Skip to content

Session title agent generates title from injected memory/system context rather than actual user message #23114

Closed as not planned

Description

@marcusyoung

Description

Description

When automatic session title generation runs, it passes the full rendered message history to the title model via MessageV2.toModelMessagesEffect. If a memory MCP server injects prior conversation summaries or facts into the system prompt or as synthetic message parts, the title model receives that injected content and generates a title based on it rather than the actual user message.

Expected Behaviour

The title should reflect the content of the current user message only.

Actual Behaviour

The title reflects injected memory context from prior sessions. Starting a session with "hi" produced:

"Discussion on Workaround and GitHub Issue Regarding Roampal Data Location Setup and Patching cli.py"

— an accurate summary of the previous session's content, surfaced via memory context injection.

Root Cause

In packages/opencode/src/session/prompt.ts, the title generation constructs its payload as:

const context = input.history.slice(0, idx + 1)
messages: [
  { role: "user", content: "Generate a title for this conversation:\n" },
  ...yield* MessageV2.toModelMessagesEffect(context, mdl)
]

MessageV2.toModelMessagesEffect renders the full message including injected memory/system content. The title model then titles based on that rather than the actual user message.

Suggested Fix

Pass only the raw text of the first real user message to the title model:

const userText = firstUser.parts
  .filter(p => p.type === "text")
  .map(p => p.text)
  .join("\n")

messages: [
  { role: "user", content: "Generate a title for this conversation:\n" + userText }
]

This strips all synthetic/injected parts before passing to the title model.

Plugins

roampal-core

OpenCode version

1.4.6

Steps to reproduce

  1. Configure a memory MCP server that injects prior session context into the conversation (e.g. Roampal, mem0, or similar).
  2. Start a new session with a short or unrelated message (e.g. "hi").
  3. Observe that the generated session title reflects content from a previous session rather than the current one.

Screenshot and/or share link

No response

Operating System

Windwos 11

Terminal

PWSH

Activity

  1. added
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on Apr 17, 2026
  2. rekram1-node commented on Apr 17, 2026

    @rekram1-node
    Collaborator

    That solution doesnt quite work because u can preload things into context like files, shell outputs, etc. These would be useful for constructing somewhat useful titles, if you strip all that out it causes issues.

    How is this MCP server autoloading things to context? Or are u referring to a plugin?

  3. marcusyoung commented on Apr 17, 2026

    @marcusyoung
    Author

    That's the plugin component of roampal doing the injection.

  4. removed
    bugSomething isn't working
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on May 3, 2026
  5. coryplastek commented on Jun 26, 2026

    @coryplastek

    I'm seeing similar behavior, although based on the use of https://github.com/obra/superpowers.

    Instead of a previous conversation's context I'm seeing the superpowers instructions being injected, generating a title of:

    Superpowers: Generating title for conversation

    Changing the title agent model to a better model that can distinguish between the injected instructions and the user prompt as a workaround worked for me

  6. mlopezcoria commented on Aug 13, 2026

    @mlopezcoria

    Code-level confirmation for v1.18.18 (path still identical on dev as of 2026-08-13):

    • SessionPrompt.ensureTitle (packages/opencode/src/session/prompt.ts:193-253) builds the title prompt from history.slice(0, idx + 1) via MessageV2.toModelMessagesEffect(context, mdl).
    • toModelMessagesEffect (packages/opencode/src/session/message-v2.ts:206) skips text parts only when part.ignored or empty — synthetic parts pass through:
    if (part.type === "text" && !part.ignored && part.text !== "")
      userMessage.parts.push({ type: "text", text: part.text })
    • Verified end-to-end: opencode-mem (chat.message hook, injectOn: "first") prepends a synthetic memory part to the first user message; that part is persisted in opencode.db and flows into the title prompt. With a weak small_model the title blends memory text with the user's message (e.g. "Error en certificado Arreglo en sitio pepe SSL").

    Re the concern that stripping everything would lose useful preloaded context (files, shell output): those are non-synthetic user/assistant parts and would remain — filtering only synthetic: true parts (or adding a stripSynthetic option alongside the existing stripMedia) would target exactly the plugin-injected context without dropping real content.

  7. RE-codes commented on Aug 30, 2026

    @RE-codes

    Reproduced on OpenCode 1.18.25 with Qwen3.8-27B via oMLX, without a configured small_model. The title request sometimes answers the original user prompt instead of producing a title. Lowering temperature and reasoning effort did not prevent it, while max_tokens only limited the damage. Because title generation runs concurrently, the extra request also disabled MTP and slowed the main response. Combining the instruction and source text as proposed here looks like the right fix.

  8. joepetrakovich commented on Sep 14, 2026

    @joepetrakovich

    I'm seeing this when using small local models that should easily be able to handle this task but trip up because the " Plan Mode..." message is included in what is sent for the title generator.

    My titles are often something like "OC | Plan mode read only constraints"

    Ideally it really only sends what we type into the chat box.

    Doing as @coryplastek mentioned and using a better model does work but of course it takes up more precious vram. The new gemma tiny models almost always produce a bad title. nvidia/nemotron-3-nano-4b is better but still sometimes fumbles.

    Received request: POST to /v1/chat/completions with body  {
     "model": "google/gemma-4-e2b",
     "max_tokens": 8192,
     "messages": [
       {
         "role": "system",
         "content": "You are a title generator. You output ONLY a threa... <Truncated in logs> ...ode toggle\" → Dark mode toggle in App\n</examples>\n"
       },
       {
         "role": "user",
         "content": "Generate a title for this conversation:\n"
       },
       {
         "role": "user",
         "content": [
           {
             "type": "text",
             "text": "good morning"
           },
           {
             "type": "text",
             "text": "<system-reminder>\n# Plan Mode - System Reminder\n\nC... <Truncated in logs> ...nstructions you have received.\n</system-reminder>\n"
           }
         ]
       }
     ],
    
  9. asdf8675309 commented on Sep 16, 2026

    @asdf8675309

    I'm experiencing this as well when using OpenCode with Multica. I set a set the model for the chat to one thing and out popped another query to gemini-3.8-flash.

    multica injected (cleaned up)
    {
    "role": "user",
    "content": "You are running as a chat assistant for a Multica workspace.\nAudience: direct room.\n\nUser message:\n\n\nTo include a file or image you produced in your reply, run multica attachment upload <local-path>. The file binds to your reply automatically and appears as an attachment card below it even if you paste nothing. The command also returns a markdown snippet you may paste on its own line to place the item where you want it (files render as a card, images inline).\n\n## Task Initiator\n\nThis task was initiated by <MULTICA_USER> (<MULTICA_USER_EMAIL>), a member of this workspace.\n\nThe initiator — not the runtime owner — is who you are answering: apply any per-person privacy or access rules your instructions define. Your Multica credentials stay scoped to the runtime owner, and initiator attribution does not change what you may read or write; do not assume the initiator can see everything you can.\n\n"
    }

  10. thdxr commented on Oct 11, 2026

    @thdxr
    Member

    Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.

    — from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

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