@eddyskywalker/dsh-chatgpt-subscription 0.62.0 → 0.63.1
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 +38 -0
- package/lib/client.js.map +1 -1
- package/lib/codemode.js +939 -326
- package/lib/index.js +663 -342
- package/lib/types/compat.d.ts +1 -1
- package/lib/types/entry-codemode.d.ts.map +1 -1
- package/lib/types/host/cache-warmup/install.d.ts.map +1 -1
- package/lib/types/host/cache-warmup/projection.d.ts.map +1 -1
- package/lib/types/host/cache-warmup/retrieval-intent.d.ts +2 -2
- package/lib/types/host/cache-warmup/retrieval-intent.d.ts.map +1 -1
- package/lib/types/host/cache-warmup/usage.d.ts.map +1 -1
- package/lib/types/host/codemode/executor.d.ts +4 -0
- package/lib/types/host/codemode/executor.d.ts.map +1 -1
- package/lib/types/host/codemode/retention.d.ts +28 -11
- package/lib/types/host/codemode/retention.d.ts.map +1 -1
- package/lib/types/host/codemode/rules.d.ts +4 -0
- package/lib/types/host/codemode/rules.d.ts.map +1 -1
- package/lib/types/host/codemode/transport.d.ts +45 -3
- package/lib/types/host/codemode/transport.d.ts.map +1 -1
- package/lib/types/host/kimi-code/adapter.d.ts +16 -0
- package/lib/types/host/kimi-code/adapter.d.ts.map +1 -1
- package/lib/types/host/kimi-code/mapper.d.ts +33 -1
- package/lib/types/host/kimi-code/mapper.d.ts.map +1 -1
- package/lib/types/host/proxy-manager.d.ts.map +1 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -14,7 +14,7 @@ import path, { basename, dirname, extname, isAbsolute, join, relative, resolve,
|
|
|
14
14
|
import os, { homedir, platform, userInfo } from "node:os";
|
|
15
15
|
import { constants, createCipheriv, createDecipheriv, createHash, createHmac, publicEncrypt, randomBytes, randomUUID } from "node:crypto";
|
|
16
16
|
import { isDeepStrictEqual } from "node:util";
|
|
17
|
-
import {
|
|
17
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
18
18
|
import fs$1, { appendFileSync, constants as constants$1, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, statSync, utimesSync, writeFileSync } from "node:fs";
|
|
19
19
|
import http, { createServer } from "node:http";
|
|
20
20
|
import { WebError } from "@deepseek-ai/dsh-web";
|
|
@@ -808,7 +808,7 @@ const ROUTE_PREFIX$5 = "/api/dsh-chatgpt-subscription";
|
|
|
808
808
|
* `test/responses-client.test.ts` asserts the two agree, so a release that
|
|
809
809
|
* forgets this constant fails the suite instead of shipping quietly.
|
|
810
810
|
*/
|
|
811
|
-
const PLUGIN_VERSION = "0.
|
|
811
|
+
const PLUGIN_VERSION = "0.63.1";
|
|
812
812
|
const CODEX_CHATGPT_PROVIDER_ID = "codex-chatgpt";
|
|
813
813
|
const CODEX_API_BASE = "https://chatgpt.com/backend-api/codex";
|
|
814
814
|
const CODEX_RESPONSES_URL = `${CODEX_API_BASE}/responses`;
|
|
@@ -11378,20 +11378,36 @@ function shouldBypassProxy(target, noProxyList = "") {
|
|
|
11378
11378
|
return noProxyBypasses(url.hostname, url.port || defaultPortFor(url.protocol), noProxyList);
|
|
11379
11379
|
}
|
|
11380
11380
|
/**
|
|
11381
|
+
* Run one system probe with no console window and no shell layer.
|
|
11382
|
+
*
|
|
11383
|
+
* `windowsHide` is not cosmetic here. `execSync` runs through `cmd.exe /d /s /c`,
|
|
11384
|
+
* and a host that has no console of its own (a detached `dsh web` host on Windows,
|
|
11385
|
+
* or any GUI-launched parent) gets a NEW VISIBLE console allocated for that
|
|
11386
|
+
* short-lived shell — the black window that flashed on every re-detection, because
|
|
11387
|
+
* the proxy cache TTL is only 5 seconds (#19, #91). `execFileSync` additionally
|
|
11388
|
+
* drops the shell hop, so one probe is one process instead of two and its argument
|
|
11389
|
+
* needs no quoting. `windowsHide` is a no-op on macOS and Linux, but it states the
|
|
11390
|
+
* intent once for every platform.
|
|
11391
|
+
*/
|
|
11392
|
+
function runSystemProbe(file, args) {
|
|
11393
|
+
return execFileSync(file, args, {
|
|
11394
|
+
timeout: 1500,
|
|
11395
|
+
encoding: "utf-8",
|
|
11396
|
+
windowsHide: true,
|
|
11397
|
+
stdio: [
|
|
11398
|
+
"ignore",
|
|
11399
|
+
"pipe",
|
|
11400
|
+
"ignore"
|
|
11401
|
+
]
|
|
11402
|
+
});
|
|
11403
|
+
}
|
|
11404
|
+
/**
|
|
11381
11405
|
* Read one GNOME proxy setting; `null` when `gsettings` is absent or the key is
|
|
11382
11406
|
* unreadable (headless box, no dconf session).
|
|
11383
11407
|
*/
|
|
11384
11408
|
function readGsettings(key) {
|
|
11385
11409
|
try {
|
|
11386
|
-
return
|
|
11387
|
-
timeout: 1500,
|
|
11388
|
-
encoding: "utf-8",
|
|
11389
|
-
stdio: [
|
|
11390
|
-
"ignore",
|
|
11391
|
-
"pipe",
|
|
11392
|
-
"ignore"
|
|
11393
|
-
]
|
|
11394
|
-
}).trim();
|
|
11410
|
+
return runSystemProbe("gsettings", ["get", key]).trim();
|
|
11395
11411
|
} catch {
|
|
11396
11412
|
return null;
|
|
11397
11413
|
}
|
|
@@ -11421,26 +11437,10 @@ function parseLinuxSystemProxy(read) {
|
|
|
11421
11437
|
function detectSystemProxy(platform = process.platform, env = process.env, readSettings = readGsettings) {
|
|
11422
11438
|
try {
|
|
11423
11439
|
if (platform === "win32") {
|
|
11424
|
-
const detected = parseWindowsProxyRegistry(
|
|
11425
|
-
timeout: 1500,
|
|
11426
|
-
encoding: "utf-8",
|
|
11427
|
-
stdio: [
|
|
11428
|
-
"ignore",
|
|
11429
|
-
"pipe",
|
|
11430
|
-
"ignore"
|
|
11431
|
-
]
|
|
11432
|
-
}));
|
|
11440
|
+
const detected = parseWindowsProxyRegistry(runSystemProbe("reg.exe", ["query", "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"]));
|
|
11433
11441
|
if (detected) return detected;
|
|
11434
11442
|
} else if (platform === "darwin") {
|
|
11435
|
-
const detected = parseMacOsScutilProxy(
|
|
11436
|
-
timeout: 1500,
|
|
11437
|
-
encoding: "utf-8",
|
|
11438
|
-
stdio: [
|
|
11439
|
-
"ignore",
|
|
11440
|
-
"pipe",
|
|
11441
|
-
"ignore"
|
|
11442
|
-
]
|
|
11443
|
-
}));
|
|
11443
|
+
const detected = parseMacOsScutilProxy(runSystemProbe("scutil", ["--proxy"]));
|
|
11444
11444
|
if (detected) return detected;
|
|
11445
11445
|
} else if (platform === "linux") {
|
|
11446
11446
|
const detected = parseLinuxSystemProxy(readSettings);
|
|
@@ -28188,7 +28188,11 @@ function isWarmupHitReport(report, previous) {
|
|
|
28188
28188
|
if (cached < CACHE_WARMUP_DEFAULTS.retainedPrefixMinCachedTokens) return false;
|
|
28189
28189
|
if (prevCached < CACHE_WARMUP_DEFAULTS.retainedPrefixMinCachedTokens) return false;
|
|
28190
28190
|
if (cached < prevCached) return false;
|
|
28191
|
-
|
|
28191
|
+
if (fresh <= CACHE_WARMUP_DEFAULTS.retainedPrefixMaxFreshTokens) return true;
|
|
28192
|
+
const prevFresh = finiteCount(previous.inputTokens);
|
|
28193
|
+
const prevAlignedTotal = Math.floor((prevCached + prevFresh) / 256) * 256;
|
|
28194
|
+
if (prevCached >= 4096 && cached > prevCached && cached >= prevAlignedTotal) return true;
|
|
28195
|
+
return false;
|
|
28192
28196
|
}
|
|
28193
28197
|
function consecutiveWarmupHits(reports) {
|
|
28194
28198
|
try {
|
|
@@ -28666,7 +28670,7 @@ function isBareFilePathLine(trimmed) {
|
|
|
28666
28670
|
*/
|
|
28667
28671
|
function isMixedEvidenceOutput(text) {
|
|
28668
28672
|
if (text.includes("[cache-warmup preview]")) return true;
|
|
28669
|
-
if (/^(
|
|
28673
|
+
if (/^(?:===\s+.+?(?:\s+===)?|---\s+(?!a\/\S).+?(?:\s+---)?)$/m.test(text) || /^###\s+\S/m.test(text) || /^--\s+.+\s+--$/m.test(text)) return true;
|
|
28670
28674
|
const lines = text.split("\n");
|
|
28671
28675
|
let inspectStringConcat = 0;
|
|
28672
28676
|
let jsonPropertyLines = 0;
|
|
@@ -29034,22 +29038,48 @@ function looksLikeContract(text) {
|
|
|
29034
29038
|
return false;
|
|
29035
29039
|
}
|
|
29036
29040
|
/**
|
|
29041
|
+
* 判断包含 `diff --git` 的文本是否为构建/测试脚本输出中内嵌的局部 diff,而非纯粹的代码补丁。
|
|
29042
|
+
*
|
|
29043
|
+
* 真实会话中(如 `npm run checks && npm run verify`),脚本前段可能打印一段快照 `diff --git`,
|
|
29044
|
+
* 紧接着运行测试或以 `[exit code: 1]` 退出。若一刀切将所有含 `diff --git` 的输出当作源码,
|
|
29045
|
+
* 会导致 25KB+ 的构建/测试失败日志无法投影。
|
|
29046
|
+
*/
|
|
29047
|
+
function isEmbeddedDiffInBuildOrTestLog(text) {
|
|
29048
|
+
const firstNonEmpty = text.split("\n").map((line) => line.trim()).find((line) => line !== "");
|
|
29049
|
+
if (firstNonEmpty === void 0) return false;
|
|
29050
|
+
if (firstNonEmpty.startsWith("diff --git ") || firstNonEmpty.startsWith("--- a/") || firstNonEmpty.startsWith("+++ b/") || firstNonEmpty.startsWith("@@ ") || /^(?:commit\s+)?[0-9a-f]{7,40}\b/.test(firstNonEmpty)) return false;
|
|
29051
|
+
const hasTestSummary = extractTestEvidence(text) !== void 0;
|
|
29052
|
+
const hasExitCode = /\[exit code: -?\d+\]/i.test(text) || /(?:exit code|exited with code|process exited with code)[:=\s]+-?\d+/i.test(text);
|
|
29053
|
+
const hasNpmScriptHeader = /^>\s+\S+@\S+\s+\S+/m.test(text);
|
|
29054
|
+
return hasTestSummary || hasExitCode && hasNpmScriptHeader;
|
|
29055
|
+
}
|
|
29056
|
+
/**
|
|
29037
29057
|
* 源码或混合证据判定:
|
|
29038
29058
|
* - 含多处 markdown 代码围栏;
|
|
29039
|
-
* - 含 `git diff` 补丁块(`diff --git` / `@@ -x,y +x,y
|
|
29059
|
+
* - 含 `git diff` 补丁块(`diff --git` / `@@ -x,y +x,y @@`),除非是带测试汇总/脚本退出码的构建日志;
|
|
29040
29060
|
* - 含至少 5 行带行号的源码切片(`1: ...` 或 `1\t...`),说明是单文件或多文件混合读取输出;
|
|
29041
29061
|
* - 代码行占比 >= 45%。
|
|
29042
29062
|
*/
|
|
29043
29063
|
function looksLikeSourceCode(text) {
|
|
29044
|
-
|
|
29064
|
+
const hasDiff = /^diff --git\s+/m.test(text) || /^@@ -\d+(?:,\d+)? \+\d+(?:,\d+)? @@/m.test(text);
|
|
29065
|
+
if (hasDiff && !isEmbeddedDiffInBuildOrTestLog(text)) return true;
|
|
29045
29066
|
let fences = 0;
|
|
29046
29067
|
for (const line of text.split("\n").slice(0, 600)) if (stripLineNumberPrefix(line).trim().startsWith("```")) fences += 1;
|
|
29047
29068
|
if (fences >= 4) return true;
|
|
29048
29069
|
let code = 0;
|
|
29049
29070
|
let numbered = 0;
|
|
29050
29071
|
let nonEmpty = 0;
|
|
29072
|
+
let inEmbeddedDiffHunk = false;
|
|
29051
29073
|
for (const raw of text.split("\n").slice(0, 600)) {
|
|
29052
29074
|
if (raw.trim() === "") continue;
|
|
29075
|
+
if (hasDiff) {
|
|
29076
|
+
if (raw.startsWith("diff --git ") || raw.startsWith("--- a/") || raw.startsWith("+++ b/") || raw.startsWith("@@ ")) {
|
|
29077
|
+
inEmbeddedDiffHunk = true;
|
|
29078
|
+
continue;
|
|
29079
|
+
}
|
|
29080
|
+
if (inEmbeddedDiffHunk && (raw.startsWith("+") || raw.startsWith("-") || raw.startsWith(" "))) continue;
|
|
29081
|
+
inEmbeddedDiffHunk = false;
|
|
29082
|
+
}
|
|
29053
29083
|
if (LINE_NUMBER_PREFIX.test(raw)) numbered += 1;
|
|
29054
29084
|
const trimmed = stripLineNumberPrefix(raw).trim();
|
|
29055
29085
|
if (trimmed === "") continue;
|
|
@@ -33807,7 +33837,159 @@ function leadingSystemText$6(options) {
|
|
|
33807
33837
|
return parts.length === 0 ? void 0 : parts.join("\n\n");
|
|
33808
33838
|
}
|
|
33809
33839
|
function nonSystemMessages$6(options) {
|
|
33810
|
-
return options.messages.filter((message) => message.role !== "system");
|
|
33840
|
+
return orderStaticPrefixMessages(options.messages.filter((message) => message.role !== "system"));
|
|
33841
|
+
}
|
|
33842
|
+
/**
|
|
33843
|
+
* Stability tier for an initial bootstrap reminder message (`1` = most static, `4` = session-scoped).
|
|
33844
|
+
*
|
|
33845
|
+
* The harness appends bootstrap reminders (`agent-instructions`, `runtime-context`,
|
|
33846
|
+
* `progress-updates-language`, `skill-catalog`) AFTER the first user prompt (or
|
|
33847
|
+
* after the retained tail following a compaction checkpoint). Because Kimi's
|
|
33848
|
+
* prefix cache matches strictly from byte 0, leaving the dynamic user prompt at
|
|
33849
|
+
* index 0 prevents subagents, forked sessions, and post-compaction turns from
|
|
33850
|
+
* sharing the ~8,300-token `AGENTS.md` + `skill-catalog` prefix.
|
|
33851
|
+
*
|
|
33852
|
+
* Mid-conversation delta reminders (`Updated instructions from:`, `Additional
|
|
33853
|
+
* instructions from:`, `Instructions removed:`, `The available skill catalog changed.`)
|
|
33854
|
+
* return `null` so they stay anchored at the exact history position where they
|
|
33855
|
+
* arrived (append-only in-history).
|
|
33856
|
+
*/
|
|
33857
|
+
function classifyBootstrapUserMessage(message) {
|
|
33858
|
+
if (message.role !== "user" || isToolResultMessage$7(message)) return null;
|
|
33859
|
+
const source = isRecord$53(message.source) ? message.source : void 0;
|
|
33860
|
+
const kind = source !== void 0 ? asString$21(source.kind) : void 0;
|
|
33861
|
+
const text = firstTextOf(message.content)?.trimStart() ?? "";
|
|
33862
|
+
if (kind === "progress-updates-language" || text.startsWith("Progress update output language override:")) return 1;
|
|
33863
|
+
if (text.startsWith("<system-reminder>\nThe following workspace instructions") || kind === "agent-instructions" && !text.startsWith("<system-reminder>\nUpdated instructions") && !text.startsWith("<system-reminder>\nAdditional instructions") && !text.startsWith("<system-reminder>\nInstructions removed")) return 2;
|
|
33864
|
+
if (text.startsWith("<system-reminder>\nA skill is a reusable set of task-specific instructions.") || kind === "skill-catalog" && !text.startsWith("<system-reminder>\nThe available skill catalog changed.")) return 3;
|
|
33865
|
+
if (kind === "runtime-context" || text.startsWith("Current runtime context. This snapshot supersedes")) return 4;
|
|
33866
|
+
return null;
|
|
33867
|
+
}
|
|
33868
|
+
function isCompactCheckpointMessage(message) {
|
|
33869
|
+
if (message === void 0 || message.role !== "user" || isToolResultMessage$7(message)) return false;
|
|
33870
|
+
const source = isRecord$53(message.source) ? message.source : void 0;
|
|
33871
|
+
if (source !== void 0 && asString$21(source.kind) === "compact-checkpoint") return true;
|
|
33872
|
+
return (firstTextOf(message.content)?.trimStart() ?? "").startsWith("This is an automatically generated checkpoint condensing");
|
|
33873
|
+
}
|
|
33874
|
+
/**
|
|
33875
|
+
* Normalize `agent-instructions` text so global `~/.dsh/` instructions precede
|
|
33876
|
+
* workspace-local `AGENTS.md` instructions when both appear in the same block.
|
|
33877
|
+
*/
|
|
33878
|
+
function normalizeAgentInstructionsMessage(message) {
|
|
33879
|
+
if (!Array.isArray(message.content) || message.content.length !== 1) return message;
|
|
33880
|
+
const first = message.content[0];
|
|
33881
|
+
if (!isRecord$53(first) || first.type !== "text" || typeof first.text !== "string") return message;
|
|
33882
|
+
const raw = first.text;
|
|
33883
|
+
const firstIdx = raw.indexOf("\n\nInstructions from: ");
|
|
33884
|
+
if (firstIdx < 0) return message;
|
|
33885
|
+
const closeIdx = raw.lastIndexOf("\n</system-reminder>");
|
|
33886
|
+
if (closeIdx <= firstIdx) return message;
|
|
33887
|
+
const header = raw.slice(0, firstIdx);
|
|
33888
|
+
const body = raw.slice(firstIdx + 2, closeIdx);
|
|
33889
|
+
const tail = raw.slice(closeIdx);
|
|
33890
|
+
const sections = body.split("\n\nInstructions from: ");
|
|
33891
|
+
if (sections.length < 2) return message;
|
|
33892
|
+
const tagged = sections.map((sec, idx) => {
|
|
33893
|
+
const firstLineEnd = sec.indexOf("\n");
|
|
33894
|
+
const sourcePath = (firstLineEnd < 0 ? sec : sec.slice(0, firstLineEnd)).trim();
|
|
33895
|
+
return {
|
|
33896
|
+
sec,
|
|
33897
|
+
idx,
|
|
33898
|
+
rank: sourcePath.startsWith("~/") || sourcePath.startsWith("~\\") || sourcePath.includes("/.dsh/") || sourcePath.includes("\\.dsh\\") ? 0 : 1
|
|
33899
|
+
};
|
|
33900
|
+
});
|
|
33901
|
+
if (!tagged.some((item, i) => i > 0 && item.rank < tagged[i - 1].rank)) return message;
|
|
33902
|
+
tagged.sort((a, b) => a.rank - b.rank || a.idx - b.idx);
|
|
33903
|
+
const rebuilt = `${header}\n\nInstructions from: ${tagged.map((t) => t.sec).join("\n\nInstructions from: ")}${tail}`;
|
|
33904
|
+
return {
|
|
33905
|
+
...message,
|
|
33906
|
+
content: [{
|
|
33907
|
+
...first,
|
|
33908
|
+
text: rebuilt
|
|
33909
|
+
}]
|
|
33910
|
+
};
|
|
33911
|
+
}
|
|
33912
|
+
/**
|
|
33913
|
+
* Deterministic entropy-sorted ordering for leading bootstrap reminders.
|
|
33914
|
+
*
|
|
33915
|
+
* 1. Within the leading pre-assistant `user` message run, orders the first
|
|
33916
|
+
* occurrence of each bootstrap tier (`1: progress-updates-language` ->
|
|
33917
|
+
* `2: agent-instructions` -> `3: skill-catalog` -> `4: runtime-context`)
|
|
33918
|
+
* ahead of the dynamic `user` prompt (`5`).
|
|
33919
|
+
* 2. When the surface starts with a `compact-checkpoint` that shadowed the
|
|
33920
|
+
* original bootstrap run and the harness re-injected Tier 1/2/3 reminders
|
|
33921
|
+
* after the retained tail, hoists the first occurrence of Tier 1/2/3 ahead
|
|
33922
|
+
* of `compact-checkpoint` so the ~21k static prefix stays intact across
|
|
33923
|
+
* compaction boundaries and every step after compaction.
|
|
33924
|
+
*/
|
|
33925
|
+
function orderStaticPrefixMessages(messages) {
|
|
33926
|
+
try {
|
|
33927
|
+
if (messages.length <= 1) return [...messages];
|
|
33928
|
+
let leadEnd = 0;
|
|
33929
|
+
while (leadEnd < messages.length && messages[leadEnd].role === "user" && !isToolResultMessage$7(messages[leadEnd])) leadEnd += 1;
|
|
33930
|
+
if (leadEnd > 0 && isCompactCheckpointMessage(messages[0])) {
|
|
33931
|
+
const seenInLead = /* @__PURE__ */ new Set();
|
|
33932
|
+
for (let i = 0; i < leadEnd; i++) {
|
|
33933
|
+
const tier = classifyBootstrapUserMessage(messages[i]);
|
|
33934
|
+
if (tier !== null) seenInLead.add(tier);
|
|
33935
|
+
}
|
|
33936
|
+
if (!seenInLead.has(1) && !seenInLead.has(2)) {
|
|
33937
|
+
const hoistedByTier = /* @__PURE__ */ new Map();
|
|
33938
|
+
for (let i = leadEnd; i < messages.length; i++) {
|
|
33939
|
+
const msg = messages[i];
|
|
33940
|
+
const tier = classifyBootstrapUserMessage(msg);
|
|
33941
|
+
if ((tier === 1 || tier === 2 || tier === 3) && !hoistedByTier.has(tier)) hoistedByTier.set(tier, {
|
|
33942
|
+
index: i,
|
|
33943
|
+
message: tier === 2 ? normalizeAgentInstructionsMessage(msg) : msg
|
|
33944
|
+
});
|
|
33945
|
+
}
|
|
33946
|
+
if (hoistedByTier.size > 0) {
|
|
33947
|
+
const hoistedIndices = /* @__PURE__ */ new Set();
|
|
33948
|
+
const prefix = [];
|
|
33949
|
+
for (const tier of [
|
|
33950
|
+
1,
|
|
33951
|
+
2,
|
|
33952
|
+
3
|
|
33953
|
+
]) {
|
|
33954
|
+
const entry = hoistedByTier.get(tier);
|
|
33955
|
+
if (entry !== void 0) {
|
|
33956
|
+
hoistedIndices.add(entry.index);
|
|
33957
|
+
prefix.push(entry.message);
|
|
33958
|
+
}
|
|
33959
|
+
}
|
|
33960
|
+
const rest = messages.filter((_, idx) => !hoistedIndices.has(idx));
|
|
33961
|
+
return [...prefix, ...rest];
|
|
33962
|
+
}
|
|
33963
|
+
}
|
|
33964
|
+
}
|
|
33965
|
+
if (leadEnd <= 1) return [...messages];
|
|
33966
|
+
const seenTiers = /* @__PURE__ */ new Set();
|
|
33967
|
+
const leadTagged = [];
|
|
33968
|
+
let hasBootstrap = false;
|
|
33969
|
+
for (let i = 0; i < leadEnd; i++) {
|
|
33970
|
+
const rawMsg = messages[i];
|
|
33971
|
+
const tier = classifyBootstrapUserMessage(rawMsg);
|
|
33972
|
+
if (tier !== null && !seenTiers.has(tier)) {
|
|
33973
|
+
seenTiers.add(tier);
|
|
33974
|
+
hasBootstrap = true;
|
|
33975
|
+
leadTagged.push({
|
|
33976
|
+
message: tier === 2 ? normalizeAgentInstructionsMessage(rawMsg) : rawMsg,
|
|
33977
|
+
rank: tier,
|
|
33978
|
+
index: i
|
|
33979
|
+
});
|
|
33980
|
+
} else leadTagged.push({
|
|
33981
|
+
message: rawMsg,
|
|
33982
|
+
rank: 5,
|
|
33983
|
+
index: i
|
|
33984
|
+
});
|
|
33985
|
+
}
|
|
33986
|
+
if (!hasBootstrap) return [...messages];
|
|
33987
|
+
if (!leadTagged.some((item, i) => i > 0 && item.rank < leadTagged[i - 1].rank || item.message !== messages[i])) return [...messages];
|
|
33988
|
+
leadTagged.sort((a, b) => a.rank - b.rank || a.index - b.index);
|
|
33989
|
+
return [...leadTagged.map((item) => item.message), ...messages.slice(leadEnd)];
|
|
33990
|
+
} catch {
|
|
33991
|
+
return [...messages];
|
|
33992
|
+
}
|
|
33811
33993
|
}
|
|
33812
33994
|
const NO_REASONING = {
|
|
33813
33995
|
fields: [],
|
|
@@ -35374,21 +35556,43 @@ function rememberScoped(store, key, value) {
|
|
|
35374
35556
|
*/
|
|
35375
35557
|
const prefixSnapshots = /* @__PURE__ */ new Map();
|
|
35376
35558
|
/**
|
|
35559
|
+
* Extract the concatenated text of leading workspace-static bootstrap reminders
|
|
35560
|
+
* (Tiers 1–3: `progress-updates-language`, initial `agent-instructions`, initial
|
|
35561
|
+
* `skill-catalog`) after static-prefix ordering.
|
|
35562
|
+
*/
|
|
35563
|
+
function leadingStaticBootstrapText(options) {
|
|
35564
|
+
const ordered = nonSystemMessages$6(options);
|
|
35565
|
+
const parts = [];
|
|
35566
|
+
for (const message of ordered) {
|
|
35567
|
+
const tier = classifyBootstrapUserMessage(message);
|
|
35568
|
+
if (tier === 1 || tier === 2 || tier === 3) {
|
|
35569
|
+
const text = textOf$7(message.content);
|
|
35570
|
+
if (text !== "") parts.push(text);
|
|
35571
|
+
continue;
|
|
35572
|
+
}
|
|
35573
|
+
break;
|
|
35574
|
+
}
|
|
35575
|
+
return parts.join("\n\n");
|
|
35576
|
+
}
|
|
35577
|
+
/**
|
|
35377
35578
|
* Hash the prefix-breaking inputs of one request.
|
|
35378
35579
|
*
|
|
35379
35580
|
* @param options - The request about to be built.
|
|
35380
|
-
* @returns The hashes of its system prompt and
|
|
35581
|
+
* @returns The hashes of its system prompt, tool list, and optional static bootstrap prefix.
|
|
35381
35582
|
*/
|
|
35382
35583
|
function prefixFingerprints(options) {
|
|
35383
35584
|
const systemText = leadingSystemText$6(options) ?? "";
|
|
35384
35585
|
const tools = options.tools ?? [];
|
|
35586
|
+
const staticBootstrap = leadingStaticBootstrapText(options);
|
|
35587
|
+
const staticPrefixHash = staticBootstrap === "" ? null : fingerprint(staticBootstrap);
|
|
35385
35588
|
return {
|
|
35386
35589
|
systemPromptHash: systemText === "" ? null : fingerprint(systemText),
|
|
35387
35590
|
toolsHash: tools.length === 0 ? null : fingerprint(JSON.stringify(tools.map((tool) => [
|
|
35388
35591
|
tool.name,
|
|
35389
35592
|
tool.description ?? "",
|
|
35390
35593
|
tool.parameters ?? {}
|
|
35391
|
-
])))
|
|
35594
|
+
]))),
|
|
35595
|
+
...staticPrefixHash === null ? {} : { staticPrefixHash }
|
|
35392
35596
|
};
|
|
35393
35597
|
}
|
|
35394
35598
|
/**
|
|
@@ -37122,6 +37326,19 @@ const inFlightColdLeaderStartedAt = /* @__PURE__ */ new Map();
|
|
|
37122
37326
|
* from the outside.
|
|
37123
37327
|
*/
|
|
37124
37328
|
const coldLeaderReleases = /* @__PURE__ */ new Map();
|
|
37329
|
+
/**
|
|
37330
|
+
* In-flight main-session requests waiting for their HTTP response headers,
|
|
37331
|
+
* keyed by sessionId. Used so a concurrent `session-title-first-prompt-llm`
|
|
37332
|
+
* side-channel request yields to the main turn's prefill instead of competing
|
|
37333
|
+
* on the same account during cold start.
|
|
37334
|
+
*/
|
|
37335
|
+
const inFlightMainSessionHeaders = /* @__PURE__ */ new Map();
|
|
37336
|
+
const mainSessionHeaderReleases = /* @__PURE__ */ new Map();
|
|
37337
|
+
/**
|
|
37338
|
+
* Per-account/model first-turn prefill pacing chain for concurrent subagent bursts.
|
|
37339
|
+
*/
|
|
37340
|
+
const inFlightSubagentBurstLeaders = /* @__PURE__ */ new Map();
|
|
37341
|
+
const subagentBurstReleases = /* @__PURE__ */ new Map();
|
|
37125
37342
|
/** The gate of a request that neither waits for nor leads a cold start. */
|
|
37126
37343
|
const NO_COLD_START_GATE = {
|
|
37127
37344
|
isLeader: false,
|
|
@@ -37132,9 +37349,9 @@ const NO_COLD_START_GATE = {
|
|
|
37132
37349
|
* The group of requests that address the same cached prefix.
|
|
37133
37350
|
*
|
|
37134
37351
|
* The service derives its cache from the request content, so two requests with
|
|
37135
|
-
* the same model, the same system prompt
|
|
37136
|
-
*
|
|
37137
|
-
* service with a cache of its own.
|
|
37352
|
+
* the same model, the same system prompt, the same tool list and the same
|
|
37353
|
+
* leading static bootstrap reminders hit the same entry. The region is part of
|
|
37354
|
+
* the key because each region is a separate service with a cache of its own.
|
|
37138
37355
|
*
|
|
37139
37356
|
* A request with no system prompt has no prefix worth warming, and gets no
|
|
37140
37357
|
* group at all.
|
|
@@ -37144,18 +37361,18 @@ const NO_COLD_START_GATE = {
|
|
|
37144
37361
|
* @returns The group key, or undefined when the request has no prefix.
|
|
37145
37362
|
*/
|
|
37146
37363
|
function coldStartGroupKey(region, options) {
|
|
37147
|
-
const { systemPromptHash, toolsHash } = prefixFingerprints(options);
|
|
37364
|
+
const { systemPromptHash, toolsHash, staticPrefixHash } = prefixFingerprints(options);
|
|
37148
37365
|
if (systemPromptHash === null) return void 0;
|
|
37149
|
-
return `${region}:${options.model}:${systemPromptHash}:${toolsHash ?? "none"}`;
|
|
37366
|
+
return `${region}:${options.model}:${systemPromptHash}:${toolsHash ?? "none"}${staticPrefixHash ? ":" + staticPrefixHash : ""}`;
|
|
37150
37367
|
}
|
|
37151
37368
|
/**
|
|
37152
37369
|
* Remember that a prefix group is warm.
|
|
37153
37370
|
*
|
|
37154
37371
|
* @param groupKey - The group that was just warmed.
|
|
37155
37372
|
*/
|
|
37156
|
-
function rememberWarmPrefix(groupKey) {
|
|
37373
|
+
function rememberWarmPrefix(groupKey, now = Date.now()) {
|
|
37157
37374
|
warmPrefixTimestamps.delete(groupKey);
|
|
37158
|
-
warmPrefixTimestamps.set(groupKey,
|
|
37375
|
+
warmPrefixTimestamps.set(groupKey, now);
|
|
37159
37376
|
while (warmPrefixTimestamps.size > MAX_WARM_PREFIX_GROUPS) {
|
|
37160
37377
|
const oldest = warmPrefixTimestamps.keys().next();
|
|
37161
37378
|
if (oldest.done === true) break;
|
|
@@ -37163,6 +37380,83 @@ function rememberWarmPrefix(groupKey) {
|
|
|
37163
37380
|
}
|
|
37164
37381
|
}
|
|
37165
37382
|
/**
|
|
37383
|
+
* Register an in-flight main-session request until its HTTP response headers arrive.
|
|
37384
|
+
*/
|
|
37385
|
+
function registerInFlightMainSessionHeader(sessionId) {
|
|
37386
|
+
const key = sessionId.trim();
|
|
37387
|
+
if (key === "") return () => void 0;
|
|
37388
|
+
mainSessionHeaderReleases.get(key)?.();
|
|
37389
|
+
let resolveHeader;
|
|
37390
|
+
const promise = new Promise((resolve) => {
|
|
37391
|
+
resolveHeader = resolve;
|
|
37392
|
+
});
|
|
37393
|
+
inFlightMainSessionHeaders.set(key, promise);
|
|
37394
|
+
let released = false;
|
|
37395
|
+
const release = () => {
|
|
37396
|
+
if (released) return;
|
|
37397
|
+
released = true;
|
|
37398
|
+
resolveHeader();
|
|
37399
|
+
if (mainSessionHeaderReleases.get(key) === release) {
|
|
37400
|
+
mainSessionHeaderReleases.delete(key);
|
|
37401
|
+
inFlightMainSessionHeaders.delete(key);
|
|
37402
|
+
}
|
|
37403
|
+
};
|
|
37404
|
+
mainSessionHeaderReleases.set(key, release);
|
|
37405
|
+
return release;
|
|
37406
|
+
}
|
|
37407
|
+
/**
|
|
37408
|
+
* Whether a first-turn request carries a subagent mandate or large uncached
|
|
37409
|
+
* dynamic payload that should stagger behind an already in-flight sibling burst.
|
|
37410
|
+
*/
|
|
37411
|
+
function isSubagentBurstCandidate(options) {
|
|
37412
|
+
let dynamicChars = 0;
|
|
37413
|
+
for (const message of options.messages) {
|
|
37414
|
+
if (message.role !== "user") continue;
|
|
37415
|
+
const tier = classifyBootstrapUserMessage(message);
|
|
37416
|
+
if (tier === 1 || tier === 2 || tier === 3) continue;
|
|
37417
|
+
if (!Array.isArray(message.content)) continue;
|
|
37418
|
+
for (const block of message.content) if (typeof block === "object" && block !== null && block.type === "text") {
|
|
37419
|
+
const text = typeof block.text === "string" ? block.text : "";
|
|
37420
|
+
if (text.includes("dsh-mandate-v2")) return true;
|
|
37421
|
+
dynamicChars += text.length;
|
|
37422
|
+
}
|
|
37423
|
+
}
|
|
37424
|
+
return dynamicChars >= 1200;
|
|
37425
|
+
}
|
|
37426
|
+
/**
|
|
37427
|
+
* Stagger concurrent subagent first-turn requests on the same account/model
|
|
37428
|
+
* so they do not stampede prefill in the same millisecond.
|
|
37429
|
+
*/
|
|
37430
|
+
async function acquireSubagentBurstPacing(pacingKey, signal) {
|
|
37431
|
+
while (true) {
|
|
37432
|
+
const existing = inFlightSubagentBurstLeaders.get(pacingKey);
|
|
37433
|
+
if (existing === void 0) break;
|
|
37434
|
+
await raceColdStartLeader(existing, signal);
|
|
37435
|
+
if (signal.aborted) return () => void 0;
|
|
37436
|
+
if (inFlightSubagentBurstLeaders.get(pacingKey) === existing) {
|
|
37437
|
+
subagentBurstReleases.get(pacingKey)?.();
|
|
37438
|
+
break;
|
|
37439
|
+
}
|
|
37440
|
+
}
|
|
37441
|
+
let resolvePacing;
|
|
37442
|
+
const promise = new Promise((resolve) => {
|
|
37443
|
+
resolvePacing = resolve;
|
|
37444
|
+
});
|
|
37445
|
+
inFlightSubagentBurstLeaders.set(pacingKey, promise);
|
|
37446
|
+
let settled = false;
|
|
37447
|
+
const release = () => {
|
|
37448
|
+
if (settled) return;
|
|
37449
|
+
settled = true;
|
|
37450
|
+
resolvePacing();
|
|
37451
|
+
if (subagentBurstReleases.get(pacingKey) === release) {
|
|
37452
|
+
subagentBurstReleases.delete(pacingKey);
|
|
37453
|
+
inFlightSubagentBurstLeaders.delete(pacingKey);
|
|
37454
|
+
}
|
|
37455
|
+
};
|
|
37456
|
+
subagentBurstReleases.set(pacingKey, release);
|
|
37457
|
+
return release;
|
|
37458
|
+
}
|
|
37459
|
+
/**
|
|
37166
37460
|
* Wait for the leader of a prefix group, for the wait budget, or for an abort.
|
|
37167
37461
|
*
|
|
37168
37462
|
* The timer and the abort listener are removed whichever way the race ends, so
|
|
@@ -37506,322 +37800,338 @@ var KimiCodeAdapter = class extends LlmAdapter {
|
|
|
37506
37800
|
}
|
|
37507
37801
|
async *requestStream(options, signal, activity) {
|
|
37508
37802
|
const fetchFn = this.options.fetchFn ?? fetch;
|
|
37509
|
-
const catalog = await this.catalog().catch(() => []);
|
|
37510
|
-
const wire = wireForCatalogEntry(options.model, catalog);
|
|
37511
|
-
const entry = catalog.find((model) => model.id === options.model);
|
|
37512
37803
|
const normalized = normalizeGenerateOptions(options);
|
|
37513
37804
|
const sideChannel = isSideChannelTitleRequest(normalized);
|
|
37514
|
-
const
|
|
37515
|
-
const
|
|
37516
|
-
|
|
37517
|
-
|
|
37518
|
-
|
|
37519
|
-
|
|
37520
|
-
|
|
37521
|
-
|
|
37522
|
-
|
|
37523
|
-
|
|
37524
|
-
|
|
37525
|
-
|
|
37526
|
-
|
|
37527
|
-
|
|
37528
|
-
|
|
37529
|
-
|
|
37530
|
-
|
|
37531
|
-
|
|
37532
|
-
|
|
37533
|
-
|
|
37534
|
-
|
|
37535
|
-
|
|
37536
|
-
|
|
37537
|
-
|
|
37538
|
-
|
|
37539
|
-
|
|
37540
|
-
*/
|
|
37541
|
-
const baseMedia = {
|
|
37542
|
-
videos,
|
|
37543
|
-
videoAccepted: modalities.includes("video"),
|
|
37544
|
-
messageTools: dynamicToolsForEntry(options.model, catalog)
|
|
37545
|
-
};
|
|
37546
|
-
/**
|
|
37547
|
-
* Which readable occurrences the reference path would cover.
|
|
37548
|
-
*
|
|
37549
|
-
* Decided here so the body-fit guard can measure the body that will actually
|
|
37550
|
-
* be sent; nothing is uploaded, and no network is touched, until that guard
|
|
37551
|
-
* has accepted the request (see the rotation loop).
|
|
37552
|
-
*/
|
|
37553
|
-
const planned = planMediaReferences({
|
|
37554
|
-
wire,
|
|
37555
|
-
imageAccepted: modalities.includes("image"),
|
|
37556
|
-
videoAccepted: baseMedia.videoAccepted === true,
|
|
37557
|
-
images,
|
|
37558
|
-
videos
|
|
37559
|
-
});
|
|
37560
|
-
const contextWindow = this.contextWindowFor(options.model, entry, settings.contextWindowOverrides);
|
|
37561
|
-
const requestedMax = options.maxTokens ?? maxOutputTokensFor$4(options.model, contextWindow);
|
|
37562
|
-
const inputCap = maxInputTokensForEntry(options.model, catalog);
|
|
37563
|
-
const promptLimit = inputCap === void 0 ? contextWindow : Math.min(contextWindow, inputCap);
|
|
37564
|
-
const boundedOptions = {
|
|
37565
|
-
...requestOptions,
|
|
37566
|
-
maxTokens: clampOutputToContext$1(requestedMax, promptLimit, estimatedInputTokens(requestOptions))
|
|
37567
|
-
};
|
|
37568
|
-
const carriesVideo = requestHasVideo(requestOptions);
|
|
37569
|
-
const bodyLimit = carriesVideo ? MAX_VIDEO_MESSAGE_BODY_BYTES : maxRequestBodyBytesFor(kimiCodeModelDef(entry?.id ?? options.model), process.env);
|
|
37570
|
-
/**
|
|
37571
|
-
* Run the greedy body-fit loop for ONE set of media references.
|
|
37572
|
-
*
|
|
37573
|
-
* A function rather than a loop in place because the same loop now serves
|
|
37574
|
-
* every build of one request: the pre-upload MEASUREMENT (placeholder
|
|
37575
|
-
* references), the real reference build after the upload, and the all-inline
|
|
37576
|
-
* rebuild the missing-resource fallback re-sends.
|
|
37577
|
-
*
|
|
37578
|
-
* Every call restarts from `boundedOptions`. That is load bearing rather than
|
|
37579
|
-
* tidy: the inline fallback has to re-fit the inline body from scratch and
|
|
37580
|
-
* stay inside the same ceiling instead of inheriting an omission the
|
|
37581
|
-
* reference path made to fit a different, much smaller body.
|
|
37582
|
-
*/
|
|
37583
|
-
const buildBodyWithinLimit = (media) => {
|
|
37584
|
-
let buildOptions = boundedOptions;
|
|
37585
|
-
for (let attempt = 0;; attempt++) {
|
|
37586
|
-
const built = buildRequest(buildOptions, wire, images, void 0, media);
|
|
37587
|
-
const body = JSON.stringify(built);
|
|
37588
|
-
const bytes = Buffer.byteLength(body, "utf8");
|
|
37589
|
-
if (bytes <= bodyLimit) return {
|
|
37590
|
-
body,
|
|
37591
|
-
options: buildOptions
|
|
37592
|
-
};
|
|
37593
|
-
const context = () => ({
|
|
37594
|
-
droppedImages: countOmitted(buildOptions, requestOptions, images),
|
|
37595
|
-
sendableImagesRemaining: requestImagePayloadLengths(buildOptions, images).length,
|
|
37596
|
-
excessBytes: bytes - bodyLimit
|
|
37597
|
-
});
|
|
37598
|
-
if (attempt > MAX_BODY_FIT_ATTEMPTS || !requestHasSendableImage(buildOptions, images)) {
|
|
37599
|
-
assertRequestBodyFits$1(built, carriesVideo, context(), bodyLimit);
|
|
37600
|
-
throw new Error("unreachable: assertRequestBodyFits returns or throws");
|
|
37601
|
-
}
|
|
37602
|
-
const drop = dropOldestImagesUntilFits(buildOptions, images, bytes - bodyLimit, requestOptions);
|
|
37603
|
-
if (drop.dropped === 0) {
|
|
37604
|
-
assertRequestBodyFits$1(built, carriesVideo, context(), bodyLimit);
|
|
37605
|
-
throw new Error("unreachable: assertRequestBodyFits returns or throws");
|
|
37606
|
-
}
|
|
37607
|
-
buildOptions = drop.options;
|
|
37608
|
-
}
|
|
37609
|
-
};
|
|
37610
|
-
/**
|
|
37611
|
-
* The measured build for the reference path, done at most once.
|
|
37612
|
-
*
|
|
37613
|
-
* Held across rotation attempts because it is account-independent: it decides
|
|
37614
|
-
* whether this request can be sent at all, and that answer cannot change
|
|
37615
|
-
* because a different account answered.
|
|
37616
|
-
*
|
|
37617
|
-
* The OPTIONS travel with the body because they, not the plan, name the media
|
|
37618
|
-
* this request still carries: see {@link mediaAttachmentIdsIn}.
|
|
37619
|
-
*/
|
|
37620
|
-
let measured;
|
|
37621
|
-
let sentBuild;
|
|
37622
|
-
const pool = this.accountPool;
|
|
37623
|
-
const tried = /* @__PURE__ */ new Set();
|
|
37624
|
-
let servedByAccountId;
|
|
37625
|
-
let response;
|
|
37626
|
-
while (true) {
|
|
37627
|
-
let apiCredentials;
|
|
37628
|
-
let accountId;
|
|
37629
|
-
if (pool === null) try {
|
|
37630
|
-
apiCredentials = await ensureAccessToken$1(this.store, {
|
|
37631
|
-
fetchFn,
|
|
37632
|
-
signal,
|
|
37633
|
-
maxAttempts: MODEL_REFRESH_MAX_ATTEMPTS
|
|
37634
|
-
});
|
|
37635
|
-
} catch (error) {
|
|
37636
|
-
throw credentialFailure(error, signal);
|
|
37637
|
-
}
|
|
37638
|
-
else {
|
|
37639
|
-
let effective;
|
|
37640
|
-
try {
|
|
37641
|
-
effective = await pool.getEffectiveCredential(tried, fetchFn, { maxAttempts: MODEL_REFRESH_MAX_ATTEMPTS });
|
|
37642
|
-
} catch (error) {
|
|
37643
|
-
throw credentialFailure(error, signal);
|
|
37644
|
-
}
|
|
37645
|
-
accountId = effective.account.id;
|
|
37646
|
-
servedByAccountId = accountId;
|
|
37647
|
-
tried.add(accountId);
|
|
37648
|
-
apiCredentials = effective.credentials;
|
|
37649
|
-
}
|
|
37650
|
-
const region = apiCredentials.region ?? await resolveRegion();
|
|
37651
|
-
const base = (apiCredentials.baseUrl ?? codingBaseUrl(region)).replace(/\/+$/, "");
|
|
37652
|
-
const endpoint = wire === "anthropic" ? `${base}/v1/messages?beta=true` : `${base}/v1/chat/completions`;
|
|
37653
|
-
const headers = await modelRequestHeaders$1(apiCredentials.accessToken, wire);
|
|
37805
|
+
const sessionKey = typeof options.sessionId === "string" ? options.sessionId.trim() : "";
|
|
37806
|
+
const mainHeaderRelease = !sideChannel && sessionKey !== "" ? registerInFlightMainSessionHeader(sessionKey) : () => void 0;
|
|
37807
|
+
try {
|
|
37808
|
+
const catalog = await this.catalog().catch(() => []);
|
|
37809
|
+
const wire = wireForCatalogEntry(options.model, catalog);
|
|
37810
|
+
const entry = catalog.find((model) => model.id === options.model);
|
|
37811
|
+
const images = await resolveRequestImages$2(normalized, this.options.attachments, signal, KIMI_CODE_REQUEST_IMAGE_MAX_EDGE);
|
|
37812
|
+
const priorDroppedKeys = getSessionDroppedImageKeys(options.sessionId, options.sessionId === void 0 ? void 0 : sessionCacheState.read(options.sessionId)?.budgetDropped);
|
|
37813
|
+
const stickyOffload = offloadRequestImagesWithStickyBudget(normalized, maxRequestImageBytes$1(), lowRequestImageBytes(), images, priorDroppedKeys);
|
|
37814
|
+
const imageTrimmed = stickyOffload.options;
|
|
37815
|
+
if (!sideChannel) markPendingImageOffload(options.sessionId, stickyOffload.newlyDroppedKeys.length);
|
|
37816
|
+
const gatedEffort = coerceThinkingEffort(options.model, mapReasoningEffort(options.reasoningEffort === void 0 || options.reasoningEffort === null ? void 0 : String(options.reasoningEffort)));
|
|
37817
|
+
if (gatedEffort.coercedFrom !== null) recordThinkingCoercion({
|
|
37818
|
+
model: options.model,
|
|
37819
|
+
wire,
|
|
37820
|
+
from: gatedEffort.coercedFrom,
|
|
37821
|
+
to: gatedEffort.effort
|
|
37822
|
+
}, options.sessionId);
|
|
37823
|
+
const videoTrimmed = offloadOldestRequestVideos(imageTrimmed, MAX_REQUEST_VIDEO_BYTES$1);
|
|
37824
|
+
const requestOptions = gatedEffort.effort === void 0 ? videoTrimmed : {
|
|
37825
|
+
...videoTrimmed,
|
|
37826
|
+
reasoningEffort: ReasoningEffortId(gatedEffort.effort)
|
|
37827
|
+
};
|
|
37828
|
+
const videos = await resolveRequestVideos(requestOptions, this.options.videos, signal);
|
|
37829
|
+
const settings = await this.settings();
|
|
37830
|
+
const modalities = inputModalitiesForEntry(options.model, catalog);
|
|
37654
37831
|
/**
|
|
37655
|
-
*
|
|
37832
|
+
* Everything every attempt shares: the media reads and the capability gates.
|
|
37656
37833
|
*
|
|
37657
|
-
*
|
|
37658
|
-
*
|
|
37659
|
-
*
|
|
37660
|
-
* never used, here or anywhere a cache key is built.
|
|
37834
|
+
* Account-specific references are merged on top of this per attempt, so the
|
|
37835
|
+
* gate is applied once and a reference can never be cited for a modality the
|
|
37836
|
+
* model does not declare.
|
|
37661
37837
|
*/
|
|
37662
|
-
const
|
|
37663
|
-
|
|
37664
|
-
|
|
37665
|
-
|
|
37666
|
-
}
|
|
37667
|
-
|
|
37668
|
-
|
|
37669
|
-
|
|
37670
|
-
|
|
37671
|
-
|
|
37672
|
-
|
|
37673
|
-
|
|
37674
|
-
|
|
37675
|
-
|
|
37676
|
-
|
|
37677
|
-
|
|
37678
|
-
|
|
37679
|
-
|
|
37838
|
+
const baseMedia = {
|
|
37839
|
+
videos,
|
|
37840
|
+
videoAccepted: modalities.includes("video"),
|
|
37841
|
+
messageTools: dynamicToolsForEntry(options.model, catalog)
|
|
37842
|
+
};
|
|
37843
|
+
/**
|
|
37844
|
+
* Which readable occurrences the reference path would cover.
|
|
37845
|
+
*
|
|
37846
|
+
* Decided here so the body-fit guard can measure the body that will actually
|
|
37847
|
+
* be sent; nothing is uploaded, and no network is touched, until that guard
|
|
37848
|
+
* has accepted the request (see the rotation loop).
|
|
37849
|
+
*/
|
|
37850
|
+
const planned = planMediaReferences({
|
|
37851
|
+
wire,
|
|
37852
|
+
imageAccepted: modalities.includes("image"),
|
|
37853
|
+
videoAccepted: baseMedia.videoAccepted === true,
|
|
37854
|
+
images,
|
|
37855
|
+
videos
|
|
37680
37856
|
});
|
|
37681
|
-
|
|
37682
|
-
|
|
37683
|
-
const
|
|
37684
|
-
|
|
37685
|
-
|
|
37686
|
-
|
|
37687
|
-
|
|
37688
|
-
|
|
37689
|
-
|
|
37857
|
+
const contextWindow = this.contextWindowFor(options.model, entry, settings.contextWindowOverrides);
|
|
37858
|
+
const requestedMax = options.maxTokens ?? maxOutputTokensFor$4(options.model, contextWindow);
|
|
37859
|
+
const inputCap = maxInputTokensForEntry(options.model, catalog);
|
|
37860
|
+
const promptLimit = inputCap === void 0 ? contextWindow : Math.min(contextWindow, inputCap);
|
|
37861
|
+
const boundedOptions = {
|
|
37862
|
+
...requestOptions,
|
|
37863
|
+
maxTokens: clampOutputToContext$1(requestedMax, promptLimit, estimatedInputTokens(requestOptions))
|
|
37864
|
+
};
|
|
37865
|
+
const carriesVideo = requestHasVideo(requestOptions);
|
|
37866
|
+
const bodyLimit = carriesVideo ? MAX_VIDEO_MESSAGE_BODY_BYTES : maxRequestBodyBytesFor(kimiCodeModelDef(entry?.id ?? options.model), process.env);
|
|
37867
|
+
/**
|
|
37868
|
+
* Run the greedy body-fit loop for ONE set of media references.
|
|
37869
|
+
*
|
|
37870
|
+
* A function rather than a loop in place because the same loop now serves
|
|
37871
|
+
* every build of one request: the pre-upload MEASUREMENT (placeholder
|
|
37872
|
+
* references), the real reference build after the upload, and the all-inline
|
|
37873
|
+
* rebuild the missing-resource fallback re-sends.
|
|
37874
|
+
*
|
|
37875
|
+
* Every call restarts from `boundedOptions`. That is load bearing rather than
|
|
37876
|
+
* tidy: the inline fallback has to re-fit the inline body from scratch and
|
|
37877
|
+
* stay inside the same ceiling instead of inheriting an omission the
|
|
37878
|
+
* reference path made to fit a different, much smaller body.
|
|
37879
|
+
*/
|
|
37880
|
+
const buildBodyWithinLimit = (media) => {
|
|
37881
|
+
let buildOptions = boundedOptions;
|
|
37882
|
+
for (let attempt = 0;; attempt++) {
|
|
37883
|
+
const built = buildRequest(buildOptions, wire, images, void 0, media);
|
|
37884
|
+
const body = JSON.stringify(built);
|
|
37885
|
+
const bytes = Buffer.byteLength(body, "utf8");
|
|
37886
|
+
if (bytes <= bodyLimit) return {
|
|
37887
|
+
body,
|
|
37888
|
+
options: buildOptions
|
|
37889
|
+
};
|
|
37890
|
+
const context = () => ({
|
|
37891
|
+
droppedImages: countOmitted(buildOptions, requestOptions, images),
|
|
37892
|
+
sendableImagesRemaining: requestImagePayloadLengths(buildOptions, images).length,
|
|
37893
|
+
excessBytes: bytes - bodyLimit
|
|
37690
37894
|
});
|
|
37691
|
-
|
|
37692
|
-
|
|
37693
|
-
|
|
37694
|
-
|
|
37695
|
-
|
|
37895
|
+
if (attempt > MAX_BODY_FIT_ATTEMPTS || !requestHasSendableImage(buildOptions, images)) {
|
|
37896
|
+
assertRequestBodyFits$1(built, carriesVideo, context(), bodyLimit);
|
|
37897
|
+
throw new Error("unreachable: assertRequestBodyFits returns or throws");
|
|
37898
|
+
}
|
|
37899
|
+
const drop = dropOldestImagesUntilFits(buildOptions, images, bytes - bodyLimit, requestOptions);
|
|
37900
|
+
if (drop.dropped === 0) {
|
|
37901
|
+
assertRequestBodyFits$1(built, carriesVideo, context(), bodyLimit);
|
|
37902
|
+
throw new Error("unreachable: assertRequestBodyFits returns or throws");
|
|
37903
|
+
}
|
|
37904
|
+
buildOptions = drop.options;
|
|
37696
37905
|
}
|
|
37697
37906
|
};
|
|
37698
|
-
|
|
37699
|
-
|
|
37700
|
-
|
|
37701
|
-
|
|
37702
|
-
|
|
37703
|
-
|
|
37704
|
-
|
|
37705
|
-
|
|
37706
|
-
}
|
|
37707
|
-
|
|
37708
|
-
|
|
37709
|
-
|
|
37710
|
-
|
|
37711
|
-
|
|
37712
|
-
|
|
37713
|
-
|
|
37714
|
-
|
|
37715
|
-
|
|
37716
|
-
|
|
37717
|
-
|
|
37907
|
+
/**
|
|
37908
|
+
* The measured build for the reference path, done at most once.
|
|
37909
|
+
*
|
|
37910
|
+
* Held across rotation attempts because it is account-independent: it decides
|
|
37911
|
+
* whether this request can be sent at all, and that answer cannot change
|
|
37912
|
+
* because a different account answered.
|
|
37913
|
+
*
|
|
37914
|
+
* The OPTIONS travel with the body because they, not the plan, name the media
|
|
37915
|
+
* this request still carries: see {@link mediaAttachmentIdsIn}.
|
|
37916
|
+
*/
|
|
37917
|
+
let measured;
|
|
37918
|
+
let sentBuild;
|
|
37919
|
+
const pool = this.accountPool;
|
|
37920
|
+
const tried = /* @__PURE__ */ new Set();
|
|
37921
|
+
let servedByAccountId;
|
|
37922
|
+
let response;
|
|
37923
|
+
while (true) {
|
|
37924
|
+
let apiCredentials;
|
|
37925
|
+
let accountId;
|
|
37926
|
+
if (pool === null) try {
|
|
37927
|
+
apiCredentials = await ensureAccessToken$1(this.store, {
|
|
37928
|
+
fetchFn,
|
|
37929
|
+
signal,
|
|
37930
|
+
maxAttempts: MODEL_REFRESH_MAX_ATTEMPTS
|
|
37931
|
+
});
|
|
37932
|
+
} catch (error) {
|
|
37933
|
+
throw credentialFailure(error, signal);
|
|
37934
|
+
}
|
|
37935
|
+
else {
|
|
37936
|
+
let effective;
|
|
37937
|
+
try {
|
|
37938
|
+
effective = await pool.getEffectiveCredential(tried, fetchFn, { maxAttempts: MODEL_REFRESH_MAX_ATTEMPTS });
|
|
37939
|
+
} catch (error) {
|
|
37940
|
+
throw credentialFailure(error, signal);
|
|
37941
|
+
}
|
|
37942
|
+
accountId = effective.account.id;
|
|
37943
|
+
servedByAccountId = accountId;
|
|
37944
|
+
tried.add(accountId);
|
|
37945
|
+
apiCredentials = effective.credentials;
|
|
37946
|
+
}
|
|
37947
|
+
const region = apiCredentials.region ?? await resolveRegion();
|
|
37948
|
+
const base = (apiCredentials.baseUrl ?? codingBaseUrl(region)).replace(/\/+$/, "");
|
|
37949
|
+
const endpoint = wire === "anthropic" ? `${base}/v1/messages?beta=true` : `${base}/v1/chat/completions`;
|
|
37950
|
+
const headers = await modelRequestHeaders$1(apiCredentials.accessToken, wire);
|
|
37951
|
+
/**
|
|
37952
|
+
* Which account's file store this attempt writes to and cites from.
|
|
37953
|
+
*
|
|
37954
|
+
* A pooled account's own id, because that is the only identity a reference
|
|
37955
|
+
* may be cached under; a single stored credential uses the user id its
|
|
37956
|
+
* sign-in reported, or a constant when it reported none. The access token is
|
|
37957
|
+
* never used, here or anywhere a cache key is built.
|
|
37958
|
+
*/
|
|
37959
|
+
const accountScope = accountId ?? legacyAccountScope(apiCredentials);
|
|
37960
|
+
if (measured === void 0) measured = buildBodyWithinLimit(planned.length === 0 ? baseMedia : {
|
|
37961
|
+
...baseMedia,
|
|
37962
|
+
...plannedReferenceMaps(planned)
|
|
37963
|
+
});
|
|
37964
|
+
const stillSent = mediaAttachmentIdsIn(measured.options);
|
|
37965
|
+
const uploadable = planned.filter((item) => stillSent.has(item.attachmentId));
|
|
37966
|
+
const uploads = uploadable.length === 0 ? void 0 : await this.uploadPlannedReferences({
|
|
37967
|
+
planned: uploadable,
|
|
37968
|
+
accessToken: apiCredentials.accessToken,
|
|
37718
37969
|
baseUrl: base,
|
|
37719
37970
|
accountScope,
|
|
37720
|
-
|
|
37721
|
-
data: item.data
|
|
37971
|
+
signal
|
|
37722
37972
|
});
|
|
37723
|
-
let
|
|
37724
|
-
|
|
37725
|
-
|
|
37726
|
-
|
|
37727
|
-
|
|
37973
|
+
let finalBuild = uploads === void 0 ? planned.length === 0 ? measured : buildBodyWithinLimit(baseMedia) : buildBodyWithinLimit({
|
|
37974
|
+
...baseMedia,
|
|
37975
|
+
imageReferences: uploads.imageReferences,
|
|
37976
|
+
videoReferences: uploads.videoReferences
|
|
37977
|
+
});
|
|
37978
|
+
sentBuild = finalBuild;
|
|
37979
|
+
let body = finalBuild.body;
|
|
37980
|
+
const post = async (payload) => {
|
|
37981
|
+
try {
|
|
37982
|
+
const response = await fetchWithContextBudgetRecovery(fetchFn)(endpoint, {
|
|
37983
|
+
method: "POST",
|
|
37984
|
+
headers,
|
|
37985
|
+
body: payload,
|
|
37986
|
+
signal
|
|
37987
|
+
});
|
|
37988
|
+
activity?.pulse();
|
|
37989
|
+
return response;
|
|
37990
|
+
} catch (error) {
|
|
37991
|
+
if (signal.aborted) throw new LlmError("Kimi Code request aborted", "ABORTED", { cause: error });
|
|
37992
|
+
throw new LlmError(`Kimi Code request failed: ${error instanceof Error ? error.message : String(error)}`, "TRANSPORT", { cause: error });
|
|
37993
|
+
}
|
|
37994
|
+
};
|
|
37995
|
+
if (sideChannel && sessionKey !== "") {
|
|
37996
|
+
const pendingMain = inFlightMainSessionHeaders.get(sessionKey);
|
|
37997
|
+
if (pendingMain !== void 0) {
|
|
37998
|
+
await raceColdStartLeader(pendingMain, signal);
|
|
37999
|
+
if (signal.aborted) throw new LlmError("Kimi Code request aborted", "ABORTED");
|
|
38000
|
+
}
|
|
37728
38001
|
}
|
|
37729
|
-
|
|
37730
|
-
|
|
37731
|
-
|
|
37732
|
-
|
|
37733
|
-
|
|
37734
|
-
|
|
38002
|
+
const coldGroupKey = sideChannel ? void 0 : coldStartGroupKey(region, requestOptions);
|
|
38003
|
+
const coldGate = coldGroupKey === void 0 ? void 0 : await acquireColdStartWarmup(coldGroupKey, signal);
|
|
38004
|
+
const burstPacingRelease = !sideChannel && sessionKey !== "" && getCacheStats(sessionKey).requests === 0 && isSubagentBurstCandidate(requestOptions) ? await acquireSubagentBurstPacing(`${accountScope}:${options.model}`, signal) : () => void 0;
|
|
38005
|
+
let coldGateSettled = false;
|
|
38006
|
+
const settleColdGate = (warm) => {
|
|
38007
|
+
if (coldGateSettled) return;
|
|
38008
|
+
coldGateSettled = true;
|
|
38009
|
+
coldGate?.release();
|
|
38010
|
+
burstPacingRelease();
|
|
38011
|
+
mainHeaderRelease();
|
|
38012
|
+
if (warm) coldGate?.markWarm();
|
|
38013
|
+
};
|
|
38014
|
+
response = await post(body).then((value) => {
|
|
38015
|
+
settleColdGate(value.ok);
|
|
38016
|
+
return value;
|
|
38017
|
+
}, (error) => {
|
|
38018
|
+
settleColdGate(false);
|
|
38019
|
+
throw error;
|
|
38020
|
+
});
|
|
38021
|
+
let detail = response.ok ? "" : (await response.text().catch(() => "")).slice(0, 2e3);
|
|
38022
|
+
if (response.status === 404 && uploads !== void 0 && uploads.cited.length > 0 && isMissingResourceBody(detail)) {
|
|
38023
|
+
const uploader = this.mediaFiles();
|
|
38024
|
+
for (const item of uploads.cited) uploader.forget({
|
|
38025
|
+
baseUrl: base,
|
|
38026
|
+
accountScope,
|
|
38027
|
+
mediaType: item.mediaType,
|
|
38028
|
+
data: item.data
|
|
38029
|
+
});
|
|
38030
|
+
let inlineBuild;
|
|
38031
|
+
try {
|
|
38032
|
+
inlineBuild = buildBodyWithinLimit(baseMedia);
|
|
38033
|
+
} catch {
|
|
38034
|
+
inlineBuild = void 0;
|
|
38035
|
+
}
|
|
38036
|
+
if (inlineBuild !== void 0) {
|
|
38037
|
+
finalBuild = inlineBuild;
|
|
38038
|
+
sentBuild = inlineBuild;
|
|
38039
|
+
body = inlineBuild.body;
|
|
38040
|
+
response = await post(body);
|
|
38041
|
+
detail = response.ok ? "" : (await response.text().catch(() => "")).slice(0, 2e3);
|
|
38042
|
+
}
|
|
37735
38043
|
}
|
|
37736
|
-
|
|
37737
|
-
|
|
37738
|
-
|
|
37739
|
-
|
|
37740
|
-
|
|
37741
|
-
|
|
37742
|
-
|
|
37743
|
-
|
|
37744
|
-
|
|
37745
|
-
if (
|
|
37746
|
-
|
|
37747
|
-
|
|
37748
|
-
if (
|
|
37749
|
-
|
|
37750
|
-
|
|
37751
|
-
|
|
38044
|
+
if (response.ok) break;
|
|
38045
|
+
const failure = classifyKimiFailure(response.status, detail);
|
|
38046
|
+
const after = response.status === 429 ? retryAfterMs$1(response.headers) : void 0;
|
|
38047
|
+
if (pool !== null && accountId !== void 0) {
|
|
38048
|
+
const planScoped = response.status === 429 && matchesAny$1(detail, ENTITLEMENT_PATTERNS);
|
|
38049
|
+
if (response.status === 429 && !planScoped && !failure.upstreamOverload) {
|
|
38050
|
+
const cooldownMs = failure.accountScoped ? accountLimitCooldownMs(detail) : after ?? POOL_COOLDOWN_MS$5;
|
|
38051
|
+
await pool.markCooldown(accountId, cooldownMs, `${PROVIDER_NAME$8} 429`).catch(() => void 0);
|
|
38052
|
+
if (await pool.hasAnotherAvailableAccount(tried)) continue;
|
|
38053
|
+
} else if (failure.code === "INVALID_CREDENTIAL") {
|
|
38054
|
+
await pool.markAuthFailed(accountId, failure.message).catch(() => void 0);
|
|
38055
|
+
if (await pool.hasAnotherAvailableAccount(tried)) continue;
|
|
38056
|
+
} else if (failure.accountScoped) {
|
|
38057
|
+
await pool.markCooldown(accountId, accountLimitCooldownMs(detail), `${PROVIDER_NAME$8} 403`).catch(() => void 0);
|
|
38058
|
+
if (await pool.hasAnotherAvailableAccount(tried)) continue;
|
|
38059
|
+
}
|
|
37752
38060
|
}
|
|
38061
|
+
throw new LlmError(failure.message, failure.code, {
|
|
38062
|
+
status: response.status,
|
|
38063
|
+
...after === void 0 ? {} : { providerRetryAfterMs: after }
|
|
38064
|
+
});
|
|
37753
38065
|
}
|
|
37754
|
-
|
|
37755
|
-
|
|
37756
|
-
|
|
37757
|
-
|
|
37758
|
-
|
|
37759
|
-
|
|
37760
|
-
|
|
37761
|
-
|
|
37762
|
-
|
|
37763
|
-
|
|
37764
|
-
|
|
37765
|
-
|
|
37766
|
-
|
|
37767
|
-
|
|
37768
|
-
|
|
37769
|
-
|
|
37770
|
-
|
|
37771
|
-
|
|
37772
|
-
|
|
37773
|
-
|
|
37774
|
-
|
|
37775
|
-
|
|
37776
|
-
|
|
37777
|
-
|
|
37778
|
-
|
|
37779
|
-
|
|
37780
|
-
|
|
37781
|
-
|
|
37782
|
-
|
|
37783
|
-
* already seen repeats just as badly as text it has already read, so the
|
|
37784
|
-
* conservative reading is the one that cannot be wrong. It is what keeps the
|
|
37785
|
-
* in-band reclassification below pre-output only.
|
|
37786
|
-
*/
|
|
37787
|
-
let outputStarted = false;
|
|
37788
|
-
try {
|
|
38066
|
+
if (sentBuild !== void 0 && !sideChannel) {
|
|
38067
|
+
const requestImageBytes = requestImagePayloadLengths(sentBuild.options, images).reduce((sum, bytes) => sum + bytes, 0);
|
|
38068
|
+
const trimmedKeys = new Set(collectRemainingImageKeys(imageTrimmed, images));
|
|
38069
|
+
const sentKeys = new Set(collectRemainingImageKeys(sentBuild.options, images));
|
|
38070
|
+
const bodyFitDroppedKeys = [...trimmedKeys].filter((key) => !sentKeys.has(key));
|
|
38071
|
+
const allDroppedKeys = /* @__PURE__ */ new Set([...stickyOffload.allDroppedKeys, ...bodyFitDroppedKeys]);
|
|
38072
|
+
recordSessionImageBudget(options.sessionId, {
|
|
38073
|
+
allDroppedKeys,
|
|
38074
|
+
newlyDroppedCount: stickyOffload.newlyDroppedKeys.length + bodyFitDroppedKeys.length,
|
|
38075
|
+
requestImageBytes,
|
|
38076
|
+
droppedImages: stickyOffload.droppedCount + bodyFitDroppedKeys.length
|
|
38077
|
+
}, requestOptions);
|
|
38078
|
+
}
|
|
38079
|
+
if (response.body === null) throw new LlmError("Kimi Code returned an empty response body", "PROVIDER_ERROR");
|
|
38080
|
+
const reader = response.body.getReader();
|
|
38081
|
+
const decoder = new TextDecoder();
|
|
38082
|
+
const state = createStreamState$5(wire, requestOptions.sessionId, sideChannel);
|
|
38083
|
+
applyCacheWriteTiers(state, response.headers);
|
|
38084
|
+
if (!sideChannel) markSessionActive(requestOptions.sessionId);
|
|
38085
|
+
let buffer = "";
|
|
38086
|
+
/**
|
|
38087
|
+
* Set the moment ANY chunk reaches the caller, and never cleared.
|
|
38088
|
+
*
|
|
38089
|
+
* Deliberately broader than "text arrived": a block-start the caller has
|
|
38090
|
+
* already seen repeats just as badly as text it has already read, so the
|
|
38091
|
+
* conservative reading is the one that cannot be wrong. It is what keeps the
|
|
38092
|
+
* in-band reclassification below pre-output only.
|
|
38093
|
+
*/
|
|
38094
|
+
let outputStarted = false;
|
|
37789
38095
|
try {
|
|
37790
|
-
|
|
37791
|
-
|
|
37792
|
-
|
|
37793
|
-
|
|
37794
|
-
|
|
37795
|
-
|
|
37796
|
-
|
|
37797
|
-
|
|
37798
|
-
|
|
37799
|
-
|
|
37800
|
-
|
|
37801
|
-
|
|
38096
|
+
try {
|
|
38097
|
+
while (true) {
|
|
38098
|
+
const { done, value } = await reader.read();
|
|
38099
|
+
if (done) break;
|
|
38100
|
+
if (value !== void 0 && value.byteLength > 0) activity?.pulse();
|
|
38101
|
+
buffer += decoder.decode(value, { stream: true });
|
|
38102
|
+
const lines = buffer.split("\n");
|
|
38103
|
+
buffer = lines.pop() ?? "";
|
|
38104
|
+
for (const line of lines) {
|
|
38105
|
+
activity?.pulse();
|
|
38106
|
+
for (const chunk of processLine(line, state, wire)) {
|
|
38107
|
+
outputStarted = true;
|
|
38108
|
+
yield chunk;
|
|
38109
|
+
}
|
|
38110
|
+
if (state.finished) return;
|
|
37802
38111
|
}
|
|
37803
|
-
if (state.finished) return;
|
|
37804
38112
|
}
|
|
38113
|
+
buffer += decoder.decode();
|
|
38114
|
+
if (buffer.trim() !== "") for (const line of buffer.split("\n")) for (const chunk of processLine(line, state, wire)) {
|
|
38115
|
+
outputStarted = true;
|
|
38116
|
+
yield chunk;
|
|
38117
|
+
}
|
|
38118
|
+
if (state.finished) return;
|
|
38119
|
+
assertStreamComplete$5(state);
|
|
38120
|
+
for (const chunk of closeStream$4(state, servedByAccountId)) {
|
|
38121
|
+
outputStarted = true;
|
|
38122
|
+
yield chunk;
|
|
38123
|
+
}
|
|
38124
|
+
} catch (error) {
|
|
38125
|
+
const inBand = state.streamError;
|
|
38126
|
+
if (error instanceof LlmError && !outputStarted && error.code === "PROVIDER_ERROR" && inBand !== void 0) throw await this.inBandStreamFailure(error, inBand, servedByAccountId);
|
|
38127
|
+
throw streamFailure(error, signal);
|
|
37805
38128
|
}
|
|
37806
|
-
|
|
37807
|
-
if (
|
|
37808
|
-
|
|
37809
|
-
yield chunk;
|
|
37810
|
-
}
|
|
37811
|
-
if (state.finished) return;
|
|
37812
|
-
assertStreamComplete$5(state);
|
|
37813
|
-
for (const chunk of closeStream$4(state, servedByAccountId)) {
|
|
37814
|
-
outputStarted = true;
|
|
37815
|
-
yield chunk;
|
|
37816
|
-
}
|
|
37817
|
-
} catch (error) {
|
|
37818
|
-
const inBand = state.streamError;
|
|
37819
|
-
if (error instanceof LlmError && !outputStarted && error.code === "PROVIDER_ERROR" && inBand !== void 0) throw await this.inBandStreamFailure(error, inBand, servedByAccountId);
|
|
37820
|
-
throw streamFailure(error, signal);
|
|
38129
|
+
} finally {
|
|
38130
|
+
if (state.sawUsage && !sideChannel) noteSessionCacheState(requestOptions.sessionId ?? state.sessionId, state);
|
|
38131
|
+
reader.cancel().catch(() => void 0);
|
|
37821
38132
|
}
|
|
37822
38133
|
} finally {
|
|
37823
|
-
|
|
37824
|
-
reader.cancel().catch(() => void 0);
|
|
38134
|
+
mainHeaderRelease();
|
|
37825
38135
|
}
|
|
37826
38136
|
}
|
|
37827
38137
|
/**
|
|
@@ -95427,12 +95737,19 @@ function messageText(data) {
|
|
|
95427
95737
|
return "";
|
|
95428
95738
|
}
|
|
95429
95739
|
/**
|
|
95430
|
-
*
|
|
95740
|
+
* 最新用户消息是否明确属于导航任务,或当前会话正处于只读计划模式(`plan/mode` `active: true`)。
|
|
95431
95741
|
* @param agent - 工具执行事件里的 agent。
|
|
95432
|
-
* @returns
|
|
95742
|
+
* @returns 明确导航(无源码工作信号)或处于只读计划模式时为 true。
|
|
95433
95743
|
*/
|
|
95434
95744
|
function cacheWarmupNavigationIntentOf(agent) {
|
|
95435
95745
|
const events = eventsOf(agent);
|
|
95746
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
95747
|
+
const event = events[index];
|
|
95748
|
+
if (event.type === "plan/mode") {
|
|
95749
|
+
if ((typeof event.data === "object" && event.data !== null ? event.data : void 0)?.active === true) return true;
|
|
95750
|
+
break;
|
|
95751
|
+
}
|
|
95752
|
+
}
|
|
95436
95753
|
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
95437
95754
|
const event = events[index];
|
|
95438
95755
|
if (event.type !== "user/message") continue;
|
|
@@ -95721,7 +96038,13 @@ function installCacheWarmup(ctx, options) {
|
|
|
95721
96038
|
const ptcRetrieval = isPtcTransportName(exec.name) && settings.retrievalEnabled === true;
|
|
95722
96039
|
if (sourceTool && !settings.enabled && !ptcRetrieval) return decision;
|
|
95723
96040
|
if (retrievalTool && settings.retrievalEnabled !== true) return decision;
|
|
95724
|
-
const
|
|
96041
|
+
const blocks = decision.content ?? result.content;
|
|
96042
|
+
if (blocks.length === 0) return decision;
|
|
96043
|
+
const baseNavigation = retrievalTool || ptcRetrieval ? cacheWarmupNavigationIntentOf(exec.agent) : void 0;
|
|
96044
|
+
const isOversizedSearchDump = (exec.name === "grep" || exec.name === "glob") && blocks.reduce((sum, block) => {
|
|
96045
|
+
return sum + (typeof block === "object" && block !== null && typeof block.text === "string" ? block.text : "").length;
|
|
96046
|
+
}, 0) >= 2e4;
|
|
96047
|
+
const navigation = baseNavigation === true || isOversizedSearchDump ? true : baseNavigation;
|
|
95725
96048
|
if (retrievalTool && navigation !== true) return decision;
|
|
95726
96049
|
if (sourceTool && !settings.enabled && navigation !== true) return decision;
|
|
95727
96050
|
const route = cacheWarmupRouteOf(exec.agent);
|
|
@@ -95730,8 +96053,6 @@ function installCacheWarmup(ctx, options) {
|
|
|
95730
96053
|
if (sessionId === void 0) return decision;
|
|
95731
96054
|
const store = storeOf();
|
|
95732
96055
|
if (store === void 0) return decision;
|
|
95733
|
-
const blocks = decision.content ?? result.content;
|
|
95734
|
-
if (blocks.length === 0) return decision;
|
|
95735
96056
|
const content = blocks;
|
|
95736
96057
|
let durable;
|
|
95737
96058
|
try {
|