Events And Approvals
Event Transport
Odyssey emits session-scoped EventMsg values over a broadcast channel. You can consume them in
two ways:
- directly from
OdysseyRuntime::subscribe_session(session_id) - through HTTP server-sent events at
GET /sessions/{id}/events
The HTTP server serializes each EventMsg as JSON and writes it into an SSE data: frame.
Event Categories
EventPayload currently includes:
TurnStartedTurnCompletedAgentMessageDeltaReasoningDeltaReasoningSectionBreakToolCallStartedToolCallDeltaToolCallFinishedExecCommandBeginExecCommandOutputDeltaExecCommandEndPermissionRequestedApprovalResolvedPlanUpdateError
Typical Turn Sequence
A normal turn usually looks like this:
TurnStarted- zero or more
ReasoningDeltaandReasoningSectionBreak - zero or more
AgentMessageDelta - zero or more tool events:
ToolCallStarted,ToolCallDelta,ToolCallFinished - if a tool or command needs approval:
PermissionRequested, then laterApprovalResolved - if a tool or operator command runs a process:
ExecCommandBegin,ExecCommandOutputDelta,ExecCommandEnd TurnCompleted
If the turn fails, the stream can include Error instead of a successful completion path.
Tool Events Versus Command Events
There are two related but different event families:
- tool events describe the logical tool call seen by the agent
- exec-command events describe a real subprocess started inside the sandbox
For example:
ReadandWriteusually emit tool events onlyBashemits tool events and may also emitExecCommand*events for the shell processrun_session_commandemitsExecCommand*events directly because it is an operator command path, not a tool call
ExecCommandOutputDelta includes:
exec_idstreamasstdoutorstderr- the output delta text
Approval Flow
Tool permission rules in agent.yaml can resolve to:
allowdenyask
When a rule resolves to ask, the runtime:
- emits
PermissionRequested - stores the request in the in-memory approval store
- pauses the tool until an approval decision arrives
Approval decisions are:
allow_onceallow_alwaysdeny
You can resolve a request through:
OdysseyRuntime::resolve_approval(request_id, decision)POST /approvals/{request_id}
Approval Scope
allow_always is session-scoped only.
It is cleared when:
- the runtime restarts
- the session is deleted
It is not persisted to disk and does not affect other sessions.
Turn Context In Events
TurnStarted includes a TurnContext snapshot with the effective runtime values for that turn.
Today that includes:
cwdmodelsandbox_mode- optional metadata
This is useful when you allow per-turn model or working-directory overrides and want subscribers to see the resolved context that actually executed.
Minimal SSE Example
data: {"id":"...","session_id":"...","created_at":"...","payload":{"type":"turn_started","payload":{"turn_id":"...","context":{"cwd":"...","model":{"provider":"openai","name":"gpt-4.1-mini","config":null},"sandbox_mode":"read_only","approval_policy":null,"metadata":{}}}}}
Subscribers should treat the stream as append-only session telemetry. The runtime uses it for TUI updates, tool progress, approval prompts, and completion reporting.