voratiq 0.1.0-beta.21 → 0.1.0-beta.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -22
- package/dist/agents/launch/chat.d.ts +3 -1
- package/dist/agents/launch/chat.js +2 -0
- package/dist/bin.js +28 -7
- package/dist/cli/auto.js +1 -0
- package/dist/cli/contract.d.ts +26 -17
- package/dist/cli/contract.js +3 -1
- package/dist/cli/doctor.d.ts +12 -0
- package/dist/cli/doctor.js +115 -0
- package/dist/cli/list.js +4 -1
- package/dist/cli/operator-envelope.d.ts +19 -6
- package/dist/cli/operator-envelope.js +61 -1
- package/dist/cli/run.js +2 -0
- package/dist/cli/verify.d.ts +1 -1
- package/dist/cli/verify.js +48 -9
- package/dist/commands/auto/command.d.ts +1 -0
- package/dist/commands/auto/command.js +22 -12
- package/dist/commands/auto/errors.js +1 -1
- package/dist/commands/doctor/agents.d.ts +5 -0
- package/dist/commands/{init → doctor}/agents.js +37 -19
- package/dist/commands/doctor/command.d.ts +22 -0
- package/dist/commands/doctor/command.js +99 -0
- package/dist/commands/doctor/environment.d.ts +2 -0
- package/dist/commands/{init → doctor}/environment.js +38 -6
- package/dist/commands/{init/types.d.ts → doctor/fix-types.d.ts} +30 -9
- package/dist/commands/doctor/fix.d.ts +2 -0
- package/dist/commands/{init/command.js → doctor/fix.js} +106 -10
- package/dist/commands/doctor/reconcile.d.ts +2 -0
- package/dist/commands/doctor/reconcile.js +101 -0
- package/dist/commands/interactive/lifecycle.d.ts +2 -0
- package/dist/commands/interactive/lifecycle.js +8 -0
- package/dist/commands/list/command.d.ts +1 -0
- package/dist/commands/list/command.js +211 -352
- package/dist/commands/list/normalization.d.ts +56 -0
- package/dist/commands/list/normalization.js +317 -0
- package/dist/commands/message/command.d.ts +2 -1
- package/dist/commands/message/command.js +35 -14
- package/dist/commands/message/errors.d.ts +12 -3
- package/dist/commands/message/errors.js +19 -3
- package/dist/commands/reduce/command.js +16 -17
- package/dist/commands/reduce/errors.d.ts +2 -2
- package/dist/commands/reduce/errors.js +3 -3
- package/dist/commands/reduce/targets.js +11 -2
- package/dist/commands/root-launcher/command.js +12 -6
- package/dist/commands/run/command.d.ts +1 -0
- package/dist/commands/run/command.js +4 -1
- package/dist/commands/run/record-init.d.ts +2 -0
- package/dist/commands/run/record-init.js +2 -1
- package/dist/commands/run/spec-provenance.d.ts +37 -0
- package/dist/commands/run/spec-provenance.js +384 -0
- package/dist/commands/run/validation.d.ts +4 -0
- package/dist/commands/run/validation.js +25 -62
- package/dist/commands/spec/command.js +19 -6
- package/dist/commands/spec/errors.d.ts +5 -0
- package/dist/commands/spec/errors.js +9 -0
- package/dist/commands/verify/agents.d.ts +4 -2
- package/dist/commands/verify/agents.js +4 -11
- package/dist/commands/verify/command.js +15 -5
- package/dist/commands/verify/errors.d.ts +12 -0
- package/dist/commands/verify/errors.js +22 -0
- package/dist/commands/verify/targets.js +108 -12
- package/dist/competition/shared/preflight.d.ts +1 -1
- package/dist/competition/shared/preflight.js +15 -2
- package/dist/contracts/list.d.ts +129 -149
- package/dist/contracts/list.js +47 -99
- package/dist/domain/interactive/persistence/adapter.d.ts +23 -0
- package/dist/domain/interactive/persistence/adapter.js +42 -0
- package/dist/domain/message/model/types.d.ts +32 -0
- package/dist/domain/message/model/types.js +25 -0
- package/dist/domain/reduce/competition/adapter.js +21 -7
- package/dist/domain/reduce/competition/finalize.d.ts +7 -0
- package/dist/domain/reduce/competition/finalize.js +19 -0
- package/dist/domain/reduce/model/types.d.ts +3 -3
- package/dist/domain/run/competition/agents/artifacts.js +4 -2
- package/dist/domain/run/competition/errors.d.ts +1 -1
- package/dist/domain/run/competition/errors.js +4 -7
- package/dist/domain/run/model/types.d.ts +384 -0
- package/dist/domain/run/model/types.js +87 -0
- package/dist/domain/spec/competition/adapter.d.ts +1 -0
- package/dist/domain/spec/competition/adapter.js +6 -1
- package/dist/domain/spec/model/types.d.ts +3 -0
- package/dist/domain/spec/model/types.js +5 -0
- package/dist/domain/verify/competition/finalize.d.ts +9 -0
- package/dist/domain/verify/competition/finalize.js +22 -3
- package/dist/domain/verify/model/types.d.ts +2 -2
- package/dist/interactive/providers/mcp.d.ts +1 -0
- package/dist/interactive/providers/mcp.js +45 -7
- package/dist/interactive/substrate.js +20 -3
- package/dist/mcp/server.js +26 -9
- package/dist/policy/verification.js +18 -1
- package/dist/preflight/agents.d.ts +24 -0
- package/dist/preflight/agents.js +71 -0
- package/dist/preflight/environment.d.ts +6 -0
- package/dist/preflight/environment.js +17 -0
- package/dist/preflight/formatting.d.ts +5 -0
- package/dist/preflight/formatting.js +20 -0
- package/dist/preflight/index.d.ts +2 -0
- package/dist/preflight/index.js +5 -9
- package/dist/preflight/operator.d.ts +32 -0
- package/dist/preflight/operator.js +40 -0
- package/dist/preflight/settings.d.ts +2 -0
- package/dist/preflight/settings.js +17 -0
- package/dist/render/transcripts/interactive.d.ts +16 -0
- package/dist/render/transcripts/interactive.js +42 -0
- package/dist/render/transcripts/list.d.ts +41 -0
- package/dist/render/transcripts/list.js +152 -3
- package/dist/render/transcripts/message.d.ts +2 -1
- package/dist/render/transcripts/message.js +19 -20
- package/dist/render/transcripts/reduce.d.ts +1 -0
- package/dist/render/transcripts/reduce.js +21 -21
- package/dist/render/transcripts/root-launcher.js +2 -12
- package/dist/render/transcripts/run.d.ts +3 -0
- package/dist/render/transcripts/run.js +30 -4
- package/dist/render/transcripts/spec.js +5 -8
- package/dist/render/transcripts/verify.d.ts +5 -3
- package/dist/render/transcripts/verify.js +44 -31
- package/dist/render/utils/duration.d.ts +5 -0
- package/dist/render/utils/duration.js +6 -0
- package/dist/render/utils/runs.d.ts +1 -0
- package/dist/render/utils/runs.js +1 -0
- package/dist/render/utils/transcript-shell.d.ts +2 -1
- package/dist/render/utils/transcript-shell.js +19 -6
- package/dist/utils/errors.d.ts +2 -1
- package/dist/utils/errors.js +3 -1
- package/dist/utils/git.d.ts +1 -1
- package/dist/utils/git.js +25 -2
- package/dist/utils/list-target.d.ts +4 -0
- package/dist/utils/list-target.js +35 -0
- package/dist/utils/terminal.d.ts +1 -0
- package/dist/utils/terminal.js +11 -0
- package/dist/workspace/chat/artifacts.d.ts +7 -0
- package/dist/workspace/chat/artifacts.js +94 -3
- package/dist/workspace/errors.js +2 -2
- package/dist/workspace/managed-state.d.ts +32 -0
- package/dist/workspace/managed-state.js +103 -0
- package/dist/workspace/setup.js +66 -2
- package/dist/workspace/shim.d.ts +1 -0
- package/dist/workspace/shim.js +3 -3
- package/dist/workspace/structure.d.ts +1 -0
- package/dist/workspace/structure.js +1 -0
- package/package.json +2 -2
- package/dist/cli/init.d.ts +0 -15
- package/dist/cli/init.js +0 -70
- package/dist/commands/init/agents.d.ts +0 -4
- package/dist/commands/init/command.d.ts +0 -2
- package/dist/commands/init/environment.d.ts +0 -2
- package/dist/render/transcripts/init.d.ts +0 -7
- package/dist/render/transcripts/init.js +0 -83
- /package/dist/commands/{init/types.js → doctor/fix-types.js} +0 -0
|
@@ -131,6 +131,8 @@ export function buildSpecOperatorEnvelope(options) {
|
|
|
131
131
|
});
|
|
132
132
|
}
|
|
133
133
|
export function buildRunOperatorEnvelope(options) {
|
|
134
|
+
const target = buildRunEnvelopeTarget(options.specTarget);
|
|
135
|
+
const alerts = buildRunEnvelopeAlerts(options.specTarget);
|
|
134
136
|
return buildOperatorEnvelope({
|
|
135
137
|
operator: "run",
|
|
136
138
|
status: normalizeTerminalStatus(options.status),
|
|
@@ -149,10 +151,12 @@ export function buildRunOperatorEnvelope(options) {
|
|
|
149
151
|
path: options.specPath,
|
|
150
152
|
},
|
|
151
153
|
],
|
|
154
|
+
target,
|
|
155
|
+
alerts,
|
|
152
156
|
});
|
|
153
157
|
}
|
|
154
158
|
export function buildVerifyOperatorEnvelope(options) {
|
|
155
|
-
const status = options.selection?.state === "unresolved"
|
|
159
|
+
const status = options.status === "unresolved" || options.selection?.state === "unresolved"
|
|
156
160
|
? "unresolved"
|
|
157
161
|
: normalizeTerminalStatus(options.status);
|
|
158
162
|
const ids = {
|
|
@@ -232,6 +236,9 @@ export function buildReduceOperatorEnvelope(options) {
|
|
|
232
236
|
if (options.target.type === "reduce") {
|
|
233
237
|
ids.reductionId = options.target.id;
|
|
234
238
|
}
|
|
239
|
+
if (options.target.type === "message") {
|
|
240
|
+
ids.messageId = options.target.id;
|
|
241
|
+
}
|
|
235
242
|
return buildOperatorEnvelope({
|
|
236
243
|
operator: "reduce",
|
|
237
244
|
status: normalizeTerminalStatus(options.status),
|
|
@@ -331,6 +338,59 @@ export function buildPruneOperatorEnvelope(options) {
|
|
|
331
338
|
: [],
|
|
332
339
|
});
|
|
333
340
|
}
|
|
341
|
+
function buildRunEnvelopeTarget(specTarget) {
|
|
342
|
+
if (specTarget?.kind === "spec") {
|
|
343
|
+
return {
|
|
344
|
+
kind: "spec",
|
|
345
|
+
sessionId: specTarget.sessionId,
|
|
346
|
+
...(specTarget.provenance
|
|
347
|
+
? buildRunEnvelopeProvenanceFields(specTarget.provenance)
|
|
348
|
+
: {}),
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
if (specTarget?.kind === "file" &&
|
|
352
|
+
specTarget.provenance?.source?.sessionId &&
|
|
353
|
+
specTarget.provenance.lineage === "invalid") {
|
|
354
|
+
return {
|
|
355
|
+
kind: "spec",
|
|
356
|
+
sessionId: specTarget.provenance.source.sessionId,
|
|
357
|
+
...buildRunEnvelopeProvenanceFields(specTarget.provenance),
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
return undefined;
|
|
361
|
+
}
|
|
362
|
+
function buildRunEnvelopeAlerts(specTarget) {
|
|
363
|
+
if (specTarget?.provenance?.lineage !== "invalid") {
|
|
364
|
+
return undefined;
|
|
365
|
+
}
|
|
366
|
+
return [
|
|
367
|
+
{
|
|
368
|
+
level: "warn",
|
|
369
|
+
message: specTarget.provenance.issueCode === "stale_source"
|
|
370
|
+
? "Run spec ancestry metadata referenced a stale or mismatched spec artifact."
|
|
371
|
+
: "Run spec ancestry metadata was malformed and was ignored.",
|
|
372
|
+
},
|
|
373
|
+
];
|
|
374
|
+
}
|
|
375
|
+
function buildRunEnvelopeProvenanceFields(provenance) {
|
|
376
|
+
return {
|
|
377
|
+
lineage: provenance.lineage,
|
|
378
|
+
...(provenance.lineage === "invalid"
|
|
379
|
+
? { issueCode: provenance.issueCode }
|
|
380
|
+
: {}),
|
|
381
|
+
...("currentContentHash" in provenance &&
|
|
382
|
+
typeof provenance.currentContentHash === "string"
|
|
383
|
+
? { currentContentHash: provenance.currentContentHash }
|
|
384
|
+
: {}),
|
|
385
|
+
...(provenance.source
|
|
386
|
+
? {
|
|
387
|
+
source: {
|
|
388
|
+
...provenance.source,
|
|
389
|
+
},
|
|
390
|
+
}
|
|
391
|
+
: {}),
|
|
392
|
+
};
|
|
393
|
+
}
|
|
334
394
|
export function writeOperatorResultEnvelope(envelope, exitCode) {
|
|
335
395
|
process.stdout.write(`${JSON.stringify(envelope)}\n`);
|
|
336
396
|
if (typeof exitCode === "number") {
|
package/dist/cli/run.js
CHANGED
|
@@ -61,6 +61,7 @@ export async function runRunCommand(options) {
|
|
|
61
61
|
const report = await executeRunCommand({
|
|
62
62
|
root,
|
|
63
63
|
runsFilePath: workspacePaths.runsFile,
|
|
64
|
+
specsFilePath: workspacePaths.specsFile,
|
|
64
65
|
specAbsolutePath: absolutePath,
|
|
65
66
|
specDisplayPath: displayPath,
|
|
66
67
|
agentIds,
|
|
@@ -131,6 +132,7 @@ export function createRunCommand() {
|
|
|
131
132
|
writeOperatorResultEnvelope(buildRunOperatorEnvelope({
|
|
132
133
|
runId: result.report.runId,
|
|
133
134
|
specPath: result.report.spec.path,
|
|
135
|
+
specTarget: result.report.spec.target,
|
|
134
136
|
status: result.report.status,
|
|
135
137
|
}), result.exitCode);
|
|
136
138
|
return;
|
package/dist/cli/verify.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export interface VerifyCommandOptions {
|
|
|
22
22
|
}
|
|
23
23
|
export interface VerifyCommandResult {
|
|
24
24
|
verificationId: string;
|
|
25
|
-
status: VerificationRecord["status"];
|
|
25
|
+
status: VerificationRecord["status"] | "unresolved";
|
|
26
26
|
target: VerificationRecord["target"];
|
|
27
27
|
body: string;
|
|
28
28
|
exitCode?: number;
|
package/dist/cli/verify.js
CHANGED
|
@@ -23,6 +23,22 @@ import { resolveWorkspacePath, VORATIQ_MESSAGE_FILE, VORATIQ_REDUCTION_FILE, VOR
|
|
|
23
23
|
import { parseVerifyExecutionCommandOptions } from "./contract.js";
|
|
24
24
|
import { buildVerifyOperatorEnvelope, createSilentCliWriter, writeOperatorResultEnvelope, } from "./operator-envelope.js";
|
|
25
25
|
import { writeCommandOutput } from "./output.js";
|
|
26
|
+
function resolveVerifyManualActionMessage(options) {
|
|
27
|
+
const { targetKind, status, selection, selectedSpecPath } = options;
|
|
28
|
+
if (status !== "succeeded") {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
if (targetKind === "spec" && !selectedSpecPath) {
|
|
32
|
+
return "Verification did not select a spec path; manual review required.";
|
|
33
|
+
}
|
|
34
|
+
if (selection?.decision.state !== "unresolved") {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
if (targetKind === "run") {
|
|
38
|
+
return "Verification did not produce a resolvable candidate; manual selection required.";
|
|
39
|
+
}
|
|
40
|
+
return "Verification did not produce a resolvable result; manual review required.";
|
|
41
|
+
}
|
|
26
42
|
export async function runVerifyCommand(options) {
|
|
27
43
|
const { target, agentIds, agentOverrideFlag, profile, maxParallel, extraContext, json = false, suppressHint, stdout, stderr, writeOutput, } = options;
|
|
28
44
|
const effectiveWriteOutput = json
|
|
@@ -97,10 +113,30 @@ export async function runVerifyCommand(options) {
|
|
|
97
113
|
return undefined;
|
|
98
114
|
});
|
|
99
115
|
const selectionWarnings = (selection?.warnings ?? []).map((warning) => `Warning: ${warning}`);
|
|
116
|
+
const selectedSpecPath = execution.record.target.kind === "spec" &&
|
|
117
|
+
typeof execution.record.target.specPath === "string"
|
|
118
|
+
? execution.record.target.specPath
|
|
119
|
+
: undefined;
|
|
120
|
+
const manualActionMessage = resolveVerifyManualActionMessage({
|
|
121
|
+
targetKind: execution.record.target.kind,
|
|
122
|
+
status: execution.record.status,
|
|
123
|
+
selection,
|
|
124
|
+
selectedSpecPath,
|
|
125
|
+
});
|
|
100
126
|
const warningMessage = [
|
|
101
127
|
...selectionWarnings,
|
|
102
128
|
...(selectionPolicyWarning ? [selectionPolicyWarning] : []),
|
|
129
|
+
...(manualActionMessage ? [manualActionMessage] : []),
|
|
103
130
|
].join("\n");
|
|
131
|
+
const displayStatus = selection?.decision.state === "unresolved"
|
|
132
|
+
? "unresolved"
|
|
133
|
+
: execution.record.status;
|
|
134
|
+
if (displayStatus === "unresolved" && !json) {
|
|
135
|
+
renderer.complete("unresolved", {
|
|
136
|
+
startedAt: execution.record.startedAt,
|
|
137
|
+
completedAt: execution.record.completedAt,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
104
140
|
const recommendedRunAgent = execution.record.target.kind === "run" &&
|
|
105
141
|
selection !== undefined &&
|
|
106
142
|
selection.decision.state === "resolvable"
|
|
@@ -109,9 +145,10 @@ export async function runVerifyCommand(options) {
|
|
|
109
145
|
const hintMessage = suppressHint ||
|
|
110
146
|
selectionPolicyWarning ||
|
|
111
147
|
execution.record.target.kind !== "run" ||
|
|
112
|
-
execution.record.status !== "succeeded"
|
|
148
|
+
execution.record.status !== "succeeded" ||
|
|
149
|
+
selection?.decision.state !== "resolvable"
|
|
113
150
|
? undefined
|
|
114
|
-
: `To apply a solution:\n voratiq apply --run ${execution.record.target.sessionId} --agent ${recommendedRunAgent
|
|
151
|
+
: `To apply a solution:\n voratiq apply --run ${execution.record.target.sessionId} --agent ${recommendedRunAgent}`;
|
|
115
152
|
const outputPath = normalizePathForDisplay(relativeToRoot(root, resolveWorkspacePath(root, VORATIQ_VERIFICATION_SESSIONS_DIR, execution.verificationId)));
|
|
116
153
|
const body = renderVerifyTranscript({
|
|
117
154
|
verificationId: execution.verificationId,
|
|
@@ -123,7 +160,7 @@ export async function runVerifyCommand(options) {
|
|
|
123
160
|
}) ?? "—",
|
|
124
161
|
workspacePath: normalizePathForDisplay(relativeToRoot(root, resolveWorkspacePath(root, VORATIQ_VERIFICATION_SESSIONS_DIR, execution.verificationId))),
|
|
125
162
|
target: execution.record.target,
|
|
126
|
-
status:
|
|
163
|
+
status: displayStatus,
|
|
127
164
|
methods: methodBlocks,
|
|
128
165
|
suppressHint,
|
|
129
166
|
...(warningMessage ? { warningMessage } : {}),
|
|
@@ -133,15 +170,17 @@ export async function runVerifyCommand(options) {
|
|
|
133
170
|
});
|
|
134
171
|
return {
|
|
135
172
|
verificationId: execution.verificationId,
|
|
136
|
-
status:
|
|
173
|
+
status: displayStatus,
|
|
137
174
|
target: execution.record.target,
|
|
138
175
|
body,
|
|
139
|
-
exitCode: execution.record.status === "succeeded"
|
|
176
|
+
exitCode: execution.record.status === "succeeded" &&
|
|
177
|
+
selection?.decision.state !== "unresolved" &&
|
|
178
|
+
!(execution.record.target.kind === "spec" &&
|
|
179
|
+
typeof selectedSpecPath !== "string")
|
|
180
|
+
? 0
|
|
181
|
+
: 1,
|
|
140
182
|
outputPath,
|
|
141
|
-
...(
|
|
142
|
-
typeof execution.record.target.specPath === "string"
|
|
143
|
-
? { selectedSpecPath: execution.record.target.specPath }
|
|
144
|
-
: {}),
|
|
183
|
+
...(selectedSpecPath ? { selectedSpecPath } : {}),
|
|
145
184
|
selection,
|
|
146
185
|
...(warningMessage ? { warningMessage } : {}),
|
|
147
186
|
};
|
|
@@ -101,12 +101,7 @@ export async function executeAutoCommand(options, dependencies) {
|
|
|
101
101
|
stderr: specVerifyResult.stderr,
|
|
102
102
|
exitCode: specVerifyResult.exitCode,
|
|
103
103
|
});
|
|
104
|
-
if (specVerifyResult.
|
|
105
|
-
specStatus = "failed";
|
|
106
|
-
specDetail = "One or more spec verifiers failed.";
|
|
107
|
-
hardFailure = true;
|
|
108
|
-
}
|
|
109
|
-
else if (typeof specVerifyResult.selectedSpecPath === "string" &&
|
|
104
|
+
if (typeof specVerifyResult.selectedSpecPath === "string" &&
|
|
110
105
|
specVerifyResult.selectedSpecPath.trim().length > 0) {
|
|
111
106
|
resolvedSpecPath = specVerifyResult.selectedSpecPath;
|
|
112
107
|
specPath = specVerifyResult.selectedSpecPath;
|
|
@@ -117,7 +112,7 @@ export async function executeAutoCommand(options, dependencies) {
|
|
|
117
112
|
"Spec verification returned a resolvable selection without a selected spec path.";
|
|
118
113
|
hardFailure = true;
|
|
119
114
|
}
|
|
120
|
-
else {
|
|
115
|
+
else if (specVerifyResult.selection) {
|
|
121
116
|
const specSelectionDisposition = classifyAutoVerificationSelection({
|
|
122
117
|
targetKind: "spec",
|
|
123
118
|
selection: specVerifyResult.selection,
|
|
@@ -128,6 +123,13 @@ export async function executeAutoCommand(options, dependencies) {
|
|
|
128
123
|
specDetail = specSelectionDisposition.detail;
|
|
129
124
|
markActionRequired(specSelectionDisposition.detail);
|
|
130
125
|
}
|
|
126
|
+
else if (specVerifyResult.exitCode === 1) {
|
|
127
|
+
specStatus = "failed";
|
|
128
|
+
specDetail =
|
|
129
|
+
specVerifyResult.warningMessage?.trim() ||
|
|
130
|
+
"Spec verification did not produce any successful verifier results.";
|
|
131
|
+
hardFailure = true;
|
|
132
|
+
}
|
|
131
133
|
}
|
|
132
134
|
catch (error) {
|
|
133
135
|
specStatus = "failed";
|
|
@@ -215,11 +217,6 @@ export async function executeAutoCommand(options, dependencies) {
|
|
|
215
217
|
});
|
|
216
218
|
verifyStatus = "succeeded";
|
|
217
219
|
verifySelection = verifyResult.selection;
|
|
218
|
-
if (verifyResult.exitCode === 1) {
|
|
219
|
-
verifyStatus = "failed";
|
|
220
|
-
verifyDetail = "One or more verifiers failed.";
|
|
221
|
-
hardFailure = true;
|
|
222
|
-
}
|
|
223
220
|
recordEvent({
|
|
224
221
|
kind: "body",
|
|
225
222
|
body: verifyResult.body,
|
|
@@ -234,6 +231,12 @@ export async function executeAutoCommand(options, dependencies) {
|
|
|
234
231
|
separateWithDivider: bodyOutputEmitted,
|
|
235
232
|
});
|
|
236
233
|
}
|
|
234
|
+
if (options.apply === true &&
|
|
235
|
+
(verifyResult.selectionWarnings?.length ?? 0) > 0) {
|
|
236
|
+
const warningDetail = "Verification reported warnings for the selected candidate; automatic apply halted. Review the verify output and apply manually if appropriate.";
|
|
237
|
+
verifyDetail = warningDetail;
|
|
238
|
+
markActionRequired(warningDetail);
|
|
239
|
+
}
|
|
237
240
|
if (verifySelection?.state === "unresolved") {
|
|
238
241
|
const verifySelectionDisposition = classifyAutoVerificationSelection({
|
|
239
242
|
targetKind: "run",
|
|
@@ -247,6 +250,13 @@ export async function executeAutoCommand(options, dependencies) {
|
|
|
247
250
|
},
|
|
248
251
|
});
|
|
249
252
|
}
|
|
253
|
+
else if (verifyResult.exitCode === 1) {
|
|
254
|
+
verifyStatus = "failed";
|
|
255
|
+
verifyDetail =
|
|
256
|
+
verifyResult.warningMessage?.trim() ||
|
|
257
|
+
"Verification did not produce any successful verifier results.";
|
|
258
|
+
hardFailure = true;
|
|
259
|
+
}
|
|
250
260
|
}
|
|
251
261
|
catch (error) {
|
|
252
262
|
verifyStatus = "failed";
|
|
@@ -12,7 +12,7 @@ export class AutoPreflightError extends CliError {
|
|
|
12
12
|
}
|
|
13
13
|
});
|
|
14
14
|
super("Preflight failed. Aborting auto.", detailLines, [
|
|
15
|
-
"Run `voratiq
|
|
15
|
+
"Run `voratiq doctor --fix` to repair workspace setup.",
|
|
16
16
|
]);
|
|
17
17
|
this.name = "AutoPreflightError";
|
|
18
18
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type AgentPreset } from "../../workspace/templates.js";
|
|
2
|
+
import type { AgentInitSummary, DoctorBootstrapConfigureOptions } from "./fix-types.js";
|
|
3
|
+
export declare const AGENTS_CONFIG_DISPLAY_PATH: string;
|
|
4
|
+
export declare function bootstrapDoctorAgents(root: string, preset: AgentPreset, options: DoctorBootstrapConfigureOptions): Promise<AgentInitSummary>;
|
|
5
|
+
export declare function reconcileManagedDoctorAgents(root: string): Promise<AgentInitSummary>;
|
|
@@ -1,21 +1,35 @@
|
|
|
1
1
|
import { getAgentDefaultId, getSupportedAgentDefaults, } from "../../configs/agents/defaults.js";
|
|
2
2
|
import { readAgentsConfig } from "../../configs/agents/loader.js";
|
|
3
3
|
import { detectBinary } from "../../utils/binaries.js";
|
|
4
|
-
import { isDefaultYamlTemplate, loadYamlConfig,
|
|
4
|
+
import { isDefaultYamlTemplate, loadYamlConfig, writeConfigIfChanged, } from "../../utils/yaml.js";
|
|
5
|
+
import { isManagedAgentsFingerprintMatch, readManagedState, } from "../../workspace/managed-state.js";
|
|
5
6
|
import { formatWorkspacePath, resolveWorkspacePath, VORATIQ_AGENTS_FILE, } from "../../workspace/structure.js";
|
|
6
7
|
import { buildDefaultAgentsTemplate, serializeAgentsConfigEntries, } from "../../workspace/templates.js";
|
|
7
8
|
export const AGENTS_CONFIG_DISPLAY_PATH = formatWorkspacePath(VORATIQ_AGENTS_FILE);
|
|
8
|
-
export async function
|
|
9
|
+
export async function bootstrapDoctorAgents(root, preset, options) {
|
|
9
10
|
void preset;
|
|
10
11
|
void options;
|
|
11
|
-
const filePath = resolveWorkspacePath(root, VORATIQ_AGENTS_FILE);
|
|
12
12
|
const defaultTemplate = buildDefaultAgentsTemplate();
|
|
13
|
+
return configureAgentsWithMode(root, defaultTemplate, "bootstrap");
|
|
14
|
+
}
|
|
15
|
+
export async function reconcileManagedDoctorAgents(root) {
|
|
16
|
+
const defaultTemplate = buildDefaultAgentsTemplate();
|
|
17
|
+
return configureAgentsWithMode(root, defaultTemplate, "reconcile");
|
|
18
|
+
}
|
|
19
|
+
async function configureAgentsWithMode(root, defaultTemplate, mode) {
|
|
20
|
+
const filePath = resolveWorkspacePath(root, VORATIQ_AGENTS_FILE);
|
|
13
21
|
const loadResult = await loadYamlConfig(filePath, readAgentsConfig);
|
|
14
22
|
const defaultStatus = isDefaultYamlTemplate(loadResult.snapshot, defaultTemplate);
|
|
15
23
|
const configCreated = !loadResult.snapshot.exists;
|
|
16
|
-
const
|
|
24
|
+
const managedState = mode === "reconcile" ? await readManagedState(root) : undefined;
|
|
25
|
+
const managed = mode === "reconcile"
|
|
26
|
+
? !loadResult.snapshot.exists ||
|
|
27
|
+
isManagedAgentsFingerprintMatch(managedState?.configs.agents, loadResult.snapshot.content) ||
|
|
28
|
+
defaultStatus
|
|
29
|
+
: defaultStatus;
|
|
30
|
+
const lifecycle = scanWorkspaceForAgentDefaults(loadResult.config, mode, getSupportedAgentDefaults());
|
|
17
31
|
const detectedProviders = collectDetectedProviders(lifecycle.templates);
|
|
18
|
-
if (!
|
|
32
|
+
if (!managed && loadResult.snapshot.exists) {
|
|
19
33
|
return buildAgentSummary({
|
|
20
34
|
entries: loadResult.config.agents,
|
|
21
35
|
zeroDetections: detectedProviders.length === 0,
|
|
@@ -23,16 +37,14 @@ export async function configureAgents(root, preset, options) {
|
|
|
23
37
|
providerEnablementPrompted: false,
|
|
24
38
|
configCreated,
|
|
25
39
|
configUpdated: false,
|
|
40
|
+
managed: false,
|
|
26
41
|
});
|
|
27
42
|
}
|
|
28
43
|
const snapshotResult = finalizeAgentConfigSnapshot(lifecycle);
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
defaultTemplate,
|
|
34
|
-
isDefaultTemplate: defaultStatus,
|
|
35
|
-
});
|
|
44
|
+
const previousNormalized = loadResult.snapshot.exists
|
|
45
|
+
? loadResult.snapshot.normalized
|
|
46
|
+
: "__missing__";
|
|
47
|
+
const updated = await writeConfigIfChanged(filePath, snapshotResult.serialized, previousNormalized);
|
|
36
48
|
return buildAgentSummary({
|
|
37
49
|
entries: snapshotResult.entries,
|
|
38
50
|
zeroDetections: lifecycle.zeroDetections,
|
|
@@ -40,9 +52,10 @@ export async function configureAgents(root, preset, options) {
|
|
|
40
52
|
providerEnablementPrompted: false,
|
|
41
53
|
configCreated,
|
|
42
54
|
configUpdated: updated,
|
|
55
|
+
managed: true,
|
|
43
56
|
});
|
|
44
57
|
}
|
|
45
|
-
function scanWorkspaceForAgentDefaults(config,
|
|
58
|
+
function scanWorkspaceForAgentDefaults(config, mode, templates) {
|
|
46
59
|
const templatesById = new Map();
|
|
47
60
|
for (const entry of config.agents) {
|
|
48
61
|
templatesById.set(entry.id, entry);
|
|
@@ -56,9 +69,7 @@ function scanWorkspaceForAgentDefaults(config, isDefaultTemplate, templates) {
|
|
|
56
69
|
}));
|
|
57
70
|
for (const { template, templateId } of templateEntries) {
|
|
58
71
|
templateIds.add(templateId);
|
|
59
|
-
const existing =
|
|
60
|
-
? undefined
|
|
61
|
-
: templatesById.get(templateId);
|
|
72
|
+
const existing = templatesById.get(templateId);
|
|
62
73
|
let detectedBinary = detectedBinaryByProvider.get(template.provider);
|
|
63
74
|
if (!detectedBinaryByProvider.has(template.provider)) {
|
|
64
75
|
detectedBinary = detectBinary(template.provider);
|
|
@@ -66,8 +77,14 @@ function scanWorkspaceForAgentDefaults(config, isDefaultTemplate, templates) {
|
|
|
66
77
|
}
|
|
67
78
|
const baseEntry = existing ?? buildEntryFromTemplate(template, templateId);
|
|
68
79
|
const entry = cloneAgentEntry(baseEntry);
|
|
69
|
-
|
|
70
|
-
|
|
80
|
+
if (hasBinary(detectedBinary)) {
|
|
81
|
+
entry.binary = detectedBinary ?? "";
|
|
82
|
+
entry.enabled = true;
|
|
83
|
+
}
|
|
84
|
+
else if (!existing || mode === "bootstrap") {
|
|
85
|
+
entry.binary = "";
|
|
86
|
+
entry.enabled = false;
|
|
87
|
+
}
|
|
71
88
|
templateStates.push({
|
|
72
89
|
templateId,
|
|
73
90
|
template,
|
|
@@ -142,7 +159,7 @@ function finalizeAgentConfigSnapshot(state) {
|
|
|
142
159
|
return { entries, serialized };
|
|
143
160
|
}
|
|
144
161
|
function buildAgentSummary(options) {
|
|
145
|
-
const { entries, zeroDetections, detectedProviders, providerEnablementPrompted, configCreated, configUpdated, } = options;
|
|
162
|
+
const { entries, zeroDetections, detectedProviders, providerEnablementPrompted, configCreated, configUpdated, managed, } = options;
|
|
146
163
|
const enabledAgents = entries
|
|
147
164
|
.filter((entry) => entry.enabled !== false)
|
|
148
165
|
.map((entry) => entry.id);
|
|
@@ -155,6 +172,7 @@ function buildAgentSummary(options) {
|
|
|
155
172
|
providerEnablementPrompted,
|
|
156
173
|
configCreated,
|
|
157
174
|
configUpdated,
|
|
175
|
+
managed,
|
|
158
176
|
};
|
|
159
177
|
}
|
|
160
178
|
function hasBinary(binary) {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { executeDoctorBootstrap } from "./fix.js";
|
|
2
|
+
import type { DoctorReconcileResult } from "./fix-types.js";
|
|
3
|
+
export interface DoctorDiagnosisResult {
|
|
4
|
+
readonly healthy: boolean;
|
|
5
|
+
readonly issueLines: readonly string[];
|
|
6
|
+
}
|
|
7
|
+
export type DoctorFixMode = "bootstrap-workspace" | "repair-and-reconcile";
|
|
8
|
+
export interface DoctorFixResult {
|
|
9
|
+
readonly mode: DoctorFixMode;
|
|
10
|
+
readonly reconcileResult?: DoctorReconcileResult;
|
|
11
|
+
}
|
|
12
|
+
export interface ExecuteDoctorDiagnosisInput {
|
|
13
|
+
readonly root: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ExecuteDoctorFixInput {
|
|
16
|
+
readonly root: string;
|
|
17
|
+
readonly mode?: DoctorFixMode;
|
|
18
|
+
readonly bootstrapOptions?: Pick<Parameters<typeof executeDoctorBootstrap>[0], "preset" | "interactive" | "assumeYes" | "confirm" | "prompt">;
|
|
19
|
+
}
|
|
20
|
+
export declare function executeDoctorDiagnosis(input: ExecuteDoctorDiagnosisInput): Promise<DoctorDiagnosisResult>;
|
|
21
|
+
export declare function resolveDoctorFixMode(root: string): Promise<DoctorFixMode>;
|
|
22
|
+
export declare function executeDoctorFix(input: ExecuteDoctorFixInput): Promise<DoctorFixResult>;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { formatPreflightIssueLines } from "../../competition/shared/preflight.js";
|
|
2
|
+
import { EnvironmentConfigParseError, MissingEnvironmentConfigError, } from "../../configs/environment/errors.js";
|
|
3
|
+
import { loadEnvironmentConfig } from "../../configs/environment/loader.js";
|
|
4
|
+
import { prepareConfiguredOperatorReadiness } from "../../preflight/operator.js";
|
|
5
|
+
import { toErrorMessage } from "../../utils/errors.js";
|
|
6
|
+
import { pathExists } from "../../utils/fs.js";
|
|
7
|
+
import { WorkspaceError, WorkspaceMissingEntryError, } from "../../workspace/errors.js";
|
|
8
|
+
import { repairWorkspaceStructure, validateWorkspace, } from "../../workspace/setup.js";
|
|
9
|
+
import { formatWorkspacePath, resolveWorkspacePath, } from "../../workspace/structure.js";
|
|
10
|
+
import { executeDoctorBootstrap } from "./fix.js";
|
|
11
|
+
import { executeDoctorReconcile } from "./reconcile.js";
|
|
12
|
+
const DOCTOR_PREFLIGHT_UNLABELED_AGENT_IDS = ["settings"];
|
|
13
|
+
export async function executeDoctorDiagnosis(input) {
|
|
14
|
+
const { root } = input;
|
|
15
|
+
const issueLines = [];
|
|
16
|
+
const workspacePresent = await pathExists(resolveWorkspacePath(root));
|
|
17
|
+
if (!workspacePresent) {
|
|
18
|
+
const missingWorkspaceIssue = new WorkspaceMissingEntryError(`${formatWorkspacePath()}/`);
|
|
19
|
+
issueLines.push(`- ${missingWorkspaceIssue.headline}`);
|
|
20
|
+
return {
|
|
21
|
+
healthy: false,
|
|
22
|
+
issueLines,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
await validateWorkspace(root);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
issueLines.push(...formatDoctorIssueLines(error));
|
|
30
|
+
return {
|
|
31
|
+
healthy: false,
|
|
32
|
+
issueLines,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const diagnostics = await prepareConfiguredOperatorReadiness({ root });
|
|
37
|
+
if (diagnostics.noAgentsEnabled) {
|
|
38
|
+
issueLines.push("- No agents are enabled in `agents.yaml`.");
|
|
39
|
+
}
|
|
40
|
+
issueLines.push(...formatPreflightIssueLines(diagnostics.issues, {
|
|
41
|
+
unlabeledAgentIds: DOCTOR_PREFLIGHT_UNLABELED_AGENT_IDS,
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
issueLines.push(...formatDoctorIssueLines(error));
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
loadEnvironmentConfig({ root });
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (error instanceof MissingEnvironmentConfigError) {
|
|
52
|
+
issueLines.push(`- ${error.headline}`);
|
|
53
|
+
}
|
|
54
|
+
else if (error instanceof EnvironmentConfigParseError) {
|
|
55
|
+
issueLines.push(`- ${error.headline}`);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
issueLines.push(`- ${toErrorMessage(error)}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
healthy: issueLines.length === 0,
|
|
63
|
+
issueLines,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export async function resolveDoctorFixMode(root) {
|
|
67
|
+
const workspacePresent = await pathExists(resolveWorkspacePath(root));
|
|
68
|
+
return workspacePresent ? "repair-and-reconcile" : "bootstrap-workspace";
|
|
69
|
+
}
|
|
70
|
+
export async function executeDoctorFix(input) {
|
|
71
|
+
const mode = input.mode ?? (await resolveDoctorFixMode(input.root));
|
|
72
|
+
if (mode === "bootstrap-workspace") {
|
|
73
|
+
const bootstrapOptions = input.bootstrapOptions;
|
|
74
|
+
await executeDoctorBootstrap({
|
|
75
|
+
root: input.root,
|
|
76
|
+
preset: bootstrapOptions?.preset ?? "pro",
|
|
77
|
+
interactive: bootstrapOptions?.interactive ?? false,
|
|
78
|
+
assumeYes: bootstrapOptions?.assumeYes,
|
|
79
|
+
confirm: bootstrapOptions?.confirm,
|
|
80
|
+
prompt: bootstrapOptions?.prompt,
|
|
81
|
+
});
|
|
82
|
+
return { mode };
|
|
83
|
+
}
|
|
84
|
+
await repairWorkspaceStructure(input.root);
|
|
85
|
+
const reconcileResult = await executeDoctorReconcile({ root: input.root });
|
|
86
|
+
return {
|
|
87
|
+
mode,
|
|
88
|
+
reconcileResult,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function formatDoctorIssueLines(error) {
|
|
92
|
+
if (error instanceof WorkspaceError) {
|
|
93
|
+
return [`- ${error.headline}`];
|
|
94
|
+
}
|
|
95
|
+
if (error instanceof Error && error.message.length > 0) {
|
|
96
|
+
return [`- ${error.message}`];
|
|
97
|
+
}
|
|
98
|
+
return [`- ${toErrorMessage(error)}`];
|
|
99
|
+
}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { detectEnvironmentConfig } from "../../configs/environment/detect.js";
|
|
2
|
+
import { EnvironmentConfigParseError } from "../../configs/environment/errors.js";
|
|
2
3
|
import { readEnvironmentConfig, serializeEnvironmentConfig, } from "../../configs/environment/loader.js";
|
|
3
4
|
import { getNodeDependencyRoots, getPythonEnvironmentPath, isNodeEnvironmentDisabled, isPythonEnvironmentDisabled, } from "../../configs/environment/types.js";
|
|
4
|
-
import {
|
|
5
|
+
import { persistYamlConfig, readConfigSnapshot } from "../../utils/yaml.js";
|
|
5
6
|
import { formatWorkspacePath, resolveWorkspacePath, VORATIQ_ENVIRONMENT_FILE, } from "../../workspace/structure.js";
|
|
6
7
|
import { buildDefaultEnvironmentTemplate } from "../../workspace/templates.js";
|
|
7
8
|
const ENVIRONMENT_CONFIG_DISPLAY_PATH = formatWorkspacePath(VORATIQ_ENVIRONMENT_FILE);
|
|
8
|
-
export async function
|
|
9
|
+
export async function reconcileDoctorEnvironment(root, options) {
|
|
9
10
|
const filePath = resolveWorkspacePath(root, VORATIQ_ENVIRONMENT_FILE);
|
|
10
11
|
const defaultTemplate = buildDefaultEnvironmentTemplate();
|
|
11
|
-
const
|
|
12
|
-
const existingConfig =
|
|
12
|
+
const originalSnapshot = await readConfigSnapshot(filePath);
|
|
13
|
+
const existingConfig = resolveExistingConfig(originalSnapshot);
|
|
13
14
|
const detection = await detectEnvironmentConfig({
|
|
14
15
|
root,
|
|
15
16
|
interactive: options.interactive,
|
|
@@ -20,17 +21,48 @@ export async function configureEnvironment(root, options) {
|
|
|
20
21
|
const configUpdated = await persistYamlConfig({
|
|
21
22
|
filePath,
|
|
22
23
|
serialized: finalSerialized,
|
|
23
|
-
original:
|
|
24
|
+
original: shouldRewriteFromScratch(originalSnapshot)
|
|
25
|
+
? { content: "", normalized: "", exists: false }
|
|
26
|
+
: originalSnapshot,
|
|
24
27
|
defaultTemplate,
|
|
25
28
|
});
|
|
26
29
|
return {
|
|
27
30
|
configPath: ENVIRONMENT_CONFIG_DISPLAY_PATH,
|
|
28
31
|
detectedEntries: describeEnvironmentEntries(mergedConfig),
|
|
29
|
-
configCreated: !
|
|
32
|
+
configCreated: !originalSnapshot.exists,
|
|
30
33
|
configUpdated,
|
|
31
34
|
config: mergedConfig,
|
|
32
35
|
};
|
|
33
36
|
}
|
|
37
|
+
function resolveExistingConfig(snapshot) {
|
|
38
|
+
if (!snapshot.exists) {
|
|
39
|
+
return {};
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
return readEnvironmentConfig(snapshot.content);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (error instanceof EnvironmentConfigParseError) {
|
|
46
|
+
return {};
|
|
47
|
+
}
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function shouldRewriteFromScratch(snapshot) {
|
|
52
|
+
if (!snapshot.exists) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
readEnvironmentConfig(snapshot.content);
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
if (error instanceof EnvironmentConfigParseError) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
34
66
|
function mergeEnvironmentConfig(existing, detected) {
|
|
35
67
|
const merged = { ...existing };
|
|
36
68
|
if (!isNodeEnvironmentDisabled(merged)) {
|