sneakoscope 4.8.7 → 5.3.0

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 (261) hide show
  1. package/README.md +51 -726
  2. package/config/bench-baseline.json +7 -0
  3. package/crates/sks-core/Cargo.lock +1 -1
  4. package/crates/sks-core/Cargo.toml +1 -1
  5. package/crates/sks-core/src/main.rs +1 -1
  6. package/dist/bin/install.js +35 -0
  7. package/dist/bin/sks.js +1 -1
  8. package/dist/cli/cli-theme.js +51 -0
  9. package/dist/cli/command-registry.js +15 -4
  10. package/dist/cli/help-fast.js +18 -8
  11. package/dist/cli/insane-search-command.js +36 -8
  12. package/dist/cli/install-helpers.js +32 -0
  13. package/dist/commands/codex-lb.js +2 -0
  14. package/dist/commands/doctor.js +94 -9
  15. package/dist/commands/proof.js +6 -3
  16. package/dist/config/skills-manifest.json +82 -54
  17. package/dist/core/agents/agent-conflict-graph.js +5 -0
  18. package/dist/core/agents/agent-lease.js +5 -0
  19. package/dist/core/agents/agent-ledger-schemas.js +1 -0
  20. package/dist/core/agents/agent-lifecycle.js +5 -3
  21. package/dist/core/agents/agent-orchestrator.js +268 -62
  22. package/dist/core/agents/agent-output-validator.js +14 -3
  23. package/dist/core/agents/agent-patch-queue-store.js +95 -2
  24. package/dist/core/agents/agent-patch-queue.js +6 -3
  25. package/dist/core/agents/agent-patch-schema.js +13 -0
  26. package/dist/core/agents/agent-proof-evidence.js +6 -1
  27. package/dist/core/agents/agent-runner-fake.js +40 -0
  28. package/dist/core/agents/agent-worker-pipeline.js +36 -2
  29. package/dist/core/agents/codex-exec-worker-adapter.js +2 -0
  30. package/dist/core/agents/native-cli-worker.js +0 -1
  31. package/dist/core/agents/native-worker-backend-router.js +58 -1
  32. package/dist/core/codex-app/sks-menubar.js +477 -132
  33. package/dist/core/codex-app.js +1 -0
  34. package/dist/core/codex-control/codex-sdk-config-policy.js +2 -1
  35. package/dist/core/codex-control/codex-sdk-env-policy.js +1 -1
  36. package/dist/core/codex-control/schemas/agent-worker-result.schema.js +12 -3
  37. package/dist/core/codex-hooks/codex-hook-managed-install.js +5 -0
  38. package/dist/core/codex-hooks/codex-hook-state-writer.js +70 -11
  39. package/dist/core/codex-hooks/codex-hook-trust-doctor.js +2 -2
  40. package/dist/core/codex-lb/codex-lb-env.js +20 -1
  41. package/dist/core/commands/basic-cli.js +18 -1
  42. package/dist/core/commands/check-command.js +37 -5
  43. package/dist/core/commands/computer-use-command.js +62 -7
  44. package/dist/core/commands/db-command.js +31 -5
  45. package/dist/core/commands/fast-mode-command.js +3 -0
  46. package/dist/core/commands/gate-result-contract.js +43 -0
  47. package/dist/core/commands/gates-command.js +6 -1
  48. package/dist/core/commands/gc-command.js +29 -2
  49. package/dist/core/commands/goal-command.js +9 -2
  50. package/dist/core/commands/gx-command.js +79 -7
  51. package/dist/core/commands/image-ux-review-command.js +188 -13
  52. package/dist/core/commands/mad-db-command.js +85 -176
  53. package/dist/core/commands/mad-sks-command.js +230 -29
  54. package/dist/core/commands/menubar-command.js +24 -2
  55. package/dist/core/commands/naruto-command.js +64 -10
  56. package/dist/core/commands/plan-command.js +76 -0
  57. package/dist/core/commands/ppt-command.js +159 -24
  58. package/dist/core/commands/qa-loop-command.js +6 -2
  59. package/dist/core/commands/release-command.js +55 -2
  60. package/dist/core/commands/research-command.js +1 -1
  61. package/dist/core/commands/review-command.js +217 -0
  62. package/dist/core/commands/route-success-helpers.js +59 -0
  63. package/dist/core/commands/run-command.js +19 -7
  64. package/dist/core/commands/seo-command.js +49 -1
  65. package/dist/core/commands/ui-command.js +161 -0
  66. package/dist/core/commands/uninstall-command.js +15 -1
  67. package/dist/core/db-safety.js +2 -2
  68. package/dist/core/doctor/doctor-dirty-planner.js +51 -7
  69. package/dist/core/doctor/doctor-transaction.js +8 -0
  70. package/dist/core/doctor/doctor-zellij-repair.js +1 -0
  71. package/dist/core/doctor/imagegen-repair.js +161 -0
  72. package/dist/core/feature-fixture-runner.js +2 -2
  73. package/dist/core/feature-fixtures.js +58 -37
  74. package/dist/core/feature-registry.js +102 -2
  75. package/dist/core/fsx.js +39 -6
  76. package/dist/core/image-ux-review/imagegen-adapter.js +48 -11
  77. package/dist/core/image-ux-review.js +16 -0
  78. package/dist/core/imagegen/imagegen-capability.js +11 -5
  79. package/dist/core/imagegen/require-imagegen.js +57 -0
  80. package/dist/core/init/skills.js +10 -5
  81. package/dist/core/mad-db/mad-db-coordinator.js +94 -18
  82. package/dist/core/mad-db/mad-db-policy.js +12 -10
  83. package/dist/core/mad-sks/executors/executor-base.js +8 -2
  84. package/dist/core/mad-sks/executors/index.js +4 -0
  85. package/dist/core/mad-sks/executors/sql-plane-executor.js +194 -0
  86. package/dist/core/naruto/naruto-active-pool.js +15 -4
  87. package/dist/core/naruto/naruto-real-worker-child.js +11 -1
  88. package/dist/core/naruto/naruto-role-policy.js +3 -0
  89. package/dist/core/naruto/naruto-task-hints.js +10 -0
  90. package/dist/core/naruto/naruto-work-graph.js +8 -2
  91. package/dist/core/naruto/naruto-work-item.js +6 -0
  92. package/dist/core/naruto/solution-tournament.js +101 -0
  93. package/dist/core/permission-gates.js +30 -0
  94. package/dist/core/pipeline-internals/runtime-core.js +11 -3
  95. package/dist/core/pipeline-internals/runtime-gates.js +27 -0
  96. package/dist/core/ppt-review/index.js +6 -1
  97. package/dist/core/ppt-review/slide-imagegen-review.js +75 -10
  98. package/dist/core/ppt.js +70 -2
  99. package/dist/core/proof/auto-finalize.js +70 -11
  100. package/dist/core/proof/proof-schema.js +2 -0
  101. package/dist/core/proof/route-adapter.js +5 -1
  102. package/dist/core/proof/route-finalizer.js +8 -5
  103. package/dist/core/proof/selftest-proof-fixtures.js +18 -5
  104. package/dist/core/proof/validation.js +2 -0
  105. package/dist/core/provider/model-router.js +53 -0
  106. package/dist/core/release/release-gate-node.js +3 -0
  107. package/dist/core/release/release-gate-resource-governor.js +1 -0
  108. package/dist/core/routes/constants.js +1 -1
  109. package/dist/core/routes.js +65 -18
  110. package/dist/core/safety/mutation-guard.js +5 -1
  111. package/dist/core/stop-gate/gate-evaluator.js +102 -0
  112. package/dist/core/stop-gate/stop-gate-check.js +16 -1
  113. package/dist/core/stop-gate/stop-gate-writer.js +4 -0
  114. package/dist/core/triwiki/agents-md-projector.js +184 -0
  115. package/dist/core/triwiki-wrongness/wrongness-schema.js +3 -1
  116. package/dist/core/trust-kernel/trust-kernel-schema.js +1 -0
  117. package/dist/core/trust-kernel/trust-report.js +1 -1
  118. package/dist/core/trust-kernel/trust-status.js +2 -0
  119. package/dist/core/ui/dashboard-html.js +111 -0
  120. package/dist/core/update/update-migration-state.js +386 -15
  121. package/dist/core/update-check.js +178 -81
  122. package/dist/core/verification/diff-quality.js +100 -0
  123. package/dist/core/verification/impact-scan.js +164 -0
  124. package/dist/core/verification/machine-feedback.js +146 -0
  125. package/dist/core/verification/mistake-rule-compiler.js +195 -0
  126. package/dist/core/version.js +1 -1
  127. package/dist/core/zellij/zellij-self-heal.js +9 -6
  128. package/dist/core/zellij/zellij-slot-telemetry.js +1 -1
  129. package/dist/scripts/check-feature-quality.js +2 -2
  130. package/dist/scripts/cli-output-consistency-check.js +57 -0
  131. package/dist/scripts/coding-bench-check.js +136 -0
  132. package/dist/scripts/doctor-imagegen-repair-check.js +62 -0
  133. package/dist/scripts/ensure-bin-executable.js +11 -3
  134. package/dist/scripts/harness-benchmark-check.js +104 -0
  135. package/dist/scripts/legacy-update-e2e-check.js +228 -0
  136. package/dist/scripts/mad-db-command-check.js +11 -8
  137. package/dist/scripts/mad-db-route-identity-check.js +8 -6
  138. package/dist/scripts/mad-db-safety-conflict-matrix-check.js +31 -8
  139. package/dist/scripts/mad-db-skill-policy-snapshot-check.js +4 -5
  140. package/dist/scripts/naruto-real-active-pool-runtime-check.js +26 -1
  141. package/dist/scripts/ppt-real-imagegen-wiring-check.js +5 -1
  142. package/dist/scripts/proof-root-cause-policy-check.js +2 -0
  143. package/dist/scripts/release-dag-full-coverage-check.js +13 -4
  144. package/dist/scripts/release-gate-dag-runner.js +14 -0
  145. package/dist/scripts/release-gate-existence-audit.js +2 -1
  146. package/dist/scripts/release-gate-planner.js +2 -1
  147. package/dist/scripts/release-gate-script-parity-check.js +45 -17
  148. package/dist/scripts/release-metadata-1-19-check.js +2 -1
  149. package/dist/scripts/seo-geo-feature-fixture-quality-check.js +1 -1
  150. package/dist/scripts/sks-1-11-gate-lib.js +8 -6
  151. package/dist/scripts/sks-menubar-install-check.js +57 -20
  152. package/dist/scripts/ux-review-real-loop-fixture-check.js +9 -4
  153. package/dist/scripts/ux-review-run-wires-imagegen-check.js +4 -0
  154. package/dist/scripts/zellij-slot-telemetry-runtime-check.js +1 -0
  155. package/docs/assets/sneakoscope-architecture-pipeline.jpg +0 -0
  156. package/docs/demo.tape +28 -0
  157. package/package.json +31 -24
  158. package/schemas/codex/completion-proof.schema.json +3 -2
  159. package/schemas/release/release-gate-node.schema.json +2 -1
  160. package/dist/scripts/agent-backfill-route-blackbox.js +0 -5
  161. package/dist/scripts/agent-dynamic-pool-route-blackbox.js +0 -5
  162. package/dist/scripts/agent-parallel-write-blackbox.js +0 -56
  163. package/dist/scripts/agent-patch-swarm-route-blackbox.js +0 -10
  164. package/dist/scripts/agent-route-blackbox-lib.js +0 -132
  165. package/dist/scripts/blackbox-command-import-smoke.js +0 -143
  166. package/dist/scripts/blackbox-global-shim.js +0 -77
  167. package/dist/scripts/blackbox-matrix.js +0 -70
  168. package/dist/scripts/blackbox-npx-one-shot.js +0 -69
  169. package/dist/scripts/blackbox-pack-install.js +0 -174
  170. package/dist/scripts/brand-neutrality-zero-leakage-blackbox.js +0 -4
  171. package/dist/scripts/build-once-runner-blackbox.js +0 -34
  172. package/dist/scripts/codex-0140-integration-blackbox.js +0 -13
  173. package/dist/scripts/codex-agent-type-blackbox.js +0 -4
  174. package/dist/scripts/codex-app-harness-blackbox.js +0 -4
  175. package/dist/scripts/codex-app-skill-agent-blackbox.js +0 -4
  176. package/dist/scripts/codex-hook-approval-blackbox.js +0 -4
  177. package/dist/scripts/codex-init-deep-directory-local-blackbox.js +0 -4
  178. package/dist/scripts/codex-native-feature-broker-blackbox.js +0 -4
  179. package/dist/scripts/codex-native-pattern-analysis-blackbox.js +0 -4
  180. package/dist/scripts/codex-native-read-repair-split-blackbox.js +0 -55
  181. package/dist/scripts/codex-native-reference-cache-blackbox.js +0 -41
  182. package/dist/scripts/core-skill-integrity-blackbox.js +0 -32
  183. package/dist/scripts/dfix-fast-blackbox-check.js +0 -37
  184. package/dist/scripts/dfix-parallel-write-blackbox.js +0 -48
  185. package/dist/scripts/dfix-patch-swarm-route-blackbox.js +0 -10
  186. package/dist/scripts/doctor-dirty-repair-blackbox.js +0 -22
  187. package/dist/scripts/doctor-dirty-semantic-blackbox.js +0 -8
  188. package/dist/scripts/doctor-native-capability-repair-blackbox.js +0 -39
  189. package/dist/scripts/doctor-transaction-engine-blackbox.js +0 -28
  190. package/dist/scripts/doctor-zellij-fix-blackbox.js +0 -4
  191. package/dist/scripts/doctor-zellij-no-homebrew-blackbox.js +0 -4
  192. package/dist/scripts/doctor-zellij-upgrade-blackbox.js +0 -4
  193. package/dist/scripts/gate-pack-runner-blackbox.js +0 -27
  194. package/dist/scripts/gate-pack-v2-blackbox.js +0 -18
  195. package/dist/scripts/geo-cli-blackbox-check.js +0 -18
  196. package/dist/scripts/loop-collision-blackbox.js +0 -3
  197. package/dist/scripts/loop-concurrency-oversubscription-blackbox.js +0 -3
  198. package/dist/scripts/loop-fixture-production-misuse-blackbox.js +0 -3
  199. package/dist/scripts/loop-kill-interrupt-real-blackbox.js +0 -3
  200. package/dist/scripts/loop-merge-strategy-blackbox.js +0 -3
  201. package/dist/scripts/loop-mesh-production-e2e-blackbox.js +0 -3
  202. package/dist/scripts/loop-side-effect-blackbox.js +0 -3
  203. package/dist/scripts/mad-db-operation-lifecycle-blackbox.js +0 -29
  204. package/dist/scripts/mad-sks-actual-executor-blackbox.js +0 -5
  205. package/dist/scripts/mad-zellij-headless-fallback-blackbox.js +0 -4
  206. package/dist/scripts/mad-zellij-self-heal-blackbox.js +0 -4
  207. package/dist/scripts/naruto-loop-mesh-blackbox.js +0 -3
  208. package/dist/scripts/naruto-real-parallelism-blackbox.js +0 -307
  209. package/dist/scripts/naruto-worktree-coding-blackbox.js +0 -29
  210. package/dist/scripts/parallel-runtime-real-blackbox.js +0 -44
  211. package/dist/scripts/pipeline-codex-native-doctor-mad-routing-real-blackbox.js +0 -77
  212. package/dist/scripts/pipeline-codex-native-e2e-blackbox.js +0 -13
  213. package/dist/scripts/pipeline-codex-native-image-routing-real-blackbox.js +0 -50
  214. package/dist/scripts/pipeline-codex-native-loop-routing-real-blackbox.js +0 -74
  215. package/dist/scripts/pipeline-codex-native-qa-routing-real-blackbox.js +0 -51
  216. package/dist/scripts/pipeline-codex-native-research-routing-real-blackbox.js +0 -45
  217. package/dist/scripts/pipeline-execution-profile-routing-blackbox.js +0 -4
  218. package/dist/scripts/postinstall-global-doctor-blackbox.js +0 -12
  219. package/dist/scripts/ppt-full-e2e-blackbox-check.js +0 -109
  220. package/dist/scripts/ppt-imagegen-blackbox-check.js +0 -46
  221. package/dist/scripts/qa-backfill-route-blackbox.js +0 -5
  222. package/dist/scripts/qa-patch-swarm-route-blackbox.js +0 -10
  223. package/dist/scripts/release-full-parallelism-blackbox.js +0 -41
  224. package/dist/scripts/release-triwiki-first-runner-blackbox.js +0 -48
  225. package/dist/scripts/release-wiring-3110-blackbox.js +0 -27
  226. package/dist/scripts/release-wiring-3112-blackbox.js +0 -17
  227. package/dist/scripts/release-wiring-3113-blackbox.js +0 -17
  228. package/dist/scripts/research-backfill-route-blackbox.js +0 -5
  229. package/dist/scripts/research-final-reviewer-blackbox.js +0 -70
  230. package/dist/scripts/research-stage-cycle-runtime-blackbox.js +0 -40
  231. package/dist/scripts/research-synthesis-writer-blackbox.js +0 -24
  232. package/dist/scripts/route-blackbox-realism-check.js +0 -21
  233. package/dist/scripts/scheduler-resource-claim-blackbox.js +0 -24
  234. package/dist/scripts/seo-cli-blackbox-check.js +0 -18
  235. package/dist/scripts/sks-3110-all-feature-regression-blackbox.js +0 -116
  236. package/dist/scripts/sks-3112-all-feature-regression-blackbox.js +0 -29
  237. package/dist/scripts/sks-3113-all-feature-regression-blackbox.js +0 -17
  238. package/dist/scripts/sks-400-all-feature-regression-blackbox.js +0 -21
  239. package/dist/scripts/sks-400-extreme-parallel-blackbox.js +0 -8
  240. package/dist/scripts/sks-400-five-minute-blackbox.js +0 -9
  241. package/dist/scripts/sks-400-legacy-purge-blackbox.js +0 -8
  242. package/dist/scripts/sks-401-all-feature-regression-blackbox.js +0 -46
  243. package/dist/scripts/sks-401-five-minute-actual-blackbox.js +0 -23
  244. package/dist/scripts/sks-402-all-feature-regression-blackbox.js +0 -9
  245. package/dist/scripts/sks-402-five-minute-real-blackbox.js +0 -22
  246. package/dist/scripts/sksd-warm-cache-blackbox.js +0 -12
  247. package/dist/scripts/team-backfill-route-blackbox.js +0 -5
  248. package/dist/scripts/team-parallel-write-blackbox.js +0 -55
  249. package/dist/scripts/team-patch-swarm-route-blackbox.js +0 -10
  250. package/dist/scripts/triwiki-affected-graph-blackbox.js +0 -28
  251. package/dist/scripts/triwiki-proof-bank-blackbox.js +0 -30
  252. package/dist/scripts/triwiki-proof-bank-lock-blackbox.js +0 -7
  253. package/dist/scripts/ux-patch-swarm-route-blackbox.js +0 -10
  254. package/dist/scripts/ux-review-imagegen-blackbox-check.js +0 -67
  255. package/dist/scripts/zellij-initial-main-only-blackbox.js +0 -28
  256. package/dist/scripts/zellij-pane-lock-concurrency-blackbox.js +0 -80
  257. package/dist/scripts/zellij-pane-lock-open-worker-integration-blackbox.js +0 -137
  258. package/dist/scripts/zellij-self-heal-typed-blackbox.js +0 -4
  259. package/dist/scripts/zellij-slot-telemetry-real-blackbox.js +0 -20
  260. package/dist/scripts/zellij-stacked-fallback-integration-blackbox.js +0 -81
  261. package/dist/scripts/zellij-worker-pane-real-ui-blackbox.js +0 -202
@@ -2,6 +2,7 @@ import fs from 'node:fs/promises';
2
2
  import { existsSync } from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { spawnSync } from 'node:child_process';
5
+ import { COMMANDS } from '../cli/command-registry.js';
5
6
  import { COMMAND_CATALOG, DOLLAR_COMMAND_ALIASES, DOLLAR_COMMANDS, ROUTES } from './routes.js';
6
7
  import { FEATURE_QUALITY_LEVELS, fixtureForFeature, fixtureSummary, validateFeatureFixtures } from './feature-fixtures.js';
7
8
  import { runFeatureFixture, writeFeatureFixtureReports } from './feature-fixture-runner.js';
@@ -51,6 +52,8 @@ export async function buildFeatureRegistry({ root = packageRoot(), generatedAt =
51
52
  }
52
53
  features.push(nativeAgentIntakeFeature());
53
54
  features.push(agentProofEvidenceFeature());
55
+ features.push(doctorImagegenRepairFeature());
56
+ features.push(...imagegenWiringFeatures());
54
57
  for (const skillName of skillNames) {
55
58
  if (!skillCoveredByRoute(skillName))
56
59
  features.push(skillFeature(skillName));
@@ -102,7 +105,8 @@ export function validateFeatureRegistry(registry = {}) {
102
105
  const blockers = [
103
106
  ...Object.entries(unmapped).flatMap(([kind, values]) => values.map((value) => `${kind}:${value}`)),
104
107
  ...duplicateFeatureIds.map((id) => `duplicate_feature_id:${id}`),
105
- ...routeMentionsWithoutRoute.map((mention) => `doc_route_mention_without_route:${mention}`)
108
+ ...routeMentionsWithoutRoute.map((mention) => `doc_route_mention_without_route:${mention}`),
109
+ ...routeGateConsistencyBlockers()
106
110
  ];
107
111
  return {
108
112
  ok: blockers.length === 0,
@@ -118,6 +122,7 @@ export function validateFeatureRegistry(registry = {}) {
118
122
  unmapped,
119
123
  duplicate_feature_ids: duplicateFeatureIds,
120
124
  doc_route_mentions_without_route: routeMentionsWithoutRoute,
125
+ route_gate_consistency_blockers: routeGateConsistencyBlockers(),
121
126
  blockers,
122
127
  nonblocking_known_gaps: [
123
128
  'feature fixtures remain progressive',
@@ -127,6 +132,41 @@ export function validateFeatureRegistry(registry = {}) {
127
132
  feature_quality_summary: featureQualitySummary(features)
128
133
  };
129
134
  }
135
+ export function routeGateConsistencyBlockers() {
136
+ const blockers = [];
137
+ for (const route of ROUTES) {
138
+ const cliName = routeCliCommandName(route);
139
+ if (!cliName)
140
+ continue;
141
+ const entry = COMMANDS[cliName];
142
+ if (!entry?.ownsGates)
143
+ continue;
144
+ const routeGates = stopGateFiles(route.stopGate);
145
+ const owned = new Set((entry.ownedGateFiles || []).map((file) => String(file)));
146
+ if (!routeGates.length && owned.size > 0)
147
+ blockers.push(`route_gate_mismatch:${route.id}:routes_none_registry_${[...owned].join('|')}`);
148
+ for (const gate of routeGates) {
149
+ if (!owned.has(gate))
150
+ blockers.push(`route_gate_mismatch:${route.id}:${gate}_not_owned_by_${cliName}`);
151
+ }
152
+ }
153
+ return blockers;
154
+ }
155
+ function routeCliCommandName(route) {
156
+ const command = String(route.command || '').replace(/^\$/, '').toLowerCase();
157
+ if (Object.hasOwn(COMMANDS, command))
158
+ return command;
159
+ const alias = (route.dollarAliases || [])
160
+ .map((value) => String(value || '').replace(/^\$/, '').toLowerCase())
161
+ .find((value) => Object.hasOwn(COMMANDS, value));
162
+ return alias || null;
163
+ }
164
+ function stopGateFiles(stopGate) {
165
+ const text = String(stopGate || '');
166
+ if (!text || text === 'none' || text === 'honest_mode' || text === 'plan-only')
167
+ return [];
168
+ return text.split('|').map((part) => part.trim()).filter((part) => part.endsWith('.json'));
169
+ }
130
170
  export async function writeFeatureInventoryDocs({ root = packageRoot(), outFile = path.join(root, 'docs', 'feature-inventory.md') } = {}) {
131
171
  const registry = await buildFeatureRegistry({ root });
132
172
  const markdown = renderFeatureInventoryMarkdown(registry);
@@ -674,6 +714,66 @@ function agentProofEvidenceFeature() {
674
714
  }
675
715
  });
676
716
  }
717
+ function doctorImagegenRepairFeature() {
718
+ return baseFeature({
719
+ id: 'doctor:imagegen-repair',
720
+ commands: ['sks doctor --json', 'sks doctor --fix --json'],
721
+ aliases: ['repair.imagegen', 'imagegen_repair'],
722
+ category: 'safety',
723
+ maturity: 'beta',
724
+ intent: 'Doctor repair path that detects Codex App imagegen, attempts repair when requested, and reports imagegen_repair evidence without claiming success unless re-detection passes.',
725
+ voxel_triwiki_integration: 'policy voxel required for visual/image routes that depend on Codex App imagegen',
726
+ completion_proof_integration: 'doctor report imagegen_repair is consumed by imagegen route blockers before visual route proof',
727
+ known_gaps: ['live Codex App feature enablement remains environment-dependent and reports manual actions when unavailable'],
728
+ source_refs: {
729
+ cli_command_names: ['doctor'],
730
+ handler_keys: ['doctor'],
731
+ dollar_commands: [],
732
+ app_skill_aliases: [],
733
+ skills: ['imagegen']
734
+ }
735
+ });
736
+ }
737
+ function imagegenWiringFeatures() {
738
+ return [
739
+ baseFeature({
740
+ id: 'ux-review:run-wires-imagegen',
741
+ commands: ['npm run ux-review:run-wires-imagegen', 'sks ux-review run --image <screenshot> --generate-callouts --json'],
742
+ aliases: ['$Image-UX-Review', '$UX-Review'],
743
+ category: 'visual-memory',
744
+ maturity: 'beta',
745
+ intent: 'Image UX route start gate, shared gpt-image-2 adapter, callout extraction, and Codex App evidence validation wiring.',
746
+ voxel_triwiki_integration: 'image/source/bbox voxel required',
747
+ completion_proof_integration: 'image-ux-review-gate and Completion Proof must separate real Codex image evidence from mock/API fallback evidence',
748
+ known_gaps: ['live Codex App image generation remains environment-dependent'],
749
+ source_refs: {
750
+ cli_command_names: ['image-ux-review'],
751
+ handler_keys: ['image-ux-review', 'ux-review'],
752
+ dollar_commands: ['$Image-UX-Review', '$UX-Review'],
753
+ app_skill_aliases: ['$image-ux-review', '$ux-review'],
754
+ skills: ['image-ux-review', 'ux-review', 'imagegen']
755
+ }
756
+ }),
757
+ baseFeature({
758
+ id: 'ppt:real-imagegen-wiring',
759
+ commands: ['npm run ppt:real-imagegen-wiring', 'sks ppt review --deck <pptx> --json'],
760
+ aliases: ['$PPT'],
761
+ category: 'visual-memory',
762
+ maturity: 'beta',
763
+ intent: 'PPT slide callout review path reuses the shared gpt-image-2 adapter and records imagegen_evidence in PPT gates.',
764
+ voxel_triwiki_integration: 'image/source/bbox voxel required',
765
+ completion_proof_integration: 'ppt-imagegen-review-gate and ppt-gate must include Codex App imagegen evidence classes and hashes',
766
+ known_gaps: ['live deck export and live Codex App image generation remain environment-dependent'],
767
+ source_refs: {
768
+ cli_command_names: ['ppt'],
769
+ handler_keys: ['ppt'],
770
+ dollar_commands: ['$PPT'],
771
+ app_skill_aliases: ['$ppt'],
772
+ skills: ['ppt', 'imagegen']
773
+ }
774
+ })
775
+ ];
776
+ }
677
777
  function skillFeature(skillName) {
678
778
  return baseFeature({
679
779
  id: `skill-${slug(skillName)}`,
@@ -876,7 +976,7 @@ function routeKnownGaps(command) {
876
976
  if (command === '$MAD-SKS')
877
977
  return ['permission closed by owning gate'];
878
978
  if (command === '$MAD-DB')
879
- return ['active cycle must execute requested SQL-plane work, read back postconditions, and close the mission-local write profile'];
979
+ return ['deprecated alias; SQL-plane execution is merged into $MAD-SKS and must still read back postconditions and close the mission-local write profile'];
880
980
  return [];
881
981
  }
882
982
  function checkRow(id, ok, blockers = []) {
package/dist/core/fsx.js CHANGED
@@ -5,12 +5,42 @@ import os from 'node:os';
5
5
  import crypto from 'node:crypto';
6
6
  import { spawn } from 'node:child_process';
7
7
  import { fileURLToPath } from 'node:url';
8
- export const PACKAGE_VERSION = '4.8.7';
8
+ export const PACKAGE_VERSION = '5.3.0';
9
9
  export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
10
10
  export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
11
11
  export function nowIso() {
12
12
  return new Date().toISOString();
13
13
  }
14
+ export function lastLine(chunk) {
15
+ const lines = String(chunk || '').split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
16
+ return lines[lines.length - 1] || '';
17
+ }
18
+ export function throttleLines(fn, ms = 500) {
19
+ let lastAt = 0;
20
+ let pending = null;
21
+ let timer = null;
22
+ const flush = () => {
23
+ timer = null;
24
+ if (!pending)
25
+ return;
26
+ lastAt = Date.now();
27
+ const line = pending;
28
+ pending = null;
29
+ fn(line);
30
+ };
31
+ return (chunk) => {
32
+ const line = lastLine(chunk);
33
+ if (!line)
34
+ return;
35
+ pending = line;
36
+ const now = Date.now();
37
+ const wait = Math.max(0, ms - (now - lastAt));
38
+ if (wait === 0)
39
+ flush();
40
+ else if (!timer)
41
+ timer = setTimeout(flush, wait);
42
+ };
43
+ }
14
44
  export function sha256(input) {
15
45
  return crypto.createHash('sha256').update(input).digest('hex');
16
46
  }
@@ -109,18 +139,21 @@ export async function writeReceiptRotated(p, data, opts = {}) {
109
139
  const rotated = prior
110
140
  .filter((entry) => entry.isFile() && entry.name.startsWith(`${base}.`) && entry.name.endsWith('.json'))
111
141
  .map((entry) => path.join(path.dirname(p), entry.name));
142
+ const rotatedFiles = new Set(rotated);
112
143
  const existing = await fsp.stat(p).catch(() => null);
113
144
  if (existing) {
114
145
  const stamp = new Date(existing.mtimeMs).toISOString().replace(/[:.]/g, '-');
115
- await fsp.rename(p, path.join(path.dirname(p), `${base}.${stamp}.json`)).catch(() => undefined);
146
+ const rotatedPath = path.join(path.dirname(p), `${base}.${stamp}.json`);
147
+ await fsp.rename(p, rotatedPath).catch(() => undefined);
148
+ rotatedFiles.add(rotatedPath);
116
149
  }
117
150
  await writeJsonAtomic(p, data);
118
- const rows = await Promise.all(rotated.map(async (file) => ({ file, stat: await fsp.stat(file).catch(() => null) })));
119
- rows
151
+ const rows = await Promise.all([...rotatedFiles].map(async (file) => ({ file, stat: await fsp.stat(file).catch(() => null) })));
152
+ const removable = rows
120
153
  .filter((row) => Boolean(row.stat))
121
154
  .sort((a, b) => b.stat.mtimeMs - a.stat.mtimeMs)
122
- .slice(keep)
123
- .forEach((row) => void fsp.rm(row.file, { force: true }).catch(() => undefined));
155
+ .slice(keep);
156
+ await Promise.all(removable.map((row) => fsp.rm(row.file, { force: true }).catch(() => undefined)));
124
157
  }
125
158
  export async function writeBinaryAtomic(p, data) {
126
159
  await ensureDir(path.dirname(p));
@@ -107,6 +107,7 @@ export function createCodexAppImagegenAdapter(opts = {}) {
107
107
  schema: 'sks.image-ux-gpt-image-2-response.v1',
108
108
  created_at: nowIso(),
109
109
  provider: 'codex_app_imagegen',
110
+ evidence_class: 'codex_app_imagegen',
110
111
  model: 'gpt-image-2',
111
112
  ok: false,
112
113
  status: 'blocked',
@@ -131,11 +132,13 @@ export function createCodexAppImagegenAdapter(opts = {}) {
131
132
  schema: 'sks.image-ux-gpt-image-2-response.v1',
132
133
  created_at: nowIso(),
133
134
  provider: 'codex_app_imagegen',
135
+ evidence_class: 'codex_app_imagegen',
134
136
  model: 'gpt-image-2',
135
137
  ok: true,
136
138
  status: 'generated',
137
139
  output_image_path: dest,
138
140
  output_image_sha256: meta.sha256,
141
+ output_sha256: meta.sha256,
139
142
  output_id: meta.output_id,
140
143
  output_source: outputSource,
141
144
  image_artifact_path_contract: imageContract?.artifact_path || null,
@@ -161,6 +164,7 @@ export function createCodexAppImagegenAdapter(opts = {}) {
161
164
  schema: 'sks.image-ux-gpt-image-2-response.v1',
162
165
  created_at: nowIso(),
163
166
  provider: 'codex_app_imagegen',
167
+ evidence_class: 'codex_app_imagegen',
164
168
  model: 'gpt-image-2',
165
169
  ok: false,
166
170
  status: 'blocked',
@@ -196,6 +200,22 @@ export function createFakeImagegenAdapter(opts = {}) {
196
200
  await ensureDir(input.output_dir);
197
201
  const requestArtifact = path.join(input.output_dir, 'image-ux-gpt-image-2-request.json');
198
202
  const responseArtifact = path.join(input.output_dir, 'image-ux-gpt-image-2-response.json');
203
+ if (!imagegenMockContext(opts)) {
204
+ await writeJsonAtomic(responseArtifact, {
205
+ schema: 'sks.image-ux-gpt-image-2-response.v1',
206
+ created_at: nowIso(),
207
+ provider: 'fake_imagegen_adapter',
208
+ fake_adapter: true,
209
+ execution_class: 'mock_fixture',
210
+ evidence_class: 'mock_fixture',
211
+ model: 'gpt-image-2',
212
+ ok: false,
213
+ status: 'blocked',
214
+ blocker: 'fake_imagegen_requires_test_or_mock_context',
215
+ local_only: true
216
+ });
217
+ return { ok: false, status: 'blocked', generated_image_path: null, output_id: null, blocker: 'fake_imagegen_requires_test_or_mock_context', provider: 'fake_imagegen_adapter', request_artifact: null, response_artifact: responseArtifact, latency_ms: Date.now() - started };
218
+ }
199
219
  const validation = await validateGptImage2Request({
200
220
  provider: 'fake_imagegen_adapter',
201
221
  endpoint: 'local hermetic fixture',
@@ -227,6 +247,9 @@ export function createFakeImagegenAdapter(opts = {}) {
227
247
  schema: 'sks.image-ux-gpt-image-2-response.v1',
228
248
  created_at: nowIso(),
229
249
  provider: 'fake_imagegen_adapter',
250
+ fake_adapter: true,
251
+ execution_class: 'mock_fixture',
252
+ evidence_class: 'mock_fixture',
230
253
  model: 'gpt-image-2',
231
254
  ok: false,
232
255
  status: 'blocked',
@@ -251,16 +274,20 @@ export function createFakeImagegenAdapter(opts = {}) {
251
274
  schema: 'sks.image-ux-gpt-image-2-response.v1',
252
275
  created_at: nowIso(),
253
276
  provider: 'fake_imagegen_adapter',
277
+ fake_adapter: true,
278
+ execution_class: 'mock_fixture',
279
+ evidence_class: 'mock_fixture',
254
280
  model: 'gpt-image-2',
255
281
  ok: true,
256
282
  status: 'generated',
257
283
  output_image_path: out,
258
284
  output_image_sha256: meta.sha256,
285
+ output_sha256: meta.sha256,
259
286
  output_id: meta.output_id,
287
+ output_source: 'mock_fixture',
260
288
  image_artifact_path_contract: imageContract?.artifact_path || null,
261
289
  dimensions: { width: meta.width, height: meta.height, format: meta.format },
262
290
  latency_ms: Date.now() - started,
263
- fake_adapter: true,
264
291
  source: 'mock_like_fixture',
265
292
  real_generated: false,
266
293
  mock: true,
@@ -320,6 +347,7 @@ export function createOpenAIImagesApiAdapter(opts = {}) {
320
347
  schema: 'sks.image-ux-gpt-image-2-response.v1',
321
348
  created_at: nowIso(),
322
349
  provider: 'openai_images_api',
350
+ evidence_class: 'non_codex_api_fallback',
323
351
  model: 'gpt-image-2',
324
352
  ok: false,
325
353
  status: 'blocked',
@@ -334,6 +362,7 @@ export function createOpenAIImagesApiAdapter(opts = {}) {
334
362
  schema: 'sks.image-ux-gpt-image-2-response.v1',
335
363
  created_at: nowIso(),
336
364
  provider: 'openai_images_api',
365
+ evidence_class: 'non_codex_api_fallback',
337
366
  model: 'gpt-image-2',
338
367
  ok: false,
339
368
  status: 'blocked',
@@ -397,6 +426,7 @@ export function createOpenAIImagesApiAdapter(opts = {}) {
397
426
  schema: 'sks.image-ux-gpt-image-2-response.v1',
398
427
  created_at: nowIso(),
399
428
  provider: 'openai_responses_image_generation',
429
+ evidence_class: 'non_codex_api_fallback',
400
430
  model: 'gpt-image-2',
401
431
  responses_model: responsesImagegenModel(opts),
402
432
  auth_source: auth.auth_source,
@@ -404,6 +434,7 @@ export function createOpenAIImagesApiAdapter(opts = {}) {
404
434
  status: 'generated',
405
435
  output_image_path: out,
406
436
  output_image_sha256: meta.sha256,
437
+ output_sha256: meta.sha256,
407
438
  output_id: meta.output_id,
408
439
  image_artifact_path_contract: imageContract?.artifact_path || null,
409
440
  dimensions: { width: meta.width, height: meta.height, format: meta.format },
@@ -454,12 +485,14 @@ export function createOpenAIImagesApiAdapter(opts = {}) {
454
485
  schema: 'sks.image-ux-gpt-image-2-response.v1',
455
486
  created_at: nowIso(),
456
487
  provider: 'openai_images_api',
488
+ evidence_class: 'non_codex_api_fallback',
457
489
  model: 'gpt-image-2',
458
490
  auth_source: auth.auth_source,
459
491
  ok: true,
460
492
  status: 'generated',
461
493
  output_image_path: out,
462
494
  output_image_sha256: meta.sha256,
495
+ output_sha256: meta.sha256,
463
496
  output_id: meta.output_id,
464
497
  image_artifact_path_contract: imageContract?.artifact_path || null,
465
498
  dimensions: { width: meta.width, height: meta.height, format: meta.format },
@@ -511,24 +544,17 @@ async function resolveImageArtifactRoot(input) {
511
544
  return projectRoot(input.output_dir || process.cwd()).catch(() => cwdRoot);
512
545
  }
513
546
  export async function generateGptImage2CalloutReview(input, opts = {}) {
514
- if (opts.fake === true || process.env.SKS_TEST_FAKE_IMAGEGEN === '1') {
515
- return createFakeImagegenAdapter(opts.fakeAdapter || {}).generateCalloutReview(input);
547
+ if ((opts.fake === true || process.env.SKS_TEST_FAKE_IMAGEGEN === '1') && imagegenMockContext(opts)) {
548
+ return createFakeImagegenAdapter({ ...(opts.fakeAdapter || {}), mockContext: true }).generateCalloutReview(input);
516
549
  }
517
550
  const capability = await detectImagegenCapability(opts.capability || {}).catch(() => null);
518
- // Auto-enable the OpenAI direct-key API fallback whenever an OPENAI_API_KEY is
519
- // available, so a missing/failed Codex App $imagegen surface still produces a
520
- // real image instead of a hard block. Explicit opt-out wins: pass
521
- // allowApiFallback:false or SKS_IMAGEGEN_ALLOW_API_FALLBACK=0.
522
- const openAiKeyPresent = Boolean(opts.openai?.apiKey || process.env.OPENAI_API_KEY);
523
- const explicitDisableApiFallback = opts.allowApiFallback === false || process.env.SKS_IMAGEGEN_ALLOW_API_FALLBACK === '0';
524
551
  // codex-lb imagegen is a direct API fallback, not Codex App imagegen evidence.
525
552
  // It must be explicitly enabled by the caller or environment.
526
553
  const explicitDisableCodexLbFallback = opts.allowCodexLbApiFallback === false || process.env.SKS_IMAGEGEN_ALLOW_CODEX_LB_API_FALLBACK === '0';
527
554
  const allowCodexLbApiFallback = !explicitDisableCodexLbFallback && (opts.allowCodexLbApiFallback === true
528
555
  || process.env.SKS_IMAGEGEN_ALLOW_CODEX_LB_API_FALLBACK === '1');
529
- const allowApiFallback = !explicitDisableApiFallback && (opts.allowApiFallback === true
556
+ const allowApiFallback = (opts.allowApiFallback === true
530
557
  || process.env.SKS_IMAGEGEN_ALLOW_API_FALLBACK === '1'
531
- || openAiKeyPresent
532
558
  || allowCodexLbApiFallback);
533
559
  const openaiOptions = {
534
560
  ...(opts.openai || {}),
@@ -544,6 +570,13 @@ export async function generateGptImage2CalloutReview(input, opts = {}) {
544
570
  return codexResult;
545
571
  return createOpenAIImagesApiAdapter(openaiOptions).generateCalloutReview(input);
546
572
  }
573
+ function imagegenMockContext(opts = {}) {
574
+ return opts.mockContext === true
575
+ || opts.testContext === true
576
+ || process.env.NODE_ENV === 'test'
577
+ || process.env.SKS_SELFTEST_MOCK === '1'
578
+ || process.env.SKS_MOCK === '1';
579
+ }
547
580
  export function imagegenCapabilityBlocker(surface = 'Codex App $imagegen') {
548
581
  return {
549
582
  schema: 'sks.image-ux-imagegen-blocker.v1',
@@ -705,6 +738,9 @@ export async function generatedImageMetadata(root, imagePath, opts = {}) {
705
738
  source_screen_id: opts.source_screen_id || null,
706
739
  provider_model: 'gpt-image-2',
707
740
  provider_surface: opts.provider_surface || 'codex_app_imagegen',
741
+ evidence_class: opts.evidence_class || (opts.mock ? 'mock_fixture' : 'codex_app_imagegen'),
742
+ output_source: opts.output_source || (opts.mock ? 'mock_fixture' : 'manual_attach'),
743
+ output_sha256: opts.output_sha256 || await sha256File(absolute),
708
744
  requested_fidelity: 'high_fidelity_automatic',
709
745
  image_input_fidelity_note: 'high_fidelity_automatic',
710
746
  privacy: 'local-only',
@@ -729,6 +765,7 @@ function redactedImagegenResponse(payload, ok, latencyMs, provider = 'openai_ima
729
765
  schema: 'sks.image-ux-gpt-image-2-response.v1',
730
766
  created_at: nowIso(),
731
767
  provider,
768
+ evidence_class: provider === 'codex_app_imagegen' ? 'codex_app_imagegen' : 'non_codex_api_fallback',
732
769
  model: 'gpt-image-2',
733
770
  ok,
734
771
  status: ok ? 'generated' : 'blocked',
@@ -790,11 +790,14 @@ function buildImagegenResponseArtifact(generatedReviewLedger = {}) {
790
790
  schema: 'sks.image-ux-gpt-image-2-response.v1',
791
791
  created_at: nowIso(),
792
792
  provider: image?.provider_surface || generatedReviewLedger.provider?.preferred_surface || 'none',
793
+ evidence_class: image?.evidence_class || (image?.mock ? 'mock_fixture' : image?.real_generated ? 'codex_app_imagegen' : null),
793
794
  model: 'gpt-image-2',
794
795
  ok: generatedReviewLedger.passed === true,
795
796
  status: generatedReviewLedger.passed === true ? 'generated' : 'blocked_or_pending',
796
797
  output_image_path: image?.path || null,
797
798
  output_image_sha256: image?.sha256 || null,
799
+ output_sha256: image?.output_sha256 || image?.sha256 || null,
800
+ output_source: image?.output_source || (image?.mock ? 'mock_fixture' : image?.real_generated ? 'manual_attach' : null),
798
801
  output_id: image?.output_id || null,
799
802
  dimensions: image ? { width: image.width || null, height: image.height || null, format: image.format || null } : null,
800
803
  latency_ms: image?.latency_ms || null,
@@ -863,6 +866,19 @@ function generatedImageEvidenceBlockers(image = {}, evidence = {}) {
863
866
  if (image.real_generated !== true || image.mock === true || image.source === 'mock_fixture')
864
867
  return [];
865
868
  const blockers = [];
869
+ const evidenceClass = String(image.evidence_class || '');
870
+ const outputSource = String(image.output_source || '');
871
+ const outputSha = String(image.output_sha256 || image.output_image_sha256 || '');
872
+ if (!evidenceClass)
873
+ blockers.push('generated_review_image_evidence_class_missing');
874
+ else if (evidenceClass !== 'codex_app_imagegen')
875
+ blockers.push(`generated_review_image_evidence_class_not_codex_app:${evidenceClass}`);
876
+ if (outputSource && !['manual_attach', 'auto_discovered_generated_images'].includes(outputSource))
877
+ blockers.push(`generated_review_image_output_source_invalid:${outputSource}`);
878
+ if (!outputSha)
879
+ blockers.push('generated_review_image_output_sha256_missing');
880
+ else if (evidence.sha256 && outputSha !== evidence.sha256)
881
+ blockers.push('generated_review_image_output_sha256_mismatch');
866
882
  if (!image.path)
867
883
  blockers.push('generated_review_image_missing');
868
884
  if (!evidence.sha256)
@@ -18,8 +18,13 @@ export async function detectImagegenCapability(opts = {}) {
18
18
  }).catch(() => null);
19
19
  const apiFallbackAvailable = openaiApiKeyPresent;
20
20
  const fakeAdapterEnabled = opts.fake === true || env.SKS_TEST_FAKE_IMAGEGEN === '1';
21
- const realGenerationAvailable = codexAppBuiltInAvailable || apiFallbackAvailable;
22
- const routeGenerationAvailable = realGenerationAvailable || fakeAdapterEnabled;
21
+ const fakeAdapterAcceptedForRoute = fakeAdapterEnabled && (opts.mockContext === true
22
+ || opts.testContext === true
23
+ || env.NODE_ENV === 'test'
24
+ || env.SKS_SELFTEST_MOCK === '1'
25
+ || env.SKS_MOCK === '1');
26
+ const realGenerationAvailable = codexAppBuiltInAvailable;
27
+ const routeGenerationAvailable = codexAppBuiltInAvailable || fakeAdapterAcceptedForRoute;
23
28
  const coreReady = codexAppBuiltInAvailable;
24
29
  const coreBlockers = coreReady ? [] : ['codex_app_builtin_imagegen_capability_missing'];
25
30
  const routeGenerationBlockers = routeGenerationAvailable ? [] : ['imagegen_capability_missing'];
@@ -68,16 +73,17 @@ export async function detectImagegenCapability(opts = {}) {
68
73
  },
69
74
  fake_adapter: {
70
75
  available: fakeAdapterEnabled,
76
+ accepted_for_route_readiness: fakeAdapterAcceptedForRoute,
71
77
  env: 'SKS_TEST_FAKE_IMAGEGEN=1',
72
78
  source: 'mock_like_fixture',
73
79
  real_generation_claim_allowed: false
74
80
  },
75
- supports_reference_image: codexAppBuiltInAvailable || apiFallbackAvailable || fakeAdapterEnabled,
81
+ supports_reference_image: codexAppBuiltInAvailable || fakeAdapterAcceptedForRoute,
76
82
  gpt_image_2_input_fidelity_automatic: true,
77
83
  input_fidelity_must_be_omitted: true,
78
84
  supported_workflows: {
79
- ux_review_callouts: codexAppBuiltInAvailable || apiFallbackAvailable || fakeAdapterEnabled,
80
- ppt_slide_callouts: codexAppBuiltInAvailable || apiFallbackAvailable || fakeAdapterEnabled,
85
+ ux_review_callouts: codexAppBuiltInAvailable || fakeAdapterAcceptedForRoute,
86
+ ppt_slide_callouts: codexAppBuiltInAvailable || fakeAdapterAcceptedForRoute,
81
87
  structured_extraction_required_after_generation: true,
82
88
  full_verification_requires_codex_app_output: true
83
89
  },
@@ -0,0 +1,57 @@
1
+ import { detectImagegenCapability } from './imagegen-capability.js';
2
+ import { repairCodexImagegen } from '../doctor/imagegen-repair.js';
3
+ export async function requireCodexImagegen(root, opts = {}) {
4
+ const capability = await detectImagegenCapability({
5
+ codexBin: opts.codexBin || undefined,
6
+ timeoutMs: opts.timeoutMs || 5000
7
+ }).catch((err) => ({
8
+ ok: false,
9
+ core_ready: false,
10
+ blockers: [err instanceof Error ? err.message : String(err)]
11
+ }));
12
+ if (capability.core_ready === true) {
13
+ return { ok: true, capability, repair: null, blocker: null, blockers: [] };
14
+ }
15
+ const repair = opts.autoRepair === true
16
+ ? await repairCodexImagegen({
17
+ root,
18
+ apply: opts.applyRepair === true,
19
+ codexBin: opts.codexBin || null,
20
+ timeoutMs: opts.timeoutMs || 5000
21
+ }).catch((err) => ({
22
+ ok: false,
23
+ recovered: false,
24
+ blockers: [err instanceof Error ? err.message : String(err)]
25
+ }))
26
+ : null;
27
+ const finalCapability = repair
28
+ ? repair.after || capability
29
+ : capability;
30
+ const ok = finalCapability.core_ready === true || repair?.recovered === true;
31
+ const blockers = ok ? [] : [
32
+ ...new Set([
33
+ ...((finalCapability?.core_blockers || []).map(String)),
34
+ ...((finalCapability?.blockers || []).map(String)),
35
+ ...((repair?.blockers || []).map(String)),
36
+ 'codex_imagegen_unavailable'
37
+ ])
38
+ ];
39
+ return {
40
+ ok,
41
+ capability: finalCapability,
42
+ repair,
43
+ blocker: ok ? null : {
44
+ schema: 'sks.codex-imagegen-required-blocker.v1',
45
+ blocker: 'codex_imagegen_unavailable',
46
+ status: 'blocked',
47
+ blockers,
48
+ next_actions: repair?.manual_actions || [
49
+ 'Install/update Codex CLI: npm i -g @openai/codex@latest',
50
+ 'Open Codex App settings and enable image_generation / $imagegen.',
51
+ 'Verify with: codex features list --json'
52
+ ]
53
+ },
54
+ blockers
55
+ };
56
+ }
57
+ //# sourceMappingURL=require-imagegen.js.map