pub struct Engine<T: Transport, C: Clock> {
pub settings: Settings,
pub notes: Vec<Note>,
pub host: Host,
pub env: EnvSnapshot,
pub probe: Arc<dyn ProcessProbe>,
transport: T,
clock: C,
openapi: Mutex<OpenapiCache>,
diff_cache: Mutex<LruCache<DiffKey, (Value, i64)>>,
}Fields§
§settings: Settings§notes: Vec<Note>§host: Host§env: EnvSnapshot§probe: Arc<dyn ProcessProbe>§transport: T§clock: C§openapi: Mutex<OpenapiCache>§diff_cache: Mutex<LruCache<DiffKey, (Value, i64)>>Implementations§
Source§impl<T: Transport, C: Clock> Engine<T, C>
impl<T: Transport, C: Clock> Engine<T, C>
pub fn new( settings: Settings, notes: Vec<Note>, host: Host, env: EnvSnapshot, transport: T, clock: C, probe: Arc<dyn ProcessProbe>, ) -> Self
Sourcepub fn diff_cache_get(&self, key: &DiffKey) -> Option<Value>
pub fn diff_cache_get(&self, key: &DiffKey) -> Option<Value>
A cached diff, promoted to most recently used; an entry older than the TTL is dropped (checked on every lookup).
Sourcepub fn diff_cache_put(&self, key: DiffKey, value: &Value)
pub fn diff_cache_put(&self, key: DiffKey, value: &Value)
Cache value unless its serialised size exceeds 32 MiB.
pub fn clock(&self) -> &C
Sourcepub async fn operation_table(
&self,
) -> (OperationTable, TableSource, Option<String>)
pub async fn operation_table( &self, ) -> (OperationTable, TableSource, Option<String>)
The live table (fetched once) or, when the fetch fails, the bundled snapshot with a warning.
Sourcepub async fn invalidate_if_changed(&self, info: &Value)
pub async fn invalidate_if_changed(&self, info: &Value)
Drop the live table when info (a server.info result) shows another pid or version, or
when the service has just recovered from SERVICE_DOWN.
Sourcepub async fn observed_server_version(&self) -> Option<String>
pub async fn observed_server_version(&self) -> Option<String>
The server.info version last observed by invalidate_if_changed.
pub async fn op(&self, id: &str) -> Result<Operation, ToolError>
pub async fn cli_version(&self) -> Result<String, ToolError>
pub async fn lifecycle(&self, verb: Lifecycle) -> Result<String, ToolError>
pub async fn wait_idle( &self, session_id: &str, deadline: Duration, ) -> Result<WaitOutcome, ToolError>
Sourcepub async fn call_json(
&self,
id: &str,
params: Params,
body: Option<Value>,
) -> Result<(Value, Option<Value>, Option<Value>), ToolError>
pub async fn call_json( &self, id: &str, params: Params, body: Option<Value>, ) -> Result<(Value, Option<Value>, Option<Value>), ToolError>
One operation call: (data, cursor, meta) with the envelope unwrapped and secrets redacted.
Sourcepub fn resolve_session(&self, arg: Option<&str>) -> Result<String, ToolError>
pub fn resolve_session(&self, arg: Option<&str>) -> Result<String, ToolError>
The tool argument, else the paired session; either must look like a session id.
Sourcepub fn created_set(&self) -> CreatedSet
pub fn created_set(&self) -> CreatedSet
The created-session set kept in the ctl home.
Sourcepub fn guard_created(&self, id: &str) -> Result<(), ToolError>
pub fn guard_created(&self, id: &str) -> Result<(), ToolError>
On Host::Opencode a session-scoped tool accepts only a session this controller created.
The set is re-read from disk on every call, so a refusal never rests on stale memory.
Sourcepub async fn record_created(
&self,
directory: &Path,
id: &str,
) -> Result<(), ToolError>
pub async fn record_created( &self, directory: &Path, id: &str, ) -> Result<(), ToolError>
Append a new session to the set, then probe at most five OTHER ids of that file and prune
those the service reports as SESSION_NOT_FOUND (any other error leaves the entry).
pub fn resolve_directory(&self, arg: Option<&Path>) -> PathBuf
pub fn finish(&self, value: Value) -> Value
Sourcepub fn render(&self, result: ToolResult) -> CallToolResult
pub fn render(&self, result: ToolResult) -> CallToolResult
Every tool result passes here: redaction, then the byte cap; errors keep the closed shape.
Source§impl<T: Transport, C: Clock> Engine<T, C>
impl<T: Transport, C: Clock> Engine<T, C>
async fn first_user_message( &self, id: &str, order: &str, ) -> Result<Option<String>, ToolError>
async fn session_directory(&self, id: &str) -> Result<Value, ToolError>
pub async fn session_changes(&self, a: SessionChangesArgs) -> ToolResult
pub async fn workspace_status(&self, a: WorkspaceStatusArgs) -> ToolResult
Source§impl<T: Transport, C: Clock> Engine<T, C>
impl<T: Transport, C: Clock> Engine<T, C>
Sourceasync fn hatch_warning(
&self,
source: TableSource,
table_warning: Option<String>,
) -> Option<String>
async fn hatch_warning( &self, source: TableSource, table_warning: Option<String>, ) -> Option<String>
The warning for a snapshot in use against a server of another version, else the table’s own note.
pub async fn opencode_operations(&self, a: OpencodeOperationsArgs) -> ToolResult
pub async fn opencode_api(&self, a: OpencodeApiArgs) -> ToolResult
Source§impl<T: Transport, C: Clock> Engine<T, C>
impl<T: Transport, C: Clock> Engine<T, C>
pub async fn session_prompt(&self, a: SessionPromptArgs) -> ToolResult
pub async fn session_messages(&self, a: SessionMessagesArgs) -> ToolResult
Source§impl<T: Transport, C: Clock> Engine<T, C>
impl<T: Transport, C: Clock> Engine<T, C>
Sourcepub fn pair_liveness(&self) -> (Liveness, Liveness)
pub fn pair_liveness(&self) -> (Liveness, Liveness)
(tui, claude) re-read from disk: none for both without a pair id, unknown for both when
the record is missing or unreadable.
pub async fn pair_status(&self, _a: PairStatusArgs) -> ToolResult
Source§impl<T: Transport, C: Clock> Engine<T, C>
impl<T: Transport, C: Clock> Engine<T, C>
pub async fn permission_reply(&self, a: PermissionReplyArgs) -> ToolResult
pub async fn form_reply(&self, a: FormReplyArgs) -> ToolResult
pub async fn saved_permissions(&self, a: SavedPermissionsArgs) -> ToolResult
Source§impl<T: Transport, C: Clock> Engine<T, C>
impl<T: Transport, C: Clock> Engine<T, C>
fn refuse_lifecycle(&self, tool: &str) -> Result<(), ToolError>
fn confirm_required(&self, tool: &str, confirm: bool) -> Result<(), ToolError>
pub async fn service_status(&self, _a: ServiceStatusArgs) -> ToolResult
Sourceasync fn status_after_lifecycle(&self) -> ToolResult
async fn status_after_lifecycle(&self) -> ToolResult
The status after a start or restart that succeeded. The read right after it can still say stopped
(MEASURED on Windows, Task 25: service_restart answered running: false and a read 4 s later showed
the new pid), so read again every SETTLE_POLL for up to SETTLE_MS and answer what the last read
saw. No server.info runs while it reads stopped, so a dead service is never started to answer.
pub async fn service_start(&self, a: ServiceStartArgs) -> ToolResult
pub async fn service_stop(&self, a: ConfirmArgs) -> ToolResult
pub async fn service_restart(&self, a: ConfirmArgs) -> ToolResult
Source§impl<T: Transport, C: Clock> Engine<T, C>
impl<T: Transport, C: Clock> Engine<T, C>
pub async fn session_list(&self, a: SessionListArgs) -> ToolResult
pub async fn session_create(&self, a: SessionCreateArgs) -> ToolResult
pub async fn session_get(&self, a: SessionGetArgs) -> ToolResult
pub async fn session_interrupt(&self, a: SessionInterruptArgs) -> ToolResult
pub async fn session_delete(&self, a: SessionDeleteArgs) -> ToolResult
Source§impl<T: Transport, C: Clock> Engine<T, C>
impl<T: Transport, C: Clock> Engine<T, C>
async fn list_op( &self, op: &str, session: &str, ) -> Result<(Value, Option<Value>, Option<Value>), ToolError>
Sourceasync fn read_pages(
&self,
session: &str,
stop: Stop<'_>,
state: &mut PageState,
cut_at: i64,
) -> Result<(Vec<Message>, bool, bool), ToolError>
async fn read_pages( &self, session: &str, stop: Stop<'_>, state: &mut PageState, cut_at: i64, ) -> Result<(Vec<Message>, bool, bool), ToolError>
The first read of a slice: order=desc limit=200, following cursor.next until stop hits or
the list ends or the clock reaches cut_at. Returns the messages OLDEST FIRST, whether stop hit and
whether the deadline cut the search.
Sourcepub async fn read_messages_since(
&self,
session: &str,
anchor: &Anchor,
state: &mut PageState,
) -> Result<Vec<Message>, ToolError>
pub async fn read_messages_since( &self, session: &str, anchor: &Anchor, state: &mut PageState, ) -> Result<Vec<Message>, ToolError>
Messages not read yet, OLDEST FIRST. The first read of a slice (state empty) pages back to the
anchor; later reads are one native “newer than” read from cursor.previous of the newest page seen.
Sourceasync fn read_incremental(
&self,
session: &str,
anchor: &Anchor,
state: &mut PageState,
out: &mut Vec<Message>,
) -> Result<(), ToolError>
async fn read_incremental( &self, session: &str, anchor: &Anchor, state: &mut PageState, out: &mut Vec<Message>, ) -> Result<(), ToolError>
read_messages_since appending into out page by page, so a read dropped mid-way (the poll ended the
slice) never loses a page the cursor has already moved past. A message already in out is not added twice.
Sourceasync fn locate(
&self,
session: &str,
after: Option<&str>,
call_start: i64,
cut_at: i64,
state: &mut PageState,
) -> Result<Option<Located>, ToolError>
async fn locate( &self, session: &str, after: Option<&str>, call_start: i64, cut_at: i64, state: &mut PageState, ) -> Result<Option<Located>, ToolError>
The anchor of this slice, or None when the session has no user message and no after was given.
cut_at is the slice deadline: a first read still paging at that time stops and sets cut.
Sourcepub async fn resolve_anchor(
&self,
session: &str,
after: Option<&str>,
) -> Result<Anchor, ToolError>
pub async fn resolve_anchor( &self, session: &str, after: Option<&str>, ) -> Result<Anchor, ToolError>
Explicit after never replaced; else the newest user message. A session with no user message and
no after has no anchor: session_wait answers idle before this point, so here it is INVALID_REQUEST.
Delegates to locate, the one anchor path session_wait itself uses.
Sourceasync fn is_active(&self, session: &str) -> Result<bool, ToolError>
async fn is_active(&self, session: &str) -> Result<bool, ToolError>
Whether the session is a key of session.active’s data map. The route lists every active session and
declares no parameter, so no sessionID is sent.
async fn tokens_of(&self, session: &str) -> Option<Value>
Sourceasync fn poll_once(&self, session: &str) -> Result<Lists, ToolError>
async fn poll_once(&self, session: &str) -> Result<Lists, ToolError>
One poll tick: both stall channels. Either failing is a failed tick.
Sourceasync fn poll_loop(
&self,
session: &str,
started: i64,
timeout_ms: i64,
poll_ms: i64,
) -> PollEnd
async fn poll_loop( &self, session: &str, started: i64, timeout_ms: i64, poll_ms: i64, ) -> PollEnd
Task (b): ticks at the slice start, at every multiple of poll_ms strictly before the deadline, and at
the deadline. A failed tick is retried at the next one; two in a row end the slice with the last error.
Sourceasync fn child_loop(
&self,
session: &str,
anchor: &Anchor,
deadline_ms: i64,
poll_ms: i64,
cs: &mut ChildState,
) -> Result<ChildDone, ToolError>
async fn child_loop( &self, session: &str, anchor: &Anchor, deadline_ms: i64, poll_ms: i64, cs: &mut ChildState, ) -> Result<ChildDone, ToolError>
Task (a): the wait child, restarted until a return whose evaluation is not Running. Each return costs the
inbox, session.active and one incremental message read besides the child itself.