- 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>
95 lines
4.0 KiB
TypeScript
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";
|