Summary
Replace V2 session status/retry/error ambiguity with explicit execution lifecycle, retry, and typed error events.
Source discussion: https://discord.com/channels/1391832426048651334/1522263729318658181
Related existing content-filter issue: #34835
Decisions / direction
Do not keep V2 session.status. Replace with execution lifecycle events:
session.execution.started {}
session.execution.succeeded {}
session.execution.failed { error }
session.execution.interrupted { reason }
interrupted.reason should distinguish at least user interruption from shutdown so future recovery can resume sessions interrupted only by server shutdown.
Retry should not reduce into many session messages. It should reduce into current assistant retry state:
session.retry.scheduled { attempt, at, error }
Assistant.retry? = { attempt, at, error }
No retry message, action, next, or isRetryable; UI derives display/actions from structured error.
Error contract
Errors across execution.failed, step.failed, tool.failed, and retry.scheduled should use a closed structured union with type as the discriminator.
Rules:
- Type strings are dot-cased with single-word segments, e.g.
permission.rejected, tool.input.invalid.
- No underscores.
- UI/model logic switches on
error.type.
- Known variants should carry enough structured data to derive UI text and agent/model text.
- Generic fallback can use
unknown with message and optional agent.
- Research best Effect/Schema
TaggedErrorClass bridge from internal _tag to event JSON type.
Tool error sketch:
type ToolError =
| { type: "permission.rejected"; permission: string; resource: string }
| { type: "tool.input.invalid"; message: string; input?: unknown }
| { type: "tool.execution.cancelled"; reason?: "user" | "shutdown" | "timeout" }
| { type: "unknown"; message: string; agent?: string }
Acceptance case
Provider finish: "content-filter" must not project as an empty successful-looking assistant message. It should render a visible terminal blocked/error state consistently across clients.
Summary
Replace V2 session status/retry/error ambiguity with explicit execution lifecycle, retry, and typed error events.
Source discussion: https://discord.com/channels/1391832426048651334/1522263729318658181
Related existing content-filter issue: #34835
Decisions / direction
Do not keep V2
session.status. Replace with execution lifecycle events:interrupted.reasonshould distinguish at least user interruption from shutdown so future recovery can resume sessions interrupted only by server shutdown.Retry should not reduce into many session messages. It should reduce into current assistant retry state:
No retry
message,action,next, orisRetryable; UI derives display/actions from structured error.Error contract
Errors across
execution.failed,step.failed,tool.failed, andretry.scheduledshould use a closed structured union withtypeas the discriminator.Rules:
permission.rejected,tool.input.invalid.error.type.unknownwithmessageand optionalagent.TaggedErrorClassbridge from internal_tagto event JSONtype.Tool error sketch:
Acceptance case
Provider
finish: "content-filter"must not project as an empty successful-looking assistant message. It should render a visible terminal blocked/error state consistently across clients.