pub struct AppState { /* private fields */ }Expand description
The shared, cheaply cloned handle every route works through.
Implementations§
Source§impl AppState
impl AppState
Sourcepub fn new(store: Arc<dyn EventStore>, factory: AgentFactory) -> Self
pub fn new(store: Arc<dyn EventStore>, factory: AgentFactory) -> Self
Builds server state over store, using factory to turn submitted
definitions into live agents. Auth is off and the clock and random
source are the runtime defaults until set with the with_* methods.
Sourcepub fn with_client_lease_ttl(self, ttl: Duration) -> Self
pub fn with_client_lease_ttl(self, ttl: Duration) -> Self
Sets how long a client-driven run’s lease stays current without the
driver presenting its token again (default 60s). Past this, the run
reports no attached driver and a re-open may take it from the driver
that has gone quiet. Additive and off-default; a test or a seed
shortens it to make a driverless client run observable quickly, exactly
as with_poll_interval shortens the stream
poll. salvor serve reads it from SALVOR_CLIENT_LEASE_TTL_SECS.
Sourcepub fn with_auth_token(self, token: impl Into<String>) -> Self
pub fn with_auth_token(self, token: impl Into<String>) -> Self
Requires Authorization: Bearer <token> on every request. Without this,
the server trusts its caller (the reverse-proxy posture).
Sourcepub fn with_model_executor(self, executor: Arc<dyn ModelExecutor>) -> Self
pub fn with_model_executor(self, executor: Arc<dyn ModelExecutor>) -> Self
Injects the general model executor the server performs a client-driven
run’s model step through. Additive and off by default (the existing
new leaves it unset), so no caller that predates it
changes behavior. salvor serve wires a default here; another host
injects its own, exactly as it supplies its own AgentFactory.
Sourcepub fn with_tool_registry(self, registry: Arc<ToolRegistry>) -> Self
pub fn with_tool_registry(self, registry: Arc<ToolRegistry>) -> Self
Injects the general tool registry the server performs a client-driven
run’s tool step through. Additive and off by default (the existing
new leaves it unset), so no caller that predates it
changes behavior. salvor serve wires an empty registry here; another
host injects one holding its own tools, exactly as it supplies its own
AgentFactory and ModelExecutor.
Sourcepub fn with_client_tools(self, decls: Arc<ClientToolRegistry>) -> Self
pub fn with_client_tools(self, decls: Arc<ClientToolRegistry>) -> Self
Loads the client-performed tool declarations this server answers
client-tool intents against. Additive and empty by default, so no caller
that predates it changes behavior: without a declaration, every
client-tool intent is a clean unknown_tool and nothing is written.
This is the ONLY way declarations enter the process. There is no
endpoint that accepts one, on purpose: a declaration fixes the effect
class, and a client that could declare its own would be choosing whether
its own write is subject to the write-ahead rule. See
crate::client_tools for the full argument.
Sourcepub fn with_hooks(self, clock: ClockFn, random: RandomFn) -> Self
pub fn with_hooks(self, clock: ClockFn, random: RandomFn) -> Self
Injects the clock and random source every Runtime this state builds
uses. Deterministic tests pass fixed functions so full logs compare
equal across a control run and a recovered one.
Sourcepub fn with_poll_interval(self, interval: Duration) -> Self
pub fn with_poll_interval(self, interval: Duration) -> Self
Sets how often the event stream polls the store for new events (default 50ms). Tests shorten it so a streamed run completes quickly.
Sourcepub fn with_wake_interval(self, interval: Duration) -> Self
pub fn with_wake_interval(self, interval: Duration) -> Self
Sets how often the wake sweeper looks for runs whose durable timer has
come due (default DEFAULT_WAKE_INTERVAL). Duration::ZERO turns the
sweeper off entirely, for a host that wakes runs some other way.
Same shape as with_poll_interval, and a
test shortens it for the same reason: to make a sweep observable without
waiting on a wall clock.
Sourcepub fn store(&self) -> Arc<dyn EventStore> ⓘ
pub fn store(&self) -> Arc<dyn EventStore> ⓘ
The event store every request reads from and writes through.
Sourcepub fn auth_token(&self) -> Option<&str>
pub fn auth_token(&self) -> Option<&str>
The expected bearer token, when auth is required.
Sourcepub fn poll_interval(&self) -> Duration
pub fn poll_interval(&self) -> Duration
How often the event stream polls for new events.
Sourcepub fn wake_interval(&self) -> Duration
pub fn wake_interval(&self) -> Duration
How often the wake sweeper looks for due timers. Duration::ZERO means
no sweeper runs on this server.
Sourcepub fn model_executor(&self) -> Option<Arc<dyn ModelExecutor>>
pub fn model_executor(&self) -> Option<Arc<dyn ModelExecutor>>
The injected model executor, if a host wired one. None means the
server cannot perform a model step and the endpoint says so.
Sourcepub fn tool_registry(&self) -> Option<Arc<ToolRegistry>>
pub fn tool_registry(&self) -> Option<Arc<ToolRegistry>>
The injected tool registry, if a host wired one. None means the server
cannot perform a tool step and the endpoint says so; a wired-but-empty
registry instead reports each tool as unknown_tool.
Sourcepub fn client_tools(&self) -> Arc<ClientToolRegistry> ⓘ
pub fn client_tools(&self) -> Arc<ClientToolRegistry> ⓘ
The client-performed tool declarations the operator loaded. Empty unless
with_client_tools was called, and an empty
set answers every client-tool intent with unknown_tool.
Sourcepub fn now(&self) -> OffsetDateTime
pub fn now(&self) -> OffsetDateTime
Reads the current instant from this state’s injected clock, or the real
UTC clock when none was injected. This stamps envelopes the server
records itself (the model-step intent and completion), the same clock
edge a Runtime would use, so deterministic tests still compare logs.
Sourcepub fn runtime(&self) -> Runtime
pub fn runtime(&self) -> Runtime
A fresh runtime over the shared store, with this state’s clock and random source.
Sourcepub async fn build_agent(
&self,
definition: AgentDefinition,
) -> Result<BuiltAgent, String>
pub async fn build_agent( &self, definition: AgentDefinition, ) -> Result<BuiltAgent, String>
Builds a live agent from a submitted definition, through the factory.
§Errors
The factory’s human message when the definition will not build.
Sourcepub fn register_agent(&self, registered: RegisteredAgent) -> String
pub fn register_agent(&self, registered: RegisteredAgent) -> String
Records a registered agent under its content hash, returning that hash.
Sourcepub fn agent(&self, hash: &str) -> Option<RegisteredAgent>
pub fn agent(&self, hash: &str) -> Option<RegisteredAgent>
The definition registered under hash, if any.
Sourcepub fn agent_hashes(&self) -> Vec<String>
pub fn agent_hashes(&self) -> Vec<String>
Every registered agent’s hash, sorted for a stable listing.
Sourcepub fn store_graph(&self, hash: String, graph: Graph) -> bool
pub fn store_graph(&self, hash: String, graph: Graph) -> bool
Records a validated graph document under hash, returning whether it was
newly stored (true) or already present (false). Re-storing the
identical document is idempotent: the second call keeps the first and
reports false, the graph counterpart of an agent register’s created.
Sourcepub fn graph(&self, hash: &str) -> Option<Graph>
pub fn graph(&self, hash: &str) -> Option<Graph>
The graph document stored under hash, if any. None is the
unknown_graph case.
Sourcepub fn graph_hashes(&self) -> Vec<String>
pub fn graph_hashes(&self) -> Vec<String>
Every stored graph’s hash, sorted for a stable listing.
Sourcepub fn run_ctx(
&self,
run_id: RunId,
log: Vec<EventEnvelope>,
) -> Result<RunCtx, RuntimeError>
pub fn run_ctx( &self, run_id: RunId, log: Vec<EventEnvelope>, ) -> Result<RunCtx, RuntimeError>
Builds a per-run RunCtx over log, with this state’s clock and random
source, so the graph engine can drive a run through the same durability
substrate the built-in loop uses. This is the graph counterpart of
runtime: the built-in loop reaches the store through a
Runtime; the graph engine reaches it through a RunCtx it drives
directly, and both share the exact clock/random hooks so a deterministic
test’s logs still compare equal.
§Errors
RuntimeError::Replay when log is not a well-formed run history.
Sourcepub fn begin_run(&self, run_id: RunId)
pub fn begin_run(&self, run_id: RunId)
Marks a run as being driven. Call this synchronously before spawning the driver task, so a stream opened at the same instant sees the run as active rather than racing the task’s first store write.
Sourcepub fn set_handle(&self, run_id: RunId, handle: JoinHandle<()>)
pub fn set_handle(&self, run_id: RunId, handle: JoinHandle<()>)
Records the driver task’s handle, for aborting at shutdown.
Sourcepub fn end_run(&self, run_id: RunId)
pub fn end_run(&self, run_id: RunId)
Marks a run’s drive as ended and drops its handle. The task calls this as its last act, whether it completed, parked, or errored.
Sourcepub fn is_run_active(&self, run_id: RunId) -> bool
pub fn is_run_active(&self, run_id: RunId) -> bool
Whether a run is still being driven by a task in this process.
Sourcepub fn lease_client_run(&self, run_id: RunId, record_prompts: bool) -> String
pub fn lease_client_run(&self, run_id: RunId, record_prompts: bool) -> String
Records (or re-leases) a client-driven run, returning a fresh drive
token. Called by the open endpoint for a new run, and for a re-open the
open endpoint has already decided is allowed: no lease stands, or the
one that does has lapsed, or the run is finished. Minting here is
unconditional, so it must never be reached while another driver’s lease
is current; that is the whole point of the check in
client_runs::open, which reads
current_client_lease first.
Sourcepub fn touch_client_run(&self, run_id: RunId)
pub fn touch_client_run(&self, run_id: RunId)
Refreshes a client-driven run’s last_seen to now, the driver’s proof of
life. Called by the lease gate on every guarded operation (the driver
presented its token, so it is alive); a no-op for a run this process holds
no lease for.
Sourcepub fn current_client_lease(
&self,
run_id: RunId,
) -> Option<(ClientRunLease, Duration)>
pub fn current_client_lease( &self, run_id: RunId, ) -> Option<(ClientRunLease, Duration)>
A client-driven run’s lease and how long is left before it lapses, when
this process holds one whose driver has proved it is alive within the
lease TTL. None covers both “no lease here” (a fresh process, a run
this server never opened) and “the lease lapsed” (the tab closed, the
SDK exited, the driver crashed), because to everything downstream those
are the same fact: nobody is driving this run.
This is the one place the TTL comparison happens, so the liveness field
on GET /v1/runs and the re-open refusal in
client_runs::open can never disagree about
whether a driver is still attached. The remaining time comes back with
the lease because the refusal has to tell the second caller when to try
again, and computing it twice would invite the two answers to drift.
Sourcepub fn client_run_driver_live(&self, run_id: RunId) -> bool
pub fn client_run_driver_live(&self, run_id: RunId) -> bool
Whether a live driver is currently attached to a client-driven run: this
process holds a lease for it AND the driver presented its token within the
lease TTL. A lapsed lease (the tab closed, the SDK exited) reports false:
the client-driven half of the liveness evidence GET /v1/runs carries.
Sourcepub fn client_run(&self, run_id: RunId) -> Option<ClientRunLease>
pub fn client_run(&self, run_id: RunId) -> Option<ClientRunLease>
The lease for a client-driven run, if this process opened one under
run_id, whether or not its driver has been heard from lately. The
drive-token gate wants exactly this: a driver that went quiet for longer
than the TTL and then presents its token again is still the run’s writer,
as long as nobody took the run away from it in the meantime. Ask
current_client_lease instead when the
question is whether someone is driving right now.
Sourcepub fn client_lease_ttl(&self) -> Duration
pub fn client_lease_ttl(&self) -> Duration
How long a client-driven run’s lease stays current without the driver presenting its token again, the TTL every freshness judgment here is made against. Read by the heartbeat endpoint, which answers with it so a driver about to be busy for a while knows how often it has to beat.
Sourcepub fn release_client_run(
&self,
run_id: RunId,
presented: Option<&str>,
) -> LeaseRelease
pub fn release_client_run( &self, run_id: RunId, presented: Option<&str>, ) -> LeaseRelease
Hands a client-driven run’s lease back when presented is the token
holding it, so the next open takes the run immediately instead of
waiting out the TTL.
The token check and the removal happen under one lock, so a release can only ever drop the lease the caller was actually holding, never one minted in between by a driver that took the run over.
Only the lease goes. Nothing about the run itself changes, its recorded
driven_by: client included, so a later open adopts it back exactly as
it would after a restart.
Sourcepub fn clear_client_lease(&self, run_id: RunId, keep: Option<&str>) -> bool
pub fn clear_client_lease(&self, run_id: RunId, keep: Option<&str>) -> bool
Drops a client-driven run’s lease unless keep is the token that holds
it, reporting whether a lease went. What a resolve calls: recording a
dangling write by hand says the driver that opened that write never came
back, so the lease it left behind is holding the run for nobody and the
next open should not have to wait out the TTL for it.
keep is how the client-driven resolve keeps its own lease. That caller
presented its current token to get in, which is the driver saying it is
right here, so the lease is not a dead one and taking it away would
strand a driver mid-run. Every other resolve passes None, because no
token was presented and nothing says a driver is still attached.
Sourcepub fn is_client_run(&self, run_id: RunId) -> bool
pub fn is_client_run(&self, run_id: RunId) -> bool
Whether run_id names a client-driven run this process opened.
Sourcepub fn mark_unwakeable_warned(&self, run_id: RunId) -> bool
pub fn mark_unwakeable_warned(&self, run_id: RunId) -> bool
Records that the wake sweeper has warned about this run being
unwakeable here. Returns true the first time (the caller logs at
WARN) and false on every later call for the same run while the
record stands (the caller logs the same fields at DEBUG instead).
Sourcepub fn unwakeable_warned(&self, run_id: RunId) -> bool
pub fn unwakeable_warned(&self, run_id: RunId) -> bool
Whether the sweeper has already warned about this run. Read-only,
unlike mark_unwakeable_warned, which
always records a sighting; a test uses this to check the record
without flipping it.
Sourcepub fn clear_unwakeable_warned(&self, run_id: RunId)
pub fn clear_unwakeable_warned(&self, run_id: RunId)
Clears a run’s unwakeable-warned record: it woke, so the next time it naps and cannot be rebuilt here is a fresh first sighting.
Sourcepub fn prune_unwakeable_warned(&self, still_due: &HashSet<RunId>)
pub fn prune_unwakeable_warned(&self, still_due: &HashSet<RunId>)
Drops every unwakeable-warned record for a run not in still_due.
Called once per sweep pass before processing, so a run that leaves the
due set some other way than being driven (the only other way its
record could go stale) does not carry a warning into a future nap that
has nothing to do with this one.