dsh-subscriptions 0.2.0 → 0.2.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.
Files changed (105) hide show
  1. package/README.md +220 -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.d.ts +8 -4
  6. package/lib/auth/rpc.js +89 -25
  7. package/lib/auth/store.js +80 -31
  8. package/lib/client/CursorCard.d.ts +1 -1
  9. package/lib/client/CursorCard.js +50 -16
  10. package/lib/client/ExternalUsageCards.d.ts +1 -1
  11. package/lib/client/ExternalUsageCards.js +37 -16
  12. package/lib/client/ImageGallery.d.ts +2 -2
  13. package/lib/client/ImageGallery.js +92 -33
  14. package/lib/client/ImageGenerateToolview.js +50 -14
  15. package/lib/client/OllamaCard.d.ts +1 -1
  16. package/lib/client/OllamaCard.js +26 -8
  17. package/lib/client/OllamaSettingsManager.js +113 -30
  18. package/lib/client/ProviderAccountManager.d.ts +1 -1
  19. package/lib/client/ProviderAccountManager.js +164 -49
  20. package/lib/client/ProviderModelEditor.js +137 -34
  21. package/lib/client/SpeedSelect.d.ts +62 -14
  22. package/lib/client/SpeedSelect.js +261 -49
  23. package/lib/client/SubscriptionUsageBadge.d.ts +4 -10
  24. package/lib/client/SubscriptionUsageBadge.js +300 -112
  25. package/lib/client/SubscriptionsSection.d.ts +2 -0
  26. package/lib/client/SubscriptionsSection.js +265 -85
  27. package/lib/client/UsageBadgeDisplaySetting.js +35 -12
  28. package/lib/client/UsageMeter.d.ts +1 -1
  29. package/lib/client/UsageMeter.js +62 -24
  30. package/lib/client/VideoGenerateToolview.js +69 -17
  31. package/lib/client/account-preferences.d.ts +8 -0
  32. package/lib/client/account-preferences.js +20 -5
  33. package/lib/client/host-contract.d.ts +11 -2
  34. package/lib/client/host-contract.js +20 -3
  35. package/lib/client/index.d.ts +5 -5
  36. package/lib/client/index.js +34 -12
  37. package/lib/client/locales.d.ts +22 -0
  38. package/lib/client/locales.js +73 -51
  39. package/lib/client/model-filter.d.ts +37 -0
  40. package/lib/client/model-filter.js +45 -0
  41. package/lib/client/ollama-draft.js +21 -12
  42. package/lib/client/reset-action.js +6 -4
  43. package/lib/client/reset-credits-view.d.ts +1 -1
  44. package/lib/client/reset-credits-view.js +66 -17
  45. package/lib/client/subscription-card-styles.js +94 -24
  46. package/lib/client/usage-color-preferences.js +4 -2
  47. package/lib/client/usage-pace.js +30 -12
  48. package/lib/client/usage-window-filter.d.ts +7 -0
  49. package/lib/client/usage-window-filter.js +12 -0
  50. package/lib/client.js +480 -136
  51. package/lib/client.js.map +1 -1
  52. package/lib/http.js +1 -1
  53. package/lib/index.d.ts +3 -3
  54. package/lib/index.js +167 -76
  55. package/lib/model-defaults.js +4 -2
  56. package/lib/provider-settings.d.ts +17 -0
  57. package/lib/provider-settings.js +79 -16
  58. package/lib/providers/account-preferences.js +51 -22
  59. package/lib/providers/accounts.js +15 -9
  60. package/lib/providers/antigravity-oauth-client.js +2 -2
  61. package/lib/providers/antigravity.js +141 -68
  62. package/lib/providers/catalog-store.js +120 -41
  63. package/lib/providers/claude-cli-version.js +3 -3
  64. package/lib/providers/claude.d.ts +9 -2
  65. package/lib/providers/claude.js +153 -90
  66. package/lib/providers/codex-client-version.js +3 -3
  67. package/lib/providers/codex-search.js +40 -13
  68. package/lib/providers/codex.js +196 -112
  69. package/lib/providers/common.js +71 -39
  70. package/lib/providers/copilot.js +143 -82
  71. package/lib/providers/cursor-adapter.js +42 -18
  72. package/lib/providers/cursor-auth.d.ts +1 -1
  73. package/lib/providers/cursor-auth.js +69 -24
  74. package/lib/providers/cursor-model-settings.js +14 -5
  75. package/lib/providers/cursor-usage.js +27 -13
  76. package/lib/providers/external-usage-controller.js +14 -5
  77. package/lib/providers/external-usage.js +59 -23
  78. package/lib/providers/grok.js +152 -89
  79. package/lib/providers/image-pool.js +29 -11
  80. package/lib/providers/minimax-usage.js +31 -11
  81. package/lib/providers/npm-cli-version.js +10 -3
  82. package/lib/providers/ollama-settings.js +24 -8
  83. package/lib/providers/ollama-web.js +29 -11
  84. package/lib/providers/ollama.js +129 -66
  85. package/lib/providers/pool-family.js +11 -3
  86. package/lib/providers/pool-health.js +28 -8
  87. package/lib/providers/pool-usage.js +16 -9
  88. package/lib/providers/pool.d.ts +8 -0
  89. package/lib/providers/pool.js +81 -42
  90. package/lib/providers/rate-limit.js +8 -2
  91. package/lib/providers/replay.js +40 -17
  92. package/lib/providers/reset-redemption.js +18 -6
  93. package/lib/tools/image-generate.js +123 -65
  94. package/lib/tools/registration.js +5 -2
  95. package/lib/tools/video-generate.js +51 -31
  96. package/lib/tools/x-search.js +50 -22
  97. package/lib/translate/anthropic.js +85 -37
  98. package/lib/translate/antigravity-schema.js +71 -28
  99. package/lib/translate/antigravity-thinking.js +22 -9
  100. package/lib/translate/antigravity.js +151 -52
  101. package/lib/translate/chat-completions.js +67 -23
  102. package/lib/translate/resolved.js +55 -21
  103. package/lib/translate/responses.js +69 -29
  104. package/lib/translate/sse.js +4 -1
  105. package/package.json +27 -4
@@ -47,7 +47,9 @@ export function buildXSearchRequest(args) {
47
47
  function normalizeHandles(value, field) {
48
48
  if (value === undefined)
49
49
  return [];
50
- const handles = value.map(handle => handle.trim().replace(/^@+/, '')).filter(handle => handle.length > 0);
50
+ const handles = value
51
+ .map((handle) => handle.trim().replace(/^@+/, ''))
52
+ .filter((handle) => handle.length > 0);
51
53
  if (handles.length > MAX_HANDLES)
52
54
  throw new Error(`x_search: ${field} supports at most ${MAX_HANDLES} handles`);
53
55
  return handles;
@@ -75,13 +77,16 @@ export function parseXSearchResponse(payload) {
75
77
  const parts = [];
76
78
  if (Array.isArray(body.output)) {
77
79
  for (const item of body.output) {
78
- if (!isRecord(item) || item.type !== 'message' || !Array.isArray(item.content))
80
+ if (!isRecord(item) ||
81
+ item.type !== 'message' ||
82
+ !Array.isArray(item.content))
79
83
  continue;
80
84
  for (const part of item.content) {
81
85
  if (!isRecord(part))
82
86
  continue;
83
- if ((part.type === 'output_text' || part.type === 'text')
84
- && typeof part.text === 'string' && part.text.trim().length > 0) {
87
+ if ((part.type === 'output_text' || part.type === 'text') &&
88
+ typeof part.text === 'string' &&
89
+ part.text.trim().length > 0) {
85
90
  parts.push(part.text.trim());
86
91
  }
87
92
  if (Array.isArray(part.annotations)) {
@@ -109,10 +114,14 @@ function truncate(text, max = 60) {
109
114
  export function createXSearchTool(options) {
110
115
  return defineTool({
111
116
  name: 'x_search',
112
- description: "Search X (Twitter) posts, profiles, and threads using the grok subscription's hosted xAI x_search. "
113
- + 'Use this for current discussion, reactions, or claims on X rather than general web pages.',
117
+ description: "Search X (Twitter) posts, profiles, and threads using the grok subscription's hosted xAI x_search. " +
118
+ 'Use this for current discussion, reactions, or claims on X rather than general web pages.',
114
119
  parameters: {
115
- query: { type: 'string', required: true, description: 'What to look up on X.' },
120
+ query: {
121
+ type: 'string',
122
+ required: true,
123
+ description: 'What to look up on X.',
124
+ },
116
125
  allowed_x_handles: {
117
126
  type: 'array',
118
127
  items: { type: 'string' },
@@ -123,8 +132,14 @@ export function createXSearchTool(options) {
123
132
  items: { type: 'string' },
124
133
  description: 'X handles to exclude (max 10).',
125
134
  },
126
- from_date: { type: 'string', description: 'Optional start date in YYYY-MM-DD format.' },
127
- to_date: { type: 'string', description: 'Optional end date in YYYY-MM-DD format.' },
135
+ from_date: {
136
+ type: 'string',
137
+ description: 'Optional start date in YYYY-MM-DD format.',
138
+ },
139
+ to_date: {
140
+ type: 'string',
141
+ description: 'Optional end date in YYYY-MM-DD format.',
142
+ },
128
143
  enable_image_understanding: {
129
144
  type: 'boolean',
130
145
  description: 'Whether xAI should analyze images attached to matching posts.',
@@ -139,19 +154,28 @@ export function createXSearchTool(options) {
139
154
  type: 'object',
140
155
  properties: {
141
156
  answer: { type: 'string', required: true },
142
- citations: { type: 'array', items: { type: 'string' }, required: true },
157
+ citations: {
158
+ type: 'array',
159
+ items: { type: 'string' },
160
+ required: true,
161
+ },
143
162
  },
144
163
  additionalProperties: false,
145
164
  },
146
- render: (_args, value) => [{
165
+ render: (_args, value) => [
166
+ {
147
167
  type: 'text',
148
168
  text: value.citations.length > 0
149
- ? `${value.answer}\n\nSources:\n${value.citations.map(citation => `- ${citation}`).join('\n')}`
169
+ ? `${value.answer}\n\nSources:\n${value.citations.map((citation) => `- ${citation}`).join('\n')}`
150
170
  : value.answer,
151
- }],
152
- presentationMeta: (_args, value) => ({ answer: value.answer, citations: value.citations }),
171
+ },
172
+ ],
173
+ presentationMeta: (_args, value) => ({
174
+ answer: value.answer,
175
+ citations: value.citations,
176
+ }),
153
177
  },
154
- presentCall: args => ({
178
+ presentCall: (args) => ({
155
179
  card: 'generic',
156
180
  title: `x_search: ${truncate(args.query)}`,
157
181
  kind: 'search',
@@ -159,15 +183,19 @@ export function createXSearchTool(options) {
159
183
  presentResult: (_args, result) => {
160
184
  if (result.isError || !isRecord(result.meta))
161
185
  return undefined;
162
- const citations = Array.isArray(result.meta.citations) ? result.meta.citations : [];
186
+ const citations = Array.isArray(result.meta.citations)
187
+ ? result.meta.citations
188
+ : [];
163
189
  return {
164
190
  card: 'web',
165
191
  kind: 'search',
166
- sources: citations.filter((citation) => typeof citation === 'string')
167
- .map(url => ({ url })),
168
- ...typeof result.meta.answer === 'string' && result.meta.answer.length > 0
192
+ sources: citations
193
+ .filter((citation) => typeof citation === 'string')
194
+ .map((url) => ({ url })),
195
+ ...(typeof result.meta.answer === 'string' &&
196
+ result.meta.answer.length > 0
169
197
  ? { answer: result.meta.answer }
170
- : {},
198
+ : {}),
171
199
  truncated: false,
172
200
  };
173
201
  },
@@ -177,9 +205,9 @@ export function createXSearchTool(options) {
177
205
  const response = await (options.fetchFn ?? hostFetch)(X_SEARCH_URL, {
178
206
  method: 'POST',
179
207
  headers: {
180
- 'authorization': `Bearer ${session.accessToken}`,
208
+ authorization: `Bearer ${session.accessToken}`,
181
209
  'content-type': 'application/json',
182
- 'accept': 'application/json',
210
+ accept: 'application/json',
183
211
  },
184
212
  body: JSON.stringify({
185
213
  model: X_SEARCH_MODEL,
@@ -12,7 +12,7 @@ import { parseSse } from './sse.js';
12
12
  * that do not present as Claude Code, so this block is REQUIRED as the first
13
13
  * system entry on every request.
14
14
  */
15
- export const CLAUDE_CODE_IDENTITY = 'You are Claude Code, Anthropic\'s official CLI for Claude.';
15
+ export const CLAUDE_CODE_IDENTITY = "You are Claude Code, Anthropic's official CLI for Claude.";
16
16
  /** Tags wrapping a mid-conversation system message where it sits in the history. */
17
17
  export const SYSTEM_REMINDER_OPEN = '<system-reminder>';
18
18
  export const SYSTEM_REMINDER_CLOSE = '</system-reminder>';
@@ -47,15 +47,24 @@ export function cacheControl(ttl = DEFAULT_PROMPT_CACHE_TTL) {
47
47
  }
48
48
  /** Preserve native image blocks, retaining the existing text-only wire shape. */
49
49
  function toolResultContent(block) {
50
- if (!block.content.some(part => part.type === 'image' && 'dataBase64' in part)) {
51
- return block.content.map(part => (part.type === 'text' ? part.text : '')).join('');
50
+ if (!block.content.some((part) => part.type === 'image' && 'dataBase64' in part)) {
51
+ return block.content
52
+ .map((part) => (part.type === 'text' ? part.text : ''))
53
+ .join('');
52
54
  }
53
55
  const content = [];
54
56
  for (const part of block.content) {
55
57
  if (part.type === 'text' && part.text.length > 0)
56
58
  content.push({ type: 'text', text: part.text });
57
59
  if (part.type === 'image' && 'dataBase64' in part) {
58
- content.push({ type: 'image', source: { type: 'base64', media_type: part.mediaType, data: part.dataBase64 } });
60
+ content.push({
61
+ type: 'image',
62
+ source: {
63
+ type: 'base64',
64
+ media_type: part.mediaType,
65
+ data: part.dataBase64,
66
+ },
67
+ });
59
68
  }
60
69
  }
61
70
  return content;
@@ -64,7 +73,9 @@ function toolResultContent(block) {
64
73
  function parseToolInput(raw) {
65
74
  try {
66
75
  const parsed = JSON.parse(raw);
67
- if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
76
+ if (typeof parsed === 'object' &&
77
+ parsed !== null &&
78
+ !Array.isArray(parsed)) {
68
79
  return parsed;
69
80
  }
70
81
  return {};
@@ -88,14 +99,16 @@ function parseToolInput(raw) {
88
99
  * @param message - one assembled user message, reordered in place.
89
100
  */
90
101
  function leadWithToolResults(message) {
91
- const firstOther = message.content.findIndex(block => block.type !== 'tool_result');
102
+ const firstOther = message.content.findIndex((block) => block.type !== 'tool_result');
92
103
  if (firstOther === -1)
93
104
  return;
94
- if (!message.content.slice(firstOther).some(block => block.type === 'tool_result'))
105
+ if (!message.content
106
+ .slice(firstOther)
107
+ .some((block) => block.type === 'tool_result'))
95
108
  return;
96
109
  message.content = [
97
- ...message.content.filter(block => block.type === 'tool_result'),
98
- ...message.content.filter(block => block.type !== 'tool_result'),
110
+ ...message.content.filter((block) => block.type === 'tool_result'),
111
+ ...message.content.filter((block) => block.type !== 'tool_result'),
99
112
  ];
100
113
  }
101
114
  /**
@@ -111,7 +124,7 @@ function leadWithToolResults(message) {
111
124
  * @returns the boundary index separating the two.
112
125
  */
113
126
  function conversationStart(messages) {
114
- const index = messages.findIndex(message => message.role !== 'system');
127
+ const index = messages.findIndex((message) => message.role !== 'system');
115
128
  return index === -1 ? messages.length : index;
116
129
  }
117
130
  /**
@@ -140,15 +153,22 @@ export function toAnthropicMessages(messages) {
140
153
  const role = message.role === 'assistant' ? 'assistant' : 'user';
141
154
  const blocks = [];
142
155
  if (message.role === 'tool') {
143
- const id = message.toolCallId ?? message.tool_call_id
144
- ?? (message.source?.kind === 'tool' ? String(message.source.callId) : undefined);
156
+ const id = message.toolCallId ??
157
+ message.tool_call_id ??
158
+ (message.source?.kind === 'tool'
159
+ ? String(message.source.callId)
160
+ : undefined);
145
161
  if (id === undefined)
146
162
  throw new LlmError('tool result has no call id', 'INVALID_REQUEST');
147
163
  blocks.push({
148
164
  type: 'tool_result',
149
165
  tool_use_id: id,
150
- content: toolResultContent({ type: 'tool-result', toolCallId: ToolCallId(id), content: message.content }),
151
- ...message.isError === true ? { is_error: true } : {},
166
+ content: toolResultContent({
167
+ type: 'tool-result',
168
+ toolCallId: ToolCallId(id),
169
+ content: message.content,
170
+ }),
171
+ ...(message.isError === true ? { is_error: true } : {}),
152
172
  });
153
173
  const last = out[out.length - 1];
154
174
  if (last?.role === 'user')
@@ -181,21 +201,28 @@ export function toAnthropicMessages(messages) {
181
201
  name: block.name,
182
202
  input: parseToolInput(block.arguments),
183
203
  }
184
- : { type: 'text', text: `[tool call ${block.name}: ${block.arguments}]` });
204
+ : {
205
+ type: 'text',
206
+ text: `[tool call ${block.name}: ${block.arguments}]`,
207
+ });
185
208
  break;
186
209
  case 'tool-result':
187
210
  blocks.push({
188
211
  type: 'tool_result',
189
212
  tool_use_id: String(block.toolCallId),
190
213
  content: toolResultContent(block),
191
- ...block.isError === true ? { is_error: true } : {},
214
+ ...(block.isError === true ? { is_error: true } : {}),
192
215
  });
193
216
  break;
194
217
  case 'image':
195
218
  if ('dataBase64' in block) {
196
219
  blocks.push({
197
220
  type: 'image',
198
- source: { type: 'base64', media_type: block.mediaType, data: block.dataBase64 },
221
+ source: {
222
+ type: 'base64',
223
+ media_type: block.mediaType,
224
+ data: block.dataBase64,
225
+ },
199
226
  });
200
227
  }
201
228
  // An unresolved ImageBlock carries only an attachment reference; the
@@ -233,7 +260,7 @@ export function toAnthropicMessages(messages) {
233
260
  * @param ttl - cache lifetime for every mark; defaults to five minutes.
234
261
  */
235
262
  export function markMessageCache(messages, ttl) {
236
- const blocks = messages.flatMap(message => message.content);
263
+ const blocks = messages.flatMap((message) => message.content);
237
264
  for (let mark = 0; mark < MESSAGE_CACHE_BREAKPOINTS; mark++) {
238
265
  const at = blocks.length - 1 - mark * CACHE_BLOCK_STRIDE;
239
266
  if (at < 0)
@@ -251,7 +278,9 @@ export function markMessageCache(messages, ttl) {
251
278
  * @returns the system content blocks.
252
279
  */
253
280
  export function toAnthropicSystem(system, messages, ttl) {
254
- const blocks = [{ type: 'text', text: CLAUDE_CODE_IDENTITY }];
281
+ const blocks = [
282
+ { type: 'text', text: CLAUDE_CODE_IDENTITY },
283
+ ];
255
284
  if (system !== undefined && system.length > 0)
256
285
  blocks.push({ type: 'text', text: system });
257
286
  const history = messages ?? [];
@@ -284,8 +313,8 @@ export function toAnthropicSystem(system, messages, ttl) {
284
313
  */
285
314
  export function toAnthropicTools(tools) {
286
315
  return [...tools]
287
- .sort((left, right) => (left.name < right.name ? -1 : left.name > right.name ? 1 : 0))
288
- .map(tool => ({
316
+ .sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0)
317
+ .map((tool) => ({
289
318
  name: tool.name,
290
319
  description: tool.description,
291
320
  input_schema: tool.parameters,
@@ -352,7 +381,7 @@ export class AnthropicStreamTranslator {
352
381
  kind,
353
382
  text: '',
354
383
  callId,
355
- ...name === undefined ? {} : { name },
384
+ ...(name === undefined ? {} : { name }),
356
385
  };
357
386
  this.blocks.set(wireIndex, block);
358
387
  this.sawAnyBlock = true;
@@ -366,12 +395,12 @@ export class AnthropicStreamTranslator {
366
395
  const usage = {
367
396
  inputTokens: this.pendingUsage?.inputTokens ?? 0,
368
397
  outputTokens: this.outputTokens ?? 0,
369
- ...this.pendingUsage?.cacheReadTokens !== undefined
398
+ ...(this.pendingUsage?.cacheReadTokens !== undefined
370
399
  ? { cacheReadTokens: this.pendingUsage.cacheReadTokens }
371
- : {},
372
- ...this.pendingUsage?.cacheWriteTokens !== undefined
400
+ : {}),
401
+ ...(this.pendingUsage?.cacheWriteTokens !== undefined
373
402
  ? { cacheWriteTokens: this.pendingUsage.cacheWriteTokens }
374
- : {},
403
+ : {}),
375
404
  };
376
405
  chunks.push({ type: 'usage', usage });
377
406
  }
@@ -390,12 +419,12 @@ export class AnthropicStreamTranslator {
390
419
  if (usage !== undefined) {
391
420
  this.pendingUsage = {
392
421
  inputTokens: usage.input_tokens ?? 0,
393
- ...usage.cache_read_input_tokens !== undefined
422
+ ...(usage.cache_read_input_tokens !== undefined
394
423
  ? { cacheReadTokens: usage.cache_read_input_tokens }
395
- : {},
396
- ...usage.cache_creation_input_tokens !== undefined
424
+ : {}),
425
+ ...(usage.cache_creation_input_tokens !== undefined
397
426
  ? { cacheWriteTokens: usage.cache_creation_input_tokens }
398
- : {},
427
+ : {}),
399
428
  };
400
429
  this.outputTokens = usage.output_tokens ?? this.outputTokens;
401
430
  }
@@ -417,7 +446,7 @@ export class AnthropicStreamTranslator {
417
446
  type: 'tool-call-delta',
418
447
  index: opened.index,
419
448
  id: ToolCallId(opened.callId),
420
- ...block.name === undefined ? {} : { name: block.name },
449
+ ...(block.name === undefined ? {} : { name: block.name }),
421
450
  argumentsDelta: '',
422
451
  });
423
452
  break;
@@ -436,11 +465,19 @@ export class AnthropicStreamTranslator {
436
465
  switch (delta.type) {
437
466
  case 'text_delta':
438
467
  block.text += delta.text ?? '';
439
- chunks.push({ type: 'text-delta', index: block.index, text: delta.text ?? '' });
468
+ chunks.push({
469
+ type: 'text-delta',
470
+ index: block.index,
471
+ text: delta.text ?? '',
472
+ });
440
473
  break;
441
474
  case 'thinking_delta':
442
475
  block.text += delta.thinking ?? '';
443
- chunks.push({ type: 'reasoning-delta', index: block.index, text: delta.thinking ?? '' });
476
+ chunks.push({
477
+ type: 'reasoning-delta',
478
+ index: block.index,
479
+ text: delta.thinking ?? '',
480
+ });
444
481
  break;
445
482
  case 'input_json_delta':
446
483
  block.text += delta.partial_json ?? '';
@@ -448,7 +485,7 @@ export class AnthropicStreamTranslator {
448
485
  type: 'tool-call-delta',
449
486
  index: block.index,
450
487
  id: ToolCallId(block.callId),
451
- ...block.name === undefined ? {} : { name: block.name },
488
+ ...(block.name === undefined ? {} : { name: block.name }),
452
489
  argumentsDelta: delta.partial_json ?? '',
453
490
  });
454
491
  break;
@@ -464,7 +501,11 @@ export class AnthropicStreamTranslator {
464
501
  if (block === undefined)
465
502
  return chunks;
466
503
  this.blocks.delete(wireIndex);
467
- chunks.push({ type: 'block-end', index: block.index, block: closeBlock(block) });
504
+ chunks.push({
505
+ type: 'block-end',
506
+ index: block.index,
507
+ block: closeBlock(block),
508
+ });
468
509
  return chunks;
469
510
  }
470
511
  case 'message_delta': {
@@ -490,7 +531,11 @@ export class AnthropicStreamTranslator {
490
531
  this.terminated = true;
491
532
  for (const [wireIndex, block] of [...this.blocks]) {
492
533
  this.blocks.delete(wireIndex);
493
- chunks.push({ type: 'block-end', index: block.index, block: closeBlock(block) });
534
+ chunks.push({
535
+ type: 'block-end',
536
+ index: block.index,
537
+ block: closeBlock(block),
538
+ });
494
539
  }
495
540
  this.emitUsage(chunks);
496
541
  if (this.stopReason === 'stop' && !this.sawAnyBlock) {
@@ -498,7 +543,10 @@ export class AnthropicStreamTranslator {
498
543
  type: 'finish',
499
544
  reason: {
500
545
  kind: 'error',
501
- failure: { message: 'model returned a completed response with no content', code: EMPTY_RESPONSE_CODE },
546
+ failure: {
547
+ message: 'model returned a completed response with no content',
548
+ code: EMPTY_RESPONSE_CODE,
549
+ },
502
550
  },
503
551
  });
504
552
  }
@@ -1,13 +1,20 @@
1
1
  /** Model-specific tool schemas for Antigravity's Gemini and custom-tool bridges. */
2
2
  import { LlmError } from '@deepseek-ai/dsh-llm';
3
- const CUSTOM_KEYS = new Set(['type', 'description', 'properties', 'required', 'items', 'enum']);
3
+ const CUSTOM_KEYS = new Set([
4
+ 'type',
5
+ 'description',
6
+ 'properties',
7
+ 'required',
8
+ 'items',
9
+ 'enum',
10
+ ]);
4
11
  function object(value) {
5
12
  return typeof value === 'object' && value !== null && !Array.isArray(value);
6
13
  }
7
14
  /** Resolve local references before removing definitions; reject cycles and remote references. */
8
15
  function resolve(value, root, refs = new Set()) {
9
16
  if (Array.isArray(value))
10
- return value.map(item => resolve(item, root, refs));
17
+ return value.map((item) => resolve(item, root, refs));
11
18
  if (!object(value))
12
19
  return value;
13
20
  if (typeof value.$ref === 'string') {
@@ -18,7 +25,8 @@ function resolve(value, root, refs = new Set()) {
18
25
  let target = root;
19
26
  for (const segment of ref.slice(2).split('/')) {
20
27
  const key = segment.replaceAll('~1', '/').replaceAll('~0', '~');
21
- target = object(target) && Object.hasOwn(target, key) ? target[key] : undefined;
28
+ target =
29
+ object(target) && Object.hasOwn(target, key) ? target[key] : undefined;
22
30
  }
23
31
  if (!object(target))
24
32
  throw new LlmError(`Antigravity tool schema reference is missing: ${ref}`, 'INVALID_REQUEST');
@@ -27,9 +35,15 @@ function resolve(value, root, refs = new Set()) {
27
35
  }
28
36
  return Object.fromEntries(Object.entries(value)
29
37
  .filter(([key]) => !['$schema', '$id', '$defs', 'definitions'].includes(key))
30
- .map(([key, item]) => [key, key === 'properties' && object(item)
31
- ? Object.fromEntries(Object.entries(item).map(([name, property]) => [name, resolve(property, root, refs)]))
32
- : resolve(item, root, refs)]));
38
+ .map(([key, item]) => [
39
+ key,
40
+ key === 'properties' && object(item)
41
+ ? Object.fromEntries(Object.entries(item).map(([name, property]) => [
42
+ name,
43
+ resolve(property, root, refs),
44
+ ]))
45
+ : resolve(item, root, refs),
46
+ ]));
33
47
  }
34
48
  /** Numeric types widen to `number`: every integer is also a number. */
35
49
  const NUMERIC = new Set(['integer', 'number']);
@@ -37,7 +51,10 @@ const NUMERIC = new Set(['integer', 'number']);
37
51
  function stringChoices(schema) {
38
52
  if (typeof schema.const === 'string')
39
53
  return [schema.const];
40
- return Array.isArray(schema.enum) && schema.enum.every(entry => typeof entry === 'string') ? schema.enum : undefined;
54
+ return Array.isArray(schema.enum) &&
55
+ schema.enum.every((entry) => typeof entry === 'string')
56
+ ? schema.enum
57
+ : undefined;
41
58
  }
42
59
  /**
43
60
  * Fold schema alternatives into one shape this bridge can express, never
@@ -51,9 +68,12 @@ function widen(variants) {
51
68
  const members = variants.filter(object);
52
69
  if (members.length === 0 || members.length !== variants.length)
53
70
  return {};
54
- const types = new Set(members.map(member => member.type));
55
- const type = types.size === 1 ? [...types][0]
56
- : [...types].every(entry => typeof entry === 'string' && NUMERIC.has(entry)) ? 'number' : undefined;
71
+ const types = new Set(members.map((member) => member.type));
72
+ const type = types.size === 1
73
+ ? [...types][0]
74
+ : [...types].every((entry) => typeof entry === 'string' && NUMERIC.has(entry))
75
+ ? 'number'
76
+ : undefined;
57
77
  if (typeof type !== 'string')
58
78
  return {};
59
79
  if (type === 'object') {
@@ -62,25 +82,29 @@ function widen(variants) {
62
82
  if (!object(member.properties))
63
83
  continue;
64
84
  for (const [name, property] of Object.entries(member.properties)) {
65
- properties[name] = !Object.hasOwn(properties, name) || JSON.stringify(properties[name]) === JSON.stringify(property)
66
- ? property
67
- : widen([properties[name], property]);
85
+ properties[name] =
86
+ !Object.hasOwn(properties, name) ||
87
+ JSON.stringify(properties[name]) === JSON.stringify(property)
88
+ ? property
89
+ : widen([properties[name], property]);
68
90
  }
69
91
  }
70
92
  const required = members
71
- .map(member => Array.isArray(member.required) ? member.required.filter(entry => typeof entry === 'string') : [])
72
- .reduce((shared, next) => shared.filter(name => next.includes(name)));
93
+ .map((member) => Array.isArray(member.required)
94
+ ? member.required.filter((entry) => typeof entry === 'string')
95
+ : [])
96
+ .reduce((shared, next) => shared.filter((name) => next.includes(name)));
73
97
  return {
74
98
  type,
75
- ...Object.keys(properties).length === 0 ? {} : { properties },
76
- ...required.length === 0 ? {} : { required },
99
+ ...(Object.keys(properties).length === 0 ? {} : { properties }),
100
+ ...(required.length === 0 ? {} : { required }),
77
101
  };
78
102
  }
79
103
  if (type === 'array') {
80
- return { type, items: widen(members.map(member => member.items ?? {})) };
104
+ return { type, items: widen(members.map((member) => member.items ?? {})) };
81
105
  }
82
106
  const choices = members.map(stringChoices);
83
- return choices.every(entry => entry !== undefined)
107
+ return choices.every((entry) => entry !== undefined)
84
108
  ? { type, enum: [...new Set(choices.flat())] }
85
109
  : { type };
86
110
  }
@@ -89,9 +113,17 @@ function absorb(schema, extra) {
89
113
  const { properties, required, ...rest } = extra;
90
114
  Object.assign(schema, rest);
91
115
  if (object(properties))
92
- schema.properties = { ...object(schema.properties) ? schema.properties : {}, ...properties };
116
+ schema.properties = {
117
+ ...(object(schema.properties) ? schema.properties : {}),
118
+ ...properties,
119
+ };
93
120
  if (Array.isArray(required)) {
94
- schema.required = [...new Set([...Array.isArray(schema.required) ? schema.required : [], ...required])];
121
+ schema.required = [
122
+ ...new Set([
123
+ ...(Array.isArray(schema.required) ? schema.required : []),
124
+ ...required,
125
+ ]),
126
+ ];
95
127
  }
96
128
  }
97
129
  /** Keep property names intact while reducing schema keywords for Claude/GPT-OSS. */
@@ -109,27 +141,38 @@ function custom(value) {
109
141
  for (const keyword of ['anyOf', 'oneOf']) {
110
142
  if (!Array.isArray(schema[keyword]))
111
143
  continue;
112
- const variants = schema[keyword].filter(item => !object(item) || item.type !== 'null');
144
+ const variants = schema[keyword].filter((item) => !object(item) || item.type !== 'null');
113
145
  delete schema[keyword];
114
- absorb(schema, variants.length === 1 && object(variants[0]) ? variants[0] : widen(variants));
146
+ absorb(schema, variants.length === 1 && object(variants[0])
147
+ ? variants[0]
148
+ : widen(variants));
115
149
  }
116
150
  const out = {};
117
151
  for (const [key, value] of Object.entries(schema)) {
118
152
  if (!CUSTOM_KEYS.has(key))
119
153
  continue;
120
154
  if (key === 'properties' && object(value)) {
121
- out[key] = Object.fromEntries(Object.entries(value).map(([name, property]) => [name, custom(property)]));
155
+ out[key] = Object.fromEntries(Object.entries(value).map(([name, property]) => [
156
+ name,
157
+ custom(property),
158
+ ]));
122
159
  }
123
160
  else if (key === 'items')
124
161
  out[key] = custom(value);
125
162
  else if (key === 'type' && Array.isArray(value)) {
126
- const types = value.filter(type => type !== 'null');
127
- const type = types.length === 1 ? types[0]
128
- : types.length > 0 && types.every(entry => NUMERIC.has(entry)) ? 'number' : undefined;
163
+ const types = value.filter((type) => type !== 'null');
164
+ const type = types.length === 1
165
+ ? types[0]
166
+ : types.length > 0 &&
167
+ types.every((entry) => NUMERIC.has(entry))
168
+ ? 'number'
169
+ : undefined;
129
170
  if (type !== undefined)
130
171
  out[key] = type;
131
172
  }
132
- else if (key !== 'enum' || Array.isArray(value) && value.every(entry => typeof entry === 'string')) {
173
+ else if (key !== 'enum' ||
174
+ (Array.isArray(value) &&
175
+ value.every((entry) => typeof entry === 'string'))) {
133
176
  out[key] = value;
134
177
  }
135
178
  }
@@ -2,22 +2,34 @@
2
2
  import { LlmError, ReasoningEffortId } from '@deepseek-ai/dsh-llm';
3
3
  /** Efforts supported by the known Antigravity runtime families. Unknown models keep upstream defaults. */
4
4
  export function antigravityReasoning(model) {
5
- const ids = model.startsWith('claude-') ? ['high']
6
- : model.startsWith('gpt-oss-') ? ['medium']
7
- : model.startsWith('gemini-3.1-pro') || model === 'gemini-pro-agent' ? ['low', 'high']
8
- : /^gemini-(?:2\.5|3)/.test(model) ? ['low', 'medium', 'high'] : [];
5
+ const ids = model.startsWith('claude-')
6
+ ? ['high']
7
+ : model.startsWith('gpt-oss-')
8
+ ? ['medium']
9
+ : model.startsWith('gemini-3.1-pro') || model === 'gemini-pro-agent'
10
+ ? ['low', 'high']
11
+ : /^gemini-(?:2\.5|3)/.test(model)
12
+ ? ['low', 'medium', 'high']
13
+ : [];
9
14
  const fixedEffort = /^gemini-.*-(low|medium|high)$/.exec(model)?.[1];
10
15
  const efforts = fixedEffort !== undefined && ids.includes(fixedEffort) ? [fixedEffort] : ids;
11
- return efforts.length === 0 ? undefined : {
12
- efforts: efforts.map(id => ({ id: ReasoningEffortId(id), name: id[0].toUpperCase() + id.slice(1) })),
13
- };
16
+ return efforts.length === 0
17
+ ? undefined
18
+ : {
19
+ efforts: efforts.map((id) => ({
20
+ id: ReasoningEffortId(id),
21
+ name: id[0].toUpperCase() + id.slice(1),
22
+ })),
23
+ };
14
24
  }
15
25
  /** Build the v1internal thinking config; do not forward arbitrary DSH effort strings. */
16
26
  export function antigravityThinking(model, effort, maxTokens) {
17
27
  if (effort === undefined)
18
28
  return undefined;
19
29
  const supported = antigravityReasoning(model);
20
- if (supported === undefined || effort !== 'off' && !supported.efforts.some(entry => entry.id === effort)) {
30
+ if (supported === undefined ||
31
+ (effort !== 'off' &&
32
+ !supported.efforts.some((entry) => entry.id === effort))) {
21
33
  throw new LlmError(`Antigravity model ${model} does not support reasoning effort ${effort}`, 'UNSUPPORTED_REASONING_EFFORT');
22
34
  }
23
35
  if (effort === 'off')
@@ -31,7 +43,8 @@ export function antigravityThinking(model, effort, maxTokens) {
31
43
  else if (model.startsWith('gemini-3.1-pro') || model === 'gemini-pro-agent') {
32
44
  budget = effort === 'high' ? 10001 : 1001;
33
45
  }
34
- else if (model.startsWith('gemini-3.5-flash') || model === 'gemini-3-flash-agent') {
46
+ else if (model.startsWith('gemini-3.5-flash') ||
47
+ model === 'gemini-3-flash-agent') {
35
48
  budget = effort === 'high' ? 10000 : effort === 'medium' ? 4000 : 1000;
36
49
  }
37
50
  else {