Skip to content

Render UserPromptSubmit.additionalContext as multiline hook context in the TUI #16486

Open

Description

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

    pub(crate) struct UserPromptSubmitHookSpecificOutputWire {
        pub hook_event_name: HookEventNameWire,
        #[serde(default)]
        pub additional_context: Option<String>,
    }
  • codex-rs/hooks/src/engine/output_parser.rs

    let additional_context = wire
        .hook_specific_output
        .and_then(|output| output.additional_context);
  • codex-rs/core/src/hook_runtime.rs

    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
    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:

Generated with Codex CLI.

Activity

  1. added
    CLIIssues related to the Codex CLI
    TUIIssues related to the terminal user interface: text input, menus and dialogs, and terminal display
    on Apr 1, 2026
  2. added
    agentIssues related to the core agent loop
    and removed
    TUIIssues related to the terminal user interface: text input, menus and dialogs, and terminal display
    CLIIssues related to the Codex CLI
    on Apr 2, 2026
  3. cluedesc commented on Apr 2, 2026

    @cluedesc

    I put together a small local patch for this since the scope looked pretty contained.

    It keeps the current single-line behavior as-is, but changes multiline hook context rendering so the TUI shows it as an indented block instead of flattening everything onto one hook context: ... line.

    I also added snapshot coverage for a multiline UserPromptSubmit case so the rendering does not regress.

    Validation I ran:

    • just fmt
    • cargo test -p codex-tui multiline_hook_context_render_snapshot
    • cargo test -p codex-tui pre_tool_use_hook_events_render_snapshot

    Branch:

    Compare:

    I have not opened a PR since the repo is invitation-only for external code changes.

    If this approach matches what the team wants here, I’d be happy to open an invited PR.

  4. added
    hooksIssues related to event hooks
    and removed
    agentIssues related to the core agent loop
    on Apr 2, 2026
  5. oxysoft commented on May 8, 2026

    @oxysoft

    Indexed this hook ticket in the umbrella tracker: #21753

    Goal: collect the scattered Codex hook requests and bugs into one parity matrix for Full Claude Code Hook Parity (29+), while preserving this issue as the detailed thread for its specific behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghooksIssues related to event hooks

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions