sneakoscope 10.3.1 → 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.1**. 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.1"
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.1"
3
+ version = "10.3.2"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema": "sks.skills-manifest.v1",
3
- "package_version": "10.3.1",
3
+ "package_version": "10.3.2",
4
4
  "skills": [
5
5
  {
6
6
  "canonical_name": "sks",
@@ -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,6 +123,7 @@ 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
  };
@@ -132,22 +134,24 @@ export async function consultJevTurnModel(input) {
132
134
  const overrides = activeOverrides();
133
135
  const env = input.env || process.env;
134
136
  const config = overrides?.config ?? await readDecisionConfig(env);
137
+ const none = (reason) => ({ called: false, model: null, effort: null, reason });
135
138
  if (!jevEnabled(config))
136
- return { called: false, model: null, reason: 'off' };
139
+ return none('off');
137
140
  const resolved = await resolveOpenRouterApiKey({ env });
138
141
  if (!resolved.key)
139
- return { called: false, model: null, reason: 'missing_key' };
142
+ return none('missing_key');
140
143
  const prompt = String(input.prompt || '').trim();
141
144
  if (!prompt)
142
- return { called: false, model: null, reason: 'empty_prompt' };
145
+ return none('empty_prompt');
146
+ const roleId = input.roleId === 'spawn' ? 'spawn' : 'turn';
143
147
  const bundle = buildDecisionBundle({
144
148
  projectId: sha256(input.root).slice(0, 32),
145
- workflowRunId: 'turn',
146
- workflowRevision: 'turn',
147
- sourceDigest: 'turn',
149
+ workflowRunId: roleId,
150
+ workflowRevision: roleId,
151
+ sourceDigest: roleId,
148
152
  graphDigest: null,
149
153
  goal: prompt,
150
- routingCandidates: [{ id: 'turn', summary: prompt.slice(0, 240) }]
154
+ routingCandidates: [{ id: roleId, summary: prompt.slice(0, 240) }]
151
155
  });
152
156
  const transport = await requestOpenRouterDecision(bundle, {
153
157
  env,
@@ -155,13 +159,14 @@ export async function consultJevTurnModel(input) {
155
159
  ...(overrides?.fetchImpl ? { fetchImpl: overrides.fetchImpl } : {})
156
160
  });
157
161
  if (!transport.ok)
158
- return { called: true, model: null, reason: transport.reason };
162
+ return { called: true, model: null, effort: null, reason: transport.reason };
159
163
  const compiled = compileDecision(bundle, transport.response);
160
164
  if (compiled.kind !== 'apply')
161
- return { called: true, model: null, reason: compiled.reason };
165
+ return { called: true, model: null, effort: null, reason: compiled.reason };
162
166
  const selected = assembleRoutingSelection(compiled.effects.flatMap((effect) => (effect.kind === 'select_routing' ? [{ roleId: effect.roleId, model: effect.model }] : [])));
163
- const model = selected?.models.turn || null;
164
- return { called: true, model, reason: model ? 'applied' : 'keep_baseline' };
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' };
165
170
  }
166
171
  export function effectAlreadyConsumed(identity) {
167
172
  return appliedEffects.has(identity);
@@ -192,6 +197,7 @@ function applyEffects(attention, contextCandidates, planCandidates, compiled) {
192
197
  let selectedContextIds = null;
193
198
  const evidence = [];
194
199
  const routingEffects = [];
200
+ const omittedRoutingRoles = [];
195
201
  for (const effect of compiled.effects) {
196
202
  if (effect.kind === 'select_optional_context') {
197
203
  nextAttention = applyOptionalContextSelection(attention, contextCandidates, effect.keepIds);
@@ -206,6 +212,10 @@ function applyEffects(attention, contextCandidates, planCandidates, compiled) {
206
212
  if (effect.kind === 'select_routing') {
207
213
  routingEffects.push({ roleId: effect.roleId, model: effect.model });
208
214
  }
215
+ if (effect.kind === 'omit_role') {
216
+ omittedRoutingRoles.push(effect.roleId);
217
+ evidence.push(`omit:${effect.roleId}`);
218
+ }
209
219
  }
210
220
  const selectedRouting = assembleRoutingSelection(routingEffects);
211
221
  if (selectedRouting) {
@@ -216,6 +226,7 @@ function applyEffects(attention, contextCandidates, planCandidates, compiled) {
216
226
  selectedPlan,
217
227
  selectedRouting,
218
228
  selectedContextIds,
229
+ omittedRoutingRoles,
219
230
  consumptionEvidence: evidence.length ? evidence.join('|') : null
220
231
  };
221
232
  }
@@ -291,6 +302,7 @@ function keep(input, reason, started) {
291
302
  selectedPlan: null,
292
303
  selectedRouting: null,
293
304
  selectedContextIds: null,
305
+ omittedRoutingRoles: [],
294
306
  llmRejudgeCalls: 0,
295
307
  bundle: null
296
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
+ }
@@ -30,6 +30,7 @@ import { activeNarutoParentLaunchMissionId, claimStandaloneParentHostCapabilityR
30
30
  import { classifyTaskProfile } from './runtime/task-profile.js';
31
31
  import { resolveSubagentThreadBudget } from './subagents/thread-budget.js';
32
32
  import { readOfficialSubagentConfig } from './subagents/official-subagent-config.js';
33
+ import { jevSpawnModelRewrite } from './hooks-runtime/jev-spawn-routing.js';
33
34
  import { subagentSpawnPolicyBlockReason } from './hooks-runtime/subagent-spawn-policy.js';
34
35
  import { withFileLock } from './locks/file-lock.js';
35
36
  import { ensureConfinedDirectory, inspectConfinedPath, removeManagedPathVerified } from './managed-path-safety.js';
@@ -596,7 +597,11 @@ async function consumeActiveOfficialWorkflowQueue(root, missionId, workflowRunId
596
597
  }
597
598
  }
598
599
  async function hookPreTool(root, state, payload, noQuestion, sessionKey = null) {
599
- 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);
600
605
  if (spawnPolicyBlock)
601
606
  return { decision: 'block', permissionDecision: 'deny', reason: spawnPolicyBlock };
602
607
  const artifactDir = officialSubagentArtifactDir(root, state, sessionKey);
@@ -665,15 +670,20 @@ async function hookPreTool(root, state, payload, noQuestion, sessionKey = null)
665
670
  const waveGuidance = await parentWaveGuidanceContext(root, state, sessionKey).catch(() => '');
666
671
  const additionalContext = [skillRefresh.context, waveGuidance].filter(Boolean).join('\n\n');
667
672
  if (additionalContext) {
668
- return {
673
+ return withJevSpawnRewrite({
669
674
  continue: true,
670
675
  additionalContext,
671
676
  ...(waveGuidance
672
677
  ? { systemMessage: visibleHookMessage('pre-tool', 'SKS Naruto wave lifecycle requires root-parent follow-up.') }
673
678
  : { silent: true })
674
- };
679
+ }, jevSpawnInput);
675
680
  }
676
- 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 };
677
687
  }
678
688
  async function parentWaveGuidanceContext(root, state = {}, sessionKey = null) {
679
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.1';
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.1",
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",