feat(FN-4075): repair room reply session creation

Fixes FN-4075: repairs room reply session creation logic in the chat module, with accompanying tests added to cover the fix.

Fusion-Task-Id: FN-4075
This commit is contained in:
Fusion
2026-05-11 22:38:41 -07:00
committed by gsxdsm
parent 588f2acb0e
commit c6edf5f49f
2 changed files with 73 additions and 11 deletions

View File

@@ -263,6 +263,54 @@ describe("Chat orchestration — rooms (FN-3805..FN-3811 contract)", () => {
); );
}); });
it("passes resolved-session runtime options when generating room replies", async () => {
mockChatStore.listRoomMembers.mockReturnValue([
{ roomId: "room-1", agentId: "agent-a", role: "member", addedAt: "2026-01-01" },
]);
mockAgentStore.listAgents.mockResolvedValue([
{
id: "agent-a",
name: "Alpha",
role: "executor",
runtimeConfig: { model: "anthropic/claude-sonnet-4-5", runtimeHint: "openclaw" },
},
]);
mockAgentStore.getAgent.mockResolvedValue({
id: "agent-a",
name: "Alpha",
role: "executor",
runtimeConfig: { model: "anthropic/claude-sonnet-4-5", runtimeHint: "openclaw" },
});
const createResolvedSession = vi.fn().mockResolvedValue({
session: {
prompt: vi.fn().mockResolvedValue(undefined),
dispose: vi.fn(),
state: {
messages: [{ role: "assistant", content: "Room reply" }],
},
},
});
__setCreateResolvedAgentSession(createResolvedSession as any);
const manager = new ChatManager(mockChatStore as any, "/tmp", mockAgentStore as any);
await manager.sendRoomMessage("room-1", "hello @Alpha");
expect(createResolvedSession).toHaveBeenCalledWith(expect.objectContaining({
sessionPurpose: "heartbeat",
pluginRunner: undefined,
runtimeHint: "openclaw",
cwd: "/tmp",
systemPrompt: expect.any(String),
tools: "coding",
defaultProvider: "anthropic",
defaultModelId: "claude-sonnet-4-5",
}));
expect(createResolvedSession.mock.calls[0]?.[0]).not.toHaveProperty("createFnAgentArgs");
expect(createResolvedSession.mock.calls[0]?.[0]).not.toHaveProperty("resolvedProvider");
expect(createResolvedSession.mock.calls[0]?.[0]).not.toHaveProperty("resolvedModel");
});
it("throws surfaced error when all room responders fail to reply", async () => { it("throws surfaced error when all room responders fail to reply", async () => {
mockChatStore.listRoomMembers.mockReturnValue([ mockChatStore.listRoomMembers.mockReturnValue([
{ roomId: "room-1", agentId: "agent-a", role: "member", addedAt: "2026-01-01" }, { roomId: "room-1", agentId: "agent-a", role: "member", addedAt: "2026-01-01" },

View File

@@ -1015,18 +1015,32 @@ export class ChatManager {
input.content, input.content,
].join("\n\n"); ].join("\n\n");
const responderRuntimeModel = extractRuntimeModel(input.responder.runtimeConfig);
const effectiveModelProvider = input.modelProvider ?? responderRuntimeModel.provider;
const effectiveModelId = input.modelId ?? responderRuntimeModel.modelId;
const chatModelSettings = await this.getChatModelSettings();
const allowFallback = !(input.modelProvider && input.modelId)
&& !(responderRuntimeModel.provider && responderRuntimeModel.modelId);
const resolvedSession = await createResolvedAgentSession({ const resolvedSession = await createResolvedAgentSession({
createFnAgent, sessionPurpose: "heartbeat",
resolvedProvider: input.modelProvider, pluginRunner: this.pluginRunner,
resolvedModel: input.modelId, runtimeHint: extractRuntimeHint(input.responder.runtimeConfig),
defaultModelProvider: input.modelProvider, cwd: this.rootDir,
defaultModelId: input.modelId, systemPrompt,
createFnAgentArgs: { tools: "coding",
rootDir: this.rootDir, ...(effectiveModelProvider && effectiveModelId
modelProvider: input.modelProvider, ? {
modelId: input.modelId, defaultProvider: effectiveModelProvider,
systemPrompt, defaultModelId: effectiveModelId,
}, }
: {}),
...(allowFallback && chatModelSettings.fallbackProvider && chatModelSettings.fallbackModelId
? {
fallbackProvider: chatModelSettings.fallbackProvider,
fallbackModelId: chatModelSettings.fallbackModelId,
}
: {}),
}); });
try { try {