sneakoscope 6.0.2 → 6.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +3 -1
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/dist/bin/fast-inline.js +5 -0
  5. package/dist/bin/sks-dispatch.js +2 -0
  6. package/dist/cli/install-helpers.js +41 -6
  7. package/dist/commands/codex-app.js +8 -3
  8. package/dist/commands/codex-lb.js +37 -12
  9. package/dist/config/skills-manifest.json +57 -57
  10. package/dist/core/agents/agent-effort-policy.js +46 -33
  11. package/dist/core/agents/agent-orchestrator.js +1 -1
  12. package/dist/core/agents/agent-proof-evidence.js +16 -2
  13. package/dist/core/agents/agent-roster.js +2 -2
  14. package/dist/core/agents/native-cli-session-swarm.js +18 -1
  15. package/dist/core/agents/native-worker-backend-router.js +179 -108
  16. package/dist/core/agents/parallel-write-fixture.js +15 -10
  17. package/dist/core/agents/runtime-proof-summary.js +21 -3
  18. package/dist/core/codex-app/codex-app-restart.js +38 -8
  19. package/dist/core/codex-app/sks-menubar.js +92 -37
  20. package/dist/core/codex-app.js +26 -4
  21. package/dist/core/codex-control/codex-model-metadata.js +8 -4
  22. package/dist/core/codex-lb/codex-lb-env.js +78 -0
  23. package/dist/core/commands/basic-cli.js +15 -0
  24. package/dist/core/commands/naruto-command.js +37 -9
  25. package/dist/core/naruto/naruto-write-e2e.js +3 -4
  26. package/dist/core/provider/model-router.js +64 -3
  27. package/dist/core/release/package-size-budget.js +2 -0
  28. package/dist/core/routes/constants.js +1 -1
  29. package/dist/core/routes/model-mode-router.js +3 -0
  30. package/dist/core/routes.js +7 -9
  31. package/dist/core/update/update-migration-state.js +1 -1
  32. package/dist/core/update/update-notice.js +23 -0
  33. package/dist/core/update-check.js +46 -10
  34. package/dist/core/version.js +1 -1
  35. package/dist/scripts/codex-app-provider-model-ui-check.js +17 -2
  36. package/dist/scripts/doctor-fix-proves-codex-read-check.js +1 -1
  37. package/dist/scripts/gate-policy-audit-check.js +2 -2
  38. package/dist/scripts/legacy-upgrade-matrix-check.js +1 -1
  39. package/dist/scripts/lib/native-cli-session-swarm-check-lib.js +2 -1
  40. package/dist/scripts/naruto-shadow-clone-swarm-check.js +88 -50
  41. package/dist/scripts/npm-publish-performance-check.js +2 -1
  42. package/dist/scripts/packlist-performance-check.js +5 -2
  43. package/dist/scripts/release-dag-full-coverage-check.js +7 -7
  44. package/dist/scripts/release-gate-existence-audit.js +8 -9
  45. package/dist/scripts/scheduler-batch-dispatch-check.js +1 -1
  46. package/dist/scripts/sizecheck.js +7 -2
  47. package/dist/scripts/sks-menubar-install-check.js +7 -0
  48. package/dist/scripts/update-default-command-check.js +4 -2
  49. package/package.json +3 -16
@@ -151,8 +151,8 @@ export function buildNarutoCloneRoster(opts = {}) {
151
151
  const cloneReadonly = readonly || !writeAllowed;
152
152
  const role = mapNarutoRoleToAgentRole(narutoRole);
153
153
  const allowedTools = writeAllowed ? ['read', 'search', 'edit', 'test'] : narutoRole === 'verifier' ? ['read', 'search', 'test'] : ['read', 'search'];
154
- // Dynamic per-clone effort like team mode, capped at low/medium and always fast.
155
- const effort = decideNarutoCloneEffort({ persona: { ...base, role, allowed_tools: allowedTools, read_only: cloneReadonly, write_policy: writeAllowed ? 'exclusive Naruto patch-envelope lease required' : 'read-only Naruto role' }, prompt: opts.prompt || '', agentId: id, readonly: cloneReadonly });
154
+ // Dynamic Naruto-only GPT-5.6 model/effort policy; there is no legacy low/medium cap.
155
+ const effort = decideNarutoCloneEffort({ persona: { ...base, role, naruto_role: narutoRole, allowed_tools: allowedTools, read_only: cloneReadonly, write_policy: writeAllowed ? 'exclusive Naruto patch-envelope lease required' : 'read-only Naruto role' }, prompt: opts.prompt || '', agentId: id, readonly: cloneReadonly });
156
156
  const persona = {
157
157
  ...base,
158
158
  id,
@@ -13,6 +13,7 @@ import { appendZellijSlotTelemetry } from '../zellij/zellij-slot-telemetry.js';
13
13
  import { appendParallelRuntimeEvent } from './parallel-runtime-proof.js';
14
14
  import { appendAgentMessage } from './agent-message-bus.js';
15
15
  import { markLoopWorkerInterrupted, registerLoopActiveWorker } from '../loops/loop-interrupt-registry.js';
16
+ import { readCodexLbModelCatalog } from '../codex-lb/codex-lb-env.js';
16
17
  export const NATIVE_CLI_SESSION_SWARM_SCHEMA = 'sks.agent-native-cli-session-swarm.v1';
17
18
  export function createNativeCliSessionSwarmRecorder(root, input) {
18
19
  return new NativeCliSessionSwarmRecorder(root, input);
@@ -26,11 +27,26 @@ class NativeCliSessionSwarmRecorder {
26
27
  writeLock = Promise.resolve();
27
28
  nextPaneToken = -1;
28
29
  visibleZellijReservations = new Set();
30
+ narutoModelCatalog = null;
29
31
  constructor(root, input) {
30
32
  this.root = root;
31
33
  this.input = input;
32
34
  }
33
35
  async initialize() {
36
+ if (/\$?naruto/i.test(this.input.route) && (this.input.backend === 'codex-sdk' || this.input.backend === 'zellij')) {
37
+ this.narutoModelCatalog = await readCodexLbModelCatalog().catch(() => ({
38
+ schema: 'sks.codex-lb-model-catalog.v1',
39
+ ok: false,
40
+ status: 'blocked',
41
+ models: [],
42
+ model_efforts: {},
43
+ blockers: ['codex_lb_model_catalog_unavailable']
44
+ }));
45
+ if (this.narutoModelCatalog?.ok !== true) {
46
+ const blockers = Array.isArray(this.narutoModelCatalog?.blockers) ? this.narutoModelCatalog.blockers.join(',') : 'unavailable';
47
+ throw new Error(`naruto_model_catalog_preflight_failed:${blockers}`);
48
+ }
49
+ }
34
50
  await this.persist();
35
51
  }
36
52
  async launchWorker(ctx) {
@@ -72,7 +88,8 @@ class NativeCliSessionSwarmRecorder {
72
88
  source_intelligence_refs: ctx.agent.source_intelligence_refs || null,
73
89
  goal_mode_ref: ctx.agent.goal_mode_ref || null,
74
90
  strategy_refs: ctx.slice?.strategy_refs || null,
75
- recursion_guard_env: true
91
+ recursion_guard_env: true,
92
+ naruto_model_catalog: this.narutoModelCatalog
76
93
  };
77
94
  await writeJsonAtomic(path.join(this.root, intakeRel), intake);
78
95
  const cliPath = await resolveWorkerCliPath();
@@ -10,8 +10,8 @@ import { normalizeAgentPatchEnvelope } from './agent-patch-schema.js';
10
10
  import { runCodexTask } from '../codex-control/codex-control-plane.js';
11
11
  import { CODEX_AGENT_WORKER_RESULT_SCHEMA_ID, codexAgentWorkerResultSchema } from '../codex-control/schemas/agent-worker-result.schema.js';
12
12
  import { leanEngineeringCompactText, leanPolicyReference } from '../lean-engineering-policy.js';
13
- import { readLbHealth } from '../codex-lb/codex-lb-env.js';
14
- import { categoryForWorkerRole, modelRouteReason, routeModel } from '../provider/model-router.js';
13
+ import { readCodexLbModelCatalog, readLbHealth } from '../codex-lb/codex-lb-env.js';
14
+ import { categoryForWorkerRole, isNarutoGpt56Model, modelRouteReason, routeModel } from '../provider/model-router.js';
15
15
  export const NATIVE_WORKER_BACKEND_ROUTER_SCHEMA = 'sks.native-worker-backend-router.v1';
16
16
  export async function runNativeWorkerBackendRouter(input) {
17
17
  const root = path.resolve(input.agentRoot);
@@ -19,6 +19,7 @@ export async function runNativeWorkerBackendRouter(input) {
19
19
  const requestedBackend = String(input.backend || '');
20
20
  let backend = normalizeBackend(requestedBackend);
21
21
  backend = await maybeAutoSelectOllamaBackend(backend, input);
22
+ const narutoRequest = isNarutoWorkerRequest(input);
22
23
  const reportRel = path.join(input.workerDirRel, 'worker-backend-router-report.json');
23
24
  const startedAt = nowIso();
24
25
  let result;
@@ -27,7 +28,12 @@ export async function runNativeWorkerBackendRouter(input) {
27
28
  let proofLevel = 'blocked';
28
29
  let outputLastMessagePath = null;
29
30
  let modelRouting = null;
30
- if (!input.guard?.ok) {
31
+ const narutoBackendBlocker = narutoWorkerBackendBlocker(backend, narutoRequest);
32
+ if (narutoBackendBlocker) {
33
+ const blocker = narutoBackendBlocker;
34
+ result = validateAgentWorkerResult(blockedResult(input, [blocker]));
35
+ }
36
+ else if (!input.guard?.ok) {
31
37
  result = validateAgentWorkerResult(blockedResult(input, ['native_cli_worker_recursion_guard_missing']));
32
38
  }
33
39
  else if (requestedBackend === 'codex-exec') {
@@ -96,104 +102,110 @@ export async function runNativeWorkerBackendRouter(input) {
96
102
  else if (backend === 'codex-sdk' || backend === 'zellij' || backend === 'local-llm') {
97
103
  const localPreferred = backend === 'local-llm';
98
104
  modelRouting = await resolveWorkerModelRouting(input);
99
- const sdkTask = await runCodexTask({
100
- route: String(input.intake.route || '$Agent'),
101
- tier: 'worker',
102
- missionId: String(input.intake.mission_id || input.intake.parent_mission_id || ''),
103
- workItemId: String(input.slice?.id || ''),
104
- slotId: String(input.agent.slot_id || input.agent.id || ''),
105
- generationIndex: Number(input.agent.generation_index || 1),
106
- sessionId: String(input.agent.session_id || ''),
107
- cwd: String(input.intake.cwd || root),
108
- prompt: buildWorkerPrompt(input.slice),
109
- model: modelRouting.choice.model,
110
- reasoningEffort: modelRouting.choice.reasoning,
111
- modelReasoningEffort: modelRouting.choice.reasoning,
112
- serviceTier: modelRouting.choice.serviceTier,
113
- inputFiles: input.intake.input_files || [],
114
- inputImages: input.intake.input_images || [],
115
- outputSchemaId: CODEX_AGENT_WORKER_RESULT_SCHEMA_ID,
116
- outputSchema: codexAgentWorkerResultSchema,
117
- sandboxPolicy: hasWriteLease(input.slice, input.intake) ? 'workspace-write' : 'read-only',
118
- requestedScopeContract: {
119
- id: String(input.intake.lease_id || input.slice?.id || ''),
105
+ if (modelRouting.blockers.length) {
106
+ proofLevel = 'blocked';
107
+ result = validateAgentWorkerResult(blockedResult(input, modelRouting.blockers));
108
+ }
109
+ else {
110
+ const sdkTask = await runCodexTask({
120
111
  route: String(input.intake.route || '$Agent'),
121
- read_only: !hasWriteLease(input.slice, input.intake),
122
- allowed_paths: writePaths(input.slice, input.intake),
123
- write_paths: writePaths(input.slice, input.intake),
124
- model_route_reason: modelRouting.reason,
112
+ tier: 'worker',
113
+ missionId: String(input.intake.mission_id || input.intake.parent_mission_id || ''),
114
+ workItemId: String(input.slice?.id || ''),
115
+ slotId: String(input.agent.slot_id || input.agent.id || ''),
116
+ generationIndex: Number(input.agent.generation_index || 1),
117
+ sessionId: String(input.agent.session_id || ''),
118
+ cwd: String(input.intake.cwd || root),
119
+ prompt: buildWorkerPrompt(input.slice),
120
+ model: modelRouting.choice.model,
121
+ reasoningEffort: modelRouting.choice.reasoning,
122
+ modelReasoningEffort: modelRouting.choice.reasoning,
123
+ serviceTier: modelRouting.choice.serviceTier,
124
+ inputFiles: input.intake.input_files || [],
125
+ inputImages: input.intake.input_images || [],
126
+ outputSchemaId: CODEX_AGENT_WORKER_RESULT_SCHEMA_ID,
127
+ outputSchema: codexAgentWorkerResultSchema,
128
+ sandboxPolicy: hasWriteLease(input.slice, input.intake) ? 'workspace-write' : 'read-only',
129
+ requestedScopeContract: {
130
+ id: String(input.intake.lease_id || input.slice?.id || ''),
131
+ route: String(input.intake.route || '$Agent'),
132
+ read_only: !hasWriteLease(input.slice, input.intake),
133
+ allowed_paths: writePaths(input.slice, input.intake),
134
+ write_paths: writePaths(input.slice, input.intake),
135
+ model_route_reason: modelRouting.reason,
136
+ service_tier: modelRouting.choice.serviceTier,
137
+ user_confirmed_full_access: false,
138
+ mad_sks_authorized: input.intake.mad_sks_authorized === true || process.env.SKS_MAD_SKS_ACTIVE === '1'
139
+ },
140
+ backendPreference: localPreferred ? ['local-llm', 'codex-sdk'] : ['codex-sdk'],
141
+ allowLocalLlm: localPreferred,
142
+ ...(localPreferred ? { localLlmPolicy: {
143
+ mode: 'local_preferred',
144
+ requiresGptFinal: true
145
+ } } : {}),
146
+ mutationLedgerRoot: path.join(root, input.workerDirRel),
147
+ zellijPaneId: await readZellijPaneId(root, input.workerDirRel),
148
+ reliabilityPolicy: {
149
+ maxEmptyResultRetries: 1,
150
+ timeoutClass: 'standard'
151
+ }
152
+ });
153
+ outputLastMessagePath = sdkTask.workerResultPath;
154
+ const sdkWorkerResult = normalizeSdkWorkerResultPaths(await readJson(sdkTask.workerResultPath, null), input, pathRoot);
155
+ patchEnvelopes = normalizeSdkPatchEnvelopes(sdkWorkerResult?.patch_envelopes || [], input, sdkTask.sdkThreadId, pathRoot);
156
+ proofLevel = sdkTask.ok ? (patchEnvelopes.length ? 'model_authored' : sdkTask.backend === 'local-llm' ? 'local_llm_worker_proven' : 'codex_sdk_thread_proven') : 'blocked';
157
+ const sdkReport = {
158
+ schema: 'sks.codex-sdk-worker-adapter.v1',
159
+ backend: sdkTask.backend,
160
+ backend_family: sdkTask.backend_family,
161
+ sdk_thread_id: sdkTask.sdkThreadId,
162
+ sdk_run_id: sdkTask.sdkRunId,
163
+ local_llm_proof_path: sdkTask.localLlmProofPath || null,
164
+ stream_event_count: sdkTask.streamEventCount,
165
+ structured_output_valid: sdkTask.structuredOutputValid,
166
+ worker_result_path: sdkTask.workerResultPath,
167
+ patch_envelope_path: sdkTask.patchEnvelopePath || null,
168
+ model: modelRouting.choice.model,
169
+ model_reasoning_effort: modelRouting.choice.reasoning,
125
170
  service_tier: modelRouting.choice.serviceTier,
126
- user_confirmed_full_access: false,
127
- mad_sks_authorized: input.intake.mad_sks_authorized === true || process.env.SKS_MAD_SKS_ACTIVE === '1'
128
- },
129
- backendPreference: localPreferred ? ['local-llm', 'codex-sdk'] : ['codex-sdk'],
130
- allowLocalLlm: localPreferred,
131
- ...(localPreferred ? { localLlmPolicy: {
132
- mode: 'local_preferred',
133
- requiresGptFinal: true
134
- } } : {}),
135
- mutationLedgerRoot: path.join(root, input.workerDirRel),
136
- zellijPaneId: await readZellijPaneId(root, input.workerDirRel),
137
- reliabilityPolicy: {
138
- maxEmptyResultRetries: 1,
139
- timeoutClass: 'standard'
140
- }
141
- });
142
- outputLastMessagePath = sdkTask.workerResultPath;
143
- const sdkWorkerResult = normalizeSdkWorkerResultPaths(await readJson(sdkTask.workerResultPath, null), input, pathRoot);
144
- patchEnvelopes = normalizeSdkPatchEnvelopes(sdkWorkerResult?.patch_envelopes || [], input, sdkTask.sdkThreadId, pathRoot);
145
- proofLevel = sdkTask.ok ? (patchEnvelopes.length ? 'model_authored' : sdkTask.backend === 'local-llm' ? 'local_llm_worker_proven' : 'codex_sdk_thread_proven') : 'blocked';
146
- const sdkReport = {
147
- schema: 'sks.codex-sdk-worker-adapter.v1',
148
- backend: sdkTask.backend,
149
- backend_family: sdkTask.backend_family,
150
- sdk_thread_id: sdkTask.sdkThreadId,
151
- sdk_run_id: sdkTask.sdkRunId,
152
- local_llm_proof_path: sdkTask.localLlmProofPath || null,
153
- stream_event_count: sdkTask.streamEventCount,
154
- structured_output_valid: sdkTask.structuredOutputValid,
155
- worker_result_path: sdkTask.workerResultPath,
156
- patch_envelope_path: sdkTask.patchEnvelopePath || null,
157
- model: modelRouting.choice.model,
158
- model_reasoning_effort: modelRouting.choice.reasoning,
159
- service_tier: modelRouting.choice.serviceTier,
160
- model_route_reason: modelRouting.reason,
161
- blockers: sdkTask.blockers
162
- };
163
- childReports = [sdkReport];
164
- result = validateAgentWorkerResult({
165
- ...sdkWorkerResult,
166
- agent_id: workerOwnerId(input),
167
- session_id: input.agent.session_id,
168
- persona_id: input.agent.persona_id || input.agent.id,
169
- task_slice_id: input.slice?.id || '',
170
- backend: sdkTask.backend === 'local-llm' ? 'local-llm' : 'codex-sdk',
171
- patch_envelopes: patchEnvelopes,
172
- ...(patchEnvelopes.length ? {} : { no_patch_reason: buildNoPatchReason(input, sdkTask.backend || backend) }),
173
- codex_child_report: sdkReport,
174
- codex_sdk_thread: sdkReport,
175
- model_authored_patch_envelopes: patchEnvelopes.length > 0,
176
- fixture_patch_envelopes: false,
177
- artifacts: [...new Set([
178
- ...(sdkWorkerResult?.artifacts || []),
179
- path.relative(root, sdkTask.workerResultPath),
180
- path.join(input.workerDirRel, 'codex-control-proof.json'),
181
- path.join(input.workerDirRel, 'codex-thread-registry.json'),
182
- sdkTask.backend === 'local-llm' ? path.join(input.workerDirRel, 'local-llm-events.jsonl') : path.join(input.workerDirRel, 'codex-sdk-events.jsonl'),
183
- ...(sdkTask.localLlmProofPath ? [path.relative(root, sdkTask.localLlmProofPath)] : [])
184
- ])],
185
- blockers: [...(sdkWorkerResult?.blockers || []), ...sdkTask.blockers],
186
- verification: {
187
- status: sdkTask.ok ? 'passed' : 'failed',
188
- checks: [
189
- ...(sdkWorkerResult?.verification?.checks || []),
190
- sdkTask.backend === 'local-llm' ? 'local-llm-control-plane' : 'codex-sdk-control-plane',
191
- sdkTask.backend === 'local-llm' ? 'local-llm-event-stream' : 'codex-sdk-event-stream',
192
- sdkTask.backend === 'local-llm' ? 'local-llm-structured-output' : 'codex-sdk-structured-output',
193
- ...(sdkTask.backend === 'local-llm' ? ['gpt-final-required-before-acceptance'] : [])
194
- ]
195
- }
196
- });
171
+ model_route_reason: modelRouting.reason,
172
+ blockers: sdkTask.blockers
173
+ };
174
+ childReports = [sdkReport];
175
+ result = validateAgentWorkerResult({
176
+ ...sdkWorkerResult,
177
+ agent_id: workerOwnerId(input),
178
+ session_id: input.agent.session_id,
179
+ persona_id: input.agent.persona_id || input.agent.id,
180
+ task_slice_id: input.slice?.id || '',
181
+ backend: sdkTask.backend === 'local-llm' ? 'local-llm' : 'codex-sdk',
182
+ patch_envelopes: patchEnvelopes,
183
+ ...(patchEnvelopes.length ? {} : { no_patch_reason: buildNoPatchReason(input, sdkTask.backend || backend) }),
184
+ codex_child_report: sdkReport,
185
+ codex_sdk_thread: sdkReport,
186
+ model_authored_patch_envelopes: patchEnvelopes.length > 0,
187
+ fixture_patch_envelopes: false,
188
+ artifacts: [...new Set([
189
+ ...(sdkWorkerResult?.artifacts || []),
190
+ path.relative(root, sdkTask.workerResultPath),
191
+ path.join(input.workerDirRel, 'codex-control-proof.json'),
192
+ path.join(input.workerDirRel, 'codex-thread-registry.json'),
193
+ sdkTask.backend === 'local-llm' ? path.join(input.workerDirRel, 'local-llm-events.jsonl') : path.join(input.workerDirRel, 'codex-sdk-events.jsonl'),
194
+ ...(sdkTask.localLlmProofPath ? [path.relative(root, sdkTask.localLlmProofPath)] : [])
195
+ ])],
196
+ blockers: [...(sdkWorkerResult?.blockers || []), ...sdkTask.blockers],
197
+ verification: {
198
+ status: sdkTask.ok ? 'passed' : 'failed',
199
+ checks: [
200
+ ...(sdkWorkerResult?.verification?.checks || []),
201
+ sdkTask.backend === 'local-llm' ? 'local-llm-control-plane' : 'codex-sdk-control-plane',
202
+ sdkTask.backend === 'local-llm' ? 'local-llm-event-stream' : 'codex-sdk-event-stream',
203
+ sdkTask.backend === 'local-llm' ? 'local-llm-structured-output' : 'codex-sdk-structured-output',
204
+ ...(sdkTask.backend === 'local-llm' ? ['gpt-final-required-before-acceptance'] : [])
205
+ ]
206
+ }
207
+ });
208
+ }
197
209
  }
198
210
  else {
199
211
  const zellijRun = await runZellijAgent(input.agent, input.slice, {
@@ -262,24 +274,69 @@ export async function runNativeWorkerBackendRouter(input) {
262
274
  patchEnvelopes
263
275
  };
264
276
  }
265
- async function resolveWorkerModelRouting(input) {
266
- const category = categoryForWorkerRole(String(input.agent?.role || input.slice?.role || input.slice?.type || input.agent?.persona_id || 'executor'));
267
- const lbHealth = await readLbHealth().catch(() => null);
268
- const explicitModel = String(process.env.SKS_WORKER_MODEL || process.env.SKS_AGENT_MODEL || '').trim();
269
- const explicitReasoning = normalizeModelReasoning(process.env.SKS_WORKER_REASONING || process.env.SKS_WORKER_MODEL_REASONING || '');
270
- const explicitTier = normalizeServiceTier(process.env.SKS_WORKER_SERVICE_TIER || process.env.SKS_SERVICE_TIER || '');
271
- const routed = explicitModel
277
+ export async function resolveWorkerModelRouting(input, deps = {}) {
278
+ const narutoOnly = Boolean(input.agent?.naruto_role) || /\$?naruto/i.test(String(input.intake?.route || ''));
279
+ const taskKindText = [
280
+ input.slice?.work_item_kind,
281
+ input.slice?.kind,
282
+ input.slice?.type,
283
+ input.slice?.title,
284
+ input.slice?.required_role,
285
+ input.slice?.role,
286
+ input.agent?.naruto_role,
287
+ input.agent?.role,
288
+ input.agent?.persona_id
289
+ ].map((value) => String(value || '')).join(' ');
290
+ const riskText = [taskKindText, input.slice?.risk, input.slice?.risk_focus, input.slice?.acceptance, input.slice?.parent_prompt, input.slice?.description, input.intake?.prompt].map((value) => String(value || '')).join(' ');
291
+ const category = categoryForWorkerRole(String(input.agent?.role || 'executor'), taskKindText);
292
+ const env = deps.env || process.env;
293
+ const lbHealth = Object.prototype.hasOwnProperty.call(deps, 'lbHealth') ? deps.lbHealth : await readLbHealth().catch(() => null);
294
+ const lbCatalog = narutoOnly
295
+ ? Object.prototype.hasOwnProperty.call(deps, 'lbCatalog')
296
+ ? deps.lbCatalog
297
+ : input.intake?.naruto_model_catalog || await readCodexLbModelCatalog().catch(() => null)
298
+ : null;
299
+ const explicitModel = String(env.SKS_WORKER_MODEL || env.SKS_AGENT_MODEL || '').trim();
300
+ const explicitReasoningRaw = String(env.SKS_WORKER_REASONING || env.SKS_WORKER_MODEL_REASONING || '').trim();
301
+ const explicitTierRaw = String(env.SKS_WORKER_SERVICE_TIER || env.SKS_SERVICE_TIER || '').trim();
302
+ const explicitReasoning = normalizeModelReasoning(explicitReasoningRaw);
303
+ const explicitTier = normalizeServiceTier(explicitTierRaw);
304
+ const explicitNarutoModelInvalid = narutoOnly && Boolean(explicitModel) && !isNarutoGpt56Model(explicitModel);
305
+ const explicitNarutoReasoningInvalid = narutoOnly && Boolean(explicitReasoningRaw) && !explicitReasoning;
306
+ const explicitNarutoTierInvalid = narutoOnly && Boolean(explicitTierRaw) && !explicitTier;
307
+ const routed = explicitModel && !narutoOnly
272
308
  ? {
273
309
  model: explicitModel,
274
310
  reasoning: explicitReasoning || normalizeModelReasoning(input.agent?.model_reasoning_effort) || 'medium',
275
311
  serviceTier: explicitTier || input.fastModePolicy.service_tier || 'fast'
276
312
  }
277
- : await routeModel(category, { lbHealth, model: input.agent?.model || null });
313
+ : await routeModel(category, {
314
+ lbHealth,
315
+ ...(narutoOnly ? {
316
+ narutoOnly: true,
317
+ taskText: taskKindText,
318
+ riskText,
319
+ availableModels: lbCatalog?.models || [],
320
+ availableModelEfforts: lbCatalog?.model_efforts || {},
321
+ ...(explicitModel ? { model: explicitModel } : {})
322
+ } : { model: input.agent?.model || null })
323
+ });
324
+ const blockers = [
325
+ ...(narutoOnly && !lbCatalog?.ok ? (lbCatalog?.blockers || ['codex_lb_model_catalog_unavailable']) : []),
326
+ ...(explicitNarutoModelInvalid ? ['naruto_worker_model_outside_gpt_5_6_family'] : []),
327
+ ...(explicitNarutoReasoningInvalid ? ['naruto_reasoning_override_invalid'] : []),
328
+ ...(explicitNarutoTierInvalid ? ['naruto_service_tier_override_invalid'] : []),
329
+ ...(narutoOnly && explicitReasoning && explicitReasoning !== routed.reasoning ? ['naruto_reasoning_override_conflicts_with_policy'] : []),
330
+ ...(narutoOnly && explicitTier && explicitTier !== 'fast' ? ['naruto_service_tier_override_conflicts_with_policy'] : []),
331
+ ...(narutoOnly && !routed.model ? ['naruto_required_gpt_5_6_model_unavailable'] : [])
332
+ ];
278
333
  return {
279
334
  category,
280
335
  choice: routed,
281
336
  explicit: Boolean(explicitModel),
282
337
  lb_health: lbHealth,
338
+ lb_catalog: lbCatalog,
339
+ blockers: [...new Set(blockers)],
283
340
  reason: modelRouteReason(category, routed, {
284
341
  explicit: Boolean(explicitModel),
285
342
  quotaLow: lbHealth?.quota_low === true,
@@ -289,7 +346,7 @@ async function resolveWorkerModelRouting(input) {
289
346
  }
290
347
  function normalizeModelReasoning(value) {
291
348
  const text = String(value || '').toLowerCase();
292
- return text === 'low' || text === 'medium' || text === 'high' || text === 'xhigh' ? text : null;
349
+ return text === 'low' || text === 'medium' || text === 'high' || text === 'xhigh' || text === 'max' || text === 'ultra' ? text : null;
293
350
  }
294
351
  function normalizeServiceTier(value) {
295
352
  const text = String(value || '').toLowerCase();
@@ -298,6 +355,8 @@ function normalizeServiceTier(value) {
298
355
  async function maybeAutoSelectOllamaBackend(backend, input) {
299
356
  if (backend !== 'codex-sdk')
300
357
  return backend;
358
+ if (isNarutoWorkerRequest(input))
359
+ return backend;
301
360
  if (input.intake?.backend_explicit === true || input.intake?.no_ollama === true)
302
361
  return backend;
303
362
  const config = await resolveOllamaWorkerConfig({
@@ -310,6 +369,18 @@ async function maybeAutoSelectOllamaBackend(backend, input) {
310
369
  const policy = classifyOllamaWorkerSlice(input.slice, { route: input.intake?.route, agent: input.agent });
311
370
  return policy.ok ? 'local-llm' : backend;
312
371
  }
372
+ function isNarutoWorkerRequest(input) {
373
+ return Boolean(input.agent?.naruto_role) || /\$?naruto/i.test(String(input.intake?.route || ''));
374
+ }
375
+ export function narutoWorkerBackendBlocker(backend, narutoRequest = true) {
376
+ if (!narutoRequest)
377
+ return null;
378
+ if (backend === 'process')
379
+ return 'naruto_gpt_5_6_family_only_process_backend_forbidden';
380
+ if (backend === 'ollama' || backend === 'local-llm')
381
+ return 'naruto_gpt_5_6_family_only_local_backend_forbidden';
382
+ return null;
383
+ }
313
384
  function normalizeBackend(value) {
314
385
  return value === 'fake' || value === 'process' || value === 'codex-sdk' || value === 'zellij' || value === 'ollama' || value === 'local-llm' ? value : null;
315
386
  }
@@ -175,24 +175,29 @@ async function runTypecheck(repoRoot) {
175
175
  return runCommand([localTsc, '-p', 'tsconfig.json', '--noEmit'], repoRoot);
176
176
  }
177
177
  async function cleanupWorktreesAndBranches(repoRoot, tmpRoot, workers, before, keepTmp) {
178
+ const removeResults = [];
178
179
  for (const worker of workers) {
179
- await runCommand(['git', 'worktree', 'remove', '--force', path.join(tmpRoot, 'worktrees', worker.id)], repoRoot);
180
+ removeResults.push(await runCommand(['git', 'worktree', 'remove', '--force', path.join(tmpRoot, 'worktrees', worker.id)], repoRoot));
180
181
  }
181
- await runCommand(['git', 'worktree', 'prune'], repoRoot);
182
+ const prune = await runCommand(['git', 'worktree', 'prune'], repoRoot);
183
+ const branchResults = [];
182
184
  for (const worker of workers) {
183
- await runCommand(['git', 'branch', '-D', worker.branch], repoRoot);
185
+ branchResults.push(await runCommand(['git', 'branch', '-D', worker.branch], repoRoot));
184
186
  }
185
187
  const after = await runCommand(['git', 'worktree', 'list', '--porcelain'], repoRoot);
188
+ const branches = await runCommand(['git', 'branch', '--list', ...workers.map((worker) => worker.branch)], repoRoot);
186
189
  const status = await runCommand(['git', 'status', '--short'], repoRoot);
187
190
  if (!keepTmp)
188
- await fsp.rm(tmpRoot, { recursive: true, force: true });
191
+ await fsp.rm(tmpRoot, { recursive: true, force: true, maxRetries: 5, retryDelay: 50 });
189
192
  const tmpRemoved = keepTmp ? false : !(await exists(tmpRoot));
193
+ const workerWorktreesRemoved = after.ok && workers.every((worker) => !after.stdout_tail.includes(worker.id));
194
+ const workerBranchesRemoved = branches.ok && branches.stdout_tail.trim().length === 0;
190
195
  return {
191
- ok: workers.every((worker) => !after.stdout_tail.includes(worker.id) && !after.stdout_tail.includes(worker.branch)) && status.stdout_tail.trim().length === 0 && tmpRemoved,
196
+ ok: removeResults.every((result) => result.ok) && prune.ok && branchResults.every((result) => result.ok) && workerWorktreesRemoved && workerBranchesRemoved && status.ok && status.stdout_tail.trim().length === 0 && tmpRemoved,
192
197
  worktree_list_before_cleanup: before,
193
198
  worktree_list_after_cleanup: after.stdout_tail,
194
- worker_worktrees_removed: workers.every((worker) => !after.stdout_tail.includes(worker.id)),
195
- worker_branches_removed: workers.every((worker) => !after.stdout_tail.includes(worker.branch)),
199
+ worker_worktrees_removed: workerWorktreesRemoved,
200
+ worker_branches_removed: workerBranchesRemoved,
196
201
  parent_dirty_after_cleanup: status.stdout_tail.trim().length > 0,
197
202
  tmp_removed: tmpRemoved
198
203
  };
@@ -200,12 +205,12 @@ async function cleanupWorktreesAndBranches(repoRoot, tmpRoot, workers, before, k
200
205
  async function bestEffortCleanup(repoRoot, tmpRoot, keepTmp) {
201
206
  const before = await runCommand(['git', 'worktree', 'list', '--porcelain'], repoRoot);
202
207
  if (!keepTmp)
203
- await fsp.rm(tmpRoot, { recursive: true, force: true });
208
+ await fsp.rm(tmpRoot, { recursive: true, force: true, maxRetries: 5, retryDelay: 50 });
204
209
  return {
205
- ok: !keepTmp && !(await exists(tmpRoot)),
210
+ ok: false,
206
211
  worktree_list_before_cleanup: before.stdout_tail,
207
212
  worktree_list_after_cleanup: '',
208
- worker_worktrees_removed: true,
213
+ worker_worktrees_removed: false,
209
214
  worker_branches_removed: false,
210
215
  parent_dirty_after_cleanup: false,
211
216
  tmp_removed: !keepTmp && !(await exists(tmpRoot))
@@ -15,6 +15,7 @@ export async function buildRuntimeProofSummary(root, missionIdInput = 'latest',
15
15
  const scheduler = await readJson(path.join(agentsDir, 'agent-scheduler-state.json'), null);
16
16
  const swarm = await readJson(path.join(agentsDir, 'agent-native-cli-session-swarm.json'), null);
17
17
  const telemetry = await readJson(path.join(dir, 'zellij', 'slot-telemetry.snapshot.json'), null);
18
+ const stopGate = await readJson(path.join(dir, 'stop-gate.json'), null);
18
19
  const governor = await readJson(path.join(agentsDir, 'naruto-concurrency-governor.json'), null);
19
20
  const messagesAll = await readAgentMessageBus(root, missionId, { max: 500 });
20
21
  const recentMessages = await readAgentMessageBus(root, missionId, { max: opts.maxMessages || 8 });
@@ -23,15 +24,17 @@ export async function buildRuntimeProofSummary(root, missionIdInput = 'latest',
23
24
  const failedMessages = messagesAll.filter((row) => row.event_type === 'worker_failed');
24
25
  const errorMessages = messagesAll.filter((row) => row.level === 'error');
25
26
  const telemetryAgeMs = telemetry?.updated_at ? Math.max(0, Date.now() - Date.parse(telemetry.updated_at)) : Number.MAX_SAFE_INTEGER;
27
+ const terminalProofAccepted = canonicalTerminalProofAccepted(stopGate, missionId);
26
28
  const visiblePanes = Number(parallel?.visible_panes ?? swarm?.zellij_pane_worker_sessions ?? telemetryVisiblePaneCount(telemetry) ?? 0);
27
29
  const targetActive = Number(scheduler?.target_active_slots ?? parallel?.target_active_slots ?? swarm?.target_active_slots ?? governor?.target_active_slots ?? 0);
28
30
  const headlessWorkers = Number(parallel?.headless_workers ?? swarm?.headless_overflow_worker_count ?? Math.max(0, targetActive - visiblePanes));
31
+ const parallelBlockers = parallel?.passed === false ? parallel.blockers || ['parallel_runtime_proof_failed'] : [];
29
32
  const blockers = [
30
33
  ...(!parallel ? ['parallel_runtime_proof_missing'] : []),
31
34
  ...(!scheduler ? ['agent_scheduler_state_missing'] : []),
32
- ...(parallel?.passed === false ? parallel.blockers || ['parallel_runtime_proof_failed'] : []),
35
+ ...(terminalProofAccepted ? parallelBlockers.filter((blocker) => String(blocker) !== 'speedup_ratio_below_target') : parallelBlockers),
33
36
  ...(errorMessages.length ? ['agent_message_bus_error_blockers'] : []),
34
- ...(telemetryAgeMs > 3000 ? ['zellij_telemetry_stale'] : []),
37
+ ...(telemetryAgeMs > 3000 && !terminalProofAccepted ? ['zellij_telemetry_stale'] : []),
35
38
  ...(zellijSummary?.blockers || [])
36
39
  ].map(String);
37
40
  const summary = {
@@ -76,6 +79,11 @@ export async function buildRuntimeProofSummary(root, missionIdInput = 'latest',
76
79
  duplicate_slot_anchor_count: Number(zellijSummary?.duplicate_slot_anchor_count || 0)
77
80
  },
78
81
  loops: loopSummary,
82
+ terminal_proof: {
83
+ accepted: terminalProofAccepted,
84
+ gate_file: terminalProofAccepted ? 'stop-gate.json' : null,
85
+ terminal_state: terminalProofAccepted ? String(stopGate?.terminal_state || 'completed') : null
86
+ },
79
87
  blockers
80
88
  };
81
89
  await writeJsonAtomic(path.join(agentsDir, 'runtime-proof-summary.json'), summary);
@@ -83,7 +91,8 @@ export async function buildRuntimeProofSummary(root, missionIdInput = 'latest',
83
91
  }
84
92
  export function renderRuntimeProofSummary(summary) {
85
93
  return [
86
- `Parallel proof: ${summary.parallel.proof_passed ? 'passed' : 'blocked'}`,
94
+ `Parallel proof: ${summary.parallel.proof_passed ? 'passed' : summary.terminal_proof?.accepted ? 'terminal gate accepted' : 'blocked'}`,
95
+ `Canonical terminal proof: ${summary.terminal_proof?.accepted ? 'accepted' : 'not available'}`,
87
96
  `Active workers: ${summary.parallel.max_active_workers}`,
88
97
  `Unique PIDs: ${summary.parallel.unique_worker_pids}`,
89
98
  `Speedup: ${summary.parallel.speedup_ratio}x`,
@@ -102,6 +111,15 @@ export function renderRuntimeProofSummary(summary) {
102
111
  ...(summary.blockers.length ? [`Blockers: ${summary.blockers.join(', ')}`] : [])
103
112
  ].join('\n');
104
113
  }
114
+ function canonicalTerminalProofAccepted(gate, missionId) {
115
+ const blockers = Array.isArray(gate?.blockers) ? gate.blockers : [];
116
+ return gate?.schema === 'sks.stop-gate.v1'
117
+ && String(gate?.mission_id || '') === missionId
118
+ && gate?.passed === true
119
+ && gate?.terminal === true
120
+ && String(gate?.terminal_state || '') === 'completed'
121
+ && blockers.length === 0;
122
+ }
105
123
  function messageStatusLabel(row) {
106
124
  if (row.event_type === 'worker_completed')
107
125
  return '[done]';
@@ -1,15 +1,16 @@
1
1
  import { exists, runProcess, which } from '../fsx.js';
2
2
  export async function restartCodexApp(opts = {}) {
3
3
  const env = opts.env || process.env;
4
- const appName = String(opts.appName || env.SKS_CODEX_APP_NAME || 'Codex');
4
+ const appName = String(opts.appName || env.SKS_CODEX_APP_NAME || 'ChatGPT');
5
+ const bundleId = String(opts.bundleId || env.SKS_CODEX_APP_BUNDLE_ID || 'com.openai.codex');
5
6
  if (opts.enabled === false || env.SKS_SKIP_CODEX_APP_RESTART === '1') {
6
7
  return skipped(appName, 'disabled');
7
8
  }
8
- if (process.platform !== 'darwin')
9
+ if ((opts.platform || process.platform) !== 'darwin')
9
10
  return skipped(appName, 'not_macos');
10
11
  const run = opts.runProcessImpl || runProcess;
11
- const osascript = await which('osascript').catch(() => null) || await exists('/usr/bin/osascript').then((ok) => ok ? '/usr/bin/osascript' : null).catch(() => null);
12
- const open = await which('open').catch(() => null) || await exists('/usr/bin/open').then((ok) => ok ? '/usr/bin/open' : null).catch(() => null);
12
+ const osascript = opts.osascriptPath || await which('osascript').catch(() => null) || await exists('/usr/bin/osascript').then((ok) => ok ? '/usr/bin/osascript' : null).catch(() => null);
13
+ const open = opts.openPath || await which('open').catch(() => null) || await exists('/usr/bin/open').then((ok) => ok ? '/usr/bin/open' : null).catch(() => null);
13
14
  if (!osascript || !open) {
14
15
  return {
15
16
  schema: 'sks.codex-app-restart.v1',
@@ -22,25 +23,54 @@ export async function restartCodexApp(opts = {}) {
22
23
  ]
23
24
  };
24
25
  }
25
- const quit = await run(osascript, ['-e', `tell application ${JSON.stringify(appName)} to quit`], { timeoutMs: 5000, maxOutputBytes: 8192 }).catch((err) => ({
26
+ const appTarget = bundleId ? `application id ${JSON.stringify(bundleId)}` : `application ${JSON.stringify(appName)}`;
27
+ const quit = await run(osascript, ['-e', `tell ${appTarget} to quit`], { timeoutMs: 5000, maxOutputBytes: 8192 }).catch((err) => ({
26
28
  code: 1,
27
29
  stdout: '',
28
30
  stderr: err?.message || String(err)
29
31
  }));
30
- await sleep(Number(opts.delayMs ?? env.SKS_CODEX_APP_RESTART_DELAY_MS ?? 1200));
31
- const launched = await run(open, ['-a', appName], { timeoutMs: 10000, maxOutputBytes: 8192 }).catch((err) => ({
32
+ const quitOk = quit.code === 0 || /not running|Can't get application|application isn't running/i.test(String(quit.stderr || quit.stdout || ''));
33
+ const exitTimeoutMs = Math.max(250, Number(opts.exitTimeoutMs ?? env.SKS_CODEX_APP_EXIT_TIMEOUT_MS ?? 5000));
34
+ const pollMs = Math.max(50, Number(opts.pollMs ?? 100));
35
+ let exited = !quitOk;
36
+ let attempts = 0;
37
+ const deadline = Date.now() + exitTimeoutMs;
38
+ while (quitOk && Date.now() < deadline) {
39
+ attempts += 1;
40
+ const probe = await run(osascript, ['-e', `${appTarget} is running`], { timeoutMs: 1000, maxOutputBytes: 1024 }).catch(() => null);
41
+ if (probe?.code === 0 && String(probe.stdout || '').trim().toLowerCase() === 'false') {
42
+ exited = true;
43
+ break;
44
+ }
45
+ await sleep(pollMs);
46
+ }
47
+ if (quitOk && !exited) {
48
+ return {
49
+ schema: 'sks.codex-app-restart.v1',
50
+ ok: false,
51
+ status: 'blocked',
52
+ app_name: appName,
53
+ bundle_id: bundleId,
54
+ quit: { ok: true, code: quit.code, error: null },
55
+ exit_wait: { ok: false, attempts, error: 'codex_app_exit_timeout' },
56
+ blockers: ['codex_app_exit_timeout']
57
+ };
58
+ }
59
+ await sleep(Number(opts.delayMs ?? env.SKS_CODEX_APP_RESTART_DELAY_MS ?? 150));
60
+ const launched = await run(open, bundleId ? ['-b', bundleId] : ['-a', appName], { timeoutMs: 10000, maxOutputBytes: 8192 }).catch((err) => ({
32
61
  code: 1,
33
62
  stdout: '',
34
63
  stderr: err?.message || String(err)
35
64
  }));
36
- const quitOk = quit.code === 0 || /not running|Can't get application|application isn't running/i.test(String(quit.stderr || quit.stdout || ''));
37
65
  const openOk = launched.code === 0;
38
66
  return {
39
67
  schema: 'sks.codex-app-restart.v1',
40
68
  ok: quitOk && openOk,
41
69
  status: quitOk && openOk ? 'restarted' : 'blocked',
42
70
  app_name: appName,
71
+ bundle_id: bundleId,
43
72
  quit: { ok: quitOk, code: quit.code, error: quitOk ? null : String(quit.stderr || quit.stdout || '').trim() },
73
+ exit_wait: { ok: exited, attempts, error: exited ? null : 'codex_app_exit_timeout' },
44
74
  open: { ok: openOk, code: launched.code, error: openOk ? null : String(launched.stderr || launched.stdout || '').trim() },
45
75
  blockers: [
46
76
  ...(quitOk ? [] : ['codex_app_quit_failed']),