@bitkyc08/opencodex 2.24.0 → 2.24.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.
@@ -10,7 +10,7 @@
10
10
  },
11
11
  {
12
12
  "path": "package.json",
13
- "sha256": "82a2b976e7b6e80afbf2b72f446758665ef0c78c83adf116f1cad43f2cfbc34b"
13
+ "sha256": "6eddddb2ce07468a1c26f394225a047ae265a21e7457f38ad6500a62c2f63b66"
14
14
  },
15
15
  {
16
16
  "path": "scripts/model-metadata.source.json",
@@ -342,7 +342,7 @@
342
342
  },
343
343
  {
344
344
  "path": "src/claude/context-windows.ts",
345
- "sha256": "41c66bc19175eace1d367f349a3311a691c3445d5b9f3f16fab7cd351939cb9c"
345
+ "sha256": "4c49d8efeec1d1060e9e61201370d4dce268b7826ca3fcff773d1d53c4c548b4"
346
346
  },
347
347
  {
348
348
  "path": "src/claude/desktop-3p-guard.ts",
@@ -354,7 +354,7 @@
354
354
  },
355
355
  {
356
356
  "path": "src/claude/desktop-3p.ts",
357
- "sha256": "10bfcaa2f68521b6b2f93c33e3d622679a25f1e9cdebc28cb0c034f25ea12aa8"
357
+ "sha256": "2218e3b4ba35dab0bf53e6b400468daf6a59e886f06afe96dd083b755e6beed0"
358
358
  },
359
359
  {
360
360
  "path": "src/claude/desktop-health.ts",
@@ -378,7 +378,7 @@
378
378
  },
379
379
  {
380
380
  "path": "src/claude/model-info.ts",
381
- "sha256": "f83181da3d11d47ebdbffcbbfbdfbc860ed2c296f30cdc36d1c7a3bec7061134"
381
+ "sha256": "8b087c12684e3e43100047227f04536321be5d2af3ca6e989693d09836fbf74f"
382
382
  },
383
383
  {
384
384
  "path": "src/claude/outbound.ts",
@@ -430,7 +430,7 @@
430
430
  },
431
431
  {
432
432
  "path": "src/cli/claude-desktop.ts",
433
- "sha256": "5ee287b01b95b4e48e3fd6f444f2642289f849b3e843b87d502a2e125c298fdb"
433
+ "sha256": "edc5fcaf62acdeaafac905bbe84fb5527cbe38d4729d4e94cff29cb7b2b4bf2f"
434
434
  },
435
435
  {
436
436
  "path": "src/cli/claude.ts",
@@ -650,7 +650,7 @@
650
650
  },
651
651
  {
652
652
  "path": "src/codex/catalog-admission.ts",
653
- "sha256": "7410dcb4e9ebfab045144d383d91655dc6be3c4fd3d57a8b2414fd1b2db6cb8d"
653
+ "sha256": "56409c9d41b58e990a35f8b4305ec58ff1ffad173e2a43a7ff0e0b671546b2ba"
654
654
  },
655
655
  {
656
656
  "path": "src/codex/catalog-refresh-status.ts",
@@ -662,7 +662,7 @@
662
662
  },
663
663
  {
664
664
  "path": "src/codex/catalog.ts",
665
- "sha256": "0c76625e56687be734c75c6be80586e298d8cbb3c471707417708ee522afb5b8"
665
+ "sha256": "879afc5613a39b758e6e8f6f63d23cef0918ffb38e6f68dff1bc3f638278bd1a"
666
666
  },
667
667
  {
668
668
  "path": "src/codex/catalog/account-models.ts",
@@ -690,7 +690,7 @@
690
690
  },
691
691
  {
692
692
  "path": "src/codex/catalog/metadata.ts",
693
- "sha256": "66d9f4c603a77d34ab44b11b9b4397cc53c6a1e88a4bfb47cbc2a6b25ef20fbc"
693
+ "sha256": "fc1cce8854a517f6c80ce137a02fb183c6710899f2a1d7536707c00ad56b4de6"
694
694
  },
695
695
  {
696
696
  "path": "src/codex/catalog/native-models.ts",
@@ -702,7 +702,7 @@
702
702
  },
703
703
  {
704
704
  "path": "src/codex/catalog/provider-fetch.ts",
705
- "sha256": "cfd71e3d50149f8f04c4d272bb1dd42f1f26bbf276cabb13bb9f21fa5ebf7dc6"
705
+ "sha256": "bfb4a4c32de3a514190c8bf4fcb660312a64a8e7d32ef9ac83106f999f8c639d"
706
706
  },
707
707
  {
708
708
  "path": "src/codex/catalog/sync.ts",
@@ -782,7 +782,7 @@
782
782
  },
783
783
  {
784
784
  "path": "src/codex/injected-marker.ts",
785
- "sha256": "20ea9be70b7dc48fc0be124ca0b4670ddbe75c9683dac96fa1cc3a8792af0f15"
785
+ "sha256": "b59a1864bc2a71dae6d0349386c137ad18ce402981f09f19fb881777fcc1556f"
786
786
  },
787
787
  {
788
788
  "path": "src/codex/integration-record.ts",
@@ -986,7 +986,7 @@
986
986
  },
987
987
  {
988
988
  "path": "src/codex/user-identity.ts",
989
- "sha256": "e2cb675b208f7ad2fdd64c5d05d9c439511f786f3887ea5cb54d87b6fe7fe8a2"
989
+ "sha256": "9641f00a7213ea6cb85cdc38adcbab1615815ca1eda878d5bfc3513e023243c7"
990
990
  },
991
991
  {
992
992
  "path": "src/codex/warmup.ts",
@@ -1046,7 +1046,7 @@
1046
1046
  },
1047
1047
  {
1048
1048
  "path": "src/grok/sync.ts",
1049
- "sha256": "20b41bbcba0f876a376c69a0a00c953632869fcba7c2dd3b5f0b850b55b987ff"
1049
+ "sha256": "a2afb3599a789276726867b559273d047b7e55a64f088ca81e4af49629e14d74"
1050
1050
  },
1051
1051
  {
1052
1052
  "path": "src/images/artifacts.ts",
@@ -1442,7 +1442,7 @@
1442
1442
  },
1443
1443
  {
1444
1444
  "path": "src/lab/projection/rebuild.ts",
1445
- "sha256": "ad3e71676d7c259b6b5993a87c5af367a7910fc86d228b37bc7cf10481eed21c"
1445
+ "sha256": "9de9cfcd613634b5afa7ba8bbea7fc24327ff1fb103ae2c70c6f5475e70695c3"
1446
1446
  },
1447
1447
  {
1448
1448
  "path": "src/lab/projection/schema.ts",
@@ -1854,11 +1854,11 @@
1854
1854
  },
1855
1855
  {
1856
1856
  "path": "src/lib/windows-elevation.ts",
1857
- "sha256": "0dc9864f87be8eed6c8dff80bec938f73fa2836c9f606f0e81c4c020494a8850"
1857
+ "sha256": "b0aab4ba8c7fe82e101669545881634ed75b59ca024522ad21e8c613c7a6cf99"
1858
1858
  },
1859
1859
  {
1860
1860
  "path": "src/lib/windows-secret-acl.ts",
1861
- "sha256": "6cbb7d5a6d9e5a1d73058af79ac02611e5cebc01fc49c7daf39ab8b0f9b41f53"
1861
+ "sha256": "1e3d7e199fa0433ca778ba3ffae2c610bf2d1850ead39c8fa1d1a1b64881bc1c"
1862
1862
  },
1863
1863
  {
1864
1864
  "path": "src/lib/windows-text.ts",
@@ -2186,7 +2186,7 @@
2186
2186
  },
2187
2187
  {
2188
2188
  "path": "src/routing/capability.ts",
2189
- "sha256": "00606e95e636abd8612bb6825c6cb8c2a62ca1eb396ebebba64501f34cd0e060"
2189
+ "sha256": "e4c7da723d461ac41b18cdf5d80085887a0b0e46917bcb4e012f52fc431475d0"
2190
2190
  },
2191
2191
  {
2192
2192
  "path": "src/routing/compatibility/assemble.ts",
@@ -2330,7 +2330,7 @@
2330
2330
  },
2331
2331
  {
2332
2332
  "path": "src/server/index.ts",
2333
- "sha256": "d3d1d19add61e536b9c1ede4676c5ef67a79ccc3e3e55302592d9f4228635d59"
2333
+ "sha256": "1ceb408f13a9700fcdd78e112fe56c35956a0e1c3207f14477ca7f18358fa3b7"
2334
2334
  },
2335
2335
  {
2336
2336
  "path": "src/server/lifecycle.ts",
@@ -2350,7 +2350,7 @@
2350
2350
  },
2351
2351
  {
2352
2352
  "path": "src/server/management-api.ts",
2353
- "sha256": "6a5238bfda08e77c550185fc54e54214da3fe6b10d4264c13f263f3c67a76939"
2353
+ "sha256": "b3e94bff1762950449552b416de10cbed72f12362dfa3c7c1f9bf043177ae1fc"
2354
2354
  },
2355
2355
  {
2356
2356
  "path": "src/server/management-auth.ts",
@@ -2358,7 +2358,7 @@
2358
2358
  },
2359
2359
  {
2360
2360
  "path": "src/server/management/agent-settings-routes.ts",
2361
- "sha256": "cf34cb29cc6e80b22b47bab31f5d6fc19e44e5604ba847ed0fbfdc500dd38136"
2361
+ "sha256": "97487f7dadf5abae969085b6058f430fce0d309e8882eaeeedae078ccc6cafc9"
2362
2362
  },
2363
2363
  {
2364
2364
  "path": "src/server/management/api-access.ts",
@@ -2378,11 +2378,11 @@
2378
2378
  },
2379
2379
  {
2380
2380
  "path": "src/server/management/config-routes.ts",
2381
- "sha256": "90a3722a21edff368f0605300f2e4e2addcc3353ca5d2372d6df99e9ebe03fbd"
2381
+ "sha256": "3a56198dbd3884707cd385a9c6b62a3c910741a0d7c576d9b30de3d7a8f86ddf"
2382
2382
  },
2383
2383
  {
2384
2384
  "path": "src/server/management/context.ts",
2385
- "sha256": "ee5a6bbd25f568425e75677d0ab11e16b4996096512599c37b785d18e6dfcb18"
2385
+ "sha256": "1b5d01755283307260fb942f35bc23b2d22bb6108e00b3c4d75a56ec13780dff"
2386
2386
  },
2387
2387
  {
2388
2388
  "path": "src/server/management/integration-routes.ts",
@@ -2410,7 +2410,7 @@
2410
2410
  },
2411
2411
  {
2412
2412
  "path": "src/server/management/native-integration-routes.ts",
2413
- "sha256": "12ebeccbcffb49b3132d799bf7faa1534da4d7f5cf6c32a15c4b75b7e6eda7c7"
2413
+ "sha256": "6c01497f98bc583ec2d76fe90e99f61076287c7a69f9dee67477ba60853e38e7"
2414
2414
  },
2415
2415
  {
2416
2416
  "path": "src/server/management/oauth-account-routes.ts",
@@ -2438,7 +2438,7 @@
2438
2438
  },
2439
2439
  {
2440
2440
  "path": "src/server/management/shared.ts",
2441
- "sha256": "111efaa480b344e3d0a0bff639c9946b4a5691c6ed02727dd929deffc532a938"
2441
+ "sha256": "2ec68a5b6d334898762c729b07eb3b98a350a4cf52d3f4debe25eb359162e863"
2442
2442
  },
2443
2443
  {
2444
2444
  "path": "src/server/management/sidebar-routes.ts",
@@ -2566,7 +2566,7 @@
2566
2566
  },
2567
2567
  {
2568
2568
  "path": "src/server/responses/core.ts",
2569
- "sha256": "15210453413f7f152083a987226695faa7a373a36f97486353272df0143f60cc"
2569
+ "sha256": "fbb0b59c9a67f10d97ec28ac48b7c2c168c7ec359f43c336a11913148ae1a11a"
2570
2570
  },
2571
2571
  {
2572
2572
  "path": "src/server/responses/empty-completion-guard.ts",
@@ -2582,7 +2582,7 @@
2582
2582
  },
2583
2583
  {
2584
2584
  "path": "src/server/responses/input-admission.ts",
2585
- "sha256": "b9536eb8dc3532c35ef65bd0a1f910e1e20db74e0ac6b581de529f4e60fd4120"
2585
+ "sha256": "cb04a5dea34d8e997c842626bad39d02baf1c64e6bcf614227bf3d49a2270131"
2586
2586
  },
2587
2587
  {
2588
2588
  "path": "src/server/responses/pacing-overload.ts",
@@ -2630,7 +2630,7 @@
2630
2630
  },
2631
2631
  {
2632
2632
  "path": "src/server/system-env.ts",
2633
- "sha256": "4a501266f1b914fa83f0646f3679d38b0fdda8399bb01cc0451054d03b93dfe1"
2633
+ "sha256": "b068365c21dbc6c92de920f9ebb3c771b9f82ecff0dfa315618923d748a93025"
2634
2634
  },
2635
2635
  {
2636
2636
  "path": "src/server/windows-tcp-drop.ts",
package/src/grok/sync.ts CHANGED
@@ -6,9 +6,7 @@
6
6
  *
7
7
  * Deps are injectable (mirrors src/codex/sync.ts) so tests can run without a live proxy.
8
8
  */
9
- import { visibleNativeSlugs, filterCatalogVisibleModels, nativeOpenAiContextWindow, type CatalogModel } from "../codex/catalog";
10
- import { providerContextCap } from "../providers/context-cap";
11
- import { OPENAI_CODEX_PROVIDER_ID } from "../providers/openai-tiers";
9
+ import { visibleNativeSlugs, filterCatalogVisibleModels, nativeContextLimits, nativeOpenAiContextWindow, type CatalogModel } from "../codex/catalog";
12
10
  import type { OcxConfig } from "../types";
13
11
  import { injectGrokConfig, type GrokInjectModel, type GrokInjectResult } from "./inject";
14
12
 
@@ -42,7 +40,7 @@ export async function syncGrokConfig(
42
40
  // default (200k) and understates models like gpt-5.6-sol, which is 372k. This is the same
43
41
  // accessor the dashboard's native rows use, so the two cannot disagree.
44
42
  ...visibleNativeSlugs(config).map(id => {
45
- const contextWindow = nativeOpenAiContextWindow(id, providerContextCap(config, OPENAI_CODEX_PROVIDER_ID));
43
+ const contextWindow = nativeOpenAiContextWindow(id, nativeContextLimits(config));
46
44
  return { id, ...(contextWindow !== undefined ? { contextWindow } : {}) };
47
45
  }),
48
46
  ...routed.map(m => ({
@@ -122,6 +122,20 @@ export function rebuildLabProjection(configDir?: string): RebuildResult {
122
122
 
123
123
  const db = new Database(paths.sqlitePath);
124
124
  let transactionOpen = false;
125
+ // Every statement prepared below, so they can be finalized before the close.
126
+ //
127
+ // Bun keeps a prepared statement alive until it is finalized or garbage collected,
128
+ // and on Windows an unfinalized statement holds the database file open: `close()`
129
+ // leaves the handle, and `close(true)` throws "database is locked". A second
130
+ // rebuild then failed to unlink the previous projection with EBUSY, and the retry
131
+ // loop in `wipeSqlite` could only turn that into a slower failure. POSIX allows
132
+ // unlinking an open file, which is why this never surfaced there.
133
+ const prepared: Array<{ finalize(): void }> = [];
134
+ const prepare = (sql: string) => {
135
+ const statement = db.prepare(sql);
136
+ prepared.push(statement);
137
+ return statement;
138
+ };
125
139
  try {
126
140
  db.exec("PRAGMA journal_mode=DELETE;");
127
141
  db.exec("PRAGMA foreign_keys=OFF;");
@@ -129,50 +143,45 @@ export function rebuildLabProjection(configDir?: string): RebuildResult {
129
143
  transactionOpen = true;
130
144
  resetProjectionSchema(db);
131
145
 
132
- db.prepare(
133
- "INSERT OR REPLACE INTO schema_meta(key, value) VALUES (?, ?)",
134
- ).run("schema_version", String(LAB_SQLITE_SCHEMA_VERSION));
135
- db.prepare(
136
- "INSERT OR REPLACE INTO schema_meta(key, value) VALUES (?, ?)",
137
- ).run("projection_spec_version", LAB_PROJECTION_SPEC_VERSION);
138
- db.prepare(
139
- "INSERT OR REPLACE INTO schema_meta(key, value) VALUES (?, ?)",
140
- ).run("built_at_ms", String(Date.now()));
146
+ const insertMeta = prepare("INSERT OR REPLACE INTO schema_meta(key, value) VALUES (?, ?)");
147
+ insertMeta.run("schema_version", String(LAB_SQLITE_SCHEMA_VERSION));
148
+ insertMeta.run("projection_spec_version", LAB_PROJECTION_SPEC_VERSION);
149
+ insertMeta.run("built_at_ms", String(Date.now()));
141
150
 
142
- const insertCorruption = db.prepare(
151
+ const insertCorruption = prepare(
143
152
  "INSERT INTO corruption(kind, line_number, event_id, detail) VALUES (?, ?, ?, ?)",
144
153
  );
145
154
  for (const c of corruptions) {
146
155
  insertCorruption.run(c.kind, c.lineNumber ?? null, c.eventId ?? null, c.detail);
147
156
  }
148
157
 
149
- const insertEvent = db.prepare(
158
+ const insertEvent = prepare(
150
159
  `INSERT INTO events(event_id, event_kind, recorded_at, producer, producer_version, payload_json, excluded, exclusion_reason)
151
160
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
152
161
  );
153
- const insertSubject = db.prepare(
162
+ const insertSubject = prepare(
154
163
  `INSERT OR IGNORE INTO subjects(subject_id, subject_kind, subject_json) VALUES (?, ?, ?)`,
155
164
  );
156
- const insertObs = db.prepare(
165
+ const insertObs = prepare(
157
166
  `INSERT INTO observations(
158
167
  event_id, subject_id, evidence_layer, suite_id, suite_version, suite_manifest_digest,
159
168
  scenario_id, scenario_version, scenario_manifest_digest, outcome, completed_at, execution_mode
160
169
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
161
170
  );
162
- const insertClaim = db.prepare(
171
+ const insertClaim = prepare(
163
172
  `INSERT INTO claims(
164
173
  event_id, subject_id, capability, polarity, source_manifest_digest,
165
174
  effective_at, recorded_at, supersedes_json, current, usable
166
175
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
167
176
  );
168
- const insertInv = db.prepare(
177
+ const insertInv = prepare(
169
178
  `INSERT INTO invalidations(event_id, reason, targets_json, recorded_at, applied) VALUES (?, ?, ?, ?, ?)`,
170
179
  );
171
- const insertPurge = db.prepare(
180
+ const insertPurge = prepare(
172
181
  `INSERT INTO purges(event_id, target_event_ids_json, target_artifact_digests_json, purge_actions_json, recorded_at)
173
182
  VALUES (?, ?, ?, ?, ?)`,
174
183
  );
175
- const insertArtifact = db.prepare(
184
+ const insertArtifact = prepare(
176
185
  `INSERT INTO artifacts(digest, artifact_class, media_type, byte_count, status, last_error)
177
186
  VALUES (?, ?, ?, ?, ?, ?)
178
187
  ON CONFLICT(digest) DO UPDATE SET
@@ -319,7 +328,7 @@ export function rebuildLabProjection(configDir?: string): RebuildResult {
319
328
  }
320
329
  }
321
330
 
322
- const insertVerdict = db.prepare(
331
+ const insertVerdict = prepare(
323
332
  `INSERT INTO verdicts(
324
333
  projection_key, subject_id, evidence_layer, suite_id, suite_version, suite_manifest_digest,
325
334
  projection_spec_version, verdict, as_of, scenario_manifest_digests_json, claim_source_digest,
@@ -369,6 +378,15 @@ export function rebuildLabProjection(configDir?: string): RebuildResult {
369
378
  } catch {
370
379
  // Closing the disposable DB is still safe if pragma restoration fails.
371
380
  }
381
+ // Finalize before closing: an outstanding statement keeps the file open on
382
+ // Windows, and the next rebuild cannot unlink the projection it is replacing.
383
+ for (const statement of prepared) {
384
+ try {
385
+ statement.finalize();
386
+ } catch {
387
+ // A statement already finalized by an error path is not a rebuild failure.
388
+ }
389
+ }
372
390
  db.close();
373
391
  artifactStore.close();
374
392
  }
@@ -175,7 +175,7 @@ export function assertTrustedSystemExecutableForTests(candidate: string, label:
175
175
  return assertTrustedSystemExecutable(candidate, label);
176
176
  }
177
177
 
178
- type ElevationExeOverrides = { powershell?: string; schtasks?: string; taskkill?: string };
178
+ type ElevationExeOverrides = { powershell?: string; schtasks?: string; taskkill?: string; icacls?: string };
179
179
  let elevationExeOverridesForTests: ElevationExeOverrides | null = null;
180
180
 
181
181
  /**
@@ -220,6 +220,15 @@ export function resolveTrustedWindowsTaskkillExe(): string {
220
220
  return assertTrustedSystemExecutable(candidate, "taskkill.exe");
221
221
  }
222
222
 
223
+ /** Absolute path to System32\\icacls.exe from a trusted system directory. */
224
+ export function resolveTrustedWindowsIcaclsExe(): string {
225
+ if (elevationExeOverridesForTests?.icacls) {
226
+ return elevationExeOverridesForTests.icacls;
227
+ }
228
+ const candidate = join(resolveTrustedWindowsSystemDirectory(), "icacls.exe");
229
+ return assertTrustedSystemExecutable(candidate, "icacls.exe");
230
+ }
231
+
223
232
  /** Stable machine-readable marker for a denied `schtasks /create`. Crosses the CLI→proxy boundary. */
224
233
  export const WINDOWS_SCHTASKS_CREATE_ACCESS_DENIED_MARKER =
225
234
  "OCX_ERROR_CODE=WINDOWS_SCHTASKS_CREATE_ACCESS_DENIED";
@@ -31,6 +31,7 @@
31
31
 
32
32
  import { existsSync, statSync } from "node:fs";
33
33
  import { env, platform } from "node:process";
34
+ import { resolveTrustedWindowsIcaclsExe } from "./windows-elevation";
34
35
  import {
35
36
  resolveCurrentWindowsPrincipal,
36
37
  resolveCurrentWindowsPrincipalAsync,
@@ -273,22 +274,55 @@ export interface IcaclsResult {
273
274
  type IcaclsRunner = (args: string[], timeoutMs: number) => IcaclsResult;
274
275
  type AsyncIcaclsRunner = (args: string[], timeoutMs: number) => Promise<IcaclsResult>;
275
276
 
277
+ function resolveIcaclsExecutable(): string {
278
+ // Same authority as schtasks/powershell: never take icacls from PATH.
279
+ // A bun-shim or stripped PATH makes `icacls.exe` throw ENOENT, which used to
280
+ // surface as "filesystem may not support per-user NTFS ACLs".
281
+ return resolveTrustedWindowsIcaclsExe();
282
+ }
283
+
284
+ function spawnFailedResult(): IcaclsResult {
285
+ return { success: false, exitCode: null, timedOut: false, stdout: "" };
286
+ }
287
+
288
+ /**
289
+ * Spawn icacls asynchronously, or return null when the executable cannot be
290
+ * launched. The pipe/ignore stdio literals stay inferred here so `stdout` keeps
291
+ * its `ReadableStream` type instead of widening to the generic default.
292
+ */
293
+ function trySpawnIcacls(args: string[]) {
294
+ try {
295
+ return Bun.spawn([resolveIcaclsExecutable(), ...args], {
296
+ stdin: "ignore",
297
+ stdout: "pipe",
298
+ stderr: "ignore",
299
+ windowsHide: true,
300
+ });
301
+ } catch {
302
+ return null;
303
+ }
304
+ }
305
+
276
306
  function defaultIcaclsRunner(args: string[], timeoutMs: number): IcaclsResult {
277
307
  // Bun.spawnSync with windowsHide: Node execFileSync has hung under the GUI/proxy even
278
308
  // with windowsHide, and console-subsystem tools flash a visible window otherwise.
279
- const result = Bun.spawnSync(["icacls.exe", ...args], {
280
- stdin: "ignore",
281
- stdout: "pipe",
282
- stderr: "ignore",
283
- timeout: timeoutMs,
284
- windowsHide: true,
285
- });
286
- return {
287
- success: result.success,
288
- exitCode: result.exitCode,
289
- timedOut: result.exitedDueToTimeout ?? false,
290
- stdout: result.stdout ? result.stdout.toString() : "",
291
- };
309
+ try {
310
+ const result = Bun.spawnSync([resolveIcaclsExecutable(), ...args], {
311
+ stdin: "ignore",
312
+ stdout: "pipe",
313
+ stderr: "ignore",
314
+ timeout: timeoutMs,
315
+ windowsHide: true,
316
+ });
317
+ return {
318
+ success: result.success,
319
+ exitCode: result.exitCode,
320
+ timedOut: result.exitedDueToTimeout ?? false,
321
+ stdout: result.stdout ? result.stdout.toString() : "",
322
+ };
323
+ } catch {
324
+ return spawnFailedResult();
325
+ }
292
326
  }
293
327
 
294
328
  /**
@@ -297,12 +331,8 @@ function defaultIcaclsRunner(args: string[], timeoutMs: number): IcaclsResult {
297
331
  * we still await process exit before classifying so settlement is confirmed.
298
332
  */
299
333
  async function defaultAsyncIcaclsRunner(args: string[], timeoutMs: number): Promise<IcaclsResult> {
300
- const proc = Bun.spawn(["icacls.exe", ...args], {
301
- stdin: "ignore",
302
- stdout: "pipe",
303
- stderr: "ignore",
304
- windowsHide: true,
305
- });
334
+ const proc = trySpawnIcacls(args);
335
+ if (!proc) return spawnFailedResult();
306
336
  let timedOutByUs = false;
307
337
  const timer = setTimeout(() => {
308
338
  timedOutByUs = true;
@@ -13,11 +13,10 @@
13
13
  import type { OcxConfig } from "../types";
14
14
  import { isCanonicalOpenAiForwardProvider, OPENAI_CODEX_PROVIDER_ID } from "../providers/openai-tiers";
15
15
  import { serviceTierSupportForModel } from "../providers/service-tier";
16
- import { applyProviderContextCap, providerContextCap } from "../providers/context-cap";
17
16
  import { PROVIDER_REGISTRY } from "../providers/registry";
18
17
  import {
19
18
  nativeInputModalities,
20
- nativeOpenAiContextWindow,
19
+ nativeContextLimits, nativeOpenAiContextWindow,
21
20
  nativeParallelToolCalls,
22
21
  nativeReasoningEfforts,
23
22
  } from "../codex/catalog/metadata";
@@ -164,11 +163,11 @@ export function candidateCapabilityEvidence(
164
163
  ?? provider?.contextWindow
165
164
  ?? registryEntry?.modelContextWindows?.[modelId]
166
165
  ?? catalogRow?.contextWindow
167
- ?? (isNative ? nativeOpenAiContextWindow(modelId) : undefined);
168
- // providerContextCaps.openai also ceilings native OpenAI rows (#1430), so routing
169
- // evidence never contradicts a capped catalog entry.
166
+ ?? (isNative ? nativeOpenAiContextWindow(modelId, nativeContextLimits(config)) : undefined);
167
+ // Native rows go through the accessor (raise-to-ceiling + opt-in). Routed rows keep
168
+ // the raw value; a provider cap on openai must not invent a window they do not have.
170
169
  const contextWindow = isNative
171
- ? applyProviderContextCap(rawContextWindow, providerContextCap(config, OPENAI_CODEX_PROVIDER_ID)) ?? rawContextWindow
170
+ ? (nativeOpenAiContextWindow(modelId, nativeContextLimits(config)) ?? rawContextWindow)
172
171
  : rawContextWindow;
173
172
 
174
173
  const modalities = provider?.modelInputModalities?.[modelId]
@@ -50,7 +50,6 @@ import { runOpenAiTierStartupMigration } from "../providers/openai-tier-startup"
50
50
  import { runAlibabaRegionStartupMigration } from "../providers/alibaba-region-startup";
51
51
  import { runModelRenameStartupMigration } from "../providers/model-rename-startup";
52
52
  import { isCanonicalOpenAiForwardProvider, OPENAI_CODEX_PROVIDER_ID } from "../providers/openai-tiers";
53
- import { providerContextCap } from "../providers/context-cap";
54
53
  import { providerCodexAccountMode } from "../providers/registry";
55
54
  import type { StorageCleanupPolicy } from "../types";
56
55
  import { MAX_DECOMPRESSED_BODY_BYTES } from "./request-decompress";
@@ -906,7 +905,7 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
906
905
  }
907
906
  throw error;
908
907
  }
909
- const { accountBoundNativeOpenAiSlugsBySelector, applyNativeVisibility, buildCatalogEntries, configuredNativeAliasSlugs, desktopAllowlistSuppressedNativeSlugs, disabledNativeSlugs, exactComboCatalogSlugs, loadCatalogTemplate, NATIVE_OPENAI_MODELS, nativeOpenAiSlugs, nativeReasoningEfforts, nativeDefaultReasoningEffort, orderForSubagents, filterCatalogVisibleModels, shouldIncludeAccountBoundNativeOpenAi, shouldIncludeNativeOpenAi, uniqueCatalogModelsForRawPublicList, visibleCodexAccountSelectors, visibleNativeSlugs, desktopVisibleNativeSlugs } = await import("../codex/catalog");
908
+ const { accountBoundNativeOpenAiSlugsBySelector, applyNativeVisibility, buildCatalogEntries, configuredNativeAliasSlugs, desktopAllowlistSuppressedNativeSlugs, disabledNativeSlugs, exactComboCatalogSlugs, loadCatalogTemplate, NATIVE_OPENAI_MODELS, nativeContextLimits, nativeOpenAiSlugs, nativeReasoningEfforts, nativeDefaultReasoningEffort, orderForSubagents, filterCatalogVisibleModels, shouldIncludeAccountBoundNativeOpenAi, shouldIncludeNativeOpenAi, uniqueCatalogModelsForRawPublicList, visibleCodexAccountSelectors, visibleNativeSlugs, desktopVisibleNativeSlugs } = await import("../codex/catalog");
910
909
  const includeNativeOpenAi = shouldIncludeNativeOpenAi(config);
911
910
  const includeAccountBoundNativeOpenAi = shouldIncludeAccountBoundNativeOpenAi(config);
912
911
  const nativeSlugs = includeNativeOpenAi
@@ -958,7 +957,7 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
958
957
  : idsParam === "desktop"
959
958
  ? "desktop3p" as const
960
959
  : (/^claude-code\//i.test(req.headers.get("user-agent") ?? "") ? "readable" as const : "desktop3p" as const);
961
- const data = buildAnthropicModelInfos([...desktopVisibleNativeSlugs(config)], goOrdered, resolveAutoContext(config.claudeCode), idStyle, activeDesktop3pAlias, providerContextCap(config, OPENAI_CODEX_PROVIDER_ID));
960
+ const data = buildAnthropicModelInfos([...desktopVisibleNativeSlugs(config)], goOrdered, resolveAutoContext(config.claudeCode), idStyle, activeDesktop3pAlias, nativeContextLimits(config));
962
961
  return jsonResponse({ data }, 200, req, policy);
963
962
  }
964
963
  if (url.searchParams.has("client_version")) {
@@ -985,7 +984,7 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
985
984
  accountSelectors,
986
985
  suppressedBareNativeSlugs,
987
986
  new Set(),
988
- providerContextCap(config, OPENAI_CODEX_PROVIDER_ID),
987
+ nativeContextLimits(config),
989
988
  accountNativeSlugs,
990
989
  accountNativeSlugsBySelector,
991
990
  config.keepNativeChatGptOnV1 === true,
@@ -1,7 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { readFileSync } from "node:fs";
3
3
  import type { CatalogModel } from "../../codex/catalog";
4
- import { catalogModelSlug, invalidateCodexModelsCache, nativeModelRows, uniqueCatalogModelsForPublicList } from "../../codex/catalog";
4
+ import { catalogModelSlug, invalidateCodexModelsCache, nativeContextLimits, nativeModelRows, uniqueCatalogModelsForPublicList } from "../../codex/catalog";
5
5
  import {
6
6
  DEFAULT_SUBAGENT_MODELS,
7
7
  codexAutoStartEnabled,
@@ -207,7 +207,7 @@ export async function handleAgentSettingsRoutes(ctx: ManagementContext): Promise
207
207
  current.apiKeys?.[0]?.key,
208
208
  "static",
209
209
  current.claudeCode.desktopProfile,
210
- providerContextCap(current, OPENAI_CODEX_PROVIDER_ID),
210
+ nativeContextLimits(current),
211
211
  );
212
212
  if (result.written && result.fingerprint) {
213
213
  current.claudeCode = { ...current.claudeCode, desktopProfile: { ...current.claudeCode.desktopProfile, appliedFingerprint: result.fingerprint, appliedAt: new Date().toISOString() } };
@@ -886,7 +886,7 @@ export async function handleAgentSettingsRoutes(ctx: ManagementContext): Promise
886
886
  latest.apiKeys?.[0]?.key,
887
887
  mode,
888
888
  state.profile,
889
- providerContextCap(latest, OPENAI_CODEX_PROVIDER_ID),
889
+ nativeContextLimits(latest),
890
890
  );
891
891
  if (!result.written) return jsonResponse({ error: result.reason ?? "Claude Desktop apply failed", saved: true, path: result.path }, 500);
892
892
  // Persist applied fingerprint + timestamp so GUI can show saved-vs-applied state.
@@ -978,7 +978,7 @@ export async function handleAgentSettingsRoutes(ctx: ManagementContext): Promise
978
978
  if (isDisabled(m.provider, m.id)) continue;
979
979
  aliases.push({ id: claudeCodeAlias(m.provider, m.id), display_name: `${m.id} (${m.provider})` });
980
980
  }
981
- const contextWindows = buildClaudeContextWindows([...visibleNativeSlugs(config)], models, providerContextCap(config, OPENAI_CODEX_PROVIDER_ID));
981
+ const contextWindows = buildClaudeContextWindows([...visibleNativeSlugs(config)], models, nativeContextLimits(config));
982
982
  const webSearchOverride = config.claudeCode?.webSearchSidecar;
983
983
  const visionOverride = config.claudeCode?.visionSidecar;
984
984
  // Auto is a RESOLUTION, recomputed per request — never stored state. Detection is
@@ -1010,7 +1010,7 @@ export async function handleAgentSettingsRoutes(ctx: ManagementContext): Promise
1010
1010
  classifierModel: config.claudeCode?.classifierModel ?? "",
1011
1011
  classifierFallbacks: config.claudeCode?.classifierFallbacks ?? [],
1012
1012
  systemEnv: config.claudeCode?.systemEnv === true,
1013
- autoConnectSupported: process.platform === "darwin",
1013
+ autoConnectSupported: (ctx.deps.platform ?? process.platform) === "darwin",
1014
1014
  maxContextTokens: config.claudeCode?.maxContextTokens ?? null,
1015
1015
  alwaysEnableEffort: config.claudeCode?.alwaysEnableEffort === true,
1016
1016
  autoContext: config.claudeCode?.autoContext !== false,
@@ -1,7 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { readFileSync } from "node:fs";
3
3
  import type { CatalogModel } from "../../codex/catalog";
4
- import { catalogModelSlug, invalidateCodexModelsCache, nativeModelRows, uniqueCatalogModelsForPublicList } from "../../codex/catalog";
4
+ import { catalogModelSlug, invalidateCodexModelsCache, nativeContextLimits, nativeModelRows, uniqueCatalogModelsForPublicList } from "../../codex/catalog";
5
5
  import {
6
6
  DEFAULT_SUBAGENT_MODELS,
7
7
  codexAutoStartEnabled,
@@ -173,7 +173,7 @@ async function syncEnabledClientIntegrations(
173
173
  config.apiKeys?.[0]?.key,
174
174
  "static",
175
175
  config.claudeCode?.desktopProfile,
176
- providerContextCap(config, OPENAI_CODEX_PROVIDER_ID),
176
+ nativeContextLimits(config),
177
177
  );
178
178
  out.push(r.written
179
179
  ? { client: "claude-desktop", ok: true, changed: true }
@@ -16,6 +16,8 @@ import type {
16
16
  } from "../../codex/app-server-restart-service";
17
17
 
18
18
  export interface ManagementApiDeps {
19
+ /** Platform seam for capability projections; does not alter host-level startup behavior. */
20
+ platform?: NodeJS.Platform;
19
21
  toggleCodexMultiAgentV2?: (enabled: boolean) => void;
20
22
  toggleDefaultModeRequestUserInput?: (enabled: boolean) => void;
21
23
  createManagementConvergeCodex?: (config: Readonly<OcxConfig>) => ConvergeCodex;
@@ -18,7 +18,7 @@
18
18
  * 011 (Claude Code), 012 (Grok).
19
19
  */
20
20
  import { loadConfig, readRuntimePort, saveConfigPreservingClaudeCode } from "../../config";
21
- import { desktopVisibleNativeSlugs, filterCatalogVisibleModels, nativeOpenAiContextWindow, visibleNativeSlugs } from "../../codex/catalog";
21
+ import { desktopVisibleNativeSlugs, filterCatalogVisibleModels, nativeContextLimits, nativeOpenAiContextWindow, visibleNativeSlugs } from "../../codex/catalog";
22
22
  import { providerContextCap } from "../../providers/context-cap";
23
23
  import { OPENAI_CODEX_PROVIDER_ID } from "../../providers/openai-tiers";
24
24
  import { inspectDesktop3pConfigLibrary, removeDesktop3pStandardPivot, writeDesktop3pConfig } from "../../claude/desktop-3p";
@@ -508,7 +508,7 @@ async function handleGrokToggle(ctx: ManagementContext): Promise<Response> {
508
508
  // Native slugs carry their context window: without it Grok falls back
509
509
  // to its own 200k default and understates a 372k model.
510
510
  ...visibleNativeSlugs(config).map(id => {
511
- const contextWindow = nativeOpenAiContextWindow(id, providerContextCap(config, OPENAI_CODEX_PROVIDER_ID));
511
+ const contextWindow = nativeOpenAiContextWindow(id, nativeContextLimits(config));
512
512
  return { id, ...(contextWindow !== undefined ? { contextWindow } : {}) };
513
513
  }),
514
514
  ...routed.map(m => ({
@@ -663,7 +663,7 @@ async function handleClaudeDesktopToggle(ctx: ManagementContext): Promise<Respon
663
663
  latest.apiKeys?.[0]?.key,
664
664
  "static",
665
665
  latest.claudeCode?.desktopProfile,
666
- providerContextCap(latest, OPENAI_CODEX_PROVIDER_ID),
666
+ nativeContextLimits(latest),
667
667
  );
668
668
  if (!result.written) return postCommitRefusal(500, "claude-desktop", "write_failed", "Claude Desktop apply failed.", { desiredEnabled: latestDesiredEnabled });
669
669
  return jsonResponse({