# Application service agent guide
## Overview
Local frontend protocols, durable session attachments/replay, streamed turns and activity, authentication, and catalog/configuration access. Stdio follows `docs/features/application-service.md`; persistent coordination follows RFC-0002 and the Unix adapter follows `docs/features/unix-daemon.md`.
## Where to look
| Envelopes, safe identities, limits, encoding | `protocol.rs` |
| Initialization, capabilities, readiness, request guards | `dispatcher.rs` |
| Request routing, worker queue, turn lifecycle | `coordinator.rs` |
| Persistent connections, grants, snapshots, operation retention, adapter entrypoint | `persistent/` |
| JSONL framing, output batches, shutdown draining | `stdio.rs` |
| Private Unix transport, endpoint security, launch/stop | `unix.rs`, `unix/connect.rs` |
| Shared runtime resources, startup loading, per-turn settings reload | `runtime.rs` |
| Session admission, agent workers, ordered sinks, terminal persistence | `turns.rs` |
| Allowlisted activity DTOs, hashed identities, summary bounds, capability flags | `activity.rs` |
| Session operations, explicit/implicit attachments, close state | `sessions.rs` |
| Auth status, login worker, confirmed logout | `auth.rs` |
| Catalog projections, refresh, scoped settings validation | `configuration.rs` |
## Local conventions
- Keep framing and output in adapters; `ServiceCoordinator` owns routing, active turns, and the worker queue. New adapters should reuse it rather than copy turn orchestration.
- Unix control handshakes never register persistent clients. Keep idle stop atomic in the core, peer UID checks on both socket ends, lifetime lock inodes intact, and launch context explicit. Never replace a live/incompatible service or treat timeout as stale-cleanup authority.
- In stdio, keep each `ServiceOutbound` alive through encoding, writing, and flushing; call `finish_worker_output` after the write attempt. Persistent coordination instead joins finished workers and settles outcomes before queueing terminal delivery; output never owns execution cleanup.
- Encode the entire batch before writing any message. Encoding failure must not emit a replacement response or lose event correlation; this is not a guarantee against partial physical writes on I/O failure.
- Preserve per-turn order: acceptance response, `turn.started` at sequence 0, interleaved assistant/activity events, exactly one `turn.terminal`. Main and child activity share the sequence lock; stamp before enqueue, count activity drops, and close admission before stamping terminal. A cancellation response is separate, not terminal completion.
- Validate accumulated assistant text against the encoded terminal envelope before accepting another delta; do not silently truncate. Terminal `assistant_text` is the authoritative final value.
- Activity sends are nonblocking and lossy; gaps and terminal `activity_dropped` expose enqueue failures. Keep assistant acceptance reliable and reserve maximum sequence/counter encoding in terminal text checks. Activity is transient, not durable replay; terminal text and replay settle conversation state, not lost progress.
- Keep activity projection in `activity.rs`, never serialize display records. Hash untrusted identities; omit arguments/results, arbitrary labels, child previews, and raw metadata. Preserve nullable status fields and parent correlation without leaking runtime names.
- Reasoning summaries require the dedicated `provider_reasoning_summary` callback and verified provider-summary provenance. Generic thinking events are not safe summary evidence. Preserve sanitization, UTF-8 bounds, and explicit redacted/truncated flags; do not promise perfect arbitrary-secret removal.
- `sessions.rs` owns exclusive frontend writer leases. Explicit create/open attachments survive terminal turns; implicit turn attachments and closing attachments release only after terminal output handling and worker cleanup. Shutdown drains workers before clearing attachments.
- Active close requests cancellation and returns `closing`; reject new work until cleanup. Idle close is idempotent for any valid ID and never deletes history or releases another process's lease.
- Delegate list/replay projections to `../sessions/frontend.rs`; never parse JSONL here. Replay is read-only and does not require an attachment. Preserve page, scan, and attachment limits in capability output.
- Durable replay cursors are not live event IDs. Keep stale-prefix resync, gap reporting, and full encoded replay payload bounds intact.
- For parseable malformed records, check safe request IDs for duplicates before returning decoder errors. Do not release the original reservation when rejecting a duplicate.
- Reload effective selection and evaluate provider auth readiness on each valid initialized `status` request. Readiness checks must not refresh credentials, write auth, or use the network; read failures use the fixed sanitized error.
- Auth supports one login flow, one protected-cancellation worker for that flow, a bounded progress queue, and one transient manual callback. Drain auth, turn, and configuration workers during EOF and output-failure shutdown.
- Auth status reads current shared credentials without refreshing. Provider configuration remains the startup snapshot.
- OAuth belongs in `login.rs`; protected auth commits and generation checks belong in `config`. The service only coordinates these boundaries.
- Authorization URLs are transient progress events. Never echo callback input or forward underlying auth/provider errors; use fixed protocol errors and lifecycle labels.
- Logout removes local credentials only. It does not unset environment variables or revoke credentials already handed to running turns.
- Serialize all five catalog/config routes through the coordinator's single configuration worker; retain its request guard through response output. Refresh must not block turn output or cancellation.
- Keep catalog reads cache-only and metadata allowlisted/bounded. Only explicit `catalog.refresh` invokes discovery; report the resulting cache state without promising freshness or exposing provider errors.
- Delegate scoped writes to `config::update_settings_checked`; validate selection/reasoning against fresh catalogs before atomic persistence. Preserve unknown fields, project pins, and global-only fast mode; never expose arbitrary settings writes.
- Stdio turn workers reload selection, reasoning, and fast settings; running turns retain their snapshot. Do not rebuild startup instructions/skills or silently treat auth's startup provider definitions as live configuration.
- Persistent turn admission captures settings in one preparation worker with shared global/project locks, then revalidates deadline and authority on the coordinator. Reject overlapping settings writes/preparations as busy. Provider definitions and startup resources remain fixed; turn workers do not reload captured settings.
## Boundaries and verification
- Stdio stdout is protocol JSONL only. Stdio EOF cancels active turns and login and drains workers; after output failure, continue cleanup without further output attempts. Persistent connection loss revokes control and cancels only that connection's login; dropping the daemon owner cancels/drains all workers.
- Keep started turns on the normal agent path. Project only allowlisted activity, separate from assistant text. Advertise MCP tool/subagent support, but no approvals, steering, MCP server lifecycle, raw tool output, or activity replay.
- Extend existing inline tests for lifecycle, activity ordering/overflow/provenance, privacy bounds, lease conflicts, close/cancellation races, disconnect cleanup, cursor/gap behavior, framing recovery, guard lifetime, and encoded payload limits when changing these contracts.
- Unix process coverage lives in `../../tests/unix_daemon_process.rs` and `unix/survival_tests.rs`. Synthetic accepted-work survival does not establish Linux cgroup survival; Linux and cross-UID checks require their own platform gates.