sneakoscope 10.5.3 → 10.5.4
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 +1 -1
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/commands/bridge.js +8 -3
- package/dist/config/skills-manifest.json +1 -1
- package/dist/core/agents/native-worker-backend-router.js +20 -12
- package/dist/core/codex-lb/desktop-controller-v3/openrouter-only-commands.js +71 -7
- package/dist/core/codex-lb/desktop-controller-v3.js +1 -1
- package/dist/core/decisions/child-model-choice.js +2 -1
- package/dist/core/decisions/cli.js +9 -2
- package/dist/core/hooks-runtime/hook-context.js +4 -4
- package/dist/core/hooks-runtime/jev-spawn-routing.js +8 -8
- package/dist/core/hooks-runtime/spawn-role-pin-heal.js +1 -1
- package/dist/core/hooks-runtime/subagent-context.js +3 -3
- package/dist/core/hooks-runtime/subagent-spawn-policy.js +18 -11
- package/dist/core/hooks-runtime.js +1 -1
- package/dist/core/subagents/child-model-allowlist.js +107 -8
- package/dist/core/subagents/child-model-plan.js +18 -27
- package/dist/core/subagents/model-tiers.js +45 -10
- package/dist/core/subagents/naruto-host-credentials.js +24 -5
- package/dist/core/subagents/official-subagent-config.js +6 -6
- package/dist/core/subagents/official-subagent-preparation.js +8 -6
- package/dist/core/subagents/official-subagent-prompt.js +11 -12
- package/dist/core/subagents/official-subagent-runner.js +3 -3
- package/dist/core/subagents/read-only-list-role.js +27 -6
- package/dist/core/subagents/role-model-preferences.js +21 -8
- package/dist/core/version.js +1 -1
- package/dist/native/sks-menubar/Sources/OpenRouterOnlyState.swift +20 -4
- package/dist/native/sks-menubar/Sources/SubagentModelsModels.swift +66 -11
- package/dist/native/sks-menubar/Sources/SubagentModelsViewController.swift +43 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
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.
|
|
18
18
|
<!-- END SKS SEARCH VISIBILITY MARKETING -->
|
|
19
19
|
|
|
20
|
-
Current package: **SKS 10.5.
|
|
20
|
+
Current package: **SKS 10.5.4**. Install the latest stable release from npm.
|
|
21
21
|
|
|
22
22
|
Sneakoscope Codex (`sks`) is a local trust layer for Codex CLI and ChatGPT Desktop. It keeps agent work bounded, preserves project context, records machine-verifiable evidence, and prevents release claims that current tests or artifacts cannot support.
|
|
23
23
|
|
package/dist/commands/bridge.js
CHANGED
|
@@ -40,7 +40,8 @@ export function usage(command = 'bridge') {
|
|
|
40
40
|
'',
|
|
41
41
|
'Provider secrets are accepted only through --api-key-stdin. Readiness changes the exit code only with --strict or --require-ready.',
|
|
42
42
|
'OpenRouter Only Mode and Codex-LB authentication priority are mutually exclusive; turning one on turns the other off.',
|
|
43
|
-
'subagent-models set reads {"subagent_models":[{"model","criteria","reasoning_effort","default"}]} (max 16) from stdin.'
|
|
43
|
+
'subagent-models set reads {"profile":"codex_lb|openai|openrouter_only","subagent_models":[{"model","criteria","reasoning_effort","default"}]} (max 16) from stdin.',
|
|
44
|
+
'The profile must match the current connection. Omitting profile preserves the legacy OpenRouter-list staging behavior.'
|
|
44
45
|
].join('\n');
|
|
45
46
|
}
|
|
46
47
|
export async function run(_command, args = []) {
|
|
@@ -152,7 +153,7 @@ async function parseInvocation(args, io) {
|
|
|
152
153
|
...base,
|
|
153
154
|
request: {
|
|
154
155
|
operation: 'subagent-models.set',
|
|
155
|
-
|
|
156
|
+
...subagentModelsFromStdin(await io.readStdin()),
|
|
156
157
|
no_restart: parsed.flags.has('--no-restart')
|
|
157
158
|
},
|
|
158
159
|
label: 'Subagent model list update'
|
|
@@ -376,7 +377,11 @@ function subagentModelsFromStdin(text) {
|
|
|
376
377
|
: undefined;
|
|
377
378
|
if (!Array.isArray(models))
|
|
378
379
|
throw new BridgeCliError('bridge_subagent_models_stdin_invalid', 'retry_with_stdin');
|
|
379
|
-
|
|
380
|
+
const profile = parsed.profile;
|
|
381
|
+
if (profile !== undefined && profile !== 'openrouter_only' && profile !== 'codex_lb' && profile !== 'openai') {
|
|
382
|
+
throw new BridgeCliError('bridge_subagent_models_profile_invalid', 'retry_with_stdin');
|
|
383
|
+
}
|
|
384
|
+
return { subagent_models: models, ...(profile === undefined ? {} : { profile }) };
|
|
380
385
|
}
|
|
381
386
|
function rejectSecretArgv(args) {
|
|
382
387
|
for (const arg of args) {
|
|
@@ -14,7 +14,7 @@ import { decideSubagentModel } from '../subagents/model-policy.js';
|
|
|
14
14
|
import { latestTierModelSet } from '../subagents/model-tiers.js';
|
|
15
15
|
import { consultJevTurnModel } from '../decisions/integration.js';
|
|
16
16
|
import { chooseChildModel, fallbackChildModel } from '../decisions/child-model-choice.js';
|
|
17
|
-
import {
|
|
17
|
+
import { allowlistedChildModel, effectiveChildModelAllowlist, childModelListProfile, listChildModelEffort, isSubagentModelEffort } from '../subagents/child-model-allowlist.js';
|
|
18
18
|
export const NATIVE_WORKER_BACKEND_ROUTER_SCHEMA = 'sks.native-worker-backend-router.v1';
|
|
19
19
|
export async function runNativeWorkerBackendRouter(input) {
|
|
20
20
|
const root = path.resolve(input.agentRoot);
|
|
@@ -93,8 +93,10 @@ export async function runNativeWorkerBackendRouter(input) {
|
|
|
93
93
|
cwd: String(input.intake.cwd || root),
|
|
94
94
|
prompt: buildWorkerPrompt(input.slice),
|
|
95
95
|
model: modelRouting.choice.model,
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
...(modelRouting.choice.reasoning ? {
|
|
97
|
+
reasoningEffort: modelRouting.choice.reasoning,
|
|
98
|
+
modelReasoningEffort: modelRouting.choice.reasoning
|
|
99
|
+
} : {}),
|
|
98
100
|
serviceTier: modelRouting.choice.serviceTier,
|
|
99
101
|
inputFiles: input.intake.input_files || [],
|
|
100
102
|
inputImages: input.intake.input_images || [],
|
|
@@ -232,7 +234,7 @@ export async function resolveWorkerModelRouting(input, deps = {}) {
|
|
|
232
234
|
const category = categoryForWorkerRole(String(input.agent?.role || 'executor'), taskKindText);
|
|
233
235
|
const env = deps.env || process.env;
|
|
234
236
|
const allowlist = effectiveChildModelAllowlist({ env });
|
|
235
|
-
if (allowlist.mode
|
|
237
|
+
if (allowlist.mode !== 'tiers') {
|
|
236
238
|
return resolveOpenRouterOnlyWorkerRouting(input, deps, { allowlist, category, env, taskKindText, riskText });
|
|
237
239
|
}
|
|
238
240
|
const lbCatalog = narutoOnly
|
|
@@ -306,11 +308,12 @@ export async function resolveWorkerModelRouting(input, deps = {}) {
|
|
|
306
308
|
}
|
|
307
309
|
async function resolveOpenRouterOnlyWorkerRouting(input, deps, ctx) {
|
|
308
310
|
const { allowlist, category, env } = ctx;
|
|
309
|
-
const state = {
|
|
311
|
+
const state = { subagent_models: allowlist.entries };
|
|
310
312
|
const explicitModel = String(env.SKS_WORKER_MODEL || env.SKS_AGENT_MODEL || '').trim();
|
|
311
|
-
const
|
|
313
|
+
const rawEffort = env.SKS_WORKER_REASONING || env.SKS_WORKER_MODEL_REASONING;
|
|
314
|
+
const explicitReasoning = allowlist.mode === 'openrouter_only' ? listReasoning(rawEffort) : normalizeModelReasoning(rawEffort);
|
|
312
315
|
const explicitTier = normalizeServiceTier(String(env.SKS_WORKER_SERVICE_TIER || env.SKS_SERVICE_TIER || '').trim());
|
|
313
|
-
const planned = /^
|
|
316
|
+
const planned = /^(openrouter_only|configured_list)_/.test(String(input.agent?.routed_model_policy || ''))
|
|
314
317
|
? allowlistedChildModel(input.agent?.routed_model, allowlist)
|
|
315
318
|
: null;
|
|
316
319
|
const requested = String(input.agent?.routed_model || input.agent?.model || '').trim() || null;
|
|
@@ -322,18 +325,23 @@ async function resolveOpenRouterOnlyWorkerRouting(input, deps, ctx) {
|
|
|
322
325
|
? fallbackChildModel(state, planned, 'planned')
|
|
323
326
|
: deps.consultJev === false || !task
|
|
324
327
|
? fallbackChildModel(state, requested, deps.consultJev === false ? 'jev_skipped' : 'empty_task')
|
|
325
|
-
: await chooseChildModel({ root: deps.root || process.cwd(), task, role: input.agent?.role || null, requestedModel: requested, state, env })
|
|
328
|
+
: await chooseChildModel({ root: deps.root || process.cwd(), task, role: input.agent?.role || null, requestedModel: requested, state, env, profile: childModelListProfile(allowlist) })
|
|
326
329
|
.catch(() => fallbackChildModel(state, requested, 'consult_failed'));
|
|
327
330
|
const taskPolicy = decideSubagentModel({ title: ctx.taskKindText, description: ctx.riskText, role: input.agent?.role });
|
|
328
331
|
const choiceModel = choice?.entry.model || '';
|
|
329
332
|
const routed = {
|
|
330
333
|
model: choiceModel,
|
|
331
|
-
reasoning:
|
|
334
|
+
reasoning: allowlist.mode === 'openrouter_only'
|
|
335
|
+
? explicitReasoning || choice?.entry.reasoning_effort || listReasoning(taskPolicy.modelReasoningEffort) || 'medium'
|
|
336
|
+
: explicitReasoning || (choice ? listChildModelEffort(choice.entry, null) : null),
|
|
332
337
|
serviceTier: explicitTier || input.fastModePolicy.service_tier || 'fast'
|
|
333
338
|
};
|
|
334
339
|
const blockers = [
|
|
335
|
-
...(!allowlist.entries.length ? ['openrouter_only_subagent_list_empty'] : []),
|
|
336
|
-
...(explicitModel && !explicitListed ? ['openrouter_only_subagent_model_not_listed'] : []),
|
|
340
|
+
...(!allowlist.entries.length ? [allowlist.mode === 'openrouter_only' ? 'openrouter_only_subagent_list_empty' : 'configured_subagent_list_unavailable'] : []),
|
|
341
|
+
...(explicitModel && !explicitListed ? [allowlist.mode === 'openrouter_only' ? 'openrouter_only_subagent_model_not_listed' : 'configured_subagent_model_not_listed'] : []),
|
|
342
|
+
...(allowlist.mode === 'configured' && rawEffort
|
|
343
|
+
&& (!explicitReasoning || !choice?.entry.supported_reasoning_efforts?.includes(explicitReasoning))
|
|
344
|
+
? ['configured_subagent_effort_unsupported'] : []),
|
|
337
345
|
...(!choiceModel ? ['naruto_worker_model_unavailable'] : [])
|
|
338
346
|
];
|
|
339
347
|
const why = choice ? `${choice.source}${choice.reason === 'applied' ? '' : `:${choice.reason}`}` : 'blocked';
|
|
@@ -343,7 +351,7 @@ async function resolveOpenRouterOnlyWorkerRouting(input, deps, ctx) {
|
|
|
343
351
|
explicit: Boolean(explicitModel),
|
|
344
352
|
lb_catalog: null,
|
|
345
353
|
blockers: [...new Set(blockers)],
|
|
346
|
-
reason: `${category}->${choiceModel || 'blocked'}@${routed.reasoning} (openrouter only list: ${why})`
|
|
354
|
+
reason: `${category}->${choiceModel || 'blocked'}@${routed.reasoning || 'default'} (${allowlist.mode === 'openrouter_only' ? 'openrouter only' : allowlist.profile} list: ${why})`
|
|
347
355
|
};
|
|
348
356
|
}
|
|
349
357
|
function listReasoning(value) {
|
|
@@ -2,7 +2,8 @@ import { readTopLevelTomlString } from '../../codex-app/codex-model-catalog.js';
|
|
|
2
2
|
import { codexRestartBlockers, maybeRestartRunningCodexApp } from '../../codex-app/codex-app-restart-policy.js';
|
|
3
3
|
import { readText } from '../../fsx.js';
|
|
4
4
|
import { isOpenRouterModelId } from '../../imagegen/imagegen-config.js';
|
|
5
|
-
import { MAX_SUBAGENT_MODELS, normalizeSubagentModelList, readOpenRouterOnlyStateSync, writeOpenRouterOnlyState } from '../../subagents/child-model-allowlist.js';
|
|
5
|
+
import { MAX_SUBAGENT_MODELS, normalizeSubagentModelList, readOpenRouterOnlyStateSync, readNativeSubagentModelStore, writeNativeSubagentModels, writeOpenRouterOnlyState } from '../../subagents/child-model-allowlist.js';
|
|
6
|
+
import { selectableChildModels } from '../../subagents/model-tiers.js';
|
|
6
7
|
import { assertDesktopBridgeStatusV3 } from '../bridge-runtime-validation.js';
|
|
7
8
|
import { readActiveCombinedBridgeCatalog } from '../combined-catalog.js';
|
|
8
9
|
import { MAX_SELECTED_OPENROUTER_MODELS, bridgeAvailableModelsPath, readBridgeModelSelectionState, writeBridgeModelSelection } from '../combined-catalog/model-selection.js';
|
|
@@ -19,12 +20,43 @@ async function finish(operation, ok, blockers, extra, options) {
|
|
|
19
20
|
const status = statusFromCore(core, options);
|
|
20
21
|
assertDesktopBridgeStatusV3(status);
|
|
21
22
|
const openRouterOnly = await openRouterOnlyStatusFromCore(core, options);
|
|
23
|
+
const subagentSettings = operation.startsWith('subagent-models.')
|
|
24
|
+
? await subagentModelSettings(core, openRouterOnly)
|
|
25
|
+
: null;
|
|
22
26
|
return commandResult(operation, ok, status, {
|
|
23
27
|
openrouter_only: openRouterOnly,
|
|
24
28
|
auth_priority: status.auth_priority,
|
|
25
|
-
...extra
|
|
29
|
+
...extra,
|
|
30
|
+
...(subagentSettings ? { subagent_model_settings: subagentSettings, available: subagentSettings.available } : {})
|
|
26
31
|
}, blockers, options);
|
|
27
32
|
}
|
|
33
|
+
async function subagentModelSettings(core, openRouterOnly) {
|
|
34
|
+
if (openRouterOnly.enabled) {
|
|
35
|
+
return {
|
|
36
|
+
schema: 'sks.subagent-model-settings.v1', profile: 'openrouter_only',
|
|
37
|
+
configured: true, editable: true, error: openRouterOnly.error,
|
|
38
|
+
subagent_models: openRouterOnly.subagent_models,
|
|
39
|
+
jev_enabled: openRouterOnly.jev_enabled, warnings: openRouterOnly.warnings,
|
|
40
|
+
available: (await availableOpenRouterModels(core.paths.home)).map((row) => ({ ...row, reasoning_efforts: ['low', 'medium', 'high', 'xhigh'] }))
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const profile = core.authPriorityEnabled ? 'codex_lb' : 'openai';
|
|
44
|
+
const location = openRouterOnlyStoreLocation(core.paths);
|
|
45
|
+
const saved = readNativeSubagentModelStore(location);
|
|
46
|
+
const available = selectableChildModels(profile, location);
|
|
47
|
+
const entries = saved.store.profiles[profile];
|
|
48
|
+
return {
|
|
49
|
+
schema: 'sks.subagent-model-settings.v1', profile,
|
|
50
|
+
configured: entries.length > 0, editable: saved.blockers.length === 0,
|
|
51
|
+
error: saved.blockers[0] ?? (available.length ? null : 'subagent_model_catalog_unavailable'),
|
|
52
|
+
subagent_models: entries.map((entry) => ({
|
|
53
|
+
...entry,
|
|
54
|
+
routable: available.some((row) => row.public_id === entry.model
|
|
55
|
+
&& (!entry.reasoning_effort || row.reasoning_efforts.includes(entry.reasoning_effort)))
|
|
56
|
+
})),
|
|
57
|
+
jev_enabled: openRouterOnly.jev_enabled, warnings: saved.blockers, available
|
|
58
|
+
};
|
|
59
|
+
}
|
|
28
60
|
function syncBlockers(sync) {
|
|
29
61
|
const activation = sync.activation && typeof sync.activation === 'object' && !Array.isArray(sync.activation)
|
|
30
62
|
? sync.activation
|
|
@@ -237,23 +269,55 @@ async function availableOpenRouterModels(home) {
|
|
|
237
269
|
}
|
|
238
270
|
}
|
|
239
271
|
export async function listSubagentModels(options) {
|
|
240
|
-
|
|
241
|
-
return finish('subagent-models.list', true, [], { available }, options);
|
|
272
|
+
return finish('subagent-models.list', true, [], {}, options);
|
|
242
273
|
}
|
|
243
|
-
export async function setSubagentModels(raw, noRestart, options) {
|
|
274
|
+
export async function setSubagentModels(raw, noRestart, options, expectedProfile) {
|
|
244
275
|
if (!Array.isArray(raw))
|
|
245
276
|
return finish('subagent-models.set', false, ['subagent_models_payload_invalid'], {}, options);
|
|
246
|
-
const
|
|
277
|
+
const core = await loadCore(options);
|
|
278
|
+
const activeProfile = core.openRouterOnlyEnabled ? 'openrouter_only' : core.authPriorityEnabled ? 'codex_lb' : 'openai';
|
|
279
|
+
if (expectedProfile && activeProfile !== expectedProfile) {
|
|
280
|
+
return finish('subagent-models.set', false, ['subagent_model_profile_changed'], {}, options);
|
|
281
|
+
}
|
|
282
|
+
const profile = expectedProfile ?? 'openrouter_only';
|
|
283
|
+
const { entries, issues } = normalizeSubagentModelList(raw, profile);
|
|
247
284
|
if (issues.length > 0) {
|
|
248
285
|
return finish('subagent-models.set', false, issues.map((issue) => `${issue.code}:${issue.index}`), {}, options);
|
|
249
286
|
}
|
|
250
287
|
const paths = controllerPaths(options);
|
|
251
288
|
const location = openRouterOnlyStoreLocation(paths);
|
|
252
289
|
const before = readOpenRouterOnlyStateSync(location);
|
|
253
|
-
if (!before.enabled) {
|
|
290
|
+
if (!before.enabled && profile === 'openrouter_only') {
|
|
254
291
|
await writeOpenRouterOnlyState({ subagent_models: entries }, location);
|
|
255
292
|
return finish('subagent-models.set', true, [], { catalog_sync: null }, options);
|
|
256
293
|
}
|
|
294
|
+
if (profile !== 'openrouter_only') {
|
|
295
|
+
const nativeProfile = core.authPriorityEnabled ? 'codex_lb' : 'openai';
|
|
296
|
+
const available = selectableChildModels(nativeProfile, location);
|
|
297
|
+
const blockers = entries.flatMap((entry, index) => {
|
|
298
|
+
const model = available.find((row) => row.public_id === entry.model);
|
|
299
|
+
return !model ? [`subagent_model_not_available:${index}`]
|
|
300
|
+
: entry.reasoning_effort && !model.reasoning_efforts.includes(entry.reasoning_effort)
|
|
301
|
+
? [`subagent_model_effort_unsupported:${index}`] : [];
|
|
302
|
+
});
|
|
303
|
+
const saved = readNativeSubagentModelStore(location);
|
|
304
|
+
if (saved.blockers.length || blockers.length) {
|
|
305
|
+
return finish('subagent-models.set', false, [...saved.blockers, ...blockers], {}, options);
|
|
306
|
+
}
|
|
307
|
+
const role = entries.length ? await ensureUserReadOnlyListRole(paths.home) : 'unchanged';
|
|
308
|
+
if (role === 'preserved_user_file') {
|
|
309
|
+
return finish('subagent-models.set', false, ['subagent_model_read_only_role_unavailable'], {}, options);
|
|
310
|
+
}
|
|
311
|
+
await writeNativeSubagentModels(nativeProfile, entries, location);
|
|
312
|
+
const changed = JSON.stringify(saved.store.profiles[nativeProfile]) !== JSON.stringify(entries);
|
|
313
|
+
const roleChanged = role === 'created' || role === 'updated';
|
|
314
|
+
const restart = await restartCodex(roleChanged, noRestart, options);
|
|
315
|
+
const relaunchPending = roleChanged && !restart.attempted && restart.reason !== 'codex_not_running';
|
|
316
|
+
return finish('subagent-models.set', true, codexRestartBlockers(restart), {
|
|
317
|
+
changed, catalog_sync: null, read_only_role: role, codex_restart: restart,
|
|
318
|
+
warnings: relaunchPending ? [CODEX_RELAUNCH_FOR_SUBAGENT_MODELS_WARNING] : []
|
|
319
|
+
}, options);
|
|
320
|
+
}
|
|
257
321
|
const exposedBefore = await exposedModelIds(paths);
|
|
258
322
|
const sync = await syncCatalogInternal(options, {
|
|
259
323
|
openRouterOnly: { ...before, subagent_models: entries },
|
|
@@ -73,7 +73,7 @@ export async function executeDesktopBridgeCommandV3(request, options = {}) {
|
|
|
73
73
|
return setOpenRouterOnly(request.enabled, request.no_restart === true, options);
|
|
74
74
|
}
|
|
75
75
|
if (request.operation === 'subagent-models.set') {
|
|
76
|
-
return setSubagentModels(request.subagent_models, request.no_restart === true, options);
|
|
76
|
+
return setSubagentModels(request.subagent_models, request.no_restart === true, options, request.profile);
|
|
77
77
|
}
|
|
78
78
|
if (request.operation === 'route.official-models')
|
|
79
79
|
return setOfficialModelsMode(request.mode, options);
|
|
@@ -72,7 +72,7 @@ export async function chooseChildModels(input) {
|
|
|
72
72
|
workflowId: input.workflowId,
|
|
73
73
|
goal: input.goal,
|
|
74
74
|
questions,
|
|
75
|
-
facts: { openrouter_only:
|
|
75
|
+
facts: { openrouter_only: (input.profile ?? 'openrouter_only') === 'openrouter_only', subagent_model_profile: input.profile ?? 'openrouter_only', subagent_models: entries.map((entry) => canonicalChildModelId(entry.model)) },
|
|
76
76
|
...(input.env ? { env: input.env } : {}),
|
|
77
77
|
...(input.deadlineMs === undefined ? {} : { deadlineMs: input.deadlineMs })
|
|
78
78
|
})
|
|
@@ -100,6 +100,7 @@ export async function chooseChildModel(input) {
|
|
|
100
100
|
goal: input.task || 'Spawn a child agent.',
|
|
101
101
|
lanes: [{ id: 'spawn', task: input.task, role: input.role ?? null, requestedModel: input.requestedModel ?? null }],
|
|
102
102
|
state: input.state,
|
|
103
|
+
...(input.profile ? { profile: input.profile } : {}),
|
|
103
104
|
...(input.env ? { env: input.env } : {}),
|
|
104
105
|
...(input.deadlineMs === undefined ? {} : { deadlineMs: input.deadlineMs })
|
|
105
106
|
});
|
|
@@ -145,9 +145,10 @@ export async function statusReport(env) {
|
|
|
145
145
|
: 'ready';
|
|
146
146
|
const childModels = effectiveChildModelAllowlist({ env });
|
|
147
147
|
const openRouterOnly = childModels.mode === 'openrouter_only';
|
|
148
|
+
const listMode = childModels.mode !== 'tiers';
|
|
148
149
|
const childTierPoints = new Set(['spawn_tier', 'worker_tier', 'role_tiers', 'role_omission']);
|
|
149
150
|
const decisionPoints = ['turn_route', 'turn_tier', 'spawn_tier', 'worker_tier', 'role_tiers', 'role_omission', 'plan', 'context', 'parent_edit_delegation', 'image_need', 'image_parameters', 'qa_effort_escalation']
|
|
150
|
-
.filter((point) => !
|
|
151
|
+
.filter((point) => !listMode || !childTierPoints.has(point));
|
|
151
152
|
return {
|
|
152
153
|
schema: 'sks.jev-decision-status.v1',
|
|
153
154
|
ok: true,
|
|
@@ -167,8 +168,14 @@ export async function statusReport(env) {
|
|
|
167
168
|
recovery: config.capabilities.recovery
|
|
168
169
|
},
|
|
169
170
|
decision_points: jevEnabled(config)
|
|
170
|
-
? [...decisionPoints, ...(
|
|
171
|
+
? [...decisionPoints, ...(listMode ? ['child_model'] : [])]
|
|
171
172
|
: [],
|
|
173
|
+
subagent_model_list: {
|
|
174
|
+
active: listMode,
|
|
175
|
+
profile: childModels.mode === 'configured' ? childModels.profile : openRouterOnly ? 'openrouter_only' : null,
|
|
176
|
+
models: listMode ? childModels.models : [],
|
|
177
|
+
default_model: childModels.default_model
|
|
178
|
+
},
|
|
172
179
|
openrouter_only: {
|
|
173
180
|
enabled: openRouterOnly,
|
|
174
181
|
subagent_model_count: openRouterOnly ? childModels.models.length : 0,
|
|
@@ -9,7 +9,7 @@ import { renderAuthoritativeSksSkillContext } from '../codex-native/sks-skill-pa
|
|
|
9
9
|
import { resolveManagedSkillSourcesForAdmission } from './managed-skill-admission.js';
|
|
10
10
|
import { managedSkillDigestBlocksEnforced } from '../verification-profile.js';
|
|
11
11
|
import { looksLikeActiveContinuationPrompt } from './naruto-decision-gate.js';
|
|
12
|
-
import { effectiveChildModelAllowlist } from '../subagents/child-model-allowlist.js';
|
|
12
|
+
import { effectiveChildModelAllowlist, childModelListLabel } from '../subagents/child-model-allowlist.js';
|
|
13
13
|
import { EXCLUSIVE_SURFACE_SPAWN_LINE } from '../subagents/exclusive-surface-rule.js';
|
|
14
14
|
import { renderChildModelCriteria, SUBAGENT_MODELS_SETTINGS_HINT } from './subagent-spawn-policy.js';
|
|
15
15
|
import { extractUserPrompt, looksLikeCodexGitAction, looksLikeCodexUiSettingsEvent } from './payload-signals.js';
|
|
@@ -31,12 +31,12 @@ const OFFICIAL_SUBAGENT_SPAWN_COMPATIBILITY_CONTEXT = [
|
|
|
31
31
|
].join('\n');
|
|
32
32
|
function openRouterOnlyChildModelLine(allowlist) {
|
|
33
33
|
if (!allowlist.models.length) {
|
|
34
|
-
return `- SKS
|
|
34
|
+
return `- SKS ${childModelListLabel(allowlist)} mode is on and the subagent model list is empty: every spawn_agent call is denied until the user adds models in ${SUBAGENT_MODELS_SETTINGS_HINT}.`;
|
|
35
35
|
}
|
|
36
|
-
return `- SKS
|
|
36
|
+
return `- SKS ${childModelListLabel(allowlist)} mode is on: every child runs a model from the user's subagent list, and the SKS PreToolUse hook denies any other model. The list, with the user's rule for each model: ${renderChildModelCriteria(allowlist)}. The hook routes each spawn_agent call to a list model (Jev picks by these rules when Jev mode is on; otherwise a listed requested model, else the default), so pass \`fork_turns="none"\` or a positive bounded turn count and do not tune model or effort yourself.`;
|
|
37
37
|
}
|
|
38
38
|
export function officialSubagentSpawnCompatibilityContext(allowlist = effectiveChildModelAllowlist()) {
|
|
39
|
-
if (allowlist.mode
|
|
39
|
+
if (allowlist.mode === 'tiers')
|
|
40
40
|
return OFFICIAL_SUBAGENT_SPAWN_COMPATIBILITY_CONTEXT;
|
|
41
41
|
return [...OFFICIAL_SUBAGENT_SPAWN_CONTRACT_HEAD, openRouterOnlyChildModelLine(allowlist)].join('\n');
|
|
42
42
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { consultJevTurnModel } from '../decisions/integration.js';
|
|
2
2
|
import { chooseChildModel, fallbackChildModel } from '../decisions/child-model-choice.js';
|
|
3
3
|
import { managedOfficialSubagentRoleByName } from '../managed-assets/managed-assets-manifest.js';
|
|
4
|
-
import {
|
|
4
|
+
import { effectiveChildModelAllowlist, childModelListLabel, childModelListProfile, listChildModelEffort } from '../subagents/child-model-allowlist.js';
|
|
5
5
|
import { subagentModelProfile } from '../subagents/model-policy.js';
|
|
6
6
|
import { effortForTier, latestModelForTier, latestTierModelSet } from '../subagents/model-tiers.js';
|
|
7
7
|
import { readRoleModelPreferences } from '../subagents/role-model-preferences.js';
|
|
@@ -52,12 +52,12 @@ export async function jevSpawnRouting(root, state, payload) {
|
|
|
52
52
|
if (!input)
|
|
53
53
|
return { input: null, route: null };
|
|
54
54
|
const allowlist = effectiveChildModelAllowlist();
|
|
55
|
-
if (allowlist.mode
|
|
55
|
+
if (allowlist.mode !== 'tiers')
|
|
56
56
|
return openRouterOnlySpawnRouting(root, state, input, allowlist);
|
|
57
57
|
return { input: await tierSpawnRewrite(root, state, input), route: null };
|
|
58
58
|
}
|
|
59
59
|
function listState(allowlist) {
|
|
60
|
-
return {
|
|
60
|
+
return { subagent_models: allowlist.entries };
|
|
61
61
|
}
|
|
62
62
|
async function openRouterOnlySpawnRouting(root, parentState, input, allowlist) {
|
|
63
63
|
if (fullHistoryForkContext(input))
|
|
@@ -70,13 +70,14 @@ async function openRouterOnlySpawnRouting(root, parentState, input, allowlist) {
|
|
|
70
70
|
const agent = String(input.agent_type || input.agentType || '').trim() || null;
|
|
71
71
|
const task = spawnTask(input, { items: true });
|
|
72
72
|
const choice = task
|
|
73
|
-
? await chooseChildModel({ root, task, role: agent, requestedModel: requested, state })
|
|
73
|
+
? await chooseChildModel({ root, task, role: agent, requestedModel: requested, state, profile: childModelListProfile(allowlist) })
|
|
74
74
|
.catch(() => fallbackChildModel(state, requested, 'consult_failed'))
|
|
75
75
|
: fallbackChildModel(state, requested, 'empty_task');
|
|
76
76
|
if (!choice)
|
|
77
77
|
return { input: null, route: null };
|
|
78
78
|
const route = {
|
|
79
|
-
mode:
|
|
79
|
+
mode: allowlist.mode,
|
|
80
|
+
...(allowlist.mode === 'configured' ? { profile: allowlist.profile } : {}),
|
|
80
81
|
model: choice.entry.model,
|
|
81
82
|
source: choice.source,
|
|
82
83
|
reason: choice.reason
|
|
@@ -90,8 +91,7 @@ async function openRouterOnlySpawnRouting(root, parentState, input, allowlist) {
|
|
|
90
91
|
else
|
|
91
92
|
delete next.agent_type;
|
|
92
93
|
}
|
|
93
|
-
const effort = choice.entry.reasoning_effort
|
|
94
|
-
|| (isSubagentModelEffort(input.reasoning_effort) ? input.reasoning_effort : null);
|
|
94
|
+
const effort = listChildModelEffort(choice.entry, input.reasoning_effort);
|
|
95
95
|
if (effort)
|
|
96
96
|
next.reasoning_effort = effort;
|
|
97
97
|
else
|
|
@@ -149,5 +149,5 @@ export function openRouterOnlyJevTurnLine(allowlist, effort, orchestrationRequir
|
|
|
149
149
|
const list = allowlist.entries.length
|
|
150
150
|
? `every spawn_agent child runs a model from the user's subagent list (${renderChildModelList(allowlist)}); Jev picks the list model for each spawn from the user's criteria, and any other model is denied.`
|
|
151
151
|
: `the subagent model list is empty, so spawn_agent is denied until the user adds models in ${SUBAGENT_MODELS_SETTINGS_HINT}.`;
|
|
152
|
-
return `${hint}SKS
|
|
152
|
+
return `${hint}SKS ${childModelListLabel(allowlist)} mode: ${list}`;
|
|
153
153
|
}
|
|
@@ -8,7 +8,7 @@ export async function healStaleRolePinForSpawn(payload, root) {
|
|
|
8
8
|
const agent = String(input.agent_type || input.agentType || '').trim();
|
|
9
9
|
if (!agent || !stalePinForAgentType(agent, { root }))
|
|
10
10
|
return false;
|
|
11
|
-
if (effectiveChildModelAllowlist().mode
|
|
11
|
+
if (effectiveChildModelAllowlist().mode !== 'tiers')
|
|
12
12
|
return false;
|
|
13
13
|
await refreshStaleManagedRolePins({ root });
|
|
14
14
|
return true;
|
|
@@ -2,7 +2,7 @@ import path from 'node:path';
|
|
|
2
2
|
import { readJson } from '../fsx.js';
|
|
3
3
|
import { managedOfficialSubagentRoleByName } from '../managed-assets/managed-assets-manifest.js';
|
|
4
4
|
import { latestModelForTier } from '../subagents/model-tiers.js';
|
|
5
|
-
import { effectiveChildModelAllowlist } from '../subagents/child-model-allowlist.js';
|
|
5
|
+
import { effectiveChildModelAllowlist, childModelListLabel } from '../subagents/child-model-allowlist.js';
|
|
6
6
|
import { jevEnabled, readDecisionConfig } from '../decisions/config.js';
|
|
7
7
|
import { renderChildModelList } from './subagent-spawn-policy.js';
|
|
8
8
|
export async function sealedSubagentRoutingContext(artifactDir, payload = {}) {
|
|
@@ -16,11 +16,11 @@ export async function sealedSubagentRoutingContext(artifactDir, payload = {}) {
|
|
|
16
16
|
if (!agentName)
|
|
17
17
|
return '';
|
|
18
18
|
const allowlist = effectiveChildModelAllowlist();
|
|
19
|
-
if (allowlist.mode
|
|
19
|
+
if (allowlist.mode !== 'tiers') {
|
|
20
20
|
return [
|
|
21
21
|
'SKS sealed child routing:',
|
|
22
22
|
`- custom agent: ${agentName}`,
|
|
23
|
-
`- model and model_reasoning_effort: sealed on the spawn call from the
|
|
23
|
+
`- model and model_reasoning_effort: sealed on the spawn call from the ${childModelListLabel(allowlist)} subagent list (${renderChildModelList(allowlist) || 'empty'})`,
|
|
24
24
|
'- keep this sealed profile; do not retarget model/effort or spawn nested agents'
|
|
25
25
|
].join('\n');
|
|
26
26
|
}
|
|
@@ -1,29 +1,30 @@
|
|
|
1
1
|
import { managedOfficialSubagentRoleByName } from '../managed-assets/managed-assets-manifest.js';
|
|
2
|
-
import { READ_ONLY_LIST_ROLE } from '../subagents/read-only-list-role.js';
|
|
2
|
+
import { READ_ONLY_LIST_ROLE, readOnlyListRoleInstalled } from '../subagents/read-only-list-role.js';
|
|
3
3
|
import { stalePinBlockReason, stalePinForAgentType } from '../subagents/role-model-pins.js';
|
|
4
4
|
import { isSpawnAgentToolName, spawnPayloadToolName } from './spawn-tool-name.js';
|
|
5
|
-
import { effectiveChildModelAllowlist, isAllowedChildModel } from '../subagents/child-model-allowlist.js';
|
|
5
|
+
import { effectiveChildModelAllowlist, childModelListLabel, isAllowedChildModel } from '../subagents/child-model-allowlist.js';
|
|
6
6
|
export const SUBAGENT_MODELS_SETTINGS_HINT = 'SKS Control Center > Subagent Models';
|
|
7
7
|
export function renderChildModelList(allowlist) {
|
|
8
|
-
if (allowlist.mode
|
|
8
|
+
if (allowlist.mode === 'tiers')
|
|
9
9
|
return allowlist.models.join(', ');
|
|
10
10
|
return allowlist.entries.map((entry) => `${entry.model}${entry.default ? ' (default)' : ''}`).join(', ');
|
|
11
11
|
}
|
|
12
12
|
export function renderChildModelCriteria(allowlist) {
|
|
13
|
-
if (allowlist.mode
|
|
13
|
+
if (allowlist.mode === 'tiers')
|
|
14
14
|
return allowlist.models.join(', ');
|
|
15
15
|
return allowlist.entries
|
|
16
16
|
.map((entry) => `${entry.model}${entry.default ? ' (default)' : ''}: ${entry.criteria || 'general work'}`)
|
|
17
17
|
.join('; ');
|
|
18
18
|
}
|
|
19
19
|
function openRouterOnlyBlockReason(allowlist) {
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const label = childModelListLabel(allowlist);
|
|
21
|
+
if (!allowlist.models.length && allowlist.unroutable?.length) {
|
|
22
|
+
return `SKS ${label} mode: no model on the subagent list is available with its saved effort right now (${allowlist.unroutable.join(', ')}), so no child may be spawned. Ask the user to replace them in ${SUBAGENT_MODELS_SETTINGS_HINT}, then retry spawn_agent. Do not implement the slice in the parent instead.`;
|
|
22
23
|
}
|
|
23
24
|
if (!allowlist.models.length) {
|
|
24
|
-
return `SKS
|
|
25
|
+
return `SKS ${label} mode: the subagent model list is empty or unavailable, so no child may be spawned. Ask the user to review the list in ${SUBAGENT_MODELS_SETTINGS_HINT}, then retry spawn_agent. Do not implement the slice in the parent instead.`;
|
|
25
26
|
}
|
|
26
|
-
return `SKS
|
|
27
|
+
return `SKS ${label} mode: children may run only a model on the user's subagent list: ${renderChildModelList(allowlist)}. Every other model is denied. Retry spawn_agent with one of them (the SKS hook routes each spawn to a list model, by Jev when Jev mode is on) and fork_turns="none" or a positive bounded turn count. Include the complete slice contract in message; do not inherit the parent model. The user adds models in ${SUBAGENT_MODELS_SETTINGS_HINT}.`;
|
|
27
28
|
}
|
|
28
29
|
export function fullHistoryForkContext(input) {
|
|
29
30
|
return input.fork_context === true || String(input.fork_context ?? '').trim().toLowerCase() === 'true';
|
|
@@ -37,16 +38,22 @@ export function subagentSpawnPolicyBlockReason(payload = {}, opts = {}) {
|
|
|
37
38
|
return null;
|
|
38
39
|
const input = payload.tool_input || payload.toolInput || payload.tool?.input || {};
|
|
39
40
|
const allowlist = effectiveChildModelAllowlist();
|
|
40
|
-
if (allowlist.mode
|
|
41
|
+
if (allowlist.mode === 'tiers' && opts.narutoParent === false)
|
|
41
42
|
return null;
|
|
42
|
-
if (allowlist.mode
|
|
43
|
+
if (allowlist.mode !== 'tiers') {
|
|
43
44
|
if (!isAllowedChildModel(input.model, allowlist))
|
|
44
45
|
return openRouterOnlyBlockReason(allowlist);
|
|
45
46
|
if (fullHistoryForkContext(input))
|
|
46
47
|
return `${FORK_BLOCK_REASON} Omit fork_context (fork_context=true is a full-history fork).`;
|
|
47
48
|
const agent = String(input.agent_type || input.agentType || '').trim();
|
|
49
|
+
if (allowlist.mode === 'configured' && agent && agent !== READ_ONLY_LIST_ROLE.codex_name) {
|
|
50
|
+
return `SKS ${childModelListLabel(allowlist)} subagent list: agent_type "${agent}" may pin its own model or effort, which Codex uses instead of the spawn values. Omit agent_type and include the role brief in message; read-only slices must use agent_type="${READ_ONLY_LIST_ROLE.codex_name}".`;
|
|
51
|
+
}
|
|
52
|
+
if (allowlist.mode === 'configured' && agent === READ_ONLY_LIST_ROLE.codex_name && opts.root && !readOnlyListRoleInstalled(opts.root)) {
|
|
53
|
+
return `SKS could not verify the model-less read-only role for this project. A missing role or custom role/config override must be resolved before retrying ${READ_ONLY_LIST_ROLE.codex_name}; do not remove the role to bypass its read-only sandbox.`;
|
|
54
|
+
}
|
|
48
55
|
if (agent && managedOfficialSubagentRoleByName(agent)) {
|
|
49
|
-
return `SKS
|
|
56
|
+
return `SKS ${childModelListLabel(allowlist)} mode: agent_type "${agent}" pins a tier model, so it cannot run a list model. Omit agent_type and put the role brief in message; read-only slices pass agent_type="${READ_ONLY_LIST_ROLE.codex_name}".`;
|
|
50
57
|
}
|
|
51
58
|
}
|
|
52
59
|
else {
|
|
@@ -310,7 +310,7 @@ async function attachJevTurnRouting(root, payload, result, orchestrationRequired
|
|
|
310
310
|
: `Jev rated this turn as ${decision.effort}-effort work (${decision.model} tier). Treat it as this turn's reasoning hint; the parent model, effort, and service tier stay as the user set them.`
|
|
311
311
|
: '';
|
|
312
312
|
const allowlist = effectiveChildModelAllowlist();
|
|
313
|
-
const line = allowlist.mode
|
|
313
|
+
const line = allowlist.mode !== 'tiers'
|
|
314
314
|
? openRouterOnlyJevTurnLine(allowlist, decision.effort, orchestrationRequired)
|
|
315
315
|
: tierLine;
|
|
316
316
|
if (!line)
|