mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 04:47:03 -06:00
f43544f752
* refactor: remove vestigial indirection * test: update Slack runtime API guard
124 lines
4.6 KiB
TypeScript
124 lines
4.6 KiB
TypeScript
// Voice Call tests cover realtime agent context plugin behavior.
|
|
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import path from "node:path";
|
|
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import type { OpenClawPluginApi } from "../api.js";
|
|
import type { VoiceCallConfig } from "./config.js";
|
|
import { buildRealtimeVoiceInstructions } from "./realtime-agent-context.js";
|
|
import { createVoiceCallBaseConfig } from "./test-fixtures.js";
|
|
|
|
const tempDirs: string[] = [];
|
|
|
|
afterEach(async () => {
|
|
await Promise.all(tempDirs.splice(0).map((dir) => rm(dir, { recursive: true, force: true })));
|
|
});
|
|
|
|
async function createWorkspace(): Promise<string> {
|
|
const workspaceDir = await mkdtemp(path.join(tmpdir(), "openclaw-voice-context-"));
|
|
tempDirs.push(workspaceDir);
|
|
return workspaceDir;
|
|
}
|
|
|
|
function createConfig(overrides?: Partial<VoiceCallConfig["realtime"]>): VoiceCallConfig {
|
|
const config = createVoiceCallBaseConfig();
|
|
config.agentId = "voice";
|
|
config.realtime.enabled = true;
|
|
config.realtime.instructions = "Base voice instructions.";
|
|
config.realtime = {
|
|
...config.realtime,
|
|
...overrides,
|
|
fastContext: {
|
|
...config.realtime.fastContext,
|
|
...overrides?.fastContext,
|
|
sources: overrides?.fastContext?.sources ?? config.realtime.fastContext.sources,
|
|
},
|
|
agentContext: {
|
|
...config.realtime.agentContext,
|
|
...overrides?.agentContext,
|
|
files: overrides?.agentContext?.files ?? config.realtime.agentContext.files,
|
|
},
|
|
tools: overrides?.tools ?? config.realtime.tools,
|
|
providers: overrides?.providers ?? config.realtime.providers,
|
|
};
|
|
return config;
|
|
}
|
|
|
|
function createAgentRuntime(workspaceDir: string): OpenClawPluginApi["runtime"]["agent"] {
|
|
return {
|
|
resolveAgentIdentity: vi.fn(() => ({
|
|
name: "Claw Voice",
|
|
emoji: ":claw:",
|
|
theme: "bright",
|
|
vibe: "snappy",
|
|
creature: "operator",
|
|
})),
|
|
resolveAgentWorkspaceDir: vi.fn(() => workspaceDir),
|
|
} as unknown as OpenClawPluginApi["runtime"]["agent"];
|
|
}
|
|
|
|
describe("buildRealtimeVoiceInstructions", () => {
|
|
it("injects bounded identity and workspace context", async () => {
|
|
const workspaceDir = await createWorkspace();
|
|
await writeFile(path.join(workspaceDir, "SOUL.md"), "Stay quick, direct, and warm.\n");
|
|
await writeFile(path.join(workspaceDir, "IDENTITY.md"), "Name: Claw Voice\nVibe: snappy\n");
|
|
await writeFile(path.join(workspaceDir, "SECRET.md"), "do not include\n");
|
|
|
|
const coreConfig = { agents: { list: [{ id: "voice" }] } } as OpenClawConfig;
|
|
|
|
const instructions = await buildRealtimeVoiceInstructions({
|
|
baseInstructions: "Base voice instructions.",
|
|
config: createConfig({
|
|
consultPolicy: "substantive",
|
|
agentContext: {
|
|
enabled: true,
|
|
maxChars: 2000,
|
|
includeIdentity: true,
|
|
includeWorkspaceFiles: true,
|
|
files: ["SOUL.md", "IDENTITY.md", "../SECRET.md"],
|
|
},
|
|
}),
|
|
coreConfig,
|
|
agentRuntime: createAgentRuntime(workspaceDir),
|
|
agentId: "voice",
|
|
});
|
|
|
|
expect(instructions).toContain("OpenClaw agent voice context:");
|
|
expect(instructions).toContain("Consult behavior:");
|
|
expect(instructions).toContain("Call openclaw_agent_consult before answering requests");
|
|
expect(instructions).toContain("- Agent id: voice");
|
|
expect(instructions).toContain("- Name: Claw Voice");
|
|
expect(instructions).toContain("- Vibe: snappy");
|
|
expect(instructions).toContain("### SOUL.md");
|
|
expect(instructions).toContain("Stay quick, direct, and warm.");
|
|
expect(instructions).toContain("### IDENTITY.md");
|
|
expect(instructions).not.toContain("do not include");
|
|
});
|
|
|
|
it("truncates injected context without splitting UTF-16 surrogate pairs", async () => {
|
|
const agentId = "abc🚀tail";
|
|
const expectedContext = "OpenClaw agent voice context:\n\n- Agent id: abc";
|
|
const config = createConfig({
|
|
agentContext: {
|
|
enabled: true,
|
|
maxChars: expectedContext.length + 33,
|
|
includeIdentity: false,
|
|
includeWorkspaceFiles: false,
|
|
files: [],
|
|
},
|
|
});
|
|
config.agentId = agentId;
|
|
|
|
const instructions = await buildRealtimeVoiceInstructions({
|
|
baseInstructions: "Base voice instructions.",
|
|
config,
|
|
coreConfig: { agents: { list: [{ id: agentId }] } } as OpenClawConfig,
|
|
agentRuntime: createAgentRuntime("/unused"),
|
|
agentId,
|
|
});
|
|
|
|
expect(instructions).toBe(`Base voice instructions.\n\n${expectedContext}\n[truncated]`);
|
|
});
|
|
});
|