fix: honor acp projector default account

This commit is contained in:
Tak Hoffman 2026-04-03 16:49:12 -05:00
parent 381a865822
commit 01534d9bd5
No known key found for this signature in database
2 changed files with 70 additions and 1 deletions

View File

@ -1033,6 +1033,48 @@ describe("tryDispatchAcpReply", () => {
expect(result?.queuedFinal).toBe(true);
});
it("honors the configured default account for ACP projector chunking when AccountId is omitted", async () => {
setReadyAcpResolution();
const cfg = createAcpTestConfig({
channels: {
discord: {
defaultAccount: "work",
accounts: {
work: {
textChunkLimit: 5,
},
},
},
},
});
managerMocks.runTurn.mockImplementation(
async ({ onEvent }: { onEvent: (event: unknown) => Promise<void> }) => {
await onEvent({ type: "text_delta", text: "abcdef", tag: "agent_message_chunk" });
await onEvent({ type: "done" });
},
);
const { dispatcher } = createDispatcher();
await runDispatch({
bodyForAgent: "reply",
cfg,
dispatcher,
ctxOverrides: {
Provider: "discord",
Surface: "discord",
},
});
expect(dispatcher.sendBlockReply).toHaveBeenNthCalledWith(
1,
expect.objectContaining({ text: "abcde" }),
);
expect(dispatcher.sendBlockReply).toHaveBeenNthCalledWith(
2,
expect.objectContaining({ text: "f" }),
);
});
it("does not add text fallback when final TTS already delivered audio", async () => {
setReadyAcpResolution();
ttsMocks.resolveTtsConfig.mockReturnValue({ mode: "final" });

View File

@ -200,6 +200,28 @@ function hasBoundConversationForSession(params: {
});
}
function resolveDispatchAccountId(params: {
cfg: OpenClawConfig;
channelRaw: string | undefined;
accountIdRaw: string | undefined;
}): string | undefined {
const channel = String(params.channelRaw ?? "")
.trim()
.toLowerCase();
if (!channel) {
return params.accountIdRaw?.trim() || undefined;
}
const explicit = params.accountIdRaw?.trim();
if (explicit) {
return explicit;
}
const channels = params.cfg.channels as Record<string, { defaultAccount?: unknown } | undefined>;
const configuredDefaultAccountId = channels?.[channel]?.defaultAccount;
return typeof configuredDefaultAccountId === "string" && configuredDefaultAccountId.trim()
? configuredDefaultAccountId.trim()
: undefined;
}
export type AcpDispatchAttemptResult = {
queuedFinal: boolean;
counts: Record<ReplyDispatchKind, number>;
@ -394,12 +416,17 @@ export async function tryDispatchAcpReply(params: {
resolveAgentIdFromSessionKey(canonicalSessionKey)
).trim()
: resolveAgentIdFromSessionKey(canonicalSessionKey);
const effectiveDispatchAccountId = resolveDispatchAccountId({
cfg: params.cfg,
channelRaw: params.ctx.OriginatingChannel ?? params.ctx.Surface ?? params.ctx.Provider,
accountIdRaw: params.ctx.AccountId,
});
const projector = createAcpReplyProjector({
cfg: params.cfg,
shouldSendToolSummaries: params.shouldSendToolSummaries,
deliver: delivery.deliver,
provider: params.ctx.Surface ?? params.ctx.Provider,
accountId: params.ctx.AccountId,
accountId: effectiveDispatchAccountId,
});
const acpDispatchStartedAt = Date.now();