@ykurilov/agent-chat-core 0.56.0 → 0.58.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 +35 -0
- package/README.md +2 -0
- package/dist/access.js +2 -2
- package/dist/{chunk-SNPF4BPB.js → chunk-FBULM4V6.js} +2 -2
- package/dist/{chunk-PWGI6NH6.js → chunk-FOAIG4DY.js} +1 -1
- package/dist/chunk-FOAIG4DY.js.map +1 -0
- package/dist/{chunk-7M7M7DAN.js → chunk-KAG3QTZC.js} +661 -46
- package/dist/chunk-KAG3QTZC.js.map +1 -0
- package/dist/index.js +77 -3
- package/dist/index.js.map +1 -1
- package/dist/storage-contract.js +87 -5
- package/dist/storage-contract.js.map +1 -1
- package/dist/types/agent-run/garbled-tail.d.ts +69 -0
- package/dist/types/agent-run/next-attempt.d.ts +12 -1
- package/dist/types/agent-run/reply-checks.d.ts +3 -1
- package/dist/types/agent-run/run-agent-turn.d.ts +6 -0
- package/dist/types/agent-run/staff-replies.d.ts +7 -0
- package/dist/types/bursts/message-bursts.d.ts +12 -0
- package/dist/types/chat-events/chat-events.d.ts +7 -0
- package/dist/types/chat-service.d.ts +37 -0
- package/dist/types/helpers/run-helper.d.ts +3 -0
- package/dist/types/index.d.ts +5 -3
- package/dist/types/provider-errors/classify-provider-error.d.ts +5 -0
- package/dist/types/provider-errors/error-signs.d.ts +2 -2
- package/dist/types/provider-errors/provider-outages.d.ts +41 -0
- package/dist/types/storage/chat-storage.d.ts +47 -0
- package/dist/types/storage/postgres-migrations.d.ts +28 -0
- package/dist/types/storage/storage-contract.d.ts +5 -0
- package/dist/types/turn-hooks/turn-gate.d.ts +2 -0
- package/dist/types/turn-hooks/turn-hooks.d.ts +7 -0
- package/package.json +1 -1
- package/dist/chunk-7M7M7DAN.js.map +0 -1
- package/dist/chunk-PWGI6NH6.js.map +0 -1
- /package/dist/{chunk-SNPF4BPB.js.map → chunk-FBULM4V6.js.map} +0 -0
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
TurnRefusedError,
|
|
4
4
|
normalizeTurnRefusal,
|
|
5
5
|
validateAppEvent
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-FOAIG4DY.js";
|
|
7
7
|
|
|
8
8
|
// src/conversation/types.ts
|
|
9
9
|
var CURRENT_FORMAT_VERSION = 1;
|
|
@@ -1718,6 +1718,9 @@ var DEFAULT_BURST_QUIET_MS = 3e3;
|
|
|
1718
1718
|
var MAX_BURST_QUIET_MS = 6e5;
|
|
1719
1719
|
var BURST_PENDING_GRACE_MS = 12e4;
|
|
1720
1720
|
var BURST_BUSY_RETRY_MS = 1e3;
|
|
1721
|
+
var BURST_HOLD_MS = 3e4;
|
|
1722
|
+
var BURST_HOLD_RENEW_MS = 1e4;
|
|
1723
|
+
var BURST_SWEEP_LIMIT = 10;
|
|
1721
1724
|
function burstPart(quietMs, at) {
|
|
1722
1725
|
return { type: BURST_PART_TYPE, data: { quietMs, at } };
|
|
1723
1726
|
}
|
|
@@ -1826,6 +1829,9 @@ function createBurstWaits() {
|
|
|
1826
1829
|
wake(chatId) {
|
|
1827
1830
|
for (const entry of [...timers]) if (entry.chatId === chatId) entry.done();
|
|
1828
1831
|
},
|
|
1832
|
+
wakeAll() {
|
|
1833
|
+
for (const entry of [...timers]) entry.done();
|
|
1834
|
+
},
|
|
1829
1835
|
has: (chatId) => waiting.has(chatId),
|
|
1830
1836
|
async holding(chatId, work) {
|
|
1831
1837
|
enter(chatId);
|
|
@@ -2478,10 +2484,9 @@ var NOT_RETRYABLE_SIGNS = signs([
|
|
|
2478
2484
|
"available balance",
|
|
2479
2485
|
"insufficient_quota",
|
|
2480
2486
|
"out of budget",
|
|
2481
|
-
"quota exceeded",
|
|
2482
|
-
"billing",
|
|
2483
2487
|
"subscription_sharing_usage_limit_exceeded"
|
|
2484
2488
|
]);
|
|
2489
|
+
var QUOTA_WORDS = signs(["quota exceeded", "billing"]);
|
|
2485
2490
|
var RETRYABLE_SIGNS = signs([
|
|
2486
2491
|
"overloaded",
|
|
2487
2492
|
"currently experiencing high demand",
|
|
@@ -2528,8 +2533,8 @@ function isContextOverflow(text, statusCode) {
|
|
|
2528
2533
|
if (NOT_OVERFLOW_SIGNS.some((sign) => sign.test(text))) return false;
|
|
2529
2534
|
return OVERFLOW_SIGNS.some((sign) => sign.test(text));
|
|
2530
2535
|
}
|
|
2531
|
-
function isNotRetryable(text) {
|
|
2532
|
-
return NOT_RETRYABLE_SIGNS.test(text);
|
|
2536
|
+
function isNotRetryable(text, statusCode) {
|
|
2537
|
+
return NOT_RETRYABLE_SIGNS.test(text) || statusCode !== 429 && QUOTA_WORDS.test(text);
|
|
2533
2538
|
}
|
|
2534
2539
|
function looksTransient(text) {
|
|
2535
2540
|
return RETRYABLE_SIGNS.test(text);
|
|
@@ -2567,7 +2572,7 @@ function isTransient(error) {
|
|
|
2567
2572
|
if (verdict === "false") return false;
|
|
2568
2573
|
const status = errorStatusCode(error);
|
|
2569
2574
|
const text = errorText(error);
|
|
2570
|
-
if (isNotRetryable(text) || isContextOverflow(text, status)) return false;
|
|
2575
|
+
if (isNotRetryable(text, status) || isContextOverflow(text, status)) return false;
|
|
2571
2576
|
if (status !== void 0) return RETRYABLE_STATUS_CODES.has(status) || status >= 500;
|
|
2572
2577
|
if (APICallError2.isInstance(error) && error.isRetryable) return true;
|
|
2573
2578
|
return looksTransient(text);
|
|
@@ -2741,6 +2746,9 @@ function sleep(ms, signal) {
|
|
|
2741
2746
|
}
|
|
2742
2747
|
|
|
2743
2748
|
// src/provider-errors/classify-provider-error.ts
|
|
2749
|
+
var KEY_SIGNS = /api[ _-]?key|authenticat|unauthori[sz]ed|credential|access[ _-]?token|key (?:is )?(?:invalid|revoked|expired|disabled)/i;
|
|
2750
|
+
var FUNDS_SIGNS = /key limit exceeded|insufficient (?:credits|funds|balance)/i;
|
|
2751
|
+
var MODERATION_SIGNS = /flagged|moderation|content[ _-]?policy|safety/i;
|
|
2744
2752
|
var RATE_LIMIT_SIGNS = /rate.?limit|too many requests|throttl|please wait|ResourceExhausted/i;
|
|
2745
2753
|
function classifyProviderError(error, provider) {
|
|
2746
2754
|
if (error instanceof MissingProviderKeyError) return "invalid_key";
|
|
@@ -2750,8 +2758,12 @@ function classifyProviderError(error, provider) {
|
|
|
2750
2758
|
for (const hint of provider?.errorHints ?? []) {
|
|
2751
2759
|
if (hint.match.test(text)) return hint.kind;
|
|
2752
2760
|
}
|
|
2753
|
-
if (status === 401
|
|
2754
|
-
if (status ===
|
|
2761
|
+
if (status === 401) return "invalid_key";
|
|
2762
|
+
if (status === 403) {
|
|
2763
|
+
if (KEY_SIGNS.test(text)) return "invalid_key";
|
|
2764
|
+
return FUNDS_SIGNS.test(text) || isNotRetryable(text) ? "insufficient_funds" : "other";
|
|
2765
|
+
}
|
|
2766
|
+
if (status === 402 || isNotRetryable(text, status)) return "insufficient_funds";
|
|
2755
2767
|
if (isContextOverflow(text, status)) return "context_overflow";
|
|
2756
2768
|
if (status === 429) return "rate_limited";
|
|
2757
2769
|
if (isProviderSilence(cause)) return "overloaded";
|
|
@@ -2759,6 +2771,10 @@ function classifyProviderError(error, provider) {
|
|
|
2759
2771
|
if (RATE_LIMIT_SIGNS.test(text)) return "rate_limited";
|
|
2760
2772
|
return looksTransient(text) ? "overloaded" : "other";
|
|
2761
2773
|
}
|
|
2774
|
+
function isAccessRefusal(error) {
|
|
2775
|
+
const cause = underlyingError(error);
|
|
2776
|
+
return errorStatusCode(cause) === 403 && !MODERATION_SIGNS.test(errorText(cause));
|
|
2777
|
+
}
|
|
2762
2778
|
|
|
2763
2779
|
// src/provider-errors/provider-failure-log.ts
|
|
2764
2780
|
import { APICallError as APICallError3 } from "ai";
|
|
@@ -2784,6 +2800,49 @@ function oneLine(text) {
|
|
|
2784
2800
|
return text.replace(/\s*[\r\n]+\s*/g, " ");
|
|
2785
2801
|
}
|
|
2786
2802
|
|
|
2803
|
+
// src/provider-errors/provider-outages.ts
|
|
2804
|
+
function isProviderOutage(kind) {
|
|
2805
|
+
return kind === "insufficient_funds" || kind === "invalid_key";
|
|
2806
|
+
}
|
|
2807
|
+
var PROVIDER_OUTAGE_PAUSE_MS = 5 * 6e4;
|
|
2808
|
+
var PROVIDER_OUTAGE_ALERT_EVERY_MS = 15 * 6e4;
|
|
2809
|
+
var PROVIDER_OUTAGE_PROBE_MS = 6e4;
|
|
2810
|
+
var SAID = {
|
|
2811
|
+
insufficient_funds: "\u043A\u043E\u043D\u0447\u0438\u043B\u0438\u0441\u044C \u0434\u0435\u043D\u044C\u0433\u0438",
|
|
2812
|
+
invalid_key: "\u043A\u043B\u044E\u0447 \u043D\u0435 \u043F\u0440\u0438\u043D\u044F\u0442"
|
|
2813
|
+
};
|
|
2814
|
+
function createProviderOutages(options2) {
|
|
2815
|
+
const outages = /* @__PURE__ */ new Map();
|
|
2816
|
+
const alertedAt = /* @__PURE__ */ new Map();
|
|
2817
|
+
const pausedKind = (providerId) => {
|
|
2818
|
+
const outage = outages.get(providerId);
|
|
2819
|
+
if (!outage) return void 0;
|
|
2820
|
+
const time = options2.now();
|
|
2821
|
+
return time < outage.pausedUntil || time < (outage.probeUntil ?? 0) ? outage.kind : void 0;
|
|
2822
|
+
};
|
|
2823
|
+
return {
|
|
2824
|
+
report({ providerId, kind, fallbackProviderId, fallbacksPaused }) {
|
|
2825
|
+
const time = options2.now();
|
|
2826
|
+
outages.set(providerId, { kind, pausedUntil: time + PROVIDER_OUTAGE_PAUSE_MS });
|
|
2827
|
+
const last = alertedAt.get(providerId);
|
|
2828
|
+
if (last !== void 0 && time - last < PROVIDER_OUTAGE_ALERT_EVERY_MS) return;
|
|
2829
|
+
alertedAt.set(providerId, time);
|
|
2830
|
+
const route = fallbackProviderId !== void 0 ? `\u0445\u043E\u0434\u044B \u0438\u0434\u0443\u0442 \u0447\u0435\u0440\u0435\u0437 \u0437\u0430\u043F\u0430\u0441\u043D\u043E\u0439 ${fallbackProviderId}` : `${fallbacksPaused ? "\u0437\u0430\u043F\u0430\u0441\u043D\u044B\u0435 \u0442\u043E\u0436\u0435 \u043D\u0430 \u043F\u0430\u0443\u0437\u0435" : "\u0437\u0430\u043F\u0430\u0441\u043D\u043E\u0433\u043E \u043D\u0435\u0442"}, \u0445\u043E\u0434\u044B \u043F\u0430\u0434\u0430\u044E\u0442 \u0441 \u043E\u0448\u0438\u0431\u043A\u043E\u0439`;
|
|
2831
|
+
options2.logError(`[\u0412\u041D\u0418\u041C\u0410\u041D\u0418\u0415 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440] ${providerId}: ${SAID[kind]} \u2014 ${route}`);
|
|
2832
|
+
},
|
|
2833
|
+
pausedKind,
|
|
2834
|
+
admit(providerId) {
|
|
2835
|
+
const kind = pausedKind(providerId);
|
|
2836
|
+
const outage = outages.get(providerId);
|
|
2837
|
+
if (kind === void 0 && outage) outage.probeUntil = options2.now() + PROVIDER_OUTAGE_PROBE_MS;
|
|
2838
|
+
return kind;
|
|
2839
|
+
},
|
|
2840
|
+
recovered(providerId) {
|
|
2841
|
+
outages.delete(providerId);
|
|
2842
|
+
}
|
|
2843
|
+
};
|
|
2844
|
+
}
|
|
2845
|
+
|
|
2787
2846
|
// src/tool-approval/tool-approval-policy.ts
|
|
2788
2847
|
var needsApprovalOf = (tool4) => tool4.needsApproval;
|
|
2789
2848
|
function prepareToolApproval(tools, alwaysAllowed) {
|
|
@@ -2859,7 +2918,7 @@ function usageMinusJournaled(usage, journaled) {
|
|
|
2859
2918
|
}
|
|
2860
2919
|
|
|
2861
2920
|
// src/agent-run/build-model-request.ts
|
|
2862
|
-
import { convertToModelMessages, wrapLanguageModel as
|
|
2921
|
+
import { convertToModelMessages, wrapLanguageModel as wrapLanguageModel4 } from "ai";
|
|
2863
2922
|
|
|
2864
2923
|
// src/conversation/hanging-tool-calls.ts
|
|
2865
2924
|
var PRELIMINARY_OUTPUT_MAX_LENGTH = 2e3;
|
|
@@ -3535,6 +3594,37 @@ function withUnansweredMarked(messages) {
|
|
|
3535
3594
|
);
|
|
3536
3595
|
}
|
|
3537
3596
|
|
|
3597
|
+
// src/agent-run/staff-replies.ts
|
|
3598
|
+
function isStaffReply(message) {
|
|
3599
|
+
if (message.role !== "user") return false;
|
|
3600
|
+
const notices = message.parts.flatMap(noticesOfPart);
|
|
3601
|
+
return notices.length > 0 && notices.every((notice) => notice.kind === "staff");
|
|
3602
|
+
}
|
|
3603
|
+
function staffPartsJoined(parts) {
|
|
3604
|
+
return parts.flatMap((part) => {
|
|
3605
|
+
if (part.type !== AGENT_NOTICE_PART_TYPE) return joinedParts([part]);
|
|
3606
|
+
const text = noticePartForModel(part);
|
|
3607
|
+
return text ? [{ type: "text", text: `
|
|
3608
|
+
|
|
3609
|
+
${text.text}` }] : [];
|
|
3610
|
+
});
|
|
3611
|
+
}
|
|
3612
|
+
function withStaffRepliesJoined(history) {
|
|
3613
|
+
const joined = [];
|
|
3614
|
+
let afterStaff = false;
|
|
3615
|
+
let changed = false;
|
|
3616
|
+
for (const message of history) {
|
|
3617
|
+
const previous = joined.at(-1);
|
|
3618
|
+
const staff = isStaffReply(message);
|
|
3619
|
+
if (previous?.role === "user" && message.role === "user" && (staff || afterStaff)) {
|
|
3620
|
+
joined[joined.length - 1] = { ...previous, parts: [...previous.parts, ...staff ? staffPartsJoined(message.parts) : joinedParts(message.parts)] };
|
|
3621
|
+
changed = true;
|
|
3622
|
+
} else joined.push(message);
|
|
3623
|
+
afterStaff = staff;
|
|
3624
|
+
}
|
|
3625
|
+
return changed ? joined : history;
|
|
3626
|
+
}
|
|
3627
|
+
|
|
3538
3628
|
// src/frozen-context/frozen-context-messages.ts
|
|
3539
3629
|
function frozenContextBase(history) {
|
|
3540
3630
|
for (let index = history.length - 1; index >= 0; index -= 1) {
|
|
@@ -3660,6 +3750,298 @@ function withoutUndelivered(history) {
|
|
|
3660
3750
|
return dropped.size === 0 ? history : history.filter((_, index) => !dropped.has(index));
|
|
3661
3751
|
}
|
|
3662
3752
|
|
|
3753
|
+
// src/agent-run/garbled-tail.ts
|
|
3754
|
+
import { wrapLanguageModel as wrapLanguageModel3 } from "ai";
|
|
3755
|
+
var SIGN_ORDER = ["think", "role", "restart", "tool"];
|
|
3756
|
+
var TOKEN_SOURCE = String.raw`<\/?think>|<\|[^|\n<>]{1,40}\|>|<|[^|\n<>]{1,40}|>`;
|
|
3757
|
+
var TEMPLATE_TOKEN = new RegExp(TOKEN_SOURCE, "g");
|
|
3758
|
+
var ROLE_LINE = /(?<=^|\n)(user|assistant)(?=[ \t]*(?:\n|$)|[А-ЯЁа-яё])/g;
|
|
3759
|
+
var TOKEN_ROLE_LINE = /(?<=^|\n)(?:(?:<\|im_start\|>|<\|start_header_id\|>)(?:user|assistant)\b|<\|(?:user|assistant)\|>|<|(?:User|Assistant)|>)/g;
|
|
3760
|
+
var ANCHOR_HINT = /(?:^|\n)(?:(?:user|assistant)(?:[ \t]*(?:\n|$)|[А-ЯЁа-яё])|(?:<\|im_start\|>|<\|start_header_id\|>)(?:user|assistant)|<\|(?:user|assistant)\|>|<|(?:User|Assistant)|>)/;
|
|
3761
|
+
var ROLE_TOKEN = /(?:<\|im_start\|>|<\|start_header_id\|>)(user|assistant)\b|<\|(user|assistant)\|>|<|(User|Assistant)|>/g;
|
|
3762
|
+
var GLUED_SENTENCES = /[а-яё]{2}[.!?…][А-ЯЁ][а-яё]/;
|
|
3763
|
+
var changesVerdict = (lines) => ANCHOR_HINT.test(lines) || GLUED_SENTENCES.test(lines) || new RegExp(TOKEN_SOURCE).test(lines);
|
|
3764
|
+
var FINISHED = /[.!?…»)"]$/;
|
|
3765
|
+
var TRAILING_TOKENS = new RegExp(String.raw`(?:\s|${TOKEN_SOURCE})+$`);
|
|
3766
|
+
function findGarbledTail(parts) {
|
|
3767
|
+
if (!parts.some((part) => part.type === "text" && ANCHOR_HINT.test(part.text))) return null;
|
|
3768
|
+
const steps = stepsOf(parts);
|
|
3769
|
+
const markersByStep = steps.map((step) => step.texts.flatMap((text, at) => markersIn(text, step.texts.slice(0, at))));
|
|
3770
|
+
const bareRoles = new Set(markersByStep.flat().flatMap((marker) => marker.bareRole ? [marker.bareRole.name] : []));
|
|
3771
|
+
const tokenRoles = new Set(
|
|
3772
|
+
steps.flatMap((step) => step.texts.flatMap((text) => [...text.visible.matchAll(ROLE_TOKEN)].map((match) => (match[1] ?? match[2] ?? match[3]).toLowerCase())))
|
|
3773
|
+
);
|
|
3774
|
+
const dialog = bareRoles.size > 1;
|
|
3775
|
+
const template = tokenRoles.size > 1;
|
|
3776
|
+
for (const [index, step] of steps.entries()) {
|
|
3777
|
+
const markers = markersByStep[index].filter((marker) => !(dialog && marker.bareRole) && !(template && marker.roleToken));
|
|
3778
|
+
const found = garbleInStep(parts, step, markers);
|
|
3779
|
+
if (found) return found;
|
|
3780
|
+
}
|
|
3781
|
+
return null;
|
|
3782
|
+
}
|
|
3783
|
+
function cutGarbledTail(parts) {
|
|
3784
|
+
const found = findGarbledTail(parts);
|
|
3785
|
+
if (!found) return null;
|
|
3786
|
+
const stepEnd = parts.findIndex((part, index) => index > found.partIndex && part.type === "step-start");
|
|
3787
|
+
const end = stepEnd === -1 ? parts.length : stepEnd;
|
|
3788
|
+
const cutPart = parts[found.partIndex];
|
|
3789
|
+
const answer = cutPart.text.slice(0, found.offset).replace(TRAILING_TOKENS, "");
|
|
3790
|
+
let removedChars = cutPart.text.length - answer.length;
|
|
3791
|
+
const kept = parts.slice(0, found.partIndex);
|
|
3792
|
+
if (answer) kept.push({ ...cutPart, text: answer, state: "done" });
|
|
3793
|
+
for (const part of parts.slice(found.partIndex + 1, end)) {
|
|
3794
|
+
if (part.type === "text") removedChars += part.text.length;
|
|
3795
|
+
else if (part.type === "reasoning") continue;
|
|
3796
|
+
else if (!(stepEnd === -1 && part.type === LENGTH_CUT_PART_TYPE)) kept.push(part);
|
|
3797
|
+
}
|
|
3798
|
+
kept.push(...parts.slice(end));
|
|
3799
|
+
if (!kept.some((part) => part.type === "text" && /[\p{L}\p{N}]/u.test(part.text.replace(TEMPLATE_TOKEN, "")))) return null;
|
|
3800
|
+
const textIndex = answer ? found.partIndex : parts.slice(0, found.partIndex).findLastIndex((part) => part.type === "text");
|
|
3801
|
+
return { parts: kept, signs: found.signs, removedChars, textIndex };
|
|
3802
|
+
}
|
|
3803
|
+
function withoutGarbledTails(history) {
|
|
3804
|
+
let changed = false;
|
|
3805
|
+
const cleaned = history.map((message) => {
|
|
3806
|
+
if (message.role !== "assistant") return message;
|
|
3807
|
+
const parts = rememberedCut(message);
|
|
3808
|
+
if (!parts) return message;
|
|
3809
|
+
changed = true;
|
|
3810
|
+
return { ...message, parts };
|
|
3811
|
+
});
|
|
3812
|
+
return changed ? cleaned : history;
|
|
3813
|
+
}
|
|
3814
|
+
var rememberedCuts = /* @__PURE__ */ new Map();
|
|
3815
|
+
var REMEMBERED_CUTS_LIMIT = 500;
|
|
3816
|
+
function rememberedCut(message) {
|
|
3817
|
+
if (!message.parts.some((part) => part.type === "text" && ANCHOR_HINT.test(part.text))) return null;
|
|
3818
|
+
const source = JSON.stringify(message.parts);
|
|
3819
|
+
const known = rememberedCuts.get(message.id);
|
|
3820
|
+
if (known?.source === source) return known.parts;
|
|
3821
|
+
const parts = cutGarbledTail(message.parts)?.parts ?? null;
|
|
3822
|
+
rememberedCuts.delete(message.id);
|
|
3823
|
+
rememberedCuts.set(message.id, { source, parts });
|
|
3824
|
+
if (rememberedCuts.size > REMEMBERED_CUTS_LIMIT) rememberedCuts.delete(rememberedCuts.keys().next().value);
|
|
3825
|
+
return parts;
|
|
3826
|
+
}
|
|
3827
|
+
var DROPPED_CALL = { type: "tool-dropped", toolCallId: "", state: "input-available", input: {} };
|
|
3828
|
+
function createGarbledTailGuard() {
|
|
3829
|
+
let parts = [];
|
|
3830
|
+
let garbled = false;
|
|
3831
|
+
let droppedCalls = 0;
|
|
3832
|
+
let hinted = false;
|
|
3833
|
+
const worthChecking = (lines) => {
|
|
3834
|
+
hinted ||= ANCHOR_HINT.test(lines);
|
|
3835
|
+
return hinted && changesVerdict(lines);
|
|
3836
|
+
};
|
|
3837
|
+
const textAt = /* @__PURE__ */ new Map();
|
|
3838
|
+
const open = /* @__PURE__ */ new Set();
|
|
3839
|
+
const droppedIds = /* @__PURE__ */ new Set();
|
|
3840
|
+
const garbledNow = (withCall) => {
|
|
3841
|
+
if (!hinted) return false;
|
|
3842
|
+
const known = parts.map(
|
|
3843
|
+
(part, index) => part.type === "text" && open.has(index) ? { ...part, text: part.text.slice(0, part.text.lastIndexOf("\n") + 1) } : part
|
|
3844
|
+
);
|
|
3845
|
+
return cutGarbledTail(withCall ? [...known, DROPPED_CALL] : known) !== null;
|
|
3846
|
+
};
|
|
3847
|
+
const transform = (part, controller) => {
|
|
3848
|
+
switch (part.type) {
|
|
3849
|
+
case "text-start": {
|
|
3850
|
+
const index = parts.push({ type: "text", text: "" }) - 1;
|
|
3851
|
+
textAt.set(part.id, index);
|
|
3852
|
+
open.add(index);
|
|
3853
|
+
break;
|
|
3854
|
+
}
|
|
3855
|
+
case "text-delta": {
|
|
3856
|
+
if (garbled) return;
|
|
3857
|
+
const index = textAt.get(part.id);
|
|
3858
|
+
if (index === void 0) break;
|
|
3859
|
+
const text = parts[index];
|
|
3860
|
+
const before = text.text.length;
|
|
3861
|
+
const lineStart = text.text.lastIndexOf("\n") + 1;
|
|
3862
|
+
text.text += part.delta;
|
|
3863
|
+
if (!part.delta.includes("\n")) break;
|
|
3864
|
+
if (worthChecking(text.text.slice(lineStart, text.text.lastIndexOf("\n") + 1)) && garbledNow(false)) {
|
|
3865
|
+
garbled = true;
|
|
3866
|
+
const keep = text.text.lastIndexOf("\n") + 1 - before;
|
|
3867
|
+
text.text = text.text.slice(0, before + Math.max(keep, 0));
|
|
3868
|
+
if (keep > 0) controller.enqueue({ ...part, delta: part.delta.slice(0, keep) });
|
|
3869
|
+
return;
|
|
3870
|
+
}
|
|
3871
|
+
break;
|
|
3872
|
+
}
|
|
3873
|
+
case "text-end": {
|
|
3874
|
+
const index = textAt.get(part.id);
|
|
3875
|
+
if (index === void 0 || garbled) break;
|
|
3876
|
+
open.delete(index);
|
|
3877
|
+
const { text } = parts[index];
|
|
3878
|
+
if (worthChecking(text.slice(text.lastIndexOf("\n") + 1)) && garbledNow(false)) garbled = true;
|
|
3879
|
+
break;
|
|
3880
|
+
}
|
|
3881
|
+
case "reasoning-delta":
|
|
3882
|
+
if (garbled) return;
|
|
3883
|
+
break;
|
|
3884
|
+
case "tool-input-start":
|
|
3885
|
+
case "tool-call": {
|
|
3886
|
+
const callId = part.type === "tool-call" ? part.toolCallId : part.id;
|
|
3887
|
+
if (droppedIds.has(callId)) return;
|
|
3888
|
+
const started = part.type === "tool-call" && parts.some((known) => known.toolCallId === callId);
|
|
3889
|
+
if (started) break;
|
|
3890
|
+
if (garbled || garbledNow(true)) {
|
|
3891
|
+
garbled = true;
|
|
3892
|
+
droppedIds.add(callId);
|
|
3893
|
+
droppedCalls += 1;
|
|
3894
|
+
return;
|
|
3895
|
+
}
|
|
3896
|
+
parts.push({ type: "tool-call", toolCallId: callId, state: "input-streaming" });
|
|
3897
|
+
break;
|
|
3898
|
+
}
|
|
3899
|
+
case "tool-input-delta":
|
|
3900
|
+
case "tool-input-end":
|
|
3901
|
+
if (droppedIds.has(part.id)) return;
|
|
3902
|
+
break;
|
|
3903
|
+
case "finish":
|
|
3904
|
+
if (droppedCalls > 0 && part.finishReason.unified === "tool-calls") {
|
|
3905
|
+
controller.enqueue({ ...part, finishReason: { ...part.finishReason, unified: "stop" } });
|
|
3906
|
+
return;
|
|
3907
|
+
}
|
|
3908
|
+
break;
|
|
3909
|
+
}
|
|
3910
|
+
controller.enqueue(part);
|
|
3911
|
+
};
|
|
3912
|
+
return {
|
|
3913
|
+
/** Новая попытка: шаги прежней стёрты — всё сначала. */
|
|
3914
|
+
reset() {
|
|
3915
|
+
parts = [];
|
|
3916
|
+
garbled = false;
|
|
3917
|
+
droppedCalls = 0;
|
|
3918
|
+
hinted = false;
|
|
3919
|
+
textAt.clear();
|
|
3920
|
+
open.clear();
|
|
3921
|
+
droppedIds.clear();
|
|
3922
|
+
},
|
|
3923
|
+
/** Модель попытки под присмотром: каждый запрос — новый шаг. */
|
|
3924
|
+
wrap(model) {
|
|
3925
|
+
if (typeof model === "string") return model;
|
|
3926
|
+
return wrapLanguageModel3({
|
|
3927
|
+
model,
|
|
3928
|
+
middleware: {
|
|
3929
|
+
specificationVersion: "v4",
|
|
3930
|
+
wrapStream: async ({ doStream }) => {
|
|
3931
|
+
const result = await doStream();
|
|
3932
|
+
parts.push({ type: "step-start" });
|
|
3933
|
+
return { ...result, stream: result.stream.pipeThrough(new TransformStream({ transform })) };
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
});
|
|
3937
|
+
},
|
|
3938
|
+
/** Срыв уже замечен в этой попытке: нового шага не нужно. */
|
|
3939
|
+
garbled: () => garbled,
|
|
3940
|
+
/** Сколько вызовов отброшено до запуска. */
|
|
3941
|
+
droppedCalls: () => droppedCalls,
|
|
3942
|
+
/** Части ответа для правила при сохранении: отброшенный вызов — на своём месте, в конце. */
|
|
3943
|
+
forCut: (saved) => droppedCalls > 0 ? [...saved, DROPPED_CALL] : saved,
|
|
3944
|
+
/** Заглушка отброшенного вызова из `forCut`: в запись не идёт. */
|
|
3945
|
+
isDroppedCall: (part) => part === DROPPED_CALL
|
|
3946
|
+
};
|
|
3947
|
+
}
|
|
3948
|
+
var isToolPart6 = (part) => part.type.startsWith("tool-") || part.type === "dynamic-tool";
|
|
3949
|
+
function stepsOf(parts) {
|
|
3950
|
+
const steps = [];
|
|
3951
|
+
let current = { from: 0, to: 0, texts: [] };
|
|
3952
|
+
parts.forEach((part, partIndex) => {
|
|
3953
|
+
if (part.type === "step-start") {
|
|
3954
|
+
current.to = partIndex;
|
|
3955
|
+
steps.push(current);
|
|
3956
|
+
current = { from: partIndex, to: partIndex, texts: [] };
|
|
3957
|
+
} else if (part.type === "text") current.texts.push({ partIndex, visible: withoutCodeAndQuotes(part.text) });
|
|
3958
|
+
});
|
|
3959
|
+
current.to = parts.length;
|
|
3960
|
+
steps.push(current);
|
|
3961
|
+
return steps.filter((step) => step.texts.length > 0);
|
|
3962
|
+
}
|
|
3963
|
+
function garbleInStep(parts, step, markers) {
|
|
3964
|
+
const roles = markers.filter((marker) => marker.kind === "role");
|
|
3965
|
+
const role = roles.find((marker) => finishedBefore(step.texts, marker));
|
|
3966
|
+
if (!role) return null;
|
|
3967
|
+
const after = (marker) => byPlace(marker, role) > 0;
|
|
3968
|
+
const tokens2 = markers.filter((marker) => marker.kind !== "role");
|
|
3969
|
+
const gluedAfter = () => textAfter(step.texts, role).some((text) => GLUED_SENTENCES.test(text));
|
|
3970
|
+
const others = /* @__PURE__ */ new Set();
|
|
3971
|
+
if (role.roleToken) {
|
|
3972
|
+
if (tokens2.some((marker) => !after(marker))) others.add("think");
|
|
3973
|
+
if (roles.some(after) || gluedAfter()) others.add("restart");
|
|
3974
|
+
} else {
|
|
3975
|
+
if (tokens2.length > 0) others.add("think");
|
|
3976
|
+
if (roles.some(after)) others.add("restart");
|
|
3977
|
+
if (role.bareRole.glued) {
|
|
3978
|
+
if (gluedAfter()) others.add("restart");
|
|
3979
|
+
if (parts.slice(role.partIndex + 1, step.to).some(isToolPart6)) others.add("tool");
|
|
3980
|
+
}
|
|
3981
|
+
}
|
|
3982
|
+
if (others.size === 0) return null;
|
|
3983
|
+
const closeThinks = markers.filter((marker) => marker.kind === "close-think" && !after(marker));
|
|
3984
|
+
const anchor = closeThinks.length >= 2 ? closeThinks[0] : role;
|
|
3985
|
+
const signs2 = /* @__PURE__ */ new Set(["role", ...others]);
|
|
3986
|
+
return { partIndex: anchor.partIndex, offset: anchor.offset, signs: SIGN_ORDER.filter((sign) => signs2.has(sign)) };
|
|
3987
|
+
}
|
|
3988
|
+
var byPlace = (a, b) => a.partIndex - b.partIndex || a.offset - b.offset;
|
|
3989
|
+
function textAfter(texts, anchor) {
|
|
3990
|
+
return texts.flatMap(
|
|
3991
|
+
(text) => text.partIndex < anchor.partIndex ? [] : [text.partIndex === anchor.partIndex ? text.visible.slice(anchor.offset) : text.visible]
|
|
3992
|
+
);
|
|
3993
|
+
}
|
|
3994
|
+
function markersIn(text, earlier) {
|
|
3995
|
+
const { partIndex, visible } = text;
|
|
3996
|
+
const markers = [];
|
|
3997
|
+
const hasTextBefore = (offset) => visibleBefore(earlier, visible, offset) !== "";
|
|
3998
|
+
const roleTokens = /* @__PURE__ */ new Set();
|
|
3999
|
+
for (const match of visible.matchAll(TOKEN_ROLE_LINE)) {
|
|
4000
|
+
roleTokens.add(match.index);
|
|
4001
|
+
if (hasTextBefore(match.index)) markers.push({ partIndex, offset: match.index, kind: "role", roleToken: true });
|
|
4002
|
+
}
|
|
4003
|
+
for (const match of visible.matchAll(TEMPLATE_TOKEN)) {
|
|
4004
|
+
if (roleTokens.has(match.index) || !hasTextBefore(match.index)) continue;
|
|
4005
|
+
markers.push({ partIndex, offset: match.index, kind: match[0] === "</think>" ? "close-think" : "token" });
|
|
4006
|
+
}
|
|
4007
|
+
for (const match of visible.matchAll(ROLE_LINE)) {
|
|
4008
|
+
if (!hasTextBefore(match.index)) continue;
|
|
4009
|
+
const name = match[1];
|
|
4010
|
+
const glued = /[А-ЯЁа-яё]/.test(visible[match.index + name.length] ?? "");
|
|
4011
|
+
markers.push({ partIndex, offset: match.index, kind: "role", bareRole: { name, glued } });
|
|
4012
|
+
}
|
|
4013
|
+
return markers.sort(byPlace);
|
|
4014
|
+
}
|
|
4015
|
+
function finishedBefore(texts, marker) {
|
|
4016
|
+
const at = texts.findIndex((text) => text.partIndex === marker.partIndex);
|
|
4017
|
+
return FINISHED.test(visibleBefore(texts.slice(0, at), texts[at].visible, marker.offset));
|
|
4018
|
+
}
|
|
4019
|
+
function visibleBefore(earlier, visible, offset) {
|
|
4020
|
+
const own = visible.slice(0, offset).replace(TRAILING_TOKENS, "");
|
|
4021
|
+
if (own) return own;
|
|
4022
|
+
for (let index = earlier.length - 1; index >= 0; index -= 1) {
|
|
4023
|
+
const text = earlier[index].visible.replace(TRAILING_TOKENS, "");
|
|
4024
|
+
if (text) return text;
|
|
4025
|
+
}
|
|
4026
|
+
return "";
|
|
4027
|
+
}
|
|
4028
|
+
function withoutCodeAndQuotes(text) {
|
|
4029
|
+
let fence = null;
|
|
4030
|
+
return text.split("\n").map((line) => {
|
|
4031
|
+
const opener = /^ {0,3}(`{3,}|~{3,})/.exec(line)?.[1];
|
|
4032
|
+
if (fence !== null) {
|
|
4033
|
+
if (opener && opener[0] === fence[0] && opener.length >= fence.length && line.trim() === opener) fence = null;
|
|
4034
|
+
return " ".repeat(line.length);
|
|
4035
|
+
}
|
|
4036
|
+
if (opener) {
|
|
4037
|
+
fence = opener;
|
|
4038
|
+
return " ".repeat(line.length);
|
|
4039
|
+
}
|
|
4040
|
+
if (/^(?: {4}|\t)/.test(line) || /^\s*>/.test(line)) return " ".repeat(line.length);
|
|
4041
|
+
return line.replace(/(`+)[^`\n]*?\1/g, (code) => " ".repeat(code.length));
|
|
4042
|
+
}).join("\n");
|
|
4043
|
+
}
|
|
4044
|
+
|
|
3663
4045
|
// src/agent-run/build-model-request.ts
|
|
3664
4046
|
async function buildModelRequest(input) {
|
|
3665
4047
|
const found = findModel(input.catalog, input.modelId, input.providerId);
|
|
@@ -3684,7 +4066,7 @@ async function buildModelRequest(input) {
|
|
|
3684
4066
|
});
|
|
3685
4067
|
const tools = input.tools ? sortToolsByName(input.tools) : void 0;
|
|
3686
4068
|
const sendable = input.switchableTools ? withSwitchedOffCallsAsText(sendableHistory(input.history), input.switchableTools, tools) : sendableHistory(input.history);
|
|
3687
|
-
const noted = withChannelNotes(withModeNotes(withBurstsJoined(withCorrectionsJoined(sendable)), input.modeNotes));
|
|
4069
|
+
const noted = withStaffRepliesJoined(withChannelNotes(withModeNotes(withBurstsJoined(withCorrectionsJoined(sendable)), input.modeNotes)));
|
|
3688
4070
|
const held = input.frozenContext ? holdNoteAfterApprovals(withFrozenContext(noted, input.frozenContext)) : { messages: noted };
|
|
3689
4071
|
const history = selectReasoningForModel(held.messages, {
|
|
3690
4072
|
modelId: profile.id,
|
|
@@ -3720,7 +4102,7 @@ async function buildModelRequest(input) {
|
|
|
3720
4102
|
};
|
|
3721
4103
|
}
|
|
3722
4104
|
function sendableHistory(history) {
|
|
3723
|
-
return withoutUndelivered(history).filter((message) => (message.role !== "assistant" || hasAnswerContent(message.parts)) && !isStateMessage(message)).map(withAssistantFilesAsText).map((message) => {
|
|
4105
|
+
return withoutGarbledTails(withoutUndelivered(history)).filter((message) => (message.role !== "assistant" || hasAnswerContent(message.parts)) && !isStateMessage(message)).map(withAssistantFilesAsText).map((message) => {
|
|
3724
4106
|
const closed = message.role === "assistant" ? closeHangingToolCalls(message.parts) : message.parts;
|
|
3725
4107
|
const parts = withoutLoneSurrogatesInParts(closed);
|
|
3726
4108
|
return parts === message.parts ? message : { ...message, parts };
|
|
@@ -3768,7 +4150,7 @@ function withoutLoneSurrogatesDeep(value) {
|
|
|
3768
4150
|
}
|
|
3769
4151
|
function withRequestWatch(model, provider, watch) {
|
|
3770
4152
|
if (!watch || typeof model === "string") return model;
|
|
3771
|
-
return
|
|
4153
|
+
return wrapLanguageModel4({
|
|
3772
4154
|
model,
|
|
3773
4155
|
middleware: {
|
|
3774
4156
|
specificationVersion: "v4",
|
|
@@ -3788,7 +4170,7 @@ function withRequestWatch(model, provider, watch) {
|
|
|
3788
4170
|
});
|
|
3789
4171
|
}
|
|
3790
4172
|
function withTransientRetries(model, context) {
|
|
3791
|
-
return typeof model === "string" ? model :
|
|
4173
|
+
return typeof model === "string" ? model : wrapLanguageModel4({ model, middleware: createTransientRetryMiddleware(context) });
|
|
3792
4174
|
}
|
|
3793
4175
|
function withLastMessageMark(messages, options2) {
|
|
3794
4176
|
if (!options2 || messages.length === 0) return messages;
|
|
@@ -4019,8 +4401,14 @@ function decideNextAttempt(result, state) {
|
|
|
4019
4401
|
if (state.overflowCompaction?.answerSteps) return { action: "compact-and-continue", scope: "answer-steps" };
|
|
4020
4402
|
return { action: "fail" };
|
|
4021
4403
|
}
|
|
4022
|
-
if (
|
|
4404
|
+
if (isProviderOutage(result.kind)) {
|
|
4405
|
+
const next2 = state.fallbackProviderIds[0];
|
|
4406
|
+
return next2 === void 0 ? { action: "fail" } : { action: "fallback", providerId: next2 };
|
|
4407
|
+
}
|
|
4408
|
+
const accessRefused = result.kind === "other" && isAccessRefusal(result.error);
|
|
4409
|
+
if (!TRANSIENT_KINDS.has(result.kind) && !accessRefused) return { action: "fail" };
|
|
4023
4410
|
if (result.contentSeen) {
|
|
4411
|
+
if (accessRefused) return { action: "fail" };
|
|
4024
4412
|
const auto = state.autoContinue;
|
|
4025
4413
|
if (!auto || auto.approvalRequested || auto.done >= AUTO_CONTINUES_PER_TURN) return { action: "fail" };
|
|
4026
4414
|
return { action: "auto-continue", pauseMs: autoContinuePauseMs({ index: auto.done, error: result.error, random: auto.random ?? Math.random }) };
|
|
@@ -4029,6 +4417,10 @@ function decideNextAttempt(result, state) {
|
|
|
4029
4417
|
if (state.fallbackUsed || next === void 0) return { action: "fail" };
|
|
4030
4418
|
return { action: "fallback", providerId: next };
|
|
4031
4419
|
}
|
|
4420
|
+
function errorKindForPerson(lastKind, firstOutage) {
|
|
4421
|
+
if (firstOutage === void 0 || TRANSIENT_KINDS.has(lastKind) || lastKind === "context_overflow") return lastKind;
|
|
4422
|
+
return firstOutage;
|
|
4423
|
+
}
|
|
4032
4424
|
function checkProviderFallback(setting, catalog) {
|
|
4033
4425
|
if (setting === void 0 || setting === false) return;
|
|
4034
4426
|
if (typeof setting !== "object" || setting === null || Array.isArray(setting)) {
|
|
@@ -5052,6 +5444,10 @@ function withTextAt(parts, index, text) {
|
|
|
5052
5444
|
function createShownSteps() {
|
|
5053
5445
|
let steps = 0;
|
|
5054
5446
|
let textSteps = [];
|
|
5447
|
+
const resetsToText = (fromEnd) => {
|
|
5448
|
+
const step = textSteps.at(-1 - fromEnd);
|
|
5449
|
+
return step === void 0 || step === 0 ? null : steps - step + 1;
|
|
5450
|
+
};
|
|
5055
5451
|
return {
|
|
5056
5452
|
observe(chunk) {
|
|
5057
5453
|
if (chunk.type === "start-step") steps += 1;
|
|
@@ -5062,10 +5458,9 @@ function createShownSteps() {
|
|
|
5062
5458
|
}
|
|
5063
5459
|
},
|
|
5064
5460
|
/** Сколько шагов стереть с конца, чтобы убрать шаг последнего текста; `null` — текста на экране нет. */
|
|
5065
|
-
resetsToLastText()
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
}
|
|
5461
|
+
resetsToLastText: () => resetsToText(0),
|
|
5462
|
+
/** То же для текста `fromEnd`-го с конца (0 — последний); `null` — такого текста на экране нет. */
|
|
5463
|
+
resetsToText
|
|
5069
5464
|
};
|
|
5070
5465
|
}
|
|
5071
5466
|
function replacementChunks(corrected, index, resets, providerId) {
|
|
@@ -5531,6 +5926,7 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
5531
5926
|
const externalContent = createExternalContentWatch();
|
|
5532
5927
|
const emitShown = (chunk) => {
|
|
5533
5928
|
shownSteps?.observe(chunk);
|
|
5929
|
+
screenSteps.observe(chunk);
|
|
5534
5930
|
emitRaw(chunk);
|
|
5535
5931
|
if (turnInput.liveDemos && externalContent.observe(chunk)) emitRaw(externalContentChunk());
|
|
5536
5932
|
};
|
|
@@ -5538,6 +5934,7 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
5538
5934
|
const seenTexts = replyChecks && createSeenTexts();
|
|
5539
5935
|
const heldText = replyChecks && (replyChecks.web === "hold" || (turnInput.channel ?? "web") !== "web") ? createHeldReplyText() : void 0;
|
|
5540
5936
|
const shownSteps = replyChecks && createShownSteps();
|
|
5937
|
+
const screenSteps = createShownSteps();
|
|
5541
5938
|
const emitToClient = (chunk) => {
|
|
5542
5939
|
for (const passed of heldText ? heldText.pass(chunk) : [chunk]) emitShown(passed);
|
|
5543
5940
|
};
|
|
@@ -5572,6 +5969,7 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
5572
5969
|
}
|
|
5573
5970
|
);
|
|
5574
5971
|
const turnUsage = createTurnUsage();
|
|
5972
|
+
const garbledTail = createGarbledTailGuard();
|
|
5575
5973
|
const turnBudget = createTurnBudget(input.turnRules, { catalog: input.catalog, modelId: input.modelId, turnUsage });
|
|
5576
5974
|
const messageUsage = () => {
|
|
5577
5975
|
const spent = turnUsage.total();
|
|
@@ -5660,13 +6058,21 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
5660
6058
|
const seen = latestFrozenContextOf(resume.message.parts);
|
|
5661
6059
|
if (seen) resumeNote = { ...resumeNote ?? { blocks: seen.blocks, sent: { changed: [], removed: [] }, base: messageId }, notices: turnNotices };
|
|
5662
6060
|
}
|
|
5663
|
-
const
|
|
6061
|
+
const outages = input.providerOutages ?? createProviderOutages({ now: () => (input.now ?? (() => /* @__PURE__ */ new Date()))().getTime(), logError });
|
|
6062
|
+
const allFallbackIds = fallbackProviders({
|
|
5664
6063
|
catalog: input.catalog,
|
|
5665
6064
|
modelId,
|
|
5666
6065
|
chosenProviderId: input.providerId,
|
|
5667
6066
|
env: input.env,
|
|
5668
6067
|
...input.providerFallback !== void 0 && { setting: input.providerFallback }
|
|
5669
6068
|
});
|
|
6069
|
+
const fallbackProviderIds = allFallbackIds.filter((candidate) => outages.pausedKind(candidate) === void 0);
|
|
6070
|
+
let firstOutage;
|
|
6071
|
+
const primaryPaused = outages.admit(input.providerId);
|
|
6072
|
+
if (primaryPaused !== void 0 && fallbackProviderIds[0] !== void 0) {
|
|
6073
|
+
providerId = fallbackProviderIds[0];
|
|
6074
|
+
logError(`[\u0437\u0430\u043F\u0430\u0441\u043D\u043E\u0439 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440] \u0447\u0430\u0442=${input.chatId} \u043C\u043E\u0434\u0435\u043B\u044C=${modelId} \u0441=${input.providerId} \u043D\u0430=${providerId} \u043F\u0440\u0438\u0447\u0438\u043D\u0430=${primaryPaused} \u043E\u0441\u043D\u043E\u0432\u043D\u043E\u0439 \u043D\u0430 \u043F\u0430\u0443\u0437\u0435`);
|
|
6075
|
+
}
|
|
5670
6076
|
emit({ type: "start", messageId, messageMetadata: { modelId, providerId: input.providerId } });
|
|
5671
6077
|
if (input.liveDemos && chatHasExternalContent(await storage.listMessages(input.chatId)) && externalContent.announce()) emitRaw(externalContentChunk());
|
|
5672
6078
|
const silenceSetting = resolveSilenceSwitch(input.providerFallback);
|
|
@@ -5739,6 +6145,7 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
5739
6145
|
emit,
|
|
5740
6146
|
turnUsage,
|
|
5741
6147
|
turnBudget,
|
|
6148
|
+
garbledTail,
|
|
5742
6149
|
// Есть куда перейти при молчании — у того же провайдера оно не повторяется.
|
|
5743
6150
|
repeatSilence: () => silenceTarget() === void 0,
|
|
5744
6151
|
...silenceSwitch.scope === "step" && switchedBySilence && {
|
|
@@ -5756,13 +6163,14 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
5756
6163
|
} catch (error2) {
|
|
5757
6164
|
if (error2 instanceof MissingProviderKeyError) logError(error2.message);
|
|
5758
6165
|
if (!input.abortSignal.aborted) {
|
|
5759
|
-
errorKind = classify(error2);
|
|
6166
|
+
errorKind = errorKindForPerson(classify(error2), firstOutage);
|
|
5760
6167
|
errors = [{ type: "error", errorText: errorKind }];
|
|
5761
6168
|
}
|
|
5762
6169
|
break;
|
|
5763
6170
|
}
|
|
5764
6171
|
const { result } = attempt;
|
|
5765
6172
|
historyRewritten = false;
|
|
6173
|
+
if (result.outcome === "ok") outages.recovered(providerId);
|
|
5766
6174
|
if (result.outcome !== "failed" || result.contentSeen) answeredProviderId = providerId;
|
|
5767
6175
|
if (result.outcome === "ok" && backToPrimary) {
|
|
5768
6176
|
backToPrimary = false;
|
|
@@ -5797,12 +6205,24 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
5797
6205
|
silenceSwitch: { done: silenceSwitches, max: silenceSwitch.maxSwitches, nextProviderId: silenceTarget() }
|
|
5798
6206
|
});
|
|
5799
6207
|
let next = stepsDone < maxSteps ? decideNextAttempt(result, nextState()) : { action: "fail" };
|
|
6208
|
+
const outage = isProviderOutage(result.kind) ? result.kind : void 0;
|
|
6209
|
+
if (outage) {
|
|
6210
|
+
if (providerId === input.providerId) firstOutage ??= outage;
|
|
6211
|
+
outages.report({
|
|
6212
|
+
providerId,
|
|
6213
|
+
kind: outage,
|
|
6214
|
+
...next.action === "fallback" ? { fallbackProviderId: next.providerId } : { fallbacksPaused: allFallbackIds.some((candidate) => candidate !== providerId && outages.pausedKind(candidate) !== void 0) }
|
|
6215
|
+
});
|
|
6216
|
+
}
|
|
5800
6217
|
if (next.action === "fallback") {
|
|
5801
6218
|
logError(`[\u0437\u0430\u043F\u0430\u0441\u043D\u043E\u0439 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440] \u0447\u0430\u0442=${input.chatId} \u043C\u043E\u0434\u0435\u043B\u044C=${modelId} \u0441=${providerId} \u043D\u0430=${next.providerId} \u043F\u0440\u0438\u0447\u0438\u043D\u0430=${result.kind} \u043A\u044D\u0448 \u043F\u043E\u0442\u0435\u0440\u044F\u043D`);
|
|
5802
|
-
fallbackUsed = true;
|
|
6219
|
+
if (!outage) fallbackUsed = true;
|
|
5803
6220
|
switchedBySilence = false;
|
|
5804
6221
|
providerId = next.providerId;
|
|
5805
|
-
|
|
6222
|
+
const closing2 = result.contentSeen ? [...openParts.closingChunks(), ...closingChunksForHangingCalls(parts2)] : [];
|
|
6223
|
+
for (const chunk of closing2) emit(chunk);
|
|
6224
|
+
if (closing2.length > 0) answer.continueAfterBreak(parts2, { modelId, providerId: answeredProviderId });
|
|
6225
|
+
else answer.update(parts2, { modelId, providerId: answeredProviderId });
|
|
5806
6226
|
turnUsage.closeAttempt();
|
|
5807
6227
|
if (input.abortSignal.aborted) break;
|
|
5808
6228
|
continue;
|
|
@@ -5874,8 +6294,8 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
5874
6294
|
turnUsage.closeAttempt();
|
|
5875
6295
|
continue;
|
|
5876
6296
|
}
|
|
5877
|
-
errorKind = result.kind;
|
|
5878
|
-
errors = attempt.errors;
|
|
6297
|
+
errorKind = errorKindForPerson(result.kind, firstOutage);
|
|
6298
|
+
errors = errorKind === result.kind ? attempt.errors : [{ type: "error", errorText: errorKind }];
|
|
5879
6299
|
terminal = attempt.terminal;
|
|
5880
6300
|
logError(
|
|
5881
6301
|
describeProviderFailure(result.error, { chatId: input.chatId, modelId, providerId, kind: result.kind, env: input.env, catalog: input.catalog })
|
|
@@ -5942,6 +6362,15 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
5942
6362
|
const failed = input.abortSignal.aborted || errors.length > 0;
|
|
5943
6363
|
const closing = !failed && pendingHumanInput(savedParts, input.waitingWidgets).length > 0 && (closure !== void 0 || await chatClosedNow());
|
|
5944
6364
|
let parts = failed || closing ? closeHangingToolCalls(denyPendingApprovals(savedParts, INTERRUPTED_REASON, RESULT_UNKNOWN_REASON), { notStarted: toolQueue.notStarted }) : savedParts;
|
|
6365
|
+
const garbled = cutGarbledTail(garbledTail.forCut(parts));
|
|
6366
|
+
const droppedCalls = garbledTail.droppedCalls();
|
|
6367
|
+
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}`;
|
|
6368
|
+
if (garbled) {
|
|
6369
|
+
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
|
+
const cut = garbled.parts.filter((part) => !garbledTail.isDroppedCall(part));
|
|
6371
|
+
if (!takeover.signal.aborted) showCutAnswer(parts, cut, garbled.textIndex, { heldText, screenSteps, providerId: answeredProviderId, emit: emitShown });
|
|
6372
|
+
parts = cut;
|
|
6373
|
+
} else if (findGarbledTail(garbledTail.forCut(parts))) logError(`[\u0445\u0432\u043E\u0441\u0442 \u043D\u0435 \u043E\u0431\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] ${garbleLog}`);
|
|
5945
6374
|
const status = input.abortSignal.aborted ? "stopped" : errors.length > 0 ? "error" : pendingHumanInput(parts, input.waitingWidgets).length > 0 ? "awaiting_approval" : "complete";
|
|
5946
6375
|
if (endTurns.hasText()) logError(`[endTurn] \u0447\u0430\u0442=${input.chatId} \u0433\u043E\u0442\u043E\u0432\u044B\u0439 \u0442\u0435\u043A\u0441\u0442 \u043D\u0435 \u043F\u043E\u043A\u0430\u0437\u0430\u043D: \u0445\u043E\u0434 \u043A\u043E\u043D\u0447\u0438\u043B\u0441\u044F \u0441\u043E \u0441\u0442\u0430\u0442\u0443\u0441\u043E\u043C ${status}`);
|
|
5947
6376
|
const textIndex = lastTextIndex(parts);
|
|
@@ -6255,8 +6684,10 @@ async function openAttempt(input, attempt, attemptSignal) {
|
|
|
6255
6684
|
return true;
|
|
6256
6685
|
};
|
|
6257
6686
|
const { noteAfterApprovals, ...modelRequest } = request;
|
|
6687
|
+
attempt.garbledTail.reset();
|
|
6258
6688
|
const result = streamText({
|
|
6259
6689
|
...modelRequest,
|
|
6690
|
+
model: attempt.garbledTail.wrap(modelRequest.model),
|
|
6260
6691
|
...approval.toolApproval && { toolApproval: gateToolApproval(approval.toolApproval, () => toolsClosed) },
|
|
6261
6692
|
// Попытке — остаток шагов хода.
|
|
6262
6693
|
// Помощник встал на подтверждении человека — ход ждёт, модель основного агента его незаконченным не видит.
|
|
@@ -6264,6 +6695,7 @@ async function openAttempt(input, attempt, attemptSignal) {
|
|
|
6264
6695
|
// Инструмент кончил ход: без текста — стоп сразу; с текстом — ещё шаг подставной модели, его держит только помощник
|
|
6265
6696
|
// на подтверждении (ход ждёт человека). Потолок шагов и прочие стопы готовый текст не теряют.
|
|
6266
6697
|
stopWhen: async (options2) => {
|
|
6698
|
+
if (attempt.garbledTail.garbled()) return true;
|
|
6267
6699
|
if (attempt.endTurns.requested()) {
|
|
6268
6700
|
if (attempt.endTurns.hasText()) return helperAwaitsApproval(options2);
|
|
6269
6701
|
attempt.endTurns.stopWithoutText();
|
|
@@ -6475,6 +6907,7 @@ function helperRunContext(input, toolQueue, logError) {
|
|
|
6475
6907
|
...input.toolTimeout && { toolTimeout: input.toolTimeout },
|
|
6476
6908
|
...input.sequentialTools && { sequentialTools: input.sequentialTools },
|
|
6477
6909
|
...input.providerFallback !== void 0 && { providerFallback: input.providerFallback },
|
|
6910
|
+
...input.providerOutages && { providerOutages: input.providerOutages },
|
|
6478
6911
|
...input.modelSilence && { modelSilence: input.modelSilence },
|
|
6479
6912
|
...input.turnObserver && { turnObserver: input.turnObserver },
|
|
6480
6913
|
...input.assertWithinLimit && { assertWithinLimit: input.assertWithinLimit }
|
|
@@ -6599,6 +7032,19 @@ function withLastUserPart(history, part) {
|
|
|
6599
7032
|
return history.map((message, position) => position === index ? { ...message, parts: [...message.parts, part] } : message);
|
|
6600
7033
|
}
|
|
6601
7034
|
var FINISH_RETRY_PAUSES_MS = [1e3, 3e3];
|
|
7035
|
+
function showCutAnswer(original, cut, anchor, screen) {
|
|
7036
|
+
if (anchor === -1) return;
|
|
7037
|
+
const textsAfter = original.slice(anchor + 1).filter((part) => part.type === "text").length;
|
|
7038
|
+
if (screen.heldText?.holdsText()) {
|
|
7039
|
+
if (textsAfter === 0 && lastTextIndex(cut) === anchor) {
|
|
7040
|
+
for (const chunk of screen.heldText.release(cut[anchor].text)) screen.emit(chunk);
|
|
7041
|
+
return;
|
|
7042
|
+
}
|
|
7043
|
+
for (const chunk of screen.heldText.release()) screen.emit(chunk);
|
|
7044
|
+
}
|
|
7045
|
+
const resets = screen.screenSteps.resetsToText(textsAfter);
|
|
7046
|
+
for (const chunk of resets && replacementChunks(cut, anchor, resets, screen.providerId) || []) screen.emit(chunk);
|
|
7047
|
+
}
|
|
6602
7048
|
async function finishWithRetries(storage, messageId, final, logError) {
|
|
6603
7049
|
for (let attempt = 0; ; attempt += 1) {
|
|
6604
7050
|
let failure;
|
|
@@ -6692,16 +7138,26 @@ async function* helperSteps(input) {
|
|
|
6692
7138
|
const messages = [...input.priorMessages ?? []];
|
|
6693
7139
|
const maxSteps = helper.maxSteps ?? DEFAULT_HELPER_MAX_STEPS;
|
|
6694
7140
|
const { modelId } = helper.model;
|
|
6695
|
-
const
|
|
7141
|
+
const outages = context.providerOutages ?? createProviderOutages({ now: Date.now, logError: context.logError });
|
|
7142
|
+
const allFallbackIds = fallbackProviders({
|
|
6696
7143
|
catalog: context.catalog,
|
|
6697
7144
|
modelId,
|
|
6698
7145
|
chosenProviderId: helper.model.providerId,
|
|
6699
7146
|
env: context.env,
|
|
6700
7147
|
...context.providerFallback !== void 0 && { setting: context.providerFallback }
|
|
6701
7148
|
});
|
|
7149
|
+
const fallbackProviderIds = allFallbackIds.filter((candidate) => outages.pausedKind(candidate) === void 0);
|
|
6702
7150
|
const toolQueue = context.toolQueue ?? createToolQueue();
|
|
6703
7151
|
let providerId = helper.model.providerId;
|
|
6704
7152
|
let fallbackUsed = false;
|
|
7153
|
+
let firstOutage;
|
|
7154
|
+
const primaryPaused = outages.admit(providerId);
|
|
7155
|
+
if (primaryPaused !== void 0 && fallbackProviderIds[0] !== void 0) {
|
|
7156
|
+
context.logError(
|
|
7157
|
+
`[\u0437\u0430\u043F\u0430\u0441\u043D\u043E\u0439 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440] \u0447\u0430\u0442=${context.chatId} \u043F\u043E\u043C\u043E\u0449\u043D\u0438\u043A=${input.name} \u043C\u043E\u0434\u0435\u043B\u044C=${modelId} \u0441=${providerId} \u043D\u0430=${fallbackProviderIds[0]} \u043F\u0440\u0438\u0447\u0438\u043D\u0430=${primaryPaused} \u043E\u0441\u043D\u043E\u0432\u043D\u043E\u0439 \u043D\u0430 \u043F\u0430\u0443\u0437\u0435`
|
|
7158
|
+
);
|
|
7159
|
+
providerId = fallbackProviderIds[0];
|
|
7160
|
+
}
|
|
6705
7161
|
let externalContent = false;
|
|
6706
7162
|
const report = (status, extra = {}) => ({
|
|
6707
7163
|
messages: [...messages],
|
|
@@ -6728,17 +7184,28 @@ async function* helperSteps(input) {
|
|
|
6728
7184
|
} catch (error) {
|
|
6729
7185
|
if (abortSignal.aborted) throw error;
|
|
6730
7186
|
const kind = classifyProviderError(error, provider);
|
|
6731
|
-
const
|
|
7187
|
+
const candidates = providerId === helper.model.providerId ? fallbackProviderIds : fallbackProviderIds.slice(fallbackProviderIds.indexOf(providerId) + 1);
|
|
7188
|
+
const next = decideNextAttempt({ outcome: "failed", kind, error, contentSeen: false }, { fallbackUsed, fallbackProviderIds: candidates });
|
|
7189
|
+
const outage = isProviderOutage(kind) ? kind : void 0;
|
|
7190
|
+
if (outage) {
|
|
7191
|
+
if (providerId === helper.model.providerId) firstOutage ??= outage;
|
|
7192
|
+
outages.report({
|
|
7193
|
+
providerId,
|
|
7194
|
+
kind: outage,
|
|
7195
|
+
...next.action === "fallback" ? { fallbackProviderId: next.providerId } : { fallbacksPaused: allFallbackIds.some((candidate) => candidate !== providerId && outages.pausedKind(candidate) !== void 0) }
|
|
7196
|
+
});
|
|
7197
|
+
}
|
|
6732
7198
|
if (next.action === "fallback") {
|
|
6733
7199
|
context.logError(`[\u0437\u0430\u043F\u0430\u0441\u043D\u043E\u0439 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440] \u0447\u0430\u0442=${context.chatId} \u043F\u043E\u043C\u043E\u0449\u043D\u0438\u043A=${input.name} \u043C\u043E\u0434\u0435\u043B\u044C=${modelId} \u0441=${providerId} \u043D\u0430=${next.providerId} \u043F\u0440\u0438\u0447\u0438\u043D\u0430=${kind}`);
|
|
6734
|
-
fallbackUsed = true;
|
|
7200
|
+
if (!outage) fallbackUsed = true;
|
|
6735
7201
|
providerId = next.providerId;
|
|
6736
7202
|
continue;
|
|
6737
7203
|
}
|
|
6738
7204
|
context.logError(describeProviderFailure(error, { chatId: context.chatId, modelId, providerId, kind, env: context.env, catalog: context.catalog }));
|
|
6739
|
-
yield report("failed", { error: FAILURE_REASONS[kind] });
|
|
7205
|
+
yield report("failed", { error: FAILURE_REASONS[errorKindForPerson(kind, firstOutage)] });
|
|
6740
7206
|
return;
|
|
6741
7207
|
}
|
|
7208
|
+
outages.recovered(providerId);
|
|
6742
7209
|
messages.push(...response);
|
|
6743
7210
|
externalContent ||= callsExternalContentTool(messages, tools);
|
|
6744
7211
|
const pendingApproval = pendingApprovalOf2(messages, tools);
|
|
@@ -6951,6 +7418,7 @@ function createChatEvents(logError = () => {
|
|
|
6951
7418
|
listeners.delete(listener);
|
|
6952
7419
|
};
|
|
6953
7420
|
},
|
|
7421
|
+
hasListeners: () => listeners.size > 0,
|
|
6954
7422
|
emit(event) {
|
|
6955
7423
|
for (const listener of [...listeners]) {
|
|
6956
7424
|
try {
|
|
@@ -7033,9 +7501,9 @@ function editAnswerParts(parts, edit, uneditable) {
|
|
|
7033
7501
|
return { ok: true, parts: textPlaced ? edited : [...edited, textPart] };
|
|
7034
7502
|
}
|
|
7035
7503
|
function toolCallIdOf(part) {
|
|
7036
|
-
return
|
|
7504
|
+
return isToolPart7(part) ? part.toolCallId : void 0;
|
|
7037
7505
|
}
|
|
7038
|
-
var
|
|
7506
|
+
var isToolPart7 = (part) => part.type.startsWith("tool-") || part.type === "dynamic-tool";
|
|
7039
7507
|
function toolNameOf2(part) {
|
|
7040
7508
|
return part.type === "dynamic-tool" ? part.toolName : part.type.slice("tool-".length);
|
|
7041
7509
|
}
|
|
@@ -7329,7 +7797,7 @@ var fireworksProvider = createFireworksProvider();
|
|
|
7329
7797
|
|
|
7330
7798
|
// src/models/providers/openrouter.ts
|
|
7331
7799
|
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
|
|
7332
|
-
import { wrapLanguageModel as
|
|
7800
|
+
import { wrapLanguageModel as wrapLanguageModel5 } from "ai";
|
|
7333
7801
|
|
|
7334
7802
|
// src/models/providers/openrouter-cache-mark-limit.ts
|
|
7335
7803
|
function openrouterCacheMarkLimit(providerModelId) {
|
|
@@ -7579,7 +8047,7 @@ function createOpenRouterProvider(options2 = {}) {
|
|
|
7579
8047
|
createLanguageModel: (providerModelId, apiKey, requestOptions) => {
|
|
7580
8048
|
const markLimit = openrouterCacheMarkLimit(providerModelId);
|
|
7581
8049
|
const fetch = markLimit === void 0 ? requestOptions?.fetch : fetchWithCacheMarkLimit(requestOptions?.fetch, markLimit);
|
|
7582
|
-
return
|
|
8050
|
+
return wrapLanguageModel5({
|
|
7583
8051
|
model: createOpenRouter({ apiKey, fetch, ...connection }).chat(providerModelId, routingOptions),
|
|
7584
8052
|
middleware: [
|
|
7585
8053
|
openrouterRoutingMiddleware(routing),
|
|
@@ -9481,7 +9949,7 @@ function isPlainObject3(value) {
|
|
|
9481
9949
|
}
|
|
9482
9950
|
|
|
9483
9951
|
// src/memory/memory-after-answer.ts
|
|
9484
|
-
import { wrapLanguageModel as
|
|
9952
|
+
import { wrapLanguageModel as wrapLanguageModel6 } from "ai";
|
|
9485
9953
|
var DRAIN_WAIT_MS = 5e3;
|
|
9486
9954
|
function createMemoryAfterAnswer({ memory, storage, catalog, env, logError, turnObserver }) {
|
|
9487
9955
|
const running = /* @__PURE__ */ new Set();
|
|
@@ -9526,7 +9994,7 @@ function createMemoryAfterAnswer({ memory, storage, catalog, env, logError, turn
|
|
|
9526
9994
|
};
|
|
9527
9995
|
}
|
|
9528
9996
|
function withRetries(model, context) {
|
|
9529
|
-
return typeof model === "string" ? model :
|
|
9997
|
+
return typeof model === "string" ? model : wrapLanguageModel6({ model, middleware: createTransientRetryMiddleware(context) });
|
|
9530
9998
|
}
|
|
9531
9999
|
|
|
9532
10000
|
// src/conversation/to-agent-chat-message.ts
|
|
@@ -10248,6 +10716,7 @@ function createTurnGate(hooks) {
|
|
|
10248
10716
|
...input.text !== void 0 && { text: input.text },
|
|
10249
10717
|
...input.messageId !== void 0 && { messageId: input.messageId },
|
|
10250
10718
|
...input.burstMessageIds && { burstMessageIds: [...input.burstMessageIds] },
|
|
10719
|
+
...input.unattended && { unattended: true },
|
|
10251
10720
|
...abandonedOf(input)
|
|
10252
10721
|
});
|
|
10253
10722
|
if (!decision) return TURN;
|
|
@@ -10280,6 +10749,7 @@ function createTurnGate(hooks) {
|
|
|
10280
10749
|
...input.messageId !== void 0 && { messageId: input.messageId },
|
|
10281
10750
|
...input.text !== void 0 && { text: input.text },
|
|
10282
10751
|
...input.burstMessageIds && { burstMessageIds: [...input.burstMessageIds] },
|
|
10752
|
+
...input.unattended && { unattended: true },
|
|
10283
10753
|
reason,
|
|
10284
10754
|
...abandonedOf(input)
|
|
10285
10755
|
});
|
|
@@ -10670,6 +11140,8 @@ var InvalidWidgetAnswerError = class extends Error {
|
|
|
10670
11140
|
this.name = "InvalidWidgetAnswerError";
|
|
10671
11141
|
}
|
|
10672
11142
|
};
|
|
11143
|
+
var FAR_FUTURE_ISO = "9999-12-31T23:59:59.999Z";
|
|
11144
|
+
var abandonedBurstReceivers = /* @__PURE__ */ new WeakMap();
|
|
10673
11145
|
var TurnEndedBeforeSubscribeError = class extends Error {
|
|
10674
11146
|
constructor(chatId) {
|
|
10675
11147
|
super(`\u0425\u043E\u0434 \u0432 \u0447\u0430\u0442\u0435 ${chatId} \u0437\u0430\u043A\u043E\u043D\u0447\u0438\u043B\u0441\u044F \u0434\u043E \u043F\u043E\u0434\u043F\u0438\u0441\u043A\u0438`);
|
|
@@ -10867,6 +11339,7 @@ function createChatService(input) {
|
|
|
10867
11339
|
const modelSilence = resolveModelSilence(modelSilenceTimeout);
|
|
10868
11340
|
const cacheMissWatch = cacheMissLog === false ? void 0 : createCacheMissWatch({ logError });
|
|
10869
11341
|
checkProviderFallback(providerFallback, options2.catalog);
|
|
11342
|
+
const providerOutages = createProviderOutages({ now: () => now().getTime(), logError });
|
|
10870
11343
|
const placeMaxLength = resolvePlaceMaxLength(placeMaxLengthSetting);
|
|
10871
11344
|
const browserToolTimeoutMs = resolveBrowserToolTimeoutMs(browserToolTimeoutSetting);
|
|
10872
11345
|
const pendingBrowserCalls = createPendingBrowserCalls();
|
|
@@ -10913,7 +11386,9 @@ function createChatService(input) {
|
|
|
10913
11386
|
const steersInFlight = /* @__PURE__ */ new Map();
|
|
10914
11387
|
const startingByChat = /* @__PURE__ */ new Map();
|
|
10915
11388
|
const burstWaits = createBurstWaits();
|
|
10916
|
-
const
|
|
11389
|
+
const copyId = crypto.randomUUID();
|
|
11390
|
+
const turnLeases = createChatTurnLeases({ storage, holder: copyId, logError });
|
|
11391
|
+
const burstSweeps = /* @__PURE__ */ new Set();
|
|
10917
11392
|
const burstWrites = /* @__PURE__ */ new Map();
|
|
10918
11393
|
const chatToolQueues = /* @__PURE__ */ new Map();
|
|
10919
11394
|
let staleSweep = null;
|
|
@@ -11540,6 +12015,7 @@ function createChatService(input) {
|
|
|
11540
12015
|
modelSilence,
|
|
11541
12016
|
...limits.maxOutputTokens !== void 0 && { maxOutputTokens: limits.maxOutputTokens },
|
|
11542
12017
|
...providerFallback !== void 0 && { providerFallback },
|
|
12018
|
+
providerOutages,
|
|
11543
12019
|
...cacheMissWatch && { cacheMissWatch },
|
|
11544
12020
|
...options2.turnObserver && { turnObserver: options2.turnObserver },
|
|
11545
12021
|
...options2.limits && { assertWithinLimit: () => assertWithinSpendingLimit(chat.ownerId, chatId) },
|
|
@@ -11661,7 +12137,15 @@ function createChatService(input) {
|
|
|
11661
12137
|
steeringTurn = steering.begin(chatId, answerId);
|
|
11662
12138
|
if (stopsWhileStarting.delete(chatId)) registry.abort(chatId);
|
|
11663
12139
|
const turnEvent = { ownerId: chat.ownerId, chatId, messageId: answerId, trigger, channel };
|
|
11664
|
-
chatEvents.emit({
|
|
12140
|
+
chatEvents.emit({
|
|
12141
|
+
type: "turn-started",
|
|
12142
|
+
...turnEvent,
|
|
12143
|
+
...launch.deliveredByApp && { deliveredByApp: true },
|
|
12144
|
+
...launch.unattended && { unattended: true }
|
|
12145
|
+
});
|
|
12146
|
+
if (launch.unattended && chatEvents.hasListeners?.() === false) {
|
|
12147
|
+
logError(`\u0425\u043E\u0434 \u0431\u0435\u0437 \u0432\u044B\u0437\u043E\u0432\u0430 \u043D\u0430 \u043F\u0430\u0447\u043A\u0443 \u0447\u0430\u0442\u0430 ${chatId}: \u043D\u0430 \u0441\u043E\u0431\u044B\u0442\u0438\u044F \u0447\u0430\u0442\u0430 \u043D\u0438\u043A\u0442\u043E \u043D\u0435 \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u043D \u2014 \u043E\u0442\u0432\u0435\u0442 \u0431\u0443\u0434\u0435\u0442 \u0442\u043E\u043B\u044C\u043A\u043E \u0432 \u0438\u0441\u0442\u043E\u0440\u0438\u0438`);
|
|
12148
|
+
}
|
|
11665
12149
|
launch.onStarted?.({ answerId, finished });
|
|
11666
12150
|
liveAnswerIds.add(answerId);
|
|
11667
12151
|
const slot = startingSlots.get(chatId);
|
|
@@ -11779,6 +12263,7 @@ function createChatService(input) {
|
|
|
11779
12263
|
await assertStillOpen(chatId);
|
|
11780
12264
|
const earlier = await storage.getMessage(message.id);
|
|
11781
12265
|
if (earlier?.chatId === chatId && earlier.role === "user") return earlier;
|
|
12266
|
+
if (sendOptions?.answerAfterRestart) await savingPendingBurst(userId, chatId, message.id, now().getTime() + quietMs, sendOptions.page);
|
|
11782
12267
|
const written = await writingHumanMessage(userId, chatId, await currentBranch(chatId), message, text, files, sendOptions, { quietMs });
|
|
11783
12268
|
chatEvents.emit({ type: "message-added", ownerId: userId, chatId, messageId: written.id });
|
|
11784
12269
|
return written;
|
|
@@ -11797,11 +12282,11 @@ function createChatService(input) {
|
|
|
11797
12282
|
return true;
|
|
11798
12283
|
};
|
|
11799
12284
|
}
|
|
11800
|
-
async function answeringBurst(userId, chatId,
|
|
12285
|
+
async function answeringBurst(userId, chatId, questionId, page, unattended = false) {
|
|
11801
12286
|
const otherCopy = waitingForOtherCopy(chatId);
|
|
11802
12287
|
for (; ; ) {
|
|
11803
12288
|
if (closing) throw new ChatBusyError(chatId);
|
|
11804
|
-
const verdict = burstVerdict(await currentBranch(chatId),
|
|
12289
|
+
const verdict = burstVerdict(await currentBranch(chatId), questionId, now().getTime());
|
|
11805
12290
|
if (verdict.kind === "skip") return null;
|
|
11806
12291
|
if (verdict.kind === "wait") {
|
|
11807
12292
|
if (closing) throw new ChatBusyError(chatId);
|
|
@@ -11818,16 +12303,20 @@ function createChatService(input) {
|
|
|
11818
12303
|
const burstMessageIds = verdict.burst.map((message) => message.id);
|
|
11819
12304
|
try {
|
|
11820
12305
|
const stream = await startingTurn(
|
|
11821
|
-
{ userId, chat, action: "send", text: burstText(verdict.burst), messageId:
|
|
12306
|
+
{ userId, chat, action: "send", text: burstText(verdict.burst), messageId: questionId, burstMessageIds, ...unattended && { unattended: true } },
|
|
11822
12307
|
async (pass) => {
|
|
11823
12308
|
const branch = await currentBranch(chatId);
|
|
11824
|
-
if (burstVerdict(branch,
|
|
11825
|
-
if (pass.kind === "hold") return heldTurn(userId, chatId,
|
|
12309
|
+
if (burstVerdict(branch, questionId, now().getTime()).kind !== "start") return null;
|
|
12310
|
+
if (pass.kind === "hold") return unattended ? (heldTurn(userId, chatId, questionId), "unattended") : heldTurn(userId, chatId, questionId);
|
|
11826
12311
|
takeTurnSlot(userId, chatId);
|
|
11827
|
-
return startAnswer("send", userId, chatId, branch,
|
|
12312
|
+
if (!unattended) return startAnswer("send", userId, chatId, branch, page, void 0, void 0, replyOf(pass));
|
|
12313
|
+
const reply = replyOf(pass);
|
|
12314
|
+
await launchTurn(userId, chatId, branch, { parentId: branch.at(-1).id, page, action: "send", detached: true, unattended: true, ...reply !== void 0 && { reply } });
|
|
12315
|
+
return "unattended";
|
|
11828
12316
|
},
|
|
11829
12317
|
(result) => result === null ? "busy" : void 0
|
|
11830
12318
|
);
|
|
12319
|
+
if (stream === "unattended") return null;
|
|
11831
12320
|
return stream && viewerStream(userId, chat, {}, async () => stream);
|
|
11832
12321
|
} catch (error) {
|
|
11833
12322
|
if (error instanceof ChatBusyError && !closing && localTurnOf(chatId)) continue;
|
|
@@ -11836,6 +12325,115 @@ function createChatService(input) {
|
|
|
11836
12325
|
}
|
|
11837
12326
|
}
|
|
11838
12327
|
}
|
|
12328
|
+
const keepsPendingBursts = Boolean(storage.savePendingBurst && storage.takeAbandonedBursts && storage.renewPendingBurst && storage.removePendingBurst);
|
|
12329
|
+
async function savingPendingBurst(ownerId, chatId, messageId, dueAt, page) {
|
|
12330
|
+
if (!keepsPendingBursts) return;
|
|
12331
|
+
await storage.savePendingBurst({
|
|
12332
|
+
chatId,
|
|
12333
|
+
ownerId,
|
|
12334
|
+
messageId,
|
|
12335
|
+
dueAt: new Date(dueAt).toISOString(),
|
|
12336
|
+
holder: copyId,
|
|
12337
|
+
heldUntil: new Date(dueAt + BURST_HOLD_MS).toISOString(),
|
|
12338
|
+
...page && { page }
|
|
12339
|
+
});
|
|
12340
|
+
}
|
|
12341
|
+
function renewingBurstHold(chatId, messageId, dueAt, holder) {
|
|
12342
|
+
const timer = setInterval(() => {
|
|
12343
|
+
const heldUntil = new Date(Math.max(now().getTime(), dueAt) + BURST_HOLD_MS).toISOString();
|
|
12344
|
+
storage.renewPendingBurst({ chatId, messageId, holder, heldUntil }).then(
|
|
12345
|
+
(held) => {
|
|
12346
|
+
if (!held) clearInterval(timer);
|
|
12347
|
+
},
|
|
12348
|
+
(error) => logError(`\u0423\u0434\u0435\u0440\u0436\u0430\u043D\u0438\u0435 \u043F\u0430\u0447\u043A\u0438 \u0447\u0430\u0442\u0430 ${chatId} \u043D\u0435 \u043F\u0440\u043E\u0434\u043B\u0438\u043B\u043E\u0441\u044C: ${error instanceof Error ? error.message : String(error)}`)
|
|
12349
|
+
);
|
|
12350
|
+
}, BURST_HOLD_RENEW_MS);
|
|
12351
|
+
timer.unref?.();
|
|
12352
|
+
return () => clearInterval(timer);
|
|
12353
|
+
}
|
|
12354
|
+
const burstWaitsForNextSweep = (error) => error instanceof ChatBusyError || error instanceof TooManyTurnsError;
|
|
12355
|
+
function answeringRecordedBurst(ownerId, chatId, questionId, page, burst) {
|
|
12356
|
+
const unattended = burst.kept && burst.unattended === true;
|
|
12357
|
+
if (!burst.kept || !keepsPendingBursts) return burstWaits.holding(chatId, () => answeringBurst(ownerId, chatId, questionId, page, unattended));
|
|
12358
|
+
const stopRenewing = renewingBurstHold(chatId, questionId, burst.dueAt, burst.holder ?? copyId);
|
|
12359
|
+
const answering = burstWaits.holding(chatId, () => answeringBurst(ownerId, chatId, questionId, page, unattended)).finally(stopRenewing);
|
|
12360
|
+
void answering.then(
|
|
12361
|
+
() => true,
|
|
12362
|
+
(error) => !burstWaitsForNextSweep(error)
|
|
12363
|
+
).then((decided) => {
|
|
12364
|
+
if (decided)
|
|
12365
|
+
return storage.removePendingBurst(chatId, questionId).catch((error) => logError(`\u0417\u0430\u043F\u0438\u0441\u044C \u043F\u0430\u0447\u043A\u0438 \u0447\u0430\u0442\u0430 ${chatId} \u043D\u0435 \u0441\u043D\u044F\u043B\u0430\u0441\u044C: ${error instanceof Error ? error.message : String(error)}`));
|
|
12366
|
+
});
|
|
12367
|
+
return answering;
|
|
12368
|
+
}
|
|
12369
|
+
let burstDecisions = 0;
|
|
12370
|
+
const burstDecisionWaiters = /* @__PURE__ */ new Set();
|
|
12371
|
+
const burstDecisionFreed = () => new Promise((resolve) => burstDecisionWaiters.add(resolve));
|
|
12372
|
+
function releasingBurstDecision() {
|
|
12373
|
+
burstDecisions--;
|
|
12374
|
+
for (const waiter of [...burstDecisionWaiters]) {
|
|
12375
|
+
burstDecisionWaiters.delete(waiter);
|
|
12376
|
+
waiter();
|
|
12377
|
+
}
|
|
12378
|
+
}
|
|
12379
|
+
function reportingAbandonedBurst(chatId, error) {
|
|
12380
|
+
if (burstWaitsForNextSweep(error)) return;
|
|
12381
|
+
const why = error instanceof TurnRefusedError ? `\u043E\u0442\u043A\u0430\u0437 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F ${error.code}: ${error.message}` : error instanceof Error ? error.message : String(error);
|
|
12382
|
+
logError(`\u0425\u043E\u0434 \u043D\u0430 \u0431\u0440\u043E\u0448\u0435\u043D\u043D\u0443\u044E \u043F\u0430\u0447\u043A\u0443 \u0447\u0430\u0442\u0430 ${chatId} \u043D\u0435 \u043D\u0430\u0447\u0430\u043B\u0441\u044F: ${why}`);
|
|
12383
|
+
}
|
|
12384
|
+
async function receivingAbandonedBurst(burst) {
|
|
12385
|
+
if (burstWaits.has(burst.chatId)) return;
|
|
12386
|
+
await answeringRecordedBurst(burst.ownerId, burst.chatId, burst.messageId, burst.page, { kept: true, dueAt: Date.parse(burst.dueAt), unattended: true, holder: burst.holder }).then(
|
|
12387
|
+
() => {
|
|
12388
|
+
},
|
|
12389
|
+
(error) => reportingAbandonedBurst(burst.chatId, error)
|
|
12390
|
+
);
|
|
12391
|
+
}
|
|
12392
|
+
async function routingAbandonedBurst(burst) {
|
|
12393
|
+
const serviceFor = options2.abandonedBursts?.serviceFor;
|
|
12394
|
+
if (!serviceFor) return receivingAbandonedBurst(burst);
|
|
12395
|
+
let target;
|
|
12396
|
+
try {
|
|
12397
|
+
const chat = await storage.getChat(burst.chatId);
|
|
12398
|
+
if (!chat) return receivingAbandonedBurst(burst);
|
|
12399
|
+
target = await serviceFor({ ownerId: burst.ownerId, chatId: burst.chatId, chat });
|
|
12400
|
+
} catch (error) {
|
|
12401
|
+
logError(`\u0421\u043B\u0443\u0436\u0431\u0430 \u0434\u043B\u044F \u0431\u0440\u043E\u0448\u0435\u043D\u043D\u043E\u0439 \u043F\u0430\u0447\u043A\u0438 \u0447\u0430\u0442\u0430 ${burst.chatId} \u043D\u0435 \u043D\u0430\u0448\u043B\u0430\u0441\u044C (serviceFor): ${error instanceof Error ? error.message : String(error)} \u2014 \u043F\u0430\u0447\u043A\u0430 \u0436\u0434\u0451\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0433\u043E \u043E\u0431\u0445\u043E\u0434\u0430`);
|
|
12402
|
+
return;
|
|
12403
|
+
}
|
|
12404
|
+
if (!target) {
|
|
12405
|
+
logError(`\u0414\u043B\u044F \u0431\u0440\u043E\u0448\u0435\u043D\u043D\u043E\u0439 \u043F\u0430\u0447\u043A\u0438 \u0447\u0430\u0442\u0430 ${burst.chatId} \u043D\u0435\u0442 \u0441\u043B\u0443\u0436\u0431\u044B (serviceFor \u2192 ${String(target)}): \u043F\u0430\u0447\u043A\u0430 \u0436\u0434\u0451\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0433\u043E \u043E\u0431\u0445\u043E\u0434\u0430`);
|
|
12406
|
+
return;
|
|
12407
|
+
}
|
|
12408
|
+
const receive = abandonedBurstReceivers.get(target);
|
|
12409
|
+
if (!receive) {
|
|
12410
|
+
logError(`serviceFor \u0434\u043B\u044F \u0431\u0440\u043E\u0448\u0435\u043D\u043D\u043E\u0439 \u043F\u0430\u0447\u043A\u0438 \u0447\u0430\u0442\u0430 ${burst.chatId} \u0432\u0435\u0440\u043D\u0443\u043B \u043D\u0435 \u0441\u043B\u0443\u0436\u0431\u0443 \u0447\u0430\u0442\u0430 SDK: \u043F\u0430\u0447\u043A\u0430 \u0436\u0434\u0451\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0433\u043E \u043E\u0431\u0445\u043E\u0434\u0430`);
|
|
12411
|
+
return;
|
|
12412
|
+
}
|
|
12413
|
+
await receive(burst);
|
|
12414
|
+
}
|
|
12415
|
+
function sweepingBursts(take) {
|
|
12416
|
+
if (!keepsPendingBursts || closing) return Promise.resolve();
|
|
12417
|
+
const sweep = (async () => {
|
|
12418
|
+
for (; ; ) {
|
|
12419
|
+
if (closing) return;
|
|
12420
|
+
const free = BURST_SWEEP_LIMIT - burstDecisions;
|
|
12421
|
+
if (free <= 0) {
|
|
12422
|
+
await burstDecisionFreed();
|
|
12423
|
+
continue;
|
|
12424
|
+
}
|
|
12425
|
+
const heldUntil = new Date(now().getTime() + BURST_HOLD_MS).toISOString();
|
|
12426
|
+
const taken = await storage.takeAbandonedBursts({ ...take, holder: copyId, heldUntil, limit: free });
|
|
12427
|
+
if (taken.length === 0) return;
|
|
12428
|
+
for (const burst of taken) {
|
|
12429
|
+
burstDecisions++;
|
|
12430
|
+
void routingAbandonedBurst(burst).finally(releasingBurstDecision);
|
|
12431
|
+
}
|
|
12432
|
+
}
|
|
12433
|
+
})().catch((error) => logError(`\u041E\u0431\u0445\u043E\u0434 \u0436\u0434\u0443\u0449\u0438\u0445 \u043F\u0430\u0447\u0435\u043A \u043D\u0435 \u0443\u0434\u0430\u043B\u0441\u044F: ${error instanceof Error ? error.message : String(error)}`)).finally(() => burstSweeps.delete(sweep));
|
|
12434
|
+
burstSweeps.add(sweep);
|
|
12435
|
+
return sweep;
|
|
12436
|
+
}
|
|
11839
12437
|
async function stoppingPendingBurst(userId, chatId) {
|
|
11840
12438
|
if (!burstWaits.has(chatId) && !isBurstPending(await currentBranch(chatId), now().getTime())) return false;
|
|
11841
12439
|
try {
|
|
@@ -12261,7 +12859,13 @@ function createChatService(input) {
|
|
|
12261
12859
|
}).catch(() => {
|
|
12262
12860
|
});
|
|
12263
12861
|
const question2 = await recording;
|
|
12264
|
-
const turn =
|
|
12862
|
+
const turn = answeringRecordedBurst(
|
|
12863
|
+
userId,
|
|
12864
|
+
chatId,
|
|
12865
|
+
question2.id,
|
|
12866
|
+
burstOptions?.page,
|
|
12867
|
+
burstOptions?.answerAfterRestart ? { kept: true, dueAt: Date.parse(question2.createdAt) + quietMs } : { kept: false }
|
|
12868
|
+
);
|
|
12265
12869
|
turn.catch(() => {
|
|
12266
12870
|
});
|
|
12267
12871
|
return { messageId: question2.id, turn };
|
|
@@ -12546,9 +13150,15 @@ function createChatService(input) {
|
|
|
12546
13150
|
},
|
|
12547
13151
|
async recoverInterruptedAnswers(recoverOptions) {
|
|
12548
13152
|
await afterTurn?.recoverPending(liveAnswerIds);
|
|
12549
|
-
const
|
|
13153
|
+
const soleServer = recoverOptions?.soleServer === true;
|
|
13154
|
+
const [count] = await Promise.all([recoverAnswersOnStart(soleServer), options2.background?.recover()]);
|
|
13155
|
+
await sweepingBursts(soleServer ? { abandonedBefore: FAR_FUTURE_ISO, exceptHolder: copyId } : { abandonedBefore: now().toISOString() });
|
|
12550
13156
|
return count;
|
|
12551
13157
|
},
|
|
13158
|
+
async tickBursts() {
|
|
13159
|
+
burstWaits.wakeAll();
|
|
13160
|
+
await sweepingBursts({ abandonedBefore: now().toISOString() });
|
|
13161
|
+
},
|
|
12552
13162
|
async closeChat({ ownerId, chatId, code, message }) {
|
|
12553
13163
|
const closed = normalizeChatClosure({ code, message });
|
|
12554
13164
|
await changeClosure(await ownChat(ownerId, chatId), closed);
|
|
@@ -12576,6 +13186,7 @@ function createChatService(input) {
|
|
|
12576
13186
|
burstWaits.cancelAll();
|
|
12577
13187
|
try {
|
|
12578
13188
|
await sweeping;
|
|
13189
|
+
while (burstSweeps.size > 0) await Promise.allSettled([...burstSweeps]);
|
|
12579
13190
|
while (startingWork.size > 0) await Promise.allSettled([...startingWork]);
|
|
12580
13191
|
const unfinished = await liveTurns.drain(waitForAnswersMs);
|
|
12581
13192
|
await burstWaits.settled();
|
|
@@ -12614,7 +13225,7 @@ function createChatService(input) {
|
|
|
12614
13225
|
const next = () => {
|
|
12615
13226
|
if (sweep.stopped) return;
|
|
12616
13227
|
sweep.timer = setTimeout(() => {
|
|
12617
|
-
sweep.running = Promise.all([recoverStaleAnswers(), afterTurn?.recoverPending(liveAnswerIds)]).then(() => void 0).catch(
|
|
13228
|
+
sweep.running = Promise.all([recoverStaleAnswers(), afterTurn?.recoverPending(liveAnswerIds), sweepingBursts({ abandonedBefore: now().toISOString() })]).then(() => void 0).catch(
|
|
12618
13229
|
(failure) => logError(`\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u0431\u0440\u043E\u0448\u0435\u043D\u043D\u044B\u0445 \u043E\u0442\u0432\u0435\u0442\u043E\u0432 \u043D\u0435 \u0443\u0434\u0430\u043B\u0430\u0441\u044C: ${failure instanceof Error ? failure.message : "\u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430\u044F \u043E\u0448\u0438\u0431\u043A\u0430"}`)
|
|
12619
13230
|
).finally(() => {
|
|
12620
13231
|
sweep.running = void 0;
|
|
@@ -12731,6 +13342,7 @@ function createChatService(input) {
|
|
|
12731
13342
|
modelSilence,
|
|
12732
13343
|
logError,
|
|
12733
13344
|
...providerFallback !== void 0 && { providerFallback },
|
|
13345
|
+
providerOutages,
|
|
12734
13346
|
...options2.turnObserver && { turnObserver: options2.turnObserver },
|
|
12735
13347
|
...options2.limits && { assertWithinLimit: () => assertWithinSpendingLimit(ownerId, chatId) }
|
|
12736
13348
|
});
|
|
@@ -12740,6 +13352,7 @@ function createChatService(input) {
|
|
|
12740
13352
|
helpers: backgroundHelpers
|
|
12741
13353
|
};
|
|
12742
13354
|
}
|
|
13355
|
+
abandonedBurstReceivers.set(service, receivingAbandonedBurst);
|
|
12743
13356
|
return service;
|
|
12744
13357
|
function isDetachedAnswer(last, runningId) {
|
|
12745
13358
|
return runningId === null && last?.status === "streaming" && !liveAnswerIds.has(last.id);
|
|
@@ -12899,6 +13512,8 @@ export {
|
|
|
12899
13512
|
isCorrection,
|
|
12900
13513
|
BURST_PART_TYPE,
|
|
12901
13514
|
DEFAULT_BURST_QUIET_MS,
|
|
13515
|
+
BURST_HOLD_MS,
|
|
13516
|
+
BURST_SWEEP_LIMIT,
|
|
12902
13517
|
isBurstMessage,
|
|
12903
13518
|
RELEASED_ANSWER_ALIVE_AT,
|
|
12904
13519
|
aliveAtByLocalClock,
|
|
@@ -12999,4 +13614,4 @@ export {
|
|
|
12999
13614
|
InvalidWidgetAnswerError,
|
|
13000
13615
|
createChatService
|
|
13001
13616
|
};
|
|
13002
|
-
//# sourceMappingURL=chunk-
|
|
13617
|
+
//# sourceMappingURL=chunk-KAG3QTZC.js.map
|