For agents, on top of README.md, which they read first.
Notes for agents
- This crate is an adapter; the policy is in
whiskers-service. A rule about what a route does (a cap, an order of checks, what a refusal says, what may pass to the model) belongs inwhiskers-serviceorwhiskers-ports, where every backend gets it. What belongs here is keeping state and calling out. Adding a route means a test inwhiskers-service/tests/service.rs, not code inmain.rs. - The wire must not change. The tablet and the app depend on it. To check a change, run
cargo test -p whiskersd(the end-to-end sync test), restart the service and runtools/preflight.sh; a refactor of this crate should also leave a recorded request script byte-identical against the old binary. - Held to the same laws as every adapter.
tests/conformance.rsruns thewhiskers-conformancesuite on these adapters and adds what only threads and files show (eight pushes at one cursor take one; eight voice lines cannot pass a cap with room for one). A new adapter here gets its laws there. - Concurrent on purpose, and Jev is behind one thread. A thread per request, so a slow model call (the
memory work after a turn) never holds up the next turn's safety check; that exact blocking was the "listens
but doesn't reply" lag. The Jev client lives on its own thread (its futures are not
Send) and request threads hand it jobs. The adapters block inside theirasync fns and never wait, somain.rsdrives the service withrun_readyon the request's own thread. Do not make the service single-threaded again, and do not give an adapter a future that really waits without givingmain.rsan executor for it. - Never log message text. What she says is in the parents' log, written by the tablet. This service logs nothing but its own start-up, counts, ids and refusals. Never log a key or the voice id.
- Spend goes through jev-http's ledger, shared with every other Jev program on the machine. Do not
construct a client that bypasses it (
judge.rsbuilds one withLedger::open()). - The voice's daily cap is the service's, reserve-then-settle, separate from the tablet's
VoiceBudget. The tablet's is a courtesy to the child; this one protects the plan. It lives inwhiskers-ports(VoiceDay) and the service;speak.rsonly speaks. Do not move a cap check into the adapter. - No default ElevenLabs voice. A voice id is not something to recall from memory;
/speakrefuses untilELEVENLABS_VOICE_IDis set. The free plan's API access is unconfirmed (open item in the brain's elevenlabs page). - TLS is on here (
ureqdefault features), unlike the tablet crates, which are plain http. This runs on the operator's machine, where a C crypto backend is fine. /v1/messagesexists so the gateway is never put on the private network. Do not widen what it lets through (other models, tools, streaming, larger replies) to suit a feature: the gateway is the operator's subscription with no login of its own, andThinkRequest::vetinwhiskers-portsis its only guard.pkill -f whiskersdrun from a shell whose own command line contains that string kills the shell; use the pid.