openclaw/docs/plugins/codex-harness/native-features.md
RoboClaw 6046d4fcb6
feat: use MCP plugin apps across conversations and workspace files (#161747)
* feat: use MCP plugin apps across conversations and workspace files

Extend the opt-in MCP Apps host with discovery and entrypoints, settings, rich forms, scoped file editing, multimodal context, deep links, and native Codex session preparation. Preserve existing requester, approval, runtime, and file authority owners.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix: keep app context compact and review large tool payloads

Reuse the canonical bounded plugin approval preview without rejecting otherwise valid large App arguments. Keep the same one-shot approval and live-authority gates. Bound context-strip icons and scrolling so attachments leave the composer usable.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix: reveal pending input above fullscreen MCP apps

Keep App fullscreen rendering in the existing browser top layer and return to inline for same-conversation questions or approvals without replacing the iframe. Consolidate host-context, resource, and input notifications under the existing bridge lifetime.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* test: preserve full attempt inputs in native assignment fixtures

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix: release session access when MCP app launch preparation fails

Preserve the upstream optional ifMatch contract and document unconditional-save semantics explicitly.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(ui): defer question controls and preserve canonical keyboard values

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(mcp): preserve app authority through startup and user turns

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* fix(codex): share native app startup across concurrent discoveries

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* docs(mcp): explain native app prompting and request limits

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* feat: use MCP plugin apps across conversations and workspace files

Worked on by:
- @steipete

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
OpenClaw-Publication: f2f8d735-c1ac-4a0d-84f3-1576113c1baf

* fix(mcp): keep form contracts and test helpers with their owners

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* feat: use MCP plugin apps across conversations and workspace files

Worked on by:
- @steipete

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
OpenClaw-Publication: 2513c3ef-4af8-4722-b802-ffa426df486a

* fix(codex): revalidate MCP App authority before native retries

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* test(ui): provide chat identity fixture context dependencies

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>

* feat: use MCP plugin apps across conversations and workspace files

Worked on by:
- @steipete

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
OpenClaw-Publication: 85c132dc-d926-46ca-b57e-2d9e01f15487

* fix: integrate MCP app owners with current main

* fix(gateway): accept option labels from installed clients for rich forms

* chore(format): anchor the native apps formatter ignore

* perf(ui): load the MCP app link parser on demand

Keep startup click interception parser-free and capture the normalized href before lazy navigation. Preserve ordinary, modified and download links; drop malformed plugin links and cancel pending navigation on disposal. Router/parser proof: 17 tests passed in 4.50s wall. Startup gzip: 374086 -> 373613 B; 159 B still above the unchanged 373454 B limit.

* perf(ui): register MCP app English with lazy consumers

Move the MCP App catalog out of startup English and register it synchronously at every consumer. Keep the shared namespace anchor and host catalog composition so all text and source order remain byte-identical. i18n verification passes without baseline changes. Startup gzip: 373613 -> 373103 B, 351 B below the unchanged 373454 B limit.

* fix(ui): intercept only well-formed MCP app links

Require the plugin/app/tool shape before cancelling browser navigation so ordinary ChatGPT plugin pages keep their default behavior. Preserve lazy strict parsing and accepted native and web app links.

* fix(gateway): watch MCP app files directly so loaded macOS hosts still notify

Watch the bound file to avoid FSEvents directory event drops under load. Re-arm on atomic replacement, retry ENOENT once on the next immediate turn, and retain subscription authority and cleanup. Cover replacement followed by a plain write through the registered resource routes.

* fix(gateway): use the errno guard when rearming MCP app watches

Use the shared filesystem error guard instead of an unchecked type assertion. Keep the single immediate ENOENT retry and all subscription behavior unchanged.

* fix(codex): read the canonical MCP transport field

Main migrates MCP transport aliases before runtime (#162256) and retired the SDK re-export of the alias resolver, so the native app catalog reads the canonical server.transport field the same way the agentsapi plugin does.

* fix(gateway): keep MCP app file subscriptions alive across rename gaps

Keep resource subscriptions alive while editors move the old file aside before installing its replacement. Poll missing paths at 250 ms, return to the inode watcher when the file reappears, and release polling on subscription close. Recheck after polling registration to cover replacement before its first stat; share the rearm guard so late callbacks cannot leak watchers.

* test(codex): type the rooted thread policy support from attempt fixtures

The rooted policy support that main added types its lifecycle input from the raw thread signature, while this branch's binding fixtures carry full attempt params. Derive it from the shared attempt-thread fixture type, as the sibling policy-refresh support already does.

* test(ui): exercise the MCP app link probe through the click boundary

The eager link probe was exported only for its unit test, which the production dead-export scan rejects. Keep it module-private and assert the same accepted and rejected links through real click interception.

* style(lint): clear MCP app lint errors

CI run 36988865432 jobs check-lint-core-1 and check-lint-core-2 rejected shadowed stat callback variables, a returning Promise executor, and reassigned projection parameters. Rename the inner variables and use local projection bindings and an executor block without changing behavior.

* test(agents): align bundle MCP fixture ownership

CI run 36988865432 job checks-node-changed-compact-large-14 failed seven merge cases because the fixture omitted the loader-required pluginIdsByServer map. Type the fixture against the producer contract and verify ownership survives only for unshadowed enabled bundle servers, preserving the migrated transport behavior.

* fix(auto-reply): register turns before MCP context leasing

CI run 36988865432 job checks-node-changed-compact-large-33-2 exposed an asynchronous MCP lease before synchronous run registration. Prepare App context after ownership registration and image admission, revalidate requester authority around the lease, and retain commit and rollback settlement with the execution outcome owner.

* test(gateway): admit MCP shutdown requests through current policy

CI run 36988865432 job checks-node-changed-compact-large-9 timed out because the fixture ignored an admission rejection before its upstream call. Publish matching Gateway and runtime config, create the session through its RPC owner, and observe early request settlement while preserving all shutdown ordering and cleanup assertions.

* refactor(apple): drop the label-only question toggle

Periphery flagged toggleOption(questionID🏷️) as dead: production selects by canonical value since the rich-form change, and only tests still called the label overload. Tests now toggle by value; the ambiguous-label case becomes an unknown-value no-op.

---------

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-10-02 07:02:35 -05:00

17 KiB

summary read_when title sidebarTitle
Share native Codex threads, supervise sessions, and enable native plugins and Computer Use
You want OpenClaw to share the native Codex home
You want to use your existing local Codex config.toml and login
You are enabling Codex supervision
You are enabling native Codex plugins or Computer Use
Native Codex state and features Native state and features

Opt-in features that connect an OpenClaw agent to native Codex state and Codex-owned capabilities. Part of the Codex harness guide; Where each section moved lists every section.

Share threads with Codex Desktop and CLI

The default appServer.homeScope: "agent" isolates each OpenClaw agent from the operator's native Codex state. To let an owner inspect and manage the same native threads shown by Codex Desktop and the Codex CLI, opt into the user Codex home:

{
  plugins: {
    entries: {
      codex: {
        enabled: true,
        config: {
          appServer: {
            homeScope: "user",
          },
        },
      },
    },
  },
}

User-home mode supports a local managed stdio process or the shared Unix-socket transport. It uses $CODEX_HOME when set and ~/.codex otherwise, including that home's native Codex auth, config, plugins, and thread store. OpenClaw does not inject an OpenClaw auth profile into this app-server, even when the agent's model route has a stored OpenAI profile. The native account is verified against the route instead, in both directions:

  • A subscription route requires the native home to be signed in to ChatGPT. Run codex login in that home if a turn reports missing subscription credentials.
  • A Platform (API-key) route refuses a native home signed in with a ChatGPT subscription, so an API-billed route never silently spends the plan. Sign that home in with codex login --with-api-key, or switch to homeScope: "agent" and let OpenClaw inject the key it already holds.

A stored OpenAI profile is fine alongside homeScope: "user"; OpenClaw keeps it for agent-scoped connections and simply does not hand it to the native home. Use openclaw models auth list --provider openai to inspect stored profiles and openclaw models auth logout <profileId> --yes to remove one you no longer want.

Owner turns gain the codex_threads tool: list, search, read, fork, rename, archive, and restore native threads. Fork a thread to continue it in OpenClaw; the fork attaches to the current OpenClaw session and remains readable by ID from other native Codex clients. It appears in native thread lists after its first user turn. Archiving requires explicit confirmation that the thread is closed elsewhere. When supervision is also enabled, transcript fields and mutations require the matching supervision.allowRawTranscripts or supervision.allowWriteControls opt-in.

Do not resume or write the same thread concurrently through independent managed stdio App Servers. Codex coordinates live writers inside one App Server, not across separate processes. Forking is the safe coexistence path for ordinary user-home stdio sessions.

appServer.homeScope: "user" alone does not control the fleet catalog. Native session discovery is enabled while the plugin is active; set sessionCatalog.enabled: false to remove it from the OpenClaw sidebar without disabling Codex. The catalog uses a separate supervision connection; without explicit appServer connection settings, that connection defaults to managed user-home stdio while the ordinary harness stays agent-scoped. Explicit appServer settings are honored by both paths. Set homeScope: "user" explicitly, as above, when the ordinary harness should also share native state.

Use an existing local config.toml

Connect to the same local Codex App Server to reuse your existing $CODEX_HOME/config.toml (~/.codex/config.toml by default), login, and native threads. Codex owns loading that file, trusted project configuration, and its normal configuration precedence. You do not need to copy the TOML into OpenClaw or sign in again through OpenClaw.

On macOS or Linux, keep the existing Codex daemon running. If you use Codex's standalone managed installation and its daemon is not running, start it with:

codex app-server daemon start

That command is idempotent and reports the control socket in its JSON response. For other installations, use the existing local App Server's Unix socket; do not start another App Server against a thread already owned by a different process.

Merge these plugin settings into your OpenClaw configuration:

{
  plugins: {
    entries: {
      codex: {
        enabled: true,
        config: {
          appServer: {
            transport: "unix",
            homeScope: "user",
          },
          supervision: {
            enabled: true,
          },
        },
      },
    },
  },
}

Without url, OpenClaw connects to $CODEX_HOME/app-server-control/app-server-control.sock. The Gateway and native daemon must resolve the same Codex home. For a custom socket, set appServer.url to "unix:///absolute/path/to/codex.sock". OpenClaw connects to the running server; it does not start or stop that daemon.

To let native Codex select the model and provider, open a stored or idle session from the Codex sidebar and send a message from its session viewer. The resulting model-locked Chat uses native configuration for its initial selection and preserves native ownership on later turns. Check /codex binding in that Chat to inspect the actual selection. Ordinary OpenClaw chats still use their OpenClaw model route; homeScope: "user" by itself does not make every chat inherit the TOML model. See branching behavior.

OpenClaw still applies its session tools, instructions, and execution policy without rewriting your TOML. Once that policy is established, ordinary follow-ups reuse it. Initial attachment or a changed policy can require Codex to unload an idle thread first. If a turn reports a session policy handoff failure, finish native work and close other views of that specific thread, then reconnect and retry. Other threads and the daemon can stay running.

Existing supervised conversations keep their recorded native search policy after an update. If native search was disabled when the conversation was created, newly advertised provider support does not enable it in that thread. Open another stored or idle native session from the Codex sidebar and send a message to create a new branch with the current native search capability and OpenClaw tool policy.

Credentials and account ownership

Connecting to the shared daemon reuses its native login; it does not import that login into an OpenClaw auth profile. For a ChatGPT login, both paths below use OAuth and Codex App Server. The difference is who owns the credentials.

Aspect Shared native daemon: Unix transport and user home OpenClaw-managed OAuth: managed stdio and agent home
Login source The native Codex account in the selected CODEX_HOME. No second OpenClaw sign-in is required. The selected OpenClaw OAuth profile.
Handoff to Codex OpenClaw attaches without sending an OpenClaw profile to account/login/start or replacing the daemon's login. OpenClaw sends an access token, ChatGPT account ID, and plan type through account/login/start. It does not send the refresh token.
Credential storage Codex keeps its configured native credential store, such as a file or keyring. OpenClaw keeps durable credentials in its credential store. Codex holds the handed-off tokens in memory, not in auth.json. The agent's Codex config and threads still persist.
Token refresh Codex refreshes its native ChatGPT login. Attaching does not create another refresh owner in OpenClaw. Codex requests fresh access tokens from OpenClaw. OpenClaw refreshes the original profile; the refresh token stays with that owner.
Later turns Turns keep using native authentication. Retaining a thread subscription does not copy or pin an OpenClaw credential. A reused client keeps its original profile and refresh owner. Changing the account under a profile selects a new client rather than redirecting the old client's refresh requests.
Gateway environment Attaching does not change the already-running daemon's environment. Its own launch environment and native configuration still apply. Prepared managed launches clear CODEX_API_KEY, OPENAI_API_KEY, and CODEX_ACCESS_TOKEN so inherited values do not replace the selected handoff.
CLI and Desktop login The connection uses native authentication without logging that home into an OpenClaw-selected account. The default <agentDir>/codex-home is separate from the native home; the handoff does not overwrite the CLI or Desktop login.

A stored OpenClaw profile can coexist with a native login, but it is not a fallback credential for a supervised native Chat. Native authentication failures must be resolved in the native connection; OpenClaw does not borrow its ordinary profile or API-key fallback to change that Chat's account.

A model-locked Chat is not an account-locked Chat. Consumers of the same native daemon share its authentication boundary. Changing its login can affect other threads and native clients using that daemon; OpenClaw does not create a separate OpenAI account for each supervised Chat. Verify the intended native account before sending more work after an account change. Do not assume that signing out of an OpenClaw profile signs out native Codex, or vice versa.

Use ordinary agent-scoped sessions with explicit OpenClaw account selection when you need separate account ownership. Agent scope alone does not mean one login per person: shared profiles remain shared, and OAuth refresh credentials are not copied between agents by default. See Auth credential semantics and Per-person model accounts.

These rules describe the Codex connection's authentication, not credentials used by shell subprocesses or separately configured OpenClaw tools. For those boundaries, refresh failure handling, and explicit credential imports, see Codex auth and environment isolation.

Supervise Codex sessions

The same codex plugin can list non-archived Codex sessions from the Gateway computer and opted-in paired nodes. A stored or idle Gateway-local session can create a model-locked Chat that mirrors its bounded persisted user and assistant history. Its private binding uses the supervision connection for the native snapshot, canonical branch, and later turns while ordinary Codex sessions remain agent-scoped. The first canonical start uses exactly the model and provider that Codex returns for the snapshot fork. Later resumes leave selection to Codex's native configuration; the outer OpenClaw model and fallback chain never replace it. Stored and idle local rows can be archived after explicit no-other-runner confirmation. Active sources cannot create a branch or be archived; an existing supervised Chat can still be opened. Paired-node sessions expose bounded, paginated transcripts. Eligible stored or idle paired-node rows also support continuation for operator.admin when the node advertises and permits the required catalog and CLI-resume commands. That flow resumes the exact native thread on the node rather than creating a Gateway-local branch; paired-node archive remains unavailable.

See Supervise Codex sessions for setup, branching rules, paired-node limits, metadata exposure, and troubleshooting.

Native Codex plugins

Native Codex plugin support uses Codex app-server's own app and plugin capabilities in the same Codex thread as the OpenClaw harness turn. OpenClaw does not translate Codex plugins into synthetic codex_plugin_* OpenClaw dynamic tools.

codexPlugins affects only sessions that select the native Codex harness. It has no effect on built-in harness runs, normal OpenAI provider runs, ACP conversation bindings, or other harnesses.

Minimal migrated config:

{
  plugins: {
    entries: {
      codex: {
        enabled: true,
        config: {
          codexPlugins: {
            enabled: true,
            allow_destructive_actions: true,
            plugins: {
              "google-calendar": {
                enabled: true,
                marketplaceName: "openai-curated",
                pluginName: "google-calendar",
              },
            },
          },
        },
      },
    },
  },
}

Thread app config is computed when OpenClaw establishes a Codex harness session or replaces a stale Codex thread binding; it is not recomputed on every turn. After changing codexPlugins, use /new, /reset, or restart the gateway so future Codex harness sessions start with the updated app set.

For migration eligibility, app inventory, destructive action policy, elicitations, and native plugin diagnostics, see Native Codex plugins.

OpenAI-side app and plugin access is controlled by the signed-in Codex account and, for Business and Enterprise/Edu workspaces, workspace app controls. See Using Codex with your ChatGPT plan for OpenAI's account and workspace-control overview.

Computer Use

Computer Use has its own setup guide: Codex Computer Use.

Short version: OpenClaw does not vendor the desktop-control app or execute desktop actions itself. It prepares Codex app-server, verifies that the computer-use MCP server is available, and then lets Codex own the native MCP tool calls during Codex-mode turns.

Rich MCP forms

OpenClaw accepts Codex's openaiForm elicitation variant for the openai/elicitation/create extension and retains the legacy openai/form variant. Ordinary MCP form and URL requests keep their own protocol semantics; opening a URL is not confirmation that the step is complete.

Codex applies its native approval policy before forwarding a form. For manual Apps in OpenClaw-created threads, use a prompting permission mode such as Guarded or Workspace when a tool requires interactive input. Full access maps to the native never approval policy, which can decline these forms before OpenClaw receives them. OpenClaw does not open a second MCP connection to bypass that policy.

The Control UI displays option descriptions and base64 image thumbnails, including a fallback tile when only some choices have an image. HTTPS thumbnails use an explicit external-image link, not an automatic private-network browser fetch. String suggestions allow custom answers. String arrays accept suggested choices and one custom entry per line, with the same string, pattern, length, uniqueness, and cardinality constraints applied to every submitted value. Unsafe or unsupported patterns are refused instead of evaluated without a bound.

Explicit resource selection returns the supplied URI, not its display title. Both single-resource and multi-resource fields accept the deprecated type: "file" alias. Defaults must refer to supplied resources and appear as initial selections; clearing an optional default does not silently restore it. Resource previews and uploads require a capability bound to the pending form and its originating MCP runtime. With that capability, explicit selection chooses supplied resources, implicit selection removes resources from the submitted set, and file/directory upload controls return only host-admitted URIs. Preview actions read the linked MCP resource or open the originating server's MCP App tool. Web MCP App surfaces that do not support uploads must not advertise that capability. A form requiring an unavailable operation is refused in full, with an explanation; it is never partially displayed or replaced with an unscoped URI text box.

Rich forms are bounded to 12 fields, 64 choices per field, and 16 custom array entries. Ordinary questions retain their four-option limit. Choices and semantic inputs are not truncated. Invalid, unsupported, or over-limit fields refuse the entire form before any question is published. Answers retain the originating request's turn correlation, cancellation, and live execution authority.