sneakoscope 2.0.18 → 3.0.1
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 +127 -71
- 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/.sks-build-stamp.json +4 -4
- package/dist/bin/sks.js +1 -1
- package/dist/commands/mad-sks.js +2 -0
- package/dist/commands/zellij.js +58 -1
- package/dist/core/agents/agent-scheduler.js +32 -24
- package/dist/core/agents/native-cli-session-swarm.js +22 -2
- package/dist/core/codex-app/codex-app-handoff.js +30 -9
- package/dist/core/codex-app/codex-app-launcher.js +103 -0
- package/dist/core/codex-control/codex-0138-capability.js +42 -4
- package/dist/core/codex-control/codex-0139-capability.js +102 -0
- package/dist/core/codex-control/codex-model-capabilities.js +25 -4
- package/dist/core/codex-control/codex-model-metadata.js +91 -0
- package/dist/core/codex-plugins/codex-plugin-cache.js +38 -0
- package/dist/core/codex-plugins/codex-plugin-diff.js +73 -0
- package/dist/core/codex-plugins/codex-plugin-json.js +35 -11
- package/dist/core/commands/mad-sks-command.js +8 -0
- package/dist/core/commands/naruto-command.js +29 -0
- package/dist/core/commands/qa-loop-command.js +41 -6
- package/dist/core/fsx.js +1 -1
- package/dist/core/image/image-artifact-path-contract.js +2 -0
- package/dist/core/image/image-artifact-registry.js +33 -0
- package/dist/core/image-ux-review/imagegen-adapter.js +27 -16
- package/dist/core/pipeline-internals/runtime-core.js +4 -2
- package/dist/core/qa-loop/qa-loop-app-handoff-confirmation.js +51 -0
- package/dist/core/qa-loop/qa-loop-budget-policy.js +1 -1
- package/dist/core/qa-loop.js +44 -3
- package/dist/core/release/release-gate-cache-v2.js +47 -5
- package/dist/core/usage/codex-account-usage.js +77 -16
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-slot-pane-renderer.js +5 -2
- package/dist/core/zellij/zellij-slot-telemetry.js +65 -12
- package/dist/core/zellij/zellij-ui-mode.js +8 -1
- package/dist/core/zellij/zellij-update.js +307 -0
- package/dist/core/zellij/zellij-worker-pane-manager.js +211 -145
- package/package.json +23 -2
- package/dist/core/naruto/naruto-work-stealing.js +0 -11
- package/dist/core/zellij/zellij-right-column-layout-proof.js +0 -42
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { detectCodex0138Capability } from '../codex-control/codex-0138-capability.js';
|
|
3
3
|
import { nowIso, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
|
|
4
|
+
import { attemptCodexAppLaunch } from './codex-app-launcher.js';
|
|
4
5
|
export function buildCodexAppHandoffPrompt(request) {
|
|
5
6
|
return [
|
|
6
7
|
'SKS Codex Desktop /app handoff request',
|
|
@@ -23,6 +24,7 @@ export async function runCodexAppHandoff(root, request) {
|
|
|
23
24
|
const capability = await detectCodex0138Capability();
|
|
24
25
|
const platformSupported = process.platform === 'darwin' || process.platform === 'win32';
|
|
25
26
|
const desktopSupported = capability.supports_app_handoff === true && platformSupported;
|
|
27
|
+
const launchMode = request.launch_mode || 'artifact-only';
|
|
26
28
|
const dir = path.join(root, '.sneakoscope', 'missions', request.mission_id, 'qa-loop');
|
|
27
29
|
const artifactPath = path.join(dir, 'app-handoff.json');
|
|
28
30
|
const promptArtifactPath = path.join(dir, 'app-handoff-prompt.md');
|
|
@@ -32,26 +34,45 @@ export async function runCodexAppHandoff(root, request) {
|
|
|
32
34
|
];
|
|
33
35
|
const prompt = buildCodexAppHandoffPrompt(request);
|
|
34
36
|
await writeTextAtomic(promptArtifactPath, prompt);
|
|
37
|
+
const launchAttempt = desktopSupported
|
|
38
|
+
? await attemptCodexAppLaunch({
|
|
39
|
+
cwd: root,
|
|
40
|
+
promptArtifactPath,
|
|
41
|
+
mode: launchMode,
|
|
42
|
+
timeoutMs: 3000
|
|
43
|
+
})
|
|
44
|
+
: await attemptCodexAppLaunch({
|
|
45
|
+
cwd: root,
|
|
46
|
+
promptArtifactPath,
|
|
47
|
+
mode: 'artifact-only',
|
|
48
|
+
timeoutMs: 3000
|
|
49
|
+
});
|
|
35
50
|
const status = request.require_desktop && !desktopSupported
|
|
36
51
|
? 'blocked_for_desktop_review'
|
|
37
|
-
:
|
|
38
|
-
? '
|
|
39
|
-
:
|
|
52
|
+
: request.require_desktop && launchMode === 'attempt-launch' && launchAttempt.attempted && !launchAttempt.launched
|
|
53
|
+
? 'blocked_for_desktop_review'
|
|
54
|
+
: desktopSupported && launchAttempt.launched
|
|
55
|
+
? 'launched_pending_confirmation'
|
|
56
|
+
: desktopSupported
|
|
57
|
+
? 'pending'
|
|
58
|
+
: 'skipped';
|
|
40
59
|
const result = {
|
|
41
60
|
schema: 'sks.codex-app-handoff-result.v1',
|
|
42
|
-
ok: request.require_desktop ?
|
|
43
|
-
attempted:
|
|
44
|
-
launched:
|
|
61
|
+
ok: request.require_desktop ? status !== 'blocked_for_desktop_review' : true,
|
|
62
|
+
attempted: launchAttempt.attempted,
|
|
63
|
+
launched: launchAttempt.launched,
|
|
45
64
|
status,
|
|
46
65
|
codex_0138_capability: capability,
|
|
47
|
-
command_line:
|
|
66
|
+
command_line: launchAttempt.command_line,
|
|
67
|
+
launch_attempt: launchAttempt,
|
|
68
|
+
confirmation_required: request.require_desktop,
|
|
48
69
|
desktop_handoff_supported: desktopSupported,
|
|
49
70
|
fallback_reason: desktopSupported
|
|
50
|
-
? '
|
|
71
|
+
? launchAttempt.fallback_reason || 'desktop_handoff_pending_operator_confirmation'
|
|
51
72
|
: blockers.join(';') || null,
|
|
52
73
|
artifact_path: artifactPath,
|
|
53
74
|
prompt_artifact_path: promptArtifactPath,
|
|
54
|
-
blockers: request.require_desktop ? blockers : []
|
|
75
|
+
blockers: request.require_desktop ? [...blockers, ...(status === 'blocked_for_desktop_review' ? launchAttempt.blockers : [])] : []
|
|
55
76
|
};
|
|
56
77
|
await writeJsonAtomic(artifactPath, {
|
|
57
78
|
...result,
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { findCodexBinary } from '../codex-adapter.js';
|
|
2
|
+
import { runProcess } from '../fsx.js';
|
|
3
|
+
export async function attemptCodexAppLaunch(input) {
|
|
4
|
+
const platform = process.platform;
|
|
5
|
+
const timeoutMs = Math.max(1, Math.min(Number(input.timeoutMs || 3000), 3000));
|
|
6
|
+
const codexBin = input.codexBin || await findCodexBinary();
|
|
7
|
+
const commandLine = [codexBin || 'codex', '/app'];
|
|
8
|
+
if (input.mode === 'artifact-only') {
|
|
9
|
+
return launchAttempt({
|
|
10
|
+
attempted: false,
|
|
11
|
+
launched: false,
|
|
12
|
+
platform,
|
|
13
|
+
mode: input.mode,
|
|
14
|
+
command_line: commandLine,
|
|
15
|
+
exit_code: null,
|
|
16
|
+
fallback_reason: 'artifact_only_mode',
|
|
17
|
+
blockers: []
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
const platformSupported = platform === 'darwin' || platform === 'win32';
|
|
21
|
+
if (!platformSupported) {
|
|
22
|
+
return launchAttempt({
|
|
23
|
+
attempted: false,
|
|
24
|
+
launched: false,
|
|
25
|
+
platform,
|
|
26
|
+
mode: input.mode,
|
|
27
|
+
command_line: commandLine,
|
|
28
|
+
exit_code: null,
|
|
29
|
+
fallback_reason: 'unsupported_platform_artifact_only_fallback',
|
|
30
|
+
blockers: ['codex_app_handoff_platform_unsupported']
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
if (!codexBin) {
|
|
34
|
+
return launchAttempt({
|
|
35
|
+
attempted: false,
|
|
36
|
+
launched: false,
|
|
37
|
+
platform,
|
|
38
|
+
mode: input.mode,
|
|
39
|
+
command_line: commandLine,
|
|
40
|
+
exit_code: null,
|
|
41
|
+
fallback_reason: 'codex_cli_missing_artifact_only_fallback',
|
|
42
|
+
blockers: ['codex_cli_missing']
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
if (process.env.SKS_CODEX_APP_LAUNCH_FAKE === '1') {
|
|
46
|
+
const launched = process.env.SKS_CODEX_APP_LAUNCH_FAKE_LAUNCHED !== '0';
|
|
47
|
+
return launchAttempt({
|
|
48
|
+
attempted: true,
|
|
49
|
+
launched,
|
|
50
|
+
platform,
|
|
51
|
+
mode: input.mode,
|
|
52
|
+
command_line: commandLine,
|
|
53
|
+
exit_code: launched ? 0 : 1,
|
|
54
|
+
stdout_tail: launched ? 'fake codex /app launched' : '',
|
|
55
|
+
stderr_tail: launched ? '' : 'fake launch failed',
|
|
56
|
+
fallback_reason: launched ? null : 'fake_launch_failed',
|
|
57
|
+
blockers: launched ? [] : ['codex_app_launch_failed']
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
const result = await runProcess(codexBin, ['/app'], {
|
|
61
|
+
cwd: input.cwd,
|
|
62
|
+
timeoutMs,
|
|
63
|
+
maxOutputBytes: 32 * 1024,
|
|
64
|
+
input: `Continue the SKS mission using this prompt artifact:\n${input.promptArtifactPath}\n`
|
|
65
|
+
}).catch((err) => ({
|
|
66
|
+
code: -1,
|
|
67
|
+
stdout: '',
|
|
68
|
+
stderr: err instanceof Error ? err.message : String(err),
|
|
69
|
+
timedOut: false
|
|
70
|
+
}));
|
|
71
|
+
const code = typeof result.code === 'number' ? result.code : null;
|
|
72
|
+
const stdout = String(result.stdout || '');
|
|
73
|
+
const stderr = String(result.stderr || '');
|
|
74
|
+
const markerLaunched = /(?:handoff|desktop|app).*?(?:launched|opened|ready)|(?:launched|opened).*?(?:handoff|desktop|app)/i.test(`${stdout}\n${stderr}`);
|
|
75
|
+
const launched = code === 0 || markerLaunched;
|
|
76
|
+
const timedOut = result.timedOut === true || code === 124;
|
|
77
|
+
const fallbackReason = launched
|
|
78
|
+
? null
|
|
79
|
+
: timedOut
|
|
80
|
+
? 'codex_app_handoff_interactive_or_timed_out_artifact_only_fallback'
|
|
81
|
+
: 'codex_app_launch_failed_artifact_only_fallback';
|
|
82
|
+
return launchAttempt({
|
|
83
|
+
attempted: true,
|
|
84
|
+
launched,
|
|
85
|
+
platform,
|
|
86
|
+
mode: input.mode,
|
|
87
|
+
command_line: commandLine,
|
|
88
|
+
exit_code: code,
|
|
89
|
+
stdout_tail: stdout.slice(-4000),
|
|
90
|
+
stderr_tail: stderr.slice(-4000),
|
|
91
|
+
fallback_reason: fallbackReason,
|
|
92
|
+
blockers: launched ? [] : [timedOut ? 'codex_app_launch_interactive_or_timeout' : 'codex_app_launch_failed']
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function launchAttempt(input) {
|
|
96
|
+
return {
|
|
97
|
+
schema: 'sks.codex-app-launch-attempt.v1',
|
|
98
|
+
stdout_tail: input.stdout_tail || '',
|
|
99
|
+
stderr_tail: input.stderr_tail || '',
|
|
100
|
+
...input
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=codex-app-launcher.js.map
|
|
@@ -12,23 +12,36 @@ export async function detectCodex0138Capability(input = {}) {
|
|
|
12
12
|
: await readCodexVersionText(codexBin);
|
|
13
13
|
const parsed = parseCodexVersion(versionText);
|
|
14
14
|
const atLeast138 = Boolean(parsed && semverGte(parsed, '0.138.0'));
|
|
15
|
+
const probeMode = process.env.SKS_CODEX_0138_PROBE === '1' ? 'feature-probe' : 'version-only';
|
|
16
|
+
const featureProbeResults = probeMode === 'feature-probe'
|
|
17
|
+
? await probeCodex0138Features(codexBin, { fake })
|
|
18
|
+
: {
|
|
19
|
+
plugin_json: 'skipped',
|
|
20
|
+
app_handoff_platform: 'skipped',
|
|
21
|
+
image_path_exposure_contract: 'sks-enforced'
|
|
22
|
+
};
|
|
23
|
+
const pluginJsonOk = atLeast138 && (probeMode === 'version-only' || featureProbeResults.plugin_json !== 'failed');
|
|
24
|
+
const appHandoffOk = atLeast138 && (probeMode === 'version-only' || featureProbeResults.app_handoff_platform === 'passed');
|
|
15
25
|
const blockers = [
|
|
16
26
|
...(!codexBin ? ['codex_cli_missing'] : []),
|
|
17
|
-
...(atLeast138 ? [] : ['codex_0_138_required_for_app_plugin_features'])
|
|
27
|
+
...(atLeast138 ? [] : ['codex_0_138_required_for_app_plugin_features']),
|
|
28
|
+
...(probeMode === 'feature-probe' && featureProbeResults.plugin_json === 'failed' ? ['codex_plugin_json_probe_failed'] : [])
|
|
18
29
|
];
|
|
19
30
|
return {
|
|
20
31
|
schema: 'sks.codex-0138-capability.v1',
|
|
21
|
-
ok: atLeast138,
|
|
32
|
+
ok: atLeast138 && blockers.length === 0,
|
|
33
|
+
probe_mode: probeMode,
|
|
22
34
|
codex_bin: codexBin || null,
|
|
23
35
|
version_text: versionText || null,
|
|
24
36
|
parsed_version: parsed,
|
|
25
|
-
supports_app_handoff:
|
|
26
|
-
supports_plugin_json:
|
|
37
|
+
supports_app_handoff: appHandoffOk,
|
|
38
|
+
supports_plugin_json: pluginJsonOk,
|
|
27
39
|
supports_image_path_exposure: atLeast138,
|
|
28
40
|
supports_model_defined_efforts: atLeast138,
|
|
29
41
|
supports_app_server_token_usage: atLeast138,
|
|
30
42
|
supports_v2_pat_auth: atLeast138,
|
|
31
43
|
supports_oauth_mcp_prerefresh: atLeast138,
|
|
44
|
+
feature_probe_results: featureProbeResults,
|
|
32
45
|
blockers
|
|
33
46
|
};
|
|
34
47
|
}
|
|
@@ -61,4 +74,29 @@ async function readCodexVersionText(codexBin) {
|
|
|
61
74
|
const text = `${result.stdout || ''}${result.stderr || ''}`.trim();
|
|
62
75
|
return result.code === 0 ? text : text || null;
|
|
63
76
|
}
|
|
77
|
+
async function probeCodex0138Features(codexBin, opts = {}) {
|
|
78
|
+
if (opts.fake) {
|
|
79
|
+
return {
|
|
80
|
+
plugin_json: process.env.SKS_CODEX_0138_FAKE_PLUGIN_JSON_FAIL === '1' ? 'failed' : 'passed',
|
|
81
|
+
app_handoff_platform: process.platform === 'darwin' || process.platform === 'win32' ? 'passed' : 'failed',
|
|
82
|
+
image_path_exposure_contract: 'sks-enforced'
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const timeoutMs = Math.max(1, Number(process.env.SKS_CODEX_0138_PROBE_TIMEOUT_MS || 3000) || 3000);
|
|
86
|
+
const platformSupported = process.platform === 'darwin' || process.platform === 'win32';
|
|
87
|
+
if (!codexBin) {
|
|
88
|
+
return {
|
|
89
|
+
plugin_json: 'failed',
|
|
90
|
+
app_handoff_platform: platformSupported ? 'skipped' : 'failed',
|
|
91
|
+
image_path_exposure_contract: 'sks-enforced'
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const list = await runProcess(codexBin, ['plugin', 'list', '--json'], { timeoutMs, maxOutputBytes: 64 * 1024 }).catch(() => ({ code: 1 }));
|
|
95
|
+
const detailHelp = await runProcess(codexBin, ['plugin', 'detail', '--help'], { timeoutMs, maxOutputBytes: 64 * 1024 }).catch(() => ({ code: 1 }));
|
|
96
|
+
return {
|
|
97
|
+
plugin_json: list.code === 0 && detailHelp.code === 0 ? 'passed' : 'failed',
|
|
98
|
+
app_handoff_platform: platformSupported ? 'passed' : 'failed',
|
|
99
|
+
image_path_exposure_contract: 'sks-enforced'
|
|
100
|
+
};
|
|
101
|
+
}
|
|
64
102
|
//# sourceMappingURL=codex-0138-capability.js.map
|
|
@@ -0,0 +1,102 @@
|
|
|
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
|
+
export async function detectCodex0139Capability(input = {}) {
|
|
6
|
+
const fake = process.env.SKS_CODEX_0139_FAKE === '1';
|
|
7
|
+
const codexBin = fake
|
|
8
|
+
? input.codexBin || process.env.CODEX_BIN || 'codex'
|
|
9
|
+
: input.codexBin || process.env.CODEX_BIN || await findCodexBinary();
|
|
10
|
+
const versionText = fake
|
|
11
|
+
? String(process.env.SKS_CODEX_VERSION_FAKE || 'codex-cli 0.139.0')
|
|
12
|
+
: await readCodexVersionText(codexBin);
|
|
13
|
+
const parsed = parseCodexVersionText(versionText);
|
|
14
|
+
const atLeast139 = Boolean(parsed && compareSemverLike(parsed, '0.139.0') >= 0);
|
|
15
|
+
const probeMode = process.env.SKS_CODEX_0139_PROBE === '1' ? 'feature-probe' : 'version-only';
|
|
16
|
+
const featureProbeResults = probeMode === 'feature-probe'
|
|
17
|
+
? await probeCodex0139Features(codexBin, { fake })
|
|
18
|
+
: {
|
|
19
|
+
marketplace_list_json: 'skipped',
|
|
20
|
+
sandbox_profile_alias: 'skipped'
|
|
21
|
+
};
|
|
22
|
+
const marketplaceOk = atLeast139 && (probeMode === 'version-only' || featureProbeResults.marketplace_list_json !== 'failed');
|
|
23
|
+
const profileAliasOk = atLeast139 && (probeMode === 'version-only' || featureProbeResults.sandbox_profile_alias !== 'failed');
|
|
24
|
+
const blockers = [
|
|
25
|
+
...(!codexBin ? ['codex_cli_missing'] : []),
|
|
26
|
+
...(atLeast139 ? [] : ['codex_0_139_required_for_search_schema_marketplace_features']),
|
|
27
|
+
...(probeMode === 'feature-probe' && featureProbeResults.marketplace_list_json === 'failed' ? ['codex_marketplace_list_json_probe_failed'] : [])
|
|
28
|
+
];
|
|
29
|
+
return {
|
|
30
|
+
schema: 'sks.codex-0139-capability.v1',
|
|
31
|
+
ok: atLeast139 && blockers.length === 0,
|
|
32
|
+
probe_mode: probeMode,
|
|
33
|
+
codex_bin: codexBin || null,
|
|
34
|
+
version_text: versionText || null,
|
|
35
|
+
parsed_version: parsed,
|
|
36
|
+
supports_code_mode_web_search: atLeast139,
|
|
37
|
+
supports_rich_tool_schemas: atLeast139,
|
|
38
|
+
supports_doctor_env_details: atLeast139,
|
|
39
|
+
supports_marketplace_source_field: marketplaceOk,
|
|
40
|
+
supports_plugin_catalog_cache: atLeast139,
|
|
41
|
+
supports_sandbox_profile_alias: profileAliasOk,
|
|
42
|
+
supports_interrupt_agent_rename: atLeast139,
|
|
43
|
+
feature_probe_results: featureProbeResults,
|
|
44
|
+
blockers
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export async function writeCodex0139CapabilityArtifacts(root, input = {}) {
|
|
48
|
+
const capability = await detectCodex0139Capability({ codexBin: input.codexBin || null });
|
|
49
|
+
const report = { ...capability, generated_at: nowIso() };
|
|
50
|
+
const rootArtifact = path.join(root, '.sneakoscope', 'codex-0139-capability.json');
|
|
51
|
+
await writeJsonAtomic(rootArtifact, report);
|
|
52
|
+
let missionArtifact = null;
|
|
53
|
+
if (input.missionId) {
|
|
54
|
+
missionArtifact = path.join(root, '.sneakoscope', 'missions', input.missionId, 'codex-0139-capability.json');
|
|
55
|
+
await writeJsonAtomic(missionArtifact, report);
|
|
56
|
+
}
|
|
57
|
+
return { report, root_artifact: rootArtifact, mission_artifact: missionArtifact };
|
|
58
|
+
}
|
|
59
|
+
async function readCodexVersionText(codexBin) {
|
|
60
|
+
if (!codexBin)
|
|
61
|
+
return null;
|
|
62
|
+
const result = await runProcess(codexBin, ['--version'], { timeoutMs: 10_000, maxOutputBytes: 16 * 1024 }).catch((err) => ({
|
|
63
|
+
code: 1,
|
|
64
|
+
stdout: '',
|
|
65
|
+
stderr: err?.message || String(err)
|
|
66
|
+
}));
|
|
67
|
+
const text = `${result.stdout || ''}${result.stderr || ''}`.trim();
|
|
68
|
+
return result.code === 0 ? text : text || null;
|
|
69
|
+
}
|
|
70
|
+
async function probeCodex0139Features(codexBin, opts = {}) {
|
|
71
|
+
if (opts.fake) {
|
|
72
|
+
return {
|
|
73
|
+
marketplace_list_json: process.env.SKS_CODEX_0139_FAKE_MARKETPLACE_FAIL === '1' ? 'failed' : 'passed',
|
|
74
|
+
sandbox_profile_alias: 'passed'
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const timeoutMs = Math.max(1, Number(process.env.SKS_CODEX_0139_PROBE_TIMEOUT_MS || 3000) || 3000);
|
|
78
|
+
if (!codexBin) {
|
|
79
|
+
return { marketplace_list_json: 'failed', sandbox_profile_alias: 'failed' };
|
|
80
|
+
}
|
|
81
|
+
const marketplace = await runProcess(codexBin, ['plugin', 'marketplace', 'list', '--json'], { timeoutMs, maxOutputBytes: 256 * 1024 }).catch(() => ({ code: 1, stdout: '' }));
|
|
82
|
+
const marketplaceListJson = marketplace.code === 0 && marketplaceSourcesPresent(marketplace.stdout) ? 'passed' : 'failed';
|
|
83
|
+
const help = await runProcess(codexBin, ['--help'], { timeoutMs, maxOutputBytes: 256 * 1024 }).catch(() => ({ code: 1, stdout: '' }));
|
|
84
|
+
const aliasOk = help.code === 0 && /(^|\s)-P[,\s]/m.test(String(help.stdout || ''));
|
|
85
|
+
return {
|
|
86
|
+
marketplace_list_json: marketplaceListJson,
|
|
87
|
+
sandbox_profile_alias: aliasOk ? 'passed' : 'failed'
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
export function marketplaceSourcesPresent(stdout) {
|
|
91
|
+
try {
|
|
92
|
+
const parsed = JSON.parse(String(stdout || ''));
|
|
93
|
+
const rows = Array.isArray(parsed) ? parsed : Array.isArray(parsed?.marketplaces) ? parsed.marketplaces : Array.isArray(parsed?.items) ? parsed.items : [];
|
|
94
|
+
if (!rows.length)
|
|
95
|
+
return true;
|
|
96
|
+
return rows.some((row) => typeof row?.source === 'string' && row.source.length > 0);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=codex-0139-capability.js.map
|
|
@@ -1,15 +1,36 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { writeJsonAtomic } from '../fsx.js';
|
|
3
|
+
import { collectCodexModelMetadata } from './codex-model-metadata.js';
|
|
1
4
|
export const SKS_FALLBACK_EFFORT_ORDER = ['minimal', 'low', 'medium', 'high', 'xhigh'];
|
|
2
5
|
export function codexModelEffortCapability(input = {}) {
|
|
3
|
-
const
|
|
6
|
+
const metadataIsFallback = input.metadata?.source === 'fallback';
|
|
7
|
+
const advertised = metadataIsFallback ? [] : normalizeAdvertisedEfforts(input.metadata?.advertised_efforts || input.advertisedEfforts);
|
|
4
8
|
const order = advertised.length ? advertised : SKS_FALLBACK_EFFORT_ORDER;
|
|
5
|
-
const
|
|
9
|
+
const requestedDefault = input.metadata?.default_effort || input.defaultEffort;
|
|
10
|
+
const defaultEffort = order.includes(String(requestedDefault || '')) ? String(requestedDefault) : order.includes('medium') ? 'medium' : order[0] || 'medium';
|
|
6
11
|
return {
|
|
7
|
-
model: String(input.model || process.env.SKS_CODEX_MODEL || process.env.CODEX_MODEL || 'gpt-5.5'),
|
|
12
|
+
model: String(input.metadata?.model || input.model || process.env.SKS_CODEX_MODEL || process.env.CODEX_MODEL || 'gpt-5.5'),
|
|
8
13
|
advertised_efforts: order,
|
|
9
14
|
default_effort: defaultEffort,
|
|
10
|
-
order_source: advertised.length ? 'model-advertised' : 'sks-fallback'
|
|
15
|
+
order_source: advertised.length ? 'model-advertised' : 'sks-fallback',
|
|
16
|
+
metadata_source: input.metadata?.source || null,
|
|
17
|
+
metadata_blockers: input.metadata?.blockers || []
|
|
11
18
|
};
|
|
12
19
|
}
|
|
20
|
+
export async function resolveCodexModelEffortCapability(input = {}) {
|
|
21
|
+
const metadata = await collectCodexModelMetadata({ model: input.model || null });
|
|
22
|
+
return codexModelEffortCapability({ metadata });
|
|
23
|
+
}
|
|
24
|
+
export async function writeCodexModelEffortCapabilityArtifact(root, input) {
|
|
25
|
+
const capability = await resolveCodexModelEffortCapability({ model: input.model || null });
|
|
26
|
+
const artifact = path.join(root, '.sneakoscope', 'missions', input.missionId, 'codex-model-effort-capability.json');
|
|
27
|
+
await writeJsonAtomic(artifact, {
|
|
28
|
+
schema: 'sks.codex-model-effort-capability-artifact.v1',
|
|
29
|
+
generated_at: new Date().toISOString(),
|
|
30
|
+
...capability
|
|
31
|
+
});
|
|
32
|
+
return { capability, artifact };
|
|
33
|
+
}
|
|
13
34
|
export function normalizeAdvertisedEfforts(value) {
|
|
14
35
|
const rows = Array.isArray(value) ? value : String(value || '').split(',');
|
|
15
36
|
const seen = new Set();
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { findCodexBinary } from '../codex-adapter.js';
|
|
2
|
+
import { runProcess } from '../fsx.js';
|
|
3
|
+
const FALLBACK_EFFORT_ORDER = ['minimal', 'low', 'medium', 'high', 'xhigh'];
|
|
4
|
+
export async function collectCodexModelMetadata(input = {}) {
|
|
5
|
+
if (process.env.SKS_CODEX_MODEL_METADATA_FAKE === '1') {
|
|
6
|
+
const advertised = normalizeAdvertisedEfforts(process.env.SKS_CODEX_MODEL_EFFORTS || 'low,medium,high,xhigh');
|
|
7
|
+
return metadata(String(input.model || process.env.SKS_CODEX_MODEL || 'gpt-5.5'), advertised, 'medium', 'app-server', []);
|
|
8
|
+
}
|
|
9
|
+
const model = String(input.model || process.env.SKS_CODEX_MODEL || process.env.CODEX_MODEL || 'gpt-5.5');
|
|
10
|
+
const appServer = await readAppServerMetadata(model);
|
|
11
|
+
if (appServer)
|
|
12
|
+
return appServer;
|
|
13
|
+
const cli = await readCodexCliMetadata(model);
|
|
14
|
+
if (cli)
|
|
15
|
+
return cli;
|
|
16
|
+
const envEfforts = normalizeAdvertisedEfforts(process.env.SKS_CODEX_MODEL_EFFORTS || '');
|
|
17
|
+
if (envEfforts.length)
|
|
18
|
+
return metadata(model, envEfforts, process.env.SKS_CODEX_MODEL_DEFAULT_EFFORT || 'medium', 'env', []);
|
|
19
|
+
return metadata(model, FALLBACK_EFFORT_ORDER, 'medium', 'fallback', ['codex_model_metadata_unavailable']);
|
|
20
|
+
}
|
|
21
|
+
async function readAppServerMetadata(model) {
|
|
22
|
+
const url = String(process.env.CODEX_APP_SERVER_METADATA_URL || process.env.SKS_CODEX_APP_SERVER_METADATA_URL || '').trim();
|
|
23
|
+
if (!url)
|
|
24
|
+
return null;
|
|
25
|
+
try {
|
|
26
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(3000) });
|
|
27
|
+
if (!response.ok)
|
|
28
|
+
return null;
|
|
29
|
+
const payload = await response.json();
|
|
30
|
+
return normalizePayload(payload, model, 'app-server');
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function readCodexCliMetadata(model) {
|
|
37
|
+
const bin = await findCodexBinary();
|
|
38
|
+
if (!bin)
|
|
39
|
+
return null;
|
|
40
|
+
const commands = [
|
|
41
|
+
['model', 'metadata', '--json'],
|
|
42
|
+
['debug', 'model-metadata', '--json'],
|
|
43
|
+
['capabilities', '--json']
|
|
44
|
+
];
|
|
45
|
+
for (const args of commands) {
|
|
46
|
+
const result = await runProcess(bin, args, { timeoutMs: 3000, maxOutputBytes: 64 * 1024 }).catch(() => null);
|
|
47
|
+
if (!result || result.code !== 0)
|
|
48
|
+
continue;
|
|
49
|
+
try {
|
|
50
|
+
const payload = JSON.parse(`${result.stdout || ''}${result.stderr || ''}`.trim() || '{}');
|
|
51
|
+
const normalized = normalizePayload(payload, model, 'codex-cli');
|
|
52
|
+
if (normalized.advertised_efforts.length)
|
|
53
|
+
return normalized;
|
|
54
|
+
}
|
|
55
|
+
catch { }
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
function normalizePayload(payload, fallbackModel, source) {
|
|
60
|
+
const row = Array.isArray(payload?.models)
|
|
61
|
+
? payload.models.find((candidate) => String(candidate?.id || candidate?.model || candidate?.name || '') === fallbackModel) || payload.models[0]
|
|
62
|
+
: payload?.model_metadata || payload?.metadata || payload;
|
|
63
|
+
const efforts = normalizeAdvertisedEfforts(row?.advertised_efforts || row?.advertisedEfforts || row?.reasoning_efforts || row?.reasoningEfforts || payload?.advertised_efforts);
|
|
64
|
+
return metadata(String(row?.model || row?.id || row?.name || fallbackModel), efforts, row?.default_effort || row?.defaultEffort || payload?.default_effort || 'medium', source, efforts.length ? [] : ['codex_model_metadata_efforts_missing']);
|
|
65
|
+
}
|
|
66
|
+
function metadata(model, efforts, defaultEffort, source, blockers) {
|
|
67
|
+
const advertised = normalizeAdvertisedEfforts(efforts);
|
|
68
|
+
const defaultValue = advertised.includes(defaultEffort) ? defaultEffort : advertised.includes('medium') ? 'medium' : advertised[0] || 'medium';
|
|
69
|
+
return {
|
|
70
|
+
schema: 'sks.codex-model-metadata.v1',
|
|
71
|
+
model,
|
|
72
|
+
advertised_efforts: advertised,
|
|
73
|
+
default_effort: defaultValue,
|
|
74
|
+
source,
|
|
75
|
+
blockers
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function normalizeAdvertisedEfforts(value) {
|
|
79
|
+
const rows = Array.isArray(value) ? value : String(value || '').split(',');
|
|
80
|
+
const seen = new Set();
|
|
81
|
+
const out = [];
|
|
82
|
+
for (const row of rows) {
|
|
83
|
+
const effort = String(row || '').trim().toLowerCase();
|
|
84
|
+
if (!effort || seen.has(effort))
|
|
85
|
+
continue;
|
|
86
|
+
seen.add(effort);
|
|
87
|
+
out.push(effort);
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=codex-model-metadata.js.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { readJson, writeJsonAtomic } from '../fsx.js';
|
|
3
|
+
import { buildCodexPluginInventory } from './codex-plugin-json.js';
|
|
4
|
+
export function codexPluginInventoryCachePath(root) {
|
|
5
|
+
return path.join(root, '.sneakoscope', 'cache', 'codex-plugin-inventory.json');
|
|
6
|
+
}
|
|
7
|
+
export async function readCodexPluginInventoryCache(root) {
|
|
8
|
+
const cache = await readJson(codexPluginInventoryCachePath(root), null);
|
|
9
|
+
return cache?.schema === 'sks.codex-plugin-inventory-cache.v1' ? cache : null;
|
|
10
|
+
}
|
|
11
|
+
export async function writeCodexPluginInventoryCache(root, inventory, ttlMs = defaultTtlMs()) {
|
|
12
|
+
const generatedAt = new Date();
|
|
13
|
+
const cache = {
|
|
14
|
+
schema: 'sks.codex-plugin-inventory-cache.v1',
|
|
15
|
+
generated_at: generatedAt.toISOString(),
|
|
16
|
+
expires_at: new Date(generatedAt.getTime() + ttlMs).toISOString(),
|
|
17
|
+
ttl_ms: ttlMs,
|
|
18
|
+
inventory
|
|
19
|
+
};
|
|
20
|
+
await writeJsonAtomic(codexPluginInventoryCachePath(root), cache);
|
|
21
|
+
return cache;
|
|
22
|
+
}
|
|
23
|
+
export async function getCodexPluginInventoryCached(root, opts = {}) {
|
|
24
|
+
const ttlMs = Math.max(1, Number(opts.ttlMs || defaultTtlMs()) || defaultTtlMs());
|
|
25
|
+
const cachePath = codexPluginInventoryCachePath(root);
|
|
26
|
+
const existing = opts.forceRefresh ? null : await readCodexPluginInventoryCache(root);
|
|
27
|
+
if (existing && Date.parse(existing.expires_at) > Date.now()) {
|
|
28
|
+
return { inventory: existing.inventory, cache_hit: true, cache_path: cachePath, cache: existing };
|
|
29
|
+
}
|
|
30
|
+
const inventory = await (opts.inventoryFactory || buildCodexPluginInventory)();
|
|
31
|
+
const cache = await writeCodexPluginInventoryCache(root, inventory, ttlMs);
|
|
32
|
+
return { inventory, cache_hit: false, cache_path: cachePath, cache };
|
|
33
|
+
}
|
|
34
|
+
function defaultTtlMs() {
|
|
35
|
+
const value = Number(process.env.SKS_CODEX_PLUGIN_CACHE_TTL_MS || 10 * 60 * 1000);
|
|
36
|
+
return Number.isFinite(value) && value > 0 ? value : 10 * 60 * 1000;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=codex-plugin-cache.js.map
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { writeJsonAtomic } from '../fsx.js';
|
|
3
|
+
export function diffCodexPluginInventories(previous, current) {
|
|
4
|
+
const prev = pluginMap(previous);
|
|
5
|
+
const next = pluginMap(current);
|
|
6
|
+
const added = [...next.keys()].filter((id) => !prev.has(id)).sort();
|
|
7
|
+
const removed = [...prev.keys()].filter((id) => !next.has(id)).sort();
|
|
8
|
+
const shared = [...next.keys()].filter((id) => prev.has(id));
|
|
9
|
+
const changedRemote = [];
|
|
10
|
+
const changedTemplates = [];
|
|
11
|
+
const changedPrompts = [];
|
|
12
|
+
const changedMetadata = [];
|
|
13
|
+
for (const id of shared) {
|
|
14
|
+
const before = prev.get(id);
|
|
15
|
+
const after = next.get(id);
|
|
16
|
+
if (!sameJson(normalizePluginMetadata(before), normalizePluginMetadata(after)))
|
|
17
|
+
changedMetadata.push(id);
|
|
18
|
+
if (!sameJson(normalizeRemoteServers(before?.remote_mcp_servers), normalizeRemoteServers(after?.remote_mcp_servers)))
|
|
19
|
+
changedRemote.push(id);
|
|
20
|
+
if (!sameJson(sorted(before?.unavailable_app_templates), sorted(after?.unavailable_app_templates)))
|
|
21
|
+
changedTemplates.push(id);
|
|
22
|
+
if (!sameJson(sorted(before?.default_prompts), sorted(after?.default_prompts)))
|
|
23
|
+
changedPrompts.push(id);
|
|
24
|
+
}
|
|
25
|
+
const changedCount = added.length + removed.length + changedMetadata.length + changedRemote.length + changedTemplates.length + changedPrompts.length;
|
|
26
|
+
return {
|
|
27
|
+
schema: 'sks.codex-plugin-inventory-diff.v1',
|
|
28
|
+
generated_at: new Date().toISOString(),
|
|
29
|
+
ok: true,
|
|
30
|
+
added_plugins: added,
|
|
31
|
+
removed_plugins: removed,
|
|
32
|
+
changed_remote_mcp_servers: changedRemote.sort(),
|
|
33
|
+
changed_unavailable_app_templates: changedTemplates.sort(),
|
|
34
|
+
changed_default_prompts: changedPrompts.sort(),
|
|
35
|
+
changed_plugin_metadata: changedMetadata.sort(),
|
|
36
|
+
changed_count: changedCount
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export async function writeCodexPluginInventoryDiff(root, previous, current) {
|
|
40
|
+
const diff = diffCodexPluginInventories(previous, current);
|
|
41
|
+
const artifact = path.join(root, '.sneakoscope', 'codex-plugin-inventory.diff.json');
|
|
42
|
+
await writeJsonAtomic(artifact, diff);
|
|
43
|
+
return { diff, artifact };
|
|
44
|
+
}
|
|
45
|
+
function pluginMap(inventory) {
|
|
46
|
+
const map = new Map();
|
|
47
|
+
for (const plugin of inventory?.plugins || [])
|
|
48
|
+
map.set(String(plugin.id || plugin.name), plugin);
|
|
49
|
+
return map;
|
|
50
|
+
}
|
|
51
|
+
function normalizeRemoteServers(rows) {
|
|
52
|
+
return (rows || []).map((row) => ({
|
|
53
|
+
name: row.name,
|
|
54
|
+
url: row.url,
|
|
55
|
+
auth_type: row.auth_type
|
|
56
|
+
})).sort((a, b) => `${a.name}:${a.url}:${a.auth_type}`.localeCompare(`${b.name}:${b.url}:${b.auth_type}`));
|
|
57
|
+
}
|
|
58
|
+
function normalizePluginMetadata(row) {
|
|
59
|
+
return {
|
|
60
|
+
id: row?.id || null,
|
|
61
|
+
name: row?.name || null,
|
|
62
|
+
source: row?.source || null,
|
|
63
|
+
installed: row?.installed === true,
|
|
64
|
+
enabled: row?.enabled === true
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function sorted(rows) {
|
|
68
|
+
return [...(rows || [])].map(String).sort();
|
|
69
|
+
}
|
|
70
|
+
function sameJson(a, b) {
|
|
71
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=codex-plugin-diff.js.map
|