sneakoscope 10.1.4 → 10.1.5

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 (72) hide show
  1. package/README.md +1 -1
  2. package/config/skills-hash-ledger.v1.json +7 -0
  3. package/crates/sks-core/Cargo.lock +1 -1
  4. package/crates/sks-core/Cargo.toml +1 -1
  5. package/dist/cli/install-helpers.js +2 -1
  6. package/dist/commands/doctor.js +2 -1
  7. package/dist/config/skills-manifest.json +17 -9
  8. package/dist/core/agents/official-subagent-helper-policy.js +2 -1
  9. package/dist/core/bench.js +1 -1
  10. package/dist/core/codex-app.js +6 -5
  11. package/dist/core/codex-lb/desktop-bridge/http-forward.js +11 -6
  12. package/dist/core/codex-lb/desktop-bridge/rejection-log.js +3 -0
  13. package/dist/core/codex-lb/desktop-bridge/responses-websocket.js +2 -2
  14. package/dist/core/codex-lb/desktop-bridge/security.js +4 -0
  15. package/dist/core/codex-lb/desktop-bridge/server.js +10 -1
  16. package/dist/core/codex-lb/desktop-bridge/types.js +12 -0
  17. package/dist/core/codex-native/core-skill-manifest.js +3 -2
  18. package/dist/core/codex-native/native-capability-repair-matrix.js +5 -4
  19. package/dist/core/commands/image-ux-review-command.js +23 -27
  20. package/dist/core/commands/ppt-command.js +4 -3
  21. package/dist/core/commands/qa-loop-command.js +8 -8
  22. package/dist/core/doctor/doctor-readiness-matrix.js +2 -1
  23. package/dist/core/doctor/imagegen-repair.js +4 -3
  24. package/dist/core/evidence/evidence-router.js +3 -3
  25. package/dist/core/evidence/evidence-schema.js +1 -1
  26. package/dist/core/feature-fixtures.js +2 -2
  27. package/dist/core/feature-registry.js +3 -2
  28. package/dist/core/hooks-runtime/hook-io.js +2 -1
  29. package/dist/core/image-ux-review/callout-extraction.js +1 -1
  30. package/dist/core/image-ux-review/imagegen-adapter.js +63 -185
  31. package/dist/core/image-ux-review/recapture.js +1 -1
  32. package/dist/core/image-ux-review.js +46 -46
  33. package/dist/core/imagegen/imagegen-auth-readiness.js +6 -13
  34. package/dist/core/imagegen/imagegen-capability.js +22 -15
  35. package/dist/core/imagegen/imagegen-model-policy.js +22 -0
  36. package/dist/core/imagegen/{gpt-image-2-request-validator.js → imagegen-request-validator.js} +13 -12
  37. package/dist/core/imagegen/require-imagegen.js +4 -2
  38. package/dist/core/init/skills.js +6 -5
  39. package/dist/core/managed-assets/managed-assets-manifest.js +2 -1
  40. package/dist/core/performance-budgets.json +1 -1
  41. package/dist/core/pipeline-internals/runtime-core.js +3 -2
  42. package/dist/core/pipeline-internals/runtime-gates.js +9 -8
  43. package/dist/core/ppt-review/index.js +1 -1
  44. package/dist/core/ppt-review/ppt-patch-handoff.js +2 -1
  45. package/dist/core/ppt-review/slide-imagegen-review.js +14 -9
  46. package/dist/core/ppt-review/slide-issue-extraction.js +2 -2
  47. package/dist/core/ppt.js +25 -22
  48. package/dist/core/qa-loop/qa-contract.js +5 -5
  49. package/dist/core/qa-loop.js +55 -54
  50. package/dist/core/recallpulse.js +2 -1
  51. package/dist/core/routes/design-policy.js +2 -1
  52. package/dist/core/routes/dollar-manifest-lite.js +2 -1
  53. package/dist/core/routes/evidence.js +5 -4
  54. package/dist/core/routes/ppt-policy.js +2 -1
  55. package/dist/core/routes.js +6 -5
  56. package/dist/core/stop-gate/gate-evaluator.js +3 -3
  57. package/dist/core/subagents/agent-catalog.js +2 -1
  58. package/dist/core/triwiki-wrongness/wrongness-schema.js +7 -6
  59. package/dist/core/trust-kernel/trust-report.js +6 -6
  60. package/dist/core/version.js +1 -1
  61. package/dist/core/wiki-image/callout-parser.js +1 -1
  62. package/dist/core/wiki-image/validation.js +2 -2
  63. package/dist/scripts/imagegen-real-smoke-check.js +4 -5
  64. package/dist/scripts/ppt-real-imagegen-smoke-check.js +1 -1
  65. package/dist/scripts/ppt-real-imagegen-wiring-check.js +2 -2
  66. package/dist/scripts/search-visibility-gate-lib.js +0 -1
  67. package/dist/scripts/ux-review-imagegen-blackbox-check.js +3 -3
  68. package/dist/scripts/ux-review-real-imagegen-smoke-check.js +1 -1
  69. package/dist/scripts/ux-review-run-wires-imagegen-check.js +2 -2
  70. package/package.json +1 -1
  71. package/schemas/codex/image-ux-issue-ledger.schema.json +1 -1
  72. package/dist/core/image-ux-review/codex-app-generated-image-discovery.js +0 -100
package/README.md CHANGED
@@ -16,7 +16,7 @@
16
16
  Sneakoscope Codex (`sks`) is an open-source trust layer for Codex CLI and ChatGPT Desktop. It coordinates bounded AI coding agents, records machine-verifiable evidence, preserves project memory, and blocks release claims that are not supported by current tests or artifacts. Search visibility outcomes are measured separately; SKS does not promise rankings or traffic.
17
17
  <!-- END SKS SEARCH VISIBILITY MARKETING -->
18
18
 
19
- Current package: **SKS 10.1.4**. Install the latest stable release from npm.
19
+ Current package: **SKS 10.1.5**. Install the latest stable release from npm.
20
20
 
21
21
  [Quick start](#install-in-one-command) · [Commands](#everyday-commands) · [SKS Center](#sks-center-macos) · [Documentation](#documentation) · [Changelog](CHANGELOG.md)
22
22
 
@@ -111,6 +111,7 @@
111
111
  {
112
112
  "canonical_name": "sks-design-artifact-expert",
113
113
  "trusted_sha256": [
114
+ "6db9a7088e5f8d21dec9c8eed00abe665e1e89f19dcbb6d8472d77e4551cb556",
114
115
  "a09daba0a2e479a68ed81ec6fba8d70e54c8398ed86f3d1879f874b98480b97c",
115
116
  "4a28d166eaf0bc4477b566b056034f972a7034165e36d8186daa9947bf92e3cf"
116
117
  ]
@@ -118,6 +119,7 @@
118
119
  {
119
120
  "canonical_name": "sks-design-system-builder",
120
121
  "trusted_sha256": [
122
+ "47654c9f74317abbd0a5a75f89cd2d563708e8234e8887cc7ab39bf49030abaa",
121
123
  "0a41e6ace960abaaf6abf5e7b1e53fd39f414e1e00b59fa725f2db9e3eb05b58",
122
124
  "c4f0007cad5774510bdc55e2815ce0911c220afa8d5dd4efc327ba1231c8f3da"
123
125
  ]
@@ -125,6 +127,7 @@
125
127
  {
126
128
  "canonical_name": "sks-design-ui-editor",
127
129
  "trusted_sha256": [
130
+ "6c24e950a12dec52c625964069c7c21a0220a58e0f69f977d802f44fdb03a940",
128
131
  "83a806cc93a3c5342e0dccfc6a45983382d5bf74d1226c4e3288228db22b737a",
129
132
  "1b65f0028a6dcb2f7eeab0b0df411a75546bc3aefa8a76067e1818da000c2249"
130
133
  ]
@@ -243,12 +246,14 @@
243
246
  {
244
247
  "canonical_name": "sks-image-ux-review",
245
248
  "trusted_sha256": [
249
+ "54d4169dc7f362ac9fe2d090bd2612e41d30acbc3e883d6bd382929bdfb9459e",
246
250
  "9293346873b5fb25a9d93b630064a2c8ceede2bfdfa4c6ffe7fbb7f64da6595c"
247
251
  ]
248
252
  },
249
253
  {
250
254
  "canonical_name": "sks-imagegen",
251
255
  "trusted_sha256": [
256
+ "478e3d8f805eed55c3f3e6c2069142482a7dbd8fa5d9a59f03b136e0e1cb77d0",
252
257
  "a7f466ba1829a757f38432fbe8c3a672e22e91948537dafe8f8ce09561d2256e",
253
258
  "7a1f9372b03975886233a623031f41e06fd782b1e7a3409517bb428d9c6b76dd"
254
259
  ]
@@ -256,6 +261,7 @@
256
261
  {
257
262
  "canonical_name": "sks-imagegen-source-scout",
258
263
  "trusted_sha256": [
264
+ "95faf4358f1c6567ca6894bfb577629f14a23d623716be70325c0ff9a893d42e",
259
265
  "95e71d4431a4c1f6911f0f76eaa1c2b475c52fd22d35c0a6c241e21e090507ad",
260
266
  "d648b571c674ed3be862bc0e8b2110f88c2bee54292bb009df0b0b2dbd6d400b"
261
267
  ]
@@ -316,6 +322,7 @@
316
322
  {
317
323
  "canonical_name": "sks-ppt",
318
324
  "trusted_sha256": [
325
+ "c9a0d90e12560dd3f04959869051370e6baf46eda6eb63f708ea1661c5c9d041",
319
326
  "1d4de05afd42ee5739c0ed925af9e0ac4785fc746e659c60d3d908d465e6386c",
320
327
  "02e8a0758ec6d056ce9851a5a4505b1d32e302e730be3eb169a41392a5d6b2e2",
321
328
  "329fc6ee24bb04478b18fb6ccd3691cd7cbb50e1b77ca7b6136383d00b3b0bb4"
@@ -259,7 +259,7 @@ dependencies = [
259
259
 
260
260
  [[package]]
261
261
  name = "sks-core"
262
- version = "10.1.4"
262
+ version = "10.1.5"
263
263
  dependencies = [
264
264
  "globset",
265
265
  "grep-matcher",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "10.1.4"
3
+ version = "10.1.5"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -1,3 +1,4 @@
1
+ import { IMAGEGEN_MODEL } from '../core/imagegen/imagegen-model-policy.js';
1
2
  import path from 'node:path';
2
3
  import fsp from 'node:fs/promises';
3
4
  import { stdin as input, stdout as output } from 'node:process';
@@ -60,7 +61,7 @@ export async function postinstall({ bootstrap }) {
60
61
  console.log('Configure providers explicitly with `sks bridge provider configure codex-lb ...` or `sks bridge provider configure openrouter ...`.');
61
62
  const imagegenRepair = await ensureCodexImagegenDuringInstall();
62
63
  if (imagegenRepair.status === 'ready')
63
- console.log('Codex App Image Gen: ready ($imagegen/gpt-image-2 detected).');
64
+ console.log(("Image generation provider capability ready (requested model: " + IMAGEGEN_MODEL + "; output not verified)."));
64
65
  else if (imagegenRepair.status === 'recovered')
65
66
  console.log('Codex App Image Gen: recovered and re-detected. Start a new Codex/Work task; restart the desktop app only if the new task still lacks $imagegen.');
66
67
  else if (imagegenRepair.status === 'blocked')
@@ -1,3 +1,4 @@
1
+ import { IMAGEGEN_MODEL } from '../core/imagegen/imagegen-model-policy.js';
1
2
  import os from 'node:os';
2
3
  import path from 'node:path';
3
4
  import { projectRoot, exists, formatBytes, nowIso, readText, writeJsonAtomic } from '../core/fsx.js';
@@ -1188,7 +1189,7 @@ async function runDoctor(args = [], root, doctorFix, deps = {}) {
1188
1189
  blockers: ['codex_imagegen_current_task_tool_manifest_unverified', 'codex_imagegen_real_output_unverified'],
1189
1190
  manual_actions: [
1190
1191
  'Start a fresh Codex/Work task so $imagegen is present in its tool manifest.',
1191
- 'Invoke $imagegen with gpt-image-2 and bind the selected raster output path to route evidence.'
1192
+ ("Invoke $imagegen with " + IMAGEGEN_MODEL + " and bind the selected raster output path to route evidence.")
1192
1193
  ],
1193
1194
  communication_test: {
1194
1195
  level: 'flag_level',
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema": "sks.skills-manifest.v1",
3
- "package_version": "10.1.4",
3
+ "package_version": "10.1.5",
4
4
  "skills": [
5
5
  {
6
6
  "canonical_name": "sks",
@@ -138,8 +138,9 @@
138
138
  {
139
139
  "canonical_name": "sks-design-artifact-expert",
140
140
  "type": "official",
141
- "content_sha256": "a09daba0a2e479a68ed81ec6fba8d70e54c8398ed86f3d1879f874b98480b97c",
141
+ "content_sha256": "6db9a7088e5f8d21dec9c8eed00abe665e1e89f19dcbb6d8472d77e4551cb556",
142
142
  "hash_history": [
143
+ "a09daba0a2e479a68ed81ec6fba8d70e54c8398ed86f3d1879f874b98480b97c",
143
144
  "4a28d166eaf0bc4477b566b056034f972a7034165e36d8186daa9947bf92e3cf"
144
145
  ],
145
146
  "deprecated_aliases": []
@@ -147,8 +148,9 @@
147
148
  {
148
149
  "canonical_name": "sks-design-system-builder",
149
150
  "type": "official",
150
- "content_sha256": "0a41e6ace960abaaf6abf5e7b1e53fd39f414e1e00b59fa725f2db9e3eb05b58",
151
+ "content_sha256": "47654c9f74317abbd0a5a75f89cd2d563708e8234e8887cc7ab39bf49030abaa",
151
152
  "hash_history": [
153
+ "0a41e6ace960abaaf6abf5e7b1e53fd39f414e1e00b59fa725f2db9e3eb05b58",
152
154
  "c4f0007cad5774510bdc55e2815ce0911c220afa8d5dd4efc327ba1231c8f3da"
153
155
  ],
154
156
  "deprecated_aliases": []
@@ -156,8 +158,9 @@
156
158
  {
157
159
  "canonical_name": "sks-design-ui-editor",
158
160
  "type": "official",
159
- "content_sha256": "83a806cc93a3c5342e0dccfc6a45983382d5bf74d1226c4e3288228db22b737a",
161
+ "content_sha256": "6c24e950a12dec52c625964069c7c21a0220a58e0f69f977d802f44fdb03a940",
160
162
  "hash_history": [
163
+ "83a806cc93a3c5342e0dccfc6a45983382d5bf74d1226c4e3288228db22b737a",
161
164
  "1b65f0028a6dcb2f7eeab0b0df411a75546bc3aefa8a76067e1818da000c2249"
162
165
  ],
163
166
  "deprecated_aliases": []
@@ -305,15 +308,18 @@
305
308
  {
306
309
  "canonical_name": "sks-image-ux-review",
307
310
  "type": "core",
308
- "content_sha256": "9293346873b5fb25a9d93b630064a2c8ceede2bfdfa4c6ffe7fbb7f64da6595c",
309
- "hash_history": [],
311
+ "content_sha256": "54d4169dc7f362ac9fe2d090bd2612e41d30acbc3e883d6bd382929bdfb9459e",
312
+ "hash_history": [
313
+ "9293346873b5fb25a9d93b630064a2c8ceede2bfdfa4c6ffe7fbb7f64da6595c"
314
+ ],
310
315
  "deprecated_aliases": []
311
316
  },
312
317
  {
313
318
  "canonical_name": "sks-imagegen",
314
319
  "type": "official",
315
- "content_sha256": "a7f466ba1829a757f38432fbe8c3a672e22e91948537dafe8f8ce09561d2256e",
320
+ "content_sha256": "478e3d8f805eed55c3f3e6c2069142482a7dbd8fa5d9a59f03b136e0e1cb77d0",
316
321
  "hash_history": [
322
+ "a7f466ba1829a757f38432fbe8c3a672e22e91948537dafe8f8ce09561d2256e",
317
323
  "7a1f9372b03975886233a623031f41e06fd782b1e7a3409517bb428d9c6b76dd"
318
324
  ],
319
325
  "deprecated_aliases": []
@@ -321,8 +327,9 @@
321
327
  {
322
328
  "canonical_name": "sks-imagegen-source-scout",
323
329
  "type": "official",
324
- "content_sha256": "95e71d4431a4c1f6911f0f76eaa1c2b475c52fd22d35c0a6c241e21e090507ad",
330
+ "content_sha256": "95faf4358f1c6567ca6894bfb577629f14a23d623716be70325c0ff9a893d42e",
325
331
  "hash_history": [
332
+ "95e71d4431a4c1f6911f0f76eaa1c2b475c52fd22d35c0a6c241e21e090507ad",
326
333
  "d648b571c674ed3be862bc0e8b2110f88c2bee54292bb009df0b0b2dbd6d400b"
327
334
  ],
328
335
  "deprecated_aliases": []
@@ -388,8 +395,9 @@
388
395
  {
389
396
  "canonical_name": "sks-ppt",
390
397
  "type": "official",
391
- "content_sha256": "1d4de05afd42ee5739c0ed925af9e0ac4785fc746e659c60d3d908d465e6386c",
398
+ "content_sha256": "c9a0d90e12560dd3f04959869051370e6baf46eda6eb63f708ea1661c5c9d041",
392
399
  "hash_history": [
400
+ "1d4de05afd42ee5739c0ed925af9e0ac4785fc746e659c60d3d908d465e6386c",
393
401
  "02e8a0758ec6d056ce9851a5a4505b1d32e302e730be3eb169a41392a5d6b2e2",
394
402
  "329fc6ee24bb04478b18fb6ccd3691cd7cbb50e1b77ca7b6136383d00b3b0bb4"
395
403
  ],
@@ -1,3 +1,4 @@
1
+ import { IMAGEGEN_MODEL } from '../imagegen/imagegen-model-policy.js';
1
2
  import path from 'node:path';
2
3
  import { nowIso, readJson, readText, writeJsonAtomic } from '../fsx.js';
3
4
  export const OFFICIAL_SUBAGENT_HELPER_POLICY_SCHEMA = 'sks.official-subagent-helper-policy.v1';
@@ -42,7 +43,7 @@ export async function writeOfficialSubagentHelperPolicy(root, input = {}) {
42
43
  built_in_agents_allowed: builtInAgentsAllowed,
43
44
  codex_app_capabilities_allowed: codexAppCapabilitiesAllowed,
44
45
  codex_builtin_imagegen_helper_allowed: true,
45
- preferred_image_generation_surface: 'Codex App $imagegen/gpt-image-2',
46
+ preferred_image_generation_surface: ("Selected image provider: " + IMAGEGEN_MODEL + ""),
46
47
  codex_app_builtin_evidence_class: 'codex_app_builtin',
47
48
  api_fallback_evidence_class: 'api_fallback',
48
49
  provider_surface_evidence_required: true,
@@ -69,7 +69,7 @@ export const TRUST_VALIDATE_BENCH_COMMAND = 'sks trust validate bench-fixture --
69
69
  export const CORE_BENCH_WARMUP_ITERATIONS = 1;
70
70
  export const UX_REVIEW_STAGED_LATENCY_BUDGETS = Object.freeze({
71
71
  source_screenshot_ingest: 500,
72
- gpt_image_2_generation: 120_000,
72
+ imagegen_generation: 120_000,
73
73
  callout_extraction: 120_000,
74
74
  fix_task_planning: 500,
75
75
  recapture_re_review: 120_000,
@@ -1,3 +1,4 @@
1
+ import { IMAGEGEN_MODEL, CODEX_BUILTIN_IMAGEGEN_MODEL } from './imagegen/imagegen-model-policy.js';
1
2
  import path from 'node:path';
2
3
  import os from 'node:os';
3
4
  import fsp from 'node:fs/promises';
@@ -483,7 +484,7 @@ function codexNativeSksMenuStatus() {
483
484
  export function codexAppGuidance({ appInstalled, codex, mcpList, featureList, requiredFeatureFlags = {}, requiredFeatureFlagsOk = true, defaultPlugins = { ok: true, missing_enabled: [] }, productDesignPlugin = null, pluginSkillShadows = { ok: true, blocking: [] }, fastModeConfig = { ok: true, blockers: [] }, providerModelUi = null, nativeSksMenu = null, gitActions = { ok: true, blockers: [] }, imageGenerationReady, inAppBrowserReady, browserUseFeatureReady, computerUseReady, browserUseReady, browserToolReady, computerUseMcpListed, browserUseMcpListed, chromeExtension, remoteControl }) {
484
485
  const lines = [];
485
486
  if (!appInstalled) {
486
- lines.push('Install and open Codex App for first-party MCP/plugin tools. Base Codex CLI flows remain available, but Codex Computer Use and imagegen/gpt-image-2 evidence will be unavailable until Codex App is ready.');
487
+ lines.push(("Install and open Codex App for first-party MCP/plugin tools. Base Codex CLI flows remain available, but Codex Computer Use requires the App; imagegen/" + IMAGEGEN_MODEL + " evidence will be unavailable until Codex App is ready."));
487
488
  lines.push(`Docs: ${CODEX_APP_DOCS_URL}`);
488
489
  }
489
490
  if (!codex?.bin)
@@ -571,10 +572,10 @@ export function codexAppGuidance({ appInstalled, codex, mcpList, featureList, re
571
572
  lines.push('Codex Chrome Extension is ready; SKS web/browser/webapp QA and UX review should use it before any other web surface.');
572
573
  }
573
574
  if (imageGenerationReady) {
574
- lines.push('Image generation is enabled; required raster assets and generated image-review evidence must invoke $imagegen/gpt-image-2 and record real output.');
575
+ lines.push(("The built-in image tool is enabled. Current-model image work requires a provider that explicitly selects " + IMAGEGEN_MODEL + " and record real output."));
575
576
  }
576
577
  else if (appInstalled || codex?.bin) {
577
- lines.push('Codex image_generation was not visible from `codex features list`. Required imagegen/gpt-image-2 evidence must stay blocked or unverified until $imagegen is available in Codex App.');
578
+ lines.push(("Codex image_generation was not visible from `codex features list`. Required imagegen/" + IMAGEGEN_MODEL + " evidence must stay blocked or unverified until $imagegen is available in Codex App."));
578
579
  lines.push('Run: sks doctor --fix to attempt Codex CLI/App image_generation repair, then rerun the blocked visual route.');
579
580
  }
580
581
  if (computerUseReady && !computerUseMcpListed) {
@@ -588,7 +589,7 @@ export function codexAppGuidance({ appInstalled, codex, mcpList, featureList, re
588
589
  lines.push('Browser Use plugin files are installed, but `codex mcp list` does not list a browser-use MCP server. Treat Browser Use as plugin-scoped, not as SKS browser verification evidence.');
589
590
  }
590
591
  if (!lines.length)
591
- lines.push('Codex App, Codex CLI, Chrome Extension, native Computer Use, Browser tooling, and image generation checks look ready. Web UI E2E uses the Chrome Extension path; native non-web visual evidence uses Computer Use; generated image evidence still requires $imagegen/gpt-image-2 output.');
592
+ lines.push(("Codex App, Codex CLI, Chrome Extension, native Computer Use, Browser tooling, and built-in image feature checks look configured. Web UI E2E uses the Chrome Extension path; native non-web visual evidence uses Computer Use; generated image evidence still requires $imagegen/" + IMAGEGEN_MODEL + " output."));
592
593
  return lines;
593
594
  }
594
595
  export function formatCodexAppStatus(status, { includeRaw = false } = {}) {
@@ -609,7 +610,7 @@ export function formatCodexAppStatus(status, { includeRaw = false } = {}) {
609
610
  `Chrome Ext: ${status.chrome_extension?.ok ? 'ok' : `setup ${(status.chrome_extension?.blockers || []).join(', ') || 'required'}`}`,
610
611
  `Computer Use:${status.mcp.has_computer_use ? status.mcp.computer_use_source === 'plugin_cache' ? ' installed (verify @Computer in thread)' : ' ok' : ' missing'}`,
611
612
  `Browser: ${status.features?.browser_tool_ready ? `ok (${status.features.browser_tool_source})` : status.mcp.has_browser_use ? status.mcp.browser_use_source === 'plugin_cache' ? 'installed (plugin scoped)' : 'ok' : 'missing'}`,
612
- `Image Gen: ${status.features?.image_generation ? 'ok ($imagegen/gpt-image-2)' : status.features?.checked ? 'missing' : 'not checked'}`,
613
+ `Image Gen: ${status.features?.image_generation ? (`enabled (${CODEX_BUILTIN_IMAGEGEN_MODEL}; ${IMAGEGEN_MODEL} provider unverified)`) : status.features?.checked ? 'missing' : 'not checked'}`,
613
614
  `Ready: ${status.ok ? 'yes' : 'no'}`,
614
615
  '',
615
616
  ...status.guidance.map((line) => `- ${line}`)
@@ -7,7 +7,7 @@ import { buildOfficialPassthroughHeaders, buildProviderUpstreamHeaders, rewriteR
7
7
  import { createDesktopBridgeRejectionLogger } from './rejection-log.js';
8
8
  import { ensureDesktopBridgeRemoteTarget, isUnreachableUpstreamError, refreshDesktopBridgeRemoteTarget, resolveAndBindDesktopBridgeRouteContext, resolveCodexSessionIdentity, resolveDesktopBridgeTarget, safeBridgeErrorCode, singleBridgeHeader } from './security.js';
9
9
  import { desktopBridgeListenOrigin } from './state.js';
10
- import { DesktopBridgeError } from './types.js';
10
+ import { DEFAULT_DESKTOP_BRIDGE_MAX_REQUEST_BODY_BYTES, DesktopBridgeError, DesktopBridgeRequestBodyTooLargeError } from './types.js';
11
11
  const MAX_UPSTREAM_ERROR_BODY_BYTES = 1024 * 1024;
12
12
  function decodeBridgeRequestBody(body, encoding, maximum) {
13
13
  const options = { maxOutputLength: maximum };
@@ -20,7 +20,9 @@ function decodeBridgeRequestBody(body, encoding, maximum) {
20
20
  try {
21
21
  return zlib.inflateSync(body, options);
22
22
  }
23
- catch {
23
+ catch (error) {
24
+ if (error.code === 'ERR_BUFFER_TOO_LARGE')
25
+ throw error;
24
26
  return zlib.inflateRawSync(body, options);
25
27
  }
26
28
  case 'br':
@@ -38,6 +40,9 @@ function decodeBridgeRequestBody(body, encoding, maximum) {
38
40
  catch (error) {
39
41
  if (error instanceof DesktopBridgeError)
40
42
  throw error;
43
+ if (error.code === 'ERR_BUFFER_TOO_LARGE') {
44
+ throw new DesktopBridgeRequestBodyTooLargeError(maximum, 'decoded');
45
+ }
41
46
  throw new DesktopBridgeError('bridge_responses_body_invalid_json');
42
47
  }
43
48
  }
@@ -48,14 +53,14 @@ function bodyCarriesModel(rawUrl) {
48
53
  async function readBoundedBody(req, maximum) {
49
54
  const declared = Number(req.headers['content-length'] || 0);
50
55
  if (Number.isFinite(declared) && declared > maximum)
51
- throw new DesktopBridgeError('bridge_request_body_too_large');
56
+ throw new DesktopBridgeRequestBodyTooLargeError(maximum, 'encoded', declared);
52
57
  const chunks = [];
53
58
  let total = 0;
54
- for await (const raw of req) {
59
+ for await (const raw of req.iterator({ destroyOnReturn: false })) {
55
60
  const chunk = Buffer.isBuffer(raw) ? raw : Buffer.from(raw);
56
61
  total += chunk.length;
57
62
  if (total > maximum)
58
- throw new DesktopBridgeError('bridge_request_body_too_large');
63
+ throw new DesktopBridgeRequestBodyTooLargeError(maximum, 'encoded', total);
59
64
  chunks.push(chunk);
60
65
  }
61
66
  return Buffer.concat(chunks);
@@ -79,7 +84,7 @@ export async function prepareDesktopBridgeRequest(req, config) {
79
84
  let payload = null;
80
85
  let contentEncodingStripped = false;
81
86
  if (bodyCarriesModel(req.url)) {
82
- const maximum = config.maxRequestBodyBytes ?? 16 * 1024 * 1024;
87
+ const maximum = config.maxRequestBodyBytes ?? DEFAULT_DESKTOP_BRIDGE_MAX_REQUEST_BODY_BYTES;
83
88
  body = await readBoundedBody(req, maximum);
84
89
  const encoding = String(req.headers['content-encoding'] || '').trim().toLowerCase();
85
90
  let decoded = body;
@@ -78,6 +78,9 @@ export function createDesktopBridgeRejectionLogger(options = {}) {
78
78
  ...(Number.isInteger(event.status) ? { status: event.status } : {}),
79
79
  ...(providerId ? { provider_id: providerId } : {}),
80
80
  ...(publicModel ? { public_model: publicModel } : {}),
81
+ ...(Number.isSafeInteger(event.max_request_body_bytes) && event.max_request_body_bytes > 0 ? { max_request_body_bytes: event.max_request_body_bytes } : {}),
82
+ ...(Number.isSafeInteger(event.request_body_bytes) && event.request_body_bytes >= 0 ? { request_body_bytes: event.request_body_bytes } : {}),
83
+ ...(event.request_body_stage === 'encoded' || event.request_body_stage === 'decoded' ? { request_body_stage: event.request_body_stage } : {}),
81
84
  });
82
85
  };
83
86
  }
@@ -5,7 +5,7 @@ import { BRIDGE_OFFICIAL_ROUTE_ID } from '../bridge-contracts.js';
5
5
  import { buildOfficialPassthroughWebSocketHeaders, buildProviderWebSocketHeaders } from './header-policy.js';
6
6
  import { createDesktopBridgeRejectionLogger } from './rejection-log.js';
7
7
  import { assertDesktopBridgeRouteContext, ensureDesktopBridgeRemoteTarget, isUnreachableUpstreamError, refreshDesktopBridgeRemoteTarget, resolveAndBindDesktopBridgeRouteContext, resolveCodexSessionIdentity, resolveDesktopBridgeTarget, safeBridgeErrorCode } from './security.js';
8
- import { DesktopBridgeError } from './types.js';
8
+ import { DEFAULT_DESKTOP_BRIDGE_MAX_REQUEST_BODY_BYTES, DesktopBridgeError } from './types.js';
9
9
  const MAX_PENDING_MESSAGES = 256;
10
10
  const CLOSE_GRACE_MS = 1_000;
11
11
  const INITIAL_CREATE_TIMEOUT_MS = 60 * 60_000;
@@ -19,7 +19,7 @@ function bytes(data) {
19
19
  }
20
20
  export function forwardResponsesWebSocket(req, socket, head, config) {
21
21
  const upgradeIdentity = resolveCodexSessionIdentity(req.headers);
22
- const maxBytes = config.maxRequestBodyBytes ?? 16 * 1024 * 1024;
22
+ const maxBytes = config.maxRequestBodyBytes ?? DEFAULT_DESKTOP_BRIDGE_MAX_REQUEST_BODY_BYTES;
23
23
  const server = new WebSocketServer({ noServer: true, clientTracking: false, perMessageDeflate: false, maxPayload: maxBytes });
24
24
  try {
25
25
  server.handleUpgrade(req, socket, head, (client) => {
@@ -569,6 +569,10 @@ export function validateDesktopBridgeConfig(config) {
569
569
  throw new DesktopBridgeError('bridge_connect_timeout_invalid');
570
570
  if (!Number.isFinite(config.idleTimeoutMs) || config.idleTimeoutMs < 1_000 || config.idleTimeoutMs > 86_400_000)
571
571
  throw new DesktopBridgeError('bridge_idle_timeout_invalid');
572
+ if (config.maxRequestBodyBytes !== undefined
573
+ && (!Number.isSafeInteger(config.maxRequestBodyBytes) || config.maxRequestBodyBytes < 1)) {
574
+ throw new DesktopBridgeError('bridge_request_body_limit_invalid');
575
+ }
572
576
  if (config.requestTimeoutMs !== undefined
573
577
  && (!Number.isFinite(config.requestTimeoutMs) || config.requestTimeoutMs < 1_000 || config.requestTimeoutMs > 120_000)) {
574
578
  throw new DesktopBridgeError('bridge_request_timeout_invalid');
@@ -5,7 +5,7 @@ import { destroyDesktopBridgeUpstreamAgents, forwardHttp, prepareDesktopBridgeRe
5
5
  import { createDesktopBridgeRejectionLogger } from './rejection-log.js';
6
6
  import { assertAllowedOrigin, assertAllowedPath, assertLoopbackPeer, assertLoopbackListenHost, assertWebSocketUpgrade, prepareDesktopBridgeConfig, safeBridgeErrorCode, validatePreparedDesktopBridgeConfig, } from './security.js';
7
7
  import { createDesktopBridgePublicState, desktopBridgeListenOrigin, desktopBridgeStatePath, refreshDesktopBridgeState, removeDesktopBridgeStateIfOwned, writeDesktopBridgeState, } from './state.js';
8
- import { DesktopBridgeError } from './types.js';
8
+ import { DesktopBridgeError, DesktopBridgeRequestBodyTooLargeError } from './types.js';
9
9
  import { PACKAGE_VERSION } from '../../version.js';
10
10
  import { DESKTOP_BRIDGE_CLIENT_PATH_PREFIX, DESKTOP_BRIDGE_DIAGNOSTIC_HEALTH_PATH, DESKTOP_BRIDGE_DIAGNOSTIC_PATH, DESKTOP_BRIDGE_DIAGNOSTIC_PROTOCOL } from './types.js';
11
11
  import { forwardWebSocket, safeEndUpgradeSocket } from './websocket-forward.js';
@@ -84,6 +84,8 @@ function handleDiagnosticWebSocket(req, socket, head) {
84
84
  consume();
85
85
  }
86
86
  function rejectionStatus(code) {
87
+ if (code === 'bridge_request_body_too_large')
88
+ return 413;
87
89
  if (code === 'bridge_path_not_allowed')
88
90
  return 404;
89
91
  if (code === 'bridge_request_capacity_exhausted')
@@ -95,6 +97,8 @@ function rejectionStatus(code) {
95
97
  return 400;
96
98
  }
97
99
  function rejectionStatusText(status) {
100
+ if (status === 413)
101
+ return 'Payload Too Large';
98
102
  if (status === 404)
99
103
  return 'Not Found';
100
104
  if (status === 403)
@@ -114,6 +118,11 @@ function writeBridgeRejection(res, error, req) {
114
118
  method: req?.method,
115
119
  url: req?.url,
116
120
  status: rejectionStatus(rejectedCode),
121
+ ...(error instanceof DesktopBridgeRequestBodyTooLargeError ? {
122
+ max_request_body_bytes: error.maximumBytes,
123
+ request_body_bytes: error.observedBytes,
124
+ request_body_stage: error.stage,
125
+ } : {}),
117
126
  });
118
127
  if (res.headersSent) {
119
128
  res.destroy(error instanceof Error ? error : undefined);
@@ -21,3 +21,15 @@ export class DesktopBridgeError extends Error {
21
21
  this.code = code;
22
22
  }
23
23
  }
24
+ export const DEFAULT_DESKTOP_BRIDGE_MAX_REQUEST_BODY_BYTES = 128 * 1024 * 1024;
25
+ export class DesktopBridgeRequestBodyTooLargeError extends DesktopBridgeError {
26
+ maximumBytes;
27
+ stage;
28
+ observedBytes;
29
+ constructor(maximumBytes, stage, observedBytes) {
30
+ super('bridge_request_body_too_large');
31
+ this.maximumBytes = maximumBytes;
32
+ this.stage = stage;
33
+ this.observedBytes = observedBytes;
34
+ }
35
+ }
@@ -1,3 +1,4 @@
1
+ import { IMAGEGEN_MODEL } from '../imagegen/imagegen-model-policy.js';
1
2
  import { PACKAGE_VERSION, nowIso, sha256 } from '../fsx.js';
2
3
  import { coreEngineeringDirectiveReference } from '../lean-engineering-policy.js';
3
4
  import { normalizeDollarSkillName, prefixKnownSksDollarReferences, sksPrefixedDollarCommand, sksPrefixedSkillName } from '../routes/dollar-prefix.js';
@@ -60,10 +61,10 @@ const CORE_SKILL_DEFINITIONS = [
60
61
  route: '$Image-UX-Review',
61
62
  purpose: 'run one screenshot-to-generated-annotation-to-UX-report pipeline without duplicate picker skills.',
62
63
  when: 'Use for $Image-UX-Review, $UX-Review, $Visual-Review, or $UI-UX-Review requests; all aliases resolve to this one canonical skill.',
63
- workflow: 'Capture or attach the real source screenshot first; web/browser/webapp capture must pass the Codex Chrome Extension readiness gate first, while Computer Use is reserved for native Mac/non-web surfaces. Send the source image as the I2I reference to the selected Codex imagegen provider with gpt-image-2 and a senior Toss UI/UX designer prompt that visibly adds numbered P0/P1/P2/P3 callouts, hierarchy, contrast, alignment, density, affordance, eye-flow arrows, and a corrected mini-comp. Then analyze the generated annotated image pixels with vision/OCR, not the source screenshot, and write the issue ledger, extraction report, and UX/UI change report with the generated image path and sha256.',
64
+ workflow: ("Capture or attach the real source screenshot first; web/browser/webapp capture must pass the Codex Chrome Extension readiness gate first, while Computer Use is reserved for native Mac/non-web surfaces. Send the source image as the I2I reference to the selected Codex imagegen provider with " + IMAGEGEN_MODEL + " and a senior Toss UI/UX designer prompt that visibly adds numbered P0/P1/P2/P3 callouts, hierarchy, contrast, alignment, density, affordance, eye-flow arrows, and a corrected mini-comp. Then analyze the generated annotated image pixels with vision/OCR, not the source screenshot, and write the issue ledger, extraction report, and UX/UI change report with the generated image path and sha256."),
64
65
  safety: 'Do not substitute text-only critique, a source-screenshot-only report, placeholders, partial image frames, mock evidence, or an unrelated API provider for the selected real imagegen path. Apply fixes only when requested and recheck changed screens.',
65
66
  cli: 'sks ux-review run --image <path> [--fix] --json; legacy $sks-ux-review, $sks-visual-review, and $sks-ui-ux-review inputs route here internally.',
66
- evidence: 'image-ux-screen-inventory.json, image-ux-gpt-image-2-request.json, image-ux-gpt-image-2-response.json, image-ux-generated-review-ledger.json, image-ux-issue-ledger.json, image-ux-callout-extraction-report.json, image-ux-iteration-report.json, and image-ux-review-gate.json.',
67
+ evidence: 'image-ux-screen-inventory.json, image-ux-imagegen-request.json, image-ux-imagegen-response.json, image-ux-generated-review-ledger.json, image-ux-issue-ledger.json, image-ux-callout-extraction-report.json, image-ux-iteration-report.json, and image-ux-review-gate.json.',
67
68
  fallback: 'Block full verification if the real source screenshot, completed generated annotated image, generated-image callout extraction, or generated-image-based UX/UI report is unavailable.'
68
69
  },
69
70
  {
@@ -1,3 +1,4 @@
1
+ import { IMAGEGEN_MODEL } from '../imagegen/imagegen-model-policy.js';
1
2
  import fs from 'node:fs/promises';
2
3
  import path from 'node:path';
3
4
  import { ensureDir, nowIso, writeJsonAtomic } from '../fsx.js';
@@ -79,16 +80,16 @@ async function stateForCapability(root, id, imageCapability, nativeFeatureMatrix
79
80
  repair_actions: verified
80
81
  ? ['postcheck-imagegen-path-contract']
81
82
  : builtInConfigured
82
- ? ['Invoke Codex App $imagegen with gpt-image-2 in a fresh task and record the selected raster output path before retrying the image route.']
83
- : ['Sign in to Codex App, enable the built-in $imagegen / gpt-image-2 surface, then verify it with a real generated raster output before retrying the image route.'],
83
+ ? [("Use the selected model-capable image provider with " + IMAGEGEN_MODEL + " in a fresh task and record the selected raster output path before retrying the image route.")]
84
+ : [("Select a provider supporting " + IMAGEGEN_MODEL + " surface, then verify it with a real generated raster output before retrying the image route.")],
84
85
  after: null,
85
86
  artifact_path: path.join(reports, 'native-capability-repair-matrix.json'),
86
87
  core_blockers: [],
87
88
  route_blockers: verified ? {} : { 'route-image': [routeBlocker] },
88
89
  manual_actions: verified ? [] : [
89
90
  builtInConfigured
90
- ? 'Generate one real raster with Codex App $imagegen / gpt-image-2 and bind its output path to the route evidence.'
91
- : 'Sign in to Codex App and enable/use the built-in $imagegen / gpt-image-2 surface before image routes.'
91
+ ? ("Generate one real raster with the selected model-capable provider: " + IMAGEGEN_MODEL + " and bind its output path to the route evidence.")
92
+ : ("Select a provider supporting " + IMAGEGEN_MODEL + " surface before image routes.")
92
93
  ],
93
94
  blockers: [],
94
95
  warnings: verified ? [] : [