sneakoscope 3.1.6 → 3.1.8

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.
Files changed (49) hide show
  1. package/README.md +10 -3
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/crates/sks-core/src/main.rs +1 -1
  5. package/dist/.sks-build-stamp.json +4 -4
  6. package/dist/bin/sks.js +1 -1
  7. package/dist/commands/codex-app.js +20 -2
  8. package/dist/commands/codex-native.js +18 -2
  9. package/dist/commands/doctor.js +106 -12
  10. package/dist/core/codex-app/codex-agent-role-sync.js +9 -5
  11. package/dist/core/codex-app/codex-init-deep.js +6 -2
  12. package/dist/core/codex-app/codex-skill-sync.js +80 -152
  13. package/dist/core/codex-control/codex-0138-capability.js +5 -2
  14. package/dist/core/codex-native/codex-native-feature-broker.js +74 -6
  15. package/dist/core/codex-native/codex-native-pattern-analysis.js +14 -2
  16. package/dist/core/codex-native/codex-native-reference-cache.js +98 -0
  17. package/dist/core/codex-native/codex-native-reference-source.js +50 -11
  18. package/dist/core/codex-native/codex-native-repair-transaction.js +150 -0
  19. package/dist/core/codex-native/core-skill-integrity.js +89 -0
  20. package/dist/core/codex-native/core-skill-manifest.js +156 -0
  21. package/dist/core/codex-native/native-capability-postcheck.js +35 -0
  22. package/dist/core/codex-native/native-capability-repair-matrix.js +210 -0
  23. package/dist/core/codex-native/native-capability-repair.js +47 -0
  24. package/dist/core/codex-native/native-media-computer-repair.js +5 -0
  25. package/dist/core/codex-native/project-skill-dedupe.js +109 -0
  26. package/dist/core/codex-native/skill-name-canonicalizer.js +21 -0
  27. package/dist/core/codex-native/skill-registry-ledger.js +85 -0
  28. package/dist/core/codex-plugins/codex-plugin-json.js +5 -2
  29. package/dist/core/commands/basic-cli.js +15 -9
  30. package/dist/core/commands/mad-sks-command.js +16 -0
  31. package/dist/core/config/config-migration-journal.js +27 -0
  32. package/dist/core/config/managed-config-merge.js +105 -0
  33. package/dist/core/config/secret-preservation.js +169 -0
  34. package/dist/core/config/supabase-secret-preservation.js +29 -0
  35. package/dist/core/doctor/doctor-native-capability-repair.js +48 -0
  36. package/dist/core/fsx.js +1 -1
  37. package/dist/core/init.js +5 -1
  38. package/dist/core/loops/loop-planner.js +1 -1
  39. package/dist/core/loops/loop-worker-prompts.js +2 -0
  40. package/dist/core/loops/loop-worker-runtime.js +8 -1
  41. package/dist/core/version.js +1 -1
  42. package/dist/scripts/codex-native-runtime-e2e-fixture.js +75 -0
  43. package/dist/scripts/loop-worker-fixture-child.js +2 -1
  44. package/dist/scripts/sizecheck.js +8 -2
  45. package/dist/scripts/sks-3-1-5-directive-check-lib.js +1 -1
  46. package/dist/scripts/sks-3-1-6-directive-check-lib.js +2 -2
  47. package/dist/scripts/sks-3-1-7-directive-check-lib.js +58 -0
  48. package/dist/scripts/sks-3-1-8-check-lib.js +30 -0
  49. package/package.json +39 -2
@@ -0,0 +1,105 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { ensureDir, nowIso, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
4
+ import { isProtectedSecretKey, PROTECTED_SECRET_KEYS } from './supabase-secret-preservation.js';
5
+ export async function writeManagedJsonConfig(file, current, managed) {
6
+ const next = safeMergeObject(current, managed);
7
+ const before = `${JSON.stringify(current, null, 2)}\n`;
8
+ const after = `${JSON.stringify(next, null, 2)}\n`;
9
+ return writeMergedText(file, before, after, 'json', protectedKeysPresent(current));
10
+ }
11
+ export async function writeManagedTomlConfig(file, currentText, managedBlocks) {
12
+ let next = String(currentText || '').trimEnd();
13
+ for (const block of managedBlocks)
14
+ next = upsertTomlBlockPreservingSecrets(next, block);
15
+ return writeMergedText(file, currentText, `${next.trim()}\n`, 'toml', protectedKeysInText(currentText));
16
+ }
17
+ export async function writeManagedEnvConfig(file, currentText, managedLines) {
18
+ const existingKeys = new Set(String(currentText || '').split(/\r?\n/).map((line) => line.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/)?.[1]).filter((value) => Boolean(value)));
19
+ const additions = managedLines.filter((line) => {
20
+ const key = line.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/)?.[1] || '';
21
+ return key && !existingKeys.has(key) && !isProtectedSecretKey(key);
22
+ });
23
+ const next = additions.length ? `${String(currentText || '').replace(/\s*$/, '\n')}${additions.join('\n')}\n` : String(currentText || '');
24
+ return writeMergedText(file, currentText, next, 'env', protectedKeysInText(currentText));
25
+ }
26
+ export function safeMergeObject(current, managed) {
27
+ const out = { ...current };
28
+ for (const [key, value] of Object.entries(managed)) {
29
+ if (isProtectedSecretKey(key) && current[key] != null)
30
+ continue;
31
+ if (isPlainObject(value) && isPlainObject(current[key]))
32
+ out[key] = safeMergeObject(current[key], value);
33
+ else
34
+ out[key] = value;
35
+ }
36
+ return out;
37
+ }
38
+ function upsertTomlBlockPreservingSecrets(text, block) {
39
+ const header = block.match(/^\s*\[([^\]]+)\]/)?.[1];
40
+ if (!header)
41
+ return text;
42
+ const lines = String(text || '').trimEnd().split('\n');
43
+ const start = lines.findIndex((line) => line.trim() === `[${header}]`);
44
+ const blockLines = block.trim().split('\n');
45
+ if (start === -1)
46
+ return [...lines.filter((line) => line.length), '', ...blockLines].join('\n');
47
+ let end = lines.length;
48
+ for (let index = start + 1; index < lines.length; index += 1) {
49
+ if (/^\s*\[.+\]\s*$/.test(lines[index] || '')) {
50
+ end = index;
51
+ break;
52
+ }
53
+ }
54
+ const existingSecretLines = lines.slice(start + 1, end).filter((line) => {
55
+ const key = line.match(/^\s*([A-Za-z0-9_.-]+)\s*=/)?.[1] || '';
56
+ return isProtectedSecretKey(`${header}.${key}`) || isProtectedSecretKey(key);
57
+ });
58
+ lines.splice(start, end - start, ...blockLines, ...existingSecretLines.filter((line) => !blockLines.includes(line)));
59
+ return lines.join('\n').replace(/\n{3,}/g, '\n\n');
60
+ }
61
+ async function writeMergedText(file, before, after, format, preserved) {
62
+ await ensureDir(path.dirname(file));
63
+ let backupPath = null;
64
+ if (before !== after) {
65
+ if (before.trim()) {
66
+ backupPath = `${file}.sks-managed-merge-${Date.now()}.bak`;
67
+ await fs.writeFile(backupPath, before, 'utf8');
68
+ }
69
+ await writeTextAtomic(file, after);
70
+ }
71
+ return {
72
+ schema: 'sks.managed-config-merge.v1',
73
+ generated_at: nowIso(),
74
+ ok: true,
75
+ path: file,
76
+ format,
77
+ changed: before !== after,
78
+ backup_path: backupPath,
79
+ protected_keys_preserved: preserved,
80
+ blockers: []
81
+ };
82
+ }
83
+ function protectedKeysPresent(value) {
84
+ const found = [];
85
+ for (const key of PROTECTED_SECRET_KEYS)
86
+ if (lookupPath(value, key) != null)
87
+ found.push(String(key));
88
+ return found;
89
+ }
90
+ function protectedKeysInText(text) {
91
+ return PROTECTED_SECRET_KEYS.filter((key) => new RegExp(`(^|\\n)\\s*(?:export\\s+)?${String(key).replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*=`).test(text)).map(String);
92
+ }
93
+ function lookupPath(value, dotted) {
94
+ let current = value;
95
+ for (const part of dotted.split('.')) {
96
+ if (!current || typeof current !== 'object' || Array.isArray(current))
97
+ return undefined;
98
+ current = current[part];
99
+ }
100
+ return current;
101
+ }
102
+ function isPlainObject(value) {
103
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
104
+ }
105
+ //# sourceMappingURL=managed-config-merge.js.map
@@ -0,0 +1,169 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import os from 'node:os';
4
+ import { ensureDir, nowIso, readJson, readText, sha256, writeJsonAtomic } from '../fsx.js';
5
+ import { PROTECTED_SECRET_KEYS, PROTECTED_SUPABASE_ENV_KEYS } from './supabase-secret-preservation.js';
6
+ export async function captureSecretPreservationSnapshot(input) {
7
+ const root = path.resolve(input.root);
8
+ const sources = secretSources(root);
9
+ const fingerprints = [];
10
+ for (const source of sources) {
11
+ const text = await readText(source, null);
12
+ if (typeof text !== 'string')
13
+ continue;
14
+ if (source.endsWith('.json')) {
15
+ const json = await readJson(source, {}).catch(() => ({}));
16
+ fingerprints.push(...fingerprintsFromObject(json, source));
17
+ }
18
+ else {
19
+ fingerprints.push(...fingerprintsFromText(text, source));
20
+ }
21
+ }
22
+ const snapshot = {
23
+ schema: 'sks.secret-preservation-snapshot.v1',
24
+ generated_at: nowIso(),
25
+ root,
26
+ fingerprints: dedupeFingerprints(fingerprints)
27
+ };
28
+ if (input.artifactPath)
29
+ await writeJsonAtomic(input.artifactPath, snapshot).catch(() => undefined);
30
+ return snapshot;
31
+ }
32
+ export async function withSecretPreservationGuard(root, operationName, fn) {
33
+ const resolvedRoot = path.resolve(root);
34
+ const reportDir = path.join(resolvedRoot, '.sneakoscope', 'reports');
35
+ await ensureDir(reportDir);
36
+ const beforePath = path.join(reportDir, 'secret-preservation-before.json');
37
+ const afterPath = path.join(reportDir, 'secret-preservation-after.json');
38
+ const guardPath = path.join(reportDir, 'secret-preservation-guard.json');
39
+ const before = await captureSecretPreservationSnapshot({ root: resolvedRoot, artifactPath: beforePath });
40
+ let result;
41
+ try {
42
+ result = await fn();
43
+ }
44
+ catch (err) {
45
+ await writeJsonAtomic(guardPath, {
46
+ schema: 'sks.secret-preservation-guard.v1',
47
+ generated_at: nowIso(),
48
+ ok: false,
49
+ operation: operationName,
50
+ before_path: beforePath,
51
+ after_path: null,
52
+ restored_keys_count: 0,
53
+ missing_after: [],
54
+ raw_values_recorded: false,
55
+ operation_error: err instanceof Error ? err.message : String(err)
56
+ }).catch(() => undefined);
57
+ throw err;
58
+ }
59
+ const after = await captureSecretPreservationSnapshot({ root: resolvedRoot, artifactPath: afterPath });
60
+ const missing = missingProtectedSecrets(before, after);
61
+ const report = {
62
+ schema: 'sks.secret-preservation-guard.v1',
63
+ generated_at: nowIso(),
64
+ ok: missing.length === 0,
65
+ operation: operationName,
66
+ before_path: beforePath,
67
+ after_path: afterPath,
68
+ restored_keys_count: 0,
69
+ missing_after: missing,
70
+ raw_values_recorded: false
71
+ };
72
+ await writeJsonAtomic(guardPath, report).catch(() => undefined);
73
+ if (missing.length) {
74
+ throw new Error(`secret_preservation_failed:${missing.map((item) => `${item.source}:${item.key}`).join(',')}`);
75
+ }
76
+ return result;
77
+ }
78
+ export function missingProtectedSecrets(before, after) {
79
+ const afterMap = new Map(after.fingerprints.filter((fp) => fp.present).map((fp) => [`${fp.source}\0${fp.key}`, fp]));
80
+ return before.fingerprints
81
+ .filter((fp) => fp.present && fp.value_sha256)
82
+ .filter((fp) => !afterMap.has(`${fp.source}\0${fp.key}`))
83
+ .map((fp) => ({ key: fp.key, source: fp.source }));
84
+ }
85
+ function secretSources(root) {
86
+ const home = process.env.HOME || os.homedir();
87
+ return [
88
+ '.env',
89
+ '.env.local',
90
+ '.env.development',
91
+ '.env.production',
92
+ '.sneakoscope/config.json',
93
+ '.codex/config.toml'
94
+ ].map((rel) => path.join(root, rel)).concat(path.join(home, '.codex', 'config.toml'));
95
+ }
96
+ function fingerprintsFromText(text, source) {
97
+ const rows = [];
98
+ for (const key of PROTECTED_SECRET_KEYS) {
99
+ const value = readAssignment(text, key);
100
+ if (!value)
101
+ continue;
102
+ rows.push(fingerprint(String(key), source, value));
103
+ }
104
+ for (const envKey of PROTECTED_SUPABASE_ENV_KEYS) {
105
+ const value = readAssignment(text, envKey);
106
+ if (value)
107
+ rows.push(fingerprint(envKey, source, value));
108
+ }
109
+ return rows;
110
+ }
111
+ function fingerprintsFromObject(value, source) {
112
+ const flat = flattenObject(value);
113
+ const rows = [];
114
+ for (const [key, raw] of Object.entries(flat)) {
115
+ if (!PROTECTED_SECRET_KEYS.includes(key))
116
+ continue;
117
+ rows.push(fingerprint(key, source, String(raw)));
118
+ }
119
+ return rows;
120
+ }
121
+ function fingerprint(key, source, value) {
122
+ return {
123
+ key,
124
+ source,
125
+ present: Boolean(value),
126
+ redacted_preview: redactPreview(value),
127
+ value_sha256: value ? sha256(value) : null
128
+ };
129
+ }
130
+ function readAssignment(text, key) {
131
+ const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
132
+ const re = new RegExp(`^\\s*(?:export\\s+)?${escaped.replace(/\\\./g, '\\s*\\.\\s*')}\\s*=\\s*(.+?)\\s*$`, 'm');
133
+ const raw = String(text || '').match(re)?.[1]?.trim() || '';
134
+ return unquote(raw);
135
+ }
136
+ function unquote(value) {
137
+ const trimmed = String(value || '').trim().replace(/\s+#.*$/, '');
138
+ if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'")))
139
+ return trimmed.slice(1, -1);
140
+ return trimmed;
141
+ }
142
+ function redactPreview(value) {
143
+ const text = String(value || '');
144
+ if (!text)
145
+ return '';
146
+ const head = text.slice(0, Math.min(3, text.length));
147
+ const tail = text.length > 6 ? text.slice(-3) : '';
148
+ return `${head}...${tail || 'redacted'}(${text.length})`;
149
+ }
150
+ function flattenObject(value, prefix = '') {
151
+ if (!value || typeof value !== 'object' || Array.isArray(value))
152
+ return {};
153
+ const out = {};
154
+ for (const [key, child] of Object.entries(value)) {
155
+ const nextKey = prefix ? `${prefix}.${key}` : key;
156
+ if (child && typeof child === 'object' && !Array.isArray(child))
157
+ Object.assign(out, flattenObject(child, nextKey));
158
+ else if (child != null)
159
+ out[nextKey] = String(child);
160
+ }
161
+ return out;
162
+ }
163
+ function dedupeFingerprints(fingerprints) {
164
+ const byKey = new Map();
165
+ for (const fp of fingerprints)
166
+ byKey.set(`${fp.source}\0${fp.key}`, fp);
167
+ return [...byKey.values()].sort((a, b) => a.source.localeCompare(b.source) || a.key.localeCompare(b.key));
168
+ }
169
+ //# sourceMappingURL=secret-preservation.js.map
@@ -0,0 +1,29 @@
1
+ export const PROTECTED_SUPABASE_ENV_KEYS = [
2
+ 'SUPABASE_URL',
3
+ 'SUPABASE_ANON_KEY',
4
+ 'SUPABASE_SERVICE_ROLE_KEY',
5
+ 'NEXT_PUBLIC_SUPABASE_URL',
6
+ 'NEXT_PUBLIC_SUPABASE_ANON_KEY',
7
+ 'VITE_SUPABASE_URL',
8
+ 'VITE_SUPABASE_ANON_KEY',
9
+ 'PUBLIC_SUPABASE_URL',
10
+ 'PUBLIC_SUPABASE_ANON_KEY'
11
+ ];
12
+ export const PROTECTED_SUPABASE_CONFIG_PATHS = [
13
+ 'supabase.url',
14
+ 'supabase.anon_key',
15
+ 'supabase.service_role_key',
16
+ 'mcp.supabase.url',
17
+ 'mcp.supabase.token',
18
+ 'mcp.supabase.access_token',
19
+ 'mcp.supabase.service_role_key'
20
+ ];
21
+ export const PROTECTED_SECRET_KEYS = [
22
+ ...PROTECTED_SUPABASE_ENV_KEYS,
23
+ ...PROTECTED_SUPABASE_CONFIG_PATHS
24
+ ];
25
+ export function isProtectedSecretKey(key) {
26
+ const normalized = String(key || '').trim();
27
+ return PROTECTED_SECRET_KEYS.some((candidate) => candidate === normalized);
28
+ }
29
+ //# sourceMappingURL=supabase-secret-preservation.js.map
@@ -0,0 +1,48 @@
1
+ import path from 'node:path';
2
+ import { nowIso, writeJsonAtomic } from '../fsx.js';
3
+ import { syncCoreSkillsIntegrity } from '../codex-native/core-skill-integrity.js';
4
+ import { dedupeProjectSkills } from '../codex-native/project-skill-dedupe.js';
5
+ import { repairNativeCapabilities } from '../codex-native/native-capability-repair.js';
6
+ import { withSecretPreservationGuard } from '../config/config-migration-journal.js';
7
+ export async function runDoctorNativeCapabilityRepair(input) {
8
+ const root = path.resolve(input.root);
9
+ const operation = async () => {
10
+ const coreSkills = await syncCoreSkillsIntegrity({ root, apply: input.fix });
11
+ const skillDedupe = await dedupeProjectSkills({
12
+ root,
13
+ fix: input.fix,
14
+ yes: input.yes,
15
+ quarantineUserDuplicates: (input.flags || []).includes('--quarantine-user-duplicate-skills')
16
+ });
17
+ const nativeCapabilities = await repairNativeCapabilities({
18
+ root,
19
+ fix: input.fix,
20
+ yes: input.yes,
21
+ allowManualInstructions: true
22
+ });
23
+ const blockers = [
24
+ ...(coreSkills.blockers || []),
25
+ ...(skillDedupe.blockers || []),
26
+ ...(nativeCapabilities.blockers || [])
27
+ ];
28
+ const report = {
29
+ schema: 'sks.doctor-native-capability-repair.v1',
30
+ generated_at: nowIso(),
31
+ ok: blockers.length === 0,
32
+ root,
33
+ fix: input.fix,
34
+ yes: input.yes,
35
+ core_skills: coreSkills,
36
+ skill_dedupe: skillDedupe,
37
+ native_capabilities: nativeCapabilities,
38
+ secret_preservation_guard: '.sneakoscope/reports/secret-preservation-guard.json',
39
+ blockers
40
+ };
41
+ await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'doctor-native-capability-repair.json'), report).catch(() => undefined);
42
+ return report;
43
+ };
44
+ if (!input.fix)
45
+ return operation();
46
+ return withSecretPreservationGuard(root, 'doctor-native-capability-repair', operation);
47
+ }
48
+ //# sourceMappingURL=doctor-native-capability-repair.js.map
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.6';
8
+ export const PACKAGE_VERSION = '3.1.8';
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() {
package/dist/core/init.js CHANGED
@@ -11,6 +11,7 @@ import { AWESOME_DESIGN_MD_REFERENCE, CODEX_APP_IMAGE_GENERATION_DOC_URL, CODEX_
11
11
  import { SKILL_DREAM_POLICY, skillDreamPolicyText } from './skill-forge.js';
12
12
  import { CODEX_HOOK_EVENT_STATE_KEYS } from './codex-compat/codex-hook-events.js';
13
13
  import { codexCommandHookCurrentHash } from './codex-hooks/codex-hook-hash.js';
14
+ import { buildSksCoreSkillManifest, isCoreSkillName, renderCoreSkillTemplate } from './codex-native/core-skill-manifest.js';
14
15
  const REFLECTION_MEMORY_PATH = '.sneakoscope/memory/q2_facts/post-route-reflection.md';
15
16
  const SKS_GENERATED_GIT_PATTERNS = [
16
17
  '.sneakoscope/missions/',
@@ -1083,9 +1084,12 @@ export async function installSkills(root) {
1083
1084
  'design-ui-editor': `---\nname: design-ui-editor\ndescription: Legacy fallback UI/UX editor for existing design.md systems when Product Design plugin is unavailable.\n---\n\nUse Product Design plugin first. When falling back, read \`design.md\`, inspect relevant UI/assets/tests, consult getdesign-reference when improving the design system, apply the smallest design-system-conformant change, use imagegen for image/logo/raster assets, and verify render quality. ${productDesignPluginPolicyText()} ${CODEX_IMAGEGEN_REQUIRED_POLICY} If design.md is missing and Product Design is unavailable, use design-system-builder as fallback.\n`,
1084
1085
  'design-artifact-expert': `---\nname: design-artifact-expert\ndescription: Legacy fallback for high-fidelity HTML/UI/prototype artifacts when Product Design plugin cannot be used.\n---\n\nUse Product Design plugin first for design/UI/prototype work. When falling back, read design.md when present, consult getdesign-reference for design-system grounding, build the usable artifact first, preserve state, verify overlap/readability/responsiveness, and use imagegen for required assets. ${productDesignPluginPolicyText()} ${CODEX_IMAGEGEN_REQUIRED_POLICY}\n`
1085
1086
  };
1087
+ for (const skill of buildSksCoreSkillManifest().skills) {
1088
+ skills[skill.canonical_name] = renderCoreSkillTemplate(skill.canonical_name);
1089
+ }
1086
1090
  for (const [name, content] of Object.entries(skills)) {
1087
1091
  const dir = path.join(root, '.agents', 'skills', name);
1088
- const skillContent = enrichSkillContent(name, content);
1092
+ const skillContent = isCoreSkillName(name) ? content : enrichSkillContent(name, content);
1089
1093
  await ensureDir(dir);
1090
1094
  await writeTextAtomic(path.join(dir, 'SKILL.md'), `${skillContent.trim()}\n`);
1091
1095
  await writeSkillMetadata(dir, name);
@@ -59,7 +59,7 @@ export async function planLoopsFromRequest(input) {
59
59
  return {
60
60
  ...node,
61
61
  ...(hints.length ? { memory_hints: hints } : {}),
62
- memory_hints_used: hints.length,
62
+ memory_hints_used: hints,
63
63
  memory_did_not_expand_scope: true
64
64
  };
65
65
  });
@@ -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,
@@ -1,2 +1,2 @@
1
- export const PACKAGE_VERSION = '3.1.6';
1
+ export const PACKAGE_VERSION = '3.1.8';
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
@@ -7,11 +7,15 @@ import path from 'node:path';
7
7
  import { fileURLToPath } from 'node:url';
8
8
  const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
9
9
  const limits = {
10
- packedBytes: Number(process.env.SKS_MAX_PACK_BYTES || 1024 * 1024),
11
- unpackedBytes: Number(process.env.SKS_MAX_UNPACKED_BYTES || 5 * 1024 * 1024),
10
+ packedBytes: Number(process.env.SKS_MAX_PACK_BYTES || 1536 * 1024),
11
+ unpackedBytes: Number(process.env.SKS_MAX_UNPACKED_BYTES || 6 * 1024 * 1024),
12
12
  packFiles: Number(process.env.SKS_MAX_PACK_FILES || 1200),
13
13
  trackedFileBytes: Number(process.env.SKS_MAX_TRACKED_FILE_BYTES || 384 * 1024)
14
14
  };
15
+ const trackedFileSizeAllowlist = new Set([
16
+ // Historical source documentation export; not included in the npm package payload.
17
+ 'docs/sks-local-llm-mode/exports/sks-local-llm-mode-deck.pdf'
18
+ ]);
15
19
  const npmBin = process.platform === 'win32' ? 'npm.cmd' : 'npm';
16
20
  function fail(message, detail = '') {
17
21
  console.error(`Size check failed: ${message}`);
@@ -40,6 +44,8 @@ function checkTrackedFiles() {
40
44
  const files = result.stdout.toString('utf8').split('\0').filter(Boolean);
41
45
  const oversized = [];
42
46
  for (const file of files) {
47
+ if (trackedFileSizeAllowlist.has(file))
48
+ continue;
43
49
  let stat;
44
50
  try {
45
51
  stat = fs.statSync(path.join(root, file));
@@ -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(role.includes('SKS managed 3.1.6 directive role') && role.includes('Execution role strategy'), 'managed agent role must include rich directive content', { role, report });
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-[56]-directive-check-lib\.ts$/.test(rel)
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
  });
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ export const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
6
+ const RELEASE_HELPERS = [
7
+ 'src/scripts/release-dag-full-coverage-check.ts',
8
+ 'src/scripts/sks-3-1-5-directive-check-lib.ts',
9
+ 'src/scripts/sks-3-1-6-directive-check-lib.ts',
10
+ 'src/scripts/sks-3-1-7-directive-check-lib.ts',
11
+ 'src/scripts/release-script-type-safety-check.ts',
12
+ 'src/scripts/no-ts-nocheck-release-scripts-check.ts'
13
+ ];
14
+ export async function runDirective317Gate(id) {
15
+ if (id === 'release-scripts:type-safe')
16
+ return releaseScriptsTypeSafe(id);
17
+ if (id === 'lint:no-ts-nocheck-release-scripts')
18
+ return noTsNoCheckReleaseScripts(id);
19
+ throw new Error(`unknown_3_1_7_gate:${id}`);
20
+ }
21
+ function releaseScriptsTypeSafe(id) {
22
+ checkNoTsNoCheckReleaseScripts();
23
+ const dag = readText('src/scripts/release-dag-full-coverage-check.ts');
24
+ for (const token of [
25
+ 'interface PackageJsonShape',
26
+ 'interface ReleaseGate',
27
+ 'interface ReleaseGateManifest',
28
+ 'function readPackageJson',
29
+ 'function readReleaseGateManifest',
30
+ 'function isReleaseGate',
31
+ 'function normalizeStringList',
32
+ 'const parsed: unknown'
33
+ ]) {
34
+ assertGate(dag.includes(token), `release DAG helper missing typed token:${token}`);
35
+ }
36
+ emitGate(id, { checked_files: RELEASE_HELPERS });
37
+ }
38
+ function noTsNoCheckReleaseScripts(id) {
39
+ checkNoTsNoCheckReleaseScripts();
40
+ emitGate(id, { checked_files: RELEASE_HELPERS });
41
+ }
42
+ function checkNoTsNoCheckReleaseScripts() {
43
+ const offenders = RELEASE_HELPERS.filter((rel) => /^\s*\/\/\s*@ts-nocheck\b/m.test(readText(rel)));
44
+ assertGate(offenders.length === 0, 'release script ts-nocheck offenders found', { offenders, checked_files: RELEASE_HELPERS });
45
+ }
46
+ function readText(rel) {
47
+ return fs.readFileSync(path.join(root, rel), 'utf8');
48
+ }
49
+ function assertGate(condition, message, detail = {}) {
50
+ if (condition)
51
+ return;
52
+ console.error(JSON.stringify({ ok: false, message, detail }, null, 2));
53
+ process.exit(1);
54
+ }
55
+ function emitGate(gate, detail = {}) {
56
+ console.log(JSON.stringify({ schema: 'sks.release-gate.v1', ok: true, gate, ...detail }, null, 2));
57
+ }
58
+ //# sourceMappingURL=sks-3-1-7-directive-check-lib.js.map