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.
- package/README.md +220 -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.d.ts +8 -4
- package/lib/auth/rpc.js +89 -25
- 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 +137 -34
- package/lib/client/SpeedSelect.d.ts +62 -14
- package/lib/client/SpeedSelect.js +261 -49
- package/lib/client/SubscriptionUsageBadge.d.ts +4 -10
- package/lib/client/SubscriptionUsageBadge.js +300 -112
- package/lib/client/SubscriptionsSection.d.ts +2 -0
- package/lib/client/SubscriptionsSection.js +265 -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 +22 -0
- package/lib/client/locales.js +73 -51
- package/lib/client/model-filter.d.ts +37 -0
- package/lib/client/model-filter.js +45 -0
- 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/usage-window-filter.d.ts +7 -0
- package/lib/client/usage-window-filter.js +12 -0
- package/lib/client.js +480 -136
- package/lib/client.js.map +1 -1
- package/lib/http.js +1 -1
- package/lib/index.d.ts +3 -3
- package/lib/index.js +167 -76
- package/lib/model-defaults.js +4 -2
- package/lib/provider-settings.d.ts +17 -0
- package/lib/provider-settings.js +79 -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 +151 -52
- 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 +27 -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
|
|
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 {
|
|
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 {
|
|
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 &&
|
|
43
|
-
|
|
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' ||
|
|
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 ||
|
|
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
|
|
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,18 +77,28 @@ 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
|
-
|
|
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
|
+
/**
|
|
92
|
+
* Whether this model validates thought signatures on replayed function calls.
|
|
93
|
+
*
|
|
94
|
+
* Gemini 3 and 4 require a signature on the first function call of each step;
|
|
95
|
+
* foreign history uses the documented placeholder when no signature can be reused.
|
|
96
|
+
* https://ai.google.dev/gemini-api/docs/generate-content/thought-signatures
|
|
97
|
+
* Keep this scoped to these model families; future versions and unversioned aliases
|
|
98
|
+
* need verification before enabling the same fallback.
|
|
99
|
+
*/
|
|
73
100
|
function requiresThoughtSignatures(model) {
|
|
74
|
-
return model !== undefined && /^gemini-
|
|
101
|
+
return model !== undefined && /^gemini-[34](?:[.-]|$)/i.test(model.trim());
|
|
75
102
|
}
|
|
76
103
|
/**
|
|
77
104
|
* Convert resolved harness messages into Gemini contents. Function response
|
|
@@ -85,18 +112,27 @@ export function toAntigravityContents(messages, model) {
|
|
|
85
112
|
if (message.role === 'system')
|
|
86
113
|
continue;
|
|
87
114
|
if (message.role === 'tool') {
|
|
88
|
-
const id = message.toolCallId ??
|
|
89
|
-
|
|
115
|
+
const id = message.toolCallId ??
|
|
116
|
+
message.tool_call_id ??
|
|
117
|
+
(message.source?.kind === 'tool'
|
|
118
|
+
? String(message.source.callId)
|
|
119
|
+
: undefined);
|
|
90
120
|
if (id === undefined)
|
|
91
121
|
throw new LlmError('tool result has no call id', 'INVALID_REQUEST');
|
|
92
|
-
const part = {
|
|
122
|
+
const part = {
|
|
123
|
+
functionResponse: {
|
|
93
124
|
id,
|
|
94
125
|
name: callNames.get(id) ?? '',
|
|
95
126
|
response: toolResultValue({
|
|
96
|
-
type: 'tool-result',
|
|
97
|
-
|
|
127
|
+
type: 'tool-result',
|
|
128
|
+
toolCallId: ToolCallId(id),
|
|
129
|
+
content: message.content,
|
|
130
|
+
...(message.isError === undefined
|
|
131
|
+
? {}
|
|
132
|
+
: { isError: message.isError }),
|
|
98
133
|
}),
|
|
99
|
-
}
|
|
134
|
+
},
|
|
135
|
+
};
|
|
100
136
|
const previous = out.at(-1);
|
|
101
137
|
if (previous?.role === 'user')
|
|
102
138
|
previous.parts.push(part);
|
|
@@ -115,7 +151,9 @@ export function toAntigravityContents(messages, model) {
|
|
|
115
151
|
// Antigravity's Claude-backed models reject empty text parts.
|
|
116
152
|
parts.push({
|
|
117
153
|
text: block.text.trim().length > 0 ? block.text : '.',
|
|
118
|
-
...metadata[index]?.thoughtSignature === undefined
|
|
154
|
+
...(metadata[index]?.thoughtSignature === undefined
|
|
155
|
+
? {}
|
|
156
|
+
: { thoughtSignature: metadata[index].thoughtSignature }),
|
|
119
157
|
});
|
|
120
158
|
break;
|
|
121
159
|
case 'reasoning': {
|
|
@@ -126,7 +164,9 @@ export function toAntigravityContents(messages, model) {
|
|
|
126
164
|
}
|
|
127
165
|
case 'image':
|
|
128
166
|
if ('dataBase64' in block) {
|
|
129
|
-
parts.push({
|
|
167
|
+
parts.push({
|
|
168
|
+
inlineData: { mimeType: block.mediaType, data: block.dataBase64 },
|
|
169
|
+
});
|
|
130
170
|
}
|
|
131
171
|
break;
|
|
132
172
|
case 'tool-call': {
|
|
@@ -139,12 +179,13 @@ export function toAntigravityContents(messages, model) {
|
|
|
139
179
|
args = {};
|
|
140
180
|
}
|
|
141
181
|
const replaySignature = metadata[index]?.thoughtSignature;
|
|
142
|
-
const thoughtSignature = replaySignature ??
|
|
143
|
-
|
|
144
|
-
|
|
182
|
+
const thoughtSignature = replaySignature ??
|
|
183
|
+
(!sawFunctionCall && requiresThoughtSignatures(model)
|
|
184
|
+
? SKIP_THOUGHT_SIGNATURE_VALIDATOR
|
|
185
|
+
: undefined);
|
|
145
186
|
parts.push({
|
|
146
187
|
functionCall: { id: String(block.id), name: block.name, args },
|
|
147
|
-
...thoughtSignature === undefined ? {} : { thoughtSignature },
|
|
188
|
+
...(thoughtSignature === undefined ? {} : { thoughtSignature }),
|
|
148
189
|
});
|
|
149
190
|
sawFunctionCall = true;
|
|
150
191
|
break;
|
|
@@ -186,19 +227,29 @@ export function toAntigravityRequest(options, messages, projectId, forStreaming
|
|
|
186
227
|
const thinkingConfig = antigravityThinking(options.model, options.reasoningEffort, options.maxTokens);
|
|
187
228
|
let maxOutputTokens = options.maxTokens;
|
|
188
229
|
// Claude models on the streaming path reject maxOutputTokens >= 65535
|
|
189
|
-
if (forStreaming &&
|
|
230
|
+
if (forStreaming &&
|
|
231
|
+
maxOutputTokens !== undefined &&
|
|
232
|
+
/^claude-/.test(options.model) &&
|
|
233
|
+
maxOutputTokens >= 65535) {
|
|
190
234
|
maxOutputTokens = 64000;
|
|
191
235
|
}
|
|
192
236
|
const generationConfig = {
|
|
193
|
-
...maxOutputTokens === undefined ? {} : { maxOutputTokens },
|
|
194
|
-
...options.temperature === undefined
|
|
195
|
-
|
|
196
|
-
|
|
237
|
+
...(maxOutputTokens === undefined ? {} : { maxOutputTokens }),
|
|
238
|
+
...(options.temperature === undefined
|
|
239
|
+
? {}
|
|
240
|
+
: { temperature: options.temperature }),
|
|
241
|
+
...(options.stop === undefined || options.stop.length === 0
|
|
242
|
+
? {}
|
|
243
|
+
: { stopSequences: options.stop }),
|
|
244
|
+
...(thinkingConfig === undefined ? {} : { thinkingConfig }),
|
|
197
245
|
};
|
|
198
|
-
const systemTexts = messages.flatMap(message => message.role === 'system'
|
|
199
|
-
? message.content
|
|
246
|
+
const systemTexts = messages.flatMap((message) => message.role === 'system'
|
|
247
|
+
? message.content
|
|
248
|
+
.filter((block) => block.type === 'text')
|
|
249
|
+
.map((block) => block.text)
|
|
200
250
|
: []);
|
|
201
|
-
const system = options.system ??
|
|
251
|
+
const system = options.system ??
|
|
252
|
+
(systemTexts.length > 0 ? systemTexts.join('\n\n') : undefined);
|
|
202
253
|
const sessionId = options.sessionId === undefined ? randomUUID() : String(options.sessionId);
|
|
203
254
|
return {
|
|
204
255
|
project: projectId,
|
|
@@ -209,12 +260,20 @@ export function toAntigravityRequest(options, messages, projectId, forStreaming
|
|
|
209
260
|
request: {
|
|
210
261
|
contents: toAntigravityContents(messages, options.model),
|
|
211
262
|
sessionId,
|
|
212
|
-
...system === undefined || system.length === 0
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
263
|
+
...(system === undefined || system.length === 0
|
|
264
|
+
? {}
|
|
265
|
+
: { systemInstruction: { parts: [{ text: system }] } }),
|
|
266
|
+
...(tools.length === 0
|
|
267
|
+
? {}
|
|
268
|
+
: {
|
|
269
|
+
tools,
|
|
270
|
+
toolConfig: {
|
|
271
|
+
functionCallingConfig: { mode: 'VALIDATED' },
|
|
272
|
+
},
|
|
273
|
+
}),
|
|
274
|
+
...(Object.keys(generationConfig).length === 0
|
|
275
|
+
? {}
|
|
276
|
+
: { generationConfig }),
|
|
218
277
|
},
|
|
219
278
|
};
|
|
220
279
|
}
|
|
@@ -224,8 +283,10 @@ export function mapAntigravityUsage(metadata) {
|
|
|
224
283
|
return {
|
|
225
284
|
inputTokens: Math.max(0, (metadata.promptTokenCount ?? 0) - cached),
|
|
226
285
|
outputTokens: metadata.candidatesTokenCount ?? 0,
|
|
227
|
-
...cached > 0 ? { cacheReadTokens: cached } : {},
|
|
228
|
-
...metadata.thoughtsTokenCount === undefined
|
|
286
|
+
...(cached > 0 ? { cacheReadTokens: cached } : {}),
|
|
287
|
+
...(metadata.thoughtsTokenCount === undefined
|
|
288
|
+
? {}
|
|
289
|
+
: { reasoningTokens: metadata.thoughtsTokenCount }),
|
|
229
290
|
};
|
|
230
291
|
}
|
|
231
292
|
/** Push translator for both parsed SSE events and one non-stream response. */
|
|
@@ -237,7 +298,12 @@ export class AntigravityStreamTranslator {
|
|
|
237
298
|
sawToolCall = false;
|
|
238
299
|
terminated = false;
|
|
239
300
|
open(key, kind, chunks, values = {}) {
|
|
240
|
-
const block = {
|
|
301
|
+
const block = {
|
|
302
|
+
index: this.nextIndex++,
|
|
303
|
+
kind,
|
|
304
|
+
text: '',
|
|
305
|
+
...values,
|
|
306
|
+
};
|
|
241
307
|
this.blocks.set(key, block);
|
|
242
308
|
chunks.push({ type: 'block-start', index: block.index, blockType: kind });
|
|
243
309
|
return block;
|
|
@@ -259,7 +325,10 @@ export class AntigravityStreamTranslator {
|
|
|
259
325
|
name: block.name ?? '',
|
|
260
326
|
arguments: block.text,
|
|
261
327
|
};
|
|
262
|
-
this.closed[block.index] =
|
|
328
|
+
this.closed[block.index] =
|
|
329
|
+
block.thoughtSignature === undefined
|
|
330
|
+
? {}
|
|
331
|
+
: { thoughtSignature: block.thoughtSignature };
|
|
263
332
|
chunks.push({ type: 'block-end', index: block.index, block: content });
|
|
264
333
|
}
|
|
265
334
|
closeAll(chunks) {
|
|
@@ -268,24 +337,45 @@ export class AntigravityStreamTranslator {
|
|
|
268
337
|
}
|
|
269
338
|
finish(reason) {
|
|
270
339
|
const replayState = {
|
|
271
|
-
response: {
|
|
340
|
+
response: {
|
|
341
|
+
kind: 'antigravity',
|
|
342
|
+
version: 1,
|
|
343
|
+
},
|
|
272
344
|
blocks: this.closed,
|
|
273
345
|
};
|
|
274
346
|
if (!this.sawContent) {
|
|
275
347
|
return {
|
|
276
348
|
type: 'finish',
|
|
277
|
-
reason: {
|
|
349
|
+
reason: {
|
|
350
|
+
kind: 'error',
|
|
351
|
+
failure: {
|
|
352
|
+
message: 'Antigravity returned no content',
|
|
353
|
+
code: EMPTY_RESPONSE_CODE,
|
|
354
|
+
},
|
|
355
|
+
},
|
|
278
356
|
};
|
|
279
357
|
}
|
|
280
358
|
if (reason === 'MAX_TOKENS')
|
|
281
359
|
return { type: 'finish', reason: { kind: 'max-tokens' }, replayState };
|
|
282
|
-
if (reason === 'SAFETY' ||
|
|
360
|
+
if (reason === 'SAFETY' ||
|
|
361
|
+
reason === 'RECITATION' ||
|
|
362
|
+
reason === 'BLOCKLIST') {
|
|
283
363
|
return {
|
|
284
364
|
type: 'finish',
|
|
285
|
-
reason: {
|
|
365
|
+
reason: {
|
|
366
|
+
kind: 'error',
|
|
367
|
+
failure: {
|
|
368
|
+
message: `Antigravity blocked the response (${reason})`,
|
|
369
|
+
code: 'CONTENT_FILTER',
|
|
370
|
+
},
|
|
371
|
+
},
|
|
286
372
|
};
|
|
287
373
|
}
|
|
288
|
-
return {
|
|
374
|
+
return {
|
|
375
|
+
type: 'finish',
|
|
376
|
+
reason: { kind: this.sawToolCall ? 'tool-calls' : 'stop' },
|
|
377
|
+
replayState,
|
|
378
|
+
};
|
|
289
379
|
}
|
|
290
380
|
/** Process one decoded Antigravity response frame. */
|
|
291
381
|
push(event) {
|
|
@@ -294,13 +384,20 @@ export class AntigravityStreamTranslator {
|
|
|
294
384
|
const chunks = [];
|
|
295
385
|
const candidate = event.response?.candidates?.[0];
|
|
296
386
|
for (const [partIndex, part] of (candidate?.content?.parts ?? []).entries()) {
|
|
297
|
-
if (part.thought === true &&
|
|
298
|
-
|
|
387
|
+
if (part.thought === true &&
|
|
388
|
+
typeof part.text === 'string' &&
|
|
389
|
+
part.text.length > 0) {
|
|
390
|
+
const block = this.blocks.get('reasoning') ??
|
|
391
|
+
this.open('reasoning', 'reasoning', chunks);
|
|
299
392
|
block.text += part.text;
|
|
300
393
|
if (part.thoughtSignature !== undefined)
|
|
301
394
|
block.thoughtSignature = part.thoughtSignature;
|
|
302
395
|
this.sawContent = true;
|
|
303
|
-
chunks.push({
|
|
396
|
+
chunks.push({
|
|
397
|
+
type: 'reasoning-delta',
|
|
398
|
+
index: block.index,
|
|
399
|
+
text: part.text,
|
|
400
|
+
});
|
|
304
401
|
}
|
|
305
402
|
else if (typeof part.text === 'string' && part.text.length > 0) {
|
|
306
403
|
const block = this.blocks.get('text') ?? this.open('text', 'text', chunks);
|
|
@@ -320,7 +417,9 @@ export class AntigravityStreamTranslator {
|
|
|
320
417
|
const block = this.open(key, 'tool-call', chunks, {
|
|
321
418
|
id,
|
|
322
419
|
name: call.name ?? '',
|
|
323
|
-
...part.thoughtSignature === undefined
|
|
420
|
+
...(part.thoughtSignature === undefined
|
|
421
|
+
? {}
|
|
422
|
+
: { thoughtSignature: part.thoughtSignature }),
|
|
324
423
|
});
|
|
325
424
|
block.text = args;
|
|
326
425
|
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
|
|
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 ??
|
|
41
|
-
|
|
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({
|
|
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
|
|
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: {
|
|
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({
|
|
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: {
|
|
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: {
|
|
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 {
|
|
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({
|
|
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({
|
|
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'
|
|
291
|
-
|
|
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') ??
|
|
332
|
+
const block = this.blocks.get('reasoning') ??
|
|
333
|
+
this.open('reasoning', 'reasoning', chunks);
|
|
295
334
|
block.text += reasoning;
|
|
296
|
-
chunks.push({
|
|
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 &&
|
|
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',
|