sneakoscope 1.0.4 → 1.0.6
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 +50 -1
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/bin/sks.js +1 -1
- package/dist/build-manifest.json +11 -1
- package/dist/cli/install-helpers.d.ts +60 -0
- package/dist/cli/install-helpers.js +129 -15
- package/dist/commands/codex-lb.js +97 -8
- package/dist/commands/wiki.d.ts +1 -1
- package/dist/core/codex-compat/codex-compat-report.d.ts +39 -0
- package/dist/core/codex-compat/codex-compat-report.js +16 -3
- package/dist/core/codex-compat/codex-hook-issues.d.ts +20 -0
- package/dist/core/codex-compat/codex-hook-issues.js +93 -0
- package/dist/core/codex-compat/codex-hook-output-builders.d.ts +45 -0
- package/dist/core/codex-compat/codex-hook-output-builders.js +122 -0
- package/dist/core/codex-compat/codex-hook-output-normalizer.d.ts +1 -1
- package/dist/core/codex-compat/codex-hook-output-normalizer.js +25 -48
- package/dist/core/codex-compat/codex-hook-schema.d.ts +9 -1
- package/dist/core/codex-compat/codex-hook-schema.js +18 -2
- package/dist/core/codex-compat/codex-hook-semantic-validator.d.ts +23 -0
- package/dist/core/codex-compat/codex-hook-semantic-validator.js +221 -0
- package/dist/core/codex-compat/codex-hook-warning-detector.d.ts +7 -0
- package/dist/core/codex-compat/codex-hook-warning-detector.js +50 -23
- package/dist/core/codex-compat/codex-schema-snapshot.d.ts +1 -0
- package/dist/core/codex-compat/codex-schema-snapshot.js +10 -2
- package/dist/core/codex-lb/codex-lb-env.d.ts +42 -0
- package/dist/core/codex-lb/codex-lb-env.js +150 -0
- package/dist/core/codex-lb/codex-lb-setup.d.ts +46 -0
- package/dist/core/codex-lb/codex-lb-setup.js +112 -0
- package/dist/core/commands/computer-use-command.js +22 -1
- package/dist/core/commands/wiki-command.d.ts +2 -2
- package/dist/core/computer-use-status.d.ts +24 -0
- package/dist/core/computer-use-status.js +46 -0
- package/dist/core/fsx.d.ts +1 -1
- package/dist/core/fsx.js +1 -1
- package/dist/core/hooks-runtime.js +17 -48
- package/dist/core/proof/evidence-collector.d.ts +1 -1
- package/dist/core/proof/route-finalizer.js +18 -1
- package/dist/core/triwiki-wrongness/wrongness-cli.d.ts +2 -2
- package/dist/core/triwiki-wrongness/wrongness-ledger.js +6 -5
- package/dist/core/triwiki-wrongness/wrongness-proof-linker.d.ts +1 -1
- package/dist/core/triwiki-wrongness/wrongness-retrieval.d.ts +1 -1
- package/dist/core/triwiki-wrongness/wrongness-schema.d.ts +1 -1
- package/dist/core/triwiki-wrongness/wrongness-schema.js +27 -1
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/vendor/openai-codex/rust-v0.131.0/hooks/snapshot-metadata.json +2 -0
- package/package.json +9 -3
package/README.md
CHANGED
|
@@ -4,7 +4,11 @@ Fast legacy-free proof-first Codex trust layer with image-based Voxel TriWiki.
|
|
|
4
4
|
|
|
5
5
|
Sneakoscope Codex (`sks`) is a Codex CLI/App harness that makes repeatable Codex work auditable.
|
|
6
6
|
|
|
7
|
-
SKS **1.0.
|
|
7
|
+
SKS **1.0.6** is the final precision polish for the Codex trust harness: hook compatibility is classified as upstream schema plus an SKS zero-warning strict subset, `sks codex-lb setup` previews and applies the exact choices the user selected, and Computer Use has an optional live smoke surface for macOS capability/evidence status.
|
|
8
|
+
|
|
9
|
+
SKS **1.0.5** sealed the prior trust harness: hook outputs were validated against both vendored OpenAI Codex CLI `rust-v0.131.0` schemas and runtime semantic parser rules, codex-lb setup survived macOS user-session launches through env-file/Keychain/launchctl-aware repair surfaces, and Computer Use became the preferred macOS visual evidence capability when available.
|
|
10
|
+
|
|
11
|
+
SKS **1.0.4** introduced the `rust-v0.131.0` schema snapshot, guided codex-lb setup path, and Computer Use/MAD-SKS separation that 1.0.5 now hardens into release gates.
|
|
8
12
|
|
|
9
13
|
SKS **1.0.3** adds git-collaboration hygiene for shared TriWiki memory: `sks git ...`, tracked shared shards under `.sneakoscope/wiki/**`, runtime-only ignores, shared wrongness publish/sync, and Codex App hook trust-state generation for current hook trust syntax.
|
|
10
14
|
|
|
@@ -19,6 +23,51 @@ SKS does not try to clone every other harness. It focuses on one thing: making C
|
|
|
19
23
|

|
|
20
24
|
|
|
21
25
|
|
|
26
|
+
## 1.0.6 Final Precision Polish
|
|
27
|
+
|
|
28
|
+
SKS validates Codex hooks against the OpenAI Codex `rust-v0.131.0` schema and enforces a stricter SKS zero-warning subset. Some fields may be accepted by upstream but are intentionally disallowed by SKS to avoid user-facing hook warnings and release drift. `sks hooks warning-check --json` now reports `schema_violation`, `upstream_semantic_unsupported`, `sks_zero_warning_disallowed`, `legacy_shape`, and `policy_disallowed` category counts.
|
|
29
|
+
|
|
30
|
+
`sks codex-lb setup` is now a two-phase plan/apply wizard. Every question maps to an actual action: provider selection, env file writing, Keychain storage, launchctl sync, shell profile snippets, and health checks.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
sks codex-lb setup --host lb.example.com --api-key-stdin --plan --json
|
|
34
|
+
sks codex-lb setup --host lb.example.com --api-key-stdin --yes --no-default-provider --no-env-file --json
|
|
35
|
+
npm run codex-lb:setup-truthfulness
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Computer Use live validation is optional and opt-in. On macOS, `SKS_TEST_REAL_COMPUTER_USE=1 sks computer-use smoke --real --json` attempts a non-destructive capability/evidence check. If Codex App or macOS denies the capability, SKS records a structured blocker and does not fabricate visual evidence.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
sks computer-use smoke --json
|
|
42
|
+
SKS_TEST_REAL_COMPUTER_USE=1 sks computer-use smoke --real --json
|
|
43
|
+
npm run computer-use:live-optional
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 1.0.5 Ultimate Harness Seal
|
|
47
|
+
|
|
48
|
+
SKS 1.0.5 treats Codex hook semantic compatibility as stricter than schema compatibility. `sks hooks warning-check --json` and `npm run hooks:semantic-check` fail if an output uses `permissionDecision:"ask"`, PreToolUse `allow` without `updatedInput`, Stop `continue:false`, `stopReason`, `suppressOutput`, snake_case keys, unknown fields, or legacy top-level hook decisions.
|
|
49
|
+
|
|
50
|
+
codex-lb setup now has a durable setup/repair path:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
sks codex-lb setup --host lb.example.com --api-key-stdin --yes --json
|
|
54
|
+
sks codex-lb status --json
|
|
55
|
+
sks codex-lb doctor --deep --json
|
|
56
|
+
npm run codex-lb:missing-env-regression
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The API key is written only to redacted status surfaces. The env file is `~/.codex/sks-codex-lb.env`, metadata is `~/.codex/sks-codex-lb.json`, and reports expose only a redacted presence state plus a fingerprint. Raw `CODEX_LB_API_KEY` missing-env errors are release failures.
|
|
60
|
+
|
|
61
|
+
Computer Use is a Codex App/macOS capability, not a MAD-SKS or DB permission. Visual routes use:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
sks computer-use status --json
|
|
65
|
+
sks computer-use require --route '$QA-LOOP' --json
|
|
66
|
+
npm run computer-use:visual-route-fixture
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
If Codex App or macOS blocks the capability, SKS records `external_capability_blocked`, `codex_app_missing`, `macos_permission_missing`, or the closest structured status and does not fabricate UI evidence.
|
|
70
|
+
|
|
22
71
|
## 1.0.4 Codex CLI Compatibility
|
|
23
72
|
|
|
24
73
|
SKS 1.0.4 targets OpenAI Codex CLI `rust-v0.131.0`. Hook outputs are validated against vendored upstream schemas, so SKS fails release checks if it emits deprecated hook shapes or unknown fields. `sks codex-lb setup` now guides users through domain/base URL and API key setup, stores secrets securely, and prevents raw `CODEX_LB_API_KEY` missing messages. On macOS, Computer Use is treated as a first-class Codex App visual evidence capability and is never blocked by MAD-SKS or a generic SKS safety policy.
|
|
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
|
|
|
4
4
|
fn main() {
|
|
5
5
|
let mut args = std::env::args().skip(1);
|
|
6
6
|
match args.next().as_deref() {
|
|
7
|
-
Some("--version") => println!("sks-rs 1.0.
|
|
7
|
+
Some("--version") => println!("sks-rs 1.0.6"),
|
|
8
8
|
Some("compact-info") => {
|
|
9
9
|
let mut input = String::new();
|
|
10
10
|
let _ = io::stdin().read_to_string(&mut input);
|
package/dist/bin/sks.js
CHANGED
package/dist/build-manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "sks.dist-build.v2",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"typescript": true,
|
|
5
5
|
"mjs_runtime_files": 0,
|
|
6
6
|
"files": [
|
|
@@ -188,10 +188,16 @@
|
|
|
188
188
|
"core/codex-compat/codex-compat-report.js",
|
|
189
189
|
"core/codex-compat/codex-config-policy.d.ts",
|
|
190
190
|
"core/codex-compat/codex-config-policy.js",
|
|
191
|
+
"core/codex-compat/codex-hook-issues.d.ts",
|
|
192
|
+
"core/codex-compat/codex-hook-issues.js",
|
|
193
|
+
"core/codex-compat/codex-hook-output-builders.d.ts",
|
|
194
|
+
"core/codex-compat/codex-hook-output-builders.js",
|
|
191
195
|
"core/codex-compat/codex-hook-output-normalizer.d.ts",
|
|
192
196
|
"core/codex-compat/codex-hook-output-normalizer.js",
|
|
193
197
|
"core/codex-compat/codex-hook-schema.d.ts",
|
|
194
198
|
"core/codex-compat/codex-hook-schema.js",
|
|
199
|
+
"core/codex-compat/codex-hook-semantic-validator.d.ts",
|
|
200
|
+
"core/codex-compat/codex-hook-semantic-validator.js",
|
|
195
201
|
"core/codex-compat/codex-hook-warning-detector.d.ts",
|
|
196
202
|
"core/codex-compat/codex-hook-warning-detector.js",
|
|
197
203
|
"core/codex-compat/codex-schema-snapshot.d.ts",
|
|
@@ -202,6 +208,10 @@
|
|
|
202
208
|
"core/codex-compat/codex-version.js",
|
|
203
209
|
"core/codex-lb-circuit.d.ts",
|
|
204
210
|
"core/codex-lb-circuit.js",
|
|
211
|
+
"core/codex-lb/codex-lb-env.d.ts",
|
|
212
|
+
"core/codex-lb/codex-lb-env.js",
|
|
213
|
+
"core/codex-lb/codex-lb-setup.d.ts",
|
|
214
|
+
"core/codex-lb/codex-lb-setup.js",
|
|
205
215
|
"core/codex-model-guard.d.ts",
|
|
206
216
|
"core/codex-model-guard.js",
|
|
207
217
|
"core/commands/autoresearch-command.d.ts",
|
|
@@ -56,10 +56,16 @@ export type CodexLbAuthInstallResult = {
|
|
|
56
56
|
export type ConfigureCodexLbResult = {
|
|
57
57
|
ok?: boolean;
|
|
58
58
|
status: string;
|
|
59
|
+
plan?: Record<string, unknown>;
|
|
60
|
+
applied_actions?: Array<Record<string, unknown>>;
|
|
61
|
+
drift?: string[];
|
|
59
62
|
config_path?: string;
|
|
60
63
|
env_path?: string;
|
|
64
|
+
metadata_path?: string;
|
|
61
65
|
base_url?: string;
|
|
62
66
|
env_key?: string;
|
|
67
|
+
keychain?: Record<string, unknown>;
|
|
68
|
+
warnings?: string[];
|
|
63
69
|
auth_reconcile?: CodexLbAuthReconcileResult;
|
|
64
70
|
codex_lb?: CodexLbStatusSnapshot;
|
|
65
71
|
codex_environment?: CodexLbEnvSyncResult;
|
|
@@ -97,6 +103,24 @@ export declare function codexLbStatus(opts?: any): Promise<{
|
|
|
97
103
|
env_file: boolean;
|
|
98
104
|
env_key_configured: boolean;
|
|
99
105
|
env_base_url_configured: boolean;
|
|
106
|
+
env_loader: {
|
|
107
|
+
configured: boolean;
|
|
108
|
+
missing: string[];
|
|
109
|
+
source: import("../core/codex-lb/codex-lb-env.js").CodexLbEnvSource;
|
|
110
|
+
source_priority: import("../core/codex-lb/codex-lb-env.js").CodexLbEnvSource[];
|
|
111
|
+
api_key: {
|
|
112
|
+
present: boolean;
|
|
113
|
+
source: import("../core/codex-lb/codex-lb-env.js").CodexLbEnvSource | null;
|
|
114
|
+
redacted: true;
|
|
115
|
+
fingerprint: string | null;
|
|
116
|
+
};
|
|
117
|
+
keychain: {
|
|
118
|
+
checked: boolean;
|
|
119
|
+
available: boolean;
|
|
120
|
+
status: string;
|
|
121
|
+
};
|
|
122
|
+
env_paths: string[];
|
|
123
|
+
};
|
|
100
124
|
base_url: string | null;
|
|
101
125
|
auth_path: any;
|
|
102
126
|
auth_mode: string;
|
|
@@ -185,6 +209,24 @@ export declare function maybePromptCodexLbSetupForLaunch(args?: any, opts?: any)
|
|
|
185
209
|
env_file: boolean;
|
|
186
210
|
env_key_configured: boolean;
|
|
187
211
|
env_base_url_configured: boolean;
|
|
212
|
+
env_loader: {
|
|
213
|
+
configured: boolean;
|
|
214
|
+
missing: string[];
|
|
215
|
+
source: import("../core/codex-lb/codex-lb-env.js").CodexLbEnvSource;
|
|
216
|
+
source_priority: import("../core/codex-lb/codex-lb-env.js").CodexLbEnvSource[];
|
|
217
|
+
api_key: {
|
|
218
|
+
present: boolean;
|
|
219
|
+
source: import("../core/codex-lb/codex-lb-env.js").CodexLbEnvSource | null;
|
|
220
|
+
redacted: true;
|
|
221
|
+
fingerprint: string | null;
|
|
222
|
+
};
|
|
223
|
+
keychain: {
|
|
224
|
+
checked: boolean;
|
|
225
|
+
available: boolean;
|
|
226
|
+
status: string;
|
|
227
|
+
};
|
|
228
|
+
env_paths: string[];
|
|
229
|
+
};
|
|
188
230
|
base_url: string | null;
|
|
189
231
|
auth_path: any;
|
|
190
232
|
auth_mode: string;
|
|
@@ -205,6 +247,24 @@ export declare function maybePromptCodexLbSetupForLaunch(args?: any, opts?: any)
|
|
|
205
247
|
env_file: boolean;
|
|
206
248
|
env_key_configured: boolean;
|
|
207
249
|
env_base_url_configured: boolean;
|
|
250
|
+
env_loader: {
|
|
251
|
+
configured: boolean;
|
|
252
|
+
missing: string[];
|
|
253
|
+
source: import("../core/codex-lb/codex-lb-env.js").CodexLbEnvSource;
|
|
254
|
+
source_priority: import("../core/codex-lb/codex-lb-env.js").CodexLbEnvSource[];
|
|
255
|
+
api_key: {
|
|
256
|
+
present: boolean;
|
|
257
|
+
source: import("../core/codex-lb/codex-lb-env.js").CodexLbEnvSource | null;
|
|
258
|
+
redacted: true;
|
|
259
|
+
fingerprint: string | null;
|
|
260
|
+
};
|
|
261
|
+
keychain: {
|
|
262
|
+
checked: boolean;
|
|
263
|
+
available: boolean;
|
|
264
|
+
status: string;
|
|
265
|
+
};
|
|
266
|
+
env_paths: string[];
|
|
267
|
+
};
|
|
208
268
|
base_url: string | null;
|
|
209
269
|
auth_path: any;
|
|
210
270
|
auth_mode: string;
|
|
@@ -11,6 +11,8 @@ import { context7ConfigToml, DOLLAR_SKILL_NAMES, GETDESIGN_REFERENCE, hasContext
|
|
|
11
11
|
import { codexLaunchCommand, platformTmuxInstallHint, tmuxReadiness, tmuxReadinessCatchFallback } from '../core/tmux-ui.js';
|
|
12
12
|
import { reconcileCodexAppUpgradeProcesses } from '../core/codex-app.js';
|
|
13
13
|
import { recordCodexLbHealthEvent } from '../core/codex-lb-circuit.js';
|
|
14
|
+
import { loadCodexLbEnv, writeCodexLbKeychain, codexLbMetadataPath } from '../core/codex-lb/codex-lb-env.js';
|
|
15
|
+
import { buildCodexLbSetupPlan, installCodexLbShellProfileSnippet } from '../core/codex-lb/codex-lb-setup.js';
|
|
14
16
|
const DEFAULT_CODEX_APP_PLUGINS = [
|
|
15
17
|
['browser', 'openai-bundled'],
|
|
16
18
|
['chrome', 'openai-bundled'],
|
|
@@ -293,7 +295,7 @@ async function restorePostinstallCodexLbConfigSnapshot(snapshot) {
|
|
|
293
295
|
export function normalizeCodexLbBaseUrl(input = '') {
|
|
294
296
|
let host = String(input || '').trim();
|
|
295
297
|
if (!host)
|
|
296
|
-
|
|
298
|
+
return '';
|
|
297
299
|
if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(host))
|
|
298
300
|
host = `https://${host}`;
|
|
299
301
|
host = host.replace(/\/+$/, '');
|
|
@@ -303,29 +305,107 @@ export async function configureCodexLb(opts = {}) {
|
|
|
303
305
|
const home = opts.home || process.env.HOME || os.homedir();
|
|
304
306
|
const configPath = opts.configPath || codexLbConfigPath(home);
|
|
305
307
|
const envPath = opts.envPath || codexLbEnvPath(home);
|
|
306
|
-
const
|
|
308
|
+
const rawHost = String(opts.host || opts.baseUrl || '');
|
|
309
|
+
const baseUrl = normalizeCodexLbBaseUrl(rawHost);
|
|
307
310
|
const apiKey = String(opts.apiKey || '').trim();
|
|
311
|
+
const useDefaultProvider = opts.useDefaultProvider !== false;
|
|
312
|
+
const writeEnvFile = opts.writeEnvFile !== false;
|
|
313
|
+
const storeKeychain = opts.storeKeychain === true || opts.keychain === true;
|
|
314
|
+
const syncLaunchctl = opts.syncLaunchctl !== false && opts.syncLaunchEnv !== false;
|
|
315
|
+
const shellProfile = opts.shellProfile || 'skip';
|
|
316
|
+
const setupAnswers = {
|
|
317
|
+
host_or_base_url: rawHost,
|
|
318
|
+
api_key_source: opts.apiKeySource || 'cli_option',
|
|
319
|
+
use_as_default_provider: useDefaultProvider,
|
|
320
|
+
write_env_file: writeEnvFile,
|
|
321
|
+
store_keychain: storeKeychain,
|
|
322
|
+
sync_launchctl: syncLaunchctl,
|
|
323
|
+
install_shell_profile: shellProfile,
|
|
324
|
+
run_health_check: opts.runHealth === true,
|
|
325
|
+
allow_insecure_localhost: opts.allowInsecureHttp === true || opts.allowInsecureLocalhost === true
|
|
326
|
+
};
|
|
327
|
+
const plan = buildCodexLbSetupPlan(setupAnswers, {
|
|
328
|
+
home,
|
|
329
|
+
configPath,
|
|
330
|
+
envPath,
|
|
331
|
+
metadataPath: opts.metadataPath || codexLbMetadataPath(home)
|
|
332
|
+
});
|
|
333
|
+
if (!baseUrl)
|
|
334
|
+
return { ok: false, status: 'missing_host_or_base_url', config_path: configPath, env_path: envPath };
|
|
335
|
+
if (plan.blockers.length)
|
|
336
|
+
return { ok: false, status: 'plan_blocked', plan: plan, drift: plan.blockers, config_path: configPath, env_path: envPath };
|
|
337
|
+
if (/[\u0000-\u001f\u007f\s]/.test(rawHost.trim()))
|
|
338
|
+
return { ok: false, status: 'invalid_host_or_base_url', config_path: configPath, env_path: envPath, error: 'host_or_base_url_contains_whitespace_or_control_character' };
|
|
308
339
|
if (!apiKey)
|
|
309
340
|
return { ok: false, status: 'missing_api_key', config_path: configPath, env_path: envPath };
|
|
341
|
+
const insecureLocalWarning = /^http:\/\//i.test(baseUrl) && !/^http:\/\/(?:localhost|127\.0\.0\.1|\[::1\])(?::|\/|$)/i.test(baseUrl) && !opts.allowInsecureHttp
|
|
342
|
+
? ['codex-lb base URL uses http outside localhost; prefer https or pass an explicit allow flag in the calling surface.']
|
|
343
|
+
: [];
|
|
344
|
+
const appliedActions = [];
|
|
310
345
|
await ensureDir(path.dirname(configPath));
|
|
311
346
|
const current = await readText(configPath, '');
|
|
312
|
-
const next = normalizeCodexFastModeUiConfig(upsertCodexLbConfig(current, baseUrl));
|
|
347
|
+
const next = normalizeCodexFastModeUiConfig(upsertCodexLbConfig(current, baseUrl, useDefaultProvider));
|
|
313
348
|
await writeTextAtomic(configPath, next);
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
349
|
+
appliedActions.push({ type: 'write_config_provider', target: configPath, ok: true });
|
|
350
|
+
if (useDefaultProvider)
|
|
351
|
+
appliedActions.push({ type: 'select_default_provider', target: configPath, ok: true });
|
|
352
|
+
if (writeEnvFile) {
|
|
353
|
+
await writeTextAtomic(envPath, `export CODEX_LB_BASE_URL=${shellSingleQuote(baseUrl)}\nexport CODEX_LB_API_KEY=${shellSingleQuote(apiKey)}\n`);
|
|
354
|
+
await fsp.chmod(envPath, 0o600).catch(() => { });
|
|
355
|
+
appliedActions.push({ type: 'write_env_file', target: envPath, ok: true });
|
|
356
|
+
}
|
|
357
|
+
process.env.CODEX_LB_BASE_URL = baseUrl;
|
|
358
|
+
process.env.CODEX_LB_API_KEY = apiKey;
|
|
359
|
+
const keyFingerprint = await sha256Text(apiKey);
|
|
360
|
+
const metadataPath = opts.metadataPath || codexLbMetadataPath(home);
|
|
361
|
+
await writeTextAtomic(metadataPath, `${JSON.stringify({
|
|
362
|
+
schema: 'sks.codex-lb-metadata.v1',
|
|
363
|
+
base_url: baseUrl,
|
|
364
|
+
updated_at: new Date().toISOString(),
|
|
365
|
+
source: opts.source || 'setup',
|
|
366
|
+
api_key: { redacted: true, sha256: keyFingerprint }
|
|
367
|
+
}, null, 2)}\n`);
|
|
368
|
+
await fsp.chmod(metadataPath, 0o600).catch(() => { });
|
|
369
|
+
appliedActions.push({ type: 'write_metadata', target: metadataPath, ok: true });
|
|
370
|
+
const keychain = storeKeychain ? await writeCodexLbKeychain(apiKey, opts).catch((err) => ({ ok: false, status: 'keychain_store_failed', error: err.message })) : { ok: false, status: 'skipped' };
|
|
371
|
+
if (storeKeychain)
|
|
372
|
+
appliedActions.push({ type: 'store_keychain', target: 'macOS Keychain service sks-codex-lb', ok: keychain.ok === true, status: keychain.status });
|
|
373
|
+
const codexEnvironment = await syncCodexLbProviderEnvironment({ env_path: envPath, base_url: baseUrl }, { ...opts, home, apiKey, baseUrl, syncLaunchEnv: syncLaunchctl });
|
|
374
|
+
if (syncLaunchctl)
|
|
375
|
+
appliedActions.push({ type: 'sync_launchctl', target: 'macOS launchctl user environment', ok: codexEnvironment.ok === true, status: codexEnvironment.status });
|
|
376
|
+
const shellProfileResult = await installCodexLbShellProfileSnippet({ home, envPath, shellProfile }).catch((err) => ({ ok: false, status: 'failed', files: [], error: err.message }));
|
|
377
|
+
if (shellProfile !== 'skip')
|
|
378
|
+
appliedActions.push({ type: 'install_shell_profile_snippet', target: shellProfileResult.files?.join(', ') || shellProfile, ok: shellProfileResult.ok === true, status: shellProfileResult.status });
|
|
317
379
|
const codexLogin = await maybeSyncCodexLbSharedLogin(apiKey, { ...opts, home, force: true });
|
|
318
380
|
const codexLb = await codexLbStatus({ ...opts, home, configPath, envPath });
|
|
319
381
|
const authReconcile = await reconcileCodexLbAuthConflict({ ...opts, home, status: codexLb }).catch((err) => ({ status: 'failed', reason: 'exception', error: err.message }));
|
|
320
382
|
const finalCodexLb = await codexLbStatus({ ...opts, home, configPath, envPath });
|
|
321
383
|
const ok = Boolean(codexEnvironment.ok && codexLogin.ok);
|
|
384
|
+
const drift = detectCodexLbSetupDrift({
|
|
385
|
+
useDefaultProvider,
|
|
386
|
+
writeEnvFile,
|
|
387
|
+
storeKeychain,
|
|
388
|
+
syncLaunchctl,
|
|
389
|
+
shellProfile,
|
|
390
|
+
selected: finalCodexLb.selected,
|
|
391
|
+
envFile: finalCodexLb.env_file,
|
|
392
|
+
keychain,
|
|
393
|
+
codexEnvironment,
|
|
394
|
+
shellProfileResult
|
|
395
|
+
});
|
|
322
396
|
return {
|
|
323
|
-
ok,
|
|
324
|
-
status: ok ? 'configured' : (codexEnvironment.status || codexLogin.status),
|
|
397
|
+
ok: ok && drift.length === 0,
|
|
398
|
+
status: ok && drift.length === 0 ? 'configured' : drift.length ? 'setup_choice_drift' : (codexEnvironment.status || codexLogin.status),
|
|
399
|
+
plan: plan,
|
|
400
|
+
applied_actions: appliedActions,
|
|
401
|
+
drift,
|
|
325
402
|
config_path: configPath,
|
|
326
403
|
env_path: envPath,
|
|
404
|
+
metadata_path: metadataPath,
|
|
327
405
|
base_url: baseUrl,
|
|
328
406
|
env_key: 'CODEX_LB_API_KEY',
|
|
407
|
+
keychain,
|
|
408
|
+
warnings: insecureLocalWarning,
|
|
329
409
|
auth_reconcile: authReconcile,
|
|
330
410
|
codex_lb: finalCodexLb,
|
|
331
411
|
codex_environment: codexEnvironment,
|
|
@@ -340,13 +420,14 @@ export async function codexLbStatus(opts = {}) {
|
|
|
340
420
|
const config = await readText(configPath, '');
|
|
341
421
|
const envExists = await exists(envPath);
|
|
342
422
|
const envText = envExists ? await readText(envPath, '') : '';
|
|
423
|
+
const envLoad = await loadCodexLbEnv({ ...opts, home, envPath });
|
|
343
424
|
const authPath = opts.authPath || codexAuthPath(home);
|
|
344
425
|
const authText = await readText(authPath, '');
|
|
345
426
|
const authMode = codexAuthModeSummary(authText);
|
|
346
|
-
const envKeyConfigured = Boolean(
|
|
427
|
+
const envKeyConfigured = Boolean(envLoad.api_key.present);
|
|
347
428
|
const providerConfigured = /\[model_providers\.codex-lb\]/.test(config);
|
|
348
429
|
const selected = hasTopLevelCodexLbSelected(config);
|
|
349
|
-
const baseUrl = codexLbProviderBaseUrl(config) ||
|
|
430
|
+
const baseUrl = codexLbProviderBaseUrl(config) || envLoad.base_url || null;
|
|
350
431
|
const providerRequiresOpenAiAuth = codexLbProviderRequiresOpenAiAuth(config);
|
|
351
432
|
return {
|
|
352
433
|
ok: providerConfigured && envKeyConfigured && Boolean(baseUrl) && providerRequiresOpenAiAuth,
|
|
@@ -357,7 +438,16 @@ export async function codexLbStatus(opts = {}) {
|
|
|
357
438
|
selected,
|
|
358
439
|
env_file: envExists,
|
|
359
440
|
env_key_configured: envKeyConfigured,
|
|
360
|
-
env_base_url_configured: Boolean(
|
|
441
|
+
env_base_url_configured: Boolean(envLoad.base_url),
|
|
442
|
+
env_loader: {
|
|
443
|
+
configured: envLoad.configured,
|
|
444
|
+
missing: envLoad.missing,
|
|
445
|
+
source: envLoad.source,
|
|
446
|
+
source_priority: envLoad.source_priority,
|
|
447
|
+
api_key: envLoad.api_key,
|
|
448
|
+
keychain: envLoad.keychain,
|
|
449
|
+
env_paths: envLoad.env_paths
|
|
450
|
+
},
|
|
361
451
|
base_url: baseUrl,
|
|
362
452
|
auth_path: authPath,
|
|
363
453
|
auth_mode: authMode.mode,
|
|
@@ -1155,10 +1245,10 @@ async function syncCodexLbProviderEnvironment(status = {}, opts = {}) {
|
|
|
1155
1245
|
const home = opts.home || process.env.HOME || os.homedir();
|
|
1156
1246
|
const envPath = opts.envPath || status.env_path || codexLbEnvPath(home);
|
|
1157
1247
|
const envText = await readText(envPath, '');
|
|
1158
|
-
const apiKey = parseCodexLbEnvKey(envText);
|
|
1248
|
+
const apiKey = String(opts.apiKey || '').trim() || parseCodexLbEnvKey(envText);
|
|
1159
1249
|
if (!apiKey)
|
|
1160
1250
|
return { ok: false, status: 'missing_env_key' };
|
|
1161
|
-
const baseUrl = status.base_url || parseCodexLbEnvBaseUrl(envText);
|
|
1251
|
+
const baseUrl = status.base_url || opts.baseUrl || parseCodexLbEnvBaseUrl(envText);
|
|
1162
1252
|
process.env.CODEX_LB_API_KEY = apiKey;
|
|
1163
1253
|
if (baseUrl)
|
|
1164
1254
|
process.env.CODEX_LB_BASE_URL = baseUrl;
|
|
@@ -1225,8 +1315,10 @@ async function syncCodexApiKeyLogin(apiKey, opts = {}) {
|
|
|
1225
1315
|
return { ok: true, status: 'synced' };
|
|
1226
1316
|
return { ok: false, status: 'login_failed', error: redactSecretText(login.stderr || login.stdout || 'codex login failed', [apiKey]).trim() };
|
|
1227
1317
|
}
|
|
1228
|
-
function upsertCodexLbConfig(text = '', baseUrl) {
|
|
1229
|
-
let next =
|
|
1318
|
+
function upsertCodexLbConfig(text = '', baseUrl, selectDefault = true) {
|
|
1319
|
+
let next = selectDefault
|
|
1320
|
+
? upsertTopLevelTomlString(text, 'model_provider', 'codex-lb')
|
|
1321
|
+
: removeTopLevelTomlKeyIfValue(text, 'model_provider', 'codex-lb');
|
|
1230
1322
|
const block = [
|
|
1231
1323
|
'[model_providers.codex-lb]',
|
|
1232
1324
|
'name = "OpenAI"',
|
|
@@ -1239,6 +1331,24 @@ function upsertCodexLbConfig(text = '', baseUrl) {
|
|
|
1239
1331
|
next = upsertTomlTable(next, 'model_providers.codex-lb', block);
|
|
1240
1332
|
return `${next.trim()}\n`;
|
|
1241
1333
|
}
|
|
1334
|
+
function detectCodexLbSetupDrift(state = {}) {
|
|
1335
|
+
const drift = [];
|
|
1336
|
+
if (state.useDefaultProvider && state.selected !== true)
|
|
1337
|
+
drift.push('default_provider_not_selected');
|
|
1338
|
+
if (!state.useDefaultProvider && state.selected === true)
|
|
1339
|
+
drift.push('default_provider_selected_despite_no_default_provider');
|
|
1340
|
+
if (state.writeEnvFile && state.envFile !== true)
|
|
1341
|
+
drift.push('env_file_not_written');
|
|
1342
|
+
if (!state.writeEnvFile && state.envFile === true)
|
|
1343
|
+
drift.push('env_file_written_despite_no_env_file');
|
|
1344
|
+
if (!state.storeKeychain && state.keychain?.status && state.keychain.status !== 'skipped')
|
|
1345
|
+
drift.push('keychain_touched_despite_no_keychain');
|
|
1346
|
+
if (!state.syncLaunchctl && state.codexEnvironment?.launch_environment?.status === 'synced')
|
|
1347
|
+
drift.push('launchctl_synced_despite_no_launchctl');
|
|
1348
|
+
if (state.shellProfile === 'skip' && state.shellProfileResult?.status === 'installed')
|
|
1349
|
+
drift.push('shell_profile_written_despite_skip');
|
|
1350
|
+
return drift;
|
|
1351
|
+
}
|
|
1242
1352
|
export async function ensureGlobalCodexFastModeDuringInstall(opts = {}) {
|
|
1243
1353
|
if (process.env.SKS_SKIP_CODEX_FAST_MODE_REPAIR === '1')
|
|
1244
1354
|
return { status: 'skipped', reason: 'SKS_SKIP_CODEX_FAST_MODE_REPAIR=1' };
|
|
@@ -1482,6 +1592,10 @@ function redactSecretText(text = '', secrets = []) {
|
|
|
1482
1592
|
}
|
|
1483
1593
|
return out;
|
|
1484
1594
|
}
|
|
1595
|
+
async function sha256Text(value = '') {
|
|
1596
|
+
const { createHash } = await import('node:crypto');
|
|
1597
|
+
return createHash('sha256').update(String(value || '')).digest('hex');
|
|
1598
|
+
}
|
|
1485
1599
|
function escapeRegExp(value) {
|
|
1486
1600
|
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
1487
1601
|
}
|
|
@@ -6,6 +6,7 @@ import { flag, readOption } from '../cli/args.js';
|
|
|
6
6
|
import { printJson } from '../cli/output.js';
|
|
7
7
|
import { codexLbMetrics, readCodexLbCircuit, recordCodexLbHealthEvent, resetCodexLbCircuit, codexLbProofEvidence } from '../core/codex-lb-circuit.js';
|
|
8
8
|
import { checkCodexLbResponseChain, codexLbStatus, configureCodexLb, formatCodexLbStatusText, releaseCodexLbAuthHold, repairCodexLbAuth, unselectCodexLbProvider } from '../cli/install-helpers.js';
|
|
9
|
+
import { buildCodexLbSetupPlan, renderCodexLbSetupPlan } from '../core/codex-lb/codex-lb-setup.js';
|
|
9
10
|
export async function run(command, args = []) {
|
|
10
11
|
const root = await projectRoot();
|
|
11
12
|
const action = args[0] || 'status';
|
|
@@ -39,7 +40,8 @@ export async function run(command, args = []) {
|
|
|
39
40
|
}
|
|
40
41
|
if (action === 'health' || action === 'verify-chain' || action === 'chain') {
|
|
41
42
|
const status = await codexLbStatus();
|
|
42
|
-
const
|
|
43
|
+
const blocker = !status.env_key_configured ? 'missing_env_key' : !status.base_url ? 'missing_base_url' : 'not_configured';
|
|
44
|
+
const result = status.ok ? await checkCodexLbResponseChain(status, { force: true, root }) : { ok: false, status: blocker, codex_lb: status };
|
|
43
45
|
if (flag(args, '--json'))
|
|
44
46
|
return printJson(result);
|
|
45
47
|
console.log(`codex-lb response chain: ${result.ok ? 'ok' : `failed (${result.status})`}`);
|
|
@@ -76,6 +78,17 @@ export async function run(command, args = []) {
|
|
|
76
78
|
}
|
|
77
79
|
if (action === 'setup' || action === 'reconfigure') {
|
|
78
80
|
const options = await codexLbSetupOptions(args);
|
|
81
|
+
const plan = buildCodexLbSetupPlan({
|
|
82
|
+
host_or_base_url: options.host || '',
|
|
83
|
+
api_key_source: options.apiKeySource,
|
|
84
|
+
use_as_default_provider: options.useDefaultProvider,
|
|
85
|
+
write_env_file: options.writeEnvFile,
|
|
86
|
+
store_keychain: options.keychain,
|
|
87
|
+
sync_launchctl: options.syncLaunchctl,
|
|
88
|
+
install_shell_profile: options.shellProfile,
|
|
89
|
+
run_health_check: options.health,
|
|
90
|
+
allow_insecure_localhost: options.allowInsecureLocalhost
|
|
91
|
+
});
|
|
79
92
|
if (!options.host || !options.apiKey) {
|
|
80
93
|
const result = {
|
|
81
94
|
schema: 'sks.codex-lb-setup.v1',
|
|
@@ -97,8 +110,43 @@ export async function run(command, args = []) {
|
|
|
97
110
|
process.exitCode = 1;
|
|
98
111
|
return;
|
|
99
112
|
}
|
|
100
|
-
|
|
113
|
+
if (flag(args, '--plan')) {
|
|
114
|
+
const result = { schema: 'sks.codex-lb-setup-plan-result.v1', ok: plan.blockers.length === 0, plan, writes: false };
|
|
115
|
+
if (flag(args, '--json'))
|
|
116
|
+
return printJson(result);
|
|
117
|
+
process.stdout.write(renderCodexLbSetupPlan(plan));
|
|
118
|
+
if (!result.ok)
|
|
119
|
+
process.exitCode = 1;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (options.interactive && !options.yes) {
|
|
123
|
+
process.stdout.write(renderCodexLbSetupPlan(plan));
|
|
124
|
+
const confirm = (await ask('Apply this codex-lb setup plan? [y/N] ')).trim();
|
|
125
|
+
if (!/^(y|yes|예|네|응)$/i.test(confirm)) {
|
|
126
|
+
const result = { schema: 'sks.codex-lb-setup.v1', ok: false, status: 'cancelled', plan, applied_actions: [] };
|
|
127
|
+
if (flag(args, '--json'))
|
|
128
|
+
return printJson(result);
|
|
129
|
+
console.log('codex-lb setup cancelled.');
|
|
130
|
+
process.exitCode = 1;
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const result = await configureCodexLb({
|
|
135
|
+
host: options.host,
|
|
136
|
+
apiKey: options.apiKey,
|
|
137
|
+
keychain: options.keychain,
|
|
138
|
+
storeKeychain: options.keychain,
|
|
139
|
+
useDefaultProvider: options.useDefaultProvider,
|
|
140
|
+
writeEnvFile: options.writeEnvFile,
|
|
141
|
+
syncLaunchctl: options.syncLaunchctl,
|
|
142
|
+
shellProfile: options.shellProfile,
|
|
143
|
+
runHealth: options.health,
|
|
144
|
+
apiKeySource: options.apiKeySource,
|
|
145
|
+
allowInsecureHttp: options.allowInsecureLocalhost
|
|
146
|
+
});
|
|
101
147
|
const shaped = { schema: 'sks.codex-lb-setup.v1', ...result, api_key: { present: Boolean(options.apiKey), redacted: true }, env_file_chmod: '0600' };
|
|
148
|
+
if (options.health)
|
|
149
|
+
shaped.applied_actions = [...(shaped.applied_actions || []), { type: 'run_health_check', target: 'codex-lb response chain', ok: true }];
|
|
102
150
|
if (options.health)
|
|
103
151
|
shaped.chain_health = result.ok ? await checkCodexLbResponseChain(result, { force: true, root }) : null;
|
|
104
152
|
if (flag(args, '--json'))
|
|
@@ -147,11 +195,13 @@ function shapeCodexLbStatus(status = {}) {
|
|
|
147
195
|
...status,
|
|
148
196
|
configured: Boolean(status.ok),
|
|
149
197
|
setup_needed: !status.ok,
|
|
198
|
+
repair_available: !status.ok,
|
|
150
199
|
api_key: {
|
|
151
200
|
present: Boolean(status.env_key_configured),
|
|
152
|
-
source: status.
|
|
201
|
+
source: status.env_loader?.api_key?.source || null,
|
|
153
202
|
redacted: true
|
|
154
203
|
},
|
|
204
|
+
env_loader: status.env_loader || null,
|
|
155
205
|
env_auto_load: Boolean(status.env_file && status.env_key_configured),
|
|
156
206
|
guidance: status.ok ? [] : [
|
|
157
207
|
'codex-lb API key is not configured.',
|
|
@@ -164,19 +214,58 @@ async function codexLbSetupOptions(args = []) {
|
|
|
164
214
|
const baseUrl = readOption(args, '--base-url', null);
|
|
165
215
|
let host = baseUrl || readOption(args, '--host', readOption(args, '--domain', null));
|
|
166
216
|
let apiKey = readOption(args, '--api-key', readOption(args, '--key', null));
|
|
217
|
+
let apiKeySource = apiKey ? 'cli_option' : 'hidden_prompt';
|
|
218
|
+
let keychain = flag(args, '--keychain');
|
|
167
219
|
if (flag(args, '--api-key-stdin'))
|
|
168
220
|
apiKey = (await readStdin()).trim();
|
|
169
|
-
|
|
221
|
+
if (flag(args, '--api-key-stdin'))
|
|
222
|
+
apiKeySource = 'stdin';
|
|
223
|
+
let health = (flag(args, '--health') || flag(args, '--check')) && !flag(args, '--no-health');
|
|
224
|
+
let useDefaultProvider = flag(args, '--no-default-provider') ? false : true;
|
|
225
|
+
if (flag(args, '--use-default-provider'))
|
|
226
|
+
useDefaultProvider = true;
|
|
227
|
+
let writeEnvFile = flag(args, '--no-env-file') ? false : true;
|
|
228
|
+
if (flag(args, '--write-env-file'))
|
|
229
|
+
writeEnvFile = true;
|
|
230
|
+
if (flag(args, '--no-keychain'))
|
|
231
|
+
keychain = false;
|
|
232
|
+
let syncLaunchctl = flag(args, '--no-launchctl') ? false : true;
|
|
233
|
+
if (flag(args, '--launchctl'))
|
|
234
|
+
syncLaunchctl = true;
|
|
235
|
+
const shellProfile = normalizeShellProfile(readOption(args, '--shell-profile', 'skip'));
|
|
236
|
+
const allowInsecureLocalhost = flag(args, '--allow-insecure-localhost') || flag(args, '--allow-insecure-http');
|
|
237
|
+
const interactive = (!host || !apiKey || canAskInteractive(args)) && canAskInteractive(args);
|
|
170
238
|
if ((!host || !apiKey) && canAskInteractive(args)) {
|
|
171
239
|
console.log('SKS codex-lb setup\n');
|
|
172
240
|
host ||= (await ask('1. codex-lb domain or base URL?\n Example: lb.example.com or https://lb.example.com/backend-api/codex\n> ')).trim();
|
|
173
241
|
apiKey ||= (await askHidden('2. API key?\n Input hidden. Value will be stored securely and never printed.\n> ')).trim();
|
|
174
|
-
|
|
175
|
-
await ask('
|
|
176
|
-
|
|
242
|
+
apiKeySource = 'hidden_prompt';
|
|
243
|
+
useDefaultProvider = parseYesNo(await ask('3. Use this codex-lb as default for Codex launches? [Y/n] '), true);
|
|
244
|
+
writeEnvFile = parseYesNo(await ask('4. Write shell env loader to ~/.codex/sks-codex-lb.env? [Y/n] '), true);
|
|
245
|
+
const storeKeychain = (await ask('5. Store the key in macOS Keychain when available? [Y/n] ')).trim();
|
|
246
|
+
keychain = !/^(n|no|아니|아니요|ㄴ)$/i.test(storeKeychain || 'y');
|
|
247
|
+
syncLaunchctl = parseYesNo(await ask('6. Sync macOS launchctl environment when available? [Y/n] '), true);
|
|
248
|
+
const profile = (await ask('7. Install shell profile snippet? [zsh/bash/fish/all/skip] ')).trim();
|
|
249
|
+
const interactiveShellProfile = normalizeShellProfile(profile || 'skip');
|
|
250
|
+
const runHealth = (await ask('8. Run health check now? [Y/n] ')).trim();
|
|
177
251
|
health = !/^(n|no|아니|아니요|ㄴ)$/i.test(runHealth || 'y');
|
|
252
|
+
return { host, apiKey, health, keychain, useDefaultProvider, writeEnvFile, syncLaunchctl, shellProfile: interactiveShellProfile, allowInsecureLocalhost, apiKeySource, interactive: true, yes: flag(args, '--yes') };
|
|
178
253
|
}
|
|
179
|
-
return { host, apiKey, health };
|
|
254
|
+
return { host, apiKey, health, keychain, useDefaultProvider, writeEnvFile, syncLaunchctl, shellProfile, allowInsecureLocalhost, apiKeySource, interactive, yes: flag(args, '--yes') };
|
|
255
|
+
}
|
|
256
|
+
function normalizeShellProfile(value) {
|
|
257
|
+
const raw = String(value || 'skip').toLowerCase();
|
|
258
|
+
return raw === 'zsh' || raw === 'bash' || raw === 'fish' || raw === 'all' ? raw : 'skip';
|
|
259
|
+
}
|
|
260
|
+
function parseYesNo(value, fallback) {
|
|
261
|
+
const raw = String(value || '').trim();
|
|
262
|
+
if (!raw)
|
|
263
|
+
return fallback;
|
|
264
|
+
if (/^(y|yes|예|네|응)$/i.test(raw))
|
|
265
|
+
return true;
|
|
266
|
+
if (/^(n|no|아니|아니요|ㄴ)$/i.test(raw))
|
|
267
|
+
return false;
|
|
268
|
+
return fallback;
|
|
180
269
|
}
|
|
181
270
|
function canAskInteractive(args = []) {
|
|
182
271
|
return !flag(args, '--json') && !flag(args, '--yes') && Boolean(input.isTTY && output.isTTY && process.env.CI !== 'true');
|
package/dist/commands/wiki.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export declare function run(_command: any, args?: any): Promise<void | {
|
|
|
15
15
|
};
|
|
16
16
|
active_records: {
|
|
17
17
|
id: string;
|
|
18
|
-
kind: "incorrect_claim" | "overconfident_claim" | "stale_evidence" | "missing_evidence" | "test_failure" | "route_misclassification" | "scout_error" | "visual_anchor_error" | "image_bbox_error" | "db_safety_false_positive" | "db_safety_false_negative" | "hook_policy_mismatch" | "codex_lb_health_misread" | "mock_real_confusion" | "user_intent_misread" | "artifact_schema_error" | "trust_status_overclaim";
|
|
18
|
+
kind: "incorrect_claim" | "overconfident_claim" | "stale_evidence" | "missing_evidence" | "test_failure" | "route_misclassification" | "scout_error" | "visual_anchor_error" | "image_bbox_error" | "db_safety_false_positive" | "db_safety_false_negative" | "hook_policy_mismatch" | "hook_semantic_mismatch" | "hook_strict_subset_misclassified" | "codex_lb_health_misread" | "codex_lb_missing_env_raw_message" | "codex_lb_setup_choice_drift" | "codex_lb_env_persistence_failure" | "computer_use_policy_misclassification" | "computer_use_live_smoke_mismatch" | "computer_use_external_block_overclaimed" | "mock_real_confusion" | "user_intent_misread" | "artifact_schema_error" | "trust_status_overclaim";
|
|
19
19
|
severity: "high" | "low" | "medium" | "critical";
|
|
20
20
|
route: string | null;
|
|
21
21
|
claim: string;
|