ryu-predict 0.1.5

Predict: the system-wide predictive-typing brain for Ryu — an extracted Core capability crate owning the completion engine (config, per-app allowlist, secure-field denylist, prompt assembly, reply cleanup) and the `/api/predict/*` HTTP surface. In-process default; Core consumes it as a NON-optional path dependency. Every cross-cutting call it needs (preferences, the agent's bound model, the default model id, the Gateway side-model call, and the plugin-owned enabled flag) is inverted through the `PredictHost` trait so this crate has ZERO dependency on apps/core. The process-global on/off flag (`predict::set_enabled` / boot seeding) stays in the kernel — it is the built-in Predict plugin's switch.
Documentation
# ryu-predict

Predict — the system-wide predictive-typing **brain**, an extracted **Core capability
crate**. It powers inline autocomplete (ghost text, Tab to accept) for any native app.
The native overlay (`apps-store/predict`) stays deliberately dumb: it reads the caret
context, POSTs it here, and renders whatever string comes back. No Gateway URL, key, or
model id ever lives in the overlay process.

## Role in the decomposition

A primitive lifted out of `apps/core`. This crate owns the completion **engine**: the
config (model / effort / per-app allowlist / debounce), the secure-field denylist,
prompt assembly, reply cleanup, and the `/api/predict/*` HTTP surface. Every
cross-cutting call — preferences, the agent's bound model, the default model id, the
Gateway side-model call, and the plugin-owned enabled flag — is inverted through the
`PredictHost` trait, so the crate has **zero dependency on `apps/core`**.

## Placement (Core vs Gateway)

Deciding *what runs* (assemble the prompt, enforce the app allowlist, refuse secure
fields) is **Core**. The model call is handed to the **Gateway** via
`PredictHost::call_side_model` (the same path `/btw`, goals, and double-check use), so
model routing / firewall / budgets / audit all apply.

## Key API

- `PredictHost` (trait) — the inversion seam; implemented Core-side in
  `predict_host.rs` over `ServerState`.
- `PredictConfig` — persisted `camelCase` config blob (pref key `predict-config`),
  shared with the desktop settings tab and the overlay.
- `is_secure_control(control)` — pure, fail-closed password/secure-field refusal.
- `app_allowed(allowlist, app)` — per-app basename allowlist (empty = all apps).
- `build_messages(context)` / `clean_suggestion(raw, max)` — prompt + reply hygiene.
- `routes()` / `PredictCtx` — the `/config` (get/put) + `/complete` axum router.

## What stayed in the kernel

The process-global on/off flag (`predict::set_enabled` / `is_enabled`, seeded at boot
from the built-in **Predict** plugin, flipped on plugin enable/disable) stays in
`apps/core` — it is the plugin's switch, read here via `PredictHost::is_enabled`.

## Consumed as

Compiled-into-core crate (non-optional path dependency), merged into Core's router.

## Swap-seam

The model is never hardcoded: it resolves agent-bound model → config `model` → env →
`PredictHost::default_model`, and the call routes through the swappable Gateway.