dsh-agy 0.2.5 → 0.2.7
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/lib/{accounts-Blqno5Te.mjs → accounts-D5LlcqCO.mjs} +21 -10
- package/lib/accounts-D5LlcqCO.mjs.map +1 -0
- package/lib/cli/index.mjs +4 -4
- package/lib/{exchange-C6y05v4v.mjs → exchange-D1sCdzuN.mjs} +2 -2
- package/lib/{exchange-C6y05v4v.mjs.map → exchange-D1sCdzuN.mjs.map} +1 -1
- package/lib/{import-Dmeiu2Xb.mjs → import-BPKii1UF.mjs} +3 -3
- package/lib/{import-Dmeiu2Xb.mjs.map → import-BPKii1UF.mjs.map} +1 -1
- package/lib/index.mjs +2 -2
- package/lib/{models-3VFg0nRK.mjs → models-ExG7kJs1.mjs} +74 -4
- package/lib/models-ExG7kJs1.mjs.map +1 -0
- package/lib/{parse-D8bV2yqa.mjs → parse-Bs4rPkHA.mjs} +4 -2
- package/lib/parse-Bs4rPkHA.mjs.map +1 -0
- package/lib/{plugin-common-CP6Czgh9.mjs → plugin-common-BkcyXO5Y.mjs} +34 -12
- package/lib/plugin-common-BkcyXO5Y.mjs.map +1 -0
- package/lib/{translate-Zf1mdBri.mjs → translate-nK61WoOC.mjs} +180 -10
- package/lib/translate-nK61WoOC.mjs.map +1 -0
- package/lib/web/plugin.mjs +6 -6
- package/package.json +1 -1
- package/lib/accounts-Blqno5Te.mjs.map +0 -1
- package/lib/models-3VFg0nRK.mjs.map +0 -1
- package/lib/parse-D8bV2yqa.mjs.map +0 -1
- package/lib/plugin-common-CP6Czgh9.mjs.map +0 -1
- package/lib/translate-Zf1mdBri.mjs.map +0 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
-
import { v as generateAntigravityRequestId } from "./accounts-
|
|
3
|
-
import { i as isLevelThinkingModel } from "./catalog-DVHPJjfc.mjs";
|
|
2
|
+
import { v as generateAntigravityRequestId } from "./accounts-D5LlcqCO.mjs";
|
|
3
|
+
import { i as isLevelThinkingModel, n as catalogModel } from "./catalog-DVHPJjfc.mjs";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
|
+
import { readFile } from "node:fs/promises";
|
|
5
6
|
const DEFAULT_TTL_MS = 36e5;
|
|
6
7
|
const MAX_ENTRIES = 2e3;
|
|
7
8
|
const store = /* @__PURE__ */ new Map();
|
|
@@ -39,6 +40,170 @@ function getThoughtSignature(toolCallId) {
|
|
|
39
40
|
return entry.signature;
|
|
40
41
|
}
|
|
41
42
|
//#endregion
|
|
43
|
+
//#region src/adapter/multimodal.ts
|
|
44
|
+
/**
|
|
45
|
+
* Multimodal file resolution for Google Gemini on Antigravity.
|
|
46
|
+
*
|
|
47
|
+
* In DeepSeek Harness (DSH), non-image files (PDF, audio, video, etc.)
|
|
48
|
+
* uploaded by users are converted by DSH's LLM runtime into deterministic
|
|
49
|
+
* text handles:
|
|
50
|
+
*
|
|
51
|
+
* `[File "${name}" (${bytes} bytes, sha256:${digest}): verbatim read-only copy saved at "${readonlyPath}". ...]`
|
|
52
|
+
*
|
|
53
|
+
* Gemini models natively support these files as base64-encoded `inlineData`
|
|
54
|
+
* parts alongside text. Claude models running behind the Antigravity Vertex
|
|
55
|
+
* proxy reject non-image `inlineData` with 500, so injection is scoped strictly
|
|
56
|
+
* to Gemini (non-Claude, vision-capable) models.
|
|
57
|
+
*/
|
|
58
|
+
/**
|
|
59
|
+
* MIME type mapping for Gemini supported multimodal formats.
|
|
60
|
+
*
|
|
61
|
+
* Covered formats (PDF observed live end-to-end; the rest follow Gemini's
|
|
62
|
+
* documented multimodal support and are not yet live-verified):
|
|
63
|
+
* - Document: .pdf -> 'application/pdf'
|
|
64
|
+
* - Audio: .mp3 -> 'audio/mp3', .wav -> 'audio/wav', .m4a -> 'audio/m4a', .aac -> 'audio/aac', .ogg -> 'audio/ogg', .flac -> 'audio/flac'
|
|
65
|
+
* - Video: .mp4 -> 'video/mp4', .mov -> 'video/quicktime', .webm -> 'video/webm'
|
|
66
|
+
* - Extended image: .bmp -> 'image/bmp', .heic -> 'image/heic', .heif -> 'image/heif'
|
|
67
|
+
*/
|
|
68
|
+
const GEMINI_MULTIMODAL_MIMES = Object.freeze({
|
|
69
|
+
pdf: "application/pdf",
|
|
70
|
+
mp3: "audio/mp3",
|
|
71
|
+
wav: "audio/wav",
|
|
72
|
+
m4a: "audio/m4a",
|
|
73
|
+
aac: "audio/aac",
|
|
74
|
+
ogg: "audio/ogg",
|
|
75
|
+
flac: "audio/flac",
|
|
76
|
+
mp4: "video/mp4",
|
|
77
|
+
mov: "video/quicktime",
|
|
78
|
+
webm: "video/webm",
|
|
79
|
+
bmp: "image/bmp",
|
|
80
|
+
heic: "image/heic",
|
|
81
|
+
heif: "image/heif",
|
|
82
|
+
".pdf": "application/pdf",
|
|
83
|
+
".mp3": "audio/mp3",
|
|
84
|
+
".wav": "audio/wav",
|
|
85
|
+
".m4a": "audio/m4a",
|
|
86
|
+
".aac": "audio/aac",
|
|
87
|
+
".ogg": "audio/ogg",
|
|
88
|
+
".flac": "audio/flac",
|
|
89
|
+
".mp4": "video/mp4",
|
|
90
|
+
".mov": "video/quicktime",
|
|
91
|
+
".webm": "video/webm",
|
|
92
|
+
".bmp": "image/bmp",
|
|
93
|
+
".heic": "image/heic",
|
|
94
|
+
".heif": "image/heif"
|
|
95
|
+
});
|
|
96
|
+
/**
|
|
97
|
+
* Regex matching DSH's deterministic read-only file handle blocks.
|
|
98
|
+
* Capture groups:
|
|
99
|
+
* 1: file name
|
|
100
|
+
* 2: file size in bytes
|
|
101
|
+
* 3: local read-only filesystem path
|
|
102
|
+
*/
|
|
103
|
+
const DSH_FILE_HANDLE_REGEX = /\[File "([^"]+)" \((\d+) bytes, sha256:[a-f0-9]+\): verbatim read-only copy saved at "([^"]+)"\./g;
|
|
104
|
+
/** Extract file handle descriptors from text content. */
|
|
105
|
+
function extractFileHandles(text) {
|
|
106
|
+
const regex = new RegExp(DSH_FILE_HANDLE_REGEX.source, "g");
|
|
107
|
+
const results = [];
|
|
108
|
+
for (const match of text.matchAll(regex)) {
|
|
109
|
+
const [, name, bytesStr, readonlyPath] = match;
|
|
110
|
+
if (name && bytesStr && readonlyPath) {
|
|
111
|
+
const bytes = parseInt(bytesStr, 10);
|
|
112
|
+
if (!Number.isNaN(bytes)) results.push({
|
|
113
|
+
name,
|
|
114
|
+
bytes,
|
|
115
|
+
readonlyPath
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return results;
|
|
120
|
+
}
|
|
121
|
+
/** Resolve MIME type for a file name or path based on extension. */
|
|
122
|
+
function getMultimodalMimeType(filenameOrPath) {
|
|
123
|
+
const dotIndex = filenameOrPath.lastIndexOf(".");
|
|
124
|
+
if (dotIndex === -1) return void 0;
|
|
125
|
+
const ext = filenameOrPath.slice(dotIndex + 1).toLowerCase();
|
|
126
|
+
return GEMINI_MULTIMODAL_MIMES[ext];
|
|
127
|
+
}
|
|
128
|
+
/** Whether a model id belongs to a Claude-branded model (Vertex-hosted). */
|
|
129
|
+
function isClaudeModel(model) {
|
|
130
|
+
return model.startsWith("claude-") || model.includes("/claude");
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Check whether a model supports Gemini multimodal file inlineData.
|
|
134
|
+
* Deny-by-default: Claude models are always excluded (Vertex 500 on
|
|
135
|
+
* non-image inlineData), catalog models must be vision-capable, and ids
|
|
136
|
+
* unknown to the catalog are only allowed when they carry the `gemini-`
|
|
137
|
+
* prefix (so future tiered ids keep working without a catalog bump).
|
|
138
|
+
*/
|
|
139
|
+
function supportsMultimodalFiles(model) {
|
|
140
|
+
if (isClaudeModel(model)) return false;
|
|
141
|
+
const meta = catalogModel(model);
|
|
142
|
+
if (meta) return meta.supportsVision === true;
|
|
143
|
+
return model.startsWith("gemini-");
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Resolve multimodal files referenced in user messages.
|
|
147
|
+
*
|
|
148
|
+
* Reads local files up to 20MB into base64 strings. Silently ignores
|
|
149
|
+
* missing/unreadable files or files exceeding the size limit so the original
|
|
150
|
+
* text handle block remains intact in the prompt.
|
|
151
|
+
*/
|
|
152
|
+
async function resolveMultimodalFiles(optionsOrMessages, modelOrOptions, extraOptions) {
|
|
153
|
+
let messages;
|
|
154
|
+
let model;
|
|
155
|
+
let customOptions;
|
|
156
|
+
if ("messages" in optionsOrMessages) {
|
|
157
|
+
messages = optionsOrMessages.messages ?? [];
|
|
158
|
+
model = optionsOrMessages.model ?? "";
|
|
159
|
+
customOptions = typeof modelOrOptions === "object" ? modelOrOptions : extraOptions;
|
|
160
|
+
} else {
|
|
161
|
+
messages = optionsOrMessages;
|
|
162
|
+
if (typeof modelOrOptions === "string") {
|
|
163
|
+
model = modelOrOptions;
|
|
164
|
+
customOptions = extraOptions;
|
|
165
|
+
} else {
|
|
166
|
+
model = "";
|
|
167
|
+
customOptions = modelOrOptions;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const result = /* @__PURE__ */ new Map();
|
|
171
|
+
if (!supportsMultimodalFiles(model)) return result;
|
|
172
|
+
const readFn = customOptions?.readFile ?? readFile;
|
|
173
|
+
for (let i = 0; i < messages.length; i++) {
|
|
174
|
+
const message = messages[i];
|
|
175
|
+
if (!message || message.role !== "user") continue;
|
|
176
|
+
const resolvedForMessage = [];
|
|
177
|
+
for (const block of message.content) {
|
|
178
|
+
if (block.type !== "text" || typeof block.text !== "string") continue;
|
|
179
|
+
const handles = extractFileHandles(block.text);
|
|
180
|
+
for (const handle of handles) {
|
|
181
|
+
if (handle.bytes > 20971520) continue;
|
|
182
|
+
const mimeType = getMultimodalMimeType(handle.name) ?? getMultimodalMimeType(handle.readonlyPath);
|
|
183
|
+
if (!mimeType) continue;
|
|
184
|
+
try {
|
|
185
|
+
const fileBuffer = await readFn(handle.readonlyPath);
|
|
186
|
+
if (fileBuffer.length > 20971520) continue;
|
|
187
|
+
resolvedForMessage.push({
|
|
188
|
+
mimeType,
|
|
189
|
+
data: Buffer.from(fileBuffer).toString("base64"),
|
|
190
|
+
name: handle.name,
|
|
191
|
+
path: handle.readonlyPath,
|
|
192
|
+
bytes: fileBuffer.length
|
|
193
|
+
});
|
|
194
|
+
} catch {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (resolvedForMessage.length > 0) {
|
|
200
|
+
const key = message.id ?? `msg-${i}`;
|
|
201
|
+
result.set(key, resolvedForMessage);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return result;
|
|
205
|
+
}
|
|
206
|
+
//#endregion
|
|
42
207
|
//#region src/adapter/translate.ts
|
|
43
208
|
/**
|
|
44
209
|
* Translate a DSH GenerateOptions into the Antigravity wrapped request.
|
|
@@ -59,12 +224,9 @@ var translate_exports = /* @__PURE__ */ __exportAll({
|
|
|
59
224
|
AGY_SCHEMA_ALLOWLIST: () => AGY_SCHEMA_ALLOWLIST,
|
|
60
225
|
isClaudeModel: () => isClaudeModel,
|
|
61
226
|
stripTrailingModelTurn: () => stripTrailingModelTurn,
|
|
227
|
+
supportsMultimodalFiles: () => supportsMultimodalFiles,
|
|
62
228
|
toAgyRequestBody: () => toAgyRequestBody
|
|
63
229
|
});
|
|
64
|
-
/** Whether a model id belongs to a Claude-branded model (Vertex-hosted). */
|
|
65
|
-
function isClaudeModel(model) {
|
|
66
|
-
return model.startsWith("claude-") || model.includes("/claude");
|
|
67
|
-
}
|
|
68
230
|
/**
|
|
69
231
|
* Vertex (the Antigravity Claude backend) rejects conversations ending on an
|
|
70
232
|
* assistant/model turn ("assistant message prefill"); never strip to empty.
|
|
@@ -187,8 +349,15 @@ function blockToParts(block, toolNames, images) {
|
|
|
187
349
|
default: return [];
|
|
188
350
|
}
|
|
189
351
|
}
|
|
190
|
-
function messageToContent(message, toolNames, images) {
|
|
352
|
+
function messageToContent(message, toolNames, images, multimodalFiles, messageIndex) {
|
|
191
353
|
const parts = message.content.flatMap((block) => block.type === "image" && message.role !== "user" ? [] : blockToParts(block, toolNames, images));
|
|
354
|
+
if (message.role === "user" && multimodalFiles) {
|
|
355
|
+
const files = (message.id ? multimodalFiles.get(message.id) : void 0) ?? multimodalFiles.get(message) ?? (messageIndex !== void 0 ? multimodalFiles.get(`msg-${messageIndex}`) : void 0) ?? (messageIndex !== void 0 ? multimodalFiles.get(String(messageIndex)) : void 0);
|
|
356
|
+
if (files) for (const file of files) parts.push({ inlineData: {
|
|
357
|
+
mimeType: file.mimeType,
|
|
358
|
+
data: file.data
|
|
359
|
+
} });
|
|
360
|
+
}
|
|
192
361
|
if (parts.length === 0) return null;
|
|
193
362
|
return {
|
|
194
363
|
role: message.role === "assistant" ? "model" : "user",
|
|
@@ -245,7 +414,8 @@ function toolsToDeclarations(tools) {
|
|
|
245
414
|
function toAgyRequestBody(options, context) {
|
|
246
415
|
const toolNames = buildToolNameIndex(options.messages);
|
|
247
416
|
const images = context.images ?? /* @__PURE__ */ new Map();
|
|
248
|
-
|
|
417
|
+
const multimodalFiles = supportsMultimodalFiles(options.model) ? context.multimodalFiles : void 0;
|
|
418
|
+
let contents = options.messages.map((message, index) => messageToContent(message, toolNames, images, multimodalFiles, index)).filter((c) => c !== null);
|
|
249
419
|
if (isClaudeModel(options.model)) contents = stripTrailingModelTurn(contents);
|
|
250
420
|
const tools = toolsToDeclarations(options.tools);
|
|
251
421
|
const generationConfig = {};
|
|
@@ -274,6 +444,6 @@ function toAgyRequestBody(options, context) {
|
|
|
274
444
|
};
|
|
275
445
|
}
|
|
276
446
|
//#endregion
|
|
277
|
-
export { translate_exports as n,
|
|
447
|
+
export { setThoughtSignature as i, translate_exports as n, resolveMultimodalFiles as r, toAgyRequestBody as t };
|
|
278
448
|
|
|
279
|
-
//# sourceMappingURL=translate-
|
|
449
|
+
//# sourceMappingURL=translate-nK61WoOC.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translate-nK61WoOC.mjs","names":[],"sources":["../src/runtime/signature-cache.ts","../src/adapter/multimodal.ts","../src/adapter/translate.ts"],"sourcesContent":["/**\n * Gemini thought-signature cache for Antigravity tool calls.\n *\n * The Antigravity backend requires every outbound `functionCall` part to carry\n * a `thoughtSignature` (400 \"Function call is missing a thought_signature\"\n * otherwise). Signatures arrive on the *response* side — as a sibling field of\n * functionCall parts, and on thought parts — and must be replayed on the next\n * turn for the same tool call id. When nothing is cached, the translator\n * injects the `skip_thought_signature_validator` sentinel (the established\n * bypass both reference implementations use by default).\n *\n * Process-local with TTL; a cold process falls back to the sentinel, which the\n * upstream accepts, so cross-process persistence is not required for\n * correctness.\n */\n\nexport const THOUGHT_SIGNATURE_SENTINEL = 'skip_thought_signature_validator'\n\ninterface CacheEntry {\n signature: string\n expiresAt: number\n}\n\nconst DEFAULT_TTL_MS = 60 * 60 * 1000\nconst MAX_ENTRIES = 2000\n\nconst store = new Map<string, CacheEntry>()\n\nfunction pruneExpired(now = Date.now()): void {\n if (store.size < MAX_ENTRIES / 2) return\n for (const [key, entry] of store) {\n if (entry.expiresAt <= now) store.delete(key)\n }\n}\n\n/** Store the signature observed for one tool call id (response side). */\nexport function setThoughtSignature(toolCallId: string, signature: string, ttlMs = DEFAULT_TTL_MS): void {\n if (!toolCallId || !signature) return\n pruneExpired()\n if (store.size >= MAX_ENTRIES) {\n // Evict the oldest entry to keep the cache bounded.\n let oldestKey: string | null = null\n let oldestExpiry = Infinity\n for (const [key, entry] of store) {\n if (entry.expiresAt < oldestExpiry) {\n oldestExpiry = entry.expiresAt\n oldestKey = key\n }\n }\n if (oldestKey) store.delete(oldestKey)\n }\n store.set(toolCallId, { signature, expiresAt: Date.now() + ttlMs })\n}\n\n/** Resolve the signature for one tool call id, or null when unknown/expired. */\nexport function getThoughtSignature(toolCallId: string): string | null {\n if (!toolCallId) return null\n const entry = store.get(toolCallId)\n if (!entry) return null\n if (entry.expiresAt <= Date.now()) {\n store.delete(toolCallId)\n return null\n }\n return entry.signature\n}\n","/**\n * Multimodal file resolution for Google Gemini on Antigravity.\n *\n * In DeepSeek Harness (DSH), non-image files (PDF, audio, video, etc.)\n * uploaded by users are converted by DSH's LLM runtime into deterministic\n * text handles:\n *\n * `[File \"${name}\" (${bytes} bytes, sha256:${digest}): verbatim read-only copy saved at \"${readonlyPath}\". ...]`\n *\n * Gemini models natively support these files as base64-encoded `inlineData`\n * parts alongside text. Claude models running behind the Antigravity Vertex\n * proxy reject non-image `inlineData` with 500, so injection is scoped strictly\n * to Gemini (non-Claude, vision-capable) models.\n */\n\nimport { readFile } from 'node:fs/promises'\nimport type { GenerateOptions, Message } from '@deepseek-ai/dsh-llm'\nimport { catalogModel } from './catalog.ts'\n\n/**\n * MIME type mapping for Gemini supported multimodal formats.\n *\n * Covered formats (PDF observed live end-to-end; the rest follow Gemini's\n * documented multimodal support and are not yet live-verified):\n * - Document: .pdf -> 'application/pdf'\n * - Audio: .mp3 -> 'audio/mp3', .wav -> 'audio/wav', .m4a -> 'audio/m4a', .aac -> 'audio/aac', .ogg -> 'audio/ogg', .flac -> 'audio/flac'\n * - Video: .mp4 -> 'video/mp4', .mov -> 'video/quicktime', .webm -> 'video/webm'\n * - Extended image: .bmp -> 'image/bmp', .heic -> 'image/heic', .heif -> 'image/heif'\n */\nexport const GEMINI_MULTIMODAL_MIMES: Readonly<Record<string, string>> = Object.freeze({\n pdf: 'application/pdf',\n mp3: 'audio/mp3',\n wav: 'audio/wav',\n m4a: 'audio/m4a',\n aac: 'audio/aac',\n ogg: 'audio/ogg',\n flac: 'audio/flac',\n mp4: 'video/mp4',\n mov: 'video/quicktime',\n webm: 'video/webm',\n bmp: 'image/bmp',\n heic: 'image/heic',\n heif: 'image/heif',\n '.pdf': 'application/pdf',\n '.mp3': 'audio/mp3',\n '.wav': 'audio/wav',\n '.m4a': 'audio/m4a',\n '.aac': 'audio/aac',\n '.ogg': 'audio/ogg',\n '.flac': 'audio/flac',\n '.mp4': 'video/mp4',\n '.mov': 'video/quicktime',\n '.webm': 'video/webm',\n '.bmp': 'image/bmp',\n '.heic': 'image/heic',\n '.heif': 'image/heif',\n})\n\n/**\n * Regex matching DSH's deterministic read-only file handle blocks.\n * Capture groups:\n * 1: file name\n * 2: file size in bytes\n * 3: local read-only filesystem path\n */\nexport const DSH_FILE_HANDLE_REGEX =\n /\\[File \"([^\"]+)\" \\((\\d+) bytes, sha256:[a-f0-9]+\\): verbatim read-only copy saved at \"([^\"]+)\"\\./g\n\nexport const FILE_HANDLE_REGEX = DSH_FILE_HANDLE_REGEX\n\n/** Maximum file size for inlining (20MB). */\nexport const MAX_MULTIMODAL_FILE_BYTES = 20 * 1024 * 1024\nexport const MAX_MULTIMODAL_FILE_SIZE = MAX_MULTIMODAL_FILE_BYTES\n\nexport interface ExtractedFileHandle {\n name: string\n bytes: number\n readonlyPath: string\n}\n\n/** Extract file handle descriptors from text content. */\nexport function extractFileHandles(text: string): ExtractedFileHandle[] {\n const regex = new RegExp(DSH_FILE_HANDLE_REGEX.source, 'g')\n const results: ExtractedFileHandle[] = []\n for (const match of text.matchAll(regex)) {\n const [, name, bytesStr, readonlyPath] = match\n if (name && bytesStr && readonlyPath) {\n const bytes = parseInt(bytesStr, 10)\n if (!Number.isNaN(bytes)) {\n results.push({ name, bytes, readonlyPath })\n }\n }\n }\n return results\n}\n\n/** Resolve MIME type for a file name or path based on extension. */\nexport function getMultimodalMimeType(filenameOrPath: string): string | undefined {\n const dotIndex = filenameOrPath.lastIndexOf('.')\n if (dotIndex === -1) return undefined\n const ext = filenameOrPath.slice(dotIndex + 1).toLowerCase()\n return GEMINI_MULTIMODAL_MIMES[ext]\n}\n\n/** Whether a model id belongs to a Claude-branded model (Vertex-hosted). */\nexport function isClaudeModel(model: string): boolean {\n return model.startsWith('claude-') || model.includes('/claude')\n}\n\n/**\n * Check whether a model supports Gemini multimodal file inlineData.\n * Deny-by-default: Claude models are always excluded (Vertex 500 on\n * non-image inlineData), catalog models must be vision-capable, and ids\n * unknown to the catalog are only allowed when they carry the `gemini-`\n * prefix (so future tiered ids keep working without a catalog bump).\n */\nexport function supportsMultimodalFiles(model: string): boolean {\n if (isClaudeModel(model)) return false\n const meta = catalogModel(model)\n if (meta) return meta.supportsVision === true\n return model.startsWith('gemini-')\n}\n\nexport const isMultimodalSupported = supportsMultimodalFiles\nexport const isMultimodalModel = supportsMultimodalFiles\n\nexport interface AgyResolvedMultimodalFile {\n mimeType: string\n /** Pure base64 (no data: prefix). */\n data: string\n name?: string\n path?: string\n bytes?: number\n}\n\nexport interface ResolveMultimodalOptions {\n readFile?: (path: string) => Promise<Buffer | Uint8Array>\n}\n\n/**\n * Resolve multimodal files referenced in user messages.\n *\n * Reads local files up to 20MB into base64 strings. Silently ignores\n * missing/unreadable files or files exceeding the size limit so the original\n * text handle block remains intact in the prompt.\n */\nexport async function resolveMultimodalFiles(\n optionsOrMessages: GenerateOptions | readonly Message[],\n modelOrOptions?: string | ResolveMultimodalOptions,\n extraOptions?: ResolveMultimodalOptions,\n): Promise<Map<string, AgyResolvedMultimodalFile[]>> {\n let messages: readonly Message[]\n let model: string\n let customOptions: ResolveMultimodalOptions | undefined\n\n if ('messages' in optionsOrMessages) {\n messages = optionsOrMessages.messages ?? []\n model = optionsOrMessages.model ?? ''\n customOptions = typeof modelOrOptions === 'object' ? modelOrOptions : extraOptions\n } else {\n messages = optionsOrMessages\n if (typeof modelOrOptions === 'string') {\n model = modelOrOptions\n customOptions = extraOptions\n } else {\n model = ''\n customOptions = modelOrOptions\n }\n }\n\n const result = new Map<string, AgyResolvedMultimodalFile[]>()\n if (!supportsMultimodalFiles(model)) {\n return result\n }\n\n const readFn = customOptions?.readFile ?? readFile\n\n for (let i = 0; i < messages.length; i++) {\n const message = messages[i]\n if (!message || message.role !== 'user') continue\n\n const resolvedForMessage: AgyResolvedMultimodalFile[] = []\n for (const block of message.content) {\n if (block.type !== 'text' || typeof block.text !== 'string') continue\n\n const handles = extractFileHandles(block.text)\n for (const handle of handles) {\n if (handle.bytes > MAX_MULTIMODAL_FILE_BYTES) continue\n\n const mimeType = getMultimodalMimeType(handle.name) ?? getMultimodalMimeType(handle.readonlyPath)\n if (!mimeType) continue\n\n try {\n const fileBuffer = await readFn(handle.readonlyPath)\n if (fileBuffer.length > MAX_MULTIMODAL_FILE_BYTES) continue\n resolvedForMessage.push({\n mimeType,\n data: Buffer.from(fileBuffer).toString('base64'),\n name: handle.name,\n path: handle.readonlyPath,\n bytes: fileBuffer.length,\n })\n } catch {\n // Error resilience: catch error silently and do not throw\n continue\n }\n }\n }\n\n if (resolvedForMessage.length > 0) {\n const key = message.id ?? `msg-${i}`\n result.set(key, resolvedForMessage)\n }\n }\n\n return result\n}\n","/**\n * Translate a DSH GenerateOptions into the Antigravity wrapped request.\n *\n * Envelope shape follows the actively-maintained OmniRoute wire format\n * (the archived opencode reference predates it): top-level `project`,\n * `requestId`, `model`, `userAgent`, `requestType`, with the Gemini-style\n * body under `request` (contents/systemInstruction/tools/generationConfig/\n * sessionId). `toolConfig` VALIDATED is attached when tools are present, and\n * Claude-path requests strip trailing model turns (Vertex rejects \"assistant\n * message prefill\").\n *\n * Thinking blocks are carried as-is (Gemini `thought` parts); nothing is\n * stripped or re-signed — that signature dance was an artifact of the\n * reference plugin's interception architecture (see docs/ARCHITECTURE.md).\n */\n\nimport { createHash } from 'node:crypto'\nimport type { ContentBlock, GenerateOptions, Message, ToolSchema } from '@deepseek-ai/dsh-llm'\nimport { generateAntigravityRequestId } from '../runtime/identity.ts'\nimport { getThoughtSignature, THOUGHT_SIGNATURE_SENTINEL } from '../runtime/signature-cache.ts'\nimport { catalogModel, isLevelThinkingModel } from './catalog.ts'\nimport { isClaudeModel, supportsMultimodalFiles, type AgyResolvedMultimodalFile } from './multimodal.ts'\n\nexport { isClaudeModel, supportsMultimodalFiles }\nexport type { AgyResolvedMultimodalFile }\n\nexport type AgyPart =\n | { text: string }\n | { thought: true; text: string }\n | { thoughtSignature: string; functionCall: { id: string; name: string; args: unknown } }\n | { functionResponse: { name: string; response: unknown } }\n | { inlineData: { mimeType: string; data: string } }\n\n/** Image bytes pre-resolved from the durable attachment store, keyed by attachment id. */\nexport interface AgyResolvedImage {\n mediaType: string\n /** Pure base64 (no data: prefix). */\n data: string\n}\n\nexport interface AgyContent {\n role: 'user' | 'model'\n parts: AgyPart[]\n}\n\nexport interface AgyRequestBody {\n project?: string\n requestId?: string\n model: string\n userAgent?: string\n requestType?: 'agent'\n request: {\n contents: AgyContent[]\n systemInstruction?: { parts: Array<{ text: string }> }\n tools?: Array<{ functionDeclarations: Array<{ name: string; description: string; parameters: unknown }> }>\n toolConfig?: { functionCallingConfig: { mode: 'VALIDATED' } }\n generationConfig?: {\n temperature?: number\n maxOutputTokens?: number\n stopSequences?: string[]\n thinkingConfig?: { thinkingLevel: string; includeThoughts: boolean }\n }\n sessionId?: string\n }\n}\n\n// isClaudeModel is imported from ./multimodal.ts and re-exported above\n\n/**\n * Vertex (the Antigravity Claude backend) rejects conversations ending on an\n * assistant/model turn (\"assistant message prefill\"); never strip to empty.\n */\nexport function stripTrailingModelTurn(contents: AgyContent[]): AgyContent[] {\n while (contents.length > 1 && contents[contents.length - 1]?.role === 'model') {\n contents.pop()\n }\n return contents\n}\n\n/**\n * The Antigravity backend parses tool `parameters` as a strict protobuf\n * schema and rejects ANY unknown keyword with 400 (verified empirically:\n * `$schema`, `propertyNames`, `pattern`, `minLength`, ... each fail in turn).\n * Denylisting is whack-a-mole, so keep only the keywords the upstream\n * accepts. Container shapes are handled distinctly: `properties` is a\n * name->schema map (keys preserved), `items`/`additionalProperties` are\n * nested schemas (additionalProperties also accepts a boolean — live-verified\n * against the Antigravity upstream), `required`/`enum` are plain arrays.\n *\n * Keyword VALUES are also constrained by the protobuf shape (verified\n * empirically): `type` must be a single enum string (union arrays like\n * `[\"string\",\"number\"]` are rejected) and every `enum` item must be a\n * string (booleans/numbers are rejected). Values are normalized to the\n * nearest valid form instead of being dropped wholesale.\n */\n// Exported for the contract invariant test (tests/adapter.test.ts); not part of\n// the package public API (translate.ts is an internal module).\nexport const AGY_SCHEMA_ALLOWLIST = new Set([\n 'type', 'format', 'title', 'description', 'nullable',\n 'items', 'enum', 'default', 'properties', 'required', 'additionalProperties',\n])\nconst AGY_SCHEMA_MAP_KEYS = new Set(['properties'])\nconst AGY_SCHEMA_NESTED_KEYS = new Set(['items', 'additionalProperties'])\nconst AGY_SCHEMA_LIST_KEYS = new Set(['required', 'enum'])\n\nfunction sanitizeToolSchema(schema: unknown): unknown {\n if (!schema || typeof schema !== 'object') return schema\n if (Array.isArray(schema)) return schema.map((entry) => sanitizeToolSchema(entry))\n\n // Normalize union types: upstream `type` is a single enum string. Pick the\n // first non-null string type (`\"null\"` maps to the `nullable` keyword);\n // fall back to `string` when no usable type remains.\n let normalized = schema as Record<string, unknown>\n if (Array.isArray(normalized.type)) {\n const types = normalized.type.filter((t): t is string => typeof t === 'string' && t !== 'null')\n normalized = { ...normalized, type: types[0] ?? 'string' }\n }\n\n const result: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(normalized)) {\n if (!AGY_SCHEMA_ALLOWLIST.has(key)) continue\n if (AGY_SCHEMA_MAP_KEYS.has(key)) {\n const map: Record<string, unknown> = {}\n for (const [name, child] of Object.entries(value as Record<string, unknown>)) {\n map[name] = sanitizeToolSchema(child)\n }\n result[key] = map\n continue\n }\n if (AGY_SCHEMA_NESTED_KEYS.has(key)) {\n result[key] = sanitizeToolSchema(value)\n continue\n }\n if (AGY_SCHEMA_LIST_KEYS.has(key)) {\n // Upstream `enum` items must be strings; filter the rest and omit an\n // empty enum entirely (an empty array would be rejected too).\n if (key === 'enum' && Array.isArray(value)) {\n const filtered = value.filter((v): v is string => typeof v === 'string')\n if (filtered.length > 0) result[key] = filtered\n } else {\n result[key] = value\n }\n continue\n }\n result[key] = value\n }\n return result\n}\n\n/** Collect tool-call names by id so tool-result blocks can name their function. */\nfunction buildToolNameIndex(messages: readonly Message[]): Map<string, string> {\n const index = new Map<string, string>()\n for (const message of messages) {\n for (const block of message.content) {\n if (block.type === 'tool-call') {\n index.set(block.id, block.name)\n }\n }\n }\n return index\n}\n\nfunction blockToParts(\n block: ContentBlock,\n toolNames: Map<string, string>,\n images: Map<string, AgyResolvedImage>,\n): AgyPart[] {\n switch (block.type) {\n case 'text':\n return [{ text: block.text }]\n case 'reasoning':\n return [{ thought: true, text: block.text }]\n case 'tool-call': {\n // Upstream parses functionCall.args as google.protobuf.Struct and\n // rejects a raw string with 400. Guarantee an object: parse the string\n // form, fall back to {} when it is truncated/malformed (the failure\n // mode for long multi-turn histories).\n let args: unknown = {}\n if (typeof block.arguments === 'object' && block.arguments !== null && !Array.isArray(block.arguments)) {\n args = block.arguments\n } else if (typeof block.arguments === 'string') {\n try {\n const parsed: unknown = JSON.parse(block.arguments)\n if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {\n args = parsed\n }\n } catch {\n // truncated/malformed JSON -> empty object\n }\n }\n // Antigravity rejects functionCall parts without a thoughtSignature\n // (400). Replay the signature captured for this tool call id on the\n // previous turn; the sentinel is the established bypass when nothing is\n // cached (both reference implementations default to it).\n const signature = getThoughtSignature(block.id) ?? THOUGHT_SIGNATURE_SENTINEL\n return [{\n thoughtSignature: signature,\n functionCall: { id: block.id, name: block.name, args },\n }]\n }\n case 'tool-result': {\n const name = toolNames.get(block.toolCallId) ?? block.toolCallId\n const text = block.content\n .filter((b): b is Extract<ContentBlock, { type: 'text' }> => b.type === 'text')\n .map((b) => b.text)\n .join('\\n')\n return [{\n functionResponse: {\n name,\n response: { result: text, is_error: block.isError === true },\n },\n }]\n }\n case 'image': {\n // Bytes are pre-resolved by the adapter before translation; a missing\n // entry breaks that invariant and must fail loudly, never silently drop.\n const resolved = images.get(block.attachment.attachmentId)\n if (!resolved) {\n throw new Error(`agy translate: unresolved image attachment \"${block.attachment.attachmentId}\"`)\n }\n return [{ inlineData: { mimeType: resolved.mediaType, data: resolved.data } }]\n }\n default:\n return [] // unknown block types (merge-extensible) are skipped\n }\n}\n\nfunction messageToContent(\n message: Message,\n toolNames: Map<string, string>,\n images: Map<string, AgyResolvedImage>,\n multimodalFiles?: Map<string, AgyResolvedMultimodalFile[]>,\n messageIndex?: number,\n): AgyContent | null {\n const parts = message.content.flatMap((block) =>\n // Non-user images are out of scope by policy (docs ANTIGRAVITY-API §3.2):\n // skip them like any other untranslatable block instead of tripping the\n // unresolved-map guard, which protects only the user-image invariant.\n block.type === 'image' && message.role !== 'user'\n ? []\n : blockToParts(block, toolNames, images),\n )\n\n if (message.role === 'user' && multimodalFiles) {\n const files =\n (message.id ? multimodalFiles.get(message.id) : undefined) ??\n (multimodalFiles as Map<unknown, AgyResolvedMultimodalFile[]>).get(message) ??\n (messageIndex !== undefined ? multimodalFiles.get(`msg-${messageIndex}`) : undefined) ??\n (messageIndex !== undefined ? multimodalFiles.get(String(messageIndex)) : undefined)\n\n if (files) {\n for (const file of files) {\n parts.push({ inlineData: { mimeType: file.mimeType, data: file.data } })\n }\n }\n }\n\n if (parts.length === 0) return null\n const role = message.role === 'assistant' ? 'model' : 'user'\n return { role, parts }\n}\n\n/**\n * Builtin Gemini tools must not shadow functionDeclarations names (upstream\n * treats them as native tools; verified by OmniRoute's GEMINI_BUILTIN_TOOL_NAMES).\n */\nconst AGY_BUILTIN_TOOL_NAMES = new Set(['google_search', 'web_search', 'search_web', 'googleSearch'])\n\n/** Level-thinking: single id + selectable low/medium/high via thinkingLevel (catalog thinking:'level'). */\nconst LEVEL_THINKING_LEVELS = new Set(['low', 'medium', 'high'])\n\n/** Upstream functionDeclarations names are `[a-zA-Z0-9_]` and ≤64 chars (OmniRoute-verified). */\nconst AGY_TOOL_NAME_MAX_LENGTH = 64\n\n/** Sanitize a tool name to the upstream charset/length; dedupe via a short hash. */\nfunction sanitizeToolName(name: string, seen: Set<string>): string {\n let candidate = name.replace(/[^a-zA-Z0-9_]/g, '_') || 'tool'\n if (candidate.length > AGY_TOOL_NAME_MAX_LENGTH || seen.has(candidate)) {\n const hash = createHash('sha256').update(candidate).digest('hex').slice(0, 8)\n const prefix = candidate.slice(0, AGY_TOOL_NAME_MAX_LENGTH - hash.length - 1)\n candidate = `${prefix}_${hash}`\n let i = 2\n while (seen.has(candidate)) candidate = `${prefix}_${i++}_${hash}`\n }\n seen.add(candidate)\n return candidate\n}\n\nfunction toolsToDeclarations(tools: ToolSchema[] | undefined): AgyRequestBody['request']['tools'] {\n if (!tools || tools.length === 0) return undefined\n const seenNames = new Set<string>()\n const declarations = []\n for (const tool of tools) {\n if (AGY_BUILTIN_TOOL_NAMES.has(tool.name)) continue\n declarations.push({\n name: sanitizeToolName(tool.name, seenNames),\n description: tool.description,\n parameters: sanitizeToolSchema(tool.parameters),\n })\n }\n if (declarations.length === 0) return undefined\n return [{ functionDeclarations: declarations }]\n}\n\n/** Build the wrapped Antigravity request body for one call. */\nexport function toAgyRequestBody(\n options: GenerateOptions,\n context: {\n projectId?: string\n sessionId?: string\n images?: Map<string, AgyResolvedImage>\n multimodalFiles?: Map<string, AgyResolvedMultimodalFile[]>\n },\n): AgyRequestBody {\n const toolNames = buildToolNameIndex(options.messages)\n const images = context.images ?? new Map<string, AgyResolvedImage>()\n const multimodalFiles = supportsMultimodalFiles(options.model) ? context.multimodalFiles : undefined\n let contents = options.messages\n .map((message, index) => messageToContent(message, toolNames, images, multimodalFiles, index))\n .filter((c): c is AgyContent => c !== null)\n if (isClaudeModel(options.model)) {\n contents = stripTrailingModelTurn(contents)\n }\n\n const tools = toolsToDeclarations(options.tools)\n const generationConfig: NonNullable<AgyRequestBody['request']['generationConfig']> = {}\n if (options.temperature !== undefined) generationConfig.temperature = options.temperature\n if (options.maxTokens !== undefined) generationConfig.maxOutputTokens = options.maxTokens\n if (options.stop !== undefined && options.stop.length > 0) generationConfig.stopSequences = options.stop\n // Level-thinking: map the DSH reasoning effort to thinkingConfig.\n // Id-bound models (thinking !== 'level') never emit it — default is UI hint, not wire default.\n const effort = options.reasoningEffort?.toLowerCase()\n if (effort && isLevelThinkingModel(options.model) && LEVEL_THINKING_LEVELS.has(effort)) {\n generationConfig.thinkingConfig = { thinkingLevel: effort, includeThoughts: true }\n }\n\n return {\n project: context.projectId || undefined,\n requestId: generateAntigravityRequestId(),\n model: options.model,\n userAgent: 'antigravity',\n requestType: 'agent',\n request: {\n contents,\n ...(options.system ? { systemInstruction: { parts: [{ text: options.system }] } } : {}),\n ...(tools ? { tools } : {}),\n ...(tools ? { toolConfig: { functionCallingConfig: { mode: 'VALIDATED' } } } : {}),\n ...(Object.keys(generationConfig).length > 0 ? { generationConfig } : {}),\n ...(context.sessionId ? { sessionId: context.sessionId } : {}),\n },\n }\n}\n"],"mappings":";;;;;AAuBA,MAAM,iBAAiB;AACvB,MAAM,cAAc;AAEpB,MAAM,wBAAQ,IAAI,IAAwB;AAE1C,SAAS,aAAa,MAAM,KAAK,IAAI,GAAS;CAC5C,IAAI,MAAM,OAAO,cAAc,GAAG;CAClC,KAAK,MAAM,CAAC,KAAK,UAAU,OACzB,IAAI,MAAM,aAAa,KAAK,MAAM,OAAO,GAAG;AAEhD;;AAGA,SAAgB,oBAAoB,YAAoB,WAAmB,QAAQ,gBAAsB;CACvG,IAAI,CAAC,cAAc,CAAC,WAAW;CAC/B,aAAa;CACb,IAAI,MAAM,QAAQ,aAAa;EAE7B,IAAI,YAA2B;EAC/B,IAAI,eAAe;EACnB,KAAK,MAAM,CAAC,KAAK,UAAU,OACzB,IAAI,MAAM,YAAY,cAAc;GAClC,eAAe,MAAM;GACrB,YAAY;EACd;EAEF,IAAI,WAAW,MAAM,OAAO,SAAS;CACvC;CACA,MAAM,IAAI,YAAY;EAAE;EAAW,WAAW,KAAK,IAAI,IAAI;CAAM,CAAC;AACpE;;AAGA,SAAgB,oBAAoB,YAAmC;CACrE,IAAI,CAAC,YAAY,OAAO;CACxB,MAAM,QAAQ,MAAM,IAAI,UAAU;CAClC,IAAI,CAAC,OAAO,OAAO;CACnB,IAAI,MAAM,aAAa,KAAK,IAAI,GAAG;EACjC,MAAM,OAAO,UAAU;EACvB,OAAO;CACT;CACA,OAAO,MAAM;AACf;;;;;;;;;;;;;;;;;;;;;;;;;;;ACnCA,MAAa,0BAA4D,OAAO,OAAO;CACrF,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CACL,MAAM;CACN,KAAK;CACL,MAAM;CACN,MAAM;CACN,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,SAAS;CACT,SAAS;AACX,CAAC;;;;;;;;AASD,MAAa,wBACX;;AAeF,SAAgB,mBAAmB,MAAqC;CACtE,MAAM,QAAQ,IAAI,OAAO,sBAAsB,QAAQ,GAAG;CAC1D,MAAM,UAAiC,CAAC;CACxC,KAAK,MAAM,SAAS,KAAK,SAAS,KAAK,GAAG;EACxC,MAAM,GAAG,MAAM,UAAU,gBAAgB;EACzC,IAAI,QAAQ,YAAY,cAAc;GACpC,MAAM,QAAQ,SAAS,UAAU,EAAE;GACnC,IAAI,CAAC,OAAO,MAAM,KAAK,GACrB,QAAQ,KAAK;IAAE;IAAM;IAAO;GAAa,CAAC;EAE9C;CACF;CACA,OAAO;AACT;;AAGA,SAAgB,sBAAsB,gBAA4C;CAChF,MAAM,WAAW,eAAe,YAAY,GAAG;CAC/C,IAAI,aAAa,IAAI,OAAO,KAAA;CAC5B,MAAM,MAAM,eAAe,MAAM,WAAW,CAAC,CAAC,CAAC,YAAY;CAC3D,OAAO,wBAAwB;AACjC;;AAGA,SAAgB,cAAc,OAAwB;CACpD,OAAO,MAAM,WAAW,SAAS,KAAK,MAAM,SAAS,SAAS;AAChE;;;;;;;;AASA,SAAgB,wBAAwB,OAAwB;CAC9D,IAAI,cAAc,KAAK,GAAG,OAAO;CACjC,MAAM,OAAO,aAAa,KAAK;CAC/B,IAAI,MAAM,OAAO,KAAK,mBAAmB;CACzC,OAAO,MAAM,WAAW,SAAS;AACnC;;;;;;;;AAyBA,eAAsB,uBACpB,mBACA,gBACA,cACmD;CACnD,IAAI;CACJ,IAAI;CACJ,IAAI;CAEJ,IAAI,cAAc,mBAAmB;EACnC,WAAW,kBAAkB,YAAY,CAAC;EAC1C,QAAQ,kBAAkB,SAAS;EACnC,gBAAgB,OAAO,mBAAmB,WAAW,iBAAiB;CACxE,OAAO;EACL,WAAW;EACX,IAAI,OAAO,mBAAmB,UAAU;GACtC,QAAQ;GACR,gBAAgB;EAClB,OAAO;GACL,QAAQ;GACR,gBAAgB;EAClB;CACF;CAEA,MAAM,yBAAS,IAAI,IAAyC;CAC5D,IAAI,CAAC,wBAAwB,KAAK,GAChC,OAAO;CAGT,MAAM,SAAS,eAAe,YAAY;CAE1C,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;EACxC,MAAM,UAAU,SAAS;EACzB,IAAI,CAAC,WAAW,QAAQ,SAAS,QAAQ;EAEzC,MAAM,qBAAkD,CAAC;EACzD,KAAK,MAAM,SAAS,QAAQ,SAAS;GACnC,IAAI,MAAM,SAAS,UAAU,OAAO,MAAM,SAAS,UAAU;GAE7D,MAAM,UAAU,mBAAmB,MAAM,IAAI;GAC7C,KAAK,MAAM,UAAU,SAAS;IAC5B,IAAI,OAAO,QAAA,UAAmC;IAE9C,MAAM,WAAW,sBAAsB,OAAO,IAAI,KAAK,sBAAsB,OAAO,YAAY;IAChG,IAAI,CAAC,UAAU;IAEf,IAAI;KACF,MAAM,aAAa,MAAM,OAAO,OAAO,YAAY;KACnD,IAAI,WAAW,SAAA,UAAoC;KACnD,mBAAmB,KAAK;MACtB;MACA,MAAM,OAAO,KAAK,UAAU,CAAC,CAAC,SAAS,QAAQ;MAC/C,MAAM,OAAO;MACb,MAAM,OAAO;MACb,OAAO,WAAW;KACpB,CAAC;IACH,QAAQ;KAEN;IACF;GACF;EACF;EAEA,IAAI,mBAAmB,SAAS,GAAG;GACjC,MAAM,MAAM,QAAQ,MAAM,OAAO;GACjC,OAAO,IAAI,KAAK,kBAAkB;EACpC;CACF;CAEA,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AChJA,SAAgB,uBAAuB,UAAsC;CAC3E,OAAO,SAAS,SAAS,KAAK,SAAS,SAAS,SAAS,EAAE,EAAE,SAAS,SACpE,SAAS,IAAI;CAEf,OAAO;AACT;;;;;;;;;;;;;;;;;AAoBA,MAAa,uCAAuB,IAAI,IAAI;CAC1C;CAAQ;CAAU;CAAS;CAAe;CAC1C;CAAS;CAAQ;CAAW;CAAc;CAAY;AACxD,CAAC;AACD,MAAM,sCAAsB,IAAI,IAAI,CAAC,YAAY,CAAC;AAClD,MAAM,yCAAyB,IAAI,IAAI,CAAC,SAAS,sBAAsB,CAAC;AACxE,MAAM,uCAAuB,IAAI,IAAI,CAAC,YAAY,MAAM,CAAC;AAEzD,SAAS,mBAAmB,QAA0B;CACpD,IAAI,CAAC,UAAU,OAAO,WAAW,UAAU,OAAO;CAClD,IAAI,MAAM,QAAQ,MAAM,GAAG,OAAO,OAAO,KAAK,UAAU,mBAAmB,KAAK,CAAC;CAKjF,IAAI,aAAa;CACjB,IAAI,MAAM,QAAQ,WAAW,IAAI,GAAG;EAClC,MAAM,QAAQ,WAAW,KAAK,QAAQ,MAAmB,OAAO,MAAM,YAAY,MAAM,MAAM;EAC9F,aAAa;GAAE,GAAG;GAAY,MAAM,MAAM,MAAM;EAAS;CAC3D;CAEA,MAAM,SAAkC,CAAC;CACzC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,UAAU,GAAG;EACrD,IAAI,CAAC,qBAAqB,IAAI,GAAG,GAAG;EACpC,IAAI,oBAAoB,IAAI,GAAG,GAAG;GAChC,MAAM,MAA+B,CAAC;GACtC,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,KAAgC,GACzE,IAAI,QAAQ,mBAAmB,KAAK;GAEtC,OAAO,OAAO;GACd;EACF;EACA,IAAI,uBAAuB,IAAI,GAAG,GAAG;GACnC,OAAO,OAAO,mBAAmB,KAAK;GACtC;EACF;EACA,IAAI,qBAAqB,IAAI,GAAG,GAAG;GAGjC,IAAI,QAAQ,UAAU,MAAM,QAAQ,KAAK,GAAG;IAC1C,MAAM,WAAW,MAAM,QAAQ,MAAmB,OAAO,MAAM,QAAQ;IACvE,IAAI,SAAS,SAAS,GAAG,OAAO,OAAO;GACzC,OACE,OAAO,OAAO;GAEhB;EACF;EACA,OAAO,OAAO;CAChB;CACA,OAAO;AACT;;AAGA,SAAS,mBAAmB,UAAmD;CAC7E,MAAM,wBAAQ,IAAI,IAAoB;CACtC,KAAK,MAAM,WAAW,UACpB,KAAK,MAAM,SAAS,QAAQ,SAC1B,IAAI,MAAM,SAAS,aACjB,MAAM,IAAI,MAAM,IAAI,MAAM,IAAI;CAIpC,OAAO;AACT;AAEA,SAAS,aACP,OACA,WACA,QACW;CACX,QAAQ,MAAM,MAAd;EACE,KAAK,QACH,OAAO,CAAC,EAAE,MAAM,MAAM,KAAK,CAAC;EAC9B,KAAK,aACH,OAAO,CAAC;GAAE,SAAS;GAAM,MAAM,MAAM;EAAK,CAAC;EAC7C,KAAK,aAAa;GAKhB,IAAI,OAAgB,CAAC;GACrB,IAAI,OAAO,MAAM,cAAc,YAAY,MAAM,cAAc,QAAQ,CAAC,MAAM,QAAQ,MAAM,SAAS,GACnG,OAAO,MAAM;QACR,IAAI,OAAO,MAAM,cAAc,UACpC,IAAI;IACF,MAAM,SAAkB,KAAK,MAAM,MAAM,SAAS;IAClD,IAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,CAAC,MAAM,QAAQ,MAAM,GACxE,OAAO;GAEX,QAAQ,CAER;GAOF,OAAO,CAAC;IACN,kBAFgB,oBAAoB,MAAM,EAAE,KAAA;IAG5C,cAAc;KAAE,IAAI,MAAM;KAAI,MAAM,MAAM;KAAM;IAAK;GACvD,CAAC;EACH;EACA,KAAK,eAMH,OAAO,CAAC,EACN,kBAAkB;GAChB,MAPS,UAAU,IAAI,MAAM,UAAU,KAAK,MAAM;GAQlD,UAAU;IAAE,QAPH,MAAM,QAChB,QAAQ,MAAoD,EAAE,SAAS,MAAM,CAAC,CAC9E,KAAK,MAAM,EAAE,IAAI,CAAC,CAClB,KAAK,IAImB;IAAG,UAAU,MAAM,YAAY;GAAK;EAC7D,EACF,CAAC;EAEH,KAAK,SAAS;GAGZ,MAAM,WAAW,OAAO,IAAI,MAAM,WAAW,YAAY;GACzD,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,+CAA+C,MAAM,WAAW,aAAa,EAAE;GAEjG,OAAO,CAAC,EAAE,YAAY;IAAE,UAAU,SAAS;IAAW,MAAM,SAAS;GAAK,EAAE,CAAC;EAC/E;EACA,SACE,OAAO,CAAC;CACZ;AACF;AAEA,SAAS,iBACP,SACA,WACA,QACA,iBACA,cACmB;CACnB,MAAM,QAAQ,QAAQ,QAAQ,SAAS,UAIrC,MAAM,SAAS,WAAW,QAAQ,SAAS,SACvC,CAAC,IACD,aAAa,OAAO,WAAW,MAAM,CAC3C;CAEA,IAAI,QAAQ,SAAS,UAAU,iBAAiB;EAC9C,MAAM,SACH,QAAQ,KAAK,gBAAgB,IAAI,QAAQ,EAAE,IAAI,KAAA,MAC/C,gBAA8D,IAAI,OAAO,MACzE,iBAAiB,KAAA,IAAY,gBAAgB,IAAI,OAAO,cAAc,IAAI,KAAA,OAC1E,iBAAiB,KAAA,IAAY,gBAAgB,IAAI,OAAO,YAAY,CAAC,IAAI,KAAA;EAE5E,IAAI,OACF,KAAK,MAAM,QAAQ,OACjB,MAAM,KAAK,EAAE,YAAY;GAAE,UAAU,KAAK;GAAU,MAAM,KAAK;EAAK,EAAE,CAAC;CAG7E;CAEA,IAAI,MAAM,WAAW,GAAG,OAAO;CAE/B,OAAO;EAAE,MADI,QAAQ,SAAS,cAAc,UAAU;EACvC;CAAM;AACvB;;;;;AAMA,MAAM,yCAAyB,IAAI,IAAI;CAAC;CAAiB;CAAc;CAAc;AAAc,CAAC;;AAGpG,MAAM,wCAAwB,IAAI,IAAI;CAAC;CAAO;CAAU;AAAM,CAAC;;AAG/D,MAAM,2BAA2B;;AAGjC,SAAS,iBAAiB,MAAc,MAA2B;CACjE,IAAI,YAAY,KAAK,QAAQ,kBAAkB,GAAG,KAAK;CACvD,IAAI,UAAU,SAAS,4BAA4B,KAAK,IAAI,SAAS,GAAG;EACtE,MAAM,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,SAAS,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC;EAC5E,MAAM,SAAS,UAAU,MAAM,GAAG,2BAA2B,KAAK,SAAS,CAAC;EAC5E,YAAY,GAAG,OAAO,GAAG;EACzB,IAAI,IAAI;EACR,OAAO,KAAK,IAAI,SAAS,GAAG,YAAY,GAAG,OAAO,GAAG,IAAI,GAAG;CAC9D;CACA,KAAK,IAAI,SAAS;CAClB,OAAO;AACT;AAEA,SAAS,oBAAoB,OAAqE;CAChG,IAAI,CAAC,SAAS,MAAM,WAAW,GAAG,OAAO,KAAA;CACzC,MAAM,4BAAY,IAAI,IAAY;CAClC,MAAM,eAAe,CAAC;CACtB,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,uBAAuB,IAAI,KAAK,IAAI,GAAG;EAC3C,aAAa,KAAK;GAChB,MAAM,iBAAiB,KAAK,MAAM,SAAS;GAC3C,aAAa,KAAK;GAClB,YAAY,mBAAmB,KAAK,UAAU;EAChD,CAAC;CACH;CACA,IAAI,aAAa,WAAW,GAAG,OAAO,KAAA;CACtC,OAAO,CAAC,EAAE,sBAAsB,aAAa,CAAC;AAChD;;AAGA,SAAgB,iBACd,SACA,SAMgB;CAChB,MAAM,YAAY,mBAAmB,QAAQ,QAAQ;CACrD,MAAM,SAAS,QAAQ,0BAAU,IAAI,IAA8B;CACnE,MAAM,kBAAkB,wBAAwB,QAAQ,KAAK,IAAI,QAAQ,kBAAkB,KAAA;CAC3F,IAAI,WAAW,QAAQ,SACpB,KAAK,SAAS,UAAU,iBAAiB,SAAS,WAAW,QAAQ,iBAAiB,KAAK,CAAC,CAAC,CAC7F,QAAQ,MAAuB,MAAM,IAAI;CAC5C,IAAI,cAAc,QAAQ,KAAK,GAC7B,WAAW,uBAAuB,QAAQ;CAG5C,MAAM,QAAQ,oBAAoB,QAAQ,KAAK;CAC/C,MAAM,mBAA+E,CAAC;CACtF,IAAI,QAAQ,gBAAgB,KAAA,GAAW,iBAAiB,cAAc,QAAQ;CAC9E,IAAI,QAAQ,cAAc,KAAA,GAAW,iBAAiB,kBAAkB,QAAQ;CAChF,IAAI,QAAQ,SAAS,KAAA,KAAa,QAAQ,KAAK,SAAS,GAAG,iBAAiB,gBAAgB,QAAQ;CAGpG,MAAM,SAAS,QAAQ,iBAAiB,YAAY;CACpD,IAAI,UAAU,qBAAqB,QAAQ,KAAK,KAAK,sBAAsB,IAAI,MAAM,GACnF,iBAAiB,iBAAiB;EAAE,eAAe;EAAQ,iBAAiB;CAAK;CAGnF,OAAO;EACL,SAAS,QAAQ,aAAa,KAAA;EAC9B,WAAW,6BAA6B;EACxC,OAAO,QAAQ;EACf,WAAW;EACX,aAAa;EACb,SAAS;GACP;GACA,GAAI,QAAQ,SAAS,EAAE,mBAAmB,EAAE,OAAO,CAAC,EAAE,MAAM,QAAQ,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC;GACrF,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;GACzB,GAAI,QAAQ,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,YAAY,EAAE,EAAE,IAAI,CAAC;GAChF,GAAI,OAAO,KAAK,gBAAgB,CAAC,CAAC,SAAS,IAAI,EAAE,iBAAiB,IAAI,CAAC;GACvE,GAAI,QAAQ,YAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;EAC9D;CACF;AACF"}
|
package/lib/web/plugin.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { d as isAgyDisabled, f as generateFingerprint, g as resolveAntigravityVersionBounded, n as maskProxyUrl, p as recordFingerprintVersion } from "../accounts-
|
|
1
|
+
import { d as isAgyDisabled, f as generateFingerprint, g as resolveAntigravityVersionBounded, n as maskProxyUrl, p as recordFingerprintVersion } from "../accounts-D5LlcqCO.mjs";
|
|
2
2
|
import { a as proxyUrlForLogs, r as normalizeProxyUrl, t as isProxyReachable } from "../proxy-DQPIwUov.mjs";
|
|
3
|
-
import { t as createAgyRuntime } from "../plugin-common-
|
|
4
|
-
import { t as exchangeAntigravity } from "../exchange-
|
|
5
|
-
import { n as upsertImportedAccount, r as authorizeAntigravity, t as importManySources } from "../import-
|
|
3
|
+
import { t as createAgyRuntime } from "../plugin-common-BkcyXO5Y.mjs";
|
|
4
|
+
import { t as exchangeAntigravity } from "../exchange-D1sCdzuN.mjs";
|
|
5
|
+
import { n as upsertImportedAccount, r as authorizeAntigravity, t as importManySources } from "../import-BPKii1UF.mjs";
|
|
6
6
|
//#region src/web/i18n.ts
|
|
7
7
|
const I18N_DICT = {
|
|
8
8
|
en: {
|
|
@@ -1382,7 +1382,7 @@ function createAgyWebRoutes(options) {
|
|
|
1382
1382
|
const session = await sessions.getSession().catch(() => void 0);
|
|
1383
1383
|
if (!session || session.index !== entry.index) continue;
|
|
1384
1384
|
try {
|
|
1385
|
-
const { fetchAvailableModels } = await import("../models-
|
|
1385
|
+
const { fetchAvailableModels } = await import("../models-ExG7kJs1.mjs").then((n) => n.r);
|
|
1386
1386
|
const discovered = await fetchAvailableModels(session.auth.access, session.account.projectId);
|
|
1387
1387
|
const models = Object.entries(discovered.models ?? {});
|
|
1388
1388
|
if (models.length > 0) {
|
|
@@ -1548,7 +1548,7 @@ function createAgyWebRoutes(options) {
|
|
|
1548
1548
|
return;
|
|
1549
1549
|
}
|
|
1550
1550
|
try {
|
|
1551
|
-
const { listAgyModels } = await import("../models-
|
|
1551
|
+
const { listAgyModels } = await import("../models-ExG7kJs1.mjs").then((n) => n.r);
|
|
1552
1552
|
const models = await listAgyModels(session.auth.access, session.account.projectId);
|
|
1553
1553
|
sendJson(res, 200, {
|
|
1554
1554
|
account: session.account.email ?? null,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-agy",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"packageManager": "pnpm@11.10.0",
|
|
5
5
|
"description": "Google Antigravity (agy) OAuth auth + model access plugin for DeepSeek Harness: multi-account pool, 429 rotation, device fingerprinting, CLI and web login.",
|
|
6
6
|
"type": "module",
|