wire/frames/query.rs
1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4// Copyright (c) 2026 2youg1 and the sprawling contributors
5
6//! Everything a page may ask, and the name table the schema hash is
7//! built from; `frames` says what every Query shares.
8
9use kernel::{Address, B3Hash, GitOid, Locator, NodeId, RunId, Seq};
10use serde::{Deserialize, Serialize};
11
12use crate::named_frames::named_frames;
13
14named_frames! {
15/// A read of the city; the module documentation says what all of them share.
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17#[serde(rename_all = "snake_case")]
18#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
19pub enum Query {
20 /// A bounded slice of the one history, ending just before `before`
21 /// or at the tail when that is absent.
22 ///
23 /// The server broadcasts what happens next and never what happened,
24 /// so a page that only listened would see a city that had been
25 /// running for a month as an empty one. Bounded because the whole ledger is not a
26 /// thing to put on a socket, and paged backwards because what a
27 /// reader wants first is the end.
28 History {
29 before: Option<Seq>,
30 limit: u32,
31 },
32 /// The same slice, narrowed to one session.
33 ///
34 /// [`Query::History`] carries no run, so a client watching four
35 /// sessions divides one bounded slice between them and a session
36 /// that started before the tab did is not in it at all.
37 /// [`Query::RunView`] says whether a run exists and where it got to,
38 /// not what happened in it. Answered with [`HistoryAnswer`], because
39 /// "a page of history" already has a shape.
40 RunHistory {
41 run: RunId,
42 before: Option<Seq>,
43 limit: u32,
44 },
45 /// The records of the one history between two sequence numbers, both
46 /// ends included: the question a page asks when the event stream tells
47 /// it what it skipped.
48 ///
49 /// [`Query::History`] walks backwards from a cursor, which answers
50 /// "what came before this" and cannot answer "what happened between
51 /// these two". A gap has a near end as well as a far one, and a page
52 /// that walked to the gap's near end from the tail would pay for every
53 /// record in between.
54 ///
55 /// Answered with [`HistoryRangeAnswer`](crate::HistoryRangeAnswer)
56 /// rather than with [`HistoryAnswer`](crate::HistoryAnswer): the
57 /// question carries no cursor a reader holds on to, so the answer has
58 /// to name the slice it is, and a page filling a gap while its record
59 /// view is open is asking both questions at once. A range the Ledger
60 /// holds more records for than `limit` allows is answered up to the
61 /// limit, and the answer says where to ask next.
62 HistoryRange {
63 from: Seq,
64 to: Seq,
65 limit: u32,
66 },
67 /// The stretches of one room, newest first (`crates/wire/spec/Answer/Sessions.lean` §8-71).
68 Sessions {
69 room: Address,
70 },
71 /// What moved between two checkpoints: paths and counts, never patch
72 /// text.
73 ///
74 /// The caller names both ends because it already knows them - a
75 /// checkpoint's oid is in the `checkpoint_committed` payload the
76 /// client folded - and computing the pair a second time on the
77 /// server would be a second answer to "which checkpoints belong to this
78 /// session". Both oids are immutable, so the answer is cacheable
79 /// forever by anybody who wants to.
80 ///
81 /// `head` absent means the working tree: a wave still running has
82 /// written files no checkpoint holds yet, and a list that ignored
83 /// them would describe the session as it was one checkpoint ago.
84 Changes {
85 base: GitOid,
86 head: Option<GitOid>,
87 },
88 /// The patch text of one file between two checkpoints.
89 ///
90 /// **A separate frame from [`Query::Changes`], and that is the
91 /// point.** `Changes` costs what the number of changed files costs
92 /// and answers which files moved; this costs what one file costs
93 /// and answers what moved inside it, the request of its own that
94 /// `storage::changes` says a hunk needs.
95 ///
96 /// One path per frame; there is no spelling that asks for all of
97 /// them. A line the credential scan matched is not echoed: the
98 /// answer reports its line number and the reason, because printing
99 /// the bytes to prove a leak is the leak.
100 Hunks {
101 oid_a: GitOid,
102 oid_b: GitOid,
103 path: String,
104 },
105 /// Which run wrote one commit the city made.
106 ///
107 /// Every commit this city makes carries five git trailers naming the
108 /// run, the resident, the model, the effort and the city itself.
109 /// Those trailers are a projection for readers outside the city, so
110 /// this query is answered from the Ledger and never from git: a city
111 /// exported and restored elsewhere, with no `.git` beside it, still
112 /// answers.
113 ///
114 /// An oid this city never wrote is [`Answer::Unavailable`], for the
115 /// reason [`Query::Changes`] gives: "I did not write it" and "it
116 /// changed nothing" are different answers.
117 Commit {
118 oid: GitOid,
119 },
120 RunView {
121 run: RunId,
122 },
123 CityView,
124 ApprovalQueue,
125 InboxView {
126 addr: Address,
127 },
128 Metrics,
129 CostView,
130 ArchiveSearch {
131 needle: String,
132 },
133 RegistryView,
134 DiscardView,
135 /// What is attached and what is chosen: the settings page's read.
136 EndpointView,
137 /// The vendors this city knows by host, and the base URL of each
138 /// face they document, so a person picks a vendor rather than
139 /// pasting its address.
140 KnownHosts,
141 /// The ACP page's catalog, detected agents and added agents
142 /// (`crates/wire/spec/Answer/Agents.lean` §8-90).
143 AgentCatalog,
144 /// One pasted launch spec read as an agent entry, or refused; the paste
145 /// grammar lives in the city alone (§8-90).
146 ParseAgentSpec { text: String },
147 /// The browsers paired at this machine's door (`crates/wire/spec/Answer/Devices.lean` §8-91).
148 Devices,
149 /// One building's own files and its archive - the pages an agent
150 /// writes for the next agent, which are also the pages a person
151 /// reads to know what happened in there.
152 BuildingView {
153 addr: Address,
154 },
155 /// What the city calls the person and the Mayor, read from the two
156 /// identity areas at the moment of asking (`crates/wire/spec/Answer/Identity.lean` §8-59).
157 Identity,
158 /// The schedule and the watch table, read at the moment of asking:
159 /// shown on the page, written by hand (`crates/wire/spec/Answer/Automation.lean` §8-62).
160 Automation,
161 /// A candidate user id: gh's login for a host (`crates/wire/spec/Answer/Github.lean` §8-67).
162 GithubLogin(Option<String>),
163 /// The first-run guide's progress (`crates/wire/spec/Guide.lean` §8-68).
164 Guide,
165 /// Who answers for this city, and what has been answered on the
166 /// person's behalf.
167 ///
168 /// Both halves in one answer because a reader needs both to make
169 /// sense of either: a list of decisions with nobody named beside it
170 /// does not say whether the person delegated them, and a delegation
171 /// with nothing decided under it does not say whether it has ever
172 /// been used.
173 Governance,
174 /// One session, folded into the rounds a person reads.
175 ///
176 /// Answered server-side, because the wire is the whole API
177 /// (ARCHITECTURE.md section 8): a second client must be able to
178 /// draw a session without reimplementing the fold.
179 ///
180 /// Bounded by [`HISTORY_MAX`](crate::HISTORY_MAX) records, which is
181 /// the same slice [`Query::RunHistory`] answers: where the fold runs
182 /// must not change how much of a session it can see.
183 Rounds {
184 run: RunId,
185 },
186 /// What one run left that somebody can check it by: the screenshots
187 /// it stored and the completions it closed plan nodes with.
188 ///
189 /// Locators, never bytes. Fetching a picture is the asset
190 /// endpoint's, for the reason [`Query::Hunks`] is separate from
191 /// [`Query::Changes`]: one question must not carry the cost of
192 /// every answer somebody might go on to want.
193 Evidence {
194 run: RunId,
195 },
196 /// What one plan node has cost, and which runs spent it.
197 ///
198 /// A node nobody claimed answers zero, not `Unavailable`: "no run
199 /// has held this node" is a true answer. The money comes from
200 /// `storage::attribution` and is priced nowhere but `gateway::cost`.
201 CostOf {
202 node: NodeId,
203 },
204 /// One directory of the city, one level deep; `None` is the root.
205 ///
206 /// The tree is the product - a building is a directory and a room
207 /// is one inside it - and this is the frame that opens a room. One level per question, so looking at a room
208 /// never pays for the ledger segments beside it.
209 Listing {
210 at: Option<Address>,
211 },
212 /// Files under `under` whose name holds `text` (`crates/wire/spec/Answer/Find.lean` §8-82).
213 Find { under: Address, text: String },
214 /// One file of the city as a version, with its first window
215 /// (`crates/wire/spec/Answer/Document.lean` §8-69). The path is an `Address`, so it cannot leave the
216 /// city root; the reserved subtree is readable on purpose, because
217 /// this door answers the person and not a resident.
218 Document {
219 at: Address,
220 },
221 /// The proposal cards still open on one document (`crates/wire/spec/Answer/Proposals.lean` §8-73).
222 Proposals(Address),
223 /// Every proposal card still open in the city, by document and
224 /// offer time (`crates/wire/spec/Answer/Proposals.lean` §8-73).
225 OpenProposals,
226 /// One window of a stored version, by its version (`crates/wire/spec/Answer/Range.lean` §8-70).
227 Range { version: B3Hash, range: documents::Span },
228 /// One document's versions, newest first (`crates/wire/spec/Answer/DocumentVersions.lean` §8-83).
229 Versions { at: Address },
230 /// One window of a stored object's bytes (`crates/wire/spec/Answer/DocumentBytes.lean` §8-80).
231 Bytes { version: B3Hash, range: documents::Span },
232 /// A stored Markdown version as one HTML file (`crates/wire/spec/Answer/DocumentBytes.lean` §8-81).
233 Export { at: Address, version: B3Hash },
234 /// One window of a stored Markdown version, laid out (`crates/wire/spec/Answer/Preview.lean` §8-74).
235 Preview { version: B3Hash, viewport: documents::Span },
236 /// A reply's text, laid out by the preview's grammar (`crates/wire/spec/Answer/Preview.lean` §8-75).
237 Reply {
238 text: String,
239 state: documents::ReplyState,
240 },
241 /// The commits this city made, newest first, a page at a time.
242 ///
243 /// `building` keeps the commits whose actor worked at that address
244 /// or under it - the actor is the authority and a session is its
245 /// projection, so filtering by session would lose the runs nobody
246 /// opened a session for. `before` is exclusive, as in
247 /// [`Query::History`]; the next page asks with the last `seq` it was
248 /// handed. Only commits this city wrote are listed: an oid a person
249 /// rewrote into trunk by hand is not this city's, and it says so by
250 /// leaving it out rather than reading a trailer back.
251 Commits {
252 building: Option<Address>,
253 before: Option<Seq>,
254 limit: u32,
255 },
256 /// What this machine has, and what this city still needs of it.
257 ///
258 /// Answered from what the city found when it started, not from a
259 /// fresh look: every item is a program asked its version, and a
260 /// query that started a dozen processes would hold the one thread
261 /// that answers every other read. A city that has not looked
262 /// answers [`Answer::Unavailable`], which is what a worker driven
263 /// one command at a time is.
264 Doctor,
265 /// The system prompt one run was frozen with: four segments, their
266 /// text, and what each was assembled from.
267 ///
268 /// Answered from the run's own `prompt_assembled` record joined to
269 /// the store, never by assembling a prefix again: what a person
270 /// needs to see is the bytes that were sent, and a second assembly
271 /// taken now would read files that have moved since. A segment the
272 /// store no longer holds says so by name rather than by an empty
273 /// string, because "this segment was empty" and "these bytes are
274 /// gone" are different answers.
275 Prefix {
276 run: RunId,
277 },
278 /// One object of the content store, bounded, with the cut stated.
279 ///
280 /// The general read behind [`Query::Prefix`], and the door every
281 /// other `cas:` reference a page is shown can be opened through -
282 /// an approval's artifact, a discarded file's way back, a norm on a
283 /// handoff's must-read list. Only the `cas:` scheme is answered: a
284 /// `file:` locator names a path in the tree, which is
285 /// [`Query::Document`]'s question and must not have a second answer
286 /// here.
287 Content {
288 locator: Locator,
289 },
290 /// What one building can do, and where each of those came from.
291 ///
292 /// Every shelf in one answer because a reader needs them together
293 /// to make sense of any: a row carries [`crate::SkillShelf`], which
294 /// names the shelves and where each row sits on one. Which runs
295 /// pinned a skill is folded from `run_started`, so a shelf nothing
296 /// has ever used says so instead of looking unused because nobody
297 /// wrote it down.
298 Skills { building: Address },
299 /// What is uncommitted in one building right now: the branch, how
300 /// far it has drifted from its upstream, the files that moved, and
301 /// the last checkpoint the city checkpointed there.
302 ///
303 /// Read from git at the moment of asking, which is the one answer
304 /// here that is about the disk rather than about the history: a
305 /// working tree is what a person is looking at, and the Ledger
306 /// records checkpoints rather than edits. The checkpoint beside it comes
307 /// from the history, for the reason [`Query::Commit`] gives.
308 GitStatus { building: Address },
309 /// Whether each tool server one address reaches is answering, and what it offers, handshaken
310 /// once at the moment of asking (`crates/wire/spec/Answer/McpHealth.lean` §8-34).
311 McpHealth {
312 addr: Address,
313 },
314 /// Which outside applications the broker offers, and where each one stands for this city
315 /// (`crates/wire/spec/Answer/Toolkits.lean` §8-35b).
316 Toolkits,
317 /// Which release this city is running, and which ones npm and crates.io offer
318 /// (`crates/wire/spec/Answer/Release.lean` §8-36).
319 NewestRelease,
320 /// The host's privacy controls, their current values and what this
321 /// app's history discloses to the account asking
322 /// (`crates/wire/spec/Privacy.lean` §8-88). Answered by the city's
323 /// listener, never through a remote device.
324 Privacy,
325 /// Everything this person settled about their own reading of the
326 /// city: the language, the appearance, the chords they rebound.
327 ///
328 /// The whole table in one answer, because a browser that cached
329 /// these row by row had a dozen names for them and read three of
330 /// those names back with its own idea of what a missing one means.
331 /// The file is the authority and this is the only door to it.
332 Preferences,
333 /// What one address is actually governed by, value by value, with
334 /// the file each value came from.
335 ///
336 /// **The layer is half the answer.** The three files form a ladder
337 /// - the city's, the building's, the room's - and a page told only
338 /// the resolved figure cannot say whether it is looking at
339 /// something this address states or something it inherited, so it
340 /// would have to read all three and climb the ladder a second
341 /// time. Two climbs of one ladder is two answers to one question.
342 Config {
343 addr: Address,
344 },
345 /// What each named run has been billed, for the runs a cost view's
346 /// `by_run` leaves out. At most
347 /// [`RUN_COSTS_MAX`](crate::RUN_COSTS_MAX) runs are answered per
348 /// question and the rest go unanswered, so a directory that names
349 /// its runs newest first shows the cost of the newest cold runs; the
350 /// money is folded from the Ledger for a run the city no longer
351 /// holds warm, so the cost is proportional to the runs asked about.
352 RunCosts {
353 runs: Vec<RunId>,
354 },
355 /// The newest release of one requirement-table item, asked of its
356 /// publisher (`crates/sprawling/Spec.lean` §8-120).
357 ///
358 /// Asked by the dependency page for each item once the page is open,
359 /// which is the person asking whether their tools are behind; the
360 /// city asks nothing on its own schedule. One item per question, so
361 /// a slow publisher holds up its own row and no other. A source that
362 /// cannot be reached is [`DoctorNewest::Refused`](crate::DoctorNewest::Refused)
363 /// rather than [`Answer::Unavailable`](crate::Answer::Unavailable).
364 UpstreamVersion {
365 item: String,
366 },
367 /// Every skill's contents, audit and uses by day, folded from the whole ledger when the
368 /// skill page opens (`crates/wire/spec/Reading.lean` D28, D33); `skill` narrows it to one.
369 SkillUsage { skill: Option<String> },
370 /// The same table per tool server and tool; `server` narrows it to one.
371 McpUsage { server: Option<String> },
372 /// Every use as one JSONL or CSV row for the User to download; the city writes no file.
373 UsageExport { what: crate::UsageKind, format: crate::ExportFormat },
374 /// Every shell interpreter's calls and failures by class, folded from the whole ledger in
375 /// the same pass as the usage tables (`crates/wire/spec/Reading.lean` D48).
376 Shells,
377}
378
379/// The Query surface, in declaration order — the order the handshake
380/// hash mixes these names in.
381pub const QUERY_NAMES;
382}