onto-mcp 0.3.0 → 0.3.2
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/authority/core-lexicon.yaml +12 -0
- package/.onto/domains/software-engineering/competency_qs.md +192 -63
- package/.onto/domains/software-engineering/concepts.md +67 -5
- package/.onto/domains/software-engineering/conciseness_rules.md +22 -2
- package/.onto/domains/software-engineering/dependency_rules.md +78 -8
- package/.onto/domains/software-engineering/domain_scope.md +181 -150
- package/.onto/domains/software-engineering/extension_cases.md +318 -542
- package/.onto/domains/software-engineering/logic_rules.md +75 -3
- package/.onto/domains/software-engineering/problem_framing_profile.md +29 -2
- package/.onto/domains/software-engineering/prompt_interface.md +122 -0
- package/.onto/domains/software-engineering/structure_spec.md +53 -4
- package/.onto/principles/llm-native-development-guideline.md +20 -0
- package/.onto/principles/productization-charter.md +6 -0
- package/.onto/processes/evolve/material-kind-adapter-contract.md +6 -0
- package/.onto/processes/reconstruct/reconstruct-boundary-contract.md +468 -81
- package/.onto/processes/reconstruct/reconstruct-execution-ux-contract.md +177 -0
- package/.onto/processes/reconstruct/source-profile-contract.md +39 -6
- package/.onto/processes/reconstruct/top-level-concept-discovery-contract.md +387 -0
- package/.onto/processes/review/binding-contract.md +8 -0
- package/.onto/processes/review/lens-registry.md +16 -0
- package/.onto/processes/review/pre-dispatch-contracts.md +34 -13
- package/.onto/processes/review/productized-live-path.md +3 -1
- package/.onto/processes/shared/pipeline-execution-ledger-contract.md +185 -0
- package/.onto/processes/shared/target-material-kind-contract.md +24 -2
- package/.onto/roles/axiology.md +7 -2
- package/AGENTS.md +4 -2
- package/README.md +52 -29
- package/dist/core-api/reconstruct-api.js +92 -5
- package/dist/core-api/review-api.js +1744 -371
- package/dist/core-runtime/cli/mock-review-unit-executor.js +17 -0
- package/dist/core-runtime/cli/render-review-final-output.js +9 -0
- package/dist/core-runtime/cli/review-invoke.js +387 -55
- package/dist/core-runtime/cli/run-review-prompt-execution.js +361 -90
- package/dist/core-runtime/path-boundary.js +58 -0
- package/dist/core-runtime/pipeline-execution-ledger.js +100 -0
- package/dist/core-runtime/reconstruct/artifact-types.js +33 -1
- package/dist/core-runtime/reconstruct/materialize-preparation.js +54 -4
- package/dist/core-runtime/reconstruct/pipeline-execution-ledger.js +342 -0
- package/dist/core-runtime/reconstruct/post-seed-validation.js +630 -0
- package/dist/core-runtime/reconstruct/record.js +105 -1
- package/dist/core-runtime/reconstruct/run.js +1594 -38
- package/dist/core-runtime/reconstruct/seed-candidate-validation.js +29 -0
- package/dist/core-runtime/review/continuation-plan.js +160 -0
- package/dist/core-runtime/review/execution-plan-boundary.js +123 -0
- package/dist/core-runtime/review/materializers.js +8 -3
- package/dist/core-runtime/review/pipeline-execution-ledger.js +250 -0
- package/dist/core-runtime/review/review-artifact-utils.js +15 -2
- package/dist/core-runtime/review/review-invocation-runner.js +604 -0
- package/dist/core-runtime/target-material-kind.js +43 -5
- package/dist/mcp/server.js +289 -59
- package/dist/mcp/tool-schemas.js +28 -2
- package/package.json +4 -2
- package/.onto/domains/llm-native-development/competency_qs.md +0 -430
- package/.onto/domains/llm-native-development/concepts.md +0 -242
- package/.onto/domains/llm-native-development/conciseness_rules.md +0 -163
- package/.onto/domains/llm-native-development/dependency_rules.md +0 -216
- package/.onto/domains/llm-native-development/domain_scope.md +0 -197
- package/.onto/domains/llm-native-development/extension_cases.md +0 -474
- package/.onto/domains/llm-native-development/logic_rules.md +0 -123
- package/.onto/domains/llm-native-development/prompt_interface.md +0 -49
- package/.onto/domains/llm-native-development/structure_spec.md +0 -245
|
@@ -1,14 +1,40 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
1
2
|
import fs from "node:fs/promises";
|
|
2
3
|
import os from "node:os";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { resolveOntoHome } from "../core-runtime/discovery/onto-home.js";
|
|
5
6
|
import { loadCoreLensRegistry } from "../core-runtime/discovery/lens-registry.js";
|
|
6
|
-
import { fileExists, isoFromTimestamp, isoNow, readYamlDocument, } from "../core-runtime/review/review-artifact-utils.js";
|
|
7
|
+
import { fileExists, isDeprecatedDomainAlias, isoFromTimestamp, isoNow, normalizeDomainValue, readYamlDocument, writeYamlDocument, } from "../core-runtime/review/review-artifact-utils.js";
|
|
8
|
+
import { assertPathInsideRoot, realpathIfExists, } from "../core-runtime/path-boundary.js";
|
|
9
|
+
import { assertReviewExecutionPlanSessionBoundary, } from "../core-runtime/review/execution-plan-boundary.js";
|
|
7
10
|
import { buildReviewRouteVisibilityFromSession, } from "../core-runtime/review/route-visibility.js";
|
|
8
11
|
import { readValidatedReviewRecord } from "../core-runtime/review/review-record-validation.js";
|
|
9
12
|
import { readReviewResultClassification } from "../core-runtime/review/review-result-classification.js";
|
|
10
13
|
import { REVIEW_EXECUTION_STEP_IDS, REVIEW_PROGRESS_STEPS, REVIEW_PROGRESS_TOTAL_STEPS, reviewProgressStepById, reviewProgressStepIdFromHalt, } from "../core-runtime/review/review-progress-contract.js";
|
|
11
|
-
import {
|
|
14
|
+
import { collectReviewInvocationArtifactRefs, prepareReviewInvocationRequest, runReviewInvocation, } from "../core-runtime/review/review-invocation-runner.js";
|
|
15
|
+
import { completeReviewSession } from "../core-runtime/cli/complete-review-session.js";
|
|
16
|
+
import { buildExecutorConfigFromRealization, } from "../core-runtime/cli/review-invoke.js";
|
|
17
|
+
import { executeReviewPromptExecution, } from "../core-runtime/cli/run-review-prompt-execution.js";
|
|
18
|
+
import { buildReviewPipelineExecutionLedger, } from "../core-runtime/review/pipeline-execution-ledger.js";
|
|
19
|
+
import { buildReviewContinuationPlan, } from "../core-runtime/review/continuation-plan.js";
|
|
20
|
+
export class ReviewContinuationError extends Error {
|
|
21
|
+
failureContent;
|
|
22
|
+
originalError;
|
|
23
|
+
constructor(args) {
|
|
24
|
+
super(args.message);
|
|
25
|
+
this.name = "ReviewContinuationError";
|
|
26
|
+
this.originalError = args.originalError;
|
|
27
|
+
this.failureContent = args.failureContent;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export class ReviewDomainResolutionError extends Error {
|
|
31
|
+
domainResolution;
|
|
32
|
+
constructor(args) {
|
|
33
|
+
super(args.message);
|
|
34
|
+
this.name = "ReviewDomainResolutionError";
|
|
35
|
+
this.domainResolution = args.domainResolution;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
12
38
|
function stringifyConsoleArgs(args) {
|
|
13
39
|
return args
|
|
14
40
|
.map((arg) => {
|
|
@@ -74,53 +100,6 @@ async function withCapturedConsole(action, observer) {
|
|
|
74
100
|
function resolveRequiredOntoHome(explicit) {
|
|
75
101
|
return resolveOntoHome(explicit);
|
|
76
102
|
}
|
|
77
|
-
function appendCommonReviewArgs(args, request, ontoHome) {
|
|
78
|
-
const result = [
|
|
79
|
-
...args,
|
|
80
|
-
request.target,
|
|
81
|
-
request.intent,
|
|
82
|
-
"--project-root",
|
|
83
|
-
path.resolve(request.projectRoot),
|
|
84
|
-
];
|
|
85
|
-
result.push("--onto-home", ontoHome);
|
|
86
|
-
if (request.domain && request.noDomain) {
|
|
87
|
-
throw new Error("Use either domain or noDomain, not both.");
|
|
88
|
-
}
|
|
89
|
-
if (request.domain) {
|
|
90
|
-
result.push("--domain", request.domain);
|
|
91
|
-
}
|
|
92
|
-
if (request.noDomain) {
|
|
93
|
-
result.push("--no-domain");
|
|
94
|
-
}
|
|
95
|
-
if (request.reviewMode) {
|
|
96
|
-
result.push("--review-mode", request.reviewMode);
|
|
97
|
-
}
|
|
98
|
-
if (request.targetScopeKind) {
|
|
99
|
-
result.push("--target-scope-kind", request.targetScopeKind);
|
|
100
|
-
}
|
|
101
|
-
if (request.primaryRef) {
|
|
102
|
-
result.push("--primary-ref", request.primaryRef);
|
|
103
|
-
}
|
|
104
|
-
for (const memberRef of request.memberRefs ?? []) {
|
|
105
|
-
result.push("--member-ref", memberRef);
|
|
106
|
-
}
|
|
107
|
-
if (request.bundleKind) {
|
|
108
|
-
result.push("--bundle-kind", request.bundleKind);
|
|
109
|
-
}
|
|
110
|
-
if (request.diffRange) {
|
|
111
|
-
result.push("--diff-range", request.diffRange);
|
|
112
|
-
}
|
|
113
|
-
if (request.executorRealization) {
|
|
114
|
-
result.push("--executor-realization", request.executorRealization);
|
|
115
|
-
}
|
|
116
|
-
for (const lensId of request.lensIds ?? []) {
|
|
117
|
-
result.push("--lens-id", lensId);
|
|
118
|
-
}
|
|
119
|
-
if (request.confirmValueAlignment) {
|
|
120
|
-
result.push("--confirm-value-alignment");
|
|
121
|
-
}
|
|
122
|
-
return result;
|
|
123
|
-
}
|
|
124
103
|
function basenameSessionId(sessionRoot) {
|
|
125
104
|
return path.basename(path.resolve(sessionRoot));
|
|
126
105
|
}
|
|
@@ -132,19 +111,63 @@ async function readOptionalYaml(filePath) {
|
|
|
132
111
|
async function readOptionalReviewRecord(filePath) {
|
|
133
112
|
if (!(await fileExists(filePath)))
|
|
134
113
|
return null;
|
|
135
|
-
|
|
114
|
+
try {
|
|
115
|
+
return await readValidatedReviewRecord(filePath);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
136
120
|
}
|
|
137
121
|
async function readOptionalText(filePath) {
|
|
138
122
|
if (!(await fileExists(filePath)))
|
|
139
123
|
return undefined;
|
|
140
124
|
return fs.readFile(filePath, "utf8");
|
|
141
125
|
}
|
|
126
|
+
async function resolveReviewRecordFinalOutputPath(args) {
|
|
127
|
+
const sessionRoot = path.resolve(args.sessionRoot);
|
|
128
|
+
const rawRef = args.finalOutputRef ?? path.join(sessionRoot, "final-output.md");
|
|
129
|
+
const candidates = path.isAbsolute(rawRef)
|
|
130
|
+
? [path.resolve(rawRef)]
|
|
131
|
+
: [
|
|
132
|
+
path.resolve(sessionRoot, rawRef),
|
|
133
|
+
...(args.projectRoot ? [path.resolve(args.projectRoot, rawRef)] : []),
|
|
134
|
+
];
|
|
135
|
+
for (const candidate of candidates) {
|
|
136
|
+
if (!(await fileExists(candidate)))
|
|
137
|
+
continue;
|
|
138
|
+
await assertPathInsideRoot({
|
|
139
|
+
root: sessionRoot,
|
|
140
|
+
candidate,
|
|
141
|
+
label: "ReviewRecord.final_output_ref",
|
|
142
|
+
});
|
|
143
|
+
return candidate;
|
|
144
|
+
}
|
|
145
|
+
const fallback = candidates[0] ?? path.join(sessionRoot, "final-output.md");
|
|
146
|
+
await assertPathInsideRoot({
|
|
147
|
+
root: sessionRoot,
|
|
148
|
+
candidate: fallback,
|
|
149
|
+
label: "ReviewRecord.final_output_ref",
|
|
150
|
+
});
|
|
151
|
+
return fallback;
|
|
152
|
+
}
|
|
153
|
+
async function assertSamePath(args) {
|
|
154
|
+
const expected = path.resolve(args.expected);
|
|
155
|
+
const actual = path.resolve(args.actual);
|
|
156
|
+
if (expected === actual)
|
|
157
|
+
return;
|
|
158
|
+
const realExpected = await realpathIfExists(expected);
|
|
159
|
+
const realActual = await realpathIfExists(actual);
|
|
160
|
+
if (realExpected && realActual && path.resolve(realExpected) === path.resolve(realActual)) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
throw new Error(`${args.label} mismatch: expected ${expected}, received ${actual}`);
|
|
164
|
+
}
|
|
142
165
|
function buildOpeningBriefPresentation(input) {
|
|
143
166
|
return {
|
|
144
167
|
prompt: [
|
|
145
168
|
"Explain this onto review opening brief to the user before execution.",
|
|
146
169
|
"Use only the provided input facts. Do not infer or invent target scope, boundary, domain, lens set, model, provider, or execution mode.",
|
|
147
|
-
"Cover: what is being reviewed, why, filesystem boundary, selected domain, review mode and lens set, execution path, model/provider settings, and where the user can change configuration.",
|
|
170
|
+
"Cover: what is being reviewed, why, filesystem boundary, selected domain and domain selection reason, review mode and lens set, execution path, model/provider settings, and where the user can change configuration.",
|
|
148
171
|
"Keep it structured and concise. Use the user's conversation language.",
|
|
149
172
|
].join("\n"),
|
|
150
173
|
input,
|
|
@@ -180,6 +203,399 @@ const OPENING_PRESENTATION_SOURCE_REF_KEYS = [
|
|
|
180
203
|
"review_target_profile",
|
|
181
204
|
"review_context_manifest",
|
|
182
205
|
];
|
|
206
|
+
const ACTIVE_REVIEW_ATTEMPT_FILENAME = "active-review-attempt.yaml";
|
|
207
|
+
const ENVIRONMENT_WARNINGS_FILENAME = "environment-warnings.yaml";
|
|
208
|
+
const REVIEW_CANCEL_REQUEST_FILENAME = "review-cancel-request.yaml";
|
|
209
|
+
const DEFAULT_ACTIVE_ATTEMPT_STALE_AFTER_SECONDS = 1_200;
|
|
210
|
+
const REVIEW_RUNNER_WARNING_PREFIX = "[review runner warning]";
|
|
211
|
+
function activeAttemptPath(sessionRoot) {
|
|
212
|
+
return path.join(sessionRoot, ACTIVE_REVIEW_ATTEMPT_FILENAME);
|
|
213
|
+
}
|
|
214
|
+
function environmentWarningsPath(sessionRoot) {
|
|
215
|
+
return path.join(sessionRoot, ENVIRONMENT_WARNINGS_FILENAME);
|
|
216
|
+
}
|
|
217
|
+
function reviewCancelRequestPath(sessionRoot) {
|
|
218
|
+
return path.join(sessionRoot, REVIEW_CANCEL_REQUEST_FILENAME);
|
|
219
|
+
}
|
|
220
|
+
function activeAttemptStaleAfterSeconds() {
|
|
221
|
+
const parsed = Number(process.env.ONTO_REVIEW_ACTIVE_ATTEMPT_STALE_AFTER_SECONDS);
|
|
222
|
+
return Number.isFinite(parsed) && parsed > 0
|
|
223
|
+
? Math.floor(parsed)
|
|
224
|
+
: DEFAULT_ACTIVE_ATTEMPT_STALE_AFTER_SECONDS;
|
|
225
|
+
}
|
|
226
|
+
function stripDomainTokenValue(domainValue) {
|
|
227
|
+
const trimmed = domainValue.trim();
|
|
228
|
+
return trimmed.startsWith("@") ? trimmed.slice(1) : trimmed;
|
|
229
|
+
}
|
|
230
|
+
function stableJson(value) {
|
|
231
|
+
if (Array.isArray(value)) {
|
|
232
|
+
return `[${value.map((item) => stableJson(item)).join(",")}]`;
|
|
233
|
+
}
|
|
234
|
+
if (value && typeof value === "object") {
|
|
235
|
+
const record = value;
|
|
236
|
+
return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stableJson(record[key])}`).join(",")}}`;
|
|
237
|
+
}
|
|
238
|
+
return JSON.stringify(value);
|
|
239
|
+
}
|
|
240
|
+
function normalizeRefForHash(ref) {
|
|
241
|
+
if (!ref)
|
|
242
|
+
return null;
|
|
243
|
+
return path.normalize(ref).split(path.sep).join(path.posix.sep);
|
|
244
|
+
}
|
|
245
|
+
function canonicalReviewRequestIdentity(input) {
|
|
246
|
+
if (!input.target || !input.intent)
|
|
247
|
+
return null;
|
|
248
|
+
return {
|
|
249
|
+
schema: "review-request-identity-v2",
|
|
250
|
+
target: normalizeRefForHash(input.target),
|
|
251
|
+
intent: input.intent,
|
|
252
|
+
domain: input.domain ? normalizeDomainValue(input.domain) : null,
|
|
253
|
+
targetScopeKind: input.targetScopeKind,
|
|
254
|
+
primaryRef: normalizeRefForHash(input.primaryRef ?? undefined),
|
|
255
|
+
memberRefs: input.memberRefs.map((ref) => normalizeRefForHash(ref) ?? ref).sort(),
|
|
256
|
+
bundleKind: input.bundleKind,
|
|
257
|
+
reviewMode: input.reviewMode,
|
|
258
|
+
lensIds: [...input.lensIds].sort(),
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function hashReviewRequestIdentity(identity) {
|
|
262
|
+
if (!identity)
|
|
263
|
+
return null;
|
|
264
|
+
return crypto.createHash("sha256").update(stableJson(identity)).digest("hex");
|
|
265
|
+
}
|
|
266
|
+
function requestHashForReviewInput(args) {
|
|
267
|
+
return hashReviewRequestIdentity(canonicalReviewRequestIdentity({
|
|
268
|
+
target: args.target,
|
|
269
|
+
intent: args.intent,
|
|
270
|
+
domain: args.noDomain ? "none" : args.domain ?? null,
|
|
271
|
+
targetScopeKind: args.targetScopeKind ?? null,
|
|
272
|
+
primaryRef: args.primaryRef ?? null,
|
|
273
|
+
memberRefs: args.memberRefs ?? [],
|
|
274
|
+
bundleKind: args.bundleKind ?? null,
|
|
275
|
+
reviewMode: args.reviewMode ?? null,
|
|
276
|
+
lensIds: args.lensIds ?? [],
|
|
277
|
+
}));
|
|
278
|
+
}
|
|
279
|
+
function requestHashFromArtifacts(args) {
|
|
280
|
+
const scope = args.interpretation?.target_scope_candidate;
|
|
281
|
+
return hashReviewRequestIdentity(canonicalReviewRequestIdentity({
|
|
282
|
+
target: args.metadata?.requested_target ?? null,
|
|
283
|
+
intent: args.interpretation?.intent_summary ?? null,
|
|
284
|
+
domain: args.binding?.resolved_session_domain ??
|
|
285
|
+
args.metadata?.requested_domain_token ??
|
|
286
|
+
null,
|
|
287
|
+
targetScopeKind: scope?.kind ?? null,
|
|
288
|
+
primaryRef: scope?.primary_ref ?? null,
|
|
289
|
+
memberRefs: scope?.member_refs ?? [],
|
|
290
|
+
bundleKind: scope?.bundle_kind ?? null,
|
|
291
|
+
reviewMode: args.binding?.resolved_review_mode ??
|
|
292
|
+
args.interpretation?.review_mode_recommendation ??
|
|
293
|
+
null,
|
|
294
|
+
lensIds: args.binding?.resolved_lens_set ??
|
|
295
|
+
args.interpretation?.lens_selection_plan?.recommended_lenses ??
|
|
296
|
+
[],
|
|
297
|
+
}));
|
|
298
|
+
}
|
|
299
|
+
function domainTokenResolution(args) {
|
|
300
|
+
const requestedToken = args.requestedToken ?? "";
|
|
301
|
+
const stripped = stripDomainTokenValue(requestedToken);
|
|
302
|
+
const normalized = args.normalizedDomain ?? null;
|
|
303
|
+
const suggestionIds = args.suggestionIds ?? [];
|
|
304
|
+
if (normalized === "none") {
|
|
305
|
+
return {
|
|
306
|
+
requestedToken,
|
|
307
|
+
normalizedDomain: null,
|
|
308
|
+
resolution: "no_domain",
|
|
309
|
+
suggestionIds,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
if (!normalized) {
|
|
313
|
+
return {
|
|
314
|
+
requestedToken,
|
|
315
|
+
normalizedDomain: null,
|
|
316
|
+
resolution: suggestionIds.length > 0 ? "suggestion" : "unknown",
|
|
317
|
+
suggestionIds,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
requestedToken,
|
|
322
|
+
normalizedDomain: normalized,
|
|
323
|
+
resolution: stripped.length > 0 && normalizeDomainValue(stripped) !== stripped
|
|
324
|
+
? "alias"
|
|
325
|
+
: "exact",
|
|
326
|
+
suggestionIds,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
async function availableDomainIds(projectRoot, ontoHome) {
|
|
330
|
+
const roots = [
|
|
331
|
+
path.join(path.resolve(projectRoot), ".onto", "domains"),
|
|
332
|
+
path.join(os.homedir(), ".onto", "domains"),
|
|
333
|
+
path.join(ontoHome, ".onto", "domains"),
|
|
334
|
+
];
|
|
335
|
+
const ids = new Set();
|
|
336
|
+
for (const root of roots) {
|
|
337
|
+
for (const id of await listDomainDirs(root)) {
|
|
338
|
+
ids.add(id);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return [...ids].sort();
|
|
342
|
+
}
|
|
343
|
+
function domainSimilarityScore(requested, candidate) {
|
|
344
|
+
const requestedTokens = new Set(requested.toLowerCase().split(/[-_\s]+/).filter(Boolean));
|
|
345
|
+
const candidateTokens = new Set(candidate.toLowerCase().split(/[-_\s]+/).filter(Boolean));
|
|
346
|
+
let overlap = 0;
|
|
347
|
+
for (const token of requestedTokens) {
|
|
348
|
+
if (candidateTokens.has(token))
|
|
349
|
+
overlap += 1;
|
|
350
|
+
}
|
|
351
|
+
if (candidate.toLowerCase().includes(requested.toLowerCase()) ||
|
|
352
|
+
requested.toLowerCase().includes(candidate.toLowerCase())) {
|
|
353
|
+
overlap += 2;
|
|
354
|
+
}
|
|
355
|
+
return overlap;
|
|
356
|
+
}
|
|
357
|
+
function suggestDomainIds(requestedToken, availableIds) {
|
|
358
|
+
const stripped = stripDomainTokenValue(requestedToken).toLowerCase();
|
|
359
|
+
if (!stripped)
|
|
360
|
+
return [];
|
|
361
|
+
return availableIds
|
|
362
|
+
.map((id) => ({ id, score: domainSimilarityScore(stripped, id) }))
|
|
363
|
+
.filter((entry) => entry.score > 0)
|
|
364
|
+
.sort((a, b) => b.score - a.score || a.id.localeCompare(b.id))
|
|
365
|
+
.slice(0, 5)
|
|
366
|
+
.map((entry) => entry.id);
|
|
367
|
+
}
|
|
368
|
+
async function validateRequestedDomainForDispatch(request, ontoHome) {
|
|
369
|
+
if (!request.domain || request.noDomain)
|
|
370
|
+
return;
|
|
371
|
+
const normalizedDomain = normalizeDomainValue(request.domain);
|
|
372
|
+
if (normalizedDomain === "none")
|
|
373
|
+
return;
|
|
374
|
+
const domains = await availableDomainIds(request.projectRoot, ontoHome);
|
|
375
|
+
if (domains.includes(normalizedDomain))
|
|
376
|
+
return;
|
|
377
|
+
const suggestionIds = suggestDomainIds(request.domain, domains);
|
|
378
|
+
throw new ReviewDomainResolutionError({
|
|
379
|
+
message: suggestionIds.length > 0
|
|
380
|
+
? `Unknown review domain ${request.domain}. Did you mean: ${suggestionIds.join(", ")}?`
|
|
381
|
+
: `Unknown review domain ${request.domain}; no safe domain suggestion is available.`,
|
|
382
|
+
domainResolution: domainTokenResolution({
|
|
383
|
+
requestedToken: request.domain,
|
|
384
|
+
normalizedDomain: null,
|
|
385
|
+
suggestionIds,
|
|
386
|
+
}),
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
function reviewTerminalStatus(status) {
|
|
390
|
+
return (status === "completed" ||
|
|
391
|
+
status === "completed_with_degradation" ||
|
|
392
|
+
status === "halted_partial" ||
|
|
393
|
+
status === "failed");
|
|
394
|
+
}
|
|
395
|
+
async function readTargetMaterialSupport(sessionRoot, executionPlan) {
|
|
396
|
+
const targetProfilePath = executionPlan?.review_target_profile_path ??
|
|
397
|
+
path.join(sessionRoot, "execution-preparation", "review-target-profile.yaml");
|
|
398
|
+
const targetProfile = await readOptionalYaml(targetProfilePath);
|
|
399
|
+
const profile = targetProfile?.material_profile;
|
|
400
|
+
if (!profile)
|
|
401
|
+
return null;
|
|
402
|
+
return {
|
|
403
|
+
targetMaterialKind: profile.target_material_kind,
|
|
404
|
+
supportStatus: profile.support_status,
|
|
405
|
+
unsupportedReason: profile.unsupported_reason,
|
|
406
|
+
detectionConfidence: profile.detection.confidence,
|
|
407
|
+
detectionConfidenceBasis: profile.detection.confidence_basis,
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
async function readEnvironmentWarnings(sessionRoot) {
|
|
411
|
+
const artifact = await readOptionalYaml(environmentWarningsPath(sessionRoot));
|
|
412
|
+
return artifact?.warnings ?? [];
|
|
413
|
+
}
|
|
414
|
+
async function writeEnvironmentWarningsFromStderr(args) {
|
|
415
|
+
const warningLines = args.stderr
|
|
416
|
+
.map((line) => line.trim())
|
|
417
|
+
.filter((line) => line.startsWith(REVIEW_RUNNER_WARNING_PREFIX))
|
|
418
|
+
.map((line) => line.slice(REVIEW_RUNNER_WARNING_PREFIX.length).trim())
|
|
419
|
+
.filter((line) => line.length > 0);
|
|
420
|
+
if (warningLines.length === 0)
|
|
421
|
+
return [];
|
|
422
|
+
const existing = await readEnvironmentWarnings(args.sessionRoot);
|
|
423
|
+
const seenMessages = new Set(existing.map((warning) => warning.message));
|
|
424
|
+
const observedAt = isoNow();
|
|
425
|
+
const additions = [];
|
|
426
|
+
for (const [index, message] of warningLines.entries()) {
|
|
427
|
+
if (seenMessages.has(message))
|
|
428
|
+
continue;
|
|
429
|
+
const digest = crypto
|
|
430
|
+
.createHash("sha256")
|
|
431
|
+
.update(`${observedAt}\n${index}\n${message}`)
|
|
432
|
+
.digest("hex")
|
|
433
|
+
.slice(0, 12);
|
|
434
|
+
additions.push({
|
|
435
|
+
warningId: `environment-warning-${digest}`,
|
|
436
|
+
source: "review_runner_warning",
|
|
437
|
+
message,
|
|
438
|
+
fatality: "non_fatal",
|
|
439
|
+
affectedCapability: "review_execution_observability",
|
|
440
|
+
outputTrustImpact: "unknown",
|
|
441
|
+
observedAt,
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
const warnings = [...existing, ...additions];
|
|
445
|
+
if (warnings.length > 0) {
|
|
446
|
+
await writeYamlDocument(environmentWarningsPath(args.sessionRoot), {
|
|
447
|
+
schema_version: "1",
|
|
448
|
+
session_id: basenameSessionId(args.sessionRoot),
|
|
449
|
+
created_at: observedAt,
|
|
450
|
+
warnings,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
return warnings;
|
|
454
|
+
}
|
|
455
|
+
function activeUnitsForInitialReview(executionPlan) {
|
|
456
|
+
const lensIds = (executionPlan?.lens_execution_seats ?? []).map((seat) => seat.lens_id);
|
|
457
|
+
return lensIds.length > 0
|
|
458
|
+
? lensIds.map((lensId) => `lens:${lensId}`)
|
|
459
|
+
: ["review_execution"];
|
|
460
|
+
}
|
|
461
|
+
function requestedUnitsMatchActive(activeUnits, targetUnits) {
|
|
462
|
+
if (activeUnits.length === 0)
|
|
463
|
+
return false;
|
|
464
|
+
if (!targetUnits || targetUnits.length === 0)
|
|
465
|
+
return true;
|
|
466
|
+
const normalizedActive = new Set(activeUnits.flatMap((unit) => {
|
|
467
|
+
const suffix = unit.includes(":") ? unit.split(":").at(-1) ?? unit : unit;
|
|
468
|
+
return [unit, suffix];
|
|
469
|
+
}));
|
|
470
|
+
return targetUnits.some((unit) => normalizedActive.has(unit));
|
|
471
|
+
}
|
|
472
|
+
async function writeActiveAttemptStarted(args) {
|
|
473
|
+
const sessionMetadata = await readOptionalYaml(path.join(args.sessionRoot, "session-metadata.yaml"));
|
|
474
|
+
const artifactRefs = await collectArtifactRefs(args.sessionRoot);
|
|
475
|
+
const observed = await artifactObservation(artifactRefs);
|
|
476
|
+
const now = isoNow();
|
|
477
|
+
const artifact = {
|
|
478
|
+
schema_version: "1",
|
|
479
|
+
attempt_id: args.attemptId,
|
|
480
|
+
attempt_kind: args.attemptKind,
|
|
481
|
+
session_id: sessionMetadata?.session_id ?? basenameSessionId(args.sessionRoot),
|
|
482
|
+
session_root: args.sessionRoot,
|
|
483
|
+
project_root: sessionMetadata?.project_root ?? null,
|
|
484
|
+
created_at: now,
|
|
485
|
+
updated_at: now,
|
|
486
|
+
status: "started",
|
|
487
|
+
active_units: args.activeUnits,
|
|
488
|
+
requested_frontier_units: args.requestedFrontierUnits ?? [],
|
|
489
|
+
run_control: {
|
|
490
|
+
stale_after_seconds: activeAttemptStaleAfterSeconds(),
|
|
491
|
+
source_tool: args.sourceTool,
|
|
492
|
+
request_hash: args.requestHash,
|
|
493
|
+
},
|
|
494
|
+
latest_observed_artifact_ref: observed.ref,
|
|
495
|
+
};
|
|
496
|
+
await writeYamlDocument(activeAttemptPath(args.sessionRoot), artifact);
|
|
497
|
+
}
|
|
498
|
+
async function updateActiveAttemptTerminal(args) {
|
|
499
|
+
const attemptPath = activeAttemptPath(args.sessionRoot);
|
|
500
|
+
const existing = await readOptionalYaml(attemptPath);
|
|
501
|
+
if (!existing)
|
|
502
|
+
return;
|
|
503
|
+
const artifactRefs = await collectArtifactRefs(args.sessionRoot);
|
|
504
|
+
const observed = await artifactObservation(artifactRefs);
|
|
505
|
+
await writeYamlDocument(attemptPath, {
|
|
506
|
+
...existing,
|
|
507
|
+
updated_at: isoNow(),
|
|
508
|
+
status: args.status,
|
|
509
|
+
latest_observed_artifact_ref: observed.ref,
|
|
510
|
+
...(args.errorMessage !== undefined ? { error_message: args.errorMessage } : {}),
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
async function activeAttemptProjection(sessionRoot) {
|
|
514
|
+
const attemptPath = activeAttemptPath(sessionRoot);
|
|
515
|
+
const artifact = await readOptionalYaml(attemptPath);
|
|
516
|
+
if (!artifact)
|
|
517
|
+
return null;
|
|
518
|
+
const updatedMs = parseTimestampMs(artifact.updated_at);
|
|
519
|
+
const secondsSinceUpdated = secondsBetween(Date.now(), updatedMs);
|
|
520
|
+
const staleAfterSeconds = artifact.run_control?.stale_after_seconds ?? DEFAULT_ACTIVE_ATTEMPT_STALE_AFTER_SECONDS;
|
|
521
|
+
const isStale = artifact.status === "started" &&
|
|
522
|
+
secondsSinceUpdated !== null &&
|
|
523
|
+
secondsSinceUpdated > staleAfterSeconds;
|
|
524
|
+
return {
|
|
525
|
+
attemptId: artifact.attempt_id,
|
|
526
|
+
attemptKind: artifact.attempt_kind,
|
|
527
|
+
status: artifact.status,
|
|
528
|
+
sessionId: artifact.session_id,
|
|
529
|
+
sessionRoot: artifact.session_root,
|
|
530
|
+
startedAt: artifact.created_at,
|
|
531
|
+
updatedAt: artifact.updated_at,
|
|
532
|
+
activeUnits: artifact.active_units ?? [],
|
|
533
|
+
requestedFrontierUnits: artifact.requested_frontier_units ?? [],
|
|
534
|
+
latestObservedArtifactRef: artifact.latest_observed_artifact_ref ?? null,
|
|
535
|
+
staleAfterSeconds,
|
|
536
|
+
secondsSinceUpdated,
|
|
537
|
+
isStale,
|
|
538
|
+
attemptManifestRef: attemptPath,
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
async function buildRunControl(sessionRoot, status) {
|
|
542
|
+
const activeAttempt = await activeAttemptProjection(sessionRoot);
|
|
543
|
+
const cancellationRequestRef = await fileExists(reviewCancelRequestPath(sessionRoot))
|
|
544
|
+
? reviewCancelRequestPath(sessionRoot)
|
|
545
|
+
: null;
|
|
546
|
+
const cancellationRequested = cancellationRequestRef !== null;
|
|
547
|
+
const alreadyRunning = status === "running" &&
|
|
548
|
+
activeAttempt?.status === "started" &&
|
|
549
|
+
!activeAttempt.isStale;
|
|
550
|
+
const lifecycleState = status === "completed" || status === "completed_with_degradation"
|
|
551
|
+
? "completed"
|
|
552
|
+
: status === "halted_partial"
|
|
553
|
+
? "halted"
|
|
554
|
+
: activeAttempt?.status === "failed"
|
|
555
|
+
? "failed_attempt"
|
|
556
|
+
: activeAttempt?.status === "started" && activeAttempt.isStale
|
|
557
|
+
? "stale_active"
|
|
558
|
+
: cancellationRequested
|
|
559
|
+
? "cancellation_requested"
|
|
560
|
+
: alreadyRunning
|
|
561
|
+
? "active"
|
|
562
|
+
: status === "prepared"
|
|
563
|
+
? "prepared"
|
|
564
|
+
: "unknown";
|
|
565
|
+
const continuationAvailable = status === "prepared" ||
|
|
566
|
+
status === "halted_partial" ||
|
|
567
|
+
lifecycleState === "failed_attempt" ||
|
|
568
|
+
lifecycleState === "stale_active";
|
|
569
|
+
const cancellationAvailable = alreadyRunning && !cancellationRequested;
|
|
570
|
+
const statusReason = lifecycleState === "active"
|
|
571
|
+
? "review attempt is actively running and can be cancelled"
|
|
572
|
+
: lifecycleState === "cancellation_requested"
|
|
573
|
+
? "cancellation has already been requested and will be observed at a runtime checkpoint"
|
|
574
|
+
: lifecycleState === "stale_active"
|
|
575
|
+
? "active attempt is stale; use review_status evidence before continuing"
|
|
576
|
+
: lifecycleState === "failed_attempt"
|
|
577
|
+
? "active attempt failed before a stronger terminal execution artifact was written"
|
|
578
|
+
: lifecycleState === "prepared"
|
|
579
|
+
? "review is prepared but no worker attempt is active"
|
|
580
|
+
: lifecycleState === "halted"
|
|
581
|
+
? "review has halted through execution artifacts"
|
|
582
|
+
: lifecycleState === "completed"
|
|
583
|
+
? "review is terminally completed"
|
|
584
|
+
: "no actionable run-control state is available";
|
|
585
|
+
return {
|
|
586
|
+
activeAttempt,
|
|
587
|
+
lifecycleState,
|
|
588
|
+
alreadyRunning,
|
|
589
|
+
cancellationAvailable,
|
|
590
|
+
cancellationRequested,
|
|
591
|
+
cancellationRequestRef,
|
|
592
|
+
continuationAvailable,
|
|
593
|
+
retryAvailable: continuationAvailable,
|
|
594
|
+
retrySemantics: "use_review_continue",
|
|
595
|
+
hostTimeoutSemantics: "review_continues_under_session",
|
|
596
|
+
statusReason,
|
|
597
|
+
};
|
|
598
|
+
}
|
|
183
599
|
function compactSeverityCounts(summary) {
|
|
184
600
|
return [
|
|
185
601
|
`blocker=${summary.severity_counts.blocker}`,
|
|
@@ -213,140 +629,6 @@ function buildHaltPresentation(input) {
|
|
|
213
629
|
input,
|
|
214
630
|
};
|
|
215
631
|
}
|
|
216
|
-
function progressEvent(args) {
|
|
217
|
-
return {
|
|
218
|
-
presentation_contract_version: REVIEW_PRESENTATION_CONTRACT_VERSION,
|
|
219
|
-
event_kind: "mcp_progress",
|
|
220
|
-
sequence: args.sequence,
|
|
221
|
-
generated_at: isoNow(),
|
|
222
|
-
source: args.source,
|
|
223
|
-
stage: args.stage,
|
|
224
|
-
session_root: args.sessionRoot,
|
|
225
|
-
message: args.message,
|
|
226
|
-
progress: {
|
|
227
|
-
current: args.current,
|
|
228
|
-
total: args.total ?? 100,
|
|
229
|
-
...(args.exactStep !== undefined ? { exact_step: args.exactStep } : {}),
|
|
230
|
-
...(args.exactTotal !== undefined ? { exact_total: args.exactTotal } : {}),
|
|
231
|
-
...(args.label !== undefined ? { label: args.label } : {}),
|
|
232
|
-
},
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
function progressUnitsForInvokeStep(step) {
|
|
236
|
-
switch (step) {
|
|
237
|
-
case 1:
|
|
238
|
-
return 5;
|
|
239
|
-
case 2:
|
|
240
|
-
return 10;
|
|
241
|
-
case 3:
|
|
242
|
-
return 90;
|
|
243
|
-
default:
|
|
244
|
-
return 0;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
function progressUnitsForRuntimeStep(step, total) {
|
|
248
|
-
if (total <= 0)
|
|
249
|
-
return 10;
|
|
250
|
-
return Math.min(89, 10 + Math.round((step / total) * 75));
|
|
251
|
-
}
|
|
252
|
-
function parseSessionRootLine(projectRoot, line) {
|
|
253
|
-
const match = /^\s*session_root:\s+(.+?)\s*$/.exec(line);
|
|
254
|
-
if (!match?.[1])
|
|
255
|
-
return null;
|
|
256
|
-
const rawSessionRoot = match[1];
|
|
257
|
-
return path.isAbsolute(rawSessionRoot)
|
|
258
|
-
? rawSessionRoot
|
|
259
|
-
: path.resolve(projectRoot, rawSessionRoot);
|
|
260
|
-
}
|
|
261
|
-
function consoleLineProgressEvent(args) {
|
|
262
|
-
const plannedSessionRoot = parseSessionRootLine(args.projectRoot, args.line);
|
|
263
|
-
if (plannedSessionRoot) {
|
|
264
|
-
return {
|
|
265
|
-
sessionRoot: plannedSessionRoot,
|
|
266
|
-
event: progressEvent({
|
|
267
|
-
sequence: args.sequence,
|
|
268
|
-
source: "review_invoke_console",
|
|
269
|
-
stage: "session_planned",
|
|
270
|
-
sessionRoot: plannedSessionRoot,
|
|
271
|
-
message: `Review session planned at ${plannedSessionRoot}.`,
|
|
272
|
-
current: 1,
|
|
273
|
-
label: "session planned",
|
|
274
|
-
}),
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
if (args.line.trim() === "[review start]") {
|
|
278
|
-
return {
|
|
279
|
-
sessionRoot: args.sessionRoot,
|
|
280
|
-
event: progressEvent({
|
|
281
|
-
sequence: args.sequence,
|
|
282
|
-
source: "review_invoke_console",
|
|
283
|
-
stage: "start_preview",
|
|
284
|
-
sessionRoot: args.sessionRoot,
|
|
285
|
-
message: "Review start preview generated.",
|
|
286
|
-
current: 0,
|
|
287
|
-
label: "start preview",
|
|
288
|
-
}),
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
const invokeStepMatch = /^\[review invoke\] step (\d+)\/3\s+(.+?)\s*$/.exec(args.line);
|
|
292
|
-
if (invokeStepMatch?.[1] && invokeStepMatch[2]) {
|
|
293
|
-
const step = Number.parseInt(invokeStepMatch[1], 10);
|
|
294
|
-
const label = invokeStepMatch[2];
|
|
295
|
-
return {
|
|
296
|
-
sessionRoot: args.sessionRoot,
|
|
297
|
-
event: progressEvent({
|
|
298
|
-
sequence: args.sequence,
|
|
299
|
-
source: "review_invoke_console",
|
|
300
|
-
stage: "invoke_step",
|
|
301
|
-
sessionRoot: args.sessionRoot,
|
|
302
|
-
message: label,
|
|
303
|
-
current: progressUnitsForInvokeStep(step),
|
|
304
|
-
exactStep: step,
|
|
305
|
-
exactTotal: 3,
|
|
306
|
-
label,
|
|
307
|
-
}),
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
const runtimeStepMatch = /^\[review progress\]\s+(\d+)\/(\d+)\s+(.+?)\s*$/.exec(args.line);
|
|
311
|
-
if (runtimeStepMatch?.[1] && runtimeStepMatch[2] && runtimeStepMatch[3]) {
|
|
312
|
-
const step = Number.parseInt(runtimeStepMatch[1], 10);
|
|
313
|
-
const total = Number.parseInt(runtimeStepMatch[2], 10);
|
|
314
|
-
const label = runtimeStepMatch[3];
|
|
315
|
-
return {
|
|
316
|
-
sessionRoot: args.sessionRoot,
|
|
317
|
-
event: progressEvent({
|
|
318
|
-
sequence: args.sequence,
|
|
319
|
-
source: "review_invoke_console",
|
|
320
|
-
stage: "runtime_step",
|
|
321
|
-
sessionRoot: args.sessionRoot,
|
|
322
|
-
message: label,
|
|
323
|
-
current: progressUnitsForRuntimeStep(step, total),
|
|
324
|
-
exactStep: step,
|
|
325
|
-
exactTotal: total,
|
|
326
|
-
label,
|
|
327
|
-
}),
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
const completedMatch = /^\[review invoke\] completed 3\/3\s+(.+?)\s*$/.exec(args.line);
|
|
331
|
-
if (completedMatch?.[1]) {
|
|
332
|
-
const label = completedMatch[1];
|
|
333
|
-
return {
|
|
334
|
-
sessionRoot: args.sessionRoot,
|
|
335
|
-
event: progressEvent({
|
|
336
|
-
sequence: args.sequence,
|
|
337
|
-
source: "review_invoke_console",
|
|
338
|
-
stage: "completed",
|
|
339
|
-
sessionRoot: args.sessionRoot,
|
|
340
|
-
message: label,
|
|
341
|
-
current: 98,
|
|
342
|
-
exactStep: 3,
|
|
343
|
-
exactTotal: 3,
|
|
344
|
-
label,
|
|
345
|
-
}),
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
return null;
|
|
349
|
-
}
|
|
350
632
|
function generatedFromArtifactRefs(artifactRefs, keys = PRESENTATION_SOURCE_REF_KEYS) {
|
|
351
633
|
const refs = {};
|
|
352
634
|
for (const key of keys) {
|
|
@@ -462,6 +744,241 @@ function livenessSummary(args) {
|
|
|
462
744
|
return `Review is still active at ${args.currentLabel ?? "unknown step"}, but no artifact change has been observed for the stale threshold.${active}${lastArtifact}${since}`;
|
|
463
745
|
}
|
|
464
746
|
}
|
|
747
|
+
const RUNTIME_UNIT_STALE_AFTER_SECONDS = 300;
|
|
748
|
+
async function observeFile(filePath) {
|
|
749
|
+
if (!filePath)
|
|
750
|
+
return { exists: false, size: 0, mtimeMs: null };
|
|
751
|
+
try {
|
|
752
|
+
const stat = await fs.stat(filePath);
|
|
753
|
+
return {
|
|
754
|
+
exists: true,
|
|
755
|
+
size: stat.size,
|
|
756
|
+
mtimeMs: stat.mtimeMs,
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
catch {
|
|
760
|
+
return { exists: false, size: 0, mtimeMs: null };
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
function allReviewUnitResults(executionResult) {
|
|
764
|
+
if (!executionResult)
|
|
765
|
+
return [];
|
|
766
|
+
return [
|
|
767
|
+
...executionResult.lens_execution_results,
|
|
768
|
+
...(executionResult.issue_artifact_execution_results ?? []),
|
|
769
|
+
...(executionResult.deliberation_execution_results ?? []),
|
|
770
|
+
...(executionResult.synthesize_execution_result
|
|
771
|
+
? [executionResult.synthesize_execution_result]
|
|
772
|
+
: []),
|
|
773
|
+
];
|
|
774
|
+
}
|
|
775
|
+
function parseRuntimeLogHeading(rawHeading) {
|
|
776
|
+
const match = /^##\s+(.+?)\s+\|\s+(.+?)\s*$/.exec(rawHeading.trim());
|
|
777
|
+
if (!match?.[2])
|
|
778
|
+
return null;
|
|
779
|
+
return {
|
|
780
|
+
at: match[1] ?? null,
|
|
781
|
+
title: match[2],
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
function bodyScalar(body, key) {
|
|
785
|
+
const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
786
|
+
const match = new RegExp(`^${escaped}:\\s*(.+?)\\s*$`, "m").exec(body);
|
|
787
|
+
return match?.[1] ?? null;
|
|
788
|
+
}
|
|
789
|
+
function parseAttempt(body) {
|
|
790
|
+
const raw = bodyScalar(body, "attempt");
|
|
791
|
+
if (!raw)
|
|
792
|
+
return null;
|
|
793
|
+
const match = /^(\d+)/.exec(raw.trim());
|
|
794
|
+
if (!match?.[1])
|
|
795
|
+
return null;
|
|
796
|
+
const parsed = Number.parseInt(match[1], 10);
|
|
797
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
798
|
+
}
|
|
799
|
+
function runtimeSignalFromEntry(entry) {
|
|
800
|
+
const [headingLine, ...bodyLines] = entry.split(/\r?\n/);
|
|
801
|
+
if (!headingLine)
|
|
802
|
+
return null;
|
|
803
|
+
const heading = parseRuntimeLogHeading(headingLine);
|
|
804
|
+
if (!heading)
|
|
805
|
+
return null;
|
|
806
|
+
const body = bodyLines.join("\n");
|
|
807
|
+
const title = heading.title;
|
|
808
|
+
const titlePatterns = [
|
|
809
|
+
{
|
|
810
|
+
kind: "started",
|
|
811
|
+
pattern: /^runner dispatch started: (.+?)\s*$/,
|
|
812
|
+
summary: "runner dispatch started",
|
|
813
|
+
},
|
|
814
|
+
{
|
|
815
|
+
kind: "retry",
|
|
816
|
+
pattern: /^runner dispatch retry: (.+?)\s*$/,
|
|
817
|
+
summary: "runner dispatch retry",
|
|
818
|
+
},
|
|
819
|
+
{
|
|
820
|
+
kind: "completed",
|
|
821
|
+
pattern: /^runner (?:nested )?dispatch completed: (.+?)\s*$/,
|
|
822
|
+
summary: "runner dispatch completed",
|
|
823
|
+
},
|
|
824
|
+
{
|
|
825
|
+
kind: "failed",
|
|
826
|
+
pattern: /^(?:lens|deliberation|issue_artifact|synthesize) failure: (.+?)\s*$/,
|
|
827
|
+
summary: "runner dispatch failed",
|
|
828
|
+
},
|
|
829
|
+
];
|
|
830
|
+
for (const candidate of titlePatterns) {
|
|
831
|
+
const match = candidate.pattern.exec(title);
|
|
832
|
+
if (!match?.[1])
|
|
833
|
+
continue;
|
|
834
|
+
const atMs = parseTimestampMs(heading.at);
|
|
835
|
+
return {
|
|
836
|
+
unitId: match[1],
|
|
837
|
+
kind: candidate.kind,
|
|
838
|
+
summary: candidate.summary,
|
|
839
|
+
at: heading.at,
|
|
840
|
+
atMs,
|
|
841
|
+
attempt: candidate.kind === "retry" ? parseAttempt(body) : null,
|
|
842
|
+
failureMessage: candidate.kind === "failed" ? bodyScalar(body, "message") : null,
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
return null;
|
|
846
|
+
}
|
|
847
|
+
async function runtimeLogSignalsByUnit(errorLogPath) {
|
|
848
|
+
const text = await readOptionalText(errorLogPath);
|
|
849
|
+
const signals = new Map();
|
|
850
|
+
if (!text)
|
|
851
|
+
return signals;
|
|
852
|
+
for (const rawEntry of text.split(/\n(?=## )/)) {
|
|
853
|
+
const signal = runtimeSignalFromEntry(rawEntry.trimEnd());
|
|
854
|
+
if (!signal)
|
|
855
|
+
continue;
|
|
856
|
+
signals.set(signal.unitId, [...(signals.get(signal.unitId) ?? []), signal]);
|
|
857
|
+
}
|
|
858
|
+
return signals;
|
|
859
|
+
}
|
|
860
|
+
function latestRuntimeSignal(signals) {
|
|
861
|
+
if (signals.length === 0)
|
|
862
|
+
return null;
|
|
863
|
+
return signals.reduce((latest, signal) => {
|
|
864
|
+
const latestMs = latest.atMs ?? -1;
|
|
865
|
+
const signalMs = signal.atMs ?? -1;
|
|
866
|
+
return signalMs >= latestMs ? signal : latest;
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
function runtimeSignalCount(signals, kind) {
|
|
870
|
+
return signals.filter((signal) => signal.kind === kind).length;
|
|
871
|
+
}
|
|
872
|
+
function runtimeUnitAlias(unitKind, unitId) {
|
|
873
|
+
if (unitKind === "lens")
|
|
874
|
+
return `lens:${unitId}`;
|
|
875
|
+
if (unitKind === "deliberation" && unitId.startsWith("deliberation-")) {
|
|
876
|
+
return `deliberation:${unitId.replace(/^deliberation-/, "")}`;
|
|
877
|
+
}
|
|
878
|
+
return unitId;
|
|
879
|
+
}
|
|
880
|
+
function runtimeUnitStepId(unitKind) {
|
|
881
|
+
if (unitKind === "lens")
|
|
882
|
+
return "lens_dispatch";
|
|
883
|
+
if (unitKind === "deliberation")
|
|
884
|
+
return "lens_deliberation_responses";
|
|
885
|
+
return null;
|
|
886
|
+
}
|
|
887
|
+
async function deriveRuntimeUnitProgress(args) {
|
|
888
|
+
const executionPlan = args.executionPlan;
|
|
889
|
+
if (!executionPlan)
|
|
890
|
+
return [];
|
|
891
|
+
const signalsByUnit = await runtimeLogSignalsByUnit(executionPlan.error_log_path);
|
|
892
|
+
const terminalResultsByUnit = new Map(allReviewUnitResults(args.executionResult).map((result) => [
|
|
893
|
+
result.unit_id,
|
|
894
|
+
result,
|
|
895
|
+
]));
|
|
896
|
+
const lensUnits = executionPlan.lens_execution_seats.map((seat) => {
|
|
897
|
+
const packetPath = executionPlan.lens_prompt_packet_seats.find((packetSeat) => packetSeat.lens_id === seat.lens_id)?.packet_path ?? null;
|
|
898
|
+
return {
|
|
899
|
+
unitId: seat.lens_id,
|
|
900
|
+
unitKind: "lens",
|
|
901
|
+
packetPath,
|
|
902
|
+
outputPath: seat.output_path,
|
|
903
|
+
runningLogRef: path.join(path.dirname(seat.output_path), `.${seat.lens_id}.running.log`),
|
|
904
|
+
};
|
|
905
|
+
});
|
|
906
|
+
const projections = [];
|
|
907
|
+
for (const unit of lensUnits) {
|
|
908
|
+
const output = await observeFile(unit.outputPath);
|
|
909
|
+
const runningLog = await observeFile(unit.runningLogRef);
|
|
910
|
+
const signals = signalsByUnit.get(unit.unitId) ?? [];
|
|
911
|
+
const latestSignal = latestRuntimeSignal(signals);
|
|
912
|
+
const terminalResult = terminalResultsByUnit.get(unit.unitId);
|
|
913
|
+
const retryCount = runtimeSignalCount(signals, "retry");
|
|
914
|
+
const hasStarted = signals.some((signal) => signal.kind === "started") ||
|
|
915
|
+
runningLog.exists;
|
|
916
|
+
const attemptCount = Math.max(terminalResult ? 1 : 0, hasStarted ? retryCount + 1 : 0);
|
|
917
|
+
let latestSignalName = latestSignal?.summary ?? null;
|
|
918
|
+
let latestSignalAt = latestSignal?.at ?? null;
|
|
919
|
+
let latestSignalMs = latestSignal?.atMs ?? null;
|
|
920
|
+
if (runningLog.exists &&
|
|
921
|
+
runningLog.mtimeMs !== null &&
|
|
922
|
+
(latestSignalMs === null || runningLog.mtimeMs > latestSignalMs)) {
|
|
923
|
+
latestSignalName = "running log updated";
|
|
924
|
+
latestSignalAt = isoFromTimestamp(runningLog.mtimeMs);
|
|
925
|
+
latestSignalMs = runningLog.mtimeMs;
|
|
926
|
+
}
|
|
927
|
+
let status = "pending";
|
|
928
|
+
let failureMessage = latestSignal?.failureMessage ?? null;
|
|
929
|
+
if (terminalResult?.status === "failed") {
|
|
930
|
+
status = "failed";
|
|
931
|
+
latestSignalName = "terminal execution result failed";
|
|
932
|
+
latestSignalAt = terminalResult.completed_at;
|
|
933
|
+
latestSignalMs = parseTimestampMs(terminalResult.completed_at);
|
|
934
|
+
failureMessage = terminalResult.failure_message ?? failureMessage;
|
|
935
|
+
}
|
|
936
|
+
else if (terminalResult?.status === "completed" ||
|
|
937
|
+
latestSignal?.kind === "completed" ||
|
|
938
|
+
(output.exists && output.size > 0)) {
|
|
939
|
+
status = "completed";
|
|
940
|
+
if (!latestSignalName || (output.mtimeMs !== null && output.mtimeMs > (latestSignalMs ?? -1))) {
|
|
941
|
+
latestSignalName = "output file present";
|
|
942
|
+
latestSignalAt = output.mtimeMs === null ? null : isoFromTimestamp(output.mtimeMs);
|
|
943
|
+
latestSignalMs = output.mtimeMs;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
else if (latestSignal?.kind === "failed") {
|
|
947
|
+
status = "failed";
|
|
948
|
+
failureMessage = latestSignal.failureMessage;
|
|
949
|
+
}
|
|
950
|
+
else if (latestSignal?.kind === "retry") {
|
|
951
|
+
const seconds = secondsBetween(args.nowMs, latestSignalMs);
|
|
952
|
+
status =
|
|
953
|
+
seconds !== null && seconds > RUNTIME_UNIT_STALE_AFTER_SECONDS
|
|
954
|
+
? "running_stale"
|
|
955
|
+
: "retrying";
|
|
956
|
+
}
|
|
957
|
+
else if (hasStarted) {
|
|
958
|
+
const seconds = secondsBetween(args.nowMs, latestSignalMs);
|
|
959
|
+
status =
|
|
960
|
+
seconds !== null && seconds > RUNTIME_UNIT_STALE_AFTER_SECONDS
|
|
961
|
+
? "running_stale"
|
|
962
|
+
: "running";
|
|
963
|
+
}
|
|
964
|
+
projections.push({
|
|
965
|
+
unitId: unit.unitId,
|
|
966
|
+
publicAlias: runtimeUnitAlias(unit.unitKind, unit.unitId),
|
|
967
|
+
unitKind: unit.unitKind,
|
|
968
|
+
progressStepId: runtimeUnitStepId(unit.unitKind),
|
|
969
|
+
status,
|
|
970
|
+
packetPath: unit.packetPath,
|
|
971
|
+
outputPath: unit.outputPath,
|
|
972
|
+
runningLogRef: runningLog.exists ? unit.runningLogRef : null,
|
|
973
|
+
latestSignal: latestSignalName,
|
|
974
|
+
latestSignalAt,
|
|
975
|
+
secondsSinceLatestSignal: secondsBetween(args.nowMs, latestSignalMs),
|
|
976
|
+
attemptCount,
|
|
977
|
+
failureMessage,
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
return projections;
|
|
981
|
+
}
|
|
465
982
|
async function existingSeatIds(seats) {
|
|
466
983
|
const existing = [];
|
|
467
984
|
for (const seat of seats ?? []) {
|
|
@@ -477,7 +994,7 @@ async function completedProgressStepIds(params) {
|
|
|
477
994
|
const completed = [];
|
|
478
995
|
if (params.executionPlan)
|
|
479
996
|
completed.push("manifest_validation");
|
|
480
|
-
const plannedLensIds = params.executionPlan?.lens_execution_seats.map((seat) => seat.lens_id)
|
|
997
|
+
const plannedLensIds = (params.executionPlan?.lens_execution_seats ?? []).map((seat) => seat.lens_id);
|
|
481
998
|
const completedLensIds = await existingSeatIds(params.executionPlan?.lens_execution_seats);
|
|
482
999
|
const allPlannedLensesCompleted = plannedLensIds.length > 0 && completedLensIds.length >= plannedLensIds.length;
|
|
483
1000
|
if (allPlannedLensesCompleted || params.artifactRefs.lens_completion_barrier) {
|
|
@@ -496,8 +1013,7 @@ async function completedProgressStepIds(params) {
|
|
|
496
1013
|
if (params.artifactRefs.deliberation_plan)
|
|
497
1014
|
completed.push("deliberation_plan");
|
|
498
1015
|
const deliberationIds = await existingSeatIds(params.executionPlan?.lens_deliberation_prompt_packet_seats);
|
|
499
|
-
const plannedDeliberationIds = params.executionPlan?.lens_deliberation_prompt_packet_seats.map((seat) => seat.lens_id)
|
|
500
|
-
[];
|
|
1016
|
+
const plannedDeliberationIds = (params.executionPlan?.lens_deliberation_prompt_packet_seats ?? []).map((seat) => seat.lens_id);
|
|
501
1017
|
if ((plannedDeliberationIds.length > 0 &&
|
|
502
1018
|
deliberationIds.length >= plannedDeliberationIds.length) ||
|
|
503
1019
|
params.artifactRefs.deliberation_output) {
|
|
@@ -531,14 +1047,13 @@ async function activeUnits(params) {
|
|
|
531
1047
|
return typeof unitId === "string" && unitId.length > 0 ? [unitId] : [];
|
|
532
1048
|
}
|
|
533
1049
|
if (params.currentStepId === "lens_dispatch") {
|
|
534
|
-
const planned = params.executionPlan?.lens_execution_seats.map((seat) => seat.lens_id)
|
|
1050
|
+
const planned = (params.executionPlan?.lens_execution_seats ?? []).map((seat) => seat.lens_id);
|
|
535
1051
|
const completed = new Set(await existingSeatIds(params.executionPlan?.lens_execution_seats));
|
|
536
1052
|
const pending = planned.filter((lensId) => !completed.has(lensId));
|
|
537
1053
|
return (pending.length > 0 ? pending : planned).map((lensId) => `lens:${lensId}`);
|
|
538
1054
|
}
|
|
539
1055
|
if (params.currentStepId === "lens_deliberation_responses") {
|
|
540
|
-
const planned = params.executionPlan?.lens_deliberation_prompt_packet_seats.map((seat) => seat.lens_id)
|
|
541
|
-
[];
|
|
1056
|
+
const planned = (params.executionPlan?.lens_deliberation_prompt_packet_seats ?? []).map((seat) => seat.lens_id);
|
|
542
1057
|
const completed = new Set(await existingSeatIds(params.executionPlan?.lens_deliberation_prompt_packet_seats));
|
|
543
1058
|
return planned
|
|
544
1059
|
.filter((lensId) => !completed.has(lensId))
|
|
@@ -665,6 +1180,24 @@ async function buildReviewStatusPresentationInput(params) {
|
|
|
665
1180
|
const completedStatus = params.status === "completed" || params.status === "completed_with_degradation";
|
|
666
1181
|
const sessionStartMs = parseTimestampMs(params.executionResult?.execution_started_at) ??
|
|
667
1182
|
parseTimestampMs(sessionMetadata?.created_at);
|
|
1183
|
+
const unitProgress = await deriveRuntimeUnitProgress({
|
|
1184
|
+
executionPlan: params.executionPlan,
|
|
1185
|
+
executionResult: params.executionResult,
|
|
1186
|
+
nowMs,
|
|
1187
|
+
});
|
|
1188
|
+
const runtimeActiveUnits = unitProgress
|
|
1189
|
+
.filter((unit) => unit.status === "running" ||
|
|
1190
|
+
unit.status === "retrying" ||
|
|
1191
|
+
unit.status === "running_stale")
|
|
1192
|
+
.map((unit) => unit.publicAlias);
|
|
1193
|
+
const progressActiveUnits = currentStepId === "lens_dispatch" && unitProgress.length > 0
|
|
1194
|
+
? runtimeActiveUnits
|
|
1195
|
+
: await activeUnits({
|
|
1196
|
+
status: params.status,
|
|
1197
|
+
currentStepId,
|
|
1198
|
+
executionPlan: params.executionPlan,
|
|
1199
|
+
executionResult: params.executionResult,
|
|
1200
|
+
});
|
|
668
1201
|
const progress = {
|
|
669
1202
|
current_step: completedStatus
|
|
670
1203
|
? totalSteps
|
|
@@ -688,12 +1221,7 @@ async function buildReviewStatusPresentationInput(params) {
|
|
|
688
1221
|
params.artifactRefs.execution_plan ? "execution_plan" : null,
|
|
689
1222
|
].filter((step) => step !== null)
|
|
690
1223
|
: completedSteps,
|
|
691
|
-
active_units:
|
|
692
|
-
status: params.status,
|
|
693
|
-
currentStepId,
|
|
694
|
-
executionPlan: params.executionPlan,
|
|
695
|
-
executionResult: params.executionResult,
|
|
696
|
-
}),
|
|
1224
|
+
active_units: progressActiveUnits,
|
|
697
1225
|
pending_units: completedStatus
|
|
698
1226
|
? []
|
|
699
1227
|
: params.status === "prepared"
|
|
@@ -712,6 +1240,7 @@ async function buildReviewStatusPresentationInput(params) {
|
|
|
712
1240
|
: currentStepId
|
|
713
1241
|
? `next ${stepById(currentStepId).label} artifact or timeout`
|
|
714
1242
|
: null,
|
|
1243
|
+
unit_progress: unitProgress,
|
|
715
1244
|
};
|
|
716
1245
|
const completedLensIds = await existingSeatIds(params.executionPlan?.lens_execution_seats);
|
|
717
1246
|
const halt = haltPresentation({
|
|
@@ -744,6 +1273,8 @@ async function buildReviewStatusPresentationInput(params) {
|
|
|
744
1273
|
secondsSinceLastArtifact,
|
|
745
1274
|
}),
|
|
746
1275
|
};
|
|
1276
|
+
const runControl = await buildRunControl(params.sessionRoot, params.status);
|
|
1277
|
+
const targetMaterialSupport = await readTargetMaterialSupport(params.sessionRoot, params.executionPlan);
|
|
747
1278
|
return {
|
|
748
1279
|
presentation_contract_version: REVIEW_PRESENTATION_CONTRACT_VERSION,
|
|
749
1280
|
presentation_kind: "progress",
|
|
@@ -762,6 +1293,9 @@ async function buildReviewStatusPresentationInput(params) {
|
|
|
762
1293
|
}),
|
|
763
1294
|
result_classification_summary: resultClassificationSummary,
|
|
764
1295
|
halt,
|
|
1296
|
+
run_control: runControl,
|
|
1297
|
+
target_material_support: targetMaterialSupport,
|
|
1298
|
+
environment_warnings: await readEnvironmentWarnings(params.sessionRoot),
|
|
765
1299
|
};
|
|
766
1300
|
}
|
|
767
1301
|
async function buildPreparedOpeningBriefInput(sessionRoot, executionPlan) {
|
|
@@ -798,6 +1332,7 @@ async function buildPreparedOpeningBriefInput(sessionRoot, executionPlan) {
|
|
|
798
1332
|
resolved_host_runtime: binding.resolved_host_runtime,
|
|
799
1333
|
boundary_policy: binding.boundary_policy,
|
|
800
1334
|
effective_boundary_state: binding.effective_boundary_state,
|
|
1335
|
+
binding_notes: binding.binding_notes ?? [],
|
|
801
1336
|
}
|
|
802
1337
|
: null,
|
|
803
1338
|
review_target_profile: reviewTargetProfile
|
|
@@ -825,60 +1360,328 @@ async function buildPreparedOpeningBriefInput(sessionRoot, executionPlan) {
|
|
|
825
1360
|
},
|
|
826
1361
|
};
|
|
827
1362
|
}
|
|
828
|
-
function
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
1363
|
+
async function buildReviewRunHandle(args) {
|
|
1364
|
+
const artifactRefs = await collectArtifactRefs(args.sessionRoot);
|
|
1365
|
+
const executionPlan = await readOptionalYaml(path.join(args.sessionRoot, "execution-plan.yaml"));
|
|
1366
|
+
const metadata = await readOptionalYaml(path.join(args.sessionRoot, "session-metadata.yaml"));
|
|
1367
|
+
const interpretation = await readOptionalYaml(path.join(args.sessionRoot, "interpretation.yaml"));
|
|
1368
|
+
const binding = await readOptionalYaml(path.join(args.sessionRoot, "binding.yaml"));
|
|
1369
|
+
const targetProfile = await readOptionalYaml(executionPlan?.review_target_profile_path ??
|
|
1370
|
+
path.join(args.sessionRoot, "execution-preparation", "review-target-profile.yaml"));
|
|
1371
|
+
const requestHash = args.requestHash ??
|
|
1372
|
+
requestHashFromArtifacts({ metadata, interpretation, binding });
|
|
1373
|
+
const normalizedDomain = binding?.resolved_session_domain ??
|
|
1374
|
+
targetProfile?.domain ??
|
|
1375
|
+
(metadata?.requested_domain_token
|
|
1376
|
+
? normalizeDomainValue(metadata.requested_domain_token)
|
|
1377
|
+
: null);
|
|
1378
|
+
return {
|
|
1379
|
+
schemaVersion: "1",
|
|
1380
|
+
sessionId: metadata?.session_id ?? executionPlan?.session_id ?? basenameSessionId(args.sessionRoot),
|
|
1381
|
+
sessionRoot: args.sessionRoot,
|
|
1382
|
+
invocationId: args.invocationId,
|
|
1383
|
+
status: args.status,
|
|
1384
|
+
projectRoot: metadata?.project_root ?? null,
|
|
1385
|
+
target: {
|
|
1386
|
+
requestedTarget: metadata?.requested_target ?? targetProfile?.requested_target ?? null,
|
|
1387
|
+
targetScopeKind: targetProfile?.target_scope_kind ?? null,
|
|
1388
|
+
targetMaterialKind: targetProfile?.target_material_kind ?? null,
|
|
1389
|
+
},
|
|
1390
|
+
domain: domainTokenResolution({
|
|
1391
|
+
requestedToken: metadata?.requested_domain_token ?? null,
|
|
1392
|
+
normalizedDomain,
|
|
1393
|
+
}),
|
|
1394
|
+
artifactRefs: {
|
|
1395
|
+
sessionMetadata: artifactRefs.session_metadata ?? null,
|
|
1396
|
+
executionPlan: artifactRefs.execution_plan ?? null,
|
|
1397
|
+
reviewRunManifest: artifactRefs.review_run_manifest ?? null,
|
|
1398
|
+
executionResult: artifactRefs.execution_result ?? null,
|
|
1399
|
+
finalOutput: artifactRefs.final_output ?? null,
|
|
1400
|
+
reviewRecord: artifactRefs.review_record ?? null,
|
|
1401
|
+
},
|
|
1402
|
+
requestHash,
|
|
1403
|
+
pollAfterSeconds: reviewTerminalStatus(args.status) ? null : 5,
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
async function buildRunningReviewRunResult(args) {
|
|
1407
|
+
const sessionRoot = path.resolve(args.sessionRoot);
|
|
1408
|
+
const artifactRefs = await collectArtifactRefs(sessionRoot);
|
|
1409
|
+
const failures = await collectStructuredFailures(sessionRoot);
|
|
1410
|
+
const executionPlan = await readOptionalYaml(path.join(sessionRoot, "execution-plan.yaml"));
|
|
1411
|
+
const executionResult = await readOptionalYaml(path.join(sessionRoot, "execution-result.yaml"));
|
|
1412
|
+
const reviewRecord = await readOptionalReviewRecord(path.join(sessionRoot, "review-record.yaml"));
|
|
1413
|
+
const progressInput = await buildReviewStatusPresentationInput({
|
|
1414
|
+
sessionRoot,
|
|
1415
|
+
status: "running",
|
|
1416
|
+
artifactRefs,
|
|
1417
|
+
executionPlan,
|
|
1418
|
+
executionResult,
|
|
1419
|
+
reviewRecord,
|
|
1420
|
+
});
|
|
1421
|
+
const runHandle = await buildReviewRunHandle({
|
|
1422
|
+
sessionRoot,
|
|
1423
|
+
status: "running",
|
|
1424
|
+
invocationId: args.invocationId,
|
|
1425
|
+
...(args.requestHash !== undefined ? { requestHash: args.requestHash } : {}),
|
|
1426
|
+
});
|
|
1427
|
+
const llmPresentation = {
|
|
1428
|
+
progress: buildProgressPresentation(progressInput),
|
|
1429
|
+
};
|
|
1430
|
+
if (executionPlan) {
|
|
1431
|
+
llmPresentation.openingBrief = buildOpeningBriefPresentation(await buildPreparedOpeningBriefInput(sessionRoot, executionPlan));
|
|
1432
|
+
}
|
|
1433
|
+
return {
|
|
1434
|
+
sessionId: runHandle.sessionId,
|
|
1435
|
+
sessionRoot,
|
|
1436
|
+
status: "running",
|
|
1437
|
+
finalOutputPath: executionPlan?.final_output_path ?? path.join(sessionRoot, "final-output.md"),
|
|
1438
|
+
reviewRecordPath: executionPlan?.review_record_path ?? path.join(sessionRoot, "review-record.yaml"),
|
|
1439
|
+
executionResultPath: executionPlan?.execution_result_path ?? path.join(sessionRoot, "execution-result.yaml"),
|
|
1440
|
+
reviewRunManifestPath: path.join(sessionRoot, "review-run-manifest.yaml"),
|
|
1441
|
+
deliberationStatus: null,
|
|
1442
|
+
participatingLensIds: [],
|
|
1443
|
+
degradedLensIds: [],
|
|
1444
|
+
artifactRefs,
|
|
1445
|
+
...failures,
|
|
1446
|
+
routeVisibility: await buildReviewRouteVisibilityFromSession(sessionRoot),
|
|
1447
|
+
llmPresentation,
|
|
1448
|
+
runHandle,
|
|
1449
|
+
runControl: progressInput.run_control,
|
|
1450
|
+
targetMaterialSupport: progressInput.target_material_support,
|
|
1451
|
+
environmentWarnings: progressInput.environment_warnings,
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1454
|
+
async function collectArtifactRefs(sessionRoot) {
|
|
1455
|
+
return collectReviewInvocationArtifactRefs(sessionRoot);
|
|
1456
|
+
}
|
|
1457
|
+
async function buildPipelineExecutionLedgerIfPossible(args) {
|
|
1458
|
+
if (!args.executionPlan)
|
|
1459
|
+
return undefined;
|
|
1460
|
+
const reviewRunManifest = await readOptionalYaml(path.join(args.sessionRoot, "review-run-manifest.yaml"));
|
|
1461
|
+
const lensCompletionBarrier = await readOptionalYaml(path.join(args.sessionRoot, "lens-completion-barrier.yaml"));
|
|
1462
|
+
try {
|
|
1463
|
+
return await buildReviewPipelineExecutionLedger({
|
|
1464
|
+
sessionRoot: args.sessionRoot,
|
|
1465
|
+
artifactRefs: args.artifactRefs,
|
|
1466
|
+
executionPlan: args.executionPlan,
|
|
1467
|
+
executionResult: args.executionResult,
|
|
1468
|
+
reviewRunManifest,
|
|
1469
|
+
lensCompletionBarrier,
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
catch {
|
|
1473
|
+
return undefined;
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
function workerExecutorToRealization(workerExecutor) {
|
|
1477
|
+
if (workerExecutor === "mock")
|
|
1478
|
+
return "mock";
|
|
1479
|
+
if (workerExecutor === "codex")
|
|
1480
|
+
return "codex";
|
|
1481
|
+
if (workerExecutor === "direct_call")
|
|
1482
|
+
return "ts_inline_http";
|
|
1483
|
+
return null;
|
|
1484
|
+
}
|
|
1485
|
+
function workerExecutorFromRealization(realization) {
|
|
1486
|
+
if (realization === "mock")
|
|
1487
|
+
return "mock";
|
|
1488
|
+
if (realization === "codex")
|
|
1489
|
+
return "codex";
|
|
1490
|
+
return "direct_call";
|
|
1491
|
+
}
|
|
1492
|
+
function reviewExecutionHostFromRuntime(hostRuntime, workerExecutor) {
|
|
1493
|
+
if (workerExecutor === "mock")
|
|
1494
|
+
return "standalone";
|
|
1495
|
+
if (workerExecutor === "codex")
|
|
1496
|
+
return "codex";
|
|
1497
|
+
if (hostRuntime === "openai" ||
|
|
1498
|
+
hostRuntime === "anthropic" ||
|
|
1499
|
+
hostRuntime === "grok" ||
|
|
1500
|
+
hostRuntime === "lmstudio") {
|
|
1501
|
+
return hostRuntime;
|
|
1502
|
+
}
|
|
1503
|
+
return "openai";
|
|
1504
|
+
}
|
|
1505
|
+
function reviewExecutionProfileFromManifest(manifest) {
|
|
1506
|
+
const profile = manifest?.review_execution_profile;
|
|
1507
|
+
const route = profile?.runtime_route;
|
|
1508
|
+
const workerExecutor = route?.worker_executor;
|
|
1509
|
+
if (workerExecutor !== "mock" &&
|
|
1510
|
+
workerExecutor !== "codex" &&
|
|
1511
|
+
workerExecutor !== "direct_call") {
|
|
1512
|
+
return undefined;
|
|
1513
|
+
}
|
|
1514
|
+
if (profile?.mode !== "main-workers" &&
|
|
1515
|
+
profile?.mode !== "nested-workers") {
|
|
1516
|
+
return undefined;
|
|
1517
|
+
}
|
|
1518
|
+
if (profile.teamlead === null ||
|
|
1519
|
+
typeof profile.teamlead !== "object" ||
|
|
1520
|
+
profile.lens === null ||
|
|
1521
|
+
typeof profile.lens !== "object" ||
|
|
1522
|
+
profile.synthesize === null ||
|
|
1523
|
+
typeof profile.synthesize !== "object" ||
|
|
1524
|
+
typeof profile.deliberation !== "string") {
|
|
1525
|
+
return undefined;
|
|
1526
|
+
}
|
|
1527
|
+
const host = reviewExecutionHostFromRuntime(route?.host_runtime, workerExecutor);
|
|
1528
|
+
const runtimeProvider = typeof route?.runtime_provider === "string" &&
|
|
1529
|
+
route.runtime_provider !== "mock" &&
|
|
1530
|
+
route.runtime_provider !== "codex"
|
|
1531
|
+
? route.runtime_provider
|
|
1532
|
+
: undefined;
|
|
1533
|
+
const authMode = route?.auth_mode === "api_key" ||
|
|
1534
|
+
route?.auth_mode === "oauth" ||
|
|
1535
|
+
route?.auth_mode === "local"
|
|
1536
|
+
? route.auth_mode
|
|
1537
|
+
: undefined;
|
|
1538
|
+
const reconstructed = {
|
|
1539
|
+
mode: profile.mode,
|
|
1540
|
+
teamlead: profile.teamlead,
|
|
1541
|
+
lens: profile.lens,
|
|
1542
|
+
synthesize: profile.synthesize,
|
|
1543
|
+
deliberation: profile.deliberation,
|
|
1544
|
+
worker_executor: workerExecutor,
|
|
1545
|
+
host,
|
|
1546
|
+
trace: Array.isArray(profile.trace)
|
|
1547
|
+
? profile.trace.filter((item) => typeof item === "string")
|
|
1548
|
+
: [],
|
|
1549
|
+
};
|
|
1550
|
+
if (runtimeProvider) {
|
|
1551
|
+
reconstructed.provider =
|
|
1552
|
+
runtimeProvider;
|
|
1553
|
+
}
|
|
1554
|
+
if (authMode) {
|
|
1555
|
+
reconstructed.auth =
|
|
1556
|
+
authMode;
|
|
1557
|
+
}
|
|
1558
|
+
if (typeof profile.model === "string")
|
|
1559
|
+
reconstructed.model = profile.model;
|
|
1560
|
+
if (typeof profile.effort === "string")
|
|
1561
|
+
reconstructed.effort = profile.effort;
|
|
1562
|
+
if (typeof profile.service_tier === "string") {
|
|
1563
|
+
reconstructed.service_tier = profile.service_tier;
|
|
1564
|
+
}
|
|
1565
|
+
if (typeof profile.base_url === "string") {
|
|
1566
|
+
reconstructed.base_url = profile.base_url;
|
|
1567
|
+
}
|
|
1568
|
+
return reconstructed;
|
|
1569
|
+
}
|
|
1570
|
+
function reviewExecutionProfileFromActorProfiles(args) {
|
|
1571
|
+
const profiles = args.actorProfiles?.profiles ?? [];
|
|
1572
|
+
const teamlead = profiles.find((profile) => profile.actor_kind === "teamlead");
|
|
1573
|
+
const lens = profiles.find((profile) => profile.actor_kind === "lens");
|
|
1574
|
+
const synthesize = profiles.find((profile) => profile.actor_kind === "synthesize");
|
|
1575
|
+
if (!teamlead || !lens || !synthesize)
|
|
1576
|
+
return undefined;
|
|
1577
|
+
const workerExecutor = workerExecutorFromRealization(args.executorRealization);
|
|
1578
|
+
const host = reviewExecutionHostFromRuntime(teamlead.host_runtime, workerExecutor);
|
|
1579
|
+
const runtimeProvider = teamlead.runtime_provider &&
|
|
1580
|
+
teamlead.runtime_provider !== "mock" &&
|
|
1581
|
+
teamlead.runtime_provider !== "codex"
|
|
1582
|
+
? teamlead.runtime_provider
|
|
1583
|
+
: undefined;
|
|
1584
|
+
const reconstructed = {
|
|
1585
|
+
mode: "main-workers",
|
|
1586
|
+
teamlead: { seat: teamlead.seat, llm: "inherit" },
|
|
1587
|
+
lens: { seat: lens.seat, llm: "inherit" },
|
|
1588
|
+
synthesize: { seat: synthesize.seat, llm: "inherit" },
|
|
1589
|
+
deliberation: "controlled-lens-deliberation",
|
|
1590
|
+
worker_executor: workerExecutor,
|
|
1591
|
+
host,
|
|
1592
|
+
trace: ["reconstructed_from_actor_invocation_profiles_for_continuation"],
|
|
1593
|
+
};
|
|
1594
|
+
if (runtimeProvider) {
|
|
1595
|
+
reconstructed.provider =
|
|
1596
|
+
runtimeProvider;
|
|
1597
|
+
}
|
|
1598
|
+
if (teamlead.auth_mode === "api_key" ||
|
|
1599
|
+
teamlead.auth_mode === "oauth" ||
|
|
1600
|
+
teamlead.auth_mode === "local") {
|
|
1601
|
+
reconstructed.auth =
|
|
1602
|
+
teamlead.auth_mode;
|
|
1603
|
+
}
|
|
1604
|
+
if (teamlead.model)
|
|
1605
|
+
reconstructed.model = teamlead.model;
|
|
1606
|
+
if (teamlead.effort)
|
|
1607
|
+
reconstructed.effort = teamlead.effort;
|
|
1608
|
+
if (teamlead.service_tier)
|
|
1609
|
+
reconstructed.service_tier = teamlead.service_tier;
|
|
1610
|
+
if (teamlead.base_url)
|
|
1611
|
+
reconstructed.base_url = teamlead.base_url;
|
|
1612
|
+
return reconstructed;
|
|
1613
|
+
}
|
|
1614
|
+
function executorRealizationFromManifest(manifest) {
|
|
1615
|
+
return workerExecutorToRealization(manifest?.review_execution_profile?.runtime_route?.worker_executor);
|
|
1616
|
+
}
|
|
1617
|
+
function continuationAttemptId() {
|
|
1618
|
+
const timestamp = new Date()
|
|
1619
|
+
.toISOString()
|
|
1620
|
+
.replace(/[-:]/g, "")
|
|
1621
|
+
.replace(/\..+$/, "Z");
|
|
1622
|
+
return `${timestamp}-${crypto.randomUUID().slice(0, 8)}`;
|
|
1623
|
+
}
|
|
1624
|
+
async function copySupersededArtifacts(args) {
|
|
1625
|
+
const backupRoot = path.join(args.attemptRoot, "superseded-artifacts");
|
|
1626
|
+
const backups = [];
|
|
1627
|
+
for (const [index, sourceRef] of args.artifactRefs.entries()) {
|
|
1628
|
+
if (!(await fileExists(sourceRef)))
|
|
832
1629
|
continue;
|
|
1630
|
+
await fs.mkdir(backupRoot, { recursive: true });
|
|
1631
|
+
const backupRef = path.join(backupRoot, `${String(index + 1).padStart(3, "0")}-${path.basename(sourceRef)}`);
|
|
1632
|
+
await fs.copyFile(sourceRef, backupRef);
|
|
1633
|
+
backups.push({ sourceRef, backupRef });
|
|
1634
|
+
}
|
|
1635
|
+
return backups;
|
|
1636
|
+
}
|
|
1637
|
+
async function restoreSupersededArtifacts(backups) {
|
|
1638
|
+
const restores = [];
|
|
1639
|
+
for (const backup of backups) {
|
|
833
1640
|
try {
|
|
834
|
-
|
|
1641
|
+
await fs.mkdir(path.dirname(backup.sourceRef), { recursive: true });
|
|
1642
|
+
await fs.copyFile(backup.backupRef, backup.sourceRef);
|
|
1643
|
+
restores.push({ ...backup, restored: true });
|
|
835
1644
|
}
|
|
836
|
-
catch {
|
|
837
|
-
|
|
1645
|
+
catch (error) {
|
|
1646
|
+
restores.push({
|
|
1647
|
+
...backup,
|
|
1648
|
+
restored: false,
|
|
1649
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
1650
|
+
});
|
|
838
1651
|
}
|
|
839
1652
|
}
|
|
840
|
-
|
|
1653
|
+
return restores;
|
|
841
1654
|
}
|
|
842
|
-
function
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
1655
|
+
function continuationSessionArtifactRefs(args) {
|
|
1656
|
+
return [
|
|
1657
|
+
args.executionPlan.execution_result_path,
|
|
1658
|
+
path.join(args.sessionRoot, "review-run-manifest.yaml"),
|
|
1659
|
+
path.join(args.sessionRoot, "degradation-summary.yaml"),
|
|
1660
|
+
args.executionPlan.error_log_path,
|
|
1661
|
+
args.executionPlan.synthesis_output_path,
|
|
1662
|
+
args.executionPlan.deliberation_output_path,
|
|
1663
|
+
args.executionPlan.final_output_path,
|
|
1664
|
+
args.executionPlan.review_record_path,
|
|
1665
|
+
];
|
|
847
1666
|
}
|
|
848
|
-
async function
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
interpretation: path.join(sessionRoot, "interpretation.yaml"),
|
|
852
|
-
binding: path.join(sessionRoot, "binding.yaml"),
|
|
853
|
-
execution_plan: path.join(sessionRoot, "execution-plan.yaml"),
|
|
854
|
-
execution_result: path.join(sessionRoot, "execution-result.yaml"),
|
|
855
|
-
actor_invocation_profiles: path.join(sessionRoot, "execution-preparation", "actor-invocation-profiles.yaml"),
|
|
856
|
-
actor_consumer_bindings: path.join(sessionRoot, "execution-preparation", "actor-consumer-bindings.yaml"),
|
|
857
|
-
domain_binding: path.join(sessionRoot, "execution-preparation", "domain-binding.yaml"),
|
|
858
|
-
review_value_alignment_criteria: path.join(sessionRoot, "execution-preparation", "review-value-alignment-criteria.yaml"),
|
|
859
|
-
review_target_profile: path.join(sessionRoot, "execution-preparation", "review-target-profile.yaml"),
|
|
860
|
-
review_context_manifest: path.join(sessionRoot, "execution-preparation", "review-context-manifest.yaml"),
|
|
861
|
-
lens_completion_barrier: path.join(sessionRoot, "lens-completion-barrier.yaml"),
|
|
862
|
-
finding_ledger: path.join(sessionRoot, "finding-ledger.yaml"),
|
|
863
|
-
finding_relation_graph: path.join(sessionRoot, "finding-relation-graph.yaml"),
|
|
864
|
-
issue_ledger: path.join(sessionRoot, "issue-ledger.yaml"),
|
|
865
|
-
issue_stance_matrix: path.join(sessionRoot, "issue-stance-matrix.yaml"),
|
|
866
|
-
deliberation_plan: path.join(sessionRoot, "deliberation-plan.yaml"),
|
|
867
|
-
problem_framing: path.join(sessionRoot, "problem-framing.yaml"),
|
|
868
|
-
deliberation_output: path.join(sessionRoot, "deliberation.md"),
|
|
869
|
-
synthesis_output: path.join(sessionRoot, "synthesis.md"),
|
|
870
|
-
review_run_manifest: path.join(sessionRoot, "review-run-manifest.yaml"),
|
|
871
|
-
degradation_summary: path.join(sessionRoot, "degradation-summary.yaml"),
|
|
872
|
-
error_log: path.join(sessionRoot, "error-log.md"),
|
|
873
|
-
final_output: path.join(sessionRoot, "final-output.md"),
|
|
874
|
-
review_record: path.join(sessionRoot, "review-record.yaml"),
|
|
875
|
-
};
|
|
876
|
-
const entries = [];
|
|
877
|
-
for (const [key, filePath] of Object.entries(candidates)) {
|
|
878
|
-
if (await fileExists(filePath))
|
|
879
|
-
entries.push([key, filePath]);
|
|
1667
|
+
async function resolveContinuationRequestText(args) {
|
|
1668
|
+
if (typeof args.requestText === "string" && args.requestText.trim().length > 0) {
|
|
1669
|
+
return args.requestText;
|
|
880
1670
|
}
|
|
881
|
-
|
|
1671
|
+
const reviewRecord = await readOptionalReviewRecord(path.join(args.sessionRoot, "review-record.yaml"));
|
|
1672
|
+
if (reviewRecord?.request_text)
|
|
1673
|
+
return reviewRecord.request_text;
|
|
1674
|
+
const interpretation = await readOptionalYaml(path.join(args.sessionRoot, "interpretation.yaml"));
|
|
1675
|
+
if (interpretation?.intent_summary)
|
|
1676
|
+
return interpretation.intent_summary;
|
|
1677
|
+
const targetProfile = await readOptionalYaml(path.join(args.sessionRoot, "execution-preparation", "review-target-profile.yaml"));
|
|
1678
|
+
if (targetProfile?.review_intent_summary) {
|
|
1679
|
+
return targetProfile.review_intent_summary;
|
|
1680
|
+
}
|
|
1681
|
+
const metadata = await readOptionalYaml(path.join(args.sessionRoot, "session-metadata.yaml"));
|
|
1682
|
+
return metadata?.requested_target
|
|
1683
|
+
? `Continue review for ${metadata.requested_target}`
|
|
1684
|
+
: "Continue review";
|
|
882
1685
|
}
|
|
883
1686
|
async function directoryHasMarkdownFiles(directoryPath) {
|
|
884
1687
|
try {
|
|
@@ -934,7 +1737,7 @@ async function listDomainDirs(root) {
|
|
|
934
1737
|
try {
|
|
935
1738
|
const entries = await fs.readdir(root, { withFileTypes: true });
|
|
936
1739
|
return entries
|
|
937
|
-
.filter((entry) => entry.isDirectory())
|
|
1740
|
+
.filter((entry) => entry.isDirectory() && !isDeprecatedDomainAlias(entry.name))
|
|
938
1741
|
.map((entry) => entry.name)
|
|
939
1742
|
.sort();
|
|
940
1743
|
}
|
|
@@ -944,10 +1747,10 @@ async function listDomainDirs(root) {
|
|
|
944
1747
|
}
|
|
945
1748
|
export function createOntoReviewCoreApi(options = {}) {
|
|
946
1749
|
const ontoHome = resolveRequiredOntoHome(options.ontoHome);
|
|
947
|
-
|
|
1750
|
+
const api = {
|
|
948
1751
|
async prepareReview(request) {
|
|
949
|
-
|
|
950
|
-
const
|
|
1752
|
+
await validateRequestedDomainForDispatch(request, ontoHome);
|
|
1753
|
+
const result = await prepareReviewInvocationRequest(request, { ontoHome });
|
|
951
1754
|
const sessionRoot = path.resolve(result.session_root);
|
|
952
1755
|
const executionPlan = await readYamlDocument(path.join(sessionRoot, "execution-plan.yaml"));
|
|
953
1756
|
const openingBriefInput = await buildPreparedOpeningBriefInput(sessionRoot, executionPlan);
|
|
@@ -962,9 +1765,17 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
962
1765
|
};
|
|
963
1766
|
},
|
|
964
1767
|
async runReview(request) {
|
|
965
|
-
|
|
1768
|
+
await validateRequestedDomainForDispatch(request, ontoHome);
|
|
1769
|
+
const requestHash = requestHashForReviewInput(request);
|
|
1770
|
+
const invocationId = `initial-${continuationAttemptId()}`;
|
|
966
1771
|
let progressSequence = 0;
|
|
967
1772
|
let observedSessionRoot = null;
|
|
1773
|
+
let sessionRootResolved = false;
|
|
1774
|
+
let activeAttemptWrite = null;
|
|
1775
|
+
let resolveSessionRoot = () => { };
|
|
1776
|
+
const sessionRootSeen = new Promise((resolve) => {
|
|
1777
|
+
resolveSessionRoot = resolve;
|
|
1778
|
+
});
|
|
968
1779
|
const emitProgress = (event) => {
|
|
969
1780
|
const observer = request.progressObserver;
|
|
970
1781
|
if (!observer)
|
|
@@ -983,133 +1794,541 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
983
1794
|
// Progress notifications are transport-only and must not affect review execution.
|
|
984
1795
|
}
|
|
985
1796
|
};
|
|
986
|
-
const
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1797
|
+
const noteSessionRoot = (sessionRoot) => {
|
|
1798
|
+
const resolved = path.resolve(sessionRoot);
|
|
1799
|
+
observedSessionRoot = resolved;
|
|
1800
|
+
if (sessionRootResolved)
|
|
1801
|
+
return;
|
|
1802
|
+
sessionRootResolved = true;
|
|
1803
|
+
resolveSessionRoot(resolved);
|
|
1804
|
+
activeAttemptWrite = (async () => {
|
|
1805
|
+
const executionPlan = await readOptionalYaml(path.join(resolved, "execution-plan.yaml"));
|
|
1806
|
+
await writeActiveAttemptStarted({
|
|
1807
|
+
sessionRoot: resolved,
|
|
1808
|
+
attemptId: invocationId,
|
|
1809
|
+
attemptKind: "initial_review",
|
|
1810
|
+
sourceTool: "onto.review",
|
|
1811
|
+
requestHash,
|
|
1812
|
+
activeUnits: activeUnitsForInitialReview(executionPlan),
|
|
1813
|
+
});
|
|
1814
|
+
})().catch(() => {
|
|
1815
|
+
// Active-attempt metadata is an operational projection; review
|
|
1816
|
+
// execution remains artifact-truthful even if this write fails.
|
|
1817
|
+
});
|
|
1818
|
+
};
|
|
1819
|
+
const runnerProgressObserver = (event) => {
|
|
1820
|
+
if (event.sessionRoot)
|
|
1821
|
+
noteSessionRoot(event.sessionRoot);
|
|
1822
|
+
const stage = event.phase === "prepare"
|
|
1823
|
+
? "session_planned"
|
|
1824
|
+
: event.phase === "execute"
|
|
1825
|
+
? "runtime_step"
|
|
1826
|
+
: event.phase === "project"
|
|
1827
|
+
? "completed"
|
|
1828
|
+
: "invoke_step";
|
|
1829
|
+
const current = event.phase === "resolve"
|
|
1830
|
+
? 5
|
|
1831
|
+
: event.phase === "prepare"
|
|
1832
|
+
? 20
|
|
1833
|
+
: event.phase === "execute"
|
|
1834
|
+
? event.status === "completed" ? 80 : 40
|
|
1835
|
+
: event.phase === "complete"
|
|
1836
|
+
? event.status === "completed" ? 95 : 85
|
|
1837
|
+
: 100;
|
|
1838
|
+
emitProgress({
|
|
1839
|
+
source: "artifact_status",
|
|
1840
|
+
stage,
|
|
1841
|
+
session_root: event.sessionRoot ?? observedSessionRoot,
|
|
1842
|
+
message: event.message,
|
|
1843
|
+
progress: {
|
|
1844
|
+
current,
|
|
1845
|
+
total: 100,
|
|
1846
|
+
label: event.phase,
|
|
1007
1847
|
},
|
|
1848
|
+
});
|
|
1849
|
+
};
|
|
1850
|
+
const fullRun = (async () => {
|
|
1851
|
+
try {
|
|
1852
|
+
const invocation = await runReviewInvocation(request, {
|
|
1853
|
+
ontoHome,
|
|
1854
|
+
noWatch: true,
|
|
1855
|
+
progressObserver: runnerProgressObserver,
|
|
1856
|
+
});
|
|
1857
|
+
const parsed = invocation.output;
|
|
1858
|
+
const result = parsed.review_result;
|
|
1859
|
+
const status = result.record_status ?? "halted_partial";
|
|
1860
|
+
const startPreview = {
|
|
1861
|
+
entrypointPlan: parsed.entrypoint_plan,
|
|
1862
|
+
routeSummary: parsed.route_summary,
|
|
1863
|
+
...(parsed.bounded_invoke_steps !== undefined
|
|
1864
|
+
? { boundedInvokeSteps: parsed.bounded_invoke_steps }
|
|
1865
|
+
: {}),
|
|
1866
|
+
};
|
|
1867
|
+
const resolvedResultSessionRoot = path.resolve(result.session_root);
|
|
1868
|
+
noteSessionRoot(resolvedResultSessionRoot);
|
|
1869
|
+
await activeAttemptWrite;
|
|
1870
|
+
await writeEnvironmentWarningsFromStderr({
|
|
1871
|
+
sessionRoot: resolvedResultSessionRoot,
|
|
1872
|
+
stderr: invocation.stderr,
|
|
1873
|
+
});
|
|
1874
|
+
await updateActiveAttemptTerminal({
|
|
1875
|
+
sessionRoot: resolvedResultSessionRoot,
|
|
1876
|
+
status: status === "halted_partial" ? "halted_partial" : "completed",
|
|
1877
|
+
});
|
|
1878
|
+
const artifactRefs = await collectArtifactRefs(resolvedResultSessionRoot);
|
|
1879
|
+
const failures = await collectStructuredFailures(resolvedResultSessionRoot);
|
|
1880
|
+
const executionPlan = await readOptionalYaml(path.join(resolvedResultSessionRoot, "execution-plan.yaml"));
|
|
1881
|
+
const executionResult = await readOptionalYaml(path.join(resolvedResultSessionRoot, "execution-result.yaml"));
|
|
1882
|
+
const pipelineExecutionLedger = await buildPipelineExecutionLedgerIfPossible({
|
|
1883
|
+
sessionRoot: resolvedResultSessionRoot,
|
|
1884
|
+
artifactRefs,
|
|
1885
|
+
executionPlan,
|
|
1886
|
+
executionResult,
|
|
1887
|
+
});
|
|
1888
|
+
const reviewRecord = await readOptionalReviewRecord(path.join(resolvedResultSessionRoot, "review-record.yaml"));
|
|
1889
|
+
const resultClassificationSummary = await readReviewResultClassification(resolvedResultSessionRoot);
|
|
1890
|
+
const progressInput = await buildReviewStatusPresentationInput({
|
|
1891
|
+
sessionRoot: resolvedResultSessionRoot,
|
|
1892
|
+
status,
|
|
1893
|
+
artifactRefs,
|
|
1894
|
+
executionPlan,
|
|
1895
|
+
executionResult,
|
|
1896
|
+
reviewRecord,
|
|
1897
|
+
});
|
|
1898
|
+
const openingBriefInput = executionPlan
|
|
1899
|
+
? await buildPreparedOpeningBriefInput(resolvedResultSessionRoot, executionPlan)
|
|
1900
|
+
: {
|
|
1901
|
+
presentation_contract_version: REVIEW_PRESENTATION_CONTRACT_VERSION,
|
|
1902
|
+
presentation_kind: "opening_brief",
|
|
1903
|
+
session_id: basenameSessionId(resolvedResultSessionRoot),
|
|
1904
|
+
session_root: resolvedResultSessionRoot,
|
|
1905
|
+
status,
|
|
1906
|
+
generated_from_artifact_refs: generatedFromArtifactRefs(artifactRefs),
|
|
1907
|
+
start_preview: startPreview,
|
|
1908
|
+
};
|
|
1909
|
+
const finalResultInput = {
|
|
1910
|
+
presentation_contract_version: REVIEW_PRESENTATION_CONTRACT_VERSION,
|
|
1911
|
+
presentation_kind: "final_result",
|
|
1912
|
+
session_id: basenameSessionId(resolvedResultSessionRoot),
|
|
1913
|
+
session_root: resolvedResultSessionRoot,
|
|
1914
|
+
status,
|
|
1915
|
+
generated_from_artifact_refs: generatedFromArtifactRefs(artifactRefs),
|
|
1916
|
+
result_overview: parsed.result_overview ?? null,
|
|
1917
|
+
result_classification_summary: resultClassificationSummary,
|
|
1918
|
+
review_result: result,
|
|
1919
|
+
};
|
|
1920
|
+
const llmPresentation = {
|
|
1921
|
+
openingBrief: buildOpeningBriefPresentation(openingBriefInput),
|
|
1922
|
+
progress: buildProgressPresentation(progressInput),
|
|
1923
|
+
...(progressInput.halt
|
|
1924
|
+
? {
|
|
1925
|
+
halt: buildHaltPresentation({
|
|
1926
|
+
...progressInput,
|
|
1927
|
+
presentation_kind: "halt",
|
|
1928
|
+
}),
|
|
1929
|
+
}
|
|
1930
|
+
: {}),
|
|
1931
|
+
finalResult: buildFinalResultPresentation(finalResultInput),
|
|
1932
|
+
};
|
|
1933
|
+
emitProgress({
|
|
1934
|
+
source: "artifact_status",
|
|
1935
|
+
stage: "final_status",
|
|
1936
|
+
session_root: resolvedResultSessionRoot,
|
|
1937
|
+
message: `Review finished with status ${status}.`,
|
|
1938
|
+
progress: {
|
|
1939
|
+
current: 100,
|
|
1940
|
+
total: 100,
|
|
1941
|
+
label: "final status",
|
|
1942
|
+
},
|
|
1943
|
+
});
|
|
1944
|
+
return {
|
|
1945
|
+
sessionId: basenameSessionId(result.session_root),
|
|
1946
|
+
sessionRoot: resolvedResultSessionRoot,
|
|
1947
|
+
status,
|
|
1948
|
+
finalOutputPath: result.final_output_path,
|
|
1949
|
+
reviewRecordPath: result.review_record_path,
|
|
1950
|
+
executionResultPath: result.execution_result_path,
|
|
1951
|
+
reviewRunManifestPath: result.review_run_manifest_path ??
|
|
1952
|
+
path.join(resolvedResultSessionRoot, "review-run-manifest.yaml"),
|
|
1953
|
+
deliberationStatus: result.deliberation_status ?? null,
|
|
1954
|
+
participatingLensIds: result.participating_lens_ids ?? [],
|
|
1955
|
+
degradedLensIds: result.degraded_lens_ids ?? [],
|
|
1956
|
+
...(result.summary !== undefined ? { summary: result.summary } : {}),
|
|
1957
|
+
...(parsed.result_overview !== undefined
|
|
1958
|
+
? { resultOverview: parsed.result_overview }
|
|
1959
|
+
: {}),
|
|
1960
|
+
artifactRefs,
|
|
1961
|
+
...(pipelineExecutionLedger ? { pipelineExecutionLedger } : {}),
|
|
1962
|
+
resultClassificationSummary,
|
|
1963
|
+
...failures,
|
|
1964
|
+
routeVisibility: await buildReviewRouteVisibilityFromSession(resolvedResultSessionRoot),
|
|
1965
|
+
startPreview,
|
|
1966
|
+
llmPresentation,
|
|
1967
|
+
runHandle: await buildReviewRunHandle({
|
|
1968
|
+
sessionRoot: resolvedResultSessionRoot,
|
|
1969
|
+
status,
|
|
1970
|
+
invocationId,
|
|
1971
|
+
}),
|
|
1972
|
+
runControl: progressInput.run_control,
|
|
1973
|
+
targetMaterialSupport: progressInput.target_material_support,
|
|
1974
|
+
environmentWarnings: progressInput.environment_warnings,
|
|
1975
|
+
};
|
|
1008
1976
|
}
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1977
|
+
catch (error) {
|
|
1978
|
+
if (observedSessionRoot) {
|
|
1979
|
+
await updateActiveAttemptTerminal({
|
|
1980
|
+
sessionRoot: observedSessionRoot,
|
|
1981
|
+
status: "failed",
|
|
1982
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
1983
|
+
});
|
|
1984
|
+
}
|
|
1985
|
+
throw error;
|
|
1014
1986
|
}
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1987
|
+
})();
|
|
1988
|
+
if (request.returnRunningAfterMs !== undefined) {
|
|
1989
|
+
const waitMs = Math.max(0, request.returnRunningAfterMs);
|
|
1990
|
+
const earlyRunning = (async () => {
|
|
1991
|
+
const sessionRoot = await sessionRootSeen;
|
|
1992
|
+
if (activeAttemptWrite)
|
|
1993
|
+
await activeAttemptWrite;
|
|
1994
|
+
if (waitMs > 0) {
|
|
1995
|
+
await new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
1996
|
+
}
|
|
1997
|
+
return buildRunningReviewRunResult({
|
|
1998
|
+
sessionRoot,
|
|
1999
|
+
invocationId,
|
|
2000
|
+
});
|
|
2001
|
+
})();
|
|
2002
|
+
const winner = await Promise.race([
|
|
2003
|
+
fullRun.then((result) => ({ kind: "completed", result })),
|
|
2004
|
+
earlyRunning.then((result) => ({ kind: "running", result })),
|
|
2005
|
+
]);
|
|
2006
|
+
if (winner.kind === "running") {
|
|
2007
|
+
fullRun.catch(() => {
|
|
2008
|
+
// The session artifacts and active-attempt projection record the failure.
|
|
2009
|
+
});
|
|
2010
|
+
}
|
|
2011
|
+
return winner.result;
|
|
1020
2012
|
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
}
|
|
1030
|
-
const
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
const
|
|
1037
|
-
const
|
|
1038
|
-
|
|
1039
|
-
|
|
2013
|
+
return fullRun;
|
|
2014
|
+
},
|
|
2015
|
+
async continueReview(request) {
|
|
2016
|
+
const resolvedSessionRoot = path.resolve(request.sessionRoot);
|
|
2017
|
+
const sessionMetadataPath = path.join(resolvedSessionRoot, "session-metadata.yaml");
|
|
2018
|
+
const sessionMetadata = await readOptionalYaml(sessionMetadataPath);
|
|
2019
|
+
if (!sessionMetadata) {
|
|
2020
|
+
throw new Error(`Cannot continue review without session-metadata.yaml: ${resolvedSessionRoot}`);
|
|
2021
|
+
}
|
|
2022
|
+
const projectRoot = path.resolve(request.projectRoot ?? sessionMetadata.project_root);
|
|
2023
|
+
await assertSamePath({
|
|
2024
|
+
label: "ReviewSessionMetadata.project_root",
|
|
2025
|
+
expected: projectRoot,
|
|
2026
|
+
actual: sessionMetadata.project_root,
|
|
2027
|
+
});
|
|
2028
|
+
const artifactRefs = await collectArtifactRefs(resolvedSessionRoot);
|
|
2029
|
+
const executionPlan = await readOptionalYaml(path.join(resolvedSessionRoot, "execution-plan.yaml"));
|
|
2030
|
+
if (!executionPlan) {
|
|
2031
|
+
throw new Error(`Cannot continue review without execution-plan.yaml: ${resolvedSessionRoot}`);
|
|
2032
|
+
}
|
|
2033
|
+
if (executionPlan.session_id !== sessionMetadata.session_id) {
|
|
2034
|
+
throw new Error(`Review continuation session id mismatch: metadata=${sessionMetadata.session_id}, executionPlan=${executionPlan.session_id}`);
|
|
2035
|
+
}
|
|
2036
|
+
await assertSamePath({
|
|
2037
|
+
label: "ReviewExecutionPlan.session_metadata_path",
|
|
2038
|
+
expected: sessionMetadataPath,
|
|
2039
|
+
actual: executionPlan.session_metadata_path,
|
|
2040
|
+
});
|
|
2041
|
+
await assertReviewExecutionPlanSessionBoundary({
|
|
2042
|
+
sessionRoot: resolvedSessionRoot,
|
|
2043
|
+
executionPlan,
|
|
2044
|
+
});
|
|
2045
|
+
const activeRunControl = await buildRunControl(resolvedSessionRoot, "running");
|
|
2046
|
+
if (activeRunControl.alreadyRunning &&
|
|
2047
|
+
requestedUnitsMatchActive(activeRunControl.activeAttempt?.activeUnits ?? [], request.targetUnits)) {
|
|
2048
|
+
const status = await api.getReviewStatus(resolvedSessionRoot);
|
|
2049
|
+
return {
|
|
2050
|
+
sessionId: status.sessionId,
|
|
2051
|
+
sessionRoot: resolvedSessionRoot,
|
|
2052
|
+
decision: "already_running",
|
|
2053
|
+
status: "running",
|
|
2054
|
+
artifactRefs: status.artifactRefs,
|
|
2055
|
+
failureRefs: status.failureRefs,
|
|
2056
|
+
...(status.pipelineExecutionLedger
|
|
2057
|
+
? { pipelineExecutionLedger: status.pipelineExecutionLedger }
|
|
2058
|
+
: {}),
|
|
2059
|
+
resultClassificationSummary: await readReviewResultClassification(resolvedSessionRoot),
|
|
2060
|
+
...(status.routeVisibility !== undefined
|
|
2061
|
+
? { routeVisibility: status.routeVisibility }
|
|
2062
|
+
: {}),
|
|
2063
|
+
...(status.llmPresentation !== undefined
|
|
2064
|
+
? { llmPresentation: status.llmPresentation }
|
|
2065
|
+
: {}),
|
|
2066
|
+
...(activeRunControl.activeAttempt
|
|
2067
|
+
? { activeAttempt: activeRunControl.activeAttempt }
|
|
2068
|
+
: {}),
|
|
2069
|
+
};
|
|
2070
|
+
}
|
|
2071
|
+
const executionResult = await readOptionalYaml(path.join(resolvedSessionRoot, "execution-result.yaml"));
|
|
2072
|
+
const pipelineExecutionLedger = await buildPipelineExecutionLedgerIfPossible({
|
|
2073
|
+
sessionRoot: resolvedSessionRoot,
|
|
1040
2074
|
artifactRefs,
|
|
1041
2075
|
executionPlan,
|
|
1042
2076
|
executionResult,
|
|
1043
|
-
reviewRecord,
|
|
1044
2077
|
});
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
2078
|
+
if (!pipelineExecutionLedger) {
|
|
2079
|
+
throw new Error(`Cannot continue review without a PipelineExecutionLedger: ${resolvedSessionRoot}`);
|
|
2080
|
+
}
|
|
2081
|
+
const continuationPlan = buildReviewContinuationPlan({
|
|
2082
|
+
ledger: pipelineExecutionLedger,
|
|
2083
|
+
...(request.targetUnits !== undefined
|
|
2084
|
+
? { targetUnits: request.targetUnits }
|
|
2085
|
+
: {}),
|
|
2086
|
+
});
|
|
2087
|
+
if (!continuationPlan.eligible) {
|
|
2088
|
+
throw new Error(`Review continuation is not eligible: ${continuationPlan.ineligibleReason ?? "unknown reason"}`);
|
|
2089
|
+
}
|
|
2090
|
+
const reviewRunManifest = await readOptionalYaml(path.join(resolvedSessionRoot, "review-run-manifest.yaml"));
|
|
2091
|
+
const executorRealization = request.executorRealization ??
|
|
2092
|
+
executorRealizationFromManifest(reviewRunManifest);
|
|
2093
|
+
if (!executorRealization) {
|
|
2094
|
+
throw new Error("Review continuation requires executorRealization when the prior review-run-manifest does not expose a worker executor.");
|
|
2095
|
+
}
|
|
2096
|
+
const actorProfiles = await readOptionalYaml(executionPlan.actor_invocation_profiles_path ??
|
|
2097
|
+
path.join(resolvedSessionRoot, "execution-preparation", "actor-invocation-profiles.yaml"));
|
|
2098
|
+
const manifestReviewExecutionProfile = request.executorRealization === undefined
|
|
2099
|
+
? reviewExecutionProfileFromManifest(reviewRunManifest)
|
|
2100
|
+
: undefined;
|
|
2101
|
+
const actorProfileReviewExecutionProfile = manifestReviewExecutionProfile
|
|
2102
|
+
? undefined
|
|
2103
|
+
: reviewExecutionProfileFromActorProfiles({
|
|
2104
|
+
actorProfiles,
|
|
2105
|
+
executorRealization,
|
|
2106
|
+
});
|
|
2107
|
+
const reviewExecutionProfile = manifestReviewExecutionProfile ?? actorProfileReviewExecutionProfile;
|
|
2108
|
+
const reviewExecutionProfileSource = manifestReviewExecutionProfile
|
|
2109
|
+
? "review-run-manifest"
|
|
2110
|
+
: actorProfileReviewExecutionProfile
|
|
2111
|
+
? "actor-invocation-profiles"
|
|
2112
|
+
: "none";
|
|
2113
|
+
const attemptId = continuationAttemptId();
|
|
2114
|
+
const attemptRoot = path.join(resolvedSessionRoot, "continuation-attempts", attemptId);
|
|
2115
|
+
const continuationPlanPath = path.join(attemptRoot, "continuation-plan.yaml");
|
|
2116
|
+
const attemptManifestPath = path.join(attemptRoot, "continuation-attempt.yaml");
|
|
2117
|
+
await writeYamlDocument(continuationPlanPath, continuationPlan);
|
|
2118
|
+
const supersededArtifactBackups = await copySupersededArtifacts({
|
|
2119
|
+
attemptRoot,
|
|
2120
|
+
artifactRefs: [
|
|
2121
|
+
...new Set([
|
|
2122
|
+
...continuationPlan.supersededArtifactRefs,
|
|
2123
|
+
...continuationSessionArtifactRefs({
|
|
2124
|
+
sessionRoot: resolvedSessionRoot,
|
|
2125
|
+
executionPlan,
|
|
2126
|
+
}),
|
|
2127
|
+
]),
|
|
2128
|
+
],
|
|
2129
|
+
});
|
|
2130
|
+
const attemptStartedAt = isoNow();
|
|
2131
|
+
const writeAttemptManifest = async (status, extra = {}) => {
|
|
2132
|
+
await writeYamlDocument(attemptManifestPath, {
|
|
2133
|
+
schema_version: "1",
|
|
2134
|
+
attempt_id: attemptId,
|
|
2135
|
+
session_id: executionPlan.session_id,
|
|
2136
|
+
session_root: resolvedSessionRoot,
|
|
2137
|
+
created_at: attemptStartedAt,
|
|
2138
|
+
updated_at: isoNow(),
|
|
1052
2139
|
status,
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
2140
|
+
executor_realization: executorRealization,
|
|
2141
|
+
target_units: request.targetUnits ?? [],
|
|
2142
|
+
continuation_plan_ref: continuationPlanPath,
|
|
2143
|
+
superseded_artifact_backups: supersededArtifactBackups,
|
|
2144
|
+
execution_route_provenance: {
|
|
2145
|
+
executor_realization: executorRealization,
|
|
2146
|
+
review_execution_profile_source: reviewExecutionProfileSource,
|
|
2147
|
+
requested_executor_realization: request.executorRealization ?? null,
|
|
2148
|
+
previous_execution_realization: executionPlan.execution_realization,
|
|
2149
|
+
previous_host_runtime: executionPlan.host_runtime,
|
|
2150
|
+
},
|
|
2151
|
+
...extra,
|
|
2152
|
+
});
|
|
1066
2153
|
};
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
2154
|
+
await writeAttemptManifest("started");
|
|
2155
|
+
await writeActiveAttemptStarted({
|
|
2156
|
+
sessionRoot: resolvedSessionRoot,
|
|
2157
|
+
attemptId,
|
|
2158
|
+
attemptKind: "continuation",
|
|
2159
|
+
sourceTool: "onto.review_continue",
|
|
2160
|
+
requestHash: null,
|
|
2161
|
+
activeUnits: continuationPlan.frontierUnits.map((unit) => unit.unitId),
|
|
2162
|
+
requestedFrontierUnits: request.targetUnits ?? [],
|
|
2163
|
+
});
|
|
2164
|
+
let promptExecutionResult;
|
|
2165
|
+
try {
|
|
2166
|
+
const executorConfig = buildExecutorConfigFromRealization(executorRealization, ontoHome);
|
|
2167
|
+
promptExecutionResult = (await withCapturedConsole(() => executeReviewPromptExecution({
|
|
2168
|
+
projectRoot,
|
|
2169
|
+
sessionRoot: resolvedSessionRoot,
|
|
2170
|
+
defaultExecutorConfig: executorConfig,
|
|
2171
|
+
...(reviewExecutionProfile ? { reviewExecutionProfile } : {}),
|
|
2172
|
+
continuationPlan,
|
|
2173
|
+
}))).result;
|
|
2174
|
+
if (promptExecutionResult.synthesis_executed) {
|
|
2175
|
+
const requestText = await resolveContinuationRequestText({
|
|
2176
|
+
sessionRoot: resolvedSessionRoot,
|
|
2177
|
+
...(request.requestText ? { requestText: request.requestText } : {}),
|
|
2178
|
+
});
|
|
2179
|
+
await withCapturedConsole(() => completeReviewSession([
|
|
2180
|
+
"--project-root",
|
|
2181
|
+
projectRoot,
|
|
2182
|
+
"--session-root",
|
|
2183
|
+
resolvedSessionRoot,
|
|
2184
|
+
"--request-text",
|
|
2185
|
+
requestText,
|
|
2186
|
+
]));
|
|
2187
|
+
}
|
|
2188
|
+
await writeAttemptManifest(promptExecutionResult.synthesis_executed
|
|
2189
|
+
? "completed"
|
|
2190
|
+
: "halted_partial", { prompt_execution_result: promptExecutionResult });
|
|
2191
|
+
await updateActiveAttemptTerminal({
|
|
2192
|
+
sessionRoot: resolvedSessionRoot,
|
|
2193
|
+
status: promptExecutionResult.synthesis_executed
|
|
2194
|
+
? "completed"
|
|
2195
|
+
: "halted_partial",
|
|
2196
|
+
});
|
|
2197
|
+
}
|
|
2198
|
+
catch (error) {
|
|
2199
|
+
const restoredArtifactBackups = await restoreSupersededArtifacts(supersededArtifactBackups);
|
|
2200
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2201
|
+
await writeAttemptManifest("failed", {
|
|
2202
|
+
error_message: errorMessage,
|
|
2203
|
+
...(promptExecutionResult
|
|
2204
|
+
? { prompt_execution_result: promptExecutionResult }
|
|
2205
|
+
: {}),
|
|
2206
|
+
restored_artifact_backups: restoredArtifactBackups,
|
|
2207
|
+
});
|
|
2208
|
+
await updateActiveAttemptTerminal({
|
|
2209
|
+
sessionRoot: resolvedSessionRoot,
|
|
2210
|
+
status: "failed",
|
|
2211
|
+
errorMessage,
|
|
2212
|
+
});
|
|
2213
|
+
throw new ReviewContinuationError({
|
|
2214
|
+
message: `Review continuation failed: ${errorMessage}`,
|
|
2215
|
+
originalError: error,
|
|
2216
|
+
failureContent: {
|
|
2217
|
+
mcp_error_code: "ONTO_REVIEW_CONTINUATION_FAILED",
|
|
2218
|
+
session_id: executionPlan.session_id,
|
|
2219
|
+
session_root: resolvedSessionRoot,
|
|
2220
|
+
attempt_id: attemptId,
|
|
2221
|
+
attempt_root: attemptRoot,
|
|
2222
|
+
attempt_manifest_ref: attemptManifestPath,
|
|
2223
|
+
continuation_plan_ref: continuationPlanPath,
|
|
2224
|
+
continuation_plan: continuationPlan,
|
|
2225
|
+
superseded_artifact_backups: supersededArtifactBackups,
|
|
2226
|
+
restored_artifact_backups: restoredArtifactBackups,
|
|
2227
|
+
error_message: errorMessage,
|
|
2228
|
+
},
|
|
2229
|
+
});
|
|
2230
|
+
}
|
|
2231
|
+
if (!promptExecutionResult) {
|
|
2232
|
+
throw new Error("Review continuation finished without prompt execution result.");
|
|
2233
|
+
}
|
|
2234
|
+
const postStatus = await api.getReviewStatus(resolvedSessionRoot);
|
|
2235
|
+
return {
|
|
2236
|
+
sessionId: postStatus.sessionId,
|
|
2237
|
+
sessionRoot: resolvedSessionRoot,
|
|
2238
|
+
decision: "executed",
|
|
2239
|
+
status: postStatus.status,
|
|
2240
|
+
continuationPlan,
|
|
2241
|
+
continuationAttempt: {
|
|
2242
|
+
attemptId,
|
|
2243
|
+
attemptRoot,
|
|
2244
|
+
continuationPlanPath,
|
|
2245
|
+
attemptManifestPath,
|
|
2246
|
+
supersededArtifactBackups,
|
|
2247
|
+
},
|
|
2248
|
+
promptExecutionResult,
|
|
2249
|
+
artifactRefs: postStatus.artifactRefs,
|
|
2250
|
+
...(postStatus.pipelineExecutionLedger
|
|
2251
|
+
? { pipelineExecutionLedger: postStatus.pipelineExecutionLedger }
|
|
2252
|
+
: {}),
|
|
2253
|
+
resultClassificationSummary: await readReviewResultClassification(resolvedSessionRoot),
|
|
2254
|
+
failureRefs: postStatus.failureRefs,
|
|
2255
|
+
...(postStatus.routeVisibility !== undefined
|
|
2256
|
+
? { routeVisibility: postStatus.routeVisibility }
|
|
2257
|
+
: {}),
|
|
2258
|
+
...(postStatus.llmPresentation !== undefined
|
|
2259
|
+
? { llmPresentation: postStatus.llmPresentation }
|
|
1077
2260
|
: {}),
|
|
1078
|
-
finalResult: buildFinalResultPresentation(finalResultInput),
|
|
1079
2261
|
};
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
2262
|
+
},
|
|
2263
|
+
async cancelReview(request) {
|
|
2264
|
+
const resolvedSessionRoot = path.resolve(request.sessionRoot);
|
|
2265
|
+
const sessionMetadata = await readOptionalYaml(path.join(resolvedSessionRoot, "session-metadata.yaml"));
|
|
2266
|
+
if (!sessionMetadata) {
|
|
2267
|
+
throw new Error(`Cannot cancel review without session-metadata.yaml: ${resolvedSessionRoot}`);
|
|
2268
|
+
}
|
|
2269
|
+
const projectRoot = path.resolve(request.projectRoot ?? sessionMetadata.project_root);
|
|
2270
|
+
await assertSamePath({
|
|
2271
|
+
label: "ReviewSessionMetadata.project_root",
|
|
2272
|
+
expected: projectRoot,
|
|
2273
|
+
actual: sessionMetadata.project_root,
|
|
1090
2274
|
});
|
|
2275
|
+
const statusBeforeCancel = await api.getReviewStatus(resolvedSessionRoot);
|
|
2276
|
+
if (reviewTerminalStatus(statusBeforeCancel.status)) {
|
|
2277
|
+
return {
|
|
2278
|
+
sessionId: statusBeforeCancel.sessionId,
|
|
2279
|
+
sessionRoot: resolvedSessionRoot,
|
|
2280
|
+
decision: "already_terminal",
|
|
2281
|
+
status: statusBeforeCancel.status,
|
|
2282
|
+
cancelRequestPath: reviewCancelRequestPath(resolvedSessionRoot),
|
|
2283
|
+
reason: "review is already terminal",
|
|
2284
|
+
artifactRefs: statusBeforeCancel.artifactRefs,
|
|
2285
|
+
...(statusBeforeCancel.runControl
|
|
2286
|
+
? { runControl: statusBeforeCancel.runControl }
|
|
2287
|
+
: {}),
|
|
2288
|
+
...(statusBeforeCancel.llmPresentation
|
|
2289
|
+
? { llmPresentation: statusBeforeCancel.llmPresentation }
|
|
2290
|
+
: {}),
|
|
2291
|
+
};
|
|
2292
|
+
}
|
|
2293
|
+
if (!statusBeforeCancel.runControl?.cancellationAvailable) {
|
|
2294
|
+
return {
|
|
2295
|
+
sessionId: statusBeforeCancel.sessionId,
|
|
2296
|
+
sessionRoot: resolvedSessionRoot,
|
|
2297
|
+
decision: "not_cancellable",
|
|
2298
|
+
status: statusBeforeCancel.status,
|
|
2299
|
+
cancelRequestPath: reviewCancelRequestPath(resolvedSessionRoot),
|
|
2300
|
+
reason: statusBeforeCancel.runControl?.statusReason ??
|
|
2301
|
+
"review is not currently cancellable",
|
|
2302
|
+
artifactRefs: statusBeforeCancel.artifactRefs,
|
|
2303
|
+
...(statusBeforeCancel.runControl
|
|
2304
|
+
? { runControl: statusBeforeCancel.runControl }
|
|
2305
|
+
: {}),
|
|
2306
|
+
...(statusBeforeCancel.llmPresentation
|
|
2307
|
+
? { llmPresentation: statusBeforeCancel.llmPresentation }
|
|
2308
|
+
: {}),
|
|
2309
|
+
};
|
|
2310
|
+
}
|
|
2311
|
+
const reason = request.reason?.trim() || "operator requested cancellation";
|
|
2312
|
+
const cancelRequest = {
|
|
2313
|
+
schema_version: "1",
|
|
2314
|
+
session_id: sessionMetadata.session_id,
|
|
2315
|
+
requested_at: isoNow(),
|
|
2316
|
+
requested_by: "mcp",
|
|
2317
|
+
reason,
|
|
2318
|
+
};
|
|
2319
|
+
const cancelRequestPath = reviewCancelRequestPath(resolvedSessionRoot);
|
|
2320
|
+
await writeYamlDocument(cancelRequestPath, cancelRequest);
|
|
2321
|
+
const status = await api.getReviewStatus(resolvedSessionRoot);
|
|
1091
2322
|
return {
|
|
1092
|
-
sessionId:
|
|
1093
|
-
sessionRoot:
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
participatingLensIds: result.participating_lens_ids ?? [],
|
|
1102
|
-
degradedLensIds: result.degraded_lens_ids ?? [],
|
|
1103
|
-
...(result.summary !== undefined ? { summary: result.summary } : {}),
|
|
1104
|
-
...(parsed.result_overview !== undefined
|
|
1105
|
-
? { resultOverview: parsed.result_overview }
|
|
1106
|
-
: {}),
|
|
1107
|
-
artifactRefs,
|
|
1108
|
-
resultClassificationSummary,
|
|
1109
|
-
...failures,
|
|
1110
|
-
routeVisibility: await buildReviewRouteVisibilityFromSession(resolvedResultSessionRoot),
|
|
1111
|
-
startPreview,
|
|
1112
|
-
llmPresentation,
|
|
2323
|
+
sessionId: status.sessionId,
|
|
2324
|
+
sessionRoot: resolvedSessionRoot,
|
|
2325
|
+
decision: "requested",
|
|
2326
|
+
status: status.status,
|
|
2327
|
+
cancelRequestPath,
|
|
2328
|
+
reason,
|
|
2329
|
+
artifactRefs: status.artifactRefs,
|
|
2330
|
+
...(status.runControl ? { runControl: status.runControl } : {}),
|
|
2331
|
+
...(status.llmPresentation ? { llmPresentation: status.llmPresentation } : {}),
|
|
1113
2332
|
};
|
|
1114
2333
|
},
|
|
1115
2334
|
async getReviewStatus(sessionRoot) {
|
|
@@ -1118,16 +2337,32 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1118
2337
|
const failures = await collectStructuredFailures(resolvedSessionRoot);
|
|
1119
2338
|
const executionPlan = await readOptionalYaml(path.join(resolvedSessionRoot, "execution-plan.yaml"));
|
|
1120
2339
|
const executionResult = await readOptionalYaml(path.join(resolvedSessionRoot, "execution-result.yaml"));
|
|
2340
|
+
const pipelineExecutionLedger = await buildPipelineExecutionLedgerIfPossible({
|
|
2341
|
+
sessionRoot: resolvedSessionRoot,
|
|
2342
|
+
artifactRefs,
|
|
2343
|
+
executionPlan,
|
|
2344
|
+
executionResult,
|
|
2345
|
+
});
|
|
2346
|
+
const continuationPlan = pipelineExecutionLedger
|
|
2347
|
+
? buildReviewContinuationPlan({ ledger: pipelineExecutionLedger })
|
|
2348
|
+
: undefined;
|
|
1121
2349
|
const reviewRecord = await readOptionalReviewRecord(path.join(resolvedSessionRoot, "review-record.yaml"));
|
|
2350
|
+
const activeAttempt = await activeAttemptProjection(resolvedSessionRoot);
|
|
2351
|
+
const activeRunInProgress = activeAttempt?.status === "started" && !activeAttempt.isStale;
|
|
1122
2352
|
const status = reviewRecord
|
|
1123
2353
|
? reviewRecord.record_status
|
|
1124
2354
|
: executionResult?.execution_status === "halted_partial"
|
|
1125
2355
|
? "halted_partial"
|
|
1126
|
-
:
|
|
1127
|
-
?
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
2356
|
+
: activeAttempt?.status === "failed"
|
|
2357
|
+
? "failed"
|
|
2358
|
+
: activeAttempt?.status === "halted_partial"
|
|
2359
|
+
? "halted_partial"
|
|
2360
|
+
: executionPlan
|
|
2361
|
+
? (activeRunInProgress ||
|
|
2362
|
+
await hasRunArtifacts(resolvedSessionRoot, artifactRefs))
|
|
2363
|
+
? "running"
|
|
2364
|
+
: "prepared"
|
|
2365
|
+
: "unknown";
|
|
1131
2366
|
const progressInput = await buildReviewStatusPresentationInput({
|
|
1132
2367
|
sessionRoot: resolvedSessionRoot,
|
|
1133
2368
|
status,
|
|
@@ -1154,9 +2389,15 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1154
2389
|
sessionRoot: resolvedSessionRoot,
|
|
1155
2390
|
status,
|
|
1156
2391
|
artifactRefs,
|
|
2392
|
+
...(pipelineExecutionLedger ? { pipelineExecutionLedger } : {}),
|
|
2393
|
+
...(continuationPlan ? { continuationPlan } : {}),
|
|
1157
2394
|
...failures,
|
|
1158
2395
|
routeVisibility: await buildReviewRouteVisibilityFromSession(resolvedSessionRoot),
|
|
1159
2396
|
llmPresentation,
|
|
2397
|
+
runControl: progressInput.run_control,
|
|
2398
|
+
targetMaterialSupport: progressInput.target_material_support,
|
|
2399
|
+
environmentWarnings: progressInput.environment_warnings,
|
|
2400
|
+
unitProgress: progressInput.progress.unit_progress,
|
|
1160
2401
|
};
|
|
1161
2402
|
}
|
|
1162
2403
|
if (executionResult?.execution_status === "halted_partial") {
|
|
@@ -1165,9 +2406,15 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1165
2406
|
sessionRoot: resolvedSessionRoot,
|
|
1166
2407
|
status,
|
|
1167
2408
|
artifactRefs,
|
|
2409
|
+
...(pipelineExecutionLedger ? { pipelineExecutionLedger } : {}),
|
|
2410
|
+
...(continuationPlan ? { continuationPlan } : {}),
|
|
1168
2411
|
...failures,
|
|
1169
2412
|
routeVisibility: await buildReviewRouteVisibilityFromSession(resolvedSessionRoot),
|
|
1170
2413
|
llmPresentation,
|
|
2414
|
+
runControl: progressInput.run_control,
|
|
2415
|
+
targetMaterialSupport: progressInput.target_material_support,
|
|
2416
|
+
environmentWarnings: progressInput.environment_warnings,
|
|
2417
|
+
unitProgress: progressInput.progress.unit_progress,
|
|
1171
2418
|
};
|
|
1172
2419
|
}
|
|
1173
2420
|
if (executionPlan) {
|
|
@@ -1176,9 +2423,15 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1176
2423
|
sessionRoot: resolvedSessionRoot,
|
|
1177
2424
|
status,
|
|
1178
2425
|
artifactRefs,
|
|
2426
|
+
...(pipelineExecutionLedger ? { pipelineExecutionLedger } : {}),
|
|
2427
|
+
...(continuationPlan ? { continuationPlan } : {}),
|
|
1179
2428
|
...failures,
|
|
1180
2429
|
routeVisibility: await buildReviewRouteVisibilityFromSession(resolvedSessionRoot),
|
|
1181
2430
|
llmPresentation,
|
|
2431
|
+
runControl: progressInput.run_control,
|
|
2432
|
+
targetMaterialSupport: progressInput.target_material_support,
|
|
2433
|
+
environmentWarnings: progressInput.environment_warnings,
|
|
2434
|
+
unitProgress: progressInput.progress.unit_progress,
|
|
1182
2435
|
};
|
|
1183
2436
|
}
|
|
1184
2437
|
return {
|
|
@@ -1186,21 +2439,41 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1186
2439
|
sessionRoot: resolvedSessionRoot,
|
|
1187
2440
|
status: "unknown",
|
|
1188
2441
|
artifactRefs,
|
|
2442
|
+
...(pipelineExecutionLedger ? { pipelineExecutionLedger } : {}),
|
|
2443
|
+
...(continuationPlan ? { continuationPlan } : {}),
|
|
1189
2444
|
...failures,
|
|
1190
2445
|
routeVisibility: await buildReviewRouteVisibilityFromSession(resolvedSessionRoot),
|
|
1191
2446
|
llmPresentation,
|
|
2447
|
+
runControl: progressInput.run_control,
|
|
2448
|
+
targetMaterialSupport: progressInput.target_material_support,
|
|
2449
|
+
environmentWarnings: progressInput.environment_warnings,
|
|
2450
|
+
unitProgress: progressInput.progress.unit_progress,
|
|
1192
2451
|
};
|
|
1193
2452
|
},
|
|
1194
|
-
async getReviewResult(sessionRoot) {
|
|
2453
|
+
async getReviewResult(sessionRoot, options = {}) {
|
|
1195
2454
|
const resolvedSessionRoot = path.resolve(sessionRoot);
|
|
2455
|
+
const projectionLevel = options.projectionLevel ?? "full";
|
|
1196
2456
|
const artifactRefs = await collectArtifactRefs(resolvedSessionRoot);
|
|
1197
2457
|
const { failureRefs } = await collectStructuredFailures(resolvedSessionRoot);
|
|
1198
2458
|
const reviewRecordPath = path.join(resolvedSessionRoot, "review-record.yaml");
|
|
1199
2459
|
const reviewRecord = await readValidatedReviewRecord(reviewRecordPath);
|
|
1200
|
-
const
|
|
1201
|
-
const
|
|
2460
|
+
const resultSessionMetadata = await readOptionalYaml(path.join(resolvedSessionRoot, "session-metadata.yaml"));
|
|
2461
|
+
const finalOutputPath = await resolveReviewRecordFinalOutputPath({
|
|
2462
|
+
sessionRoot: resolvedSessionRoot,
|
|
2463
|
+
projectRoot: resultSessionMetadata?.project_root ?? null,
|
|
2464
|
+
finalOutputRef: reviewRecord.final_output_ref,
|
|
2465
|
+
});
|
|
2466
|
+
const finalOutputText = projectionLevel === "compact"
|
|
2467
|
+
? undefined
|
|
2468
|
+
: await readOptionalText(finalOutputPath);
|
|
1202
2469
|
const executionPlan = await readOptionalYaml(path.join(resolvedSessionRoot, "execution-plan.yaml"));
|
|
1203
2470
|
const executionResult = await readOptionalYaml(path.join(resolvedSessionRoot, "execution-result.yaml"));
|
|
2471
|
+
const pipelineExecutionLedger = await buildPipelineExecutionLedgerIfPossible({
|
|
2472
|
+
sessionRoot: resolvedSessionRoot,
|
|
2473
|
+
artifactRefs,
|
|
2474
|
+
executionPlan,
|
|
2475
|
+
executionResult,
|
|
2476
|
+
});
|
|
1204
2477
|
const resultClassificationSummary = await readReviewResultClassification(resolvedSessionRoot);
|
|
1205
2478
|
const status = reviewRecord.record_status;
|
|
1206
2479
|
const progressInput = await buildReviewStatusPresentationInput({
|
|
@@ -1219,15 +2492,36 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1219
2492
|
status,
|
|
1220
2493
|
generated_from_artifact_refs: generatedFromArtifactRefs(artifactRefs),
|
|
1221
2494
|
result_classification_summary: resultClassificationSummary,
|
|
1222
|
-
review_record: reviewRecord,
|
|
2495
|
+
review_record: projectionLevel === "full" ? reviewRecord : null,
|
|
2496
|
+
review_record_summary: {
|
|
2497
|
+
review_record_id: reviewRecord.review_record_id,
|
|
2498
|
+
record_status: reviewRecord.record_status,
|
|
2499
|
+
resolved_lens_ids: reviewRecord.resolved_lens_ids,
|
|
2500
|
+
participating_lens_ids: reviewRecord.participating_lens_ids,
|
|
2501
|
+
degraded_lens_ids: reviewRecord.degraded_lens_ids,
|
|
2502
|
+
deliberation_status: reviewRecord.deliberation_status,
|
|
2503
|
+
},
|
|
1223
2504
|
};
|
|
2505
|
+
const targetMaterialSupport = await readTargetMaterialSupport(resolvedSessionRoot, executionPlan);
|
|
2506
|
+
const environmentWarnings = await readEnvironmentWarnings(resolvedSessionRoot);
|
|
1224
2507
|
return {
|
|
1225
2508
|
sessionId: reviewRecord.session_id,
|
|
1226
2509
|
sessionRoot: resolvedSessionRoot,
|
|
1227
|
-
|
|
2510
|
+
projectionLevel,
|
|
2511
|
+
reviewRecordSummary: {
|
|
2512
|
+
reviewRecordId: reviewRecord.review_record_id,
|
|
2513
|
+
recordStatus: reviewRecord.record_status,
|
|
2514
|
+
requestText: reviewRecord.request_text,
|
|
2515
|
+
resolvedLensIds: reviewRecord.resolved_lens_ids,
|
|
2516
|
+
participatingLensIds: reviewRecord.participating_lens_ids,
|
|
2517
|
+
degradedLensIds: reviewRecord.degraded_lens_ids,
|
|
2518
|
+
deliberationStatus: reviewRecord.deliberation_status,
|
|
2519
|
+
},
|
|
2520
|
+
...(projectionLevel === "full" ? { reviewRecord } : {}),
|
|
1228
2521
|
finalOutputPath,
|
|
1229
2522
|
reviewRunManifestPath: path.join(resolvedSessionRoot, "review-run-manifest.yaml"),
|
|
1230
2523
|
artifactRefs,
|
|
2524
|
+
...(pipelineExecutionLedger ? { pipelineExecutionLedger } : {}),
|
|
1231
2525
|
resultClassificationSummary,
|
|
1232
2526
|
failureRefs,
|
|
1233
2527
|
routeVisibility: await buildReviewRouteVisibilityFromSession(resolvedSessionRoot),
|
|
@@ -1243,9 +2537,87 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1243
2537
|
: {}),
|
|
1244
2538
|
finalResult: buildFinalResultPresentation(finalResultInput),
|
|
1245
2539
|
},
|
|
2540
|
+
targetMaterialSupport,
|
|
2541
|
+
environmentWarnings,
|
|
1246
2542
|
...(finalOutputText !== undefined ? { finalOutputText } : {}),
|
|
1247
2543
|
};
|
|
1248
2544
|
},
|
|
2545
|
+
async findLatestReviewSessions(query) {
|
|
2546
|
+
const projectRoot = path.resolve(query.projectRoot);
|
|
2547
|
+
const reviewRoot = path.join(projectRoot, ".onto", "review");
|
|
2548
|
+
let entries;
|
|
2549
|
+
try {
|
|
2550
|
+
entries = await fs.readdir(reviewRoot, { withFileTypes: true });
|
|
2551
|
+
}
|
|
2552
|
+
catch {
|
|
2553
|
+
return [];
|
|
2554
|
+
}
|
|
2555
|
+
const createdAfterMs = query.createdAfter
|
|
2556
|
+
? parseTimestampMs(query.createdAfter)
|
|
2557
|
+
: null;
|
|
2558
|
+
const targetFilter = query.target ? path.normalize(query.target) : null;
|
|
2559
|
+
const domainFilter = query.domain ? normalizeDomainValue(query.domain) : null;
|
|
2560
|
+
const matches = [];
|
|
2561
|
+
for (const entry of entries) {
|
|
2562
|
+
if (!entry.isDirectory())
|
|
2563
|
+
continue;
|
|
2564
|
+
const sessionRoot = path.join(reviewRoot, entry.name);
|
|
2565
|
+
const metadata = await readOptionalYaml(path.join(sessionRoot, "session-metadata.yaml"));
|
|
2566
|
+
if (!metadata)
|
|
2567
|
+
continue;
|
|
2568
|
+
const interpretation = await readOptionalYaml(path.join(sessionRoot, "interpretation.yaml"));
|
|
2569
|
+
const binding = await readOptionalYaml(path.join(sessionRoot, "binding.yaml"));
|
|
2570
|
+
const targetProfile = await readOptionalYaml(path.join(sessionRoot, "execution-preparation", "review-target-profile.yaml"));
|
|
2571
|
+
const createdAt = metadata.created_at ?? null;
|
|
2572
|
+
const createdAtMs = parseTimestampMs(createdAt);
|
|
2573
|
+
if (createdAfterMs !== null &&
|
|
2574
|
+
createdAtMs !== null &&
|
|
2575
|
+
createdAtMs < createdAfterMs) {
|
|
2576
|
+
continue;
|
|
2577
|
+
}
|
|
2578
|
+
if (targetFilter &&
|
|
2579
|
+
path.normalize(metadata.requested_target) !== targetFilter &&
|
|
2580
|
+
path.normalize(targetProfile?.requested_target ?? "") !== targetFilter) {
|
|
2581
|
+
continue;
|
|
2582
|
+
}
|
|
2583
|
+
const normalizedDomain = binding?.resolved_session_domain ??
|
|
2584
|
+
targetProfile?.domain ??
|
|
2585
|
+
normalizeDomainValue(metadata.requested_domain_token ?? "");
|
|
2586
|
+
if (domainFilter &&
|
|
2587
|
+
normalizeDomainValue(normalizedDomain) !== domainFilter) {
|
|
2588
|
+
continue;
|
|
2589
|
+
}
|
|
2590
|
+
const requestHash = requestHashFromArtifacts({
|
|
2591
|
+
metadata,
|
|
2592
|
+
interpretation,
|
|
2593
|
+
binding,
|
|
2594
|
+
});
|
|
2595
|
+
if (query.requestHash && requestHash !== query.requestHash) {
|
|
2596
|
+
continue;
|
|
2597
|
+
}
|
|
2598
|
+
const artifactRefs = await collectArtifactRefs(sessionRoot);
|
|
2599
|
+
const status = (await api.getReviewStatus(sessionRoot)).status;
|
|
2600
|
+
matches.push({
|
|
2601
|
+
sessionId: metadata.session_id ?? entry.name,
|
|
2602
|
+
sessionRoot,
|
|
2603
|
+
createdAt,
|
|
2604
|
+
requestedTarget: metadata.requested_target ?? null,
|
|
2605
|
+
requestedDomainToken: metadata.requested_domain_token ?? null,
|
|
2606
|
+
normalizedDomain: normalizedDomain === "none" || normalizedDomain.length === 0
|
|
2607
|
+
? null
|
|
2608
|
+
: normalizeDomainValue(normalizedDomain),
|
|
2609
|
+
requestHash,
|
|
2610
|
+
status,
|
|
2611
|
+
artifactRefs,
|
|
2612
|
+
});
|
|
2613
|
+
}
|
|
2614
|
+
matches.sort((a, b) => {
|
|
2615
|
+
const left = parseTimestampMs(a.createdAt) ?? 0;
|
|
2616
|
+
const right = parseTimestampMs(b.createdAt) ?? 0;
|
|
2617
|
+
return right - left;
|
|
2618
|
+
});
|
|
2619
|
+
return matches.slice(0, query.limit ?? 5);
|
|
2620
|
+
},
|
|
1249
2621
|
async listLenses() {
|
|
1250
2622
|
const registry = loadCoreLensRegistry();
|
|
1251
2623
|
return {
|
|
@@ -1268,4 +2640,5 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1268
2640
|
return [...names].sort();
|
|
1269
2641
|
},
|
|
1270
2642
|
};
|
|
2643
|
+
return api;
|
|
1271
2644
|
}
|