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
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
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.
|
|
17
17
|
<!-- END SKS SEARCH VISIBILITY MARKETING -->
|
|
18
18
|
|
|
19
|
-
Current package: **SKS 10.3.
|
|
19
|
+
Current package: **SKS 10.3.4**. Install the latest stable release from npm.
|
|
20
20
|
|
|
21
21
|
[Quick start](#install-in-one-command) · [Commands](#everyday-commands) · [SKS Center](#sks-center-macos) · [Documentation](#documentation) · [Changelog](CHANGELOG.md)
|
|
22
22
|
|
|
@@ -39,7 +39,7 @@ sks bootstrap --yes
|
|
|
39
39
|
|
|
40
40
|
| Capability | What you get |
|
|
41
41
|
| --- | --- |
|
|
42
|
-
| Focused execution |
|
|
42
|
+
| Focused execution | Answers and tiny edits stay lightweight; implementation work runs through official Codex subagents while the parent orchestrates and integrates. |
|
|
43
43
|
| Project context | TriWiki indexes repository code and supplies bounded context that can be checked against source. |
|
|
44
44
|
| Verification | Tests, diagnostics, and release evidence support completion claims. Security and data-integrity checks stay in place. |
|
|
45
45
|
| Native controls | SKS Center brings connections, updates, MCP servers, and diagnostics together on macOS. |
|
|
@@ -138,16 +138,26 @@ configuration, transport checks, and recovery commands.
|
|
|
138
138
|
|
|
139
139
|
## Naruto workflow
|
|
140
140
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
141
|
+
The parent orchestrates: it decomposes the task, spawns a child for each
|
|
142
|
+
disjoint slice, waits, and owns integration and final verification. It does not
|
|
143
|
+
implement slices itself. The SKS PreToolUse hook denies parent source edits
|
|
144
|
+
until the first child starts and while children are still running. Children
|
|
145
|
+
receive bounded tasks and do not spawn children.
|
|
146
|
+
|
|
147
|
+
No model family is pinned. Every child runs the newest model of the tier its
|
|
148
|
+
work needs, read from the Codex models cache, so a new model family is used as
|
|
149
|
+
soon as Codex lists it:
|
|
150
|
+
|
|
151
|
+
| Work | Tier | Effort | Today |
|
|
152
|
+
| --- | --- | --- | --- |
|
|
153
|
+
| Tiny mechanical tasks | fast | low | `gpt-6-luna` |
|
|
154
|
+
| Instructed implementation | balanced | low | `gpt-6-sol` |
|
|
155
|
+
| Exploration, large-context reads, and direct tool operation | context | medium | `gpt-6-sol` |
|
|
156
|
+
| Planning, review, debugging, and focused judgment | deep | max | `gpt-6-astra` |
|
|
157
|
+
|
|
158
|
+
With Jev mode on, Jev picks the tier for every spawn, every gated parent edit,
|
|
159
|
+
the plan, and the context, so the parent spends no time on those choices. A
|
|
160
|
+
stored role-model preference on a current model wins in both modes.
|
|
151
161
|
|
|
152
162
|
An active Codex task keeps the user's selected main model, effort, and service
|
|
153
163
|
tier. Codex native `/goal` remains the persisted goal owner. Parallelism depends
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
{
|
|
5
5
|
"canonical_name": "sks",
|
|
6
6
|
"trusted_sha256": [
|
|
7
|
+
"8aa34586ba331f0447668c2ce863fe6c9ec4f881828ebed26ca5ecc754d66840",
|
|
7
8
|
"d0288d27c8bc1c89decb258c6805ed4286cd0d65371ef0eec453be233b39a2a1",
|
|
8
9
|
"905fc5472d36f0780b56f0e09404f42ea20293b1a4615d2050b7db3043231dbd",
|
|
9
10
|
"6d90142f3617a8ae92d9af8cb2fc18ab58a7d0070868504e1fe89e0effc305a5",
|
|
@@ -324,13 +325,14 @@
|
|
|
324
325
|
{
|
|
325
326
|
"canonical_name": "sks-naruto",
|
|
326
327
|
"trusted_sha256": [
|
|
328
|
+
"b2eb207b2773ab1995ee6e113ba6f3b64de43389e76aef4a0f06d4845ff86223",
|
|
329
|
+
"65e19a5d16f486b13541048c239e828f8039bfc6e38bafd7949d9c936fe4b5b2",
|
|
327
330
|
"da43debf97f0b4a1583d8202a7306aca97ac97865accf9bca8f41d75c84a1cb9",
|
|
328
331
|
"b981bea4183376c6c0204941ad6c2e78782e2cdea4f8d25cc85630c609faaf95",
|
|
329
332
|
"5df9c9678eb68fd79ceb998ae7a52dd54dc92336ad5169e8a01acca89f76bd60",
|
|
330
333
|
"a1219f6dbad9c8c185953125ef0b8dccf081aa01efb8e8acaa543c6214ac049c",
|
|
331
334
|
"d0d5968e78ac25e1d3e588735de1ddbcf149cfb3165edebca5dec1ed7c7a5c46",
|
|
332
335
|
"34222c72e4e1ead3584907f4d905444b7de820283ee7348018f110d5e6b04461",
|
|
333
|
-
"f8e808622e222b4ad7dcb2907e5f06a890db83139ae443ec6c8c33781dfa3e37",
|
|
334
336
|
"49b5e3dcc1b537b49c2cf09e5b875cb16acb05c622b22798e265e64dacd8ac90"
|
|
335
337
|
]
|
|
336
338
|
},
|
|
@@ -345,6 +347,7 @@
|
|
|
345
347
|
{
|
|
346
348
|
"canonical_name": "sks-pipeline-runner",
|
|
347
349
|
"trusted_sha256": [
|
|
350
|
+
"6f38d3387e4ab020291afe7f74713b135543e52707f5bfbffa6b3584542f45a5",
|
|
348
351
|
"7749638f1e8393f3beb1f7b0c37195dd0463abdb6fd7da5d31b3d816ee91fa37",
|
|
349
352
|
"fd06dfb27be41e5ffec3e3ec8c5362289f5919cf2afa9da5ab6b97e7baa4a0ac",
|
|
350
353
|
"66e105773b8e042eec5d5679b97b7cb305cad8d45671034907fbfa728fc4d68a",
|
|
@@ -372,6 +375,8 @@
|
|
|
372
375
|
{
|
|
373
376
|
"canonical_name": "sks-prompt-pipeline",
|
|
374
377
|
"trusted_sha256": [
|
|
378
|
+
"f82a103e7962e416961add515d7b3e65df89848dec1fbb9319bd49d7f1dac982",
|
|
379
|
+
"93fd938dccd65d9b4d1dd140b60e1baedc627e7fcc54f9afd95eb2e44efd1143",
|
|
375
380
|
"033bb114ec1fac3f8c312f698b1861ed4c2ce6f65c06f4f1a872c4b101b74b92",
|
|
376
381
|
"e7c8ef56d38fdc4879c983f7d6706f26fc1cf0a60c508836bd628e815700bcc3",
|
|
377
382
|
"480c8afffafa9d52c483a963b4b8fafdffc0a47cd6d2efd3fb6d7924c11832c5",
|
|
@@ -388,6 +393,7 @@
|
|
|
388
393
|
{
|
|
389
394
|
"canonical_name": "sks-reasoning-router",
|
|
390
395
|
"trusted_sha256": [
|
|
396
|
+
"41e14e8d5b93d743096664869ee68c41c86e911b0bbc165bdcd10e4a45faa4b6",
|
|
391
397
|
"03d68436e568b3cee2bffc6ca58819019ca406f7b648ef4704e3465c7c83c114",
|
|
392
398
|
"647f730520a1c7dc89c2642f5b8502624c65a861ae41aecf1fb137db40fd0611",
|
|
393
399
|
"da0ddb58adc68ddeebdb9c45bf111c26eaa64d3e9dffe1dd39d7f30b943b8b88",
|
package/dist/commands/doctor.js
CHANGED
|
@@ -127,7 +127,8 @@ export async function run(_command, args = [], deps = {}) {
|
|
|
127
127
|
const doctorFix = flag(args, '--fix');
|
|
128
128
|
const homeDir = path.resolve(deps.home || process.env.HOME || os.homedir());
|
|
129
129
|
const globalOnly = doctorGlobalOnlySelection({ args, doctorFix, root, home: homeDir }).global_only;
|
|
130
|
-
|
|
130
|
+
const doctorProfile = doctorProfileFromArgs(args, doctorFix);
|
|
131
|
+
if (doctorFix && doctorProfile !== 'migration') {
|
|
131
132
|
const conflictScan = await scanHarnessConflicts(root);
|
|
132
133
|
if (conflictScan.hard_block) {
|
|
133
134
|
const blocked = {
|
|
@@ -151,7 +152,6 @@ export async function run(_command, args = [], deps = {}) {
|
|
|
151
152
|
return blocked;
|
|
152
153
|
}
|
|
153
154
|
}
|
|
154
|
-
const doctorProfile = doctorProfileFromArgs(args, doctorFix);
|
|
155
155
|
if (!flag(args, '--json')) {
|
|
156
156
|
cliUi.banner('doctor');
|
|
157
157
|
cliUi.step(doctorFix ? 'repairing and validating' : 'validating');
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "sks.skills-manifest.v1",
|
|
3
|
-
"package_version": "10.3.
|
|
3
|
+
"package_version": "10.3.4",
|
|
4
4
|
"skills": [
|
|
5
5
|
{
|
|
6
6
|
"canonical_name": "sks",
|
|
7
7
|
"type": "official",
|
|
8
|
-
"content_sha256": "
|
|
8
|
+
"content_sha256": "8aa34586ba331f0447668c2ce863fe6c9ec4f881828ebed26ca5ecc754d66840",
|
|
9
9
|
"hash_history": [
|
|
10
|
+
"d0288d27c8bc1c89decb258c6805ed4286cd0d65371ef0eec453be233b39a2a1",
|
|
10
11
|
"905fc5472d36f0780b56f0e09404f42ea20293b1a4615d2050b7db3043231dbd",
|
|
11
12
|
"6d90142f3617a8ae92d9af8cb2fc18ab58a7d0070868504e1fe89e0effc305a5",
|
|
12
13
|
"5b17aeb1484e9e2b3791bff6dcb175f30857f47e71b36eb0340e575385e8e82b"
|
|
@@ -393,14 +394,15 @@
|
|
|
393
394
|
{
|
|
394
395
|
"canonical_name": "sks-naruto",
|
|
395
396
|
"type": "core",
|
|
396
|
-
"content_sha256": "
|
|
397
|
+
"content_sha256": "b2eb207b2773ab1995ee6e113ba6f3b64de43389e76aef4a0f06d4845ff86223",
|
|
397
398
|
"hash_history": [
|
|
399
|
+
"65e19a5d16f486b13541048c239e828f8039bfc6e38bafd7949d9c936fe4b5b2",
|
|
400
|
+
"da43debf97f0b4a1583d8202a7306aca97ac97865accf9bca8f41d75c84a1cb9",
|
|
398
401
|
"b981bea4183376c6c0204941ad6c2e78782e2cdea4f8d25cc85630c609faaf95",
|
|
399
402
|
"5df9c9678eb68fd79ceb998ae7a52dd54dc92336ad5169e8a01acca89f76bd60",
|
|
400
403
|
"a1219f6dbad9c8c185953125ef0b8dccf081aa01efb8e8acaa543c6214ac049c",
|
|
401
404
|
"d0d5968e78ac25e1d3e588735de1ddbcf149cfb3165edebca5dec1ed7c7a5c46",
|
|
402
405
|
"34222c72e4e1ead3584907f4d905444b7de820283ee7348018f110d5e6b04461",
|
|
403
|
-
"f8e808622e222b4ad7dcb2907e5f06a890db83139ae443ec6c8c33781dfa3e37",
|
|
404
406
|
"49b5e3dcc1b537b49c2cf09e5b875cb16acb05c622b22798e265e64dacd8ac90"
|
|
405
407
|
],
|
|
406
408
|
"deprecated_aliases": []
|
|
@@ -418,8 +420,9 @@
|
|
|
418
420
|
{
|
|
419
421
|
"canonical_name": "sks-pipeline-runner",
|
|
420
422
|
"type": "official",
|
|
421
|
-
"content_sha256": "
|
|
423
|
+
"content_sha256": "6f38d3387e4ab020291afe7f74713b135543e52707f5bfbffa6b3584542f45a5",
|
|
422
424
|
"hash_history": [
|
|
425
|
+
"7749638f1e8393f3beb1f7b0c37195dd0463abdb6fd7da5d31b3d816ee91fa37",
|
|
423
426
|
"fd06dfb27be41e5ffec3e3ec8c5362289f5919cf2afa9da5ab6b97e7baa4a0ac",
|
|
424
427
|
"66e105773b8e042eec5d5679b97b7cb305cad8d45671034907fbfa728fc4d68a",
|
|
425
428
|
"020ba4da2f96bcda23cee4596b1e790816780178f1917dee748ff4b289cba0e5",
|
|
@@ -451,8 +454,10 @@
|
|
|
451
454
|
{
|
|
452
455
|
"canonical_name": "sks-prompt-pipeline",
|
|
453
456
|
"type": "official",
|
|
454
|
-
"content_sha256": "
|
|
457
|
+
"content_sha256": "f82a103e7962e416961add515d7b3e65df89848dec1fbb9319bd49d7f1dac982",
|
|
455
458
|
"hash_history": [
|
|
459
|
+
"93fd938dccd65d9b4d1dd140b60e1baedc627e7fcc54f9afd95eb2e44efd1143",
|
|
460
|
+
"033bb114ec1fac3f8c312f698b1861ed4c2ce6f65c06f4f1a872c4b101b74b92",
|
|
456
461
|
"e7c8ef56d38fdc4879c983f7d6706f26fc1cf0a60c508836bd628e815700bcc3",
|
|
457
462
|
"480c8afffafa9d52c483a963b4b8fafdffc0a47cd6d2efd3fb6d7924c11832c5",
|
|
458
463
|
"5dc8524de9bad66cef133ea5ff2a8052331bd12a7ec85695f727d0d2c8523145"
|
|
@@ -471,8 +476,9 @@
|
|
|
471
476
|
{
|
|
472
477
|
"canonical_name": "sks-reasoning-router",
|
|
473
478
|
"type": "official",
|
|
474
|
-
"content_sha256": "
|
|
479
|
+
"content_sha256": "41e14e8d5b93d743096664869ee68c41c86e911b0bbc165bdcd10e4a45faa4b6",
|
|
475
480
|
"hash_history": [
|
|
481
|
+
"03d68436e568b3cee2bffc6ca58819019ca406f7b648ef4704e3465c7c83c114",
|
|
476
482
|
"647f730520a1c7dc89c2642f5b8502624c65a861ae41aecf1fb137db40fd0611",
|
|
477
483
|
"da0ddb58adc68ddeebdb9c45bf111c26eaa64d3e9dffe1dd39d7f30b943b8b88",
|
|
478
484
|
"1595beabf2d31397217311fa1323bd62c801cf6c87db9029ff544c457efec8bb",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { codexModelEffortCapability } from '../codex-control/codex-model-capabilities.js';
|
|
2
2
|
import { managedOfficialSubagentRoleByName } from '../managed-assets/managed-assets-manifest.js';
|
|
3
|
-
import {
|
|
3
|
+
import { decideSubagentModel, subagentModelProfile } from '../subagents/model-policy.js';
|
|
4
|
+
import { MODEL_TIERS, resolveLatestModelTiers } from '../subagents/model-tiers.js';
|
|
4
5
|
export function decideAgentEffort(input = {}) {
|
|
5
6
|
return decideOfficialSubagentModel(input);
|
|
6
7
|
}
|
|
@@ -38,14 +39,14 @@ export function decideOfficialSubagentModel(input = {}) {
|
|
|
38
39
|
reason: `managed_role:${managedRole.codex_name}:${profile.policy}`,
|
|
39
40
|
dynamic: true,
|
|
40
41
|
escalation_triggers: [
|
|
41
|
-
'focused review, debugging, planning, integration, security, database, research, release, or unresolved ambiguity selects
|
|
42
|
+
'focused review, debugging, planning, integration, security, database, research, release, or unresolved ambiguity selects the deep tier',
|
|
42
43
|
'incidental judgment vocabulary does not override a clearly classified implementation or context/tools slice',
|
|
43
44
|
'requested model/effort profile unavailable blocks instead of silently falling back'
|
|
44
45
|
],
|
|
45
46
|
downshift_triggers: [
|
|
46
|
-
'instructed UI, logic, backend, or native implementation selects
|
|
47
|
-
'long-context, Browser/Chrome, Computer Use, image-generation, or large search selects
|
|
48
|
-
'tiny short-context mechanical search/typing/rename work selects
|
|
47
|
+
'instructed UI, logic, backend, or native implementation selects the balanced tier',
|
|
48
|
+
'long-context, Browser/Chrome, Computer Use, image-generation, or large search selects the context tier',
|
|
49
|
+
'tiny short-context mechanical search/typing/rename work selects the balanced tier'
|
|
49
50
|
]
|
|
50
51
|
};
|
|
51
52
|
}
|
|
@@ -92,14 +93,14 @@ export function decideOfficialSubagentModel(input = {}) {
|
|
|
92
93
|
reason: routed.reason,
|
|
93
94
|
dynamic: true,
|
|
94
95
|
escalation_triggers: [
|
|
95
|
-
'focused review, debugging, planning, integration, security, database, research, release, or unresolved ambiguity selects
|
|
96
|
+
'focused review, debugging, planning, integration, security, database, research, release, or unresolved ambiguity selects the deep tier',
|
|
96
97
|
'incidental judgment vocabulary does not override a clearly classified implementation or context/tools slice',
|
|
97
98
|
'requested model/effort profile unavailable blocks instead of silently falling back'
|
|
98
99
|
],
|
|
99
100
|
downshift_triggers: [
|
|
100
|
-
'instructed UI, logic, backend, or native implementation selects
|
|
101
|
-
'long-context, Browser/Chrome, Computer Use, or image-generation execution selects
|
|
102
|
-
'tiny short-context mechanical work selects
|
|
101
|
+
'instructed UI, logic, backend, or native implementation selects the balanced tier',
|
|
102
|
+
'long-context, Browser/Chrome, Computer Use, or image-generation execution selects the context tier',
|
|
103
|
+
'tiny short-context mechanical work selects the balanced tier'
|
|
103
104
|
]
|
|
104
105
|
};
|
|
105
106
|
}
|
|
@@ -118,21 +119,22 @@ export function buildAgentEffortPolicy(roster = {}) {
|
|
|
118
119
|
reason: agent.reasoning_reason,
|
|
119
120
|
dynamic: true
|
|
120
121
|
})) : [];
|
|
122
|
+
const latest = resolveLatestModelTiers();
|
|
121
123
|
return {
|
|
122
124
|
schema: 'sks.agent-effort-policy.v1',
|
|
123
125
|
policy_version: 1,
|
|
124
126
|
dynamic: true,
|
|
125
127
|
service_tier: 'fast',
|
|
126
128
|
model_catalog_policy: 'official_subagent_four_profile_matrix',
|
|
127
|
-
model_constraint: [
|
|
128
|
-
model_tiers:
|
|
129
|
+
model_constraint: [...new Set(MODEL_TIERS.map((tier) => latest.models[tier]))],
|
|
130
|
+
model_tiers: MODEL_TIERS.map((tier) => `${tier}:${latest.models[tier]}-${latest.efforts[tier]}`),
|
|
129
131
|
allowed_efforts: ['low', 'medium', 'high', 'max'],
|
|
130
|
-
model_effort_capability: codexModelEffortCapability({ model:
|
|
132
|
+
model_effort_capability: codexModelEffortCapability({ model: latest.models.deep }),
|
|
131
133
|
max_agents: roster.max_agents || 20,
|
|
132
134
|
agent_count: roster.agent_count || decisions.length,
|
|
133
135
|
concurrency: roster.concurrency || decisions.length,
|
|
134
136
|
decisions,
|
|
135
|
-
rule: '
|
|
137
|
+
rule: 'Every child uses the newest model of the tier its work needs: fast for tiny mechanical work, balanced for instructed implementation, context for reads and tool execution, and deep for focused judgment. With Jev mode on, Jev picks the tier for each spawn. The parent keeps its user-selected model, reasoning effort, and service tier; stored role preferences may override role defaults.'
|
|
136
138
|
};
|
|
137
139
|
}
|
|
138
140
|
export function reasoningProfileName(effort) {
|
|
@@ -10,7 +10,8 @@ import { leanEngineeringCompactText, leanPolicyReference } from '../lean-enginee
|
|
|
10
10
|
import { readCodexLbModelCatalog, readLbHealth } from '../codex-lb/codex-lb-env.js';
|
|
11
11
|
import { categoryForWorkerRole, isNarutoGpt56Model, modelRouteReason, routeModel } from '../provider/model-router.js';
|
|
12
12
|
import { codexTimeoutClassForRoute } from '../codex-control/codex-reliability-shield.js';
|
|
13
|
-
import {
|
|
13
|
+
import { decideSubagentModel } from '../subagents/model-policy.js';
|
|
14
|
+
import { latestTierModelSet } from '../subagents/model-tiers.js';
|
|
14
15
|
export const NATIVE_WORKER_BACKEND_ROUTER_SCHEMA = 'sks.native-worker-backend-router.v1';
|
|
15
16
|
export async function runNativeWorkerBackendRouter(input) {
|
|
16
17
|
const root = path.resolve(input.agentRoot);
|
|
@@ -241,15 +242,19 @@ export async function resolveWorkerModelRouting(input, deps = {}) {
|
|
|
241
242
|
const explicitNarutoModelInvalid = narutoOnly && Boolean(explicitModel) && !isNarutoGpt56Model(explicitModel);
|
|
242
243
|
const explicitNarutoReasoningInvalid = narutoOnly && Boolean(explicitReasoningRaw) && !explicitReasoning;
|
|
243
244
|
const explicitNarutoTierInvalid = narutoOnly && Boolean(explicitTierRaw) && !explicitTier;
|
|
244
|
-
const
|
|
245
|
-
|
|
245
|
+
const currentModels = latestTierModelSet();
|
|
246
|
+
const savedPreferenceModel = input.agent?.routed_model_policy === 'user_role_model_preference'
|
|
247
|
+
&& currentModels.has(String(input.agent?.routed_model || ''))
|
|
248
|
+
? String(input.agent?.routed_model)
|
|
249
|
+
: '';
|
|
250
|
+
const savedAstraEffort = savedPreferenceModel
|
|
246
251
|
? normalizeModelReasoning(input.agent?.routed_model_reasoning_effort)
|
|
247
252
|
: null;
|
|
248
253
|
const jevModel = !explicitModel && input.agent?.routed_model_policy === 'jev_sealed_routing'
|
|
249
254
|
? String(input.agent.routed_model || '').trim()
|
|
250
255
|
: '';
|
|
251
256
|
const jevEffort = jevModel ? normalizeModelReasoning(input.agent?.routed_model_reasoning_effort) : null;
|
|
252
|
-
const selectedModel = explicitModel || jevModel;
|
|
257
|
+
const selectedModel = explicitModel || jevModel || savedPreferenceModel;
|
|
253
258
|
const taskPolicy = decideSubagentModel({ title: taskKindText, description: riskText, role: input.agent?.role });
|
|
254
259
|
const routed = narutoOnly
|
|
255
260
|
? await routeModel(category, {
|
|
@@ -263,14 +268,14 @@ export async function resolveWorkerModelRouting(input, deps = {}) {
|
|
|
263
268
|
...(selectedModel ? { model: selectedModel } : {})
|
|
264
269
|
})
|
|
265
270
|
: {
|
|
266
|
-
model:
|
|
271
|
+
model: (explicitModel && currentModels.has(explicitModel) ? explicitModel : '') || savedPreferenceModel || taskPolicy.model,
|
|
267
272
|
reasoning: explicitReasoning || savedAstraEffort || taskPolicy.modelReasoningEffort,
|
|
268
273
|
serviceTier: explicitTier || input.fastModePolicy.service_tier || 'fast'
|
|
269
274
|
};
|
|
270
275
|
const blockers = [
|
|
271
276
|
...(narutoOnly && !lbCatalog?.ok ? (lbCatalog?.blockers || ['codex_lb_model_catalog_unavailable']) : []),
|
|
272
|
-
...(explicitNarutoModelInvalid ? ['
|
|
273
|
-
...(!narutoOnly && explicitModel && explicitModel
|
|
277
|
+
...(explicitNarutoModelInvalid ? ['naruto_worker_model_not_current'] : []),
|
|
278
|
+
...(!narutoOnly && explicitModel && !currentModels.has(explicitModel) ? ['subagent_model_must_be_current'] : []),
|
|
274
279
|
...(explicitNarutoReasoningInvalid ? ['naruto_reasoning_override_invalid'] : []),
|
|
275
280
|
...(explicitNarutoTierInvalid ? ['naruto_service_tier_override_invalid'] : []),
|
|
276
281
|
...(narutoOnly && explicitReasoning && explicitReasoning !== routed.reasoning ? ['naruto_reasoning_override_conflicts_with_policy'] : []),
|
|
@@ -306,7 +311,7 @@ export function narutoWorkerBackendBlocker(backend, narutoRequest = true) {
|
|
|
306
311
|
if (!narutoRequest)
|
|
307
312
|
return null;
|
|
308
313
|
if (backend === 'process')
|
|
309
|
-
return '
|
|
314
|
+
return 'naruto_process_backend_forbidden';
|
|
310
315
|
return null;
|
|
311
316
|
}
|
|
312
317
|
function normalizeBackend(value) {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import os from 'node:os';
|
|
1
3
|
import path from 'node:path';
|
|
2
|
-
import { ensureDir, nowIso, readText, writeTextAtomic } from '../fsx.js';
|
|
4
|
+
import { ensureDir, nowIso, packageRoot, readText, writeTextAtomic } from '../fsx.js';
|
|
3
5
|
import { CODEX_HOOK_EVENTS } from '../codex-compat/codex-hook-events.js';
|
|
4
6
|
import { postToolEvidenceEnabled } from '../verification-profile.js';
|
|
5
7
|
import { buildCodexCommandHookToml, matcherApplies } from './codex-hook-config-writer.js';
|
|
@@ -22,7 +24,7 @@ export async function installManagedCodexHooks(root, opts = {}) {
|
|
|
22
24
|
const requirementsPath = path.resolve(root, opts.requirementsPath || path.join('.codex', 'requirements.toml'));
|
|
23
25
|
const scriptPath = path.join(managedDir, 'sks-managed-hook.sh');
|
|
24
26
|
const tomlPath = path.join(managedDir, 'sks-managed-hooks.toml');
|
|
25
|
-
const binCommand = opts.binCommand ||
|
|
27
|
+
const binCommand = opts.binCommand || await defaultManagedHookCommand();
|
|
26
28
|
const installedEvents = CODEX_HOOK_EVENTS.filter((event) => event !== 'PostToolUse' || postToolEvidenceEnabled(root));
|
|
27
29
|
const hooksToml = installedEvents.map((event) => buildCodexCommandHookToml({
|
|
28
30
|
event,
|
|
@@ -77,6 +79,32 @@ export async function installManagedCodexHooks(root, opts = {}) {
|
|
|
77
79
|
blockers: actual?.blockers || []
|
|
78
80
|
};
|
|
79
81
|
}
|
|
82
|
+
export async function defaultManagedHookCommand() {
|
|
83
|
+
const entrypoint = path.join(packageRoot(), 'dist', 'bin', 'sks.js');
|
|
84
|
+
if (!(await isRegularFile(entrypoint)))
|
|
85
|
+
return 'sks hook';
|
|
86
|
+
return `${shellQuote(process.execPath)} ${shellQuote(entrypoint)} hook`;
|
|
87
|
+
}
|
|
88
|
+
export async function retargetLiveManagedHookScript(env = process.env) {
|
|
89
|
+
const home = env.HOME || os.homedir();
|
|
90
|
+
const codexHome = path.resolve(env.CODEX_HOME || path.join(home, '.codex'));
|
|
91
|
+
const requirementsPath = path.join(codexHome, 'requirements.toml');
|
|
92
|
+
const requirements = await readText(requirementsPath, '');
|
|
93
|
+
const configuredDir = managedDirFromRequirements(String(requirements || ''), requirementsPath);
|
|
94
|
+
const managedDir = configuredDir || path.join(codexHome, 'managed-hooks');
|
|
95
|
+
const resolvedDir = path.resolve(managedDir);
|
|
96
|
+
const script = path.join(resolvedDir, 'sks-managed-hook.sh');
|
|
97
|
+
if (!pathIsInside(codexHome, script)) {
|
|
98
|
+
return { ok: false, status: 'absent', script, command: null };
|
|
99
|
+
}
|
|
100
|
+
if (!(await isRegularFile(script))) {
|
|
101
|
+
return { ok: true, status: 'absent', script, command: null };
|
|
102
|
+
}
|
|
103
|
+
const command = await defaultManagedHookCommand();
|
|
104
|
+
await writeTextAtomic(script, managedHookScript(command));
|
|
105
|
+
await chmodExecutable(script);
|
|
106
|
+
return { ok: true, status: 'rewritten', script, command };
|
|
107
|
+
}
|
|
80
108
|
function managedHookScript(binCommand) {
|
|
81
109
|
return [
|
|
82
110
|
'#!/usr/bin/env sh',
|
|
@@ -86,6 +114,32 @@ function managedHookScript(binCommand) {
|
|
|
86
114
|
`exec ${binCommand} "$subcommand" "$@"`
|
|
87
115
|
].join('\n') + '\n';
|
|
88
116
|
}
|
|
117
|
+
function shellQuote(value) {
|
|
118
|
+
if (/[\0\r\n]/.test(value))
|
|
119
|
+
throw new Error('hook_command_path_invalid');
|
|
120
|
+
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
121
|
+
}
|
|
122
|
+
function managedDirFromRequirements(text, requirementsPath) {
|
|
123
|
+
const match = text.match(/^\s*managed_dir\s*=\s*(.+)\s*$/m);
|
|
124
|
+
if (!match?.[1])
|
|
125
|
+
return null;
|
|
126
|
+
const raw = match[1].trim().replace(/^['"]|['"]$/g, '');
|
|
127
|
+
if (!raw || raw.includes('\0'))
|
|
128
|
+
return null;
|
|
129
|
+
return path.resolve(path.dirname(requirementsPath), raw);
|
|
130
|
+
}
|
|
131
|
+
function pathIsInside(root, target) {
|
|
132
|
+
const relative = path.relative(path.resolve(root), path.resolve(target));
|
|
133
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
134
|
+
}
|
|
135
|
+
async function isRegularFile(file) {
|
|
136
|
+
try {
|
|
137
|
+
return (await fs.stat(file)).isFile();
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
89
143
|
function mergeRequirementsToml(existing, opts) {
|
|
90
144
|
const withoutManagedOnly = String(existing || '')
|
|
91
145
|
.replace(/^\s*allow_managed_hooks_only\s*=.*$/gm, '')
|
|
@@ -18,6 +18,7 @@ const SETTINGS_V2_KEYS = new Set([
|
|
|
18
18
|
'schema', 'listen_host', 'listen_port', 'provider_registry', 'route_policy',
|
|
19
19
|
'provider_session_pins', 'client_capability_sha256', 'allowed_origins',
|
|
20
20
|
'connect_timeout_ms', 'idle_timeout_ms', 'official_passthrough',
|
|
21
|
+
'auth_priority_enabled',
|
|
21
22
|
]);
|
|
22
23
|
const TRANSFERABLE_V1_KEYS = [
|
|
23
24
|
'listen_host', 'listen_port', 'allowed_origins', 'connect_timeout_ms', 'idle_timeout_ms',
|
|
@@ -14,13 +14,13 @@ const CORE_SKILL_DEFINITIONS = [
|
|
|
14
14
|
display_name: 'naruto',
|
|
15
15
|
route: '$Naruto',
|
|
16
16
|
purpose: 'run a Codex official subagent workflow with official agent threads while parent integration remains owner.',
|
|
17
|
-
discovery: 'Run $sks-naruto
|
|
18
|
-
when: 'Use when the user
|
|
19
|
-
workflow: 'Run sks naruto run "<task>" [--agents N] [--max-threads N] [--json] with Codex official subagent threads only. The parent owns decomposition, per-wave capacity, later root-owned waves, integration, and final verification. Automatic targets begin at 4/6/8/16 by task size: bounded, explicit parallel, large-scale, then mass mechanical or exploration fan-out
|
|
17
|
+
discovery: 'Run $sks-naruto to delegate implementation to child subagents.',
|
|
18
|
+
when: 'Use when the user invokes $sks-naruto or $sks-work, or the selected route is Naruto: ordinary implementation work, where the parent orchestrates and children implement.',
|
|
19
|
+
workflow: 'Run sks naruto run "<task>" [--agents N] [--max-threads N] [--json] with Codex official subagent threads only. The parent orchestrates only: decompose, assign disjoint slices, spawn, integrate, and verify. Do not implement slice work in the parent thread; the SKS PreToolUse gate denies parent-thread source edits until the first child thread starts and while children are still running, and .sneakoscope artifacts stay parent-writable. The parent owns decomposition, per-wave capacity, later root-owned waves, integration, and final verification. Automatic targets begin at 4/6/8/16 by task size: bounded, explicit parallel, large-scale, then mass mechanical or exploration fan-out. After decomposition both lanes may expand to the SKS-owned 256-child ceiling when independent useful slices and real host capacity remain positive; max_threads defaults to a 256-child frame budget cap, never a target. A measured lower Codex host cap or explicit provider/API budget remains authoritative, and multi-wave scheduling reuses returned capacity. Every child runs the newest model of the tier its work needs (fast, balanced, context, or deep); no model family is pinned. When Jev mode is on, Jev picks the tier for each new spawn and SKS seals it. A user role preference stays authoritative. Do not choose child models yourself. Keep the four task-class profiles and compatibility IDs; max_depth=1 blocks nested delegation. Wait for every planned thread before final. In an active Codex App Naruto mission, commit the strict parent evidence with sks naruto parent-summary --mission <id> --stdin, then return localized Markdown without exposing the JSON.',
|
|
20
20
|
safety: 'Preserve user-authored content, inherit the parent permission mode, do not spawn nested subagents, do not inject the full pack or the full TriWiki context into every child, and do not fall back to another model, process runtime, custom scheduler, or worker pool. The historical Naruto process runtime is removed; stop with explicit blocker evidence when the official path is unavailable.',
|
|
21
21
|
cli: 'sks naruto run "<task>" [--agents N] [--max-threads N] [--json]; sks naruto status|subagents|proof [--mission <id>] [--json]; sks naruto parent-summary --mission <id> --stdin [--json]',
|
|
22
22
|
evidence: 'subagent-plan.json, subagent-events.jsonl, subagent-parent-summary.json, subagent-evidence.json, naruto-summary.json, and naruto-gate.json.',
|
|
23
|
-
fallback: '
|
|
23
|
+
fallback: 'When official subagents are unavailable, report the explicit availability blocker; the orchestration gate releases parent edits only after its bounded denials, and that release is recorded. Never fabricate process, PID, or subagent evidence.'
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
26
|
id: 'sks-core-qa-loop',
|
|
@@ -3,7 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { printJson } from '../../cli/output.js';
|
|
4
4
|
import { nowIso, writeJsonAtomic } from '../fsx.js';
|
|
5
5
|
import { resolveOpenRouterApiKey } from '../providers/openrouter/openrouter-secret-store.js';
|
|
6
|
-
import { jevEnabled, readDecisionConfig, writeDecisionConfig } from './config.js';
|
|
6
|
+
import { jevEnabled, readDecisionConfig, writeDecisionConfig, jevCapabilityActive } from './config.js';
|
|
7
7
|
import { buildEvaluationReport } from './evaluation.js';
|
|
8
8
|
import { OPENROUTER_DECISIONS_ENDPOINT, OPENROUTER_DECISIONS_MODEL, requestOpenRouterDecision } from './openrouter.js';
|
|
9
9
|
import { buildDecisionBundle } from './questions.js';
|
|
@@ -152,7 +152,14 @@ export async function statusReport(env) {
|
|
|
152
152
|
source: resolved.source,
|
|
153
153
|
preview: resolved.key_preview
|
|
154
154
|
},
|
|
155
|
-
capabilities:
|
|
155
|
+
capabilities: {
|
|
156
|
+
context: { ...config.capabilities.context, ready: jevCapabilityActive(config, 'context') },
|
|
157
|
+
plan: { ...config.capabilities.plan, ready: jevCapabilityActive(config, 'plan') },
|
|
158
|
+
recovery: config.capabilities.recovery
|
|
159
|
+
},
|
|
160
|
+
decision_points: jevEnabled(config)
|
|
161
|
+
? ['turn_tier', 'spawn_tier', 'role_tiers', 'role_omission', 'plan', 'context', 'parent_edit_delegation']
|
|
162
|
+
: [],
|
|
156
163
|
recovery: RECOVERY_CAPABILITY,
|
|
157
164
|
nextStep,
|
|
158
165
|
notes: [
|
|
@@ -70,6 +70,13 @@ export async function writeDecisionConfig(patch, env = process.env) {
|
|
|
70
70
|
export function jevEnabled(config) {
|
|
71
71
|
return config.mode === 'jev' && config.consentCloud === true;
|
|
72
72
|
}
|
|
73
|
+
export function jevCapabilityActive(config, capability) {
|
|
74
|
+
if (!jevEnabled(config))
|
|
75
|
+
return false;
|
|
76
|
+
if (capability === 'recovery')
|
|
77
|
+
return config.capabilities.recovery.ready;
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
73
80
|
function normalizeConfig(raw) {
|
|
74
81
|
const fallback = defaultDecisionConfig();
|
|
75
82
|
if (!isRecord(raw) || raw.schema !== DECISION_CONFIG_SCHEMA)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { sha256 } from '../fsx.js';
|
|
2
2
|
import { resolveOpenRouterApiKey } from '../providers/openrouter/openrouter-secret-store.js';
|
|
3
|
-
import { jevEnabled, readDecisionConfig } from './config.js';
|
|
3
|
+
import { jevCapabilityActive, jevEnabled, readDecisionConfig } from './config.js';
|
|
4
4
|
import { requestOpenRouterDecision } from './openrouter.js';
|
|
5
5
|
import { compileDecision } from './policy.js';
|
|
6
6
|
import { buildDecisionBundle, validatePlanCoverage } from './questions.js';
|
|
@@ -8,7 +8,7 @@ import { applyRecoveryEffect, listProductionRecoveryCandidates } from './recover
|
|
|
8
8
|
import { assembleRoutingSelection, buildRoutingCandidates } from './routing.js';
|
|
9
9
|
import { buildDecisionReceipt } from './receipt.js';
|
|
10
10
|
import { applyOptionalContextSelection, graphFileDigest, hydrateContextCandidates, sourceSnapshotDigest } from './state.js';
|
|
11
|
-
import { DESIGN_DEFAULTS, POLICY_REVISION, UNKNOWN_USAGE
|
|
11
|
+
import { DESIGN_DEFAULTS, POLICY_REVISION, UNKNOWN_USAGE } from './types.js';
|
|
12
12
|
let testOverrides = null;
|
|
13
13
|
const appliedEffects = new Set();
|
|
14
14
|
export function setDecisionTestOverrides(overrides) {
|
|
@@ -43,16 +43,16 @@ async function decideOfficialSubagentPreparationInner(input, started) {
|
|
|
43
43
|
overrides?.observe?.({ mode: config.mode, eligible: false, compiled: missing.compiled, receipt: missing.receipt });
|
|
44
44
|
return missing;
|
|
45
45
|
}
|
|
46
|
-
const contextCandidates = config
|
|
46
|
+
const contextCandidates = jevCapabilityActive(config, 'context')
|
|
47
47
|
? await hydrateContextCandidates(input.root, input.attention, input.changedPaths || [])
|
|
48
48
|
: [];
|
|
49
|
-
const planCandidates = config
|
|
49
|
+
const planCandidates = jevCapabilityActive(config, 'plan') && input.requestedSource === 'automatic'
|
|
50
50
|
? buildAutomaticPlanCandidates(input)
|
|
51
51
|
: [];
|
|
52
52
|
const routingCandidates = input.requestedSource === 'automatic'
|
|
53
53
|
? buildRoutingCandidates({ roles: input.routingRoles || [] })
|
|
54
54
|
: [];
|
|
55
|
-
const recoveryCandidates = config
|
|
55
|
+
const recoveryCandidates = jevCapabilityActive(config, 'recovery')
|
|
56
56
|
? [...listProductionRecoveryCandidates()]
|
|
57
57
|
: [];
|
|
58
58
|
const optionalContext = contextCandidates.filter((row) => !row.pinned && row.excerpt);
|
|
@@ -134,7 +134,7 @@ export async function consultJevTurnModel(input) {
|
|
|
134
134
|
const overrides = activeOverrides();
|
|
135
135
|
const env = input.env || process.env;
|
|
136
136
|
const config = overrides?.config ?? await readDecisionConfig(env);
|
|
137
|
-
const none = (reason) => ({ called: false, model: null, effort: null, reason });
|
|
137
|
+
const none = (reason) => ({ called: false, model: null, effort: null, tier: null, reason });
|
|
138
138
|
if (!jevEnabled(config))
|
|
139
139
|
return none('off');
|
|
140
140
|
const resolved = await resolveOpenRouterApiKey({ env });
|
|
@@ -159,14 +159,56 @@ export async function consultJevTurnModel(input) {
|
|
|
159
159
|
...(overrides?.fetchImpl ? { fetchImpl: overrides.fetchImpl } : {})
|
|
160
160
|
});
|
|
161
161
|
if (!transport.ok)
|
|
162
|
-
return { called: true, model: null, effort: null, reason: transport.reason };
|
|
162
|
+
return { called: true, model: null, effort: null, tier: null, reason: transport.reason };
|
|
163
163
|
const compiled = compileDecision(bundle, transport.response);
|
|
164
164
|
if (compiled.kind !== 'apply')
|
|
165
|
-
return { called: true, model: null, effort: null, reason: compiled.reason };
|
|
166
|
-
const selected = assembleRoutingSelection(compiled.effects.flatMap((effect) => (effect.kind === 'select_routing' ? [{ roleId: effect.roleId,
|
|
165
|
+
return { called: true, model: null, effort: null, tier: null, reason: compiled.reason };
|
|
166
|
+
const selected = assembleRoutingSelection(compiled.effects.flatMap((effect) => (effect.kind === 'select_routing' ? [{ roleId: effect.roleId, tier: effect.tier }] : [])));
|
|
167
167
|
const model = selected?.models[roleId] || null;
|
|
168
|
-
const
|
|
169
|
-
|
|
168
|
+
const effort = selected?.efforts[roleId] || null;
|
|
169
|
+
const tier = selected?.tiers[roleId] || null;
|
|
170
|
+
if (!model || !effort || !tier)
|
|
171
|
+
return { called: true, model: null, effort: null, tier: null, reason: 'keep_baseline' };
|
|
172
|
+
return { called: true, model, effort, tier, reason: 'applied' };
|
|
173
|
+
}
|
|
174
|
+
export async function consultJevToolDelegation(input) {
|
|
175
|
+
const overrides = activeOverrides();
|
|
176
|
+
const env = input.env || process.env;
|
|
177
|
+
const config = overrides?.config ?? await readDecisionConfig(env);
|
|
178
|
+
const none = (reason) => ({ called: false, choice: null, reason });
|
|
179
|
+
if (!jevEnabled(config))
|
|
180
|
+
return none('off');
|
|
181
|
+
const resolved = await resolveOpenRouterApiKey({ env });
|
|
182
|
+
if (!resolved.key)
|
|
183
|
+
return none('missing_key');
|
|
184
|
+
const goal = String(input.missionGoal || '').trim();
|
|
185
|
+
const toolName = String(input.toolName || '').trim();
|
|
186
|
+
if (!goal || !toolName)
|
|
187
|
+
return none('empty_candidate');
|
|
188
|
+
const targets = input.targets.map((row) => String(row || '').trim()).filter(Boolean);
|
|
189
|
+
const bundle = buildDecisionBundle({
|
|
190
|
+
projectId: sha256(input.root).slice(0, 32),
|
|
191
|
+
workflowRunId: 'delegation',
|
|
192
|
+
workflowRevision: 'delegation',
|
|
193
|
+
sourceDigest: sha256(JSON.stringify([toolName, targets])).slice(0, 32),
|
|
194
|
+
graphDigest: null,
|
|
195
|
+
goal,
|
|
196
|
+
delegationCandidate: { toolName, targets, missionGoal: goal }
|
|
197
|
+
});
|
|
198
|
+
const transport = await requestOpenRouterDecision(bundle, {
|
|
199
|
+
env,
|
|
200
|
+
deadlineMs: DESIGN_DEFAULTS.deadlineMs,
|
|
201
|
+
...(overrides?.fetchImpl ? { fetchImpl: overrides.fetchImpl } : {})
|
|
202
|
+
});
|
|
203
|
+
if (!transport.ok)
|
|
204
|
+
return { called: true, choice: null, reason: transport.reason };
|
|
205
|
+
const compiled = compileDecision(bundle, transport.response);
|
|
206
|
+
if (compiled.kind !== 'apply')
|
|
207
|
+
return { called: true, choice: null, reason: compiled.reason };
|
|
208
|
+
const effect = compiled.effects.find((row) => row.kind === 'select_delegation');
|
|
209
|
+
if (!effect || effect.kind !== 'select_delegation')
|
|
210
|
+
return { called: true, choice: null, reason: 'keep_baseline' };
|
|
211
|
+
return { called: true, choice: effect.choice, reason: 'applied' };
|
|
170
212
|
}
|
|
171
213
|
export function effectAlreadyConsumed(identity) {
|
|
172
214
|
return appliedEffects.has(identity);
|
|
@@ -210,7 +252,7 @@ function applyEffects(attention, contextCandidates, planCandidates, compiled) {
|
|
|
210
252
|
evidence.push(`plan:${selectedPlan.id}`);
|
|
211
253
|
}
|
|
212
254
|
if (effect.kind === 'select_routing') {
|
|
213
|
-
routingEffects.push({ roleId: effect.roleId,
|
|
255
|
+
routingEffects.push({ roleId: effect.roleId, tier: effect.tier });
|
|
214
256
|
}
|
|
215
257
|
if (effect.kind === 'omit_role') {
|
|
216
258
|
omittedRoutingRoles.push(effect.roleId);
|