mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 08:48:37 +00:00
chore: Fix types in tests 42/N.
This commit is contained in:
@@ -370,6 +370,7 @@ describe("resolveGroupRequireMention", () => {
|
|||||||
GroupSpace: "145",
|
GroupSpace: "145",
|
||||||
};
|
};
|
||||||
const groupResolution: GroupKeyResolution = {
|
const groupResolution: GroupKeyResolution = {
|
||||||
|
key: "discord:group:123",
|
||||||
channel: "discord",
|
channel: "discord",
|
||||||
id: "123",
|
id: "123",
|
||||||
chatType: "group",
|
chatType: "group",
|
||||||
@@ -394,6 +395,7 @@ describe("resolveGroupRequireMention", () => {
|
|||||||
GroupSubject: "#general",
|
GroupSubject: "#general",
|
||||||
};
|
};
|
||||||
const groupResolution: GroupKeyResolution = {
|
const groupResolution: GroupKeyResolution = {
|
||||||
|
key: "slack:group:C123",
|
||||||
channel: "slack",
|
channel: "slack",
|
||||||
id: "C123",
|
id: "C123",
|
||||||
chatType: "group",
|
chatType: "group",
|
||||||
|
|||||||
@@ -79,7 +79,11 @@ describe("buildInboundMediaNote", () => {
|
|||||||
const note = buildInboundMediaNote({
|
const note = buildInboundMediaNote({
|
||||||
MediaPaths: ["/tmp/a.png", "/tmp/b.png"],
|
MediaPaths: ["/tmp/a.png", "/tmp/b.png"],
|
||||||
MediaUrls: ["https://example.com/a.png", "https://example.com/b.png"],
|
MediaUrls: ["https://example.com/a.png", "https://example.com/b.png"],
|
||||||
MediaUnderstandingDecisions: [createSuccessfulImageMediaDecision()],
|
MediaUnderstandingDecisions: [
|
||||||
|
createSuccessfulImageMediaDecision() as unknown as NonNullable<
|
||||||
|
Parameters<typeof buildInboundMediaNote>[0]["MediaUnderstandingDecisions"]
|
||||||
|
>[number],
|
||||||
|
],
|
||||||
});
|
});
|
||||||
expect(note).toBe("[media attached: /tmp/b.png | https://example.com/b.png]");
|
expect(note).toBe("[media attached: /tmp/b.png | https://example.com/b.png]");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import fs from "node:fs/promises";
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { loadSessionStore } from "../config/sessions.js";
|
import { loadSessionStore } from "../config/sessions.js";
|
||||||
|
import type { ModelDefinitionConfig } from "../config/types.models.js";
|
||||||
import { drainSystemEvents } from "../infra/system-events.js";
|
import { drainSystemEvents } from "../infra/system-events.js";
|
||||||
import {
|
import {
|
||||||
assertModelSelection,
|
assertModelSelection,
|
||||||
@@ -13,6 +14,18 @@ import {
|
|||||||
} from "./reply.directive.directive-behavior.e2e-harness.js";
|
} from "./reply.directive.directive-behavior.e2e-harness.js";
|
||||||
import { getReplyFromConfig } from "./reply.js";
|
import { getReplyFromConfig } from "./reply.js";
|
||||||
|
|
||||||
|
function makeModelDefinition(id: string, name: string): ModelDefinitionConfig {
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
reasoning: false,
|
||||||
|
input: ["text"],
|
||||||
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||||
|
contextWindow: 128_000,
|
||||||
|
maxTokens: 8_192,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
describe("directive behavior", () => {
|
describe("directive behavior", () => {
|
||||||
installDirectiveBehaviorE2EHooks();
|
installDirectiveBehaviorE2EHooks();
|
||||||
|
|
||||||
@@ -42,13 +55,13 @@ describe("directive behavior", () => {
|
|||||||
baseUrl: "https://api.moonshot.ai/v1",
|
baseUrl: "https://api.moonshot.ai/v1",
|
||||||
apiKey: "sk-test",
|
apiKey: "sk-test",
|
||||||
api: "openai-completions",
|
api: "openai-completions",
|
||||||
models: [{ id: "kimi-k2-0905-preview", name: "Kimi K2" }],
|
models: [makeModelDefinition("kimi-k2-0905-preview", "Kimi K2")],
|
||||||
},
|
},
|
||||||
lmstudio: {
|
lmstudio: {
|
||||||
baseUrl: "http://127.0.0.1:1234/v1",
|
baseUrl: "http://127.0.0.1:1234/v1",
|
||||||
apiKey: "lmstudio",
|
apiKey: "lmstudio",
|
||||||
api: "openai-responses",
|
api: "openai-responses",
|
||||||
models: [{ id: "kimi-k2-0905-preview", name: "Kimi K2 (Local)" }],
|
models: [makeModelDefinition("kimi-k2-0905-preview", "Kimi K2 (Local)")],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import "./reply.directive.directive-behavior.e2e-mocks.js";
|
import "./reply.directive.directive-behavior.e2e-mocks.js";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
import type { OpenClawConfig } from "../config/config.js";
|
||||||
import { loadSessionStore } from "../config/sessions.js";
|
import { loadSessionStore } from "../config/sessions.js";
|
||||||
import {
|
import {
|
||||||
installDirectiveBehaviorE2EHooks,
|
installDirectiveBehaviorE2EHooks,
|
||||||
@@ -27,7 +28,7 @@ describe("directive behavior", () => {
|
|||||||
},
|
},
|
||||||
channels: { whatsapp: { allowFrom: ["*"] } },
|
channels: { whatsapp: { allowFrom: ["*"] } },
|
||||||
session: { store: storePath },
|
session: { store: storePath },
|
||||||
};
|
} as unknown as OpenClawConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runQueueDirective(params: { home: string; storePath: string; body: string }) {
|
async function runQueueDirective(params: { home: string; storePath: string; body: string }) {
|
||||||
@@ -94,7 +95,7 @@ describe("directive behavior", () => {
|
|||||||
CommandAuthorized: true,
|
CommandAuthorized: true,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
makeRestrictedElevatedDisabledConfig(home),
|
makeRestrictedElevatedDisabledConfig(home) as unknown as OpenClawConfig,
|
||||||
);
|
);
|
||||||
|
|
||||||
const text = extractReplyText(res);
|
const text = extractReplyText(res);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import "./reply.directive.directive-behavior.e2e-mocks.js";
|
import "./reply.directive.directive-behavior.e2e-mocks.js";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
import type { OpenClawConfig } from "../config/config.js";
|
||||||
import { loadSessionStore } from "../config/sessions.js";
|
import { loadSessionStore } from "../config/sessions.js";
|
||||||
import {
|
import {
|
||||||
AUTHORIZED_WHATSAPP_COMMAND,
|
AUTHORIZED_WHATSAPP_COMMAND,
|
||||||
@@ -65,7 +66,7 @@ describe("directive behavior", () => {
|
|||||||
CommandAuthorized: true,
|
CommandAuthorized: true,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
makeRestrictedElevatedDisabledConfig(home),
|
makeRestrictedElevatedDisabledConfig(home) as unknown as OpenClawConfig,
|
||||||
);
|
);
|
||||||
|
|
||||||
const text = replyText(res);
|
const text = replyText(res);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import type { OpenClawConfig } from "../config/config.js";
|
||||||
import { createTempHomeHarness, makeReplyConfig } from "./reply.test-harness.js";
|
import { createTempHomeHarness, makeReplyConfig } from "./reply.test-harness.js";
|
||||||
|
|
||||||
const runEmbeddedPiAgentMock = vi.fn();
|
const runEmbeddedPiAgentMock = vi.fn();
|
||||||
@@ -63,7 +64,7 @@ describe("getReplyFromConfig typing (heartbeat)", () => {
|
|||||||
await getReplyFromConfig(
|
await getReplyFromConfig(
|
||||||
{ Body: "hi", From: "+1000", To: "+2000", Provider: "whatsapp" },
|
{ Body: "hi", From: "+1000", To: "+2000", Provider: "whatsapp" },
|
||||||
{ onReplyStart, isHeartbeat: false },
|
{ onReplyStart, isHeartbeat: false },
|
||||||
makeReplyConfig(home),
|
makeReplyConfig(home) as unknown as OpenClawConfig,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(onReplyStart).toHaveBeenCalled();
|
expect(onReplyStart).toHaveBeenCalled();
|
||||||
@@ -81,7 +82,7 @@ describe("getReplyFromConfig typing (heartbeat)", () => {
|
|||||||
await getReplyFromConfig(
|
await getReplyFromConfig(
|
||||||
{ Body: "hi", From: "+1000", To: "+2000", Provider: "whatsapp" },
|
{ Body: "hi", From: "+1000", To: "+2000", Provider: "whatsapp" },
|
||||||
{ onReplyStart, isHeartbeat: true },
|
{ onReplyStart, isHeartbeat: true },
|
||||||
makeReplyConfig(home),
|
makeReplyConfig(home) as unknown as OpenClawConfig,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(onReplyStart).not.toHaveBeenCalled();
|
expect(onReplyStart).not.toHaveBeenCalled();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import path from "node:path";
|
|||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
import { withTempHome as withTempHomeBase } from "../../test/helpers/temp-home.js";
|
import { withTempHome as withTempHomeBase } from "../../test/helpers/temp-home.js";
|
||||||
import { runEmbeddedPiAgent } from "../agents/pi-embedded.js";
|
import { runEmbeddedPiAgent } from "../agents/pi-embedded.js";
|
||||||
|
import type { OpenClawConfig } from "../config/config.js";
|
||||||
import { getReplyFromConfig } from "./reply.js";
|
import { getReplyFromConfig } from "./reply.js";
|
||||||
|
|
||||||
vi.mock("../agents/pi-embedded.js", () => ({
|
vi.mock("../agents/pi-embedded.js", () => ({
|
||||||
@@ -48,7 +49,7 @@ function makeCfg(home: string) {
|
|||||||
},
|
},
|
||||||
channels: { whatsapp: { allowFrom: ["*"] } },
|
channels: { whatsapp: { allowFrom: ["*"] } },
|
||||||
session: { store: path.join(home, "sessions.json") },
|
session: { store: path.join(home, "sessions.json") },
|
||||||
};
|
} as unknown as OpenClawConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("getReplyFromConfig media note plumbing", () => {
|
describe("getReplyFromConfig media note plumbing", () => {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import type { OpenClawConfig } from "../config/config.js";
|
||||||
import { createTempHomeHarness, makeReplyConfig } from "./reply.test-harness.js";
|
import { createTempHomeHarness, makeReplyConfig } from "./reply.test-harness.js";
|
||||||
|
|
||||||
const agentMocks = vi.hoisted(() => ({
|
const agentMocks = vi.hoisted(() => ({
|
||||||
@@ -70,7 +71,11 @@ describe("RawBody directive parsing", () => {
|
|||||||
CommandAuthorized: true,
|
CommandAuthorized: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await getReplyFromConfig(groupMessageCtx, {}, makeReplyConfig(home));
|
const res = await getReplyFromConfig(
|
||||||
|
groupMessageCtx,
|
||||||
|
{},
|
||||||
|
makeReplyConfig(home) as OpenClawConfig,
|
||||||
|
);
|
||||||
|
|
||||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||||
expect(text).toBe("ok");
|
expect(text).toBe("ok");
|
||||||
|
|||||||
@@ -16,6 +16,14 @@ beforeAll(async () => {
|
|||||||
|
|
||||||
installTriggerHandlingE2eTestHooks();
|
installTriggerHandlingE2eTestHooks();
|
||||||
|
|
||||||
|
function requireSessionStorePath(cfg: { session?: { store?: string } }): string {
|
||||||
|
const storePath = cfg.session?.store;
|
||||||
|
if (!storePath) {
|
||||||
|
throw new Error("expected session store path");
|
||||||
|
}
|
||||||
|
return storePath;
|
||||||
|
}
|
||||||
|
|
||||||
describe("trigger handling", () => {
|
describe("trigger handling", () => {
|
||||||
it("allows approved sender to toggle elevated mode", async () => {
|
it("allows approved sender to toggle elevated mode", async () => {
|
||||||
await withTempHome(async (home) => {
|
await withTempHome(async (home) => {
|
||||||
@@ -46,7 +54,7 @@ describe("trigger handling", () => {
|
|||||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||||
expect(text).toContain("tools.elevated.enabled");
|
expect(text).toContain("tools.elevated.enabled");
|
||||||
|
|
||||||
const storeRaw = await fs.readFile(cfg.session!.store, "utf-8");
|
const storeRaw = await fs.readFile(requireSessionStorePath(cfg), "utf-8");
|
||||||
const store = JSON.parse(storeRaw) as Record<string, { elevatedLevel?: string }>;
|
const store = JSON.parse(storeRaw) as Record<string, { elevatedLevel?: string }>;
|
||||||
expect(store[MAIN_SESSION_KEY]?.elevatedLevel).toBeUndefined();
|
expect(store[MAIN_SESSION_KEY]?.elevatedLevel).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,6 +16,14 @@ beforeAll(async () => {
|
|||||||
|
|
||||||
installTriggerHandlingE2eTestHooks();
|
installTriggerHandlingE2eTestHooks();
|
||||||
|
|
||||||
|
function requireSessionStorePath(cfg: { session?: { store?: string } }): string {
|
||||||
|
const storePath = cfg.session?.store;
|
||||||
|
if (!storePath) {
|
||||||
|
throw new Error("expected session store path");
|
||||||
|
}
|
||||||
|
return storePath;
|
||||||
|
}
|
||||||
|
|
||||||
describe("trigger handling", () => {
|
describe("trigger handling", () => {
|
||||||
it("allows elevated off in groups without mention", async () => {
|
it("allows elevated off in groups without mention", async () => {
|
||||||
await withTempHome(async (home) => {
|
await withTempHome(async (home) => {
|
||||||
@@ -38,7 +46,7 @@ describe("trigger handling", () => {
|
|||||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||||
expect(text).toContain("Elevated mode disabled.");
|
expect(text).toContain("Elevated mode disabled.");
|
||||||
|
|
||||||
const store = loadSessionStore(cfg.session!.store);
|
const store = loadSessionStore(requireSessionStorePath(cfg));
|
||||||
expect(store["agent:main:whatsapp:group:123@g.us"]?.elevatedLevel).toBe("off");
|
expect(store["agent:main:whatsapp:group:123@g.us"]?.elevatedLevel).toBe("off");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -64,7 +72,7 @@ describe("trigger handling", () => {
|
|||||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||||
expect(text).toContain("Elevated mode set to ask");
|
expect(text).toContain("Elevated mode set to ask");
|
||||||
|
|
||||||
const storeRaw = await fs.readFile(cfg.session!.store, "utf-8");
|
const storeRaw = await fs.readFile(requireSessionStorePath(cfg), "utf-8");
|
||||||
const store = JSON.parse(storeRaw) as Record<string, { elevatedLevel?: string }>;
|
const store = JSON.parse(storeRaw) as Record<string, { elevatedLevel?: string }>;
|
||||||
expect(store["agent:main:whatsapp:group:123@g.us"]?.elevatedLevel).toBe("on");
|
expect(store["agent:main:whatsapp:group:123@g.us"]?.elevatedLevel).toBe("on");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { beforeAll, describe, expect, it } from "vitest";
|
import { beforeAll, describe, expect, it } from "vitest";
|
||||||
|
import type { OpenClawConfig } from "../config/config.js";
|
||||||
import {
|
import {
|
||||||
getRunEmbeddedPiAgentMock,
|
getRunEmbeddedPiAgentMock,
|
||||||
installTriggerHandlingE2eTestHooks,
|
installTriggerHandlingE2eTestHooks,
|
||||||
@@ -17,6 +18,14 @@ beforeAll(async () => {
|
|||||||
|
|
||||||
installTriggerHandlingE2eTestHooks();
|
installTriggerHandlingE2eTestHooks();
|
||||||
|
|
||||||
|
function requireSessionStorePath(cfg: { session?: { store?: string } }): string {
|
||||||
|
const storePath = cfg.session?.store;
|
||||||
|
if (!storePath) {
|
||||||
|
throw new Error("expected session store path");
|
||||||
|
}
|
||||||
|
return storePath;
|
||||||
|
}
|
||||||
|
|
||||||
describe("trigger handling", () => {
|
describe("trigger handling", () => {
|
||||||
it("ignores inline elevated directive for unapproved sender", async () => {
|
it("ignores inline elevated directive for unapproved sender", async () => {
|
||||||
await withTempHome(async (home) => {
|
await withTempHome(async (home) => {
|
||||||
@@ -56,7 +65,7 @@ describe("trigger handling", () => {
|
|||||||
},
|
},
|
||||||
tools: { elevated: { allowFrom: { discord: ["steipete"] } } },
|
tools: { elevated: { allowFrom: { discord: ["steipete"] } } },
|
||||||
session: { store: join(home, "sessions.json") },
|
session: { store: join(home, "sessions.json") },
|
||||||
};
|
} as OpenClawConfig;
|
||||||
|
|
||||||
const res = await getReplyFromConfig(
|
const res = await getReplyFromConfig(
|
||||||
{
|
{
|
||||||
@@ -75,7 +84,7 @@ describe("trigger handling", () => {
|
|||||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||||
expect(text).toContain("Elevated mode set to ask");
|
expect(text).toContain("Elevated mode set to ask");
|
||||||
|
|
||||||
const storeRaw = await fs.readFile(cfg.session.store, "utf-8");
|
const storeRaw = await fs.readFile(requireSessionStorePath(cfg), "utf-8");
|
||||||
const store = JSON.parse(storeRaw) as Record<string, { elevatedLevel?: string }>;
|
const store = JSON.parse(storeRaw) as Record<string, { elevatedLevel?: string }>;
|
||||||
expect(store[MAIN_SESSION_KEY]?.elevatedLevel).toBe("on");
|
expect(store[MAIN_SESSION_KEY]?.elevatedLevel).toBe("on");
|
||||||
});
|
});
|
||||||
@@ -95,7 +104,7 @@ describe("trigger handling", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
session: { store: join(home, "sessions.json") },
|
session: { store: join(home, "sessions.json") },
|
||||||
};
|
} as OpenClawConfig;
|
||||||
|
|
||||||
const res = await getReplyFromConfig(
|
const res = await getReplyFromConfig(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -34,9 +34,17 @@ function mockEmbeddedOkPayload() {
|
|||||||
return runEmbeddedPiAgentMock;
|
return runEmbeddedPiAgentMock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function requireSessionStorePath(cfg: { session?: { store?: string } }): string {
|
||||||
|
const storePath = cfg.session?.store;
|
||||||
|
if (!storePath) {
|
||||||
|
throw new Error("expected session store path");
|
||||||
|
}
|
||||||
|
return storePath;
|
||||||
|
}
|
||||||
|
|
||||||
async function writeStoredModelOverride(cfg: ReturnType<typeof makeCfg>): Promise<void> {
|
async function writeStoredModelOverride(cfg: ReturnType<typeof makeCfg>): Promise<void> {
|
||||||
await fs.writeFile(
|
await fs.writeFile(
|
||||||
cfg.session!.store,
|
requireSessionStorePath(cfg),
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
[MAIN_SESSION_KEY]: {
|
[MAIN_SESSION_KEY]: {
|
||||||
sessionId: "main",
|
sessionId: "main",
|
||||||
@@ -154,7 +162,7 @@ describe("trigger handling", () => {
|
|||||||
);
|
);
|
||||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||||
expect(text).toContain("Group activation set to always");
|
expect(text).toContain("Group activation set to always");
|
||||||
const store = JSON.parse(await fs.readFile(cfg.session!.store, "utf-8")) as Record<
|
const store = JSON.parse(await fs.readFile(requireSessionStorePath(cfg), "utf-8")) as Record<
|
||||||
string,
|
string,
|
||||||
{ groupActivation?: string }
|
{ groupActivation?: string }
|
||||||
>;
|
>;
|
||||||
|
|||||||
@@ -40,6 +40,14 @@ function makeUnauthorizedWhatsAppCfg(home: string) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function requireSessionStorePath(cfg: { session?: { store?: string } }): string {
|
||||||
|
const storePath = cfg.session?.store;
|
||||||
|
if (!storePath) {
|
||||||
|
throw new Error("expected session store path");
|
||||||
|
}
|
||||||
|
return storePath;
|
||||||
|
}
|
||||||
|
|
||||||
async function runInlineUnauthorizedCommand(params: {
|
async function runInlineUnauthorizedCommand(params: {
|
||||||
home: string;
|
home: string;
|
||||||
command: "/status" | "/help";
|
command: "/status" | "/help";
|
||||||
@@ -143,7 +151,7 @@ describe("trigger handling", () => {
|
|||||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||||
expect(text).toContain("Send policy set to off");
|
expect(text).toContain("Send policy set to off");
|
||||||
|
|
||||||
const storeRaw = await fs.readFile(cfg.session!.store, "utf-8");
|
const storeRaw = await fs.readFile(requireSessionStorePath(cfg), "utf-8");
|
||||||
const store = JSON.parse(storeRaw) as Record<string, { sendPolicy?: string }>;
|
const store = JSON.parse(storeRaw) as Record<string, { sendPolicy?: string }>;
|
||||||
expect(store[MAIN_SESSION_KEY]?.sendPolicy).toBe("deny");
|
expect(store[MAIN_SESSION_KEY]?.sendPolicy).toBe("deny");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,6 +18,14 @@ beforeAll(async () => {
|
|||||||
|
|
||||||
installTriggerHandlingE2eTestHooks();
|
installTriggerHandlingE2eTestHooks();
|
||||||
|
|
||||||
|
function requireSessionStorePath(cfg: { session?: { store?: string } }): string {
|
||||||
|
const storePath = cfg.session?.store;
|
||||||
|
if (!storePath) {
|
||||||
|
throw new Error("expected session store path");
|
||||||
|
}
|
||||||
|
return storePath;
|
||||||
|
}
|
||||||
|
|
||||||
describe("trigger handling", () => {
|
describe("trigger handling", () => {
|
||||||
it("reports active auth profile and key snippet in status", async () => {
|
it("reports active auth profile and key snippet in status", async () => {
|
||||||
await withTempHome(async (home) => {
|
await withTempHome(async (home) => {
|
||||||
@@ -50,7 +58,7 @@ describe("trigger handling", () => {
|
|||||||
Provider: "whatsapp",
|
Provider: "whatsapp",
|
||||||
} as Parameters<typeof resolveSessionKey>[1]);
|
} as Parameters<typeof resolveSessionKey>[1]);
|
||||||
await fs.writeFile(
|
await fs.writeFile(
|
||||||
cfg.session!.store,
|
requireSessionStorePath(cfg),
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
{
|
{
|
||||||
[sessionKey]: {
|
[sessionKey]: {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { beforeAll, describe, expect, it } from "vitest";
|
import { beforeAll, describe, expect, it } from "vitest";
|
||||||
import { normalizeTestText } from "../../test/helpers/normalize-text.js";
|
import { normalizeTestText } from "../../test/helpers/normalize-text.js";
|
||||||
|
import type { OpenClawConfig } from "../config/config.js";
|
||||||
import {
|
import {
|
||||||
getRunEmbeddedPiAgentMock,
|
getRunEmbeddedPiAgentMock,
|
||||||
installTriggerHandlingE2eTestHooks,
|
installTriggerHandlingE2eTestHooks,
|
||||||
@@ -47,7 +48,7 @@ describe("trigger handling", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
} as unknown as OpenClawConfig;
|
||||||
const res = await getReplyFromConfig(modelStatusCtx, {}, cfg);
|
const res = await getReplyFromConfig(modelStatusCtx, {}, cfg);
|
||||||
|
|
||||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||||
@@ -78,7 +79,7 @@ describe("trigger handling", () => {
|
|||||||
it("restarts when enabled", async () => {
|
it("restarts when enabled", async () => {
|
||||||
await withTempHome(async (home) => {
|
await withTempHome(async (home) => {
|
||||||
const runEmbeddedPiAgentMock = getRunEmbeddedPiAgentMock();
|
const runEmbeddedPiAgentMock = getRunEmbeddedPiAgentMock();
|
||||||
const cfg = { ...makeCfg(home), commands: { restart: true } };
|
const cfg = { ...makeCfg(home), commands: { restart: true } } as OpenClawConfig;
|
||||||
const res = await getReplyFromConfig(
|
const res = await getReplyFromConfig(
|
||||||
{
|
{
|
||||||
Body: "/restart",
|
Body: "/restart",
|
||||||
|
|||||||
@@ -16,6 +16,14 @@ installTriggerHandlingE2eTestHooks();
|
|||||||
|
|
||||||
const DEFAULT_SESSION_KEY = "telegram:slash:111";
|
const DEFAULT_SESSION_KEY = "telegram:slash:111";
|
||||||
|
|
||||||
|
function requireSessionStorePath(cfg: { session?: { store?: string } }): string {
|
||||||
|
const storePath = cfg.session?.store;
|
||||||
|
if (!storePath) {
|
||||||
|
throw new Error("expected session store path");
|
||||||
|
}
|
||||||
|
return storePath;
|
||||||
|
}
|
||||||
|
|
||||||
function makeTelegramModelCommand(body: string, sessionKey = DEFAULT_SESSION_KEY) {
|
function makeTelegramModelCommand(body: string, sessionKey = DEFAULT_SESSION_KEY) {
|
||||||
return {
|
return {
|
||||||
Body: body,
|
Body: body,
|
||||||
@@ -78,7 +86,7 @@ describe("trigger handling", () => {
|
|||||||
|
|
||||||
expect(normalized).toContain("Model set to openrouter/anthropic/claude-opus-4-5");
|
expect(normalized).toContain("Model set to openrouter/anthropic/claude-opus-4-5");
|
||||||
|
|
||||||
const store = loadSessionStore(cfg.session!.store);
|
const store = loadSessionStore(requireSessionStorePath(cfg));
|
||||||
expect(store[sessionKey]?.providerOverride).toBe("openrouter");
|
expect(store[sessionKey]?.providerOverride).toBe("openrouter");
|
||||||
expect(store[sessionKey]?.modelOverride).toBe("anthropic/claude-opus-4-5");
|
expect(store[sessionKey]?.modelOverride).toBe("anthropic/claude-opus-4-5");
|
||||||
});
|
});
|
||||||
@@ -92,7 +100,7 @@ describe("trigger handling", () => {
|
|||||||
expect(normalized).toContain("Browse: /models or /models <provider>");
|
expect(normalized).toContain("Browse: /models or /models <provider>");
|
||||||
expect(normalized).toContain("Switch: /model <provider/model>");
|
expect(normalized).toContain("Switch: /model <provider/model>");
|
||||||
|
|
||||||
const store = loadSessionStore(cfg.session!.store);
|
const store = loadSessionStore(requireSessionStorePath(cfg));
|
||||||
expect(store[sessionKey]?.providerOverride).toBeUndefined();
|
expect(store[sessionKey]?.providerOverride).toBeUndefined();
|
||||||
expect(store[sessionKey]?.modelOverride).toBeUndefined();
|
expect(store[sessionKey]?.modelOverride).toBeUndefined();
|
||||||
});
|
});
|
||||||
@@ -107,7 +115,7 @@ describe("trigger handling", () => {
|
|||||||
|
|
||||||
expect(normalized).toContain("Model reset to default (anthropic/claude-opus-4-5)");
|
expect(normalized).toContain("Model reset to default (anthropic/claude-opus-4-5)");
|
||||||
|
|
||||||
const store = loadSessionStore(cfg.session!.store);
|
const store = loadSessionStore(requireSessionStorePath(cfg));
|
||||||
expect(store[sessionKey]?.providerOverride).toBeUndefined();
|
expect(store[sessionKey]?.providerOverride).toBeUndefined();
|
||||||
expect(store[sessionKey]?.modelOverride).toBeUndefined();
|
expect(store[sessionKey]?.modelOverride).toBeUndefined();
|
||||||
});
|
});
|
||||||
@@ -119,7 +127,7 @@ describe("trigger handling", () => {
|
|||||||
|
|
||||||
expect(normalized).toContain("Model set to openai/gpt-5.2");
|
expect(normalized).toContain("Model set to openai/gpt-5.2");
|
||||||
|
|
||||||
const store = loadSessionStore(cfg.session!.store);
|
const store = loadSessionStore(requireSessionStorePath(cfg));
|
||||||
expect(store[sessionKey]?.providerOverride).toBe("openai");
|
expect(store[sessionKey]?.providerOverride).toBe("openai");
|
||||||
expect(store[sessionKey]?.modelOverride).toBe("gpt-5.2");
|
expect(store[sessionKey]?.modelOverride).toBe("gpt-5.2");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -642,10 +642,11 @@ describe("runReplyAgent claude-cli routing", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
it("uses claude-cli runner for claude-cli provider", async () => {
|
it("uses claude-cli runner for claude-cli provider", async () => {
|
||||||
const randomSpy = vi.spyOn(crypto, "randomUUID").mockReturnValue("run-1");
|
const runId = "00000000-0000-0000-0000-000000000001";
|
||||||
|
const randomSpy = vi.spyOn(crypto, "randomUUID").mockReturnValue(runId);
|
||||||
const lifecyclePhases: string[] = [];
|
const lifecyclePhases: string[] = [];
|
||||||
const unsubscribe = onAgentEvent((evt) => {
|
const unsubscribe = onAgentEvent((evt) => {
|
||||||
if (evt.runId !== "run-1") {
|
if (evt.runId !== runId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (evt.stream !== "lifecycle") {
|
if (evt.stream !== "lifecycle") {
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ describe("/model chat UX", () => {
|
|||||||
describe("handleDirectiveOnly model persist behavior (fixes #1435)", () => {
|
describe("handleDirectiveOnly model persist behavior (fixes #1435)", () => {
|
||||||
const allowedModelKeys = new Set(["anthropic/claude-opus-4-5", "openai/gpt-4o"]);
|
const allowedModelKeys = new Set(["anthropic/claude-opus-4-5", "openai/gpt-4o"]);
|
||||||
const allowedModelCatalog = [
|
const allowedModelCatalog = [
|
||||||
{ provider: "anthropic", id: "claude-opus-4-5" },
|
{ provider: "anthropic", id: "claude-opus-4-5", name: "Claude Opus 4.5" },
|
||||||
{ provider: "openai", id: "gpt-4o" },
|
{ provider: "openai", id: "gpt-4o", name: "GPT-4o" },
|
||||||
];
|
];
|
||||||
const sessionKey = "agent:main:dm:1";
|
const sessionKey = "agent:main:dm:1";
|
||||||
const storePath = "/tmp/sessions.json";
|
const storePath = "/tmp/sessions.json";
|
||||||
|
|||||||
@@ -66,12 +66,12 @@ describe("handleInlineActions", () => {
|
|||||||
elevatedEnabled: false,
|
elevatedEnabled: false,
|
||||||
elevatedAllowed: false,
|
elevatedAllowed: false,
|
||||||
elevatedFailures: [],
|
elevatedFailures: [],
|
||||||
defaultActivation: () => ({ enabled: true, message: "" }),
|
defaultActivation: () => "always",
|
||||||
resolvedThinkLevel: undefined,
|
resolvedThinkLevel: undefined,
|
||||||
resolvedVerboseLevel: undefined,
|
resolvedVerboseLevel: undefined,
|
||||||
resolvedReasoningLevel: "off",
|
resolvedReasoningLevel: "off",
|
||||||
resolvedElevatedLevel: "off",
|
resolvedElevatedLevel: "off",
|
||||||
resolveDefaultThinkingLevel: () => "off",
|
resolveDefaultThinkingLevel: async () => "off",
|
||||||
provider: "openai",
|
provider: "openai",
|
||||||
model: "gpt-4o-mini",
|
model: "gpt-4o-mini",
|
||||||
contextTokens: 0,
|
contextTokens: 0,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
import type { OpenClawConfig } from "../../config/config.js";
|
||||||
import { resolveMemoryFlushPromptForRun } from "./memory-flush.js";
|
import { resolveMemoryFlushPromptForRun } from "./memory-flush.js";
|
||||||
|
|
||||||
describe("resolveMemoryFlushPromptForRun", () => {
|
describe("resolveMemoryFlushPromptForRun", () => {
|
||||||
@@ -9,7 +10,7 @@ describe("resolveMemoryFlushPromptForRun", () => {
|
|||||||
timeFormat: "12",
|
timeFormat: "12",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
} as OpenClawConfig;
|
||||||
|
|
||||||
it("replaces YYYY-MM-DD using user timezone and appends current time", () => {
|
it("replaces YYYY-MM-DD using user timezone and appends current time", () => {
|
||||||
const prompt = resolveMemoryFlushPromptForRun({
|
const prompt = resolveMemoryFlushPromptForRun({
|
||||||
|
|||||||
@@ -612,7 +612,7 @@ let previousRuntimeError: typeof defaultRuntime.error;
|
|||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
previousRuntimeError = defaultRuntime.error;
|
previousRuntimeError = defaultRuntime.error;
|
||||||
defaultRuntime.error = undefined;
|
defaultRuntime.error = (() => {}) as typeof defaultRuntime.error;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
@@ -1160,7 +1160,8 @@ describe("createReplyDispatcher", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("fires onIdle when the queue drains", async () => {
|
it("fires onIdle when the queue drains", async () => {
|
||||||
const deliver = vi.fn(async () => await new Promise((resolve) => setTimeout(resolve, 5)));
|
const deliver: Parameters<typeof createReplyDispatcher>[0]["deliver"] = async () =>
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 5));
|
||||||
const onIdle = vi.fn();
|
const onIdle = vi.fn();
|
||||||
const dispatcher = createReplyDispatcher({ deliver, onIdle });
|
const dispatcher = createReplyDispatcher({ deliver, onIdle });
|
||||||
|
|
||||||
|
|||||||
@@ -369,9 +369,9 @@ describe("createTypingSignaler", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await signaler.signalTextDelta("hello");
|
await signaler.signalTextDelta("hello");
|
||||||
typing.startTypingLoop.mockClear();
|
(typing.startTypingLoop as ReturnType<typeof vi.fn>).mockClear();
|
||||||
typing.startTypingOnText.mockClear();
|
(typing.startTypingOnText as ReturnType<typeof vi.fn>).mockClear();
|
||||||
typing.refreshTypingTtl.mockClear();
|
(typing.refreshTypingTtl as ReturnType<typeof vi.fn>).mockClear();
|
||||||
await signaler.signalToolStart();
|
await signaler.signalToolStart();
|
||||||
|
|
||||||
expect(typing.refreshTypingTtl).toHaveBeenCalled();
|
expect(typing.refreshTypingTtl).toHaveBeenCalled();
|
||||||
|
|||||||
@@ -64,6 +64,9 @@ const { routeReply } = await import("./route-reply.js");
|
|||||||
const createRegistry = (channels: PluginRegistry["channels"]): PluginRegistry => ({
|
const createRegistry = (channels: PluginRegistry["channels"]): PluginRegistry => ({
|
||||||
plugins: [],
|
plugins: [],
|
||||||
tools: [],
|
tools: [],
|
||||||
|
hooks: [],
|
||||||
|
typedHooks: [],
|
||||||
|
commands: [],
|
||||||
channels,
|
channels,
|
||||||
providers: [],
|
providers: [],
|
||||||
gatewayHandlers: {},
|
gatewayHandlers: {},
|
||||||
|
|||||||
@@ -132,7 +132,9 @@ describe("buildStatusMessage", () => {
|
|||||||
sessionKey: "agent:main:main",
|
sessionKey: "agent:main:main",
|
||||||
queue: { mode: "none" },
|
queue: { mode: "none" },
|
||||||
mediaDecisions: [
|
mediaDecisions: [
|
||||||
createSuccessfulImageMediaDecision(),
|
createSuccessfulImageMediaDecision() as unknown as NonNullable<
|
||||||
|
Parameters<typeof buildStatusMessage>[0]["mediaDecisions"]
|
||||||
|
>[number],
|
||||||
{
|
{
|
||||||
capability: "audio",
|
capability: "audio",
|
||||||
outcome: "skipped",
|
outcome: "skipped",
|
||||||
|
|||||||
Reference in New Issue
Block a user