coding-tool-x 3.8.0 → 3.8.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.
Files changed (53) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +4 -6
  3. package/dist/web/assets/{Analytics-8dRv1aYW.js → Analytics-COEUXmd4.js} +1 -1
  4. package/dist/web/assets/{ConfigTemplates-BQRXSICO.js → ConfigTemplates-DLLkS53H.js} +1 -1
  5. package/dist/web/assets/{Home-BtzWhKIa.js → Home-DCEyLzwX.js} +1 -1
  6. package/dist/web/assets/{PluginManager-BTa-Ygf6.js → PluginManager-Oe1ZHoMO.js} +1 -1
  7. package/dist/web/assets/{ProjectList-F_cB8ldA.js → ProjectList-CWjuAFof.js} +1 -1
  8. package/dist/web/assets/{SessionList-LOrbPQvu.js → SessionList-v3ehGg9U.js} +1 -1
  9. package/dist/web/assets/{SkillManager-BzI_t-yV.js → SkillManager-DUF_qP5S.js} +1 -1
  10. package/dist/web/assets/{WorkspaceManager-Db9RGiz7.js → WorkspaceManager-CjAWBosO.js} +1 -1
  11. package/dist/web/assets/index-D5hyHPj7.js +2 -0
  12. package/dist/web/index.html +1 -1
  13. package/docs/superpowers/plans/2026-08-10-codex-conversion-compatibility.md +251 -0
  14. package/docs/superpowers/plans/2026-08-11-windows-command-window-hiding.md +90 -0
  15. package/docs/superpowers/specs/2026-08-10-codex-conversion-compatibility-design.md +61 -0
  16. package/docs/superpowers/specs/2026-08-11-windows-command-window-hiding-design.md +44 -0
  17. package/package.json +2 -2
  18. package/src/commands/update.js +1 -1
  19. package/src/config/paths.js +1 -0
  20. package/src/server/api/codex-sessions.js +14 -14
  21. package/src/server/api/gemini-sessions.js +52 -14
  22. package/src/server/api/omp-projects.js +2 -2
  23. package/src/server/api/omp-sessions.js +61 -23
  24. package/src/server/api/sessions.js +44 -2
  25. package/src/server/opencode-proxy-server.js +55 -1037
  26. package/src/server/services/claude-wire.js +694 -0
  27. package/src/server/services/codex-sessions.js +41 -233
  28. package/src/server/services/codex-wire.js +219 -0
  29. package/src/server/services/dashboard-snapshot-worker.js +5 -5
  30. package/src/server/services/gemini-sessions.js +67 -157
  31. package/src/server/services/gemini-wire.js +759 -0
  32. package/src/server/services/model-detector.js +49 -131
  33. package/src/server/services/omp-channels.js +23 -4
  34. package/src/server/services/omp-gateway.js +29 -1
  35. package/src/server/services/omp-sessions.js +102 -105
  36. package/src/server/services/omp-settings-manager.js +27 -3
  37. package/src/server/services/opencode-gateway-adapters.js +22 -1147
  38. package/src/server/services/opencode-normalization.js +546 -0
  39. package/src/server/services/opencode-sessions.js +339 -546
  40. package/src/server/services/session-converter.js +4 -4
  41. package/src/server/services/session-history-adapters/claude.js +235 -0
  42. package/src/server/services/session-history-adapters/codex.js +158 -0
  43. package/src/server/services/session-history-adapters/gemini.js +292 -0
  44. package/src/server/services/session-history-adapters/index.js +20 -0
  45. package/src/server/services/session-history-adapters/omp.js +256 -0
  46. package/src/server/services/session-history-index.js +930 -0
  47. package/src/server/services/session-history-worker.js +99 -0
  48. package/src/server/services/session-snapshot-worker.js +7 -7
  49. package/src/server/services/sessions.js +167 -775
  50. package/src/server/services/speed-test.js +85 -303
  51. package/src/server/services/sqlite-connection.js +78 -0
  52. package/src/server/services/workspace-service.js +3 -3
  53. package/dist/web/assets/index-DIWJmyL6.js +0 -2
@@ -5,7 +5,7 @@
5
5
  <link rel="icon" href="/favicon.ico">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <title>coding-tool-x · AI 编程工作台</title>
8
- <script type="module" crossorigin src="/assets/index-DIWJmyL6.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-D5hyHPj7.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/markdown-BNJfEgCA.js">
10
10
  <link rel="modulepreload" crossorigin href="/assets/vue-vendor-B5RqjEjQ.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/naive-ui-BALVpMgd.js">
@@ -0,0 +1,251 @@
1
+ # Codex Conversion Compatibility Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** Make both OpenCode's Codex conversion proxy and OMP's managed Codex provider work with Codex-only OpenAI-compatible services.
6
+
7
+ **Architecture:** Add a small shared Codex wire helper for current client identity, request metadata, headers, and body normalization. OpenCode uses it before sending upstream; OMP provider API normalization emits `openai-codex-responses` only for explicit Codex channels while generic OpenAI Responses remains unchanged.
8
+
9
+ **Tech Stack:** Node.js 22 CommonJS, Express/http, Vitest, js-yaml, installed `codex-cli 0.144.1`, OMP 17.2.12.
10
+
11
+ ---
12
+
13
+ ### Task 1: Lock the Codex wire contract with failing tests
14
+
15
+ **Files:**
16
+ - Create: `tests/unit/services/codex-wire.test.js`
17
+ - Modify: `tests/unit/services/opencode-gateway-adapters.test.js` if the existing adapter test file exists; otherwise extend `scripts/test-opencode-gateway-adapters.js`
18
+ - Modify: `tests/unit/services/omp-settings-manager.test.js:41-90`
19
+
20
+ - [ ] **Step 1: Write the failing shared-wire test**
21
+
22
+ Add a Vitest test that calls the planned `codex-wire` exports with a payload containing `system` input, `temperature`, `top_p`, `max_output_tokens`, `max_completion_tokens`, `service_tier`, `prompt_cache_retention`, and a caller session id. Assert the output has:
23
+
24
+ ```js
25
+ expect(result.body.input[0].role).toBe('developer');
26
+ expect(result.body.stream).toBe(true);
27
+ expect(result.body.store).toBe(false);
28
+ expect(result.body.include).toContain('reasoning.encrypted_content');
29
+ expect(result.body.temperature).toBeUndefined();
30
+ expect(result.body.max_output_tokens).toBeUndefined();
31
+ expect(result.headers['version']).toBe('0.144.1');
32
+ expect(result.headers.originator).toBe('codex_exec');
33
+ expect(result.headers['session-id']).toBe('session-test');
34
+ expect(result.headers['thread-id']).toBeTruthy();
35
+ expect(result.headers['x-codex-window-id']).toBeTruthy();
36
+ expect(result.body.client_metadata['x-codex-turn-metadata']).toBeTruthy();
37
+ ```
38
+
39
+ The test must import `createCodexRequest` from `src/server/services/codex-wire.js`, so it fails with module-not-found before implementation.
40
+
41
+ - [ ] **Step 2: Run the focused test and verify the expected red failure**
42
+
43
+ Run: `npx vitest run tests/unit/services/codex-wire.test.js`
44
+
45
+ Expected: FAIL because `src/server/services/codex-wire.js` does not exist.
46
+
47
+ - [ ] **Step 3: Add the OMP mapping regression test**
48
+
49
+ In `tests/unit/services/omp-settings-manager.test.js`, add a test beside the provider writer tests:
50
+
51
+ ```js
52
+ test('writes Codex-source providers with the Codex Responses API while preserving generic Responses', () => {
53
+ const manager = require('../../../src/server/services/omp-settings-manager');
54
+ const target = manager.writeManagedOmpProviders([
55
+ {
56
+ id: 'edge-codex',
57
+ providerKey: 'edge-codex',
58
+ baseUrl: 'https://edge.example/v1',
59
+ apiKey: 'secret',
60
+ gatewaySourceType: 'codex',
61
+ providerApi: 'responses',
62
+ model: 'gpt-5.5'
63
+ },
64
+ {
65
+ id: 'generic-openai',
66
+ providerKey: 'generic-openai',
67
+ baseUrl: 'https://generic.example/v1',
68
+ apiKey: 'secret',
69
+ gatewaySourceType: 'openai_compatible',
70
+ providerApi: 'openai-responses',
71
+ model: 'gpt-4.1'
72
+ }
73
+ ]);
74
+ const config = yaml.load(fs.readFileSync(target, 'utf8'));
75
+ expect(config.providers['ctx-edge-codex'].api).toBe('openai-codex-responses');
76
+ expect(config.providers['ctx-generic-openai'].api).toBe('openai-responses');
77
+ });
78
+ ```
79
+
80
+ - [ ] **Step 4: Run the OMP test and verify it fails for the current mapping**
81
+
82
+ Run: `npx vitest run tests/unit/services/omp-settings-manager.test.js -t "Codex-source providers"`
83
+
84
+ Expected: FAIL because `normalizeProviderApi('responses')` currently returns `responses`, or the provider entry does not classify the Codex source.
85
+
86
+ ---
87
+
88
+ ### Task 2: Implement the shared Codex wire helper
89
+
90
+ **Files:**
91
+ - Create: `src/server/services/codex-wire.js`
92
+ - Test: `tests/unit/services/codex-wire.test.js`
93
+
94
+ - [ ] **Step 1: Implement deterministic request identity and headers**
95
+
96
+ Export:
97
+
98
+ ```js
99
+ const CODEX_CLIENT_VERSION = '0.144.1';
100
+ function createCodexRequest(payload, options = {}) { /* { body, headers, model } */ }
101
+ function normalizeCodexResponsesInput(inputValue) { /* exported for direct tests */ }
102
+ function buildCodexTargetUrl(baseUrl) { /* exported for direct tests */ }
103
+ ```
104
+
105
+ Use `options.sessionId` as the stable prompt/session identity, defaulting to `crypto.randomUUID()`. Generate UUIDs for `threadId`, `windowId`, `turnId`, and a process-local installation id. Use `originator: 'codex_exec'` and `user-agent: codex_exec/0.144.1 (<OS> <release>; <arch>) <terminal> (codex_exec; 0.144.1)`, with `process.platform`, `process.release.name`/`TERM_PROGRAM` fallback, and `process.arch`; tests may inject `userAgent` to avoid platform dependence.
106
+
107
+ Emit lowercase header keys:
108
+
109
+ ```js
110
+ {
111
+ authorization: `Bearer ${apiKey}`,
112
+ accept: 'text/event-stream',
113
+ 'content-type': 'application/json',
114
+ originator: 'codex_exec',
115
+ 'user-agent': userAgent,
116
+ 'session-id': sessionId,
117
+ 'thread-id': threadId,
118
+ 'x-client-request-id': sessionId,
119
+ 'x-codex-window-id': windowId,
120
+ 'x-codex-turn-metadata': JSON.stringify({
121
+ installation_id: installationId,
122
+ session_id: sessionId,
123
+ thread_id: threadId,
124
+ turn_id: turnId,
125
+ window_id: windowId,
126
+ request_kind: 'turn'
127
+ })
128
+ }
129
+ ```
130
+
131
+ Do not include `openai-beta`, `version`, `session_id`, or `conversation_id` because the verified official `0.144.1` request to the target service did not emit them. Never log the authorization value.
132
+
133
+ - [ ] **Step 2: Implement Codex body normalization**
134
+
135
+ Clone JSON-compatible payload data. Fill `model` from `fallbackModel` if absent. Convert string input to a single `message/user/input_text` item. Clone array input and rewrite only `role: 'system'` to `role: 'developer'`. Force `stream: true`, `store: false`; preserve tools, tool choice, reasoning, text, and unknown fields. Ensure `include` is a clean string array containing `reasoning.encrypted_content`.
136
+
137
+ Delete Codex-rejected fields: `max_output_tokens`, `max_completion_tokens`, `temperature`, `top_p`, `top_k`, `min_p`, `presence_penalty`, `frequency_penalty`, `repetition_penalty`, `stop`, `service_tier`, `user`, `previous_response_id`, `prompt_cache_retention`, and `safety_identifier`. Set `prompt_cache_key` to the stable session id unless the caller supplied a non-empty key. Add `client_metadata` with the same identity projection as the official client; preserve non-reserved caller metadata.
138
+
139
+ - [ ] **Step 3: Run focused tests and verify green**
140
+
141
+ Run: `npx vitest run tests/unit/services/codex-wire.test.js`
142
+
143
+ Expected: PASS.
144
+
145
+ ---
146
+
147
+ ### Task 3: Replace OpenCode's duplicated Codex conversion path
148
+
149
+ **Files:**
150
+ - Modify: `src/server/opencode-proxy-server.js:24-30,1044-1121,1335-1356,3089-3158`
151
+ - Modify: `src/server/services/opencode-gateway-adapters.js:741-811`
152
+ - Create: `tests/unit/services/opencode-codex-proxy.test.js`
153
+
154
+ - [ ] **Step 1: Add the local fake-upstream integration regression test**
155
+
156
+ Start a local `http.createServer` that records request headers/body and returns a minimal successful Responses SSE stream containing `response.created`, `response.completed`, and `[DONE]`. Start the OpenCode proxy on port 0 with stubs for channel allocation, API key, and configuration dependencies as needed by the existing module seams. POST `/v1/responses` with a Codex channel and payload containing a system input plus rejected sampling fields. Assert the fake upstream receives `/v1/responses`, `originator: codex_exec`, `user-agent` containing `0.144.1`, `session-id`, `thread-id`, `x-codex-window-id`, `x-codex-turn-metadata`, and a body without the rejected fields. Assert the downstream response completes and the upstream API key was not written to logs or body metadata.
157
+
158
+ - [ ] **Step 2: Run the integration test and verify the old path fails**
159
+
160
+ Run: `npx vitest run tests/unit/services/opencode-codex-proxy.test.js`
161
+
162
+ Expected: FAIL because the current handler sends `codex_cli_rs/0.101.0`, `Version: 0.101.0`, and lacks the current Codex identity metadata.
163
+
164
+ - [ ] **Step 3: Make the proxy use the shared helper**
165
+
166
+ Import `createCodexRequest` and `buildCodexTargetUrl` in `opencode-proxy-server.js`. Remove the local `normalizeCodexResponsesInput`, `convertOpenCodePayloadToCodexResponses`, and `buildCodexTargetUrl` implementations. Keep the independent response relay functions unchanged.
167
+
168
+ In `handleCodexGatewayRequest`, derive `sessionId` from `extractSessionIdFromRequest(req, originalPayload)` or let the helper generate it. Call `createCodexRequest(originalPayload, { apiKey: effectiveKey, fallbackModel: channel.model, sessionId })`, use its `body` and `headers`, and dispatch to the helper's target URL. Keep `wantsStream`, target-model validation, and existing error reporting behavior.
169
+
170
+ - [ ] **Step 4: Remove the second adapter implementation without changing its public API**
171
+
172
+ Change `convertOpenCodePayloadToCodexResponses` in `opencode-gateway-adapters.js` to delegate to `createCodexRequest(payload, { fallbackModel, apiKey: '' })` and return `{ requestBody: body, model }`, or remove the export only after updating every test/callsite. Keep the adapter export during this cutover because the existing regression script imports it.
173
+
174
+ - [ ] **Step 5: Run focused tests and verify green**
175
+
176
+ Run: `npx vitest run tests/unit/services/codex-wire.test.js tests/unit/services/opencode-codex-proxy.test.js tests/unit/services/opencode-gateway-adapters.test.js`
177
+
178
+ Expected: PASS.
179
+
180
+ ---
181
+
182
+ ### Task 4: Emit the correct Codex API in OMP configuration
183
+
184
+ **Files:**
185
+ - Modify: `src/server/services/omp-settings-manager.js:63-71,308-364`
186
+ - Modify: `src/server/services/omp-channels.js:1-16,608-615`
187
+ - Modify: `tests/unit/services/omp-settings-manager.test.js`
188
+ - Modify: `tests/unit/services/omp-channels.test.js` if a direct normalization seam is available
189
+
190
+ - [ ] **Step 1: Implement explicit Codex alias normalization**
191
+
192
+ Change `normalizeProviderApi(value, options = {})` so:
193
+
194
+ ```js
195
+ if (normalized === 'responses' && options.gatewaySourceType === 'codex') {
196
+ return 'openai-codex-responses';
197
+ }
198
+ if (normalized === 'responses') return 'openai-responses';
199
+ ```
200
+
201
+ Preserve all explicit supported API identifiers exactly. In `buildProviderEntry`, pass `channel.gatewaySourceType` into `normalizeProviderApi`. In the OMP sync candidate, normalize the provider API once using the source provider's `api` and the selected gateway source; keep the raw `wireApi` for compatibility with existing UI fields.
202
+
203
+ - [ ] **Step 2: Run the OMP mapping regression and verify green**
204
+
205
+ Run: `npx vitest run tests/unit/services/omp-settings-manager.test.js -t "Codex-source providers"`
206
+
207
+ Expected: PASS, with generic OpenAI Responses still serialized as `openai-responses`.
208
+
209
+ - [ ] **Step 3: Run all affected service tests**
210
+
211
+ Run: `npx vitest run tests/unit/services/omp-settings-manager.test.js tests/unit/services/omp-channels.test.js tests/unit/services/omp-gateway-routing.test.js`
212
+
213
+ Expected: PASS.
214
+
215
+ ---
216
+
217
+ ### Task 5: Verify both live paths and finish cleanup
218
+
219
+ **Files:**
220
+ - Modify: `CHANGELOG.md:7-12`
221
+ - No credential-bearing files
222
+
223
+ - [ ] **Step 1: Run the project's OpenCode adapter regression script**
224
+
225
+ Run: `node scripts/test-opencode-gateway-adapters.js`
226
+
227
+ Expected: `OpenCode gateway adapters regression tests passed`.
228
+
229
+ - [ ] **Step 2: Exercise the OpenCode proxy against the real target**
230
+
231
+ Start the project proxy with a temporary channel/config supplied through an isolated temporary HOME or injected test seam. Send a minimal OpenAI Responses request using `gpt-5.5`, `stream: true`, and a prompt asking for `OK`. Pass the key only through `EDGE_API_KEY` in the process environment. Assert the response reaches `response.completed` and contains non-empty output; capture only redacted headers and status.
232
+
233
+ - [ ] **Step 3: Exercise OMP with generated Codex provider configuration**
234
+
235
+ Generate a temporary `models.yml` entry for `openai-codex-responses` with base URL `https://api.ai-edge.xyz/v1`, model `gpt-5.5`, and an environment-backed key. Run installed `omp` with the temporary config and `EDGE_API_KEY` environment variable, asking for `OK`. Assert the command succeeds. Delete temporary config and ensure the repository contains no key.
236
+
237
+ - [ ] **Step 4: Run the complete relevant verification suite**
238
+
239
+ Run:
240
+
241
+ ```bash
242
+ npx vitest run tests/unit/services/codex-wire.test.js tests/unit/services/opencode-codex-proxy.test.js tests/unit/services/opencode-gateway-adapters.test.js tests/unit/services/omp-settings-manager.test.js tests/unit/services/omp-gateway-routing.test.js
243
+ node scripts/test-opencode-gateway-adapters.js
244
+ npm run test:basic
245
+ ```
246
+
247
+ Expected: all commands exit 0. If unrelated existing failures appear, report their exact command/output separately instead of weakening the new assertions.
248
+
249
+ - [ ] **Step 5: Record the fix in the changelog and scan for credentials**
250
+
251
+ Add one Unreleased fixed entry describing the current Codex compatibility headers/metadata and OMP API classification. Run a repository search for the literal key prefix/value and confirm no match in tracked source, tests, docs, or logs. Do not add a “follow-up” placeholder.
@@ -0,0 +1,90 @@
1
+ # Windows Command Window Hiding Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** Ensure every non-interactive child process launched by the project hides its Windows console window, without changing interactive session resume behavior.
6
+
7
+ **Architecture:** Keep the existing direct child-process APIs and add `windowsHide: true` at each missing launch boundary. Use the existing injectable command runners and Windows regression script to prove options are passed, and use a focused source audit for launchers without test seams.
8
+
9
+ **Tech Stack:** Node.js `child_process`, Vitest, Node assertion-based Windows regression script.
10
+
11
+ ---
12
+
13
+ ### Task 1: Add failing regression coverage for missing launch options
14
+
15
+ **Files:**
16
+ - Modify: `scripts/test-windows-regression.js`
17
+ - Test reference: `src/commands/resume.js`, `src/server/services/session-history-worker.js`, `src/commands/update.js`
18
+
19
+ - [ ] **Step 1: Add source-level assertions for known missing launch sites**
20
+
21
+ Add a helper that reads a file and asserts each child-process call's surrounding options contain `windowsHide: true`. Apply it to the session-history worker, update version probe, and resume launcher with separate expectations: the worker and update probe require hidden windows; resume requires both `stdio: 'inherit'` and `windowsHide: true` so its terminal behavior remains explicit.
22
+
23
+ - [ ] **Step 2: Run the focused regression to verify it fails**
24
+
25
+ Run: `npm run test:windows`
26
+ Expected: FAIL because `session-history-worker.js` has no `windowsHide: true`, `update.js`'s `execFileAsync` options omit it, and/or other audited launch sites remain unhidden.
27
+
28
+ ---
29
+
30
+ ### Task 2: Hide command and worker windows at missing launch boundaries
31
+
32
+ **Files:**
33
+ - Modify: `src/server/services/session-history-worker.js:13-24`
34
+ - Modify: `src/commands/update.js:15-19`
35
+ - Modify: `src/server/services/plugins-service.js:2817-2822`
36
+ - Modify: `src/server/services/notification-hooks.js` generated OpenCode/OMP spawn option templates around lines 393 and 446, if absent after the audit
37
+ - Modify: any additional `spawn`/`exec`/`execFile`/`spawnSync`/`fork` site found by the launch inventory that lacks `windowsHide: true`
38
+
39
+ - [ ] **Step 1: Add only the missing option fields**
40
+
41
+ For `session-history-worker`, add `windowsHide: true` to the `fork` options while preserving `silent`, `stdio`, environment, IPC, timeout, and signal handling.
42
+
43
+ For `update.getLatestVersion`, add `windowsHide: true` to the options passed to `execFileAsync` while preserving the 15-second timeout.
44
+
45
+ For every other non-interactive launcher found by the audit, add `windowsHide: true` directly to its existing options object. Do not change command strings, argument arrays, shell settings, output routing, cwd, environment, or error handling. Keep `src/commands/resume.js`'s `stdio: 'inherit'` unchanged; its existing `windowsHide: true` is retained as an explicit compatibility option.
46
+
47
+ - [ ] **Step 2: Run the focused regression to verify it passes**
48
+
49
+ Run: `npm run test:windows`
50
+ Expected: PASS with `Windows 专项回归测试通过`.
51
+
52
+ ---
53
+
54
+ ### Task 3: Add runner-level tests for command option propagation
55
+
56
+ **Files:**
57
+ - Modify: `tests/unit/services/omp-settings-manager.test.js` or the existing closest service test file for each injectable runner
58
+ - Modify: `tests/unit/services/omp-auth-gateway-client.test.js` or relevant existing test file if it already covers the runner
59
+ - Modify: `tests/unit/commands/daemon.test.js` only if a tested command launcher is added there
60
+
61
+ - [ ] **Step 1: Add behavior assertions where the module exposes an injected runner**
62
+
63
+ Capture the options argument in existing `catalogRunner`, `modelsRunner`, `commandRunner`, or equivalent test callbacks and assert `options.windowsHide === true`. Use real module behavior with only the existing runner seam; do not test a mock's own implementation.
64
+
65
+ - [ ] **Step 2: Run the focused unit tests**
66
+
67
+ Run: `npx vitest run tests/unit/services/omp-settings-manager.test.js tests/unit/services/omp-auth-gateway-client.test.js tests/unit/commands/daemon.test.js`
68
+ Expected: PASS with zero failures. If a listed file has no relevant test, run the closest existing test file instead and record the exact command output.
69
+
70
+ ---
71
+
72
+ ### Task 4: Verify complete launch inventory and interactive behavior
73
+
74
+ **Files:**
75
+ - Modify: `scripts/test-windows-regression.js` only if the audit needs a final explicit launcher list
76
+
77
+ - [ ] **Step 1: Re-run the scoped launch inventory**
78
+
79
+ Run the repository search for `spawn`, `spawnSync`, `exec`, `execFile`, `execSync`, and `fork` under `src`, `bin`, and `scripts`. Review every actual child-process call, ignoring regex `.exec` and database `.exec` calls. Confirm every non-interactive launch has `windowsHide: true` and the resume launcher retains `stdio: 'inherit'`.
80
+
81
+ - [ ] **Step 2: Run all required verification commands**
82
+
83
+ Run: `npm run test:windows`
84
+ Run: `npm run test:unit`
85
+ Run: `npm test`
86
+ Expected: each command exits 0 with no test failures. The Windows-specific script may be run on the current non-Windows host because it uses platform-parameterized helpers and source assertions.
87
+
88
+ - [ ] **Step 3: Inspect the final diff for scope**
89
+
90
+ Confirm only the approved spec/plan, runtime launch options, and focused regression tests changed. Do not alter unrelated existing working-tree changes in `CHANGELOG.md`, `package.json`, `package-lock.json`, `src/server/services/omp-gateway.js`, or `tests/unit/services/omp-gateway.test.js`.
@@ -0,0 +1,61 @@
1
+ # Codex Conversion Compatibility Design
2
+
3
+ ## Goal
4
+
5
+ Restore Codex-only upstream compatibility for both OpenCode conversion proxy requests and OMP-managed providers, using `https://api.ai-edge.xyz/v1` with `gpt-5.5` as the live acceptance target without persisting credentials.
6
+
7
+ ## Observed failure
8
+
9
+ - The installed official `codex-cli 0.144.1` succeeds against the target service and returns `OK`.
10
+ - The project's current simulated Codex request uses a hard-coded `0.101.0` identity and receives HTTP 403: `This account only allows Codex official clients`.
11
+ - The OpenCode proxy contains a duplicated Codex payload converter in `opencode-proxy-server.js` in addition to `opencode-gateway-adapters.js`.
12
+ - OMP maps `responses` to generic `openai-responses`; Codex-only services require OMP's `openai-codex-responses` provider contract.
13
+
14
+ ## Architecture
15
+
16
+ ### Shared Codex compatibility module
17
+
18
+ Add one focused module under `src/server/services/` that owns Codex wire compatibility:
19
+
20
+ - current pinned client identity matching the reference implementation;
21
+ - stable request-scoped session, conversation, thread, window, and client metadata;
22
+ - Codex request headers;
23
+ - Codex Responses body normalization.
24
+
25
+ The body normalization will force streaming upstream, disable storage, convert system roles to developer roles, ensure encrypted reasoning inclusion, and remove unsupported sampling/output-cap fields. It will preserve caller-owned tools, input items, model selection, and response conversion semantics.
26
+
27
+ ### OpenCode conversion path
28
+
29
+ `opencode-proxy-server.js` will import the shared compatibility functions instead of maintaining a second payload converter. Its responsibilities remain routing, credential selection, upstream transport, SSE collection/relay, and conversion back to the caller's requested streaming mode.
30
+
31
+ Session identity will derive from an existing prompt cache/session identifier when supplied; otherwise the handler creates one request identity and uses it consistently in headers and body metadata.
32
+
33
+ ### OMP configuration path
34
+
35
+ OMP remains a byte-transparent data-plane gateway. For channels configured with Codex conversion semantics, generated provider configuration must use `openai-codex-responses`, allowing OMP's native provider implementation to construct the official Codex wire request. Generic OpenAI Responses providers remain `openai-responses`; no global remapping is allowed.
36
+
37
+ The normalization decision is based on the channel's explicit provider API first and Codex gateway source only when a generic `responses` alias would otherwise be emitted.
38
+
39
+ ## Error handling
40
+
41
+ - Invalid or missing models continue to fail before upstream dispatch.
42
+ - Invalid upstream URLs continue to return the existing structured proxy error.
43
+ - Upstream status and body remain visible through the existing failure logger without logging authorization credentials.
44
+ - The compatibility layer is deterministic and side-effect free except for explicitly injected/generated identity values.
45
+
46
+ ## Tests
47
+
48
+ 1. Adapter unit test: Codex body contains current compatibility metadata and removes every unsupported sampling/output-cap field.
49
+ 2. Header unit test: current version, beta, originator, session/conversation/thread/window identifiers, and official-compatible user agent are emitted consistently.
50
+ 3. OpenCode proxy integration test with a local fake upstream: inspect the actual headers/body crossing the process boundary and return a minimal Responses SSE stream.
51
+ 4. OMP settings test: a Codex-source channel using `responses` emits `openai-codex-responses`; a generic OpenAI channel remains `openai-responses`.
52
+ 5. Live acceptance:
53
+ - OpenCode proxy conversion path against the provided service;
54
+ - generated OMP provider exercised by installed OMP against the same service.
55
+
56
+ ## Non-goals
57
+
58
+ - No WebSocket implementation in the OpenCode proxy.
59
+ - No OAuth or ChatGPT account-token handling.
60
+ - No change to generic OpenAI, Claude, or Gemini conversion behavior.
61
+ - No persistence of the provided API key in source, fixtures, logs, or design documents.
@@ -0,0 +1,44 @@
1
+ # Windows Command Window Hiding Design
2
+
3
+ **Date:** 2026-08-11
4
+
5
+ ## Goal
6
+
7
+ Prevent Windows console windows from appearing for every background, non-interactive command launched by the project, including `ps`, PowerShell, `node`, `npm`, PM2, MCP, and worker processes, while preserving the current terminal behavior of interactive session resume commands.
8
+
9
+ ## Scope
10
+
11
+ The audit covers actual child-process launch APIs under `src/`, `bin/`, and project scripts: `spawn`, `spawnSync`, `exec`, `execFile`, `execSync`, and `fork`.
12
+
13
+ Every non-interactive child-process launch must pass `windowsHide: true`. This includes command probes, process inspection/termination, package installation and update operations, PM2 shell commands, MCP transports, OAuth/provider probes, generated notification helpers, certificate generation, web builds, and background workers.
14
+
15
+ The interactive session-resume path in `src/commands/resume.js` remains terminal-attached (`stdio: 'inherit'`) so Claude/CLI input and output continue to work in the caller's terminal. It is not treated as a hidden background process.
16
+
17
+ ## Design
18
+
19
+ Use the existing project convention: add or retain `windowsHide: true` directly in each child-process options object. Do not introduce a process wrapper or global monkey patch. This keeps dependency injection used by tests intact, limits the behavioral change to Windows process creation, and avoids changing command arguments, shell selection, stdio, environment, cwd, timeout, or error handling.
20
+
21
+ The implementation will update only missing call sites. Existing `windowsHide: true` options remain unchanged. For `fork` workers, add the option alongside the existing `stdio`/`silent` configuration. For callback and promise-based APIs, options must be passed to the actual launch call, not merely to a higher-level helper that may be bypassed.
22
+
23
+ ## Affected Areas
24
+
25
+ The implementation audit will verify and update the following areas when a launch lacks the option:
26
+
27
+ - `src/commands/`: log following, updates, and any other command launchers; preserve interactive resume semantics.
28
+ - `src/utils/`: process/port inspection and termination commands.
29
+ - `src/plugins/`: plugin installation commands.
30
+ - `src/server/api/`: PM2 autostart and API-triggered command launches.
31
+ - `src/server/services/`: MCP clients, OAuth/provider probes, OMP model probes, marketplace sync, certificate/web-build helpers, notification-generated Node helpers, and all worker forks.
32
+ - `scripts/`: Windows regression coverage; scripts themselves are not runtime package APIs unless they launch child processes as part of tested project behavior.
33
+
34
+ ## Testing
35
+
36
+ Add focused regression assertions using existing injectable runners where available. The assertions must verify `windowsHide: true` is passed to command runners and worker/process launch options. Extend the Windows regression test for source-level worker/background launch coverage only where existing source checks are insufficient. Keep the interactive resume test/behavior unchanged and verify its `stdio: 'inherit'` option remains present.
37
+
38
+ Run the focused unit tests for changed modules, `npm run test:windows`, and the project test command appropriate to the changed contract. A passing test must demonstrate the options are present at the actual launch boundary; source-only checks are a fallback for modules without injectable launch seams.
39
+
40
+ ## Non-goals
41
+
42
+ - Do not hide the user's interactive Claude/CLI terminal.
43
+ - Do not alter process lifetime, signal handling, shell quoting, command resolution, or output routing.
44
+ - Do not add retries, new abstractions, or unrelated Windows compatibility changes.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coding-tool-x",
3
- "version": "3.8.0",
3
+ "version": "3.8.2",
4
4
  "description": "Vibe Coding 增强工作助手 - 智能会话管理、动态渠道切换、全局搜索、实时监控",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -37,7 +37,7 @@
37
37
  "author": "CooperJiang",
38
38
  "license": "MIT",
39
39
  "engines": {
40
- "node": ">=14.0.0"
40
+ "node": ">=22.13.0"
41
41
  },
42
42
  "files": [
43
43
  "bin/",
@@ -15,7 +15,7 @@ async function getLatestVersion(packageName) {
15
15
  const { stdout } = await execFileAsync(
16
16
  npmCommand,
17
17
  ['view', packageName, 'version', '--json'],
18
- { timeout: 15000 }
18
+ { timeout: 15000, windowsHide: true }
19
19
  );
20
20
  const parsed = JSON.parse(stdout.trim());
21
21
  if (typeof parsed === 'string') return parsed;
@@ -439,6 +439,7 @@ const PATHS = {
439
439
  sessionHasCache: path.join(CACHE_DIR, 'session-has-cache.json'),
440
440
  channelModels: path.join(CACHE_DIR, 'channel-models.json'),
441
441
  channelBalanceStrategies: path.join(CACHE_DIR, 'channel-balance-strategies.json'),
442
+ sessionHistoryIndex: path.join(CACHE_DIR, 'session-history.sqlite'),
442
443
  envBackups: path.join(BACKUPS_DIR, 'env'),
443
444
  proxyRuntime: {
444
445
  claude: path.join(RUNTIME_DIR, 'claude-proxy.json'),
@@ -79,7 +79,7 @@ module.exports = (config) => {
79
79
  * GET /api/codex/sessions/search/global?keyword=xxx
80
80
  * 全局搜索
81
81
  */
82
- router.get('/search/global', (req, res) => {
82
+ router.get('/search/global', async (req, res) => {
83
83
  try {
84
84
  if (!isCodexInstalled()) {
85
85
  return res.status(404).json({ error: 'Codex CLI not installed' });
@@ -91,7 +91,7 @@ module.exports = (config) => {
91
91
  return res.status(400).json({ error: 'Keyword is required' });
92
92
  }
93
93
 
94
- const results = searchSessions(keyword);
94
+ const results = await searchSessions(keyword);
95
95
 
96
96
  // 按会话分组,统计每个会话的匹配数
97
97
  const sessionMap = new Map();
@@ -132,14 +132,14 @@ module.exports = (config) => {
132
132
  * GET /api/codex/sessions/recent/list?limit=10
133
133
  * 获取最近会话
134
134
  */
135
- router.get('/recent/list', (req, res) => {
135
+ router.get('/recent/list', async (req, res) => {
136
136
  try {
137
137
  if (!isCodexInstalled()) {
138
138
  return res.status(404).json({ error: 'Codex CLI not installed' });
139
139
  }
140
140
 
141
141
  const limit = parseInt(req.query.limit) || 5;
142
- const sessions = getRecentSessions(limit);
142
+ const sessions = await getRecentSessions(limit);
143
143
 
144
144
  res.json({
145
145
  sessions,
@@ -191,7 +191,7 @@ module.exports = (config) => {
191
191
  * GET /api/codex/sessions/:projectName/search
192
192
  * 项目级搜索
193
193
  */
194
- router.get('/:projectName/search', (req, res) => {
194
+ router.get('/:projectName/search', async (req, res) => {
195
195
  try {
196
196
  if (!isCodexInstalled()) {
197
197
  return res.status(404).json({ error: 'Codex CLI not installed' });
@@ -205,7 +205,7 @@ module.exports = (config) => {
205
205
  }
206
206
 
207
207
  // 使用全局搜索,然后过滤项目
208
- const allResults = searchSessions(keyword);
208
+ const allResults = await searchSessions(keyword);
209
209
  const filteredResults = allResults.filter(r => r.projectName === projectName);
210
210
 
211
211
  // 按会话分组
@@ -242,14 +242,14 @@ module.exports = (config) => {
242
242
  }
243
243
  });
244
244
 
245
- router.get('/:projectName/:sessionId/status', (req, res) => {
245
+ router.get('/:projectName/:sessionId/status', async (req, res) => {
246
246
  try {
247
247
  if (!isCodexInstalled()) {
248
248
  return res.status(404).json({ error: 'Codex CLI not installed' });
249
249
  }
250
250
 
251
251
  const { sessionId } = req.params;
252
- const session = getSessionById(sessionId);
252
+ const session = await getSessionById(sessionId);
253
253
  if (!session || !session.filePath) {
254
254
  return res.status(404).json({ error: 'Session not found' });
255
255
  }
@@ -267,14 +267,14 @@ module.exports = (config) => {
267
267
  }
268
268
  });
269
269
 
270
- router.get('/:projectName/:sessionId/outline', (req, res) => {
270
+ router.get('/:projectName/:sessionId/outline', async (req, res) => {
271
271
  try {
272
272
  if (!isCodexInstalled()) {
273
273
  return res.status(404).json({ error: 'Codex CLI not installed' });
274
274
  }
275
275
 
276
276
  const { sessionId } = req.params;
277
- const session = getSessionById(sessionId);
277
+ const session = await getSessionById(sessionId);
278
278
  if (!session) {
279
279
  return res.status(404).json({ error: 'Session not found' });
280
280
  }
@@ -307,7 +307,7 @@ module.exports = (config) => {
307
307
  * GET /api/codex/sessions/:projectName/:sessionId/messages
308
308
  * 获取会话的消息列表
309
309
  */
310
- router.get('/:projectName/:sessionId/messages', (req, res) => {
310
+ router.get('/:projectName/:sessionId/messages', async (req, res) => {
311
311
  const startMs = Date.now();
312
312
  try {
313
313
  if (!isCodexInstalled()) {
@@ -318,7 +318,7 @@ module.exports = (config) => {
318
318
  const { sessionId } = req.params;
319
319
  const { page = 1, limit = 20, order = 'desc' } = req.query;
320
320
 
321
- const session = getSessionById(sessionId);
321
+ const session = await getSessionById(sessionId);
322
322
 
323
323
  if (!session) {
324
324
  logPerf('GET /api/codex/sessions/:projectName/:sessionId/messages', startMs, `session=${sessionId}, not found`);
@@ -574,7 +574,7 @@ module.exports = (config) => {
574
574
  * POST /api/codex/sessions/:projectName/:sessionId/launch
575
575
  * 获取会话启动命令(用于复制)
576
576
  */
577
- router.post('/:projectName/:sessionId/launch', (req, res) => {
577
+ router.post('/:projectName/:sessionId/launch', async (req, res) => {
578
578
  try {
579
579
  if (!isCodexInstalled()) {
580
580
  return res.status(404).json({ error: 'Codex CLI not installed' });
@@ -584,7 +584,7 @@ module.exports = (config) => {
584
584
  const fs = require('fs');
585
585
 
586
586
  // 获取会话详情
587
- const session = getSessionById(sessionId);
587
+ const session = await getSessionById(sessionId);
588
588
 
589
589
  if (!session) {
590
590
  return res.status(404).json({ error: 'Session not found' });