sneakoscope 4.8.3 β†’ 4.8.5

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
@@ -49,7 +49,7 @@ sks seo-geo-optimizer apply latest --mode seo --apply --json
49
49
  sks seo-geo-optimizer audit --mode geo --target package --offline --json
50
50
  ```
51
51
 
52
- > πŸ“‹ **Current release: `v4.8.3`** β€” full release history lives in [CHANGELOG.md](CHANGELOG.md). This README documents how Sneakoscope works today, not its version-by-version changes. Release readiness is tracked in [docs/release-readiness.md](docs/release-readiness.md).
52
+ > πŸ“‹ **Current release: `v4.8.5`** β€” full release history lives in [CHANGELOG.md](CHANGELOG.md). This README documents how Sneakoscope works today, not its version-by-version changes. Release readiness is tracked in [docs/release-readiness.md](docs/release-readiness.md).
53
53
 
54
54
  ## πŸ₯ Parallelism, UX, And Integrations
55
55
 
@@ -76,7 +76,7 @@ dependencies = [
76
76
 
77
77
  [[package]]
78
78
  name = "sks-core"
79
- version = "4.8.3"
79
+ version = "4.8.5"
80
80
  dependencies = [
81
81
  "serde_json",
82
82
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "4.8.3"
3
+ version = "4.8.5"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
4
4
  fn main() {
5
5
  let mut args = std::env::args().skip(1);
6
6
  match args.next().as_deref() {
7
- Some("--version") => println!("sks-rs 4.8.3"),
7
+ Some("--version") => println!("sks-rs 4.8.5"),
8
8
  Some("compact-info") => {
9
9
  let mut input = String::new();
10
10
  let _ = io::stdin().read_to_string(&mut input);
package/dist/bin/sks.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const FAST_PACKAGE_VERSION = '4.8.3';
2
+ const FAST_PACKAGE_VERSION = '4.8.5';
3
3
  const args = process.argv.slice(2);
4
4
  try {
5
5
  if (args[0] === '--agent' && args[1] === 'worker') {
@@ -8,6 +8,7 @@ const CONTROL_CENTER_PREFERRED_POSITION = 360;
8
8
  const MENU_ITEMS = [
9
9
  'Use codex-lb',
10
10
  'Use ChatGPT OAuth',
11
+ 'Set codex-lb Domain and Key',
11
12
  'Set OpenRouter Key and GLM Profiles',
12
13
  'Fast Check',
13
14
  'SKS Version Check',
@@ -294,7 +295,7 @@ func runInTerminal(_ command: String) {
294
295
  .replacingOccurrences(of: "\\\\", with: "\\\\\\\\")
295
296
  .replacingOccurrences(of: "\\\"", with: "\\\\\\\"")
296
297
  let script = "tell application \\"Terminal\\" to activate\\n" +
297
- "tell application \\"Terminal\\" to do script \\"\(escaped)\\""
298
+ "tell application \\"Terminal\\" to do script \\"\\(escaped)\\""
298
299
  runDetached("/usr/bin/osascript", ["-e", script])
299
300
  }
300
301
 
@@ -313,6 +314,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
313
314
  let menu = NSMenu()
314
315
  add(menu, "Use codex-lb", #selector(useCodexLb))
315
316
  add(menu, "Use ChatGPT OAuth", #selector(useChatGptOAuth))
317
+ add(menu, "Set codex-lb Domain and Key", #selector(setCodexLbDomainAndKey))
316
318
  menu.addItem(NSMenuItem.separator())
317
319
  add(menu, "Set OpenRouter Key and GLM Profiles", #selector(setOpenRouterKey))
318
320
  add(menu, "Fast Check", #selector(fastCheck))
@@ -360,6 +362,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
360
362
  runSks(["codex-lb", "use-oauth"])
361
363
  }
362
364
 
365
+ @objc func setCodexLbDomainAndKey() {
366
+ runSks(["codex-lb", "setup"])
367
+ }
368
+
363
369
  @objc func setOpenRouterKey() {
364
370
  let command = "printf 'Paste OpenRouter key, then press Return: '; read -r key; printf '%s\\\\n' \\"$key\\" | \\(shellQuote(actionScript)) codex-app set-openrouter-key --api-key-stdin; \\(shellQuote(actionScript)) codex-app glm-profile install; echo; echo 'OpenRouter/GLM update finished. Restart Codex if the model picker was already open.'"
365
371
  runInTerminal(command)
@@ -1,5 +1,5 @@
1
1
  import path from 'node:path';
2
- import { createMission, findLatestMission, loadMission, setCurrent } from '../mission.js';
2
+ import { createMission, findLatestMission, loadMission, missionExists, setCurrent } from '../mission.js';
3
3
  import { nowIso, readJson, sksRoot, writeJsonAtomic } from '../fsx.js';
4
4
  import { runNativeAgentOrchestrator } from '../agents/agent-orchestrator.js';
5
5
  import { classifyOllamaWorkerSlice } from '../agents/agent-runner-ollama.js';
@@ -692,18 +692,28 @@ async function narutoStatus(parsed) {
692
692
  const id = parsed.missionId && parsed.missionId !== 'latest' ? parsed.missionId : await findLatestMission(root);
693
693
  if (!id)
694
694
  return emit(parsed, { schema: NARUTO_RESULT_SCHEMA, ok: false, action: 'status', status: 'missing_mission' }, () => console.log('No Naruto mission found.'));
695
+ if (!(await missionExists(root, id)))
696
+ return emit(parsed, { schema: NARUTO_RESULT_SCHEMA, ok: false, action: 'status', mission_id: id, status: 'stale_missing_mission' }, () => console.log('Naruto mission is stale or missing: ' + id));
695
697
  const { dir } = await loadMission(root, id);
698
+ const gate = await readJson(path.join(dir, 'naruto-gate.json'), null);
696
699
  const proof = await readJson(path.join(dir, 'agents', 'agent-proof-evidence.json'), null);
697
700
  const scheduler = await readJson(path.join(dir, 'agents', 'agent-scheduler-state.json'), null);
698
701
  const roleDistribution = await readJson(path.join(dir, 'agents', 'naruto-role-distribution.json'), null);
699
702
  const workGraph = await readJson(path.join(dir, 'agents', 'naruto-work-graph.json'), null);
700
703
  const governor = await readJson(path.join(dir, 'agents', 'naruto-concurrency-governor.json'), null);
704
+ const gateBlockers = Array.isArray(gate?.blockers) ? gate.blockers : [];
705
+ const preparedNotStarted = !proof && gateBlockers.includes('naruto_run_not_started');
701
706
  const summary = {
702
707
  schema: NARUTO_RESULT_SCHEMA,
703
708
  ok: proof !== null,
704
709
  action: 'status',
705
710
  mission_id: id,
711
+ status: preparedNotStarted ? 'prepared_not_started' : (proof ? 'recorded' : 'missing_proof'),
706
712
  proof: proof?.status || 'missing',
713
+ gate: gate ? {
714
+ passed: gate.passed === true,
715
+ blockers: gateBlockers
716
+ } : null,
707
717
  target_active_slots: scheduler?.target_active_slots ?? null,
708
718
  max_active_slots: scheduler?.max_active_slots ?? null,
709
719
  completed: scheduler?.completed_count ?? null,
@@ -719,7 +729,10 @@ async function narutoStatus(parsed) {
719
729
  };
720
730
  return emit(parsed, summary, () => {
721
731
  console.log('πŸ₯ Naruto mission: ' + id);
732
+ console.log('Status: ' + summary.status);
722
733
  console.log('Proof: ' + summary.proof);
734
+ if (gateBlockers.length)
735
+ console.log('Gate blockers: ' + gateBlockers.join(', '));
723
736
  if (summary.target_active_slots !== null)
724
737
  console.log('Active clones: ' + summary.target_active_slots + ' / max ' + summary.max_active_slots);
725
738
  if (roleDistribution?.entries)
@@ -1,13 +1,13 @@
1
1
  import path from 'node:path';
2
2
  import { projectRoot, readJson } from '../fsx.js';
3
- import { missionDir, stateFile } from '../mission.js';
3
+ import { missionDir, normalizeCurrentState, stateFile } from '../mission.js';
4
4
  import { PIPELINE_PLAN_ARTIFACT, projectGateStatus, writePipelinePlan } from '../pipeline.js';
5
5
  import { routePrompt } from '../routes.js';
6
6
  import { flag, positionalArgs, readFlagValue, resolveMissionId } from './command-utils.js';
7
7
  export async function pipelineCommand(args = []) {
8
8
  const root = await projectRoot();
9
9
  const action = args[0] || 'status';
10
- const state = await readJson(stateFile(root), {});
10
+ const state = await normalizeCurrentState(root, await readJson(stateFile(root), {}));
11
11
  if (action === 'status') {
12
12
  const result = { schema: 'sks.pipeline-status.v1', ok: true, state };
13
13
  if (flag(args, '--json'))
@@ -1,12 +1,12 @@
1
1
  import path from 'node:path';
2
2
  import { projectRoot, readJson } from '../fsx.js';
3
- import { stateFile } from '../mission.js';
3
+ import { normalizeCurrentState, stateFile } from '../mission.js';
4
4
  import { readRouteProof } from '../proof/proof-reader.js';
5
5
  import { latestTrustReport } from '../trust-kernel/trust-report.js';
6
6
  import { flag } from './command-utils.js';
7
7
  export async function statusCommand(args = []) {
8
8
  const root = await projectRoot();
9
- const state = await readJson(stateFile(root), {});
9
+ const state = await normalizeCurrentState(root, await readJson(stateFile(root), {}));
10
10
  const missionId = state.mission_id || null;
11
11
  const proof = missionId ? await readRouteProof(root, missionId) : null;
12
12
  const trust = missionId ? await latestTrustReport(root, missionId) : null;
@@ -22,6 +22,7 @@ export async function statusCommand(args = []) {
22
22
  agent_status: proof?.evidence?.agents?.status || (state.agents_required === false ? 'not_required' : 'not_recorded'),
23
23
  image_voxel_status: proof?.evidence?.image_voxels?.status || 'not_recorded',
24
24
  db_safety_status: proof?.evidence?.db || proof?.evidence?.db_safety ? 'recorded' : 'not_recorded',
25
+ stale_mission: state.stale_mission || null,
25
26
  next_action: nextAction(state, trust, proof),
26
27
  files: missionId ? {
27
28
  mission: path.join(root, '.sneakoscope', 'missions', missionId),
package/dist/core/fsx.js CHANGED
@@ -5,7 +5,7 @@ import os from 'node:os';
5
5
  import crypto from 'node:crypto';
6
6
  import { spawn } from 'node:child_process';
7
7
  import { fileURLToPath } from 'node:url';
8
- export const PACKAGE_VERSION = '4.8.3';
8
+ export const PACKAGE_VERSION = '4.8.5';
9
9
  export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
10
10
  export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
11
11
  export function nowIso() {
@@ -1,7 +1,7 @@
1
1
  import path from 'node:path';
2
2
  import { projectRoot, readJson, readText, writeJsonAtomic, appendJsonl, readStdin, nowIso, runProcess, sha256, packageRoot, tmpdir } from './fsx.js';
3
3
  import { looksInteractiveCommand, interactiveCommandReason } from './no-question-guard.js';
4
- import { missionDir, setCurrent, stateFile } from './mission.js';
4
+ import { missionDir, normalizeCurrentState, setCurrent, stateFile } from './mission.js';
5
5
  import { checkDbOperation, dbBlockReason, handleMadSksUserConfirmation } from './db-safety.js';
6
6
  import { maybeRecordMadDbToolResultFromToolUse } from './mad-db/mad-db-result-lifecycle.js';
7
7
  import { checkHarnessModification, harnessGuardBlockReason, isHarnessSourceProject } from './harness-guard.js';
@@ -11,7 +11,7 @@ import { activeRouteContext, evaluateStop, prepareRoute, promptPipelineContext a
11
11
  import { localizedFinalizationReason } from './language-preference.js';
12
12
  import { classifyToolError } from './evaluation.js';
13
13
  import { REQUIRED_CODEX_MODEL, isForbiddenCodexModel } from './codex-model-guard.js';
14
- import { dollarCommand, routeRequiresSubagents, stripVisibleDecisionAnswerBlocks } from './routes.js';
14
+ import { dollarCommand, looksLikeTeamDefaultWork, routeRequiresSubagents, stripVisibleDecisionAnswerBlocks } from './routes.js';
15
15
  import { leanEngineeringCompactText } from './lean-engineering-policy.js';
16
16
  import { appendMissionStatus } from './recallpulse.js';
17
17
  import { scanAgentTextForRecursion } from './agents/agent-recursion-guard.js';
@@ -42,7 +42,7 @@ async function loadHookPayload() {
42
42
  }
43
43
  }
44
44
  async function loadState(root) {
45
- return readJson(stateFile(root), {});
45
+ return normalizeCurrentState(root, await readJson(stateFile(root), {}));
46
46
  }
47
47
  function isNoQuestionRunning(state) {
48
48
  return (state.mode === 'RESEARCH' && state.phase === 'RESEARCH_RUNNING_NO_QUESTIONS')
@@ -346,6 +346,8 @@ function shouldPrepareFreshRouteOnActivePrompt(prompt, route = null, opts = {})
346
346
  return false;
347
347
  if (looksLikeActiveContinuationPrompt(prompt))
348
348
  return false;
349
+ if (route.id === 'SKS' && looksLikeTeamDefaultWork(prompt))
350
+ return true;
349
351
  return routeRequiresSubagents(route, prompt);
350
352
  }
351
353
  function looksLikeActiveContinuationPrompt(prompt = '') {
@@ -1259,7 +1261,9 @@ function visibleHookMessage(name, text = '') {
1259
1261
  return 'SKS: native Computer Use lane injected; defer TriWiki/Honest Mode to final closeout.';
1260
1262
  if (body.includes('MANDATORY ambiguity-removal gate') || body.includes('VISIBLE RESPONSE CONTRACT') || body.includes('Required questions still pending'))
1261
1263
  return 'SKS: stale clarification gate detected; continue from inferred route contract.';
1262
- if (body.includes('$Team route prepared') || body.includes('Team route'))
1264
+ if (body.includes('$SKS route prepared') || body.includes('SKS skill-first pipeline active. Route: $SKS'))
1265
+ return 'SKS: SKS fast route injected; native sessions optional.';
1266
+ if (body.includes('$Team route prepared') || body.includes('Active Team mission'))
1263
1267
  return 'SKS: Team route, live transcript, and native multi-session plan injected.';
1264
1268
  if (body.includes('$Research route prepared'))
1265
1269
  return 'SKS: Research route, xhigh Eureka agent council, source/debate ledgers, paper output, and falsification gate injected.';
@@ -10,6 +10,7 @@ export function sineDir(root) { return path.join(root, '.sneakoscope'); }
10
10
  export function missionsDir(root) { return path.join(sineDir(root), 'missions'); }
11
11
  export function missionDir(root, id) { return path.join(missionsDir(root), id); }
12
12
  export function stateFile(root) { return path.join(sineDir(root), 'state', 'current.json'); }
13
+ export function missionFile(root, id) { return path.join(missionDir(root, id), 'mission.json'); }
13
14
  export async function createMission(root, { mode, prompt }) {
14
15
  const id = missionId();
15
16
  const dir = missionDir(root, id);
@@ -33,9 +34,42 @@ export async function createMission(root, { mode, prompt }) {
33
34
  }
34
35
  export async function loadMission(root, id) {
35
36
  const dir = missionDir(root, id);
36
- const mission = await readJson(path.join(dir, 'mission.json'));
37
+ const mission = await readJson(missionFile(root, id));
37
38
  return { id, dir, mission };
38
39
  }
40
+ export async function missionExists(root, id) {
41
+ return Boolean(id) && await exists(missionFile(root, id));
42
+ }
43
+ export async function normalizeCurrentState(root, state = null, opts = {}) {
44
+ const current = state || await readJson(stateFile(root), {});
45
+ const id = current?.mission_id;
46
+ if (!id || await missionExists(root, id))
47
+ return current || {};
48
+ const detectedAt = nowIso();
49
+ const repaired = {
50
+ mission_id: null,
51
+ mode: null,
52
+ route: null,
53
+ route_command: null,
54
+ phase: 'IDLE',
55
+ stop_gate: null,
56
+ implementation_allowed: false,
57
+ questions_allowed: true,
58
+ updated_at: detectedAt,
59
+ stale_mission: {
60
+ mission_id: id,
61
+ reason: 'mission_json_missing',
62
+ detected_at: detectedAt,
63
+ previous_mode: current.mode || null,
64
+ previous_route: current.route_command || current.route || null,
65
+ previous_phase: current.phase || null,
66
+ previous_stop_gate: current.stop_gate || null
67
+ }
68
+ };
69
+ if (opts.repair !== false)
70
+ await writeJsonAtomic(stateFile(root), repaired);
71
+ return repaired;
72
+ }
39
73
  export async function findLatestMission(root) {
40
74
  const dir = missionsDir(root);
41
75
  if (!(await exists(dir)))
@@ -45,8 +79,10 @@ export async function findLatestMission(root) {
45
79
  const ids = entries.filter((e) => e.isDirectory() && e.name.startsWith('M-')).map((e) => e.name);
46
80
  const candidates = await Promise.all(ids.map(async (id) => {
47
81
  const dirPath = missionDir(root, id);
82
+ if (!(await missionExists(root, id)))
83
+ return null;
48
84
  const stat = await fs.stat(dirPath).catch(() => null);
49
- const mission = await readJson(path.join(dirPath, 'mission.json'), {}).catch(() => ({}));
85
+ const mission = await readJson(missionFile(root, id), {}).catch(() => ({}));
50
86
  const createdMs = Date.parse(mission.created_at || mission.updated_at || '');
51
87
  return {
52
88
  id,
@@ -54,8 +90,9 @@ export async function findLatestMission(root) {
54
90
  mtimeMs: stat?.mtimeMs || 0
55
91
  };
56
92
  }));
57
- candidates.sort((a, b) => (a.createdMs - b.createdMs) || (a.mtimeMs - b.mtimeMs) || a.id.localeCompare(b.id));
58
- return candidates.at(-1)?.id || null;
93
+ const present = candidates.filter(Boolean);
94
+ present.sort((a, b) => (a.createdMs - b.createdMs) || (a.mtimeMs - b.mtimeMs) || a.id.localeCompare(b.id));
95
+ return present.at(-1)?.id || null;
59
96
  }
60
97
  export async function setCurrent(root, patch, opts = {}) {
61
98
  const current = opts.replace ? {} : await readJson(stateFile(root), {});
@@ -2,7 +2,7 @@ import fsp from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { appendJsonl, exists, nowIso, readJson, readText, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
4
4
  import { containsUserQuestion, noQuestionContinuationReason } from '../no-question-guard.js';
5
- import { createMission, missionDir, setCurrent } from '../mission.js';
5
+ import { createMission, missionDir, missionExists, setCurrent } from '../mission.js';
6
6
  import { buildQuestionSchemaForRoute, buildRequestIntake, REQUEST_INTAKE_ARTIFACT, writeQuestions } from '../questions.js';
7
7
  import { sealContract } from '../decision-contract.js';
8
8
  import { scanDbSafety } from '../db-safety.js';
@@ -26,7 +26,7 @@ import { prepareMadDbMission } from '../mad-db/mad-db-coordinator.js';
26
26
  import { AGENT_INTAKE_STAGE_ID, AGENT_COUNT } from '../agents/agent-schema.js';
27
27
  import { normalizeAgentPolicy, routeRequiresAgentIntake, agentPipelineStage } from '../agents/agent-plan.js';
28
28
  import { readAgentGateStatus } from '../agents/agent-gate.js';
29
- import { CODEX_APP_IMAGE_GENERATION_DOC_URL, CODEX_COMPUTER_USE_EVIDENCE_SOURCE, CODEX_COMPUTER_USE_ONLY_POLICY, CODEX_IMAGEGEN_REQUIRED_POLICY, CODEX_WEB_VERIFICATION_POLICY, 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, SOLUTION_SCOUT_STAGE_ID, chatCaptureIntakeText, context7RequirementText, dollarCommand, evidenceMentionsForbiddenBrowserAutomation, getdesignReferencePolicyText, hasFromChatImgSignal, hasMadSksSignal, imageUxReviewPipelinePolicyText, leanEngineeringCompactText, looksLikeProblemSolvingRequest, pptPipelineAllowlistPolicyText, reflectionRequiredForRoute, reasoningInstruction, routeNeedsContext7, routePrompt, routeReasoning, routeRequiresSubagents, solutionScoutPolicyText, stripDollarCommand, stripMadSksSignal, stripVisibleDecisionAnswerBlocks, subagentExecutionPolicyText, stackCurrentDocsPolicyText, triwikiContextTracking, triwikiContextTrackingText, triwikiStagePolicyText } from '../routes.js';
29
+ import { CODEX_APP_IMAGE_GENERATION_DOC_URL, CODEX_COMPUTER_USE_EVIDENCE_SOURCE, CODEX_COMPUTER_USE_ONLY_POLICY, CODEX_IMAGEGEN_REQUIRED_POLICY, CODEX_WEB_VERIFICATION_POLICY, 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, SOLUTION_SCOUT_STAGE_ID, chatCaptureIntakeText, context7RequirementText, dollarCommand, evidenceMentionsForbiddenBrowserAutomation, getdesignReferencePolicyText, hasFromChatImgSignal, hasMadSksSignal, imageUxReviewPipelinePolicyText, leanEngineeringCompactText, looksLikeProblemSolvingRequest, looksLikeTeamDefaultWork, pptPipelineAllowlistPolicyText, reflectionRequiredForRoute, reasoningInstruction, routeNeedsContext7, routePrompt, routeReasoning, routeRequiresSubagents, solutionScoutPolicyText, stripDollarCommand, stripMadSksSignal, stripVisibleDecisionAnswerBlocks, subagentExecutionPolicyText, stackCurrentDocsPolicyText, triwikiContextTracking, triwikiContextTrackingText, triwikiStagePolicyText } from '../routes.js';
30
30
  import { normalizeLeanDecision, validateLeanDecision } from '../lean-engineering-policy.js';
31
31
  import { TEAM_DECOMPOSITION_ARTIFACT, TEAM_GRAPH_ARTIFACT, TEAM_INBOX_DIR, TEAM_RUNTIME_TASKS_ARTIFACT, teamRuntimePlanMetadata, teamRuntimeRequiredArtifacts, validateTeamRuntimeArtifacts, writeTeamRuntimeArtifacts } from '../team-dag.js';
32
32
  import { formatAgentReasoning, formatRoleCounts, initTeamLive, parseTeamSpecText, teamReasoningPolicy } from '../team-live.js';
@@ -54,7 +54,7 @@ const HARD_BLOCKER_ARTIFACT = 'hard-blocker.json';
54
54
  const DEFAULT_COMPLIANCE_LOOP_LIMIT = 3;
55
55
  const CLARIFICATION_BYPASS_ROUTES = new Set(['Answer', 'DFix', 'Help', 'Wiki', 'ComputerUse', 'Goal']);
56
56
  const QUESTION_GATE_ROUTES = new Set(['QALoop', 'PPT']);
57
- const LIGHTWEIGHT_ROUTES = new Set(['Answer', 'DFix', 'Help', 'Wiki']);
57
+ const LIGHTWEIGHT_ROUTES = new Set(['Answer', 'DFix', 'Help', 'Wiki', 'SKS']);
58
58
  const FULL_ROUTE_STAGES = Object.freeze([
59
59
  'route_classification',
60
60
  SOLUTION_SCOUT_STAGE_ID,
@@ -344,7 +344,7 @@ function buildPipelineStages(route, task, ambiguity, lane, context7Required, age
344
344
  const skipped = skippedByFast || optional.skip;
345
345
  if (id === AGENT_INTAKE_STAGE_ID) {
346
346
  if (!agentPolicy.required)
347
- return agentPipelineStage(agentPolicy);
347
+ return { ...agentPipelineStage(agentPolicy), status: 'not_applicable', reason: 'five_agent_intake_not_required_for_route' };
348
348
  return { ...agentPipelineStage(agentPolicy), status: skipped ? 'skipped' : 'required', reason: skippedByFast ? 'proof_field_fast_lane' : agentPolicy.reason };
349
349
  }
350
350
  return {
@@ -432,10 +432,10 @@ export function promptPipelineContext(prompt, route = null) {
432
432
  'Route contract: execution routes infer contract answers from the prompt, TriWiki/current-code defaults, and conservative SKS policy. DFix and Answer bypass stateful execution because they do not start implementation.',
433
433
  `Wiki-informed request intake: when a mission exists, read ${REQUEST_INTAKE_ARTIFACT} before execution; preserve every source-order requirement, apply TriWiki attention/use_first and hydrate_first context, and execute request_intake.transformed_prompt through the selected route instead of relying on the vague original wording alone.`,
434
434
  'Plan-first interaction: when ambiguity questions are truly required, show the user only the missing human decision(s), then seal the decision contract internally and execute/verify.',
435
- 'Question-shaped directive policy: before using Answer, decide whether a question is a real information request or an implicit instruction/complaint about broken behavior. Rhetorical bug reports, mandatory-policy statements, and "why is this not happening?" execution complaints must route to Naruto, not Answer.',
435
+ 'Question-shaped directive policy: before using Answer, decide whether a question is a real information request or an implicit instruction/complaint about broken behavior. Rhetorical bug reports, mandatory-policy statements, and "why is this not happening?" execution complaints route to the parent-owned SKS fast path unless the user explicitly asks for Naruto/parallel agents.',
436
436
  'Best-practice prompt shape: extract Goal, Context, Constraints, and Done-when before implementation; keep questions compact and only ask for answers that can change scope, safety, user-facing behavior, or acceptance criteria.',
437
437
  chatCaptureIntakeText(),
438
- 'Default execution routing: general implementation/code-changing prompts promote to Naruto so the normal path is shadow-clone fan-out, hardware-safe concurrency, patch envelopes, verification DAG, and GPT final arbitration. Answer, DFix, Help, Wiki maintenance, and safety-specific routes are intentional exceptions.',
438
+ 'Default execution routing: ordinary implementation/code-changing prompts use the parent-owned SKS fast path with focused verification and Honest Mode. Promote to Naruto only when the user explicitly asks for Naruto, Team, multi-agent, subagent, parallel-agent, swarm, or fan-out execution, or when a route-specific safety surface requires it.',
439
439
  'Stance: infer the user intent aggressively from rough wording, local context, TriWiki, and conservative defaults; do not surface prequestion sheets before work.',
440
440
  subagentExecutionPolicyText(route, cleanPrompt),
441
441
  solutionScoutPolicyText(cleanPrompt),
@@ -535,6 +535,7 @@ export async function prepareRoute(root, prompt, state = {}) {
535
535
  const required = routeNeedsContext7(route, cleanPrompt);
536
536
  const reasoning = routeReasoning(route, cleanPrompt);
537
537
  const nativeSessionsRequired = routeRequiresSubagents(route, cleanPrompt);
538
+ const replaceActiveWithSksFastRoute = route.id === 'SKS' && Boolean(state?.mission_id) && looksLikeTeamDefaultWork(cleanPrompt);
538
539
  if (QUESTION_GATE_ROUTES.has(route.id) || route.id === 'MadSKS')
539
540
  return withSkillDreamContext(await prepareClarificationGate(root, route, task, required, { madSksAuthorization }), dreamContext);
540
541
  if (route.id === 'Naruto')
@@ -549,7 +550,7 @@ export async function prepareRoute(root, prompt, state = {}) {
549
550
  return withSkillDreamContext(await prepareDb(root, route, task, required), dreamContext);
550
551
  if (route.id === 'GX')
551
552
  return withSkillDreamContext(await prepareGx(root, route, task, required), dreamContext);
552
- if (explicit || required)
553
+ if (explicit || required || replaceActiveWithSksFastRoute)
553
554
  return withSkillDreamContext(await prepareLightRoute(root, route, task, required), dreamContext);
554
555
  return withSkillDreamContext({
555
556
  route,
@@ -673,6 +674,8 @@ async function prepareImageUxReview(root, route, task, required) {
673
674
  export async function activeRouteContext(root, state) {
674
675
  if (!state?.route && !state?.mode)
675
676
  return '';
677
+ if (state?.mission_id && !(await missionExists(root, state.mission_id)))
678
+ return '';
676
679
  const id = state.route || state.mode;
677
680
  const reasoningNote = state.reasoning_effort ? ` Temporary reasoning remains ${state.reasoning_effort} (${state.reasoning_profile}); return to the default profile after this route completes.` : '';
678
681
  const planNote = await activePipelinePlanNote(root, state);
@@ -136,7 +136,8 @@ export async function projectGateStatus(root, state = {}) {
136
136
  });
137
137
  }
138
138
  if (state?.subagents_required) {
139
- const evidence = await subagentEvidence(root, state);
139
+ const alreadyVerified = state.subagents_verified === true || state.native_sessions_verified === true;
140
+ const evidence = alreadyVerified ? { ok: true } : await subagentEvidence(root, state);
140
141
  gates.push({
141
142
  id: 'native-session-evidence',
142
143
  ok: evidence.ok,
@@ -207,7 +208,7 @@ export async function evaluateStop(root, state, payload, opts = {}) {
207
208
  if (state?.context7_required && !(await hasContext7DocsEvidence(root, state))) {
208
209
  return complianceBlock(root, state, `SKS ${state.route_command || state.mode || 'route'} requires Context7 evidence before completion. Use Context7 resolve-library-id, then query-docs (or legacy get-library-docs), so SKS can record context7-evidence.jsonl.`, { gate: 'context7-evidence' });
209
210
  }
210
- if (state?.subagents_required && !(await hasSubagentEvidence(root, state))) {
211
+ if (state?.subagents_required && state.subagents_verified !== true && state.native_sessions_verified !== true && !(await hasSubagentEvidence(root, state))) {
211
212
  return complianceBlock(root, state, `SKS ${state.route_command || state.mode || 'route'} requires native multi-session evidence before completion. Run worker/reviewer native sessions for disjoint code-changing work, or record explicit evidence that native sessions were unavailable or unsafe to split.`, { gate: 'native-session-evidence' });
212
213
  }
213
214
  if (state?.mission_id && !(await exists(path.join(missionDir(root, state.mission_id), 'completion-proof.json'))) && routeRequiresAgentIntake(routeFromState(state), { task: state.prompt, force: state.forceAgents === true, noAgents: state.agents_required === false })) {
@@ -390,6 +391,18 @@ async function passedHardBlocker(root, state) {
390
391
  }
391
392
  function missingRequiredGateFields(file, state, gate = {}) {
392
393
  const mode = String(state?.mode || '').toUpperCase();
394
+ if (file === 'stop-gate.json' && gate.schema === 'sks.stop-gate.v1') {
395
+ const missing = [];
396
+ if (gate.passed !== true)
397
+ missing.push('passed');
398
+ if (gate.terminal !== true)
399
+ missing.push('terminal');
400
+ if (gate.status !== 'passed')
401
+ missing.push('status');
402
+ if (Array.isArray(gate.blockers) && gate.blockers.length > 0)
403
+ missing.push('blockers');
404
+ return missing;
405
+ }
393
406
  if (file === 'team-gate.json' || mode === 'TEAM') {
394
407
  const required = ['team_roster_confirmed', 'analysis_artifact', 'triwiki_refreshed', 'triwiki_validated', 'ssot_guard', 'consensus_artifact', 'implementation_team_fresh', 'review_artifact', 'integration_evidence', 'session_cleanup'];
395
408
  if (fromChatImgCoverageRequired(state, gate))
@@ -445,6 +458,8 @@ function missingRequiredGateFields(file, state, gate = {}) {
445
458
  }
446
459
  async function missingRequiredGateArtifacts(root, file, state, gate = {}) {
447
460
  const mode = String(state?.mode || '').toUpperCase();
461
+ if (file === 'stop-gate.json' && gate.schema === 'sks.stop-gate.v1')
462
+ return [];
448
463
  if (file === 'research-gate.json' || mode === 'RESEARCH') {
449
464
  const evaluated = await evaluateResearchGate(missionDir(root, state.mission_id));
450
465
  if (evaluated.passed === true)
@@ -918,7 +918,7 @@ export function routePrompt(prompt) {
918
918
  return routeById('MadSKS');
919
919
  }
920
920
  const route = routeByDollarCommand(command) || routeById('SKS');
921
- if (route?.id === 'SKS' && looksLikeTeamDefaultWork(stripDollarCommand(text)))
921
+ if (route?.id === 'SKS' && looksLikeExplicitParallelWorkRequest(stripDollarCommand(text)))
922
922
  return routeById('Naruto');
923
923
  if (route?.id === 'Team' && command === 'TEAM')
924
924
  return routeById('Naruto');
@@ -938,12 +938,12 @@ export function routePrompt(prompt) {
938
938
  if (looksLikeTinyDirectFix(text))
939
939
  return routeById('DFix');
940
940
  if (looksLikeQuestionShapedDirective(text))
941
- return routeById('Naruto');
941
+ return routeById('SKS');
942
942
  if (looksLikeAnswerOnlyRequest(text))
943
943
  return routeById('Answer');
944
944
  if (/\b(SQL|Supabase|Postgres|migration|RLS|Prisma|Drizzle|Knex|database|DB|execute_sql)\b/i.test(text))
945
945
  return routeById('DB');
946
- if (/\b(team|multi-agent|subagent|parallel agents|agent team)\b|병렬|νŒ€/i.test(text))
946
+ if (looksLikeExplicitParallelWorkRequest(text))
947
947
  return routeById('Naruto');
948
948
  if (looksLikeChatCaptureRequest(text) && !looksLikeAnswerOnlyRequest(text))
949
949
  return routeById('Team');
@@ -964,7 +964,7 @@ export function routePrompt(prompt) {
964
964
  if (/\b(GX|vgraph|visual context|render cartridge|wiki coordinate|rgba|trig|llm wiki)\b/i.test(text))
965
965
  return routeById('GX');
966
966
  if (looksLikeTeamDefaultWork(text))
967
- return routeById('Naruto');
967
+ return routeById('SKS');
968
968
  return routeById('SKS');
969
969
  }
970
970
  export function looksLikeComputerUseFastLane(prompt = '') {
@@ -984,6 +984,12 @@ export function looksLikeTeamDefaultWork(prompt = '') {
984
984
  return false;
985
985
  return looksLikeCodeChangingWork(text) || looksLikeDirectWorkRequest(text);
986
986
  }
987
+ export function looksLikeExplicitParallelWorkRequest(prompt = '') {
988
+ const text = String(prompt || '').trim();
989
+ if (!text)
990
+ return false;
991
+ return /\b(?:team|multi-agent|multiagent|subagent|parallel agents?|agent team|shadow\s*clone|naruto|kage\s*bunshin|swarm|fan\s*out)\b|병렬|λ©€ν‹°\s*μ—μ΄μ „νŠΈ|μ„œλΈŒ\s*μ—μ΄μ „νŠΈ|νŒ€\s*(?:으둜|μž‘μ—…|리뷰|ꡬ성|μ‹€ν–‰)|λ‚˜λ£¨ν† |λΆ„μ‹ /i.test(text);
992
+ }
987
993
  export function looksLikeAnswerOnlyRequest(prompt = '') {
988
994
  const text = String(prompt || '').trim();
989
995
  if (!text)
@@ -1045,7 +1051,7 @@ export function routeRequiresSubagents(route, prompt = '') {
1045
1051
  if (route.id === 'Team' || route.id === 'Naruto')
1046
1052
  return true;
1047
1053
  if (route.id === 'SKS')
1048
- return looksLikeTeamDefaultWork(prompt);
1054
+ return false;
1049
1055
  if (route.id === 'Help' || route.id === 'Answer' || route.id === 'Wiki' || route.id === 'ComputerUse' || route.id === 'Commit' || route.id === 'CommitAndPush')
1050
1056
  return false;
1051
1057
  if (route.id === 'PPT')
@@ -68,6 +68,9 @@ export async function resolveStopGate(input) {
68
68
  }
69
69
  }
70
70
  }
71
+ if (missionId) {
72
+ return makeResolution(root, route, missionId, null, null, checkedPaths, statePath, stateMissionId, 'current_or_explicit_mission_gate_not_found');
73
+ }
71
74
  // 4. latest mission fallback
72
75
  const latest = await findLatestMission(root);
73
76
  if (latest) {
@@ -1,2 +1,2 @@
1
- export const PACKAGE_VERSION = '4.8.3';
1
+ export const PACKAGE_VERSION = '4.8.5';
2
2
  //# sourceMappingURL=version.js.map
@@ -35,12 +35,17 @@ const hasVisibleLabelSource = generatedSource.includes('NSStatusItem.variableLen
35
35
  // once-dragged item does not jump back behind the notch on the next doctor --fix.
36
36
  const hasAutosaveNameSource = generatedSource.includes('statusItem.autosaveName = "com.sneakoscope.sks-menubar"');
37
37
  const hasExplicitVisibleSource = generatedSource.includes('statusItem.isVisible = true');
38
+ const terminalCommandLine = generatedSource.split(/\r?\n/)
39
+ .find((line) => line.includes('do script') && line.includes('escaped')) || '';
40
+ const hasTerminalCommandInterpolation = terminalCommandLine.includes(String.raw `\(escaped)`)
41
+ && !terminalCommandLine.includes(String.raw `\"(escaped)\"`);
38
42
  const hasNoUnconditionalKeepAlive = !launchAgentSource.includes('<key>KeepAlive</key>');
39
43
  const hasInteractiveProcessType = launchAgentSource.includes('<key>ProcessType</key>')
40
44
  && launchAgentSource.includes('<string>Interactive</string>');
41
45
  const expectedMenuItems = [
42
46
  'Use codex-lb',
43
47
  'Use ChatGPT OAuth',
48
+ 'Set codex-lb Domain and Key',
44
49
  'Set OpenRouter Key and GLM Profiles',
45
50
  'Fast Check',
46
51
  'SKS Version Check',
@@ -75,6 +80,7 @@ const ok = process.platform === 'darwin'
75
80
  && hasVisibleLabelSource
76
81
  && hasAutosaveNameSource
77
82
  && hasExplicitVisibleSource
83
+ && hasTerminalCommandInterpolation
78
84
  && hasNoUnconditionalKeepAlive
79
85
  && hasInteractiveProcessType
80
86
  && launchSkippedForTempHome
@@ -93,9 +99,11 @@ const report = {
93
99
  launch_agent_exists: launchAgentExists,
94
100
  action_script_exists: actionScriptExists,
95
101
  generated_source_path: generatedSourcePath,
102
+ terminal_command_line: terminalCommandLine,
96
103
  has_visible_label_source: hasVisibleLabelSource,
97
104
  has_autosave_name_source: hasAutosaveNameSource,
98
105
  has_explicit_visible_source: hasExplicitVisibleSource,
106
+ has_terminal_command_interpolation: hasTerminalCommandInterpolation,
99
107
  has_no_unconditional_keepalive: hasNoUnconditionalKeepAlive,
100
108
  has_interactive_process_type: hasInteractiveProcessType,
101
109
  launch_skipped_for_temp_home: launchSkippedForTempHome,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sneakoscope",
3
3
  "displayName": "γ……γ…‹γ……",
4
- "version": "4.8.3",
4
+ "version": "4.8.5",
5
5
  "description": "Sneakoscope Codex: fast proof-first Codex trust layer with image-based Voxel TriWiki.",
6
6
  "type": "module",
7
7
  "homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",