sneakoscope 6.0.1 → 6.0.2

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 (87) hide show
  1. package/README.md +2 -1
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/dist/bin/fast-inline.js +0 -60
  5. package/dist/bin/sks-dispatch.js +0 -24
  6. package/dist/cli/codex-app-command.js +1 -2
  7. package/dist/cli/help-fast.js +2 -2
  8. package/dist/cli/install-helpers.js +25 -31
  9. package/dist/config/skills-manifest.json +57 -57
  10. package/dist/core/agents/agent-effort-policy.js +19 -38
  11. package/dist/core/agents/agent-orchestrator.js +2 -2
  12. package/dist/core/agents/agent-role-config.js +17 -10
  13. package/dist/core/agents/agent-roster.js +19 -39
  14. package/dist/core/agents/agent-scheduler.js +6 -5
  15. package/dist/core/agents/agent-schema.js +4 -5
  16. package/dist/core/agents/agent-task-graph.js +1 -1
  17. package/dist/core/agents/agent-work-partition.js +2 -1
  18. package/dist/core/agents/native-worker-backend-router.js +1 -1
  19. package/dist/core/agents/route-collaboration-ledger.js +2 -2
  20. package/dist/core/auto-review.js +1 -4
  21. package/dist/core/codex/codex-config-guard.js +9 -13
  22. package/dist/core/codex-adapter.js +2 -2
  23. package/dist/core/codex-app/codex-agent-role-sync.js +0 -1
  24. package/dist/core/codex-app/codex-app-fast-ui-repair.js +6 -40
  25. package/dist/core/codex-app/codex-app-ui-state-snapshot.js +1 -7
  26. package/dist/core/codex-control/codex-model-capabilities.js +1 -2
  27. package/dist/core/codex-control/codex-model-metadata.js +13 -6
  28. package/dist/core/codex-control/codex-sdk-config-policy.js +3 -3
  29. package/dist/core/codex-control/codex-task-runner.js +2 -3
  30. package/dist/core/codex-model-guard.js +24 -87
  31. package/dist/core/commands/basic-cli.js +6 -0
  32. package/dist/core/commands/loop-command.js +2 -2
  33. package/dist/core/commands/naruto-command.js +11 -11
  34. package/dist/core/commands/profile-command.js +14 -5
  35. package/dist/core/commands/qa-loop-command.js +2 -2
  36. package/dist/core/commands/research-command.js +2 -2
  37. package/dist/core/doctor/doctor-codex-startup-repair.js +0 -3
  38. package/dist/core/evaluation.js +1 -1
  39. package/dist/core/feature-fixtures.js +2 -2
  40. package/dist/core/feature-registry.js +1 -1
  41. package/dist/core/hooks-runtime.js +7 -45
  42. package/dist/core/image-ux-review/imagegen-adapter.js +17 -2
  43. package/dist/core/init/skills.js +6 -7
  44. package/dist/core/init.js +6 -20
  45. package/dist/core/loops/loop-concurrency-budget.js +6 -4
  46. package/dist/core/loops/loop-planner.js +1 -1
  47. package/dist/core/loops/loop-runtime.js +2 -2
  48. package/dist/core/loops/loop-scheduler.js +7 -7
  49. package/dist/core/managed-assets/managed-assets-manifest.js +1 -4
  50. package/dist/core/naruto/naruto-concurrency-governor.js +20 -17
  51. package/dist/core/naruto/naruto-loop-mesh.js +3 -3
  52. package/dist/core/naruto/resource-pressure-monitor.js +5 -2
  53. package/dist/core/provider/model-router.js +15 -35
  54. package/dist/core/release/release-gate-batch-runner.js +2 -1
  55. package/dist/core/release/release-gate-resource-governor.js +12 -12
  56. package/dist/core/release/resource-class-budget.js +5 -5
  57. package/dist/core/routes/constants.js +3 -4
  58. package/dist/core/routes.js +1 -1
  59. package/dist/core/structured-output-adapter.js +15 -1
  60. package/dist/core/update/update-migration-state.js +3 -32
  61. package/dist/core/version.js +1 -1
  62. package/dist/scripts/agent-backfill-replenishment-check.js +1 -1
  63. package/dist/scripts/agent-dynamic-pool-check.js +3 -3
  64. package/dist/scripts/agent-dynamic-pool-fixture.js +1 -1
  65. package/dist/scripts/agent-goal-mode-propagation-check.js +1 -1
  66. package/dist/scripts/agent-patch-swarm-gate-lib.js +2 -2
  67. package/dist/scripts/agent-role-config-repair-check.js +9 -5
  68. package/dist/scripts/agent-scheduler-proof-check.js +1 -1
  69. package/dist/scripts/agent-scheduler-proof-hardening-check.js +1 -1
  70. package/dist/scripts/agent-source-intelligence-propagation-check.js +1 -1
  71. package/dist/scripts/agent-strategy-to-lease-wiring-check.js +2 -2
  72. package/dist/scripts/agent-zellij-dynamic-backfill-panes-check.js +1 -1
  73. package/dist/scripts/codex-app-fast-ui-preservation-check.js +2 -2
  74. package/dist/scripts/codex-app-ui-preservation-check.js +6 -5
  75. package/dist/scripts/codex-lb-fast-ui-preservation-check.js +3 -3
  76. package/dist/scripts/codex-lb-gpt55-fast-profile-check.js +2 -3
  77. package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +7 -7
  78. package/dist/scripts/install-update-preserves-config-check.js +4 -4
  79. package/dist/scripts/loop-hardening-check-lib.js +1 -1
  80. package/dist/scripts/naruto-parallelism-mode-check.js +2 -1
  81. package/dist/scripts/naruto-shadow-clone-swarm-check.js +25 -34
  82. package/dist/scripts/npm-publish-performance-check.js +1 -1
  83. package/dist/scripts/release-aggressive-resource-governor-check.js +2 -2
  84. package/dist/scripts/release-parallel-check.js +1 -2
  85. package/dist/scripts/release-real-check.js +1 -1
  86. package/dist/scripts/sizecheck.js +3 -3
  87. package/package.json +3 -2
@@ -187,6 +187,8 @@ export async function updateCommand(sub = 'now', args = []) {
187
187
  process.exitCode = 1;
188
188
  }
189
189
  export async function setupCommand(args = []) {
190
+ if (flag(args, '--help') || flag(args, '-h'))
191
+ return usageCommand(['setup']);
190
192
  const root = await projectRoot();
191
193
  const installScope = installScopeFromArgs(args);
192
194
  let res = null;
@@ -221,9 +223,13 @@ export async function setupCommand(args = []) {
221
223
  console.log(`- ${file}`);
222
224
  }
223
225
  export async function bootstrapCommand(args = []) {
226
+ if (flag(args, '--help') || flag(args, '-h'))
227
+ return usageCommand(['bootstrap']);
224
228
  return setupCommand(['--local-only', ...args]);
225
229
  }
226
230
  export async function initCommand(args = []) {
231
+ if (flag(args, '--help') || flag(args, '-h'))
232
+ return usageCommand(['init']);
227
233
  return setupCommand(args);
228
234
  }
229
235
  export async function fixPathCommand(args = []) {
@@ -77,7 +77,7 @@ async function loopRun(args) {
77
77
  console.log(`Loop plan blocked: ${plan.blockers.join(', ')}`);
78
78
  return;
79
79
  }
80
- const parallelism = normalizeParallelism(readFlagValue(args, '--parallelism', 'balanced'));
80
+ const parallelism = normalizeParallelism(readFlagValue(args, '--parallelism', 'safe'));
81
81
  const result = await runLoopPlan({ root, plan, parallelism });
82
82
  await setCurrent(root, { mission_id: missionId, mode: 'LOOP', route: 'Loop', route_command: '$Loop', phase: result.ok ? 'LOOP_COMPLETED' : 'LOOP_BLOCKED', stop_gate: 'loop-graph-proof.json' });
83
83
  if (flag(args, '--json'))
@@ -165,7 +165,7 @@ async function loopResume(args) {
165
165
  const existingProofs = await Promise.all(plan.graph.nodes.map((node) => readJson(loopProofPath(root, missionId, node.loop_id), null)));
166
166
  const completed = new Set(existingProofs.filter((proof) => proof !== null && proof.status === 'completed').map((proof) => proof.loop_id));
167
167
  const runnable = rerunCompleted ? plan.graph.nodes : plan.graph.nodes.filter((node) => !completed.has(node.loop_id));
168
- const schedule = scheduleLoopGraph(runnable, normalizeParallelism(readFlagValue(args, '--parallelism', 'balanced')));
168
+ const schedule = scheduleLoopGraph(runnable, normalizeParallelism(readFlagValue(args, '--parallelism', 'safe')));
169
169
  const started = Date.now();
170
170
  const resumedProofs = [];
171
171
  for (const batch of schedule.batches) {
@@ -219,9 +219,8 @@ async function narutoRun(parsed) {
219
219
  backend: schedulerBackend,
220
220
  parallelismMode: parsed.parallelism
221
221
  });
222
- const backendMinimum = schedulerBackend === 'fake' ? roster.agent_count : Math.min(roster.agent_count, 2);
223
- const activeCap = parsed.parallelism === 'safe' ? safe.cap : MAX_NARUTO_AGENT_COUNT;
224
- const activeSlots = Math.max(1, Math.min(roster.agent_count, parsed.concurrency || Math.max(governor.safe_active_workers, backendMinimum), activeCap));
222
+ const activeCap = Math.min(safe.cap, governor.safe_active_workers);
223
+ const activeSlots = Math.max(1, Math.min(roster.agent_count, parsed.concurrency || activeCap, activeCap));
225
224
  const zellijVisiblePanes = Math.max(1, Math.min(activeSlots, governor.safe_zellij_visible_panes));
226
225
  const runPreRunSmoke = parsed.smoke === true || process.env.SKS_NARUTO_PRE_RUN_SMOKE === '1';
227
226
  const realActivePoolSmoke = runPreRunSmoke
@@ -257,9 +256,6 @@ async function narutoRun(parsed) {
257
256
  };
258
257
  const verificationCommand = resolveNarutoVerificationCommand(root);
259
258
  const verificationDag = buildNarutoVerificationDag(workGraph, { cwd: root, command: verificationCommand });
260
- const verificationDagRunPromise = verificationDag.configured && verificationDag.tasks.length > 0
261
- ? runNarutoVerificationPool(verificationDag, governor, { cwd: root, logDir: path.join(mission.dir, 'agents', 'verification-logs') })
262
- : Promise.resolve(null);
263
259
  const gptFinalPack = buildNarutoGptFinalPack({
264
260
  missionId: mission.id,
265
261
  graph: workGraph,
@@ -443,7 +439,11 @@ async function narutoRun(parsed) {
443
439
  // skipped the proof precisely when it mattered most — real execution.
444
440
  const parallelRuntimeOk = parsed.mock || roster.agent_count < 16 || (parallelRuntime?.passed === true
445
441
  && Number(parallelRuntime.max_observed_active_workers || 0) >= Math.min(16, activeSlots));
446
- const verificationDagResult = await verificationDagRunPromise;
442
+ // Verification starts only after clone workers finish so two process pools do
443
+ // not compete for CPU, memory, and disk at the same time.
444
+ const verificationDagResult = verificationDag.configured && verificationDag.tasks.length > 0
445
+ ? await runNarutoVerificationPool(verificationDag, governor, { cwd: root, logDir: path.join(mission.dir, 'agents', 'verification-logs') })
446
+ : null;
447
447
  if (verificationDagResult)
448
448
  await writeJsonAtomic(path.join(mission.dir, 'naruto-verification-dag-result.json'), verificationDagResult);
449
449
  const verificationDagOk = !verificationDag.configured || verificationDagResult === null ? true : verificationDagResult.ok === true;
@@ -1009,7 +1009,7 @@ function parseNarutoArgs(args = []) {
1009
1009
  // Computed early (normally derived further down) so an explicit --clones/
1010
1010
  // --agents can still override this exactly as before, but the *default*
1011
1011
  // scales with apparent task size instead of unconditionally requesting
1012
- // DEFAULT_NARUTO_CLONES (32) for every run, including a one-line typo fix
1012
+ // DEFAULT_NARUTO_CLONES for every run, including a one-line typo fix
1013
1013
  // (20차 P2-3). --work-items defaults to clones*2 below, so this alone
1014
1014
  // scales that down proportionally too.
1015
1015
  const promptSizingValueFlags = 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', '--tournament']);
@@ -1062,17 +1062,17 @@ function parseNarutoArgs(args = []) {
1062
1062
  const noOpenZellij = hasFlag(args, '--no-open-zellij') || hasFlag(args, '--no-zellij');
1063
1063
  const attach = hasFlag(args, '--attach');
1064
1064
  const smoke = hasFlag(args, '--smoke');
1065
- const parallelism = normalizeParallelism(readOption(args, '--parallelism', 'extreme'));
1065
+ const parallelism = normalizeParallelism(readOption(args, '--parallelism', 'safe'));
1066
1066
  const messages = normalizeMessages(readOption(args, '--messages', '8'));
1067
1067
  const tournament = normalizeTournament(readOption(args, '--tournament', '0'));
1068
1068
  const prompt = promptForSizing || 'Naruto shadow clone swarm run';
1069
1069
  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, tournament };
1070
1070
  }
1071
1071
  function normalizeParallelism(value) {
1072
- const text = String(value || 'extreme').toLowerCase();
1072
+ const text = String(value || 'safe').toLowerCase();
1073
1073
  if (text === 'safe' || text === 'balanced' || text === 'extreme')
1074
1074
  return text;
1075
- return 'extreme';
1075
+ return 'safe';
1076
1076
  }
1077
1077
  function normalizeMessages(value) {
1078
1078
  const parsed = Number(value);
@@ -1,18 +1,27 @@
1
1
  import path from 'node:path';
2
2
  import { readJson, sksRoot, writeJsonAtomic } from '../fsx.js';
3
- import { DEFAULT_CODEX_REASONING_EFFORT, REQUIRED_CODEX_MODEL } from '../codex-model-guard.js';
3
+ import { DEFAULT_CODEX_REASONING_EFFORT } from '../codex-model-guard.js';
4
4
  import { ALLOWED_REASONING_EFFORTS } from '../routes.js';
5
5
  export async function profileCommand(sub, args = []) {
6
6
  const root = await sksRoot();
7
7
  if (sub === 'show')
8
- return console.log(JSON.stringify(await readJson(path.join(root, '.sneakoscope', 'model', 'current.json'), { model: REQUIRED_CODEX_MODEL, reasoning_effort: DEFAULT_CODEX_REASONING_EFFORT }), null, 2));
8
+ return console.log(JSON.stringify(await readJson(path.join(root, '.sneakoscope', 'model', 'current.json'), { model: null, model_policy: 'inherit_codex_selection', reasoning_effort: DEFAULT_CODEX_REASONING_EFFORT }), null, 2));
9
9
  if (sub === 'set') {
10
10
  const effort = args[0] || DEFAULT_CODEX_REASONING_EFFORT;
11
11
  if (!ALLOWED_REASONING_EFFORTS.has(effort))
12
12
  throw new Error(`unsupported reasoning effort: ${effort}; use ${[...ALLOWED_REASONING_EFFORTS].join(', ')}`);
13
- await writeJsonAtomic(path.join(root, '.sneakoscope', 'model', 'current.json'), { model: REQUIRED_CODEX_MODEL, reasoning_effort: effort, set_at: new Date().toISOString() });
14
- return console.log(`Model profile set: ${REQUIRED_CODEX_MODEL} ${effort}`);
13
+ const modelIndex = args.indexOf('--model');
14
+ const model = modelIndex >= 0 ? String(args[modelIndex + 1] || '').trim() : '';
15
+ if (modelIndex >= 0 && !model)
16
+ throw new Error('--model requires a Codex model identifier');
17
+ await writeJsonAtomic(path.join(root, '.sneakoscope', 'model', 'current.json'), {
18
+ model: model || null,
19
+ model_policy: model ? 'explicit' : 'inherit_codex_selection',
20
+ reasoning_effort: effort,
21
+ set_at: new Date().toISOString()
22
+ });
23
+ return console.log(`Model profile set: ${model || 'Codex-selected model'} ${effort}`);
15
24
  }
16
- console.error(`Usage: sks profile show|set <${[...ALLOWED_REASONING_EFFORTS].join('|')}>`);
25
+ console.error(`Usage: sks profile show|set <${[...ALLOWED_REASONING_EFFORTS].join('|')}> [--model <codex-model-id>]`);
17
26
  }
18
27
  //# sourceMappingURL=profile-command.js.map
@@ -160,7 +160,7 @@ async function qaLoopRun(args) {
160
160
  const fallbackCycles = Number.parseInt(contract.answers?.MAX_QA_CYCLES, 10) || DEFAULT_QA_MAX_CYCLES;
161
161
  const maxCycles = readMaxCycles(args, fallbackCycles);
162
162
  const requestedAgents = readBoundedIntegerFlag(args, '--agents', 3, 1, 20);
163
- const targetActiveSlots = readBoundedIntegerFlag(args, '--target-active-slots', requestedAgents, 1, 20);
163
+ const targetActiveSlots = readBoundedIntegerFlag(args, '--target-active-slots', Math.min(requestedAgents, 4), 1, 4);
164
164
  const desiredWorkItemCount = readBoundedIntegerFlag(args, '--work-items', targetActiveSlots, 1, 200);
165
165
  const minimumWorkItems = readBoundedIntegerFlag(args, '--minimum-work-items', targetActiveSlots, 1, 200);
166
166
  const maxQueueExpansion = readBoundedIntegerFlag(args, '--max-queue-expansion', 10, 0, 200);
@@ -322,7 +322,7 @@ async function qaLoopRun(args) {
322
322
  emitStreamEvent('progress', { mission_id: id, type: 'qaloop.run.started', max_cycles: maxCycles, mock });
323
323
  const nativeAgentPlan = await readJson(path.join(dir, 'qa-agent-plan.json'), null);
324
324
  const nativeRoster = requestedAgents === 3 ? nativeAgentPlan : null;
325
- 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' } });
325
+ 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, 4), 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' } });
326
326
  await writeJsonAtomic(path.join(dir, 'qa-native-agent-run.json'), nativeAgentRun);
327
327
  await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'qaloop.native_agents.completed', backend: nativeAgentRun.backend, ok: nativeAgentRun.ok, proof: nativeAgentRun.proof?.status });
328
328
  if (stream)
@@ -158,7 +158,7 @@ async function researchRun(args) {
158
158
  }
159
159
  const maxCycles = readMaxCycles(args, RESEARCH_DEFAULT_MAX_CYCLES);
160
160
  const requestedAgents = readBoundedIntegerFlag(args, '--agents', plan.native_agent_plan?.session_count || 5, 1, 20);
161
- const targetActiveSlots = readBoundedIntegerFlag(args, '--target-active-slots', requestedAgents, 1, 20);
161
+ const targetActiveSlots = readBoundedIntegerFlag(args, '--target-active-slots', Math.min(requestedAgents, 4), 1, 4);
162
162
  const desiredWorkItemCount = readBoundedIntegerFlag(args, '--work-items', targetActiveSlots, 1, 200);
163
163
  const minimumWorkItems = readBoundedIntegerFlag(args, '--minimum-work-items', targetActiveSlots, 1, 200);
164
164
  const maxQueueExpansion = readBoundedIntegerFlag(args, '--max-queue-expansion', 10, 0, 200);
@@ -181,7 +181,7 @@ async function researchRun(args) {
181
181
  await runResearchCycle(dir, researchWorkGraph, { cycle: 0, status: mock ? 'mock_native_orchestrator_planned' : 'native_orchestrator_planned' });
182
182
  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 });
183
183
  await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'research.run.started', maxCycles, mock, cycleTimeoutMinutes, real_run_required: !mock });
184
- 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: !applyPatches, profile, writeMode: writeMode, applyPatches, dryRunPatches, maxWriteAgents, roster: plan.native_agent_plan, routeCommand: 'sks research run', routeBlackboxKind: 'actual_research_command', narutoWorkGraph: researchWorkGraph });
184
+ 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, 4), readonly: !applyPatches, profile, writeMode: writeMode, applyPatches, dryRunPatches, maxWriteAgents, roster: plan.native_agent_plan, routeCommand: 'sks research run', routeBlackboxKind: 'actual_research_command', narutoWorkGraph: researchWorkGraph });
185
185
  await writeJsonAtomic(path.join(dir, 'research-native-agent-run.json'), nativeAgentRun);
186
186
  await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'research.native_agents.completed', backend: nativeAgentRun.backend, ok: nativeAgentRun.ok, proof: nativeAgentRun.proof?.status });
187
187
  if (!nativeAgentRun.ok) {
@@ -1,7 +1,6 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import os from 'node:os';
4
- import { REQUIRED_CODEX_MODEL, DEFAULT_CODEX_REASONING_EFFORT } from '../codex-model-guard.js';
5
4
  import { ensureDir, exists, nowIso, readText, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
6
5
  import { isUnmanagedProjectCodexConfig, writeCodexConfigGuarded } from '../codex/codex-config-guard.js';
7
6
  export const DOCTOR_CODEX_STARTUP_REPAIR_SCHEMA = 'sks.doctor-codex-startup-repair.v1';
@@ -486,8 +485,6 @@ function roleConfigToml(name, description, sandbox) {
486
485
  return [
487
486
  `name = "${name}"`,
488
487
  `description = "${description}"`,
489
- `model = "${REQUIRED_CODEX_MODEL}"`,
490
- `model_reasoning_effort = "${DEFAULT_CODEX_REASONING_EFFORT}"`,
491
488
  `sandbox_mode = "${sandbox}"`,
492
489
  'approval_policy = "never"',
493
490
  'developer_instructions = """',
@@ -67,7 +67,7 @@ export const PERMISSION_PROFILES = Object.freeze({
67
67
  dangerous_full_access: { filesystem: 'full-access', network: 'controlled', purpose: 'Never default. Requires explicit reason and review.' }
68
68
  });
69
69
  export const DEFAULT_MULTIAGENT_V2 = Object.freeze({
70
- max_threads: 6,
70
+ max_threads: 4,
71
71
  max_depth: 1,
72
72
  job_max_runtime_seconds: 1800,
73
73
  wait_control: 'bounded_wait_then_structured_summary',
@@ -216,8 +216,8 @@ const FIXTURES = Object.freeze({
216
216
  'route-commit-and-push': fixture('mock', '$Commit-And-Push git route', ['completion-proof.json'], 'pass', {
217
217
  reason: 'Dollar-command alias of cli-commit-and-push; dispatches to the same simpleGitCommitCommand() that performs a real commit and `git push` with no working --dry-run mode. Left as documented mock alongside cli-commit-and-push.'
218
218
  }),
219
- 'route-release-review': fixture('execute_and_validate_artifacts', 'sks agent run "release audit" --route "$Release-Review" --agents 10 --concurrency 5 --mock --json', ['release-review-native-agent-plan.json', 'agents/agent-proof-evidence.json', 'agents/agent-effort-policy.json'], 'pass', { timeout_ms: 90000 }),
220
- 'route-native-agent-intake': fixture('execute_and_validate_artifacts', 'sks agent run "fixture" --route "$Team" --agents 5 --concurrency 5 --mock --json', ['agents/agent-central-ledger.json', 'agents/agent-task-board.json', 'agents/agent-leases.json', 'agents/agent-no-overlap-proof.json', 'agents/agent-session-cleanup.json', 'agents/agent-proof-evidence.json', 'agents/agent-effort-policy.json'], 'pass', { timeout_ms: 90000 }),
219
+ 'route-release-review': fixture('execute_and_validate_artifacts', 'sks agent run "release audit" --route "$Release-Review" --agents 10 --concurrency 4 --mock --json', ['release-review-native-agent-plan.json', 'agents/agent-proof-evidence.json', 'agents/agent-effort-policy.json'], 'pass', { timeout_ms: 90000 }),
220
+ 'route-native-agent-intake': fixture('execute_and_validate_artifacts', 'sks agent run "fixture" --route "$Team" --agents 5 --concurrency 4 --mock --json', ['agents/agent-central-ledger.json', 'agents/agent-task-board.json', 'agents/agent-leases.json', 'agents/agent-no-overlap-proof.json', 'agents/agent-session-cleanup.json', 'agents/agent-proof-evidence.json', 'agents/agent-effort-policy.json'], 'pass', { timeout_ms: 90000 }),
221
221
  'proof-agent-evidence': fixture('execute_and_validate_artifacts', 'sks naruto run "fixture" --backend fake --work-items 4 --json', ['naruto-gate.json', 'agents/agent-proof-evidence.json'], 'pass', { timeout_ms: 240000 })
222
222
  });
223
223
  const STATIC_CONTRACT_FEATURES = new Set([
@@ -668,7 +668,7 @@ function routeFeature(route) {
668
668
  function nativeAgentIntakeFeature() {
669
669
  return baseFeature({
670
670
  id: 'route-native-agent-intake',
671
- commands: ['sks agent run "task" --route "$Team" --agents 5 --concurrency 5 --mock --json'],
671
+ commands: ['sks agent run "task" --route "$Team" --agents 5 --concurrency 4 --mock --json'],
672
672
  aliases: ['sks team "task" [executor:5 reviewer:6 user:1]'],
673
673
  category: 'proof-route',
674
674
  maturity: 'stable',
@@ -10,7 +10,6 @@ import { classifyMadSksShellCommand } from './mad-sks/write-guard.js';
10
10
  import { activeRouteContext, evaluateStop, prepareRoute, promptPipelineContext as routePipelineContext, recordContext7Evidence, recordSubagentEvidence, routePrompt } from './pipeline.js';
11
11
  import { localizedFinalizationReason } from './language-preference.js';
12
12
  import { classifyToolError } from './evaluation.js';
13
- import { REQUIRED_CODEX_MODEL, SUPPORTED_CODEX_MODELS, isForbiddenCodexModel } from './codex-model-guard.js';
14
13
  import { dollarCommand, routeRequiresSubagents, stripVisibleDecisionAnswerBlocks } from './routes.js';
15
14
  import { leanEngineeringCompactText } from './lean-engineering-policy.js';
16
15
  import { appendMissionStatus } from './recallpulse.js';
@@ -188,9 +187,6 @@ export async function evaluateHookPayload(name, payload = {}, opts = {}) {
188
187
  const state = { ...loadedState, _session_key: loadedState?._session_key || sessionKey };
189
188
  const noQuestion = isNoQuestionRunning(state);
190
189
  if (name === 'user-prompt-submit') {
191
- const modelBlock = blockForbiddenClientModel(payload);
192
- if (modelBlock)
193
- return modelBlock;
194
190
  return hookUserPrompt(root, state, payload, noQuestion, sessionKey);
195
191
  }
196
192
  if (name === 'pre-tool')
@@ -208,20 +204,14 @@ export async function evaluateHookPayload(name, payload = {}, opts = {}) {
208
204
  async function hookSubagentStart(root, state, sessionKey = null) {
209
205
  void sessionKey;
210
206
  const active = await activeRouteContext(root, state).catch(() => '');
211
- const additionalContext = [leanEngineeringCompactText(), active].filter(Boolean).join('\n\n');
207
+ const resourceGuard = [
208
+ 'SKS resource guard: keep at most 4 active subagents in this session.',
209
+ 'Subagents must not spawn nested subagents or duplicate an already assigned audit lane.',
210
+ 'Queue additional independent work instead of increasing active fan-out.'
211
+ ].join(' ');
212
+ const additionalContext = [leanEngineeringCompactText(), resourceGuard, active].filter(Boolean).join('\n\n');
212
213
  return { continue: true, additionalContext };
213
214
  }
214
- function blockForbiddenClientModel(payload = {}) {
215
- const model = forbiddenClientModelFromPayload(payload);
216
- if (!model || !isForbiddenCodexModel(model))
217
- return null;
218
- if (looksLikeCodexUiSettingsEvent(payload))
219
- return null;
220
- return {
221
- decision: 'block',
222
- reason: `SKS requires one of [${SUPPORTED_CODEX_MODELS.join(', ')}]; client payload requested ${model}. Switch the Codex client/session model to ${REQUIRED_CODEX_MODEL} (default) or another supported model and retry.`
223
- };
224
- }
225
215
  function looksLikeCodexUiSettingsEvent(payload = {}) {
226
216
  const prompt = stripVisibleDecisionAnswerBlocks(extractUserPrompt(payload));
227
217
  const haystack = [
@@ -247,34 +237,6 @@ function looksLikeCodexUiSettingsEvent(payload = {}) {
247
237
  ].filter(Boolean).join(' ');
248
238
  return !prompt && /\b(?:settings|preferences|profile|speed|fast[_\s-]*mode|reasoning|model[_\s-]*select|codex[_\s-]*app)\b/i.test(haystack);
249
239
  }
250
- function forbiddenClientModelFromPayload(payload = {}) {
251
- const candidates = [
252
- payload.model,
253
- payload.model_id,
254
- payload.modelId,
255
- payload.client_model,
256
- payload.clientModel,
257
- ...clientModelCandidates(payload.client),
258
- ...clientModelCandidates(payload.metadata),
259
- ...clientModelCandidates(payload.context),
260
- ...clientModelCandidates(payload.thread),
261
- ...clientModelCandidates(payload.session)
262
- ];
263
- return candidates.find((value) => typeof value === 'string' && isForbiddenCodexModel(value)) || '';
264
- }
265
- function clientModelCandidates(value, depth = 0) {
266
- if (!value || typeof value !== 'object' || depth > 4)
267
- return [];
268
- const out = [];
269
- for (const key of ['model', 'model_id', 'modelId', 'client_model', 'clientModel']) {
270
- if (typeof value[key] === 'string')
271
- out.push(value[key]);
272
- }
273
- for (const key of ['client', 'metadata', 'context', 'thread', 'session']) {
274
- out.push(...clientModelCandidates(value[key], depth + 1));
275
- }
276
- return out;
277
- }
278
240
  async function hookUserPrompt(root, state, payload, noQuestion, sessionKey = null) {
279
241
  if (looksLikeCodexGitAction(payload)) {
280
242
  await armCodexGitActionStopBypass(root, payload).catch(() => null);
@@ -1111,7 +1073,7 @@ export async function selftestCodexCommitHooks() {
1111
1073
  throw new Error(`selftest failed: metadata-light commit-push stop ${metadataLightStop.code}: ${metadataLightStop.stderr}`);
1112
1074
  if (JSON.parse(metadataLightStop.stdout).decision === 'block')
1113
1075
  throw new Error('selftest failed: metadata-light commit-push stop bypass');
1114
- const settingsHook = await runHook('user-prompt-submit', { model: 'gpt-5.0-forbidden', metadata: { source: 'codex_app_settings', feature: 'speed profile' } });
1076
+ const settingsHook = await runHook('user-prompt-submit', { model: 'future-codex-catalog-model', metadata: { source: 'codex_app_settings', feature: 'speed profile' } });
1115
1077
  if (settingsHook.code !== 0)
1116
1078
  throw new Error(`selftest failed: settings hook ${settingsHook.code}: ${settingsHook.stderr}`);
1117
1079
  const settingsJson = JSON.parse(settingsHook.stdout);
@@ -9,7 +9,6 @@ import { withResponsesRetry } from '../responses-retry-policy.js';
9
9
  import { discoverCodexAppGeneratedImage } from './codex-app-generated-image-discovery.js';
10
10
  import { writeImageArtifactPathContract } from '../image/image-artifact-path-contract.js';
11
11
  import { registerImageArtifact } from '../image/image-artifact-registry.js';
12
- import { REQUIRED_CODEX_MODEL } from '../codex-model-guard.js';
13
12
  const DEFAULT_OPENAI_IMAGE_EDITS_ENDPOINT = 'https://api.openai.com/v1/images/edits';
14
13
  export function buildCalloutPrompt(sourceScreenId, context = {}) {
15
14
  return [
@@ -376,6 +375,22 @@ export function createOpenAIImagesApiAdapter(opts = {}) {
376
375
  await writeJsonAtomic(responseArtifact, blocked);
377
376
  return { ok: false, status: 'blocked', generated_image_path: null, output_id: null, blocker: auth.blocker, provider: 'openai_images_api', request_artifact: requestArtifact, response_artifact: responseArtifact, latency_ms: Date.now() - started };
378
377
  }
378
+ if (useResponsesImageTool && !responsesImagegenModel(opts)) {
379
+ const blocker = 'imagegen_responses_model_missing';
380
+ await writeJsonAtomic(responseArtifact, {
381
+ schema: 'sks.image-ux-gpt-image-2-response.v1',
382
+ created_at: nowIso(),
383
+ provider: 'openai_responses_image_generation',
384
+ evidence_class: 'non_codex_api_fallback',
385
+ model: 'gpt-image-2',
386
+ ok: false,
387
+ status: 'blocked',
388
+ blocker,
389
+ setup_guidance: 'Set SKS_IMAGEGEN_RESPONSES_MODEL to a model available through the configured Responses provider; SKS does not hardcode a text model.',
390
+ local_only: true
391
+ });
392
+ return { ok: false, status: 'blocked', generated_image_path: null, output_id: null, blocker, provider: 'openai_responses_image_generation', request_artifact: requestArtifact, response_artifact: responseArtifact, latency_ms: Date.now() - started };
393
+ }
379
394
  try {
380
395
  if (useResponsesImageTool) {
381
396
  const imageDataUrl = `data:${mimeForPath(sourcePath)};base64,${await fsp.readFile(sourcePath, 'base64')}`;
@@ -630,7 +645,7 @@ function responsesEndpoint(baseUrl = '') {
630
645
  return /\/responses$/i.test(base) ? base : `${base}/responses`;
631
646
  }
632
647
  function responsesImagegenModel(opts = {}) {
633
- return String(opts.responsesModel || process.env.SKS_IMAGEGEN_RESPONSES_MODEL || process.env.OPENAI_MODEL || REQUIRED_CODEX_MODEL);
648
+ return String(opts.responsesModel || process.env.SKS_IMAGEGEN_RESPONSES_MODEL || process.env.OPENAI_MODEL || '').trim();
634
649
  }
635
650
  function imagegenFetchTimeoutMs(opts = {}) {
636
651
  const value = Number(opts.fetchTimeoutMs || process.env.SKS_IMAGEGEN_FETCH_TIMEOUT_MS || 90000);
@@ -7,7 +7,6 @@ import { syncCoreSkillsIntegrity } from '../codex-native/core-skill-integrity.js
7
7
  import { dbSafetyGuardSkillText, madDbSkillText } from '../mad-db/mad-db-policy.js';
8
8
  import { SKILL_DREAM_POLICY, skillDreamPolicyText } from '../skill-forge.js';
9
9
  import { AWESOME_DESIGN_MD_REFERENCE, CODEX_APP_IMAGE_GENERATION_DOC_URL, CODEX_COMPUTER_USE_ONLY_POLICY, CODEX_IMAGEGEN_EVIDENCE_SOURCE, CODEX_IMAGEGEN_REQUIRED_POLICY, CODEX_WEB_VERIFICATION_POLICY, DEFAULT_CODEX_APP_PLUGINS, DESIGN_SYSTEM_SSOT, DOLLAR_COMMANDS, DOLLAR_SKILL_NAMES, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS, GETDESIGN_REFERENCE, IMAGEGEN_SOCIAL_SOURCE_POLICY, OPENAI_CHATGPT_IMAGES_2_DOC_URL, OPENAI_GPT_IMAGE_2_MODEL_DOC_URL, OPENAI_IMAGE_GENERATION_DOC_URL, PPT_CONDITIONAL_SKILL_ALLOWLIST, PPT_PIPELINE_MCP_ALLOWLIST, PPT_PIPELINE_SKILL_ALLOWLIST, RECOMMENDED_SKILLS, RESERVED_CODEX_PLUGIN_SKILL_NAMES, SOLUTION_SCOUT_SKILL_NAME, chatCaptureIntakeText, context7ConfigToml, getdesignReferencePolicyText, imageUxReviewPipelinePolicyText, leanEngineeringCompactText, outcomeRubricPolicyText, pptPipelineAllowlistPolicyText, productDesignPluginPolicyText, solutionScoutPolicyText, speedLanePolicyText, stackCurrentDocsPolicyText, triwikiContextTrackingText, triwikiStagePolicyText } from '../routes.js';
10
- import { REQUIRED_CODEX_MODEL } from '../codex-model-guard.js';
11
10
  const SKS_SKILL_MANIFEST_FILE = '.sks-generated.json';
12
11
  const PACKAGED_SKILLS_MANIFEST_SCHEMA = 'sks.skills-manifest.v1';
13
12
  const GENERATED_PRUNE_POLICY = 'remove_previous_sks_generated_paths_absent_from_current_manifest';
@@ -543,12 +542,12 @@ async function removeDirIfEmpty(dir) {
543
542
  }
544
543
  export async function installCodexAgents(root) {
545
544
  const agents = {
546
- 'analysis-scout.toml': `name = "analysis_scout"\ndescription = "SKS analysis scout with bounded write capability retained for stale Codex agent-role config repair."\nmodel = "${REQUIRED_CODEX_MODEL}"\nmodel_reasoning_effort = "low"\nsandbox_mode = "workspace-write"\ndeveloper_instructions = """\nYou are an SKS analysis scout.\nOnly edit bounded files assigned by the parent orchestrator; otherwise inspect only and return concise source-backed findings.\n"""\n`,
547
- 'native-agent-intake.toml': `name = "native_agent"\ndescription = "Team native agent with bounded write capability. Maps one independent repo/docs/tests/API/risk/user-friction slice and can produce patch-envelope work when assigned."\nmodel = "${REQUIRED_CODEX_MODEL}"\nmodel_reasoning_effort = "low"\nsandbox_mode = "workspace-write"\ndeveloper_instructions = """\nYou are an SKS Team native agent.\nOnly edit bounded files assigned by the parent orchestrator.\nOwn exactly one investigation or implementation slice assigned by the parent orchestrator.\nUse the mission roster or worker inbox model and model_reasoning_effort when the host exposes it: simple bounded work may use gpt-5.4-mini, ordinary tool work uses ${REQUIRED_CODEX_MODEL} low, and knowledge/research/safety/release work uses ${REQUIRED_CODEX_MODEL} high.\nMap relevant source files, docs, tests, APIs, DB or safety risks, UX friction, and likely implementation boundaries.\nReturn concise source-backed claims suitable for team-analysis.md and TriWiki ingestion: claim, source path, evidence hash or quoted anchor, risk, confidence, and recommended implementation slice.\nDo not debate the final plan. Implement only when the assigned slice includes write paths.\nAlso return a concise LIVE_EVENT line that the parent can record with sks team event.\n"""\n`,
548
- 'team-consensus.toml': `name = "team_consensus"\ndescription = "Planning and debate specialist for SKS Team mode with bounded write capability. Maps options, constraints, role-persona risks, and proposes the agreed objective before implementation starts."\nmodel = "${REQUIRED_CODEX_MODEL}"\nmodel_reasoning_effort = "high"\nsandbox_mode = "workspace-write"\ndeveloper_instructions = """\nYou are the SKS Team consensus specialist.\nOnly edit bounded files assigned by the parent orchestrator.\nUse the mission roster model and model_reasoning_effort when the host exposes them; planning normally uses ${REQUIRED_CODEX_MODEL} low or high depending on risk.\nMap the affected code paths, viable approaches, constraints, risks, and acceptance criteria.\nRun the debate as role-persona synthesis: final users are low-context, self-interested, stubborn, and inconvenience-averse; executors are capable developers; reviewers are strict.\nArgue for the smallest coherent objective that can be handed to a fresh executor_N development team.\nPlan for at least ${MIN_TEAM_REVIEWER_LANES} independent reviewer/QA validation lanes before integration or final.\nReturn: recommended objective, rejected alternatives, implementation slices, required reviewers, user-friction risks, and unresolved risks.\nAlso return a concise LIVE_EVENT line that the parent can record with sks team event.\n"""\n`,
549
- 'implementation-worker.toml': `name = "implementation_worker"\ndescription = "Implementation specialist for SKS Team mode. Owns one bounded write set and coordinates with other executor_N workers."\nmodel = "${REQUIRED_CODEX_MODEL}"\nmodel_reasoning_effort = "medium"\nsandbox_mode = "workspace-write"\ndeveloper_instructions = """\nYou are an SKS Team executor/developer in the fresh development bundle.\nYou are not alone in the codebase. Other executor_N workers may be editing disjoint files.\nUse the mission roster or worker inbox reasoning_effort when the host exposes it; simple bounded changes can use low, tool-heavy implementation medium, and safety/release/DB work high.\nOnly edit the files or module slice assigned to you.\nDo not revert or overwrite edits made by others.\nRead local patterns first, make the smallest correct change, avoid adding user friction, run focused verification for your slice, and report changed paths plus evidence.\nDo not create fallback implementation code, substitute behavior, mock behavior, or compatibility shims unless the user or sealed decision contract explicitly requested them.\nRespect all SKS hooks, DB safety rules, no-question run rules, and H-Proof completion gates.\nAlso return concise LIVE_EVENT lines for started, blocked, changed files, verification, and final result so the parent can record them.\n"""\n`,
550
- 'db-safety-reviewer.toml': `name = "db_safety_reviewer"\ndescription = "Database safety reviewer with bounded write capability for SQL, migrations, Supabase, RLS, destructive-operation risk, and rollback safety."\nmodel = "${REQUIRED_CODEX_MODEL}"\nmodel_reasoning_effort = "high"\nsandbox_mode = "workspace-write"\ndeveloper_instructions = """\nYou are a database safety reviewer.\nOnly edit bounded files assigned by the parent orchestrator. Never execute destructive commands.\nReview migrations, SQL, Supabase RLS, transaction boundaries, rollback safety, and MCP database tool usage.\nBlock DROP, TRUNCATE, mass DELETE/UPDATE, db reset, db push, project deletion, branch reset/merge/delete, RLS disabling, and live execute_sql writes.\nReturn concrete risks, exact file references, and required fixes.\nAlso return a concise LIVE_EVENT line that the parent can record with sks team event.\n"""\n`,
551
- 'qa-reviewer.toml': `name = "qa_reviewer"\ndescription = "Strict verification reviewer with bounded write capability for correctness, regressions, missing tests, user friction, and final evidence."\nmodel = "${REQUIRED_CODEX_MODEL}"\nmodel_reasoning_effort = "high"\nsandbox_mode = "workspace-write"\ndeveloper_instructions = """\nYou are an SKS Team strict reviewer.\nOnly edit bounded files assigned by the parent orchestrator.\nReview correctness, edge cases, regression risk, missing tests, unsupported claims, and whether the final evidence proves the claimed outcome.\nTeam review must cover at least ${MIN_TEAM_REVIEWER_LANES} independent reviewer/QA validation lanes before integration or final; flag missing review lane evidence.\nAlso evaluate practical friction from the viewpoint of a stubborn, low-context final user who dislikes inconvenience.\nPrioritize concrete findings with file references and focused verification suggestions.\nFlag any unrequested fallback implementation code, substitute behavior, mock behavior, or compatibility shim as a blocking finding unless the user or sealed decision contract explicitly requested it.\nReturn no findings if the implementation is sound, and clearly list residual test gaps.\nAlso return a concise LIVE_EVENT line that the parent can record with sks team event.\n"""\n`
545
+ 'analysis-scout.toml': `name = "analysis_scout"\ndescription = "SKS analysis scout with bounded write capability retained for stale Codex agent-role config repair."\nsandbox_mode = "workspace-write"\ndeveloper_instructions = """\nYou are an SKS analysis scout.\nInherit the model and reasoning level selected by Codex; never replace or constrain its model catalog.\nOnly edit bounded files assigned by the parent orchestrator; otherwise inspect only and return concise source-backed findings.\n"""\n`,
546
+ 'native-agent-intake.toml': `name = "native_agent"\ndescription = "Team native agent with bounded write capability. Maps one independent repo/docs/tests/API/risk/user-friction slice and can produce patch-envelope work when assigned."\nsandbox_mode = "workspace-write"\ndeveloper_instructions = """\nYou are an SKS Team native agent.\nInherit the model and reasoning level selected by Codex; never replace or constrain its model catalog.\nOnly edit bounded files assigned by the parent orchestrator.\nOwn exactly one investigation or implementation slice assigned by the parent orchestrator.\nUse a capability-advertised reasoning level when the runtime supplies a per-task override; otherwise keep the current Codex selection.\nMap relevant source files, docs, tests, APIs, DB or safety risks, UX friction, and likely implementation boundaries.\nReturn concise source-backed claims suitable for team-analysis.md and TriWiki ingestion: claim, source path, evidence hash or quoted anchor, risk, confidence, and recommended implementation slice.\nDo not debate the final plan. Implement only when the assigned slice includes write paths.\nAlso return a concise LIVE_EVENT line that the parent can record with sks team event.\n"""\n`,
547
+ 'team-consensus.toml': `name = "team_consensus"\ndescription = "Planning and debate specialist for SKS Team mode with bounded write capability. Maps options, constraints, role-persona risks, and proposes the agreed objective before implementation starts."\nsandbox_mode = "workspace-write"\ndeveloper_instructions = """\nYou are the SKS Team consensus specialist.\nInherit the model and reasoning level selected by Codex; never replace or constrain its model catalog.\nOnly edit bounded files assigned by the parent orchestrator.\nUse a capability-advertised reasoning level when the runtime supplies a per-task override; otherwise keep the current Codex selection.\nMap the affected code paths, viable approaches, constraints, risks, and acceptance criteria.\nRun the debate as role-persona synthesis: final users are low-context, self-interested, stubborn, and inconvenience-averse; executors are capable developers; reviewers are strict.\nArgue for the smallest coherent objective that can be handed to a fresh executor_N development team.\nPlan for at least ${MIN_TEAM_REVIEWER_LANES} independent reviewer/QA validation lanes before integration or final.\nReturn: recommended objective, rejected alternatives, implementation slices, required reviewers, user-friction risks, and unresolved risks.\nAlso return a concise LIVE_EVENT line that the parent can record with sks team event.\n"""\n`,
548
+ 'implementation-worker.toml': `name = "implementation_worker"\ndescription = "Implementation specialist for SKS Team mode. Owns one bounded write set and coordinates with other executor_N workers."\nsandbox_mode = "workspace-write"\ndeveloper_instructions = """\nYou are an SKS Team executor/developer in the fresh development bundle.\nInherit the model and reasoning level selected by Codex; never replace or constrain its model catalog.\nYou are not alone in the codebase. Other executor_N workers may be editing disjoint files.\nUse a capability-advertised reasoning level when the runtime supplies a per-task override; otherwise keep the current Codex selection.\nOnly edit the files or module slice assigned to you.\nDo not revert or overwrite edits made by others.\nRead local patterns first, make the smallest correct change, avoid adding user friction, run focused verification for your slice, and report changed paths plus evidence.\nDo not create fallback implementation code, substitute behavior, mock behavior, or compatibility shims unless the user or sealed decision contract explicitly requested them.\nRespect all SKS hooks, DB safety rules, no-question run rules, and H-Proof completion gates.\nAlso return concise LIVE_EVENT lines for started, blocked, changed files, verification, and final result so the parent can record them.\n"""\n`,
549
+ 'db-safety-reviewer.toml': `name = "db_safety_reviewer"\ndescription = "Database safety reviewer with bounded write capability for SQL, migrations, Supabase, RLS, destructive-operation risk, and rollback safety."\nsandbox_mode = "workspace-write"\ndeveloper_instructions = """\nYou are a database safety reviewer.\nInherit the model and reasoning level selected by Codex; never replace or constrain its model catalog.\nOnly edit bounded files assigned by the parent orchestrator. Never execute destructive commands.\nReview migrations, SQL, Supabase RLS, transaction boundaries, rollback safety, and MCP database tool usage.\nBlock DROP, TRUNCATE, mass DELETE/UPDATE, db reset, db push, project deletion, branch reset/merge/delete, RLS disabling, and live execute_sql writes.\nReturn concrete risks, exact file references, and required fixes.\nAlso return a concise LIVE_EVENT line that the parent can record with sks team event.\n"""\n`,
550
+ 'qa-reviewer.toml': `name = "qa_reviewer"\ndescription = "Strict verification reviewer with bounded write capability for correctness, regressions, missing tests, user friction, and final evidence."\nsandbox_mode = "workspace-write"\ndeveloper_instructions = """\nYou are an SKS Team strict reviewer.\nInherit the model and reasoning level selected by Codex; never replace or constrain its model catalog.\nOnly edit bounded files assigned by the parent orchestrator.\nReview correctness, edge cases, regression risk, missing tests, unsupported claims, and whether the final evidence proves the claimed outcome.\nTeam review must cover at least ${MIN_TEAM_REVIEWER_LANES} independent reviewer/QA validation lanes before integration or final; flag missing review lane evidence.\nAlso evaluate practical friction from the viewpoint of a stubborn, low-context final user who dislikes inconvenience.\nPrioritize concrete findings with file references and focused verification suggestions.\nFlag any unrequested fallback implementation code, substitute behavior, mock behavior, or compatibility shim as a blocking finding unless the user or sealed decision contract explicitly requested it.\nReturn no findings if the implementation is sound, and clearly list residual test gaps.\nAlso return a concise LIVE_EVENT line that the parent can record with sks team event.\n"""\n`
552
551
  };
553
552
  const dir = path.join(root, '.codex', 'agents');
554
553
  await ensureDir(dir);
package/dist/core/init.js CHANGED
@@ -2,7 +2,6 @@ import path from 'node:path';
2
2
  import fsp from 'node:fs/promises';
3
3
  import { ensureDir, readJson, readText, writeJsonAtomic, writeTextAtomic, mergeManagedBlock, nowIso, PACKAGE_VERSION, exists } from './fsx.js';
4
4
  import { DEFAULT_RETENTION_POLICY } from './retention.js';
5
- import { REQUIRED_CODEX_MODEL } from './codex-model-guard.js';
6
5
  import { DEFAULT_DB_SAFETY_POLICY } from './db-safety.js';
7
6
  import { isHarnessSourceProject, writeHarnessGuardPolicy } from './harness-guard.js';
8
7
  import { repairSksGeneratedArtifacts } from './harness-conflicts.js';
@@ -548,7 +547,7 @@ export async function initProject(root, opts = {}) {
548
547
  block_other_codex_harnesses: true,
549
548
  hard_blockers: ['OMX', 'DCodex'],
550
549
  cleanup_prompt_command: `${commandPrefix} conflicts prompt`,
551
- cleanup_model: REQUIRED_CODEX_MODEL,
550
+ cleanup_model_policy: 'inherit_codex_selection',
552
551
  cleanup_reasoning_effort: 'high',
553
552
  human_approval_required: true
554
553
  },
@@ -583,12 +582,9 @@ export async function initProject(root, opts = {}) {
583
582
  'tool_suggest', 'plugins', 'codex_hooks'
584
583
  ];
585
584
  function mergeManagedCodexConfigToml(existingContent = '') {
586
- let next = removeLegacyTopLevelCodexModeLocks(String(existingContent || '').trimEnd());
585
+ let next = String(existingContent || '').trimEnd();
587
586
  next = removeTomlTableKey(next, 'notice', 'fast_default_opt_out');
588
587
  next = removeTomlTableKey(next, 'features', 'codex_hooks');
589
- // The current Codex config sample uses the canonical top-level model alias.
590
- // Set it only when absent so user-selected models are preserved.
591
- next = upsertTopLevelTomlStringIfAbsent(next, 'model', REQUIRED_CODEX_MODEL);
592
588
  next = upsertTopLevelTomlBooleanIfAbsent(next, 'suppress_unstable_features_warning', true);
593
589
  // Codex App feature flags: SET-IF-ABSENT only (see note above); flags the
594
590
  // 2026-07 renewal removed from the schema are stripped.
@@ -600,7 +596,10 @@ export async function initProject(root, opts = {}) {
600
596
  }
601
597
  next = removeWholeTomlTable(next, 'user.fast_mode');
602
598
  next = removeWholeTomlTable(next, 'profiles.sks-fast-high');
603
- next = upsertTomlTableKeyIfAbsent(next, 'agents', 'max_threads = 6');
599
+ if (process.env.SKS_ALLOW_HIGH_AGENT_CONCURRENCY !== '1') {
600
+ next = upsertTomlTableKey(next, 'agents', 'max_threads = 4');
601
+ }
602
+ next = removeWholeTomlTable(next, 'features.multi_agent_v2');
604
603
  next = upsertTomlTableKeyIfAbsent(next, 'agents', 'max_depth = 1');
605
604
  for (const block of managedCodexConfigBlocks()) {
606
605
  if (block.preserveExisting === true && hasTomlTable(next, block.table))
@@ -711,16 +710,6 @@ export async function initProject(root, opts = {}) {
711
710
  }
712
711
  return lines.filter((_, index) => index < start || index >= end).join('\n').replace(/^\n+/, '').replace(/\n{3,}/g, '\n\n');
713
712
  }
714
- function removeLegacyTopLevelCodexModeLocks(text = '') {
715
- const lines = String(text || '').split('\n');
716
- const firstTable = lines.findIndex((x) => /^\s*\[.+\]\s*$/.test(x));
717
- const end = firstTable === -1 ? lines.length : firstTable;
718
- return lines.filter((line, index) => {
719
- if (index >= end)
720
- return true;
721
- return !/^\s*(?:model|model_reasoning_effort)\s*=/.test(line);
722
- }).join('\n').replace(/^\n+/, '').replace(/\n{3,}/g, '\n\n');
723
- }
724
713
  function removeTopLevelTomlKeyIfValue(text = '', key = '', value = '') {
725
714
  const lines = String(text || '').split('\n');
726
715
  const firstTable = lines.findIndex((x) => /^\s*\[.+\]\s*$/.test(x));
@@ -766,9 +755,6 @@ export async function initProject(root, opts = {}) {
766
755
  return true;
767
756
  return false;
768
757
  }
769
- function upsertTopLevelTomlStringIfAbsent(text, key, value) {
770
- return hasTopLevelTomlKey(text, key) ? String(text || '') : upsertTopLevelTomlString(text, key, value);
771
- }
772
758
  function upsertTopLevelTomlBooleanIfAbsent(text, key, value) {
773
759
  return hasTopLevelTomlKey(text, key) ? String(text || '') : upsertTopLevelTomlBoolean(text, key, value);
774
760
  }
@@ -8,13 +8,15 @@ export function computeLoopConcurrencyBudget(input) {
8
8
  ? 'codex-0140-usage'
9
9
  : 'sks-local-estimate';
10
10
  const cores = Math.max(1, os.cpus().length || 1);
11
- const requestedLoops = input.parallelism === 'safe' ? 2 : input.parallelism === 'extreme' ? Math.min(16, cores) : Math.min(8, cores);
11
+ const requestedLoops = input.parallelism === 'safe' ? 1 : input.parallelism === 'extreme' ? Math.min(4, cores) : Math.min(2, cores);
12
12
  const envLoops = positiveInt(env.SKS_LOOP_MAX_ACTIVE_LOOPS);
13
13
  const envWorkers = positiveInt(env.SKS_LOOP_MAX_ACTIVE_WORKERS);
14
14
  const envModelCalls = positiveInt(env.SKS_LOOP_MAX_MODEL_CALLS);
15
- const maxActiveLoops = envLoops || requestedLoops;
16
- const maxActiveWorkers = envWorkers || (input.parallelism === 'safe' ? Math.min(8, cores) : input.parallelism === 'extreme' ? Math.min(32, Math.max(4, cores * 2)) : Math.min(16, Math.max(4, cores)));
17
- const maxModelCalls = envModelCalls || Math.max(1, Math.min(maxActiveWorkers, input.plan.global_budget.max_model_calls || maxActiveWorkers));
15
+ const maxActiveLoops = Math.min(requestedLoops, envLoops || requestedLoops);
16
+ const requestedWorkers = input.parallelism === 'safe' ? Math.min(2, cores) : input.parallelism === 'extreme' ? Math.min(4, cores) : Math.min(3, cores);
17
+ const maxActiveWorkers = Math.min(requestedWorkers, envWorkers || requestedWorkers);
18
+ const requestedModelCalls = Math.max(1, Math.min(maxActiveWorkers, input.plan.global_budget.max_model_calls || maxActiveWorkers));
19
+ const maxModelCalls = Math.min(requestedModelCalls, envModelCalls || requestedModelCalls);
18
20
  let remainingWorkers = maxActiveWorkers;
19
21
  let remainingModelCalls = maxModelCalls;
20
22
  const perLoop = input.plan.graph.nodes.map((node) => {
@@ -7,7 +7,7 @@ import { classifyLoopRisk } from './loop-risk-classifier.js';
7
7
  import { defaultLoopBudget, validateLoopPlan } from './loop-schema.js';
8
8
  import { readInitDeepMemory, readInitDeepMemoryHints } from '../codex-app/codex-init-deep.js';
9
9
  export async function planLoopsFromRequest(input) {
10
- const parallelism = input.parallelism || 'balanced';
10
+ const parallelism = input.parallelism || 'safe';
11
11
  const maxLoops = Math.max(1, Math.min(32, input.maxLoops || 8));
12
12
  const allDomains = decomposeRequestIntoLoopDomains(input.request);
13
13
  const domains = allDomains.slice(0, maxLoops);
@@ -12,9 +12,9 @@ import { runLoopCheckerWorkers, runLoopMakerWorkers } from './loop-worker-runtim
12
12
  import { allocateLoopWorktree, computeLoopDiff } from './loop-worktree-runtime.js';
13
13
  export async function runLoopPlan(input) {
14
14
  const started = Date.now();
15
- const concurrencyBudget = computeLoopConcurrencyBudget({ plan: input.plan, parallelism: input.parallelism || 'balanced' });
15
+ const concurrencyBudget = computeLoopConcurrencyBudget({ plan: input.plan, parallelism: input.parallelism || 'safe' });
16
16
  await writeLoopConcurrencyBudget(input.root, concurrencyBudget);
17
- const schedule = scheduleLoopGraph(input.plan.graph.nodes, input.parallelism || 'balanced', concurrencyBudget);
17
+ const schedule = scheduleLoopGraph(input.plan.graph.nodes, input.parallelism || 'safe', concurrencyBudget);
18
18
  const proofs = [];
19
19
  for (const batch of schedule.batches) {
20
20
  if (await shouldKillLoop(input.root, input.plan.mission_id, 'all'))
@@ -1,5 +1,5 @@
1
1
  import os from 'node:os';
2
- export function scheduleLoopGraph(nodes, parallelism = 'balanced', budget) {
2
+ export function scheduleLoopGraph(nodes, parallelism = 'safe', budget) {
3
3
  const pending = new Map(nodes.map((node) => [node.loop_id, node]));
4
4
  const completed = new Set();
5
5
  const batches = [];
@@ -23,21 +23,21 @@ export function scheduleLoopGraph(nodes, parallelism = 'balanced', budget) {
23
23
  }
24
24
  return { ok: blockers.length === 0, batches, max_active_loops: Math.max(0, ...batches.map((batch) => batch.length)), blockers };
25
25
  }
26
- export function maxConcurrentLoops(nodes, parallelism = 'balanced') {
26
+ export function maxConcurrentLoops(nodes, parallelism = 'safe') {
27
27
  const cores = Math.max(1, os.cpus().length || 1);
28
- const base = parallelism === 'safe' ? 2 : parallelism === 'extreme' ? Math.min(16, cores) : Math.min(8, cores);
28
+ const base = parallelism === 'safe' ? 1 : parallelism === 'extreme' ? Math.min(4, cores) : Math.min(2, cores);
29
29
  return Math.max(1, Math.min(base, riskAwareLoopCap(nodes, parallelism, cores)));
30
30
  }
31
31
  function riskAwareLoopCap(nodes, parallelism, cores) {
32
32
  if (parallelism === 'extreme')
33
- return Math.min(16, cores);
33
+ return Math.min(4, cores);
34
34
  const hasCritical = nodes.some((node) => node.risk.level === 'critical');
35
35
  const hasHigh = nodes.some((node) => node.risk.level === 'high');
36
36
  if (hasCritical)
37
- return parallelism === 'safe' ? 1 : Math.max(2, Math.floor(cores / 4));
37
+ return 1;
38
38
  if (hasHigh)
39
- return parallelism === 'safe' ? 2 : Math.max(4, Math.floor(cores / 2));
40
- return parallelism === 'safe' ? 2 : Math.min(8, cores);
39
+ return parallelism === 'safe' ? 1 : Math.min(2, cores);
40
+ return parallelism === 'safe' ? 1 : parallelism === 'balanced' ? Math.min(2, cores) : Math.min(4, cores);
41
41
  }
42
42
  export function graphProofFromLoopProofs(input) {
43
43
  const selected = [...new Set(input.proofs.flatMap((proof) => proof.gate_result.selected_gates))];
@@ -1,4 +1,3 @@
1
- import { REQUIRED_CODEX_MODEL, DEFAULT_CODEX_REASONING_EFFORT } from '../codex-model-guard.js';
2
1
  export const MANAGED_ASSET_SCHEMA_VERSION = 1;
3
2
  export const MANAGED_ASSET_VERSION = '4.8.1';
4
3
  export const MANAGED_ASSET_MARKER = 'SKS-MANAGED-ASSET';
@@ -63,8 +62,6 @@ export function managedAgentRoleContent(role) {
63
62
  `# sks_managed_version = "${MANAGED_ASSET_VERSION}"`,
64
63
  `name = "${role.codex_name}"`,
65
64
  `description = "${role.description}"`,
66
- `model = "${REQUIRED_CODEX_MODEL}"`,
67
- `model_reasoning_effort = "${DEFAULT_CODEX_REASONING_EFFORT}"`,
68
65
  `sandbox_mode = "${role.sandbox}"`,
69
66
  `permission_profile = "${role.permission_profile}"`,
70
67
  `legacy_sandbox_projection = "${role.legacy_sandbox_projection}"`,
@@ -83,7 +80,7 @@ export function managedAgentRoleOwnsText(text, role) {
83
80
  && text.includes(`sandbox_mode = "${role.sandbox}"`);
84
81
  const legacyCompatible = text.includes(`name = "${role.codex_name}"`)
85
82
  && text.includes(`sandbox_mode = "${role.sandbox}"`)
86
- && text.includes(`model = "${REQUIRED_CODEX_MODEL}"`);
83
+ && text.includes('developer_instructions = """');
87
84
  return hasManagedMarker || legacyCompatible;
88
85
  }
89
86
  export function normalizeRoleName(name) {