[Bug]: Cited assistant quotes reach providers as raw t3-citation links#16327

opened23d agobyJjosephv123

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

  1. In any thread, select part of an assistant response and cite it, optionally with a comment.
  2. Send the message.
  3. Inspect the text the provider adapter receives for that turn (for example, ask the agent to echo its last user message verbatim).

Expected behavior

The provider receives the expanded form from expandAssistantCitationsForProvider (packages/shared/src/assistantCitations.ts): each link is replaced by an inline marker such as [assistant-quote-1], followed by an <assistant_citations> block. That block holds the readable excerpt and comment as JSON and says the quote is reference material, not instructions, and that the comment is the user's. The persisted message keeps the clickable link. This is the contract described in docs/internals/assistant-citations.md.

Actual behavior

The provider receives the raw stored link:

1[Assistant quote](t3-citation://v1/<env>/<thread>/<message>?text=...&start=..&end=..&prefix=..&suffix=..&comment=..)

The quote and comment are URL-encoded (+, %2C, %0A, multi-byte %XX for non-ASCII). That costs extra input tokens, makes the text harder for the model to read, and drops the framing that separates quoted assistant text from the user's comment.

Cause: the expansion used to run in ProviderService.sendTurn. That path was removed in the orchestrator rewrite (de34391427, #2829), and nothing replaced the call. On main, expandAssistantCitationsForProvider is only called by the web composer's length check (apps/web/src/components/chat/composerSubmission.ts). The v2 paths that build provider text call only projectComposerContextForProvider:

  • apps/server/src/orchestration-v2/ProviderTurnStartService.ts (two call sites)
  • apps/server/src/orchestration-v2/ProviderTurnControlService.ts

The likely fix is to apply the citation expansion wherever provider text is built, keep the post-expansion input limit check, and add a server test proving a cited message reaches the adapter in expanded form.

Impact

Minor bug or occasional failure

Version or commit

main @ 204a639096

Environment

All providers. Citations are created in web/desktop; the server sends them on.

Workaround

None needed for basic use. Models can usually decode the URL-encoded quote, which is probably why this went unnoticed.

Note

Grok responding on behalf of Julius.

Triage

Thanks for the detailed write-up and for tracing the cause, @josephv123! This matches what we see on current main.

What I found

  • This looks like a regression from the orchestrator rewrite in #2829 (de34391427). ProviderService.sendTurn used to run expandAssistantCitationsForProvider and, when the text changed, re-check it against ProviderSendTurnInput's 120,000-character limit. That call wasn't carried over.
  • On main, the only production caller of expandAssistantCitationsForProvider is the web composer's length check in apps/web/src/components/chat/composerSubmission.ts. Adapter text is built only with projectComposerContextForProvider, which rewrites t3-context: links but leaves t3-citation: links alone:
    • apps/server/src/orchestration-v2/ProviderTurnStartService.ts (the turn prompt and the provider slash-command path)
    • apps/server/src/orchestration-v2/ProviderTurnControlService.ts (steer)
  • Context handoff is a second path. historicalMessage in apps/server/src/orchestration-v2/ContextHandoffBudget.ts copies the stored item.text, so a provider switch or thread rebuild also injects earlier cited messages as raw links.
  • The web composer still rejects drafts whose expanded form exceeds PROVIDER_SEND_TURN_MAX_INPUT_CHARS, but the server no longer enforces that, since nothing expands before send.
  • Persisted messages correctly keep the clickable link, and docs/internals/assistant-citations.md still describes expanding only at dispatch.

Likely fix area

  • Restoring expansion at the provider send boundaries in orchestration-v2 (turn start, slash command, steer), with the input limit applied to the expanded string.
  • Possibly also expanding in the context handoff path, so rebuilt history matches what live turns send.
  • A server test showing a cited message reaches the adapter in expanded form would guard against this regressing again.

A maintainer will decide on the fix direction.

Reproduced on Ubuntu Linux with the real web dev server at a12d6ed942. Selected an assistant sentence, clicked Cite, and asked Codex GPT-6-Astra to echo the input. The provider transcript contains the raw t3-citation link and no assistant_citations block. A second attempt with an accented/Japanese comment also passed the URL-encoded comment unchanged. Runtime transcript. A focused server test reproduces the same failure.

Confirmed again on current main a50cd989ae, Ubuntu Linux, real web dev server and Codex GPT-6-Astra. Selected an assistant sentence, clicked Cite, and sent it. Both the native provider input and its echo contain the raw URL-encoded citation, without an assistant_citations block. Runtime transcript, recording. No duplicate with the same provider-delivery symptom found among the open issues.