sneakoscope 2.0.18 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +125 -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-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 +4 -0
- package/dist/core/commands/naruto-command.js +27 -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/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 +22 -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
|
|
@@ -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
|
|
@@ -19,14 +19,18 @@ export async function runCodexPluginDetailJson(pluginId) {
|
|
|
19
19
|
return runCodexJson(bin, ['plugin', 'detail', pluginId, '--json']);
|
|
20
20
|
}
|
|
21
21
|
export async function buildCodexPluginInventory() {
|
|
22
|
+
const started = Date.now();
|
|
22
23
|
const capability = await detectCodex0138Capability();
|
|
23
24
|
const listJson = await runCodexPluginListJson();
|
|
24
25
|
const summaries = normalizePluginList(listJson);
|
|
25
|
-
const
|
|
26
|
-
|
|
26
|
+
const concurrency = Math.max(1, Number(process.env.SKS_CODEX_PLUGIN_DETAIL_CONCURRENCY || 6) || 6);
|
|
27
|
+
let failed = 0;
|
|
28
|
+
const plugins = await mapWithConcurrency(summaries, concurrency, async (summary) => {
|
|
27
29
|
const detail = await runCodexPluginDetailJson(summary.id || summary.name).catch((err) => ({ error: err?.message || String(err) }));
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
if (detail?.error || normalizeList(detail?.blockers).length > 0)
|
|
31
|
+
failed += 1;
|
|
32
|
+
return normalizePlugin(summary, detail);
|
|
33
|
+
});
|
|
30
34
|
const blockers = [
|
|
31
35
|
...(capability.supports_plugin_json ? [] : ['codex_0_138_plugin_json_unavailable']),
|
|
32
36
|
...normalizeList(listJson?.blockers)
|
|
@@ -35,11 +39,29 @@ export async function buildCodexPluginInventory() {
|
|
|
35
39
|
schema: 'sks.codex-plugin-inventory.v1',
|
|
36
40
|
generated_at: nowIso(),
|
|
37
41
|
codex_0138_capability: capability,
|
|
42
|
+
fetch_concurrency: concurrency,
|
|
43
|
+
detail_fetch_count: summaries.length,
|
|
44
|
+
detail_fetch_failed_count: failed,
|
|
45
|
+
duration_ms: Date.now() - started,
|
|
38
46
|
plugins,
|
|
39
47
|
marketplace_available: plugins.some((plugin) => plugin.source === 'marketplace' || plugin.source === 'remote') || Boolean(listJson?.marketplace_available || listJson?.marketplaceAvailable),
|
|
40
48
|
blockers
|
|
41
49
|
};
|
|
42
50
|
}
|
|
51
|
+
export async function mapWithConcurrency(items, concurrency, fn) {
|
|
52
|
+
const limit = Math.max(1, Math.floor(concurrency || 1));
|
|
53
|
+
const results = new Array(items.length);
|
|
54
|
+
let next = 0;
|
|
55
|
+
async function worker() {
|
|
56
|
+
while (next < items.length) {
|
|
57
|
+
const index = next;
|
|
58
|
+
next += 1;
|
|
59
|
+
results[index] = await fn(items[index]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length || 1) }, () => worker()));
|
|
63
|
+
return results;
|
|
64
|
+
}
|
|
43
65
|
export async function writeCodexPluginInventoryArtifacts(root, inventory = null) {
|
|
44
66
|
const report = inventory || await buildCodexPluginInventory();
|
|
45
67
|
const artifact = path.join(root, '.sneakoscope', 'codex-plugin-inventory.json');
|
|
@@ -126,15 +148,17 @@ function boolish(value, fallback = false) {
|
|
|
126
148
|
return fallback;
|
|
127
149
|
}
|
|
128
150
|
function fakePluginList() {
|
|
151
|
+
const count = Math.max(1, Number(process.env.SKS_CODEX_PLUGIN_JSON_FAKE_COUNT || 1) || 1);
|
|
129
152
|
return {
|
|
130
153
|
marketplace_available: true,
|
|
131
|
-
plugins:
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
154
|
+
plugins: Array.from({ length: count }, (_, index) => ({
|
|
155
|
+
id: 'fixture-plugin',
|
|
156
|
+
name: index === 0 ? 'Fixture Plugin' : `Fixture Plugin ${index + 1}`,
|
|
157
|
+
...(index === 0 ? {} : { id: `fixture-plugin-${index + 1}` }),
|
|
158
|
+
source: 'marketplace',
|
|
159
|
+
installed: true,
|
|
160
|
+
enabled: true
|
|
161
|
+
}))
|
|
138
162
|
};
|
|
139
163
|
}
|
|
140
164
|
function fakePluginDetail(pluginId) {
|
|
@@ -37,6 +37,10 @@ export async function madHighCommand(args = [], deps = {}) {
|
|
|
37
37
|
process.exitCode = 1;
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
|
+
// Zellij is checked the same way Codex is, but it stays NON-blocking: a
|
|
41
|
+
// failed or skipped zellij upgrade never prevents the MAD launch.
|
|
42
|
+
const zellijUpdate = deps.maybePromptZellijUpdateForLaunch ? await deps.maybePromptZellijUpdateForLaunch(args, { label: 'MAD launch' }).catch(() => ({ status: 'error' })) : { status: 'skipped' };
|
|
43
|
+
void zellijUpdate;
|
|
40
44
|
const depStatus = deps.ensureMadLaunchDependencies ? await deps.ensureMadLaunchDependencies(args) : { ready: true, actions: [] };
|
|
41
45
|
if (!depStatus.ready) {
|
|
42
46
|
console.error('SKS MAD launch blocked by missing dependencies.');
|
|
@@ -7,6 +7,7 @@ import { buildNarutoCloneRoster, systemSafeNarutoConcurrency } from '../agents/a
|
|
|
7
7
|
import { DEFAULT_NARUTO_CLONES, MAX_NARUTO_AGENT_COUNT } from '../agents/agent-schema.js';
|
|
8
8
|
import { resolveOllamaWorkerConfig } from '../agents/ollama-worker-config.js';
|
|
9
9
|
import { attachZellijSessionInteractive, launchZellijLayout } from '../zellij/zellij-launcher.js';
|
|
10
|
+
import { maybePromptZellijUpdateForLaunch } from '../zellij/zellij-update.js';
|
|
10
11
|
import { buildNarutoWorkGraph } from '../naruto/naruto-work-graph.js';
|
|
11
12
|
import { buildNarutoRoleDistribution } from '../naruto/naruto-role-policy.js';
|
|
12
13
|
import { decideNarutoConcurrency } from '../naruto/naruto-concurrency-governor.js';
|
|
@@ -15,6 +16,7 @@ import { collectActualNarutoWorker, spawnActualNarutoWorker } from '../naruto/na
|
|
|
15
16
|
import { allocateNarutoTasksToWorkers } from '../naruto/naruto-allocation-policy.js';
|
|
16
17
|
import { rebalanceNarutoReadyWork } from '../naruto/naruto-rebalance-policy.js';
|
|
17
18
|
import { buildNarutoVerificationDag } from '../naruto/naruto-verification-dag.js';
|
|
19
|
+
import { evaluateNarutoFinalizer } from '../naruto/naruto-finalizer.js';
|
|
18
20
|
import { buildNarutoGptFinalPack } from '../naruto/naruto-gpt-final-pack.js';
|
|
19
21
|
import { planNarutoZellijDashboard } from '../zellij/zellij-naruto-dashboard.js';
|
|
20
22
|
import { checkPromptPlaceholders } from '../prompt/prompt-placeholder-guard.js';
|
|
@@ -41,6 +43,12 @@ export async function narutoCommand(commandOrArgs = 'naruto', maybeArgs = []) {
|
|
|
41
43
|
return narutoWorkers(parsed);
|
|
42
44
|
if (parsed.action === 'proof')
|
|
43
45
|
return narutoProof(parsed);
|
|
46
|
+
// Like the Codex CLI update prompt: check the installed zellij version and
|
|
47
|
+
// offer an upgrade to the latest stable release before the live session
|
|
48
|
+
// opens. Never blocks the run.
|
|
49
|
+
if (!parsed.json && !parsed.mock && !parsed.noOpenZellij) {
|
|
50
|
+
await maybePromptZellijUpdateForLaunch(args, { label: '$Naruto launch' }).catch(() => undefined);
|
|
51
|
+
}
|
|
44
52
|
return narutoRun(parsed);
|
|
45
53
|
}
|
|
46
54
|
async function narutoRun(parsed) {
|
|
@@ -299,6 +307,10 @@ async function narutoRun(parsed) {
|
|
|
299
307
|
console.log(' parallelism mode: ' + parsed.parallelism);
|
|
300
308
|
if (activeSlots < roster.agent_count)
|
|
301
309
|
console.log(' cap reasons: ' + (governor.reasons.join(', ') || 'host safety cap'));
|
|
310
|
+
// Backpressure used to throttle silently (50% when throttled, 25% when
|
|
311
|
+
// saturated); always tell the operator when host pressure reduced workers.
|
|
312
|
+
if (governor.backpressure !== 'normal')
|
|
313
|
+
console.log(' backpressure: ' + governor.backpressure + ' — host resource pressure reduced active workers (memory/cpu/fd/disk thresholds)');
|
|
302
314
|
if (parsed.parallelism !== 'safe' && activeSlots < 10)
|
|
303
315
|
console.log(' warning: active workers below 10 in non-safe mode');
|
|
304
316
|
}
|
|
@@ -418,6 +430,18 @@ async function narutoRun(parsed) {
|
|
|
418
430
|
});
|
|
419
431
|
const clones = result.roster?.agent_count ?? roster.agent_count;
|
|
420
432
|
const localWorkerSummary = summarizeNarutoLocalWorkerResult(localWorker, result);
|
|
433
|
+
// Finalizer policy: when local LLM workers contributed patches, the GPT
|
|
434
|
+
// final arbiter must have accepted before patches are considered final.
|
|
435
|
+
const finalizer = evaluateNarutoFinalizer({
|
|
436
|
+
localParticipated: Number(localWorkerSummary?.selected_worker_count || 0) > 0,
|
|
437
|
+
gptFinalStatus: result.proof?.gpt_final_status || null,
|
|
438
|
+
applyPatches: writeCapable
|
|
439
|
+
});
|
|
440
|
+
await writeJsonAtomic(path.join(mission.dir, 'naruto-finalizer.json'), {
|
|
441
|
+
...finalizer,
|
|
442
|
+
generated_at: nowIso(),
|
|
443
|
+
mission_id: mission.id
|
|
444
|
+
});
|
|
421
445
|
const summary = {
|
|
422
446
|
schema: NARUTO_RESULT_SCHEMA,
|
|
423
447
|
ok: result.ok === true,
|
|
@@ -477,6 +501,7 @@ async function narutoRun(parsed) {
|
|
|
477
501
|
passed: parallelRuntime.passed
|
|
478
502
|
} : null,
|
|
479
503
|
local_worker: localWorkerSummary,
|
|
504
|
+
finalizer,
|
|
480
505
|
proof: result.proof?.status || 'missing',
|
|
481
506
|
run: compactNarutoRunResult(result),
|
|
482
507
|
zellij: null
|
|
@@ -489,6 +514,8 @@ async function narutoRun(parsed) {
|
|
|
489
514
|
console.log('Backend: ' + result.backend);
|
|
490
515
|
console.log('Roles: ' + roleDistribution.entries.map((entry) => `${entry.role}:${entry.count}`).join(', '));
|
|
491
516
|
console.log('Proof: ' + summary.proof);
|
|
517
|
+
if (!finalizer.ok)
|
|
518
|
+
console.log('Finalizer: blocked — ' + finalizer.blockers.join(', '));
|
|
492
519
|
if (summary.parallel_runtime) {
|
|
493
520
|
console.log('$Naruto parallel proof:');
|
|
494
521
|
console.log(' max active workers: ' + summary.parallel_runtime.max_observed_active_workers);
|