@ykurilov/agent-chat-react 0.63.2 → 0.65.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 +12 -0
- package/README.md +17 -0
- package/dist/{chunk-Y5HL6THS.js → chunk-JLGBVDAR.js} +50 -8
- package/dist/chunk-JLGBVDAR.js.map +1 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/panel.js +1 -1
- package/dist/types/agent-chat-extensions.d.ts +12 -0
- package/dist/types/chat-types.d.ts +17 -3
- package/dist/types/index.d.ts +4 -3
- package/dist/types/labels.d.ts +12 -1
- package/dist/types/notices/agent-notices.d.ts +11 -1
- package/dist/types/skin-contract.d.ts +14 -2
- package/package.json +1 -1
- package/dist/chunk-Y5HL6THS.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @ykurilov/agent-chat-react
|
|
2
2
|
|
|
3
|
+
## 0.65.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 814ee7f: Пометка «Агент написал первым — человек молчал 1 ч 7 мин» перед ответом, который агент написал сам. Ядро: право чата `appNotices` (по умолчанию выключено) — служебные пометки приложения (`kind: "app"`) уходят в историю без текста для модели и только через белый список `appNoticeViews` (имя → безопасные данные для экрана; без функции пометка не уходит); `modelDetails: false` выключает и `appNotices`. React: `noticeLines` у `AgentChat` (имя пометки → строка), договор скина 27 — необязательный `ServiceLine`. Сценарии: в данных пометки `silentMs` (от последнего сообщения человека) и `exit`, проекция для экрана регистрируется в `withChat`, готовые строки `flowNoticeLines()` с подписями `russianFlowNoticeLabels`/`englishFlowNoticeLabels` (`FlowLabels.notices`); `FlowTestChat` передаёт их в `renderChat(chatId, { noticeLines })`. Тандем рисует `ServiceLine`. Как подключить — CHANGES.md, раздел 0.65.0.
|
|
8
|
+
|
|
9
|
+
## 0.64.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 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`, миграций нет.
|
|
14
|
+
|
|
3
15
|
## 0.63.2
|
|
4
16
|
|
|
5
17
|
No changes in this release.
|
package/README.md
CHANGED
|
@@ -39,6 +39,11 @@ import { AgentChat, ChatLabelsProvider, russianLabels } from "@ykurilov/agent-ch
|
|
|
39
39
|
необязательными `wide` (по ширине ответа со своей пропорцией) и `caption` (подпись под картинкой). Договор скина
|
|
40
40
|
версии 26; скин версии 25 работает без правок: плитка ближайшей обычной пропорции без подписи —
|
|
41
41
|
[docs/skin-v26.md](https://github.com/ykurilov/agent-chat-sdk/blob/main/docs/skin-v26.md).
|
|
42
|
+
- Служебные пометки приложения (`kind: "app"`) — серой строкой перед ответом агента: «Агент написал первым — человек
|
|
43
|
+
молчал 1 ч 7 мин». Нужны право чата `appNotices` на сервере (по умолчанию выключено), `noticeLines` у `AgentChat`
|
|
44
|
+
(имя пометки → строка; для сценариев — `flowNoticeLines()`) и `ServiceLine` у скина. Договор скина версии 27; скин
|
|
45
|
+
версии 26 работает без правок: пометок не видно —
|
|
46
|
+
[docs/skin-v27.md](https://github.com/ykurilov/agent-chat-sdk/blob/main/docs/skin-v27.md).
|
|
42
47
|
- `ChatWorkspace` — список чатов и открытый чат вместе: создать, переименовать, удалить (нужен `chatList: true` на сервере). Хук — `useChatList`. Скину дополнительно нужны `ChatWorkspace` и `ChatList` (`ChatListSkin`, договор версии 7).
|
|
43
48
|
- `AgentMemory` — экран долгой памяти агента: записи по разделам, добавить, изменить, удалить, «Стереть всю память» (нужна опция `memory` на сервере, пакет `@ykurilov/agent-chat-memory`). `api` — `createMemoryApi({ api: "/api/chat" })`, создавать один раз; `skin` — `MemorySkin` (`tandemMemorySkin`). Хук — `useMemoryNotes`. Без памяти на сервере экран пишет, что память не включена. Договор скина версии 11 — отдельный `MemorySkin`, `ChatSkin` не менялся ([contracts/memory.md](../../specs/017-long-term-memory/contracts/memory.md)).
|
|
44
49
|
- Панель агента: вход `@ykurilov/agent-chat-react/panel`; основной вход его не подтягивает. `AgentPanelProvider` ставится в корневой макет выше страниц (`api`, `userKey`, `browserTools`, `mode` — `overlay` или `push`, `width`, `onDataChanged`, `fetch` — свой `fetch` только для списка чатов), `AgentPanel` рисует кнопку, рамку и чат через скин. Хуки: `useAgentPanel` (`open`, `openChat`, `openNewChat({ draft })`, `pageInset` — отступ страницы в режиме `push`, на экране ≤ 640 px он 0, `busy`), `useAgentPlace({ description, data? })` (где человек; модель видит только описание, `data` — только инструменты), `useAgentDataChanges(tag, handler)` (страница узнаёт, что инструмент изменил данные). Браузерные инструменты — функции в `browserTools` с теми же именами, что описания на сервере (`BrowserToolImplementations`); нет функции — модель получает «Инструмент недоступен на этой странице» (надпись `agentPanelToolUnavailable`). Подробно — [docs/getting-started.md](../../docs/getting-started.md#панель-агента-в-приложении-по-желанию). Общие части: `AgentAppBridge` (связь чата с приложением; `appBridge` в `AgentChat`, `useAgentChat`, `ChatWorkspace`), `initialDraft` (текст в поле ввода при открытии чата), `rememberKey` в `useChatList` (свой ключ запомненного чата). Договор скина версии 12 — отдельный `AgentPanelSkin` (`AgentPanelLauncher`, `AgentPanel`), надписи `agentPanel…` в словаре; `ChatSkin`, `ChatListSkin` и `MemorySkin` не менялись ([contracts/skin.md](../../specs/021-agent-panel/contracts/skin.md)).
|
|
@@ -125,6 +130,18 @@ import { AgentChat, ChatLabelsProvider, russianLabels } from "@ykurilov/agent-ch
|
|
|
125
130
|
(`process.env.NODE_ENV !== "production"`) в консоли подсказка, какой адрес вписать.
|
|
126
131
|
Состояние — `refusal: "signed_out" | "too_many_turns" | "forbidden_origin" | null` у `useAgentChat`; скин получает их в `ErrorNotice` как `kind`
|
|
127
132
|
(текст — `labels.errors[kind]`).
|
|
133
|
+
Сбой ответа подписывается `labels.errors[kind]`. Своя подпись — функция `labels.errorText(error, plain)`: `error` — вид и,
|
|
134
|
+
если сервер их отдал (право `errorDetails`), подробности `details` (`providerId`, `modelId`, `statusCode`, `reason`,
|
|
135
|
+
`resetsAt`); `plain` — `errors[kind]`. Текст уходит скину в `ErrorNotice` как `text` и диктору. Функция бросила
|
|
136
|
+
исключение — подпись `plain` и одна запись в консоль. Например, админу:
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
const labels = {
|
|
140
|
+
...russianLabels,
|
|
141
|
+
errorText: (error, plain) =>
|
|
142
|
+
error.details?.resetsAt ? `${plain}. Предел снимут ${new Date(error.details.resetsAt).toLocaleDateString("ru")}` : plain,
|
|
143
|
+
};
|
|
144
|
+
```
|
|
128
145
|
Ответ брошен упавшей копией сервера — экран сам перечитывает историю раз в 3 с и через 30 с показывает «прерван» с «Повторить»; обновлять страницу не нужно.
|
|
129
146
|
Подробности — [contracts/liveness.md](../../specs/002-fast-feed-and-liveness/contracts/liveness.md).
|
|
130
147
|
|
|
@@ -214,6 +214,20 @@ function resetDate(moment, timeZone = "UTC", withTime = false) {
|
|
|
214
214
|
var resetPhrase = (info) => info.resetsAt ? `, \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u0441\u044F ${resetDate(info.resetsAt, info.timeZone, info.period === "rolling")}` : "";
|
|
215
215
|
var mergeDate = new Intl.DateTimeFormat("ru-RU", { day: "numeric", month: "long", hour: "2-digit", minute: "2-digit" });
|
|
216
216
|
var DEFAULT_VOICE_LIMIT_SECONDS = 300;
|
|
217
|
+
var reportedErrorTextFailure = false;
|
|
218
|
+
function customErrorText(labels, error) {
|
|
219
|
+
if (!error || !labels.errorText) return void 0;
|
|
220
|
+
const plain = labels.errors[error.kind];
|
|
221
|
+
try {
|
|
222
|
+
return labels.errorText(error, plain);
|
|
223
|
+
} catch (failure) {
|
|
224
|
+
if (!reportedErrorTextFailure) {
|
|
225
|
+
reportedErrorTextFailure = true;
|
|
226
|
+
console.error("labels.errorText \u0443\u043F\u0430\u043B \u2014 \u043F\u043E\u043A\u0430\u0437\u0430\u043D\u0430 \u043E\u0431\u044B\u0447\u043D\u0430\u044F \u043F\u043E\u0434\u043F\u0438\u0441\u044C \u0441\u0431\u043E\u044F.", failure);
|
|
227
|
+
}
|
|
228
|
+
return plain;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
217
231
|
function voiceErrorText(errors, kind, details) {
|
|
218
232
|
if (kind !== "time_limit") return errors[kind] ?? errors.other;
|
|
219
233
|
const label = errors.time_limit;
|
|
@@ -2173,8 +2187,17 @@ function useBackgroundTasks({ chatUrl: chatUrl2, enabled, version, fetch: custom
|
|
|
2173
2187
|
|
|
2174
2188
|
// src/notices/agent-notices.ts
|
|
2175
2189
|
var AGENT_NOTICE_PART_TYPE = "data-agent-notice";
|
|
2190
|
+
var isAgentNoticePart = (part) => part.type === AGENT_NOTICE_PART_TYPE;
|
|
2176
2191
|
function agentNoticesOf(message) {
|
|
2177
|
-
return rawNoticesOf(message).filter((notice) => notice.kind !== STAFF_NOTICE_KIND);
|
|
2192
|
+
return rawNoticesOf(message).filter((notice) => notice.kind !== STAFF_NOTICE_KIND && notice.kind !== APP_NOTICE_KIND);
|
|
2193
|
+
}
|
|
2194
|
+
var APP_NOTICE_KIND = "app";
|
|
2195
|
+
function appNoticesOf(message) {
|
|
2196
|
+
return rawNoticesOf(message).flatMap((notice) => {
|
|
2197
|
+
if (notice.kind !== APP_NOTICE_KIND) return [];
|
|
2198
|
+
const { name, data } = notice;
|
|
2199
|
+
return typeof name === "string" ? [{ name, data }] : [];
|
|
2200
|
+
});
|
|
2178
2201
|
}
|
|
2179
2202
|
var STAFF_NOTICE_KIND = "staff";
|
|
2180
2203
|
function staffRepliesOf(message) {
|
|
@@ -4079,7 +4102,7 @@ function announcementFor(messages, labels) {
|
|
|
4079
4102
|
const answer = messages.findLast((message) => message.role === "assistant");
|
|
4080
4103
|
if (!answer) return "";
|
|
4081
4104
|
const status = answer.metadata?.status ?? "complete";
|
|
4082
|
-
if (status === "error") return labels.errors[answer.metadata?.error?.kind ?? "other"];
|
|
4105
|
+
if (status === "error") return customErrorText(labels, answer.metadata?.error) ?? labels.errors[answer.metadata?.error?.kind ?? "other"];
|
|
4083
4106
|
if (status === "stopped") return labels.answerStopped;
|
|
4084
4107
|
if (status === "interrupted") return labels.answerInterrupted;
|
|
4085
4108
|
if (status === "awaiting_approval") return widgetQuestionOf(answer, labels) ?? labels.approvalAnnouncement;
|
|
@@ -5776,7 +5799,7 @@ function SubjectAgentChat(props) {
|
|
|
5776
5799
|
if (found.state === "failed") return /* @__PURE__ */ jsx9(props.skin.ErrorNotice, { kind: "other", onRetry: found.retry });
|
|
5777
5800
|
return /* @__PURE__ */ jsx9(SubjectChatLoading, { skin: props.skin });
|
|
5778
5801
|
}
|
|
5779
|
-
function AgentChatBody({ api, chatId, skin, liveness, voice, voiceDialog, attachments: attachmentsPlugin, spendingLimits, onBusyChange, appBridge, initialDraft, answerEditing, onFeedChange, messageActions, onTurnFinished, onTurnRefused, composerActions, onAppEvent, onChatAccessChange, onStateChange, onModelChosen, onChatUnavailable, reloadKey, steering, widgets, diagrams, liveDemos, images, videos, appEvents, workView, stepPhrases }) {
|
|
5802
|
+
function AgentChatBody({ api, chatId, skin, liveness, voice, voiceDialog, attachments: attachmentsPlugin, spendingLimits, onBusyChange, appBridge, initialDraft, answerEditing, onFeedChange, messageActions, onTurnFinished, onTurnRefused, composerActions, onAppEvent, onChatAccessChange, onStateChange, onModelChosen, onChatUnavailable, reloadKey, steering, widgets, diagrams, liveDemos, images, videos, appEvents, workView, stepPhrases, noticeLines }) {
|
|
5780
5803
|
const spendingNoticeId = useId2();
|
|
5781
5804
|
const closedNoticeId = useId2();
|
|
5782
5805
|
const chat = useAgentChat({
|
|
@@ -5817,9 +5840,10 @@ function AgentChatBody({ api, chatId, skin, liveness, voice, voiceDialog, attach
|
|
|
5817
5840
|
...videos && { videos },
|
|
5818
5841
|
...appEvents && { appEvents },
|
|
5819
5842
|
...workView && { workView },
|
|
5820
|
-
...stepPhrases && { stepPhrases }
|
|
5843
|
+
...stepPhrases && { stepPhrases },
|
|
5844
|
+
...noticeLines && { noticeLines }
|
|
5821
5845
|
}),
|
|
5822
|
-
[widgets, diagrams, liveDemos, imageClients, videos, appEvents, workView, stepPhrases]
|
|
5846
|
+
[widgets, diagrams, liveDemos, imageClients, videos, appEvents, workView, stepPhrases, noticeLines]
|
|
5823
5847
|
);
|
|
5824
5848
|
const scrollRef = useRef17(null);
|
|
5825
5849
|
const feedRef = useRef17(null);
|
|
@@ -5985,7 +6009,10 @@ ${text}` : text),
|
|
|
5985
6009
|
const NoticeView = skin.AgentNoticeView;
|
|
5986
6010
|
const notices2 = agentNoticesOf(message);
|
|
5987
6011
|
const staffReplies = staffRepliesOf(message);
|
|
5988
|
-
const
|
|
6012
|
+
const appNotices = appNoticesOf(message);
|
|
6013
|
+
const serviceLines = appNotices.length > 0 && /* @__PURE__ */ jsx9(ServiceLines, { skin, notices: appNotices, lines: extensions.noticeLines });
|
|
6014
|
+
const onlyAppNotices = appNotices.length > 0 && notices2.length === 0 && staffReplies.length === 0 && message.parts.every(isAgentNoticePart);
|
|
6015
|
+
const content = onlyAppNotices ? /* @__PURE__ */ jsx9(Fragment2, {}) : staffReplies.length > 0 ? /* @__PURE__ */ jsx9(StaffReplies, { skin, replies: staffReplies, label: labels.notices.staffSaid }) : notices2.length > 0 ? NoticeView ? /* @__PURE__ */ jsx9(Fragment2, { children: notices2.map((notice2, noticeIndex) => /* @__PURE__ */ jsx9(NoticeView, { ...noticeViewOf(notice2, noticeControls) }, noticeIndex)) }) : (
|
|
5989
6016
|
// Скин без плашек (v16): короткая строка из словаря, не пустой пузырь; править служебное сообщение нельзя.
|
|
5990
6017
|
/* @__PURE__ */ jsx9(skin.UserMessage, { text: noticesAsText(notices2, labels.notices), ...branchSwitcher && { branchSwitcher } })
|
|
5991
6018
|
) : message.role === "user" ? editingId === message.id ? /* @__PURE__ */ jsx9(
|
|
@@ -6058,6 +6085,10 @@ ${text}` : text),
|
|
|
6058
6085
|
}
|
|
6059
6086
|
}
|
|
6060
6087
|
);
|
|
6088
|
+
const shown = serviceLines ? /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
6089
|
+
serviceLines,
|
|
6090
|
+
content
|
|
6091
|
+
] }) : content;
|
|
6061
6092
|
const markers = dividers.get(message.id);
|
|
6062
6093
|
return markers ? /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
6063
6094
|
shown,
|
|
@@ -6255,6 +6286,15 @@ var EDIT_REFUSAL_ERRORS = {
|
|
|
6255
6286
|
// Текст сервера идёт в редактор как есть; надпись — на случай, если текста почему-то нет.
|
|
6256
6287
|
server_refused: "rejected"
|
|
6257
6288
|
};
|
|
6289
|
+
function ServiceLines({ skin, notices, lines }) {
|
|
6290
|
+
const View = skin.ServiceLine;
|
|
6291
|
+
if (!View || !lines) return null;
|
|
6292
|
+
const texts = notices.flatMap((notice) => {
|
|
6293
|
+
const text = Object.hasOwn(lines, notice.name) ? lines[notice.name](notice) : void 0;
|
|
6294
|
+
return text ? [{ name: notice.name, text }] : [];
|
|
6295
|
+
});
|
|
6296
|
+
return /* @__PURE__ */ jsx9(Fragment2, { children: texts.map(({ name, text }, index) => /* @__PURE__ */ jsx9(View, { text }, `${name}-${index}`)) });
|
|
6297
|
+
}
|
|
6258
6298
|
function StaffReplies({ skin, replies, label }) {
|
|
6259
6299
|
const View = skin.StaffMessage;
|
|
6260
6300
|
return /* @__PURE__ */ jsx9(Fragment2, { children: replies.map(
|
|
@@ -6503,7 +6543,8 @@ var AgentAnswer = memo4(function AgentAnswer2({
|
|
|
6503
6543
|
busy: runningActionIds?.includes(action.id) ?? false,
|
|
6504
6544
|
onRun: () => onRunAction?.(action, message, text)
|
|
6505
6545
|
}));
|
|
6506
|
-
const
|
|
6546
|
+
const errorText = status === "error" ? customErrorText(labels, message.metadata?.error) : void 0;
|
|
6547
|
+
const notice = status === "error" ? /* @__PURE__ */ jsx9(skin.ErrorNotice, { kind: message.metadata?.error?.kind ?? "other", ...errorText !== void 0 && { text: errorText }, ...onRetry && { onRetry } }) : void 0;
|
|
6507
6548
|
return /* @__PURE__ */ jsx9(ThinkingClockProvider, { id: message.id, waiting: status === "streaming", children: /* @__PURE__ */ jsxs(
|
|
6508
6549
|
skin.AgentMessage,
|
|
6509
6550
|
{
|
|
@@ -7016,6 +7057,7 @@ export {
|
|
|
7016
7057
|
splitVoiceTags,
|
|
7017
7058
|
stripVoiceTags,
|
|
7018
7059
|
voiceAnswerIds,
|
|
7060
|
+
customErrorText,
|
|
7019
7061
|
voiceErrorText,
|
|
7020
7062
|
englishWorkViewLabels,
|
|
7021
7063
|
englishWorkLabels,
|
|
@@ -7042,4 +7084,4 @@ export {
|
|
|
7042
7084
|
activeChatTitle,
|
|
7043
7085
|
useWorkspaceListSync
|
|
7044
7086
|
};
|
|
7045
|
-
//# sourceMappingURL=chunk-
|
|
7087
|
+
//# sourceMappingURL=chunk-JLGBVDAR.js.map
|