Skip to content

Events

AgentEvent

tau_agent_core.events.AgentEvent

A single event from the agent loop.

Reference: SUBPHASE-0.0.md, "5. Agent Events" section. Reference: docs/SUBMISSION-LIFECYCLE.md, "Provenance on events" (phase 2).

args

tau_agent_core.events.AgentEvent.args: dict[str, Any] | None

Tool execution arguments (tool_execution_start)

blocked

tau_agent_core.events.AgentEvent.blocked: bool

Whether a tool_execution_end is an extension VETO (S50) — distinct from a generic errored result, so a front-end can render "⛔ blocked by : " rather than a plain error box.

blocked_by

tau_agent_core.events.AgentEvent.blocked_by: str | None

The extension that vetoed the call (its runner path label), paired with blocked on a tool_execution_end; None otherwise.

correlation

tau_agent_core.events.AgentEvent.correlation: dict[str, Any] | None

The submission's free-form origin detail (bus subject, cron id, HTTP request id), carried through unchanged so an embedded server can fan out to the right stream. None alongside submission_id (an EMPTY dict would claim "a submission with no correlation data"; None says "no submission stamped this event" instead).

details

tau_agent_core.events.AgentEvent.details: dict[str, Any] | None

A tool's structured facts about its own execution on tool_execution_end — the path it read, the line range, the match count, the diff. What result holds is what the MODEL reads; this is what a head can render beside it. None when the tool declared none, and on every other event type.

end_reason

tau_agent_core.events.AgentEvent.end_reason: AgentEndReason | None

How an agent_end closed, as one of "done", "terminate", "aborted", "max_turns", "repeat_tool_calls" or "error". None on every other event type. This distinguishes a loop that finished from one that was cut short: before it, a run stopped by max_turns emitted the same agent_end as one where the model simply had nothing more to say, so a caller could not tell a truncated answer from a complete one.

error

tau_agent_core.events.AgentEvent.error: str | None

Why an agent_end closed, when the loop raised rather than finishing ("RuntimeError: Connection refused"). None on a normal close; always paired with is_error=True.

is_error

tau_agent_core.events.AgentEvent.is_error: bool

Whether this event represents an error

message

tau_agent_core.events.AgentEvent.message: dict[str, Any] | None

Message data (agent_start/end, message_*)

messages

tau_agent_core.events.AgentEvent.messages: list[dict[str, Any]] | None

List of messages produced (agent_end)

result

tau_agent_core.events.AgentEvent.result: Any | None

Tool execution result (tool_execution_*)

source

tau_agent_core.events.AgentEvent.source: SubmissionSource | None

The submission's origin ("interactive", "bus", "agent", …) — pi's InputSource equivalent, so a renderer can decide how to show a turn (Jupyter's rule: render every source, differently) without the core knowing any renderer exists. None alongside submission_id.

submission_id

tau_agent_core.events.AgentEvent.submission_id: str | None

The Submission that drove this turn, if any — Jupyter's parent_header. None for a turn not driven through submit() (e.g. continue_conversation(), which predates the Submission contract) — an honest "no submission", never a fabricated id.

submitter

tau_agent_core.events.AgentEvent.submitter: str | None

WHO submitted — an extension name, "human", a channel id. None alongside submission_id.

timestamp

tau_agent_core.events.AgentEvent.timestamp: int

Milliseconds since epoch

tool_call_id

tau_agent_core.events.AgentEvent.tool_call_id: str | None

Tool call ID (tool_*)

tool_name

tau_agent_core.events.AgentEvent.tool_name: str | None

Tool name (tool_*)

tool_results

tau_agent_core.events.AgentEvent.tool_results: list[dict[str, Any]] | None

List of tool result messages (turn_end)

turn_index

tau_agent_core.events.AgentEvent.turn_index: int | None

Turn number (turn_*)

type

tau_agent_core.events.AgentEvent.type: Literal['agent_start', 'agent_end', 'turn_start', 'turn_end', 'message_start', 'message_update', 'message_end', 'tool_execution_start', 'tool_execution_update', 'tool_execution_end']

Event type discriminator

EventBus

tau_agent_core.events.EventBus

Central event bus for τ-agent-core.

Provides publish/subscribe for AgentEvents. Used by: - AgentSession (for TUI consumers) - AgentLoop (for emitting events) - Extensions (for event-driven behavior)

Reference: SUBPHASE-0.0.md, "5. Agent Events" section. Reference: PHASE-3-SUBPHASE-0.md EventBus contract.

Constraint: "fire-and-forget" is a failure-isolation contract, not a scheduling one — see the module docstring. emit awaits each handler that returns a coroutine before moving to the next, so handlers are ordered with respect to each other AND to the emitter, and a handler that suspends suspends the emitter. Subscribers rely on this to pace the agent loop; do not "optimize" it into create_task.

Contract

class EventBus: def on(self, channel: str, handler: Callable) -> Callable[[], None]: ... def off(self, channel: str, handler: Callable) -> None: ... async def emit(self, event: AgentEvent) -> None: ... async def emit_channel(self, channel: str, args: Any, *kwargs: Any) -> None: ...

emit

emit(event: AgentEvent) -> None

tau_agent_core.events.EventBus.emit

Emit an event to all matching handlers.

Handlers subscribed to the specific event type AND to 'all' will receive the event. Handlers are called synchronously. This is an async method to be compatible with async consumers.

Parameters

  • event: AgentEvent — The AgentEvent to emit.

emit_channel

emit_channel(channel: str, *args: Any, **kwargs: Any) -> None

tau_agent_core.events.EventBus.emit_channel

Emit to all handlers on a specific channel.

Parameters

  • channel: str — Channel name.
  • *args: Any — (no description)
  • **kwargs: Any — (no description)

off

off(channel: str, handler: Callable) -> None

tau_agent_core.events.EventBus.off

Remove a specific handler from a channel.

Parameters

  • channel: str — Channel name.
  • handler: Callable — The handler to remove.

on

on(channel: str, handler: Callable) -> Callable[[], None]

tau_agent_core.events.EventBus.on

Subscribe to a channel.

Parameters

  • channel: str — Channel name (e.g., 'all', 'agent_start').
  • handler: Callable — Callable that receives an AgentEvent.

Returns

An unsubscribe function.

Example

bus = EventBus() def my_handler(event): ... print(event.type) unsub = bus.on('all', my_handler) await bus.emit(AgentEvent(type='agent_start', timestamp=0)) unsub() # Remove subscription

on_error

on_error(listener: ErrorListener) -> Callable[[], None]

tau_agent_core.events.EventBus.on_error

Register a listener for handler exceptions. Returns an unsubscribe.

S44 (anchor G3). A notify handler that raises is routed here instead of being swallowed — the session binds this to the same on_error surface the :class:~tau_agent_core.extensions.runner.ExtensionRunner uses, so a failing observer is as visible as a failing mutating hook.

Parameters

  • listener: ErrorListener — (no description)