mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-15 10:16:07 +00:00
* feat(gateway): add generic sessions.catalog surface with plugin SDK registration seam
* feat(agents): support one-shot forked CLI session resume with successor rebinding
* feat(anthropic): adopt local Claude CLI sessions into native chats via catalog continue
* feat(codex): register the session catalog provider independently of supervision
* refactor(ui): delete the retired custom Claude/Codex session tab views
* feat(ui): render external session catalogs as native sidebar sessions and chat panes
* docs: describe the unified native session catalog
* fix: harden forked CLI resume and catalog transcript mapping after review
* fix: satisfy strict typecheck for catalog source guard and imported message cast
* chore(i18n): regenerate session catalog locales
* fix(codex): simplify session source guard type
* fix(ui): paginate sidebar session catalogs
* chore(i18n): refresh catalog metadata after main merge
* fix(ui): harden session catalog pagination refresh
* test(agents): use sqlite session accessor in fork tests
* fix(ci): refresh session catalog generated surfaces
* fix(ui): align session catalog locales
* fix: address session catalog review findings
* fix(sessions): roll back failed plugin catalog adoption
* test(sessions): preserve CLI binding literals
* fix(ui): restore native session pagination label
* docs: note external session catalogs
* Revert "docs: note external session catalogs"
This reverts commit cfb503f160.
431 lines
23 KiB
Markdown
431 lines
23 KiB
Markdown
---
|
|
title: Codex supervision
|
|
summary: "Architecture and product boundary for supervising native Codex sessions from OpenClaw."
|
|
read_when:
|
|
- Designing Codex session discovery, continuation, or archive behavior
|
|
- Changing the native session catalog UI or Gateway RPCs
|
|
- Extending Codex supervision across paired nodes
|
|
---
|
|
|
|
# Codex supervision
|
|
|
|
## Goal
|
|
|
|
Codex supervision lets an OpenClaw operator discover native Codex sessions and,
|
|
when safe, create a local branch through the normal OpenClaw Chat surface.
|
|
Codex App Server remains the thread and model-loop owner. OpenClaw supplies the
|
|
fleet catalog, authenticated operator UI, session binding, and channel delivery.
|
|
|
|
The feature belongs to the official `codex` plugin. There is no separate
|
|
Supervisor plugin or second Codex protocol implementation.
|
|
|
|
## Product boundary
|
|
|
|
The catalog registers whenever the Codex plugin is active. Enable agent-facing
|
|
supervision tools with:
|
|
|
|
```text
|
|
plugins.entries.codex.config.supervision.enabled = true
|
|
```
|
|
|
|
The active initial product is intentionally smaller than the long-term fleet
|
|
plan:
|
|
|
|
- List only non-archived Codex threads.
|
|
- Group local and opted-in paired-node rows by stable host identity.
|
|
- Create a normal, model-locked Chat branch from a stored or idle Gateway-local
|
|
thread, start its full Codex harness thread on the first turn, or open the Chat
|
|
created for an earlier branch.
|
|
- Archive a stored or idle Gateway-local thread only after explicit
|
|
no-other-runner confirmation.
|
|
- Show active local sources without new-branch or archive controls while still
|
|
allowing an existing supervised Chat to open.
|
|
- Show the newest rows per host in the main sidebar, keep the full catalog on
|
|
the sessions page, and provide bounded, cursor-paginated transcript reads for
|
|
local and paired-node rows.
|
|
- Isolate catalog failures by host.
|
|
|
|
The catalog is the non-archived collection. A row within it can still have an
|
|
idle, active, `notLoaded`, or error turn status.
|
|
|
|
Agent-facing supervision remains opt-in. Guided onboarding attempts to install and enable it
|
|
after native Codex installation detection succeeds and the selected inference
|
|
backend passes its live check, independently of which primary backend the user
|
|
selects. Supervision activates only when that opportunistic plugin setup
|
|
succeeds. An explicit disabled plugin, policy block, or
|
|
`supervision.enabled: false` remains authoritative for supervision tools, but
|
|
does not disable the operator session catalog.
|
|
|
|
## Ownership
|
|
|
|
The `codex` plugin owns all Codex App Server behavior:
|
|
|
|
- endpoint discovery and connection lifecycle
|
|
- protocol initialization and version checks
|
|
- thread list, read, resume, archive, and event handling
|
|
- approval and user-input bridges
|
|
- native thread bindings to OpenClaw sessions
|
|
- Codex-only model and harness enforcement after continuation
|
|
|
|
The Control UI and Gateway consume that plugin-owned service. They do not read
|
|
Codex rollout files directly and do not implement another App Server client.
|
|
|
|
The default local topology is:
|
|
|
|
```text
|
|
Codex Desktop -> private stdio App Server -> user Codex home
|
|
^
|
|
OpenClaw Codex plugin -> supervision App Server connection
|
|
(defaults to managed user-home stdio; explicit appServer settings are honored)
|
|
-> passive source catalog and read
|
|
-> snapshot pin -> canonical appServer-source branch
|
|
-> visible-history injection and every later supervised Chat turn
|
|
|
|
Ordinary OpenClaw Codex sessions -> managed agent-home stdio by default
|
|
-> ordinary full harness threads -> OpenClaw Chat and channel delivery
|
|
```
|
|
|
|
Enabling supervision does not change the ordinary Codex harness: it remains
|
|
agent-scoped by default. The separate supervision connection defaults
|
|
to managed user-home stdio, so its catalog and snapshot operations see native
|
|
stored threads. Explicit `appServer` connection settings are honored. When
|
|
`homeScope` is unset, the supervision connection resolves it to `"user"` for stdio
|
|
or Unix and `"agent"` for WebSocket. Set `appServer.homeScope: "user"`
|
|
explicitly only when the ordinary harness should also share the native Codex
|
|
home. A Chat adopted from the Codex sidebar group is the exception: its private
|
|
supervision binding keeps source reads, canonical branch creation, and later
|
|
turns on the supervision connection. Live status and ownership remain
|
|
process-local; a thread unknown to OpenClaw's supervision process is `notLoaded`
|
|
even when Codex Desktop is actively running it.
|
|
|
|
Codex has an experimental canonical local daemon with a separate
|
|
installer-managed bootstrap contract. This feature must not bootstrap, claim,
|
|
or assume that daemon implicitly.
|
|
|
|
## Catalog flow
|
|
|
|
The generic Gateway method `sessions.catalog.list` dispatches to the `codex`
|
|
catalog provider, which always requests `archived: false` and
|
|
the interactive `cli` and `vscode` source kinds. It combines:
|
|
|
|
1. Gateway-local `thread/list` results from the supervision App Server,
|
|
which defaults to managed user-home stdio.
|
|
2. `codex.appServer.threads.list.v1` results from each connected, opted-in node.
|
|
|
|
Transcript selection uses `thread/turns/list` with `itemsView: "full"` locally or
|
|
the versioned `codex.appServer.thread.turns.list.v1` command on the selected
|
|
node. Every response contains at most 20 persisted turns plus opaque
|
|
forward/backward cursors. The Control UI requests newest-first pages, renders each page in
|
|
chronological order, and prepends older pages. It never falls back to an
|
|
unbounded `thread/read`. OpenClaw also rejects any serialized item page above
|
|
20 MiB before it can cross the node or Gateway transport.
|
|
|
|
The native macOS paired-node implementation supports only an unset/default or
|
|
explicit `appServer.transport: "stdio"` with unset/default supervision scope or
|
|
explicit `appServer.homeScope: "user"`. It carries configured `command`, `args`,
|
|
and normalized `clearEnv` into the child process. With `"unix"`, `"websocket"`,
|
|
or explicit `homeScope: "agent"`, it advertises neither the catalog capability
|
|
nor command; direct invocation also fails closed. It must never expose the user
|
|
Codex home for an agent-scoped configuration or substitute local stdio for an
|
|
explicit endpoint.
|
|
|
|
The catalog projection normalizes identifiers, title, cwd, status, active wait
|
|
flags, timestamps, source, model provider, Codex version, and Git branch. It
|
|
does not return transcript previews, turns, rollout paths, Codex home paths,
|
|
Git remotes, commit SHAs, raw endpoints, or raw App Server errors. Transcript
|
|
responses contain only the explicitly requested App Server item page and its
|
|
opaque cursors.
|
|
|
|
Host failures remain local to each host result. An offline node or unavailable
|
|
local App Server does not erase healthy hosts from the page. Connectivity is a
|
|
host property, not a thread status: a failed host result contains no fresh
|
|
session rows and does not project `offline` onto native threads.
|
|
|
|
Catalog discovery is passive. Listing or reading metadata must not call
|
|
`thread/resume`, subscribe the OpenClaw client to live thread requests, or
|
|
answer an approval.
|
|
|
|
Search is title-only and case-insensitive. For each returned catalog page, the
|
|
Gateway and paired Mac scan a bounded number of native pages without passing
|
|
the query to App Server, because native search can also match transcript
|
|
previews. The returned native cursor lets callers continue the scan.
|
|
|
|
## Operator CLI boundary
|
|
|
|
The plugin registers three Gateway-backed shell commands:
|
|
|
|
```text
|
|
openclaw codex sessions [--search <text>] [--host <id>] [--limit <count>] [--cursor <cursor>] [--json] [gateway-options]
|
|
openclaw codex continue <thread-id> [--json] [gateway-options]
|
|
openclaw codex archive <thread-id> --confirm-no-other-runner [--json] [gateway-options]
|
|
```
|
|
|
|
`[gateway-options]` is `--url <url>`, `--token <token>`, `--timeout <ms>`, and
|
|
the inherited `--expect-final` switch. Session listing defaults to 75,000 ms;
|
|
continue and archive default to 30,000 ms;
|
|
`--expect-final` has no additional effect for these unary RPCs. Session search
|
|
is title-only and case-insensitive; each response scans a bounded native page
|
|
chain, and `--cursor` continues older results. The limit defaults to 50 per host
|
|
and accepts 1 through 100, and a cursor requires one stable `--host`
|
|
destination. No command accepts
|
|
an archived/include-archived option. Only `sessions` can target paired hosts;
|
|
`continue` and `archive` always send `hostId: "gateway:local"`, and archive
|
|
requires the explicit confirmation flag.
|
|
|
|
The shell namespace is not the in-chat `/codex` runtime namespace. In
|
|
particular, `/codex sessions --host <node>` lists Codex CLI session files on one
|
|
node, `/codex threads` lists App Server threads for the current conversation
|
|
connection, and `/codex resume` or `/codex bind` mutates that conversation's
|
|
binding. Those commands do not replace `sessions.catalog.continue`, and there is
|
|
no `/codex continue` or `/codex archive` runtime command.
|
|
|
|
## Local continuation
|
|
|
|
For a stored or idle Gateway-local row, the UI calls
|
|
`sessions.catalog.continue` with `catalogId: "codex"` plus the host and thread
|
|
ids. The plugin:
|
|
|
|
1. Reuses the existing supervised Chat when the source already has one.
|
|
2. Otherwise projects bounded user and assistant history through the source's
|
|
last terminal persisted turn (completed, interrupted, or failed) into a new
|
|
OpenClaw Chat and records a pending harness branch.
|
|
3. Stores the pending Codex-only model-lock policy, not a concrete model or
|
|
provider selection, plus the private supervision connection scope, and
|
|
returns the OpenClaw `sessionKey`.
|
|
|
|
The history projection selects the newest tail of visible user and assistant
|
|
messages, with hard limits of 200 messages, 512 KiB of UTF-8 text in total, and
|
|
64 KiB per message. It replaces image and local-image inputs with
|
|
`[Image attachment]`, never copies image payloads or paths, and omits reasoning,
|
|
tool calls, and tool results.
|
|
|
|
The UI navigates to normal Chat with that session key. No canonical harness
|
|
thread exists yet. On the first normal Chat turn, the harness installs the real
|
|
Codex approval, elicitation, event, and delivery handlers, then:
|
|
|
|
1. Uses the supervision connection to call native `thread/fork` without a model
|
|
or provider override and pin the persisted source snapshot. Codex's current
|
|
`ConfigManager` state selects the model and provider, and the fork response
|
|
reports the actual pair. If the model differs from the last model recorded
|
|
in the source, Codex emits its normal model-difference warning.
|
|
2. On that same connection, starts the canonical full Codex harness thread with
|
|
`threadSource: "appServer"`, OpenClaw's cwd, policy, config, environment, the
|
|
full OpenClaw harness tool surface, and exactly the model and provider
|
|
returned by the fork for this initial start.
|
|
3. Injects the bounded visible user and assistant history through that
|
|
connection, commits the canonical binding without dropping its supervision
|
|
scope, runs the turn, and archives the temporary fork.
|
|
|
|
Before the first turn, the Chat is a locked pending branch with a visible
|
|
history mirror; afterward, every model turn runs through the canonical Codex
|
|
harness thread on the supervision connection. The branch is not a full native
|
|
rollout clone: source reasoning, tool calls, and tool results are deliberately
|
|
omitted. If snapshot pinning or canonical thread creation fails, the pending
|
|
branch remains retryable. A binding race, disabled supervision, or an unavailable
|
|
or mismatched supervision connection fails closed before the turn runs instead
|
|
of falling back to the ordinary agent-home harness.
|
|
|
|
This guarantees Codex-owned selection, not preservation of the source's
|
|
historical model. The fork's returned pair is used for the canonical thread
|
|
start, and Codex persists that thread's native model and provider. Later resumes
|
|
omit OpenClaw model and provider overrides, so Codex restores the persisted pair.
|
|
If a separate native Codex control changes the canonical thread, OpenClaw accepts
|
|
that native persisted selection. The outer OpenClaw model and fallback chain
|
|
never substitute for it.
|
|
|
|
Model changes, session deletion, and session reset/new operations fail closed
|
|
for the supervised model-locked Chat. Mutating `/codex model <model>`, `/codex
|
|
bind`, `/codex resume` (including node `--bind here`), and `/codex detach` or
|
|
`/codex unbind` also fail closed because they replace or clear the binding. The
|
|
`/codex model` query and `/codex fast`, `/codex permissions`, and `/codex
|
|
threads` remain available. The `codex_threads` agent tool cannot attach a new
|
|
fork or archive the bound native thread. List and metadata-only read remain
|
|
available; transcript fields require `supervision.allowRawTranscripts`, while
|
|
rename, unarchive, detached fork, and archive of an unrelated thread require
|
|
`supervision.allowWriteControls`. Neither option can replace the locked binding.
|
|
Deleting or resetting the OpenClaw entry would otherwise discard the native
|
|
binding and create or permit a generic thread behind a Codex-looking session.
|
|
Retention maintenance therefore preserves model-locked entries even when they
|
|
exceed ordinary age, count, or disk-budget limits. Disabling or uninstalling the
|
|
owning plugin also retains the lock and plugin ownership marker. The Chat stays
|
|
unavailable and fails closed until the same plugin is re-enabled; cleanup never
|
|
converts it into an ordinary model session.
|
|
|
|
The source is never resumed or mutated by this action. The temporary fork pins a
|
|
snapshot; it is not the durable continuation thread. Starting a distinct
|
|
canonical harness thread on the first turn prevents OpenClaw from becoming a
|
|
competing source writer merely because process-local status failed to see a
|
|
Desktop-owned turn. The visible-history mirror and pinned snapshot may omit work
|
|
that has not yet completed in an active source. The original CLI or VS Code
|
|
source remains eligible for both native and OpenClaw catalogs. The canonical
|
|
branch remains a native Codex thread in the supervision store, but native clients
|
|
may filter its `appServer` source kind, so Codex Desktop visibility is not a
|
|
contract.
|
|
|
|
## Archive behavior
|
|
|
|
For a stored or idle Gateway-local row, `sessions.catalog.archive` with
|
|
`catalogId: "codex"` requires
|
|
explicit `confirmNoOtherRunner: true`, freshly reads current process-local
|
|
status, proceeds only for `idle` or `notLoaded`, calls native `thread/archive`,
|
|
and returns success only after Codex accepts the operation. The row then leaves
|
|
the non-archived catalog.
|
|
|
|
An active or error status from the fresh read rejects archive. So does an
|
|
initializing or pending supervised branch from the source: the first Chat turn
|
|
must materialize its canonical branch before the source can be archived. A
|
|
known active OpenClaw binding owner for the exact target or any non-archived
|
|
spawned descendant also rejects archive. OpenClaw paginates Codex's experimental
|
|
`thread/list ancestorThreadId` relation and fails closed on request or response
|
|
errors, cursor or thread cycles, and safety-limit exhaustion. Native archive can
|
|
shut down loaded parent and descendant work, so archive is not an interrupt
|
|
shortcut. The read, descendant enumeration, and archive calls are not atomic.
|
|
An independent client can still own or start work on a row that appears idle or
|
|
`notLoaded` locally. The no-other-runner confirmation covers unknown clients and
|
|
that race until Codex has a conditional archive or cross-process lease.
|
|
Paired-node archive is prohibited.
|
|
|
|
There is no archived view in the Codex catalog. A thread restored with
|
|
`thread/unarchive` in another owner-authorized Codex surface becomes eligible
|
|
for the non-archived catalog again.
|
|
|
|
## Active thread safety
|
|
|
|
Codex serializes mutations for a thread among clients of one App Server, but it
|
|
does not expose an exclusive cross-process runner or approval-owner lease.
|
|
Independent stdio App Servers can append to the same rollout, while each sees
|
|
only its own in-memory status. Approval requests can also reach every subscriber
|
|
of one server, with the first valid response completing the request.
|
|
|
|
Therefore:
|
|
|
|
- passive catalog clients do not subscribe or auto-deny approvals
|
|
- rows currently reported active expose neither a new branch nor Archive
|
|
- an unmapped source becomes a visible-history branch whose canonical harness
|
|
thread never resumes the source
|
|
- `notLoaded` is shown as activity unknown and can be archived only after
|
|
informed no-other-runner confirmation
|
|
- local archive requires that confirmation plus a fresh `idle` or `notLoaded`
|
|
read, while acknowledging the protocol race between read and archive
|
|
|
|
Interrupt and multi-client handoff are future product decisions. They are not
|
|
implied by showing an active row.
|
|
|
|
## Paired-node boundary
|
|
|
|
Node invoke is currently request/response only. It can safely return bounded
|
|
catalog metadata and transcript turn pages, but it cannot carry the long-lived event stream, approval
|
|
requests, tool calls, cancellation, and assistant deltas required by a Codex
|
|
harness run.
|
|
|
|
The node contract therefore supports list and transcript-turn pages. Remote
|
|
rows stay readable, but **Continue** and **Archive** are unavailable, regardless of idle status. A
|
|
real remote continuation requires a node-side runner and streaming bridge that
|
|
preserves the same approval and binding invariants as the local harness.
|
|
|
|
## Permissions
|
|
|
|
Each computer opts in locally. Enabling the Gateway does not authorize another
|
|
node to read its Codex metadata. The node capability must pass normal pairing
|
|
and command-policy approval.
|
|
|
|
Fleet listing and transcript viewing use the `operator.write` Gateway scope
|
|
because they invoke paired nodes. Local continuation and archive are
|
|
authenticated operator actions and remain subject to host and status checks.
|
|
|
|
Autonomous agent and standalone MCP access is separate. The shipped
|
|
`codex_endpoint_probe`, `codex_sessions_list`, `codex_session_read`,
|
|
`codex_session_send`, and `codex_session_interrupt` tool contracts remain owned
|
|
by the `codex` plugin. With supervision enabled, raw `codex_threads` transcript
|
|
reads and transcript-derived list fields also require
|
|
`supervision.allowRawTranscripts`; every `codex_threads` fork, rename, archive,
|
|
or unarchive requires `supervision.allowWriteControls`. Both policies default to
|
|
disabled.
|
|
|
|
## Compatibility
|
|
|
|
`openclaw doctor --fix` migrates shipped `plugins.entries.codex-supervisor`
|
|
configuration, including endpoints and transcript/write policies, plus plugin
|
|
allow/deny references into
|
|
`plugins.entries.codex.config.supervision`. Explicit canonical destination
|
|
values win conflicts. Runtime code uses only the canonical `codex` plugin
|
|
shape after migration.
|
|
|
|
The official plugin retains exactly five Supervisor compatibility tools:
|
|
`codex_endpoint_probe`, `codex_sessions_list`, `codex_session_read`,
|
|
`codex_session_send`, and `codex_session_interrupt`. Session list is loaded-only
|
|
by default; there is no `loaded_only` parameter. `include_stored: true` adds
|
|
non-archived state-database rows, bounded per endpoint by `max_stored_sessions`
|
|
(default 200, accepted range 1 through 1,000); loaded rows are uncapped by that
|
|
setting. Transcript-derived fields and reads remain gated by
|
|
`allowRawTranscripts`; send and interrupt remain gated by `allowWriteControls`.
|
|
|
|
Compatibility send never starts or resumes an idle thread. `mode: "start"` is
|
|
always refused; `"auto"` and `"steer"` steer only a readable active turn.
|
|
Interrupt likewise requires an active readable turn. Idle continuation routes
|
|
to the native Codex catalog so the full harness owns approvals, tools, and the binding.
|
|
The standalone legacy MCP adapter resolves these same tools from the official
|
|
plugin and is the only path that honors the retained legacy policy environment
|
|
variables.
|
|
|
|
The July catalog UI, Gateway method, node capability, and CLI registration had
|
|
not shipped under the old plugin id. They move directly to `codex` ownership
|
|
without a second runtime facade.
|
|
|
|
## Future work
|
|
|
|
- node-side streaming runner and event bridge for remote continuation
|
|
- explicit runner and approval-owner leases for simultaneous client handoff
|
|
- remote archive after a runner-ownership lease or equivalent fencing exists
|
|
- interrupt and richer active-session observation
|
|
- audited handoff between Codex Desktop, CLI, and OpenClaw
|
|
|
|
Archived browsing is not part of the planned supervision sidebar. Native Codex
|
|
surfaces remain the recovery path for archived threads.
|
|
|
|
## Acceptance tests
|
|
|
|
- Enabling supervision lists non-archived local sessions.
|
|
- Archived sessions never appear in the catalog response or UI.
|
|
- Healthy hosts remain visible when another host fails; an unavailable host
|
|
returns no fresh rows instead of inventing an offline session status.
|
|
- A stored or idle local row creates a Chat mirror with a Codex-only
|
|
model/runtime lock; the first turn pins a temporary snapshot and starts the
|
|
canonical full harness thread, and repeating Continue opens the existing Chat.
|
|
- The first turn omits model/provider overrides on the snapshot fork and pins
|
|
the canonical start to the exact pair returned by Codex, even when Codex warns
|
|
that its current model differs from the source's last recorded model.
|
|
- Pending and committed supervised bindings use the supervision connection for
|
|
source access, canonical branch creation, and every later turn; ordinary
|
|
Codex sessions remain agent-scoped.
|
|
- Later resumes omit OpenClaw model/provider overrides, preserve Codex's
|
|
canonical persisted selection, accept separate native changes to that thread,
|
|
and never substitute the outer OpenClaw model or fallback chain.
|
|
- Disabling supervision or losing the binding/connection lifecycle fails closed
|
|
instead of moving the Chat to the ordinary agent-home harness.
|
|
- A supervised model-locked Chat cannot be deleted while it protects the native
|
|
binding.
|
|
- The Chat mirrors at most 200 user and assistant messages, 512 KiB total, and
|
|
64 KiB per message. Images become placeholders; source reasoning, tool calls,
|
|
tool results, image payloads, and local paths are not cloned.
|
|
- The branch flow never resumes the source thread.
|
|
- The original source remains eligible for both catalogs. The canonical native
|
|
branch uses the `appServer` source kind and is not guaranteed to appear in
|
|
Codex Desktop.
|
|
- Active local sources cannot create a branch or be archived; an existing
|
|
supervised Chat can still open.
|
|
- Activity-unknown rows can branch without confirmation; archiving requires
|
|
explicit no-other-runner confirmation.
|
|
- A source with an initializing or pending supervised branch cannot be archived
|
|
until the first Chat turn materializes the canonical branch.
|
|
- A known active binding owner for the exact target or any non-archived spawned
|
|
descendant blocks archive; descendant enumeration failures fail closed, and
|
|
explicit confirmation remains responsible for unknown clients and the
|
|
status-to-archive race.
|
|
- Confirmed stored or idle local archive removes the row after native success.
|
|
- Paired-node rows remain visible without Continue or Archive.
|
|
- Passive listing never subscribes to or answers thread approvals.
|
|
- Legacy Supervisor config migrates to the canonical Codex config shape.
|
|
- Legacy list is loaded-only by default, stored enumeration obeys its per-endpoint
|
|
cap, and compatibility send never starts or resumes an idle thread.
|