sneakoscope 7.3.0 → 7.6.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.
- package/README.md +129 -10
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/cli/codex-app-command.js +1 -1
- package/dist/cli/command-manifest-lite.js +4 -1
- package/dist/cli/command-registry.js +2 -0
- package/dist/cli/install-helpers-codex-lb-chain.js +42 -0
- package/dist/cli/install-helpers-codex-lb-config.js +114 -9
- package/dist/cli/install-helpers-install-support.js +6 -16
- package/dist/cli/install-helpers.js +788 -600
- package/dist/cli/router.js +30 -2
- package/dist/commands/codex-app.js +37 -1
- package/dist/commands/codex-lb.js +491 -300
- package/dist/config/skills-manifest.json +57 -71
- package/dist/core/align/align-route.js +127 -0
- package/dist/core/codex-app/codex-app-fast-ui-repair.js +8 -4
- package/dist/core/codex-app/codex-app-restart.js +83 -0
- package/dist/core/codex-app/codex-app-ui-state-snapshot.js +8 -4
- package/dist/core/codex-app/desktop-routing-snapshot.js +1 -1
- package/dist/core/codex-app/menubar/constants.js +5 -0
- package/dist/core/codex-app/menubar/generation-transaction-journal.js +1 -3
- package/dist/core/codex-app/menubar/installer.js +18 -0
- package/dist/core/codex-app/multi-provider-router.js +22 -10
- package/dist/core/codex-app/openrouter-activate.js +26 -1
- package/dist/core/codex-app.js +118 -26
- package/dist/core/codex-control/codex-app-server-runtime-env.js +86 -0
- package/dist/core/codex-control/codex-app-server-v2-client.js +78 -8
- package/dist/core/codex-control/codex-task-runner.js +2 -2
- package/dist/core/codex-lb/capability-runner.js +204 -0
- package/dist/core/codex-lb/capability-types.js +1 -0
- package/dist/core/codex-lb/codex-lb-env.js +9 -13
- package/dist/core/codex-lb/codex-lb-setup.js +45 -8
- package/dist/core/codex-lb/codex-lb-tool-catalog.js +52 -74
- package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +1 -1
- package/dist/core/codex-lb/desktop-auth-invariant.js +185 -0
- package/dist/core/codex-lb/desktop-bridge/client.js +34 -0
- package/dist/core/codex-lb/desktop-bridge/header-policy.js +106 -0
- package/dist/core/codex-lb/desktop-bridge/http-forward.js +101 -0
- package/dist/core/codex-lb/desktop-bridge/index.js +10 -0
- package/dist/core/codex-lb/desktop-bridge/launchd.js +81 -0
- package/dist/core/codex-lb/desktop-bridge/location-rewrite.js +27 -0
- package/dist/core/codex-lb/desktop-bridge/security.js +274 -0
- package/dist/core/codex-lb/desktop-bridge/server.js +179 -0
- package/dist/core/codex-lb/desktop-bridge/state.js +119 -0
- package/dist/core/codex-lb/desktop-bridge/types.js +16 -0
- package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +151 -0
- package/dist/core/codex-lb/desktop-center-credentials.js +189 -0
- package/dist/core/codex-lb/desktop-controller.js +1410 -0
- package/dist/core/codex-lb/desktop-mode.js +32 -0
- package/dist/core/codex-lb/desktop-real-evidence.js +168 -0
- package/dist/core/codex-lb/desktop-service.js +613 -0
- package/dist/core/codex-lb/legacy-migration.js +575 -0
- package/dist/core/codex-lb/migration-receipt.js +246 -0
- package/dist/core/codex-lb/probes/auxiliary-surfaces-probe.js +60 -0
- package/dist/core/codex-lb/probes/bridge-probe.js +75 -0
- package/dist/core/codex-lb/probes/catalog-probe.js +128 -0
- package/dist/core/codex-lb/probes/computer-use-probe.js +50 -0
- package/dist/core/codex-lb/probes/image-generation-probe.js +47 -0
- package/dist/core/codex-lb/probes/probe-evidence.js +39 -0
- package/dist/core/codex-lb/probes/voice-realtime-probe.js +44 -0
- package/dist/core/codex-lb/trusted-deep-evidence.js +403 -0
- package/dist/core/codex-native/core-skill-manifest.js +22 -6
- package/dist/core/codex-runtime/codex-desktop-config-policy.js +2 -1
- package/dist/core/commands/align-command.js +220 -0
- package/dist/core/commands/image-ux-review-command.js +280 -68
- package/dist/core/commands/naruto-command.js +257 -8
- package/dist/core/commands/ppt-command.js +2 -2
- package/dist/core/commands/telegram-command.js +402 -34
- package/dist/core/commands/triwiki-command.js +11 -1
- package/dist/core/commands/triwiki-graph-command.js +160 -0
- package/dist/core/commands/wiki-command.js +100 -20
- package/dist/core/doctor/skill-legacy-surface.js +1 -3
- package/dist/core/errors/message.js +3 -0
- package/dist/core/evidence/evidence-freshness.js +14 -0
- package/dist/core/feature-fixtures.js +9 -1
- package/dist/core/feature-registry.js +5 -3
- package/dist/core/fsx.js +7 -3
- package/dist/core/hooks-runtime/codex-commit-hooks-selftest.js +82 -0
- package/dist/core/hooks-runtime/context-graph-freshness-preflight.js +113 -0
- package/dist/core/hooks-runtime/hook-context.js +15 -2
- package/dist/core/hooks-runtime/managed-skill-generation-heal.js +46 -0
- package/dist/core/hooks-runtime/naruto-terminal-finalization.js +1 -0
- package/dist/core/hooks-runtime/official-subagent-lifecycle.js +1 -1
- package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
- package/dist/core/hooks-runtime.js +33 -89
- package/dist/core/image-ux-review/callout-extraction.js +6 -0
- package/dist/core/image-ux-review/imagegen-adapter.js +64 -23
- package/dist/core/image-ux-review/real-callout-extractor.js +1 -0
- package/dist/core/image-ux-review.js +230 -12
- package/dist/core/imagegen/codex-lb-imagegen-target.js +21 -7
- package/dist/core/imagegen/imagegen-capability.js +10 -4
- package/dist/core/imagegen/require-imagegen.js +29 -5
- package/dist/core/init/skills.js +25 -13
- package/dist/core/init.js +1 -1
- package/dist/core/install/installed-package-smoke.js +157 -4
- package/dist/core/managed-assets/managed-assets-manifest.js +2 -1
- package/dist/core/managed-path-safety.js +8 -10
- package/dist/core/mcp-config/backup.js +2 -4
- package/dist/core/mcp-config/config-reader.js +2 -4
- package/dist/core/mcp-config/scope.js +3 -5
- package/dist/core/naruto/context-graph-advisor-scope.js +242 -0
- package/dist/core/naruto/context-graph-advisor.js +0 -0
- package/dist/core/perf/release-latency-slo.js +12 -6
- package/dist/core/pipeline-internals/runtime-core.js +78 -0
- package/dist/core/pipeline-internals/runtime-gates.js +5 -2
- package/dist/core/policy/latest-version-guidance.js +113 -0
- package/dist/core/ppt.js +8 -8
- package/dist/core/preflight/parallel-preflight-engine.js +1 -1
- package/dist/core/proof/auto-finalize.js +98 -6
- package/dist/core/proof/route-proof-gate.js +12 -1
- package/dist/core/proof/route-proof-policy.js +8 -8
- package/dist/core/release/gate-manifest.js +32 -1
- package/dist/core/release/npm-stage-contract.js +21 -0
- package/dist/core/release/package-size-budget.js +2 -2
- package/dist/core/release/release-gate-contract.js +5 -0
- package/dist/core/release/release-proof-io.js +1 -3
- package/dist/core/release/release-real-contract.js +5 -4
- package/dist/core/release/release-stamp-proof.js +1 -3
- package/dist/core/release/stage-publish.js +27 -0
- package/dist/core/remote/audit-idempotency.js +50 -18
- package/dist/core/remote/codex-public-activity.js +283 -0
- package/dist/core/remote/index.js +1 -0
- package/dist/core/remote/ssh-worker-client.js +4 -1
- package/dist/core/remote/worker.js +206 -27
- package/dist/core/research/research-adversarial-review.js +1 -3
- package/dist/core/research/research-claim-synthesizer.js +1 -3
- package/dist/core/research/research-falsification-runner.js +1 -3
- package/dist/core/research/research-source-evidence.js +1 -3
- package/dist/core/responses-stream.js +65 -19
- package/dist/core/routes/constants.js +1 -1
- package/dist/core/routes/design-policy.js +7 -2
- package/dist/core/routes/dollar-manifest-lite.js +2 -4
- package/dist/core/routes/evidence.js +1 -1
- package/dist/core/routes.js +30 -10
- package/dist/core/safety/command-contract/contracts.js +2 -0
- package/dist/core/safety/command-contract/types.js +1 -1
- package/dist/core/search/context-graph-seeds.js +205 -0
- package/dist/core/search/context.js +0 -0
- package/dist/core/search/provider.js +2 -2
- package/dist/core/secret-redaction.js +6 -0
- package/dist/core/subagents/naruto-help-contract.js +2 -1
- package/dist/core/subagents/naruto-host-credentials.js +161 -0
- package/dist/core/subagents/official-subagent-preparation.js +4 -1
- package/dist/core/subagents/official-subagent-prompt.js +44 -2
- package/dist/core/subagents/official-subagent-runner.js +26 -8
- package/dist/core/subagents/triwiki-attention.js +135 -123
- package/dist/core/telegram/bot-api.js +85 -10
- package/dist/core/telegram/center-snapshot.js +718 -0
- package/dist/core/telegram/config.js +158 -31
- package/dist/core/telegram/hub.js +288 -33
- package/dist/core/telegram/index.js +2 -0
- package/dist/core/telegram/ingress-spool.js +419 -0
- package/dist/core/telegram/ledgers.js +188 -29
- package/dist/core/telegram/messages.js +26 -6
- package/dist/core/telegram/owner-lock.js +10 -7
- package/dist/core/telegram/runtime-projection.js +38 -3
- package/dist/core/telegram/runtime.js +298 -63
- package/dist/core/telegram/service.js +137 -8
- package/dist/core/telegram/setup.js +291 -73
- package/dist/core/text/strings.js +3 -0
- package/dist/core/triwiki/code-pack.js +12 -120
- package/dist/core/triwiki/context-graph/compiler/cache-key.js +277 -0
- package/dist/core/triwiki/context-graph/compiler/extract.js +104 -0
- package/dist/core/triwiki/context-graph/compiler/freshness.js +108 -0
- package/dist/core/triwiki/context-graph/compiler/index.js +237 -0
- package/dist/core/triwiki/context-graph/compiler/merge.js +0 -0
- package/dist/core/triwiki/context-graph/compiler/serialize.js +115 -0
- package/dist/core/triwiki/context-graph/contracts.js +236 -0
- package/dist/core/triwiki/context-graph/extractors/code/builders.js +150 -0
- package/dist/core/triwiki/context-graph/extractors/code/declarations.js +161 -0
- package/dist/core/triwiki/context-graph/extractors/code/index.js +265 -0
- package/dist/core/triwiki/context-graph/extractors/code/inventory.js +237 -0
- package/dist/core/triwiki/context-graph/extractors/code/module-graph.js +252 -0
- package/dist/core/triwiki/context-graph/extractors/code/modules.js +79 -0
- package/dist/core/triwiki/context-graph/extractors/code/references.js +184 -0
- package/dist/core/triwiki/context-graph/extractors/code/selection.js +100 -0
- package/dist/core/triwiki/context-graph/extractors/code/ts-config.js +161 -0
- package/dist/core/triwiki/context-graph/extractors/code/types.js +18 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/claims.js +289 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/index.js +54 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/proof-index.js +213 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/proofs.js +266 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/redaction.js +177 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/shared.js +0 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/sources.js +154 -0
- package/dist/core/triwiki/context-graph/extractors/index.js +7 -0
- package/dist/core/triwiki/context-graph/extractors/topology/commands.js +207 -0
- package/dist/core/triwiki/context-graph/extractors/topology/gate-edges.js +201 -0
- package/dist/core/triwiki/context-graph/extractors/topology/gates.js +182 -0
- package/dist/core/triwiki/context-graph/extractors/topology/globs.js +124 -0
- package/dist/core/triwiki/context-graph/extractors/topology/index.js +92 -0
- package/dist/core/triwiki/context-graph/extractors/topology/routes.js +135 -0
- package/dist/core/triwiki/context-graph/extractors/topology/shared.js +215 -0
- package/dist/core/triwiki/context-graph/graph-index.js +143 -0
- package/dist/core/triwiki/context-graph/ids.js +0 -0
- package/dist/core/triwiki/context-graph/lint/index.js +0 -0
- package/dist/core/triwiki/context-graph/lint/rules.js +232 -0
- package/dist/core/triwiki/context-graph/lint/warnings.js +93 -0
- package/dist/core/triwiki/context-graph/paths.js +116 -0
- package/dist/core/triwiki/context-graph/profiles.js +78 -0
- package/dist/core/triwiki/context-graph/projections/anchors.js +91 -0
- package/dist/core/triwiki/context-graph/projections/attention.js +58 -0
- package/dist/core/triwiki/context-graph/projections/code-pack-workspace.js +62 -0
- package/dist/core/triwiki/context-graph/projections/code-pack.js +144 -0
- package/dist/core/triwiki/context-graph/projections/index.js +7 -0
- package/dist/core/triwiki/context-graph/projections/module-view.js +140 -0
- package/dist/core/triwiki/context-graph/projections/node-summary.js +118 -0
- package/dist/core/triwiki/context-graph/projections/pack-contract.js +6 -0
- package/dist/core/triwiki/context-graph/query/explain.js +0 -0
- package/dist/core/triwiki/context-graph/query/index.js +178 -0
- package/dist/core/triwiki/context-graph/query/load.js +116 -0
- package/dist/core/triwiki/context-graph/query/pack.js +117 -0
- package/dist/core/triwiki/context-graph/query/rank.js +142 -0
- package/dist/core/triwiki/context-graph/query/ranking-config.js +64 -0
- package/dist/core/triwiki/context-graph/query/seeds.js +241 -0
- package/dist/core/triwiki/context-graph/query/snapshot-cache.js +72 -0
- package/dist/core/triwiki/context-graph/query/traverse.js +154 -0
- package/dist/core/triwiki/context-graph/query-types.js +29 -0
- package/dist/core/triwiki/context-graph/store/compile-lock.js +21 -0
- package/dist/core/triwiki/context-graph/store/event-log.js +48 -0
- package/dist/core/triwiki/context-graph/store/fragment-cache.js +85 -0
- package/dist/core/triwiki/context-graph/store/graph-status.js +85 -0
- package/dist/core/triwiki/context-graph/store/snapshot-store.js +155 -0
- package/dist/core/triwiki/triwiki-proof-bank-index-repair.js +115 -0
- package/dist/core/triwiki/triwiki-proof-bank-index-store.js +290 -0
- package/dist/core/triwiki/triwiki-proof-bank-index.js +126 -0
- package/dist/core/triwiki/triwiki-proof-bank.js +7 -1
- package/dist/core/verification/context-graph-affected.js +271 -0
- package/dist/core/version.js +1 -1
- package/dist/native/sks-menubar/Sources/AlertFactory.swift +11 -3
- package/dist/native/sks-menubar/Sources/AppDelegate.swift +9 -0
- package/dist/native/sks-menubar/Sources/ControlCenterWindowController.swift +25 -3
- package/dist/native/sks-menubar/Sources/ControlKit.swift +19 -8
- package/dist/native/sks-menubar/Sources/DiagnosticsViewController.swift +1 -2
- package/dist/native/sks-menubar/Sources/MCPServersViewController.swift +45 -17
- package/dist/native/sks-menubar/Sources/OverviewSummary.swift +162 -0
- package/dist/native/sks-menubar/Sources/OverviewViewController.swift +136 -190
- package/dist/native/sks-menubar/Sources/ProcessClient.swift +107 -11
- package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +5 -1
- package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +16 -4
- package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +4 -1
- package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +131 -0
- package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +288 -341
- package/dist/native/sks-menubar/Sources/RemoteTelegramCenterState.swift +447 -0
- package/dist/native/sks-menubar/Sources/RemoteTelegramRendering.swift +484 -0
- package/dist/native/sks-menubar/Sources/RemoteTelegramViewController.swift +364 -115
- package/dist/native/sks-menubar/Sources/SettingsViewController.swift +73 -12
- package/dist/native/sks-menubar/Sources/SidebarItem.swift +3 -3
- package/dist/native/sks-menubar/Sources/StatusItemController.swift +13 -13
- package/dist/native/sks-menubar/Sources/TelegramHubSupervisor.swift +469 -0
- package/dist/native/sks-menubar/Sources/UpdatesViewController.swift +1 -6
- package/dist/native/sks-menubar/Tests/AppRuntimeTestSupport.swift +12 -0
- package/dist/native/sks-menubar/Tests/NativeTelegramRegressionRunner.swift +55 -0
- package/dist/native/sks-menubar/Tests/ProcessClientTests.swift +33 -0
- package/dist/native/sks-menubar/Tests/ProvidersViewControllerTests.swift +142 -0
- package/dist/native/sks-menubar/Tests/RemoteTelegramCenterStateTests.swift +56 -0
- package/dist/native/sks-menubar/Tests/TelegramHubSupervisorTests.swift +55 -0
- package/dist/scripts/codex-lb-catalog-passthrough-check.js +120 -0
- package/dist/scripts/codex-lb-desktop-auth-invariant-check.js +169 -0
- package/dist/scripts/codex-lb-desktop-bridge-check.js +394 -0
- package/dist/scripts/codex-lb-desktop-bridge-latency-check.js +196 -0
- package/dist/scripts/codex-lb-desktop-capabilities-check.js +287 -0
- package/dist/scripts/codex-lb-desktop-real-evidence-check.js +123 -0
- package/dist/scripts/codex-lb-fast-mode-truth-check.js +10 -3
- package/dist/scripts/codex-lb-fast-ui-preservation-check.js +81 -40
- package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +99 -37
- package/dist/scripts/context-graph-check.js +191 -0
- package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +1 -1
- package/dist/scripts/gate-policy-audit-check.js +1 -1
- package/dist/scripts/helper-dedup-check.js +0 -2
- package/dist/scripts/installed-package-smoke-check.js +4 -1
- package/dist/scripts/latest-version-guidance-check.js +74 -0
- package/dist/scripts/release-metadata-check.js +21 -4
- package/dist/scripts/release-real-check.js +7 -0
- package/dist/scripts/ux-review-imagegen-blackbox-check.js +25 -1
- package/dist/scripts/ux-review-run-wires-imagegen-check.js +9 -1
- package/package.json +8 -3
- package/release-gates.v2.json +176 -1
- package/runtime-required-scripts.json +30 -0
- package/schemas/triwiki/context-graph.schema.json +119 -0
- package/dist/cli/install-helpers-codex-lb-selftest-chain.js +0 -112
- package/dist/cli/install-helpers-codex-lb-selftest.js +0 -481
- package/dist/core/triwiki/code-index-scanner.js +0 -309
|
@@ -12,9 +12,11 @@ import { discoverCodexAppGeneratedImage } from './codex-app-generated-image-disc
|
|
|
12
12
|
import { writeImageArtifactPathContract } from '../image/image-artifact-path-contract.js';
|
|
13
13
|
import { registerImageArtifact } from '../image/image-artifact-registry.js';
|
|
14
14
|
const DEFAULT_OPENAI_IMAGE_EDITS_ENDPOINT = 'https://api.openai.com/v1/images/edits';
|
|
15
|
+
export const DEFAULT_IMAGEGEN_FETCH_TIMEOUT_MS = 180000;
|
|
16
|
+
const responseDeadlines = new WeakMap();
|
|
15
17
|
export function buildCalloutPrompt(sourceScreenId, context = {}) {
|
|
16
18
|
return [
|
|
17
|
-
'Review this UI screenshot as a senior product
|
|
19
|
+
'Review this UI screenshot as a senior Toss UI/UX product designer.',
|
|
18
20
|
`Screenshot source id: ${sourceScreenId}.`,
|
|
19
21
|
'Output must be a new image artifact, not prose.',
|
|
20
22
|
'Text-only response is invalid.',
|
|
@@ -322,7 +324,7 @@ export function createOpenAIImagesApiAdapter(opts = {}) {
|
|
|
322
324
|
const responsesOutputSource = codexLbProviderSelected ? CODEX_LB_PROVIDER_OUTPUT_SOURCE : null;
|
|
323
325
|
const validation = await validateGptImage2Request({
|
|
324
326
|
provider: 'openai_images_api',
|
|
325
|
-
endpoint: effectiveEndpoint,
|
|
327
|
+
endpoint: String(effectiveEndpoint || ''),
|
|
326
328
|
model: 'gpt-image-2',
|
|
327
329
|
prompt: input.prompt,
|
|
328
330
|
source_image_path: sourcePath,
|
|
@@ -362,7 +364,7 @@ export function createOpenAIImagesApiAdapter(opts = {}) {
|
|
|
362
364
|
});
|
|
363
365
|
return { ok: false, status: 'blocked', generated_image_path: null, output_id: null, blocker: 'gpt_image_2_request_validation_failed', provider: 'openai_images_api', request_artifact: requestArtifact, response_artifact: responseArtifact, latency_ms: Date.now() - started };
|
|
364
366
|
}
|
|
365
|
-
if (!auth.apiKey) {
|
|
367
|
+
if (auth.blocker || !auth.apiKey) {
|
|
366
368
|
const blocked = {
|
|
367
369
|
schema: 'sks.image-ux-gpt-image-2-response.v1',
|
|
368
370
|
created_at: nowIso(),
|
|
@@ -373,7 +375,7 @@ export function createOpenAIImagesApiAdapter(opts = {}) {
|
|
|
373
375
|
status: 'blocked',
|
|
374
376
|
blocker: auth.blocker,
|
|
375
377
|
setup_guidance: auth.auth_source === 'CODEX_LB_API_KEY'
|
|
376
|
-
? '
|
|
378
|
+
? 'Repair the selected codex-lb credential binding. Completed output from a selected provider can satisfy full evidence; codex-lb used only as a fallback while another provider is selected cannot.'
|
|
377
379
|
: 'Set OPENAI_API_KEY only for an explicit non-Codex Images API fallback, or attach a real Codex App $imagegen output image for full SKS verification.',
|
|
378
380
|
local_only: true
|
|
379
381
|
};
|
|
@@ -463,6 +465,8 @@ export function createOpenAIImagesApiAdapter(opts = {}) {
|
|
|
463
465
|
output_id: meta.output_id,
|
|
464
466
|
output_source: responsesOutputSource,
|
|
465
467
|
image_output_recovered_from_stream: payload?.image_output_recovered_from_stream === true,
|
|
468
|
+
image_output_provenance: payload?.image_output_provenance || 'response.output',
|
|
469
|
+
image_output_partial_frame: false,
|
|
466
470
|
image_artifact_path_contract: imageContract?.artifact_path || null,
|
|
467
471
|
dimensions: { width: meta.width, height: meta.height, format: meta.format },
|
|
468
472
|
latency_ms: Date.now() - started,
|
|
@@ -605,6 +609,8 @@ function codexLbTargetInputs(source = {}) {
|
|
|
605
609
|
const inputs = {};
|
|
606
610
|
if (source?.home)
|
|
607
611
|
inputs.home = source.home;
|
|
612
|
+
if (source?.codexHome)
|
|
613
|
+
inputs.codexHome = source.codexHome;
|
|
608
614
|
if (source?.env)
|
|
609
615
|
inputs.env = source.env;
|
|
610
616
|
if (typeof source?.configText === 'string')
|
|
@@ -625,15 +631,22 @@ export function imagegenCapabilityBlocker(surface = 'Codex App $imagegen') {
|
|
|
625
631
|
blocker: 'imagegen_capability_missing',
|
|
626
632
|
surface,
|
|
627
633
|
model: 'gpt-image-2',
|
|
628
|
-
guidance: 'Run the request
|
|
634
|
+
guidance: 'Run the request with gpt-image-2 through Codex App $imagegen or a selected, ready codex-lb provider and bind the completed generated image path. OPENAI_API_KEY, or codex-lb used while another provider is selected, remains non-Codex fallback evidence. SKS must not fabricate or substitute a text-only review.'
|
|
629
635
|
};
|
|
630
636
|
}
|
|
631
637
|
async function resolveImagesApiAuth(opts = {}) {
|
|
638
|
+
const codexLb = opts.codexLb?.available === true ? opts.codexLb : null;
|
|
639
|
+
const target = opts.codexLbTarget || (codexLb
|
|
640
|
+
? await resolveCodexLbImagegenTarget(codexLbTargetInputs(opts)).catch(() => null)
|
|
641
|
+
: null);
|
|
642
|
+
if (target?.selected === true)
|
|
643
|
+
return codexLbImagesApiAuth(opts, codexLb, target);
|
|
632
644
|
const openAiKey = String(opts.apiKey || process.env.OPENAI_API_KEY || '').trim();
|
|
633
645
|
if (openAiKey) {
|
|
634
646
|
return {
|
|
635
647
|
apiKey: openAiKey,
|
|
636
648
|
auth_source: 'OPENAI_API_KEY',
|
|
649
|
+
api_key_source: null,
|
|
637
650
|
responses_model: responsesImagegenModel(opts),
|
|
638
651
|
responses_model_source: responsesImagegenModel(opts) ? 'explicit' : null,
|
|
639
652
|
endpoint: imageEditsEndpoint(opts.baseUrl || 'https://api.openai.com/v1'),
|
|
@@ -641,11 +654,11 @@ async function resolveImagesApiAuth(opts = {}) {
|
|
|
641
654
|
blocker: null
|
|
642
655
|
};
|
|
643
656
|
}
|
|
644
|
-
const codexLb = opts.codexLb?.available === true ? opts.codexLb : null;
|
|
645
657
|
if (!codexLb) {
|
|
646
658
|
return {
|
|
647
659
|
apiKey: null,
|
|
648
660
|
auth_source: null,
|
|
661
|
+
api_key_source: null,
|
|
649
662
|
responses_model: responsesImagegenModel(opts),
|
|
650
663
|
responses_model_source: null,
|
|
651
664
|
endpoint: imageEditsEndpoint(opts.baseUrl || 'https://api.openai.com/v1'),
|
|
@@ -653,19 +666,26 @@ async function resolveImagesApiAuth(opts = {}) {
|
|
|
653
666
|
blocker: 'openai_api_key_missing'
|
|
654
667
|
};
|
|
655
668
|
}
|
|
656
|
-
|
|
657
|
-
|
|
669
|
+
return codexLbImagesApiAuth(opts, codexLb, target);
|
|
670
|
+
}
|
|
671
|
+
function codexLbImagesApiAuth(opts, codexLb, target) {
|
|
672
|
+
const envKey = codexLb?.env_key || 'CODEX_LB_API_KEY';
|
|
658
673
|
const codexLbKey = String(opts.codexLbApiKey || target?.api_key || '').trim();
|
|
659
|
-
const baseUrl = target?.base_url || codexLb
|
|
674
|
+
const baseUrl = target?.base_url || codexLb?.base_url || opts.baseUrl || '';
|
|
675
|
+
const blocker = !baseUrl
|
|
676
|
+
? 'codex_lb_base_url_missing'
|
|
677
|
+
: codexLbKey
|
|
678
|
+
? null
|
|
679
|
+
: target?.blocker || 'codex_lb_api_key_missing';
|
|
660
680
|
return {
|
|
661
681
|
apiKey: codexLbKey || null,
|
|
662
682
|
auth_source: envKey,
|
|
663
683
|
api_key_source: target?.api_key_source || null,
|
|
664
684
|
responses_model: responsesImagegenModel(opts) || target?.model || '',
|
|
665
685
|
responses_model_source: responsesImagegenModel(opts) ? 'explicit' : target?.model_source || null,
|
|
666
|
-
endpoint: imageEditsEndpoint(baseUrl),
|
|
667
|
-
responses_endpoint: responsesEndpoint(baseUrl),
|
|
668
|
-
blocker
|
|
686
|
+
endpoint: baseUrl ? imageEditsEndpoint(baseUrl) : null,
|
|
687
|
+
responses_endpoint: baseUrl ? responsesEndpoint(baseUrl) : null,
|
|
688
|
+
blocker
|
|
669
689
|
};
|
|
670
690
|
}
|
|
671
691
|
function imageEditsEndpoint(baseUrl = '') {
|
|
@@ -677,23 +697,27 @@ function responsesEndpoint(baseUrl = '') {
|
|
|
677
697
|
return /\/responses$/i.test(base) ? base : `${base}/responses`;
|
|
678
698
|
}
|
|
679
699
|
function responsesImagegenModel(opts = {}) {
|
|
680
|
-
return String(opts.responsesModel || process.env.SKS_IMAGEGEN_RESPONSES_MODEL ||
|
|
700
|
+
return String(opts.responsesModel || process.env.SKS_IMAGEGEN_RESPONSES_MODEL || '').trim();
|
|
681
701
|
}
|
|
682
702
|
function imagegenFetchTimeoutMs(opts = {}) {
|
|
683
|
-
const value = Number(opts.fetchTimeoutMs || process.env.SKS_IMAGEGEN_FETCH_TIMEOUT_MS ||
|
|
684
|
-
return Number.isFinite(value) && value > 0 ? value :
|
|
703
|
+
const value = Number(opts.fetchTimeoutMs || process.env.SKS_IMAGEGEN_FETCH_TIMEOUT_MS || DEFAULT_IMAGEGEN_FETCH_TIMEOUT_MS);
|
|
704
|
+
return Number.isFinite(value) && value > 0 ? value : DEFAULT_IMAGEGEN_FETCH_TIMEOUT_MS;
|
|
685
705
|
}
|
|
686
706
|
async function fetchWithTimeout(url, init, timeoutMs) {
|
|
687
707
|
const controller = new AbortController();
|
|
708
|
+
const startedAt = Date.now();
|
|
688
709
|
const timeout = setTimeout(() => controller.abort(new Error(`imagegen_fetch_timeout_${timeoutMs}ms`)), timeoutMs);
|
|
689
710
|
try {
|
|
690
|
-
|
|
711
|
+
const response = await fetch(url, { ...init, signal: controller.signal });
|
|
712
|
+
responseDeadlines.set(response, { controller, startedAt, timeoutMs, timer: timeout });
|
|
713
|
+
return response;
|
|
691
714
|
}
|
|
692
|
-
|
|
715
|
+
catch (err) {
|
|
693
716
|
clearTimeout(timeout);
|
|
717
|
+
throw err;
|
|
694
718
|
}
|
|
695
719
|
}
|
|
696
|
-
async function readResponsePayload(response, timeoutMs =
|
|
720
|
+
async function readResponsePayload(response, timeoutMs = DEFAULT_IMAGEGEN_FETCH_TIMEOUT_MS) {
|
|
697
721
|
const text = await textWithTimeout(response, timeoutMs);
|
|
698
722
|
if (!text)
|
|
699
723
|
return {};
|
|
@@ -708,23 +732,39 @@ async function readResponsePayload(response, timeoutMs = 90000) {
|
|
|
708
732
|
}
|
|
709
733
|
}
|
|
710
734
|
async function textWithTimeout(response, timeoutMs) {
|
|
735
|
+
const deadline = responseDeadlines.get(response);
|
|
736
|
+
if (deadline)
|
|
737
|
+
clearTimeout(deadline.timer);
|
|
738
|
+
const remainingMs = deadline
|
|
739
|
+
? Math.max(1, deadline.timeoutMs - (Date.now() - deadline.startedAt))
|
|
740
|
+
: timeoutMs;
|
|
711
741
|
let timeout = null;
|
|
712
742
|
try {
|
|
713
743
|
return await Promise.race([
|
|
714
744
|
response.text(),
|
|
715
745
|
new Promise((_, reject) => {
|
|
716
|
-
timeout = setTimeout(() =>
|
|
746
|
+
timeout = setTimeout(() => {
|
|
747
|
+
const error = new Error(`imagegen_response_read_timeout_${timeoutMs}ms`);
|
|
748
|
+
deadline?.controller.abort(error);
|
|
749
|
+
void response.body?.cancel(error).catch(() => { });
|
|
750
|
+
reject(error);
|
|
751
|
+
}, remainingMs);
|
|
717
752
|
})
|
|
718
753
|
]);
|
|
719
754
|
}
|
|
720
755
|
finally {
|
|
721
756
|
if (timeout)
|
|
722
757
|
clearTimeout(timeout);
|
|
758
|
+
responseDeadlines.delete(response);
|
|
723
759
|
}
|
|
724
760
|
}
|
|
725
761
|
function findResponsesImageGenerationOutput(payload) {
|
|
762
|
+
if (payload?.image_output_partial_frame === true)
|
|
763
|
+
return null;
|
|
726
764
|
for (const output of Array.isArray(payload?.output) ? payload.output : []) {
|
|
727
765
|
if (String(output?.type || '') === 'image_generation_call') {
|
|
766
|
+
if (String(output?.status || '') === 'partial')
|
|
767
|
+
continue;
|
|
728
768
|
const b64 = typeof output?.result === 'string' ? output.result : output?.result?.b64_json || output?.b64_json || null;
|
|
729
769
|
if (b64)
|
|
730
770
|
return { b64: String(b64), id: output?.id || payload?.id || null };
|
|
@@ -813,10 +853,8 @@ function imagegenRetryOptions(opts = {}) {
|
|
|
813
853
|
return {
|
|
814
854
|
sleep: opts.retrySleep,
|
|
815
855
|
classifyError: (err) => {
|
|
816
|
-
const
|
|
817
|
-
|
|
818
|
-
|| err?.message
|
|
819
|
-
|| '');
|
|
856
|
+
const row = err;
|
|
857
|
+
const code = [row?.code, row?.name, row?.message].filter(Boolean).join(' ');
|
|
820
858
|
if (/AbortError|timeout|abort/i.test(code))
|
|
821
859
|
return { code: 'ETIMEDOUT', status: null };
|
|
822
860
|
if (/ECONNRESET|EAI_AGAIN|ETIMEDOUT|ENOTFOUND|ECONNREFUSED|fetch failed/i.test(code))
|
|
@@ -834,6 +872,9 @@ function summarizeImagegenPayload(payload) {
|
|
|
834
872
|
model: payload?.model || null,
|
|
835
873
|
error_type: payload?.error?.type || null,
|
|
836
874
|
error_code: payload?.error?.code || null,
|
|
875
|
+
image_output_partial_frame: payload?.image_output_partial_frame === true,
|
|
876
|
+
partial_image_output_present: payload?.partial_image_output_present === true,
|
|
877
|
+
image_output_provenance: payload?.image_output_provenance || null,
|
|
837
878
|
output_count: outputs.length,
|
|
838
879
|
output: outputs.slice(0, 8).map((output) => ({
|
|
839
880
|
id: output?.id || null,
|
|
@@ -130,6 +130,7 @@ export function buildRealCalloutExtractionPrompt(input) {
|
|
|
130
130
|
const source = input.sourceScreenshot || {};
|
|
131
131
|
return [
|
|
132
132
|
'Analyze the generated UX review image pixels directly.',
|
|
133
|
+
'The source screenshot is context only; do not analyze it instead of the generated annotated image.',
|
|
133
134
|
'Return only visible numbered callouts from the generated image.',
|
|
134
135
|
'Do not invent issues, requirements, or invisible callouts.',
|
|
135
136
|
'Return bbox coordinates in the generated image coordinate system as [x,y,width,height].',
|
|
@@ -12,7 +12,7 @@ import { buildIssueLedgerFromGeneratedCallouts } from './image-ux-review/callout
|
|
|
12
12
|
import { planImageUxFixTasks } from './image-ux-review/fix-task-planner.js';
|
|
13
13
|
import { runImageUxFixLoop } from './image-ux-review/fix-loop.js';
|
|
14
14
|
import { buildRecapturePlan } from './image-ux-review/recapture.js';
|
|
15
|
-
import { addVisualAnchor, ingestImage } from './wiki-image/image-voxel-ledger.js';
|
|
15
|
+
import { addImageRelation, addVisualAnchor, ingestImage } from './wiki-image/image-voxel-ledger.js';
|
|
16
16
|
import { validateFinalHonestModeReport } from './artifact-schemas.js';
|
|
17
17
|
import { imagegenEvidenceClassBlockers, isFullImagegenOutputSource } from './imagegen/imagegen-evidence.js';
|
|
18
18
|
export const IMAGE_UX_REVIEW_GATE_ARTIFACT = 'image-ux-review-gate.json';
|
|
@@ -69,6 +69,35 @@ export function imageUxReviewGateAllowsReferenceCloseout(gate = {}) {
|
|
|
69
69
|
&& gate?.verified_level === 'verified_partial'
|
|
70
70
|
&& gate?.full_review_passed !== true;
|
|
71
71
|
}
|
|
72
|
+
export function imageUxReviewCommandOutcome(gate = {}, proofOk = false, opts = {}) {
|
|
73
|
+
const reviewReportCompleted = gate?.review_report_completed === true;
|
|
74
|
+
const reviewOnlyCompletionAllowed = opts.allowReviewOnlyCompletion === true;
|
|
75
|
+
const reviewOnlyCompleted = reviewOnlyCompletionAllowed && reviewReportCompleted;
|
|
76
|
+
const gateAndProofPassed = gate?.passed === true && proofOk === true;
|
|
77
|
+
const fullReviewPassed = gate?.full_review_passed === true && gateAndProofPassed;
|
|
78
|
+
return {
|
|
79
|
+
ok: gateAndProofPassed || reviewOnlyCompleted,
|
|
80
|
+
status: fullReviewPassed
|
|
81
|
+
? 'passed'
|
|
82
|
+
: reviewOnlyCompleted
|
|
83
|
+
? 'review_completed'
|
|
84
|
+
: gateAndProofPassed
|
|
85
|
+
? gate?.status || 'passed'
|
|
86
|
+
: 'blocked',
|
|
87
|
+
completion_scope: fullReviewPassed
|
|
88
|
+
? 'full_review_and_optional_fix_verification'
|
|
89
|
+
: reviewOnlyCompleted
|
|
90
|
+
? 'capture_imagegen_ocr_and_ux_report'
|
|
91
|
+
: null,
|
|
92
|
+
review_report_completed: reviewReportCompleted,
|
|
93
|
+
review_only_completion_allowed: reviewOnlyCompletionAllowed,
|
|
94
|
+
full_review_passed: gate?.full_review_passed === true,
|
|
95
|
+
requires_human_review: gate?.requires_human_review === true,
|
|
96
|
+
unresolved_p0_p1_count: Number(gate?.unresolved_p0_p1_count || 0),
|
|
97
|
+
safety_blockers: Array.isArray(gate?.blockers) ? gate.blockers : [],
|
|
98
|
+
proof_ok: proofOk === true
|
|
99
|
+
};
|
|
100
|
+
}
|
|
72
101
|
function cleanText(value, fallback = '') {
|
|
73
102
|
const text = String(value ?? '').replace(/\s+/g, ' ').trim();
|
|
74
103
|
return text || fallback;
|
|
@@ -257,9 +286,21 @@ export async function hydrateImageUxScreenInventory(root, inventory) {
|
|
|
257
286
|
export function buildImageUxGeneratedReviewLedger(contract = {}, inventory = buildImageUxScreenInventory(contract), existing = null, opts = {}) {
|
|
258
287
|
const existingImages = Array.isArray(existing?.generated_review_images) ? existing.generated_review_images : [];
|
|
259
288
|
const sourceScreens = inventory.source_screens || [];
|
|
260
|
-
const normalizedImages = existingImages.map((image, index) =>
|
|
289
|
+
const normalizedImages = existingImages.map((image, index) => {
|
|
290
|
+
const declaredSourceId = image.source_screen_id
|
|
291
|
+
|| (Array.isArray(image.source_screen_ids) ? image.source_screen_ids[0] : null);
|
|
292
|
+
const sourceScreen = sourceScreens.find((screen) => screen.id === declaredSourceId)
|
|
293
|
+
|| sourceScreens[index]
|
|
294
|
+
|| {};
|
|
295
|
+
return normalizeGeneratedReviewImage(image, sourceScreen, opts);
|
|
296
|
+
});
|
|
261
297
|
const realGeneratedCount = normalizedImages.filter((image) => image.real_generated === true && image.mock !== true).length;
|
|
262
|
-
const
|
|
298
|
+
const coveredSourceScreenIds = sourceScreens
|
|
299
|
+
.filter((screen) => normalizedImages.some((image) => generatedImageCoversScreen(image, screen.id)
|
|
300
|
+
&& image.real_generated === true
|
|
301
|
+
&& image.mock !== true))
|
|
302
|
+
.map((screen) => screen.id);
|
|
303
|
+
const missingRealGeneratedScreens = sourceScreens.filter((screen) => !coveredSourceScreenIds.includes(screen.id));
|
|
263
304
|
const nonRealGeneratedCount = normalizedImages.filter((image) => image.real_generated !== true || image.mock === true).length;
|
|
264
305
|
const textOnlyCount = normalizedImages.filter((image) => image.text_only === true).length;
|
|
265
306
|
const evidenceBlockers = Array.from(new Set(normalizedImages.flatMap((image) => image.evidence_blockers || []).map((blocker) => String(blocker))));
|
|
@@ -295,7 +336,7 @@ export function buildImageUxGeneratedReviewLedger(contract = {}, inventory = bui
|
|
|
295
336
|
source_screen_id: screen.id,
|
|
296
337
|
source_sha256: screen.sha256 || null,
|
|
297
338
|
prompt: buildCalloutPrompt(screen.id, { target: inventory.target }),
|
|
298
|
-
status: normalizedImages.some((image) => image
|
|
339
|
+
status: normalizedImages.some((image) => generatedImageCoversScreen(image, screen.id)) ? 'generated_or_attached' : 'pending_imagegen',
|
|
299
340
|
required_output: 'annotated_review_image_with_numbered_callouts_severity_labels_markers_arrows_and_mini_comp',
|
|
300
341
|
requested_fidelity: 'high_fidelity_automatic',
|
|
301
342
|
image_input_fidelity_note: 'gpt-image-2 processes image inputs at high fidelity automatically; SKS omits unsupported input_fidelity.',
|
|
@@ -303,6 +344,8 @@ export function buildImageUxGeneratedReviewLedger(contract = {}, inventory = bui
|
|
|
303
344
|
})),
|
|
304
345
|
generated_count: normalizedImages.length,
|
|
305
346
|
real_generated_count: realGeneratedCount,
|
|
347
|
+
covered_source_screen_count: coveredSourceScreenIds.length,
|
|
348
|
+
covered_source_screen_ids: coveredSourceScreenIds,
|
|
306
349
|
non_real_generated_count: nonRealGeneratedCount,
|
|
307
350
|
required_count: sourceScreens.length,
|
|
308
351
|
text_only_count: textOnlyCount,
|
|
@@ -313,7 +356,9 @@ export function buildImageUxGeneratedReviewLedger(contract = {}, inventory = bui
|
|
|
313
356
|
&& textOnlyCount === 0
|
|
314
357
|
&& missingRealGeneratedScreens.length > 0,
|
|
315
358
|
blockers: uniqueBlockers,
|
|
316
|
-
passed: sourceScreens.length > 0
|
|
359
|
+
passed: sourceScreens.length > 0
|
|
360
|
+
&& blockers.length === 0
|
|
361
|
+
&& coveredSourceScreenIds.length === sourceScreens.length,
|
|
317
362
|
imagegen_blocker: uniqueBlockers.includes('imagegen_capability_missing')
|
|
318
363
|
? imagegenCapabilityBlocker()
|
|
319
364
|
: uniqueBlockers.includes('generated_review_image_missing')
|
|
@@ -340,6 +385,17 @@ export function buildImageUxIssueLedger(contract = {}, generatedReviewLedger = b
|
|
|
340
385
|
};
|
|
341
386
|
}
|
|
342
387
|
export function buildImageUxIterationReport(contract = {}, policy = buildImageUxReviewPolicy(contract), generatedReviewLedger = buildImageUxGeneratedReviewLedger(contract), issueLedger = buildImageUxIssueLedger(contract, generatedReviewLedger), fixTaskPlan = planImageUxFixTasks(issueLedger), fixLoop = runImageUxFixLoop(issueLedger, fixTaskPlan), recapturePlan = buildRecapturePlan(fixLoop)) {
|
|
388
|
+
const generatedImage = (generatedReviewLedger.generated_review_images || [])[0] || null;
|
|
389
|
+
const issues = Array.isArray(issueLedger.issues) ? issueLedger.issues : [];
|
|
390
|
+
const basedOnGeneratedImage = issueLedger.extracted_from_generated_callout === true
|
|
391
|
+
&& issues.length > 0
|
|
392
|
+
&& issues.every((issue) => issue.extracted_from_generated_image === true
|
|
393
|
+
&& issue.generated_image_sha256 === generatedImage?.sha256);
|
|
394
|
+
const requiresHumanReview = (fixTaskPlan.tasks || []).some((task) => task.requires_human_review === true);
|
|
395
|
+
const severityCounts = Object.fromEntries(['P0', 'P1', 'P2', 'P3'].map((severity) => [
|
|
396
|
+
severity,
|
|
397
|
+
issues.filter((issue) => issue.severity === severity).length
|
|
398
|
+
]));
|
|
343
399
|
const passed = generatedReviewLedger.passed === true
|
|
344
400
|
&& issueLedger.passed === true
|
|
345
401
|
&& fixLoop.passed === true
|
|
@@ -351,6 +407,43 @@ export function buildImageUxIterationReport(contract = {}, policy = buildImageUx
|
|
|
351
407
|
schema_version: 2,
|
|
352
408
|
created_at: nowIso(),
|
|
353
409
|
contract_hash: contract.sealed_hash || null,
|
|
410
|
+
report_type: 'ux_ui_change_report',
|
|
411
|
+
review_report_completed: generatedReviewLedger.passed === true
|
|
412
|
+
&& issueLedger.validation?.ok === true
|
|
413
|
+
&& basedOnGeneratedImage,
|
|
414
|
+
full_review_passed: passed,
|
|
415
|
+
requires_human_review: requiresHumanReview,
|
|
416
|
+
unresolved_p0_p1_count: Number(issueLedger.blocking_issue_count || 0),
|
|
417
|
+
analysis_basis: {
|
|
418
|
+
primary_evidence: 'generated_annotated_review_image',
|
|
419
|
+
generated_image_path: generatedImage?.path || null,
|
|
420
|
+
generated_image_sha256: generatedImage?.sha256 || null,
|
|
421
|
+
generated_image_id: generatedImage?.id || null,
|
|
422
|
+
source_screen_id: generatedImage?.source_screen_id || 'screen-1',
|
|
423
|
+
issue_extraction_source: issueLedger.extraction_source || IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT,
|
|
424
|
+
based_on_generated_image: basedOnGeneratedImage
|
|
425
|
+
},
|
|
426
|
+
executive_summary: {
|
|
427
|
+
issue_count: issues.length,
|
|
428
|
+
severity_counts: severityCounts,
|
|
429
|
+
highest_priority: issues.find((issue) => ['P0', 'P1'].includes(issue.severity))?.severity
|
|
430
|
+
|| issues[0]?.severity
|
|
431
|
+
|| null
|
|
432
|
+
},
|
|
433
|
+
recommendations: issues.map((issue) => ({
|
|
434
|
+
issue_id: issue.id,
|
|
435
|
+
callout_id: issue.callout_id,
|
|
436
|
+
severity: issue.severity,
|
|
437
|
+
title: issue.title,
|
|
438
|
+
observed_problem: issue.detail,
|
|
439
|
+
likely_cause: issue.likely_cause,
|
|
440
|
+
recommended_ux_ui_change: issue.fix_action,
|
|
441
|
+
target_surface: issue.target_surface,
|
|
442
|
+
generated_image_bbox: issue.bbox,
|
|
443
|
+
generated_image_sha256: issue.generated_image_sha256,
|
|
444
|
+
confidence: issue.confidence,
|
|
445
|
+
verification_status: issue.fix_verification_status
|
|
446
|
+
})),
|
|
354
447
|
loop_policy: {
|
|
355
448
|
max_full_surface_passes: policy.max_full_surface_passes,
|
|
356
449
|
max_screen_retries: policy.max_screen_retries,
|
|
@@ -416,6 +509,15 @@ export function defaultImageUxReviewGate(contract = {}, parts = {}) {
|
|
|
416
509
|
const wrongnessChecked = parts.wrongnessChecked === true || generatedReviewLedger.blockers?.length === 0;
|
|
417
510
|
const honestModeEvidence = parts.honestModeEvidence || null;
|
|
418
511
|
const honestModeComplete = parts.honestModeComplete === true && (honestModeEvidence?.ok === true || parts.honestModeEvidenceRequired !== true);
|
|
512
|
+
const requiresHumanReview = (fixTaskPlan.tasks || []).some((task) => task.requires_human_review === true);
|
|
513
|
+
const reviewReportCompleted = realSourceScreenshotPresent
|
|
514
|
+
&& officialOrUserScreenshotSource
|
|
515
|
+
&& generatedReviewLedger.passed === true
|
|
516
|
+
&& realGeneratedCount > 0
|
|
517
|
+
&& calloutExtractionSchemaValid
|
|
518
|
+
&& issueLedger.extracted_from_generated_callout === true
|
|
519
|
+
&& iterationReport.review_report_completed === true
|
|
520
|
+
&& iterationReport.analysis_basis?.based_on_generated_image === true;
|
|
419
521
|
const referenceCloseoutPassed = generatedImageUnavailable
|
|
420
522
|
&& realSourceScreenshotPresent
|
|
421
523
|
&& officialOrUserScreenshotSource
|
|
@@ -436,6 +538,10 @@ export function defaultImageUxReviewGate(contract = {}, parts = {}) {
|
|
|
436
538
|
status: 'blocked',
|
|
437
539
|
verified_level: 'blocked',
|
|
438
540
|
full_review_passed: false,
|
|
541
|
+
review_report_completed: reviewReportCompleted,
|
|
542
|
+
completion_scope: reviewReportCompleted ? 'capture_imagegen_ocr_and_ux_report' : null,
|
|
543
|
+
requires_human_review: requiresHumanReview,
|
|
544
|
+
unresolved_p0_p1_count: Number(issueLedger.blocking_issue_count || 0),
|
|
439
545
|
reference_only: false,
|
|
440
546
|
real_source_screenshot_present: realSourceScreenshotPresent,
|
|
441
547
|
computer_use_or_user_screenshot_source: officialOrUserScreenshotSource,
|
|
@@ -448,7 +554,8 @@ export function defaultImageUxReviewGate(contract = {}, parts = {}) {
|
|
|
448
554
|
fix_loop_executed_or_not_needed: fixLoopExecutedOrNotNeeded,
|
|
449
555
|
changed_screens_rechecked: changedScreensRechecked,
|
|
450
556
|
image_voxel_reference_anchor_created: imageVoxelReferenceAnchorCreated,
|
|
451
|
-
image_voxel_relations_created: parts.imageVoxelRelationsCreated === true
|
|
557
|
+
image_voxel_relations_created: parts.imageVoxelRelationsCreated === true,
|
|
558
|
+
image_voxel_relation_evidence: parts.imageVoxelRelationEvidence || null,
|
|
452
559
|
wrongness_checked: wrongnessChecked,
|
|
453
560
|
honest_mode_complete: honestModeComplete,
|
|
454
561
|
required_artifacts: [
|
|
@@ -497,8 +604,18 @@ export function defaultImageUxReviewGate(contract = {}, parts = {}) {
|
|
|
497
604
|
return {
|
|
498
605
|
...gate,
|
|
499
606
|
passed: fullPassed || referenceCloseoutPassed,
|
|
500
|
-
status: fullPassed
|
|
501
|
-
|
|
607
|
+
status: fullPassed
|
|
608
|
+
? 'passed'
|
|
609
|
+
: referenceCloseoutPassed
|
|
610
|
+
? 'verified_partial_reference'
|
|
611
|
+
: reviewReportCompleted
|
|
612
|
+
? 'review_completed'
|
|
613
|
+
: 'blocked',
|
|
614
|
+
verified_level: fullPassed
|
|
615
|
+
? 'verified'
|
|
616
|
+
: referenceCloseoutPassed || reviewReportCompleted
|
|
617
|
+
? 'verified_partial'
|
|
618
|
+
: 'blocked',
|
|
502
619
|
full_review_passed: fullPassed,
|
|
503
620
|
reference_only: referenceCloseoutPassed
|
|
504
621
|
};
|
|
@@ -513,12 +630,14 @@ export async function writeImageUxReviewRouteArtifacts(dir, contract = {}, opts
|
|
|
513
630
|
const extractionReport = await readExistingJson(dir, IMAGE_UX_REVIEW_CALLOUT_EXTRACTION_REPORT_ARTIFACT);
|
|
514
631
|
const existingImagegenRequest = await readExistingJson(dir, IMAGE_UX_REVIEW_GPT_IMAGE_2_REQUEST_ARTIFACT);
|
|
515
632
|
const existingImagegenResponse = await readExistingJson(dir, IMAGE_UX_REVIEW_GPT_IMAGE_2_RESPONSE_ARTIFACT);
|
|
633
|
+
const existingRecapture = await readExistingJson(dir, IMAGE_UX_REVIEW_RECAPTURE_ARTIFACT);
|
|
516
634
|
const generatedReviewLedger = buildImageUxGeneratedReviewLedger(contract, inventory, existingGenerated, { root });
|
|
517
635
|
const sourceReferenceEvidence = await ensureImageUxSourceReferenceEvidence(root, dir, inventory);
|
|
636
|
+
const imageVoxelRelationEvidence = await ensureImageUxGeneratedRelationEvidence(root, dir, inventory, generatedReviewLedger, sourceReferenceEvidence);
|
|
518
637
|
const issueLedger = buildImageUxIssueLedger(contract, generatedReviewLedger, existingIssues);
|
|
519
638
|
const fixTaskPlan = planImageUxFixTasks(issueLedger);
|
|
520
639
|
const fixLoop = runImageUxFixLoop(issueLedger, fixTaskPlan, opts.fixLoop || {});
|
|
521
|
-
const recapturePlan =
|
|
640
|
+
const recapturePlan = selectImageUxRecapturePlan(fixLoop, opts, existingRecapture);
|
|
522
641
|
const iterationReport = buildImageUxIterationReport(contract, policy, generatedReviewLedger, issueLedger, fixTaskPlan, fixLoop, recapturePlan);
|
|
523
642
|
const outputSchema = await detectCodexExecResumeOutputSchema().catch((err) => ({ ok: true, status: 'integration_optional', warnings: [err.message] }));
|
|
524
643
|
const honestModeEvidence = await ensureImageUxHonestModeEvidence(dir, {
|
|
@@ -540,7 +659,8 @@ export async function writeImageUxReviewRouteArtifacts(dir, contract = {}, opts
|
|
|
540
659
|
recapturePlan,
|
|
541
660
|
iterationReport,
|
|
542
661
|
imageVoxelReferenceAnchorCreated: sourceReferenceEvidence.ok === true,
|
|
543
|
-
imageVoxelRelationsCreated: opts.imageVoxelRelationsCreated === true,
|
|
662
|
+
imageVoxelRelationsCreated: opts.imageVoxelRelationsCreated === true || imageVoxelRelationEvidence.ok === true,
|
|
663
|
+
imageVoxelRelationEvidence,
|
|
544
664
|
wrongnessChecked: opts.wrongnessChecked === true,
|
|
545
665
|
honestModeComplete: honestModeEvidence.ok === true,
|
|
546
666
|
honestModeEvidenceRequired: true,
|
|
@@ -567,6 +687,7 @@ export async function writeImageUxReviewRouteArtifacts(dir, contract = {}, opts
|
|
|
567
687
|
policy,
|
|
568
688
|
inventory,
|
|
569
689
|
imagegen_request: imagegenRequest,
|
|
690
|
+
gpt_image_2_request: imagegenRequest,
|
|
570
691
|
imagegen_response: imagegenResponse,
|
|
571
692
|
generated_review_ledger: generatedReviewLedger,
|
|
572
693
|
issue_ledger: issueLedger,
|
|
@@ -580,6 +701,12 @@ export async function writeImageUxReviewRouteArtifacts(dir, contract = {}, opts
|
|
|
580
701
|
gate
|
|
581
702
|
};
|
|
582
703
|
}
|
|
704
|
+
export function selectImageUxRecapturePlan(fixLoop = {}, opts = {}, existing = null) {
|
|
705
|
+
if (opts.preserveExistingRecapture === true && existing?.schema === 'sks.image-ux-recapture-plan.v2') {
|
|
706
|
+
return existing;
|
|
707
|
+
}
|
|
708
|
+
return buildRecapturePlan(fixLoop, opts.recapture || {});
|
|
709
|
+
}
|
|
583
710
|
export function imageUxReviewProofEvidence(gate = {}, artifacts = {}) {
|
|
584
711
|
const issueLedger = artifacts.issue_ledger || {};
|
|
585
712
|
const generated = artifacts.generated_review_ledger || {};
|
|
@@ -599,7 +726,11 @@ export function imageUxReviewProofEvidence(gate = {}, artifacts = {}) {
|
|
|
599
726
|
open_p0_p1_count: issueLedger.blocking_issue_count || 0,
|
|
600
727
|
fixed_p0_p1_count: (issueLedger.issues || []).filter((issue) => ['P0', 'P1'].includes(issue.severity) && issue.status === 'fixed').length,
|
|
601
728
|
recapture_re_review_status: artifacts.recapture_plan?.changed_screens_rechecked_or_not_applicable ? 'complete_or_not_applicable' : 'blocked',
|
|
602
|
-
image_voxel_relation_count: referenceOnly
|
|
729
|
+
image_voxel_relation_count: referenceOnly
|
|
730
|
+
? 0
|
|
731
|
+
: gate.image_voxel_relations_created === true
|
|
732
|
+
? Number(gate.image_voxel_relation_evidence?.relations || generated.covered_source_screen_count || realGeneratedImages.length)
|
|
733
|
+
: 0,
|
|
603
734
|
computer_use_evidence_mode: artifacts.inventory?.source_screens?.some((screen) => screen.capture_source === 'codex_native_computer_use_screenshot' || screen.capture_source === 'codex_computer_use_screenshot') ? 'native_computer_use_source_screenshot' : artifacts.inventory?.source_screens?.some((screen) => screen.capture_source === 'codex_chrome_extension_screenshot') ? 'chrome_extension_source_screenshot' : 'user_or_static_screenshot',
|
|
604
735
|
claims: {
|
|
605
736
|
ux_review_source_screenshot_verified: artifacts.inventory?.passed === true,
|
|
@@ -609,7 +740,7 @@ export function imageUxReviewProofEvidence(gate = {}, artifacts = {}) {
|
|
|
609
740
|
ux_review_changed_screens_rechecked: artifacts.recapture_plan?.changed_screens_rechecked_or_not_applicable === true,
|
|
610
741
|
ux_review_image_voxel_relations_verified: referenceOnly
|
|
611
742
|
? gate.source_reference_evidence?.ok === true
|
|
612
|
-
:
|
|
743
|
+
: gate.image_voxel_relations_created === true
|
|
613
744
|
},
|
|
614
745
|
full_verification_blockers: gate.full_verification_blockers || gate.blockers || [],
|
|
615
746
|
blockers: gate.blockers || []
|
|
@@ -668,6 +799,77 @@ async function ensureImageUxSourceReferenceEvidence(root, dir, inventory = {}) {
|
|
|
668
799
|
issues
|
|
669
800
|
};
|
|
670
801
|
}
|
|
802
|
+
async function ensureImageUxGeneratedRelationEvidence(root, dir, inventory = {}, generatedReviewLedger = {}, sourceReferenceEvidence = {}) {
|
|
803
|
+
const missionId = path.basename(String(dir));
|
|
804
|
+
const sourceScreens = (inventory.source_screens || []).filter((screen) => screen.status === 'captured' && screen.source);
|
|
805
|
+
const generatedImages = (generatedReviewLedger.generated_review_images || [])
|
|
806
|
+
.filter((image) => image.real_generated === true && image.mock !== true && image.path);
|
|
807
|
+
if (!missionId || !sourceScreens.length || !generatedImages.length || sourceReferenceEvidence.ok !== true) {
|
|
808
|
+
return {
|
|
809
|
+
schema: 'sks.image-ux-generated-relation-evidence.v1',
|
|
810
|
+
ok: false,
|
|
811
|
+
reason: !missionId
|
|
812
|
+
? 'mission_id_missing'
|
|
813
|
+
: !sourceScreens.length
|
|
814
|
+
? 'captured_source_screenshot_missing'
|
|
815
|
+
: !generatedImages.length
|
|
816
|
+
? 'real_generated_image_missing'
|
|
817
|
+
: 'source_reference_anchor_missing',
|
|
818
|
+
relations: 0,
|
|
819
|
+
issues: []
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
const anchorIds = Array.isArray(sourceReferenceEvidence.anchor_ids) ? sourceReferenceEvidence.anchor_ids : [];
|
|
823
|
+
const relationIds = [];
|
|
824
|
+
const issues = [];
|
|
825
|
+
let expectedRelations = 0;
|
|
826
|
+
for (const image of generatedImages) {
|
|
827
|
+
const generatedImageId = `${missionId}-${image.id}`;
|
|
828
|
+
try {
|
|
829
|
+
const ingested = await ingestImage(root, image.path, {
|
|
830
|
+
missionId,
|
|
831
|
+
source: 'image-ux-review:gpt-image-2-generated-callout',
|
|
832
|
+
id: generatedImageId
|
|
833
|
+
});
|
|
834
|
+
if (!ingested.ok) {
|
|
835
|
+
issues.push(...(ingested.validation?.issues || [`generated_image_ingest_failed:${image.id}`]));
|
|
836
|
+
continue;
|
|
837
|
+
}
|
|
838
|
+
const coveredScreens = sourceScreens.filter((screen) => generatedImageCoversScreen(image, screen.id));
|
|
839
|
+
expectedRelations += coveredScreens.length;
|
|
840
|
+
for (const sourceScreen of coveredScreens) {
|
|
841
|
+
const sourceImageId = `${missionId}-${sourceScreen.id}-source`;
|
|
842
|
+
const relation = await addImageRelation(root, {
|
|
843
|
+
missionId,
|
|
844
|
+
route: '$Image-UX-Review',
|
|
845
|
+
type: 'generated_callout_review_of',
|
|
846
|
+
beforeImageId: sourceImageId,
|
|
847
|
+
afterImageId: generatedImageId,
|
|
848
|
+
sourceImageId,
|
|
849
|
+
generatedImageId,
|
|
850
|
+
anchors: anchorIds.filter((anchorId) => String(anchorId).startsWith(`${sourceImageId}-`)),
|
|
851
|
+
status: 'verified_partial',
|
|
852
|
+
verification: 'gpt-image-2-generated-callout-bound-to-source'
|
|
853
|
+
});
|
|
854
|
+
if (relation.ok)
|
|
855
|
+
relationIds.push(`${sourceImageId}->${generatedImageId}`);
|
|
856
|
+
else
|
|
857
|
+
issues.push(...(relation.validation?.issues || [`generated_image_relation_failed:${image.id}:${sourceScreen.id}`]));
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
catch (err) {
|
|
861
|
+
issues.push(err instanceof Error ? err.message : String(err));
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
return {
|
|
865
|
+
schema: 'sks.image-ux-generated-relation-evidence.v1',
|
|
866
|
+
ok: expectedRelations > 0 && relationIds.length === expectedRelations && issues.length === 0,
|
|
867
|
+
relations: relationIds.length,
|
|
868
|
+
relation_ids: relationIds,
|
|
869
|
+
anchor_ids: anchorIds,
|
|
870
|
+
issues
|
|
871
|
+
};
|
|
872
|
+
}
|
|
671
873
|
async function ensureImageUxHonestModeEvidence(dir, parts = {}, opts = {}) {
|
|
672
874
|
const missionId = path.basename(String(dir));
|
|
673
875
|
const existing = await readExistingJson(dir, IMAGE_UX_REVIEW_HONEST_MODE_ARTIFACT);
|
|
@@ -744,6 +946,10 @@ export async function buildImageUxCalloutExtractionReport(root, extraction = {},
|
|
|
744
946
|
schema: 'sks.image-ux-callout-extraction-report.v1',
|
|
745
947
|
created_at: nowIso(),
|
|
746
948
|
provider: extraction.provider || opts.provider || 'unknown',
|
|
949
|
+
analysis_input: 'generated_annotated_review_image',
|
|
950
|
+
generated_image_is_primary_evidence: true,
|
|
951
|
+
source_screenshot_used_for_extraction: false,
|
|
952
|
+
generated_image_id: opts.generatedImageId || ledger.generated_image_id || issues[0]?.generated_review_image_id || null,
|
|
747
953
|
generated_image_path: generatedPath,
|
|
748
954
|
generated_image_sha256: generatedSha,
|
|
749
955
|
source_screenshot_sha256: sourceSha,
|
|
@@ -819,6 +1025,10 @@ function generatedReviewImageMissingBlocker() {
|
|
|
819
1025
|
}
|
|
820
1026
|
function normalizeGeneratedReviewImage(image = {}, screen = {}, opts = {}) {
|
|
821
1027
|
const sourceScreenId = image.source_screen_id || screen.id || 'screen-1';
|
|
1028
|
+
const sourceScreenIds = [...new Set([
|
|
1029
|
+
...(Array.isArray(image.source_screen_ids) ? image.source_screen_ids : []),
|
|
1030
|
+
sourceScreenId
|
|
1031
|
+
].map((id) => String(id || '').trim()).filter(Boolean))];
|
|
822
1032
|
const fileEvidence = generatedImageFileEvidence(opts.root, image);
|
|
823
1033
|
const sha256 = fileEvidence.sha256 || image.sha256 || null;
|
|
824
1034
|
const width = Number(image.width || 0) > 0 ? Number(image.width) : null;
|
|
@@ -832,6 +1042,7 @@ function normalizeGeneratedReviewImage(image = {}, screen = {}, opts = {}) {
|
|
|
832
1042
|
...image,
|
|
833
1043
|
id: image.id || compactId('generated-review', `${sourceScreenId}:${image.path || nowIso()}`),
|
|
834
1044
|
source_screen_id: sourceScreenId,
|
|
1045
|
+
source_screen_ids: sourceScreenIds,
|
|
835
1046
|
path: image.path || null,
|
|
836
1047
|
sha256,
|
|
837
1048
|
width,
|
|
@@ -863,6 +1074,13 @@ function normalizeGeneratedReviewImage(image = {}, screen = {}, opts = {}) {
|
|
|
863
1074
|
image_voxel_relation: image.image_voxel_relation || (image.id && sourceScreenId ? 'generated_callout_review_of' : null)
|
|
864
1075
|
};
|
|
865
1076
|
}
|
|
1077
|
+
function generatedImageCoversScreen(image = {}, screenId) {
|
|
1078
|
+
const id = String(screenId || '');
|
|
1079
|
+
if (!id)
|
|
1080
|
+
return false;
|
|
1081
|
+
return image.source_screen_id === id
|
|
1082
|
+
|| (Array.isArray(image.source_screen_ids) && image.source_screen_ids.includes(id));
|
|
1083
|
+
}
|
|
866
1084
|
function generatedImageEvidenceBlockers(image = {}, evidence = {}) {
|
|
867
1085
|
if (image.real_generated !== true || image.mock === true || image.source === 'mock_fixture')
|
|
868
1086
|
return [];
|