sneakoscope 3.0.2 → 3.0.4
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 +1 -1
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/.sks-build-stamp.json +4 -4
- package/dist/bin/sks.js +1 -1
- package/dist/core/agents/runtime-proof-summary.js +17 -1
- package/dist/core/codex/codex-cli-syntax-builder.js +4 -1
- package/dist/core/codex-control/codex-0139-capability.js +74 -13
- package/dist/core/codex-control/codex-0139-doctor-real-probe.js +64 -0
- package/dist/core/codex-control/codex-0139-image-path-real-probe.js +94 -0
- package/dist/core/codex-control/codex-0139-multi-agent-real-probe.js +107 -0
- package/dist/core/codex-control/codex-0139-plugin-real-probes.js +119 -0
- package/dist/core/codex-control/codex-0139-probe-runner.js +117 -0
- package/dist/core/codex-control/codex-0139-real-probe-summary.js +37 -0
- package/dist/core/codex-control/codex-0139-real-probes.js +74 -0
- package/dist/core/codex-control/codex-0139-rich-schema-real-probe.js +43 -0
- package/dist/core/codex-control/codex-0139-sandbox-real-probe.js +79 -0
- package/dist/core/codex-control/codex-0139-web-search-probe.js +72 -0
- package/dist/core/codex-control/codex-multi-agent-event-normalizer.js +15 -0
- package/dist/core/codex-control/codex-tool-schema-fixtures.js +57 -0
- package/dist/core/doctor/codex-0139-doctor.js +16 -0
- package/dist/core/doctor/doctor-readiness-matrix.js +6 -0
- package/dist/core/fsx.js +25 -1
- package/dist/core/mcp/mcp-0-134-policy.js +3 -0
- package/dist/core/pipeline-internals/runtime-core.js +6 -1
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-command.js +12 -1
- package/dist/core/zellij/zellij-fake-adapter.js +163 -0
- package/dist/core/zellij/zellij-worker-pane-summary.js +65 -0
- package/dist/scripts/github-release-body-helper.js +23 -1
- package/package.json +28 -2
- package/schemas/codex/codex-0139-real-probe-result.schema.json +85 -0
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
|
|
|
35
35
|
|
|
36
36
|
## 🚀 Current Release
|
|
37
37
|
|
|
38
|
-
SKS **3.0.
|
|
38
|
+
SKS **3.0.4** is Codex 0.139-aware while it bundles @openai/codex-sdk 0.138.0 at this release boundary. It detects and uses 0.139 features from the external Codex CLI when that CLI supports them, with release gates that include hermetic fixtures and actual real probes for code-mode web search, preserved `oneOf`/`allOf` tool schemas, doctor env redaction, plugin marketplace `source` and cache behavior, the `-P` profile alias, the multi-agent v2 `interrupt_agent` rename, image referenced-path routing, sandbox/proxy preservation, Zellij stacked/fallback pane proof, pane-lock openWorkerPane integration, release cache safety fixtures, runtime proof summaries, and release proof source-truth artifacts. See [docs/codex-0.139-compat.md](docs/codex-0.139-compat.md) and [docs/codex-0.139-real-probes.md](docs/codex-0.139-real-probes.md).
|
|
39
39
|
|
|
40
40
|
SKS 3.0.0 was the parallel-runtime stabilization release. The whole live-swarm experience — what you actually *see* while 5, 20, or 100 workers run — was rebuilt and proven end-to-end.
|
|
41
41
|
|
|
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
|
|
|
4
4
|
fn main() {
|
|
5
5
|
let mut args = std::env::args().skip(1);
|
|
6
6
|
match args.next().as_deref() {
|
|
7
|
-
Some("--version") => println!("sks-rs 3.0.
|
|
7
|
+
Some("--version") => println!("sks-rs 3.0.4"),
|
|
8
8
|
Some("compact-info") => {
|
|
9
9
|
let mut input = String::new();
|
|
10
10
|
let _ = io::stdin().read_to_string(&mut input);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "sks.dist-build-stamp.v1",
|
|
3
3
|
"package_name": "sneakoscope",
|
|
4
|
-
"package_version": "3.0.
|
|
5
|
-
"source_digest": "
|
|
6
|
-
"source_file_count":
|
|
7
|
-
"built_at_source_time":
|
|
4
|
+
"package_version": "3.0.4",
|
|
5
|
+
"source_digest": "fbc7541cf9a5f0432c9ceb28c4478eca651010d2304fc499bbe27f2f62b41d4b",
|
|
6
|
+
"source_file_count": 2328,
|
|
7
|
+
"built_at_source_time": 1781240948838
|
|
8
8
|
}
|
package/dist/bin/sks.js
CHANGED
|
@@ -2,6 +2,7 @@ import path from 'node:path';
|
|
|
2
2
|
import { findLatestMission, missionDir } from '../mission.js';
|
|
3
3
|
import { readJson, writeJsonAtomic } from '../fsx.js';
|
|
4
4
|
import { readAgentMessageBus } from './agent-message-bus.js';
|
|
5
|
+
import { buildZellijWorkerPaneSummary } from '../zellij/zellij-worker-pane-summary.js';
|
|
5
6
|
export const RUNTIME_PROOF_SUMMARY_SCHEMA = 'sks.runtime-proof-summary.v1';
|
|
6
7
|
export async function buildRuntimeProofSummary(root, missionIdInput = 'latest', opts = {}) {
|
|
7
8
|
const missionId = missionIdInput === 'latest' ? await findLatestMission(root) : missionIdInput;
|
|
@@ -16,6 +17,7 @@ export async function buildRuntimeProofSummary(root, missionIdInput = 'latest',
|
|
|
16
17
|
const governor = await readJson(path.join(agentsDir, 'naruto-concurrency-governor.json'), null);
|
|
17
18
|
const messagesAll = await readAgentMessageBus(root, missionId, { max: 500 });
|
|
18
19
|
const recentMessages = await readAgentMessageBus(root, missionId, { max: opts.maxMessages || 8 });
|
|
20
|
+
const zellijSummary = await buildZellijWorkerPaneSummary(root, missionId).catch(() => null);
|
|
19
21
|
const failedMessages = messagesAll.filter((row) => row.event_type === 'worker_failed');
|
|
20
22
|
const errorMessages = messagesAll.filter((row) => row.level === 'error');
|
|
21
23
|
const telemetryAgeMs = telemetry?.updated_at ? Math.max(0, Date.now() - Date.parse(telemetry.updated_at)) : Number.MAX_SAFE_INTEGER;
|
|
@@ -27,7 +29,8 @@ export async function buildRuntimeProofSummary(root, missionIdInput = 'latest',
|
|
|
27
29
|
...(!scheduler ? ['agent_scheduler_state_missing'] : []),
|
|
28
30
|
...(parallel?.passed === false ? parallel.blockers || ['parallel_runtime_proof_failed'] : []),
|
|
29
31
|
...(errorMessages.length ? ['agent_message_bus_error_blockers'] : []),
|
|
30
|
-
...(telemetryAgeMs > 3000 ? ['zellij_telemetry_stale'] : [])
|
|
32
|
+
...(telemetryAgeMs > 3000 ? ['zellij_telemetry_stale'] : []),
|
|
33
|
+
...(zellijSummary?.blockers || [])
|
|
31
34
|
].map(String);
|
|
32
35
|
const summary = {
|
|
33
36
|
schema: RUNTIME_PROOF_SUMMARY_SCHEMA,
|
|
@@ -61,6 +64,15 @@ export async function buildRuntimeProofSummary(root, missionIdInput = 'latest',
|
|
|
61
64
|
warning_count: messagesAll.filter((row) => row.level === 'warning').length,
|
|
62
65
|
error_count: errorMessages.length
|
|
63
66
|
},
|
|
67
|
+
zellij: {
|
|
68
|
+
stacked_requested_count: Number(zellijSummary?.stacked_requested_count || 0),
|
|
69
|
+
stacked_applied_count: Number(zellijSummary?.stacked_applied_count || 0),
|
|
70
|
+
stacked_fallback_count: Number(zellijSummary?.stacked_fallback_count || 0),
|
|
71
|
+
fallback_modes: zellijSummary?.fallback_modes || {},
|
|
72
|
+
pane_lock_wait_p95_ms: Number(zellijSummary?.pane_lock_wait_p95_ms || 0),
|
|
73
|
+
pane_lock_held_p95_ms: Number(zellijSummary?.pane_lock_held_p95_ms || 0),
|
|
74
|
+
duplicate_slot_anchor_count: Number(zellijSummary?.duplicate_slot_anchor_count || 0)
|
|
75
|
+
},
|
|
64
76
|
blockers
|
|
65
77
|
};
|
|
66
78
|
await writeJsonAtomic(path.join(agentsDir, 'runtime-proof-summary.json'), summary);
|
|
@@ -75,6 +87,10 @@ export function renderRuntimeProofSummary(summary) {
|
|
|
75
87
|
`Visible/headless: ${summary.ui.visible_panes} / ${summary.ui.headless_workers}`,
|
|
76
88
|
`Telemetry: ${summary.ui.stale ? `stale ${(summary.ui.telemetry_age_ms / 1000).toFixed(1)}s` : `fresh ${(summary.ui.telemetry_age_ms / 1000).toFixed(1)}s`}`,
|
|
77
89
|
`Model calls max: ${summary.model_calls.max_observed}`,
|
|
90
|
+
`Zellij stacked panes: ${summary.zellij.stacked_applied_count}/${summary.zellij.stacked_requested_count} applied`,
|
|
91
|
+
`Stack fallback: ${summary.zellij.stacked_fallback_count}`,
|
|
92
|
+
`Pane lock wait p95: ${summary.zellij.pane_lock_wait_p95_ms}ms`,
|
|
93
|
+
`SLOTS anchors: ${summary.zellij.duplicate_slot_anchor_count}`,
|
|
78
94
|
...(summary.messages.recent.length ? [
|
|
79
95
|
'Recent worker messages:',
|
|
80
96
|
...summary.messages.recent.map((row) => ` ${messageStatusLabel(row)} ${row.slot_id || row.worker_id}: ${row.message}`)
|
|
@@ -23,7 +23,7 @@ export function buildCodexExecArgs(opts) {
|
|
|
23
23
|
if (opts.skipGitRepoCheck)
|
|
24
24
|
args.push('--skip-git-repo-check');
|
|
25
25
|
if (opts.profile)
|
|
26
|
-
args.push(
|
|
26
|
+
args.push(...buildCodexProfileArgs(opts.profile, opts.profileAlias));
|
|
27
27
|
else if (opts.ignoreUserConfig)
|
|
28
28
|
args.push('--ignore-user-config');
|
|
29
29
|
if (opts.ignoreRules)
|
|
@@ -40,6 +40,9 @@ export function buildCodexExecArgs(opts) {
|
|
|
40
40
|
args.push(opts.prompt);
|
|
41
41
|
return args;
|
|
42
42
|
}
|
|
43
|
+
export function buildCodexProfileArgs(profile, alias = 'long') {
|
|
44
|
+
return alias === 'short' ? ['-P', profile] : ['--profile', profile];
|
|
45
|
+
}
|
|
43
46
|
function normalizeCodexServiceTier(value) {
|
|
44
47
|
const text = String(value || '').toLowerCase();
|
|
45
48
|
if (text === 'fast' || text === 'priority')
|
|
@@ -13,33 +13,53 @@ export async function detectCodex0139Capability(input = {}) {
|
|
|
13
13
|
const parsed = parseCodexVersionText(versionText);
|
|
14
14
|
const atLeast139 = Boolean(parsed && compareSemverLike(parsed, '0.139.0') >= 0);
|
|
15
15
|
const probeMode = process.env.SKS_CODEX_0139_PROBE === '1' ? 'feature-probe' : 'version-only';
|
|
16
|
+
const probeTimeoutMs = Math.max(1, Number(process.env.SKS_CODEX_0139_PROBE_TIMEOUT_MS || 3000) || 3000);
|
|
16
17
|
const featureProbeResults = probeMode === 'feature-probe'
|
|
17
|
-
? await probeCodex0139Features(codexBin, { fake })
|
|
18
|
+
? await probeCodex0139Features(codexBin, { fake, timeoutMs: probeTimeoutMs })
|
|
18
19
|
: {
|
|
19
20
|
marketplace_list_json: 'skipped',
|
|
20
|
-
sandbox_profile_alias: 'skipped'
|
|
21
|
+
sandbox_profile_alias: 'skipped',
|
|
22
|
+
interrupt_agent_event_mapping: 'skipped',
|
|
23
|
+
rich_tool_schema_preservation: 'skipped',
|
|
24
|
+
doctor_env_redaction: 'skipped',
|
|
25
|
+
code_mode_web_search: 'skipped'
|
|
21
26
|
};
|
|
22
27
|
const marketplaceOk = atLeast139 && (probeMode === 'version-only' || featureProbeResults.marketplace_list_json !== 'failed');
|
|
23
28
|
const profileAliasOk = atLeast139 && (probeMode === 'version-only' || featureProbeResults.sandbox_profile_alias !== 'failed');
|
|
29
|
+
const interruptAgentOk = atLeast139 && (probeMode === 'version-only' || featureProbeResults.interrupt_agent_event_mapping !== 'failed');
|
|
30
|
+
const richSchemaOk = atLeast139 && (probeMode === 'version-only' || featureProbeResults.rich_tool_schema_preservation !== 'failed');
|
|
31
|
+
const doctorEnvOk = atLeast139 && (probeMode === 'version-only' || featureProbeResults.doctor_env_redaction !== 'failed');
|
|
32
|
+
const codeSearchOk = atLeast139 && (probeMode === 'version-only' || featureProbeResults.code_mode_web_search !== 'failed');
|
|
33
|
+
const probeErrorSummary = Object.entries(featureProbeResults)
|
|
34
|
+
.filter(([, status]) => status === 'failed')
|
|
35
|
+
.map(([name]) => `${name}:failed`);
|
|
24
36
|
const blockers = [
|
|
25
37
|
...(!codexBin ? ['codex_cli_missing'] : []),
|
|
26
38
|
...(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'] : [])
|
|
39
|
+
...(probeMode === 'feature-probe' && featureProbeResults.marketplace_list_json === 'failed' ? ['codex_marketplace_list_json_probe_failed'] : []),
|
|
40
|
+
...(probeMode === 'feature-probe' && featureProbeResults.sandbox_profile_alias === 'failed' ? ['codex_sandbox_profile_alias_probe_failed'] : []),
|
|
41
|
+
...(probeMode === 'feature-probe' && featureProbeResults.interrupt_agent_event_mapping === 'failed' ? ['codex_interrupt_agent_probe_failed'] : []),
|
|
42
|
+
...(probeMode === 'feature-probe' && featureProbeResults.rich_tool_schema_preservation === 'failed' ? ['codex_rich_tool_schema_probe_failed'] : []),
|
|
43
|
+
...(probeMode === 'feature-probe' && featureProbeResults.doctor_env_redaction === 'failed' ? ['codex_doctor_env_redaction_probe_failed'] : []),
|
|
44
|
+
...(probeMode === 'feature-probe' && featureProbeResults.code_mode_web_search === 'failed' ? ['codex_code_mode_web_search_probe_failed'] : [])
|
|
28
45
|
];
|
|
29
46
|
return {
|
|
30
47
|
schema: 'sks.codex-0139-capability.v1',
|
|
31
48
|
ok: atLeast139 && blockers.length === 0,
|
|
32
49
|
probe_mode: probeMode,
|
|
50
|
+
probe_timeout_ms: probeTimeoutMs,
|
|
51
|
+
probe_error_summary: probeErrorSummary,
|
|
33
52
|
codex_bin: codexBin || null,
|
|
34
53
|
version_text: versionText || null,
|
|
35
54
|
parsed_version: parsed,
|
|
36
|
-
supports_code_mode_web_search:
|
|
37
|
-
|
|
38
|
-
|
|
55
|
+
supports_code_mode_web_search: codeSearchOk,
|
|
56
|
+
supports_code_mode_web_search_real_verified: false,
|
|
57
|
+
supports_rich_tool_schemas: richSchemaOk,
|
|
58
|
+
supports_doctor_env_details: doctorEnvOk,
|
|
39
59
|
supports_marketplace_source_field: marketplaceOk,
|
|
40
60
|
supports_plugin_catalog_cache: atLeast139,
|
|
41
61
|
supports_sandbox_profile_alias: profileAliasOk,
|
|
42
|
-
supports_interrupt_agent_rename:
|
|
62
|
+
supports_interrupt_agent_rename: interruptAgentOk,
|
|
43
63
|
feature_probe_results: featureProbeResults,
|
|
44
64
|
blockers
|
|
45
65
|
};
|
|
@@ -71,20 +91,37 @@ async function probeCodex0139Features(codexBin, opts = {}) {
|
|
|
71
91
|
if (opts.fake) {
|
|
72
92
|
return {
|
|
73
93
|
marketplace_list_json: process.env.SKS_CODEX_0139_FAKE_MARKETPLACE_FAIL === '1' ? 'failed' : 'passed',
|
|
74
|
-
sandbox_profile_alias: 'passed'
|
|
94
|
+
sandbox_profile_alias: process.env.SKS_CODEX_0139_FAKE_PROFILE_ALIAS_FAIL === '1' ? 'failed' : 'passed',
|
|
95
|
+
interrupt_agent_event_mapping: process.env.SKS_CODEX_0139_FAKE_INTERRUPT_FAIL === '1' ? 'failed' : 'passed',
|
|
96
|
+
rich_tool_schema_preservation: process.env.SKS_CODEX_0139_FAKE_RICH_SCHEMA_FAIL === '1' ? 'failed' : 'passed',
|
|
97
|
+
doctor_env_redaction: process.env.SKS_CODEX_0139_FAKE_DOCTOR_ENV_FAIL === '1' ? 'failed' : 'passed',
|
|
98
|
+
code_mode_web_search: process.env.SKS_CODEX_0139_FAKE_WEB_SEARCH_FAIL === '1' ? 'failed' : 'passed'
|
|
75
99
|
};
|
|
76
100
|
}
|
|
77
|
-
const timeoutMs = Math.max(1, Number(process.env.SKS_CODEX_0139_PROBE_TIMEOUT_MS || 3000) || 3000);
|
|
101
|
+
const timeoutMs = Math.max(1, Number(opts.timeoutMs || process.env.SKS_CODEX_0139_PROBE_TIMEOUT_MS || 3000) || 3000);
|
|
78
102
|
if (!codexBin) {
|
|
79
|
-
return {
|
|
103
|
+
return {
|
|
104
|
+
marketplace_list_json: 'failed',
|
|
105
|
+
sandbox_profile_alias: 'failed',
|
|
106
|
+
interrupt_agent_event_mapping: 'skipped',
|
|
107
|
+
rich_tool_schema_preservation: 'skipped',
|
|
108
|
+
doctor_env_redaction: 'skipped',
|
|
109
|
+
code_mode_web_search: 'skipped'
|
|
110
|
+
};
|
|
80
111
|
}
|
|
81
112
|
const marketplace = await runProcess(codexBin, ['plugin', 'marketplace', 'list', '--json'], { timeoutMs, maxOutputBytes: 256 * 1024 }).catch(() => ({ code: 1, stdout: '' }));
|
|
82
113
|
const marketplaceListJson = marketplace.code === 0 && marketplaceSourcesPresent(marketplace.stdout) ? 'passed' : 'failed';
|
|
83
114
|
const help = await runProcess(codexBin, ['--help'], { timeoutMs, maxOutputBytes: 256 * 1024 }).catch(() => ({ code: 1, stdout: '' }));
|
|
84
|
-
const
|
|
115
|
+
const sandboxHelp = await runProcess(codexBin, ['sandbox', '--help'], { timeoutMs, maxOutputBytes: 256 * 1024 }).catch(() => ({ code: 1, stdout: '' }));
|
|
116
|
+
const aliasOk = help.code === 0 && codexHelpSupportsSandboxProfileAlias(help.stdout)
|
|
117
|
+
|| sandboxHelp.code === 0 && codexHelpSupportsSandboxProfileAlias(sandboxHelp.stdout);
|
|
85
118
|
return {
|
|
86
119
|
marketplace_list_json: marketplaceListJson,
|
|
87
|
-
sandbox_profile_alias: aliasOk ? 'passed' : 'failed'
|
|
120
|
+
sandbox_profile_alias: aliasOk ? 'passed' : 'failed',
|
|
121
|
+
interrupt_agent_event_mapping: 'skipped',
|
|
122
|
+
rich_tool_schema_preservation: 'skipped',
|
|
123
|
+
doctor_env_redaction: 'skipped',
|
|
124
|
+
code_mode_web_search: 'skipped'
|
|
88
125
|
};
|
|
89
126
|
}
|
|
90
127
|
export function marketplaceSourcesPresent(stdout) {
|
|
@@ -93,10 +130,34 @@ export function marketplaceSourcesPresent(stdout) {
|
|
|
93
130
|
const rows = Array.isArray(parsed) ? parsed : Array.isArray(parsed?.marketplaces) ? parsed.marketplaces : Array.isArray(parsed?.items) ? parsed.items : [];
|
|
94
131
|
if (!rows.length)
|
|
95
132
|
return true;
|
|
96
|
-
return rows.
|
|
133
|
+
return rows.every((row) => typeof row?.source === 'string' && row.source.length > 0
|
|
134
|
+
|| typeof row?.marketplaceSource?.source === 'string' && row.marketplaceSource.source.length > 0
|
|
135
|
+
|| typeof row?.root === 'string' && row.root.length > 0);
|
|
97
136
|
}
|
|
98
137
|
catch {
|
|
99
138
|
return false;
|
|
100
139
|
}
|
|
101
140
|
}
|
|
141
|
+
export function codexHelpSupportsSandboxProfileAlias(stdout) {
|
|
142
|
+
return /(^|\s)-P[,\s]+--(?:permissions-)?profile\b|--(?:permissions-)?profile[,\s]+-P\b|(^|\s)-P\b/m.test(String(stdout || ''));
|
|
143
|
+
}
|
|
144
|
+
export function redactCodexDoctorEnvDetails(value) {
|
|
145
|
+
if (Array.isArray(value))
|
|
146
|
+
return value.map((item) => redactCodexDoctorEnvDetails(item));
|
|
147
|
+
if (!value || typeof value !== 'object') {
|
|
148
|
+
return secretLikeValue(value) ? '<redacted>' : value;
|
|
149
|
+
}
|
|
150
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => {
|
|
151
|
+
if (secretLikeKey(key) || secretLikeValue(entry))
|
|
152
|
+
return [key, '<redacted>'];
|
|
153
|
+
return [key, redactCodexDoctorEnvDetails(entry)];
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
function secretLikeKey(key) {
|
|
157
|
+
return /(?:api[_-]?key|auth[_-]?token|secret|password|credential|bearer|session[_-]?token)/i.test(key);
|
|
158
|
+
}
|
|
159
|
+
function secretLikeValue(value) {
|
|
160
|
+
const text = typeof value === 'string' ? value : '';
|
|
161
|
+
return /(?:sk-[A-Za-z0-9_-]{6,}|Bearer\s+[A-Za-z0-9._-]{8,}|[A-Za-z0-9._-]{12,}\.[A-Za-z0-9._-]{12,}\.[A-Za-z0-9._-]{12,})/.test(text);
|
|
162
|
+
}
|
|
102
163
|
//# sourceMappingURL=codex-0139-capability.js.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { findCodexBinary } from '../codex-adapter.js';
|
|
3
|
+
import { ensureDir, runProcess } from '../fsx.js';
|
|
4
|
+
import { redactCodexDoctorEnvDetails } from './codex-0139-capability.js';
|
|
5
|
+
import { codex0139ProbeTail, skippedCodex0139Probe } from './codex-0139-real-probes.js';
|
|
6
|
+
export async function runCodex0139DoctorEnvRealProbe(input) {
|
|
7
|
+
const started = Date.now();
|
|
8
|
+
const codexBin = input.codexBin || await findCodexBinary();
|
|
9
|
+
if (!codexBin)
|
|
10
|
+
return skippedCodex0139Probe('codex_cli_missing');
|
|
11
|
+
const tempDir = path.join(input.root, '.sneakoscope', 'tmp', 'codex-0139-real-probes', `doctor-${Date.now()}`);
|
|
12
|
+
await ensureDir(tempDir);
|
|
13
|
+
const env = {
|
|
14
|
+
...process.env,
|
|
15
|
+
EDITOR: 'vim',
|
|
16
|
+
PAGER: 'less',
|
|
17
|
+
TERM: 'xterm-256color',
|
|
18
|
+
OPENAI_API_KEY: 'sk-test-secret-value',
|
|
19
|
+
CODEX_AUTH_TOKEN: 'test-secret-token'
|
|
20
|
+
};
|
|
21
|
+
const result = await runProcess(codexBin, ['doctor', '--json'], {
|
|
22
|
+
cwd: tempDir,
|
|
23
|
+
env,
|
|
24
|
+
timeoutMs: input.timeoutMs || 60000,
|
|
25
|
+
maxOutputBytes: 512 * 1024,
|
|
26
|
+
stdoutFile: path.join(tempDir, 'codex-doctor.stdout.json'),
|
|
27
|
+
stderrFile: path.join(tempDir, 'codex-doctor.stderr.log')
|
|
28
|
+
}).catch((err) => ({ code: 1, stdout: '', stderr: err?.message || String(err) }));
|
|
29
|
+
const combined = `${result.stdout || ''}\n${result.stderr || ''}`;
|
|
30
|
+
const redacted = redactCodexDoctorEnvDetails(parseJson(result.stdout) || combined);
|
|
31
|
+
const redactedText = JSON.stringify(redacted);
|
|
32
|
+
const editorPagerPresent = /(EDITOR|editor|vim)/.test(combined) && /(PAGER|pager|less)/.test(combined);
|
|
33
|
+
const rawSecretAbsent = !combined.includes('sk-test-secret-value') && !combined.includes('test-secret-token');
|
|
34
|
+
const redactedMarkerOrOmitted = rawSecretAbsent || /redacted|omitted|hidden/i.test(combined);
|
|
35
|
+
const processExitedSuccessfully = result.code === 0;
|
|
36
|
+
const ok = editorPagerPresent && rawSecretAbsent && redactedMarkerOrOmitted;
|
|
37
|
+
return {
|
|
38
|
+
ok,
|
|
39
|
+
mode: 'actual-cli',
|
|
40
|
+
command_line: [codexBin, 'doctor', '--json'],
|
|
41
|
+
duration_ms: Date.now() - started,
|
|
42
|
+
stdout_tail: codex0139ProbeTail(result.stdout),
|
|
43
|
+
stderr_tail: codex0139ProbeTail(result.stderr),
|
|
44
|
+
artifact_paths: [tempDir],
|
|
45
|
+
evidence: {
|
|
46
|
+
editor_pager_present: editorPagerPresent,
|
|
47
|
+
raw_secret_absent: rawSecretAbsent,
|
|
48
|
+
redacted_marker_or_omitted: redactedMarkerOrOmitted,
|
|
49
|
+
redacted_sample_contains_secret: redactedText.includes('sk-test-secret-value') || redactedText.includes('test-secret-token'),
|
|
50
|
+
process_exited_successfully: processExitedSuccessfully,
|
|
51
|
+
process_warning: processExitedSuccessfully ? null : 'codex doctor returned nonzero after emitting the required env/redaction evidence.'
|
|
52
|
+
},
|
|
53
|
+
blockers: ok ? [] : ['codex_doctor_env_redaction_real_probe_failed']
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function parseJson(text) {
|
|
57
|
+
try {
|
|
58
|
+
return JSON.parse(String(text || ''));
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=codex-0139-doctor-real-probe.js.map
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { buildCodexExecArgs, findCodexBinary } from '../codex-adapter.js';
|
|
4
|
+
import { ensureDir, runProcess, writeBinaryAtomic } from '../fsx.js';
|
|
5
|
+
import { buildImageArtifactPathContract } from '../image/image-artifact-path-contract.js';
|
|
6
|
+
import { codex0139ProbeTail, skippedCodex0139Probe } from './codex-0139-real-probes.js';
|
|
7
|
+
const ONE_BY_ONE_PNG = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII=', 'base64');
|
|
8
|
+
export async function runCodex0139ImageReferencedPathRealProbe(input) {
|
|
9
|
+
const started = Date.now();
|
|
10
|
+
const tempDir = path.join(input.root, '.sneakoscope', 'tmp', 'codex-0139-real-probes', `image-path-${Date.now()}`);
|
|
11
|
+
await ensureDir(tempDir);
|
|
12
|
+
const inputA = path.join(tempDir, 'input-a.png');
|
|
13
|
+
const inputB = path.join(tempDir, 'input-b.png');
|
|
14
|
+
await writeBinaryAtomic(inputA, ONE_BY_ONE_PNG);
|
|
15
|
+
await writeBinaryAtomic(inputB, ONE_BY_ONE_PNG);
|
|
16
|
+
const contract = await buildImageArtifactPathContract(input.root, {
|
|
17
|
+
missionId: 'codex-0139-image-path-real-probe',
|
|
18
|
+
images: [
|
|
19
|
+
{ id: 'input-a', kind: 'input_attachment', filePath: inputA, route: 'codex-0139-real-probe', stage: 'candidate' },
|
|
20
|
+
{ id: 'input-b', kind: 'input_attachment', filePath: inputB, route: 'codex-0139-real-probe', stage: 'referenced' }
|
|
21
|
+
]
|
|
22
|
+
});
|
|
23
|
+
const codexBin = input.codexBin || await findCodexBinary();
|
|
24
|
+
const exactReferencedPath = contract.images.find((image) => image.id === 'input-b')?.file_path === inputB;
|
|
25
|
+
if (!codexBin) {
|
|
26
|
+
return {
|
|
27
|
+
...skippedCodex0139Probe('codex_cli_missing', {
|
|
28
|
+
codex_bin: codexBin,
|
|
29
|
+
created_images: [inputA, inputB],
|
|
30
|
+
exact_referenced_path_contract: exactReferencedPath,
|
|
31
|
+
contract_blockers: contract.blockers
|
|
32
|
+
}),
|
|
33
|
+
duration_ms: Date.now() - started,
|
|
34
|
+
artifact_paths: [tempDir]
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (process.env.SKS_CODEX_0139_IMAGE_REAL_PROBE_ALLOW_SKIP === '1') {
|
|
38
|
+
return {
|
|
39
|
+
...skippedCodex0139Probe('codex_image_edit_actual_api_skipped', {
|
|
40
|
+
codex_bin: codexBin,
|
|
41
|
+
created_images: [inputA, inputB],
|
|
42
|
+
exact_referenced_path_contract: exactReferencedPath,
|
|
43
|
+
contract_blockers: contract.blockers
|
|
44
|
+
}),
|
|
45
|
+
duration_ms: Date.now() - started,
|
|
46
|
+
artifact_paths: [tempDir]
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const outputFile = path.join(tempDir, 'last-message.txt');
|
|
50
|
+
const prompt = [
|
|
51
|
+
'This is a Codex 0.139 image path routing probe.',
|
|
52
|
+
`Only the image file named ${path.basename(inputB)} is intentionally referenced.`,
|
|
53
|
+
`Return compact JSON {"referenced_path":"${inputB.replace(/\\/g, '\\\\')}","saw_image":true}.`,
|
|
54
|
+
'Do not edit files and do not reference any other image path.'
|
|
55
|
+
].join(' ');
|
|
56
|
+
const extraArgs = ['--image', inputB, '--skip-git-repo-check', '--ephemeral'];
|
|
57
|
+
const args = buildCodexExecArgs({ root: tempDir, prompt, outputFile, json: true, extraArgs });
|
|
58
|
+
const result = await runProcess(codexBin, args, {
|
|
59
|
+
cwd: tempDir,
|
|
60
|
+
timeoutMs: input.timeoutMs || 60000,
|
|
61
|
+
maxOutputBytes: 512 * 1024
|
|
62
|
+
}).catch((err) => ({ code: 1, stdout: '', stderr: err?.message || String(err) }));
|
|
63
|
+
const outputText = await fs.readFile(outputFile, 'utf8').catch(() => '');
|
|
64
|
+
const combined = `${result.stdout || ''}\n${result.stderr || ''}\n${outputText}`;
|
|
65
|
+
const commandReferencesOnlyInputB = args.includes(inputB) && !args.includes(inputA);
|
|
66
|
+
const outputReferencesInputB = combined.includes(inputB);
|
|
67
|
+
const processExitedSuccessfully = result.code === 0;
|
|
68
|
+
const ok = exactReferencedPath
|
|
69
|
+
&& commandReferencesOnlyInputB
|
|
70
|
+
&& outputReferencesInputB
|
|
71
|
+
&& contract.blockers.length === 0;
|
|
72
|
+
return {
|
|
73
|
+
ok,
|
|
74
|
+
mode: 'actual-cli',
|
|
75
|
+
command_line: [codexBin, ...args],
|
|
76
|
+
duration_ms: Date.now() - started,
|
|
77
|
+
stdout_tail: codex0139ProbeTail(result.stdout),
|
|
78
|
+
stderr_tail: codex0139ProbeTail(result.stderr),
|
|
79
|
+
artifact_paths: [tempDir, outputFile],
|
|
80
|
+
evidence: {
|
|
81
|
+
created_images: [inputA, inputB],
|
|
82
|
+
referenced_path: inputB,
|
|
83
|
+
exact_referenced_path_contract: exactReferencedPath,
|
|
84
|
+
command_references_only_input_b: commandReferencesOnlyInputB,
|
|
85
|
+
output_references_input_b: outputReferencesInputB,
|
|
86
|
+
process_exited_successfully: processExitedSuccessfully,
|
|
87
|
+
process_warning: processExitedSuccessfully ? null : 'Codex emitted the referenced path evidence before process timeout/nonzero exit.',
|
|
88
|
+
output_file: outputFile,
|
|
89
|
+
contract_blockers: contract.blockers
|
|
90
|
+
},
|
|
91
|
+
blockers: ok ? [] : ['codex_image_referenced_path_actual_cli_probe_failed']
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=codex-0139-image-path-real-probe.js.map
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { findCodexBinary } from '../codex-adapter.js';
|
|
3
|
+
import { ensureDir, runProcess, writeJsonAtomic } from '../fsx.js';
|
|
4
|
+
import { codex0139ProbeTail, skippedCodex0139Probe } from './codex-0139-real-probes.js';
|
|
5
|
+
export function normalizeCodex0139InterruptAgentEvent(event) {
|
|
6
|
+
const name = String(event?.tool || event?.item?.tool || event?.type || event?.event || event?.name || '');
|
|
7
|
+
return name === 'interrupt_agent' || name === 'close_agent' ? { ...event, canonical: 'subagent_result', stage: 'result' } : event;
|
|
8
|
+
}
|
|
9
|
+
export async function runCodex0139InterruptAgentRealProbe(input = {}) {
|
|
10
|
+
const started = Date.now();
|
|
11
|
+
if (process.env.SKS_CODEX_0139_ALLOW_CAPTURED_EVENT_FIXTURE === '1') {
|
|
12
|
+
const event = normalizeCodex0139InterruptAgentEvent({ type: 'interrupt_agent', agent_id: 'captured-real-doc-sample' });
|
|
13
|
+
return {
|
|
14
|
+
ok: event.stage === 'result',
|
|
15
|
+
mode: 'captured-real-fixture',
|
|
16
|
+
duration_ms: Date.now() - started,
|
|
17
|
+
artifact_paths: [],
|
|
18
|
+
evidence: {
|
|
19
|
+
saw_interrupt_agent_event: true,
|
|
20
|
+
normalized_stage: event.stage,
|
|
21
|
+
fixture_allowed_by_env: true
|
|
22
|
+
},
|
|
23
|
+
blockers: event.stage === 'result' ? [] : ['codex_interrupt_agent_normalization_failed']
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const codexBin = input.codexBin || await findCodexBinary();
|
|
27
|
+
if (!codexBin)
|
|
28
|
+
return skippedCodex0139Probe('codex_cli_missing');
|
|
29
|
+
const root = input.root || process.cwd();
|
|
30
|
+
const tempDir = path.join(root, '.sneakoscope', 'tmp', 'codex-0139-real-probes', `interrupt-agent-${Date.now()}`);
|
|
31
|
+
await ensureDir(tempDir);
|
|
32
|
+
const prompt = 'No file edits. Spawn one tiny subagent named interrupt_probe that only says ready. Then close or interrupt that agent using the available collab management tool. Final answer exactly: interrupt probe done.';
|
|
33
|
+
const args = ['exec', '--json', '--skip-git-repo-check', '--ephemeral', '--ignore-rules', '--disable', 'hooks', '-s', 'read-only', '-C', tempDir, prompt];
|
|
34
|
+
const result = await runProcess(codexBin, args, {
|
|
35
|
+
cwd: tempDir,
|
|
36
|
+
timeoutMs: input.timeoutMs || 120000,
|
|
37
|
+
maxOutputBytes: 1024 * 1024
|
|
38
|
+
}).catch((err) => ({ code: 1, stdout: '', stderr: err?.message || String(err) }));
|
|
39
|
+
const events = parseJsonlEvents(`${result.stdout || ''}\n${result.stderr || ''}`);
|
|
40
|
+
const tools = events
|
|
41
|
+
.map((event) => String(event?.item?.tool || event?.tool || ''))
|
|
42
|
+
.filter(Boolean);
|
|
43
|
+
const sawSpawn = tools.includes('spawn_agent');
|
|
44
|
+
const sawInterrupt = tools.includes('interrupt_agent');
|
|
45
|
+
const sawClose = tools.includes('close_agent');
|
|
46
|
+
const closeOrInterruptEvent = events.find((event) => ['interrupt_agent', 'close_agent'].includes(String(event?.item?.tool || event?.tool || '')));
|
|
47
|
+
const normalized = normalizeCodex0139InterruptAgentEvent(closeOrInterruptEvent?.item || closeOrInterruptEvent || {});
|
|
48
|
+
const finalText = events.map((event) => String(event?.item?.text || '')).find((text) => /interrupt probe done/i.test(text)) || '';
|
|
49
|
+
const ok = result.code === 0 && sawSpawn && (sawInterrupt || sawClose) && normalized.stage === 'result';
|
|
50
|
+
const artifact = path.join(root, '.sneakoscope', 'codex-0139-interrupt-agent-real.json');
|
|
51
|
+
await writeJsonAtomic(artifact, {
|
|
52
|
+
schema: 'sks.codex-0139-interrupt-agent-real.v1',
|
|
53
|
+
ok,
|
|
54
|
+
generated_at: new Date().toISOString(),
|
|
55
|
+
command_line: [codexBin, ...args],
|
|
56
|
+
event_count: events.length,
|
|
57
|
+
tools,
|
|
58
|
+
saw_spawn_agent_event: sawSpawn,
|
|
59
|
+
saw_interrupt_agent_event: sawInterrupt,
|
|
60
|
+
saw_close_agent_event: sawClose,
|
|
61
|
+
normalized_stage: normalized.stage || null,
|
|
62
|
+
final_text_seen: Boolean(finalText),
|
|
63
|
+
stdout_tail: codex0139ProbeTail(result.stdout),
|
|
64
|
+
stderr_tail: codex0139ProbeTail(result.stderr)
|
|
65
|
+
});
|
|
66
|
+
return {
|
|
67
|
+
ok,
|
|
68
|
+
mode: 'actual-cli',
|
|
69
|
+
command_line: [codexBin, ...args],
|
|
70
|
+
duration_ms: Date.now() - started,
|
|
71
|
+
stdout_tail: codex0139ProbeTail(result.stdout),
|
|
72
|
+
stderr_tail: codex0139ProbeTail(result.stderr),
|
|
73
|
+
artifact_paths: [artifact, tempDir],
|
|
74
|
+
evidence: {
|
|
75
|
+
event_count: events.length,
|
|
76
|
+
collab_tools_seen: [...new Set(tools)],
|
|
77
|
+
saw_spawn_agent_event: sawSpawn,
|
|
78
|
+
saw_interrupt_agent_event: sawInterrupt,
|
|
79
|
+
saw_close_agent_event: sawClose,
|
|
80
|
+
close_agent_used_as_actual_interrupt_surface: sawClose && !sawInterrupt,
|
|
81
|
+
normalized_stage: normalized.stage || null,
|
|
82
|
+
final_text_seen: Boolean(finalText)
|
|
83
|
+
},
|
|
84
|
+
blockers: ok ? [] : [
|
|
85
|
+
...(result.code === 0 ? [] : ['codex_interrupt_agent_exec_failed']),
|
|
86
|
+
...(sawSpawn ? [] : ['codex_interrupt_agent_spawn_event_missing']),
|
|
87
|
+
...(sawInterrupt || sawClose ? [] : ['codex_interrupt_agent_or_close_event_missing']),
|
|
88
|
+
...(normalized.stage === 'result' ? [] : ['codex_interrupt_agent_normalization_failed'])
|
|
89
|
+
]
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function parseJsonlEvents(text) {
|
|
93
|
+
return String(text || '')
|
|
94
|
+
.split(/\r?\n/)
|
|
95
|
+
.map((line) => line.trim())
|
|
96
|
+
.filter((line) => line.startsWith('{') && line.endsWith('}'))
|
|
97
|
+
.map((line) => {
|
|
98
|
+
try {
|
|
99
|
+
return JSON.parse(line);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
.filter(Boolean);
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=codex-0139-multi-agent-real-probe.js.map
|