Agent-Driven Sessions (External AI Drives an Omniscio Session)
An external AI can ask Omniscio for a brand-new session over HTTP, get one approval through the inbox, then drive that session through bounded multi-turn dialog under turn and dollar caps, with a revoke control always available in the session header.
What it is
Omniscio lets an external AI agent — Claude.ai in a browser, ChatGPT on your phone, a Claude Code session in another project, or any tool holding the Omniscio bearer token — request a brand-new Omniscio session over HTTP, get your one-time approval through the inbox, and then drive that session through multi-turn dialog under turn and dollar caps. The agent posts the initial prompt, polls for approval, sends follow-ups, reads replies, and ends the session voluntarily — all without you touching the keyboard except for the single approval click.
This is intentionally different from the other two ways Omniscio lets external AIs act on your data. Recipe agent-triggering (agent-trigger-recipes.md) is one-time pre-approval: you flip a per-recipe toggle, and any agent can fire that exact recipe forever. Cron / automation CLI authoring (recipes-cli-authoring.md) is per-fire approval-gated: every job lands in the inbox before it runs. Agent-driven sessions sit in the middle: one approval per session creation, then turn-bounded multi-turn dialog with revoke-anytime. After approval, the agent has up to N follow-up turns and $M of spend before Omniscio stops accepting new turns; you can revoke at any moment from the session header.
One caller never sees that card: an Overseer. A worker spawn asked for by a running Overseer (agentType set, source __overseer__:<slot>) is applied on the spot — standing that Overseer up was the approval, so asking again would only put your own decision back in front of you on a card the Overseer can never clear itself. It is still fully attributed, rate-limited and capped. Nothing else changes: an Overseer whose slot you switched off, or a session you paused, gets no exemption, and every other caller still queues for a click. (overseers.md)
The whole feature is off by default. The user opts in at Settings → CLI Control → Agent-Driven Sessions. Until that toggle is on, every endpoint described below returns HTTP 503 with {"ok": false, "error": "agent-driven sessions disabled"}.
Where to find it
User-visible flow
Agent posts a spawn request. The HTTP call lands as a row in your Omniscio inbox under CLI Pending Actions. The card shows the project name, the agent's initial prompt (collapsed if longer than 300 characters), and the proposed caps (e.g. "20 turns / $1.00"). You see Approve and Reject buttons.
You approve (or reject). On Approve, the dispatcher spawns a normal Omniscio session in that project's working directory, seeded with the agent's initial prompt as the first user turn. On Reject, the agent's status poll returns
{"status": "rejected"}and the request closes. If you ignore the row for 7 days, it auto-expires and the polling agent gets{"status": "rejected", "reason": "expired"}.Session appears in a new sidebar group. Approved agent-driven sessions surface in a dedicated Agent-driven group at the top of the project sidebar — above the projects list, separate from your normal sessions. Sessions leave the group automatically when they reach a terminal status (paused, ended, error, archived).
Header shows an "Agent driving" pill. While the agent is in control, the session header shows an accent-colored pill reading "Agent driving (N/M turns)" alongside a red "Revoke control" button. The pill counts every follow-up turn the agent has sent. Once you revoke, the pill flips to a muted "Agent control revoked" badge.
Agent drives the conversation. Behind the scenes, the agent polls
GET /agent/sessions/:id/messages?since=Nfor new replies, then callsPOST /agent/sessions/:id/follow-upto send the next turn. Omniscio bills exactly like any other session — the cost shows up in your usage bar.Revoke whenever you want. Clicking "Revoke control" stops the agent immediately. Subsequent follow-ups return HTTP 403 with
terminal: trueso the agent stops polling. Importantly, revoke does NOT end the session — the conversation stays open inrunningstate and you can take over manually by typing in the composer.
Settings reference
All four settings live at Settings → CLI Control under the "Agent-driven sessions" subsection. Field names below are the persisted keys in config.json — see src/shared/types.ts for the type definitions.
| Setting | Default | Range | What it does |
|---|---|---|---|
agentDrivenSessionsEnabled |
false |
bool | Master feature flag. When off, every /agent/sessions/* endpoint returns HTTP 503. Routes are still wired but inert. |
agentDrivenMaxTurnsPerSession |
20 |
1-200 | Ceiling on follow-up turns for a single session. Not enforced today — the value is stored and shown back to you, but nothing stops a session at the count. |
agentDrivenMaxDollarsPerSession |
1.00 |
0.01-20 (USD) | Ceiling on cumulative cost per session in dollars. Not enforced today — the value is stored and shown back to you, but nothing stops a session at the amount. |
agentDrivenMaxConcurrent |
3 |
1-10 | Global concurrency limit across all active agent-driven sessions. New spawns are refused at dispatch time once this many sessions are running. |
How it behaves
Caps and safety
- Per-session turn cap (1-200, agent picks at spawn time, default 20). Atomic increment on each follow-up. When the cap hits, the next follow-up returns HTTP 429 with
{"error": "cap_reached", "cap": "turn", "limit": N, "used": N}AND a system message is inserted into the session conversation so you see why the agent stopped. - Per-session dollar cap (1-2000 cents, agent picks at spawn time, default 100 = $1.00). Pre-flight check before each turn; same 429 + system message pattern when hit.
- Machine-wide concurrency cap (default 3, configurable 1-10). Enforced at dispatch time — if approving a request would exceed the cap, the spawn is rejected and the inbox row is marked failed.
- Per-kind subcap on the inbox queue — at most 3 simultaneously-pending agent-spawn requests. The 4th returns HTTP 429 with
too many pending agent-spawn requests. - Per-token rate limit — 10 spawn requests per trailing 60 minutes, per bearer-token hash. Sliding window, returns HTTP 429 with a
Retry-Afterheader when hit. - In-flight serialization — at most one follow-up per session at a time. Concurrent calls get HTTP 409 with
turn_in_flight. Lock auto-releases after 60 seconds as a safety net. - Body size caps — initial prompt 100 KB, follow-up prompt 32 KB. Oversize returns HTTP 413.
Revoke behavior
Clicking "Revoke control" on the session header sets agent_driven_sessions.revoked_at to the current timestamp. From that moment forward:
- Follow-ups return HTTP 403 with
{"error": "revoked", "terminal": true}. Theterminal: trueflag is the agent's signal to stop polling — there is no recovery path; the user must spawn a fresh session if they want the agent back. - Messages endpoint still works but returns
revoked: trueandterminal: trueso the agent can drain any final replies before exiting. - The session stays in
runningstate. Revoke does NOT pause or archive the session. You keep the conversation, take it over manually by typing in the composer, or archive it yourself when you're done. - The header pill flips from the accent-colored "Agent driving" + red "Revoke control" pair to a muted gray "Agent control revoked" badge.
The "revoke ≠ end" split is deliberate. End is for the agent to release voluntarily ("I'm done"); revoke is for you to override ("I'll take it from here"). Both leave the conversation history intact.
Error codes
| Status | error field |
Meaning | What the agent should do |
|---|---|---|---|
| 400 | validation |
Body failed Zod validation, OR projectId is unknown. |
Read the error / detail fields. Fix and retry. |
| 400 | (no source session) | requireSpawnSourceSession is on (default) and no X-AMC-Source-Session-Id header was sent. The error text names the fix. |
Send the header ($AMC_SESSION_ID), or have the user turn off "Require a source session for command-line spawns" in Settings → CLI Control. |
| 401 | Unauthorized |
Missing or wrong bearer token. | Re-read ~/.amc/cli-token. If still failing, tell the user to regenerate via Settings → CLI Control. |
| 403 | revoked |
Follow-up against a session the user revoked. Response includes terminal: true. |
Stop polling. There is no recovery — the user must initiate a new spawn. |
| 404 | not_found |
Unknown sessionId or requestId. | Don't retry. The session may have been archived or never existed. |
| 409 | session_state |
Follow-up against a session in a terminal state (paused / ended / error / archived). | Don't retry. The session is closed. |
| 409 | turn_in_flight |
Another follow-up is already running for this session. | Wait briefly (1-2 s) and retry — the lock auto-releases when the prior turn completes. |
| 409 | (queue full) | Total pending CLI actions hit the global cap (covers cron + automation + sessions + everything). | Tell the user to clear the inbox queue. |
| 413 | body_too_large |
Initial prompt > 100 KB or follow-up prompt > 32 KB. | Trim the prompt and retry. |
| 429 | rate_limited |
More than 10 spawn requests in the trailing 60 minutes for this token. Includes Retry-After header. |
Honor the header and back off. |
| 429 | cap_reached |
Turn or dollar cap hit. Response includes cap: 'turn'|'dollar', limit, used. |
Stop sending follow-ups. The user must extend the cap or revoke and respawn. |
| 429 | (per-kind subcap) | More than 3 pending agent-spawn requests in the inbox. An Overseer's spawns never queue, so they never count toward this cap. | Tell the user to approve or reject existing rows first. |
| 503 | agent-driven sessions disabled |
Feature flag is off. | Tell the user to enable it at Settings → CLI Control → Agent-Driven Sessions. |
For agents
How to use it (HTTP)
All five endpoints require the Omniscio bearer token. Read it from ~/.amc/cli-token (Windows: %USERPROFILE%\.amc\cli-token). The auth pattern matches the rest of CLI Control — see the omniscio-control skill.
1. Enqueue a spawn request
TOKEN=$([ -n "$AMC_CLOUD_SESSION_BOX" ] && printf '%s' "$AMC_CLI_TOKEN" || cat ~/.amc/cli-token)
curl -s -X POST http://127.0.0.1:19519/agent/sessions \
-H "Authorization: Bearer $TOKEN" \
-H "X-AMC-Source-Session-Id: $AMC_SESSION_ID" \
-H "Content-Type: application/json" \
-d '{
"projectId": "proj_abc123",
"initialPrompt": "Audit the imports in src/ for unused exports and report findings.",
"turnCap": 20,
"dollarCapCents": 100
}'
The X-AMC-Source-Session-Id header identifies the session making the request (Omniscio-spawned agents have their id in $AMC_SESSION_ID), so the spawned session links back to its origin with a "Spawned by" note. It is required by default: with the requireSpawnSourceSession setting on (the default), a spawn with no source is refused with HTTP 400. A non-session caller must send a real session id or have the user turn that setting off in Settings → CLI Control.
Response (HTTP 200):
{ "ok": true, "requestId": "cli_pending_xyz", "status": "pending" }
Body fields:
projectId(required) — UUID of the Omniscio project the session will run in. Get it fromGET /status.initialPrompt(required) — the first user turn. Capped at 100 KB.turnCap(optional, integer, 1-200, default20) — hard ceiling on follow-up turns. The agent gets this many turns total.dollarCapCents(optional, integer, 1-2000, default100) — hard ceiling on cumulative cost in cents. 100 = $1.00.
2. Poll for approval status
curl -s -H "Authorization: Bearer $TOKEN" \
http://127.0.0.1:19519/agent/sessions/request/cli_pending_xyz
Three response shapes:
{ "ok": true, "status": "pending" }
{ "ok": true, "status": "approved", "sessionId": "ses_abc789" }
{ "ok": true, "status": "rejected", "reason": "rejected by user" }
Once you see approved, capture the sessionId and switch to the follow-up + messages endpoints. Poll politely — every 2-5 seconds is fine.
3. Send the next turn
curl -s -X POST http://127.0.0.1:19519/agent/sessions/ses_abc789/follow-up \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "prompt": "Now check tests/ for skipped specs." }'
Response (HTTP 200):
{ "ok": true, "turnsUsed": 3 }
Only one follow-up may be in flight at a time. A second concurrent caller gets HTTP 409 with {"error": "turn_in_flight"}.
4. Read new messages
curl -s -H "Authorization: Bearer $TOKEN" \
"http://127.0.0.1:19519/agent/sessions/ses_abc789/messages?since=0"
Response (HTTP 200):
{
"ok": true,
"messages": [{ "rowid": 12, "source": "agent", "text": "...", "createdAt": "..." }],
"cursor": 12,
"sessionState": "running",
"revoked": false,
"terminal": false,
"turnsUsed": 3,
"dollarsUsedCents": 47
}
since is a rowid cursor — pass back the prior response's cursor value on the next poll to get only newer messages. The query caps at 500 rows per call. terminal: true means the agent should stop polling: it's set when the user has revoked control or when the session reached ended / error / archived status. (A paused session is not terminal here — the user can resume it, so polling should continue. Follow-up sends WILL fail with HTTP 409 session_state while paused; resume the polling loop without burning a turn.)
5. End the session voluntarily
curl -s -X POST http://127.0.0.1:19519/agent/sessions/ses_abc789/end \
-H "Authorization: Bearer $TOKEN"
Response (HTTP 200): { "ok": true }. Routes through Omniscio's archive flow — the session moves to archived status and leaves the active sidebar.
Where to look in code
For agents working inside the Omniscio repo:
- HTTP routes — src/main/services/cli/cli-server-agent-routes.ts
- Service (caps, in-flight lock, rate limiter) — src/main/services/agent-driven-session.ts
- Dispatcher arm (approve → spawn) — src/main/services/cli/cli-pending-dispatcher.ts (search
session.spawn.agent_driven) - Zod schemas — src/shared/ipc-schemas.ts (
agentDrivenSpawnPayloadSchema,agentDrivenFollowUpSchema) - DB queries + table — src/main/db/queries-agent-driven.ts, migration v129 in src/main/db/incremental-migrations.ts (search
agent_driven_sessions) - Inbox approval pane — src/renderer/src/features/cli-pending/AgentDrivenSpawnApprovalPane.tsx
- Sidebar group — src/renderer/src/features/dashboard/ProjectsSidebar.tsx (search
Agent-driven) - Header chip — src/renderer/src/features/sessions/AgentDrivenHeaderChip.tsx
- Settings UI — src/renderer/src/features/settings/CliControlSettings.tsx
- Settings types + defaults — src/shared/types.ts (search
agentDriven)
Related
- agent-trigger-recipes.md — pre-approved recipes (one toggle, agent fires forever)
- recipes-cli-authoring.md — recipe authoring via inbox approval (per-fire gate)
- cli-control.md — full CLI Control Server endpoint reference
- cli-pending-actions.md — the inbox queue these spawn requests live in
- omniscio-control skill — bearer-token auth contract
Last verified 2026-09-28