Skip to main content

agentd/runtime/
events.rs

1// SPDX-License-Identifier: AGPL-3.0-only
2//! The runtime's **event vocabulary**: everything the loop reacts to arrives
3//! here — child frames, reaped children, executor results, timers, signals,
4//! and the durable inbox events (A2A messages, start-node firings, signals).
5//!
6//! Inbox events are written to the store *before* they are handed to the loop,
7//! so an event that has been accepted from the outside world survives a crash
8//! and is replayed on restart rather than being lost between accept and act.
9
10use crate::state::InboxEvent;
11use crate::subagent::protocol::AgentMsg;
12use crate::supervisor::reap::Reaped;
13use crate::supervisor::tree::NodeId;
14use serde_json::Value;
15
16/// One loop event.
17#[derive(Debug)]
18pub enum Event {
19    /// A frame from a child (turn worker / subagent).
20    Child(NodeId, AgentMsg),
21    /// A child exited (from the reaper).
22    Reaped(Reaped),
23    /// An executor thread finished a workflow step (`mcp.tool`, mapped tools…).
24    StepDone {
25        run: String,
26        step: String,
27        output: Value,
28        is_error: bool,
29        error: Option<String>,
30        tokens: u64,
31    },
32    /// An executor thread finished a mapped/MCP call made on behalf of a child's
33    /// tool request.
34    ToolDone {
35        node: NodeId,
36        req: u64,
37        result: Value,
38        is_error: bool,
39    },
40    /// Knowledge auto-context retrieval finished for a staged turn.
41    KnowledgeDone { job: u64, block: Option<String> },
42    /// A background intelligence call finished. `id` names the caller
43    /// (`goal.judge`, `human.judge:<gate>`) so the loop can route the result
44    /// back to whoever asked without blocking on the call itself.
45    Background { id: String, result: Value },
46    /// A durable timer fired.
47    TimerFired {
48        id: String,
49        owner: Value,
50        payload: Value,
51    },
52    /// A durable event was accepted (already in the inbox).
53    Inbox(InboxEvent),
54    /// An A2A transport request awaiting a loop-computed reply. The transport
55    /// thread parks on a channel until the loop answers, so every reply is
56    /// computed against a single consistent view of runtime state.
57    #[cfg(feature = "a2a")]
58    A2a(Box<super::a2a_server::A2aRequest>),
59    /// An inbound webhook awaiting a loop-computed reply.
60    #[cfg(feature = "a2a")]
61    Webhook(Box<super::webhooks::WebhookRequest>),
62    /// A `subscribe` start node's notify-then-read finished off-loop.
63    SubscribeRead {
64        server: String,
65        uri: String,
66        content: Option<Value>,
67    },
68    /// The 200 ms tick.
69    Tick,
70}
71
72/// The inbox event kinds the runtime understands.
73pub mod kinds {
74    /// A start node fired: `{workflow, node, payload, inputs}`.
75    pub const START_FIRED: &str = "start_fired";
76    /// An A2A message: `{context_id, task_id?, principal, role, parts, message_id}`.
77    pub const A2A_MESSAGE: &str = "a2a_message";
78    /// A named signal: `{name, payload, from}`.
79    pub const SIGNAL: &str = "signal";
80    /// A2A control: `{op, args}`.
81    pub const A2A_CONTROL: &str = "a2a_control";
82    /// A tool-driven request to run a workflow (`workflow.run`): `{workflow, inputs, start, requested_by}`.
83    pub const WORKFLOW_RUN: &str = "workflow_run";
84}