fix(outbound): preserve channel registry during provider snapshots
This commit is contained in:
parent
7a6be3d531
commit
0c2ae71366
@ -10,7 +10,7 @@ import { normalizeModelCompat } from "../../src/agents/model-compat.js";
|
||||
import { parseDurationMs } from "../../src/cli/parse-duration.js";
|
||||
import {
|
||||
normalizeSecretInputModeInput,
|
||||
promptSecretRefForOnboarding,
|
||||
promptSecretRefForSetup,
|
||||
resolveSecretInputModeForEnvSelection,
|
||||
} from "../../src/commands/auth-choice.apply-helpers.js";
|
||||
import { buildTokenProfileId, validateAnthropicSetupToken } from "../../src/commands/auth-token.js";
|
||||
@ -144,7 +144,7 @@ async function runAnthropicSetupToken(ctx: ProviderAuthContext): Promise<Provide
|
||||
let token = "";
|
||||
let tokenRef: { source: "env" | "file" | "exec"; provider: string; id: string } | undefined;
|
||||
if (selectedMode === "ref") {
|
||||
const resolved = await promptSecretRefForOnboarding({
|
||||
const resolved = await promptSecretRefForSetup({
|
||||
provider: "anthropic-setup-token",
|
||||
config: ctx.config,
|
||||
prompter: ctx.prompter,
|
||||
|
||||
@ -18,7 +18,10 @@ import {
|
||||
pickGatewaySelfPresence,
|
||||
resolveGatewayProbeAuthResolution,
|
||||
} from "./status.gateway-probe.js";
|
||||
import type { buildChannelsTable, collectChannelStatusIssues } from "./status.scan.runtime.js";
|
||||
import type {
|
||||
buildChannelsTable as buildChannelsTableFn,
|
||||
collectChannelStatusIssues as collectChannelStatusIssuesFn,
|
||||
} from "./status.scan.runtime.js";
|
||||
import { getStatusSummary } from "./status.summary.js";
|
||||
import { getUpdateCheckResult } from "./status.update.js";
|
||||
|
||||
@ -164,9 +167,9 @@ export type StatusScanResult = {
|
||||
gatewayProbe: Awaited<ReturnType<typeof probeGateway>> | null;
|
||||
gatewayReachable: boolean;
|
||||
gatewaySelf: ReturnType<typeof pickGatewaySelfPresence>;
|
||||
channelIssues: ChannelStatusIssues;
|
||||
channelIssues: ReturnType<typeof collectChannelStatusIssuesFn>;
|
||||
agentStatus: Awaited<ReturnType<typeof getAgentLocalStatuses>>;
|
||||
channels: ChannelsTable;
|
||||
channels: Awaited<ReturnType<typeof buildChannelsTableFn>>;
|
||||
summary: Awaited<ReturnType<typeof getStatusSummary>>;
|
||||
memory: MemoryStatusSnapshot | null;
|
||||
memoryPlugin: MemoryPluginStatus;
|
||||
|
||||
@ -133,6 +133,23 @@ describe("outbound channel resolution", () => {
|
||||
expect(loadOpenClawPluginsMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("bootstraps when the active registry has other channels but not the requested one", async () => {
|
||||
const plugin = { id: "telegram" };
|
||||
getChannelPluginMock.mockReturnValueOnce(undefined).mockReturnValueOnce(plugin);
|
||||
getActivePluginRegistryMock.mockReturnValue({
|
||||
channels: [{ plugin: { id: "discord" } }],
|
||||
});
|
||||
const channelResolution = await importChannelResolution("bootstrap-missing-target");
|
||||
|
||||
expect(
|
||||
channelResolution.resolveOutboundChannelPlugin({
|
||||
channel: "telegram",
|
||||
cfg: { channels: {} } as never,
|
||||
}),
|
||||
).toBe(plugin);
|
||||
expect(loadOpenClawPluginsMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("retries bootstrap after a transient load failure", async () => {
|
||||
getChannelPluginMock.mockReturnValue(undefined);
|
||||
loadOpenClawPluginsMock.mockImplementationOnce(() => {
|
||||
|
||||
@ -33,7 +33,10 @@ function maybeBootstrapChannelPlugin(params: {
|
||||
}
|
||||
|
||||
const activeRegistry = getActivePluginRegistry();
|
||||
if ((activeRegistry?.channels?.length ?? 0) > 0) {
|
||||
const activeHasRequestedChannel = activeRegistry?.channels?.some(
|
||||
(entry) => entry?.plugin?.id === params.channel,
|
||||
);
|
||||
if (activeHasRequestedChannel) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -34,6 +34,7 @@ import { createSubsystemLogger } from "../../logging/subsystem.js";
|
||||
import { getAgentScopedMediaLocalRoots } from "../../media/local-roots.js";
|
||||
import { getGlobalHookRunner } from "../../plugins/hook-runner-global.js";
|
||||
import { throwIfAborted } from "./abort.js";
|
||||
import { resolveOutboundChannelPlugin } from "./channel-resolution.js";
|
||||
import { ackDelivery, enqueueDelivery, failDelivery } from "./delivery-queue.js";
|
||||
import type { OutboundIdentity } from "./identity.js";
|
||||
import type { DeliveryMirror } from "./mirror.js";
|
||||
@ -113,6 +114,13 @@ type ChannelHandlerParams = {
|
||||
|
||||
// Channel docking: outbound delivery delegates to plugin.outbound adapters.
|
||||
async function createChannelHandler(params: ChannelHandlerParams): Promise<ChannelHandler> {
|
||||
// Recover channel plugins the same way target resolution does so direct cron
|
||||
// delivery still works when a prior test or lazy path left the active plugin
|
||||
// registry empty.
|
||||
resolveOutboundChannelPlugin({
|
||||
channel: params.channel,
|
||||
cfg: params.cfg,
|
||||
});
|
||||
const outbound = await loadChannelOutboundAdapter(params.channel);
|
||||
const handler = createPluginHandler({ ...params, outbound });
|
||||
if (!handler) {
|
||||
|
||||
@ -38,6 +38,8 @@ describe("resolvePluginProviders", () => {
|
||||
expect.objectContaining({
|
||||
workspaceDir: "/workspace/explicit",
|
||||
env,
|
||||
cache: false,
|
||||
activate: false,
|
||||
}),
|
||||
);
|
||||
});
|
||||
@ -59,6 +61,8 @@ describe("resolvePluginProviders", () => {
|
||||
allow: expect.arrayContaining(["openrouter", "google", "kilocode", "moonshot"]),
|
||||
}),
|
||||
}),
|
||||
cache: false,
|
||||
activate: false,
|
||||
}),
|
||||
);
|
||||
});
|
||||
@ -76,6 +80,8 @@ describe("resolvePluginProviders", () => {
|
||||
allow: expect.arrayContaining(["openai", "moonshot", "zai"]),
|
||||
}),
|
||||
}),
|
||||
cache: false,
|
||||
activate: false,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
@ -35,6 +35,7 @@ const BUNDLED_PROVIDER_ALLOWLIST_COMPAT_PLUGIN_IDS = [
|
||||
"venice",
|
||||
"vercel-ai-gateway",
|
||||
"volcengine",
|
||||
"xai",
|
||||
"vllm",
|
||||
"xiaomi",
|
||||
"zai",
|
||||
@ -142,8 +143,8 @@ export function resolvePluginProviders(params: {
|
||||
workspaceDir: params.workspaceDir,
|
||||
env: params.env,
|
||||
onlyPluginIds: params.onlyPluginIds,
|
||||
activate: params.activate,
|
||||
cache: params.cache,
|
||||
cache: params.cache ?? false,
|
||||
activate: params.activate ?? false,
|
||||
logger: createPluginLoaderLogger(log),
|
||||
});
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ function createChatHeaderState(
|
||||
} = {},
|
||||
): { state: AppViewState; request: ReturnType<typeof vi.fn> } {
|
||||
let currentModel = overrides.model ?? null;
|
||||
let currentModelProvider = currentModel ? "openai" : undefined;
|
||||
let currentModelProvider = currentModel ? "openai" : null;
|
||||
const omitSessionFromList = overrides.omitSessionFromList ?? false;
|
||||
const catalog = overrides.models ?? [
|
||||
{ id: "gpt-5", name: "GPT-5", provider: "openai" },
|
||||
@ -39,7 +39,7 @@ function createChatHeaderState(
|
||||
const nextModel = (params.model as string | null | undefined) ?? null;
|
||||
if (!nextModel) {
|
||||
currentModel = null;
|
||||
currentModelProvider = undefined;
|
||||
currentModelProvider = null;
|
||||
} else {
|
||||
const normalized = nextModel.trim();
|
||||
const slashIndex = normalized.indexOf("/");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user