onto-mcp 0.3.1 → 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 +1 -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/reconstruct/reconstruct-boundary-contract.md +278 -91
- package/.onto/processes/reconstruct/reconstruct-execution-ux-contract.md +45 -12
- 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/lens-registry.md +16 -0
- package/.onto/processes/shared/target-material-kind-contract.md +18 -2
- package/.onto/roles/axiology.md +7 -2
- package/AGENTS.md +3 -2
- package/README.md +39 -33
- package/dist/core-api/reconstruct-api.js +22 -5
- package/dist/core-api/review-api.js +1288 -533
- package/dist/core-runtime/cli/mock-review-unit-executor.js +17 -0
- package/dist/core-runtime/cli/review-invoke.js +23 -48
- package/dist/core-runtime/cli/run-review-prompt-execution.js +122 -0
- package/dist/core-runtime/path-boundary.js +58 -0
- package/dist/core-runtime/reconstruct/artifact-types.js +5 -0
- package/dist/core-runtime/reconstruct/materialize-preparation.js +54 -4
- package/dist/core-runtime/reconstruct/pipeline-execution-ledger.js +38 -2
- package/dist/core-runtime/reconstruct/post-seed-validation.js +13 -0
- package/dist/core-runtime/reconstruct/record.js +11 -0
- package/dist/core-runtime/reconstruct/run.js +1133 -26
- package/dist/core-runtime/reconstruct/seed-candidate-validation.js +29 -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/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 +158 -39
- package/dist/mcp/tool-schemas.js +22 -2
- package/package.json +3 -1
- 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
|
@@ -4,13 +4,16 @@ import os from "node:os";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { resolveOntoHome } from "../core-runtime/discovery/onto-home.js";
|
|
6
6
|
import { loadCoreLensRegistry } from "../core-runtime/discovery/lens-registry.js";
|
|
7
|
-
import { fileExists, isoFromTimestamp, isoNow, readYamlDocument, writeYamlDocument, } 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";
|
|
8
10
|
import { buildReviewRouteVisibilityFromSession, } from "../core-runtime/review/route-visibility.js";
|
|
9
11
|
import { readValidatedReviewRecord } from "../core-runtime/review/review-record-validation.js";
|
|
10
12
|
import { readReviewResultClassification } from "../core-runtime/review/review-result-classification.js";
|
|
11
13
|
import { REVIEW_EXECUTION_STEP_IDS, REVIEW_PROGRESS_STEPS, REVIEW_PROGRESS_TOTAL_STEPS, reviewProgressStepById, reviewProgressStepIdFromHalt, } from "../core-runtime/review/review-progress-contract.js";
|
|
14
|
+
import { collectReviewInvocationArtifactRefs, prepareReviewInvocationRequest, runReviewInvocation, } from "../core-runtime/review/review-invocation-runner.js";
|
|
12
15
|
import { completeReviewSession } from "../core-runtime/cli/complete-review-session.js";
|
|
13
|
-
import { buildExecutorConfigFromRealization,
|
|
16
|
+
import { buildExecutorConfigFromRealization, } from "../core-runtime/cli/review-invoke.js";
|
|
14
17
|
import { executeReviewPromptExecution, } from "../core-runtime/cli/run-review-prompt-execution.js";
|
|
15
18
|
import { buildReviewPipelineExecutionLedger, } from "../core-runtime/review/pipeline-execution-ledger.js";
|
|
16
19
|
import { buildReviewContinuationPlan, } from "../core-runtime/review/continuation-plan.js";
|
|
@@ -24,6 +27,14 @@ export class ReviewContinuationError extends Error {
|
|
|
24
27
|
this.failureContent = args.failureContent;
|
|
25
28
|
}
|
|
26
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
|
+
}
|
|
27
38
|
function stringifyConsoleArgs(args) {
|
|
28
39
|
return args
|
|
29
40
|
.map((arg) => {
|
|
@@ -89,53 +100,6 @@ async function withCapturedConsole(action, observer) {
|
|
|
89
100
|
function resolveRequiredOntoHome(explicit) {
|
|
90
101
|
return resolveOntoHome(explicit);
|
|
91
102
|
}
|
|
92
|
-
function appendCommonReviewArgs(args, request, ontoHome) {
|
|
93
|
-
const result = [
|
|
94
|
-
...args,
|
|
95
|
-
request.target,
|
|
96
|
-
request.intent,
|
|
97
|
-
"--project-root",
|
|
98
|
-
path.resolve(request.projectRoot),
|
|
99
|
-
];
|
|
100
|
-
result.push("--onto-home", ontoHome);
|
|
101
|
-
if (request.domain && request.noDomain) {
|
|
102
|
-
throw new Error("Use either domain or noDomain, not both.");
|
|
103
|
-
}
|
|
104
|
-
if (request.domain) {
|
|
105
|
-
result.push("--domain", request.domain);
|
|
106
|
-
}
|
|
107
|
-
if (request.noDomain) {
|
|
108
|
-
result.push("--no-domain");
|
|
109
|
-
}
|
|
110
|
-
if (request.reviewMode) {
|
|
111
|
-
result.push("--review-mode", request.reviewMode);
|
|
112
|
-
}
|
|
113
|
-
if (request.targetScopeKind) {
|
|
114
|
-
result.push("--target-scope-kind", request.targetScopeKind);
|
|
115
|
-
}
|
|
116
|
-
if (request.primaryRef) {
|
|
117
|
-
result.push("--primary-ref", request.primaryRef);
|
|
118
|
-
}
|
|
119
|
-
for (const memberRef of request.memberRefs ?? []) {
|
|
120
|
-
result.push("--member-ref", memberRef);
|
|
121
|
-
}
|
|
122
|
-
if (request.bundleKind) {
|
|
123
|
-
result.push("--bundle-kind", request.bundleKind);
|
|
124
|
-
}
|
|
125
|
-
if (request.diffRange) {
|
|
126
|
-
result.push("--diff-range", request.diffRange);
|
|
127
|
-
}
|
|
128
|
-
if (request.executorRealization) {
|
|
129
|
-
result.push("--executor-realization", request.executorRealization);
|
|
130
|
-
}
|
|
131
|
-
for (const lensId of request.lensIds ?? []) {
|
|
132
|
-
result.push("--lens-id", lensId);
|
|
133
|
-
}
|
|
134
|
-
if (request.confirmValueAlignment) {
|
|
135
|
-
result.push("--confirm-value-alignment");
|
|
136
|
-
}
|
|
137
|
-
return result;
|
|
138
|
-
}
|
|
139
103
|
function basenameSessionId(sessionRoot) {
|
|
140
104
|
return path.basename(path.resolve(sessionRoot));
|
|
141
105
|
}
|
|
@@ -147,36 +111,44 @@ async function readOptionalYaml(filePath) {
|
|
|
147
111
|
async function readOptionalReviewRecord(filePath) {
|
|
148
112
|
if (!(await fileExists(filePath)))
|
|
149
113
|
return null;
|
|
150
|
-
return readValidatedReviewRecord(filePath);
|
|
151
|
-
}
|
|
152
|
-
async function readOptionalText(filePath) {
|
|
153
|
-
if (!(await fileExists(filePath)))
|
|
154
|
-
return undefined;
|
|
155
|
-
return fs.readFile(filePath, "utf8");
|
|
156
|
-
}
|
|
157
|
-
function isInsidePath(root, candidate) {
|
|
158
|
-
const relative = path.relative(path.resolve(root), path.resolve(candidate));
|
|
159
|
-
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
160
|
-
}
|
|
161
|
-
async function realpathIfExists(targetPath) {
|
|
162
114
|
try {
|
|
163
|
-
return await
|
|
115
|
+
return await readValidatedReviewRecord(filePath);
|
|
164
116
|
}
|
|
165
117
|
catch {
|
|
166
118
|
return null;
|
|
167
119
|
}
|
|
168
120
|
}
|
|
169
|
-
async function
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
121
|
+
async function readOptionalText(filePath) {
|
|
122
|
+
if (!(await fileExists(filePath)))
|
|
123
|
+
return undefined;
|
|
124
|
+
return fs.readFile(filePath, "utf8");
|
|
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;
|
|
180
152
|
}
|
|
181
153
|
async function assertSamePath(args) {
|
|
182
154
|
const expected = path.resolve(args.expected);
|
|
@@ -190,115 +162,6 @@ async function assertSamePath(args) {
|
|
|
190
162
|
}
|
|
191
163
|
throw new Error(`${args.label} mismatch: expected ${expected}, received ${actual}`);
|
|
192
164
|
}
|
|
193
|
-
async function assertRefInsideSession(args) {
|
|
194
|
-
const sessionRoot = path.resolve(args.sessionRoot);
|
|
195
|
-
const resolvedRef = path.isAbsolute(args.ref)
|
|
196
|
-
? path.resolve(args.ref)
|
|
197
|
-
: path.resolve(sessionRoot, args.ref);
|
|
198
|
-
if (!isInsidePath(sessionRoot, resolvedRef)) {
|
|
199
|
-
throw new Error(`Review continuation blocked because ${args.label} escapes the session root: ${resolvedRef}`);
|
|
200
|
-
}
|
|
201
|
-
const realSessionRoot = (await realpathIfExists(sessionRoot)) ?? sessionRoot;
|
|
202
|
-
const realRef = await realpathIfExists(resolvedRef);
|
|
203
|
-
if (realRef && !isInsidePath(realSessionRoot, realRef)) {
|
|
204
|
-
throw new Error(`Review continuation blocked because ${args.label} realpath escapes the session root: ${realRef}`);
|
|
205
|
-
}
|
|
206
|
-
if (!realRef) {
|
|
207
|
-
const realNearest = await realpathNearestExisting(path.dirname(resolvedRef));
|
|
208
|
-
if (realNearest && !isInsidePath(realSessionRoot, realNearest)) {
|
|
209
|
-
throw new Error(`Review continuation blocked because ${args.label} parent realpath escapes the session root: ${realNearest}`);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
async function validateReviewExecutionPlanSessionBoundary(args) {
|
|
214
|
-
const { executionPlan, sessionRoot } = args;
|
|
215
|
-
const plannedSessionRoot = path.isAbsolute(executionPlan.session_root)
|
|
216
|
-
? executionPlan.session_root
|
|
217
|
-
: path.resolve(sessionRoot, executionPlan.session_root);
|
|
218
|
-
await assertSamePath({
|
|
219
|
-
label: "ReviewExecutionPlan.session_root",
|
|
220
|
-
expected: sessionRoot,
|
|
221
|
-
actual: plannedSessionRoot,
|
|
222
|
-
});
|
|
223
|
-
const refs = [
|
|
224
|
-
{ label: "interpretation_artifact_path", ref: executionPlan.interpretation_artifact_path },
|
|
225
|
-
{ label: "binding_output_path", ref: executionPlan.binding_output_path },
|
|
226
|
-
{ label: "session_metadata_path", ref: executionPlan.session_metadata_path },
|
|
227
|
-
{ label: "execution_preparation_root", ref: executionPlan.execution_preparation_root },
|
|
228
|
-
{ label: "round1_root", ref: executionPlan.round1_root },
|
|
229
|
-
{ label: "prompt_packets_root", ref: executionPlan.prompt_packets_root },
|
|
230
|
-
{
|
|
231
|
-
label: "teamlead_deliberation_prompt_packet_path",
|
|
232
|
-
ref: executionPlan.teamlead_deliberation_prompt_packet_path,
|
|
233
|
-
},
|
|
234
|
-
{ label: "synthesize_prompt_packet_path", ref: executionPlan.synthesize_prompt_packet_path },
|
|
235
|
-
{ label: "actor_invocation_profiles_path", ref: executionPlan.actor_invocation_profiles_path },
|
|
236
|
-
{ label: "actor_consumer_bindings_path", ref: executionPlan.actor_consumer_bindings_path },
|
|
237
|
-
{ label: "domain_binding_path", ref: executionPlan.domain_binding_path },
|
|
238
|
-
{ label: "review_target_profile_path", ref: executionPlan.review_target_profile_path },
|
|
239
|
-
{
|
|
240
|
-
label: "review_value_alignment_criteria_path",
|
|
241
|
-
ref: executionPlan.review_value_alignment_criteria_path,
|
|
242
|
-
},
|
|
243
|
-
{ label: "review_context_manifest_path", ref: executionPlan.review_context_manifest_path },
|
|
244
|
-
{ label: "synthesis_output_path", ref: executionPlan.synthesis_output_path },
|
|
245
|
-
{ label: "finding_ledger_path", ref: executionPlan.finding_ledger_path },
|
|
246
|
-
{
|
|
247
|
-
label: "finding_relation_graph_path",
|
|
248
|
-
ref: executionPlan.finding_relation_graph_path,
|
|
249
|
-
},
|
|
250
|
-
{ label: "issue_ledger_path", ref: executionPlan.issue_ledger_path },
|
|
251
|
-
{ label: "issue_stance_matrix_path", ref: executionPlan.issue_stance_matrix_path },
|
|
252
|
-
{ label: "deliberation_plan_path", ref: executionPlan.deliberation_plan_path },
|
|
253
|
-
{ label: "problem_framing_path", ref: executionPlan.problem_framing_path },
|
|
254
|
-
{ label: "lens_completion_barrier_path", ref: executionPlan.lens_completion_barrier_path },
|
|
255
|
-
{ label: "deliberation_root_path", ref: executionPlan.deliberation_root_path },
|
|
256
|
-
{ label: "deliberation_output_path", ref: executionPlan.deliberation_output_path },
|
|
257
|
-
{ label: "execution_result_path", ref: executionPlan.execution_result_path },
|
|
258
|
-
{ label: "error_log_path", ref: executionPlan.error_log_path },
|
|
259
|
-
{ label: "final_output_path", ref: executionPlan.final_output_path },
|
|
260
|
-
{ label: "review_record_path", ref: executionPlan.review_record_path },
|
|
261
|
-
...executionPlan.lens_execution_seats.map((seat) => ({
|
|
262
|
-
label: `lens_execution_seats.${seat.lens_id}.output_path`,
|
|
263
|
-
ref: seat.output_path,
|
|
264
|
-
})),
|
|
265
|
-
...executionPlan.lens_prompt_packet_seats.flatMap((seat) => [
|
|
266
|
-
{
|
|
267
|
-
label: `lens_prompt_packet_seats.${seat.lens_id}.packet_path`,
|
|
268
|
-
ref: seat.packet_path,
|
|
269
|
-
},
|
|
270
|
-
{
|
|
271
|
-
label: `lens_prompt_packet_seats.${seat.lens_id}.output_path`,
|
|
272
|
-
ref: seat.output_path,
|
|
273
|
-
},
|
|
274
|
-
]),
|
|
275
|
-
...executionPlan.issue_artifact_prompt_packet_seats.flatMap((seat) => [
|
|
276
|
-
{
|
|
277
|
-
label: `issue_artifact_prompt_packet_seats.${seat.artifact_id}.packet_path`,
|
|
278
|
-
ref: seat.packet_path,
|
|
279
|
-
},
|
|
280
|
-
{
|
|
281
|
-
label: `issue_artifact_prompt_packet_seats.${seat.artifact_id}.output_path`,
|
|
282
|
-
ref: seat.output_path,
|
|
283
|
-
},
|
|
284
|
-
]),
|
|
285
|
-
...executionPlan.lens_deliberation_prompt_packet_seats.flatMap((seat) => [
|
|
286
|
-
{
|
|
287
|
-
label: `lens_deliberation_prompt_packet_seats.${seat.lens_id}.packet_path`,
|
|
288
|
-
ref: seat.packet_path,
|
|
289
|
-
},
|
|
290
|
-
{
|
|
291
|
-
label: `lens_deliberation_prompt_packet_seats.${seat.lens_id}.output_path`,
|
|
292
|
-
ref: seat.output_path,
|
|
293
|
-
},
|
|
294
|
-
]),
|
|
295
|
-
];
|
|
296
|
-
for (const { label, ref } of refs) {
|
|
297
|
-
if (!ref)
|
|
298
|
-
continue;
|
|
299
|
-
await assertRefInsideSession({ sessionRoot, ref, label });
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
165
|
function buildOpeningBriefPresentation(input) {
|
|
303
166
|
return {
|
|
304
167
|
prompt: [
|
|
@@ -340,6 +203,399 @@ const OPENING_PRESENTATION_SOURCE_REF_KEYS = [
|
|
|
340
203
|
"review_target_profile",
|
|
341
204
|
"review_context_manifest",
|
|
342
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
|
+
}
|
|
343
599
|
function compactSeverityCounts(summary) {
|
|
344
600
|
return [
|
|
345
601
|
`blocker=${summary.severity_counts.blocker}`,
|
|
@@ -373,140 +629,6 @@ function buildHaltPresentation(input) {
|
|
|
373
629
|
input,
|
|
374
630
|
};
|
|
375
631
|
}
|
|
376
|
-
function progressEvent(args) {
|
|
377
|
-
return {
|
|
378
|
-
presentation_contract_version: REVIEW_PRESENTATION_CONTRACT_VERSION,
|
|
379
|
-
event_kind: "mcp_progress",
|
|
380
|
-
sequence: args.sequence,
|
|
381
|
-
generated_at: isoNow(),
|
|
382
|
-
source: args.source,
|
|
383
|
-
stage: args.stage,
|
|
384
|
-
session_root: args.sessionRoot,
|
|
385
|
-
message: args.message,
|
|
386
|
-
progress: {
|
|
387
|
-
current: args.current,
|
|
388
|
-
total: args.total ?? 100,
|
|
389
|
-
...(args.exactStep !== undefined ? { exact_step: args.exactStep } : {}),
|
|
390
|
-
...(args.exactTotal !== undefined ? { exact_total: args.exactTotal } : {}),
|
|
391
|
-
...(args.label !== undefined ? { label: args.label } : {}),
|
|
392
|
-
},
|
|
393
|
-
};
|
|
394
|
-
}
|
|
395
|
-
function progressUnitsForInvokeStep(step) {
|
|
396
|
-
switch (step) {
|
|
397
|
-
case 1:
|
|
398
|
-
return 5;
|
|
399
|
-
case 2:
|
|
400
|
-
return 10;
|
|
401
|
-
case 3:
|
|
402
|
-
return 90;
|
|
403
|
-
default:
|
|
404
|
-
return 0;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
function progressUnitsForRuntimeStep(step, total) {
|
|
408
|
-
if (total <= 0)
|
|
409
|
-
return 10;
|
|
410
|
-
return Math.min(89, 10 + Math.round((step / total) * 75));
|
|
411
|
-
}
|
|
412
|
-
function parseSessionRootLine(projectRoot, line) {
|
|
413
|
-
const match = /^\s*session_root:\s+(.+?)\s*$/.exec(line);
|
|
414
|
-
if (!match?.[1])
|
|
415
|
-
return null;
|
|
416
|
-
const rawSessionRoot = match[1];
|
|
417
|
-
return path.isAbsolute(rawSessionRoot)
|
|
418
|
-
? rawSessionRoot
|
|
419
|
-
: path.resolve(projectRoot, rawSessionRoot);
|
|
420
|
-
}
|
|
421
|
-
function consoleLineProgressEvent(args) {
|
|
422
|
-
const plannedSessionRoot = parseSessionRootLine(args.projectRoot, args.line);
|
|
423
|
-
if (plannedSessionRoot) {
|
|
424
|
-
return {
|
|
425
|
-
sessionRoot: plannedSessionRoot,
|
|
426
|
-
event: progressEvent({
|
|
427
|
-
sequence: args.sequence,
|
|
428
|
-
source: "review_invoke_console",
|
|
429
|
-
stage: "session_planned",
|
|
430
|
-
sessionRoot: plannedSessionRoot,
|
|
431
|
-
message: `Review session planned at ${plannedSessionRoot}.`,
|
|
432
|
-
current: 1,
|
|
433
|
-
label: "session planned",
|
|
434
|
-
}),
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
if (args.line.trim() === "[review start]") {
|
|
438
|
-
return {
|
|
439
|
-
sessionRoot: args.sessionRoot,
|
|
440
|
-
event: progressEvent({
|
|
441
|
-
sequence: args.sequence,
|
|
442
|
-
source: "review_invoke_console",
|
|
443
|
-
stage: "start_preview",
|
|
444
|
-
sessionRoot: args.sessionRoot,
|
|
445
|
-
message: "Review start preview generated.",
|
|
446
|
-
current: 0,
|
|
447
|
-
label: "start preview",
|
|
448
|
-
}),
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
const invokeStepMatch = /^\[review invoke\] step (\d+)\/3\s+(.+?)\s*$/.exec(args.line);
|
|
452
|
-
if (invokeStepMatch?.[1] && invokeStepMatch[2]) {
|
|
453
|
-
const step = Number.parseInt(invokeStepMatch[1], 10);
|
|
454
|
-
const label = invokeStepMatch[2];
|
|
455
|
-
return {
|
|
456
|
-
sessionRoot: args.sessionRoot,
|
|
457
|
-
event: progressEvent({
|
|
458
|
-
sequence: args.sequence,
|
|
459
|
-
source: "review_invoke_console",
|
|
460
|
-
stage: "invoke_step",
|
|
461
|
-
sessionRoot: args.sessionRoot,
|
|
462
|
-
message: label,
|
|
463
|
-
current: progressUnitsForInvokeStep(step),
|
|
464
|
-
exactStep: step,
|
|
465
|
-
exactTotal: 3,
|
|
466
|
-
label,
|
|
467
|
-
}),
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
const runtimeStepMatch = /^\[review progress\]\s+(\d+)\/(\d+)\s+(.+?)\s*$/.exec(args.line);
|
|
471
|
-
if (runtimeStepMatch?.[1] && runtimeStepMatch[2] && runtimeStepMatch[3]) {
|
|
472
|
-
const step = Number.parseInt(runtimeStepMatch[1], 10);
|
|
473
|
-
const total = Number.parseInt(runtimeStepMatch[2], 10);
|
|
474
|
-
const label = runtimeStepMatch[3];
|
|
475
|
-
return {
|
|
476
|
-
sessionRoot: args.sessionRoot,
|
|
477
|
-
event: progressEvent({
|
|
478
|
-
sequence: args.sequence,
|
|
479
|
-
source: "review_invoke_console",
|
|
480
|
-
stage: "runtime_step",
|
|
481
|
-
sessionRoot: args.sessionRoot,
|
|
482
|
-
message: label,
|
|
483
|
-
current: progressUnitsForRuntimeStep(step, total),
|
|
484
|
-
exactStep: step,
|
|
485
|
-
exactTotal: total,
|
|
486
|
-
label,
|
|
487
|
-
}),
|
|
488
|
-
};
|
|
489
|
-
}
|
|
490
|
-
const completedMatch = /^\[review invoke\] completed 3\/3\s+(.+?)\s*$/.exec(args.line);
|
|
491
|
-
if (completedMatch?.[1]) {
|
|
492
|
-
const label = completedMatch[1];
|
|
493
|
-
return {
|
|
494
|
-
sessionRoot: args.sessionRoot,
|
|
495
|
-
event: progressEvent({
|
|
496
|
-
sequence: args.sequence,
|
|
497
|
-
source: "review_invoke_console",
|
|
498
|
-
stage: "completed",
|
|
499
|
-
sessionRoot: args.sessionRoot,
|
|
500
|
-
message: label,
|
|
501
|
-
current: 98,
|
|
502
|
-
exactStep: 3,
|
|
503
|
-
exactTotal: 3,
|
|
504
|
-
label,
|
|
505
|
-
}),
|
|
506
|
-
};
|
|
507
|
-
}
|
|
508
|
-
return null;
|
|
509
|
-
}
|
|
510
632
|
function generatedFromArtifactRefs(artifactRefs, keys = PRESENTATION_SOURCE_REF_KEYS) {
|
|
511
633
|
const refs = {};
|
|
512
634
|
for (const key of keys) {
|
|
@@ -622,6 +744,241 @@ function livenessSummary(args) {
|
|
|
622
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}`;
|
|
623
745
|
}
|
|
624
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
|
+
}
|
|
625
982
|
async function existingSeatIds(seats) {
|
|
626
983
|
const existing = [];
|
|
627
984
|
for (const seat of seats ?? []) {
|
|
@@ -637,7 +994,7 @@ async function completedProgressStepIds(params) {
|
|
|
637
994
|
const completed = [];
|
|
638
995
|
if (params.executionPlan)
|
|
639
996
|
completed.push("manifest_validation");
|
|
640
|
-
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);
|
|
641
998
|
const completedLensIds = await existingSeatIds(params.executionPlan?.lens_execution_seats);
|
|
642
999
|
const allPlannedLensesCompleted = plannedLensIds.length > 0 && completedLensIds.length >= plannedLensIds.length;
|
|
643
1000
|
if (allPlannedLensesCompleted || params.artifactRefs.lens_completion_barrier) {
|
|
@@ -656,8 +1013,7 @@ async function completedProgressStepIds(params) {
|
|
|
656
1013
|
if (params.artifactRefs.deliberation_plan)
|
|
657
1014
|
completed.push("deliberation_plan");
|
|
658
1015
|
const deliberationIds = await existingSeatIds(params.executionPlan?.lens_deliberation_prompt_packet_seats);
|
|
659
|
-
const plannedDeliberationIds = params.executionPlan?.lens_deliberation_prompt_packet_seats.map((seat) => seat.lens_id)
|
|
660
|
-
[];
|
|
1016
|
+
const plannedDeliberationIds = (params.executionPlan?.lens_deliberation_prompt_packet_seats ?? []).map((seat) => seat.lens_id);
|
|
661
1017
|
if ((plannedDeliberationIds.length > 0 &&
|
|
662
1018
|
deliberationIds.length >= plannedDeliberationIds.length) ||
|
|
663
1019
|
params.artifactRefs.deliberation_output) {
|
|
@@ -691,14 +1047,13 @@ async function activeUnits(params) {
|
|
|
691
1047
|
return typeof unitId === "string" && unitId.length > 0 ? [unitId] : [];
|
|
692
1048
|
}
|
|
693
1049
|
if (params.currentStepId === "lens_dispatch") {
|
|
694
|
-
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);
|
|
695
1051
|
const completed = new Set(await existingSeatIds(params.executionPlan?.lens_execution_seats));
|
|
696
1052
|
const pending = planned.filter((lensId) => !completed.has(lensId));
|
|
697
1053
|
return (pending.length > 0 ? pending : planned).map((lensId) => `lens:${lensId}`);
|
|
698
1054
|
}
|
|
699
1055
|
if (params.currentStepId === "lens_deliberation_responses") {
|
|
700
|
-
const planned = params.executionPlan?.lens_deliberation_prompt_packet_seats.map((seat) => seat.lens_id)
|
|
701
|
-
[];
|
|
1056
|
+
const planned = (params.executionPlan?.lens_deliberation_prompt_packet_seats ?? []).map((seat) => seat.lens_id);
|
|
702
1057
|
const completed = new Set(await existingSeatIds(params.executionPlan?.lens_deliberation_prompt_packet_seats));
|
|
703
1058
|
return planned
|
|
704
1059
|
.filter((lensId) => !completed.has(lensId))
|
|
@@ -825,6 +1180,24 @@ async function buildReviewStatusPresentationInput(params) {
|
|
|
825
1180
|
const completedStatus = params.status === "completed" || params.status === "completed_with_degradation";
|
|
826
1181
|
const sessionStartMs = parseTimestampMs(params.executionResult?.execution_started_at) ??
|
|
827
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
|
+
});
|
|
828
1201
|
const progress = {
|
|
829
1202
|
current_step: completedStatus
|
|
830
1203
|
? totalSteps
|
|
@@ -848,12 +1221,7 @@ async function buildReviewStatusPresentationInput(params) {
|
|
|
848
1221
|
params.artifactRefs.execution_plan ? "execution_plan" : null,
|
|
849
1222
|
].filter((step) => step !== null)
|
|
850
1223
|
: completedSteps,
|
|
851
|
-
active_units:
|
|
852
|
-
status: params.status,
|
|
853
|
-
currentStepId,
|
|
854
|
-
executionPlan: params.executionPlan,
|
|
855
|
-
executionResult: params.executionResult,
|
|
856
|
-
}),
|
|
1224
|
+
active_units: progressActiveUnits,
|
|
857
1225
|
pending_units: completedStatus
|
|
858
1226
|
? []
|
|
859
1227
|
: params.status === "prepared"
|
|
@@ -872,6 +1240,7 @@ async function buildReviewStatusPresentationInput(params) {
|
|
|
872
1240
|
: currentStepId
|
|
873
1241
|
? `next ${stepById(currentStepId).label} artifact or timeout`
|
|
874
1242
|
: null,
|
|
1243
|
+
unit_progress: unitProgress,
|
|
875
1244
|
};
|
|
876
1245
|
const completedLensIds = await existingSeatIds(params.executionPlan?.lens_execution_seats);
|
|
877
1246
|
const halt = haltPresentation({
|
|
@@ -904,6 +1273,8 @@ async function buildReviewStatusPresentationInput(params) {
|
|
|
904
1273
|
secondsSinceLastArtifact,
|
|
905
1274
|
}),
|
|
906
1275
|
};
|
|
1276
|
+
const runControl = await buildRunControl(params.sessionRoot, params.status);
|
|
1277
|
+
const targetMaterialSupport = await readTargetMaterialSupport(params.sessionRoot, params.executionPlan);
|
|
907
1278
|
return {
|
|
908
1279
|
presentation_contract_version: REVIEW_PRESENTATION_CONTRACT_VERSION,
|
|
909
1280
|
presentation_kind: "progress",
|
|
@@ -922,6 +1293,9 @@ async function buildReviewStatusPresentationInput(params) {
|
|
|
922
1293
|
}),
|
|
923
1294
|
result_classification_summary: resultClassificationSummary,
|
|
924
1295
|
halt,
|
|
1296
|
+
run_control: runControl,
|
|
1297
|
+
target_material_support: targetMaterialSupport,
|
|
1298
|
+
environment_warnings: await readEnvironmentWarnings(params.sessionRoot),
|
|
925
1299
|
};
|
|
926
1300
|
}
|
|
927
1301
|
async function buildPreparedOpeningBriefInput(sessionRoot, executionPlan) {
|
|
@@ -986,74 +1360,118 @@ async function buildPreparedOpeningBriefInput(sessionRoot, executionPlan) {
|
|
|
986
1360
|
},
|
|
987
1361
|
};
|
|
988
1362
|
}
|
|
989
|
-
function
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
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
|
+
};
|
|
1008
1405
|
}
|
|
1009
|
-
async function
|
|
1010
|
-
const
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
degradation_summary: path.join(sessionRoot, "degradation-summary.yaml"),
|
|
1033
|
-
error_log: path.join(sessionRoot, "error-log.md"),
|
|
1034
|
-
final_output: path.join(sessionRoot, "final-output.md"),
|
|
1035
|
-
review_record: path.join(sessionRoot, "review-record.yaml"),
|
|
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),
|
|
1036
1429
|
};
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
if (await fileExists(filePath))
|
|
1040
|
-
entries.push([key, filePath]);
|
|
1430
|
+
if (executionPlan) {
|
|
1431
|
+
llmPresentation.openingBrief = buildOpeningBriefPresentation(await buildPreparedOpeningBriefInput(sessionRoot, executionPlan));
|
|
1041
1432
|
}
|
|
1042
|
-
return
|
|
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);
|
|
1043
1456
|
}
|
|
1044
1457
|
async function buildPipelineExecutionLedgerIfPossible(args) {
|
|
1045
1458
|
if (!args.executionPlan)
|
|
1046
1459
|
return undefined;
|
|
1047
1460
|
const reviewRunManifest = await readOptionalYaml(path.join(args.sessionRoot, "review-run-manifest.yaml"));
|
|
1048
1461
|
const lensCompletionBarrier = await readOptionalYaml(path.join(args.sessionRoot, "lens-completion-barrier.yaml"));
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
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
|
+
}
|
|
1057
1475
|
}
|
|
1058
1476
|
function workerExecutorToRealization(workerExecutor) {
|
|
1059
1477
|
if (workerExecutor === "mock")
|
|
@@ -1319,7 +1737,7 @@ async function listDomainDirs(root) {
|
|
|
1319
1737
|
try {
|
|
1320
1738
|
const entries = await fs.readdir(root, { withFileTypes: true });
|
|
1321
1739
|
return entries
|
|
1322
|
-
.filter((entry) => entry.isDirectory())
|
|
1740
|
+
.filter((entry) => entry.isDirectory() && !isDeprecatedDomainAlias(entry.name))
|
|
1323
1741
|
.map((entry) => entry.name)
|
|
1324
1742
|
.sort();
|
|
1325
1743
|
}
|
|
@@ -1331,8 +1749,8 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1331
1749
|
const ontoHome = resolveRequiredOntoHome(options.ontoHome);
|
|
1332
1750
|
const api = {
|
|
1333
1751
|
async prepareReview(request) {
|
|
1334
|
-
|
|
1335
|
-
const
|
|
1752
|
+
await validateRequestedDomainForDispatch(request, ontoHome);
|
|
1753
|
+
const result = await prepareReviewInvocationRequest(request, { ontoHome });
|
|
1336
1754
|
const sessionRoot = path.resolve(result.session_root);
|
|
1337
1755
|
const executionPlan = await readYamlDocument(path.join(sessionRoot, "execution-plan.yaml"));
|
|
1338
1756
|
const openingBriefInput = await buildPreparedOpeningBriefInput(sessionRoot, executionPlan);
|
|
@@ -1347,9 +1765,17 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1347
1765
|
};
|
|
1348
1766
|
},
|
|
1349
1767
|
async runReview(request) {
|
|
1350
|
-
|
|
1768
|
+
await validateRequestedDomainForDispatch(request, ontoHome);
|
|
1769
|
+
const requestHash = requestHashForReviewInput(request);
|
|
1770
|
+
const invocationId = `initial-${continuationAttemptId()}`;
|
|
1351
1771
|
let progressSequence = 0;
|
|
1352
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
|
+
});
|
|
1353
1779
|
const emitProgress = (event) => {
|
|
1354
1780
|
const observer = request.progressObserver;
|
|
1355
1781
|
if (!observer)
|
|
@@ -1368,141 +1794,223 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1368
1794
|
// Progress notifications are transport-only and must not affect review execution.
|
|
1369
1795
|
}
|
|
1370
1796
|
};
|
|
1371
|
-
const
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
},
|
|
1393
|
-
}
|
|
1394
|
-
: undefined;
|
|
1395
|
-
const captured = await withCapturedConsole(async () => {
|
|
1396
|
-
const exitCode = await runReviewInvokeCli(argv);
|
|
1397
|
-
if (exitCode !== 0) {
|
|
1398
|
-
throw new Error(`review invocation failed with exit code ${exitCode}`);
|
|
1399
|
-
}
|
|
1400
|
-
return exitCode;
|
|
1401
|
-
}, captureObserver);
|
|
1402
|
-
const parsed = parseReviewInvokeOutput(captured.stdout);
|
|
1403
|
-
if (!isReviewInvokeShape(parsed)) {
|
|
1404
|
-
throw new Error("review invocation returned an unexpected result shape.");
|
|
1405
|
-
}
|
|
1406
|
-
const result = parsed.review_result;
|
|
1407
|
-
const status = result.record_status ?? "halted_partial";
|
|
1408
|
-
const startPreview = {
|
|
1409
|
-
entrypointPlan: parsed.entrypoint_plan,
|
|
1410
|
-
routeSummary: parsed.route_summary,
|
|
1411
|
-
...(parsed.bounded_invoke_steps !== undefined
|
|
1412
|
-
? { boundedInvokeSteps: parsed.bounded_invoke_steps }
|
|
1413
|
-
: {}),
|
|
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
|
+
});
|
|
1414
1818
|
};
|
|
1415
|
-
const
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
start_preview: startPreview,
|
|
1446
|
-
};
|
|
1447
|
-
const finalResultInput = {
|
|
1448
|
-
presentation_contract_version: REVIEW_PRESENTATION_CONTRACT_VERSION,
|
|
1449
|
-
presentation_kind: "final_result",
|
|
1450
|
-
session_id: basenameSessionId(resolvedResultSessionRoot),
|
|
1451
|
-
session_root: resolvedResultSessionRoot,
|
|
1452
|
-
status,
|
|
1453
|
-
generated_from_artifact_refs: generatedFromArtifactRefs(artifactRefs),
|
|
1454
|
-
result_overview: parsed.result_overview ?? null,
|
|
1455
|
-
result_classification_summary: resultClassificationSummary,
|
|
1456
|
-
review_result: result,
|
|
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,
|
|
1847
|
+
},
|
|
1848
|
+
});
|
|
1457
1849
|
};
|
|
1458
|
-
const
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
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,
|
|
1466
1971
|
}),
|
|
1972
|
+
runControl: progressInput.run_control,
|
|
1973
|
+
targetMaterialSupport: progressInput.target_material_support,
|
|
1974
|
+
environmentWarnings: progressInput.environment_warnings,
|
|
1975
|
+
};
|
|
1976
|
+
}
|
|
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
|
+
});
|
|
1467
1984
|
}
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
};
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
: {}),
|
|
1498
|
-
artifactRefs,
|
|
1499
|
-
...(pipelineExecutionLedger ? { pipelineExecutionLedger } : {}),
|
|
1500
|
-
resultClassificationSummary,
|
|
1501
|
-
...failures,
|
|
1502
|
-
routeVisibility: await buildReviewRouteVisibilityFromSession(resolvedResultSessionRoot),
|
|
1503
|
-
startPreview,
|
|
1504
|
-
llmPresentation,
|
|
1505
|
-
};
|
|
1985
|
+
throw error;
|
|
1986
|
+
}
|
|
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;
|
|
2012
|
+
}
|
|
2013
|
+
return fullRun;
|
|
1506
2014
|
},
|
|
1507
2015
|
async continueReview(request) {
|
|
1508
2016
|
const resolvedSessionRoot = path.resolve(request.sessionRoot);
|
|
@@ -1530,10 +2038,36 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1530
2038
|
expected: sessionMetadataPath,
|
|
1531
2039
|
actual: executionPlan.session_metadata_path,
|
|
1532
2040
|
});
|
|
1533
|
-
await
|
|
2041
|
+
await assertReviewExecutionPlanSessionBoundary({
|
|
1534
2042
|
sessionRoot: resolvedSessionRoot,
|
|
1535
2043
|
executionPlan,
|
|
1536
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
|
+
}
|
|
1537
2071
|
const executionResult = await readOptionalYaml(path.join(resolvedSessionRoot, "execution-result.yaml"));
|
|
1538
2072
|
const pipelineExecutionLedger = await buildPipelineExecutionLedgerIfPossible({
|
|
1539
2073
|
sessionRoot: resolvedSessionRoot,
|
|
@@ -1618,6 +2152,15 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1618
2152
|
});
|
|
1619
2153
|
};
|
|
1620
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
|
+
});
|
|
1621
2164
|
let promptExecutionResult;
|
|
1622
2165
|
try {
|
|
1623
2166
|
const executorConfig = buildExecutorConfigFromRealization(executorRealization, ontoHome);
|
|
@@ -1645,6 +2188,12 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1645
2188
|
await writeAttemptManifest(promptExecutionResult.synthesis_executed
|
|
1646
2189
|
? "completed"
|
|
1647
2190
|
: "halted_partial", { prompt_execution_result: promptExecutionResult });
|
|
2191
|
+
await updateActiveAttemptTerminal({
|
|
2192
|
+
sessionRoot: resolvedSessionRoot,
|
|
2193
|
+
status: promptExecutionResult.synthesis_executed
|
|
2194
|
+
? "completed"
|
|
2195
|
+
: "halted_partial",
|
|
2196
|
+
});
|
|
1648
2197
|
}
|
|
1649
2198
|
catch (error) {
|
|
1650
2199
|
const restoredArtifactBackups = await restoreSupersededArtifacts(supersededArtifactBackups);
|
|
@@ -1656,6 +2205,11 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1656
2205
|
: {}),
|
|
1657
2206
|
restored_artifact_backups: restoredArtifactBackups,
|
|
1658
2207
|
});
|
|
2208
|
+
await updateActiveAttemptTerminal({
|
|
2209
|
+
sessionRoot: resolvedSessionRoot,
|
|
2210
|
+
status: "failed",
|
|
2211
|
+
errorMessage,
|
|
2212
|
+
});
|
|
1659
2213
|
throw new ReviewContinuationError({
|
|
1660
2214
|
message: `Review continuation failed: ${errorMessage}`,
|
|
1661
2215
|
originalError: error,
|
|
@@ -1681,6 +2235,7 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1681
2235
|
return {
|
|
1682
2236
|
sessionId: postStatus.sessionId,
|
|
1683
2237
|
sessionRoot: resolvedSessionRoot,
|
|
2238
|
+
decision: "executed",
|
|
1684
2239
|
status: postStatus.status,
|
|
1685
2240
|
continuationPlan,
|
|
1686
2241
|
continuationAttempt: {
|
|
@@ -1705,6 +2260,77 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1705
2260
|
: {}),
|
|
1706
2261
|
};
|
|
1707
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,
|
|
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);
|
|
2322
|
+
return {
|
|
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 } : {}),
|
|
2332
|
+
};
|
|
2333
|
+
},
|
|
1708
2334
|
async getReviewStatus(sessionRoot) {
|
|
1709
2335
|
const resolvedSessionRoot = path.resolve(sessionRoot);
|
|
1710
2336
|
const artifactRefs = await collectArtifactRefs(resolvedSessionRoot);
|
|
@@ -1721,15 +2347,22 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1721
2347
|
? buildReviewContinuationPlan({ ledger: pipelineExecutionLedger })
|
|
1722
2348
|
: undefined;
|
|
1723
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;
|
|
1724
2352
|
const status = reviewRecord
|
|
1725
2353
|
? reviewRecord.record_status
|
|
1726
2354
|
: executionResult?.execution_status === "halted_partial"
|
|
1727
2355
|
? "halted_partial"
|
|
1728
|
-
:
|
|
1729
|
-
?
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
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";
|
|
1733
2366
|
const progressInput = await buildReviewStatusPresentationInput({
|
|
1734
2367
|
sessionRoot: resolvedSessionRoot,
|
|
1735
2368
|
status,
|
|
@@ -1761,6 +2394,10 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1761
2394
|
...failures,
|
|
1762
2395
|
routeVisibility: await buildReviewRouteVisibilityFromSession(resolvedSessionRoot),
|
|
1763
2396
|
llmPresentation,
|
|
2397
|
+
runControl: progressInput.run_control,
|
|
2398
|
+
targetMaterialSupport: progressInput.target_material_support,
|
|
2399
|
+
environmentWarnings: progressInput.environment_warnings,
|
|
2400
|
+
unitProgress: progressInput.progress.unit_progress,
|
|
1764
2401
|
};
|
|
1765
2402
|
}
|
|
1766
2403
|
if (executionResult?.execution_status === "halted_partial") {
|
|
@@ -1774,6 +2411,10 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1774
2411
|
...failures,
|
|
1775
2412
|
routeVisibility: await buildReviewRouteVisibilityFromSession(resolvedSessionRoot),
|
|
1776
2413
|
llmPresentation,
|
|
2414
|
+
runControl: progressInput.run_control,
|
|
2415
|
+
targetMaterialSupport: progressInput.target_material_support,
|
|
2416
|
+
environmentWarnings: progressInput.environment_warnings,
|
|
2417
|
+
unitProgress: progressInput.progress.unit_progress,
|
|
1777
2418
|
};
|
|
1778
2419
|
}
|
|
1779
2420
|
if (executionPlan) {
|
|
@@ -1787,6 +2428,10 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1787
2428
|
...failures,
|
|
1788
2429
|
routeVisibility: await buildReviewRouteVisibilityFromSession(resolvedSessionRoot),
|
|
1789
2430
|
llmPresentation,
|
|
2431
|
+
runControl: progressInput.run_control,
|
|
2432
|
+
targetMaterialSupport: progressInput.target_material_support,
|
|
2433
|
+
environmentWarnings: progressInput.environment_warnings,
|
|
2434
|
+
unitProgress: progressInput.progress.unit_progress,
|
|
1790
2435
|
};
|
|
1791
2436
|
}
|
|
1792
2437
|
return {
|
|
@@ -1799,16 +2444,28 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1799
2444
|
...failures,
|
|
1800
2445
|
routeVisibility: await buildReviewRouteVisibilityFromSession(resolvedSessionRoot),
|
|
1801
2446
|
llmPresentation,
|
|
2447
|
+
runControl: progressInput.run_control,
|
|
2448
|
+
targetMaterialSupport: progressInput.target_material_support,
|
|
2449
|
+
environmentWarnings: progressInput.environment_warnings,
|
|
2450
|
+
unitProgress: progressInput.progress.unit_progress,
|
|
1802
2451
|
};
|
|
1803
2452
|
},
|
|
1804
|
-
async getReviewResult(sessionRoot) {
|
|
2453
|
+
async getReviewResult(sessionRoot, options = {}) {
|
|
1805
2454
|
const resolvedSessionRoot = path.resolve(sessionRoot);
|
|
2455
|
+
const projectionLevel = options.projectionLevel ?? "full";
|
|
1806
2456
|
const artifactRefs = await collectArtifactRefs(resolvedSessionRoot);
|
|
1807
2457
|
const { failureRefs } = await collectStructuredFailures(resolvedSessionRoot);
|
|
1808
2458
|
const reviewRecordPath = path.join(resolvedSessionRoot, "review-record.yaml");
|
|
1809
2459
|
const reviewRecord = await readValidatedReviewRecord(reviewRecordPath);
|
|
1810
|
-
const
|
|
1811
|
-
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);
|
|
1812
2469
|
const executionPlan = await readOptionalYaml(path.join(resolvedSessionRoot, "execution-plan.yaml"));
|
|
1813
2470
|
const executionResult = await readOptionalYaml(path.join(resolvedSessionRoot, "execution-result.yaml"));
|
|
1814
2471
|
const pipelineExecutionLedger = await buildPipelineExecutionLedgerIfPossible({
|
|
@@ -1835,12 +2492,32 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1835
2492
|
status,
|
|
1836
2493
|
generated_from_artifact_refs: generatedFromArtifactRefs(artifactRefs),
|
|
1837
2494
|
result_classification_summary: resultClassificationSummary,
|
|
1838
|
-
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
|
+
},
|
|
1839
2504
|
};
|
|
2505
|
+
const targetMaterialSupport = await readTargetMaterialSupport(resolvedSessionRoot, executionPlan);
|
|
2506
|
+
const environmentWarnings = await readEnvironmentWarnings(resolvedSessionRoot);
|
|
1840
2507
|
return {
|
|
1841
2508
|
sessionId: reviewRecord.session_id,
|
|
1842
2509
|
sessionRoot: resolvedSessionRoot,
|
|
1843
|
-
|
|
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 } : {}),
|
|
1844
2521
|
finalOutputPath,
|
|
1845
2522
|
reviewRunManifestPath: path.join(resolvedSessionRoot, "review-run-manifest.yaml"),
|
|
1846
2523
|
artifactRefs,
|
|
@@ -1860,9 +2537,87 @@ export function createOntoReviewCoreApi(options = {}) {
|
|
|
1860
2537
|
: {}),
|
|
1861
2538
|
finalResult: buildFinalResultPresentation(finalResultInput),
|
|
1862
2539
|
},
|
|
2540
|
+
targetMaterialSupport,
|
|
2541
|
+
environmentWarnings,
|
|
1863
2542
|
...(finalOutputText !== undefined ? { finalOutputText } : {}),
|
|
1864
2543
|
};
|
|
1865
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
|
+
},
|
|
1866
2621
|
async listLenses() {
|
|
1867
2622
|
const registry = loadCoreLensRegistry();
|
|
1868
2623
|
return {
|