fix(config): expose native matching session repair

This commit is contained in:
gensokyo-dev
2026-10-25 17:55:38 +00:00
parent abcb1b2cd3
commit 6e9dc4534b
6 changed files with 57 additions and 3 deletions
@@ -1,3 +1,4 @@
import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
import { expect, it } from "vitest";
import { ConfigurationError } from "../domain/configurationErrors.js";
import { parseEnvironment } from "./environment.js";
@@ -76,3 +77,19 @@ it("refuses invalid launch values instead of falling back to the default", () =>
});
}
});
it("advertises current-session repair for an invalid native matching launch seed", () => {
const parsed = parseNativeMatchingAllowance({
[NATIVE_MATCHING_ALLOWANCE_SETTING]: "0",
});
if (parsed.ok) throw new Error("Invalid seed was accepted");
const projection = projectAnalysisError(parsed.error);
expect(projection.code).toBe("configuration_invalid");
expect(projection.remediation.action).toContain("update_analysis_settings");
expect(projection.remediation.action).toContain(
"native.matching_representation_bytes",
);
expect(projection.remediation.action).toContain(
NATIVE_MATCHING_ALLOWANCE_SETTING,
);
});
+1
View File
@@ -47,6 +47,7 @@ export const parseNativeMatchingAllowance = (
? ok({ maximumBytes: parsed.data, source: "setting" })
: err(
new ConfigurationError(nativeMatchingRepresentationBytesConstraint, {
remediationAction: `On a running MCP session, call update_analysis_settings with ${JSON.stringify({ changes: [{ setting: "native.matching_representation_bytes", action: "set", value: DEFAULT_NATIVE_MATCHING_REPRESENTATION_BYTES }] })}, then retry matching. This repairs the current session for the next operation. For CLI or future REA processes, correct ${NATIVE_MATCHING_ALLOWANCE_SETTING} in the launch environment.`,
settings: [
{
setting: NATIVE_MATCHING_ALLOWANCE_SETTING,
+7 -2
View File
@@ -158,8 +158,13 @@ export const analysisErrorRemediationAction = (
return "Check the current process's read access to the selected path. Retry with a readable local file.";
if (error instanceof AnalysisArtifactChangedError)
return "Wait until the selected file is stable. For an active binary session, reopen the target with open_binary before retrying so REA acquires its current identity; for a CLI command or target-free tool, rerun the operation.";
if (error instanceof ConfigurationError && error.settings.length > 0)
return `Correct ${[...new Set(error.settings.map(({ setting }) => setting))].join(", ")} in the environment that starts REA (your shell or the MCP client's registration), then rerun.`;
if (error instanceof ConfigurationError)
return (
error.remediationAction ??
(error.settings.length > 0
? `Correct ${[...new Set(error.settings.map(({ setting }) => setting))].join(", ")} in the environment that starts REA (your shell or the MCP client's registration), then rerun.`
: analysisErrorUserMessage(error))
);
if (error instanceof AnalysisInputError)
return (
error.remediationAction ??
+25 -1
View File
@@ -9,7 +9,10 @@ import {
AnalysisResourceConstraintError,
} from "./analysisErrorCore.js";
import { ArtifactOperationError } from "./artifactOperationError.js";
import { BinaryTargetError } from "./configurationErrors.js";
import {
BinaryTargetError,
ConfigurationError,
} from "./configurationErrors.js";
import { BrowserObservationError } from "./browserObservationError.js";
import { EvidenceFileError, EvidenceIntegrityError } from "./evidenceErrors.js";
import {
@@ -1170,3 +1173,24 @@ it("does not offer entry-policy retries for integrity refusals without a declare
expect(native.message).not.toContain("record-and-continue");
expect(native.remediation.action).not.toContain("record-and-continue");
});
it("preserves ordinary configuration recovery and projects a producer-owned session repair", () => {
const settings = [{ setting: "REA_EXAMPLE", constraint: "must be positive" }];
const original = projectAnalysisError(
new ConfigurationError("Invalid setting", { settings }),
);
const repairing = projectAnalysisError(
new ConfigurationError("Invalid setting", {
settings,
remediationAction: "Repair the running session, then retry.",
}),
);
expect(original.remediation.action).toBe(
"Correct REA_EXAMPLE in the environment that starts REA (your shell or the MCP client's registration), then rerun.",
);
expect(repairing).toEqual({
...original,
remediation: { action: "Repair the running session, then retry." },
});
expect(repairing.code).toBe("configuration_invalid");
});
+4
View File
@@ -12,14 +12,18 @@ export interface ConfigurationSettingIssue {
/** Rejected settings retained for diagnostics; their values are not. */
export interface ConfigurationErrorOptions extends AnalysisErrorOptions {
readonly settings?: readonly ConfigurationSettingIssue[];
/** Producer-owned recovery when this setting can be repaired in a running session. */
readonly remediationAction?: string;
}
/** Runtime configuration could not be parsed safely. */
export class ConfigurationError extends AnalysisError {
readonly _tag = "ConfigurationError";
readonly settings: readonly ConfigurationSettingIssue[];
readonly remediationAction: string | undefined;
constructor(message: string, options?: ConfigurationErrorOptions) {
super(message, options);
this.remediationAction = options?.remediationAction;
this.settings = (options?.settings ?? []).map(
({ setting, constraint }) => ({ setting, constraint }),
);
@@ -266,6 +266,9 @@ it("lowers, restores and seeds the native matching allowance over MCP", async ()
if (text?.type !== "text") throw new Error("Missing projected error");
expect(JSON.parse(text.text).error).toMatchObject({
code: "configuration_invalid",
remediation: {
action: expect.stringContaining("update_analysis_settings"),
},
details: {
settings: [{ setting: "REA_NATIVE_MATCHING_REPRESENTATION_BYTES" }],
},