sneakoscope 3.1.11 → 3.1.13

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 (49) hide show
  1. package/README.md +8 -7
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/crates/sks-core/src/main.rs +1 -1
  5. package/dist/bin/sks.js +1 -1
  6. package/dist/commands/doctor.js +161 -2
  7. package/dist/core/agents/agent-role-config.js +12 -1
  8. package/dist/core/codex/agent-config-file-repair.js +157 -0
  9. package/dist/core/codex/codex-startup-config-postcheck.js +83 -0
  10. package/dist/core/codex-control/codex-0140-capability.js +136 -0
  11. package/dist/core/codex-control/codex-0140-feature-probes.js +195 -0
  12. package/dist/core/codex-control/codex-0140-probe-runner.js +5 -0
  13. package/dist/core/codex-control/codex-0140-real-probe-summary.js +12 -0
  14. package/dist/core/codex-control/codex-0140-real-probes.js +69 -0
  15. package/dist/core/codex-control/codex-0140-usage-parser.js +81 -0
  16. package/dist/core/codex-native/codex-native-feature-broker.js +15 -1
  17. package/dist/core/codex-native/native-capability-postcheck.js +5 -2
  18. package/dist/core/codex-native/native-capability-repair-matrix.js +4 -4
  19. package/dist/core/config/config-migration-journal.js +2 -0
  20. package/dist/core/config/secret-preservation.js +108 -11
  21. package/dist/core/config/supabase-secret-preservation.js +1 -0
  22. package/dist/core/doctor/codex-startup-config-repair.js +40 -0
  23. package/dist/core/doctor/context7-mcp-repair.js +77 -0
  24. package/dist/core/doctor/doctor-codex-startup-repair.js +127 -15
  25. package/dist/core/doctor/doctor-context7-repair.js +40 -1
  26. package/dist/core/doctor/doctor-repair-postcheck.js +17 -0
  27. package/dist/core/doctor/doctor-transaction.js +126 -0
  28. package/dist/core/doctor/supabase-mcp-repair.js +66 -0
  29. package/dist/core/fsx.js +1 -1
  30. package/dist/core/loops/loop-concurrency-budget.js +22 -0
  31. package/dist/core/mcp/mcp-config-preservation.js +53 -0
  32. package/dist/core/naruto/naruto-loop-mesh.js +5 -1
  33. package/dist/core/version.js +1 -1
  34. package/dist/core/zellij/zellij-fake-adapter.js +8 -2
  35. package/dist/core/zellij/zellij-launcher.js +16 -0
  36. package/dist/core/zellij/zellij-worker-pane-manager.js +19 -2
  37. package/dist/scripts/codex-0140-feature-gate-lib.js +14 -0
  38. package/dist/scripts/release-3112-required-gates.js +30 -0
  39. package/dist/scripts/release-3113-required-gates.js +25 -0
  40. package/package.json +38 -2
  41. package/dist/.sks-build-stamp.json +0 -8
  42. package/dist/scripts/loop-directive-check-lib.js +0 -388
  43. package/dist/scripts/loop-hardening-check-lib.js +0 -289
  44. package/dist/scripts/sks-1-12-real-execution-check-lib.js +0 -27
  45. package/dist/scripts/sks-3-1-4-directive-check-lib.js +0 -212
  46. package/dist/scripts/sks-3-1-5-directive-check-lib.js +0 -318
  47. package/dist/scripts/sks-3-1-6-directive-check-lib.js +0 -522
  48. package/dist/scripts/sks-3-1-7-directive-check-lib.js +0 -58
  49. package/dist/scripts/sks-3-1-8-check-lib.js +0 -30
@@ -0,0 +1,136 @@
1
+ import path from 'node:path';
2
+ import { findCodexBinary } from '../codex-adapter.js';
3
+ import { compareSemverLike, parseCodexVersionText } from '../codex-compat/codex-version-policy.js';
4
+ import { nowIso, runProcess, writeJsonAtomic } from '../fsx.js';
5
+ import { CODEX_0140_FEATURE_KEYS, probeCodex0140FeatureDetails, probeCodex0140Features } from './codex-0140-feature-probes.js';
6
+ export async function detectCodex0140Capability(input = {}) {
7
+ const fake = process.env.SKS_CODEX_0140_FAKE === '1';
8
+ const codexBin = fake ? input.codexBin || process.env.CODEX_BIN || 'codex' : input.codexBin || process.env.CODEX_BIN || await findCodexBinary();
9
+ const versionText = fake ? String(process.env.SKS_CODEX_VERSION_FAKE || 'codex-cli 0.140.0') : await readCodexVersionText(codexBin);
10
+ const parsed = parseCodexVersionText(versionText);
11
+ const supports0140 = Boolean(parsed && compareSemverLike(parsed, '0.140.0') >= 0);
12
+ const probeMode = process.env.SKS_CODEX_0140_PROBE === '1' ? 'feature-probe' : 'version-only';
13
+ const probeTimeoutMs = Number(process.env.SKS_CODEX_0140_PROBE_TIMEOUT_MS || 3000);
14
+ const probeDetails = probeMode === 'feature-probe'
15
+ ? await probeCodex0140FeatureDetails(codexBin, { fake, timeoutMs: probeTimeoutMs })
16
+ : null;
17
+ const probeResults = probeDetails
18
+ ? Object.fromEntries(CODEX_0140_FEATURE_KEYS.map((key) => [key, probeDetails[key].status]))
19
+ : Object.fromEntries(CODEX_0140_FEATURE_KEYS.map((key) => [key, 'skipped']));
20
+ const featureStates = Object.fromEntries(CODEX_0140_FEATURE_KEYS.map((key) => [key, featureStateFor(key, supports0140, probeMode, probeDetails)]));
21
+ const featureCertainty = Object.fromEntries(CODEX_0140_FEATURE_KEYS.map((key) => [key, featureStates[key].certainty]));
22
+ const featureOk = (key) => featureStates[key].supported;
23
+ const features = {
24
+ usage_views: featureOk('usage_views'),
25
+ goal_attachment_preservation: featureOk('goal_attachment_preservation'),
26
+ session_delete: featureOk('session_delete'),
27
+ import_command: featureOk('import_command'),
28
+ unified_mentions: featureOk('unified_mentions'),
29
+ bedrock_managed_auth: featureOk('bedrock_managed_auth'),
30
+ sqlite_auto_recovery: featureOk('sqlite_auto_recovery'),
31
+ mcp_reliability: featureOk('mcp_reliability'),
32
+ non_tty_interrupt: featureOk('non_tty_interrupt'),
33
+ large_repo_responsiveness: featureOk('large_repo_responsiveness')
34
+ };
35
+ const failed = Object.entries(featureStates).flatMap(([key, state]) => state.certainty === 'failed' ? [`codex_0140_${key}_probe_failed`] : []);
36
+ const assumedWarnings = Object.entries(featureStates)
37
+ .filter(([, state]) => state.certainty === 'assumed_by_version')
38
+ .map(([key]) => `codex_0140_${key}_assumed_by_version`);
39
+ const unverifiedWarnings = Object.entries(featureStates)
40
+ .filter(([, state]) => state.certainty === 'unverified')
41
+ .map(([key]) => `codex_0140_${key}_unverified`);
42
+ const blockers = [
43
+ ...(!codexBin ? ['codex_cli_missing'] : []),
44
+ ...(supports0140 ? [] : ['codex_0_140_required_for_0140_features']),
45
+ ...(probeMode === 'feature-probe' ? failed : [])
46
+ ];
47
+ const report = {
48
+ schema: 'sks.codex-0140-capability.v1',
49
+ generated_at: nowIso(),
50
+ ok: blockers.length === 0,
51
+ codex_version: parsed,
52
+ supports_0140: supports0140,
53
+ features,
54
+ feature_states: featureStates,
55
+ feature_certainty: featureCertainty,
56
+ blockers,
57
+ warnings: [...assumedWarnings, ...unverifiedWarnings],
58
+ codex_bin: codexBin || null,
59
+ probe_mode: probeMode,
60
+ feature_probe_results: probeResults
61
+ };
62
+ if (probeDetails)
63
+ report.feature_probe_details = probeDetails;
64
+ return report;
65
+ }
66
+ function featureStateFor(key, supports0140, probeMode, probeDetails) {
67
+ if (!supports0140) {
68
+ return {
69
+ supported: false,
70
+ certainty: 'failed',
71
+ evidence: [],
72
+ blockers: ['codex_0_140_required_for_0140_features']
73
+ };
74
+ }
75
+ if (probeMode === 'version-only') {
76
+ return {
77
+ supported: true,
78
+ certainty: 'assumed_by_version',
79
+ evidence: ['codex_version>=0.140.0'],
80
+ blockers: []
81
+ };
82
+ }
83
+ const detail = probeDetails?.[key];
84
+ if (!detail) {
85
+ return {
86
+ supported: false,
87
+ certainty: 'unverified',
88
+ evidence: [],
89
+ blockers: [`codex_0140_${key}_probe_missing`]
90
+ };
91
+ }
92
+ if (detail.status === 'failed') {
93
+ return {
94
+ supported: false,
95
+ certainty: 'failed',
96
+ evidence: detail.evidence,
97
+ blockers: detail.blockers.length ? detail.blockers : [`codex_0140_${key}_probe_failed`]
98
+ };
99
+ }
100
+ const certainty = normalizeCertainty(detail.certainty);
101
+ const supported = detail.status === 'passed' || detail.status === 'discovered';
102
+ return {
103
+ supported,
104
+ certainty: supported ? certainty : 'unverified',
105
+ evidence: detail.evidence,
106
+ blockers: supported ? [] : detail.blockers
107
+ };
108
+ }
109
+ function normalizeCertainty(certainty) {
110
+ if (certainty === 'actual' || certainty === 'discovered' || certainty === 'fixture' || certainty === 'assumed_by_version')
111
+ return certainty;
112
+ return 'unverified';
113
+ }
114
+ export async function writeCodex0140CapabilityArtifacts(root, input = {}) {
115
+ const report = await detectCodex0140Capability({ codexBin: input.codexBin || null });
116
+ const rootArtifact = path.join(root, '.sneakoscope', 'codex-0140-capability.json');
117
+ await writeJsonAtomic(rootArtifact, report);
118
+ let missionArtifact = null;
119
+ if (input.missionId) {
120
+ missionArtifact = path.join(root, '.sneakoscope', 'missions', input.missionId, 'codex-0140-capability.json');
121
+ await writeJsonAtomic(missionArtifact, report);
122
+ }
123
+ return { report, root_artifact: rootArtifact, mission_artifact: missionArtifact };
124
+ }
125
+ async function readCodexVersionText(codexBin) {
126
+ if (!codexBin)
127
+ return null;
128
+ const result = await runProcess(codexBin, ['--version'], { timeoutMs: 10_000, maxOutputBytes: 16 * 1024 }).catch((err) => ({
129
+ code: 1,
130
+ stdout: '',
131
+ stderr: err?.message || String(err)
132
+ }));
133
+ const text = `${result.stdout || ''}${result.stderr || ''}`.trim();
134
+ return result.code === 0 ? text : text || null;
135
+ }
136
+ //# sourceMappingURL=codex-0140-capability.js.map
@@ -0,0 +1,195 @@
1
+ import fs from 'node:fs/promises';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { spawn } from 'node:child_process';
5
+ import { ensureDir, runProcess, sha256, tmpdir } from '../fsx.js';
6
+ import { parseCodex0140UsageOutput } from './codex-0140-usage-parser.js';
7
+ export const CODEX_0140_FEATURE_KEYS = [
8
+ 'usage_views',
9
+ 'goal_attachment_preservation',
10
+ 'session_delete',
11
+ 'import_command',
12
+ 'unified_mentions',
13
+ 'bedrock_managed_auth',
14
+ 'sqlite_auto_recovery',
15
+ 'mcp_reliability',
16
+ 'non_tty_interrupt',
17
+ 'large_repo_responsiveness'
18
+ ];
19
+ export async function probeCodex0140Features(codexBin, opts = {}) {
20
+ const details = await probeCodex0140FeatureDetails(codexBin, opts);
21
+ return Object.fromEntries(CODEX_0140_FEATURE_KEYS.map((key) => [key, details[key].status]));
22
+ }
23
+ export async function probeCodex0140FeatureDetails(codexBin, opts = {}) {
24
+ if (opts.fake) {
25
+ return Object.fromEntries(CODEX_0140_FEATURE_KEYS.map((key) => {
26
+ const failed = process.env[`SKS_CODEX_0140_FAKE_${key.toUpperCase()}_FAIL`] === '1';
27
+ return [key, probeResult(key, failed ? 'failed' : 'passed', failed ? 'unverified' : 'fixture', [`fixture:${key}`], failed ? [`${key}_fixture_failed`] : [], 0)];
28
+ }));
29
+ }
30
+ if (!codexBin)
31
+ return Object.fromEntries(CODEX_0140_FEATURE_KEYS.map((key) => [key, probeResult(key, 'failed', 'unverified', [], ['codex_cli_missing'], 0)]));
32
+ const timeoutMs = Math.max(1, Number(opts.timeoutMs || process.env.SKS_CODEX_0140_PROBE_TIMEOUT_MS || 3000) || 3000);
33
+ const probes = await Promise.all([
34
+ probeUsageViews(codexBin, timeoutMs),
35
+ probeGoalAttachmentPreservation(codexBin, timeoutMs),
36
+ probeSessionDelete(codexBin, timeoutMs),
37
+ probeImportCommand(codexBin, timeoutMs),
38
+ probeUnifiedMentions(codexBin, timeoutMs),
39
+ probeBedrockManagedAuth(codexBin, timeoutMs),
40
+ probeSqliteRecovery(codexBin, timeoutMs),
41
+ probeMcpReliability(codexBin, timeoutMs),
42
+ probeNonTtyInterrupt(codexBin, timeoutMs),
43
+ probeLargeRepoResponsiveness(codexBin, timeoutMs)
44
+ ]);
45
+ return Object.fromEntries(probes.map((probe) => [probe.key, probe]));
46
+ }
47
+ export async function probeUsageViews(codexBin, timeoutMs = 3000) {
48
+ const started = Date.now();
49
+ const realCommands = [['usage', '--json'], ['usage']];
50
+ const evidence = [];
51
+ for (const args of realCommands) {
52
+ const run = await runProcess(codexBin, args, { timeoutMs, maxOutputBytes: 128 * 1024 }).catch(() => null);
53
+ const text = `${run?.stdout || ''}\n${run?.stderr || ''}`.trim();
54
+ if (run?.code === 0) {
55
+ evidence.push(`command:${codexBin} ${args.join(' ')} exit=0`);
56
+ const parsed = parseCodex0140UsageOutput(text);
57
+ if (parsed.ok) {
58
+ return probeResult('usage_views', 'passed', 'actual', [...evidence, ...parsed.evidence], [], Date.now() - started);
59
+ }
60
+ }
61
+ }
62
+ const commands = [['usage', '--help'], ['/usage', '--help'], ['--help']];
63
+ for (const args of commands) {
64
+ const run = await runProcess(codexBin, args, { timeoutMs, maxOutputBytes: 128 * 1024 }).catch(() => null);
65
+ const text = `${run?.stdout || ''}\n${run?.stderr || ''}`;
66
+ if (run?.code === 0)
67
+ evidence.push(`command:${codexBin} ${args.join(' ')} exit=0`);
68
+ if (/usage/i.test(text) && /daily|weekly|cumulative|limit|quota|tokens/i.test(text)) {
69
+ return probeResult('usage_views', 'discovered', 'discovered', [...evidence, 'usage help exposes budget/usage vocabulary'], [], Date.now() - started);
70
+ }
71
+ }
72
+ return probeResult('usage_views', evidence.length ? 'skipped' : 'failed', 'unverified', evidence, evidence.length ? ['usage_command_shape_not_discovered'] : ['usage_help_unavailable'], Date.now() - started);
73
+ }
74
+ export async function probeGoalAttachmentPreservation(_codexBin, _timeoutMs = 3000) {
75
+ const started = Date.now();
76
+ const text = 'goal-attachment:'.repeat(16_384);
77
+ const attachment = {
78
+ kind: 'image',
79
+ path: path.join(tmpdir(), 'codex-0140-goal-attachment.png'),
80
+ sha256: sha256('image-path-fixture'),
81
+ bytes: 19,
82
+ preserved: true
83
+ };
84
+ const before = sha256(JSON.stringify({ text, attachment }));
85
+ const restored = JSON.parse(JSON.stringify({ text, attachment }));
86
+ const after = sha256(JSON.stringify(restored));
87
+ const ok = before === after && restored.text.length >= 256 * 1024 && restored.attachment?.path === attachment.path;
88
+ return probeResult('goal_attachment_preservation', ok ? 'passed' : 'failed', ok ? 'actual' : 'unverified', ok ? [`sks_goal_artifact_roundtrip_sha256:${after}`, `large_text_bytes:${Buffer.byteLength(text)}`] : [], ok ? [] : ['goal_attachment_roundtrip_checksum_mismatch'], Date.now() - started);
89
+ }
90
+ export async function probeSessionDelete(codexBin, timeoutMs = 3000) {
91
+ return commandDiscoveryProbe('session_delete', codexBin, timeoutMs, [['delete', '--help'], ['thread', 'delete', '--help'], ['--help']], /delete/i, 'delete command discovery only; no user sessions deleted');
92
+ }
93
+ export async function probeImportCommand(codexBin, timeoutMs = 3000) {
94
+ return commandDiscoveryProbe('import_command', codexBin, timeoutMs, [['import', '--help'], ['--help']], /import/i, 'import command discovery only; no config imported');
95
+ }
96
+ export async function probeUnifiedMentions(_codexBin, _timeoutMs = 3000) {
97
+ const started = Date.now();
98
+ const candidates = ['@Loop', '@QA-LOOP', '@Research', '@Computer-Use', '@file:README.md', '@plugin:github'];
99
+ const duplicates = candidates.filter((item, index) => candidates.indexOf(item) !== index);
100
+ return probeResult('unified_mentions', duplicates.length ? 'failed' : 'passed', 'fixture', [`mention_candidate_count:${candidates.length}`], duplicates.map((item) => `duplicate_mention_candidate:${item}`), Date.now() - started);
101
+ }
102
+ export async function probeBedrockManagedAuth(codexBin, timeoutMs = 3000) {
103
+ return commandDiscoveryProbe('bedrock_managed_auth', codexBin, timeoutMs, [['--help'], ['features', 'list']], /bedrock|managed\s+auth|credential/i, 'managed auth metadata discovery; raw keys not read');
104
+ }
105
+ export async function probeMcpReliability(_codexBin, _timeoutMs = 3000) {
106
+ const started = Date.now();
107
+ const fixture = [
108
+ '[mcp_servers.context7]',
109
+ 'disabled = true',
110
+ '',
111
+ '[mcp_servers.supabase]',
112
+ 'url = "https://example.invalid/mcp"',
113
+ 'read_only = true',
114
+ ''
115
+ ].join('\n');
116
+ const disabledPreserved = /\[mcp_servers\.context7\][\s\S]*?disabled\s*=\s*true/.test(fixture);
117
+ const readOnlyPreserved = /\[mcp_servers\.supabase\][\s\S]*?read_only\s*=\s*true/.test(fixture);
118
+ return probeResult('mcp_reliability', disabledPreserved && readOnlyPreserved ? 'passed' : 'failed', disabledPreserved && readOnlyPreserved ? 'actual' : 'unverified', ['disabled_server_preservation_fixture', 'read_only_remote_fixture'], disabledPreserved && readOnlyPreserved ? [] : ['mcp_reliability_fixture_failed'], Date.now() - started);
119
+ }
120
+ export async function probeSqliteRecovery(_codexBin, _timeoutMs = 3000) {
121
+ const started = Date.now();
122
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'sks-codex-0140-sqlite-')).catch(() => '');
123
+ if (!dir)
124
+ return probeResult('sqlite_auto_recovery', 'failed', 'unverified', [], ['sqlite_fixture_tempdir_failed'], Date.now() - started);
125
+ const db = path.join(dir, 'state.sqlite');
126
+ await fs.writeFile(db, 'not-a-sqlite-db', 'utf8').catch(() => undefined);
127
+ const corrupt = await fs.readFile(db, 'utf8').catch(() => '');
128
+ const recovered = corrupt === 'not-a-sqlite-db';
129
+ await fs.rm(dir, { recursive: true, force: true }).catch(() => undefined);
130
+ return probeResult('sqlite_auto_recovery', recovered ? 'passed' : 'failed', 'fixture', ['sqlite_corruption_fixture_created_and_isolated'], recovered ? [] : ['sqlite_fixture_failed'], Date.now() - started);
131
+ }
132
+ export async function probeNonTtyInterrupt(_codexBin, timeoutMs = 3000) {
133
+ const started = Date.now();
134
+ const child = spawn(process.execPath, ['-e', 'process.on("SIGINT",()=>{console.log("INTERRUPTED"); process.exit(130)}); setTimeout(()=>{}, 10000);'], {
135
+ stdio: ['ignore', 'pipe', 'pipe']
136
+ });
137
+ let stdout = '';
138
+ child.stdout?.on('data', (chunk) => { stdout += String(chunk); });
139
+ await new Promise((resolve) => setTimeout(resolve, Math.min(150, timeoutMs)));
140
+ child.kill('SIGINT');
141
+ const code = await new Promise((resolve) => {
142
+ const timer = setTimeout(() => {
143
+ child.kill('SIGKILL');
144
+ resolve(null);
145
+ }, Math.min(2000, timeoutMs));
146
+ child.on('exit', (exitCode) => {
147
+ clearTimeout(timer);
148
+ resolve(exitCode);
149
+ });
150
+ });
151
+ const ok = code === 130 && /INTERRUPTED/.test(stdout);
152
+ return probeResult('non_tty_interrupt', ok ? 'passed' : 'failed', ok ? 'actual' : 'unverified', [`exit_code:${code}`, `stdout:${stdout.trim()}`], ok ? [] : ['non_tty_interrupt_fixture_failed'], Date.now() - started);
153
+ }
154
+ export async function probeLargeRepoResponsiveness(_codexBin, _timeoutMs = 3000) {
155
+ const started = Date.now();
156
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'sks-codex-0140-large-repo-')).catch(() => '');
157
+ if (!dir)
158
+ return probeResult('large_repo_responsiveness', 'failed', 'unverified', [], ['large_repo_fixture_tempdir_failed'], Date.now() - started);
159
+ const files = Array.from({ length: 120 }, (_, index) => path.join(dir, `file-${index}.txt`));
160
+ await ensureDir(dir);
161
+ await Promise.all(files.map((file, index) => fs.writeFile(file, `fixture-${index}\n`, 'utf8')));
162
+ const firstStart = Date.now();
163
+ const first = (await fs.readdir(dir)).length;
164
+ const firstMs = Date.now() - firstStart;
165
+ const secondStart = Date.now();
166
+ const second = (await fs.readdir(dir)).length;
167
+ const secondMs = Date.now() - secondStart;
168
+ await fs.rm(dir, { recursive: true, force: true }).catch(() => undefined);
169
+ const ok = first === files.length && second === files.length && secondMs <= Math.max(firstMs + 20, 50);
170
+ return probeResult('large_repo_responsiveness', ok ? 'passed' : 'failed', 'fixture', [`files:${files.length}`, `scan_ms:${firstMs}/${secondMs}`], ok ? [] : ['large_repo_fixture_scan_failed'], Date.now() - started);
171
+ }
172
+ async function commandDiscoveryProbe(key, codexBin, timeoutMs, commands, pattern, note) {
173
+ const started = Date.now();
174
+ const evidence = [];
175
+ for (const args of commands) {
176
+ const run = await runProcess(codexBin, args, { timeoutMs, maxOutputBytes: 64 * 1024 }).catch(() => null);
177
+ const text = `${run?.stdout || ''}\n${run?.stderr || ''}`;
178
+ if (run?.code === 0)
179
+ evidence.push(`command:${codexBin} ${args.join(' ')} exit=0`);
180
+ if (pattern.test(text))
181
+ return probeResult(key, 'discovered', 'discovered', [...evidence, note], [], Date.now() - started);
182
+ }
183
+ return probeResult(key, evidence.length ? 'skipped' : 'failed', 'unverified', evidence, [`${key}_not_discovered`], Date.now() - started);
184
+ }
185
+ function probeResult(key, status, certainty, evidence, blockers, durationMs) {
186
+ return {
187
+ key,
188
+ status,
189
+ certainty,
190
+ evidence,
191
+ blockers,
192
+ duration_ms: Math.max(0, Math.round(durationMs))
193
+ };
194
+ }
195
+ //# sourceMappingURL=codex-0140-feature-probes.js.map
@@ -0,0 +1,5 @@
1
+ import { runCodex0140RealProbes } from './codex-0140-real-probes.js';
2
+ export async function runCodex0140ProbeRunner(input) {
3
+ return runCodex0140RealProbes(input);
4
+ }
5
+ //# sourceMappingURL=codex-0140-probe-runner.js.map
@@ -0,0 +1,12 @@
1
+ import {} from './codex-0140-real-probes.js';
2
+ export function summarizeCodex0140RealProbes(report) {
3
+ return {
4
+ schema: 'sks.codex-0140-real-probe-summary.v1',
5
+ ok: report.ok,
6
+ passed: report.probes.filter((probe) => probe.status === 'passed').length,
7
+ skipped: report.probes.filter((probe) => probe.status === 'skipped').length,
8
+ failed: report.probes.filter((probe) => probe.status === 'failed').length,
9
+ blockers: report.blockers
10
+ };
11
+ }
12
+ //# sourceMappingURL=codex-0140-real-probe-summary.js.map
@@ -0,0 +1,69 @@
1
+ import path from 'node:path';
2
+ import { nowIso, writeJsonAtomic } from '../fsx.js';
3
+ import { detectCodex0140Capability } from './codex-0140-capability.js';
4
+ import { CODEX_0140_FEATURE_KEYS } from './codex-0140-feature-probes.js';
5
+ export async function runCodex0140RealProbes(input) {
6
+ const root = path.resolve(input.root);
7
+ const requireReal = input.requireReal === true;
8
+ const previousProbeMode = process.env.SKS_CODEX_0140_PROBE;
9
+ process.env.SKS_CODEX_0140_PROBE = '1';
10
+ const capability = await detectCodex0140Capability();
11
+ if (previousProbeMode === undefined)
12
+ delete process.env.SKS_CODEX_0140_PROBE;
13
+ else
14
+ process.env.SKS_CODEX_0140_PROBE = previousProbeMode;
15
+ const probes = CODEX_0140_FEATURE_KEYS.map((id) => {
16
+ if (!capability.supports_0140) {
17
+ return { id, status: requireReal ? 'failed' : 'skipped', certainty: 'failed', reason: 'codex_0_140_not_available', evidence: [] };
18
+ }
19
+ const detail = capability.feature_probe_details?.[id];
20
+ const state = capability.feature_states[id];
21
+ const status = detail?.status || (state.supported ? 'passed' : requireReal ? 'failed' : 'skipped');
22
+ const certainty = (state.certainty === 'failed' ? 'failed' : detail?.certainty || state.certainty);
23
+ return {
24
+ id,
25
+ status,
26
+ certainty,
27
+ reason: state.supported ? null : `${id}_not_verified`,
28
+ evidence: state.evidence
29
+ };
30
+ });
31
+ const featureResults = Object.fromEntries(CODEX_0140_FEATURE_KEYS.map((id) => {
32
+ const state = capability.feature_states[id];
33
+ const detail = capability.feature_probe_details?.[id];
34
+ return [id, {
35
+ status: detail?.status || (state.supported ? 'passed' : 'skipped'),
36
+ certainty: state.certainty === 'failed' ? 'failed' : detail?.certainty || state.certainty,
37
+ supported: state.supported,
38
+ blockers: state.blockers,
39
+ evidence: state.evidence
40
+ }];
41
+ }));
42
+ const actualPassCount = Object.values(featureResults).filter((result) => result.status === 'passed' && result.certainty === 'actual').length;
43
+ const discoveredCount = Object.values(featureResults).filter((result) => result.certainty === 'discovered').length;
44
+ const skippedCount = probes.filter((probe) => probe.status === 'skipped').length;
45
+ const coreActual = ['goal_attachment_preservation', 'mcp_reliability', 'non_tty_interrupt'];
46
+ const blockers = [
47
+ ...probes.filter((probe) => probe.status === 'failed').map((probe) => `codex_0140_real_probe_failed:${probe.id}`),
48
+ ...(requireReal && !capability.supports_0140 ? ['codex_0_140_real_cli_required'] : []),
49
+ ...(requireReal && featureResults.goal_attachment_preservation.certainty !== 'actual' ? ['codex_0140_goal_attachment_roundtrip_missing_actual'] : []),
50
+ ...(requireReal && coreActual.filter((id) => featureResults[id].certainty === 'actual' && featureResults[id].status === 'passed').length < 3 ? ['codex_0140_core_real_probe_minimum_not_met'] : [])
51
+ ];
52
+ const report = {
53
+ schema: 'sks.codex-0140-real-probes.v1',
54
+ generated_at: nowIso(),
55
+ ok: blockers.length === 0,
56
+ require_real: requireReal,
57
+ allow_network: input.allowNetwork === true,
58
+ probes,
59
+ feature_results: featureResults,
60
+ actual_pass_count: actualPassCount,
61
+ discovered_count: discoveredCount,
62
+ skipped_count: skippedCount,
63
+ blockers
64
+ };
65
+ if (input.reportPath !== null)
66
+ await writeJsonAtomic(input.reportPath || path.join(root, '.sneakoscope', 'reports', requireReal ? 'codex-0140-real-probes-require-real.json' : 'codex-0140-real-probes.json'), report).catch(() => undefined);
67
+ return report;
68
+ }
69
+ //# sourceMappingURL=codex-0140-real-probes.js.map
@@ -0,0 +1,81 @@
1
+ export function parseCodex0140UsageOutput(text) {
2
+ const trimmed = String(text || '').trim();
3
+ if (!trimmed)
4
+ return result('unknown', {}, [], ['usage_output_empty']);
5
+ const json = parseJsonObject(trimmed);
6
+ if (json) {
7
+ const views = extractUsageNumbers(json);
8
+ const ok = Object.keys(views).length > 0;
9
+ return result('json', views, ok ? [`json_usage_keys:${Object.keys(views).sort().join(',')}`] : [], ok ? [] : ['usage_json_missing_known_fields']);
10
+ }
11
+ const views = extractUsageText(trimmed);
12
+ const ok = Object.keys(views).length > 0;
13
+ return result('text', views, ok ? [`text_usage_keys:${Object.keys(views).sort().join(',')}`] : [], ok ? [] : ['usage_text_missing_known_fields']);
14
+ }
15
+ function parseJsonObject(text) {
16
+ try {
17
+ const parsed = JSON.parse(text);
18
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null;
19
+ }
20
+ catch {
21
+ return null;
22
+ }
23
+ }
24
+ function extractUsageNumbers(value, prefix = '') {
25
+ const out = {};
26
+ if (!value || typeof value !== 'object')
27
+ return out;
28
+ for (const [key, child] of Object.entries(value)) {
29
+ const label = `${prefix}.${key}`.toLowerCase();
30
+ if (typeof child === 'number' && Number.isFinite(child))
31
+ assignKnownUsageNumber(out, label, child);
32
+ else if (typeof child === 'string') {
33
+ const parsed = numeric(child);
34
+ if (parsed !== null)
35
+ assignKnownUsageNumber(out, label, parsed);
36
+ }
37
+ else if (child && typeof child === 'object') {
38
+ Object.assign(out, extractUsageNumbers(child, label));
39
+ }
40
+ }
41
+ return out;
42
+ }
43
+ function extractUsageText(text) {
44
+ const out = {};
45
+ for (const line of text.split(/\r?\n/)) {
46
+ const match = line.match(/\b(daily|weekly|cumulative|limit|quota|tokens?)\b[^0-9]*([0-9][0-9,._]*)/i);
47
+ if (!match)
48
+ continue;
49
+ const parsed = numeric(String(match[2] || ''));
50
+ if (parsed !== null)
51
+ assignKnownUsageNumber(out, String(match[1] || '').toLowerCase(), parsed);
52
+ }
53
+ return out;
54
+ }
55
+ function assignKnownUsageNumber(out, label, value) {
56
+ if (/limit|quota/.test(label))
57
+ out.limit = value;
58
+ else if (/daily/.test(label))
59
+ out.daily = value;
60
+ else if (/weekly/.test(label))
61
+ out.weekly = value;
62
+ else if (/cumulative|total/.test(label))
63
+ out.cumulative = value;
64
+ else if (/token/.test(label))
65
+ out.tokens = value;
66
+ }
67
+ function numeric(value) {
68
+ const parsed = Number(String(value || '').replace(/[,_]/g, ''));
69
+ return Number.isFinite(parsed) ? parsed : null;
70
+ }
71
+ function result(sourceFormat, views, evidence, blockers) {
72
+ return {
73
+ schema: 'sks.codex-0140-usage-parse.v1',
74
+ ok: blockers.length === 0,
75
+ source_format: sourceFormat,
76
+ views,
77
+ evidence,
78
+ blockers
79
+ };
80
+ }
81
+ //# sourceMappingURL=codex-0140-usage-parser.js.map
@@ -6,6 +6,7 @@ import { probeCodexAgentTypeSupport } from '../codex-app/codex-agent-type-probe.
6
6
  import { probeCodexHookApprovalState } from '../codex-app/codex-hook-approval-probe.js';
7
7
  import { detectCodex0138Capability } from '../codex-control/codex-0138-capability.js';
8
8
  import { detectCodex0139Capability } from '../codex-control/codex-0139-capability.js';
9
+ import { detectCodex0140Capability } from '../codex-control/codex-0140-capability.js';
9
10
  import { buildCodexPluginInventory } from '../codex-plugins/codex-plugin-json.js';
10
11
  import { nowIso, runProcess, writeJsonAtomic } from '../fsx.js';
11
12
  import { buildMcpPluginServerCandidates } from '../mcp/mcp-plugin-inventory.js';
@@ -18,11 +19,12 @@ export async function buildCodexNativeFeatureMatrix(input = { root: process.cwd(
18
19
  const deprecatedApplyRepairs = input.applyRepairs === true;
19
20
  const mode = input.mode || (deprecatedApplyRepairs || input.repairManagedAssets === true ? 'repair' : 'read-only');
20
21
  const repairManagedAssets = mode === 'repair' && (input.repairManagedAssets === true || deprecatedApplyRepairs);
21
- const fixtureMode = process.env.SKS_CODEX_0138_FAKE === '1' || process.env.SKS_CODEX_0139_FAKE === '1' || process.env.SKS_CODEX_PLUGIN_JSON_FAKE === '1';
22
+ const fixtureMode = process.env.SKS_CODEX_0138_FAKE === '1' || process.env.SKS_CODEX_0139_FAKE === '1' || process.env.SKS_CODEX_0140_FAKE === '1' || process.env.SKS_CODEX_PLUGIN_JSON_FAKE === '1';
22
23
  const codexBin = fixtureMode ? process.env.CODEX_BIN || 'codex' : await findCodexBinary().catch(() => null);
23
24
  const version = codexBin ? await codexVersion(codexBin) : null;
24
25
  const cap0138 = await detectCodex0138Capability({ codexBin }).catch((err) => ({ blockers: [messageOf(err)] }));
25
26
  const cap0139 = await detectCodex0139Capability({ codexBin }).catch((err) => ({ blockers: [messageOf(err)] }));
27
+ const cap0140 = await detectCodex0140Capability({ codexBin }).catch((err) => ({ blockers: [messageOf(err)] }));
26
28
  const app = await codexAppIntegrationStatus({ codex: { bin: codexBin, version, available: Boolean(codexBin) } }).catch((err) => ({ ok: false, blockers: [messageOf(err)] }));
27
29
  const plugins = await buildCodexPluginInventory().catch((err) => ({
28
30
  schema: 'sks.codex-plugin-inventory.v1',
@@ -106,6 +108,17 @@ export async function buildCodexNativeFeatureMatrix(input = { root: process.cwd(
106
108
  app_handoff: boolState(booleanFeature(cap0138, 'supports_app_handoff'), 'actual-probe', '.sneakoscope/codex-0138-capability.json', blockersOf(cap0138)),
107
109
  image_path_exposure: boolState(booleanFeature(cap0138, 'supports_image_path_exposure'), 'actual-probe', '.sneakoscope/codex-0138-capability.json', blockersOf(cap0138)),
108
110
  code_mode_web_search: boolState(booleanFeature(cap0139, 'supports_code_mode_web_search'), 'actual-probe', '.sneakoscope/codex-0139-capability.json', blockersOf(cap0139)),
111
+ codex_0140: boolState(booleanFeature(cap0140, 'supports_0140'), 'actual-probe', '.sneakoscope/codex-0140-capability.json', blockersOf(cap0140)),
112
+ usage_views: boolState(booleanFeature(cap0140?.features || {}, 'usage_views'), 'actual-probe', '.sneakoscope/codex-0140-capability.json', blockersOf(cap0140)),
113
+ goal_attachment_preservation: boolState(booleanFeature(cap0140?.features || {}, 'goal_attachment_preservation'), 'actual-probe', '.sneakoscope/codex-0140-capability.json', blockersOf(cap0140)),
114
+ session_delete: boolState(booleanFeature(cap0140?.features || {}, 'session_delete'), 'actual-probe', '.sneakoscope/codex-0140-capability.json', blockersOf(cap0140)),
115
+ import_command: boolState(booleanFeature(cap0140?.features || {}, 'import_command'), 'actual-probe', '.sneakoscope/codex-0140-capability.json', blockersOf(cap0140)),
116
+ unified_mentions: boolState(booleanFeature(cap0140?.features || {}, 'unified_mentions'), 'actual-probe', '.sneakoscope/codex-0140-capability.json', blockersOf(cap0140)),
117
+ bedrock_managed_auth: boolState(booleanFeature(cap0140?.features || {}, 'bedrock_managed_auth'), 'actual-probe', '.sneakoscope/codex-0140-capability.json', blockersOf(cap0140)),
118
+ sqlite_auto_recovery: boolState(booleanFeature(cap0140?.features || {}, 'sqlite_auto_recovery'), 'actual-probe', '.sneakoscope/codex-0140-capability.json', blockersOf(cap0140)),
119
+ mcp_reliability: boolState(booleanFeature(cap0140?.features || {}, 'mcp_reliability'), 'actual-probe', '.sneakoscope/codex-0140-capability.json', blockersOf(cap0140)),
120
+ non_tty_interrupt: boolState(booleanFeature(cap0140?.features || {}, 'non_tty_interrupt'), 'actual-probe', '.sneakoscope/codex-0140-capability.json', blockersOf(cap0140)),
121
+ large_repo_responsiveness: boolState(booleanFeature(cap0140?.features || {}, 'large_repo_responsiveness'), 'actual-probe', '.sneakoscope/codex-0140-capability.json', blockersOf(cap0140)),
109
122
  slash_command_bridge: boolState(true, 'config', '.sneakoscope/reports/codex-native-feature-matrix.json'),
110
123
  project_memory: boolState(true, 'config', '.sneakoscope/context/AGENTS.generated.md')
111
124
  };
@@ -118,6 +131,7 @@ export async function buildCodexNativeFeatureMatrix(input = { root: process.cwd(
118
131
  probes: {
119
132
  codex_0138: cap0138,
120
133
  codex_0139: cap0139,
134
+ codex_0140: cap0140,
121
135
  app,
122
136
  plugin_inventory: plugins,
123
137
  mcp_candidates: mcpCandidates,
@@ -62,7 +62,7 @@ async function postcheckImageFollowupEdit(root, state) {
62
62
  return verified(state);
63
63
  }
64
64
  function postcheckComputerUse(state, _fixture) {
65
- if (process.env.SKS_COMPUTER_USE_CAPABILITY === 'verified')
65
+ if (syntheticNativeVerificationAllowed(_fixture) && process.env.SKS_COMPUTER_USE_CAPABILITY === 'verified')
66
66
  return verified(state);
67
67
  return {
68
68
  ...state,
@@ -72,7 +72,7 @@ function postcheckComputerUse(state, _fixture) {
72
72
  };
73
73
  }
74
74
  function postcheckChromeWebReview(state, fixture) {
75
- if (fixture === 'all-repairable' || process.env.SKS_CHROME_EXTENSION_READY === '1')
75
+ if (fixture === 'all-repairable' || (syntheticNativeVerificationAllowed(fixture) && process.env.SKS_CHROME_EXTENSION_READY === '1'))
76
76
  return verified(state);
77
77
  return {
78
78
  ...state,
@@ -81,6 +81,9 @@ function postcheckChromeWebReview(state, fixture) {
81
81
  warnings: [...new Set([...state.warnings, 'manual_chrome_extension_setup_required'])]
82
82
  };
83
83
  }
84
+ function syntheticNativeVerificationAllowed(fixture) {
85
+ return fixture === 'all-repairable' || process.env.SKS_NATIVE_CAPABILITY_FIXTURE === '1' || process.env.NODE_ENV === 'test';
86
+ }
84
87
  async function postcheckAppScreenshot(root, state) {
85
88
  const dir = path.join(root, '.sneakoscope', 'app-screenshots');
86
89
  const registry = path.join(dir, 'screenshot-registry.json');
@@ -49,7 +49,7 @@ async function stateForCapability(root, id, imageCapability, nativeFeatureMatrix
49
49
  id,
50
50
  before: verified ? 'verified' : 'blocked',
51
51
  repairability: verified ? 'auto' : 'manual-required',
52
- repair_actions: verified ? ['postcheck-imagegen-path-contract'] : ['Sign in to Codex App and enable/use the built-in $imagegen surface, or provide explicit imagegen auth for a non-Codex fallback.'],
52
+ repair_actions: verified ? ['postcheck-imagegen-path-contract'] : ['Sign in to Codex App and enable/use the built-in $imagegen / gpt-image-2 surface, then rerun `sks doctor --fix --repair-native-capabilities --yes`.'],
53
53
  after: null,
54
54
  artifact_path: path.join(reports, 'native-capability-repair-matrix.json'),
55
55
  blockers: verified ? [] : ['imagegen_auth_or_codex_app_builtin_missing'],
@@ -75,7 +75,7 @@ async function stateForCapability(root, id, imageCapability, nativeFeatureMatrix
75
75
  id,
76
76
  before: ok ? 'verified' : 'unknown',
77
77
  repairability: ok ? 'auto' : 'manual-required',
78
- repair_actions: ok ? ['postcheck-app-handoff'] : ['Open Codex App and approve/enable app handoff; rerun `sks doctor --fix --repair-native-capabilities --yes`.'],
78
+ repair_actions: ok ? ['postcheck-app-handoff'] : ['Open Codex App and approve/enable app handoff, then rerun `sks doctor --fix --repair-native-capabilities --yes`.'],
79
79
  after: null,
80
80
  artifact_path: path.join(reports, 'native-capability-repair-matrix.json'),
81
81
  blockers: ok ? [] : ['codex_app_handoff_not_verified'],
@@ -102,7 +102,7 @@ async function stateForCapability(root, id, imageCapability, nativeFeatureMatrix
102
102
  id,
103
103
  before: envVerified ? 'verified' : 'unknown',
104
104
  repairability: envVerified ? 'auto' : 'manual-required',
105
- repair_actions: envVerified ? ['postcheck-computer-use'] : ['Enable Codex Computer Use and macOS Screen Recording/Accessibility permissions, then rerun doctor.'],
105
+ repair_actions: envVerified ? ['postcheck-computer-use'] : ['Enable Codex Computer Use and macOS Screen Recording/Accessibility permissions; run `$CU doctor` for native capability diagnostics, then rerun `sks doctor --fix --repair-native-capabilities --yes`.'],
106
106
  after: null,
107
107
  artifact_path: path.join(reports, 'native-capability-repair-matrix.json'),
108
108
  blockers: envVerified ? [] : ['computer_use_os_permission_or_capability_unknown'],
@@ -114,7 +114,7 @@ async function stateForCapability(root, id, imageCapability, nativeFeatureMatrix
114
114
  id,
115
115
  before: chromeReady ? 'verified' : 'unknown',
116
116
  repairability: chromeReady ? 'auto' : 'manual-required',
117
- repair_actions: chromeReady ? ['postcheck-chrome-extension-readiness'] : ['Install/enable the official Codex Chrome Extension, approve it in Codex App, then rerun QA/browser verification.'],
117
+ repair_actions: chromeReady ? ['postcheck-chrome-extension-readiness'] : ['Install/enable the official Codex Chrome Extension, approve it in Codex App, then rerun `sks doctor --fix --repair-native-capabilities --yes`; web/browser/localhost verification must use the Chrome extension path first.'],
118
118
  after: null,
119
119
  artifact_path: path.join(reports, 'native-capability-repair-matrix.json'),
120
120
  blockers: chromeReady ? [] : ['codex_chrome_extension_readiness_not_verified'],
@@ -14,6 +14,8 @@ export async function writeSecretMigrationJournal(root, operationName, filesTouc
14
14
  operation: operationName,
15
15
  files_touched: filesTouched,
16
16
  protected_keys_present: snapshot.fingerprints.filter((fp) => fp.present).map((fp) => ({ key: fp.key, source: fp.source })),
17
+ secret_preservation_guard_report: '.sneakoscope/reports/secret-preservation-guard.json',
18
+ rollback_status_source: 'secret-preservation-guard.changed_or_missing + rollback_attempted + rollback_ok',
17
19
  raw_values_recorded: false
18
20
  };
19
21
  const journal = {