@laintern/chat-sdk 0.1.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.
@@ -0,0 +1,284 @@
1
+ type ContentType = "forum_topic" | "article";
2
+ interface SearchResult {
3
+ content_id: string;
4
+ content_type: ChatContentType;
5
+ title: string;
6
+ url: string;
7
+ imageUrl?: string | null;
8
+ similarity: number;
9
+ snippet?: string;
10
+ reply_count?: number;
11
+ author_first_name?: string;
12
+ /** ISO 8601 — forum: topic created_at; article: updated_at_source or created_at (for display/sort) */
13
+ posted_at?: string;
14
+ /** Kennisbank articles only */
15
+ read_time_minutes?: number;
16
+ /**
17
+ * Where inside the source this result sits — e.g. a `kb_chunk`'s heading
18
+ * path ("Traject › Aanmelden"). Lets a citation name the passage, not just
19
+ * the document. Absent for whole-document sources.
20
+ */
21
+ section?: string;
22
+ }
23
+ /**
24
+ * What a chat citation can point at. `kb_chunk` is a passage from the chunked
25
+ * knowledge base: it has no public URL, so consumers must render it as a
26
+ * non-navigable source (document title + `section`) rather than a link.
27
+ */
28
+ type ChatContentType = ContentType | "medical_web" | "kb_chunk";
29
+ interface CitationSource {
30
+ source_id: string;
31
+ title: string;
32
+ url: string;
33
+ content_type: ChatContentType;
34
+ /** Passage location within the source (see {@link SearchResult.section}). */
35
+ section?: string;
36
+ }
37
+ type StepEvent = {
38
+ status: "started";
39
+ tool: string;
40
+ } | {
41
+ status: "completed";
42
+ tool: string;
43
+ count?: number;
44
+ };
45
+ type ExternalSourceType = ContentType;
46
+ interface ExternalSourcesEvent {
47
+ type: "external_sources";
48
+ source_type: ExternalSourceType;
49
+ items: SearchResult[];
50
+ }
51
+ /**
52
+ * Where the medical input guardrail wants to point the user.
53
+ * - `112` / `113` are HARD blocks: the agent does not respond.
54
+ * - `huisarts` / `huisartsenpost` / `null` are SOFT advisories: the agent
55
+ * responds normally and the widget shows a less-invasive notice.
56
+ */
57
+ type GuardrailRedirect = "112" | "113" | "huisarts" | "huisartsenpost" | null;
58
+ type ChatStreamEvent = {
59
+ type: "session";
60
+ session_id: string;
61
+ } | {
62
+ type: "mode";
63
+ mode: "search" | "medical";
64
+ } | {
65
+ type: "step";
66
+ step: StepEvent;
67
+ } | {
68
+ type: "delta";
69
+ content: string;
70
+ } | {
71
+ type: "citation";
72
+ index: number;
73
+ source: CitationSource;
74
+ } | {
75
+ type: "sources";
76
+ items: SearchResult[];
77
+ } | ExternalSourcesEvent
78
+ /**
79
+ * Soft guardrail notice emitted alongside a normal agent response. The
80
+ * widget renders this as an unobtrusive info bar; the assistant message
81
+ * itself still streams as usual. Hard blocks stay on the `error` event
82
+ * with code `GUARDRAIL_TRIGGERED`.
83
+ */
84
+ | {
85
+ type: "advisory";
86
+ redirect: Exclude<GuardrailRedirect, "112" | "113">;
87
+ message: string;
88
+ } | {
89
+ type: "done";
90
+ usage?: {
91
+ prompt_tokens: number;
92
+ completion_tokens: number;
93
+ };
94
+ message_id?: string;
95
+ } | {
96
+ type: "error";
97
+ code: string;
98
+ message: string;
99
+ guardrail_slug?: string;
100
+ };
101
+
102
+ /**
103
+ * Refresh this long before a token expires. It mirrors the margin the API
104
+ * applies when it clamps a chat token to a credential inside it: the server
105
+ * guarantees the token dies *after* the credential's own refresh point, and
106
+ * this buffer is what makes the client come back in time to use that.
107
+ */
108
+ declare const DEFAULT_REFRESH_BUFFER_MS = 60000;
109
+ interface TokenResponse {
110
+ token: string;
111
+ /** ISO-8601 or epoch ms, if the endpoint reports one. */
112
+ expiresAt?: string | number | null;
113
+ }
114
+ /** Minimal storage surface — `sessionStorage`, `localStorage` or your own. */
115
+ interface TokenStorage {
116
+ getItem(key: string): string | null;
117
+ setItem(key: string, value: string): void;
118
+ removeItem(key: string): void;
119
+ }
120
+ interface TokenManagerOptions {
121
+ /**
122
+ * Fetches a fresh token from YOUR backend. This is the one integration
123
+ * point that cannot be generic: only your server may hold the API key that
124
+ * mints Laintern tokens, and only it knows who the current user is.
125
+ */
126
+ fetchToken: () => Promise<TokenResponse | string>;
127
+ storage?: TokenStorage | null;
128
+ storageKey?: string;
129
+ refreshBufferMs?: number;
130
+ onWarning?: (message: string, detail?: Record<string, unknown>) => void;
131
+ }
132
+ interface TokenManager {
133
+ /** Cached token when still valid, otherwise a freshly fetched one. */
134
+ get(): Promise<string>;
135
+ /** Drop the cached token — the next `get()` fetches. */
136
+ clear(): void;
137
+ /** Expiry of the cached token, in epoch ms. Null when nothing is cached. */
138
+ expiresAt(): number | null;
139
+ }
140
+ /** Read a JWT's `exp` claim (epoch ms) without verifying the signature. */
141
+ declare function decodeJwtExpiryMs(token: string): number | null;
142
+ /**
143
+ * Caches the chat token and refreshes it before it dies.
144
+ *
145
+ * Concurrent callers share one in-flight fetch: a page that opens a stream
146
+ * while another request is already refreshing must not mint two sessions.
147
+ */
148
+ declare function createTokenManager(options: TokenManagerOptions): TokenManager;
149
+
150
+ interface LainternClientOptions {
151
+ /** Base URL of the Laintern API, e.g. "https://api.laintern.com". */
152
+ apiBase: string;
153
+ /**
154
+ * How to get a chat token. Either your own function, or the URL of an
155
+ * endpoint on your backend that returns `{ token, expires_at }`.
156
+ *
157
+ * Never put a Laintern API key in the browser: the key mints tokens for any
158
+ * user, so it belongs on your server, behind your own session check.
159
+ */
160
+ token: TokenManagerOptions["fetchToken"] | {
161
+ endpoint: string;
162
+ init?: RequestInit;
163
+ };
164
+ storage?: TokenManagerOptions["storage"];
165
+ refreshBufferMs?: number;
166
+ onWarning?: TokenManagerOptions["onWarning"];
167
+ /** Injectable for tests and non-browser runtimes. */
168
+ fetch?: typeof globalThis.fetch;
169
+ }
170
+ interface SendMessageOptions {
171
+ /** Continues an existing conversation. Omit to start a new one. */
172
+ sessionId?: string | null;
173
+ /** Base64 data URLs, for agents with image input enabled. */
174
+ images?: string[];
175
+ /** Force a specific tool on this turn, when the agent allows it. */
176
+ forceTool?: string;
177
+ signal?: AbortSignal;
178
+ }
179
+ interface AgentConfig {
180
+ /** Transparency notice to show before the conversation starts (EU AI Act). */
181
+ disclaimer: {
182
+ enabled: boolean;
183
+ text: string | null;
184
+ };
185
+ }
186
+ interface LainternClient {
187
+ /**
188
+ * Send one message and iterate the response as it streams.
189
+ *
190
+ * Yields typed events (`delta`, `citation`, `sources`, `step`, `done`, …).
191
+ * Prefer {@link createConversation} unless you want to assemble the turn
192
+ * yourself.
193
+ */
194
+ sendMessage(message: string, options?: SendMessageOptions): AsyncGenerator<ChatStreamEvent>;
195
+ /** Per-agent presentation config. Best-effort: throws on transport errors. */
196
+ getConfig(): Promise<AgentConfig>;
197
+ submitFeedback(messageId: string, feedback: "positive" | "negative"): Promise<void>;
198
+ /** The token cache, exposed so a host can clear it on logout. */
199
+ tokens: TokenManager;
200
+ }
201
+ declare function createLainternClient(options: LainternClientOptions): LainternClient;
202
+
203
+ /** Soft advisory alongside an answer — the agent flagged the topic, but answered. */
204
+ interface TurnAdvisory {
205
+ redirect: string | null;
206
+ message: string;
207
+ }
208
+ /** A guardrail refused the turn. The agent produced no answer. */
209
+ interface TurnBlock {
210
+ /** Which guardrail fired, when the server names one. */
211
+ slug: string | null;
212
+ /** Copy meant for the end user, written by the guardrail's configuration. */
213
+ message: string;
214
+ }
215
+ interface TurnError {
216
+ code: string;
217
+ message: string;
218
+ rateLimitScope?: "user" | "agent";
219
+ rateLimitWindow?: "minute" | "hour" | "day";
220
+ }
221
+ interface Turn {
222
+ id: string;
223
+ userMessage: string;
224
+ userImages?: string[];
225
+ assistantContent: string;
226
+ /** Citation number (the `[1]` in the text) → the source it points at. */
227
+ citations: Map<number, CitationSource>;
228
+ /** Everything the agent cited or consulted, in the order the server ranked it. */
229
+ sources: SearchResult[];
230
+ /** Sidebar suggestions per content type — not necessarily cited. */
231
+ externalSources: Partial<Record<ExternalSourceType, SearchResult[]>>;
232
+ mode: string | null;
233
+ steps: StepEvent[];
234
+ isStreaming: boolean;
235
+ error?: TurnError;
236
+ blocked?: TurnBlock;
237
+ advisory?: TurnAdvisory;
238
+ messageId?: string;
239
+ feedback?: "positive" | "negative" | null;
240
+ }
241
+ interface ConversationState {
242
+ turns: Turn[];
243
+ sessionId: string | null;
244
+ isStreaming: boolean;
245
+ }
246
+ interface SendOptions {
247
+ images?: string[];
248
+ forceTool?: string;
249
+ }
250
+ interface Conversation {
251
+ getState(): ConversationState;
252
+ /** Subscribe to state changes. Returns an unsubscribe function. */
253
+ subscribe(listener: (state: ConversationState) => void): () => void;
254
+ /** Send a message and stream the answer into state. Never throws. */
255
+ send(message: string, options?: SendOptions): Promise<void>;
256
+ /** Stop the current stream. The partial answer stays in state. */
257
+ abort(): void;
258
+ /** Drop all turns and start a new server-side session on the next send. */
259
+ reset(): void;
260
+ setFeedback(turnId: string, feedback: "positive" | "negative"): Promise<void>;
261
+ }
262
+ interface ConversationOptions {
263
+ /** Resume an existing server-side session. */
264
+ sessionId?: string | null;
265
+ /** Restore turns from your own storage. */
266
+ initialTurns?: Turn[];
267
+ /** Called whenever the session id changes, so you can persist it. */
268
+ onSessionId?: (sessionId: string) => void;
269
+ }
270
+ /**
271
+ * Stateful conversation on top of {@link LainternClient}.
272
+ *
273
+ * Holds the turn list, applies each stream event to the turn in flight, and
274
+ * notifies subscribers. Framework-agnostic on purpose: the React binding in
275
+ * `@laintern/chat-sdk/react` is a thin `useSyncExternalStore` wrapper, and any
276
+ * other UI layer can subscribe the same way.
277
+ *
278
+ * `send()` never throws. A failed turn is part of the conversation — it lands
279
+ * on `turn.error` (or `turn.blocked` for a guardrail) so the UI can render it
280
+ * in place, which is what you want in a chat transcript.
281
+ */
282
+ declare function createConversation(client: LainternClient, options?: ConversationOptions): Conversation;
283
+
284
+ export { type AgentConfig as A, type ChatStreamEvent as C, DEFAULT_REFRESH_BUFFER_MS as D, type ExternalSourceType as E, type LainternClient as L, type SearchResult as S, type TokenManager as T, type ChatContentType as a, type CitationSource as b, type ContentType as c, type Conversation as d, type ConversationOptions as e, type ConversationState as f, type LainternClientOptions as g, type SendMessageOptions as h, type SendOptions as i, type StepEvent as j, type TokenManagerOptions as k, type TokenResponse as l, type TokenStorage as m, type Turn as n, type TurnAdvisory as o, type TurnBlock as p, type TurnError as q, createConversation as r, createLainternClient as s, createTokenManager as t, decodeJwtExpiryMs as u };
@@ -0,0 +1,284 @@
1
+ type ContentType = "forum_topic" | "article";
2
+ interface SearchResult {
3
+ content_id: string;
4
+ content_type: ChatContentType;
5
+ title: string;
6
+ url: string;
7
+ imageUrl?: string | null;
8
+ similarity: number;
9
+ snippet?: string;
10
+ reply_count?: number;
11
+ author_first_name?: string;
12
+ /** ISO 8601 — forum: topic created_at; article: updated_at_source or created_at (for display/sort) */
13
+ posted_at?: string;
14
+ /** Kennisbank articles only */
15
+ read_time_minutes?: number;
16
+ /**
17
+ * Where inside the source this result sits — e.g. a `kb_chunk`'s heading
18
+ * path ("Traject › Aanmelden"). Lets a citation name the passage, not just
19
+ * the document. Absent for whole-document sources.
20
+ */
21
+ section?: string;
22
+ }
23
+ /**
24
+ * What a chat citation can point at. `kb_chunk` is a passage from the chunked
25
+ * knowledge base: it has no public URL, so consumers must render it as a
26
+ * non-navigable source (document title + `section`) rather than a link.
27
+ */
28
+ type ChatContentType = ContentType | "medical_web" | "kb_chunk";
29
+ interface CitationSource {
30
+ source_id: string;
31
+ title: string;
32
+ url: string;
33
+ content_type: ChatContentType;
34
+ /** Passage location within the source (see {@link SearchResult.section}). */
35
+ section?: string;
36
+ }
37
+ type StepEvent = {
38
+ status: "started";
39
+ tool: string;
40
+ } | {
41
+ status: "completed";
42
+ tool: string;
43
+ count?: number;
44
+ };
45
+ type ExternalSourceType = ContentType;
46
+ interface ExternalSourcesEvent {
47
+ type: "external_sources";
48
+ source_type: ExternalSourceType;
49
+ items: SearchResult[];
50
+ }
51
+ /**
52
+ * Where the medical input guardrail wants to point the user.
53
+ * - `112` / `113` are HARD blocks: the agent does not respond.
54
+ * - `huisarts` / `huisartsenpost` / `null` are SOFT advisories: the agent
55
+ * responds normally and the widget shows a less-invasive notice.
56
+ */
57
+ type GuardrailRedirect = "112" | "113" | "huisarts" | "huisartsenpost" | null;
58
+ type ChatStreamEvent = {
59
+ type: "session";
60
+ session_id: string;
61
+ } | {
62
+ type: "mode";
63
+ mode: "search" | "medical";
64
+ } | {
65
+ type: "step";
66
+ step: StepEvent;
67
+ } | {
68
+ type: "delta";
69
+ content: string;
70
+ } | {
71
+ type: "citation";
72
+ index: number;
73
+ source: CitationSource;
74
+ } | {
75
+ type: "sources";
76
+ items: SearchResult[];
77
+ } | ExternalSourcesEvent
78
+ /**
79
+ * Soft guardrail notice emitted alongside a normal agent response. The
80
+ * widget renders this as an unobtrusive info bar; the assistant message
81
+ * itself still streams as usual. Hard blocks stay on the `error` event
82
+ * with code `GUARDRAIL_TRIGGERED`.
83
+ */
84
+ | {
85
+ type: "advisory";
86
+ redirect: Exclude<GuardrailRedirect, "112" | "113">;
87
+ message: string;
88
+ } | {
89
+ type: "done";
90
+ usage?: {
91
+ prompt_tokens: number;
92
+ completion_tokens: number;
93
+ };
94
+ message_id?: string;
95
+ } | {
96
+ type: "error";
97
+ code: string;
98
+ message: string;
99
+ guardrail_slug?: string;
100
+ };
101
+
102
+ /**
103
+ * Refresh this long before a token expires. It mirrors the margin the API
104
+ * applies when it clamps a chat token to a credential inside it: the server
105
+ * guarantees the token dies *after* the credential's own refresh point, and
106
+ * this buffer is what makes the client come back in time to use that.
107
+ */
108
+ declare const DEFAULT_REFRESH_BUFFER_MS = 60000;
109
+ interface TokenResponse {
110
+ token: string;
111
+ /** ISO-8601 or epoch ms, if the endpoint reports one. */
112
+ expiresAt?: string | number | null;
113
+ }
114
+ /** Minimal storage surface — `sessionStorage`, `localStorage` or your own. */
115
+ interface TokenStorage {
116
+ getItem(key: string): string | null;
117
+ setItem(key: string, value: string): void;
118
+ removeItem(key: string): void;
119
+ }
120
+ interface TokenManagerOptions {
121
+ /**
122
+ * Fetches a fresh token from YOUR backend. This is the one integration
123
+ * point that cannot be generic: only your server may hold the API key that
124
+ * mints Laintern tokens, and only it knows who the current user is.
125
+ */
126
+ fetchToken: () => Promise<TokenResponse | string>;
127
+ storage?: TokenStorage | null;
128
+ storageKey?: string;
129
+ refreshBufferMs?: number;
130
+ onWarning?: (message: string, detail?: Record<string, unknown>) => void;
131
+ }
132
+ interface TokenManager {
133
+ /** Cached token when still valid, otherwise a freshly fetched one. */
134
+ get(): Promise<string>;
135
+ /** Drop the cached token — the next `get()` fetches. */
136
+ clear(): void;
137
+ /** Expiry of the cached token, in epoch ms. Null when nothing is cached. */
138
+ expiresAt(): number | null;
139
+ }
140
+ /** Read a JWT's `exp` claim (epoch ms) without verifying the signature. */
141
+ declare function decodeJwtExpiryMs(token: string): number | null;
142
+ /**
143
+ * Caches the chat token and refreshes it before it dies.
144
+ *
145
+ * Concurrent callers share one in-flight fetch: a page that opens a stream
146
+ * while another request is already refreshing must not mint two sessions.
147
+ */
148
+ declare function createTokenManager(options: TokenManagerOptions): TokenManager;
149
+
150
+ interface LainternClientOptions {
151
+ /** Base URL of the Laintern API, e.g. "https://api.laintern.com". */
152
+ apiBase: string;
153
+ /**
154
+ * How to get a chat token. Either your own function, or the URL of an
155
+ * endpoint on your backend that returns `{ token, expires_at }`.
156
+ *
157
+ * Never put a Laintern API key in the browser: the key mints tokens for any
158
+ * user, so it belongs on your server, behind your own session check.
159
+ */
160
+ token: TokenManagerOptions["fetchToken"] | {
161
+ endpoint: string;
162
+ init?: RequestInit;
163
+ };
164
+ storage?: TokenManagerOptions["storage"];
165
+ refreshBufferMs?: number;
166
+ onWarning?: TokenManagerOptions["onWarning"];
167
+ /** Injectable for tests and non-browser runtimes. */
168
+ fetch?: typeof globalThis.fetch;
169
+ }
170
+ interface SendMessageOptions {
171
+ /** Continues an existing conversation. Omit to start a new one. */
172
+ sessionId?: string | null;
173
+ /** Base64 data URLs, for agents with image input enabled. */
174
+ images?: string[];
175
+ /** Force a specific tool on this turn, when the agent allows it. */
176
+ forceTool?: string;
177
+ signal?: AbortSignal;
178
+ }
179
+ interface AgentConfig {
180
+ /** Transparency notice to show before the conversation starts (EU AI Act). */
181
+ disclaimer: {
182
+ enabled: boolean;
183
+ text: string | null;
184
+ };
185
+ }
186
+ interface LainternClient {
187
+ /**
188
+ * Send one message and iterate the response as it streams.
189
+ *
190
+ * Yields typed events (`delta`, `citation`, `sources`, `step`, `done`, …).
191
+ * Prefer {@link createConversation} unless you want to assemble the turn
192
+ * yourself.
193
+ */
194
+ sendMessage(message: string, options?: SendMessageOptions): AsyncGenerator<ChatStreamEvent>;
195
+ /** Per-agent presentation config. Best-effort: throws on transport errors. */
196
+ getConfig(): Promise<AgentConfig>;
197
+ submitFeedback(messageId: string, feedback: "positive" | "negative"): Promise<void>;
198
+ /** The token cache, exposed so a host can clear it on logout. */
199
+ tokens: TokenManager;
200
+ }
201
+ declare function createLainternClient(options: LainternClientOptions): LainternClient;
202
+
203
+ /** Soft advisory alongside an answer — the agent flagged the topic, but answered. */
204
+ interface TurnAdvisory {
205
+ redirect: string | null;
206
+ message: string;
207
+ }
208
+ /** A guardrail refused the turn. The agent produced no answer. */
209
+ interface TurnBlock {
210
+ /** Which guardrail fired, when the server names one. */
211
+ slug: string | null;
212
+ /** Copy meant for the end user, written by the guardrail's configuration. */
213
+ message: string;
214
+ }
215
+ interface TurnError {
216
+ code: string;
217
+ message: string;
218
+ rateLimitScope?: "user" | "agent";
219
+ rateLimitWindow?: "minute" | "hour" | "day";
220
+ }
221
+ interface Turn {
222
+ id: string;
223
+ userMessage: string;
224
+ userImages?: string[];
225
+ assistantContent: string;
226
+ /** Citation number (the `[1]` in the text) → the source it points at. */
227
+ citations: Map<number, CitationSource>;
228
+ /** Everything the agent cited or consulted, in the order the server ranked it. */
229
+ sources: SearchResult[];
230
+ /** Sidebar suggestions per content type — not necessarily cited. */
231
+ externalSources: Partial<Record<ExternalSourceType, SearchResult[]>>;
232
+ mode: string | null;
233
+ steps: StepEvent[];
234
+ isStreaming: boolean;
235
+ error?: TurnError;
236
+ blocked?: TurnBlock;
237
+ advisory?: TurnAdvisory;
238
+ messageId?: string;
239
+ feedback?: "positive" | "negative" | null;
240
+ }
241
+ interface ConversationState {
242
+ turns: Turn[];
243
+ sessionId: string | null;
244
+ isStreaming: boolean;
245
+ }
246
+ interface SendOptions {
247
+ images?: string[];
248
+ forceTool?: string;
249
+ }
250
+ interface Conversation {
251
+ getState(): ConversationState;
252
+ /** Subscribe to state changes. Returns an unsubscribe function. */
253
+ subscribe(listener: (state: ConversationState) => void): () => void;
254
+ /** Send a message and stream the answer into state. Never throws. */
255
+ send(message: string, options?: SendOptions): Promise<void>;
256
+ /** Stop the current stream. The partial answer stays in state. */
257
+ abort(): void;
258
+ /** Drop all turns and start a new server-side session on the next send. */
259
+ reset(): void;
260
+ setFeedback(turnId: string, feedback: "positive" | "negative"): Promise<void>;
261
+ }
262
+ interface ConversationOptions {
263
+ /** Resume an existing server-side session. */
264
+ sessionId?: string | null;
265
+ /** Restore turns from your own storage. */
266
+ initialTurns?: Turn[];
267
+ /** Called whenever the session id changes, so you can persist it. */
268
+ onSessionId?: (sessionId: string) => void;
269
+ }
270
+ /**
271
+ * Stateful conversation on top of {@link LainternClient}.
272
+ *
273
+ * Holds the turn list, applies each stream event to the turn in flight, and
274
+ * notifies subscribers. Framework-agnostic on purpose: the React binding in
275
+ * `@laintern/chat-sdk/react` is a thin `useSyncExternalStore` wrapper, and any
276
+ * other UI layer can subscribe the same way.
277
+ *
278
+ * `send()` never throws. A failed turn is part of the conversation — it lands
279
+ * on `turn.error` (or `turn.blocked` for a guardrail) so the UI can render it
280
+ * in place, which is what you want in a chat transcript.
281
+ */
282
+ declare function createConversation(client: LainternClient, options?: ConversationOptions): Conversation;
283
+
284
+ export { type AgentConfig as A, type ChatStreamEvent as C, DEFAULT_REFRESH_BUFFER_MS as D, type ExternalSourceType as E, type LainternClient as L, type SearchResult as S, type TokenManager as T, type ChatContentType as a, type CitationSource as b, type ContentType as c, type Conversation as d, type ConversationOptions as e, type ConversationState as f, type LainternClientOptions as g, type SendMessageOptions as h, type SendOptions as i, type StepEvent as j, type TokenManagerOptions as k, type TokenResponse as l, type TokenStorage as m, type Turn as n, type TurnAdvisory as o, type TurnBlock as p, type TurnError as q, createConversation as r, createLainternClient as s, createTokenManager as t, decodeJwtExpiryMs as u };