Normalize Codex paseo_voice.speak MCP calls and wrap spoken input
Broaden the Codex speak tool name matcher to recognize paseo_voice.speak MCP calls alongside the existing paseo.speak convention. Wrap voice transcription input in <spoken-input> tags so agents can distinguish spoken from typed messages.
This commit is contained in:
@@ -539,6 +539,26 @@ describe("codex tool-call mapper", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("normalizes codex paseo_voice.speak mcp calls and extracts spoken text", () => {
|
||||
const item = mapCodexToolCallFromThreadItem({
|
||||
type: "mcpToolCall",
|
||||
id: "codex-speak-thread-2",
|
||||
status: "completed",
|
||||
server: "paseo_voice",
|
||||
tool: "speak",
|
||||
arguments: { text: "Voice response from Codex via paseo_voice." },
|
||||
result: { ok: true },
|
||||
});
|
||||
|
||||
expect(item).toBeTruthy();
|
||||
expect(item?.name).toBe("speak");
|
||||
expect(item?.detail).toEqual({
|
||||
type: "unknown",
|
||||
input: "Voice response from Codex via paseo_voice.",
|
||||
output: null,
|
||||
});
|
||||
});
|
||||
|
||||
it("normalizes codex paseo speak rollout names and extracts spoken text", () => {
|
||||
const item = expectMapped(
|
||||
mapCodexRolloutToolCall({
|
||||
|
||||
@@ -3,6 +3,7 @@ import { z } from "zod";
|
||||
import type { ToolCallTimelineItem } from "../../agent-sdk-types.js";
|
||||
import { extractCodexShellOutput, truncateDiffText } from "../tool-call-mapper-utils.js";
|
||||
import { deriveCodexToolDetail, normalizeCodexFilePath } from "./tool-call-detail-parser.js";
|
||||
import { isSpeakToolName } from "../../tool-name-normalization.js";
|
||||
|
||||
type CodexMapperOptions = { cwd?: string | null };
|
||||
|
||||
@@ -67,7 +68,7 @@ const CodexEditToolNameSchema = z.union([
|
||||
z.literal("apply_diff"),
|
||||
]);
|
||||
const CodexSearchToolNameSchema = z.union([z.literal("search"), z.literal("web_search")]);
|
||||
const CodexSpeakToolNameSchema = z.literal("paseo.speak");
|
||||
const CodexSpeakToolNameSchema = z.string().min(1).refine((name) => isSpeakToolName(name.trim()));
|
||||
|
||||
const CodexToolKindSchema = z.enum([
|
||||
"shell",
|
||||
|
||||
@@ -133,6 +133,7 @@ import {
|
||||
buildVoiceAgentMcpServerConfig,
|
||||
buildVoiceModeSystemPrompt,
|
||||
stripVoiceModeSystemPrompt,
|
||||
wrapSpokenInput,
|
||||
} from "./voice-config.js";
|
||||
import { isVoicePermissionAllowed } from "./voice-permission-policy.js";
|
||||
import {
|
||||
@@ -2834,6 +2835,7 @@ export class Session {
|
||||
messageId?: string,
|
||||
images?: Array<{ data: string; mimeType: string }>,
|
||||
runOptions?: AgentRunOptions,
|
||||
options?: { spokenInput?: boolean },
|
||||
): Promise<void> {
|
||||
this.sessionLogger.info(
|
||||
{ agentId, textPreview: text.substring(0, 50), imageCount: images?.length ?? 0 },
|
||||
@@ -2861,7 +2863,8 @@ export class Session {
|
||||
);
|
||||
}
|
||||
|
||||
const prompt = this.buildAgentPrompt(text, images);
|
||||
const promptText = options?.spokenInput ? wrapSpokenInput(text) : text;
|
||||
const prompt = this.buildAgentPrompt(promptText, images);
|
||||
|
||||
this.startAgentStream(agentId, prompt, runOptions);
|
||||
}
|
||||
@@ -7019,9 +7022,14 @@ export class Session {
|
||||
return;
|
||||
}
|
||||
|
||||
// Route voice utterances through the same send path as regular text input:
|
||||
// interrupt-if-running, record message, then start a new stream.
|
||||
await this.handleSendAgentMessage(agentId, result.text);
|
||||
await this.handleSendAgentMessage(
|
||||
agentId,
|
||||
result.text,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
{ spokenInput: true },
|
||||
);
|
||||
await this.flushPendingAudioSegments("transcription complete");
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,10 @@ export function buildVoiceModeSystemPrompt(existing: string | undefined, enabled
|
||||
.join("\n\n");
|
||||
}
|
||||
|
||||
export function wrapSpokenInput(text: string): string {
|
||||
return `<spoken-input>\n${text}\n</spoken-input>`;
|
||||
}
|
||||
|
||||
export function buildVoiceAgentMcpServerConfig(params: {
|
||||
command: string;
|
||||
baseArgs: string[];
|
||||
|
||||
Reference in New Issue
Block a user