onto-mcp 0.4.13 → 0.4.14
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/.onto/processes/review/external-oauth-worker-contract.md +12 -5
- package/README.md +33 -17
- package/dist/core-api/reconstruct-api.js +41 -18
- package/dist/core-api/review-api.js +9 -1
- package/dist/core-runtime/cli/prepare-review-session.js +16 -1
- package/dist/core-runtime/cli/review-invocation-runner.js +6 -0
- package/dist/core-runtime/cli/review-invoke.js +26 -2
- package/dist/core-runtime/discovery/llm-override.js +262 -0
- package/dist/core-runtime/discovery/review-cert-assemble.js +1 -1
- package/dist/core-runtime/discovery/review-cert-record.js +119 -21
- package/dist/core-runtime/discovery/settings-chain.js +33 -11
- package/dist/core-runtime/llm/model-switcher.js +7 -1
- package/dist/core-runtime/reconstruct/run.js +1 -1
- package/dist/core-runtime/review/materializers.js +4 -0
- package/dist/core-runtime/review/semantic-quality-gate.js +227 -32
- package/dist/mcp/server.js +177 -14
- package/dist/mcp/tool-schemas.js +22 -5
- package/package.json +1 -1
|
@@ -65,11 +65,18 @@ Output formats are shared: `lens-sidecar`, `issue-artifact`,
|
|
|
65
65
|
### 3.1 Adapter-specific schema delivery
|
|
66
66
|
|
|
67
67
|
- `codex_cli`: the schema is written to a file and passed via `codex exec --output-schema`.
|
|
68
|
-
- `claude_code`:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
- `claude_code`: `claude --json-schema <schema>` **is** used — it constrains
|
|
69
|
+
output at the API level (grammar-based structured outputs) so the model cannot
|
|
70
|
+
emit syntactically malformed JSON, and the CLI returns a parsed
|
|
71
|
+
`structured_output` object. The earlier claim here that its validator "silently
|
|
72
|
+
rejects the runtime's complex submit schemas" was **inaccurate**: empirically
|
|
73
|
+
(opus-4.8 + haiku, 2026-07) the flag accepts the runtime's submit schemas
|
|
74
|
+
(`additionalProperties:false` + deep `required`, ~20 params, enums) and returns
|
|
75
|
+
native objects; without it, opus free-forms the JSON in-prompt and can drop a
|
|
76
|
+
comma on large findings → an unrecoverable `output_contract` failure. The
|
|
77
|
+
in-prompt schema remains as a redundant semantic hint, and the in-process submit
|
|
78
|
+
tool remains the authoritative validator of field CONTENT — so schema
|
|
79
|
+
enforcement holds regardless of delivery mechanism.
|
|
73
80
|
|
|
74
81
|
### 3.2 Result extraction
|
|
75
82
|
|
package/README.md
CHANGED
|
@@ -83,6 +83,19 @@ npm exec -- onto mcp
|
|
|
83
83
|
Before running reviews, configure an LLM provider in `.onto/settings.json` or
|
|
84
84
|
`~/.onto/settings.json` (see [Configuration](#configuration)).
|
|
85
85
|
|
|
86
|
+
## onto CLI
|
|
87
|
+
|
|
88
|
+
The `onto` binary exposes a small set of commands; the actual product work is
|
|
89
|
+
driven through the MCP tools by your host.
|
|
90
|
+
|
|
91
|
+
| Command | What it does |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `onto mcp` | Start the MCP stdio tool server — each MCP host launches this (see [Quickstart](#quickstart)) |
|
|
94
|
+
| `onto register` | Register the server into supported MCP hosts (see [Quickstart](#quickstart)) |
|
|
95
|
+
| `onto configure-provider` | Write LLM provider/model settings into the settings.json chain (see [Configuration](#configuration)) |
|
|
96
|
+
| `onto seats` | Print a read-only inventory of every LLM model seat the runtime can dispatch, resolved against the settings.json chain (`--json` for machine output); writes nothing |
|
|
97
|
+
| `onto watch [session]` | Open a live, read-only TUI over a review/reconstruct session — pass a session id substring or path, or omit it for the most recent (see [Observing a run](#observing-a-run)) |
|
|
98
|
+
|
|
86
99
|
## What it does
|
|
87
100
|
|
|
88
101
|
### Review
|
|
@@ -159,16 +172,12 @@ point-in-time map (v0.4.7 snapshot, not maintained) is kept at
|
|
|
159
172
|
| `onto_review_round` | Host-orchestration: return the units ready to execute now, with prompt packets materialized |
|
|
160
173
|
| `onto_review_advance` | Host-orchestration: report host-executed units; onto validates seats, records results, and returns the next round |
|
|
161
174
|
| `onto_review_cancel` | Request cancellation for a running review session |
|
|
162
|
-
| `
|
|
163
|
-
| `
|
|
164
|
-
| `onto_list_lenses` | List canonical lens sets |
|
|
165
|
-
| `onto_list_domains` | List available domain ids |
|
|
166
|
-
| `onto_list_source_profiles` | List reconstruct source profiles |
|
|
175
|
+
| `onto_review_read` | Read a review session — one entry point for liveness while running and the bounded result once complete; `projectionLevel` `compact`/`standard`/`full` (`full` adds `review-record.yaml` and final output) |
|
|
176
|
+
| `onto_list` | List a registry by `kind`: `lenses` (canonical lens sets), `domains` (available domain ids), or `source_profiles` (reconstruct source profiles) |
|
|
167
177
|
| `onto_observe_source` | Materialize reconstruct material profile, inventory, source observations, and initial record |
|
|
168
178
|
| `onto_validate_reconstruct_directive` | Validate LLM-authored reconstruct artifacts |
|
|
169
179
|
| `onto_reconstruct` | Run the material-aware direct-call reconstruct path with runtime validation gates |
|
|
170
|
-
| `
|
|
171
|
-
| `onto_reconstruct_result` | Read `reconstruct-record.yaml`, run manifest, progress projection, and final output |
|
|
180
|
+
| `onto_reconstruct_read` | Read a reconstruct session — stage progress, liveness, and counts, or the full record, run manifest, and final output at `projectionLevel=full` |
|
|
172
181
|
|
|
173
182
|
MCP results include `llmPresentation` prompts: the runtime supplies bounded
|
|
174
183
|
facts, and the host LLM uses those prompts to explain the opening brief and
|
|
@@ -185,17 +194,22 @@ onto without external docs:
|
|
|
185
194
|
`intent`, `reviewMode`) and `reconstruct_seed` (args: `targetRefs`,
|
|
186
195
|
`intent`).
|
|
187
196
|
|
|
188
|
-
`
|
|
197
|
+
`onto_review_read` accepts `projectionLevel` (`compact` | `standard` |
|
|
189
198
|
`full`); use `compact` in token-limited hosts.
|
|
190
199
|
|
|
191
|
-
###
|
|
200
|
+
### Observing a run
|
|
201
|
+
|
|
202
|
+
`onto watch` opens a live, read-only TUI over a review or reconstruct session:
|
|
203
|
+
browse the workflow tree, node details, and log. Pass a session id substring or
|
|
204
|
+
a session path, or omit it to attach to the most recent session
|
|
205
|
+
(`--project-root` selects where to look for `.onto/{review,reconstruct}`). It
|
|
206
|
+
observes only — it writes nothing and drives nothing.
|
|
192
207
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
`
|
|
198
|
-
unsupported hosts.
|
|
208
|
+
The runtime also writes a session-local `runtime-events.ndjson` stream and tries
|
|
209
|
+
to open a watcher automatically in a supported terminal (tmux, Warp, Cursor,
|
|
210
|
+
iTerm2, Apple Terminal, Codex Desktop with a configured launcher). Set
|
|
211
|
+
`ONTO_RUNTIME_WATCHER=0` to disable, or `ONTO_RUNTIME_WATCHER_COMMAND` with a
|
|
212
|
+
`{watcherCommand}` template for unsupported hosts.
|
|
199
213
|
|
|
200
214
|
## Configuration
|
|
201
215
|
|
|
@@ -322,8 +336,10 @@ onto configure-provider --provider anthropic --model claude-opus-4-8 --auth oaut
|
|
|
322
336
|
- `--project`는 프로젝트 seat(`.onto/settings.json`)에, 생략하면 사용자 seat
|
|
323
337
|
(`~/.onto/settings.json`)에 기록합니다.
|
|
324
338
|
- 지원 model id는 [`.onto/authority/supported-models.yaml`](.onto/authority/supported-models.yaml)가
|
|
325
|
-
authority
|
|
326
|
-
`
|
|
339
|
+
authority입니다. 범용 seat은 `gpt-5.5`(openai)·`claude-opus-4-8`(anthropic)이고,
|
|
340
|
+
역할 전용으로 `review`에 `gpt-5.6-sol`·`claude-fable-5`, `semantic_map_synthesize`에
|
|
341
|
+
`gpt-5.6-luna`·`claude-sonnet-5`가 인증돼 있습니다. 현재 프로젝트에서 실제로
|
|
342
|
+
해석되는 seat은 `onto seats`로 확인하세요.
|
|
327
343
|
- 이 명령은 actor 블록만 기록합니다. `units[].llm.model`로 unit별 model을 고정해
|
|
328
344
|
두었다면(위 정적 프로필 예시가 그렇습니다) 같은 model로 바꾸거나 그 `model` 키를
|
|
329
345
|
지워 actor 값을 상속하게 하세요.
|
|
@@ -13,7 +13,8 @@ import { RECONSTRUCT_SEMANTIC_AUTHOR_MAX_BASE_OUTPUT_TOKENS, } from "../core-run
|
|
|
13
13
|
// allowlisted in scripts/check-import-boundary.ts). Active only when
|
|
14
14
|
// ONTO_LLM_MOCK=1; mock runs record mock actor ids in the run manifest.
|
|
15
15
|
import { RECONSTRUCT_MOCK_AUTHOR_ID, RECONSTRUCT_MOCK_CONFIRMATION_PROVIDER_ID, callReconstructMockLlm, isReconstructMockLlmRealizationEnabled, } from "../core-runtime/reconstruct/mock-llm-realization.js";
|
|
16
|
-
import { assertSettingsModelsSupported, completeDispatchBreakerSettings, isReconstructSemanticMapAuthoringEnabled, resolveOptionalReconstructActorLlmSettings, resolveSettingsChain, resolveReconstructActorLlmSettings, resolveReconstructSemanticAuthorLlmRuntimeSettings, } from "../core-runtime/discovery/settings-chain.js";
|
|
16
|
+
import { assertSettingsModelsSupported, completeDispatchBreakerSettings, isReconstructSemanticMapAuthoringEnabled, resolveOptionalReconstructActorLlmSettings, resolveSettingsChain, resolveReconstructActorLlmSettings, resolveReconstructSemanticAuthorLlmRuntimeSettings, PerCallLlmOverrideSchema, } from "../core-runtime/discovery/settings-chain.js";
|
|
17
|
+
import { applyReconstructLlmOverride } from "../core-runtime/discovery/llm-override.js";
|
|
17
18
|
import { assertDispatchFallbackSessionAdmission } from "../core-runtime/reconstruct/dispatch-fallback-artifacts.js";
|
|
18
19
|
import { normalizeLlmModelSwitcher, } from "../core-runtime/llm/model-switcher.js";
|
|
19
20
|
import { resolveOntoHome, } from "../core-runtime/discovery/onto-home.js";
|
|
@@ -329,13 +330,12 @@ export function resolveSemanticMapSynthesizeWiring(args) {
|
|
|
329
330
|
}
|
|
330
331
|
return {
|
|
331
332
|
enableSemanticMapAuthoring: true,
|
|
333
|
+
// Effort comes from the seat's own (already overlaid) llm block —
|
|
334
|
+
// resolveLlmProviderConfig reads config.llm.effort (design v4 §6(a)).
|
|
332
335
|
semanticMapSynthesizeLlmConfig: args.mockRealizationEnabled
|
|
333
336
|
? synthesizeSeatIdentityProjection(seat)
|
|
334
337
|
: resolveLlmProviderConfig({
|
|
335
338
|
config: { llm: seat },
|
|
336
|
-
...(args.llmEffortOverride
|
|
337
|
-
? { cliOverrides: args.llmEffortOverride }
|
|
338
|
-
: {}),
|
|
339
339
|
}),
|
|
340
340
|
};
|
|
341
341
|
}
|
|
@@ -577,7 +577,32 @@ export function createOntoReconstructCoreApi(options = {}) {
|
|
|
577
577
|
...(ontoHome ? { ontoHome } : {}),
|
|
578
578
|
});
|
|
579
579
|
const targetRefs = request.targetRefs.map((targetRef) => resolveFromBase(projectRoot, targetRef));
|
|
580
|
-
|
|
580
|
+
// Per-call LLM override (design v4): overlay the resolved settings ONCE,
|
|
581
|
+
// here, so every downstream consumer (dispatch-fallback admission, the
|
|
582
|
+
// supported-model gate, actor/judge provider resolution, census,
|
|
583
|
+
// provenance) reads the SAME effective settings. Identity when absent →
|
|
584
|
+
// byte-identical default-off.
|
|
585
|
+
// The strict override schema IS the security boundary (it excludes
|
|
586
|
+
// base_url/api_key_env/timeout_ms so a per-call input cannot pick an
|
|
587
|
+
// endpoint or credential env). The MCP handler validates, but this Core
|
|
588
|
+
// API is also called programmatically, where a static type enforces
|
|
589
|
+
// nothing at runtime — so re-parse here, at admission, and overlay ONLY
|
|
590
|
+
// the parsed value.
|
|
591
|
+
const llmOverride = request.llmOverride === undefined
|
|
592
|
+
? undefined
|
|
593
|
+
: PerCallLlmOverrideSchema.parse(request.llmOverride);
|
|
594
|
+
const baseSettings = await resolveSettingsChain(ontoHome ?? projectRoot, projectRoot);
|
|
595
|
+
// design v4 §2.5: `judgeModel` names a model ON THE AUTHOR'S PROVIDER, so
|
|
596
|
+
// combining it with an override that switches that provider is ambiguous —
|
|
597
|
+
// the judge would silently resolve (or degrade) on the switched-in
|
|
598
|
+
// provider, reintroducing the "believe X, run Y" failure the override
|
|
599
|
+
// contract forbids. Reject at the handler, before any dispatch.
|
|
600
|
+
const settings = applyReconstructLlmOverride(baseSettings, llmOverride);
|
|
601
|
+
const authorProviderBefore = baseSettings.reconstruct?.execution?.actors?.semantic_author?.llm?.provider;
|
|
602
|
+
const authorProviderAfter = settings.reconstruct?.execution?.actors?.semantic_author?.llm?.provider;
|
|
603
|
+
if (request.judgeModel && authorProviderAfter !== authorProviderBefore) {
|
|
604
|
+
throw new Error(`llmOverride switches the semantic author's provider (${String(authorProviderBefore)} → ${String(authorProviderAfter)}) while judgeModel="${request.judgeModel}" names a model on the author's provider. This combination is ambiguous: drop judgeModel, or keep the author's provider.`);
|
|
605
|
+
}
|
|
581
606
|
await assertDispatchFallbackSessionAdmission({
|
|
582
607
|
sessionRoot,
|
|
583
608
|
enabled: settings.reconstruct?.execution?.dispatch_fallback?.enabled === true,
|
|
@@ -598,9 +623,9 @@ export function createOntoReconstructCoreApi(options = {}) {
|
|
|
598
623
|
// Mock realization needs no provider config: actor llm settings stay
|
|
599
624
|
// required only for live direct_call execution, and the recorded route
|
|
600
625
|
// comes from the mock result marker, not from a configured provider.
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
626
|
+
// The author-side effort is carried on each actor's (already overlaid) llm
|
|
627
|
+
// block — resolveLlmProviderConfig reads config.llm.effort — so no separate
|
|
628
|
+
// effort-override plumbing is needed (design v4 §6(a)).
|
|
604
629
|
// The semantic_author actor settings (non-mock). The MODEL provider here
|
|
605
630
|
// (e.g. "openai") is the supported-models registry key, DISTINCT from the
|
|
606
631
|
// resolved RUNTIME provider (openai OAuth resolves to "codex"). The document
|
|
@@ -612,7 +637,6 @@ export function createOntoReconstructCoreApi(options = {}) {
|
|
|
612
637
|
let semanticAuthorLlmConfig = semanticAuthorActorLlm
|
|
613
638
|
? resolveLlmProviderConfig({
|
|
614
639
|
config: { llm: semanticAuthorActorLlm },
|
|
615
|
-
...(llmEffortOverride ? { cliOverrides: llmEffortOverride } : {}),
|
|
616
640
|
})
|
|
617
641
|
: {};
|
|
618
642
|
const confirmationProviderLlmConfig = mockRealizationEnabled
|
|
@@ -621,7 +645,6 @@ export function createOntoReconstructCoreApi(options = {}) {
|
|
|
621
645
|
config: {
|
|
622
646
|
llm: resolveReconstructActorLlmSettings(settings, "confirmation_provider"),
|
|
623
647
|
},
|
|
624
|
-
...(llmEffortOverride ? { cliOverrides: llmEffortOverride } : {}),
|
|
625
648
|
});
|
|
626
649
|
// The supported-models registry (non-mock only): loaded once and shared by
|
|
627
650
|
// the judge-override support check and the document projection-budget
|
|
@@ -649,13 +672,12 @@ export function createOntoReconstructCoreApi(options = {}) {
|
|
|
649
672
|
// Semantic-map authoring opt-in + optional synthesize seat (INV-MODEL-1
|
|
650
673
|
// role-aware design §5.4/§5.5): ONE deterministic seam owns the wiring
|
|
651
674
|
// (resolveSemanticMapSynthesizeWiring) — live completes the seat into a
|
|
652
|
-
// provider config (own auth/adapter, cross-provider; the
|
|
653
|
-
//
|
|
654
|
-
//
|
|
675
|
+
// provider config (own auth/adapter, cross-provider; effort comes from the
|
|
676
|
+
// seat's own — already overlaid — llm block), mock takes an identity-only
|
|
677
|
+
// projection (no auth material required).
|
|
655
678
|
const semanticMapWiring = resolveSemanticMapSynthesizeWiring({
|
|
656
679
|
settings,
|
|
657
680
|
mockRealizationEnabled,
|
|
658
|
-
llmEffortOverride,
|
|
659
681
|
});
|
|
660
682
|
// Single seed-stage document projection budget (chars), derived once from
|
|
661
683
|
// the semantic author's MODEL window.
|
|
@@ -687,18 +709,19 @@ export function createOntoReconstructCoreApi(options = {}) {
|
|
|
687
709
|
if (!semanticMapWiring.enableSemanticMapAuthoring) {
|
|
688
710
|
throw new Error("reconstruct dispatch_fallback requires reconstruct.execution.semantic_map_authoring=true.");
|
|
689
711
|
}
|
|
690
|
-
|
|
712
|
+
// Effort is carried on each (already overlaid) llm block: the primary
|
|
713
|
+
// seats via the semantic_map_synthesize/semantic_author actors, the
|
|
714
|
+
// fallback via dispatch_fallback.llm (applyReconstructLlmOverride threads
|
|
715
|
+
// override.effort into it). No separate request-effort pin (design v4 §6(a)).
|
|
716
|
+
// Fresh copies keep these dispatch inputs independent of the shared settings.
|
|
691
717
|
const primarySynthesizeLlm = {
|
|
692
718
|
...(resolveOptionalReconstructActorLlmSettings(settings, "semantic_map_synthesize") ?? resolveReconstructActorLlmSettings(settings, "semantic_author")),
|
|
693
|
-
...(effectiveEffort ? { effort: effectiveEffort } : {}),
|
|
694
719
|
};
|
|
695
720
|
const primaryVerifyLlm = {
|
|
696
721
|
...resolveReconstructActorLlmSettings(settings, "semantic_author"),
|
|
697
|
-
...(effectiveEffort ? { effort: effectiveEffort } : {}),
|
|
698
722
|
};
|
|
699
723
|
const fallbackLlm = {
|
|
700
724
|
...dispatchFallbackSettings.llm,
|
|
701
|
-
...(effectiveEffort ? { effort: effectiveEffort } : {}),
|
|
702
725
|
};
|
|
703
726
|
const [primarySynthesize, primaryVerify, fallbackSynthesize, fallbackVerify] = await Promise.all([
|
|
704
727
|
tryCreateEligiblePrimarySealedDispatchCapability({
|
|
@@ -6,6 +6,7 @@ import os from "node:os";
|
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { resolveOntoHome } from "../core-runtime/discovery/onto-home.js";
|
|
8
8
|
import { completeDispatchBreakerSettings, defaultReviewRetrySettings, resolveSettingsChain, } from "../core-runtime/discovery/settings-chain.js";
|
|
9
|
+
import { applyReviewLlmOverride } from "../core-runtime/discovery/llm-override.js";
|
|
9
10
|
import { loadCoreLensRegistry } from "../core-runtime/discovery/lens-registry.js";
|
|
10
11
|
import { fileExists, isDeprecatedDomainAlias, isoFromTimestamp, isoNow, normalizeDomainValue, readYamlDocument, writeYamlDocument, } from "../core-runtime/review/review-artifact-utils.js";
|
|
11
12
|
import { assertPathInsideRoot, realpathIfExists, } from "../core-runtime/path-boundary.js";
|
|
@@ -2448,7 +2449,11 @@ function reviewExecutionProfileFromActorProfiles(args) {
|
|
|
2448
2449
|
return reconstructed;
|
|
2449
2450
|
}
|
|
2450
2451
|
async function projectReviewExecutionProfileForContinuation(args) {
|
|
2451
|
-
const
|
|
2452
|
+
const projectSettings = await resolveSettingsChain(args.ontoHome, args.projectRoot);
|
|
2453
|
+
// Re-apply the session's per-call override (design v4 §7) so the unit
|
|
2454
|
+
// execution policy this profile carries stays on the overridden route rather
|
|
2455
|
+
// than reverting to the non-overlaid project models. Identity when absent.
|
|
2456
|
+
const settings = applyReviewLlmOverride(projectSettings, args.llmOverride);
|
|
2452
2457
|
const resolved = resolveReviewExecutionProfile({
|
|
2453
2458
|
explicitCodex: false,
|
|
2454
2459
|
settings,
|
|
@@ -3116,6 +3121,9 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
3116
3121
|
: await projectReviewExecutionProfileForContinuation({
|
|
3117
3122
|
projectRoot,
|
|
3118
3123
|
ontoHome,
|
|
3124
|
+
...(executionPlan.llm_override
|
|
3125
|
+
? { llmOverride: executionPlan.llm_override }
|
|
3126
|
+
: {}),
|
|
3119
3127
|
});
|
|
3120
3128
|
const actorProfileReviewExecutionProfile = manifestReviewExecutionProfile
|
|
3121
3129
|
? undefined
|
|
@@ -11,6 +11,7 @@ import { printOntoReleaseChannelNotice } from "../release-channel/release-channe
|
|
|
11
11
|
import { detectCodexEnvSignal } from "../discovery/host-detection.js";
|
|
12
12
|
import { resolveOntoHome } from "../discovery/onto-home.js";
|
|
13
13
|
import { resolveSettingsChain } from "../discovery/settings-chain.js";
|
|
14
|
+
import { applyReviewLlmOverride, parsePerCallLlmOverrideArg, } from "../discovery/llm-override.js";
|
|
14
15
|
/**
|
|
15
16
|
* Stage 1 — resolve the window-proportional review prompt budget multiplier from
|
|
16
17
|
* the lens model's registered context window. The registry load and model
|
|
@@ -178,6 +179,7 @@ export async function runPrepareReviewSessionCli(argv) {
|
|
|
178
179
|
"max-listing-depth": { type: "string" },
|
|
179
180
|
"max-listing-entries": { type: "string" },
|
|
180
181
|
"max-embed-lines": { type: "string" },
|
|
182
|
+
"llm-override": { type: "string" },
|
|
181
183
|
},
|
|
182
184
|
strict: true,
|
|
183
185
|
allowPositionals: false,
|
|
@@ -195,7 +197,16 @@ export async function runPrepareReviewSessionCli(argv) {
|
|
|
195
197
|
const executionRealization = resolveExecutionRealization(argv, values["execution-realization"] ?? values["execution-mode"], hostRuntime);
|
|
196
198
|
const projectRoot = path.resolve(requireString(values["project-root"], "project-root"));
|
|
197
199
|
const ontoHome = resolveOntoHome(optionalString(values["onto-home"]) || undefined);
|
|
198
|
-
|
|
200
|
+
// Per-call LLM override (design v4): overlay the resolved settings so the
|
|
201
|
+
// baked artifacts (session-metadata resolved_llm_plan, actor-invocation
|
|
202
|
+
// profiles) carry the effective actor LLM — this is what onto_review_continue
|
|
203
|
+
// replays. Same overlay applied at the orchestrator profile seam
|
|
204
|
+
// (resolveReviewInvokeSetup); identity when absent → byte-identical default-off.
|
|
205
|
+
const overlay = parsePerCallLlmOverrideArg(values["llm-override"]);
|
|
206
|
+
const resolvedConfig = await resolveSettingsChain(ontoHome, projectRoot);
|
|
207
|
+
const ontoConfig = overlay
|
|
208
|
+
? applyReviewLlmOverride(resolvedConfig, overlay)
|
|
209
|
+
: resolvedConfig;
|
|
199
210
|
// Stage 1 — resolve the window-proportional review prompt budget ONCE, here,
|
|
200
211
|
// and thread it to both consumers: the spreadsheet inventory prompt caps
|
|
201
212
|
// (into the materialize call) and the embed line budget (persisted into the
|
|
@@ -209,6 +220,10 @@ export async function runPrepareReviewSessionCli(argv) {
|
|
|
209
220
|
const bindingParams = {
|
|
210
221
|
projectRoot,
|
|
211
222
|
ontoConfig,
|
|
223
|
+
// Stamp the RAW override into the execution plan too: ontoConfig carries its
|
|
224
|
+
// effect on the actors, but continuation re-resolves the project profile for
|
|
225
|
+
// unit execution policy and must re-overlay it (design v4 §7).
|
|
226
|
+
...(overlay ? { llmOverride: overlay } : {}),
|
|
212
227
|
requestedTarget: requireString(values["requested-target"], "requested-target"),
|
|
213
228
|
requestedDomainToken: optionalString(values["requested-domain-token"]),
|
|
214
229
|
targetScopeKind,
|
|
@@ -57,6 +57,12 @@ export function appendReviewInvocationRequestArgs(args, request, options) {
|
|
|
57
57
|
if (request.confirmValueAlignment) {
|
|
58
58
|
result.push("--confirm-value-alignment");
|
|
59
59
|
}
|
|
60
|
+
if (request.llmOverride) {
|
|
61
|
+
// Serialize the per-call override as a single JSON passthrough arg — it must
|
|
62
|
+
// survive into the start argv so both the orchestrator profile resolve and
|
|
63
|
+
// the prepare-session bake apply the same overlay (design v4 §2.3).
|
|
64
|
+
result.push("--llm-override", JSON.stringify(request.llmOverride));
|
|
65
|
+
}
|
|
60
66
|
return result;
|
|
61
67
|
}
|
|
62
68
|
function executorRealizationFromRequest(request) {
|
|
@@ -8,7 +8,8 @@ import { generateReviewSessionId } from "../review/materializers.js";
|
|
|
8
8
|
import { fileExists, hasOptionFlag, isDeprecatedDomainAlias, normalizeDomainValue, readMultiOptionValuesFromArgv, readYamlDocument, readSingleOptionValueFromArgv, } from "../review/review-artifact-utils.js";
|
|
9
9
|
import { isOntoRoot, resolveOntoHome } from "../discovery/onto-home.js";
|
|
10
10
|
import { resolveInstallationPath } from "../discovery/installation-paths.js";
|
|
11
|
-
import { REVIEW_EXECUTION_UNIT_IDS, resolveSettingsChain, } from "../discovery/settings-chain.js";
|
|
11
|
+
import { assertSettingsModelsSupported, REVIEW_EXECUTION_UNIT_IDS, resolveSettingsChain, } from "../discovery/settings-chain.js";
|
|
12
|
+
import { applyReviewLlmOverride, parsePerCallLlmOverrideArg, } from "../discovery/llm-override.js";
|
|
12
13
|
import { loadCoreLensRegistry } from "../discovery/lens-registry.js";
|
|
13
14
|
import { isPathInsideRoot, isPathInsideRootRealpathAwareSync, } from "../path-boundary.js";
|
|
14
15
|
import { isDirectModelCallSelection, isExternalOauthWorkerSelection, normalizeLlmModelSwitcher, } from "../llm/model-switcher.js";
|
|
@@ -61,6 +62,9 @@ const KNOWN_PASSTHROUGH_OPTION_NAMES = [
|
|
|
61
62
|
"max-listing-depth",
|
|
62
63
|
"max-listing-entries",
|
|
63
64
|
"max-embed-lines",
|
|
65
|
+
// Per-call LLM override JSON — passthrough so it reaches the prepare-session
|
|
66
|
+
// bake (prepare-review-session.ts) as well as the orchestrator profile resolve.
|
|
67
|
+
"llm-override",
|
|
64
68
|
];
|
|
65
69
|
const KNOWN_PASSTHROUGH_FLAG_NAMES = [
|
|
66
70
|
"codex",
|
|
@@ -2053,9 +2057,29 @@ export async function resolveReviewInvokeSetup(argv) {
|
|
|
2053
2057
|
const ontoHomeFlag = readSingleOptionValueFromArgv(argv, "onto-home");
|
|
2054
2058
|
const ontoHome = resolveOntoHome(ontoHomeFlag);
|
|
2055
2059
|
const projectRoot = path.resolve(readSingleOptionValueFromArgv(argv, "project-root") ?? ".");
|
|
2056
|
-
const
|
|
2060
|
+
const resolvedOntoConfig = ontoHome
|
|
2057
2061
|
? await resolveSettingsChain(ontoHome, projectRoot)
|
|
2058
2062
|
: await readOntoConfig(projectRoot);
|
|
2063
|
+
// Per-call LLM override (design v4): overlay the resolved settings BEFORE the
|
|
2064
|
+
// execution profile is resolved, so the profile route/adapter/model reflects
|
|
2065
|
+
// the effective settings and flows into the spawn `--provider/--model/...`
|
|
2066
|
+
// flags. Identity when absent → default-off is byte-identical. When an override
|
|
2067
|
+
// IS present, fail loud (cause = override) if it selects an unsupported model
|
|
2068
|
+
// (INV-MODEL-1) — the one new review-side model-support gate; it stays scoped
|
|
2069
|
+
// to overrides so an existing (ungated) config's behavior is unchanged.
|
|
2070
|
+
const llmOverride = parsePerCallLlmOverrideArg(readSingleOptionValueFromArgv(argv, "llm-override"));
|
|
2071
|
+
const ontoConfig = llmOverride
|
|
2072
|
+
? applyReviewLlmOverride(resolvedOntoConfig, llmOverride)
|
|
2073
|
+
: resolvedOntoConfig;
|
|
2074
|
+
if (llmOverride) {
|
|
2075
|
+
// Gate ONLY the review dispatch seats. The shared gate walks every seat in
|
|
2076
|
+
// the object it is handed, so passing the whole config would fail a review
|
|
2077
|
+
// because of an unrelated reconstruct model the review never dispatches.
|
|
2078
|
+
// Review resolves its route from `review.execution` actors + units alone
|
|
2079
|
+
// (llmRouteEntries), and salvage lives under review.execution.retry — so the
|
|
2080
|
+
// review block is exactly the set this run can dispatch.
|
|
2081
|
+
assertSettingsModelsSupported(ontoConfig.review ? { review: ontoConfig.review } : {});
|
|
2082
|
+
}
|
|
2059
2083
|
const resolvedInvokeInputs = await resolveReviewInvokeInputs(argvWithSessionId, ontoConfig, projectRoot, sessionId, ontoHome);
|
|
2060
2084
|
const { optionTokens: argvWithoutPositionals } = splitArgvIntoOptionsAndPositionals(argvWithSessionId, [...KNOWN_INVOKE_ONLY_OPTION_NAMES, ...KNOWN_PASSTHROUGH_OPTION_NAMES], [...KNOWN_INVOKE_ONLY_FLAG_NAMES, ...KNOWN_PASSTHROUGH_FLAG_NAMES]);
|
|
2061
2085
|
const normalizedStartArgv = appendReviewInvokeDerivedArgs(stripOptionsFromArgv(argvWithoutPositionals, [...KNOWN_INVOKE_ONLY_OPTION_NAMES], [...KNOWN_INVOKE_ONLY_FLAG_NAMES]), resolvedInvokeInputs);
|