@niadra/sdk 0.1.0 → 0.7.0

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.
Files changed (133) hide show
  1. package/CHANGELOG.md +161 -20
  2. package/README.md +484 -38
  3. package/dist/ai-sdk.cjs +1037 -0
  4. package/dist/ai-sdk.cjs.map +1 -0
  5. package/dist/ai-sdk.d.cts +118 -0
  6. package/dist/ai-sdk.d.ts +118 -0
  7. package/dist/ai-sdk.js +1030 -0
  8. package/dist/ai-sdk.js.map +1 -0
  9. package/dist/anthropic.cjs +528 -0
  10. package/dist/anthropic.cjs.map +1 -0
  11. package/dist/anthropic.d.cts +35 -0
  12. package/dist/anthropic.d.ts +35 -0
  13. package/dist/anthropic.js +523 -0
  14. package/dist/anthropic.js.map +1 -0
  15. package/dist/bedrock.cjs +392 -0
  16. package/dist/bedrock.cjs.map +1 -0
  17. package/dist/bedrock.d.cts +25 -0
  18. package/dist/bedrock.d.ts +25 -0
  19. package/dist/bedrock.js +388 -0
  20. package/dist/bedrock.js.map +1 -0
  21. package/dist/cli.js +11045 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/client-DsIxZxZk.d.cts +6905 -0
  24. package/dist/client-DsIxZxZk.d.ts +6905 -0
  25. package/dist/cloudflare-agents.cjs +626 -0
  26. package/dist/cloudflare-agents.cjs.map +1 -0
  27. package/dist/cloudflare-agents.d.cts +113 -0
  28. package/dist/cloudflare-agents.d.ts +113 -0
  29. package/dist/cloudflare-agents.js +622 -0
  30. package/dist/cloudflare-agents.js.map +1 -0
  31. package/dist/elevenlabs.cjs +738 -0
  32. package/dist/elevenlabs.cjs.map +1 -0
  33. package/dist/elevenlabs.d.cts +86 -0
  34. package/dist/elevenlabs.d.ts +86 -0
  35. package/dist/elevenlabs.js +733 -0
  36. package/dist/elevenlabs.js.map +1 -0
  37. package/dist/genkit.cjs +323 -0
  38. package/dist/genkit.cjs.map +1 -0
  39. package/dist/genkit.d.cts +67 -0
  40. package/dist/genkit.d.ts +67 -0
  41. package/dist/genkit.js +318 -0
  42. package/dist/genkit.js.map +1 -0
  43. package/dist/google-adk.cjs +705 -0
  44. package/dist/google-adk.cjs.map +1 -0
  45. package/dist/google-adk.d.cts +91 -0
  46. package/dist/google-adk.d.ts +91 -0
  47. package/dist/google-adk.js +701 -0
  48. package/dist/google-adk.js.map +1 -0
  49. package/dist/google-genai.cjs +422 -0
  50. package/dist/google-genai.cjs.map +1 -0
  51. package/dist/google-genai.d.cts +26 -0
  52. package/dist/google-genai.d.ts +26 -0
  53. package/dist/google-genai.js +418 -0
  54. package/dist/google-genai.js.map +1 -0
  55. package/dist/index.cjs +11449 -1796
  56. package/dist/index.cjs.map +1 -1
  57. package/dist/index.d.cts +1006 -1247
  58. package/dist/index.d.ts +1006 -1247
  59. package/dist/index.js +11393 -1797
  60. package/dist/index.js.map +1 -1
  61. package/dist/intercept-0_lJE6k1.d.cts +20 -0
  62. package/dist/intercept-D7qFCaRb.d.ts +20 -0
  63. package/dist/langchain.cjs +1077 -0
  64. package/dist/langchain.cjs.map +1 -0
  65. package/dist/langchain.d.cts +102 -0
  66. package/dist/langchain.d.ts +102 -0
  67. package/dist/langchain.js +1068 -0
  68. package/dist/langchain.js.map +1 -0
  69. package/dist/livekit.cjs +404 -0
  70. package/dist/livekit.cjs.map +1 -0
  71. package/dist/livekit.d.cts +113 -0
  72. package/dist/livekit.d.ts +113 -0
  73. package/dist/livekit.js +398 -0
  74. package/dist/livekit.js.map +1 -0
  75. package/dist/llamaindex.cjs +349 -0
  76. package/dist/llamaindex.cjs.map +1 -0
  77. package/dist/llamaindex.d.cts +92 -0
  78. package/dist/llamaindex.d.ts +92 -0
  79. package/dist/llamaindex.js +344 -0
  80. package/dist/llamaindex.js.map +1 -0
  81. package/dist/mastra.cjs +1082 -0
  82. package/dist/mastra.cjs.map +1 -0
  83. package/dist/mastra.d.cts +86 -0
  84. package/dist/mastra.d.ts +86 -0
  85. package/dist/mastra.js +1075 -0
  86. package/dist/mastra.js.map +1 -0
  87. package/dist/openai-agents.cjs +465 -0
  88. package/dist/openai-agents.cjs.map +1 -0
  89. package/dist/openai-agents.d.cts +75 -0
  90. package/dist/openai-agents.d.ts +75 -0
  91. package/dist/openai-agents.js +459 -0
  92. package/dist/openai-agents.js.map +1 -0
  93. package/dist/retell.cjs +846 -0
  94. package/dist/retell.cjs.map +1 -0
  95. package/dist/retell.d.cts +161 -0
  96. package/dist/retell.d.ts +161 -0
  97. package/dist/retell.js +841 -0
  98. package/dist/retell.js.map +1 -0
  99. package/dist/shared-Bb5B59Bu.d.ts +39 -0
  100. package/dist/shared-DTPWVlWm.d.cts +39 -0
  101. package/dist/strands.cjs +352 -0
  102. package/dist/strands.cjs.map +1 -0
  103. package/dist/strands.d.cts +72 -0
  104. package/dist/strands.d.ts +72 -0
  105. package/dist/strands.js +347 -0
  106. package/dist/strands.js.map +1 -0
  107. package/dist/twilio.cjs +174 -0
  108. package/dist/twilio.cjs.map +1 -0
  109. package/dist/twilio.d.cts +64 -0
  110. package/dist/twilio.d.ts +64 -0
  111. package/dist/twilio.js +167 -0
  112. package/dist/twilio.js.map +1 -0
  113. package/dist/vapi.cjs +661 -0
  114. package/dist/vapi.cjs.map +1 -0
  115. package/dist/vapi.d.cts +78 -0
  116. package/dist/vapi.d.ts +78 -0
  117. package/dist/vapi.js +656 -0
  118. package/dist/vapi.js.map +1 -0
  119. package/dist/voltagent.cjs +509 -0
  120. package/dist/voltagent.cjs.map +1 -0
  121. package/dist/voltagent.d.cts +70 -0
  122. package/dist/voltagent.d.ts +70 -0
  123. package/dist/voltagent.js +503 -0
  124. package/dist/voltagent.js.map +1 -0
  125. package/dist/webhook-1ozGyksG.d.ts +37 -0
  126. package/dist/webhook-CG4om_DK.d.cts +37 -0
  127. package/dist/whatsapp.cjs +214 -0
  128. package/dist/whatsapp.cjs.map +1 -0
  129. package/dist/whatsapp.d.cts +81 -0
  130. package/dist/whatsapp.d.ts +81 -0
  131. package/dist/whatsapp.js +207 -0
  132. package/dist/whatsapp.js.map +1 -0
  133. package/package.json +355 -7
@@ -0,0 +1,113 @@
1
+ import { voice, llm } from '@livekit/agents';
2
+ import { bt as Conversation, h as Handle } from './client-DsIxZxZk.cjs';
3
+ import { a as ProofSource, A as AgentMemoryOption } from './shared-DTPWVlWm.cjs';
4
+ export { P as Proof, c as attestationProof } from './shared-DTPWVlWm.cjs';
5
+
6
+ /**
7
+ * Niadra for LiveKit Agents (Node, `@livekit/agents` 1.9).
8
+ *
9
+ * The same hook as LiveKit's own RAG recipe: `onUserTurnCompleted(turnCtx, newMessage)`. There
10
+ * the customer's final transcript is recorded, the conversation's pack goes into the turn's chat
11
+ * context as a system message right after the agent's instructions, and the suffix (live turns
12
+ * from other channels, what the caller's turn needs from memory, and deltas) goes after the new message. The read sends the caller's turn along. The turn context is
13
+ * a copy LiveKit builds for this reply only, so nothing piles up in the agent's history and the prompt prefix
14
+ * stays byte-identical turn after turn. Session events record the agent's answers (with the
15
+ * LLM usage LiveKit measured), handoffs between agents and the end of the call.
16
+ *
17
+ * `NiadraMemory` starts the caller's first read when it is built (`begin()`), so it runs while the
18
+ * session starts, and the first reply waits for it within `timeouts.contextVoiceStart` (1.5 s).
19
+ * After that no turn waits on a round trip for the pack: it comes from memory at once. While the
20
+ * caller is still speaking, each `user_input_transcribed` event (interim or final) sends the turn
21
+ * so far with `prefetch()`, in the background: the server warms the caller's memory, and once the
22
+ * words stop changing the SDK reads the turn with them, so the slots are there when LiveKit ends
23
+ * the turn. A turn waits at most 200 ms for such a read still on its way, then answers without
24
+ * the slots; a prefetch never holds or fails a turn.
25
+ *
26
+ * @example
27
+ * const caller = ctx.room.remoteParticipants.values().next().value;
28
+ * const convo = niadra.conversation({ subject: sipSubject(caller), channel: "voice", conversation_id: sipConversationId(caller, ctx.room.name) });
29
+ * const memory = new NiadraMemory({ conversation: convo, verify: attestationProof(caller.attributes["sip.h.x-stir-verstat"]) });
30
+ * const agent = new NiadraAgent({ instructions: "You are the support line of Acme.", memory });
31
+ * memory.attach(session);
32
+ * await session.start({ agent, room: ctx.room });
33
+ */
34
+
35
+ interface NiadraMemoryOptions {
36
+ /** The call, opened with `channel: "voice"` so reads use the voice view and budget. */
37
+ conversation: Conversation;
38
+ /**
39
+ * What the call proved before the first read, such as the carrier's attestation
40
+ * (`attestationProof(...)`). Recorded once with `verify()` before the first context.
41
+ */
42
+ verify?: ProofSource;
43
+ /** Records the agent's answers from `conversation_item_added`. Defaults to `true`. */
44
+ recordAgent?: boolean;
45
+ /** Puts the agent's own notes before the caller's context and adds its memory tools. */
46
+ agentMemory?: AgentMemoryOption;
47
+ }
48
+ /** Participant fields the helpers read. A `RemoteParticipant` has them. */
49
+ interface ParticipantLike {
50
+ identity: string;
51
+ attributes?: Record<string, string> | undefined;
52
+ }
53
+ /**
54
+ * The caller as a handle: the phone number LiveKit SIP puts in `sip.phoneNumber` (E.164, `+` added
55
+ * when missing), otherwise the participant identity as your app's user id.
56
+ */
57
+ declare function sipSubject(participant: ParticipantLike): Handle;
58
+ /** The call id LiveKit SIP puts in `sip.callID`, otherwise the room name. */
59
+ declare function sipConversationId(participant: ParticipantLike, roomName: string): string;
60
+ /**
61
+ * The memory of one call, shared by every agent that takes part in it. Put its
62
+ * `onUserTurnCompleted` in your agent (or use `NiadraAgent`), its `toolset()` in the agent's
63
+ * tools, and `attach()` it to the session.
64
+ */
65
+ declare class NiadraMemory {
66
+ readonly conversation: Conversation;
67
+ private readonly bridge;
68
+ private readonly recordAgent;
69
+ private readonly customerTurns;
70
+ private usage;
71
+ /** The final transcript segments of the turn the caller is still speaking. */
72
+ private heard;
73
+ constructor(options: NiadraMemoryOptions);
74
+ /**
75
+ * Records the customer's final transcript, then puts the pack after the instructions and the
76
+ * suffix after the new message, for this reply only. Never throws.
77
+ */
78
+ onUserTurnCompleted(turnCtx: llm.ChatContext, newMessage: llm.ChatMessage): Promise<void>;
79
+ /**
80
+ * Listens to the session: the agent's answers, handoffs between agents and the end of the call.
81
+ * Returns a function that stops listening.
82
+ */
83
+ attach(session: voice.AgentSession): () => void;
84
+ /** The navigation kit as LiveKit function tools, bound to this caller. */
85
+ tools(): llm.FunctionTool[];
86
+ /** The same tools grouped as one toolset, to add next to your own. */
87
+ toolset(): llm.Toolset;
88
+ /**
89
+ * Records a transfer to a person, such as a SIP transfer to the queue. Call it right before
90
+ * the transfer, so the attendant's screen can read the context at once.
91
+ */
92
+ handoffToHuman(reason?: string): Promise<void>;
93
+ private onItem;
94
+ /** The turn so far: its final segments and, when `transcript` is interim, that one too. */
95
+ private hearing;
96
+ private recordCustomer;
97
+ }
98
+ interface NiadraAgentOptions<UserData> extends voice.AgentOptions<UserData> {
99
+ memory: NiadraMemory;
100
+ /** Adds the navigation kit to the agent's tools. Defaults to `true`. */
101
+ historyTools?: boolean;
102
+ }
103
+ /**
104
+ * A LiveKit `Agent` with Niadra wired in: context before each reply and the navigation kit
105
+ * among its tools. Subclasses that override `onUserTurnCompleted` call `super.onUserTurnCompleted`.
106
+ */
107
+ declare class NiadraAgent<UserData = unknown> extends voice.Agent<UserData> {
108
+ readonly memory: NiadraMemory;
109
+ constructor({ memory, historyTools, ...options }: NiadraAgentOptions<UserData>);
110
+ onUserTurnCompleted(turnCtx: llm.ChatContext, newMessage: llm.ChatMessage): Promise<void>;
111
+ }
112
+
113
+ export { AgentMemoryOption, NiadraAgent, type NiadraAgentOptions, NiadraMemory, type NiadraMemoryOptions, type ParticipantLike, ProofSource, sipConversationId, sipSubject };
@@ -0,0 +1,113 @@
1
+ import { voice, llm } from '@livekit/agents';
2
+ import { bt as Conversation, h as Handle } from './client-DsIxZxZk.js';
3
+ import { a as ProofSource, A as AgentMemoryOption } from './shared-Bb5B59Bu.js';
4
+ export { P as Proof, c as attestationProof } from './shared-Bb5B59Bu.js';
5
+
6
+ /**
7
+ * Niadra for LiveKit Agents (Node, `@livekit/agents` 1.9).
8
+ *
9
+ * The same hook as LiveKit's own RAG recipe: `onUserTurnCompleted(turnCtx, newMessage)`. There
10
+ * the customer's final transcript is recorded, the conversation's pack goes into the turn's chat
11
+ * context as a system message right after the agent's instructions, and the suffix (live turns
12
+ * from other channels, what the caller's turn needs from memory, and deltas) goes after the new message. The read sends the caller's turn along. The turn context is
13
+ * a copy LiveKit builds for this reply only, so nothing piles up in the agent's history and the prompt prefix
14
+ * stays byte-identical turn after turn. Session events record the agent's answers (with the
15
+ * LLM usage LiveKit measured), handoffs between agents and the end of the call.
16
+ *
17
+ * `NiadraMemory` starts the caller's first read when it is built (`begin()`), so it runs while the
18
+ * session starts, and the first reply waits for it within `timeouts.contextVoiceStart` (1.5 s).
19
+ * After that no turn waits on a round trip for the pack: it comes from memory at once. While the
20
+ * caller is still speaking, each `user_input_transcribed` event (interim or final) sends the turn
21
+ * so far with `prefetch()`, in the background: the server warms the caller's memory, and once the
22
+ * words stop changing the SDK reads the turn with them, so the slots are there when LiveKit ends
23
+ * the turn. A turn waits at most 200 ms for such a read still on its way, then answers without
24
+ * the slots; a prefetch never holds or fails a turn.
25
+ *
26
+ * @example
27
+ * const caller = ctx.room.remoteParticipants.values().next().value;
28
+ * const convo = niadra.conversation({ subject: sipSubject(caller), channel: "voice", conversation_id: sipConversationId(caller, ctx.room.name) });
29
+ * const memory = new NiadraMemory({ conversation: convo, verify: attestationProof(caller.attributes["sip.h.x-stir-verstat"]) });
30
+ * const agent = new NiadraAgent({ instructions: "You are the support line of Acme.", memory });
31
+ * memory.attach(session);
32
+ * await session.start({ agent, room: ctx.room });
33
+ */
34
+
35
+ interface NiadraMemoryOptions {
36
+ /** The call, opened with `channel: "voice"` so reads use the voice view and budget. */
37
+ conversation: Conversation;
38
+ /**
39
+ * What the call proved before the first read, such as the carrier's attestation
40
+ * (`attestationProof(...)`). Recorded once with `verify()` before the first context.
41
+ */
42
+ verify?: ProofSource;
43
+ /** Records the agent's answers from `conversation_item_added`. Defaults to `true`. */
44
+ recordAgent?: boolean;
45
+ /** Puts the agent's own notes before the caller's context and adds its memory tools. */
46
+ agentMemory?: AgentMemoryOption;
47
+ }
48
+ /** Participant fields the helpers read. A `RemoteParticipant` has them. */
49
+ interface ParticipantLike {
50
+ identity: string;
51
+ attributes?: Record<string, string> | undefined;
52
+ }
53
+ /**
54
+ * The caller as a handle: the phone number LiveKit SIP puts in `sip.phoneNumber` (E.164, `+` added
55
+ * when missing), otherwise the participant identity as your app's user id.
56
+ */
57
+ declare function sipSubject(participant: ParticipantLike): Handle;
58
+ /** The call id LiveKit SIP puts in `sip.callID`, otherwise the room name. */
59
+ declare function sipConversationId(participant: ParticipantLike, roomName: string): string;
60
+ /**
61
+ * The memory of one call, shared by every agent that takes part in it. Put its
62
+ * `onUserTurnCompleted` in your agent (or use `NiadraAgent`), its `toolset()` in the agent's
63
+ * tools, and `attach()` it to the session.
64
+ */
65
+ declare class NiadraMemory {
66
+ readonly conversation: Conversation;
67
+ private readonly bridge;
68
+ private readonly recordAgent;
69
+ private readonly customerTurns;
70
+ private usage;
71
+ /** The final transcript segments of the turn the caller is still speaking. */
72
+ private heard;
73
+ constructor(options: NiadraMemoryOptions);
74
+ /**
75
+ * Records the customer's final transcript, then puts the pack after the instructions and the
76
+ * suffix after the new message, for this reply only. Never throws.
77
+ */
78
+ onUserTurnCompleted(turnCtx: llm.ChatContext, newMessage: llm.ChatMessage): Promise<void>;
79
+ /**
80
+ * Listens to the session: the agent's answers, handoffs between agents and the end of the call.
81
+ * Returns a function that stops listening.
82
+ */
83
+ attach(session: voice.AgentSession): () => void;
84
+ /** The navigation kit as LiveKit function tools, bound to this caller. */
85
+ tools(): llm.FunctionTool[];
86
+ /** The same tools grouped as one toolset, to add next to your own. */
87
+ toolset(): llm.Toolset;
88
+ /**
89
+ * Records a transfer to a person, such as a SIP transfer to the queue. Call it right before
90
+ * the transfer, so the attendant's screen can read the context at once.
91
+ */
92
+ handoffToHuman(reason?: string): Promise<void>;
93
+ private onItem;
94
+ /** The turn so far: its final segments and, when `transcript` is interim, that one too. */
95
+ private hearing;
96
+ private recordCustomer;
97
+ }
98
+ interface NiadraAgentOptions<UserData> extends voice.AgentOptions<UserData> {
99
+ memory: NiadraMemory;
100
+ /** Adds the navigation kit to the agent's tools. Defaults to `true`. */
101
+ historyTools?: boolean;
102
+ }
103
+ /**
104
+ * A LiveKit `Agent` with Niadra wired in: context before each reply and the navigation kit
105
+ * among its tools. Subclasses that override `onUserTurnCompleted` call `super.onUserTurnCompleted`.
106
+ */
107
+ declare class NiadraAgent<UserData = unknown> extends voice.Agent<UserData> {
108
+ readonly memory: NiadraMemory;
109
+ constructor({ memory, historyTools, ...options }: NiadraAgentOptions<UserData>);
110
+ onUserTurnCompleted(turnCtx: llm.ChatContext, newMessage: llm.ChatMessage): Promise<void>;
111
+ }
112
+
113
+ export { AgentMemoryOption, NiadraAgent, type NiadraAgentOptions, NiadraMemory, type NiadraMemoryOptions, type ParticipantLike, ProofSource, sipConversationId, sipSubject };
@@ -0,0 +1,398 @@
1
+ import { llm, voice } from '@livekit/agents';
2
+
3
+ // src/integrations/livekit.ts
4
+
5
+ // src/handles.ts
6
+ function build(type, value, scope, options) {
7
+ const handle = { type, value };
8
+ if (scope !== void 0) handle.scope = scope;
9
+ if (options?.subjectKind !== void 0) handle.subject_kind = options.subjectKind;
10
+ return handle;
11
+ }
12
+ var handles = {
13
+ /** A phone number in E.164 form, such as `+5511987654321`. */
14
+ phone: (e164, options) => build("phone_e164", e164, void 0, options),
15
+ email: (address, options) => build("email", address, void 0, options),
16
+ /** The WhatsApp id (`wa_id`) the Cloud API reports for a contact. */
17
+ waId: (value, options) => build("wa_id", value, void 0, options),
18
+ waJid: (value, options) => build("wa_jid", value, void 0, options),
19
+ waLid: (value, options) => build("wa_lid", value, void 0, options),
20
+ /** A business-scoped WhatsApp user id; `businessAccount` is the WhatsApp Business account it belongs to. */
21
+ waBsuid: (value, businessAccount, options) => build("wa_bsuid", value, businessAccount, options),
22
+ /** The user id in your own app or site. */
23
+ appUserId: (value, options) => build("app_user_id", value, void 0, options),
24
+ /** The id of a person or organization in a system of record; `system` names that system, such as `crm`. */
25
+ systemId: (value, system, options) => build("system_id", value, system, options),
26
+ /** An HMAC of a national document number; `country` is its ISO 3166-1 alpha-2 code. */
27
+ govIdHmac: (value, country, options) => build("gov_id_hmac", value, country, options),
28
+ /** An HMAC of a company registry number. Identifies an organization. */
29
+ orgRegistryHmac: (value, country) => build("org_registry_hmac", value, country, { subjectKind: "account" }),
30
+ /** An e-mail domain, such as `acme.com`. Identifies an organization. */
31
+ emailDomain: (domain) => build("email_domain", domain, void 0, { subjectKind: "account" }),
32
+ /** An anonymous visitor or device id, before the person is known. */
33
+ anonId: (value, options) => build("anon_id", value, void 0, options)
34
+ };
35
+
36
+ // src/context.ts
37
+ function emptyResult(error) {
38
+ return { text: "", suffix: "", variables: {}, pack: null, source: "none", response: null, error, constraints: null, state: null, coordination: null };
39
+ }
40
+
41
+ // src/integrations/shared.ts
42
+ var ATTESTED = { A: "V2", B: "V1", C: "V1" };
43
+ function attestationProof(attestation) {
44
+ if (!attestation) return null;
45
+ const value = attestation.trim().toUpperCase();
46
+ if (value.includes("FAILED") || value.startsWith("NO-")) return null;
47
+ const letter = value.length === 1 ? value : /PASSED-([ABC])$/.exec(value)?.[1];
48
+ const level = letter ? ATTESTED[letter] : void 0;
49
+ return level ? { method: "network_attestation", level } : null;
50
+ }
51
+ function isConversation(session) {
52
+ return "customer" in session && typeof session.customer === "function";
53
+ }
54
+ function errorName(error) {
55
+ return error instanceof Error ? error.name : typeof error;
56
+ }
57
+ var Bridge = class {
58
+ constructor(session, proof, memory = false) {
59
+ this.session = session;
60
+ this.proof = proof;
61
+ this.memory = memory;
62
+ }
63
+ session;
64
+ proof;
65
+ memory;
66
+ proving = null;
67
+ /** `begin()` started the first read of a call: the first `context()` waits for it with `ready()`. */
68
+ begun = false;
69
+ opened = false;
70
+ /**
71
+ * The context and, when enabled, the agent's notes, read side by side within the same budget.
72
+ * Never rejects.
73
+ */
74
+ async read(options = {}) {
75
+ const memoryParams = this.memory === false ? null : this.memory === true ? {} : withoutWrite(this.memory);
76
+ const [context, memory] = await Promise.all([
77
+ this.context(options),
78
+ memoryParams ? this.agentMemory(memoryParams, options) : Promise.resolve("")
79
+ ]);
80
+ return { context, memory, prefix: [memory, context.text].filter(Boolean).join("\n\n"), suffix: context.suffix };
81
+ }
82
+ get logger() {
83
+ return this.session.logger;
84
+ }
85
+ /**
86
+ * Starts the first read of a call now, in the background, after the proof when one was given (the
87
+ * pack depends on the level it proves): at call start, so the read runs while the call is set up.
88
+ * The first `context()` then waits for it within `timeouts.contextVoiceStart`. Never throws.
89
+ */
90
+ begin() {
91
+ const session = this.session;
92
+ if (!isConversation(session)) return;
93
+ const start = () => {
94
+ this.safe("start the first read", () => {
95
+ this.begun = session.begin();
96
+ });
97
+ };
98
+ if (this.proof === void 0 || this.proof === null) start();
99
+ else void this.verifyOnce().then(start);
100
+ }
101
+ /** Verifies once (when a proof was given), then reads the context. Never rejects. */
102
+ async context(options = {}) {
103
+ await this.verifyOnce();
104
+ try {
105
+ const session = this.session;
106
+ if (this.begun && !this.opened && isConversation(session)) {
107
+ this.opened = true;
108
+ await session.ready(options.timeout === void 0 ? {} : { timeout: options.timeout });
109
+ }
110
+ return await this.session.context(options);
111
+ } catch (error) {
112
+ this.logger.warn(`could not read context (${errorName(error)})`);
113
+ return emptyResult(null);
114
+ }
115
+ }
116
+ /** Records that `context` went into the prompt, unless it was empty for a reason other than the holdout. */
117
+ injected(context) {
118
+ if (!context.text && !context.suffix && context.response?.path !== "holdout") return;
119
+ this.safe("mark the injection", () => {
120
+ this.session.markInjected(context);
121
+ });
122
+ }
123
+ customer(text, options = {}) {
124
+ const session = this.session;
125
+ if (!text.trim() || !isConversation(session)) return;
126
+ this.safe("record the customer's turn", () => session.customer(text, options));
127
+ }
128
+ /** Sends the customer's turn so far, while they speak. In the background; never throws. */
129
+ prefetch(text) {
130
+ const session = this.session;
131
+ if (!text.trim() || !isConversation(session)) return;
132
+ this.safe("prefetch the turn", () => session.prefetch(text));
133
+ }
134
+ agent(text, options = {}) {
135
+ if (!text.trim()) return;
136
+ this.safe("record the agent's turn", () => this.session.agent(text, options));
137
+ }
138
+ /** Records a transfer. Tasks have no transfer; the call is ignored for them. */
139
+ async handoff(target, reason) {
140
+ const session = this.session;
141
+ if (!isConversation(session)) return;
142
+ try {
143
+ await session.handoff(reason ? { target, reason } : { target });
144
+ } catch (error) {
145
+ this.logger.warn(`could not record the handoff (${errorName(error)})`);
146
+ }
147
+ }
148
+ async end() {
149
+ try {
150
+ await this.session.end();
151
+ } catch (error) {
152
+ this.logger.warn(`could not end the session (${errorName(error)})`);
153
+ }
154
+ }
155
+ /** The navigation kit as plain specs (with the agent's memory tools when enabled), or none for a task about an object only. */
156
+ tools() {
157
+ let bound;
158
+ try {
159
+ const memory = this.memory;
160
+ bound = this.session.tools(memory === false ? {} : { agentMemory: true, writeAgentMemory: memory !== true && memory.write === true });
161
+ } catch (error) {
162
+ this.logger.warn(`could not build the tools (${errorName(error)})`);
163
+ return [];
164
+ }
165
+ return bound ? toolSpecs(bound) : [];
166
+ }
167
+ async agentMemory(params, options) {
168
+ try {
169
+ const result = await this.session.agentMemory(params, options.timeout === void 0 ? {} : { timeout: options.timeout });
170
+ return result.text;
171
+ } catch (error) {
172
+ this.logger.warn(`could not read the agent's memory (${errorName(error)})`);
173
+ return "";
174
+ }
175
+ }
176
+ async verifyOnce() {
177
+ if (this.proof === void 0 || this.proof === null) return;
178
+ this.proving ??= this.runVerify();
179
+ await this.proving;
180
+ }
181
+ async runVerify() {
182
+ try {
183
+ const source = this.proof;
184
+ const proof = typeof source === "function" ? await source() : source;
185
+ if (!proof) return;
186
+ await this.session.verify(proof);
187
+ } catch (error) {
188
+ this.logger.warn(`could not record the verification (${errorName(error)})`);
189
+ }
190
+ }
191
+ safe(what, run) {
192
+ try {
193
+ run();
194
+ } catch (error) {
195
+ this.logger.warn(`could not ${what} (${errorName(error)})`);
196
+ }
197
+ }
198
+ };
199
+ function withoutWrite(option) {
200
+ const { write: _, ...params } = option;
201
+ return params;
202
+ }
203
+ function toolSpecs(bound) {
204
+ return bound.definitions.map(({ function: definition }) => ({
205
+ name: definition.name,
206
+ description: definition.description,
207
+ parameters: definition.parameters,
208
+ execute: (args) => bound.call(definition.name, args)
209
+ }));
210
+ }
211
+ function count(value) {
212
+ return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : null;
213
+ }
214
+
215
+ // src/integrations/livekit.ts
216
+ var LEADING_IDS = /* @__PURE__ */ new Set(["lk.agent_task.instructions", "lk.expressive.instructions"]);
217
+ var INSTRUCTION_ROLES = /* @__PURE__ */ new Set(["system", "developer"]);
218
+ var CONTEXT_ID = "niadra.context";
219
+ var rawTool = llm.tool;
220
+ var SUFFIX_ID = "niadra.suffix";
221
+ function sipSubject(participant) {
222
+ const phone = participant.attributes?.["sip.phoneNumber"]?.trim();
223
+ if (phone) return handles.phone(phone.startsWith("+") ? phone : `+${phone}`);
224
+ return handles.appUserId(participant.identity);
225
+ }
226
+ function sipConversationId(participant, roomName) {
227
+ const callId = participant.attributes?.["sip.callID"]?.trim();
228
+ if (callId) return callId;
229
+ return roomName;
230
+ }
231
+ var NiadraMemory = class {
232
+ conversation;
233
+ bridge;
234
+ recordAgent;
235
+ customerTurns = /* @__PURE__ */ new Set();
236
+ usage = null;
237
+ /** The final transcript segments of the turn the caller is still speaking. */
238
+ heard = [];
239
+ constructor(options) {
240
+ this.conversation = options.conversation;
241
+ this.bridge = new Bridge(options.conversation, options.verify, options.agentMemory);
242
+ this.bridge.begin();
243
+ this.recordAgent = options.recordAgent ?? true;
244
+ }
245
+ /**
246
+ * Records the customer's final transcript, then puts the pack after the instructions and the
247
+ * suffix after the new message, for this reply only. Never throws.
248
+ */
249
+ async onUserTurnCompleted(turnCtx, newMessage) {
250
+ this.recordCustomer(newMessage);
251
+ const read = await this.bridge.read();
252
+ try {
253
+ const items = turnCtx.items;
254
+ for (const id of [CONTEXT_ID, SUFFIX_ID]) {
255
+ const index = items.findIndex((item) => item.id === id);
256
+ if (index >= 0) items.splice(index, 1);
257
+ }
258
+ if (read.prefix) {
259
+ let position = 0;
260
+ while (position < items.length && isInstruction(items[position])) position++;
261
+ const message = turnCtx.addMessage({ id: CONTEXT_ID, role: "system", content: read.prefix });
262
+ items.splice(items.indexOf(message), 1);
263
+ items.splice(position, 0, message);
264
+ }
265
+ if (read.suffix) turnCtx.addMessage({ id: SUFFIX_ID, role: "system", content: read.suffix });
266
+ this.bridge.injected(read.context);
267
+ } catch (error) {
268
+ this.bridge.logger.warn(`could not inject context (${errorName(error)})`);
269
+ }
270
+ }
271
+ /**
272
+ * Listens to the session: the agent's answers, handoffs between agents and the end of the call.
273
+ * Returns a function that stops listening.
274
+ */
275
+ attach(session) {
276
+ const onItem = (event) => {
277
+ this.onItem(event.item);
278
+ };
279
+ const onMetrics = (event) => {
280
+ const metrics = event.metrics;
281
+ if (metrics.type === "llm_metrics") this.usage = usageOf(metrics);
282
+ };
283
+ const onClose = () => {
284
+ void this.bridge.end();
285
+ };
286
+ const onTranscribed = (event) => {
287
+ this.bridge.prefetch(this.hearing(event.transcript, event.isFinal));
288
+ };
289
+ session.on(voice.AgentSessionEventTypes.ConversationItemAdded, onItem);
290
+ session.on(voice.AgentSessionEventTypes.MetricsCollected, onMetrics);
291
+ session.on(voice.AgentSessionEventTypes.UserInputTranscribed, onTranscribed);
292
+ session.on(voice.AgentSessionEventTypes.Close, onClose);
293
+ return () => {
294
+ session.off(voice.AgentSessionEventTypes.ConversationItemAdded, onItem);
295
+ session.off(voice.AgentSessionEventTypes.MetricsCollected, onMetrics);
296
+ session.off(voice.AgentSessionEventTypes.UserInputTranscribed, onTranscribed);
297
+ session.off(voice.AgentSessionEventTypes.Close, onClose);
298
+ };
299
+ }
300
+ /** The navigation kit as LiveKit function tools, bound to this caller. */
301
+ tools() {
302
+ return this.bridge.tools().map(
303
+ (spec) => rawTool({
304
+ name: spec.name,
305
+ description: spec.description,
306
+ parameters: spec.parameters,
307
+ // LiveKit serializes what a tool returns; the parsed result keeps the model's JSON unescaped.
308
+ execute: async (args) => parsed(await spec.execute(args ?? {}))
309
+ })
310
+ );
311
+ }
312
+ /** The same tools grouped as one toolset, to add next to your own. */
313
+ toolset() {
314
+ return new llm.Toolset({ id: "niadra", tools: this.tools() });
315
+ }
316
+ /**
317
+ * Records a transfer to a person, such as a SIP transfer to the queue. Call it right before
318
+ * the transfer, so the attendant's screen can read the context at once.
319
+ */
320
+ handoffToHuman(reason) {
321
+ return this.bridge.handoff("human", reason);
322
+ }
323
+ onItem(item) {
324
+ if (item.type === "agent_handoff") {
325
+ const reason = item.oldAgentId ? `${item.oldAgentId} to ${item.newAgentId}` : `to ${item.newAgentId}`;
326
+ void this.bridge.handoff("agent", reason);
327
+ return;
328
+ }
329
+ if (item.role === "user") {
330
+ this.recordCustomer(item);
331
+ return;
332
+ }
333
+ if (item.role !== "assistant" || !this.recordAgent) return;
334
+ const text = item.textContent ?? "";
335
+ const usage = this.usage;
336
+ this.usage = null;
337
+ this.bridge.agent(text, usage ? { usage, idempotency_key: item.id } : { idempotency_key: item.id });
338
+ }
339
+ /** The turn so far: its final segments and, when `transcript` is interim, that one too. */
340
+ hearing(transcript, isFinal) {
341
+ const text = transcript.trim();
342
+ if (isFinal && text) {
343
+ this.heard.push(text);
344
+ return this.heard.join(" ");
345
+ }
346
+ return (text ? [...this.heard, text] : this.heard).join(" ");
347
+ }
348
+ recordCustomer(message) {
349
+ if (this.customerTurns.has(message.id)) return;
350
+ this.customerTurns.add(message.id);
351
+ this.heard = [];
352
+ const confidence = message.transcriptConfidence;
353
+ this.bridge.customer(message.textContent ?? "", {
354
+ idempotency_key: message.id,
355
+ ...typeof confidence === "number" && confidence >= 0 && confidence <= 1 ? { stt_confidence: confidence } : {}
356
+ });
357
+ }
358
+ };
359
+ var NiadraAgent = class extends voice.Agent {
360
+ memory;
361
+ constructor({ memory, historyTools = true, ...options }) {
362
+ const tools = historyTools ? withToolset(options.tools, memory.toolset()) : options.tools;
363
+ super(tools === void 0 ? options : { ...options, tools });
364
+ this.memory = memory;
365
+ }
366
+ async onUserTurnCompleted(turnCtx, newMessage) {
367
+ await this.memory.onUserTurnCompleted(turnCtx, newMessage);
368
+ }
369
+ };
370
+ function withToolset(tools, toolset) {
371
+ const base = tools === void 0 ? [] : llm.toToolContext(tools).tools;
372
+ return new llm.ToolContext([...base, toolset]);
373
+ }
374
+ function parsed(text) {
375
+ try {
376
+ return JSON.parse(text);
377
+ } catch {
378
+ return text;
379
+ }
380
+ }
381
+ function isInstruction(item) {
382
+ if (!item) return false;
383
+ if (LEADING_IDS.has(item.id)) return true;
384
+ return item.type === "message" && INSTRUCTION_ROLES.has(item.role);
385
+ }
386
+ function usageOf(metrics) {
387
+ const provider = metrics.metadata?.modelProvider?.toLowerCase();
388
+ const model = metrics.metadata?.modelName;
389
+ const prompt = count(metrics.promptTokens);
390
+ if (!provider || !model || provider === "unknown" || model === "unknown" || !prompt) return null;
391
+ const cached = count(metrics.promptCachedTokens) ?? 0;
392
+ const written = count(metrics.cacheCreationTokens) ?? 0;
393
+ return { provider, model, prompt_tokens: Math.max(prompt, cached + written), cached_tokens: cached, cache_write_tokens: written };
394
+ }
395
+
396
+ export { NiadraAgent, NiadraMemory, attestationProof, sipConversationId, sipSubject };
397
+ //# sourceMappingURL=livekit.js.map
398
+ //# sourceMappingURL=livekit.js.map