fix: honor acp dispatch default account

This commit is contained in:
Tak Hoffman 2026-04-03 16:26:36 -05:00
parent ee45a59b4e
commit 037da3ce34
No known key found for this signature in database
2 changed files with 94 additions and 4 deletions

View File

@ -811,6 +811,90 @@ describe("tryDispatchAcpReply", () => {
);
});
it("honors the configured default account when checking bound-session identity notices", async () => {
const canonicalSessionKey = "agent:main:main";
managerMocks.resolveSession.mockReturnValue({
kind: "ready",
sessionKey: canonicalSessionKey,
meta: createAcpSessionMeta({
identity: {
state: "pending",
source: "ensure",
lastUpdatedAt: Date.now(),
acpxRecordId: "rec-work",
},
}),
});
bindingServiceMocks.listBySession.mockImplementation((targetSessionKey: string) =>
targetSessionKey === canonicalSessionKey
? [
{
bindingId: "discord:work:thread-1",
targetSessionKey: canonicalSessionKey,
targetKind: "session",
conversation: {
channel: "discord",
accountId: "work",
conversationId: "thread-1",
},
status: "active",
boundAt: 0,
},
]
: [],
);
sessionMetaMocks.readAcpSessionEntry.mockImplementation(
(params: { sessionKey: string; cfg?: OpenClawConfig }) =>
params.sessionKey === canonicalSessionKey
? {
cfg: params.cfg ?? createAcpTestConfig(),
storePath: "/tmp/openclaw-session-store.json",
sessionKey: canonicalSessionKey,
storeSessionKey: canonicalSessionKey,
acp: createAcpSessionMeta({
identity: {
state: "resolved",
source: "status",
lastUpdatedAt: Date.now(),
acpxSessionId: "acpx-work",
},
}),
}
: null,
);
managerMocks.runTurn.mockResolvedValue(undefined);
const { dispatcher } = createDispatcher();
await runDispatch({
bodyForAgent: "test",
dispatcher,
cfg: createAcpTestConfig({
channels: {
discord: {
defaultAccount: "work",
},
},
}),
ctxOverrides: {
Provider: "discord",
Surface: "discord",
},
sessionKeyOverride: canonicalSessionKey,
});
expect(bindingServiceMocks.listBySession).toHaveBeenCalledWith(canonicalSessionKey);
expect(dispatcher.sendFinalReply).toHaveBeenCalledWith(
expect.objectContaining({
text: expect.stringContaining("Session ids resolved."),
}),
);
expect(dispatcher.sendFinalReply).toHaveBeenCalledWith(
expect.objectContaining({
text: expect.stringContaining("acpx session id: acpx-work"),
}),
);
});
it("does not deliver final fallback text when routed block text was already visible", async () => {
setReadyAcpResolution();
ttsMocks.resolveTtsConfig.mockReturnValue({ mode: "final" });

View File

@ -163,6 +163,7 @@ function resolveAcpRequestId(ctx: FinalizedMsgContext): string {
}
function hasBoundConversationForSession(params: {
cfg: OpenClawConfig;
sessionKey: string;
channelRaw: string | undefined;
accountIdRaw: string | undefined;
@ -173,10 +174,14 @@ function hasBoundConversationForSession(params: {
if (!channel) {
return false;
}
const accountId = String(params.accountIdRaw ?? "")
.trim()
.toLowerCase();
const normalizedAccountId = accountId || "default";
const accountId = String(params.accountIdRaw ?? "").trim().toLowerCase();
const channels = params.cfg.channels as Record<string, { defaultAccount?: unknown } | undefined>;
const configuredDefaultAccountId = channels?.[channel]?.defaultAccount;
const normalizedAccountId =
accountId ||
(typeof configuredDefaultAccountId === "string" && configuredDefaultAccountId.trim()
? configuredDefaultAccountId.trim().toLowerCase()
: "default");
const bindingService = getSessionBindingService();
const bindings = bindingService.listBySession(params.sessionKey);
return bindings.some((binding) => {
@ -375,6 +380,7 @@ export async function tryDispatchAcpReply(params: {
identityPendingBeforeTurn &&
(Boolean(params.ctx.MessageThreadId != null && String(params.ctx.MessageThreadId).trim()) ||
hasBoundConversationForSession({
cfg: params.cfg,
sessionKey: canonicalSessionKey,
channelRaw: params.ctx.OriginatingChannel ?? params.ctx.Surface ?? params.ctx.Provider,
accountIdRaw: params.ctx.AccountId,