sneakoscope 10.3.0 → 10.3.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 CHANGED
@@ -16,7 +16,7 @@
16
16
  Sneakoscope Codex (`sks`) is an open-source trust layer for Codex CLI and ChatGPT Desktop. It coordinates bounded AI coding agents, records machine-verifiable evidence, preserves project memory, and blocks release claims that are not supported by current tests or artifacts. Search visibility outcomes are measured separately; SKS does not promise rankings or traffic.
17
17
  <!-- END SKS SEARCH VISIBILITY MARKETING -->
18
18
 
19
- Current package: **SKS 10.3.0**. Install the latest stable release from npm.
19
+ Current package: **SKS 10.3.2**. Install the latest stable release from npm.
20
20
 
21
21
  [Quick start](#install-in-one-command) · [Commands](#everyday-commands) · [SKS Center](#sks-center-macos) · [Documentation](#documentation) · [Changelog](CHANGELOG.md)
22
22
 
@@ -259,7 +259,7 @@ dependencies = [
259
259
 
260
260
  [[package]]
261
261
  name = "sks-core"
262
- version = "10.3.0"
262
+ version = "10.3.2"
263
263
  dependencies = [
264
264
  "globset",
265
265
  "grep-matcher",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "10.3.0"
3
+ version = "10.3.2"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -4,6 +4,14 @@ import { escapeRegExp } from '../core/text/regex.js';
4
4
  export const DESKTOP_BRIDGE_MANAGED_MARKER = '# sks-desktop-bridge-managed';
5
5
  export const DESKTOP_BRIDGE_MANAGED_BASE_URL_MARKER = '# sks-desktop-bridge-managed-base-url';
6
6
  export const DESKTOP_BRIDGE_MANAGED_MODEL_CATALOG_MARKER = '# sks-desktop-bridge-managed-model-catalog';
7
+ export function bindCodexClientProvider(text, provider) {
8
+ const selected = topLevelTomlString(text, 'model_provider');
9
+ if (selected && selected !== 'openai' && selected !== 'codex-lb')
10
+ return text;
11
+ if (selected === provider)
12
+ return text;
13
+ return upsertTopLevelTomlString(String(text || ''), 'model_provider', provider);
14
+ }
7
15
  export function upsertDesktopBridgeManagedConfig(text, input) {
8
16
  const bridgeBaseUrl = normalizeManagedBridgeBaseUrl(input.bridgeBaseUrl);
9
17
  const combinedCatalogPath = String(input.combinedCatalogPath || '').trim();
@@ -16,7 +24,8 @@ export function upsertDesktopBridgeManagedConfig(text, input) {
16
24
  const orphanCleanup = removeDesktopBridgeOrphanManagedMarkers(String(text || ''));
17
25
  let next = orphanCleanup.text;
18
26
  const selectedProvider = topLevelTomlString(next, 'model_provider');
19
- if (selectedProvider && (selectedProvider !== 'openai'
27
+ const managedProvider = selectedProvider === 'codex-lb' || selectedProvider === 'openai';
28
+ if (selectedProvider && (!managedProvider
20
29
  || !topLevelHasLine(next, DESKTOP_BRIDGE_MANAGED_MARKER))) {
21
30
  throw new Error('historical_user_owned_config_conflict:model_provider');
22
31
  }
@@ -30,8 +39,9 @@ export function upsertDesktopBridgeManagedConfig(text, input) {
30
39
  if (existingCatalog && !catalogOwned) {
31
40
  throw new Error('historical_user_owned_config_conflict:model_catalog_json');
32
41
  }
42
+ const keepCodexLb = selectedProvider === 'codex-lb';
33
43
  next = removeManagedBridgeTopLevelBindings(next);
34
- next = upsertTopLevelTomlString(next, 'model_provider', 'openai');
44
+ next = upsertTopLevelTomlString(next, 'model_provider', keepCodexLb ? 'codex-lb' : 'openai');
35
45
  next = addTopLevelMarkerBeforeKey(next, 'model_provider', DESKTOP_BRIDGE_MANAGED_MARKER);
36
46
  next = upsertTopLevelTomlString(next, 'openai_base_url', bridgeBaseUrl);
37
47
  next = addTopLevelMarkerBeforeKey(next, 'openai_base_url', DESKTOP_BRIDGE_MANAGED_BASE_URL_MARKER);
@@ -45,7 +55,7 @@ export function removeDesktopBridgeManagedConfig(text) {
45
55
  const baseUrl = topLevelTomlString(next, 'openai_base_url');
46
56
  const catalog = topLevelTomlString(next, 'model_catalog_json');
47
57
  const owned = [
48
- [DESKTOP_BRIDGE_MANAGED_MARKER, 'model_provider', selectedProvider === 'openai'],
58
+ [DESKTOP_BRIDGE_MANAGED_MARKER, 'model_provider', selectedProvider === 'openai' || selectedProvider === 'codex-lb'],
49
59
  [DESKTOP_BRIDGE_MANAGED_BASE_URL_MARKER, 'openai_base_url', Boolean(baseUrl)],
50
60
  [DESKTOP_BRIDGE_MANAGED_MODEL_CATALOG_MARKER, 'model_catalog_json', Boolean(catalog)]
51
61
  ];
@@ -67,7 +77,7 @@ export function removeDesktopBridgeOrphanManagedMarkers(text) {
67
77
  const source = String(text || '');
68
78
  const selectedProvider = topLevelTomlString(source, 'model_provider');
69
79
  const markerTargets = [
70
- [DESKTOP_BRIDGE_MANAGED_MARKER, selectedProvider === 'openai'],
80
+ [DESKTOP_BRIDGE_MANAGED_MARKER, selectedProvider === 'openai' || selectedProvider === 'codex-lb'],
71
81
  [DESKTOP_BRIDGE_MANAGED_BASE_URL_MARKER, Boolean(topLevelTomlString(source, 'openai_base_url'))],
72
82
  [DESKTOP_BRIDGE_MANAGED_MODEL_CATALOG_MARKER, Boolean(topLevelTomlString(source, 'model_catalog_json'))]
73
83
  ];
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema": "sks.skills-manifest.v1",
3
- "package_version": "10.3.0",
3
+ "package_version": "10.3.2",
4
4
  "skills": [
5
5
  {
6
6
  "canonical_name": "sks",
@@ -1,3 +1,6 @@
1
+ import { bindCodexClientProvider } from '../../../cli/install-helpers-codex-lb-config.js';
2
+ import { safeWriteCodexConfigToml } from '../../codex-runtime/codex-desktop-config-policy.js';
3
+ import { readText } from '../../fsx.js';
1
4
  import { testOpenRouterConnection } from '../../providers/openrouter/openrouter-account.js';
2
5
  import { openRouterSecretPaths } from '../../providers/openrouter/openrouter-secret-store.js';
3
6
  import { codexLbEnvPath, codexLbMetadataPath, readCodexLbModelCatalog } from '../codex-lb-env.js';
@@ -108,6 +111,12 @@ export async function setProviderState(providerId, enabled, operation, options)
108
111
  });
109
112
  }
110
113
  }
114
+ if (providerId === 'codex-lb') {
115
+ const current = await readText(paths.configPath, '');
116
+ const next = bindCodexClientProvider(current, enabled ? 'codex-lb' : 'openai');
117
+ if (next !== current)
118
+ await safeWriteCodexConfigToml(paths.configPath, current, next, 'codex-lb-client-provider');
119
+ }
111
120
  const status = await desktopBridgeStatusV3(options);
112
121
  return commandResult(operation, true, status, { provider_id: providerId, enabled }, [], options);
113
122
  }
@@ -8,7 +8,7 @@ import { applyRecoveryEffect, listProductionRecoveryCandidates } from './recover
8
8
  import { assembleRoutingSelection, buildRoutingCandidates } from './routing.js';
9
9
  import { buildDecisionReceipt } from './receipt.js';
10
10
  import { applyOptionalContextSelection, graphFileDigest, hydrateContextCandidates, sourceSnapshotDigest } from './state.js';
11
- import { DESIGN_DEFAULTS, POLICY_REVISION, UNKNOWN_USAGE } from './types.js';
11
+ import { DESIGN_DEFAULTS, POLICY_REVISION, UNKNOWN_USAGE, sealedRoutingModel } from './types.js';
12
12
  let testOverrides = null;
13
13
  const appliedEffects = new Set();
14
14
  export function setDecisionTestOverrides(overrides) {
@@ -101,6 +101,7 @@ async function decideOfficialSubagentPreparationInner(input, started) {
101
101
  selectedPlan: null,
102
102
  selectedRouting: null,
103
103
  selectedContextIds: null,
104
+ omittedRoutingRoles: [],
104
105
  consumptionEvidence: null
105
106
  };
106
107
  const receipt = buildDecisionReceipt({
@@ -122,12 +123,51 @@ async function decideOfficialSubagentPreparationInner(input, started) {
122
123
  selectedPlan: applied.selectedPlan,
123
124
  selectedRouting: applied.selectedRouting,
124
125
  selectedContextIds: applied.selectedContextIds,
126
+ omittedRoutingRoles: applied.omittedRoutingRoles,
125
127
  llmRejudgeCalls: 0,
126
128
  bundle
127
129
  };
128
130
  overrides?.observe?.({ mode: config.mode, eligible: true, compiled, receipt });
129
131
  return result;
130
132
  }
133
+ export async function consultJevTurnModel(input) {
134
+ const overrides = activeOverrides();
135
+ const env = input.env || process.env;
136
+ const config = overrides?.config ?? await readDecisionConfig(env);
137
+ const none = (reason) => ({ called: false, model: null, effort: null, reason });
138
+ if (!jevEnabled(config))
139
+ return none('off');
140
+ const resolved = await resolveOpenRouterApiKey({ env });
141
+ if (!resolved.key)
142
+ return none('missing_key');
143
+ const prompt = String(input.prompt || '').trim();
144
+ if (!prompt)
145
+ return none('empty_prompt');
146
+ const roleId = input.roleId === 'spawn' ? 'spawn' : 'turn';
147
+ const bundle = buildDecisionBundle({
148
+ projectId: sha256(input.root).slice(0, 32),
149
+ workflowRunId: roleId,
150
+ workflowRevision: roleId,
151
+ sourceDigest: roleId,
152
+ graphDigest: null,
153
+ goal: prompt,
154
+ routingCandidates: [{ id: roleId, summary: prompt.slice(0, 240) }]
155
+ });
156
+ const transport = await requestOpenRouterDecision(bundle, {
157
+ env,
158
+ deadlineMs: DESIGN_DEFAULTS.deadlineMs,
159
+ ...(overrides?.fetchImpl ? { fetchImpl: overrides.fetchImpl } : {})
160
+ });
161
+ if (!transport.ok)
162
+ return { called: true, model: null, effort: null, reason: transport.reason };
163
+ const compiled = compileDecision(bundle, transport.response);
164
+ if (compiled.kind !== 'apply')
165
+ return { called: true, model: null, effort: null, reason: compiled.reason };
166
+ const selected = assembleRoutingSelection(compiled.effects.flatMap((effect) => (effect.kind === 'select_routing' ? [{ roleId: effect.roleId, model: effect.model }] : [])));
167
+ const model = selected?.models[roleId] || null;
168
+ const sealed = model ? sealedRoutingModel(model) : null;
169
+ return { called: true, model: sealed?.id || null, effort: sealed?.effort || null, reason: sealed ? 'applied' : 'keep_baseline' };
170
+ }
131
171
  export function effectAlreadyConsumed(identity) {
132
172
  return appliedEffects.has(identity);
133
173
  }
@@ -157,6 +197,7 @@ function applyEffects(attention, contextCandidates, planCandidates, compiled) {
157
197
  let selectedContextIds = null;
158
198
  const evidence = [];
159
199
  const routingEffects = [];
200
+ const omittedRoutingRoles = [];
160
201
  for (const effect of compiled.effects) {
161
202
  if (effect.kind === 'select_optional_context') {
162
203
  nextAttention = applyOptionalContextSelection(attention, contextCandidates, effect.keepIds);
@@ -171,6 +212,10 @@ function applyEffects(attention, contextCandidates, planCandidates, compiled) {
171
212
  if (effect.kind === 'select_routing') {
172
213
  routingEffects.push({ roleId: effect.roleId, model: effect.model });
173
214
  }
215
+ if (effect.kind === 'omit_role') {
216
+ omittedRoutingRoles.push(effect.roleId);
217
+ evidence.push(`omit:${effect.roleId}`);
218
+ }
174
219
  }
175
220
  const selectedRouting = assembleRoutingSelection(routingEffects);
176
221
  if (selectedRouting) {
@@ -181,6 +226,7 @@ function applyEffects(attention, contextCandidates, planCandidates, compiled) {
181
226
  selectedPlan,
182
227
  selectedRouting,
183
228
  selectedContextIds,
229
+ omittedRoutingRoles,
184
230
  consumptionEvidence: evidence.length ? evidence.join('|') : null
185
231
  };
186
232
  }
@@ -256,6 +302,7 @@ function keep(input, reason, started) {
256
302
  selectedPlan: null,
257
303
  selectedRouting: null,
258
304
  selectedContextIds: null,
305
+ omittedRoutingRoles: [],
259
306
  llmRejudgeCalls: 0,
260
307
  bundle: null
261
308
  };
@@ -1,4 +1,4 @@
1
- import { CHOICE_MIN_CONFIDENCE, CHOICE_MIN_PROBABILITY, CONTEXT_DROP_NOUL_MAX, DISTRIBUTION_SUM_TOLERANCE, KEEP_BASELINE_CHOICE, NEEDS_EVIDENCE_CHOICE, POLICY_REVISION, ROUTING_RISK_NOUL_MIN, UNKNOWN_USAGE, sealedRoutingModel } from './types.js';
1
+ import { CHOICE_MIN_CONFIDENCE, CHOICE_MIN_PROBABILITY, CONTEXT_DROP_NOUL_MAX, DISTRIBUTION_SUM_TOLERANCE, KEEP_BASELINE_CHOICE, NEEDS_EVIDENCE_CHOICE, POLICY_REVISION, ROUTING_RISK_NOUL_MIN, ROUTING_ROLE_OMIT_NOUL_MAX, UNKNOWN_USAGE, sealedRoutingModel } from './types.js';
2
2
  export function compileDecision(bundle, response) {
3
3
  const usage = usageFromWire(response.usage);
4
4
  const decoded = decodeWireResponse(bundle, response);
@@ -151,6 +151,16 @@ function compileRouting(bundle, answers) {
151
151
  else if (!reason)
152
152
  reason = compiled.reason;
153
153
  }
154
+ for (const [questionId, binding] of Object.entries(bundle.questionBindings)) {
155
+ if (binding.kind !== 'routing_needed')
156
+ continue;
157
+ const answer = answers[questionId];
158
+ if (!answer || answer.type !== 'noul')
159
+ continue;
160
+ if (answer.noul <= ROUTING_ROLE_OMIT_NOUL_MAX) {
161
+ effects.push({ kind: 'omit_role', roleId: binding.roleId });
162
+ }
163
+ }
154
164
  return { effects, reason };
155
165
  }
156
166
  function compileRoleRouting(bundle, answers, roleId) {
@@ -210,6 +210,20 @@ function appendRoutingSpeculation(roles, questions, bindings, recoverySlot) {
210
210
  }
211
211
  };
212
212
  bindings[riskId] = { kind: 'routing_risk', roleId: role.id };
213
+ if (questionRoom(questions, recoverySlot) < 1)
214
+ break;
215
+ const neededId = `needed_${role.id}`;
216
+ const neededInstructions = redactDecisionText(`Does state.task require routing.roles.${role.id} as its own child agent? Omit the role when another listed role already covers the work.`);
217
+ assertIndependentQuestion(neededInstructions);
218
+ questions[neededId] = {
219
+ type: 'noul',
220
+ instructions: neededInstructions,
221
+ criteria: {
222
+ true: 'This role must be spawned to finish the task.',
223
+ false: 'This role is unnecessary. The parent should not spawn it.'
224
+ }
225
+ };
226
+ bindings[neededId] = { kind: 'routing_needed', roleId: role.id };
213
227
  }
214
228
  }
215
229
  export { QUESTION_REVISION };
@@ -27,6 +27,7 @@ export const ROUTING_DIFFICULTY_RUBRIC = Object.freeze([
27
27
  'Judgment, architecture, ambiguity, security, or other high-stakes work.'
28
28
  ]);
29
29
  export const ROUTING_RISK_NOUL_MIN = 0.70;
30
+ export const ROUTING_ROLE_OMIT_NOUL_MAX = 0.35;
30
31
  export function sealedRoutingModel(model) {
31
32
  const match = SEALED_ROUTING_MODELS.find((row) => row.id === model);
32
33
  return match ? { id: match.id, effort: match.effort } : null;
@@ -1,6 +1,6 @@
1
1
  import { IMAGEGEN_MODEL } from '../imagegen/imagegen-model-policy.js';
2
2
  import { readStdin } from '../fsx.js';
3
- import { buildCompactContinue, buildPermissionRequestAllow, buildPermissionRequestDeny, buildPostToolUseBlock, buildPostToolUseContinue, buildPreToolUseContinue, buildPreToolUseDeny, buildSessionStartContinue, buildStopBlock, buildStopContinue, buildSubagentStartContinue, buildSubagentStopBlock, buildSubagentStopContinue, buildUserPromptSubmitBlock, buildUserPromptSubmitContinue } from '../codex-compat/codex-hook-output-builders.js';
3
+ import { buildCompactContinue, buildPermissionRequestAllow, buildPermissionRequestDeny, buildPostToolUseBlock, buildPostToolUseContinue, buildPreToolUseAllowRewrite, buildPreToolUseContinue, buildPreToolUseDeny, buildSessionStartContinue, buildStopBlock, buildStopContinue, buildSubagentStartContinue, buildSubagentStopBlock, buildSubagentStopContinue, buildUserPromptSubmitBlock, buildUserPromptSubmitContinue } from '../codex-compat/codex-hook-output-builders.js';
4
4
  export async function loadHookPayload() {
5
5
  const raw = await readStdin();
6
6
  try {
@@ -33,6 +33,8 @@ export function normalizeHookResult(name, result = {}) {
33
33
  if (out.decision === 'block' || out.permissionDecision === 'deny' || out.decision === 'deny') {
34
34
  return buildPreToolUseDeny(reason, { systemMessage });
35
35
  }
36
+ if (out.updatedInput)
37
+ return buildPreToolUseAllowRewrite(out.updatedInput, { systemMessage });
36
38
  return buildPreToolUseContinue({ additionalContext: out.additionalContext, systemMessage });
37
39
  }
38
40
  if (eventName === 'PermissionRequest') {
@@ -0,0 +1,50 @@
1
+ import { consultJevTurnModel } from '../decisions/integration.js';
2
+ import { readRoleModelPreferences } from '../subagents/role-model-preferences.js';
3
+ const SPAWN_TOOLS = new Set(['spawn_agent', 'collaboration.spawn_agent', 'functions.spawn_agent']);
4
+ function spawnInput(payload) {
5
+ const name = String(payload?.tool_name || payload?.toolName || payload?.tool?.name || '');
6
+ if (!SPAWN_TOOLS.has(name))
7
+ return null;
8
+ const input = payload?.tool_input || payload?.toolInput || payload?.tool?.input;
9
+ if (!input || typeof input !== 'object' || Array.isArray(input))
10
+ return null;
11
+ return { ...input };
12
+ }
13
+ function narutoParent(state) {
14
+ const mode = String(state?.mode || '').toUpperCase();
15
+ const route = String(state?.route || state?.route_command || '').replace(/^\$/, '').toUpperCase();
16
+ return mode === 'NARUTO' || route === 'NARUTO' || state?.subagents_required === true;
17
+ }
18
+ function spawnTask(input) {
19
+ return [input.message, input.prompt, input.task, input.agent_type, input.agentType]
20
+ .map((value) => String(value || '').trim())
21
+ .filter(Boolean)
22
+ .join('\n')
23
+ .slice(0, 1200);
24
+ }
25
+ export async function jevSpawnModelRewrite(root, state, payload) {
26
+ if (!narutoParent(state))
27
+ return null;
28
+ const input = spawnInput(payload);
29
+ if (!input)
30
+ return null;
31
+ const agent = String(input.agent_type || input.agentType || '').trim();
32
+ if (agent) {
33
+ const preferences = await readRoleModelPreferences().catch(() => null);
34
+ if (preferences?.store.roles[agent])
35
+ return null;
36
+ }
37
+ const task = spawnTask(input);
38
+ if (!task)
39
+ return null;
40
+ const decision = await consultJevTurnModel({ root, prompt: task, roleId: 'spawn' }).catch(() => null);
41
+ if (!decision?.model || !decision.effort)
42
+ return null;
43
+ if (input.model === decision.model && input.reasoning_effort === decision.effort)
44
+ return null;
45
+ return {
46
+ ...input,
47
+ model: decision.model,
48
+ reasoning_effort: decision.effort
49
+ };
50
+ }
@@ -22,6 +22,7 @@ import { codePackFreshnessNote } from './hooks-runtime/code-pack-freshness-prefl
22
22
  import { claimHookInvocation } from './hooks-runtime/hook-invocation-dedupe.js';
23
23
  import { armLightTurnStopBypass, clearLightTurnStopBypass, consumeLightTurnStopBypass, hasMatchingLightTurnStopBypass } from './hooks-runtime/light-turn.js';
24
24
  import { evaluateHookNarutoDecisionGate, looksLikeActiveContinuationPrompt } from './hooks-runtime/naruto-decision-gate.js';
25
+ import { consultJevTurnModel } from './decisions/integration.js';
25
26
  import { ensureOfficialSubagentArtifactDirConfined, inspectActiveOfficialSubagentWorkflow, recordOfficialSubagentLifecycleCaptureFailure, officialSubagentArtifactDir, recordAndRefreshSubagentEvidence, refreshOfficialSubagentCompletionArtifacts } from './hooks-runtime/official-subagent-lifecycle.js';
26
27
  import { finalizationRepeatDecision } from './hooks-runtime/stop-repeat-guard.js';
27
28
  import { armCodexGitActionStopBypass, consumeCodexGitActionStopBypass, consumeLightRouteStop, hasCompletionSummary, hasDfixLightCompletion, hasHonestMode, hasHonestModeUnresolvedGap, honestModeLoopbackBudgetExhausted, recordHonestModeLoopback, recordHonestModeTerminalUnverified, resolveHonestModeLoopback, shouldLoopBackAfterHonestMode, successfulAppNarutoStopNeedsVisibleSummary } from './hooks-runtime/stop-finalization.js';
@@ -29,6 +30,7 @@ import { activeNarutoParentLaunchMissionId, claimStandaloneParentHostCapabilityR
29
30
  import { classifyTaskProfile } from './runtime/task-profile.js';
30
31
  import { resolveSubagentThreadBudget } from './subagents/thread-budget.js';
31
32
  import { readOfficialSubagentConfig } from './subagents/official-subagent-config.js';
33
+ import { jevSpawnModelRewrite } from './hooks-runtime/jev-spawn-routing.js';
32
34
  import { subagentSpawnPolicyBlockReason } from './hooks-runtime/subagent-spawn-policy.js';
33
35
  import { withFileLock } from './locks/file-lock.js';
34
36
  import { ensureConfinedDirectory, inspectConfinedPath, removeManagedPathVerified } from './managed-path-safety.js';
@@ -120,7 +122,8 @@ export async function evaluateHookPayload(name, payload = {}, opts = {}) {
120
122
  const withNarutoDecision = (result) => ({ ...result, sksNarutoDecision });
121
123
  if (name === 'user-prompt-submit') {
122
124
  const result = await hookUserPrompt(root, state, payload, noQuestion, sessionKey);
123
- const withSkillContext = await attachAuthoritativeSksSkillContext(root, state, payload, result);
125
+ const withJev = await attachJevTurnRouting(root, payload, result);
126
+ const withSkillContext = await attachAuthoritativeSksSkillContext(root, state, payload, withJev);
124
127
  return withNarutoDecision(attachOfficialSubagentSpawnCompatibilityContext(state, payload, withSkillContext));
125
128
  }
126
129
  if (name === 'session-start' || name === 'pre-compact' || name === 'post-compact') {
@@ -233,6 +236,28 @@ async function hookSubagentStart(root, state, payload = {}, sessionKey = null) {
233
236
  const additionalContext = [coreEngineeringDirectiveReferenceText(), resourceGuard, routingContext, active, skillContext].filter(Boolean).join('\n\n');
234
237
  return { continue: true, additionalContext, ...(skillBlockers.length ? { silent: true } : {}) };
235
238
  }
239
+ async function attachJevTurnRouting(root, payload, result) {
240
+ if (!result || result.decision === 'block')
241
+ return result;
242
+ const prompt = stripVisibleDecisionAnswerBlocks(extractUserPrompt(payload));
243
+ if (!String(prompt || '').trim())
244
+ return result;
245
+ const decision = await consultJevTurnModel({ root, prompt }).catch(() => null);
246
+ if (!decision?.called)
247
+ return result;
248
+ const line = decision.model
249
+ ? `Jev sealed this turn to ${decision.model}. Use that sealed model and do not reclassify it.`
250
+ : '';
251
+ if (!line)
252
+ return { ...result, jev_turn: decision };
253
+ const additionalContext = [result.additionalContext, line].filter(Boolean).join('\n\n');
254
+ return {
255
+ ...result,
256
+ additionalContext,
257
+ jev_turn: decision,
258
+ ...(result.systemMessage ? { systemMessage: visibleHookMessage('user-prompt-submit', additionalContext) } : {})
259
+ };
260
+ }
236
261
  async function hookUserPrompt(root, state, payload, noQuestion, sessionKey = null) {
237
262
  await clearLightTurnStopBypass(root, { sessionKey }).catch(() => undefined);
238
263
  const submittedPrompt = stripVisibleDecisionAnswerBlocks(extractUserPrompt(payload));
@@ -572,7 +597,11 @@ async function consumeActiveOfficialWorkflowQueue(root, missionId, workflowRunId
572
597
  }
573
598
  }
574
599
  async function hookPreTool(root, state, payload, noQuestion, sessionKey = null) {
575
- const spawnPolicyBlock = subagentSpawnPolicyBlockReason(payload);
600
+ const jevSpawnInput = await jevSpawnModelRewrite(root, state, payload).catch(() => null);
601
+ const spawnPayload = jevSpawnInput
602
+ ? { ...payload, tool_input: jevSpawnInput, toolInput: jevSpawnInput }
603
+ : payload;
604
+ const spawnPolicyBlock = subagentSpawnPolicyBlockReason(spawnPayload);
576
605
  if (spawnPolicyBlock)
577
606
  return { decision: 'block', permissionDecision: 'deny', reason: spawnPolicyBlock };
578
607
  const artifactDir = officialSubagentArtifactDir(root, state, sessionKey);
@@ -641,15 +670,20 @@ async function hookPreTool(root, state, payload, noQuestion, sessionKey = null)
641
670
  const waveGuidance = await parentWaveGuidanceContext(root, state, sessionKey).catch(() => '');
642
671
  const additionalContext = [skillRefresh.context, waveGuidance].filter(Boolean).join('\n\n');
643
672
  if (additionalContext) {
644
- return {
673
+ return withJevSpawnRewrite({
645
674
  continue: true,
646
675
  additionalContext,
647
676
  ...(waveGuidance
648
677
  ? { systemMessage: visibleHookMessage('pre-tool', 'SKS Naruto wave lifecycle requires root-parent follow-up.') }
649
678
  : { silent: true })
650
- };
679
+ }, jevSpawnInput);
651
680
  }
652
- return { continue: true };
681
+ return withJevSpawnRewrite({ continue: true }, jevSpawnInput);
682
+ }
683
+ function withJevSpawnRewrite(result, updatedInput) {
684
+ if (!updatedInput || result?.decision === 'block' || result?.permissionDecision === 'deny')
685
+ return result;
686
+ return { ...result, permissionDecision: 'allow', updatedInput };
653
687
  }
654
688
  async function parentWaveGuidanceContext(root, state = {}, sessionKey = null) {
655
689
  if (!state?.mission_id && !state?.official_subagent_run_id)
@@ -1211,6 +1211,9 @@ export function narutoDecisionForRoute(route, prompt = '', profile = classifyTas
1211
1211
  return narutoRouteDecision('none', routeId, profile, `task_profile_${profile}_bypass`, true);
1212
1212
  }
1213
1213
  if (profile === 'bounded-work') {
1214
+ if (routeId === 'Naruto') {
1215
+ return narutoRouteDecision('generic_naruto', routeId, profile, 'naruto_parent_orchestrates', false);
1216
+ }
1214
1217
  return narutoRouteDecision('none', routeId, profile, 'task_profile_bounded_work_parent_owned', false);
1215
1218
  }
1216
1219
  if (profile === 'parallel-read' || profile === 'parallel-write' || profile === 'high-risk') {
@@ -461,6 +461,12 @@ function applyOfficialSubagentDecision(derived, decided) {
461
461
  : { ...derived.fanoutPolicy };
462
462
  if (selectedRouting)
463
463
  fanoutPolicy = { ...fanoutPolicy, jev_selected_routing: selectedRouting.id };
464
+ const omitted = new Set(decided.omittedRoutingRoles || []);
465
+ if (omitted.size)
466
+ fanoutPolicy = { ...fanoutPolicy, jev_omitted_roles: [...omitted] };
467
+ const recommendedAgents = omitted.size > 0 && derived.suggestedAgents.some((name) => !omitted.has(name))
468
+ ? derived.suggestedAgents.filter((name) => !omitted.has(name))
469
+ : derived.suggestedAgents;
464
470
  const routingPreferences = selectedRouting
465
471
  ? Object.fromEntries(Object.entries(selectedRouting.efforts)
466
472
  .filter(([name]) => !derived.roleModelPreferences.store.roles[name])
@@ -483,7 +489,7 @@ function applyOfficialSubagentDecision(derived, decided) {
483
489
  waveCount: budget.waveCount,
484
490
  capacity: budget.capacity,
485
491
  triwikiAttention: attention,
486
- recommendedAgents: derived.suggestedAgents,
492
+ recommendedAgents,
487
493
  roleModelPreferences: {
488
494
  ...derived.roleModelPreferences.store.roles,
489
495
  ...routingPreferences
@@ -519,6 +525,7 @@ function applyOfficialSubagentDecision(derived, decided) {
519
525
  }),
520
526
  triwiki_attention: attention,
521
527
  agents,
528
+ suggested_agents: recommendedAgents,
522
529
  fanout_policy: fanoutPolicy,
523
530
  capacity_controller: budget.capacity,
524
531
  jev_decision: decided.receipt,
@@ -53,6 +53,8 @@ export function buildOfficialSubagentPrompt(input) {
53
53
  const spawnModelRouting = renderSpawnModelRouting(narutoChildren);
54
54
  const childModelRules = narutoChildren
55
55
  ? [
56
+ '- the parent orchestrates only: decompose the goal, assign disjoint slices, spawn children, and integrate their results',
57
+ '- do not implement the assigned slice work in the parent thread',
56
58
  '- use the model and reasoning_effort named in each slice spawn contract',
57
59
  '- sealed child models are gpt-5.6-luna, gpt-5.6-sol, gpt-5.6-terra, and gpt-6-astra',
58
60
  '- keep a stored user role-model preference for that role'
@@ -1 +1 @@
1
- export const PACKAGE_VERSION = '10.3.0';
1
+ export const PACKAGE_VERSION = '10.3.2';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sneakoscope",
3
3
  "displayName": "ㅅㅋㅅ",
4
- "version": "10.3.0",
4
+ "version": "10.3.2",
5
5
  "description": "Sneakoscope Codex (`sks`) is an open-source trust layer for Codex CLI and ChatGPT Desktop. It coordinates bounded AI coding agents, records machine-verifiable evidence, preserves project memory, and blocks release claims that are not supported by current tests or artifacts. Search visibility outcomes are measured separately; SKS does not promise rankings or traffic.",
6
6
  "type": "module",
7
7
  "homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",