feat(acp): bundle into CLI, on-demand install, S1 safety + evidence (U8)

Wires the ACP runtime plugin into the published CLI (RUNTIME_PLUGIN_IDS in
tsup.config) and the on-demand BUILTIN_PLUGINS catalog (experimental), matching
the untrusted-subprocess security posture. Adds the Risk S1 default-policy
safety: an acpAllowUnrestricted acknowledgement (default false) — without it, a
blanket allow on a sensitive category is escalated to approval rather than
auto-approved under the allow-all default policy, applied in both the permission
floor and fs write gating. Adds docs/acp-contract.md (launch/readiness +
failure taxonomy), a README with the AGENTS.md-required upstream evidence
(SDK repo/docs/release/integrity), a bundle-output test for the staged plugin,
and a @runfusion/fusion minor changeset. Package green at 179 tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-06-03 09:25:56 -07:00
parent d9f1392b86
commit 489a287d6f
16 changed files with 309 additions and 11 deletions

View File

@@ -260,6 +260,23 @@ describe("CLI bundle output", () => {
expect(manifest.name?.length).toBeGreaterThan(0);
});
it("dist/plugins/fusion-plugin-acp-runtime/ is staged with the acp runtime manifest", () => {
const stagedRoot = join(cliRoot, "dist", "plugins", "fusion-plugin-acp-runtime");
const manifestPath = join(stagedRoot, "manifest.json");
expect(existsSync(manifestPath)).toBe(true);
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8")) as {
id?: string;
runtime?: { runtimeId?: string };
};
expect(manifest.id).toBe("fusion-plugin-acp-runtime");
// The runtime is selected by runtimeId; assert it is "acp".
expect(manifest.runtime?.runtimeId).toBe("acp");
expect(existsSync(join(stagedRoot, "bundled.js"))).toBe(true);
// v1 ships no mcp-schema-server.cjs (MCP forwarding deferred, KTD5).
expect(existsSync(join(stagedRoot, "mcp-schema-server.cjs"))).toBe(false);
});
it("pi-claude-cli source imports child process helpers from node:child_process", () => {
const processManagerSource = readFileSync(join(cliRoot, "dist", "pi-claude-cli", "src", "process-manager.ts"), "utf-8");

View File

@@ -57,6 +57,14 @@ export const BUILTIN_PLUGINS: BuiltinPluginCatalogEntry[] = [
path: "./plugins/fusion-plugin-droid-runtime",
experimental: true,
},
{
id: "fusion-plugin-acp-runtime",
name: "ACP Runtime",
description: "Runtime provider that drives any external Agent Client Protocol agent over JSON-RPC/stdio.",
category: "runtime",
path: "./plugins/fusion-plugin-acp-runtime",
experimental: true,
},
{
id: "fusion-plugin-dependency-graph",
name: "Dependency Graph",

View File

@@ -14,6 +14,7 @@ const RUNTIME_PLUGIN_IDS = [
"fusion-plugin-paperclip-runtime",
"fusion-plugin-cursor-runtime",
"fusion-plugin-droid-runtime",
"fusion-plugin-acp-runtime",
] as const;
const RUNTIME_PLUGINS_WITH_MCP_SCHEMA_SERVER = new Set([