mirror of
https://github.com/morluto/rea.git
synced 2026-11-09 16:26:36 +00:00
fix(cli)!: retain repeated WASM input paths (#9624)
BREAKING CHANGE: caller_input_paths values may now be ordered arrays of strings as well as strings. Clients must accept both forms.
This commit is contained in:
1 parent
8dc2d7bec4
commit
400a91b5f9
7 files changed
+79
-12
No files matched your search
@@ -633,7 +633,9 @@ including relative spelling and symlink aliases. Its subject identifies the
|
||||
inspected artifact; `caller_input_path` does not change the Evidence ID or
|
||||
snapshot query. A replay reports the current invocation's path while leaving
|
||||
the saved Evidence unchanged. Provider inspections continue to include the
|
||||
established `parameters.input_path` field.
|
||||
established `parameters.input_path` field. Named auxiliary inputs use
|
||||
`caller_input_paths`; each value is a string or an ordered array of strings
|
||||
for repeated selections. Consumers must accept both forms.
|
||||
|
||||
Importing an Evidence bundle refreshes retained request and subject display metadata.
|
||||
It preserves cached analysis results, including observations for other targets.
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
parseEvidence,
|
||||
withEvidenceCallerInputPath,
|
||||
type Evidence,
|
||||
type CallerInputPaths,
|
||||
} from "../domain/evidence.js";
|
||||
import type { JsonValue } from "../domain/jsonValue.js";
|
||||
import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
|
||||
@@ -60,7 +61,7 @@ interface CliEvidenceDelivery {
|
||||
/** Exact one-shot CLI target spelling, when this invocation selects a file. */
|
||||
readonly callerInputPath?: string;
|
||||
/** Verbatim named auxiliary path choices for the same direct invocation. */
|
||||
readonly callerInputPaths?: Readonly<Record<string, string>>;
|
||||
readonly callerInputPaths?: CallerInputPaths;
|
||||
/** Derived workflows publish their selected source closure with the result. */
|
||||
readonly sourceEvidence?: readonly Evidence[];
|
||||
/** Invocation exclusions cover final cleanup and publication paths/diagnostics. */
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
parseEvidence,
|
||||
withEvidenceCallerInputPath,
|
||||
type Evidence,
|
||||
type CallerInputPaths,
|
||||
} from "../domain/evidence.js";
|
||||
import type { AnalysisError } from "../domain/analysisErrorBase.js";
|
||||
import type { Result } from "../domain/result.js";
|
||||
@@ -13,7 +14,7 @@ export const bindCliEvidenceCallerPath =
|
||||
(
|
||||
callerPath: string,
|
||||
run: () => Promise<Result<Evidence, AnalysisError>>,
|
||||
additionalPaths?: Readonly<Record<string, string>>,
|
||||
additionalPaths?: CallerInputPaths,
|
||||
): (() => Promise<Result<Evidence, AnalysisError>>) =>
|
||||
async () => {
|
||||
const result = await run();
|
||||
@@ -49,9 +50,10 @@ export const finishCliEvidenceCommand = (
|
||||
providerId: string,
|
||||
operation: string,
|
||||
signal: AbortSignal,
|
||||
additionalPaths?: CallerInputPaths,
|
||||
) =>
|
||||
finishAnalysisCommand(
|
||||
bindCliEvidenceCallerPath(callerPath, run),
|
||||
bindCliEvidenceCallerPath(callerPath, run, additionalPaths),
|
||||
service,
|
||||
providerId,
|
||||
operation,
|
||||
|
||||
@@ -107,6 +107,17 @@ export const registerWasmCommands = (
|
||||
"wabt-artifact",
|
||||
"inspect_wasm_artifact",
|
||||
signal,
|
||||
options.glue_paths === undefined &&
|
||||
options.candidate_paths === undefined
|
||||
? undefined
|
||||
: {
|
||||
...(options.glue_paths === undefined
|
||||
? {}
|
||||
: { glue_paths: options.glue_paths }),
|
||||
...(options.candidate_paths === undefined
|
||||
? {}
|
||||
: { candidate_paths: options.candidate_paths }),
|
||||
},
|
||||
).then(projectCliAnalysisResult),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -331,16 +331,24 @@ it("retains named auxiliary path choices without changing identity, history or s
|
||||
result: { rows: ["source facts"] },
|
||||
}),
|
||||
);
|
||||
const paths = { operations_path: "./queries 雪.graphql" };
|
||||
const paths = {
|
||||
operations_path: "./queries 雪.graphql",
|
||||
candidate_paths: ["./a", "./a", "./b"],
|
||||
};
|
||||
const rebound = withEvidenceCallerInputPath(
|
||||
original,
|
||||
"./schema.graphql",
|
||||
paths,
|
||||
);
|
||||
paths.operations_path = "later unrelated choice";
|
||||
paths.candidate_paths.push("./later");
|
||||
expect(rebound.caller_input_paths).toEqual({
|
||||
operations_path: "./queries 雪.graphql",
|
||||
candidate_paths: ["./a", "./a", "./b"],
|
||||
});
|
||||
expect(Object.isFrozen(rebound.caller_input_paths?.candidate_paths)).toBe(
|
||||
true,
|
||||
);
|
||||
expect(original.caller_input_paths).toBeUndefined();
|
||||
expect(rebound.evidence_id).toBe(original.evidence_id);
|
||||
expect(rebound.normalized_result).toBe(original.normalized_result);
|
||||
@@ -349,6 +357,7 @@ it("retains named auxiliary path choices without changing identity, history or s
|
||||
expect(
|
||||
withEvidenceCallerInputPath(rebound, "./schema.graphql", {
|
||||
operations_path: "./queries 雪.graphql",
|
||||
candidate_paths: ["./a", "./a", "./b"],
|
||||
}),
|
||||
).toBe(rebound);
|
||||
expect(
|
||||
|
||||
@@ -88,8 +88,12 @@ const executionEnvironmentSchema = z.strictObject({
|
||||
});
|
||||
const callerInputPathsSchema = z.record(
|
||||
nulFreeStringSchema,
|
||||
nulFreeStringSchema,
|
||||
z.union([nulFreeStringSchema, z.array(nulFreeStringSchema)]),
|
||||
);
|
||||
/** Caller-selected auxiliary paths, retaining ordered repeated options. */
|
||||
export type CallerInputPaths = Readonly<
|
||||
Record<string, string | readonly string[]>
|
||||
>;
|
||||
|
||||
const evidenceBaseSchema = z
|
||||
.object({
|
||||
@@ -107,7 +111,7 @@ const evidenceBaseSchema = z
|
||||
caller_input_paths: callerInputPathsSchema
|
||||
.exactOptional()
|
||||
.describe(
|
||||
"Verbatim auxiliary input paths keyed by argument name; caller assertions outside semantic identity.",
|
||||
"Verbatim auxiliary input paths keyed by argument name; repeated selections retain their order. Caller assertions are outside semantic identity.",
|
||||
),
|
||||
raw_result: jsonValueSchema.nullable(),
|
||||
normalized_result: jsonValueSchema,
|
||||
@@ -375,7 +379,7 @@ export const rebindEvidenceToTargetPath = (
|
||||
export const withEvidenceCallerInputPath = (
|
||||
evidence: Evidence,
|
||||
rawPath: string,
|
||||
additionalPaths?: Readonly<Record<string, string>>,
|
||||
additionalPaths?: CallerInputPaths,
|
||||
): Evidence => {
|
||||
const callerInputPath = nulFreeStringSchema.parse(rawPath);
|
||||
const owned = immutableEvidence(evidence);
|
||||
|
||||
@@ -6,8 +6,14 @@ import { z } from "zod";
|
||||
|
||||
import { analysisErrorProjectionSchema } from "../../../src/contracts/errorSchemas.js";
|
||||
import { toolContract } from "../../../src/contracts/toolContracts.js";
|
||||
import { parseEvidence } from "../../../src/domain/evidence.js";
|
||||
import {
|
||||
createEvidence,
|
||||
parseEvidence,
|
||||
withEvidenceCallerInputPath,
|
||||
} from "../../../src/domain/evidence.js";
|
||||
import { wasmArtifactFixture } from "../../fixtures/wasm/artifact.js";
|
||||
import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js";
|
||||
import { advertisedMcpToolSchemas } from "../../fixtures/advertisedMcpTool.js";
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
import { parseMcpToolError } from "../../fixtures/mcpToolError.js";
|
||||
import { cliTest } from "../../support/cli/cliFixture.js";
|
||||
@@ -98,15 +104,18 @@ ${body}
|
||||
const invocation = await cli.run({
|
||||
arguments: [
|
||||
"inspect-wasm-artifact",
|
||||
selected,
|
||||
"./selected.wasm",
|
||||
"--candidate_paths",
|
||||
candidate,
|
||||
"./candidate.wasm",
|
||||
"--candidate_paths",
|
||||
"./candidate.wasm",
|
||||
"--glue_paths",
|
||||
glue,
|
||||
"./glue.js",
|
||||
"--format",
|
||||
"json",
|
||||
],
|
||||
environment,
|
||||
cwd: directory,
|
||||
});
|
||||
expect(invocation.exitCode, invocation.stderr).toBe(1);
|
||||
const cliError = analysisErrorProjectionSchema.parse(invocation.json);
|
||||
@@ -114,12 +123,41 @@ ${body}
|
||||
code: "invalid_request",
|
||||
details: {
|
||||
partial_observation: {
|
||||
caller_input_paths: {
|
||||
candidate_paths: ["./candidate.wasm", "./candidate.wasm"],
|
||||
glue_paths: ["./glue.js"],
|
||||
},
|
||||
normalized_result: expected,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { client, call } = await connectLocalToolsMcp(environment);
|
||||
const schema = await advertisedMcpToolSchemas(
|
||||
client,
|
||||
"inspect_wasm_artifact",
|
||||
);
|
||||
const valid = withEvidenceCallerInputPath(
|
||||
createEvidence(
|
||||
undefined,
|
||||
{ id: "fixture-wabt", name: "fixture", version: "1" },
|
||||
{
|
||||
operation: "inspect_wasm_artifact",
|
||||
parameters: {},
|
||||
result: wasmArtifactFixture(),
|
||||
confidence: "observed",
|
||||
},
|
||||
),
|
||||
"./selected.wasm",
|
||||
{
|
||||
candidate_paths: ["./candidate.wasm", "./candidate.wasm"],
|
||||
glue_paths: ["./glue.js"],
|
||||
},
|
||||
);
|
||||
expect(
|
||||
schema.ajv.validate(schema.outputSchema, valid),
|
||||
JSON.stringify(schema.ajv.errors),
|
||||
).toBe(true);
|
||||
const response = await call("inspect_wasm_artifact", {
|
||||
path: selected,
|
||||
candidate_paths: [candidate],
|
||||
|
||||
Reference in new issue
Block a user