mirror of
https://github.com/morluto/rea.git
synced 2026-11-10 00:36:35 +00:00
* fix: retain address context cleanup failures * fix!: preserve address context input errors BREAKING CHANGE: Strict clients that cached the previous full inspect_address_context output schema must refresh tools/list and accept the typed error field on unavailable facets. * test: use session contract for address context * fix!: preserve address context resource recovery BREAKING CHANGE: Clients validating unavailable context facets against a cached strict full output schema must refresh tools/list to accept resource_constraint errors. * fix: expose authenticated records refused by the evidence ledger (#8426) * fix: preserve completed evidence on retention refusal * fix: preserve context when unretained evidence exceeds delivery limits * fix(mcp): recover oversized context from session evidence * fix(evidence): preserve completed atomic write results * fix: retain completed results when source publication fails * test: cover bounded recovery with unknown retention * fix(evidence): preserve recovery identity and finalization facts * fix(evidence): clarify retention of omitted observations * test(evidence): compare omission guidance with its owner * fix(evidence): preserve completed composed workflows * fix(evidence): clarify unretained recovery export * test(evidence): cover mixed recovery guidance * fix(evidence): preserve caller scope in secondary failures * refactor(evidence): remove superseded cleanup projection * test(evidence): align recovery checks with owners * fix: preserve cleanup on partial recovery limits * test: use owners for diagnostic wording * fix: admit completed analysis before evidence construction * fix: preserve selected paths when reusing evidence * fix(cli): preserve caller path in unretained evidence * fix(evidence): preserve recovery metadata and admission bounds * refactor: consolidate evidence closure imports * style(test): format CLI help default annotations (#9280) * test(mcp): keep recovery checks focused on contract facts * fix(evidence): preserve feature trace replay revision * test: align evidence recovery checks with ownership * test: align partial recovery and cleanup diagnostics * fix(evidence): preserve nested partials during cleanup * fix(evidence): respect outer recovery ownership * fix(capture): discard rejected nested observations * test(frida): assert inline evidence recovery --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
135 lines
4.9 KiB
TypeScript
135 lines
4.9 KiB
TypeScript
import { Client, InMemoryTransport } from "@modelcontextprotocol/client";
|
|
import pino from "pino";
|
|
import { expect, it, onTestFinished } from "vitest";
|
|
import {
|
|
createAnalysisExecution,
|
|
type AnalysisOperationPort,
|
|
} from "../../../src/application/AnalysisProvider.js";
|
|
import { EvidenceLedger } from "../../../src/application/investigation/EvidenceLedger.js";
|
|
import { createEvidence } from "../../../src/domain/evidence.js";
|
|
import { AnalysisResourceConstraintError } from "../../../src/domain/analysisErrorCore.js";
|
|
import { err, ok } from "../../../src/domain/result.js";
|
|
import { EvidenceMcpServer } from "../../../src/server/EvidenceMcpServer.js";
|
|
import { registerEnhancedTools } from "../../../src/server/registerEnhancedTools.js";
|
|
import { withAdmittedAnalysis } from "../../../src/server/analysisAdmission.js";
|
|
import { createToolResultDelivery } from "../../../src/server/toolResult.js";
|
|
import { parseMcpToolError } from "../../fixtures/mcpToolError.js";
|
|
const provider = { id: "fixture", name: "Fixture", version: "1" };
|
|
const source = createEvidence(undefined, provider, {
|
|
operation: "health",
|
|
parameters: {},
|
|
result: "source",
|
|
});
|
|
it.each([
|
|
{ size: 100, denyAdmission: false, ownerKnown: true },
|
|
{ size: 100_000, denyAdmission: false, ownerKnown: true },
|
|
{ size: 100, denyAdmission: true, ownerKnown: true },
|
|
{ size: 100, denyAdmission: false, ownerKnown: false },
|
|
])(
|
|
"preserves capacity error with size $size, denied admission $denyAdmission, and known owner $ownerKnown",
|
|
async ({ size, denyAdmission, ownerKnown }) => {
|
|
const ledger = new EvidenceLedger({ maximumRepresentationBytes: 0 });
|
|
const record = (evidence: typeof source) => ledger.record(evidence);
|
|
const analysis: AnalysisOperationPort = {
|
|
execute: async (operation) => {
|
|
return ok(
|
|
createAnalysisExecution(
|
|
operation === "procedure_address"
|
|
? "0x1000"
|
|
: operation === "address_name"
|
|
? "example"
|
|
: "x".repeat(size),
|
|
provider,
|
|
operation === "procedure_pseudo_code"
|
|
? { sourceEvidence: [source] }
|
|
: {},
|
|
),
|
|
);
|
|
},
|
|
};
|
|
const server = new EvidenceMcpServer(
|
|
{ name: "batch-overflow", version: "1" },
|
|
{},
|
|
record,
|
|
createToolResultDelivery(10 * 1024),
|
|
);
|
|
registerEnhancedTools(server, {
|
|
withAdmittedAnalysis: withAdmittedAnalysis({ kind: "fixed", analysis }),
|
|
logger: pino({ enabled: false }),
|
|
activeTarget: () => undefined,
|
|
analysisProfile: undefined,
|
|
recordEvidence: record,
|
|
...(ownerKnown
|
|
? { evidenceForAnalysis: (id: string) => ledger.forAnalysis(id) }
|
|
: {}),
|
|
...(denyAdmission
|
|
? {
|
|
admitRecovery: () =>
|
|
err(
|
|
new AnalysisResourceConstraintError(
|
|
"batch_decompile",
|
|
"memory",
|
|
"no headroom",
|
|
null,
|
|
),
|
|
),
|
|
}
|
|
: {}),
|
|
evidenceById: ownerKnown ? (id) => ledger.forAnalysis(id) : () => source,
|
|
recordUnknown: undefined,
|
|
allowsSnapshotReplay: undefined,
|
|
recordWorkflowSnapshot: undefined,
|
|
});
|
|
const client = new Client({ name: "batch-overflow-test", version: "1" });
|
|
onTestFinished(async () => {
|
|
await client.close();
|
|
await server.close();
|
|
});
|
|
const [ct, st] = InMemoryTransport.createLinkedPair();
|
|
await server.connect(st);
|
|
await client.connect(ct);
|
|
const response = await client.callTool({
|
|
name: "batch_decompile",
|
|
arguments: { addresses: ["example"] },
|
|
});
|
|
expect(response.isError).toBe(true);
|
|
const error = parseMcpToolError(response).error;
|
|
expect(error).toMatchObject({
|
|
code: "resource_constraint",
|
|
details: { partial_observation: { kind: "unretained-evidence" } },
|
|
});
|
|
if (denyAdmission)
|
|
expect(error.details).toMatchObject({
|
|
completed_observation_omitted: {
|
|
reason: "insufficient-authentication-headroom",
|
|
source_closure: "not-established",
|
|
},
|
|
});
|
|
else if (!ownerKnown)
|
|
expect(error.details).toMatchObject({
|
|
completed_observation: {
|
|
kind: "inline-evidence",
|
|
retention: "unknown",
|
|
source_closure: "not-established",
|
|
evidence: { operation: "batch_decompile" },
|
|
},
|
|
});
|
|
else if (size === 100)
|
|
expect(error.details).toMatchObject({
|
|
completed_observation: {
|
|
kind: "unretained-evidence",
|
|
source_closure: "not-established",
|
|
evidence: { operation: "batch_decompile" },
|
|
},
|
|
});
|
|
else
|
|
expect(error.details).toMatchObject({
|
|
completed_observation_omitted: {
|
|
reason: "mcp-response-limit",
|
|
source_closure: "not-established",
|
|
},
|
|
});
|
|
expect(ledger.statistics().evidenceRecords).toBe(0);
|
|
},
|
|
);
|