Describe the feature or problem you'd like to solve
A hook that changes what the user sees for an assistant message without changing what the model sees. Today a postToolUse hook can redact sensitive values in a tool result (modifiedResult.textResultForLlm) so the model only sees tokens like <EMAIL_1>. But nothing can turn those tokens back into the real values on screen, so the user is stuck reading <EMAIL_1> too.
Proposed solution
Add a display-only hook event for assistant output, for example messageDisplay / MessageDisplay:
- Input: the assistant text as it streams (or the whole message when it's done), plus
sessionId and a message id.
- Output:
displayContent, the text to render in place of the original. Leaving it out shows the original.
- Display only: the session history, the model's context and
--output-format json keep the original text, so the model never sees the replacement.
- Fail-open: on error or timeout, show the original text.
This is the reverse of userPromptTransformed, which today changes what the model gets while "the prompt displayed in the timeline is unaffected".
Claude Code ships this as the MessageDisplay hook (hookSpecificOutput.displayContent). It would also help plugins that target both CLIs, since Copilot CLI already accepts Claude-style PascalCase hooks.
Example prompts or workflows
- Keep PII away from the model. An MCP server returns customer records. A
postToolUse hook replaces emails and phone numbers with <EMAIL_1>, <PHONE_1> and keeps the mapping locally. The model answers "Contact <EMAIL_1> about the refund", and the display hook shows the real email to the user.
- Internal identifiers. Swap internal hostnames or account IDs for tokens before the model sees them, and show the real values on screen.
- Shown differently, not hidden. Turn ticket IDs into clickable links, or mark up text on screen, without changing what the model sees or what's stored in the history.
Additional context
- Tested on Copilot CLI 1.0.94: redacting with
postToolUse → modifiedResult works (the model only sees the token), but the user also sees only the token.
--secret-env-vars and streamer mode only hide values. They can't put the real value back for the user.
- Claude Code reference: https://code.claude.com/docs/en/hooks (MessageDisplay).
Describe the feature or problem you'd like to solve
A hook that changes what the user sees for an assistant message without changing what the model sees. Today a
postToolUsehook can redact sensitive values in a tool result (modifiedResult.textResultForLlm) so the model only sees tokens like<EMAIL_1>. But nothing can turn those tokens back into the real values on screen, so the user is stuck reading<EMAIL_1>too.Proposed solution
Add a display-only hook event for assistant output, for example
messageDisplay/MessageDisplay:sessionIdand a message id.displayContent, the text to render in place of the original. Leaving it out shows the original.--output-format jsonkeep the original text, so the model never sees the replacement.This is the reverse of
userPromptTransformed, which today changes what the model gets while "the prompt displayed in the timeline is unaffected".Claude Code ships this as the
MessageDisplayhook (hookSpecificOutput.displayContent). It would also help plugins that target both CLIs, since Copilot CLI already accepts Claude-style PascalCase hooks.Example prompts or workflows
postToolUsehook replaces emails and phone numbers with<EMAIL_1>,<PHONE_1>and keeps the mapping locally. The model answers "Contact<EMAIL_1>about the refund", and the display hook shows the real email to the user.Additional context
postToolUse→modifiedResultworks (the model only sees the token), but the user also sees only the token.--secret-env-varsand streamer mode only hide values. They can't put the real value back for the user.