@savantoai/ai-sdk 3.0.0 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -30,6 +30,71 @@ under semver, meaning:
30
30
  caret update. Bumping that range is itself a major-version event for the
31
31
  SDK.
32
32
 
33
+ ## Unreleased
34
+
35
+ ### Breaking — streaming chat protocol
36
+
37
+ > The SDK's TypeScript surface is unchanged, but the **wire protocol** for
38
+ > the `/chat` streaming endpoint has been rewritten. Any client code that
39
+ > switches on `chunk.type` (including code generated against earlier
40
+ > versions of this README's example) must be migrated. The SDK is not
41
+ > under semver for the wire protocol — see _Versioning policy_ above —
42
+ > but we're flagging this prominently because real client switches will
43
+ > silently stop matching after this rollout.
44
+
45
+ The `/chat` streaming protocol has been rewritten to a block-based shape.
46
+ The legacy `text_delta`, `product`, `post`, `post_delta`, and `bubble_termination`
47
+ chunk types are gone; the new types are `block_start`, `block_delta`, `block_data`,
48
+ `item_delta`, and `block_end`, plus existing flow-control chunks (`progress`,
49
+ `prompts`, `metadata`, `complete`, `error`, `domain_offer`, `domain_offer_success`,
50
+ `agent_status`, `handoff_ended`, `analytics`, `usage`).
51
+
52
+ Migration cheat-sheet for code switching on `chunk.type`:
53
+
54
+ | Old chunk type | New chunk type |
55
+ | --------------------- | -------------------------------------------------------------- |
56
+ | `text_delta` | `block_delta` (read `chunk.data.content`) |
57
+ | `product` / `post` | `block_data` (full payload) + `block_start` (ordering) |
58
+ | `post_delta` | `item_delta` (read `chunk.data.field` + `chunk.data.delta`) |
59
+ | `bubble_termination` | (removed — single message bubble per turn now) |
60
+
61
+ The SDK's exported `ChatStreamChunk` type is intentionally unchanged
62
+ (`{ type: string; data?: unknown }`) — no SDK regen is required. See
63
+ the [Streaming Chat docs](https://savanto.ai/docs/developers/streaming)
64
+ for the full chunk reference and a worked React example.
65
+
66
+ The streaming `chat.message_sent` webhook payload has also picked up
67
+ `responseMessage.respondedBy` (which domain answered) and
68
+ `responseMessage.composedBlocks` (a tiny `{ type, ids }[]` describing
69
+ the structured blocks the composer wove together for multi-domain answers).
70
+ See the [Webhooks docs](https://savanto.ai/docs/developers/webhooks#chatmessage_sent).
71
+
72
+ ## 3.1.0 — 2026-05-19
73
+
74
+ ### Added
75
+
76
+ - Search responses may include `query.truncated` and `query.originalLength` on
77
+ product and post search when the input text was shortened server-side.
78
+
79
+ ### Changed
80
+
81
+ - **`POST /recommendations`** response type now uses the standard
82
+ `{ object, requestId, data }` envelope (`object: "recommendations"`).
83
+ Payload fields remain under `data`.
84
+ - **OpenAPI alignment** for non-streaming `POST /chat` and `POST /threads/search`
85
+ (documented envelopes; chat wire format unchanged if you already read `data`).
86
+ - **`POST /threads/search`** list field is `data.items` in generated types (matches
87
+ the handler; replaces any `results` naming in older spec snapshots).
88
+
89
+ ### Breaking (TypeScript)
90
+
91
+ - **`Product.stockStatus` and `Product.type`** are strict enums. Values outside
92
+ `instock | outofstock | onbackorder` and `simple | grouped | external | variable`
93
+ are omitted at runtime and absent from the type.
94
+ - Any client code that assumed recommendations returned only `{ data: T }` at the
95
+ top level should use the generated `PostRecommendationsResponse` type (includes
96
+ `object` and `requestId`).
97
+
33
98
  ## 3.0.0 — 2026-05-12
34
99
 
35
100
  ### Breaking (TypeScript)
package/README.md CHANGED
@@ -114,7 +114,12 @@ const { data, response } = await chat({
114
114
  throwOnError: true,
115
115
  });
116
116
 
117
- // For streaming (NDJSON), read the response body directly:
117
+ // For streaming (NDJSON), read the response body directly. The protocol is
118
+ // block-based: text streams as `block_delta` chunks inside an active text
119
+ // block, structured items arrive as `block_data` keyed by item id, and
120
+ // per-field streaming (post summaries, product reasons, etc.) flows through
121
+ // `item_delta`. See https://savanto.ai/docs/developers/streaming for the
122
+ // full chunk-type reference.
118
123
  const reader = response.body!.getReader();
119
124
  const decoder = new TextDecoder();
120
125
 
@@ -124,7 +129,7 @@ while (true) {
124
129
  const lines = decoder.decode(value).split('\n').filter(Boolean);
125
130
  for (const line of lines) {
126
131
  const chunk = JSON.parse(line);
127
- if (chunk.type === 'text_delta') process.stdout.write(chunk.data);
132
+ if (chunk.type === 'block_delta') process.stdout.write(chunk.data.content);
128
133
  }
129
134
  }
130
135
  ```
package/dist/index.cjs CHANGED
@@ -325,8 +325,8 @@ const checkForExistence = (options, name) => {
325
325
  if (options.headers.has(name) || options.query?.[name] || options.headers.get("Cookie")?.includes(`${name}=`)) return true;
326
326
  return false;
327
327
  };
328
- const setAuthParams = async ({ security, ...options }) => {
329
- for (const auth of security) {
328
+ async function setAuthParams(options) {
329
+ for (const auth of options.security ?? []) {
330
330
  if (checkForExistence(options, auth.name)) continue;
331
331
  const token = await getAuthToken(auth, options.auth);
332
332
  if (!token) continue;
@@ -344,7 +344,7 @@ const setAuthParams = async ({ security, ...options }) => {
344
344
  break;
345
345
  }
346
346
  }
347
- };
347
+ }
348
348
  const buildUrl = (options) => getUrl({
349
349
  baseUrl: options.baseUrl,
350
350
  path: options.path,
@@ -454,10 +454,7 @@ const createClient = (config = {}) => {
454
454
  headers: mergeHeaders(_config.headers, options.headers),
455
455
  serializedBody: void 0
456
456
  };
457
- if (opts.security) await setAuthParams({
458
- ...opts,
459
- security: opts.security
460
- });
457
+ if (opts.security) await setAuthParams(opts);
461
458
  if (opts.requestValidator) await opts.requestValidator(opts);
462
459
  if (opts.body !== void 0 && opts.bodySerializer) opts.serializedBody = opts.bodySerializer(opts.body);
463
460
  if (opts.body === void 0 || opts.serializedBody === "") opts.headers.delete("Content-Type");