codex-copilot-dx 0.9.12 → 0.9.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # Documentation index
2
+
3
+ The [root README](../README.md) is the quick start; the [command/configuration
4
+ reference](REFERENCE.md) preserves complete current behavior and limits. `ccdx --help` and
5
+ `ccdx <command> --help` describe the installed package's CLI. Code and tests
6
+ resolve any discrepancy; CLI version and running-adapter version can differ.
7
+
8
+ `ccdx doctor config` performs local, read-only configuration diagnostics and
9
+ previews the current startup writer. It does not claim to resolve the complete
10
+ effective Codex configuration or validate every field against the installed app.
11
+
12
+ ## Historical release evidence
13
+
14
+ These reports record a specific release and test environment. Their version,
15
+ test counts, timings, live-provider observations, and limitations remain
16
+ historical facts rather than current guarantees.
17
+
18
+ - [0.9.13: model-bound Auto-review reasoning selection](RELEASE_0.9.13.md)
19
+ - [0.9.12: GPT-6.1 Sol / low Auto-review default](RELEASE_0.9.12.md)
20
+ - [0.9.11: bounded usage-summary cache and diagnostic redaction](RELEASE_0.9.11.md)
21
+ - [0.9.10: agent messages, opaque-state tests and bounded request timelines](RELEASE_0.9.10.md)
22
+
23
+ - [0.9.9: English and Simplified Chinese dashboard](RELEASE_0.9.9.md)
24
+ - [0.9.8: underscore account names in the dashboard](RELEASE_0.9.8.md)
25
+ - [0.9.7: saved GitHub account in the dashboard header](RELEASE_0.9.7.md)
26
+ - [0.9.6: lazy dashboard controls and daily usage analytics](RELEASE_0.9.6.md)
27
+ - [0.9.5: cache hit rates with incomplete history](RELEASE_0.9.5.md)
28
+ - [0.9.4: token cache hit rate in usage and dashboard](RELEASE_0.9.4.md)
29
+ - [0.9.3: GPT-6.1 Sol Codex catalog compatibility](RELEASE_0.9.3.md)
30
+ - [0.9.2: read-only runtime limits in the dashboard](RELEASE_0.9.2.md)
31
+ - [0.9.1: safe Qwen data-URI image delivery](RELEASE_0.9.1.md)
32
+ - [0.9.0: opt-in decoded request-body limit CLI](RELEASE_0.9.0.md)
33
+ - [0.8.11: theme-aware double-peak SVG favicon](RELEASE_0.8.11.md)
34
+ - [0.8.10: borderless theme toggle and fuller moon icon](RELEASE_0.8.10.md)
35
+ - [0.8.9: local dashboard light and dark themes](RELEASE_0.8.9.md)
36
+ - [0.8.8: local dashboard with animation, live models, and usage](RELEASE_0.8.8.md)
37
+ - [0.8.7: faithful Chat fallback and response-outcome diagnostics](RELEASE_0.8.7.md)
38
+ - [0.8.6: bounded large-body memory and history accounting improvements](RELEASE_0.8.6.md)
39
+ - [0.8.5: GPT-6 Sol/Luna compatibility and runtime cache control](RELEASE_0.8.5.md)
40
+ - [0.8.4: bounded audit fixes and safety verification](RELEASE_0.8.4.md)
41
+ - [0.8.3: image configuration recovery and core-service isolation](RELEASE_0.8.3.md)
42
+ - [0.8.2: progressive image guidance and opt-in lifecycle](RELEASE_0.8.2.md)
43
+ - [0.8.1: chat image delivery and image-provider base URLs](RELEASE_0.8.1.md)
44
+ - [0.8.0: configuration safety, bounded failure recovery, and image integration hardening](RELEASE_0.8.0.md)
45
+ - [0.7.10: generated-image editing and simultaneous animation previews](RELEASE_0.7.10.md)
46
+ - [0.7.9: image tool discovery and invocation](RELEASE_0.7.9.md)
47
+ - [0.7.8: streamed failure recovery and optional image generation](RELEASE_0.7.8.md)
48
+ - [0.7.7: terminal animation additions and Braille retirement](RELEASE_0.7.7.md)
49
+ - [0.7.6: configuration doctor and documentation audit](RELEASE_0.7.6.md)
50
+ - [0.7.5: context-management default](RELEASE_0.7.5.md)
51
+ - [0.7.4: preparation timing and performance checks](RELEASE_0.7.4.md)
52
+ - [0.7.3: native Responses message identity](RELEASE_0.7.3.md)
53
+
54
+ The [benchmark-fixture provenance](../scripts/fixtures/README.md) documents the
55
+ photo source and distinguishes it from the generated screenshot fixture.
@@ -0,0 +1,481 @@
1
+ # CCDX command, configuration and compatibility reference
2
+
3
+ Use Codex App with GPT models available to your **GitHub Copilot** account.
4
+
5
+ CCDX is a local compatibility adapter for Codex App. It configures Codex to use an OpenAI Responses API on loopback while GitHub authentication, model discovery, and inference stay backed by GitHub Copilot.
6
+
7
+ ## How it works
8
+
9
+ One in-process adapter listens on `127.0.0.1:2026` by default and exposes:
10
+
11
+ - `POST /v1/responses`
12
+ - `POST /v1/responses/compact`
13
+ - `GET /v1/models`
14
+
15
+ Responses-only models and compaction use Copilot's Responses transport directly. For requests routed through Chat Completions, CCDX converts the supported text, image, and function-tool subset and rejects incompatible message content rather than turning it into JSON text. Chat `length` and `content_filter` finish reasons become incomplete Responses in both streaming and non-streaming modes. Both streaming SSE and non-streaming responses are supported.
16
+
17
+ The adapter handles response history, encrypted reasoning state, function/custom tools, images, compaction, cancellation, retries, and usage metadata within the compatibility boundaries below. Routing prefers endpoint metadata from Copilot's live or cached catalog; when metadata is unavailable, a small built-in set identifies known Responses-only GPT models.
18
+
19
+ ### Fast and Standard modes
20
+
21
+ Codex can select Standard or Fast without changing CCDX configuration. When the client requests a model with `service_tier: "priority"`, CCDX uses its exact `-fast` model only when the live Copilot catalog advertises that variant as enabled, selectable, OpenAI-owned, and Responses-only. Standard mode continues to use the base model; CCDX does not invent Fast availability when the upstream catalog does not provide it. This retains the established `gpt-5.6-sol` / `gpt-5.6-sol-fast` mapping and applies the same exact-match rule to future models.
22
+
23
+ Codex Auto-review uses the hidden `codex-auto-review` model ID. CCDX maps it to Copilot's `gpt-6.1-sol` Responses model by default, independently of the interactive Fast/Standard selection, and logs both model IDs when the mapping is used. Without a saved reasoning override, an omitted Auto-review effort defaults to `low`; explicit client efforts are preserved. A saved override changes only review effort, preserving other reasoning fields. The review target and effort can be changed interactively without reinstalling or restarting a current CCDX adapter.
24
+
25
+ ### GPT-6 model catalog
26
+
27
+ Codex App requests a versioned, capability-rich model catalog that is different from Copilot's raw model list. CCDX reads the complete catalog bundled with the installed Codex App and preserves every other model entry and capability. It exposes each managed GPT-6 entry only when Copilot advertises that exact model as enabled, selectable, OpenAI-owned, and Responses-capable.
28
+
29
+ When an installed 0.155.x catalog does not yet contain `gpt-6-sol` or `gpt-6-luna`, CCDX derives their client-compatible entries from that catalog's GPT-6 Astra schema and the published OpenAI Codex model definitions at commit `0a2eb4696c26ac33204bcd255721ab30220a4774`. When an installed 0.158+ catalog contains `gpt-6-sol` but not `gpt-6.1-sol`, CCDX derives only the missing 6.1 entry from that client's Sol schema and OpenAI Codex model identity/defaults at commit `ab84d71f5767e4a565ce81c2c426287cb48c7918`. Fallback reasoning controls are intersected with Copilot's advertised efforts; every other bundled entry retains its established behavior. A newer client-provided entry supplies its schema and future fields while CCDX still gates its visibility, speed tiers, and 6.1 Sol reasoning controls against Copilot.
30
+
31
+ CCDX exposes GPT-6 Standard paths and removes unverified speed-tier metadata. Existing GPT-6 bundled reasoning-effort options remain unchanged; 6.1 Sol is limited to efforts advertised by Copilot. Fast follows the exact upstream-variant rule above. Versioned model discovery matches the bundled catalog to the client's `client_version`, including the nested Codex CLI binary in current macOS App bundles, caches it by application binary identity, and reloads it after an App update. If a matching usable catalog cannot be loaded, the versioned models request returns `503`; an unversioned request returns the Copilot-style list.
32
+
33
+ `/v1/models` still attempts live discovery first. With a usable in-process catalog, it limits that upstream wait to 1.5 seconds (or a shorter configured upstream timeout) before falling back to last-known-good data. Without a catalog it retains the regular upstream timeout. Received HTTP `401`/`403` responses remain errors, not cached successes; fallback does not grant inference access. This does not change the explicitly live `ccdx models` command.
34
+
35
+ ## Prerequisites
36
+
37
+ - A GitHub account with Copilot access to the desired models
38
+ - Node.js 22.15 or newer, for built-in Zstandard request decompression
39
+ - Codex App on macOS
40
+
41
+ ## Install and start
42
+
43
+ ```bash
44
+ npm install -g codex-copilot-dx@latest
45
+ ccdx
46
+ ```
47
+
48
+ `ccdx` is the primary launcher. The old `codex-copilot-dx` executable remains temporarily as a compatibility shim: it prints a deprecation warning at most once every seven days in interactive terminals, runs `ccdx`, and remains silent in scripts. The warning timestamp is stored under `XDG_CACHE_HOME` or `~/.cache`; `doctor config` bypasses that warning cache to remain read-only. The alias will be removed in a future breaking release.
49
+
50
+ For a one-off run without a global install:
51
+
52
+ ```bash
53
+ npm exec --yes --package=codex-copilot-dx@latest -- ccdx
54
+ ```
55
+
56
+ On a normal launch, CCDX:
57
+
58
+ 1. Prints the installed package version and checks for a newer npm release in the background.
59
+ 2. Reuses a compatible running adapter when available; otherwise it reuses a compatible local Copilot credential or starts GitHub Device Flow when authentication is required.
60
+ 3. Loads a usable model cache or attempts a live refresh, then starts listening. A cache older than two hours refreshes in the background; a fresh cache can be used immediately. A failed refresh retains the last-known-good list when one is available.
61
+ 4. Checks the startup-managed settings in `~/.codex/config.toml`, including the local `/v1` endpoint.
62
+ 5. Attempts to open Codex App on macOS unless `CCDX_AUTO_LAUNCH` disables it.
63
+
64
+ The `OPENAI_API_KEY=dummy` value used in the Codex shell environment is only a client-side placeholder. It is not a GitHub credential. CCDX exchanges the saved GitHub OAuth credential for short-lived Copilot service tokens internally.
65
+
66
+ On startup, CCDX also adds `context_management = true` under `[features]` when the key is missing, creating the section if needed. Existing values, including `false`, are preserved. Existing inline or dotted `features` declarations and nested context-management settings are left intact to avoid conflicting TOML definitions. This opts unconfigured users into Codex context management; support and behavior depend on the installed Codex version. A configuration that already matches is not rewritten.
67
+
68
+ If a compatible adapter is already running, a later launch reuses it and checks the local Codex configuration. Reuse requires the same package version, protocol version, and required capabilities. After updating the package, stop the old adapter before starting the new version. Installing or updating the package alone does not restart the adapter or update Codex configuration.
69
+
70
+ ### Codex configuration writes
71
+
72
+ Normal startup and reuse of an existing adapter check `~/.codex/config.toml` once, after startup prerequisites succeed. There is no file watcher or periodic configuration rewrite. The path is based on the OS home directory; CCDX does not redirect it through `CODEX_HOME` or edit project-level Codex files.
73
+
74
+ | Setting | Existing file | New file |
75
+ | --- | --- | --- |
76
+ | Top-level `openai_base_url` | Add or update to the adapter's `/v1` address | Set to the adapter address |
77
+ | `model_context_window` | Add `1000000` only when missing | `1000000` |
78
+ | `model_auto_compact_token_limit` | Add `900000` only when missing | `900000` |
79
+ | `[shell_environment_policy.set]` OpenAI URL/key | Update `OPENAI_BASE_URL` and set `OPENAI_API_KEY="dummy"` when this section is recognized; leave an absent section absent | Create the section and set both keys; set `shell_environment_policy.inherit="core"` |
80
+ | `[features]` `context_management` | Add `true` when missing; preserve explicit values and alternative declarations as described above | `true` |
81
+ | `[mcp_servers.ccdx_image]` | Added in a marked CCDX-owned block only after `ccdx enable-image`; removed by `ccdx disable-image` | Absent by default |
82
+
83
+ The two token limits are fixed defaults, not detected model capabilities. Existing custom values are retained. Writes target the listed keys with format-preserving edits and atomic replacement; this is not a general TOML formatter. Quoted keys, quoted table headers, and explicit dotted shell-environment declarations are recognized; inline shell-environment tables are left intact. Startup and image configuration commands validate UTF-8/TOML and verify that unrelated settings retain their values before writing. An unsafe edit leaves the original file unchanged. Use `ccdx doctor config` to inspect parsed values and preview what this writer would do with the current file. No actual change means no file rewrite.
84
+
85
+ Interactive image setup rereads the shared config after prompts and network validation, so unrelated changes made during setup are preserved. It checks the snapshot again before committing and rejects conflicting image declarations. Identical configuration bytes are not replaced; private file permissions may still be corrected. These checks reduce lost-update risks but are not a filesystem-wide transaction with other applications that do not share a lock.
86
+
87
+ ## Version 0.7.0 migration
88
+
89
+ CCDX 0.7.0 retires the former Claude App, Claude Code, Anthropic Messages API, isolated Claude account, and PM Studio patch integrations. Their setup, status, restore, profile, and routing commands are no longer supported.
90
+
91
+ If PM Studio is still patched by an older CCDX release, update or reinstall PM Studio to restore its official application bundle. CCDX 0.7.0 does not patch PM Studio and does not provide a restore workflow.
92
+
93
+ The upgrade intentionally does not delete or rewrite retired Claude or PM Studio configuration, historical patch backups, or isolated credentials created by older versions. Normal Codex configuration updates continue as documented above. Review and remove obsolete retired-integration state yourself only if you no longer need it.
94
+
95
+ ## Commands
96
+
97
+ Run `ccdx <command> --help` for command-specific behavior and side-effect details.
98
+ After startup, CCDX prints a short reminder that `ccdx help` lists all available commands.
99
+
100
+ ### Live models
101
+
102
+ ```bash
103
+ ccdx models
104
+ ```
105
+
106
+ This performs a fresh, read-only GitHub Copilot model-directory lookup without starting the adapter, opening Device Flow, reading the local model cache, or consuming an inference request. It lists selectable GPT model IDs, providers, advertised APIs, and preview status. Catalog availability does not guarantee that a later inference request will avoid quota, rate-limit, or policy enforcement.
107
+
108
+ Interactive terminals use aligned tables for `models`, `auth status`, and `usage`. Redirected or piped output retains the plain-text layout for scripts; use `--format table` or `--format plain` when a command offers an explicit format override.
109
+
110
+ `ccdx usage` and the dashboard Usage table show token cache hit rate for each model and the aggregate: recorded cached input tokens divided by recorded input tokens, weighted by token count rather than averaging request percentages. This describes the upstream prompt cache; the local history and image-transform caches are separate. Incomplete historical records do not hide a computable ratio: `~` marks a rate derived from incomplete counts. A rate is `—` when input tokens are zero, usable totals are missing, or token counts are invalid. Older logs may omit explicit zero cache counts; new Responses usage records retain reported zeros. CLI tables keep existing token columns on narrow terminals and show rates below the table when the extra column does not fit.
111
+
112
+ ### Auto-review model
113
+
114
+ ```bash
115
+ ccdx auto-review-model
116
+ ```
117
+
118
+ The selector first queries the running adapter, including its last-known-good model list when live refresh is unavailable, then falls back to a local model cache no older than seven days. It offers model IDs that advertise a Responses endpoint and are not explicitly hidden from the model picker. This selection does not itself verify policy or inference access.
119
+
120
+ Enter a model number, then a reasoning-effort number. The second menu reuses the same model-directory lookup and lists only advertised efforts. GPT-6.1 Sol excludes `none` and `minimal`, even if upstream metadata incorrectly lists them. Enter keeps the current valid effort or selects `low` for a new selection when advertised; `0` follows the client (or `low` if the request omits effort). If effort metadata is absent, only `0` is offered. Enter `q` at either step to cancel without saving either choice.
121
+
122
+ Both choices are saved together in `~/.config/codex-copilot-dx/config.json`, or under `XDG_CONFIG_HOME` when set. Choosing `gpt-6.1-sol` clears the model override and restores the package default. A selected effort is saved as `auto_review_reasoning: { "model": "gpt-6.1-sol", "effort": "medium" }` and applies only when its model matches the effective review target. Existing saved models, including `gpt-5.5`, and environment model overrides retain their precedence; changing the model via an environment variable does not transfer another model's effort. Choose `0` to clear the effort override; its `null` marker remembers client-following so Enter preserves that choice next time. Users who do not configure effort retain their existing behavior. Adapters running 0.9.13 or newer read both settings on the next Auto-review request; older adapters must first be upgraded and restarted. No client patch, foreground-model change, Fast mapping, permission or approval-policy change is involved.
123
+
124
+ ### Runtime caches
125
+
126
+ ```bash
127
+ ccdx cache
128
+ ccdx cache --limit 128
129
+ ccdx cache --limit default
130
+ ccdx cache --clean
131
+ ccdx cache --clean --history
132
+ ```
133
+
134
+ `ccdx cache` separates the shared in-memory Responses history from the rebuildable input-image transform cache. The history limit accepts 16–1024 MiB, is saved in the normal CCDX settings file, and applies immediately to a compatible running adapter. `CCDX_RESPONSE_HISTORY_MAX_BYTES` remains the higher-precedence environment override. Lowering the limit below current usage is rejected rather than evicting tasks silently. `ccdx cache -128` is a short alias for `--limit 128`; `-clean` aliases `--clean`.
135
+
136
+ Plain `--clean` clears only completed image transforms; the next matching request may recompute them. Adding `--history` also invalidates all process-local `previous_response_id` chains and can make a task's next continuation fail, so it requires interactive confirmation or `--yes`. Cleanup waits for active cache users to finish and never deletes Codex transcripts, generated image files, provider credentials, image edit handles, model catalogs, or usage/debug logs. A stopped or older adapter must be restarted before runtime cleanup; a saved limit applies on the next start.
137
+
138
+ ### Request-body limits
139
+
140
+ ```bash
141
+ ccdx limits
142
+ ccdx limits --decoded 256
143
+ ccdx limits -256
144
+ ccdx limits --decoded default
145
+ ```
146
+
147
+ `ccdx limits` distinguishes the saved decoded request-body limit from the running adapter's active limit. The default is 128 MiB; the opt-in saved limit accepts 128–512 MiB. Changes take effect only after restarting the adapter, never restart it automatically, and do not clear conversations or caches. `CCDX_MAX_DECODED_BODY_BYTES` overrides the saved setting; unset it before changing the saved limit. The separate 64 MiB raw/compressed-body limit is not changed. Larger decoded requests can use substantially more process memory during JSON parsing and image preparation; raising this limit may enable a large thread to compact, but does not guarantee successful inference or compaction. Return to `default` after temporary use.
148
+
149
+ ### Terminal animation
150
+
151
+ Choose the terminal activity animation interactively:
152
+
153
+ ```bash
154
+ ccdx animation
155
+ ```
156
+
157
+ The selector is ordered numerically:
158
+
159
+ 1. Comet (default)
160
+ 2. Twin
161
+ 3. Shuttle
162
+ 4. Chase
163
+ 5. Mirror
164
+ 6. Pulse
165
+ 7. Stack — short light trails arrive, accumulate, then leave as one bundle
166
+ 8. Relay — fixed nodes receive and pass a short light trail
167
+ 9. Split — one incoming light trail divides into two outgoing trails
168
+
169
+ All nine rows preview their animations together while you enter a number to select one. The live previews keep each theme's original timing and leave the input cursor in place; selection ends them immediately. Small terminals, disabled animation, CI and dumb terminals retain static previews. Resizing or clearing the terminal pauses the live gallery so input stays usable. When the gallery cannot run, CCDX retains the selected animation's one-cycle confirmation when supported. The setting is saved in `~/.config/codex-copilot-dx/config.json`, or under `XDG_CONFIG_HOME` when set.
170
+
171
+ All nine animations use the same 20-column colon track and cyan/blue palette. They indicate activity, not completion percentage. Braille was removed in 0.7.7. A saved `terminal_animation: "braille"` falls back to Comet without rewriting the settings file; choosing another animation replaces it, and explicitly choosing Comet clears it. Other settings remain intact.
172
+
173
+ The selected theme is loaded once when a new adapter starts, so changing it does not restart or alter an adapter that is already running. Stop and start CCDX to apply the new theme. `CCDX_TERMINAL_ANIMATION=0` (also `false`, `no`, or `off`) remains the boolean gate that disables terminal animation regardless of the selected theme.
174
+
175
+ ### Optional image generation
176
+
177
+ Image generation is disabled by default. Configure it interactively only when you want Codex App to receive the local CCDX image tool:
178
+
179
+ ```bash
180
+ ccdx enable-image
181
+ ```
182
+
183
+ The command accepts an HTTPS base URL or a full `/images/generations` endpoint, then asks for an API key without echoing the key. A bare origin such as `https://api.example` expands to `/v1/images/generations`; `https://api.example/v1` expands to the same endpoint. Custom base paths are retained, and existing full endpoints remain unchanged. CCDX uses GET for the corresponding `/models` catalog and POST for image requests; there is no endpoint-search or automatic fallback. It asks for a numeric model selection only when multiple image models are available, then detects either the standard OpenAI Images request format or the supported `input.messages` format for the selected model only. Its single validation request omits the prompt and does not generate an image.
184
+
185
+ An empty key answer retains the saved key only when the normalized endpoint has the same origin (scheme, host, and port). Changing origins requires explicitly entering a key before any provider request; the old key is never automatically sent to a new origin.
186
+
187
+ The provider credential is stored in `~/.config/codex-copilot-dx/image-provider.json`, or under `XDG_CONFIG_HOME`, with mode `0600`. The API key is never written to `~/.codex/config.toml`; that file receives only a marked local MCP entry pointing to the running CCDX adapter with a 210-second tool timeout. Only after enabling, CCDX installs `ccdx-image` guidance under `~/.codex/skills`, with a self-contained Node helper. The concise `SKILL.md` keeps operation selection, safety, and final-image delivery rules; editing details and helper instructions live in references read only for those operations. Codex uses the MCP tool directly, including through deferred tool search; an already-open task without that tool can use the helper to call the same local service and display the saved image. Neither route requires Python, an OpenAI SDK, or copying credentials. Built-in skills and unrelated tools are not modified.
188
+
189
+ The enable command verifies the local MCP handshake and generation-tool directory without generating an image. It reports provider configuration separately from local service readiness; it cannot assert that every running Codex task has refreshed its catalog. Codex normally detects changed skills; restart Codex App once if an open task has not refreshed them. Normal CCDX startup installs or updates this guidance only for an already-enabled provider, without repeating API setup. Without `enable-image`, it installs neither this skill nor the MCP registration. Unchanged guidance is not rewritten. The existing adapter can remain running because provider settings are loaded when the tool is invoked.
190
+
191
+ For Qwen-style image responses, CCDX accepts canonical PNG, JPEG, or WebP `data:image/...;base64,` output within the existing 32 MiB image limit. Public HTTPS image URLs still use the same pinned-DNS download checks; HTTP, private-network targets, and URLs with embedded credentials remain rejected.
192
+
193
+ Existing compatible `ccdx_image` registrations are reused without requiring CCDX's comment markers or resetting user timeouts/tool policies. Missing or damaged markers alone do not block startup. Core GPT configuration is committed independently before optional image maintenance: image configuration conflicts, image-only write failures, or Skill maintenance failures warn without aborting the core service. No new image guidance is installed after failed image configuration. With images disabled or their provider configuration invalid/unreadable, normal startup performs no image configuration maintenance, Skill installation, or image-network probe; explicit `disable-image` owns cleanup. Automatic updates/removal are limited to recognizable CCDX defaults and isolated TOML declarations, never everything between two comments. Ambiguous shared-inline or customized registrations are left intact; `disable-image` still disables the CCDX provider and removes its owned guidance. Invalid TOML/UTF-8 in the shared Codex config or a failed essential core configuration write remains an explicit error rather than a silent success.
194
+
195
+ Direct MCP generations and edits retain their original inline image content and editing ID, and also save the exact image bytes privately under `~/.local/share/codex-copilot-dx/images`. The result includes ready-to-use image Markdown for Codex's final chat reply, so the image appears as a clickable thumbnail instead of only inside a tool result. Files use unique names and are not overwritten or removed on adapter restart. Asking to show the same image again reuses its existing Markdown, not a new generation or edit. If saving fails, CCDX still returns the generated image content, reports the delivery failure, and does not retry the provider or invent a file path. The bundled helper continues saving once at its existing output location, without a second adapter-side copy. Existing enabled users receive updated, unmodified CCDX-owned guidance on the next CCDX startup; no new setup command is needed.
196
+
197
+ Inspect or disable the optional provider with:
198
+
199
+ ```bash
200
+ ccdx image-status
201
+ ccdx disable-image
202
+ ```
203
+
204
+ `image-status` never prints the API key and performs only a local, non-generating tool-readiness check. Disabling removes the stored provider credential, the CCDX-owned MCP block, and unmodified skill, reference, and helper files; user-modified files are preserved. Cached tool calls then fail closed. The Node helper creates a new file under the task's `output/imagegen` directory by default and reports its absolute path; it never overwrites an existing file or automatically repeats a failed generation. The image endpoint is contacted only by `enable-image` validation and by explicit `generate_image` or `edit_image` tool calls. Ordinary Responses, models, authentication, Fast, Auto-review, compaction, and image-input paths remain on their existing routes.
205
+
206
+ Generated image downloads must use public HTTPS addresses, do not follow redirects, are DNS-checked against private and reserved addresses, and are capped at 32 MiB. DNS resolution and downloading share one deadline and respond to cancellation; a late DNS result cannot start a cancelled download. The local MCP route accepts calls only from the same device and permits at most two concurrent image operations. It requires `application/json` and rejects browser `Origin` headers; native Codex and Node clients do not send an Origin. The tool supports new-image generation at `1024x1024`, `1536x1024`, or `1024x1536`.
207
+
208
+ With `qwen-image-3.0-pro` or `qwen-image-3.0` using either supported protocol, CCDX also exposes `edit_image`. Ask Codex to modify a specific CCDX-generated image; it passes that image's returned `image_id` and the edit instruction to the same configured provider. Each edit returns a new image and ID while preserving the original; omitting `size` retains the source's requested dimensions. The Qwen request contains the source image again on every edit, as required by its [single-turn image editing API](https://help.aliyun.com/zh/model-studio/qwen-image-generation-and-editing-api-reference).
209
+
210
+ Editing references are opaque IDs bound to the provider configuration, not paths or URLs. Source images up to 10 MiB are kept in a 64 MiB byte-bounded in-memory LRU. IDs expire on eviction, provider/credential changes, or adapter restart; restarting Codex App alone does not clear them. A missing or expired ID fails before upstream dispatch. This feature does not import arbitrary local images or provide masks, transparent output, or an `/images/edits` endpoint. Other providers retain their existing generation behavior. The helper accepts `--image-id` for the same edit operation; its stdout remains the saved absolute file path and stderr reports the new ID. Normal host approvals still apply, and failed edits are never replaced with an automatic new generation.
211
+
212
+ ### Update
213
+
214
+ Choose a source interactively:
215
+
216
+ ```bash
217
+ ccdx update
218
+ ```
219
+
220
+ Or choose directly:
221
+
222
+ ```bash
223
+ ccdx update npm
224
+ ccdx update github
225
+ ```
226
+
227
+ `ccdx update gh` is accepted as shorthand for the GitHub source. The npm source installs `codex-copilot-dx@latest` through the registry already configured for npm, including a company mirror. The GitHub source installs the latest commit from `DaleXiao/codex-copilot-dx` `main` and opts in to Git fetching only for that command. It does not change npm's persistent `allow-git` setting.
228
+
229
+ If a configured npm mirror has not synchronized the current release yet, use the GitHub source. A running adapter keeps its loaded version until it is stopped and restarted.
230
+
231
+ ## Diagnostics
232
+
233
+ `ccdx doctor` also inspects installed macOS App version metadata and running
234
+ Codex/ChatGPT App executable identities, without reading command arguments,
235
+ changing client files or killing processes. It shows the running adapter version,
236
+ upstream model-directory source, and the last client-version/bundled-directory
237
+ match observed by that adapter. These are observations, not proof of the App's
238
+ internal model cache. Fully quit the named App with Command-Q before reopening;
239
+ closing its window alone may leave its App server running. `doctor config` stays
240
+ configuration-only, with no process inspection or adapter request.
241
+
242
+ The default streaming path sends small downstream SSE comments after 15 seconds
243
+ of silence only once valid stream headers have been sent. Comments do not count
244
+ as model output/TTFT, extend upstream/idle deadlines, start another upstream call,
245
+ or release a held compatibility-retry prelude. They stop on completion, failure
246
+ or cancellation and are skipped while the downstream is backpressured. A valid
247
+ JSON reply to a streaming request is still not silently converted to SSE.
248
+
249
+ ### Local dashboard
250
+
251
+ After starting `ccdx`, open [http://127.0.0.1:2026/](http://127.0.0.1:2026/) in a browser. If `ADAPTER_PORT` is customized, use that port instead. The dashboard shares the existing adapter listener; CCDX does not open a browser automatically or start another service. The page and its assets require both a loopback connection and a loopback Host header, even when LAN binding is enabled.
252
+
253
+ The dashboard loads local status, a live GitHub Copilot model directory, and a summary of local usage metadata once when opened. Each section has an explicit Refresh control; nothing polls in the background. The appearance button switches between the default dark terminal theme (sun icon) and a light theme (moon icon); the choice is stored only in that browser. The Requests card shows the running adapter's active raw and decoded per-request body limits, not request-body usage; Local History shows its in-memory Responses history use and active limit. Limit changes remain CLI-only (`ccdx limits` and `ccdx cache`). The live model table uses the same fresh account validation and selectable GPT filtering as `ccdx models`, never labels cached fallback data as online, and makes no inference request. A catalog listing does not guarantee inference access. The Usage table combines the current and rotated local usage logs, shows totals and up to 100 model rows, and contains no prompt, response body, or credential fields. The animation selector previews the existing themes and saves through the same settings writer as `ccdx animation`; changes take effect on the next adapter start, and `CCDX_TERMINAL_ANIMATION=off` still disables terminal motion. Dashboard data requests require a custom header that cross-site simple requests cannot send; animation writes also require a matching same-origin `Origin`. The dashboard does not initialize the optional image handler. The overview's known-model count is the adapter's current in-memory catalog, not the live table. A missing image-generation status does not prove the feature is disabled: use `ccdx image-status` to check setup. Recent upstream error prose may contain sensitive details, so review the page before sharing screenshots.
254
+
255
+ The transparent, single-color double-peak favicon follows the dashboard's saved light/dark choice without a separate network request.
256
+
257
+ The saved GitHub Copilot account appears immediately to the left of the Snapshot time. It uses the same local credentials and fingerprint-bound identity metadata as `ccdx auth status`, with username (or numeric ID fallback) and a `SAVED` label; this is not an online entitlement check. Missing, invalid, unidentified, or unreadable credentials are shown explicitly. The account read runs once on page load and on the top Refresh action, independently of adapter status, with no polling or upstream authentication calls. Its loopback-only, custom-header-protected API exposes no token, fingerprint, credential path, or raw metadata. The general status endpoint retains its existing identity-free contract.
258
+
259
+ Account names containing underscores are supported, including `dingxiao_microsoft`. A numeric ID is a fallback, not a replacement for an available, safely displayable username; the existing length bounds and credential-shaped text suppression still apply.
260
+
261
+ The language button immediately right of the appearance button switches between English and Simplified Chinese. English remains the default; the choice is saved only in the current browser, separately from light/dark mode. Switching translates the existing page in place: it does not fetch API data, rebuild charts, change selected filters, discard unsaved animation choices, or reset expanded failure details. Static labels, live status text, chart tooltips and accessibility labels follow the selected language. Model IDs, account names, dates, numeric values and upstream diagnostic prose stay unchanged; copied diagnostic fields retain their original names and values. The local translation asset adds no dependency, external service, polling or inference-path work.
262
+
263
+ Usage Analytics is collapsed by default. Opening it adds daily input/output token bars (7/30/90 days) and a 365-day calendar of recorded usage calls or tokens, grouped in the browser's time zone. Cache-read tokens are already part of input and are not added again. Model and range filters use the current snapshot without additional requests; selecting a day shows its filtered model totals in the Usage table, and All history restores the original table. Analytics shares the existing current/rotated-log scan and exposes only bounded metadata (365 days, up to 100 model filters). Days before retained history are hatched, not presented as zero; the oldest retained day may be partial. Counts describe retained usage records, not messages, session time, or all HTTP requests. Refresh explicitly to update the snapshot; there is no background collection or polling.
264
+
265
+ Terminal Animation is collapsed at the bottom of the page, with an Animation shortcut in the header. Preview frames load only when opened; closing it, hiding the page, or requesting reduced motion stops the preview timer. Unsaved choices survive folding and reopening. Recent response failures expand to diagnostic fields (time, model, code, message, response/request IDs, and retry policy) with a Copy diagnostic control. The view combines the last 10 retained `response.failed` events with failures from the 20 most recently finished Responses/compact requests, showing at most 10 entries. Matching local request IDs attach the final outcome and timing metadata, so a recovered event can show `completed`; an uncorrelated retry attempt does not prove recovery. Transport failures and incomplete/cancelled requests can appear without a `response.failed` event. These are bounded runtime observations, not complete session histories. Diagnostic prose is redacted by the existing recorder but can still contain sensitive context: review before copying or sharing.
266
+
267
+ Inspect only the local Codex configuration:
268
+
269
+ ```bash
270
+ ccdx doctor config
271
+ ```
272
+
273
+ This command reads `~/.codex/config.toml` and checks UTF-8/TOML syntax, correctly scoped CCDX-managed URL/environment settings, positive integer context limits, their relative ordering, and `features.context_management`. Explicit `false` is valid. A missing optional shell environment section is valid for an existing file. Structured context-management settings are retained and reported as unverified for the installed Codex version.
274
+
275
+ It also runs the existing startup writer in memory and lists the managed keys that would change. A missing file produces a creation preview. A preview that yields invalid TOML or changes unowned settings is reported as an error rather than applied. No files are written, including debug logs; it does not read credentials, contact the adapter or upstream, start Device Flow, or open Codex App. It uses `ADAPTER_HOST` and `ADAPTER_PORT` for the expected URL. Unrelated startup/probe timeout settings do not affect this config-only command.
276
+
277
+ The syntax check uses a TOML 1.1 parser and validates only the listed managed fields, not the full installed Codex schema. It does not merge project files, profiles, environment overrides, or command-line overrides into an effective Codex configuration. Syntax errors report line/column without source snippets; configured URL and key values are not printed. The command accepts no `--online`, `--compat`, or write/fix options.
278
+
279
+ For the broader credential/configuration/adapter diagnostic:
280
+
281
+ ```bash
282
+ ccdx doctor
283
+ ```
284
+
285
+ The ordinary doctor reuses the same configuration checks, inspects the saved GitHub credential, and probes the local adapter. It does not start the adapter or modify credentials or Codex configuration; when `CCDX_LOG_PATH` is set, this broader command can write its diagnostics to that log. Both doctor modes exit `1` when any check is an error, and `0` for OK/warning-only results, including missing configuration or an explicit feature opt-out. Unsupported arguments or invalid relevant CLI options exit `2`.
286
+
287
+ To verify the saved GitHub token, Copilot entitlement, and live models endpoint without starting Device Flow or changing the credential:
288
+
289
+ ```bash
290
+ ccdx doctor --online
291
+ ```
292
+
293
+ To exercise the protocol through an already-running adapter:
294
+
295
+ ```bash
296
+ ccdx doctor --compat
297
+ ```
298
+
299
+ The compatibility doctor sends a few minimal Copilot requests covering Auto-review, native Responses, streaming history, and compaction. It consumes a small amount of Copilot usage, never starts the adapter or Device Flow, and does not change client configuration. Combine it with `--online` when both checks are needed.
300
+
301
+ For a concise summary of the running adapter:
302
+
303
+ ```bash
304
+ ccdx status
305
+ ```
306
+
307
+ For the complete machine-readable status payload:
308
+
309
+ ```bash
310
+ curl -s http://127.0.0.1:2026/_ccdx/status | jq
311
+ ```
312
+
313
+ The status endpoint is restricted to the socket's real loopback address even when LAN binding is explicitly enabled. It reports fixed-size HTTP request counters separately from bounded `completed` / `incomplete` / `failed` / `cancelled` response outcomes by model and origin, plus bounded incomplete-reason counts, TTFT/TPOT histograms, admission pressure, memory use, response-history size/tree concentration/lookup misses, image queue/cache and adaptive-history state, model-cache counts, token expiry state, and the ten most recent upstream `response.failed` diagnostics retained by the running process. HTTP errors without a model terminal remain "without terminal" and retain their known origin; they are not counted as model failures. History eviction markers are not failed-request counts; `ccdx status` warns near the cache limit without deleting or compressing history. Failure diagnostics include the event type, model, error code/message, response/request IDs when supplied upstream, and retry outcome. Common credential forms, long opaque encrypted values, and explicitly labelled input/prompt/tool-content echoes are redacted. They are cleared when the adapter restarts. No request/response bodies, credential fields, or account profiles are copied into status. Arbitrary upstream prose cannot be guaranteed free of sensitive content; review diagnostics before sharing them.
314
+
315
+ `image_mcp` has its own HTTP route counter. After the image handler has been used, `image_generation` separately reports active, succeeded, failed, busy, cancelled, and chat-file delivery-failure counts, including tool errors returned with HTTP 200. A delivery failure can accompany a successful generation; it never causes another generation. Before handler initialization this field is `null`, meaning not observed, not necessarily disabled. Status never initializes that handler, reads its provider credential, installs guidance, or probes the provider. Input-image optimization metrics remain separate. `logging` reports pending records/bytes, queue caps, overflow drops, and write failures for usage and enabled debug-file logging.
316
+
317
+ `stream_performance.by_route` also reports `request_ttft_ms`, measured from adapter entry to the first observed output delta, including local preparation. The existing `ttft_ms` continues to start at upstream dispatch. Neither timestamp means the client has drained or displayed the output. `preparation_ms` contains bounded histograms for admission (including history reservations), body reading/decompression/JSON parsing, history materialization/compact preparation, image optimization, and request serialization. Each sample measures one operation, including failed operations; retries can add samples. These are diagnostic intervals rather than a complete breakdown of request time: body reading includes internal decoded-body admission waits; authentication, routing, and other preparation are not individually timed. Non-streaming requests contribute preparation samples without inventing a first-token measurement.
318
+
319
+ `stream_performance.recent_requests` retains at most 20 finished Responses/compact requests. Each entry contains a CCDX-owned request ID, start timestamp, bounded model/route labels, HTTP status, final model outcome, known error origin, last observed phase and provider-call count. `timings_ms` includes cumulative preparation durations and offsets from adapter entry for the first upstream dispatch, latest upstream headers, first output, latest observed upstream activity, terminal and finish. Null means not observed, not zero. Stage durations can overlap and should not be added into a total. Provider-call counts include compatibility attempts but exclude OAuth exchanges and low-level connection retries. Streams update a single activity timestamp per read; no content, headers, opaque state or unbounded event list is retained. Entries are process-local and reset on restart; normal status refresh reads them without new upstream calls or background polling.
320
+
321
+ Every API response includes a safe `X-Request-Id` for correlation. To include the same ID in related terminal and file log lines, start with:
322
+
323
+ ```bash
324
+ ccdx --show-request-id
325
+ ```
326
+
327
+ ### Authentication recovery
328
+
329
+ The dashboard's collapsed **RECENT REQUEST CONTEXT** section reuses the same
330
+ status snapshot. It shows at most 20 finished request observations: serialized
331
+ upstream payload bytes, image count, reported input tokens, the advertised model
332
+ window and terminal outcome, plus process-local compaction outcome counts. Missing
333
+ fields are unknown, not zero; input/window is a reference ratio, not an exact live
334
+ session measurement. No prompts, image bytes, files, tool results or extra session
335
+ scan are retained. Rows render when opened, without another API call.
336
+
337
+ When the saved token is missing, `ccdx` looks for compatible local Copilot GitHub tokens, validates them with GitHub and Copilot, and imports a valid one before starting Device Flow. It checks explicit token sources (`CCDX_GITHUB_TOKEN`, `CCDX_GITHUB_TOKEN_PATH`, and `CCDX_GITHUB_TOKEN_PATHS`) plus common local `auth.json` layouts. Generic discovery refuses to choose silently when valid credentials for multiple GitHub accounts are found.
338
+
339
+ After an account is selected, automatic `401`/`403` recovery and in-process token rotation accept only that same GitHub account. Concurrent callers share token refresh work without sharing cancellation, and a still-valid Copilot token may be used briefly after a transient refresh failure. Explicit token variables remain the intentional way to select another account.
340
+
341
+ If refresh still fails with `401` or `403`, the saved GitHub OAuth credential may be expired, revoked, or missing Copilot access. After stopping the adapter, remove that exact saved token and start again to rerun authentication recovery:
342
+
343
+ ```bash
344
+ rm ~/.local/share/copilot-api/github_token
345
+ ccdx
346
+ ```
347
+
348
+ Recovery first tries compatible local credentials and starts Device Flow only when none can be imported. Set `CCDX_DISABLE_TOKEN_DISCOVERY=1` for that launch if a fresh Device Flow is intended after removing the saved token.
349
+
350
+ ## Security
351
+
352
+ - CCDX binds to loopback by default. Non-loopback hosts are rejected unless `CCDX_ALLOW_LAN=1` is set explicitly.
353
+ - Enabling LAN binding exposes a Copilot-backed API to other reachable machines. Provide your own host firewall and network isolation; do not expose the listener to an untrusted network.
354
+ - The GitHub OAuth credential is stored locally at `~/.local/share/copilot-api/github_token`. Short-lived Copilot service tokens remain in process memory and are refreshed as needed.
355
+ - The optional image API credential is stored separately with mode `0600` only after `ccdx enable-image`; it is never placed in Codex configuration or diagnostic output.
356
+ - Runtime status and usage records retain metadata rather than request/response content. Diagnostic logging records status and error messages; it is not a general redaction layer for arbitrary error text. See Debug logging before sharing logs.
357
+ - Request/response bodies, request-admission queues, retained history, image work, SSE buffers, log queues, retries, and shutdown have explicit limits. These component limits are not a single process-wide memory ceiling; completed image files are persistent user artifacts, not an automatically evicted cache.
358
+
359
+ ## Configuration
360
+
361
+ | Variable | Default | Description |
362
+ |----------|---------|-------------|
363
+ | `ADAPTER_HOST` | `127.0.0.1` | Adapter host; only loopback hosts are allowed by default |
364
+ | `ADAPTER_PORT` | `2026` | Adapter port |
365
+ | `CCDX_ALLOW_LAN` | unset | Set to `1` to permit a non-loopback host such as `0.0.0.0`; this exposes the Copilot-backed adapter beyond this machine |
366
+ | `CCDX_MAX_BODY_BYTES` | `67108864` | Maximum compressed or raw request-body size |
367
+ | `CCDX_MAX_DECODED_BODY_BYTES` | `134217728` | Maximum decoded request-body size after decompression |
368
+ | `CCDX_MAX_INFLIGHT_BODY_BYTES` | `33554432` | Per-pool byte budget for raw bodies, decoded bodies, and materialized response history; a larger item runs exclusively in its pool |
369
+ | `CCDX_MAX_UPSTREAM_CHAT_RESPONSE_BYTES` | `67108864` | Maximum buffered successful Chat Completions response used by the internal Responses compatibility path |
370
+ | `CCDX_MAX_UPSTREAM_RESPONSES_RESPONSE_BYTES` | `268435456` | Maximum buffered successful native Responses or compact response; streaming Responses use bounded backpressure |
371
+ | `CCDX_MAX_QUEUED_REQUESTS` | `16` | Maximum body requests waiting for shared byte admission |
372
+ | `CCDX_REQUEST_QUEUE_TIMEOUT_MS` | `120000` | Maximum wait for request-body admission before returning `503` |
373
+ | `CCDX_REQUEST_BODY_TIMEOUT_MS` | `120000` | Maximum time to receive and decode a request body before returning `408` |
374
+ | `CCDX_MAX_UPSTREAM_BODY_BYTES` | `31457280` | Strict maximum forwarded Responses body size; larger payloads are adapted locally or rejected with `413` |
375
+ | `CCDX_MAX_SSE_BUFFER_BYTES` | `8388608` | Maximum buffered bytes for one unterminated upstream SSE line or event |
376
+ | `CCDX_UPSTREAM_TIMEOUT_MS` | `120000` | Per-phase timeout for Responses preparation and non-streaming upstream requests |
377
+ | `CCDX_STREAM_HANDSHAKE_TIMEOUT_MS` | `120000` | Timeout while waiting for upstream streaming response headers |
378
+ | `CCDX_STREAM_IDLE_TIMEOUT_MS` | `120000` | Maximum idle time between upstream streaming body chunks |
379
+ | `CCDX_UPSTREAM_RETRIES` | `2` | Retries for safe requests and clearly pre-connect POST failures; capped at `5` |
380
+ | `CCDX_UPSTREAM_RETRY_DELAY_MS` | `300` | Initial upstream retry backoff in milliseconds; capped at `5000` |
381
+ | `CCDX_AUTO_REVIEW_MODEL` | saved selection or `gpt-6.1-sol` | Copilot Responses model used for Codex Auto-review; overrides the interactive selection |
382
+ | `CCDX_LOG_PATH` | unset | Mirror terminal logs to a file; set to `1` for `~/.local/share/codex-copilot-dx/debug.log` |
383
+ | `CCDX_LOG_LEVEL` | `info` | Set to `debug` for upstream attempts, status codes, retry causes, and timings |
384
+ | `CCDX_LOG_MAX_BYTES` | `16777216` | Rotate the debug log at this size and retain one `.1` backup; set to `0` to disable rotation |
385
+ | `CCDX_TERMINAL_ANIMATION` | auto | Show the selected transient animation while an interactive terminal has active requests and no output for 800 ms; set to `0`, `false`, `no`, or `off` to disable |
386
+ | `CCDX_IMG_MAX_DIM` | `2048` | Maximum long edge in pixels for image downscaling |
387
+ | `CCDX_IMG_QUALITY` | `82` | Initial WebP quality for image re-encoding |
388
+ | `CCDX_IMG_MIN_BYTES` | `100000` | In-bounds images smaller than this skip re-encoding; oversized dimensions still trigger an optimization attempt |
389
+ | `CCDX_IMG_CONCURRENCY` | `2` | Global concurrent image-optimization tasks; values above `12` are capped |
390
+ | `CCDX_IMG_MAX_INPUT_PIXELS` | `40000000` | Sharp's per-image decode limit; an optimization failure retains the original image, subject to the overall forwarded-body limit |
391
+ | `CCDX_IMG_CACHE_MAX_BYTES` | `67108864` | Process-local byte ceiling for cached image transforms; set to `0` to disable the cache |
392
+ | `CCDX_DISABLE_IMG_OPT` | unset | Set to `1` to disable image optimization |
393
+ | `CCDX_AUTO_LAUNCH` | enabled | Set to `0`, `false`, `no`, or `off` to start without opening Codex App |
394
+ | `CCDX_GITHUB_TOKEN` | unset | Explicit GitHub Copilot OAuth token to validate and import before Device Flow |
395
+ | `CCDX_GITHUB_TOKEN_PATH` | unset | Explicit file containing a GitHub Copilot OAuth token |
396
+ | `CCDX_GITHUB_TOKEN_PATHS` | unset | Multiple token files separated by the platform path delimiter (`:` on macOS/Linux, `;` on Windows) |
397
+ | `CCDX_GITHUB_LOGIN` | saved account | Require automatic discovery and recovery to use this GitHub login |
398
+ | `CCDX_DISABLE_TOKEN_DISCOVERY` | unset | Set to `1` to skip local token discovery and use the saved token or Device Flow |
399
+ | `CCDX_TOKEN_LOCK_TIMEOUT_MS` | `600000` | Maximum time to wait for another process to finish GitHub token login or import |
400
+ | `CCDX_TOKEN_LOCK_STALE_MS` | `900000` | Minimum age for stale-lock recovery; a lock with a live owner is not removed |
401
+ | `CCDX_EXISTING_ADAPTER_TIMEOUT_MS` | `500` | Timeout for detecting an already-running local adapter during startup |
402
+ | `CCDX_MODEL_REFRESH_TIMEOUT_MS` | `5000` | Timeout for Copilot model refresh or live Auto-review model lookup |
403
+ | `CCDX_MODEL_REFRESH_INTERVAL_MS` | `7200000` | Periodic model refresh interval; `0` disables periodic refresh, while startup cache/refresh behavior still applies |
404
+ | `CCDX_RESPONSE_HISTORY_MAX_BYTES` | `67108864` | Higher-precedence total in-memory byte budget for locally expanded Responses history; `ccdx cache --limit` provides a persistent 16–1024 MiB setting when this is unset |
405
+ | `CCDX_RESPONSE_HISTORY_MAX_ENTRIES` | `4096` | Maximum stored incremental Responses history nodes |
406
+ | `CCDX_USAGE_PATH` | `~/.local/share/codex-copilot-dx/usage.jsonl` | Local JSONL token-usage log |
407
+ | `CCDX_USAGE_MAX_BYTES` | `33554432` | Rotate the usage log at this size and retain one `.1` backup; set to `0` to disable rotation |
408
+ | `CCDX_DISABLE_USAGE` | unset | Set to `1` to disable usage logging |
409
+ | `CCDX_SHUTDOWN_TIMEOUT_MS` | `5000` | Time to drain active HTTP connections before forcing shutdown |
410
+
411
+ ## Usage logging
412
+
413
+ When upstream responses contain usage fields, CCDX records token counts, model names, API surface, and response IDs in `~/.local/share/codex-copilot-dx/usage.jsonl`. It does not record prompts, completions, tool arguments, or image content.
414
+
415
+ ```bash
416
+ ccdx usage
417
+ ```
418
+
419
+ The summary covers the current log and its single rotated backup, not an unbounded lifetime history. It reports recorded responses and per-model input, cache-read, output, and total token metadata.
420
+
421
+ Usage writes are asynchronous and batched under the existing cross-process rotation lock. Pending and in-flight records share a 4096-record / 4 MiB ceiling. If either ceiling is reached, new excess records are dropped with a warning and a status counter; inference does not wait or retry. Normal records and per-record rotation boundaries are unchanged. The bounded shutdown flush remains 1.5 seconds; disk failures and shutdown deadlines can prevent persistence, so usage is diagnostic rather than a billing ledger.
422
+
423
+ Repeated plain usage summaries in the same process reuse one bounded cache entry when both retained log files are unchanged. File identity, size, modification/change timestamps and a two-second recent-write guard control reuse; concurrent settled-file queries share one scan. The serialized summary budget is 256 KiB and at most 128 model groups; larger results remain complete but uncached. Warnings, unstable reads, metadata errors and scans with changed files are not retained. Each caller receives an independent result. Analytics record callbacks and custom-warning reads keep the original streaming path. There is no persistent index, background polling, cross-process cache or change to usage writes; opening analytics still scans the retained records.
424
+
425
+ Diagnostic redaction removes Basic authorization payloads and URL userinfo, and consumes escaped quotes inside sensitive JSON fields. Ordinary Basic-related prose, safe host/path information and error codes are retained. These protections apply to existing failure/status/debug diagnostics, not forwarded requests or upstream response bodies. They do not make arbitrary upstream prose guaranteed safe to share.
426
+
427
+ ## Debug logging
428
+
429
+ Set `CCDX_LOG_PATH=1` to mirror terminal logs to `~/.local/share/codex-copilot-dx/debug.log`, or provide a custom path. Add `CCDX_LOG_LEVEL=debug` for upstream attempts, retry causes, status codes, and timings. The adapter does not deliberately dump request/response bodies or authorization headers. File diagnostics redact common credential forms and labelled content echoes, but this cannot identify every secret in arbitrary external prose; review logs before sharing them. File writes are asynchronous and batched with a 4096-record / 2 MiB pending-and-in-flight ceiling. Overflow drops excess file records with one warning and a status counter; terminal output continues. CLI exit paths flush for at most 1.5 seconds. `ccdx doctor config` bypasses file logging entirely.
430
+
431
+ ## Large histories and images
432
+
433
+ Long computer-use sessions can accumulate screenshots that are sent again on later turns. This can increase local preparation time, upstream handshake latency, and the chance of a `413 Payload Too Large` response.
434
+
435
+ The response-history cache is process-local and byte/entry bounded. Restarting the adapter discards it; a `previous_response_id` stored only in that process becomes unavailable. CCDX does not persist conversation history to disk. Near-limit status diagnostics report the largest tree and actual lookup misses, but do not recover an already evicted ID or change the cache budget. Clients can still submit explicit history in the request body, subject to the same limits.
436
+
437
+ CCDX attempts to downsample inline base64 images to model-appropriate pixel bounds, with a 2048 px long-edge fallback, and initially encodes them as WebP quality 82. It never replaces an image with a larger encoding and uses one global concurrency limit across direct images and function/custom-tool outputs. GIFs and non-inline references are not re-encoded. An encoding/decode failure keeps the original image; the final request-byte limit still applies.
438
+
439
+ The final serialized UTF-8 body is measured before forwarding. Above the configured limit, unique source images are processed largest-first from their originals at quality 75 / 1600 px and then quality 65 / 1280 px, stopping when the body fits. If necessary, the forwarded view omits older duplicate images, historical tool images, other historical images, and finally old tool outputs while preserving current input and tool-call skeletons. Requests that still do not fit receive a structured local `413`.
440
+
441
+ Completed image transforms are reused from a byte-bounded process-local LRU cache, and concurrent requests for the same transform share one encode. Failures are not cached and request cancellation remains isolated.
442
+
443
+ The visual-history pressure pass does not omit images while there are at most 24 historical images and the expanded body is at most 18 MiB; normal image encoding and other limits still apply. Above either threshold, this pass preserves current-turn images and keeps up to 16 recent historical images, targeting 16 MiB. The separate 50-image and total-body limits remain in force. After an eligible large-visual-history preparation, handshake, upstream timeout, or HTTP `408`, the next retry uses an 8-image, 10 MiB recovery window for ten minutes. Two successful requests or a successful compaction clear recovery mode. CCDX never transparently repeats an ambiguous timed-out POST, and it does not delete local history.
444
+
445
+ When expanded history exceeds Copilot's 50-image request limit, CCDX removes older duplicate occurrences first and then keeps the 50 most recent images. This affects only the forwarded request; local history remains complete and current images are preferred.
446
+
447
+ For `/v1/responses/compact`, CCDX sends one terminal `compaction_trigger`, validates completed compaction state, and preserves the complete returned window as the canonical next context. Only a validated window becomes a new local history root. Existing older branches remain available until normal history eviction.
448
+
449
+ Changing the model or transport of a continuation can require removing incompatible encrypted history from the forwarded request. A specific upstream encrypted-state verification failure can also trigger one sanitized retry, including when a nominally successful HTTP response ends with `response.failed`. CCDX withholds only the pre-output prelude of an encrypted continuation while evaluating that terminal event; it never retries after visible model output, so it cannot duplicate an already-started answer. Unrelated failures pass through unchanged. CCDX does not decrypt that state locally.
450
+
451
+ Native Responses forwards Codex `agent_message` items unchanged. When a model requires Chat Completions, CCDX frames them as messages from another agent, not human input or approval, retaining author/recipient, typed text/refusal/reasoning and representable image/screenshot content. Content unsupported by Chat, including file, audio or opaque encrypted parts, receives an explicit compatibility error instead of being serialized into ordinary text. This projection does not introduce another model request or change native Responses, Fast, Auto-review or compaction routing.
452
+
453
+ After the stream handshake succeeds, stream liveness uses the idle timeout rather than the expired handshake deadline. Eligible HTTP-200 failure recovery acquires a bounded preparation budget and history admission before rebuilding its payload, then releases those reservations when the retry opens upstream or fails. Normal streams still release preparation admission after their response headers arrive.
454
+
455
+ Downstream backpressure waits also observe cancellation and the stream deadline. A client that stops reading cannot retain the pending stream indefinitely after timeout; CCDX closes that connection without retrying its request. Final error-frame delivery has a separate one-second maximum drain wait.
456
+
457
+ Successful streaming responses are accepted only when the upstream body is SSE and reaches its protocol terminal event. Unexpected EOF becomes a protocol-native stream error, and repeated blank tool-argument deltas are stopped per tool call. Allowlisted rate-limit, retry, model, trace, and upstream request-ID metadata are forwarded; cookies, authorization, body-length, and encoding headers are not.
458
+
459
+ CCDX keeps each native Responses message on its first upstream ID when Copilot changes that ID during streaming. Message deltas, completion events, and cached history stay consistent, addressing this cause of duplicate live messages in Codex App. Tool identities, reasoning/encrypted content, message phases, and speed-tier routing are unchanged by this ID normalization. Stable events retain their original SSE bytes; changed events are rewritten with bounded, backpressured streaming.
460
+
461
+ Newer Codex App builds can advertise an `image_gen` namespace that already exists upstream. CCDX removes that exact conflicting client tool before forwarding and retries once only when Copilot explicitly reports an image namespace collision. Image input and screenshot optimization remain enabled.
462
+
463
+ ## Development
464
+
465
+ ```bash
466
+ npm ci
467
+ npm run verify
468
+ npm run bench:payload
469
+ ```
470
+
471
+ `bench:check` covers both SSE parsers, including native Responses identity rewriting, 1/4/8 MiB fragmented events, write batching, and slow-client backpressure. It also repeats four concurrent image preparations across six rounds using a desktop-sized synthetic screenshot and a public-domain photograph. Checks enforce content preservation, single-pass JSON parsing, bounded copying, cache reuse, settled work, and bounded retained heap/ArrayBuffers after warm-up and GC. Sampled RSS and absolute timings remain report-only because allocators, runtimes, and machines differ; the fixtures do not establish production latency or visual model accuracy.
472
+
473
+ `npm test` runs unit, handler, CLI, and documentation-contract tests. `npm run test:smoke` starts a real local adapter with fully injected offline upstreams. `npm run bench:check` enforces linear SSE scanning, image/tool processing, and request-admission resource limits. `npm run pack:check` verifies npm tarball contents without publishing. `npm run bench:payload` is a report-only isolated-process benchmark for 5–60 MiB synthetic padded-image payloads and does not contact Copilot. CI runs the complete `verify` gate on Node 22.15.0 and 24.x.
474
+
475
+ The [documentation index](https://github.com/DaleXiao/codex-copilot-dx/blob/main/docs/README.md) distinguishes current guidance from historical release evidence. Release reports retain the measurements and limits of their specific versions; they do not establish current live-provider compatibility.
476
+
477
+ From a source checkout, `node scripts/codex-message-id-replay.mjs --codex /absolute/path/to/codex` checks message identity against an installed Codex runtime using only synthetic SSE, a local mock server, and automatically cleaned temporary Codex directories. It does not use the real client configuration or call Copilot.
478
+
479
+ ## License
480
+
481
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-copilot-dx",
3
- "version": "0.9.12",
3
+ "version": "0.9.14",
4
4
  "description": "Use Codex App with GitHub Copilot",
5
5
  "author": "Dale Xiao",
6
6
  "type": "module",
@@ -14,7 +14,8 @@
14
14
  "scripts/benchmark.mjs",
15
15
  "scripts/benchmark-probes.mjs",
16
16
  "scripts/fixtures/",
17
- "README.md"
17
+ "README.md",
18
+ "docs/REFERENCE.md"
18
19
  ],
19
20
  "keywords": [
20
21
  "codex",
@@ -38,7 +39,7 @@
38
39
  },
39
40
  "scripts": {
40
41
  "test": "node --test",
41
- "test:smoke": "node scripts/offline-smoke.mjs",
42
+ "test:smoke": "node scripts/offline-smoke.mjs && node scripts/sse-idle-replay.mjs",
42
43
  "test:e2e": "bash test/e2e.sh",
43
44
  "bench": "node scripts/benchmark.mjs",
44
45
  "bench:payload": "node scripts/benchmark.mjs --large-payload",
package/src/adapter.mjs CHANGED
@@ -178,6 +178,7 @@ export function createAdapterHandler(options = {}) {
178
178
  chatCompletionsFn,
179
179
  getCachedModelEndpointsFn,
180
180
  imagePressure,
181
+ keepaliveIntervalMs: options.keepaliveIntervalMs,
181
182
  modelRegistry: codexModelRegistry,
182
183
  now: options.now,
183
184
  openAIModelEnv,
@@ -235,6 +236,7 @@ export function createAdapterHandler(options = {}) {
235
236
  modelRegistry: codexModelRegistry,
236
237
  codexClient,
237
238
  codexModelRegistry,
239
+ codexModelCatalog,
238
240
  })));
239
241
  return;
240
242
  }