@voicelayer/sdk 0.1.6 → 0.1.8
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.js
CHANGED
|
@@ -137,6 +137,9 @@ var init_flow = __esm({
|
|
|
137
137
|
"llm",
|
|
138
138
|
"rag",
|
|
139
139
|
"memory",
|
|
140
|
+
// Call-wide voice/turn-taking capability (barge-in + endpointing tuning);
|
|
141
|
+
// managed in the Capabilities panel like `memory`, never a graph step.
|
|
142
|
+
"speech",
|
|
140
143
|
"classify",
|
|
141
144
|
// Agentic router: per-turn, selects 1-of-N skills by their plain-language
|
|
142
145
|
// descriptions and takes that port (the resident routing surface / hub).
|
|
@@ -335,6 +338,19 @@ var init_agents = __esm({
|
|
|
335
338
|
}).optional(),
|
|
336
339
|
// Tool capabilities compiled from tool nodes (maps to AgentConfig.tools).
|
|
337
340
|
tools: z.array(ProcessToolDTO).max(32).optional(),
|
|
341
|
+
// Voice turn-taking compiled from the flow's speech capability node (maps to
|
|
342
|
+
// AgentConfig.speech → applySpeechTurnHandling). Absent = platform defaults.
|
|
343
|
+
speech: z.object({
|
|
344
|
+
interruption: z.object({
|
|
345
|
+
enabled: z.boolean().optional(),
|
|
346
|
+
minWords: z.number().int().min(0).max(10).optional(),
|
|
347
|
+
minDuration: z.number().int().min(0).max(5e3).optional()
|
|
348
|
+
}).optional(),
|
|
349
|
+
endpointing: z.object({
|
|
350
|
+
minDelay: z.number().int().min(0).max(5e3).optional(),
|
|
351
|
+
maxDelay: z.number().int().min(0).max(15e3).optional()
|
|
352
|
+
}).optional()
|
|
353
|
+
}).optional(),
|
|
338
354
|
// Graph-preserving compiled program. When present and the flow-runtime graph
|
|
339
355
|
// mode is enabled, the worker executes this node-graph deterministically
|
|
340
356
|
// (the interpreter) instead of the flat goals+rails form above. Embedded here
|
|
@@ -494,7 +510,7 @@ var init_consultation = __esm({
|
|
|
494
510
|
});
|
|
495
511
|
}
|
|
496
512
|
});
|
|
497
|
-
var CALL_CONTROL_PUBSUB_CHANNEL, CallEventKind, SayCommand, HangupCommand, DtmfCommand, InstructCommand, CallControlCommand;
|
|
513
|
+
var CALL_CONTROL_PUBSUB_CHANNEL, CallEventKind, OP_CALL_EVENT_KIND, OpCallEventKind, SayCommand, HangupCommand, DtmfCommand, InstructCommand, CallControlCommand;
|
|
498
514
|
var init_call_events = __esm({
|
|
499
515
|
"../contracts/src/call-events.ts"() {
|
|
500
516
|
init_consultation();
|
|
@@ -520,6 +536,8 @@ var init_call_events = __esm({
|
|
|
520
536
|
"dtmf.received",
|
|
521
537
|
"dtmf.sent"
|
|
522
538
|
]);
|
|
539
|
+
OP_CALL_EVENT_KIND = /^(tool|handoff|lookup|record|notify)\.[a-z0-9_]{1,48}(\.[a-z0-9_]{1,48})?$/;
|
|
540
|
+
OpCallEventKind = z.string().regex(OP_CALL_EVENT_KIND);
|
|
523
541
|
z.object({
|
|
524
542
|
reason: z.string().optional(),
|
|
525
543
|
durationMs: z.number().int().min(0).optional(),
|
|
@@ -557,7 +575,8 @@ var init_call_events = __esm({
|
|
|
557
575
|
seq: z.number().int().min(0),
|
|
558
576
|
callId: z.string().uuid(),
|
|
559
577
|
projectId: z.string().min(1),
|
|
560
|
-
|
|
578
|
+
// Closed lifecycle kinds OR the namespaced operational vocabulary.
|
|
579
|
+
kind: z.union([CallEventKind, OpCallEventKind]),
|
|
561
580
|
payload: z.unknown(),
|
|
562
581
|
ts: z.string().datetime()
|
|
563
582
|
});
|
|
@@ -1914,7 +1933,20 @@ function nodeConfigFor(node) {
|
|
|
1914
1933
|
...typeof data.connectionRef === "string" ? { connectionRef: data.connectionRef } : {},
|
|
1915
1934
|
// Registry reference: run the project HTTP tool with this NAME via the
|
|
1916
1935
|
// server-side proxy instead of the inline url/method/auth above.
|
|
1917
|
-
...typeof data.toolRef === "string" && data.toolRef ? { toolRef: data.toolRef } : {}
|
|
1936
|
+
...typeof data.toolRef === "string" && data.toolRef ? { toolRef: data.toolRef } : {},
|
|
1937
|
+
// Promote result fields into NAMED slots on success:
|
|
1938
|
+
// { slotName: "data.path" }. Later nodes then read {{ slotName }}
|
|
1939
|
+
// directly instead of {{ tool.<nodeId>.data.path }} via a set node.
|
|
1940
|
+
...(() => {
|
|
1941
|
+
if (!data.outputAs || typeof data.outputAs !== "object") return {};
|
|
1942
|
+
const outputAs = {};
|
|
1943
|
+
for (const [slot, path] of Object.entries(data.outputAs)) {
|
|
1944
|
+
if (typeof path === "string" && path.trim() && /^[a-zA-Z_][\w.]*$/.test(slot)) {
|
|
1945
|
+
outputAs[slot] = path.trim();
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
return Object.keys(outputAs).length ? { outputAs } : {};
|
|
1949
|
+
})()
|
|
1918
1950
|
};
|
|
1919
1951
|
}
|
|
1920
1952
|
case "gate":
|
|
@@ -1927,7 +1959,10 @@ function nodeConfigFor(node) {
|
|
|
1927
1959
|
...typeof data.fallback === "string" ? { fallback: data.fallback } : {},
|
|
1928
1960
|
...typeof data.on === "string" ? { on: data.on } : {},
|
|
1929
1961
|
// Per-node warm (conference brief) / cold (blind REFER) override.
|
|
1930
|
-
...data.mode === "warm" || data.mode === "cold" ? { mode: data.mode } : {}
|
|
1962
|
+
...data.mode === "warm" || data.mode === "cold" ? { mode: data.mode } : {},
|
|
1963
|
+
// What the joining human HEARS (warm mode speaks it verbatim once they
|
|
1964
|
+
// answer). {{ }}-interpolated at runtime against the slot scope.
|
|
1965
|
+
...typeof data.briefing === "string" && data.briefing.trim() ? { briefing: data.briefing } : {}
|
|
1931
1966
|
};
|
|
1932
1967
|
case "end":
|
|
1933
1968
|
return {
|
|
@@ -2091,7 +2126,7 @@ var init_flow_compile = __esm({
|
|
|
2091
2126
|
HISTORY_POLICIES = /* @__PURE__ */ new Set(["full", "last-turn", "slots-only"]);
|
|
2092
2127
|
ADHERENCE_MODES = /* @__PURE__ */ new Set(["off", "observe", "enforce"]);
|
|
2093
2128
|
UNSUPPORTED_NODE_TYPES = /* @__PURE__ */ new Set([]);
|
|
2094
|
-
CAPABILITY_NODE_TYPES = /* @__PURE__ */ new Set(["memory", "trigger"]);
|
|
2129
|
+
CAPABILITY_NODE_TYPES = /* @__PURE__ */ new Set(["memory", "trigger", "speech"]);
|
|
2095
2130
|
ANNOTATION_NODE_TYPES = /* @__PURE__ */ new Set(["note"]);
|
|
2096
2131
|
}
|
|
2097
2132
|
});
|
|
@@ -3141,7 +3176,7 @@ var init_executors = __esm({
|
|
|
3141
3176
|
if (msg) await speak(interpolate(msg, scopeFor(state, deps)), deps);
|
|
3142
3177
|
return { kind: "end" };
|
|
3143
3178
|
};
|
|
3144
|
-
handoffExecutor = async (node,
|
|
3179
|
+
handoffExecutor = async (node, state, deps) => {
|
|
3145
3180
|
const cfg = node.config;
|
|
3146
3181
|
const target = str(cfg.to) || str(cfg.fallback);
|
|
3147
3182
|
if (!target) {
|
|
@@ -3150,9 +3185,14 @@ var init_executors = __esm({
|
|
|
3150
3185
|
}
|
|
3151
3186
|
const reason = str(cfg.reason) || `flow:${node.id}`;
|
|
3152
3187
|
const mode = cfg.mode === "warm" || cfg.mode === "cold" ? cfg.mode : void 0;
|
|
3188
|
+
const briefing = str(cfg.briefing) ? interpolate(str(cfg.briefing), scopeFor(state, deps)) : void 0;
|
|
3153
3189
|
deps.events.note("handoff.start", { node: node.id, to: target, ...mode ? { mode } : {} });
|
|
3154
3190
|
try {
|
|
3155
|
-
await deps.ctx.handoff(target, {
|
|
3191
|
+
await deps.ctx.handoff(target, {
|
|
3192
|
+
reason,
|
|
3193
|
+
...mode ? { mode } : {},
|
|
3194
|
+
...briefing ? { briefing } : {}
|
|
3195
|
+
});
|
|
3156
3196
|
} catch (err) {
|
|
3157
3197
|
deps.events.note("handoff.failed", {
|
|
3158
3198
|
node: node.id,
|
|
@@ -3206,7 +3246,18 @@ ${collected}`);
|
|
|
3206
3246
|
const cfg = node.config;
|
|
3207
3247
|
const name = str(cfg.name) || node.id;
|
|
3208
3248
|
const routeByOutcome = () => {
|
|
3209
|
-
const
|
|
3249
|
+
const result = state.toolResults[node.id];
|
|
3250
|
+
const ok = result?.ok === true;
|
|
3251
|
+
if (ok && cfg.outputAs && typeof cfg.outputAs === "object") {
|
|
3252
|
+
for (const [slot, path] of Object.entries(cfg.outputAs)) {
|
|
3253
|
+
if (typeof path !== "string" || !path.trim()) continue;
|
|
3254
|
+
const value = resolvePath(result, path.trim());
|
|
3255
|
+
if (value !== void 0 && value !== null) {
|
|
3256
|
+
deps.processRt.captureField(slot, value);
|
|
3257
|
+
deps.events.note("tool.output_captured", { node: node.id, slot });
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3210
3261
|
return routeByPort(node, deps, ok ? "ok" : "fail");
|
|
3211
3262
|
};
|
|
3212
3263
|
const toolRef = str(cfg.toolRef);
|
|
@@ -5262,12 +5313,15 @@ var CallsClient = class {
|
|
|
5262
5313
|
);
|
|
5263
5314
|
}
|
|
5264
5315
|
/**
|
|
5265
|
-
* Append a structured call event
|
|
5266
|
-
*
|
|
5316
|
+
* Append a structured call event — consultation lifecycle kinds, or the
|
|
5317
|
+
* operational vocabulary the call-detail Timeline renders between
|
|
5318
|
+
* transcript rows (tool.* / handoff.* / lookup.* / record.* / notify.*).
|
|
5319
|
+
* See apps/api/src/routes/calls.ts AppendEventBody for the accepted shapes.
|
|
5267
5320
|
*
|
|
5268
5321
|
* The agent SDK's built-in `ask_host` tool calls this on consultation start
|
|
5269
|
-
* / end
|
|
5270
|
-
*
|
|
5322
|
+
* / end; the handoff runtime and graph interpreter emit op events through it
|
|
5323
|
+
* so the operational story (dial → answered → briefed → dropped, tool calls)
|
|
5324
|
+
* is visible in the dashboard, not just the spoken transcript.
|
|
5271
5325
|
*/
|
|
5272
5326
|
async appendEvent(callId, input) {
|
|
5273
5327
|
await this.transport.request(
|
|
@@ -5686,6 +5740,18 @@ init_conditions();
|
|
|
5686
5740
|
init_interpreter();
|
|
5687
5741
|
|
|
5688
5742
|
// src/runtime/graph/events.ts
|
|
5743
|
+
var OP_EVENT_NOTE = /^(tool|handoff|lookup|record|notify)\./;
|
|
5744
|
+
function withOpEventFanout(events, notify) {
|
|
5745
|
+
return {
|
|
5746
|
+
enter: (nodeId, type) => events.enter(nodeId, type),
|
|
5747
|
+
traverse: (from, to, via) => events.traverse(from, to, via),
|
|
5748
|
+
note(kind, data) {
|
|
5749
|
+
events.note(kind, data);
|
|
5750
|
+
if (OP_EVENT_NOTE.test(kind)) notify(kind, data ?? {});
|
|
5751
|
+
},
|
|
5752
|
+
currentNodeId: () => events.currentNodeId()
|
|
5753
|
+
};
|
|
5754
|
+
}
|
|
5689
5755
|
function createGraphEvents(processRt) {
|
|
5690
5756
|
let current = null;
|
|
5691
5757
|
return {
|
|
@@ -8214,13 +8280,33 @@ function processSchemaToAgentParts(schema, opts = {}) {
|
|
|
8214
8280
|
);
|
|
8215
8281
|
}
|
|
8216
8282
|
}
|
|
8283
|
+
let speech;
|
|
8284
|
+
if (schema.speech) {
|
|
8285
|
+
const i = schema.speech.interruption;
|
|
8286
|
+
const e = schema.speech.endpointing;
|
|
8287
|
+
const interruption = i ? {
|
|
8288
|
+
...i.enabled !== void 0 ? { enabled: i.enabled } : {},
|
|
8289
|
+
...i.minWords !== void 0 ? { minWords: i.minWords } : {},
|
|
8290
|
+
...i.minDuration !== void 0 ? { minDuration: i.minDuration } : {}
|
|
8291
|
+
} : void 0;
|
|
8292
|
+
const endpointing = e ? {
|
|
8293
|
+
...e.minDelay !== void 0 ? { minDelay: e.minDelay } : {},
|
|
8294
|
+
...e.maxDelay !== void 0 ? { maxDelay: e.maxDelay } : {}
|
|
8295
|
+
} : void 0;
|
|
8296
|
+
speech = {
|
|
8297
|
+
...interruption && Object.keys(interruption).length ? { interruption } : {},
|
|
8298
|
+
...endpointing && Object.keys(endpointing).length ? { endpointing } : {}
|
|
8299
|
+
};
|
|
8300
|
+
if (Object.keys(speech).length === 0) speech = void 0;
|
|
8301
|
+
}
|
|
8217
8302
|
return {
|
|
8218
8303
|
prompt,
|
|
8219
8304
|
process: process2,
|
|
8220
8305
|
...triggers ? { triggers } : {},
|
|
8221
8306
|
...handoff ? { handoff } : {},
|
|
8222
8307
|
...memory ? { memory } : {},
|
|
8223
|
-
...tools ? { tools } : {}
|
|
8308
|
+
...tools ? { tools } : {},
|
|
8309
|
+
...speech ? { speech } : {}
|
|
8224
8310
|
};
|
|
8225
8311
|
}
|
|
8226
8312
|
var BrainConfigError = class extends Error {
|
|
@@ -10858,40 +10944,64 @@ function resolveRoute(config, key) {
|
|
|
10858
10944
|
if (!config) return void 0;
|
|
10859
10945
|
return config.routes?.[key] ?? config.fallback;
|
|
10860
10946
|
}
|
|
10861
|
-
async function
|
|
10947
|
+
async function sayFixed(deps, text) {
|
|
10862
10948
|
try {
|
|
10863
|
-
|
|
10949
|
+
const session = deps.session;
|
|
10950
|
+
const handle = session.say?.(text, { allowInterruptions: false });
|
|
10951
|
+
if (handle?.waitForPlayout) await handle.waitForPlayout();
|
|
10864
10952
|
} catch (err) {
|
|
10865
10953
|
console.warn("[handoff] announcement failed", err);
|
|
10866
10954
|
}
|
|
10867
10955
|
}
|
|
10956
|
+
async function quiesce(deps) {
|
|
10957
|
+
try {
|
|
10958
|
+
await deps.session.interrupt?.();
|
|
10959
|
+
} catch {
|
|
10960
|
+
}
|
|
10961
|
+
}
|
|
10868
10962
|
async function executeHandoff(args, config, deps) {
|
|
10869
10963
|
const briefing = args.briefing ?? await buildBriefing(config, deps) ?? `Handoff to ${args.to}`;
|
|
10870
10964
|
const mode = args.mode ?? config?.mode ?? "warm";
|
|
10871
10965
|
const sipOps = deps.sipOps ?? buildDefaultSipOps();
|
|
10966
|
+
const notify = deps.notify ?? (() => {
|
|
10967
|
+
});
|
|
10872
10968
|
if (mode === "warm" && sipOps && sipOps.canConference) {
|
|
10873
10969
|
try {
|
|
10874
|
-
await
|
|
10970
|
+
await sayFixed(deps, "One moment \u2014 I'm bringing our on-call engineer onto the line now. Stay with me.");
|
|
10971
|
+
notify("handoff.dialing", { to: args.to, mode: "warm" });
|
|
10875
10972
|
await sipOps.addParticipant(deps.roomName, normalizeSipTarget(args.to));
|
|
10876
|
-
|
|
10877
|
-
|
|
10878
|
-
|
|
10879
|
-
);
|
|
10973
|
+
notify("handoff.answered", { to: args.to });
|
|
10974
|
+
await sayFixed(deps, briefing);
|
|
10975
|
+
notify("handoff.briefed", { to: args.to, briefing });
|
|
10976
|
+
await sayFixed(deps, "You're both connected now \u2014 I'll drop off.");
|
|
10977
|
+
await quiesce(deps);
|
|
10880
10978
|
await deps.disconnect();
|
|
10979
|
+
notify("handoff.agent_dropped", { to: args.to });
|
|
10881
10980
|
return { ok: true, via: "conference", target: args.to };
|
|
10882
10981
|
} catch (err) {
|
|
10883
10982
|
console.error("[handoff] conference bridge failed, falling back to REFER", err);
|
|
10983
|
+
notify("handoff.failed", {
|
|
10984
|
+
to: args.to,
|
|
10985
|
+
via: "conference",
|
|
10986
|
+
error: err instanceof Error ? err.message : String(err)
|
|
10987
|
+
});
|
|
10884
10988
|
}
|
|
10885
10989
|
}
|
|
10886
10990
|
if (sipOps) {
|
|
10887
10991
|
if (mode === "warm") {
|
|
10888
|
-
await
|
|
10992
|
+
await sayFixed(deps, "I'm transferring you now \u2014 one moment.");
|
|
10889
10993
|
}
|
|
10890
10994
|
try {
|
|
10891
10995
|
await sipOps.refer(deps.roomName, deps.callerIdentity, normalizeSipTarget(args.to));
|
|
10996
|
+
notify("handoff.refer", { to: args.to });
|
|
10892
10997
|
return { ok: true, via: "sip", target: args.to };
|
|
10893
10998
|
} catch (err) {
|
|
10894
10999
|
console.error("[handoff] SIP REFER failed, falling back to disconnect", err);
|
|
11000
|
+
notify("handoff.failed", {
|
|
11001
|
+
to: args.to,
|
|
11002
|
+
via: "refer",
|
|
11003
|
+
error: err instanceof Error ? err.message : String(err)
|
|
11004
|
+
});
|
|
10895
11005
|
}
|
|
10896
11006
|
}
|
|
10897
11007
|
try {
|
|
@@ -10935,7 +11045,8 @@ function buildDefaultSipOps() {
|
|
|
10935
11045
|
const identity = `xfer-${target.replace(/[^0-9+]/g, "")}`;
|
|
10936
11046
|
await client.createSipParticipant(trunkId, target.replace(/^tel:/, ""), roomName, {
|
|
10937
11047
|
participantIdentity: identity,
|
|
10938
|
-
playDialtone: false
|
|
11048
|
+
playDialtone: false,
|
|
11049
|
+
waitUntilAnswered: true
|
|
10939
11050
|
});
|
|
10940
11051
|
return { identity };
|
|
10941
11052
|
}
|
|
@@ -12637,7 +12748,11 @@ var Agent = class {
|
|
|
12637
12748
|
...parts.triggers ? { triggers: parts.triggers } : {},
|
|
12638
12749
|
...parts.handoff ? { handoff: parts.handoff } : {},
|
|
12639
12750
|
...parts.memory ? { memory: parts.memory } : {},
|
|
12640
|
-
...parts.tools ? { tools: { ...effectiveConfig.tools ?? {}, ...parts.tools } } : {}
|
|
12751
|
+
...parts.tools ? { tools: { ...effectiveConfig.tools ?? {}, ...parts.tools } } : {},
|
|
12752
|
+
// Flow-authored turn-taking (schema.speech). Merged over any
|
|
12753
|
+
// worker-level speech config; applySpeechTurnHandling reads this
|
|
12754
|
+
// off effectiveConfig at session build.
|
|
12755
|
+
...parts.speech ? { speech: { ...effectiveConfig.speech ?? {}, ...parts.speech } } : {}
|
|
12641
12756
|
};
|
|
12642
12757
|
console.log("[agent] booted from flow", {
|
|
12643
12758
|
agentId: flowAgentId,
|
|
@@ -12698,7 +12813,13 @@ var Agent = class {
|
|
|
12698
12813
|
const processRt = createProcessRuntime(effectiveConfig.process);
|
|
12699
12814
|
seedCallPrefill(processRt, dispatchMdEarly);
|
|
12700
12815
|
await seedProjectVariables(processRt);
|
|
12701
|
-
|
|
12816
|
+
let opEventCallId = null;
|
|
12817
|
+
const emitOpEvent = (kind, payload) => {
|
|
12818
|
+
if (!sdkClient || !opEventCallId) return;
|
|
12819
|
+
void sdkClient.calls.appendEvent(opEventCallId, { kind, payload }).catch(() => {
|
|
12820
|
+
});
|
|
12821
|
+
};
|
|
12822
|
+
const graphEvents = graphMode ? withOpEventFanout(createGraphEvents(processRt), emitOpEvent) : null;
|
|
12702
12823
|
const isOutbound = stringValue(dispatchMdEarly["direction"]) === "outbound";
|
|
12703
12824
|
const perCallAmd = stringValue(dispatchMdEarly["amd"]);
|
|
12704
12825
|
const amdMode = perCallAmd === "off" || perCallAmd === "detect" || perCallAmd === "detectMessageEnd" ? perCallAmd : effectiveConfig.outbound?.amd ?? "off";
|
|
@@ -12790,6 +12911,7 @@ var Agent = class {
|
|
|
12790
12911
|
});
|
|
12791
12912
|
}
|
|
12792
12913
|
}
|
|
12914
|
+
opEventCallId = consultationCallId;
|
|
12793
12915
|
let agentSpeechState = "initializing";
|
|
12794
12916
|
const idleWaiters = /* @__PURE__ */ new Set();
|
|
12795
12917
|
const waitUntilAgentIdle = async (timeoutMs) => {
|
|
@@ -12973,7 +13095,8 @@ ${callIntent}`
|
|
|
12973
13095
|
}
|
|
12974
13096
|
},
|
|
12975
13097
|
getProcessData: () => processRt.getData(),
|
|
12976
|
-
transcript
|
|
13098
|
+
transcript,
|
|
13099
|
+
notify: (kind, payload) => emitOpEvent(kind, payload)
|
|
12977
13100
|
});
|
|
12978
13101
|
const triggersRt = createTriggersRuntime(effectiveConfig.triggers, handoffRt);
|
|
12979
13102
|
const audioExperience = new AudioExperiencePrimitive();
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { a6 as GraphTraceEvent, q as RunTextSessionOptions, r as RunTextTranscriptOptions, T as TextSession, O as TextTranscriptResult, a3 as runTextSession, a4 as runTextTranscript } from '../text-session-
|
|
1
|
+
export { a6 as GraphTraceEvent, q as RunTextSessionOptions, r as RunTextTranscriptOptions, T as TextSession, O as TextTranscriptResult, a3 as runTextSession, a4 as runTextTranscript } from '../text-session-BoUmDEX9.js';
|
|
2
2
|
import '@livekit/agents';
|
|
3
3
|
import 'zod';
|
|
@@ -97,6 +97,9 @@ var init_flow = __esm({
|
|
|
97
97
|
"llm",
|
|
98
98
|
"rag",
|
|
99
99
|
"memory",
|
|
100
|
+
// Call-wide voice/turn-taking capability (barge-in + endpointing tuning);
|
|
101
|
+
// managed in the Capabilities panel like `memory`, never a graph step.
|
|
102
|
+
"speech",
|
|
100
103
|
"classify",
|
|
101
104
|
// Agentic router: per-turn, selects 1-of-N skills by their plain-language
|
|
102
105
|
// descriptions and takes that port (the resident routing surface / hub).
|
|
@@ -295,6 +298,19 @@ var init_agents = __esm({
|
|
|
295
298
|
}).optional(),
|
|
296
299
|
// Tool capabilities compiled from tool nodes (maps to AgentConfig.tools).
|
|
297
300
|
tools: z.array(ProcessToolDTO).max(32).optional(),
|
|
301
|
+
// Voice turn-taking compiled from the flow's speech capability node (maps to
|
|
302
|
+
// AgentConfig.speech → applySpeechTurnHandling). Absent = platform defaults.
|
|
303
|
+
speech: z.object({
|
|
304
|
+
interruption: z.object({
|
|
305
|
+
enabled: z.boolean().optional(),
|
|
306
|
+
minWords: z.number().int().min(0).max(10).optional(),
|
|
307
|
+
minDuration: z.number().int().min(0).max(5e3).optional()
|
|
308
|
+
}).optional(),
|
|
309
|
+
endpointing: z.object({
|
|
310
|
+
minDelay: z.number().int().min(0).max(5e3).optional(),
|
|
311
|
+
maxDelay: z.number().int().min(0).max(15e3).optional()
|
|
312
|
+
}).optional()
|
|
313
|
+
}).optional(),
|
|
298
314
|
// Graph-preserving compiled program. When present and the flow-runtime graph
|
|
299
315
|
// mode is enabled, the worker executes this node-graph deterministically
|
|
300
316
|
// (the interpreter) instead of the flat goals+rails form above. Embedded here
|
|
@@ -452,7 +468,7 @@ var init_consultation = __esm({
|
|
|
452
468
|
});
|
|
453
469
|
}
|
|
454
470
|
});
|
|
455
|
-
var CallEventKind, SayCommand, HangupCommand, DtmfCommand, InstructCommand;
|
|
471
|
+
var CallEventKind, OP_CALL_EVENT_KIND, OpCallEventKind, SayCommand, HangupCommand, DtmfCommand, InstructCommand;
|
|
456
472
|
var init_call_events = __esm({
|
|
457
473
|
"../contracts/src/call-events.ts"() {
|
|
458
474
|
init_consultation();
|
|
@@ -477,6 +493,8 @@ var init_call_events = __esm({
|
|
|
477
493
|
"dtmf.received",
|
|
478
494
|
"dtmf.sent"
|
|
479
495
|
]);
|
|
496
|
+
OP_CALL_EVENT_KIND = /^(tool|handoff|lookup|record|notify)\.[a-z0-9_]{1,48}(\.[a-z0-9_]{1,48})?$/;
|
|
497
|
+
OpCallEventKind = z.string().regex(OP_CALL_EVENT_KIND);
|
|
480
498
|
z.object({
|
|
481
499
|
reason: z.string().optional(),
|
|
482
500
|
durationMs: z.number().int().min(0).optional(),
|
|
@@ -514,7 +532,8 @@ var init_call_events = __esm({
|
|
|
514
532
|
seq: z.number().int().min(0),
|
|
515
533
|
callId: z.string().uuid(),
|
|
516
534
|
projectId: z.string().min(1),
|
|
517
|
-
|
|
535
|
+
// Closed lifecycle kinds OR the namespaced operational vocabulary.
|
|
536
|
+
kind: z.union([CallEventKind, OpCallEventKind]),
|
|
518
537
|
payload: z.unknown(),
|
|
519
538
|
ts: z.string().datetime()
|
|
520
539
|
});
|
|
@@ -2645,7 +2664,7 @@ var init_executors = __esm({
|
|
|
2645
2664
|
if (msg) await speak(interpolate(msg, scopeFor(state, deps)), deps);
|
|
2646
2665
|
return { kind: "end" };
|
|
2647
2666
|
};
|
|
2648
|
-
handoffExecutor = async (node,
|
|
2667
|
+
handoffExecutor = async (node, state, deps) => {
|
|
2649
2668
|
const cfg = node.config;
|
|
2650
2669
|
const target = str(cfg.to) || str(cfg.fallback);
|
|
2651
2670
|
if (!target) {
|
|
@@ -2654,9 +2673,14 @@ var init_executors = __esm({
|
|
|
2654
2673
|
}
|
|
2655
2674
|
const reason = str(cfg.reason) || `flow:${node.id}`;
|
|
2656
2675
|
const mode = cfg.mode === "warm" || cfg.mode === "cold" ? cfg.mode : void 0;
|
|
2676
|
+
const briefing = str(cfg.briefing) ? interpolate(str(cfg.briefing), scopeFor(state, deps)) : void 0;
|
|
2657
2677
|
deps.events.note("handoff.start", { node: node.id, to: target, ...mode ? { mode } : {} });
|
|
2658
2678
|
try {
|
|
2659
|
-
await deps.ctx.handoff(target, {
|
|
2679
|
+
await deps.ctx.handoff(target, {
|
|
2680
|
+
reason,
|
|
2681
|
+
...mode ? { mode } : {},
|
|
2682
|
+
...briefing ? { briefing } : {}
|
|
2683
|
+
});
|
|
2660
2684
|
} catch (err) {
|
|
2661
2685
|
deps.events.note("handoff.failed", {
|
|
2662
2686
|
node: node.id,
|
|
@@ -2710,7 +2734,18 @@ ${collected}`);
|
|
|
2710
2734
|
const cfg = node.config;
|
|
2711
2735
|
const name = str(cfg.name) || node.id;
|
|
2712
2736
|
const routeByOutcome = () => {
|
|
2713
|
-
const
|
|
2737
|
+
const result = state.toolResults[node.id];
|
|
2738
|
+
const ok = result?.ok === true;
|
|
2739
|
+
if (ok && cfg.outputAs && typeof cfg.outputAs === "object") {
|
|
2740
|
+
for (const [slot, path] of Object.entries(cfg.outputAs)) {
|
|
2741
|
+
if (typeof path !== "string" || !path.trim()) continue;
|
|
2742
|
+
const value = resolvePath(result, path.trim());
|
|
2743
|
+
if (value !== void 0 && value !== null) {
|
|
2744
|
+
deps.processRt.captureField(slot, value);
|
|
2745
|
+
deps.events.note("tool.output_captured", { node: node.id, slot });
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2714
2749
|
return routeByPort(node, deps, ok ? "ok" : "fail");
|
|
2715
2750
|
};
|
|
2716
2751
|
const toolRef = str(cfg.toolRef);
|
|
@@ -90,7 +90,7 @@ declare const FlowProgram: z.ZodObject<{
|
|
|
90
90
|
entry: z.ZodArray<z.ZodString, "many">;
|
|
91
91
|
nodes: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
92
92
|
id: z.ZodString;
|
|
93
|
-
type: z.ZodEnum<["start", "say", "ask", "confirm", "tool", "decision", "handoff", "end", "trigger", "gate", "playbook", "menu", "code", "set", "subflow", "llm", "rag", "memory", "classify", "router", "note"]>;
|
|
93
|
+
type: z.ZodEnum<["start", "say", "ask", "confirm", "tool", "decision", "handoff", "end", "trigger", "gate", "playbook", "menu", "code", "set", "subflow", "llm", "rag", "memory", "speech", "classify", "router", "note"]>;
|
|
94
94
|
config: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
95
95
|
out: z.ZodArray<z.ZodObject<{
|
|
96
96
|
to: z.ZodString;
|
|
@@ -99,34 +99,34 @@ declare const FlowProgram: z.ZodObject<{
|
|
|
99
99
|
label: z.ZodOptional<z.ZodString>;
|
|
100
100
|
}, "strip", z.ZodTypeAny, {
|
|
101
101
|
to: string;
|
|
102
|
+
condition?: string | undefined;
|
|
102
103
|
sourceHandle?: string | undefined;
|
|
103
104
|
label?: string | undefined;
|
|
104
|
-
condition?: string | undefined;
|
|
105
105
|
}, {
|
|
106
106
|
to: string;
|
|
107
|
+
condition?: string | undefined;
|
|
107
108
|
sourceHandle?: string | undefined;
|
|
108
109
|
label?: string | undefined;
|
|
109
|
-
condition?: string | undefined;
|
|
110
110
|
}>, "many">;
|
|
111
111
|
}, "strip", z.ZodTypeAny, {
|
|
112
|
-
type: "code" | "start" | "say" | "ask" | "confirm" | "tool" | "decision" | "handoff" | "end" | "trigger" | "gate" | "playbook" | "menu" | "
|
|
112
|
+
type: "code" | "set" | "start" | "say" | "ask" | "confirm" | "tool" | "decision" | "handoff" | "end" | "trigger" | "gate" | "playbook" | "menu" | "subflow" | "llm" | "rag" | "memory" | "speech" | "classify" | "router" | "note";
|
|
113
113
|
id: string;
|
|
114
114
|
config: Record<string, unknown>;
|
|
115
115
|
out: {
|
|
116
116
|
to: string;
|
|
117
|
+
condition?: string | undefined;
|
|
117
118
|
sourceHandle?: string | undefined;
|
|
118
119
|
label?: string | undefined;
|
|
119
|
-
condition?: string | undefined;
|
|
120
120
|
}[];
|
|
121
121
|
}, {
|
|
122
|
-
type: "code" | "start" | "say" | "ask" | "confirm" | "tool" | "decision" | "handoff" | "end" | "trigger" | "gate" | "playbook" | "menu" | "
|
|
122
|
+
type: "code" | "set" | "start" | "say" | "ask" | "confirm" | "tool" | "decision" | "handoff" | "end" | "trigger" | "gate" | "playbook" | "menu" | "subflow" | "llm" | "rag" | "memory" | "speech" | "classify" | "router" | "note";
|
|
123
123
|
id: string;
|
|
124
124
|
config: Record<string, unknown>;
|
|
125
125
|
out: {
|
|
126
126
|
to: string;
|
|
127
|
+
condition?: string | undefined;
|
|
127
128
|
sourceHandle?: string | undefined;
|
|
128
129
|
label?: string | undefined;
|
|
129
|
-
condition?: string | undefined;
|
|
130
130
|
}[];
|
|
131
131
|
}>>;
|
|
132
132
|
completionGate: z.ZodObject<{
|
|
@@ -152,18 +152,18 @@ declare const FlowProgram: z.ZodObject<{
|
|
|
152
152
|
}, "strip", z.ZodTypeAny, {
|
|
153
153
|
v: 1;
|
|
154
154
|
id: string;
|
|
155
|
+
entry: string[];
|
|
155
156
|
nodes: Record<string, {
|
|
156
|
-
type: "code" | "start" | "say" | "ask" | "confirm" | "tool" | "decision" | "handoff" | "end" | "trigger" | "gate" | "playbook" | "menu" | "
|
|
157
|
+
type: "code" | "set" | "start" | "say" | "ask" | "confirm" | "tool" | "decision" | "handoff" | "end" | "trigger" | "gate" | "playbook" | "menu" | "subflow" | "llm" | "rag" | "memory" | "speech" | "classify" | "router" | "note";
|
|
157
158
|
id: string;
|
|
158
159
|
config: Record<string, unknown>;
|
|
159
160
|
out: {
|
|
160
161
|
to: string;
|
|
162
|
+
condition?: string | undefined;
|
|
161
163
|
sourceHandle?: string | undefined;
|
|
162
164
|
label?: string | undefined;
|
|
163
|
-
condition?: string | undefined;
|
|
164
165
|
}[];
|
|
165
166
|
}>;
|
|
166
|
-
entry: string[];
|
|
167
167
|
completionGate: {
|
|
168
168
|
requiredFields: string[];
|
|
169
169
|
};
|
|
@@ -175,18 +175,18 @@ declare const FlowProgram: z.ZodObject<{
|
|
|
175
175
|
}, {
|
|
176
176
|
v: 1;
|
|
177
177
|
id: string;
|
|
178
|
+
entry: string[];
|
|
178
179
|
nodes: Record<string, {
|
|
179
|
-
type: "code" | "start" | "say" | "ask" | "confirm" | "tool" | "decision" | "handoff" | "end" | "trigger" | "gate" | "playbook" | "menu" | "
|
|
180
|
+
type: "code" | "set" | "start" | "say" | "ask" | "confirm" | "tool" | "decision" | "handoff" | "end" | "trigger" | "gate" | "playbook" | "menu" | "subflow" | "llm" | "rag" | "memory" | "speech" | "classify" | "router" | "note";
|
|
180
181
|
id: string;
|
|
181
182
|
config: Record<string, unknown>;
|
|
182
183
|
out: {
|
|
183
184
|
to: string;
|
|
185
|
+
condition?: string | undefined;
|
|
184
186
|
sourceHandle?: string | undefined;
|
|
185
187
|
label?: string | undefined;
|
|
186
|
-
condition?: string | undefined;
|
|
187
188
|
}[];
|
|
188
189
|
}>;
|
|
189
|
-
entry: string[];
|
|
190
190
|
completionGate: {
|
|
191
191
|
requiredFields: string[];
|
|
192
192
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voicelayer/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Official SDK for VoiceLayer — define, run, and orchestrate voice AI agents on the VoiceLayer control plane.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "VoiceLayer",
|
|
@@ -82,13 +82,13 @@
|
|
|
82
82
|
"@voicelayer/contracts": "0.1.0",
|
|
83
83
|
"@voicelayer/observability": "0.1.0",
|
|
84
84
|
"@voicelayer/primitive-audio-experience": "0.1.0",
|
|
85
|
-
"@voicelayer/primitive-process-schema": "0.1.0",
|
|
86
85
|
"@voicelayer/primitive-audio-routing": "0.1.0",
|
|
86
|
+
"@voicelayer/primitive-process-schema": "0.1.0",
|
|
87
|
+
"@voicelayer/primitive-security": "0.1.0",
|
|
87
88
|
"@voicelayer/agents-plugin-deepgram": "1.0.0",
|
|
88
89
|
"@voicelayer/agents-plugin-livekit": "1.0.0",
|
|
89
|
-
"@voicelayer/
|
|
90
|
-
"@voicelayer/agents-plugin-openai": "1.0.0"
|
|
91
|
-
"@voicelayer/agents-plugin-silero": "1.0.0"
|
|
90
|
+
"@voicelayer/agents-plugin-silero": "1.0.0",
|
|
91
|
+
"@voicelayer/agents-plugin-openai": "1.0.0"
|
|
92
92
|
},
|
|
93
93
|
"scripts": {
|
|
94
94
|
"typecheck": "tsc -b --pretty false",
|