dsh-agy 0.4.1 → 0.4.3
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/README.md +5 -1
- package/lib/{accounts-BjdDqUq1.mjs → accounts-CB4blXpP.mjs} +10 -10
- package/lib/{accounts-BjdDqUq1.mjs.map → accounts-CB4blXpP.mjs.map} +1 -1
- package/lib/{catalog-BgdDVJEB.mjs → catalog-BRHHLLd5.mjs} +13 -3
- package/lib/{catalog-BgdDVJEB.mjs.map → catalog-BRHHLLd5.mjs.map} +1 -1
- package/lib/cli/index.mjs +6 -6
- package/lib/client.js +1479 -170
- package/lib/{constants-BpyRirvk.mjs → constants-CmA9wXNe.mjs} +3 -3
- package/lib/{constants-BpyRirvk.mjs.map → constants-CmA9wXNe.mjs.map} +1 -1
- package/lib/{exchange-LzEyxve1.mjs → exchange-C-tndv7j.mjs} +3 -3
- package/lib/{exchange-LzEyxve1.mjs.map → exchange-C-tndv7j.mjs.map} +1 -1
- package/lib/{import-CotRHNDk.mjs → import-SxPDjH3u.mjs} +4 -4
- package/lib/{import-CotRHNDk.mjs.map → import-SxPDjH3u.mjs.map} +1 -1
- package/lib/index.mjs +2 -2
- package/lib/{models-Qn0-sh6X.mjs → models-CjB0rXtI.mjs} +4 -4
- package/lib/{models-Qn0-sh6X.mjs.map → models-CjB0rXtI.mjs.map} +1 -1
- package/lib/parse-38V1zT1B.mjs +270 -0
- package/lib/parse-38V1zT1B.mjs.map +1 -0
- package/lib/{plugin-common-B7OSfnyl.mjs → plugin-common-D2NWMsX6.mjs} +10 -9
- package/lib/plugin-common-D2NWMsX6.mjs.map +1 -0
- package/lib/{proxy-DfaL73mL.mjs → proxy-BbuQX5U9.mjs} +28 -8
- package/lib/proxy-BbuQX5U9.mjs.map +1 -0
- package/lib/{quota-summary-adID82I2.mjs → quota-summary-DEA3P3Zh.mjs} +3 -3
- package/lib/{quota-summary-adID82I2.mjs.map → quota-summary-DEA3P3Zh.mjs.map} +1 -1
- package/lib/{translate-YhGDUuVa.mjs → translate-B9ZOauqw.mjs} +2 -2
- package/lib/{translate-YhGDUuVa.mjs.map → translate-B9ZOauqw.mjs.map} +1 -1
- package/lib/web/plugin.mjs +102 -8
- package/lib/web/plugin.mjs.map +1 -1
- package/package.json +4 -5
- package/lib/parse-Bs4rPkHA.mjs +0 -190
- package/lib/parse-Bs4rPkHA.mjs.map +0 -1
- package/lib/plugin-common-B7OSfnyl.mjs.map +0 -1
- package/lib/proxy-DfaL73mL.mjs.map +0 -1
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import * as DshLlm from "@deepseek-ai/dsh-llm";
|
|
3
|
+
//#region src/adapter/parse.ts
|
|
4
|
+
var parse_exports = /* @__PURE__ */ __exportAll({
|
|
5
|
+
UnmappedFinishReasonError: () => UnmappedFinishReasonError,
|
|
6
|
+
parseAgySse: () => parseAgySse,
|
|
7
|
+
parseSseDataLine: () => parseSseDataLine
|
|
8
|
+
});
|
|
9
|
+
const CallId = DshLlm.ToolCallId ?? DshLlm.CallId;
|
|
10
|
+
if (!CallId) throw new Error("dsh-llm: neither ToolCallId nor CallId found");
|
|
11
|
+
/**
|
|
12
|
+
* Parse one SSE `data:` line; returns null for `[DONE]` or empty lines.
|
|
13
|
+
* Accepts the `{"response": {...}}` envelope (daily endpoint wire shape) and
|
|
14
|
+
* the bare array/object shapes older clients emitted.
|
|
15
|
+
*/
|
|
16
|
+
function parseSseDataLine(line) {
|
|
17
|
+
const trimmed = line.trim();
|
|
18
|
+
if (!trimmed.startsWith("data:")) return null;
|
|
19
|
+
const data = trimmed.slice(5).trim();
|
|
20
|
+
if (data === "" || data === "[DONE]") return null;
|
|
21
|
+
const parsed = JSON.parse(data);
|
|
22
|
+
const root = parsed?.response ?? parsed;
|
|
23
|
+
return (Array.isArray(root) ? root[0] : root) ?? null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Extract an error body from a line that does not carry the SSE `data:`
|
|
27
|
+
* prefix. The upstream occasionally drops its `{"error":{...}}` vocabulary as
|
|
28
|
+
* a bare JSON line right before terminating the stream — the socket closes
|
|
29
|
+
* cleanly and nothing else surfaces the failure. Returns null for anything
|
|
30
|
+
* that is not a JSON object carrying an `error` object; SSE comments and
|
|
31
|
+
* event/id/retry fields stay skipped.
|
|
32
|
+
*/
|
|
33
|
+
function extractBareJsonError(line) {
|
|
34
|
+
const trimmed = line.trim();
|
|
35
|
+
if (!trimmed.startsWith("{")) return null;
|
|
36
|
+
try {
|
|
37
|
+
const parsed = JSON.parse(trimmed);
|
|
38
|
+
if (parsed.error && typeof parsed.error === "object") return parsed.error;
|
|
39
|
+
} catch {}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Thrown when the upstream ends a stream with a `finishReason` this parser
|
|
44
|
+
* does not map (SAFETY, RECITATION, MALFORMED_FUNCTION_CALL, ...). A silent
|
|
45
|
+
* default to `stop` presented policy-blocked and malformed-call turns as
|
|
46
|
+
* completed. This is a CONTENT-level verdict, not account health: the adapter
|
|
47
|
+
* reports it as `request-error` (no cooldown, no rotation), never
|
|
48
|
+
* `network-error`.
|
|
49
|
+
*/
|
|
50
|
+
var UnmappedFinishReasonError = class extends Error {
|
|
51
|
+
reason;
|
|
52
|
+
constructor(reason) {
|
|
53
|
+
super(`agy upstream ended the stream with unrecognized finishReason: ${reason}`);
|
|
54
|
+
this.reason = reason;
|
|
55
|
+
this.name = "UnmappedFinishReasonError";
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Map the upstream `finishReason` vocabulary onto DSH's. WHITELIST, not
|
|
60
|
+
* blacklist: the completable reasons map to their kinds and every other
|
|
61
|
+
* explicit reason throws. `FINISH_REASON_UNSPECIFIED` is granted `stop` — it
|
|
62
|
+
* carries no information, exactly like the absent field.
|
|
63
|
+
*/
|
|
64
|
+
function mapFinishReason(reason) {
|
|
65
|
+
switch (reason) {
|
|
66
|
+
case "MAX_TOKENS": return { kind: "max-tokens" };
|
|
67
|
+
case "STOP":
|
|
68
|
+
case "FINISH_REASON_UNSPECIFIED": return { kind: "stop" };
|
|
69
|
+
case "TOOL_CALLS":
|
|
70
|
+
case "FUNCTION_CALL": return { kind: "tool-calls" };
|
|
71
|
+
default: throw new UnmappedFinishReasonError(reason);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async function* parseAgySse(body, options = {}) {
|
|
75
|
+
const { signal } = options;
|
|
76
|
+
const reader = body.getReader();
|
|
77
|
+
const decoder = new TextDecoder();
|
|
78
|
+
let buffer = "";
|
|
79
|
+
let blockIndex = 0;
|
|
80
|
+
let finishReason = { kind: "stop" };
|
|
81
|
+
let sawDone = false;
|
|
82
|
+
let sawFinishReason = false;
|
|
83
|
+
let sawUsage = false;
|
|
84
|
+
let lastUsage = null;
|
|
85
|
+
let open = null;
|
|
86
|
+
const closeBlock = () => {
|
|
87
|
+
if (!open) return null;
|
|
88
|
+
const block = open.kind === "tool-call" ? {
|
|
89
|
+
type: "block-end",
|
|
90
|
+
index: blockIndex,
|
|
91
|
+
block: {
|
|
92
|
+
type: "tool-call",
|
|
93
|
+
id: CallId(open.id ?? `call-${blockIndex}`),
|
|
94
|
+
name: open.name ?? "",
|
|
95
|
+
arguments: open.arguments
|
|
96
|
+
}
|
|
97
|
+
} : {
|
|
98
|
+
type: "block-end",
|
|
99
|
+
index: blockIndex,
|
|
100
|
+
block: {
|
|
101
|
+
type: open.kind,
|
|
102
|
+
text: open.text
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
open = null;
|
|
106
|
+
blockIndex += 1;
|
|
107
|
+
return block;
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Ensure a block of the given kind is open, switching when needed.
|
|
111
|
+
* Returns chunks to yield (a closed block's end, then the new block's start).
|
|
112
|
+
* Callers MUST yield everything returned — dropping the end silently corrupts
|
|
113
|
+
* the block stream for DSH (verified: multi-tool turns and text→tool
|
|
114
|
+
* transitions lost their block-end).
|
|
115
|
+
*/
|
|
116
|
+
const ensureBlock = (kind, meta = {}) => {
|
|
117
|
+
const out = [];
|
|
118
|
+
if (open && open.kind !== kind) {
|
|
119
|
+
const end = closeBlock();
|
|
120
|
+
if (end) out.push(end);
|
|
121
|
+
}
|
|
122
|
+
if (!open) {
|
|
123
|
+
open = {
|
|
124
|
+
kind,
|
|
125
|
+
arguments: "",
|
|
126
|
+
text: "",
|
|
127
|
+
id: meta.id,
|
|
128
|
+
name: meta.name
|
|
129
|
+
};
|
|
130
|
+
const blockType = kind === "tool-call" ? "tool-call" : kind;
|
|
131
|
+
out.push({
|
|
132
|
+
type: "block-start",
|
|
133
|
+
index: blockIndex,
|
|
134
|
+
blockType
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return out;
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Handle one wire line and return the chunks to yield. Shared by the read
|
|
141
|
+
* loop and the EOF residual so a final line without a trailing newline is
|
|
142
|
+
* processed exactly like a newline-terminated one — dropping it would lose
|
|
143
|
+
* content AND, since the completeness guard landed, falsify a premature
|
|
144
|
+
* termination on streams that end bare.
|
|
145
|
+
*/
|
|
146
|
+
const handleLine = (line) => {
|
|
147
|
+
const out = [];
|
|
148
|
+
const trimmed = line.trim();
|
|
149
|
+
if (!trimmed.startsWith("data:")) {
|
|
150
|
+
const bare = extractBareJsonError(trimmed);
|
|
151
|
+
if (bare) {
|
|
152
|
+
const message = bare.message ?? bare.status ?? "upstream error";
|
|
153
|
+
throw new Error(`agy stream error (${bare.code ?? "unknown"}): ${message}`);
|
|
154
|
+
}
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
if (trimmed.slice(5).trim() === "[DONE]") {
|
|
158
|
+
sawDone = true;
|
|
159
|
+
return out;
|
|
160
|
+
}
|
|
161
|
+
const payload = parseSseDataLine(trimmed);
|
|
162
|
+
if (!payload) return out;
|
|
163
|
+
if (payload.error) {
|
|
164
|
+
const message = payload.error.message ?? payload.error.status ?? "upstream error";
|
|
165
|
+
throw new Error(`agy stream error (${payload.error.code ?? "unknown"}): ${message}`);
|
|
166
|
+
}
|
|
167
|
+
for (const candidate of payload.candidates ?? []) {
|
|
168
|
+
if (candidate.finishReason) {
|
|
169
|
+
sawFinishReason = true;
|
|
170
|
+
finishReason = mapFinishReason(candidate.finishReason);
|
|
171
|
+
}
|
|
172
|
+
for (const part of candidate.content?.parts ?? []) {
|
|
173
|
+
if (part.text === "" && !part.functionCall) continue;
|
|
174
|
+
if (part.text !== void 0 && part.thought !== true) {
|
|
175
|
+
out.push(...ensureBlock("text"));
|
|
176
|
+
open.text += part.text;
|
|
177
|
+
out.push({
|
|
178
|
+
type: "text-delta",
|
|
179
|
+
index: blockIndex,
|
|
180
|
+
text: part.text
|
|
181
|
+
});
|
|
182
|
+
} else if (part.text !== void 0 && part.thought === true) {
|
|
183
|
+
out.push(...ensureBlock("reasoning"));
|
|
184
|
+
open.text += part.text;
|
|
185
|
+
out.push({
|
|
186
|
+
type: "reasoning-delta",
|
|
187
|
+
index: blockIndex,
|
|
188
|
+
text: part.text
|
|
189
|
+
});
|
|
190
|
+
} else if (part.functionCall) {
|
|
191
|
+
const upstreamId = part.functionCall.id || String(blockIndex);
|
|
192
|
+
if (open) {
|
|
193
|
+
const end = closeBlock();
|
|
194
|
+
if (end) out.push(end);
|
|
195
|
+
}
|
|
196
|
+
const start = ensureBlock("tool-call", {
|
|
197
|
+
id: upstreamId,
|
|
198
|
+
name: part.functionCall.name
|
|
199
|
+
});
|
|
200
|
+
out.push(...start);
|
|
201
|
+
if (part.thoughtSignature) options.onToolSignature?.(upstreamId, part.thoughtSignature);
|
|
202
|
+
const argsJson = typeof part.functionCall.args === "string" ? part.functionCall.args : JSON.stringify(part.functionCall.args ?? {});
|
|
203
|
+
open.arguments += argsJson;
|
|
204
|
+
out.push({
|
|
205
|
+
type: "tool-call-delta",
|
|
206
|
+
index: blockIndex,
|
|
207
|
+
id: CallId(open.id ?? ""),
|
|
208
|
+
name: open.name,
|
|
209
|
+
argumentsDelta: argsJson
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (payload.usageMetadata) {
|
|
215
|
+
sawUsage = true;
|
|
216
|
+
const promptTokens = payload.usageMetadata.promptTokenCount ?? 0;
|
|
217
|
+
const cachedTokens = payload.usageMetadata.cachedContentTokenCount ?? 0;
|
|
218
|
+
lastUsage = {
|
|
219
|
+
inputTokens: Math.max(0, promptTokens - cachedTokens),
|
|
220
|
+
outputTokens: payload.usageMetadata.candidatesTokenCount ?? 0,
|
|
221
|
+
cacheReadTokens: cachedTokens
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
return out;
|
|
225
|
+
};
|
|
226
|
+
try {
|
|
227
|
+
while (true) {
|
|
228
|
+
if (signal?.aborted) throw new DOMException("aborted", "AbortError");
|
|
229
|
+
const { done, value } = await reader.read();
|
|
230
|
+
if (done) break;
|
|
231
|
+
buffer += decoder.decode(value, { stream: true });
|
|
232
|
+
let newlineIndex;
|
|
233
|
+
while ((newlineIndex = buffer.indexOf("\n")) !== -1) {
|
|
234
|
+
const line = buffer.slice(0, newlineIndex);
|
|
235
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
236
|
+
for (const chunk of handleLine(line)) yield chunk;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
buffer += decoder.decode();
|
|
240
|
+
if (buffer.trim() !== "") {
|
|
241
|
+
const line = buffer;
|
|
242
|
+
buffer = "";
|
|
243
|
+
for (const chunk of handleLine(line)) yield chunk;
|
|
244
|
+
}
|
|
245
|
+
if (!sawDone && !sawFinishReason) throw new Error("agy stream terminated prematurely without [DONE] or finishReason");
|
|
246
|
+
const closed = closeBlock();
|
|
247
|
+
if (closed) yield closed;
|
|
248
|
+
if (lastUsage) yield {
|
|
249
|
+
type: "usage",
|
|
250
|
+
usage: lastUsage
|
|
251
|
+
};
|
|
252
|
+
else if (sawUsage) yield {
|
|
253
|
+
type: "usage",
|
|
254
|
+
usage: {
|
|
255
|
+
inputTokens: 0,
|
|
256
|
+
outputTokens: 0
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
yield {
|
|
260
|
+
type: "finish",
|
|
261
|
+
reason: finishReason
|
|
262
|
+
};
|
|
263
|
+
} finally {
|
|
264
|
+
reader.releaseLock();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
//#endregion
|
|
268
|
+
export { parseAgySse as n, parse_exports as r, UnmappedFinishReasonError as t };
|
|
269
|
+
|
|
270
|
+
//# sourceMappingURL=parse-38V1zT1B.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-38V1zT1B.mjs","names":[],"sources":["../src/adapter/parse.ts"],"sourcesContent":["/**\n * Parse Antigravity SSE responses (Gemini-style `candidates[]` events) into the\n * DSH StreamChunk protocol: block-start / text-delta / reasoning-delta /\n * tool-call-delta / block-end / usage / finish.\n *\n * Wire shape: each `data:` payload is a JSON array of candidate objects with\n * `content.parts[]` (text / {thought:true} / functionCall), `usageMetadata`,\n * and `finishReason`. MEASURED (2026-10-03, daily endpoint, 3/3): a normal\n * stream terminates via the candidate `finishReason` alone — the endpoint\n * sends NO `data: [DONE]`; the SSE `[DONE]` convention is accepted but never\n * observed on the wire.\n *\n * Robustness contract: a cut-short stream must FAIL, never masquerade as a\n * completed turn. Three cases throw:\n * - an error body in-band (`data:` prefixed) or BARE (the upstream emits its\n * `{\"error\":{...}}` vocabulary without the prefix right before dropping the\n * connection), including in the final unterminated line;\n * - a clean EOF carrying neither `data: [DONE]` nor any `finishReason`\n * (either signal alone completes the stream);\n * - an explicit `finishReason` this parser does not map\n * ({@link UnmappedFinishReasonError}).\n */\n\nimport type { FinishReason, StreamChunk } from '@deepseek-ai/dsh-llm'\nimport * as DshLlm from '@deepseek-ai/dsh-llm'\n\n// ponytail: DSH 0.0.1-rc.1 exports CallId, 0.1.x renames to ToolCallId — pick whichever exists at runtime\nconst CallId = ((DshLlm as unknown as { ToolCallId?: (id: string) => unknown }).ToolCallId\n ?? (DshLlm as unknown as { CallId?: (id: string) => unknown }).CallId) as (id: string) => never\nif (!CallId) throw new Error('dsh-llm: neither ToolCallId nor CallId found')\n\nexport interface SsePart {\n text?: string\n thought?: boolean\n thoughtSignature?: string\n functionCall?: { id?: string; name?: string; args?: unknown }\n}\n\nexport interface SseCandidate {\n index?: number\n content?: {\n role?: string\n parts?: SsePart[]\n }\n finishReason?: string\n}\n\n/** The upstream error-vocabulary shape, in-band or bare. */\nexport interface SseError {\n code?: number\n status?: string\n message?: string\n}\n\nexport interface SsePayload {\n candidates?: SseCandidate[]\n usageMetadata?: {\n promptTokenCount?: number\n candidatesTokenCount?: number\n totalTokenCount?: number\n cachedContentTokenCount?: number\n }\n error?: SseError\n}\n\n/**\n * Parse one SSE `data:` line; returns null for `[DONE]` or empty lines.\n * Accepts the `{\"response\": {...}}` envelope (daily endpoint wire shape) and\n * the bare array/object shapes older clients emitted.\n */\nexport function parseSseDataLine(line: string): SsePayload | null {\n const trimmed = line.trim()\n if (!trimmed.startsWith('data:')) return null\n const data = trimmed.slice(5).trim()\n if (data === '' || data === '[DONE]') return null\n const parsed = JSON.parse(data) as unknown\n const root = (parsed as { response?: unknown })?.response ?? parsed\n const payload = (Array.isArray(root) ? root[0] : root) as SsePayload | undefined\n return payload ?? null\n}\n\n/**\n * Extract an error body from a line that does not carry the SSE `data:`\n * prefix. The upstream occasionally drops its `{\"error\":{...}}` vocabulary as\n * a bare JSON line right before terminating the stream — the socket closes\n * cleanly and nothing else surfaces the failure. Returns null for anything\n * that is not a JSON object carrying an `error` object; SSE comments and\n * event/id/retry fields stay skipped.\n */\nfunction extractBareJsonError(line: string): SseError | null {\n const trimmed = line.trim()\n if (!trimmed.startsWith('{')) return null\n try {\n const parsed = JSON.parse(trimmed) as { error?: SseError }\n if (parsed.error && typeof parsed.error === 'object') return parsed.error\n } catch {\n // not JSON (or truncated) — nothing to extract\n }\n return null\n}\n\n/**\n * Thrown when the upstream ends a stream with a `finishReason` this parser\n * does not map (SAFETY, RECITATION, MALFORMED_FUNCTION_CALL, ...). A silent\n * default to `stop` presented policy-blocked and malformed-call turns as\n * completed. This is a CONTENT-level verdict, not account health: the adapter\n * reports it as `request-error` (no cooldown, no rotation), never\n * `network-error`.\n */\nexport class UnmappedFinishReasonError extends Error {\n constructor(readonly reason: string) {\n super(`agy upstream ended the stream with unrecognized finishReason: ${reason}`)\n this.name = 'UnmappedFinishReasonError'\n }\n}\n\n/**\n * Map the upstream `finishReason` vocabulary onto DSH's. WHITELIST, not\n * blacklist: the completable reasons map to their kinds and every other\n * explicit reason throws. `FINISH_REASON_UNSPECIFIED` is granted `stop` — it\n * carries no information, exactly like the absent field.\n */\nfunction mapFinishReason(reason: string): FinishReason {\n switch (reason) {\n case 'MAX_TOKENS':\n return { kind: 'max-tokens' }\n case 'STOP':\n case 'FINISH_REASON_UNSPECIFIED':\n return { kind: 'stop' }\n case 'TOOL_CALLS':\n case 'FUNCTION_CALL':\n return { kind: 'tool-calls' }\n default:\n throw new UnmappedFinishReasonError(reason)\n }\n}\n\n/**\n * Consume an SSE text stream and yield StreamChunks. One accumulating block is\n * kept open at a time; tool-call argument deltas accumulate until a different\n * part kind, usage metadata, or stream end closes it.\n */\nexport interface ParseAgySseOptions {\n signal?: AbortSignal\n /**\n * Invoked when a functionCall part carries a sibling thoughtSignature, and\n * when a thought part carries one, keyed by the functionCall id (or the\n * thought signature is ignored unless paired). The adapter persists these\n * for replay on the next turn (see signature-cache.ts).\n */\n onToolSignature?(toolCallId: string, signature: string): void\n}\n\nexport async function* parseAgySse(\n body: ReadableStream<Uint8Array>,\n options: ParseAgySseOptions = {},\n): AsyncGenerator<StreamChunk> {\n const { signal } = options\n const reader = body.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n let blockIndex = 0\n let finishReason: FinishReason = { kind: 'stop' }\n let sawDone = false\n let sawFinishReason = false\n let sawUsage = false\n let lastUsage: { inputTokens: number; outputTokens: number; cacheReadTokens?: number } | null = null\n\n interface OpenBlock {\n kind: 'text' | 'reasoning' | 'tool-call'\n id?: string\n name?: string\n arguments: string\n text: string\n }\n let open: OpenBlock | null = null\n\n const closeBlock = (): StreamChunk | null => {\n if (!open) return null\n const block: StreamChunk = open.kind === 'tool-call'\n ? {\n type: 'block-end',\n index: blockIndex,\n block: {\n type: 'tool-call',\n id: CallId(open.id ?? `call-${blockIndex}`),\n name: open.name ?? '',\n arguments: open.arguments,\n },\n }\n : {\n type: 'block-end',\n index: blockIndex,\n block: { type: open.kind, text: open.text },\n }\n open = null\n blockIndex += 1\n return block\n }\n\n /**\n * Ensure a block of the given kind is open, switching when needed.\n * Returns chunks to yield (a closed block's end, then the new block's start).\n * Callers MUST yield everything returned — dropping the end silently corrupts\n * the block stream for DSH (verified: multi-tool turns and text→tool\n * transitions lost their block-end).\n */\n const ensureBlock = (kind: OpenBlock['kind'], meta: { id?: string; name?: string } = {}): StreamChunk[] => {\n const out: StreamChunk[] = []\n if (open && open.kind !== kind) {\n const end = closeBlock()\n if (end) out.push(end)\n }\n if (!open) {\n open = { kind, arguments: '', text: '', id: meta.id, name: meta.name }\n const blockType = kind === 'tool-call' ? 'tool-call' : kind\n out.push({ type: 'block-start', index: blockIndex, blockType })\n }\n return out\n }\n\n /**\n * Handle one wire line and return the chunks to yield. Shared by the read\n * loop and the EOF residual so a final line without a trailing newline is\n * processed exactly like a newline-terminated one — dropping it would lose\n * content AND, since the completeness guard landed, falsify a premature\n * termination on streams that end bare.\n */\n const handleLine = (line: string): StreamChunk[] => {\n const out: StreamChunk[] = []\n const trimmed = line.trim()\n if (!trimmed.startsWith('data:')) {\n const bare = extractBareJsonError(trimmed)\n if (bare) {\n const message = bare.message ?? bare.status ?? 'upstream error'\n throw new Error(`agy stream error (${bare.code ?? 'unknown'}): ${message}`)\n }\n return out\n }\n const data = trimmed.slice(5).trim()\n if (data === '[DONE]') {\n sawDone = true\n return out\n }\n const payload = parseSseDataLine(trimmed)\n if (!payload) return out\n if (payload.error) {\n const message = payload.error.message ?? payload.error.status ?? 'upstream error'\n throw new Error(`agy stream error (${payload.error.code ?? 'unknown'}): ${message}`)\n }\n for (const candidate of payload.candidates ?? []) {\n if (candidate.finishReason) {\n sawFinishReason = true\n finishReason = mapFinishReason(candidate.finishReason)\n }\n for (const part of candidate.content?.parts ?? []) {\n // A zero-length text part is a routine wire shape (the daily endpoint\n // ends turns with `{thoughtSignature, text: \"\"}`) — fabricating an\n // empty block from it poisoned session history for stricter\n // downstream serializers (issue #77). Skip it without touching an\n // open block, so \"Hel\" + \"\" + \"lo\" stays one block; `!part.functionCall`\n // keeps a text+functionCall part on its current branch. Signatures\n // ride functionCall parts only, so the skip loses nothing.\n if (part.text === '' && !part.functionCall) continue\n if (part.text !== undefined && part.thought !== true) {\n out.push(...ensureBlock('text'))\n open!.text += part.text\n out.push({ type: 'text-delta', index: blockIndex, text: part.text })\n } else if (part.text !== undefined && part.thought === true) {\n out.push(...ensureBlock('reasoning'))\n open!.text += part.text\n out.push({ type: 'reasoning-delta', index: blockIndex, text: part.text })\n } else if (part.functionCall) {\n // Use the upstream functionCall id when present so the signature\n // captured on this part can be replayed for the same id next turn.\n const upstreamId = part.functionCall.id || String(blockIndex)\n // Each functionCall part is an ATOMIC block: a stream can carry\n // several functionCall parts in one turn (multi-tool responses),\n // and they share kind \"tool-call\" — ensureBlock alone would not\n // switch between them, concatenating their args JSON into one\n // invalid string. Close any open block (yielding its end) first.\n if (open) {\n const end = closeBlock()\n if (end) out.push(end)\n }\n const start = ensureBlock('tool-call', {\n id: upstreamId,\n name: part.functionCall.name,\n })\n out.push(...start)\n if (part.thoughtSignature) {\n options.onToolSignature?.(upstreamId, part.thoughtSignature)\n }\n const argsJson = typeof part.functionCall.args === 'string'\n ? part.functionCall.args\n : JSON.stringify(part.functionCall.args ?? {})\n open!.arguments += argsJson\n out.push({\n type: 'tool-call-delta',\n index: blockIndex,\n id: CallId(open!.id ?? ''),\n name: open!.name,\n argumentsDelta: argsJson,\n })\n }\n }\n }\n if (payload.usageMetadata) {\n // The upstream sends usageMetadata on EVERY SSE event (cumulative).\n // Do NOT close the block here: closing per event would split one\n // continuous text stream into a block per chunk (frontend renders\n // block boundaries as line breaks). Stash the last (full) totals\n // and emit one usage chunk at stream end.\n sawUsage = true\n // DSH TokenUsage buckets are DISJOINT: inputTokens must be the\n // uncached portion only; cache reads are reported separately.\n // Reporting the full promptTokenCount here double-counts cached\n // tokens in the stats line's cache-hit percentage (it divides by\n // uncached + cacheRead + cacheWrite).\n const promptTokens = payload.usageMetadata.promptTokenCount ?? 0\n const cachedTokens = payload.usageMetadata.cachedContentTokenCount ?? 0\n lastUsage = {\n inputTokens: Math.max(0, promptTokens - cachedTokens),\n outputTokens: payload.usageMetadata.candidatesTokenCount ?? 0,\n cacheReadTokens: cachedTokens,\n }\n }\n return out\n }\n\n try {\n while (true) {\n if (signal?.aborted) {\n throw new DOMException('aborted', 'AbortError')\n }\n const { done, value } = await reader.read()\n if (done) break\n buffer += decoder.decode(value, { stream: true })\n let newlineIndex: number\n while ((newlineIndex = buffer.indexOf('\\n')) !== -1) {\n const line = buffer.slice(0, newlineIndex)\n buffer = buffer.slice(newlineIndex + 1)\n for (const chunk of handleLine(line)) yield chunk\n }\n }\n // Flush the decoder and process a final line that arrived without its\n // trailing newline: content, usage, finishReason, `[DONE]`, and bare\n // error bodies all ride it.\n buffer += decoder.decode()\n if (buffer.trim() !== '') {\n const line = buffer\n buffer = ''\n for (const chunk of handleLine(line)) yield chunk\n }\n // Completeness guard: a CLEAN close carrying neither `[DONE]` nor any\n // `finishReason` is a cut-short stream, not a completed turn — yielding\n // the default `stop` here presented truncated replies as successful\n // (issue #85). Either signal alone completes the stream.\n if (!sawDone && !sawFinishReason) {\n throw new Error('agy stream terminated prematurely without [DONE] or finishReason')\n }\n const closed = closeBlock()\n if (closed) yield closed\n if (lastUsage) {\n yield { type: 'usage', usage: lastUsage }\n } else if (sawUsage) {\n yield { type: 'usage', usage: { inputTokens: 0, outputTokens: 0 } }\n }\n yield { type: 'finish', reason: finishReason }\n } finally {\n reader.releaseLock()\n }\n}\n"],"mappings":";;;;;;;;AA2BA,MAAM,SAAW,OAA+D,cAC1E,OAA2D;AACjE,IAAI,CAAC,QAAQ,MAAM,IAAI,MAAM,8CAA8C;;;;;;AAyC3E,SAAgB,iBAAiB,MAAiC;CAChE,MAAM,UAAU,KAAK,KAAK;CAC1B,IAAI,CAAC,QAAQ,WAAW,OAAO,GAAG,OAAO;CACzC,MAAM,OAAO,QAAQ,MAAM,CAAC,CAAC,CAAC,KAAK;CACnC,IAAI,SAAS,MAAM,SAAS,UAAU,OAAO;CAC7C,MAAM,SAAS,KAAK,MAAM,IAAI;CAC9B,MAAM,OAAQ,QAAmC,YAAY;CAE7D,QADiB,MAAM,QAAQ,IAAI,IAAI,KAAK,KAAK,SAC/B;AACpB;;;;;;;;;AAUA,SAAS,qBAAqB,MAA+B;CAC3D,MAAM,UAAU,KAAK,KAAK;CAC1B,IAAI,CAAC,QAAQ,WAAW,GAAG,GAAG,OAAO;CACrC,IAAI;EACF,MAAM,SAAS,KAAK,MAAM,OAAO;EACjC,IAAI,OAAO,SAAS,OAAO,OAAO,UAAU,UAAU,OAAO,OAAO;CACtE,QAAQ,CAER;CACA,OAAO;AACT;;;;;;;;;AAUA,IAAa,4BAAb,cAA+C,MAAM;CAC9B;CAArB,YAAY,QAAyB;EACnC,MAAM,iEAAiE,QAAQ;EAD5D,KAAA,SAAA;EAEnB,KAAK,OAAO;CACd;AACF;;;;;;;AAQA,SAAS,gBAAgB,QAA8B;CACrD,QAAQ,QAAR;EACE,KAAK,cACH,OAAO,EAAE,MAAM,aAAa;EAC9B,KAAK;EACL,KAAK,6BACH,OAAO,EAAE,MAAM,OAAO;EACxB,KAAK;EACL,KAAK,iBACH,OAAO,EAAE,MAAM,aAAa;EAC9B,SACE,MAAM,IAAI,0BAA0B,MAAM;CAC9C;AACF;AAkBA,gBAAuB,YACrB,MACA,UAA8B,CAAC,GACF;CAC7B,MAAM,EAAE,WAAW;CACnB,MAAM,SAAS,KAAK,UAAU;CAC9B,MAAM,UAAU,IAAI,YAAY;CAChC,IAAI,SAAS;CACb,IAAI,aAAa;CACjB,IAAI,eAA6B,EAAE,MAAM,OAAO;CAChD,IAAI,UAAU;CACd,IAAI,kBAAkB;CACtB,IAAI,WAAW;CACf,IAAI,YAA4F;CAShG,IAAI,OAAyB;CAE7B,MAAM,mBAAuC;EAC3C,IAAI,CAAC,MAAM,OAAO;EAClB,MAAM,QAAqB,KAAK,SAAS,cACrC;GACE,MAAM;GACN,OAAO;GACP,OAAO;IACL,MAAM;IACN,IAAI,OAAO,KAAK,MAAM,QAAQ,YAAY;IAC1C,MAAM,KAAK,QAAQ;IACnB,WAAW,KAAK;GAClB;EACF,IACA;GACE,MAAM;GACN,OAAO;GACP,OAAO;IAAE,MAAM,KAAK;IAAM,MAAM,KAAK;GAAK;EAC5C;EACJ,OAAO;EACP,cAAc;EACd,OAAO;CACT;;;;;;;;CASA,MAAM,eAAe,MAAyB,OAAuC,CAAC,MAAqB;EACzG,MAAM,MAAqB,CAAC;EAC5B,IAAI,QAAQ,KAAK,SAAS,MAAM;GAC9B,MAAM,MAAM,WAAW;GACvB,IAAI,KAAK,IAAI,KAAK,GAAG;EACvB;EACA,IAAI,CAAC,MAAM;GACT,OAAO;IAAE;IAAM,WAAW;IAAI,MAAM;IAAI,IAAI,KAAK;IAAI,MAAM,KAAK;GAAK;GACrE,MAAM,YAAY,SAAS,cAAc,cAAc;GACvD,IAAI,KAAK;IAAE,MAAM;IAAe,OAAO;IAAY;GAAU,CAAC;EAChE;EACA,OAAO;CACT;;;;;;;;CASA,MAAM,cAAc,SAAgC;EAClD,MAAM,MAAqB,CAAC;EAC5B,MAAM,UAAU,KAAK,KAAK;EAC1B,IAAI,CAAC,QAAQ,WAAW,OAAO,GAAG;GAChC,MAAM,OAAO,qBAAqB,OAAO;GACzC,IAAI,MAAM;IACR,MAAM,UAAU,KAAK,WAAW,KAAK,UAAU;IAC/C,MAAM,IAAI,MAAM,qBAAqB,KAAK,QAAQ,UAAU,KAAK,SAAS;GAC5E;GACA,OAAO;EACT;EAEA,IADa,QAAQ,MAAM,CAAC,CAAC,CAAC,KACvB,MAAM,UAAU;GACrB,UAAU;GACV,OAAO;EACT;EACA,MAAM,UAAU,iBAAiB,OAAO;EACxC,IAAI,CAAC,SAAS,OAAO;EACrB,IAAI,QAAQ,OAAO;GACjB,MAAM,UAAU,QAAQ,MAAM,WAAW,QAAQ,MAAM,UAAU;GACjE,MAAM,IAAI,MAAM,qBAAqB,QAAQ,MAAM,QAAQ,UAAU,KAAK,SAAS;EACrF;EACA,KAAK,MAAM,aAAa,QAAQ,cAAc,CAAC,GAAG;GAChD,IAAI,UAAU,cAAc;IAC1B,kBAAkB;IAClB,eAAe,gBAAgB,UAAU,YAAY;GACvD;GACA,KAAK,MAAM,QAAQ,UAAU,SAAS,SAAS,CAAC,GAAG;IAQjD,IAAI,KAAK,SAAS,MAAM,CAAC,KAAK,cAAc;IAC5C,IAAI,KAAK,SAAS,KAAA,KAAa,KAAK,YAAY,MAAM;KACpD,IAAI,KAAK,GAAG,YAAY,MAAM,CAAC;KAC/B,KAAM,QAAQ,KAAK;KACnB,IAAI,KAAK;MAAE,MAAM;MAAc,OAAO;MAAY,MAAM,KAAK;KAAK,CAAC;IACrE,OAAO,IAAI,KAAK,SAAS,KAAA,KAAa,KAAK,YAAY,MAAM;KAC3D,IAAI,KAAK,GAAG,YAAY,WAAW,CAAC;KACpC,KAAM,QAAQ,KAAK;KACnB,IAAI,KAAK;MAAE,MAAM;MAAmB,OAAO;MAAY,MAAM,KAAK;KAAK,CAAC;IAC1E,OAAO,IAAI,KAAK,cAAc;KAG5B,MAAM,aAAa,KAAK,aAAa,MAAM,OAAO,UAAU;KAM5D,IAAI,MAAM;MACR,MAAM,MAAM,WAAW;MACvB,IAAI,KAAK,IAAI,KAAK,GAAG;KACvB;KACA,MAAM,QAAQ,YAAY,aAAa;MACrC,IAAI;MACJ,MAAM,KAAK,aAAa;KAC1B,CAAC;KACD,IAAI,KAAK,GAAG,KAAK;KACjB,IAAI,KAAK,kBACP,QAAQ,kBAAkB,YAAY,KAAK,gBAAgB;KAE7D,MAAM,WAAW,OAAO,KAAK,aAAa,SAAS,WAC/C,KAAK,aAAa,OAClB,KAAK,UAAU,KAAK,aAAa,QAAQ,CAAC,CAAC;KAC/C,KAAM,aAAa;KACnB,IAAI,KAAK;MACP,MAAM;MACN,OAAO;MACP,IAAI,OAAO,KAAM,MAAM,EAAE;MACzB,MAAM,KAAM;MACZ,gBAAgB;KAClB,CAAC;IACH;GACF;EACF;EACA,IAAI,QAAQ,eAAe;GAMzB,WAAW;GAMX,MAAM,eAAe,QAAQ,cAAc,oBAAoB;GAC/D,MAAM,eAAe,QAAQ,cAAc,2BAA2B;GACtE,YAAY;IACV,aAAa,KAAK,IAAI,GAAG,eAAe,YAAY;IACpD,cAAc,QAAQ,cAAc,wBAAwB;IAC5D,iBAAiB;GACnB;EACF;EACA,OAAO;CACT;CAEA,IAAI;EACF,OAAO,MAAM;GACX,IAAI,QAAQ,SACV,MAAM,IAAI,aAAa,WAAW,YAAY;GAEhD,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,KAAK;GAC1C,IAAI,MAAM;GACV,UAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;GAChD,IAAI;GACJ,QAAQ,eAAe,OAAO,QAAQ,IAAI,OAAO,IAAI;IACnD,MAAM,OAAO,OAAO,MAAM,GAAG,YAAY;IACzC,SAAS,OAAO,MAAM,eAAe,CAAC;IACtC,KAAK,MAAM,SAAS,WAAW,IAAI,GAAG,MAAM;GAC9C;EACF;EAIA,UAAU,QAAQ,OAAO;EACzB,IAAI,OAAO,KAAK,MAAM,IAAI;GACxB,MAAM,OAAO;GACb,SAAS;GACT,KAAK,MAAM,SAAS,WAAW,IAAI,GAAG,MAAM;EAC9C;EAKA,IAAI,CAAC,WAAW,CAAC,iBACf,MAAM,IAAI,MAAM,kEAAkE;EAEpF,MAAM,SAAS,WAAW;EAC1B,IAAI,QAAQ,MAAM;EAClB,IAAI,WACF,MAAM;GAAE,MAAM;GAAS,OAAO;EAAU;OACnC,IAAI,UACT,MAAM;GAAE,MAAM;GAAS,OAAO;IAAE,aAAa;IAAG,cAAc;GAAE;EAAE;EAEpE,MAAM;GAAE,MAAM;GAAU,QAAQ;EAAa;CAC/C,UAAU;EACR,OAAO,YAAY;CACrB;AACF"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { m as fetchAgyFirstOk, v as AgyAuthError, y as AgyPoolBlockedError } from "./constants-
|
|
2
|
-
import { a as probeFetch, o as proxiedFetch, t as accountFetch } from "./proxy-
|
|
3
|
-
import {
|
|
1
|
+
import { m as fetchAgyFirstOk, v as AgyAuthError, y as AgyPoolBlockedError } from "./constants-CmA9wXNe.mjs";
|
|
2
|
+
import { a as probeFetch, o as proxiedFetch, t as accountFetch } from "./proxy-BbuQX5U9.mjs";
|
|
3
|
+
import { C as isSessionAccumulationOverflow, S as describeFetchError, _ as persistMasterKey, b as classifyFetchError, f as migrateToAgyDir, g as loadMasterKey, h as deriveKey, m as createAesGcmCodec, p as MASTER_KEY_REF, r as AgySessionManager, s as resolveAntigravityVersion, t as JsonAccountStore, u as pickProbeProxyUrl, v as resolveDshHome, x as classifyHttpError, y as resolveMasterKeyCodec } from "./accounts-CB4blXpP.mjs";
|
|
4
4
|
import { i as generateAntigravityRequestId, n as currentSessionGeneration, r as deriveAntigravitySessionId, t as bumpSessionGeneration } from "./identity-Ci8EGc1F.mjs";
|
|
5
|
-
import { i as setThoughtSignature, r as resolveMultimodalFiles, t as toAgyRequestBody } from "./translate-
|
|
6
|
-
import {
|
|
7
|
-
import { i as resolveAgyModel, n as listAgyModels, t as catalogModelList } from "./models-
|
|
5
|
+
import { i as setThoughtSignature, r as resolveMultimodalFiles, t as toAgyRequestBody } from "./translate-B9ZOauqw.mjs";
|
|
6
|
+
import { n as parseAgySse, t as UnmappedFinishReasonError } from "./parse-38V1zT1B.mjs";
|
|
7
|
+
import { i as resolveAgyModel, n as listAgyModels, t as catalogModelList } from "./models-CjB0rXtI.mjs";
|
|
8
8
|
import { randomBytes } from "node:crypto";
|
|
9
9
|
import { LlmAdapter, LlmError, ProviderRequestId, QUOTA_EXCEEDED_CODE } from "@deepseek-ai/dsh-llm";
|
|
10
10
|
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
@@ -340,10 +340,11 @@ var AgyAdapter = class extends LlmAdapter {
|
|
|
340
340
|
}, startedAt);
|
|
341
341
|
} catch (error) {
|
|
342
342
|
if (error instanceof DOMException && error.name === "AbortError") throw new LlmError("agy stream aborted", "ABORTED", { cause: error });
|
|
343
|
-
|
|
343
|
+
const unmappedFinish = error instanceof UnmappedFinishReasonError;
|
|
344
|
+
await this.options.reportFailure(unmappedFinish ? "request-error" : "network-error", session);
|
|
344
345
|
this.recordUsage(session, options.model, {
|
|
345
346
|
ok: false,
|
|
346
|
-
reason: "network-error"
|
|
347
|
+
reason: unmappedFinish ? "request-error" : "network-error"
|
|
347
348
|
}, startedAt);
|
|
348
349
|
throw new LlmError(error instanceof Error ? describeFetchError(error) : "agy stream parse failed", UPSTREAM_ERROR_CODE, { cause: error });
|
|
349
350
|
}
|
|
@@ -1640,4 +1641,4 @@ async function buildAgyRuntime(ctx) {
|
|
|
1640
1641
|
//#endregion
|
|
1641
1642
|
export { CLAUDE_BUDGET_MIN as a, CLAUDE_BUDGET_MAX as i, dayKey as n, THINKING_BUDGET_MAX as o, foldWindowBreakdown as r, createAgyRuntime as t };
|
|
1642
1643
|
|
|
1643
|
-
//# sourceMappingURL=plugin-common-
|
|
1644
|
+
//# sourceMappingURL=plugin-common-D2NWMsX6.mjs.map
|