Files
openclaw/extensions/voice-call/src/realtime-agent-context.test.ts
Peter Steinberger f43544f752 refactor: remove vestigial runtime indirection (#124554)
* refactor: remove vestigial indirection

* test: update Slack runtime API guard
2026-08-16 05:31:36 -07:00

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]`);
});
});