sneakoscope 10.3.2 → 10.3.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 +22 -12
- package/config/skills-hash-ledger.v1.json +7 -1
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/commands/doctor.js +2 -2
- package/dist/config/skills-manifest.json +13 -7
- package/dist/core/agents/agent-effort-policy.js +15 -13
- package/dist/core/agents/native-worker-backend-router.js +13 -8
- package/dist/core/codex-hooks/codex-hook-managed-install.js +56 -2
- package/dist/core/codex-lb/desktop-bridge-migration/retired-runtime-cleanup.js +1 -0
- package/dist/core/codex-native/core-skill-manifest.js +4 -4
- package/dist/core/decisions/cli.js +9 -2
- package/dist/core/decisions/config.js +7 -0
- package/dist/core/decisions/integration.js +54 -12
- package/dist/core/decisions/policy.js +27 -4
- package/dist/core/decisions/questions.js +39 -9
- package/dist/core/decisions/routing.js +15 -10
- package/dist/core/decisions/types.js +11 -25
- package/dist/core/doctor/current-project-guidance.js +38 -1
- package/dist/core/doctor/skill-legacy-surface.js +2 -1
- package/dist/core/hooks-runtime/hook-context.js +1 -1
- package/dist/core/hooks-runtime/jev-spawn-routing.js +21 -2
- package/dist/core/hooks-runtime/managed-guidance-preflight.js +30 -0
- package/dist/core/hooks-runtime/official-subagent-lifecycle.js +3 -3
- package/dist/core/hooks-runtime/parent-orchestration-gate.js +372 -0
- package/dist/core/hooks-runtime/subagent-context.js +13 -3
- package/dist/core/hooks-runtime/subagent-spawn-policy.js +5 -10
- package/dist/core/hooks-runtime.js +46 -8
- package/dist/core/init/skills/inventory.js +5 -1
- package/dist/core/init/skills.js +4 -4
- package/dist/core/init.js +39 -7
- package/dist/core/managed-assets/managed-assets-manifest.js +17 -17
- package/dist/core/pipeline-internals/runtime-core.js +2 -2
- package/dist/core/provider/model-router.js +8 -5
- package/dist/core/recallpulse/policy.js +2 -2
- package/dist/core/recallpulse.js +3 -3
- package/dist/core/release/gate-affected-globs.js +0 -1
- package/dist/core/research/mock-result.js +2 -2
- package/dist/core/research/research-adversarial-review.js +7 -7
- package/dist/core/research/research-claim-synthesizer.js +3 -3
- package/dist/core/research/research-falsification-runner.js +3 -3
- package/dist/core/research/research-super-search.js +5 -2
- package/dist/core/research/research-synthesis-writer.js +2 -2
- package/dist/core/research.js +5 -5
- package/dist/core/routes/dollar-manifest-lite.js +1 -1
- package/dist/core/routes.js +10 -10
- package/dist/core/runtime/task-profile.js +9 -5
- package/dist/core/subagents/model-policy.js +27 -45
- package/dist/core/subagents/model-tiers.js +153 -0
- package/dist/core/subagents/naruto-command-args.js +3 -3
- package/dist/core/subagents/naruto-help-contract.js +18 -13
- package/dist/core/subagents/naruto-host-credentials.js +8 -13
- package/dist/core/subagents/official-subagent-config.js +11 -7
- package/dist/core/subagents/official-subagent-preparation.js +12 -8
- package/dist/core/subagents/official-subagent-prompt.js +33 -33
- package/dist/core/subagents/official-subagent-runner.js +3 -3
- package/dist/core/subagents/role-model-preferences.js +26 -21
- package/dist/core/update/managed-permission-repair.js +462 -0
- package/dist/core/update/update-migration-state/retired-local-decision.js +32 -2
- package/dist/core/update/update-migration-state/simple-stages.js +42 -5
- package/dist/core/update/update-migration-state.js +63 -1
- package/dist/core/update-check.js +35 -8
- package/dist/core/version.js +1 -1
- package/dist/scripts/codex-native-agent-role-content-check.js +10 -5
- package/dist/scripts/codex-native-gate-lib.js +3 -1
- package/dist/scripts/current-surface-update-e2e-check.js +1 -0
- package/dist/scripts/mutation-callsite-coverage-check.js +2 -2
- package/dist/scripts/release-affected-selector-check.js +1 -2
- package/dist/scripts/typed-routing-gate-lib.js +3 -1
- package/package.json +1 -1
- package/release-gates.v2.json +2 -6
|
@@ -6,7 +6,8 @@ import { attentionRiskForTask, classifyTaskProfile } from '../runtime/task-profi
|
|
|
6
6
|
import { chooseVerificationBudget } from '../runtime/verification-budget.js';
|
|
7
7
|
import { buildOfficialSubagentPrompt, validateOfficialSubagentSlices } from './official-subagent-prompt.js';
|
|
8
8
|
import { readOfficialSubagentConfig } from './official-subagent-config.js';
|
|
9
|
-
import {
|
|
9
|
+
import { NARUTO_PARENT_EFFORT, narutoParentModel } from './model-policy.js';
|
|
10
|
+
import { latestModelForTier } from './model-tiers.js';
|
|
10
11
|
import { MAX_ON_DEMAND_SUBAGENT_ROLE_COUNT, officialSubagentFanoutPolicy, officialSubagentOnDemandRolePlan, officialSubagentRoleCatalog, recommendOfficialSubagentRoles } from './agent-catalog.js';
|
|
11
12
|
import { resolveSubagentThreadBudget } from './thread-budget.js';
|
|
12
13
|
import { decideNarutoConcurrency } from '../naruto/naruto-concurrency-governor.js';
|
|
@@ -270,7 +271,7 @@ async function deriveOfficialSubagentPreparation(input, snapshot) {
|
|
|
270
271
|
return [name, {
|
|
271
272
|
...config,
|
|
272
273
|
routed_provider: 'openai',
|
|
273
|
-
routed_model: preference?.model ||
|
|
274
|
+
routed_model: preference?.model || config.model || decision.model,
|
|
274
275
|
routed_model_reasoning_effort: routedReasoning,
|
|
275
276
|
routed_model_policy: preference
|
|
276
277
|
? 'user_role_model_preference'
|
|
@@ -341,11 +342,11 @@ async function deriveOfficialSubagentPreparation(input, snapshot) {
|
|
|
341
342
|
},
|
|
342
343
|
slice_safety: sliceSafety,
|
|
343
344
|
slices,
|
|
344
|
-
parent_model_policy:
|
|
345
|
+
parent_model_policy: narutoParentModel(),
|
|
345
346
|
observed_parent_model: observedParentModel,
|
|
346
347
|
parent_model_match: parentModelMatch,
|
|
347
348
|
parent: {
|
|
348
|
-
model:
|
|
349
|
+
model: narutoParentModel(),
|
|
349
350
|
model_reasoning_effort: NARUTO_PARENT_EFFORT
|
|
350
351
|
},
|
|
351
352
|
agent_catalog: agentCatalog,
|
|
@@ -472,7 +473,7 @@ function applyOfficialSubagentDecision(derived, decided) {
|
|
|
472
473
|
.filter(([name]) => !derived.roleModelPreferences.store.roles[name])
|
|
473
474
|
.map(([name, effort]) => [name, {
|
|
474
475
|
provider: 'openai',
|
|
475
|
-
model: selectedRouting.models[name] || '
|
|
476
|
+
model: selectedRouting.models[name] || latestModelForTier('deep'),
|
|
476
477
|
reasoning_effort: effort,
|
|
477
478
|
updated_at: derived.plan.workflow_run_id
|
|
478
479
|
}]))
|
|
@@ -496,6 +497,8 @@ function applyOfficialSubagentDecision(derived, decided) {
|
|
|
496
497
|
},
|
|
497
498
|
routedAgents: agents,
|
|
498
499
|
narutoChildRouting: derived.mode === 'naruto',
|
|
500
|
+
jevRouting: decided.compiled.kind === 'apply'
|
|
501
|
+
|| !['off', 'missing_key'].includes(String(decided.compiled.reason || '')),
|
|
499
502
|
activeMainModel: derived.activeMainModel,
|
|
500
503
|
parentOutputMode: derived.mode === 'naruto' && derived.sessionScope ? 'app_naruto_stdin' : 'raw_json',
|
|
501
504
|
missionId: derived.plan.mission_id,
|
|
@@ -503,6 +506,7 @@ function applyOfficialSubagentDecision(derived, decided) {
|
|
|
503
506
|
decisionContract: decided.compiled.kind === 'apply'
|
|
504
507
|
? {
|
|
505
508
|
planId: selectedPlan?.id ?? null,
|
|
509
|
+
workerCount: selectedPlan?.workerCount ?? null,
|
|
506
510
|
keepContextIds: decided.selectedContextIds,
|
|
507
511
|
routingLane: selectedRouting
|
|
508
512
|
? Object.entries(selectedRouting.models).map(([name, model]) => `${name}=${model}`).join(', ')
|
|
@@ -914,7 +918,7 @@ export function buildNarutoSummary(input) {
|
|
|
914
918
|
workflow_run_id: input.workflowRunId || input.evidence?.run_id || null,
|
|
915
919
|
mission_id: input.missionId,
|
|
916
920
|
parent: {
|
|
917
|
-
model:
|
|
921
|
+
model: narutoParentModel(),
|
|
918
922
|
model_reasoning_effort: NARUTO_PARENT_EFFORT,
|
|
919
923
|
observed_model: input.observedParentModel || null,
|
|
920
924
|
observed_model_match: input.parentModelMatch ?? null
|
|
@@ -994,7 +998,7 @@ export function buildNarutoGateResult(input) {
|
|
|
994
998
|
workflow: 'official_codex_subagent',
|
|
995
999
|
workflow_run_id: input.workflowRunId || input.evidence?.run_id || null,
|
|
996
1000
|
mission_id: input.missionId,
|
|
997
|
-
parent_model_policy:
|
|
1001
|
+
parent_model_policy: narutoParentModel(),
|
|
998
1002
|
observed_parent_model: input.observedParentModel || null,
|
|
999
1003
|
parent_model_match: input.parentModelMatch ?? null,
|
|
1000
1004
|
status: passed ? 'passed' : 'blocked',
|
|
@@ -1039,7 +1043,7 @@ export function buildNarutoGateResult(input) {
|
|
|
1039
1043
|
};
|
|
1040
1044
|
}
|
|
1041
1045
|
function observedParentModelMatchesPolicy(model) {
|
|
1042
|
-
return model.trim().toLowerCase() ===
|
|
1046
|
+
return model.trim().toLowerCase() === narutoParentModel();
|
|
1043
1047
|
}
|
|
1044
1048
|
export const NARUTO_MISSION_RUN_LOCK = '.naruto-run.lock';
|
|
1045
1049
|
export const NARUTO_MISSION_RUN_STALE_MS = 120_000;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { latestTierModelSet } from './model-tiers.js';
|
|
2
2
|
import { HARD_NARUTO_MAX_THREADS } from './thread-budget.js';
|
|
3
3
|
import { coreEngineeringDirectiveReferenceText } from '../lean-engineering-policy.js';
|
|
4
4
|
import { MAX_AUTOMATIC_REVIEWER_COUNT, MAX_AUTOMATIC_SUBAGENT_COUNT, MAX_CRITICAL_AUTOMATIC_REVIEWER_COUNT, officialSubagentOnDemandRoleCatalog, officialSubagentRoleCatalog, selectOfficialSubagentRole } from './agent-catalog.js';
|
|
@@ -44,30 +44,30 @@ export function buildOfficialSubagentPrompt(input) {
|
|
|
44
44
|
...(input.recommendedAgents || [])
|
|
45
45
|
]);
|
|
46
46
|
const activeMainModel = normalizedActiveMainModel(input.activeMainModel);
|
|
47
|
-
const
|
|
48
|
-
|
|
47
|
+
const currentModels = latestTierModelSet();
|
|
48
|
+
const rolePreferences = Object.fromEntries(Object.entries(input.roleModelPreferences || {})
|
|
49
|
+
.filter(([name, row]) => currentModels.has(row.model)
|
|
49
50
|
&& ['low', 'medium', 'high', 'xhigh', 'max', 'ultra'].includes(row.reasoning_effort)
|
|
50
|
-
&& officialSubagentOnDemandRoleCatalog([name]).some((role) => role.name === name))
|
|
51
|
-
|
|
51
|
+
&& officialSubagentOnDemandRoleCatalog([name]).some((role) => role.name === name)));
|
|
52
|
+
const effortPreferences = Object.fromEntries(Object.entries(rolePreferences).map(([name, row]) => [name, row.reasoning_effort]));
|
|
52
53
|
const narutoChildren = input.narutoChildRouting === true;
|
|
53
|
-
const
|
|
54
|
+
const jevRouting = input.jevRouting === true;
|
|
55
|
+
const spawnModelRouting = renderSpawnModelRouting(narutoChildren, jevRouting);
|
|
56
|
+
const tierRules = jevRouting
|
|
57
|
+
? []
|
|
58
|
+
: [
|
|
59
|
+
'- tiers: fast for tiny mechanical shards (search, rename, copy, label, one-line edits), balanced for instructed UI, logic, backend, and native implementation, context for long reads, exploration, large first drafts, Computer Use, browser, or image work, deep for planning, review, debugging, architecture, security, database, research, release, or other judgment',
|
|
60
|
+
'- explicit task class and phase win over incidental keywords; a mixed slice takes the deeper tier; never apply the parent profile to every child'
|
|
61
|
+
];
|
|
54
62
|
const childModelRules = narutoChildren
|
|
55
63
|
? [
|
|
56
64
|
'- the parent orchestrates only: decompose the goal, assign disjoint slices, spawn children, and integrate their results',
|
|
57
65
|
'- do not implement the assigned slice work in the parent thread',
|
|
58
|
-
'- use the model and reasoning_effort named in each slice spawn contract',
|
|
59
|
-
|
|
66
|
+
'- use the model and reasoning_effort named in each slice spawn contract; each is the newest model of its tier',
|
|
67
|
+
...tierRules,
|
|
60
68
|
'- keep a stored user role-model preference for that role'
|
|
61
69
|
].join('\n')
|
|
62
|
-
:
|
|
63
|
-
'- use `worker` with gpt-6-astra and low reasoning for tiny short-context mechanical work such as simple search, typing, rename, copy, label, or one-line edits with no exploration or judgment',
|
|
64
|
-
'- use gpt-6-astra with low reasoning for ordinary UI, logic, backend, and native implementation with established instructions',
|
|
65
|
-
'- use gpt-6-astra with max reasoning for planning, analysis, review, focused unresolved, high-risk, architecture, security, database, research, release, or other explicit judgment slices',
|
|
66
|
-
'- use gpt-6-astra with medium reasoning for long context/memory, large docs/repository reads or exploration, large-scale first-draft code processing, and direct Computer Use, Browser/Chrome, or image generation',
|
|
67
|
-
'- explicit task class and phase win over incidental keywords: Astra Medium explores, Astra Low executes mechanical and instructed coding tasks, and Astra Max judges',
|
|
68
|
-
'- in mass fan-out, use worker/Astra Low for tiny mechanical shards and explorer/Astra Medium for broad exploration; use Astra Low for instructed implementation and Astra Max for judgment',
|
|
69
|
-
'- keep context, exploration, review, debugging, planning, and direct tool operation on their assigned defaults; preserve each sealed role model and effort instead of applying the parent profile to every child'
|
|
70
|
-
].join('\n');
|
|
70
|
+
: tierRules.join('\n');
|
|
71
71
|
const parentOutputMode = input.parentOutputMode === 'app_naruto_stdin'
|
|
72
72
|
? 'app_naruto_stdin'
|
|
73
73
|
: 'raw_json';
|
|
@@ -80,10 +80,10 @@ export function buildOfficialSubagentPrompt(input) {
|
|
|
80
80
|
|| effortPreferences[agentName]
|
|
81
81
|
|| role?.model_reasoning_effort
|
|
82
82
|
|| 'medium';
|
|
83
|
-
const sealedModel = routed?.routed_model || role?.model;
|
|
83
|
+
const sealedModel = routed?.routed_model || rolePreferences[agentName]?.model || role?.model;
|
|
84
84
|
const spawnContract = role
|
|
85
85
|
? `pass model=${JSON.stringify(sealedModel)} and reasoning_effort=${JSON.stringify(sealedReasoning)} from the sealed role policy`
|
|
86
|
-
: 'stop before spawning: resolve an installed sealed
|
|
86
|
+
: 'stop before spawning: resolve an installed sealed role and its tier first';
|
|
87
87
|
return [
|
|
88
88
|
`${index + 1}. [${slice.id}] use custom agent \`${agentName}\``,
|
|
89
89
|
` ${slice.title}: ${slice.description}`,
|
|
@@ -114,15 +114,15 @@ Host capability policy:
|
|
|
114
114
|
- Slack delivery is ACAS-runtime-only, never a model tool
|
|
115
115
|
|
|
116
116
|
Subagent rules:
|
|
117
|
-
- parent model policy: ${activeMainModel ? `keep the current app-selected main model ${activeMainModel.provider}:${activeMainModel.model}` :
|
|
117
|
+
- parent model policy: ${activeMainModel ? `keep the current app-selected main model ${activeMainModel.provider}:${activeMainModel.model}` : 'keep the user-selected parent model'}
|
|
118
118
|
- use only Codex official subagent threads; do not launch shell workers, a custom scheduler, a worker pool, or model fanout
|
|
119
119
|
- select the narrowest matching project custom agent by its description; the custom agent name is the spawn type
|
|
120
120
|
- custom \`agent_type\` selection and spawn-time \`model\`/\`reasoning_effort\` overrides must use \`fork_turns="none"\` or a positive bounded turn count, with the complete bounded slice contract in \`message\`; context contract: pass fork_turns="none" for listed slices
|
|
121
|
-
- \`spawn_agent\` has no provider argument; ${narutoChildren ? 'Naruto children use the
|
|
121
|
+
- \`spawn_agent\` has no provider argument; ${narutoChildren ? 'Naruto children use the tier model named in the spawn contract' : 'children use the sealed model slug'}
|
|
122
122
|
- never combine \`fork_turns="all"\` or the omitted/default full-history mode with \`agent_type\`, \`model\`, or \`reasoning_effort\`; Codex rejects that start before SubagentStart
|
|
123
123
|
- never use a full-history fork for SKS children
|
|
124
124
|
${spawnModelRouting}
|
|
125
|
-
${Object.keys(effortPreferences).length ? `-
|
|
125
|
+
${Object.keys(effortPreferences).length ? `- stored role effort preferences override role defaults, including later slices: ${JSON.stringify(effortPreferences)}` : ''}
|
|
126
126
|
${childModelRules}
|
|
127
127
|
|
|
128
128
|
Plan and capacity:
|
|
@@ -228,18 +228,17 @@ function normalizedActiveMainModel(value) {
|
|
|
228
228
|
const model = String(value?.model || '').trim();
|
|
229
229
|
return provider && model ? { provider, model } : null;
|
|
230
230
|
}
|
|
231
|
-
function renderSpawnModelRouting(narutoChildRouting) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
'-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
]
|
|
239
|
-
}
|
|
231
|
+
function renderSpawnModelRouting(narutoChildRouting, jevRouting) {
|
|
232
|
+
const lines = jevRouting
|
|
233
|
+
? [
|
|
234
|
+
'- Jev mode: Jev picks each child tier (fast, balanced, context, or deep) and the SKS PreToolUse hook seals that tier\'s newest model on every spawn_agent call; pass the contract model unchanged and spend no time choosing models or efforts'
|
|
235
|
+
]
|
|
236
|
+
: [
|
|
237
|
+
`- model routing applies to every child, including slices created after parent decomposition: the newest model of the role tier${narutoChildRouting ? ' named in the spawn contract' : ''}`
|
|
238
|
+
];
|
|
240
239
|
return [
|
|
241
|
-
|
|
242
|
-
'-
|
|
240
|
+
...lines,
|
|
241
|
+
'- parent selection never overrides the child model; stored user role preferences stay authoritative',
|
|
243
242
|
'- preserve the user-selected parent model, reasoning effort, and service tier'
|
|
244
243
|
].join('\n');
|
|
245
244
|
}
|
|
@@ -330,6 +329,7 @@ function renderDecisionContract(value) {
|
|
|
330
329
|
return [
|
|
331
330
|
'Selected decision contract:',
|
|
332
331
|
`- execute the selected plan${value.planId ? ` ${value.planId}` : ''} and retained optional context IDs; do not choose them again`,
|
|
332
|
+
value.workerCount ? `- Jev fixed the automatic child target at ${value.workerCount}; do not recompute it (real host capacity still caps each wave)` : '',
|
|
333
333
|
value.routingLane ? `- Jev sealed models: ${value.routingLane}` : '',
|
|
334
334
|
value.keepContextIds?.length
|
|
335
335
|
? `- retained optional context IDs: ${value.keepContextIds.join(', ')}`
|
|
@@ -2,7 +2,7 @@ import fsp from 'node:fs/promises';
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { randomId, runProcess, writeJsonAtomic } from '../fsx.js';
|
|
5
|
-
import { DEFAULT_SUBAGENT_EFFORT,
|
|
5
|
+
import { DEFAULT_SUBAGENT_EFFORT, defaultSubagentModel, NARUTO_PARENT_EFFORT, narutoParentModel } from './model-policy.js';
|
|
6
6
|
import { nextAgentGenerationEnv } from '../agents/agent-recursion-guard.js';
|
|
7
7
|
import { inspectDesktopBridgeCliLaunchGuard, stripRetiredDirectProviderEnv } from '../codex-control/desktop-bridge-launch-guard.js';
|
|
8
8
|
import { prepareCodexAppServerRuntimeEnv } from '../codex-control/codex-app-server-runtime-env.js';
|
|
@@ -93,9 +93,9 @@ export function defaultNarutoCredentialPolicy() {
|
|
|
93
93
|
return resolveNarutoCredentialPolicy({
|
|
94
94
|
args: [],
|
|
95
95
|
env: {},
|
|
96
|
-
defaultParentModel:
|
|
96
|
+
defaultParentModel: narutoParentModel(),
|
|
97
97
|
defaultParentEffort: NARUTO_PARENT_EFFORT,
|
|
98
|
-
defaultSubagentModel:
|
|
98
|
+
defaultSubagentModel: defaultSubagentModel(),
|
|
99
99
|
defaultSubagentEffort: DEFAULT_SUBAGENT_EFFORT
|
|
100
100
|
});
|
|
101
101
|
}
|
|
@@ -5,24 +5,28 @@ import { writeTextAtomic } from '../fsx.js';
|
|
|
5
5
|
import { MANAGED_OFFICIAL_SUBAGENT_ROLES, managedOfficialSubagentRoleByName } from '../managed-assets/managed-assets-manifest.js';
|
|
6
6
|
import { inferProviderFromModel, normalizeCodexModelId, normalizeCodexReasoningEffort, readConfiguredCodexModelRoutingContext } from '../codex-app/codex-model-catalog.js';
|
|
7
7
|
import { isRecord } from '../json/records.js';
|
|
8
|
-
import {
|
|
8
|
+
import { codexListedEfforts, latestModelForTier, latestTierModelSet, modelTierForModel } from './model-tiers.js';
|
|
9
9
|
export const ROLE_MODEL_PREFERENCES_SCHEMA = 'sks.role-model-preferences.v2';
|
|
10
10
|
const LEGACY_ROLE_MODEL_PREFERENCES_SCHEMA = 'sks.role-model-preferences.v1';
|
|
11
11
|
const ROLE_MODEL_PROFILE_PRESENTATION_LIMIT = 1_000;
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
const ROLE_PREFERENCE_EFFORTS = ['low', 'medium', 'high', 'max'];
|
|
13
|
+
export function supportedRoleModelProfiles() {
|
|
14
|
+
const defaults = new Set(MANAGED_OFFICIAL_SUBAGENT_ROLES.map((role) => `${role.model}\0${role.model_reasoning_effort}`));
|
|
15
|
+
return [...latestTierModelSet()].flatMap((model) => (codexListedEfforts(model) || ROLE_PREFERENCE_EFFORTS)
|
|
16
|
+
.filter((effort) => ROLE_PREFERENCE_EFFORTS.includes(effort))
|
|
17
|
+
.map((effort) => ({
|
|
14
18
|
provider: 'openai',
|
|
15
|
-
model
|
|
16
|
-
reasoning_effort:
|
|
17
|
-
source: 'managed-default'
|
|
18
|
-
}))
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
model,
|
|
20
|
+
reasoning_effort: effort,
|
|
21
|
+
source: defaults.has(`${model}\0${effort}`) ? 'managed-default' : 'explicit-override'
|
|
22
|
+
})));
|
|
23
|
+
}
|
|
24
|
+
function currentRoleModel(model, fallback) {
|
|
25
|
+
if (latestTierModelSet().has(model))
|
|
26
|
+
return model;
|
|
27
|
+
const tier = modelTierForModel(model);
|
|
28
|
+
return tier ? latestModelForTier(tier) : fallback;
|
|
29
|
+
}
|
|
26
30
|
export function roleModelPreferencesPath(env = process.env) {
|
|
27
31
|
const sksHome = path.resolve(env.SKS_HOME || path.join(env.HOME || os.homedir(), '.sneakoscope'));
|
|
28
32
|
return path.join(sksHome, 'preferences', 'role-models.json');
|
|
@@ -52,10 +56,11 @@ export async function readRoleModelPreferences(input = {}) {
|
|
|
52
56
|
blockers.push(`role_model_preference_invalid_profile:${role.codex_name}`);
|
|
53
57
|
continue;
|
|
54
58
|
}
|
|
55
|
-
const
|
|
59
|
+
const effectiveModel = provider === 'openai' ? currentRoleModel(model, role.model) : role.model;
|
|
60
|
+
const keepEffort = effectiveModel === model && isSupportedRoleModelProfile(effectiveModel, reasoning);
|
|
56
61
|
roles[role.codex_name] = {
|
|
57
62
|
provider: 'openai',
|
|
58
|
-
model:
|
|
63
|
+
model: effectiveModel,
|
|
59
64
|
reasoning_effort: keepEffort ? reasoning : role.model_reasoning_effort,
|
|
60
65
|
updated_at: String(rawPreference.updated_at || parsed.updated_at || '')
|
|
61
66
|
};
|
|
@@ -91,13 +96,13 @@ export async function roleModelPreferencesStatus(input = {}) {
|
|
|
91
96
|
? []
|
|
92
97
|
: [`role_model_preference_not_managed:${role}`]));
|
|
93
98
|
const allProfiles = dedupeProfiles([
|
|
94
|
-
...
|
|
99
|
+
...supportedRoleModelProfiles()
|
|
95
100
|
]);
|
|
96
101
|
const supportedProfiles = allProfiles.slice(0, ROLE_MODEL_PROFILE_PRESENTATION_LIMIT);
|
|
97
102
|
const roles = MANAGED_OFFICIAL_SUBAGENT_ROLES.map((role) => {
|
|
98
103
|
const override = read.store.roles[role.codex_name] || null;
|
|
99
104
|
const effectiveProvider = 'openai';
|
|
100
|
-
const effectiveModel =
|
|
105
|
+
const effectiveModel = override?.model || role.model;
|
|
101
106
|
const effectiveReasoning = override?.reasoning_effort || role.model_reasoning_effort;
|
|
102
107
|
return {
|
|
103
108
|
role: role.codex_name,
|
|
@@ -163,8 +168,8 @@ export async function setRoleModelPreference(input) {
|
|
|
163
168
|
const reasoning = normalizeCodexReasoningEffort(input.reasoning);
|
|
164
169
|
if (!model || !reasoning)
|
|
165
170
|
return mutationBlocked('role_model_profile_invalid');
|
|
166
|
-
if (model
|
|
167
|
-
return mutationBlocked('
|
|
171
|
+
if (!latestTierModelSet().has(model))
|
|
172
|
+
return mutationBlocked('role_model_current_model_required');
|
|
168
173
|
const env = input.env || process.env;
|
|
169
174
|
const routing = await readConfiguredCodexModelRoutingContext({
|
|
170
175
|
env,
|
|
@@ -248,7 +253,7 @@ export async function resetRoleModelPreference(input) {
|
|
|
248
253
|
export function isSupportedRoleModelProfile(model, reasoning) {
|
|
249
254
|
const normalizedModel = normalizeCodexModelId(model);
|
|
250
255
|
const normalizedReasoning = normalizeCodexReasoningEffort(reasoning);
|
|
251
|
-
return
|
|
256
|
+
return supportedRoleModelProfiles().some((profile) => (profile.model === normalizedModel
|
|
252
257
|
&& profile.reasoning_effort === normalizedReasoning));
|
|
253
258
|
}
|
|
254
259
|
async function writeOwnerOnlyStore(filePath, store) {
|