pub struct ViewSurface {
workspace: Arc<Workspace>,
usage_cache: Mutex<Option<(Instant, UsageReport)>>,
pricing_refill: AtomicBool,
usage_scan: Mutex<()>,
usage_scans: AtomicU64,
usage_gate: Mutex<Option<Arc<Barrier>>>,
}Expand description
A view’s read handle on the core.
Fields§
§workspace: Arc<Workspace>§usage_cache: Mutex<Option<(Instant, UsageReport)>>The pool’s last scan and when it was taken, so a reader asking twice
inside the window does not walk it twice. A Mutex rather than a
second cache type: one report, one window.
pricing_refill: AtomicBoolWhether a pricing refresh is already out. The fetch crosses the network, so the ask that finds one running is not a reason for a second.
usage_scan: Mutex<()>The one scan a cold cache runs: a second reader waits here rather than walking the pool beside the first.
usage_scans: AtomicU64How many scans were started, so a test can prove two misses ran one.
usage_gate: Mutex<Option<Arc<Barrier>>>Holds every miss at one point, so the race the guard covers is a barrier rather than a large pool that might finish first.
Implementations§
Source§impl ViewSurface
impl ViewSurface
Sourcepub fn accounts(&self) -> AccountPoolView
pub fn accounts(&self) -> AccountPoolView
The pool as the home snapshot and AccountsChanged both state it.
Delegated rather than rebuilt. The frame’s payload and the snapshot’s field must be one builder’s answer, or a client folding the frame and a client reading the snapshot can disagree (rule 25).
Source§impl ViewSurface
impl ViewSurface
Sourcepub fn failed_turn(&self, slot: &SessionSlot) -> Option<SystemTime>
pub fn failed_turn(&self, slot: &SessionSlot) -> Option<SystemTime>
When slot’s newest turn ended in failure, or None when nothing
failed or a newer turn has since been committed.
The same fact a row carries, read for one seat: showing the seat spends that mark, and whether there was one to spend decides whether the home needs re-encoding for the connection that attached.
Sourcepub fn seat_shown(&self, slot: &SessionSlot) -> bool
pub fn seat_shown(&self, slot: &SessionSlot) -> bool
A client has slot on screen, which is this view saying the seat was
shown: a death the reader had not been shown is spent with it - the
mark and its reason leave the row - so a crash nobody watched is the
durable trace until someone looks. Answers whether anything was
spent, which is the connection’s cue to re-encode the home it holds.
Called when the connection’s showing and its subscription meet,
from whichever half lands second. Both clients subscribe first on a
resume, but the route effect that sends Showing races the page’s
mount, so a showing alone is remembered rather than spent until the
seat is actually watched.
Called after the seat’s own snapshot was encoded, deliberately: the read that spends the mark is the read that shows it.
Source§impl ViewSurface
impl ViewSurface
Sourcepub fn biometric_enrolments(&self) -> Result<Vec<BiometricEnrolment>, String>
pub fn biometric_enrolments(&self) -> Result<Vec<BiometricEnrolment>, String>
Every device’s enrolment, with no wrapped blob in it: a view draws which purposes a device enrolled and when each was last released, and the bytes stay behind a release.
A store that cannot be read is an error rather than an empty list, so the page can tell “no device is enrolled” from “the list could not be read”.
Source§impl ViewSurface
impl ViewSurface
Source§impl ViewSurface
impl ViewSurface
Sourcepub fn slash_commands(&self, slot: &SessionSlot) -> Vec<AvailableCommand>
pub fn slash_commands(&self, slot: &SessionSlot) -> Vec<AvailableCommand>
The slash commands the CLI last advertised for slot, named as
they are typed. Empty for a seat whose session has not connected,
and for one whose CLI advertised none.
The core retains them from the init frame each turn and from
commands_changed, so a view that arrived after the turn started
reads them here rather than waiting a whole turn for the next
frame.
The wire carries the names bare, so the leading slash is added
here: a caller renders name as it comes back.
Forge’s own commands are not in this list. A view rendering the /
dropdown reads those from Self::forge_commands, and a name in
both is drawn from the catalogue rather than the CLI’s copy.
Sourcepub fn forge_commands() -> &'static [ForgeCommand]
pub fn forge_commands() -> &'static [ForgeCommand]
forge’s own commands, which a view offers beside the ones the CLI
advertises: a / dropdown is built from this table and
Self::slash_commands together, so the two views cannot show
different lists.
A name here shadows the CLI’s row for the same name
(crate::commands::is_forge_command), so one row is drawn, with the
catalogue’s words.
Sourcepub fn subagents(&self, slot: &SessionSlot) -> Vec<AvailableAgent>
pub fn subagents(&self, slot: &SessionSlot) -> Vec<AvailableAgent>
The subagents the CLI last advertised for slot, retained by the
same frames and empty in the same two cases as
Self::slash_commands.
Sourcepub async fn resumable_sessions(
&self,
slot: &SessionSlot,
) -> Vec<SDKSessionInfo>
pub async fn resumable_sessions( &self, slot: &SessionSlot, ) -> Vec<SDKSessionInfo>
The sessions slot can jump back to with /resume, newest first,
which is what the seat’s own claude --resume can find from the
directory it runs in. See forge_workspace::Workspace::resumable_sessions
for the set a lead and a worker each get.
Sourcepub fn subagent_cards(&self, slot: &SessionSlot) -> Vec<SubagentCard>
pub fn subagent_cards(&self, slot: &SessionSlot) -> Vec<SubagentCard>
The sub-agent instances the session at slot has folded, as the list
it pushes when the list moves.
A record that folded the conversation’s messages itself would answer with whatever window of them it holds, which is not the conversation the cards describe.
Sourcepub fn file_index(&self, slot: &SessionSlot) -> Option<Arc<FileIndex>>
pub fn file_index(&self, slot: &SessionSlot) -> Option<Arc<FileIndex>>
The seat’s file index, as the seat’s own loop last walked it, or
None until the first walk lands.
Held rather than walked here. The walk is a whole tree, and the
seat’s loop is the one that takes it - throttled, and announced as
FileIndexChanged when it moves - so this answers the store the loop
writes rather than paying for a walk of its own.
Sourcepub async fn file_index_for_record(&self, slot: &SessionSlot) -> Arc<FileIndex> ⓘ
pub async fn file_index_for_record(&self, slot: &SessionSlot) -> Arc<FileIndex> ⓘ
The record’s file index for slot: the store, or the walk the seat’s
loop would take, under the same gate - so the snapshot’s fallback and
the first pass are one walk rather than two of the same tree.
Sourcepub fn walk_file_index(&self, root: &Path) -> FileIndex
pub fn walk_file_index(&self, root: &Path) -> FileIndex
One working tree’s files, walked on demand. root is the session’s
own scan cwd, which a git worker’s worktree overrides.
A page’s own walk. A page that holds no seat - nothing subscribes on its behalf, so no seat’s loop runs for it - has no store to read, and walks where it stands.
Sourcepub fn respect_gitignore(&self) -> bool
pub fn respect_gitignore(&self) -> bool
The user’s own respectGitignore, the preference
Self::walk_file_index runs with. Read on each call from the CLI’s
per-user preferences document, so a flip reaches the next walk.
Source§impl ViewSurface
impl ViewSurface
Sourcepub fn connectors(&self, project: Option<&str>) -> ConnectorsView
pub fn connectors(&self, project: Option<&str>) -> ConnectorsView
project scopes the subscription lists to that project’s own.
None reports only the connection status, the way an unstamped
session reads.
Source§impl ViewSurface
impl ViewSurface
Sourcepub fn dictate_close(&self, key: &SessionSlot, initiator: u64) -> bool
pub fn dictate_close(&self, key: &SessionSlot, initiator: u64) -> bool
The connection that was streaming a take for key has gone: drop
the take it started and free the seat for the next one.
A connection’s own lifecycle rather than a user action, which is why it is a verb here and not a command: nothing a view dispatches means “the client went away”.
Sourcepub fn dictate_axes(&self) -> DictateAxes
pub fn dictate_axes(&self) -> DictateAxes
The axes a client that captures starts on and resets to: the
[dictate] keys over the crate’s own defaults.
Sourcepub fn dictate_push(
&self,
key: &SessionSlot,
samples: &[f32],
initiator: Option<u64>,
) -> bool
pub fn dictate_push( &self, key: &SessionSlot, samples: &[f32], initiator: Option<u64>, ) -> bool
Push one frame of client-captured audio into a seat’s live take,
answering whether the samples were kept. The take must belong to
initiator: a seat’s live take can be another connection’s, and its
audio is not the pushing connection’s to feed.
A data plane rather than a command: a client sends this ~50 times a second for as long as it talks, and the command bus’s dispatch is for the actions a view takes on the user’s behalf, not for the audio itself.
Sourcepub fn dictate_read_aloud_push(
&self,
samples: &[f32],
initiator: Option<u64>,
) -> bool
pub fn dictate_read_aloud_push( &self, samples: &[f32], initiator: Option<u64>, ) -> bool
Self::dictate_push for the read-aloud recording, which belongs to
no seat: the same data plane, kept only where the recording is the
pushing connection’s.
pub fn dictate(&self) -> DictateView
Sourcepub fn dictate_models(&self) -> DictateModelsSnapshot
pub fn dictate_models(&self) -> DictateModelsSnapshot
The models page’s whole read: the pinned models, the catalogue check, what the feed proposes, and every row it carries.
Read when the page subscribes and again from each
forge_workspace::SessionUpdate::DictateModelsChanged; the
check it reports on is started by the core at boot and by
Command::DictateCatalogueCheck.
Sourcepub fn dictate_bind(&self) -> DictateBind
pub fn dictate_bind(&self) -> DictateBind
The [dictate] bind key, as forge.toml declares it. A client
draws the keyboard the record carries rather than hardcoding one.
Sourcepub fn dictate_mode(&self) -> DictateMode
pub fn dictate_mode(&self) -> DictateMode
How a press maps onto a take, as forge.toml declares it.
Source§impl ViewSurface
impl ViewSurface
pub async fn extensions(&self) -> ExtensionsView
Source§impl ViewSurface
impl ViewSurface
Sourcepub fn header(&self, slot: &SessionSlot) -> SessionHeader
pub fn header(&self, slot: &SessionSlot) -> SessionHeader
The session’s header facts: model, effort, permission mode and context usage.
One call rather than four, because a header renders them together and each read takes the session’s lock.
Sourcepub fn mcp_servers(&self, slot: &SessionSlot) -> Option<McpServers>
pub fn mcp_servers(&self, slot: &SessionSlot) -> Option<McpServers>
The MCP servers the session’s bridge last reported, and the failure standing beside them when the read did not complete.
MCP is configured per session rather than per account, so this is
the session’s own set. None until the first snapshot lands, as
opposed to a snapshot that carries none.
Sourcepub fn processes(&self, slot: &SessionSlot) -> Option<ProcessSnapshot>
pub fn processes(&self, slot: &SessionSlot) -> Option<ProcessSnapshot>
The last OS walk of the session’s process tree.
The walk is driven by whoever owns the tick that performs it and
the core holds its answer, so this neither performs one nor waits
for the next: a slot nothing has walked reads as None.
The snapshot carries the instant it was taken, and a view draws it: a walk is only ever taken for the session a view is looking at, so the answer here can be arbitrarily old and must not be drawn as though it were taken now.
Sourcepub async fn work(
&self,
slot: &SessionSlot,
cwd: &Path,
review: bool,
) -> WorkSnapshot
pub async fn work( &self, slot: &SessionSlot, cwd: &Path, review: bool, ) -> WorkSnapshot
The seat’s working tree, read on the loop’s own rule: the stored scan while it is inside the snapshot window, a new one past it.
The record’s read is the seat’s first read, and the loop’s too. A held seat’s snapshot takes the tree under the same gate the watch’s first pass uses, so the two are one scan: whichever gets there first stores, and the other answers that store rather than reading the tree again. A seat nobody holds has no loop to race and reads unguarded.
The window is what keeps the record self-consistent. The record carries the scan’s hunks beside its branch and count; serving an arbitrarily old scan would put its hunks beside a row the first pass has since moved, so a store past the window is re-read here rather than carried.
Sourcepub async fn refresh_diff_base(
&self,
slot: &SessionSlot,
held: &WorkSnapshot,
) -> Option<BaseRefresh>
pub async fn refresh_diff_base( &self, slot: &SessionSlot, held: &WorkSnapshot, ) -> Option<BaseRefresh>
Answer the base the seat’s drawn branch diff is measured against, and keep it fresh off the read.
None when no branch layer is drawn: the default branch, a detached
HEAD, or no resolved default. Only the read that draws the diff calls
this, so the check it reports is the one that read drew.
A seat with no session waits for its own check. No watch runs for one, so a kicked refresh would land in a store nothing pushes to the page: this read is the only path that can carry the answer.
Sourcepub async fn diff_scan(
&self,
slot: &SessionSlot,
cwd: &Path,
refresh: &BaseRefresh,
) -> Option<WorkSnapshot>
pub async fn diff_scan( &self, slot: &SessionSlot, cwd: &Path, refresh: &BaseRefresh, ) -> Option<WorkSnapshot>
The scan a drawing read must take: pinned to the base revision the refresh resolved, and gate-awaited so it cannot serve a store written before the base moved. Local only.
None when the scan could not be taken - no session behind the seat,
or its gate held past the read’s bound (which scan_work_pinned logs
with the slot and the wait). The caller must then re-read locally
(Self::work_pinned_local) at the same sha: the store’s count
predates the base the refresh moved, and a scan against the ref’s name
cannot be paired with content read at the sha this read resolved.
The refresh’s state rides the stored row, so the base beside a pinned
ahead is that base’s own measurement rather than the last watch’s.
Sourcepub async fn work_pinned_local(
&self,
slot: &SessionSlot,
cwd: &Path,
base: Option<&str>,
refresh: Option<&BaseRefresh>,
) -> WorkSnapshot
pub async fn work_pinned_local( &self, slot: &SessionSlot, cwd: &Path, base: Option<&str>, refresh: Option<&BaseRefresh>, ) -> WorkSnapshot
The local re-read a drawing read takes when its pin could not be: the
count and the chain must describe the ref the content is read at, and
that ref is the one the refresh just left. Pinned to the same sha the
content read takes, so a fetch that moves the ref between the two
cannot straddle two bases; the refresh’s own state rides it, so the
row’s base and the diff’s agree. Never fetches, and takes no gate.
Sourcepub fn store_work_snapshot(&self, slot: &SessionSlot, snapshot: WorkSnapshot)
pub fn store_work_snapshot(&self, slot: &SessionSlot, snapshot: WorkSnapshot)
Store a scan’s answer, which is the write half of Self::work.
A fixture’s door, and the same store the seat’s own loop writes: one store means a record read and a pushed row cannot disagree.
Sourcepub fn hold_seat(&self, slot: &SessionSlot)
pub fn hold_seat(&self, slot: &SessionSlot)
Show a seat: this view is looking at it, which is what keeps its working tree scanned while the view holds it. A sessionless seat is held too - its watch waits for the session - so every call takes a hold, and a caller must release it exactly once.
The caller owes Self::note_record_read once its record is built.
That mark is what starts the watch’s first pass; a view that holds
without it has a watch that waits and scans nothing.
Sourcepub fn note_record_read(&self, slot: &SessionSlot)
pub fn note_record_read(&self, slot: &SessionSlot)
Mark the seat’s record read, so its watch’s first pass starts from the stores the snapshot carried.
Sourcepub fn release_seat(&self, slot: &SessionSlot)
pub fn release_seat(&self, slot: &SessionSlot)
Stop showing a seat, which is what lets its scan go when nobody else is holding it.
Sourcepub fn refresh_context_usage(
&self,
slot: &SessionSlot,
) -> Result<(), DispatchError>
pub fn refresh_context_usage( &self, slot: &SessionSlot, ) -> Result<(), DispatchError>
Ask the core for a fresh context reading on slot, which its bridge
answers with SessionUpdate::ContextUsageSnapshot.
The ask rather than the read, because the reading only exists once the CLI has computed it: a view asks for the seat it is addressing, and the socket asks for the seat a client reads, so both reach the one probe rather than each holding a way to reach the CLI.
§Errors
DispatchError::UnknownSession
when the seat has no agent to ask or has not stamped a session id yet,
and DispatchError::SessionClosed
when the ask could not be handed to that agent.
Sourcepub fn refresh_mcp_snapshot(
&self,
slot: &SessionSlot,
) -> Result<(), DispatchError>
pub fn refresh_mcp_snapshot( &self, slot: &SessionSlot, ) -> Result<(), DispatchError>
Ask the session’s bridge for a fresh MCP snapshot, which it answers
with SessionUpdate::McpSnapshot.
The ask rather than the read, for the same reason as
Self::refresh_context_usage: the snapshot only exists once the
bridge has answered one, and the socket asks for the seat a client
reads the way a view asks for the seat it is addressing.
§Errors
Sourcepub fn monitors(&self, slot: &SessionSlot) -> Vec<MonitorRecord>
pub fn monitors(&self, slot: &SessionSlot) -> Vec<MonitorRecord>
The monitors the session has running, and the ones that settled while it did, folded from the wire.
Empty when nothing is running, however many monitors have been and gone: the set drains once every entry in it is terminal, which is the rule a view applies to its own copy.
Sourcepub fn background_tasks(&self, slot: &SessionSlot) -> Vec<BackgroundTask>
pub fn background_tasks(&self, slot: &SessionSlot) -> Vec<BackgroundTask>
The seat’s live tasks, each entry with the command its own card carried: the CLI’s background registry plus the long foreground calls it announced, which never enter that registry.
Empty when nothing is running. A view draws the processes feed’s leading rows from this: the tasks are held on the session so a view that attached after the snapshot arrived still reads them, and the command is what tells it whether the OS walk has already adopted the process behind one.
Source§impl ViewSurface
impl ViewSurface
pub fn plugins(&self) -> PluginsView
Source§impl ViewSurface
impl ViewSurface
pub fn reviews(&self, project: &str, branch: &str) -> ReviewsView
Sourcepub fn tree_reviews(&self, seat: &SessionSlot) -> TreeReviewsView
pub fn tree_reviews(&self, seat: &SessionSlot) -> TreeReviewsView
The seat’s tree-scope reviews, each thread placed against the seat’s working files by line content and neighbours - one bounded read per commented file, zero threads meaning zero reads.
Sourcefn place_tree_threads(
&self,
seat: &SessionSlot,
threads: Vec<ReviewThread>,
) -> Vec<PlacedThread>
fn place_tree_threads( &self, seat: &SessionSlot, threads: Vec<ReviewThread>, ) -> Vec<PlacedThread>
Place every seated thread by line content and neighbours against the file it was written on, read from the seat’s tree NOW.
Each commented file is read once per call, through the same bounded document read a viewer’s route uses (the ceiling, the escape check, the UTF-8 answer); zero threads means zero reads. A file the read refused leaves its threads unplaced rather than dropping them - a vanished file must not erase the comments about it.
Sourcepub fn place_document_threads(
&self,
threads: Vec<ReviewThread>,
lines: Option<&[String]>,
) -> Vec<PlacedThread>
pub fn place_document_threads( &self, threads: Vec<ReviewThread>, lines: Option<&[String]>, ) -> Vec<PlacedThread>
A document’s threads, each placed against the document’s own current lines by its first captured line - the code review’s re-anchoring rule, over a whole document rather than a diff.
None when the read’s bytes are not text: a viewer that draws no
source lines has no line to place a thread on.
Source§impl ViewSurface
impl ViewSurface
Sourcepub fn handoff_in_flight(&self, slot: &SessionSlot) -> bool
pub fn handoff_in_flight(&self, slot: &SessionSlot) -> bool
Whether a handoff owns this seat: its judgment turn is running, or the swap it opened is still building or spawning.
From the moment the judgment prompt is sent until the successor connects, the seat has no composer to take input for - which is what tells the page to stand down. A handoff merely parked on a running turn is not this: the person still has the seat until the boundary, when it begins for real.
Source§impl ViewSurface
impl ViewSurface
Sourcepub async fn usage(self: &Arc<Self>) -> Result<UsageReport>
pub async fn usage(self: &Arc<Self>) -> Result<UsageReport>
Scan the shared session-JSONL pool into the four windows a usage view drew.
The scan walks the pool and parses every file behind it, so it runs on the blocking pool rather than on the task that serves a socket: a connection must not wait on a disk walk, and neither must a render.
A scan that panicked is an error rather than an empty report - an all-zero report reads as a pool with no usage in it, which is a different answer from “the scan did not run”.
A scan also kicks the pricing refill (refill_pricing): the answer
that goes out carries costs from whatever table was cached, the
first-open behaviour a view has, and the priced report lands in the
cache for the next ask.
Sourcefn refill_pricing(self: &Arc<Self>)
fn refill_pricing(self: &Arc<Self>)
Refresh the pricing table in the background, and re-scan once it lands, so the next ask answers priced figures.
The fetch must not be waited on. It crosses the network and can take seconds - up to the litellm timeout - and a subject’s ask is answered inside its connection’s own loop, where a wait on one ask stalls every other message on that socket. This is a view’s move instead (scan now, re-price when the fetch lands), with the re-scan replacing the held report so the re-ask finds it.
Workspace::refresh_pricing answers false for a cache that is
still fresh, so a kick costs a redb read when there is nothing to
fetch - and the once-a-day fetch happens on the first scan after the
window lapsed, whatever view asked.
Sourcefn held_usage(&self) -> Option<UsageReport>
fn held_usage(&self) -> Option<UsageReport>
The last scan, while it is inside USAGE_WINDOW.
fn remember_usage(&self, report: &UsageReport)
Source§impl ViewSurface
impl ViewSurface
Sourcepub fn vault_state(&self) -> Option<VaultState>
pub fn vault_state(&self) -> Option<VaultState>
The shared vault session as the settings page draws it, or None when
no [vault] is configured.
Source§impl ViewSurface
impl ViewSurface
Sourcepub fn voices(&self) -> TtsView
pub fn voices(&self) -> TtsView
The [tts] section’s own read: on or off, and where its model lands.
No seat, because neither fact varies by one.
Sourcepub fn voice_release(&self, client: u64) -> Vec<SessionSlot>
pub fn voice_release(&self, client: u64) -> Vec<SessionSlot>
A connection has left: every seat it was listening to drops it, and each seat’s subscribers hear the count it left behind.
Source§impl ViewSurface
impl ViewSurface
pub fn new(workspace: Arc<Workspace>) -> Self
Sourcepub fn terminal_attach_or_open(
&self,
seat: &SessionSlot,
id: Option<u16>,
) -> Result<Arc<TerminalEntry>, String>
pub fn terminal_attach_or_open( &self, seat: &SessionSlot, id: Option<u16>, ) -> Result<Arc<TerminalEntry>, String>
The terminal a client asked to view, with this connection counted as a viewer. An attach naming a terminal the seat no longer has is refused; an attach naming none opens the seat’s terminal, which is the person’s door on a seat no agent has touched.
Sourcepub fn take_terminal_control(
&self,
seat: &SessionSlot,
id: u16,
viewer: u64,
) -> Result<(), String>
pub fn take_terminal_control( &self, seat: &SessionSlot, id: u16, viewer: u64, ) -> Result<(), String>
The person’s own take: always explicit, and landing even during a parked hand-off, whose surface opens as a viewer’s.
Sourcepub fn terminal_close(&self, seat: &SessionSlot, id: u16) -> Result<(), String>
pub fn terminal_close(&self, seat: &SessionSlot, id: u16) -> Result<(), String>
The person’s close door: end the terminal’s process group.
Sourcepub fn terminal_resize(
&self,
seat: &SessionSlot,
id: u16,
cols: u16,
rows: u16,
) -> Result<(), String>
pub fn terminal_resize( &self, seat: &SessionSlot, id: u16, cols: u16, rows: u16, ) -> Result<(), String>
The attached surface changed size.
Sourcepub fn terminal_write(
&self,
seat: &SessionSlot,
id: u16,
bytes: &[u8],
) -> Result<(), String>
pub fn terminal_write( &self, seat: &SessionSlot, id: u16, bytes: &[u8], ) -> Result<(), String>
The person’s own keystrokes, written to the PTY. They are not logged or forwarded, but the shell may echo them onto the screen the terminal tools read once the hand-off resolves.
Sourcepub fn terminal_held(&self, seat: &SessionSlot) -> bool
pub fn terminal_held(&self, seat: &SessionSlot) -> bool
Whether a hand-off parked on the seat’s terminal owns its input.
Sourcepub fn terminal(&self, seat: &SessionSlot) -> Option<TerminalSummary>
pub fn terminal(&self, seat: &SessionSlot) -> Option<TerminalSummary>
The seat’s terminal as the record draws it, when it has one.
Sourcepub fn test_usage_scans(&self) -> u64
pub fn test_usage_scans(&self) -> u64
How many pool scans this surface started. Test-only: the single-flight guard’s whole claim is that two concurrent misses start one.
Sourcepub fn seed_test_usage_gate(&self, gate: Arc<Barrier>)
pub fn seed_test_usage_gate(&self, gate: Arc<Barrier>)
Hold every usage miss here until the test lets it go. Test-only, and
None outside the tests that seed one.
async fn pass_test_usage_gate(&self)
Sourcepub fn tool_record_cap_bytes(&self) -> usize
pub fn tool_record_cap_bytes(&self) -> usize
The byte cap one frame’s tool_use_result may reach in a served
page (#2374): [server] tool_record_cap_kb, with 0 meaning whole.
Sourcepub fn first_frame_cap_bytes(&self) -> usize
pub fn first_frame_cap_bytes(&self) -> usize
The conversation bytes a session’s first snapshot may carry:
[server] first_frame_cap_kb, with 0 meaning the turn count alone
decides the page.
Sourcepub fn more_page_cap_bytes(&self) -> usize
pub fn more_page_cap_bytes(&self) -> usize
The conversation bytes a more page may carry:
[server] more_page_cap_kb, with 0 meaning the turn count alone
decides the page.
Sourcepub fn dispatch(&self, command: Command) -> Result<(), DispatchError>
pub fn dispatch(&self, command: Command) -> Result<(), DispatchError>
Dispatch one command to the core, which is how a view acts rather than only reads.
A verb rather than an accessor, on purpose: a second view is handed the writes it needs and not the whole core, so nothing here grows a way to reach the workspace itself.
Sourcepub fn fatal_error(&self) -> Option<AppError>
pub fn fatal_error(&self) -> Option<AppError>
The last fatal error, or None when nothing has failed fatally.
App-level rather than the seat’s: it names a startup that could not happen. It is held because the update carries no state of its own, so a view that attached afterwards has no other way to learn of it.
Sourcepub fn service_status(&self) -> Option<ServiceIssue>
pub fn service_status(&self) -> Option<ServiceIssue>
The statuspage’s last answer, or None.
App-level: the answer is the same for every viewer, so the core
probes it once and every view reads this rather than fetching its
own. None covers both a healthy statuspage and one that could not
be reached, which is the fetch’s own contract.
Sourcepub fn pending_asks(&self, slot: &SessionSlot) -> Vec<PendingAsk>
pub fn pending_asks(&self, slot: &SessionSlot) -> Vec<PendingAsk>
What the seat at slot is holding, with a draft leading and the rest
oldest first, so a view that attached after a prompt landed still
draws what it offers. Empty when the seat is holding nothing.
Sourcepub fn subscribe_mirror(&self) -> UnboundedReceiver<SeatUpdate>
pub fn subscribe_mirror(&self) -> UnboundedReceiver<SeatUpdate>
The core’s own update stream for a consumer that reads it WITHOUT answering.
The caller this exists for is a process-wide fold. It folds into state of its own and never replies, so the answering role would park a permission, question or Slack draft waiting on an answer nobody sends.
A view with a dock to answer from takes Self::subscribe_client.
Sourcepub fn subscribe_client(&self, answering: bool) -> UnboundedReceiver<SeatUpdate>
pub fn subscribe_client(&self, answering: bool) -> UnboundedReceiver<SeatUpdate>
The core’s stream for a connecting client, registered with the role that client declared.
A stream carries what follows its own subscribe and nothing before it, whatever the role: a client that attaches late reads what it missed from the subject’s snapshot.
The role decides one thing: whether the core may park a prompt on this
connection’s reply. answering is the client’s own statement that it
can show and answer a prompt.
Sourcepub fn roster(&self) -> Roster
pub fn roster(&self) -> Roster
The projects, their sessions, and the per-project lists the home page renders.
Sourcepub fn session(&self, slot: &SessionSlot, cwd_raw: &Path) -> SessionState
pub fn session(&self, slot: &SessionSlot, cwd_raw: &Path) -> SessionState
One session’s operational state. cwd_raw is the caller’s own
cwd for the session, which a git worker’s worktree overrides.
Sourcepub fn seat_next_seq(&self, slot: &SessionSlot) -> u64
pub fn seat_next_seq(&self, slot: &SessionSlot) -> u64
The number slot’s next frame will be minted under: the read a
client makes when it attaches. It belongs to the epoch its occupant
names - a seat whose occupant changed starts over at 1 - so the two
are read together.
Sourcepub fn seat_frames_between(
&self,
slot: &SessionSlot,
from: u64,
to: u64,
) -> Vec<SeatUpdate>
pub fn seat_frames_between( &self, slot: &SessionSlot, from: u64, to: u64, ) -> Vec<SeatUpdate>
The frames slot’s stream emitted under the numbers in from..=to,
oldest first: the answer to a client’s range ask while the seat’s own
window holds them.
Sourcepub fn seat_oldest_kept_seq(&self, slot: &SessionSlot) -> Option<u64>
pub fn seat_oldest_kept_seq(&self, slot: &SessionSlot) -> Option<u64>
The oldest number slot’s window still holds, None for a seat that
has emitted nothing: what tells a range answered from memory from one
that has to be read off the transcript.
Sourcepub fn seat_unringed_between(
&self,
slot: &SessionSlot,
from: u64,
to: u64,
) -> u64
pub fn seat_unringed_between( &self, slot: &SessionSlot, from: u64, to: u64, ) -> u64
How many of the numbers in from..=to on slot’s stream no window
holds: the frames that close an epoch are numbered but kept out of the
ring, so a reader measuring whether a copy covers a stretch leaves them
out of what it must account for.
Sourcepub fn seat_epoch_opened_with_rows(&self, slot: &SessionSlot) -> bool
pub fn seat_epoch_opened_with_rows(&self, slot: &SessionSlot) -> bool
Whether slot’s latest epoch-opening seal carried rows, which no ring
holds.
Sourcepub fn cli_version(&self) -> Option<CliVersionInfo>
pub fn cli_version(&self) -> Option<CliVersionInfo>
The installed and npm-published claude CLI versions. The same
answer for every viewer, so the core holds it and a change arrives
as SessionUpdate::CliVersionChanged; None until the boot probe
lands.
Sourcepub fn client_attached(&self)
pub fn client_attached(&self)
Register this connection as an attached client for as long as it lives, so a document hand-off can tell a viewer exists to open on. The transport’s per-connection guard calls these two.
Sourcepub fn client_detached(&self)
pub fn client_detached(&self)
The counterpart of Self::client_attached.
Sourcepub fn server_config(&self) -> ServerConfig
pub fn server_config(&self) -> ServerConfig
The live [server] snapshot, read by the socket’s origin gate on
every upgrade so a reload’s new allowances apply to the next
connection without a restart.
Sourcepub fn client_config(&self) -> ClientConfig
pub fn client_config(&self) -> ClientConfig
The [client] settings a connection greets with. Read when the
connection opens, so an edit applies to the next connection.
Sourcepub fn save_settings(
&self,
fingerprint: &str,
edits: &[SettingsEditRequest],
) -> Result<(), SettingsSaveError>
pub fn save_settings( &self, fingerprint: &str, edits: &[SettingsEditRequest], ) -> Result<(), SettingsSaveError>
Apply a batched settings save. The refusal is answered by the connection that asked, on the settings refusal frame.
Sourcepub fn read_document(
&self,
seat: &SessionSlot,
path: &str,
reviewable: bool,
) -> Result<DocumentRead, String>
pub fn read_document( &self, seat: &SessionSlot, path: &str, reviewable: bool, ) -> Result<DocumentRead, String>
One document, read out of the seat’s own tree for a viewer’s route.
path resolves against the session’s working directory and
reviewable is what the hand-off flagged; both go to the read in
forge_agent::env::documents, whose refusals are the sentences a
route draws and a refused hand-off returns. The seat’s project’s
document_roots go with them, so a document the hand-off accepted
opens here too. The read is blocking (a file off disk), so a
caller offloads it rather than running it in a handler.
Sourcepub fn document_max_bytes(&self) -> u64
pub fn document_max_bytes(&self) -> u64
The byte ceiling every whole-file read takes, which the record’s whole-file anchor fallback shares with the review page’s expansion.
Sourcepub async fn file_content(
&self,
seat: &SessionSlot,
path: &str,
sha: Option<&str>,
) -> Result<FileLines, String>
pub async fn file_content( &self, seat: &SessionSlot, path: &str, sha: Option<&str>, ) -> Result<FileLines, String>
One file’s whole content at a revision, for a review page’s between-hunk expansion.
sha names the revision - a commit’s own tree for a commit scope -
and None reads the seat’s branch tip, which is the new side of the
all-changes diff. The read is bounded by the same ceiling a document
read is, and a file cut at that ceiling answers the lines it carried,
flagged, rather than refusing: the gaps those lines reach are still
worth drawing.
None is the branch tip read NOW, and it is stated rather than
pinned for want of anything to pin it to. The record’s diff was
scanned from this same seat, but it carries no revision for its
branch layer - its commit chain names only the commits ahead of the
default - so a branch that moves between the record’s read and the
reader’s ask can draw newer lines against frozen hunks. The window is
the record’s age, the page draws its own controls only for lines the
read reaches, and the alternative (carrying the scanned revision on
the record, and a wire field with it) buys a race nobody has hit for
a field every reader would pay.
Sourcepub fn conversation(
&self,
slot: &SessionSlot,
cwd_raw: &Path,
) -> ConversationHistory
pub fn conversation( &self, slot: &SessionSlot, cwd_raw: &Path, ) -> ConversationHistory
The conversation the session at slot holds, read from its own
transcript on disk: the same read a resume performs, for a view
that arrived after the session was already running or one opening a
seat that is asleep (the id is the live occupant’s when there is one,
and the store’s own row otherwise - transcript_place resolves it).
Subscribe first, then call this. The read is the baseline the stream is applied on top of, so a message may be in both: a view resolves that by dropping the stream’s copy when the read already carries its id, and reading first instead would lose whatever arrived in between, which no later read brings back.
The one class the two halves cannot reconcile by id is the prompts
the workspace injects. A cron, a Gotify delivery, a Slack bundle or a
peer comm reaches the stream as text alone (CronPromptAppended,
GotifyNotificationAppended, SlackMessageAppended) and reaches this
read as the envelope-wrapped row the CLI persisted, with no id shared
between them, so a view holding both has to match on the envelope’s
source and body instead.
This reads a whole transcript off disk on the calling thread, with no size cap: a real session costs milliseconds, and the largest transcript on this machine 2.5 s in a release build. A caller must offload it rather than run it in a handler, the way this project’s other async work does with a spawned task on its own channel.
cwd_raw is the session’s own cwd, which a git worker’s worktree
overrides. An empty cwd_raw resolves to the slot’s recorded path,
because the read’s no-directory arm walks every projects/ directory
for the first file named after the session: that pays the walk per
call and takes whichever copy of the same session it meets first. A
slot with no live session, or one whose cwd no record places, reads as
an empty conversation, and records which of the two it was, so a view
drawing nothing can say which of them it met rather than only that it
has nothing to draw.
This is the surface’s read for a view that does not hold its own,
and the transport reads it only for a seat it holds nothing for.
A live seat’s conversation is the one the session task seeds, so no
request walks a file for it; a sleeping one has no task to ask and
this read is what answers it (transport::wire::seed_from_transcript).
A view keeps its own copy in session state. The verb’s other
callers are the surface’s own tests and the measurement harness, which
is what a surface test is for rather than a sign it is unused.
Sourcepub fn cron_names(
&self,
slot: &SessionSlot,
uuids: &[&str],
) -> BTreeMap<String, CronName>
pub fn cron_names( &self, slot: &SessionSlot, uuids: &[&str], ) -> BTreeMap<String, CronName>
The schedules the fired prompts in uuids came from, keyed by the
prompt’s own id: the read half of the pairing the delivery frame
carries once.
Sourcepub fn transcript_rows(
&self,
slot: &SessionSlot,
cwd_raw: &Path,
below: Option<&TranscriptAnchor>,
rows: usize,
) -> Option<TranscriptSpan>
pub fn transcript_rows( &self, slot: &SessionSlot, cwd_raw: &Path, below: Option<&TranscriptAnchor>, rows: usize, ) -> Option<TranscriptSpan>
The rows of slot’s transcript that sit strictly before below, for a
paging read that has walked below the held window’s floor.
A bounded row read. below is the row the page above stopped at -
the id it carries and, once a read has seen it, the byte it starts at -
and None reads the file’s own newest rows, which is what a walk asks
for on a window that holds no row to name. None when the transcript
cannot answer: no session for the slot, no file, or an anchor the file
does not carry.
Sourcefn transcript_place(
&self,
slot: &SessionSlot,
cwd_raw: &Path,
) -> Option<(String, String)>
fn transcript_place( &self, slot: &SessionSlot, cwd_raw: &Path, ) -> Option<(String, String)>
The session id and the working directory a transcript read for slot
needs, each unmet case recorded as it was met.
The id is the live occupant’s when there is one, and the store’s own row otherwise. A sleeping seat still has a session - the one a wake resumes - and its transcript is what a view opening on it reads, so a read that resolved only pooled sessions drew a sleeping seat empty.
Auto Trait Implementations§
impl !Freeze for ViewSurface
impl !RefUnwindSafe for ViewSurface
impl !UnwindSafe for ViewSurface
impl Send for ViewSurface
impl Sync for ViewSurface
impl Unpin for ViewSurface
impl UnsafeUnpin for ViewSurface
Blanket Implementations§
§impl<T> AnySync for T
impl<T> AnySync for T
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
impl<S, T> Duplex<S> for Twhere
T: FromSample<S> + ToSample<S>,
§impl<S> FromSample<S> for S
impl<S> FromSample<S> for S
fn from_sample_(s: S) -> S
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more