@anuma/sdk 6.33.1 → 6.34.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 (45) hide show
  1. package/dist/design/index.cjs +0 -2
  2. package/dist/design/index.d.mts +0 -7
  3. package/dist/design/index.d.ts +0 -7
  4. package/dist/design/index.mjs +0 -2
  5. package/dist/expo/{chunk-XD6WOQ5I.mjs → chunk-GQBTAM6Y.mjs} +0 -70
  6. package/dist/expo/{consolidate-7DBUHQRK.mjs → consolidate-3H2WY3FU.mjs} +1 -8
  7. package/dist/expo/index.cjs +15947 -16025
  8. package/dist/expo/index.d.mts +5934 -6646
  9. package/dist/expo/index.d.ts +5934 -6646
  10. package/dist/expo/index.mjs +15996 -15997
  11. package/dist/index.cjs +2289 -2430
  12. package/dist/index.d.mts +914 -1054
  13. package/dist/index.d.ts +914 -1054
  14. package/dist/index.mjs +2290 -2429
  15. package/dist/memory/context.d.mts +0 -73
  16. package/dist/memory/context.d.ts +0 -73
  17. package/dist/next/index.cjs +1 -8
  18. package/dist/next/index.mjs +1 -8
  19. package/dist/pii/transformers.d.mts +0 -32
  20. package/dist/pii/transformers.d.ts +0 -32
  21. package/dist/react/{chunk-XNQRJBIH.mjs → chunk-UFO4W3JX.mjs} +0 -70
  22. package/dist/react/{consolidate-5DVINCUE.mjs → consolidate-RHIAUBT4.mjs} +1 -8
  23. package/dist/react/index.cjs +33267 -33441
  24. package/dist/react/index.d.mts +13598 -14550
  25. package/dist/react/index.d.ts +13598 -14550
  26. package/dist/react/index.mjs +32920 -33019
  27. package/dist/server/{chunk-KSYV7UO2.mjs → chunk-4RGGAOAJ.mjs} +835 -905
  28. package/dist/server/{consolidate-MCNBSUXD.mjs → consolidate-YEGACWLR.mjs} +1 -8
  29. package/dist/server/index.cjs +22357 -23132
  30. package/dist/server/index.d.mts +7895 -8768
  31. package/dist/server/index.d.ts +7895 -8768
  32. package/dist/server/index.mjs +20548 -21258
  33. package/dist/telemetry/index.cjs +0 -9
  34. package/dist/telemetry/index.d.mts +86 -197
  35. package/dist/telemetry/index.d.ts +86 -197
  36. package/dist/telemetry/index.mjs +0 -9
  37. package/dist/tools/index.cjs +13036 -13347
  38. package/dist/tools/index.d.mts +2434 -2729
  39. package/dist/tools/index.d.ts +2434 -2729
  40. package/dist/tools/index.mjs +13036 -13347
  41. package/dist/tools/selection/index.cjs +18 -122
  42. package/dist/tools/selection/index.d.mts +0 -159
  43. package/dist/tools/selection/index.d.ts +0 -159
  44. package/dist/tools/selection/index.mjs +18 -122
  45. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -1,126 +1,396 @@
1
- type AuthToken = string | undefined;
2
- interface Auth {
1
+ /**
2
+ * Cross-platform primitive constants.
3
+ *
4
+ * This module is intentionally dependency-free and side-effect-free so it can
5
+ * be loaded by *any* consumer — including React Native / Hermes — without
6
+ * pulling in the rest of the SDK. It is published as the `@anuma/sdk/constants`
7
+ * subpath (which carries a `react-native` export condition); slim consumers
8
+ * such as the agent packages import values from here instead of the bare
9
+ * `@anuma/sdk` entry, whose Node/web bundle is not RN-safe.
10
+ *
11
+ * Only add genuinely pure, cross-platform constants here (numeric/string
12
+ * literals, default IDs, size caps). No classes, no runtime APIs, no imports.
13
+ */
14
+ /** Character cap for multiline / textarea fields. Agent gateways (e.g. cf-tasks)
15
+ * truncate values to this length before interpolating them into prompt templates.
16
+ * Shared across all agent packages so a single change updates every journey
17
+ * declaration simultaneously. */
18
+ declare const MULTILINE_FIELD_MAX = 50000;
19
+
20
+ /** Runtime environment where the agent can execute. */
21
+ type AgentRuntime = "client" | "server";
22
+ /** Strategy a consumer should use to extract text from an uploaded file before
23
+ * feeding it into a skill journey field. The SDK only labels the strategy —
24
+ * the actual extraction (pdf.js, OCR, etc.) is the consumer's responsibility. */
25
+ type FileExtractionStrategy = "pdf-text" | "csv-text" | "image-ocr";
26
+
27
+ /** Configuration for a single agent skill (task template). */
28
+ interface SkillConfig {
29
+ /** Unique identifier. Convention: "domain.action" e.g. "housing.lease-review" */
30
+ id: string;
31
+ /** Human-readable name. */
32
+ name: string;
33
+ /** System prompt template with {{variable}} placeholders. */
34
+ promptTemplate: string;
3
35
  /**
4
- * Which part of the request do we use to send the auth?
5
- *
6
- * @default 'header'
36
+ * User-turn template, interpolated with the same {{variable}} placeholders
37
+ * as `promptTemplate`. Optional, but strongly recommended for skill-driven
38
+ * (non-freeform) flows so the model receives a proper user turn — most
39
+ * Anthropic models reject requests that contain only a system message.
7
40
  */
8
- in?: 'header' | 'query' | 'cookie';
41
+ userTemplate?: string;
42
+ /** MCP tool names this skill requires. */
43
+ requiredTools?: string[];
44
+ /** Preferred model when the caller doesn't specify one. */
45
+ preferredModel?: string;
46
+ /** Max agentic loop steps for this skill. */
47
+ maxSteps?: number;
48
+ /** Variables the promptTemplate expects at runtime. */
49
+ requiredVariables?: string[];
9
50
  /**
10
- * Header or query parameter name.
51
+ * SMS-friendly question prompts keyed by variable name. The Anuma text
52
+ * gateway sends each prompt to the user one at a time and treats the
53
+ * user's reply as the slot value. Missing entries fall back to a
54
+ * generic "Please provide {name}." question in the gateway.
11
55
  *
12
- * @default 'Authorization'
56
+ * Order of questions follows requiredVariables[] order.
13
57
  */
14
- name?: string;
15
- scheme?: 'basic' | 'bearer';
16
- type: 'apiKey' | 'http';
58
+ smsPrompts?: Record<string, string>;
59
+ /** Additional context appended after the prompt template. */
60
+ contextSuffix?: string;
61
+ /** Lucide icon identifier (e.g. "FileSearch") for renderers that draw a skill icon. */
62
+ iconName?: string;
63
+ /** One-line announcement string shown when the skill is launched (channel-neutral). */
64
+ launchMessage?: string;
65
+ /** Conversational preamble shown after the user picks this skill, before the field
66
+ * questions begin. Used by chat-native guided flows (web, mobile, SMS, voice). */
67
+ preamble?: string;
68
+ /** Closing line shown after the user finishes the last field and before the agent
69
+ * starts the tool loop. Used by chat-native guided flows. */
70
+ closing?: string;
71
+ /** Fallback nudge for any required field on this skill that doesn't define its own
72
+ * `requiredNudge` string. */
73
+ requiredNudgeDefault?: string;
17
74
  }
18
-
19
- interface SerializerOptions<T> {
20
- /**
21
- * @default true
22
- */
23
- explode: boolean;
24
- style: T;
75
+ /** Public-safe manifest describing an agent (no prompt or tool details). */
76
+ interface AgentManifest {
77
+ id: string;
78
+ name: string;
79
+ description: string;
80
+ runtimes: AgentRuntime[];
81
+ skills: Array<{
82
+ id: string;
83
+ name: string;
84
+ requiredVariables?: string[];
85
+ smsPrompts?: Record<string, string>;
86
+ }>;
25
87
  }
26
- type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited';
27
- type ObjectStyle = 'form' | 'deepObject';
28
-
29
- type QuerySerializer = (query: Record<string, unknown>) => string;
30
- type BodySerializer = (body: unknown) => unknown;
31
- type QuerySerializerOptionsObject = {
32
- allowReserved?: boolean;
33
- array?: Partial<SerializerOptions<ArrayStyle>>;
34
- object?: Partial<SerializerOptions<ObjectStyle>>;
35
- };
36
- type QuerySerializerOptions = QuerySerializerOptionsObject & {
37
- /**
38
- * Per-parameter serialization overrides. When provided, these settings
39
- * override the global array/object settings for specific parameter names.
40
- */
41
- parameters?: Record<string, QuerySerializerOptionsObject>;
42
- };
43
-
44
- type HttpMethod = 'connect' | 'delete' | 'get' | 'head' | 'options' | 'patch' | 'post' | 'put' | 'trace';
45
- type Client$1<RequestFn = never, Config = unknown, MethodFn = never, BuildUrlFn = never, SseFn = never> = {
46
- /**
47
- * Returns the final request URL.
48
- */
49
- buildUrl: BuildUrlFn;
50
- getConfig: () => Config;
51
- request: RequestFn;
52
- setConfig: (config: Config) => Config;
53
- } & {
54
- [K in HttpMethod]: MethodFn;
55
- } & ([SseFn] extends [never] ? {
56
- sse?: never;
57
- } : {
58
- sse: {
59
- [K in HttpMethod]: SseFn;
60
- };
88
+ /** Allowed input types for skill journey form fields. */
89
+ type SkillJourneyFieldType = "text" | "textarea" | "select";
90
+ /** Properties shared by every skill journey field, regardless of input type. */
91
+ interface SkillJourneyFieldBase {
92
+ key: string;
93
+ label: string;
94
+ placeholder: string;
95
+ helper?: string;
96
+ required?: boolean;
97
+ /** Conversational ask used by chat-native guided flows (web, mobile, SMS, voice).
98
+ * Falls through to `label` when not set. */
99
+ chatPrompt?: string;
100
+ /** Skill-specific nudge shown when the user skips a required field. Falls through
101
+ * to `SkillConfig.requiredNudgeDefault` when not set. */
102
+ requiredNudge?: string;
103
+ /** Server-side sanitiser cap on the value's character length. Today's gateways
104
+ * truncate textarea fields at `MULTILINE_FIELD_MAX` characters. */
105
+ maxLength?: number;
106
+ }
107
+ /**
108
+ * A single field in a skill journey intake form. Discriminated on `type`:
109
+ * `select` requires `options`; `text` / `textarea` cannot carry options.
110
+ */
111
+ type SkillJourneyField = (SkillJourneyFieldBase & {
112
+ type: "text" | "textarea";
113
+ }) | (SkillJourneyFieldBase & {
114
+ type: "select";
115
+ options: string[];
61
116
  });
62
- interface Config$1 {
63
- /**
64
- * Auth token or a function returning auth token. The resolved value will be
65
- * added to the request payload as defined by its `security` array.
66
- */
67
- auth?: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken;
68
- /**
69
- * A function for serializing request body parameter. By default,
70
- * {@link JSON.stringify()} will be used.
71
- */
72
- bodySerializer?: BodySerializer | null;
73
- /**
74
- * An object containing any HTTP headers that you want to pre-populate your
75
- * `Headers` object with.
76
- *
77
- * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more}
78
- */
79
- headers?: RequestInit['headers'] | Record<string, string | number | boolean | (string | number | boolean)[] | null | undefined | unknown>;
80
- /**
81
- * The request method.
82
- *
83
- * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more}
84
- */
85
- method?: Uppercase<HttpMethod>;
86
- /**
87
- * A function for serializing request query parameters. By default, arrays
88
- * will be exploded in form style, objects will be exploded in deepObject
89
- * style, and reserved characters are percent-encoded.
90
- *
91
- * This method will have no effect if the native `paramsSerializer()` Axios
92
- * API function is used.
93
- *
94
- * {@link https://swagger.io/docs/specification/serialization/#query View examples}
95
- */
96
- querySerializer?: QuerySerializer | QuerySerializerOptions;
97
- /**
98
- * A function validating request data. This is useful if you want to ensure
99
- * the request conforms to the desired shape, so it can be safely sent to
100
- * the server.
101
- */
102
- requestValidator?: (data: unknown) => Promise<unknown>;
103
- /**
104
- * A function transforming response data before it's returned. This is useful
105
- * for post-processing data, e.g., converting ISO strings into Date objects.
106
- */
107
- responseTransformer?: (data: unknown) => Promise<unknown>;
117
+ /** Full definition for rendering a skill intake form and building the prompt. */
118
+ interface SkillJourneyDefinition {
119
+ title: string;
120
+ description: string;
121
+ steps: string[];
122
+ /** When true, the journey renders a file upload step before the fields. */
123
+ acceptsFiles: boolean;
124
+ /** Label for the file upload step. Only meaningful when `acceptsFiles` is true. */
125
+ fileLabel?: string;
126
+ /** Helper text for the file upload step. Only meaningful when `acceptsFiles` is true. */
127
+ fileHint?: string;
128
+ /** Per-skill copy for the file upload step prompt. Only meaningful when `acceptsFiles` is true. */
129
+ filePrompt?: string;
130
+ /** How consumers should derive text from an attached file. Describes extraction
131
+ * only — it is **not** a file-type filter. Files outside the strategy's expected
132
+ * MIME type (e.g. photos when the strategy is `pdf-text`) are still uploaded and
133
+ * forwarded to the agent as attachments; they are simply not text-extracted.
134
+ * The extracted text is written to the variable named by `extractedTextTarget`.
135
+ * Only meaningful when `acceptsFiles` is true. */
136
+ fileExtractionStrategy?: FileExtractionStrategy;
137
+ /** Variable name to write extracted text into. May coincide with a field's `key`
138
+ * (the upload replaces typed input for that field, e.g. `lease_text`) or name a
139
+ * separate template variable not tied to any field (e.g. `rent_notice_text`,
140
+ * `supporting_document_text`). Only meaningful when `acceptsFiles` is true and
141
+ * `fileExtractionStrategy` is set. */
142
+ extractedTextTarget?: string;
143
+ fields: SkillJourneyField[];
108
144
  /**
109
- * A function validating response data. This is useful if you want to ensure
110
- * the response conforms to the desired shape, so it can be safely passed to
111
- * the transformers and returned to the user.
145
+ * When true, the journey expects substantive input — at least one of an
146
+ * `acceptsFiles` upload or a text/textarea field — for the agent to act on.
147
+ * Consumers (form renderers and direct API callers alike) should enforce
148
+ * "file or text provided" before invoking the skill.
112
149
  */
113
- responseValidator?: (data: unknown) => Promise<unknown>;
150
+ requiresContext: boolean;
151
+ submitLabel: string;
152
+ promptTitle: string;
153
+ /** Extra system prompt context injected per-skill for structured output guidance. */
154
+ systemContext?: string;
114
155
  }
115
-
116
- type ServerSentEventsOptions<TData = unknown> = Omit<RequestInit, 'method'> & Pick<Config$1, 'method' | 'responseTransformer' | 'responseValidator'> & {
117
- /**
118
- * Fetch API implementation. You can use this option to provide a custom
119
- * fetch instance.
120
- *
121
- * @default globalThis.fetch
122
- */
123
- fetch?: typeof fetch;
156
+ /** Marketplace filter the agent should appear under in the picker UI. */
157
+ type AgentMarketplaceFamily = "text" | "lifestyle";
158
+ /** What kind of action the marketplace card's primary CTA performs. */
159
+ type AgentPrimaryActionKind = "text" | "group" | "guided";
160
+ /** Visual tone for a transcript section heading. */
161
+ type AgentTranscriptTone = "danger" | "warning" | "success";
162
+ /** A single bullet inside a marketplace analysis transcript section. */
163
+ interface AgentTranscriptItem {
164
+ title: string;
165
+ detail: string;
166
+ }
167
+ /** A grouped block of bullets in a marketplace analysis transcript. */
168
+ interface AgentAnalysisSection {
169
+ title: string;
170
+ tone: AgentTranscriptTone;
171
+ items: AgentTranscriptItem[];
172
+ }
173
+ /** A todo entry inside a marketplace agenda transcript. */
174
+ interface AgentAgendaItem {
175
+ title: string;
176
+ badge?: string;
177
+ badgeTone?: "warning" | "danger";
178
+ }
179
+ /** A schedule entry inside a marketplace agenda transcript. */
180
+ interface AgentScheduleItem {
181
+ title: string;
182
+ time: string;
183
+ }
184
+ /** Marketplace transcript shape for productivity / agenda style agents. */
185
+ interface AgentAgendaTranscript {
186
+ kind: "agenda";
187
+ userMessage: string;
188
+ intro: string;
189
+ todos: AgentAgendaItem[];
190
+ schedule: AgentScheduleItem[];
191
+ followUp: string;
192
+ actions: string[];
193
+ }
194
+ /** Marketplace transcript shape for analysis / dispute style agents. */
195
+ interface AgentAnalysisTranscript {
196
+ kind: "analysis";
197
+ userMessage: string;
198
+ intro: string;
199
+ sections: AgentAnalysisSection[];
200
+ followUp: string;
201
+ actions: string[];
202
+ }
203
+ /** A scripted marketplace conversation preview, discriminated on `kind`. */
204
+ type AgentMarketplaceTranscript = AgentAgendaTranscript | AgentAnalysisTranscript;
205
+ /** Marketplace card content for an agent — the data the picker UI renders. */
206
+ interface AgentMarketplaceContent {
207
+ family: AgentMarketplaceFamily;
208
+ roleLabel: string;
209
+ primaryActionLabel: string;
210
+ primaryActionKind: AgentPrimaryActionKind;
211
+ cardUseCases: string[];
212
+ cardKnowledge: string[];
213
+ quickPrompts: string[];
214
+ emptyStateDescription: string;
215
+ transcript: AgentMarketplaceTranscript;
216
+ }
217
+ /** A single (user, agent) example conversation entry, keyed by i18n string ids. */
218
+ interface AgentExampleConversation {
219
+ userKey: string;
220
+ agentKey: string;
221
+ }
222
+ /** UI metadata used by clients when the portal doesn't supply richer data. */
223
+ interface AgentUiMetadata {
224
+ /** Hex color used by the agent card and accents. */
225
+ color: string;
226
+ /** Lucide icon identifier (e.g. "users"). */
227
+ icon: string;
228
+ /** i18n keys for the per-agent feature bullets. */
229
+ features: string[];
230
+ /** i18n keys for the example conversation strings. */
231
+ exampleConversations: AgentExampleConversation[];
232
+ /** Memory folder names this agent typically uses. */
233
+ suggestedMemoryFolders?: string[];
234
+ }
235
+ /** Full agent configuration including prompt, skills, tools, and model settings. */
236
+ interface AgentConfig {
237
+ id: string;
238
+ runtimes: AgentRuntime[];
239
+ prompt: string;
240
+ skills: SkillConfig[];
241
+ /** Tool configs are agent-specific, so we keep this generic. */
242
+ tools: unknown[];
243
+ model: {
244
+ default: string;
245
+ allowed?: string[];
246
+ };
247
+ manifest: AgentManifest;
248
+ /** Form schemas for rendering skill intake forms on the client. */
249
+ skillJourneys?: Record<string, SkillJourneyDefinition>;
250
+ /** First assistant message shown when the agent is opened. Supports the
251
+ * `{{agent_name}}` placeholder so consumers can substitute the portal-display
252
+ * name without changing this string. */
253
+ greeting?: string;
254
+ /** Sub-copy shown under the agent picker / guided-flow header. Supports the
255
+ * `{{agent_name}}` placeholder, same as `greeting`. */
256
+ guidedFlowSubcopy?: string;
257
+ /** Marketplace card content rendered in the agent picker. */
258
+ marketplace?: AgentMarketplaceContent;
259
+ /** UI metadata (color, icon, example conversations) for renderer fallbacks. */
260
+ uiMetadata?: AgentUiMetadata;
261
+ /** One-time disclaimer shown in a popup the first time a user opens
262
+ * a chat with this agent. Plain text; consumers split on `\n\n` for paragraphs.
263
+ * May include `{{agent_name}}` placeholder. */
264
+ firstTimeDisclaimer?: string;
265
+ /** Persistent one-line disclaimer rendered under the chat input
266
+ * whenever this agent's chat is open. May include `{{agent_name}}`
267
+ * placeholder. */
268
+ persistentFooter?: string;
269
+ }
270
+
271
+ type AuthToken = string | undefined;
272
+ interface Auth {
273
+ /**
274
+ * Which part of the request do we use to send the auth?
275
+ *
276
+ * @default 'header'
277
+ */
278
+ in?: 'header' | 'query' | 'cookie';
279
+ /**
280
+ * Header or query parameter name.
281
+ *
282
+ * @default 'Authorization'
283
+ */
284
+ name?: string;
285
+ scheme?: 'basic' | 'bearer';
286
+ type: 'apiKey' | 'http';
287
+ }
288
+
289
+ interface SerializerOptions<T> {
290
+ /**
291
+ * @default true
292
+ */
293
+ explode: boolean;
294
+ style: T;
295
+ }
296
+ type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited';
297
+ type ObjectStyle = 'form' | 'deepObject';
298
+
299
+ type QuerySerializer = (query: Record<string, unknown>) => string;
300
+ type BodySerializer = (body: unknown) => unknown;
301
+ type QuerySerializerOptionsObject = {
302
+ allowReserved?: boolean;
303
+ array?: Partial<SerializerOptions<ArrayStyle>>;
304
+ object?: Partial<SerializerOptions<ObjectStyle>>;
305
+ };
306
+ type QuerySerializerOptions = QuerySerializerOptionsObject & {
307
+ /**
308
+ * Per-parameter serialization overrides. When provided, these settings
309
+ * override the global array/object settings for specific parameter names.
310
+ */
311
+ parameters?: Record<string, QuerySerializerOptionsObject>;
312
+ };
313
+
314
+ type HttpMethod = 'connect' | 'delete' | 'get' | 'head' | 'options' | 'patch' | 'post' | 'put' | 'trace';
315
+ type Client$1<RequestFn = never, Config = unknown, MethodFn = never, BuildUrlFn = never, SseFn = never> = {
316
+ /**
317
+ * Returns the final request URL.
318
+ */
319
+ buildUrl: BuildUrlFn;
320
+ getConfig: () => Config;
321
+ request: RequestFn;
322
+ setConfig: (config: Config) => Config;
323
+ } & {
324
+ [K in HttpMethod]: MethodFn;
325
+ } & ([SseFn] extends [never] ? {
326
+ sse?: never;
327
+ } : {
328
+ sse: {
329
+ [K in HttpMethod]: SseFn;
330
+ };
331
+ });
332
+ interface Config$1 {
333
+ /**
334
+ * Auth token or a function returning auth token. The resolved value will be
335
+ * added to the request payload as defined by its `security` array.
336
+ */
337
+ auth?: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken;
338
+ /**
339
+ * A function for serializing request body parameter. By default,
340
+ * {@link JSON.stringify()} will be used.
341
+ */
342
+ bodySerializer?: BodySerializer | null;
343
+ /**
344
+ * An object containing any HTTP headers that you want to pre-populate your
345
+ * `Headers` object with.
346
+ *
347
+ * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more}
348
+ */
349
+ headers?: RequestInit['headers'] | Record<string, string | number | boolean | (string | number | boolean)[] | null | undefined | unknown>;
350
+ /**
351
+ * The request method.
352
+ *
353
+ * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more}
354
+ */
355
+ method?: Uppercase<HttpMethod>;
356
+ /**
357
+ * A function for serializing request query parameters. By default, arrays
358
+ * will be exploded in form style, objects will be exploded in deepObject
359
+ * style, and reserved characters are percent-encoded.
360
+ *
361
+ * This method will have no effect if the native `paramsSerializer()` Axios
362
+ * API function is used.
363
+ *
364
+ * {@link https://swagger.io/docs/specification/serialization/#query View examples}
365
+ */
366
+ querySerializer?: QuerySerializer | QuerySerializerOptions;
367
+ /**
368
+ * A function validating request data. This is useful if you want to ensure
369
+ * the request conforms to the desired shape, so it can be safely sent to
370
+ * the server.
371
+ */
372
+ requestValidator?: (data: unknown) => Promise<unknown>;
373
+ /**
374
+ * A function transforming response data before it's returned. This is useful
375
+ * for post-processing data, e.g., converting ISO strings into Date objects.
376
+ */
377
+ responseTransformer?: (data: unknown) => Promise<unknown>;
378
+ /**
379
+ * A function validating response data. This is useful if you want to ensure
380
+ * the response conforms to the desired shape, so it can be safely passed to
381
+ * the transformers and returned to the user.
382
+ */
383
+ responseValidator?: (data: unknown) => Promise<unknown>;
384
+ }
385
+
386
+ type ServerSentEventsOptions<TData = unknown> = Omit<RequestInit, 'method'> & Pick<Config$1, 'method' | 'responseTransformer' | 'responseValidator'> & {
387
+ /**
388
+ * Fetch API implementation. You can use this option to provide a custom
389
+ * fetch instance.
390
+ *
391
+ * @default globalThis.fetch
392
+ */
393
+ fetch?: typeof fetch;
124
394
  /**
125
395
  * Implementing clients can call request interceptors inside this hook.
126
396
  */
@@ -7226,22 +7496,68 @@ type LlmapiChatCompletionResponse = Omit<LlmapiChatCompletionResponse$1, "usage"
7226
7496
  };
7227
7497
 
7228
7498
  /**
7229
- * PII detection patterns for structured data types.
7499
+ * True when a text part is the attachment-contents part built by {@link buildAttachedFilesText}.
7230
7500
  *
7231
- * Each pattern has a category (used for placeholder tags like [EMAIL_1]),
7232
- * a regex, and an optional validator for reducing false positives.
7501
+ * Matches the whole generated shape — opening tag plus the fixed header line, and the closing
7502
+ * tag — not just the tag, so user text that merely starts with `<attached_files>` is still
7503
+ * treated as the user's prompt.
7233
7504
  */
7234
- type PiiCategory = "EMAIL" | "PHONE" | "SSN" | "CREDIT_CARD" | "IP_ADDRESS" | "API_KEY" | "US_ADDRESS" | "DATE_OF_BIRTH";
7235
- interface PiiPattern {
7236
- /**
7237
- * Placeholder tag prefix for matches. Built-ins use {@link PiiCategory};
7238
- * custom patterns may use any string (e.g. "PASSPORT", "IBAN").
7239
- */
7240
- category: PiiCategory | (string & {});
7241
- regex: RegExp;
7242
- /** Optional post-match validation to reduce false positives. */
7243
- validate?: (match: string) => boolean;
7244
- /**
7505
+ declare function isAttachedFilesText(text: string | undefined | null): boolean;
7506
+ /**
7507
+ * Wrap a turn's extracted file contents (the `[Extracted content from <name>]`
7508
+ * blocks `preprocessFiles` emits) in the tagged part that rides on that turn's
7509
+ * user message.
7510
+ */
7511
+ declare function buildAttachedFilesText(fileContext: string): string;
7512
+ /**
7513
+ * Put the current turn's extracted attachment contents on the last user
7514
+ * message, as a text part inserted right after that message's LAST text part:
7515
+ * after everything the user wrote, and before any parts that follow it
7516
+ * (typically images). With interleaved parts such as `[text, image, text,
7517
+ * image]` it lands between the second text and the second image.
7518
+ *
7519
+ * Why not a system message: a detached system message at the front of the
7520
+ * request ("the user has attached files to this conversation") is separated
7521
+ * from the words that refer to it by the whole system prompt, tool catalog and
7522
+ * history. In a long, multi-file conversation fast models stopped connecting
7523
+ * the two and told users the attachment was unreadable even though its text
7524
+ * was in the request. Keeping the contents on the turn that attached them is
7525
+ * how native file attachments work on every provider, and survives the auto
7526
+ * router switching models between turns.
7527
+ *
7528
+ * The part goes AFTER the user's own text because the portal's query
7529
+ * classifier routes on the first text part of the last user message — it must
7530
+ * keep reading the user's prompt, not the document.
7531
+ *
7532
+ * Returns a new array; the input is not mutated. When there is no user
7533
+ * message the messages are returned unchanged.
7534
+ */
7535
+ declare function attachFileContextToLastUserMessage(messages: LlmapiMessage[], fileContext: string): LlmapiMessage[];
7536
+
7537
+ /**
7538
+ * Value of the `origin` column on a row the SDK synthesised for a turn's tool results (v44, #866).
7539
+ *
7540
+ * `satisfies MessageOrigin` so this constant and the column's union cannot drift apart silently.
7541
+ */
7542
+ declare const TOOL_RESULT_ORIGIN = "tool_result";
7543
+
7544
+ /**
7545
+ * PII detection patterns for structured data types.
7546
+ *
7547
+ * Each pattern has a category (used for placeholder tags like [EMAIL_1]),
7548
+ * a regex, and an optional validator for reducing false positives.
7549
+ */
7550
+ type PiiCategory = "EMAIL" | "PHONE" | "SSN" | "CREDIT_CARD" | "IP_ADDRESS" | "API_KEY" | "US_ADDRESS" | "DATE_OF_BIRTH";
7551
+ interface PiiPattern {
7552
+ /**
7553
+ * Placeholder tag prefix for matches. Built-ins use {@link PiiCategory};
7554
+ * custom patterns may use any string (e.g. "PASSPORT", "IBAN").
7555
+ */
7556
+ category: PiiCategory | (string & {});
7557
+ regex: RegExp;
7558
+ /** Optional post-match validation to reduce false positives. */
7559
+ validate?: (match: string) => boolean;
7560
+ /**
7245
7561
  * Optional context guard. When set, a match is only redacted if this regex
7246
7562
  * matches the text immediately preceding it (a short window). Used to require
7247
7563
  * a cue word — e.g. only treat a date as a date-of-birth when "DOB" / "born"
@@ -7263,23 +7579,6 @@ interface PiiPattern {
7263
7579
  */
7264
7580
  declare const PII_PATTERNS: PiiPattern[];
7265
7581
 
7266
- /**
7267
- * NER detector contract for PII redaction.
7268
- *
7269
- * The regex layer in {@link ./patterns} handles *structured* PII (email, phone,
7270
- * SSN, credit card, …). It cannot detect *unstructured* PII — person names,
7271
- * locations, organizations — which need a named-entity-recognition model.
7272
- *
7273
- * Rather than bundle a model (and a runtime) into the SDK, we define a small
7274
- * runtime-agnostic contract: a {@link NerDetector} takes text and returns the
7275
- * spans it found. Consumers supply an implementation appropriate to their
7276
- * platform — a Transformers.js model in the browser/Node (see
7277
- * `./detectors/transformers`), an `onnxruntime-react-native` model on mobile,
7278
- * a remote service, or a fake in tests. The SDK keeps detection *on-device* by
7279
- * never calling out itself; it only consumes the spans a detector returns and
7280
- * merges them into the existing placeholder map.
7281
- */
7282
-
7283
7582
  /**
7284
7583
  * A character-offset span of detected PII. `start`/`end` are indices into the
7285
7584
  * text passed to {@link NerDetector.detect} (`end` exclusive). `category`
@@ -7308,17 +7607,6 @@ interface NerDetector {
7308
7607
  detect(text: string): Promise<PiiSpan[]>;
7309
7608
  }
7310
7609
 
7311
- /**
7312
- * PII redaction engine.
7313
- *
7314
- * Scans text for personally identifiable information, replaces matches with
7315
- * tagged placeholders ([EMAIL_1], [PHONE_2], …), and maintains a mapping
7316
- * table so the original values can be restored in LLM responses.
7317
- *
7318
- * One `PiiRedactor` instance should be used per conversation to keep
7319
- * placeholder numbering consistent across turns.
7320
- */
7321
-
7322
7610
  interface PiiMatch {
7323
7611
  category: PiiCategory | (string & {});
7324
7612
  original: string;
@@ -7604,36 +7892,6 @@ declare function createStreamingDeAnonymizer(redactor: PiiRedactor, emit: (chunk
7604
7892
  flush: () => void;
7605
7893
  };
7606
7894
 
7607
- /**
7608
- * Union type for API responses - raw pass-through from server.
7609
- * Responses API returns LlmapiResponseResponse (with output[]).
7610
- * Completions API returns LlmapiChatCompletionResponse (with choices[]).
7611
- */
7612
- type ApiResponse = LlmapiResponseResponse | LlmapiChatCompletionResponse;
7613
- /**
7614
- * Portal-side fields live under `response.portal` for the Chat Completions API
7615
- * (new OpenAI-compliant wire shape) and at the top level for the Responses API.
7616
- *
7617
- * The SDK's `buildFinalResponse` mirrors portal fields back onto the top level
7618
- * (and into `usage`) for backwards compatibility, so for streaming chat
7619
- * completions both paths carry the data. For non-streaming chat completions
7620
- * the wire ships only the portal-nested form.
7621
- *
7622
- * Precedence is per *field*, not per object: `portal?.x ?? legacy.x` reads the
7623
- * portal field when it is non-nullish and otherwise falls through to the legacy
7624
- * location — so a present-but-partial `portal` (e.g. checksum set, cost unset)
7625
- * still resolves each field from wherever it actually carries a value.
7626
- */
7627
- declare function getToolCallEvents(r: ApiResponse): LlmapiToolCallEvent[] | undefined;
7628
- declare function getCostMicroUsd(r: ApiResponse): number | undefined;
7629
- declare function getCreditsUsed(r: ApiResponse): number | undefined;
7630
- /**
7631
- * Image model the portal resolved for the request (set when an image-generation
7632
- * tool ran). Chat Completions ships it under `portal.image_model` with a legacy
7633
- * top-level mirror; the Responses API keeps it at the top level.
7634
- */
7635
- declare function getImageModel(r: ApiResponse): string | undefined;
7636
-
7637
7895
  /**
7638
7896
  * Tool executor function type
7639
7897
  */
@@ -7710,11 +7968,100 @@ type ToolConfig = LlmapiChatCompletionTool & {
7710
7968
  };
7711
7969
 
7712
7970
  /**
7713
- * Server-side tools caching module
7971
+ * Union type for API responses - raw pass-through from server.
7972
+ * Responses API returns LlmapiResponseResponse (with output[]).
7973
+ * Completions API returns LlmapiChatCompletionResponse (with choices[]).
7974
+ */
7975
+ type ApiResponse = LlmapiResponseResponse | LlmapiChatCompletionResponse;
7976
+ /**
7977
+ * Portal-side fields live under `response.portal` for the Chat Completions API
7978
+ * (new OpenAI-compliant wire shape) and at the top level for the Responses API.
7979
+ *
7980
+ * The SDK's `buildFinalResponse` mirrors portal fields back onto the top level
7981
+ * (and into `usage`) for backwards compatibility, so for streaming chat
7982
+ * completions both paths carry the data. For non-streaming chat completions
7983
+ * the wire ships only the portal-nested form.
7984
+ *
7985
+ * Precedence is per *field*, not per object: `portal?.x ?? legacy.x` reads the
7986
+ * portal field when it is non-nullish and otherwise falls through to the legacy
7987
+ * location — so a present-but-partial `portal` (e.g. checksum set, cost unset)
7988
+ * still resolves each field from wherever it actually carries a value.
7989
+ */
7990
+ declare function getToolCallEvents(r: ApiResponse): LlmapiToolCallEvent[] | undefined;
7991
+ declare function getCostMicroUsd(r: ApiResponse): number | undefined;
7992
+ declare function getCreditsUsed(r: ApiResponse): number | undefined;
7993
+ /**
7994
+ * Image model the portal resolved for the request (set when an image-generation
7995
+ * tool ran). Chat Completions ships it under `portal.image_model` with a legacy
7996
+ * top-level mirror; the Responses API keeps it at the top level.
7997
+ */
7998
+ declare function getImageModel(r: ApiResponse): string | undefined;
7999
+
8000
+ /**
8001
+ * Drop every cached plaintext title and pending decrypt promise.
8002
+ *
8003
+ * Wired into `clearAllEncryptionState()` via the listener registry in
8004
+ * `useEncryption.ts`. Also exported so consumers can clear the cache
8005
+ * proactively (e.g. on wallet switch within a session before the
8006
+ * full encryption-state teardown lands).
8007
+ */
8008
+ declare function clearLazyTitleCache(): void;
8009
+ /**
8010
+ * Decrypt a single conversation title on demand.
8011
+ *
8012
+ * Designed for the lazy display path: pair with `listConversationsLazy`
8013
+ * and call this once a row is actually visible.
8014
+ *
8015
+ * Behavior:
8016
+ * - Plaintext input (no `enc:` prefix) is returned unchanged. This
8017
+ * covers conversations created before encryption was enabled and
8018
+ * keeps the helper safe to call unconditionally from rendering
8019
+ * code that may receive a mix of encrypted and plaintext titles.
8020
+ * - Encrypted input is decrypted via `decryptField`, which uses the
8021
+ * same per-version cached `CryptoKey` as the eager path — no new
8022
+ * key derivations are triggered.
8023
+ * - Concurrent calls for the same `(address, encryptedTitle)` share
8024
+ * a single decrypt promise.
8025
+ * - The result is memoized in a 256-entry LRU.
8026
+ *
8027
+ * Throws if the encryption key for `address` isn't loaded. (The
8028
+ * underlying `decryptField` would otherwise silently return the
8029
+ * ciphertext, which would surface to the UI as a literal `enc:v3:...`
8030
+ * title — strictly worse than a thrown error the caller can catch.)
8031
+ *
8032
+ * @param encryptedTitle - The stored title. May be ciphertext or plaintext.
8033
+ * @param address - Wallet address that owns the encryption key.
8034
+ * @returns The decrypted plaintext title.
8035
+ */
8036
+ declare function decryptConversationTitle(encryptedTitle: string, address: string): Promise<string>;
8037
+
8038
+ /**
8039
+ * Why a file was not (fully) read. Paired with {@link FileProcessingStatus}.
8040
+ *
8041
+ * - `too_large`: over `maxFileSizeBytes`
8042
+ * - `unsupported_type`: no processor handles the file's type
8043
+ * - `no_data`: the file has no URL/data to read
8044
+ * - `empty`: the file was read but contained no extractable content
8045
+ * - `timeout`: processing exceeded `timeoutMs`
8046
+ * - `error`: the processor threw (corrupt, password-protected, …)
8047
+ */
8048
+ type FileProcessingReason = "too_large" | "unsupported_type" | "no_data" | "empty" | "timeout" | "error";
8049
+ /**
8050
+ * What happened to one attached file during preprocessing — so the app can tell the user
8051
+ * precisely which attachment the model could not read, and why.
7714
8052
  *
7715
- * Fetches and caches tools from /api/v1/tools endpoint
7716
- * with configurable expiration and localStorage persistence.
8053
+ * - `extracted`: its full text reached the model
8054
+ * - `truncated`: part of it reached the model (a size/row/image budget cut the rest)
8055
+ * - `rendered_as_images`: some or all pages were sent as images (scanned PDF)
8056
+ * - `skipped`: not processed (see `reason`)
8057
+ * - `failed`: processing was attempted and failed (see `reason`)
7717
8058
  */
8059
+ interface FileProcessingStatus {
8060
+ fileId: string;
8061
+ fileName: string;
8062
+ status: "extracted" | "truncated" | "rendered_as_images" | "skipped" | "failed";
8063
+ reason?: FileProcessingReason;
8064
+ }
7718
8065
 
7719
8066
  /**
7720
8067
  * Server tool definition with parameters field.
@@ -7951,723 +8298,153 @@ declare function createServerToolsFilter(options?: CreateServerToolsFilterOption
7951
8298
  */
7952
8299
  declare const DEFAULT_EXCLUDED_SERVER_TOOLS: readonly string[];
7953
8300
  /** Default match options for the server-tools filter (limit 5, minSim 0.5). */
7954
- declare const DEFAULT_SERVER_TOOLS_MATCH_OPTIONS: ToolMatchOptions;
7955
- /**
7956
- * Pre-configured server-tools filter ready to drop into `useChatStorage`'s
7957
- * `serverTools` option. Semantic matching against the user prompt with the
7958
- * default exclusion list applied, plus call-chain expansion via
7959
- * {@link SERVER_TOOL_DEPENDENCY_SETS} so continuation tools (read-after-search,
7960
- * geocode-before-weather) ride in with their entry tool.
7961
- *
7962
- * @example
7963
- * ```ts
7964
- * import { defaultServerToolsFilter, useChatStorage } from "@anuma/sdk/react";
7965
- *
7966
- * useChatStorage({
7967
- * ...,
7968
- * serverTools: defaultServerToolsFilter,
7969
- * });
7970
- * ```
7971
- *
7972
- * If you need to customize (extra excludes, different limits, opt into
7973
- * tool-set expansion), call `createServerToolsFilter` directly.
7974
- */
7975
- declare const defaultServerToolsFilter: ServerToolsFilterFunction;
7976
- /**
7977
- * Type for a server-tools filter — a function that takes prompt embeddings
7978
- * and the full server tool catalog and returns the names of tools to keep.
7979
- * Matches `useChatStorage`'s `serverTools` callback signature.
7980
- */
7981
- type ServerToolsFilterFunction = ((embeddings: number[] | number[][], tools: ServerTool[]) => string[]) & {
7982
- /**
7983
- * The unconditional exclusions this filter applies, exposed by
7984
- * {@link createServerToolsFilter} so defer-loading can honour them without the caller
7985
- * repeating the list — see {@link resolveDeferredServerTools}. Absent on a hand-written
7986
- * filter, or on one wrapped in a plain closure.
7987
- */
7988
- readonly excludeTools?: readonly string[];
7989
- };
7990
- /**
7991
- * Options for selectServerSideTools
7992
- */
7993
- interface SelectServerSideToolsOptions {
7994
- /** The user prompt to match tools against */
7995
- prompt: string;
7996
- /** Function to get auth token (uses Authorization: Bearer header) */
7997
- getToken?: () => Promise<string | null>;
7998
- /** Direct API key for server-side usage (uses X-API-Key header) */
7999
- apiKey?: string;
8000
- /** Base URL for the API */
8001
- baseUrl?: string;
8002
- /** Cache expiration in ms (default: 24h) */
8003
- cacheExpirationMs?: number;
8004
- /** Force refresh tools cache */
8005
- forceRefresh?: boolean;
8006
- /** Embedding model to use */
8007
- embeddingModel?: string;
8008
- /** Max tools to return (default: 10) */
8009
- limit?: number;
8010
- /** Minimum cosine similarity 0-1 (default: 0.3) */
8011
- minSimilarity?: number;
8012
- /** API format for returned tools (default: "responses") */
8013
- apiType?: "responses" | "completions";
8014
- }
8015
- /**
8016
- * Select server tools that are semantically relevant to a prompt.
8017
- *
8018
- * Fetches available tools (with caching), generates embeddings for the prompt,
8019
- * runs cosine-similarity matching, and returns tool schemas in the requested
8020
- * API format (Responses or Completions).
8021
- *
8022
- * @example
8023
- * ```ts
8024
- * import { selectServerSideTools } from "@anuma/sdk/tools";
8025
- *
8026
- * const tools = await selectServerSideTools({
8027
- * prompt: "Draw me a cat",
8028
- * getToken: async () => identityToken,
8029
- * });
8030
- *
8031
- * const response = await postApiV1Responses({
8032
- * body: {
8033
- * messages: [{ role: "user", content: [{ type: "text", text: "Draw me a cat" }] }],
8034
- * model: "fireworks/accounts/fireworks/models/kimi-k2p5",
8035
- * tools,
8036
- * },
8037
- * headers: { Authorization: `Bearer ${identityToken}` },
8038
- * });
8039
- * ```
8040
- */
8041
- declare function selectServerSideTools(options: SelectServerSideToolsOptions): Promise<Array<Record<string, unknown>>>;
8042
-
8043
- /**
8044
- * Connector tool catalog + denied-tools system-prompt rider.
8045
- *
8046
- * Granular connector access works by ALLOW/DENY-ing individual SDK tools.
8047
- * Denying a tool means it's never handed to the model that turn, so it
8048
- * physically can't be called — bypass-proof at the model layer.
8049
- *
8050
- * Consumer contract (this PR ships the foundation only — it does NOT wire
8051
- * anything into runtime filtering or the client; those are later phases):
8052
- *
8053
- * 1. Hide the denied tools from the model — pass the denied tool names as
8054
- * `createServerToolsFilter({ excludeTools })` so they're dropped from the
8055
- * toolset before it reaches the LLM.
8056
- * 2. Tell the user how to re-enable — append `buildDeniedToolsRider(denied)`
8057
- * to the system prompt. Because the tool is absent, the model can't name
8058
- * it on its own and would give a generic "I don't have access". The rider
8059
- * gives it the specific capability names (grouped by connector) so it can
8060
- * tell the user to re-enable that exact tool in Connected Apps settings.
8061
- *
8062
- * `TOOL_CATALOG` is the single source of truth for connector tool → friendly
8063
- * label + provider. `provider` is the canonical logical-provider identifier the
8064
- * portal and SDK already key on (matches `internal/oauth/providers.go` and the
8065
- * `createConnectorTokenGetter` call sites) — Phase 2/3 use it to group and
8066
- * persist per-tool denial. `connector` is the human-facing UI display string.
8067
- * Consumers (portal, client) read this on a version bump rather than re-encoding
8068
- * tool names. Labels are placeholders that will be refined later — keep them
8069
- * sensible.
8070
- */
8071
-
8072
- interface ToolCatalogEntry {
8073
- label: string;
8074
- /** Canonical logical-provider id (e.g. "gmail", "gcalendar", "gdrive"). */
8075
- provider: string;
8076
- /** Human-facing connector display name (UI label). */
8077
- connector: string;
8078
- }
8079
- declare const TOOL_CATALOG: Record<string, ToolCatalogEntry>;
8080
- /**
8081
- * Build a system-prompt fragment telling the model which connector capabilities
8082
- * the user has turned off, so it can give the specific "re-enable this tool"
8083
- * message instead of a generic refusal.
8084
- *
8085
- * Unknown names (not in {@link TOOL_CATALOG}) are skipped. Returns `""` when the
8086
- * input is empty or every name is unknown — callers append the result
8087
- * conditionally, so an empty string is a no-op.
8088
- */
8089
- declare function buildDeniedToolsRider(deniedToolNames: Iterable<string>): string;
8090
- interface ConnectorGuidanceInput {
8091
- /** The candidate tools for this turn (the final assembled set). */
8092
- tools: ToolConfig[];
8093
- /**
8094
- * Logical provider codes connected for the user — the same codes used as
8095
- * {@link TOOL_CATALOG} `provider` values (e.g. "gmail", "gcalendar",
8096
- * "gdrive", "notion", "github", "dropbox").
8097
- */
8098
- connectedProviders: string[];
8099
- /** Exact SDK tool-name strings the user turned off ({@link TOOL_CATALOG} keys). */
8100
- deniedToolNames: string[];
8101
- }
8102
- interface ConnectorGuidance {
8103
- /** `input.tools` with any denied tool removed (the helper owns filtering). */
8104
- tools: ToolConfig[];
8105
- /** Compact system-prompt block, or "" when there's nothing actionable. */
8106
- rider: string;
8107
- }
8108
- /**
8109
- * Single helper both web and mobile use so neither re-implements connector
8110
- * tool filtering or the accuracy messaging. It enforces denial (drops denied
8111
- * tools from the turn's toolset) AND builds a compact rider letting the model
8112
- * give an accurate message for each connector state instead of a generic
8113
- * "I don't have access":
8114
- *
8115
- * 1. Toggled off — connected provider whose tool is denied → "turned off,
8116
- * re-enable in Connected Apps".
8117
- * 2a. Pruned / 2b. no tool — covered by a general line telling the model not to
8118
- * claim no-access for a connected app. It rides along only when another
8119
- * section is present (on its own it isn't actionable).
8120
- * 3. Not connected — catalog provider absent from `connectedProviders` →
8121
- * "connect it in Connected Apps".
8122
- *
8123
- * State 3 is derived from {@link TOOL_CATALOG}'s provider set — providers with
8124
- * no tools (e.g. Disconnect-only Dropbox) aren't actionable via tools, so they
8125
- * aren't surfaced here. Everything is sorted alphabetically for deterministic
8126
- * output (no cache / snapshot churn). `rider` is "" when nothing is actionable
8127
- * (every catalog provider connected and nothing denied).
8128
- */
8129
- declare function buildConnectorGuidance(input: ConnectorGuidanceInput): ConnectorGuidance;
8130
-
8131
- /**
8132
- * Idle-keepalive wrapper for SSE streams produced by the generated
8133
- * SSE client in `src/client/core/serverSentEvents.gen.ts`.
8134
- *
8135
- * The generated SSE client does not enforce an idle timeout, so if the
8136
- * upstream server dies without sending `FIN`, a consumer can sit forever
8137
- * on a half-open socket. This wrapper:
8138
- *
8139
- * - Tracks the timestamp of the last event yielded by the source.
8140
- * - Starts an idle timer (default 60s, configurable via `idleMs`).
8141
- * - Resets the timer on every event.
8142
- * - Invokes `onIdle()` if the timer fires, and then closes the stream
8143
- * by throwing a typed {@link SseIdleTimeoutError} out of the iterator.
8144
- *
8145
- * This file lives outside `src/client/` on purpose: `@hey-api/openapi-ts`
8146
- * wipes its output directory on every regeneration, so anything inside
8147
- * `src/client/` would be clobbered by `pnpm run generate`.
8148
- */
8149
- /**
8150
- * Options accepted by {@link withSseKeepalive}.
8151
- */
8152
- interface SseKeepaliveOptions {
8153
- /**
8154
- * Idle window, in milliseconds. If no event is yielded by the source for
8155
- * this long, the stream is considered dead and is torn down.
8156
- *
8157
- * Set to `0` or a negative number to disable the idle timer (in which
8158
- * case the wrapper is a straight passthrough).
8159
- *
8160
- * @default 60000
8161
- */
8162
- idleMs?: number;
8163
- /**
8164
- * Invoked when the idle timer fires. The callback runs before the
8165
- * wrapper closes the stream, so consumers can log, emit metrics, or
8166
- * attempt reconnects from here.
8167
- *
8168
- * The callback is best-effort — thrown errors are swallowed so they do
8169
- * not mask the {@link SseIdleTimeoutError} that follows.
8170
- */
8171
- onIdle?: (info: {
8172
- idleMs: number;
8173
- lastEventAt: number;
8174
- }) => void;
8175
- }
8176
- /**
8177
- * Thrown by {@link withSseKeepalive} when no event arrives within the
8178
- * configured idle window. Callers can `instanceof`-check this to
8179
- * distinguish idle timeouts from other stream errors.
8180
- */
8181
- declare class SseIdleTimeoutError extends Error {
8182
- readonly idleMs: number;
8183
- readonly lastEventAt: number;
8184
- constructor(idleMs: number, lastEventAt: number);
8185
- }
8186
- /**
8187
- * Wrap an SSE-style async iterable with an idle timeout. If no value is
8188
- * yielded by `source` within `idleMs`, `onIdle` fires and the wrapper
8189
- * throws {@link SseIdleTimeoutError}, which propagates to the consumer's
8190
- * `for await` loop and, via the iterator protocol, calls `source.return()`
8191
- * so the underlying fetch/reader is cancelled.
8192
- *
8193
- * Typical usage with the generated SSE client:
8194
- *
8195
- * ```ts
8196
- * import { createSseClient } from "@anuma/sdk/client/core/serverSentEvents.gen";
8197
- * import { withSseKeepalive } from "@anuma/sdk";
8198
- *
8199
- * const { stream } = createSseClient({ url });
8200
- * for await (const event of withSseKeepalive(stream, {
8201
- * idleMs: 60_000,
8202
- * onIdle: ({ idleMs }) => logger.warn(`SSE idle for ${idleMs}ms`),
8203
- * })) {
8204
- * // ...
8205
- * }
8206
- * ```
8207
- *
8208
- * The wrapper is a pure passthrough when `idleMs <= 0`.
8209
- */
8210
- declare function withSseKeepalive<T>(source: AsyncIterable<T>, options?: SseKeepaliveOptions): AsyncGenerator<T, void, unknown>;
8211
-
8212
- /**
8213
- * Cross-platform primitive constants.
8214
- *
8215
- * This module is intentionally dependency-free and side-effect-free so it can
8216
- * be loaded by *any* consumer — including React Native / Hermes — without
8217
- * pulling in the rest of the SDK. It is published as the `@anuma/sdk/constants`
8218
- * subpath (which carries a `react-native` export condition); slim consumers
8219
- * such as the agent packages import values from here instead of the bare
8220
- * `@anuma/sdk` entry, whose Node/web bundle is not RN-safe.
8221
- *
8222
- * Only add genuinely pure, cross-platform constants here (numeric/string
8223
- * literals, default IDs, size caps). No classes, no runtime APIs, no imports.
8224
- */
8225
- /** Character cap for multiline / textarea fields. Agent gateways (e.g. cf-tasks)
8226
- * truncate values to this length before interpolating them into prompt templates.
8227
- * Shared across all agent packages so a single change updates every journey
8228
- * declaration simultaneously. */
8229
- declare const MULTILINE_FIELD_MAX = 50000;
8230
-
8231
- /** Runtime environment where the agent can execute. */
8232
- type AgentRuntime = "client" | "server";
8233
- /** Strategy a consumer should use to extract text from an uploaded file before
8234
- * feeding it into a skill journey field. The SDK only labels the strategy —
8235
- * the actual extraction (pdf.js, OCR, etc.) is the consumer's responsibility. */
8236
- type FileExtractionStrategy = "pdf-text" | "csv-text" | "image-ocr";
8237
-
8238
- /** Configuration for a single agent skill (task template). */
8239
- interface SkillConfig {
8240
- /** Unique identifier. Convention: "domain.action" e.g. "housing.lease-review" */
8241
- id: string;
8242
- /** Human-readable name. */
8243
- name: string;
8244
- /** System prompt template with {{variable}} placeholders. */
8245
- promptTemplate: string;
8246
- /**
8247
- * User-turn template, interpolated with the same {{variable}} placeholders
8248
- * as `promptTemplate`. Optional, but strongly recommended for skill-driven
8249
- * (non-freeform) flows so the model receives a proper user turn — most
8250
- * Anthropic models reject requests that contain only a system message.
8251
- */
8252
- userTemplate?: string;
8253
- /** MCP tool names this skill requires. */
8254
- requiredTools?: string[];
8255
- /** Preferred model when the caller doesn't specify one. */
8256
- preferredModel?: string;
8257
- /** Max agentic loop steps for this skill. */
8258
- maxSteps?: number;
8259
- /** Variables the promptTemplate expects at runtime. */
8260
- requiredVariables?: string[];
8261
- /**
8262
- * SMS-friendly question prompts keyed by variable name. The Anuma text
8263
- * gateway sends each prompt to the user one at a time and treats the
8264
- * user's reply as the slot value. Missing entries fall back to a
8265
- * generic "Please provide {name}." question in the gateway.
8266
- *
8267
- * Order of questions follows requiredVariables[] order.
8268
- */
8269
- smsPrompts?: Record<string, string>;
8270
- /** Additional context appended after the prompt template. */
8271
- contextSuffix?: string;
8272
- /** Lucide icon identifier (e.g. "FileSearch") for renderers that draw a skill icon. */
8273
- iconName?: string;
8274
- /** One-line announcement string shown when the skill is launched (channel-neutral). */
8275
- launchMessage?: string;
8276
- /** Conversational preamble shown after the user picks this skill, before the field
8277
- * questions begin. Used by chat-native guided flows (web, mobile, SMS, voice). */
8278
- preamble?: string;
8279
- /** Closing line shown after the user finishes the last field and before the agent
8280
- * starts the tool loop. Used by chat-native guided flows. */
8281
- closing?: string;
8282
- /** Fallback nudge for any required field on this skill that doesn't define its own
8283
- * `requiredNudge` string. */
8284
- requiredNudgeDefault?: string;
8285
- }
8286
- /** Public-safe manifest describing an agent (no prompt or tool details). */
8287
- interface AgentManifest {
8288
- id: string;
8289
- name: string;
8290
- description: string;
8291
- runtimes: AgentRuntime[];
8292
- skills: Array<{
8293
- id: string;
8294
- name: string;
8295
- requiredVariables?: string[];
8296
- smsPrompts?: Record<string, string>;
8297
- }>;
8298
- }
8299
- /** Allowed input types for skill journey form fields. */
8300
- type SkillJourneyFieldType = "text" | "textarea" | "select";
8301
- /** Properties shared by every skill journey field, regardless of input type. */
8302
- interface SkillJourneyFieldBase {
8303
- key: string;
8304
- label: string;
8305
- placeholder: string;
8306
- helper?: string;
8307
- required?: boolean;
8308
- /** Conversational ask used by chat-native guided flows (web, mobile, SMS, voice).
8309
- * Falls through to `label` when not set. */
8310
- chatPrompt?: string;
8311
- /** Skill-specific nudge shown when the user skips a required field. Falls through
8312
- * to `SkillConfig.requiredNudgeDefault` when not set. */
8313
- requiredNudge?: string;
8314
- /** Server-side sanitiser cap on the value's character length. Today's gateways
8315
- * truncate textarea fields at `MULTILINE_FIELD_MAX` characters. */
8316
- maxLength?: number;
8317
- }
8318
- /**
8319
- * A single field in a skill journey intake form. Discriminated on `type`:
8320
- * `select` requires `options`; `text` / `textarea` cannot carry options.
8321
- */
8322
- type SkillJourneyField = (SkillJourneyFieldBase & {
8323
- type: "text" | "textarea";
8324
- }) | (SkillJourneyFieldBase & {
8325
- type: "select";
8326
- options: string[];
8327
- });
8328
- /** Full definition for rendering a skill intake form and building the prompt. */
8329
- interface SkillJourneyDefinition {
8330
- title: string;
8331
- description: string;
8332
- steps: string[];
8333
- /** When true, the journey renders a file upload step before the fields. */
8334
- acceptsFiles: boolean;
8335
- /** Label for the file upload step. Only meaningful when `acceptsFiles` is true. */
8336
- fileLabel?: string;
8337
- /** Helper text for the file upload step. Only meaningful when `acceptsFiles` is true. */
8338
- fileHint?: string;
8339
- /** Per-skill copy for the file upload step prompt. Only meaningful when `acceptsFiles` is true. */
8340
- filePrompt?: string;
8341
- /** How consumers should derive text from an attached file. Describes extraction
8342
- * only — it is **not** a file-type filter. Files outside the strategy's expected
8343
- * MIME type (e.g. photos when the strategy is `pdf-text`) are still uploaded and
8344
- * forwarded to the agent as attachments; they are simply not text-extracted.
8345
- * The extracted text is written to the variable named by `extractedTextTarget`.
8346
- * Only meaningful when `acceptsFiles` is true. */
8347
- fileExtractionStrategy?: FileExtractionStrategy;
8348
- /** Variable name to write extracted text into. May coincide with a field's `key`
8349
- * (the upload replaces typed input for that field, e.g. `lease_text`) or name a
8350
- * separate template variable not tied to any field (e.g. `rent_notice_text`,
8351
- * `supporting_document_text`). Only meaningful when `acceptsFiles` is true and
8352
- * `fileExtractionStrategy` is set. */
8353
- extractedTextTarget?: string;
8354
- fields: SkillJourneyField[];
8355
- /**
8356
- * When true, the journey expects substantive input — at least one of an
8357
- * `acceptsFiles` upload or a text/textarea field — for the agent to act on.
8358
- * Consumers (form renderers and direct API callers alike) should enforce
8359
- * "file or text provided" before invoking the skill.
8360
- */
8361
- requiresContext: boolean;
8362
- submitLabel: string;
8363
- promptTitle: string;
8364
- /** Extra system prompt context injected per-skill for structured output guidance. */
8365
- systemContext?: string;
8366
- }
8367
- /** Marketplace filter the agent should appear under in the picker UI. */
8368
- type AgentMarketplaceFamily = "text" | "lifestyle";
8369
- /** What kind of action the marketplace card's primary CTA performs. */
8370
- type AgentPrimaryActionKind = "text" | "group" | "guided";
8371
- /** Visual tone for a transcript section heading. */
8372
- type AgentTranscriptTone = "danger" | "warning" | "success";
8373
- /** A single bullet inside a marketplace analysis transcript section. */
8374
- interface AgentTranscriptItem {
8375
- title: string;
8376
- detail: string;
8377
- }
8378
- /** A grouped block of bullets in a marketplace analysis transcript. */
8379
- interface AgentAnalysisSection {
8380
- title: string;
8381
- tone: AgentTranscriptTone;
8382
- items: AgentTranscriptItem[];
8383
- }
8384
- /** A todo entry inside a marketplace agenda transcript. */
8385
- interface AgentAgendaItem {
8386
- title: string;
8387
- badge?: string;
8388
- badgeTone?: "warning" | "danger";
8389
- }
8390
- /** A schedule entry inside a marketplace agenda transcript. */
8391
- interface AgentScheduleItem {
8392
- title: string;
8393
- time: string;
8394
- }
8395
- /** Marketplace transcript shape for productivity / agenda style agents. */
8396
- interface AgentAgendaTranscript {
8397
- kind: "agenda";
8398
- userMessage: string;
8399
- intro: string;
8400
- todos: AgentAgendaItem[];
8401
- schedule: AgentScheduleItem[];
8402
- followUp: string;
8403
- actions: string[];
8404
- }
8405
- /** Marketplace transcript shape for analysis / dispute style agents. */
8406
- interface AgentAnalysisTranscript {
8407
- kind: "analysis";
8408
- userMessage: string;
8409
- intro: string;
8410
- sections: AgentAnalysisSection[];
8411
- followUp: string;
8412
- actions: string[];
8413
- }
8414
- /** A scripted marketplace conversation preview, discriminated on `kind`. */
8415
- type AgentMarketplaceTranscript = AgentAgendaTranscript | AgentAnalysisTranscript;
8416
- /** Marketplace card content for an agent — the data the picker UI renders. */
8417
- interface AgentMarketplaceContent {
8418
- family: AgentMarketplaceFamily;
8419
- roleLabel: string;
8420
- primaryActionLabel: string;
8421
- primaryActionKind: AgentPrimaryActionKind;
8422
- cardUseCases: string[];
8423
- cardKnowledge: string[];
8424
- quickPrompts: string[];
8425
- emptyStateDescription: string;
8426
- transcript: AgentMarketplaceTranscript;
8427
- }
8428
- /** A single (user, agent) example conversation entry, keyed by i18n string ids. */
8429
- interface AgentExampleConversation {
8430
- userKey: string;
8431
- agentKey: string;
8432
- }
8433
- /** UI metadata used by clients when the portal doesn't supply richer data. */
8434
- interface AgentUiMetadata {
8435
- /** Hex color used by the agent card and accents. */
8436
- color: string;
8437
- /** Lucide icon identifier (e.g. "users"). */
8438
- icon: string;
8439
- /** i18n keys for the per-agent feature bullets. */
8440
- features: string[];
8441
- /** i18n keys for the example conversation strings. */
8442
- exampleConversations: AgentExampleConversation[];
8443
- /** Memory folder names this agent typically uses. */
8444
- suggestedMemoryFolders?: string[];
8445
- }
8446
- /** Full agent configuration including prompt, skills, tools, and model settings. */
8447
- interface AgentConfig {
8448
- id: string;
8449
- runtimes: AgentRuntime[];
8450
- prompt: string;
8451
- skills: SkillConfig[];
8452
- /** Tool configs are agent-specific, so we keep this generic. */
8453
- tools: unknown[];
8454
- model: {
8455
- default: string;
8456
- allowed?: string[];
8457
- };
8458
- manifest: AgentManifest;
8459
- /** Form schemas for rendering skill intake forms on the client. */
8460
- skillJourneys?: Record<string, SkillJourneyDefinition>;
8461
- /** First assistant message shown when the agent is opened. Supports the
8462
- * `{{agent_name}}` placeholder so consumers can substitute the portal-display
8463
- * name without changing this string. */
8464
- greeting?: string;
8465
- /** Sub-copy shown under the agent picker / guided-flow header. Supports the
8466
- * `{{agent_name}}` placeholder, same as `greeting`. */
8467
- guidedFlowSubcopy?: string;
8468
- /** Marketplace card content rendered in the agent picker. */
8469
- marketplace?: AgentMarketplaceContent;
8470
- /** UI metadata (color, icon, example conversations) for renderer fallbacks. */
8471
- uiMetadata?: AgentUiMetadata;
8472
- /** One-time disclaimer shown in a popup the first time a user opens
8473
- * a chat with this agent. Plain text; consumers split on `\n\n` for paragraphs.
8474
- * May include `{{agent_name}}` placeholder. */
8475
- firstTimeDisclaimer?: string;
8476
- /** Persistent one-line disclaimer rendered under the chat input
8477
- * whenever this agent's chat is open. May include `{{agent_name}}`
8478
- * placeholder. */
8479
- persistentFooter?: string;
8480
- }
8481
-
8482
- /**
8483
- * Pluggable logger for the Anuma SDK.
8484
- *
8485
- * By default all SDK logging goes to `console`. Call {@link setLogger} at app
8486
- * init (or use `<LoggerProvider>` in React) to redirect output to your own
8487
- * logging infrastructure (PostHog, Datadog, Sentry, etc.).
8488
- *
8489
- * @example
8490
- * ```ts
8491
- * import { setLogger, type Logger } from "@anuma/sdk";
8492
- *
8493
- * const myLogger: Logger = {
8494
- * debug: () => {},
8495
- * info: (...args) => posthog.capture("sdk_info", { message: args }),
8496
- * warn: (...args) => console.warn("[SDK]", ...args),
8497
- * error: (...args) => Sentry.captureMessage(args.join(" ")),
8498
- * };
8499
- *
8500
- * setLogger(myLogger);
8501
- * ```
8502
- */
8503
- interface Logger {
8504
- debug: (...args: unknown[]) => void;
8505
- /** Used for outcomes that are noteworthy but expected — a consolidation
8506
- * refusal that preserved a memory, for example. Keep genuinely-wrong things at
8507
- * `warn` so the two stay distinguishable in a log search. */
8508
- info: (...args: unknown[]) => void;
8509
- warn: (...args: unknown[]) => void;
8510
- error: (...args: unknown[]) => void;
8511
- }
8512
- /** Default logger that delegates to the global `console` object. */
8513
- declare const consoleLogger: Logger;
8514
- /** Silent logger that discards all output. */
8515
- declare const noopLogger: Logger;
8516
- /** Replace the active SDK logger. Pass {@link consoleLogger} to restore defaults. */
8517
- declare function setLogger(logger: Logger): void;
8518
- /** Return the active SDK logger. */
8519
- declare function getLogger(): Logger;
8520
-
8521
- /** A single section in the system prompt. */
8522
- interface PromptSection {
8523
- /** Unique identifier (used for disabling/overriding). */
8524
- key: string;
8525
- /** Lower priority = earlier in the prompt. Sections sort ascending. */
8526
- priority: number;
8527
- /** Return text to append, or null to skip this section. */
8528
- render: (ctx: PromptContext) => string | null;
8529
- }
8530
- /** Context passed to every section renderer. */
8531
- interface PromptContext {
8532
- /** Base personality prompt (from PersonaConfig.prompt). */
8533
- basePrompt: string;
8534
- /** User-set "act as X" communication preference. */
8535
- persona?: string | null;
8536
- /** Analyzed user communication style profile. */
8537
- styleProfile?: string | null;
8538
- /** Detected user sentiment. */
8539
- sentiment?: "frustrated" | "positive" | "neutral" | null;
8540
- /** ISO 639-1 language code, 'other', or null for English/undetected. */
8541
- preferredLanguage?: string | null;
8542
- /** Platform-specific formatting instructions from PersonaConfig. */
8543
- platformFormatting?: string | null;
8544
- /** Available tools. */
8545
- toolSummaries?: Array<{
8546
- name: string;
8547
- description: string;
8548
- }>;
8549
- /** Consumer-specific data for custom sections. */
8550
- extra?: Record<string, unknown>;
8551
- }
8552
- /** Configurable template strings for built-in section renderers. */
8553
- interface PromptTemplates {
8554
- tools: string;
8555
- persona: string;
8556
- style: string;
8557
- sentiment: {
8558
- frustrated: string;
8559
- positive: string;
8560
- };
8561
- language: string;
8562
- languageGeneric: string;
8563
- platformFormatting: string;
8564
- }
8565
- /** Options for buildSystemPrompt(). */
8566
- interface BuildPromptOptions {
8567
- /** Additional sections appended to defaults. */
8568
- extraSections?: PromptSection[];
8569
- /** Section keys to skip. */
8570
- disabled?: string[];
8571
- /** Max total prompt length in characters. 0 = unlimited. */
8572
- maxLength?: number;
8573
- /** Override default template strings. */
8574
- templates?: Partial<PromptTemplates>;
8575
- }
8576
- /** Result of buildSystemPrompt(). */
8577
- interface BuildPromptResult {
8578
- /** The assembled prompt string. */
8579
- prompt: string;
8580
- /** Keys of sections that rendered (in order). */
8581
- activeSections: string[];
8582
- }
8583
- /** Options for analyzeStyle(). */
8584
- interface AnalyzeStyleOptions {
8585
- /** Recent conversation messages. */
8586
- messages: Array<{
8587
- role: string;
8588
- content: string;
8589
- }>;
8590
- /** Callback to send messages to an LLM and get a text response. */
8591
- callLlm: (messages: Array<{
8592
- role: string;
8593
- content: string;
8594
- }>) => Promise<string | null>;
8595
- /** Max user messages to consider (default: 10). */
8596
- maxInputMessages?: number;
8597
- /** Minimum user messages required (default: 3). */
8598
- minMessages?: number;
8599
- /** Max profile length in characters (default: 200). */
8600
- maxProfileLength?: number;
8601
- /** Custom system prompt for the analysis model. */
8602
- analysisPrompt?: string;
8603
- /** Regex to filter out document-embedded messages. */
8604
- documentPattern?: RegExp;
8605
- }
8606
- /** Result of analyzeStyle(). */
8607
- interface AnalyzeStyleResult {
8608
- /** The style profile string, or null if skipped/failed. */
8609
- profile: string | null;
8610
- }
8611
- /** Input for shouldAnalyzeStyle(). */
8612
- interface StyleAnalysisSchedule {
8613
- /** Current message count for the user. */
8614
- messageCount: number;
8615
- /** Whether the user already has a style profile. */
8616
- hasProfile: boolean;
8617
- /** Whether analysis has ever run (updatedAt timestamp is set). */
8618
- hasBeenAnalyzed: boolean;
8619
- /** Whether the user opted out (updatedAt set but profile is null). */
8620
- optedOut: boolean;
8621
- /** Trigger analysis after this many messages (default: 5). */
8622
- analyzeAfterMessages?: number;
8623
- /** Re-analyze every N messages (default: 20). */
8624
- refreshEveryMessages?: number;
8301
+ declare const DEFAULT_SERVER_TOOLS_MATCH_OPTIONS: ToolMatchOptions;
8302
+ /**
8303
+ * Pre-configured server-tools filter ready to drop into `useChatStorage`'s
8304
+ * `serverTools` option. Semantic matching against the user prompt with the
8305
+ * default exclusion list applied, plus call-chain expansion via
8306
+ * {@link SERVER_TOOL_DEPENDENCY_SETS} so continuation tools (read-after-search,
8307
+ * geocode-before-weather) ride in with their entry tool.
8308
+ *
8309
+ * @example
8310
+ * ```ts
8311
+ * import { defaultServerToolsFilter, useChatStorage } from "@anuma/sdk/react";
8312
+ *
8313
+ * useChatStorage({
8314
+ * ...,
8315
+ * serverTools: defaultServerToolsFilter,
8316
+ * });
8317
+ * ```
8318
+ *
8319
+ * If you need to customize (extra excludes, different limits, opt into
8320
+ * tool-set expansion), call `createServerToolsFilter` directly.
8321
+ */
8322
+ declare const defaultServerToolsFilter: ServerToolsFilterFunction;
8323
+ /**
8324
+ * Type for a server-tools filter — a function that takes prompt embeddings
8325
+ * and the full server tool catalog and returns the names of tools to keep.
8326
+ * Matches `useChatStorage`'s `serverTools` callback signature.
8327
+ */
8328
+ type ServerToolsFilterFunction = ((embeddings: number[] | number[][], tools: ServerTool[]) => string[]) & {
8329
+ /**
8330
+ * The unconditional exclusions this filter applies, exposed by
8331
+ * {@link createServerToolsFilter} so defer-loading can honour them without the caller
8332
+ * repeating the list — see {@link resolveDeferredServerTools}. Absent on a hand-written
8333
+ * filter, or on one wrapped in a plain closure.
8334
+ */
8335
+ readonly excludeTools?: readonly string[];
8336
+ };
8337
+ /**
8338
+ * Options for selectServerSideTools
8339
+ */
8340
+ interface SelectServerSideToolsOptions {
8341
+ /** The user prompt to match tools against */
8342
+ prompt: string;
8343
+ /** Function to get auth token (uses Authorization: Bearer header) */
8344
+ getToken?: () => Promise<string | null>;
8345
+ /** Direct API key for server-side usage (uses X-API-Key header) */
8346
+ apiKey?: string;
8347
+ /** Base URL for the API */
8348
+ baseUrl?: string;
8349
+ /** Cache expiration in ms (default: 24h) */
8350
+ cacheExpirationMs?: number;
8351
+ /** Force refresh tools cache */
8352
+ forceRefresh?: boolean;
8353
+ /** Embedding model to use */
8354
+ embeddingModel?: string;
8355
+ /** Max tools to return (default: 10) */
8356
+ limit?: number;
8357
+ /** Minimum cosine similarity 0-1 (default: 0.3) */
8358
+ minSimilarity?: number;
8359
+ /** API format for returned tools (default: "responses") */
8360
+ apiType?: "responses" | "completions";
8625
8361
  }
8626
-
8627
- declare const DEFAULT_ANALYSIS_PROMPT = "Analyze these user messages and describe their communication style in 1-2 concise sentences. Focus on: tone (formal/casual/playful), message length preference, vocabulary level, and any notable patterns. Output only the style description, nothing else.";
8628
- declare const DEFAULT_MAX_INPUT_MESSAGES = 10;
8629
- declare const DEFAULT_MIN_MESSAGES = 3;
8630
- declare const DEFAULT_MAX_PROFILE_LENGTH = 200;
8631
- declare const DEFAULT_ANALYZE_AFTER_MESSAGES = 5;
8632
- declare const DEFAULT_REFRESH_EVERY_MESSAGES = 20;
8633
- declare const DEFAULT_DOCUMENT_PATTERN: RegExp;
8634
- declare function shouldAnalyzeStyle(schedule: StyleAnalysisSchedule): boolean;
8635
- declare function analyzeStyle(options: AnalyzeStyleOptions): Promise<AnalyzeStyleResult>;
8636
-
8637
- declare const DEFAULT_PROMPT_TEMPLATES: PromptTemplates;
8638
- /** Collapse runs of 3+ double-quotes to a single quote to prevent prompt injection. */
8639
- declare function sanitizeQuotes(s: string): string;
8640
- /** Replace {content} in a template with sanitized value. Returns null if value is falsy. */
8641
- declare function renderTemplate(value: string | null | undefined, template: string): string | null;
8642
- declare function buildSystemPrompt(ctx: PromptContext, options?: BuildPromptOptions): BuildPromptResult;
8643
-
8644
8362
  /**
8645
- * Why a file was not (fully) read. Paired with {@link FileProcessingStatus}.
8363
+ * Select server tools that are semantically relevant to a prompt.
8646
8364
  *
8647
- * - `too_large`: over `maxFileSizeBytes`
8648
- * - `unsupported_type`: no processor handles the file's type
8649
- * - `no_data`: the file has no URL/data to read
8650
- * - `empty`: the file was read but contained no extractable content
8651
- * - `timeout`: processing exceeded `timeoutMs`
8652
- * - `error`: the processor threw (corrupt, password-protected, …)
8365
+ * Fetches available tools (with caching), generates embeddings for the prompt,
8366
+ * runs cosine-similarity matching, and returns tool schemas in the requested
8367
+ * API format (Responses or Completions).
8368
+ *
8369
+ * @example
8370
+ * ```ts
8371
+ * import { selectServerSideTools } from "@anuma/sdk/tools";
8372
+ *
8373
+ * const tools = await selectServerSideTools({
8374
+ * prompt: "Draw me a cat",
8375
+ * getToken: async () => identityToken,
8376
+ * });
8377
+ *
8378
+ * const response = await postApiV1Responses({
8379
+ * body: {
8380
+ * messages: [{ role: "user", content: [{ type: "text", text: "Draw me a cat" }] }],
8381
+ * model: "fireworks/accounts/fireworks/models/kimi-k2p5",
8382
+ * tools,
8383
+ * },
8384
+ * headers: { Authorization: `Bearer ${identityToken}` },
8385
+ * });
8386
+ * ```
8653
8387
  */
8654
- type FileProcessingReason = "too_large" | "unsupported_type" | "no_data" | "empty" | "timeout" | "error";
8388
+ declare function selectServerSideTools(options: SelectServerSideToolsOptions): Promise<Array<Record<string, unknown>>>;
8389
+
8390
+ interface ToolCatalogEntry {
8391
+ label: string;
8392
+ /** Canonical logical-provider id (e.g. "gmail", "gcalendar", "gdrive"). */
8393
+ provider: string;
8394
+ /** Human-facing connector display name (UI label). */
8395
+ connector: string;
8396
+ }
8397
+ declare const TOOL_CATALOG: Record<string, ToolCatalogEntry>;
8655
8398
  /**
8656
- * What happened to one attached file during preprocessing — so the app can tell the user
8657
- * precisely which attachment the model could not read, and why.
8399
+ * Build a system-prompt fragment telling the model which connector capabilities
8400
+ * the user has turned off, so it can give the specific "re-enable this tool"
8401
+ * message instead of a generic refusal.
8658
8402
  *
8659
- * - `extracted`: its full text reached the model
8660
- * - `truncated`: part of it reached the model (a size/row/image budget cut the rest)
8661
- * - `rendered_as_images`: some or all pages were sent as images (scanned PDF)
8662
- * - `skipped`: not processed (see `reason`)
8663
- * - `failed`: processing was attempted and failed (see `reason`)
8403
+ * Unknown names (not in {@link TOOL_CATALOG}) are skipped. Returns `""` when the
8404
+ * input is empty or every name is unknown — callers append the result
8405
+ * conditionally, so an empty string is a no-op.
8664
8406
  */
8665
- interface FileProcessingStatus {
8666
- fileId: string;
8667
- fileName: string;
8668
- status: "extracted" | "truncated" | "rendered_as_images" | "skipped" | "failed";
8669
- reason?: FileProcessingReason;
8407
+ declare function buildDeniedToolsRider(deniedToolNames: Iterable<string>): string;
8408
+ interface ConnectorGuidanceInput {
8409
+ /** The candidate tools for this turn (the final assembled set). */
8410
+ tools: ToolConfig[];
8411
+ /**
8412
+ * Logical provider codes connected for the user — the same codes used as
8413
+ * {@link TOOL_CATALOG} `provider` values (e.g. "gmail", "gcalendar",
8414
+ * "gdrive", "notion", "github", "dropbox").
8415
+ */
8416
+ connectedProviders: string[];
8417
+ /** Exact SDK tool-name strings the user turned off ({@link TOOL_CATALOG} keys). */
8418
+ deniedToolNames: string[];
8419
+ }
8420
+ interface ConnectorGuidance {
8421
+ /** `input.tools` with any denied tool removed (the helper owns filtering). */
8422
+ tools: ToolConfig[];
8423
+ /** Compact system-prompt block, or "" when there's nothing actionable. */
8424
+ rider: string;
8670
8425
  }
8426
+ /**
8427
+ * Single helper both web and mobile use so neither re-implements connector
8428
+ * tool filtering or the accuracy messaging. It enforces denial (drops denied
8429
+ * tools from the turn's toolset) AND builds a compact rider letting the model
8430
+ * give an accurate message for each connector state instead of a generic
8431
+ * "I don't have access":
8432
+ *
8433
+ * 1. Toggled off — connected provider whose tool is denied → "turned off,
8434
+ * re-enable in Connected Apps".
8435
+ * 2a. Pruned / 2b. no tool — covered by a general line telling the model not to
8436
+ * claim no-access for a connected app. It rides along only when another
8437
+ * section is present (on its own it isn't actionable).
8438
+ * 3. Not connected — catalog provider absent from `connectedProviders` →
8439
+ * "connect it in Connected Apps".
8440
+ *
8441
+ * State 3 is derived from {@link TOOL_CATALOG}'s provider set — providers with
8442
+ * no tools (e.g. Disconnect-only Dropbox) aren't actionable via tools, so they
8443
+ * aren't surfaced here. Everything is sorted alphabetically for deterministic
8444
+ * output (no cache / snapshot churn). `rider` is "" when nothing is actionable
8445
+ * (every catalog provider connected and nothing denied).
8446
+ */
8447
+ declare function buildConnectorGuidance(input: ConnectorGuidanceInput): ConnectorGuidance;
8671
8448
 
8672
8449
  interface StoredConversation {
8673
8450
  uniqueId: string;
@@ -8707,15 +8484,43 @@ interface LazyStoredConversation extends Omit<StoredConversation, "title"> {
8707
8484
  }
8708
8485
 
8709
8486
  /**
8710
- * Memory Retrieval Embeddings
8487
+ * Pluggable logger for the Anuma SDK.
8488
+ *
8489
+ * By default all SDK logging goes to `console`. Call {@link setLogger} at app
8490
+ * init (or use `<LoggerProvider>` in React) to redirect output to your own
8491
+ * logging infrastructure (PostHog, Datadog, Sentry, etc.).
8492
+ *
8493
+ * @example
8494
+ * ```ts
8495
+ * import { setLogger, type Logger } from "@anuma/sdk";
8496
+ *
8497
+ * const myLogger: Logger = {
8498
+ * debug: () => {},
8499
+ * info: (...args) => posthog.capture("sdk_info", { message: args }),
8500
+ * warn: (...args) => console.warn("[SDK]", ...args),
8501
+ * error: (...args) => Sentry.captureMessage(args.join(" ")),
8502
+ * };
8711
8503
  *
8712
- * Message-level embedding + persistence helpers: find messages via the
8713
- * WatermelonDB-backed `db/chat/operations`, embed their content, and store the
8714
- * vectors/chunks back. The raw "text → embedding" calls live in `./generate`
8715
- * (dependency-free so the tool-selection engine can import them without the DB
8716
- * layer) and are re-exported here so existing importers of
8717
- * `../memoryEngine/embeddings` and the `../memoryEngine` barrel keep working.
8504
+ * setLogger(myLogger);
8505
+ * ```
8718
8506
  */
8507
+ interface Logger {
8508
+ debug: (...args: unknown[]) => void;
8509
+ /** Used for outcomes that are noteworthy but expected — a consolidation
8510
+ * refusal that preserved a memory, for example. Keep genuinely-wrong things at
8511
+ * `warn` so the two stay distinguishable in a log search. */
8512
+ info: (...args: unknown[]) => void;
8513
+ warn: (...args: unknown[]) => void;
8514
+ error: (...args: unknown[]) => void;
8515
+ }
8516
+ /** Default logger that delegates to the global `console` object. */
8517
+ declare const consoleLogger: Logger;
8518
+ /** Silent logger that discards all output. */
8519
+ declare const noopLogger: Logger;
8520
+ /** Replace the active SDK logger. Pass {@link consoleLogger} to restore defaults. */
8521
+ declare function setLogger(logger: Logger): void;
8522
+ /** Return the active SDK logger. */
8523
+ declare function getLogger(): Logger;
8719
8524
 
8720
8525
  /**
8721
8526
  * An ordinary message whose chunk vectors were built over `enc:v3:` ciphertext
@@ -8824,42 +8629,6 @@ declare function dequantizeEmbedding({ data, scale }: QuantizedEmbedding): Float
8824
8629
  */
8825
8630
  declare function cosineInt8(a: Int8Array, scaleA: number, b: Int8Array, scaleB: number): number;
8826
8631
 
8827
- /**
8828
- * Storage encoding for `MessageChunk.vector`.
8829
- *
8830
- * Chunk vectors are persisted inside the `chunks` column as JSON. Since #732
8831
- * their values are canonically float32 — `generateEmbeddings` round-trips its
8832
- * return value through a `Float32Array` so a cache hit and a cache miss agree —
8833
- * which paradoxically made the JSON LONGER: `JSON.stringify` emits the shortest
8834
- * decimal that round-trips to the float64, and a binary32-exact value needs ~17
8835
- * significant digits. Measured at 4096 dims (`qwen/qwen3-embedding-8b`): 54.5 KB
8836
- * of JSON before #732, 84.5 KB after.
8837
- *
8838
- * Base64-encoding the `Float32Array` stores the same bits as 4 bytes per value
8839
- * instead of ~21 characters — 21.3 KB at 4096 dims. Because the data already IS
8840
- * float32, the encoding is EXACTLY lossless, so the round-trip assertion is
8841
- * equality and never a tolerance.
8842
- *
8843
- * Rollout (sdk#862). `decodeChunkVector` accepts both encodings and ships first;
8844
- * `encodeChunkVector` is deliberately NOT wired into the write path yet. Every
8845
- * stored chunk on every device is still a JSON array, and chat history syncs
8846
- * those rows between devices verbatim — the backup push spreads the whole raw
8847
- * row and the restore copies every column — so a device on an older build would
8848
- * meet a base64 string where it expects `number[]`. It does not throw, and it
8849
- * does not fall back: the row still HAS chunks, so the old build takes the chunk
8850
- * branch and never reaches the whole-message one. `Float32Array.from` over the
8851
- * string yields one NaN per character, `cosineSimilarity` returns 0 on the
8852
- * dimension mismatch, and the default `minSimilarity` cuts it — the row leaves
8853
- * search entirely. The old build then caches what it built: ~87 KB of NaN per
8854
- * chunk, so ~54 MB resident for a 620-chunk row, on exactly the device this
8855
- * sequencing exists to protect. The writer flips in a later release, once a
8856
- * build that reads both encodings has saturated; the single flip site is
8857
- * `updateMessageChunksOp`.
8858
- *
8859
- * Byte order is the platform's, since this reads the `Float32Array` bytes
8860
- * directly. Every runtime the SDK targets (browsers, iOS, Android, Node on
8861
- * x86/ARM) is little-endian.
8862
- */
8863
8632
  /**
8864
8633
  * Encode an embedding as base64 float32 for storage.
8865
8634
  *
@@ -8867,73 +8636,150 @@ declare function cosineInt8(a: Int8Array, scaleA: number, b: Int8Array, scaleB:
8867
8636
  * narrowed to float32 first, which is lossless for anything `generateEmbeddings`
8868
8637
  * produced and is what makes the decode byte-exact.
8869
8638
  */
8870
- declare function encodeChunkVector(vector: ArrayLike<number>): string;
8871
- /**
8872
- * Read a stored chunk vector in either encoding: a base64 float32 string, or the
8873
- * legacy `number[]` that every row written before the writer flip still holds.
8874
- *
8875
- * Returns a zero-length `Float32Array` for a missing, empty, or unreadable
8876
- * value, which callers already treat as "this chunk has no vector" — the same
8877
- * degradation a malformed `chunks` JSON gets today, rather than a throw that
8878
- * would take down a whole search pass over one bad row.
8879
- *
8880
- * That one return value covers two different situations, and a caller cannot
8881
- * tell them apart: a chunk that legitimately has no vector, and a chunk whose
8882
- * vector is corrupt. `onMalformed` separates them. It fires only on the corrupt
8883
- * paths, never on an absent or empty value, so a caller can count corruption
8884
- * without counting normal empties.
8885
- *
8886
- * Nothing is logged here. A corrupt row holds hundreds of chunks and this runs
8887
- * once per chunk, so the caller aggregates and reports once per pass — the shape
8888
- * `searchChunksOp` already uses for stale-model vectors.
8889
- */
8890
- declare function decodeChunkVector(vector: number[] | string | null | undefined, onMalformed?: () => void): Float32Array<ArrayBuffer>;
8891
-
8892
- /**
8893
- * Value of the `origin` column on a row the SDK synthesised for a turn's tool results (v44, #866).
8894
- *
8895
- * `satisfies MessageOrigin` so this constant and the column's union cannot drift apart silently.
8896
- */
8897
- declare const TOOL_RESULT_ORIGIN = "tool_result";
8898
-
8899
- /**
8900
- * True when a text part is the attachment-contents part built by {@link buildAttachedFilesText}.
8901
- *
8902
- * Matches the whole generated shape — opening tag plus the fixed header line, and the closing
8903
- * tag — not just the tag, so user text that merely starts with `<attached_files>` is still
8904
- * treated as the user's prompt.
8905
- */
8906
- declare function isAttachedFilesText(text: string | undefined | null): boolean;
8907
- /**
8908
- * Wrap a turn's extracted file contents (the `[Extracted content from <name>]`
8909
- * blocks `preprocessFiles` emits) in the tagged part that rides on that turn's
8910
- * user message.
8911
- */
8912
- declare function buildAttachedFilesText(fileContext: string): string;
8639
+ declare function encodeChunkVector(vector: ArrayLike<number>): string;
8913
8640
  /**
8914
- * Put the current turn's extracted attachment contents on the last user
8915
- * message, as a text part inserted right after that message's LAST text part:
8916
- * after everything the user wrote, and before any parts that follow it
8917
- * (typically images). With interleaved parts such as `[text, image, text,
8918
- * image]` it lands between the second text and the second image.
8641
+ * Read a stored chunk vector in either encoding: a base64 float32 string, or the
8642
+ * legacy `number[]` that every row written before the writer flip still holds.
8919
8643
  *
8920
- * Why not a system message: a detached system message at the front of the
8921
- * request ("the user has attached files to this conversation") is separated
8922
- * from the words that refer to it by the whole system prompt, tool catalog and
8923
- * history. In a long, multi-file conversation fast models stopped connecting
8924
- * the two and told users the attachment was unreadable even though its text
8925
- * was in the request. Keeping the contents on the turn that attached them is
8926
- * how native file attachments work on every provider, and survives the auto
8927
- * router switching models between turns.
8644
+ * Returns a zero-length `Float32Array` for a missing, empty, or unreadable
8645
+ * value, which callers already treat as "this chunk has no vector" — the same
8646
+ * degradation a malformed `chunks` JSON gets today, rather than a throw that
8647
+ * would take down a whole search pass over one bad row.
8928
8648
  *
8929
- * The part goes AFTER the user's own text because the portal's query
8930
- * classifier routes on the first text part of the last user message — it must
8931
- * keep reading the user's prompt, not the document.
8649
+ * That one return value covers two different situations, and a caller cannot
8650
+ * tell them apart: a chunk that legitimately has no vector, and a chunk whose
8651
+ * vector is corrupt. `onMalformed` separates them. It fires only on the corrupt
8652
+ * paths, never on an absent or empty value, so a caller can count corruption
8653
+ * without counting normal empties.
8932
8654
  *
8933
- * Returns a new array; the input is not mutated. When there is no user
8934
- * message the messages are returned unchanged.
8655
+ * Nothing is logged here. A corrupt row holds hundreds of chunks and this runs
8656
+ * once per chunk, so the caller aggregates and reports once per pass — the shape
8657
+ * `searchChunksOp` already uses for stale-model vectors.
8935
8658
  */
8936
- declare function attachFileContextToLastUserMessage(messages: LlmapiMessage[], fileContext: string): LlmapiMessage[];
8659
+ declare function decodeChunkVector(vector: number[] | string | null | undefined, onMalformed?: () => void): Float32Array<ArrayBuffer>;
8660
+
8661
+ /** A single section in the system prompt. */
8662
+ interface PromptSection {
8663
+ /** Unique identifier (used for disabling/overriding). */
8664
+ key: string;
8665
+ /** Lower priority = earlier in the prompt. Sections sort ascending. */
8666
+ priority: number;
8667
+ /** Return text to append, or null to skip this section. */
8668
+ render: (ctx: PromptContext) => string | null;
8669
+ }
8670
+ /** Context passed to every section renderer. */
8671
+ interface PromptContext {
8672
+ /** Base personality prompt (from PersonaConfig.prompt). */
8673
+ basePrompt: string;
8674
+ /** User-set "act as X" communication preference. */
8675
+ persona?: string | null;
8676
+ /** Analyzed user communication style profile. */
8677
+ styleProfile?: string | null;
8678
+ /** Detected user sentiment. */
8679
+ sentiment?: "frustrated" | "positive" | "neutral" | null;
8680
+ /** ISO 639-1 language code, 'other', or null for English/undetected. */
8681
+ preferredLanguage?: string | null;
8682
+ /** Platform-specific formatting instructions from PersonaConfig. */
8683
+ platformFormatting?: string | null;
8684
+ /** Available tools. */
8685
+ toolSummaries?: Array<{
8686
+ name: string;
8687
+ description: string;
8688
+ }>;
8689
+ /** Consumer-specific data for custom sections. */
8690
+ extra?: Record<string, unknown>;
8691
+ }
8692
+ /** Configurable template strings for built-in section renderers. */
8693
+ interface PromptTemplates {
8694
+ tools: string;
8695
+ persona: string;
8696
+ style: string;
8697
+ sentiment: {
8698
+ frustrated: string;
8699
+ positive: string;
8700
+ };
8701
+ language: string;
8702
+ languageGeneric: string;
8703
+ platformFormatting: string;
8704
+ }
8705
+ /** Options for buildSystemPrompt(). */
8706
+ interface BuildPromptOptions {
8707
+ /** Additional sections appended to defaults. */
8708
+ extraSections?: PromptSection[];
8709
+ /** Section keys to skip. */
8710
+ disabled?: string[];
8711
+ /** Max total prompt length in characters. 0 = unlimited. */
8712
+ maxLength?: number;
8713
+ /** Override default template strings. */
8714
+ templates?: Partial<PromptTemplates>;
8715
+ }
8716
+ /** Result of buildSystemPrompt(). */
8717
+ interface BuildPromptResult {
8718
+ /** The assembled prompt string. */
8719
+ prompt: string;
8720
+ /** Keys of sections that rendered (in order). */
8721
+ activeSections: string[];
8722
+ }
8723
+ /** Options for analyzeStyle(). */
8724
+ interface AnalyzeStyleOptions {
8725
+ /** Recent conversation messages. */
8726
+ messages: Array<{
8727
+ role: string;
8728
+ content: string;
8729
+ }>;
8730
+ /** Callback to send messages to an LLM and get a text response. */
8731
+ callLlm: (messages: Array<{
8732
+ role: string;
8733
+ content: string;
8734
+ }>) => Promise<string | null>;
8735
+ /** Max user messages to consider (default: 10). */
8736
+ maxInputMessages?: number;
8737
+ /** Minimum user messages required (default: 3). */
8738
+ minMessages?: number;
8739
+ /** Max profile length in characters (default: 200). */
8740
+ maxProfileLength?: number;
8741
+ /** Custom system prompt for the analysis model. */
8742
+ analysisPrompt?: string;
8743
+ /** Regex to filter out document-embedded messages. */
8744
+ documentPattern?: RegExp;
8745
+ }
8746
+ /** Result of analyzeStyle(). */
8747
+ interface AnalyzeStyleResult {
8748
+ /** The style profile string, or null if skipped/failed. */
8749
+ profile: string | null;
8750
+ }
8751
+ /** Input for shouldAnalyzeStyle(). */
8752
+ interface StyleAnalysisSchedule {
8753
+ /** Current message count for the user. */
8754
+ messageCount: number;
8755
+ /** Whether the user already has a style profile. */
8756
+ hasProfile: boolean;
8757
+ /** Whether analysis has ever run (updatedAt timestamp is set). */
8758
+ hasBeenAnalyzed: boolean;
8759
+ /** Whether the user opted out (updatedAt set but profile is null). */
8760
+ optedOut: boolean;
8761
+ /** Trigger analysis after this many messages (default: 5). */
8762
+ analyzeAfterMessages?: number;
8763
+ /** Re-analyze every N messages (default: 20). */
8764
+ refreshEveryMessages?: number;
8765
+ }
8766
+
8767
+ declare const DEFAULT_ANALYSIS_PROMPT = "Analyze these user messages and describe their communication style in 1-2 concise sentences. Focus on: tone (formal/casual/playful), message length preference, vocabulary level, and any notable patterns. Output only the style description, nothing else.";
8768
+ declare const DEFAULT_MAX_INPUT_MESSAGES = 10;
8769
+ declare const DEFAULT_MIN_MESSAGES = 3;
8770
+ declare const DEFAULT_MAX_PROFILE_LENGTH = 200;
8771
+ declare const DEFAULT_ANALYZE_AFTER_MESSAGES = 5;
8772
+ declare const DEFAULT_REFRESH_EVERY_MESSAGES = 20;
8773
+ declare const DEFAULT_DOCUMENT_PATTERN: RegExp;
8774
+ declare function shouldAnalyzeStyle(schedule: StyleAnalysisSchedule): boolean;
8775
+ declare function analyzeStyle(options: AnalyzeStyleOptions): Promise<AnalyzeStyleResult>;
8776
+
8777
+ declare const DEFAULT_PROMPT_TEMPLATES: PromptTemplates;
8778
+ /** Collapse runs of 3+ double-quotes to a single quote to prevent prompt injection. */
8779
+ declare function sanitizeQuotes(s: string): string;
8780
+ /** Replace {content} in a template with sanitized value. Returns null if value is falsy. */
8781
+ declare function renderTemplate(value: string | null | undefined, template: string): string | null;
8782
+ declare function buildSystemPrompt(ctx: PromptContext, options?: BuildPromptOptions): BuildPromptResult;
8937
8783
 
8938
8784
  /**
8939
8785
  * One line per file the model did NOT get, saying why — e.g.
@@ -8948,71 +8794,85 @@ declare function formatFileProcessingNotes(statuses: FileProcessingStatus[], opt
8948
8794
  }): string | null;
8949
8795
 
8950
8796
  /**
8951
- * Lazy decryption helpers for conversation titles.
8952
- *
8953
- * Today the SDK eagerly decrypts every conversation title returned from
8954
- * `getConversationsOp` so callers can render them immediately. For users
8955
- * with hundreds or thousands of conversations this means hundreds of
8956
- * plaintext title strings sitting in client RAM at all times — a
8957
- * non-trivial memory cost on long-lived tabs, and an unnecessary one
8958
- * since only the visible rows actually need plaintext.
8797
+ * Idle-keepalive wrapper for SSE streams produced by the generated
8798
+ * SSE client in `src/client/core/serverSentEvents.gen.ts`.
8959
8799
  *
8960
- * `decryptConversationTitle` is the opt-in alternative. Callers pair it
8961
- * with the lazy `listConversationsLazy` operation (see operations.ts) to
8962
- * keep encrypted titles in their store and decrypt only on row visibility
8963
- * (e.g. inside an IntersectionObserver callback or a virtualized list's
8964
- * `renderItem`).
8800
+ * The generated SSE client does not enforce an idle timeout, so if the
8801
+ * upstream server dies without sending `FIN`, a consumer can sit forever
8802
+ * on a half-open socket. This wrapper:
8965
8803
  *
8966
- * The helper is a thin wrapper over `decryptField` plus:
8967
- * - a small bounded LRU so a row that briefly leaves and re-enters the
8968
- * viewport doesn't trigger a fresh decrypt;
8969
- * - in-flight dedupe so concurrent calls for the same encrypted title
8970
- * run a single decrypt rather than racing the same WebCrypto call;
8971
- * - a hard error when the encryption key isn't loaded — `decryptField`
8972
- * itself silently returns the ciphertext on key-missing, which is
8973
- * useful for backwards-compatible reads but actively misleading for
8974
- * a lazy display path (the UI would render `enc:v3:...` as the title).
8804
+ * - Tracks the timestamp of the last event yielded by the source.
8805
+ * - Starts an idle timer (default 60s, configurable via `idleMs`).
8806
+ * - Resets the timer on every event.
8807
+ * - Invokes `onIdle()` if the timer fires, and then closes the stream
8808
+ * by throwing a typed {@link SseIdleTimeoutError} out of the iterator.
8975
8809
  *
8976
- * The LRU is cleared on every `clearAllEncryptionState()` so a shared
8977
- * browser can't leak the previous user's titles after logout.
8810
+ * This file lives outside `src/client/` on purpose: `@hey-api/openapi-ts`
8811
+ * wipes its output directory on every regeneration, so anything inside
8812
+ * `src/client/` would be clobbered by `pnpm run generate`.
8978
8813
  */
8979
8814
  /**
8980
- * Drop every cached plaintext title and pending decrypt promise.
8981
- *
8982
- * Wired into `clearAllEncryptionState()` via the listener registry in
8983
- * `useEncryption.ts`. Also exported so consumers can clear the cache
8984
- * proactively (e.g. on wallet switch within a session before the
8985
- * full encryption-state teardown lands).
8815
+ * Options accepted by {@link withSseKeepalive}.
8986
8816
  */
8987
- declare function clearLazyTitleCache(): void;
8817
+ interface SseKeepaliveOptions {
8818
+ /**
8819
+ * Idle window, in milliseconds. If no event is yielded by the source for
8820
+ * this long, the stream is considered dead and is torn down.
8821
+ *
8822
+ * Set to `0` or a negative number to disable the idle timer (in which
8823
+ * case the wrapper is a straight passthrough).
8824
+ *
8825
+ * @default 60000
8826
+ */
8827
+ idleMs?: number;
8828
+ /**
8829
+ * Invoked when the idle timer fires. The callback runs before the
8830
+ * wrapper closes the stream, so consumers can log, emit metrics, or
8831
+ * attempt reconnects from here.
8832
+ *
8833
+ * The callback is best-effort — thrown errors are swallowed so they do
8834
+ * not mask the {@link SseIdleTimeoutError} that follows.
8835
+ */
8836
+ onIdle?: (info: {
8837
+ idleMs: number;
8838
+ lastEventAt: number;
8839
+ }) => void;
8840
+ }
8988
8841
  /**
8989
- * Decrypt a single conversation title on demand.
8842
+ * Thrown by {@link withSseKeepalive} when no event arrives within the
8843
+ * configured idle window. Callers can `instanceof`-check this to
8844
+ * distinguish idle timeouts from other stream errors.
8845
+ */
8846
+ declare class SseIdleTimeoutError extends Error {
8847
+ readonly idleMs: number;
8848
+ readonly lastEventAt: number;
8849
+ constructor(idleMs: number, lastEventAt: number);
8850
+ }
8851
+ /**
8852
+ * Wrap an SSE-style async iterable with an idle timeout. If no value is
8853
+ * yielded by `source` within `idleMs`, `onIdle` fires and the wrapper
8854
+ * throws {@link SseIdleTimeoutError}, which propagates to the consumer's
8855
+ * `for await` loop and, via the iterator protocol, calls `source.return()`
8856
+ * so the underlying fetch/reader is cancelled.
8990
8857
  *
8991
- * Designed for the lazy display path: pair with `listConversationsLazy`
8992
- * and call this once a row is actually visible.
8858
+ * Typical usage with the generated SSE client:
8993
8859
  *
8994
- * Behavior:
8995
- * - Plaintext input (no `enc:` prefix) is returned unchanged. This
8996
- * covers conversations created before encryption was enabled and
8997
- * keeps the helper safe to call unconditionally from rendering
8998
- * code that may receive a mix of encrypted and plaintext titles.
8999
- * - Encrypted input is decrypted via `decryptField`, which uses the
9000
- * same per-version cached `CryptoKey` as the eager path — no new
9001
- * key derivations are triggered.
9002
- * - Concurrent calls for the same `(address, encryptedTitle)` share
9003
- * a single decrypt promise.
9004
- * - The result is memoized in a 256-entry LRU.
8860
+ * ```ts
8861
+ * import { createSseClient } from "@anuma/sdk/client/core/serverSentEvents.gen";
8862
+ * import { withSseKeepalive } from "@anuma/sdk";
9005
8863
  *
9006
- * Throws if the encryption key for `address` isn't loaded. (The
9007
- * underlying `decryptField` would otherwise silently return the
9008
- * ciphertext, which would surface to the UI as a literal `enc:v3:...`
9009
- * title — strictly worse than a thrown error the caller can catch.)
8864
+ * const { stream } = createSseClient({ url });
8865
+ * for await (const event of withSseKeepalive(stream, {
8866
+ * idleMs: 60_000,
8867
+ * onIdle: ({ idleMs }) => logger.warn(`SSE idle for ${idleMs}ms`),
8868
+ * })) {
8869
+ * // ...
8870
+ * }
8871
+ * ```
9010
8872
  *
9011
- * @param encryptedTitle - The stored title. May be ciphertext or plaintext.
9012
- * @param address - Wallet address that owns the encryption key.
9013
- * @returns The decrypted plaintext title.
8873
+ * The wrapper is a pure passthrough when `idleMs <= 0`.
9014
8874
  */
9015
- declare function decryptConversationTitle(encryptedTitle: string, address: string): Promise<string>;
8875
+ declare function withSseKeepalive<T>(source: AsyncIterable<T>, options?: SseKeepaliveOptions): AsyncGenerator<T, void, unknown>;
9016
8876
 
9017
8877
  /**
9018
8878
  * Class-B task types — the vocabulary of the `X-Anuma-Task-Type` header.