@ykurilov/agent-chat-core 0.63.2 → 0.64.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 +6 -0
- package/README.md +2 -2
- package/dist/access.js +2 -2
- package/dist/{chunk-FBULM4V6.js → chunk-5UUWSY6V.js} +2 -2
- package/dist/{chunk-FOAIG4DY.js → chunk-CVRDB3NM.js} +1 -1
- package/dist/chunk-CVRDB3NM.js.map +1 -0
- package/dist/{chunk-4Y26SCCM.js → chunk-EWEEYX6U.js} +149 -12
- package/dist/chunk-EWEEYX6U.js.map +1 -0
- package/dist/index.js +3 -3
- package/dist/storage-contract.js +13 -2
- package/dist/storage-contract.js.map +1 -1
- package/dist/types/access/chat-capabilities.d.ts +12 -8
- package/dist/types/access/client-visibility.d.ts +2 -2
- package/dist/types/agent-run/run-agent-turn.d.ts +2 -4
- package/dist/types/conversation/types.d.ts +19 -6
- package/dist/types/index.d.ts +1 -1
- package/dist/types/provider-errors/provider-error-details.d.ts +18 -0
- package/dist/types/text/scrub-secrets.d.ts +3 -1
- package/dist/types/turn-hooks/turn-hooks.d.ts +3 -4
- package/package.json +1 -1
- package/dist/chunk-4Y26SCCM.js.map +0 -1
- package/dist/chunk-FOAIG4DY.js.map +0 -1
- /package/dist/{chunk-FBULM4V6.js.map → chunk-5UUWSY6V.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @ykurilov/agent-chat-core
|
|
2
2
|
|
|
3
|
+
## 0.64.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 99cded9: Подробности сбоя провайдера для админа. Ответ, упавший на провайдере, хранит `error.details`: провайдер, модель, код, причина одной строкой (до 300 знаков; ключи окружения, `Bearer`, значения `key=`/`token`/`api-key`/`password`, маски ключей со `*`, параметры адресов, `org-…`/`proj_…` и e-mail вычищены; модерация может повторять текст человека) и `resetsAt` — когда провайдер снимет предел (OpenRouter «resets 2026-11-01»; только будущее до двух лет, без пояса — UTC). В браузер подробности уходят только по новому праву `chatCapabilities` → `errorDetails: true` (по умолчанию `false`, при `modelDetails: false` тоже `false`) — в истории, потоке и при переподключении; остальным — только `error.kind`. `afterTurn` получает их всегда. Экран: подпись-функция `labels.errorText(error, plain)`, без неё или если она бросила исключение — прежний текст `labels.errors[kind]`; типы `ChatError`, `ChatErrorDetails`, помощник `customErrorText`. Хранилища: тип колонки `error` — `ChatError`, миграций нет.
|
|
8
|
+
|
|
3
9
|
## 0.63.2
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -82,8 +82,8 @@ import type { ChatStorage } from "@ykurilov/agent-chat-core";
|
|
|
82
82
|
- Данные чата без записи: `service.getChatData(userId, chatId)` — копия, проверки владения как у `updateChatData`.
|
|
83
83
|
- Проверка ответа виджета на сервере: `checkAnswer(answer, ctx)` у своего виджета и общая `agent.widgets.checkAnswer(call, answer, ctx)` (для всех виджетов без своей). Зовётся после схемы под замком чата. Исход: `undefined` — принять, `{ answer }` — принять с заменой, `{ refuse: "текст" }` — отказать (`WidgetAnswerRefusedError`, сервер — 409 `widget_answer_refused`, виджет остаётся открытым). `ctx` — `ownerId`, `chatId`, `messageId`, `toolName`, `toolCallId`, `input`, `subject?`, `chatData?`, `state` (записи ложатся в ответ вместе с результатом виджета, хук `onStateChanged` — с причиной `widgetAnswer`), `emitAppEvent` (событие приложения вместе с ответом виджета), `signal` (отмена по сроку или «Стопу»: приложение слушает его само). Срок — `agent.widgets.checkAnswerTimeoutMs` (5 000 мс, заметно меньше `requestTimeoutMs` страницы). Исключение — 500, ничего не записано, в журнале только имя ошибки; срок вышел — `WidgetAnswerCheckTimeoutError`, сервер — 504 `widget_check_timeout`. Чат закрыли во время проверки — `ChatClosedError`, ответ не пишется.
|
|
84
84
|
- Набор виджетов по чату: `agent.widgets.enabledFor({ chat: { id, ownerId, data?, subject? } })` → имена включённых. Выключенных модель не видит (нет в инструментах запроса), их прошлые вызовы идут модели текстом. Синхронная, те же данные — тот же набор; смена набора сбрасывает кэш модели. Неизвестное имя — строка в журнал, исключение — ошибка хода. Подробно — [docs/widgets.md](../../docs/widgets.md).
|
|
85
|
-
- Права человека в чате: `chatCapabilities({ userId, chat })` возвращает запреты (`edit`, `regenerate`, `retry`, `branches`, `modelChoice`, `answerEditing`, `chatList`, `compact`, `attachments`, `voice`, `reasoning`, `toolDetails`, `steer`, `modelDetails`; по умолчанию всё `true`; `modelDetails: false` выключает и `modelChoice`). Неизвестное право или не булево значение — `TypeError`. Запрещённое действие — `ActionForbiddenError(action)` до любой записи и до `beforeTurn`. За один вызов службы права спрашиваются не больше раза. История несёт `capabilities`. Вручную — `service.assertCapability(userId, chatId, capability)`.
|
|
86
|
-
- Скрытие частей ответа: `reasoning: false` — мысли модели, `toolDetails: false` — служебные инструменты не уходят в историю, поток и повтор кусков после переподключения; хранилище, модель и `afterTurn` получают всё. Виджеты, браузерные инструменты, вызовы с запросом подтверждения и инструменты с `metadata: { showOnScreen: true }` в описании видны всегда. `modelDetails: false` убирает у чата истории `ownerId`, `modelId`, `providerId`, `reasoning`, у сообщений — `metadata.modelId`, `providerId`, `usage`, у частей и кусков — `providerMetadata`/`callProviderMetadata`/`resultProviderMetadata`, у `data-step-reset` — провайдера; отметки сжатия идут без текста сводки (`summary: ""`, `summaryTokens: 0`) в истории и `getCompactions`. Меню моделей — опция `modelMenu({ userId })`: `false` — `getModelMenu(userId)` бросает `ActionForbiddenError("modelDetails")`. История несёт `modelAcceptsImages` (видит ли модель чата картинки). Права — на того, кто открыл поток.
|
|
85
|
+
- Права человека в чате: `chatCapabilities({ userId, chat })` возвращает запреты (`edit`, `regenerate`, `retry`, `branches`, `modelChoice`, `answerEditing`, `chatList`, `compact`, `attachments`, `voice`, `reasoning`, `toolDetails`, `steer`, `modelDetails`, `errorDetails`; по умолчанию всё `true`, кроме `errorDetails`; `modelDetails: false` выключает и `modelChoice`, и `errorDetails`). Неизвестное право или не булево значение — `TypeError`. Запрещённое действие — `ActionForbiddenError(action)` до любой записи и до `beforeTurn`. За один вызов службы права спрашиваются не больше раза. История несёт `capabilities`. Вручную — `service.assertCapability(userId, chatId, capability)`.
|
|
86
|
+
- Скрытие частей ответа: `reasoning: false` — мысли модели, `toolDetails: false` — служебные инструменты не уходят в историю, поток и повтор кусков после переподключения; хранилище, модель и `afterTurn` получают всё. Виджеты, браузерные инструменты, вызовы с запросом подтверждения и инструменты с `metadata: { showOnScreen: true }` в описании видны всегда. `modelDetails: false` убирает у чата истории `ownerId`, `modelId`, `providerId`, `reasoning`, у сообщений — `metadata.modelId`, `providerId`, `usage`, у частей и кусков — `providerMetadata`/`callProviderMetadata`/`resultProviderMetadata`, у `data-step-reset` — провайдера; отметки сжатия идут без текста сводки (`summary: ""`, `summaryTokens: 0`) в истории и `getCompactions`. Подробности сбоя провайдера (`metadata.error.details`: `providerId`, `modelId`, `statusCode`, `reason` — одна строка до 300 знаков без секретов, параметров адресов, `org-…`/`proj_…` и e-mail, `resetsAt` — ISO, только будущее не дальше двух лет) хранятся всегда, а в историю и поток идут только при `errorDetails: true` (например, админу); без права — только `error.kind`. Ответ модерации в `reason` может повторять текст человека — отдавайте право только тому, кому этот текст можно видеть. Меню моделей — опция `modelMenu({ userId })`: `false` — `getModelMenu(userId)` бросает `ActionForbiddenError("modelDetails")`. История несёт `modelAcceptsImages` (видит ли модель чата картинки). Права — на того, кто открыл поток.
|
|
87
87
|
- Закрытый чат: `service.closeChat({ ownerId, chatId, code, message })` и `reopenChat({ ownerId, chatId })` (события `chat-closed`, `chat-reopened`), из инструмента — `model.closeChat({ code, message })`. Закрытие — запись `$closed` на ветке: из инструмента — в ответе сразу при вызове, из кода — служебной записью в конце ветки (идёт ход — после него). Правка, «Переделать», выбор ветки и правка ответа до точки закрытия открывают чат; закрыть навсегда — правами (`chatCapabilities` видит `chat.closed`). Закрытие побеждает паузу: ждущие подтверждения и виджеты закрываются отказом, как при «Стопе». В закрытом чате отправка, «Повторить», подтверждения и виджеты — `ChatClosedError`; служебные сообщения пишутся без хода. `listChats` отдаёт `closed` — копию закрытия текущей ветки (`setChatClosed`); права и история берут закрытие с ветки, не записавшуюся копию дописывает следующее открытие истории. Закрытие чатов 0.27 переносится в запись лениво, при первой правке или смене ветки.
|
|
88
88
|
- `beforeCreateChat({ userId, origin? })` — можно ли человеку завести новый чат (`createChat`): `{ code, message }` → `ChatCreationRefusedError`. Чаты объектов и импорт хук не спрашивают. У чата `closed?: { code, message }`, хранилище — необязательный `setChatClosed(chatId, closed | null)`.
|
|
89
89
|
- Чат объекта: `service.openSubjectChat({ ownerId, key, data?, title?, greeting? })` → `{ chat, created }`, один чат на ключ у человека. Инструменты получают `model.subject`, итог хода — `turn.subject`. `listChats(userId, { subjects })` — по умолчанию без чатов объектов.
|
package/dist/access.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
normalizeTurnRefusal
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-CVRDB3NM.js";
|
|
4
4
|
|
|
5
5
|
// src/access/resolved-user.ts
|
|
6
6
|
function allowedUser(resolved) {
|
|
@@ -17,4 +17,4 @@ export {
|
|
|
17
17
|
allowedUser,
|
|
18
18
|
accessRefusedResponse
|
|
19
19
|
};
|
|
20
|
-
//# sourceMappingURL=chunk-
|
|
20
|
+
//# sourceMappingURL=chunk-5UUWSY6V.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/turn-hooks/turn-hooks.ts"],"sourcesContent":["import type { AgentChannel, Chat, ChatError, ChatSubject, FinalAnswerStatus, StoredMessage, TokenUsage } from \"../conversation/types\";\n\n/** Что человек делает с чатом, когда приложение решает, пускать ли ход. */\nexport type TurnAction = \"send\" | \"edit\" | \"retry\" | \"regenerate\" | \"approval\" | \"widget\" | \"notice\";\n\n/** Отказ приложения в ходе: код для программы, текст для человека. */\nexport interface TurnRefusal {\n /** Латиница в нижнем регистре, цифры и `.`, `_`, `-`; от 1 до 64 знаков. */\n code: string;\n /** До 300 знаков; длиннее — обрезается. */\n message: string;\n}\n\n/**\n * Сотрудник, который действует в чужом чате (копия чата человека в песочнице). Пока он есть у хода (`model.staff`),\n * инструменты приложения данные `ownerId` только читают: любая запись в них запрещена — это обязанность инструментов.\n */\nexport interface StaffActor {\n id: string;\n}\n\n/** Событие приложения в ответе агента: `type` — 1–100 знаков, `data` — JSON до 16 КБ. */\nexport interface AppEvent {\n type: string;\n data?: unknown;\n}\n\n/** Записать сообщение человека без хода: агент промолчит (рамки дня, пауза сценария). */\nexport interface TurnHold {\n hold: true;\n}\n\n/** Ход с готовым текстом, без модели: путь `reply` из `deliverNotices`. */\nexport interface TurnReply {\n reply: { text: string };\n}\n\n/** Что `beforeTurn` может решить, кроме «пускать»: отказ, «записать без хода» или «ответить готовым текстом». */\nexport type BeforeTurnDecision = TurnRefusal | TurnHold | TurnReply;\n\n/**\n * Проверка приложения перед ходом: вернуть отказ — ход не начнётся и ничего не запишется. `{ hold: true }` и\n * `{ reply }` — с 0.49 (сценарии): сообщение записывается, хода модели нет.\n */\nexport type BeforeTurnHandler = (input: {\n userId: string;\n chat: Chat;\n action: TurnAction;\n /** Текст сообщения человека (`send`, `edit`). */\n text?: string;\n /** Id сообщения человека (`send`, `edit`): по нему приложение свяжет свой резерв с ходом. */\n messageId?: string;\n /**\n * Ход на пачку сообщений человека (`sendBurstMessage`): id всех её сообщений по порядку, последнее — `messageId`, а\n * `text` — их тексты через пустую строку. Одно решение на всю пачку; счёт сообщений — по длине списка.\n */\n burstMessageIds?: string[];\n /**\n * Ход на пачку начал сам сервер после перезапуска или за упавшую копию (`answerAfterRestart`): вызова, который ждёт\n * ответ или отказ, нет. Отказ дойдёт только до журнала — сообщить человеку может само приложение.\n */\n unattended?: true;\n} & AbandonedAnswers) => Promise<BeforeTurnDecision | void> | BeforeTurnDecision | void;\n\n/**\n * Ответы агента, которые уйдут с текущей ветки, если ход начнётся (`regenerate`, `edit`, `retry`): по ним приложение откатывает\n * побочные дела брошенных ответов (правки формы и т. п.). Ход не начался (`turnNotStarted`) — ветка прежняя, ничего не\n * ушло: тот хук получает те же поля. Посчитаны по ветке на миг проверки; сообщения нет на ветке — полей нет.\n */\nexport interface AbandonedAnswers {\n /** Ответы агента, которые уходят с ветки, сверху вниз; у `edit` без ответов ниже — пустой список. */\n abandonedAnswerIds?: string[];\n /**\n * Ответ, который заменит новый: переделываемый (`regenerate`), ответ на правленый вопрос (`edit`), если был, или\n * оборванный ответ, который «Повторить» (`retry`) заменяет соседом. У «Повторить» вопроса без ответа полей нет.\n */\n previousAnswerId?: string;\n}\n\n/** Почему ход не начался после того, как `beforeTurn` его пропустил. */\nexport type TurnNotStartedReason =\n /** В чате уже идёт ответ, или служба останавливается (`flush`). */\n | \"busy\"\n /** У человека уже идёт предел одновременных ходов. */\n | \"too_many_turns\"\n /** Лимит расходов исчерпан. */\n | \"spending_limit\"\n /** Сообщение с таким id уже есть (повтор запроса). */\n | \"duplicate_message\"\n /** Запрос неверен: сообщение, файл, решение, ответ на виджет. */\n | \"invalid\"\n /** Чат закрыли, пока ход запускался (`closeChat` между проверкой и началом хода). */\n | \"chat_closed\"\n /** Хода и не будет: проверка без хода (`assertTurnAllowed`), ответ ждёт ещё решений, служебное сообщение без ответа. */\n | \"no_turn\"\n /** Сбой (база, сеть): ход не начат. */\n | \"error\";\n\n/**\n * Ход не начался, хотя `beforeTurn` его пропустил: приложение снимает свой резерв. Каждый пропуск `beforeTurn`\n * закрывается ровно одним: ходом (`afterTurn`) или этим хуком. Ошибка хода наружу прежняя; бросок хука — в журнал.\n */\nexport type TurnNotStartedHandler = (input: {\n userId: string;\n chat: Chat;\n action: TurnAction;\n messageId?: string;\n text?: string;\n /** Пачка сообщений, как в `beforeTurn`. */\n burstMessageIds?: string[];\n /** Ход без вызова, как в `beforeTurn`: об отказе (например, `spending_limit`) человеку может сказать только приложение. */\n unattended?: true;\n reason: TurnNotStartedReason;\n} & AbandonedAnswers) => Promise<void> | void;\n\n/** Кто остановил ход: человек («Стоп»), общий срок хода (`agent.turnTimeoutMs`) или остановка службы (`flush`). */\nexport type TurnStopReason = \"user\" | \"timeout\" | \"shutdown\";\n\n/** Итог законченного хода для приложения. */\nexport interface TurnResult {\n ownerId: string;\n /** Кто сделал ход: владелец чата, а в копии у сотрудника — сотрудник. */\n actorId: string;\n chatId: string;\n messageId: string;\n status: FinalAnswerStatus;\n /** Кто остановил ход; только у `status: \"stopped\"` живого хода (у доделанного после перезапуска нет). */\n stopReason?: TurnStopReason;\n /** Ответ упёрся в предел токенов (`finishReason: \"length\"`, см. `agent.maxOutputTokens`) и оборван. */\n lengthCut?: true;\n /**\n * Ход дошёл до предела шагов (`agent.maxSteps`): последний шаг был итоговым, без инструментов. Это «дошли до потолка»,\n * а не обязательно «недоделали»: модель могла закончить дело как раз на последнем шаге. Статус обычный (`complete`).\n * В журнале SDK — строка `[предел шагов]`.\n */\n stepLimit?: true;\n /** Текстовые части ответа подряд. */\n text: string;\n /** Части ответа, как они записаны, кроме служебной части с блоками контекста (`data-frozen-context`). */\n parts: StoredMessage[\"parts\"];\n usage: TokenUsage | null;\n /** Цена хода в долларах; `null` — у модели нет цены. */\n costUsd: number | null;\n /** Вид сбоя и подробности провайдера (`details`): хук на сервере получает их всегда. */\n error?: ChatError;\n /**\n * Каким действием начат ход (см. `TurnAction`). `null` — итог доделан после перезапуска (`recovered`), а ядро не\n * помнит действие; ход по служебному сообщению и тогда — `notice`.\n */\n trigger: TurnAction | null;\n channel: AgentChannel;\n /**\n * Ход сделал сотрудник в копии чата человека (песочница): `ownerId` и `subject` — человека и его исходного чата.\n * При `staff` в данные человека не писать.\n */\n staff?: StaffActor;\n /** Объект чата (урок, заказ…): ключ и данные чата объекта. Нет — обычный чат. */\n subject?: ChatSubject;\n /** Данные чата (`Chat.data`) на момент итога; нет данных — поля нет. */\n chatData?: Record<string, unknown>;\n /** Итог доделан после перезапуска сервера, а не сразу после хода. */\n recovered: boolean;\n}\n\n/**\n * Хук итога хода: зовётся после записи итога и расхода, до `finish` клиенту; пока он идёт, чат занят. Получает любой\n * итог: законченный, остановленный, с ошибкой, прерванный (в том числе восстановлением после перезапуска) и ждущий\n * человека. Ждущий ответ (`awaiting_approval`) после решения человека продолжается тем же сообщением — хук позовут\n * второй раз, с новым статусом. Хук может быть вызван повторно (повтор после сбоя, доделка после перезапуска):\n * ключ повтора — `messageId` + `status`. Возвращённые `events` ложатся в конец ответа частями `data-app-event`.\n */\nexport type AfterTurnHandler = (\n result: TurnResult,\n) => Promise<{ events?: AppEvent[] } | void> | { events?: AppEvent[] } | void;\n\nexport const TURN_REFUSAL_MESSAGE_MAX_LENGTH = 300;\nexport const APP_EVENT_TYPE_MAX_LENGTH = 100;\nexport const APP_EVENT_DATA_MAX_BYTES = 16 * 1024;\n\nconst TURN_REFUSAL_CODE = /^[a-z0-9_.-]{1,64}$/;\n\nexport function isTurnRefusalCode(code: unknown): code is string {\n return typeof code === \"string\" && TURN_REFUSAL_CODE.test(code);\n}\n\n/** Отказ по правилам: код проверен (иначе `TypeError`), текст обрезан до 300 знаков. */\nexport function normalizeTurnRefusal(refusal: TurnRefusal): TurnRefusal {\n if (!isTurnRefusalCode(refusal?.code)) throw new TypeError(\"Код отказа — [a-z0-9_.-], от 1 до 64 знаков\");\n if (typeof refusal.message !== \"string\") throw new TypeError(\"Текст отказа — строка\");\n // По знакам, а не по половинкам: эмодзи на границе не рвётся.\n const message = Array.from(refusal.message).slice(0, TURN_REFUSAL_MESSAGE_MAX_LENGTH).join(\"\");\n return { code: refusal.code, message };\n}\n\n/** Приложение отказало в ходе: ход не начат, ничего не записано. */\nexport class TurnRefusedError extends Error {\n readonly code: string;\n\n constructor(refusal: TurnRefusal) {\n const normalized = normalizeTurnRefusal(refusal);\n super(normalized.message);\n this.name = \"TurnRefusedError\";\n this.code = normalized.code;\n }\n\n get refusal(): TurnRefusal {\n return { code: this.code, message: this.message };\n }\n}\n\n/** Событие по правилам или `TypeError`. Отдаёт копию: данные прошли через JSON. */\nexport function validateAppEvent(event: unknown): AppEvent {\n if (event === null || typeof event !== \"object\") throw new TypeError(\"Событие приложения — объект { type, data? }\");\n const { type, data } = event as { type?: unknown; data?: unknown };\n if (typeof type !== \"string\" || type.length < 1 || type.length > APP_EVENT_TYPE_MAX_LENGTH) {\n throw new TypeError(`Тип события приложения — строка от 1 до ${APP_EVENT_TYPE_MAX_LENGTH} знаков`);\n }\n if (data === undefined) return { type };\n let json: string | undefined;\n try {\n json = JSON.stringify(data);\n } catch {\n json = undefined;\n }\n if (json === undefined) throw new TypeError(\"Данные события приложения — значение JSON\");\n if (new TextEncoder().encode(json).length > APP_EVENT_DATA_MAX_BYTES) {\n throw new TypeError(`Данные события приложения больше ${APP_EVENT_DATA_MAX_BYTES} байт`);\n }\n return { type, data: JSON.parse(json) };\n}\n"],"mappings":";AA+KO,IAAM,kCAAkC;AACxC,IAAM,4BAA4B;AAClC,IAAM,2BAA2B,KAAK;AAE7C,IAAM,oBAAoB;AAEnB,SAAS,kBAAkB,MAA+B;AAC/D,SAAO,OAAO,SAAS,YAAY,kBAAkB,KAAK,IAAI;AAChE;AAGO,SAAS,qBAAqB,SAAmC;AACtE,MAAI,CAAC,kBAAkB,SAAS,IAAI,EAAG,OAAM,IAAI,UAAU,iJAA6C;AACxG,MAAI,OAAO,QAAQ,YAAY,SAAU,OAAM,IAAI,UAAU,iHAAuB;AAEpF,QAAM,UAAU,MAAM,KAAK,QAAQ,OAAO,EAAE,MAAM,GAAG,+BAA+B,EAAE,KAAK,EAAE;AAC7F,SAAO,EAAE,MAAM,QAAQ,MAAM,QAAQ;AACvC;AAGO,IAAM,mBAAN,cAA+B,MAAM;AAAA,EACjC;AAAA,EAET,YAAY,SAAsB;AAChC,UAAM,aAAa,qBAAqB,OAAO;AAC/C,UAAM,WAAW,OAAO;AACxB,SAAK,OAAO;AACZ,SAAK,OAAO,WAAW;AAAA,EACzB;AAAA,EAEA,IAAI,UAAuB;AACzB,WAAO,EAAE,MAAM,KAAK,MAAM,SAAS,KAAK,QAAQ;AAAA,EAClD;AACF;AAGO,SAAS,iBAAiB,OAA0B;AACzD,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,OAAM,IAAI,UAAU,qKAA6C;AAClH,QAAM,EAAE,MAAM,KAAK,IAAI;AACvB,MAAI,OAAO,SAAS,YAAY,KAAK,SAAS,KAAK,KAAK,SAAS,2BAA2B;AAC1F,UAAM,IAAI,UAAU,sMAA2C,yBAAyB,uCAAS;AAAA,EACnG;AACA,MAAI,SAAS,OAAW,QAAO,EAAE,KAAK;AACtC,MAAI;AACJ,MAAI;AACF,WAAO,KAAK,UAAU,IAAI;AAAA,EAC5B,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,SAAS,OAAW,OAAM,IAAI,UAAU,2MAA2C;AACvF,MAAI,IAAI,YAAY,EAAE,OAAO,IAAI,EAAE,SAAS,0BAA0B;AACpE,UAAM,IAAI,UAAU,qLAAoC,wBAAwB,2BAAO;AAAA,EACzF;AACA,SAAO,EAAE,MAAM,MAAM,KAAK,MAAM,IAAI,EAAE;AACxC;","names":[]}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
TurnRefusedError,
|
|
4
4
|
normalizeTurnRefusal,
|
|
5
5
|
validateAppEvent
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-CVRDB3NM.js";
|
|
7
7
|
|
|
8
8
|
// src/conversation/types.ts
|
|
9
9
|
var CURRENT_FORMAT_VERSION = 1;
|
|
@@ -525,7 +525,10 @@ var MIN_SECRET_LENGTH = 8;
|
|
|
525
525
|
var HIDDEN = "[\u0441\u043A\u0440\u044B\u0442\u043E]";
|
|
526
526
|
var SECRET_FIELD = String.raw`[\w-]*(?:password|passwd|secret|token|api[_-]?key|(?:secret|private|access)[_-]?key)(?![\w-])`;
|
|
527
527
|
var FIELD_VALUE = String.raw`(?!\d+(?![^&\s"',;]))(?:"[^"]*"|'[^']*'|[^&\s"',;]+)`;
|
|
528
|
+
var SECRET_LIKE = String.raw`(?=[^\s"',;]*\d)(?=[^\s"',;]*[A-Za-z])[^\s"',;]{8,}`;
|
|
528
529
|
var KNOWN_KEY_PATTERNS = [
|
|
530
|
+
// Маска ключа целиком: провайдер показывает начало и последние знаки ключа.
|
|
531
|
+
/[\w-]*\*{4,}[\w-]*/g,
|
|
529
532
|
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
|
|
530
533
|
/\bsk-[A-Za-z0-9_-]{8,}/g,
|
|
531
534
|
/\b[spr]k_(?:live|test)_[A-Za-z0-9]{8,}/g,
|
|
@@ -547,7 +550,7 @@ function scrubSecrets(text, knownSecrets = []) {
|
|
|
547
550
|
if (secret && secret.length >= MIN_SECRET_LENGTH) scrubbed = scrubbed.split(secret).join(HIDDEN);
|
|
548
551
|
}
|
|
549
552
|
for (const pattern of KNOWN_KEY_PATTERNS) scrubbed = scrubbed.replace(pattern, HIDDEN);
|
|
550
|
-
return scrubbed.replace(URL_PASSWORD, `$1:${HIDDEN}@`).replace(/\b((?:Proxy-)?Authorization\s*[:=]\s*)(?:(Basic|Bearer|Token|Digest)\s+)?[^\s"',;]+/gi, (_, head, scheme) => `${head}${scheme ? `${scheme} ` : ""}${HIDDEN}`).replace(/\
|
|
553
|
+
return scrubbed.replace(URL_PASSWORD, `$1:${HIDDEN}@`).replace(/\b((?:Proxy-)?Authorization\s*[:=]\s*)(?:(Basic|Bearer|Token|Digest)\s+)?[^\s"',;]+/gi, (_, head, scheme) => `${head}${scheme ? `${scheme} ` : ""}${HIDDEN}`).replace(/\b(Bearer\s+)(?!\[скрыто\])[^\s"',;]+/gi, `$1${HIDDEN}`).replace(/([?&;]key=)[^&\s"]+/g, `$1${HIDDEN}`).replace(new RegExp(String.raw`(?<![\w-])(key\s*[:=]\s*)${SECRET_LIKE}`, "gi"), `$1${HIDDEN}`).replace(new RegExp(String.raw`(?<![\w-])((?:token|api[_-]?key|secret|password|passwd|key)\s+)${SECRET_LIKE}`, "gi"), `$1${HIDDEN}`).replace(new RegExp(String.raw`("${SECRET_FIELD}"\s*:\s*)"[^"]*"`, "gi"), `$1"${HIDDEN}"`).replace(
|
|
551
554
|
new RegExp(String.raw`(?<![\w-])(${SECRET_FIELD}\s*[:=]\s*)(?!"\[скрыто\]")(${FIELD_VALUE})`, "gi"),
|
|
552
555
|
(_, head, value) => /^["']/.test(value) ? `${head}${value[0]}${HIDDEN}${value[0]}` : `${head}${HIDDEN}`
|
|
553
556
|
);
|
|
@@ -5326,6 +5329,100 @@ ${summary}
|
|
|
5326
5329
|
</answer_progress>`;
|
|
5327
5330
|
}
|
|
5328
5331
|
|
|
5332
|
+
// src/provider-errors/provider-error-details.ts
|
|
5333
|
+
import { APICallError as APICallError4 } from "ai";
|
|
5334
|
+
var MAX_REASON_LENGTH = 300;
|
|
5335
|
+
var MAX_RESET_AHEAD_MS = 2 * 366 * 24 * 60 * 60 * 1e3;
|
|
5336
|
+
function providerErrorDetails(error, context) {
|
|
5337
|
+
const cause = underlyingError(error);
|
|
5338
|
+
const statusCode = errorStatusCode(cause);
|
|
5339
|
+
const { message, metadata } = providerMessageOf(cause);
|
|
5340
|
+
const reason = message === void 0 ? void 0 : shownReason(message, environmentSecrets(context.env, context.catalog));
|
|
5341
|
+
const nowMs = (context.now ?? Date.now)();
|
|
5342
|
+
const found = resetFromMetadata(metadata, nowMs) ?? (message === void 0 ? void 0 : resetFromText(message));
|
|
5343
|
+
const resetsAt = found !== void 0 && found > nowMs && found - nowMs <= MAX_RESET_AHEAD_MS ? new Date(found).toISOString() : void 0;
|
|
5344
|
+
return {
|
|
5345
|
+
providerId: context.providerId,
|
|
5346
|
+
modelId: context.modelId,
|
|
5347
|
+
...statusCode !== void 0 && { statusCode },
|
|
5348
|
+
...reason !== void 0 && reason !== "" && { reason },
|
|
5349
|
+
...resetsAt !== void 0 && { resetsAt }
|
|
5350
|
+
};
|
|
5351
|
+
}
|
|
5352
|
+
function providerMessageOf(error) {
|
|
5353
|
+
if (APICallError4.isInstance(error)) {
|
|
5354
|
+
const fromBody = typeof error.responseBody === "string" ? fromJson(error.responseBody) : void 0;
|
|
5355
|
+
if (fromBody?.message !== void 0) return fromBody;
|
|
5356
|
+
const raw = [error.responseBody, error.message].find(isText);
|
|
5357
|
+
return { ...raw !== void 0 && { message: raw }, metadata: fromBody?.metadata };
|
|
5358
|
+
}
|
|
5359
|
+
if (typeof error === "string") return isText(error) ? { message: error } : {};
|
|
5360
|
+
return fromObject(error) ?? {};
|
|
5361
|
+
}
|
|
5362
|
+
function fromJson(body) {
|
|
5363
|
+
try {
|
|
5364
|
+
return fromObject(JSON.parse(body));
|
|
5365
|
+
} catch {
|
|
5366
|
+
return void 0;
|
|
5367
|
+
}
|
|
5368
|
+
}
|
|
5369
|
+
function fromObject(value) {
|
|
5370
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
5371
|
+
const fields = value;
|
|
5372
|
+
if (typeof fields.error === "object" && fields.error !== null) {
|
|
5373
|
+
const nested = fields.error;
|
|
5374
|
+
if (isText(nested.message)) return { message: nested.message, metadata: nested.metadata };
|
|
5375
|
+
}
|
|
5376
|
+
if (isText(fields.error)) return { message: fields.error, metadata: fields.metadata };
|
|
5377
|
+
if (isText(fields.message)) return { message: fields.message, metadata: fields.metadata };
|
|
5378
|
+
return void 0;
|
|
5379
|
+
}
|
|
5380
|
+
var isText = (value) => typeof value === "string" && value.trim() !== "";
|
|
5381
|
+
var URL_WITH_QUERY = /(https?:\/\/[^\s?#"'<>]+)[?#][^\s"'<>]*/g;
|
|
5382
|
+
var ACCOUNT_IDS = [/\borg-[A-Za-z0-9]{6,}/g, /\bproj_[A-Za-z0-9]{6,}/g, /[\w.+-]+@[\w-]+(?:\.[\w-]+)+/g];
|
|
5383
|
+
function shownReason(message, knownSecrets) {
|
|
5384
|
+
const oneLine2 = message.replace(/\s*[\r\n]+\s*/g, " ").trim();
|
|
5385
|
+
let shown = scrubSecrets(oneLine2.replace(URL_WITH_QUERY, "$1"), knownSecrets);
|
|
5386
|
+
for (const pattern of ACCOUNT_IDS) shown = shown.replace(pattern, "[\u0441\u043A\u0440\u044B\u0442\u043E]");
|
|
5387
|
+
return cutAtCharBoundary(shown, MAX_REASON_LENGTH);
|
|
5388
|
+
}
|
|
5389
|
+
var RESET_FIELD = /reset/i;
|
|
5390
|
+
var RELATIVE_FIELD = /sec|after|in$|_in_|ms$|_ms|Ms$/i;
|
|
5391
|
+
var MS_FIELD = /ms$|_ms|Ms$/;
|
|
5392
|
+
var MIN_UNIX_SECONDS = 1e9;
|
|
5393
|
+
function resetFromMetadata(metadata, nowMs) {
|
|
5394
|
+
if (typeof metadata !== "object" || metadata === null) return void 0;
|
|
5395
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
5396
|
+
if (!RESET_FIELD.test(key)) continue;
|
|
5397
|
+
const parsed = typeof value === "number" ? fromNumber(key, value, nowMs) : typeof value === "string" ? fromDateText(value) : void 0;
|
|
5398
|
+
if (parsed !== void 0) return parsed;
|
|
5399
|
+
}
|
|
5400
|
+
return void 0;
|
|
5401
|
+
}
|
|
5402
|
+
function fromNumber(key, value, nowMs) {
|
|
5403
|
+
if (!Number.isFinite(value) || value <= 0) return void 0;
|
|
5404
|
+
if (RELATIVE_FIELD.test(key) || value < MIN_UNIX_SECONDS) return nowMs + (MS_FIELD.test(key) ? value : value * 1e3);
|
|
5405
|
+
return value < 1e12 ? value * 1e3 : value;
|
|
5406
|
+
}
|
|
5407
|
+
var LIMIT_MESSAGE_START = /^[^'"«“]{0,80}?\b(?:limit|budget|quota)\b/i;
|
|
5408
|
+
var QUOTED = /'[^']*'|"[^"]*"|«[^»]*»|“[^”]*”/g;
|
|
5409
|
+
var RESET_IN_TEXT = /\bresets?\s+(?:at\s+|on\s+)?(\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?)?)/i;
|
|
5410
|
+
function resetFromText(message) {
|
|
5411
|
+
if (!LIMIT_MESSAGE_START.test(message)) return void 0;
|
|
5412
|
+
const found = RESET_IN_TEXT.exec(message.replace(QUOTED, " "))?.[1];
|
|
5413
|
+
return found === void 0 ? void 0 : fromDateText(found);
|
|
5414
|
+
}
|
|
5415
|
+
function fromDateText(text) {
|
|
5416
|
+
const trimmed = text.trim();
|
|
5417
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?)(Z|[+-]\d{2}:?\d{2})?)?$/i.exec(trimmed);
|
|
5418
|
+
if (!match) return void 0;
|
|
5419
|
+
const [, year, month, day, time, zone] = match;
|
|
5420
|
+
const calendarDay = new Date(Date.UTC(Number(year), Number(month) - 1, Number(day)));
|
|
5421
|
+
if (calendarDay.getUTCMonth() !== Number(month) - 1 || calendarDay.getUTCDate() !== Number(day)) return void 0;
|
|
5422
|
+
const parsed = Date.parse(`${year}-${month}-${day}${time === void 0 ? "" : `T${time}${zone ?? "Z"}`}`);
|
|
5423
|
+
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
5424
|
+
}
|
|
5425
|
+
|
|
5329
5426
|
// src/agent-run/answer-saver.ts
|
|
5330
5427
|
import { readUIMessageStream } from "ai";
|
|
5331
5428
|
|
|
@@ -6608,6 +6705,14 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
6608
6705
|
let errors = [];
|
|
6609
6706
|
let terminal = [];
|
|
6610
6707
|
let errorKind;
|
|
6708
|
+
let errorDetails;
|
|
6709
|
+
const detailsOf2 = (error2, failedProviderId) => providerErrorDetails(error2, {
|
|
6710
|
+
providerId: failedProviderId,
|
|
6711
|
+
modelId,
|
|
6712
|
+
env: input.env,
|
|
6713
|
+
catalog: input.catalog,
|
|
6714
|
+
now: () => (input.now ?? (() => /* @__PURE__ */ new Date()))().getTime()
|
|
6715
|
+
});
|
|
6611
6716
|
let providerId = input.providerId;
|
|
6612
6717
|
let answeredProviderId = input.providerId;
|
|
6613
6718
|
let viewer;
|
|
@@ -6650,6 +6755,7 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
6650
6755
|
});
|
|
6651
6756
|
const fallbackProviderIds = allFallbackIds.filter((candidate) => outages.pausedKind(candidate) === void 0);
|
|
6652
6757
|
let firstOutage;
|
|
6758
|
+
let firstOutageDetails;
|
|
6653
6759
|
const primaryPaused = outages.admit(input.providerId);
|
|
6654
6760
|
if (primaryPaused !== void 0 && fallbackProviderIds[0] !== void 0) {
|
|
6655
6761
|
providerId = fallbackProviderIds[0];
|
|
@@ -6746,7 +6852,9 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
6746
6852
|
} catch (error2) {
|
|
6747
6853
|
if (error2 instanceof MissingProviderKeyError) logError(error2.message);
|
|
6748
6854
|
if (!input.abortSignal.aborted) {
|
|
6749
|
-
|
|
6855
|
+
const kind = classify(error2);
|
|
6856
|
+
errorKind = errorKindForPerson(kind, firstOutage);
|
|
6857
|
+
errorDetails = errorKind === firstOutage ? firstOutageDetails : detailsOf2(error2, providerId);
|
|
6750
6858
|
errors = [{ type: "error", errorText: errorKind }];
|
|
6751
6859
|
}
|
|
6752
6860
|
break;
|
|
@@ -6790,7 +6898,10 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
6790
6898
|
let next = stepsDone < maxSteps ? decideNextAttempt(result, nextState()) : { action: "fail" };
|
|
6791
6899
|
const outage = isProviderOutage(result.kind) ? result.kind : void 0;
|
|
6792
6900
|
if (outage) {
|
|
6793
|
-
if (providerId === input.providerId
|
|
6901
|
+
if (providerId === input.providerId && firstOutage === void 0) {
|
|
6902
|
+
firstOutage = outage;
|
|
6903
|
+
firstOutageDetails = detailsOf2(result.error, providerId);
|
|
6904
|
+
}
|
|
6794
6905
|
outages.report({
|
|
6795
6906
|
providerId,
|
|
6796
6907
|
kind: outage,
|
|
@@ -6879,6 +6990,7 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
6879
6990
|
continue;
|
|
6880
6991
|
}
|
|
6881
6992
|
errorKind = errorKindForPerson(result.kind, firstOutage);
|
|
6993
|
+
errorDetails = errorKind === firstOutage ? firstOutageDetails : detailsOf2(result.error, providerId);
|
|
6882
6994
|
errors = errorKind === result.kind ? attempt.errors : [{ type: "error", errorText: errorKind }];
|
|
6883
6995
|
terminal = attempt.terminal;
|
|
6884
6996
|
logError(
|
|
@@ -6916,6 +7028,7 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
6916
7028
|
if (!input.abortSignal.aborted) {
|
|
6917
7029
|
if (error2 instanceof AgentViewerError) logError(`[agent.viewer] \u0447\u0430\u0442=${input.chatId} \u0445\u043E\u0434 \u043D\u0435 \u043D\u0430\u0447\u0430\u0442: ${error2.message}`);
|
|
6918
7030
|
errorKind = error2 instanceof AgentViewerError ? "other" : classifyProviderError(error2, void 0);
|
|
7031
|
+
errorDetails = void 0;
|
|
6919
7032
|
errors = [{ type: "error", errorText: errorKind }];
|
|
6920
7033
|
}
|
|
6921
7034
|
}
|
|
@@ -6986,7 +7099,7 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
6986
7099
|
for (const chunk of heldText?.release() ?? []) emitShown(chunk);
|
|
6987
7100
|
turnUsage.closeAttempt();
|
|
6988
7101
|
const usage = messageUsage();
|
|
6989
|
-
const error = status === "error" ? { kind: errorKind ?? "other" } : null;
|
|
7102
|
+
const error = status === "error" ? { kind: errorKind ?? "other", ...errorDetails && { details: errorDetails } } : null;
|
|
6990
7103
|
const answeredByBackup = answeredProviderId !== input.providerId && { providerId: answeredProviderId };
|
|
6991
7104
|
let finished = false;
|
|
6992
7105
|
if (!takeover.signal.aborted) {
|
|
@@ -9419,10 +9532,12 @@ var CHAT_CAPABILITIES = [
|
|
|
9419
9532
|
"reasoning",
|
|
9420
9533
|
"toolDetails",
|
|
9421
9534
|
"steer",
|
|
9422
|
-
"modelDetails"
|
|
9535
|
+
"modelDetails",
|
|
9536
|
+
"errorDetails"
|
|
9423
9537
|
];
|
|
9538
|
+
var OFF_BY_DEFAULT = /* @__PURE__ */ new Set(["errorDetails"]);
|
|
9424
9539
|
function resolveChatCapabilities(given) {
|
|
9425
|
-
const resolved = Object.fromEntries(CHAT_CAPABILITIES.map((capability) => [capability,
|
|
9540
|
+
const resolved = Object.fromEntries(CHAT_CAPABILITIES.map((capability) => [capability, !OFF_BY_DEFAULT.has(capability)]));
|
|
9426
9541
|
if (given === void 0 || given === null) return resolved;
|
|
9427
9542
|
if (typeof given !== "object") throw new TypeError("\u041F\u0440\u0430\u0432\u0430 \u0447\u0430\u0442\u0430 \u2014 \u043E\u0431\u044A\u0435\u043A\u0442 { \u043F\u0440\u0430\u0432\u043E: boolean }");
|
|
9428
9543
|
const unknown = Object.keys(given).find((key) => !CHAT_CAPABILITIES.includes(key));
|
|
@@ -9433,7 +9548,10 @@ function resolveChatCapabilities(given) {
|
|
|
9433
9548
|
if (typeof value !== "boolean") throw new TypeError(`\u041F\u0440\u0430\u0432\u043E \xAB${capability}\xBB \u2014 true \u0438\u043B\u0438 false`);
|
|
9434
9549
|
resolved[capability] = value;
|
|
9435
9550
|
}
|
|
9436
|
-
if (!resolved.modelDetails)
|
|
9551
|
+
if (!resolved.modelDetails) {
|
|
9552
|
+
resolved.modelChoice = false;
|
|
9553
|
+
resolved.errorDetails = false;
|
|
9554
|
+
}
|
|
9437
9555
|
return resolved;
|
|
9438
9556
|
}
|
|
9439
9557
|
var ActionForbiddenError = class extends Error {
|
|
@@ -11465,6 +11583,12 @@ var SHOWS_EVERYTHING = {
|
|
|
11465
11583
|
};
|
|
11466
11584
|
var MODEL_METADATA_KEYS = ["modelId", "providerId", "usage"];
|
|
11467
11585
|
var MODEL_CHAT_KEYS = ["ownerId", "modelId", "providerId", "reasoning"];
|
|
11586
|
+
var withoutErrorDetails = (metadata) => {
|
|
11587
|
+
const error = metadata.error;
|
|
11588
|
+
if (typeof error !== "object" || error === null || !("details" in error)) return metadata;
|
|
11589
|
+
const { details: _details, ...rest } = error;
|
|
11590
|
+
return { ...metadata, error: rest };
|
|
11591
|
+
};
|
|
11468
11592
|
var without = (carrier, keys) => {
|
|
11469
11593
|
if (!keys.some((key) => key in carrier)) return carrier;
|
|
11470
11594
|
const rest = { ...carrier };
|
|
@@ -11502,10 +11626,15 @@ function clientVisibilityOf(capabilities, sdkScreenTool) {
|
|
|
11502
11626
|
const hidesReasoning = !capabilities.reasoning;
|
|
11503
11627
|
const hidesTools = !capabilities.toolDetails;
|
|
11504
11628
|
const hidesModel = capabilities.modelDetails === false;
|
|
11505
|
-
|
|
11629
|
+
const hidesErrorDetails = hidesModel || capabilities.errorDetails !== true;
|
|
11630
|
+
if (!hidesReasoning && !hidesTools && !hidesModel && !hidesErrorDetails) return SHOWS_EVERYTHING;
|
|
11506
11631
|
const hidesCall = (toolName, call) => hidesTools && !sdkScreenTool(toolName) && !markedForScreen(call);
|
|
11507
11632
|
const hiddenMetadataKeys = [...hidesReasoning ? ["reasoningDurationsMs"] : [], ...hidesModel ? MODEL_METADATA_KEYS : []];
|
|
11508
|
-
const visibleMetadata = (metadata) =>
|
|
11633
|
+
const visibleMetadata = (metadata) => {
|
|
11634
|
+
if (typeof metadata !== "object" || metadata === null) return metadata;
|
|
11635
|
+
const shown = without(metadata, hiddenMetadataKeys);
|
|
11636
|
+
return hidesErrorDetails ? withoutErrorDetails(shown) : shown;
|
|
11637
|
+
};
|
|
11509
11638
|
const withoutProviderData2 = (carrier) => hidesModel ? without(carrier, PROVIDER_METADATA_KEYS) : hidesReasoning ? withoutReasoningDetails(carrier) : carrier;
|
|
11510
11639
|
return {
|
|
11511
11640
|
showsEverything: false,
|
|
@@ -12216,10 +12345,18 @@ function createChatService(input) {
|
|
|
12216
12345
|
scope.capabilities ??= askCapabilities(userId, chat);
|
|
12217
12346
|
return scope.capabilities;
|
|
12218
12347
|
}
|
|
12348
|
+
let warnedErrorDetailsWithoutModel = false;
|
|
12349
|
+
function warnErrorDetailsWithoutModel(given) {
|
|
12350
|
+
if (warnedErrorDetailsWithoutModel || given?.errorDetails !== true || given.modelDetails !== false) return;
|
|
12351
|
+
warnedErrorDetailsWithoutModel = true;
|
|
12352
|
+
logError("chatCapabilities: errorDetails: true \u043F\u0440\u0438 modelDetails: false \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D\u043E \u2014 \u043F\u043E\u0434\u0440\u043E\u0431\u043D\u043E\u0441\u0442\u0438 \u0441\u0431\u043E\u044F \u043D\u0430\u0437\u044B\u0432\u0430\u044E\u0442 \u043C\u043E\u0434\u0435\u043B\u044C \u0438 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430.");
|
|
12353
|
+
}
|
|
12219
12354
|
async function askCapabilities(userId, chat) {
|
|
12220
12355
|
if (!options2.chatCapabilities) return resolveChatCapabilities(void 0);
|
|
12221
12356
|
try {
|
|
12222
|
-
|
|
12357
|
+
const given = await options2.chatCapabilities({ userId, chat });
|
|
12358
|
+
warnErrorDetailsWithoutModel(given);
|
|
12359
|
+
return resolveChatCapabilities(given);
|
|
12223
12360
|
} catch (error) {
|
|
12224
12361
|
logError(`chatCapabilities \u0443\u043F\u0430\u043B \u0438\u043B\u0438 \u0432\u0435\u0440\u043D\u0443\u043B \u043D\u0435\u0432\u0435\u0440\u043D\u044B\u0435 \u043F\u0440\u0430\u0432\u0430 (\u0447\u0430\u0442 ${chat.id}): ${error instanceof Error ? error.message : String(error)}`);
|
|
12225
12362
|
throw error;
|
|
@@ -14284,4 +14421,4 @@ export {
|
|
|
14284
14421
|
InvalidWidgetAnswerError,
|
|
14285
14422
|
createChatService
|
|
14286
14423
|
};
|
|
14287
|
-
//# sourceMappingURL=chunk-
|
|
14424
|
+
//# sourceMappingURL=chunk-EWEEYX6U.js.map
|