mirror of
https://github.com/morluto/rea.git
synced 2026-11-14 02:32:54 +00:00
feat(analysis-view): project rich captures from Evidence files and template path pairs (#5293)
Project large local Evidence captures through the authenticated file-source boundary and add guarded opt-in sibling-pair path inference. Preserve request cancellation, resource ownership and file-relative validation diagnostics. Validated relevant source and compiled CLI/MCP/environment boundaries, static/build/docs/catalog gates, and a real isolated Gitea 28.2.0 capture through CLI, default stdio MCP and HTTP replay. Current-head automated review passed; unrelated long-running platform jobs may still be pending. Co-authored-by: Edgar Arakelyan <edgar.arakelyan@scale.com> Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com> Co-authored-by: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
This commit is contained in:
28 files changed
+1227
-66
No files matched your search
@@ -1,3 +1,4 @@
|
||||
import { setImmediate } from "node:timers/promises";
|
||||
import { INSPECT_ANALYSIS_VIEW_OPERATION } from "../../domain/analysisView/analysisViewErrors.js";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -6,7 +7,10 @@ import type { EvidenceLookup } from "../EvidenceInputResolver.js";
|
||||
import { resolveEvidenceInput } from "../EvidenceInputResolver.js";
|
||||
import type { EvidenceWriter } from "../investigation/InvestigationRecordPort.js";
|
||||
import type { AnalysisError } from "../../domain/analysisErrorBase.js";
|
||||
import { AnalysisCapabilityUnavailableError } from "../../domain/analysisErrorCore.js";
|
||||
import {
|
||||
AnalysisCancelledError,
|
||||
AnalysisCapabilityUnavailableError,
|
||||
} from "../../domain/analysisErrorCore.js";
|
||||
import { analysisInputErrorFromIssues } from "../../domain/inputIssueProjection.js";
|
||||
import { EvidenceIntegrityError } from "../../domain/evidenceErrors.js";
|
||||
import {
|
||||
@@ -29,6 +33,10 @@ import { err, ok, type Result } from "../../domain/result.js";
|
||||
import { WebNetworkApiSurfaceAccumulator } from "../../domain/analysisView/WebNetworkApiSurfaceAccumulator.js";
|
||||
import { EnvironmentManifestAccumulator } from "../../domain/analysisView/EnvironmentManifestAccumulator.js";
|
||||
import { admitAnalysisViewData } from "../../domain/analysisView/analysisViewDataAdmission.js";
|
||||
import {
|
||||
readEvidenceFileSource,
|
||||
type EvidenceFileReadOptions,
|
||||
} from "./evidenceFileSource.js";
|
||||
|
||||
/** Authenticate a selected-view request and project already completed Evidence. */
|
||||
export const inspectAnalysisView = (
|
||||
@@ -48,6 +56,58 @@ export const inspectAnalysisView = (
|
||||
return inspectAnalysisViewValidated(parsed.data, lookup);
|
||||
};
|
||||
|
||||
/** Validate a request whose source may be an Evidence file, then project it. */
|
||||
export const inspectAnalysisViewWithFiles = async (
|
||||
rawInput: unknown,
|
||||
lookup?: EvidenceLookup,
|
||||
options: EvidenceFileReadOptions = {},
|
||||
): Promise<Result<Evidence, AnalysisError>> => {
|
||||
const parsed = inspectAnalysisViewInputSchema.safeParse(rawInput);
|
||||
if (!parsed.success)
|
||||
return err(
|
||||
analysisInputErrorFromIssues(
|
||||
INSPECT_ANALYSIS_VIEW_OPERATION,
|
||||
parsed.error.issues,
|
||||
rawInput,
|
||||
{ cause: parsed.error },
|
||||
),
|
||||
);
|
||||
return inspectAnalysisViewValidatedWithFiles(parsed.data, lookup, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Read and authenticate an Evidence-file source, then project it as retained
|
||||
* Evidence: its strings are bounded by the file reader rather than charged as
|
||||
* inline input. Other sources project unchanged.
|
||||
*/
|
||||
export const inspectAnalysisViewValidatedWithFiles = async (
|
||||
input: InspectAnalysisViewInput,
|
||||
lookup?: EvidenceLookup,
|
||||
options: EvidenceFileReadOptions = {},
|
||||
): Promise<Result<Evidence, AnalysisError>> => {
|
||||
if (input.source.kind !== "evidence-file")
|
||||
return inspectAnalysisViewValidated(input, lookup);
|
||||
const loaded = await readEvidenceFileSource(input.source.path, options);
|
||||
// Authentication and projection are synchronous; yield so a pending abort is dispatched before each check.
|
||||
const cancelled = async (): Promise<boolean> => {
|
||||
await setImmediate();
|
||||
return options.signal?.aborted === true;
|
||||
};
|
||||
// A request cancelled during authentication reports cancellation, even when authentication failed.
|
||||
if (await cancelled())
|
||||
return err(new AnalysisCancelledError(INSPECT_ANALYSIS_VIEW_OPERATION));
|
||||
if (!loaded.ok) return loaded;
|
||||
const id = loaded.value.evidence_id;
|
||||
const projected = inspectAnalysisViewValidated(
|
||||
{ ...input, source: { kind: "retained-evidence", evidence_id: id } },
|
||||
(evidenceId) => (evidenceId === id ? loaded.value : lookup?.(evidenceId)),
|
||||
);
|
||||
// A request cancelled during projection delivers and records nothing.
|
||||
return (await cancelled())
|
||||
? err(new AnalysisCancelledError(INSPECT_ANALYSIS_VIEW_OPERATION))
|
||||
: projected;
|
||||
};
|
||||
|
||||
/** Authenticate borrowed inline Evidence after adapter envelope validation. */
|
||||
export const inspectAnalysisViewValidated = (
|
||||
input: InspectAnalysisViewInput,
|
||||
@@ -182,8 +242,28 @@ const resolveAnalysisViewSource = (
|
||||
): Result<Evidence, AnalysisError> => {
|
||||
if (source.kind === "retained-evidence")
|
||||
return resolveEvidenceInput(source, lookup);
|
||||
// Files are read by inspectAnalysisViewValidatedWithFiles, which the CLI and MCP adapters call.
|
||||
if (source.kind === "evidence-file")
|
||||
return err(
|
||||
new AnalysisCapabilityUnavailableError(
|
||||
ANALYSIS_VIEW_PROVIDER.id,
|
||||
INSPECT_ANALYSIS_VIEW_OPERATION,
|
||||
"evidence-file sources are read before projection",
|
||||
),
|
||||
);
|
||||
return authenticateEvidence(source.evidence, ["source", "evidence"], {
|
||||
source,
|
||||
});
|
||||
};
|
||||
|
||||
/** Parse complete Evidence, reporting schema issues under the given request path. */
|
||||
const authenticateEvidence = (
|
||||
value: unknown,
|
||||
path: readonly (string | number)[],
|
||||
input: unknown,
|
||||
): Result<Evidence, AnalysisError> => {
|
||||
try {
|
||||
return ok(parseEvidence(source.evidence));
|
||||
return ok(parseEvidence(value));
|
||||
} catch (cause: unknown) {
|
||||
if (cause instanceof z.ZodError)
|
||||
return err(
|
||||
@@ -191,9 +271,9 @@ const resolveAnalysisViewSource = (
|
||||
INSPECT_ANALYSIS_VIEW_OPERATION,
|
||||
cause.issues.map((issue) => ({
|
||||
...issue,
|
||||
path: ["source", "evidence", ...issue.path],
|
||||
path: [...path, ...issue.path],
|
||||
})),
|
||||
{ source },
|
||||
input,
|
||||
{ cause },
|
||||
),
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user