Plugins: fail fast on channel and binding collisions (#45628)

* Plugins: reject duplicate channel ids

* Bindings: reject duplicate adapter registration

* Plugins: fail on export id mismatch
This commit is contained in:
Vincent Koc
2026-03-13 19:13:35 -07:00
committed by GitHub
parent 27e863ce40
commit bcbfbb831e
5 changed files with 140 additions and 11 deletions

View File

@@ -198,4 +198,24 @@ describe("session binding service", () => {
placements: [],
});
});
it("rejects duplicate adapter registration for the same channel account", () => {
registerSessionBindingAdapter({
channel: "discord",
accountId: "default",
bind: async (input) => createRecord(input),
listBySession: () => [],
resolveByConversation: () => null,
});
expect(() =>
registerSessionBindingAdapter({
channel: "Discord",
accountId: "DEFAULT",
bind: async (input) => createRecord(input),
listBySession: () => [],
resolveByConversation: () => null,
}),
).toThrow("Session binding adapter already registered for discord:default");
});
});

View File

@@ -148,15 +148,22 @@ function resolveAdapterCapabilities(
const ADAPTERS_BY_CHANNEL_ACCOUNT = new Map<string, SessionBindingAdapter>();
export function registerSessionBindingAdapter(adapter: SessionBindingAdapter): void {
const key = toAdapterKey({
channel: adapter.channel,
accountId: adapter.accountId,
});
ADAPTERS_BY_CHANNEL_ACCOUNT.set(key, {
const normalizedAdapter = {
...adapter,
channel: adapter.channel.trim().toLowerCase(),
accountId: normalizeAccountId(adapter.accountId),
};
const key = toAdapterKey({
channel: normalizedAdapter.channel,
accountId: normalizedAdapter.accountId,
});
const existing = ADAPTERS_BY_CHANNEL_ACCOUNT.get(key);
if (existing && existing !== adapter) {
throw new Error(
`Session binding adapter already registered for ${normalizedAdapter.channel}:${normalizedAdapter.accountId}`,
);
}
ADAPTERS_BY_CHANNEL_ACCOUNT.set(key, normalizedAdapter);
}
export function unregisterSessionBindingAdapter(params: {