aide-inhouse-only-policy-hook · git:20260829.3450c8e · 2026-08-29 · sha256 489cc1dd7724fe00

aide-inhouse-only-policy-hook git:20260829.3450c8eA

Immutable. This exact content is served forever at /api/v1/blob/489cc1dd7724fe00.

---
name: aide-inhouse-only-policy-hook
description: The doctrine that AIDE's desktop-control act() MUST route the policy inference through the in-house model (Cipher v1, 4B) — never a cloud model, never a BYOK provider. Why this is the differentiator (a small model fine-tuned on a verified desktop-control trajectory corpus is more reliable on that exact task than a giant cloud model with no domain adaptation), how to gate it in routes/desktop.ts and the agent's desktop_action tool, and how the operator-overrides-the-safety-rails exception works for the rare case where the operator chooses a different model. Use when wiring the policy hook, when the desktop service gains a new caller, when reviewing desktop safety, or when a user asks "why doesn't my cloud provider work for desktop control".
---

# Desktop Control — In-House Model Only

Born 2026-08-28 from the operator's directive: "as of right now the
only model we're going to allow to be used for desktop control is
our in-house model because we have specifically fine tuned for this
kind of workflow." This is the doctrine for that decision and the
mechanics that enforce it.

## Why this is the differentiator

The competitive claim is **not** "AIDE can drive a desktop" — every
cloud agent in 2026 can do that. The claim is **"AIDE's house model
drives the desktop, locally, with operator-owned safety rails."** Three
reasons in priority order:

1. **Domain adaptation beats scale, on narrow tasks.** Cipher v1 is
   a 4B model that we have specifically fine-tuned on verified
   desktop-control trajectories (1,098 rows per AGENT_NOTES
   2026-08-26, 90/90 generator battery). A giant cloud model with
   no desktop-specific training has a higher loss on the exact
   question "given this Windows snapshot and this grants manifest,
   emit the correct desktop_action DSL". A 4B model that's seen
   the task 1,000 times beats a 1T model that's seen it 0 times.

2. **Latency + all-local loop.** The desktop act() cycle is
   sense-parse-execute-snapshot-verify. The model's response is in
   the hot path. A 4B model on the local GPU returns a DSL action
   in 50-200ms; a 1T cloud model takes 1-3s. The hot path gets
   faster, the operator's pending-approval experience improves, and

## What "in-house only" means concretely

- `cipher v1` (4B, fine-tuned on desktop-control trajectories) is the
  **only** model allowed in the desktop policy hook. The hook
  resolves a model by ID and fails closed if the model is not
  `cipher` family.
- BYOK providers are **not** in scope. Even if the operator has
  `OPENAI_API_KEY` set, `desktop_policy.resolveModel('desktop-act')`
  returns Cipher, not GPT-5. The operator can override per-call
  via a wizard that REQUIRES a one-time confirmation; the override
  is logged.
- The fine-tune is the contract. If the operator wants desktop
  control backed by a different model, the workflow is: ship
  verification trajectories + eval battery for the new model, pass
  the battery, then add to the allowlist. There is no "just swap
  the model" path.

## Where this is enforced

The policy hook has to live in three places; missing any one is a
safety hole:

### 1. The desktop service's policy hook (the canonical gate)
`node/src/services/desktop-control.mjs` does NOT currently invoke a

## Threat matrix

| Threat | Signature | Defense |
|---|---|---|
| Operator routes desktop to a cloud model | byok routing includes `desktop-act: 'openai'` | Policy hook fail-closed: model not in cipher allowlist throws BEFORE act() |
| BYOK provider is the chat model at chat time | operator selects a non-cipher chat model | Desktop-only: the chat model is irrelevant; the desktop policy hook is independent. The chat model proposes; the policy model executes. |
| Cipher model itself goes off-policy (rogue generation) | action tries credential field or out-of-scope target | Existing desktop-control safety: grants + panic + DesktopRefusedError codes + zod-validated op enum. Plus the trajectory recorder captures the bad action as refusal-recovery training data. |
| Operator overrides the safety rail for a single call | wizard sets a non-cipher model in the override slot | The override is LOGGED in the trajectory with `policy_override: true, model_id, operator_id, reason`. Override count per session is capped at 3. After 3 overrides in a session, the hook fails closed. |
| Trained model emits a dangerous action | model has been fine-tuned on verified trajectories, but adversarial input could still trick it | Same defenses as above. Plus: any model output that fails to parse becomes a NO_APPROVAL refusal + trajectory entry. The corpus grows with the failure. |
| Cipher model not running when the policy hook fires | `models/manifest.json` lists cipher but no engine is up | Hook returns `POLICY_MODEL_NOT_READY`. The agent's `desktop_action` tool surfaces this as `NOT_READY` and the operator must wait or start cipher. No fallback to a different model. |

## Pitfalls

- **Do NOT add a "fallback to any chat model" path** to the policy
  hook. The whole point of this doctrine is that desktop control is
  NEVER a generic chat completion. If cipher is not ready, the
  hook fails; the operator sees the failure; the operator starts
  cipher. The system teaches the operator to keep cipher running
  for desktop sessions.
- **Do NOT let the override slot persist** between sessions. The
  override is a per-call flag set by the operator through a wizard,
  not a session preference. Each session starts with cipher-only.
- **Do NOT skip the trajectory record on the policy-override path.**
  Override rows are MORE valuable than normal rows — they teach the
  fine-tune what the operator accepts in edge cases.
- **Do NOT let the operator train a non-cipher model on the
  desktop corpus.** The corpus is a competitive asset; it trains
  the house model only. BYOK users do not get the corpus. This is
  encoded in the file permissions on `.aide/desktop/trajectories/`.

model — `act()` takes a pre-formed request. The policy hook is the
new code that comes between the operator / agent and `act()`. It
must:
- Look up the model configured for the `desktop-act` role in
  `models/manifest.json`.
- Fail closed if the model is not in the cipher allowlist
  (allowlist = `cipher` family, currently `cipher v1`).
- Invoke the model with a structured action DSL
  (see `aide-desktop-control-cipher` skill + `aide-grammar-constrained-generation`).
- Validate the response against the same parser the agent uses
  (`parseDesktopAction` in `node/src/services/desktop-policy.mjs`).
- Pass the parsed action to `desktop.act()` with `approved: true`
  (because the operator has approved the policy hook's invocation
  by configuring it; the agent's `desktop_action` tool is NOT
  this hook — the agent goes through its own approval flow).

### 2. The agent's `desktop_action` tool
The agent tool already routes to `desktop.act()`. The policy hook
sits IN FRONT of the agent tool for model-driven proposals (future
work, Phase D per `aide-desktop-control-cipher`). The current
`desktop_action` tool is operator-driven or scripted — the model
drafts a proposal, the operator approves, the tool runs. No change
needed to enforce in-house-only here; the change is in the policy
hook above.

### 3. The telegram /ask brain
`telegram-brain.mjs` builds a system prompt and calls the
cipher-first model. The current `resolveEngineChat` already prefers
cipher. The new rule: when the request is a desktop action, the
brain MUST resolve to cipher even if a cloud model is in the
operator's route config. Add an explicit guard at the call site.

   the model never sees the snapshot leave the box.

3. **Operator-owned training data.** Every action the house model
   proposes, every operator verdict (approve/reject), and every
   refusal-recovery row becomes training data for the next fine-tune.
   Cloud models don't return their inference traces; the in-house
   model does. The corpus compounds. A cloud provider's model is
   fixed at the moment of last training; the house model is alive.