@ykurilov/agent-chat-core 0.80.0 → 0.81.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 +87 -0
- package/dist/{chunk-Z44ZAFZC.js → chunk-YGHGURNF.js} +72 -29
- package/dist/chunk-YGHGURNF.js.map +1 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/storage-contract.js +1 -1
- package/dist/types/agent-run/build-model-request.d.ts +3 -9
- package/dist/types/agent-run/channel-notes.d.ts +14 -1
- package/dist/types/compaction/run-compaction.d.ts +4 -0
- package/dist/types/compaction/summary-part.d.ts +10 -0
- package/dist/types/frozen-context/frozen-context-messages.d.ts +8 -2
- package/dist/types/index.d.ts +1 -1
- package/dist/types/notices/agent-notice.d.ts +12 -1
- package/package.json +1 -1
- package/dist/chunk-Z44ZAFZC.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,92 @@
|
|
|
1
1
|
# @ykurilov/agent-chat-core
|
|
2
2
|
|
|
3
|
+
## 0.81.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 58204d2: **Ломает приложения со своим хранилищем мессенджеров или сценариев.** Хранилища из пакетов (память, SQLite,
|
|
8
|
+
Postgres) уже готовы — если вы передаёте их, менять ничего не нужно.
|
|
9
|
+
|
|
10
|
+
- `MessengerStore`: новый обязательный метод `saveLinkIfFree` и признак `guarded: true`. Привязка аккаунта по коду
|
|
11
|
+
идёт только через `saveLinkIfFree`: два кода разных людей на один аккаунт мессенджера больше не переписывают друг
|
|
12
|
+
друга.
|
|
13
|
+
- `FlowStore`: новый обязательный метод `swapDelivery` и признак `guarded: true`.
|
|
14
|
+
- Без `guarded: true` `createMessengers` и `createAgentFlow` сразу бросают `TypeError`. Признак — обещание, что
|
|
15
|
+
хранилище выполняет условия держателя и аренды (список — в шаге 3 ниже).
|
|
16
|
+
|
|
17
|
+
Новое для своей доставки сценария (`attach({ deliver })`):
|
|
18
|
+
|
|
19
|
+
- Исход `{ status: "await_repair" }` — канал сломан и ждёт починки (например, отозван токен бота). Ответ ждёт,
|
|
20
|
+
попытка не тратится, новых ходов и касаний нет. Починили — `flow.resumeDelivery({ ownerId, chatId, messageId })`:
|
|
21
|
+
тот же `messageId`, текст и части уходят ближайшим обходом таймеров. Ответ: `resumed`, `pending`, `busy`,
|
|
22
|
+
`not_found` или `dropped` (человек уже написал после ответа — ответ снят и помечен недоставленным). Ожидание
|
|
23
|
+
переживает перезапуск. В `reportDelivery` этот исход — `TypeError`.
|
|
24
|
+
- Пока идёт `deliver`, аренда чата продлевается: вторая копия сервера ту же попытку не начинает. Копия упала посреди
|
|
25
|
+
отправки — та же попытка повторяется через её паузу. Итог устаревшей копии не пишется.
|
|
26
|
+
- Перед самым `deliver`, уже под резервом, SDK ещё раз сверяет: разговор не закончен после хода (`flow.end`), ответ
|
|
27
|
+
есть в ветке, после починки человек не написал после него. Иначе `deliver` не зовётся, ответ помечен недоставленным.
|
|
28
|
+
- Повтор доставки прощания у законченного разговора теперь доходит — без нового хода и без открытия разговора.
|
|
29
|
+
- «Ровно один раз» снаружи SDK не обещает: после падения копии тот же ответ может уйти ещё раз с тем же `messageId`.
|
|
30
|
+
Двойную отправку отсекает приложение по `messageId`.
|
|
31
|
+
|
|
32
|
+
Остальные исправления аудита работают без правок в приложении:
|
|
33
|
+
|
|
34
|
+
- Ход группы мессенджера остаётся групповым и тогда, когда агент пишет первым, когда человек правит сообщение
|
|
35
|
+
группы, шлёт поправку к ходу или нажимает кнопку-подсказку: без памяти и личных блоков владельца, только
|
|
36
|
+
разрешённые группе инструменты, с пределом сообщений участника.
|
|
37
|
+
- Удаление чата ждёт хода на любой копии сервера: идёт ход — `ChatBusyError`, чат цел.
|
|
38
|
+
- Не удалось отметить вложения сообщения прикреплёнными — ошибка доходит до вызывающего, сообщение не записывается,
|
|
39
|
+
место в счёте сообщений отдаётся назад; повтор того же сообщения проходит.
|
|
40
|
+
- Сценарий, который не сходится с подтверждениями приложения (`confirmations`, `exitGuards`), проверяется до записи
|
|
41
|
+
версии и закрепления чата: чат идёт без сценария, перевод чатов на такую версию — отказ.
|
|
42
|
+
- Копия сервера, у которой забрали аренду таймера сценария или обновления мессенджера, больше не шлёт касание и не
|
|
43
|
+
переписывает срок и итог другой копии.
|
|
44
|
+
|
|
45
|
+
### Как обновить приложение
|
|
46
|
+
|
|
47
|
+
Нужно только если приложение передаёт в `createMessengers` или `createAgentFlow` своё хранилище (не
|
|
48
|
+
`createMemoryMessengerStore`, `createSqliteMessengerStore`, `createPostgresMessengerStore`, `memoryFlowStore`,
|
|
49
|
+
`sqliteFlowStore`, `postgresFlowStore`).
|
|
50
|
+
|
|
51
|
+
1. Найдите свою реализацию `MessengerStore` (поиск по `MessengerStore` или `saveLink(`).
|
|
52
|
+
2. Добавьте метод — одной атомарной операцией базы:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
async saveLinkIfFree(link) {
|
|
56
|
+
// Аккаунт свободен или уже за link.userId — записать (имя и время обновить) и вернуть "saved".
|
|
57
|
+
// Аккаунт за другим пользователем — ничего не менять и вернуть "taken".
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
В SQL это `insert … on conflict (messenger, bot_id, external_user_id) do update set … where user_id = $userId
|
|
62
|
+
returning …`: строка вернулась — `"saved"`, нет — `"taken"`. Отдельные «найти, потом записать» не годятся: между
|
|
63
|
+
ними успевает второй код.
|
|
64
|
+
3. Добавьте признак `guarded: true` в объект хранилища. Он обещает:
|
|
65
|
+
- `finishUpdate(key, status, now, holder)`, `renewUpdates` и `releaseUpdate` пишут, только пока обновление за этим
|
|
66
|
+
держателем; `acquireLease` не отдаёт чужую живую аренду;
|
|
67
|
+
- `saveLinkIfFree` — одна атомарная операция.
|
|
68
|
+
4. Своё `FlowStore` (если есть): метод `swapDelivery(chatId, { expected, next, due, claim, freeAt, endedAt })` одной
|
|
69
|
+
транзакцией. Заменить запись доставки, только если сейчас там `expected` (совпали `messageId`, `attempt` и `token`;
|
|
70
|
+
`null` — записи нет). С `claim` — только если аренда чата свободна, уже у `claim.owner` или у `claim.from`, и взять
|
|
71
|
+
её до `now + leaseMs`. С `freeAt` — только если живой аренды нет. С `endedAt` — только если конец разговора всё
|
|
72
|
+
ещё этот (`null` — не закончен). `due` записать вместе с записью. Вернуть `true`,
|
|
73
|
+
если заменил этот вызов. Плюс `guarded: true`, который обещает:
|
|
74
|
+
- `setDue(…, { leaseOwner })` пишет, только пока аренда у этой копии; `{ unlessDelivery: true }` — только если
|
|
75
|
+
доставки не ждёт;
|
|
76
|
+
- `leaseDue` не берёт живую аренду, а законченные разговоры берёт только со сроком `FLOW_DELIVERY_EXIT`;
|
|
77
|
+
- `endChat` при ждущей доставке не снимает срок и аренду.
|
|
78
|
+
Образец — `postgresFlowStore` и `sqliteFlowStore` в `@ykurilov/agent-chat-flows`.
|
|
79
|
+
5. Проверка: `pnpm typecheck` проходит. Без признака или без `saveLinkIfFree` запуск сразу бросает `TypeError` с
|
|
80
|
+
подсказкой, без `swapDelivery` — `attach({ deliver })`.
|
|
81
|
+
|
|
82
|
+
## 0.80.1
|
|
83
|
+
|
|
84
|
+
### Patch Changes
|
|
85
|
+
|
|
86
|
+
- d59ae4e: После сжатия истории в режиме `contextBlocksRefresh: "history-start"` полный свежий снимок приложения размещается рядом со сводкой. Старые снимки оставленного хвоста больше не возвращают устаревшие данные; последующие изменения идут от свежей базы. Ошибка источника сохраняет прежний fallback, успешный пустой снимок тоже учитывается. Дата, память, место и notices не переносятся; сохранённые ответы не переписываются.
|
|
87
|
+
|
|
88
|
+
Запрос сжатия использует тот же сборщик frozen context, что и обычный ход, с исключением личных app-блоков, памяти, даты и места из сохраняемой сводки. Задание пересказа уточняет границу сжимаемой части. Настройки рассуждений провайдера не меняются. Обновить все пакеты одной версией; SQL-миграций и новых настроек нет.
|
|
89
|
+
|
|
3
90
|
## 0.80.0
|
|
4
91
|
|
|
5
92
|
### Minor Changes
|
|
@@ -1700,9 +1700,13 @@ function channelOfMessage(message) {
|
|
|
1700
1700
|
}
|
|
1701
1701
|
var isNoticeMessage = (message) => message.parts.some((part) => part.type === AGENT_NOTICE_PART_TYPE);
|
|
1702
1702
|
var isHumanMessage = (message) => message.role === "user" && !isNoticeMessage(message) && !isStateMessage(message);
|
|
1703
|
+
var hasChannelPart = (message) => message.parts.some((part) => part.type === CHANNEL_PART_TYPE);
|
|
1703
1704
|
function turnChannelOf(branch) {
|
|
1704
|
-
const
|
|
1705
|
-
return
|
|
1705
|
+
const source = branch.findLast((message) => isHumanMessage(message) || message.role === "user" && isNoticeMessage(message) && hasChannelPart(message));
|
|
1706
|
+
return source ? channelOfMessage(source) : { channel: "web" };
|
|
1707
|
+
}
|
|
1708
|
+
function inheritedGroupPart(scope) {
|
|
1709
|
+
return scope.group ? channelPart(scope.channel, scope.group) : void 0;
|
|
1706
1710
|
}
|
|
1707
1711
|
function turnOriginOf(branch) {
|
|
1708
1712
|
const parent = branch.at(-2);
|
|
@@ -4656,6 +4660,11 @@ function withStaffRepliesJoined(history) {
|
|
|
4656
4660
|
return changed ? joined : history;
|
|
4657
4661
|
}
|
|
4658
4662
|
|
|
4663
|
+
// src/compaction/summary-part.ts
|
|
4664
|
+
var SUMMARY = /* @__PURE__ */ Symbol("compaction-summary");
|
|
4665
|
+
var summaryPart = (text) => ({ type: "text", text, [SUMMARY]: true });
|
|
4666
|
+
var isSummaryPart = (part) => part[SUMMARY] === true;
|
|
4667
|
+
|
|
4659
4668
|
// src/frozen-context/frozen-context-messages.ts
|
|
4660
4669
|
function frozenContextBase(history) {
|
|
4661
4670
|
for (let index = history.length - 1; index >= 0; index -= 1) {
|
|
@@ -4670,6 +4679,11 @@ var RESUME_NOTE_ID = "frozen-context-note-";
|
|
|
4670
4679
|
function withFrozenContext(history, options2) {
|
|
4671
4680
|
const { current, group } = options2;
|
|
4672
4681
|
const dataOf = (message) => message.role !== "assistant" ? void 0 : current && message.id === current.answerId ? current.data : frozenContextOf(message.parts);
|
|
4682
|
+
const root = history[0];
|
|
4683
|
+
const anchorHistory = options2.anchorHistory && options2.anchorHistory[0]?.id === root?.id ? options2.anchorHistory : history;
|
|
4684
|
+
const allowed = options2.includeKey ?? (() => true);
|
|
4685
|
+
const anchor = root?.parts.some(isSummaryPart) ? anchorHistory.flatMap((message) => message.role === "assistant" ? [dataOf(message), ...message.parts.filter((p) => p.type === FROZEN_CONTEXT_PART_TYPE).slice(1).map((p) => p.data)] : []).concat(current?.data).find((data) => data?.appContextFrom === root.id) : void 0;
|
|
4686
|
+
let anchored = false;
|
|
4673
4687
|
const seen = /* @__PURE__ */ new Map();
|
|
4674
4688
|
const output = [];
|
|
4675
4689
|
const place = (texts) => {
|
|
@@ -4679,22 +4693,28 @@ function withFrozenContext(history, options2) {
|
|
|
4679
4693
|
if (last?.role === "user") output[output.length - 1] = { ...last, parts: [...last.parts, ...parts] };
|
|
4680
4694
|
else output.push({ id: `frozen-context-${output.length}`, role: "user", parts: output.length === 0 ? [{ type: "text", text: GREETING_LEAD }, ...parts] : parts });
|
|
4681
4695
|
};
|
|
4682
|
-
const textsOf = (data
|
|
4696
|
+
const textsOf = (data, base) => {
|
|
4697
|
+
const include = (key) => allowed(key) && (!anchor || anchored || isReservedKey(key));
|
|
4698
|
+
const texts = blockTexts(data, base, group, anchor || options2.includeKey ? include : void 0);
|
|
4699
|
+
if (data === anchor) anchored = true;
|
|
4700
|
+
return texts;
|
|
4701
|
+
};
|
|
4683
4702
|
let currentPlaced = false;
|
|
4684
4703
|
for (const message of history) {
|
|
4685
4704
|
const data = dataOf(message);
|
|
4686
4705
|
if (data) {
|
|
4687
|
-
place(textsOf(data));
|
|
4706
|
+
place(textsOf(data, data.base === null ? void 0 : seen.get(data.base)));
|
|
4688
4707
|
seen.set(message.id, data);
|
|
4689
4708
|
if (message.id === current?.answerId) currentPlaced = true;
|
|
4690
4709
|
}
|
|
4691
|
-
|
|
4692
|
-
|
|
4710
|
+
const placed = anchor && message === root ? { ...message, parts: message.parts.flatMap((part) => isSummaryPart(part) ? [part, ...anchor.blocks.filter((block) => !isReservedKey(block.key) && allowed(block.key) && !(group && block.private)).map((block) => ({ type: "text", text: block.text }))] : [part]) } : message;
|
|
4711
|
+
output.push(...withResumeNotes(placed, (note) => {
|
|
4712
|
+
const texts = textsOf(note, seen.get(message.id));
|
|
4693
4713
|
seen.set(message.id, note);
|
|
4694
4714
|
return texts;
|
|
4695
4715
|
}));
|
|
4696
4716
|
}
|
|
4697
|
-
if (current && !currentPlaced) place(textsOf(current.data));
|
|
4717
|
+
if (current && !currentPlaced) place(textsOf(current.data, current.data.base === null ? void 0 : seen.get(current.data.base)));
|
|
4698
4718
|
return output;
|
|
4699
4719
|
}
|
|
4700
4720
|
function withResumeNotes(message, textsOf) {
|
|
@@ -4724,11 +4744,11 @@ function holdNoteAfterApprovals(messages) {
|
|
|
4724
4744
|
return { messages: messages.slice(0, -1), note };
|
|
4725
4745
|
}
|
|
4726
4746
|
var awaitsApprovedRun = (part) => part.state === "approval-responded";
|
|
4727
|
-
function blockTexts(data, base, group) {
|
|
4728
|
-
return [...changedBlockTexts(data, base, group), ...(data.notices ?? []).map(noticeText)];
|
|
4747
|
+
function blockTexts(data, base, group, include) {
|
|
4748
|
+
return [...changedBlockTexts(data, base, group, include), ...(data.notices ?? []).map(noticeText)];
|
|
4729
4749
|
}
|
|
4730
|
-
function changedBlockTexts(data, base, group) {
|
|
4731
|
-
const shown = (block) => !(group && block.private);
|
|
4750
|
+
function changedBlockTexts(data, base, group, include) {
|
|
4751
|
+
const shown = (block) => (!include || include(block.key)) && !(group && block.private);
|
|
4732
4752
|
if (data.sent === "all" || !base) return data.blocks.filter(shown).map((block) => block.text);
|
|
4733
4753
|
const now = new Map(data.blocks.map((block) => [block.key, block]));
|
|
4734
4754
|
const before = new Map(base.blocks.map((block) => [block.key, block]));
|
|
@@ -4736,7 +4756,7 @@ function changedBlockTexts(data, base, group) {
|
|
|
4736
4756
|
const block = now.get(key);
|
|
4737
4757
|
return block && shown(block) ? [block.text] : [];
|
|
4738
4758
|
});
|
|
4739
|
-
const visibleKeys = (keys) => keys.filter((key) => !(group && (before.get(key)?.private ?? isMemoryKey(key))));
|
|
4759
|
+
const visibleKeys = (keys) => keys.filter((key) => (!include || include(key)) && !(group && (before.get(key)?.private ?? isMemoryKey(key))));
|
|
4740
4760
|
const removed = visibleKeys(data.sent.removed);
|
|
4741
4761
|
if (removed.length > 0) texts.push(noLongerValid(removed));
|
|
4742
4762
|
const hidden = visibleKeys(data.sent.hidden ?? []);
|
|
@@ -5106,7 +5126,10 @@ async function buildModelRequest(input) {
|
|
|
5106
5126
|
const cleaned = sendableHistory(input.serviceBlocks ? withoutForgedBlocks(untitled, input.serviceBlocks) : untitled);
|
|
5107
5127
|
const sendable = input.switchableTools ? withSwitchedOffCallsAsText(cleaned, input.switchableTools, tools) : cleaned;
|
|
5108
5128
|
const noted = withStaffRepliesJoined(withChannelNotes(withModeNotes(withBurstsJoined(withCorrectionsJoined(sendable)), input.modeNotes)));
|
|
5109
|
-
const held = input.frozenContext ? holdNoteAfterApprovals(withFrozenContext(noted,
|
|
5129
|
+
const held = input.frozenContext ? holdNoteAfterApprovals(withFrozenContext(noted, {
|
|
5130
|
+
...input.frozenContext,
|
|
5131
|
+
...input.frozenContext.anchorHistory && { anchorHistory: sendableHistory(input.frozenContext.anchorHistory) }
|
|
5132
|
+
})) : { messages: noted };
|
|
5110
5133
|
const history = selectReasoningForModel(held.messages, {
|
|
5111
5134
|
modelId: profile.id,
|
|
5112
5135
|
providerId: provider.id,
|
|
@@ -8620,7 +8643,8 @@ function shortOutput(value) {
|
|
|
8620
8643
|
var MAX_SERVER_TURNS_IN_ROW = 5;
|
|
8621
8644
|
function noticeMessageParts(input) {
|
|
8622
8645
|
const data = { notices: input.notices, deliveryKey: input.deliveryKey, wakeAgent: input.wakeAgent };
|
|
8623
|
-
|
|
8646
|
+
const channel = channelPart(input.channel ?? "web", input.group);
|
|
8647
|
+
return [{ type: AGENT_NOTICE_PART_TYPE, data }, ...channel ? [channel] : []];
|
|
8624
8648
|
}
|
|
8625
8649
|
function noticeDataOf(message) {
|
|
8626
8650
|
if (message.role !== "user") return void 0;
|
|
@@ -11577,7 +11601,7 @@ function tokens(count) {
|
|
|
11577
11601
|
}
|
|
11578
11602
|
function compactionTask(input) {
|
|
11579
11603
|
if (input.instruction !== void 0) return input.instruction;
|
|
11580
|
-
return `\u0421\u0434\u0435\u043B\u0430\u0439 \u0441\u0432\u043E\u0434\u043A\u0443 \
|
|
11604
|
+
return `\u0421\u0434\u0435\u043B\u0430\u0439 \u0441\u0432\u043E\u0434\u043A\u0443 \u0441\u0436\u0438\u043C\u0430\u0435\u043C\u043E\u0439 \u0447\u0430\u0441\u0442\u0438 \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u043A\u0438 \u0432\u044B\u0448\u0435. \u041E\u043D\u0430 \u0437\u0430\u043A\u0430\u043D\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044F \u043D\u0430 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u043C \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0438 \u043F\u0435\u0440\u0435\u0434 \u044D\u0442\u0438\u043C \u0437\u0430\u0434\u0430\u043D\u0438\u0435\u043C; \u0431\u043E\u043B\u0435\u0435 \u043F\u043E\u0437\u0434\u043D\u044F\u044F \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u043A\u0430 \u043E\u0441\u0442\u0430\u043D\u0435\u0442\u0441\u044F \u0432 \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0435 \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E. \u041F\u0435\u0440\u0435\u0441\u043A\u0430\u0437 \u0434\u043E\u043B\u0436\u0435\u043D \u043E\u0442\u0440\u0430\u0436\u0430\u0442\u044C \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u043D\u0430 \u044D\u0442\u043E\u0439 \u0433\u0440\u0430\u043D\u0438\u0446\u0435, \u0447\u0442\u043E\u0431\u044B \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u044C \u0440\u0430\u0437\u0433\u043E\u0432\u043E\u0440 \u0432\u043C\u0435\u0441\u0442\u0435 \u0441 \u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u043D\u044B\u043C \u0445\u0432\u043E\u0441\u0442\u043E\u043C, \u0431\u0435\u0437 \u043F\u043E\u0432\u0442\u043E\u0440\u0435\u043D\u0438\u044F \u0443\u0436\u0435 \u0441\u0434\u0435\u043B\u0430\u043D\u043D\u043E\u0433\u043E \u0432 \u043D\u0451\u043C.
|
|
11581
11605
|
|
|
11582
11606
|
\u0415\u0441\u043B\u0438 \u0432 \u043D\u0430\u0447\u0430\u043B\u0435 \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u043A\u0438 \u0443\u0436\u0435 \u0435\u0441\u0442\u044C \u0441\u0432\u043E\u0434\u043A\u0430, \u043D\u0435 \u0434\u043E\u043F\u0438\u0441\u044B\u0432\u0430\u0439 \u043A \u043D\u0435\u0439, \u0430 \u043F\u0435\u0440\u0435\u043F\u0438\u0448\u0438 \u0435\u0451 \u0446\u0435\u043B\u0438\u043A\u043E\u043C \u0432\u043C\u0435\u0441\u0442\u0435 \u0441 \u043D\u043E\u0432\u044B\u043C\u0438 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F\u043C\u0438: \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u0441\u044F \u043E\u0434\u043D\u0430 \u0441\u0432\u043E\u0434\u043A\u0430.
|
|
11583
11607
|
|
|
@@ -11608,10 +11632,10 @@ ${summary}
|
|
|
11608
11632
|
function applyCompaction(branch, active) {
|
|
11609
11633
|
if (!active) return branch.map(toAgentChatMessage);
|
|
11610
11634
|
const tail = branch.slice(active.throughIndex + 1).map(toAgentChatMessage);
|
|
11611
|
-
const
|
|
11635
|
+
const summary = summaryPart(summaryFrame(active.record.summary));
|
|
11612
11636
|
const [first, ...rest] = tail;
|
|
11613
|
-
if (first?.role === "user") return [{ ...first, parts: [
|
|
11614
|
-
return [{ id: `${active.record.id}-summary`, role: "user", parts: [
|
|
11637
|
+
if (first?.role === "user") return [{ ...first, parts: [summary, ...first.parts] }, ...rest];
|
|
11638
|
+
return [{ id: `${active.record.id}-summary`, role: "user", parts: [summary] }, ...tail];
|
|
11615
11639
|
}
|
|
11616
11640
|
|
|
11617
11641
|
// src/compaction/run-compaction.ts
|
|
@@ -11643,6 +11667,13 @@ async function runCompaction(input) {
|
|
|
11643
11667
|
const request = await buildModelRequest({
|
|
11644
11668
|
...requestInput,
|
|
11645
11669
|
...input.model,
|
|
11670
|
+
// Общая сводка не несёт privacy-схему: только публичные app blocks, без памяти/даты/места.
|
|
11671
|
+
// Пересказ шагов остаётся на прежнем пути без frozen blocks; никакого нового private opt-in.
|
|
11672
|
+
...!input.transient && { frozenContext: {
|
|
11673
|
+
group: true,
|
|
11674
|
+
includeKey: (key) => !isReservedKey(key),
|
|
11675
|
+
...input.contextHistory && { anchorHistory: input.contextHistory }
|
|
11676
|
+
} },
|
|
11646
11677
|
// Бюджет рассуждений — от предела сводки, а не от потолка модели: иначе он больше предела.
|
|
11647
11678
|
maxOutputTokens,
|
|
11648
11679
|
...lowestLevel && { reasoning: lowestLevel },
|
|
@@ -11653,7 +11684,7 @@ async function runCompaction(input) {
|
|
|
11653
11684
|
role: "user",
|
|
11654
11685
|
content: input.task ?? compactionTask({ summaryCeilingTokens: settings.summaryCeilingTokens, instruction: settings.instruction })
|
|
11655
11686
|
};
|
|
11656
|
-
let base = [...request.messages, task];
|
|
11687
|
+
let base = [...request.messages, ...request.noteAfterApprovals ?? [], task];
|
|
11657
11688
|
let messages = base;
|
|
11658
11689
|
let summary = "";
|
|
11659
11690
|
let summaryTokens = 0;
|
|
@@ -11788,7 +11819,8 @@ function createCompactionService(options2) {
|
|
|
11788
11819
|
}
|
|
11789
11820
|
overflow?.onStart();
|
|
11790
11821
|
model = settings.model ?? model;
|
|
11791
|
-
const
|
|
11822
|
+
const active = activeCompaction(branch, compactions);
|
|
11823
|
+
const history = applyCompaction(branch.slice(0, plan.throughIndex + 1), active);
|
|
11792
11824
|
const chat = await storage.getChat(chatId);
|
|
11793
11825
|
const ownerId = chat?.ownerId ?? "";
|
|
11794
11826
|
const result = await runCompaction({
|
|
@@ -11801,6 +11833,7 @@ function createCompactionService(options2) {
|
|
|
11801
11833
|
...options2.switchableTools && { switchableTools: options2.switchableTools },
|
|
11802
11834
|
...observation(ownerId, answerId),
|
|
11803
11835
|
history,
|
|
11836
|
+
contextHistory: applyCompaction(branch, active),
|
|
11804
11837
|
...options2.attachments && { attachments: options2.attachments },
|
|
11805
11838
|
...options2.serviceBlocks && { serviceBlocks: options2.serviceBlocks },
|
|
11806
11839
|
settings,
|
|
@@ -11945,6 +11978,7 @@ function createCompactionService(options2) {
|
|
|
11945
11978
|
...options2.switchableTools && { switchableTools: options2.switchableTools },
|
|
11946
11979
|
...observation(ownerId, answerId),
|
|
11947
11980
|
history: [...history, { id: `${chatId}-answer-steps`, role: "assistant", parts: steps }],
|
|
11981
|
+
transient: true,
|
|
11948
11982
|
...options2.attachments && { attachments: options2.attachments },
|
|
11949
11983
|
...options2.serviceBlocks && { serviceBlocks: options2.serviceBlocks },
|
|
11950
11984
|
settings,
|
|
@@ -13159,9 +13193,9 @@ function createChatService(input) {
|
|
|
13159
13193
|
throw error;
|
|
13160
13194
|
}
|
|
13161
13195
|
}
|
|
13162
|
-
async function confirmFiles(
|
|
13196
|
+
async function confirmFiles(fileParts) {
|
|
13163
13197
|
if (fileParts.length === 0) return;
|
|
13164
|
-
await options2.attachments.confirmUserParts(fileParts)
|
|
13198
|
+
await options2.attachments.confirmUserParts(fileParts);
|
|
13165
13199
|
}
|
|
13166
13200
|
async function ownChat(userId, chatId) {
|
|
13167
13201
|
const chat = await storage.getChat(chatId);
|
|
@@ -13878,7 +13912,7 @@ function createChatService(input) {
|
|
|
13878
13912
|
// Окно тишины пачки считается от этого времени: по часам службы.
|
|
13879
13913
|
...burst && { createdAt: writtenAt.toISOString() }
|
|
13880
13914
|
});
|
|
13881
|
-
await confirmFiles(
|
|
13915
|
+
await confirmFiles(fileParts);
|
|
13882
13916
|
await storage.insertMessage(question3);
|
|
13883
13917
|
return question3;
|
|
13884
13918
|
});
|
|
@@ -14114,14 +14148,20 @@ function createChatService(input) {
|
|
|
14114
14148
|
return await writingCountedMessage(userId, chatId, async () => {
|
|
14115
14149
|
const queued = await steering.accept(chatId, async (parentId) => {
|
|
14116
14150
|
const fileParts = await acceptFiles(userId, chatId, files);
|
|
14151
|
+
const groupPart = inheritedGroupPart(turnChannelOf(await currentBranch(chatId)));
|
|
14117
14152
|
const correction = newStoredMessage({
|
|
14118
14153
|
id: message.id,
|
|
14119
14154
|
chatId,
|
|
14120
14155
|
parentId,
|
|
14121
14156
|
role: "user",
|
|
14122
|
-
parts: [
|
|
14157
|
+
parts: [
|
|
14158
|
+
...await filePartsWithNumbers(chatId, fileParts),
|
|
14159
|
+
...text.trim().length > 0 ? [{ type: "text", text }] : [],
|
|
14160
|
+
steerPart(),
|
|
14161
|
+
...groupPart ? [groupPart] : []
|
|
14162
|
+
]
|
|
14123
14163
|
});
|
|
14124
|
-
await confirmFiles(
|
|
14164
|
+
await confirmFiles(fileParts);
|
|
14125
14165
|
await storage.insertMessage(correction);
|
|
14126
14166
|
return correction;
|
|
14127
14167
|
}, page);
|
|
@@ -14409,7 +14449,7 @@ function createChatService(input) {
|
|
|
14409
14449
|
await compaction.cancel(chatId);
|
|
14410
14450
|
await options2.attachments?.removeChatFiles?.(chatId);
|
|
14411
14451
|
await storage.deleteChat(chatId);
|
|
14412
|
-
}).finally(() => deleting.delete(chatId));
|
|
14452
|
+
}, { lease: "required" }).finally(() => deleting.delete(chatId));
|
|
14413
14453
|
await options2.background?.chatDeleted(userId, chatId).catch((error) => logError(`\u0424\u043E\u043D: \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0443\u0431\u0440\u0430\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0438 \u0443\u0434\u0430\u043B\u0451\u043D\u043D\u043E\u0433\u043E \u0447\u0430\u0442\u0430 ${chatId}: ${String(error)}`));
|
|
14414
14454
|
await options2.limits?.chatDeleted?.({ userId, chatId }).catch((error) => logError(`\u041B\u0438\u043C\u0438\u0442\u044B: \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0441\u0442\u0435\u0440\u0435\u0442\u044C \u043E\u043A\u043D\u0430 \u0443\u0434\u0430\u043B\u0451\u043D\u043D\u043E\u0433\u043E \u0447\u0430\u0442\u0430 ${chatId}: ${String(error)}`));
|
|
14415
14455
|
},
|
|
@@ -14591,11 +14631,13 @@ function createChatService(input) {
|
|
|
14591
14631
|
)
|
|
14592
14632
|
] : [],
|
|
14593
14633
|
// Правленое сообщение пачки остаётся в своей пачке: отметка с прежним временем записи.
|
|
14594
|
-
...edited2.parts.filter((part) => part.type === BURST_PART_TYPE)
|
|
14634
|
+
...edited2.parts.filter((part) => part.type === BURST_PART_TYPE),
|
|
14635
|
+
// Правленое сообщение группы остаётся в границах группы: отметка — из записанного сообщения, не из запроса.
|
|
14636
|
+
...[inheritedGroupPart(channelOfMessage(edited2))].filter((part) => part !== void 0)
|
|
14595
14637
|
],
|
|
14596
14638
|
createdAt: nextSiblingTime(messages, selections, edited2.parentId)
|
|
14597
14639
|
});
|
|
14598
|
-
await confirmFiles(
|
|
14640
|
+
await confirmFiles(fileParts);
|
|
14599
14641
|
await storage.insertMessage(question3);
|
|
14600
14642
|
return { branch: branch2, index: index2, edited: edited2, question: question3 };
|
|
14601
14643
|
});
|
|
@@ -15199,6 +15241,7 @@ export {
|
|
|
15199
15241
|
THOUGHT_TITLE_METADATA_KEY,
|
|
15200
15242
|
thoughtTitlesOfParts,
|
|
15201
15243
|
CHANNEL_PART_TYPE,
|
|
15244
|
+
isHumanMessage,
|
|
15202
15245
|
turnOriginOf,
|
|
15203
15246
|
GREETING_LEAD,
|
|
15204
15247
|
STEER_PART_TYPE,
|
|
@@ -15323,4 +15366,4 @@ export {
|
|
|
15323
15366
|
InvalidWidgetAnswerError,
|
|
15324
15367
|
createChatService
|
|
15325
15368
|
};
|
|
15326
|
-
//# sourceMappingURL=chunk-
|
|
15369
|
+
//# sourceMappingURL=chunk-YGHGURNF.js.map
|