@ykurilov/agent-chat-core 0.79.0 → 0.80.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 +14 -0
- package/README.md +1 -0
- package/dist/{chunk-AJSRGBVM.js → chunk-Z44ZAFZC.js} +34 -14
- package/dist/chunk-Z44ZAFZC.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/storage-contract.js +1 -1
- package/dist/types/agent-run/run-agent-turn.d.ts +2 -1
- package/dist/types/chat-service.d.ts +3 -1
- package/dist/types/frozen-context/context-blocks.d.ts +6 -1
- package/dist/types/frozen-context/freeze-context.d.ts +2 -0
- package/dist/types/index.d.ts +1 -1
- package/package.json +1 -1
- package/dist/chunk-AJSRGBVM.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @ykurilov/agent-chat-core
|
|
2
2
|
|
|
3
|
+
## 0.80.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 2cc8429: Добавлена серверная настройка `agent.contextBlocksRefresh: "history-start"`: блоки приложения читаются при первом снимке и после смены начала видимой истории, используя существующий frozen context ветки. Между этими событиями источник не вызывается и изменённые данные приложения не досылаются модели. По умолчанию и при `"turn"` поведение прежнее. Перезапуск и ветки используют сохранённый снимок; продолжение с выполненными шагами сохраняет frozen context. Retry без завершённых шагов создаёт новый ответ и может заново прочитать источник. Ошибка источника сохраняет fallback и оставляет следующей новой сборке повторную попытку.
|
|
8
|
+
|
|
9
|
+
### Как обновить приложение
|
|
10
|
+
|
|
11
|
+
Обновите установленные пакеты SDK одной версией. Если профиль должен застывать как память, добавьте `contextBlocksRefresh: "history-start"` рядом с `contextBlocks`. Изменения вне переписки передавайте через `prepareTurn.notices`; права по-прежнему проверяйте сервером. Для классификации каждого вопроса оставьте `"turn"`. Никаких SQL-миграций или собственного кэша не требуется. [Триггеры, ограничения и пример](https://github.com/ykurilov/agent-chat-sdk/blob/main/docs/context-blocks-refresh.md).
|
|
12
|
+
|
|
13
|
+
## 0.79.1
|
|
14
|
+
|
|
15
|
+
No changes in this release.
|
|
16
|
+
|
|
3
17
|
## 0.79.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -78,6 +78,7 @@ import type { ChatStorage } from "@ykurilov/agent-chat-core";
|
|
|
78
78
|
- Снимок перед каждым шагом: `agent.stepContext: ({ state, stepNumber, chat, subject, … }) => string | null` — текст ложится сообщением человека в самый конец запроса шага, после результатов инструментов, в историю не пишется. Снимок на шаге один, всё до него от шага к шагу те же байты (кэш модели). `null` — шаг без снимка, бросок — без снимка и строка в журнал. Функция видит те же `channel`, `audience`, `staff`, что инструменты (в группе личного не показывайте), данные чата — копиями. Срок `agent.stepContextTimeoutMs` (по умолчанию 5 с): не ответила — шаг без снимка, `abortSignal` отменён. Типы `StepContext`, `StepContextInput`.
|
|
79
79
|
- Застывающий контекст: память, сегодняшняя дата, место на экране и блоки приложения вписываются в вопрос человека один раз за ход и дальше не меняются ни на байт. Следующий ход побайтно начинается с последнего шага прошлого (с рассуждениями и инструментами), поэтому всё прошлое читается из кэша модели. В новый вопрос идут только изменившиеся блоки и строка `[Больше не действует: «ключ»]` про пропавшие. Источник может вернуть `{ key, hidden: true }` — блок есть, но не показан ради места: вместо «больше не действует» модель, видевшая его, получит `[Не показано ради места, но по-прежнему действует: «ключ»]`. Набор хода хранится служебной частью ответа `data-frozen-context` (`FROZEN_CONTEXT_PART_TYPE`): в поток, историю, `afterTurn` и поиск по истории она не уходит. Продолжение после подтверждения, автопродолжение, запасной провайдер и «Повторить» берут блоки из ответа и источники заново не опрашивают. Память строже остальных блоков: читается один раз на чат — в первом ходе, после сжатия истории, при смене начала истории (правка первого сообщения, «Переделать» первого ответа), в первом личном ходе после группового и после сбоя чтения — и в идущем чате не перечитывается и не досылается (ни изменения, ни новые записи, ни «Больше не действует»); `snapshot()` и `blocks()` в остальных ходах не зовутся. Своя долгая память может отдавать блоки по записям: `LongTermMemory.blocks?({ userId, chatId })`; без него — один блок из `snapshot()`.
|
|
80
80
|
- Блоки приложения в вопросе хода: `agent.contextBlocks: ({ chatId, ownerId, chat, state, subject, channel, audience, … }) => [{ key, text, private? }] | null` — список скиллов, роль, правила. Зовётся один раз в начале хода; текст вписывается в вопрос после памяти и даты и дальше не меняется ни на байт (кэш модели), ядро его не оборачивает. На следующих ходах в вопрос идут только изменённые блоки целиком и строка `[Больше не действует: «ключ»]` про убранные. Ключи `memory`, `memory:*`, `today`, `screen_place` заняты ядром: такой блок отбрасывается со строкой в журнал. `private: true` — в групповом ходе блок не показывается. Срок `agent.contextBlocksTimeoutMs` (по умолчанию 5 с; 0 или `Infinity` — без срока, иначе от 1000): не ответила или бросила — блоки приложения берутся из прошлого ответа, в журнал строка `[блоки контекста] …`, `abortSignal` отменён; «Стоп» — без строки. Блоки хранятся служебной частью ответа: в потоке, истории, `afterTurn` и поиске по истории её нет. Типы `ContextBlock`, `ContextBlocksInput`, `ContextBlocksSource`, `HiddenContextBlock`, `ContextBlockEntry`.
|
|
81
|
+
- `agent.contextBlocksRefresh: "history-start"` включает снимок приложения до смены начала видимой истории, как у памяти. Без настройки и при `"turn"` источник по-прежнему читается каждый новый ход. Снимок хранится в frozen context ветки; своего кэша или SQL-миграций не требуется. Изменения вне переписки передаются через `prepareTurn.notices`, права проверяются сервером. [Триггеры, ошибки и пример](https://github.com/ykurilov/agent-chat-sdk/blob/main/docs/context-blocks-refresh.md).
|
|
81
82
|
- Конец хода сразу после инструмента: `execute` возвращает `endTurn(output, { text? })` — модель следующего шага не делает, готовый `text` идёт в ответ обычной текстовой частью без запроса к провайдеру и без расхода; `output` модель и запись видят как обычно. Тип `EndTurnOptions`. С `endTurn(output, { requireText: true })` остановка принимается только при непустом пользовательском text-part текущего ответа; без него результат обычный и модель продолжает. Проверка в памяти потока, без чтения БД/flush. Пробелы и чисто форматные невидимые Unicode, reasoning, tool output, ошибки и прежняя история не считаются; Markdown/HTML по смыслу не разбирается. Явный непустой `options.text` сохраняет приоритет и прежний путь готового текста. Это не автоподтверждение и не гарантия ответа при отмене, лимите или сбое. [Полная семантика и переход](../../docs/in-product-agent.md#14-конец-хода-сразу-после-инструмента).
|
|
82
83
|
- Данные чата без записи: `service.getChatData(userId, chatId)` — копия, проверки владения как у `updateChatData`.
|
|
83
84
|
- Проверка ответа виджета на сервере: `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`, ответ не пишется.
|
|
@@ -6918,25 +6918,29 @@ function appBlocksSource(options2) {
|
|
|
6918
6918
|
// src/frozen-context/freeze-context.ts
|
|
6919
6919
|
var sourceOf = (key) => isMemoryKey(key) ? "memory" : isReservedKey(key) ? "core" : "app";
|
|
6920
6920
|
async function freezeContext(input) {
|
|
6921
|
-
const { chatId, sources, base, signal, memoryFrom } = input;
|
|
6921
|
+
const { chatId, sources, base, signal, memoryFrom, appContextFrom } = input;
|
|
6922
6922
|
const log = input.logError ?? console.error;
|
|
6923
6923
|
const fromBase = (source) => base?.data.blocks.filter((block) => sourceOf(block.key) === source) ?? [];
|
|
6924
|
-
const read = async (source, fetch) => {
|
|
6925
|
-
|
|
6924
|
+
const read = async (source, fetch, from) => {
|
|
6925
|
+
const previous = source === "memory" ? base?.data.memoryFrom : base?.data.appContextFrom;
|
|
6926
|
+
const kept = from !== void 0 && previous === from;
|
|
6927
|
+
if (kept || !fetch) return { blocks: fromBase(source), ...kept && { from } };
|
|
6926
6928
|
try {
|
|
6927
6929
|
const blocks2 = await fetch();
|
|
6928
6930
|
if (blocks2 !== null && blocks2 !== void 0 && !Array.isArray(blocks2)) throw new TypeError(`\u0436\u0434\u0451\u043C \u0441\u043F\u0438\u0441\u043E\u043A \u0431\u043B\u043E\u043A\u043E\u0432, \u0430 \u043F\u0440\u0438\u0448\u043B\u043E ${typeof blocks2}`);
|
|
6929
|
-
return { blocks: blocks2 ?? [],
|
|
6931
|
+
return { blocks: blocks2 ?? [], ...from !== void 0 && { from } };
|
|
6930
6932
|
} catch (error) {
|
|
6931
6933
|
if (!signal?.aborted) log(`[\u0431\u043B\u043E\u043A\u0438 \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430] \u0447\u0430\u0442=${chatId} \u0438\u0441\u0442\u043E\u0447\u043D\u0438\u043A=${source} \u0441\u0431\u043E\u0439: ${error instanceof Error ? error.message : "\u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430\u044F \u043E\u0448\u0438\u0431\u043A\u0430"}; \u0431\u043B\u043E\u043A\u0438 \u0438\u0437 \u043F\u0440\u043E\u0448\u043B\u043E\u0433\u043E \u0445\u043E\u0434\u0430`);
|
|
6932
|
-
return { blocks: fromBase(source)
|
|
6934
|
+
return { blocks: fromBase(source) };
|
|
6933
6935
|
}
|
|
6934
6936
|
};
|
|
6935
|
-
const
|
|
6936
|
-
const [memoryRead, appRead] = await Promise.all([read("memory", memoryKept ? void 0 : sources.memory), read("app", sources.app)]);
|
|
6937
|
+
const [memoryRead, appRead] = await Promise.all([read("memory", sources.memory, memoryFrom), read("app", sources.app, appContextFrom)]);
|
|
6937
6938
|
const memory = memoryRead.blocks;
|
|
6938
6939
|
const app = appRead.blocks;
|
|
6939
|
-
const
|
|
6940
|
+
const marks = {
|
|
6941
|
+
...memoryRead.from !== void 0 && { memoryFrom: memoryRead.from },
|
|
6942
|
+
...appRead.from !== void 0 && { appContextFrom: appRead.from }
|
|
6943
|
+
};
|
|
6940
6944
|
const accepted = /* @__PURE__ */ new Set();
|
|
6941
6945
|
const hidden = /* @__PURE__ */ new Set();
|
|
6942
6946
|
const accept = (block, source) => {
|
|
@@ -6962,7 +6966,7 @@ async function freezeContext(input) {
|
|
|
6962
6966
|
accepted.add(SCREEN_PLACE_KEY);
|
|
6963
6967
|
blocks.push(...app.filter((block) => accept(block, "app")).map(copyBlock));
|
|
6964
6968
|
if (sources.place) blocks.push({ key: SCREEN_PLACE_KEY, text: sources.place });
|
|
6965
|
-
if (!base) return { blocks, sent: "all", base: null, ...
|
|
6969
|
+
if (!base) return { blocks, sent: "all", base: null, ...marks };
|
|
6966
6970
|
const before = new Map(base.data.blocks.map((block) => [block.key, block.text]));
|
|
6967
6971
|
const keys = new Set(blocks.map((block) => block.key));
|
|
6968
6972
|
const gone = base.data.blocks.filter((block) => !keys.has(block.key)).map((block) => block.key);
|
|
@@ -6975,7 +6979,7 @@ async function freezeContext(input) {
|
|
|
6975
6979
|
...hiddenNow.length > 0 && { hidden: hiddenNow }
|
|
6976
6980
|
},
|
|
6977
6981
|
base: base.id,
|
|
6978
|
-
...
|
|
6982
|
+
...marks
|
|
6979
6983
|
};
|
|
6980
6984
|
}
|
|
6981
6985
|
function copyBlock(block) {
|
|
@@ -6993,7 +6997,7 @@ async function resumeNoteForTurn(input, turn) {
|
|
|
6993
6997
|
if (!seen) return void 0;
|
|
6994
6998
|
const place = input.samePlace ? seen.blocks.find((block) => block.key === SCREEN_PLACE_KEY)?.text ?? null : screenPlaceBlock(input.place);
|
|
6995
6999
|
const note = await freezeTurnContext(input, turn, { id: turn.answer.id, data: seen }, place);
|
|
6996
|
-
return hasNewBlocks(note) ? note : void 0;
|
|
7000
|
+
return hasNewBlocks(note) || note.appContextFrom !== seen.appContextFrom ? note : void 0;
|
|
6997
7001
|
}
|
|
6998
7002
|
async function freezeTurnContext(input, turn, base, place) {
|
|
6999
7003
|
const sources = {
|
|
@@ -7027,11 +7031,13 @@ async function freezeTurnContext(input, turn, base, place) {
|
|
|
7027
7031
|
}
|
|
7028
7032
|
});
|
|
7029
7033
|
}
|
|
7034
|
+
const historyStart = turn.history[0]?.id ?? input.messageId;
|
|
7030
7035
|
return freezeContext({
|
|
7031
7036
|
chatId: input.chatId,
|
|
7032
7037
|
sources,
|
|
7033
7038
|
// Начало видимой истории: то же — память из базы; после сжатия другое — память заново. Агент говорит первым — сам ответ.
|
|
7034
|
-
memoryFrom:
|
|
7039
|
+
memoryFrom: historyStart,
|
|
7040
|
+
...input.contextBlocksRefresh === "history-start" && { appContextFrom: historyStart },
|
|
7035
7041
|
base,
|
|
7036
7042
|
signal: input.abortSignal,
|
|
7037
7043
|
...input.logError && { logError: input.logError }
|
|
@@ -7232,7 +7238,17 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
7232
7238
|
let resumeNote = resume && !resume.replay && !frozen.fresh ? await resumeNoteForTurn(input, { history: answer.request().history, answer: resume.message, state: publicAppState(appState.values()), ...viewer && { viewer } }) : void 0;
|
|
7233
7239
|
if (resume && turnNotices && !frozen.fresh) {
|
|
7234
7240
|
const seen = latestFrozenContextOf(resume.message.parts);
|
|
7235
|
-
if (seen)
|
|
7241
|
+
if (seen) {
|
|
7242
|
+
resumeNote = {
|
|
7243
|
+
...resumeNote ?? {
|
|
7244
|
+
blocks: seen.blocks,
|
|
7245
|
+
sent: { changed: [], removed: [] },
|
|
7246
|
+
base: messageId,
|
|
7247
|
+
...input.contextBlocksRefresh === "history-start" && seen.appContextFrom !== void 0 && { appContextFrom: seen.appContextFrom }
|
|
7248
|
+
},
|
|
7249
|
+
notices: turnNotices
|
|
7250
|
+
};
|
|
7251
|
+
}
|
|
7236
7252
|
}
|
|
7237
7253
|
const outages = input.providerOutages ?? createProviderOutages({ now: () => (input.now ?? (() => /* @__PURE__ */ new Date()))().getTime(), logError });
|
|
7238
7254
|
const allFallbackIds = fallbackProviders({
|
|
@@ -7843,6 +7859,7 @@ async function openAttempt(input, attempt, attemptSignal) {
|
|
|
7843
7859
|
stepContext,
|
|
7844
7860
|
stepContextTimeoutMs,
|
|
7845
7861
|
contextBlocks: _contextBlocks,
|
|
7862
|
+
contextBlocksRefresh: _contextBlocksRefresh,
|
|
7846
7863
|
contextBlocksTimeoutMs: _contextBlocksTimeoutMs,
|
|
7847
7864
|
...requestInput
|
|
7848
7865
|
} = input;
|
|
@@ -12904,6 +12921,9 @@ function createChatService(input) {
|
|
|
12904
12921
|
prepareTurn,
|
|
12905
12922
|
...agentSettings
|
|
12906
12923
|
} = options2.agent;
|
|
12924
|
+
if (agentSettings.contextBlocksRefresh !== void 0 && agentSettings.contextBlocksRefresh !== "turn" && agentSettings.contextBlocksRefresh !== "history-start") {
|
|
12925
|
+
throw new TypeError('agent.contextBlocksRefresh: \u0436\u0434\u0451\u043C "turn" \u0438\u043B\u0438 "history-start"');
|
|
12926
|
+
}
|
|
12907
12927
|
const stepContextTimeoutMs = resolveTimeoutMs(stepContextTimeoutSetting, DEFAULT_STEP_CONTEXT_TIMEOUT_MS, "agent.stepContextTimeoutMs");
|
|
12908
12928
|
const contextBlocksTimeoutMs = resolveTimeoutMs(contextBlocksTimeoutSetting, DEFAULT_CONTEXT_BLOCKS_TIMEOUT_MS, "agent.contextBlocksTimeoutMs");
|
|
12909
12929
|
const turnLimits = resolveTurnLimits({ turnTimeoutMs, maxOutputTokens }, options2.catalog);
|
|
@@ -15303,4 +15323,4 @@ export {
|
|
|
15303
15323
|
InvalidWidgetAnswerError,
|
|
15304
15324
|
createChatService
|
|
15305
15325
|
};
|
|
15306
|
-
//# sourceMappingURL=chunk-
|
|
15326
|
+
//# sourceMappingURL=chunk-Z44ZAFZC.js.map
|