From 519f4d9e26da7867f79e1cc3ad748a2ac42a90c0 Mon Sep 17 00:00:00 2001 From: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> Date: Mon, 17 Aug 2026 20:36:19 -0700 Subject: [PATCH 1/2] feat(providers): add openai-chatgpt provider (ChatGPT subscription via OAuth) Wires the official provider-openai-chatgpt module into amplifier-agent's provider catalog. Adds openai-chatgpt as a selectable provider that authenticates via OAuth device-code flow to ChatGPT's backend API, enabling use of Plus/Pro/Team/Enterprise subscriptions without per-token billing. Changes: - loader.py: added openai-chatgpt to _VALID_PROVIDER_MODULES - provider_sources.py: added to KNOWN_PROVIDERS, PROVIDER_CATALOG, and new resolve_credential_detailed branch that checks for OAuth token cache at ~/.amplifier/openai-chatgpt-oauth.json (no api-key env var needed) - auth.py: added openai-chatgpt to _CONFIG_CREDENTIAL_UNSUPPORTED to prevent 'auth set' (device-code flow is the only auth method, like github-copilot) - bundle.md: declared provider-openai-chatgpt in install-only stub list Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- src/amplifier_agent_cli/admin/auth.py | 2 +- src/amplifier_agent_cli/provider_sources.py | 49 ++++++++++++++++++++- src/amplifier_agent_lib/bundle/bundle.md | 2 + src/amplifier_agent_lib/config/loader.py | 4 +- 4 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/amplifier_agent_cli/admin/auth.py b/src/amplifier_agent_cli/admin/auth.py index 53667cf4..f2288c85 100644 --- a/src/amplifier_agent_cli/admin/auth.py +++ b/src/amplifier_agent_cli/admin/auth.py @@ -71,7 +71,7 @@ #: agent-delivered credential from config first, then its env chain, then cached #: OAuth. When that lands, DELETE this constant and its gate in ``auth_set`` #: outright. Do NOT grow it into a general provider-capability mechanism. -_CONFIG_CREDENTIAL_UNSUPPORTED: Final[frozenset[str]] = frozenset({"github-copilot"}) +_CONFIG_CREDENTIAL_UNSUPPORTED: Final[frozenset[str]] = frozenset({"github-copilot", "openai-chatgpt"}) # --------------------------------------------------------------------------- diff --git a/src/amplifier_agent_cli/provider_sources.py b/src/amplifier_agent_cli/provider_sources.py index 3e8ad91b..d4d20d0f 100644 --- a/src/amplifier_agent_cli/provider_sources.py +++ b/src/amplifier_agent_cli/provider_sources.py @@ -105,7 +105,14 @@ def _emit_legacy_env_var_notice(legacy_var: str, preferred_var: str) -> None: #: ``models list --provider ``, or aggregate iteration in admin #: commands) against the supported set. Kept in sync with #: ``PROVIDER_CATALOG.keys()``. -KNOWN_PROVIDERS: Final[tuple[str, ...]] = ("anthropic", "openai", "azure-openai", "ollama", "github-copilot") +KNOWN_PROVIDERS: Final[tuple[str, ...]] = ( + "anthropic", + "openai", + "azure-openai", + "ollama", + "github-copilot", + "openai-chatgpt", +) #: Map provider short-name → bootstrap catalog row. @@ -136,6 +143,10 @@ def _emit_legacy_env_var_notice(legacy_var: str, preferred_var: str) -> None: "module": "provider-github-copilot", "source": "git+https://github.com/microsoft/amplifier-module-provider-github-copilot@main", }, + "openai-chatgpt": { + "module": "provider-openai-chatgpt", + "source": "git+https://github.com/microsoft/amplifier-module-provider-openai-chatgpt@main", + }, } @@ -375,6 +386,42 @@ def resolve_credential_detailed(provider_name: str) -> CredentialResolution: fields={"host": _OLLAMA_DEFAULT_HOST}, ) + if provider_name == "openai-chatgpt": + # ChatGPT subscription provider (provider-openai-chatgpt): authenticates + # via OAuth device-code, NOT an api key. There is no credential env var; + # the module caches its OAuth tokens to a file and refreshes them itself. + # We report "resolvable" iff that token cache exists and parses with a + # token present -- an honest signal of whether a prior device-code login + # happened -- without ever reading or emitting the token material. The + # module's own ``login_on_mount`` drives the interactive device-code flow + # at mount time when no cache exists. ``auth set`` is refused for this + # provider (see _CONFIG_CREDENTIAL_UNSUPPORTED in admin.auth) since there + # is no static key to store. + import json + from pathlib import Path + + token_file = Path("~/.amplifier/openai-chatgpt-oauth.json").expanduser() + try: + data = json.loads(token_file.read_text()) + has_token = isinstance(data, dict) and bool(data.get("access_token") or data.get("refresh_token")) + except (OSError, ValueError): + has_token = False + if has_token: + return CredentialResolution( + provider=provider_name, + resolved=True, + source="file", + env_var=None, + fields={}, + ) + return CredentialResolution( + provider=provider_name, + resolved=False, + source="none", + env_var=None, + fields={}, + ) + env_vars = PROVIDER_CREDENTIAL_VARS.get(provider_name) if not env_vars: return CredentialResolution(provider=provider_name, resolved=False, source="none", env_var=None, fields={}) diff --git a/src/amplifier_agent_lib/bundle/bundle.md b/src/amplifier_agent_lib/bundle/bundle.md index 878e60a1..b42db36a 100644 --- a/src/amplifier_agent_lib/bundle/bundle.md +++ b/src/amplifier_agent_lib/bundle/bundle.md @@ -68,6 +68,8 @@ providers: source: git+https://github.com/microsoft/amplifier-module-provider-ollama@main - module: provider-github-copilot source: git+https://github.com/microsoft/amplifier-module-provider-github-copilot@main + - module: provider-openai-chatgpt + source: git+https://github.com/microsoft/amplifier-module-provider-openai-chatgpt@main session: raw: true diff --git a/src/amplifier_agent_lib/config/loader.py b/src/amplifier_agent_lib/config/loader.py index 64820f69..0db46887 100644 --- a/src/amplifier_agent_lib/config/loader.py +++ b/src/amplifier_agent_lib/config/loader.py @@ -29,7 +29,9 @@ __all__ = ["VALID_APPROVAL_MODES", "ConfigError", "load_config"] _VALID_TOP_LEVEL_KEYS = frozenset({"mcp", "approval", "provider", "providers", "allowProtocolSkew", "skills", "debug"}) -_VALID_PROVIDER_MODULES = frozenset({"anthropic", "openai", "azure-openai", "ollama", "github-copilot"}) +_VALID_PROVIDER_MODULES = frozenset( + {"anthropic", "openai", "azure-openai", "ollama", "github-copilot", "openai-chatgpt"} +) # G3: explicit set of host-supplied approval modes. ``CliApprovalSystem`` accepts # exactly these three strings; any other value must be rejected at parse time # rather than producing a silent fall-through deep in the approval pipeline. From 026c9570ce55367600dfd8f84cab790cfaf06c35 Mon Sep 17 00:00:00 2001 From: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> Date: Mon, 17 Aug 2026 20:36:29 -0700 Subject: [PATCH 2/2] docs(providers): document openai-chatgpt provider Comprehensive documentation across all spec and integration layers for the new openai-chatgpt provider. Updates: - README.md: added to provider matrix - CHANGELOG.md: release note entry - docs/CONFIGURATION.md: configuration and credential handling (device-code OAuth) - docs/spec/providers-and-models.md: provider overview, models, auth flow - docs/spec/host-config.md: host config schema and examples - docs/spec/bundle-and-cache.md: cache behavior for OAuth tokens - docs/spec/cli.md: 'auth' command limitations for device-code flow - docs/LAYERS_AND_RELEASES.md: release metadata - docs/INTEGRATION.md: integration points and callbacks - docs/architecture/architecture.dot: updated provider catalog diagram - skills/amplifier-agent/SKILL.md: skill definition and capability index Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- CHANGELOG.md | 12 ++++++++++++ README.md | 2 +- docs/CONFIGURATION.md | 8 ++++++++ docs/INTEGRATION.md | 2 +- docs/LAYERS_AND_RELEASES.md | 2 +- docs/architecture/architecture.dot | 2 +- docs/spec/bundle-and-cache.md | 2 +- docs/spec/cli.md | 3 ++- docs/spec/host-config.md | 4 ++-- docs/spec/providers-and-models.md | 23 +++++++++++++++++------ skills/amplifier-agent/SKILL.md | 6 +++--- 11 files changed, 49 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86d55dc1..9f9b6f9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- **ChatGPT provider.** `provider.module: "openai-chatgpt"` is now a valid host-config value, + backed by `amplifier-module-provider-openai-chatgpt`. It uses a ChatGPT Plus/Pro/Team + subscription as the backend instead of a per-token API key, talking to the ChatGPT backend + (Codex API) rather than the public OpenAI API. Default model is `gpt-5.5`. + Auth is OAuth device-code, not an environment variable: the provider module drives an + interactive login at mount time (`login_on_mount`, default true) and caches tokens to + `~/.amplifier/openai-chatgpt-oauth.json`, refreshing them itself. Requires "Sign in with + device code" enabled in the account's ChatGPT Security settings. Like `github-copilot`, + `auth set openai-chatgpt` is refused — there is no static key to store. + ## [0.12.0] — 2026-07-29 ### Added diff --git a/README.md b/README.md index ce6ed2de..1b3663c1 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Public integrations run opencode, paperclip, and NanoClaw on it: see [who has in `amplifier-agent` ships with: -- Five providers behind one interface: Anthropic, OpenAI, Azure OpenAI, Ollama, and GitHub Copilot, with credentials read from the environment +- Six providers behind one interface: Anthropic, OpenAI, Azure OpenAI, Ollama, GitHub Copilot, and ChatGPT (a Plus/Pro/Team subscription via OAuth device-code, no API key), with credentials read from the environment or a cached OAuth session - Role-based model routing, so a sub-agent gets a model matched to its job rather than the frontier model for everything, re-matched when you switch providers - Context management that keeps long sessions running, compacting history before it overruns the window - Tools for filesystem, bash, web, search, todo, and MCP diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index b29234e9..c9722535 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -11,6 +11,12 @@ Provider is auto-detected from environment variables in this precedence: 3. `AZURE_OPENAI_API_KEY` + `AZURE_OPENAI_ENDPOINT` 4. `OLLAMA_HOST` (defaults to `http://localhost:11434`) +`github-copilot` and `openai-chatgpt` are excluded from this auto-detect chain -- neither resolves +from a single API-key environment variable. `github-copilot` reads its own token chain from the +environment (see below). `openai-chatgpt` has no credential env var at all: it authenticates via +OAuth device-code, caching tokens to `~/.amplifier/openai-chatgpt-oauth.json`. Both must be +selected explicitly with `provider.module` in a host config file. + Override by passing `--config ` at a host config file that names a provider explicitly. > **Deprecated alias:** `AZURE_OPENAI_KEY` (without `_API_`) is still accepted as a fallback for backwards compatibility and triggers a one-time stderr warning when used. Prefer `AZURE_OPENAI_API_KEY`. The legacy name will be removed in a future release. @@ -58,6 +64,8 @@ Resolution is **env-first**, so existing shell-rc workflows keep working unchang This matters for hosts that spawn `amplifier-agent` as a subprocess: once you have run `auth set` a single time, every subsequent invocation picks the key up automatically, from any terminal, from any directory, with or without exported environment variables. > **`github-copilot` is environment-only.** The other providers receive their credential through the mount config, so `auth set` works for them. The Copilot provider reads its token directly from the environment and ignores the config value, so `auth set github-copilot` is refused rather than storing a token the provider can never see. Set one of these instead (first non-empty wins): `COPILOT_AGENT_TOKEN`, `COPILOT_GITHUB_TOKEN`, `GH_TOKEN`, `GITHUB_TOKEN`. + +> **`openai-chatgpt` has no static key to store, so `auth set openai-chatgpt` is also refused.** It authenticates via OAuth device-code instead: the provider module drives an interactive login at mount time (`login_on_mount`, default true) and caches tokens to `~/.amplifier/openai-chatgpt-oauth.json`, refreshing them itself. Requires "Sign in with device code" enabled in the account's ChatGPT Security settings. > > ```bash > export GITHUB_TOKEN=$(gh auth token) diff --git a/docs/INTEGRATION.md b/docs/INTEGRATION.md index 1ee333e7..15e1a181 100644 --- a/docs/INTEGRATION.md +++ b/docs/INTEGRATION.md @@ -8,7 +8,7 @@ The engine runs **one turn per invocation** and exits. Continuity across turns c `amplifier-agent` is a standalone binary. You do not need the Amplifier CLI, bundles, or any other repository in the `microsoft/amplifier*` family, and none of them is a substitute for it here. -Use it when your software needs to run an agent: a loop with tools, file access, sub-agents, and/or multi-turn state. It also works for plain LLM calls, where you get routing across five providers behind one interface. +Use it when your software needs to run an agent: a loop with tools, file access, sub-agents, and/or multi-turn state. It also works for plain LLM calls, where you get routing across six providers behind one interface. Then pick a surface below, install the engine ([INSTALL.md](INSTALL.md)), and finish with the [checklist](#checklist-for-a-new-integration). diff --git a/docs/LAYERS_AND_RELEASES.md b/docs/LAYERS_AND_RELEASES.md index e29c09ac..accc224f 100644 --- a/docs/LAYERS_AND_RELEASES.md +++ b/docs/LAYERS_AND_RELEASES.md @@ -115,7 +115,7 @@ The engine ships with `bundle.md` baked into the wheel. It declares which module **Pre-wired modules:** -- **Providers:** `provider-anthropic`, `provider-openai`, `provider-azure-openai`, `provider-ollama`, `provider-github-copilot` +- **Providers:** `provider-anthropic`, `provider-openai`, `provider-azure-openai`, `provider-ollama`, `provider-github-copilot`, `provider-openai-chatgpt` - **Orchestrator:** `loop-streaming` (with `extended_thinking: true`) - **Context:** `context-simple` (300K tokens, auto-compact at 80%) - **Tools:** `tool-filesystem`, `tool-bash`, `tool-web`, `tool-search`, `tool-todo`, `tool-apply-patch`, `tool-delegate`, `tool-mcp`, `tool-skills`, `tool-mode`, `tool-recipes` diff --git a/docs/architecture/architecture.dot b/docs/architecture/architecture.dot index e5b66aa2..4e59a9ed 100644 --- a/docs/architecture/architecture.dot +++ b/docs/architecture/architecture.dot @@ -84,7 +84,7 @@ digraph amplifier_agent { node [fillcolor="#d9d9d9"]; kernel [label="amplifier-foundation\nbundle / session kernel"]; - providers [label="LLM providers\nanthropic · openai · azure\nollama · copilot"]; + providers [label="LLM providers\nanthropic · openai · azure\nollama · copilot · chatgpt"]; mcp [label="MCP servers"]; } diff --git a/docs/spec/bundle-and-cache.md b/docs/spec/bundle-and-cache.md index b427f0c3..fb9908e3 100644 --- a/docs/spec/bundle-and-cache.md +++ b/docs/spec/bundle-and-cache.md @@ -30,7 +30,7 @@ default_provider: anthropic REQUIRED, engine-level, top-level key providers: install-only stubs, no config and no credentials provider-anthropic, provider-openai, provider-azure-openai, - provider-ollama, provider-github-copilot + provider-ollama, provider-github-copilot, provider-openai-chatgpt session.orchestrator: loop-streaming extended_thinking: true session.context: context-simple max_tokens 300000, auto_compact diff --git a/docs/spec/cli.md b/docs/spec/cli.md index aa03f7b0..66eb3fa7 100644 --- a/docs/spec/cli.md +++ b/docs/spec/cli.md @@ -197,7 +197,8 @@ serve restart auth set PROVIDER [API_KEY] [--stdin] [--endpoint URL] Writes ~/.amplifier-agent/credentials.json (mode 0600, atomic write). --stdin reads the key from stdin so it never appears in argv. --endpoint carries an Azure-style deployment URL. - `github-copilot` is refused: it reads its token from the environment. + `github-copilot` is refused: it reads its token from the environment. `openai-chatgpt` is + also refused: it has no static key, authenticating instead via OAuth device-code. auth list Per-provider table: masked value plus source (`env=` / `file` / `default` / `not set`). diff --git a/docs/spec/host-config.md b/docs/spec/host-config.md index 11962406..6765718b 100644 --- a/docs/spec/host-config.md +++ b/docs/spec/host-config.md @@ -81,7 +81,7 @@ approval.patterns must be a list of strings `provider` selects the provider module and carries its config. ``` -provider.module one of: anthropic, openai, azure-openai, ollama, github-copilot +provider.module one of: anthropic, openai, azure-openai, ollama, github-copilot, openai-chatgpt provider.config free-form; belongs to the provider module ``` @@ -101,7 +101,7 @@ module config. Closed per-entry schema: } ``` -`module` defaults to the entry's own id when omitted and must be one of the five valid module names. +`module` defaults to the entry's own id when omitted and must be one of the six valid module names. `config` must be an object. Unknown keys inside an entry raise `config_unknown_key`. An empty `providers` object passes validation; HTTP startup rejects it separately at boot so single-turn mode never trips on a stale block. diff --git a/docs/spec/providers-and-models.md b/docs/spec/providers-and-models.md index 91a6ef01..926767fc 100644 --- a/docs/spec/providers-and-models.md +++ b/docs/spec/providers-and-models.md @@ -9,7 +9,7 @@ routes a wire `model` field to a provider (see `http-face.md`). ## Supported providers -Five providers are supported, and only five. The provider name is the value used in configuration, +Six providers are supported, and only six. The provider name is the value used in configuration, in `auth` subcommands, and in `models list --provider`. ``` @@ -18,10 +18,11 @@ openai provider-openai azure-openai provider-azure-openai ollama provider-ollama github-copilot provider-github-copilot +openai-chatgpt provider-openai-chatgpt ``` Each module is installed from `git+https://github.com/microsoft/amplifier-module-@main`. -All five are declared by the shipped bundle as install-only, so preparing the bundle makes every +All six are declared by the shipped bundle as install-only, so preparing the bundle makes every provider importable before any session exists. The agent holds no static table of default models, credential field shapes, or display names. Those @@ -48,6 +49,7 @@ openai OPENAI_API_KEY azure-openai AZURE_OPENAI_API_KEY, then AZURE_OPENAI_KEY ollama OLLAMA_HOST, then OLLAMA_BASE_URL github-copilot GITHUB_TOKEN +openai-chatgpt (none -- OAuth device-code) ``` `AZURE_OPENAI_KEY` is the only deprecated alias. Consulting it emits a one-time warning on stderr. @@ -61,6 +63,11 @@ github-copilot lists only `GITHUB_TOKEN` here. The provider module resolves its (`COPILOT_AGENT_TOKEN`, `COPILOT_GITHUB_TOKEN`, `GH_TOKEN`, `GITHUB_TOKEN`); listing those here would mark them deprecated, which they are not. +openai-chatgpt has no environment variable at all. It resolves from a cached OAuth token file +(`~/.amplifier/openai-chatgpt-oauth.json`), written by the provider module's own device-code login +flow (`login_on_mount`) and refreshed automatically thereafter. Its resolution reports source +`"file"` when a token is cached and `"none"` otherwise -- never `"env"`. + A resolution reports the provider, whether it resolved, the source (`env`, `file`, `default`, or `none`), the variable consulted, and the resolved fields. Ollama backed only by the built-in default host reports unresolved on purpose, so auto-enrollment does not enlist a local daemon that may not @@ -100,9 +107,13 @@ on the next write. Unknown provider keys round-trip verbatim. A malformed file f with an error but resolves as empty on the read path, so one bad write does not break every later invocation. -`auth set github-copilot` is refused. The agent normalizes every credential into an `api_key` -config field, and that provider's module reads only the environment, so a stored value would report -success and change nothing. This refusal is temporary and specific to that one provider. +`auth set github-copilot` and `auth set openai-chatgpt` are both refused, for different reasons. +The agent normalizes every credential into an `api_key` config field: github-copilot's module reads +only the environment and ignores it, so a stored value would report success and change nothing. +openai-chatgpt has no static key at all -- it authenticates via OAuth device-code and caches tokens +to `~/.amplifier/openai-chatgpt-oauth.json`, refreshed by the provider module itself. Both refusals +are enumerated in the same `_CONFIG_CREDENTIAL_UNSUPPORTED` gate; this is temporary and specific to +these two providers. `auth clear` without `--force` exits 2. @@ -114,7 +125,7 @@ success and change nothing. This refusal is temporary and specific to that one p 3. no further fallback: a bundle declaring neither is a hard error at boot ``` -`provider.module` is closed to the five supported names. Any other value fails validation with +`provider.module` is closed to the six supported names. Any other value fails validation with error code `config_invalid_provider_module`. `"auto"` is not a valid value. There is no `--provider` flag and no environment-based provider auto-detection. See Non-goals. diff --git a/skills/amplifier-agent/SKILL.md b/skills/amplifier-agent/SKILL.md index fd2fc998..3a092a0a 100644 --- a/skills/amplifier-agent/SKILL.md +++ b/skills/amplifier-agent/SKILL.md @@ -22,7 +22,7 @@ metadata: `amplifier-agent` is an agent engine that other software runs on. Give it a prompt and it runs the full loop, with tools, sub-agents, skills, and MCP, then returns a result. Anything that can spawn a subprocess can use it; Python hosts can embed the engine library in-process instead. -Reach for it when the project needs an *agent* (a tool loop, file access, sub-agents, multi-turn state) rather than a single completion. You can also use it for plain LLM calls, with routing across five providers behind one interface. +Reach for it when the project needs an *agent* (a tool loop, file access, sub-agents, multi-turn state) rather than a single completion. You can also use it for plain LLM calls, with routing across six providers behind one interface. **The engine runs one turn per invocation and exits.** Continuity across turns comes from a session id, not from a long-lived process. Every surface below is a different way of delivering a prompt to that same engine. @@ -54,7 +54,7 @@ The installer needs `uv` and `curl` and will not bootstrap them silently; it tel Install as **the same user that runs the host process**; a host spawning a subprocess inherits that user's `PATH`. `amplifier-agent doctor` is the check that the install actually works, so run it before writing any integration code. -Credentials are read from the environment, first match wins: `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `AZURE_OPENAI_API_KEY` plus `AZURE_OPENAI_ENDPOINT`, `OLLAMA_HOST`. GitHub Copilot is environment-only (`COPILOT_AGENT_TOKEN`, `COPILOT_GITHUB_TOKEN`, `GH_TOKEN`, `GITHUB_TOKEN`). Or store one with `amplifier-agent auth set anthropic sk-ant-...`. +Credentials are read from the environment, first match wins: `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `AZURE_OPENAI_API_KEY` plus `AZURE_OPENAI_ENDPOINT`, `OLLAMA_HOST`. GitHub Copilot is environment-only (`COPILOT_AGENT_TOKEN`, `COPILOT_GITHUB_TOKEN`, `GH_TOKEN`, `GITHUB_TOKEN`). ChatGPT (`openai-chatgpt`) has no credential env var at all: it authenticates via OAuth device-code, caching tokens to `~/.amplifier/openai-chatgpt-oauth.json`. Or store a static key with `amplifier-agent auth set anthropic sk-ant-...` (not supported for github-copilot or openai-chatgpt). ## Pick a surface @@ -191,7 +191,7 @@ A per-instance config file looks like this: | `config_unreadable`, `config_malformed_json` | The `--config` file could not be opened, or is not a JSON object | Check the path the host wrote, and that it serialized an object | | `config_unknown_key` | Unrecognized **top-level** config key | The top level is closed: `approval`, `provider`, `providers`, `mcp`, `skills`, `debug`, `allowProtocolSkew` | | `config_invalid_type` | A known key has the wrong type, or an unknown sub-key in a closed inner shape | `skills.*` and `debug.*` are closed and raise this rather than `config_unknown_key`, which is reserved for the top level and `providers.` entries | -| `config_invalid_provider_module` | `provider.module` is not a known provider | One of `anthropic`, `openai`, `azure-openai`, `ollama`, `github-copilot`. `"auto"` is not valid | +| `config_invalid_provider_module` | `provider.module` is not a known provider | One of `anthropic`, `openai`, `azure-openai`, `ollama`, `github-copilot`, `openai-chatgpt`. `"auto"` is not valid | | `protocol_version_mismatch` | Wrapper and engine protocol versions differ | Update the lagging side. `allowProtocolSkew` is an unblock, not a fix | | `lifecycle_unsupported` | `submit()` called twice on one handle | New handle per turn, same `sessionId` with `resume` | | `env_injection_rejected` | The wrapper refused the environment you asked it to inject | Check the key against the wrapper's allowlist and blocked-key list |