sneakoscope 3.1.8 → 3.1.9

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 CHANGED
@@ -35,7 +35,7 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
35
35
 
36
36
  ## 🚀 Current Release
37
37
 
38
- SKS **3.1.8** hardens update/setup/doctor safety around four operator-facing failure modes: immutable core skills, duplicate project skills, real native capability repair status, and Supabase/secret preservation.
38
+ SKS **3.1.9** hardens update/setup/doctor safety around four operator-facing failure modes: immutable core skills, duplicate project skills, real native capability repair status, and Supabase/secret preservation.
39
39
 
40
40
  What changed in 3.1.8:
41
41
 
@@ -76,7 +76,7 @@ dependencies = [
76
76
 
77
77
  [[package]]
78
78
  name = "sks-core"
79
- version = "3.1.8"
79
+ version = "3.1.9"
80
80
  dependencies = [
81
81
  "serde_json",
82
82
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "3.1.8"
3
+ version = "3.1.9"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
4
4
  fn main() {
5
5
  let mut args = std::env::args().skip(1);
6
6
  match args.next().as_deref() {
7
- Some("--version") => println!("sks-rs 3.1.8"),
7
+ Some("--version") => println!("sks-rs 3.1.9"),
8
8
  Some("compact-info") => {
9
9
  let mut input = String::new();
10
10
  let _ = io::stdin().read_to_string(&mut input);
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schema": "sks.dist-build-stamp.v1",
3
3
  "package_name": "sneakoscope",
4
- "package_version": "3.1.8",
5
- "source_digest": "94742dc3eb63f280df98aa286ac2a94a14fd140f27c89db85880e4f92c630454",
6
- "source_file_count": 2602,
7
- "built_at_source_time": 1781513577024
4
+ "package_version": "3.1.9",
5
+ "source_digest": "dbfd5ddb700abfc4a008256b36e1743363164fd504bba959542ce79c25916dad",
6
+ "source_file_count": 2605,
7
+ "built_at_source_time": 1781537952236
8
8
  }
package/dist/bin/sks.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const FAST_PACKAGE_VERSION = '3.1.8';
2
+ const FAST_PACKAGE_VERSION = '3.1.9';
3
3
  const args = process.argv.slice(2);
4
4
  try {
5
5
  if (args[0] === '--agent' && args[1] === 'worker') {
package/dist/cli/args.js CHANGED
@@ -28,6 +28,23 @@ export function positionalArgs(args = []) {
28
28
  '--command',
29
29
  '--project-ref',
30
30
  '--agent',
31
+ '--agents',
32
+ '--target-active-slots',
33
+ '--work-items',
34
+ '--minimum-work-items',
35
+ '--max-queue-expansion',
36
+ '--concurrency',
37
+ '--backend',
38
+ '--route',
39
+ '--mission',
40
+ '--mission-id',
41
+ '--profile',
42
+ '--write-mode',
43
+ '--max-write-agents',
44
+ '--service-tier',
45
+ '--zellij-session-name',
46
+ '--worker-placement',
47
+ '--zellij-visible-pane-cap',
31
48
  '--phase',
32
49
  '--message',
33
50
  '--role',
@@ -99,21 +99,16 @@ export const COMMANDS = {
99
99
  'codex-app': entry('beta', 'Check Codex App readiness', 'dist/commands/codex-app.js', directCommand(() => import('../commands/codex-app.js'), 'dist/commands/codex-app.js')),
100
100
  'codex-native': entry('beta', 'Inspect Codex Native broker and routing readiness', 'dist/commands/codex-native.js', directCommand(() => import('../commands/codex-native.js'), 'dist/commands/codex-native.js')),
101
101
  'codex-lb': entry('beta', 'Inspect codex-lb status and circuit health', 'dist/commands/codex-lb.js', directCommand(() => import('../commands/codex-lb.js'), 'dist/commands/codex-lb.js')),
102
- auth: entry('beta', 'Alias for codex-lb auth commands', 'dist/commands/codex-lb.js', directCommand(() => import('../commands/codex-lb.js'), 'dist/commands/codex-lb.js')),
103
102
  hooks: entry('beta', 'Explain and inspect Codex hooks', 'dist/commands/hooks.js', directCommand(() => import('../commands/hooks.js'), 'dist/commands/hooks.js')),
104
103
  tmux: entry('beta', 'Show removed-runtime migration notice', 'dist/commands/tmux.js', directCommand(() => import('../commands/tmux.js'), 'dist/commands/tmux.js')),
105
104
  'zellij-lane': entry('beta', 'Render a Zellij lane frame for SKS sessions', 'dist/commands/zellij-lane.js', directCommand(() => import('../commands/zellij-lane.js'), 'dist/commands/zellij-lane.js')),
106
105
  'zellij-slot-pane': entry('beta', 'Render a compact Zellij worker slot pane', 'dist/commands/zellij-slot-pane.js', directCommand(() => import('../commands/zellij-slot-pane.js'), 'dist/commands/zellij-slot-pane.js')),
107
106
  'zellij-slot-column-anchor': entry('beta', 'Render the compact SLOTS anchor pane for first-slot-down Zellij stacks', 'dist/commands/zellij-slot-column-anchor.js', directCommand(() => import('../commands/zellij-slot-column-anchor.js'), 'dist/commands/zellij-slot-column-anchor.js')),
108
107
  zellij: entry('beta', 'Inspect Zellij runtime status and explain repair (no auto-install)', 'dist/commands/zellij.js', directCommand(() => import('../commands/zellij.js'), 'dist/commands/zellij.js')),
109
- mad: entry('beta', 'MAD-SKS Zellij permission launcher', 'dist/commands/mad-sks.js', directCommand(() => import('../commands/mad-sks.js'), 'dist/commands/mad-sks.js')),
110
108
  'mad-sks': entry('beta', 'MAD-SKS scoped permission modifier', 'dist/commands/mad-sks.js', directCommand(() => import('../commands/mad-sks.js'), 'dist/commands/mad-sks.js')),
111
109
  'mad-db': entry('beta', 'Create or inspect one-cycle Mad-DB break-glass capability tokens', 'dist/commands/mad-db.js', directCommand(() => import('../commands/mad-db.js'), 'dist/commands/mad-db.js')),
112
110
  'auto-review': entry('beta', 'Manage auto-review profile', 'dist/commands/auto-review.js', directCommand(() => import('../commands/auto-review.js'), 'dist/commands/auto-review.js')),
113
- autoreview: entry('beta', 'Alias for auto-review', 'dist/commands/auto-review.js', directCommand(() => import('../commands/auto-review.js'), 'dist/commands/auto-review.js')),
114
111
  'dollar-commands': entry('stable', 'List Codex App dollar commands', 'dist/core/commands/basic-cli.js', basicArgs('dollarCommandsCommand')),
115
- dollars: entry('stable', 'Alias for dollar-commands', 'dist/core/commands/basic-cli.js', basicArgs('dollarCommandsCommand')),
116
- '$': entry('stable', 'Alias for dollar-commands', 'dist/core/commands/basic-cli.js', basicArgs('dollarCommandsCommand')),
117
112
  'fast-mode': entry('stable', 'Toggle SKS Fast mode default for dollar-command routes', 'dist/core/commands/fast-mode-command.js', argsCommand(() => import('../core/commands/fast-mode-command.js'), 'fastModeCommand', 'dist/core/commands/fast-mode-command.js')),
118
113
  commit: entry('stable', 'Create a simple git commit', 'dist/commands/commit.js', directCommand(() => import('../commands/commit.js'), 'dist/commands/commit.js')),
119
114
  'commit-and-push': entry('stable', 'Create a simple git commit and push', 'dist/commands/commit-and-push.js', directCommand(() => import('../commands/commit-and-push.js'), 'dist/commands/commit-and-push.js')),
@@ -128,14 +123,9 @@ export const COMMANDS = {
128
123
  autoresearch: entry('labs', 'Alias for research/autoresearch route', 'dist/core/commands/autoresearch-command.js', subcommand(() => import('../core/commands/autoresearch-command.js'), 'autoresearchCommand', 'dist/core/commands/autoresearch-command.js', 'status')),
129
124
  ppt: entry('labs', 'Inspect/build PPT artifacts', 'dist/core/commands/ppt-command.js', commandArgsCommand(() => import('../core/commands/ppt-command.js'), 'pptCommand', 'dist/core/commands/ppt-command.js')),
130
125
  'image-ux-review': entry('labs', 'Inspect image UX artifacts', 'dist/core/commands/image-ux-review-command.js', commandArgsCommand(() => import('../core/commands/image-ux-review-command.js'), 'imageUxReviewCommand', 'dist/core/commands/image-ux-review-command.js')),
131
- 'ux-review': entry('labs', 'Alias for image UX review', 'dist/core/commands/image-ux-review-command.js', commandArgsCommand(() => import('../core/commands/image-ux-review-command.js'), 'imageUxReviewCommand', 'dist/core/commands/image-ux-review-command.js')),
132
- 'visual-review': entry('labs', 'Alias for image UX review', 'dist/core/commands/image-ux-review-command.js', commandArgsCommand(() => import('../core/commands/image-ux-review-command.js'), 'imageUxReviewCommand', 'dist/core/commands/image-ux-review-command.js')),
133
- 'ui-ux-review': entry('labs', 'Alias for image UX review', 'dist/core/commands/image-ux-review-command.js', commandArgsCommand(() => import('../core/commands/image-ux-review-command.js'), 'imageUxReviewCommand', 'dist/core/commands/image-ux-review-command.js')),
134
126
  'computer-use': entry('beta', 'Record native Mac/non-web Computer Use visual evidence', 'dist/core/commands/computer-use-command.js', commandArgsCommand(() => import('../core/commands/computer-use-command.js'), 'computerUseCommand', 'dist/core/commands/computer-use-command.js')),
135
- cu: entry('beta', 'Alias for native Computer Use', 'dist/core/commands/computer-use-command.js', commandArgsCommand(() => import('../core/commands/computer-use-command.js'), 'computerUseCommand', 'dist/core/commands/computer-use-command.js')),
136
127
  context7: entry('beta', 'Context7 checks and docs', 'dist/cli/context7-command.js', subcommand(() => import('./context7-command.js'), 'context7Command', 'dist/cli/context7-command.js', 'check')),
137
128
  xai: entry('beta', 'Set up and check xAI/Grok search MCP integration', 'dist/cli/xai-command.js', subcommand(() => import('./xai-command.js'), 'xaiCommand', 'dist/cli/xai-command.js', 'check')),
138
- grok: entry('beta', 'Alias for xAI/Grok search setup', 'dist/cli/xai-command.js', subcommand(() => import('./xai-command.js'), 'xaiCommand', 'dist/cli/xai-command.js', 'check')),
139
129
  recallpulse: entry('labs', 'RecallPulse evidence route', 'dist/commands/recallpulse.js', directCommand(() => import('../commands/recallpulse.js'), 'dist/commands/recallpulse.js')),
140
130
  pipeline: entry('beta', 'Inspect pipeline missions', 'dist/commands/pipeline.js', directCommand(() => import('../commands/pipeline.js'), 'dist/commands/pipeline.js')),
141
131
  guard: entry('beta', 'Check harness guard', 'dist/commands/guard.js', directCommand(() => import('../commands/guard.js'), 'dist/commands/guard.js')),
@@ -156,7 +146,6 @@ export const COMMANDS = {
156
146
  'skill-dream': entry('labs', 'Track skill dream counters', 'dist/core/commands/skill-dream-command.js', subcommand(() => import('../core/commands/skill-dream-command.js'), 'skillDreamCommand', 'dist/core/commands/skill-dream-command.js', 'status')),
157
147
  'code-structure': entry('labs', 'Scan source structure', 'dist/core/commands/code-structure-command.js', subcommand(() => import('../core/commands/code-structure-command.js'), 'codeStructureCommand', 'dist/core/commands/code-structure-command.js', 'scan')),
158
148
  rust: entry('beta', 'Inspect optional Rust accelerator status and smoke parity', 'dist/commands/rust.js', directCommand(() => import('../commands/rust.js'), 'dist/commands/rust.js')),
159
- memory: entry('labs', 'Run retention checks', 'dist/core/commands/gc-command.js', subcommand(() => import('../core/commands/gc-command.js'), 'memoryCommand', 'dist/core/commands/gc-command.js')),
160
149
  gx: entry('labs', 'Render/validate GX cartridges', 'dist/core/commands/gx-command.js', subcommand(() => import('../core/commands/gx-command.js'), 'gxCommand', 'dist/core/commands/gx-command.js', 'validate')),
161
150
  db: entry('beta', 'Inspect DB safety policy', 'dist/core/commands/db-command.js', subcommand(() => import('../core/commands/db-command.js'), 'dbCommand', 'dist/core/commands/db-command.js', 'policy')),
162
151
  eval: entry('labs', 'Run eval reports', 'dist/core/commands/eval-command.js', subcommand(() => import('../core/commands/eval-command.js'), 'evalCommand', 'dist/core/commands/eval-command.js', 'run')),
@@ -170,13 +159,27 @@ export const COMMANDS = {
170
159
  bench: entry('beta', 'Run core trust-kernel benchmark budgets', 'dist/core/commands/bench-command.js', argsCommand(() => import('../core/commands/bench-command.js'), 'benchCommand', 'dist/core/commands/bench-command.js'))
171
160
  };
172
161
  export const TYPED_COMMANDS = COMMANDS;
162
+ export const LEGACY_COMMAND_ALIASES = {
163
+ auth: 'codex-lb',
164
+ mad: 'mad-sks',
165
+ autoreview: 'auto-review',
166
+ dollars: 'dollar-commands',
167
+ '$': 'dollar-commands',
168
+ 'ux-review': 'image-ux-review',
169
+ 'visual-review': 'image-ux-review',
170
+ 'ui-ux-review': 'image-ux-review',
171
+ cu: 'computer-use',
172
+ grok: 'xai',
173
+ memory: 'gc'
174
+ };
173
175
  export const COMMAND_ALIASES = {
176
+ ...LEGACY_COMMAND_ALIASES,
174
177
  '--help': 'help',
175
178
  '-h': 'help',
176
179
  '--version': 'version',
177
180
  '-v': 'version',
178
- '--mad': 'mad',
179
- '--MAD': 'mad',
181
+ '--mad': 'mad-sks',
182
+ '--MAD': 'mad-sks',
180
183
  '--mad-sks': 'mad-sks',
181
184
  '--agent': 'agent',
182
185
  '--naruto': 'naruto'
@@ -5,17 +5,20 @@ export function isCommandName(value) {
5
5
  export function normalizeCommand(args = []) {
6
6
  const cmd = args[0];
7
7
  if (!cmd)
8
- return { command: null, args: [...args] };
9
- let mapped = cmd in COMMAND_ALIASES ? COMMAND_ALIASES[cmd] : cmd;
8
+ return { command: null, rawCommand: null, aliasTarget: null, args: [...args] };
9
+ const mapped = cmd in COMMAND_ALIASES ? COMMAND_ALIASES[cmd] : cmd;
10
10
  const rest = args.slice(1);
11
+ const command = isCommandName(mapped) ? mapped : null;
11
12
  return {
12
- command: isCommandName(mapped) ? mapped : null,
13
+ command,
14
+ rawCommand: cmd,
15
+ aliasTarget: command && mapped !== cmd ? command : null,
13
16
  args: rest,
14
17
  };
15
18
  }
16
19
  export async function dispatch(args) {
17
20
  const argv = args ?? process.argv.slice(2);
18
- const { command, args: rest } = normalizeCommand(argv);
21
+ const { command, rawCommand, args: rest } = normalizeCommand(argv);
19
22
  if (!command) {
20
23
  if (!argv.length) {
21
24
  const mod = await import('../commands/doctor.js');
@@ -36,6 +39,6 @@ export async function dispatch(args) {
36
39
  const mod = await entry.lazy();
37
40
  if (typeof mod.run !== 'function')
38
41
  throw new Error(`Command ${command} must export run(command, args)`);
39
- return mod.run(command, rest);
42
+ return mod.run(rawCommand || command, rest);
40
43
  }
41
44
  //# sourceMappingURL=router.js.map
@@ -28,6 +28,7 @@ import { buildCodexAppHarnessMatrix } from '../core/codex-app/codex-app-harness-
28
28
  import { buildCodexNativeFeatureMatrix } from '../core/codex-native/codex-native-feature-broker.js';
29
29
  import { repairCodexNativeManagedAssets } from '../core/codex-native/codex-native-repair-transaction.js';
30
30
  import { runDoctorNativeCapabilityRepair } from '../core/doctor/doctor-native-capability-repair.js';
31
+ import { runDoctorCommandAliasCleanup } from '../core/doctor/command-alias-cleanup.js';
31
32
  export async function run(_command, args = []) {
32
33
  const doctorFix = flag(args, '--fix');
33
34
  let setupRepair = null;
@@ -70,6 +71,23 @@ export async function run(_command, args = []) {
70
71
  };
71
72
  }
72
73
  const root = await projectRoot();
74
+ const commandAliasCleanup = await runDoctorCommandAliasCleanup({
75
+ root,
76
+ fix: doctorFix
77
+ }).catch((err) => ({
78
+ schema: 'sks.command-alias-cleanup.v1',
79
+ ok: false,
80
+ status: 'blocked',
81
+ root,
82
+ fix: doctorFix,
83
+ report_path: `${root}/.sneakoscope/reports/command-alias-cleanup.json`,
84
+ canonical_command_count: 0,
85
+ legacy_alias_count: 0,
86
+ aliases: [],
87
+ detected: { registered_alias_commands: [], catalog_alias_rows: [], missing_canonical_targets: [] },
88
+ actions: [],
89
+ blockers: [err?.message || String(err)]
90
+ }));
73
91
  const doctorNativeCapabilityRepair = await runDoctorNativeCapabilityRepair({
74
92
  root,
75
93
  fix: doctorFix || flag(args, '--repair-native-capabilities'),
@@ -287,7 +305,7 @@ export async function run(_command, args = []) {
287
305
  const runtimeReadiness = buildRuntimeReadiness(zellijReadiness, codexNativeFeatureMatrix);
288
306
  const result = {
289
307
  schema: 'sks.doctor-status.v1',
290
- ok: ready.ready && (!sksUpdate || sksUpdate.ok !== false),
308
+ ok: ready.ready && (!sksUpdate || sksUpdate.ok !== false) && commandAliasCleanup.ok !== false,
291
309
  root,
292
310
  node: { ok: Number(process.versions.node.split('.')[0]) >= 20, version: process.version },
293
311
  codex,
@@ -305,6 +323,7 @@ export async function run(_command, args = []) {
305
323
  agent_role_config: agentRoleConfigRepair,
306
324
  zellij_readiness: zellijReadiness,
307
325
  codex_permission_profiles: permissionProfiles,
326
+ command_aliases: commandAliasCleanup,
308
327
  imagegen: {
309
328
  ok: imagegen.auth_readiness?.available_paths?.length > 0,
310
329
  auth_readiness: imagegen.auth_readiness || null,
@@ -323,7 +342,7 @@ export async function run(_command, args = []) {
323
342
  ready,
324
343
  sneakoscope: { ok: await exists(`${root}/.sneakoscope`) },
325
344
  package: { bytes: pkgBytes, human: formatBytes(pkgBytes) },
326
- repair: { sks_update: sksUpdate, setup: setupRepair, codex_config: configRepair, migration_journal: migrationJournal, global_sks_installs: globalSksInstallCleanup, agent_role_config: agentRoleConfigRepair, zellij: zellijRepair, codex_native: codexNativeRepair, doctor_native_capability: doctorNativeCapabilityRepair }
345
+ repair: { sks_update: sksUpdate, setup: setupRepair, codex_config: configRepair, migration_journal: migrationJournal, global_sks_installs: globalSksInstallCleanup, agent_role_config: agentRoleConfigRepair, zellij: zellijRepair, codex_native: codexNativeRepair, doctor_native_capability: doctorNativeCapabilityRepair, command_aliases: commandAliasCleanup }
327
346
  };
328
347
  if (flag(args, '--json')) {
329
348
  printJson(result);
@@ -381,6 +400,12 @@ export async function run(_command, args = []) {
381
400
  console.log('SKS Skills:');
382
401
  console.log(` core skills: ${doctorSkillStatus(doctorNativeCapabilityRepair?.core_skills)}`);
383
402
  console.log(` duplicate project skills: ${doctorDedupeStatus(doctorNativeCapabilityRepair?.skill_dedupe)}`);
403
+ console.log('SKS Command Aliases:');
404
+ console.log(` status: ${commandAliasCleanup.status || (commandAliasCleanup.ok ? 'clean' : 'blocked')}`);
405
+ console.log(` canonical commands: ${commandAliasCleanup.canonical_command_count ?? 0}`);
406
+ console.log(` compatibility aliases: ${commandAliasCleanup.legacy_alias_count ?? 0}`);
407
+ if (commandAliasCleanup.report_path)
408
+ console.log(` report: ${commandAliasCleanup.report_path}`);
384
409
  console.log('Secret preservation:');
385
410
  console.log(` Supabase keys: ${doctorNativeCapabilityRepair?.ok === false && String((doctorNativeCapabilityRepair?.blockers || []).join(' ')).includes('secret_preservation_failed') ? 'blocked' : 'preserved'}`);
386
411
  console.log(' secret values: redacted');
@@ -1,6 +1,6 @@
1
1
  import path from 'node:path';
2
2
  import { spawnSync } from 'node:child_process';
3
- import { COMMANDS } from '../../cli/command-registry.js';
3
+ import { COMMANDS, LEGACY_COMMAND_ALIASES } from '../../cli/command-registry.js';
4
4
  import { flag } from '../../cli/args.js';
5
5
  import { printJson, sksTextLogo } from '../../cli/output.js';
6
6
  import { PACKAGE_VERSION, ensureDir, exists, nowIso, projectRoot, readJson, sksRoot, tmpdir, writeJsonAtomic } from '../fsx.js';
@@ -55,6 +55,9 @@ export function dollarCommandsCommand(args = []) {
55
55
  export function aliasesCommand() {
56
56
  console.log('Aliases');
57
57
  console.log('- sks, sneakoscope');
58
+ console.log('- CLI compatibility aliases:');
59
+ for (const [alias, canonical] of Object.entries(LEGACY_COMMAND_ALIASES))
60
+ console.log(` sks ${alias} -> sks ${canonical}`);
58
61
  console.log('- $ aliases:');
59
62
  for (const entry of DOLLAR_COMMAND_ALIASES)
60
63
  console.log(` ${entry.app_skill} -> ${entry.canonical}`);
@@ -30,6 +30,7 @@ export async function madHighCommand(args = [], deps = {}) {
30
30
  return madSksSubcommand(subcommand, args.filter((arg) => String(arg) !== subcommand));
31
31
  const cleanArgs = stripMadLaunchOnlyArgs(args);
32
32
  const rawArgs = (args || []).map((arg) => String(arg));
33
+ const madDbGrant = resolveMadLaunchMadDbGrant(rawArgs);
33
34
  const dryRun = rawArgs.includes('--dry-run');
34
35
  if (args.includes('--json') && !dryRun) {
35
36
  const profile = buildMadHighLaunchProfileNoWrite();
@@ -133,13 +134,6 @@ export async function madHighCommand(args = [], deps = {}) {
133
134
  // later when the Zellij session opens. All filesystem/permission/EPERM/symlink/ACL
134
135
  // readability + repair checks still run. SKS_LAUNCH_FULL_CODEX_PROBE=1 restores the
135
136
  // old behavior.
136
- const madDbRequested = rawArgs.includes('--mad-db');
137
- const madDbAck = readOption(rawArgs, '--ack', '');
138
- if (madDbRequested && madDbAck !== MAD_DB_ACK) {
139
- console.error(`SKS MAD-DB launch blocked. Required --ack ${JSON.stringify(MAD_DB_ACK)}`);
140
- process.exitCode = 2;
141
- return { ok: false, status: 'blocked', reason: 'mad_db_ack_phrase_required', required_ack: MAD_DB_ACK };
142
- }
143
137
  const allowMadRepair = rawArgs.includes('--repair-config') || rawArgs.includes('--fix') || rawArgs.includes('--yes-repair');
144
138
  const launchPreflight = await runCodexLaunchPreflight(launchRoot, { fix: allowMadRepair, launchFast: process.env.SKS_LAUNCH_FULL_CODEX_PROBE !== '1', profile: profile.profile_name, sandbox: 'danger-full-access', serviceTier: 'fast' });
145
139
  const afterPreflightUi = beforeUi ? await writeCodexAppUiSnapshot(launchRoot, `mad-after-preflight-${uiSnapshotId}`).catch(() => null) : null;
@@ -160,18 +154,38 @@ export async function madHighCommand(args = [], deps = {}) {
160
154
  return launchPreflight;
161
155
  }
162
156
  const madLaunch = await activateMadZellijPermissionState(process.cwd(), args);
163
- const madDbCapability = madDbRequested
164
- ? await createMadDbCapability(madLaunch.root, { missionId: madLaunch.mission_id, ack: madDbAck, cwd: process.cwd() })
157
+ const madDbCapability = madDbGrant.enabled
158
+ ? await createMadDbCapability(madLaunch.root, { missionId: madLaunch.mission_id, ack: madDbGrant.ack, cwd: process.cwd() })
165
159
  : null;
166
160
  if (madDbCapability) {
161
+ const grantReport = {
162
+ schema: 'sks.mad-sks-launch-grants.v1',
163
+ generated_at: nowIso(),
164
+ mission_id: madLaunch.mission_id,
165
+ mad_sks_active: true,
166
+ mad_db_active: true,
167
+ mad_db_default_grant: madDbGrant.source === 'sks_mad_default',
168
+ mad_db_grant_source: madDbGrant.source,
169
+ mad_db_one_cycle_only: true,
170
+ mad_db_capability_file: 'mad-db-capability.json',
171
+ mad_db_cycle_id: madDbCapability.cycle_id,
172
+ mad_db_expires_at: madDbCapability.expires_at,
173
+ standalone_mad_db_enable_still_requires_ack: true
174
+ };
175
+ await writeJsonAtomic(path.join(madLaunch.dir, 'mad-sks-launch-grants.json'), grantReport);
167
176
  await setCurrent(madLaunch.root, {
168
177
  mission_id: madLaunch.mission_id,
169
178
  mad_db_active: true,
170
179
  mad_db_cycle_id: madDbCapability.cycle_id,
171
180
  mad_db_capability_file: 'mad-db-capability.json',
172
- mad_db_break_glass: true
181
+ mad_db_break_glass: true,
182
+ mad_db_default_grant: madDbGrant.source === 'sks_mad_default',
183
+ mad_db_grant_source: madDbGrant.source,
184
+ mad_db_one_cycle_only: true,
185
+ mad_db_priority_override_active: true,
186
+ mad_sks_launch_grants_file: 'mad-sks-launch-grants.json'
173
187
  });
174
- await appendJsonlBounded(path.join(madLaunch.dir, 'events.jsonl'), { ts: nowIso(), type: 'mad_db.capability_created', cycle_id: madDbCapability.cycle_id, expires_at: madDbCapability.expires_at });
188
+ await appendJsonlBounded(path.join(madLaunch.dir, 'events.jsonl'), { ts: nowIso(), type: 'mad_db.capability_created', grant_source: madDbGrant.source, cycle_id: madDbCapability.cycle_id, expires_at: madDbCapability.expires_at });
175
189
  }
176
190
  const updateNotice = await checkSksUpdateNotice({
177
191
  packageName: deps.packageName || 'sneakoscope',
@@ -192,10 +206,10 @@ export async function madHighCommand(args = [], deps = {}) {
192
206
  await appendJsonlBounded(path.join(madLaunch.dir, 'events.jsonl'), { ts: nowIso(), type: 'mad_sks.update_notice_checked', non_blocking: true, update_available: updateNotice.update_available === true, source: updateNotice.source });
193
207
  console.log(`SKS MAD ready: ${madHighProfileName()} | gate ${madLaunch.mission_id}`);
194
208
  if (madDbCapability)
195
- console.log(`MAD-DB one-cycle capability active; expires ${madDbCapability.expires_at}.`);
209
+ console.log(`MAD-DB one-cycle capability active (${madDbGrant.source}); expires ${madDbCapability.expires_at}.`);
196
210
  if (updateNotice.update_available === true)
197
211
  console.log(`SKS update notice: ${updateNotice.latest_version} available (non-blocking).`);
198
- console.log('Scoped high-power maintenance authority active; add explicit --allow-* flags for packages, services, network, browser/Computer Use, generated assets, file permissions, DB writes, or system/admin scopes. Catastrophic guards remain.');
212
+ console.log('Scoped high-power maintenance authority active; add explicit --allow-* flags for packages, services, network, browser/Computer Use, generated assets, file permissions, or system/admin scopes. MAD-DB one-cycle DB break-glass is already active for this launch; protected-core, audit, and one-cycle bounds remain.');
199
213
  const launchLb = lb.status === 'present' ? { ...lb, status: 'configured' } : lb;
200
214
  const madSksEnv = {
201
215
  SKS_PROTECTED_CORE_POLICY: madLaunch.gate.protected_core_policy,
@@ -260,6 +274,15 @@ export async function madHighCommand(args = [], deps = {}) {
260
274
  console.log('MAD launch running headless: live_panes=false.');
261
275
  return launch;
262
276
  }
277
+ export function resolveMadLaunchMadDbGrant(args = []) {
278
+ const list = (args || []).map((arg) => String(arg));
279
+ return {
280
+ enabled: true,
281
+ source: list.includes('--mad-db') ? 'sks_mad_explicit_redundant_flag' : 'sks_mad_default',
282
+ ack: MAD_DB_ACK,
283
+ one_cycle_only: true
284
+ };
285
+ }
263
286
  export async function startMadNativeSwarm(root, madLaunch, args = [], profile = {}, opts = {}) {
264
287
  const swarm = resolveMadNativeSwarmOptions(args, profile, opts);
265
288
  const dir = madLaunch.dir || missionDirLike(root, madLaunch.mission_id);
@@ -114,6 +114,7 @@ async function narutoRun(parsed) {
114
114
  prompt: parsed.prompt,
115
115
  requestedClones: roster.agent_count,
116
116
  totalWorkItems: parsed.workItems,
117
+ honorExplicitTotalWorkItems: parsed.workItemsExplicit,
117
118
  readonly: parsed.readonly,
118
119
  writeCapable,
119
120
  leaseBasePath: patchEnvelopeBasePath,
@@ -127,6 +128,7 @@ async function narutoRun(parsed) {
127
128
  prompt: parsed.prompt,
128
129
  requestedClones: roster.agent_count,
129
130
  totalWorkItems: parsed.workItems,
131
+ honorExplicitTotalWorkItems: parsed.workItemsExplicit,
130
132
  readonly: parsed.readonly,
131
133
  writeCapable,
132
134
  leaseBasePath: patchEnvelopeBasePath,
@@ -791,6 +793,7 @@ function parseNarutoArgs(args = []) {
791
793
  const json = hasFlag(args, '--json');
792
794
  const requestedClones = Number(readOption(args, '--clones', readOption(args, '--agents', DEFAULT_NARUTO_CLONES)));
793
795
  const clones = clampClones(requestedClones);
796
+ const workItemsExplicit = hasOption(args, '--work-items');
794
797
  const workItems = clampWorkItems(Number(readOption(args, '--work-items', clones * 2)), clones);
795
798
  const concurrency = normalizeConcurrency(readOption(args, '--concurrency', readOption(args, '--target-active-slots', null)), clones);
796
799
  const useOllama = hasFlag(args, '--ollama') || hasFlag(args, '--local-model');
@@ -831,7 +834,7 @@ function parseNarutoArgs(args = []) {
831
834
  const messages = normalizeMessages(readOption(args, '--messages', '8'));
832
835
  const valueFlags = new Set(['--clones', '--agents', '--work-items', '--concurrency', '--target-active-slots', '--backend', '--write-mode', '--max-write-agents', '--service-tier', '--mission', '--mission-id', '--ollama-model', '--local-model-model', '--ollama-base-url', '--local-model-base-url', '--parallelism', '--messages']);
833
836
  const prompt = positionalArgs(rest, valueFlags).join(' ').trim() || 'Naruto shadow clone swarm run';
834
- return { action, prompt, clones, workItems, concurrency, backend, backendExplicit, mock, real, readonly, ollamaEnabled: useOllama && !noOllama, noOllama, ollamaModel, ollamaBaseUrl, writeMode, applyPatches, dryRunPatches, maxWriteAgents, fastMode, serviceTier, noFast, json, missionId, noOpenZellij, attach, smoke, parallelism, messages };
837
+ return { action, prompt, clones, workItems, workItemsExplicit, concurrency, backend, backendExplicit, mock, real, readonly, ollamaEnabled: useOllama && !noOllama, noOllama, ollamaModel, ollamaBaseUrl, writeMode, applyPatches, dryRunPatches, maxWriteAgents, fastMode, serviceTier, noFast, json, missionId, noOpenZellij, attach, smoke, parallelism, messages };
835
838
  }
836
839
  function normalizeParallelism(value) {
837
840
  const text = String(value || 'extreme').toLowerCase();
@@ -1,7 +1,7 @@
1
1
  import path from 'node:path';
2
- import { projectRoot, readJson, writeJsonAtomic } from '../fsx.js';
2
+ import { projectRoot, readJson } from '../fsx.js';
3
3
  import { missionDir, stateFile } from '../mission.js';
4
- import { buildPipelinePlan, PIPELINE_PLAN_ARTIFACT, projectGateStatus } from '../pipeline.js';
4
+ import { PIPELINE_PLAN_ARTIFACT, projectGateStatus, writePipelinePlan } from '../pipeline.js';
5
5
  import { routePrompt } from '../routes.js';
6
6
  import { flag, positionalArgs, readFlagValue, resolveMissionId } from './command-utils.js';
7
7
  export async function pipelineCommand(args = []) {
@@ -33,14 +33,13 @@ export async function pipelineCommand(args = []) {
33
33
  force: flag(args, '--force-agents'),
34
34
  noAgents: flag(args, '--no-agents')
35
35
  };
36
- const plan = buildPipelinePlan({
36
+ const plan = await writePipelinePlan(dir, {
37
37
  missionId: id,
38
38
  route,
39
39
  task: routeContext.task || mission.prompt || state.prompt || '',
40
40
  required: Boolean(routeContext.context7_required || state.context7_required),
41
41
  agents
42
42
  });
43
- await writeJsonAtomic(path.join(dir, PIPELINE_PLAN_ARTIFACT), plan);
44
43
  if (flag(args, '--json'))
45
44
  return console.log(JSON.stringify({ schema: 'sks.pipeline-plan.v1', ok: true, mission_id: id, plan }, null, 2));
46
45
  console.log(`Pipeline plan written: .sneakoscope/missions/${id}/${PIPELINE_PLAN_ARTIFACT}`);
@@ -5,7 +5,7 @@ import { getCodexInfo, runCodexExec } from '../codex-adapter.js';
5
5
  import { createMission, loadMission, setCurrent, stateFile } from '../mission.js';
6
6
  import { writeQuestions } from '../questions.js';
7
7
  import { sealContract } from '../decision-contract.js';
8
- import { buildQaLoopQuestionSchema, buildQaLoopPrompt, evaluateQaGate, qaStatus, qaUiRequired, writeMockQaResult, writeQaLoopArtifacts, writeQaNativeAgentLedger } from '../qa-loop.js';
8
+ import { buildQaLoopQuestionSchema, buildQaLoopPrompt, ensureQaLoopVisualEvidenceContract, evaluateQaGate, qaGptImage2AnnotatedReviewRequired, qaStatus, qaUiRequired, writeMockQaResult, writeQaLoopArtifacts, writeQaNativeAgentLedger } from '../qa-loop.js';
9
9
  import { containsUserQuestion, noQuestionContinuationReason } from '../no-question-guard.js';
10
10
  import { ROUTES, routePrompt, stripVisibleDecisionAnswerBlocks } from '../routes.js';
11
11
  import { codexChromeExtensionStatus } from '../codex-app.js';
@@ -124,6 +124,8 @@ async function qaLoopRun(args) {
124
124
  const contract = await readJson(contractPath, {});
125
125
  if (!(await exists(path.join(dir, 'qa-ledger.json'))))
126
126
  await writeQaLoopArtifacts(dir, mission, contract);
127
+ else
128
+ await ensureQaLoopVisualEvidenceContract(dir, mission, contract);
127
129
  const safetyScan = await scanDbSafety(root);
128
130
  if (!safetyScan.ok) {
129
131
  console.error('QA-LOOP cannot run: SKS safety scan found unsafe project data-tool configuration.');
@@ -148,6 +150,7 @@ async function qaLoopRun(args) {
148
150
  const reportFile = qaGate.qa_report_file;
149
151
  const executionProfile = await readJson(path.join(dir, 'qa-loop', 'execution-profile.json'), null);
150
152
  const uiRequired = qaUiRequired(contract.answers || {});
153
+ const gptImage2ReviewRequired = qaGptImage2AnnotatedReviewRequired(contract, mission.prompt);
151
154
  const capabilityArtifact = await writeCodex0138CapabilityArtifacts(root, { missionId: id }).catch((err) => ({ error: err?.message || String(err), report: null }));
152
155
  const usageArtifact = await writeCodexAccountUsageArtifacts(root, { missionId: id }).catch((err) => ({ error: err?.message || String(err), snapshot: null }));
153
156
  const budgetPolicy = buildQaLoopBudgetPolicy({ usage: usageArtifact?.snapshot || null, provider: 'codex-sdk' });
@@ -253,6 +256,16 @@ async function qaLoopRun(args) {
253
256
  ui_chrome_extension_evidence: false,
254
257
  ui_computer_use_evidence: false,
255
258
  ui_evidence_source: 'blocked_chrome_extension_setup_required',
259
+ ui_chrome_extension_screenshot_required: true,
260
+ ui_chrome_extension_screenshot_captured: false,
261
+ ui_chrome_extension_screenshot_artifact: null,
262
+ ui_chrome_extension_screenshot_sha256: null,
263
+ gpt_image_2_annotated_review_required: gptImage2ReviewRequired,
264
+ gpt_image_2_annotated_review_generated: false,
265
+ gpt_image_2_annotated_review_artifact: null,
266
+ gpt_image_2_annotated_review_sha256: null,
267
+ gpt_image_2_annotated_review_model: gptImage2ReviewRequired ? null : 'not_required',
268
+ gpt_image_2_annotated_review_provider: gptImage2ReviewRequired ? null : 'not_required',
256
269
  blocker: 'codex_chrome_extension_setup_required',
257
270
  blockers: Array.from(new Set([...(qaGate.blockers || []), 'codex_chrome_extension_setup_required', ...(chrome.blockers || [])])),
258
271
  evidence: [...(qaGate.evidence || []), 'Codex Chrome Extension preflight failed before web QA execution.'],
@@ -277,6 +290,28 @@ async function qaLoopRun(args) {
277
290
  const nativeAgentRun = await runNativeAgentOrchestrator({ root, missionId: id, route: '$QA-LOOP', prompt: mission.prompt || 'QA-LOOP run', backend: mock ? 'fake' : 'codex-sdk', mock, agents: requestedAgents, targetActiveSlots, desiredWorkItemCount, minimumWorkItems, maxQueueExpansion, concurrency: Math.min(requestedAgents, 5), readonly: !(applyPatches && writeMode !== 'off'), profile, writeMode: writeMode, applyPatches, dryRunPatches, maxWriteAgents, roster: nativeRoster, routeCommand: 'sks qa-loop run', routeBlackboxKind: 'actual_qa_command', env: { SKS_CODEX_APP_EXECUTION_PROFILE: executionProfile?.mode || 'unknown', SKS_CODEX_AGENT_ROLE_STRATEGY: executionProfile?.agent_role_strategy || 'message-role' } });
278
291
  await writeJsonAtomic(path.join(dir, 'qa-native-agent-run.json'), nativeAgentRun);
279
292
  await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'qaloop.native_agents.completed', backend: nativeAgentRun.backend, ok: nativeAgentRun.ok, proof: nativeAgentRun.proof?.status });
293
+ if (flag(args, '--native-proof-only')) {
294
+ const proofOnlyGate = {
295
+ schema: 'sks.qa-native-proof-only-gate.v1',
296
+ ok: nativeAgentRun.proof?.ok === true,
297
+ native_agent_proof: nativeAgentRun.proof?.ok === true,
298
+ proof_status: nativeAgentRun.proof?.status || null,
299
+ blockers: nativeAgentRun.proof?.blockers || []
300
+ };
301
+ if (flag(args, '--json'))
302
+ return console.log(JSON.stringify({
303
+ schema: 'sks.qa-loop-run.v1',
304
+ ok: proofOnlyGate.ok,
305
+ status: proofOnlyGate.ok ? 'native_proof_ready' : 'blocked',
306
+ mission_id: id,
307
+ gate: proofOnlyGate,
308
+ proof: nativeAgentRun.proof,
309
+ native_agent_run: nativeAgentRun,
310
+ native_proof_only: true
311
+ }, null, 2));
312
+ console.log(`QA-LOOP native proof ready: ${id}`);
313
+ return;
314
+ }
280
315
  if (mock) {
281
316
  let gate = await writeMockQaResult(dir, mission, contract);
282
317
  const needsVisual = uiRequired;
@@ -40,6 +40,38 @@ export async function researchCommand(sub, args = []) {
40
40
  export async function autoresearchCommand(sub, args = []) {
41
41
  return researchCommand(sub || 'status', args);
42
42
  }
43
+ function hasFlagOption(args = [], name) {
44
+ return args.includes(name) || args.some((arg) => String(arg).startsWith(`${name}=`));
45
+ }
46
+ function limitResearchNativeWorkGraph(graph, limit) {
47
+ const count = Math.max(1, Math.min(Number(graph?.work_items?.length || 0) || 1, Math.floor(Number(limit) || 1)));
48
+ const workItems = (graph.work_items || []).slice(0, count).map((item) => {
49
+ const selectedIds = new Set((graph.work_items || []).slice(0, count).map((row) => String(row.id || '')));
50
+ return {
51
+ ...item,
52
+ dependencies: (item.dependencies || []).map(String).filter((id) => selectedIds.has(id)),
53
+ can_run_in_parallel_with: (item.can_run_in_parallel_with || []).map(String).filter((id) => selectedIds.has(id))
54
+ };
55
+ });
56
+ const selectedIds = new Set(workItems.map((item) => String(item.id || '')));
57
+ const activeWaves = (graph.active_waves || [])
58
+ .map((wave) => ({
59
+ ...wave,
60
+ work_item_ids: (wave.work_item_ids || []).map(String).filter((id) => selectedIds.has(id)),
61
+ write_paths: (wave.write_paths || []).map(String),
62
+ conflict_count: Number(wave.conflict_count || 0)
63
+ }))
64
+ .filter((wave) => wave.work_item_ids.length > 0);
65
+ return {
66
+ ...graph,
67
+ requested_clones: Math.min(Number(graph.requested_clones || count), count),
68
+ total_work_items: workItems.length,
69
+ work_items: workItems,
70
+ active_waves: activeWaves,
71
+ mixed_work_kinds: [...new Set(workItems.map((item) => item.kind))],
72
+ write_allowed_count: workItems.filter((item) => item.write_allowed === true).length
73
+ };
74
+ }
43
75
  async function researchPrepare(args) {
44
76
  const root = await sksRoot();
45
77
  if (!(await exists(path.join(root, '.sneakoscope'))))
@@ -139,10 +171,16 @@ async function researchRun(args) {
139
171
  const mock = flag(args, '--mock');
140
172
  const researchWorkGraph = await writeResearchWorkGraph(dir, plan);
141
173
  const graphWorkItemCount = Math.max(1, Number(researchWorkGraph.total_work_items || researchWorkGraph.work_items?.length || 0));
174
+ const explicitWorkItems = hasFlagOption(args, '--work-items');
175
+ const effectiveDesiredWorkItemCount = explicitWorkItems ? desiredWorkItemCount : Math.max(desiredWorkItemCount, graphWorkItemCount);
176
+ const effectiveMinimumWorkItems = Math.min(effectiveDesiredWorkItemCount, explicitWorkItems ? minimumWorkItems : Math.max(minimumWorkItems, Math.min(graphWorkItemCount, targetActiveSlots)));
177
+ const nativeResearchWorkGraph = explicitWorkItems
178
+ ? limitResearchNativeWorkGraph(researchWorkGraph, effectiveDesiredWorkItemCount)
179
+ : researchWorkGraph;
142
180
  await runResearchCycle(dir, researchWorkGraph, { cycle: 0, status: mock ? 'mock_native_orchestrator_planned' : 'native_orchestrator_planned' });
143
181
  await setCurrent(root, { mission_id: id, mode: 'RESEARCH', phase: 'RESEARCH_RUNNING_NO_QUESTIONS', questions_allowed: false, implementation_allowed: false, research_real_run_required: !mock, research_cycle_timeout_minutes: cycleTimeoutMinutes });
144
182
  await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'research.run.started', maxCycles, mock, cycleTimeoutMinutes, real_run_required: !mock });
145
- const nativeAgentRun = await runNativeAgentOrchestrator({ root, missionId: id, route: flag(args, '--autoresearch') ? '$AutoResearch' : '$Research', prompt: mission.prompt || plan.prompt || 'Research run', backend: mock ? 'fake' : 'codex-sdk', mock, agents: requestedAgents, targetActiveSlots, desiredWorkItemCount: Math.max(desiredWorkItemCount, graphWorkItemCount), minimumWorkItems: Math.max(minimumWorkItems, Math.min(graphWorkItemCount, targetActiveSlots)), maxQueueExpansion, concurrency: Math.min(requestedAgents, 5), readonly: true, profile, writeMode: writeMode, applyPatches: false, dryRunPatches, maxWriteAgents, roster: plan.native_agent_plan, routeCommand: 'sks research run', routeBlackboxKind: 'actual_research_command', narutoWorkGraph: researchWorkGraph });
183
+ const nativeAgentRun = await runNativeAgentOrchestrator({ root, missionId: id, route: flag(args, '--autoresearch') ? '$AutoResearch' : '$Research', prompt: mission.prompt || plan.prompt || 'Research run', backend: mock ? 'fake' : 'codex-sdk', mock, agents: requestedAgents, targetActiveSlots, desiredWorkItemCount: effectiveDesiredWorkItemCount, minimumWorkItems: effectiveMinimumWorkItems, maxQueueExpansion, concurrency: Math.min(requestedAgents, 5), readonly: true, profile, writeMode: writeMode, applyPatches: false, dryRunPatches, maxWriteAgents, roster: plan.native_agent_plan, routeCommand: 'sks research run', routeBlackboxKind: 'actual_research_command', narutoWorkGraph: nativeResearchWorkGraph });
146
184
  await writeJsonAtomic(path.join(dir, 'research-native-agent-run.json'), nativeAgentRun);
147
185
  await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'research.native_agents.completed', backend: nativeAgentRun.backend, ok: nativeAgentRun.ok, proof: nativeAgentRun.proof?.status });
148
186
  if (!nativeAgentRun.ok) {
@@ -151,6 +189,28 @@ async function researchRun(args) {
151
189
  process.exitCode = 2;
152
190
  return;
153
191
  }
192
+ if (flag(args, '--native-proof-only')) {
193
+ const proofOnlyGate = {
194
+ schema: 'sks.research-native-proof-only-gate.v1',
195
+ ok: nativeAgentRun.proof?.ok === true,
196
+ native_agent_proof: nativeAgentRun.proof?.ok === true,
197
+ proof_status: nativeAgentRun.proof?.status || null,
198
+ blockers: nativeAgentRun.proof?.blockers || []
199
+ };
200
+ if (flag(args, '--json'))
201
+ return console.log(JSON.stringify({
202
+ schema: flag(args, '--autoresearch') ? 'sks.autoresearch-run.v1' : 'sks.research-run.v1',
203
+ ok: proofOnlyGate.ok,
204
+ mission_id: id,
205
+ gate: proofOnlyGate,
206
+ proof: nativeAgentRun.proof,
207
+ native_agent_run: nativeAgentRun,
208
+ research_work_graph: nativeResearchWorkGraph,
209
+ native_proof_only: true
210
+ }, null, 2));
211
+ console.log(`Research native proof ready: ${id}`);
212
+ return;
213
+ }
154
214
  const legacyResearchCycle = flag(args, '--legacy-research-cycle') || process.env.SKS_RESEARCH_LEGACY_CYCLE === '1';
155
215
  const sourceMutationBaseline = await researchCodeMutationSnapshot(root, id);
156
216
  if (!legacyResearchCycle) {