dsh-subscriptions 0.2.0 → 0.2.2

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.
Files changed (99) hide show
  1. package/README.md +212 -180
  2. package/lib/auth/claude-code-creds.js +31 -9
  3. package/lib/auth/device-flow.js +16 -9
  4. package/lib/auth/oauth-flow.js +13 -10
  5. package/lib/auth/rpc.js +74 -23
  6. package/lib/auth/store.js +80 -31
  7. package/lib/client/CursorCard.d.ts +1 -1
  8. package/lib/client/CursorCard.js +50 -16
  9. package/lib/client/ExternalUsageCards.d.ts +1 -1
  10. package/lib/client/ExternalUsageCards.js +37 -16
  11. package/lib/client/ImageGallery.d.ts +2 -2
  12. package/lib/client/ImageGallery.js +92 -33
  13. package/lib/client/ImageGenerateToolview.js +50 -14
  14. package/lib/client/OllamaCard.d.ts +1 -1
  15. package/lib/client/OllamaCard.js +26 -8
  16. package/lib/client/OllamaSettingsManager.js +113 -30
  17. package/lib/client/ProviderAccountManager.d.ts +1 -1
  18. package/lib/client/ProviderAccountManager.js +164 -49
  19. package/lib/client/ProviderModelEditor.js +107 -26
  20. package/lib/client/SpeedSelect.d.ts +62 -14
  21. package/lib/client/SpeedSelect.js +257 -46
  22. package/lib/client/SubscriptionUsageBadge.d.ts +4 -10
  23. package/lib/client/SubscriptionUsageBadge.js +300 -112
  24. package/lib/client/SubscriptionsSection.js +258 -85
  25. package/lib/client/UsageBadgeDisplaySetting.js +35 -12
  26. package/lib/client/UsageMeter.d.ts +1 -1
  27. package/lib/client/UsageMeter.js +62 -24
  28. package/lib/client/VideoGenerateToolview.js +69 -17
  29. package/lib/client/account-preferences.d.ts +8 -0
  30. package/lib/client/account-preferences.js +20 -5
  31. package/lib/client/host-contract.d.ts +11 -2
  32. package/lib/client/host-contract.js +20 -3
  33. package/lib/client/index.d.ts +5 -5
  34. package/lib/client/index.js +34 -12
  35. package/lib/client/locales.d.ts +10 -0
  36. package/lib/client/locales.js +61 -51
  37. package/lib/client/ollama-draft.js +21 -12
  38. package/lib/client/reset-action.js +6 -4
  39. package/lib/client/reset-credits-view.d.ts +1 -1
  40. package/lib/client/reset-credits-view.js +66 -17
  41. package/lib/client/subscription-card-styles.js +94 -24
  42. package/lib/client/usage-color-preferences.js +4 -2
  43. package/lib/client/usage-pace.js +30 -12
  44. package/lib/client.js +287 -47
  45. package/lib/client.js.map +1 -1
  46. package/lib/http.js +1 -1
  47. package/lib/index.d.ts +3 -3
  48. package/lib/index.js +118 -64
  49. package/lib/model-defaults.js +4 -2
  50. package/lib/provider-settings.d.ts +12 -0
  51. package/lib/provider-settings.js +58 -16
  52. package/lib/providers/account-preferences.js +51 -22
  53. package/lib/providers/accounts.js +15 -9
  54. package/lib/providers/antigravity-oauth-client.js +2 -2
  55. package/lib/providers/antigravity.js +141 -68
  56. package/lib/providers/catalog-store.js +120 -41
  57. package/lib/providers/claude-cli-version.js +3 -3
  58. package/lib/providers/claude.d.ts +9 -2
  59. package/lib/providers/claude.js +153 -90
  60. package/lib/providers/codex-client-version.js +3 -3
  61. package/lib/providers/codex-search.js +40 -13
  62. package/lib/providers/codex.js +196 -112
  63. package/lib/providers/common.js +71 -39
  64. package/lib/providers/copilot.js +143 -82
  65. package/lib/providers/cursor-adapter.js +42 -18
  66. package/lib/providers/cursor-auth.d.ts +1 -1
  67. package/lib/providers/cursor-auth.js +69 -24
  68. package/lib/providers/cursor-model-settings.js +14 -5
  69. package/lib/providers/cursor-usage.js +27 -13
  70. package/lib/providers/external-usage-controller.js +14 -5
  71. package/lib/providers/external-usage.js +59 -23
  72. package/lib/providers/grok.js +152 -89
  73. package/lib/providers/image-pool.js +29 -11
  74. package/lib/providers/minimax-usage.js +31 -11
  75. package/lib/providers/npm-cli-version.js +10 -3
  76. package/lib/providers/ollama-settings.js +24 -8
  77. package/lib/providers/ollama-web.js +29 -11
  78. package/lib/providers/ollama.js +129 -66
  79. package/lib/providers/pool-family.js +11 -3
  80. package/lib/providers/pool-health.js +28 -8
  81. package/lib/providers/pool-usage.js +16 -9
  82. package/lib/providers/pool.d.ts +8 -0
  83. package/lib/providers/pool.js +81 -42
  84. package/lib/providers/rate-limit.js +8 -2
  85. package/lib/providers/replay.js +40 -17
  86. package/lib/providers/reset-redemption.js +18 -6
  87. package/lib/tools/image-generate.js +123 -65
  88. package/lib/tools/registration.js +5 -2
  89. package/lib/tools/video-generate.js +51 -31
  90. package/lib/tools/x-search.js +50 -22
  91. package/lib/translate/anthropic.js +85 -37
  92. package/lib/translate/antigravity-schema.js +71 -28
  93. package/lib/translate/antigravity-thinking.js +22 -9
  94. package/lib/translate/antigravity.js +141 -50
  95. package/lib/translate/chat-completions.js +67 -23
  96. package/lib/translate/resolved.js +55 -21
  97. package/lib/translate/responses.js +69 -29
  98. package/lib/translate/sse.js +4 -1
  99. package/package.json +26 -4
@@ -22,37 +22,54 @@ import { antigravityToolParameters } from './antigravity-schema.js';
22
22
  * successful results retain their existing serialized shape.
23
23
  */
24
24
  function toolResultValue(block) {
25
- const text = block.content.map(part => part.type === 'text' ? part.text : '').join('');
25
+ const text = block.content
26
+ .map((part) => (part.type === 'text' ? part.text : ''))
27
+ .join('');
26
28
  let parsed;
27
29
  try {
28
30
  parsed = JSON.parse(text);
29
31
  }
30
32
  catch {
31
- return { output: text, ...block.isError === true ? { isError: true } : {} };
33
+ return {
34
+ output: text,
35
+ ...(block.isError === true ? { isError: true } : {}),
36
+ };
32
37
  }
33
38
  if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {
34
39
  const value = parsed;
35
40
  return block.isError === true ? { ...value, isError: true } : value;
36
41
  }
37
- return { output: parsed, ...block.isError === true ? { isError: true } : {} };
42
+ return {
43
+ output: parsed,
44
+ ...(block.isError === true ? { isError: true } : {}),
45
+ };
38
46
  }
39
47
  /** Safely read per-block replay metadata emitted by this adapter. */
40
48
  function replayBlocks(message, model) {
41
49
  const source = message.source;
42
- if (model !== undefined && (message.source?.kind !== 'model'
43
- || message.source.provider !== 'antigravity' || message.source.model !== model))
50
+ if (model !== undefined &&
51
+ (message.source?.kind !== 'model' ||
52
+ message.source.provider !== 'antigravity' ||
53
+ message.source.model !== model))
44
54
  return [];
45
- if (source?.kind !== 'model' || typeof source.replayState !== 'object' || source.replayState === null)
55
+ if (source?.kind !== 'model' ||
56
+ typeof source.replayState !== 'object' ||
57
+ source.replayState === null)
46
58
  return [];
47
59
  const envelope = source.replayState;
48
60
  const response = envelope.response;
49
- if (response === null || response?.kind !== 'antigravity' || response.version !== 1 || !Array.isArray(envelope.blocks))
61
+ if (response === null ||
62
+ response?.kind !== 'antigravity' ||
63
+ response.version !== 1 ||
64
+ !Array.isArray(envelope.blocks))
50
65
  return [];
51
66
  return envelope.blocks.map((entry) => {
52
67
  if (typeof entry !== 'object' || entry === null)
53
68
  return {};
54
69
  const signature = entry.thoughtSignature;
55
- return typeof signature === 'string' && signature.length > 0 ? { thoughtSignature: signature } : {};
70
+ return typeof signature === 'string' && signature.length > 0
71
+ ? { thoughtSignature: signature }
72
+ : {};
56
73
  });
57
74
  }
58
75
  /** Map harness tool schemas to Gemini function declarations. */
@@ -60,13 +77,15 @@ export function toAntigravityTools(tools, model = 'gemini') {
60
77
  if (tools.length === 0)
61
78
  return [];
62
79
  const legacy = /^(claude-|gpt-oss-)/.test(model);
63
- return [{
64
- functionDeclarations: tools.map(tool => ({
80
+ return [
81
+ {
82
+ functionDeclarations: tools.map((tool) => ({
65
83
  name: tool.name,
66
84
  description: tool.description,
67
85
  [legacy ? 'parameters' : 'parametersJsonSchema']: antigravityToolParameters(tool.parameters, legacy),
68
86
  })),
69
- }];
87
+ },
88
+ ];
70
89
  }
71
90
  const SKIP_THOUGHT_SIGNATURE_VALIDATOR = 'skip_thought_signature_validator';
72
91
  /** Gemini 3 validates the first function call in every model tool-call step. */
@@ -85,18 +104,27 @@ export function toAntigravityContents(messages, model) {
85
104
  if (message.role === 'system')
86
105
  continue;
87
106
  if (message.role === 'tool') {
88
- const id = message.toolCallId ?? message.tool_call_id
89
- ?? (message.source?.kind === 'tool' ? String(message.source.callId) : undefined);
107
+ const id = message.toolCallId ??
108
+ message.tool_call_id ??
109
+ (message.source?.kind === 'tool'
110
+ ? String(message.source.callId)
111
+ : undefined);
90
112
  if (id === undefined)
91
113
  throw new LlmError('tool result has no call id', 'INVALID_REQUEST');
92
- const part = { functionResponse: {
114
+ const part = {
115
+ functionResponse: {
93
116
  id,
94
117
  name: callNames.get(id) ?? '',
95
118
  response: toolResultValue({
96
- type: 'tool-result', toolCallId: ToolCallId(id), content: message.content,
97
- ...message.isError === undefined ? {} : { isError: message.isError },
119
+ type: 'tool-result',
120
+ toolCallId: ToolCallId(id),
121
+ content: message.content,
122
+ ...(message.isError === undefined
123
+ ? {}
124
+ : { isError: message.isError }),
98
125
  }),
99
- } };
126
+ },
127
+ };
100
128
  const previous = out.at(-1);
101
129
  if (previous?.role === 'user')
102
130
  previous.parts.push(part);
@@ -115,7 +143,9 @@ export function toAntigravityContents(messages, model) {
115
143
  // Antigravity's Claude-backed models reject empty text parts.
116
144
  parts.push({
117
145
  text: block.text.trim().length > 0 ? block.text : '.',
118
- ...metadata[index]?.thoughtSignature === undefined ? {} : { thoughtSignature: metadata[index].thoughtSignature },
146
+ ...(metadata[index]?.thoughtSignature === undefined
147
+ ? {}
148
+ : { thoughtSignature: metadata[index].thoughtSignature }),
119
149
  });
120
150
  break;
121
151
  case 'reasoning': {
@@ -126,7 +156,9 @@ export function toAntigravityContents(messages, model) {
126
156
  }
127
157
  case 'image':
128
158
  if ('dataBase64' in block) {
129
- parts.push({ inlineData: { mimeType: block.mediaType, data: block.dataBase64 } });
159
+ parts.push({
160
+ inlineData: { mimeType: block.mediaType, data: block.dataBase64 },
161
+ });
130
162
  }
131
163
  break;
132
164
  case 'tool-call': {
@@ -139,12 +171,13 @@ export function toAntigravityContents(messages, model) {
139
171
  args = {};
140
172
  }
141
173
  const replaySignature = metadata[index]?.thoughtSignature;
142
- const thoughtSignature = replaySignature ?? (!sawFunctionCall && requiresThoughtSignatures(model)
143
- ? SKIP_THOUGHT_SIGNATURE_VALIDATOR
144
- : undefined);
174
+ const thoughtSignature = replaySignature ??
175
+ (!sawFunctionCall && requiresThoughtSignatures(model)
176
+ ? SKIP_THOUGHT_SIGNATURE_VALIDATOR
177
+ : undefined);
145
178
  parts.push({
146
179
  functionCall: { id: String(block.id), name: block.name, args },
147
- ...thoughtSignature === undefined ? {} : { thoughtSignature },
180
+ ...(thoughtSignature === undefined ? {} : { thoughtSignature }),
148
181
  });
149
182
  sawFunctionCall = true;
150
183
  break;
@@ -186,19 +219,29 @@ export function toAntigravityRequest(options, messages, projectId, forStreaming
186
219
  const thinkingConfig = antigravityThinking(options.model, options.reasoningEffort, options.maxTokens);
187
220
  let maxOutputTokens = options.maxTokens;
188
221
  // Claude models on the streaming path reject maxOutputTokens >= 65535
189
- if (forStreaming && maxOutputTokens !== undefined && /^claude-/.test(options.model) && maxOutputTokens >= 65535) {
222
+ if (forStreaming &&
223
+ maxOutputTokens !== undefined &&
224
+ /^claude-/.test(options.model) &&
225
+ maxOutputTokens >= 65535) {
190
226
  maxOutputTokens = 64000;
191
227
  }
192
228
  const generationConfig = {
193
- ...maxOutputTokens === undefined ? {} : { maxOutputTokens },
194
- ...options.temperature === undefined ? {} : { temperature: options.temperature },
195
- ...options.stop === undefined || options.stop.length === 0 ? {} : { stopSequences: options.stop },
196
- ...thinkingConfig === undefined ? {} : { thinkingConfig },
229
+ ...(maxOutputTokens === undefined ? {} : { maxOutputTokens }),
230
+ ...(options.temperature === undefined
231
+ ? {}
232
+ : { temperature: options.temperature }),
233
+ ...(options.stop === undefined || options.stop.length === 0
234
+ ? {}
235
+ : { stopSequences: options.stop }),
236
+ ...(thinkingConfig === undefined ? {} : { thinkingConfig }),
197
237
  };
198
- const systemTexts = messages.flatMap(message => message.role === 'system'
199
- ? message.content.filter((block) => block.type === 'text').map(block => block.text)
238
+ const systemTexts = messages.flatMap((message) => message.role === 'system'
239
+ ? message.content
240
+ .filter((block) => block.type === 'text')
241
+ .map((block) => block.text)
200
242
  : []);
201
- const system = options.system ?? (systemTexts.length > 0 ? systemTexts.join('\n\n') : undefined);
243
+ const system = options.system ??
244
+ (systemTexts.length > 0 ? systemTexts.join('\n\n') : undefined);
202
245
  const sessionId = options.sessionId === undefined ? randomUUID() : String(options.sessionId);
203
246
  return {
204
247
  project: projectId,
@@ -209,12 +252,20 @@ export function toAntigravityRequest(options, messages, projectId, forStreaming
209
252
  request: {
210
253
  contents: toAntigravityContents(messages, options.model),
211
254
  sessionId,
212
- ...system === undefined || system.length === 0 ? {} : { systemInstruction: { parts: [{ text: system }] } },
213
- ...tools.length === 0 ? {} : {
214
- tools,
215
- toolConfig: { functionCallingConfig: { mode: 'VALIDATED' } },
216
- },
217
- ...Object.keys(generationConfig).length === 0 ? {} : { generationConfig },
255
+ ...(system === undefined || system.length === 0
256
+ ? {}
257
+ : { systemInstruction: { parts: [{ text: system }] } }),
258
+ ...(tools.length === 0
259
+ ? {}
260
+ : {
261
+ tools,
262
+ toolConfig: {
263
+ functionCallingConfig: { mode: 'VALIDATED' },
264
+ },
265
+ }),
266
+ ...(Object.keys(generationConfig).length === 0
267
+ ? {}
268
+ : { generationConfig }),
218
269
  },
219
270
  };
220
271
  }
@@ -224,8 +275,10 @@ export function mapAntigravityUsage(metadata) {
224
275
  return {
225
276
  inputTokens: Math.max(0, (metadata.promptTokenCount ?? 0) - cached),
226
277
  outputTokens: metadata.candidatesTokenCount ?? 0,
227
- ...cached > 0 ? { cacheReadTokens: cached } : {},
228
- ...metadata.thoughtsTokenCount === undefined ? {} : { reasoningTokens: metadata.thoughtsTokenCount },
278
+ ...(cached > 0 ? { cacheReadTokens: cached } : {}),
279
+ ...(metadata.thoughtsTokenCount === undefined
280
+ ? {}
281
+ : { reasoningTokens: metadata.thoughtsTokenCount }),
229
282
  };
230
283
  }
231
284
  /** Push translator for both parsed SSE events and one non-stream response. */
@@ -237,7 +290,12 @@ export class AntigravityStreamTranslator {
237
290
  sawToolCall = false;
238
291
  terminated = false;
239
292
  open(key, kind, chunks, values = {}) {
240
- const block = { index: this.nextIndex++, kind, text: '', ...values };
293
+ const block = {
294
+ index: this.nextIndex++,
295
+ kind,
296
+ text: '',
297
+ ...values,
298
+ };
241
299
  this.blocks.set(key, block);
242
300
  chunks.push({ type: 'block-start', index: block.index, blockType: kind });
243
301
  return block;
@@ -259,7 +317,10 @@ export class AntigravityStreamTranslator {
259
317
  name: block.name ?? '',
260
318
  arguments: block.text,
261
319
  };
262
- this.closed[block.index] = block.thoughtSignature === undefined ? {} : { thoughtSignature: block.thoughtSignature };
320
+ this.closed[block.index] =
321
+ block.thoughtSignature === undefined
322
+ ? {}
323
+ : { thoughtSignature: block.thoughtSignature };
263
324
  chunks.push({ type: 'block-end', index: block.index, block: content });
264
325
  }
265
326
  closeAll(chunks) {
@@ -268,24 +329,45 @@ export class AntigravityStreamTranslator {
268
329
  }
269
330
  finish(reason) {
270
331
  const replayState = {
271
- response: { kind: 'antigravity', version: 1 },
332
+ response: {
333
+ kind: 'antigravity',
334
+ version: 1,
335
+ },
272
336
  blocks: this.closed,
273
337
  };
274
338
  if (!this.sawContent) {
275
339
  return {
276
340
  type: 'finish',
277
- reason: { kind: 'error', failure: { message: 'Antigravity returned no content', code: EMPTY_RESPONSE_CODE } },
341
+ reason: {
342
+ kind: 'error',
343
+ failure: {
344
+ message: 'Antigravity returned no content',
345
+ code: EMPTY_RESPONSE_CODE,
346
+ },
347
+ },
278
348
  };
279
349
  }
280
350
  if (reason === 'MAX_TOKENS')
281
351
  return { type: 'finish', reason: { kind: 'max-tokens' }, replayState };
282
- if (reason === 'SAFETY' || reason === 'RECITATION' || reason === 'BLOCKLIST') {
352
+ if (reason === 'SAFETY' ||
353
+ reason === 'RECITATION' ||
354
+ reason === 'BLOCKLIST') {
283
355
  return {
284
356
  type: 'finish',
285
- reason: { kind: 'error', failure: { message: `Antigravity blocked the response (${reason})`, code: 'CONTENT_FILTER' } },
357
+ reason: {
358
+ kind: 'error',
359
+ failure: {
360
+ message: `Antigravity blocked the response (${reason})`,
361
+ code: 'CONTENT_FILTER',
362
+ },
363
+ },
286
364
  };
287
365
  }
288
- return { type: 'finish', reason: { kind: this.sawToolCall ? 'tool-calls' : 'stop' }, replayState };
366
+ return {
367
+ type: 'finish',
368
+ reason: { kind: this.sawToolCall ? 'tool-calls' : 'stop' },
369
+ replayState,
370
+ };
289
371
  }
290
372
  /** Process one decoded Antigravity response frame. */
291
373
  push(event) {
@@ -294,13 +376,20 @@ export class AntigravityStreamTranslator {
294
376
  const chunks = [];
295
377
  const candidate = event.response?.candidates?.[0];
296
378
  for (const [partIndex, part] of (candidate?.content?.parts ?? []).entries()) {
297
- if (part.thought === true && typeof part.text === 'string' && part.text.length > 0) {
298
- const block = this.blocks.get('reasoning') ?? this.open('reasoning', 'reasoning', chunks);
379
+ if (part.thought === true &&
380
+ typeof part.text === 'string' &&
381
+ part.text.length > 0) {
382
+ const block = this.blocks.get('reasoning') ??
383
+ this.open('reasoning', 'reasoning', chunks);
299
384
  block.text += part.text;
300
385
  if (part.thoughtSignature !== undefined)
301
386
  block.thoughtSignature = part.thoughtSignature;
302
387
  this.sawContent = true;
303
- chunks.push({ type: 'reasoning-delta', index: block.index, text: part.text });
388
+ chunks.push({
389
+ type: 'reasoning-delta',
390
+ index: block.index,
391
+ text: part.text,
392
+ });
304
393
  }
305
394
  else if (typeof part.text === 'string' && part.text.length > 0) {
306
395
  const block = this.blocks.get('text') ?? this.open('text', 'text', chunks);
@@ -320,7 +409,9 @@ export class AntigravityStreamTranslator {
320
409
  const block = this.open(key, 'tool-call', chunks, {
321
410
  id,
322
411
  name: call.name ?? '',
323
- ...part.thoughtSignature === undefined ? {} : { thoughtSignature: part.thoughtSignature },
412
+ ...(part.thoughtSignature === undefined
413
+ ? {}
414
+ : { thoughtSignature: part.thoughtSignature }),
324
415
  });
325
416
  block.text = args;
326
417
  this.sawContent = true;
@@ -11,7 +11,9 @@ import { parseSse } from './sse.js';
11
11
  import { withToolResultImages } from './resolved.js';
12
12
  /** Flatten a tool result's content to plain text for a `tool` message. */
13
13
  function toolResultText(block) {
14
- return block.content.map(part => (part.type === 'text' ? part.text : '')).join('');
14
+ return block.content
15
+ .map((part) => (part.type === 'text' ? part.text : ''))
16
+ .join('');
15
17
  }
16
18
  /**
17
19
  * Convert harness messages into chat completions `messages`. System-role
@@ -37,15 +39,27 @@ export function toChatMessages(messages, system) {
37
39
  continue;
38
40
  }
39
41
  if (message.role === 'tool') {
40
- const callId = message.toolCallId ?? message.tool_call_id
41
- ?? (message.source?.kind === 'tool' ? String(message.source.callId) : undefined);
42
+ const callId = message.toolCallId ??
43
+ message.tool_call_id ??
44
+ (message.source?.kind === 'tool'
45
+ ? String(message.source.callId)
46
+ : undefined);
42
47
  if (callId === undefined)
43
48
  throw new LlmError('tool result has no call id', 'INVALID_REQUEST');
44
- out.push({ role: 'tool', tool_call_id: callId, content: message.content.map(block => block.type === 'text' ? block.text : '').join('') });
49
+ out.push({
50
+ role: 'tool',
51
+ tool_call_id: callId,
52
+ content: message.content
53
+ .map((block) => (block.type === 'text' ? block.text : ''))
54
+ .join(''),
55
+ });
45
56
  continue;
46
57
  }
47
58
  if (message.role === 'developer') {
48
- const content = message.content.filter(block => block.type === 'text').map(block => block.text).join('');
59
+ const content = message.content
60
+ .filter((block) => block.type === 'text')
61
+ .map((block) => block.text)
62
+ .join('');
49
63
  if (content.length > 0)
50
64
  out.push({ role: 'developer', content });
51
65
  continue;
@@ -79,7 +93,9 @@ export function toChatMessages(messages, system) {
79
93
  if ('dataBase64' in block) {
80
94
  parts.push({
81
95
  type: 'image_url',
82
- image_url: { url: `data:${block.mediaType};base64,${block.dataBase64}` },
96
+ image_url: {
97
+ url: `data:${block.mediaType};base64,${block.dataBase64}`,
98
+ },
83
99
  });
84
100
  }
85
101
  break;
@@ -123,7 +139,7 @@ export function toChatMessages(messages, system) {
123
139
  out.push({
124
140
  role: 'assistant',
125
141
  content: texts.join('\n'),
126
- ...toolCalls.length > 0 ? { tool_calls: toolCalls } : {},
142
+ ...(toolCalls.length > 0 ? { tool_calls: toolCalls } : {}),
127
143
  });
128
144
  }
129
145
  const systemText = system ?? (systemTexts.length > 0 ? systemTexts.join('\n\n') : undefined);
@@ -137,7 +153,7 @@ export function toChatMessages(messages, system) {
137
153
  * @returns the wire `tools` array.
138
154
  */
139
155
  export function toChatTools(tools) {
140
- return tools.map(tool => ({
156
+ return tools.map((tool) => ({
141
157
  type: 'function',
142
158
  function: {
143
159
  name: tool.name,
@@ -158,8 +174,8 @@ export function mapChatCompletionsUsage(usage) {
158
174
  return {
159
175
  inputTokens: usage.prompt_tokens - (cached ?? 0),
160
176
  outputTokens: usage.completion_tokens,
161
- ...cached !== undefined ? { cacheReadTokens: cached } : {},
162
- ...reasoning !== undefined ? { reasoningTokens: reasoning } : {},
177
+ ...(cached !== undefined ? { cacheReadTokens: cached } : {}),
178
+ ...(reasoning !== undefined ? { reasoningTokens: reasoning } : {}),
163
179
  };
164
180
  }
165
181
  /** Assemble the final ContentBlock for one open block. */
@@ -208,7 +224,7 @@ export class ChatCompletionsStreamTranslator {
208
224
  kind,
209
225
  text: '',
210
226
  callId,
211
- ...name === undefined ? {} : { name },
227
+ ...(name === undefined ? {} : { name }),
212
228
  };
213
229
  this.blocks.set(key, block);
214
230
  this.order.push(block);
@@ -220,7 +236,11 @@ export class ChatCompletionsStreamTranslator {
220
236
  if (block === undefined)
221
237
  return;
222
238
  this.blocks.delete(key);
223
- chunks.push({ type: 'block-end', index: block.index, block: closeBlock(block) });
239
+ chunks.push({
240
+ type: 'block-end',
241
+ index: block.index,
242
+ block: closeBlock(block),
243
+ });
224
244
  }
225
245
  closeAll(chunks) {
226
246
  for (const key of [...this.blocks.keys()])
@@ -233,7 +253,10 @@ export class ChatCompletionsStreamTranslator {
233
253
  type: 'finish',
234
254
  reason: {
235
255
  kind: 'error',
236
- failure: { message: 'model returned a completed response with no content', code: EMPTY_RESPONSE_CODE },
256
+ failure: {
257
+ message: 'model returned a completed response with no content',
258
+ code: EMPTY_RESPONSE_CODE,
259
+ },
237
260
  },
238
261
  };
239
262
  }
@@ -247,17 +270,26 @@ export class ChatCompletionsStreamTranslator {
247
270
  type: 'finish',
248
271
  reason: {
249
272
  kind: 'error',
250
- failure: { message: 'the response was blocked by the provider content filter', code: 'CONTENT_FILTER' },
273
+ failure: {
274
+ message: 'the response was blocked by the provider content filter',
275
+ code: 'CONTENT_FILTER',
276
+ },
251
277
  },
252
278
  };
253
279
  default:
254
- return { type: 'finish', reason: { kind: this.sawToolCall ? 'tool-calls' : 'stop' } };
280
+ return {
281
+ type: 'finish',
282
+ reason: { kind: this.sawToolCall ? 'tool-calls' : 'stop' },
283
+ };
255
284
  }
256
285
  }
257
286
  /** Usage, then the armed finish: the only order the harness accepts. */
258
287
  drainTerminal(chunks) {
259
288
  if (this.pendingUsage !== undefined) {
260
- chunks.push({ type: 'usage', usage: mapChatCompletionsUsage(this.pendingUsage) });
289
+ chunks.push({
290
+ type: 'usage',
291
+ usage: mapChatCompletionsUsage(this.pendingUsage),
292
+ });
261
293
  this.pendingUsage = undefined;
262
294
  }
263
295
  if (this.armedFinish !== undefined) {
@@ -285,15 +317,26 @@ export class ChatCompletionsStreamTranslator {
285
317
  if (typeof delta.content === 'string' && delta.content.length > 0) {
286
318
  const block = this.blocks.get('content') ?? this.open('content', 'text', chunks);
287
319
  block.text += delta.content;
288
- chunks.push({ type: 'text-delta', index: block.index, text: delta.content });
320
+ chunks.push({
321
+ type: 'text-delta',
322
+ index: block.index,
323
+ text: delta.content,
324
+ });
289
325
  }
290
- const reasoning = typeof delta.reasoning_content === 'string' ? delta.reasoning_content
291
- : typeof delta.reasoning_text === 'string' ? delta.reasoning_text
326
+ const reasoning = typeof delta.reasoning_content === 'string'
327
+ ? delta.reasoning_content
328
+ : typeof delta.reasoning_text === 'string'
329
+ ? delta.reasoning_text
292
330
  : undefined;
293
331
  if (reasoning !== undefined && reasoning.length > 0) {
294
- const block = this.blocks.get('reasoning') ?? this.open('reasoning', 'reasoning', chunks);
332
+ const block = this.blocks.get('reasoning') ??
333
+ this.open('reasoning', 'reasoning', chunks);
295
334
  block.text += reasoning;
296
- chunks.push({ type: 'reasoning-delta', index: block.index, text: reasoning });
335
+ chunks.push({
336
+ type: 'reasoning-delta',
337
+ index: block.index,
338
+ text: reasoning,
339
+ });
297
340
  }
298
341
  for (const call of delta.tool_calls ?? []) {
299
342
  const key = `call:${String(call.index ?? 0)}`;
@@ -305,11 +348,12 @@ export class ChatCompletionsStreamTranslator {
305
348
  type: 'tool-call-delta',
306
349
  index: block.index,
307
350
  id: ToolCallId(block.callId),
308
- ...block.name === undefined ? {} : { name: block.name },
351
+ ...(block.name === undefined ? {} : { name: block.name }),
309
352
  argumentsDelta: '',
310
353
  });
311
354
  }
312
- if (call.function?.arguments !== undefined && call.function.arguments.length > 0) {
355
+ if (call.function?.arguments !== undefined &&
356
+ call.function.arguments.length > 0) {
313
357
  block.text += call.function.arguments;
314
358
  chunks.push({
315
359
  type: 'tool-call-delta',