---
title: Omniscio Control (how an AI agent changes the app live)
---

# Omniscio Control (bundled skill — how AI agents change Omniscio live)

## What it is

### What it is

**Omniscio Control** is a bundled Claude Code skill that teaches any AI agent — Claude Code, ChatGPT with file access, the in-app chat panel, an external Claude session you spawn yourself — how to read and change a running Omniscio install **without editing files on disk or restarting the app**.

It is installed into `~/.claude/skills/omniscio-control/` automatically on every Omniscio launch (no opt-in, no setup). Any Claude Code session you spawn — from Omniscio or from any other repo — sees the skill in its toolbelt and reaches for it when the user asks Omniscio-related questions.

### The problem it solves

Before this skill existed, when a user asked an AI to "turn off Focus Mode" or "create a cron job to run every weeknight at 9 PM", the AI would do one of three things, all wrong:

1. **Write to `config.json` on disk** — Omniscio's main process holds settings in memory; a disk edit changes nothing until restart, and then Omniscio's `safeStorage` re-encryption can clobber the change on the next save.
2. **Try to spawn a fresh Omniscio and shove flags in** — Omniscio's single-instance lock blocks the second process; the request silently fails.
3. **Tell the user "I can't do that — change it manually in Settings"** — the user has to walk a non-programmer through the UI.

Omniscio has had a localhost HTTP control server on `127.0.0.1:19519` for over a year (bearer-token-auth, JSON-in / JSON-out, hundreds of routes across dozens of surface areas — enumerated in the lint-checked `endpoint-index.md`). The AI just didn't know it existed, and even once it did, it had a hard time **discovering** which of the many routes served a given request. Omniscio Control is the missing manual — and, as of the 2026-05-29 redesign, a manual built so the agent can always find the piece it needs and never has to say "there's no tool for that."

### The discoverability redesign (2026-05-29)

The earlier skill had a hub plus ~10 surface files, and its routing table only named about half the surfaces the server actually exposed. An agent asking "how do I manage a Drip?" or "is there a heap-snapshot endpoint?" would dead-end, because those surfaces had no doc and the hub didn't mention them. The redesign closes that hole with a **rigid three-layer architecture** and **two CI lints that keep it honest**:

- **100% route coverage.** Every one of the server's routes — including power, diagnostic, feature-flagged, and kill-switch-gated ones — now has a documented home.
- **A master fallback.** `endpoint-index.md` lists the routes in one searchable table, so even when the hub's intent router doesn't obviously name a surface, the agent has a last-resort lookup that is machine-checked against the served route set — every route is either documented as a row or named on the inherited-debt list, so a route missing from the table is not proof the server lacks it.
- **It can't silently drift.** Two lint tests fail the build the moment the docs and the server disagree (see "Self-maintaining" below).

### The category discovery layer (2026-06-23)

The 2026-05-29 redesign fixed **navigation** — once the agent opens the skill, it can always find the right surface. But **triggering** stayed weak: all its surfaces sat behind one umbrella `description`, so on an _implicit_ ask ("remind me at 3pm", "what is my other session doing") the model often never connected the request to Omniscio at all — it answered in chat or reached for a generic skill. One description is a single trigger surface competing against ~80 other skills.

The fix adds a thin **discovery layer** on top of the hub — seven sibling "category" skills, each a short overview with its own sharp, phrasing-rich `description` (the real trigger surface) that routes straight to the hub's spokes:

- **omniscio-sessions** — sessions, projects, tags, cross-session messaging, spawn.
- **omniscio-scheduling** — cron, alarms, drip ("remind me at 3pm", "every morning run X").
- **omniscio-automation** — automations, recipes, away-mode, Nighty Tidy, OpenClaw port.
- **omniscio-content** — tasks, mind maps, whiteboards, KMS notes, scratchpads, saved prompts, bookmarks, AI coaching.
- **omniscio-settings** — settings, keybindings, app control, running apps, plugins, browser logins.
- **omniscio-inbox** — alerts, the approval queue, quick replies, email-summarizer, SMS, intake sources, feedback.
- **omniscio-observe** — status/search, UI pointing, diagnostics, share, convert, Google APIs, Team Time.

Each category skill is **thin** — it owns no endpoints; it names `omniscio-control` for the shared mechanics (auth, the approval model, errors) and links directly to the relevant `../omniscio-control/<spoke>.md` docs. The spokes stay in one place (DRY). All eight skills install together as one bundle (`cliSkillIds`).

The same change rebalanced the **Omniscio awareness preamble** (`AMC_AWARENESS_SYSTEM_PROMPT`, injected into every Omniscio-spawned session): it now names the seven categories and drops the old "entirely optional … never just because the capability exists" tail that measurably discouraged invocation — restraint is now a light "judiciously, not on every turn." See [amc-session-awareness.md](amc-session-awareness.md).

**Self-maintaining (a third lint).** [tests/unit/lint/amc-control-category-skills.test.ts](/tests/unit/lint/amc-control-category-skills.test.ts) derives the category set from the manifest's `cliSkillIds` and asserts every category skill has valid frontmatter, links only real surfaces (no dead links), and that the **union of all category references covers every omniscio-control surface** — so a new spoke can never be added without being routed into a category, and no surface can silently fall back behind the umbrella description.

## Where to find it

There is no screen for it. The skill lives with your Claude Code skills and is loaded by an agent that needs to act on the app; what you see is the result of what you asked for in chat.

## How it behaves

### How to use it

The skill is **agent-facing**, not user-facing — most users never see it directly. The user just talks to their AI in plain English:

- "Turn off the typing-indicator setting in Omniscio."
- "Pause my Claude session called 'inbox triage' and snooze it until 9 AM tomorrow."
- "Set up a Drip that releases one of these 30 articles into my inbox every morning."
- "Subscribe this project to nightly Nighty Tidy audits, lint in report mode."
- "Publish this markdown file as a share link."
- "Have the research session tell the writing session what it found."
- "Show me all the pending Omniscio approvals in my inbox and tell me what each one would do."

The AI loads the `omniscio-control` skill, reads the hub (`SKILL.md`), routes to the matching surface file via the "Pick your surface" table, and — if the intent isn't obvious — searches `endpoint-index.md`. Then it makes the HTTP call.

### Spawning into the cloud

**A cloud session is the same call with one more field.** `POST /project/:name/new {"prompt": "…", "location": "cloud"}` — `location` takes `"local"` or `"cloud"`, and **omitting it is today's local spawn, byte for byte**. You do not need `/fanout` for a single cloud session; `/fanout` is the bake-off road (one prompt across many projects) and it accepts the same `location` per target.

**Two things that once sent an agent the wrong way** (it concluded "the cloud feature is off and the spawn API can't express it", and spawned locally):

- **`GET /cloud/preference` → `{"on": false}` does NOT mean cloud sessions are off.** That switch chooses where agent **checks** run — the test/build offload. Cloud **sessions** are a separate feature. The answer now carries a `governs` line saying exactly that, so the scope is readable from the payload rather than from documentation.
- **While cloud sessions are dark, `location: "cloud"` is inert** — the session launches locally, exactly as it did before the feature existed. That is deliberate (contract `purely-additive`), not a silent failure. A refused cloud launch names its reason; read the refusal instead of concluding the feature is switched off.

**Spawning into the cloud is still a billable spawn** — the same explicit per-request authorization rule above applies.

### What "live" means

- **Cosmetic / personal-preference changes apply immediately**: keybindings, cosmetic project edits, project creation, sidebar reordering, per-session tag apply/unapply, quick replies, bookmarks, alarms, Team Time, the Tasks outliner, Drip item management, share publishing, feedback, UI highlight, app reload, and **session lifecycle** (pause / unpause / snooze / archive — benign + reversible; re-gate via Settings → CLI Control → Approval requirements). The Omniscio UI reflects the change within a renderer tick.
- **Anything that affects the inbox, billing, your data, or other agents lands in your Approvals inbox first**: settings, cron jobs, automations, recipes (authoring + per-run), project deletion / docs / bug-intake, away-mode rules, the tag library, Drip + Drip-source CRUD, email-summarizer rules, AI-coaching artifact edits, and arming a bug-intake source. Each shows up as a card in `Inbox → Approvals`.
- **Some routes spawn real Claude processes (cost money)** — `/project/:name/new`, `/recipes/run`, `/recipe/configs/:id/run`, `/nighty-tidy-2/.../run-now`, `/ai-coaching/interviews`, `/agent/sessions`. The skill tells every agent to call these **only** on explicit per-request user authorization, then **verify the result**.
- **Either way, you never restart Omniscio.** The change is in memory and on disk at the same moment.

### Auth — there is one bearer token, the AI fetches it itself

Every CLI control request (except a handful of health / open-read / UI-nav routes) needs `Authorization: Bearer <token>`. The token lives DPAPI-encrypted in the user's Windows vault as `amc-cli` and is auto-delivered to `~/.amc/cli-token` (mode 0600) on every Omniscio launch. The skill tells every agent the same fetch order:

1. **DPAPI vault first** — `powershell.exe -NoProfile -File "$HOME/.claude/secrets/get-secret.ps1" amc-cli` (the source of truth on this machine).
2. **Dotfile** — `~/.amc/cli-token` (a single UTF-8 line).
3. **Environment variable** — `$AMC_CLI_TOKEN`.

The skill never echoes the token in chat, and tells the user to rotate via **Omniscio → Settings → CLI Control → Regenerate** if a token ever leaks. (After a rotation the dotfile updates but the vault does not — the skill documents the one-line vault re-set.)

### Identify your session — `X-AMC-Source-Session-Id`

Alongside the bearer token, an Omniscio-spawned agent sends `-H "X-AMC-Source-Session-Id: $AMC_SESSION_ID"` (Omniscio injects `AMC_SESSION_ID` into every spawned agent's environment). This makes every action and spawn traceable back to the calling session — an inbox action reads "Generated by \<your session>", and a session you spawn opens with a clickable "Spawned by" note. It is a trace hint, not a security boundary. **For session spawns it is required by default:** with the `requireSpawnSourceSession` setting on (the default), `POST /project/:name/new` and `POST /agent/sessions` refuse a spawn that carries no source with HTTP `400`. A non-session caller (a manual script with no `$AMC_SESSION_ID`) either sends a real session id or has the user turn the setting off in **Settings → CLI Control**.

### Rate limits

Shared across all endpoints, all surfaces:

- **10 mutations per minute** per token (returns `429`).
- **60 reads per minute** per token hash.
- **20 pending approval items** in the inbox at any time (the queue cap — once full, every new approval-gated request returns `409`).

A few surfaces have their own buckets (cross-session messaging 120/hour; agent-sessions 10 spawns/hour; UI snapshot 60/min), documented in their spoke files.

### Trigger keywords

The skill's `description` field lists every common phrasing — change/toggle/set a setting; pause/snooze/archive/spawn/message a session; create/edit/run a cron job, automation, recipe, drip, audit, tag, project, bookmark, alarm, keybinding; manage quick replies, Team Time, email-summarizer rules, intake sources, AI coaching, shares, the Tasks outliner; file feedback; read status/search/state; point at the UI; diagnostics. If the user's request mentions Omniscio or maps onto any Omniscio capability, the skill loads, the hub routes, and `endpoint-index.md` backstops anything the router doesn't name.

### Where it does not apply

The skill covers **Omniscio's own state** — its settings, data, inbox, scheduled work, and UI. It does **not** spawn arbitrary Claude sessions on its own initiative; the billable routes are called only with explicit per-request user authorization in the current chat, and even then the agent never echoes the bearer token. For the inbox approval flow the user sees, see [cli-pending-actions.md](cli-pending-actions.md).

## For agents

### How it works

### Three-layer hub-and-spoke structure

```
~/.claude/skills/omniscio-control/
  SKILL.md            ← Hub. Golden rule, auth, approval model, rate limits,
                        idempotency, error table, and a "Pick your surface"
                        intent router covering ALL 42 surfaces.
  endpoint-index.md   ← Master fallback. One row per documented route (2,382
                        of 2,556 served, measured 2026-09-15): method, path,
                        surface doc, auth, gating, one-liner. Searchable. The
                        rest are named in tests/unit/lint/amc-control-doc-debt-
                        baseline.ts, so a route absent here may still be served.
  <42 surface docs>   ← One flat .md per surface (settings, cron, drip,
                        nighty-tidy, share, ai-coaching, diagnostics, …).
                        Each: "Read SKILL.md first" backlink + ## Endpoints
                        table + workflow + errors.
```

The 42 surfaces: read-and-status, sessions, sessions-spawn, agent-sessions, cross-session-messaging, projects, settings, settings-catalog, cron, automations, recipes, away-mode, keybindings, tags, quick-replies, alarms, team-time, bookmarks, tasks, tasks-v2, drip, alert, email-summarizer, nighty-tidy, intake-sources, ai-coaching, share, google-apis, feedback, ui-discovery, discover-values, app-control, merge-priority, diagnostics, pending-actions, kms, mindmaps, plugins, sms, scratchpads, openclaw-port, golden-paths.

The agent loads the hub first, follows one link to the right spoke, and falls back to `endpoint-index.md` only when the intent is ambiguous. Two hops, maximum, from cold start to the exact endpoint.

### Folded-in legacy skills

The redesign folded three previously-standalone skills into omniscio-control so there is one place to look:

- `amc-cli-auth` → its token-fetch + rotation + "dotfile missing despite CLI control on" troubleshooting now lives in the hub's **Authentication** section.
- `amc-ui-discovery` → became **ui-discovery.md** (`/ui/snapshot`, `/ui/highlight`, `/ui/highlight/clear`).
- `amc-share-publish` → became **share.md** (`/share/*` plus `/gdoc/publish`).

### Self-maintaining — two CI lints keep the docs honest

This is what makes the master index trustworthy enough to rely on:

- **[tests/unit/lint/amc-control-route-coverage.test.ts](/tests/unit/lint/amc-control-route-coverage.test.ts)** scans the server source (`src/main/services/cli/cli-server*.ts`) for every registered route — including the lifecycle `ROUTE_TABLE` template and the `url ===` / `parseCliPath` special cases — and asserts that each one is **either** documented as a row in `endpoint-index.md` **or** named on the inherited-debt list in [amc-control-doc-debt-baseline.ts](/tests/unit/lint/amc-control-doc-debt-baseline.ts). Add a route and forget to document it, and the build fails naming the exact missing route and the route family's `.claude/endpoint-descriptions/endpoint-descriptions-<family>.json` entry to add (the family files live beside the skills tree, not inside the skill — Claude Code walks every file under a skill at every turn start). **The table itself is generated** (`npm run endpoint-index:reindex`) from that sidecar plus a derived Auth column, so a hand-added row is reverted by the next reindex. Document a route that no longer exists, and it fails naming the phantom row. That debt list is a hand-maintained constant that is meant to only shrink — nothing pins its size, so a deliberate line on it is the one legal way for a served route to stay undocumented. It also carries a **self-alerting guard**: the moment a developer adds a new `url === '/foo'` branch or a new `parseCliPath` action the scanner can't see, the guard fails and tells them to register it.
- **[tests/unit/lint/amc-control-doc-structure.test.ts](/tests/unit/lint/amc-control-doc-structure.test.ts)** pins the architecture bidirectionally: every surface the index references exists as a flat top-level `.md`; every surface doc is referenced by the index (no orphans); every surface doc is linked from the hub (reachable in one hop); every hub link resolves; the hub wires the `endpoint-index.md` escape hatch; and every spoke carries the house-style minimum (an `## Endpoints` heading + a `Read [SKILL.md](SKILL.md)` backlink).

Together they mean a future developer **cannot** add a CLI route, rename one, or remove a spoke without the docs being forced back into agreement — the failure messages tell them exactly what to fix.

### Bundled-skills installer

On every launch, Omniscio's main process calls `ensureBundledSkills()` from [/src/main/services/bundled-skills-installer.ts](/src/main/services/bundled-skills-installer.ts). The installer reads every **top-level** `*.md` file in the source dir (subdirectories are intentionally skipped — which is why the architecture is strictly flat), hashes them, and syncs to `~/.claude/skills/omniscio-control/` only when the hash changes. A `.amc-managed.json` marker records `{ installedBy, version, installedAt, sourceHash }`; a foreign marker (user hand-edit) is left untouched. The opt-out is `Settings → Workflow → Sync skills to Claude Code` (defaults to on). The omniscio-control manifest in [/src/shared/integrations/omniscio-control.ts](/src/shared/integrations/omniscio-control.ts) (`cliSkillIds: ['omniscio-control', 'omniscio-sessions', 'omniscio-scheduling', 'omniscio-automation', 'omniscio-content', 'omniscio-settings', 'omniscio-inbox', 'omniscio-observe']`, `llmDocPath: 'docs/llm-library/omniscio-control.md'`) is what registers the hub **and** the seven category skills — all eight install together as one bundle.

**The hub can never be archived on its own.** The seven category skills route into the hub's pages, so the manifest names it in `requiredSkillIds: ['omniscio-control']`. Every remove path refuses it (the Skills manager shows *"Needed by 7 other Omniscio skills, so it can't be removed"*, the Skill Overhead card never offers it, and `DELETE /skills/:id` / `POST /skills/bloat/archive` answer `400` before any approval card is queued), and the installer ignores an old archive entry for it — a machine that archived it before this rule existed gets the whole current hub back on its next launch, along with a working `amc-control` stub in place of any broken leftover link. Background: bug report 5fd2eaf0, where an archived hub left all seven companions linking into nothing for about ten days. Rules: [bundled-skill-feature-gating-contract.md](/.claude/memory/contracts/bundled-skill-feature-gating-contract.md).

### Approval model — what the user actually sees

When an AI sends an approval-gated request (say `PATCH /settings/typingIndicatorEnabled` with `{ value: false }`):

1. The server inserts a `cli_pending_actions` row (`actionKind`, `targetId`, `payload`, `previewText`, `clientRequestId`, `status: 'pending'`).
2. It returns `202 Accepted` with the new row id.
3. The renderer reloads and shows an inbox card: "Set 'Typing indicator' to 'off'?" with Approve / Reject.

On Approve, Omniscio re-validates the payload against the live Zod schema and **then** applies it — even a tampered DB row must pass the live check. In-app sessions (Omniscio's own session token) get a bypass: `200 OK { applied: true }` flat, no inbox round-trip. This never fires for external agents.

### Idempotency — `X-Client-Request-Id`

Every approval-gated mutation accepts a client-supplied request id (`clientRequestId` in the body, or the `X-Client-Request-Id` header for bodyless routes). A second call with the same id for the same `action_kind` within 30 days returns the existing pending row instead of queuing a duplicate — so a network-hiccup retry is safe.

## Related

- [cli-control.md](cli-control.md) — the local control server the skill drives.
- [agent-tools.md](agent-tools.md) — the rest of what an agent session can reach.
- [agent-permission-level.md](agent-permission-level.md) — the gate that decides how much an agent may do before it asks.

