@ykurilov/agent-chat-react 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.
Files changed (106) hide show
  1. package/CHANGELOG.md +1688 -0
  2. package/LICENSE +21 -0
  3. package/README.md +132 -2
  4. package/dist/chunk-7DVTSVKQ.js +6967 -0
  5. package/dist/chunk-7DVTSVKQ.js.map +1 -0
  6. package/dist/index.js +501 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/panel.js +399 -0
  9. package/dist/panel.js.map +1 -0
  10. package/dist/types/agent-chat-extensions.d.ts +80 -0
  11. package/dist/types/agent-chat.d.ts +103 -0
  12. package/dist/types/agent-panel/agent-panel-hooks.d.ts +14 -0
  13. package/dist/types/agent-panel/agent-panel-provider.d.ts +24 -0
  14. package/dist/types/agent-panel/agent-panel-skin.d.ts +41 -0
  15. package/dist/types/agent-panel/agent-panel.d.ts +13 -0
  16. package/dist/types/agent-panel/index.d.ts +8 -0
  17. package/dist/types/agent-panel/panel-context.d.ts +46 -0
  18. package/dist/types/agent-panel/panel-state.d.ts +12 -0
  19. package/dist/types/answer-editing/answer-editing.d.ts +25 -0
  20. package/dist/types/app-bridge/app-bridge.d.ts +50 -0
  21. package/dist/types/app-bridge/browser-calls.d.ts +24 -0
  22. package/dist/types/app-bridge/create-app-bridge.d.ts +35 -0
  23. package/dist/types/app-bridge/remembered-ids.d.ts +6 -0
  24. package/dist/types/app-bridge/tab-id.d.ts +10 -0
  25. package/dist/types/app-events.d.ts +39 -0
  26. package/dist/types/app-refusal.d.ts +21 -0
  27. package/dist/types/attachments-contract.d.ts +82 -0
  28. package/dist/types/background-tasks/use-background-tasks.d.ts +53 -0
  29. package/dist/types/chat-access.d.ts +33 -0
  30. package/dist/types/chat-list/active-chat-memory.d.ts +5 -0
  31. package/dist/types/chat-list/chat-list-client.d.ts +35 -0
  32. package/dist/types/chat-list/chat-list-skin.d.ts +63 -0
  33. package/dist/types/chat-list/chat-workspace.d.ts +17 -0
  34. package/dist/types/chat-list/use-chat-list-view.d.ts +12 -0
  35. package/dist/types/chat-list/use-chat-list.d.ts +47 -0
  36. package/dist/types/chat-list/use-workspace-list-sync.d.ts +7 -0
  37. package/dist/types/chat-state/chat-state.d.ts +27 -0
  38. package/dist/types/chat-types.d.ts +85 -0
  39. package/dist/types/chat-watch/chat-watch.d.ts +57 -0
  40. package/dist/types/close-stopped-tool-calls.d.ts +8 -0
  41. package/dist/types/composer-actions.d.ts +29 -0
  42. package/dist/types/diagrams/diagram-renders.d.ts +16 -0
  43. package/dist/types/diagrams/mermaid-blocks.d.ts +18 -0
  44. package/dist/types/diagrams/unique-svg-ids.d.ts +6 -0
  45. package/dist/types/diagrams/use-diagram-blocks.d.ts +25 -0
  46. package/dist/types/diagrams/use-diagram-colors.d.ts +8 -0
  47. package/dist/types/dictation-field.d.ts +19 -0
  48. package/dist/types/expanded-parts.d.ts +11 -0
  49. package/dist/types/feed-announcement.d.ts +7 -0
  50. package/dist/types/helpers/helper-call.d.ts +57 -0
  51. package/dist/types/index.d.ts +53 -0
  52. package/dist/types/labels.d.ts +468 -0
  53. package/dist/types/live-demos/answer-fences.d.ts +25 -0
  54. package/dist/types/live-demos/live-demo-document.d.ts +42 -0
  55. package/dist/types/live-demos/live-demo-frame.d.ts +14 -0
  56. package/dist/types/live-demos/use-live-demo-blocks.d.ts +18 -0
  57. package/dist/types/liveness/connection-errors.d.ts +15 -0
  58. package/dist/types/liveness/reconnect-attempts.d.ts +14 -0
  59. package/dist/types/liveness/request-deadline.d.ts +16 -0
  60. package/dist/types/liveness/silence-watchdog.d.ts +21 -0
  61. package/dist/types/media-viewer/answer-file-images.d.ts +18 -0
  62. package/dist/types/media-viewer/collect-viewer-items.d.ts +16 -0
  63. package/dist/types/media-viewer/generated-images.d.ts +20 -0
  64. package/dist/types/media-viewer/media-viewer-types.d.ts +17 -0
  65. package/dist/types/media-viewer/open-attachments.d.ts +9 -0
  66. package/dist/types/media-viewer/use-media-viewer.d.ts +23 -0
  67. package/dist/types/memory/agent-memory.d.ts +13 -0
  68. package/dist/types/memory/memory-client.d.ts +19 -0
  69. package/dist/types/memory/memory-skin.d.ts +103 -0
  70. package/dist/types/memory/memory-tree.d.ts +17 -0
  71. package/dist/types/memory/memory-types.d.ts +77 -0
  72. package/dist/types/memory/use-memory-notes.d.ts +23 -0
  73. package/dist/types/message-actions.d.ts +45 -0
  74. package/dist/types/message-feed.d.ts +27 -0
  75. package/dist/types/notices/agent-notices.d.ts +40 -0
  76. package/dist/types/pause.d.ts +2 -0
  77. package/dist/types/server-refusal.d.ts +16 -0
  78. package/dist/types/skin-contract.d.ts +687 -0
  79. package/dist/types/spending-status.d.ts +76 -0
  80. package/dist/types/steering/corrections.d.ts +19 -0
  81. package/dist/types/step-reset.d.ts +15 -0
  82. package/dist/types/subject-chat/subject-chat-loading.d.ts +8 -0
  83. package/dist/types/subject-chat/use-subject-chat-id.d.ts +21 -0
  84. package/dist/types/thinking-status/thinking-status.d.ts +33 -0
  85. package/dist/types/turn-finished.d.ts +14 -0
  86. package/dist/types/use-agent-chat.d.ts +228 -0
  87. package/dist/types/use-model-choice.d.ts +31 -0
  88. package/dist/types/use-voice-dialog.d.ts +18 -0
  89. package/dist/types/use-voice-dictation.d.ts +17 -0
  90. package/dist/types/voice-dialog.d.ts +103 -0
  91. package/dist/types/voice-input.d.ts +47 -0
  92. package/dist/types/voice-tags.d.ts +18 -0
  93. package/dist/types/voice-turn.d.ts +11 -0
  94. package/dist/types/widgets/custom-widget-guard.d.ts +21 -0
  95. package/dist/types/widgets/form-check.d.ts +29 -0
  96. package/dist/types/widgets/suggested-replies.d.ts +6 -0
  97. package/dist/types/widgets/use-widget-answer.d.ts +54 -0
  98. package/dist/types/widgets/widget-answer-keys.d.ts +14 -0
  99. package/dist/types/widgets/widget-messages.d.ts +14 -0
  100. package/dist/types/widgets/widget-part.d.ts +17 -0
  101. package/dist/types/widgets/widget-view.d.ts +50 -0
  102. package/dist/types/without-markdown.d.ts +1 -0
  103. package/dist/types/work-view/step-phrases.d.ts +37 -0
  104. package/dist/types/work-view/work-layout.d.ts +57 -0
  105. package/dist/types/work-view/work-steps-part.d.ts +15 -0
  106. package/package.json +53 -3
@@ -0,0 +1,103 @@
1
+ import type { AgentChatMessage, ChatModelChoice } from "./chat-types.js";
2
+ import type { AgentChatExtensions } from "./agent-chat-extensions.js";
3
+ import type { AnswerEditing } from "./answer-editing/answer-editing.js";
4
+ import type { AttachmentsPlugin } from "./attachments-contract.js";
5
+ import type { ChatSkin } from "./skin-contract.js";
6
+ import type { VoiceInput } from "./voice-input.js";
7
+ import type { VoiceDialogEngine } from "./voice-dialog.js";
8
+ import type { AppRefusal } from "./app-refusal.js";
9
+ import type { ChatAccess } from "./chat-access.js";
10
+ import type { LivenessOptions } from "./use-agent-chat.js";
11
+ import type { AgentAppBridge } from "./app-bridge/app-bridge.js";
12
+ import type { MessageAction } from "./message-actions.js";
13
+ import type { ComposerActions } from "./composer-actions.js";
14
+ import type { AppEventHandler } from "./app-events.js";
15
+ import type { TurnFinishedEvent } from "./turn-finished.js";
16
+ import type { ChatStateValues } from "./chat-state/chat-state.js";
17
+ export interface AgentChatProps extends AgentChatExtensions {
18
+ /** Адрес обработчика чата, например `/api/chat`. */
19
+ api: string;
20
+ /** Какой чат открыть. Нужен ровно один из `chatId` и `subject`: оба сразу или ни одного — ошибка разработчика (исключение). */
21
+ chatId?: string;
22
+ /**
23
+ * Ключ объекта приложения (урок, заказ), например `"lesson:42"`: при открытии чат спрашивает у сервера
24
+ * `POST {api}/subject` чат человека по этому ключу (нет — сервер создаёт, с приветствием) и дальше работает с ним как
25
+ * с обычным. Отказ сервера показывается текстом. Нужен `subjectChats` в обработчике чата.
26
+ */
27
+ subject?: string;
28
+ skin: ChatSkin;
29
+ /** Сроки живой связи; по умолчанию как в договоре 002. */
30
+ liveness?: LivenessOptions;
31
+ /** Голосовой ввод; без него кнопки микрофона в поле ввода нет. */
32
+ voice?: VoiceInput;
33
+ /** Голосовой режим («разговор»): `createVoiceDialogEngine` из `@ykurilov/agent-chat-voice-dialog`. Без него кнопки режима нет. */
34
+ voiceDialog?: VoiceDialogEngine;
35
+ /** Вложения к сообщениям: `attachmentsClient` из `@ykurilov/agent-chat-attachments/react`. Нет — чат только текстовый. */
36
+ attachments?: AttachmentsPlugin;
37
+ /** У сервера включены лимиты расходов (`limits` в обработчике чата): показывать предупреждение и исчерпание. */
38
+ spendingLimits?: boolean;
39
+ /** Чат занят (идёт ответ) или свободен; вызывается при открытии чата и при каждой смене. Для расширений вокруг чата, например списка чатов. */
40
+ onBusyChange?: (busy: boolean) => void;
41
+ /** Связь с приложением (место на экране, изменения данных, браузерные инструменты); её создаёт `AgentPanelProvider`. Нет — чат как раньше. */
42
+ appBridge?: AgentAppBridge;
43
+ /** Текст в поле ввода при открытии чата. */
44
+ initialDraft?: string;
45
+ /** Правка ответов агента (песочница): кнопка правки у законченных ответов, отметки «изменено», редактор страницы. Нет — ответы не правятся. */
46
+ answerEditing?: AnswerEditing;
47
+ /** Сообщения текущей ветки и занятость чата; вызывается при каждой смене. Для панелей рядом с чатом, например журнала песочницы. */
48
+ onFeedChange?: (feed: {
49
+ messages: AgentChatMessage[];
50
+ busy: boolean;
51
+ }) => void;
52
+ /** Свои кнопки под ответами агента («Нравится», «В заметки»): рисует скин в ряду действий ответа. Массив держите постоянным. */
53
+ messageActions?: MessageAction[];
54
+ /** Ход закончен: у ответа `messageId` итог `status` (включая остановку и ошибку). Один раз на ход; открытие чата хода не даёт. */
55
+ onTurnFinished?: (turn: TurnFinishedEvent) => void;
56
+ /** Свои кнопки у поля ввода («Проверить работу»): узел или функция от помощника (`ComposerHelper`: поле, `send`, `addFiles`). Нет — кнопок нет. */
57
+ composerActions?: ComposerActions;
58
+ /**
59
+ * События приложения (`afterTurn`, `emitAppEvent`, `publishAppEvent` на сервере): по разу на событие. `live: true` —
60
+ * пришло живьём (во время хода или вне хода через канал наблюдения), `false` — лежало в истории при открытии чата.
61
+ * У события вне хода нет `messageId`. Вид события в ответе задаёт `appEvents`.
62
+ */
63
+ onAppEvent?: AppEventHandler;
64
+ /**
65
+ * Приложение отказало ходу (`beforeTurn`) или человеку в доступе (`resolveUser`): `kind` — `turn_refused` или `access_refused`,
66
+ * `code` — для программы, `message` — слова для человека (до 300 знаков; чат их уже показал). Зовётся на каждый такой отказ,
67
+ * в том числе когда сервер не открыл чат объекта (`subject`).
68
+ */
69
+ onTurnRefused?: (refusal: AppRefusal) => void;
70
+ /**
71
+ * Права человека в чате и закрытие (`closed`, `null` — открыт): после загрузки истории и при каждой смене (отказ
72
+ * `chat_closed`, закрытие инструментом, события `chat-closed` и `chat-reopened`). Кнопки чат прячет сам.
73
+ */
74
+ onChatAccessChange?: (access: ChatAccess) => void;
75
+ /**
76
+ * Состояние чата показанной ветки (`model.state` на сервере) после загрузки истории и при каждой смене: запись по
77
+ * ходу ответа, смена ветки, правка. То же значение повторно не приходит. Внутри ленты — `useChatState()`.
78
+ */
79
+ onStateChange?: (state: ChatStateValues) => void;
80
+ /**
81
+ * Человек выбрал модель или уровень рассуждений в меню, и сервер сохранил выбор. Выбор по умолчанию и выбор из истории
82
+ * его не зовут. На сервере то же сообщает хук `onModelChosen`.
83
+ */
84
+ onModelChosen?: (choice: ChatModelChoice) => void;
85
+ /**
86
+ * Служебный, для `ChatWorkspace`: приложению не нужен. Чат не открылся и прав не будет: его нет, приложение не пустило
87
+ * человека или история не загрузилась. Пока права неизвестны, список чатов скрыт, а в этих случаях он нужен,
88
+ * чтобы выбрать другой чат.
89
+ */
90
+ onChatUnavailable?: () => void;
91
+ /**
92
+ * Другое значение — история перечитывается с сервера: права, закрытие и лента. Для случая, когда чат изменили не
93
+ * через этот экран (например, песочница открыла закрытый чат снова). Первое значение ничего не перечитывает.
94
+ */
95
+ reloadKey?: number;
96
+ /**
97
+ * Поправки на лету: пока идёт ответ, поле принимает текст, агент учтёт его на ближайшем шаге. Включено, если скин их
98
+ * рисует (`ChatSkin.steering`, договор 21); `false` — выключить и у такого скина. Скин без `steering` — как до 0.34.
99
+ */
100
+ steering?: boolean;
101
+ }
102
+ /** Лента чата с агентом. Всё видимое рисуют компоненты скина. */
103
+ export declare function AgentChat(props: AgentChatProps): import("react").JSX.Element;
@@ -0,0 +1,14 @@
1
+ import type { AppDataChange, ScreenPlace } from "../app-bridge/app-bridge.js";
2
+ import type { AgentPanelControls } from "./panel-context.js";
3
+ export type { AgentPanelControls } from "./panel-context.js";
4
+ /** Управление панелью из кода приложения: открыть, закрыть, открыть чат, новый чат с готовым текстом, отступ для `push`. */
5
+ export declare function useAgentPanel(): AgentPanelControls;
6
+ /**
7
+ * Где сейчас человек: место уходит с каждым запросом хода, пока компонент на экране. Главнее место компонента, который
8
+ * появился на экране позже; вложенный всегда главнее внешнего (страница главнее макета, в котором стоит), и при первом
9
+ * показе, и после переходов. Смена места не меняет, чьё главнее. `null` или размонтирование снимает только это место —
10
+ * снова действует место другого компонента.
11
+ */
12
+ export declare function useAgentPlace(place: ScreenPlace | null): void;
13
+ /** Подписка на изменения данных по метке (или списку меток). Подписчик зовётся один раз на сообщение, всегда свежий. */
14
+ export declare function useAgentDataChanges(tag: string | readonly string[], handler: (change: AppDataChange) => void): void;
@@ -0,0 +1,24 @@
1
+ import type { ReactNode } from "react";
2
+ import type { AppDataChange, BrowserToolFunction } from "../app-bridge/app-bridge.js";
3
+ export interface AgentPanelProviderProps {
4
+ /** Адрес обработчика чата, например `/api/chat`. */
5
+ api: string;
6
+ /** Номер человека в приложении: открыта ли панель и какой чат открыт, помнится отдельно для каждого. Смена — состояние другого человека. */
7
+ userKey: string;
8
+ /** Функции браузерных инструментов по именам (описания задаёт сервер в `agent.browserTools`). */
9
+ browserTools?: Record<string, BrowserToolFunction>;
10
+ /** `overlay` — панель поверх страницы, `push` — приложение сдвигает страницу на `pageInset`. По умолчанию `overlay`. */
11
+ mode?: "overlay" | "push";
12
+ /** Ширина панели на широком экране, px. По умолчанию 420. */
13
+ width?: number;
14
+ /** Каждое сообщение об изменении данных, любой метки. */
15
+ onDataChanged?: (change: AppDataChange) => void;
16
+ /** Свой `fetch` для списка чатов. Сам чат, как и `AgentChat`, ходит через `globalThis.fetch`. */
17
+ fetch?: typeof globalThis.fetch;
18
+ children?: ReactNode;
19
+ }
20
+ /**
21
+ * Состояние панели агента и связь чата с приложением. Ставится в корневой макет, над маршрутами:
22
+ * панель, открытый чат и идущий ответ живут при переходах по страницам приложения.
23
+ */
24
+ export declare function AgentPanelProvider({ api, userKey, browserTools, mode, width, onDataChanged, fetch, children }: AgentPanelProviderProps): import("react").JSX.Element;
@@ -0,0 +1,41 @@
1
+ import type { ComponentType, ReactNode, Ref } from "react";
2
+ /**
3
+ * Договор скина, версия 12: отдельный интерфейс для боковой панели агента (`AgentPanel` из `@ykurilov/agent-chat-react/panel`).
4
+ * `ChatSkin`, `ChatListSkin` (версия 7) и `MemorySkin` (версия 11) не менялись. Скин без `AgentPanelSkin` работает как раньше,
5
+ * но для панели не годится (ошибка типов). Надписи компоненты берут из `useChatLabels()`.
6
+ */
7
+ export interface AgentPanelSkin {
8
+ /** Кнопка агента в углу страницы. Положение и вид — дело скина. */
9
+ AgentPanelLauncher: ComponentType<{
10
+ open: boolean;
11
+ /** Идёт ответ в открытом чате: показать значок. */
12
+ busy: boolean;
13
+ onToggle: () => void;
14
+ /** Сюда слой поведения возвращает фокус при закрытии. */
15
+ buttonRef: Ref<HTMLButtonElement>;
16
+ }>;
17
+ /**
18
+ * Рамка панели. Смонтирована всегда; закрытая — скрыта (`hidden`/`inert`), содержимое живёт.
19
+ * Страница под панелью не затемняется и остаётся кликабельной. При переходе `open: false → true` фокус уходит в поле ввода внутри рамки;
20
+ * роль `complementary` или `dialog` без `aria-modal`, `aria-label` из словаря. На экране ≤ 640 px панель на весь экран,
21
+ * «Закрыть» всегда видна, горизонтальной прокрутки нет.
22
+ */
23
+ AgentPanel: ComponentType<{
24
+ open: boolean;
25
+ mode: "overlay" | "push";
26
+ /** Ширина на широком экране, px. */
27
+ width: number;
28
+ /** Название открытого чата (или «Новый чат»). */
29
+ title: string;
30
+ /** Список открыт поверх чата. */
31
+ listOpen: boolean;
32
+ onListOpenChange: (open: boolean) => void;
33
+ onClose: () => void;
34
+ /** Esc внутри панели. Слой поведения сам решает: закрыть список или панель. Список закрылся и фокус пропал — верните его в поле ввода. */
35
+ onEscape: () => void;
36
+ /** Открытый чат (`AgentChat`). */
37
+ children: ReactNode;
38
+ /** Список чатов (`skin.ChatList`); рисовать поверх чата, когда `listOpen`. */
39
+ list: ReactNode;
40
+ }>;
41
+ }
@@ -0,0 +1,13 @@
1
+ import type { AgentChatProps } from "../agent-chat.js";
2
+ import type { ChatListSkin } from "../chat-list/chat-list-skin.js";
3
+ import type { ChatSkin } from "../skin-contract.js";
4
+ import type { AgentPanelSkin } from "./agent-panel-skin.js";
5
+ export interface AgentPanelProps extends Omit<AgentChatProps, "api" | "chatId" | "subject" | "skin" | "onBusyChange" | "appBridge" | "initialDraft"> {
6
+ /** Скин чата, списка и панели (например, `tandemSkin`). */
7
+ skin: ChatSkin & ChatListSkin & AgentPanelSkin;
8
+ }
9
+ /**
10
+ * Кнопка агента и панель с открытым чатом и списком чатов поверх него. Всё видимое рисует скин.
11
+ * Открытый чат смонтирован и при закрытой панели: ответ идёт, подписчики на изменения данных слышат его.
12
+ */
13
+ export declare function AgentPanel({ skin, ...chatProps }: AgentPanelProps): import("react").JSX.Element;
@@ -0,0 +1,8 @@
1
+ export { AgentPanelProvider } from "./agent-panel-provider.js";
2
+ export type { AgentPanelProviderProps } from "./agent-panel-provider.js";
3
+ export { AgentPanel } from "./agent-panel.js";
4
+ export type { AgentPanelProps } from "./agent-panel.js";
5
+ export { useAgentDataChanges, useAgentPanel, useAgentPlace } from "./agent-panel-hooks.js";
6
+ export type { AgentPanelControls } from "./agent-panel-hooks.js";
7
+ export type { AgentPanelSkin } from "./agent-panel-skin.js";
8
+ export type { AgentAppBridge, AppDataChange, BrowserCall, BrowserCallResult, BrowserToolFunction, BrowserToolImplementations, ScreenPlace, } from "../app-bridge/app-bridge.js";
@@ -0,0 +1,46 @@
1
+ import type { RefObject } from "react";
2
+ import type { AppBridgeControl } from "../app-bridge/create-app-bridge.js";
3
+ import type { ChatListState } from "../chat-list/use-chat-list.js";
4
+ /** Управление панелью из кода приложения (`useAgentPanel`). */
5
+ export interface AgentPanelControls {
6
+ open: boolean;
7
+ setOpen(open: boolean): void;
8
+ toggle(): void;
9
+ /** Открыть панель на чате; чужой или удалённый — «Чат не найден», список — по кнопке «Чаты». */
10
+ openChat(chatId: string): void;
11
+ /** Новый (или пустой) чат и готовый текст в поле ввода. `false` — не создался (список открыт с ошибкой). */
12
+ openNewChat(options?: {
13
+ draft?: string;
14
+ }): Promise<boolean>;
15
+ /** Отступ справа для страницы в режиме `push`; 0 — закрыто, режим `overlay` или экран ≤ 640 px. */
16
+ pageInset: number;
17
+ mode: "overlay" | "push";
18
+ width: number;
19
+ /** Идёт ответ в открытом чате — для значка на кнопке. */
20
+ busy: boolean;
21
+ }
22
+ /** То, что нужно `AgentPanel`, чтобы нарисовать кнопку, рамку, список и открытый чат. */
23
+ export interface PanelView {
24
+ api: string;
25
+ controls: AgentPanelControls;
26
+ list: ChatListState;
27
+ listOpen: boolean;
28
+ setListOpen(open: boolean): void;
29
+ /** Список выбран или создан: закрыть его. */
30
+ closeList(): void;
31
+ /** Esc внутри панели: открыт список — закрыть его, иначе закрыть панель; при подтверждении удаления — ничего. */
32
+ escape(): void;
33
+ /** Ключ открытого чата: меняется вместе с чатом и с новым готовым текстом. */
34
+ chatKey: string;
35
+ initialDraft: string | undefined;
36
+ setBusy(busy: boolean): void;
37
+ launcherRef: RefObject<HTMLButtonElement | null>;
38
+ bridge: AppBridgeControl["bridge"];
39
+ }
40
+ export declare const AppBridgeContext: import("react").Context<AppBridgeControl | null>;
41
+ export declare const PanelControlsContext: import("react").Context<AgentPanelControls | null>;
42
+ export declare const PanelViewContext: import("react").Context<PanelView | null>;
43
+ /** Мост и регистрация мест и подписок; для хуков панели. */
44
+ export declare function useAppBridgeControl(user?: string): AppBridgeControl;
45
+ export declare function usePanelControls(user: string): AgentPanelControls;
46
+ export declare function usePanelView(user: string): PanelView;
@@ -0,0 +1,12 @@
1
+ /** Под этим ключом `localStorage` помнит, открыта ли панель: `{ "open": boolean }`. */
2
+ export declare const panelOpenKey: (api: string, userKey: string) => string;
3
+ /** Под этим ключом `useChatList` помнит чат, открытый в панели у этого человека. */
4
+ export declare const panelChatKey: (api: string, userKey: string) => string;
5
+ /** Была ли панель открыта у этого человека. Нет записи, битая запись или хранилище недоступно — закрыта. */
6
+ export declare function readPanelOpen(api: string, userKey: string): boolean;
7
+ export declare function rememberPanelOpen(api: string, userKey: string, open: boolean): void;
8
+ /**
9
+ * Открыта ли панель у этого человека; смена `api` или `userKey` — его запомненное состояние.
10
+ * При отрисовке на сервере и в первом проходе гидратации — закрыта, потом — запомненное: разметка не расходится.
11
+ */
12
+ export declare function usePanelOpen(api: string, userKey: string): [boolean, (open: boolean) => void];
@@ -0,0 +1,25 @@
1
+ import type { ReactNode } from "react";
2
+ import type { AgentChatMessage } from "../chat-types.js";
3
+ /** Что в ответе изменено человеком: текст и (или) результаты вызовов инструментов. */
4
+ export interface AnswerEditMark {
5
+ textEdited: boolean;
6
+ editedToolCallIds: string[];
7
+ }
8
+ /** Что получает редактор ответа, который рисует страница. */
9
+ export interface AnswerEditorSlot {
10
+ message: AgentChatMessage;
11
+ /** Сервер принял правку и создал ответ `newMessageId`: чат переходит на его ветку и закрывает редактор. */
12
+ onSaved: (newMessageId: string) => void;
13
+ onCancel: () => void;
14
+ }
15
+ /**
16
+ * Правка ответов агента (песочница). Чат даёт законченным ответам кнопку правки, показывает
17
+ * отметки «изменено» и ставит редактор страницы на место ответа. Без этого свойства ответы не правятся.
18
+ */
19
+ export interface AnswerEditing {
20
+ /** Отметки правок по id ответа. */
21
+ edits: ReadonlyMap<string, AnswerEditMark>;
22
+ renderEditor: (slot: AnswerEditorSlot) => ReactNode;
23
+ }
24
+ /** Изменён ли ответ человеком хоть в чём-то. */
25
+ export declare function answerEdited(mark: AnswerEditMark | undefined): boolean;
@@ -0,0 +1,50 @@
1
+ import type { InferToolInput, ToolSet } from "ai";
2
+ /** Где сейчас человек в приложении. Форма совпадает с `ScreenPlace` из `@ykurilov/agent-chat-core`: ядро браузеру не нужно. */
3
+ export interface ScreenPlace {
4
+ description: string;
5
+ data?: Record<string, unknown>;
6
+ }
7
+ /** Сообщение «данные приложения изменены»; `id` один на сообщение, повторы с тем же `id` игнорируются. */
8
+ export interface AppDataChange {
9
+ id: string;
10
+ tag: string;
11
+ details?: unknown;
12
+ }
13
+ /** Тип куска потока ответа «данные изменены»; совпадает с `APP_CHANGE_PART_TYPE` ядра. */
14
+ export declare const APP_CHANGE_PART_TYPE = "data-app-change";
15
+ /** Вызов браузерного инструмента, который сервер просит выполнить на этой вкладке. */
16
+ export interface BrowserCall {
17
+ toolCallId: string;
18
+ toolName: string;
19
+ input: unknown;
20
+ }
21
+ /** Итог вызова: результат или текст ошибки для модели. */
22
+ export type BrowserCallResult = {
23
+ output: unknown;
24
+ } | {
25
+ error: string;
26
+ };
27
+ /** Функция браузерного инструмента: делает действие на странице. `signal` обрывает её (срок вышел, «стоп»). */
28
+ export type BrowserToolFunction<Input = any> = (input: Input, context: {
29
+ signal: AbortSignal;
30
+ }) => unknown;
31
+ /**
32
+ * Функции браузерных инструментов по именам. С описаниями из общего модуля (`tool({ description, inputSchema })`)
33
+ * вход каждой функции выводится из схемы: `BrowserToolImplementations<typeof browserToolDescriptions>`.
34
+ */
35
+ export type BrowserToolImplementations<Descriptions extends ToolSet = ToolSet> = {
36
+ [Name in keyof Descriptions]: BrowserToolFunction<InferToolInput<Descriptions[Name]>>;
37
+ };
38
+ /** Связь чата с приложением: место на экране, номер вкладки, сообщения об изменении данных, браузерные вызовы. */
39
+ export interface AgentAppBridge {
40
+ /** Номер вкладки; сервер по нему отдаёт браузерные вызовы той вкладке, где идёт ответ. */
41
+ tabId: string;
42
+ /** Место на момент запроса; `null` — места нет. */
43
+ currentPlace(): ScreenPlace | null;
44
+ /** Раздаёт сообщение подписчикам; повтор `id` игнорирует. */
45
+ dataChanged(change: AppDataChange): void;
46
+ /** Исполняет функцию приложения по имени с `signal`; нет функции — `{ error: "Инструмент недоступен на этой странице" }`, брошенная ошибка — `{ error: её текст }`. */
47
+ browserCall(call: BrowserCall, options?: {
48
+ signal?: AbortSignal;
49
+ }): Promise<BrowserCallResult>;
50
+ }
@@ -0,0 +1,24 @@
1
+ import type { AgentAppBridge, BrowserCall } from "./app-bridge.js";
2
+ /** Тип куска потока ответа «выполни браузерный инструмент»; совпадает с `BROWSER_CALL_PART_TYPE` ядра. */
3
+ export declare const BROWSER_CALL_PART_TYPE = "data-browser-call";
4
+ /**
5
+ * Под этим ключом `sessionStorage` хранит исполненные на вкладке вызовы (адрес чата и номер вызова, как ключ ожидания на
6
+ * сервере): повтор кусков после перезагрузки их не повторит, а тот же номер в другом чате исполнится.
7
+ */
8
+ export declare const BROWSER_CALLS_DONE_KEY = "agent-chat:browser-calls-done";
9
+ /** Данные куска `data-browser-call`: вызов и вкладка, которой он адресован. */
10
+ export interface BrowserCallRequest extends BrowserCall {
11
+ tabId: string;
12
+ }
13
+ /**
14
+ * Исполняет вызов из потока ответа, если он адресован этой вкладке и ещё не исполнялся, и отправляет итог на
15
+ * `{chatUrl}/browser-tools/{toolCallId}`. Сбой связи — тихо, вызов на сервере закроется сам. `409` (срок вышел, ход
16
+ * кончился) — тоже без ошибки, но сервер уже ушёл дальше: `onNotPending` даёт ленте перечитать историю.
17
+ */
18
+ export declare function runBrowserCall(call: BrowserCallRequest, context: {
19
+ bridge: AgentAppBridge;
20
+ chatUrl: string;
21
+ request: (input: string, init?: RequestInit) => Promise<Response>;
22
+ signal: AbortSignal;
23
+ onNotPending?: () => void;
24
+ }): Promise<void>;
@@ -0,0 +1,35 @@
1
+ import type { AgentAppBridge, AppDataChange, BrowserToolFunction, ScreenPlace } from "./app-bridge.js";
2
+ /** Что мост берёт у приложения; читается в момент вызова, поэтому новые свойства провайдера видны без нового моста. */
3
+ export interface AppBridgeOptions {
4
+ /** Функции браузерных инструментов по именам. */
5
+ browserTools?: Record<string, BrowserToolFunction>;
6
+ /** Получает каждое сообщение об изменении данных, любой метки. */
7
+ onDataChanged?: (change: AppDataChange) => void;
8
+ /** Ответ модели, если функции инструмента на этой странице нет. */
9
+ toolUnavailableMessage: string;
10
+ }
11
+ /** Владелец места: чем больше `order`, тем позже создан и тем главнее его место. */
12
+ export interface PlaceOwner {
13
+ readonly order: number;
14
+ }
15
+ /** Мост и то, чем его наполняют хуки панели: места компонентов и подписки на изменения данных. */
16
+ export interface AppBridgeControl {
17
+ bridge: AgentAppBridge;
18
+ /**
19
+ * Владелец места для одного компонента; создаётся при его первой отрисовке. Владелец, созданный позже, главнее:
20
+ * вложенный компонент рисуется после внешнего, поэтому страница всегда главнее макета, в котором стоит.
21
+ */
22
+ createPlaceOwner(): PlaceOwner;
23
+ /** Место компонента `owner`; смена места не меняет, чьё главнее. `null` — снять своё место. */
24
+ setPlace(owner: PlaceOwner, place: ScreenPlace | null): void;
25
+ /** Снимает только место `owner`; места других компонентов остаются. */
26
+ removePlace(owner: PlaceOwner): void;
27
+ /** Подписка на сообщения с одной из меток `tags`; возвращает отписку. */
28
+ subscribe(tags: readonly string[], handler: (change: AppDataChange) => void): () => void;
29
+ }
30
+ /**
31
+ * Под этим ключом `sessionStorage` хранит `id` розданных сообщений (последние 500): повтор кусков ответа, в том числе
32
+ * после перезагрузки страницы во время ответа, не раздаёт сообщение второй раз.
33
+ */
34
+ export declare const APP_CHANGES_DELIVERED_KEY = "agent-chat:app-changes-delivered";
35
+ export declare function createAppBridge(readOptions: () => AppBridgeOptions): AppBridgeControl;
@@ -0,0 +1,6 @@
1
+ /** Последние id, которые помнит вкладка: в `sessionStorage` под `key` (переживают перезагрузку), без хранилища — на время жизни страницы. */
2
+ export interface RememberedIds {
3
+ /** Отмечает id; `false` — он уже отмечен. */
4
+ claim(id: string): boolean;
5
+ }
6
+ export declare function rememberedIds(key: string, limit: number): RememberedIds;
@@ -0,0 +1,10 @@
1
+ /** Под этим ключом `sessionStorage` хранит номер вкладки: переживает перезагрузку, у новой вкладки свой. */
2
+ export declare const TAB_ID_KEY = "agent-chat:tab-id";
3
+ /**
4
+ * Под ключом `agent-chat:tab-open:{номер}` `localStorage` отмечает, что страница с этим номером открыта; уходя (`pagehide`),
5
+ * страница снимает отметку. «Дублировать вкладку» копирует `sessionStorage` вместе с номером, но исходная вкладка ещё открыта —
6
+ * дубликат видит отметку и берёт себе новый номер. Иначе обе вкладки исполнили бы один браузерный вызов.
7
+ */
8
+ export declare const tabOpenKey: (tabId: string) => string;
9
+ /** Номер этой вкладки: прочитанный из `sessionStorage`, если другая открытая вкладка его не занимает, иначе новый случайный. */
10
+ export declare function readTabId(): string;
@@ -0,0 +1,39 @@
1
+ import type { ComponentType } from "react";
2
+ import type { AgentChatMessage } from "./chat-types.js";
3
+ /** Событие приложения в ответе агента. Форма совпадает с `AppEvent` из `@ykurilov/agent-chat-core`: ядро браузеру не нужно. */
4
+ export interface AppEvent {
5
+ type: string;
6
+ data?: unknown;
7
+ }
8
+ /** Где и когда событие пришло: `live` — живьём (поток ответа или канал наблюдения), а не из истории. */
9
+ export interface AppEventInfo {
10
+ live: boolean;
11
+ /** Ответ агента с событием; у события вне хода (`publishAppEvent`) его нет. */
12
+ messageId?: string;
13
+ }
14
+ /**
15
+ * Свой вид событий приложения внутри ответа: тип события → компонент («урок зачтён» — плашка). Рисуется прямо в теле
16
+ * ответа, там, где лежит часть события, как виджеты: отдельного слота скина нет. Тип без компонента не виден.
17
+ */
18
+ export type AppEventViews = Record<string, ComponentType<{
19
+ event: AppEvent;
20
+ messageId: string;
21
+ }>>;
22
+ export type AppEventHandler = (event: AppEvent, info: AppEventInfo) => void;
23
+ /** Событие из присланного сервером значения; не событие — `undefined`. */
24
+ export declare function appEventOf(value: unknown): AppEvent | undefined;
25
+ /** Событие из части ответа; часть не событие или битая — `undefined`. */
26
+ export declare function appEventOfPart(part: AgentChatMessage["parts"][number]): AppEvent | undefined;
27
+ /** Учёт событий в ответах ленты. */
28
+ export interface AppEventReport {
29
+ /** Новые события ответа `messageId` придут живьём, с перечитанной историей (хук итога, доделанный после «Стопа»). */
30
+ expectLive(messageId: string): void;
31
+ }
32
+ /**
33
+ * Зовёт `onAppEvent` по разу на каждое событие в ответах ленты. События, что лежали в истории при открытии чата или
34
+ * пришли с перечитанной историей, когда чат свободен (смена ветки), — `live: false`; пришедшие, пока идёт ход, и
35
+ * ожидаемые (`expectLive`) — `live: true`. Скопированные из прежнего ответа («Повторить», `id` вида `copied:…`) — всегда
36
+ * `live: false`: они уже сработали там. Повторная загрузка тех же ответов событие второй раз не даёт; смена чата
37
+ * начинает счёт заново (`AgentChat` к тому же пересоздаёт ленту на каждый чат).
38
+ */
39
+ export declare function useAppEventReport(chatId: string, loaded: boolean, busy: boolean, messages: readonly AgentChatMessage[], onAppEvent: AppEventHandler | undefined): AppEventReport;
@@ -0,0 +1,21 @@
1
+ /** Длиннее экран не показывает: текст отказа — короткая причина, а не страница. */
2
+ export declare const SERVER_REFUSAL_MAX_LENGTH = 300;
3
+ /**
4
+ * Чей отказ: `turn_refused` — приложение не пустило ход (`beforeTurn`), `access_refused` — не пустило человека
5
+ * (`resolveUser`), `chat_closed` — чат закрыт, `action_forbidden` — действие запрещено правами чата (`code` — право),
6
+ * `chat_creation_refused` — приложение не дало создать новый чат («Новый чат» в списке).
7
+ */
8
+ export type AppRefusalKind = "turn_refused" | "access_refused" | "chat_closed" | "action_forbidden" | "chat_creation_refused";
9
+ /** Отказ приложения: код для программы и слова для человека (до 300 знаков). */
10
+ export interface AppRefusal {
11
+ kind: AppRefusalKind;
12
+ code: string;
13
+ message: string;
14
+ }
15
+ /**
16
+ * Отказ приложения из тела ответа: `{ error: { kind, code, message } }`, `kind` — один из `AppRefusalKind`.
17
+ * Статус не важен (403 или 401): вид и слова в теле главнее. `null` — тело другое, без кода или без слов.
18
+ */
19
+ export declare function appRefusalOf(response: Response): Promise<AppRefusal | null>;
20
+ /** Слова отказа в одну строку, не длиннее `SERVER_REFUSAL_MAX_LENGTH`. */
21
+ export declare function shortenedRefusalText(text: string): string;
@@ -0,0 +1,82 @@
1
+ import type { FileUIPart } from "ai";
2
+ import type { AgentChatMessage } from "./chat-types.js";
3
+ /** Файл в поле ввода до отправки. */
4
+ export interface ComposerAttachmentItem {
5
+ id: string;
6
+ name: string;
7
+ kind: "image" | "file";
8
+ /** Адрес для миниатюры картинки (локальный, пока файл не отправлен). */
9
+ previewUrl?: string;
10
+ status: "uploading" | "ready" | "error";
11
+ }
12
+ /** Вложения в поле ввода (договор скина, версия 5): скрепка, перетаскивание, вставка, превью с удалением. */
13
+ export interface ComposerAttachments {
14
+ items: ComposerAttachmentItem[];
15
+ /** Выбранные, перетащенные или вставленные файлы. */
16
+ onAddFiles: (files: File[]) => void;
17
+ onRemove: (id: string) => void;
18
+ /** Значение для `accept` поля выбора файла. */
19
+ accept: string;
20
+ /** Что сказать человеку до отправки, готовой надписью: отказ по файлу, лимит, «модель не видит картинки». */
21
+ notice?: string;
22
+ }
23
+ /** Вложение в ленте у сообщения человека. */
24
+ export interface MessageAttachmentItem {
25
+ id: string;
26
+ /**
27
+ * Адрес части `file` сообщения, из которой этот элемент (`attachment:…`). По нему файл в ответе агента встаёт на место
28
+ * своей части. Нет — в ответе агента элемент не узнаётся, часть показывается ссылкой без адреса.
29
+ */
30
+ partUrl?: string;
31
+ name: string;
32
+ kind: "image" | "file";
33
+ /** Адрес файла на сервере: миниатюра картинки или ссылка на скачивание. */
34
+ url: string;
35
+ mediaType: string;
36
+ /** Подпись, если нужна (например, «PDF»). */
37
+ caption?: string;
38
+ /**
39
+ * Открыть картинку в окне просмотра (версия 13). Есть только у картинки, когда у скина есть `MediaViewer`:
40
+ * миниатюру сделать кнопкой с подписью `labels.images.open`. Нет — миниатюра как раньше.
41
+ */
42
+ onOpen?: (trigger: HTMLElement) => void;
43
+ }
44
+ /** Вложения сообщения для ленты (договор скина, версия 5). */
45
+ export interface MessageAttachmentsView {
46
+ /** Подпись группы для экранного диктора. */
47
+ label: string;
48
+ items: MessageAttachmentItem[];
49
+ }
50
+ /** Состояние вложений в открытом чате: его ведёт плагин, слой поведения только связывает с полем ввода и лентой. */
51
+ export interface AttachmentsController {
52
+ composer: ComposerAttachments;
53
+ /** Все файлы загружены без ошибок и вложений не больше допустимого: можно отправлять. */
54
+ ready: boolean;
55
+ /** В поле ввода есть хотя бы один файл. */
56
+ hasFiles: boolean;
57
+ /** Готовые файлы для отправки; поле не трогает. */
58
+ readFiles: () => FileUIPart[];
59
+ /**
60
+ * Файлы ушли с сообщением: поле очищается, но файлы помнятся, пока их можно вернуть (`restoreFiles`).
61
+ * Следующая отправка забывает прежние.
62
+ */
63
+ markSent: () => void;
64
+ /**
65
+ * Сервер не принял сообщение: помнящиеся файлы возвращаются в поле. `kind` — причина от сервера; если файл
66
+ * устарел и удалён, он возвращается с пометкой «загрузите снова».
67
+ */
68
+ restoreFiles: (kind: string | undefined) => void;
69
+ /** Вложения сообщения для ленты; `undefined` — у сообщения нет файлов. Функция стабильна, пока чат тот же. */
70
+ describe: (message: AgentChatMessage) => MessageAttachmentsView | undefined;
71
+ }
72
+ /** Расширение вложений для `AgentChat` (пакет `@ykurilov/agent-chat-attachments/react`). */
73
+ export interface AttachmentsPlugin {
74
+ /** Хук: вызывается один раз за отрисовку, всегда в одном и том же порядке. */
75
+ useController: (input: {
76
+ api: string;
77
+ chatId: string;
78
+ /** Видит ли выбранная модель картинки; `undefined` — пока неизвестно. */
79
+ modelSeesImages: boolean | undefined;
80
+ fetch?: typeof globalThis.fetch;
81
+ }) => AttachmentsController;
82
+ }