3.3 KiB
app's client module
app/src/client contains platform- and UI-independent client logic. It
must not depend on iris; a use iris:: below this directory is a layering
defect. event-model remains a separate crate because the server and client
both depend on that wire contract.
Contents
api.rs: REST client over the injectableTransporttrait.sse.rsandevent_stream.rs: SSE framing and session-event following.transcript_cache.rs: bounded, persistent transcript chunks.transcript_source.rs: cache/server selection and live cache updates.transcript_fold.rs: event folding, tool grouping, and page healing.markdown_blocks.rs,ansi.rs, andhighlight/: display-independent text parsing and spans.config.rs: enrollment-link parsing and the sharedEnrolledServervalue.log_ring.rs: bounded process-local diagnostics.
API coverage
ApiClient covers session list/read, messages, unqueue, answers, interrupt,
stop/start, rename, working directory, model, permission mode, notification
setting, commands, compaction, deletion, and transcript pages.
Still missing are setups and discovery, file operations, usage, models and
downloads, attachments, imports, and the global notifications stream.
server/src/routes.rs is the authoritative route table.
Transcript invariants
join_pages heals messages and tool runs split across page boundaries. It
asserts that a tool id does not survive in both halves. It must not assert
sequence ordering across the seam: a peer note carries the sequence of the
turn it belongs above and can legitimately interleave with the page where it
arrived.
Event has no catch-all variant. A newer server adding an event type will
make an older client reject that line rather than draw a placeholder. Fixing
that requires a shared wire-model decision, not a client-only workaround.
TranscriptSource::page(0, ..) returns OlderPage::NothingLoaded without
touching cache or network. This is deliberately distinct from
OlderPage::Events(vec![]), which means the start of the conversation was
actually reached. Network and cache parse failures are errors for the same
reason: none of these states may latch a caller's “no more history” flag.
Fetched transcript lines retain the server's exact JSON bytes through
RawValue. Re-serializing parsed JSON can change floating-point text, causing
the cached and streamed forms of one event to disagree byte-for-byte.
The reconnect/backoff loop and cancellation of a live stream belong to the
embedding runtime. TranscriptSource::follow only guarantees that each frame
is cached before the caller receives it.
Enrollment
EnrolledServer and parse_link understand the same
aiapp://enroll?host=H&port=P&token=T[&ca=B] value used by Android. Storage
is caller-specific: Android uses its platform storage and the desktop writes a
0600 file under its XDG config directory.
Markdown scope
markdown_blocks splits top-level headings, paragraphs, fences, lists,
tables, and quotes. It intentionally does not build a full nested CommonMark
AST; inline styling and nested presentation remain renderer concerns until a
shared non-UI consumer needs them.
Verification
Run ./scripts/run-tests.sh from the repository root. For this crate alone,
run cargo test, cargo clippy --all-targets, and cargo fmt --check from
app/.