sneakoscope 3.1.3 → 3.1.5

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 (51) hide show
  1. package/README.md +1 -1
  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/cli/install-helpers.js +56 -4
  8. package/dist/commands/codex-app.js +45 -1
  9. package/dist/commands/codex-lb.js +12 -9
  10. package/dist/commands/doctor.js +44 -1
  11. package/dist/core/codex-app/codex-agent-role-sync.js +119 -0
  12. package/dist/core/codex-app/codex-agent-type-probe.js +202 -0
  13. package/dist/core/codex-app/codex-app-execution-profile.js +39 -0
  14. package/dist/core/codex-app/codex-app-fast-ui-repair.js +7 -4
  15. package/dist/core/codex-app/codex-app-harness-matrix.js +127 -0
  16. package/dist/core/codex-app/codex-app-types.js +21 -0
  17. package/dist/core/codex-app/codex-hook-approval-probe.js +188 -0
  18. package/dist/core/codex-app/codex-hook-lifecycle.js +61 -0
  19. package/dist/core/codex-app/codex-init-deep.js +180 -0
  20. package/dist/core/codex-app/codex-skill-sync.js +164 -0
  21. package/dist/core/codex-app/lazycodex-analysis.js +72 -0
  22. package/dist/core/codex-app/lazycodex-interop-policy.js +60 -0
  23. package/dist/core/codex-app/lazycodex-live-analyzer.js +98 -0
  24. package/dist/core/commands/loop-command.js +11 -0
  25. package/dist/core/commands/mad-sks-command.js +113 -17
  26. package/dist/core/commands/qa-loop-command.js +3 -2
  27. package/dist/core/commands/research-command.js +2 -2
  28. package/dist/core/doctor/doctor-readiness-matrix.js +7 -0
  29. package/dist/core/doctor/doctor-zellij-repair.js +40 -0
  30. package/dist/core/feature-fixtures.js +1 -0
  31. package/dist/core/feature-registry.js +4 -1
  32. package/dist/core/fsx.js +1 -1
  33. package/dist/core/hooks-runtime.js +13 -0
  34. package/dist/core/init.js +4 -1
  35. package/dist/core/loops/loop-continuation-enforcer.js +40 -0
  36. package/dist/core/loops/loop-planner.js +29 -3
  37. package/dist/core/loops/loop-worker-runtime.js +27 -7
  38. package/dist/core/naruto/naruto-loop-worker-router.js +11 -2
  39. package/dist/core/qa-loop.js +39 -4
  40. package/dist/core/research/research-cycle-runner.js +1 -0
  41. package/dist/core/research/research-stage-runner.js +9 -2
  42. package/dist/core/research.js +35 -1
  43. package/dist/core/version.js +1 -1
  44. package/dist/core/zellij/homebrew-policy.js +44 -0
  45. package/dist/core/zellij/zellij-capability.js +32 -3
  46. package/dist/core/zellij/zellij-self-heal-types.js +45 -0
  47. package/dist/core/zellij/zellij-self-heal.js +414 -0
  48. package/dist/core/zellij/zellij-update.js +39 -6
  49. package/dist/scripts/sks-3-1-4-directive-check-lib.js +241 -0
  50. package/dist/scripts/sks-3-1-5-directive-check-lib.js +347 -0
  51. package/package.json +52 -2
@@ -0,0 +1,202 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { findCodexBinary } from '../codex-adapter.js';
4
+ import { nowIso, runProcess, writeJsonAtomic } from '../fsx.js';
5
+ import { isRecord } from './codex-app-types.js';
6
+ export async function probeCodexAgentTypeSupport(root, input = {}) {
7
+ const env = input.env || process.env;
8
+ const fixture = await fixtureSchemaProbe(env);
9
+ if (fixture)
10
+ return persist(root, fixture, input.writeReport !== false);
11
+ const schema = await readSchemaFromEnv(env);
12
+ if (schema)
13
+ return persist(root, probeSchema(schema.value, schema.source, schema.schemaPath), input.writeReport !== false);
14
+ const doctor = await probeDoctorJson(input.codexBin, env);
15
+ if (doctor.source !== 'unknown')
16
+ return persist(root, doctor, input.writeReport !== false);
17
+ const help = await probeHelp(input.codexBin, env);
18
+ if (help.source !== 'unknown')
19
+ return persist(root, help, input.writeReport !== false);
20
+ const envProbe = envFallbackProbe(env);
21
+ if (envProbe)
22
+ return persist(root, envProbe, input.writeReport !== false);
23
+ return persist(root, {
24
+ schema: 'sks.codex-agent-type-probe.v1',
25
+ generated_at: nowIso(),
26
+ ok: true,
27
+ supported: false,
28
+ source: 'unknown',
29
+ spawn_tool_name: 'unknown',
30
+ schema_path: null,
31
+ evidence: [],
32
+ blockers: [],
33
+ warnings: ['agent_type_support_unknown_message_role_fallback']
34
+ }, input.writeReport !== false);
35
+ }
36
+ export function agentRolePayloadFor(role, probe) {
37
+ if (probe.supported)
38
+ return { strategy: 'agent_type', agent_type: role, probe_artifact_path: '.sneakoscope/reports/codex-agent-type-probe.json' };
39
+ return {
40
+ strategy: 'message-role',
41
+ message_role_prefix: `Role: ${role}. Use this as a message-level role because native agent_type is unavailable or unverified.`,
42
+ probe_artifact_path: '.sneakoscope/reports/codex-agent-type-probe.json'
43
+ };
44
+ }
45
+ async function fixtureSchemaProbe(env) {
46
+ const raw = env.SKS_CODEX_AGENT_TYPE_FIXTURE;
47
+ if (!raw)
48
+ return null;
49
+ const supported = raw === '1' || raw === 'supported' || raw === 'true';
50
+ return {
51
+ schema: 'sks.codex-agent-type-probe.v1',
52
+ generated_at: nowIso(),
53
+ ok: true,
54
+ supported,
55
+ source: 'fixture',
56
+ spawn_tool_name: supported ? 'spawn_agent' : 'unknown',
57
+ schema_path: supported ? 'fixture.spawn_agent.parameters.agent_type' : null,
58
+ evidence: [`fixture:${raw}`],
59
+ blockers: [],
60
+ warnings: []
61
+ };
62
+ }
63
+ async function readSchemaFromEnv(env) {
64
+ if (env.SKS_CODEX_TOOL_SCHEMA_FILE) {
65
+ const file = path.resolve(env.SKS_CODEX_TOOL_SCHEMA_FILE);
66
+ const text = await fs.readFile(file, 'utf8').catch(() => '');
67
+ if (text)
68
+ return { value: JSON.parse(text), source: 'codex-tool-schema', schemaPath: file };
69
+ }
70
+ if (env.SKS_CODEX_TOOL_SCHEMA_JSON) {
71
+ return { value: JSON.parse(env.SKS_CODEX_TOOL_SCHEMA_JSON), source: 'codex-tool-schema', schemaPath: 'env:SKS_CODEX_TOOL_SCHEMA_JSON' };
72
+ }
73
+ return null;
74
+ }
75
+ async function probeDoctorJson(codexBin, env) {
76
+ const bin = codexBin || env.CODEX_BIN || await findCodexBinary().catch(() => null);
77
+ if (!bin)
78
+ return unknownProbe(['codex_cli_missing']);
79
+ const run = await runProcess(bin, ['doctor', '--json'], { env, timeoutMs: 8000, maxOutputBytes: 256 * 1024 }).catch(() => null);
80
+ const text = `${run?.stdout || ''}${run?.stderr || ''}`.trim();
81
+ if (!run || run.code !== 0 || !text)
82
+ return unknownProbe(['codex_doctor_json_unavailable']);
83
+ try {
84
+ const parsed = JSON.parse(text);
85
+ const probed = probeSchema(parsed, 'codex-doctor-json', 'doctor-json');
86
+ return { ...probed, source: probed.supported ? 'codex-doctor-json' : 'unknown' };
87
+ }
88
+ catch {
89
+ return unknownProbe(['codex_doctor_json_parse_failed']);
90
+ }
91
+ }
92
+ async function probeHelp(codexBin, env) {
93
+ const bin = codexBin || env.CODEX_BIN || await findCodexBinary().catch(() => null);
94
+ if (!bin)
95
+ return unknownProbe(['codex_cli_missing']);
96
+ const run = await runProcess(bin, ['--help'], { env, timeoutMs: 5000, maxOutputBytes: 128 * 1024 }).catch(() => null);
97
+ const text = `${run?.stdout || ''}${run?.stderr || ''}`;
98
+ if (!run || run.code !== 0 || !text)
99
+ return unknownProbe(['codex_help_unavailable']);
100
+ const supported = /\bagent_type\b/.test(text) && /\bspawn_agent\b|\bmulti_agent_v2\b/.test(text);
101
+ return {
102
+ schema: 'sks.codex-agent-type-probe.v1',
103
+ generated_at: nowIso(),
104
+ ok: true,
105
+ supported,
106
+ source: supported ? 'codex-help' : 'unknown',
107
+ spawn_tool_name: text.includes('multi_agent_v2') ? 'multi_agent_v2' : supported ? 'spawn_agent' : 'unknown',
108
+ schema_path: supported ? 'codex --help' : null,
109
+ evidence: supported ? ['help_mentions_agent_type'] : [],
110
+ blockers: [],
111
+ warnings: supported ? [] : ['agent_type_not_found_in_help']
112
+ };
113
+ }
114
+ function envFallbackProbe(env) {
115
+ if (env.SKS_CODEX_AGENT_TYPE_SUPPORTED === undefined)
116
+ return null;
117
+ if (env.SKS_CODEX_AGENT_TYPE_ALLOW_ENV_FALLBACK !== '1' && env.NODE_ENV !== 'test') {
118
+ return {
119
+ schema: 'sks.codex-agent-type-probe.v1',
120
+ generated_at: nowIso(),
121
+ ok: true,
122
+ supported: false,
123
+ source: 'env',
124
+ spawn_tool_name: 'unknown',
125
+ schema_path: null,
126
+ evidence: [],
127
+ blockers: [],
128
+ warnings: ['env_agent_type_fallback_ignored_outside_test_mode']
129
+ };
130
+ }
131
+ const supported = env.SKS_CODEX_AGENT_TYPE_SUPPORTED === '1';
132
+ return {
133
+ schema: 'sks.codex-agent-type-probe.v1',
134
+ generated_at: nowIso(),
135
+ ok: true,
136
+ supported,
137
+ source: 'env',
138
+ spawn_tool_name: supported ? 'spawn_agent' : 'unknown',
139
+ schema_path: supported ? 'env:SKS_CODEX_AGENT_TYPE_SUPPORTED' : null,
140
+ evidence: [`env:SKS_CODEX_AGENT_TYPE_SUPPORTED=${env.SKS_CODEX_AGENT_TYPE_SUPPORTED}`],
141
+ blockers: [],
142
+ warnings: ['env_agent_type_fallback_test_only']
143
+ };
144
+ }
145
+ function probeSchema(value, source, schemaPath) {
146
+ const found = findAgentType(value);
147
+ return {
148
+ schema: 'sks.codex-agent-type-probe.v1',
149
+ generated_at: nowIso(),
150
+ ok: true,
151
+ supported: found.supported,
152
+ source,
153
+ spawn_tool_name: found.tool,
154
+ schema_path: found.path || schemaPath,
155
+ evidence: found.supported ? [`agent_type:${found.path || 'found'}`] : ['agent_type_absent'],
156
+ blockers: [],
157
+ warnings: found.supported ? [] : ['agent_type_not_supported_message_role_fallback']
158
+ };
159
+ }
160
+ function findAgentType(value, trail = []) {
161
+ if (Array.isArray(value)) {
162
+ for (let index = 0; index < value.length; index += 1) {
163
+ const found = findAgentType(value[index], [...trail, String(index)]);
164
+ if (found.supported)
165
+ return found;
166
+ }
167
+ return { supported: false, tool: 'unknown', path: null };
168
+ }
169
+ if (!isRecord(value))
170
+ return { supported: false, tool: 'unknown', path: null };
171
+ const name = String(value.name || value.tool || value.id || '');
172
+ const tool = name.includes('multi_agent_v2') ? 'multi_agent_v2' : name.includes('spawn_agent') ? 'spawn_agent' : 'unknown';
173
+ if ('agent_type' in value || isRecord(value.properties) && 'agent_type' in value.properties) {
174
+ return { supported: true, tool: tool === 'unknown' ? 'spawn_agent' : tool, path: [...trail, 'agent_type'].join('.') };
175
+ }
176
+ for (const [key, entry] of Object.entries(value)) {
177
+ const found = findAgentType(entry, [...trail, key]);
178
+ if (found.supported)
179
+ return { ...found, tool: found.tool === 'unknown' && tool !== 'unknown' ? tool : found.tool };
180
+ }
181
+ return { supported: false, tool, path: null };
182
+ }
183
+ function unknownProbe(blockers) {
184
+ return {
185
+ schema: 'sks.codex-agent-type-probe.v1',
186
+ generated_at: nowIso(),
187
+ ok: blockers.length === 0,
188
+ supported: false,
189
+ source: 'unknown',
190
+ spawn_tool_name: 'unknown',
191
+ schema_path: null,
192
+ evidence: [],
193
+ blockers,
194
+ warnings: ['agent_type_support_unknown_message_role_fallback']
195
+ };
196
+ }
197
+ async function persist(root, report, writeReport) {
198
+ if (writeReport)
199
+ await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-agent-type-probe.json'), report).catch(() => undefined);
200
+ return report;
201
+ }
202
+ //# sourceMappingURL=codex-agent-type-probe.js.map
@@ -0,0 +1,39 @@
1
+ import path from 'node:path';
2
+ import { nowIso, writeJsonAtomic } from '../fsx.js';
3
+ import { buildCodexAppHarnessMatrix } from './codex-app-harness-matrix.js';
4
+ import { isCodexAppHarnessMatrix } from './codex-app-types.js';
5
+ export async function resolveCodexAppExecutionProfile(input = { root: process.cwd() }) {
6
+ const root = path.resolve(input.root || process.cwd());
7
+ const maybeMatrix = input.matrix || await buildCodexAppHarnessMatrix({ root }).catch(() => null);
8
+ const matrix = isCodexAppHarnessMatrix(maybeMatrix) ? maybeMatrix : null;
9
+ const mode = !matrix?.codex_cli.available
10
+ ? 'degraded-no-app'
11
+ : matrix.app_features.app_handoff_ready && matrix.app_features.agent_type_supported
12
+ ? 'codex-app-native'
13
+ : matrix.codex_cli.available
14
+ ? 'codex-cli-headless'
15
+ : 'sks-loop-headless';
16
+ const profile = {
17
+ schema: 'sks.codex-app-execution-profile.v1',
18
+ generated_at: nowIso(),
19
+ ok: mode !== 'degraded-no-app',
20
+ mode,
21
+ agent_role_strategy: matrix?.app_features.agent_type_supported ? 'agent_type' : 'message-role',
22
+ hooks_assumed_running: false,
23
+ hooks_approval_required: matrix?.app_features.hook_approval_state !== 'approved',
24
+ hook_approval_state: matrix?.app_features.hook_approval_state || 'unknown',
25
+ app_handoff_ready: matrix?.app_features.app_handoff_ready === true,
26
+ image_path_exposure_ready: matrix?.app_features.image_path_exposure_ready === true,
27
+ plugin_mcp_inventory_ready: matrix?.app_features.mcp_inventory_ready === true,
28
+ loop_mesh_app_profile_available: true,
29
+ artifact_path: '.sneakoscope/reports/codex-app-execution-profile.json',
30
+ matrix_artifact_path: '.sneakoscope/reports/codex-app-harness-matrix.json',
31
+ agent_type_probe_artifact_path: '.sneakoscope/reports/codex-agent-type-probe.json',
32
+ hook_approval_probe_artifact_path: '.sneakoscope/reports/codex-hook-approval-probe.json',
33
+ blockers: mode === 'degraded-no-app' ? ['codex_cli_missing'] : [],
34
+ warnings: matrix?.warnings || []
35
+ };
36
+ await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-app-execution-profile.json'), profile).catch(() => undefined);
37
+ return profile;
38
+ }
39
+ //# sourceMappingURL=codex-app-execution-profile.js.map
@@ -4,8 +4,9 @@ import { ensureDir, nowIso, readText, sha256, writeJsonAtomic, writeTextAtomic }
4
4
  import { codexHome, scanProjectLocalForbiddenKeys, snapshotCodexAppUiState } from './codex-app-ui-state-snapshot.js';
5
5
  import { assertCodexAppUiMutationAllowed } from './codex-app-ui-clobber-guard.js';
6
6
  export const CODEX_APP_FAST_UI_REPAIR_SCHEMA = 'sks.codex-app-fast-ui-repair.v1';
7
- const HOST_OWNED_TOP_LEVEL_RE = /^\s*(openai_base_url|chatgpt_base_url|apps_mcp_product_sku|model_provider|notify|profile|experimental_realtime_ws_base_url|service_tier)\s*=/;
8
- const HOST_OWNED_TABLE_RE = /^\s*\[(model_providers(?:[.\]].*)?|profiles(?:[.\]].*)?|otel(?:[.\]].*)?|user\.fast_mode(?:[.\]].*)?)\]\s*$/;
7
+ const FAST_UI_TOP_LEVEL_RE = /^\s*service_tier\s*=/;
8
+ const FAST_UI_FEATURE_LINE_RE = /^\s*fast_mode\s*=/;
9
+ const FAST_UI_USER_TABLE_LINE_RE = /^\s*(enabled|visible|locked|hidden|disabled)\s*=/;
9
10
  const SKS_CAUSED_RE = /(?:SKS|Sneakoscope|codex-lb|sks-mad|sks fast)/i;
10
11
  export async function repairCodexAppFastUi(root = process.cwd(), input = {}) {
11
12
  const resolvedRoot = path.resolve(root);
@@ -124,8 +125,10 @@ function stripProjectLocalForbiddenKeys(text) {
124
125
  }
125
126
  function stripSksCausedHostOwnedLines(text) {
126
127
  return stripMatchingLines(text, (line, table, previous, next) => {
127
- const isHostOwned = HOST_OWNED_TOP_LEVEL_RE.test(line) || HOST_OWNED_TABLE_RE.test(line) || Boolean(table && /^(model_providers|profiles|otel|user\.fast_mode)/.test(table));
128
- return isHostOwned && (SKS_CAUSED_RE.test(line) || SKS_CAUSED_RE.test(previous) || SKS_CAUSED_RE.test(next));
128
+ const isFastUiLine = FAST_UI_TOP_LEVEL_RE.test(line)
129
+ || (table === 'features' && FAST_UI_FEATURE_LINE_RE.test(line))
130
+ || (table === 'user.fast_mode' && FAST_UI_USER_TABLE_LINE_RE.test(line));
131
+ return isFastUiLine && (SKS_CAUSED_RE.test(line) || SKS_CAUSED_RE.test(previous) || SKS_CAUSED_RE.test(next));
129
132
  });
130
133
  }
131
134
  function stripMatchingLines(text, shouldRemove) {
@@ -0,0 +1,127 @@
1
+ import path from 'node:path';
2
+ import { findCodexBinary } from '../codex-adapter.js';
3
+ import { codexAppIntegrationStatus } from '../codex-app.js';
4
+ import { detectCodex0138Capability } from '../codex-control/codex-0138-capability.js';
5
+ import { detectCodex0139Capability } from '../codex-control/codex-0139-capability.js';
6
+ import { buildCodexPluginInventory } from '../codex-plugins/codex-plugin-json.js';
7
+ import { nowIso, runProcess, writeJsonAtomic } from '../fsx.js';
8
+ import { repairAgentRoleConfigs } from '../agents/agent-role-config.js';
9
+ import { buildLazyCodexInteropPolicy } from './lazycodex-interop-policy.js';
10
+ import { probeCodexAgentTypeSupport } from './codex-agent-type-probe.js';
11
+ import { probeCodexHookApprovalState } from './codex-hook-approval-probe.js';
12
+ import { isRecord } from './codex-app-types.js';
13
+ export async function buildCodexAppHarnessMatrix(input = { root: process.cwd() }) {
14
+ const root = path.resolve(input.root || process.cwd());
15
+ const codexBin = await findCodexBinary().catch(() => null);
16
+ const version = codexBin ? await codexVersion(codexBin) : null;
17
+ const cap0138 = await detectCodex0138Capability({ codexBin }).catch((err) => ({ blockers: [messageOf(err)] }));
18
+ const cap0139 = await detectCodex0139Capability({ codexBin }).catch((err) => ({ blockers: [messageOf(err)] }));
19
+ const app = await codexAppIntegrationStatus({ codex: { bin: codexBin, version, available: Boolean(codexBin) } }).catch((err) => ({ ok: false, blockers: [messageOf(err)] }));
20
+ const plugins = await buildCodexPluginInventory().catch((err) => ({ plugins: [], marketplace_available: false, blockers: [messageOf(err)] }));
21
+ const hookApproval = await probeCodexHookApprovalState(root, { codexBin }).catch((err) => ({
22
+ schema: 'sks.codex-hook-approval-probe.v1',
23
+ generated_at: nowIso(),
24
+ ok: false,
25
+ detectable: false,
26
+ approval_state: 'unknown',
27
+ sources_checked: [],
28
+ blockers: [messageOf(err)],
29
+ warnings: ['hook_approval_probe_failed']
30
+ }));
31
+ const agentType = await probeCodexAgentTypeSupport(root, { codexBin }).catch((err) => ({
32
+ schema: 'sks.codex-agent-type-probe.v1',
33
+ generated_at: nowIso(),
34
+ ok: false,
35
+ supported: false,
36
+ source: 'unknown',
37
+ spawn_tool_name: 'unknown',
38
+ schema_path: null,
39
+ evidence: [],
40
+ blockers: [messageOf(err)],
41
+ warnings: ['agent_type_probe_failed_message_role_fallback']
42
+ }));
43
+ const agents = await repairAgentRoleConfigs({ root, apply: input.applyRepairs === true, reportPath: path.join(root, '.sneakoscope', 'reports', 'codex-agent-role-sync.json') }).catch((err) => ({ ok: false, blockers: [messageOf(err)] }));
44
+ const interop = await buildLazyCodexInteropPolicy({ root, inventory: plugins }).catch(() => null);
45
+ const appRecord = isRecord(app) ? app : {};
46
+ const requiredSkills = isRecord(appRecord.required_skills) ? appRecord.required_skills : {};
47
+ const skills = isRecord(appRecord.skills) ? appRecord.skills : {};
48
+ const skillShadows = isRecord(appRecord.skill_shadows) ? appRecord.skill_shadows : {};
49
+ const hasSkills = requiredSkills.ok === true || skills.ok === true || skillShadows.ok !== false;
50
+ const hookApprovalKnown = hookApproval.detectable === true;
51
+ const hookBlockers = hookApproval.approval_state === 'modified_requires_reapproval'
52
+ ? ['hook_modified_requires_reapproval']
53
+ : [];
54
+ const matrix = {
55
+ schema: 'sks.codex-app-harness-matrix.v1',
56
+ generated_at: nowIso(),
57
+ ok: Boolean(codexBin) && feature(cap0138, 'supports_plugin_json') !== false && recordOk(agents) !== false,
58
+ codex_cli: { available: Boolean(codexBin), version },
59
+ app_features: {
60
+ plugin_json: feature(cap0138, 'supports_plugin_json') === true,
61
+ marketplace_add: feature(cap0139, 'supports_marketplace_source_field') === true || feature(plugins, 'marketplace_available') === true,
62
+ marketplace_upgrade: feature(cap0139, 'supports_marketplace_source_field') === true,
63
+ startup_review_detectable: hookApprovalKnown,
64
+ hook_approval_state_detectable: hookApprovalKnown,
65
+ hook_approval_state: hookApproval.approval_state,
66
+ skill_picker_ready: Boolean(hasSkills),
67
+ agent_type_supported: agentType.supported,
68
+ mcp_inventory_ready: Array.isArray(isRecord(plugins) ? plugins.plugins : null),
69
+ app_handoff_ready: feature(cap0138, 'supports_app_handoff') === true,
70
+ image_path_exposure_ready: feature(cap0138, 'supports_image_path_exposure') === true
71
+ },
72
+ sks_integrations: {
73
+ dollar_skills_synced: Boolean(hasSkills),
74
+ agent_roles_synced: recordOk(agents) !== false,
75
+ hooks_synced: hookApproval.approval_state === 'approved',
76
+ init_deep_available: true,
77
+ loop_mesh_app_profile_available: true
78
+ },
79
+ probes: {
80
+ hook_approval: hookApproval,
81
+ agent_type: agentType
82
+ },
83
+ blockers: [
84
+ ...(!codexBin ? ['codex_cli_missing'] : []),
85
+ ...blockersOf(cap0138),
86
+ ...blockersOf(plugins),
87
+ ...blockersOf(agents),
88
+ ...hookBlockers
89
+ ],
90
+ warnings: [
91
+ ...blockersOf(cap0139).map((b) => `codex_0139:${b}`),
92
+ ...hookApproval.blockers.map((b) => `hooks:${b}`),
93
+ ...hookApproval.warnings,
94
+ ...agentType.warnings,
95
+ ...(hookApproval.approval_state === 'pending_review' ? ['hook_approval_pending_review'] : []),
96
+ ...(hookApproval.approval_state === 'not_installed' ? ['hooks_not_installed'] : []),
97
+ ...(interop?.lazycodex_detected ? ['lazycodex_detected_coexist_mode'] : [])
98
+ ]
99
+ };
100
+ matrix.ok = matrix.blockers.length === 0;
101
+ await writeCodexAppHarnessMatrix(root, matrix, input.missionDir);
102
+ return matrix;
103
+ }
104
+ export async function writeCodexAppHarnessMatrix(root, matrix, missionDir) {
105
+ await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-app-harness-matrix.json'), matrix);
106
+ if (missionDir)
107
+ await writeJsonAtomic(path.join(missionDir, 'codex-app-harness-matrix.json'), matrix).catch(() => undefined);
108
+ }
109
+ async function codexVersion(bin) {
110
+ const run = await runProcess(bin, ['--version'], { timeoutMs: 5000, maxOutputBytes: 16 * 1024 }).catch(() => null);
111
+ return run?.code === 0 ? `${run.stdout || run.stderr || ''}`.trim() || null : null;
112
+ }
113
+ function blockersOf(value) {
114
+ if (!isRecord(value) || !Array.isArray(value.blockers))
115
+ return [];
116
+ return value.blockers.map((item) => String(item)).filter(Boolean);
117
+ }
118
+ function feature(value, key) {
119
+ return isRecord(value) && typeof value[key] === 'boolean' ? value[key] : undefined;
120
+ }
121
+ function recordOk(value) {
122
+ return isRecord(value) && typeof value.ok === 'boolean' ? value.ok : undefined;
123
+ }
124
+ function messageOf(err) {
125
+ return err instanceof Error ? err.message : String(err);
126
+ }
127
+ //# sourceMappingURL=codex-app-harness-matrix.js.map
@@ -0,0 +1,21 @@
1
+ export function isRecord(value) {
2
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
3
+ }
4
+ export function stringList(value) {
5
+ return Array.isArray(value) ? value.map((item) => String(item)).filter(Boolean) : [];
6
+ }
7
+ export function isCodexAppHarnessMatrix(value) {
8
+ if (!isRecord(value))
9
+ return false;
10
+ if (value.schema !== 'sks.codex-app-harness-matrix.v1')
11
+ return false;
12
+ const app = value.app_features;
13
+ const sks = value.sks_integrations;
14
+ return isRecord(app)
15
+ && isRecord(sks)
16
+ && typeof value.ok === 'boolean'
17
+ && isRecord(value.codex_cli)
18
+ && Array.isArray(value.blockers)
19
+ && Array.isArray(value.warnings);
20
+ }
21
+ //# sourceMappingURL=codex-app-types.js.map
@@ -0,0 +1,188 @@
1
+ import path from 'node:path';
2
+ import { findCodexBinary } from '../codex-adapter.js';
3
+ import { readCodexHookActualState } from '../codex-hooks/codex-hook-actual-discovery.js';
4
+ import { nowIso, runProcess, writeJsonAtomic } from '../fsx.js';
5
+ import { isRecord } from './codex-app-types.js';
6
+ export async function probeCodexHookApprovalState(root, input = {}) {
7
+ const env = input.env || process.env;
8
+ const sources = [];
9
+ const fixture = normalizeApprovalState(env.SKS_CODEX_HOOK_APPROVAL_FIXTURE);
10
+ if (fixture) {
11
+ const report = buildProbe(fixture !== 'unknown', fixture, [{
12
+ source: 'config',
13
+ ok: fixture !== 'unknown',
14
+ evidence: [`fixture:${fixture}`],
15
+ blockers: fixture === 'unknown' ? ['fixture_unknown'] : []
16
+ }]);
17
+ return persist(root, report, input.writeReport !== false);
18
+ }
19
+ const actual = await readCodexHookActualState(root).catch((err) => ({
20
+ schema: 'sks.codex-hook-actual-state.v1',
21
+ ok: false,
22
+ root,
23
+ sources: [],
24
+ managed_dirs: [],
25
+ entries: [],
26
+ unsupported_handlers: [],
27
+ invalid_matchers: [],
28
+ dual_representation: [],
29
+ warnings: [],
30
+ blockers: [messageOf(err)]
31
+ }));
32
+ const actualState = approvalFromActualState(actual);
33
+ sources.push({
34
+ source: 'hook-actual-state',
35
+ ok: actual.ok !== false,
36
+ evidence: [
37
+ `entries:${actual.entries.length}`,
38
+ `trusted:${actual.entries.filter((entry) => entry.trust_status === 'Trusted' || entry.trust_status === 'Managed').length}`,
39
+ `modified:${actual.entries.filter((entry) => entry.trust_status === 'Modified').length}`,
40
+ `untrusted:${actual.entries.filter((entry) => entry.trust_status === 'Untrusted').length}`
41
+ ],
42
+ blockers: actual.blockers
43
+ });
44
+ const doctor = await probeCodexDoctorApproval(input.codexBin, env);
45
+ sources.push(doctor.source);
46
+ const doctorState = doctor.approval_state;
47
+ const state = strongestApprovalState([actualState, doctorState]);
48
+ const detectable = state !== 'unknown';
49
+ const blockers = [
50
+ ...sources.flatMap((source) => source.blockers),
51
+ ...(state === 'modified_requires_reapproval' ? ['hook_modified_requires_reapproval'] : [])
52
+ ];
53
+ const warnings = [
54
+ ...(state === 'unknown' ? ['hook_approval_state_unknown'] : []),
55
+ ...(actual.entries.length > 0 && state === 'unknown' ? ['hooks_installed_but_approval_unknown'] : [])
56
+ ];
57
+ const report = {
58
+ schema: 'sks.codex-hook-approval-probe.v1',
59
+ generated_at: nowIso(),
60
+ ok: blockers.length === 0 && state !== 'pending_review' && state !== 'modified_requires_reapproval',
61
+ detectable,
62
+ approval_state: state,
63
+ sources_checked: sources,
64
+ blockers,
65
+ warnings
66
+ };
67
+ return persist(root, report, input.writeReport !== false);
68
+ }
69
+ function approvalFromActualState(actual) {
70
+ if (actual.entries.length === 0)
71
+ return 'not_installed';
72
+ if (actual.entries.some((entry) => entry.trust_status === 'Modified'))
73
+ return 'modified_requires_reapproval';
74
+ if (actual.entries.some((entry) => entry.trust_status === 'Untrusted'))
75
+ return 'pending_review';
76
+ if (actual.entries.every((entry) => entry.trust_status === 'Trusted' || entry.trust_status === 'Managed'))
77
+ return 'approved';
78
+ return 'unknown';
79
+ }
80
+ async function probeCodexDoctorApproval(codexBin, env) {
81
+ const bin = codexBin || env.CODEX_BIN || await findCodexBinary().catch(() => null);
82
+ if (!bin) {
83
+ return { approval_state: 'unknown', source: { source: 'codex-doctor-json', ok: false, evidence: [], blockers: ['codex_cli_missing'] } };
84
+ }
85
+ const run = await runProcess(bin, ['doctor', '--json'], { env, timeoutMs: 8000, maxOutputBytes: 256 * 1024 }).catch((err) => ({
86
+ code: 1,
87
+ stdout: '',
88
+ stderr: messageOf(err)
89
+ }));
90
+ const text = `${run.stdout || ''}${run.stderr || ''}`.trim();
91
+ if (run.code !== 0 || !text) {
92
+ return {
93
+ approval_state: 'unknown',
94
+ source: { source: 'codex-doctor-json', ok: false, evidence: text ? [text.slice(0, 240)] : [], blockers: ['codex_doctor_json_unavailable'] }
95
+ };
96
+ }
97
+ try {
98
+ const parsed = JSON.parse(text);
99
+ const state = findApprovalState(parsed);
100
+ return {
101
+ approval_state: state || 'unknown',
102
+ source: {
103
+ source: 'codex-doctor-json',
104
+ ok: Boolean(state),
105
+ evidence: state ? [`approval_state:${state}`] : ['doctor_json_no_hook_approval_field'],
106
+ blockers: state ? [] : ['hook_approval_not_exposed_by_codex_doctor']
107
+ }
108
+ };
109
+ }
110
+ catch {
111
+ return {
112
+ approval_state: 'unknown',
113
+ source: { source: 'codex-doctor-json', ok: false, evidence: [text.slice(0, 240)], blockers: ['codex_doctor_json_parse_failed'] }
114
+ };
115
+ }
116
+ }
117
+ function findApprovalState(value) {
118
+ if (!isRecord(value))
119
+ return null;
120
+ for (const key of ['hook_approval_state', 'approval_state', 'hookApprovalState']) {
121
+ const state = normalizeApprovalState(value[key]);
122
+ if (state)
123
+ return state;
124
+ }
125
+ for (const nested of Object.values(value)) {
126
+ if (Array.isArray(nested)) {
127
+ for (const item of nested) {
128
+ const found = findApprovalState(item);
129
+ if (found)
130
+ return found;
131
+ }
132
+ }
133
+ else if (isRecord(nested)) {
134
+ const found = findApprovalState(nested);
135
+ if (found)
136
+ return found;
137
+ }
138
+ }
139
+ return null;
140
+ }
141
+ function normalizeApprovalState(value) {
142
+ const raw = String(value || '').trim().toLowerCase().replace(/[-\s]+/g, '_');
143
+ if (raw === 'approved' || raw === 'trusted' || raw === 'managed')
144
+ return 'approved';
145
+ if (raw === 'pending' || raw === 'pending_review' || raw === 'untrusted')
146
+ return 'pending_review';
147
+ if (raw === 'modified' || raw === 'modified_requires_reapproval')
148
+ return 'modified_requires_reapproval';
149
+ if (raw === 'not_installed' || raw === 'missing')
150
+ return 'not_installed';
151
+ if (raw === 'unknown')
152
+ return 'unknown';
153
+ return null;
154
+ }
155
+ function strongestApprovalState(states) {
156
+ if (states.includes('modified_requires_reapproval'))
157
+ return 'modified_requires_reapproval';
158
+ if (states.includes('pending_review'))
159
+ return 'pending_review';
160
+ if (states.includes('approved'))
161
+ return 'approved';
162
+ if (states.every((state) => state === 'not_installed'))
163
+ return 'not_installed';
164
+ if (states.includes('not_installed') && states.length === 1)
165
+ return 'not_installed';
166
+ return 'unknown';
167
+ }
168
+ function buildProbe(detectable, approvalState, sources) {
169
+ return {
170
+ schema: 'sks.codex-hook-approval-probe.v1',
171
+ generated_at: nowIso(),
172
+ ok: approvalState === 'approved' || approvalState === 'not_installed',
173
+ detectable,
174
+ approval_state: approvalState,
175
+ sources_checked: sources,
176
+ blockers: sources.flatMap((source) => source.blockers),
177
+ warnings: approvalState === 'unknown' ? ['hook_approval_state_unknown'] : []
178
+ };
179
+ }
180
+ async function persist(root, report, writeReport) {
181
+ if (writeReport)
182
+ await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-hook-approval-probe.json'), report).catch(() => undefined);
183
+ return report;
184
+ }
185
+ function messageOf(err) {
186
+ return err instanceof Error ? err.message : String(err);
187
+ }
188
+ //# sourceMappingURL=codex-hook-approval-probe.js.map
@@ -0,0 +1,61 @@
1
+ import path from 'node:path';
2
+ import { nowIso, writeJsonAtomic } from '../fsx.js';
3
+ import { readCodexHookActualState } from '../codex-hooks/codex-hook-actual-discovery.js';
4
+ import { installManagedCodexHooks } from '../codex-hooks/codex-hook-managed-install.js';
5
+ import { probeCodexHookApprovalState } from './codex-hook-approval-probe.js';
6
+ export async function buildCodexHookLifecycle(input = {}) {
7
+ const root = path.resolve(input.root || process.cwd());
8
+ const install = input.apply === true
9
+ ? await installManagedCodexHooks(root).catch((err) => ({ ok: false, blockers: [messageOf(err)] }))
10
+ : null;
11
+ const actual = await readCodexHookActualState(root).catch((err) => ({ ok: false, entries: [], blockers: [messageOf(err)] }));
12
+ const probe = await probeCodexHookApprovalState(root).catch((err) => ({
13
+ schema: 'sks.codex-hook-approval-probe.v1',
14
+ generated_at: nowIso(),
15
+ ok: false,
16
+ detectable: false,
17
+ approval_state: 'unknown',
18
+ sources_checked: [],
19
+ blockers: [messageOf(err)],
20
+ warnings: ['hook_approval_probe_failed']
21
+ }));
22
+ const events = {
23
+ UserPromptSubmit: ['route_classifier', 'goal_to_loop_compiler', 'permission_profile_badge'],
24
+ PreToolUse: ['requested_scope_guard', 'mad_db_priority_resolver', 'side_effect_zero_gate'],
25
+ PostToolUse: ['evidence_ledger', 'mutation_ledger', 'side_effect_scanner', 'context7_native_session_updates'],
26
+ Stop: ['continuation_enforcer', 'final_proof_check', 'loop_resume_hint'],
27
+ Notification: ['operator_status', 'zellij_anchor', 'codex_app_status']
28
+ };
29
+ const installedEvents = new Set(Array.isArray(actual.entries) ? actual.entries.map((entry) => String(entry.event || '')) : []);
30
+ const report = {
31
+ schema: 'sks.codex-hook-lifecycle.v1',
32
+ generated_at: nowIso(),
33
+ ok: actual.ok !== false && probe.approval_state !== 'modified_requires_reapproval',
34
+ apply: input.apply === true,
35
+ approval_state: probe.approval_state,
36
+ approval_state_detectable: probe.detectable,
37
+ lifecycle: Object.fromEntries(Object.entries(events).map(([event, actions]) => [event, {
38
+ actions,
39
+ installed: installedEvents.has(event),
40
+ approval_state: installedEvents.has(event) ? probe.approval_state : 'not_installed'
41
+ }])),
42
+ actual_state: actual,
43
+ install,
44
+ probe,
45
+ blockers: [
46
+ ...(Array.isArray(actual.blockers) ? actual.blockers.map(String) : []),
47
+ ...(probe.approval_state === 'modified_requires_reapproval' ? ['hook_modified_requires_reapproval'] : [])
48
+ ],
49
+ warnings: [
50
+ ...probe.warnings,
51
+ ...(probe.approval_state === 'pending_review' ? ['hook_approval_pending_review'] : []),
52
+ ...(probe.approval_state === 'unknown' ? ['hook_approval_state_unknown'] : [])
53
+ ]
54
+ };
55
+ await writeJsonAtomic(path.join(root, '.sneakoscope', 'reports', 'codex-hook-lifecycle.json'), report).catch(() => undefined);
56
+ return report;
57
+ }
58
+ function messageOf(err) {
59
+ return err instanceof Error ? err.message : String(err);
60
+ }
61
+ //# sourceMappingURL=codex-hook-lifecycle.js.map