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}