Update files

This commit is contained in:
Mohamed Boudra
2026-02-09 21:26:12 +07:00
parent ab23d5d9b1
commit 7a21a5cce1
7 changed files with 465 additions and 167 deletions
+8 -41
View File
@@ -68,6 +68,11 @@ import type {
} from "./agent/agent-sdk-types.js";
import { AgentStorage, type StoredAgentRecord } from "./agent/agent-storage.js";
import { isValidAgentProvider, AGENT_PROVIDER_IDS } from "./agent/provider-manifest.js";
import {
buildVoiceAgentMcpServerConfig,
buildVoiceModeSystemPrompt,
stripVoiceModeSystemPrompt,
} from "./voice-config.js";
import { isVoicePermissionAllowed } from "./voice-permission-policy.js";
import {
listDirectoryEntries,
@@ -130,18 +135,6 @@ const MAX_AGENTS_PER_PROJECT = 5;
* Uses Claude Haiku for speed and cost efficiency.
*/
const AUTO_GEN_MODEL = "haiku";
const VOICE_AGENT_SYSTEM_INSTRUCTION = [
"You are the Paseo voice assistant.",
"The user cannot see your chat messages or tool calls.",
"Always use the speak tool for all user-facing communication.",
"Before calling any non-speak tool, first call speak with a short acknowledgement of what you heard and what you will do next.",
"For long-running work, use speak to provide progress updates before and during execution.",
"Treat the user input as transcribed speech and confirm intent in your first speak response to catch transcription errors early.",
"If the transcription seems incomplete, cut off, or ambiguous, ask a clarifying question via speak before taking action.",
"Use concise plain language suitable for speech output.",
"Never use bash, file-edit, or web tools directly.",
"Only use the paseo MCP tools.",
].join(" ");
function deriveRemoteProjectKey(remoteUrl: string | null): string | null {
if (!remoteUrl) {
@@ -222,25 +215,6 @@ function escapeXmlText(value: string): string {
.replace(/>/g, ">");
}
export function buildVoiceAgentMcpServerConfig(params: {
command: string;
baseArgs: string[];
socketPath: string;
env?: Record<string, string>;
}): {
type: "stdio";
command: string;
args: string[];
env?: Record<string, string>;
} {
return {
type: "stdio",
command: params.command,
args: [...params.baseArgs, "--socket", params.socketPath],
...(params.env ? { env: params.env } : {}),
};
}
type ProcessingPhase = "idle" | "transcribing" | "llm";
type NormalizedGitOptions = {
@@ -1541,13 +1515,6 @@ export class Session {
return JSON.parse(JSON.stringify(servers)) as Record<string, McpServerConfig>;
}
private buildVoiceModeSystemPrompt(existing?: string): string {
const chunks = [existing?.trim(), VOICE_AGENT_SYSTEM_INSTRUCTION].filter(
(value): value is string => Boolean(value && value.length > 0)
);
return chunks.join("\n\n");
}
private buildVoiceModeMcpServers(
existing: Record<string, McpServerConfig> | undefined,
socketPath: string
@@ -1579,12 +1546,12 @@ export class Session {
this.registerVoiceBridgeForAgent(agentId);
const baseConfig: VoiceModeBaseConfig = {
systemPrompt: existing.config.systemPrompt,
systemPrompt: stripVoiceModeSystemPrompt(existing.config.systemPrompt),
mcpServers: this.cloneMcpServers(existing.config.mcpServers),
};
this.voiceModeBaseConfig = baseConfig;
const refreshOverrides: Partial<AgentSessionConfig> = {
systemPrompt: this.buildVoiceModeSystemPrompt(baseConfig.systemPrompt),
systemPrompt: buildVoiceModeSystemPrompt(baseConfig.systemPrompt, true),
mcpServers: this.buildVoiceModeMcpServers(baseConfig.mcpServers, socketPath),
};
@@ -1625,7 +1592,7 @@ export class Session {
const baseConfig = this.voiceModeBaseConfig;
try {
await this.agentManager.refreshAgentFromPersistence(agentId, {
systemPrompt: baseConfig.systemPrompt,
systemPrompt: buildVoiceModeSystemPrompt(baseConfig.systemPrompt, false),
mcpServers: this.cloneMcpServers(baseConfig.mcpServers),
});
} catch (error) {
@@ -1,6 +1,10 @@
import { describe, expect, test } from "vitest";
import { buildVoiceAgentMcpServerConfig } from "./session.js";
import {
buildVoiceAgentMcpServerConfig,
buildVoiceModeSystemPrompt,
stripVoiceModeSystemPrompt,
} from "./voice-config.js";
describe("voice MCP stdio config", () => {
test("builds stdio MCP config for voice agent", () => {
@@ -25,3 +29,48 @@ describe("voice MCP stdio config", () => {
});
});
});
describe("voice mode prompt instructions", () => {
test("builds enabled voice instructions and preserves base prompt", () => {
const prompt = buildVoiceModeSystemPrompt("Base system prompt", true);
expect(prompt).toContain("Base system prompt");
expect(prompt).toContain("<paseo_voice_mode>");
expect(prompt).toContain("Paseo voice mode is now on.");
expect(prompt).toContain("Always use the speak tool for all user-facing communication.");
expect(prompt).toContain("</paseo_voice_mode>");
});
test("builds disabled voice instructions and supersedes previous voice block", () => {
const existing = [
"Base system prompt",
"<paseo_voice_mode>",
"legacy voice instruction",
"</paseo_voice_mode>",
].join("\n\n");
const prompt = buildVoiceModeSystemPrompt(existing, false);
expect(prompt).toContain("Base system prompt");
expect(prompt).toContain("Paseo voice mode is now off.");
expect(prompt).toContain("Ignore any earlier Paseo voice mode instructions in this thread.");
expect(prompt.match(/<paseo_voice_mode>/g)?.length ?? 0).toBe(1);
expect(prompt).not.toContain("legacy voice instruction");
});
test("strips voice blocks from persisted prompt", () => {
const existing = [
"Base system prompt",
"<paseo_voice_mode>",
"legacy voice instruction",
"</paseo_voice_mode>",
].join("\n\n");
expect(stripVoiceModeSystemPrompt(existing)).toBe("Base system prompt");
expect(
stripVoiceModeSystemPrompt(
["<paseo_voice_mode>", "legacy voice instruction", "</paseo_voice_mode>"].join("\n\n")
)
).toBeUndefined();
});
});
@@ -0,0 +1,75 @@
const VOICE_PROMPT_BLOCK_START = "<paseo_voice_mode>";
const VOICE_PROMPT_BLOCK_END = "</paseo_voice_mode>";
const VOICE_AGENT_SYSTEM_INSTRUCTION = [
"Paseo voice mode is now on.",
"You are the Paseo voice assistant.",
"The user cannot see your chat messages or tool calls.",
"Always use the speak tool for all user-facing communication.",
"Before calling any non-speak tool, first call speak with a short acknowledgement of what you heard and what you will do next.",
"For long-running work, use speak to provide progress updates before and during execution.",
"Treat the user input as transcribed speech.",
"If the user intent is clear, proceed without extra confirmation.",
"If the transcription seems incomplete, cut off, ambiguous, or may contain a non-obvious mistake or misspelling, ask a clarifying question via speak before taking action.",
"Use concise plain language suitable for speech output.",
].join(" ");
const VOICE_AGENT_DISABLED_INSTRUCTION = [
"Paseo voice mode is now off.",
"Ignore any earlier Paseo voice mode instructions in this thread.",
].join(" ");
function escapeRegExp(value: string): string {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function makeVoicePromptBlockRegex(): RegExp {
return new RegExp(
`${escapeRegExp(VOICE_PROMPT_BLOCK_START)}[\\s\\S]*?${escapeRegExp(
VOICE_PROMPT_BLOCK_END
)}`,
"g"
);
}
export function stripVoiceModeSystemPrompt(existing?: string): string | undefined {
const trimmed = existing?.trim();
if (!trimmed) {
return undefined;
}
const stripped = trimmed.replace(makeVoicePromptBlockRegex(), "").trim();
return stripped.length > 0 ? stripped : undefined;
}
export function buildVoiceModeSystemPrompt(existing: string | undefined, enabled: boolean): string {
const basePrompt = stripVoiceModeSystemPrompt(existing);
const voiceInstruction = enabled
? VOICE_AGENT_SYSTEM_INSTRUCTION
: VOICE_AGENT_DISABLED_INSTRUCTION;
const voiceBlock = [VOICE_PROMPT_BLOCK_START, voiceInstruction, VOICE_PROMPT_BLOCK_END].join(
"\n"
);
return [basePrompt, voiceBlock]
.filter((entry): entry is string => typeof entry === "string" && entry.length > 0)
.join("\n\n");
}
export function buildVoiceAgentMcpServerConfig(params: {
command: string;
baseArgs: string[];
socketPath: string;
env?: Record<string, string>;
}): {
type: "stdio";
command: string;
args: string[];
env?: Record<string, string>;
} {
return {
type: "stdio",
command: params.command,
args: [...params.baseArgs, "--socket", params.socketPath],
...(params.env ? { env: params.env } : {}),
};
}