Files
ai-app/docs/CLIENT_CORE.md

75 lines
3.3 KiB
Markdown

# `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 injectable `Transport` trait.
- `sse.rs` and `event_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`, and `highlight/`: display-independent text
parsing and spans.
- `config.rs`: enrollment-link parsing and the shared `EnrolledServer` value.
- `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/`.