mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:00:43 +00:00
test: share auto-reply command fixtures
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { TemplateContext } from "../templating.js";
|
||||
import type { FollowupRun, QueueSettings } from "./queue.js";
|
||||
import { createTestFollowupRun } from "./agent-runner.test-fixtures.js";
|
||||
import type { QueueSettings } from "./queue.js";
|
||||
import { createMockTypingController } from "./test-helpers.js";
|
||||
|
||||
const freshCfg = { runtimeFresh: true };
|
||||
@@ -56,6 +57,56 @@ vi.mock("./queue.js", async () => {
|
||||
|
||||
const { runReplyAgent } = await import("./agent-runner.js");
|
||||
|
||||
function createTelegramSessionCtx(): TemplateContext {
|
||||
return {
|
||||
Provider: "telegram",
|
||||
OriginatingChannel: "telegram",
|
||||
OriginatingTo: "12345",
|
||||
AccountId: "default",
|
||||
ChatType: "dm",
|
||||
MessageSid: "msg-1",
|
||||
} as unknown as TemplateContext;
|
||||
}
|
||||
|
||||
function createDirectRuntimeReplyParams({
|
||||
shouldFollowup,
|
||||
isActive,
|
||||
}: {
|
||||
shouldFollowup: boolean;
|
||||
isActive: boolean;
|
||||
}) {
|
||||
const followupRun = createTestFollowupRun({
|
||||
sessionId: "session-1",
|
||||
sessionKey: "agent:main:telegram:default:direct:test",
|
||||
messageProvider: "telegram",
|
||||
config: staleCfg,
|
||||
provider: "openai",
|
||||
model: "gpt-5.4",
|
||||
});
|
||||
const resolvedQueue = { mode: "interrupt" } as QueueSettings;
|
||||
const replyParams: Parameters<typeof runReplyAgent>[0] = {
|
||||
commandBody: "hello",
|
||||
followupRun,
|
||||
queueKey: "main",
|
||||
resolvedQueue,
|
||||
shouldSteer: false,
|
||||
shouldFollowup,
|
||||
isActive,
|
||||
isStreaming: false,
|
||||
typing: createMockTypingController(),
|
||||
sessionCtx: createTelegramSessionCtx(),
|
||||
defaultModel: "openai/gpt-5.4",
|
||||
resolvedVerboseLevel: "off",
|
||||
isNewSession: false,
|
||||
blockStreamingEnabled: false,
|
||||
resolvedBlockStreamingBreak: "message_end",
|
||||
shouldInjectGroupIntro: false,
|
||||
typingMode: "instant",
|
||||
};
|
||||
|
||||
return { followupRun, resolvedQueue, replyParams };
|
||||
}
|
||||
|
||||
describe("runReplyAgent runtime config", () => {
|
||||
beforeEach(() => {
|
||||
resolveQueuedReplyExecutionConfigMock.mockReset();
|
||||
@@ -75,65 +126,12 @@ describe("runReplyAgent runtime config", () => {
|
||||
});
|
||||
|
||||
it("resolves direct reply runs before early helpers read config", async () => {
|
||||
const followupRun = {
|
||||
prompt: "hello",
|
||||
summaryLine: "hello",
|
||||
enqueuedAt: Date.now(),
|
||||
run: {
|
||||
sessionId: "session-1",
|
||||
sessionKey: "agent:main:telegram:default:direct:test",
|
||||
messageProvider: "telegram",
|
||||
sessionFile: "/tmp/session.jsonl",
|
||||
workspaceDir: "/tmp",
|
||||
config: staleCfg,
|
||||
skillsSnapshot: {},
|
||||
provider: "openai",
|
||||
model: "gpt-5.4",
|
||||
thinkLevel: "low",
|
||||
verboseLevel: "off",
|
||||
elevatedLevel: "off",
|
||||
bashElevated: {
|
||||
enabled: false,
|
||||
allowed: false,
|
||||
defaultLevel: "off",
|
||||
},
|
||||
timeoutMs: 1_000,
|
||||
blockReplyBreak: "message_end",
|
||||
},
|
||||
} as unknown as FollowupRun;
|
||||
const { followupRun, replyParams } = createDirectRuntimeReplyParams({
|
||||
shouldFollowup: false,
|
||||
isActive: false,
|
||||
});
|
||||
|
||||
const resolvedQueue = { mode: "interrupt" } as QueueSettings;
|
||||
const typing = createMockTypingController();
|
||||
const sessionCtx = {
|
||||
Provider: "telegram",
|
||||
OriginatingChannel: "telegram",
|
||||
OriginatingTo: "12345",
|
||||
AccountId: "default",
|
||||
ChatType: "dm",
|
||||
MessageSid: "msg-1",
|
||||
} as unknown as TemplateContext;
|
||||
|
||||
await expect(
|
||||
runReplyAgent({
|
||||
commandBody: "hello",
|
||||
followupRun,
|
||||
queueKey: "main",
|
||||
resolvedQueue,
|
||||
shouldSteer: false,
|
||||
shouldFollowup: false,
|
||||
isActive: false,
|
||||
isStreaming: false,
|
||||
typing,
|
||||
sessionCtx,
|
||||
defaultModel: "openai/gpt-5.4",
|
||||
resolvedVerboseLevel: "off",
|
||||
isNewSession: false,
|
||||
blockStreamingEnabled: false,
|
||||
resolvedBlockStreamingBreak: "message_end",
|
||||
shouldInjectGroupIntro: false,
|
||||
typingMode: "instant",
|
||||
}),
|
||||
).rejects.toBe(sentinelError);
|
||||
await expect(runReplyAgent(replyParams)).rejects.toBe(sentinelError);
|
||||
|
||||
expect(followupRun.run.config).toBe(freshCfg);
|
||||
expect(resolveQueuedReplyExecutionConfigMock).toHaveBeenCalledWith(
|
||||
@@ -167,65 +165,12 @@ describe("runReplyAgent runtime config", () => {
|
||||
});
|
||||
|
||||
it("does not resolve secrets before the enqueue-followup queue path", async () => {
|
||||
const followupRun = {
|
||||
prompt: "hello",
|
||||
summaryLine: "hello",
|
||||
enqueuedAt: Date.now(),
|
||||
run: {
|
||||
sessionId: "session-1",
|
||||
sessionKey: "agent:main:telegram:default:direct:test",
|
||||
messageProvider: "telegram",
|
||||
sessionFile: "/tmp/session.jsonl",
|
||||
workspaceDir: "/tmp",
|
||||
config: staleCfg,
|
||||
skillsSnapshot: {},
|
||||
provider: "openai",
|
||||
model: "gpt-5.4",
|
||||
thinkLevel: "low",
|
||||
verboseLevel: "off",
|
||||
elevatedLevel: "off",
|
||||
bashElevated: {
|
||||
enabled: false,
|
||||
allowed: false,
|
||||
defaultLevel: "off",
|
||||
},
|
||||
timeoutMs: 1_000,
|
||||
blockReplyBreak: "message_end",
|
||||
},
|
||||
} as unknown as FollowupRun;
|
||||
const { followupRun, resolvedQueue, replyParams } = createDirectRuntimeReplyParams({
|
||||
shouldFollowup: true,
|
||||
isActive: true,
|
||||
});
|
||||
|
||||
const resolvedQueue = { mode: "interrupt" } as QueueSettings;
|
||||
const typing = createMockTypingController();
|
||||
const sessionCtx = {
|
||||
Provider: "telegram",
|
||||
OriginatingChannel: "telegram",
|
||||
OriginatingTo: "12345",
|
||||
AccountId: "default",
|
||||
ChatType: "dm",
|
||||
MessageSid: "msg-1",
|
||||
} as unknown as TemplateContext;
|
||||
|
||||
await expect(
|
||||
runReplyAgent({
|
||||
commandBody: "hello",
|
||||
followupRun,
|
||||
queueKey: "main",
|
||||
resolvedQueue,
|
||||
shouldSteer: false,
|
||||
shouldFollowup: true,
|
||||
isActive: true,
|
||||
isStreaming: false,
|
||||
typing,
|
||||
sessionCtx,
|
||||
defaultModel: "openai/gpt-5.4",
|
||||
resolvedVerboseLevel: "off",
|
||||
isNewSession: false,
|
||||
blockStreamingEnabled: false,
|
||||
resolvedBlockStreamingBreak: "message_end",
|
||||
shouldInjectGroupIntro: false,
|
||||
typingMode: "instant",
|
||||
}),
|
||||
).resolves.toBeUndefined();
|
||||
await expect(runReplyAgent(replyParams)).resolves.toBeUndefined();
|
||||
|
||||
expect(resolveQueuedReplyExecutionConfigMock).not.toHaveBeenCalled();
|
||||
expect(enqueueFollowupRunMock).toHaveBeenCalledWith(
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
} from "../../plugins/memory-state.js";
|
||||
import type { TemplateContext } from "../templating.js";
|
||||
import { runMemoryFlushIfNeeded, setAgentRunnerMemoryTestDeps } from "./agent-runner-memory.js";
|
||||
import type { FollowupRun } from "./queue.js";
|
||||
import { createTestFollowupRun, writeTestSessionStore } from "./agent-runner.test-fixtures.js";
|
||||
|
||||
const runWithModelFallbackMock = vi.fn();
|
||||
const runEmbeddedPiAgentMock = vi.fn();
|
||||
@@ -24,44 +24,6 @@ function createReplyOperation() {
|
||||
} as never;
|
||||
}
|
||||
|
||||
function createFollowupRun(overrides: Partial<FollowupRun["run"]> = {}): FollowupRun {
|
||||
return {
|
||||
prompt: "hello",
|
||||
summaryLine: "hello",
|
||||
enqueuedAt: Date.now(),
|
||||
run: {
|
||||
agentId: "main",
|
||||
agentDir: "/tmp/agent",
|
||||
sessionId: "session",
|
||||
sessionKey: "main",
|
||||
messageProvider: "whatsapp",
|
||||
sessionFile: "/tmp/session.jsonl",
|
||||
workspaceDir: "/tmp",
|
||||
config: {},
|
||||
skillsSnapshot: {},
|
||||
provider: "anthropic",
|
||||
model: "claude",
|
||||
thinkLevel: "low",
|
||||
verboseLevel: "off",
|
||||
elevatedLevel: "off",
|
||||
bashElevated: { enabled: false, allowed: false, defaultLevel: "off" },
|
||||
timeoutMs: 1_000,
|
||||
blockReplyBreak: "message_end",
|
||||
skipProviderRuntimeHints: true,
|
||||
...overrides,
|
||||
},
|
||||
} as unknown as FollowupRun;
|
||||
}
|
||||
|
||||
async function writeSessionStore(
|
||||
storePath: string,
|
||||
sessionKey: string,
|
||||
entry: SessionEntry,
|
||||
): Promise<void> {
|
||||
await fs.mkdir(path.dirname(storePath), { recursive: true });
|
||||
await fs.writeFile(storePath, JSON.stringify({ [sessionKey]: entry }, null, 2), "utf8");
|
||||
}
|
||||
|
||||
describe("runMemoryFlushIfNeeded", () => {
|
||||
let rootDir = "";
|
||||
|
||||
@@ -100,7 +62,7 @@ describe("runMemoryFlushIfNeeded", () => {
|
||||
}
|
||||
params.sessionStore[sessionKey] = nextEntry;
|
||||
if (typeof params.storePath === "string") {
|
||||
await writeSessionStore(params.storePath, sessionKey, nextEntry);
|
||||
await writeTestSessionStore(params.storePath, sessionKey, nextEntry);
|
||||
}
|
||||
return nextEntry.compactionCount;
|
||||
});
|
||||
@@ -131,7 +93,7 @@ describe("runMemoryFlushIfNeeded", () => {
|
||||
compactionCount: 1,
|
||||
};
|
||||
const sessionStore = { [sessionKey]: sessionEntry };
|
||||
await writeSessionStore(storePath, sessionKey, sessionEntry);
|
||||
await writeTestSessionStore(storePath, sessionKey, sessionEntry);
|
||||
|
||||
runEmbeddedPiAgentMock.mockImplementationOnce(
|
||||
async (params: {
|
||||
@@ -145,7 +107,7 @@ describe("runMemoryFlushIfNeeded", () => {
|
||||
},
|
||||
);
|
||||
|
||||
const followupRun = createFollowupRun();
|
||||
const followupRun = createTestFollowupRun();
|
||||
const entry = await runMemoryFlushIfNeeded({
|
||||
cfg: {
|
||||
agents: {
|
||||
@@ -206,7 +168,7 @@ describe("runMemoryFlushIfNeeded", () => {
|
||||
|
||||
const entry = await runMemoryFlushIfNeeded({
|
||||
cfg: { agents: { defaults: { cliBackends: { "codex-cli": { command: "codex" } } } } },
|
||||
followupRun: createFollowupRun({ provider: "codex-cli" }),
|
||||
followupRun: createTestFollowupRun({ provider: "codex-cli" }),
|
||||
sessionCtx: { Provider: "whatsapp" } as unknown as TemplateContext,
|
||||
defaultModel: "codex-cli/gpt-5.4",
|
||||
agentCfgContextTokens: 100_000,
|
||||
@@ -257,7 +219,7 @@ describe("runMemoryFlushIfNeeded", () => {
|
||||
|
||||
await runMemoryFlushIfNeeded({
|
||||
cfg: { agents: { defaults: { compaction: { memoryFlush: {} } } } },
|
||||
followupRun: createFollowupRun({ extraSystemPrompt: "extra system" }),
|
||||
followupRun: createTestFollowupRun({ extraSystemPrompt: "extra system" }),
|
||||
sessionCtx: { Provider: "whatsapp" } as unknown as TemplateContext,
|
||||
defaultModel: "anthropic/claude-opus-4-6",
|
||||
agentCfgContextTokens: 100_000,
|
||||
|
||||
@@ -7,45 +7,11 @@ import {
|
||||
resetReplyRunSession,
|
||||
setAgentRunnerSessionResetTestDeps,
|
||||
} from "./agent-runner-session-reset.js";
|
||||
import type { FollowupRun } from "./queue.js";
|
||||
import { createTestFollowupRun, writeTestSessionStore } from "./agent-runner.test-fixtures.js";
|
||||
|
||||
const refreshQueuedFollowupSessionMock = vi.fn();
|
||||
const errorMock = vi.fn();
|
||||
|
||||
function createFollowupRun(): FollowupRun {
|
||||
return {
|
||||
prompt: "hello",
|
||||
summaryLine: "hello",
|
||||
enqueuedAt: Date.now(),
|
||||
run: {
|
||||
sessionId: "session",
|
||||
sessionKey: "main",
|
||||
messageProvider: "whatsapp",
|
||||
sessionFile: "/tmp/session.jsonl",
|
||||
workspaceDir: "/tmp",
|
||||
config: {},
|
||||
skillsSnapshot: {},
|
||||
provider: "anthropic",
|
||||
model: "claude",
|
||||
thinkLevel: "low",
|
||||
verboseLevel: "off",
|
||||
elevatedLevel: "off",
|
||||
bashElevated: { enabled: false, allowed: false, defaultLevel: "off" },
|
||||
timeoutMs: 1_000,
|
||||
blockReplyBreak: "message_end",
|
||||
},
|
||||
} as unknown as FollowupRun;
|
||||
}
|
||||
|
||||
async function writeSessionStore(
|
||||
storePath: string,
|
||||
sessionKey: string,
|
||||
entry: SessionEntry,
|
||||
): Promise<void> {
|
||||
await fs.mkdir(path.dirname(storePath), { recursive: true });
|
||||
await fs.writeFile(storePath, JSON.stringify({ [sessionKey]: entry }, null, 2), "utf8");
|
||||
}
|
||||
|
||||
describe("resetReplyRunSession", () => {
|
||||
let rootDir = "";
|
||||
|
||||
@@ -87,8 +53,8 @@ describe("resetReplyRunSession", () => {
|
||||
},
|
||||
};
|
||||
const sessionStore = { main: sessionEntry };
|
||||
const followupRun = createFollowupRun();
|
||||
await writeSessionStore(storePath, "main", sessionEntry);
|
||||
const followupRun = createTestFollowupRun();
|
||||
await writeTestSessionStore(storePath, "main", sessionEntry);
|
||||
|
||||
let activeSessionEntry: SessionEntry | undefined = sessionEntry;
|
||||
let isNewSession = false;
|
||||
@@ -147,7 +113,7 @@ describe("resetReplyRunSession", () => {
|
||||
sessionFile: oldTranscriptPath,
|
||||
};
|
||||
const sessionStore = { main: sessionEntry };
|
||||
await writeSessionStore(storePath, "main", sessionEntry);
|
||||
await writeTestSessionStore(storePath, "main", sessionEntry);
|
||||
|
||||
await resetReplyRunSession({
|
||||
options: {
|
||||
@@ -160,7 +126,7 @@ describe("resetReplyRunSession", () => {
|
||||
activeSessionEntry: sessionEntry,
|
||||
activeSessionStore: sessionStore,
|
||||
storePath,
|
||||
followupRun: createFollowupRun(),
|
||||
followupRun: createTestFollowupRun(),
|
||||
onActiveSessionEntry: () => {},
|
||||
onNewSession: () => {},
|
||||
});
|
||||
|
||||
42
src/auto-reply/reply/agent-runner.test-fixtures.ts
Normal file
42
src/auto-reply/reply/agent-runner.test-fixtures.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import type { SessionEntry } from "../../config/sessions.js";
|
||||
import type { FollowupRun } from "./queue.js";
|
||||
|
||||
export function createTestFollowupRun(overrides: Partial<FollowupRun["run"]> = {}): FollowupRun {
|
||||
return {
|
||||
prompt: "hello",
|
||||
summaryLine: "hello",
|
||||
enqueuedAt: Date.now(),
|
||||
run: {
|
||||
agentId: "main",
|
||||
agentDir: "/tmp/agent",
|
||||
sessionId: "session",
|
||||
sessionKey: "main",
|
||||
messageProvider: "whatsapp",
|
||||
sessionFile: "/tmp/session.jsonl",
|
||||
workspaceDir: "/tmp",
|
||||
config: {},
|
||||
skillsSnapshot: {},
|
||||
provider: "anthropic",
|
||||
model: "claude",
|
||||
thinkLevel: "low",
|
||||
verboseLevel: "off",
|
||||
elevatedLevel: "off",
|
||||
bashElevated: { enabled: false, allowed: false, defaultLevel: "off" },
|
||||
timeoutMs: 1_000,
|
||||
blockReplyBreak: "message_end",
|
||||
skipProviderRuntimeHints: true,
|
||||
...overrides,
|
||||
},
|
||||
} as unknown as FollowupRun;
|
||||
}
|
||||
|
||||
export async function writeTestSessionStore(
|
||||
storePath: string,
|
||||
sessionKey: string,
|
||||
entry: SessionEntry,
|
||||
): Promise<void> {
|
||||
await fs.mkdir(path.dirname(storePath), { recursive: true });
|
||||
await fs.writeFile(storePath, JSON.stringify({ [sessionKey]: entry }, null, 2), "utf8");
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { handleAbortTrigger } from "./commands-session-abort.js";
|
||||
import "./commands-session-abort.test-support.js";
|
||||
import type { HandleCommandsParams } from "./commands-types.js";
|
||||
|
||||
const abortEmbeddedPiRunMock = vi.hoisted(() => vi.fn());
|
||||
@@ -37,10 +38,6 @@ vi.mock("./commands-session-store.js", () => ({
|
||||
persistAbortTargetEntry: persistAbortTargetEntryMock,
|
||||
}));
|
||||
|
||||
vi.mock("./queue.js", () => ({
|
||||
clearSessionQueues: vi.fn(() => ({ followupCleared: 0, laneCleared: 0, keys: [] })),
|
||||
}));
|
||||
|
||||
vi.mock("./reply-run-registry.js", () => ({
|
||||
replyRunRegistry: {
|
||||
abort: vi.fn(),
|
||||
|
||||
17
src/auto-reply/reply/commands-agent-scope.test-support.ts
Normal file
17
src/auto-reply/reply/commands-agent-scope.test-support.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { vi } from "vitest";
|
||||
|
||||
export const resolveSessionAgentIdMock = vi.fn(() => "main");
|
||||
export const resolveAgentDirMock = vi.fn(
|
||||
(_cfg: unknown, agentId: string) => `/tmp/workspace/.openclaw/agents/${agentId}/agent`,
|
||||
);
|
||||
|
||||
vi.doMock("../../agents/agent-scope.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("../../agents/agent-scope.js")>(
|
||||
"../../agents/agent-scope.js",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
resolveSessionAgentId: resolveSessionAgentIdMock,
|
||||
resolveAgentDir: resolveAgentDirMock,
|
||||
};
|
||||
});
|
||||
@@ -1,22 +1,13 @@
|
||||
import { describe, expect, it, vi, beforeEach } from "vitest";
|
||||
import { resolveAgentDir } from "../../agents/agent-scope.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import {
|
||||
resolveAgentDirMock,
|
||||
resolveSessionAgentIdMock,
|
||||
} from "./commands-agent-scope.test-support.js";
|
||||
import { buildCommandTestParams } from "./commands.test-harness.js";
|
||||
import { createMockTypingController } from "./test-helpers.js";
|
||||
|
||||
const runBtwSideQuestionMock = vi.fn();
|
||||
const resolveSessionAgentIdMock = vi.hoisted(() => vi.fn(() => "main"));
|
||||
|
||||
vi.mock("../../agents/agent-scope.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("../../agents/agent-scope.js")>(
|
||||
"../../agents/agent-scope.js",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
resolveSessionAgentId: resolveSessionAgentIdMock,
|
||||
resolveAgentDir: vi.fn(actual.resolveAgentDir),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../../agents/btw.js", () => ({
|
||||
runBtwSideQuestion: (...args: unknown[]) => runBtwSideQuestionMock(...args),
|
||||
@@ -35,6 +26,10 @@ function buildParams(commandBody: string) {
|
||||
describe("handleBtwCommand", () => {
|
||||
beforeEach(() => {
|
||||
runBtwSideQuestionMock.mockReset();
|
||||
resolveAgentDirMock.mockReset();
|
||||
resolveAgentDirMock.mockImplementation(
|
||||
(_cfg: unknown, agentId: string) => `/tmp/workspace/.openclaw/agents/${agentId}/agent`,
|
||||
);
|
||||
resolveSessionAgentIdMock.mockReset();
|
||||
resolveSessionAgentIdMock.mockReturnValue("main");
|
||||
});
|
||||
@@ -207,7 +202,7 @@ describe("handleBtwCommand", () => {
|
||||
updatedAt: Date.now(),
|
||||
};
|
||||
resolveSessionAgentIdMock.mockReturnValue("worker-1");
|
||||
vi.mocked(resolveAgentDir).mockReturnValue("/tmp/worker-1-agent");
|
||||
resolveAgentDirMock.mockReturnValue("/tmp/worker-1-agent");
|
||||
runBtwSideQuestionMock.mockResolvedValue({ text: "resolved fallback" });
|
||||
|
||||
const result = await handleBtwCommand(params, true);
|
||||
@@ -216,7 +211,7 @@ describe("handleBtwCommand", () => {
|
||||
sessionKey: "agent:worker-1:whatsapp:direct:12345",
|
||||
config: expect.any(Object),
|
||||
});
|
||||
expect(vi.mocked(resolveAgentDir)).toHaveBeenCalledWith(expect.any(Object), "worker-1");
|
||||
expect(resolveAgentDirMock).toHaveBeenCalledWith(expect.any(Object), "worker-1");
|
||||
expect(runBtwSideQuestionMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
agentDir: "/tmp/worker-1-agent",
|
||||
|
||||
@@ -1,22 +1,11 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { resolveAgentDir } from "../../agents/agent-scope.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { handleCompactCommand } from "./commands-compact.js";
|
||||
import {
|
||||
resolveAgentDirMock,
|
||||
resolveSessionAgentIdMock,
|
||||
} from "./commands-agent-scope.test-support.js";
|
||||
import type { HandleCommandsParams } from "./commands-types.js";
|
||||
|
||||
const resolveSessionAgentIdMock = vi.hoisted(() => vi.fn(() => "main"));
|
||||
|
||||
vi.mock("../../agents/agent-scope.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("../../agents/agent-scope.js")>(
|
||||
"../../agents/agent-scope.js",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
resolveSessionAgentId: resolveSessionAgentIdMock,
|
||||
resolveAgentDir: vi.fn(actual.resolveAgentDir),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("./commands-compact.runtime.js", () => ({
|
||||
abortEmbeddedPiRun: vi.fn(),
|
||||
compactEmbeddedPiSession: vi.fn(),
|
||||
@@ -33,6 +22,7 @@ vi.mock("./commands-compact.runtime.js", () => ({
|
||||
|
||||
const { compactEmbeddedPiSession, incrementCompactionCount, resolveSessionFilePathOptions } =
|
||||
await import("./commands-compact.runtime.js");
|
||||
const { handleCompactCommand } = await import("./commands-compact.js");
|
||||
|
||||
function buildCompactParams(
|
||||
commandBodyNormalized: string,
|
||||
@@ -63,6 +53,9 @@ function buildCompactParams(
|
||||
describe("handleCompactCommand", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
resolveAgentDirMock.mockImplementation(
|
||||
(_cfg: unknown, agentId: string) => `/tmp/workspace/.openclaw/agents/${agentId}/agent`,
|
||||
);
|
||||
resolveSessionAgentIdMock.mockReturnValue("main");
|
||||
});
|
||||
|
||||
@@ -202,7 +195,7 @@ describe("handleCompactCommand", () => {
|
||||
compacted: false,
|
||||
});
|
||||
resolveSessionAgentIdMock.mockReturnValue("target");
|
||||
vi.mocked(resolveAgentDir).mockReturnValue("/tmp/target-agent");
|
||||
resolveAgentDirMock.mockReturnValue("/tmp/target-agent");
|
||||
|
||||
await handleCompactCommand(
|
||||
{
|
||||
@@ -226,7 +219,7 @@ describe("handleCompactCommand", () => {
|
||||
agentDir: "/tmp/target-agent",
|
||||
}),
|
||||
);
|
||||
expect(vi.mocked(resolveAgentDir)).toHaveBeenCalledWith(expect.any(Object), "target");
|
||||
expect(resolveAgentDirMock).toHaveBeenCalledWith(expect.any(Object), "target");
|
||||
});
|
||||
|
||||
it("prefers the target session entry for compaction runtime metadata", async () => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { withTempHome } from "../../config/home-env.test-harness.js";
|
||||
import { createCommandWorkspaceHarness } from "./commands-filesystem.test-support.js";
|
||||
import { handlePluginsCommand } from "./commands-plugins.js";
|
||||
import type { HandleCommandsParams } from "./commands-types.js";
|
||||
import { buildPluginsCommandParams } from "./commands.test-harness.js";
|
||||
|
||||
const { installPluginFromPathMock, installPluginFromClawHubMock, persistPluginInstallMock } =
|
||||
vi.hoisted(() => ({
|
||||
@@ -39,45 +39,12 @@ vi.mock("../../cli/plugins-install-persist.js", () => ({
|
||||
|
||||
const workspaceHarness = createCommandWorkspaceHarness("openclaw-command-plugins-install-");
|
||||
|
||||
function buildPluginsParams(
|
||||
commandBodyNormalized: string,
|
||||
workspaceDir: string,
|
||||
): HandleCommandsParams {
|
||||
return {
|
||||
cfg: {
|
||||
commands: {
|
||||
text: true,
|
||||
plugins: true,
|
||||
},
|
||||
plugins: { enabled: true },
|
||||
},
|
||||
ctx: {
|
||||
Provider: "whatsapp",
|
||||
Surface: "whatsapp",
|
||||
CommandSource: "text",
|
||||
GatewayClientScopes: ["operator.admin", "operator.write", "operator.pairing"],
|
||||
AccountId: undefined,
|
||||
},
|
||||
command: {
|
||||
commandBodyNormalized,
|
||||
rawBodyNormalized: commandBodyNormalized,
|
||||
isAuthorizedSender: true,
|
||||
senderIsOwner: true,
|
||||
senderId: "owner",
|
||||
channel: "whatsapp",
|
||||
channelId: "whatsapp",
|
||||
surface: "whatsapp",
|
||||
ownerList: [],
|
||||
from: "test-user",
|
||||
to: "test-bot",
|
||||
},
|
||||
sessionKey: "agent:main:whatsapp:direct:test-user",
|
||||
sessionEntry: {
|
||||
sessionId: "session-plugin-command",
|
||||
updatedAt: Date.now(),
|
||||
},
|
||||
function buildPluginsParams(commandBodyNormalized: string, workspaceDir: string) {
|
||||
return buildPluginsCommandParams({
|
||||
commandBodyNormalized,
|
||||
workspaceDir,
|
||||
} as unknown as HandleCommandsParams;
|
||||
gatewayClientScopes: ["operator.admin", "operator.write", "operator.pairing"],
|
||||
});
|
||||
}
|
||||
|
||||
describe("handleCommands /plugins install", () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { handlePluginsCommand } from "./commands-plugins.js";
|
||||
import type { HandleCommandsParams } from "./commands-types.js";
|
||||
import { buildPluginsCommandParams } from "./commands.test-harness.js";
|
||||
|
||||
const readConfigFileSnapshotMock = vi.hoisted(() => vi.fn());
|
||||
const validateConfigObjectWithPluginsMock = vi.hoisted(() => vi.fn());
|
||||
@@ -90,39 +90,11 @@ function buildCfg(): OpenClawConfig {
|
||||
};
|
||||
}
|
||||
|
||||
function buildPluginsParams(
|
||||
commandBodyNormalized: string,
|
||||
cfg: OpenClawConfig,
|
||||
): HandleCommandsParams {
|
||||
return {
|
||||
function buildPluginsParams(commandBodyNormalized: string, cfg: OpenClawConfig) {
|
||||
return buildPluginsCommandParams({
|
||||
commandBodyNormalized,
|
||||
cfg,
|
||||
ctx: {
|
||||
Provider: "whatsapp",
|
||||
Surface: "whatsapp",
|
||||
CommandSource: "text",
|
||||
GatewayClientScopes: ["operator.write", "operator.pairing"],
|
||||
AccountId: undefined,
|
||||
},
|
||||
command: {
|
||||
commandBodyNormalized,
|
||||
rawBodyNormalized: commandBodyNormalized,
|
||||
isAuthorizedSender: true,
|
||||
senderIsOwner: true,
|
||||
senderId: "owner",
|
||||
channel: "whatsapp",
|
||||
channelId: "whatsapp",
|
||||
surface: "whatsapp",
|
||||
ownerList: [],
|
||||
from: "test-user",
|
||||
to: "test-bot",
|
||||
},
|
||||
sessionKey: "agent:main:whatsapp:direct:test-user",
|
||||
sessionEntry: {
|
||||
sessionId: "session-plugin-command",
|
||||
updatedAt: Date.now(),
|
||||
},
|
||||
workspaceDir: "/tmp/plugins-workspace",
|
||||
} as unknown as HandleCommandsParams;
|
||||
});
|
||||
}
|
||||
|
||||
describe("handlePluginsCommand", () => {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { vi } from "vitest";
|
||||
|
||||
vi.mock("./queue.js", () => ({
|
||||
clearSessionQueues: vi.fn(() => ({ followupCleared: 0, laneCleared: 0, keys: [] })),
|
||||
}));
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
addSubagentRunForTests,
|
||||
resetSubagentRegistryForTests,
|
||||
} from "../../agents/subagent-registry.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import {
|
||||
completeTaskRunByRunId,
|
||||
createQueuedTaskRun,
|
||||
@@ -15,15 +14,14 @@ import {
|
||||
failTaskRunByRunId,
|
||||
} from "../../tasks/task-executor.js";
|
||||
import { resetTaskRegistryForTests } from "../../tasks/task-registry.js";
|
||||
import { configureTaskRegistryRuntime } from "../../tasks/task-registry.store.js";
|
||||
import { buildStatusReply, buildStatusText } from "./commands-status.js";
|
||||
import { buildCommandTestParams } from "./commands.test-harness.js";
|
||||
import {
|
||||
baseCommandTestConfig,
|
||||
buildCommandTestParams,
|
||||
configureInMemoryTaskRegistryStoreForTests,
|
||||
} from "./commands.test-harness.js";
|
||||
|
||||
const baseCfg = {
|
||||
commands: { text: true },
|
||||
channels: { whatsapp: { allowFrom: ["*"] } },
|
||||
session: { mainKey: "main", scope: "per-sender" },
|
||||
} as OpenClawConfig;
|
||||
const baseCfg = baseCommandTestConfig;
|
||||
|
||||
async function buildStatusReplyForTest(params: { sessionKey?: string; verbose?: boolean }) {
|
||||
const commandParams = buildCommandTestParams("/status", baseCfg);
|
||||
@@ -87,25 +85,6 @@ function writeTranscriptUsageLog(params: {
|
||||
);
|
||||
}
|
||||
|
||||
function configureInMemoryTaskRegistryStoreForTests(): void {
|
||||
configureTaskRegistryRuntime({
|
||||
store: {
|
||||
loadSnapshot: () => ({
|
||||
tasks: new Map(),
|
||||
deliveryStates: new Map(),
|
||||
}),
|
||||
saveSnapshot: () => {},
|
||||
upsertTaskWithDeliveryState: () => {},
|
||||
upsertTask: () => {},
|
||||
deleteTaskWithDeliveryState: () => {},
|
||||
deleteTask: () => {},
|
||||
upsertDeliveryState: () => {},
|
||||
deleteDeliveryState: () => {},
|
||||
close: () => {},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
describe("buildStatusReply subagent summary", () => {
|
||||
beforeEach(() => {
|
||||
resetSubagentRegistryForTests();
|
||||
|
||||
@@ -33,6 +33,25 @@ vi.mock("./queue.js", () => ({
|
||||
|
||||
const { buildStatusReply } = await import("./commands-status.js");
|
||||
|
||||
async function buildKiraStatusReply(cfg: OpenClawConfig) {
|
||||
return await buildStatusReply({
|
||||
cfg,
|
||||
command: {
|
||||
isAuthorizedSender: true,
|
||||
channel: "whatsapp",
|
||||
} as never,
|
||||
sessionKey: "agent:kira:main",
|
||||
provider: "openai",
|
||||
model: "gpt-5.4",
|
||||
contextTokens: 0,
|
||||
resolvedVerboseLevel: "off",
|
||||
resolvedReasoningLevel: "off",
|
||||
resolveDefaultThinkingLevel: async () => undefined,
|
||||
isGroup: false,
|
||||
defaultGroupActivation: () => "mention",
|
||||
});
|
||||
}
|
||||
|
||||
describe("buildStatusReply", () => {
|
||||
it("shows per-agent thinkingDefault in the status card", async () => {
|
||||
const cfg = {
|
||||
@@ -54,22 +73,7 @@ describe("buildStatusReply", () => {
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
const reply = await buildStatusReply({
|
||||
cfg,
|
||||
command: {
|
||||
isAuthorizedSender: true,
|
||||
channel: "whatsapp",
|
||||
} as never,
|
||||
sessionKey: "agent:kira:main",
|
||||
provider: "openai",
|
||||
model: "gpt-5.4",
|
||||
contextTokens: 0,
|
||||
resolvedVerboseLevel: "off",
|
||||
resolvedReasoningLevel: "off",
|
||||
resolveDefaultThinkingLevel: async () => undefined,
|
||||
isGroup: false,
|
||||
defaultGroupActivation: () => "mention",
|
||||
});
|
||||
const reply = await buildKiraStatusReply(cfg);
|
||||
|
||||
expect(reply?.text).toContain("Think: xhigh");
|
||||
});
|
||||
@@ -99,22 +103,7 @@ describe("buildStatusReply", () => {
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
const reply = await buildStatusReply({
|
||||
cfg,
|
||||
command: {
|
||||
isAuthorizedSender: true,
|
||||
channel: "whatsapp",
|
||||
} as never,
|
||||
sessionKey: "agent:kira:main",
|
||||
provider: "openai",
|
||||
model: "gpt-5.4",
|
||||
contextTokens: 0,
|
||||
resolvedVerboseLevel: "off",
|
||||
resolvedReasoningLevel: "off",
|
||||
resolveDefaultThinkingLevel: async () => undefined,
|
||||
isGroup: false,
|
||||
defaultGroupActivation: () => "mention",
|
||||
});
|
||||
const reply = await buildKiraStatusReply(cfg);
|
||||
|
||||
expect(reply?.text).toContain("Fallbacks: google/gemini-2.5-flash");
|
||||
expect(reply?.text).not.toContain("Fallbacks: anthropic/claude-sonnet-4-6");
|
||||
@@ -144,22 +133,7 @@ describe("buildStatusReply", () => {
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
const reply = await buildStatusReply({
|
||||
cfg,
|
||||
command: {
|
||||
isAuthorizedSender: true,
|
||||
channel: "whatsapp",
|
||||
} as never,
|
||||
sessionKey: "agent:kira:main",
|
||||
provider: "openai",
|
||||
model: "gpt-5.4",
|
||||
contextTokens: 0,
|
||||
resolvedVerboseLevel: "off",
|
||||
resolvedReasoningLevel: "off",
|
||||
resolveDefaultThinkingLevel: async () => undefined,
|
||||
isGroup: false,
|
||||
defaultGroupActivation: () => "mention",
|
||||
});
|
||||
const reply = await buildKiraStatusReply(cfg);
|
||||
|
||||
expect(reply?.text).toContain("Fallbacks: anthropic/claude-sonnet-4-6");
|
||||
});
|
||||
@@ -189,22 +163,7 @@ describe("buildStatusReply", () => {
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
const reply = await buildStatusReply({
|
||||
cfg,
|
||||
command: {
|
||||
isAuthorizedSender: true,
|
||||
channel: "whatsapp",
|
||||
} as never,
|
||||
sessionKey: "agent:kira:main",
|
||||
provider: "openai",
|
||||
model: "gpt-5.4",
|
||||
contextTokens: 0,
|
||||
resolvedVerboseLevel: "off",
|
||||
resolvedReasoningLevel: "off",
|
||||
resolveDefaultThinkingLevel: async () => undefined,
|
||||
isGroup: false,
|
||||
defaultGroupActivation: () => "mention",
|
||||
});
|
||||
const reply = await buildKiraStatusReply(cfg);
|
||||
|
||||
expect(reply?.text).not.toContain("Fallbacks:");
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { handleStopCommand } from "./commands-session-abort.js";
|
||||
import "./commands-session-abort.test-support.js";
|
||||
import type { HandleCommandsParams } from "./commands-types.js";
|
||||
|
||||
const abortEmbeddedPiRunMock = vi.hoisted(() => vi.fn());
|
||||
@@ -40,10 +41,6 @@ vi.mock("./commands-session-store.js", () => ({
|
||||
persistAbortTargetEntry: persistAbortTargetEntryMock,
|
||||
}));
|
||||
|
||||
vi.mock("./queue.js", () => ({
|
||||
clearSessionQueues: vi.fn(() => ({ followupCleared: 0, laneCleared: 0, keys: [] })),
|
||||
}));
|
||||
|
||||
vi.mock("./reply-run-registry.js", () => ({
|
||||
replyRunRegistry: {
|
||||
abort: replyRunAbortMock,
|
||||
|
||||
@@ -9,6 +9,7 @@ import { failTaskRunByRunId } from "../../tasks/task-executor.js";
|
||||
import { createTaskRecord, resetTaskRegistryForTests } from "../../tasks/task-registry.js";
|
||||
import type { ReplyPayload } from "../types.js";
|
||||
import { handleSubagentsInfoAction } from "./commands-subagents/action-info.js";
|
||||
import { baseCommandTestConfig } from "./commands.test-harness.js";
|
||||
|
||||
function buildInfoContext(params: { cfg: OpenClawConfig; runs: object[]; restTokens: string[] }) {
|
||||
return {
|
||||
@@ -71,11 +72,7 @@ describe("subagents info", () => {
|
||||
terminalSummary: "Completed the requested task",
|
||||
deliveryStatus: "delivered",
|
||||
});
|
||||
const cfg = {
|
||||
commands: { text: true },
|
||||
channels: { whatsapp: { allowFrom: ["*"] } },
|
||||
session: { mainKey: "main", scope: "per-sender" },
|
||||
} as OpenClawConfig;
|
||||
const cfg = baseCommandTestConfig;
|
||||
const result = handleSubagentsInfoAction(
|
||||
buildInfoContext({ cfg, runs: [run], restTokens: ["1"] }),
|
||||
);
|
||||
@@ -135,11 +132,7 @@ describe("subagents info", () => {
|
||||
].join("\n"),
|
||||
terminalSummary: "Needs manual follow-up.",
|
||||
});
|
||||
const cfg = {
|
||||
commands: { text: true },
|
||||
channels: { whatsapp: { allowFrom: ["*"] } },
|
||||
session: { mainKey: "main", scope: "per-sender" },
|
||||
} as OpenClawConfig;
|
||||
const cfg = baseCommandTestConfig;
|
||||
const result = handleSubagentsInfoAction(
|
||||
buildInfoContext({ cfg, runs: [run], restTokens: ["1"] }),
|
||||
);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { resolveSessionAgentId } from "../../agents/agent-scope.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import {
|
||||
completeTaskRunByRunId,
|
||||
createQueuedTaskRun,
|
||||
@@ -8,9 +7,12 @@ import {
|
||||
failTaskRunByRunId,
|
||||
} from "../../tasks/task-executor.js";
|
||||
import { resetTaskRegistryForTests } from "../../tasks/task-registry.js";
|
||||
import { configureTaskRegistryRuntime } from "../../tasks/task-registry.store.js";
|
||||
import { buildTasksReply, handleTasksCommand } from "./commands-tasks.js";
|
||||
import { buildCommandTestParams } from "./commands.test-harness.js";
|
||||
import {
|
||||
baseCommandTestConfig,
|
||||
buildCommandTestParams,
|
||||
configureInMemoryTaskRegistryStoreForTests,
|
||||
} from "./commands.test-harness.js";
|
||||
|
||||
vi.mock("../../agents/agent-scope.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("../../agents/agent-scope.js")>(
|
||||
@@ -22,11 +24,7 @@ vi.mock("../../agents/agent-scope.js", async () => {
|
||||
};
|
||||
});
|
||||
|
||||
const baseCfg = {
|
||||
commands: { text: true },
|
||||
channels: { whatsapp: { allowFrom: ["*"] } },
|
||||
session: { mainKey: "main", scope: "per-sender" },
|
||||
} as OpenClawConfig;
|
||||
const baseCfg = baseCommandTestConfig;
|
||||
|
||||
async function buildTasksReplyForTest(params: { sessionKey?: string } = {}) {
|
||||
const commandParams = buildCommandTestParams("/tasks", baseCfg);
|
||||
@@ -36,25 +34,6 @@ async function buildTasksReplyForTest(params: { sessionKey?: string } = {}) {
|
||||
});
|
||||
}
|
||||
|
||||
function configureInMemoryTaskRegistryStoreForTests(): void {
|
||||
configureTaskRegistryRuntime({
|
||||
store: {
|
||||
loadSnapshot: () => ({
|
||||
tasks: new Map(),
|
||||
deliveryStates: new Map(),
|
||||
}),
|
||||
saveSnapshot: () => {},
|
||||
upsertTaskWithDeliveryState: () => {},
|
||||
upsertTask: () => {},
|
||||
deleteTaskWithDeliveryState: () => {},
|
||||
deleteTask: () => {},
|
||||
upsertDeliveryState: () => {},
|
||||
deleteDeliveryState: () => {},
|
||||
close: () => {},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
describe("buildTasksReply", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { configureTaskRegistryRuntime } from "../../tasks/task-registry.store.js";
|
||||
import type { MsgContext } from "../templating.js";
|
||||
import { buildCommandContext } from "./commands-context.js";
|
||||
import type { HandleCommandsParams } from "./commands-types.js";
|
||||
import { parseInlineDirectives } from "./directive-handling.parse.js";
|
||||
|
||||
export const baseCommandTestConfig = {
|
||||
commands: { text: true },
|
||||
channels: { whatsapp: { allowFrom: ["*"] } },
|
||||
session: { mainKey: "main", scope: "per-sender" },
|
||||
} as OpenClawConfig;
|
||||
|
||||
export function buildCommandTestParams(
|
||||
commandBody: string,
|
||||
cfg: OpenClawConfig,
|
||||
@@ -49,3 +56,68 @@ export function buildCommandTestParams(
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
export function configureInMemoryTaskRegistryStoreForTests(): void {
|
||||
configureTaskRegistryRuntime({
|
||||
store: {
|
||||
loadSnapshot: () => ({
|
||||
tasks: new Map(),
|
||||
deliveryStates: new Map(),
|
||||
}),
|
||||
saveSnapshot: () => {},
|
||||
upsertTaskWithDeliveryState: () => {},
|
||||
upsertTask: () => {},
|
||||
deleteTaskWithDeliveryState: () => {},
|
||||
deleteTask: () => {},
|
||||
upsertDeliveryState: () => {},
|
||||
deleteDeliveryState: () => {},
|
||||
close: () => {},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function buildPluginsCommandParams(params: {
|
||||
commandBodyNormalized: string;
|
||||
cfg?: OpenClawConfig;
|
||||
workspaceDir?: string;
|
||||
gatewayClientScopes?: string[];
|
||||
}): HandleCommandsParams {
|
||||
const commandBodyNormalized = params.commandBodyNormalized;
|
||||
return {
|
||||
cfg:
|
||||
params.cfg ??
|
||||
({
|
||||
commands: {
|
||||
text: true,
|
||||
plugins: true,
|
||||
},
|
||||
plugins: { enabled: true },
|
||||
} as OpenClawConfig),
|
||||
ctx: {
|
||||
Provider: "whatsapp",
|
||||
Surface: "whatsapp",
|
||||
CommandSource: "text",
|
||||
GatewayClientScopes: params.gatewayClientScopes ?? ["operator.write", "operator.pairing"],
|
||||
AccountId: undefined,
|
||||
},
|
||||
command: {
|
||||
commandBodyNormalized,
|
||||
rawBodyNormalized: commandBodyNormalized,
|
||||
isAuthorizedSender: true,
|
||||
senderIsOwner: true,
|
||||
senderId: "owner",
|
||||
channel: "whatsapp",
|
||||
channelId: "whatsapp",
|
||||
surface: "whatsapp",
|
||||
ownerList: [],
|
||||
from: "test-user",
|
||||
to: "test-bot",
|
||||
},
|
||||
sessionKey: "agent:main:whatsapp:direct:test-user",
|
||||
sessionEntry: {
|
||||
sessionId: "session-plugin-command",
|
||||
updatedAt: Date.now(),
|
||||
},
|
||||
workspaceDir: params.workspaceDir ?? "/tmp/plugins-workspace",
|
||||
} as unknown as HandleCommandsParams;
|
||||
}
|
||||
|
||||
@@ -87,6 +87,39 @@ function createCoordinator(onReplyStart?: (...args: unknown[]) => Promise<void>)
|
||||
});
|
||||
}
|
||||
|
||||
function createDiscordAcpCoordinator(cfg: OpenClawConfig) {
|
||||
return createAcpDispatchDeliveryCoordinator({
|
||||
cfg,
|
||||
ctx: buildTestCtx({
|
||||
Provider: "discord",
|
||||
Surface: "discord",
|
||||
SessionKey: "agent:codex-acp:session-1",
|
||||
}),
|
||||
dispatcher: createDispatcher(),
|
||||
inboundAudio: false,
|
||||
shouldRouteToOriginating: true,
|
||||
originatingChannel: "discord",
|
||||
originatingTo: "channel:thread-1",
|
||||
});
|
||||
}
|
||||
|
||||
async function expectDiscordBlockRoutesToAccount(
|
||||
cfg: OpenClawConfig,
|
||||
accountId: string | undefined,
|
||||
): Promise<void> {
|
||||
const coordinator = createDiscordAcpCoordinator(cfg);
|
||||
|
||||
await coordinator.deliver("block", { text: "hello" }, { skipTts: true });
|
||||
|
||||
expect(deliveryMocks.routeReply).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
channel: "discord",
|
||||
to: "channel:thread-1",
|
||||
accountId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
describe("createAcpDispatchDeliveryCoordinator", () => {
|
||||
beforeEach(() => {
|
||||
deliveryMocks.routeReply.mockClear();
|
||||
@@ -369,61 +402,20 @@ describe("createAcpDispatchDeliveryCoordinator", () => {
|
||||
});
|
||||
|
||||
it("routes ACP replies through the configured default account when AccountId is omitted", async () => {
|
||||
const coordinator = createAcpDispatchDeliveryCoordinator({
|
||||
cfg: createAcpTestConfig({
|
||||
await expectDiscordBlockRoutesToAccount(
|
||||
createAcpTestConfig({
|
||||
channels: {
|
||||
discord: {
|
||||
defaultAccount: "work",
|
||||
},
|
||||
},
|
||||
}),
|
||||
ctx: buildTestCtx({
|
||||
Provider: "discord",
|
||||
Surface: "discord",
|
||||
SessionKey: "agent:codex-acp:session-1",
|
||||
}),
|
||||
dispatcher: createDispatcher(),
|
||||
inboundAudio: false,
|
||||
shouldRouteToOriginating: true,
|
||||
originatingChannel: "discord",
|
||||
originatingTo: "channel:thread-1",
|
||||
});
|
||||
|
||||
await coordinator.deliver("block", { text: "hello" }, { skipTts: true });
|
||||
|
||||
expect(deliveryMocks.routeReply).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
channel: "discord",
|
||||
to: "channel:thread-1",
|
||||
accountId: "work",
|
||||
}),
|
||||
"work",
|
||||
);
|
||||
});
|
||||
|
||||
it("routes ACP replies when cfg.channels is missing", async () => {
|
||||
const coordinator = createAcpDispatchDeliveryCoordinator({
|
||||
cfg: {} as OpenClawConfig,
|
||||
ctx: buildTestCtx({
|
||||
Provider: "discord",
|
||||
Surface: "discord",
|
||||
SessionKey: "agent:codex-acp:session-1",
|
||||
}),
|
||||
dispatcher: createDispatcher(),
|
||||
inboundAudio: false,
|
||||
shouldRouteToOriginating: true,
|
||||
originatingChannel: "discord",
|
||||
originatingTo: "channel:thread-1",
|
||||
});
|
||||
|
||||
await coordinator.deliver("block", { text: "hello" }, { skipTts: true });
|
||||
|
||||
expect(deliveryMocks.routeReply).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
channel: "discord",
|
||||
to: "channel:thread-1",
|
||||
accountId: undefined,
|
||||
}),
|
||||
);
|
||||
await expectDiscordBlockRoutesToAccount({} as OpenClawConfig, undefined);
|
||||
});
|
||||
|
||||
it("treats routed discord block text as visible", async () => {
|
||||
|
||||
Reference in New Issue
Block a user