sneakoscope 0.7.54 → 0.7.55
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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sneakoscope",
|
|
3
3
|
"displayName": "ㅅㅋㅅ",
|
|
4
|
-
"version": "0.7.
|
|
4
|
+
"version": "0.7.55",
|
|
5
5
|
"description": "Sneakoscope Codex: database-safe Codex CLI/App harness with Team, Goal, AutoResearch, TriWiki, and Honest Mode.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { codexRemoteControlStatus, formatCodexRemoteControlStatus } from '../core/codex-app.mjs';
|
|
3
|
+
import { forceGpt55CodexConfigArgs } from '../core/codex-model-guard.mjs';
|
|
3
4
|
|
|
4
5
|
export async function codexAppRemoteControlCommand(args = [], opts = {}) {
|
|
5
6
|
const controlArgs = argsBeforeSeparator(args);
|
|
@@ -27,7 +28,7 @@ export async function codexAppRemoteControlCommand(args = [], opts = {}) {
|
|
|
27
28
|
return;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
const passthrough = stripSeparator(args);
|
|
31
|
+
const passthrough = forceGpt55CodexConfigArgs(stripSeparator(args));
|
|
31
32
|
const spawnFn = opts.spawn || spawn;
|
|
32
33
|
const code = await spawnInherited(spawnFn, status.codex_cli.bin, ['remote-control', ...passthrough], {
|
|
33
34
|
cwd: process.cwd(),
|
package/src/cli/main.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import readline from 'node:readline/promises';
|
|
|
5
5
|
import { stdin as input, stdout as output } from 'node:process';
|
|
6
6
|
import { projectRoot, readJson, writeJsonAtomic, writeTextAtomic, appendJsonlBounded, nowIso, exists, ensureDir, tmpdir, packageRoot, dirSize, formatBytes, which, runProcess, PACKAGE_VERSION, sksRoot, globalSksRoot, findProjectRoot, readStdin } from '../core/fsx.mjs';
|
|
7
7
|
import { initProject, installSkills, normalizeInstallScope, sksCommandPrefix } from '../core/init.mjs';
|
|
8
|
-
import { getCodexInfo, runCodexExec } from '../core/codex-adapter.mjs';
|
|
8
|
+
import { buildCodexExecArgs, getCodexInfo, runCodexExec } from '../core/codex-adapter.mjs';
|
|
9
9
|
import { createMission, loadMission, findLatestMission, missionDir, setCurrent, stateFile } from '../core/mission.mjs';
|
|
10
10
|
import { buildQuestionSchema, writeQuestions } from '../core/questions.mjs';
|
|
11
11
|
import { sealContract, validateAnswers } from '../core/decision-contract.mjs';
|
|
@@ -2277,6 +2277,10 @@ async function selftest() {
|
|
|
2277
2277
|
if (defaultFastHighPlan.codexArgs.join(' ') !== '--model gpt-5.5 -c model_reasoning_effort="high"') throw new Error('selftest failed: default sks tmux launch is not fast-high');
|
|
2278
2278
|
const forcedModelPlan = await buildTmuxLaunchPlan({ root: tmp, env: { SKS_CODEX_MODEL: 'gpt-5.4-mini', SKS_CODEX_FAST_HIGH: '0', SKS_CODEX_REASONING: 'medium' }, tmux: { ok: true, bin: 'tmux', version: '3.4' }, codex: { bin: 'codex', version: 'codex-cli 99.0.0' }, app: { ok: true } });
|
|
2279
2279
|
if (forcedModelPlan.codexArgs.includes('gpt-5.4-mini') || forcedModelPlan.codexArgs.join(' ') !== '--model gpt-5.5 -c model_reasoning_effort="medium"') throw new Error('selftest failed: sks tmux launch allowed a non-GPT-5.5 model override');
|
|
2280
|
+
const explicitBadModelPlan = await buildTmuxLaunchPlan({ root: tmp, codexArgs: ['--profile', 'legacy-5.4', '--model', 'gpt-5.4-mini', '-c', 'model="gpt-5.4"', '-c', 'model_reasoning_effort="low"'], tmux: { ok: true, bin: 'tmux', version: '3.4' }, codex: { bin: 'codex', version: 'codex-cli 99.0.0' }, app: { ok: true } });
|
|
2281
|
+
if (explicitBadModelPlan.codexArgs.join(' ').includes('gpt-5.4') || explicitBadModelPlan.codexArgs.join(' ') !== '--model gpt-5.5 --profile legacy-5.4 -c model_reasoning_effort="low"') throw new Error('selftest failed: explicit tmux model override was not forced back to GPT-5.5');
|
|
2282
|
+
const codexExecArgs = buildCodexExecArgs({ root: tmp, prompt: 'model guard selftest', profile: 'legacy-5.4', extraArgs: ['--model=gpt-5.4-mini', '--config', 'model = "gpt-5.4"', '-c', 'model_reasoning_effort="medium"'] });
|
|
2283
|
+
if (codexExecArgs.join(' ').includes('gpt-5.4') || !codexExecArgs.includes('gpt-5.5') || codexExecArgs.includes('--model=gpt-5.4-mini')) throw new Error('selftest failed: codex exec args allowed a non-GPT-5.5 model override');
|
|
2280
2284
|
const codexLbHome = path.join(tmp, 'codex-lb-home');
|
|
2281
2285
|
await ensureDir(path.join(codexLbHome, '.codex'));
|
|
2282
2286
|
const codexLbFakeBin = path.join(tmp, 'codex-lb-fake-bin');
|
|
@@ -2309,6 +2313,7 @@ async function selftest() {
|
|
|
2309
2313
|
if (!/^model = "gpt-5\.5"/m.test(codexLbConfig) || !codexLbConfig.includes('service_tier = "fast"') || !codexLbConfig.includes('hooks = true') || codexLbConfig.includes('codex_hooks = true') || !codexLbConfig.includes('fast_mode = true') || !codexLbConfig.includes('fast_mode_ui = true') || !codexLbConfig.includes('[user.fast_mode]') || !codexLbConfig.includes('visible = true') || !codexLbConfig.includes('enabled = true') || !codexLbConfig.includes('default_profile = "sks-fast-high"') || !/\[profiles\.sks-fast-high\][\s\S]*?service_tier = "fast"/.test(codexLbConfig) || codexLbConfig.includes('fast_default_opt_out = true') || hasTopLevelCodexModeLock(codexLbConfig)) throw new Error('selftest failed: codex-lb setup did not preserve Codex App Fast mode defaults, force GPT-5.5, or migrate the hooks feature flag');
|
|
2310
2314
|
const codexLbLaunch = codexLaunchCommand(tmp, 'codex', []);
|
|
2311
2315
|
if (!codexLbLaunch.includes('sks-codex-lb.env')) throw new Error('selftest failed: tmux launch command does not source codex-lb env file');
|
|
2316
|
+
if (!codexLbLaunch.includes("'--model' 'gpt-5.5'")) throw new Error('selftest failed: tmux launch command without args did not force GPT-5.5');
|
|
2312
2317
|
if (!codexLbLaunch.includes('SKS_TMUX_LOGO_ANIMATION') || !codexLbLaunch.includes('SNEAKOSCOPE CODEX')) throw new Error('selftest failed: tmux launch command does not include the animated SKS logo intro');
|
|
2313
2318
|
const madLaunchSource = await safeReadText(path.join(packageRoot(), 'src', 'cli', 'main.mjs'));
|
|
2314
2319
|
if (!madLaunchSource.includes('const lb = await maybePromptCodexLbSetupForLaunch(args)') || !madLaunchSource.includes("const launchLb = lb.status === 'present'") || !madLaunchSource.includes('codexLbImmediateLaunchOpts(cleanArgs, launchLb')) throw new Error('selftest failed: MAD launch does not sync codex-lb auth and fresh-session launch options');
|
|
@@ -2353,7 +2358,7 @@ async function selftest() {
|
|
|
2353
2358
|
if (!String(openClawAutoUpdate.stdout || '').includes('Codex CLI ready: 0.1.0 -> codex-cli 99.0.0')) throw new Error('selftest failed: OpenClaw mode did not auto-approve Codex CLI update before tmux launch');
|
|
2354
2359
|
const remoteControlBin = path.join(tmp, 'remote-control-bin');
|
|
2355
2360
|
await ensureDir(remoteControlBin);
|
|
2356
|
-
await writeTextAtomic(path.join(remoteControlBin, 'codex'), '#!/bin/sh\nif [ "$1" = "--version" ]; then echo "codex-cli 0.130.0"; exit 0; fi\nif [ "$1" = "remote-control" ]; then echo "remote-control $*"; exit 0; fi\necho "unexpected codex $*" >&2\nexit 2\n');
|
|
2361
|
+
await writeTextAtomic(path.join(remoteControlBin, 'codex'), '#!/bin/sh\nif [ "$1" = "--version" ]; then echo "codex-cli 0.130.0"; exit 0; fi\nif [ "$1" = "remote-control" ]; then shift; for arg in "$@"; do if [ "$arg" = "--model" ]; then echo "remote-control rejects --model" >&2; exit 64; fi; done; echo "remote-control $*"; exit 0; fi\necho "unexpected codex $*" >&2\nexit 2\n');
|
|
2357
2362
|
await fsp.chmod(path.join(remoteControlBin, 'codex'), 0o755);
|
|
2358
2363
|
const remoteControlStatus = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'codex-app', 'remote-control', '--dry-run', '--json'], {
|
|
2359
2364
|
cwd: globalCwd,
|
|
@@ -2364,6 +2369,14 @@ async function selftest() {
|
|
|
2364
2369
|
if (remoteControlStatus.code !== 0) throw new Error(`selftest failed: Codex remote-control status exited ${remoteControlStatus.code}: ${remoteControlStatus.stderr}`);
|
|
2365
2370
|
const remoteControlJson = JSON.parse(remoteControlStatus.stdout);
|
|
2366
2371
|
if (!remoteControlJson.ok || remoteControlJson.min_version !== '0.130.0' || !String(remoteControlJson.command || '').includes('remote-control')) throw new Error('selftest failed: Codex remote-control status did not report 0.130.0 readiness');
|
|
2372
|
+
const remoteControlLaunch = await runProcess(process.execPath, [path.join(packageRoot(), 'bin', 'sks.mjs'), 'codex-app', 'remote-control', '--', '--model', 'gpt-5.4-mini', '-c', 'model="gpt-5.4"', '--example'], {
|
|
2373
|
+
cwd: globalCwd,
|
|
2374
|
+
env: { SKS_GLOBAL_ROOT: globalRuntimeRoot, PATH: remoteControlBin },
|
|
2375
|
+
timeoutMs: 15000,
|
|
2376
|
+
maxOutputBytes: 64 * 1024
|
|
2377
|
+
});
|
|
2378
|
+
const remoteControlLaunchText = `${remoteControlLaunch.stdout}\n${remoteControlLaunch.stderr}`;
|
|
2379
|
+
if (remoteControlLaunch.code !== 0 || remoteControlLaunchText.includes('gpt-5.4') || remoteControlLaunchText.includes('--model') || !remoteControlLaunchText.includes('-c model="gpt-5.5"')) throw new Error('selftest failed: Codex remote-control passthrough did not force GPT-5.5 with config syntax');
|
|
2367
2380
|
const remoteControlOldBin = path.join(tmp, 'remote-control-old-bin');
|
|
2368
2381
|
await ensureDir(remoteControlOldBin);
|
|
2369
2382
|
await writeTextAtomic(path.join(remoteControlOldBin, 'codex'), '#!/bin/sh\nif [ "$1" = "--version" ]; then echo "codex-cli 0.129.0"; exit 0; fi\necho "unexpected codex $*" >&2\nexit 2\n');
|
|
@@ -2804,6 +2817,10 @@ async function selftest() {
|
|
|
2804
2817
|
if (hookTeamState.phase !== 'TEAM_PARALLEL_ANALYSIS_SCOUTING' || hookTeamState.implementation_allowed === false || !hookTeamState.team_plan_ready) throw new Error('selftest failed: $Team hook did not prepare direct Team mission');
|
|
2805
2818
|
if (!hookTeamState.pipeline_plan_ready || !(await exists(path.join(missionDir(hookTeamTmp, hookTeamState.mission_id), PIPELINE_PLAN_ARTIFACT)))) throw new Error('selftest failed: $Team hook did not write a pipeline plan');
|
|
2806
2819
|
if (!(await exists(path.join(missionDir(hookTeamTmp, hookTeamState.mission_id), 'team-plan.json')))) throw new Error('selftest failed: Team plan was not created directly');
|
|
2820
|
+
const hookForbiddenModelResult = await runProcess(process.execPath, [hookBin, 'hook', 'user-prompt-submit'], { cwd: hookTeamTmp, input: JSON.stringify({ cwd: hookTeamTmp, prompt: '$Team should be blocked before route work', model: 'gpt-5.5', metadata: { client: { modelId: 'gpt-5.4-mini' } } }), env: { SKS_DISABLE_UPDATE_CHECK: '1' }, timeoutMs: 15000, maxOutputBytes: 128 * 1024 });
|
|
2821
|
+
if (hookForbiddenModelResult.code !== 0) throw new Error(`selftest failed: forbidden model hook exited ${hookForbiddenModelResult.code}: ${hookForbiddenModelResult.stderr}`);
|
|
2822
|
+
const hookForbiddenModelJson = JSON.parse(hookForbiddenModelResult.stdout);
|
|
2823
|
+
if (hookForbiddenModelJson.decision !== 'block' || !String(hookForbiddenModelJson.reason || '').includes('gpt-5.5') || !String(hookForbiddenModelJson.reason || '').includes('gpt-5.4-mini')) throw new Error('selftest failed: hook did not block GPT-5.4 client model metadata');
|
|
2807
2824
|
const hookTeamPendingResult = await runProcess(process.execPath, [hookBin, 'hook', 'user-prompt-submit'], { cwd: hookTeamTmp, input: JSON.stringify({ cwd: hookTeamTmp, prompt: '$Team 새 작업으로 넘어가' }), env: { SKS_DISABLE_UPDATE_CHECK: '1' }, timeoutMs: 15000, maxOutputBytes: 256 * 1024 });
|
|
2808
2825
|
if (hookTeamPendingResult.code !== 0) throw new Error(`selftest failed: pending clarification hook exited ${hookTeamPendingResult.code}: ${hookTeamPendingResult.stderr}`);
|
|
2809
2826
|
const hookTeamPendingJson = JSON.parse(hookTeamPendingResult.stdout);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { exists, packageRoot, runProcess, which } from './fsx.mjs';
|
|
3
|
+
import { forceGpt55CodexArgs } from './codex-model-guard.mjs';
|
|
3
4
|
|
|
4
5
|
export async function findCodexBinary() {
|
|
5
6
|
const env = process.env.SKS_CODEX_BIN || process.env.DCODEX_CODEX_BIN || process.env.CODEX_BIN;
|
|
@@ -25,17 +26,22 @@ export async function getCodexInfo() {
|
|
|
25
26
|
return { bin, version, available: Boolean(bin) };
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
export
|
|
29
|
-
const bin = await findCodexBinary();
|
|
30
|
-
if (!bin) {
|
|
31
|
-
return { code: 127, stdout: '', stderr: 'Codex CLI not found. Install @openai/codex or set SKS_CODEX_BIN.' };
|
|
32
|
-
}
|
|
29
|
+
export function buildCodexExecArgs({ root, prompt, outputFile, json = true, profile = null, extraArgs = [] }) {
|
|
33
30
|
const args = ['exec', '--cd', root];
|
|
34
31
|
if (profile) args.push('--profile', profile);
|
|
35
32
|
if (json) args.push('--json');
|
|
36
33
|
if (outputFile) args.push('--output-last-message', outputFile);
|
|
37
|
-
args.push(...extraArgs);
|
|
34
|
+
args.push(...forceGpt55CodexArgs(extraArgs));
|
|
38
35
|
args.push(prompt);
|
|
36
|
+
return args;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function runCodexExec({ root, prompt, outputFile, json = true, profile = null, extraArgs = [], onStdout, onStderr, logDir = null, stdoutFile = null, stderrFile = null, maxBufferBytes = 256 * 1024, timeoutMs = null }) {
|
|
40
|
+
const bin = await findCodexBinary();
|
|
41
|
+
if (!bin) {
|
|
42
|
+
return { code: 127, stdout: '', stderr: 'Codex CLI not found. Install @openai/codex or set SKS_CODEX_BIN.' };
|
|
43
|
+
}
|
|
44
|
+
const args = buildCodexExecArgs({ root, prompt, outputFile, json, profile, extraArgs });
|
|
39
45
|
const effectiveTimeoutMs = Number(timeoutMs || process.env.SKS_CODEX_TIMEOUT_MS || process.env.DCODEX_CODEX_TIMEOUT_MS || 30 * 60 * 1000);
|
|
40
46
|
return runProcess(bin, args, {
|
|
41
47
|
cwd: root,
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export const REQUIRED_CODEX_MODEL = 'gpt-5.5';
|
|
2
|
+
|
|
3
|
+
const MODEL_VALUE_FLAGS = new Set(['--model', '-m']);
|
|
4
|
+
const CONFIG_VALUE_FLAGS = new Set(['-c', '--config']);
|
|
5
|
+
|
|
6
|
+
function isModelConfigOverride(value = '') {
|
|
7
|
+
return /^model\s*=/.test(String(value || '').trim());
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function stripCodexModelOverrides(args = []) {
|
|
11
|
+
const out = [];
|
|
12
|
+
const input = Array.isArray(args) ? args : [];
|
|
13
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
14
|
+
const arg = String(input[i]);
|
|
15
|
+
if (MODEL_VALUE_FLAGS.has(arg)) {
|
|
16
|
+
i += 1;
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (arg.startsWith('--model=') || arg.startsWith('-m=')) continue;
|
|
20
|
+
if (CONFIG_VALUE_FLAGS.has(arg)) {
|
|
21
|
+
const value = i + 1 < input.length ? String(input[i + 1]) : '';
|
|
22
|
+
if (isModelConfigOverride(value)) {
|
|
23
|
+
i += 1;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
out.push(arg);
|
|
27
|
+
if (i + 1 < input.length) out.push(String(input[++i]));
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (arg.startsWith('-c=') || arg.startsWith('--config=')) {
|
|
31
|
+
const value = arg.slice(arg.indexOf('=') + 1);
|
|
32
|
+
if (isModelConfigOverride(value)) continue;
|
|
33
|
+
}
|
|
34
|
+
out.push(arg);
|
|
35
|
+
}
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function forceGpt55CodexArgs(args = []) {
|
|
40
|
+
return ['--model', REQUIRED_CODEX_MODEL, ...stripCodexModelOverrides(args)];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function forceGpt55CodexConfigArgs(args = []) {
|
|
44
|
+
return ['-c', `model="${REQUIRED_CODEX_MODEL}"`, ...stripCodexModelOverrides(args)];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function isForbiddenCodexModel(value = '') {
|
|
48
|
+
const model = String(value || '').trim().toLowerCase();
|
|
49
|
+
return /^gpt-5\./.test(model) && model !== REQUIRED_CODEX_MODEL;
|
|
50
|
+
}
|
package/src/core/fsx.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import os from 'node:os';
|
|
|
5
5
|
import crypto from 'node:crypto';
|
|
6
6
|
import { spawn } from 'node:child_process';
|
|
7
7
|
|
|
8
|
-
export const PACKAGE_VERSION = '0.7.
|
|
8
|
+
export const PACKAGE_VERSION = '0.7.55';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
|
|
@@ -6,6 +6,7 @@ import { checkDbOperation, dbBlockReason, handleMadSksUserConfirmation } from '.
|
|
|
6
6
|
import { checkHarnessModification, harnessGuardBlockReason } from './harness-guard.mjs';
|
|
7
7
|
import { activeRouteContext, evaluateStop, prepareRoute, promptPipelineContext as routePipelineContext, recordContext7Evidence, recordSubagentEvidence, routePrompt } from './pipeline.mjs';
|
|
8
8
|
import { classifyToolError } from './evaluation.mjs';
|
|
9
|
+
import { REQUIRED_CODEX_MODEL, isForbiddenCodexModel } from './codex-model-guard.mjs';
|
|
9
10
|
|
|
10
11
|
const TEAM_DIGEST_MAX_EVENTS = 4;
|
|
11
12
|
const TEAM_DIGEST_MESSAGE_CHARS = 180;
|
|
@@ -91,7 +92,11 @@ export async function hookMain(name) {
|
|
|
91
92
|
const root = await projectRoot(payload.cwd || process.cwd());
|
|
92
93
|
const state = await loadState(root);
|
|
93
94
|
const noQuestion = isNoQuestionRunning(state);
|
|
94
|
-
if (name === 'user-prompt-submit')
|
|
95
|
+
if (name === 'user-prompt-submit') {
|
|
96
|
+
const modelBlock = blockForbiddenClientModel(payload);
|
|
97
|
+
if (modelBlock) return modelBlock;
|
|
98
|
+
return hookUserPrompt(root, state, payload, noQuestion);
|
|
99
|
+
}
|
|
95
100
|
if (name === 'pre-tool') return hookPreTool(root, state, payload, noQuestion);
|
|
96
101
|
if (name === 'post-tool') return hookPostTool(root, state, payload, noQuestion);
|
|
97
102
|
if (name === 'permission-request') return hookPermission(root, state, payload, noQuestion);
|
|
@@ -99,6 +104,43 @@ export async function hookMain(name) {
|
|
|
99
104
|
return { continue: true };
|
|
100
105
|
}
|
|
101
106
|
|
|
107
|
+
function blockForbiddenClientModel(payload = {}) {
|
|
108
|
+
const model = forbiddenClientModelFromPayload(payload);
|
|
109
|
+
if (!model || !isForbiddenCodexModel(model)) return null;
|
|
110
|
+
return {
|
|
111
|
+
decision: 'block',
|
|
112
|
+
reason: `SKS requires ${REQUIRED_CODEX_MODEL}; client payload requested ${model}. Switch the Codex client/session model to ${REQUIRED_CODEX_MODEL} and retry.`
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function forbiddenClientModelFromPayload(payload = {}) {
|
|
117
|
+
const candidates = [
|
|
118
|
+
payload.model,
|
|
119
|
+
payload.model_id,
|
|
120
|
+
payload.modelId,
|
|
121
|
+
payload.client_model,
|
|
122
|
+
payload.clientModel,
|
|
123
|
+
...clientModelCandidates(payload.client),
|
|
124
|
+
...clientModelCandidates(payload.metadata),
|
|
125
|
+
...clientModelCandidates(payload.context),
|
|
126
|
+
...clientModelCandidates(payload.thread),
|
|
127
|
+
...clientModelCandidates(payload.session)
|
|
128
|
+
];
|
|
129
|
+
return candidates.find((value) => typeof value === 'string' && isForbiddenCodexModel(value)) || '';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function clientModelCandidates(value, depth = 0) {
|
|
133
|
+
if (!value || typeof value !== 'object' || depth > 4) return [];
|
|
134
|
+
const out = [];
|
|
135
|
+
for (const key of ['model', 'model_id', 'modelId', 'client_model', 'clientModel']) {
|
|
136
|
+
if (typeof value[key] === 'string') out.push(value[key]);
|
|
137
|
+
}
|
|
138
|
+
for (const key of ['client', 'metadata', 'context', 'thread', 'session']) {
|
|
139
|
+
out.push(...clientModelCandidates(value[key], depth + 1));
|
|
140
|
+
}
|
|
141
|
+
return out;
|
|
142
|
+
}
|
|
143
|
+
|
|
102
144
|
async function hookUserPrompt(root, state, payload, noQuestion) {
|
|
103
145
|
if (!noQuestion) {
|
|
104
146
|
const prompt = extractUserPrompt(payload);
|
package/src/core/tmux-ui.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
4
4
|
import { exists, nowIso, packageRoot, readJson, runProcess, sha256, sksRoot, which, writeJsonAtomic } from './fsx.mjs';
|
|
5
5
|
import { getCodexInfo } from './codex-adapter.mjs';
|
|
6
6
|
import { codexAppIntegrationStatus, formatCodexAppStatus } from './codex-app.mjs';
|
|
7
|
+
import { REQUIRED_CODEX_MODEL, forceGpt55CodexArgs } from './codex-model-guard.mjs';
|
|
7
8
|
import { MIN_TEAM_REVIEWER_LANES } from './team-review-policy.mjs';
|
|
8
9
|
|
|
9
10
|
export const SKS_TMUX_LOGO = [
|
|
@@ -114,7 +115,7 @@ const SKS_TMUX_LOGO_ANIMATION_STEPS = Object.freeze([
|
|
|
114
115
|
{ frame: 9, color: '51', bold: true, delay: '0.16' }
|
|
115
116
|
]);
|
|
116
117
|
|
|
117
|
-
export const DEFAULT_SKS_CODEX_MODEL =
|
|
118
|
+
export const DEFAULT_SKS_CODEX_MODEL = REQUIRED_CODEX_MODEL;
|
|
118
119
|
export const DEFAULT_SKS_CODEX_REASONING = 'high';
|
|
119
120
|
|
|
120
121
|
export function defaultCodexLaunchArgs(env = process.env) {
|
|
@@ -200,7 +201,7 @@ export function tmuxStatusKind(tmux = {}) {
|
|
|
200
201
|
}
|
|
201
202
|
|
|
202
203
|
export function codexLaunchCommand(root, codexBin, codexArgs = []) {
|
|
203
|
-
const extraArgs =
|
|
204
|
+
const extraArgs = forceGpt55CodexArgs(codexArgs);
|
|
204
205
|
return [
|
|
205
206
|
sksLogoIntroCommand(codexBin),
|
|
206
207
|
`printf '\\nProject: %s\\n' ${shellEscape(root)}`,
|
|
@@ -322,7 +323,7 @@ export async function buildTmuxLaunchPlan(opts = {}) {
|
|
|
322
323
|
const codex = opts.codex || await getCodexInfo().catch(() => ({}));
|
|
323
324
|
const tmux = opts.tmux || await tmuxReadiness(opts);
|
|
324
325
|
const app = opts.app || await codexAppIntegrationStatus({ codex });
|
|
325
|
-
const codexArgs = Array.isArray(opts.codexArgs) ? opts.codexArgs : defaultCodexLaunchArgs(opts.env || process.env);
|
|
326
|
+
const codexArgs = forceGpt55CodexArgs(Array.isArray(opts.codexArgs) ? opts.codexArgs : defaultCodexLaunchArgs(opts.env || process.env));
|
|
326
327
|
return {
|
|
327
328
|
root,
|
|
328
329
|
session,
|