What issue are you seeing?
SessionStart.hookSpecificOutput.additionalContext preserves multiline text in the model/context path, but the Codex TUI does not preserve that line structure when rendering the completed hook output in history.
The completed hook history cell appears to render each HookOutputEntry as one TUI Line. As a result, explicit newline characters inside hook output do not become separate TUI rows, making structured hook context hard to inspect.
This affects multiline output for completed hook entries such as:
Context
Warning
Error
Stop
Feedback
Concrete example from a local SessionStart working-memory hook:
SessionStart hook (completed)
hook context: ## Working Memory Recall
Source: Codex compaction
Scope: Durable workspace memory
### Feedback
- ...
The model receives the intended multiline content, but the visible TUI history does not render it as a readable multiline block.
What steps can reproduce the bug?
-
Register a SessionStart hook that returns multiline additionalContext.
-
Example hook output:
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "## Working Memory Recall\n\nSource: Codex compaction\nScope: Durable workspace memory\n\n### Feedback\n- Example preference"
}
}
-
Start Codex TUI with hooks enabled.
-
Observe the completed hook output in the transcript/history area.
Minimal behavior to inspect:
- the model-side injection path preserves the raw multiline string
- the TUI-side completed hook notification path does not render that structure as separate rows
What is the expected behavior?
The TUI should preserve explicit newline structure in completed hook output. For example:
• SessionStart hook (completed)
hook context: ## Working Memory Recall
Source: Codex compaction
Scope: Durable workspace memory
### Feedback
- Example preference
At minimum:
- hook authors should not need to flatten multiline context purely for display
- the TUI-visible hook output should preserve the same line structure that is sent to the model
- hook parsing, protocol payloads, and model injection behavior should remain unchanged
Additional information
Related issue: #16486 reports the same TUI rendering issue for UserPromptSubmit.additionalContext. This issue tracks the same root cause as observed through SessionStart.additionalContext.
I tested a small TUI-only fix against current main:
8a94430bb273623be42b68f144f1ab1df343bb53
Patch direction:
- split completed hook
entry.text on \n in codex-rs/tui/src/history_cell/hook_cell.rs
- keep the hook output prefix (
hook context: , warning: , etc.) on the first physical line only
- indent non-empty continuation lines by four spaces
- preserve explicit blank lines
- leave hook parsing, protocol, and model injection behavior unchanged
Patch shape:
let prefix = hook_output_prefix(entry.kind);
for (index, text) in entry.text.split('\n').enumerate() {
if index == 0 {
lines.push(format!(" {prefix}{text}").into());
} else if text.is_empty() {
lines.push("".into());
} else {
lines.push(format!(" {text}").into());
}
}
Coverage added locally:
- unit test for multiline
Context, including an explicit blank line, asserting both display_lines() and raw_lines()
- unit test for multiline
Warning, asserting the prefix stays on the first line and continuation lines are indented
- existing chatwidget snapshot updated so a
SessionStart context containing session context\nsecond line renders on two rows
Local verification:
PATH=/opt/homebrew/opt/rustup/bin:$PATH just fix -p codex-tui
# exit 0
PATH=/opt/homebrew/opt/rustup/bin:$PATH CARGO_NET_GIT_FETCH_WITH_CLI=true just test -p codex-tui
# 2635 tests run: 2635 passed, 4 skipped
# bench-smoke completed with exit 0
PATH=/opt/homebrew/opt/rustup/bin:$PATH cargo insta pending-snapshots
# No pending snapshots.
What issue are you seeing?
SessionStart.hookSpecificOutput.additionalContextpreserves multiline text in the model/context path, but the Codex TUI does not preserve that line structure when rendering the completed hook output in history.The completed hook history cell appears to render each
HookOutputEntryas one TUILine. As a result, explicit newline characters inside hook output do not become separate TUI rows, making structured hook context hard to inspect.This affects multiline output for completed hook entries such as:
ContextWarningErrorStopFeedbackConcrete example from a local
SessionStartworking-memory hook:The model receives the intended multiline content, but the visible TUI history does not render it as a readable multiline block.
What steps can reproduce the bug?
Register a
SessionStarthook that returns multilineadditionalContext.Example hook output:
{ "hookSpecificOutput": { "hookEventName": "SessionStart", "additionalContext": "## Working Memory Recall\n\nSource: Codex compaction\nScope: Durable workspace memory\n\n### Feedback\n- Example preference" } }Start Codex TUI with hooks enabled.
Observe the completed hook output in the transcript/history area.
Minimal behavior to inspect:
What is the expected behavior?
The TUI should preserve explicit newline structure in completed hook output. For example:
At minimum:
Additional information
Related issue: #16486 reports the same TUI rendering issue for
UserPromptSubmit.additionalContext. This issue tracks the same root cause as observed throughSessionStart.additionalContext.I tested a small TUI-only fix against current
main:Patch direction:
entry.texton\nincodex-rs/tui/src/history_cell/hook_cell.rshook context:,warning:, etc.) on the first physical line onlyPatch shape:
Coverage added locally:
Context, including an explicit blank line, asserting bothdisplay_lines()andraw_lines()Warning, asserting the prefix stays on the first line and continuation lines are indentedSessionStartcontext containingsession context\nsecond linerenders on two rowsLocal verification: