@savantoai/ai-sdk 2.1.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,206 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@savantoai/ai-sdk` will be documented in this file.
4
+
5
+ The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and we follow [semantic versioning](https://semver.org/) — see _Versioning policy_
7
+ below for the one nuance that applies to a generated SDK.
8
+
9
+ ## Versioning policy
10
+
11
+ This SDK is fully regenerated from `cloud/openapi.json` by
12
+ [`@hey-api/openapi-ts`](https://github.com/hey-api/openapi-ts). Generator and
13
+ schema changes can _both_ produce TypeScript-only breaking changes (e.g. a
14
+ field gains `| null`, a `body` shape narrows, a top-level helper type
15
+ widens) without changing any runtime behaviour. We treat those as breaking
16
+ under semver, meaning:
17
+
18
+ - A change that **narrows** a previously-accepted type (e.g. record →
19
+ concrete interface) is a major version bump.
20
+ - A change that **widens** a previously-non-nullable type to include `null`
21
+ is a major version bump.
22
+ - Generator-driven changes to top-level re-exports (`Client`, `Config`,
23
+ `Options`, `RequestResult`, `RequestOptions`) are major version bumps.
24
+ - Pure additions (new endpoints, new optional fields on requests,
25
+ newly-exported types) are minor.
26
+ - Internal regen with no public-surface diff is patch.
27
+
28
+ `@hey-api/openapi-ts` is pinned to `~0.97.x` (patch range only) in
29
+ `package.json` so generator-driven type narrowings can't slip in via a
30
+ caret update. Bumping that range is itself a major-version event for the
31
+ SDK.
32
+
33
+ ## 3.1.0 — 2026-05-19
34
+
35
+ ### Added
36
+
37
+ - Search responses may include `query.truncated` and `query.originalLength` on
38
+ product and post search when the input text was shortened server-side.
39
+
40
+ ### Changed
41
+
42
+ - **`POST /recommendations`** response type now uses the standard
43
+ `{ object, requestId, data }` envelope (`object: "recommendations"`).
44
+ Payload fields remain under `data`.
45
+ - **OpenAPI alignment** for non-streaming `POST /chat` and `POST /threads/search`
46
+ (documented envelopes; chat wire format unchanged if you already read `data`).
47
+ - **`POST /threads/search`** list field is `data.items` in generated types (matches
48
+ the handler; replaces any `results` naming in older spec snapshots).
49
+
50
+ ### Breaking (TypeScript)
51
+
52
+ - **`Product.stockStatus` and `Product.type`** are strict enums. Values outside
53
+ `instock | outofstock | onbackorder` and `simple | grouped | external | variable`
54
+ are omitted at runtime and absent from the type.
55
+ - Any client code that assumed recommendations returned only `{ data: T }` at the
56
+ top level should use the generated `PostRecommendationsResponse` type (includes
57
+ `object` and `requestId`).
58
+
59
+ ## Unreleased
60
+
61
+ ### Breaking — streaming chat protocol
62
+
63
+ > The SDK's TypeScript surface is unchanged, but the **wire protocol** for
64
+ > the `/chat` streaming endpoint has been rewritten. Any client code that
65
+ > switches on `chunk.type` (including code generated against earlier
66
+ > versions of this README's example) must be migrated. The SDK is not
67
+ > under semver for the wire protocol — see _Versioning policy_ above —
68
+ > but we're flagging this prominently because real client switches will
69
+ > silently stop matching after this rollout.
70
+
71
+ The `/chat` streaming protocol has been rewritten to a block-based shape.
72
+ The legacy `text_delta`, `product`, `post`, `post_delta`, and `bubble_termination`
73
+ chunk types are gone; the new types are `block_start`, `block_delta`, `block_data`,
74
+ `item_delta`, and `block_end`, plus existing flow-control chunks (`progress`,
75
+ `prompts`, `metadata`, `complete`, `error`, `domain_offer`, `domain_offer_success`,
76
+ `agent_status`, `handoff_ended`, `analytics`, `usage`).
77
+
78
+ Migration cheat-sheet for code switching on `chunk.type`:
79
+
80
+ | Old chunk type | New chunk type |
81
+ | --------------------- | -------------------------------------------------------------- |
82
+ | `text_delta` | `block_delta` (read `chunk.data.content`) |
83
+ | `product` / `post` | `block_data` (full payload) + `block_start` (ordering) |
84
+ | `post_delta` | `item_delta` (read `chunk.data.field` + `chunk.data.delta`) |
85
+ | `bubble_termination` | (removed — single message bubble per turn now) |
86
+
87
+ The SDK's exported `ChatStreamChunk` type is intentionally unchanged
88
+ (`{ type: string; data?: unknown }`) — no SDK regen is required. See
89
+ the [Streaming Chat docs](https://savanto.ai/docs/developers/streaming)
90
+ for the full chunk reference and a worked React example.
91
+
92
+ The streaming `chat.message_sent` webhook payload has also picked up
93
+ `responseMessage.respondedBy` (which domain answered) and
94
+ `responseMessage.composedBlocks` (a tiny `{ type, ids }[]` describing
95
+ the structured blocks the composer wove together for multi-domain answers).
96
+ See the [Webhooks docs](https://savanto.ai/docs/developers/webhooks#chatmessage_sent).
97
+
98
+ ## 3.0.0 — 2026-05-12
99
+
100
+ ### Breaking (TypeScript)
101
+
102
+ - **`POST /threads/search` `sortBy` enum renamed.** Previously the SDK
103
+ emitted `'timestamp' | 'message_count' | 'token_count'`, but the
104
+ server-side handler routed those names through to OpenSearch
105
+ unchanged — and OpenSearch indexes those fields as `messageCount`
106
+ and `tokenCount`, so the snake_case values produced an invalid
107
+ sort clause that callers either silently ignored or got a 500 on.
108
+ The schema now matches the OpenSearch field names:
109
+ `'timestamp' | 'messageCount' | 'tokenCount'`.
110
+ **Migration**: switch `sortBy: 'message_count'` →
111
+ `sortBy: 'messageCount'`, `'token_count'` → `'tokenCount'`.
112
+
113
+ ### Other
114
+
115
+ - `UpdateMcpConfigRequest` body has been reopened server-side to
116
+ accept arbitrary string-URL fields (`catchall(z.string().url())`),
117
+ so the generated SDK type now allows forward-compatible MCP URI
118
+ keys (e.g. a future `crmUri`) without an SDK regen. This restores
119
+ some of the pre-2.2.0 surface flagged in the 2.2.0 section below,
120
+ but with per-field URL/length validation instead of `passthrough()`.
121
+
122
+ ## 2.2.0 — 2026-05-07
123
+
124
+ This release was published as a minor bump but **contains TypeScript-level
125
+ breaking changes** that, per the policy above, should have been a major
126
+ (`3.0.0`). It is documented here retroactively so consumers know to add
127
+ null-guards / update destructuring patterns when upgrading from `2.1.x`.
128
+ The next regeneration will be released as `3.0.0`.
129
+
130
+ ### Breaking (TypeScript)
131
+
132
+ - **~30 fields gained `| null`.** The cloud OpenAPI emitter switched from
133
+ 3.0 (`app.doc()`) to 3.1 (`app.doc31()`), so fields whose Zod schema
134
+ uses `.nullable()` now serialise as `type: [..., 'null']`, and the
135
+ generator faithfully reflects that with `T | null`. Affected fields
136
+ include:
137
+ - `ErrorResponse.error.param` (`string` → `string | null`)
138
+ - `Pagination.total` / `Pagination.cursor` (`number | null`,
139
+ `string | null`) — note that `total` lives on every paginated list
140
+ response in the SDK.
141
+ - `Product.price`, `Product.salePrice`, `Product.priceMin`,
142
+ `Product.priceMax`, `Product.weight`, `Product.dimensions`,
143
+ `Product.image`, `Product.attributes`, `Product.rating`, and their
144
+ `ProductInput.*` counterparts.
145
+ - `Post.publishedAt`, `Post.featuredImage`, `Post.attributes`.
146
+ - `TenantStatus.publishableKey`.
147
+
148
+ Consumer code like `product.price.toFixed(2)` or
149
+ `if (pagination.total > 0)` will fail TypeScript strict-mode typecheck.
150
+ **Migration**: add nullish guards or use `??` defaults:
151
+
152
+ ```ts
153
+ product.price?.toFixed(2);
154
+ if ((pagination.total ?? 0) > 0) { /* … */ }
155
+ ```
156
+
157
+ - **Five admin/tenant request bodies narrowed from open-record to concrete
158
+ interface.** Cloud PR #138 replaced a `genericBody` Zod schema with six
159
+ typed request schemas. The regenerated SDK reflects this in:
160
+ - `UpdateWorkspaceData.body` → `UpdateWorkspaceRequest`
161
+ - `GetUploadUrlData.body` → `GetUploadUrlRequest`
162
+ - `UpdateMcpConfigData.body` → `UpdateMcpConfigRequest` _(reopened
163
+ server-side after audit #153 — see Unreleased)_
164
+ - `StoreCredentialsData.body` → `StoreLiveAgentCredentialsRequest`
165
+ - `UpdateLiveAgentScheduleData.body` →
166
+ `UpdateLiveAgentScheduleRequest`
167
+
168
+ Callers that were passing extra forward-compatible fields will fail
169
+ TypeScript typecheck. **Migration**: drop the extra fields — the cloud
170
+ was always ignoring them; the type just no longer admits them. (The
171
+ one exception, `updateMcpConfig`, has been reopened server-side; see
172
+ _Unreleased_.)
173
+
174
+ - **`RequestResult.request` / `response` made optional.** Generator bump
175
+ `@hey-api/openapi-ts` `0.94.x` → `0.97.x` changed the success branch of
176
+ `RequestResult` from `& { request: Request; response: Response }` to
177
+ `& { request?: Request; response?: Response }`. The change reflects
178
+ that `request` / `response` can be `undefined` if the failure happened
179
+ before the network call (DNS, request-building, etc.). Consumer code
180
+ like `const { data, response } = await chat(...); response.body` will
181
+ fail strict typecheck. **Migration** — either:
182
+
183
+ ```ts
184
+ // Option A — guard with `if (response)`:
185
+ const { data, response } = await chat({ client, body: { … } });
186
+ if (response) { /* response.body, response.status, etc. */ }
187
+
188
+ // Option B — opt in to `throwOnError`, which keeps response non-optional:
189
+ const { data, response } = await chat({
190
+ client,
191
+ body: { … },
192
+ throwOnError: true,
193
+ });
194
+ response.body; // typed as `Response`, not `Response | undefined`
195
+ ```
196
+
197
+ ### Other notes
198
+
199
+ - `@hey-api/openapi-ts` was bumped from `^0.94.x` to `^0.97.1` as part of
200
+ this release. Subsequent versions pin to `~0.97.x` to avoid further
201
+ silent generator-driven type narrowings via caret updates.
202
+
203
+ ## 2.1.0 and earlier
204
+
205
+ No formal changelog kept prior to 2.2.0. The git history (`sdks/javascript/`)
206
+ is the source of truth for older releases.
package/README.md CHANGED
@@ -101,6 +101,9 @@ const { data: ids } = await listProductIds({ client });
101
101
  ```typescript
102
102
  import { chat } from '@savantoai/ai-sdk';
103
103
 
104
+ // `throwOnError: true` makes `response` non-optional so `response.body` is
105
+ // safe to read directly. Without it (since 2.2.0) `response` is typed as
106
+ // `Response | undefined`. See CHANGELOG.md for details.
104
107
  const { data, response } = await chat({
105
108
  client,
106
109
  body: {
@@ -108,9 +111,15 @@ const { data, response } = await chat({
108
111
  threadId: 'thread-1',
109
112
  stream: true,
110
113
  },
114
+ throwOnError: true,
111
115
  });
112
116
 
113
- // 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.
114
123
  const reader = response.body!.getReader();
115
124
  const decoder = new TextDecoder();
116
125
 
@@ -120,7 +129,7 @@ while (true) {
120
129
  const lines = decoder.decode(value).split('\n').filter(Boolean);
121
130
  for (const line of lines) {
122
131
  const chunk = JSON.parse(line);
123
- if (chunk.type === 'text_delta') process.stdout.write(chunk.data);
132
+ if (chunk.type === 'block_delta') process.stdout.write(chunk.data.content);
124
133
  }
125
134
  }
126
135
  ```
@@ -266,6 +275,12 @@ Full endpoint documentation with request/response schemas:
266
275
 
267
276
  **[savanto.ai/docs/api](https://savanto.ai/docs/api)**
268
277
 
278
+ ## Versioning & changelog
279
+
280
+ See [CHANGELOG.md](./CHANGELOG.md) for per-version migration notes,
281
+ including the TypeScript-level breaking changes in `2.2.0`. Type
282
+ narrowings and generator-driven shape changes are treated as major.
283
+
269
284
  ## Regeneration
270
285
 
271
286
  The SDK is auto-generated from the OpenAPI spec. To regenerate after API changes: