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
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { exists, nowIso, runProcess, sha256, writeJsonAtomic } from '../fsx.js';
|
|
4
|
+
export async function ensureCodexNativeReferenceSnapshot(input) {
|
|
5
|
+
const root = path.resolve(input.root);
|
|
6
|
+
const cacheDir = path.join(root, '.sneakoscope', 'cache', 'codex-native-reference');
|
|
7
|
+
const reportPath = path.join(root, '.sneakoscope', 'reports', 'codex-native-reference-cache.json');
|
|
8
|
+
const sourceUrl = input.sourceUrl ?? process.env.SKS_CODEX_NATIVE_REFERENCE_SOURCE_URL ?? null;
|
|
9
|
+
const sourceRef = input.ref || process.env.SKS_CODEX_NATIVE_REFERENCE_REF || 'HEAD';
|
|
10
|
+
const offline = input.offline === true || process.env.SKS_CODEX_NATIVE_REFERENCE_OFFLINE === '1' || !sourceUrl;
|
|
11
|
+
const blockers = [];
|
|
12
|
+
const warnings = [];
|
|
13
|
+
let refreshed = false;
|
|
14
|
+
if (!offline && sourceUrl) {
|
|
15
|
+
const parent = path.dirname(cacheDir);
|
|
16
|
+
await fs.mkdir(parent, { recursive: true });
|
|
17
|
+
const gitDir = path.join(cacheDir, '.git');
|
|
18
|
+
const timeoutMs = input.timeoutMs || 60_000;
|
|
19
|
+
if (!(await exists(gitDir))) {
|
|
20
|
+
if (await exists(cacheDir))
|
|
21
|
+
await fs.rm(cacheDir, { recursive: true, force: true });
|
|
22
|
+
const cloneArgs = [
|
|
23
|
+
'clone',
|
|
24
|
+
'--depth',
|
|
25
|
+
'1',
|
|
26
|
+
'--filter=blob:none',
|
|
27
|
+
...(sourceRef === 'HEAD' ? [] : ['--branch', sourceRef]),
|
|
28
|
+
sourceUrl,
|
|
29
|
+
cacheDir
|
|
30
|
+
];
|
|
31
|
+
const cloned = await runProcess('git', cloneArgs, {
|
|
32
|
+
timeoutMs,
|
|
33
|
+
maxOutputBytes: 128 * 1024
|
|
34
|
+
}).catch((err) => ({ code: 1, stderr: messageOf(err), stdout: '' }));
|
|
35
|
+
if (cloned.code === 0)
|
|
36
|
+
refreshed = true;
|
|
37
|
+
else
|
|
38
|
+
blockers.push('source_snapshot_fetch_failed');
|
|
39
|
+
}
|
|
40
|
+
else if (input.refresh === true) {
|
|
41
|
+
const fetched = await runProcess('git', ['fetch', '--depth', '1', 'origin', sourceRef], {
|
|
42
|
+
cwd: cacheDir,
|
|
43
|
+
timeoutMs,
|
|
44
|
+
maxOutputBytes: 128 * 1024
|
|
45
|
+
}).catch((err) => ({ code: 1, stderr: messageOf(err), stdout: '' }));
|
|
46
|
+
if (fetched.code === 0) {
|
|
47
|
+
await runProcess('git', ['checkout', 'FETCH_HEAD'], { cwd: cacheDir, timeoutMs, maxOutputBytes: 64 * 1024 }).catch(() => null);
|
|
48
|
+
refreshed = true;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
blockers.push('source_snapshot_refresh_failed');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
warnings.push(sourceUrl ? 'reference_cache_offline_mode' : 'reference_source_url_missing_offline_cache_only');
|
|
57
|
+
}
|
|
58
|
+
const cacheExists = await hasTextFiles(cacheDir);
|
|
59
|
+
if (!cacheExists)
|
|
60
|
+
blockers.push('source_snapshot_missing');
|
|
61
|
+
const report = {
|
|
62
|
+
schema: 'sks.codex-native-reference-cache.v1',
|
|
63
|
+
generated_at: nowIso(),
|
|
64
|
+
ok: blockers.length === 0,
|
|
65
|
+
cache_dir: path.relative(root, cacheDir),
|
|
66
|
+
source_url_hash: sourceUrl ? sha256(sourceUrl) : null,
|
|
67
|
+
source_ref: sourceRef,
|
|
68
|
+
source_sha: await gitSha(cacheDir),
|
|
69
|
+
refreshed,
|
|
70
|
+
offline,
|
|
71
|
+
blockers: [...new Set(blockers)],
|
|
72
|
+
warnings: [...new Set(warnings)]
|
|
73
|
+
};
|
|
74
|
+
await writeJsonAtomic(reportPath, report).catch(() => undefined);
|
|
75
|
+
return report;
|
|
76
|
+
}
|
|
77
|
+
async function hasTextFiles(dir) {
|
|
78
|
+
const rows = await fs.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
79
|
+
for (const row of rows) {
|
|
80
|
+
if (row.name === '.git' || row.name === 'node_modules' || row.name === 'dist')
|
|
81
|
+
continue;
|
|
82
|
+
const full = path.join(dir, row.name);
|
|
83
|
+
if (row.isFile() && /\.(md|txt|json|toml|ya?ml|js|ts|mjs|cjs)$/i.test(row.name))
|
|
84
|
+
return true;
|
|
85
|
+
if (row.isDirectory() && await hasTextFiles(full))
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
async function gitSha(sourceDir) {
|
|
91
|
+
const run = await runProcess('git', ['rev-parse', 'HEAD'], { cwd: sourceDir, timeoutMs: 5000, maxOutputBytes: 4096 }).catch(() => null);
|
|
92
|
+
const sha = run?.code === 0 ? `${run.stdout || ''}`.trim() : '';
|
|
93
|
+
return /^[0-9a-f]{40}$/i.test(sha) ? sha : null;
|
|
94
|
+
}
|
|
95
|
+
function messageOf(err) {
|
|
96
|
+
return err instanceof Error ? err.message : String(err);
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=codex-native-reference-cache.js.map
|
|
@@ -2,6 +2,7 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
4
|
import { nowIso, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
|
|
5
|
+
import { ensureCodexNativeReferenceSnapshot } from './codex-native-reference-cache.js';
|
|
5
6
|
const PATTERNS = [
|
|
6
7
|
{ id: 'no-global-optional-tooling', claim: 'optional tooling avoids mandatory global install', re: /\bnpx\b|no[- ]global|global install/i },
|
|
7
8
|
{ id: 'plugin-lifecycle-state-separation', claim: 'plugin install state is separated from approval/readiness', re: /\bplugin\b.+\b(install|enable|marketplace|lifecycle)\b/i },
|
|
@@ -18,7 +19,26 @@ const PATTERNS = [
|
|
|
18
19
|
];
|
|
19
20
|
export async function analyzeCodexNativeReferenceSource(input) {
|
|
20
21
|
const root = path.resolve(input.root);
|
|
21
|
-
const
|
|
22
|
+
const cacheInput = input.sourceRef ? { root, ref: input.sourceRef } : { root };
|
|
23
|
+
const cache = input.sourceDir
|
|
24
|
+
? null
|
|
25
|
+
: await ensureCodexNativeReferenceSnapshot(cacheInput).catch((err) => ({
|
|
26
|
+
schema: 'sks.codex-native-reference-cache.v1',
|
|
27
|
+
generated_at: nowIso(),
|
|
28
|
+
ok: false,
|
|
29
|
+
cache_dir: '.sneakoscope/cache/codex-native-reference',
|
|
30
|
+
source_url_hash: null,
|
|
31
|
+
source_ref: input.sourceRef || 'HEAD',
|
|
32
|
+
source_sha: null,
|
|
33
|
+
refreshed: false,
|
|
34
|
+
offline: true,
|
|
35
|
+
blockers: [messageOf(err)],
|
|
36
|
+
warnings: ['reference_cache_exception']
|
|
37
|
+
}));
|
|
38
|
+
const sourceDir = input.sourceDir
|
|
39
|
+
? path.resolve(input.sourceDir)
|
|
40
|
+
: path.join(root, cache?.cache_dir || '.sneakoscope/cache/codex-native-reference');
|
|
41
|
+
const confidence = input.sourceDir ? 'high' : cache?.ok ? cache.refreshed ? 'high' : 'medium' : 'low';
|
|
22
42
|
const files = await listTextFiles(sourceDir);
|
|
23
43
|
const evidence = [];
|
|
24
44
|
const blockers = [];
|
|
@@ -26,7 +46,7 @@ export async function analyzeCodexNativeReferenceSource(input) {
|
|
|
26
46
|
const rel = path.relative(sourceDir, file).split(path.sep).join('/');
|
|
27
47
|
const text = await fs.readFile(file, 'utf8').catch(() => '');
|
|
28
48
|
if (text)
|
|
29
|
-
evidence.push(...extractCodexNativeEvidence(rel, text));
|
|
49
|
+
evidence.push(...extractCodexNativeEvidence(rel, text).map((row) => ({ ...row, confidence })));
|
|
30
50
|
}
|
|
31
51
|
if (!files.length || !evidence.length)
|
|
32
52
|
blockers.push('source_snapshot_missing');
|
|
@@ -34,11 +54,14 @@ export async function analyzeCodexNativeReferenceSource(input) {
|
|
|
34
54
|
schema: 'sks.codex-native-reference-evidence.v1',
|
|
35
55
|
generated_at: nowIso(),
|
|
36
56
|
source_kind: 'external-reference-source',
|
|
37
|
-
source_ref: input
|
|
38
|
-
source_sha: await gitSha(sourceDir),
|
|
57
|
+
source_ref: neutralSourceRef(input, cache, sourceDir),
|
|
58
|
+
source_sha: cache?.source_sha || await gitSha(sourceDir),
|
|
59
|
+
source_url_hash: cache?.source_url_hash || null,
|
|
60
|
+
cache_report_path: cache ? '.sneakoscope/reports/codex-native-reference-cache.json' : null,
|
|
61
|
+
cache,
|
|
39
62
|
evidence,
|
|
40
|
-
blockers,
|
|
41
|
-
warnings: blockers.length ? ['reference_evidence_incomplete'] : []
|
|
63
|
+
blockers: [...new Set([...blockers, ...(cache?.blockers || [])])],
|
|
64
|
+
warnings: [...new Set([...(cache?.warnings || []), ...(blockers.length ? ['reference_evidence_incomplete'] : [])])]
|
|
42
65
|
};
|
|
43
66
|
if (input.writeReport !== false) {
|
|
44
67
|
await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-native-reference-evidence.json'), report).catch(() => undefined);
|
|
@@ -74,6 +97,8 @@ export function renderCodexNativeReferenceMarkdown(report) {
|
|
|
74
97
|
'',
|
|
75
98
|
`Generated at: \`${report.generated_at}\``,
|
|
76
99
|
`Source kind: \`${report.source_kind}\``,
|
|
100
|
+
`Source URL hash: \`${report.source_url_hash || 'none'}\``,
|
|
101
|
+
`Source SHA: \`${report.source_sha || 'none'}\``,
|
|
77
102
|
'',
|
|
78
103
|
'| Pattern | File | Lines | Snippet Hash | Confidence |',
|
|
79
104
|
'|---|---|---:|---|---|',
|
|
@@ -81,21 +106,32 @@ export function renderCodexNativeReferenceMarkdown(report) {
|
|
|
81
106
|
''
|
|
82
107
|
].join('\n');
|
|
83
108
|
}
|
|
109
|
+
function neutralSourceRef(input, cache, sourceDir) {
|
|
110
|
+
if (cache)
|
|
111
|
+
return `cache:${createHash('sha256').update(`${cache.cache_dir}:${cache.source_ref}:${cache.source_sha || ''}`).digest('hex').slice(0, 16)}`;
|
|
112
|
+
if (input.sourceRef)
|
|
113
|
+
return `explicit:${createHash('sha256').update(input.sourceRef).digest('hex').slice(0, 16)}`;
|
|
114
|
+
return `explicit-source-dir:${createHash('sha256').update(sourceDir).digest('hex').slice(0, 16)}`;
|
|
115
|
+
}
|
|
84
116
|
async function listTextFiles(dir) {
|
|
85
117
|
const out = [];
|
|
86
|
-
await walk(dir, out);
|
|
87
|
-
return out
|
|
118
|
+
await walk(dir, out, 500);
|
|
119
|
+
return out;
|
|
88
120
|
}
|
|
89
|
-
async function walk(dir, out) {
|
|
121
|
+
async function walk(dir, out, maxFiles) {
|
|
122
|
+
if (out.length >= maxFiles)
|
|
123
|
+
return;
|
|
90
124
|
const rows = await fs.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
91
125
|
for (const row of rows) {
|
|
126
|
+
if (out.length >= maxFiles)
|
|
127
|
+
return;
|
|
92
128
|
const full = path.join(dir, row.name);
|
|
93
129
|
if (row.isDirectory()) {
|
|
94
130
|
if (['.git', 'node_modules', 'dist'].includes(row.name))
|
|
95
131
|
continue;
|
|
96
|
-
await walk(full, out);
|
|
132
|
+
await walk(full, out, maxFiles);
|
|
97
133
|
}
|
|
98
|
-
else if (row.isFile()) {
|
|
134
|
+
else if (row.isFile() && /\.(md|txt|json|toml|ya?ml|js|ts|mjs|cjs)$/i.test(row.name)) {
|
|
99
135
|
out.push(full);
|
|
100
136
|
}
|
|
101
137
|
}
|
|
@@ -107,4 +143,7 @@ async function gitSha(sourceDir) {
|
|
|
107
143
|
return (await fs.readFile(path.join(sourceDir, '.git', ref), 'utf8').catch(() => '')).trim() || null;
|
|
108
144
|
return /^[0-9a-f]{40}$/i.test(head.trim()) ? head.trim() : null;
|
|
109
145
|
}
|
|
146
|
+
function messageOf(err) {
|
|
147
|
+
return err instanceof Error ? err.message : String(err);
|
|
148
|
+
}
|
|
110
149
|
//# sourceMappingURL=codex-native-reference-source.js.map
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { buildCodexHookLifecycle } from '../codex-app/codex-hook-lifecycle.js';
|
|
3
|
+
import { runCodexInitDeep } from '../codex-app/codex-init-deep.js';
|
|
4
|
+
import { syncCodexAgentRoles } from '../codex-app/codex-agent-role-sync.js';
|
|
5
|
+
import { syncCodexSksSkills } from '../codex-app/codex-skill-sync.js';
|
|
6
|
+
import { nowIso, writeJsonAtomic } from '../fsx.js';
|
|
7
|
+
import { createRequestedScopeContract } from '../safety/requested-scope-contract.js';
|
|
8
|
+
import { evaluateMutation, mutationLedgerPath, recordMutation } from '../safety/mutation-ledger.js';
|
|
9
|
+
export async function repairCodexNativeManagedAssets(input) {
|
|
10
|
+
const root = path.resolve(input.root);
|
|
11
|
+
const requested = {
|
|
12
|
+
skills: input.repairSkills !== false,
|
|
13
|
+
agent_roles: input.repairAgentRoles !== false,
|
|
14
|
+
hooks: input.repairHooks !== false,
|
|
15
|
+
project_memory: input.repairProjectMemory !== false
|
|
16
|
+
};
|
|
17
|
+
const requestedAssets = Object.entries(requested)
|
|
18
|
+
.filter(([, enabled]) => enabled)
|
|
19
|
+
.map(([asset]) => asset);
|
|
20
|
+
if (input.yes !== true) {
|
|
21
|
+
const report = {
|
|
22
|
+
schema: 'sks.codex-native-repair-transaction.v1',
|
|
23
|
+
ok: false,
|
|
24
|
+
generated_at: nowIso(),
|
|
25
|
+
requested_by: input.requestedBy,
|
|
26
|
+
repaired: requestedAssets.map((asset) => ({
|
|
27
|
+
asset,
|
|
28
|
+
ok: false,
|
|
29
|
+
changed: false,
|
|
30
|
+
artifact_path: artifactPathFor(asset),
|
|
31
|
+
blockers: ['repair_transaction_requires_yes']
|
|
32
|
+
})),
|
|
33
|
+
confirmed: false,
|
|
34
|
+
mutation_ledger_path: null,
|
|
35
|
+
blockers: ['repair_transaction_requires_yes'],
|
|
36
|
+
warnings: []
|
|
37
|
+
};
|
|
38
|
+
await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-native-repair-transaction.json'), report).catch(() => undefined);
|
|
39
|
+
return report;
|
|
40
|
+
}
|
|
41
|
+
const rows = [];
|
|
42
|
+
if (requested.skills) {
|
|
43
|
+
const report = await syncCodexSksSkills({ root, apply: true }).catch((err) => ({ ok: false, blockers: [messageOf(err)] }));
|
|
44
|
+
rows.push({
|
|
45
|
+
asset: 'skills',
|
|
46
|
+
ok: recordOk(report) !== false,
|
|
47
|
+
changed: listLength(report, 'created') > 0,
|
|
48
|
+
artifact_path: '.sneakoscope/reports/codex-skill-sync.json',
|
|
49
|
+
blockers: blockersOf(report)
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if (requested.agent_roles) {
|
|
53
|
+
const report = await syncCodexAgentRoles({ root, apply: true }).catch((err) => ({ ok: false, blockers: [messageOf(err)] }));
|
|
54
|
+
rows.push({
|
|
55
|
+
asset: 'agent_roles',
|
|
56
|
+
ok: recordOk(report) !== false,
|
|
57
|
+
changed: listLength(report, 'created') > 0,
|
|
58
|
+
artifact_path: '.sneakoscope/reports/codex-agent-role-sync.json',
|
|
59
|
+
blockers: blockersOf(report)
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (requested.hooks) {
|
|
63
|
+
const report = await buildCodexHookLifecycle({ root, apply: true }).catch((err) => ({ ok: false, blockers: [messageOf(err)] }));
|
|
64
|
+
rows.push({
|
|
65
|
+
asset: 'hooks',
|
|
66
|
+
ok: recordOk(report) !== false,
|
|
67
|
+
changed: recordOk(report) !== false,
|
|
68
|
+
artifact_path: '.sneakoscope/reports/codex-hook-lifecycle.json',
|
|
69
|
+
blockers: blockersOf(report)
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
if (requested.project_memory) {
|
|
73
|
+
const report = await runCodexInitDeep({ root, apply: true, directoryLocal: true }).catch((err) => ({ ok: false, blockers: [messageOf(err)] }));
|
|
74
|
+
rows.push({
|
|
75
|
+
asset: 'project_memory',
|
|
76
|
+
ok: recordOk(report) !== false,
|
|
77
|
+
changed: listLength(report, 'directory_local_agents.created') + listLength(report, 'directory_local_agents.updated') > 0,
|
|
78
|
+
artifact_path: '.sneakoscope/reports/codex-init-deep.json',
|
|
79
|
+
blockers: blockersOf(report)
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const blockers = rows.flatMap((row) => row.blockers);
|
|
83
|
+
const mutationWarnings = await recordRepairMutations(root, rows);
|
|
84
|
+
const report = {
|
|
85
|
+
schema: 'sks.codex-native-repair-transaction.v1',
|
|
86
|
+
ok: rows.every((row) => row.ok) && blockers.length === 0,
|
|
87
|
+
generated_at: nowIso(),
|
|
88
|
+
requested_by: input.requestedBy,
|
|
89
|
+
repaired: rows,
|
|
90
|
+
confirmed: true,
|
|
91
|
+
mutation_ledger_path: path.relative(root, mutationLedgerPath(root)).split(path.sep).join('/'),
|
|
92
|
+
blockers: [...new Set(blockers)],
|
|
93
|
+
warnings: mutationWarnings
|
|
94
|
+
};
|
|
95
|
+
await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-native-repair-transaction.json'), report).catch(() => undefined);
|
|
96
|
+
return report;
|
|
97
|
+
}
|
|
98
|
+
async function recordRepairMutations(root, rows) {
|
|
99
|
+
const contract = createRequestedScopeContract({
|
|
100
|
+
route: 'codex-native-repair-transaction',
|
|
101
|
+
userRequest: 'Repair SKS-managed Codex Native assets after explicit --yes confirmation.',
|
|
102
|
+
projectRoot: root,
|
|
103
|
+
overrides: { global_codex_config: true }
|
|
104
|
+
});
|
|
105
|
+
const warnings = [];
|
|
106
|
+
for (const row of rows) {
|
|
107
|
+
const kind = row.asset === 'project_memory' ? 'file_write' : 'global_config_write';
|
|
108
|
+
const entry = evaluateMutation(contract, kind, {
|
|
109
|
+
target: row.asset,
|
|
110
|
+
confirmed: true,
|
|
111
|
+
applied: row.changed,
|
|
112
|
+
noOpReason: row.changed ? 'sks_managed_asset_transaction_recorded' : 'repair_noop'
|
|
113
|
+
});
|
|
114
|
+
await recordMutation(root, entry).catch((err) => warnings.push(`mutation_ledger_write_failed:${messageOf(err)}`));
|
|
115
|
+
}
|
|
116
|
+
return warnings;
|
|
117
|
+
}
|
|
118
|
+
function artifactPathFor(asset) {
|
|
119
|
+
return asset === 'skills'
|
|
120
|
+
? '.sneakoscope/reports/codex-skill-sync.json'
|
|
121
|
+
: asset === 'agent_roles'
|
|
122
|
+
? '.sneakoscope/reports/codex-agent-role-sync.json'
|
|
123
|
+
: asset === 'hooks'
|
|
124
|
+
? '.sneakoscope/reports/codex-hook-lifecycle.json'
|
|
125
|
+
: '.sneakoscope/reports/codex-init-deep.json';
|
|
126
|
+
}
|
|
127
|
+
function recordOk(value) {
|
|
128
|
+
return Boolean(value) && typeof value === 'object' && typeof value.ok === 'boolean'
|
|
129
|
+
? value.ok
|
|
130
|
+
: undefined;
|
|
131
|
+
}
|
|
132
|
+
function blockersOf(value) {
|
|
133
|
+
return Boolean(value) && typeof value === 'object' && Array.isArray(value.blockers)
|
|
134
|
+
? value.blockers.map((item) => String(item)).filter(Boolean)
|
|
135
|
+
: [];
|
|
136
|
+
}
|
|
137
|
+
function listLength(value, key) {
|
|
138
|
+
const parts = key.split('.');
|
|
139
|
+
let current = value;
|
|
140
|
+
for (const part of parts) {
|
|
141
|
+
if (!current || typeof current !== 'object')
|
|
142
|
+
return 0;
|
|
143
|
+
current = current[part];
|
|
144
|
+
}
|
|
145
|
+
return Array.isArray(current) ? current.length : 0;
|
|
146
|
+
}
|
|
147
|
+
function messageOf(err) {
|
|
148
|
+
return err instanceof Error ? err.message : String(err);
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=codex-native-repair-transaction.js.map
|
|
@@ -33,7 +33,8 @@ export async function buildCodexPluginInventory() {
|
|
|
33
33
|
});
|
|
34
34
|
const blockers = [
|
|
35
35
|
...(capability.supports_plugin_json ? [] : ['codex_0_138_plugin_json_unavailable']),
|
|
36
|
-
...normalizeList(listJson?.blockers)
|
|
36
|
+
...normalizeList(listJson?.blockers),
|
|
37
|
+
...(process.env.SKS_CODEX_PLUGIN_JSON_FAKE_NO_MCP === '1' ? ['fixture_mcp_candidates_disabled'] : [])
|
|
37
38
|
];
|
|
38
39
|
return {
|
|
39
40
|
schema: 'sks.codex-plugin-inventory.v1',
|
|
@@ -169,7 +170,9 @@ function fakePluginDetail(pluginId) {
|
|
|
169
170
|
installed: true,
|
|
170
171
|
enabled: true,
|
|
171
172
|
default_prompts: ['Use the fixture plugin safely.'],
|
|
172
|
-
remote_mcp_servers:
|
|
173
|
+
remote_mcp_servers: process.env.SKS_CODEX_PLUGIN_JSON_FAKE_NO_MCP === '1'
|
|
174
|
+
? []
|
|
175
|
+
: [{ name: 'fixture-db-docs', url: 'https://mcp.example.test', auth_type: 'oauth' }],
|
|
173
176
|
unavailable_app_templates: ['fixture-desktop-template']
|
|
174
177
|
};
|
|
175
178
|
}
|
|
@@ -22,6 +22,7 @@ import { checkSksUpdateNotice } from '../update/update-notice.js';
|
|
|
22
22
|
import { createMadDbCapability, MAD_DB_ACK } from '../mad-db/mad-db-capability.js';
|
|
23
23
|
import { writeCodex0138CapabilityArtifacts } from '../codex-control/codex-0138-capability.js';
|
|
24
24
|
import { writeCodex0139CapabilityArtifacts } from '../codex-control/codex-0139-capability.js';
|
|
25
|
+
import { resolveCodexNativeInvocationPlan } from '../codex-native/codex-native-invocation-router.js';
|
|
25
26
|
import { repairZellijForSks } from '../zellij/zellij-self-heal.js';
|
|
26
27
|
export async function madHighCommand(args = [], deps = {}) {
|
|
27
28
|
const subcommand = firstSubcommand(args);
|
|
@@ -478,6 +479,14 @@ async function activateMadZellijPermissionState(cwd = process.cwd(), args = [])
|
|
|
478
479
|
const { id, dir } = await createMission(root, { mode: 'mad-sks', prompt: 'sks --mad Zellij scoped high-power maintenance session' });
|
|
479
480
|
await writeCodex0138CapabilityArtifacts(root, { missionId: id }).catch(() => null);
|
|
480
481
|
await writeCodex0139CapabilityArtifacts(root, { missionId: id }).catch(() => null);
|
|
482
|
+
const codexNativeInvocation = await resolveCodexNativeInvocationPlan({
|
|
483
|
+
root,
|
|
484
|
+
missionId: id,
|
|
485
|
+
route: '$MAD',
|
|
486
|
+
desiredCapability: 'hook-evidence'
|
|
487
|
+
}).catch(() => null);
|
|
488
|
+
if (codexNativeInvocation)
|
|
489
|
+
await writeJsonAtomic(path.join(dir, 'mad-codex-native-invocation.json'), codexNativeInvocation).catch(() => undefined);
|
|
481
490
|
const protectedCore = resolveProtectedCore({ packageRoot: packageRoot(), targetRoot: cwd });
|
|
482
491
|
// The interactive launch 'before' snapshot is only persisted (env + policy json)
|
|
483
492
|
// and is never compared against an 'after' snapshot during the session, so the
|
|
@@ -521,6 +530,13 @@ async function activateMadZellijPermissionState(cwd = process.cwd(), args = [])
|
|
|
521
530
|
protected_core_policy: protectedCorePolicyPath,
|
|
522
531
|
protected_core_before: protectedCoreBeforePath,
|
|
523
532
|
protected_core_digest: protectedCoreBefore.digest,
|
|
533
|
+
codex_native_invocation_plan: codexNativeInvocation ? {
|
|
534
|
+
selected_strategy: codexNativeInvocation.selected_strategy,
|
|
535
|
+
hook_evidence_policy: codexNativeInvocation.env.SKS_CODEX_NATIVE_HOOK_EVIDENCE_POLICY,
|
|
536
|
+
blockers: codexNativeInvocation.blockers,
|
|
537
|
+
warnings: codexNativeInvocation.warnings,
|
|
538
|
+
artifact_path: 'mad-codex-native-invocation.json'
|
|
539
|
+
} : null,
|
|
524
540
|
activated_by: 'sks --mad',
|
|
525
541
|
cwd: path.resolve(cwd || process.cwd())
|
|
526
542
|
};
|
package/dist/core/fsx.js
CHANGED
|
@@ -5,7 +5,7 @@ import os from 'node:os';
|
|
|
5
5
|
import crypto from 'node:crypto';
|
|
6
6
|
import { spawn } from 'node:child_process';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
|
-
export const PACKAGE_VERSION = '3.1.
|
|
8
|
+
export const PACKAGE_VERSION = '3.1.7';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
export function nowIso() {
|
|
@@ -10,6 +10,7 @@ export function buildLoopMakerPrompt(input) {
|
|
|
10
10
|
`Owner directories: ${node.owner_scope.directories.join(', ') || '-'}`,
|
|
11
11
|
`Allowed mutation scope: ${ownerScopeText(node)}`,
|
|
12
12
|
'Do not mutate outside the owner scope.',
|
|
13
|
+
'Memory hints are guidance only; memory never grants write permission or expands owner scope.',
|
|
13
14
|
`Selected local gates: ${allGateIds(node.gates).join(', ') || '-'}`,
|
|
14
15
|
`Budget: ${JSON.stringify(node.budget)}`,
|
|
15
16
|
`Worktree path: ${input.worktreePath || '-'}`,
|
|
@@ -30,6 +31,7 @@ export function buildLoopCheckerPrompt(input) {
|
|
|
30
31
|
`Selected gates: ${allGateIds(node.gates).join(', ') || '-'}`,
|
|
31
32
|
`Risk: ${node.risk.level} (${node.risk.reasons.join(', ') || '-'})`,
|
|
32
33
|
'Reject unrequested side effects and owner-scope violations.',
|
|
34
|
+
'Memory hints are guidance only; memory never grants write permission or expands owner scope.',
|
|
33
35
|
'Write checker-findings.json with fresh_session, reviewed_maker_artifacts, side_effects_detected, and approved.',
|
|
34
36
|
'No synthetic pass is allowed for production proof.'
|
|
35
37
|
].join('\n');
|
|
@@ -159,6 +159,12 @@ async function runLoopWorkerFixture(input) {
|
|
|
159
159
|
});
|
|
160
160
|
const dir = path.join(loopNodeRoot(input.root, input.plan.mission_id, input.node.loop_id), input.phase);
|
|
161
161
|
await ensureDir(dir);
|
|
162
|
+
const invocationPlan = await resolveCodexNativeInvocationPlan({
|
|
163
|
+
root: input.root,
|
|
164
|
+
missionId: input.plan.mission_id,
|
|
165
|
+
route: '$Loop',
|
|
166
|
+
desiredCapability: 'agent-role'
|
|
167
|
+
}).catch(() => null);
|
|
162
168
|
const resultPath = path.join(dir, 'worker-runtime-result.json');
|
|
163
169
|
const childInputPath = path.join(dir, 'worker-fixture-intake.json');
|
|
164
170
|
await writeJsonAtomic(childInputPath, {
|
|
@@ -170,7 +176,8 @@ async function runLoopWorkerFixture(input) {
|
|
|
170
176
|
worker_count: input.phase === 'maker' ? input.node.maker.worker_count : input.node.checker.worker_count,
|
|
171
177
|
result_path: resultPath,
|
|
172
178
|
owner_scope: input.node.owner_scope,
|
|
173
|
-
maker_artifacts: input.makerArtifacts || []
|
|
179
|
+
maker_artifacts: input.makerArtifacts || [],
|
|
180
|
+
codex_native_invocation_plan: invocationPlan ? compactInvocationPlan(invocationPlan) : null
|
|
174
181
|
});
|
|
175
182
|
const child = await runProcess(process.execPath, [fixtureChildEntrypoint(), childInputPath], {
|
|
176
183
|
cwd: input.root,
|
package/dist/core/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const PACKAGE_VERSION = '3.1.
|
|
1
|
+
export const PACKAGE_VERSION = '3.1.7';
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
export async function createCodexNativeRuntimeFixture(input) {
|
|
5
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'sks-codex-native-e2e-'));
|
|
6
|
+
const missionId = `M-check-${Date.now().toString(36)}`;
|
|
7
|
+
const home = path.join(root, 'home');
|
|
8
|
+
const codexHome = path.join(root, 'codex-home');
|
|
9
|
+
await fs.mkdir(home, { recursive: true });
|
|
10
|
+
await fs.mkdir(codexHome, { recursive: true });
|
|
11
|
+
await fs.mkdir(path.join(root, '.sneakoscope', 'reports'), { recursive: true });
|
|
12
|
+
await fs.mkdir(path.join(root, '.sneakoscope', 'missions', missionId), { recursive: true });
|
|
13
|
+
await fs.mkdir(path.join(root, 'src', 'core', 'loops'), { recursive: true });
|
|
14
|
+
await fs.mkdir(path.join(root, 'src', 'core', 'qa'), { recursive: true });
|
|
15
|
+
await fs.mkdir(path.join(root, 'src', 'core', 'research'), { recursive: true });
|
|
16
|
+
await fs.mkdir(path.join(root, 'docs'), { recursive: true });
|
|
17
|
+
await fs.writeFile(path.join(root, 'package.json'), '{"name":"fixture","version":"3.1.7","scripts":{}}\n', 'utf8');
|
|
18
|
+
await fs.writeFile(path.join(root, 'src', 'core', 'loops', 'fixture.ts'), 'export const loopFixture = true\n', 'utf8');
|
|
19
|
+
await fs.writeFile(path.join(root, 'src', 'core', 'qa', 'fixture.ts'), 'export const qaFixture = true\n', 'utf8');
|
|
20
|
+
await fs.writeFile(path.join(root, 'src', 'core', 'research', 'fixture.ts'), 'export const researchFixture = true\n', 'utf8');
|
|
21
|
+
await fs.writeFile(path.join(root, 'docs', 'fixture.md'), '# Fixture\n', 'utf8');
|
|
22
|
+
return {
|
|
23
|
+
root,
|
|
24
|
+
missionId,
|
|
25
|
+
matrixPath: path.join(root, '.sneakoscope', 'reports', 'codex-native-feature-matrix.json'),
|
|
26
|
+
env: {
|
|
27
|
+
...process.env,
|
|
28
|
+
HOME: home,
|
|
29
|
+
USERPROFILE: home,
|
|
30
|
+
CODEX_HOME: codexHome,
|
|
31
|
+
CODEX_BIN: 'codex',
|
|
32
|
+
SKS_CODEX_0138_FAKE: '1',
|
|
33
|
+
SKS_CODEX_0138_PROBE: '1',
|
|
34
|
+
SKS_CODEX_0139_FAKE: '1',
|
|
35
|
+
SKS_CODEX_0139_PROBE: '1',
|
|
36
|
+
SKS_CODEX_PLUGIN_JSON_FAKE: '1',
|
|
37
|
+
SKS_CODEX_HOOK_APPROVAL_FIXTURE: input.hook,
|
|
38
|
+
SKS_CODEX_AGENT_TYPE_FIXTURE: input.agentType,
|
|
39
|
+
SKS_CODEX_0138_FAKE_APP_HANDOFF_FAIL: input.appHandoff ? '0' : '1',
|
|
40
|
+
SKS_CODEX_0138_FAKE_IMAGE_PATH_FAIL: input.imagePathExposure ? '0' : '1',
|
|
41
|
+
SKS_CODEX_0138_FAKE_PLUGIN_JSON_FAIL: '0',
|
|
42
|
+
SKS_CODEX_PLUGIN_JSON_FAKE_NO_MCP: input.mcpCandidates ? '0' : '1',
|
|
43
|
+
SKS_CODEX_0139_FAKE_WEB_SEARCH_FAIL: input.codeModeWebSearch ? '0' : '1',
|
|
44
|
+
SKS_CODEX_0139_FAKE_MARKETPLACE_FAIL: '0',
|
|
45
|
+
SKS_CODEX_0139_FAKE_PROFILE_ALIAS_FAIL: '0',
|
|
46
|
+
SKS_CODEX_0139_FAKE_INTERRUPT_FAIL: '0',
|
|
47
|
+
SKS_CODEX_0139_FAKE_RICH_SCHEMA_FAIL: '0',
|
|
48
|
+
SKS_CODEX_0139_FAKE_DOCTOR_ENV_FAIL: '0',
|
|
49
|
+
SKS_LOOP_RUNTIME_FIXTURE: '1',
|
|
50
|
+
SKS_TEST_RUNTIME_FIXTURE_ALLOWED: '1'
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export async function withFixtureEnv(fixture, fn) {
|
|
55
|
+
const previous = new Map();
|
|
56
|
+
for (const [key, value] of Object.entries(fixture.env)) {
|
|
57
|
+
previous.set(key, process.env[key]);
|
|
58
|
+
if (value === undefined)
|
|
59
|
+
delete process.env[key];
|
|
60
|
+
else
|
|
61
|
+
process.env[key] = value;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
return await fn();
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
for (const [key, value] of previous) {
|
|
68
|
+
if (value === undefined)
|
|
69
|
+
delete process.env[key];
|
|
70
|
+
else
|
|
71
|
+
process.env[key] = value;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=codex-native-runtime-e2e-fixture.js.map
|
|
@@ -48,6 +48,7 @@ await fs.writeFile(intake.result_path, `${JSON.stringify({
|
|
|
48
48
|
blockers: [],
|
|
49
49
|
runtime_proof_path: intake.result_path,
|
|
50
50
|
worker_ids: workerIds,
|
|
51
|
-
session_ids: sessionIds
|
|
51
|
+
session_ids: sessionIds,
|
|
52
|
+
codex_native_invocation_plan: intake.codex_native_invocation_plan || null
|
|
52
53
|
}, null, 2)}\n`);
|
|
53
54
|
//# sourceMappingURL=loop-worker-fixture-child.js.map
|
|
@@ -259,7 +259,7 @@ async function richContentGate(id) {
|
|
|
259
259
|
const codexHome = path.join(rootDir, 'codex-home');
|
|
260
260
|
const report = await mod.syncCodexAgentRoles({ root: rootDir, codexHome, apply: true });
|
|
261
261
|
const role = fs.readFileSync(path.join(codexHome, 'agents', 'sks-checker.toml'), 'utf8');
|
|
262
|
-
assertGate(
|
|
262
|
+
assertGate(/SKS managed 3\.1\.[67] directive role/.test(role) && role.includes('Execution role strategy'), 'managed agent role must include rich directive content', { role, report });
|
|
263
263
|
emitGate(id, { roles: report.created.length });
|
|
264
264
|
}
|
|
265
265
|
finally {
|
|
@@ -321,7 +321,7 @@ async function initDeepMemoryScopeSafety(id) {
|
|
|
321
321
|
emitGate(id);
|
|
322
322
|
}
|
|
323
323
|
async function releaseScriptsTypeSafe(id) {
|
|
324
|
-
for (const rel of ['src/scripts/release-dag-full-coverage-check.ts', 'src/scripts/sks-3-1-5-directive-check-lib.ts', 'src/scripts/sks-3-1-6-directive-check-lib.ts']) {
|
|
324
|
+
for (const rel of ['src/scripts/release-dag-full-coverage-check.ts', 'src/scripts/sks-3-1-5-directive-check-lib.ts', 'src/scripts/sks-3-1-6-directive-check-lib.ts', 'src/scripts/sks-3-1-7-directive-check-lib.ts']) {
|
|
325
325
|
const text = readText(rel);
|
|
326
326
|
assertGate(!/^\s*\/\/\s*@ts-nocheck\b/m.test(text), `release helper still has ts-nocheck:${rel}`);
|
|
327
327
|
}
|
|
@@ -334,7 +334,7 @@ async function noTsNoCheckReleaseScripts(id) {
|
|
|
334
334
|
const checked = listFiles(path.join(root, 'src/scripts')).filter((file) => {
|
|
335
335
|
const rel = path.relative(root, file).split(path.sep).join('/');
|
|
336
336
|
return /^src\/scripts\/release-dag-full-coverage-check\.ts$/.test(rel)
|
|
337
|
-
|| /^src\/scripts\/sks-3-1-[
|
|
337
|
+
|| /^src\/scripts\/sks-3-1-[567]-directive-check-lib\.ts$/.test(rel)
|
|
338
338
|
|| /^src\/scripts\/no-ts-nocheck-release-scripts-check\.ts$/.test(rel)
|
|
339
339
|
|| /^src\/scripts\/release-script-type-safety-check\.ts$/.test(rel);
|
|
340
340
|
});
|