@jiayunxie/aerial 0.1.0 → 0.1.2

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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Jeremy Xie
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jeremy Xie
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,264 +1,263 @@
1
- # Aerial
2
-
3
- [![CI](https://github.com/Xiejiayun/aerial/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/Xiejiayun/aerial/actions/workflows/ci.yml)
4
- [![Nightly](https://github.com/Xiejiayun/aerial/actions/workflows/nightly.yml/badge.svg)](https://github.com/Xiejiayun/aerial/actions/workflows/nightly.yml)
5
- [![npm version](https://img.shields.io/npm/v/@jiayunxie/aerial.svg)](https://www.npmjs.com/package/@jiayunxie/aerial)
6
-
7
- Aerial is a lightweight local-only proxy that lets one user connect their own GitHub Copilot subscription to local coding CLIs.
8
-
9
- The MVP runs on `127.0.0.1:18181`, requires a local Aerial API key for model routes, stores credentials on the user's machine, and avoids public deployment, account sharing, quota bypass, dashboards, analytics, and image APIs.
10
-
11
- ## MVP Support
12
-
13
- Implemented routes:
14
-
15
- - `GET /health` without auth
16
- - `GET /v1/models` for model discovery, with an `aerial` support annotation per model
17
- - `POST /v1/responses` for Codex CLI
18
- - `POST /v1/messages` for Claude Code's Anthropic gateway path
19
- - `POST /v1/messages/count_tokens` with a local estimate
20
- - `POST /v1/chat/completions` as a simple passthrough fallback
21
-
22
- Implemented CLI commands:
23
-
24
- ```bash
25
- aerial login
26
- aerial key generate
27
- aerial key print
28
- aerial start
29
- aerial setup codex
30
- aerial setup claude --model <available-copilot-model-id>
31
- aerial setup all
32
- aerial doctor
33
- aerial probe
34
- ```
35
-
36
- Service installation, rollback automation, Gemini CLI support, dashboards, and analytics are intentionally out of this MVP. For Codex, local clients keep using HTTP POST /v1/responses. Aerial can optionally use Copilot's upstream `ws:/responses` transport for streaming Responses and translate events back to SSE — this transport is opt-in (`AERIAL_RESPONSES_WEBSOCKET=on`) and HTTP is the default.
37
-
38
- ## Requirements
39
-
40
- - Node.js 22+
41
- - A GitHub account with an active Copilot subscription
42
- - Codex CLI and/or Claude Code installed locally
43
-
44
- ## Install
45
-
46
- ```bash
47
- npm install -g @jiayunxie/aerial
48
- ```
49
-
50
- After install, the CLI is available as `aerial`.
51
-
52
- ### Nightly builds (advanced, unstable)
53
-
54
- The nightly workflow publishes a `nightly` pre-release of Aerial when there are new commits on `main`. The default install command above is unaffected — nightlies are gated behind the `@nightly` dist-tag and will never be served as `latest`. To opt in:
55
-
56
- ```bash
57
- npm install -g @jiayunxie/aerial@nightly
58
- ```
59
-
60
- Nightly versions look like `0.1.1-nightly.YYYYMMDD.<sha7>`. They reflect the current `main` and may break; report regressions with `aerial --version` so the exact build can be reproduced. Switch back to stable any time with `npm install -g @jiayunxie/aerial`.
61
-
62
- ### From source (local development)
63
-
64
- For working directly on the Aerial codebase:
65
-
66
- ```bash
67
- git clone https://github.com/Xiejiayun/aerial.git
68
- cd aerial
69
- npm install -g .
70
- ```
71
-
72
- Run without global install:
73
-
74
- ```bash
75
- node src/cli.js --help
76
- ```
77
-
78
- ## First Run
79
-
80
- Configure your local clients. This also creates Aerial's local API key and wires it into supported clients:
81
-
82
- ```bash
83
- aerial setup all --model <available-copilot-model-id>
84
- ```
85
-
86
- On Windows, newly persisted user environment variables are visible to new terminals and newly launched apps. Restart your terminal or VS Code after the first setup if Codex was already open.
87
-
88
- Log in to GitHub with device flow:
89
-
90
- ```bash
91
- aerial login
92
- ```
93
-
94
- Start the local server:
95
-
96
- ```bash
97
- aerial start
98
- ```
99
-
100
- Check health:
101
-
102
- ```bash
103
- curl http://127.0.0.1:18181/health
104
- ```
105
-
106
- ## Codex CLI Setup
107
-
108
- Aerial configures Codex through the Responses wire API provider path:
109
-
110
- ```bash
111
- aerial setup codex --model <available-copilot-model-id>
112
- ```
113
-
114
- This updates `~/.codex/config.toml` and creates a timestamped backup first. If you only want to inspect the exact change, set `HOME`/`USERPROFILE` to a temporary directory before running setup. The inserted provider uses:
115
-
116
- ```toml
117
- [model_providers.aerial]
118
- base_url = "http://127.0.0.1:18181/v1"
119
- wire_api = "responses"
120
- env_key = "AERIAL_API_KEY"
121
- ```
122
-
123
- Aerial creates the key automatically and persists `AERIAL_API_KEY` for new user sessions when the platform supports it.
124
-
125
- ## Claude Code Setup
126
-
127
- Aerial configures Claude Code through its Anthropic-compatible gateway settings:
128
-
129
- ```bash
130
- aerial setup claude
131
- ```
132
-
133
- This updates `~/.claude/settings.json` and creates a timestamped backup first. If you only want to inspect the exact change, set `HOME`/`USERPROFILE` to a temporary directory before running setup. It sets:
134
-
135
- ```json
136
- {
137
- "model": "<available-copilot-model-id>",
138
- "apiKeyHelper": "aerial key print",
139
- "env": {
140
- "ANTHROPIC_BASE_URL": "http://127.0.0.1:18181",
141
- "CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY": "1"
142
- }
143
- }
144
- ```
145
-
146
- The `model` field is written when you pass `--model` or set Aerial's `defaultModel`; otherwise Aerial leaves any existing Claude Code model choice alone while still switching the gateway to the local Aerial endpoint.
147
-
148
- `aerial key print` prints the locally stored Aerial API key for Claude Code's helper flow. Users normally do not need to call it directly.
149
-
150
- When switching from another Anthropic-compatible gateway, setup removes stale `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_MODEL`, and `ANTHROPIC_DEFAULT_*_MODEL` entries from Claude Code's managed `env` block so `apiKeyHelper` and the Aerial gateway are the active route.
151
-
152
- ## Diagnostics
153
-
154
- ```bash
155
- aerial doctor
156
- aerial probe
157
- ```
158
-
159
- The doctor checks config, local API key presence, GitHub login state, Node version, and local bind address.
160
-
161
- ## Security Boundary
162
-
163
- Aerial is for personal local use only.
164
-
165
- - It binds to `127.0.0.1` by default.
166
- - Model routes require `Authorization: Bearer <AERIAL_API_KEY>` or `x-api-key: <AERIAL_API_KEY>`.
167
- - GitHub tokens are stored under the user config directory with private file permissions where supported.
168
- - Aerial does not log raw GitHub tokens, Copilot JWTs, API keys, or request bodies by default.
169
- - Do not expose this service publicly or use it for account sharing.
170
-
171
- ## Current Limitations
172
-
173
- - Copilot inference routes are an observed compatibility target, not a public stable GitHub REST API.
174
- - `/v1/messages/count_tokens` is a local estimate, not upstream tokenization.
175
- - Service install/uninstall and disable/rollback are not implemented yet.
176
- - Model choice is not automated; query `/v1/models` and select an available model explicitly.
177
- - Chat Completions requests normalize `max_tokens` to `max_completion_tokens` for newer OpenAI models that reject the older field.
178
- - Prompt caching is upstream-managed: Aerial does not store prompt bodies locally, and it preserves or injects cache protocol fields before forwarding.
179
-
180
-
181
-
182
- ## Capability Probe
183
-
184
- Use `aerial probe` to inspect the live Copilot model matrix through the same local credentials Aerial uses for proxying:
185
-
186
- ```bash
187
- aerial probe
188
- ```
189
-
190
- This prints model IDs, Aerial routes, and unsupported notes such as `embeddings_not_implemented` or `no_supported_endpoint_advertised`. Models with upstream `ws:/responses` support are shown with the `responses_websocket` route.
191
-
192
- Run low-cost live route checks when you want to verify end-to-end behavior:
193
-
194
- ```bash
195
- aerial probe --live
196
- # machine-readable output
197
- aerial probe --live --json
198
- ```
199
-
200
- `--live` sends one small request through the first available `responses`, `messages`, and `chat` model. It does not test every model by default, which keeps the command lightweight.
201
- ## Prompt Cache
202
-
203
- Aerial uses the upstream Copilot/OpenAI/Anthropic cache protocols instead of keeping a local prompt-content cache. That keeps the MVP lightweight and avoids storing prompts on disk.
204
-
205
- Supported behavior:
206
-
207
- - Responses and Chat Completions preserve `prompt_cache_retention` and `prompt_cache_key` when the client sends them.
208
- - Responses and Chat Completions automatically add `prompt_cache_retention: "in_memory"` and a stable hashed `prompt_cache_key` when the client omits them.
209
- - Anthropic Messages preserves client `cache_control` blocks. When none are present and caching is enabled, Aerial automatically adds `cache_control: { "type": "ephemeral" }` to stable `system` content, or to the final tool definition when there is no system content.
210
- - Usage fields from upstream are returned unchanged, including `cached_tokens`, `cache_creation_input_tokens`, `cache_read_input_tokens`, and Copilot `cache_read` / `cache_write` token details when present.
211
- - Aerial logs cache metadata to stderr as `cache_request` and `cache_observe` events. These logs do not include prompt text or request bodies.
212
-
213
- OpenAI's prompt caching documentation says cache hits require an exact prompt prefix match and are only possible once the prompt is at least 1024 tokens. Confirm cache behavior from returned usage fields such as `usage.prompt_tokens_details.cached_tokens` or `usage.input_tokens_details.cached_tokens`; for shorter prompts this value is expected to be zero.
214
-
215
- Aerial enables ephemeral prompt caching by default for OpenAI-style routes and Anthropic Messages. Override it only when needed:
216
-
217
- ```bash
218
- aerial config set promptCacheRetention in_memory
219
- # or
220
- aerial config set promptCacheRetention 24h
221
- # disable automatic cache hints
222
- aerial config set promptCacheRetention off
223
- aerial config set promptCacheKey off
224
- # pin all requests to an explicit cache partition
225
- aerial config set promptCacheKey my-project
226
- ```
227
-
228
- You can also set it per process:
229
-
230
- ```bash
231
- export AERIAL_PROMPT_CACHE_RETENTION=in_memory
232
- export AERIAL_PROMPT_CACHE_KEY=my-project
233
- ```
234
-
235
- Per-request fields win over the configured default. Use `24h` only with models whose upstream route accepts extended retention; Anthropic Messages still uses Anthropic-style `cache_control: { "type": "ephemeral" }`. Set `promptCacheKey` to `auto` to use Aerial's hashed stable key, `off` to omit it, or a string to force a specific cache partition.
236
- ## Model Support
237
-
238
- `GET /v1/models` returns Copilot's raw model metadata and adds an Aerial-specific field:
239
-
240
- ```json
241
- {
242
- "id": "gpt-5.4-mini",
243
- "supported_endpoints": ["/responses", "ws:/responses"],
244
- "aerial": {
245
- "supported": true,
246
- "routes": ["responses", "responses_websocket"],
247
- "notes": []
248
- }
249
- }
250
- ```
251
-
252
- Route meanings:
253
-
254
- - `responses`: usable by Codex through HTTP `POST /v1/responses`.
255
- - `responses_websocket`: Aerial can use Copilot's upstream `ws:/responses` transport for streaming `/v1/responses` requests when `AERIAL_RESPONSES_WEBSOCKET=on` is set, then return SSE to the local client. When the opt-in is off (default), Aerial always uses HTTP upstream Responses even if a model advertises `ws:/responses`.
256
- - `messages`: usable by Claude Code through `POST /v1/messages`.
257
- - `chat`: usable by generic OpenAI Chat clients through `POST /v1/chat/completions`.
258
-
259
- Local clients should not open WebSocket connections to Aerial directly. Direct WebSocket upgrades still return `501 Not Implemented`; use HTTP `POST /v1/responses` and let Aerial choose the upstream transport. HTTP upstream Responses is the default; set `AERIAL_RESPONSES_WEBSOCKET=on` to opt into Copilot's upstream WebSocket transport for streaming Responses calls (still experimental, only effective for streaming requests on models that advertise `ws:/responses`).
260
-
261
- Known unsupported notes:
262
-
263
- - `embeddings_not_implemented`: Aerial does not expose `/v1/embeddings` yet.
264
- - `no_supported_endpoint_advertised`: the model did not declare a route Aerial can safely select.
1
+ # Aerial
2
+
3
+ [![CI](https://github.com/Xiejiayun/aerial/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/Xiejiayun/aerial/actions/workflows/ci.yml)
4
+ [![npm version](https://img.shields.io/npm/v/@jiayunxie/aerial.svg)](https://www.npmjs.com/package/@jiayunxie/aerial)
5
+
6
+ Aerial is a lightweight local-only proxy that lets one user connect their own GitHub Copilot subscription to local coding CLIs.
7
+
8
+ The MVP runs on `127.0.0.1:18181`, requires a local Aerial API key for model routes, stores credentials on the user's machine, and avoids public deployment, account sharing, quota bypass, dashboards, analytics, and image APIs.
9
+
10
+ ## MVP Support
11
+
12
+ Implemented routes:
13
+
14
+ - `GET /health` without auth
15
+ - `GET /v1/models` for model discovery, with an `aerial` support annotation per model
16
+ - `POST /v1/responses` for Codex CLI
17
+ - `POST /v1/messages` for Claude Code's Anthropic gateway path
18
+ - `POST /v1/messages/count_tokens` with a local estimate
19
+ - `POST /v1/chat/completions` as a simple passthrough fallback
20
+
21
+ Implemented CLI commands:
22
+
23
+ ```bash
24
+ aerial login
25
+ aerial key generate
26
+ aerial key print
27
+ aerial start
28
+ aerial setup codex
29
+ aerial setup claude --model <available-copilot-model-id>
30
+ aerial setup all
31
+ aerial doctor
32
+ aerial probe
33
+ ```
34
+
35
+ Service installation, rollback automation, Gemini CLI support, dashboards, and analytics are intentionally out of this MVP. For Codex, local clients keep using HTTP POST /v1/responses. Aerial can optionally use Copilot's upstream `ws:/responses` transport for streaming Responses and translate events back to SSE — this transport is opt-in (`AERIAL_RESPONSES_WEBSOCKET=on`) and HTTP is the default.
36
+
37
+ ## Requirements
38
+
39
+ - Node.js 22+
40
+ - A GitHub account with an active Copilot subscription
41
+ - Codex CLI and/or Claude Code installed locally
42
+
43
+ ## Install
44
+
45
+ ```bash
46
+ npm install -g @jiayunxie/aerial
47
+ ```
48
+
49
+ After install, the CLI is available as `aerial`.
50
+
51
+ ### Nightly builds (advanced, unstable)
52
+
53
+ Nightly pre-releases are published by the same `release.yml` workflow as stable releases, dispatched with `mode=nightly` (and, once scheduled publishing is enabled, by a daily cron on the same workflow). They are routed through a single workflow because npm only allows one trusted publisher per package. The default install command above is unaffected — nightlies are gated behind the `@nightly` dist-tag and will never be served as `latest`. To opt in:
54
+
55
+ ```bash
56
+ npm install -g @jiayunxie/aerial@nightly
57
+ ```
58
+
59
+ Nightly versions look like `0.1.1-nightly.YYYYMMDD.<sha7>`. They reflect the current `main` and may break; report regressions with `aerial --version` so the exact build can be reproduced. Switch back to stable any time with `npm install -g @jiayunxie/aerial`.
60
+
61
+ ### From source (local development)
62
+
63
+ For working directly on the Aerial codebase:
64
+
65
+ ```bash
66
+ git clone https://github.com/Xiejiayun/aerial.git
67
+ cd aerial
68
+ npm install -g .
69
+ ```
70
+
71
+ Run without global install:
72
+
73
+ ```bash
74
+ node src/cli.js --help
75
+ ```
76
+
77
+ ## First Run
78
+
79
+ Configure your local clients. This also creates Aerial's local API key and wires it into supported clients:
80
+
81
+ ```bash
82
+ aerial setup all --model <available-copilot-model-id>
83
+ ```
84
+
85
+ On Windows, newly persisted user environment variables are visible to new terminals and newly launched apps. Restart your terminal or VS Code after the first setup if Codex was already open.
86
+
87
+ Log in to GitHub with device flow:
88
+
89
+ ```bash
90
+ aerial login
91
+ ```
92
+
93
+ Start the local server:
94
+
95
+ ```bash
96
+ aerial start
97
+ ```
98
+
99
+ Check health:
100
+
101
+ ```bash
102
+ curl http://127.0.0.1:18181/health
103
+ ```
104
+
105
+ ## Codex CLI Setup
106
+
107
+ Aerial configures Codex through the Responses wire API provider path:
108
+
109
+ ```bash
110
+ aerial setup codex --model <available-copilot-model-id>
111
+ ```
112
+
113
+ This updates `~/.codex/config.toml` and creates a timestamped backup first. If you only want to inspect the exact change, set `HOME`/`USERPROFILE` to a temporary directory before running setup. The inserted provider uses:
114
+
115
+ ```toml
116
+ [model_providers.aerial]
117
+ base_url = "http://127.0.0.1:18181/v1"
118
+ wire_api = "responses"
119
+ env_key = "AERIAL_API_KEY"
120
+ ```
121
+
122
+ Aerial creates the key automatically and persists `AERIAL_API_KEY` for new user sessions when the platform supports it.
123
+
124
+ ## Claude Code Setup
125
+
126
+ Aerial configures Claude Code through its Anthropic-compatible gateway settings:
127
+
128
+ ```bash
129
+ aerial setup claude
130
+ ```
131
+
132
+ This updates `~/.claude/settings.json` and creates a timestamped backup first. If you only want to inspect the exact change, set `HOME`/`USERPROFILE` to a temporary directory before running setup. It sets:
133
+
134
+ ```json
135
+ {
136
+ "model": "<available-copilot-model-id>",
137
+ "apiKeyHelper": "aerial key print",
138
+ "env": {
139
+ "ANTHROPIC_BASE_URL": "http://127.0.0.1:18181",
140
+ "CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY": "1"
141
+ }
142
+ }
143
+ ```
144
+
145
+ The `model` field is written when you pass `--model` or set Aerial's `defaultModel`; otherwise Aerial leaves any existing Claude Code model choice alone while still switching the gateway to the local Aerial endpoint.
146
+
147
+ `aerial key print` prints the locally stored Aerial API key for Claude Code's helper flow. Users normally do not need to call it directly.
148
+
149
+ When switching from another Anthropic-compatible gateway, setup removes stale `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_MODEL`, and `ANTHROPIC_DEFAULT_*_MODEL` entries from Claude Code's managed `env` block so `apiKeyHelper` and the Aerial gateway are the active route.
150
+
151
+ ## Diagnostics
152
+
153
+ ```bash
154
+ aerial doctor
155
+ aerial probe
156
+ ```
157
+
158
+ The doctor checks config, local API key presence, GitHub login state, Node version, and local bind address.
159
+
160
+ ## Security Boundary
161
+
162
+ Aerial is for personal local use only.
163
+
164
+ - It binds to `127.0.0.1` by default.
165
+ - Model routes require `Authorization: Bearer <AERIAL_API_KEY>` or `x-api-key: <AERIAL_API_KEY>`.
166
+ - GitHub tokens are stored under the user config directory with private file permissions where supported.
167
+ - Aerial does not log raw GitHub tokens, Copilot JWTs, API keys, or request bodies by default.
168
+ - Do not expose this service publicly or use it for account sharing.
169
+
170
+ ## Current Limitations
171
+
172
+ - Copilot inference routes are an observed compatibility target, not a public stable GitHub REST API.
173
+ - `/v1/messages/count_tokens` is a local estimate, not upstream tokenization.
174
+ - Service install/uninstall and disable/rollback are not implemented yet.
175
+ - Model choice is not automated; query `/v1/models` and select an available model explicitly.
176
+ - Chat Completions requests normalize `max_tokens` to `max_completion_tokens` for newer OpenAI models that reject the older field.
177
+ - Prompt caching is upstream-managed: Aerial does not store prompt bodies locally, and it preserves or injects cache protocol fields before forwarding.
178
+
179
+
180
+
181
+ ## Capability Probe
182
+
183
+ Use `aerial probe` to inspect the live Copilot model matrix through the same local credentials Aerial uses for proxying:
184
+
185
+ ```bash
186
+ aerial probe
187
+ ```
188
+
189
+ This prints model IDs, Aerial routes, and unsupported notes such as `embeddings_not_implemented` or `no_supported_endpoint_advertised`. Models with upstream `ws:/responses` support are shown with the `responses_websocket` route.
190
+
191
+ Run low-cost live route checks when you want to verify end-to-end behavior:
192
+
193
+ ```bash
194
+ aerial probe --live
195
+ # machine-readable output
196
+ aerial probe --live --json
197
+ ```
198
+
199
+ `--live` sends one small request through the first available `responses`, `messages`, and `chat` model. It does not test every model by default, which keeps the command lightweight.
200
+ ## Prompt Cache
201
+
202
+ Aerial uses the upstream Copilot/OpenAI/Anthropic cache protocols instead of keeping a local prompt-content cache. That keeps the MVP lightweight and avoids storing prompts on disk.
203
+
204
+ Supported behavior:
205
+
206
+ - Responses and Chat Completions preserve `prompt_cache_retention` and `prompt_cache_key` when the client sends them.
207
+ - Responses and Chat Completions automatically add `prompt_cache_retention: "in_memory"` and a stable hashed `prompt_cache_key` when the client omits them.
208
+ - Anthropic Messages preserves client `cache_control` blocks. When none are present and caching is enabled, Aerial automatically adds `cache_control: { "type": "ephemeral" }` to stable `system` content, or to the final tool definition when there is no system content.
209
+ - Usage fields from upstream are returned unchanged, including `cached_tokens`, `cache_creation_input_tokens`, `cache_read_input_tokens`, and Copilot `cache_read` / `cache_write` token details when present.
210
+ - Aerial logs cache metadata to stderr as `cache_request` and `cache_observe` events. These logs do not include prompt text or request bodies.
211
+
212
+ OpenAI's prompt caching documentation says cache hits require an exact prompt prefix match and are only possible once the prompt is at least 1024 tokens. Confirm cache behavior from returned usage fields such as `usage.prompt_tokens_details.cached_tokens` or `usage.input_tokens_details.cached_tokens`; for shorter prompts this value is expected to be zero.
213
+
214
+ Aerial enables ephemeral prompt caching by default for OpenAI-style routes and Anthropic Messages. Override it only when needed:
215
+
216
+ ```bash
217
+ aerial config set promptCacheRetention in_memory
218
+ # or
219
+ aerial config set promptCacheRetention 24h
220
+ # disable automatic cache hints
221
+ aerial config set promptCacheRetention off
222
+ aerial config set promptCacheKey off
223
+ # pin all requests to an explicit cache partition
224
+ aerial config set promptCacheKey my-project
225
+ ```
226
+
227
+ You can also set it per process:
228
+
229
+ ```bash
230
+ export AERIAL_PROMPT_CACHE_RETENTION=in_memory
231
+ export AERIAL_PROMPT_CACHE_KEY=my-project
232
+ ```
233
+
234
+ Per-request fields win over the configured default. Use `24h` only with models whose upstream route accepts extended retention; Anthropic Messages still uses Anthropic-style `cache_control: { "type": "ephemeral" }`. Set `promptCacheKey` to `auto` to use Aerial's hashed stable key, `off` to omit it, or a string to force a specific cache partition.
235
+ ## Model Support
236
+
237
+ `GET /v1/models` returns Copilot's raw model metadata and adds an Aerial-specific field:
238
+
239
+ ```json
240
+ {
241
+ "id": "gpt-5.4-mini",
242
+ "supported_endpoints": ["/responses", "ws:/responses"],
243
+ "aerial": {
244
+ "supported": true,
245
+ "routes": ["responses", "responses_websocket"],
246
+ "notes": []
247
+ }
248
+ }
249
+ ```
250
+
251
+ Route meanings:
252
+
253
+ - `responses`: usable by Codex through HTTP `POST /v1/responses`.
254
+ - `responses_websocket`: Aerial can use Copilot's upstream `ws:/responses` transport for streaming `/v1/responses` requests when `AERIAL_RESPONSES_WEBSOCKET=on` is set, then return SSE to the local client. When the opt-in is off (default), Aerial always uses HTTP upstream Responses even if a model advertises `ws:/responses`.
255
+ - `messages`: usable by Claude Code through `POST /v1/messages`.
256
+ - `chat`: usable by generic OpenAI Chat clients through `POST /v1/chat/completions`.
257
+
258
+ Local clients should not open WebSocket connections to Aerial directly. Direct WebSocket upgrades still return `501 Not Implemented`; use HTTP `POST /v1/responses` and let Aerial choose the upstream transport. HTTP upstream Responses is the default; set `AERIAL_RESPONSES_WEBSOCKET=on` to opt into Copilot's upstream WebSocket transport for streaming Responses calls (still experimental, only effective for streaming requests on models that advertise `ws:/responses`).
259
+
260
+ Known unsupported notes:
261
+
262
+ - `embeddings_not_implemented`: Aerial does not expose `/v1/embeddings` yet.
263
+ - `no_supported_endpoint_advertised`: the model did not declare a route Aerial can safely select.