---
title: Plugin AI-Native Sessions
---

# Plugin AI-Native Sessions

## What it is

**Status: shipped (always on).** The `plugin-ai-native-cli` feature landed 2026-08-06 — the plugin
CLI forwarder and the in-plugin session experience below are available in every installation. No Lab
flag gates them, and this surface is safe to document publicly.

The in-plugin session experience: inside every marketplace-plugin surface (GitHub Integration,
Calendar, Newsletter, Smrzz, …) you can see, open, and start first-class AI sessions — "start a
session right there" — and every session started there opens already knowing what the plugin is and
how to drive it through its AI-callable actions.

## Where to find it

It has no sidebar row of its own; it lives inside the plugins you already have. Open a marketplace
plugin from wherever it sits in the app, and look beside the plugin's own screen: a **Sessions
pane** mounts there whenever that plugin declares at least one AI-callable action, carrying the
standard **"+ New session"** affordance, the usual section model, and the right-click management
menu every integration shares. From a paired phone the same sessions are reached the ordinary way —
they appear in the normal sessions list and open as the normal chat.

## How it behaves

The experience is made of five pieces.

### The Sessions pane

When a plugin project is active AND that plugin's OWN manifest declares at least one AI-callable
endpoint (`cli.endpoints`), a pane mounts beside the plugin's webview listing the AI sessions that
live at this plugin, with the standard "+ New session" affordance, section model, and right-click
management menu (the same shared `SessionHostSidebar` every integration uses). A plugin that
declares no `cli.endpoints` (a UI-only plugin, like the former Uplink messenger) never shows the
pane — it is a per-plugin manifest opt-in, not feature-wide. A plugin that DOES declare endpoints
can still decline the pane with `ui.hideSessionsPane: true` — the sibling of `ui.hideProjectPanel`,
for a plugin that owns its full width; both are needed to clear the column entirely, because
declining only one hands the slot to the other surface. Opening a session swaps the webview for the
chat; a "Show <plugin>" button swaps back. On mobile, plugin sessions appear in the normal sessions
list and open as the normal chat.

### First-class visibility — from ANY entry point

Sessions the USER starts under a plugin — the pane's "+ New session" OR the global "+ New" / Cmd+N /
quick-launch (which carry `source='ui'`) — live in the plugin's own project and appear in the
unified Inbox, project badges, and Needs-You counters like any other session. Only sessions the
PLUGIN starts for itself (the v2-worker `ctx.sessions.create` background research spawn,
`source='plugin:<id>'`) stay hidden. The chokepoint decides by a POSITIVE managed-discriminator —
hidden IFF the source is `plugin:<id>` (or provenance is nullish → fail-safe, or the feature is
dark) — NEVER a user-source allowlist (that allowlist was a bug that hid `ui`/quick-launch user
sessions). The archived count AND list apply the same `__plugin__` exclusion (flag-independent) so
the pane header can't drift from the list and managed sessions don't leak into the global Archive
view.

### Purpose-built auto-context (the capability card)

The first message of a plugin-surface session invisibly carries: a short orientation (the agent runs
inside Omniscio; the user is viewing the chat inside this plugin's surface; Omniscio provides the
session, the inbox where approvals land, and the control server), the plugin's identity, its
AI-action list as method-bearing lines (`GET status — description`, `[requires human approval]`
marks reflecting the ask-first rule — any action that is not a declared read or marked `safe`,
whichever key the caller holds, not just a bare `requiresConfirmation` flag), how to call
`http://127.0.0.1:19519/plugins/<id>/cli/<path*>` (bearer from the session's own `AMC_CLI_TOKEN`,
plus the `X-AMC-Source-Session-Id` header) including
one worked curl example synthesized from the plugin's own first read action plus the generic POST
form, what to do when an action needs the user's approval (HTTP 202 + `approvalRequired` → the
request is in the Omniscio inbox; the AI must never claim the action already ran), a scope contract
("you are the <plugin> agent — prefer this plugin's actions; work unrelated to the plugin is out of
scope for this session, suggest a regular Omniscio session"), and the caveat that a CLI call fails
loudly (404 unknown plugin · 409 disabled / worker down) rather than silently doing nothing. Only
the SENT text carries this — the chat bubble shows just what the user typed.

### Suggested-prompt chips (chat-first entry)

A plugin may declare up to 4 curated starters in its manifest — `ui.sessions.suggestedPrompts:
[{ label, prompt }]`. The Sessions pane renders them as chips: front and center in the empty state,
a compact row above the list otherwise. ONE click starts a session with that prompt auto-sent as
the first message — zero setup. A malformed list fails the whole manifest loudly at load; no
declaration means the pane looks exactly as before. An auto-sent prompt gets no special powers: if
the agent immediately calls an action marked `requiresConfirmation`, the call still queues to the
inbox for the human.

### Reach into YOUR OWN projects (opt-in, grant-scoped)

Everything above is about sessions living *at the plugin*. A plugin may also contribute standing
context to sessions you start in **your own repositories** — so a session in `my-app` opens already
knowing that plugin is there and how to drive it. Two things must both be true: the plugin declares
`ui.sessions.realProjectContext: true` in its manifest, **and** you have granted it that specific
project (the same per-project `workspace` grant used for file access). There is **no new permission
and no new toggle** — granting is the opt-in, revoking the grant or disabling the plugin is the off
switch. In an ungranted project the block never appears, which is deliberate: every action it would
advertise there would be refused anyway, so showing it would advertise a lie. Unlike the static
`contextTemplate`, the block is **built by the plugin** — the host calls the plugin's own
single-segment `context` action (host-called only; it is not in the agent-facing action list) and
expects `{ "context": "<the block>" }` back. Rules the host enforces: it is **frozen at spawn** and
rides the whole session, so it should name only stable facts (how to call the plugin, package/test
counts, detected runners) and nothing that changes per run; it gets **~1.5 seconds** and is dropped
if the plugin is slow, not running, or answers oddly — a session never waits on a plugin to launch;
it carries the **same 5,000-character cap** as `contextTemplate`, and over it the block is
**dropped whole, never truncated**; it is **never written into your repository** (no
`.claude/session-context.md` in your git tree); and it runs only when the plugin declares
`GET context` as an unflagged read — it runs automatically as the session starts, with no one
there to approve it, so anything else (a different verb, an undeclared path, or a path flagged
`requiresConfirmation`) is **skipped entirely** rather than run without the approval it would need.
You can see when it fired: the session shows a collapsed "seed-context" divider labelled with the
plugin's name. Several granted plugins compose into one block.

## For agents

### Where the seams live

- Sessions pane: `src/renderer/src/features/plugins/PluginSessionsPane.tsx` +
  `plugin-session-host.ts` + `compute-plugin-chat-active.ts` +
  `compute-plugin-sessions-pane-visible.ts` (the manifest opt-in mount gate:
  active plugin's `manifest.cli.endpoints` must be non-empty AND it must not have
  opted out via `manifest.ui.hideSessionsPane`) — governed by
  `.claude/memory/contracts/session-host-contract.md`.
- Hide-tag managed-discriminator: `src/main/services/session/session-create.ts`
  (`applyHideTags` — hidden IFF `isPluginManagedSpawnSource` / nullish / dark) +
  the shared source classifiers in `src/shared/session-host/types.ts`.
- Archived count/list alignment: `src/main/db/queries-sessions/archive.ts`
  (`buildArchivedWhere` excludes `__plugin__`, shared by count + list) — the SOLE
  `__plugin__` filter now that RT-F004 made the "Archived (N)" total a server value
  (the `useArchivedTotalCount` hook → `SESSION_COUNT_ARCHIVED`); the old renderer-side
  `bumpArchivedTotalCountForSession` delta guard was removed.
- Auto-context (capability card): `src/main/services/session-context/plugin-provider.ts` —
  governed by `.claude/memory/contracts/session-context-provider-contract.md`
  (`plugins-declare-context` + its AI-actions extension).
- Real-project reach (piece 5): `src/main/services/session-context/plugin-project-context-provider.ts`
  (a CONTENT provider — matched by `matches(folderPath)` on a real folder, not by a
  `__plugin_<id>__` sentinel), reached through `listEnabledPlugins` / `dispatchCliAction`
  on `session-context/plugin-registry-accessor.ts` (wired in `src/main/ipc/register.ts`);
  merge rules for several matched providers in `session-context/compose.ts`; the
  manifest bit `ui.sessions.realProjectContext` in `src/shared/types/plugins.ts` +
  `plugin-manifest-validator.ts`. Governed by
  `.claude/memory/contracts/session-context-provider-contract.md` (`content-tier-is-multi-valued` / `suppresses-profile-gates-the-coaching-seed` / `real-project-access-needs-a-workspace-grant`), and
  its refusal of a `requiresConfirmation`-flagged action by
  `.claude/memory/contracts/plugin-cli-dispatch-contract.md`.
- Suggested prompts: manifest field in `src/shared/types/plugins.ts`
  (`PLUGIN_SUGGESTED_PROMPTS_MAX`) + Zod in
  `src/main/services/plugin/plugin-manifest-validator.ts`; chips in
  `PluginSessionsPane.tsx` (AsyncButton, `plugin-suggested-prompts` anchor).
- The CLI forwarder the context teaches:
  `src/main/services/cli/cli-server-plugins-routes.ts`
  (`ALL /plugins/:id/cli/:path*`) — governed by
  `.claude/memory/contracts/plugin-cli-dispatch-contract.md` (approval gating
  landed with DF-0005).

### Proof

Unit + integration suites listed in the two contracts, plus the pure mount-gate
lock `tests/unit/features/plugins/compute-plugin-sessions-pane-visible.test.ts`
(the "Uplink regression lock" + empty-`endpoints` boundary); end-to-end:
`tests/e2e/ui/plugin-sessions-pane.spec.ts` (flag-on pane/row/yield/chips +
flag-off absence even with prompts declared + the manifest-scoping assertion:
flag-on with no `cli.endpoints` → no pane) with 3-width screenshots under
`docs/edo/plugin-sessions-proof/` and 3-width light/dark chips proof under
`docs/edo/plugin-suggested-prompts-proof/`.

## Related

Two neighbouring pages cover what a plugin's sessions can reach. The human-facing one-liner about
those AI actions is [plugin-capability-card.md](plugin-capability-card.md), and the rules for what a
plugin's own screen may call are [plugin-bridge-capabilities.md](plugin-bridge-capabilities.md). A
session that instead has to FIND a plugin by name and drive it is
[plugin-cli-discovery.md](plugin-cli-discovery.md), and installing or removing the plugins
themselves is [plugin-marketplace.md](plugin-marketplace.md).
