@ykurilov/agent-chat-core 0.58.0 → 0.59.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 +10 -0
- package/dist/{chunk-KAG3QTZC.js → chunk-QA5DVEXF.js} +535 -33
- package/dist/chunk-QA5DVEXF.js.map +1 -0
- package/dist/index.js +9 -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 +6 -0
- package/dist/types/agent-run/forged-blocks.d.ts +117 -0
- package/dist/types/agent-run/run-agent-turn.d.ts +6 -0
- package/dist/types/attachments/message-attachments.d.ts +3 -0
- package/dist/types/chat-service.d.ts +7 -0
- package/dist/types/compaction/compaction-service.d.ts +3 -0
- package/dist/types/compaction/run-compaction.d.ts +3 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/memory/long-term-memory.d.ts +3 -0
- package/dist/types/text/markdown-code.d.ts +6 -0
- package/package.json +1 -1
- package/dist/chunk-KAG3QTZC.js.map +0 -1
|
@@ -1853,6 +1853,494 @@ function createBurstWaits() {
|
|
|
1853
1853
|
};
|
|
1854
1854
|
}
|
|
1855
1855
|
|
|
1856
|
+
// src/agent-run/forged-blocks.ts
|
|
1857
|
+
import { wrapLanguageModel as wrapLanguageModel2 } from "ai";
|
|
1858
|
+
|
|
1859
|
+
// src/text/markdown-code.ts
|
|
1860
|
+
function codeFenceOf(line) {
|
|
1861
|
+
return /^ {0,3}(`{3,}|~{3,})/.exec(line)?.[1] ?? null;
|
|
1862
|
+
}
|
|
1863
|
+
function closesCodeFence(line, fence) {
|
|
1864
|
+
const opener = codeFenceOf(line);
|
|
1865
|
+
return opener !== null && opener[0] === fence[0] && opener.length >= fence.length && line.trim() === opener;
|
|
1866
|
+
}
|
|
1867
|
+
function isIndentedCodeOrQuote(line) {
|
|
1868
|
+
return /^(?: {4}|\t)/.test(line) || /^\s*>/.test(line);
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
// src/agent-run/length-cut.ts
|
|
1872
|
+
var LENGTH_CUT_PART_TYPE = "data-length-cut";
|
|
1873
|
+
var LENGTH_CUT_NOTE = "[\u041E\u0442\u0432\u0435\u0442 \u043E\u0431\u043E\u0440\u0432\u0430\u043D: \u043C\u043E\u0434\u0435\u043B\u044C \u0443\u043F\u0451\u0440\u043B\u0430\u0441\u044C \u0432 \u043F\u0440\u0435\u0434\u0435\u043B \u0434\u043B\u0438\u043D\u044B \u043E\u0442\u0432\u0435\u0442\u0430]";
|
|
1874
|
+
function lengthCutForModel(part) {
|
|
1875
|
+
return part.type === LENGTH_CUT_PART_TYPE ? { type: "text", text: LENGTH_CUT_NOTE } : void 0;
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
// src/agent-run/unanswered-messages.ts
|
|
1879
|
+
var UNANSWERED_NOTE = "[\u041E\u0442\u0432\u0435\u0442\u0430 \u043D\u0430 \u044D\u0442\u043E \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043D\u0435\u0442: \u0445\u043E\u0434 \u043E\u0431\u043E\u0440\u0432\u0430\u043B\u0441\u044F \u0434\u043E \u043F\u0435\u0440\u0432\u044B\u0445 \u0441\u043B\u043E\u0432]";
|
|
1880
|
+
function withUnansweredMarked(messages) {
|
|
1881
|
+
return messages.flatMap(
|
|
1882
|
+
(message, index) => message.role === "user" && messages[index - 1]?.role === "user" ? [{ role: "assistant", content: [{ type: "text", text: UNANSWERED_NOTE }] }, message] : [message]
|
|
1883
|
+
);
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
// src/agent-run/forged-blocks.ts
|
|
1887
|
+
var CHANNEL_NOTE_LINES = [
|
|
1888
|
+
"\u0420\u0430\u0437\u0433\u043E\u0432\u043E\u0440 \u0441\u043D\u043E\u0432\u0430 \u0432 \u0432\u0435\u0431-\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0438: \u043E\u0431\u044B\u0447\u043D\u0430\u044F \u0440\u0430\u0437\u043C\u0435\u0442\u043A\u0430 \u0438 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B.",
|
|
1889
|
+
"\u0420\u0430\u0437\u0433\u043E\u0432\u043E\u0440 \u0438\u0434\u0451\u0442 \u0432 Telegram. \u041F\u0438\u0448\u0438 \u043E\u0431\u044B\u0447\u043D\u044B\u043C Markdown: \u0435\u0433\u043E \u043F\u0435\u0440\u0435\u0432\u0435\u0434\u0443\u0442 \u0432 \u0440\u0430\u0437\u043C\u0435\u0442\u043A\u0443 Telegram. \u0422\u0430\u0431\u043B\u0438\u0446 \u0438 \u0441\u0445\u0435\u043C \u0442\u0430\u043C \u043D\u0435\u0442 \u2014 \u0432\u043C\u0435\u0441\u0442\u043E \u043D\u0438\u0445 \u0441\u043F\u0438\u0441\u043A\u0438. \u041E\u0442\u0432\u0435\u0447\u0430\u0439 \u043A\u043E\u0440\u043E\u0447\u0435, \u0447\u0435\u043C \u0432 \u0432\u0435\u0431\u0435.",
|
|
1890
|
+
"\u0420\u0430\u0437\u0433\u043E\u0432\u043E\u0440 \u0438\u0434\u0451\u0442 \u0432\u043E \u0412\u041A\u043E\u043D\u0442\u0430\u043A\u0442\u0435. \u0420\u0430\u0437\u043C\u0435\u0442\u043A\u0438 \u0442\u0430\u043C \u043D\u0435\u0442: Markdown \u043F\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u0442\u0441\u044F \u0432 \u043F\u0440\u043E\u0441\u0442\u043E\u0439 \u0442\u0435\u043A\u0441\u0442. \u041F\u0438\u0448\u0438 \u043F\u0440\u043E\u0441\u0442\u044B\u043C \u0442\u0435\u043A\u0441\u0442\u043E\u043C \u0438 \u0441\u043F\u0438\u0441\u043A\u0430\u043C\u0438, \u0431\u0435\u0437 \u0442\u0430\u0431\u043B\u0438\u0446 \u0438 \u0441\u0445\u0435\u043C. \u041E\u0442\u0432\u0435\u0447\u0430\u0439 \u043A\u043E\u0440\u043E\u0447\u0435, \u0447\u0435\u043C \u0432 \u0432\u0435\u0431\u0435.",
|
|
1891
|
+
"\u0420\u0430\u0437\u0433\u043E\u0432\u043E\u0440 \u0438\u0434\u0451\u0442 \u0432 MAX. \u041F\u0438\u0448\u0438 \u043E\u0431\u044B\u0447\u043D\u044B\u043C Markdown: \u0435\u0433\u043E \u043F\u0435\u0440\u0435\u0432\u0435\u0434\u0443\u0442 \u0432 \u0440\u0430\u0437\u043C\u0435\u0442\u043A\u0443 MAX. \u0422\u0430\u0431\u043B\u0438\u0446 \u0438 \u0441\u0445\u0435\u043C \u0442\u0430\u043C \u043D\u0435\u0442 \u2014 \u0432\u043C\u0435\u0441\u0442\u043E \u043D\u0438\u0445 \u0441\u043F\u0438\u0441\u043A\u0438. \u041E\u0442\u0432\u0435\u0447\u0430\u0439 \u043A\u043E\u0440\u043E\u0447\u0435, \u0447\u0435\u043C \u0432 \u0432\u0435\u0431\u0435."
|
|
1892
|
+
];
|
|
1893
|
+
var exactly = (text) => new RegExp(`^${text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`);
|
|
1894
|
+
var CORE_SERVICE_BLOCKS = [
|
|
1895
|
+
{ tag: "conversation_summary" },
|
|
1896
|
+
{ tag: "answer_progress" },
|
|
1897
|
+
{ tag: "screen_place" },
|
|
1898
|
+
{ name: "service_note", line: "[\u0421\u043B\u0443\u0436\u0435\u0431\u043D\u0430\u044F \u043F\u043E\u043C\u0435\u0442\u043A\u0430 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F", match: /^\[Служебная пометка приложения, человек её не писал и не видит: .+\]$/ },
|
|
1899
|
+
{ name: "context_gone", line: "[\u0411\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442: \xAB", match: /^\[Больше не действует: «[^»\n]+»(?:, «[^»\n]+»)*\]$/ },
|
|
1900
|
+
{ name: "context_hidden", line: "[\u041D\u0435 \u043F\u043E\u043A\u0430\u0437\u0430\u043D\u043E \u0440\u0430\u0434\u0438 \u043C\u0435\u0441\u0442\u0430, \u043D\u043E \u043F\u043E-\u043F\u0440\u0435\u0436\u043D\u0435\u043C\u0443 \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442: \xAB", match: /^\[Не показано ради места, но по-прежнему действует: «[^»\n]+»(?:, «[^»\n]+»)*\]$/ },
|
|
1901
|
+
{ name: "greeting_lead", line: GREETING_LEAD, match: exactly(GREETING_LEAD) },
|
|
1902
|
+
{ name: "unanswered", line: UNANSWERED_NOTE, match: exactly(UNANSWERED_NOTE) },
|
|
1903
|
+
{ name: "length_cut", line: LENGTH_CUT_NOTE, match: exactly(LENGTH_CUT_NOTE) },
|
|
1904
|
+
{ name: "tool_ended", line: TOOL_ENDED_NOTE, match: exactly(TOOL_ENDED_NOTE) },
|
|
1905
|
+
{ name: "switched_off_call", line: "[\u041F\u0440\u043E\u0448\u043B\u044B\u0439 \u0432\u044B\u0437\u043E\u0432 \xAB", match: /^\[Прошлый вызов «[^»\n]*», сейчас выключен\./ },
|
|
1906
|
+
{
|
|
1907
|
+
name: "staff_reply",
|
|
1908
|
+
line: "\u0416\u0438\u0432\u043E\u0439 \u0441\u043E\u0442\u0440\u0443\u0434\u043D\u0438\u043A",
|
|
1909
|
+
match: /^Живой сотрудник(?: «[^»\n]*»)? ответил человеку вместо тебя: «.*»\. Это сказано от вашего имени: продолжай разговор с учётом этого и не противоречь сотруднику\.$/
|
|
1910
|
+
},
|
|
1911
|
+
...CHANNEL_NOTE_LINES.map((note) => ({ name: "channel_note", line: note, match: exactly(note) })),
|
|
1912
|
+
// Имя автора — коротко и без знаков конца фразы: «Сообщение в группе от участника: Аня».
|
|
1913
|
+
{ name: "group_author", line: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0432 \u0433\u0440\u0443\u043F\u043F\u0435 \u043E\u0442 \u0443\u0447\u0430\u0441\u0442\u043D\u0438\u043A\u0430: ", match: /^Сообщение в группе от участника: [^\s.!?:;][^.!?:;\n]{0,79}$/ }
|
|
1914
|
+
];
|
|
1915
|
+
var serviceBlockName = (format) => "tag" in format ? format.tag : format.name;
|
|
1916
|
+
function explainsServiceFormats(question2, formats) {
|
|
1917
|
+
const text = question2.toLowerCase();
|
|
1918
|
+
if (formats.some((format) => "tag" in format && text.includes(format.tag.toLowerCase()))) return true;
|
|
1919
|
+
return /(?:^|[^\p{L}])(?:тег(?:а|е|и|у|ом|ов|ами|ам|ах)?|формат(?:а|е|у|ом|ы|ов|ами|ам|ах)?|разметк[аеиу]|tags?|formats?|markup)(?:[^\p{L}]|$)/u.test(text);
|
|
1920
|
+
}
|
|
1921
|
+
var EXAMPLE_LEAD = /(?:^|[^\p{L}])(?:например|пример\p{L}*|образец|шаблон\p{L}*|выгляд\p{L}*|формат\p{L}*|такой|такая|такое|такие|так|example|e\.g\.|looks like|format)(?:[^\p{L}][^\n]*)?:\s*$/iu;
|
|
1922
|
+
var textOfMessage = (message) => message.parts.map((part) => part.type === "text" ? part.text : "").join("\n");
|
|
1923
|
+
function lastQuestionOf(history) {
|
|
1924
|
+
const question2 = history.findLast((message) => message.role === "user");
|
|
1925
|
+
return question2 ? textOfMessage(question2) : "";
|
|
1926
|
+
}
|
|
1927
|
+
function checkServiceBlockFormats(formats) {
|
|
1928
|
+
for (const format of formats) {
|
|
1929
|
+
const shown = JSON.stringify(format);
|
|
1930
|
+
if (!format || typeof format !== "object") throw new TypeError(`serviceBlocks: \u043D\u0443\u0436\u0435\u043D \u043E\u0431\u044A\u0435\u043A\u0442 \u0444\u043E\u0440\u043C\u0430\u0442\u0430, \u0430 \u043D\u0435 ${shown}`);
|
|
1931
|
+
if ("tag" in format) {
|
|
1932
|
+
if (typeof format.tag !== "string" || !/^[A-Za-z][\w:.-]*$/.test(format.tag)) throw new TypeError(`serviceBlocks: \u0438\u043C\u044F \u0442\u0435\u0433\u0430 \u2014 \u043B\u0430\u0442\u0438\u043D\u0438\u0446\u0430, \u0446\u0438\u0444\u0440\u044B, _ : . -, \u0430 \u043D\u0435 ${shown}`);
|
|
1933
|
+
continue;
|
|
1934
|
+
}
|
|
1935
|
+
if (typeof format.name !== "string" || format.name === "") throw new TypeError(`serviceBlocks: \u0443 \u0441\u043B\u0443\u0436\u0435\u0431\u043D\u043E\u0439 \u0441\u0442\u0440\u043E\u043A\u0438 \u043D\u0443\u0436\u043D\u043E \u0438\u043C\u044F \u0434\u043B\u044F \u0436\u0443\u0440\u043D\u0430\u043B\u0430: ${shown}`);
|
|
1936
|
+
if (typeof format.line !== "string" || format.line.trim() === "") throw new TypeError(`serviceBlocks: \u0443 \u0441\u043B\u0443\u0436\u0435\u0431\u043D\u043E\u0439 \u0441\u0442\u0440\u043E\u043A\u0438 \u043D\u0443\u0436\u043D\u043E \u043D\u0435\u043F\u0443\u0441\u0442\u043E\u0435 \u043D\u0430\u0447\u0430\u043B\u043E line: ${shown}`);
|
|
1937
|
+
if (format.match !== void 0 && !(format.match instanceof RegExp)) throw new TypeError(`serviceBlocks: match \u2014 \u0440\u0435\u0433\u0443\u043B\u044F\u0440\u043D\u043E\u0435 \u0432\u044B\u0440\u0430\u0436\u0435\u043D\u0438\u0435: ${format.name}`);
|
|
1938
|
+
if (format.until !== void 0 && format.until !== "paragraph" && !(format.until instanceof RegExp)) {
|
|
1939
|
+
throw new TypeError(`serviceBlocks: until \u2014 "paragraph" \u0438\u043B\u0438 \u0440\u0435\u0433\u0443\u043B\u044F\u0440\u043D\u043E\u0435 \u0432\u044B\u0440\u0430\u0436\u0435\u043D\u0438\u0435: ${format.name}`);
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
var compiledByList = /* @__PURE__ */ new WeakMap();
|
|
1944
|
+
function compile(formats) {
|
|
1945
|
+
const known = compiledByList.get(formats);
|
|
1946
|
+
if (known) return known;
|
|
1947
|
+
const tags = formats.flatMap((format) => "tag" in format ? [format.tag] : []);
|
|
1948
|
+
const lines = formats.filter((format) => !("tag" in format));
|
|
1949
|
+
const escape = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1950
|
+
const starts = [...tags.map((tag) => `<\\/?${escape(tag)}`), ...lines.map((format) => escape(format.line))];
|
|
1951
|
+
const hint = starts.length > 0 ? new RegExp(`(?:^|\\n) {0,3}(?:${starts.join("|")})`) : /(?!)/;
|
|
1952
|
+
const compiled = { tags, lines, hint };
|
|
1953
|
+
compiledByList.set(formats, compiled);
|
|
1954
|
+
return compiled;
|
|
1955
|
+
}
|
|
1956
|
+
var MAX_ATTRIBUTES_LENGTH = 200;
|
|
1957
|
+
function paragraphEnd(text) {
|
|
1958
|
+
const blank = /\n[ \t]*(?:\n|$)/g;
|
|
1959
|
+
blank.lastIndex = Math.max(0, text.indexOf("\n"));
|
|
1960
|
+
const found = text.indexOf("\n") < 0 ? null : blank.exec(text);
|
|
1961
|
+
return found ? found.index + 1 : -1;
|
|
1962
|
+
}
|
|
1963
|
+
function createForgedBlockScanner(formats, options2 = {}) {
|
|
1964
|
+
const compiled = compile(formats);
|
|
1965
|
+
const lines = compiled.lines;
|
|
1966
|
+
const tags = options2.explaining ? [] : compiled.tags;
|
|
1967
|
+
let fence = null;
|
|
1968
|
+
let previousLine = "";
|
|
1969
|
+
let pending = "";
|
|
1970
|
+
let ordinaryLine = null;
|
|
1971
|
+
let block = null;
|
|
1972
|
+
let skipSpace = false;
|
|
1973
|
+
let out = [];
|
|
1974
|
+
const emitText = (text) => {
|
|
1975
|
+
if (!text) return;
|
|
1976
|
+
const last = out.at(-1);
|
|
1977
|
+
if (last?.kind === "text") last.text += text;
|
|
1978
|
+
else out.push({ kind: "text", text });
|
|
1979
|
+
};
|
|
1980
|
+
const emitGap = (text) => {
|
|
1981
|
+
const last = out.at(-1);
|
|
1982
|
+
if (last?.kind === "gap") last.text += text;
|
|
1983
|
+
else out.push({ kind: "gap", text });
|
|
1984
|
+
};
|
|
1985
|
+
const closeOrdinaryLine = (line) => {
|
|
1986
|
+
if (line.trim() !== "") previousLine = line;
|
|
1987
|
+
if (fence !== null) {
|
|
1988
|
+
if (closesCodeFence(line, fence)) fence = null;
|
|
1989
|
+
} else {
|
|
1990
|
+
const opener = codeFenceOf(line);
|
|
1991
|
+
if (opener) fence = opener;
|
|
1992
|
+
}
|
|
1993
|
+
};
|
|
1994
|
+
function classify(line, complete) {
|
|
1995
|
+
if (fence !== null || isIndentedCodeOrQuote(line) || codeFenceOf(line)) return "text";
|
|
1996
|
+
const indent = /^ {0,3}/.exec(line)[0].length;
|
|
1997
|
+
const body = line.slice(indent);
|
|
1998
|
+
if (body === "") return complete ? "text" : "wait";
|
|
1999
|
+
let waiting = false;
|
|
2000
|
+
if (body[0] === "<") {
|
|
2001
|
+
const closing = body[1] === "/";
|
|
2002
|
+
const nameAt = closing ? 2 : 1;
|
|
2003
|
+
for (const tag of tags) {
|
|
2004
|
+
const head = body.slice(nameAt, nameAt + tag.length);
|
|
2005
|
+
if (!tag.startsWith(head)) continue;
|
|
2006
|
+
if (head.length < tag.length) {
|
|
2007
|
+
waiting ||= body.length === nameAt + head.length;
|
|
2008
|
+
continue;
|
|
2009
|
+
}
|
|
2010
|
+
if (closing) continue;
|
|
2011
|
+
const next = body[nameAt + tag.length];
|
|
2012
|
+
if (next === void 0) {
|
|
2013
|
+
waiting = true;
|
|
2014
|
+
continue;
|
|
2015
|
+
}
|
|
2016
|
+
if (next !== ">" && !/\s/.test(next)) continue;
|
|
2017
|
+
const end = body.indexOf(">", nameAt + tag.length);
|
|
2018
|
+
if (end >= 0 && end - (nameAt + tag.length) <= MAX_ATTRIBUTES_LENGTH && !body.slice(nameAt + tag.length, end).includes("\n")) {
|
|
2019
|
+
if (EXAMPLE_LEAD.test(previousLine.trimEnd())) continue;
|
|
2020
|
+
const close = `</${tag}>`;
|
|
2021
|
+
const rest = body.slice(end + 1);
|
|
2022
|
+
if (rest.includes(close)) return { kind: "tag", name: tag, close, from: indent + end + 1 };
|
|
2023
|
+
if (!complete) {
|
|
2024
|
+
waiting = true;
|
|
2025
|
+
continue;
|
|
2026
|
+
}
|
|
2027
|
+
if (rest.trim() === "" || /^\s*[[{"]/.test(rest)) return { kind: "tag", name: tag, close, from: indent + end + 1 };
|
|
2028
|
+
continue;
|
|
2029
|
+
}
|
|
2030
|
+
if (end < 0 && !complete && body.length - (nameAt + tag.length) <= MAX_ATTRIBUTES_LENGTH) waiting = true;
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
for (const format of lines) {
|
|
2034
|
+
if (body.length < format.line.length) {
|
|
2035
|
+
waiting ||= format.line.startsWith(body);
|
|
2036
|
+
continue;
|
|
2037
|
+
}
|
|
2038
|
+
if (!body.startsWith(format.line)) continue;
|
|
2039
|
+
if (!complete) {
|
|
2040
|
+
waiting = true;
|
|
2041
|
+
continue;
|
|
2042
|
+
}
|
|
2043
|
+
if (!format.match || format.match.test(body.trimEnd())) return { kind: "line", format };
|
|
2044
|
+
}
|
|
2045
|
+
return waiting && !complete ? "wait" : "text";
|
|
2046
|
+
}
|
|
2047
|
+
function openBlock(opener, line, newline) {
|
|
2048
|
+
const whole = line + (newline ? "\n" : "");
|
|
2049
|
+
if (opener.kind === "tag") {
|
|
2050
|
+
block = { kind: "tag", name: opener.name, close: opener.close, text: whole, searchFrom: opener.from };
|
|
2051
|
+
return;
|
|
2052
|
+
}
|
|
2053
|
+
const { format } = opener;
|
|
2054
|
+
if (!format.until) {
|
|
2055
|
+
out.push({ kind: "block", name: format.name, text: whole });
|
|
2056
|
+
skipSpace = true;
|
|
2057
|
+
return;
|
|
2058
|
+
}
|
|
2059
|
+
block = format.until === "paragraph" ? { kind: "paragraph", name: format.name, text: whole } : { kind: "until", name: format.name, until: format.until, text: whole };
|
|
2060
|
+
}
|
|
2061
|
+
function feedBlock(current, delta) {
|
|
2062
|
+
current.text += delta;
|
|
2063
|
+
if (current.kind === "tag") {
|
|
2064
|
+
const at = current.text.indexOf(current.close, current.searchFrom);
|
|
2065
|
+
if (at < 0) {
|
|
2066
|
+
current.searchFrom = Math.max(current.searchFrom, current.text.length - current.close.length + 1);
|
|
2067
|
+
return null;
|
|
2068
|
+
}
|
|
2069
|
+
const end = at + current.close.length;
|
|
2070
|
+
return finishBlock(current, end);
|
|
2071
|
+
}
|
|
2072
|
+
let lineStart = current.text.indexOf("\n") + 1;
|
|
2073
|
+
while (lineStart > 0) {
|
|
2074
|
+
const lineEnd = current.text.indexOf("\n", lineStart);
|
|
2075
|
+
if (lineEnd < 0) return null;
|
|
2076
|
+
const line = current.text.slice(lineStart, lineEnd);
|
|
2077
|
+
if (current.kind === "paragraph" && line.trim() === "") return finishBlock(current, lineStart);
|
|
2078
|
+
if (current.kind === "until" && current.until.test(line.trim())) return finishBlock(current, lineEnd + 1);
|
|
2079
|
+
lineStart = lineEnd + 1;
|
|
2080
|
+
}
|
|
2081
|
+
return null;
|
|
2082
|
+
}
|
|
2083
|
+
function finishBlock(current, end) {
|
|
2084
|
+
out.push({ kind: "block", name: current.name, text: current.text.slice(0, end) });
|
|
2085
|
+
block = null;
|
|
2086
|
+
skipSpace = true;
|
|
2087
|
+
previousLine = "";
|
|
2088
|
+
return current.text.slice(end);
|
|
2089
|
+
}
|
|
2090
|
+
function feed(delta) {
|
|
2091
|
+
let rest = delta;
|
|
2092
|
+
while (rest) {
|
|
2093
|
+
if (block) {
|
|
2094
|
+
const left = feedBlock(block, rest);
|
|
2095
|
+
if (left === null) return;
|
|
2096
|
+
rest = left;
|
|
2097
|
+
continue;
|
|
2098
|
+
}
|
|
2099
|
+
if (skipSpace) {
|
|
2100
|
+
const space = /^\s*/.exec(rest)[0];
|
|
2101
|
+
if (space) emitGap(space);
|
|
2102
|
+
rest = rest.slice(space.length);
|
|
2103
|
+
if (!rest) return;
|
|
2104
|
+
skipSpace = false;
|
|
2105
|
+
pending = "";
|
|
2106
|
+
ordinaryLine = null;
|
|
2107
|
+
continue;
|
|
2108
|
+
}
|
|
2109
|
+
if (ordinaryLine !== null) {
|
|
2110
|
+
const newline2 = rest.indexOf("\n");
|
|
2111
|
+
if (newline2 < 0) {
|
|
2112
|
+
ordinaryLine += rest;
|
|
2113
|
+
emitText(rest);
|
|
2114
|
+
return;
|
|
2115
|
+
}
|
|
2116
|
+
closeOrdinaryLine(ordinaryLine + rest.slice(0, newline2));
|
|
2117
|
+
emitText(rest.slice(0, newline2 + 1));
|
|
2118
|
+
ordinaryLine = null;
|
|
2119
|
+
rest = rest.slice(newline2 + 1);
|
|
2120
|
+
continue;
|
|
2121
|
+
}
|
|
2122
|
+
const newline = rest.indexOf("\n");
|
|
2123
|
+
const line = pending + (newline < 0 ? rest : rest.slice(0, newline));
|
|
2124
|
+
const verdict = classify(line, newline >= 0);
|
|
2125
|
+
if (verdict === "wait") {
|
|
2126
|
+
pending = line;
|
|
2127
|
+
return;
|
|
2128
|
+
}
|
|
2129
|
+
pending = "";
|
|
2130
|
+
const after = newline < 0 ? "" : rest.slice(newline + 1);
|
|
2131
|
+
if (verdict === "text") {
|
|
2132
|
+
if (newline < 0) {
|
|
2133
|
+
ordinaryLine = line;
|
|
2134
|
+
emitText(line);
|
|
2135
|
+
return;
|
|
2136
|
+
}
|
|
2137
|
+
closeOrdinaryLine(line);
|
|
2138
|
+
emitText(`${line}
|
|
2139
|
+
`);
|
|
2140
|
+
rest = after;
|
|
2141
|
+
continue;
|
|
2142
|
+
}
|
|
2143
|
+
openBlock(verdict, line, newline >= 0);
|
|
2144
|
+
rest = after;
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
const take = () => {
|
|
2148
|
+
const pieces = out;
|
|
2149
|
+
out = [];
|
|
2150
|
+
return pieces;
|
|
2151
|
+
};
|
|
2152
|
+
return {
|
|
2153
|
+
/** Следующий кусок текста → готовые куски. Начало возможного блока придерживается до следующего. */
|
|
2154
|
+
push(delta) {
|
|
2155
|
+
feed(delta);
|
|
2156
|
+
return take();
|
|
2157
|
+
},
|
|
2158
|
+
/**
|
|
2159
|
+
* Текст кончился: придержанное решается на неполной строке. Незакрытый тег — блок до первой пустой строки, остальное
|
|
2160
|
+
* разбирается дальше; строчный блок — до конца текста.
|
|
2161
|
+
*/
|
|
2162
|
+
end() {
|
|
2163
|
+
for (; ; ) {
|
|
2164
|
+
const current = block;
|
|
2165
|
+
if (current) {
|
|
2166
|
+
block = null;
|
|
2167
|
+
const blank = current.kind === "tag" ? paragraphEnd(current.text) : -1;
|
|
2168
|
+
if (blank < 0) {
|
|
2169
|
+
out.push({ kind: "block", name: current.name, text: current.text });
|
|
2170
|
+
continue;
|
|
2171
|
+
}
|
|
2172
|
+
finishBlock(current, blank);
|
|
2173
|
+
feed(current.text.slice(blank));
|
|
2174
|
+
continue;
|
|
2175
|
+
}
|
|
2176
|
+
if (!pending) break;
|
|
2177
|
+
const line = pending;
|
|
2178
|
+
pending = "";
|
|
2179
|
+
const verdict = classify(line, true);
|
|
2180
|
+
if (verdict === "text" || verdict === "wait") emitText(line);
|
|
2181
|
+
else openBlock(verdict, line, false);
|
|
2182
|
+
}
|
|
2183
|
+
return take();
|
|
2184
|
+
}
|
|
2185
|
+
};
|
|
2186
|
+
}
|
|
2187
|
+
function cutForgedBlocks(text, formats, options2 = {}) {
|
|
2188
|
+
if (!compile(formats).hint.test(text)) return { text, blocks: [] };
|
|
2189
|
+
const scanner = createForgedBlockScanner(formats, options2);
|
|
2190
|
+
const pieces = [...scanner.push(text), ...scanner.end()];
|
|
2191
|
+
return {
|
|
2192
|
+
text: pieces.map((piece) => piece.kind === "text" ? piece.text : "").join(""),
|
|
2193
|
+
blocks: pieces.flatMap((piece) => piece.kind === "block" ? [piece.name] : [])
|
|
2194
|
+
};
|
|
2195
|
+
}
|
|
2196
|
+
var hasWords = (text) => /[\p{L}\p{N}]/u.test(text);
|
|
2197
|
+
function cutForgedBlocksInParts(parts, formats, options2 = {}) {
|
|
2198
|
+
const blocks = [];
|
|
2199
|
+
const cleaned = parts.flatMap((part) => {
|
|
2200
|
+
if (part.type !== "text") return [part];
|
|
2201
|
+
const cut = cutForgedBlocks(part.text, formats, options2);
|
|
2202
|
+
if (cut.blocks.length === 0) return [part];
|
|
2203
|
+
blocks.push(...cut.blocks);
|
|
2204
|
+
return cut.text.trim() === "" ? [] : [{ ...part, text: cut.text }];
|
|
2205
|
+
});
|
|
2206
|
+
if (blocks.length === 0) return null;
|
|
2207
|
+
if (!cleaned.some((part) => part.type === "text" && hasWords(part.text))) return null;
|
|
2208
|
+
return { parts: cleaned, blocks };
|
|
2209
|
+
}
|
|
2210
|
+
function withoutForgedBlocks(history, formats) {
|
|
2211
|
+
let changed = false;
|
|
2212
|
+
let question2 = "";
|
|
2213
|
+
const cleaned = history.map((message) => {
|
|
2214
|
+
if (message.role === "user") question2 = textOfMessage(message);
|
|
2215
|
+
if (message.role !== "assistant") return message;
|
|
2216
|
+
const cut = cutForgedBlocksInParts(message.parts, formats, { explaining: explainsServiceFormats(question2, formats) });
|
|
2217
|
+
if (!cut) return message;
|
|
2218
|
+
changed = true;
|
|
2219
|
+
return { ...message, parts: cut.parts };
|
|
2220
|
+
});
|
|
2221
|
+
return changed ? cleaned : history;
|
|
2222
|
+
}
|
|
2223
|
+
function createForgedBlockGuard(formats) {
|
|
2224
|
+
let worded = false;
|
|
2225
|
+
let explaining = false;
|
|
2226
|
+
const cut = [];
|
|
2227
|
+
const kept = [];
|
|
2228
|
+
const transformFor = () => {
|
|
2229
|
+
const scanners = /* @__PURE__ */ new Map();
|
|
2230
|
+
let held = null;
|
|
2231
|
+
const starts = /* @__PURE__ */ new Map();
|
|
2232
|
+
const started = /* @__PURE__ */ new Set();
|
|
2233
|
+
const enqueue = (part, controller) => {
|
|
2234
|
+
if (part.type === "text-start" && scanners.has(part.id)) {
|
|
2235
|
+
starts.set(part.id, part);
|
|
2236
|
+
return;
|
|
2237
|
+
}
|
|
2238
|
+
if (part.type === "text-delta" && !started.has(part.id) && starts.has(part.id)) {
|
|
2239
|
+
controller.enqueue(starts.get(part.id));
|
|
2240
|
+
started.add(part.id);
|
|
2241
|
+
}
|
|
2242
|
+
if (part.type === "text-end" && starts.has(part.id) && !started.has(part.id)) return;
|
|
2243
|
+
controller.enqueue(part);
|
|
2244
|
+
};
|
|
2245
|
+
const isBlock = (item) => "name" in item && "text" in item && !("type" in item);
|
|
2246
|
+
const settle = (keep, controller) => {
|
|
2247
|
+
const items = held ?? [];
|
|
2248
|
+
held = null;
|
|
2249
|
+
for (const item of items) {
|
|
2250
|
+
if (!isBlock(item)) enqueue(item, controller);
|
|
2251
|
+
else {
|
|
2252
|
+
if (keep) enqueue({ type: "text-delta", id: item.id, delta: item.text }, controller);
|
|
2253
|
+
if (item.name) (keep ? kept : cut).push(item.name);
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
};
|
|
2257
|
+
const send = (part, controller) => {
|
|
2258
|
+
if (held) held.push(part);
|
|
2259
|
+
else enqueue(part, controller);
|
|
2260
|
+
};
|
|
2261
|
+
const deltaOf = (id, pieces, controller) => {
|
|
2262
|
+
let delta = "";
|
|
2263
|
+
for (const piece of pieces) {
|
|
2264
|
+
if (piece.kind === "text" && hasWords(piece.text)) {
|
|
2265
|
+
worded = true;
|
|
2266
|
+
if (held) settle(false, controller);
|
|
2267
|
+
}
|
|
2268
|
+
if (piece.kind === "text" && !held) delta += piece.text;
|
|
2269
|
+
else if (worded) {
|
|
2270
|
+
if (piece.kind === "block") cut.push(piece.name);
|
|
2271
|
+
} else {
|
|
2272
|
+
(held ??= []).push({ id, name: piece.kind === "block" ? piece.name : "", text: piece.text });
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
return delta;
|
|
2276
|
+
};
|
|
2277
|
+
const transform = (part, controller) => {
|
|
2278
|
+
switch (part.type) {
|
|
2279
|
+
case "text-start":
|
|
2280
|
+
scanners.set(part.id, createForgedBlockScanner(formats, { explaining }));
|
|
2281
|
+
break;
|
|
2282
|
+
case "text-delta": {
|
|
2283
|
+
const scanner = scanners.get(part.id);
|
|
2284
|
+
if (!scanner) break;
|
|
2285
|
+
const delta = deltaOf(part.id, scanner.push(part.delta), controller);
|
|
2286
|
+
if (delta) send({ ...part, delta }, controller);
|
|
2287
|
+
return;
|
|
2288
|
+
}
|
|
2289
|
+
case "text-end": {
|
|
2290
|
+
const scanner = scanners.get(part.id);
|
|
2291
|
+
if (!scanner) break;
|
|
2292
|
+
scanners.delete(part.id);
|
|
2293
|
+
const delta = deltaOf(part.id, scanner.end(), controller);
|
|
2294
|
+
if (delta) send({ type: "text-delta", id: part.id, delta }, controller);
|
|
2295
|
+
break;
|
|
2296
|
+
}
|
|
2297
|
+
case "tool-input-start":
|
|
2298
|
+
case "tool-call":
|
|
2299
|
+
if (held) settle(false, controller);
|
|
2300
|
+
break;
|
|
2301
|
+
case "finish":
|
|
2302
|
+
if (held) settle(part.finishReason.unified !== "tool-calls", controller);
|
|
2303
|
+
break;
|
|
2304
|
+
}
|
|
2305
|
+
send(part, controller);
|
|
2306
|
+
};
|
|
2307
|
+
const flush = (controller) => {
|
|
2308
|
+
if (held) settle(true, controller);
|
|
2309
|
+
};
|
|
2310
|
+
return new TransformStream({ transform, flush });
|
|
2311
|
+
};
|
|
2312
|
+
return {
|
|
2313
|
+
/**
|
|
2314
|
+
* Новая попытка с последним вопросом человека: шаги прежней стёрты — слов у ответа снова нет. Вырезанное раньше
|
|
2315
|
+
* остаётся в счёте для журнала.
|
|
2316
|
+
*/
|
|
2317
|
+
reset(question2 = "") {
|
|
2318
|
+
worded = false;
|
|
2319
|
+
explaining = explainsServiceFormats(question2, formats);
|
|
2320
|
+
},
|
|
2321
|
+
/** Законченный текст мимо потока (переписка проверкой) — по тем же правилам, что поток этой попытки. */
|
|
2322
|
+
cutText: (text) => cutForgedBlocks(text, formats, { explaining }),
|
|
2323
|
+
/** Модель попытки под присмотром: каждый запрос — новый шаг. */
|
|
2324
|
+
wrap(model) {
|
|
2325
|
+
if (typeof model === "string" || formats.length === 0) return model;
|
|
2326
|
+
return wrapLanguageModel2({
|
|
2327
|
+
model,
|
|
2328
|
+
middleware: {
|
|
2329
|
+
specificationVersion: "v4",
|
|
2330
|
+
wrapStream: async ({ doStream }) => {
|
|
2331
|
+
const result = await doStream();
|
|
2332
|
+
return { ...result, stream: result.stream.pipeThrough(transformFor()) };
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
});
|
|
2336
|
+
},
|
|
2337
|
+
/** Вырезанные блоки хода по порядку (имена) — для журнала. */
|
|
2338
|
+
cutBlocks: () => cut,
|
|
2339
|
+
/** Блоки, оставленные ради непустого ответа. */
|
|
2340
|
+
keptBlocks: () => kept
|
|
2341
|
+
};
|
|
2342
|
+
}
|
|
2343
|
+
|
|
1856
2344
|
// src/storage/chat-storage.ts
|
|
1857
2345
|
var RELEASED_ANSWER_ALIVE_AT = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
1858
2346
|
function aliveAtByLocalClock(mark) {
|
|
@@ -2918,7 +3406,7 @@ function usageMinusJournaled(usage, journaled) {
|
|
|
2918
3406
|
}
|
|
2919
3407
|
|
|
2920
3408
|
// src/agent-run/build-model-request.ts
|
|
2921
|
-
import { convertToModelMessages, wrapLanguageModel as
|
|
3409
|
+
import { convertToModelMessages, wrapLanguageModel as wrapLanguageModel5 } from "ai";
|
|
2922
3410
|
|
|
2923
3411
|
// src/conversation/hanging-tool-calls.ts
|
|
2924
3412
|
var PRELIMINARY_OUTPUT_MAX_LENGTH = 2e3;
|
|
@@ -3138,7 +3626,7 @@ ${chunk.delta}` } : chunk];
|
|
|
3138
3626
|
}
|
|
3139
3627
|
|
|
3140
3628
|
// src/agent-run/observe-model-calls.ts
|
|
3141
|
-
import { wrapLanguageModel as
|
|
3629
|
+
import { wrapLanguageModel as wrapLanguageModel3 } from "ai";
|
|
3142
3630
|
function observeModelCalls(model, context) {
|
|
3143
3631
|
if (typeof model === "string") return model;
|
|
3144
3632
|
const now = context.now ?? Date.now;
|
|
@@ -3148,7 +3636,7 @@ function observeModelCalls(model, context) {
|
|
|
3148
3636
|
attempts.set(params.prompt, attempt);
|
|
3149
3637
|
return attempt;
|
|
3150
3638
|
};
|
|
3151
|
-
return
|
|
3639
|
+
return wrapLanguageModel3({
|
|
3152
3640
|
model,
|
|
3153
3641
|
middleware: {
|
|
3154
3642
|
specificationVersion: "v4",
|
|
@@ -3579,21 +4067,6 @@ function estimateMessagesTokens(messages) {
|
|
|
3579
4067
|
}, 0);
|
|
3580
4068
|
}
|
|
3581
4069
|
|
|
3582
|
-
// src/agent-run/length-cut.ts
|
|
3583
|
-
var LENGTH_CUT_PART_TYPE = "data-length-cut";
|
|
3584
|
-
var LENGTH_CUT_NOTE = "[\u041E\u0442\u0432\u0435\u0442 \u043E\u0431\u043E\u0440\u0432\u0430\u043D: \u043C\u043E\u0434\u0435\u043B\u044C \u0443\u043F\u0451\u0440\u043B\u0430\u0441\u044C \u0432 \u043F\u0440\u0435\u0434\u0435\u043B \u0434\u043B\u0438\u043D\u044B \u043E\u0442\u0432\u0435\u0442\u0430]";
|
|
3585
|
-
function lengthCutForModel(part) {
|
|
3586
|
-
return part.type === LENGTH_CUT_PART_TYPE ? { type: "text", text: LENGTH_CUT_NOTE } : void 0;
|
|
3587
|
-
}
|
|
3588
|
-
|
|
3589
|
-
// src/agent-run/unanswered-messages.ts
|
|
3590
|
-
var UNANSWERED_NOTE = "[\u041E\u0442\u0432\u0435\u0442\u0430 \u043D\u0430 \u044D\u0442\u043E \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043D\u0435\u0442: \u0445\u043E\u0434 \u043E\u0431\u043E\u0440\u0432\u0430\u043B\u0441\u044F \u0434\u043E \u043F\u0435\u0440\u0432\u044B\u0445 \u0441\u043B\u043E\u0432]";
|
|
3591
|
-
function withUnansweredMarked(messages) {
|
|
3592
|
-
return messages.flatMap(
|
|
3593
|
-
(message, index) => message.role === "user" && messages[index - 1]?.role === "user" ? [{ role: "assistant", content: [{ type: "text", text: UNANSWERED_NOTE }] }, message] : [message]
|
|
3594
|
-
);
|
|
3595
|
-
}
|
|
3596
|
-
|
|
3597
4070
|
// src/agent-run/staff-replies.ts
|
|
3598
4071
|
function isStaffReply(message) {
|
|
3599
4072
|
if (message.role !== "user") return false;
|
|
@@ -3751,7 +4224,7 @@ function withoutUndelivered(history) {
|
|
|
3751
4224
|
}
|
|
3752
4225
|
|
|
3753
4226
|
// src/agent-run/garbled-tail.ts
|
|
3754
|
-
import { wrapLanguageModel as
|
|
4227
|
+
import { wrapLanguageModel as wrapLanguageModel4 } from "ai";
|
|
3755
4228
|
var SIGN_ORDER = ["think", "role", "restart", "tool"];
|
|
3756
4229
|
var TOKEN_SOURCE = String.raw`<\/?think>|<\|[^|\n<>]{1,40}\|>|<|[^|\n<>]{1,40}|>`;
|
|
3757
4230
|
var TEMPLATE_TOKEN = new RegExp(TOKEN_SOURCE, "g");
|
|
@@ -3923,7 +4396,7 @@ function createGarbledTailGuard() {
|
|
|
3923
4396
|
/** Модель попытки под присмотром: каждый запрос — новый шаг. */
|
|
3924
4397
|
wrap(model) {
|
|
3925
4398
|
if (typeof model === "string") return model;
|
|
3926
|
-
return
|
|
4399
|
+
return wrapLanguageModel4({
|
|
3927
4400
|
model,
|
|
3928
4401
|
middleware: {
|
|
3929
4402
|
specificationVersion: "v4",
|
|
@@ -4028,16 +4501,16 @@ function visibleBefore(earlier, visible, offset) {
|
|
|
4028
4501
|
function withoutCodeAndQuotes(text) {
|
|
4029
4502
|
let fence = null;
|
|
4030
4503
|
return text.split("\n").map((line) => {
|
|
4031
|
-
const opener = /^ {0,3}(`{3,}|~{3,})/.exec(line)?.[1];
|
|
4032
4504
|
if (fence !== null) {
|
|
4033
|
-
if (
|
|
4505
|
+
if (closesCodeFence(line, fence)) fence = null;
|
|
4034
4506
|
return " ".repeat(line.length);
|
|
4035
4507
|
}
|
|
4508
|
+
const opener = codeFenceOf(line);
|
|
4036
4509
|
if (opener) {
|
|
4037
4510
|
fence = opener;
|
|
4038
4511
|
return " ".repeat(line.length);
|
|
4039
4512
|
}
|
|
4040
|
-
if (
|
|
4513
|
+
if (isIndentedCodeOrQuote(line)) return " ".repeat(line.length);
|
|
4041
4514
|
return line.replace(/(`+)[^`\n]*?\1/g, (code) => " ".repeat(code.length));
|
|
4042
4515
|
}).join("\n");
|
|
4043
4516
|
}
|
|
@@ -4065,7 +4538,8 @@ async function buildModelRequest(input) {
|
|
|
4065
4538
|
...input.repeatSilence && { repeatSilence: input.repeatSilence }
|
|
4066
4539
|
});
|
|
4067
4540
|
const tools = input.tools ? sortToolsByName(input.tools) : void 0;
|
|
4068
|
-
const
|
|
4541
|
+
const cleaned = sendableHistory(input.serviceBlocks ? withoutForgedBlocks(input.history, input.serviceBlocks) : input.history);
|
|
4542
|
+
const sendable = input.switchableTools ? withSwitchedOffCallsAsText(cleaned, input.switchableTools, tools) : cleaned;
|
|
4069
4543
|
const noted = withStaffRepliesJoined(withChannelNotes(withModeNotes(withBurstsJoined(withCorrectionsJoined(sendable)), input.modeNotes)));
|
|
4070
4544
|
const held = input.frozenContext ? holdNoteAfterApprovals(withFrozenContext(noted, input.frozenContext)) : { messages: noted };
|
|
4071
4545
|
const history = selectReasoningForModel(held.messages, {
|
|
@@ -4150,7 +4624,7 @@ function withoutLoneSurrogatesDeep(value) {
|
|
|
4150
4624
|
}
|
|
4151
4625
|
function withRequestWatch(model, provider, watch) {
|
|
4152
4626
|
if (!watch || typeof model === "string") return model;
|
|
4153
|
-
return
|
|
4627
|
+
return wrapLanguageModel5({
|
|
4154
4628
|
model,
|
|
4155
4629
|
middleware: {
|
|
4156
4630
|
specificationVersion: "v4",
|
|
@@ -4170,7 +4644,7 @@ function withRequestWatch(model, provider, watch) {
|
|
|
4170
4644
|
});
|
|
4171
4645
|
}
|
|
4172
4646
|
function withTransientRetries(model, context) {
|
|
4173
|
-
return typeof model === "string" ? model :
|
|
4647
|
+
return typeof model === "string" ? model : wrapLanguageModel5({ model, middleware: createTransientRetryMiddleware(context) });
|
|
4174
4648
|
}
|
|
4175
4649
|
function withLastMessageMark(messages, options2) {
|
|
4176
4650
|
if (!options2 || messages.length === 0) return messages;
|
|
@@ -5970,6 +6444,7 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
5970
6444
|
);
|
|
5971
6445
|
const turnUsage = createTurnUsage();
|
|
5972
6446
|
const garbledTail = createGarbledTailGuard();
|
|
6447
|
+
const forgedBlocks = createForgedBlockGuard(turnInput.serviceBlocks ?? CORE_SERVICE_BLOCKS);
|
|
5973
6448
|
const turnBudget = createTurnBudget(input.turnRules, { catalog: input.catalog, modelId: input.modelId, turnUsage });
|
|
5974
6449
|
const messageUsage = () => {
|
|
5975
6450
|
const spent = turnUsage.total();
|
|
@@ -6146,6 +6621,7 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
6146
6621
|
turnUsage,
|
|
6147
6622
|
turnBudget,
|
|
6148
6623
|
garbledTail,
|
|
6624
|
+
forgedBlocks,
|
|
6149
6625
|
// Есть куда перейти при молчании — у того же провайдера оно не повторяется.
|
|
6150
6626
|
repeatSilence: () => silenceTarget() === void 0,
|
|
6151
6627
|
...silenceSwitch.scope === "step" && switchedBySilence && {
|
|
@@ -6365,6 +6841,8 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
6365
6841
|
const garbled = cutGarbledTail(garbledTail.forCut(parts));
|
|
6366
6842
|
const droppedCalls = garbledTail.droppedCalls();
|
|
6367
6843
|
const garbleLog = `\u0447\u0430\u0442=${input.chatId} \u043E\u0442\u0432\u0435\u0442=${messageId} \u043C\u043E\u0434\u0435\u043B\u044C=${modelId} \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440=${answeredProviderId}`;
|
|
6844
|
+
for (const name of new Set(forgedBlocks.cutBlocks())) logError(`[\u0441\u043B\u0443\u0436\u0435\u0431\u043D\u044B\u0439 \u0431\u043B\u043E\u043A \u0432\u044B\u0440\u0435\u0437\u0430\u043D] \u0442\u0435\u0433=${name} ${garbleLog}`);
|
|
6845
|
+
for (const name of new Set(forgedBlocks.keptBlocks())) logError(`[\u0441\u043B\u0443\u0436\u0435\u0431\u043D\u044B\u0439 \u0431\u043B\u043E\u043A \u043D\u0435 \u0432\u044B\u0440\u0435\u0437\u0430\u043D: \u043E\u0442\u0432\u0435\u0442 \u043E\u0441\u0442\u0430\u043B\u0441\u044F \u0431\u044B \u043F\u0443\u0441\u0442\u044B\u043C] \u0442\u0435\u0433=${name} ${garbleLog}`);
|
|
6368
6846
|
if (garbled) {
|
|
6369
6847
|
logError(`[\u0445\u0432\u043E\u0441\u0442 \u043E\u0431\u0440\u0435\u0437\u0430\u043D] ${garbleLog} \u043F\u0440\u0438\u0437\u043D\u0430\u043A\u0438=${garbled.signs.join(",")} \u0437\u043D\u0430\u043A\u043E\u0432=${garbled.removedChars} \u0432\u044B\u0437\u043E\u0432\u043E\u0432_\u043D\u0435_\u0437\u0430\u043F\u0443\u0449\u0435\u043D\u043E=${droppedCalls}`);
|
|
6370
6848
|
const cut = garbled.parts.filter((part) => !garbledTail.isDroppedCall(part));
|
|
@@ -6376,7 +6854,7 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
6376
6854
|
const textIndex = lastTextIndex(parts);
|
|
6377
6855
|
if (replyChecks && status === "complete" && textIndex !== -1 && !takeover.signal.aborted) {
|
|
6378
6856
|
const original = parts[textIndex].text;
|
|
6379
|
-
const
|
|
6857
|
+
const checked2 = await checkedReplyText({
|
|
6380
6858
|
checks: replyChecks.checks,
|
|
6381
6859
|
text: original,
|
|
6382
6860
|
ownerId: input.ownerId ?? "",
|
|
@@ -6386,6 +6864,7 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
6386
6864
|
rewrite: createReplyRewrite({ ...input, providerId: answeredProviderId, messageId, logError }),
|
|
6387
6865
|
logError
|
|
6388
6866
|
});
|
|
6867
|
+
const text = checked2 === original ? checked2 : withoutForgedBlocksInRewrite(forgedBlocks.cutText(checked2), checked2, (name) => logError(`[\u0441\u043B\u0443\u0436\u0435\u0431\u043D\u044B\u0439 \u0431\u043B\u043E\u043A \u0432\u044B\u0440\u0435\u0437\u0430\u043D] \u0442\u0435\u0433=${name} ${garbleLog}`));
|
|
6389
6868
|
if (text !== original) {
|
|
6390
6869
|
const corrected = withTextAt(parts, textIndex, text);
|
|
6391
6870
|
if (heldText?.holdsText()) for (const chunk of heldText.release(text)) emitShown(chunk);
|
|
@@ -6643,6 +7122,7 @@ async function openAttempt(input, attempt, attemptSignal) {
|
|
|
6643
7122
|
} = input;
|
|
6644
7123
|
const request = await buildModelRequest({
|
|
6645
7124
|
...requestInput,
|
|
7125
|
+
serviceBlocks: input.serviceBlocks ?? CORE_SERVICE_BLOCKS,
|
|
6646
7126
|
providerId,
|
|
6647
7127
|
history: attempt.history,
|
|
6648
7128
|
frozenContext: { current: { answerId: input.messageId, data: attempt.frozenContext }, group: Boolean(input.group) },
|
|
@@ -6685,9 +7165,11 @@ async function openAttempt(input, attempt, attemptSignal) {
|
|
|
6685
7165
|
};
|
|
6686
7166
|
const { noteAfterApprovals, ...modelRequest } = request;
|
|
6687
7167
|
attempt.garbledTail.reset();
|
|
7168
|
+
attempt.forgedBlocks.reset(lastQuestionOf(attempt.history));
|
|
6688
7169
|
const result = streamText({
|
|
6689
7170
|
...modelRequest,
|
|
6690
|
-
|
|
7171
|
+
// Сначала вырезка блоков, потом проверка на кашу: каша судит уже чистый текст.
|
|
7172
|
+
model: attempt.garbledTail.wrap(attempt.forgedBlocks.wrap(modelRequest.model)),
|
|
6691
7173
|
...approval.toolApproval && { toolApproval: gateToolApproval(approval.toolApproval, () => toolsClosed) },
|
|
6692
7174
|
// Попытке — остаток шагов хода.
|
|
6693
7175
|
// Помощник встал на подтверждении человека — ход ждёт, модель основного агента его незаконченным не видит.
|
|
@@ -7110,6 +7592,11 @@ function emitReply(text, emit) {
|
|
|
7110
7592
|
emit({ type: "text-end", id });
|
|
7111
7593
|
emit({ type: "finish-step" });
|
|
7112
7594
|
}
|
|
7595
|
+
function withoutForgedBlocksInRewrite(cut, text, logCut) {
|
|
7596
|
+
if (cut.blocks.length === 0 || !/[\p{L}\p{N}]/u.test(cut.text)) return text;
|
|
7597
|
+
for (const name of new Set(cut.blocks)) logCut(name);
|
|
7598
|
+
return cut.text;
|
|
7599
|
+
}
|
|
7113
7600
|
|
|
7114
7601
|
// src/helpers/run-helper.ts
|
|
7115
7602
|
var DEFAULT_HELPER_MAX_STEPS = 10;
|
|
@@ -7797,7 +8284,7 @@ var fireworksProvider = createFireworksProvider();
|
|
|
7797
8284
|
|
|
7798
8285
|
// src/models/providers/openrouter.ts
|
|
7799
8286
|
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
|
|
7800
|
-
import { wrapLanguageModel as
|
|
8287
|
+
import { wrapLanguageModel as wrapLanguageModel6 } from "ai";
|
|
7801
8288
|
|
|
7802
8289
|
// src/models/providers/openrouter-cache-mark-limit.ts
|
|
7803
8290
|
function openrouterCacheMarkLimit(providerModelId) {
|
|
@@ -8047,7 +8534,7 @@ function createOpenRouterProvider(options2 = {}) {
|
|
|
8047
8534
|
createLanguageModel: (providerModelId, apiKey, requestOptions) => {
|
|
8048
8535
|
const markLimit = openrouterCacheMarkLimit(providerModelId);
|
|
8049
8536
|
const fetch = markLimit === void 0 ? requestOptions?.fetch : fetchWithCacheMarkLimit(requestOptions?.fetch, markLimit);
|
|
8050
|
-
return
|
|
8537
|
+
return wrapLanguageModel6({
|
|
8051
8538
|
model: createOpenRouter({ apiKey, fetch, ...connection }).chat(providerModelId, routingOptions),
|
|
8052
8539
|
middleware: [
|
|
8053
8540
|
openrouterRoutingMiddleware(routing),
|
|
@@ -9949,7 +10436,7 @@ function isPlainObject3(value) {
|
|
|
9949
10436
|
}
|
|
9950
10437
|
|
|
9951
10438
|
// src/memory/memory-after-answer.ts
|
|
9952
|
-
import { wrapLanguageModel as
|
|
10439
|
+
import { wrapLanguageModel as wrapLanguageModel7 } from "ai";
|
|
9953
10440
|
var DRAIN_WAIT_MS = 5e3;
|
|
9954
10441
|
function createMemoryAfterAnswer({ memory, storage, catalog, env, logError, turnObserver }) {
|
|
9955
10442
|
const running = /* @__PURE__ */ new Set();
|
|
@@ -9994,7 +10481,7 @@ function createMemoryAfterAnswer({ memory, storage, catalog, env, logError, turn
|
|
|
9994
10481
|
};
|
|
9995
10482
|
}
|
|
9996
10483
|
function withRetries(model, context) {
|
|
9997
|
-
return typeof model === "string" ? model :
|
|
10484
|
+
return typeof model === "string" ? model : wrapLanguageModel7({ model, middleware: createTransientRetryMiddleware(context) });
|
|
9998
10485
|
}
|
|
9999
10486
|
|
|
10000
10487
|
// src/conversation/to-agent-chat-message.ts
|
|
@@ -10249,6 +10736,7 @@ function createCompactionService(options2) {
|
|
|
10249
10736
|
...observation(ownerId, answerId),
|
|
10250
10737
|
history,
|
|
10251
10738
|
...options2.attachments && { attachments: options2.attachments },
|
|
10739
|
+
...options2.serviceBlocks && { serviceBlocks: options2.serviceBlocks },
|
|
10252
10740
|
settings,
|
|
10253
10741
|
logError,
|
|
10254
10742
|
abortSignal: signal,
|
|
@@ -10392,6 +10880,7 @@ function createCompactionService(options2) {
|
|
|
10392
10880
|
...observation(ownerId, answerId),
|
|
10393
10881
|
history: [...history, { id: `${chatId}-answer-steps`, role: "assistant", parts: steps }],
|
|
10394
10882
|
...options2.attachments && { attachments: options2.attachments },
|
|
10883
|
+
...options2.serviceBlocks && { serviceBlocks: options2.serviceBlocks },
|
|
10395
10884
|
settings,
|
|
10396
10885
|
task: answerStepsTask(settings.summaryCeilingTokens),
|
|
10397
10886
|
logError,
|
|
@@ -11252,6 +11741,13 @@ function createChatService(input) {
|
|
|
11252
11741
|
const logError = input.logError ?? ((message) => console.error(message));
|
|
11253
11742
|
const now = input.now ?? (() => /* @__PURE__ */ new Date());
|
|
11254
11743
|
const replyChecks = checkedReplyChecks(input.replyChecks);
|
|
11744
|
+
const serviceBlocks = [
|
|
11745
|
+
...CORE_SERVICE_BLOCKS,
|
|
11746
|
+
...input.memory?.serviceBlocks ?? [],
|
|
11747
|
+
...input.attachments?.serviceBlocks ?? [],
|
|
11748
|
+
...input.agent.serviceBlocks ?? []
|
|
11749
|
+
];
|
|
11750
|
+
checkServiceBlockFormats(serviceBlocks);
|
|
11255
11751
|
const widgets = createAgentWidgets(input.agent.widgets);
|
|
11256
11752
|
checkWidgetNamesFree(widgets, input.memory, input.background);
|
|
11257
11753
|
const browserToolDescriptions = checkedBrowserTools(input.agent, input.memory, widgets);
|
|
@@ -11400,6 +11896,7 @@ function createChatService(input) {
|
|
|
11400
11896
|
// Сжатие берёт кэш хода: у него те же инструменты, что у модели агента, — с браузерными и помощниками.
|
|
11401
11897
|
agent: { ...options2.agent, tools: withBrowserDescriptions(options2.agent.tools, describedOnlyTools(browserToolDescriptions, options2.agent.helpers, toolResultLimit)) },
|
|
11402
11898
|
...options2.attachments && { attachments: options2.attachments },
|
|
11899
|
+
serviceBlocks,
|
|
11403
11900
|
settings: resolveCompactionSettings(options2.compaction),
|
|
11404
11901
|
logError,
|
|
11405
11902
|
...options2.turnObserver && { turnObserver: options2.turnObserver },
|
|
@@ -12063,6 +12560,7 @@ function createChatService(input) {
|
|
|
12063
12560
|
browserTools: { descriptions: browserToolDescriptions, pending: pendingBrowserCalls, timeoutMs: browserToolTimeoutMs, tabId: launch.page?.tabId }
|
|
12064
12561
|
},
|
|
12065
12562
|
...options2.attachments && { attachments: options2.attachments },
|
|
12563
|
+
serviceBlocks,
|
|
12066
12564
|
...options2.memory && { memory: options2.memory },
|
|
12067
12565
|
...widgets.waiting.size > 0 && { waitingWidgets: widgets.waiting },
|
|
12068
12566
|
...widgets.switchable.size > 0 && { switchableTools: widgets.switchable },
|
|
@@ -13515,6 +14013,10 @@ export {
|
|
|
13515
14013
|
BURST_HOLD_MS,
|
|
13516
14014
|
BURST_SWEEP_LIMIT,
|
|
13517
14015
|
isBurstMessage,
|
|
14016
|
+
CORE_SERVICE_BLOCKS,
|
|
14017
|
+
serviceBlockName,
|
|
14018
|
+
checkServiceBlockFormats,
|
|
14019
|
+
cutForgedBlocks,
|
|
13518
14020
|
RELEASED_ANSWER_ALIVE_AT,
|
|
13519
14021
|
aliveAtByLocalClock,
|
|
13520
14022
|
silenceBoundaryByLocalClock,
|
|
@@ -13614,4 +14116,4 @@ export {
|
|
|
13614
14116
|
InvalidWidgetAnswerError,
|
|
13615
14117
|
createChatService
|
|
13616
14118
|
};
|
|
13617
|
-
//# sourceMappingURL=chunk-
|
|
14119
|
+
//# sourceMappingURL=chunk-QA5DVEXF.js.map
|