@ykurilov/agent-chat-core 0.58.0 → 0.60.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 +16 -0
- package/dist/{chunk-KAG3QTZC.js → chunk-WQZQZCAB.js} +595 -40
- package/dist/chunk-WQZQZCAB.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/models/providers/tool-result-images.d.ts +8 -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;
|
|
@@ -7755,6 +8242,59 @@ function sdkReasoningRequest(level) {
|
|
|
7755
8242
|
}
|
|
7756
8243
|
}
|
|
7757
8244
|
|
|
8245
|
+
// src/models/providers/tool-result-images.ts
|
|
8246
|
+
function withToolResultImages(given) {
|
|
8247
|
+
return (async (input, init) => {
|
|
8248
|
+
const fetchImplementation = given ?? globalThis.fetch;
|
|
8249
|
+
if (typeof init?.body !== "string" || !init.body.includes('"role":"tool"')) return fetchImplementation(input, init);
|
|
8250
|
+
let body;
|
|
8251
|
+
try {
|
|
8252
|
+
body = JSON.parse(init.body);
|
|
8253
|
+
} catch {
|
|
8254
|
+
return fetchImplementation(input, init);
|
|
8255
|
+
}
|
|
8256
|
+
let changed = false;
|
|
8257
|
+
for (const message of body.messages ?? []) {
|
|
8258
|
+
if (message.role !== "tool" || typeof message.content !== "string") continue;
|
|
8259
|
+
const parts = imageContent(message.content);
|
|
8260
|
+
if (!parts) continue;
|
|
8261
|
+
message.content = parts;
|
|
8262
|
+
changed = true;
|
|
8263
|
+
}
|
|
8264
|
+
return fetchImplementation(input, changed ? { ...init, body: JSON.stringify(body) } : init);
|
|
8265
|
+
});
|
|
8266
|
+
}
|
|
8267
|
+
function imageContent(text) {
|
|
8268
|
+
if (!text.startsWith("[")) return void 0;
|
|
8269
|
+
let value;
|
|
8270
|
+
try {
|
|
8271
|
+
value = JSON.parse(text);
|
|
8272
|
+
} catch {
|
|
8273
|
+
return void 0;
|
|
8274
|
+
}
|
|
8275
|
+
if (!Array.isArray(value)) return void 0;
|
|
8276
|
+
const parts = [];
|
|
8277
|
+
let images = 0;
|
|
8278
|
+
for (const part of value) {
|
|
8279
|
+
if (part?.type === "text" && typeof part.text === "string") {
|
|
8280
|
+
parts.push({ type: "text", text: part.text });
|
|
8281
|
+
continue;
|
|
8282
|
+
}
|
|
8283
|
+
const url = part?.type === "file" && typeof part.mediaType === "string" && part.mediaType.startsWith("image/") ? imageUrl(part.mediaType, part.data) : void 0;
|
|
8284
|
+
if (!url) return void 0;
|
|
8285
|
+
parts.push({ type: "image_url", image_url: { url } });
|
|
8286
|
+
images += 1;
|
|
8287
|
+
}
|
|
8288
|
+
return images > 0 ? parts : void 0;
|
|
8289
|
+
}
|
|
8290
|
+
function imageUrl(mediaType, data) {
|
|
8291
|
+
if (data?.type === "url" && typeof data.url === "string") return data.url;
|
|
8292
|
+
if (data?.type !== "data") return void 0;
|
|
8293
|
+
const raw = data.data;
|
|
8294
|
+
const base64 = typeof raw === "string" ? raw : raw && typeof raw === "object" ? Buffer.from(Object.keys(raw).sort((a, b) => Number(a) - Number(b)).map((key) => raw[key])).toString("base64") : void 0;
|
|
8295
|
+
return base64 === void 0 ? void 0 : `data:${mediaType === "image/jpg" ? "image/jpeg" : mediaType};base64,${base64}`;
|
|
8296
|
+
}
|
|
8297
|
+
|
|
7758
8298
|
// src/models/providers/session-header.ts
|
|
7759
8299
|
var SAFE_SESSION_ID = /^[A-Za-z0-9._:~-]{1,128}$/;
|
|
7760
8300
|
function sessionHeaderValue(chatId) {
|
|
@@ -7780,7 +8320,7 @@ function createFireworksProvider(connectionOptions = {}) {
|
|
|
7780
8320
|
id: "fireworks",
|
|
7781
8321
|
displayName: "Fireworks",
|
|
7782
8322
|
apiKeyEnv: "FIREWORKS_API_KEY",
|
|
7783
|
-
createLanguageModel: (providerModelId, apiKey, options2) => createFireworks({ apiKey, fetch: options2?.fetch, ...connection })(providerModelId),
|
|
8323
|
+
createLanguageModel: (providerModelId, apiKey, options2) => createFireworks({ apiKey, fetch: withToolResultImages(options2?.fetch), ...connection })(providerModelId),
|
|
7784
8324
|
reasoningRequest: (level) => sdkReasoningRequest(level),
|
|
7785
8325
|
requestOptions: ({ chatId }) => ({
|
|
7786
8326
|
providerOptions: {
|
|
@@ -7797,7 +8337,7 @@ var fireworksProvider = createFireworksProvider();
|
|
|
7797
8337
|
|
|
7798
8338
|
// src/models/providers/openrouter.ts
|
|
7799
8339
|
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
|
|
7800
|
-
import { wrapLanguageModel as
|
|
8340
|
+
import { wrapLanguageModel as wrapLanguageModel6 } from "ai";
|
|
7801
8341
|
|
|
7802
8342
|
// src/models/providers/openrouter-cache-mark-limit.ts
|
|
7803
8343
|
function openrouterCacheMarkLimit(providerModelId) {
|
|
@@ -8047,7 +8587,7 @@ function createOpenRouterProvider(options2 = {}) {
|
|
|
8047
8587
|
createLanguageModel: (providerModelId, apiKey, requestOptions) => {
|
|
8048
8588
|
const markLimit = openrouterCacheMarkLimit(providerModelId);
|
|
8049
8589
|
const fetch = markLimit === void 0 ? requestOptions?.fetch : fetchWithCacheMarkLimit(requestOptions?.fetch, markLimit);
|
|
8050
|
-
return
|
|
8590
|
+
return wrapLanguageModel6({
|
|
8051
8591
|
model: createOpenRouter({ apiKey, fetch, ...connection }).chat(providerModelId, routingOptions),
|
|
8052
8592
|
middleware: [
|
|
8053
8593
|
openrouterRoutingMiddleware(routing),
|
|
@@ -8152,7 +8692,7 @@ function createTogetherProvider(connectionOptions = {}) {
|
|
|
8152
8692
|
id: "together",
|
|
8153
8693
|
displayName: "Together AI",
|
|
8154
8694
|
apiKeyEnv: "TOGETHER_API_KEY",
|
|
8155
|
-
createLanguageModel: (providerModelId, apiKey, options2) => createTogetherAI({ apiKey, fetch: options2?.fetch, ...connection })(providerModelId),
|
|
8695
|
+
createLanguageModel: (providerModelId, apiKey, options2) => createTogetherAI({ apiKey, fetch: withToolResultImages(options2?.fetch), ...connection })(providerModelId),
|
|
8156
8696
|
reasoningRequest: (level) => sdkReasoningRequest(level),
|
|
8157
8697
|
samplingRequest: ({ minP }) => minP === void 0 ? {} : { togetherai: { min_p: minP } },
|
|
8158
8698
|
requestOptions: () => ({}),
|
|
@@ -8212,11 +8752,11 @@ var deepseek41FlashProfile = {
|
|
|
8212
8752
|
displayName: "DeepSeek 4.1 Flash",
|
|
8213
8753
|
contextWindowTokens: 1e6,
|
|
8214
8754
|
acceptsImages: true,
|
|
8215
|
-
//
|
|
8216
|
-
|
|
8217
|
-
//
|
|
8218
|
-
//
|
|
8219
|
-
toolResultImageProviders: ["deepseek", "openrouter"],
|
|
8755
|
+
// Картинку в результате инструмента видит у самого DeepSeek (проверено вживую 2026-10-05), у Together и Fireworks
|
|
8756
|
+
// (вживую 2026-10-15: картинка в ответе инструмента и в сообщении человека — цвет назван верно; запись каталога
|
|
8757
|
+
// Together «только текст» устарела) и через OpenRouter у всех хозяев, кроме запрещённых ниже: одни её не видят,
|
|
8758
|
+
// другие отвергают запрос (замер всех хозяев модели в OpenRouter 2026-10-06, specs/research-openrouter-tool-images.md).
|
|
8759
|
+
toolResultImageProviders: ["deepseek", "together", "fireworks", "openrouter"],
|
|
8220
8760
|
reasoning: { replayPastTurns: true, levels: ["off", "on"], defaultLevel: "on" },
|
|
8221
8761
|
// Запрет стоит всегда, а не только при картинке: запрос одинаков в каждом ходе, кэш и выбранный хозяин не теряются.
|
|
8222
8762
|
providerOptions: { openrouter: { provider: { ignore: ["deepinfra", "open-inference", "phala", "sail-research", "venice"] } } },
|
|
@@ -9949,7 +10489,7 @@ function isPlainObject3(value) {
|
|
|
9949
10489
|
}
|
|
9950
10490
|
|
|
9951
10491
|
// src/memory/memory-after-answer.ts
|
|
9952
|
-
import { wrapLanguageModel as
|
|
10492
|
+
import { wrapLanguageModel as wrapLanguageModel7 } from "ai";
|
|
9953
10493
|
var DRAIN_WAIT_MS = 5e3;
|
|
9954
10494
|
function createMemoryAfterAnswer({ memory, storage, catalog, env, logError, turnObserver }) {
|
|
9955
10495
|
const running = /* @__PURE__ */ new Set();
|
|
@@ -9994,7 +10534,7 @@ function createMemoryAfterAnswer({ memory, storage, catalog, env, logError, turn
|
|
|
9994
10534
|
};
|
|
9995
10535
|
}
|
|
9996
10536
|
function withRetries(model, context) {
|
|
9997
|
-
return typeof model === "string" ? model :
|
|
10537
|
+
return typeof model === "string" ? model : wrapLanguageModel7({ model, middleware: createTransientRetryMiddleware(context) });
|
|
9998
10538
|
}
|
|
9999
10539
|
|
|
10000
10540
|
// src/conversation/to-agent-chat-message.ts
|
|
@@ -10249,6 +10789,7 @@ function createCompactionService(options2) {
|
|
|
10249
10789
|
...observation(ownerId, answerId),
|
|
10250
10790
|
history,
|
|
10251
10791
|
...options2.attachments && { attachments: options2.attachments },
|
|
10792
|
+
...options2.serviceBlocks && { serviceBlocks: options2.serviceBlocks },
|
|
10252
10793
|
settings,
|
|
10253
10794
|
logError,
|
|
10254
10795
|
abortSignal: signal,
|
|
@@ -10392,6 +10933,7 @@ function createCompactionService(options2) {
|
|
|
10392
10933
|
...observation(ownerId, answerId),
|
|
10393
10934
|
history: [...history, { id: `${chatId}-answer-steps`, role: "assistant", parts: steps }],
|
|
10394
10935
|
...options2.attachments && { attachments: options2.attachments },
|
|
10936
|
+
...options2.serviceBlocks && { serviceBlocks: options2.serviceBlocks },
|
|
10395
10937
|
settings,
|
|
10396
10938
|
task: answerStepsTask(settings.summaryCeilingTokens),
|
|
10397
10939
|
logError,
|
|
@@ -11252,6 +11794,13 @@ function createChatService(input) {
|
|
|
11252
11794
|
const logError = input.logError ?? ((message) => console.error(message));
|
|
11253
11795
|
const now = input.now ?? (() => /* @__PURE__ */ new Date());
|
|
11254
11796
|
const replyChecks = checkedReplyChecks(input.replyChecks);
|
|
11797
|
+
const serviceBlocks = [
|
|
11798
|
+
...CORE_SERVICE_BLOCKS,
|
|
11799
|
+
...input.memory?.serviceBlocks ?? [],
|
|
11800
|
+
...input.attachments?.serviceBlocks ?? [],
|
|
11801
|
+
...input.agent.serviceBlocks ?? []
|
|
11802
|
+
];
|
|
11803
|
+
checkServiceBlockFormats(serviceBlocks);
|
|
11255
11804
|
const widgets = createAgentWidgets(input.agent.widgets);
|
|
11256
11805
|
checkWidgetNamesFree(widgets, input.memory, input.background);
|
|
11257
11806
|
const browserToolDescriptions = checkedBrowserTools(input.agent, input.memory, widgets);
|
|
@@ -11400,6 +11949,7 @@ function createChatService(input) {
|
|
|
11400
11949
|
// Сжатие берёт кэш хода: у него те же инструменты, что у модели агента, — с браузерными и помощниками.
|
|
11401
11950
|
agent: { ...options2.agent, tools: withBrowserDescriptions(options2.agent.tools, describedOnlyTools(browserToolDescriptions, options2.agent.helpers, toolResultLimit)) },
|
|
11402
11951
|
...options2.attachments && { attachments: options2.attachments },
|
|
11952
|
+
serviceBlocks,
|
|
11403
11953
|
settings: resolveCompactionSettings(options2.compaction),
|
|
11404
11954
|
logError,
|
|
11405
11955
|
...options2.turnObserver && { turnObserver: options2.turnObserver },
|
|
@@ -12063,6 +12613,7 @@ function createChatService(input) {
|
|
|
12063
12613
|
browserTools: { descriptions: browserToolDescriptions, pending: pendingBrowserCalls, timeoutMs: browserToolTimeoutMs, tabId: launch.page?.tabId }
|
|
12064
12614
|
},
|
|
12065
12615
|
...options2.attachments && { attachments: options2.attachments },
|
|
12616
|
+
serviceBlocks,
|
|
12066
12617
|
...options2.memory && { memory: options2.memory },
|
|
12067
12618
|
...widgets.waiting.size > 0 && { waitingWidgets: widgets.waiting },
|
|
12068
12619
|
...widgets.switchable.size > 0 && { switchableTools: widgets.switchable },
|
|
@@ -13515,6 +14066,10 @@ export {
|
|
|
13515
14066
|
BURST_HOLD_MS,
|
|
13516
14067
|
BURST_SWEEP_LIMIT,
|
|
13517
14068
|
isBurstMessage,
|
|
14069
|
+
CORE_SERVICE_BLOCKS,
|
|
14070
|
+
serviceBlockName,
|
|
14071
|
+
checkServiceBlockFormats,
|
|
14072
|
+
cutForgedBlocks,
|
|
13518
14073
|
RELEASED_ANSWER_ALIVE_AT,
|
|
13519
14074
|
aliveAtByLocalClock,
|
|
13520
14075
|
silenceBoundaryByLocalClock,
|
|
@@ -13614,4 +14169,4 @@ export {
|
|
|
13614
14169
|
InvalidWidgetAnswerError,
|
|
13615
14170
|
createChatService
|
|
13616
14171
|
};
|
|
13617
|
-
//# sourceMappingURL=chunk-
|
|
14172
|
+
//# sourceMappingURL=chunk-WQZQZCAB.js.map
|