pub trait Api:
Send
+ Sync
+ 'static {
Show 19 methods
// Required methods
fn list_agents(
&self,
) -> impl Future<Output = Result<AgentList, Problem>> + Send;
fn get_costs(
&self,
query: GetCostsQuery,
) -> impl Future<Output = Result<CostReport, Problem>> + Send;
fn list_pending(
&self,
) -> impl Future<Output = Result<PendingList, Problem>> + Send;
fn send_flight(
&self,
body: SendFlightRequest,
) -> impl Future<Output = Result<FlightAccepted, Problem>> + Send;
fn cancel_flight(
&self,
path: CancelFlightPath,
) -> impl Future<Output = Result<PendingList, Problem>> + Send;
fn get_graph(
&self,
query: GetGraphQuery,
) -> impl Future<Output = Result<RouteMap, Problem>> + Send;
fn engage_ground_stop(
&self,
) -> impl Future<Output = Result<GroundStop, Problem>> + Send;
fn release_ground_stop(
&self,
) -> impl Future<Output = Result<GroundStop, Problem>> + Send;
fn get_health(&self) -> impl Future<Output = Result<Health, Problem>> + Send;
fn list_help(
&self,
query: ListHelpQuery,
) -> impl Future<Output = Result<HelpList, Problem>> + Send;
fn resolve_help(
&self,
body: ResolveHelpRequest,
) -> impl Future<Output = Result<HelpResolved, Problem>> + Send;
fn list_itineraries(
&self,
query: ListItinerariesQuery,
) -> impl Future<Output = Result<ItineraryList, Problem>> + Send;
fn list_learnings(
&self,
query: ListLearningsQuery,
) -> impl Future<Output = Result<LearningList, Problem>> + Send;
fn judge_learning(
&self,
path: JudgeLearningPath,
body: JudgeLearningRequest,
) -> impl Future<Output = Result<Learning, Problem>> + Send;
fn list_pipelines(
&self,
) -> impl Future<Output = Result<PipelineList, Problem>> + Send;
fn list_runs(
&self,
query: ListRunsQuery,
) -> impl Future<Output = Result<RunList, Problem>> + Send;
fn get_run(
&self,
path: GetRunPath,
) -> impl Future<Output = Result<Run, Problem>> + Send;
fn get_report(
&self,
path: GetReportPath,
) -> impl Future<Output = Result<Report, Problem>> + Send;
fn stream_run(
&self,
path: StreamRunPath,
) -> impl Future<Output = Result<EventStream, Problem>> + Send;
}Expand description
Everything the Tower must implement to serve this API.
One method per operationId. Adding an operation to api/openapi.yaml adds a method
here, so an unimplemented endpoint is a compile error rather than a 404 discovered in
production.
Required Methods§
Sourcefn list_agents(&self) -> impl Future<Output = Result<AgentList, Problem>> + Send
fn list_agents(&self) -> impl Future<Output = Result<AgentList, Problem>> + Send
Every configured agent and the route map between them.
This is the UI’s graph view, and it is also how a human answers “what can talk to what” without reading the TOML.
GET /agents
Sourcefn get_costs(
&self,
query: GetCostsQuery,
) -> impl Future<Output = Result<CostReport, Problem>> + Send
fn get_costs( &self, query: GetCostsQuery, ) -> impl Future<Output = Result<CostReport, Problem>> + Send
What the factory has spent, and how much of that is measured.
A single Fuel figure says whether a chain may continue. This says where the money went — per agent, per model — and, critically, how much of the total the runners actually reported rather than Layover inferring it from a rate card.
Treat confidence as part of the number. A total that is mostly measured is still not
measured, so any estimated or unreported run downgrades the whole figure.
GET /costs
Sourcefn list_pending(
&self,
) -> impl Future<Output = Result<PendingList, Problem>> + Send
fn list_pending( &self, ) -> impl Future<Output = Result<PendingList, Problem>> + Send
Flights waiting for something to dispatch them.
Work that has been asked for and not yet started. Until the supervisor exists this is everything anybody has triggered; afterwards it is the backlog.
GET /flights
Sourcefn send_flight(
&self,
body: SendFlightRequest,
) -> impl Future<Output = Result<FlightAccepted, Problem>> + Send
fn send_flight( &self, body: SendFlightRequest, ) -> impl Future<Output = Result<FlightAccepted, Problem>> + Send
Start work by sending the first flight of a new itinerary.
Give either a pipeline or a to. A pipeline is the normal way in: it names the entry
agent and declares which flags may be set. A bare to sends to an agent marked
entry = true and accepts no flags.
The flight is queued, not run. Dispatching it needs the supervisor, which is not part
of this release, so 202 means the work is booked and durable — it will start when there
is something to start it. GET /flights shows what is waiting.
POST /flights
Sourcefn cancel_flight(
&self,
path: CancelFlightPath,
) -> impl Future<Output = Result<PendingList, Problem>> + Send
fn cancel_flight( &self, path: CancelFlightPath, ) -> impl Future<Output = Result<PendingList, Problem>> + Send
Take a queued flight back off the queue.
Only work that has not started can be cancelled. A run that is already going is stopped with a Ground Stop, which is a different decision with a different blast radius — one flight versus the whole factory — and conflating them would make the smaller action feel as dangerous as the larger one.
DELETE /flights/{flight_id}
Sourcefn get_graph(
&self,
query: GetGraphQuery,
) -> impl Future<Output = Result<RouteMap, Problem>> + Send
fn get_graph( &self, query: GetGraphQuery, ) -> impl Future<Output = Result<RouteMap, Problem>> + Send
The route map as a diagram, with what is happening drawn on it.
Mermaid source, generated from the configuration as it is on disk right now. Edit
layover.toml and reload; the diagram changes with it, because nothing here is baked at
build time.
Agents currently running, waiting at a barrier, or freshly failed are coloured. An edge into a joined agent from a sender the barrier does not name is drawn as bypassing it, which is what actually happens.
GET /graph
Sourcefn engage_ground_stop(
&self,
) -> impl Future<Output = Result<GroundStop, Problem>> + Send
fn engage_ground_stop( &self, ) -> impl Future<Output = Result<GroundStop, Problem>> + Send
Halt everything.
Ground Stop is a file on disk rather than in-memory state, so it survives a Tower crash and can be set by hand when nothing else is responding.
POST /ground-stop
Sourcefn release_ground_stop(
&self,
) -> impl Future<Output = Result<GroundStop, Problem>> + Send
fn release_ground_stop( &self, ) -> impl Future<Output = Result<GroundStop, Problem>> + Send
Resume.
DELETE /ground-stop
Sourcefn get_health(&self) -> impl Future<Output = Result<Health, Problem>> + Send
fn get_health(&self) -> impl Future<Output = Result<Health, Problem>> + Send
Liveness, version and whether a Ground Stop is engaged.
GET /health
Sourcefn list_help(
&self,
query: ListHelpQuery,
) -> impl Future<Output = Result<HelpList, Problem>> + Send
fn list_help( &self, query: ListHelpQuery, ) -> impl Future<Output = Result<HelpList, Problem>> + Send
What agents are stuck on.
A lights-out factory’s worst failure is not a crash — a crash is loud. It is an agent that quietly cannot do what it was asked, produces something plausible anyway, and passes it downstream. This is the channel that stops that being invisible.
Measured against a working prototype, five of six requests were permission or access
failures, so access is worth filtering for first.
GET /help
Sourcefn resolve_help(
&self,
body: ResolveHelpRequest,
) -> impl Future<Output = Result<HelpResolved, Problem>> + Send
fn resolve_help( &self, body: ResolveHelpRequest, ) -> impl Future<Output = Result<HelpResolved, Problem>> + Send
Mark help requests as dealt with.
Resolving says the blocker is gone, not I have read this. An agent that raises the same problem on its next run will raise it again, which is the point: a list that clears itself on being looked at stops being evidence of anything.
Narrow by agent, blocker or run. An empty body resolves every open request in the window, which is what you want after fixing something that stopped everything.
POST /help/resolve
Sourcefn list_itineraries(
&self,
query: ListItinerariesQuery,
) -> impl Future<Output = Result<ItineraryList, Problem>> + Send
fn list_itineraries( &self, query: ListItinerariesQuery, ) -> impl Future<Output = Result<ItineraryList, Problem>> + Send
Chains of work, and what became of each.
A run is one agent doing one thing; an itinerary is the whole causal chain and the budget it shares. The distinction matters most when something goes wrong: a chain can be stalled — every run in it succeeded and nothing will ever happen again — and a list of runs cannot show that, because there is no failed run to point at.
GET /itineraries
Sourcefn list_learnings(
&self,
query: ListLearningsQuery,
) -> impl Future<Output = Result<LearningList, Problem>> + Send
fn list_learnings( &self, query: ListLearningsQuery, ) -> impl Future<Output = Result<LearningList, Problem>> + Send
What agents have worked out, and how well established it is.
A learning applies as soon as it is proposed and expires unless later runs arrive at it independently. There is no approval queue, deliberately: a sibling project built one and after 22 days held 88 learnings, none ever approved, so not one had ever reached a run.
A learning becomes permanent through independent rediscovery, which is evidence, rather
than through its impact rating, which is the agent’s own claim about its own work.
GET /learnings
Sourcefn judge_learning(
&self,
path: JudgeLearningPath,
body: JudgeLearningRequest,
) -> impl Future<Output = Result<Learning, Problem>> + Send
fn judge_learning( &self, path: JudgeLearningPath, body: JudgeLearningRequest, ) -> impl Future<Output = Result<Learning, Problem>> + Send
Settle a learning, either way.
This is not an approval queue. A learning applies from the moment it is proposed, and nothing is waiting on you. A sibling project gated learnings behind approval and after 22 days held 88 of them, none ever approved, so not one had ever reached a run.
This is the override. confirmed means “this is real, keep it indefinitely” and spares it
from lapsing. rejected means “this is wrong, stop applying it” and takes it out of every
future run. Both are judgements a person makes about something already in use, not
permission for it to start being used.
PATCH /learnings/{learning_id}
Sourcefn list_pipelines(
&self,
) -> impl Future<Output = Result<PipelineList, Problem>> + Send
fn list_pipelines( &self, ) -> impl Future<Output = Result<PipelineList, Problem>> + Send
Every declared pipeline, its trigger and the flags it accepts.
GET /pipelines
Sourcefn list_runs(
&self,
query: ListRunsQuery,
) -> impl Future<Output = Result<RunList, Problem>> + Send
fn list_runs( &self, query: ListRunsQuery, ) -> impl Future<Output = Result<RunList, Problem>> + Send
Runs, live and historical.
GET /runs
Sourcefn get_run(
&self,
path: GetRunPath,
) -> impl Future<Output = Result<Run, Problem>> + Send
fn get_run( &self, path: GetRunPath, ) -> impl Future<Output = Result<Run, Problem>> + Send
One run, including how it ended.
GET /runs/{run_id}
Sourcefn get_report(
&self,
path: GetReportPath,
) -> impl Future<Output = Result<Report, Problem>> + Send
fn get_report( &self, path: GetReportPath, ) -> impl Future<Output = Result<Report, Problem>> + Send
What the agent wrote about this run.
A transcript is not a report: it contains every approach the agent abandoned, and reading one to find out what happened is slower than doing the work again. This is the agent’’s own account of what it concluded.
GET /runs/{run_id}/report
Sourcefn stream_run(
&self,
path: StreamRunPath,
) -> impl Future<Output = Result<EventStream, Problem>> + Send
fn stream_run( &self, path: StreamRunPath, ) -> impl Future<Output = Result<EventStream, Problem>> + Send
Live output from a run, as server-sent events.
GET /runs/{run_id}/stream
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".