1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//! What **one agent** is doing right now (DESIGN §5.1 #28b, §11) — the finest
//! live-activity fact yog derives, and the whole vocabulary of the §11 live
//! mark: one circle per agent, hue = its [`Doing`].
//!
//! Five states, total over the snapshot, and every one of them a **query** over
//! facts the tick already carries — the agent state (§5.1 #9: the executor lock
//! plus the open `response.json` fd), the latest step's tool records (#10), and
//! the kind of the last content delta in that same response file (#28b). No
//! flag is stored and none could be: yog observes neither a start nor an end,
//! only the disk at this tick.
//!
//! **This refines [`super::flight`], it does not compete with it.** The §5.1
//! #28 class is a fact about a *conversation's subtree*; this is a fact about
//! *one agent*, and the three model-call states here are the one thing #28
//! calls `Inference`. So [`super::flight::flight`] is written as a **fold over
//! this** — one authority for "is a model call streaming", one for "is a tool
//! running" — and the operator's `inference > tools > subagents` priority is
//! read off it rather than decided twice.
//!
//! **Idle is not "stopped".** The mark's green says *nothing is happening on
//! this seat right now*, which is equally true of a quiescent agent awaiting a
//! message, a killed one, and a seat with no agent in it at all. Whether a
//! branch ended well is a different question with its own carriers (the §3.5
//! state badge, the §6 marks); asking this one to answer it too would put two
//! facts on one circle.
use crate;
/// What one agent is doing, right now.
/// What `agent` is doing (§5.1 #28b).
///
/// The order is #28's, not a new one: an open model call outranks a tool,
/// because a model call is the more immediate thing and the priority is the
/// answer to "both at once" rather than a defect to design around. Under an
/// open call, the last delta splits it three ways — nothing back, thinking,
/// answering — and `None` meaning *waiting* is the general path with an empty
/// stream, not a case.
///
/// **A tool counts only under a live driver.** `output.json` never lands for a
/// tool whose driver died mid-call, so an unguarded reading would light that
/// seat forever; requiring the driver dissolves the stale record with an
/// invariant instead of an expiry rule.
/// One seat of the §11 live mark: an agent, named as every other seat names it
/// (§3.3), and what it is doing.
/// The mark's seats for the conversation rooted at `root_id`: the **eye first**
/// — the agent the operator is talking to — then its subagents in §2.3 descent
/// order. An id that roots nothing yields no seats at all, which is what an
/// operator with no conversation open is looking at: the mark at rest.
///
/// The list is **not capped here.** How many circles the mark has is the mark's
/// own fact (`theme::icon::NODE_SEATS`), and a view-model that pre-truncated to
/// it would leave the seat that says *how many were dropped* with nothing to
/// count.