@rimelabs/sdk 0.1.0-alpha.2 → 0.1.0-alpha.21

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 (85) hide show
  1. package/CHANGELOG.md +155 -0
  2. package/LICENSE-PRISM +201 -0
  3. package/NOTICE-PRISM +9 -0
  4. package/README.md +279 -114
  5. package/dist/auth.d.ts +9 -3
  6. package/dist/auth.js +12 -4
  7. package/dist/cancellation.d.ts +6 -0
  8. package/dist/cancellation.js +54 -0
  9. package/dist/client.d.ts +24 -23
  10. package/dist/client.js +41 -92
  11. package/dist/grpc.d.ts +27 -0
  12. package/dist/grpc.js +175 -0
  13. package/dist/index.d.ts +10 -2
  14. package/dist/index.js +6 -2
  15. package/dist/pcm.d.ts +21 -0
  16. package/dist/pcm.js +52 -0
  17. package/dist/realtime/async.d.ts +23 -0
  18. package/dist/realtime/async.js +87 -0
  19. package/dist/realtime/audio.d.ts +2 -0
  20. package/dist/realtime/audio.js +11 -0
  21. package/dist/realtime/client.d.ts +12 -0
  22. package/dist/realtime/client.js +139 -0
  23. package/dist/realtime/conversation.d.ts +47 -0
  24. package/dist/realtime/conversation.js +321 -0
  25. package/dist/realtime/faults.d.ts +2 -0
  26. package/dist/realtime/faults.js +25 -0
  27. package/dist/realtime/protocol.d.ts +99 -0
  28. package/dist/realtime/protocol.js +432 -0
  29. package/dist/realtime/session.d.ts +79 -0
  30. package/dist/realtime/session.js +808 -0
  31. package/dist/realtime/settings.d.ts +11 -0
  32. package/dist/realtime/settings.js +142 -0
  33. package/dist/realtime/transport.d.ts +16 -0
  34. package/dist/realtime/transport.js +86 -0
  35. package/dist/realtime/types.d.ts +292 -0
  36. package/dist/realtime/types.js +12 -0
  37. package/dist/stt/audio.d.ts +15 -0
  38. package/dist/stt/audio.js +46 -0
  39. package/dist/stt/client.d.ts +17 -0
  40. package/dist/stt/client.js +121 -0
  41. package/dist/stt/policy.d.ts +11 -0
  42. package/dist/stt/policy.js +11 -0
  43. package/dist/stt/protocol.d.ts +14 -0
  44. package/dist/stt/protocol.js +76 -0
  45. package/dist/stt/queue.d.ts +16 -0
  46. package/dist/stt/queue.js +45 -0
  47. package/dist/stt/stream.d.ts +48 -0
  48. package/dist/stt/stream.js +224 -0
  49. package/dist/stt/transport.d.ts +15 -0
  50. package/dist/stt/transport.js +94 -0
  51. package/dist/stt/types.d.ts +51 -0
  52. package/dist/stt/types.js +1 -0
  53. package/dist/{audio.d.ts → tts/audio.d.ts} +9 -0
  54. package/dist/{audio.js → tts/audio.js} +44 -4
  55. package/dist/tts/buffer.d.ts +3 -0
  56. package/dist/tts/buffer.js +53 -0
  57. package/dist/tts/client.d.ts +53 -0
  58. package/dist/tts/client.js +151 -0
  59. package/dist/tts/lexicon.d.ts +7 -0
  60. package/dist/tts/lexicon.js +23 -0
  61. package/dist/{policy.d.ts → tts/policy.d.ts} +2 -3
  62. package/dist/tts/policy.js +41 -0
  63. package/dist/{stream.d.ts → tts/stream.d.ts} +15 -2
  64. package/dist/{stream.js → tts/stream.js} +54 -30
  65. package/dist/tts/timestamps.d.ts +24 -0
  66. package/dist/tts/timestamps.js +46 -0
  67. package/dist/{transport.d.ts → tts/transport.d.ts} +10 -13
  68. package/dist/tts/transport.js +133 -0
  69. package/dist/validation.d.ts +5 -0
  70. package/dist/validation.js +28 -0
  71. package/docs/realtime.md +537 -0
  72. package/docs/stt.md +186 -0
  73. package/docs/tts.md +284 -0
  74. package/package.json +14 -12
  75. package/dist/policy.js +0 -82
  76. package/dist/sentences.d.ts +0 -21
  77. package/dist/sentences.js +0 -142
  78. package/dist/transport.js +0 -223
  79. package/vendor/LICENSE.blingfire +0 -21
  80. package/vendor/README.md +0 -11
  81. package/vendor/blingfire.cjs +0 -4
  82. package/vendor/blingfire.upstream.cjs +0 -1
  83. package/vendor/blingfire.wasm +0 -0
  84. /package/dist/{queue.d.ts → tts/queue.d.ts} +0 -0
  85. /package/dist/{queue.js → tts/queue.js} +0 -0
@@ -0,0 +1,537 @@
1
+ # Realtime / Prism for Node.js
2
+
3
+ The endpoint must use Rime's `prism.` events and `prism_` fields. Endpoints
4
+ that use the former `prsm` names are not supported.
5
+
6
+ Keep one event consumer running while your application sends input, runs tools,
7
+ and plays audio. The SDK manages the protocol state. Your application owns
8
+ microphone capture, tool execution, and playback.
9
+
10
+ Start with the [recorded-speech quick start](../README.md#prism-send-recorded-speech)
11
+ to save a spoken reply without audio hardware.
12
+
13
+ For live conversations, keep the session open after a response is cancelled.
14
+ Report playback from the output clock. Saving a file is not playback.
15
+ Typed turns also support mixed text and voice applications.
16
+
17
+ ## Optional conversation runner
18
+
19
+ Use `runConversation` to connect your audio adapters once. It sends input,
20
+ queues output, handles interruption, reports measured playback, and stops its
21
+ operations when the conversation ends. The direct session methods remain available.
22
+
23
+ ```ts
24
+ import { Rime, runConversation } from "@rimelabs/sdk";
25
+
26
+ const client = new Rime();
27
+ const controller = new AbortController();
28
+ try {
29
+ const session = await client.realtime.connect();
30
+ // microphone and speaker are adapters owned by your application.
31
+ await runConversation(session, {
32
+ source: microphone,
33
+ playback: speaker,
34
+ onEvent(event) {
35
+ if (event.payload.kind === "transcript.final")
36
+ console.log(event.payload.text);
37
+ },
38
+ signal: controller.signal,
39
+ });
40
+ } finally {
41
+ await client.close();
42
+ }
43
+ ```
44
+
45
+ The input adapter implements `ConversationSource`.
46
+ `read(signal: AbortSignal): Promise<AudioChunk | null>` returns one chunk,
47
+ with an optional format, or `null` at EOF. An omitted format uses 16 kHz mono
48
+ PCM16. Abort must reject and release a pending
49
+ read. The runner awaits each `session.sendAudio` call before reading again. It
50
+ uses the session's existing PCM conversion and transport backpressure. Input
51
+ continues during output playback.
52
+
53
+ The playback adapter implements `ConversationPlayback`:
54
+
55
+ | Method | Contract |
56
+ | --- | --- |
57
+ | `open(output: OutputRef): ConversationOutput` | Queue this output after previously opened outputs. It can be opened before the first audio chunk. |
58
+ | `interrupt(outputs: readonly OutputRef[]): void` | Stop the selected outputs as one batch, discard their unplayed audio, then resume remaining output in queue order. |
59
+ | `stop(signal: AbortSignal): Promise<void>` | Confirm all playback has stopped and queued audio is discarded. Keep caller-owned resources open. |
60
+
61
+ Each `ConversationOutput` implements:
62
+
63
+ | Method | Contract |
64
+ | --- | --- |
65
+ | `write(chunk: AudioChunk): void` | Queue 24 kHz mono PCM16 without blocking. Bound the queue and throw on overflow. |
66
+ | `end(): void` | Accept no more audio. Let queued and device audio drain. |
67
+ | `completion(signal: AbortSignal): Promise<number>` | Return actual played milliseconds after drain or interruption. Reject if measurement fails or the signal aborts. |
68
+
69
+ All synchronous adapter methods must return promptly. Completion can be awaited
70
+ before `end()`. Keep receipts separate for each `OutputRef`, even when a new
71
+ response starts before an old receipt completes. Play outputs in the order they
72
+ were opened, using one FIFO audio queue. Do not mix outputs, interleave their
73
+ audio, or play later outputs before earlier outputs finish or are interrupted.
74
+ Prism assumes that interrupting a partially played reply leaves later replies
75
+ in the same turn unheard. Independent concurrent playback violates that assumption.
76
+ The runner passes all affected output references in one `interrupt` call. Stop
77
+ them and discard their queued audio before starting any remaining output. Then
78
+ resume valid output in queue order, without waiting for another chunk or event.
79
+ Interruption must work after `end()` and tolerate repeated calls. A remote
80
+ adapter must preserve the batch, for example with one message containing all
81
+ selected output IDs. Shutdown stops all output and must not resume playback.
82
+ Receipt delivery can be delayed or arrive out of order; audible playback cannot.
83
+ A receipt includes the device buffer where device timing is available. Bytes
84
+ received or queued do not measure playback. Zero is valid for an output that
85
+ played no audio.
86
+
87
+ Abort must release each pending adapter operation and remove its listeners.
88
+ Cancellation of a completion wait must not cancel the device receipt. The runner
89
+ stops input, event processing, and report submission on exit. It then awaits
90
+ `stop(signal)` before aborting remaining receipt waits. The stop signal is
91
+ independent of caller cancellation and has a five-second deadline. `stop` must
92
+ resolve only after local or remote playback has stopped, including device
93
+ buffers. Sending a stop command is not confirmation. It must reject and release
94
+ its wait when the stop signal aborts. The runner awaits this cancellation cleanup
95
+ before it returns, so cleanup can extend beyond the deadline. A missing confirmation raises
96
+ `RimeTimeoutError`; disconnects and other shutdown failures must reject too.
97
+ The caller opens and closes the adapter resources. The runner does not open
98
+ audio devices or host a browser.
99
+
100
+ `onEvent` is optional. It receives every event read from `session.events`, in
101
+ order, after the runner applies audio coordination. It must be synchronous and
102
+ nonblocking. Returning a promise is rejected. Callback exceptions reach the
103
+ caller. The callback can schedule application work. Continue to use
104
+ `submitToolResult` and `continueReply`; the runner neither registers nor executes
105
+ tools. The application must cancel and await its own tool tasks. Response
106
+ failures and nonterminal faults are forwarded for application handling. Session
107
+ failures reject the runner promise.
108
+
109
+ The runner is the only event reader. Do not consume `session.events` at the same
110
+ time, call `reportPlayback` independently, or send a second audio source. Other
111
+ session methods remain available for manual tools and application actions.
112
+
113
+ On confirmed `speech.started`, the runner uses the interruption setting of the
114
+ latest reply that is still generating or has unmeasured physical playback. It
115
+ resolves that setting from the reply's revision when the session receives the
116
+ reply. Updates and late event consumption cannot change it. If interruption is
117
+ enabled, the runner stops affected output, waits for the interrupted receipt,
118
+ and reports the measured position. It ignores late audio for that response.
119
+ A `speech.hint` alone does not stop playback. A server
120
+ `truncate_not_current` refusal is forwarded as an event; it does not stop a newer
121
+ response. Finished reports wait for generation completion and every output
122
+ receipt, including outstanding playback from earlier responses. Prism treats
123
+ the latest drain token as confirmation that all earlier playback has stopped.
124
+ An abandoned response also stops its dependent tool continuations, identified
125
+ by response parent references. Unrelated later turns remain eligible to play.
126
+ Late audio from an interrupted response or its continuations is discarded.
127
+ The runner retains at most 128 response parent references.
128
+ A report retains the measured duration of its own response. Waiting for earlier
129
+ playback does not block events or input, and does not wait for later responses.
130
+
131
+ Once called, the runner owns the session's active lifetime. Input EOF, explicit
132
+ session closure, abort, or failure stops all runner operations and closes the
133
+ session. EOF stops remaining playback and waits for shutdown confirmation.
134
+ Abort, including an abort received during cleanup, waits for playback shutdown
135
+ confirmation or its five-second deadline, then awaits adapter cancellation cleanup
136
+ before rejecting with `signal.reason`.
137
+ Explicit session closure ends normally even with playback reports pending;
138
+ remote connection failures reject with the session error.
139
+ Expected cancellation errors during cleanup are ignored. Other cleanup errors
140
+ reach the caller if there is no original failure. They do not replace an
141
+ existing failure or caller abort. The signal is optional. Use
142
+ an application deadline if an adapter can wait indefinitely.
143
+
144
+ The runner adds no input or callback queue. It retains at most 64 responses,
145
+ 128 output streams, and 256 pending operations. Exceeding a limit throws
146
+ `RimeResourceLimitError`. The adapters must also bound their audio queues.
147
+
148
+ The [live voice example](https://github.com/rimelabs/rime-sdk/blob/main/typescript/examples/realtime/conversation.ts) supplies the
149
+ [browser audio adapter](https://github.com/rimelabs/rime-sdk/blob/main/typescript/examples/common/browser-audio.ts). Browser hosting
150
+ stays in the example. Its player uses `AudioContext.getOutputTimestamp()` for
151
+ receipts and shutdown confirmation. A disconnect cannot confirm a remote stop.
152
+ The [manual tool example](https://github.com/rimelabs/rime-sdk/blob/main/typescript/examples/realtime/manual-tools.ts) keeps
153
+ tool execution outside the runner.
154
+
155
+ ## Connect
156
+
157
+ Use `await client.realtime.connect()` with `RIME_API_KEY` set. It opens a WebSocket,
158
+ initializes one conversation, and returns a `RealtimeSession`. Use
159
+ `session.update` to change instructions, tools, voice, or interruption.
160
+ Use `session.setResponseMetadata` to replace reply labels.
161
+
162
+ | Option | Required? | Default and behavior |
163
+ | --- | --- | --- |
164
+ | `endpoint` | No | Defaults to `wss://prism.api.rime.ai/v1/realtime`. Overrides must be full URLs; plain `ws://` is allowed only for localhost. |
165
+ | `voice` | If the server has no default voice | Omitted or `null` uses the server default. Supplied strings must be nonblank. |
166
+ | `instructions` | No | Omitted or `null` uses server defaults. Supplied strings must be nonblank. |
167
+ | `tools` | No | `[]`. Each tool requires `name` and a JSON Schema `parameters` object. `description` defaults to `""`. |
168
+ | `interruptOnSpeech` | No | `true`. Requests interruption when the server confirms user speech. |
169
+ | `turnDetection` | No | Omitted settings use the server's turn timing defaults. |
170
+ | `timeouts` | No | `{ connectS: 10, readyS: 30, requestS: 10 }`. Each field is optional. Units are seconds. |
171
+ | `model` | No | `"prism"`, the only accepted value. |
172
+ | `requestId` | No | Your id for the session. Sent as `x-request-id`. It must be nonblank printable ASCII, or the SDK throws `RimeInputError`. Prism uses it if it is at most 128 characters, else makes its own. |
173
+ | `responseMetadata` | No | Labels every reply carries in `response.started.metadata`, such as `{ step: "3" }`: up to 16 strings, keys up to 64 characters and values up to 512, no key starting with `prism_`. Prism refuses labels that break these limits, or keys starting with `prism_`, with `RimeRealtimeError` code `invalid_session`. |
174
+ | `signal` | No | An `AbortSignal` that cancels connection setup. It does not cancel an established session. |
175
+
176
+ Credentials come from `new Rime({ apiKey: "..." })` or `RIME_API_KEY`. Prism uses
177
+ `Authorization: Bearer <key>`, without a TTS token exchange. TTS endpoint, model,
178
+ voice, and timeout settings do not apply to Realtime.
179
+
180
+ After connection, `session.info` contains `sessionId`, `requestId`, `model`,
181
+ `voice`, `interruptOnSpeech`, `turnDetection`, `responseMetadata`,
182
+ `toolResultTimeoutS`, and `toolContinuationTimeoutS`. `requestId` is the id
183
+ Prism's logs use; include it when you report a problem. It is `null` on servers
184
+ that do not send it. An event's or error's `requestId` names the SDK request
185
+ that caused it instead.
186
+ Check the effective interruption setting there. Tool wait durations are total
187
+ server deadlines, not remaining time. A `null` duration can mean an omitted
188
+ field; it does not prove that no deadline exists.
189
+
190
+ Close the session in `finally` with `await session.close()`. Closing the parent
191
+ client closes all its TTS and Realtime operations. Both objects also support
192
+ `Symbol.asyncDispose`. Repeated close calls are safe.
193
+
194
+ ## Optional turn timing
195
+
196
+ Pass a `TurnDetection` object to override how readily Prism ends a spoken turn.
197
+ Each field is optional; omitted fields use server defaults.
198
+
199
+ | Field | Values | Current server default |
200
+ | --- | --- | --- |
201
+ | `eagerness` | `"low"`, `"medium"`, `"high"`, `"auto"` | `"auto"`, equivalent to `"medium"` |
202
+ | `endOfTurnTimeoutMs` | Integer from 1,000 through 10,000 | 5,000 ms |
203
+
204
+ `"low"` waits for more certainty that the user has finished. `"high"` answers
205
+ sooner, with more risk of cutting in. The timeout is a maximum silence limit,
206
+ not a fixed delay before each reply. If the server falls back to its local
207
+ voice activity detector, it ends turns after 500 ms of silence instead.
208
+
209
+ ```typescript
210
+ const session = await client.realtime.connect({
211
+ turnDetection: { eagerness: "low", endOfTurnTimeoutMs: 5000 },
212
+ });
213
+ try {
214
+ console.log(session.info.turnDetection);
215
+ // Send input and consume session.events here.
216
+ } finally {
217
+ await session.close();
218
+ }
219
+ ```
220
+
221
+ `session.info.turnDetection` is a `TurnDetectionInfo` with the settings the
222
+ server reported in its initialization acknowledgment. A missing field is
223
+ `null`; the SDK does not substitute a requested value or assume a server
224
+ default. An unrecognized server eagerness becomes `"unknown"`.
225
+ `interruptOnSpeech` remains a separate connection option.
226
+
227
+ ## Update session settings
228
+
229
+ This method is available in the repository source and requires an SDK release
230
+ that includes session updates.
231
+
232
+ ```ts
233
+ const info = await session.update({
234
+ instructions: "Handle billing. Use only the supplied tools.",
235
+ tools: [refundTool], // An existing ToolDefinition.
236
+ });
237
+ console.log(info.settingsRevision);
238
+
239
+ // For a transfer triggered by a tool call:
240
+ await session.submitToolResult(call, "Transferred to billing.");
241
+ await session.continueReply(parent);
242
+ ```
243
+
244
+ `RealtimeUpdateOptions` has optional `instructions`, `tools`, `voice`,
245
+ `interruptOnSpeech`, and `signal` fields. Omitted fields and `undefined` keep
246
+ their values. `null` is not accepted.
247
+
248
+ Omitted fields keep their values. A supplied tool list replaces the complete
249
+ list. Empty instructions clear the prompt; an empty tool list removes all tools.
250
+ An empty voice selects the deployment default. Instructions and tools supplied
251
+ together change in one request.
252
+
253
+ The method waits for the matching `session.updated` acknowledgment and returns
254
+ session information. The stored session information changes only after that
255
+ acknowledgment. A rejected update leaves it unchanged and keeps the connection
256
+ open. Cancellation after submission does not undo an accepted update.
257
+
258
+ Updates do not wait for turn readiness or tool results. Each reply keeps the
259
+ settings it began with. A reply prepared on an early turn-end guess can be
260
+ announced after an update but still use the previous settings revision.
261
+ Modalities, eagerness, and the end-of-turn timeout cannot change mid-session.
262
+
263
+ `SessionInfo`, `ResponseStarted`, and `ResponseEnded` expose `settingsRevision`.
264
+ It is `null` when the server does not publish it. The field is optional on
265
+ caller-created values. Users do not need to manage it. Use each reply's revision
266
+ to identify its settings, rather than the latest session revision.
267
+
268
+ ## Choose an operation
269
+
270
+ Every method returns a promise. Arguments without `?` are required.
271
+
272
+ | Method | Result and behavior |
273
+ | --- | --- |
274
+ | `update(options?)` | Changes supplied settings without reconnecting; returns `SessionInfo` after acknowledgment. |
275
+ | `sendText(text, options?)` | Starts a typed user turn after readiness; returns `ResponseRef` at response creation. |
276
+ | `sendAudio(chunk, options?)` | Sends microphone audio with transport backpressure. |
277
+ | `requestReply(options?)` | Requests a reply without new user input; returns `ResponseRef`. Options can include `instruction` and `toolCall`. |
278
+ | `addMessage(role, text, options?)` | Adds user or assistant history without a reply; returns `ItemRef` after acknowledgment. |
279
+ | `submitToolResult(call, output, options?)` | Records a string result and waits for acknowledgment. |
280
+ | `continueReply(parent, options?)` | Waits for parent completion and all tool result acknowledgments, then returns a new `ResponseRef`. |
281
+ | `cancel(response, options?)` | Requests generation cancellation and waits for a terminal response or refusal. |
282
+ | `clearAudio(options?)` | Clears buffered input and resets audio conversion after acknowledgment. |
283
+ | `setResponseMetadata(metadata, options?)` | Replaces the labels for later replies once Prism confirms. |
284
+ | `reportPlayback(report, options?)` | Reports interrupted or finished playback. |
285
+ | `close()` | Closes the session and releases its resources. |
286
+
287
+ Operation options accept `signal?: AbortSignal`. `requestReply` accepts its
288
+ instruction, tool call, and signal in one object. Methods other than `update`,
289
+ `sendText`, `requestReply`, `addMessage`, and `continueReply` return `Promise<void>`.
290
+
291
+ Text must be nonblank and contain at most 4,000 Unicode characters. History
292
+ roles are `"user"` and `"assistant"`. A reply instruction applies to one response;
293
+ it does not replace session instructions.
294
+
295
+ A returned `ResponseRef` means generation started. Read `response.ended` for the
296
+ terminal status. Generation can finish before cancellation takes effect, so a
297
+ successful `cancel()` does not guarantee a `"cancelled"` status.
298
+
299
+ ## Send audio
300
+
301
+ Pass a plain object with `data: Uint8Array`. Node.js `Buffer` is also accepted.
302
+ The optional `format` object defaults to 16 kHz mono signed little-endian PCM16.
303
+
304
+ ```typescript
305
+ await session.sendAudio({
306
+ data: pcmBytes,
307
+ format: { sampleRate: 48000, channels: 2 },
308
+ });
309
+ ```
310
+
311
+ Supported `sampleRate` values are `8000`, `16000`, `24000`, and `48000`.
312
+ Channels can be `1` or `2`; `encoding` can only be `"pcm_s16le"`. Each field in
313
+ `format` is optional. Chunks must contain complete sample frames and at most
314
+ 192,000 bytes. The SDK downmixes and resamples to 16 kHz mono, retaining state
315
+ across calls. A format change or acknowledged `clearAudio()` resets that state.
316
+
317
+ Await each send and bound your capture queue. Keep audio flowing, including
318
+ silence and while the assistant speaks or tools run. Prism detects and commits
319
+ speech; there is no manual commit method. Echo cancellation belongs to your app.
320
+
321
+ Received `audio.delta` events carry 24 kHz mono PCM16. The outgoing chunk limit
322
+ does not apply to received audio.
323
+
324
+ ## Handle tools
325
+
326
+ Declare tools with plain `ToolDefinition` objects. Names must be nonblank and
327
+ unique; schemas must contain finite JSON values. The SDK copies schemas before
328
+ connection setup, so later caller changes cannot alter initialization.
329
+
330
+ Set `prismFiller` to control what Prism says while the tool runs:
331
+
332
+ ```ts
333
+ import type { ToolDefinition } from "@rimelabs/sdk";
334
+
335
+ const pressDigits: ToolDefinition = {
336
+ name: "press_digits",
337
+ parameters: { type: "object", properties: { digits: { type: "string" } } },
338
+ prismFiller: { type: "none" },
339
+ };
340
+ ```
341
+
342
+ | Setting | Behavior |
343
+ | --- | --- |
344
+ | Omitted | Use the server default, currently `auto`. |
345
+ | `{ type: "none" }` | Suppress filler for this tool. |
346
+ | `{ type: "text", text: "One moment." }` | Speak the supplied text. |
347
+ | `{ type: "auto" }` | Let Prism write a line from the tool description. |
348
+ | `{ type: "auto", instructions: "Say you are checking the order." }` | Guide the generated line. |
349
+
350
+ Text and instructions must be valid UTF-8, contain at most 500 Unicode scalar
351
+ values, and contain a character outside the Unicode `White_Space` set. This
352
+ matches the engine's validation. The SDK preserves the supplied text and copies
353
+ filler settings before connection setup. Invalid settings raise `RimeInputError` before opening a
354
+ connection, including for JavaScript callers. The exported `PrismFiller` union
355
+ also checks TypeScript callers.
356
+
357
+ Filler applies when the reply has said nothing before calling a tool. If a reply
358
+ calls several tools, the first call's tool controls the filler. `none` suppresses
359
+ filler only; it does not silence other speech. Use `session.update({ tools })` to replace tools and their filler settings.
360
+
361
+ Execute a tool once for each `tool.call` event. Validate its parsed `arguments`,
362
+ then submit a string result with `submitToolResult(payload.call, output)`.
363
+ Use `JSON.stringify(...)` for a structured result. Call
364
+ `continueReply(payload.call)` when the assistant should answer; a tool call
365
+ reference also carries its parent response identity.
366
+
367
+ Keep the event consumer running while submitting tool results and requesting
368
+ continuation. Run slow tools asynchronously or in a worker so they do not block
369
+ the event loop.
370
+
371
+ A new user turn or proactive reply supersedes the old tool round. Late results
372
+ can still be recorded. Use `requestReply({ toolCall: call })` to ask the assistant
373
+ to report a recorded result. Repeating the same result is safe within the live
374
+ session; changing it is rejected. Do not rerun a tool because delivery failed.
375
+
376
+ ## Read events and references
377
+
378
+ Use `for await (const event of session.events)`. Each `SessionEvent` has
379
+ `sessionId`, `eventId`, nullable `requestId`, and a `payload`. Switch on
380
+ `payload.kind` to narrow the TypeScript union. All public types and error classes
381
+ are exported from `@rimelabs/sdk`.
382
+
383
+ | Kind | Payload fields |
384
+ | --- | --- |
385
+ | `input.ready` | No data fields. Turn methods handle the readiness wait. |
386
+ | `speech.hint` | `audioStartMs`; the user may have started speaking over a playing reply. It can be a false alarm: lower playback, do not stop it. |
387
+ | `speech.started` | `itemId`, `audioStartMs` |
388
+ | `speech.stopped` | `itemId`, `audioEndMs`, `endReason`: what ended the turn (`semantic`, `timeout`, `silence`, `max_duration` or `detector_changed`), `unknown` for a newer value, or null from a server that does not send it |
389
+ | `input.committed` | `itemId` |
390
+ | `transcript.delta`, `transcript.final` | `itemId` and `delta` or `text` |
391
+ | `transcript.failed` | `itemId`, `error` |
392
+ | `response.started` | `response`, `cause`, nullable `parent` and `inputItemId`, `inputItemIds` (the user items it answers), `metadata` (your own keys) |
393
+ | `message.started` | `output` |
394
+ | `text.delta`, `text.done` | `output` and `delta` or `text` |
395
+ | `audio.delta`, `audio.done` | `output`; a delta also has `audio.data` and a complete `audio.format` |
396
+ | `tool.call` | `call`, `itemId`, `name`, parsed `arguments` |
397
+ | `response.ended` | `response`, `status`, nullable `reason` |
398
+ | `response.abandoned` | `response`; the SDK requests cancellation after the requesting caller aborted. |
399
+ | `error` | `error`, a `RealtimeFault` |
400
+
401
+ References come from the SDK. Pass them back to identify the object you mean.
402
+
403
+ | Reference | Fields |
404
+ | --- | --- |
405
+ | `ResponseRef` | `sessionId`, `responseId` |
406
+ | `ItemRef` | `sessionId`, `itemId` |
407
+ | `ToolCallRef` | `sessionId`, `responseId`, `callId` |
408
+ | `OutputRef` | `response`, `itemId`, `outputIndex`, `contentIndex` |
409
+
410
+ Every reference belongs to one session. Only the last 128 responses are retained;
411
+ older references expire. Current output uses `contentIndex: 0`.
412
+
413
+ Unknown event kinds and extra fields are ignored. Unknown response causes,
414
+ terminal statuses, and error scopes become `"unknown"`. An absent response cause
415
+ means speech. The local `response.abandoned` event has an empty `eventId` and
416
+ `requestId: null`; it can follow `response.ended` when completion races cancellation.
417
+
418
+ ## Report playback
419
+
420
+ Generation completion and playback completion are separate. Report the player's
421
+ actual state, including output latency and queued audio.
422
+
423
+ | Report object | Required fields | Optional fields |
424
+ | --- | --- | --- |
425
+ | Interrupted | `kind: "interrupted"`, `output`, `audioEndMs` | None |
426
+ | Finished | `kind: "finished"`, `response` | `playedMs`, `audibleTailMs`; both default to `null` |
427
+
428
+ Pass the object to `await session.reportPlayback(report)`. Interruption records
429
+ the point where caller speech stopped an assistant message. `audioEndMs` must
430
+ be an integer from 0 through 4,294,967,295. Finished playback means all queued
431
+ audio has played or has permanently stopped. Optional durations must be finite,
432
+ nonnegative milliseconds. The SDK waits for generation completion and supplies
433
+ the server's drain token.
434
+
435
+ Saving audio does not establish playback. Reports are optional. If you cannot
436
+ measure playback, omit them and let the server use its estimate. The recorded and typed examples have no player and omit these reports.
437
+ The voice examples report actual playback.
438
+
439
+ ## Timeouts and failures
440
+
441
+ Configure positive, finite seconds through `timeouts`. `connectS` bounds the
442
+ WebSocket handshake and initial session event. `readyS` bounds turn admission,
443
+ tool readiness, and waiting for generation to end before a playback report.
444
+ Once a request is submitted, `requestS` bounds its write and acknowledgment.
445
+
446
+ These limits do not bound the entire conversation or generation after creation.
447
+ Set an application deadline and close the session when it expires. The recorded and typed examples use a 60-second deadline. Voice conversations
448
+ run until stopped or the connection fails.
449
+
450
+ `RealtimeAdmissionTimeout` means readiness expired with no request outstanding;
451
+ the session remains usable. An unknown request outcome closes the connection and
452
+ is never replayed. The SDK retries typed-turn admission only after named refusals
453
+ that confirm the request made no change.
454
+
455
+ Protocol faults appear as `error` events and can also reject an affected method
456
+ with `RimeRealtimeError`. Its `.fault` has `code`, `message`, `scope`, nullable
457
+ `requestId`, `responseId`, `itemId`, `correlationId`, `parameter`, and a `callIds`
458
+ array. A response fault does not itself end generation. Session faults and lost
459
+ connections end the session.
460
+
461
+ `NormalizedRealtimeFault.retryable` reports whether the same request, unchanged,
462
+ may succeed on another attempt. Read `event.payload.error.retryable` on an `error`
463
+ event or `error.fault.retryable` on `RimeRealtimeError`. The SDK derives this
464
+ value from the code; Prism does not send a retry flag.
465
+
466
+ `RealtimeFault` accepts the previous fault shape, with an optional `retryable`
467
+ field, for caller-created faults. `RimeRealtimeError` accepts this shape and
468
+ ignores any supplied retry flag. Its `.fault` and SDK-emitted event faults are
469
+ `NormalizedRealtimeFault` values with a required boolean `retryable` field.
470
+ The SDK freezes these faults and their `callIds` arrays. Later changes to a
471
+ constructor input do not change the error's fault.
472
+
473
+ `TranscriptFailed`, `FaultEvent`, `InputEvent`, and `SessionEvent` default to
474
+ `RealtimeFault`, so existing event literals can omit `retryable`. These types
475
+ also accept a fault type parameter. `session.events` and the `runConversation`
476
+ event callback use `SessionEvent<NormalizedRealtimeFault>`, which gives their
477
+ faults a required boolean `retryable` field. An explicit `SessionEvent`
478
+ annotation uses the default type and makes `retryable` optional.
479
+
480
+ | Error code | `retryable` |
481
+ | --- | --- |
482
+ | `capacity_exceeded` | `true` |
483
+ | `unavailable` | `true` |
484
+ | `response_timeout` | `true` |
485
+ | `context_length_exceeded` | `false` |
486
+ | All other codes, including unknown codes | `false` |
487
+
488
+ The application chooses the delay, attempt limit, and recovery operation. This
489
+ value does not start a retry. It does not describe a local `RimeTimeoutError` or
490
+ prove that no output or side effect occurred. The SDK's existing readiness
491
+ retries have separate rules: an explicit refusal such as `typed_turn_busy`
492
+ permits another attempt after a new readiness event, although its `retryable`
493
+ value is `false` under this contract.
494
+
495
+ For example, Prism can accept a user's question and then report
496
+ `capacity_exceeded`. `sendText()` has already returned a `ResponseRef`; the
497
+ failure arrives through an `error` event. Wait for `response.ended`, then apply
498
+ the application's retry policy. If the question is already in history, use
499
+ `requestReply()` to ask for another reply when appropriate. Repeating
500
+ `sendText()` can add the question twice. A `response_timeout` can follow partial
501
+ text or audio, which the application must handle before another attempt.
502
+
503
+ For `context_length_exceeded`, the server message contains token counts.
504
+ Unchanged attempts will fail until the conversation is shorter. Start a new
505
+ session with shorter context. The protocol also supports history deletion, but
506
+ this SDK does not yet expose `conversation.item.delete`.
507
+
508
+ Malformed fields in known events and mismatched request acknowledgments close
509
+ the session with `RimeStreamError`. Unknown event kinds and extra fields remain
510
+ accepted and ignored. The SDK does not replay a request after a protocol failure.
511
+
512
+ Aborting an operation rejects it with `RimeCancelledError`. For reply requests,
513
+ the SDK prevents an unsent request or cancels a late accepted response. Control
514
+ operations can wait for acknowledgment before rejecting, to keep acknowledgment
515
+ matching correct. A submitted tool result keeps matching its acknowledgment even
516
+ if its caller aborts. Aborting audio after submission starts closes the session;
517
+ before submission, it restores conversion state. Do not resend a chunk with an
518
+ unknown outcome.
519
+
520
+ Keep up with the event stream. The queue holds 256 events; incoming messages
521
+ are limited to 1 MiB. A slow consumer ends the session. There is no automatic
522
+ reconnect or history replay.
523
+
524
+ Local peer tests cover the SDK protocol handling. Validate the gateway handshake
525
+ and endpoint behavior for the deployment you use.
526
+
527
+ The SDK retains the latest 128 interruption policy changes. Instructions or tools
528
+ updates that keep the same interruption policy do not consume this history.
529
+ If an unannounced reply refers to an older evicted policy, the session fails
530
+ with a resource limit error instead of guessing its policy. A reply already
531
+ received keeps its captured policy even after later settings updates.
532
+
533
+ If an interruption setting changes without a published revision, later reply
534
+ revisions can be ambiguous. The SDK preserves earlier known settings and raises
535
+ a resource limit error for an ambiguous reply instead of guessing. A later
536
+ acknowledgment with a revision establishes a new known setting. Replies without
537
+ a revision use the current acknowledged setting.