Skip to content

Commit a1ac559

Browse files
authored
feat(codex): enable native plugin app support (#78733)
* feat(codex): add native plugin config schema * feat(codex): add native plugin inventory activation * feat(codex): configure native plugin apps for threads * feat(codex): enforce plugin elicitation policy * feat(codex): migrate native plugins * docs(codex): document native plugin support * fix(codex): harden plugin migration refresh * fix(codex): satisfy plugin activation lint * fix: stabilize codex plugin app config * fix: address codex plugin review feedback * fix: key codex plugin app cache by websocket credentials * fix: keep codex plugin app fingerprints stable * fix: refresh codex plugin cache test fixtures * fix: refresh plugin app readiness after activation * fix: support remote codex plugin activation * fix: recover plugin app bindings after cache refresh * fix: force codex app refresh after plugin activation * fix: recover partial codex plugin app bindings * fix: sync codex plugin selection config * fix: keep codex plugin activation fail closed * fix: align codex plugin protocol types with main * fix: refresh partial codex plugin app bindings * fix: key codex app cache by env api key * fix: skip failed codex plugin migration config * test: update codex prompt snapshots * fix: fail closed on missing codex app inventory entries * fix(codex): enforce native plugin policy gates * fix(codex): normalize native plugin policy types * fix(codex): fail closed on plugin refresh errors * fix(codex): use native plugin destructive policy * fix(codex): key plugin cache by api-key profiles * fix(codex): drop unshipped plugin fingerprint compat * fix(codex): let native app policy gate plugin tools * fix(codex): allow open-world plugin app tools * fix(codex): revalidate native plugin app bindings * fix(codex): preserve plugin binding on recheck failure * docs(codex): clarify plugin harness scope * fix(codex): return activation report state exhaustively * test(codex): refresh prompt snapshots after rebase * fix(codex): match namespaced plugin ids
1 parent b75e5c5 commit a1ac559

40 files changed

Lines changed: 7307 additions & 187 deletions

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ Docs: https://docs.openclaw.ai
3131
- Gateway/sessions: fast-path already-qualified model refs while building session-list rows so `openclaw sessions` and Control UI session lists avoid heavyweight model resolution on large stores. (#77902) Thanks @ragesaq.
3232
- Contributor PRs: remind external contributors to redact private information like IP addresses, API keys, phone numbers, and non-public endpoints from real behavior proof. Thanks @pashpashpash.
3333
- Codex/approvals: in Codex approval modes, stop installing the pre-guardian native `PermissionRequest` hook by default so Codex's reviewer can approve safe commands before OpenClaw surfaces an approval, remember `allow-always` decisions for identical Codex native `PermissionRequest` payloads within the active session window, and make plugin approval requests validate/render their actual allowed decisions so Telegram and other native approval UIs cannot offer stale actions. Thanks @shakkernerd.
34+
- Codex/plugins: enable migrated source-installed `openai-curated` Codex plugins in the same Codex harness thread with explicit `codexPlugins` config, cached app readiness, and fail-closed destructive-action policy. Thanks @kevinslin.
35+
- Codex/plugins: enforce native plugin destructive-action policy with Codex app-level `destructive_enabled` config instead of OpenClaw-maintained per-tool deny lists, leave plugin app `open_world_enabled` on by default, and invalidate existing plugin app thread bindings so old generated app config is rebuilt. Thanks @kevinslin.
3436
- PR triage: mark external pull requests with `proof: supplied` when Barnacle finds structured real behavior proof, keep stale negative proof labels in sync across CRLF-edited PR bodies, and let ClawSweeper own the stronger `proof: sufficient` judgement.
3537
- Sessions CLI: show the selected agent runtime in the `openclaw sessions` table so terminal output matches the runtime visibility already present in JSON/status surfaces. Thanks @vincentkoc.
3638
- ACPX/Codex: preserve trusted Codex project declarations when launching isolated Codex ACP sessions, avoiding interactive trust prompts in headless runs. Thanks @Stedyclaw.

docs/cli/migrate.md

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ openclaw migrate list
2121
openclaw migrate claude --dry-run
2222
openclaw migrate codex --dry-run
2323
openclaw migrate codex --skill gog-vault77-google-workspace
24+
openclaw migrate codex --plugin google-calendar --dry-run
2425
openclaw migrate hermes --dry-run
2526
openclaw migrate hermes
2627
openclaw migrate apply codex --yes --skill gog-vault77-google-workspace
28+
openclaw migrate apply codex --yes --plugin google-calendar
2729
openclaw migrate apply codex --yes
2830
openclaw migrate apply claude --yes
2931
openclaw migrate apply hermes --yes
@@ -54,6 +56,9 @@ openclaw onboard --import-from hermes --import-source ~/.hermes
5456
<ParamField path="--skill <name>" type="string">
5557
Select one skill copy item by skill name or item id. Repeat the flag to migrate multiple skills. When omitted, interactive Codex migrations show a checkbox selector and non-interactive migrations keep all planned skills.
5658
</ParamField>
59+
<ParamField path="--plugin <name>" type="string">
60+
Select one Codex plugin install item by plugin name or item id. Repeat the flag to migrate multiple Codex plugins. This only applies to source-installed `openai-curated` Codex plugins discovered by the Codex app-server inventory.
61+
</ParamField>
5762
<ParamField path="--no-backup" type="boolean">
5863
Skip the pre-apply backup. Requires `--force` when local OpenClaw state exists.
5964
</ParamField>
@@ -129,20 +134,51 @@ openclaw migrate codex --dry-run --skill gog-vault77-google-workspace
129134
openclaw migrate apply codex --yes --skill gog-vault77-google-workspace
130135
```
131136

137+
Use `--plugin <name>` to limit native Codex plugin migration to one or more
138+
source-installed curated plugins:
139+
140+
```bash
141+
openclaw migrate codex --dry-run --plugin google-calendar
142+
openclaw migrate apply codex --yes --plugin google-calendar
143+
```
144+
132145
### What Codex imports
133146

134147
- Codex CLI skill directories under `$CODEX_HOME/skills`, excluding Codex's
135148
`.system` cache.
136149
- Personal AgentSkills under `$HOME/.agents/skills`, copied into the current
137150
OpenClaw agent workspace when you want per-agent ownership.
151+
- Source-installed `openai-curated` Codex plugins discovered through Codex
152+
app-server `plugin/list`. Apply calls app-server `plugin/install` for each
153+
selected plugin, even if the target app-server already reports that plugin as
154+
installed and enabled. Migrated Codex plugins are usable only in sessions that
155+
select the native Codex harness; they are not exposed to Pi, normal OpenAI
156+
provider runs, ACP conversation bindings, or other harnesses.
138157

139158
### Manual-review Codex state
140159

141-
Codex native plugins, `config.toml`, and native `hooks/hooks.json` are not
142-
activated automatically. Plugins may expose MCP servers, apps, hooks, or other
143-
executable behavior, so the provider reports them for review instead of loading
144-
them into OpenClaw. Config and hook files are copied into the migration report
145-
for manual review.
160+
Codex `config.toml`, native `hooks/hooks.json`, non-curated marketplaces, and
161+
cached plugin bundles that are not source-installed curated plugins are not
162+
activated automatically. They are copied or reported in the migration report for
163+
manual review.
164+
165+
For migrated source-installed curated plugins, apply writes:
166+
167+
- `plugins.entries.codex.enabled: true`
168+
- `plugins.entries.codex.config.codexPlugins.enabled: true`
169+
- `plugins.entries.codex.config.codexPlugins.allow_destructive_actions: false`
170+
- one explicit plugin entry with `marketplaceName: "openai-curated"` and
171+
`pluginName` for each selected plugin
172+
173+
Migration never writes `plugins["*"]` and never stores local marketplace cache
174+
paths. Auth-required installs are reported on the affected plugin item with
175+
`status: "skipped"`, `reason: "auth_required"`, and sanitized app identifiers.
176+
Their explicit config entries are written disabled until you reauthorize and
177+
enable them. Other install failures are item-scoped `error` results.
178+
179+
If Codex app-server plugin inventory is unavailable during planning, migration
180+
falls back to cached bundle advisory items instead of failing the whole
181+
migration.
146182

147183
## Hermes provider
148184

docs/gateway/configuration-reference.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,70 @@ See [MCP](/cli/mcp#openclaw-as-an-mcp-client-registry) and
200200
- `plugins.entries.<id>.subagent.allowedModels`: optional allowlist of canonical `provider/model` targets for trusted subagent overrides. Use `"*"` only when you intentionally want to allow any model.
201201
- `plugins.entries.<id>.config`: plugin-defined config object (validated by native OpenClaw plugin schema when available).
202202
- Channel plugin account/runtime settings live under `channels.<id>` and should be described by the owning plugin's manifest `channelConfigs` metadata, not by a central OpenClaw option registry.
203+
204+
### Codex harness plugin config
205+
206+
The bundled `codex` plugin owns native Codex app-server harness settings under
207+
`plugins.entries.codex.config`. See [Codex harness](/plugins/codex-harness) for
208+
the full runtime model.
209+
210+
`codexPlugins` applies only to sessions that select the native Codex harness.
211+
It does not enable Codex plugins for Pi, normal OpenAI provider runs, ACP
212+
conversation bindings, or any non-Codex harness.
213+
214+
```json5
215+
{
216+
plugins: {
217+
entries: {
218+
codex: {
219+
enabled: true,
220+
config: {
221+
codexPlugins: {
222+
enabled: true,
223+
allow_destructive_actions: false,
224+
plugins: {
225+
"google-calendar": {
226+
enabled: true,
227+
marketplaceName: "openai-curated",
228+
pluginName: "google-calendar",
229+
allow_destructive_actions: false,
230+
},
231+
},
232+
},
233+
},
234+
},
235+
},
236+
},
237+
}
238+
```
239+
240+
- `plugins.entries.codex.config.codexPlugins.enabled`: enables native Codex
241+
plugin/app support for the Codex harness. Default: `false`.
242+
- `plugins.entries.codex.config.codexPlugins.allow_destructive_actions`:
243+
default destructive-action policy for migrated plugin app elicitations.
244+
Default: `false`.
245+
- `plugins.entries.codex.config.codexPlugins.plugins.<key>.enabled`: enables a
246+
migrated plugin entry when global `codexPlugins.enabled` is also true.
247+
Default: `true` for explicit entries.
248+
- `plugins.entries.codex.config.codexPlugins.plugins.<key>.marketplaceName`:
249+
stable marketplace identity. V1 only supports `"openai-curated"`.
250+
- `plugins.entries.codex.config.codexPlugins.plugins.<key>.pluginName`: stable
251+
Codex plugin identity from migration, for example `"google-calendar"`.
252+
- `plugins.entries.codex.config.codexPlugins.plugins.<key>.allow_destructive_actions`:
253+
per-plugin destructive-action override. When omitted, the global
254+
`allow_destructive_actions` value is used.
255+
256+
`codexPlugins.enabled` is the global enablement directive. Explicit plugin
257+
entries written by migration are the durable install and repair eligibility set.
258+
`plugins["*"]` is not supported, there is no `install` switch, and local
259+
`marketplacePath` values are intentionally not config fields because they are
260+
host-specific.
261+
262+
`app/list` readiness checks are cached for one hour and refreshed
263+
asynchronously when stale. Codex thread app config is computed at Codex harness
264+
session establishment, not on every turn; use `/new`, `/reset`, or a gateway
265+
restart after changing native plugin config.
266+
203267
- `plugins.entries.firecrawl.config.webFetch`: Firecrawl web-fetch provider settings.
204268
- `apiKey`: Firecrawl API key (accepts SecretRef). Falls back to `plugins.entries.firecrawl.config.webSearch.apiKey`, legacy `tools.web.fetch.firecrawl.apiKey`, or `FIRECRAWL_API_KEY` env var.
205269
- `baseUrl`: Firecrawl API base URL (default: `https://api.firecrawl.dev`; self-hosted overrides must target private/internal endpoints).

docs/plugins/codex-harness.md

Lines changed: 106 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -563,9 +563,11 @@ openclaw migrate apply codex --yes
563563
```
564564

565565
The Codex migration provider copies skills into the current OpenClaw agent
566-
workspace. Codex native plugins, hooks, and config files are reported or archived
567-
for manual review instead of being activated automatically, because they can
568-
execute commands, expose MCP servers, or carry credentials.
566+
workspace. For source-installed `openai-curated` Codex plugins, migration also
567+
calls Codex app-server `plugin/install` and records explicit native plugin
568+
config under `plugins.entries.codex.config.codexPlugins`. Codex config files,
569+
hooks, and cached plugin bundles that are not source-installed curated plugins
570+
remain report-only manual-review items.
569571

570572
Auth is selected in this order:
571573

@@ -629,6 +631,7 @@ Supported top-level Codex plugin fields:
629631
| `codexDynamicToolsProfile` | `"native-first"` | Use `"openclaw-compat"` to expose the full OpenClaw dynamic tool set to Codex app-server. |
630632
| `codexDynamicToolsLoading` | `"searchable"` | Use `"direct"` to put OpenClaw dynamic tools directly in the initial Codex tool context. |
631633
| `codexDynamicToolsExclude` | `[]` | Additional OpenClaw dynamic tool names to omit from Codex app-server turns. |
634+
| `codexPlugins` | disabled | Native Codex plugin/app support for migrated source-installed curated plugins. |
632635

633636
Supported `appServer` fields:
634637

@@ -684,6 +687,106 @@ Environment overrides remain available for local testing:
684687
preferred for repeatable deployments because it keeps the plugin behavior in the
685688
same reviewed file as the rest of the Codex harness setup.
686689

690+
## Native Codex plugins
691+
692+
Native Codex plugin support uses Codex app-server's own app and plugin
693+
capabilities in the same Codex thread as the OpenClaw harness turn. OpenClaw
694+
does not translate Codex plugins into synthetic `codex_plugin_*` OpenClaw
695+
dynamic tools. That keeps plugin calls in the native Codex transcript and avoids
696+
starting a second ephemeral Codex thread for each plugin invocation.
697+
698+
Codex plugins only work when the selected OpenClaw agent runtime is the native
699+
Codex harness. The `codexPlugins` config has no effect on Pi runs, normal
700+
OpenAI provider runs, ACP conversation bindings, or other harnesses, because
701+
those paths do not create Codex app-server threads with native `apps` config.
702+
703+
V1 support is intentionally narrow:
704+
705+
- Only `openai-curated` plugins that were already installed in the source Codex
706+
app-server inventory are migration-eligible.
707+
- Migration writes explicit plugin identities with `marketplaceName` and
708+
`pluginName`; it does not write local `marketplacePath` cache paths.
709+
- `codexPlugins.enabled` is the global enablement switch. There is no
710+
`plugins["*"]` wildcard and no config key that grants arbitrary install
711+
authority.
712+
- Unsupported marketplaces, cached plugin bundles, hooks, and Codex config files
713+
are preserved in the migration report for manual review.
714+
715+
Example migrated config:
716+
717+
```json5
718+
{
719+
plugins: {
720+
entries: {
721+
codex: {
722+
enabled: true,
723+
config: {
724+
codexPlugins: {
725+
enabled: true,
726+
allow_destructive_actions: false,
727+
plugins: {
728+
"google-calendar": {
729+
enabled: true,
730+
marketplaceName: "openai-curated",
731+
pluginName: "google-calendar",
732+
},
733+
},
734+
},
735+
},
736+
},
737+
},
738+
},
739+
}
740+
```
741+
742+
Thread app config is computed when OpenClaw establishes a Codex harness session
743+
or replaces a stale Codex thread binding. It is not recomputed on every turn.
744+
After changing `codexPlugins`, use `/new`, `/reset`, or restart the gateway so
745+
future Codex harness sessions start with the updated app set.
746+
747+
OpenClaw reads Codex app inventory through app-server `app/list`, caches it for
748+
one hour, and refreshes stale or missing entries asynchronously. A plugin app is
749+
exposed only when OpenClaw can map it back to the migrated plugin through stable
750+
ownership: an exact app id from plugin detail, a known MCP server name, or
751+
unique stable metadata. Display-name-only or ambiguous ownership is excluded
752+
until the next inventory refresh proves ownership.
753+
754+
Plugin-owned app tools use Codex's native app configuration. OpenClaw injects a
755+
restrictive `config.apps` patch for the Codex thread: `_default` is disabled and
756+
only apps owned by enabled migrated plugins are enabled. OpenClaw sets
757+
app-level `destructive_enabled` from the effective global/per-plugin
758+
`allow_destructive_actions` policy and lets Codex enforce destructive tool
759+
metadata from its native app tool annotations. Plugin apps are emitted with
760+
`open_world_enabled: true`; OpenClaw does not expose a separate plugin
761+
open-world policy knob. OpenClaw does not maintain per-plugin destructive
762+
tool-name deny lists. Tool approval mode is prompted by default for plugin
763+
apps, because OpenClaw does not have an interactive app-elicitation UI in this
764+
same-thread path.
765+
766+
Destructive plugin elicitations fail closed by default:
767+
768+
- Global `allow_destructive_actions` defaults to `false`.
769+
- Per-plugin `allow_destructive_actions` overrides the global policy for that
770+
plugin.
771+
- When policy is `false`, OpenClaw returns a deterministic decline.
772+
- When policy is `true`, OpenClaw auto-accepts only safe schemas it can map to
773+
an approval response, such as a boolean approve field.
774+
- Missing plugin identity, ambiguous ownership, a missing turn id, a wrong turn
775+
id, or an unsafe elicitation schema declines instead of prompting.
776+
777+
Common diagnostics:
778+
779+
- `auth_required`: migration installed the plugin but one of its apps still
780+
needs authentication. The explicit plugin entry is written disabled until you
781+
reauthorize and enable it.
782+
- `marketplace_missing` or `plugin_missing`: the target Codex app-server cannot
783+
see the expected `openai-curated` marketplace or plugin.
784+
- `app_inventory_missing` or `app_inventory_stale`: app readiness came from an
785+
empty or stale cache; OpenClaw schedules an async refresh and excludes plugin
786+
apps until ownership/readiness is known.
787+
- `app_ownership_ambiguous`: app inventory only matched by display name, so the
788+
app is not exposed to the Codex thread.
789+
687790
## Computer use
688791

689792
Computer Use is covered in its own setup guide:

extensions/codex/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export default definePluginEntry({
2929
api.registerMediaUnderstandingProvider(
3030
buildCodexMediaUnderstandingProvider({ pluginConfig: api.pluginConfig }),
3131
);
32-
api.registerMigrationProvider(buildCodexMigrationProvider());
32+
api.registerMigrationProvider(buildCodexMigrationProvider({ runtime: api.runtime }));
3333
api.registerCommand(createCodexCommand({ pluginConfig: api.pluginConfig }));
3434
api.on("inbound_claim", (event, ctx) =>
3535
handleCodexConversationInboundClaim(event, ctx, {

extensions/codex/openclaw.plugin.json

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,42 @@
9696
}
9797
}
9898
},
99+
"codexPlugins": {
100+
"type": "object",
101+
"additionalProperties": false,
102+
"properties": {
103+
"enabled": {
104+
"type": "boolean",
105+
"default": false
106+
},
107+
"allow_destructive_actions": {
108+
"type": "boolean",
109+
"default": false
110+
},
111+
"plugins": {
112+
"type": "object",
113+
"additionalProperties": {
114+
"type": "object",
115+
"additionalProperties": false,
116+
"properties": {
117+
"enabled": {
118+
"type": "boolean"
119+
},
120+
"marketplaceName": {
121+
"type": "string",
122+
"enum": ["openai-curated"]
123+
},
124+
"pluginName": {
125+
"type": "string"
126+
},
127+
"allow_destructive_actions": {
128+
"type": "boolean"
129+
}
130+
}
131+
}
132+
}
133+
}
134+
},
99135
"appServer": {
100136
"type": "object",
101137
"additionalProperties": false,
@@ -234,6 +270,26 @@
234270
"help": "MCP server name exposed by the Computer Use plugin.",
235271
"advanced": true
236272
},
273+
"codexPlugins": {
274+
"label": "Native Codex Plugins",
275+
"help": "Controls native Codex plugin availability for Codex harness turns.",
276+
"advanced": true
277+
},
278+
"codexPlugins.enabled": {
279+
"label": "Enable Native Plugins",
280+
"help": "Expose explicit migrated Codex plugin entries to Codex harness turns.",
281+
"advanced": true
282+
},
283+
"codexPlugins.allow_destructive_actions": {
284+
"label": "Allow Destructive Plugin Actions",
285+
"help": "Default policy for plugin app write or destructive action elicitations. Defaults to false.",
286+
"advanced": true
287+
},
288+
"codexPlugins.plugins": {
289+
"label": "Migrated Plugin Entries",
290+
"help": "Explicit migration-authored plugin entries. The wildcard key * is not supported.",
291+
"advanced": true
292+
},
237293
"appServer": {
238294
"label": "App Server",
239295
"help": "Runtime controls for connecting to Codex app-server.",

0 commit comments

Comments
 (0)