Skip to content

Commit 522d01a

Browse files
committed
fix(channels): avoid unnecessary bootstrap during message sends
1 parent b821966 commit 522d01a

3 files changed

Lines changed: 29 additions & 9 deletions

File tree

src/infra/outbound/channel-resolution.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,22 @@ describe("outbound channel resolution", () => {
121121
expect(resolveRuntimePluginRegistryMock).not.toHaveBeenCalled();
122122
});
123123

124+
it("returns a bundled plugin without bootstrapping", async () => {
125+
const plugin = { id: "alpha" };
126+
getLoadedChannelPluginMock.mockReturnValue(undefined);
127+
getChannelPluginMock.mockReturnValue(plugin);
128+
const channelResolution = await importChannelResolution("bundled-plugin");
129+
130+
expect(
131+
channelResolution.resolveOutboundChannelPlugin({
132+
channel: "alpha",
133+
cfg: {} as never,
134+
allowBootstrap: true,
135+
}),
136+
).toBe(plugin);
137+
expect(resolveRuntimePluginRegistryMock).not.toHaveBeenCalled();
138+
});
139+
124140
it("falls back to the active registry when getChannelPlugin misses", async () => {
125141
const plugin = { id: "alpha" };
126142
getChannelPluginMock.mockReturnValue(undefined);

src/infra/outbound/channel-resolution.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,13 @@ export function resolveOutboundChannelPlugin(params: {
192192
return directCurrent;
193193
}
194194

195+
const bundledCurrent = resolve();
196+
if (bundledCurrent) {
197+
return bundledCurrent;
198+
}
199+
195200
if (params.allowBootstrap !== true) {
196-
return resolve();
201+
return undefined;
197202
}
198203

199204
maybeBootstrapChannelPlugin({ channel: normalized, cfg: params.cfg });

src/infra/outbound/channel-selection.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,13 @@ function resolveAvailableKnownChannel(params: {
4949
if (!normalized) {
5050
return undefined;
5151
}
52-
// Pass `allowBootstrap: true` so the in-agent message tool path resolves
53-
// outbound channels in processes where channel adapters have not been
54-
// eagerly loaded (e.g. `openclaw agent --local`). In gateway context where
55-
// the channel plugin is already loaded, this is a no-op fast path. Without
56-
// it, `resolveOutboundChannelPlugin` falls through to `getChannelPlugin`
57-
// (metadata-only) and `resolveAvailableKnownChannel` treats the channel as
58-
// unavailable, surfacing as the recurring "Channel is unavailable" error
59-
// on `--local`-routed dispatches that the CLI send-path could deliver to.
52+
// Pass `allowBootstrap: true` so the in-agent message tool path can resolve
53+
// outbound channels in processes where external channel adapters have not
54+
// been eagerly loaded (e.g. `openclaw agent --local`). Already-loaded and
55+
// bundled plugins still resolve through side-effect-free fast paths first.
56+
// Without the bootstrap fallback, official external channels can surface as
57+
// the recurring "Channel is unavailable" error on `--local`-routed
58+
// dispatches that the CLI send-path could deliver to.
6059
// Adjacent to #77254 (cron-announce / final-reply paths); this closes the
6160
// remaining in-agent caller in the same family.
6261
return resolveOutboundChannelPlugin({

0 commit comments

Comments
 (0)