@ykurilov/agent-chat-limits 0.0.0-stage → 0.56.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,749 @@
1
+ # @ykurilov/agent-chat-limits
2
+
3
+ ## 0.56.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [24c313e]
8
+ - @ykurilov/agent-chat-core@0.56.0
9
+
10
+ ## 0.55.0
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies [51f7b38]
15
+ - Updated dependencies [cdc7dd7]
16
+ - @ykurilov/agent-chat-core@0.55.0
17
+
18
+ ## 0.54.0
19
+
20
+ ### Patch Changes
21
+
22
+ - Updated dependencies [fa90219]
23
+ - @ykurilov/agent-chat-core@0.54.0
24
+
25
+ ## 0.53.0
26
+
27
+ ### Patch Changes
28
+
29
+ - Updated dependencies [b6a7786]
30
+ - @ykurilov/agent-chat-core@0.53.0
31
+
32
+ ## 0.52.0
33
+
34
+ ### Patch Changes
35
+
36
+ - @ykurilov/agent-chat-core@0.52.0
37
+
38
+ ## 0.51.0
39
+
40
+ ### Patch Changes
41
+
42
+ - Updated dependencies [41b1726]
43
+ - @ykurilov/agent-chat-core@0.51.0
44
+
45
+ ## 0.50.0
46
+
47
+ ### Patch Changes
48
+
49
+ - @ykurilov/agent-chat-core@0.50.0
50
+
51
+ ## 0.49.0
52
+
53
+ ### Patch Changes
54
+
55
+ - Updated dependencies [084cef7]
56
+ - @ykurilov/agent-chat-core@0.49.0
57
+
58
+ ## 0.48.0
59
+
60
+ ### Patch Changes
61
+
62
+ - Updated dependencies [fa10d5a]
63
+ - @ykurilov/agent-chat-core@0.48.0
64
+
65
+ ## 0.47.0
66
+
67
+ ### Patch Changes
68
+
69
+ - Updated dependencies [5a59c33]
70
+ - @ykurilov/agent-chat-core@0.47.0
71
+
72
+ ## 0.46.2
73
+
74
+ ### Patch Changes
75
+
76
+ - Updated dependencies [3d0d2f2]
77
+ - @ykurilov/agent-chat-core@0.46.2
78
+
79
+ ## 0.46.1
80
+
81
+ ### Patch Changes
82
+
83
+ - @ykurilov/agent-chat-core@0.46.1
84
+
85
+ ## 0.46.0
86
+
87
+ ### Patch Changes
88
+
89
+ - Updated dependencies [56d7528]
90
+ - Updated dependencies [a1933fd]
91
+ - Updated dependencies [52cc67d]
92
+ - @ykurilov/agent-chat-core@0.46.0
93
+
94
+ ## 0.45.0
95
+
96
+ ### Patch Changes
97
+
98
+ - @ykurilov/agent-chat-core@0.45.0
99
+
100
+ ## 0.44.0
101
+
102
+ ### Patch Changes
103
+
104
+ - Updated dependencies [6a2b5d9]
105
+ - @ykurilov/agent-chat-core@0.44.0
106
+
107
+ ## 0.43.0
108
+
109
+ ### Patch Changes
110
+
111
+ - Updated dependencies [763f4c5]
112
+ - @ykurilov/agent-chat-core@0.43.0
113
+
114
+ ## 0.42.0
115
+
116
+ ### Patch Changes
117
+
118
+ - Updated dependencies [a385276]
119
+ - @ykurilov/agent-chat-core@0.42.0
120
+
121
+ ## 0.41.0
122
+
123
+ ### Patch Changes
124
+
125
+ - Updated dependencies [9472fcb]
126
+ - @ykurilov/agent-chat-core@0.41.0
127
+
128
+ ## 0.40.0
129
+
130
+ ### Patch Changes
131
+
132
+ - Updated dependencies [0e7aab3]
133
+ - @ykurilov/agent-chat-core@0.40.0
134
+
135
+ ## 0.39.1
136
+
137
+ ### Patch Changes
138
+
139
+ - @ykurilov/agent-chat-core@0.39.1
140
+
141
+ ## 0.39.0
142
+
143
+ ### Patch Changes
144
+
145
+ - Updated dependencies [3b90429]
146
+ - @ykurilov/agent-chat-core@0.39.0
147
+
148
+ ## 0.38.0
149
+
150
+ ### Patch Changes
151
+
152
+ - Updated dependencies [1cae504]
153
+ - Updated dependencies [4065a4a]
154
+ - @ykurilov/agent-chat-core@0.38.0
155
+
156
+ ## 0.37.0
157
+
158
+ ### Patch Changes
159
+
160
+ - Updated dependencies [7f49d12]
161
+ - Updated dependencies [ea78982]
162
+ - @ykurilov/agent-chat-core@0.37.0
163
+
164
+ ## 0.36.0
165
+
166
+ ### Patch Changes
167
+
168
+ - Updated dependencies [3487fb4]
169
+ - Updated dependencies [8c43b86]
170
+ - Updated dependencies [6f42418]
171
+ - Updated dependencies [5f13c74]
172
+ - @ykurilov/agent-chat-core@0.36.0
173
+
174
+ ## 0.35.0
175
+
176
+ ### Patch Changes
177
+
178
+ - Updated dependencies [2382026]
179
+ - @ykurilov/agent-chat-core@0.35.0
180
+
181
+ ## 0.34.0
182
+
183
+ ### Patch Changes
184
+
185
+ - Updated dependencies [d039ce4]
186
+ - @ykurilov/agent-chat-core@0.34.0
187
+
188
+ ## 0.33.0
189
+
190
+ ### Patch Changes
191
+
192
+ - Updated dependencies [4a72339]
193
+ - @ykurilov/agent-chat-core@0.33.0
194
+
195
+ ## 0.32.0
196
+
197
+ ### Minor Changes
198
+
199
+ - 27f4b1e: Удаление чата стирает состояние его окон лимитов (окна области `chat`: начало скользящего окна, перенос перерасхода,
200
+ отправленные пороги). Раньше эти записи оставались в базе навсегда. Расход в журнале и окна человека и сервиса не трогаются.
201
+
202
+ Новое: необязательный метод хранилища `deleteSpendingLimitStates(keys)` (есть в памяти, SQLite и PostgreSQL) и
203
+ необязательный `chatDeleted({ userId, chatId })` у `SpendingLimits` — служба чата зовёт его после удаления чата;
204
+ ошибка уходит в журнал сервера и удаление не отменяет.
205
+
206
+ ### Как обновить приложение
207
+
208
+ Ничего, если хранилище встроенное. Своё хранилище может добавить `deleteSpendingLimitStates`; без него записи окон
209
+ удалённых чатов просто остаются, как раньше.
210
+
211
+ ### Patch Changes
212
+
213
+ - Updated dependencies [71c32b7]
214
+ - Updated dependencies [cae13bf]
215
+ - Updated dependencies [a94bda9]
216
+ - Updated dependencies [4e44492]
217
+ - Updated dependencies [ac9b7d3]
218
+ - Updated dependencies [27f4b1e]
219
+ - @ykurilov/agent-chat-core@0.32.0
220
+
221
+ ## 0.31.2
222
+
223
+ ### Patch Changes
224
+
225
+ - @ykurilov/agent-chat-core@0.31.2
226
+
227
+ ## 0.31.1
228
+
229
+ ### Patch Changes
230
+
231
+ - @ykurilov/agent-chat-core@0.31.1
232
+
233
+ ## 0.31.0
234
+
235
+ ### Patch Changes
236
+
237
+ - @ykurilov/agent-chat-core@0.31.0
238
+
239
+ ## 0.30.0
240
+
241
+ ### Patch Changes
242
+
243
+ - @ykurilov/agent-chat-core@0.30.0
244
+
245
+ ## 0.29.0
246
+
247
+ ### Patch Changes
248
+
249
+ - @ykurilov/agent-chat-core@0.29.0
250
+
251
+ ## 0.28.0
252
+
253
+ ### Patch Changes
254
+
255
+ - Updated dependencies [11c3c97]
256
+ - Updated dependencies [d63ccaa]
257
+ - Updated dependencies [6a2f903]
258
+ - Updated dependencies [189cba9]
259
+ - @ykurilov/agent-chat-core@0.28.0
260
+
261
+ ## 0.27.0
262
+
263
+ ### Patch Changes
264
+
265
+ - Updated dependencies [d8341b2]
266
+ - Updated dependencies [5e4ed28]
267
+ - Updated dependencies [e0bb519]
268
+ - @ykurilov/agent-chat-core@0.27.0
269
+
270
+ ## 0.26.2
271
+
272
+ ### Patch Changes
273
+
274
+ - Updated dependencies [f24858a]
275
+ - @ykurilov/agent-chat-core@0.26.2
276
+
277
+ ## 0.26.1
278
+
279
+ ### Patch Changes
280
+
281
+ - @ykurilov/agent-chat-core@0.26.1
282
+
283
+ ## 0.26.0
284
+
285
+ ### Patch Changes
286
+
287
+ - Updated dependencies [eb43e47]
288
+ - @ykurilov/agent-chat-core@0.26.0
289
+
290
+ ## 0.25.0
291
+
292
+ ### Patch Changes
293
+
294
+ - Updated dependencies [99d426e]
295
+ - Updated dependencies [d8201f6]
296
+ - @ykurilov/agent-chat-core@0.25.0
297
+
298
+ ## 0.24.0
299
+
300
+ ### Minor Changes
301
+
302
+ - 9b895cd: Ломает приложения: только исчерпывающие `switch` по видам ошибок сервера и событиям чата (новые значения) и код, который
303
+ сам читает ответ `resolveUser`. Без новых настроек поведение прежнее.
304
+
305
+ Агент внутри продукта: агент работает с объектами приложения — уроком, заказом, документом. Всё по желанию; без новых
306
+ настроек запрос к модели не меняется ни на байт. Подробно — `docs/in-product-agent.md`.
307
+
308
+ - **Проверка перед ходом.** `beforeTurn({ userId, chat, action, text? })` отказывает своими словами: 403 `turn_refused`
309
+ до любых записей и лимитов, на отправке, правке, повторе, переделке, подтверждении, виджете, служебных сообщениях, фоновой
310
+ задаче и в мессенджерах. `resolveUser` может вернуть `{ refused: { code, message, status? } }` → `access_refused` на всех
311
+ адресах чата, голоса, голосового режима (WebSocket 4401) и мессенджеров. React: `onTurnRefused`, текст отказа вместо «нет
312
+ связи», сообщение возвращается в поле. Ручная проверка — `service.assertTurnAllowed`.
313
+ - **Итог хода.** `afterTurn(turn)` после каждого хода с повторами и сроком (`afterTurnAttempts`, `afterTurnTimeoutMs`),
314
+ переживает перезапуск сервера. `TurnResult`: владелец и автор хода, текст, части, расход, действие, канал, объект,
315
+ сотрудник. События приложения `{ type, data? }` — из хука, из инструмента (`model.emitAppEvent`) и вне хода
316
+ (`service.publishAppEvent`); в React `appEvents` и `onAppEvent`, события переживают перезагрузку. Модели не уходят.
317
+ - **Чат объекта.** Ключ и данные объекта у чата, один чат на ключ у человека: `service.openSubjectChat`, на сервере
318
+ `subjectChats` (`POST …/subject`), в React `<AgentChat subject="lesson:42" />`. Данные объекта видят `beforeTurn`,
319
+ инструменты (`model.subject`) и `afterTurn`; в браузер и модели не уходят.
320
+ - **Приветствие.** `greeting: { text }` — первое сообщение от агента без вызова модели. Проверено на всех моделях каталога.
321
+ - **Лимит сообщений.** Окно лимитов с `limitMessages` вместо `limitUsd`, `exemptUsers` — люди без лимитов,
322
+ `onMessageThreshold` — пороги. Отказ — 429 с `unit: "messages"`.
323
+ - **Импорт старой переписки.** `service.importChat({ ownerId, chat, messages })` с датами и картинками
324
+ (`attachments.importFile`), без хода, памяти, расхода и лимитов.
325
+ - **Сотрудник проверяет чат человека.** Песочница с опцией `people`: админ видит чаты человека только на чтение и
326
+ проверяет ответы в копии у себя. Память человека в копии только читается, расход — на админа, человек ничего не видит.
327
+ Инструменты приложения получают `model.staff` и в копии в данные человека не пишут.
328
+ - **Свои кнопки у поля ввода.** `composerActions` с помощником `{ value, setValue, send, addFiles, busy, blocked, canSend }`.
329
+ Договор скина v19: `Composer.actions`; скин v18 работает без кнопок, Тандем готов.
330
+ - **Хранилища.** Миграции применяются сами: SQLite `0015`, PostgreSQL `0014` (ключ объекта, состояние хука итога).
331
+
332
+ ### Что может сломаться
333
+
334
+ - **Исчерпывающие `switch` по закрытым спискам.** Новые значения: вид ошибки сервера `turn_refused` и `access_refused`
335
+ (`HandlerErrorKind`), события чата `after-turn-failed` и `app-event` (`ChatEvent`), `unit` у отказа лимита. Добавьте ветки
336
+ и надписи.
337
+ - **Список чатов без чатов объектов.** `listChats` и `GET {basePath}` по умолчанию не показывают чаты с ключом объекта.
338
+ Касается только приложений, которые начнут ими пользоваться; показать — `chatList: { subjectChats: true }` или
339
+ `listChats(userId, { subjects: "include" })`.
340
+ - **Свой `resolveUser` в обёртках.** Тип ответа — `ResolvedUser` (`{ id } | { refused } | null`). Код, который сам вызывает
341
+ `resolveUser` и читает `.id`, сначала проверяет `"refused" in user`.
342
+ - **Своё хранилище.** Для новых возможностей нужны необязательные методы (`findChatBySubject`, `importChat`,
343
+ `setAfterTurnState`, `listPendingAfterTurn`); без них эти возможности при включении дают ошибку при старте, остальное
344
+ работает. `listChats(ownerId, options?)` получил второй параметр — чаты с ключом скрывать по умолчанию.
345
+ - **Свой `ChatService`** (заглушки в тестах) добавляет новые методы: `openSubjectChat`, `importChat`, `assertTurnAllowed`, `peekHistory`,
346
+ `publishAppEvent`.
347
+ - **Своя реализация `SpendingLimits`** может добавить `takeMessage` — без него сообщения не считаются.
348
+ - **`AgentChat`: `chatId` стал необязательным** — нужен ровно один из `chatId` и `subject`; ни одного или оба — ошибка при
349
+ рендере. При смене `chatId` чат пересоздаётся: черновик и состояние прошлого чата не переносятся.
350
+ - **Окна лимитов: `limitUsd` стал необязательным** (у окна сообщений его нет). Код, который читает свои окна, проверяет
351
+ `limitMessages`.
352
+ - **Отказ сервера в React** (`ServerRefusal`) получил `kind` и `code`. 401 со словами приложения (`access_refused`) — это
353
+ отказ, а не «вход истёк».
354
+ - **«Пустой чат»** для «Нового чата»: чат объекта пустым не считается, обычный чат с одним приветствием — считается.
355
+ - Новый подпуть `@ykurilov/agent-chat-core/access` — помощники отказа в доступе для своих обработчиков.
356
+
357
+ ### Как обновить приложение
358
+
359
+ 1. `pnpm up "@ykurilov/agent-chat-*@^0.24.0"`. Миграции базы применяются сами при открытии хранилища.
360
+ 2. Исчерпывающие `switch` по `HandlerErrorKind` (сервер) и `ChatEvent` (`chatEvents` ядра): найдите их поиском и добавьте
361
+ ветки `turn_refused`, `access_refused`, `after-turn-failed`, `app-event`. Обязательных надписей не прибавилось: текст
362
+ отказа приходит с сервера. Свой словарь для лимита сообщений — необязательные `spendingMessagesLeft`,
363
+ `spendingMessagesExhausted`, `editMessageLimit`.
364
+
365
+ Было: `switch (event.type) { case "tasks-changed": … case "notice-added": … default: assertNever(event) }`
366
+ Стало: те же ветки плюс `case "after-turn-failed": case "app-event": break;`
367
+ 3. Свой код, который сам зовёт `resolveUser` и читает `.id`: сначала `if (user && "refused" in user) …`.
368
+ 4. Ничего больше не нужно, если новые возможности не включать. Чтобы включить — `docs/in-product-agent.md`.
369
+ 5. Свой скин: кнопки у поля появятся, когда `Composer` начнёт рисовать `actions` (договор v19, `docs/skin-v19.md`).
370
+ 6. Проверка: `pnpm typecheck` без ошибок, чат открывается и отвечает как раньше.
371
+
372
+ ### Patch Changes
373
+
374
+ - Updated dependencies [9b895cd]
375
+ - @ykurilov/agent-chat-core@0.24.0
376
+
377
+ ## 0.23.0
378
+
379
+ ### Patch Changes
380
+
381
+ - @ykurilov/agent-chat-core@0.23.0
382
+
383
+ ## 0.22.0
384
+
385
+ ### Patch Changes
386
+
387
+ - @ykurilov/agent-chat-core@0.22.0
388
+
389
+ ## 0.21.0
390
+
391
+ ### Patch Changes
392
+
393
+ - Updated dependencies [7c3bcc4]
394
+ - @ykurilov/agent-chat-core@0.21.0
395
+
396
+ ## 0.20.0
397
+
398
+ ### Patch Changes
399
+
400
+ - Updated dependencies [fd16b01]
401
+ - @ykurilov/agent-chat-core@0.20.0
402
+
403
+ ## 0.19.0
404
+
405
+ ### Minor Changes
406
+
407
+ - aa0fd51: Гибкие лимиты расходов. Вместо одного бюджета на месяц можно задать любой набор окон сразу: сутки, неделю или месяц по
408
+ часовому поясу (со своим днём начала недели), «N часов от первого запроса» или на всё время. Область окна — человек, чат или
409
+ весь сервис. Ход начинается, только если не исчерпано ни одно окно.
410
+
411
+ - `createSpendingLimits({ windows: [...] })` вместо `budget` и `period`. Прежний `budget` работает как раньше (и получил
412
+ `period: "week"`).
413
+ - `carryOverrun: true` у окна: перерасход начатого хода переносится в начало следующего окна.
414
+ - `turn: { minRemainingUsd, maxCostUsd, maxToolCalls, overrun }` — запас на старт хода и потолки одного хода. На потолке
415
+ ход заканчивается итогом без новых вызовов инструментов.
416
+ - Человеку: свой лимит окна, блокировка (`setUserSettings`, `getUserSettings`) и ручной сброс окна (`resetWindow`).
417
+ - `onThreshold(event)` — событие при 80% и 100% лимита окна (пороги — `thresholds`), одно на порог в окне, проверка после
418
+ каждой записи расхода.
419
+ - `getSpending` отдаёт доллары по каждому окну (`windows`) и `blocked`.
420
+ - Сервер: в ответе 429 `spending_limit_reached` появились `reason` (`limit` | `blocked` | `reserve`), `windowId` и `hours`.
421
+ `GET …/spending` отдаёт `reason`, `windowId`, `hours`, `blocked` и список окон `windows` — только доли и время сброса,
422
+ без долларов.
423
+ - Браузер: надписи «на неделю», «на 5 ч» (с временем обновления), «сервиса», «Лимита … не хватит на новый ответ», «Новые
424
+ ответы для вас приостановлены». В `SpendingInfo` новые необязательные поля `hours`, `reason`, `windowId`, `windows`.
425
+
426
+ Скользящим окнам, личным настройкам, сбросу и порогам нужны методы состояния хранилища: в памяти, SQLite и PostgreSQL они есть
427
+ (запись `storage-limit-state`). Подробно — README пакета `@ykurilov/agent-chat-limits`, раздел «Окна лимитов».
428
+
429
+ **Ломает приложения:** только исчерпывающие проверки и свои словари по периоду. У `SpendingScope` появилось значение
430
+ `service`, у `SpendingPeriod` — `week` и `rolling` (в `@ykurilov/agent-chat-core` и `@ykurilov/agent-chat-react`).
431
+
432
+ ### Как обновить приложение
433
+
434
+ 1. Поищите в приложении `SpendingPeriod`, `SpendingScope`, `info.period`, `info.scope` и `spendingWarning` / `spendingExhausted`
435
+ в своём словаре надписей. Если надпись строится по таблице периодов, добавьте новые значения:
436
+
437
+ Было:
438
+
439
+ ```ts
440
+ const periodPhrase = { month: " на месяц", day: " на день", none: "" };
441
+ ```
442
+
443
+ Стало:
444
+
445
+ ```ts
446
+ const periodPhrase = { month: " на месяц", week: " на неделю", day: " на день", none: "" };
447
+ const phrase = info.period === "rolling" ? ` на ${info.hours} ч` : periodPhrase[info.period];
448
+ ```
449
+
450
+ То же для области: `info.scope === "service"` — лимит всего сервиса.
451
+ 2. Свой словарь с `spendingExhausted`: учтите `info.reason` — `"blocked"` (человек заблокирован, `resetsAt` нет) и `"reserve"`
452
+ (остаток меньше запаса на старт хода).
453
+ 3. Свой разбор ответа 429 или `GET …/spending` на сервере или в браузере: новые поля необязательные, старые на месте.
454
+ 4. Ничего из этого нет в приложении — обновление ничего не требует.
455
+ 5. Проверка: `pnpm typecheck` проходит; с исчерпанным лимитом чат показывает надпись и не даёт отправить сообщение.
456
+ - 2013f15: Цена модели теперь может зависеть от времени суток: в профиле появились часы пик (`pricing.peakHours`) — окна по дням недели
457
+ и часам со своим множителем, отдельный множитель для попаданий в кэш и даты-исключения. Каждый шаг хода считается по времени
458
+ своего запроса. DeepSeek 4.1 Flash у провайдера DeepSeek больше не считается всегда по пиковой цене: вне пика 0,15 / 0,60 / 0,003 $
459
+ за миллион токенов, по будням 01–04 и 06–10 UTC вдвое. Расход этой пары в лимитах в непиковые часы стал вдвое меньше — как в счёте DeepSeek.
460
+
461
+ `costOfUsage(usage, price, at?)` принимает третьим параметром время запроса: без него — цена вне пика.
462
+
463
+ Обновление ничего не требует. Своя модель с часами пик — по образцу:
464
+
465
+ ```ts
466
+ pricing: {
467
+ myprovider: {
468
+ inputPerMillion: 0.15, outputPerMillion: 0.6, cachedInputPerMillion: 0.003, // цены вне пика
469
+ peakHours: { timeZone: "UTC", multiplier: 2, windows: [{ days: [1, 2, 3, 4, 5], from: "01:00", to: "04:00" }] },
470
+ },
471
+ },
472
+ ```
473
+
474
+ ### Patch Changes
475
+
476
+ - Updated dependencies [fde2297]
477
+ - Updated dependencies [aa0fd51]
478
+ - Updated dependencies [9f94dbe]
479
+ - Updated dependencies [2013f15]
480
+ - Updated dependencies [aa0fd51]
481
+ - Updated dependencies [05e258b]
482
+ - Updated dependencies [aa0fd51]
483
+ - @ykurilov/agent-chat-core@0.19.0
484
+
485
+ ## 0.18.6
486
+
487
+ ### Patch Changes
488
+
489
+ - @ykurilov/agent-chat-core@0.18.6
490
+
491
+ ## 0.18.5
492
+
493
+ ### Patch Changes
494
+
495
+ - @ykurilov/agent-chat-core@0.18.5
496
+
497
+ ## 0.18.4
498
+
499
+ ### Patch Changes
500
+
501
+ - @ykurilov/agent-chat-core@0.18.4
502
+
503
+ ## 0.18.3
504
+
505
+ ### Patch Changes
506
+
507
+ - @ykurilov/agent-chat-core@0.18.3
508
+
509
+ ## 0.18.2
510
+
511
+ ### Patch Changes
512
+
513
+ - @ykurilov/agent-chat-core@0.18.2
514
+
515
+ ## 0.18.1
516
+
517
+ ### Patch Changes
518
+
519
+ - @ykurilov/agent-chat-core@0.18.1
520
+
521
+ ## 0.18.0
522
+
523
+ ### Patch Changes
524
+
525
+ - Updated dependencies [43d7ebe]
526
+ - @ykurilov/agent-chat-core@0.18.0
527
+
528
+ ## 0.17.0
529
+
530
+ ### Patch Changes
531
+
532
+ - @ykurilov/agent-chat-core@0.17.0
533
+
534
+ ## 0.16.0
535
+
536
+ ### Minor Changes
537
+
538
+ - cf52f10: **Ломает только исчерпывающие проверки:** у `UsageSource` появилось значение `images`, у видов ошибок сервера — `widget_not_pending` и `invalid_answer`, у `ChatService` — метод `answerWidget`. Своё хранилище должно поддержать условную запись `AwaitingMessagePatch.expectedParts` (при несовпадении частей — `MessageNotAwaitingError`; помощник `sameMessageParts` из core), иначе две копии сервера могут затереть ответы друг друга; общие тесты хранилищ это проверяют. Остальное необязательно, приложение без правок работает как раньше.
539
+
540
+ Что нового: агент может спросить кнопками, звёздами или формой (виджеты), нарисовать схему блоком ` ```mermaid ` и создать картинки через Runware. Всё включается отдельно:
541
+
542
+ - виджеты: `agent: { widgets: { basic: true, custom: [свойВиджет] } }`, свой виджет — `defineWidget` из core и компонент в `widgets={{ … }}` у `AgentChat`; ответ человека идёт на `POST /:chatId/widgets/:toolCallId`, ход ждёт со статусом `awaiting_approval`;
543
+ - схемы: новый пакет `@ykurilov/agent-chat-diagrams`, `agent: { diagrams: true }` на сервере и `diagrams={diagramsClient}` на странице;
544
+ - картинки: новый пакет `@ykurilov/agent-chat-images` (нужен пакет вложений и `RUNWARE_API_KEY`), `images.tools(model)` в `agent.tools`, `images={imagesClient}` на странице; свой срок ожидания (210 с) инструмент сообщает ядру сам (`declareToolTimeout`), расход бронируется до заказа (`assertWithinLimit({ pendingUsd })`);
545
+ - окно просмотра картинок и схем: Тандем готов, своему скину — необязательные части договора версии 13 (`Widget`, `SuggestedReplies`, `GeneratedImages`, `MediaViewer`, `diagramColors`, `renderDiagram`). `splitMermaidBlocks` экспортируется из react;
546
+ - вложения: `Attachments.storage` и `detectFileKind` экспортируются из пакета вложений;
547
+ - голосовой режим произносит вопрос и варианты ждущего виджета и говорит «схема на экране» вместо блока схемы;
548
+ - «Стоп» на экране закрывает висящие вызовы так же, как сервер, разрешённые без результата — «результат неизвестен» (`stoppedToolCallReasons.resultUnknown`);
549
+ - упавший свой виджет страницы заменяется запасным видом скина, лента цела.
550
+
551
+ Подробно: `docs/widgets.md`, `docs/diagrams.md`, `docs/images.md`, `docs/media-viewer.md`.
552
+
553
+ ### Как обновить приложение
554
+
555
+ Обычному приложению на SQLite или PostgreSQL из набора делать ничего не нужно: миграций нет, всё новое включается отдельно.
556
+
557
+ 1. Исчерпывающий `switch` по `UsageSource` — добавьте ветку `"images"`; по видам ошибок сервера — `"widget_not_pending"` и `"invalid_answer"`.
558
+ 2. Своя реализация `ChatService` — добавьте метод `answerWidget({ chatId, ownerId, toolCallId, answer, page }): Promise<{ resumed: boolean }>` (тип `WidgetAnswerRequest` из core, образец — служба из core).
559
+ 3. Своё хранилище: `updateAwaitingMessage` получает необязательное поле `expectedParts`. Если оно есть и части сообщения в базе с ним не совпадают (`sameMessageParts` из core), бросайте `MessageNotAwaitingError`; проверку и запись делайте в одной транзакции. Прогоните общие тесты хранилищ из core.
560
+ 4. Своя обёртка `assertWithinLimit` из limits — пробрасывайте новое необязательное поле `pendingUsd`.
561
+ 5. Проверка: `pnpm typecheck`, затем в чате отправить сообщение и получить ответ.
562
+
563
+ ### Patch Changes
564
+
565
+ - Updated dependencies [cf52f10]
566
+ - @ykurilov/agent-chat-core@0.16.0
567
+
568
+ ## 0.15.0
569
+
570
+ ### Minor Changes
571
+
572
+ - f05ad23: Часовой пояс для лимитов расходов: `createSpendingLimits({ timeZone: "Europe/Moscow" })`. Сутки и месяцы режутся
573
+ по полуночи этого пояса, а не по UTC (раньше московский суточный лимит обнулялся в 03:00). Переход на летнее время
574
+ учтён: сутки бывают 23 и 25 часов. Для сегодняшней даты агента этот пояс только запасной: главнее пояс браузера
575
+ человека, а пояс лимитов берётся, когда браузер свой пояс не прислал. Дата обновления лимита на экране — по поясу лимитов. Пояс не из базы IANA — ошибка при старте.
576
+
577
+ Новое: `timeZone` в настройках лимитов и необязательное поле `timeZone` у `SpendingLimits`, `SpendingStatus`,
578
+ `SpendingLimitReachedError`, `SpendingReport` и `SpendingInfo` (экран); ответы `GET …/spending` и отказ 429 получают
579
+ `timeZone`, если пояс задан. Ядро экспортирует `resolveTimeZone`.
580
+
581
+ ### Как обновить приложение
582
+
583
+ Ничего делать не нужно: без `timeZone` лимиты по UTC, дата агента по поясу человека или сервера, как раньше.
584
+
585
+ Чтобы считать сутки по Москве, в файле, где создаются лимиты:
586
+
587
+ ```ts
588
+ const limits = createSpendingLimits({ storage, catalog, budget, period: "day", timeZone: "Europe/Moscow" });
589
+ ```
590
+
591
+ `agent.timeZone` после этого задавать не нужно. Если он уже задан, он должен совпадать с поясом лимитов (другое имя того же пояса
592
+ вроде `Asia/Calcutta` и `Asia/Kolkata` — совпадение), иначе
593
+ служба чата не создастся (ошибка называет оба пояса). Свои надписи `spendingExhausted`, которые сами форматируют
594
+ `resetsAt`, могут брать пояс из `info.timeZone`. Проверка: `limits.getSpending({ userId })` отдаёт `periodStart`
595
+ вида `…T21:00:00.000Z` (полночь по Москве).
596
+
597
+ ### Patch Changes
598
+
599
+ - Updated dependencies [f05ad23]
600
+ - Updated dependencies [f05ad23]
601
+ - @ykurilov/agent-chat-core@0.15.0
602
+
603
+ ## 0.14.0
604
+
605
+ ### Minor Changes
606
+
607
+ - 6b20d83: Голосовой режим («разговор»): человек говорит, слова сами уходят сообщением в чат, ответ агента звучит голосом, человека можно перебить. Новый пакет `@ykurilov/agent-chat-voice-dialog`: распознавание Soniox, озвучка ElevenLabs (голос по умолчанию «Профессиональный диктор для аудиокниг», модель `eleven_v4_turbo`), ключи только на сервере. Перебитый ответ остаётся в ленте с отметкой «Вы перебили: озвучено до «…»», она хранится и видна после перезагрузки. Расход на озвучку идёт в учёт и лимиты (`source: "speech"`). Человек выбирает голос в меню режима; выбор хранится на сервере.
608
+
609
+ Модель узнаёт о режиме двумя служебными пометками: одна при входе («ответы озвучиваются, отвечай коротко и разговорно»), одна при возврате к тексту («разговор снова текстом»). Они стоят в конце сообщения человека, где режим сменился, а не в каждом запросе. Сообщение из режима запоминается голосовым отметкой в самом сообщении (часть `data-voice-mode` в `parts`), поэтому миграции для этого нет, пометки выводятся из сохранённого и кэш модели цел. Тексты задают настройки агента `voiceModeHint` и `textModeNote` (есть умолчания).
610
+
611
+ Обновление ничего не ломает: без нового пакета чат работает как раньше. Диктовка в поле ввода (`@ykurilov/agent-chat-voice`) не меняется.
612
+
613
+ ### Как обновить приложение
614
+
615
+ Нужно, только если вы хотите голосовой режим.
616
+
617
+ 1. Поставьте пакет `@ykurilov/agent-chat-voice-dialog` той же версии, что и остальные пакеты SDK. Скин Тандем обновите вместе с SDK: договор скина теперь версии 10 (необязательный `dialog` у поля ввода, панель режима, меню голосов, отметка обрыва; свой скин может их не рисовать, тогда кнопки режима не будет).
618
+ 2. Сервер. Рядом с чатом подключите режим (нужен WebSocket; с Hono и `@hono/node-ws`):
619
+
620
+ ```ts
621
+ import { mountDialogRelay } from "@ykurilov/agent-chat-voice-dialog/hono";
622
+
623
+ mountDialogRelay(app, {
624
+ path: "/api/voice-dialog",
625
+ upgradeWebSocket,
626
+ storage,
627
+ resolveUser, // тот же вход, что у чата
628
+ spendingLimits, // по желанию
629
+ logError: (message) => console.error(message),
630
+ speech: {
631
+ voices: [{ id: "oE0n0S3wK5Mj9C62iehn", name: "Профессиональный диктор для аудиокниг" }],
632
+ defaultVoiceId: "oE0n0S3wK5Mj9C62iehn",
633
+ pricePerThousandChars: { eleven_v4_turbo: 0.11, eleven_v4: 0.22 }, // ваша цена, $ за 1000 знаков
634
+ },
635
+ });
636
+ ```
637
+
638
+ Ключи `ELEVENLABS_API_KEY` и `SONIOX_API_KEY` — в окружении сервера. Next.js: WebSocket нужен свой Node-сервер, см. README пакета (`createDialogRelay`).
639
+ 3. Браузер:
640
+
641
+ ```tsx
642
+ const voiceDialog = createVoiceDialogEngine({ basePath: "/api/voice-dialog" });
643
+ <AgentChat api="/api/chat" skin={tandemSkin} voiceDialog={voiceDialog} />
644
+ ```
645
+
646
+ 4. Настройки. Слова остановки и междометия по умолчанию только русские; для другого языка задайте `bargeIn.stopWords` и `bargeIn.ignoreWords` у движка и `language` на сервере. Строгая политика содержимого страницы: добавьте `worklet-src blob:`.
647
+ 5. Хранилище. SQLite получает миграцию 0008, PostgreSQL — 0007 (схема `agent_chat`) сами при открытии базы. Свой `ChatStorage`: добавьте необязательные методы `setVoiceCut`, `getVoiceChoice`, `setVoiceChoice` (без них отметка обрыва и выбор голоса живут только до перезагрузки) и значение `"speech"` в `UsageSource`, если учёт расхода у вас свой.
648
+ 6. Свой экран (не наш скин): части сообщения с типом `data-voice-mode` не рисуйте, показывайте только текстовые части.
649
+ 7. Проверка: `pnpm typecheck` проходит; в чате появилась кнопка режима, после нажатия виден статус «Слушаю», фраза уходит сообщением, ответ звучит.
650
+
651
+ ### Patch Changes
652
+
653
+ - 96098be: Мелкие исправления (022). Символ с кодом ноль в первом сообщении больше не роняет отправку на Postgres: он выбрасывается из названия чата, из записей памяти и из сводки сжатия истории (иначе запись сжатия падала бы при каждой попытке); в самом тексте сообщения он лежит как раньше. Вложения отмечаются прикреплёнными до записи сообщения, а не после: если сервер упал между двумя шагами, чистка брошенных файлов не удалит их через сутки (файл, отмеченный зря, безвреден и уходит вместе с чатом). Во всех пакетах появилось поле `engines: { node: ">=22" }`; голосовой сервер (`createVoiceRelay`) и голосовой диалог (`createDialogRelay`) на Node 20 сразу при создании говорят понятной ошибкой, что нужен Node 22 (раньше падали при первом соединении; со своим `connectUpstream` проверки нет). В пакете вложений `react` и `@ykurilov/agent-chat-react` стали необязательными peer-зависимостями: серверу без экрана они больше не ставятся. Приложению, которое берёт `@ykurilov/agent-chat-attachments/react`, достаточно иметь их в проекте — для чата на экране они нужны и так; если они подтягивались только через вложения, добавьте их в зависимости приложения.
654
+ - Updated dependencies [4f33e85]
655
+ - Updated dependencies [15fbbcc]
656
+ - Updated dependencies [c09c1e2]
657
+ - Updated dependencies [062ce36]
658
+ - Updated dependencies [656233a]
659
+ - Updated dependencies [96098be]
660
+ - Updated dependencies [b6f4855]
661
+ - Updated dependencies [278a718]
662
+ - Updated dependencies [c1581f4]
663
+ - Updated dependencies [213bcb3]
664
+ - Updated dependencies [927c02c]
665
+ - Updated dependencies [be77ee2]
666
+ - Updated dependencies [8b59096]
667
+ - Updated dependencies [7fbc105]
668
+ - Updated dependencies [84ad64f]
669
+ - Updated dependencies [5027963]
670
+ - Updated dependencies [c2769dd]
671
+ - Updated dependencies [0cdf102]
672
+ - Updated dependencies [ba16989]
673
+ - Updated dependencies [aba59fe]
674
+ - Updated dependencies [47a56ac]
675
+ - Updated dependencies [f9b6162]
676
+ - Updated dependencies [f9b6162]
677
+ - Updated dependencies [f9b6162]
678
+ - Updated dependencies [1b2f9f1]
679
+ - Updated dependencies [0e8e0ae]
680
+ - Updated dependencies [6b20d83]
681
+ - Updated dependencies [d12dcf3]
682
+ - Updated dependencies [366d785]
683
+ - Updated dependencies [946e5d8]
684
+ - @ykurilov/agent-chat-core@0.14.0
685
+
686
+ ## 0.13.0
687
+
688
+ ### Patch Changes
689
+
690
+ - Updated dependencies [4793e4d]
691
+ - @ykurilov/agent-chat-core@0.13.0
692
+
693
+ ## 0.12.0
694
+
695
+ ### Patch Changes
696
+
697
+ - @ykurilov/agent-chat-core@0.12.0
698
+
699
+ ## 0.11.0
700
+
701
+ ### Patch Changes
702
+
703
+ - @ykurilov/agent-chat-core@0.11.0
704
+
705
+ ## 0.10.0
706
+
707
+ ### Patch Changes
708
+
709
+ - Updated dependencies [4a76ff3]
710
+ - @ykurilov/agent-chat-core@0.10.0
711
+
712
+ ## 0.9.0
713
+
714
+ ### Minor Changes
715
+
716
+ - d07a1c3: **Ломает приложения:** только если вы сами реализовали `ChatStorage` или собрали словарь надписей целиком (без `{ ...russianLabels, … }`); остальным ничего делать не нужно.
717
+
718
+ Новый пакет `@ykurilov/agent-chat-limits`: лимиты расходов. Бюджет в долларах на человека, на чат или на то и другое; период — календарный месяц (по умолчанию), сутки или без обнуления. Исчерпанный лимит останавливает ход до его начала; при 80% чат предупреждает.
719
+
720
+ Что изменилось:
721
+
722
+ - Ядро: цены в профиле модели (`ModelProfile.pricing` по провайдерам; вход, выход, чтение и запись кэша, рассуждения считаются как выход). Цены есть у всех стандартных моделей. Журнал расхода `usage_records` — токены и стоимость в долларах (`costUsd`, по ценам модели на момент хода) каждого ответа и фонового сжатия, в том числе сжатия, которое не удалось, но было оплачено; запись датируется началом хода. Журнал только дописывается и переживает удаление чата. Сумма лимита берётся из записей, поэтому смена цен и удаление модели из каталога прошлый расход не меняют. Новые методы хранилища `insertUsageRecord` и `sumUsage`.
723
+ - Ядро: `createChatService({ limits })` и `ChatService.getSpendingStatus`. Проверка идёт перед отправкой, повтором, переделкой и правкой, до любых записей; исчерпание — `SpendingLimitReachedError`.
724
+ - Сервер: `createChatHandler({ limits })`. Исчерпанный лимит — `429 {"error":{"kind":"spending_limit_reached","scope","period","resetsAt"}}`; новый адрес `GET /api/chat/:id/spending` (без сумм в долларах).
725
+ - `@ykurilov/agent-chat-limits`: `createSpendingLimits({ storage, catalog, budget, period?, warnAt?, unpricedModels?, now?, logError? })` и `getSpending({ userId, chatId })` для страницы «тариф» (расход чата считается только среди записей этого человека). Своя модель без цены — понятная ошибка при создании; явно `unpricedModels: "ignore"` — такие ходы пишутся с нулевой стоимостью. `compactNow` лимит не проверяет: ручное сжатие остаётся доступным.
726
+ - Хранилища: SQLite (миграция `0006`) и PostgreSQL (миграция `0005`) добавляют таблицу `usage_records`; миграции применяются при открытии хранилища сами, существующие данные не трогаются. Проверено на копии базы песочницы.
727
+ - Браузер: `AgentChat` принимает `spendingLimits`. Договор скина версии 8: необязательный компонент `SpendingNotice({ id, state, text })` (`state` бывает `none`: область для диктора всегда на странице) и необязательные свойства `Composer.sendingBlocked` и `Composer.sendingBlockedReasonId` (для `aria-describedby`). Скин Тандема их рисует; при остановке фокус переходит на надпись и возвращается в поле, когда лимит снят. Отказ 429 не запускает переподключения: текст возвращается в поле (при правке сообщения редактор остаётся открытым с текстом и надписью `editErrors.spendingLimit`), показывается «Лимит на месяц исчерпан, обновится 1 ноября»; исчерпание снимается само в момент обновления.
728
+ - Словарь надписей: `spendingWarning` и `spendingExhausted` (функции от сведений о лимите) и `editErrors.spendingLimit`.
729
+
730
+ Не входит в расход: веб-инструменты (`web-tools`, другой сервис), незаконченный шаг остановленного хода и ход, оборванный перезапуском сервера. Ход, начатый в пределах бюджета, доводится до конца; одновременные ходы могут вместе выйти за бюджет (проверка не резервирует деньги).
731
+
732
+ ### Как обновить приложение
733
+
734
+ Если лимиты не нужны, делать ничего не нужно, кроме двух случаев:
735
+
736
+ 1. **Только если вы реализовали `ChatStorage` сами:** добавьте `insertUsageRecord(record)` и `sumUsage(filter)` (запись расхода несёт `costUsd`, сумма возвращает `costUsd`) и прогоните `describeChatStorageContract` (в нём появился блок «записи расхода»). Хранилища SQLite и PostgreSQL из этого репозитория обновляются сами.
737
+ 2. **Только если словарь надписей написан целиком** (не через `{ ...russianLabels, … }`): добавьте функции `spendingWarning` и `spendingExhausted` и надпись `editErrors.spendingLimit`. Значения — в `russianLabels`.
738
+
739
+ Чтобы включить лимиты:
740
+
741
+ 1. `pnpm add @ykurilov/agent-chat-limits`.
742
+ 2. На сервере: `const limits = createSpendingLimits({ storage, catalog, budget: async ({ userId }) => ({ user: 5 }) })` и `createChatHandler({ …, limits })`. `storage` и `catalog` — те же, что у чата.
743
+ 3. В браузере: `<AgentChat … spendingLimits />`. Скин свой — добавьте `SpendingNotice` (с `id`, состоянием `none` и `role="status"`) и учтите `Composer.sendingBlocked` и `sendingBlockedReasonId`; нет их — надписи не будет, но отправка остановится.
744
+ 4. Проверка: с маленьким бюджетом чат сначала предупреждает, потом останавливает отправку и пишет дату обновления.
745
+
746
+ ### Patch Changes
747
+
748
+ - Updated dependencies [d07a1c3]
749
+ - @ykurilov/agent-chat-core@0.9.0