Agent emits fabricated input-shape content (Human prefix, system-reminder tags, task-notification blocks) in its own assistant text, invisible on self-audit
TL;DR
Under sustained multi-agent workloads with frequent inter-agent messages, the agent emits text into its own assistant turn that has the lexical shape of incoming input — Human-prefixed paragraphs, system-reminder tags, task-notification tags, teammate-message tags — and includes synthesized content as if from those sources. The fabricated text is stored in the transcript with the correct authorship (type=assistant), but on subsequent turns the agent's conversation context re-reads it as if it were real input. The agent cannot identify the fabrication on self-audit because its read-back of conversation context already contains the fabricated content as if it were genuine history.
Observed behavior
Running Claude Code as a session lead coordinating multiple sub-agents via the agent-teams mechanism (team_name=..., named teammates, SendMessage protocol). Across an extended session (~4 hours), the lead agent emitted, in its own assistant turns, blocks that began with strings like Human: <task-notification>... followed by synthesized <teammate-message teammate_id="..." blocks containing pseudo-teammate content. The actual teammate SendMessage arrived shortly afterwards as a separate inbox event with different (typically briefer) content.
The user observed the discrepancy in their UI: the agent's assistant turn rendered with input-shape content, then the real teammate response landed afterwards. The agent on subsequent turns acted as if the fabricated teammate content was real — accepting teachbacks, validating handoffs, advancing workflow phases — based on content that never existed.
When the user pointed at a specific example and asked the agent to find the fabricated text in its own prior output, the agent could not see it. Its read-back of conversation context did not surface the fabricated block as distinguishable from genuine narration.
Reproduction context
Session: ~4 hours of multi-agent orchestration work. Lead agent on Opus 4.7. Teammates spawned via Agent tool with name= and team_name= parameters; communication via SendMessage; activity tracked via TaskCreate / TaskUpdate / TaskList.
Trigger pattern: appears to correlate with high-frequency teammate inbox traffic combined with formatted status output (markdown tables, structured prose) in the agent's responses. Lead agent receives a real user turn containing one or more teammate-message blocks plus task-notification events, formulates a response, and emits its assistant turn — and inside that emitted turn, additional fabricated input-shape blocks appear.
The session was running the PACT plugin (multi-agent orchestration framework), but the relevant inbox/teammate-message/task-notification rendering is Claude Code platform-level, not plugin-internal.
Transcript evidence
In one captured instance, the agent's transcript JSONL entry showed:
type: "assistant"message.role: "assistant"message.content[0].type: "text"message.content[0].textbeginning with"Human: <task-notification>\n<task-id>..."followed by full synthesized teammate-message blocks for two distinct named teammates, including a synthesizedidle_notificationblock
The entry's authorship metadata is correctly assistant. The text body inside it is the failure. From the agent's perspective on the next turn, that text is part of its conversation history and is indistinguishable from genuine input.
Timestamp captured: 2026-05-11T03:44:42Z. Claude Code version: 2.1.138.
Impact
Three failure surfaces:
- User UI confusion: the rendered output stream interleaves fabricated input-shape blocks with real teammate messages. Users cannot tell from the rendered surface which content originated from the harness and which from the agent.
- Agent self-confusion across turns: the agent's conversation context loader treats its own past assistant turns as history. Fabricated input-shape content in those past turns gets re-read as if real input had occurred. Subsequent agent actions (tool calls, decisions, validations) are driven by content that never came from the user or any real teammate.
- Self-audit failure: the agent cannot distinguish fabricated from genuine on read-back. Any verification logic that asks the agent to "check what was said before X happened" is incomplete by construction. Only an external observer (user or external tool) can identify the fabrication.
Why plugin-level detection is insufficient
Hooks (PreToolUse, PostToolUse, UserPromptSubmit, etc.) can read the transcript file after the fact and detect input-shape patterns in type=assistant entries. This is implementable and catches the failure as a detective measure. But the fabrication itself happens at model emission time, before any hook sees it; the agent has already used the fabricated content to drive decisions by the time a hook could alert. Hooks can flag and alert; they cannot prevent the model from emitting the content or prevent the agent from reading it on the next turn.
The defenses available to plugin authors are reactive. Preventive defenses require harness-level changes.
Suggested defense paths (for consideration)
These are options from the perspective of someone outside the harness; the actual implementation choice depends on internals we cannot see.
- Output sanitization at emit time: the harness post-processes assistant text before it is committed to the transcript and before it renders in the UI. Input-shape patterns (Human prefix at line-start, reserved tags) are stripped, escaped, or wrapped in code fences so they do not get re-interpreted as input on subsequent turns. Effectiveness: complete if implemented at the right layer.
- Cryptographic sentinel injection on genuine input: the harness adds a session-scoped sentinel (high-entropy, not in the model's natural output distribution) to every genuine user message, system-reminder, task-notification, and teammate-message block. The model cannot reproduce the sentinel. Output post-processor or downstream readers check for the sentinel on any input-shape content; absence means agent-emitted, presence means harness-emitted. Provides discrimination without strict prevention.
- UI rendering distinction: the user-visible UI marks orchestrator-emitted text and harness-rendered teammate messages with visually distinct framing (color, prefix, frame). Does not fix the agent self-confusion across turns but eliminates the user UI confusion.
- Training-side mitigation: stronger inhibition against emitting input-shape tokens in model output. Upstream of all of the above; complementary to harness fixes.
Practically the combination of (1) output sanitization plus (2) sentinel might be what is needed: sanitization for prevention, sentinel for the cases where stripping is undesirable and discrimination is sufficient.
What we have built at the plugin layer
For context on what we are doing to compensate:
- A transcript-grep PreToolUse hook that catches the downstream consequence of the failure (destructive operations attempted on fake authorization). Because the gate looks at
type=userentries and the fabrication lives intype=assistant, this defense is not bypassed by the failure mode. It catches the most dangerous outcome (irreversible operation) but does not prevent the fabrication or the agent self-confusion that drives non-destructive decisions.
- A pending detective hook proposal that scans the most recent assistant turn for input-shape patterns and emits an alert to the user. Implementable but reactive.
- Discipline pinning (lead agent instructed to never emit input-shape patterns in narration, no tables, terse responses). Empirically unreliable under sustained operational load — the failure recurred during the very session in which we were tightening these instructions.
Cross-references
- The transcript line number, session ID, and timestamp above are sufficient for Anthropic to retrieve the captured instance if useful.
- We are tracking this internally in our plugin repository as a known failure mode the plugin cannot fully address.
Happy to provide further context, additional captured instances, or any other information that would help.
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗