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.
- package/README.md +212 -180
- package/lib/auth/claude-code-creds.js +31 -9
- package/lib/auth/device-flow.js +16 -9
- package/lib/auth/oauth-flow.js +13 -10
- package/lib/auth/rpc.js +74 -23
- package/lib/auth/store.js +80 -31
- package/lib/client/CursorCard.d.ts +1 -1
- package/lib/client/CursorCard.js +50 -16
- package/lib/client/ExternalUsageCards.d.ts +1 -1
- package/lib/client/ExternalUsageCards.js +37 -16
- package/lib/client/ImageGallery.d.ts +2 -2
- package/lib/client/ImageGallery.js +92 -33
- package/lib/client/ImageGenerateToolview.js +50 -14
- package/lib/client/OllamaCard.d.ts +1 -1
- package/lib/client/OllamaCard.js +26 -8
- package/lib/client/OllamaSettingsManager.js +113 -30
- package/lib/client/ProviderAccountManager.d.ts +1 -1
- package/lib/client/ProviderAccountManager.js +164 -49
- package/lib/client/ProviderModelEditor.js +107 -26
- package/lib/client/SpeedSelect.d.ts +62 -14
- package/lib/client/SpeedSelect.js +257 -46
- package/lib/client/SubscriptionUsageBadge.d.ts +4 -10
- package/lib/client/SubscriptionUsageBadge.js +300 -112
- package/lib/client/SubscriptionsSection.js +258 -85
- package/lib/client/UsageBadgeDisplaySetting.js +35 -12
- package/lib/client/UsageMeter.d.ts +1 -1
- package/lib/client/UsageMeter.js +62 -24
- package/lib/client/VideoGenerateToolview.js +69 -17
- package/lib/client/account-preferences.d.ts +8 -0
- package/lib/client/account-preferences.js +20 -5
- package/lib/client/host-contract.d.ts +11 -2
- package/lib/client/host-contract.js +20 -3
- package/lib/client/index.d.ts +5 -5
- package/lib/client/index.js +34 -12
- package/lib/client/locales.d.ts +10 -0
- package/lib/client/locales.js +61 -51
- package/lib/client/ollama-draft.js +21 -12
- package/lib/client/reset-action.js +6 -4
- package/lib/client/reset-credits-view.d.ts +1 -1
- package/lib/client/reset-credits-view.js +66 -17
- package/lib/client/subscription-card-styles.js +94 -24
- package/lib/client/usage-color-preferences.js +4 -2
- package/lib/client/usage-pace.js +30 -12
- package/lib/client.js +287 -47
- package/lib/client.js.map +1 -1
- package/lib/http.js +1 -1
- package/lib/index.d.ts +3 -3
- package/lib/index.js +118 -64
- package/lib/model-defaults.js +4 -2
- package/lib/provider-settings.d.ts +12 -0
- package/lib/provider-settings.js +58 -16
- package/lib/providers/account-preferences.js +51 -22
- package/lib/providers/accounts.js +15 -9
- package/lib/providers/antigravity-oauth-client.js +2 -2
- package/lib/providers/antigravity.js +141 -68
- package/lib/providers/catalog-store.js +120 -41
- package/lib/providers/claude-cli-version.js +3 -3
- package/lib/providers/claude.d.ts +9 -2
- package/lib/providers/claude.js +153 -90
- package/lib/providers/codex-client-version.js +3 -3
- package/lib/providers/codex-search.js +40 -13
- package/lib/providers/codex.js +196 -112
- package/lib/providers/common.js +71 -39
- package/lib/providers/copilot.js +143 -82
- package/lib/providers/cursor-adapter.js +42 -18
- package/lib/providers/cursor-auth.d.ts +1 -1
- package/lib/providers/cursor-auth.js +69 -24
- package/lib/providers/cursor-model-settings.js +14 -5
- package/lib/providers/cursor-usage.js +27 -13
- package/lib/providers/external-usage-controller.js +14 -5
- package/lib/providers/external-usage.js +59 -23
- package/lib/providers/grok.js +152 -89
- package/lib/providers/image-pool.js +29 -11
- package/lib/providers/minimax-usage.js +31 -11
- package/lib/providers/npm-cli-version.js +10 -3
- package/lib/providers/ollama-settings.js +24 -8
- package/lib/providers/ollama-web.js +29 -11
- package/lib/providers/ollama.js +129 -66
- package/lib/providers/pool-family.js +11 -3
- package/lib/providers/pool-health.js +28 -8
- package/lib/providers/pool-usage.js +16 -9
- package/lib/providers/pool.d.ts +8 -0
- package/lib/providers/pool.js +81 -42
- package/lib/providers/rate-limit.js +8 -2
- package/lib/providers/replay.js +40 -17
- package/lib/providers/reset-redemption.js +18 -6
- package/lib/tools/image-generate.js +123 -65
- package/lib/tools/registration.js +5 -2
- package/lib/tools/video-generate.js +51 -31
- package/lib/tools/x-search.js +50 -22
- package/lib/translate/anthropic.js +85 -37
- package/lib/translate/antigravity-schema.js +71 -28
- package/lib/translate/antigravity-thinking.js +22 -9
- package/lib/translate/antigravity.js +141 -50
- package/lib/translate/chat-completions.js +67 -23
- package/lib/translate/resolved.js +55 -21
- package/lib/translate/responses.js +69 -29
- package/lib/translate/sse.js +4 -1
- package/package.json +26 -4
package/lib/tools/x-search.js
CHANGED
|
@@ -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
|
|
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) ||
|
|
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
|
-
|
|
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
|
-
|
|
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: {
|
|
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: {
|
|
127
|
-
|
|
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: {
|
|
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
|
-
|
|
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)
|
|
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
|
|
167
|
-
.
|
|
168
|
-
|
|
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
|
-
|
|
208
|
+
authorization: `Bearer ${session.accessToken}`,
|
|
181
209
|
'content-type': 'application/json',
|
|
182
|
-
|
|
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 =
|
|
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
|
|
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({
|
|
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' &&
|
|
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
|
|
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 ??
|
|
144
|
-
|
|
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({
|
|
151
|
-
|
|
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
|
-
: {
|
|
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: {
|
|
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 = [
|
|
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) =>
|
|
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({
|
|
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({
|
|
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({
|
|
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({
|
|
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: {
|
|
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([
|
|
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 =
|
|
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]) => [
|
|
31
|
-
|
|
32
|
-
|
|
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) &&
|
|
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
|
|
56
|
-
|
|
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] =
|
|
66
|
-
|
|
67
|
-
|
|
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)
|
|
72
|
-
.
|
|
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 = {
|
|
116
|
+
schema.properties = {
|
|
117
|
+
...(object(schema.properties) ? schema.properties : {}),
|
|
118
|
+
...properties,
|
|
119
|
+
};
|
|
93
120
|
if (Array.isArray(required)) {
|
|
94
|
-
schema.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])
|
|
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]) => [
|
|
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
|
|
128
|
-
|
|
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' ||
|
|
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-')
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
|
12
|
-
|
|
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 ||
|
|
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') ||
|
|
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 {
|