Skip to content

Each user message should carry per-message requester identity #562

@sentry-junior

Description

@sentry-junior

When a new user sends a message into an existing Slack thread, model-generated attributions (e.g. Action taken on behalf of) use the original thread starter's identity rather than the actual requester's.

Correct RCA

The runtime turn context is injected once at bootstrap (needsBootstrapContext = true on the first turn). It includes the full context block — skills, config, runtime metadata, and the requester's identity. For all subsequent user messages, no identity is injected at all.

This means: when user B sends a message in a thread originally started by user A, the only <requester> block in the model's history belongs to user A's bootstrap turn. The model uses that stale identity for any attributions it generates.

The symptom is the same whether the stale context comes from the projection or from a resumed session — the real problem is that individual user messages don't carry their sender's identity.

Correct fix

Inject a lightweight requester identity block into every user message as it is stored, not just at bootstrap. The first turn continues to receive the full runtime context (skills, config, etc.) as today. Each subsequent user message should include a minimal block identifying who sent it:

<runtime-turn-context>
<requester>
- user_name: user.beta
- user_id: U_BETA
</requester>
</runtime-turn-context>

This makes each message self-describing regardless of thread length, number of turns, or which user started the conversation.

Why the projection-strip in PR #560 is insufficient

Stripping the old context from the projection unblocks re-injection of a fresh full bootstrap context on the next turn, but that is treating a symptom. If the projection does not contain a context block, needsBootstrapContext fires and re-injects the current requester — but it also re-injects everything else (skills, config, etc.) unnecessarily. The real invariant should be: every user message carries the identity of whoever sent it.

Prior art investigation

Researched how other agent frameworks handle per-message identity in multi-user threads:

  • OpenAI Chat Completions API: explicitly supported a name field on each message object for multi-user and multi-persona flows. When the newer Responses API dropped the name field, the community-recommended workaround is inline sender prefixes (e.g. [Alice]: <message>) directly in the user message content. No native per-message identity in the new API.
  • Anthropic: no dedicated per-message identity field. Their context engineering guidance emphasizes injecting the smallest high-signal token set at each inference step — directly supporting the "minimal identity sub-block per user message" approach over re-injecting the full bootstrap context.
  • General pattern: embedding sender identity in message content (inline prefix or metadata block) before each user turn is the common approach across agents. No framework injects it automatically; it is a builder responsibility.

System prompt implication

The current <conversation> rule — "the requester is the person asking now, which may differ from the original reporter or subject" — asks the model to infer identity from context but gives it no reliable signal to work with once bootstrap context is stale.

Once per-message identity blocks are added, this rule should be updated to clarify that the <requester> block in the current user message is the authoritative identity for that turn, and should be used for attributions like Action taken on behalf of.

Action taken on behalf of Nelson Osacky.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions