Files
fusion/plugins/fusion-plugin-acp-runtime/src/index.ts
gsxdsm 5696d4497f fix(review): address PR #1681 feedback (acp.active accuracy + FNXC comments)
- Greptile P2: `acp.active` now reflects the ACTUAL dispatch determinant
  (FUSION_CLAUDE_ACP, which includes the operator force-override), not the
  experimental flag alone — so the status isn't misleading when forced on/off.
- CodeRabbit/Greptile P2: add FNXC:ClaudeAcp comments to the new code blocks
  per the AGENTS.md greppable-comment convention.

Already fixed in the prior commit (daa37d08c): the P1 "sticky env" / latch
(applyClaudeAcpEnable now recomputes each call + FUSION_CLAUDE_ACP_FORCE
override) and the enable->disable-on-same-env regression test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 12:34:44 -07:00

95 lines
4.0 KiB
TypeScript

import { definePlugin } from "@fusion/plugin-sdk";
import type { FusionPlugin, PluginRuntimeFactory, PluginRuntimeManifestMetadata } from "@fusion/plugin-sdk";
import { resolveCliSettings, resolveBundledClaudeBridgeBinary } from "./cli-spawn.js";
import { AcpRuntimeAdapter } from "./runtime-adapter.js";
import { killAllProcesses } from "./process-manager.js";
import { setupHooks, setupManifest } from "./setup.js";
// Reap any live agent subprocesses on hard process exit so none are orphaned
// (KTD4 — the registry SIGKILL is the authoritative no-orphan guarantee). Scoped
// to tracked agent subprocesses only; never touches other processes/ports.
process.on("exit", killAllProcesses);
export const ACP_RUNTIME_ID = "acp";
const ACP_RUNTIME_VERSION = "0.1.0";
export const acpRuntimeMetadata: PluginRuntimeManifestMetadata = {
runtimeId: ACP_RUNTIME_ID,
name: "ACP Runtime",
description: "Drives any external ACP-compatible agent over JSON-RPC/stdio",
version: ACP_RUNTIME_VERSION,
};
export const acpRuntimeFactory: PluginRuntimeFactory = async (ctx) =>
new AcpRuntimeAdapter(ctx.settings as Record<string, unknown> | undefined);
const plugin: FusionPlugin = definePlugin({
manifest: {
id: "fusion-plugin-acp-runtime",
name: "ACP Runtime Plugin",
version: ACP_RUNTIME_VERSION,
description: "Drives any external ACP-compatible agent over JSON-RPC/stdio",
runtime: acpRuntimeMetadata,
},
state: "installed",
hooks: {
onLoad: (ctx) => {
const settings = resolveCliSettings(ctx.settings as Record<string, unknown>);
ctx.logger.info(
// Log the arg COUNT, not values — args can carry inline tokens/secrets.
`ACP Runtime Plugin loaded — binary=${settings.binaryPath} argCount=${settings.args.length} ` +
`fsRead=${settings.fsRead} fsWrite=${settings.fsWrite}`,
);
// Risk S1: the ACP agent is an untrusted subprocess. Acknowledging the
// unrestricted policy disables the per-call approval escalation — warn so
// it is a deliberate, visible choice.
if (settings.allowUnrestricted) {
ctx.logger.warn(
"ACP Runtime: acpAllowUnrestricted is set — sensitive tool calls from the untrusted agent " +
"will be auto-approved under an allow-all policy. Prefer an approval-required policy.",
);
}
// FNXC:ClaudeAcp 2026-06-15-11:40:
// KTD10 (Route A): publish the bundled `claude-code-cli-acp` bridge path
// process-wide so the pi-claude-cli provider's kill-switch can resolve it
// WITHOUT a manual FUSION_CLAUDE_ACP_BRIDGE env var. This only PUBLISHES the
// path — the ACP transport stays OFF until an operator sets
// FUSION_CLAUDE_ACP=1 (the rollout gate). An explicit env override wins, and
// the resolver is identity-pinned to the plugin-owned node_modules/.bin shim
// so a same-named global binary cannot replace the reviewed bridge.
if (!process.env.FUSION_CLAUDE_ACP_BRIDGE) {
const resolved = resolveBundledClaudeBridgeBinary();
if (resolved.kind === "resolved") {
process.env.FUSION_CLAUDE_ACP_BRIDGE = resolved.path;
ctx.logger.info(
"ACP Runtime: published bundled Claude bridge path for Route A " +
"(transport stays off until FUSION_CLAUDE_ACP=1).",
);
} else {
ctx.logger.info(`ACP Runtime: bundled Claude bridge not resolved (${resolved.reason}); Route A unavailable.`);
}
}
},
},
runtime: {
metadata: acpRuntimeMetadata,
factory: acpRuntimeFactory,
},
setup: {
manifest: setupManifest,
hooks: setupHooks,
},
});
export default plugin;
export { AcpRuntimeAdapter };
export { checkSetup, setupHooks, setupManifest, validateBundledBridgeIdentity } from "./setup.js";
export {
CLAUDE_CODE_CLI_ACP_BINARY,
bundledClaudeBridgeBinPath,
resolveBundledClaudeBridgeBinary,
resolveClaudeBridgeAskSettings,
resolveCliSettings,
} from "./cli-spawn.js";
export type { AcpBinaryResolution, AcpCliSettings } from "./cli-spawn.js";