chore: merge main into the streamable HTTP transport branch

Picks up upstream's test-runner configuration and the command-finalization
test fixes so the fork lane stops failing on unrelated flaky shards.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Edgar ArakelyanandClaude Fable 5.1 committed 2026-10-18 11:58:12 -07:00
commit cfbf595437
135 files changed
+33233 -1087

No files matched your search

+5 -24
View File
@@ -1,28 +1,9 @@
import { ArtifactResourceScope } from "../../dist/artifacts/ArtifactResourceScope.js";
import { AnalysisCleanupLease } from "../../dist/artifacts/AnalysisCleanupLease.js";
import { inspectGhidraInstallation } from "../../dist/ghidra/GhidraInstallation.js";
import { closeVerifierResources } from "./verifier-cleanup.mjs";
import { withVerifierResourceOwner } from "./verifier-file-resources.mjs";
/** Complete one prerequisite inspection and release its exact retained owners. */
export async function inspectVerifierGhidraInstallation(options, host) {
const resources = new ArtifactResourceScope();
const resourceOwner = {
retain(owner, resource) {
resources.retain({
kind: "lease",
resource,
lease: AnalysisCleanupLease.forCloseOwner(owner, resource),
});
},
run: (operation) => resources.run(operation),
};
let primaryError;
try {
return await inspectGhidraInstallation({ ...options, resourceOwner }, host);
} catch (cause) {
primaryError = cause;
throw cause;
} finally {
await closeVerifierResources([() => resources.close()], primaryError);
}
export function inspectVerifierGhidraInstallation(options, host) {
return withVerifierResourceOwner((resourceOwner) =>
inspectGhidraInstallation({ ...options, resourceOwner }, host),
);
}
@@ -0,0 +1,11 @@
import type { validateJadxConfiguration } from "../../src/android/JadxConfiguration.js";
type ConfigurationArguments = Parameters<typeof validateJadxConfiguration>;
/** Validate prerequisites with a real owner, joining cleanup before completion. */
export function validateVerifierJadxConfiguration(
configuration: ConfigurationArguments[0],
environment: ConfigurationArguments[1],
operation: ConfigurationArguments[2],
options?: Omit<ConfigurationArguments[3], "resourceOwner" | "operation">,
): ReturnType<typeof validateJadxConfiguration>;
@@ -0,0 +1,17 @@
import { validateJadxConfiguration } from "../../dist/android/JadxConfiguration.js";
import { withVerifierResourceOwner } from "./verifier-file-resources.mjs";
/** Validate Java and the engine archive before beginning real verification. */
export function validateVerifierJadxConfiguration(
configuration,
environment,
operation,
options = {},
) {
return withVerifierResourceOwner((resourceOwner) =>
validateJadxConfiguration(configuration, environment, operation, {
...options,
resourceOwner,
}),
);
}
@@ -0,0 +1,2 @@
export function isOwnedTestTemporaryName(name: string): boolean;
export function normalizeTestTemporaryPrefix(prefix: string): string;
+18
View File
@@ -0,0 +1,18 @@
const OWNED_ENTRY = /^(?:rea-|bb-session-)/u;
const SAFE_PREFIX = /^[A-Za-z0-9][A-Za-z0-9._-]*-$/u;
/** Recognize test resources independently of their readiness or contents. */
export const isOwnedTestTemporaryName = (name) => OWNED_ENTRY.test(name);
/** Preserve caller labels while making every shared fixture root recognizable. */
export function normalizeTestTemporaryPrefix(prefix) {
if (
typeof prefix !== "string" ||
!SAFE_PREFIX.test(prefix) ||
prefix.length > 80
)
throw new TypeError(
"Test temporary directory prefixes must be safe basenames ending in '-'",
);
return isOwnedTestTemporaryName(prefix) ? prefix : `rea-${prefix}`;
}
@@ -1,4 +1,10 @@
import type { ArtifactResourceScope } from "../../src/artifacts/ArtifactResourceScope.js";
import type { FileCloseResourceOwner } from "../../src/application/AnalysisProvider.js";
/** Complete a prerequisite operation, including retained cleanup on failure. */
export function withVerifierResourceOwner<T>(
operation: (owner: FileCloseResourceOwner) => Promise<T>,
): Promise<T>;
/** Hash one verifier input and transfer failed-close ownership to its scope. */
export function hashVerifierFile(
+27
View File
@@ -1,4 +1,31 @@
import { hashStableFile } from "../../dist/filesystem/StableFileHash.js";
import { ArtifactResourceScope } from "../../dist/artifacts/ArtifactResourceScope.js";
import { AnalysisCleanupLease } from "../../dist/artifacts/AnalysisCleanupLease.js";
import { closeVerifierResources } from "./verifier-cleanup.mjs";
/** Finish a prerequisite probe and release its exact retained cleanup owners. */
export async function withVerifierResourceOwner(operation) {
const resources = new ArtifactResourceScope();
const resourceOwner = {
retain(owner, resource) {
resources.retain({
kind: "lease",
resource,
lease: AnalysisCleanupLease.forCloseOwner(owner, resource),
});
},
run: (work) => resources.run(work),
};
let primaryError;
try {
return await operation(resourceOwner);
} catch (cause) {
primaryError = cause;
throw cause;
} finally {
await closeVerifierResources([() => resources.close()], primaryError);
}
}
/** Hash one verifier input while retaining any descriptor whose close fails. */
export function hashVerifierFile(path, resources) {
+3 -2
View File
@@ -4,15 +4,16 @@ import { dirname, join, relative, resolve } from "node:path";
import { spawnSync } from "node:child_process";
import { fileURLToPath } from "node:url";
import { isOwnedTestTemporaryName } from "./lib/test-temporary-ownership.mjs";
const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const vitestPath = resolve(repositoryRoot, "node_modules/vitest/vitest.mjs");
const OWNED_ENTRY = /^(?:rea-|bb-session-)/u;
const findOwnedEntries = async (root, directory = root) => {
const owned = [];
for (const entry of await readdir(directory, { withFileTypes: true })) {
const path = join(directory, entry.name);
if (OWNED_ENTRY.test(entry.name)) {
if (isOwnedTestTemporaryName(entry.name)) {
owned.push(relative(root, path));
continue;
}
+3 -5
View File
@@ -14,10 +14,8 @@ import { verifyAndroidCliCancellation } from "./cli-cancellation.mjs";
import { verifyAndroidMetadata } from "./metadata.mjs";
import { verifyAndroidClassSearch } from "./class-search.mjs";
import { verifyAndroidFailureRecovery } from "./failure-recovery.mjs";
import {
resolveJadxConfigurationInputs,
validateJadxConfiguration,
} from "../../../dist/android/JadxConfiguration.js";
import { resolveJadxConfigurationInputs } from "../../../dist/android/JadxConfiguration.js";
import { validateVerifierJadxConfiguration } from "../../lib/jadx-configuration-inspection.mjs";
import { verifyWindowsAndroidLifecycle } from "./windows-lifecycle.mjs";
const repository = resolve(dirname(fileURLToPath(import.meta.url)), "../../..");
@@ -43,7 +41,7 @@ const configuration = await resolveJadxConfigurationInputs(
process.env,
"inspect_android_package",
);
await validateJadxConfiguration(
await validateVerifierJadxConfiguration(
configuration,
process.env,
"inspect_android_package",
+44 -12
View File
@@ -17,6 +17,7 @@ import { delimiter, dirname, isAbsolute, join } from "node:path";
import { Client } from "@modelcontextprotocol/client";
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
import { TOOL_CONTRACTS } from "../../../dist/contracts/toolContracts.js";
import { analysisErrorProjectionSchema } from "../../../dist/contracts/errorSchemas.js";
/** Real unusable Java runtimes and selectors must lead to applicable recovery. */
export async function verifyAndroidFailureRecovery({
@@ -103,18 +104,46 @@ export async function verifyAndroidFailureRecovery({
label: "JRE without compiler",
env: { ...environment, JAVA_HOME: jre },
observation: "jdk.compiler",
diagnostics: {
failure_kind: "java_runtime",
failure_code: "runtime_missing",
jdk_compiler: false,
},
message: /full JDK/u,
remediation: /java --list-modules/u,
},
{
label: "missing java",
env: { ...withoutJavaHome, PATH: empty },
observation:
process.platform === "win32" ? "Cannot find java.exe" : "ENOENT",
diagnostics: {
failure_kind: "java_runtime",
failure_code: "runtime_missing",
...(process.platform === "win32"
? {}
: { process_error_code: "ENOENT" }),
},
message:
process.platform === "win32"
? /Cannot find java\.exe/u
: /selected Java runtime/u,
remediation:
process.platform === "win32"
? /JAVA_HOME|PATH/u
: /selected launcher/u,
},
{
label: "invalid engine archive with a working JDK",
env: { ...environment, REA_JADX_MCP_JAR: invalidJar },
observation: "Cannot inspect REA_JADX_MCP_JAR",
jarFailure: true,
diagnostics: {
failure_kind: "jar",
failure_code: "version_unresolved",
},
message: /REA_JADX_MCP_JAR/u,
remediation: /jadx-headless-mcp/u,
forbiddenGuidance: /Select a full JDK/u,
},
];
if (process.platform === "darwin") {
@@ -126,6 +155,12 @@ export async function verifyAndroidFailureRecovery({
label: "macOS Java stub",
env: { ...withoutJavaHome, PATH: "/usr/bin:/bin" },
observation: "Unable to locate a Java Runtime",
diagnostics: {
failure_kind: "java_probe",
failure_code: "runtime_unavailable",
},
message: /selected Java runtime/u,
remediation: /selected launcher/u,
});
}
}
@@ -145,23 +180,20 @@ export async function verifyAndroidFailureRecovery({
assert.fail(`${scenario.label} unexpectedly succeeded`);
} catch (cause) {
assert.equal(cause.code, 1);
failure = JSON.parse(cause.stdout);
failure = analysisErrorProjectionSchema.parse(JSON.parse(cause.stdout));
}
assert.equal(failure.code, "capability_unavailable");
assert.ok(
JSON.stringify(failure).includes(scenario.observation),
JSON.stringify(failure),
);
assert.match(
failure.message,
scenario.jarFailure ? /REA_JADX_MCP_JAR/u : /full JDK/u,
);
assert.match(
failure.remediation.action,
scenario.jarFailure ? /jadx-headless-mcp/u : /java --list-modules/u,
);
if (scenario.jarFailure)
assert.doesNotMatch(failure.message, /Select a full JDK/u);
assert.ok(failure.details.diagnostics);
for (const [key, value] of Object.entries(scenario.diagnostics))
assert.deepEqual(failure.details.diagnostics[key], value);
assert.match(failure.message, scenario.message);
assert.match(failure.remediation.action, scenario.remediation);
if (scenario.forbiddenGuidance)
assert.doesNotMatch(failure.message, scenario.forbiddenGuidance);
assert.doesNotMatch(JSON.stringify(failure), /rea doctor/u);
await verifyAndroidReadiness({
entrypoint,
+4 -6
View File
@@ -13,10 +13,8 @@ import { dirname, join } from "node:path";
import { setTimeout as delay } from "node:timers/promises";
import { Client } from "@modelcontextprotocol/client";
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
import {
resolveJadxConfigurationInputs,
validateJadxConfiguration,
} from "../../../dist/android/JadxConfiguration.js";
import { resolveJadxConfigurationInputs } from "../../../dist/android/JadxConfiguration.js";
import { validateVerifierJadxConfiguration } from "../../lib/jadx-configuration-inspection.mjs";
import { verifyWindowsGuardianIsolation } from "./windows-guardian-isolation.mjs";
/** Verify real Windows cancellation, disconnect and abrupt CLI owner exit. */
@@ -45,7 +43,7 @@ async function verifyJavaSelection(environment) {
environment,
"inspect_android_package",
);
await validateJadxConfiguration(
await validateVerifierJadxConfiguration(
selected,
environment,
"inspect_android_package",
@@ -65,7 +63,7 @@ async function verifyJavaSelection(environment) {
pathEnvironment,
"inspect_android_package",
);
await validateJadxConfiguration(
await validateVerifierJadxConfiguration(
onPath,
pathEnvironment,
"inspect_android_package",