---
title: Agent input modal (a session asks you to fill a value it must never see)
---

# Agent input modal (a session asks you to fill a value it must never see)

## What it is

Some things an AI session genuinely cannot do for you — the clearest example is
**setting an API key**, because the key is a secret the AI must never see. Before this
feature the AI could only say "open Settings and paste it yourself." Now, when a session
is blocked on a value only you can provide, it makes a **well-labeled modal pop up right
in the app**: you type the value, press Enter, and it saves exactly as if you'd entered
it in Settings. **The AI never sees what you typed** — it only learns whether you saved
or cancelled.

### What you see

On desktop a small **floating window** opens — you can drag it aside (even to another
monitor) and click back to the app without it disappearing; on a phone or browser it's an
in-app pop-up instead. Either way it shows:

- The session's own message (why it needs the value).
- **"Saving to: <Setting>"** — the app's own label for exactly which setting the value
  fills (e.g. "Gemini API key"). This label is written by the app, not the session, so a
  session can never disguise where your input goes.
- **Who's asking** — the name of the session making the request, shown as a link. On
  desktop, clicking it brings the app forward and opens that session while the prompt
  window keeps floating on top — so you can read the session and still answer.
- A text box — masked with a show/hide toggle when the value is a secret (an API key),
  or a plain box for a non-secret setting.
- **Save** (or press Enter) and **Cancel**.

The prompt **never steals your focus** — the floating window appears without pulling you
out of what you're doing, and it stays visible (and put) through clicking away, switching
apps, or moving to another monitor. It closes only when you **Save** or **Cancel**, close
its window (which counts as Cancel), or the request resolves / times out. Only one prompt
shows at a time; if two sessions ask at once, they queue into the same window.

**It shows up right away, and it tells the truth when it can't.** The window appears the
moment it is created rather than waiting for its contents to finish drawing, and it loads
its own small page instead of the whole app — so it is not something you wait several
seconds for, even on a brand-new install. If the window genuinely cannot draw (a failed
load, a crashed renderer), Omniscio retries once and then answers the waiting session
`unavailable` within seconds instead of leaving it to sit out the five-minute timeout.
And the key box no longer waits for your settings to load first: a slow or failed settings
read used to leave the prompt on a spinner you could never type into — that was the "it
showed up late and then didn't work" case on a fresh install.

## Where to find it

The prompt comes to you rather than the other way round — a session raises it over the app when it needs a value, as a floating window on the desktop or an in-app pop-up on a phone or browser. The only part of it that lives in Settings is the switch that turns the capability off.

### Turning it off

It's **on by default**. To disable it, turn off **Settings → Features → "Allow agents to
ask you for a value."** When off, any such request is refused.

## How it behaves

### How it saves

You type the value and press Enter. It's saved through the **exact same path** as a
manual Settings save — encrypted at rest for credentials, validated the same way, and
verified as actually stored before the session is told "saved." The value travels from
the box straight into the app's own storage; it is **never sent back to the AI** and
never written to a log.

### Checking an API key before it saves

For an **AI-provider API key** — Anthropic, OpenAI, Gemini, OpenRouter, DeepSeek, Kimi,
GLM, MiniMax, or Meta — the modal **checks the key live against the provider before it
saves**, so a typo'd or dead key is caught right there instead of being silently stored:

- **Valid** → it saves normally.
- **Rejected by the provider** → the save is blocked and the modal tells you the key was
  rejected, so you can re-paste it — and it also offers a **Save anyway** button. A
  provider's rejection is one answer from a remote service and can be wrong (seen on
  2026-09-16: a key that worked on every direct check was refused), so you are never left
  stuck. Editing the value clears that offer, so it can only ever override the key it was
  raised for.
- **Provider unreachable** (a network blip, or the service is busy) → the modal shows the
  same **Save anyway** button, so a momentary outage never traps a key you know is good.

This reuses the exact live check the Settings API-key fields use. A target that isn't an
AI-provider key (an integration token, or a non-key setting) just saves as before, with
no check.

## For agents

### For agents (how to trigger it)

A session asks by calling the local CLI control server:

- `POST /input-request` with `{ target, title, description?, secret? }` — `target` is a
  settable string setting (discover valid ones with `GET /input-request/targets`). It
  pops the modal and returns `{ requestId, status }` after a bounded wait of **up to 10
  seconds**.
- `GET /input-request/:requestId` — poll for the outcome (`saved` / `cancelled` /
  `timeout` / `unavailable`). **The status never carries the value.**
- Send an `X-Client-Request-Id` header so a retry doesn't pop a second modal.

**Limits.** One calling source may create at most **30 requests a minute**; past that the
request is refused with `429` and no modal is shown, so a loop cannot bury the person in
popups. The two waits are deliberately different, and a `pending` from the create call is
**not** a failure: the create call waits a **short 10 seconds** for the human to answer —
long enough to catch a fast reply, short enough not to hold the caller — and the separate
poll waits up to **90 seconds**. A slow human therefore shows up as `pending` from the
create and is picked up by the poll; only the poll's own window ending is a `timeout`.

The value is submitted by the human on a **desktop-only, human-only** channel that no
agent path can reach — an agent can _ask_, but only a person can _answer_. For a secret
target the value is redacted on every settings read too, so the agent can never retrieve
it afterward; a non-secret setting is readable like any ordinary setting. Full route
details live in the `omniscio-control` skill's `agent-input.md` spoke; the invariants are
locked in the `agent-input-contract.md` contract.

## Related

The settings a prompt can fill are the ones [API keys](api-keys.md) describes, and the route an agent calls to raise one is part of the local server documented in [CLI Control](cli-control.md). Where this modal lets a session ask a person for a value, [Agent-driven sessions](agent-driven-sessions.md) covers the reverse direction — an external AI asking for a whole session, approved through the inbox.
