sneakoscope 6.1.2 → 6.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +11 -4
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/dist/cli/install-helpers.js +8 -3
  5. package/dist/config/skills-manifest.json +58 -58
  6. package/dist/core/agents/agent-effort-policy.js +28 -17
  7. package/dist/core/agents/agent-schema.js +1 -1
  8. package/dist/core/codex/codex-config-guard.js +178 -7
  9. package/dist/core/codex/codex-config-readability.js +21 -8
  10. package/dist/core/codex/codex-config-toml.js +14 -11
  11. package/dist/core/codex-app/mcp-manager.js +679 -0
  12. package/dist/core/codex-app/sks-menubar.js +405 -6
  13. package/dist/core/codex-control/codex-lb-launch-recovery.js +15 -0
  14. package/dist/core/codex-native/core-skill-manifest.js +1 -1
  15. package/dist/core/commands/mad-sks-command.js +44 -3
  16. package/dist/core/commands/menubar-command.js +94 -0
  17. package/dist/core/commands/naruto-command.js +3 -1
  18. package/dist/core/commands/wiki-command.js +11 -5
  19. package/dist/core/fsx.js +1 -0
  20. package/dist/core/hooks-runtime/code-pack-freshness-preflight.js +14 -8
  21. package/dist/core/hooks-runtime/naruto-decision-gate.js +184 -0
  22. package/dist/core/hooks-runtime/stop-repeat-guard.js +89 -0
  23. package/dist/core/hooks-runtime.js +36 -105
  24. package/dist/core/init/skills.js +4 -4
  25. package/dist/core/init.js +1 -1
  26. package/dist/core/managed-assets/managed-assets-manifest.js +178 -29
  27. package/dist/core/preflight/parallel-preflight-engine.js +16 -3
  28. package/dist/core/proof/route-adapter.js +1 -1
  29. package/dist/core/proof/selftest-proof-fixtures.js +3 -5
  30. package/dist/core/provider/model-router.js +15 -6
  31. package/dist/core/release/package-size-budget.js +4 -6
  32. package/dist/core/research/research-plan-markdown.js +123 -0
  33. package/dist/core/research/research-super-search.js +8 -4
  34. package/dist/core/research.js +4 -119
  35. package/dist/core/retention.js +70 -2
  36. package/dist/core/routes/dollar-manifest-lite.js +1 -1
  37. package/dist/core/routes.js +75 -22
  38. package/dist/core/subagents/agent-catalog.js +87 -15
  39. package/dist/core/subagents/model-policy.js +189 -48
  40. package/dist/core/subagents/naruto-help-contract.js +11 -4
  41. package/dist/core/subagents/official-subagent-preparation.js +55 -9
  42. package/dist/core/subagents/official-subagent-prompt.js +45 -23
  43. package/dist/core/subagents/thread-budget.js +1 -1
  44. package/dist/core/subagents/triwiki-attention.js +117 -14
  45. package/dist/core/triwiki/code-pack-head-freshness.js +291 -0
  46. package/dist/core/version.js +1 -1
  47. package/dist/core/zellij/zellij-launcher.js +12 -2
  48. package/dist/core/zellij/zellij-pane-proof.js +9 -1
  49. package/dist/core/zellij/zellij-update.js +14 -1
  50. package/dist/scripts/canonical-test-runner.js +7 -1
  51. package/dist/scripts/codex-lb-fast-mode-truth-check.js +11 -1
  52. package/dist/scripts/codex-lb-fast-ui-preservation-check.js +9 -2
  53. package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +13 -2
  54. package/dist/scripts/codex-native-agent-role-content-check.js +13 -1
  55. package/dist/scripts/codex-sdk-backend-router-check.js +2 -0
  56. package/dist/scripts/lib/codex-sdk-gate-lib.js +4 -0
  57. package/dist/scripts/official-subagent-workflow-check.js +1 -1
  58. package/dist/scripts/packlist-performance-check.js +1 -18
  59. package/dist/scripts/python-codex-sdk-all-pipelines-check.js +8 -0
  60. package/dist/scripts/sks-menubar-install-check.js +6 -1
  61. package/dist/scripts/super-search-provider-interface-check.js +31 -18
  62. package/package.json +3 -1
  63. package/dist/scripts/codex-0139-feature-probes-check.js +0 -30
  64. package/dist/scripts/codex-0139-marketplace-source-check.js +0 -13
@@ -1,29 +1,58 @@
1
1
  export const NARUTO_PARENT_MODEL = 'gpt-5.6-sol';
2
2
  export const NARUTO_PARENT_EFFORT = 'max';
3
- export const DEFAULT_SUBAGENT_MODEL = 'gpt-5.6-luna';
4
- export const THINKING_SUBAGENT_MODEL = 'gpt-5.6-sol';
5
- export const SUBAGENT_EFFORT = 'max';
6
- const SOL_TASK_RE = new RegExp([
7
- '\\bui\\b',
8
- '\\bux\\b',
9
- '\\bgui\\b',
10
- 'frontend',
11
- 'visual',
12
- 'review',
13
- 'audit',
14
- 'debug',
3
+ export const LUNA_SUBAGENT_MODEL = 'gpt-5.6-luna';
4
+ export const TERRA_SUBAGENT_MODEL = 'gpt-5.6-terra';
5
+ export const SOL_SUBAGENT_MODEL = 'gpt-5.6-sol';
6
+ export const LUNA_SUBAGENT_EFFORT = 'max';
7
+ export const TERRA_SUBAGENT_EFFORT = 'medium';
8
+ export const DEFAULT_SUBAGENT_EFFORT = 'high';
9
+ export const SOL_MAX_SUBAGENT_EFFORT = 'max';
10
+ // Compatibility exports retained for research and older callers. New code
11
+ // should select a policy profile instead of combining model/effort constants.
12
+ export const DEFAULT_SUBAGENT_MODEL = SOL_SUBAGENT_MODEL;
13
+ export const THINKING_SUBAGENT_MODEL = SOL_SUBAGENT_MODEL;
14
+ export const SUBAGENT_EFFORT = SOL_MAX_SUBAGENT_EFFORT;
15
+ export const SUBAGENT_MODEL_POLICIES = Object.freeze({
16
+ luna_max_mechanical: Object.freeze({
17
+ policy: 'luna_max_mechanical',
18
+ kind: 'worker',
19
+ model: LUNA_SUBAGENT_MODEL,
20
+ modelReasoningEffort: LUNA_SUBAGENT_EFFORT
21
+ }),
22
+ sol_high_implementation: Object.freeze({
23
+ policy: 'sol_high_implementation',
24
+ kind: 'worker',
25
+ model: SOL_SUBAGENT_MODEL,
26
+ modelReasoningEffort: DEFAULT_SUBAGENT_EFFORT
27
+ }),
28
+ sol_max_judgment: Object.freeze({
29
+ policy: 'sol_max_judgment',
30
+ kind: 'expert',
31
+ model: SOL_SUBAGENT_MODEL,
32
+ modelReasoningEffort: SOL_MAX_SUBAGENT_EFFORT
33
+ }),
34
+ terra_medium_context_tools: Object.freeze({
35
+ policy: 'terra_medium_context_tools',
36
+ kind: 'worker',
37
+ model: TERRA_SUBAGENT_MODEL,
38
+ modelReasoningEffort: TERRA_SUBAGENT_EFFORT
39
+ })
40
+ });
41
+ const JUDGMENT_TASK_RE = new RegExp([
42
+ '\\breview(?:er|ing)?\\b',
43
+ '\\baudit(?:or|ing)?\\b',
44
+ '\\bdebug(?:ger|ging)?\\b',
15
45
  'diagnos',
16
46
  'investigat',
17
47
  '\\bwhy\\b',
18
48
  '\\bfail(?:ed|ure|ing)?\\b',
19
49
  'root[- ]?cause',
20
50
  'strategy',
21
- '\\bplan\\b',
22
- 'planning',
51
+ '\\bplan(?:ning)?\\b',
23
52
  'product',
24
53
  'architecture',
25
54
  'architect',
26
- 'design',
55
+ '\\bdesign(?:ing)?\\b',
27
56
  'refactor',
28
57
  'integrat',
29
58
  'conflict',
@@ -36,8 +65,12 @@ const SOL_TASK_RE = new RegExp([
36
65
  'release',
37
66
  'publish',
38
67
  'deploy',
39
- 'tests?[-_ ]?(?:fail|failure|debug|diagnos|root[- ]?cause)',
40
- 'failing tests?',
68
+ 'research',
69
+ 'hypothesis',
70
+ 'falsif',
71
+ 'novelty',
72
+ 'test[-_ ]?(?:design|strategy|coverage|gap|review)',
73
+ 'regression',
41
74
  'flaky',
42
75
  'ambiguous',
43
76
  'ambiguity',
@@ -47,15 +80,12 @@ const SOL_TASK_RE = new RegExp([
47
80
  'assessment',
48
81
  '\\bexpert\\b',
49
82
  '\\brisk\\b',
50
- '사용성',
51
- '화면',
52
- '유아이',
53
- '유엑스',
54
- '프론트엔드',
55
83
  '리뷰',
56
84
  '검토',
85
+ '감사',
57
86
  '디버깅',
58
87
  '진단',
88
+ '조사',
59
89
  '원인',
60
90
  '전략',
61
91
  '기획',
@@ -66,27 +96,123 @@ const SOL_TASK_RE = new RegExp([
66
96
  '통합',
67
97
  '충돌',
68
98
  '보안',
99
+ '인증',
69
100
  '안전',
70
101
  '데이터베이스',
71
102
  '마이그레이션',
72
103
  '배포',
73
104
  '릴리즈',
74
105
  '출시',
106
+ '연구',
107
+ '가설',
108
+ '반증',
109
+ '회귀',
75
110
  '모호',
76
111
  '트레이드오프',
77
112
  '품질',
78
113
  '판정',
79
114
  '위험'
80
115
  ].join('|'), 'i');
81
- const CLEAR_BOUNDED_TASK_RE = new RegExp([
82
- '\\b(?:clear|bounded|exact|specified)\\b[^\n]{0,48}\\b(?:task|change|step|scope|fixture|file|implementation|rename|copy|format|inventory|extract|list|check)\\b',
83
- '\\b(?:mechanical|repeatable)\\b',
84
- '\\bstructured[- ]?(?:extract|extraction|output|inventory|list)\\b',
85
- 'code_modification',
86
- 'test_execution',
87
- 'single[- ]?(?:file|step|check|change)',
88
- '(?:정해진|명확한?|단순한?|반복적?|기계적)[^\n]{0,32}(?:작업|변경|단계|파일|목록|추출|이름\\s*변경)'
116
+ const CONTEXT_TOOL_TASK_RE = new RegExp([
117
+ 'computer[- ]?use',
118
+ '\\bbrowser(?:[- ]?use)?\\b',
119
+ '\\bchrome\\b',
120
+ '\\bplaywright\\b',
121
+ '\\bselenium\\b',
122
+ '\\bpuppeteer\\b',
123
+ 'image[- ]?(?:gen|generation)',
124
+ '\\bimagegen\\b',
125
+ 'gpt[- ]?image',
126
+ '\\bscreenshot(?:ting)?\\b',
127
+ 'long[- ]?context',
128
+ 'large[- ]?(?:file|document|codebase|repository|context)',
129
+ '(?:repository|repo)[- ]?wide',
130
+ 'read[- ]?heavy',
131
+ 'supporting[- ]?documents?',
132
+ 'multi[- ]?(?:document|file) scan',
133
+ '\\bexplor(?:e|er|ation)\\b',
134
+ '\\bscan(?:ning)?\\b',
135
+ '\\bextract(?:ion)?\\b',
136
+ '\\binventory\\b',
137
+ 'extensive[- ]?logs?',
138
+ '컴퓨터\\s*유즈',
139
+ '브라우저',
140
+ '크롬',
141
+ '이미지\\s*생성',
142
+ '스크린샷',
143
+ '롱\\s*컨텍스트',
144
+ '긴\\s*컨텍스트',
145
+ '장문',
146
+ '대규모',
147
+ '저장소\\s*전체',
148
+ '여러\\s*(?:문서|파일)',
149
+ '탐색',
150
+ '스캔',
151
+ '추출',
152
+ '인벤토리'
153
+ ].join('|'), 'i');
154
+ const SIMPLE_MECHANICAL_TASK_RE = new RegExp([
155
+ '\\bone[- ]?line\\b',
156
+ '\\bsingle[- ]?file\\b',
157
+ '\\btiny\\b',
158
+ '\\btrivial\\b',
159
+ '\\bmechanical\\b',
160
+ '\\brepeatable\\b',
161
+ '\\bexact(?:ly)?\\b[^\\n]{0,36}\\b(?:rename|copy|replace|format|fixture|change)\\b',
162
+ '\\b(?:typo|spacing|label|copy|format)[-_ ]?(?:only|fix|change)?\\b',
163
+ '\\brename\\b[^\\n]{0,24}\\b(?:symbol|file|label|key|field)\\b',
164
+ '한\\s*줄',
165
+ '단일\\s*파일',
166
+ '아주\\s*(?:작은|단순한?)',
167
+ '극단적으로\\s*단순',
168
+ '기계적',
169
+ '반복적',
170
+ '정확한?[^\\n]{0,24}(?:이름\\s*변경|복사|치환|포맷|수정)',
171
+ '오타',
172
+ '문구',
173
+ '라벨',
174
+ '간격',
175
+ '이름\\s*변경'
176
+ ].join('|'), 'i');
177
+ const IMPLEMENTATION_TASK_RE = new RegExp([
178
+ '\\bimplement(?:ation|er|ing)?\\b',
179
+ '\\bbuild(?:ing)?\\b',
180
+ '\\bcreate\\b',
181
+ '\\badd\\b[^\\n]{0,40}\\b(?:feature|endpoint|component|modal|button|handler|parser)\\b',
182
+ '\\bmodify\\b',
183
+ '\\bfix\\b',
184
+ '\\bcod(?:e|ing)\\b',
185
+ '\\bui\\b',
186
+ '\\bux\\b',
187
+ '\\bgui\\b',
188
+ 'frontend',
189
+ 'backend',
190
+ '\\blogic\\b',
191
+ '\\bparser\\b',
192
+ '\\bcomponent\\b',
193
+ '\\bmodal\\b',
194
+ '\\bappkit\\b',
195
+ '\\bswift\\b',
196
+ '\\bfeature\\b',
197
+ '구현',
198
+ '기능\\s*추가',
199
+ '코딩',
200
+ '코드',
201
+ '로직',
202
+ '화면',
203
+ '유아이',
204
+ '유엑스',
205
+ '프론트엔드',
206
+ '백엔드',
207
+ '컴포넌트',
208
+ '모달',
209
+ '버튼',
210
+ '앱킷',
211
+ '스위프트'
89
212
  ].join('|'), 'i');
213
+ export function subagentModelProfile(policy) {
214
+ return SUBAGENT_MODEL_POLICIES[policy];
215
+ }
90
216
  export function decideSubagentModel(input = {}) {
91
217
  const text = [
92
218
  input.title,
@@ -94,25 +220,40 @@ export function decideSubagentModel(input = {}) {
94
220
  input.role,
95
221
  input.expectedOutput
96
222
  ].filter(Boolean).join(' ');
97
- const useSol = input.requiresJudgment === true
98
- || SOL_TASK_RE.test(text)
99
- || !CLEAR_BOUNDED_TASK_RE.test(text);
100
- if (useSol) {
101
- return {
102
- kind: 'expert',
103
- model: THINKING_SUBAGENT_MODEL,
104
- modelReasoningEffort: SUBAGENT_EFFORT,
105
- reason: 'reasoning_sensitive_task'
106
- };
107
- }
108
- return {
109
- kind: 'worker',
110
- model: DEFAULT_SUBAGENT_MODEL,
111
- modelReasoningEffort: SUBAGENT_EFFORT,
112
- reason: 'clear_bounded_repeatable_task'
113
- };
223
+ const judgment = input.requiresJudgment === true
224
+ || input.taskClass === 'judgment'
225
+ || JUDGMENT_TASK_RE.test(text);
226
+ if (judgment)
227
+ return decision('sol_max_judgment');
228
+ const contextOrTools = input.longContext === true
229
+ || input.toolHeavy === true
230
+ || input.taskClass === 'context_tools'
231
+ || input.contextMode === 'long'
232
+ || (input.toolSurface !== undefined && input.toolSurface !== 'none')
233
+ || input.scopeSize === 'large'
234
+ || CONTEXT_TOOL_TASK_RE.test(text);
235
+ if (contextOrTools)
236
+ return decision('terra_medium_context_tools');
237
+ const simpleMechanical = input.simpleMechanical === true
238
+ || input.taskClass === 'mechanical'
239
+ || input.scopeSize === 'tiny'
240
+ || SIMPLE_MECHANICAL_TASK_RE.test(text);
241
+ if (simpleMechanical)
242
+ return decision('luna_max_mechanical');
243
+ const implementation = input.taskClass === 'implementation' || IMPLEMENTATION_TASK_RE.test(text);
244
+ if (implementation)
245
+ return decision('sol_high_implementation');
246
+ // Ambiguous work defaults to the trust-first lane. Sol High is reserved for
247
+ // clearly identified implementation, not for underspecified judgment.
248
+ return decision('sol_max_judgment');
114
249
  }
115
250
  export function isReasoningSensitiveSubagentTask(input = {}) {
116
- return decideSubagentModel(input).kind === 'expert';
251
+ return decideSubagentModel(input).policy === 'sol_max_judgment';
252
+ }
253
+ function decision(policy) {
254
+ return {
255
+ ...subagentModelProfile(policy),
256
+ reason: policy
257
+ };
117
258
  }
118
259
  //# sourceMappingURL=model-policy.js.map
@@ -1,6 +1,5 @@
1
1
  import { NARUTO_PARENT_EFFORT, NARUTO_PARENT_MODEL } from './model-policy.js';
2
- import { DEFAULT_NARUTO_REQUESTED_SUBAGENTS } from './thread-budget.js';
3
- import { MAX_AUTOMATIC_REVIEWER_COUNT, MAX_AUTOMATIC_SUBAGENT_COUNT, officialSubagentRolePlan } from './agent-catalog.js';
2
+ import { DEFAULT_AUTOMATIC_SUBAGENT_COUNT, MAX_AUTOMATIC_REVIEWER_COUNT, MAX_AUTOMATIC_SUBAGENT_COUNT, officialSubagentRolePlan } from './agent-catalog.js';
4
3
  export const NARUTO_HELP_SCHEMA = 'sks.naruto-subagent-workflow.v1';
5
4
  export function buildNarutoHelpResult() {
6
5
  return {
@@ -16,13 +15,20 @@ export function buildNarutoHelpResult() {
16
15
  'sks naruto proof [latest|M-...] [--json]'
17
16
  ],
18
17
  commands: ['help', 'status', 'subagents', 'proof', 'run'],
19
- default_requested_subagents: DEFAULT_NARUTO_REQUESTED_SUBAGENTS,
20
- scaling_policy: 'one_safe_direct_child_by_default_parent_owned_risk_based_expansion',
18
+ default_requested_subagents: DEFAULT_AUTOMATIC_SUBAGENT_COUNT,
19
+ scaling_policy: 'two_independent_children_for_non_trivial_work_parent_owned_risk_based_expansion',
21
20
  automatic_subagent_ceiling: MAX_AUTOMATIC_SUBAGENT_COUNT,
22
21
  automatic_reviewer_ceiling: MAX_AUTOMATIC_REVIEWER_COUNT,
23
22
  critical_multi_domain_reviewer_ceiling: MAX_AUTOMATIC_SUBAGENT_COUNT,
24
23
  max_depth: 1,
25
24
  triwiki_context: 'bounded_attention_use_first_with_on_demand_hydration',
25
+ model_routing_policy: {
26
+ luna_max: 'tiny_short_context_mechanical_only',
27
+ sol_high: 'ordinary_ui_logic_backend_and_native_implementation',
28
+ sol_max: 'review_debug_planning_architecture_security_database_research_release_and_judgment',
29
+ terra_medium: 'long_context_computer_use_browser_chrome_and_image_generation_execution',
30
+ mixed_slice_rule: 'split_execution_from_judgment_when_possible_otherwise_sol_max_wins'
31
+ },
26
32
  completion_evidence: {
27
33
  lifecycle_events: ['SubagentStart', 'SubagentStop'],
28
34
  stop_is_success_evidence: false,
@@ -34,6 +40,7 @@ export function buildNarutoHelpResult() {
34
40
  },
35
41
  legacy_process_runtime_available: false,
36
42
  parent: { model: NARUTO_PARENT_MODEL, model_reasoning_effort: NARUTO_PARENT_EFFORT },
43
+ agent_catalog_mode: 'full_catalog_only_on_explicit_help',
37
44
  agents: officialSubagentRolePlan()
38
45
  };
39
46
  }
@@ -6,7 +6,7 @@ import { chooseVerificationBudget } from '../runtime/verification-budget.js';
6
6
  import { buildOfficialSubagentPrompt } from './official-subagent-prompt.js';
7
7
  import { readOfficialSubagentConfig } from './official-subagent-config.js';
8
8
  import { NARUTO_PARENT_EFFORT, NARUTO_PARENT_MODEL } from './model-policy.js';
9
- import { officialSubagentFanoutPolicy, officialSubagentRolePlan, recommendOfficialSubagentRoles } from './agent-catalog.js';
9
+ import { MAX_ON_DEMAND_SUBAGENT_ROLE_COUNT, officialSubagentFanoutPolicy, officialSubagentOnDemandRolePlan, officialSubagentRoleCatalog, recommendOfficialSubagentRoles } from './agent-catalog.js';
10
10
  import { resolveSubagentThreadBudget } from './thread-budget.js';
11
11
  import { readBoundedTriwikiAttention } from './triwiki-attention.js';
12
12
  import { SUBAGENT_EVIDENCE_FILENAME, SUBAGENT_EVENT_LOG_FILENAME, SUBAGENT_PARENT_SUMMARY_FILENAME, normalizeSubagentParentSummary, writeSubagentEvidence } from './subagent-evidence.js';
@@ -24,11 +24,21 @@ export async function prepareOfficialSubagentMission(input) {
24
24
  requiresWrite: input.readOnly !== true
25
25
  });
26
26
  const officialConfig = await readOfficialSubagentConfig(input.root);
27
- const triwikiAttention = await readBoundedTriwikiAttention(input.root);
28
- const requestedExplicit = input.requestedSubagentsExplicit ?? input.requestedSubagents !== undefined;
27
+ const triwikiAttention = await readBoundedTriwikiAttention(input.root, triwikiAttentionLimit(taskProfile), goal);
28
+ const operatorRequested = input.requestedSubagentsExplicit ?? input.requestedSubagents !== undefined;
29
+ const routeContract = mode === 'generic' && !operatorRequested
30
+ ? routeOwnedSubagentContract(input.route)
31
+ : null;
32
+ const requestedSubagents = input.requestedSubagents ?? routeContract?.count;
33
+ const requestedSource = operatorRequested
34
+ ? 'operator'
35
+ : routeContract
36
+ ? 'route_contract'
37
+ : 'automatic';
29
38
  const selectedFanoutPolicy = officialSubagentFanoutPolicy({
30
- ...(input.requestedSubagents === undefined ? {} : { requestedSubagents: input.requestedSubagents }),
31
- requestedExplicit,
39
+ ...(requestedSubagents === undefined ? {} : { requestedSubagents }),
40
+ requestedExplicit: requestedSource !== 'automatic',
41
+ requestedSource,
32
42
  taskProfile,
33
43
  suggestedRoles: suggestedAgents,
34
44
  goal
@@ -53,12 +63,15 @@ export async function prepareOfficialSubagentMission(input) {
53
63
  goal: delegationGoal,
54
64
  slices: [],
55
65
  requestedSubagents: budget.requestedSubagents,
56
- requestedSubagentsExplicit: requestedExplicit,
66
+ requestedSubagentsExplicit: requestedSource === 'operator',
67
+ requestedSubagentsSource: requestedSource,
57
68
  maxThreads: budget.maxThreads,
58
69
  decompositionStatus: 'parent_required',
59
70
  triwikiAttention,
60
71
  recommendedAgents: suggestedAgents
61
72
  });
73
+ const selectedAgentPlan = officialSubagentOnDemandRolePlan(suggestedAgents);
74
+ const agentCatalog = onDemandAgentCatalogMetadata(selectedAgentPlan);
62
75
  const configBlockers = officialConfig.blockers.map((blocker) => `official_subagent_config:${blocker}`);
63
76
  const plan = {
64
77
  schema: 'sks.subagent-plan.v1',
@@ -73,7 +86,9 @@ export async function prepareOfficialSubagentMission(input) {
73
86
  decomposition_status: 'parent_required',
74
87
  delegation_prompt: delegationPrompt,
75
88
  requested_subagents: budget.requestedSubagents,
76
- requested_subagents_explicit: requestedExplicit,
89
+ requested_subagents_explicit: requestedSource === 'operator',
90
+ requested_subagents_source: requestedSource,
91
+ route_owned_count_contract: routeContract,
77
92
  max_threads: budget.maxThreads,
78
93
  first_wave: budget.firstWave,
79
94
  wave_count: budget.waveCount,
@@ -92,7 +107,8 @@ export async function prepareOfficialSubagentMission(input) {
92
107
  model: NARUTO_PARENT_MODEL,
93
108
  model_reasoning_effort: NARUTO_PARENT_EFFORT
94
109
  },
95
- agents: officialSubagentRolePlan(),
110
+ agent_catalog: agentCatalog,
111
+ agents: selectedAgentPlan,
96
112
  verification_budget: verification,
97
113
  verification_checks: [],
98
114
  verification: { budget: verification },
@@ -134,6 +150,7 @@ export async function prepareOfficialSubagentMission(input) {
134
150
  ok: false,
135
151
  blockers,
136
152
  sessionKey: input.sessionScope || null,
153
+ suggestedAgents,
137
154
  observedParentModel,
138
155
  parentModelMatch
139
156
  }));
@@ -165,8 +182,25 @@ export async function prepareOfficialSubagentMission(input) {
165
182
  parentModelMatch
166
183
  };
167
184
  }
185
+ function triwikiAttentionLimit(taskProfile) {
186
+ return ['parallel-read', 'parallel-write', 'high-risk'].includes(taskProfile) ? 6 : 4;
187
+ }
188
+ function routeOwnedSubagentContract(route) {
189
+ const normalized = String(route || '').replace(/^\$/, '').trim().toLowerCase();
190
+ if (normalized === 'research') {
191
+ return { count: 3, reason: 'research_exact_three_independent_reviewers' };
192
+ }
193
+ if (normalized === 'autoresearch') {
194
+ return { count: 3, reason: 'autoresearch_exact_three_independent_reviewers' };
195
+ }
196
+ return null;
197
+ }
168
198
  export function buildNarutoSummary(input) {
169
199
  const parentSummary = normalizeSubagentParentSummary(input.parentSummary);
200
+ const suggestedAgents = Array.isArray(input.suggestedAgents)
201
+ ? uniqueStrings(input.suggestedAgents)
202
+ : [];
203
+ const selectedAgentPlan = officialSubagentOnDemandRolePlan(suggestedAgents);
170
204
  return {
171
205
  schema: NARUTO_RESULT_SCHEMA,
172
206
  ok: input.ok === true,
@@ -188,7 +222,8 @@ export function buildNarutoSummary(input) {
188
222
  started_subagents: Number(input.evidence?.started_threads || 0),
189
223
  completed_subagents: Number(input.evidence?.completed_threads || 0),
190
224
  failed_subagents: Number(input.evidence?.failed_threads || 0),
191
- agents: officialSubagentRolePlan(),
225
+ agent_catalog: onDemandAgentCatalogMetadata(selectedAgentPlan),
226
+ agents: selectedAgentPlan,
192
227
  verification: {
193
228
  budget: input.verification,
194
229
  checks: []
@@ -203,6 +238,17 @@ export function buildNarutoSummary(input) {
203
238
  updated_at: nowIso()
204
239
  };
205
240
  }
241
+ function onDemandAgentCatalogMetadata(selectedAgentPlan) {
242
+ const selectedAgents = Object.keys(selectedAgentPlan);
243
+ return {
244
+ mode: 'on_demand',
245
+ total_available: officialSubagentRoleCatalog().length,
246
+ selected_count: selectedAgents.length,
247
+ selected_agents: selectedAgents,
248
+ max_injected: MAX_ON_DEMAND_SUBAGENT_ROLE_COUNT,
249
+ full_catalog_injected: false
250
+ };
251
+ }
206
252
  export async function writeNarutoGate(dir, input) {
207
253
  await writeJsonAtomic(path.join(dir, NARUTO_GATE_FILENAME), buildNarutoGateResult(input));
208
254
  }
@@ -1,30 +1,45 @@
1
1
  import { HARD_NARUTO_MAX_THREADS } from './thread-budget.js';
2
- import { MAX_AUTOMATIC_REVIEWER_COUNT, MAX_AUTOMATIC_SUBAGENT_COUNT, officialSubagentRoleCatalog, selectOfficialSubagentRole } from './agent-catalog.js';
2
+ import { MAX_AUTOMATIC_REVIEWER_COUNT, MAX_AUTOMATIC_SUBAGENT_COUNT, officialSubagentOnDemandRoleCatalog, officialSubagentRoleCatalog, selectOfficialSubagentRole } from './agent-catalog.js';
3
3
  export function buildOfficialSubagentPrompt(input) {
4
4
  const maxThreads = clampThreads(input.maxThreads);
5
5
  const requestedSubagents = normalizeRequestedSubagents(input.requestedSubagents, input.slices.length);
6
6
  const waveCount = requestedSubagents === 0 ? 0 : Math.ceil(requestedSubagents / maxThreads);
7
7
  const parentDecompositionRequired = input.decompositionStatus === 'parent_required';
8
- const requestedPolicy = input.requestedSubagentsExplicit === true
8
+ const requestedSource = input.requestedSubagentsSource === 'route_contract'
9
+ ? 'route_contract'
10
+ : input.requestedSubagentsExplicit === true || input.requestedSubagentsSource === 'operator'
11
+ ? 'operator'
12
+ : 'automatic';
13
+ const requestedPolicy = requestedSource === 'operator'
9
14
  ? `${requestedSubagents} (explicit operator request)`
10
- : `${requestedSubagents} (risk-based automatic count; one child is the safe default; keep the plan and evidence count exact)`;
15
+ : requestedSource === 'route_contract'
16
+ ? `${requestedSubagents} (route-owned exact orchestration contract)`
17
+ : `${requestedSubagents} (risk-based automatic count; two independent children are the non-trivial default; keep the plan and evidence count exact)`;
11
18
  const triwiki = renderBoundedTriwikiAttention(input.triwikiAttention);
12
- const catalog = renderAgentCatalog(input.recommendedAgents);
13
- const rows = input.slices.map((slice, index) => {
14
- const agentName = slice.agent || selectOfficialSubagentRole({
19
+ const resolvedSlices = input.slices.map((slice) => ({
20
+ slice,
21
+ agentName: slice.agent || selectOfficialSubagentRole({
15
22
  title: slice.title,
16
23
  description: slice.description,
17
24
  role: slice.kind,
18
25
  ...(slice.paths === undefined ? {} : { paths: slice.paths }),
19
26
  readOnly: slice.readOnly === true,
20
27
  requiresWrite: slice.readOnly !== true
21
- });
28
+ })
29
+ }));
30
+ const catalog = renderAgentCatalog([
31
+ ...resolvedSlices.map((row) => row.agentName),
32
+ ...(input.recommendedAgents || [])
33
+ ]);
34
+ const rows = resolvedSlices.map(({ slice, agentName }, index) => {
22
35
  const mode = slice.readOnly ? 'read-only' : 'use the parent permission mode';
23
36
  const paths = (slice.paths || []).map((entry) => String(entry).trim()).filter(Boolean);
37
+ const role = officialSubagentOnDemandRoleCatalog([agentName])[0];
24
38
  return [
25
39
  `${index + 1}. [${slice.id}] use custom agent \`${agentName}\``,
26
40
  ` title: ${slice.title}`,
27
41
  ` task: ${slice.description}`,
42
+ ` model policy: ${role ? `${role.model_policy} (${role.model}/${role.model_reasoning_effort})` : 'resolve from installed custom agent'}`,
28
43
  ` mode: ${mode}`,
29
44
  ` paths: ${paths.join(', ') || 'assigned by parent'}`
30
45
  ].join('\n');
@@ -40,10 +55,14 @@ Parent agent:
40
55
  Subagent rules:
41
56
  - use only Codex official subagent threads; do not launch shell workers, a custom scheduler, a worker pool, or model fanout
42
57
  - select the narrowest matching project custom agent by its description; the custom agent name is the spawn type
43
- - use \`worker\` with gpt-5.6-luna and max reasoning only for clear, bounded, repeatable work
44
- - use \`expert\` with gpt-5.6-sol and max reasoning only as the read-only judgment fallback
45
- - automatic fan-out is selected before execution: one by default, two only for explicit parallel work or independent risk domains,
46
- and at most ${MAX_AUTOMATIC_SUBAGENT_COUNT} for critical multi-domain risk; never spawn beyond the requested count
58
+ - use \`worker\` with gpt-5.6-luna and max reasoning only for tiny, short-context, mechanical work with no exploration or judgment
59
+ - use gpt-5.6-sol with high reasoning for ordinary UI, logic, backend, and native implementation
60
+ - use gpt-5.6-sol with max reasoning for review, debugging, planning, architecture, security, database, research, release, ambiguity, and every judgment-bearing slice
61
+ - use gpt-5.6-terra with medium reasoning for long-context analysis and direct Computer Use, Browser/Chrome, or image-generation execution
62
+ - split mixed work when possible: Terra gathers or operates, Sol High implements, and Sol Max judges; when one slice cannot be split safely, judgment takes priority
63
+ - never assign Luna to long-context, exploration, review, debugging, planning, or tool-heavy work
64
+ - automatic fan-out is selected before execution: two independent children for non-trivial work,
65
+ and at most ${MAX_AUTOMATIC_SUBAGENT_COUNT} only for critical multi-domain risk; trivial Answer/DFix work is filtered before this route
47
66
  - automatic reviewer-only fan-out is capped at ${MAX_AUTOMATIC_REVIEWER_COUNT} for ordinary work; critical multi-domain review may use the overall cap of ${MAX_AUTOMATIC_SUBAGENT_COUNT}
48
67
  - requested subagents: ${requestedPolicy}
49
68
  - max open agent threads: ${maxThreads}
@@ -57,9 +76,11 @@ Subagent rules:
57
76
  ${parentDecompositionRequired ? `- decomposition status: parent_required
58
77
  - before spawning, decompose the goal into independent, non-overlapping slices
59
78
  - do not invent write scopes merely to reach the requested count
60
- ${input.requestedSubagentsExplicit === true
79
+ ${requestedSource === 'operator'
61
80
  ? '- the explicit operator count is authoritative; if it cannot be defended safely, block and report instead of silently changing it'
62
- : '- if fewer defensible slices exist, reduce the delegation plan and requested count before execution; never increase automatically beyond the selected count'}` : '- decomposition status: ready'}
81
+ : requestedSource === 'route_contract'
82
+ ? '- the route-owned exact count is authoritative; preserve it and follow the route-specific orchestration contract'
83
+ : '- if fewer defensible slices exist, reduce the delegation plan and requested count before execution; never increase automatically beyond the selected count'}` : '- decomposition status: ready'}
63
84
 
64
85
  Central TriWiki context:
65
86
  ${triwiki}
@@ -111,16 +132,17 @@ function renderBoundedTriwikiAttention(value) {
111
132
  `- bounded anchors: ${JSON.stringify(anchors)}`
112
133
  ].join('\n');
113
134
  }
114
- function renderAgentCatalog(recommended) {
115
- const preferred = new Set((recommended || []).map(String));
116
- const included = new Set(['worker', 'implementation_specialist', 'expert', ...preferred]);
117
- return officialSubagentRoleCatalog()
118
- .filter((role) => included.has(role.name))
119
- .map((role) => {
120
- const marker = preferred.has(role.name) ? ' [suggested for this goal]' : '';
121
- return `- \`${role.name}\`${marker}: ${role.model}/${role.model_reasoning_effort}, ${role.sandbox_mode}; ${role.description}`;
122
- })
123
- .join('\n');
135
+ function renderAgentCatalog(requested) {
136
+ const names = [...new Set(requested.map(String).map((name) => name.trim()).filter(Boolean))];
137
+ const selected = officialSubagentOnDemandRoleCatalog(names.length ? names : ['expert']);
138
+ const preferred = new Set(names);
139
+ return [
140
+ `- metadata mode: on-demand (${selected.length}/${officialSubagentRoleCatalog().length} roles included; full catalog is not injected)`,
141
+ ...selected.map((role) => {
142
+ const marker = preferred.has(role.name) ? ' [suggested for this goal]' : '';
143
+ return `- \`${role.name}\`${marker}: ${role.model_policy}, ${role.model}/${role.model_reasoning_effort}, ${role.sandbox_mode}; ${role.description}`;
144
+ })
145
+ ].join('\n');
124
146
  }
125
147
  function clampThreads(value) {
126
148
  const parsed = Number(value);
@@ -1,6 +1,6 @@
1
1
  export const DEFAULT_NARUTO_MAX_THREADS = 12;
2
2
  export const HARD_NARUTO_MAX_THREADS = 32;
3
- export const DEFAULT_NARUTO_REQUESTED_SUBAGENTS = 1;
3
+ export const DEFAULT_NARUTO_REQUESTED_SUBAGENTS = 2;
4
4
  export function resolveSubagentThreadBudget(input = {}) {
5
5
  const requested = clamp(input.requested ?? input.independentSliceCount ?? DEFAULT_NARUTO_REQUESTED_SUBAGENTS, 1, HARD_NARUTO_MAX_THREADS);
6
6
  const configured = clamp(input.configuredMaxThreads ?? DEFAULT_NARUTO_MAX_THREADS, 1, HARD_NARUTO_MAX_THREADS);