@absolutejs/voice 0.0.22-beta.520 → 0.0.22-beta.522
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +4 -0
- package/dist/index.js +161 -0
- package/dist/mcpToolset.d.ts +58 -0
- package/dist/pathwayGenerator.d.ts +27 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -71,6 +71,8 @@ export { createVoiceSessionListRoutes, createVoiceSessionReplayHTMLHandler, crea
|
|
|
71
71
|
export { createVoiceAgent, createVoiceAgentSquad, createVoiceAgentTool, } from "./agent";
|
|
72
72
|
export { createPersonaVoiceCaller, createScriptedVoiceCaller, renderVoiceSimulationTranscript, runVoiceConversationSimulation, } from "./conversationSimulator";
|
|
73
73
|
export type { RunVoiceConversationSimulationInput, VoiceConversationSimulationEndedReason, VoiceConversationSimulationResult, VoicePersonaCallerCompletion, VoiceScriptedCallerStep, VoiceSimulatedSpeaker, VoiceSimulatedTurn, VoiceSimulatorCaller, VoiceSimulatorCallerModel, VoiceSimulatorCallerReply, } from "./conversationSimulator";
|
|
74
|
+
export { createVoiceMCPToolset } from "./mcpToolset";
|
|
75
|
+
export type { CreateVoiceMCPToolsetOptions, MCPClientLike, MCPToolCallResult, MCPToolContentBlock, MCPToolDefinition, VoiceMCPToolResult, } from "./mcpToolset";
|
|
74
76
|
export { createAIVoiceModel } from "./aiVoiceModel";
|
|
75
77
|
export type { CreateAIVoiceModelOptions } from "./aiVoiceModel";
|
|
76
78
|
export { createVoiceAIJudgeCompletion, createVoiceLLMJudge, } from "./llmJudge";
|
|
@@ -353,6 +355,8 @@ export { compileVoicePathwayToAssistant } from "./pathwayCompiler";
|
|
|
353
355
|
export type { CompileVoicePathwayOptions, VoicePathwayCompiledAssistant, VoicePathwayCompilerToolDefinition, } from "./pathwayCompiler";
|
|
354
356
|
export { renderVoicePathwayMermaid, renderVoicePathwayText, visualizeVoicePathway, } from "./pathwayVisualizer";
|
|
355
357
|
export type { VoicePathwayVisualization } from "./pathwayVisualizer";
|
|
358
|
+
export { generateVoicePathwayFromPrompt } from "./pathwayGenerator";
|
|
359
|
+
export type { GenerateVoicePathwayInput, GenerateVoicePathwayResult, VoicePathwayGeneratorCompletion, } from "./pathwayGenerator";
|
|
356
360
|
export { createVoiceCRMRegistry } from "./crmContract";
|
|
357
361
|
export type { CreateVoiceCRMRegistryOptions, VoiceCRMCallActivityInput, VoiceCRMContactSummary, VoiceCRMContract, VoiceCRMLeadInput, VoiceCRMNoteInput, VoiceCRMRegistry, VoiceCRMTaskInput, } from "./crmContract";
|
|
358
362
|
export { createInMemoryVoiceCallerCRMLinkCache, createVoiceCallerCRMLinker, } from "./callerCRMLinker";
|
package/dist/index.js
CHANGED
|
@@ -35415,6 +35415,52 @@ Respond with only your spoken line. When your goal is met or you want to hang up
|
|
|
35415
35415
|
persona: options.persona
|
|
35416
35416
|
};
|
|
35417
35417
|
};
|
|
35418
|
+
// src/mcpToolset.ts
|
|
35419
|
+
var flattenContent = (result) => {
|
|
35420
|
+
const blocks = result.content ?? [];
|
|
35421
|
+
const text = blocks.filter((block) => block.type === "text" && typeof block.text === "string").map((block) => block.text).join(`
|
|
35422
|
+
`).trim();
|
|
35423
|
+
if (text.length > 0)
|
|
35424
|
+
return text;
|
|
35425
|
+
if (result.structuredContent !== undefined) {
|
|
35426
|
+
return JSON.stringify(result.structuredContent);
|
|
35427
|
+
}
|
|
35428
|
+
return "";
|
|
35429
|
+
};
|
|
35430
|
+
var createVoiceMCPToolset = async (options) => {
|
|
35431
|
+
const prefix = options.namePrefix ?? "";
|
|
35432
|
+
const allowed = options.allowedTools ? new Set(options.allowedTools) : undefined;
|
|
35433
|
+
const blocked = options.blockedTools ? new Set(options.blockedTools) : undefined;
|
|
35434
|
+
const listed = await Promise.resolve(options.client.listTools());
|
|
35435
|
+
const tools = [];
|
|
35436
|
+
for (const definition of listed.tools) {
|
|
35437
|
+
if (allowed && !allowed.has(definition.name))
|
|
35438
|
+
continue;
|
|
35439
|
+
if (blocked && blocked.has(definition.name))
|
|
35440
|
+
continue;
|
|
35441
|
+
const exposedName = `${prefix}${definition.name}`;
|
|
35442
|
+
tools.push(createVoiceAgentTool({
|
|
35443
|
+
...definition.description !== undefined ? { description: definition.description } : {},
|
|
35444
|
+
execute: async ({ args }) => {
|
|
35445
|
+
const raw = await Promise.resolve(options.client.callTool({
|
|
35446
|
+
arguments: args,
|
|
35447
|
+
name: definition.name
|
|
35448
|
+
}));
|
|
35449
|
+
const result = {
|
|
35450
|
+
isError: raw.isError === true,
|
|
35451
|
+
raw,
|
|
35452
|
+
text: flattenContent(raw),
|
|
35453
|
+
...raw.structuredContent !== undefined ? { structuredContent: raw.structuredContent } : {}
|
|
35454
|
+
};
|
|
35455
|
+
return result;
|
|
35456
|
+
},
|
|
35457
|
+
name: exposedName,
|
|
35458
|
+
...definition.inputSchema !== undefined ? { parameters: definition.inputSchema } : {},
|
|
35459
|
+
resultToMessage: options.resultToMessage ?? ((result) => result.isError ? `Tool error: ${result.text || "unknown error"}` : result.text || "(no output)")
|
|
35460
|
+
}));
|
|
35461
|
+
}
|
|
35462
|
+
return tools;
|
|
35463
|
+
};
|
|
35418
35464
|
// src/aiVoiceModel.ts
|
|
35419
35465
|
var toProviderMessages = (messages) => {
|
|
35420
35466
|
const out = [];
|
|
@@ -51531,6 +51577,119 @@ var visualizeVoicePathway = (pathway) => ({
|
|
|
51531
51577
|
mermaid: renderVoicePathwayMermaid(pathway),
|
|
51532
51578
|
text: renderVoicePathwayText(pathway)
|
|
51533
51579
|
});
|
|
51580
|
+
// src/pathwayGenerator.ts
|
|
51581
|
+
var SYSTEM_PROMPT = `You design conversation pathways for voice agents as strict JSON.
|
|
51582
|
+
|
|
51583
|
+
A pathway is a state machine. Output ONLY a JSON object with this shape:
|
|
51584
|
+
{
|
|
51585
|
+
"id": "kebab-case-id",
|
|
51586
|
+
"label": "Human readable label",
|
|
51587
|
+
"entryStateId": "<id of the first state>",
|
|
51588
|
+
"slots": [
|
|
51589
|
+
{ "id": "slot_id", "type": "string|number|boolean|date|time|phone|email|currency|choice", "prompt": "what to ask", "required": true, "choices": ["a","b"] }
|
|
51590
|
+
],
|
|
51591
|
+
"states": [
|
|
51592
|
+
{
|
|
51593
|
+
"id": "state_id",
|
|
51594
|
+
"label": "Label",
|
|
51595
|
+
"kind": "entry|collect|branch|action|terminal",
|
|
51596
|
+
"actions": [
|
|
51597
|
+
{ "kind": "say", "text": "..." },
|
|
51598
|
+
{ "kind": "collect-slot", "slotId": "slot_id" },
|
|
51599
|
+
{ "kind": "call-tool", "toolId": "tool_id", "argsFromSlots": ["slot_id"] },
|
|
51600
|
+
{ "kind": "transfer", "destination": "..." },
|
|
51601
|
+
{ "kind": "end-call", "reason": "..." }
|
|
51602
|
+
],
|
|
51603
|
+
"transitions": [
|
|
51604
|
+
{ "to": "next_state", "condition": { "kind": "always" } },
|
|
51605
|
+
{ "to": "x", "condition": { "kind": "slot-filled", "slotId": "slot_id" } },
|
|
51606
|
+
{ "to": "y", "condition": { "kind": "slot-equals", "slotId": "slot_id", "value": "yes" } },
|
|
51607
|
+
{ "to": "z", "condition": { "kind": "fallback" } }
|
|
51608
|
+
]
|
|
51609
|
+
}
|
|
51610
|
+
],
|
|
51611
|
+
"tools": [ { "id": "tool_id", "description": "..." } ]
|
|
51612
|
+
}
|
|
51613
|
+
|
|
51614
|
+
Hard rules:
|
|
51615
|
+
- Exactly one entry state; entryStateId must reference a real state.
|
|
51616
|
+
- At least one terminal state (kind "terminal" or a state with no transitions) must be reachable from entry.
|
|
51617
|
+
- Every transition "to" must reference a defined state id.
|
|
51618
|
+
- Every slotId referenced in actions/conditions must be defined in "slots".
|
|
51619
|
+
- A "fallback" transition, if present, must be the LAST transition in its state.
|
|
51620
|
+
- Do not invent extra fields. Output JSON only \u2014 no prose, no markdown fences.`;
|
|
51621
|
+
var slugify = (value) => value.toLowerCase().trim().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "").slice(0, 60) || "generated-pathway";
|
|
51622
|
+
var extractJson4 = (raw) => {
|
|
51623
|
+
const trimmed = raw.trim();
|
|
51624
|
+
if (!trimmed)
|
|
51625
|
+
throw new Error("Pathway generator returned an empty response");
|
|
51626
|
+
const fenced = /```(?:json)?\s*([\s\S]*?)```/iu.exec(trimmed);
|
|
51627
|
+
const candidate = fenced ? fenced[1].trim() : trimmed;
|
|
51628
|
+
try {
|
|
51629
|
+
return JSON.parse(candidate);
|
|
51630
|
+
} catch {
|
|
51631
|
+
const start = candidate.indexOf("{");
|
|
51632
|
+
const end = candidate.lastIndexOf("}");
|
|
51633
|
+
if (start >= 0 && end > start) {
|
|
51634
|
+
return JSON.parse(candidate.slice(start, end + 1));
|
|
51635
|
+
}
|
|
51636
|
+
throw new Error(`Pathway generator response was not valid JSON: ${raw.slice(0, 200)}`);
|
|
51637
|
+
}
|
|
51638
|
+
};
|
|
51639
|
+
var coercePathway = (parsed, fallbackId) => {
|
|
51640
|
+
if (!parsed || typeof parsed !== "object") {
|
|
51641
|
+
throw new Error("Pathway generator response is not a JSON object");
|
|
51642
|
+
}
|
|
51643
|
+
const root = parsed;
|
|
51644
|
+
return {
|
|
51645
|
+
entryStateId: String(root.entryStateId ?? ""),
|
|
51646
|
+
id: typeof root.id === "string" && root.id.length > 0 ? root.id : fallbackId,
|
|
51647
|
+
label: String(root.label ?? "Generated pathway"),
|
|
51648
|
+
slots: Array.isArray(root.slots) ? root.slots : [],
|
|
51649
|
+
states: Array.isArray(root.states) ? root.states : [],
|
|
51650
|
+
...Array.isArray(root.tools) ? { tools: root.tools } : {},
|
|
51651
|
+
...root.metadata && typeof root.metadata === "object" ? { metadata: root.metadata } : {}
|
|
51652
|
+
};
|
|
51653
|
+
};
|
|
51654
|
+
var generateVoicePathwayFromPrompt = async (input) => {
|
|
51655
|
+
const fallbackId = input.id ?? slugify(input.description);
|
|
51656
|
+
const maxRepairs = input.maxRepairAttempts ?? 2;
|
|
51657
|
+
const systemPrompt = input.guidance ? `${SYSTEM_PROMPT}
|
|
51658
|
+
|
|
51659
|
+
Additional guidance:
|
|
51660
|
+
${input.guidance}` : SYSTEM_PROMPT;
|
|
51661
|
+
const rawOutputs = [];
|
|
51662
|
+
let lastReport = null;
|
|
51663
|
+
let lastPathway = null;
|
|
51664
|
+
for (let attempt = 0;attempt <= maxRepairs; attempt += 1) {
|
|
51665
|
+
const prompt = attempt === 0 ? `Build a voice pathway for:
|
|
51666
|
+
${input.description}
|
|
51667
|
+
|
|
51668
|
+
Suggested id: ${fallbackId}` : `The previous pathway JSON failed validation with these errors:
|
|
51669
|
+
${lastReport.issues.filter((issue) => issue.severity === "error").map((issue) => `- ${issue.message}`).join(`
|
|
51670
|
+
`)}
|
|
51671
|
+
|
|
51672
|
+
Here was your previous output:
|
|
51673
|
+
${rawOutputs.at(-1)}
|
|
51674
|
+
|
|
51675
|
+
Return a corrected pathway JSON that fixes every error.`;
|
|
51676
|
+
const raw = await input.completion({ prompt, systemPrompt });
|
|
51677
|
+
rawOutputs.push(raw);
|
|
51678
|
+
const pathway = coercePathway(extractJson4(raw), fallbackId);
|
|
51679
|
+
const report = validateVoicePathway(pathway);
|
|
51680
|
+
lastReport = report;
|
|
51681
|
+
lastPathway = pathway;
|
|
51682
|
+
if (report.valid) {
|
|
51683
|
+
return { attempts: attempt + 1, pathway, rawOutputs, report };
|
|
51684
|
+
}
|
|
51685
|
+
}
|
|
51686
|
+
return {
|
|
51687
|
+
attempts: rawOutputs.length,
|
|
51688
|
+
pathway: lastPathway,
|
|
51689
|
+
rawOutputs,
|
|
51690
|
+
report: lastReport
|
|
51691
|
+
};
|
|
51692
|
+
};
|
|
51534
51693
|
// src/crmContract.ts
|
|
51535
51694
|
var createVoiceCRMRegistry = (options) => {
|
|
51536
51695
|
const byVendor = new Map;
|
|
@@ -51970,6 +52129,7 @@ export {
|
|
|
51970
52129
|
getLatestVoiceTelephonyMediaReport,
|
|
51971
52130
|
getLatestVoiceBrowserMediaReport,
|
|
51972
52131
|
getDefaultVoiceTelephonyBenchmarkScenarios,
|
|
52132
|
+
generateVoicePathwayFromPrompt,
|
|
51973
52133
|
generateVoiceCalendarSlots,
|
|
51974
52134
|
fromVapiAssistantConfig,
|
|
51975
52135
|
formatVoiceProofTrendAge,
|
|
@@ -52275,6 +52435,7 @@ export {
|
|
|
52275
52435
|
createVoiceMemoryAuditEventStore,
|
|
52276
52436
|
createVoiceMemoryAssistantMemoryStore,
|
|
52277
52437
|
createVoiceMediaPipelineRoutes,
|
|
52438
|
+
createVoiceMCPToolset,
|
|
52278
52439
|
createVoiceLiveOpsRoutes,
|
|
52279
52440
|
createVoiceLiveOpsController,
|
|
52280
52441
|
createVoiceLiveMonitorRoutes,
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type VoiceAgentTool } from "./agent";
|
|
2
|
+
import type { VoiceSessionRecord } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* Minimal structural shapes from the Model Context Protocol. Any MCP client
|
|
5
|
+
* (`@modelcontextprotocol/sdk` over stdio / SSE / streamable-HTTP, or a custom
|
|
6
|
+
* transport) that exposes `listTools` + `callTool` satisfies this — voice does
|
|
7
|
+
* not bundle an MCP SDK.
|
|
8
|
+
*/
|
|
9
|
+
export type MCPToolDefinition = {
|
|
10
|
+
name: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
inputSchema?: Record<string, unknown>;
|
|
13
|
+
};
|
|
14
|
+
export type MCPToolContentBlock = {
|
|
15
|
+
type: "text";
|
|
16
|
+
text: string;
|
|
17
|
+
} | {
|
|
18
|
+
type: string;
|
|
19
|
+
[key: string]: unknown;
|
|
20
|
+
};
|
|
21
|
+
export type MCPToolCallResult = {
|
|
22
|
+
content?: MCPToolContentBlock[];
|
|
23
|
+
structuredContent?: unknown;
|
|
24
|
+
isError?: boolean;
|
|
25
|
+
};
|
|
26
|
+
export type MCPClientLike = {
|
|
27
|
+
listTools: () => Promise<{
|
|
28
|
+
tools: MCPToolDefinition[];
|
|
29
|
+
}> | {
|
|
30
|
+
tools: MCPToolDefinition[];
|
|
31
|
+
};
|
|
32
|
+
callTool: (input: {
|
|
33
|
+
name: string;
|
|
34
|
+
arguments?: Record<string, unknown>;
|
|
35
|
+
}) => Promise<MCPToolCallResult> | MCPToolCallResult;
|
|
36
|
+
};
|
|
37
|
+
export type VoiceMCPToolResult = {
|
|
38
|
+
text: string;
|
|
39
|
+
structuredContent?: unknown;
|
|
40
|
+
isError: boolean;
|
|
41
|
+
raw: MCPToolCallResult;
|
|
42
|
+
};
|
|
43
|
+
export type CreateVoiceMCPToolsetOptions = {
|
|
44
|
+
client: MCPClientLike;
|
|
45
|
+
/** Prefix applied to every exposed tool name (e.g. "mcp_"). */
|
|
46
|
+
namePrefix?: string;
|
|
47
|
+
/** Only expose tools whose (unprefixed) name is in this allow-list. */
|
|
48
|
+
allowedTools?: ReadonlyArray<string>;
|
|
49
|
+
/** Drop tools whose (unprefixed) name is in this block-list. */
|
|
50
|
+
blockedTools?: ReadonlyArray<string>;
|
|
51
|
+
/** Override how an MCP result is flattened to the assistant-visible string. */
|
|
52
|
+
resultToMessage?: (result: VoiceMCPToolResult) => string;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Bridges the tools exposed by an MCP server into `VoiceAgentTool`s. Call once
|
|
56
|
+
* at setup; the returned array spreads straight into `createVoiceAgent({ tools })`.
|
|
57
|
+
*/
|
|
58
|
+
export declare const createVoiceMCPToolset: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord>(options: CreateVoiceMCPToolsetOptions) => Promise<VoiceAgentTool<TContext, TSession, Record<string, unknown>, VoiceMCPToolResult>[]>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type VoicePathway, type VoicePathwayValidationReport } from "./pathway";
|
|
2
|
+
export type VoicePathwayGeneratorCompletion = (input: {
|
|
3
|
+
prompt: string;
|
|
4
|
+
systemPrompt: string;
|
|
5
|
+
}) => Promise<string>;
|
|
6
|
+
export type GenerateVoicePathwayInput = {
|
|
7
|
+
/** Plain-text description of the agent / flow to build. */
|
|
8
|
+
description: string;
|
|
9
|
+
completion: VoicePathwayGeneratorCompletion;
|
|
10
|
+
/** Suggested pathway id (slugified). Defaults to "generated-pathway". */
|
|
11
|
+
id?: string;
|
|
12
|
+
/** Extra guidance appended to the system prompt. */
|
|
13
|
+
guidance?: string;
|
|
14
|
+
/**
|
|
15
|
+
* If the first attempt fails validation, retry this many times feeding the
|
|
16
|
+
* issues back to the model. Defaults to 2.
|
|
17
|
+
*/
|
|
18
|
+
maxRepairAttempts?: number;
|
|
19
|
+
};
|
|
20
|
+
export type GenerateVoicePathwayResult = {
|
|
21
|
+
pathway: VoicePathway;
|
|
22
|
+
report: VoicePathwayValidationReport;
|
|
23
|
+
attempts: number;
|
|
24
|
+
/** Raw model outputs from each attempt, for debugging. */
|
|
25
|
+
rawOutputs: string[];
|
|
26
|
+
};
|
|
27
|
+
export declare const generateVoicePathwayFromPrompt: (input: GenerateVoicePathwayInput) => Promise<GenerateVoicePathwayResult>;
|