Skip to content

feat: expose warning-severity advisories for AIR - #393

Open
nikita-ashihmin wants to merge 2 commits into
agentclientprotocol:mainfrom
nikita-ashihmin:codex/ijai-993-warning-severity-advisories
Open

feat: expose warning-severity advisories for AIR#393
nikita-ashihmin wants to merge 2 commits into
agentclientprotocol:mainfrom
nikita-ashihmin:codex/ijai-993-warning-severity-advisories

Conversation

@nikita-ashihmin

@nikita-ashihmin nikita-ashihmin commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Problem

Codex reports non-fatal advisories as dedicated app-server notifications, but we flatten them into assistant text:

private createWarningEvent(event: WarningNotification): UpdateSessionEvent {
    return createAgentTextMessageChunk(`Warning: ${event.message}\n\n`);
}

Called with neither messageId nor meta, so the client sees an ordinary agent_message_chunk and cannot tell it from the model's own output. In practice a compaction hint renders as if the agent had said it:

{"type":"MessageDelta","payload":{"messageId":"acp-…-msg-0","role":"Agent",
 "contentDelta":"Warning: Heads up: Long threads and multiple compactions can cause the model to be less accurate. …"}}

The structure exists upstream — codex emits warning { message } from codex_core::compact — and is lost here, at the adapter.

Change

Extends the typed session-failure extension from #383 instead of adding a parallel notice concept:

  • severity?: "error" | "warning" on the record. Optional, and absent means error, so a client that predates this change keeps treating every record it receives as a failure.
  • New advisory category, whose wording comes from the app-server rather than our canned presentation table.
  • Advisories are recorded in their own sessionNotice slot under a :notice id, so they never disturb the revision bookkeeping of an in-flight terminal failure. The two coexist and the client decides which to surface.

Three notifications now route through it, all gated on the client having negotiated the sessionFailure capability:

notification without the capability
warning unchanged Warning: <message> text
configWarning unchanged Config warning: <summary> text
deprecationNotice unchanged: still dropped

deprecationNotice carries the same {summary, details} shape as configWarning, so the two now share one formatting rule (joinSummaryAndDetails). It had no legacy rendering to preserve, so it stays invisible to clients that did not negotiate typed records rather than newly appearing as text.

Scoped out deliberately

  • thread/compacted keeps its italic transcript line — informational rather than a warning, and promoting it to a banner would be louder than the current UX.
  • guardianWarning stays dropped: the guardian review it refers to is already rendered as a tool call, so a banner may duplicate it. Worth revisiting against real traffic.
  • windows/worldWritableWarning stays dropped: it carries a structured path list, so it needs its own rendering rather than a text advisory.

Tests

Six cases added to typed-session-failure-wire.test.ts, all exercised end to end over the real ACP transport:

  • warning arrives as an advisory record with severity: "warning" — and asserts the payload contains neither agent_message_chunk nor the Warning: prefix
  • configWarning folds details into the advisory message
  • deprecationNotice surfaces as an advisory
  • without the capability: warning text is byte-identical to today, and deprecationNotice still produces nothing
  • an advisory and a terminal error coexist with distinct ids, each starting its own revision sequence, and the error record carries no severity

npx vitest run: 406 passed, 28 skipped. npm run typecheck clean.

Client side: IJAI-993.

Codex reports non-fatal advisories as dedicated app-server notifications, but
codex-acp flattened them into assistant text: `warning` became
`Warning: <message>` and `configWarning` became `Config warning: <summary>`,
both as an untagged `agent_message_chunk`. The client could not tell them from
the model's own words, so a compaction hint rendered as if the agent had said it.

Extend the typed session-failure extension from agentclientprotocol#383 rather than adding a
parallel notice concept: a record now carries an optional `severity` of `error`
or `warning`, plus an `advisory` category whose wording comes from the
app-server. Clients that negotiated the `sessionFailure` capability receive the
structured record; everyone else keeps the existing text, unchanged.

Advisories live in their own `sessionNotice` slot under a `:notice` id, so they
never disturb the revision bookkeeping of an in-flight terminal failure — the
two records coexist and the client decides which to show.

`severity` is optional on the wire and absent means `error`, so an AIR build
that predates this change keeps treating every record it receives as a failure.

Scoped out deliberately: `thread/compacted` keeps its italic transcript line
(informational, not a warning), and `guardianWarning` / `deprecationNotice`
remain dropped as before.
deprecationNotice carries the same {summary, details} shape as configWarning,
so treating the two differently was inertia, not design: it was simply already
being dropped.

Unlike warning and configWarning it has no legacy text rendering to preserve,
so it is emitted only to clients that negotiated typed records. A client that
did not must keep seeing exactly what it sees today, which is nothing.

Extracts joinSummaryAndDetails so the two notifications share one formatting
rule rather than repeating it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant