mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-08 00:16:24 +00:00
Add reasoningDefault support under agents.defaults and preserve the existing per-agent/session/inline override order. Includes authorization gating for configured reasoning state, /status coverage, config schema/docs baseline updates, and regression tests for the reply and status paths. Also carries the related cron startup-run preservation fix and CI test stabilization needed for this PR branch. Validated locally with pnpm check:changed, the focused Vitest bundle for touched gateway/cron/auto-reply/plugin-sdk/tooling tests, pnpm config:docs:check, and git diff --check. GitHub checks are green on the merged head; Greptile latest visible review is 4/5 with no P0/P1 findings.
253 lines
6.9 KiB
TypeScript
253 lines
6.9 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const buildStatusReply = vi.fn(async (params: unknown) => params);
|
|
const loadSessionEntry = vi.fn();
|
|
const resolveSessionAgentId = vi.fn();
|
|
const listAgentEntries = vi.fn();
|
|
const resolveDefaultModelForAgent = vi.fn();
|
|
const resolveDefaultModel = vi.fn();
|
|
const createModelSelectionState = vi.fn();
|
|
const resolveCurrentDirectiveLevels = vi.fn();
|
|
|
|
vi.mock("../auto-reply/reply/commands-status.js", () => ({
|
|
buildStatusReply,
|
|
}));
|
|
|
|
vi.mock("../gateway/session-utils.js", () => ({
|
|
loadSessionEntry,
|
|
}));
|
|
|
|
vi.mock("../agents/agent-scope.js", () => ({
|
|
listAgentEntries,
|
|
resolveSessionAgentId,
|
|
}));
|
|
|
|
vi.mock("../agents/model-selection.js", () => ({
|
|
resolveDefaultModelForAgent,
|
|
}));
|
|
|
|
vi.mock("../auto-reply/reply/directive-handling.defaults.js", () => ({
|
|
resolveDefaultModel,
|
|
}));
|
|
|
|
vi.mock("../auto-reply/reply/model-selection.js", () => ({
|
|
createModelSelectionState,
|
|
}));
|
|
|
|
vi.mock("../auto-reply/reply/directive-handling.levels.js", () => ({
|
|
resolveCurrentDirectiveLevels,
|
|
}));
|
|
|
|
const { resolveDirectStatusReplyForSession } = await import("./command-status.runtime.js");
|
|
|
|
describe("resolveDirectStatusReplyForSession", () => {
|
|
beforeEach(() => {
|
|
buildStatusReply.mockReset();
|
|
loadSessionEntry.mockReset();
|
|
resolveSessionAgentId.mockReset();
|
|
listAgentEntries.mockReset();
|
|
resolveDefaultModelForAgent.mockReset();
|
|
resolveDefaultModel.mockReset();
|
|
createModelSelectionState.mockReset();
|
|
resolveCurrentDirectiveLevels.mockReset();
|
|
|
|
buildStatusReply.mockImplementation(async (params: unknown) => params);
|
|
loadSessionEntry.mockReturnValue({
|
|
cfg: {
|
|
agents: {
|
|
defaults: {
|
|
reasoningDefault: "off",
|
|
},
|
|
},
|
|
},
|
|
canonicalKey: "main",
|
|
entry: {
|
|
sessionId: "sess-main",
|
|
},
|
|
store: {},
|
|
storePath: "/tmp/sessions.json",
|
|
});
|
|
resolveSessionAgentId.mockReturnValue("main");
|
|
listAgentEntries.mockReturnValue([]);
|
|
resolveDefaultModelForAgent.mockReturnValue({ provider: "openai", model: "gpt-5.4" });
|
|
resolveDefaultModel.mockReturnValue({ defaultProvider: "openai", defaultModel: "gpt-5.4" });
|
|
createModelSelectionState.mockResolvedValue({
|
|
resolveDefaultThinkingLevel: vi.fn(async () => "off"),
|
|
resolveDefaultReasoningLevel: vi.fn(async () => "on"),
|
|
});
|
|
resolveCurrentDirectiveLevels.mockResolvedValue({
|
|
currentThinkLevel: "off",
|
|
currentFastMode: false,
|
|
currentVerboseLevel: "off",
|
|
currentReasoningLevel: "off",
|
|
currentElevatedLevel: "off",
|
|
});
|
|
});
|
|
|
|
it("treats agentCfg reasoningDefault as explicit for direct /status", async () => {
|
|
const result = await resolveDirectStatusReplyForSession({
|
|
cfg: {},
|
|
sessionKey: "main",
|
|
channel: "cli",
|
|
senderIsOwner: true,
|
|
isAuthorizedSender: true,
|
|
isGroup: false,
|
|
defaultGroupActivation: () => "always",
|
|
});
|
|
|
|
expect(buildStatusReply).toHaveBeenCalledOnce();
|
|
expect(buildStatusReply.mock.calls[0]?.[0]).toMatchObject({
|
|
resolvedReasoningLevel: "off",
|
|
});
|
|
expect(result).toMatchObject({
|
|
resolvedReasoningLevel: "off",
|
|
});
|
|
});
|
|
|
|
it("allows configured reasoning defaults for authorized direct /status senders", async () => {
|
|
loadSessionEntry.mockReturnValue({
|
|
cfg: {
|
|
agents: {
|
|
defaults: {
|
|
reasoningDefault: "stream",
|
|
},
|
|
},
|
|
},
|
|
canonicalKey: "main",
|
|
entry: {
|
|
sessionId: "sess-main",
|
|
},
|
|
store: {},
|
|
storePath: "/tmp/sessions.json",
|
|
});
|
|
resolveCurrentDirectiveLevels.mockResolvedValueOnce({
|
|
currentThinkLevel: "off",
|
|
currentFastMode: false,
|
|
currentVerboseLevel: "off",
|
|
currentReasoningLevel: "stream",
|
|
currentElevatedLevel: "off",
|
|
});
|
|
|
|
const result = await resolveDirectStatusReplyForSession({
|
|
cfg: {},
|
|
sessionKey: "main",
|
|
channel: "cli",
|
|
senderIsOwner: false,
|
|
isAuthorizedSender: true,
|
|
isGroup: false,
|
|
defaultGroupActivation: () => "always",
|
|
});
|
|
|
|
expect(result).toMatchObject({
|
|
resolvedReasoningLevel: "stream",
|
|
});
|
|
});
|
|
|
|
it("hides configured reasoning defaults from unauthorized direct /status senders", async () => {
|
|
loadSessionEntry.mockReturnValue({
|
|
cfg: {
|
|
agents: {
|
|
defaults: {
|
|
reasoningDefault: "stream",
|
|
},
|
|
},
|
|
},
|
|
canonicalKey: "main",
|
|
entry: {
|
|
sessionId: "sess-main",
|
|
},
|
|
store: {},
|
|
storePath: "/tmp/sessions.json",
|
|
});
|
|
resolveCurrentDirectiveLevels.mockResolvedValueOnce({
|
|
currentThinkLevel: "off",
|
|
currentFastMode: false,
|
|
currentVerboseLevel: "off",
|
|
currentReasoningLevel: "stream",
|
|
currentElevatedLevel: "off",
|
|
});
|
|
|
|
const result = await resolveDirectStatusReplyForSession({
|
|
cfg: {},
|
|
sessionKey: "main",
|
|
channel: "cli",
|
|
senderIsOwner: false,
|
|
isAuthorizedSender: false,
|
|
isGroup: false,
|
|
defaultGroupActivation: () => "always",
|
|
});
|
|
|
|
expect(result).toMatchObject({
|
|
resolvedReasoningLevel: "off",
|
|
});
|
|
});
|
|
|
|
it("hides session reasoning state from unauthorized direct /status senders", async () => {
|
|
loadSessionEntry.mockReturnValue({
|
|
cfg: {},
|
|
canonicalKey: "main",
|
|
entry: {
|
|
sessionId: "sess-main",
|
|
reasoningLevel: "stream",
|
|
},
|
|
store: {},
|
|
storePath: "/tmp/sessions.json",
|
|
});
|
|
resolveCurrentDirectiveLevels.mockResolvedValueOnce({
|
|
currentThinkLevel: "off",
|
|
currentFastMode: false,
|
|
currentVerboseLevel: "off",
|
|
currentReasoningLevel: "stream",
|
|
currentElevatedLevel: "off",
|
|
});
|
|
|
|
const result = await resolveDirectStatusReplyForSession({
|
|
cfg: {},
|
|
sessionKey: "main",
|
|
channel: "cli",
|
|
senderIsOwner: false,
|
|
isAuthorizedSender: false,
|
|
isGroup: false,
|
|
defaultGroupActivation: () => "always",
|
|
});
|
|
|
|
expect(result).toMatchObject({
|
|
resolvedReasoningLevel: "off",
|
|
});
|
|
});
|
|
|
|
it("allows session reasoning state for authorized direct /status senders", async () => {
|
|
loadSessionEntry.mockReturnValue({
|
|
cfg: {},
|
|
canonicalKey: "main",
|
|
entry: {
|
|
sessionId: "sess-main",
|
|
reasoningLevel: "stream",
|
|
},
|
|
store: {},
|
|
storePath: "/tmp/sessions.json",
|
|
});
|
|
resolveCurrentDirectiveLevels.mockResolvedValueOnce({
|
|
currentThinkLevel: "off",
|
|
currentFastMode: false,
|
|
currentVerboseLevel: "off",
|
|
currentReasoningLevel: "stream",
|
|
currentElevatedLevel: "off",
|
|
});
|
|
|
|
const result = await resolveDirectStatusReplyForSession({
|
|
cfg: {},
|
|
sessionKey: "main",
|
|
channel: "cli",
|
|
senderIsOwner: false,
|
|
isAuthorizedSender: true,
|
|
isGroup: false,
|
|
defaultGroupActivation: () => "always",
|
|
});
|
|
|
|
expect(result).toMatchObject({
|
|
resolvedReasoningLevel: "stream",
|
|
});
|
|
});
|
|
});
|