Render `UserPromptSubmit.additionalContext` as multiline hook context in the TUI

Open 💬 2 comments Opened Apr 1, 2026 by 92645417d9e5c763259dbebc306e3e

What version of Codex CLI is running?

0.118.0

What subscription do you have?

Plus

Which model were you using?

_No response_

What platform is your computer?

_No response_

What terminal emulator and version are you using (if applicable)?

_No response_

What issue are you seeing?

UserPromptSubmit.hookSpecificOutput.additionalContext is already treated as the model-visible hook context surface, and the core pipeline preserves it as a raw string, including newlines.

Relevant code paths:

  • codex-rs/hooks/src/schema.rs

``rust
pub(crate) struct UserPromptSubmitHookSpecificOutputWire {
pub hook_event_name: HookEventNameWire,
#[serde(default)]
pub additional_context: Option<String>,
}
``

  • codex-rs/hooks/src/engine/output_parser.rs

``rust
let additional_context = wire
.hook_specific_output
.and_then(|output| output.additional_context);
``

  • codex-rs/core/src/hook_runtime.rs

``rust
fn additional_context_messages(additional_contexts: Vec<String>) -> Vec<ResponseItem> {
additional_contexts
.into_iter()
.map(|additional_context| DeveloperInstructions::new(additional_context).into())
.collect()
}
``

So the injected content path already supports structured multiline text.

The TUI side does not present that structure cleanly. It currently prefixes the whole entry with hook context: and renders it as one formatted line:

  • codex-rs/tui/src/chatwidget.rs

``rust
let prefix = match entry.kind {
codex_protocol::protocol::HookOutputEntryKind::Context => "hook context: ",
// ...
};
lines.push(format!(" {prefix}{}", entry.text).into());
``

This makes structured hook context hard to read in the TUI. In practice, hook authors are pushed to flatten rich context into synthetic one-line encodings such as:

[Action now]: ...; [Authority]: ...; [Wait rule]: ...

instead of emitting the more natural multiline content they actually want the model to see, for example:

Action now
- Keep replying with plan preview only.
- Do not emit the wrapped full plan.

Authority
- Managed planning is active.

Wait rule
- If managed evidence is needed, spawn a managed sub-agent first.

What steps can reproduce the bug?

  1. Register a UserPromptSubmit hook that returns multiline additionalContext.
  2. Example hook output:
{
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "Action now\n- Keep replying with plan preview only.\n- Do not emit the wrapped full plan.\n\nAuthority\n- Managed planning is active."
  }
}
  1. Trigger the hook in the Codex TUI.
  2. Observe the rendered hook notification in the transcript/history area.

Minimal behavior to inspect:

  • model-side injection path preserves the raw string
  • TUI-side hook notification path does not render the structure as a readable multiline block

What is the expected behavior?

The TUI should render HookOutputEntryKind::Context using the exact text structure carried in additionalContext, including line breaks.

For example:

UserPromptSubmit hook (completed)
  hook context:
    Action now
    - Keep replying with plan preview only.
    - Do not emit the wrapped full plan.

    Authority
    - Managed planning is active.

At minimum:

  • multiline additionalContext should not force hook authors to collapse content into a single line
  • the TUI-visible content should preserve the same structure that gets injected as developer instructions
  • only the host prefix should differ; the body should stay semantically identical

Additional information

Why this matters:

  • Hook authors often need to send structured policy or coordination context to the model.
  • The cleanest machine/human shape for that content is often multiline Markdown-like text.
  • The current TUI rendering strongly encourages ugly one-line serialization purely for display.
  • That makes hook debugging harder, because the operator sees a degraded version of the intended context shape.
  • It also pushes hook implementations to optimize for host rendering instead of clear model guidance.

Suggested implementation direction:

  • In codex-rs/tui/src/chatwidget.rs, special-case HookOutputEntryKind::Context when entry.text contains \n.
  • Render a standalone hook context: label line, then append the body as indented lines rather than format!(" {prefix}{}", entry.text).
  • Keep the underlying injected additionalContext string unchanged in core/hooks.

Duplicate-issue search:

  • I searched open issues in openai/codex for combinations of UserPromptSubmit, additionalContext, hook context, multiline, and TUI.
  • I did not find a clear duplicate for this exact topic.
  • Closest but different issues:
  • #5253 about missing interrupted tool-call context, not hook context rendering: https://github.com/openai/codex/issues/5253
  • #4790 about prior tool calls disappearing from the TUI transcript, not multiline hook rendering: https://github.com/openai/codex/issues/4790

Generated with Codex CLI.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗