openclaw/src/auto-reply/reply/get-reply.reset-hooks-fallb...

154 lines
4.8 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
import type { MsgContext } from "../templating.js";
import { registerGetReplyCommonMocks } from "./get-reply.test-mocks.js";
const mocks = vi.hoisted(() => ({
resolveReplyDirectives: vi.fn(),
handleInlineActions: vi.fn(),
emitResetCommandHooks: vi.fn(),
initSessionState: vi.fn(),
}));
registerGetReplyCommonMocks();
vi.mock("../../link-understanding/apply.js", () => ({
applyLinkUnderstanding: vi.fn(async () => undefined),
}));
vi.mock("../../media-understanding/apply.js", () => ({
applyMediaUnderstanding: vi.fn(async () => undefined),
}));
vi.mock("./commands-core.js", () => ({
emitResetCommandHooks: (...args: unknown[]) => mocks.emitResetCommandHooks(...args),
}));
vi.mock("./get-reply-directives.js", () => ({
resolveReplyDirectives: (...args: unknown[]) => mocks.resolveReplyDirectives(...args),
}));
vi.mock("./get-reply-inline-actions.js", () => ({
handleInlineActions: (...args: unknown[]) => mocks.handleInlineActions(...args),
}));
vi.mock("./session.js", () => ({
initSessionState: (...args: unknown[]) => mocks.initSessionState(...args),
}));
const { getReplyFromConfig } = await import("./get-reply.js");
function buildNativeResetContext(): MsgContext {
return {
Provider: "telegram",
Surface: "telegram",
ChatType: "direct",
Body: "/new",
RawBody: "/new",
CommandBody: "/new",
CommandSource: "native",
CommandAuthorized: true,
SessionKey: "telegram:slash:123",
CommandTargetSessionKey: "agent:main:telegram:direct:123",
From: "telegram:123",
To: "slash:123",
};
}
function createContinueDirectivesResult(resetHookTriggered: boolean) {
return {
kind: "continue" as const,
result: {
commandSource: "/new",
command: {
surface: "telegram",
channel: "telegram",
channelId: "telegram",
ownerList: [],
senderIsOwner: true,
isAuthorizedSender: true,
senderId: "123",
abortKey: "telegram:slash:123",
rawBodyNormalized: "/new",
commandBodyNormalized: "/new",
from: "telegram:123",
to: "slash:123",
resetHookTriggered,
},
allowTextCommands: true,
skillCommands: [],
directives: {},
cleanedBody: "/new",
elevatedEnabled: false,
elevatedAllowed: false,
elevatedFailures: [],
defaultActivation: "always",
resolvedThinkLevel: undefined,
resolvedVerboseLevel: "off",
resolvedReasoningLevel: "off",
resolvedElevatedLevel: "off",
execOverrides: undefined,
blockStreamingEnabled: false,
blockReplyChunking: undefined,
resolvedBlockStreamingBreak: undefined,
provider: "openai",
model: "gpt-4o-mini",
modelState: {
resolveDefaultThinkingLevel: async () => undefined,
},
contextTokens: 0,
inlineStatusRequested: false,
directiveAck: undefined,
perMessageQueueMode: undefined,
perMessageQueueOptions: undefined,
},
};
}
describe("getReplyFromConfig reset-hook fallback", () => {
beforeEach(() => {
mocks.resolveReplyDirectives.mockReset();
mocks.handleInlineActions.mockReset();
mocks.emitResetCommandHooks.mockReset();
mocks.initSessionState.mockReset();
mocks.initSessionState.mockResolvedValue({
sessionCtx: buildNativeResetContext(),
sessionEntry: {},
previousSessionEntry: {},
sessionStore: {},
sessionKey: "agent:main:telegram:direct:123",
sessionId: "session-1",
isNewSession: true,
resetTriggered: true,
systemSent: false,
abortedLastRun: false,
storePath: "/tmp/sessions.json",
sessionScope: "per-sender",
groupResolution: undefined,
isGroup: false,
triggerBodyNormalized: "/new",
bodyStripped: "",
});
mocks.resolveReplyDirectives.mockResolvedValue(createContinueDirectivesResult(false));
});
it("emits reset hooks when inline actions return early without marking resetHookTriggered", async () => {
mocks.handleInlineActions.mockResolvedValue({ kind: "reply", reply: undefined });
await getReplyFromConfig(buildNativeResetContext(), undefined, {});
expect(mocks.emitResetCommandHooks).toHaveBeenCalledTimes(1);
expect(mocks.emitResetCommandHooks).toHaveBeenCalledWith(
expect.objectContaining({
action: "new",
sessionKey: "agent:main:telegram:direct:123",
}),
);
});
it("does not emit fallback hooks when resetHookTriggered is already set", async () => {
mocks.handleInlineActions.mockResolvedValue({ kind: "reply", reply: undefined });
mocks.resolveReplyDirectives.mockResolvedValue(createContinueDirectivesResult(true));
await getReplyFromConfig(buildNativeResetContext(), undefined, {});
expect(mocks.emitResetCommandHooks).not.toHaveBeenCalled();
});
});