fix: preserve cli and slack fallback behavior

This commit is contained in:
Peter Steinberger 2026-03-31 20:36:22 +01:00
parent 9d1b443542
commit 64091caf8f
No known key found for this signature in database
5 changed files with 69 additions and 3 deletions

View File

@ -288,6 +288,29 @@ describe("resolveCliBackendConfig claude-cli defaults", () => {
expect(resolved?.config.args).not.toContain("bypassPermissions");
expect(resolved?.config.resumeArgs).not.toContain("bypassPermissions");
});
it("keeps bundle MCP enabled for override-only claude-cli config when the plugin registry is absent", () => {
const registry = createEmptyPluginRegistry();
setActivePluginRegistry(registry);
const cfg = {
agents: {
defaults: {
cliBackends: {
"claude-cli": {
command: "/usr/local/bin/claude",
args: ["-p", "--output-format", "json"],
},
},
},
},
} satisfies OpenClawConfig;
const resolved = resolveCliBackendConfig("claude-cli", cfg);
expect(resolved).not.toBeNull();
expect(resolved?.bundleMcp).toBe(true);
});
});
describe("resolveCliBackendConfig google-gemini-cli defaults", () => {

View File

@ -10,6 +10,13 @@ export type ResolvedCliBackend = {
pluginId?: string;
};
function resolveFallbackBundleMcpCapability(provider: string): boolean {
// Claude CLI consumes explicit MCP config overlays even when the runtime
// plugin registry is not initialized yet (for example direct runner tests or
// narrow non-gateway entrypoints).
return provider === "claude-cli";
}
function normalizeBackendKey(key: string): string {
return normalizeProviderId(key);
}
@ -114,5 +121,9 @@ export function resolveCliBackendConfig(
if (!command) {
return null;
}
return { id: normalized, config: { ...override, command }, bundleMcp: false };
return {
id: normalized,
config: { ...override, command },
bundleMcp: resolveFallbackBundleMcpCapability(normalized),
};
}

View File

@ -142,7 +142,16 @@ async function waitForAcpBackendHealthy(timeoutMs = 60_000): Promise<void> {
const startedAt = Date.now();
while (Date.now() - startedAt < timeoutMs) {
const backend = getAcpRuntimeBackend("acpx");
if (backend && (!backend.healthy || backend.healthy())) {
if (backend?.healthy?.() ?? false) {
return;
}
const runtime = backend?.runtime as { probeAvailability?: () => Promise<void> } | undefined;
if (runtime?.probeAvailability) {
await runtime.probeAvailability().catch(() => {});
if (backend?.healthy?.() ?? false) {
return;
}
} else if (backend && !backend.healthy) {
return;
}
await sleep(250);

View File

@ -76,6 +76,22 @@ describe("generic current-conversation bindings", () => {
).toBeNull();
});
it("keeps Slack current-conversation binding support when the runtime registry is empty", () => {
setActivePluginRegistry(createTestRegistry([]));
expect(
getGenericCurrentConversationBindingCapabilities({
channel: "slack",
accountId: "default",
}),
).toEqual({
adapterAvailable: true,
bindSupported: true,
unbindSupported: true,
placements: ["current"],
});
});
it("reloads persisted bindings after the in-memory cache is cleared", async () => {
const bound = await bindGenericCurrentConversation({
targetSessionKey: "agent:codex:acp:slack-dm",

View File

@ -22,6 +22,7 @@ type PersistedCurrentConversationBindingsFile = {
const CURRENT_BINDINGS_FILE_VERSION = 1;
const CURRENT_BINDINGS_ID_PREFIX = "generic:";
const FALLBACK_CURRENT_CONVERSATION_BINDING_CHANNELS = new Set(["slack"]);
let bindingsLoaded = false;
let persistPromise: Promise<void> = Promise.resolve();
@ -132,7 +133,13 @@ function resolveChannelSupportsCurrentConversationBinding(channel: string): bool
const plugin = getActivePluginChannelRegistry()?.channels.find((entry) =>
matchesPluginId(entry.plugin),
)?.plugin;
return plugin?.conversationBindings?.supportsCurrentConversationBinding === true;
if (plugin?.conversationBindings?.supportsCurrentConversationBinding === true) {
return true;
}
// Slack live/gateway tests intentionally skip channel startup, so there is no
// active runtime plugin snapshot even though the generic current-conversation
// path is still expected to work.
return FALLBACK_CURRENT_CONVERSATION_BINDING_CHANNELS.has(normalized);
}
export function getGenericCurrentConversationBindingCapabilities(params: {