Skip to content

Side-chat reply anchor is persisted but omitted from the first provider turn #2316

Description

@kongenpei

Summary

"Reply in side chat" displays the selected source message under "Replying to", but the explicit agent-only reply anchor is omitted from the first provider turn. The forked provider session inherits conversation history, but it does not receive the text that identifies which earlier message the user selected.

Versions and environment

  • bb from source at 7e10ea4b3742f3d14bde086c2963118238fec3ab, current origin/main
  • bb CLI 0.39.0
  • macOS 26.3, Node v24.6.0
  • Codex CLI 0.149.0
  • Local reused-workspace fork

Steps to reproduce

  1. Create an idle fork through POST /api/v1/threads/fork with an agentContextSeed containing Replying to the selected earlier message and workspace: "reuse".
  2. Complete the queued zero-input thread.start, returning a provider thread id.
  3. Send the first user message to the fork through POST /api/v1/threads/:id/send with Explain that quoted message.
  4. Inspect the queued turn.submit.input sent to the host daemon.

Minimal regression assertion run on main:

expect(firstTurn.command.input).toEqual([seed, firstReply]);

Expected vs actual

Expected turn.submit.input:
[
  {
    "type": "text",
    "text": "Replying to the selected earlier message",
    "mentions": [],
    "visibility": "agent-only"
  },
  {
    "type": "text",
    "text": "Explain that quoted message",
    "mentions": []
  }
]

Actual turn.submit.input:
[
  {
    "type": "text",
    "text": "Explain that quoted message",
    "mentions": []
  }
]

Vitest reports:

AssertionError: expected [ { type: 'text', …(2) } ] to deeply equal [ { type: 'text', …(3) }, …(1) ]

Evidence and root cause

The side-chat plugin builds an agentContextSeed for an earlier-message anchor and passes it to threads.fork: plugins/side-chat/server.ts.

The server combines that seed into the fork's persisted input, but for a seed-only idle fork it separately sets providerInput: []: apps/server/src/services/threads/thread-fork.ts.

Provisioning persists args.input in client/turn/requested, while args.providerInput ?? args.input becomes the daemon-facing input. The explicit empty providerInput therefore removes the seed from the zero-turn provider fork: apps/server/src/services/threads/thread-provisioning.ts.

The first subsequent send builds turn.submit from only the new request input. It does not recover the persisted seed: apps/server/src/services/threads/thread-send.ts.

The current test covers only persistence plus the intentionally empty initial provider input; it does not exercise the first user turn: apps/server/test/public/public-thread-fork.test.ts.

Investigation thread: thr_93njf8in3x.

What was ruled out

  • Still reproduces on current origin/main at 7e10ea4b3742f3d14bde086c2963118238fec3ab.
  • The panel UI does retain and render the anchor; the loss occurs between persisted fork input and provider input.
  • Searches across open and closed issues for side-chat seed, reply anchor, and reply context found no duplicate.

AGENT GENERATED

Metadata

Metadata

Assignees

No one assigned

    Labels

    side-chatBuilt-in plugin: side-chatthreadsTurns, timeline, messaging, forks

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions