sneakoscope 3.1.10 → 3.1.12
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 +12 -9
- 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/bin/sks.js +1 -1
- package/dist/commands/doctor.js +205 -2
- package/dist/core/codex/agent-config-file-repair.js +61 -0
- package/dist/core/codex/codex-startup-config-postcheck.js +30 -0
- package/dist/core/codex-app/codex-agent-role-sync.js +6 -6
- package/dist/core/codex-control/codex-0140-capability.js +71 -0
- package/dist/core/codex-control/codex-0140-feature-probes.js +37 -0
- package/dist/core/codex-control/codex-0140-probe-runner.js +5 -0
- package/dist/core/codex-control/codex-0140-real-probe-summary.js +12 -0
- package/dist/core/codex-control/codex-0140-real-probes.js +29 -0
- package/dist/core/codex-native/codex-native-feature-broker.js +15 -1
- package/dist/core/config/config-migration-journal.js +2 -0
- package/dist/core/config/secret-preservation.js +1 -1
- package/dist/core/config/supabase-secret-preservation.js +1 -0
- package/dist/core/doctor/codex-startup-config-repair.js +40 -0
- package/dist/core/doctor/context7-mcp-repair.js +62 -0
- package/dist/core/doctor/doctor-codex-startup-repair.js +381 -0
- package/dist/core/doctor/doctor-context7-repair.js +155 -0
- package/dist/core/doctor/doctor-repair-postcheck.js +11 -0
- package/dist/core/doctor/doctor-transaction.js +30 -0
- package/dist/core/doctor/supabase-mcp-repair.js +36 -0
- package/dist/core/fsx.js +1 -1
- package/dist/core/mcp/mcp-config-preservation.js +30 -0
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-capability.js +1 -1
- package/dist/core/zellij/zellij-worker-pane-manager.js +19 -2
- package/dist/scripts/codex-0140-feature-gate-lib.js +12 -0
- package/dist/scripts/release-3112-required-gates.js +30 -0
- package/package.json +32 -2
- package/dist/.sks-build-stamp.json +0 -8
package/README.md
CHANGED
|
@@ -35,15 +35,18 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
|
|
|
35
35
|
|
|
36
36
|
## 🚀 Current Release
|
|
37
37
|
|
|
38
|
-
SKS **3.1.
|
|
39
|
-
|
|
40
|
-
What changed in 3.1.
|
|
41
|
-
|
|
42
|
-
- **
|
|
43
|
-
- **
|
|
44
|
-
- **
|
|
45
|
-
-
|
|
46
|
-
- **
|
|
38
|
+
SKS **3.1.12** is a release-ready repair pass for `sks doctor --fix` production recovery, Codex 0.140 capability coverage, and MAD Zellij right-column stack reliability.
|
|
39
|
+
|
|
40
|
+
What changed in 3.1.12:
|
|
41
|
+
|
|
42
|
+
- **MAD Zellij panes are stack-reconciled.** Second and later visible workers still launch with native `new-pane --stacked`, then SKS calls Zellij `stack-panes` with the observed worker pane ids so the right column stays one stack instead of drifting into automatic split geometry.
|
|
43
|
+
- **Codex 0.140 readiness is gated.** The release records hermetic coverage for usage metadata, goal attachment preservation, session delete/import, unified mentions, Bedrock managed auth, MCP reliability, SQLite recovery, non-TTY interrupt behavior, large-repo performance, and optional real-probe enforcement.
|
|
44
|
+
- **Doctor production repair is transactional.** `sks doctor --fix` now writes a doctor fix transaction and postcheck report so startup config, Context7 MCP, Supabase MCP, command alias, and native capability repair results are visible in JSON output instead of disappearing into console-only repair steps.
|
|
45
|
+
- **Context7 stdio lockups are doctor-repairable.** `sks doctor --fix` detects local `@upstash/context7-mcp` stdio config and migrates it to the remote Context7 MCP endpoint so Codex launches do not stall at the stdio server banner.
|
|
46
|
+
- **Codex startup warnings are repaired more completely.** `sks doctor --fix` rewrites stale SKS agent `config_file` paths, removes unsupported managed `message_role_prefix` role fields, preserves optional `supabase_sauron`, and now repairs `node_repl` to a valid Codex App command when available or removes both the stale parent table and child env table when it is not.
|
|
47
|
+
- **Doctor JSON exposes the Context7, startup, Supabase, and production postcheck reports.** `context7_repair`, `codex_startup_repair`, `startup_config_repair`, `context7_mcp_repair`, `supabase_mcp_repair`, `doctor_fix_transaction`, `doctor_fix_postcheck`, and their `repair.*` entries carry migration status, backups, actions, warnings, and any manual auth actions.
|
|
48
|
+
- **Secret rollback is stricter.** The secret-preservation guard treats protected-value changes the same way as missing values, rolls back affected files from redacted backups, and records rollback status without writing raw secret values.
|
|
49
|
+
- **Release metadata is aligned for 3.1.12.** Package, lockfile, CLI version constants, Rust helper metadata, README, and changelog all point at the same release.
|
|
47
50
|
|
|
48
51
|
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.
|
|
49
52
|
|
|
@@ -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.1.
|
|
7
|
+
Some("--version") => println!("sks-rs 3.1.12"),
|
|
8
8
|
Some("compact-info") => {
|
|
9
9
|
let mut input = String::new();
|
|
10
10
|
let _ = io::stdin().read_to_string(&mut input);
|
package/dist/bin/sks.js
CHANGED
package/dist/commands/doctor.js
CHANGED
|
@@ -24,11 +24,18 @@ import { runCodex0138Doctor } from '../core/doctor/codex-0138-doctor.js';
|
|
|
24
24
|
import { writeCodexPluginInventoryArtifacts, pluginAppTemplatePolicy } from '../core/codex-plugins/codex-plugin-json.js';
|
|
25
25
|
import { writeMcpPluginInventoryArtifacts } from '../core/mcp/mcp-plugin-inventory.js';
|
|
26
26
|
import { runDoctorZellijRepair, doctorZellijRepairConsoleLine } from '../core/doctor/doctor-zellij-repair.js';
|
|
27
|
+
import { runDoctorContext7Repair } from '../core/doctor/doctor-context7-repair.js';
|
|
28
|
+
import { runDoctorCodexStartupRepair } from '../core/doctor/doctor-codex-startup-repair.js';
|
|
27
29
|
import { buildCodexAppHarnessMatrix } from '../core/codex-app/codex-app-harness-matrix.js';
|
|
28
30
|
import { buildCodexNativeFeatureMatrix } from '../core/codex-native/codex-native-feature-broker.js';
|
|
29
31
|
import { repairCodexNativeManagedAssets } from '../core/codex-native/codex-native-repair-transaction.js';
|
|
30
32
|
import { runDoctorNativeCapabilityRepair } from '../core/doctor/doctor-native-capability-repair.js';
|
|
31
33
|
import { runDoctorCommandAliasCleanup } from '../core/doctor/command-alias-cleanup.js';
|
|
34
|
+
import { repairCodexStartupConfig } from '../core/doctor/codex-startup-config-repair.js';
|
|
35
|
+
import { repairContext7Mcp } from '../core/doctor/context7-mcp-repair.js';
|
|
36
|
+
import { repairSupabaseMcp } from '../core/doctor/supabase-mcp-repair.js';
|
|
37
|
+
import { writeDoctorFixTransaction } from '../core/doctor/doctor-transaction.js';
|
|
38
|
+
import { doctorRepairPostcheck } from '../core/doctor/doctor-repair-postcheck.js';
|
|
32
39
|
import { withSecretPreservationGuard } from '../core/config/config-migration-journal.js';
|
|
33
40
|
export async function run(_command, args = []) {
|
|
34
41
|
const root = await projectRoot();
|
|
@@ -118,6 +125,19 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
118
125
|
requireActualCodex: flag(args, '--fix') || flag(args, '--require-actual-codex'),
|
|
119
126
|
codexBin: codexBin || undefined
|
|
120
127
|
};
|
|
128
|
+
let codexStartupRepair = await runDoctorCodexStartupRepair({ root, fix: doctorFix }).catch((err) => ({
|
|
129
|
+
schema: 'sks.doctor-codex-startup-repair.v1',
|
|
130
|
+
ok: false,
|
|
131
|
+
generated_at: new Date().toISOString(),
|
|
132
|
+
fix: doctorFix,
|
|
133
|
+
configs: [],
|
|
134
|
+
agent_role_files: { sanitized: [], created: [], blockers: [err?.message || String(err)] },
|
|
135
|
+
actions: [],
|
|
136
|
+
manual_actions: [],
|
|
137
|
+
blockers: [err?.message || String(err)],
|
|
138
|
+
warnings: [],
|
|
139
|
+
report_path: `${root}/.sneakoscope/reports/doctor-codex-startup-repair.json`
|
|
140
|
+
}));
|
|
121
141
|
const codexDoctorBefore = flag(args, '--fix') ? await runCodexDoctorBridge({ codexBin: codexBin || null, cwd: root, required: flag(args, '--require-actual-codex') }).catch(() => null) : null;
|
|
122
142
|
const configRepair = flag(args, '--fix') ? await repairCodexConfigEperm(root, { fix: true, ...configProbeOpts }) : null;
|
|
123
143
|
const migrationJournal = flag(args, '--fix')
|
|
@@ -126,6 +146,7 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
126
146
|
const codexConfig = configRepair?.after || await inspectCodexConfigReadability(root, configProbeOpts);
|
|
127
147
|
const codexDoctor = await runCodexDoctorBridge({ codexBin: codexBin || null, cwd: root, required: flag(args, '--require-actual-codex') });
|
|
128
148
|
const codexDoctorDiff = compareCodexDoctorBridge(codexDoctorBefore, codexDoctor);
|
|
149
|
+
codexStartupRepair = mergeObservedCodexStartupWarnings(codexStartupRepair, codexDoctor);
|
|
129
150
|
const codex = codexBin
|
|
130
151
|
? { bin: codexBin, version: 'fixture-or-explicit', available: true }
|
|
131
152
|
: await getCodexInfo().catch(() => ({ bin: null, version: null, available: false }));
|
|
@@ -212,6 +233,132 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
212
233
|
blockers: [err?.message || String(err)],
|
|
213
234
|
warnings: []
|
|
214
235
|
}));
|
|
236
|
+
const context7Repair = await runDoctorContext7Repair({ root, fix: doctorFix }).catch((err) => ({
|
|
237
|
+
schema: 'sks.doctor-context7-repair.v1',
|
|
238
|
+
ok: false,
|
|
239
|
+
generated_at: new Date().toISOString(),
|
|
240
|
+
fix: doctorFix,
|
|
241
|
+
preferred_transport: 'remote',
|
|
242
|
+
configs: [],
|
|
243
|
+
actions: [],
|
|
244
|
+
blockers: [err?.message || String(err)],
|
|
245
|
+
warnings: [],
|
|
246
|
+
report_path: `${root}/.sneakoscope/reports/doctor-context7-repair.json`
|
|
247
|
+
}));
|
|
248
|
+
const startupConfigRepair = doctorFix
|
|
249
|
+
? await repairCodexStartupConfig({ root, apply: true }).catch((err) => ({
|
|
250
|
+
schema: 'sks.codex-startup-config-repair.v1',
|
|
251
|
+
ok: false,
|
|
252
|
+
apply: true,
|
|
253
|
+
blockers: [err?.message || String(err)]
|
|
254
|
+
}))
|
|
255
|
+
: null;
|
|
256
|
+
const context7McpRepair = doctorFix
|
|
257
|
+
? await repairContext7Mcp({ root, apply: true }).catch((err) => ({
|
|
258
|
+
schema: 'sks.doctor-context7-mcp-repair.v1',
|
|
259
|
+
ok: false,
|
|
260
|
+
apply: true,
|
|
261
|
+
repaired: false,
|
|
262
|
+
manual_required: false,
|
|
263
|
+
blockers: [err?.message || String(err)],
|
|
264
|
+
warnings: []
|
|
265
|
+
}))
|
|
266
|
+
: null;
|
|
267
|
+
const supabaseMcpRepair = doctorFix
|
|
268
|
+
? await repairSupabaseMcp({ root, apply: true }).catch((err) => ({
|
|
269
|
+
schema: 'sks.doctor-supabase-mcp-repair.v1',
|
|
270
|
+
ok: false,
|
|
271
|
+
apply: true,
|
|
272
|
+
configured: false,
|
|
273
|
+
disabled: false,
|
|
274
|
+
token_env_present: false,
|
|
275
|
+
unsafe_write_access: false,
|
|
276
|
+
manual_required: true,
|
|
277
|
+
next_action: 'Review Supabase MCP configuration manually.',
|
|
278
|
+
blockers: [err?.message || String(err)],
|
|
279
|
+
warnings: [],
|
|
280
|
+
raw_secret_values_recorded: false
|
|
281
|
+
}))
|
|
282
|
+
: null;
|
|
283
|
+
const doctorFixTransaction = doctorFix
|
|
284
|
+
? await writeDoctorFixTransaction({
|
|
285
|
+
root,
|
|
286
|
+
phases: [
|
|
287
|
+
{
|
|
288
|
+
id: 'setup',
|
|
289
|
+
ok: setupRepair !== null,
|
|
290
|
+
repaired: setupRepair !== null,
|
|
291
|
+
blockers: setupRepair === null ? ['setup_repair_not_recorded'] : []
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
id: 'codex_startup_repair',
|
|
295
|
+
ok: codexStartupRepair?.ok !== false,
|
|
296
|
+
repaired: doctorFix,
|
|
297
|
+
blockers: codexStartupRepair?.blockers || [],
|
|
298
|
+
warnings: codexStartupRepair?.warnings || []
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
id: 'startup_config_repair',
|
|
302
|
+
ok: startupConfigRepair?.ok === true,
|
|
303
|
+
repaired: startupConfigRepair?.apply === true,
|
|
304
|
+
blockers: startupConfigRepair?.blockers || []
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
id: 'context7_repair',
|
|
308
|
+
ok: context7Repair?.ok !== false,
|
|
309
|
+
repaired: doctorFix,
|
|
310
|
+
blockers: context7Repair?.blockers || [],
|
|
311
|
+
warnings: context7Repair?.warnings || []
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
id: 'context7_mcp_repair',
|
|
315
|
+
ok: context7McpRepair?.ok === true,
|
|
316
|
+
repaired: context7McpRepair?.repaired === true,
|
|
317
|
+
manual_required: context7McpRepair?.manual_required === true,
|
|
318
|
+
blockers: context7McpRepair?.blockers || [],
|
|
319
|
+
warnings: context7McpRepair?.warnings || []
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
id: 'supabase_mcp_repair',
|
|
323
|
+
ok: supabaseMcpRepair?.ok === true,
|
|
324
|
+
repaired: false,
|
|
325
|
+
manual_required: supabaseMcpRepair?.manual_required === true,
|
|
326
|
+
blockers: supabaseMcpRepair?.blockers || [],
|
|
327
|
+
warnings: supabaseMcpRepair?.warnings || []
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
id: 'command_alias_cleanup',
|
|
331
|
+
ok: commandAliasCleanup?.ok !== false,
|
|
332
|
+
repaired: Array.isArray(commandAliasCleanup?.actions) && commandAliasCleanup.actions.length > 0,
|
|
333
|
+
blockers: commandAliasCleanup?.blockers || []
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
id: 'native_capability_repair',
|
|
337
|
+
ok: doctorNativeCapabilityRepair?.ok !== false,
|
|
338
|
+
repaired: doctorFix,
|
|
339
|
+
blockers: doctorNativeCapabilityRepair?.blockers || []
|
|
340
|
+
}
|
|
341
|
+
]
|
|
342
|
+
}).catch((err) => ({
|
|
343
|
+
schema: 'sks.doctor-fix-transaction.v1',
|
|
344
|
+
ok: false,
|
|
345
|
+
postcheck_ok: false,
|
|
346
|
+
phases: [
|
|
347
|
+
{
|
|
348
|
+
id: 'doctor_fix_transaction',
|
|
349
|
+
ok: false,
|
|
350
|
+
repaired: false,
|
|
351
|
+
manual_required: false,
|
|
352
|
+
blockers: [err?.message || String(err)],
|
|
353
|
+
warnings: [],
|
|
354
|
+
artifact_path: null
|
|
355
|
+
}
|
|
356
|
+
],
|
|
357
|
+
rollback_performed: false,
|
|
358
|
+
raw_secret_values_recorded: false
|
|
359
|
+
}))
|
|
360
|
+
: null;
|
|
361
|
+
const doctorFixPostcheck = doctorFix ? doctorRepairPostcheck(doctorFixTransaction) : null;
|
|
215
362
|
const zellij = await checkZellijCapability({ root, require: process.env.SKS_REQUIRE_ZELLIJ === '1' });
|
|
216
363
|
const localModel = await readLocalModelConfig().catch(() => null);
|
|
217
364
|
const permissionProfiles = await inventoryCodexPermissionProfiles(root, { writeReport: true });
|
|
@@ -291,6 +438,13 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
291
438
|
codex_doctor: codexDoctor,
|
|
292
439
|
require_codex_doctor: flag(args, '--fix') || flag(args, '--require-actual-codex'),
|
|
293
440
|
zellij,
|
|
441
|
+
context7_repair: context7Repair,
|
|
442
|
+
codex_startup_repair: codexStartupRepair,
|
|
443
|
+
startup_config_repair: startupConfigRepair,
|
|
444
|
+
context7_mcp_repair: context7McpRepair,
|
|
445
|
+
supabase_mcp_repair: supabaseMcpRepair,
|
|
446
|
+
doctor_fix_transaction: doctorFixTransaction,
|
|
447
|
+
doctor_fix_postcheck: doctorFixPostcheck,
|
|
294
448
|
local_model: localModel,
|
|
295
449
|
agent_role_config: agentRoleConfigRepair,
|
|
296
450
|
repair: configRepair,
|
|
@@ -304,6 +458,7 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
304
458
|
...(codexConfig.operator_actions || []),
|
|
305
459
|
...(configRepair?.operator_actions || []),
|
|
306
460
|
...(zellijRepair && !zellijRepair.ok && zellijRepair.command ? [`Run: ${zellijRepair.command}`] : []),
|
|
461
|
+
...(codexStartupRepair.manual_actions || []),
|
|
307
462
|
...(pluginPolicy?.doctor_warnings || [])
|
|
308
463
|
]
|
|
309
464
|
});
|
|
@@ -311,7 +466,7 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
311
466
|
const runtimeReadiness = buildRuntimeReadiness(zellijReadiness, codexNativeFeatureMatrix);
|
|
312
467
|
const result = {
|
|
313
468
|
schema: 'sks.doctor-status.v1',
|
|
314
|
-
ok: ready.ready && (!sksUpdate || sksUpdate.ok !== false) && commandAliasCleanup.ok !== false,
|
|
469
|
+
ok: ready.ready && (!sksUpdate || sksUpdate.ok !== false) && commandAliasCleanup.ok !== false && codexStartupRepair.ok !== false && (!doctorFixPostcheck || doctorFixPostcheck.ok !== false),
|
|
315
470
|
root,
|
|
316
471
|
node: { ok: Number(process.versions.node.split('.')[0]) >= 20, version: process.version },
|
|
317
472
|
codex,
|
|
@@ -325,6 +480,13 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
325
480
|
codex_doctor_diff: codexDoctorDiff,
|
|
326
481
|
zellij,
|
|
327
482
|
zellij_repair: zellijRepair,
|
|
483
|
+
context7_repair: context7Repair,
|
|
484
|
+
codex_startup_repair: codexStartupRepair,
|
|
485
|
+
startup_config_repair: startupConfigRepair,
|
|
486
|
+
context7_mcp_repair: context7McpRepair,
|
|
487
|
+
supabase_mcp_repair: supabaseMcpRepair,
|
|
488
|
+
doctor_fix_transaction: doctorFixTransaction,
|
|
489
|
+
doctor_fix_postcheck: doctorFixPostcheck,
|
|
328
490
|
local_model: localModel,
|
|
329
491
|
agent_role_config: agentRoleConfigRepair,
|
|
330
492
|
zellij_readiness: zellijReadiness,
|
|
@@ -348,7 +510,7 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
348
510
|
ready,
|
|
349
511
|
sneakoscope: { ok: await exists(`${root}/.sneakoscope`) },
|
|
350
512
|
package: { bytes: pkgBytes, human: formatBytes(pkgBytes) },
|
|
351
|
-
repair: { sks_update: sksUpdate, setup: setupRepair, codex_config: configRepair, migration_journal: migrationJournal, global_sks_installs: globalSksInstallCleanup, agent_role_config: agentRoleConfigRepair, zellij: zellijRepair, codex_native: codexNativeRepair, doctor_native_capability: doctorNativeCapabilityRepair, command_aliases: commandAliasCleanup }
|
|
513
|
+
repair: { sks_update: sksUpdate, setup: setupRepair, codex_config: configRepair, migration_journal: migrationJournal, global_sks_installs: globalSksInstallCleanup, agent_role_config: agentRoleConfigRepair, zellij: zellijRepair, context7: context7Repair, codex_startup: codexStartupRepair, startup_config: startupConfigRepair, context7_mcp: context7McpRepair, supabase_mcp: supabaseMcpRepair, doctor_transaction: doctorFixTransaction, doctor_postcheck: doctorFixPostcheck, codex_native: codexNativeRepair, doctor_native_capability: doctorNativeCapabilityRepair, command_aliases: commandAliasCleanup }
|
|
352
514
|
};
|
|
353
515
|
if (flag(args, '--json')) {
|
|
354
516
|
printJson(result);
|
|
@@ -375,6 +537,21 @@ async function runDoctor(args = [], root, doctorFix) {
|
|
|
375
537
|
const zellijRepairLine = doctorZellijRepairConsoleLine(zellijRepair);
|
|
376
538
|
if (zellijRepairLine)
|
|
377
539
|
console.log(zellijRepairLine);
|
|
540
|
+
console.log('Context7 MCP:');
|
|
541
|
+
console.log(` transport: ${context7Repair.preferred_transport || 'remote'}`);
|
|
542
|
+
console.log(` repair: ${context7Repair.ok ? 'ok' : 'blocked'}`);
|
|
543
|
+
for (const action of context7Repair.actions || [])
|
|
544
|
+
console.log(` - ${action}`);
|
|
545
|
+
for (const warning of context7Repair.warnings || [])
|
|
546
|
+
console.log(` warning: ${warning}`);
|
|
547
|
+
console.log('Codex startup config:');
|
|
548
|
+
console.log(` repair: ${codexStartupRepair.ok ? 'ok' : 'blocked'}`);
|
|
549
|
+
for (const action of codexStartupRepair.actions || [])
|
|
550
|
+
console.log(` - ${action}`);
|
|
551
|
+
for (const action of codexStartupRepair.manual_actions || [])
|
|
552
|
+
console.log(` manual: ${action}`);
|
|
553
|
+
for (const warning of codexStartupRepair.warnings || [])
|
|
554
|
+
console.log(` warning: ${warning}`);
|
|
378
555
|
console.log(` codex doctor: ${codexDoctor.available ? (codexDoctor.exit_code === 0 ? 'ok' : 'warning') : 'unavailable'}`);
|
|
379
556
|
console.log(`Rust acc.: ${rust.mode || (rust.available ? 'rust_accelerated' : 'js_fallback')} ${rust.version || rust.status || ''}`);
|
|
380
557
|
console.log(`Codex App: ${ready.codex_app_ready ? 'ok' : 'optional_missing'}`);
|
|
@@ -721,4 +898,30 @@ function readOption(args = [], name, fallback = null) {
|
|
|
721
898
|
const index = args.indexOf(name);
|
|
722
899
|
return index >= 0 && args[index + 1] ? args[index + 1] : fallback;
|
|
723
900
|
}
|
|
901
|
+
function mergeObservedCodexStartupWarnings(startupRepair, codexDoctor) {
|
|
902
|
+
const text = `${codexDoctor?.stdout_tail || ''}\n${codexDoctor?.stderr_tail || ''}`;
|
|
903
|
+
const manual = new Set(Array.isArray(startupRepair?.manual_actions) ? startupRepair.manual_actions : []);
|
|
904
|
+
const warnings = new Set(Array.isArray(startupRepair?.warnings) ? startupRepair.warnings : []);
|
|
905
|
+
const blockers = new Set(Array.isArray(startupRepair?.blockers) ? startupRepair.blockers : []);
|
|
906
|
+
if (/codex_apps[\s\S]{0,500}token_expired|token_expired[\s\S]{0,500}codex_apps/i.test(text)) {
|
|
907
|
+
manual.add('Codex Apps MCP token is expired; sign in to Codex App/CLI again so the connector can mint a fresh token.');
|
|
908
|
+
warnings.add('codex_apps_token_expired_observed');
|
|
909
|
+
blockers.add('codex_apps_token_expired_manual_reauth_required');
|
|
910
|
+
}
|
|
911
|
+
if (/SUPABASE_ACCESS_TOKEN[\s\S]{0,500}mcp server ['"`]?supabase['"`]?|mcp server ['"`]?supabase['"`]?[\s\S]{0,500}SUPABASE_ACCESS_TOKEN/i.test(text)) {
|
|
912
|
+
manual.add('Supabase MCP uses SUPABASE_ACCESS_TOKEN but the variable is unset; export the token or migrate that server to a read-only remote URL.');
|
|
913
|
+
warnings.add('supabase_access_token_missing_observed');
|
|
914
|
+
blockers.add('supabase_access_token_missing_manual_auth_required');
|
|
915
|
+
}
|
|
916
|
+
if (/node_repl[\s\S]{0,500}No such file or directory|No such file or directory[\s\S]{0,500}node_repl/i.test(text)) {
|
|
917
|
+
warnings.add('node_repl_missing_command_observed');
|
|
918
|
+
}
|
|
919
|
+
return {
|
|
920
|
+
...startupRepair,
|
|
921
|
+
ok: blockers.size === 0 && startupRepair?.ok !== false,
|
|
922
|
+
manual_actions: [...manual],
|
|
923
|
+
warnings: [...warnings],
|
|
924
|
+
blockers: [...blockers]
|
|
925
|
+
};
|
|
926
|
+
}
|
|
724
927
|
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { ensureDir, nowIso, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
|
|
4
|
+
export async function repairAgentConfigFileReferences(input) {
|
|
5
|
+
const root = path.resolve(input.root);
|
|
6
|
+
const configPath = path.join(root, '.codex', 'config.toml');
|
|
7
|
+
const original = await fs.readFile(configPath, 'utf8').catch(() => '');
|
|
8
|
+
const createdFiles = [];
|
|
9
|
+
const repairedPaths = [];
|
|
10
|
+
const removedUnsupportedFields = [];
|
|
11
|
+
let text = original.replace(/^\s*message_role_prefix\s*=.*$/gm, (line) => {
|
|
12
|
+
removedUnsupportedFields.push(line.trim());
|
|
13
|
+
return '';
|
|
14
|
+
});
|
|
15
|
+
text = text.replace(/config_file\s*=\s*"([^"]+)"/g, (_match, value) => {
|
|
16
|
+
const absolute = path.isAbsolute(value) ? value : path.join(root, value);
|
|
17
|
+
repairedPaths.push(absolute);
|
|
18
|
+
return `config_file = "${absolute}"`;
|
|
19
|
+
});
|
|
20
|
+
if (input.apply && text !== original) {
|
|
21
|
+
for (const file of repairedPaths) {
|
|
22
|
+
const exists = await fs.stat(file).then((stat) => stat.isFile()).catch(() => false);
|
|
23
|
+
if (!exists) {
|
|
24
|
+
await ensureDir(path.dirname(file));
|
|
25
|
+
await writeTextAtomic(file, '# SKS managed agent config placeholder\n');
|
|
26
|
+
createdFiles.push(file);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
await writeTextAtomic(configPath, text);
|
|
30
|
+
}
|
|
31
|
+
const missing = await missingAgentConfigFiles(text);
|
|
32
|
+
const report = {
|
|
33
|
+
schema: 'sks.agent-config-file-repair.v1',
|
|
34
|
+
generated_at: nowIso(),
|
|
35
|
+
ok: missing.length === 0 && !/^\s*message_role_prefix\s*=/m.test(text),
|
|
36
|
+
apply: input.apply === true,
|
|
37
|
+
config_path: configPath,
|
|
38
|
+
repaired_paths: repairedPaths,
|
|
39
|
+
created_files: createdFiles,
|
|
40
|
+
removed_unsupported_fields: removedUnsupportedFields,
|
|
41
|
+
blockers: missing.map((file) => `missing_agent_config_file:${file}`)
|
|
42
|
+
};
|
|
43
|
+
if (input.reportPath !== null)
|
|
44
|
+
await writeJsonAtomic(input.reportPath || path.join(root, '.sneakoscope', 'reports', 'agent-config-file-repair.json'), report).catch(() => undefined);
|
|
45
|
+
return report;
|
|
46
|
+
}
|
|
47
|
+
export async function missingAgentConfigFiles(text) {
|
|
48
|
+
const rows = [...String(text || '').matchAll(/config_file\s*=\s*"([^"]+)"/g)].map((match) => match[1]).filter((file) => Boolean(file));
|
|
49
|
+
const missing = [];
|
|
50
|
+
for (const file of rows) {
|
|
51
|
+
if (!path.isAbsolute(file)) {
|
|
52
|
+
missing.push(file);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const ok = await fs.stat(file).then((stat) => stat.isFile()).catch(() => false);
|
|
56
|
+
if (!ok)
|
|
57
|
+
missing.push(file);
|
|
58
|
+
}
|
|
59
|
+
return missing;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=agent-config-file-repair.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { nowIso, writeJsonAtomic } from '../fsx.js';
|
|
4
|
+
import { missingAgentConfigFiles } from './agent-config-file-repair.js';
|
|
5
|
+
export async function postcheckCodexStartupConfig(input) {
|
|
6
|
+
const root = path.resolve(input.root);
|
|
7
|
+
const configPath = path.join(root, '.codex', 'config.toml');
|
|
8
|
+
const text = await fs.readFile(configPath, 'utf8').catch(() => '');
|
|
9
|
+
const missing = await missingAgentConfigFiles(text);
|
|
10
|
+
const unsupportedRoleFields = /^\s*message_role_prefix\s*=/m.test(text);
|
|
11
|
+
const relativePaths = [...text.matchAll(/config_file\s*=\s*"([^"]+)"/g)].map((match) => match[1]).filter((file) => file && !path.isAbsolute(file));
|
|
12
|
+
const report = {
|
|
13
|
+
schema: 'sks.codex-startup-config-postcheck.v1',
|
|
14
|
+
generated_at: nowIso(),
|
|
15
|
+
ok: missing.length === 0 && relativePaths.length === 0 && !unsupportedRoleFields,
|
|
16
|
+
config_path: configPath,
|
|
17
|
+
missing_config_files: missing,
|
|
18
|
+
relative_config_files: relativePaths,
|
|
19
|
+
unsupported_managed_role_fields: unsupportedRoleFields,
|
|
20
|
+
blockers: [
|
|
21
|
+
...missing.map((file) => `missing_agent_config_file:${file}`),
|
|
22
|
+
...relativePaths.map((file) => `relative_agent_config_file:${file}`),
|
|
23
|
+
...(unsupportedRoleFields ? ['unsupported_message_role_prefix_field'] : [])
|
|
24
|
+
]
|
|
25
|
+
};
|
|
26
|
+
if (input.reportPath !== null)
|
|
27
|
+
await writeJsonAtomic(input.reportPath || path.join(root, '.sneakoscope', 'reports', 'codex-startup-config-postcheck.json'), report).catch(() => undefined);
|
|
28
|
+
return report;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=codex-startup-config-postcheck.js.map
|
|
@@ -56,7 +56,7 @@ export async function syncCodexAgentRoles(input) {
|
|
|
56
56
|
for (const role of DIRECTIVE_ROLES) {
|
|
57
57
|
const file = path.join(targetDir, `${role}.toml`);
|
|
58
58
|
const current = await fs.readFile(file, 'utf8').catch(() => '');
|
|
59
|
-
if (current && !
|
|
59
|
+
if (current && !isSksManagedDirectiveRole(current))
|
|
60
60
|
continue;
|
|
61
61
|
await writeTextAtomic(file, roleToml(role, rolePayloads[role]));
|
|
62
62
|
created.push(file);
|
|
@@ -84,13 +84,9 @@ export async function syncCodexAgentRoles(input) {
|
|
|
84
84
|
return report;
|
|
85
85
|
}
|
|
86
86
|
function roleToml(role, payload) {
|
|
87
|
-
const strategyLine = payload?.strategy === 'agent_type'
|
|
88
|
-
? `agent_type = "${payload.agent_type || role}"`
|
|
89
|
-
: `message_role_prefix = "${escapeToml(payload?.message_role_prefix || `Role: ${role}.`)}"`;
|
|
90
87
|
return [
|
|
91
88
|
`name = "${role}"`,
|
|
92
|
-
`description = "SKS managed 3.1.
|
|
93
|
-
strategyLine,
|
|
89
|
+
`description = "SKS managed 3.1.11 directive role: ${role}"`,
|
|
94
90
|
'model_reasoning_effort = "medium"',
|
|
95
91
|
role.includes('implementer') ? 'sandbox_mode = "workspace-write"' : 'sandbox_mode = "read-only"',
|
|
96
92
|
'approval_policy = "never"',
|
|
@@ -110,6 +106,10 @@ function roleToml(role, payload) {
|
|
|
110
106
|
''
|
|
111
107
|
].join('\n');
|
|
112
108
|
}
|
|
109
|
+
function isSksManagedDirectiveRole(text) {
|
|
110
|
+
return /SKS managed 3\.1\.(?:4|5|6|7|11) (?:directive|bounded) role/.test(text)
|
|
111
|
+
|| /\bmessage_role_prefix\s*=/.test(text) && /SKS managed 3\.1\./.test(text);
|
|
112
|
+
}
|
|
113
113
|
function blockersOf(value) {
|
|
114
114
|
return Boolean(value) && typeof value === 'object' && Array.isArray(value.blockers)
|
|
115
115
|
? (value.blockers).map((item) => String(item)).filter(Boolean)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { findCodexBinary } from '../codex-adapter.js';
|
|
3
|
+
import { compareSemverLike, parseCodexVersionText } from '../codex-compat/codex-version-policy.js';
|
|
4
|
+
import { nowIso, runProcess, writeJsonAtomic } from '../fsx.js';
|
|
5
|
+
import { CODEX_0140_FEATURE_KEYS, probeCodex0140Features } from './codex-0140-feature-probes.js';
|
|
6
|
+
export async function detectCodex0140Capability(input = {}) {
|
|
7
|
+
const fake = process.env.SKS_CODEX_0140_FAKE === '1';
|
|
8
|
+
const codexBin = fake ? input.codexBin || process.env.CODEX_BIN || 'codex' : input.codexBin || process.env.CODEX_BIN || await findCodexBinary();
|
|
9
|
+
const versionText = fake ? String(process.env.SKS_CODEX_VERSION_FAKE || 'codex-cli 0.140.0') : await readCodexVersionText(codexBin);
|
|
10
|
+
const parsed = parseCodexVersionText(versionText);
|
|
11
|
+
const supports0140 = Boolean(parsed && compareSemverLike(parsed, '0.140.0') >= 0);
|
|
12
|
+
const probeMode = process.env.SKS_CODEX_0140_PROBE === '1' ? 'feature-probe' : 'version-only';
|
|
13
|
+
const probeResults = probeMode === 'feature-probe'
|
|
14
|
+
? await probeCodex0140Features(codexBin, { fake, timeoutMs: Number(process.env.SKS_CODEX_0140_PROBE_TIMEOUT_MS || 3000) })
|
|
15
|
+
: Object.fromEntries(CODEX_0140_FEATURE_KEYS.map((key) => [key, 'skipped']));
|
|
16
|
+
const featureOk = (key) => supports0140 && (probeMode === 'version-only' || probeResults[key] !== 'failed');
|
|
17
|
+
const features = {
|
|
18
|
+
usage_views: featureOk('usage_views'),
|
|
19
|
+
goal_attachment_preservation: featureOk('goal_attachment_preservation'),
|
|
20
|
+
session_delete: featureOk('session_delete'),
|
|
21
|
+
import_command: featureOk('import_command'),
|
|
22
|
+
unified_mentions: featureOk('unified_mentions'),
|
|
23
|
+
bedrock_managed_auth: featureOk('bedrock_managed_auth'),
|
|
24
|
+
sqlite_auto_recovery: featureOk('sqlite_auto_recovery'),
|
|
25
|
+
mcp_reliability: featureOk('mcp_reliability'),
|
|
26
|
+
non_tty_interrupt: featureOk('non_tty_interrupt'),
|
|
27
|
+
large_repo_responsiveness: featureOk('large_repo_responsiveness')
|
|
28
|
+
};
|
|
29
|
+
const failed = Object.entries(probeResults).filter(([, status]) => status === 'failed').map(([key]) => `codex_0140_${key}_probe_failed`);
|
|
30
|
+
const blockers = [
|
|
31
|
+
...(!codexBin ? ['codex_cli_missing'] : []),
|
|
32
|
+
...(supports0140 ? [] : ['codex_0_140_required_for_0140_features']),
|
|
33
|
+
...(probeMode === 'feature-probe' ? failed : [])
|
|
34
|
+
];
|
|
35
|
+
return {
|
|
36
|
+
schema: 'sks.codex-0140-capability.v1',
|
|
37
|
+
generated_at: nowIso(),
|
|
38
|
+
ok: blockers.length === 0,
|
|
39
|
+
codex_version: parsed,
|
|
40
|
+
supports_0140: supports0140,
|
|
41
|
+
features,
|
|
42
|
+
blockers,
|
|
43
|
+
warnings: [],
|
|
44
|
+
codex_bin: codexBin || null,
|
|
45
|
+
probe_mode: probeMode,
|
|
46
|
+
feature_probe_results: probeResults
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export async function writeCodex0140CapabilityArtifacts(root, input = {}) {
|
|
50
|
+
const report = await detectCodex0140Capability({ codexBin: input.codexBin || null });
|
|
51
|
+
const rootArtifact = path.join(root, '.sneakoscope', 'codex-0140-capability.json');
|
|
52
|
+
await writeJsonAtomic(rootArtifact, report);
|
|
53
|
+
let missionArtifact = null;
|
|
54
|
+
if (input.missionId) {
|
|
55
|
+
missionArtifact = path.join(root, '.sneakoscope', 'missions', input.missionId, 'codex-0140-capability.json');
|
|
56
|
+
await writeJsonAtomic(missionArtifact, report);
|
|
57
|
+
}
|
|
58
|
+
return { report, root_artifact: rootArtifact, mission_artifact: missionArtifact };
|
|
59
|
+
}
|
|
60
|
+
async function readCodexVersionText(codexBin) {
|
|
61
|
+
if (!codexBin)
|
|
62
|
+
return null;
|
|
63
|
+
const result = await runProcess(codexBin, ['--version'], { timeoutMs: 10_000, maxOutputBytes: 16 * 1024 }).catch((err) => ({
|
|
64
|
+
code: 1,
|
|
65
|
+
stdout: '',
|
|
66
|
+
stderr: err?.message || String(err)
|
|
67
|
+
}));
|
|
68
|
+
const text = `${result.stdout || ''}${result.stderr || ''}`.trim();
|
|
69
|
+
return result.code === 0 ? text : text || null;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=codex-0140-capability.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { runProcess } from '../fsx.js';
|
|
2
|
+
export const CODEX_0140_FEATURE_KEYS = [
|
|
3
|
+
'usage_views',
|
|
4
|
+
'goal_attachment_preservation',
|
|
5
|
+
'session_delete',
|
|
6
|
+
'import_command',
|
|
7
|
+
'unified_mentions',
|
|
8
|
+
'bedrock_managed_auth',
|
|
9
|
+
'sqlite_auto_recovery',
|
|
10
|
+
'mcp_reliability',
|
|
11
|
+
'non_tty_interrupt',
|
|
12
|
+
'large_repo_responsiveness'
|
|
13
|
+
];
|
|
14
|
+
export async function probeCodex0140Features(codexBin, opts = {}) {
|
|
15
|
+
if (opts.fake) {
|
|
16
|
+
return Object.fromEntries(CODEX_0140_FEATURE_KEYS.map((key) => [key, process.env[`SKS_CODEX_0140_FAKE_${key.toUpperCase()}_FAIL`] === '1' ? 'failed' : 'passed']));
|
|
17
|
+
}
|
|
18
|
+
if (!codexBin)
|
|
19
|
+
return Object.fromEntries(CODEX_0140_FEATURE_KEYS.map((key) => [key, 'failed']));
|
|
20
|
+
const timeoutMs = Math.max(1, Number(opts.timeoutMs || process.env.SKS_CODEX_0140_PROBE_TIMEOUT_MS || 3000) || 3000);
|
|
21
|
+
const help = await runProcess(codexBin, ['--help'], { timeoutMs, maxOutputBytes: 256 * 1024 }).catch(() => ({ code: 1, stdout: '', stderr: '' }));
|
|
22
|
+
const text = `${help.stdout || ''}\n${help.stderr || ''}`;
|
|
23
|
+
const passIf140 = help.code === 0 ? 'passed' : 'skipped';
|
|
24
|
+
return {
|
|
25
|
+
usage_views: /usage/i.test(text) ? 'passed' : passIf140,
|
|
26
|
+
goal_attachment_preservation: /goal/i.test(text) ? 'passed' : passIf140,
|
|
27
|
+
session_delete: /delete/i.test(text) ? 'passed' : passIf140,
|
|
28
|
+
import_command: /import/i.test(text) ? 'passed' : passIf140,
|
|
29
|
+
unified_mentions: /@|mention|plugin|skill/i.test(text) ? 'passed' : passIf140,
|
|
30
|
+
bedrock_managed_auth: /bedrock/i.test(text) ? 'passed' : passIf140,
|
|
31
|
+
sqlite_auto_recovery: passIf140,
|
|
32
|
+
mcp_reliability: /mcp/i.test(text) ? 'passed' : passIf140,
|
|
33
|
+
non_tty_interrupt: passIf140,
|
|
34
|
+
large_repo_responsiveness: passIf140
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=codex-0140-feature-probes.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {} from './codex-0140-real-probes.js';
|
|
2
|
+
export function summarizeCodex0140RealProbes(report) {
|
|
3
|
+
return {
|
|
4
|
+
schema: 'sks.codex-0140-real-probe-summary.v1',
|
|
5
|
+
ok: report.ok,
|
|
6
|
+
passed: report.probes.filter((probe) => probe.status === 'passed').length,
|
|
7
|
+
skipped: report.probes.filter((probe) => probe.status === 'skipped').length,
|
|
8
|
+
failed: report.probes.filter((probe) => probe.status === 'failed').length,
|
|
9
|
+
blockers: report.blockers
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=codex-0140-real-probe-summary.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { nowIso, writeJsonAtomic } from '../fsx.js';
|
|
3
|
+
import { detectCodex0140Capability } from './codex-0140-capability.js';
|
|
4
|
+
import { CODEX_0140_FEATURE_KEYS } from './codex-0140-feature-probes.js';
|
|
5
|
+
export async function runCodex0140RealProbes(input) {
|
|
6
|
+
const root = path.resolve(input.root);
|
|
7
|
+
const requireReal = input.requireReal === true;
|
|
8
|
+
const capability = await detectCodex0140Capability();
|
|
9
|
+
const probes = CODEX_0140_FEATURE_KEYS.map((id) => {
|
|
10
|
+
if (!capability.supports_0140) {
|
|
11
|
+
return { id, status: requireReal ? 'failed' : 'skipped', reason: 'codex_0_140_not_available' };
|
|
12
|
+
}
|
|
13
|
+
return capability.features[id] ? { id, status: 'passed', reason: null } : { id, status: requireReal ? 'failed' : 'skipped', reason: `${id}_not_verified` };
|
|
14
|
+
});
|
|
15
|
+
const blockers = probes.filter((probe) => probe.status === 'failed').map((probe) => `codex_0140_real_probe_failed:${probe.id}`);
|
|
16
|
+
const report = {
|
|
17
|
+
schema: 'sks.codex-0140-real-probes.v1',
|
|
18
|
+
generated_at: nowIso(),
|
|
19
|
+
ok: blockers.length === 0,
|
|
20
|
+
require_real: requireReal,
|
|
21
|
+
allow_network: input.allowNetwork === true,
|
|
22
|
+
probes,
|
|
23
|
+
blockers
|
|
24
|
+
};
|
|
25
|
+
if (input.reportPath !== null)
|
|
26
|
+
await writeJsonAtomic(input.reportPath || path.join(root, '.sneakoscope', 'reports', requireReal ? 'codex-0140-real-probes-require-real.json' : 'codex-0140-real-probes.json'), report).catch(() => undefined);
|
|
27
|
+
return report;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=codex-0140-real-probes.js.map
|