sneakoscope 3.0.3 → 3.1.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 +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/cli/command-registry.js +1 -0
- package/dist/cli/context7-command.js +29 -5
- package/dist/cli/install-helpers.js +15 -7
- package/dist/core/agents/runtime-proof-summary.js +4 -0
- package/dist/core/codex-control/codex-0139-capability.js +8 -3
- 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/commands/goal-command.js +19 -1
- package/dist/core/commands/loop-command.js +135 -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/init.js +6 -1
- package/dist/core/loops/goal-to-loop-compat.js +23 -0
- package/dist/core/loops/loop-artifacts.js +41 -0
- package/dist/core/loops/loop-decomposer.js +56 -0
- package/dist/core/loops/loop-finalizer.js +28 -0
- package/dist/core/loops/loop-gate-ladder.js +16 -0
- package/dist/core/loops/loop-gate-runner.js +29 -0
- package/dist/core/loops/loop-gate-selector.js +52 -0
- package/dist/core/loops/loop-iteration-runner.js +2 -0
- package/dist/core/loops/loop-lease.js +76 -0
- package/dist/core/loops/loop-observability.js +19 -0
- package/dist/core/loops/loop-owner-inference.js +57 -0
- package/dist/core/loops/loop-owner-ledger.js +2 -0
- package/dist/core/loops/loop-planner.js +139 -0
- package/dist/core/loops/loop-proof-summary.js +10 -0
- package/dist/core/loops/loop-proof.js +2 -0
- package/dist/core/loops/loop-risk-classifier.js +42 -0
- package/dist/core/loops/loop-runtime.js +159 -0
- package/dist/core/loops/loop-scheduler.js +60 -0
- package/dist/core/loops/loop-schema.js +63 -0
- package/dist/core/loops/loop-state.js +61 -0
- package/dist/core/naruto/naruto-loop-mesh.js +33 -0
- package/dist/core/naruto/naruto-loop-worker-router.js +38 -0
- package/dist/core/pipeline-internals/runtime-core.js +82 -2
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-slot-column-anchor.js +5 -2
- package/dist/core/zellij/zellij-slot-pane-renderer.js +2 -0
- package/dist/scripts/github-release-body-helper.js +3 -1
- package/dist/scripts/loop-directive-check-lib.js +165 -0
- package/package.json +47 -3
- package/schemas/codex/codex-0139-real-probe-result.schema.json +85 -0
- package/schemas/loops/loop-node.schema.json +21 -0
- package/schemas/loops/loop-plan.schema.json +21 -0
- package/schemas/loops/loop-proof.schema.json +20 -0
- package/schemas/loops/loop-state.schema.json +19 -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.1.0** 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.1.0"),
|
|
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.1.0",
|
|
5
|
+
"source_digest": "0eed1c5f95e3d3e67d263680e28b27bfd1dabe232ff7265720a57d7d377252e8",
|
|
6
|
+
"source_file_count": 2392,
|
|
7
|
+
"built_at_source_time": 1781256416238
|
|
8
8
|
}
|
package/dist/bin/sks.js
CHANGED
|
@@ -123,6 +123,7 @@ export const COMMANDS = {
|
|
|
123
123
|
agent: entry('beta', 'Run native multi-session agent missions', 'dist/core/commands/agent-command.js', argsCommand(() => import('../core/commands/agent-command.js'), 'agentCommand', 'dist/core/commands/agent-command.js')),
|
|
124
124
|
'with-local-llm': entry('beta', 'Enable or inspect local Ollama worker backend', 'dist/core/commands/local-model-command.js', argsCommand(() => import('../core/commands/local-model-command.js'), 'localModelCommand', 'dist/core/commands/local-model-command.js')),
|
|
125
125
|
naruto: entry('labs', 'Run $Naruto shadow-clone swarm (up to 100 parallel sessions)', 'dist/core/commands/naruto-command.js', argsCommand(() => import('../core/commands/naruto-command.js'), 'narutoCommand', 'dist/core/commands/naruto-command.js')),
|
|
126
|
+
loop: entry('labs', 'Dynamic Loop Runtime: plan/run/status/proof loop graphs.', 'dist/core/commands/loop-command.js', subcommand(() => import('../core/commands/loop-command.js'), 'loopCommand', 'dist/core/commands/loop-command.js', 'help')),
|
|
126
127
|
'qa-loop': entry('beta', 'Run QA loop missions', 'dist/core/commands/qa-loop-command.js', subcommand(() => import('../core/commands/qa-loop-command.js'), 'qaLoopCommand', 'dist/core/commands/qa-loop-command.js')),
|
|
127
128
|
research: entry('labs', 'Run research missions', 'dist/core/commands/research-command.js', subcommand(() => import('../core/commands/research-command.js'), 'researchCommand', 'dist/core/commands/research-command.js')),
|
|
128
129
|
autoresearch: entry('labs', 'Alias for research/autoresearch route', 'dist/core/commands/autoresearch-command.js', subcommand(() => import('../core/commands/autoresearch-command.js'), 'autoresearchCommand', 'dist/core/commands/autoresearch-command.js', 'status')),
|
|
@@ -3,7 +3,7 @@ import { getCodexInfo } from '../core/codex-adapter.js';
|
|
|
3
3
|
import { context7Docs, context7Resolve, context7Text, context7Tools } from '../core/context7-client.js';
|
|
4
4
|
import { context7Evidence, recordContext7Evidence } from '../core/pipeline.js';
|
|
5
5
|
import { stateFile } from '../core/mission.js';
|
|
6
|
-
import { checkContext7, ensureProjectContext7Config } from './install-helpers.js';
|
|
6
|
+
import { checkContext7, context7GlobalMcpStatus, ensureProjectContext7Config } from './install-helpers.js';
|
|
7
7
|
const flag = (args, name) => args.includes(name);
|
|
8
8
|
export async function context7Command(sub = 'check', args = []) {
|
|
9
9
|
const action = sub || 'check';
|
|
@@ -92,9 +92,24 @@ export async function context7Command(sub = 'check', args = []) {
|
|
|
92
92
|
timeoutMs: readNumberOption(args, '--timeout-ms', 30000)
|
|
93
93
|
});
|
|
94
94
|
const state = { ...(await readJson(stateFile(root), {})), mission_id: missionId };
|
|
95
|
-
|
|
95
|
+
const evidenceQuery = readOption(args, '--query', readOption(args, '--topic', libraryNameOrId));
|
|
96
|
+
const evidenceTopic = readOption(args, '--topic', libraryNameOrId);
|
|
97
|
+
await recordContext7Evidence(root, state, {
|
|
98
|
+
tool_name: 'resolve-library-id',
|
|
99
|
+
library: libraryNameOrId,
|
|
100
|
+
library_id: result.library_id,
|
|
101
|
+
query: evidenceQuery,
|
|
102
|
+
source: result.resolve ? 'sks context7 evidence' : 'sks context7 evidence explicit-library-id'
|
|
103
|
+
});
|
|
96
104
|
if (result.docs_tool) {
|
|
97
|
-
await recordContext7Evidence(root, state, {
|
|
105
|
+
await recordContext7Evidence(root, state, {
|
|
106
|
+
tool_name: result.docs_tool,
|
|
107
|
+
library_id: result.library_id,
|
|
108
|
+
query: evidenceQuery,
|
|
109
|
+
topic: evidenceTopic,
|
|
110
|
+
tokens: readNumberOption(args, '--tokens', 2000),
|
|
111
|
+
source: 'sks context7 evidence'
|
|
112
|
+
});
|
|
98
113
|
}
|
|
99
114
|
const evidence = await context7Evidence(root, state);
|
|
100
115
|
const out = { ...result, mission_id: missionId, evidence };
|
|
@@ -126,12 +141,21 @@ export async function context7Command(sub = 'check', args = []) {
|
|
|
126
141
|
const codex = await getCodexInfo();
|
|
127
142
|
if (!codex.bin)
|
|
128
143
|
throw new Error('Codex CLI missing. Install separately: npm i -g @openai/codex, or set SKS_CODEX_BIN.');
|
|
144
|
+
const env = { ...process.env };
|
|
145
|
+
env.CODEX_LB_API_KEY = '';
|
|
146
|
+
const existing = await context7GlobalMcpStatus(codex.bin, env);
|
|
147
|
+
if (existing.present) {
|
|
148
|
+
if (flag(args, '--json'))
|
|
149
|
+
return console.log(JSON.stringify({ changed: false, status: 'present', codex_mcp_list: existing }, null, 2));
|
|
150
|
+
console.log('Context7 global MCP already configured; existing entry preserved.');
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
129
153
|
const cmdArgs = transport === 'remote'
|
|
130
154
|
? ['mcp', 'add', 'context7', '--url', 'https://mcp.context7.com/mcp']
|
|
131
155
|
: ['mcp', 'add', 'context7', '--', 'npx', '-y', '@upstash/context7-mcp@latest'];
|
|
132
|
-
const result = await runProcess(codex.bin, cmdArgs, { timeoutMs: 30000, maxOutputBytes: 64 * 1024 });
|
|
156
|
+
const result = await runProcess(codex.bin, cmdArgs, { env, timeoutMs: 30000, maxOutputBytes: 64 * 1024 });
|
|
133
157
|
if (flag(args, '--json'))
|
|
134
|
-
return console.log(JSON.stringify({ command: `${codex.bin} ${cmdArgs.join(' ')}`, result }, null, 2));
|
|
158
|
+
return console.log(JSON.stringify({ changed: result.code === 0, status: result.code === 0 ? 'installed' : 'failed', command: `${codex.bin} ${cmdArgs.join(' ')}`, result }, null, 2));
|
|
135
159
|
if (result.code !== 0)
|
|
136
160
|
throw new Error(result.stderr || result.stdout || 'codex mcp add failed');
|
|
137
161
|
console.log('Context7 global MCP configured.');
|
|
@@ -2019,14 +2019,26 @@ async function ensureGlobalContext7DuringInstall() {
|
|
|
2019
2019
|
if (!codex.bin)
|
|
2020
2020
|
return { status: 'codex_missing' };
|
|
2021
2021
|
const env = withoutSecretEnv(['CODEX_LB_API_KEY']);
|
|
2022
|
-
const
|
|
2023
|
-
if (
|
|
2022
|
+
const existing = await context7GlobalMcpStatus(codex.bin, env);
|
|
2023
|
+
if (existing.present)
|
|
2024
2024
|
return { status: 'present' };
|
|
2025
2025
|
const add = await runProcess(codex.bin, ['mcp', 'add', 'context7', '--', 'npx', '-y', '@upstash/context7-mcp@latest'], { env, timeoutMs: 30000, maxOutputBytes: 64 * 1024 }).catch((err) => ({ code: 1, stdout: '', stderr: err.message }));
|
|
2026
2026
|
if (add.code === 0)
|
|
2027
2027
|
return { status: 'installed' };
|
|
2028
2028
|
return { status: 'failed', error: `${add.stderr || add.stdout || 'codex mcp add failed'}`.trim() };
|
|
2029
2029
|
}
|
|
2030
|
+
export async function context7GlobalMcpStatus(codexBin, env = process.env) {
|
|
2031
|
+
const list = await runProcess(codexBin, ['mcp', 'list'], { env, timeoutMs: 8000, maxOutputBytes: 32 * 1024 })
|
|
2032
|
+
.catch((err) => ({ code: 1, stderr: err.message, stdout: '' }));
|
|
2033
|
+
const output = `${list.stdout || ''}\n${list.stderr || ''}`;
|
|
2034
|
+
return {
|
|
2035
|
+
checked: true,
|
|
2036
|
+
ok: list.code === 0,
|
|
2037
|
+
present: list.code === 0 && /context7/i.test(output),
|
|
2038
|
+
stdout: list.stdout || '',
|
|
2039
|
+
stderr: list.stderr || ''
|
|
2040
|
+
};
|
|
2041
|
+
}
|
|
2030
2042
|
function withoutSecretEnv(keys = []) {
|
|
2031
2043
|
const env = { ...process.env };
|
|
2032
2044
|
for (const key of keys)
|
|
@@ -2348,11 +2360,7 @@ export async function ensureProjectContext7Config(root, transport = 'local') {
|
|
|
2348
2360
|
const block = context7ConfigToml(transport).trim();
|
|
2349
2361
|
const existingBlock = /(^|\n)\[mcp_servers\.context7\]\n[\s\S]*?(?=\n\[[^\]]+\]|\s*$)/;
|
|
2350
2362
|
if (existingBlock.test(current)) {
|
|
2351
|
-
|
|
2352
|
-
if (next === current)
|
|
2353
|
-
return false;
|
|
2354
|
-
await writeTextAtomic(configPath, next.endsWith('\n') ? next : `${next}\n`);
|
|
2355
|
-
return true;
|
|
2363
|
+
return false;
|
|
2356
2364
|
}
|
|
2357
2365
|
if (hasContext7ConfigText(current))
|
|
2358
2366
|
return false;
|
|
@@ -3,6 +3,7 @@ import { findLatestMission, missionDir } from '../mission.js';
|
|
|
3
3
|
import { readJson, writeJsonAtomic } from '../fsx.js';
|
|
4
4
|
import { readAgentMessageBus } from './agent-message-bus.js';
|
|
5
5
|
import { buildZellijWorkerPaneSummary } from '../zellij/zellij-worker-pane-summary.js';
|
|
6
|
+
import { readLoopGraphProof, summarizeLoopGraphProof } from '../loops/loop-observability.js';
|
|
6
7
|
export const RUNTIME_PROOF_SUMMARY_SCHEMA = 'sks.runtime-proof-summary.v1';
|
|
7
8
|
export async function buildRuntimeProofSummary(root, missionIdInput = 'latest', opts = {}) {
|
|
8
9
|
const missionId = missionIdInput === 'latest' ? await findLatestMission(root) : missionIdInput;
|
|
@@ -18,6 +19,7 @@ export async function buildRuntimeProofSummary(root, missionIdInput = 'latest',
|
|
|
18
19
|
const messagesAll = await readAgentMessageBus(root, missionId, { max: 500 });
|
|
19
20
|
const recentMessages = await readAgentMessageBus(root, missionId, { max: opts.maxMessages || 8 });
|
|
20
21
|
const zellijSummary = await buildZellijWorkerPaneSummary(root, missionId).catch(() => null);
|
|
22
|
+
const loopSummary = summarizeLoopGraphProof(await readLoopGraphProof(root, missionId).catch(() => null));
|
|
21
23
|
const failedMessages = messagesAll.filter((row) => row.event_type === 'worker_failed');
|
|
22
24
|
const errorMessages = messagesAll.filter((row) => row.level === 'error');
|
|
23
25
|
const telemetryAgeMs = telemetry?.updated_at ? Math.max(0, Date.now() - Date.parse(telemetry.updated_at)) : Number.MAX_SAFE_INTEGER;
|
|
@@ -73,6 +75,7 @@ export async function buildRuntimeProofSummary(root, missionIdInput = 'latest',
|
|
|
73
75
|
pane_lock_held_p95_ms: Number(zellijSummary?.pane_lock_held_p95_ms || 0),
|
|
74
76
|
duplicate_slot_anchor_count: Number(zellijSummary?.duplicate_slot_anchor_count || 0)
|
|
75
77
|
},
|
|
78
|
+
loops: loopSummary,
|
|
76
79
|
blockers
|
|
77
80
|
};
|
|
78
81
|
await writeJsonAtomic(path.join(agentsDir, 'runtime-proof-summary.json'), summary);
|
|
@@ -91,6 +94,7 @@ export function renderRuntimeProofSummary(summary) {
|
|
|
91
94
|
`Stack fallback: ${summary.zellij.stacked_fallback_count}`,
|
|
92
95
|
`Pane lock wait p95: ${summary.zellij.pane_lock_wait_p95_ms}ms`,
|
|
93
96
|
`SLOTS anchors: ${summary.zellij.duplicate_slot_anchor_count}`,
|
|
97
|
+
`Loops: ${summary.loops.total} total / ${summary.loops.completed} done / ${summary.loops.blocked} blocked / ${summary.loops.speedup_ratio}x`,
|
|
94
98
|
...(summary.messages.recent.length ? [
|
|
95
99
|
'Recent worker messages:',
|
|
96
100
|
...summary.messages.recent.map((row) => ` ${messageStatusLabel(row)} ${row.slot_id || row.worker_id}: ${row.message}`)
|
|
@@ -53,6 +53,7 @@ export async function detectCodex0139Capability(input = {}) {
|
|
|
53
53
|
version_text: versionText || null,
|
|
54
54
|
parsed_version: parsed,
|
|
55
55
|
supports_code_mode_web_search: codeSearchOk,
|
|
56
|
+
supports_code_mode_web_search_real_verified: false,
|
|
56
57
|
supports_rich_tool_schemas: richSchemaOk,
|
|
57
58
|
supports_doctor_env_details: doctorEnvOk,
|
|
58
59
|
supports_marketplace_source_field: marketplaceOk,
|
|
@@ -111,7 +112,9 @@ async function probeCodex0139Features(codexBin, opts = {}) {
|
|
|
111
112
|
const marketplace = await runProcess(codexBin, ['plugin', 'marketplace', 'list', '--json'], { timeoutMs, maxOutputBytes: 256 * 1024 }).catch(() => ({ code: 1, stdout: '' }));
|
|
112
113
|
const marketplaceListJson = marketplace.code === 0 && marketplaceSourcesPresent(marketplace.stdout) ? 'passed' : 'failed';
|
|
113
114
|
const help = await runProcess(codexBin, ['--help'], { timeoutMs, maxOutputBytes: 256 * 1024 }).catch(() => ({ code: 1, stdout: '' }));
|
|
114
|
-
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);
|
|
115
118
|
return {
|
|
116
119
|
marketplace_list_json: marketplaceListJson,
|
|
117
120
|
sandbox_profile_alias: aliasOk ? 'passed' : 'failed',
|
|
@@ -127,14 +130,16 @@ export function marketplaceSourcesPresent(stdout) {
|
|
|
127
130
|
const rows = Array.isArray(parsed) ? parsed : Array.isArray(parsed?.marketplaces) ? parsed.marketplaces : Array.isArray(parsed?.items) ? parsed.items : [];
|
|
128
131
|
if (!rows.length)
|
|
129
132
|
return true;
|
|
130
|
-
return rows.every((row) => typeof row?.source === 'string' && row.source.length > 0
|
|
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);
|
|
131
136
|
}
|
|
132
137
|
catch {
|
|
133
138
|
return false;
|
|
134
139
|
}
|
|
135
140
|
}
|
|
136
141
|
export function codexHelpSupportsSandboxProfileAlias(stdout) {
|
|
137
|
-
return /(^|\s)-P[,\s]+--profile\b|--profile[,\s]+-P\b|(^|\s)-P\b/m.test(String(stdout || ''));
|
|
142
|
+
return /(^|\s)-P[,\s]+--(?:permissions-)?profile\b|--(?:permissions-)?profile[,\s]+-P\b|(^|\s)-P\b/m.test(String(stdout || ''));
|
|
138
143
|
}
|
|
139
144
|
export function redactCodexDoctorEnvDetails(value) {
|
|
140
145
|
if (Array.isArray(value))
|
|
@@ -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
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { findCodexBinary } from '../codex-adapter.js';
|
|
3
|
+
import { ensureDir, runProcess, writeJsonAtomic } from '../fsx.js';
|
|
4
|
+
import { marketplaceSourcesPresent } from './codex-0139-capability.js';
|
|
5
|
+
import { codex0139ProbeTail, skippedCodex0139Probe } from './codex-0139-real-probes.js';
|
|
6
|
+
export async function runCodex0139MarketplaceSourceRealProbe(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', `marketplace-${Date.now()}`);
|
|
12
|
+
await ensureDir(tempDir);
|
|
13
|
+
const result = await runProcess(codexBin, ['plugin', 'marketplace', 'list', '--json'], {
|
|
14
|
+
cwd: tempDir,
|
|
15
|
+
timeoutMs: input.timeoutMs || 60000,
|
|
16
|
+
maxOutputBytes: 512 * 1024
|
|
17
|
+
}).catch((err) => ({ code: 1, stdout: '', stderr: err?.message || String(err) }));
|
|
18
|
+
const rows = parseRows(result.stdout);
|
|
19
|
+
const missingSourceRows = rows
|
|
20
|
+
.map((row, index) => ({ index, name: String(row?.name || row?.id || row?.pluginId || `row-${index + 1}`), keys: Object.keys(row || {}), root: typeof row?.root === 'string' ? row.root : null }))
|
|
21
|
+
.filter((row, index) => !rowHasMarketplaceSourceLocator(rows[index]));
|
|
22
|
+
const ok = result.code === 0 && marketplaceSourcesPresent(result.stdout);
|
|
23
|
+
const artifact = path.join(input.root, '.sneakoscope', 'codex-0139-plugin-marketplace-real.json');
|
|
24
|
+
await writeJsonAtomic(artifact, {
|
|
25
|
+
schema: 'sks.codex-0139-plugin-marketplace-real.v1',
|
|
26
|
+
ok,
|
|
27
|
+
generated_at: new Date().toISOString(),
|
|
28
|
+
command_line: [codexBin, 'plugin', 'marketplace', 'list', '--json'],
|
|
29
|
+
empty_marketplace_list: rows.length === 0,
|
|
30
|
+
row_count: rows.length,
|
|
31
|
+
rows_have_source: rows.every(rowHasMarketplaceSource),
|
|
32
|
+
rows_have_source_locator: rows.every(rowHasMarketplaceSourceLocator),
|
|
33
|
+
missing_source_rows: missingSourceRows,
|
|
34
|
+
stdout_tail: codex0139ProbeTail(result.stdout),
|
|
35
|
+
stderr_tail: codex0139ProbeTail(result.stderr)
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
ok,
|
|
39
|
+
mode: 'actual-cli',
|
|
40
|
+
command_line: [codexBin, 'plugin', 'marketplace', 'list', '--json'],
|
|
41
|
+
duration_ms: Date.now() - started,
|
|
42
|
+
stdout_tail: codex0139ProbeTail(result.stdout),
|
|
43
|
+
stderr_tail: codex0139ProbeTail(result.stderr),
|
|
44
|
+
artifact_paths: [artifact],
|
|
45
|
+
evidence: {
|
|
46
|
+
empty_marketplace_list: rows.length === 0,
|
|
47
|
+
row_count: rows.length,
|
|
48
|
+
rows_have_source: rows.every(rowHasMarketplaceSource),
|
|
49
|
+
rows_have_source_locator: rows.every(rowHasMarketplaceSourceLocator),
|
|
50
|
+
missing_source_rows: missingSourceRows
|
|
51
|
+
},
|
|
52
|
+
blockers: ok ? [] : ['codex_plugin_marketplace_source_real_probe_failed']
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function rowHasMarketplaceSource(row) {
|
|
56
|
+
return typeof row?.source === 'string' && row.source.length > 0
|
|
57
|
+
|| typeof row?.marketplaceSource?.source === 'string' && row.marketplaceSource.source.length > 0;
|
|
58
|
+
}
|
|
59
|
+
function rowHasMarketplaceSourceLocator(row) {
|
|
60
|
+
return rowHasMarketplaceSource(row) || typeof row?.root === 'string' && row.root.length > 0;
|
|
61
|
+
}
|
|
62
|
+
export async function runCodex0139PluginCacheRealProbe(input) {
|
|
63
|
+
const started = Date.now();
|
|
64
|
+
const codexBin = input.codexBin || await findCodexBinary();
|
|
65
|
+
if (!codexBin)
|
|
66
|
+
return skippedCodex0139Probe('codex_cli_missing');
|
|
67
|
+
const tempDir = path.join(input.root, '.sneakoscope', 'tmp', 'codex-0139-real-probes', `plugin-cache-${Date.now()}`);
|
|
68
|
+
await ensureDir(tempDir);
|
|
69
|
+
const firstStarted = Date.now();
|
|
70
|
+
const first = await runProcess(codexBin, ['plugin', 'list', '--json'], {
|
|
71
|
+
cwd: tempDir,
|
|
72
|
+
timeoutMs: input.timeoutMs || 60000,
|
|
73
|
+
maxOutputBytes: 512 * 1024
|
|
74
|
+
}).catch((err) => ({ code: 1, stdout: '', stderr: err?.message || String(err) }));
|
|
75
|
+
const firstMs = Date.now() - firstStarted;
|
|
76
|
+
const secondStarted = Date.now();
|
|
77
|
+
const second = await runProcess(codexBin, ['plugin', 'list', '--json'], {
|
|
78
|
+
cwd: tempDir,
|
|
79
|
+
timeoutMs: input.timeoutMs || 60000,
|
|
80
|
+
maxOutputBytes: 512 * 1024
|
|
81
|
+
}).catch((err) => ({ code: 1, stdout: '', stderr: err?.message || String(err) }));
|
|
82
|
+
const secondMs = Date.now() - secondStarted;
|
|
83
|
+
const marker = /cache|cached|catalog|remote/i.test(`${first.stdout}\n${second.stdout}\n${first.stderr}\n${second.stderr}`);
|
|
84
|
+
const secondNotMuchSlower = secondMs <= Math.max(firstMs * 2, firstMs + 1000);
|
|
85
|
+
const ok = first.code === 0 && second.code === 0 && secondNotMuchSlower;
|
|
86
|
+
return {
|
|
87
|
+
ok,
|
|
88
|
+
mode: 'actual-cli',
|
|
89
|
+
command_line: [codexBin, 'plugin', 'list', '--json'],
|
|
90
|
+
duration_ms: Date.now() - started,
|
|
91
|
+
stdout_tail: codex0139ProbeTail(`${first.stdout || ''}\n${second.stdout || ''}`),
|
|
92
|
+
stderr_tail: codex0139ProbeTail(`${first.stderr || ''}\n${second.stderr || ''}`),
|
|
93
|
+
artifact_paths: [tempDir],
|
|
94
|
+
evidence: {
|
|
95
|
+
first_duration_ms: firstMs,
|
|
96
|
+
second_duration_ms: secondMs,
|
|
97
|
+
second_not_slower_than_2x: secondNotMuchSlower,
|
|
98
|
+
cache_marker_seen: marker,
|
|
99
|
+
cache_marker_warning: marker ? null : 'cache marker unavailable; timing success accepted'
|
|
100
|
+
},
|
|
101
|
+
blockers: ok ? [] : ['codex_plugin_catalog_cache_real_probe_failed']
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function parseRows(stdout) {
|
|
105
|
+
try {
|
|
106
|
+
const parsed = JSON.parse(String(stdout || ''));
|
|
107
|
+
if (Array.isArray(parsed))
|
|
108
|
+
return parsed;
|
|
109
|
+
if (Array.isArray(parsed?.marketplaces))
|
|
110
|
+
return parsed.marketplaces;
|
|
111
|
+
if (Array.isArray(parsed?.items))
|
|
112
|
+
return parsed.items;
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=codex-0139-plugin-real-probes.js.map
|