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.
- package/README.md +2 -1
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/bin/fast-inline.js +0 -60
- package/dist/bin/sks-dispatch.js +0 -24
- package/dist/cli/codex-app-command.js +1 -2
- package/dist/cli/help-fast.js +2 -2
- package/dist/cli/install-helpers.js +25 -31
- package/dist/config/skills-manifest.json +57 -57
- package/dist/core/agents/agent-effort-policy.js +19 -38
- package/dist/core/agents/agent-orchestrator.js +2 -2
- package/dist/core/agents/agent-role-config.js +17 -10
- package/dist/core/agents/agent-roster.js +19 -39
- package/dist/core/agents/agent-scheduler.js +6 -5
- package/dist/core/agents/agent-schema.js +4 -5
- package/dist/core/agents/agent-task-graph.js +1 -1
- package/dist/core/agents/agent-work-partition.js +2 -1
- package/dist/core/agents/native-worker-backend-router.js +1 -1
- package/dist/core/agents/route-collaboration-ledger.js +2 -2
- package/dist/core/auto-review.js +1 -4
- package/dist/core/codex/codex-config-guard.js +9 -13
- package/dist/core/codex-adapter.js +2 -2
- package/dist/core/codex-app/codex-agent-role-sync.js +0 -1
- package/dist/core/codex-app/codex-app-fast-ui-repair.js +6 -40
- package/dist/core/codex-app/codex-app-ui-state-snapshot.js +1 -7
- package/dist/core/codex-control/codex-model-capabilities.js +1 -2
- package/dist/core/codex-control/codex-model-metadata.js +13 -6
- package/dist/core/codex-control/codex-sdk-config-policy.js +3 -3
- package/dist/core/codex-control/codex-task-runner.js +2 -3
- package/dist/core/codex-model-guard.js +24 -87
- package/dist/core/commands/basic-cli.js +6 -0
- package/dist/core/commands/loop-command.js +2 -2
- package/dist/core/commands/naruto-command.js +11 -11
- package/dist/core/commands/profile-command.js +14 -5
- package/dist/core/commands/qa-loop-command.js +2 -2
- package/dist/core/commands/research-command.js +2 -2
- package/dist/core/doctor/doctor-codex-startup-repair.js +0 -3
- package/dist/core/evaluation.js +1 -1
- package/dist/core/feature-fixtures.js +2 -2
- package/dist/core/feature-registry.js +1 -1
- package/dist/core/hooks-runtime.js +7 -45
- package/dist/core/image-ux-review/imagegen-adapter.js +17 -2
- package/dist/core/init/skills.js +6 -7
- package/dist/core/init.js +6 -20
- package/dist/core/loops/loop-concurrency-budget.js +6 -4
- package/dist/core/loops/loop-planner.js +1 -1
- package/dist/core/loops/loop-runtime.js +2 -2
- package/dist/core/loops/loop-scheduler.js +7 -7
- package/dist/core/managed-assets/managed-assets-manifest.js +1 -4
- package/dist/core/naruto/naruto-concurrency-governor.js +20 -17
- package/dist/core/naruto/naruto-loop-mesh.js +3 -3
- package/dist/core/naruto/resource-pressure-monitor.js +5 -2
- package/dist/core/provider/model-router.js +15 -35
- package/dist/core/release/release-gate-batch-runner.js +2 -1
- package/dist/core/release/release-gate-resource-governor.js +12 -12
- package/dist/core/release/resource-class-budget.js +5 -5
- package/dist/core/routes/constants.js +3 -4
- package/dist/core/routes.js +1 -1
- package/dist/core/structured-output-adapter.js +15 -1
- package/dist/core/update/update-migration-state.js +3 -32
- package/dist/core/version.js +1 -1
- package/dist/scripts/agent-backfill-replenishment-check.js +1 -1
- package/dist/scripts/agent-dynamic-pool-check.js +3 -3
- package/dist/scripts/agent-dynamic-pool-fixture.js +1 -1
- package/dist/scripts/agent-goal-mode-propagation-check.js +1 -1
- package/dist/scripts/agent-patch-swarm-gate-lib.js +2 -2
- package/dist/scripts/agent-role-config-repair-check.js +9 -5
- package/dist/scripts/agent-scheduler-proof-check.js +1 -1
- package/dist/scripts/agent-scheduler-proof-hardening-check.js +1 -1
- package/dist/scripts/agent-source-intelligence-propagation-check.js +1 -1
- package/dist/scripts/agent-strategy-to-lease-wiring-check.js +2 -2
- package/dist/scripts/agent-zellij-dynamic-backfill-panes-check.js +1 -1
- package/dist/scripts/codex-app-fast-ui-preservation-check.js +2 -2
- package/dist/scripts/codex-app-ui-preservation-check.js +6 -5
- package/dist/scripts/codex-lb-fast-ui-preservation-check.js +3 -3
- package/dist/scripts/codex-lb-gpt55-fast-profile-check.js +2 -3
- package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +7 -7
- package/dist/scripts/install-update-preserves-config-check.js +4 -4
- package/dist/scripts/loop-hardening-check-lib.js +1 -1
- package/dist/scripts/naruto-parallelism-mode-check.js +2 -1
- package/dist/scripts/naruto-shadow-clone-swarm-check.js +25 -34
- package/dist/scripts/npm-publish-performance-check.js +1 -1
- package/dist/scripts/release-aggressive-resource-governor-check.js +2 -2
- package/dist/scripts/release-parallel-check.js +1 -2
- package/dist/scripts/release-real-check.js +1 -1
- package/dist/scripts/sizecheck.js +3 -3
- 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', '
|
|
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', '
|
|
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
|
|
223
|
-
const
|
|
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
|
-
|
|
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
|
|
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', '
|
|
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 || '
|
|
1072
|
+
const text = String(value || 'safe').toLowerCase();
|
|
1073
1073
|
if (text === 'safe' || text === 'balanced' || text === 'extreme')
|
|
1074
1074
|
return text;
|
|
1075
|
-
return '
|
|
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
|
|
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:
|
|
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
|
-
|
|
14
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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 = """',
|
package/dist/core/evaluation.js
CHANGED
|
@@ -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:
|
|
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
|
|
220
|
-
'route-native-agent-intake': fixture('execute_and_validate_artifacts', 'sks agent run "fixture" --route "$Team" --agents 5 --concurrency
|
|
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
|
|
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
|
|
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: '
|
|
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 ||
|
|
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);
|
package/dist/core/init/skills.js
CHANGED
|
@@ -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."\
|
|
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."\
|
|
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."\
|
|
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."\
|
|
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."\
|
|
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."\
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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' ?
|
|
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
|
|
17
|
-
const
|
|
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 || '
|
|
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 || '
|
|
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 || '
|
|
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 = '
|
|
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 = '
|
|
26
|
+
export function maxConcurrentLoops(nodes, parallelism = 'safe') {
|
|
27
27
|
const cores = Math.max(1, os.cpus().length || 1);
|
|
28
|
-
const base = parallelism === 'safe' ?
|
|
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(
|
|
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
|
|
37
|
+
return 1;
|
|
38
38
|
if (hasHigh)
|
|
39
|
-
return parallelism === 'safe' ?
|
|
40
|
-
return parallelism === 'safe' ? 2 : Math.min(
|
|
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(
|
|
83
|
+
&& text.includes('developer_instructions = """');
|
|
87
84
|
return hasManagedMarker || legacyCompatible;
|
|
88
85
|
}
|
|
89
86
|
export function normalizeRoleName(name) {
|