dsh-agy 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.
@@ -0,0 +1,270 @@
1
+ import { T as fetchAgyFirstOk, a as MASTER_KEY_REF, c as loadMasterKey, d as resolveMasterKeyCodec, g as generateAntigravityRequestId, h as deriveAntigravitySessionId, i as JsonAccountStore, l as persistMasterKey, o as createAesGcmCodec, p as resolveAntigravityVersion, s as deriveKey, t as AgySessionManager, u as resolveDshHome } from "./session-tawi2_uh.mjs";
2
+ import { r as setThoughtSignature, t as toAgyRequestBody } from "./translate-h2bjH7ub.mjs";
3
+ import { t as parseAgySse } from "./parse-CiEzD9d_.mjs";
4
+ import { r as resolveAgyModel, t as listAgyModels } from "./models-Cu1g7AXV.mjs";
5
+ import { randomBytes } from "node:crypto";
6
+ import { LlmAdapter, LlmError, ProviderRequestId, QUOTA_EXCEEDED_CODE, attributionHeaders } from "@deepseek-ai/dsh-llm";
7
+ //#region src/runtime/classify.ts
8
+ const QUOTA_EXHAUSTED_KEYWORDS = [
9
+ "quota_exhausted",
10
+ "quota exhausted",
11
+ "quota reached",
12
+ "enable overages",
13
+ "individual quota"
14
+ ];
15
+ /** Classify a 429 body into the four upstream categories. */
16
+ function classifyRateLimit(bodyText, retryAfterMs) {
17
+ const text = (bodyText ?? "").toLowerCase();
18
+ if (QUOTA_EXHAUSTED_KEYWORDS.some((keyword) => text.includes(keyword))) return "quota_exhausted";
19
+ if (retryAfterMs !== void 0 && retryAfterMs < 3e3) return "soft_rate_limit";
20
+ if (retryAfterMs !== void 0) return "rate_limited";
21
+ return text.includes("quota") || text.includes("resource_exhausted") ? "quota_exhausted" : "unknown";
22
+ }
23
+ const RATE_LIMIT_RESET_FIELDS = [
24
+ "resetTime",
25
+ "reset_time",
26
+ "resetAt",
27
+ "quotaResetTime"
28
+ ];
29
+ function extractResetTime(bodyText) {
30
+ if (!bodyText) return void 0;
31
+ try {
32
+ const data = JSON.parse(bodyText);
33
+ for (const field of RATE_LIMIT_RESET_FIELDS) {
34
+ const value = data[field];
35
+ if (typeof value === "string" && value) return value;
36
+ if (typeof value === "number" && Number.isFinite(value)) return value > 0xe8d4a51000 ? new Date(value).toISOString() : new Date(Date.now() + value * 1e3).toISOString();
37
+ }
38
+ const quotaInfo = data.quotaInfo;
39
+ if (quotaInfo && typeof quotaInfo.resetTime === "string") return quotaInfo.resetTime;
40
+ } catch {}
41
+ }
42
+ function parseRetryAfter(header) {
43
+ if (!header) return void 0;
44
+ const seconds = Number(header);
45
+ if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1e3;
46
+ const date = Date.parse(header);
47
+ if (!Number.isNaN(date)) return Math.max(0, date - Date.now());
48
+ }
49
+ /** Classify a completed HTTP response (non-2xx). */
50
+ function classifyHttpError(status, headers, bodyText) {
51
+ const retryAfterMs = parseRetryAfter(headers.get("retry-after"));
52
+ const resetTime = extractResetTime(bodyText);
53
+ if (status === 429) return {
54
+ kind: "rate-limit",
55
+ rateLimitCategory: classifyRateLimit(bodyText, retryAfterMs),
56
+ status,
57
+ retryAfterMs,
58
+ resetTime,
59
+ message: bodyText ? bodyText.slice(0, 200) : void 0
60
+ };
61
+ if (status === 401) return {
62
+ kind: "auth-failure",
63
+ status,
64
+ message: bodyText ? bodyText.slice(0, 200) : void 0
65
+ };
66
+ if (status === 403) {
67
+ const category = classifyRateLimit(bodyText, void 0);
68
+ if (category === "quota_exhausted") return {
69
+ kind: "rate-limit",
70
+ rateLimitCategory: category,
71
+ status,
72
+ message: bodyText ? bodyText.slice(0, 200) : void 0
73
+ };
74
+ return {
75
+ kind: "auth-failure",
76
+ status,
77
+ message: bodyText ? bodyText.slice(0, 200) : void 0
78
+ };
79
+ }
80
+ if (status === 404) return {
81
+ kind: "transient",
82
+ status,
83
+ message: bodyText ? bodyText.slice(0, 200) : void 0
84
+ };
85
+ if (status >= 500) return {
86
+ kind: "transient",
87
+ status,
88
+ retryAfterMs,
89
+ message: bodyText ? bodyText.slice(0, 200) : void 0
90
+ };
91
+ return {
92
+ kind: "transient",
93
+ status,
94
+ message: bodyText ? bodyText.slice(0, 200) : void 0
95
+ };
96
+ }
97
+ /** Classify a fetch-level failure (DNS, refused, timeout, abort). */
98
+ function classifyFetchError(error) {
99
+ const message = error instanceof Error ? error.message : String(error);
100
+ if (error instanceof DOMException && error.name === "AbortError") return {
101
+ kind: "network-error",
102
+ message
103
+ };
104
+ return {
105
+ kind: "network-error",
106
+ message
107
+ };
108
+ }
109
+ //#endregion
110
+ //#region src/adapter/adapter.ts
111
+ /**
112
+ * AgyAdapter: the DSH seam. A thin orchestrator over the deep modules —
113
+ * account session resolution (shell-provided), request translation, SSE
114
+ * parsing, failure classification, and rotation reporting. All wire details
115
+ * live in translate.ts / parse.ts / models.ts.
116
+ */
117
+ const UPSTREAM_ERROR_CODE = "UPSTREAM";
118
+ /** Build the impersonation headers for one request (per-request randomization applied by the shell). */
119
+ function buildRequestHeaders(session) {
120
+ return {
121
+ authorization: `Bearer ${session.auth.access}`,
122
+ "content-type": "application/json",
123
+ accept: "text/event-stream",
124
+ "x-goog-request-id": generateAntigravityRequestId(),
125
+ ...attributionHeaders(),
126
+ ...session.impersonation
127
+ };
128
+ }
129
+ var AgyAdapter = class extends LlmAdapter {
130
+ options;
131
+ constructor(options) {
132
+ super();
133
+ this.options = options;
134
+ }
135
+ providerInfo(_provider) {
136
+ return {
137
+ id: "agy",
138
+ name: "Antigravity (agy)"
139
+ };
140
+ }
141
+ async listModels(_provider) {
142
+ const session = await this.options.getSession();
143
+ return listAgyModels(session?.auth.access, session?.account.projectId);
144
+ }
145
+ async resolveModel(provider, model) {
146
+ return resolveAgyModel(provider, model);
147
+ }
148
+ async *stream(options) {
149
+ const session = await this.options.getSession();
150
+ if (!session) throw new LlmError("No agy account configured — run `dsh-agy login` to authenticate.", "NO_CREDENTIAL");
151
+ const body = toAgyRequestBody(options, {
152
+ projectId: session.account.projectId,
153
+ sessionId: deriveAntigravitySessionId(session.account.email) ?? void 0
154
+ });
155
+ const headers = buildRequestHeaders(session);
156
+ let response;
157
+ try {
158
+ response = await fetchAgyFirstOk("/v1internal:streamGenerateContent?alt=sse", {
159
+ method: "POST",
160
+ headers,
161
+ body: JSON.stringify(body),
162
+ signal: options.signal
163
+ });
164
+ } catch (error) {
165
+ const classified = classifyFetchError(error);
166
+ await this.options.reportFailure(classified.kind, session);
167
+ throw new LlmError(classified.message ?? "agy fetch failed", "TRANSPORT", { cause: error });
168
+ }
169
+ if (!response.ok) {
170
+ const bodyText = await response.text().catch(() => void 0);
171
+ const classified = classifyHttpError(response.status, response.headers, bodyText);
172
+ await this.options.reportFailure(classified.kind, session, {
173
+ retryAfterMs: classified.retryAfterMs,
174
+ status: response.status,
175
+ rateLimitCategory: classified.rateLimitCategory
176
+ });
177
+ if (classified.kind === "rate-limit") {
178
+ if (classified.rateLimitCategory === "quota_exhausted") throw new LlmError(`agy daily quota exhausted (${response.status}): ${classified.message ?? ""}`, QUOTA_EXCEEDED_CODE);
179
+ throw new LlmError(`agy rate-limited (${response.status}): ${classified.message ?? ""}`, "RATE_LIMIT", {
180
+ providerRetryAfterMs: classified.retryAfterMs ?? void 0,
181
+ requestId: ProviderRequestId(generateAntigravityRequestId())
182
+ });
183
+ }
184
+ if (classified.kind === "auth-failure") throw new LlmError(`agy authentication failed (${response.status}) — run \`dsh-agy login\``, "INVALID_CREDENTIAL");
185
+ throw new LlmError(`agy upstream error (${response.status}): ${classified.message ?? ""}`, UPSTREAM_ERROR_CODE);
186
+ }
187
+ if (!response.body) throw new LlmError("agy stream returned no body", UPSTREAM_ERROR_CODE);
188
+ try {
189
+ yield* parseAgySse(response.body, {
190
+ signal: options.signal,
191
+ onToolSignature: (toolCallId, signature) => {
192
+ setThoughtSignature(toolCallId, signature);
193
+ }
194
+ });
195
+ await this.options.markSuccess?.(session);
196
+ } catch (error) {
197
+ if (error instanceof DOMException && error.name === "AbortError") throw new LlmError("agy stream aborted", "ABORTED", { cause: error });
198
+ await this.options.reportFailure("network-error", session);
199
+ throw new LlmError(error instanceof Error ? error.message : "agy stream parse failed", UPSTREAM_ERROR_CODE, { cause: error });
200
+ }
201
+ }
202
+ };
203
+ //#endregion
204
+ //#region src/plugin-common.ts
205
+ /**
206
+ * Shared runtime construction for the in-harness plugin entries: master-key
207
+ * codec resolution (credentials seam first, credentials document fallback),
208
+ * account store, session manager, and adapter. Used by the main plugin
209
+ * (adapter registration) and the web plugin (route registration) so both
210
+ * entries operate on the same store.
211
+ */
212
+ function codecFrom(masterKey) {
213
+ return createAesGcmCodec(deriveKey(masterKey));
214
+ }
215
+ /** Resolve or create the master key, preferring the credentials seam when available. */
216
+ async function resolveCodec(ctx) {
217
+ const dshHome = resolveDshHome();
218
+ const credentials = ctx.get("credentials");
219
+ if (credentials) {
220
+ const resolved = await credentials.resolve(MASTER_KEY_REF);
221
+ if (resolved) return {
222
+ codec: codecFrom(resolved.value),
223
+ created: false
224
+ };
225
+ const fileKey = loadMasterKey(dshHome);
226
+ if (fileKey) return {
227
+ codec: codecFrom(fileKey),
228
+ created: false
229
+ };
230
+ const fresh = randomBytes(32).toString("hex");
231
+ try {
232
+ await credentials.set(MASTER_KEY_REF, fresh);
233
+ return {
234
+ codec: codecFrom(fresh),
235
+ created: true
236
+ };
237
+ } catch {
238
+ persistMasterKey(dshHome, fresh);
239
+ return {
240
+ codec: codecFrom(fresh),
241
+ created: true
242
+ };
243
+ }
244
+ }
245
+ return resolveMasterKeyCodec(dshHome);
246
+ }
247
+ /** Build the store, session manager, and adapter for one plugin entry. */
248
+ async function createAgyRuntime(ctx) {
249
+ resolveAntigravityVersion().catch(() => {});
250
+ const { codec } = await resolveCodec(ctx);
251
+ const dshHome = resolveDshHome();
252
+ const store = new JsonAccountStore({
253
+ file: `${dshHome}/agy-accounts.json`,
254
+ codec
255
+ });
256
+ const sessions = new AgySessionManager({ store });
257
+ return {
258
+ store,
259
+ sessions,
260
+ adapter: new AgyAdapter({
261
+ getSession: () => sessions.getSession(),
262
+ reportFailure: (kind, session, info) => sessions.reportFailure(kind, session, info),
263
+ markSuccess: (session) => sessions.markSuccess(session)
264
+ })
265
+ };
266
+ }
267
+ //#endregion
268
+ export { createAgyRuntime as t };
269
+
270
+ //# sourceMappingURL=plugin-common-I56kmGjr.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-common-I56kmGjr.mjs","names":[],"sources":["../src/runtime/classify.ts","../src/adapter/adapter.ts","../src/plugin-common.ts"],"sourcesContent":["/**\n * Upstream failure classification: HTTP status + headers + body → FailureKind.\n * Conservative by design: only auth-failure may revoke an account; everything\n * else cools, rotates, or retries.\n */\n\nimport type { FailureKind } from '../types.ts'\n\nexport interface ClassifiedError {\n kind: FailureKind\n status?: number\n /** Server-provided retry delay (ms), when the response carried it. */\n retryAfterMs?: number\n /** Server-provided absolute reset time, when the response carried it. */\n resetTime?: string\n message?: string\n /** 429 sub-category, present when kind is rate-limit. */\n rateLimitCategory?: RateLimitCategory\n}\n\n/**\n * 429 sub-category (mirrors CLIProxyAPI/OmniRoute's classify429):\n * - soft_rate_limit: transient burst, retry immediately on the same account\n * - rate_limited: per-minute limit, short cooldown then retry (same account\n * when there is no other)\n * - quota_exhausted: daily/plan quota gone, long cooldown (24h)\n * - unknown: exponential backoff\n */\nexport type RateLimitCategory = 'soft_rate_limit' | 'rate_limited' | 'quota_exhausted' | 'unknown'\n\nconst QUOTA_EXHAUSTED_KEYWORDS = [\n 'quota_exhausted',\n 'quota exhausted',\n 'quota reached',\n 'enable overages',\n 'individual quota',\n]\n\n/** Classify a 429 body into the four upstream categories. */\nexport function classifyRateLimit(\n bodyText: string | undefined,\n retryAfterMs: number | undefined,\n): RateLimitCategory {\n const text = (bodyText ?? '').toLowerCase()\n if (QUOTA_EXHAUSTED_KEYWORDS.some((keyword) => text.includes(keyword))) {\n return 'quota_exhausted'\n }\n if (retryAfterMs !== undefined && retryAfterMs < 3000) {\n return 'soft_rate_limit'\n }\n if (retryAfterMs !== undefined) {\n return 'rate_limited'\n }\n // No explicit retry hint: \"quota\" wording → daily quota, otherwise unknown.\n return text.includes('quota') || text.includes('resource_exhausted') ? 'quota_exhausted' : 'unknown'\n}\n\nconst RATE_LIMIT_RESET_FIELDS = ['resetTime', 'reset_time', 'resetAt', 'quotaResetTime'] as const\n\nfunction extractResetTime(bodyText: string | undefined): string | undefined {\n if (!bodyText) return undefined\n try {\n const data = JSON.parse(bodyText) as Record<string, unknown>\n for (const field of RATE_LIMIT_RESET_FIELDS) {\n const value = data[field]\n if (typeof value === 'string' && value) return value\n if (typeof value === 'number' && Number.isFinite(value)) {\n // Unix ms when large, seconds when small (relative to now).\n return value > 1_000_000_000_000 ? new Date(value).toISOString() : new Date(Date.now() + value * 1000).toISOString()\n }\n }\n const quotaInfo = data.quotaInfo as Record<string, unknown> | undefined\n if (quotaInfo && typeof quotaInfo.resetTime === 'string') return quotaInfo.resetTime\n } catch {\n // not JSON — no reset info\n }\n return undefined\n}\n\nfunction parseRetryAfter(header: string | null): number | undefined {\n if (!header) return undefined\n const seconds = Number(header)\n if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1000\n const date = Date.parse(header)\n if (!Number.isNaN(date)) return Math.max(0, date - Date.now())\n return undefined\n}\n\n/** Classify a completed HTTP response (non-2xx). */\nexport function classifyHttpError(\n status: number,\n headers: Headers,\n bodyText?: string,\n): ClassifiedError {\n const retryAfterMs = parseRetryAfter(headers.get('retry-after'))\n const resetTime = extractResetTime(bodyText)\n\n if (status === 429) {\n const category = classifyRateLimit(bodyText, retryAfterMs)\n return {\n kind: 'rate-limit',\n rateLimitCategory: category,\n status,\n retryAfterMs,\n resetTime,\n message: bodyText ? bodyText.slice(0, 200) : undefined,\n }\n }\n if (status === 401) {\n return { kind: 'auth-failure', status, message: bodyText ? bodyText.slice(0, 200) : undefined }\n }\n if (status === 403) {\n // Google also reports quota walls as 403 RESOURCE_EXHAUSTED, and the\n // endpoint fallback chain ends on hosts answering 403 for \"no license\"\n // (not bad auth). Only treat a 403 as auth-failure when the body carries\n // no quota wording — a false revoke permanently disables the account.\n const category = classifyRateLimit(bodyText, undefined)\n if (category === 'quota_exhausted') {\n return {\n kind: 'rate-limit',\n rateLimitCategory: category,\n status,\n message: bodyText ? bodyText.slice(0, 200) : undefined,\n }\n }\n return { kind: 'auth-failure', status, message: bodyText ? bodyText.slice(0, 200) : undefined }\n }\n if (status === 404) {\n // Model/route missing — nothing to rotate for; surface as transient request error.\n return { kind: 'transient', status, message: bodyText ? bodyText.slice(0, 200) : undefined }\n }\n if (status >= 500) {\n return { kind: 'transient', status, retryAfterMs, message: bodyText ? bodyText.slice(0, 200) : undefined }\n }\n // 400-range others: request-level errors, no rotation semantics.\n return { kind: 'transient', status, message: bodyText ? bodyText.slice(0, 200) : undefined }\n}\n\n/** Classify a fetch-level failure (DNS, refused, timeout, abort). */\nexport function classifyFetchError(error: unknown): ClassifiedError {\n const message = error instanceof Error ? error.message : String(error)\n if (error instanceof DOMException && error.name === 'AbortError') {\n return { kind: 'network-error', message }\n }\n return { kind: 'network-error', message }\n}\n\n/** Classify the result of a refresh attempt (shared by adapter + verify paths). */\nexport function classifyRefreshFailure(status: number, code?: string): ClassifiedError {\n if (code === 'invalid_grant') return { kind: 'auth-failure', status, message: code }\n if (status === 429) return { kind: 'rate-limit', status, message: code }\n if (status >= 500) return { kind: 'transient', status, message: code }\n return { kind: 'transient', status, message: code }\n}\n\nexport type { FailureKind }\n","/**\n * AgyAdapter: the DSH seam. A thin orchestrator over the deep modules —\n * account session resolution (shell-provided), request translation, SSE\n * parsing, failure classification, and rotation reporting. All wire details\n * live in translate.ts / parse.ts / models.ts.\n */\n\nimport {\n LlmAdapter,\n LlmError,\n ProviderRequestId,\n QUOTA_EXCEEDED_CODE,\n attributionHeaders,\n} from '@deepseek-ai/dsh-llm'\nimport type {\n GenerateOptions,\n LlmModelInfo,\n LlmProviderInfo,\n LlmResolvedModelInfo,\n StreamChunk,\n ToolSchema,\n} from '@deepseek-ai/dsh-llm'\nimport type { FailureKind, ManagedAccount, OAuthAuthDetails } from '../types.ts'\nimport type { RateLimitCategory } from '../runtime/classify.ts'\nimport { fetchAgyFirstOk } from '../oauth/constants.ts'\nimport { classifyFetchError, classifyHttpError } from '../runtime/classify.ts'\nimport { deriveAntigravitySessionId, generateAntigravityRequestId } from '../runtime/identity.ts'\nimport { setThoughtSignature } from '../runtime/signature-cache.ts'\nimport { toAgyRequestBody } from './translate.ts'\nimport { parseAgySse } from './parse.ts'\nimport { AGY_PROVIDER, listAgyModels, resolveAgyModel } from './models.ts'\n\nexport interface AgyAccountSession {\n auth: OAuthAuthDetails\n account: ManagedAccount\n index: number\n /** Fingerprint + randomized impersonation headers for this request. */\n impersonation: {\n 'User-Agent': string\n 'X-Goog-Api-Client': string\n 'Client-Metadata': string\n }\n}\n\nexport interface AgyAdapterOptions {\n /** Resolve the active account (shell refreshes expired tokens and switches on rotation). */\n getSession(): Promise<AgyAccountSession | undefined>\n /** Report a classified upstream failure so the shell can cool/rotate/revoke. */\n reportFailure(\n kind: FailureKind,\n session: AgyAccountSession,\n info?: { retryAfterMs?: number; status?: number; rateLimitCategory?: RateLimitCategory },\n ): Promise<void>\n /** Report a clean stream completion (resets the failure counter). */\n markSuccess?(session: AgyAccountSession): Promise<void>\n}\n\nconst UPSTREAM_ERROR_CODE = 'UPSTREAM'\n\n/** Build the impersonation headers for one request (per-request randomization applied by the shell). */\nexport function buildRequestHeaders(session: AgyAccountSession): Record<string, string> {\n return {\n authorization: `Bearer ${session.auth.access}`,\n 'content-type': 'application/json',\n accept: 'text/event-stream',\n 'x-goog-request-id': generateAntigravityRequestId(),\n ...attributionHeaders(),\n ...session.impersonation,\n }\n}\n\nexport class AgyAdapter extends LlmAdapter {\n private readonly options: AgyAdapterOptions\n\n constructor(options: AgyAdapterOptions) {\n super()\n this.options = options\n }\n\n override providerInfo(_provider: string): LlmProviderInfo {\n return { id: AGY_PROVIDER, name: 'Antigravity (agy)' }\n }\n\n override async listModels(_provider: string): Promise<readonly LlmModelInfo[]> {\n const session = await this.options.getSession()\n return listAgyModels(session?.auth.access, session?.account.projectId)\n }\n\n override async resolveModel(provider: string, model: string): Promise<LlmResolvedModelInfo> {\n return resolveAgyModel(provider, model)\n }\n\n override async *stream(options: GenerateOptions): AsyncIterable<StreamChunk> {\n const session = await this.options.getSession()\n if (!session) {\n throw new LlmError(\n 'No agy account configured — run `dsh-agy login` to authenticate.',\n 'NO_CREDENTIAL',\n )\n }\n\n const body = toAgyRequestBody(options, {\n projectId: session.account.projectId,\n sessionId: deriveAntigravitySessionId(session.account.email) ?? undefined,\n })\n const headers = buildRequestHeaders(session)\n\n let response: Response\n try {\n response = await fetchAgyFirstOk('/v1internal:streamGenerateContent?alt=sse', {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n signal: options.signal,\n })\n } catch (error) {\n const classified = classifyFetchError(error)\n await this.options.reportFailure(classified.kind, session)\n throw new LlmError(classified.message ?? 'agy fetch failed', 'TRANSPORT', { cause: error })\n }\n\n if (!response.ok) {\n const bodyText = await response.text().catch(() => undefined)\n const classified = classifyHttpError(response.status, response.headers, bodyText)\n await this.options.reportFailure(classified.kind, session, {\n retryAfterMs: classified.retryAfterMs,\n status: response.status,\n rateLimitCategory: classified.rateLimitCategory,\n })\n if (classified.kind === 'rate-limit') {\n // soft/rate limits are retryable by the harness (RATE_LIMIT + delay);\n // daily quota exhaustion is terminal (QUOTA, 24h cooldown already set).\n if (classified.rateLimitCategory === 'quota_exhausted') {\n throw new LlmError(\n `agy daily quota exhausted (${response.status}): ${classified.message ?? ''}`,\n QUOTA_EXCEEDED_CODE,\n )\n }\n throw new LlmError(\n `agy rate-limited (${response.status}): ${classified.message ?? ''}`,\n 'RATE_LIMIT',\n {\n providerRetryAfterMs: classified.retryAfterMs ?? undefined,\n requestId: ProviderRequestId(generateAntigravityRequestId()),\n },\n )\n }\n if (classified.kind === 'auth-failure') {\n throw new LlmError(\n `agy authentication failed (${response.status}) — run \\`dsh-agy login\\``,\n 'INVALID_CREDENTIAL',\n )\n }\n throw new LlmError(\n `agy upstream error (${response.status}): ${classified.message ?? ''}`,\n UPSTREAM_ERROR_CODE,\n )\n }\n\n if (!response.body) {\n throw new LlmError('agy stream returned no body', UPSTREAM_ERROR_CODE)\n }\n\n try {\n yield* parseAgySse(response.body, {\n signal: options.signal,\n onToolSignature: (toolCallId, signature) => {\n setThoughtSignature(toolCallId, signature)\n },\n })\n await this.options.markSuccess?.(session)\n } catch (error) {\n if (error instanceof DOMException && error.name === 'AbortError') {\n throw new LlmError('agy stream aborted', 'ABORTED', { cause: error })\n }\n await this.options.reportFailure('network-error', session)\n throw new LlmError(\n error instanceof Error ? error.message : 'agy stream parse failed',\n UPSTREAM_ERROR_CODE,\n { cause: error },\n )\n }\n }\n}\n\nexport type { ToolSchema }\n","/**\n * Shared runtime construction for the in-harness plugin entries: master-key\n * codec resolution (credentials seam first, credentials document fallback),\n * account store, session manager, and adapter. Used by the main plugin\n * (adapter registration) and the web plugin (route registration) so both\n * entries operate on the same store.\n */\n\nimport { randomBytes } from 'node:crypto'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { AgyAdapter } from './adapter/adapter.ts'\nimport { AGY_PROVIDER } from './adapter/models.ts'\nimport { resolveAntigravityVersion } from './runtime/version.ts'\nimport { AgySessionManager } from './session.ts'\nimport { JsonAccountStore } from './store/accounts.ts'\nimport type { AccountStore } from './store/accounts.ts'\nimport {\n MASTER_KEY_REF,\n createAesGcmCodec,\n deriveKey,\n loadMasterKey,\n persistMasterKey,\n resolveDshHome,\n resolveMasterKeyCodec,\n} from './store/keyring.ts'\nimport type { SecretCodec } from './store/keyring.ts'\n\nexport interface CredentialsSeam {\n resolve(ref: string): Promise<{ value: string } | undefined>\n set(ref: string, value: string): Promise<void>\n}\n\nfunction codecFrom(masterKey: string): SecretCodec {\n return createAesGcmCodec(deriveKey(masterKey))\n}\n\n/** Resolve or create the master key, preferring the credentials seam when available. */\nexport async function resolveCodec(ctx: Context): Promise<{ codec: SecretCodec; created: boolean }> {\n const dshHome = resolveDshHome()\n const credentials = ctx.get('credentials') as CredentialsSeam | undefined\n\n if (credentials) {\n const resolved = await credentials.resolve(MASTER_KEY_REF)\n if (resolved) return { codec: codecFrom(resolved.value), created: false }\n const fileKey = loadMasterKey(dshHome)\n if (fileKey) return { codec: codecFrom(fileKey), created: false }\n const fresh = randomBytes(32).toString('hex')\n try {\n await credentials.set(MASTER_KEY_REF, fresh)\n return { codec: codecFrom(fresh), created: true }\n } catch {\n // Read-only shadowing: persist to the credentials document directly.\n persistMasterKey(dshHome, fresh)\n return { codec: codecFrom(fresh), created: true }\n }\n }\n\n return resolveMasterKeyCodec(dshHome)\n}\n\n/** Build the store, session manager, and adapter for one plugin entry. */\nexport async function createAgyRuntime(ctx: Context): Promise<{\n store: AccountStore\n sessions: AgySessionManager\n adapter: AgyAdapter\n}> {\n // Warm the version cache (non-blocking) so fingerprint generation inside\n // the rate-limit path never waits on a cold feed.\n void resolveAntigravityVersion().catch(() => {})\n const { codec } = await resolveCodec(ctx)\n const dshHome = resolveDshHome()\n const store = new JsonAccountStore({ file: `${dshHome}/agy-accounts.json`, codec })\n const sessions = new AgySessionManager({ store })\n const adapter = new AgyAdapter({\n getSession: () => sessions.getSession(),\n reportFailure: (kind, session, info) => sessions.reportFailure(kind, session, info),\n markSuccess: (session) => sessions.markSuccess(session),\n })\n return { store, sessions, adapter }\n}\n\nexport { AGY_PROVIDER }\n"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __defProp = Object.defineProperty;
3
+ var __exportAll = (all, no_symbols) => {
4
+ let target = {};
5
+ for (var name in all) __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true
8
+ });
9
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
10
+ return target;
11
+ };
12
+ //#endregion
13
+ export { __exportAll as t };