dsh-agy 0.4.4 → 0.4.5

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/lib/cli/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { n as AGY_DEFAULT_REDIRECT_URI } from "../constants-CmA9wXNe.mjs";
2
2
  import { i as normalizeProxyUrl, n as isProxyReachable, o as proxiedFetch, t as accountFetch } from "../proxy-BbuQX5U9.mjs";
3
- import { c as resolveAntigravityVersionBounded, f as migrateToAgyDir, g as loadMasterKey, h as deriveKey, i as isAgyDisabled, m as createAesGcmCodec, n as maskProxyUrl, r as AgySessionManager, t as JsonAccountStore, v as resolveDshHome, y as resolveMasterKeyCodec } from "../accounts-DW-BL9c3.mjs";
3
+ import { c as resolveAntigravityVersionBounded, f as migrateToAgyDir, g as loadMasterKey, h as deriveKey, i as isAgyDisabled, m as createAesGcmCodec, n as maskProxyUrl, r as AgySessionManager, t as JsonAccountStore, v as resolveDshHome, y as resolveMasterKeyCodec } from "../accounts-BOuz6_Sh.mjs";
4
4
  import { t as exchangeAntigravity } from "../exchange-C-tndv7j.mjs";
5
5
  import { n as upsertImportedAccount, r as authorizeAntigravity, t as importManySources } from "../import-SxPDjH3u.mjs";
6
6
  import { r as encodeCredentialBlob } from "../blob-D1e7_uT1.mjs";
package/lib/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { i as isAgyDisabled } from "./accounts-DW-BL9c3.mjs";
2
- import { t as createAgyRuntime } from "./plugin-common-CKTYXxSc.mjs";
1
+ import { i as isAgyDisabled } from "./accounts-BOuz6_Sh.mjs";
2
+ import { t as createAgyRuntime } from "./plugin-common-Dwa1K13O.mjs";
3
3
  //#region src/index.ts
4
4
  const name = "dsh-agy";
5
5
  const inject = ["llm"];
@@ -164,6 +164,16 @@ async function* parseAgySse(body, options = {}) {
164
164
  const message = payload.error.message ?? payload.error.status ?? "upstream error";
165
165
  throw new Error(`agy stream error (${payload.error.code ?? "unknown"}): ${message}`);
166
166
  }
167
+ if (payload.usageMetadata) {
168
+ sawUsage = true;
169
+ const promptTokens = payload.usageMetadata.promptTokenCount ?? 0;
170
+ const cachedTokens = payload.usageMetadata.cachedContentTokenCount ?? 0;
171
+ lastUsage = {
172
+ inputTokens: Math.max(0, promptTokens - cachedTokens),
173
+ outputTokens: payload.usageMetadata.candidatesTokenCount ?? 0,
174
+ cacheReadTokens: cachedTokens
175
+ };
176
+ }
167
177
  for (const candidate of payload.candidates ?? []) {
168
178
  if (candidate.finishReason) {
169
179
  sawFinishReason = true;
@@ -211,17 +221,29 @@ async function* parseAgySse(body, options = {}) {
211
221
  }
212
222
  }
213
223
  }
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
224
+ return out;
225
+ };
226
+ /**
227
+ * Yield one line's chunks, but flush the stashed usage first if the line
228
+ * aborts the stream.
229
+ *
230
+ * An unmapped `finishReason` throws from inside `handleLine`, i.e. BEFORE the
231
+ * usageMetadata stash on that same line AND before the end-of-stream usage
232
+ * chunk below. Without this the caller sees no usage at all for an attempt
233
+ * that the upstream really did bill — and the adapter's `MALFORMED_FUNCTION_CALL`
234
+ * re-sample discards exactly such an attempt, so its tokens would silently
235
+ * never reach the ledger (AGENTS.md: the ledger accumulates per attempt).
236
+ */
237
+ const drainLine = function* (line) {
238
+ try {
239
+ yield* handleLine(line);
240
+ } catch (error) {
241
+ if (error instanceof UnmappedFinishReasonError && lastUsage !== null) yield {
242
+ type: "usage",
243
+ usage: lastUsage
222
244
  };
245
+ throw error;
223
246
  }
224
- return out;
225
247
  };
226
248
  try {
227
249
  while (true) {
@@ -233,14 +255,14 @@ async function* parseAgySse(body, options = {}) {
233
255
  while ((newlineIndex = buffer.indexOf("\n")) !== -1) {
234
256
  const line = buffer.slice(0, newlineIndex);
235
257
  buffer = buffer.slice(newlineIndex + 1);
236
- for (const chunk of handleLine(line)) yield chunk;
258
+ for (const chunk of drainLine(line)) yield chunk;
237
259
  }
238
260
  }
239
261
  buffer += decoder.decode();
240
262
  if (buffer.trim() !== "") {
241
263
  const line = buffer;
242
264
  buffer = "";
243
- for (const chunk of handleLine(line)) yield chunk;
265
+ for (const chunk of drainLine(line)) yield chunk;
244
266
  }
245
267
  if (!sawDone && !sawFinishReason) throw new Error("agy stream terminated prematurely without [DONE] or finishReason");
246
268
  const closed = closeBlock();
@@ -267,4 +289,4 @@ async function* parseAgySse(body, options = {}) {
267
289
  //#endregion
268
290
  export { parseAgySse as n, parse_exports as r, UnmappedFinishReasonError as t };
269
291
 
270
- //# sourceMappingURL=parse-38V1zT1B.mjs.map
292
+ //# sourceMappingURL=parse-D3InNyUx.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-D3InNyUx.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 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 //\n // Stashed BEFORE the candidates loop below, because that loop is where an\n // unmapped `finishReason` throws: the totals ride on the same event that\n // ends the stream, and `drainLine` can only flush a stash that exists.\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 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 return out\n }\n\n /**\n * Yield one line's chunks, but flush the stashed usage first if the line\n * aborts the stream.\n *\n * An unmapped `finishReason` throws from inside `handleLine`, i.e. BEFORE the\n * usageMetadata stash on that same line AND before the end-of-stream usage\n * chunk below. Without this the caller sees no usage at all for an attempt\n * that the upstream really did bill — and the adapter's `MALFORMED_FUNCTION_CALL`\n * re-sample discards exactly such an attempt, so its tokens would silently\n * never reach the ledger (AGENTS.md: the ledger accumulates per attempt).\n */\n const drainLine = function* (line: string): Generator<StreamChunk> {\n try {\n yield* handleLine(line)\n } catch (error) {\n if (error instanceof UnmappedFinishReasonError && lastUsage !== null) {\n yield { type: 'usage', usage: lastUsage }\n }\n throw error\n }\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 drainLine(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 drainLine(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":"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"}
@@ -1,9 +1,9 @@
1
1
  import { m as fetchAgyFirstOk, v as AgyAuthError, y as AgyPoolBlockedError } from "./constants-CmA9wXNe.mjs";
2
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-DW-BL9c3.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-BOuz6_Sh.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-BR1kuCaz.mjs";
6
- import { n as parseAgySse, t as UnmappedFinishReasonError } from "./parse-38V1zT1B.mjs";
5
+ import { i as setThoughtSignature, r as resolveMultimodalFiles, t as toAgyRequestBody } from "./translate-Cq6PGO4b.mjs";
6
+ import { n as parseAgySse, t as UnmappedFinishReasonError } from "./parse-D3InNyUx.mjs";
7
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";
@@ -29,6 +29,22 @@ function collectImageRefs(options) {
29
29
  const UPSTREAM_ERROR_CODE = "UPSTREAM";
30
30
  /** First-class DSH retryable code: the default retry policy honors SERVER (5xx), not UPSTREAM. */
31
31
  const SERVER_ERROR_CODE = "SERVER";
32
+ /** Abort-aware delay helper for transient stream retries. */
33
+ async function sleepWithSignal(ms, signal) {
34
+ if (signal?.aborted) throw new DOMException("aborted", "AbortError");
35
+ await new Promise((resolve, reject) => {
36
+ const onAbort = () => {
37
+ clearTimeout(timer);
38
+ reject(new DOMException("aborted", "AbortError"));
39
+ };
40
+ const timer = setTimeout(() => {
41
+ signal?.removeEventListener("abort", onAbort);
42
+ resolve();
43
+ }, ms);
44
+ if (!signal) return;
45
+ signal.addEventListener("abort", onAbort, { once: true });
46
+ });
47
+ }
32
48
  /** Stable ledger key for an account: email when present, else the generated id. */
33
49
  function ledgerAccountKey(session) {
34
50
  return session.account.email ?? session.account.id;
@@ -219,8 +235,6 @@ var AgyAdapter = class extends LlmAdapter {
219
235
  holder.account = session.account;
220
236
  this.options.noteRequestStarted?.(session.account);
221
237
  const conversationAccount = ledgerAccountKey(session);
222
- /** Wall-clock origin for this attempt's latency figures. */
223
- const startedAt = Date.now();
224
238
  const routing = {
225
239
  proxyUrl: session.account.proxy,
226
240
  streaming: true
@@ -235,8 +249,11 @@ var AgyAdapter = class extends LlmAdapter {
235
249
  * names a fresh upstream session and recovers the conversation. This is not
236
250
  * an account fault, so it does not go through `reportFailure`: the account
237
251
  * stays healthy and only the derived id changes.
252
+ *
253
+ * `attemptStartedAt` is the clock of the outer attempt that owns this send,
254
+ * so a transport failure is billed to that attempt rather than to the call.
238
255
  */
239
- const sendAttempt = async () => {
256
+ const sendAttempt = async (attemptStartedAt) => {
240
257
  for (let attempt = 0;; attempt++) {
241
258
  const generation = conversationKey !== void 0 && conversationAccount !== void 0 ? currentSessionGeneration(conversationAccount, conversationKey) : 0;
242
259
  const requestId = generateAntigravityRequestId();
@@ -264,7 +281,7 @@ var AgyAdapter = class extends LlmAdapter {
264
281
  this.recordUsage(session, options.model, {
265
282
  ok: false,
266
283
  reason: "network-error"
267
- }, startedAt);
284
+ }, attemptStartedAt);
268
285
  throw new LlmError(classified.message ?? "agy fetch failed", "TRANSPORT", { cause: error });
269
286
  }
270
287
  if (response.ok) return { response };
@@ -279,74 +296,153 @@ var AgyAdapter = class extends LlmAdapter {
279
296
  };
280
297
  }
281
298
  };
282
- const { response, bodyText } = await sendAttempt();
283
- if (!response.ok) {
284
- const classified = classifyHttpError(response.status, response.headers, bodyText);
285
- await this.options.reportFailure(classified.kind, session, {
286
- retryAfterMs: classified.retryAfterMs,
287
- status: response.status,
288
- rateLimitCategory: classified.rateLimitCategory,
289
- resetTime: classified.resetTime,
290
- model: options.model,
291
- verificationUrl: classified.verificationUrl
292
- });
293
- this.recordUsage(session, options.model, {
294
- ok: false,
295
- rateLimited: classified.kind === "rate-limit",
296
- reason: classified.kind
297
- }, startedAt);
298
- if (classified.kind === "rate-limit") {
299
- if (classified.rateLimitCategory === "quota_exhausted") throw new LlmError(`agy daily quota exhausted (${response.status}): ${classified.message ?? ""}`, QUOTA_EXCEEDED_CODE);
300
- throw new LlmError(`agy rate-limited (${response.status}): ${classified.message ?? ""}`, "RATE_LIMIT", {
299
+ /**
300
+ * Maximum internal retries when the upstream ends a stream with
301
+ * `MALFORMED_FUNCTION_CALL` before any user-facing text chunks have been
302
+ * yielded to DSH. Gemini models occasionally glitch on tool-call JSON syntax;
303
+ * re-sampling cleanly recovers the turn without crashing the conversation.
304
+ *
305
+ * This deliberately generalises the `sendAttempt` accumulation-wall resend
306
+ * into the stream path: the invariant that one DSH call is one provider
307
+ * attempt now has this second, documented exception (see AGENTS.md, "the
308
+ * adapter does not silent-retry"). Both exceptions share the same shape —
309
+ * the account is healthy and the request was accepted — and neither goes
310
+ * through `reportFailure`, so the pool sees nothing.
311
+ */
312
+ const MAX_MALFORMED_RETRIES = 3;
313
+ for (let malformedAttempt = 0;; malformedAttempt++) {
314
+ const attemptStartedAt = Date.now();
315
+ const { response, bodyText } = await sendAttempt(attemptStartedAt);
316
+ if (!response.ok) {
317
+ const classified = classifyHttpError(response.status, response.headers, bodyText);
318
+ await this.options.reportFailure(classified.kind, session, {
319
+ retryAfterMs: classified.retryAfterMs,
320
+ status: response.status,
321
+ rateLimitCategory: classified.rateLimitCategory,
322
+ resetTime: classified.resetTime,
323
+ model: options.model,
324
+ verificationUrl: classified.verificationUrl
325
+ });
326
+ this.recordUsage(session, options.model, {
327
+ ok: false,
328
+ rateLimited: classified.kind === "rate-limit",
329
+ reason: classified.kind
330
+ }, attemptStartedAt);
331
+ if (classified.kind === "rate-limit") {
332
+ if (classified.rateLimitCategory === "quota_exhausted") throw new LlmError(`agy daily quota exhausted (${response.status}): ${classified.message ?? ""}`, QUOTA_EXCEEDED_CODE);
333
+ throw new LlmError(`agy rate-limited (${response.status}): ${classified.message ?? ""}`, "RATE_LIMIT", {
334
+ providerRetryAfterMs: classified.retryAfterMs ?? void 0,
335
+ requestId: ProviderRequestId(generateAntigravityRequestId())
336
+ });
337
+ }
338
+ if (classified.kind === "verification-required") {
339
+ const appeal = classified.verificationUrl ? ` Verify at: ${classified.verificationUrl}` : "";
340
+ throw new LlmError(`agy account needs verification (${response.status}): ${classified.message ?? ""}${appeal}`, "RATE_LIMIT", { requestId: ProviderRequestId(generateAntigravityRequestId()) });
341
+ }
342
+ if (classified.kind === "auth-failure") throw new LlmError(`agy authentication failed (${response.status}) — run \`dsh-agy login\``, "INVALID_CREDENTIAL");
343
+ if (classified.status !== void 0 && classified.status >= 500) throw new LlmError(`agy upstream error (${response.status}): ${classified.message ?? ""}`, SERVER_ERROR_CODE, {
301
344
  providerRetryAfterMs: classified.retryAfterMs ?? void 0,
302
345
  requestId: ProviderRequestId(generateAntigravityRequestId())
303
346
  });
347
+ throw new LlmError(`agy upstream error (${response.status}): ${classified.message ?? ""}`, UPSTREAM_ERROR_CODE);
304
348
  }
305
- if (classified.kind === "verification-required") {
306
- const appeal = classified.verificationUrl ? ` Verify at: ${classified.verificationUrl}` : "";
307
- throw new LlmError(`agy account needs verification (${response.status}): ${classified.message ?? ""}${appeal}`, "RATE_LIMIT", { requestId: ProviderRequestId(generateAntigravityRequestId()) });
308
- }
309
- if (classified.kind === "auth-failure") throw new LlmError(`agy authentication failed (${response.status}) — run \`dsh-agy login\``, "INVALID_CREDENTIAL");
310
- if (classified.status !== void 0 && classified.status >= 500) throw new LlmError(`agy upstream error (${response.status}): ${classified.message ?? ""}`, SERVER_ERROR_CODE, {
311
- providerRetryAfterMs: classified.retryAfterMs ?? void 0,
312
- requestId: ProviderRequestId(generateAntigravityRequestId())
313
- });
314
- throw new LlmError(`agy upstream error (${response.status}): ${classified.message ?? ""}`, UPSTREAM_ERROR_CODE);
315
- }
316
- if (!response.body) throw new LlmError("agy stream returned no body", UPSTREAM_ERROR_CODE);
317
- try {
349
+ if (!response.body) throw new LlmError("agy stream returned no body", UPSTREAM_ERROR_CODE);
350
+ const bufferedChunks = [];
351
+ /**
352
+ * Thought signatures observed during this attempt, held back with the
353
+ * chunks that carried them. `onToolSignature` fires as soon as the parser
354
+ * sees a signature, i.e. BEFORE the attempt is known to be keepable, so
355
+ * caching eagerly would let a discarded attempt write the process-global
356
+ * signature cache (keyed by `toolCallId` — and issue #99 records those ids
357
+ * colliding across distant turns, which would poison the very retry it
358
+ * triggered).
359
+ */
360
+ const pendingSignatures = [];
361
+ let yieldedDirect = false;
318
362
  let usage;
319
363
  let ttftMs;
320
- for await (const chunk of parseAgySse(response.body, {
321
- signal: options.signal,
322
- onToolSignature: (toolCallId, signature) => {
323
- setThoughtSignature(toolCallId, signature);
364
+ /**
365
+ * Persist the signatures observed so far.
366
+ *
367
+ * Called at every point where the chunks that carried them have been
368
+ * handed off — never on the discard path, so a thrown-away attempt leaves
369
+ * no trace. The hand-off is the only sound anchor: a `functionCall` can
370
+ * arrive AFTER the first `text-delta` (text-then-tool is a common shape),
371
+ * so anchoring at stream end would drop the signature of an already
372
+ * delivered call the moment the stream dies mid-body, silently degrading
373
+ * the next round to the sentinel.
374
+ */
375
+ const commitSignatures = () => {
376
+ for (const [toolCallId, signature] of pendingSignatures) setThoughtSignature(toolCallId, signature);
377
+ pendingSignatures.length = 0;
378
+ };
379
+ /** Hand the held chunks to the consumer. */
380
+ const drainBuffer = () => {
381
+ const drained = bufferedChunks.slice();
382
+ bufferedChunks.length = 0;
383
+ return drained;
384
+ };
385
+ try {
386
+ for await (const chunk of parseAgySse(response.body, {
387
+ signal: options.signal,
388
+ onToolSignature: (toolCallId, signature) => {
389
+ pendingSignatures.push([toolCallId, signature]);
390
+ }
391
+ })) {
392
+ if (chunk.type === "usage") usage = {
393
+ input: chunk.usage.inputTokens,
394
+ output: chunk.usage.outputTokens,
395
+ cacheRead: chunk.usage.cacheReadTokens ?? 0,
396
+ cacheWrite: chunk.usage.cacheWriteTokens ?? 0
397
+ };
398
+ if (yieldedDirect) {
399
+ if (pendingSignatures.length !== 0) commitSignatures();
400
+ yield chunk;
401
+ } else if (chunk.type === "text-delta") {
402
+ yieldedDirect = true;
403
+ commitSignatures();
404
+ ttftMs = Date.now() - attemptStartedAt;
405
+ for (const buffered of drainBuffer()) yield buffered;
406
+ yield chunk;
407
+ } else bufferedChunks.push(chunk);
324
408
  }
325
- })) {
326
- if (chunk.type === "usage") usage = {
327
- input: chunk.usage.inputTokens,
328
- output: chunk.usage.outputTokens,
329
- cacheRead: chunk.usage.cacheReadTokens ?? 0,
330
- cacheWrite: chunk.usage.cacheWriteTokens ?? 0
331
- };
332
- else if (ttftMs === void 0 && (chunk.type === "text-delta" || chunk.type === "reasoning-delta")) ttftMs = Date.now() - startedAt;
333
- yield chunk;
409
+ if (ttftMs === void 0) ttftMs = Date.now() - attemptStartedAt;
410
+ commitSignatures();
411
+ for (const buffered of drainBuffer()) yield buffered;
412
+ await this.options.markSuccess?.(session);
413
+ this.recordUsage(session, options.model, {
414
+ ok: true,
415
+ usage,
416
+ ttftMs
417
+ }, attemptStartedAt);
418
+ return;
419
+ } catch (error) {
420
+ if (error instanceof DOMException && error.name === "AbortError") throw new LlmError("agy stream aborted", "ABORTED", { cause: error });
421
+ if (error instanceof UnmappedFinishReasonError && error.reason === "MALFORMED_FUNCTION_CALL" && !yieldedDirect && malformedAttempt < MAX_MALFORMED_RETRIES) {
422
+ this.recordUsage(session, options.model, {
423
+ ok: false,
424
+ reason: "request-error",
425
+ usage
426
+ }, attemptStartedAt);
427
+ await response.body?.cancel().catch(() => {});
428
+ try {
429
+ await sleepWithSignal(200 * (malformedAttempt + 1), options.signal);
430
+ } catch (sleepError) {
431
+ if (sleepError instanceof DOMException && sleepError.name === "AbortError") throw new LlmError("agy stream aborted", "ABORTED", { cause: sleepError });
432
+ throw sleepError;
433
+ }
434
+ continue;
435
+ }
436
+ const unmappedFinish = error instanceof UnmappedFinishReasonError;
437
+ await this.options.reportFailure(unmappedFinish ? "request-error" : "network-error", session);
438
+ this.recordUsage(session, options.model, {
439
+ ok: false,
440
+ reason: unmappedFinish ? "request-error" : "network-error",
441
+ usage
442
+ }, attemptStartedAt);
443
+ await response.body?.cancel().catch(() => {});
444
+ throw new LlmError(error instanceof Error ? describeFetchError(error) : "agy stream parse failed", UPSTREAM_ERROR_CODE, { cause: error });
334
445
  }
335
- await this.options.markSuccess?.(session);
336
- this.recordUsage(session, options.model, {
337
- ok: true,
338
- usage,
339
- ttftMs
340
- }, startedAt);
341
- } catch (error) {
342
- if (error instanceof DOMException && error.name === "AbortError") throw new LlmError("agy stream aborted", "ABORTED", { cause: error });
343
- const unmappedFinish = error instanceof UnmappedFinishReasonError;
344
- await this.options.reportFailure(unmappedFinish ? "request-error" : "network-error", session);
345
- this.recordUsage(session, options.model, {
346
- ok: false,
347
- reason: unmappedFinish ? "request-error" : "network-error"
348
- }, startedAt);
349
- throw new LlmError(error instanceof Error ? describeFetchError(error) : "agy stream parse failed", UPSTREAM_ERROR_CODE, { cause: error });
350
446
  }
351
447
  }
352
448
  /**
@@ -1641,4 +1737,4 @@ async function buildAgyRuntime(ctx) {
1641
1737
  //#endregion
1642
1738
  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 };
1643
1739
 
1644
- //# sourceMappingURL=plugin-common-CKTYXxSc.mjs.map
1740
+ //# sourceMappingURL=plugin-common-Dwa1K13O.mjs.map