Skip to content

Latest commit

 

History

History
75 lines (61 loc) · 5.09 KB

mobile-control.md

File metadata and controls

75 lines (61 loc) · 5.09 KB

Mobile control — driving missions from uteke-mobile (issue #202)

The 0.8.0 remote-approval surface (#200) is the entire backend a phone needs: missions run on the box under tole serve, the phone lists running missions, receives pending Write approvals, and approves/denies from anywhere. No new backend exists or is planned — the phone is a client of the token-authenticated serve face.

The surface uteke-mobile consumes

All routes live under tole serve (bearer-token auth on everything except /health; decisions revalidate per request; auth failures are rate-limited per source IP).

Action Route Notes
List sessions GET /sessions id + busy per session
Start a mission POST /sessions {cwd} → POST /sessions/{id}/prompt {text} the prompt is the mission goal (tole mission semantics chain turns; a serve prompt runs ONE turn — mission chaining over serve is a follow-up)
Mission status GET /sessions/{id}/status busy flag, entry count, usage_report (tokens, cache-hit rate, request-size split; #211), mission cost report (fact/mission register, #201)
Pending approvals GET /approvals queue entries: id, session, tool, status; expires to denied after 15 min
Approve / deny POST /approvals/{id}/decision {"decision":"allow"|"deny"} allow = one-shot for exactly that (session, tool+input) + automatic resume; deny = recorded verdict
Cancel a turn POST /sessions/{id}/cancel (#178) the turn settles cancelled, durably
Steer a turn POST /sessions/{id}/interrupt {text, urgent?} (#377) 202 queued; delivered at the turn's next safe point (after the running tool, before the next model call) as an attributed user-role entry, nothing is lost. urgent also withdraws a tool call that has not started. 409 when no turn is running, 400 for blank/oversized text or a full queue (16 pending)
Cost report GET /sessions/{id}/status the mission register: turns, steps, tokens, wall time, per-risk-tier tool calls

The round trip the app must support (the cross-repo acceptance):

  1. GET /approvals on a poll interval (the push-channel decision — ntfy/UnifiedPush vs FCM — belongs to uteke-mobile; tole exposes no push endpoint by design, see the threat model).
  2. Render the pending Write (tool + description + session).
  3. POST /approvals/{id}/decision — allow or deny.
  4. 202 (allow) means the resume is running; GET /sessions/{id}/status until busy clears.

Status JSON fields

GET /sessions/{id}/status returns {id, entries, busy, usage_report}. entries and usage_report are null while a turn is in flight (the session storage is busy). usage_report was added by #211 and is purely additive: clients must ignore unknown fields (the existing three keep their meaning). Its shape — every unknown value is null, never a fabricated 0:

Field Meaning
steps provider steps that reported usage (the same count the mission step budget uses; rows holding only tole_wire are not counted)
prompt_tokens, completion_tokens sums over the usage ledger
reasoning_tokens sum of completion_tokens_details.reasoning_tokens; null if no step reported it
cached_tokens sum of cached prompt tokens (prompt_tokens_details.cached_tokens, falling back to cached_read_tokens and the other gateway spellings); null if no step reported it
cache_hit_rate cached / prompt over the steps that reported cached tokens; null if unknown
wire null for sessions recorded before #211, else {first, last, history_growth_per_step}; first/last = {prefix_chars, system_chars, tools_chars, history_chars, messages} of the first/last step that has it (characters, not bytes; prefix_chars = system + tools)
turns per user turn (#379), derived from the same ledger: [{turn, user_entry, steps, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, reasoning_tokens, cost_usd}]. Each token field is the sum over the turn's steps that reported it and null when none did (unknown is never 0); no elapsed time is carried. [] for a session with no turn

The mission cost report (fact/mission) is rendered by tole status; it gained an additive cached_tokens (null when no step reported it). The MCP/ACP tole_session_status tool returns the same usage_report next to busy and entries.

Auth model

The serve token is approval authority (see the threat model): a phone holding it can let code write to the box. Practical scoping until dedicated device tokens exist (#follow-up): run missions under a dedicated OS user, keep --allow/--trust minimal on the serve process, and treat the token as a revocable secret (rotate = restart serve).

What lives where

  • tole (this repo): the REST surface above, its hardening, audit trail, expiry semantics. Done in #200/#214.
  • uteke-mobile: the Flutter client, the push channel, device storage for the token, biometric gating of the approve button. The parked FFI plan (flutter_rust_bridge over tole-core) is NOT required for control — the REST surface is the contract.