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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CHOICE_MIN_CONFIDENCE, CHOICE_MIN_PROBABILITY, CONTEXT_DROP_NOUL_MAX, DISTRIBUTION_SUM_TOLERANCE, KEEP_BASELINE_CHOICE, NEEDS_EVIDENCE_CHOICE, POLICY_REVISION, ROUTING_RISK_NOUL_MIN, ROUTING_ROLE_OMIT_NOUL_MAX, UNKNOWN_USAGE,
|
|
1
|
+
import { CHOICE_MIN_CONFIDENCE, CHOICE_MIN_PROBABILITY, CONTEXT_DROP_NOUL_MAX, DISTRIBUTION_SUM_TOLERANCE, DELEGATION_CHOICES, KEEP_BASELINE_CHOICE, NEEDS_EVIDENCE_CHOICE, POLICY_REVISION, ROUTING_RISK_NOUL_MIN, ROUTING_ROLE_OMIT_NOUL_MAX, UNKNOWN_USAGE, ESCALATION_ROUTING_TIER, routingTier } from './types.js';
|
|
2
2
|
export function compileDecision(bundle, response) {
|
|
3
3
|
const usage = usageFromWire(response.usage);
|
|
4
4
|
const decoded = decodeWireResponse(bundle, response);
|
|
@@ -33,6 +33,14 @@ export function compileDecision(bundle, response) {
|
|
|
33
33
|
else if (!fallbackReason)
|
|
34
34
|
fallbackReason = compiled.reason;
|
|
35
35
|
}
|
|
36
|
+
const delegationBinding = Object.entries(bundle.questionBindings).find(([, binding]) => binding.kind === 'delegation');
|
|
37
|
+
if (delegationBinding) {
|
|
38
|
+
const compiled = compileDelegation(decoded.response.answers[delegationBinding[0]]);
|
|
39
|
+
if (compiled.kind === 'effect')
|
|
40
|
+
effects.push(compiled.effect);
|
|
41
|
+
else if (!fallbackReason)
|
|
42
|
+
fallbackReason = compiled.reason;
|
|
43
|
+
}
|
|
36
44
|
if (effects.length === 0) {
|
|
37
45
|
return {
|
|
38
46
|
kind: 'keep_baseline',
|
|
@@ -172,7 +180,7 @@ function compileRoleRouting(bundle, answers, roleId) {
|
|
|
172
180
|
return { kind: 'baseline', reason: 'invalid_response' };
|
|
173
181
|
if (answer.choice === KEEP_BASELINE_CHOICE)
|
|
174
182
|
return { kind: 'baseline', reason: 'keep_baseline_selected' };
|
|
175
|
-
const selected =
|
|
183
|
+
const selected = routingTier(answer.choice);
|
|
176
184
|
if (!selected)
|
|
177
185
|
return { kind: 'baseline', reason: 'invalid_response' };
|
|
178
186
|
const choiceQuestion = choiceId ? bundle.request.questions[choiceId] : undefined;
|
|
@@ -180,8 +188,8 @@ function compileRoleRouting(bundle, answers, roleId) {
|
|
|
180
188
|
const uncertainty = requiredChoiceUncertainty(answer, labels);
|
|
181
189
|
if (!uncertainty.ok)
|
|
182
190
|
return { kind: 'baseline', reason: uncertainty.reason };
|
|
183
|
-
const
|
|
184
|
-
return { kind: 'effect', effect: { kind: 'select_routing', roleId,
|
|
191
|
+
const tier = escalateRole(bundle, answers, roleId) ? ESCALATION_ROUTING_TIER : selected.id;
|
|
192
|
+
return { kind: 'effect', effect: { kind: 'select_routing', roleId, tier } };
|
|
185
193
|
}
|
|
186
194
|
function escalateRole(bundle, answers, roleId) {
|
|
187
195
|
const difficultyId = questionIdFor(bundle, 'routing_difficulty', roleId);
|
|
@@ -264,6 +272,21 @@ function compileRecovery(bundle, answer) {
|
|
|
264
272
|
return { kind: 'baseline', reason: uncertainty.reason };
|
|
265
273
|
return { kind: 'effect', effect: { kind: 'dispatch_recovery', actionId: candidate.id } };
|
|
266
274
|
}
|
|
275
|
+
function compileDelegation(answer) {
|
|
276
|
+
if (!answer)
|
|
277
|
+
return { kind: 'baseline', reason: 'missing_answer' };
|
|
278
|
+
if (answer.type !== 'choice')
|
|
279
|
+
return { kind: 'baseline', reason: 'invalid_response' };
|
|
280
|
+
if (answer.choice === KEEP_BASELINE_CHOICE)
|
|
281
|
+
return { kind: 'baseline', reason: 'keep_baseline_selected' };
|
|
282
|
+
const choice = DELEGATION_CHOICES.find((row) => row === answer.choice);
|
|
283
|
+
if (!choice)
|
|
284
|
+
return { kind: 'baseline', reason: 'invalid_response' };
|
|
285
|
+
const uncertainty = requiredChoiceUncertainty(answer, [...DELEGATION_CHOICES, KEEP_BASELINE_CHOICE]);
|
|
286
|
+
if (!uncertainty.ok)
|
|
287
|
+
return { kind: 'baseline', reason: uncertainty.reason };
|
|
288
|
+
return { kind: 'effect', effect: { kind: 'select_delegation', choice } };
|
|
289
|
+
}
|
|
267
290
|
function canDropOptional(candidate, noul) {
|
|
268
291
|
return !candidate.pinned
|
|
269
292
|
&& candidate.fresh
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CONTEXT_RELEVANCE_RUBRIC, DESIGN_DEFAULTS, KEEP_BASELINE_CHOICE, NEEDS_EVIDENCE_CHOICE, QUESTION_REVISION, ROUTING_DIFFICULTY_RUBRIC,
|
|
1
|
+
import { CONTEXT_RELEVANCE_RUBRIC, DELEGATION_CHOICES, DESIGN_DEFAULTS, KEEP_BASELINE_CHOICE, NEEDS_EVIDENCE_CHOICE, QUESTION_REVISION, ROUTING_DIFFICULTY_RUBRIC, ROUTING_TIERS } from './types.js';
|
|
2
2
|
import { buildDecisionBinding, redactDecisionText } from './state.js';
|
|
3
3
|
const SAME_BATCH_DEPENDENCY = /\b(selected above|answer above|previous answer|the (plan|action) selected|if the (plan|action) selected)\b/i;
|
|
4
4
|
export const CONTEXT_RELEVANCE_LEVELS = CONTEXT_RELEVANCE_RUBRIC.length;
|
|
@@ -33,6 +33,7 @@ export function buildDecisionBundle(input) {
|
|
|
33
33
|
const contextCandidates = [...(input.contextCandidates || [])];
|
|
34
34
|
const recoveryCandidates = [...(input.recoveryCandidates || [])];
|
|
35
35
|
const routingCandidates = [...(input.routingCandidates || [])];
|
|
36
|
+
const delegationCandidate = input.delegationCandidate || null;
|
|
36
37
|
const questions = {};
|
|
37
38
|
const questionBindings = {};
|
|
38
39
|
const recoverySlot = recoveryCandidates.length > 0 ? 1 : 0;
|
|
@@ -50,6 +51,7 @@ export function buildDecisionBundle(input) {
|
|
|
50
51
|
questionBindings.plan = { kind: 'plan' };
|
|
51
52
|
}
|
|
52
53
|
const routedRoles = appendRoutingChoices(routingCandidates, questions, questionBindings, recoverySlot);
|
|
54
|
+
appendDelegationChoice(delegationCandidate, questions, questionBindings, recoverySlot);
|
|
53
55
|
for (const candidate of contextCandidates.filter((row) => !row.pinned && row.excerpt)) {
|
|
54
56
|
if (Object.keys(questions).length + 2 > DESIGN_DEFAULTS.maxQuestions - recoverySlot)
|
|
55
57
|
break;
|
|
@@ -123,8 +125,16 @@ export function buildDecisionBundle(input) {
|
|
|
123
125
|
diagnostic: redactDecisionText(input.recoveryDiagnostic || '', 800),
|
|
124
126
|
handlers: recoveryCandidates
|
|
125
127
|
},
|
|
128
|
+
delegation: delegationCandidate
|
|
129
|
+
? {
|
|
130
|
+
tool: redactDecisionText(delegationCandidate.toolName, 120),
|
|
131
|
+
targets: delegationCandidate.targets.slice(0, 16).map((target) => redactDecisionText(target, 240)),
|
|
132
|
+
mission_goal: redactDecisionText(delegationCandidate.missionGoal, 800),
|
|
133
|
+
children_started: 0
|
|
134
|
+
}
|
|
135
|
+
: null,
|
|
126
136
|
routing: {
|
|
127
|
-
|
|
137
|
+
tiers: Object.fromEntries(ROUTING_TIERS.map((row) => [row.id, {
|
|
128
138
|
effort: row.effort,
|
|
129
139
|
summary: row.summary
|
|
130
140
|
}])),
|
|
@@ -146,7 +156,7 @@ export function buildDecisionBundle(input) {
|
|
|
146
156
|
workflowRevision: input.workflowRevision,
|
|
147
157
|
sourceDigest: input.sourceDigest,
|
|
148
158
|
graphDigest: input.graphDigest,
|
|
149
|
-
candidates: { planCandidates, contextCandidates, recoveryCandidates, routingCandidates: routedRoles },
|
|
159
|
+
candidates: { planCandidates, contextCandidates, recoveryCandidates, routingCandidates: routedRoles, delegationCandidate },
|
|
150
160
|
questions,
|
|
151
161
|
requestedModel: DESIGN_DEFAULTS.model
|
|
152
162
|
}),
|
|
@@ -155,6 +165,7 @@ export function buildDecisionBundle(input) {
|
|
|
155
165
|
contextCandidates,
|
|
156
166
|
recoveryCandidates,
|
|
157
167
|
routingCandidates: routedRoles,
|
|
168
|
+
delegationCandidate,
|
|
158
169
|
baselinePlanId: input.baselinePlanId ?? planCandidates[0]?.id ?? null,
|
|
159
170
|
questionBindings
|
|
160
171
|
};
|
|
@@ -168,14 +179,14 @@ function appendRoutingChoices(roles, questions, bindings, recoverySlot) {
|
|
|
168
179
|
if (questionRoom(questions, recoverySlot) < 1)
|
|
169
180
|
break;
|
|
170
181
|
const id = `route_${role.id}`;
|
|
171
|
-
const instructions = redactDecisionText(`Choose the
|
|
182
|
+
const instructions = redactDecisionText(`Choose the model tier for routing.roles.${role.id} using state.task and that role summary. Each tier is the newest model for that speed and accuracy. Prefer the fastest tier that can do the work correctly. Use deep for judgment, ambiguity, or high-stakes work. Select ${KEEP_BASELINE_CHOICE} when the work is not distinguished. Do not invent a tier.`);
|
|
172
183
|
assertIndependentQuestion(instructions);
|
|
173
184
|
questions[id] = {
|
|
174
185
|
type: 'choice',
|
|
175
186
|
instructions,
|
|
176
187
|
criteria: {
|
|
177
|
-
...Object.fromEntries(
|
|
178
|
-
[KEEP_BASELINE_CHOICE]: 'The role work is not distinguished enough to leave the baseline
|
|
188
|
+
...Object.fromEntries(ROUTING_TIERS.map((row) => [row.id, row.summary])),
|
|
189
|
+
[KEEP_BASELINE_CHOICE]: 'The role work is not distinguished enough to leave the baseline tier.'
|
|
179
190
|
}
|
|
180
191
|
};
|
|
181
192
|
bindings[id] = { kind: 'routing', roleId: role.id };
|
|
@@ -183,6 +194,25 @@ function appendRoutingChoices(roles, questions, bindings, recoverySlot) {
|
|
|
183
194
|
}
|
|
184
195
|
return included;
|
|
185
196
|
}
|
|
197
|
+
function appendDelegationChoice(candidate, questions, bindings, recoverySlot) {
|
|
198
|
+
if (!candidate || questionRoom(questions, recoverySlot) < 1)
|
|
199
|
+
return;
|
|
200
|
+
const instructions = redactDecisionText(`state.delegation describes a tool call the Naruto parent thread wants to make before any child thread exists. Using state.task, state.delegation.tool, and state.delegation.targets, choose whether that edit is slice implementation that a child must own, or orchestration scaffolding that no slice owns and that children depend on (a shared interface or type stub, workspace or build wiring, or a plan file). Any change to feature, fix, or test code is slice work, however small. Select ${KEEP_BASELINE_CHOICE} when the evidence does not distinguish them.`);
|
|
201
|
+
assertIndependentQuestion(instructions);
|
|
202
|
+
questions.delegation = {
|
|
203
|
+
type: 'choice',
|
|
204
|
+
instructions,
|
|
205
|
+
criteria: {
|
|
206
|
+
...Object.fromEntries(DELEGATION_CHOICES.map((choice) => [choice, DELEGATION_CHOICE_SUMMARIES[choice]])),
|
|
207
|
+
[KEEP_BASELINE_CHOICE]: 'The evidence does not distinguish orchestration scaffolding from slice work.'
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
bindings.delegation = { kind: 'delegation' };
|
|
211
|
+
}
|
|
212
|
+
const DELEGATION_CHOICE_SUMMARIES = Object.freeze({
|
|
213
|
+
delegate_child: 'The edit implements assigned slice work. The parent must spawn a child for it first.',
|
|
214
|
+
parent_owned: 'The edit is orchestration scaffolding that no slice owns and that children depend on.'
|
|
215
|
+
});
|
|
186
216
|
function appendRoutingSpeculation(roles, questions, bindings, recoverySlot) {
|
|
187
217
|
for (const role of roles) {
|
|
188
218
|
if (questionRoom(questions, recoverySlot) < 1)
|
|
@@ -199,14 +229,14 @@ function appendRoutingSpeculation(roles, questions, bindings, recoverySlot) {
|
|
|
199
229
|
if (questionRoom(questions, recoverySlot) < 1)
|
|
200
230
|
break;
|
|
201
231
|
const riskId = `risk_${role.id}`;
|
|
202
|
-
const riskInstructions = redactDecisionText(`Does routing.roles.${role.id} need
|
|
232
|
+
const riskInstructions = redactDecisionText(`Does routing.roles.${role.id} need the deep tier because the work is high-stakes, ambiguous, or unsafe on a faster tier?`);
|
|
203
233
|
assertIndependentQuestion(riskInstructions);
|
|
204
234
|
questions[riskId] = {
|
|
205
235
|
type: 'noul',
|
|
206
236
|
instructions: riskInstructions,
|
|
207
237
|
criteria: {
|
|
208
|
-
true: 'The work needs the most capable
|
|
209
|
-
false: 'A faster
|
|
238
|
+
true: 'The work needs the most capable latest model.',
|
|
239
|
+
false: 'A faster latest model can do this work.'
|
|
210
240
|
}
|
|
211
241
|
};
|
|
212
242
|
bindings[riskId] = { kind: 'routing_risk', roleId: role.id };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { effortForTier, latestTierModelSet, resolveLatestModelTiers } from '../subagents/model-tiers.js';
|
|
2
|
+
import { routingTier } from './types.js';
|
|
2
3
|
const ROLE_ID = /^[a-z][a-z0-9_]{0,40}$/;
|
|
3
4
|
export const MAX_JEV_ROUTING_ROLES = 8;
|
|
4
5
|
export function buildRoutingCandidates(input) {
|
|
@@ -19,17 +20,17 @@ export function applySealedRouting(agents, selected) {
|
|
|
19
20
|
if (!selected || !agents)
|
|
20
21
|
return { ...(agents || {}) };
|
|
21
22
|
const next = { ...agents };
|
|
23
|
+
const current = latestTierModelSet();
|
|
22
24
|
for (const [name, effort] of Object.entries(selected.efforts)) {
|
|
23
25
|
const model = selected.models[name];
|
|
24
|
-
|
|
25
|
-
if (!sealed || sealed.effort !== effort)
|
|
26
|
+
if (!model || !current.has(model))
|
|
26
27
|
continue;
|
|
27
28
|
const row = next[name];
|
|
28
29
|
if (!row || row.routing_dynamic !== true || row.routed_model_policy === 'user_role_model_preference')
|
|
29
30
|
continue;
|
|
30
31
|
next[name] = {
|
|
31
32
|
...row,
|
|
32
|
-
routed_model:
|
|
33
|
+
routed_model: model,
|
|
33
34
|
routed_model_reasoning_effort: effort,
|
|
34
35
|
routed_model_policy: 'jev_sealed_routing',
|
|
35
36
|
jev_routing_lane: selected.id
|
|
@@ -38,21 +39,25 @@ export function applySealedRouting(agents, selected) {
|
|
|
38
39
|
return next;
|
|
39
40
|
}
|
|
40
41
|
export function assembleRoutingSelection(effects) {
|
|
42
|
+
const resolved = resolveLatestModelTiers();
|
|
41
43
|
const models = {};
|
|
42
44
|
const efforts = {};
|
|
45
|
+
const tiers = {};
|
|
43
46
|
for (const effect of effects) {
|
|
44
|
-
const
|
|
45
|
-
if (!
|
|
47
|
+
const tier = routingTier(effect.tier);
|
|
48
|
+
if (!tier || !ROLE_ID.test(effect.roleId))
|
|
46
49
|
continue;
|
|
47
|
-
models[effect.roleId] =
|
|
48
|
-
efforts[effect.roleId] =
|
|
50
|
+
models[effect.roleId] = resolved.models[tier.id];
|
|
51
|
+
efforts[effect.roleId] = effortForTier(tier.id, resolved);
|
|
52
|
+
tiers[effect.roleId] = tier.id;
|
|
49
53
|
}
|
|
50
54
|
if (Object.keys(models).length === 0)
|
|
51
55
|
return null;
|
|
52
56
|
return {
|
|
53
57
|
id: 'jev_role_fanout',
|
|
54
|
-
summary: 'Jev chose a
|
|
58
|
+
summary: 'Jev chose a tier for each dynamic Naruto role; each tier is its newest model.',
|
|
55
59
|
models,
|
|
56
|
-
efforts
|
|
60
|
+
efforts,
|
|
61
|
+
tiers
|
|
57
62
|
};
|
|
58
63
|
}
|
|
@@ -1,25 +1,14 @@
|
|
|
1
|
-
export const
|
|
2
|
-
Object.freeze({
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}),
|
|
7
|
-
Object.freeze({
|
|
8
|
-
id: 'gpt-5.6-sol',
|
|
9
|
-
effort: 'low',
|
|
10
|
-
summary: 'Fast sealed model. Simple coding whose result is already specified.'
|
|
11
|
-
}),
|
|
12
|
-
Object.freeze({
|
|
13
|
-
id: 'gpt-5.6-terra',
|
|
14
|
-
effort: 'medium',
|
|
15
|
-
summary: 'Context and tools. Search, multi-file reading, and broad exploration.'
|
|
16
|
-
}),
|
|
17
|
-
Object.freeze({
|
|
18
|
-
id: 'gpt-6-astra',
|
|
19
|
-
effort: 'max',
|
|
20
|
-
summary: 'Escalate. Judgment, architecture, ambiguity, security, or high-stakes work.'
|
|
21
|
-
})
|
|
1
|
+
export const ROUTING_TIERS = Object.freeze([
|
|
2
|
+
Object.freeze({ id: 'fast', effort: 'low', summary: 'Fastest latest model. Mechanical edits, renames, formatting, and other one-step changes.' }),
|
|
3
|
+
Object.freeze({ id: 'balanced', effort: 'low', summary: 'Fast latest model. Simple coding whose result is already specified.' }),
|
|
4
|
+
Object.freeze({ id: 'context', effort: 'medium', summary: 'Latest model for context and tools. Search, multi-file reading, and broad exploration.' }),
|
|
5
|
+
Object.freeze({ id: 'deep', effort: 'max', summary: 'Escalate to the most capable latest model. Judgment, architecture, ambiguity, security, or high-stakes work.' })
|
|
22
6
|
]);
|
|
7
|
+
export const ESCALATION_ROUTING_TIER = 'deep';
|
|
8
|
+
export function routingTier(value) {
|
|
9
|
+
const match = ROUTING_TIERS.find((row) => row.id === value);
|
|
10
|
+
return match ? { id: match.id, effort: match.effort } : null;
|
|
11
|
+
}
|
|
23
12
|
export const ROUTING_DIFFICULTY_RUBRIC = Object.freeze([
|
|
24
13
|
'Mechanical one-step work: rename, format, copy, or a single exact edit.',
|
|
25
14
|
'Simple coding whose result is already specified.',
|
|
@@ -28,10 +17,7 @@ export const ROUTING_DIFFICULTY_RUBRIC = Object.freeze([
|
|
|
28
17
|
]);
|
|
29
18
|
export const ROUTING_RISK_NOUL_MIN = 0.70;
|
|
30
19
|
export const ROUTING_ROLE_OMIT_NOUL_MAX = 0.35;
|
|
31
|
-
export
|
|
32
|
-
const match = SEALED_ROUTING_MODELS.find((row) => row.id === model);
|
|
33
|
-
return match ? { id: match.id, effort: match.effort } : null;
|
|
34
|
-
}
|
|
20
|
+
export const DELEGATION_CHOICES = Object.freeze(['delegate_child', 'parent_owned']);
|
|
35
21
|
export const DESIGN_DEFAULTS = Object.freeze({
|
|
36
22
|
mode: 'off',
|
|
37
23
|
model: 'typesafe/jev-1.13',
|
|
@@ -11,7 +11,7 @@ import { collectNestedProjectRoots } from './current-project-guidance-nested.js'
|
|
|
11
11
|
import { escapeRegExp } from '../text/regex.js';
|
|
12
12
|
export const CURRENT_PROJECT_GUIDANCE_SCHEMA = 'sks.current-project-guidance.v1';
|
|
13
13
|
const AGENTS_MARKER = 'BEGIN Sneakoscope Codex GX MANAGED BLOCK';
|
|
14
|
-
const RETIRED_COMMAND_NAMES = ['team', 'mad-db', 'tmux', 'xai', 'swarm', 'agent', 'ralph', 'db', 'ui', 'glm'];
|
|
14
|
+
const RETIRED_COMMAND_NAMES = ['team', 'mad-db', 'tmux', 'xai', 'swarm', 'agent', 'ralph', 'loop', 'db', 'ui', 'glm'];
|
|
15
15
|
const RETIRED_DOLLAR_COMMAND_NAMES = ['Agent', 'Team', 'MAD-DB', 'Swarm', 'ShadowClone', 'Kagebunshin', 'Ralph'];
|
|
16
16
|
const LEGACY_UNPREFIXED_DOLLAR_COMMAND_NAMES = Array.from(new Set([
|
|
17
17
|
...RETIRED_DOLLAR_COMMAND_NAMES,
|
|
@@ -69,6 +69,43 @@ export async function reconcileCurrentProjectGuidance(opts) {
|
|
|
69
69
|
warnings: counters.warnings
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
|
+
export async function reconcileManagedProjectPromptGuidance(root) {
|
|
73
|
+
const projectRoot = path.resolve(root);
|
|
74
|
+
const refreshed = [];
|
|
75
|
+
let errors = 0;
|
|
76
|
+
const agentsFile = path.join(projectRoot, 'AGENTS.md');
|
|
77
|
+
const agentsStat = await fsp.lstat(agentsFile).catch(() => null);
|
|
78
|
+
if (agentsStat?.isFile() && !agentsStat.isSymbolicLink()) {
|
|
79
|
+
const before = await readText(agentsFile, '');
|
|
80
|
+
if (before.includes(AGENTS_MARKER) && managedAgentsBlockNeedsReconcile(before)) {
|
|
81
|
+
try {
|
|
82
|
+
await mergeManagedBlock(agentsFile, 'Sneakoscope Codex GX MANAGED BLOCK', agentsBlockText());
|
|
83
|
+
refreshed.push('AGENTS.md');
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
errors += 1;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const quickFile = path.join(projectRoot, '.codex', 'SNEAKOSCOPE.md');
|
|
91
|
+
const quickStat = await fsp.lstat(quickFile).catch(() => null);
|
|
92
|
+
if (quickStat?.isFile() && !quickStat.isSymbolicLink()) {
|
|
93
|
+
const before = await readText(quickFile, '');
|
|
94
|
+
if (isManagedQuickReference(before)) {
|
|
95
|
+
const expected = codexAppQuickReference(quickReferenceInstallScope(before, 'project'), quickReferenceCommandPrefix(before, 'project'));
|
|
96
|
+
if (before !== expected) {
|
|
97
|
+
try {
|
|
98
|
+
await writeTextAtomic(quickFile, expected);
|
|
99
|
+
refreshed.push('.codex/SNEAKOSCOPE.md');
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
errors += 1;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return { refreshed, errors };
|
|
108
|
+
}
|
|
72
109
|
async function reconcileGuidanceScope(scope, fix, runId, counters) {
|
|
73
110
|
const rootStat = await fsp.lstat(scope.root).catch((error) => error?.code === 'ENOENT' ? null : Promise.reject(error));
|
|
74
111
|
if (!rootStat)
|
|
@@ -31,7 +31,8 @@ const REWRITE_RULES = [
|
|
|
31
31
|
{ id: 'cli-team', pattern: new RegExp(`${LEFT}sks\\s+team${RIGHT}`, 'gi'), replace: '$1sks naruto' },
|
|
32
32
|
{ id: 'cli-agent', pattern: new RegExp(`${LEFT}sks\\s+agent${RIGHT}`, 'gi'), replace: '$1sks naruto' },
|
|
33
33
|
{ id: 'cli-swarm', pattern: new RegExp(`${LEFT}sks\\s+swarm${RIGHT}`, 'gi'), replace: '$1sks naruto' },
|
|
34
|
-
{ id: 'cli-ralph', pattern: new RegExp(`${LEFT}sks\\s+ralph${RIGHT}`, 'gi'), replace: '$1sks
|
|
34
|
+
{ id: 'cli-ralph', pattern: new RegExp(`${LEFT}sks\\s+ralph${RIGHT}`, 'gi'), replace: '$1sks naruto' },
|
|
35
|
+
{ id: 'cli-loop', pattern: new RegExp(`${LEFT}sks\\s+loop${RIGHT}`, 'gi'), replace: '$1sks naruto' },
|
|
35
36
|
{ id: 'cli-tmux', pattern: new RegExp(`${LEFT}sks\\s+tmux${RIGHT}`, 'gi'), replace: '$1sks --mad' },
|
|
36
37
|
{ id: 'cli-xai', pattern: new RegExp(`${LEFT}sks\\s+xai${RIGHT}`, 'gi'), replace: '$1sks bridge provider configure openrouter --api-key-stdin' },
|
|
37
38
|
{ id: 'cli-glm', pattern: new RegExp(`${LEFT}sks\\s+glm${RIGHT}`, 'gi'), replace: '$1sks bridge provider configure openrouter --api-key-stdin' },
|
|
@@ -19,7 +19,7 @@ const OFFICIAL_SUBAGENT_SPAWN_COMPATIBILITY_CONTEXT = [
|
|
|
19
19
|
'SKS Codex 0.145 official-subagent spawn compatibility:',
|
|
20
20
|
'- Full-history forks (`fork_turns="all"`, including the omitted/default full-history mode) inherit the parent agent type, model, and reasoning effort.',
|
|
21
21
|
'- When selecting a custom `agent_type` or overriding `model`/`reasoning_effort`, set `fork_turns="none"` or a positive bounded turn count and put the complete bounded slice contract in `message`.',
|
|
22
|
-
'- SKS children must
|
|
22
|
+
'- SKS children must pass the slice contract `model` (the newest model of the role tier), its `reasoning_effort`, and `fork_turns=\"none\"` or a positive bounded turn count. When Jev mode is on, the SKS PreToolUse hook seals Jev\'s tier on every spawn, so do not tune model or effort yourself. A stored user role-model preference wins. Do not use omitted/default or full-history forks, which inherit the parent model.'
|
|
23
23
|
].join('\n');
|
|
24
24
|
export function officialSubagentSpawnCompatibilityContext() {
|
|
25
25
|
return OFFICIAL_SUBAGENT_SPAWN_COMPATIBILITY_CONTEXT;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { consultJevTurnModel } from '../decisions/integration.js';
|
|
2
|
+
import { managedOfficialSubagentRoleByName } from '../managed-assets/managed-assets-manifest.js';
|
|
3
|
+
import { subagentModelProfile } from '../subagents/model-policy.js';
|
|
4
|
+
import { effortForTier, latestModelForTier, latestTierModelSet } from '../subagents/model-tiers.js';
|
|
2
5
|
import { readRoleModelPreferences } from '../subagents/role-model-preferences.js';
|
|
3
6
|
const SPAWN_TOOLS = new Set(['spawn_agent', 'collaboration.spawn_agent', 'functions.spawn_agent']);
|
|
4
7
|
function spawnInput(payload) {
|
|
@@ -22,6 +25,14 @@ function spawnTask(input) {
|
|
|
22
25
|
.join('\n')
|
|
23
26
|
.slice(0, 1200);
|
|
24
27
|
}
|
|
28
|
+
export function roleTierFallback(agentType) {
|
|
29
|
+
const role = agentType ? managedOfficialSubagentRoleByName(agentType) : null;
|
|
30
|
+
if (role?.model_policy) {
|
|
31
|
+
const profile = subagentModelProfile(role.model_policy);
|
|
32
|
+
return { model: profile.model, effort: effortForTier(profile.tier) };
|
|
33
|
+
}
|
|
34
|
+
return { model: latestModelForTier('deep'), effort: effortForTier('deep') };
|
|
35
|
+
}
|
|
25
36
|
export async function jevSpawnModelRewrite(root, state, payload) {
|
|
26
37
|
if (!narutoParent(state))
|
|
27
38
|
return null;
|
|
@@ -38,13 +49,21 @@ export async function jevSpawnModelRewrite(root, state, payload) {
|
|
|
38
49
|
if (!task)
|
|
39
50
|
return null;
|
|
40
51
|
const decision = await consultJevTurnModel({ root, prompt: task, roleId: 'spawn' }).catch(() => null);
|
|
41
|
-
if (!decision?.
|
|
52
|
+
if (!decision?.called)
|
|
42
53
|
return null;
|
|
54
|
+
const forkTurns = input.fork_turns === undefined ? { fork_turns: 'none' } : {};
|
|
55
|
+
if (!decision.model || !decision.effort) {
|
|
56
|
+
if (latestTierModelSet().has(String(input.model || '')))
|
|
57
|
+
return null;
|
|
58
|
+
const fallback = roleTierFallback(agent);
|
|
59
|
+
return { ...input, model: fallback.model, reasoning_effort: fallback.effort, ...forkTurns };
|
|
60
|
+
}
|
|
43
61
|
if (input.model === decision.model && input.reasoning_effort === decision.effort)
|
|
44
62
|
return null;
|
|
45
63
|
return {
|
|
46
64
|
...input,
|
|
47
65
|
model: decision.model,
|
|
48
|
-
reasoning_effort: decision.effort
|
|
66
|
+
reasoning_effort: decision.effort,
|
|
67
|
+
...forkTurns
|
|
49
68
|
};
|
|
50
69
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import fsp from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { nowIso, PACKAGE_VERSION, readJson, writeJsonAtomic } from '../fsx.js';
|
|
4
|
+
import { ensureConfinedDirectory } from '../managed-path-safety.js';
|
|
5
|
+
import { reconcileManagedProjectPromptGuidance } from '../doctor/current-project-guidance.js';
|
|
6
|
+
export const MANAGED_GUIDANCE_STAMP_SCHEMA = 'sks.managed-guidance-generation.v1';
|
|
7
|
+
export function managedGuidanceStampPath(root) {
|
|
8
|
+
return path.join(root, '.sneakoscope', 'state', 'managed-guidance-generation.json');
|
|
9
|
+
}
|
|
10
|
+
export async function maybeReconcileManagedGuidancePreflight(root) {
|
|
11
|
+
const projectRoot = path.resolve(root);
|
|
12
|
+
const stampPath = managedGuidanceStampPath(projectRoot);
|
|
13
|
+
const stamp = await readJson(stampPath, null).catch(() => null);
|
|
14
|
+
if (stamp?.schema === MANAGED_GUIDANCE_STAMP_SCHEMA && stamp?.version === PACKAGE_VERSION)
|
|
15
|
+
return null;
|
|
16
|
+
const sksDir = await fsp.lstat(path.join(projectRoot, '.sneakoscope')).catch(() => null);
|
|
17
|
+
if (!sksDir?.isDirectory() || sksDir.isSymbolicLink())
|
|
18
|
+
return null;
|
|
19
|
+
const report = await reconcileManagedProjectPromptGuidance(projectRoot);
|
|
20
|
+
if (report.errors > 0)
|
|
21
|
+
return { refreshed: report.refreshed };
|
|
22
|
+
await ensureConfinedDirectory(projectRoot, path.dirname(stampPath));
|
|
23
|
+
await writeJsonAtomic(stampPath, {
|
|
24
|
+
schema: MANAGED_GUIDANCE_STAMP_SCHEMA,
|
|
25
|
+
version: PACKAGE_VERSION,
|
|
26
|
+
refreshed: report.refreshed,
|
|
27
|
+
checked_at: nowIso()
|
|
28
|
+
});
|
|
29
|
+
return { refreshed: report.refreshed };
|
|
30
|
+
}
|
|
@@ -3,7 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { appendJsonl, nowIso, readJson, sha256, writeJsonAtomic } from '../fsx.js';
|
|
4
4
|
import { missionDir, updateCurrentIfMissionAndRun } from '../mission.js';
|
|
5
5
|
import { ensureConfinedDirectory } from '../managed-path-safety.js';
|
|
6
|
-
import { NARUTO_PARENT_EFFORT,
|
|
6
|
+
import { NARUTO_PARENT_EFFORT, narutoParentModel } from '../subagents/model-policy.js';
|
|
7
7
|
import { officialSubagentRolePlan } from '../subagents/agent-catalog.js';
|
|
8
8
|
import { bindTrustworthySubagentParentSummaryToRun, normalizeSubagentEvent, normalizeSubagentParentSummary, persistOrReuseTrustworthySubagentParentSummary, readSubagentEvents, recordSubagentEvent, SUBAGENT_EVIDENCE_FILENAME, SUBAGENT_PARENT_SUMMARY_FILENAME, writeSubagentEvidence } from '../subagents/subagent-evidence.js';
|
|
9
9
|
import { officialSubagentPreparationInProgress, SUBAGENT_LIFECYCLE_CAPTURE_FAILURE_DIR, withOfficialSubagentLifecycleLock, writeNarutoGate } from '../subagents/official-subagent-preparation.js';
|
|
@@ -308,7 +308,7 @@ async function refreshOfficialSubagentCompletionArtifactsLocked(root, state, par
|
|
|
308
308
|
}
|
|
309
309
|
const previousGate = existingGate || {};
|
|
310
310
|
const parentModel = plan.observed_parent_model || state.observed_parent_model || null;
|
|
311
|
-
const parentModelMismatch = previousGate.parent_model_match === false || observedParentModelMismatch(parentModel,
|
|
311
|
+
const parentModelMismatch = previousGate.parent_model_match === false || observedParentModelMismatch(parentModel, narutoParentModel());
|
|
312
312
|
const blockers = [...new Set([
|
|
313
313
|
...evidence.blockers,
|
|
314
314
|
...(Array.isArray(previousGate.config_blockers) ? previousGate.config_blockers.map(String) : []),
|
|
@@ -329,7 +329,7 @@ async function refreshOfficialSubagentCompletionArtifactsLocked(root, state, par
|
|
|
329
329
|
route: '$Naruto',
|
|
330
330
|
status: passed ? 'completed' : evidence.status,
|
|
331
331
|
parent: {
|
|
332
|
-
model:
|
|
332
|
+
model: narutoParentModel(),
|
|
333
333
|
model_reasoning_effort: NARUTO_PARENT_EFFORT,
|
|
334
334
|
observed_model: parentModel,
|
|
335
335
|
observed_model_match: parentModel ? !parentModelMismatch : null
|