diff --git a/packages/coding-agent/src/session/agent-session.ts b/packages/coding-agent/src/session/agent-session.ts index 608e59404..c34049459 100644 --- a/packages/coding-agent/src/session/agent-session.ts +++ b/packages/coding-agent/src/session/agent-session.ts @@ -668,19 +668,22 @@ export class AgentSession { } } - // Deobfuscate assistant message text for display — the LLM echoes back obfuscated - // placeholders, but the user should see real values. convertToLlm re-obfuscates on next turn. - if (event.type === "message_end" && event.message.role === "assistant" && this.#obfuscator) { - for (const block of event.message.content) { - if (block.type === "text") { - block.text = this.#obfuscator.deobfuscate(block.text); - } else if (block.type === "thinking" && "thinking" in block) { - block.thinking = this.#obfuscator.deobfuscate(block.thinking); - } + // Deobfuscate assistant message content for display emission — the LLM echoes back + // obfuscated placeholders, but listeners (TUI, extensions, exporters) must see real + // values. The original event.message stays obfuscated so the persistence path below + // writes `#HASH#` tokens to the session file; convertToLlm re-obfuscates outbound + // traffic on the next turn. Walks text, thinking, and toolCall arguments/intent. + let displayEvent: AgentEvent = event; + const obfuscator = this.#obfuscator; + if (obfuscator && event.type === "message_end" && event.message.role === "assistant") { + const message = event.message; + const deobfuscatedContent = obfuscator.deobfuscateObject(message.content); + if (deobfuscatedContent !== message.content) { + displayEvent = { ...event, message: { ...message, content: deobfuscatedContent } }; } } - await this.#emitSessionEvent(event); + await this.#emitSessionEvent(displayEvent); if (event.type === "turn_start") { this.#resetStreamingEditState();