dsh-subscriptions 0.1.0

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 (150) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +129 -0
  3. package/cordis.patch.yml +12 -0
  4. package/lib/auth/claude-code-creds.d.ts +23 -0
  5. package/lib/auth/claude-code-creds.js +172 -0
  6. package/lib/auth/device-flow.d.ts +55 -0
  7. package/lib/auth/device-flow.js +177 -0
  8. package/lib/auth/jwt.d.ts +10 -0
  9. package/lib/auth/jwt.js +25 -0
  10. package/lib/auth/oauth-flow.d.ts +91 -0
  11. package/lib/auth/oauth-flow.js +227 -0
  12. package/lib/auth/pkce.d.ts +31 -0
  13. package/lib/auth/pkce.js +35 -0
  14. package/lib/auth/rpc.d.ts +209 -0
  15. package/lib/auth/rpc.js +386 -0
  16. package/lib/auth/store.d.ts +196 -0
  17. package/lib/auth/store.js +468 -0
  18. package/lib/client/CursorCard.d.ts +9 -0
  19. package/lib/client/CursorCard.js +113 -0
  20. package/lib/client/ExternalUsageCards.d.ts +9 -0
  21. package/lib/client/ExternalUsageCards.js +72 -0
  22. package/lib/client/ImageGallery.d.ts +54 -0
  23. package/lib/client/ImageGallery.js +112 -0
  24. package/lib/client/ImageGenerateToolview.d.ts +48 -0
  25. package/lib/client/ImageGenerateToolview.js +133 -0
  26. package/lib/client/ProviderAccountManager.d.ts +13 -0
  27. package/lib/client/ProviderAccountManager.js +147 -0
  28. package/lib/client/ProviderModelEditor.d.ts +39 -0
  29. package/lib/client/ProviderModelEditor.js +107 -0
  30. package/lib/client/SpeedSelect.d.ts +71 -0
  31. package/lib/client/SpeedSelect.js +177 -0
  32. package/lib/client/SubscriptionUsageBadge.d.ts +116 -0
  33. package/lib/client/SubscriptionUsageBadge.js +543 -0
  34. package/lib/client/SubscriptionsSection.d.ts +140 -0
  35. package/lib/client/SubscriptionsSection.js +544 -0
  36. package/lib/client/UsageBadgeDisplaySetting.d.ts +7 -0
  37. package/lib/client/UsageBadgeDisplaySetting.js +36 -0
  38. package/lib/client/VideoGenerateToolview.d.ts +42 -0
  39. package/lib/client/VideoGenerateToolview.js +175 -0
  40. package/lib/client/account-preferences.d.ts +24 -0
  41. package/lib/client/account-preferences.js +21 -0
  42. package/lib/client/fast-command.d.ts +2 -0
  43. package/lib/client/fast-command.js +4 -0
  44. package/lib/client/host-contract.d.ts +90 -0
  45. package/lib/client/host-contract.js +101 -0
  46. package/lib/client/host-icons.d.ts +24 -0
  47. package/lib/client/host-icons.js +21 -0
  48. package/lib/client/index.d.ts +27 -0
  49. package/lib/client/index.js +136 -0
  50. package/lib/client/locales.d.ts +297 -0
  51. package/lib/client/locales.js +295 -0
  52. package/lib/client/subscription-card-styles.d.ts +3 -0
  53. package/lib/client/subscription-card-styles.js +53 -0
  54. package/lib/client/subscriptions-rpc.d.ts +14 -0
  55. package/lib/client/subscriptions-rpc.js +34 -0
  56. package/lib/client/usage-badge-preferences.d.ts +11 -0
  57. package/lib/client/usage-badge-preferences.js +41 -0
  58. package/lib/client.js +4472 -0
  59. package/lib/client.js.map +1 -0
  60. package/lib/compat.d.ts +36 -0
  61. package/lib/compat.js +20 -0
  62. package/lib/http.d.ts +6 -0
  63. package/lib/http.js +15 -0
  64. package/lib/index.d.ts +80 -0
  65. package/lib/index.js +14018 -0
  66. package/lib/model-defaults.d.ts +23 -0
  67. package/lib/model-defaults.js +237 -0
  68. package/lib/provider-settings.d.ts +39 -0
  69. package/lib/provider-settings.js +151 -0
  70. package/lib/providers/account-preferences.d.ts +41 -0
  71. package/lib/providers/account-preferences.js +153 -0
  72. package/lib/providers/accounts.d.ts +112 -0
  73. package/lib/providers/accounts.js +142 -0
  74. package/lib/providers/antigravity-oauth-client.d.ts +7 -0
  75. package/lib/providers/antigravity-oauth-client.js +7 -0
  76. package/lib/providers/antigravity.d.ts +110 -0
  77. package/lib/providers/antigravity.js +516 -0
  78. package/lib/providers/catalog-store.d.ts +48 -0
  79. package/lib/providers/catalog-store.js +243 -0
  80. package/lib/providers/claude-cli-version.d.ts +15 -0
  81. package/lib/providers/claude-cli-version.js +24 -0
  82. package/lib/providers/claude.d.ts +181 -0
  83. package/lib/providers/claude.js +680 -0
  84. package/lib/providers/codex-client-version.d.ts +8 -0
  85. package/lib/providers/codex-client-version.js +17 -0
  86. package/lib/providers/codex-search.d.ts +29 -0
  87. package/lib/providers/codex-search.js +198 -0
  88. package/lib/providers/codex.d.ts +229 -0
  89. package/lib/providers/codex.js +821 -0
  90. package/lib/providers/common.d.ts +389 -0
  91. package/lib/providers/common.js +656 -0
  92. package/lib/providers/copilot.d.ts +344 -0
  93. package/lib/providers/copilot.js +888 -0
  94. package/lib/providers/cursor-adapter.d.ts +15 -0
  95. package/lib/providers/cursor-adapter.js +39 -0
  96. package/lib/providers/cursor-auth.d.ts +43 -0
  97. package/lib/providers/cursor-auth.js +169 -0
  98. package/lib/providers/cursor-model-settings.d.ts +15 -0
  99. package/lib/providers/cursor-model-settings.js +51 -0
  100. package/lib/providers/cursor-usage.d.ts +9 -0
  101. package/lib/providers/cursor-usage.js +112 -0
  102. package/lib/providers/external-usage-controller.d.ts +19 -0
  103. package/lib/providers/external-usage-controller.js +32 -0
  104. package/lib/providers/external-usage.d.ts +8 -0
  105. package/lib/providers/external-usage.js +82 -0
  106. package/lib/providers/grok.d.ts +205 -0
  107. package/lib/providers/grok.js +713 -0
  108. package/lib/providers/image-pool.d.ts +30 -0
  109. package/lib/providers/image-pool.js +89 -0
  110. package/lib/providers/npm-cli-version.d.ts +56 -0
  111. package/lib/providers/npm-cli-version.js +98 -0
  112. package/lib/providers/pool-family.d.ts +56 -0
  113. package/lib/providers/pool-family.js +45 -0
  114. package/lib/providers/pool-health.d.ts +74 -0
  115. package/lib/providers/pool-health.js +150 -0
  116. package/lib/providers/pool-usage.d.ts +86 -0
  117. package/lib/providers/pool-usage.js +223 -0
  118. package/lib/providers/pool.d.ts +108 -0
  119. package/lib/providers/pool.js +368 -0
  120. package/lib/providers/rate-limit.d.ts +192 -0
  121. package/lib/providers/rate-limit.js +338 -0
  122. package/lib/tools/image-generate.d.ts +128 -0
  123. package/lib/tools/image-generate.js +438 -0
  124. package/lib/tools/registration.d.ts +15 -0
  125. package/lib/tools/registration.js +24 -0
  126. package/lib/tools/video-generate.d.ts +89 -0
  127. package/lib/tools/video-generate.js +257 -0
  128. package/lib/tools/x-search.d.ts +58 -0
  129. package/lib/tools/x-search.js +197 -0
  130. package/lib/translate/anthropic.d.ts +161 -0
  131. package/lib/translate/anthropic.js +515 -0
  132. package/lib/translate/antigravity-schema.d.ts +4 -0
  133. package/lib/translate/antigravity-schema.js +145 -0
  134. package/lib/translate/antigravity-thinking.d.ts +5 -0
  135. package/lib/translate/antigravity-thinking.js +44 -0
  136. package/lib/translate/antigravity.d.ts +114 -0
  137. package/lib/translate/antigravity.js +366 -0
  138. package/lib/translate/chat-completions.d.ts +120 -0
  139. package/lib/translate/chat-completions.js +382 -0
  140. package/lib/translate/resolved.d.ts +80 -0
  141. package/lib/translate/resolved.js +118 -0
  142. package/lib/translate/responses.d.ts +179 -0
  143. package/lib/translate/responses.js +408 -0
  144. package/lib/translate/sse.d.ts +21 -0
  145. package/lib/translate/sse.js +56 -0
  146. package/package.json +114 -0
  147. package/vendor/cursor/LICENSE +21 -0
  148. package/vendor/cursor/index.d.ts +18 -0
  149. package/vendor/cursor/index.js +3574 -0
  150. package/vendor/cursor/proto.js +254 -0
@@ -0,0 +1,44 @@
1
+ /** Cloud Code Assist thinking budgets, matched to the runtime model family. */
2
+ import { LlmError, ReasoningEffortId } from '@deepseek-ai/dsh-llm';
3
+ /** Efforts supported by the known Antigravity runtime families. Unknown models keep upstream defaults. */
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'] : [];
9
+ const fixedEffort = /^gemini-.*-(low|medium|high)$/.exec(model)?.[1];
10
+ 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
+ };
14
+ }
15
+ /** Build the v1internal thinking config; do not forward arbitrary DSH effort strings. */
16
+ export function antigravityThinking(model, effort, maxTokens) {
17
+ if (effort === undefined)
18
+ return undefined;
19
+ const supported = antigravityReasoning(model);
20
+ if (supported === undefined || effort !== 'off' && !supported.efforts.some(entry => entry.id === effort)) {
21
+ throw new LlmError(`Antigravity model ${model} does not support reasoning effort ${effort}`, 'UNSUPPORTED_REASONING_EFFORT');
22
+ }
23
+ if (effort === 'off')
24
+ return { includeThoughts: false, thinkingBudget: 0 };
25
+ // Reference: pi-antigravity src/models/models.ts at 697858c (v1internal wire).
26
+ let budget;
27
+ if (model.startsWith('claude-'))
28
+ budget = 1024;
29
+ else if (model.startsWith('gpt-oss-'))
30
+ budget = 8192;
31
+ else if (model.startsWith('gemini-3.1-pro') || model === 'gemini-pro-agent') {
32
+ budget = effort === 'high' ? 10001 : 1001;
33
+ }
34
+ else if (model.startsWith('gemini-3.5-flash') || model === 'gemini-3-flash-agent') {
35
+ budget = effort === 'high' ? 10000 : effort === 'medium' ? 4000 : 1000;
36
+ }
37
+ else {
38
+ budget = effort === 'high' ? -1 : effort === 'medium' ? 4000 : 1000;
39
+ }
40
+ if (maxTokens !== undefined && budget > 0 && maxTokens <= budget) {
41
+ throw new LlmError(`Antigravity maxTokens must exceed the thinking budget (${budget}) for ${model}/${effort}`, 'INVALID_REQUEST');
42
+ }
43
+ return { includeThoughts: true, thinkingBudget: budget };
44
+ }
@@ -0,0 +1,114 @@
1
+ /**
2
+ * DeepSeek Harness message/tool vocabulary to Antigravity's Gemini-shaped
3
+ * v1internal request envelope, plus response/SSE translation back to the
4
+ * harness streaming contract.
5
+ */
6
+ import type { GenerateOptions, StreamChunk, TokenUsage, ToolSchema } from '@deepseek-ai/dsh-llm';
7
+ import type { TranslatableMessage } from './resolved.js';
8
+ /** Minimal Gemini part shape used by v1internal. */
9
+ export interface AntigravityPart {
10
+ text?: string;
11
+ thought?: boolean;
12
+ thoughtSignature?: string;
13
+ inlineData?: {
14
+ mimeType: string;
15
+ data: string;
16
+ };
17
+ functionCall?: {
18
+ id?: string;
19
+ name?: string;
20
+ args?: unknown;
21
+ };
22
+ functionResponse?: {
23
+ id: string;
24
+ name: string;
25
+ response: Record<string, unknown>;
26
+ };
27
+ }
28
+ /** Full Antigravity request envelope. */
29
+ export interface AntigravityRequest {
30
+ project: string;
31
+ requestId: string;
32
+ model: string;
33
+ userAgent: 'antigravity';
34
+ requestType: 'agent';
35
+ request: {
36
+ contents: {
37
+ role: 'user' | 'model';
38
+ parts: AntigravityPart[];
39
+ }[];
40
+ sessionId: string;
41
+ systemInstruction?: {
42
+ parts: {
43
+ text: string;
44
+ }[];
45
+ };
46
+ tools?: {
47
+ functionDeclarations: Record<string, unknown>[];
48
+ }[];
49
+ toolConfig?: {
50
+ functionCallingConfig: {
51
+ mode: 'VALIDATED';
52
+ };
53
+ };
54
+ generationConfig?: Record<string, unknown>;
55
+ };
56
+ }
57
+ /** Map harness tool schemas to Gemini function declarations. */
58
+ export declare function toAntigravityTools(tools: readonly ToolSchema[], model?: string): {
59
+ functionDeclarations: Record<string, unknown>[];
60
+ }[];
61
+ /**
62
+ * Convert resolved harness messages into Gemini contents. Function response
63
+ * names are recovered from prior tool calls because DSH correlates results by
64
+ * id while the Gemini wire requires both id and name.
65
+ */
66
+ export declare function toAntigravityContents(messages: readonly TranslatableMessage[], model?: string): {
67
+ role: 'user' | 'model';
68
+ parts: AntigravityPart[];
69
+ }[];
70
+ /**
71
+ * Build one v1internal generateContent/streamGenerateContent request.
72
+ * When forStreaming is true and the model is Claude, caps maxOutputTokens at 64000
73
+ * to avoid INVALID_ARGUMENT errors.
74
+ */
75
+ export declare function toAntigravityRequest(options: GenerateOptions, messages: readonly TranslatableMessage[], projectId: string, forStreaming?: boolean): AntigravityRequest;
76
+ /** Antigravity SSE/non-stream response subset. */
77
+ export interface AntigravityResponseEvent {
78
+ response?: {
79
+ candidates?: {
80
+ content?: {
81
+ parts?: AntigravityPart[];
82
+ };
83
+ finishReason?: string;
84
+ }[];
85
+ usageMetadata?: {
86
+ promptTokenCount?: number;
87
+ candidatesTokenCount?: number;
88
+ thoughtsTokenCount?: number;
89
+ totalTokenCount?: number;
90
+ cachedContentTokenCount?: number;
91
+ };
92
+ };
93
+ }
94
+ /** Map Gemini usage metadata to the harness's disjoint counters. */
95
+ export declare function mapAntigravityUsage(metadata: NonNullable<NonNullable<AntigravityResponseEvent['response']>['usageMetadata']>): TokenUsage;
96
+ /** Push translator for both parsed SSE events and one non-stream response. */
97
+ export declare class AntigravityStreamTranslator {
98
+ private blocks;
99
+ private closed;
100
+ private nextIndex;
101
+ private sawContent;
102
+ private sawToolCall;
103
+ terminated: boolean;
104
+ private open;
105
+ private close;
106
+ private closeAll;
107
+ private finish;
108
+ /** Process one decoded Antigravity response frame. */
109
+ push(event: AntigravityResponseEvent): StreamChunk[];
110
+ }
111
+ /** Consume Antigravity's SSE response into the DSH streaming contract. */
112
+ export declare function streamAntigravity(stream: ReadableStream<Uint8Array>, onActivity?: () => void): AsyncGenerator<StreamChunk>;
113
+ /** Translate a non-stream generateContent response using the same state machine. */
114
+ export declare function parseAntigravityResponse(event: AntigravityResponseEvent): StreamChunk[];
@@ -0,0 +1,366 @@
1
+ /**
2
+ * DeepSeek Harness message/tool vocabulary to Antigravity's Gemini-shaped
3
+ * v1internal request envelope, plus response/SSE translation back to the
4
+ * harness streaming contract.
5
+ */
6
+ import { randomUUID } from 'node:crypto';
7
+ import { ToolCallId } from '../compat.js';
8
+ import { EMPTY_RESPONSE_CODE, LlmError } from '@deepseek-ai/dsh-llm';
9
+ import { withToolResultImages } from './resolved.js';
10
+ import { parseSse } from './sse.js';
11
+ import { antigravityThinking } from './antigravity-thinking.js';
12
+ import { antigravityToolParameters } from './antigravity-schema.js';
13
+ /**
14
+ * Normalize a harness tool result to the JSON object Antigravity's
15
+ * `FunctionResponse.response` field requires. That field maps to a singular
16
+ * protobuf `Struct`, so a bare array, string, number or boolean is rejected
17
+ * by the endpoint with "Proto field is not repeating, cannot start list".
18
+ * Only a non-array JSON object survives verbatim; every other JSON value is
19
+ * wrapped in the same "output" envelope the non-JSON path already uses --
20
+ * the shape other Cloud Code Assist clients send for every tool result.
21
+ */
22
+ function toolResultValue(block) {
23
+ const text = block.content.map(part => part.type === 'text' ? part.text : '').join('');
24
+ let parsed;
25
+ try {
26
+ parsed = JSON.parse(text);
27
+ }
28
+ catch {
29
+ return { output: text, ...block.isError === true ? { isError: true } : {} };
30
+ }
31
+ if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed))
32
+ return parsed;
33
+ return { output: parsed };
34
+ }
35
+ /** Safely read per-block replay metadata emitted by this adapter. */
36
+ function replayBlocks(message, model) {
37
+ const source = message.source;
38
+ if (model !== undefined && (message.source?.kind !== 'model'
39
+ || message.source.provider !== 'antigravity' || message.source.model !== model))
40
+ return [];
41
+ if (source?.kind !== 'model' || typeof source.replayState !== 'object' || source.replayState === null)
42
+ return [];
43
+ const envelope = source.replayState;
44
+ const response = envelope.response;
45
+ if (response === null || response?.kind !== 'antigravity' || response.version !== 1 || !Array.isArray(envelope.blocks))
46
+ return [];
47
+ return envelope.blocks.map((entry) => {
48
+ if (typeof entry !== 'object' || entry === null)
49
+ return {};
50
+ const signature = entry.thoughtSignature;
51
+ return typeof signature === 'string' && signature.length > 0 ? { thoughtSignature: signature } : {};
52
+ });
53
+ }
54
+ /** Map harness tool schemas to Gemini function declarations. */
55
+ export function toAntigravityTools(tools, model = 'gemini') {
56
+ if (tools.length === 0)
57
+ return [];
58
+ const legacy = /^(claude-|gpt-oss-)/.test(model);
59
+ return [{
60
+ functionDeclarations: tools.map(tool => ({
61
+ name: tool.name,
62
+ description: tool.description,
63
+ [legacy ? 'parameters' : 'parametersJsonSchema']: antigravityToolParameters(tool.parameters, legacy),
64
+ })),
65
+ }];
66
+ }
67
+ const SKIP_THOUGHT_SIGNATURE_VALIDATOR = 'skip_thought_signature_validator';
68
+ /** Gemini 3 validates the first function call in every model tool-call step. */
69
+ function requiresThoughtSignatures(model) {
70
+ return model !== undefined && /^gemini-3(?:[.-]|$)/i.test(model);
71
+ }
72
+ /**
73
+ * Convert resolved harness messages into Gemini contents. Function response
74
+ * names are recovered from prior tool calls because DSH correlates results by
75
+ * id while the Gemini wire requires both id and name.
76
+ */
77
+ export function toAntigravityContents(messages, model) {
78
+ const out = [];
79
+ const callNames = new Map();
80
+ for (const message of withToolResultImages(messages)) {
81
+ if (message.role === 'system')
82
+ continue;
83
+ if (message.role === 'tool') {
84
+ const id = message.toolCallId ?? message.tool_call_id
85
+ ?? (message.source?.kind === 'tool' ? String(message.source.callId) : undefined);
86
+ if (id === undefined)
87
+ throw new LlmError('tool result has no call id', 'INVALID_REQUEST');
88
+ const part = { functionResponse: {
89
+ id,
90
+ name: callNames.get(id) ?? '',
91
+ response: toolResultValue({ type: 'tool-result', toolCallId: ToolCallId(id), content: message.content }),
92
+ } };
93
+ const previous = out.at(-1);
94
+ if (previous?.role === 'user')
95
+ previous.parts.push(part);
96
+ else
97
+ out.push({ role: 'user', parts: [part] });
98
+ continue;
99
+ }
100
+ const role = message.role === 'assistant' ? 'model' : 'user';
101
+ const metadata = replayBlocks(message, model);
102
+ const parts = [];
103
+ let sawFunctionCall = false;
104
+ for (let index = 0; index < message.content.length; index++) {
105
+ const block = message.content[index];
106
+ switch (block.type) {
107
+ case 'text':
108
+ // Antigravity's Claude-backed models reject empty text parts.
109
+ parts.push({
110
+ text: block.text.trim().length > 0 ? block.text : '.',
111
+ ...metadata[index]?.thoughtSignature === undefined ? {} : { thoughtSignature: metadata[index].thoughtSignature },
112
+ });
113
+ break;
114
+ case 'reasoning': {
115
+ const thoughtSignature = metadata[index]?.thoughtSignature;
116
+ if (thoughtSignature !== undefined)
117
+ parts.push({ thought: true, text: block.text, thoughtSignature });
118
+ break;
119
+ }
120
+ case 'image':
121
+ if ('dataBase64' in block) {
122
+ parts.push({ inlineData: { mimeType: block.mediaType, data: block.dataBase64 } });
123
+ }
124
+ break;
125
+ case 'tool-call': {
126
+ callNames.set(String(block.id), block.name);
127
+ let args;
128
+ try {
129
+ args = JSON.parse(block.arguments);
130
+ }
131
+ catch {
132
+ args = {};
133
+ }
134
+ const replaySignature = metadata[index]?.thoughtSignature;
135
+ const thoughtSignature = replaySignature ?? (!sawFunctionCall && requiresThoughtSignatures(model)
136
+ ? SKIP_THOUGHT_SIGNATURE_VALIDATOR
137
+ : undefined);
138
+ parts.push({
139
+ functionCall: { id: String(block.id), name: block.name, args },
140
+ ...thoughtSignature === undefined ? {} : { thoughtSignature },
141
+ });
142
+ sawFunctionCall = true;
143
+ break;
144
+ }
145
+ case 'tool-result': {
146
+ const id = String(block.toolCallId);
147
+ parts.push({
148
+ functionResponse: {
149
+ id,
150
+ name: callNames.get(id) ?? '',
151
+ response: toolResultValue(block),
152
+ },
153
+ });
154
+ break;
155
+ }
156
+ default:
157
+ // Reasoning is not replayed without its provider signature. The
158
+ // signature-bearing metadata remains attached to tool-call blocks.
159
+ break;
160
+ }
161
+ }
162
+ if (parts.length === 0)
163
+ continue;
164
+ const previous = out.at(-1);
165
+ if (previous?.role === role)
166
+ previous.parts.push(...parts);
167
+ else
168
+ out.push({ role, parts });
169
+ }
170
+ return out;
171
+ }
172
+ /**
173
+ * Build one v1internal generateContent/streamGenerateContent request.
174
+ * When forStreaming is true and the model is Claude, caps maxOutputTokens at 64000
175
+ * to avoid INVALID_ARGUMENT errors.
176
+ */
177
+ export function toAntigravityRequest(options, messages, projectId, forStreaming = false) {
178
+ const tools = toAntigravityTools(options.tools ?? [], options.model);
179
+ const thinkingConfig = antigravityThinking(options.model, options.reasoningEffort, options.maxTokens);
180
+ let maxOutputTokens = options.maxTokens;
181
+ // Claude models on the streaming path reject maxOutputTokens >= 65535
182
+ if (forStreaming && maxOutputTokens !== undefined && /^claude-/.test(options.model) && maxOutputTokens >= 65535) {
183
+ maxOutputTokens = 64000;
184
+ }
185
+ const generationConfig = {
186
+ ...maxOutputTokens === undefined ? {} : { maxOutputTokens },
187
+ ...options.temperature === undefined ? {} : { temperature: options.temperature },
188
+ ...options.stop === undefined || options.stop.length === 0 ? {} : { stopSequences: options.stop },
189
+ ...thinkingConfig === undefined ? {} : { thinkingConfig },
190
+ };
191
+ const systemTexts = messages.flatMap(message => message.role === 'system'
192
+ ? message.content.filter((block) => block.type === 'text').map(block => block.text)
193
+ : []);
194
+ const system = options.system ?? (systemTexts.length > 0 ? systemTexts.join('\n\n') : undefined);
195
+ const sessionId = options.sessionId === undefined ? randomUUID() : String(options.sessionId);
196
+ return {
197
+ project: projectId,
198
+ requestId: `agent/${String(Date.now())}/${randomUUID()}/4`,
199
+ model: options.model,
200
+ userAgent: 'antigravity',
201
+ requestType: 'agent',
202
+ request: {
203
+ contents: toAntigravityContents(messages, options.model),
204
+ sessionId,
205
+ ...system === undefined || system.length === 0 ? {} : { systemInstruction: { parts: [{ text: system }] } },
206
+ ...tools.length === 0 ? {} : {
207
+ tools,
208
+ toolConfig: { functionCallingConfig: { mode: 'VALIDATED' } },
209
+ },
210
+ ...Object.keys(generationConfig).length === 0 ? {} : { generationConfig },
211
+ },
212
+ };
213
+ }
214
+ /** Map Gemini usage metadata to the harness's disjoint counters. */
215
+ export function mapAntigravityUsage(metadata) {
216
+ const cached = metadata.cachedContentTokenCount ?? 0;
217
+ return {
218
+ inputTokens: Math.max(0, (metadata.promptTokenCount ?? 0) - cached),
219
+ outputTokens: metadata.candidatesTokenCount ?? 0,
220
+ ...cached > 0 ? { cacheReadTokens: cached } : {},
221
+ ...metadata.thoughtsTokenCount === undefined ? {} : { reasoningTokens: metadata.thoughtsTokenCount },
222
+ };
223
+ }
224
+ /** Push translator for both parsed SSE events and one non-stream response. */
225
+ export class AntigravityStreamTranslator {
226
+ blocks = new Map();
227
+ closed = [];
228
+ nextIndex = 0;
229
+ sawContent = false;
230
+ sawToolCall = false;
231
+ terminated = false;
232
+ open(key, kind, chunks, values = {}) {
233
+ const block = { index: this.nextIndex++, kind, text: '', ...values };
234
+ this.blocks.set(key, block);
235
+ chunks.push({ type: 'block-start', index: block.index, blockType: kind });
236
+ return block;
237
+ }
238
+ close(key, chunks) {
239
+ const block = this.blocks.get(key);
240
+ if (block === undefined)
241
+ return;
242
+ this.blocks.delete(key);
243
+ let content;
244
+ if (block.kind === 'text')
245
+ content = { type: 'text', text: block.text };
246
+ else if (block.kind === 'reasoning')
247
+ content = { type: 'reasoning', text: block.text };
248
+ else
249
+ content = {
250
+ type: 'tool-call',
251
+ id: ToolCallId(block.id ?? `call_${randomUUID().replaceAll('-', '')}`),
252
+ name: block.name ?? '',
253
+ arguments: block.text,
254
+ };
255
+ this.closed[block.index] = block.thoughtSignature === undefined ? {} : { thoughtSignature: block.thoughtSignature };
256
+ chunks.push({ type: 'block-end', index: block.index, block: content });
257
+ }
258
+ closeAll(chunks) {
259
+ for (const key of [...this.blocks.keys()])
260
+ this.close(key, chunks);
261
+ }
262
+ finish(reason) {
263
+ const replayState = {
264
+ response: { kind: 'antigravity', version: 1 },
265
+ blocks: this.closed,
266
+ };
267
+ if (!this.sawContent) {
268
+ return {
269
+ type: 'finish',
270
+ reason: { kind: 'error', failure: { message: 'Antigravity returned no content', code: EMPTY_RESPONSE_CODE } },
271
+ };
272
+ }
273
+ if (reason === 'MAX_TOKENS')
274
+ return { type: 'finish', reason: { kind: 'max-tokens' }, replayState };
275
+ if (reason === 'SAFETY' || reason === 'RECITATION' || reason === 'BLOCKLIST') {
276
+ return {
277
+ type: 'finish',
278
+ reason: { kind: 'error', failure: { message: `Antigravity blocked the response (${reason})`, code: 'CONTENT_FILTER' } },
279
+ };
280
+ }
281
+ return { type: 'finish', reason: { kind: this.sawToolCall ? 'tool-calls' : 'stop' }, replayState };
282
+ }
283
+ /** Process one decoded Antigravity response frame. */
284
+ push(event) {
285
+ if (this.terminated)
286
+ return [];
287
+ const chunks = [];
288
+ const candidate = event.response?.candidates?.[0];
289
+ for (const [partIndex, part] of (candidate?.content?.parts ?? []).entries()) {
290
+ if (part.thought === true && typeof part.text === 'string' && part.text.length > 0) {
291
+ const block = this.blocks.get('reasoning') ?? this.open('reasoning', 'reasoning', chunks);
292
+ block.text += part.text;
293
+ if (part.thoughtSignature !== undefined)
294
+ block.thoughtSignature = part.thoughtSignature;
295
+ this.sawContent = true;
296
+ chunks.push({ type: 'reasoning-delta', index: block.index, text: part.text });
297
+ }
298
+ else if (typeof part.text === 'string' && part.text.length > 0) {
299
+ const block = this.blocks.get('text') ?? this.open('text', 'text', chunks);
300
+ block.text += part.text;
301
+ if (part.thoughtSignature !== undefined)
302
+ block.thoughtSignature = part.thoughtSignature;
303
+ this.sawContent = true;
304
+ chunks.push({ type: 'text-delta', index: block.index, text: part.text });
305
+ }
306
+ else if (part.functionCall !== undefined) {
307
+ const call = part.functionCall;
308
+ const id = typeof call.id === 'string' && call.id.length > 0
309
+ ? call.id
310
+ : `call_${randomUUID().replaceAll('-', '')}`;
311
+ const key = `call:${id}:${String(partIndex)}`;
312
+ const args = JSON.stringify(call.args ?? {});
313
+ const block = this.open(key, 'tool-call', chunks, {
314
+ id,
315
+ name: call.name ?? '',
316
+ ...part.thoughtSignature === undefined ? {} : { thoughtSignature: part.thoughtSignature },
317
+ });
318
+ block.text = args;
319
+ this.sawContent = true;
320
+ this.sawToolCall = true;
321
+ chunks.push({
322
+ type: 'tool-call-delta',
323
+ index: block.index,
324
+ id: ToolCallId(id),
325
+ name: call.name ?? '',
326
+ argumentsDelta: args,
327
+ });
328
+ this.close(key, chunks);
329
+ }
330
+ }
331
+ if (candidate?.finishReason !== undefined) {
332
+ this.closeAll(chunks);
333
+ const usage = event.response?.usageMetadata;
334
+ if (usage !== undefined)
335
+ chunks.push({ type: 'usage', usage: mapAntigravityUsage(usage) });
336
+ chunks.push(this.finish(candidate.finishReason));
337
+ this.terminated = true;
338
+ }
339
+ return chunks;
340
+ }
341
+ }
342
+ /** Consume Antigravity's SSE response into the DSH streaming contract. */
343
+ export async function* streamAntigravity(stream, onActivity) {
344
+ const translator = new AntigravityStreamTranslator();
345
+ for await (const event of parseSse(stream, onActivity)) {
346
+ if (event.data === '[DONE]')
347
+ break;
348
+ let parsed;
349
+ try {
350
+ parsed = JSON.parse(event.data);
351
+ }
352
+ catch {
353
+ throw new LlmError(`malformed Antigravity SSE payload: ${event.data.slice(0, 120)}`, 'MALFORMED_RESPONSE');
354
+ }
355
+ yield* translator.push(parsed);
356
+ if (translator.terminated)
357
+ return;
358
+ }
359
+ if (!translator.terminated) {
360
+ throw new LlmError('Antigravity SSE stream ended before a finish chunk', 'STREAM_CLOSED');
361
+ }
362
+ }
363
+ /** Translate a non-stream generateContent response using the same state machine. */
364
+ export function parseAntigravityResponse(event) {
365
+ return new AntigravityStreamTranslator().push(event);
366
+ }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Translate between the harness message vocabulary and the OpenAI chat
3
+ * completions wire format the Copilot provider speaks: request message/tool
4
+ * assembly and a push-model SSE-chunk → StreamChunk state machine
5
+ * ({@link ChatCompletionsStreamTranslator}) mirroring the Responses
6
+ * translator, so tests need no streams.
7
+ */
8
+ import type { StreamChunk, TokenUsage, ToolSchema } from '@deepseek-ai/dsh-llm';
9
+ import type { TranslatableMessage } from './resolved.js';
10
+ /**
11
+ * Convert harness messages into chat completions `messages`. System-role
12
+ * messages become one leading `system` message; an explicit `system` argument
13
+ * wins over them when both exist. Reasoning blocks are not replayed (matching
14
+ * the Responses translator). Images must arrive pre-resolved; an unresolved
15
+ * ImageBlock is skipped because its bytes are unreachable here. A user message
16
+ * carrying only text collapses to a plain string body (some endpoints still
17
+ * reject content-part arrays); tool results become separate `tool` messages.
18
+ * @param messages - ordered conversation messages with resolved images.
19
+ * @param system - explicit system prompt, which takes precedence.
20
+ * @returns the wire `messages` array.
21
+ */
22
+ export declare function toChatMessages(messages: readonly TranslatableMessage[], system?: string): Record<string, unknown>[];
23
+ /**
24
+ * Map harness tool schemas to chat completions function tools.
25
+ * @param tools - tool schemas from the request.
26
+ * @returns the wire `tools` array.
27
+ */
28
+ export declare function toChatTools(tools: readonly ToolSchema[]): Record<string, unknown>[];
29
+ /** The subset of chat-completion chunk shapes this translator reads. */
30
+ export interface ChatCompletionsStreamEvent {
31
+ choices?: {
32
+ index?: number;
33
+ delta?: {
34
+ content?: string | null;
35
+ role?: string;
36
+ reasoning_content?: string | null;
37
+ /** Copilot's Gemini models stream thinking as `reasoning_text`. */
38
+ reasoning_text?: string | null;
39
+ tool_calls?: {
40
+ index?: number;
41
+ id?: string;
42
+ function?: {
43
+ name?: string;
44
+ arguments?: string;
45
+ };
46
+ }[];
47
+ };
48
+ finish_reason?: string | null;
49
+ }[];
50
+ usage?: ChatCompletionsUsage | null;
51
+ }
52
+ /** Chat completions `usage` object shape. */
53
+ export interface ChatCompletionsUsage {
54
+ prompt_tokens: number;
55
+ completion_tokens: number;
56
+ prompt_tokens_details?: {
57
+ cached_tokens?: number;
58
+ };
59
+ completion_tokens_details?: {
60
+ reasoning_tokens?: number;
61
+ };
62
+ }
63
+ /**
64
+ * Map chat completions usage to disjoint harness counts (cached input is
65
+ * subtracted out of `inputTokens` and reported as `cacheReadTokens`).
66
+ * @param usage - wire usage from the terminal chunk.
67
+ * @returns harness token usage.
68
+ */
69
+ export declare function mapChatCompletionsUsage(usage: ChatCompletionsUsage): TokenUsage;
70
+ /**
71
+ * Push-model chat completions SSE translator: feed each parsed chunk object
72
+ * to {@link push} and collect the emitted harness StreamChunks. The terminal
73
+ * `finish_reason` chunk closes every block but only ARMS the finish chunk —
74
+ * usage must precede the terminal finish, and where usage lives differs by
75
+ * upstream: OpenAI-style streams send a trailing usage-only chunk
76
+ * (stream_options.include_usage), while Copilot's Gemini models attach a
77
+ * (zero) usage object to EVERY chunk and fold the real usage into the
78
+ * finish chunk itself. A chunk therefore never early-returns on `usage`
79
+ * alone: its deltas are always processed, and the terminal pair is drained
80
+ * when the finish is armed and usage arrived (or when a usage-only chunk
81
+ * follows an armed finish). `flush()` emits whatever remains when the
82
+ * stream's `[DONE]` (or EOF) arrives.
83
+ */
84
+ export declare class ChatCompletionsStreamTranslator {
85
+ /** Text/reasoning blocks keyed by kind; tool calls keyed by their wire index. */
86
+ private blocks;
87
+ private order;
88
+ private nextIndex;
89
+ private sawToolCall;
90
+ private pendingUsage;
91
+ private armedFinish;
92
+ /** Set once the terminal finish chunk was emitted. */
93
+ terminated: boolean;
94
+ private open;
95
+ private close;
96
+ private closeAll;
97
+ /** Build the terminal finish chunk for one wire finish reason. */
98
+ private finishChunk;
99
+ /** Usage, then the armed finish: the only order the harness accepts. */
100
+ private drainTerminal;
101
+ /**
102
+ * Process one parsed chat-completion chunk.
103
+ * @param event - the parsed chunk object.
104
+ * @returns the StreamChunks this event produced (possibly none).
105
+ */
106
+ push(event: ChatCompletionsStreamEvent): StreamChunk[];
107
+ /**
108
+ * Emit whatever the stream left pending (`[DONE]` or EOF without a final
109
+ * usage chunk). Safe to call repeatedly.
110
+ * @returns the remaining terminal chunks.
111
+ */
112
+ flush(): StreamChunk[];
113
+ }
114
+ /**
115
+ * Consume a chat completions SSE byte stream and yield harness StreamChunks.
116
+ * @param stream - raw response body.
117
+ * @param onActivity - transport-activity callback for the idle watchdog.
118
+ * @returns the chunk stream; throws when the stream ends before any finish chunk.
119
+ */
120
+ export declare function streamChatCompletions(stream: ReadableStream<Uint8Array>, onActivity?: () => void): AsyncGenerator<StreamChunk>;