sneakoscope 3.1.6 → 3.1.7
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 +2 -2
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/.sks-build-stamp.json +4 -4
- package/dist/bin/sks.js +1 -1
- package/dist/commands/codex-app.js +20 -2
- package/dist/commands/codex-native.js +18 -2
- package/dist/commands/doctor.js +36 -12
- package/dist/core/codex-app/codex-agent-role-sync.js +9 -5
- package/dist/core/codex-app/codex-init-deep.js +6 -2
- package/dist/core/codex-app/codex-skill-sync.js +4 -2
- package/dist/core/codex-control/codex-0138-capability.js +5 -2
- package/dist/core/codex-native/codex-native-feature-broker.js +74 -6
- package/dist/core/codex-native/codex-native-pattern-analysis.js +14 -2
- package/dist/core/codex-native/codex-native-reference-cache.js +98 -0
- package/dist/core/codex-native/codex-native-reference-source.js +50 -11
- package/dist/core/codex-native/codex-native-repair-transaction.js +150 -0
- package/dist/core/codex-plugins/codex-plugin-json.js +5 -2
- package/dist/core/commands/mad-sks-command.js +16 -0
- package/dist/core/fsx.js +1 -1
- package/dist/core/loops/loop-planner.js +1 -1
- package/dist/core/loops/loop-worker-prompts.js +2 -0
- package/dist/core/loops/loop-worker-runtime.js +8 -1
- package/dist/core/version.js +1 -1
- package/dist/scripts/codex-native-runtime-e2e-fixture.js +75 -0
- package/dist/scripts/loop-worker-fixture-child.js +2 -1
- package/dist/scripts/sks-3-1-5-directive-check-lib.js +1 -1
- package/dist/scripts/sks-3-1-6-directive-check-lib.js +2 -2
- package/dist/scripts/sks-3-1-7-directive-check-lib.js +58 -0
- package/package.json +13 -2
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.1.
|
|
38
|
+
SKS **3.1.7** closes Codex-native runtime-proof gaps with real routing blackboxes, neutral reference cache refresh, read-only broker checks, explicit managed-asset repair transactions, and final brand-neutral artifact scans.
|
|
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
|
|
|
@@ -79,7 +79,7 @@ npm run runtime:ts-python-boundary
|
|
|
79
79
|
npm run codex-control:all-pipelines
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
Change-aware release checks live behind `npm run release:check`; publish-authorizing full DAG checks use `npm run release:check:full`. Detailed release history is in [CHANGELOG.md](CHANGELOG.md), and release readiness is tracked in [docs/release-readiness.md](docs/release-readiness.md).
|
|
83
83
|
|
|
84
84
|
## 🍥 Parallelism, UX, And Integrations
|
|
85
85
|
|
|
@@ -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.7"),
|
|
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.1.
|
|
5
|
-
"source_digest": "
|
|
6
|
-
"source_file_count":
|
|
7
|
-
"built_at_source_time":
|
|
4
|
+
"package_version": "3.1.7",
|
|
5
|
+
"source_digest": "8927dd50bf67d1f2ce1df5744a3b21645a870f8f9182fe7b86a92affedf416aa",
|
|
6
|
+
"source_file_count": 2558,
|
|
7
|
+
"built_at_source_time": 1781491235171
|
|
8
8
|
}
|
package/dist/bin/sks.js
CHANGED
|
@@ -9,12 +9,15 @@ import { syncCodexAgentRoles } from '../core/codex-app/codex-agent-role-sync.js'
|
|
|
9
9
|
import { runCodexInitDeep } from '../core/codex-app/codex-init-deep.js';
|
|
10
10
|
import { buildCodexHookLifecycle } from '../core/codex-app/codex-hook-lifecycle.js';
|
|
11
11
|
import { resolveCodexAppExecutionProfile } from '../core/codex-app/codex-app-execution-profile.js';
|
|
12
|
+
import { repairCodexNativeManagedAssets } from '../core/codex-native/codex-native-repair-transaction.js';
|
|
12
13
|
export async function run(_command, args = []) {
|
|
13
14
|
const action = args[0] || 'check';
|
|
14
15
|
if (action === 'remote-control' || action === 'remote')
|
|
15
16
|
return codexAppRemoteControlCommand(args.slice(1));
|
|
16
|
-
if (action === 'harness-matrix')
|
|
17
|
-
|
|
17
|
+
if (action === 'harness-matrix') {
|
|
18
|
+
const root = await sksRoot();
|
|
19
|
+
return printCodexAppResult(args, await maybeRepairThenReadOnlyHarness(args, root));
|
|
20
|
+
}
|
|
18
21
|
if (action === 'skill-sync')
|
|
19
22
|
return printCodexAppResult(args, await syncCodexSksSkills({ root: await sksRoot(), apply: flag(args, '--apply') || flag(args, '--fix') }));
|
|
20
23
|
if (action === 'agent-role-sync')
|
|
@@ -103,4 +106,19 @@ function printCodexAppResult(args = [], result) {
|
|
|
103
106
|
if (result?.ok === false)
|
|
104
107
|
process.exitCode = 1;
|
|
105
108
|
}
|
|
109
|
+
async function maybeRepairThenReadOnlyHarness(args = [], root) {
|
|
110
|
+
const wantsRepair = flag(args, '--fix') || flag(args, '--apply') || flag(args, '--repair-codex-native');
|
|
111
|
+
if (!wantsRepair)
|
|
112
|
+
return buildCodexAppHarnessMatrix({ root, mode: 'read-only' });
|
|
113
|
+
const repair = await repairCodexNativeManagedAssets({ root, requestedBy: 'manual', yes: flag(args, '--yes') });
|
|
114
|
+
const matrix = await buildCodexAppHarnessMatrix({ root, mode: 'read-only' });
|
|
115
|
+
return {
|
|
116
|
+
schema: 'sks.codex-app-harness-read-repair-split.v1',
|
|
117
|
+
ok: repair.ok && matrix?.ok !== false,
|
|
118
|
+
repair,
|
|
119
|
+
matrix,
|
|
120
|
+
blockers: [...(repair.blockers || []), ...(matrix?.blockers || [])],
|
|
121
|
+
warnings: [...(repair.warnings || []), 'harness_probe_after_explicit_repair_transaction']
|
|
122
|
+
};
|
|
123
|
+
}
|
|
106
124
|
//# sourceMappingURL=codex-app.js.map
|
|
@@ -12,14 +12,15 @@ import { resolveCodexNativeInvocationPlan } from '../core/codex-native/codex-nat
|
|
|
12
12
|
import { buildCodexNativeInteropPolicy } from '../core/codex-native/codex-native-interop-policy.js';
|
|
13
13
|
import { analyzeCodexNativeReferenceSource } from '../core/codex-native/codex-native-reference-evidence.js';
|
|
14
14
|
import { writeCodexNativePatternAnalysis } from '../core/codex-native/codex-native-pattern-analysis.js';
|
|
15
|
+
import { repairCodexNativeManagedAssets } from '../core/codex-native/codex-native-repair-transaction.js';
|
|
15
16
|
export async function run(_command, args = []) {
|
|
16
17
|
const root = await sksRoot();
|
|
17
18
|
const action = String(args[0] || 'status');
|
|
18
19
|
if (action === 'status' || action === 'check' || action === 'feature-broker' || action === 'feature-matrix') {
|
|
19
|
-
return printCodexNativeResult(args, await
|
|
20
|
+
return printCodexNativeResult(args, await maybeRepairThenReadOnlyMatrix(args, root, () => buildCodexNativeFeatureMatrix({ root, mode: 'read-only' })));
|
|
20
21
|
}
|
|
21
22
|
if (action === 'harness-matrix' || action === 'harness-compat') {
|
|
22
|
-
return printCodexNativeResult(args, await
|
|
23
|
+
return printCodexNativeResult(args, await maybeRepairThenReadOnlyMatrix(args, root, () => buildCodexAppHarnessMatrix({ root, mode: 'read-only' })));
|
|
23
24
|
}
|
|
24
25
|
if (action === 'skill-sync')
|
|
25
26
|
return printCodexNativeResult(args, await syncCodexSksSkills({ root, apply: flag(args, '--apply') || flag(args, '--fix') }));
|
|
@@ -46,6 +47,21 @@ export async function run(_command, args = []) {
|
|
|
46
47
|
console.error('Usage: sks codex-native status|feature-broker|harness-compat|skill-sync|agent-role-sync|init-deep|hook-lifecycle|execution-profile|interop-policy|reference-evidence|pattern-analysis|invocation-plan [--json]');
|
|
47
48
|
process.exitCode = 1;
|
|
48
49
|
}
|
|
50
|
+
async function maybeRepairThenReadOnlyMatrix(args = [], root, matrixFn) {
|
|
51
|
+
const wantsRepair = flag(args, '--fix') || flag(args, '--apply') || flag(args, '--repair-codex-native');
|
|
52
|
+
if (!wantsRepair)
|
|
53
|
+
return matrixFn();
|
|
54
|
+
const repair = await repairCodexNativeManagedAssets({ root, requestedBy: 'manual', yes: flag(args, '--yes') });
|
|
55
|
+
const matrix = await matrixFn();
|
|
56
|
+
return {
|
|
57
|
+
schema: 'sks.codex-native-read-repair-split.v1',
|
|
58
|
+
ok: repair.ok && matrix?.ok !== false,
|
|
59
|
+
repair,
|
|
60
|
+
matrix,
|
|
61
|
+
blockers: [...(repair.blockers || []), ...(matrix?.blockers || [])],
|
|
62
|
+
warnings: [...(repair.warnings || []), 'matrix_probe_after_explicit_repair_transaction']
|
|
63
|
+
};
|
|
64
|
+
}
|
|
49
65
|
function printCodexNativeResult(args = [], result) {
|
|
50
66
|
if (flag(args, '--json')) {
|
|
51
67
|
printJson(result);
|
package/dist/commands/doctor.js
CHANGED
|
@@ -26,6 +26,7 @@ import { writeMcpPluginInventoryArtifacts } from '../core/mcp/mcp-plugin-invento
|
|
|
26
26
|
import { runDoctorZellijRepair, doctorZellijRepairConsoleLine } from '../core/doctor/doctor-zellij-repair.js';
|
|
27
27
|
import { buildCodexAppHarnessMatrix } from '../core/codex-app/codex-app-harness-matrix.js';
|
|
28
28
|
import { buildCodexNativeFeatureMatrix } from '../core/codex-native/codex-native-feature-broker.js';
|
|
29
|
+
import { repairCodexNativeManagedAssets } from '../core/codex-native/codex-native-repair-transaction.js';
|
|
29
30
|
export async function run(_command, args = []) {
|
|
30
31
|
const doctorFix = flag(args, '--fix');
|
|
31
32
|
let setupRepair = null;
|
|
@@ -198,7 +199,23 @@ export async function run(_command, args = []) {
|
|
|
198
199
|
const mcpPluginInventory = pluginInventory?.report
|
|
199
200
|
? await writeMcpPluginInventoryArtifacts(root, { inventory: pluginInventory.report }).catch((err) => ({ error: err?.message || String(err), candidates: null }))
|
|
200
201
|
: null;
|
|
201
|
-
const
|
|
202
|
+
const repairCodexNative = doctorFix && flag(args, '--repair-codex-native');
|
|
203
|
+
const codexNativeRepair = repairCodexNative
|
|
204
|
+
? await repairCodexNativeManagedAssets({
|
|
205
|
+
root,
|
|
206
|
+
requestedBy: 'doctor --fix',
|
|
207
|
+
yes: flag(args, '--yes') || flag(args, '-y')
|
|
208
|
+
}).catch((err) => ({
|
|
209
|
+
schema: 'sks.codex-native-repair-transaction.v1',
|
|
210
|
+
ok: false,
|
|
211
|
+
generated_at: new Date().toISOString(),
|
|
212
|
+
requested_by: 'doctor --fix',
|
|
213
|
+
repaired: [],
|
|
214
|
+
blockers: [err?.message || String(err)],
|
|
215
|
+
warnings: []
|
|
216
|
+
}))
|
|
217
|
+
: null;
|
|
218
|
+
const codexAppHarnessMatrix = await buildCodexAppHarnessMatrix({ root, mode: 'read-only' }).catch((err) => ({
|
|
202
219
|
schema: 'sks.codex-app-harness-matrix.v1',
|
|
203
220
|
ok: false,
|
|
204
221
|
codex_cli: { available: false, version: null },
|
|
@@ -207,7 +224,7 @@ export async function run(_command, args = []) {
|
|
|
207
224
|
blockers: [err?.message || String(err)],
|
|
208
225
|
warnings: []
|
|
209
226
|
}));
|
|
210
|
-
const codexNativeFeatureMatrix = await buildCodexNativeFeatureMatrix({ root,
|
|
227
|
+
const codexNativeFeatureMatrix = await buildCodexNativeFeatureMatrix({ root, mode: 'read-only' }).catch((err) => ({
|
|
211
228
|
schema: 'sks.codex-native-feature-matrix.v1',
|
|
212
229
|
ok: false,
|
|
213
230
|
codex_cli: { available: Boolean(codex.bin), version: codex.version || null, bin: codex.bin || null },
|
|
@@ -288,7 +305,7 @@ export async function run(_command, args = []) {
|
|
|
288
305
|
ready,
|
|
289
306
|
sneakoscope: { ok: await exists(`${root}/.sneakoscope`) },
|
|
290
307
|
package: { bytes: pkgBytes, human: formatBytes(pkgBytes) },
|
|
291
|
-
repair: { sks_update: sksUpdate, setup: setupRepair, codex_config: configRepair, migration_journal: migrationJournal, global_sks_installs: globalSksInstallCleanup, agent_role_config: agentRoleConfigRepair, zellij: zellijRepair }
|
|
308
|
+
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 }
|
|
292
309
|
};
|
|
293
310
|
if (flag(args, '--json')) {
|
|
294
311
|
printJson(result);
|
|
@@ -324,8 +341,14 @@ export async function run(_command, args = []) {
|
|
|
324
341
|
console.log(` Loop Mesh: ${runtimeReadiness.loop_mesh}`);
|
|
325
342
|
console.log(` QA Visual: ${runtimeReadiness.qa_visual}`);
|
|
326
343
|
console.log(` Research Sources: ${runtimeReadiness.research_sources}`);
|
|
327
|
-
|
|
328
|
-
|
|
344
|
+
console.log(` Image Follow-up: ${runtimeReadiness.image_followup}`);
|
|
345
|
+
for (const note of runtimeReadiness.notes)
|
|
346
|
+
console.log(` ${note}`);
|
|
347
|
+
if (runtimeReadiness.repair_actions.length) {
|
|
348
|
+
console.log('Repair actions:');
|
|
349
|
+
for (const action of runtimeReadiness.repair_actions)
|
|
350
|
+
console.log(` - ${action}`);
|
|
351
|
+
}
|
|
329
352
|
console.log('Codex App Harness:');
|
|
330
353
|
console.log(` plugins: ${codexAppHarnessMatrix.app_features?.plugin_json ? 'ok' : 'degraded'}`);
|
|
331
354
|
console.log(` hook approval: ${codexAppHarnessMatrix.app_features?.hook_approval_state_detectable ? 'ok' : 'unknown'}`);
|
|
@@ -424,14 +447,14 @@ function buildRuntimeReadiness(zellijReadiness, matrix) {
|
|
|
424
447
|
? 'ok'
|
|
425
448
|
: matrix?.codex_cli?.available ? 'degraded' : 'blocked';
|
|
426
449
|
const repairActions = [];
|
|
427
|
-
if (zellijStatus
|
|
428
|
-
repairActions.push('sks doctor --fix --yes');
|
|
450
|
+
if (zellijStatus !== 'ok') {
|
|
451
|
+
repairActions.push('Zellij: sks doctor --fix --yes');
|
|
452
|
+
repairActions.push('Homebrew + Zellij: sks doctor --fix --install-homebrew --yes');
|
|
453
|
+
}
|
|
429
454
|
if (codexNative !== 'ok')
|
|
430
|
-
repairActions.push('sks doctor --fix --repair-codex-native --yes');
|
|
455
|
+
repairActions.push('Codex Native managed assets: sks doctor --fix --repair-codex-native --yes');
|
|
431
456
|
if (matrix?.features?.project_memory?.ok !== true)
|
|
432
|
-
repairActions.push('sks codex-native init-deep --apply --directory-local');
|
|
433
|
-
if (hookPolicy !== 'approved-only')
|
|
434
|
-
repairActions.push('hook-derived evidence will not count until Codex hook approval is approved');
|
|
457
|
+
repairActions.push('Project memory: sks codex-native init-deep --apply --directory-local');
|
|
435
458
|
return {
|
|
436
459
|
schema: 'sks.runtime-readiness-story.v1',
|
|
437
460
|
zellij: zellijStatus,
|
|
@@ -439,6 +462,7 @@ function buildRuntimeReadiness(zellijReadiness, matrix) {
|
|
|
439
462
|
loop_mesh: agentStrategy === 'agent_type' ? 'ok' : 'fallback',
|
|
440
463
|
qa_visual: defaults.qa_visual_review_strategy || 'blocked',
|
|
441
464
|
research_sources: defaults.research_source_strategy || 'local-files',
|
|
465
|
+
image_followup: defaults.image_followup_strategy || 'blocked',
|
|
442
466
|
hook_evidence_policy: hookPolicy,
|
|
443
467
|
agent_role_strategy: agentStrategy,
|
|
444
468
|
notes: [
|
|
@@ -446,7 +470,7 @@ function buildRuntimeReadiness(zellijReadiness, matrix) {
|
|
|
446
470
|
...(hookPolicy !== 'approved-only' ? ['hook-derived evidence will not count'] : []),
|
|
447
471
|
...(agentStrategy !== 'agent_type' ? ['message-role fallback active'] : [])
|
|
448
472
|
],
|
|
449
|
-
repair_actions: repairActions
|
|
473
|
+
repair_actions: [...new Set(repairActions)]
|
|
450
474
|
};
|
|
451
475
|
}
|
|
452
476
|
// Assemble the explicit Zellij readiness block for `doctor --json` from the
|
|
@@ -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 && !current.includes('SKS managed 3.1.4 directive role') && !current.includes('SKS managed 3.1.5 directive role') && !current.includes('SKS managed 3.1.6 directive role') && !current.includes('SKS managed 3.1.6 bounded role'))
|
|
59
|
+
if (current && !current.includes('SKS managed 3.1.4 directive role') && !current.includes('SKS managed 3.1.5 directive role') && !current.includes('SKS managed 3.1.6 directive role') && !current.includes('SKS managed 3.1.6 bounded role') && !current.includes('SKS managed 3.1.7 directive role'))
|
|
60
60
|
continue;
|
|
61
61
|
await writeTextAtomic(file, roleToml(role, rolePayloads[role]));
|
|
62
62
|
created.push(file);
|
|
@@ -69,6 +69,9 @@ export async function syncCodexAgentRoles(input) {
|
|
|
69
69
|
apply: input.apply === true,
|
|
70
70
|
agent_type_supported: agentTypeProbe.supported,
|
|
71
71
|
fallback: agentTypeProbe.supported ? 'agent_type' : 'message-role',
|
|
72
|
+
strategy: agentTypeProbe.supported ? 'agent_type' : 'message-role',
|
|
73
|
+
probe_artifact_path: '.sneakoscope/reports/codex-agent-type-probe.json',
|
|
74
|
+
clobbered_user_roles: false,
|
|
72
75
|
codex_home: codexHome,
|
|
73
76
|
directive_roles: DIRECTIVE_ROLES,
|
|
74
77
|
role_payloads: rolePayloads,
|
|
@@ -86,21 +89,22 @@ function roleToml(role, payload) {
|
|
|
86
89
|
: `message_role_prefix = "${escapeToml(payload?.message_role_prefix || `Role: ${role}.`)}"`;
|
|
87
90
|
return [
|
|
88
91
|
`name = "${role}"`,
|
|
89
|
-
`description = "SKS managed 3.1.
|
|
92
|
+
`description = "SKS managed 3.1.7 directive role: ${role}"`,
|
|
90
93
|
strategyLine,
|
|
91
94
|
'model_reasoning_effort = "medium"',
|
|
92
95
|
role.includes('implementer') ? 'sandbox_mode = "workspace-write"' : 'sandbox_mode = "read-only"',
|
|
93
96
|
'approval_policy = "never"',
|
|
94
97
|
'developer_instructions = """',
|
|
95
|
-
`You are ${role}. SKS managed 3.1.
|
|
98
|
+
`You are ${role}. SKS managed 3.1.7 directive role with bounded ownership.`,
|
|
96
99
|
'Bounded ownership: use only the assigned owner files/directories and treat memory as guidance, not permission.',
|
|
97
100
|
role.includes('implementer') ? 'Maker/checker separation: implementer may patch only owner scope and cannot self-approve.' : 'Maker/checker separation: checker is read-only and must reject missing gates or missing proof artifacts.',
|
|
101
|
+
role.includes('implementer') ? 'Allowed sandbox: workspace-write only within assigned owner scope.' : 'Allowed sandbox: read-only; checker roles cannot mutate.',
|
|
98
102
|
role.includes('release') ? 'Release verifier: verify version truth, release DAG coverage, package scripts, packlist, and changelog evidence.' : '',
|
|
99
103
|
role.includes('zellij') ? 'UI/Zellij verifier: inspect readiness status, headless fallback, repair_required, pane proof, and slot telemetry without mutating unrelated UI state.' : '',
|
|
100
104
|
role.includes('codex') ? 'Codex native verifier: inspect hook approval, agent_type, skill sync, plugin inventory, MCP candidates, and invocation plan artifacts.' : '',
|
|
101
|
-
'Side
|
|
105
|
+
'Side-effect restrictions: no destructive shell, package publish, global config mutation, database mutation, or external service write unless the sealed route contract explicitly allows it.',
|
|
102
106
|
'Required proof artifacts: cite concrete repo paths, command outputs, and route-local JSON proof before claiming completion.',
|
|
103
|
-
'Final arbiter: parent integration owns final acceptance; this role supplies evidence and cannot override missing gates.',
|
|
107
|
+
'Final arbiter constraints: parent integration owns final acceptance; this role supplies evidence and cannot override missing gates.',
|
|
104
108
|
`Execution role strategy: ${payload?.strategy || 'message-role'}. Probe: ${payload?.probe_artifact_path || '.sneakoscope/reports/codex-agent-type-probe.json'}.`,
|
|
105
109
|
'"""',
|
|
106
110
|
''
|
|
@@ -28,7 +28,7 @@ export async function runCodexInitDeep(input = {}) {
|
|
|
28
28
|
}
|
|
29
29
|
if (existing.trim()) {
|
|
30
30
|
const beforeHash = hashText(existing);
|
|
31
|
-
const backup = `${agentsPath}.sks-backup-${beforeHash.slice(0, 12)}
|
|
31
|
+
const backup = `${agentsPath}.sks-backup-${Date.now()}-${beforeHash.slice(0, 12)}`;
|
|
32
32
|
await fs.copyFile(agentsPath, backup);
|
|
33
33
|
directoryLocalAgents.backup_paths.push(path.relative(root, backup));
|
|
34
34
|
directoryLocalAgents.backups_created += 1;
|
|
@@ -83,9 +83,10 @@ async function pruneBackups(root, agentsPath, keep) {
|
|
|
83
83
|
return [];
|
|
84
84
|
const dir = path.dirname(agentsPath);
|
|
85
85
|
const base = path.basename(agentsPath);
|
|
86
|
+
const backupPattern = new RegExp(`^${escapeRegExp(base)}\\.sks-backup-\\d{13}-[0-9a-f]{8,12}$`);
|
|
86
87
|
const rows = await fs.readdir(dir).catch(() => []);
|
|
87
88
|
const backups = rows
|
|
88
|
-
.filter((name) =>
|
|
89
|
+
.filter((name) => backupPattern.test(name))
|
|
89
90
|
.map((name) => path.join(dir, name))
|
|
90
91
|
.sort();
|
|
91
92
|
const remove = backups.slice(0, Math.max(0, backups.length - keep));
|
|
@@ -99,6 +100,9 @@ async function pruneBackups(root, agentsPath, keep) {
|
|
|
99
100
|
await guardedRm(guard, file, { force: true }).catch(() => undefined);
|
|
100
101
|
return remove;
|
|
101
102
|
}
|
|
103
|
+
function escapeRegExp(value) {
|
|
104
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
105
|
+
}
|
|
102
106
|
function hashText(text) {
|
|
103
107
|
let hash = 2166136261;
|
|
104
108
|
for (let index = 0; index < text.length; index += 1) {
|
|
@@ -56,7 +56,9 @@ export async function syncCodexSksSkills(input) {
|
|
|
56
56
|
interop: {
|
|
57
57
|
mode: 'coexist',
|
|
58
58
|
clobbered_external_routes: false,
|
|
59
|
-
clobbered_user_skills: false
|
|
59
|
+
clobbered_user_skills: false,
|
|
60
|
+
skipped_user_skills: skipped,
|
|
61
|
+
managed_skills: desired
|
|
60
62
|
},
|
|
61
63
|
blockers: []
|
|
62
64
|
};
|
|
@@ -84,7 +86,7 @@ function skillContent(name) {
|
|
|
84
86
|
`Use when: ${profile.when}`,
|
|
85
87
|
`Route: ${profile.command}`,
|
|
86
88
|
`Evidence: ${profile.evidence}`,
|
|
87
|
-
'Safety: keep route state bounded, preserve user and external route assets, and stop on hard blockers instead of fabricating fallback behavior.',
|
|
89
|
+
'Safety rules: keep route state bounded, preserve user and external route assets, and stop on hard blockers instead of fabricating fallback behavior.',
|
|
88
90
|
'Proof paths: write the route-local mission artifact named in Evidence before claiming completion.',
|
|
89
91
|
'Failure recovery: if a proof path cannot be produced, record the blocker and continue only when the selected SKS route has another allowed evidence path.',
|
|
90
92
|
`Fallback: ${profile.fallback}`,
|
|
@@ -22,6 +22,7 @@ export async function detectCodex0138Capability(input = {}) {
|
|
|
22
22
|
};
|
|
23
23
|
const pluginJsonOk = atLeast138 && (probeMode === 'version-only' || featureProbeResults.plugin_json !== 'failed');
|
|
24
24
|
const appHandoffOk = atLeast138 && (probeMode === 'version-only' || featureProbeResults.app_handoff_platform === 'passed');
|
|
25
|
+
const imagePathExposureOk = atLeast138 && process.env.SKS_CODEX_0138_FAKE_IMAGE_PATH_FAIL !== '1';
|
|
25
26
|
const blockers = [
|
|
26
27
|
...(!codexBin ? ['codex_cli_missing'] : []),
|
|
27
28
|
...(atLeast138 ? [] : ['codex_0_138_required_for_app_plugin_features']),
|
|
@@ -36,7 +37,7 @@ export async function detectCodex0138Capability(input = {}) {
|
|
|
36
37
|
parsed_version: parsed,
|
|
37
38
|
supports_app_handoff: appHandoffOk,
|
|
38
39
|
supports_plugin_json: pluginJsonOk,
|
|
39
|
-
supports_image_path_exposure:
|
|
40
|
+
supports_image_path_exposure: imagePathExposureOk,
|
|
40
41
|
supports_model_defined_efforts: atLeast138,
|
|
41
42
|
supports_app_server_token_usage: atLeast138,
|
|
42
43
|
supports_v2_pat_auth: atLeast138,
|
|
@@ -78,7 +79,9 @@ async function probeCodex0138Features(codexBin, opts = {}) {
|
|
|
78
79
|
if (opts.fake) {
|
|
79
80
|
return {
|
|
80
81
|
plugin_json: process.env.SKS_CODEX_0138_FAKE_PLUGIN_JSON_FAIL === '1' ? 'failed' : 'passed',
|
|
81
|
-
app_handoff_platform: process.
|
|
82
|
+
app_handoff_platform: process.env.SKS_CODEX_0138_FAKE_APP_HANDOFF_FAIL === '1'
|
|
83
|
+
? 'failed'
|
|
84
|
+
: process.platform === 'darwin' || process.platform === 'win32' ? 'passed' : 'failed',
|
|
82
85
|
image_path_exposure_contract: 'sks-enforced'
|
|
83
86
|
};
|
|
84
87
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
2
3
|
import { findCodexBinary } from '../codex-adapter.js';
|
|
3
4
|
import { codexAppIntegrationStatus } from '../codex-app.js';
|
|
4
|
-
import { syncCodexAgentRoles } from '../codex-app/codex-agent-role-sync.js';
|
|
5
5
|
import { probeCodexAgentTypeSupport } from '../codex-app/codex-agent-type-probe.js';
|
|
6
6
|
import { probeCodexHookApprovalState } from '../codex-app/codex-hook-approval-probe.js';
|
|
7
|
-
import { syncCodexSksSkills } from '../codex-app/codex-skill-sync.js';
|
|
8
7
|
import { detectCodex0138Capability } from '../codex-control/codex-0138-capability.js';
|
|
9
8
|
import { detectCodex0139Capability } from '../codex-control/codex-0139-capability.js';
|
|
10
9
|
import { buildCodexPluginInventory } from '../codex-plugins/codex-plugin-json.js';
|
|
@@ -12,8 +11,13 @@ import { nowIso, runProcess, writeJsonAtomic } from '../fsx.js';
|
|
|
12
11
|
import { buildMcpPluginServerCandidates } from '../mcp/mcp-plugin-inventory.js';
|
|
13
12
|
import { codexNativeFeatureState, computeCodexNativeInvocationDefaults } from './codex-native-feature-matrix.js';
|
|
14
13
|
const REPORT_PATH = '.sneakoscope/reports/codex-native-feature-matrix.json';
|
|
14
|
+
const REQUIRED_SKILL_NAMES = ['loop', 'naruto', 'qa-loop', 'research', 'dfix', 'image-ux-review', 'computer-use', 'init-deep'];
|
|
15
|
+
const REQUIRED_AGENT_ROLES = ['sks-explorer', 'sks-planner', 'sks-implementer', 'sks-checker', 'sks-release-verifier', 'sks-zellij-ui-verifier', 'sks-codex-probe-verifier'];
|
|
15
16
|
export async function buildCodexNativeFeatureMatrix(input = { root: process.cwd() }) {
|
|
16
17
|
const root = path.resolve(input.root || process.cwd());
|
|
18
|
+
const deprecatedApplyRepairs = input.applyRepairs === true;
|
|
19
|
+
const mode = input.mode || (deprecatedApplyRepairs || input.repairManagedAssets === true ? 'repair' : 'read-only');
|
|
20
|
+
const repairManagedAssets = mode === 'repair' && (input.repairManagedAssets === true || deprecatedApplyRepairs);
|
|
17
21
|
const fixtureMode = process.env.SKS_CODEX_0138_FAKE === '1' || process.env.SKS_CODEX_0139_FAKE === '1' || process.env.SKS_CODEX_PLUGIN_JSON_FAKE === '1';
|
|
18
22
|
const codexBin = fixtureMode ? process.env.CODEX_BIN || 'codex' : await findCodexBinary().catch(() => null);
|
|
19
23
|
const version = codexBin ? await codexVersion(codexBin) : null;
|
|
@@ -57,8 +61,8 @@ export async function buildCodexNativeFeatureMatrix(input = { root: process.cwd(
|
|
|
57
61
|
blockers: [messageOf(err)],
|
|
58
62
|
warnings: ['agent_type_probe_failed_message_role_fallback']
|
|
59
63
|
}));
|
|
60
|
-
const skillSync = await
|
|
61
|
-
const agentRoles = await
|
|
64
|
+
const skillSync = await inspectManagedSkillState(root);
|
|
65
|
+
const agentRoles = await inspectManagedAgentRoleState(root);
|
|
62
66
|
const appRecord = isRecord(app) ? app : {};
|
|
63
67
|
const requiredSkills = isRecord(appRecord.required_skills) ? appRecord.required_skills : {};
|
|
64
68
|
const skills = isRecord(appRecord.skills) ? appRecord.skills : {};
|
|
@@ -91,7 +95,7 @@ export async function buildCodexNativeFeatureMatrix(input = { root: process.cwd(
|
|
|
91
95
|
unavailableStatus: 'fallback'
|
|
92
96
|
}),
|
|
93
97
|
mcp_inventory: codexNativeFeatureState({
|
|
94
|
-
ok: mcpCandidates.candidates.length > 0
|
|
98
|
+
ok: mcpCandidates.candidates.length > 0,
|
|
95
99
|
source: 'plugin-inventory',
|
|
96
100
|
artifact_path: '.sneakoscope/mcp-plugin-server-candidates.json',
|
|
97
101
|
evidence: [`candidate_count:${mcpCandidates.candidates.length}`],
|
|
@@ -134,7 +138,11 @@ export async function buildCodexNativeFeatureMatrix(input = { root: process.cwd(
|
|
|
134
138
|
...(!codexBin ? ['codex_cli_missing'] : []),
|
|
135
139
|
...Object.values(features).flatMap((feature) => feature.blockers)
|
|
136
140
|
],
|
|
137
|
-
warnings:
|
|
141
|
+
warnings: [
|
|
142
|
+
...Object.values(features).flatMap((feature) => feature.warnings),
|
|
143
|
+
...(deprecatedApplyRepairs ? ['deprecated_apply_repairs_input'] : []),
|
|
144
|
+
...(mode === 'repair' && !repairManagedAssets ? ['repair_mode_without_managed_asset_repair'] : [])
|
|
145
|
+
]
|
|
138
146
|
};
|
|
139
147
|
const matrix = {
|
|
140
148
|
...matrixBase,
|
|
@@ -144,6 +152,66 @@ export async function buildCodexNativeFeatureMatrix(input = { root: process.cwd(
|
|
|
144
152
|
await writeCodexNativeFeatureMatrix(root, matrix, input.missionDir);
|
|
145
153
|
return matrix;
|
|
146
154
|
}
|
|
155
|
+
async function inspectManagedSkillState(root) {
|
|
156
|
+
const skillRoots = [
|
|
157
|
+
path.join(root, '.agents', 'skills'),
|
|
158
|
+
...(process.env.CODEX_HOME ? [path.join(process.env.CODEX_HOME, 'skills')] : [])
|
|
159
|
+
];
|
|
160
|
+
let existingCount = 0;
|
|
161
|
+
const managed = new Set();
|
|
162
|
+
for (const dir of skillRoots) {
|
|
163
|
+
const rows = await fs.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
164
|
+
existingCount += rows.filter((row) => row.isDirectory()).length;
|
|
165
|
+
for (const name of REQUIRED_SKILL_NAMES) {
|
|
166
|
+
if (managed.has(name))
|
|
167
|
+
continue;
|
|
168
|
+
const text = await fs.readFile(path.join(dir, name, 'SKILL.md'), 'utf8').catch(() => '');
|
|
169
|
+
if (text.includes('BEGIN SKS MANAGED SKILL'))
|
|
170
|
+
managed.add(name);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const missing = REQUIRED_SKILL_NAMES.filter((name) => !managed.has(name));
|
|
174
|
+
return {
|
|
175
|
+
ok: missing.length === 0,
|
|
176
|
+
apply: false,
|
|
177
|
+
artifact_path: '.sneakoscope/reports/codex-skill-sync.json',
|
|
178
|
+
existing_count: existingCount,
|
|
179
|
+
managed_count: managed.size,
|
|
180
|
+
missing_required: missing,
|
|
181
|
+
blockers: missing.length ? [`managed_skills_missing:${missing.join(',')}`] : [],
|
|
182
|
+
warnings: existingCount > managed.size ? ['non_sks_skill_dirs_ignored'] : []
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
async function inspectManagedAgentRoleState(root) {
|
|
186
|
+
const dirs = [
|
|
187
|
+
path.join(root, '.codex', 'agents'),
|
|
188
|
+
...(process.env.CODEX_HOME ? [path.join(process.env.CODEX_HOME, 'agents')] : [])
|
|
189
|
+
];
|
|
190
|
+
let existingCount = 0;
|
|
191
|
+
const managed = new Set();
|
|
192
|
+
for (const dir of dirs) {
|
|
193
|
+
const rows = await fs.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
194
|
+
existingCount += rows.filter((row) => row.isFile() && row.name.endsWith('.toml')).length;
|
|
195
|
+
for (const role of REQUIRED_AGENT_ROLES) {
|
|
196
|
+
if (managed.has(role))
|
|
197
|
+
continue;
|
|
198
|
+
const text = await fs.readFile(path.join(dir, `${role}.toml`), 'utf8').catch(() => '');
|
|
199
|
+
if (text.includes('SKS managed 3.1.7 directive role'))
|
|
200
|
+
managed.add(role);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const missing = REQUIRED_AGENT_ROLES.filter((role) => !managed.has(role));
|
|
204
|
+
return {
|
|
205
|
+
ok: missing.length === 0,
|
|
206
|
+
apply: false,
|
|
207
|
+
artifact_path: '.sneakoscope/reports/codex-agent-role-sync.json',
|
|
208
|
+
existing_count: existingCount,
|
|
209
|
+
managed_count: managed.size,
|
|
210
|
+
missing_required: missing,
|
|
211
|
+
blockers: missing.length ? [`managed_agent_roles_missing:${missing.join(',')}`] : [],
|
|
212
|
+
warnings: existingCount > managed.size ? ['non_sks_agent_roles_ignored'] : []
|
|
213
|
+
};
|
|
214
|
+
}
|
|
147
215
|
export async function writeCodexNativeFeatureMatrix(root, matrix, missionDir) {
|
|
148
216
|
await writeJsonAtomic(path.join(root, REPORT_PATH), matrix);
|
|
149
217
|
if (missionDir)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
2
3
|
import { nowIso, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
|
|
3
4
|
import { analyzeCodexNativeReferenceSource, renderCodexNativeReferenceMarkdown } from './codex-native-reference-source.js';
|
|
4
5
|
const PATTERN_ROWS = [
|
|
@@ -19,22 +20,26 @@ export async function buildCodexNativePatternAnalysis(input) {
|
|
|
19
20
|
const root = path.resolve(input.root);
|
|
20
21
|
const evidence = input.evidence || await analyzeCodexNativeReferenceSource({ root, sourceDir: input.sourceDir || null, writeReport: true }).catch(() => null);
|
|
21
22
|
const evidenceByPattern = new Map();
|
|
23
|
+
const confidenceByPattern = new Map();
|
|
22
24
|
for (const item of evidence?.evidence || []) {
|
|
23
25
|
const list = evidenceByPattern.get(item.pattern_id) || [];
|
|
24
26
|
list.push(item.snippet_hash);
|
|
25
27
|
evidenceByPattern.set(item.pattern_id, list);
|
|
28
|
+
confidenceByPattern.set(item.pattern_id, strongerConfidence(confidenceByPattern.get(item.pattern_id), item.confidence));
|
|
26
29
|
}
|
|
27
30
|
return {
|
|
28
31
|
schema: 'sks.codex-native-pattern-analysis.v1',
|
|
29
32
|
generated_at: nowIso(),
|
|
30
33
|
source_kind: 'external-reference-source',
|
|
31
|
-
source_ref: evidence?.source_ref || input.sourceDir || '.sneakoscope/cache/codex-native-reference',
|
|
34
|
+
source_ref: evidence?.source_ref || neutralSourceRef(input.sourceDir || '.sneakoscope/cache/codex-native-reference'),
|
|
32
35
|
source_sha: evidence?.source_sha || null,
|
|
36
|
+
source_url_hash: evidence?.source_url_hash || null,
|
|
37
|
+
cache_report_path: evidence?.cache_report_path || null,
|
|
33
38
|
patterns: PATTERN_ROWS.map((pattern) => {
|
|
34
39
|
const hashes = evidenceByPattern.get(pattern.id) || [];
|
|
35
40
|
return {
|
|
36
41
|
...pattern,
|
|
37
|
-
confidence: hashes.length ? '
|
|
42
|
+
confidence: confidenceByPattern.get(pattern.id) || (hashes.length ? 'medium' : evidence ? 'low' : 'low'),
|
|
38
43
|
evidence_hashes: hashes
|
|
39
44
|
};
|
|
40
45
|
}),
|
|
@@ -42,6 +47,13 @@ export async function buildCodexNativePatternAnalysis(input) {
|
|
|
42
47
|
warnings: evidence?.warnings || ['reference_evidence_unavailable']
|
|
43
48
|
};
|
|
44
49
|
}
|
|
50
|
+
function neutralSourceRef(value) {
|
|
51
|
+
return `source:${createHash('sha256').update(value).digest('hex').slice(0, 16)}`;
|
|
52
|
+
}
|
|
53
|
+
function strongerConfidence(current, next) {
|
|
54
|
+
const rank = { low: 0, medium: 1, high: 2 };
|
|
55
|
+
return current && rank[current] >= rank[next] ? current : next;
|
|
56
|
+
}
|
|
45
57
|
export async function writeCodexNativePatternAnalysis(root, input = {}) {
|
|
46
58
|
const evidence = await analyzeCodexNativeReferenceSource({ root, sourceDir: input.sourceDir || null, writeReport: true }).catch(() => null);
|
|
47
59
|
const report = await buildCodexNativePatternAnalysis({ root, evidence, sourceDir: input.sourceDir || null });
|