pi-provider-antigravity 0.5.0 → 0.6.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.
package/README.md CHANGED
@@ -95,7 +95,7 @@ Remaining percentages are automatically colorized to keep depletion visible with
95
95
  - **Low** (≤ 30%): Yellow
96
96
  - **Critical** (≤ 10%): Red
97
97
 
98
- In `both` mode, each window carries its own color based on its remaining percentage.
98
+ In `all` mode, each window carries its own color based on its remaining percentage.
99
99
 
100
100
  ### Display Modes
101
101
 
@@ -104,13 +104,27 @@ Switch between display modes at any time using `/antigravity settings`:
104
104
  | Mode | Example | Behavior |
105
105
  |---|---|---|
106
106
  | `off` *(default)* | *(hidden)* | Slot stays empty; zero background quota requests |
107
- | `single` | `Wk 6% (2d 14h)` | Shows only the primary bottleneck window |
108
- | `both` | `5h 90% (3h 54m) · Wk 6% (2d 14h)` | Shows both 5-hour and weekly windows side-by-side |
107
+ | `smart` | `Wk 6% (2d 14h)` | Shows the window that runs out first, 5h or weekly |
108
+ | `all` | `5h 90% (3h 54m) · Wk 6% (2d 14h)` | Shows every window of the pool backing the current model |
109
+
110
+ ### Footer placement
111
+
112
+ The extension never calls `setFooter` — it only publishes the slot text under the key `pi-provider-antigravity-footer-usage` via `setStatus`. Where that text renders is up to your footer; Pi's built-in one gives it its own line, below the default two.
113
+
114
+ To move it elsewhere (the right side of the first line, for example), ask Pi in a session and name the key:
115
+
116
+ > Move the `pi-provider-antigravity-footer-usage` slot to the right side of the first footer line, keeping the rest of the footer as it is.
117
+
118
+ Pi edits your own footer extension, creating one if you have none — based on
119
+ Pi's [complete custom-footer
120
+ example](https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/examples/extensions/custom-footer.ts),
121
+ which Pi maintains. A custom footer replaces the built-in one, so Pi has to
122
+ redraw whichever lines you want to keep.
109
123
 
110
124
  ### Under the Hood
111
125
 
112
126
  - **Model-aware pool routing**: Automatically aligns with your active model. Selecting a Gemini model displays the Gemini quota pool; switching to Claude or GPT switches to the third-party pool. When using a non-Antigravity model or no model at all, the slot cleanly disappears.
113
- - **Intelligent bottleneck detection**: In `single` mode, the extension determines urgency using `min(r5h, rWk × R)` rather than a naive percentage comparison, accounting for total volume differences between the 5-hour and weekly pools. The volume multiplier `R` starts at 6.0 and automatically self-calibrates between 1.0 and 20.0 based on real usage deltas, persisting across restarts.
127
+ - **Bottleneck detection**: In `smart` mode, the extension determines urgency using `min(r5h, rWk × R)` rather than a naive percentage comparison, accounting for total volume differences between the 5-hour and weekly pools. The volume multiplier `R` starts at 6.0 and automatically self-calibrates between 1.0 and 20.0 based on real usage deltas, persisting across restarts.
114
128
  - **Battery- and network-friendly caching**: Idle sessions perform zero network requests. The slot repaints instantly from memory on turn completion, session launch, and model changes. Upstream quota queries are throttled to at most once every 5 minutes. Running `/antigravity usage` refreshes upstream data immediately.
115
129
 
116
130
  ## Configuration
@@ -136,7 +150,7 @@ Configuration is stored in `pi-provider-antigravity.json` alongside Pi's `settin
136
150
  ```json
137
151
  {
138
152
  "settings": {
139
- "quotaFooter": "single"
153
+ "quotaFooter": "smart"
140
154
  }
141
155
  }
142
156
  ```
@@ -147,7 +161,7 @@ Configuration is stored in `pi-provider-antigravity.json` alongside Pi's `settin
147
161
 
148
162
  | Key | Values | Default | Description |
149
163
  |---|---|---|---|
150
- | `quotaFooter` | `off`, `single`, `both` | `off` | Display remaining quota in Pi's status footer. See [Quota footer](#quota-footer). |
164
+ | `quotaFooter` | `off`, `smart`, `all` | `off` | Display remaining quota in Pi's status footer. See [Quota footer](#quota-footer). |
151
165
 
152
166
  ## Disclaimer
153
167
 
package/dist/index.js CHANGED
@@ -525,6 +525,14 @@ async function refreshCatalog(context) {
525
525
  return context.stored?.models || [];
526
526
  }
527
527
  }
528
+ async function refreshCatalogGeneration(doRefresh) {
529
+ const before = getCatalogSnapshot().version;
530
+ await doRefresh();
531
+ const catalog = getStoredCatalog();
532
+ if (!catalog) return { status: "failed" };
533
+ if (getCatalogSnapshot().version === before) return { status: "stale", catalog };
534
+ return { status: "fresh", catalog };
535
+ }
528
536
  function parseAvailableModels(data) {
529
537
  const models = [];
530
538
  const modelEnums = {};
@@ -770,7 +778,7 @@ function colorizeQuotaFooterBoth(footer) {
770
778
  if (!footer) return void 0;
771
779
  return footer.split(" \xB7 ").map((part) => colorizeQuotaFooter(part) ?? part).join(" \xB7 ");
772
780
  }
773
- var QUOTA_STATUS_KEY = "antigravity_quota";
781
+ var QUOTA_STATUS_KEY = "pi-provider-antigravity-footer-usage";
774
782
  var QUOTA_REFRESH_INTERVAL_MS = 5 * 60 * 1e3;
775
783
  var MAX_OBSERVATION_AGE_MS = 60 * 60 * 1e3;
776
784
  function isFiniteNumber(value) {
@@ -799,7 +807,7 @@ function paintQuotaStatus(coord, ctx) {
799
807
  const plain = coord.footerFor(ctx.model?.id, mode);
800
808
  ctx.ui.setStatus(
801
809
  QUOTA_STATUS_KEY,
802
- mode === "both" ? colorizeQuotaFooterBoth(plain) : colorizeQuotaFooter(plain)
810
+ mode === "all" ? colorizeQuotaFooterBoth(plain) : colorizeQuotaFooter(plain)
803
811
  );
804
812
  }
805
813
  var QuotaStatusCoordinator = class {
@@ -821,9 +829,9 @@ var QuotaStatusCoordinator = class {
821
829
  mode() {
822
830
  return this.store.loadMode();
823
831
  }
824
- footerFor(modelId, mode = "single") {
832
+ footerFor(modelId, mode = "smart") {
825
833
  if (!this.summary || mode === "off") return void 0;
826
- return mode === "both" ? buildQuotaFooterBoth(this.summary, modelId) : buildQuotaFooter(this.summary, modelId, this.weeklyTo5hRatio);
834
+ return mode === "all" ? buildQuotaFooterBoth(this.summary, modelId) : buildQuotaFooter(this.summary, modelId, this.weeklyTo5hRatio);
827
835
  }
828
836
  get isFresh() {
829
837
  return !!this.summary && Date.now() - this.fetchedAt < QUOTA_REFRESH_INTERVAL_MS;
@@ -844,12 +852,13 @@ var QuotaStatusCoordinator = class {
844
852
  await this.refresh(ctx);
845
853
  paintQuotaStatus(this, ctx);
846
854
  }
847
- // One fetch for preview purposes even when the footer slot is off: opening
848
- // quota settings is an explicit look at quota.
855
+ // One fetch for preview purposes even when the footer slot is off or the
856
+ // current model belongs to another provider: opening quota settings is an
857
+ // explicit look at quota (like /antigravity usage), and the preview is the
858
+ // only place that quota is visible while a foreign model is selected.
849
859
  ensurePreview(ctx) {
850
860
  if (this.summary) return Promise.resolve(this.summary);
851
861
  if (this.inflight) return this.inflight;
852
- if (!isAntigravityModel(ctx.model)) return Promise.resolve(void 0);
853
862
  return this.fetchOnce(ctx);
854
863
  }
855
864
  fetchOnce(ctx, signal) {
@@ -940,7 +949,7 @@ function saveProviderConfig(file, data) {
940
949
  }
941
950
  function normalizeFooterMode(value) {
942
951
  const v = typeof value === "string" ? value.trim().toLowerCase() : "";
943
- return v === "off" || v === "single" || v === "both" ? v : void 0;
952
+ return v === "off" || v === "smart" || v === "all" ? v : void 0;
944
953
  }
945
954
  function resolveFooterMode(config) {
946
955
  return normalizeFooterMode(config?.settings?.quotaFooter) ?? "off";
@@ -980,13 +989,16 @@ var SETTINGS_FIELDS = Object.freeze([
980
989
  key: "quotaFooter",
981
990
  label: "Quota footer",
982
991
  description: "Show remaining quota in the footer",
983
- options: ["off", "single", "both"],
992
+ options: ["off", "smart", "all"],
984
993
  defaultValue: "off",
994
+ optionNotes: {
995
+ smart: "Picks whichever window runs out first (5h or weekly), weighting the weekly pool by a ratio learned from your usage",
996
+ all: "Lists every window of the pool backing the current model (5h or weekly)"
997
+ },
985
998
  onChange: async (ctx, quotaStatus) => {
986
- if (quotaStatus) {
987
- await quotaStatus.refresh(ctx);
988
- paintQuotaStatus(quotaStatus, ctx);
989
- }
999
+ if (!quotaStatus) return;
1000
+ if (quotaStatus.mode() !== "off") await quotaStatus.refresh(ctx, { ignoreMode: true });
1001
+ paintQuotaStatus(quotaStatus, ctx);
990
1002
  }
991
1003
  }
992
1004
  ]);
@@ -1009,9 +1021,10 @@ async function applySettingValue(field, value, file, ctx, quotaStatus) {
1009
1021
  }
1010
1022
  function previewQuotaFooterText(coord, modelId, mode) {
1011
1023
  if (!coord || mode === "off") return void 0;
1012
- const plain = coord.footerFor(modelId, mode === "both" ? "both" : "single");
1024
+ const plain = coord.footerFor(modelId, mode === "all" ? "all" : "smart");
1013
1025
  if (!plain) return void 0;
1014
- return mode === "both" ? colorizeQuotaFooterBoth(plain) : colorizeQuotaFooter(plain);
1026
+ const colored = mode === "all" ? colorizeQuotaFooterBoth(plain) : colorizeQuotaFooter(plain);
1027
+ return ANSI_FG_RESET + colored;
1015
1028
  }
1016
1029
  function buildSettingsItems(config, fields = SETTINGS_FIELDS) {
1017
1030
  return fields.map((f) => ({
@@ -1093,7 +1106,10 @@ async function openSettingsDialog(ctx, quotaStatus, file) {
1093
1106
  if (!item) return;
1094
1107
  const base = quotaField?.description ?? "";
1095
1108
  const sample = mode === "off" ? "hidden" : previewQuotaFooterText(quotaStatus, ctx.model?.id, mode);
1096
- item.description = sample && base ? `${base}: ${sample}` : sample ?? base;
1109
+ const head = sample && base ? `${base}: ${sample}` : sample ?? base;
1110
+ const note = quotaField?.optionNotes?.[mode];
1111
+ item.description = note ? `${head}
1112
+ ${note}` : head;
1097
1113
  };
1098
1114
  paintPreview(resolveFooterMode(config));
1099
1115
  const list = new SettingsList(
@@ -1284,6 +1300,7 @@ function createSseFeed() {
1284
1300
  // src/builder.ts
1285
1301
  import { createHash as createHash2, randomUUID } from "node:crypto";
1286
1302
  var BASE64_SIGNATURE_PATTERN = /^[A-Za-z0-9+/_-]+={0,2}$/;
1303
+ var SKIP_THOUGHT_SIGNATURE_VALIDATOR = "skip_thought_signature_validator";
1287
1304
  function isValidThoughtSignature(sig) {
1288
1305
  if (typeof sig !== "string" || sig.length === 0) return false;
1289
1306
  if (sig.includes("=") && sig.length % 4 !== 0) return false;
@@ -1315,12 +1332,12 @@ function resolveSessionTrajectory(sessionId) {
1315
1332
  const hex = [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
1316
1333
  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
1317
1334
  }
1335
+ var AGY_SESSION_ID = "-3750763034362895579";
1318
1336
  function resolveNumericSessionId(sessionId) {
1319
1337
  if (/^-?\d+$/.test(sessionId)) {
1320
1338
  return sessionId;
1321
1339
  }
1322
- const hash = createHash2("sha256").update(`antigravity:session:${sessionId}`).digest();
1323
- return hash.readBigInt64BE(0).toString();
1340
+ return AGY_SESSION_ID;
1324
1341
  }
1325
1342
  function normalizeToolCallId(id) {
1326
1343
  if (!id || typeof id !== "string") return id;
@@ -1393,6 +1410,7 @@ function convertTools(tools) {
1393
1410
  function translateTurnTrace(context, runtimeModelId) {
1394
1411
  const contents = [];
1395
1412
  const messages = context.messages || [];
1413
+ const isGeminiRequest = classifyModelFamily(runtimeModelId) === "gemini";
1396
1414
  for (const msg of messages) {
1397
1415
  if (msg.role === "user") {
1398
1416
  const parts = [];
@@ -1486,6 +1504,8 @@ function translateTurnTrace(context, runtimeModelId) {
1486
1504
  if (sig) {
1487
1505
  part.thoughtSignature = sig;
1488
1506
  pendingThinkingSig = void 0;
1507
+ } else if (isGeminiRequest && !isSameProviderAndModel) {
1508
+ part.thoughtSignature = SKIP_THOUGHT_SIGNATURE_VALIDATOR;
1489
1509
  }
1490
1510
  parts.push(part);
1491
1511
  }
@@ -1752,17 +1772,17 @@ async function runModelsSubcommand(ctx) {
1752
1772
  }
1753
1773
  try {
1754
1774
  if (ctx.hasUI) ctx.ui.notify("Fetching available models\u2026", "info");
1755
- const before = getCatalogSnapshot().version;
1756
- await ctx.modelRegistry?.refresh?.({ force: true, providers: [PROVIDER_ID], signal: ctx.signal });
1757
- const catalog = getStoredCatalog();
1758
- if (!catalog || getCatalogSnapshot().version === before) {
1759
- if (catalog) {
1760
- emitOutput(ctx, "Failed to refresh models, showing last known list.", "warning");
1761
- emitOutput(ctx, formatModelsList(catalog));
1762
- return;
1763
- }
1775
+ const { status, catalog } = await refreshCatalogGeneration(
1776
+ () => ctx.modelRegistry?.refresh?.({ force: true, providers: [PROVIDER_ID], signal: ctx.signal })
1777
+ );
1778
+ if (status === "failed" || !catalog) {
1764
1779
  throw new Error("no new generation received");
1765
1780
  }
1781
+ if (status === "stale") {
1782
+ emitOutput(ctx, "Failed to refresh models, showing last known list.", "warning");
1783
+ emitOutput(ctx, formatModelsList(catalog));
1784
+ return;
1785
+ }
1766
1786
  emitOutput(ctx, formatModelsList(catalog));
1767
1787
  } catch (err) {
1768
1788
  emitOutput(ctx, `Failed to fetch models: ${err.message}`, "error");
@@ -1814,8 +1834,16 @@ async function runAntigravitySubcommand(args, ctx, quotaStatus) {
1814
1834
  if (sub === "refresh") {
1815
1835
  try {
1816
1836
  if (ctx.hasUI) ctx.ui.notify("Refreshing models\u2026", "info");
1817
- await ctx.modelRegistry?.refresh?.({ force: true, providers: [PROVIDER_ID], signal: ctx.signal });
1818
- emitOutput(ctx, "Antigravity models refreshed successfully.");
1837
+ const { status } = await refreshCatalogGeneration(
1838
+ () => ctx.modelRegistry?.refresh?.({ force: true, providers: [PROVIDER_ID], signal: ctx.signal })
1839
+ );
1840
+ if (status === "fresh") {
1841
+ emitOutput(ctx, "Antigravity models refreshed successfully.");
1842
+ } else if (status === "stale") {
1843
+ emitOutput(ctx, "Failed to refresh models, keeping last known list.", "warning");
1844
+ } else {
1845
+ emitOutput(ctx, "Failed to refresh models: no new generation received.", "error");
1846
+ }
1819
1847
  } catch (err) {
1820
1848
  emitOutput(ctx, `Failed to refresh: ${err.message}`, "error");
1821
1849
  }
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/auth.ts", "../src/protocol.ts", "../src/model-catalog.ts", "../src/catalog-refresh.ts", "../src/quota-status.ts", "../src/settings.ts", "../src/stream.ts", "../src/parser.ts", "../src/builder.ts", "../src/commands.ts", "../src/index.ts"],
4
- "sourcesContent": ["import { createHash, randomBytes } from \"node:crypto\";\nimport type { OAuthCredentials, OAuthLoginCallbacks } from \"@earendil-works/pi-ai\";\nimport { postAntigravity } from \"./protocol.ts\";\n\nexport const REDIRECT_URI = \"https://antigravity.google/oauth-callback\";\nexport const AUTH_URL = \"https://accounts.google.com/o/oauth2/auth\";\nexport const TOKEN_URL = \"https://oauth2.googleapis.com/token\";\nexport const OAUTH_CALLBACK_TIMEOUT_MS = 5 * 60 * 1000;\nexport const FALLBACK_PROJECT_ID = \"aicode-consumers\";\n\nexport const SCOPES = [\n \"https://www.googleapis.com/auth/cloud-platform\",\n \"https://www.googleapis.com/auth/userinfo.email\",\n \"https://www.googleapis.com/auth/userinfo.profile\",\n \"https://www.googleapis.com/auth/cclog\",\n \"https://www.googleapis.com/auth/experimentsandconfigs\",\n \"https://www.googleapis.com/auth/aicode\",\n \"openid\",\n];\n\nexport const CLIENT_ID =\n process.env.ANTIGRAVITY_CLIENT_ID ||\n Buffer.from(\n \"MTA3MTAwNjA2MDU5MS10bWhzc2luMmgyMWxjcmUyMzV2dG9sb2poNGc0MDNlcC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbQ==\",\n \"base64\"\n ).toString(\"utf-8\");\n\nexport const CLIENT_SECRET =\n process.env.ANTIGRAVITY_CLIENT_SECRET ||\n Buffer.from(\"R09DU1BYLUs1OEZXUjQ4NkxkTEoxbUxCOHNYQzR6NnFEQWY=\", \"base64\").toString(\"utf-8\");\n\nfunction base64Url(buf: Buffer): string {\n return buf.toString(\"base64url\");\n}\n\nexport function extractCodeFromInput(rawInput: string): string {\n let code = rawInput.trim();\n if (!code) {\n throw new Error(\"No authorization code provided\");\n }\n\n if (code.startsWith(\"code=\")) {\n code = code.slice(5).trim();\n if (!code) {\n throw new Error(\"No authorization code provided\");\n }\n }\n\n // Reject callback URLs and other non-code input here instead of passing\n // garbage downstream (Google rejects those with a bare invalid_grant).\n // Note: real codes contain \"/\" (e.g. \"4/0ATs\u2026\"), so only reject\n // characters a code can never contain.\n if (/[\\s:?#&=%+]/.test(code)) {\n throw new Error(\"Invalid authorization code: paste the code value itself, not the callback URL\");\n }\n\n return code;\n}\n\nfunction generatePKCE(): { verifier: string; challenge: string } {\n const verifier = base64Url(randomBytes(32));\n const challenge = base64Url(createHash(\"sha256\").update(verifier).digest());\n return { verifier, challenge };\n}\n\nexport async function fetchProjectId(token: string, signal?: AbortSignal): Promise<string> {\n try {\n const res = await postAntigravity({\n token,\n path: \"v1internal:loadCodeAssist\",\n body: { metadata: { ideType: \"ANTIGRAVITY\" } },\n signal,\n });\n if (res.ok) {\n const data = (await res.json()) as { cloudaicompanionProject?: string };\n if (data.cloudaicompanionProject) {\n return data.cloudaicompanionProject;\n }\n }\n } catch {\n // Fall back to standard project\n }\n return FALLBACK_PROJECT_ID;\n}\n\nexport async function loginAntigravity(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n const { verifier, challenge } = generatePKCE();\n // state is still sent for agy wire parity, but never validated back:\n // with code-only paste the callback URL (and its state) never reaches us.\n const state = randomBytes(16).toString(\"hex\");\n\n const authParams = new URLSearchParams({\n client_id: CLIENT_ID,\n response_type: \"code\",\n redirect_uri: REDIRECT_URI,\n scope: SCOPES.join(\" \"),\n code_challenge: challenge,\n code_challenge_method: \"S256\",\n state,\n access_type: \"offline\",\n prompt: \"consent\",\n });\n\n const authUrl = `${AUTH_URL}?${authParams.toString()}`;\n callbacks.onAuth({ url: authUrl });\n\n const manualPromptPromise = (async () => {\n const input = await callbacks.onPrompt({\n message: \"Paste authorization code from browser:\",\n });\n return extractCodeFromInput(input);\n })();\n\n const timeout = new Promise<never>((_, rej) =>\n setTimeout(() => rej(new Error(\"Timed out waiting for authorization code (5 minutes)\")), OAUTH_CALLBACK_TIMEOUT_MS)\n );\n\n const code = await Promise.race([manualPromptPromise, timeout]);\n\n const tokenParams = new URLSearchParams({\n client_id: CLIENT_ID,\n client_secret: CLIENT_SECRET,\n code,\n grant_type: \"authorization_code\",\n redirect_uri: REDIRECT_URI,\n code_verifier: verifier,\n });\n\n const tokenRes = await fetch(TOKEN_URL, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: tokenParams.toString(),\n });\n\n if (!tokenRes.ok) {\n const errText = await tokenRes.text();\n throw new Error(`Failed to exchange token (${tokenRes.status}): ${errText}`);\n }\n\n const tokens = (await tokenRes.json()) as {\n access_token: string;\n refresh_token?: string;\n expires_in?: number;\n };\n const projectId = await fetchProjectId(tokens.access_token);\n\n return {\n refresh: tokens.refresh_token || \"\",\n access: JSON.stringify({ token: tokens.access_token, projectId }),\n expires: Date.now() + (tokens.expires_in || 3600) * 1000,\n };\n}\n\nexport async function refreshAntigravityToken(\n credentials: OAuthCredentials,\n signal?: AbortSignal\n): Promise<OAuthCredentials> {\n const params = new URLSearchParams({\n client_id: CLIENT_ID,\n client_secret: CLIENT_SECRET,\n refresh_token: credentials.refresh,\n grant_type: \"refresh_token\",\n });\n\n const res = await fetch(TOKEN_URL, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: params.toString(),\n signal,\n });\n\n if (!res.ok) {\n const errText = await res.text();\n throw new Error(`Failed to refresh token (${res.status}): ${errText}`);\n }\n\n const tokens = (await res.json()) as {\n access_token: string;\n refresh_token?: string;\n expires_in?: number;\n };\n\n let existingProjectId = FALLBACK_PROJECT_ID;\n try {\n const parsed = JSON.parse(credentials.access);\n if (parsed.projectId) existingProjectId = parsed.projectId;\n } catch {\n // ignore\n }\n\n // Re-resolve: a login-time lookup failure pins the \"aicode-consumers\"\n // default forever unless refresh retries it. A failed lookup yields that\n // same default, which must never clobber a known value.\n const freshProjectId = await fetchProjectId(tokens.access_token, signal);\n const projectId = freshProjectId !== FALLBACK_PROJECT_ID ? freshProjectId : existingProjectId;\n\n return {\n refresh: tokens.refresh_token || credentials.refresh,\n access: JSON.stringify({ token: tokens.access_token, projectId }),\n expires: Date.now() + (tokens.expires_in || 3600) * 1000,\n };\n}\n\nexport function getApiKey(credentials: OAuthCredentials): string {\n return credentials.access;\n}\n\nexport function parseStoredCredentials(rawApiKey: string): { token: string; projectId: string } {\n try {\n const parsed = JSON.parse(rawApiKey);\n if (parsed.token) {\n return { token: parsed.token, projectId: parsed.projectId || FALLBACK_PROJECT_ID };\n }\n } catch {\n // If plain token\n }\n return { token: rawApiKey, projectId: FALLBACK_PROJECT_ID };\n}\n", "export const DEFAULT_ENDPOINT = \"https://daily-cloudcode-pa.googleapis.com\";\nexport const DEFAULT_USER_AGENT =\n \"antigravity/cli/1.1.28 (aidev_client; os_type=linux; arch=amd64; cl=978129418; auth_method=consumer)\";\n\nexport const PROVIDER_ID = \"antigravity\";\n\nexport interface AntigravityPostParams {\n token: string;\n /** Path below the endpoint, e.g. \"v1internal:retrieveUserQuotaSummary\". */\n path: string;\n body: unknown;\n signal?: AbortSignal;\n}\n\n/**\n * Owns the Antigravity base POST: endpoint, Wire Fingerprint headers, and\n * JSON envelope serialization. Callers pass path + body only.\n * Returns the raw Response: status/body reading stays with callers\n * (streaming needs the body reader; error texts differ per call site).\n */\nexport async function postAntigravity({\n token,\n path,\n body,\n signal,\n}: AntigravityPostParams): Promise<Response> {\n return fetch(`${DEFAULT_ENDPOINT}/${path}`, {\n method: \"POST\",\n headers: buildAntigravityHeaders(token),\n body: JSON.stringify(body),\n signal,\n });\n}\n\n/**\n * Builds the canonical HTTP request headers matching the official agy CLI Wire Fingerprint.\n */\nexport function buildAntigravityHeaders(token: string, userAgent = DEFAULT_USER_AGENT): Record<string, string> {\n return {\n Host: \"daily-cloudcode-pa.googleapis.com\",\n \"User-Agent\": userAgent,\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n };\n}\n", "import type { Model } from \"@earendil-works/pi-ai\";\nimport { DEFAULT_ENDPOINT, PROVIDER_ID } from \"./protocol.ts\";\n\n/**\n * Model Catalog map (pure, in-process): Public Model ID \u2194 Runtime Model ID\n * mapping, Model Plan resolution, the versioned snapshot store, Pi Model\n * synthesis, and presentation (display names, Subcommand table format).\n * No network, no credentials, no persistence \u2014 the wire side\n * (fetch/parse/publish) lives in catalog-refresh.ts and talks to this\n * module only through ingestCatalog (writes) and the snapshot accessors\n * (reads), so one ingested generation is the single freshness truth.\n */\nexport function extractBaseModelId(runtimeId: string): string {\n if (runtimeId === \"gemini-pro-agent\") return \"gemini-3.1-pro\";\n if (runtimeId.startsWith(\"gemini-3.1-pro-\")) return \"gemini-3.1-pro\";\n\n return runtimeId.replace(/-(?:high|medium|low|tiered|thinking|agent|extra-low)$/, \"\");\n}\n\n/**\n * Checks Model Family compatibility for thoughtSignature replay.\n * Official agy CLI wire captures demonstrate that:\n * - Gemini models (gemini-3.7, gemini-3.8, etc.) share thoughtSignatures seamlessly.\n * - Claude models replay thoughtSignatures within the Claude family, part-split\n * like Gemini (1.1.27 stream_turn8/9 counter-capture).\n * - Non-Gemini models (Claude, GPT-OSS) do NOT share signatures with Gemini models.\n */\nexport type ModelFamily = \"gemini\" | \"claude\" | \"gpt\" | \"unknown\";\n\n/**\n * The single model-identity predicate (see CONTEXT.md Model Family): which\n * family a model ID belongs to. Accepts any ID space \u2014 Public or Runtime\n * Model ID, with or without a `provider/` prefix \u2014 by stripping the prefix\n * before the prefix match, so every caller classifies identically. Unknown\n * (including missing) IDs report \"unknown\"; mapping that onto a Quota Pool\n * or plan flags stays with the consumer, preserving each caller's default.\n */\nexport function classifyModelFamily(modelId?: string): ModelFamily {\n const bare = ((modelId || \"\").split(\"/\").pop() || \"\").toLowerCase();\n if (bare.startsWith(\"gemini-\")) return \"gemini\";\n if (bare.startsWith(\"claude-\")) return \"claude\";\n if (bare.startsWith(\"gpt-\")) return \"gpt\";\n return \"unknown\";\n}\n\nexport function isCompatibleFamily(msgModel?: string, targetModelId?: string): boolean {\n if (!msgModel || !targetModelId) return true;\n if (msgModel === targetModelId) return true;\n\n const msgFamily = classifyModelFamily(msgModel);\n if (msgFamily !== \"unknown\" && msgFamily === classifyModelFamily(targetModelId)) return true;\n\n return extractBaseModelId(msgModel) === extractBaseModelId(targetModelId);\n}\n\nexport function getThinkingConfig(modelId: string, effort?: string): { includeThoughts: boolean; thinkingBudget: number } {\n const isOff = effort === \"off\" || effort === \"none\";\n if (isOff) {\n return { includeThoughts: false, thinkingBudget: 0 };\n }\n\n // Gemini 3.8 / 3.7 / 3.6 Flash\n if (modelId.includes(\"flash\")) {\n if (effort === \"high\" || effort === \"xhigh\" || modelId.endsWith(\"-high\") || modelId.endsWith(\"-agent\")) {\n return { includeThoughts: true, thinkingBudget: -1 };\n }\n if (effort === \"medium\" || modelId.endsWith(\"-medium\")) {\n return { includeThoughts: true, thinkingBudget: 4000 };\n }\n if (effort === \"low\" || effort === \"minimal\" || modelId.endsWith(\"-low\")) {\n return { includeThoughts: true, thinkingBudget: 1000 };\n }\n return { includeThoughts: true, thinkingBudget: -1 };\n }\n\n // Gemini 3.1 Pro\n if (modelId.includes(\"pro\")) {\n if (effort === \"low\" || effort === \"minimal\" || modelId.endsWith(\"-low\")) {\n return { includeThoughts: true, thinkingBudget: 1001 };\n }\n return { includeThoughts: true, thinkingBudget: 10001 };\n }\n\n // Claude models\n if (modelId.startsWith(\"claude-\")) {\n return { includeThoughts: true, thinkingBudget: 1024 };\n }\n\n // GPT-OSS 120B\n if (modelId.startsWith(\"gpt-oss-\")) {\n return { includeThoughts: true, thinkingBudget: 8192 };\n }\n\n return { includeThoughts: true, thinkingBudget: -1 };\n}\n\nexport interface AvailableModelItem {\n id: string;\n displayName?: string;\n modelEnum?: string;\n remainingFraction?: number;\n resetTime?: string;\n supportsThinking?: boolean;\n thinkingBudget?: number;\n minThinkingBudget?: number;\n supportsImages?: boolean;\n maxTokens?: number;\n maxOutputTokens?: number;\n}\n\nexport interface AvailableModelsCatalog {\n models: AvailableModelItem[];\n modelEnums: Record<string, string>;\n agentModelSorts?: string[];\n deprecated?: Record<string, string>;\n}\n\n// Active catalog state: single versioned snapshot. refreshCatalog owns writes;\n// request paths take one snapshot per call so enums, runtime IDs, and thinking\n// configs always come from the same refresh generation (see Model Plan in CONTEXT.md).\nexport interface CatalogThinking {\n budget?: number;\n supportsThinking?: boolean;\n}\n\nexport interface CatalogSnapshot {\n enums: Record<string, string>;\n runtimeIds: string[];\n thinking?: Record<string, CatalogThinking>;\n deprecated?: Record<string, string>;\n version: number;\n}\n\n/**\n * Builds the per-Runtime-ID thinking lookup for a snapshot generation from\n * parsed catalog items. Pure: shared by the refresh path and tests so both\n * derive the same snapshot shape from one parse.\n */\nexport function buildThinkingMap(models: AvailableModelItem[]): Record<string, CatalogThinking> {\n return Object.fromEntries(\n models.map((m) => [\n m.id,\n {\n ...(typeof m.thinkingBudget === \"number\" ? { budget: m.thinkingBudget } : {}),\n supportsThinking: m.supportsThinking ?? false,\n },\n ]),\n );\n}\n\nlet activeStore: CatalogSnapshot = {\n enums: {},\n runtimeIds: [],\n thinking: {},\n deprecated: {},\n version: 0,\n};\n\nexport function getCatalogSnapshot(): CatalogSnapshot {\n return {\n enums: { ...activeStore.enums },\n runtimeIds: [...activeStore.runtimeIds],\n thinking: Object.fromEntries(\n Object.entries(activeStore.thinking ?? {}).map(([id, info]) => [id, { ...info }]),\n ),\n deprecated: { ...(activeStore.deprecated ?? {}) },\n version: activeStore.version,\n };\n}\n\n/**\n * Records one complete refresh generation. Wire-side only: the sole writer is\n * the refresh path in catalog-refresh.ts. Request paths never call this \u2014\n * they read via getCatalogSnapshot and pass the snapshot explicitly.\n */\nexport function updateCatalogStore(\n enums: Record<string, string>,\n runtimeIds: string[],\n thinking: Record<string, CatalogThinking> = {},\n deprecated: Record<string, string> = {}\n): void {\n // A fresh generation is complete: replace instead of merging, so enums for\n // server-removed models are evicted instead of pinned forever.\n activeStore = {\n enums: { ...enums },\n runtimeIds: [...new Set(runtimeIds)],\n thinking: { ...thinking },\n deprecated: { ...deprecated },\n version: activeStore.version + 1,\n };\n}\n\n// Last full Model Catalog generation behind the single catalog seam: the\n// snapshot holds only IDs/enums/thinking, while the models table also needs\n// display names, quota flags, and sort order. Written only by ingestCatalog.\nlet lastCatalog: AvailableModelsCatalog | undefined;\n\n/**\n * Records one complete catalog generation: snapshot store plus the full\n * items the models table formats. The sole writer is the refresh path;\n * request paths read via getCatalogSnapshot / getStoredCatalog and never\n * write, so one generation is always the single freshness truth.\n */\nexport function ingestCatalog(catalog: AvailableModelsCatalog): void {\n updateCatalogStore(\n catalog.modelEnums,\n catalog.models.map((m) => m.id),\n buildThinkingMap(catalog.models),\n catalog.deprecated || {},\n );\n lastCatalog = catalog;\n}\n\n/** Full catalog behind the seam, if any generation was ingested yet. */\nexport function getStoredCatalog(): AvailableModelsCatalog | undefined {\n return lastCatalog;\n}\n\nfunction resolveRuntimeModelId(\n modelId: string,\n effort?: string,\n availableRuntimeIds: string[] = activeStore.runtimeIds\n): string {\n if (\n modelId.endsWith(\"-high\") ||\n modelId.endsWith(\"-medium\") ||\n modelId.endsWith(\"-low\") ||\n modelId.endsWith(\"-thinking\") ||\n modelId.endsWith(\"-agent\") ||\n modelId.endsWith(\"-extra-low\") ||\n modelId.endsWith(\"-tiered\")\n ) {\n return modelId;\n }\n\n const isOff = effort === \"off\" || effort === \"none\";\n const isLow = isOff || effort === \"low\" || effort === \"minimal\";\n const isMedium = effort === \"medium\";\n\n // Tier resolution against the live snapshot: effort picks suffix candidates\n // in preference order, the first one the server lists wins \u2014 so newly\n // released models (e.g. gemini-3.9-flash) resolve with no code change.\n if (Array.isArray(availableRuntimeIds) && availableRuntimeIds.length > 0) {\n const candidates: string[] = [];\n if (isLow) {\n candidates.push(`${modelId}-low`, `${modelId}-extra-low`, modelId);\n } else if (isMedium) {\n candidates.push(`${modelId}-medium`, modelId);\n } else {\n candidates.push(`${modelId}-high`, `${modelId}-thinking`, `${modelId}-agent`, modelId);\n }\n\n for (const cand of candidates) {\n if (availableRuntimeIds.includes(cand)) {\n return cand;\n }\n }\n }\n\n // Fallback heuristics when runtime ID catalog is not available\n if (modelId.includes(\"flash\")) {\n if (isLow) return `${modelId}-low`;\n if (isMedium) return `${modelId}-medium`;\n return `${modelId}-high`;\n }\n\n if (modelId.includes(\"pro\")) {\n if (isLow) return `${modelId}-low`;\n return `${modelId}-high`;\n }\n\n if (modelId.startsWith(\"claude-\")) {\n if (modelId.includes(\"opus\")) return `${modelId}-thinking`;\n return modelId;\n }\n\n if (modelId.startsWith(\"gpt-\")) {\n return `${modelId}-medium`;\n }\n\n return modelId;\n}\n\nexport interface ModelPlan {\n runtimeModelId: string;\n modelEnum: string;\n thinkingConfig: { includeThoughts: boolean; thinkingBudget: number };\n isNonGemini: boolean;\n isClaude: boolean;\n}\n\n/**\n * Resolves one Model Plan (see CONTEXT.md) from a single snapshot generation:\n * Runtime Model ID, model enum, thinking budget, and the non-Gemini flag that\n * switches tool schema mode. Pass an explicit snapshot in tests; otherwise the\n * live snapshot is read once, so enums and runtime IDs never mix generations.\n */\nexport function resolveModelPlan(\n publicModelId: string,\n effort?: string,\n snapshot: CatalogSnapshot = getCatalogSnapshot()\n): ModelPlan {\n const runtimeModelId = followRenames(\n resolveRuntimeModelId(publicModelId, effort, snapshot.runtimeIds),\n snapshot\n );\n const modelEnum = snapshot.enums[runtimeModelId];\n if (!modelEnum) {\n // Fail fast: a retired or mistyped ID must surface here with guidance,\n // not as a cryptic server rejection for an empty model_enum label.\n throw new Error(\n `Unknown model \"${runtimeModelId}\" (not in catalog snapshot v${snapshot.version}). ` +\n `Run /antigravity refresh and pick a current model.`\n );\n }\n const family = classifyModelFamily(runtimeModelId);\n return {\n runtimeModelId,\n modelEnum,\n thinkingConfig: resolveThinkingConfig(runtimeModelId, effort, snapshot),\n isNonGemini: family !== \"gemini\",\n isClaude: family === \"claude\",\n };\n}\n\n/**\n * Follows server-directed renames (deprecatedModelIds), e.g.\n * gemini-3.1-pro-high \u2192 gemini-pro-agent. Applied uniformly to derived and\n * explicitly passed IDs: the server lists the old ID as deprecated, so new\n * code must not keep sending it. Cycles terminate via the visited set.\n */\nfunction followRenames(runtimeModelId: string, snapshot: CatalogSnapshot): string {\n const renamed = snapshot.deprecated;\n if (!renamed) return runtimeModelId;\n let current = runtimeModelId;\n const seen = new Set<string>([current]);\n while (renamed[current] && !seen.has(renamed[current])) {\n current = renamed[current];\n seen.add(current);\n }\n return current;\n}\n\n/**\n * Resolves the thinking config for one Runtime Model ID from a single snapshot\n * generation. Snapshot wire values win; the hardcoded heuristic below only\n * serves snapshots without per-ID thinking data (pre-budget persists) and\n * models the wire marks as non-thinking resolve to disabled thoughts.\n */\nfunction resolveThinkingConfig(\n runtimeModelId: string,\n effort?: string,\n snapshot: CatalogSnapshot = getCatalogSnapshot()\n): { includeThoughts: boolean; thinkingBudget: number } {\n if (effort === \"off\" || effort === \"none\") {\n return { includeThoughts: false, thinkingBudget: 0 };\n }\n const info = snapshot.thinking?.[runtimeModelId];\n if (info) {\n if (typeof info.budget === \"number\") {\n return { includeThoughts: true, thinkingBudget: info.budget };\n }\n if (!info.supportsThinking) {\n return { includeThoughts: false, thinkingBudget: 0 };\n }\n }\n return getThinkingConfig(runtimeModelId, effort);\n}\n\n// Antigravity is quota-based with no per-token billing, so every model\n// reports zero cost instead of fictitious Gemini API prices.\n// Revisit if a metered paid tier ever appears.\nexport function estimateModelCost(_baseId: string): Model<any>[\"cost\"] {\n return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };\n}\n\nexport function synthesizeDynamicModel(baseId: string, items: AvailableModelItem[]): Model<any> {\n const repItem = items.find((it) => it.id === `${baseId}-high` || it.id === baseId) || items[0];\n\n const isFlash = baseId.includes(\"flash\");\n const isClaude = baseId.startsWith(\"claude-\");\n const isGpt = baseId.startsWith(\"gpt-\");\n\n // Static fallbacks mirror captures/agy_cli_1.1.27/models.resp.json\n // (Claude maxTokens 250000 / maxOutputTokens 64000, also seen on the wire\n // in stream_turn8/9). Live refresh overwrites these with catalog values.\n const defaultContext = isFlash ? 1048576 : isClaude ? 250000 : isGpt ? 128000 : 1048576;\n const defaultMaxOutput = isClaude ? 64000 : isGpt ? 32768 : 65536;\n\n return {\n id: baseId,\n name: formatModelDisplayName(baseId, repItem?.displayName),\n provider: PROVIDER_ID,\n api: \"antigravity-api\",\n baseUrl: DEFAULT_ENDPOINT,\n reasoning: repItem?.supportsThinking ?? true,\n input: repItem?.supportsImages ? [\"text\", \"image\"] : [\"text\"],\n cost: estimateModelCost(baseId),\n contextWindow: repItem?.maxTokens || defaultContext,\n maxTokens: repItem?.maxOutputTokens || defaultMaxOutput,\n };\n}\n\nexport function buildDynamicPublicModels(catalog?: AvailableModelsCatalog): Array<Model<any>> {\n if (!catalog || !Array.isArray(catalog.models) || catalog.models.length === 0) {\n return [];\n }\n\n // Filter eligible models according to agentModelSorts (strict agy CLI parity)\n const agentSortIds = catalog.agentModelSorts;\n const eligibleItems =\n Array.isArray(agentSortIds) && agentSortIds.length > 0\n ? catalog.models.filter((m) => agentSortIds.includes(m.id))\n : catalog.models;\n\n const runtimeGroups = new Map<string, AvailableModelItem[]>();\n for (const item of eligibleItems) {\n const baseId = extractBaseModelId(item.id);\n const list = runtimeGroups.get(baseId) || [];\n list.push(item);\n runtimeGroups.set(baseId, list);\n }\n\n // Preserve recommended ordering from agentModelSorts\n const orderedBaseIds: string[] = [];\n if (Array.isArray(agentSortIds)) {\n for (const sortId of agentSortIds) {\n const baseId = extractBaseModelId(sortId);\n if (runtimeGroups.has(baseId) && !orderedBaseIds.includes(baseId)) {\n orderedBaseIds.push(baseId);\n }\n }\n }\n\n for (const baseId of runtimeGroups.keys()) {\n if (!orderedBaseIds.includes(baseId)) {\n orderedBaseIds.push(baseId);\n }\n }\n\n return orderedBaseIds.map((baseId) => synthesizeDynamicModel(baseId, runtimeGroups.get(baseId)!));\n}\n\n/**\n * Model Catalog presentation (pure, in-process): display-name rules and the\n * Subcommand table format. No network, no snapshot access \u2014 everything enters\n * as plain CatalogSnapshot / AvailableModelsCatalog values, so tests pin the\n * rendered output without fetching. Lives here (not in catalog-refresh.ts)\n * so the wire module keeps only fetch/parse/publish.\n */\nexport function formatModelDisplayName(baseId: string, rawDisplayName?: string): string {\n if (rawDisplayName) {\n const cleaned = rawDisplayName.replace(/\\s*\\([^)]*\\)/g, \"\").trim();\n if (cleaned.length > 0) {\n return cleaned;\n }\n }\n const words = baseId.split(\"-\").map((w) => {\n if (/^\\d+(\\.\\d+)?$/.test(w)) return w;\n return w.charAt(0).toUpperCase() + w.slice(1);\n });\n return words.join(\" \");\n}\n\nfunction formatTokenCount(n?: number): string {\n if (typeof n !== \"number\") return \"n/a\";\n if (n % 1048576 === 0) return `${n / 1048576}M`;\n if (n % 1024 === 0) return `${n / 1024}k`;\n if (n % 1000 === 0) return `${n / 1000}k`;\n // ponytail: odd values (e.g. 65535) round to nearest KiB instead of a noisy decimal\n return `${Math.round(n / 1024)}k`;\n}\n\nexport function formatModelsList(catalog: AvailableModelsCatalog): string {\n const sorts = catalog.agentModelSorts;\n const recommendedOnly = Array.isArray(sorts) && sorts.length > 0;\n const rank = new Map((sorts || []).map((id, i) => [id, i]));\n\n const models = (recommendedOnly ? catalog.models.filter((m) => rank.has(m.id)) : catalog.models.slice()).sort(\n (a, b) => (rank.get(a.id) ?? Number.MAX_SAFE_INTEGER) - (rank.get(b.id) ?? Number.MAX_SAFE_INTEGER) ||\n a.id.localeCompare(b.id)\n );\n\n const rows = models.map((m) => {\n const flags: string[] = [];\n if (m.supportsThinking) flags.push(\"thinking\");\n if (m.supportsImages) flags.push(\"images\");\n return {\n id: m.id,\n name: formatModelDisplayName(m.id, m.displayName || m.id),\n ctx: `${formatTokenCount(m.maxTokens)}/${formatTokenCount(m.maxOutputTokens)}`,\n feat: flags.length > 0 ? flags.join(\", \") : \"-\",\n rem: typeof m.remainingFraction === \"number\" ? `${Math.round(m.remainingFraction * 100)}%` : \"N/A\",\n };\n });\n\n const headers = { id: \"Model\", name: \"Name\", ctx: \"Context\", feat: \"Features\", rem: \"Rem\" };\n const w = {\n id: Math.max(headers.id.length, ...rows.map((r) => r.id.length)),\n name: Math.max(headers.name.length, ...rows.map((r) => r.name.length)),\n ctx: Math.max(headers.ctx.length, ...rows.map((r) => r.ctx.length)),\n feat: Math.max(headers.feat.length, ...rows.map((r) => r.feat.length)),\n };\n\n const lines: string[] = [\n `Available Antigravity Models (${models.length}${recommendedOnly ? \" recommended\" : \"\"}):`,\n ` ${headers.id.padEnd(w.id)} ${headers.name.padEnd(w.name)} ${headers.ctx.padEnd(w.ctx)} ${headers.feat.padEnd(w.feat)} ${headers.rem}`,\n ];\n for (const r of rows) {\n lines.push(\n ` ${r.id.padEnd(w.id)} ${r.name.padEnd(w.name)} ${r.ctx.padEnd(w.ctx)} ${r.feat.padEnd(w.feat)} ${r.rem.padStart(4)}`\n );\n }\n\n return lines.join(\"\\n\");\n}\n", "import { postAntigravity } from \"./protocol.ts\";\nimport { parseStoredCredentials } from \"./auth.ts\";\nimport type { AvailableModelItem, AvailableModelsCatalog } from \"./model-catalog.ts\";\nimport { buildDynamicPublicModels, getCatalogSnapshot, ingestCatalog, updateCatalogStore } from \"./model-catalog.ts\";\nimport type { Model } from \"@earendil-works/pi-ai\";\n\n/**\n * Model Catalog refresh (wire, ports & adapters): fetchAvailableModels fetch,\n * Capture Fixture-shaped parse, and Catalog Persistence publish.\n * Produces AvailableModelsCatalog values and records generations via\n * updateCatalogStore; all pure mapping and presentation lives in model-catalog.ts.\n */\nexport async function refreshCatalog(context: any): Promise<Array<Model<any>>> {\n // Restore from context.stored first for offline restart support,\n // but only into a pristine store: a failed refresh must not clobber a\n // fresher in-memory snapshot with older persisted data.\n const storedEnums = context.stored?.[\"pi-provider-antigravity\"]?.modelEnums;\n const storedRuntimeIds = context.stored?.[\"pi-provider-antigravity\"]?.runtimeIds;\n const storedThinking = context.stored?.[\"pi-provider-antigravity\"]?.thinking;\n const storedDeprecated = context.stored?.[\"pi-provider-antigravity\"]?.deprecated;\n if ((storedEnums || storedRuntimeIds || storedThinking || storedDeprecated) && getCatalogSnapshot().version === 0) {\n updateCatalogStore(storedEnums || {}, storedRuntimeIds || [], storedThinking || {}, storedDeprecated || {});\n }\n\n if (!context.allowNetwork) {\n return context.stored?.models || [];\n }\n\n try {\n const apiKey = context.credential?.access;\n if (!apiKey) {\n return context.stored?.models || [];\n }\n\n const { token, projectId } = parseStoredCredentials(apiKey);\n const catalog = await fetchAvailableModelsCatalog(token, projectId, context.signal);\n ingestCatalog(catalog);\n\n const dynamicModels = buildDynamicPublicModels(catalog);\n\n // Publish to Pi models-store.json from the stored generation (not the\n // transient parse), so stored and published are always one generation.\n if (context.publish) {\n const snap = getCatalogSnapshot();\n await context.publish({\n persist: {\n models: dynamicModels,\n checkedAt: Date.now(),\n \"pi-provider-antigravity\": {\n modelEnums: { ...snap.enums },\n runtimeIds: [...snap.runtimeIds],\n thinking: { ...(snap.thinking ?? {}) },\n deprecated: { ...(snap.deprecated ?? {}) },\n },\n },\n });\n }\n\n return dynamicModels;\n } catch {\n return context.stored?.models || [];\n }\n}\n\nexport function parseAvailableModels(data: any): AvailableModelsCatalog {\n const models: AvailableModelItem[] = [];\n const modelEnums: Record<string, string> = {};\n\n const modelsObj = data.models || {};\n for (const [id, info] of Object.entries<any>(modelsObj)) {\n if (id.startsWith(\"tab_\") || id.startsWith(\"chat_\")) continue; // hide internal completions\n\n const modelEnum = typeof info.model === \"string\" ? info.model : undefined;\n if (modelEnum) {\n modelEnums[id] = modelEnum;\n }\n\n const quotaInfo = info.quotaInfo || {};\n models.push({\n id,\n displayName: info.displayName || info.label || id,\n modelEnum,\n remainingFraction: quotaInfo.remainingFraction,\n resetTime: quotaInfo.resetTime,\n supportsThinking: Boolean(info.supportsThinking),\n thinkingBudget: typeof info.thinkingBudget === \"number\" ? info.thinkingBudget : undefined,\n minThinkingBudget: typeof info.minThinkingBudget === \"number\" ? info.minThinkingBudget : undefined,\n supportsImages: Boolean(info.supportsImages),\n maxTokens: typeof info.maxTokens === \"number\" ? info.maxTokens : undefined,\n maxOutputTokens: typeof info.maxOutputTokens === \"number\" ? info.maxOutputTokens : undefined,\n });\n }\n\n const agentModelSorts: string[] = [];\n if (Array.isArray(data.agentModelSorts)) {\n for (const sortGroup of data.agentModelSorts) {\n if (Array.isArray(sortGroup.groups)) {\n for (const group of sortGroup.groups) {\n if (Array.isArray(group.modelIds)) {\n agentModelSorts.push(...group.modelIds);\n }\n }\n }\n }\n }\n\n models.sort((a, b) => a.id.localeCompare(b.id));\n\n // Server-directed renames (old Runtime Model ID \u2192 current one).\n const deprecated: Record<string, string> = {};\n const rawDeprecated = (data as any).deprecatedModelIds;\n if (rawDeprecated && typeof rawDeprecated === \"object\") {\n for (const [oldId, info] of Object.entries<any>(rawDeprecated)) {\n if (info && typeof info.newModelId === \"string\") {\n deprecated[oldId] = info.newModelId;\n }\n }\n }\n\n return {\n models,\n modelEnums,\n ...(agentModelSorts.length > 0 ? { agentModelSorts } : {}),\n ...(Object.keys(deprecated).length > 0 ? { deprecated } : {}),\n };\n}\n\nexport async function fetchAvailableModelsCatalog(\n token: string,\n projectId: string,\n signal?: AbortSignal\n): Promise<AvailableModelsCatalog> {\n const res = await postAntigravity({\n token,\n path: \"v1internal:fetchAvailableModels\",\n body: { project: projectId },\n signal,\n });\n if (!res.ok) {\n const errText = await res.text();\n throw new Error(`Failed to fetch models (${res.status}): ${errText}`);\n }\n const json = await res.json();\n return parseAvailableModels(json);\n}\n\n\n", "import type { ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport { parseStoredCredentials } from \"./auth.ts\";\nimport { classifyModelFamily } from \"./model-catalog.ts\";\nimport { postAntigravity, PROVIDER_ID } from \"./protocol.ts\";\nimport type { QuotaFooterMode } from \"./settings.ts\";\n\n/**\n * Quota Status module (deep, mock): everything behind the footer slot and\n * /antigravity usage \u2014 Quota Pool wire parse, urgent-window math, ratio\n * self-calibration, throttle, cache, and calibration persistence.\n * Callers use footerFor / ingest / refresh / refreshAndPaint only; the fetch,\n * the ratio state, and the file shape behind the injected store are internal.\n * The store itself (file-backed or in-memory) arrives through QuotaStatusStore.\n */\n\nexport interface QuotaBucket {\n bucketId: string;\n displayName: string;\n window?: string;\n resetTime?: string;\n description?: string;\n remainingFraction: number;\n}\n\nexport interface QuotaGroup {\n displayName: string;\n description?: string;\n buckets: QuotaBucket[];\n}\n\nexport interface QuotaSummary {\n groups: QuotaGroup[];\n description?: string;\n}\n\nexport function renderProgressBar(fraction: number, width = 10): string {\n const clamped = Math.max(0, Math.min(1, fraction));\n const filled = Math.round(clamped * width);\n return `[${\"#\".repeat(filled)}${\"-\".repeat(width - filled)}]`;\n}\n\ntype QuotaWindowKind = \"5h\" | \"weekly\" | \"other\";\n\nconst QUOTA_WINDOW_RANK: Record<QuotaWindowKind, number> = {\n \"5h\": 0,\n weekly: 1,\n other: 2,\n};\n\nfunction classifyQuotaWindow(bucket: QuotaBucket): QuotaWindowKind {\n // The wire reports a machine `window` (\"5h\"/\"weekly\" in every capture). Trust\n // it \u2014 no display-prose guessing. Unknown or missing windows are \"other\".\n const w = (bucket.window || \"\").trim().toLowerCase();\n if (w === \"5h\") return \"5h\";\n if (w === \"weekly\") return \"weekly\";\n return \"other\";\n}\n\nfunction shortWindowLabel(bucket: QuotaBucket, kind: QuotaWindowKind): string {\n if (kind === \"5h\") return \"5h\";\n if (kind === \"weekly\") return \"Wk\";\n return bucket.displayName || bucket.bucketId || \"Quota\";\n}\n\nfunction sortQuotaBuckets(buckets: QuotaBucket[]): QuotaBucket[] {\n return buckets\n .slice()\n .sort((a, b) => QUOTA_WINDOW_RANK[classifyQuotaWindow(a)] - QUOTA_WINDOW_RANK[classifyQuotaWindow(b)]);\n}\n\nexport function formatResetRemaining(resetTime?: string): string | null {\n if (!resetTime) return null;\n const ts = Date.parse(resetTime);\n if (!Number.isFinite(ts)) return null;\n const delta = ts - Date.now();\n if (delta <= 0) return \"ready\";\n const totalMin = Math.ceil(delta / 60000);\n const days = Math.floor(totalMin / (60 * 24));\n const hours = Math.floor((totalMin % (60 * 24)) / 60);\n const mins = totalMin % 60;\n if (days > 0) return `in ${days}d ${hours}h`;\n if (hours > 0) return `in ${hours}h ${mins}m`;\n return `in ${mins}m`;\n}\n\nexport function parseQuotaSummary(data: any): QuotaSummary {\n const groups: QuotaGroup[] = [];\n\n for (const g of data.groups || []) {\n const buckets: QuotaBucket[] = [];\n for (const b of g.buckets || []) {\n buckets.push({\n bucketId: b.bucketId || \"unknown\",\n displayName: b.displayName || b.bucketId || \"Limit\",\n window: b.window,\n resetTime: b.resetTime,\n description: b.description,\n remainingFraction: typeof b.remainingFraction === \"number\" ? b.remainingFraction : 1,\n });\n }\n groups.push({\n displayName: g.displayName || \"Quota Group\",\n description: g.description,\n buckets,\n });\n }\n\n return {\n groups,\n description: data.description,\n };\n}\n\nexport function formatQuotaSummary(summary: QuotaSummary): string {\n const lines: string[] = [];\n\n for (const group of summary.groups) {\n if (lines.length > 0) lines.push(\"\");\n lines.push(`${group.displayName}`);\n for (const b of sortQuotaBuckets(group.buckets)) {\n const pct = Math.round(b.remainingFraction * 100);\n const label = shortWindowLabel(b, classifyQuotaWindow(b)).padEnd(3);\n const reset = formatResetRemaining(b.resetTime);\n const suffix = reset && pct < 100 ? ` (${reset})` : \"\";\n lines.push(` ${label} ${renderProgressBar(b.remainingFraction)} ${String(pct).padStart(3)}% left${suffix}`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\nexport function isGeminiQuotaGroup(group: QuotaGroup): boolean {\n // Bucket ids (\"gemini-5h\" vs \"3p-5h\") \u2014 not display prose (\"Gemini Models\").\n return (group.buckets || []).some((b) => (b.bucketId || \"\").toLowerCase().includes(\"gemini\"));\n}\n\nexport function selectQuotaGroup(groups: QuotaGroup[], modelId?: string): QuotaGroup | undefined {\n if (groups.length === 0) return undefined;\n const family = classifyModelFamily(modelId);\n if (family === \"claude\" || family === \"gpt\") {\n const found = groups.find((g) => !isGeminiQuotaGroup(g));\n if (found) return found;\n }\n return groups.find((g) => isGeminiQuotaGroup(g)) || groups[0];\n}\n\n// Compact one-line footer text (e.g. \"Wk 6% (2d 15h)\") showing the most\n// urgent window of the group backing the current model: 5h and weekly pools\n// differ in volume, so the wall you hit first is min(r5h, rWk \u00D7 R) \u2014 not min\n// fraction. R self-calibrates from observed deltas (see calibrateWeeklyTo5hRatio).\nexport const DEFAULT_WEEKLY_TO_5H_RATIO = 6.0;\n\nexport interface WindowFractionPair {\n \"5h\": number;\n weekly: number;\n}\n\nfunction windowBucket(buckets: QuotaBucket[], kind: QuotaWindowKind): QuotaBucket | undefined {\n return buckets.find((b) => classifyQuotaWindow(b) === kind);\n}\n\n// Pool-group key for persisted pairs: the 5h bucket's wire id minus its window\n// suffix (\"gemini-5h\" \u2192 \"gemini\"). Display names are upstream prose, and the raw\n// id would echo the window inside every record (\"gemini-5h\": { \"5h\", weekly }).\n// Unrecognized shapes keep the full id \u2014 matching only needs determinism.\nfunction poolKey(bucket: QuotaBucket): string {\n const id = bucket.bucketId || \"unknown\";\n return id.replace(/[-_](5h|five-?hour|weekly)$/i, \"\") || id;\n}\n\n// Plain-object form for JSON persistence (see coordinator state).\nexport function extractWindowFractionPairs(summary?: QuotaSummary): Record<string, WindowFractionPair> {\n const pairs: Record<string, WindowFractionPair> = {};\n for (const g of summary?.groups || []) {\n const bucket5h = windowBucket(g.buckets, \"5h\");\n const bucketWeekly = windowBucket(g.buckets, \"weekly\");\n if (bucket5h && bucketWeekly) pairs[poolKey(bucket5h)] = { \"5h\": bucket5h.remainingFraction, weekly: bucketWeekly.remainingFraction };\n }\n return pairs;\n}\n\n// Self-calibrates the 5h vs weekly volume ratio R from delta consumption:\n// the same absolute spend drops the 5h fraction R\u00D7 faster than weekly.\n// First valid pool wins; resets and noise fall outside the guards below.\nexport function calibrateWeeklyTo5hRatio(\n previous: Record<string, WindowFractionPair> | undefined,\n current: Record<string, WindowFractionPair> | undefined,\n currentRatio = DEFAULT_WEEKLY_TO_5H_RATIO,\n): number {\n if (!previous || !current) return currentRatio;\n for (const key of Object.keys(current)) {\n const prev = previous[key];\n const curr = current[key];\n if (!prev || !curr) continue;\n\n const dFiveHour = prev[\"5h\"] - curr[\"5h\"];\n const dWeekly = prev.weekly - curr.weekly;\n\n if (dFiveHour > 0.0001 && dWeekly > 0.00001) {\n const observedRatio = dFiveHour / dWeekly;\n if (observedRatio >= 1.0 && observedRatio <= 20.0) {\n return Math.round(observedRatio * 100) / 100;\n }\n }\n }\n return currentRatio;\n}\n\nfunction selectUrgentBucket(group: QuotaGroup, weeklyTo5hRatio: number): QuotaBucket | undefined {\n const buckets = group.buckets;\n if (buckets.length === 0) return undefined;\n const bucket5h = windowBucket(buckets, \"5h\");\n const bucketWeekly = windowBucket(buckets, \"weekly\");\n if (bucket5h && bucketWeekly) {\n return bucketWeekly.remainingFraction * weeklyTo5hRatio < bucket5h.remainingFraction ? bucketWeekly : bucket5h;\n }\n let worst = buckets[0];\n for (const b of buckets) {\n if (b.remainingFraction < worst.remainingFraction) worst = b;\n }\n return worst;\n}\n\nexport function buildQuotaFooter(\n summary: QuotaSummary,\n modelId?: string,\n weeklyTo5hRatio = DEFAULT_WEEKLY_TO_5H_RATIO,\n): string | undefined {\n const group = selectQuotaGroup(summary.groups, modelId);\n if (!group) return undefined;\n const urgent = selectUrgentBucket(group, weeklyTo5hRatio);\n return urgent ? formatQuotaWindowPart(urgent) : undefined;\n}\n\n// Both windows of the backing group, 5h first (e.g. \"5h 35% (26m) \u00B7 Wk 75% (6d 14h)\").\nexport function buildQuotaFooterBoth(summary: QuotaSummary, modelId?: string): string | undefined {\n const group = selectQuotaGroup(summary.groups, modelId);\n if (!group || group.buckets.length === 0) return undefined;\n return sortQuotaBuckets(group.buckets).map(formatQuotaWindowPart).join(\" \u00B7 \");\n}\n\n// Window prefix names the bucket (\"5h\"/\"Wk\"); unknown windows fall back to\n// the bucket's own display name.\nexport function formatQuotaWindowPart(bucket: QuotaBucket): string {\n const kind = classifyQuotaWindow(bucket);\n const prefix = kind === \"5h\" ? \"5h\" : kind === \"weekly\" ? \"Wk\" : bucket.displayName || bucket.bucketId;\n const pct = Math.round(bucket.remainingFraction * 100);\n const short = formatResetRemaining(bucket.resetTime)?.replace(/^in /, \"\");\n if (short && pct < 100) return `${prefix} ${pct}% (${short})`;\n return `${prefix} ${pct}%`;\n}\n\n// Threshold coloring for footer slots. Event handlers never see Theme, so this\n// uses basic ANSI (theme.fg(\"error\"/\"warning\") equivalents on dark) with a\n// foreground-only reset, mirroring Theme.fg instead of a full reset.\nconst ANSI_RED = \"\\x1b[31m\";\nconst ANSI_YELLOW = \"\\x1b[33m\";\nconst ANSI_FG_RESET = \"\\x1b[39m\";\n\nexport const QUOTA_WARN_PCT = 30;\nexport const QUOTA_ALERT_PCT = 10;\n\nexport function colorizeQuotaFooter(footer: string | undefined): string | undefined {\n if (!footer) return undefined;\n const match = footer.match(/(\\d+)%/);\n if (!match) return footer;\n const pct = parseInt(match[1], 10);\n if (pct <= QUOTA_ALERT_PCT) return `${ANSI_RED}${footer}${ANSI_FG_RESET}`;\n if (pct <= QUOTA_WARN_PCT) return `${ANSI_YELLOW}${footer}${ANSI_FG_RESET}`;\n return footer;\n}\n\n// Colors each \" \u00B7 \"-separated window part by its own percentage.\nexport function colorizeQuotaFooterBoth(footer: string | undefined): string | undefined {\n if (!footer) return undefined;\n return footer\n .split(\" \u00B7 \")\n .map((part) => colorizeQuotaFooter(part) ?? part)\n .join(\" \u00B7 \");\n}\n\n// Distinct from personal-config keys (e.g. \"quota\") so both extensions can\n// coexist without overwriting each other's footer slot.\nexport const QUOTA_STATUS_KEY = \"antigravity_quota\";\nexport const QUOTA_REFRESH_INTERVAL_MS = 5 * 60 * 1000;\n// Baselines older than this may straddle a quota reset (negative or\n// meaningless deltas), so cross-session calibration ignores them. An hour\n// caps the straddle risk at ~1/5 of the 5h window while an hour of active\n// use still dwarfs the noise guards below thousands-fold.\nexport const MAX_OBSERVATION_AGE_MS = 60 * 60 * 1000;\n\n// Persisted Quota Pool calibration state (states.quota in the provider file).\n// The file shape is owned by the settings module; the coordinator only sees\n// this narrow view through its store.\nexport interface QuotaState {\n [key: string]: unknown;\n}\n\n// The seam behind the coordinator: production uses the file-backed store\n// from the settings module, tests use an in-memory adapter.\nexport interface QuotaStatusStore {\n loadMode: () => QuotaFooterMode;\n loadQuotaState: () => QuotaState | undefined;\n saveQuotaState: (state: {\n weeklyTo5hRatio: number;\n previousObservation: Record<string, WindowFractionPair>;\n updatedAt: number;\n }) => boolean;\n}\n\nexport interface RefreshOptions {\n force?: boolean;\n // Explicit looks at quota (/antigravity usage) bypass the mode and model\n // gates but still calibrate, persist, and share the throttle window.\n ignoreMode?: boolean;\n signal?: AbortSignal;\n}\n\nfunction isFiniteNumber(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value);\n}\n\n// Lenient shape check for persisted observations; garbage restores as absent.\nfunction validPairs(raw: unknown): Record<string, WindowFractionPair> | undefined {\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return undefined;\n const pairs: Record<string, WindowFractionPair> = {};\n for (const [key, value] of Object.entries(raw)) {\n const rec = value as { \"5h\"?: unknown; weekly?: unknown };\n const fiveHour = rec?.[\"5h\"];\n const weekly = rec?.weekly;\n if (isFiniteNumber(fiveHour) && isFiniteNumber(weekly)) pairs[key] = { \"5h\": fiveHour, weekly };\n }\n return Object.keys(pairs).length > 0 ? pairs : undefined;\n}\n\nexport type QuotaStatusCtx = Pick<ExtensionContext, \"ui\" | \"modelRegistry\" | \"model\">;\n\n// Footer quota belongs to this provider: foreign models (or none selected)\n// get no slot and trigger no quota fetch. Pi core keeps Model.id bare\n// (\"gemini-3.8-flash\"); the \"antigravity/...\" form is only /model selection\n// syntax, so the provider field is the single reliable signal.\nexport function isAntigravityModel(model?: { provider?: string }): boolean {\n return model?.provider === PROVIDER_ID;\n}\n\nexport function paintQuotaStatus(coord: QuotaStatusCoordinator, ctx: QuotaStatusCtx): void {\n const mode = coord.mode();\n if (mode === \"off\" || !isAntigravityModel(ctx.model)) {\n ctx.ui.setStatus(QUOTA_STATUS_KEY, undefined);\n return;\n }\n const plain = coord.footerFor(ctx.model?.id, mode);\n ctx.ui.setStatus(\n QUOTA_STATUS_KEY,\n mode === \"both\" ? colorizeQuotaFooterBoth(plain) : colorizeQuotaFooter(plain),\n );\n}\n\n// In-memory throttle around the quota fetch: at most one network call per\n// QUOTA_REFRESH_INTERVAL_MS, with inflight dedup. Failures keep the stale\n// footer instead of flashing errors in the status bar.\nexport class QuotaStatusCoordinator {\n private summary: QuotaSummary | undefined;\n private fetchedAt = 0;\n private inflight: Promise<QuotaSummary | undefined> | null = null;\n private weeklyTo5hRatio: number;\n private lastPairs: Record<string, WindowFractionPair> | undefined;\n private readonly store: QuotaStatusStore;\n\n constructor(store: QuotaStatusStore) {\n this.store = store;\n this.weeklyTo5hRatio = DEFAULT_WEEKLY_TO_5H_RATIO;\n this.loadPersisted();\n }\n\n get ratio(): number {\n return this.weeklyTo5hRatio;\n }\n\n // Single source of truth for the mode: the injected store.\n mode(): QuotaFooterMode {\n return this.store.loadMode();\n }\n\n footerFor(modelId?: string, mode: QuotaFooterMode = \"single\"): string | undefined {\n if (!this.summary || mode === \"off\") return undefined;\n return mode === \"both\"\n ? buildQuotaFooterBoth(this.summary, modelId)\n : buildQuotaFooter(this.summary, modelId, this.weeklyTo5hRatio);\n }\n\n get isFresh(): boolean {\n return !!this.summary && Date.now() - this.fetchedAt < QUOTA_REFRESH_INTERVAL_MS;\n }\n\n refresh(ctx: QuotaStatusCtx, opts: RefreshOptions = {}): Promise<QuotaSummary | undefined> {\n if (!opts.ignoreMode && (this.mode() === \"off\" || !isAntigravityModel(ctx.model))) {\n return Promise.resolve(undefined);\n }\n if (this.inflight) return this.inflight;\n if (!opts.force && this.isFresh) return Promise.resolve(this.summary);\n return this.fetchOnce(ctx, opts.signal);\n }\n\n // The single ordering behind the footer slot: paint cached text instantly,\n // refresh when the throttle window expired, repaint with fresh text.\n // Event handlers delegate here instead of replicating the sequence.\n async refreshAndPaint(ctx: QuotaStatusCtx): Promise<void> {\n paintQuotaStatus(this, ctx);\n await this.refresh(ctx);\n paintQuotaStatus(this, ctx);\n }\n\n // One fetch for preview purposes even when the footer slot is off: opening\n // quota settings is an explicit look at quota.\n ensurePreview(ctx: QuotaStatusCtx): Promise<QuotaSummary | undefined> {\n if (this.summary) return Promise.resolve(this.summary);\n if (this.inflight) return this.inflight;\n if (!isAntigravityModel(ctx.model)) return Promise.resolve(undefined);\n return this.fetchOnce(ctx);\n }\n\n private fetchOnce(ctx: QuotaStatusCtx, signal?: AbortSignal): Promise<QuotaSummary | undefined> {\n this.inflight = this.fetch(ctx, signal).finally(() => {\n this.inflight = null;\n });\n return this.inflight;\n }\n\n private async fetch(ctx: QuotaStatusCtx, signal?: AbortSignal): Promise<QuotaSummary | undefined> {\n try {\n const apiKey = await ctx.modelRegistry?.getApiKeyForProvider?.(PROVIDER_ID);\n if (!apiKey) return undefined;\n const { token, projectId } = parseStoredCredentials(apiKey);\n if (!token) return undefined;\n const res = await postAntigravity({\n token,\n path: \"v1internal:retrieveUserQuotaSummary\",\n body: { project: projectId },\n signal,\n });\n if (!res.ok) return undefined;\n const summary = parseQuotaSummary(await res.json());\n this.ingest(summary);\n return summary;\n } catch {\n return undefined;\n }\n }\n\n // Accepts an externally fetched summary into the shared cache: same\n // calibration, persistence, and freshness as fetch().\n ingest(summary: QuotaSummary): void {\n const calibrated = calibrateWeeklyTo5hRatio(this.lastPairs, extractWindowFractionPairs(summary), this.weeklyTo5hRatio);\n this.weeklyTo5hRatio = calibrated;\n this.lastPairs = extractWindowFractionPairs(summary);\n this.summary = summary;\n this.fetchedAt = Date.now();\n this.saveState();\n }\n\n private loadPersisted(): void {\n const entry = this.store.loadQuotaState();\n const ratio = entry?.weeklyTo5hRatio;\n if (typeof ratio === \"number\" && Number.isFinite(ratio) && ratio > 0) {\n this.weeklyTo5hRatio = ratio;\n }\n const at = entry?.updatedAt;\n this.lastPairs = typeof at === \"number\" && Date.now() - at <= MAX_OBSERVATION_AGE_MS\n ? validPairs(entry?.previousObservation)\n : undefined;\n }\n\n // Persists calibration state through the injected store. Best-effort: a\n // stale ratio is still usable.\n private saveState(): void {\n try {\n this.store.saveQuotaState({\n weeklyTo5hRatio: this.weeklyTo5hRatio,\n previousObservation: this.lastPairs ?? {},\n updatedAt: Date.now(),\n });\n } catch {\n // Cache is best-effort; a stale ratio is still usable.\n }\n }\n}\n", "import * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport type { ExtensionCommandContext, Theme } from \"@earendil-works/pi-coding-agent\";\nimport { SettingsList, type SettingItem, type SettingsListTheme } from \"@earendil-works/pi-tui\";\nimport {\n colorizeQuotaFooter,\n colorizeQuotaFooterBoth,\n paintQuotaStatus,\n type QuotaStatusCoordinator,\n type QuotaStatusStore,\n} from \"./quota-status.ts\";\n\nexport const PROVIDER_CONFIG_FILE = \"pi-provider-antigravity.json\";\n\nexport interface ProviderFileConfig {\n settings?: { quotaFooter?: unknown; [key: string]: unknown };\n // Runtime state namespaced per subsystem (e.g. states.quota); unknown\n // entries pass through untouched.\n states?: { [name: string]: { [key: string]: unknown } | undefined };\n}\n\nexport type QuotaFooterMode = \"off\" | \"single\" | \"both\";\n\n// Single opt-in file next to Pi's settings.json (NOT settings.json itself \u2014\n// Pi manages that file and may drop unknown keys). Pi resolves its dir via\n// PI_CODING_AGENT_DIR else ~/.pi/agent; mirror that:\n// { \"settings\": { \"quotaFooter\": \"single\" } } // off (default) | single | both\n// A \"states\" section holds runtime data namespaced per subsystem\n// (e.g. states.quota); unknown keys and sections pass through untouched. Read per call: tiny file, and edits apply\n// on the next refresh without a restart.\nexport function defaultConfigFile(env: NodeJS.ProcessEnv = process.env): string {\n // Mirrors Pi's canonical getAgentDir() (PI_CODING_AGENT_DIR else ~/.pi/agent)\n // \u2014 the same source pi-subagents imports from @earendil-works/pi-coding-agent.\n // Hand-rolled because a root value-import breaks plain-node tests: the\n // package index pulls @earendil-works/pi-server, which isn't installable here.\n const dir = (env.PI_CODING_AGENT_DIR || \"\").trim() || path.join(os.homedir(), \".pi\", \"agent\");\n return path.join(dir, PROVIDER_CONFIG_FILE);\n}\n\nexport function loadProviderConfig(file = defaultConfigFile()): ProviderFileConfig | undefined {\n try {\n const raw = JSON.parse(fs.readFileSync(file, \"utf-8\"));\n if (raw && typeof raw === \"object\" && !Array.isArray(raw)) return raw as ProviderFileConfig;\n } catch {\n // Missing/unreadable/invalid file means \"not configured\".\n }\n return undefined;\n}\n\nexport function saveProviderConfig(file: string, data: ProviderFileConfig): boolean {\n try {\n fs.mkdirSync(path.dirname(file), { recursive: true });\n fs.writeFileSync(file, JSON.stringify(data, null, 2) + \"\\n\", \"utf-8\");\n return true;\n } catch {\n return false;\n }\n}\n\nexport function normalizeFooterMode(value: unknown): QuotaFooterMode | undefined {\n const v = typeof value === \"string\" ? value.trim().toLowerCase() : \"\";\n return v === \"off\" || v === \"single\" || v === \"both\" ? v : undefined;\n}\n\nexport function resolveFooterMode(config?: ProviderFileConfig): QuotaFooterMode {\n return normalizeFooterMode(config?.settings?.quotaFooter) ?? \"off\";\n}\n\n// Production QuotaStatusStore backed by the provider file. Reads per call:\n// tiny file, and edits apply on the next refresh without a restart.\nexport function fileQuotaStatusStore(file = defaultConfigFile()): QuotaStatusStore {\n return {\n loadMode: () => resolveFooterMode(loadProviderConfig(file)),\n loadQuotaState: () => {\n const quota = loadProviderConfig(file)?.states?.quota;\n return quota && typeof quota === \"object\" && !Array.isArray(quota) ? quota : undefined;\n },\n // Merges state into the existing file, preserving settings and unknown\n // keys. Never clobbers a file we couldn't parse.\n saveQuotaState: (state) => {\n try {\n let data: ProviderFileConfig = {};\n try {\n const raw = JSON.parse(fs.readFileSync(file, \"utf-8\"));\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return false;\n data = raw as ProviderFileConfig;\n } catch (err: any) {\n if (err?.code !== \"ENOENT\") return false;\n }\n const states =\n data.states && typeof data.states === \"object\" && !Array.isArray(data.states) ? data.states : {};\n const quota =\n states.quota && typeof states.quota === \"object\" && !Array.isArray(states.quota) ? states.quota : {};\n states.quota = { ...quota, ...state };\n data.states = states;\n return saveProviderConfig(file, data);\n } catch {\n // Cache is best-effort; a stale ratio is still usable.\n return false;\n }\n },\n };\n}\n\nexport interface SettingsFieldDef {\n key: string;\n label: string;\n description?: string;\n options: readonly string[];\n defaultValue?: string;\n // Immediate side-effect after save (e.g. repaint). Absent = save only.\n onChange?: (ctx: ExtensionCommandContext, quotaStatus?: QuotaStatusCoordinator) => Promise<void>;\n}\n\n// Adding a setting = one row here. The TUI dialog and the select fallback\n// both render field pickers automatically once there are two or more rows.\n// Frozen: callers and tests read the registry, never mutate it.\nexport const SETTINGS_FIELDS: readonly SettingsFieldDef[] = Object.freeze([\n {\n key: \"quotaFooter\",\n label: \"Quota footer\",\n description: \"Show remaining quota in the footer\",\n options: [\"off\", \"single\", \"both\"],\n defaultValue: \"off\",\n onChange: async (ctx, quotaStatus) => {\n if (quotaStatus) {\n await quotaStatus.refresh(ctx);\n paintQuotaStatus(quotaStatus, ctx);\n }\n },\n },\n]);\n\nexport function fieldDisplayValue(config: ProviderFileConfig | undefined, field: SettingsFieldDef): string {\n const raw = config?.settings?.[field.key];\n if (typeof raw === \"string\" && raw.trim()) return raw.trim();\n return field.defaultValue ?? \"unset\";\n}\n\nexport function saveSettingValue(field: SettingsFieldDef, value: string, file: string): boolean {\n const current = loadProviderConfig(file) ?? {};\n const settings =\n current.settings && typeof current.settings === \"object\" ? current.settings : {};\n return saveProviderConfig(file, { ...current, settings: { ...settings, [field.key]: value } });\n}\n\n// Shared by the TUI dialog (silent per cycle) and the select fallback:\n// saves, runs the field's side-effect, reports success.\nexport async function applySettingValue(\n field: SettingsFieldDef,\n value: string,\n file: string,\n ctx: ExtensionCommandContext,\n quotaStatus?: QuotaStatusCoordinator,\n): Promise<boolean> {\n if (!saveSettingValue(field, value, file)) {\n return false;\n }\n await field.onChange?.(ctx, quotaStatus);\n return true;\n}\n\n// Rendered footer sample for a mode, for settings previews. Model-aware like\n// the slot itself (a Claude model previews the 3P pool). Plain data only \u2014\n// callers colorize. Off has no sample: the slot stays empty.\nexport function previewQuotaFooterText(\n coord: QuotaStatusCoordinator | undefined,\n modelId: string | undefined,\n mode: string,\n): string | undefined {\n if (!coord || mode === \"off\") return undefined;\n const plain = coord.footerFor(modelId, mode === \"both\" ? \"both\" : \"single\");\n if (!plain) return undefined;\n return mode === \"both\" ? colorizeQuotaFooterBoth(plain) : colorizeQuotaFooter(plain);\n}\n\n// Rows for pi-tui's SettingsList \u2014 the same list component Pi's /settings is\n// built on. Rows with `values` cycle inline on Enter/Space (no submenu).\nexport function buildSettingsItems(\n config: ProviderFileConfig | undefined,\n fields: readonly SettingsFieldDef[] = SETTINGS_FIELDS,\n): SettingItem[] {\n return fields.map((f) => ({\n id: f.key,\n label: f.label,\n description: f.description,\n currentValue: fieldDisplayValue(config, f),\n values: [...f.options],\n }));\n}\n\nexport function emitOutput(ctx: ExtensionCommandContext, text: string, type: \"info\" | \"warning\" | \"error\" = \"info\"): void {\n if (ctx.hasUI) {\n ctx.ui.notify(text, type);\n } else {\n if (type === \"error\" || type === \"warning\") console.error(text);\n else console.log(text);\n }\n}\n\n// Pi's canonical list theme when the host package is importable; otherwise an\n// identical mapping on the live theme (plain-node tests can't load the host\n// package root, and the mapping is five stable color roles).\nasync function settingsListTheme(theme: Theme): Promise<SettingsListTheme> {\n try {\n const pi = await import(\"@earendil-works/pi-coding-agent\");\n return pi.getSettingsListTheme();\n } catch {\n return {\n label: (text, selected) => (selected ? theme.fg(\"accent\", text) : text),\n value: (text, selected) => (selected ? theme.fg(\"accent\", text) : theme.fg(\"muted\", text)),\n description: (text) => theme.fg(\"dim\", text),\n cursor: theme.fg(\"accent\", \"\u2192 \"),\n hint: (text) => theme.fg(\"dim\", text),\n };\n }\n}\n\n// Single entry behind the settings seam: commands dispatches here and knows\n// nothing about dialogs, file paths, or previews.\nexport async function openSettings(\n ctx: ExtensionCommandContext,\n quotaStatus?: QuotaStatusCoordinator,\n): Promise<void> {\n const file = defaultConfigFile();\n if (ctx.mode === \"tui\" && ctx.hasUI) {\n await openSettingsDialog(ctx, quotaStatus, file);\n return;\n }\n const config = loadProviderConfig(file);\n if (!ctx.hasUI) {\n emitOutput(\n ctx,\n SETTINGS_FIELDS.map((f) => `${f.label}: ${fieldDisplayValue(config, f)}`).join(\"\\n\") +\n `\\nEdit ${file} to change.`,\n );\n return;\n }\n let field = SETTINGS_FIELDS[0];\n if (!field) return;\n if (SETTINGS_FIELDS.length > 1) {\n const labels = SETTINGS_FIELDS.map(\n (f) => `${f.label} (current: ${fieldDisplayValue(config, f)})`,\n );\n const choice = await ctx.ui.select(\"Antigravity settings\", labels);\n if (!choice) return; // dismissed \u2014 change nothing\n const found = SETTINGS_FIELDS[labels.indexOf(choice)];\n if (!found) return;\n field = found;\n }\n const picked = await ctx.ui.select(\n `${field.label} (current: ${fieldDisplayValue(config, field)})`,\n [...field.options],\n );\n if (!picked || !field.options.includes(picked)) return; // dismissed \u2014 change nothing\n if (await applySettingValue(field, picked, file, ctx, quotaStatus)) {\n emitOutput(ctx, `${field.label} set to ${picked}.`);\n } else {\n emitOutput(ctx, `Failed to write ${file}.`, \"error\");\n }\n}\n\nasync function openSettingsDialog(\n ctx: ExtensionCommandContext,\n quotaStatus: QuotaStatusCoordinator | undefined,\n file: string,\n): Promise<void> {\n if (quotaStatus) await quotaStatus.ensurePreview(ctx);\n await ctx.ui.custom<void>(async (tui, theme, _kb, done) => {\n const config = loadProviderConfig(file);\n const items = buildSettingsItems(config);\n const quotaField = SETTINGS_FIELDS.find((f) => f.key === \"quotaFooter\");\n // Live sample of what the footer will show for a mode, using cached\n // (just ensured) quota numbers. Model-aware like the slot itself.\n const paintPreview = (mode: string) => {\n const item = items.find((i) => i.id === \"quotaFooter\");\n if (!item) return;\n const base = quotaField?.description ?? \"\";\n const sample = mode === \"off\" ? \"hidden\" : previewQuotaFooterText(quotaStatus, ctx.model?.id, mode);\n item.description = sample && base ? `${base}: ${sample}` : (sample ?? base);\n };\n paintPreview(resolveFooterMode(config));\n const list = new SettingsList(\n items,\n Math.min(items.length, 10),\n await settingsListTheme(theme),\n (id, newValue) => {\n const field = SETTINGS_FIELDS.find((f) => f.key === id);\n if (!field) return;\n list.invalidate();\n tui.requestRender();\n void applySettingValue(field, newValue, file, ctx, quotaStatus).then(() => {\n paintPreview(newValue);\n list.invalidate();\n tui.requestRender();\n });\n },\n () => done(),\n );\n // Pi-native frame: a border line above and below the list, like\n // /settings (SettingsSelectorComponent wraps its SettingsList in\n // DynamicBorders). Extension custom UI can't reuse pi's separate\n // focus target, so the wrapper delegates input to the inner list.\n const border = {\n render: (width: number) => [theme.fg(\"border\", \"\u2500\".repeat(Math.max(1, width)))],\n };\n return {\n render: (width: number) => [...border.render(width), ...list.render(width), ...border.render(width)],\n handleInput: (data: string) => list.handleInput(data),\n invalidate: () => list.invalidate(),\n };\n });\n}\n", "import {\n type AssistantMessage,\n type AssistantMessageEventStream,\n type Context,\n type Model,\n type SimpleStreamOptions,\n type TextContent,\n type ThinkingContent,\n createAssistantMessageEventStream,\n} from \"@earendil-works/pi-ai\";\nimport { parseStoredCredentials } from \"./auth.ts\";\nimport { createSseFeed, type SseFeedOutput } from \"./parser.ts\";\nimport { buildAntigravityRequestBody } from \"./builder.ts\";\nimport { postAntigravity } from \"./protocol.ts\";\nimport { resolveModelPlan, getCatalogSnapshot } from \"./model-catalog.ts\";\n\nexport function streamAntigravity(\n model: Model<any>,\n context: Context,\n options?: SimpleStreamOptions\n): AssistantMessageEventStream {\n const stream = createAssistantMessageEventStream();\n\n (async () => {\n const output: AssistantMessage = {\n role: \"assistant\",\n content: [],\n api: model.api,\n provider: model.provider,\n model: model.id,\n usage: {\n input: 0,\n output: 0,\n cacheRead: 0,\n cacheWrite: 0,\n totalTokens: 0,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n },\n stopReason: \"pending\",\n timestamp: Date.now(),\n };\n\n try {\n stream.push({ type: \"start\", partial: output });\n\n const rawApiKey = options?.apiKey;\n if (!rawApiKey) {\n throw new Error(\"Missing Antigravity credentials. Run /login antigravity first.\");\n }\n\n const { token, projectId } = parseStoredCredentials(rawApiKey);\n\n const effort = options?.reasoning;\n // trajectoryId is provider-specific (absent from the SDK options\n // type), so read it via runtime-checked `in` narrowing instead of a cast.\n const trajectoryId =\n options && \"trajectoryId\" in options && typeof options.trajectoryId === \"string\"\n ? options.trajectoryId\n : undefined;\n const snapshot = getCatalogSnapshot();\n const plan = resolveModelPlan(model.id, effort, snapshot);\n\n const requestBody = buildAntigravityRequestBody({\n projectId,\n plan,\n context,\n sessionId: options?.sessionId,\n trajectoryId,\n maxOutputTokens: model.maxTokens,\n toolChoice: options?.toolChoice,\n });\n\n const res = await postAntigravity({\n token,\n path: \"v1internal:streamGenerateContent?alt=sse\",\n body: requestBody,\n signal: options?.signal,\n });\n\n if (!res.ok) {\n const errText = await res.text();\n throw new Error(`Antigravity stream failed (${res.status}): ${errText}`);\n }\n\n if (!res.body) {\n throw new Error(\"No response stream received from Antigravity.\");\n }\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n const feed = createSseFeed();\n\n // Single driver for one feed output: the feed owns block events,\n // usage counts, and stopReason. Content lives in the feed's store \u2014\n // every output re-points at the live ref, so close() hands over the\n // final blocks with Thought Signatures already placed (no copy here).\n // The caller only attaches the `partial` live helper and pushes;\n // there is no translation seam. Cost math stays here: it needs\n // model.cost, which the feed never sees.\n const applyFeedOutput = (fed: SseFeedOutput) => {\n output.content = fed.content;\n output.usage.input = fed.usage.input;\n output.usage.cacheRead = fed.usage.cacheRead;\n output.usage.output = fed.usage.output;\n output.usage.totalTokens = fed.usage.total;\n\n if (model.cost) {\n const c = model.cost;\n const inputCost = (output.usage.input * (c.input || 0)) / 1_000_000;\n const outputCost = (output.usage.output * (c.output || 0)) / 1_000_000;\n const cacheCost = (output.usage.cacheRead * (c.cacheRead || 0)) / 1_000_000;\n output.usage.cost = {\n input: inputCost,\n output: outputCost,\n cacheRead: cacheCost,\n cacheWrite: 0,\n total: inputCost + outputCost + cacheCost,\n };\n }\n\n output.stopReason = fed.stopReason;\n for (const ev of fed.events) {\n stream.push({ ...ev, partial: output });\n }\n };\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n applyFeedOutput(feed.feed(decoder.decode(value, { stream: true })));\n }\n\n applyFeedOutput(feed.close());\n\n const doneReason: \"stop\" | \"toolUse\" | \"length\" =\n output.stopReason === \"toolUse\" || output.stopReason === \"length\"\n ? output.stopReason\n : \"stop\";\n\n stream.push({\n type: \"done\",\n reason: doneReason,\n message: output,\n });\n stream.end();\n } catch (error) {\n output.stopReason = options?.signal?.aborted ? \"aborted\" : \"error\";\n output.errorMessage = error instanceof Error ? error.message : String(error);\n stream.push({ type: \"error\", reason: output.stopReason, error: output });\n stream.end();\n }\n })();\n\n return stream;\n}\n", "import type { TextContent, ThinkingContent, ToolCall } from \"@earendil-works/pi-ai\";\n\n/**\n * One assembled content block, Pi-spelled (TextContent | ThinkingContent |\n * ToolCall): thinking blocks carry thinkingSignature, text blocks carry\n * textSignature, toolCalls carry the wire-spelled thoughtSignature.\n * The import is type-only \u2014 the module stays dependency-free at runtime.\n */\nexport type ParsedBlock = TextContent | ThinkingContent | ToolCall;\n\nexport interface ParsedStreamResult {\n content: ParsedBlock[];\n usage: {\n input: number;\n output: number;\n cacheRead: number;\n total: number;\n };\n stopReason: \"stop\" | \"toolUse\" | \"length\" | \"error\";\n}\n\n/**\n * Stream events in pi-ai spelling, minus the `partial` live helper the\n * caller attaches at push time. The feed is the single owner of the\n * response surface \u2014 block events, usage counts, and stopReason all come\n * out of here, so no caller translates between vocabularies. This union\n * must track AssistantMessageEvent (minus start/done/error); the push site\n * in stream.ts fails typecheck if the two drift apart.\n * The import stays type-only \u2014 the module is dependency-free at runtime.\n */\nexport type FeedStreamEvent =\n | { type: \"text_start\" | \"thinking_start\"; contentIndex: number }\n | { type: \"text_delta\" | \"thinking_delta\"; contentIndex: number; delta: string }\n | { type: \"text_end\" | \"thinking_end\"; contentIndex: number; content: string }\n | { type: \"toolcall_start\"; contentIndex: number }\n | { type: \"toolcall_delta\"; contentIndex: number; delta: string }\n | { type: \"toolcall_end\"; contentIndex: number; toolCall: ToolCall };\n\n\nexport interface SseFeedOutput {\n events: FeedStreamEvent[];\n usage: ParsedStreamResult[\"usage\"];\n stopReason: ParsedStreamResult[\"stopReason\"];\n /**\n * Live ref to the single assembled-block store owned by this feed.\n * Every event index is valid into it at the time the output is returned.\n * Read-only for callers: the feed is the sole writer. Signatures are\n * placement-complete only on close() (SDK spelling); no respelling downstream.\n */\n content: ParsedBlock[];\n}\n\n/**\n * Stateful incremental SSE reader: the single module that understands the\n * Antigravity Wire Fingerprint on the wire. Feed raw response chunks as they\n * arrive; line buffering, block accumulation, usage, stopReason, lone\n * Thought Signature attachment, event translation, and the single\n * assembled-block store all live here. Callers attach `partial` and push \u2014\n * never a parallel array, never a second vocabulary. close() is the single\n * final surface: terminal events plus the fully assembled content.\n */\nexport function createSseFeed(): {\n feed(chunk: string): SseFeedOutput;\n close(): SseFeedOutput;\n} {\n const content: ParsedBlock[] = [];\n const usage = {\n input: 0,\n output: 0,\n cacheRead: 0,\n total: 0,\n };\n let stopReason: ParsedStreamResult[\"stopReason\"] = \"stop\";\n let lastThoughtSignature: string | undefined;\n let buffer = \"\";\n let openType: \"text\" | \"thinking\" | null = null;\n\n const snapshot = (events: FeedStreamEvent[]): SseFeedOutput => ({\n events,\n usage: { ...usage },\n stopReason,\n content,\n });\n\n // Lone-signature turn: a Thought Signature that arrived detached from any\n // thinking part rides the first thinking block (SDK spelling), else the\n // last text block. Runs once at close, so the final content carries the\n // placement policy instead of every caller re-deriving it.\n const attachLoneSignature = (): void => {\n if (!lastThoughtSignature) return;\n const thinking = content.find((b): b is ThinkingContent => b.type === \"thinking\");\n if (thinking && !thinking.thinkingSignature) {\n thinking.thinkingSignature = lastThoughtSignature;\n } else if (!thinking) {\n for (let i = content.length - 1; i >= 0; i--) {\n const block = content[i];\n if (block?.type === \"text\" && !block.textSignature) {\n block.textSignature = lastThoughtSignature;\n break;\n }\n }\n }\n };\n\n const closeOpenBlock = (events: FeedStreamEvent[]): void => {\n if (openType === null) return;\n const contentIndex = content.length - 1;\n const block = content[contentIndex];\n if (block?.type === \"thinking\") {\n events.push({ type: \"thinking_end\", contentIndex, content: block.thinking });\n } else if (block?.type === \"text\") {\n events.push({ type: \"text_end\", contentIndex, content: block.text });\n }\n openType = null;\n };\n\n const processLine = (rawLine: string, events: FeedStreamEvent[]): void => {\n const line = rawLine.trim();\n if (!line.startsWith(\"data:\")) return;\n\n const dataStr = line.slice(5).trim();\n if (!dataStr || dataStr === \"[DONE]\") return;\n\n let payload: any;\n try {\n payload = JSON.parse(dataStr);\n } catch {\n return;\n }\n\n const response = payload.response || payload;\n\n // Usage metadata (promptTokenCount includes cached tokens per Google spec).\n // output counts thinking tokens too, mirroring the pi-ai Google adapter:\n // usageMetadata reports candidates and thoughts separately.\n if (response.usageMetadata) {\n const um = response.usageMetadata;\n const promptTokens = typeof um.promptTokenCount === \"number\" ? um.promptTokenCount : 0;\n const cacheRead = typeof um.cachedContentTokenCount === \"number\" ? um.cachedContentTokenCount : 0;\n usage.input = Math.max(0, promptTokens - cacheRead);\n usage.cacheRead = cacheRead;\n const candidates = typeof um.candidatesTokenCount === \"number\" ? um.candidatesTokenCount : 0;\n const thoughts = typeof um.thoughtsTokenCount === \"number\" ? um.thoughtsTokenCount : 0;\n usage.output = candidates + thoughts;\n if (typeof um.totalTokenCount === \"number\") usage.total = um.totalTokenCount;\n }\n\n const candidate = response.candidates?.[0];\n if (!candidate) return;\n\n // Sticky: length/error/toolUse are never downgraded by a later STOP.\n // Mirrors pi-ai mapStopReasonString: only STOP is benign, MAX_TOKENS is\n // length, every other reason (SAFETY, RECITATION, BLOCKLIST, ...) is error.\n if (candidate.finishReason === \"MAX_TOKENS\") {\n stopReason = \"length\";\n } else if (typeof candidate.finishReason === \"string\" && candidate.finishReason !== \"STOP\") {\n stopReason = \"error\";\n }\n\n const parts = candidate.content?.parts || [];\n for (const part of parts) {\n if (part.thoughtSignature) {\n lastThoughtSignature = part.thoughtSignature;\n }\n\n if (part.thought) {\n if (openType !== \"thinking\") {\n closeOpenBlock(events);\n content.push({ type: \"thinking\", thinking: \"\" });\n events.push({ type: \"thinking_start\", contentIndex: content.length - 1 });\n openType = \"thinking\";\n }\n const block = content[content.length - 1];\n if (block?.type !== \"thinking\") continue;\n const delta = part.text || \"\";\n block.thinking += delta;\n if (part.thoughtSignature) {\n block.thinkingSignature = part.thoughtSignature;\n }\n events.push({\n type: \"thinking_delta\",\n contentIndex: content.length - 1,\n delta,\n });\n } else if (part.functionCall) {\n closeOpenBlock(events);\n stopReason = \"toolUse\";\n const block: ToolCall = {\n type: \"toolCall\",\n id: part.functionCall.id || `call_${content.length}`,\n name: part.functionCall.name || \"\",\n arguments: part.functionCall.args || {},\n thoughtSignature: part.thoughtSignature || lastThoughtSignature,\n };\n content.push(block);\n const contentIndex = content.length - 1;\n events.push({ type: \"toolcall_start\", contentIndex });\n events.push({\n type: \"toolcall_delta\",\n contentIndex,\n delta: JSON.stringify(block.arguments),\n });\n events.push({ type: \"toolcall_end\", contentIndex, toolCall: block });\n } else if (part.text !== undefined) {\n if (openType !== \"text\") {\n closeOpenBlock(events);\n content.push({ type: \"text\", text: \"\" });\n events.push({ type: \"text_start\", contentIndex: content.length - 1 });\n openType = \"text\";\n }\n const block = content[content.length - 1];\n if (block?.type !== \"text\") continue;\n block.text += part.text;\n events.push({ type: \"text_delta\", contentIndex: content.length - 1, delta: part.text });\n }\n }\n };\n\n return {\n feed(chunk: string): SseFeedOutput {\n const events: FeedStreamEvent[] = [];\n buffer += chunk;\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\";\n for (const rawLine of lines) processLine(rawLine, events);\n return snapshot(events);\n },\n close(): SseFeedOutput {\n const events: FeedStreamEvent[] = [];\n if (buffer.trim() !== \"\") processLine(buffer, events);\n buffer = \"\";\n closeOpenBlock(events);\n attachLoneSignature();\n return snapshot(events);\n },\n };\n}\n\n", "import { createHash, randomUUID } from \"node:crypto\";\nimport type { ModelPlan } from \"./model-catalog.ts\";\nimport { isCompatibleFamily } from \"./model-catalog.ts\";\nimport { PROVIDER_ID } from \"./protocol.ts\";\n\nexport interface BuildRequestBodyParams {\n projectId: string;\n plan: ModelPlan;\n context: {\n systemPrompt?: string;\n messages: Array<any>;\n tools?: Array<any>;\n };\n sessionId?: string;\n trajectoryId?: string;\n maxOutputTokens?: number;\n toolChoice?: string;\n}\n\nconst BASE64_SIGNATURE_PATTERN = /^[A-Za-z0-9+/_-]+={0,2}$/;\n\n/**\n * Verifies that a thought signature candidate is a valid base64 string suitable\n * for Google Protobuf TYPE_BYTES fields in JSON mapping.\n */\nfunction isValidThoughtSignature(sig: unknown): sig is string {\n if (typeof sig !== \"string\" || sig.length === 0) return false;\n if (sig.includes(\"=\") && sig.length % 4 !== 0) return false;\n return BASE64_SIGNATURE_PATTERN.test(sig);\n}\n\n/**\n * Resolves a thoughtSignature candidate only when coming from the same provider and\n * compatible model family, and only when formatted as valid base64.\n */\nfunction resolveThoughtSignature(\n isSameProviderAndModel: boolean,\n sig: unknown\n): string | undefined {\n if (!isSameProviderAndModel) return undefined;\n return isValidThoughtSignature(sig) ? sig : undefined;\n}\n\n/**\n * Deterministically derives a persistent session ID from the initial conversation turn.\n */\nfunction deriveSessionId(\n context: { messages?: Array<any>; systemPrompt?: string },\n explicitSessionId?: string\n): string {\n if (explicitSessionId && typeof explicitSessionId === \"string\" && explicitSessionId.trim().length > 0) {\n return explicitSessionId.trim();\n }\n\n const firstMsg = context.messages?.[0];\n if (!firstMsg) {\n return randomUUID();\n }\n\n const seedContent =\n typeof firstMsg.content === \"string\"\n ? firstMsg.content\n : JSON.stringify(firstMsg.content || \"\");\n\n const hash = createHash(\"sha256\")\n .update(`antigravity:conversation:${seedContent}`)\n .digest()\n .subarray(0, 16);\n\n hash[6] = (hash[6] & 0x0f) | 0x40; // v4 UUID\n hash[8] = (hash[8] & 0x3f) | 0x80;\n const hex = [...hash].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\n/**\n * Computes deterministic v5 UUID trajectory ID from the session ID.\n */\nfunction resolveSessionTrajectory(sessionId: string): string {\n const bytes = createHash(\"sha1\").update(`antigravity:${sessionId}`).digest().subarray(0, 16);\n bytes[6] = (bytes[6] & 0x0f) | 0x50; // v5 UUID\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = [...bytes].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\n/**\n * Computes signed 64-bit integer session ID matching official agy CLI Wire Fingerprint.\n */\nfunction resolveNumericSessionId(sessionId: string): string {\n if (/^-?\\d+$/.test(sessionId)) {\n return sessionId;\n }\n const hash = createHash(\"sha256\").update(`antigravity:session:${sessionId}`).digest();\n return hash.readBigInt64BE(0).toString();\n}\n\n/**\n * Normalizes tool call IDs for cross-provider compatibility.\n */\nfunction normalizeToolCallId(id: string | undefined): string | undefined {\n if (!id || typeof id !== \"string\") return id;\n return id.replace(/[^a-zA-Z0-9_-]/g, \"_\").slice(0, 64);\n}\n\nconst CUSTOM_TOOL_SCHEMA_ALLOW = new Set([\n \"type\",\n \"description\",\n \"properties\",\n \"required\",\n \"items\",\n \"enum\",\n]);\n\nfunction stripMetaSchema(schema: unknown): unknown {\n if (!schema || typeof schema !== \"object\" || Array.isArray(schema)) return schema;\n const omit = new Set([\"$schema\", \"$id\", \"$defs\", \"definitions\"]);\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(schema)) {\n if (!omit.has(key)) out[key] = stripMetaSchema(value);\n }\n return out;\n}\n\nfunction normalizeCustomToolSchema(schema: unknown): unknown {\n if (!schema || typeof schema !== \"object\") return schema;\n if (Array.isArray(schema)) return schema.map(normalizeCustomToolSchema);\n\n const s = schema as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(s)) {\n if (!CUSTOM_TOOL_SCHEMA_ALLOW.has(key)) {\n // Map string const to single-item enum for Claude bridge compatibility\n if (key === \"const\" && s.enum === undefined && typeof value === \"string\") {\n out.enum = [value];\n }\n continue;\n }\n if (key === \"type\" && Array.isArray(value)) {\n const scalar = value.find((e) => typeof e === \"string\" && e !== \"null\");\n if (scalar) out.type = scalar;\n continue;\n }\n if (key === \"properties\" && value && typeof value === \"object\" && !Array.isArray(value)) {\n const props: Record<string, unknown> = {};\n for (const [propName, propSchema] of Object.entries(value as Record<string, unknown>)) {\n props[propName] = normalizeCustomToolSchema(propSchema);\n }\n out.properties = props;\n continue;\n }\n if (key === \"enum\" && Array.isArray(value) && !value.every((e) => typeof e === \"string\")) {\n continue;\n }\n out[key] = normalizeCustomToolSchema(value);\n }\n return out;\n}\n\n/**\n * Strict agy parity: versioned captures (1.1.26/1.1.27, every declaration)\n * always use legacy `parameters`, never `parametersJsonSchema` \u2014 for Gemini\n * and non-Gemini models alike. Captured declarations also carry no\n * anyOf/patternProperties/const keys, hence the normalization below.\n */\nfunction convertTools(\n tools: Array<any> | undefined\n): Array<{ functionDeclarations: Array<any> }> | undefined {\n if (!tools?.length) return undefined;\n return [\n {\n functionDeclarations: tools.map((tool) => {\n const schema = stripMetaSchema(tool.parameters) || { type: \"object\", properties: {} };\n return {\n name: tool.name,\n description: tool.description,\n parameters: normalizeCustomToolSchema(schema),\n };\n }),\n },\n ];\n}\n\n/**\n * Translates one Turn Trace (see CONTEXT.md) into Antigravity wire contents.\n */\nfunction translateTurnTrace(\n context: { messages?: Array<any>; systemPrompt?: string },\n runtimeModelId: string\n): Array<any> {\n const contents: Array<any> = [];\n const messages = context.messages || [];\n\n for (const msg of messages) {\n if (msg.role === \"user\") {\n const parts: Array<any> = [];\n if (typeof msg.content === \"string\") {\n parts.push({ text: msg.content });\n } else if (Array.isArray(msg.content)) {\n for (const item of msg.content) {\n if (item.type === \"text\") {\n parts.push({ text: item.text });\n } else if (item.type === \"image\") {\n parts.push({\n inlineData: {\n mimeType: item.mimeType || \"image/png\",\n data: item.data,\n },\n });\n }\n }\n }\n if (parts.length > 0) {\n contents.push({ role: \"user\", parts });\n }\n } else if (msg.role === \"assistant\") {\n if (msg.stopReason === \"error\" || msg.stopReason === \"aborted\") {\n continue;\n }\n\n const isSameProvider = !msg.provider || msg.provider === PROVIDER_ID;\n const isSameModel = isCompatibleFamily(msg.model, runtimeModelId);\n const isSameProviderAndModel = isSameProvider && isSameModel;\n\n const parts: Array<any> = [];\n // agy CLI shape (1.1.27 turn4 fixture): a thinking replay never carries its\n // own signature \u2014 it stays pending and rides on the NEXT text/functionCall\n // part. Only when the turn ends with no carrying part does it fall back\n // onto the last part (uncovered edge, same as the message-level fallback).\n // Signature fields are canonical SDK spellings only (textSignature /\n // thinkingSignature on blocks, thoughtSignature on toolCalls). No\n // legacy-spelling reads: pre-#26 sessions are abandoned (see #26).\n let pendingThinkingSig: string | undefined;\n if (typeof msg.content === \"string\") {\n parts.push({ text: msg.content });\n } else if (Array.isArray(msg.content)) {\n for (const item of msg.content) {\n if (item.type === \"text\") {\n if (typeof item.text !== \"string\" || item.text === \"\") {\n // Vacuous text block (e.g. a leading empty part the live feed\n // opened): drop the part but keep a valid signature pending\n // for the next part carrying one, mirroring the precedence\n // below. The Claude backend rejects {\"text\": \"\"} with 400\n // (text.text: Field required, observed live on multi-turn\n // replay); Gemini accepts the same payload (probed 200), but\n // an empty part carries nothing either way, and agy never\n // emits one, so this stays regardless of model family.\n pendingThinkingSig =\n resolveThoughtSignature(isSameProviderAndModel, item.textSignature) ||\n pendingThinkingSig;\n continue;\n }\n const part: any = { text: item.text };\n const sig =\n resolveThoughtSignature(\n isSameProviderAndModel,\n item.textSignature\n ) || pendingThinkingSig;\n if (sig) {\n part.thoughtSignature = sig;\n pendingThinkingSig = undefined;\n }\n parts.push(part);\n } else if (item.type === \"thinking\") {\n const candidateSig = item.thinkingSignature;\n const isForeignReasoning =\n !isSameProviderAndModel ||\n (typeof candidateSig === \"string\" && candidateSig.trim().startsWith(\"{\"));\n\n if (!isForeignReasoning) {\n // No thoughtSignature here by design: it stays pending for the\n // next text/functionCall part (agy CLI part-split shape).\n const part: any = {\n thought: true,\n text: item.thinking || \"\",\n };\n const sig = resolveThoughtSignature(true, candidateSig);\n if (sig) {\n pendingThinkingSig = sig;\n }\n parts.push(part);\n } else {\n // Cross-provider/model or foreign reasoning item: drop foreign signature and\n // serialize reasoning text (or summary) as plain text so the model retains\n // conversation context without failing Google's TYPE_BYTES base64 validation.\n let text = item.thinking || \"\";\n if (!text && typeof candidateSig === \"string\" && candidateSig.trim().startsWith(\"{\")) {\n try {\n const parsed = JSON.parse(candidateSig);\n if (Array.isArray(parsed.summary)) {\n text = parsed.summary\n .filter((s: any) => s && typeof s.text === \"string\")\n .map((s: any) => s.text)\n .join(\"\\n\");\n }\n } catch {\n // Ignore JSON parse errors\n }\n }\n if (text && text.trim() !== \"\") {\n parts.push({ text });\n }\n }\n } else if (item.type === \"tool_use\" || item.type === \"toolCall\") {\n const part: any = {\n functionCall: {\n id: normalizeToolCallId(item.id),\n name: item.name,\n args: item.input || item.arguments || {},\n },\n };\n const sig =\n resolveThoughtSignature(\n isSameProviderAndModel,\n item.thoughtSignature\n ) || pendingThinkingSig;\n if (sig) {\n part.thoughtSignature = sig;\n pendingThinkingSig = undefined;\n }\n parts.push(part);\n }\n }\n }\n if (parts.length > 0) {\n if (pendingThinkingSig && !parts.some((p) => p.thoughtSignature)) {\n parts[parts.length - 1].thoughtSignature = pendingThinkingSig;\n }\n contents.push({ role: \"model\", parts });\n }\n } else if (msg.role === \"toolResult\") {\n let toolName = msg.toolName || msg.name;\n if (!toolName && (msg.toolCallId || msg.id)) {\n const rawTargetId = msg.toolCallId || msg.id;\n const normTargetId = normalizeToolCallId(rawTargetId);\n for (const prev of messages) {\n if (prev.role === \"assistant\" && Array.isArray(prev.content)) {\n const found = prev.content.find(\n (c: any) =>\n (c.type === \"tool_use\" || c.type === \"toolCall\") &&\n (c.id === rawTargetId || normalizeToolCallId(c.id) === normTargetId)\n );\n if (found) {\n toolName = found.name;\n break;\n }\n }\n }\n }\n\n let toolText = \"\";\n const imageParts: Array<any> = [];\n\n if (Array.isArray(msg.content)) {\n const textParts: string[] = [];\n for (const c of msg.content) {\n if (typeof c === \"string\") {\n textParts.push(c);\n } else if (c.type === \"text\") {\n textParts.push(c.text);\n } else if (c.type === \"image\") {\n imageParts.push({\n inlineData: {\n mimeType: c.mimeType || \"image/png\",\n data: c.data,\n },\n });\n } else {\n textParts.push(JSON.stringify(c));\n }\n }\n toolText = textParts.join(\"\\n\");\n } else if (typeof msg.content === \"string\") {\n toolText = msg.content;\n } else if (msg.content) {\n toolText = JSON.stringify(msg.content);\n }\n\n // Strict agy parity (captured 2026-09-05 via mitmdump, agy 1.1.26):\n // - response is always { output: string }, never a top-level list.\n // - error tool results also use the \"output\" key.\n const responseObj = { output: toolText };\n\n const functionResponse: any = {\n name: toolName || \"tool\",\n response: responseObj,\n };\n\n const rawCallId = msg.toolCallId || msg.id;\n const callId = normalizeToolCallId(rawCallId);\n if (callId) {\n functionResponse.id = callId;\n }\n\n if (imageParts.length > 0) {\n functionResponse.parts = imageParts;\n }\n\n contents.push({\n role: \"model\",\n parts: [{ functionResponse }],\n });\n }\n }\n\n // Antigravity rejects requests ending with an incomplete model turn.\n if (contents.length > 0) {\n const lastContent = contents[contents.length - 1];\n if (\n lastContent.role === \"model\" &&\n !lastContent.parts?.some((p: any) => p.functionResponse)\n ) {\n contents.push({\n role: \"user\",\n parts: [{ text: \"continue\" }],\n });\n }\n }\n\n return contents;\n}\n\n/**\n * Builds the complete Antigravity Wire Fingerprint request envelope.\n * Encapsulates session identity derivation, Thought Signature replay,\n * Turn Trace translation, and tool schema normalization.\n */\nexport function buildAntigravityRequestBody(params: BuildRequestBodyParams): Record<string, any> {\n const {\n projectId,\n plan,\n context,\n maxOutputTokens = 65536,\n } = params;\n const runtimeModelId = plan.runtimeModelId;\n\n const sessionId = deriveSessionId(context, params.sessionId);\n const trajectoryId =\n params.trajectoryId && typeof params.trajectoryId === \"string\" && params.trajectoryId.trim().length > 0\n ? params.trajectoryId.trim()\n : resolveSessionTrajectory(sessionId);\n const contents = translateTurnTrace(context, runtimeModelId);\n\n // Request ID sequence increments by completed assistant turns\n const requestIndex = context.messages.filter((m) => m.role === \"assistant\").length;\n const requestId = `agent/${sessionId}/${Date.now()}/${trajectoryId}/${contents.length}`;\n\n const modelEnum = plan.modelEnum;\n\n const labels: Record<string, string> = {\n last_step_index: String(Math.max(0, contents.length - 1)),\n model_enum: modelEnum,\n request_id: `${trajectoryId}-${requestIndex}`,\n trajectory_id: trajectoryId,\n used_claude: String(plan.isClaude),\n used_claude_conservative: String(plan.isClaude),\n used_non_gemini_model: String(plan.isNonGemini),\n };\n\n const generationConfig: Record<string, any> = {\n maxOutputTokens,\n thinkingConfig: plan.thinkingConfig,\n };\n\n const request: Record<string, any> = {\n contents,\n };\n\n if (context.systemPrompt) {\n request.systemInstruction = {\n role: \"user\",\n parts: [{ text: context.systemPrompt }],\n };\n }\n\n const tools = convertTools(context.tools);\n if (tools) {\n request.tools = tools;\n }\n\n if (params.toolChoice && params.toolChoice !== \"auto\" && params.toolChoice !== \"Auto\") {\n let mode = \"AUTO\";\n const tc = String(params.toolChoice).toLowerCase();\n if (tc === \"none\") mode = \"NONE\";\n else if (tc === \"any\" || tc === \"required\") mode = \"ANY\";\n request.toolConfig = {\n functionCallingConfig: {\n mode,\n },\n };\n }\n\n request.labels = labels;\n request.generationConfig = generationConfig;\n request.sessionId = resolveNumericSessionId(sessionId);\n\n return {\n project: projectId,\n requestId,\n request,\n model: runtimeModelId,\n userAgent: \"antigravity\",\n requestType: \"agent\",\n };\n}\n", "import type { ExtensionCommandContext } from \"@earendil-works/pi-coding-agent\";\nimport { parseStoredCredentials } from \"./auth.ts\";\nimport { PROVIDER_ID } from \"./protocol.ts\";\nimport {\n formatQuotaSummary,\n paintQuotaStatus,\n type QuotaStatusCoordinator,\n} from \"./quota-status.ts\";\nimport {\n formatModelsList,\n getCatalogSnapshot,\n getStoredCatalog,\n} from \"./model-catalog.ts\";\n\nimport { emitOutput, openSettings } from \"./settings.ts\";\n\nconst USAGE_TEXT =\n \"Usage: /antigravity <command>\\n\\nCommands:\\n usage Show 5h and weekly quota pool limits\\n models List recommended models with context window and remaining quota (alias: model)\\n refresh Force refresh model catalog\\n settings Pick provider settings: Enter cycles values (alias: setting)\\n login Run /login antigravity\";\n\nexport async function resolveToken(\n ctx: ExtensionCommandContext\n): Promise<{ token: string; projectId: string } | null> {\n const apiKey = await ctx.modelRegistry?.getApiKeyForProvider?.(PROVIDER_ID);\n if (!apiKey) return null;\n return parseStoredCredentials(apiKey);\n}\n\nasync function runModelsSubcommand(ctx: ExtensionCommandContext): Promise<void> {\n if ((await resolveToken(ctx)) === null) {\n emitOutput(ctx, \"Not logged in. Run /login antigravity first.\", \"warning\");\n return;\n }\n try {\n if (ctx.hasUI) ctx.ui.notify(\"Fetching available models\u2026\", \"info\");\n // Single Model Catalog path: the refresh owns fetch\u2192snapshot\u2192publish and\n // the table formats the generation it ingested. refreshCatalog swallows\n // fetch errors internally, so a snapshot version bump is the only proof\n // a fresh generation landed \u2014 no bump means the fetch failed, and the\n // failure is reported instead of showing the retained stale generation.\n const before = getCatalogSnapshot().version;\n await ctx.modelRegistry?.refresh?.({ force: true, providers: [PROVIDER_ID], signal: ctx.signal });\n const catalog = getStoredCatalog();\n if (!catalog || getCatalogSnapshot().version === before) {\n // Refresh failed: say so, but still show the retained generation when\n // one exists \u2014 a stale list beats no list, as long as it is labeled.\n if (catalog) {\n emitOutput(ctx, \"Failed to refresh models, showing last known list.\", \"warning\");\n emitOutput(ctx, formatModelsList(catalog));\n return;\n }\n throw new Error(\"no new generation received\");\n }\n emitOutput(ctx, formatModelsList(catalog));\n } catch (err: any) {\n emitOutput(ctx, `Failed to fetch models: ${err.message}`, \"error\");\n }\n}\n\nexport function parseAntigravitySubcommand(args: string): string {\n const sub = (args || \"\").trim().toLowerCase();\n // Aliases: \"model\" \u2192 \"models\", \"setting\" \u2192 \"settings\"\n if (sub === \"model\") return \"models\";\n if (sub === \"setting\") return \"settings\";\n return sub;\n}\n\nasync function runUsageSubcommand(\n ctx: ExtensionCommandContext,\n quotaStatus?: QuotaStatusCoordinator,\n): Promise<void> {\n if ((await resolveToken(ctx)) === null) {\n emitOutput(ctx, \"Not logged in. Run /login antigravity first.\", \"warning\");\n return;\n }\n if (!quotaStatus) {\n emitOutput(ctx, \"Quota status is unavailable.\", \"error\");\n return;\n }\n try {\n if (ctx.hasUI) ctx.ui.notify(\"Fetching quota summary\u2026\", \"info\");\n // Explicit look at quota: bypass the mode and model gates, but share the\n // fetch, calibration, and persistence with the footer \u2014 no redundant\n // fetch, no stale footer.\n const summary = await quotaStatus.refresh(ctx, { force: true, ignoreMode: true, signal: ctx.signal });\n if (!summary) {\n emitOutput(ctx, \"Failed to fetch usage.\", \"error\");\n return;\n }\n paintQuotaStatus(quotaStatus, ctx);\n emitOutput(ctx, formatQuotaSummary(summary));\n } catch (err: any) {\n emitOutput(ctx, `Failed to fetch usage: ${err.message}`, \"error\");\n }\n}\n\nexport async function runAntigravitySubcommand(\n args: string,\n ctx: ExtensionCommandContext,\n quotaStatus?: QuotaStatusCoordinator,\n): Promise<void> {\n const parts = (args || \"\").trim().split(/\\s+/).filter(Boolean);\n const sub = parseAntigravitySubcommand(parts[0] || \"\");\n\n if (sub === \"usage\") {\n await runUsageSubcommand(ctx, quotaStatus);\n return;\n }\n\n if (sub === \"models\") {\n await runModelsSubcommand(ctx);\n return;\n }\n\n if (sub === \"settings\") {\n await openSettings(ctx, quotaStatus);\n return;\n }\n\n if (sub === \"refresh\") {\n try {\n if (ctx.hasUI) ctx.ui.notify(\"Refreshing models\u2026\", \"info\");\n await ctx.modelRegistry?.refresh?.({ force: true, providers: [PROVIDER_ID], signal: ctx.signal });\n emitOutput(ctx, \"Antigravity models refreshed successfully.\");\n } catch (err: any) {\n emitOutput(ctx, `Failed to refresh: ${err.message}`, \"error\");\n }\n return;\n }\n\n if (sub === \"login\") {\n if (ctx.hasUI) {\n ctx.ui.setEditorText(\"/login antigravity\");\n ctx.ui.notify(\"Press Enter to log in to Antigravity.\", \"info\");\n } else {\n emitOutput(ctx, \"Please run /login antigravity to authenticate.\");\n }\n return;\n }\n\n emitOutput(ctx, USAGE_TEXT);\n}\n", "import type { ExtensionAPI, ExtensionCommandContext } from \"@earendil-works/pi-coding-agent\";\nimport { loginAntigravity, refreshAntigravityToken, getApiKey } from \"./auth.ts\";\nimport { DEFAULT_ENDPOINT, PROVIDER_ID } from \"./protocol.ts\";\nimport { refreshCatalog } from \"./catalog-refresh.ts\";\nimport { QuotaStatusCoordinator } from \"./quota-status.ts\";\nimport { fileQuotaStatusStore } from \"./settings.ts\";\nimport { streamAntigravity } from \"./stream.ts\";\nimport { runAntigravitySubcommand } from \"./commands.ts\";\n\nexport { PROVIDER_ID };\nexport const PROVIDER_NAME = \"Antigravity\";\n\nconst SUBCOMMANDS = [\n { name: \"usage\", description: \"Show 5h and weekly quota pool limits\" },\n { name: \"models\", description: \"List recommended models with context window and remaining quota\" },\n { name: \"refresh\", description: \"Force refresh model catalog\" },\n { name: \"settings\", description: \"Pick provider settings: Enter cycles values\" },\n { name: \"login\", description: \"Run /login antigravity\" },\n];\n\nexport default function (pi: ExtensionAPI): void {\n const quotaStatus = new QuotaStatusCoordinator(fileQuotaStatusStore());\n\n pi.registerProvider(PROVIDER_ID, {\n name: PROVIDER_NAME,\n baseUrl: DEFAULT_ENDPOINT,\n api: \"antigravity-api\",\n models: [], // Purely dynamic provider per Pi SDK architecture\n oauth: {\n name: PROVIDER_NAME,\n login: loginAntigravity,\n refreshToken: refreshAntigravityToken,\n getApiKey,\n },\n streamSimple: streamAntigravity,\n refreshModels: async (context: any) => refreshCatalog(context),\n });\n\n pi.registerCommand(\"antigravity\", {\n description: \"Antigravity quota, models and settings\",\n getArgumentCompletions: (prefix) => {\n const trimmed = prefix.trim();\n if (trimmed === \"\") {\n return SUBCOMMANDS.map((s) => ({ value: s.name, label: s.name, description: s.description }));\n }\n if (/\\s/.test(trimmed)) return null;\n const lower = trimmed.toLowerCase();\n return SUBCOMMANDS.filter((s) => s.name.startsWith(lower)).map((s) => ({\n value: s.name,\n label: s.name,\n description: s.description,\n }));\n },\n handler: async (args: string, ctx: ExtensionCommandContext) => {\n await runAntigravitySubcommand(args, ctx, quotaStatus);\n },\n });\n\n // Footer quota slot: paint cached text instantly, refresh in background.\n pi.on(\"session_start\", async (_event, ctx) => {\n await quotaStatus.refreshAndPaint(ctx);\n });\n\n // Model switch changes which Quota Pool backs the footer \u2014 repaint instantly,\n // then fill in fresh text when switching (back) to an Antigravity model.\n pi.on(\"model_select\", async (event, ctx) => {\n const modelCtx = {\n ui: ctx.ui,\n modelRegistry: ctx.modelRegistry,\n model: event.model || ctx.model,\n };\n await quotaStatus.refreshAndPaint(modelCtx);\n });\n\n // Turn settled: refresh only when the throttle window expired.\n pi.on(\"agent_settled\", async (_event, ctx) => {\n await quotaStatus.refreshAndPaint(ctx);\n });\n}\n"],
5
- "mappings": ";AAAA,SAAS,YAAY,mBAAmB;;;ACAjC,IAAM,mBAAmB;AACzB,IAAM,qBACX;AAEK,IAAM,cAAc;AAgB3B,eAAsB,gBAAgB;AAAA,EACpC;AAAA,EACA,MAAAA;AAAA,EACA;AAAA,EACA;AACF,GAA6C;AAC3C,SAAO,MAAM,GAAG,gBAAgB,IAAIA,KAAI,IAAI;AAAA,IAC1C,QAAQ;AAAA,IACR,SAAS,wBAAwB,KAAK;AAAA,IACtC,MAAM,KAAK,UAAU,IAAI;AAAA,IACzB;AAAA,EACF,CAAC;AACH;AAKO,SAAS,wBAAwB,OAAe,YAAY,oBAA4C;AAC7G,SAAO;AAAA,IACL,MAAM;AAAA,IACN,cAAc;AAAA,IACd,gBAAgB;AAAA,IAChB,eAAe,UAAU,KAAK;AAAA,EAChC;AACF;;;ADxCO,IAAM,eAAe;AACrB,IAAM,WAAW;AACjB,IAAM,YAAY;AAClB,IAAM,4BAA4B,IAAI,KAAK;AAC3C,IAAM,sBAAsB;AAE5B,IAAM,SAAS;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,YACX,QAAQ,IAAI,yBACZ,OAAO;AAAA,EACL;AAAA,EACA;AACF,EAAE,SAAS,OAAO;AAEb,IAAM,gBACX,QAAQ,IAAI,6BACZ,OAAO,KAAK,oDAAoD,QAAQ,EAAE,SAAS,OAAO;AAE5F,SAAS,UAAU,KAAqB;AACtC,SAAO,IAAI,SAAS,WAAW;AACjC;AAEO,SAAS,qBAAqB,UAA0B;AAC7D,MAAI,OAAO,SAAS,KAAK;AACzB,MAAI,CAAC,MAAM;AACT,UAAM,IAAI,MAAM,gCAAgC;AAAA,EAClD;AAEA,MAAI,KAAK,WAAW,OAAO,GAAG;AAC5B,WAAO,KAAK,MAAM,CAAC,EAAE,KAAK;AAC1B,QAAI,CAAC,MAAM;AACT,YAAM,IAAI,MAAM,gCAAgC;AAAA,IAClD;AAAA,EACF;AAMA,MAAI,cAAc,KAAK,IAAI,GAAG;AAC5B,UAAM,IAAI,MAAM,+EAA+E;AAAA,EACjG;AAEA,SAAO;AACT;AAEA,SAAS,eAAwD;AAC/D,QAAM,WAAW,UAAU,YAAY,EAAE,CAAC;AAC1C,QAAM,YAAY,UAAU,WAAW,QAAQ,EAAE,OAAO,QAAQ,EAAE,OAAO,CAAC;AAC1E,SAAO,EAAE,UAAU,UAAU;AAC/B;AAEA,eAAsB,eAAe,OAAe,QAAuC;AACzF,MAAI;AACF,UAAM,MAAM,MAAM,gBAAgB;AAAA,MAChC;AAAA,MACA,MAAM;AAAA,MACN,MAAM,EAAE,UAAU,EAAE,SAAS,cAAc,EAAE;AAAA,MAC7C;AAAA,IACF,CAAC;AACD,QAAI,IAAI,IAAI;AACV,YAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,UAAI,KAAK,yBAAyB;AAChC,eAAO,KAAK;AAAA,MACd;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAEA,eAAsB,iBAAiB,WAA2D;AAChG,QAAM,EAAE,UAAU,UAAU,IAAI,aAAa;AAG7C,QAAM,QAAQ,YAAY,EAAE,EAAE,SAAS,KAAK;AAE5C,QAAM,aAAa,IAAI,gBAAgB;AAAA,IACrC,WAAW;AAAA,IACX,eAAe;AAAA,IACf,cAAc;AAAA,IACd,OAAO,OAAO,KAAK,GAAG;AAAA,IACtB,gBAAgB;AAAA,IAChB,uBAAuB;AAAA,IACvB;AAAA,IACA,aAAa;AAAA,IACb,QAAQ;AAAA,EACV,CAAC;AAED,QAAM,UAAU,GAAG,QAAQ,IAAI,WAAW,SAAS,CAAC;AACpD,YAAU,OAAO,EAAE,KAAK,QAAQ,CAAC;AAEjC,QAAM,uBAAuB,YAAY;AACvC,UAAM,QAAQ,MAAM,UAAU,SAAS;AAAA,MACrC,SAAS;AAAA,IACX,CAAC;AACD,WAAO,qBAAqB,KAAK;AAAA,EACnC,GAAG;AAEH,QAAM,UAAU,IAAI;AAAA,IAAe,CAAC,GAAG,QACrC,WAAW,MAAM,IAAI,IAAI,MAAM,sDAAsD,CAAC,GAAG,yBAAyB;AAAA,EACpH;AAEA,QAAM,OAAO,MAAM,QAAQ,KAAK,CAAC,qBAAqB,OAAO,CAAC;AAE9D,QAAM,cAAc,IAAI,gBAAgB;AAAA,IACtC,WAAW;AAAA,IACX,eAAe;AAAA,IACf;AAAA,IACA,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,eAAe;AAAA,EACjB,CAAC;AAED,QAAM,WAAW,MAAM,MAAM,WAAW;AAAA,IACtC,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,IAC/D,MAAM,YAAY,SAAS;AAAA,EAC7B,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,UAAU,MAAM,SAAS,KAAK;AACpC,UAAM,IAAI,MAAM,6BAA6B,SAAS,MAAM,MAAM,OAAO,EAAE;AAAA,EAC7E;AAEA,QAAM,SAAU,MAAM,SAAS,KAAK;AAKpC,QAAM,YAAY,MAAM,eAAe,OAAO,YAAY;AAE1D,SAAO;AAAA,IACL,SAAS,OAAO,iBAAiB;AAAA,IACjC,QAAQ,KAAK,UAAU,EAAE,OAAO,OAAO,cAAc,UAAU,CAAC;AAAA,IAChE,SAAS,KAAK,IAAI,KAAK,OAAO,cAAc,QAAQ;AAAA,EACtD;AACF;AAEA,eAAsB,wBACpB,aACA,QAC2B;AAC3B,QAAM,SAAS,IAAI,gBAAgB;AAAA,IACjC,WAAW;AAAA,IACX,eAAe;AAAA,IACf,eAAe,YAAY;AAAA,IAC3B,YAAY;AAAA,EACd,CAAC;AAED,QAAM,MAAM,MAAM,MAAM,WAAW;AAAA,IACjC,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,IAC/D,MAAM,OAAO,SAAS;AAAA,IACtB;AAAA,EACF,CAAC;AAED,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,UAAU,MAAM,IAAI,KAAK;AAC/B,UAAM,IAAI,MAAM,4BAA4B,IAAI,MAAM,MAAM,OAAO,EAAE;AAAA,EACvE;AAEA,QAAM,SAAU,MAAM,IAAI,KAAK;AAM/B,MAAI,oBAAoB;AACxB,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,YAAY,MAAM;AAC5C,QAAI,OAAO,UAAW,qBAAoB,OAAO;AAAA,EACnD,QAAQ;AAAA,EAER;AAKA,QAAM,iBAAiB,MAAM,eAAe,OAAO,cAAc,MAAM;AACvE,QAAM,YAAY,mBAAmB,sBAAsB,iBAAiB;AAE5E,SAAO;AAAA,IACL,SAAS,OAAO,iBAAiB,YAAY;AAAA,IAC7C,QAAQ,KAAK,UAAU,EAAE,OAAO,OAAO,cAAc,UAAU,CAAC;AAAA,IAChE,SAAS,KAAK,IAAI,KAAK,OAAO,cAAc,QAAQ;AAAA,EACtD;AACF;AAEO,SAAS,UAAU,aAAuC;AAC/D,SAAO,YAAY;AACrB;AAEO,SAAS,uBAAuB,WAAyD;AAC9F,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,SAAS;AACnC,QAAI,OAAO,OAAO;AAChB,aAAO,EAAE,OAAO,OAAO,OAAO,WAAW,OAAO,aAAa,oBAAoB;AAAA,IACnF;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO,EAAE,OAAO,WAAW,WAAW,oBAAoB;AAC5D;;;AE7MO,SAAS,mBAAmB,WAA2B;AAC5D,MAAI,cAAc,mBAAoB,QAAO;AAC7C,MAAI,UAAU,WAAW,iBAAiB,EAAG,QAAO;AAEpD,SAAO,UAAU,QAAQ,yDAAyD,EAAE;AACtF;AAoBO,SAAS,oBAAoB,SAA+B;AACjE,QAAM,SAAS,WAAW,IAAI,MAAM,GAAG,EAAE,IAAI,KAAK,IAAI,YAAY;AAClE,MAAI,KAAK,WAAW,SAAS,EAAG,QAAO;AACvC,MAAI,KAAK,WAAW,SAAS,EAAG,QAAO;AACvC,MAAI,KAAK,WAAW,MAAM,EAAG,QAAO;AACpC,SAAO;AACT;AAEO,SAAS,mBAAmB,UAAmB,eAAiC;AACrF,MAAI,CAAC,YAAY,CAAC,cAAe,QAAO;AACxC,MAAI,aAAa,cAAe,QAAO;AAEvC,QAAM,YAAY,oBAAoB,QAAQ;AAC9C,MAAI,cAAc,aAAa,cAAc,oBAAoB,aAAa,EAAG,QAAO;AAExF,SAAO,mBAAmB,QAAQ,MAAM,mBAAmB,aAAa;AAC1E;AAEO,SAAS,kBAAkB,SAAiB,QAAuE;AACxH,QAAM,QAAQ,WAAW,SAAS,WAAW;AAC7C,MAAI,OAAO;AACT,WAAO,EAAE,iBAAiB,OAAO,gBAAgB,EAAE;AAAA,EACrD;AAGA,MAAI,QAAQ,SAAS,OAAO,GAAG;AAC7B,QAAI,WAAW,UAAU,WAAW,WAAW,QAAQ,SAAS,OAAO,KAAK,QAAQ,SAAS,QAAQ,GAAG;AACtG,aAAO,EAAE,iBAAiB,MAAM,gBAAgB,GAAG;AAAA,IACrD;AACA,QAAI,WAAW,YAAY,QAAQ,SAAS,SAAS,GAAG;AACtD,aAAO,EAAE,iBAAiB,MAAM,gBAAgB,IAAK;AAAA,IACvD;AACA,QAAI,WAAW,SAAS,WAAW,aAAa,QAAQ,SAAS,MAAM,GAAG;AACxE,aAAO,EAAE,iBAAiB,MAAM,gBAAgB,IAAK;AAAA,IACvD;AACA,WAAO,EAAE,iBAAiB,MAAM,gBAAgB,GAAG;AAAA,EACrD;AAGA,MAAI,QAAQ,SAAS,KAAK,GAAG;AAC3B,QAAI,WAAW,SAAS,WAAW,aAAa,QAAQ,SAAS,MAAM,GAAG;AACxE,aAAO,EAAE,iBAAiB,MAAM,gBAAgB,KAAK;AAAA,IACvD;AACA,WAAO,EAAE,iBAAiB,MAAM,gBAAgB,MAAM;AAAA,EACxD;AAGA,MAAI,QAAQ,WAAW,SAAS,GAAG;AACjC,WAAO,EAAE,iBAAiB,MAAM,gBAAgB,KAAK;AAAA,EACvD;AAGA,MAAI,QAAQ,WAAW,UAAU,GAAG;AAClC,WAAO,EAAE,iBAAiB,MAAM,gBAAgB,KAAK;AAAA,EACvD;AAEA,SAAO,EAAE,iBAAiB,MAAM,gBAAgB,GAAG;AACrD;AA4CO,SAAS,iBAAiB,QAA+D;AAC9F,SAAO,OAAO;AAAA,IACZ,OAAO,IAAI,CAAC,MAAM;AAAA,MAChB,EAAE;AAAA,MACF;AAAA,QACE,GAAI,OAAO,EAAE,mBAAmB,WAAW,EAAE,QAAQ,EAAE,eAAe,IAAI,CAAC;AAAA,QAC3E,kBAAkB,EAAE,oBAAoB;AAAA,MAC1C;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEA,IAAI,cAA+B;AAAA,EACjC,OAAO,CAAC;AAAA,EACR,YAAY,CAAC;AAAA,EACb,UAAU,CAAC;AAAA,EACX,YAAY,CAAC;AAAA,EACb,SAAS;AACX;AAEO,SAAS,qBAAsC;AACpD,SAAO;AAAA,IACL,OAAO,EAAE,GAAG,YAAY,MAAM;AAAA,IAC9B,YAAY,CAAC,GAAG,YAAY,UAAU;AAAA,IACtC,UAAU,OAAO;AAAA,MACf,OAAO,QAAQ,YAAY,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC;AAAA,IAClF;AAAA,IACA,YAAY,EAAE,GAAI,YAAY,cAAc,CAAC,EAAG;AAAA,IAChD,SAAS,YAAY;AAAA,EACvB;AACF;AAOO,SAAS,mBACd,OACA,YACA,WAA4C,CAAC,GAC7C,aAAqC,CAAC,GAChC;AAGN,gBAAc;AAAA,IACZ,OAAO,EAAE,GAAG,MAAM;AAAA,IAClB,YAAY,CAAC,GAAG,IAAI,IAAI,UAAU,CAAC;AAAA,IACnC,UAAU,EAAE,GAAG,SAAS;AAAA,IACxB,YAAY,EAAE,GAAG,WAAW;AAAA,IAC5B,SAAS,YAAY,UAAU;AAAA,EACjC;AACF;AAKA,IAAI;AAQG,SAAS,cAAc,SAAuC;AACnE;AAAA,IACE,QAAQ;AAAA,IACR,QAAQ,OAAO,IAAI,CAAC,MAAM,EAAE,EAAE;AAAA,IAC9B,iBAAiB,QAAQ,MAAM;AAAA,IAC/B,QAAQ,cAAc,CAAC;AAAA,EACzB;AACA,gBAAc;AAChB;AAGO,SAAS,mBAAuD;AACrE,SAAO;AACT;AAEA,SAAS,sBACP,SACA,QACA,sBAAgC,YAAY,YACpC;AACR,MACE,QAAQ,SAAS,OAAO,KACxB,QAAQ,SAAS,SAAS,KAC1B,QAAQ,SAAS,MAAM,KACvB,QAAQ,SAAS,WAAW,KAC5B,QAAQ,SAAS,QAAQ,KACzB,QAAQ,SAAS,YAAY,KAC7B,QAAQ,SAAS,SAAS,GAC1B;AACA,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,WAAW,SAAS,WAAW;AAC7C,QAAM,QAAQ,SAAS,WAAW,SAAS,WAAW;AACtD,QAAM,WAAW,WAAW;AAK5B,MAAI,MAAM,QAAQ,mBAAmB,KAAK,oBAAoB,SAAS,GAAG;AACxE,UAAM,aAAuB,CAAC;AAC9B,QAAI,OAAO;AACT,iBAAW,KAAK,GAAG,OAAO,QAAQ,GAAG,OAAO,cAAc,OAAO;AAAA,IACnE,WAAW,UAAU;AACnB,iBAAW,KAAK,GAAG,OAAO,WAAW,OAAO;AAAA,IAC9C,OAAO;AACL,iBAAW,KAAK,GAAG,OAAO,SAAS,GAAG,OAAO,aAAa,GAAG,OAAO,UAAU,OAAO;AAAA,IACvF;AAEA,eAAW,QAAQ,YAAY;AAC7B,UAAI,oBAAoB,SAAS,IAAI,GAAG;AACtC,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAGA,MAAI,QAAQ,SAAS,OAAO,GAAG;AAC7B,QAAI,MAAO,QAAO,GAAG,OAAO;AAC5B,QAAI,SAAU,QAAO,GAAG,OAAO;AAC/B,WAAO,GAAG,OAAO;AAAA,EACnB;AAEA,MAAI,QAAQ,SAAS,KAAK,GAAG;AAC3B,QAAI,MAAO,QAAO,GAAG,OAAO;AAC5B,WAAO,GAAG,OAAO;AAAA,EACnB;AAEA,MAAI,QAAQ,WAAW,SAAS,GAAG;AACjC,QAAI,QAAQ,SAAS,MAAM,EAAG,QAAO,GAAG,OAAO;AAC/C,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,WAAW,MAAM,GAAG;AAC9B,WAAO,GAAG,OAAO;AAAA,EACnB;AAEA,SAAO;AACT;AAgBO,SAAS,iBACd,eACA,QACA,WAA4B,mBAAmB,GACpC;AACX,QAAM,iBAAiB;AAAA,IACrB,sBAAsB,eAAe,QAAQ,SAAS,UAAU;AAAA,IAChE;AAAA,EACF;AACA,QAAM,YAAY,SAAS,MAAM,cAAc;AAC/C,MAAI,CAAC,WAAW;AAGd,UAAM,IAAI;AAAA,MACR,kBAAkB,cAAc,+BAA+B,SAAS,OAAO;AAAA,IAEjF;AAAA,EACF;AACA,QAAM,SAAS,oBAAoB,cAAc;AACjD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,gBAAgB,sBAAsB,gBAAgB,QAAQ,QAAQ;AAAA,IACtE,aAAa,WAAW;AAAA,IACxB,UAAU,WAAW;AAAA,EACvB;AACF;AAQA,SAAS,cAAc,gBAAwB,UAAmC;AAChF,QAAM,UAAU,SAAS;AACzB,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI,UAAU;AACd,QAAM,OAAO,oBAAI,IAAY,CAAC,OAAO,CAAC;AACtC,SAAO,QAAQ,OAAO,KAAK,CAAC,KAAK,IAAI,QAAQ,OAAO,CAAC,GAAG;AACtD,cAAU,QAAQ,OAAO;AACzB,SAAK,IAAI,OAAO;AAAA,EAClB;AACA,SAAO;AACT;AAQA,SAAS,sBACP,gBACA,QACA,WAA4B,mBAAmB,GACO;AACtD,MAAI,WAAW,SAAS,WAAW,QAAQ;AACzC,WAAO,EAAE,iBAAiB,OAAO,gBAAgB,EAAE;AAAA,EACrD;AACA,QAAM,OAAO,SAAS,WAAW,cAAc;AAC/C,MAAI,MAAM;AACR,QAAI,OAAO,KAAK,WAAW,UAAU;AACnC,aAAO,EAAE,iBAAiB,MAAM,gBAAgB,KAAK,OAAO;AAAA,IAC9D;AACA,QAAI,CAAC,KAAK,kBAAkB;AAC1B,aAAO,EAAE,iBAAiB,OAAO,gBAAgB,EAAE;AAAA,IACrD;AAAA,EACF;AACA,SAAO,kBAAkB,gBAAgB,MAAM;AACjD;AAKO,SAAS,kBAAkB,SAAqC;AACrE,SAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,YAAY,EAAE;AAC5D;AAEO,SAAS,uBAAuB,QAAgB,OAAyC;AAC9F,QAAM,UAAU,MAAM,KAAK,CAAC,OAAO,GAAG,OAAO,GAAG,MAAM,WAAW,GAAG,OAAO,MAAM,KAAK,MAAM,CAAC;AAE7F,QAAM,UAAU,OAAO,SAAS,OAAO;AACvC,QAAM,WAAW,OAAO,WAAW,SAAS;AAC5C,QAAM,QAAQ,OAAO,WAAW,MAAM;AAKtC,QAAM,iBAAiB,UAAU,UAAU,WAAW,OAAS,QAAQ,QAAS;AAChF,QAAM,mBAAmB,WAAW,OAAQ,QAAQ,QAAQ;AAE5D,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM,uBAAuB,QAAQ,SAAS,WAAW;AAAA,IACzD,UAAU;AAAA,IACV,KAAK;AAAA,IACL,SAAS;AAAA,IACT,WAAW,SAAS,oBAAoB;AAAA,IACxC,OAAO,SAAS,iBAAiB,CAAC,QAAQ,OAAO,IAAI,CAAC,MAAM;AAAA,IAC5D,MAAM,kBAAkB,MAAM;AAAA,IAC9B,eAAe,SAAS,aAAa;AAAA,IACrC,WAAW,SAAS,mBAAmB;AAAA,EACzC;AACF;AAEO,SAAS,yBAAyB,SAAqD;AAC5F,MAAI,CAAC,WAAW,CAAC,MAAM,QAAQ,QAAQ,MAAM,KAAK,QAAQ,OAAO,WAAW,GAAG;AAC7E,WAAO,CAAC;AAAA,EACV;AAGA,QAAM,eAAe,QAAQ;AAC7B,QAAM,gBACJ,MAAM,QAAQ,YAAY,KAAK,aAAa,SAAS,IACjD,QAAQ,OAAO,OAAO,CAAC,MAAM,aAAa,SAAS,EAAE,EAAE,CAAC,IACxD,QAAQ;AAEd,QAAM,gBAAgB,oBAAI,IAAkC;AAC5D,aAAW,QAAQ,eAAe;AAChC,UAAM,SAAS,mBAAmB,KAAK,EAAE;AACzC,UAAM,OAAO,cAAc,IAAI,MAAM,KAAK,CAAC;AAC3C,SAAK,KAAK,IAAI;AACd,kBAAc,IAAI,QAAQ,IAAI;AAAA,EAChC;AAGA,QAAM,iBAA2B,CAAC;AAClC,MAAI,MAAM,QAAQ,YAAY,GAAG;AAC/B,eAAW,UAAU,cAAc;AACjC,YAAM,SAAS,mBAAmB,MAAM;AACxC,UAAI,cAAc,IAAI,MAAM,KAAK,CAAC,eAAe,SAAS,MAAM,GAAG;AACjE,uBAAe,KAAK,MAAM;AAAA,MAC5B;AAAA,IACF;AAAA,EACF;AAEA,aAAW,UAAU,cAAc,KAAK,GAAG;AACzC,QAAI,CAAC,eAAe,SAAS,MAAM,GAAG;AACpC,qBAAe,KAAK,MAAM;AAAA,IAC5B;AAAA,EACF;AAEA,SAAO,eAAe,IAAI,CAAC,WAAW,uBAAuB,QAAQ,cAAc,IAAI,MAAM,CAAE,CAAC;AAClG;AASO,SAAS,uBAAuB,QAAgB,gBAAiC;AACtF,MAAI,gBAAgB;AAClB,UAAM,UAAU,eAAe,QAAQ,iBAAiB,EAAE,EAAE,KAAK;AACjE,QAAI,QAAQ,SAAS,GAAG;AACtB,aAAO;AAAA,IACT;AAAA,EACF;AACA,QAAM,QAAQ,OAAO,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM;AACzC,QAAI,gBAAgB,KAAK,CAAC,EAAG,QAAO;AACpC,WAAO,EAAE,OAAO,CAAC,EAAE,YAAY,IAAI,EAAE,MAAM,CAAC;AAAA,EAC9C,CAAC;AACD,SAAO,MAAM,KAAK,GAAG;AACvB;AAEA,SAAS,iBAAiB,GAAoB;AAC5C,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,MAAI,IAAI,YAAY,EAAG,QAAO,GAAG,IAAI,OAAO;AAC5C,MAAI,IAAI,SAAS,EAAG,QAAO,GAAG,IAAI,IAAI;AACtC,MAAI,IAAI,QAAS,EAAG,QAAO,GAAG,IAAI,GAAI;AAEtC,SAAO,GAAG,KAAK,MAAM,IAAI,IAAI,CAAC;AAChC;AAEO,SAAS,iBAAiB,SAAyC;AACxE,QAAM,QAAQ,QAAQ;AACtB,QAAM,kBAAkB,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS;AAC/D,QAAM,OAAO,IAAI,KAAK,SAAS,CAAC,GAAG,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;AAE1D,QAAM,UAAU,kBAAkB,QAAQ,OAAO,OAAO,CAAC,MAAM,KAAK,IAAI,EAAE,EAAE,CAAC,IAAI,QAAQ,OAAO,MAAM,GAAG;AAAA,IACvG,CAAC,GAAG,OAAO,KAAK,IAAI,EAAE,EAAE,KAAK,OAAO,qBAAqB,KAAK,IAAI,EAAE,EAAE,KAAK,OAAO,qBAChF,EAAE,GAAG,cAAc,EAAE,EAAE;AAAA,EAC3B;AAEA,QAAM,OAAO,OAAO,IAAI,CAAC,MAAM;AAC7B,UAAM,QAAkB,CAAC;AACzB,QAAI,EAAE,iBAAkB,OAAM,KAAK,UAAU;AAC7C,QAAI,EAAE,eAAgB,OAAM,KAAK,QAAQ;AACzC,WAAO;AAAA,MACL,IAAI,EAAE;AAAA,MACN,MAAM,uBAAuB,EAAE,IAAI,EAAE,eAAe,EAAE,EAAE;AAAA,MACxD,KAAK,GAAG,iBAAiB,EAAE,SAAS,CAAC,IAAI,iBAAiB,EAAE,eAAe,CAAC;AAAA,MAC5E,MAAM,MAAM,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI;AAAA,MAC5C,KAAK,OAAO,EAAE,sBAAsB,WAAW,GAAG,KAAK,MAAM,EAAE,oBAAoB,GAAG,CAAC,MAAM;AAAA,IAC/F;AAAA,EACF,CAAC;AAED,QAAM,UAAU,EAAE,IAAI,SAAS,MAAM,QAAQ,KAAK,WAAW,MAAM,YAAY,KAAK,MAAM;AAC1F,QAAM,IAAI;AAAA,IACR,IAAI,KAAK,IAAI,QAAQ,GAAG,QAAQ,GAAG,KAAK,IAAI,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC;AAAA,IAC/D,MAAM,KAAK,IAAI,QAAQ,KAAK,QAAQ,GAAG,KAAK,IAAI,CAAC,MAAM,EAAE,KAAK,MAAM,CAAC;AAAA,IACrE,KAAK,KAAK,IAAI,QAAQ,IAAI,QAAQ,GAAG,KAAK,IAAI,CAAC,MAAM,EAAE,IAAI,MAAM,CAAC;AAAA,IAClE,MAAM,KAAK,IAAI,QAAQ,KAAK,QAAQ,GAAG,KAAK,IAAI,CAAC,MAAM,EAAE,KAAK,MAAM,CAAC;AAAA,EACvE;AAEA,QAAM,QAAkB;AAAA,IACtB,iCAAiC,OAAO,MAAM,GAAG,kBAAkB,iBAAiB,EAAE;AAAA,IACtF,KAAK,QAAQ,GAAG,OAAO,EAAE,EAAE,CAAC,KAAK,QAAQ,KAAK,OAAO,EAAE,IAAI,CAAC,KAAK,QAAQ,IAAI,OAAO,EAAE,GAAG,CAAC,KAAK,QAAQ,KAAK,OAAO,EAAE,IAAI,CAAC,KAAK,QAAQ,GAAG;AAAA,EAC5I;AACA,aAAW,KAAK,MAAM;AACpB,UAAM;AAAA,MACJ,KAAK,EAAE,GAAG,OAAO,EAAE,EAAE,CAAC,KAAK,EAAE,KAAK,OAAO,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,OAAO,EAAE,GAAG,CAAC,KAAK,EAAE,KAAK,OAAO,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,SAAS,CAAC,CAAC;AAAA,IAC1H;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;;;ACvfA,eAAsB,eAAe,SAA0C;AAI7E,QAAM,cAAc,QAAQ,SAAS,yBAAyB,GAAG;AACjE,QAAM,mBAAmB,QAAQ,SAAS,yBAAyB,GAAG;AACtE,QAAM,iBAAiB,QAAQ,SAAS,yBAAyB,GAAG;AACpE,QAAM,mBAAmB,QAAQ,SAAS,yBAAyB,GAAG;AACtE,OAAK,eAAe,oBAAoB,kBAAkB,qBAAqB,mBAAmB,EAAE,YAAY,GAAG;AACjH,uBAAmB,eAAe,CAAC,GAAG,oBAAoB,CAAC,GAAG,kBAAkB,CAAC,GAAG,oBAAoB,CAAC,CAAC;AAAA,EAC5G;AAEA,MAAI,CAAC,QAAQ,cAAc;AACzB,WAAO,QAAQ,QAAQ,UAAU,CAAC;AAAA,EACpC;AAEA,MAAI;AACF,UAAM,SAAS,QAAQ,YAAY;AACnC,QAAI,CAAC,QAAQ;AACX,aAAO,QAAQ,QAAQ,UAAU,CAAC;AAAA,IACpC;AAEA,UAAM,EAAE,OAAO,UAAU,IAAI,uBAAuB,MAAM;AAC1D,UAAM,UAAU,MAAM,4BAA4B,OAAO,WAAW,QAAQ,MAAM;AAClF,kBAAc,OAAO;AAErB,UAAM,gBAAgB,yBAAyB,OAAO;AAItD,QAAI,QAAQ,SAAS;AACnB,YAAM,OAAO,mBAAmB;AAChC,YAAM,QAAQ,QAAQ;AAAA,QACpB,SAAS;AAAA,UACP,QAAQ;AAAA,UACR,WAAW,KAAK,IAAI;AAAA,UACpB,2BAA2B;AAAA,YACzB,YAAY,EAAE,GAAG,KAAK,MAAM;AAAA,YAC5B,YAAY,CAAC,GAAG,KAAK,UAAU;AAAA,YAC/B,UAAU,EAAE,GAAI,KAAK,YAAY,CAAC,EAAG;AAAA,YACrC,YAAY,EAAE,GAAI,KAAK,cAAc,CAAC,EAAG;AAAA,UAC3C;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO,QAAQ,QAAQ,UAAU,CAAC;AAAA,EACpC;AACF;AAEO,SAAS,qBAAqB,MAAmC;AACtE,QAAM,SAA+B,CAAC;AACtC,QAAM,aAAqC,CAAC;AAE5C,QAAM,YAAY,KAAK,UAAU,CAAC;AAClC,aAAW,CAAC,IAAI,IAAI,KAAK,OAAO,QAAa,SAAS,GAAG;AACvD,QAAI,GAAG,WAAW,MAAM,KAAK,GAAG,WAAW,OAAO,EAAG;AAErD,UAAM,YAAY,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;AAChE,QAAI,WAAW;AACb,iBAAW,EAAE,IAAI;AAAA,IACnB;AAEA,UAAM,YAAY,KAAK,aAAa,CAAC;AACrC,WAAO,KAAK;AAAA,MACV;AAAA,MACA,aAAa,KAAK,eAAe,KAAK,SAAS;AAAA,MAC/C;AAAA,MACA,mBAAmB,UAAU;AAAA,MAC7B,WAAW,UAAU;AAAA,MACrB,kBAAkB,QAAQ,KAAK,gBAAgB;AAAA,MAC/C,gBAAgB,OAAO,KAAK,mBAAmB,WAAW,KAAK,iBAAiB;AAAA,MAChF,mBAAmB,OAAO,KAAK,sBAAsB,WAAW,KAAK,oBAAoB;AAAA,MACzF,gBAAgB,QAAQ,KAAK,cAAc;AAAA,MAC3C,WAAW,OAAO,KAAK,cAAc,WAAW,KAAK,YAAY;AAAA,MACjE,iBAAiB,OAAO,KAAK,oBAAoB,WAAW,KAAK,kBAAkB;AAAA,IACrF,CAAC;AAAA,EACH;AAEA,QAAM,kBAA4B,CAAC;AACnC,MAAI,MAAM,QAAQ,KAAK,eAAe,GAAG;AACvC,eAAW,aAAa,KAAK,iBAAiB;AAC5C,UAAI,MAAM,QAAQ,UAAU,MAAM,GAAG;AACnC,mBAAW,SAAS,UAAU,QAAQ;AACpC,cAAI,MAAM,QAAQ,MAAM,QAAQ,GAAG;AACjC,4BAAgB,KAAK,GAAG,MAAM,QAAQ;AAAA,UACxC;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,KAAK,CAAC,GAAG,MAAM,EAAE,GAAG,cAAc,EAAE,EAAE,CAAC;AAG9C,QAAM,aAAqC,CAAC;AAC5C,QAAM,gBAAiB,KAAa;AACpC,MAAI,iBAAiB,OAAO,kBAAkB,UAAU;AACtD,eAAW,CAAC,OAAO,IAAI,KAAK,OAAO,QAAa,aAAa,GAAG;AAC9D,UAAI,QAAQ,OAAO,KAAK,eAAe,UAAU;AAC/C,mBAAW,KAAK,IAAI,KAAK;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,GAAI,gBAAgB,SAAS,IAAI,EAAE,gBAAgB,IAAI,CAAC;AAAA,IACxD,GAAI,OAAO,KAAK,UAAU,EAAE,SAAS,IAAI,EAAE,WAAW,IAAI,CAAC;AAAA,EAC7D;AACF;AAEA,eAAsB,4BACpB,OACA,WACA,QACiC;AACjC,QAAM,MAAM,MAAM,gBAAgB;AAAA,IAChC;AAAA,IACA,MAAM;AAAA,IACN,MAAM,EAAE,SAAS,UAAU;AAAA,IAC3B;AAAA,EACF,CAAC;AACD,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,UAAU,MAAM,IAAI,KAAK;AAC/B,UAAM,IAAI,MAAM,2BAA2B,IAAI,MAAM,MAAM,OAAO,EAAE;AAAA,EACtE;AACA,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,SAAO,qBAAqB,IAAI;AAClC;;;AC7GO,SAAS,kBAAkB,UAAkB,QAAQ,IAAY;AACtE,QAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,QAAQ,CAAC;AACjD,QAAM,SAAS,KAAK,MAAM,UAAU,KAAK;AACzC,SAAO,IAAI,IAAI,OAAO,MAAM,CAAC,GAAG,IAAI,OAAO,QAAQ,MAAM,CAAC;AAC5D;AAIA,IAAM,oBAAqD;AAAA,EACzD,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AACT;AAEA,SAAS,oBAAoB,QAAsC;AAGjE,QAAM,KAAK,OAAO,UAAU,IAAI,KAAK,EAAE,YAAY;AACnD,MAAI,MAAM,KAAM,QAAO;AACvB,MAAI,MAAM,SAAU,QAAO;AAC3B,SAAO;AACT;AAEA,SAAS,iBAAiB,QAAqB,MAA+B;AAC5E,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,SAAS,SAAU,QAAO;AAC9B,SAAO,OAAO,eAAe,OAAO,YAAY;AAClD;AAEA,SAAS,iBAAiB,SAAuC;AAC/D,SAAO,QACJ,MAAM,EACN,KAAK,CAAC,GAAG,MAAM,kBAAkB,oBAAoB,CAAC,CAAC,IAAI,kBAAkB,oBAAoB,CAAC,CAAC,CAAC;AACzG;AAEO,SAAS,qBAAqB,WAAmC;AACtE,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,KAAK,KAAK,MAAM,SAAS;AAC/B,MAAI,CAAC,OAAO,SAAS,EAAE,EAAG,QAAO;AACjC,QAAM,QAAQ,KAAK,KAAK,IAAI;AAC5B,MAAI,SAAS,EAAG,QAAO;AACvB,QAAM,WAAW,KAAK,KAAK,QAAQ,GAAK;AACxC,QAAM,OAAO,KAAK,MAAM,YAAY,KAAK,GAAG;AAC5C,QAAM,QAAQ,KAAK,MAAO,YAAY,KAAK,MAAO,EAAE;AACpD,QAAM,OAAO,WAAW;AACxB,MAAI,OAAO,EAAG,QAAO,MAAM,IAAI,KAAK,KAAK;AACzC,MAAI,QAAQ,EAAG,QAAO,MAAM,KAAK,KAAK,IAAI;AAC1C,SAAO,MAAM,IAAI;AACnB;AAEO,SAAS,kBAAkB,MAAyB;AACzD,QAAM,SAAuB,CAAC;AAE9B,aAAW,KAAK,KAAK,UAAU,CAAC,GAAG;AACjC,UAAM,UAAyB,CAAC;AAChC,eAAW,KAAK,EAAE,WAAW,CAAC,GAAG;AAC/B,cAAQ,KAAK;AAAA,QACX,UAAU,EAAE,YAAY;AAAA,QACxB,aAAa,EAAE,eAAe,EAAE,YAAY;AAAA,QAC5C,QAAQ,EAAE;AAAA,QACV,WAAW,EAAE;AAAA,QACb,aAAa,EAAE;AAAA,QACf,mBAAmB,OAAO,EAAE,sBAAsB,WAAW,EAAE,oBAAoB;AAAA,MACrF,CAAC;AAAA,IACH;AACA,WAAO,KAAK;AAAA,MACV,aAAa,EAAE,eAAe;AAAA,MAC9B,aAAa,EAAE;AAAA,MACf;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL;AAAA,IACA,aAAa,KAAK;AAAA,EACpB;AACF;AAEO,SAAS,mBAAmB,SAA+B;AAChE,QAAM,QAAkB,CAAC;AAEzB,aAAW,SAAS,QAAQ,QAAQ;AAClC,QAAI,MAAM,SAAS,EAAG,OAAM,KAAK,EAAE;AACnC,UAAM,KAAK,GAAG,MAAM,WAAW,EAAE;AACjC,eAAW,KAAK,iBAAiB,MAAM,OAAO,GAAG;AAC/C,YAAM,MAAM,KAAK,MAAM,EAAE,oBAAoB,GAAG;AAChD,YAAM,QAAQ,iBAAiB,GAAG,oBAAoB,CAAC,CAAC,EAAE,OAAO,CAAC;AAClE,YAAM,QAAQ,qBAAqB,EAAE,SAAS;AAC9C,YAAM,SAAS,SAAS,MAAM,MAAM,KAAK,KAAK,MAAM;AACpD,YAAM,KAAK,KAAK,KAAK,IAAI,kBAAkB,EAAE,iBAAiB,CAAC,IAAI,OAAO,GAAG,EAAE,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE;AAAA,IAC7G;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEO,SAAS,mBAAmB,OAA4B;AAE7D,UAAQ,MAAM,WAAW,CAAC,GAAG,KAAK,CAAC,OAAO,EAAE,YAAY,IAAI,YAAY,EAAE,SAAS,QAAQ,CAAC;AAC9F;AAEO,SAAS,iBAAiB,QAAsB,SAA0C;AAC/F,MAAI,OAAO,WAAW,EAAG,QAAO;AAChC,QAAM,SAAS,oBAAoB,OAAO;AAC1C,MAAI,WAAW,YAAY,WAAW,OAAO;AAC3C,UAAM,QAAQ,OAAO,KAAK,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;AACvD,QAAI,MAAO,QAAO;AAAA,EACpB;AACA,SAAO,OAAO,KAAK,CAAC,MAAM,mBAAmB,CAAC,CAAC,KAAK,OAAO,CAAC;AAC9D;AAMO,IAAM,6BAA6B;AAO1C,SAAS,aAAa,SAAwB,MAAgD;AAC5F,SAAO,QAAQ,KAAK,CAAC,MAAM,oBAAoB,CAAC,MAAM,IAAI;AAC5D;AAMA,SAAS,QAAQ,QAA6B;AAC5C,QAAM,KAAK,OAAO,YAAY;AAC9B,SAAO,GAAG,QAAQ,gCAAgC,EAAE,KAAK;AAC3D;AAGO,SAAS,2BAA2B,SAA4D;AACrG,QAAM,QAA4C,CAAC;AACnD,aAAW,KAAK,SAAS,UAAU,CAAC,GAAG;AACrC,UAAM,WAAW,aAAa,EAAE,SAAS,IAAI;AAC7C,UAAM,eAAe,aAAa,EAAE,SAAS,QAAQ;AACrD,QAAI,YAAY,aAAc,OAAM,QAAQ,QAAQ,CAAC,IAAI,EAAE,MAAM,SAAS,mBAAmB,QAAQ,aAAa,kBAAkB;AAAA,EACtI;AACA,SAAO;AACT;AAKO,SAAS,yBACd,UACA,SACA,eAAe,4BACP;AACR,MAAI,CAAC,YAAY,CAAC,QAAS,QAAO;AAClC,aAAW,OAAO,OAAO,KAAK,OAAO,GAAG;AACtC,UAAM,OAAO,SAAS,GAAG;AACzB,UAAM,OAAO,QAAQ,GAAG;AACxB,QAAI,CAAC,QAAQ,CAAC,KAAM;AAEpB,UAAM,YAAY,KAAK,IAAI,IAAI,KAAK,IAAI;AACxC,UAAM,UAAU,KAAK,SAAS,KAAK;AAEnC,QAAI,YAAY,QAAU,UAAU,MAAS;AAC3C,YAAM,gBAAgB,YAAY;AAClC,UAAI,iBAAiB,KAAO,iBAAiB,IAAM;AACjD,eAAO,KAAK,MAAM,gBAAgB,GAAG,IAAI;AAAA,MAC3C;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,mBAAmB,OAAmB,iBAAkD;AAC/F,QAAM,UAAU,MAAM;AACtB,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,QAAM,WAAW,aAAa,SAAS,IAAI;AAC3C,QAAM,eAAe,aAAa,SAAS,QAAQ;AACnD,MAAI,YAAY,cAAc;AAC5B,WAAO,aAAa,oBAAoB,kBAAkB,SAAS,oBAAoB,eAAe;AAAA,EACxG;AACA,MAAI,QAAQ,QAAQ,CAAC;AACrB,aAAW,KAAK,SAAS;AACvB,QAAI,EAAE,oBAAoB,MAAM,kBAAmB,SAAQ;AAAA,EAC7D;AACA,SAAO;AACT;AAEO,SAAS,iBACd,SACA,SACA,kBAAkB,4BACE;AACpB,QAAM,QAAQ,iBAAiB,QAAQ,QAAQ,OAAO;AACtD,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,SAAS,mBAAmB,OAAO,eAAe;AACxD,SAAO,SAAS,sBAAsB,MAAM,IAAI;AAClD;AAGO,SAAS,qBAAqB,SAAuB,SAAsC;AAChG,QAAM,QAAQ,iBAAiB,QAAQ,QAAQ,OAAO;AACtD,MAAI,CAAC,SAAS,MAAM,QAAQ,WAAW,EAAG,QAAO;AACjD,SAAO,iBAAiB,MAAM,OAAO,EAAE,IAAI,qBAAqB,EAAE,KAAK,QAAK;AAC9E;AAIO,SAAS,sBAAsB,QAA6B;AACjE,QAAM,OAAO,oBAAoB,MAAM;AACvC,QAAM,SAAS,SAAS,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO,eAAe,OAAO;AAC9F,QAAM,MAAM,KAAK,MAAM,OAAO,oBAAoB,GAAG;AACrD,QAAM,QAAQ,qBAAqB,OAAO,SAAS,GAAG,QAAQ,QAAQ,EAAE;AACxE,MAAI,SAAS,MAAM,IAAK,QAAO,GAAG,MAAM,IAAI,GAAG,MAAM,KAAK;AAC1D,SAAO,GAAG,MAAM,IAAI,GAAG;AACzB;AAKA,IAAM,WAAW;AACjB,IAAM,cAAc;AACpB,IAAM,gBAAgB;AAEf,IAAM,iBAAiB;AACvB,IAAM,kBAAkB;AAExB,SAAS,oBAAoB,QAAgD;AAClF,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,QAAQ,OAAO,MAAM,QAAQ;AACnC,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,MAAM,SAAS,MAAM,CAAC,GAAG,EAAE;AACjC,MAAI,OAAO,gBAAiB,QAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,aAAa;AACvE,MAAI,OAAO,eAAgB,QAAO,GAAG,WAAW,GAAG,MAAM,GAAG,aAAa;AACzE,SAAO;AACT;AAGO,SAAS,wBAAwB,QAAgD;AACtF,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,OACJ,MAAM,QAAK,EACX,IAAI,CAAC,SAAS,oBAAoB,IAAI,KAAK,IAAI,EAC/C,KAAK,QAAK;AACf;AAIO,IAAM,mBAAmB;AACzB,IAAM,4BAA4B,IAAI,KAAK;AAK3C,IAAM,yBAAyB,KAAK,KAAK;AA6BhD,SAAS,eAAe,OAAiC;AACvD,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK;AAC3D;AAGA,SAAS,WAAW,KAA8D;AAChF,MAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,EAAG,QAAO;AAClE,QAAM,QAA4C,CAAC;AACnD,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,GAAG,GAAG;AAC9C,UAAM,MAAM;AACZ,UAAM,WAAW,MAAM,IAAI;AAC3B,UAAM,SAAS,KAAK;AACpB,QAAI,eAAe,QAAQ,KAAK,eAAe,MAAM,EAAG,OAAM,GAAG,IAAI,EAAE,MAAM,UAAU,OAAO;AAAA,EAChG;AACA,SAAO,OAAO,KAAK,KAAK,EAAE,SAAS,IAAI,QAAQ;AACjD;AAQO,SAAS,mBAAmB,OAAwC;AACzE,SAAO,OAAO,aAAa;AAC7B;AAEO,SAAS,iBAAiB,OAA+B,KAA2B;AACzF,QAAM,OAAO,MAAM,KAAK;AACxB,MAAI,SAAS,SAAS,CAAC,mBAAmB,IAAI,KAAK,GAAG;AACpD,QAAI,GAAG,UAAU,kBAAkB,MAAS;AAC5C;AAAA,EACF;AACA,QAAM,QAAQ,MAAM,UAAU,IAAI,OAAO,IAAI,IAAI;AACjD,MAAI,GAAG;AAAA,IACL;AAAA,IACA,SAAS,SAAS,wBAAwB,KAAK,IAAI,oBAAoB,KAAK;AAAA,EAC9E;AACF;AAKO,IAAM,yBAAN,MAA6B;AAAA,EAC1B;AAAA,EACA,YAAY;AAAA,EACZ,WAAqD;AAAA,EACrD;AAAA,EACA;AAAA,EACS;AAAA,EAEjB,YAAY,OAAyB;AACnC,SAAK,QAAQ;AACb,SAAK,kBAAkB;AACvB,SAAK,cAAc;AAAA,EACrB;AAAA,EAEA,IAAI,QAAgB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,OAAwB;AACtB,WAAO,KAAK,MAAM,SAAS;AAAA,EAC7B;AAAA,EAEA,UAAU,SAAkB,OAAwB,UAA8B;AAChF,QAAI,CAAC,KAAK,WAAW,SAAS,MAAO,QAAO;AAC5C,WAAO,SAAS,SACZ,qBAAqB,KAAK,SAAS,OAAO,IAC1C,iBAAiB,KAAK,SAAS,SAAS,KAAK,eAAe;AAAA,EAClE;AAAA,EAEA,IAAI,UAAmB;AACrB,WAAO,CAAC,CAAC,KAAK,WAAW,KAAK,IAAI,IAAI,KAAK,YAAY;AAAA,EACzD;AAAA,EAEA,QAAQ,KAAqB,OAAuB,CAAC,GAAsC;AACzF,QAAI,CAAC,KAAK,eAAe,KAAK,KAAK,MAAM,SAAS,CAAC,mBAAmB,IAAI,KAAK,IAAI;AACjF,aAAO,QAAQ,QAAQ,MAAS;AAAA,IAClC;AACA,QAAI,KAAK,SAAU,QAAO,KAAK;AAC/B,QAAI,CAAC,KAAK,SAAS,KAAK,QAAS,QAAO,QAAQ,QAAQ,KAAK,OAAO;AACpE,WAAO,KAAK,UAAU,KAAK,KAAK,MAAM;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAAgB,KAAoC;AACxD,qBAAiB,MAAM,GAAG;AAC1B,UAAM,KAAK,QAAQ,GAAG;AACtB,qBAAiB,MAAM,GAAG;AAAA,EAC5B;AAAA;AAAA;AAAA,EAIA,cAAc,KAAwD;AACpE,QAAI,KAAK,QAAS,QAAO,QAAQ,QAAQ,KAAK,OAAO;AACrD,QAAI,KAAK,SAAU,QAAO,KAAK;AAC/B,QAAI,CAAC,mBAAmB,IAAI,KAAK,EAAG,QAAO,QAAQ,QAAQ,MAAS;AACpE,WAAO,KAAK,UAAU,GAAG;AAAA,EAC3B;AAAA,EAEQ,UAAU,KAAqB,QAAyD;AAC9F,SAAK,WAAW,KAAK,MAAM,KAAK,MAAM,EAAE,QAAQ,MAAM;AACpD,WAAK,WAAW;AAAA,IAClB,CAAC;AACD,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,MAAM,KAAqB,QAAyD;AAChG,QAAI;AACF,YAAM,SAAS,MAAM,IAAI,eAAe,uBAAuB,WAAW;AAC1E,UAAI,CAAC,OAAQ,QAAO;AACpB,YAAM,EAAE,OAAO,UAAU,IAAI,uBAAuB,MAAM;AAC1D,UAAI,CAAC,MAAO,QAAO;AACnB,YAAM,MAAM,MAAM,gBAAgB;AAAA,QAChC;AAAA,QACA,MAAM;AAAA,QACN,MAAM,EAAE,SAAS,UAAU;AAAA,QAC3B;AAAA,MACF,CAAC;AACD,UAAI,CAAC,IAAI,GAAI,QAAO;AACpB,YAAM,UAAU,kBAAkB,MAAM,IAAI,KAAK,CAAC;AAClD,WAAK,OAAO,OAAO;AACnB,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA,EAIA,OAAO,SAA6B;AAClC,UAAM,aAAa,yBAAyB,KAAK,WAAW,2BAA2B,OAAO,GAAG,KAAK,eAAe;AACrH,SAAK,kBAAkB;AACvB,SAAK,YAAY,2BAA2B,OAAO;AACnD,SAAK,UAAU;AACf,SAAK,YAAY,KAAK,IAAI;AAC1B,SAAK,UAAU;AAAA,EACjB;AAAA,EAEQ,gBAAsB;AAC5B,UAAM,QAAQ,KAAK,MAAM,eAAe;AACxC,UAAM,QAAQ,OAAO;AACrB,QAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,KAAK,QAAQ,GAAG;AACpE,WAAK,kBAAkB;AAAA,IACzB;AACA,UAAM,KAAK,OAAO;AAClB,SAAK,YAAY,OAAO,OAAO,YAAY,KAAK,IAAI,IAAI,MAAM,yBAC1D,WAAW,OAAO,mBAAmB,IACrC;AAAA,EACN;AAAA;AAAA;AAAA,EAIQ,YAAkB;AACxB,QAAI;AACF,WAAK,MAAM,eAAe;AAAA,QACxB,iBAAiB,KAAK;AAAA,QACtB,qBAAqB,KAAK,aAAa,CAAC;AAAA,QACxC,WAAW,KAAK,IAAI;AAAA,MACtB,CAAC;AAAA,IACH,QAAQ;AAAA,IAER;AAAA,EACF;AACF;;;ACteA,YAAY,QAAQ;AACpB,YAAY,QAAQ;AACpB,YAAY,UAAU;AAEtB,SAAS,oBAA8D;AAShE,IAAM,uBAAuB;AAkB7B,SAAS,kBAAkB,MAAyB,QAAQ,KAAa;AAK9E,QAAM,OAAO,IAAI,uBAAuB,IAAI,KAAK,KAAU,UAAQ,WAAQ,GAAG,OAAO,OAAO;AAC5F,SAAY,UAAK,KAAK,oBAAoB;AAC5C;AAEO,SAAS,mBAAmB,OAAO,kBAAkB,GAAmC;AAC7F,MAAI;AACF,UAAM,MAAM,KAAK,MAAS,gBAAa,MAAM,OAAO,CAAC;AACrD,QAAI,OAAO,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,GAAG,EAAG,QAAO;AAAA,EACpE,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAEO,SAAS,mBAAmB,MAAc,MAAmC;AAClF,MAAI;AACF,IAAG,aAAe,aAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AACpD,IAAG,iBAAc,MAAM,KAAK,UAAU,MAAM,MAAM,CAAC,IAAI,MAAM,OAAO;AACpE,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,oBAAoB,OAA6C;AAC/E,QAAM,IAAI,OAAO,UAAU,WAAW,MAAM,KAAK,EAAE,YAAY,IAAI;AACnE,SAAO,MAAM,SAAS,MAAM,YAAY,MAAM,SAAS,IAAI;AAC7D;AAEO,SAAS,kBAAkB,QAA8C;AAC9E,SAAO,oBAAoB,QAAQ,UAAU,WAAW,KAAK;AAC/D;AAIO,SAAS,qBAAqB,OAAO,kBAAkB,GAAqB;AACjF,SAAO;AAAA,IACL,UAAU,MAAM,kBAAkB,mBAAmB,IAAI,CAAC;AAAA,IAC1D,gBAAgB,MAAM;AACpB,YAAM,QAAQ,mBAAmB,IAAI,GAAG,QAAQ;AAChD,aAAO,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAAI,QAAQ;AAAA,IAC/E;AAAA;AAAA;AAAA,IAGA,gBAAgB,CAAC,UAAU;AACzB,UAAI;AACF,YAAI,OAA2B,CAAC;AAChC,YAAI;AACF,gBAAM,MAAM,KAAK,MAAS,gBAAa,MAAM,OAAO,CAAC;AACrD,cAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,EAAG,QAAO;AAClE,iBAAO;AAAA,QACT,SAAS,KAAU;AACjB,cAAI,KAAK,SAAS,SAAU,QAAO;AAAA,QACrC;AACA,cAAM,SACJ,KAAK,UAAU,OAAO,KAAK,WAAW,YAAY,CAAC,MAAM,QAAQ,KAAK,MAAM,IAAI,KAAK,SAAS,CAAC;AACjG,cAAM,QACJ,OAAO,SAAS,OAAO,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,OAAO,KAAK,IAAI,OAAO,QAAQ,CAAC;AACrG,eAAO,QAAQ,EAAE,GAAG,OAAO,GAAG,MAAM;AACpC,aAAK,SAAS;AACd,eAAO,mBAAmB,MAAM,IAAI;AAAA,MACtC,QAAQ;AAEN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;AAeO,IAAM,kBAA+C,OAAO,OAAO;AAAA,EACxE;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,aAAa;AAAA,IACb,SAAS,CAAC,OAAO,UAAU,MAAM;AAAA,IACjC,cAAc;AAAA,IACd,UAAU,OAAO,KAAK,gBAAgB;AACpC,UAAI,aAAa;AACf,cAAM,YAAY,QAAQ,GAAG;AAC7B,yBAAiB,aAAa,GAAG;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AACF,CAAC;AAEM,SAAS,kBAAkB,QAAwC,OAAiC;AACzG,QAAM,MAAM,QAAQ,WAAW,MAAM,GAAG;AACxC,MAAI,OAAO,QAAQ,YAAY,IAAI,KAAK,EAAG,QAAO,IAAI,KAAK;AAC3D,SAAO,MAAM,gBAAgB;AAC/B;AAEO,SAAS,iBAAiB,OAAyB,OAAe,MAAuB;AAC9F,QAAM,UAAU,mBAAmB,IAAI,KAAK,CAAC;AAC7C,QAAM,WACJ,QAAQ,YAAY,OAAO,QAAQ,aAAa,WAAW,QAAQ,WAAW,CAAC;AACjF,SAAO,mBAAmB,MAAM,EAAE,GAAG,SAAS,UAAU,EAAE,GAAG,UAAU,CAAC,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;AAC/F;AAIA,eAAsB,kBACpB,OACA,OACA,MACA,KACA,aACkB;AAClB,MAAI,CAAC,iBAAiB,OAAO,OAAO,IAAI,GAAG;AACzC,WAAO;AAAA,EACT;AACA,QAAM,MAAM,WAAW,KAAK,WAAW;AACvC,SAAO;AACT;AAKO,SAAS,uBACd,OACA,SACA,MACoB;AACpB,MAAI,CAAC,SAAS,SAAS,MAAO,QAAO;AACrC,QAAM,QAAQ,MAAM,UAAU,SAAS,SAAS,SAAS,SAAS,QAAQ;AAC1E,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,SAAS,SAAS,wBAAwB,KAAK,IAAI,oBAAoB,KAAK;AACrF;AAIO,SAAS,mBACd,QACA,SAAsC,iBACvB;AACf,SAAO,OAAO,IAAI,CAAC,OAAO;AAAA,IACxB,IAAI,EAAE;AAAA,IACN,OAAO,EAAE;AAAA,IACT,aAAa,EAAE;AAAA,IACf,cAAc,kBAAkB,QAAQ,CAAC;AAAA,IACzC,QAAQ,CAAC,GAAG,EAAE,OAAO;AAAA,EACvB,EAAE;AACJ;AAEO,SAAS,WAAW,KAA8B,MAAc,OAAqC,QAAc;AACxH,MAAI,IAAI,OAAO;AACb,QAAI,GAAG,OAAO,MAAM,IAAI;AAAA,EAC1B,OAAO;AACL,QAAI,SAAS,WAAW,SAAS,UAAW,SAAQ,MAAM,IAAI;AAAA,QACzD,SAAQ,IAAI,IAAI;AAAA,EACvB;AACF;AAKA,eAAe,kBAAkB,OAA0C;AACzE,MAAI;AACF,UAAM,KAAK,MAAM,OAAO,iCAAiC;AACzD,WAAO,GAAG,qBAAqB;AAAA,EACjC,QAAQ;AACN,WAAO;AAAA,MACL,OAAO,CAAC,MAAM,aAAc,WAAW,MAAM,GAAG,UAAU,IAAI,IAAI;AAAA,MAClE,OAAO,CAAC,MAAM,aAAc,WAAW,MAAM,GAAG,UAAU,IAAI,IAAI,MAAM,GAAG,SAAS,IAAI;AAAA,MACxF,aAAa,CAAC,SAAS,MAAM,GAAG,OAAO,IAAI;AAAA,MAC3C,QAAQ,MAAM,GAAG,UAAU,SAAI;AAAA,MAC/B,MAAM,CAAC,SAAS,MAAM,GAAG,OAAO,IAAI;AAAA,IACtC;AAAA,EACF;AACF;AAIA,eAAsB,aACpB,KACA,aACe;AACf,QAAM,OAAO,kBAAkB;AAC/B,MAAI,IAAI,SAAS,SAAS,IAAI,OAAO;AACnC,UAAM,mBAAmB,KAAK,aAAa,IAAI;AAC/C;AAAA,EACF;AACA,QAAM,SAAS,mBAAmB,IAAI;AACtC,MAAI,CAAC,IAAI,OAAO;AACd;AAAA,MACE;AAAA,MACA,gBAAgB,IAAI,CAAC,MAAM,GAAG,EAAE,KAAK,KAAK,kBAAkB,QAAQ,CAAC,CAAC,EAAE,EAAE,KAAK,IAAI,IACjF;AAAA,OAAU,IAAI;AAAA,IAClB;AACA;AAAA,EACF;AACA,MAAI,QAAQ,gBAAgB,CAAC;AAC7B,MAAI,CAAC,MAAO;AACZ,MAAI,gBAAgB,SAAS,GAAG;AAC9B,UAAM,SAAS,gBAAgB;AAAA,MAC7B,CAAC,MAAM,GAAG,EAAE,KAAK,cAAc,kBAAkB,QAAQ,CAAC,CAAC;AAAA,IAC7D;AACA,UAAM,SAAS,MAAM,IAAI,GAAG,OAAO,wBAAwB,MAAM;AACjE,QAAI,CAAC,OAAQ;AACb,UAAM,QAAQ,gBAAgB,OAAO,QAAQ,MAAM,CAAC;AACpD,QAAI,CAAC,MAAO;AACZ,YAAQ;AAAA,EACV;AACA,QAAM,SAAS,MAAM,IAAI,GAAG;AAAA,IAC1B,GAAG,MAAM,KAAK,cAAc,kBAAkB,QAAQ,KAAK,CAAC;AAAA,IAC5D,CAAC,GAAG,MAAM,OAAO;AAAA,EACnB;AACA,MAAI,CAAC,UAAU,CAAC,MAAM,QAAQ,SAAS,MAAM,EAAG;AAChD,MAAI,MAAM,kBAAkB,OAAO,QAAQ,MAAM,KAAK,WAAW,GAAG;AAClE,eAAW,KAAK,GAAG,MAAM,KAAK,WAAW,MAAM,GAAG;AAAA,EACpD,OAAO;AACL,eAAW,KAAK,mBAAmB,IAAI,KAAK,OAAO;AAAA,EACrD;AACF;AAEA,eAAe,mBACb,KACA,aACA,MACe;AACf,MAAI,YAAa,OAAM,YAAY,cAAc,GAAG;AACpD,QAAM,IAAI,GAAG,OAAa,OAAO,KAAK,OAAO,KAAK,SAAS;AACzD,UAAM,SAAS,mBAAmB,IAAI;AACtC,UAAM,QAAQ,mBAAmB,MAAM;AACvC,UAAM,aAAa,gBAAgB,KAAK,CAAC,MAAM,EAAE,QAAQ,aAAa;AAGtE,UAAM,eAAe,CAAC,SAAiB;AACrC,YAAM,OAAO,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,aAAa;AACrD,UAAI,CAAC,KAAM;AACX,YAAM,OAAO,YAAY,eAAe;AACxC,YAAM,SAAS,SAAS,QAAQ,WAAW,uBAAuB,aAAa,IAAI,OAAO,IAAI,IAAI;AAClG,WAAK,cAAc,UAAU,OAAO,GAAG,IAAI,KAAK,MAAM,KAAM,UAAU;AAAA,IACxE;AACA,iBAAa,kBAAkB,MAAM,CAAC;AACtC,UAAM,OAAO,IAAI;AAAA,MACf;AAAA,MACA,KAAK,IAAI,MAAM,QAAQ,EAAE;AAAA,MACzB,MAAM,kBAAkB,KAAK;AAAA,MAC7B,CAAC,IAAI,aAAa;AAChB,cAAM,QAAQ,gBAAgB,KAAK,CAAC,MAAM,EAAE,QAAQ,EAAE;AACtD,YAAI,CAAC,MAAO;AACZ,aAAK,WAAW;AAChB,YAAI,cAAc;AAClB,aAAK,kBAAkB,OAAO,UAAU,MAAM,KAAK,WAAW,EAAE,KAAK,MAAM;AACzE,uBAAa,QAAQ;AACrB,eAAK,WAAW;AAChB,cAAI,cAAc;AAAA,QACpB,CAAC;AAAA,MACH;AAAA,MACA,MAAM,KAAK;AAAA,IACb;AAKA,UAAM,SAAS;AAAA,MACb,QAAQ,CAAC,UAAkB,CAAC,MAAM,GAAG,UAAU,SAAI,OAAO,KAAK,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC;AAAA,IAChF;AACA,WAAO;AAAA,MACL,QAAQ,CAAC,UAAkB,CAAC,GAAG,OAAO,OAAO,KAAK,GAAG,GAAG,KAAK,OAAO,KAAK,GAAG,GAAG,OAAO,OAAO,KAAK,CAAC;AAAA,MACnG,aAAa,CAAC,SAAiB,KAAK,YAAY,IAAI;AAAA,MACpD,YAAY,MAAM,KAAK,WAAW;AAAA,IACpC;AAAA,EACF,CAAC;AACH;;;ACzTA;AAAA,EAQE;AAAA,OACK;;;ACoDA,SAAS,gBAGd;AACA,QAAM,UAAyB,CAAC;AAChC,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,OAAO;AAAA,EACT;AACA,MAAI,aAA+C;AACnD,MAAI;AACJ,MAAI,SAAS;AACb,MAAI,WAAuC;AAE3C,QAAM,WAAW,CAAC,YAA8C;AAAA,IAC9D;AAAA,IACA,OAAO,EAAE,GAAG,MAAM;AAAA,IAClB;AAAA,IACA;AAAA,EACF;AAMA,QAAM,sBAAsB,MAAY;AACtC,QAAI,CAAC,qBAAsB;AAC3B,UAAM,WAAW,QAAQ,KAAK,CAAC,MAA4B,EAAE,SAAS,UAAU;AAChF,QAAI,YAAY,CAAC,SAAS,mBAAmB;AAC3C,eAAS,oBAAoB;AAAA,IAC/B,WAAW,CAAC,UAAU;AACpB,eAAS,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,KAAK;AAC5C,cAAM,QAAQ,QAAQ,CAAC;AACvB,YAAI,OAAO,SAAS,UAAU,CAAC,MAAM,eAAe;AAClD,gBAAM,gBAAgB;AACtB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,iBAAiB,CAAC,WAAoC;AAC1D,QAAI,aAAa,KAAM;AACvB,UAAM,eAAe,QAAQ,SAAS;AACtC,UAAM,QAAQ,QAAQ,YAAY;AAClC,QAAI,OAAO,SAAS,YAAY;AAC9B,aAAO,KAAK,EAAE,MAAM,gBAAgB,cAAc,SAAS,MAAM,SAAS,CAAC;AAAA,IAC7E,WAAW,OAAO,SAAS,QAAQ;AACjC,aAAO,KAAK,EAAE,MAAM,YAAY,cAAc,SAAS,MAAM,KAAK,CAAC;AAAA,IACrE;AACA,eAAW;AAAA,EACb;AAEA,QAAM,cAAc,CAAC,SAAiB,WAAoC;AACxE,UAAM,OAAO,QAAQ,KAAK;AAC1B,QAAI,CAAC,KAAK,WAAW,OAAO,EAAG;AAE/B,UAAM,UAAU,KAAK,MAAM,CAAC,EAAE,KAAK;AACnC,QAAI,CAAC,WAAW,YAAY,SAAU;AAEtC,QAAI;AACJ,QAAI;AACF,gBAAU,KAAK,MAAM,OAAO;AAAA,IAC9B,QAAQ;AACN;AAAA,IACF;AAEA,UAAM,WAAW,QAAQ,YAAY;AAKrC,QAAI,SAAS,eAAe;AAC1B,YAAM,KAAK,SAAS;AACpB,YAAM,eAAe,OAAO,GAAG,qBAAqB,WAAW,GAAG,mBAAmB;AACrF,YAAM,YAAY,OAAO,GAAG,4BAA4B,WAAW,GAAG,0BAA0B;AAChG,YAAM,QAAQ,KAAK,IAAI,GAAG,eAAe,SAAS;AAClD,YAAM,YAAY;AAClB,YAAM,aAAa,OAAO,GAAG,yBAAyB,WAAW,GAAG,uBAAuB;AAC3F,YAAM,WAAW,OAAO,GAAG,uBAAuB,WAAW,GAAG,qBAAqB;AACrF,YAAM,SAAS,aAAa;AAC5B,UAAI,OAAO,GAAG,oBAAoB,SAAU,OAAM,QAAQ,GAAG;AAAA,IAC/D;AAEA,UAAM,YAAY,SAAS,aAAa,CAAC;AACzC,QAAI,CAAC,UAAW;AAKhB,QAAI,UAAU,iBAAiB,cAAc;AAC3C,mBAAa;AAAA,IACf,WAAW,OAAO,UAAU,iBAAiB,YAAY,UAAU,iBAAiB,QAAQ;AAC1F,mBAAa;AAAA,IACf;AAEA,UAAM,QAAQ,UAAU,SAAS,SAAS,CAAC;AAC3C,eAAW,QAAQ,OAAO;AACxB,UAAI,KAAK,kBAAkB;AACzB,+BAAuB,KAAK;AAAA,MAC9B;AAEA,UAAI,KAAK,SAAS;AAChB,YAAI,aAAa,YAAY;AAC3B,yBAAe,MAAM;AACrB,kBAAQ,KAAK,EAAE,MAAM,YAAY,UAAU,GAAG,CAAC;AAC/C,iBAAO,KAAK,EAAE,MAAM,kBAAkB,cAAc,QAAQ,SAAS,EAAE,CAAC;AACxE,qBAAW;AAAA,QACb;AACA,cAAM,QAAQ,QAAQ,QAAQ,SAAS,CAAC;AACxC,YAAI,OAAO,SAAS,WAAY;AAChC,cAAM,QAAQ,KAAK,QAAQ;AAC3B,cAAM,YAAY;AAClB,YAAI,KAAK,kBAAkB;AACzB,gBAAM,oBAAoB,KAAK;AAAA,QACjC;AACA,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,cAAc,QAAQ,SAAS;AAAA,UAC/B;AAAA,QACF,CAAC;AAAA,MACH,WAAW,KAAK,cAAc;AAC5B,uBAAe,MAAM;AACrB,qBAAa;AACb,cAAM,QAAkB;AAAA,UACtB,MAAM;AAAA,UACN,IAAI,KAAK,aAAa,MAAM,QAAQ,QAAQ,MAAM;AAAA,UAClD,MAAM,KAAK,aAAa,QAAQ;AAAA,UAChC,WAAW,KAAK,aAAa,QAAQ,CAAC;AAAA,UACtC,kBAAkB,KAAK,oBAAoB;AAAA,QAC7C;AACA,gBAAQ,KAAK,KAAK;AAClB,cAAM,eAAe,QAAQ,SAAS;AACtC,eAAO,KAAK,EAAE,MAAM,kBAAkB,aAAa,CAAC;AACpD,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN;AAAA,UACA,OAAO,KAAK,UAAU,MAAM,SAAS;AAAA,QACvC,CAAC;AACD,eAAO,KAAK,EAAE,MAAM,gBAAgB,cAAc,UAAU,MAAM,CAAC;AAAA,MACrE,WAAW,KAAK,SAAS,QAAW;AAClC,YAAI,aAAa,QAAQ;AACvB,yBAAe,MAAM;AACrB,kBAAQ,KAAK,EAAE,MAAM,QAAQ,MAAM,GAAG,CAAC;AACvC,iBAAO,KAAK,EAAE,MAAM,cAAc,cAAc,QAAQ,SAAS,EAAE,CAAC;AACpE,qBAAW;AAAA,QACb;AACA,cAAM,QAAQ,QAAQ,QAAQ,SAAS,CAAC;AACxC,YAAI,OAAO,SAAS,OAAQ;AAC5B,cAAM,QAAQ,KAAK;AACnB,eAAO,KAAK,EAAE,MAAM,cAAc,cAAc,QAAQ,SAAS,GAAG,OAAO,KAAK,KAAK,CAAC;AAAA,MACxF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,KAAK,OAA8B;AACjC,YAAM,SAA4B,CAAC;AACnC,gBAAU;AACV,YAAM,QAAQ,OAAO,MAAM,IAAI;AAC/B,eAAS,MAAM,IAAI,KAAK;AACxB,iBAAW,WAAW,MAAO,aAAY,SAAS,MAAM;AACxD,aAAO,SAAS,MAAM;AAAA,IACxB;AAAA,IACA,QAAuB;AACrB,YAAM,SAA4B,CAAC;AACnC,UAAI,OAAO,KAAK,MAAM,GAAI,aAAY,QAAQ,MAAM;AACpD,eAAS;AACT,qBAAe,MAAM;AACrB,0BAAoB;AACpB,aAAO,SAAS,MAAM;AAAA,IACxB;AAAA,EACF;AACF;;;AC5OA,SAAS,cAAAC,aAAY,kBAAkB;AAmBvC,IAAM,2BAA2B;AAMjC,SAAS,wBAAwB,KAA6B;AAC5D,MAAI,OAAO,QAAQ,YAAY,IAAI,WAAW,EAAG,QAAO;AACxD,MAAI,IAAI,SAAS,GAAG,KAAK,IAAI,SAAS,MAAM,EAAG,QAAO;AACtD,SAAO,yBAAyB,KAAK,GAAG;AAC1C;AAMA,SAAS,wBACP,wBACA,KACoB;AACpB,MAAI,CAAC,uBAAwB,QAAO;AACpC,SAAO,wBAAwB,GAAG,IAAI,MAAM;AAC9C;AAKA,SAAS,gBACP,SACA,mBACQ;AACR,MAAI,qBAAqB,OAAO,sBAAsB,YAAY,kBAAkB,KAAK,EAAE,SAAS,GAAG;AACrG,WAAO,kBAAkB,KAAK;AAAA,EAChC;AAEA,QAAM,WAAW,QAAQ,WAAW,CAAC;AACrC,MAAI,CAAC,UAAU;AACb,WAAO,WAAW;AAAA,EACpB;AAEA,QAAM,cACJ,OAAO,SAAS,YAAY,WACxB,SAAS,UACT,KAAK,UAAU,SAAS,WAAW,EAAE;AAE3C,QAAM,OAAOC,YAAW,QAAQ,EAC7B,OAAO,4BAA4B,WAAW,EAAE,EAChD,OAAO,EACP,SAAS,GAAG,EAAE;AAEjB,OAAK,CAAC,IAAK,KAAK,CAAC,IAAI,KAAQ;AAC7B,OAAK,CAAC,IAAK,KAAK,CAAC,IAAI,KAAQ;AAC7B,QAAM,MAAM,CAAC,GAAG,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AACzE,SAAO,GAAG,IAAI,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,MAAM,GAAG,EAAE,CAAC,IAAI,IAAI,MAAM,IAAI,EAAE,CAAC,IAAI,IAAI,MAAM,IAAI,EAAE,CAAC,IAAI,IAAI,MAAM,EAAE,CAAC;AAC1G;AAKA,SAAS,yBAAyB,WAA2B;AAC3D,QAAM,QAAQA,YAAW,MAAM,EAAE,OAAO,eAAe,SAAS,EAAE,EAAE,OAAO,EAAE,SAAS,GAAG,EAAE;AAC3F,QAAM,CAAC,IAAK,MAAM,CAAC,IAAI,KAAQ;AAC/B,QAAM,CAAC,IAAK,MAAM,CAAC,IAAI,KAAQ;AAC/B,QAAM,MAAM,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAC1E,SAAO,GAAG,IAAI,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,MAAM,GAAG,EAAE,CAAC,IAAI,IAAI,MAAM,IAAI,EAAE,CAAC,IAAI,IAAI,MAAM,IAAI,EAAE,CAAC,IAAI,IAAI,MAAM,EAAE,CAAC;AAC1G;AAKA,SAAS,wBAAwB,WAA2B;AAC1D,MAAI,UAAU,KAAK,SAAS,GAAG;AAC7B,WAAO;AAAA,EACT;AACA,QAAM,OAAOA,YAAW,QAAQ,EAAE,OAAO,uBAAuB,SAAS,EAAE,EAAE,OAAO;AACpF,SAAO,KAAK,eAAe,CAAC,EAAE,SAAS;AACzC;AAKA,SAAS,oBAAoB,IAA4C;AACvE,MAAI,CAAC,MAAM,OAAO,OAAO,SAAU,QAAO;AAC1C,SAAO,GAAG,QAAQ,mBAAmB,GAAG,EAAE,MAAM,GAAG,EAAE;AACvD;AAEA,IAAM,2BAA2B,oBAAI,IAAI;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,SAAS,gBAAgB,QAA0B;AACjD,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,EAAG,QAAO;AAC3E,QAAM,OAAO,oBAAI,IAAI,CAAC,WAAW,OAAO,SAAS,aAAa,CAAC;AAC/D,QAAM,MAA+B,CAAC;AACtC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,QAAI,CAAC,KAAK,IAAI,GAAG,EAAG,KAAI,GAAG,IAAI,gBAAgB,KAAK;AAAA,EACtD;AACA,SAAO;AACT;AAEA,SAAS,0BAA0B,QAA0B;AAC3D,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,MAAI,MAAM,QAAQ,MAAM,EAAG,QAAO,OAAO,IAAI,yBAAyB;AAEtE,QAAM,IAAI;AACV,QAAM,MAA+B,CAAC;AAEtC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,CAAC,GAAG;AAC5C,QAAI,CAAC,yBAAyB,IAAI,GAAG,GAAG;AAEtC,UAAI,QAAQ,WAAW,EAAE,SAAS,UAAa,OAAO,UAAU,UAAU;AACxE,YAAI,OAAO,CAAC,KAAK;AAAA,MACnB;AACA;AAAA,IACF;AACA,QAAI,QAAQ,UAAU,MAAM,QAAQ,KAAK,GAAG;AAC1C,YAAM,SAAS,MAAM,KAAK,CAAC,MAAM,OAAO,MAAM,YAAY,MAAM,MAAM;AACtE,UAAI,OAAQ,KAAI,OAAO;AACvB;AAAA,IACF;AACA,QAAI,QAAQ,gBAAgB,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AACvF,YAAM,QAAiC,CAAC;AACxC,iBAAW,CAAC,UAAU,UAAU,KAAK,OAAO,QAAQ,KAAgC,GAAG;AACrF,cAAM,QAAQ,IAAI,0BAA0B,UAAU;AAAA,MACxD;AACA,UAAI,aAAa;AACjB;AAAA,IACF;AACA,QAAI,QAAQ,UAAU,MAAM,QAAQ,KAAK,KAAK,CAAC,MAAM,MAAM,CAAC,MAAM,OAAO,MAAM,QAAQ,GAAG;AACxF;AAAA,IACF;AACA,QAAI,GAAG,IAAI,0BAA0B,KAAK;AAAA,EAC5C;AACA,SAAO;AACT;AAQA,SAAS,aACP,OACyD;AACzD,MAAI,CAAC,OAAO,OAAQ,QAAO;AAC3B,SAAO;AAAA,IACL;AAAA,MACE,sBAAsB,MAAM,IAAI,CAAC,SAAS;AACxC,cAAM,SAAS,gBAAgB,KAAK,UAAU,KAAK,EAAE,MAAM,UAAU,YAAY,CAAC,EAAE;AACpF,eAAO;AAAA,UACL,MAAM,KAAK;AAAA,UACX,aAAa,KAAK;AAAA,UAClB,YAAY,0BAA0B,MAAM;AAAA,QAC9C;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAKA,SAAS,mBACP,SACA,gBACY;AACZ,QAAM,WAAuB,CAAC;AAC9B,QAAM,WAAW,QAAQ,YAAY,CAAC;AAEtC,aAAW,OAAO,UAAU;AAC1B,QAAI,IAAI,SAAS,QAAQ;AACvB,YAAM,QAAoB,CAAC;AAC3B,UAAI,OAAO,IAAI,YAAY,UAAU;AACnC,cAAM,KAAK,EAAE,MAAM,IAAI,QAAQ,CAAC;AAAA,MAClC,WAAW,MAAM,QAAQ,IAAI,OAAO,GAAG;AACrC,mBAAW,QAAQ,IAAI,SAAS;AAC9B,cAAI,KAAK,SAAS,QAAQ;AACxB,kBAAM,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC;AAAA,UAChC,WAAW,KAAK,SAAS,SAAS;AAChC,kBAAM,KAAK;AAAA,cACT,YAAY;AAAA,gBACV,UAAU,KAAK,YAAY;AAAA,gBAC3B,MAAM,KAAK;AAAA,cACb;AAAA,YACF,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AACA,UAAI,MAAM,SAAS,GAAG;AACpB,iBAAS,KAAK,EAAE,MAAM,QAAQ,MAAM,CAAC;AAAA,MACvC;AAAA,IACF,WAAW,IAAI,SAAS,aAAa;AACnC,UAAI,IAAI,eAAe,WAAW,IAAI,eAAe,WAAW;AAC9D;AAAA,MACF;AAEA,YAAM,iBAAiB,CAAC,IAAI,YAAY,IAAI,aAAa;AACzD,YAAM,cAAc,mBAAmB,IAAI,OAAO,cAAc;AAChE,YAAM,yBAAyB,kBAAkB;AAEjD,YAAM,QAAoB,CAAC;AAQ3B,UAAI;AACJ,UAAI,OAAO,IAAI,YAAY,UAAU;AACnC,cAAM,KAAK,EAAE,MAAM,IAAI,QAAQ,CAAC;AAAA,MAClC,WAAW,MAAM,QAAQ,IAAI,OAAO,GAAG;AACrC,mBAAW,QAAQ,IAAI,SAAS;AAC9B,cAAI,KAAK,SAAS,QAAQ;AACxB,gBAAI,OAAO,KAAK,SAAS,YAAY,KAAK,SAAS,IAAI;AASrD,mCACE,wBAAwB,wBAAwB,KAAK,aAAa,KAClE;AACF;AAAA,YACF;AACA,kBAAM,OAAY,EAAE,MAAM,KAAK,KAAK;AACpC,kBAAM,MACJ;AAAA,cACE;AAAA,cACA,KAAK;AAAA,YACP,KAAK;AACP,gBAAI,KAAK;AACP,mBAAK,mBAAmB;AACxB,mCAAqB;AAAA,YACvB;AACA,kBAAM,KAAK,IAAI;AAAA,UACjB,WAAW,KAAK,SAAS,YAAY;AACnC,kBAAM,eAAe,KAAK;AAC1B,kBAAM,qBACJ,CAAC,0BACA,OAAO,iBAAiB,YAAY,aAAa,KAAK,EAAE,WAAW,GAAG;AAEzE,gBAAI,CAAC,oBAAoB;AAGvB,oBAAM,OAAY;AAAA,gBAChB,SAAS;AAAA,gBACT,MAAM,KAAK,YAAY;AAAA,cACzB;AACA,oBAAM,MAAM,wBAAwB,MAAM,YAAY;AACtD,kBAAI,KAAK;AACP,qCAAqB;AAAA,cACvB;AACA,oBAAM,KAAK,IAAI;AAAA,YACjB,OAAO;AAIL,kBAAI,OAAO,KAAK,YAAY;AAC5B,kBAAI,CAAC,QAAQ,OAAO,iBAAiB,YAAY,aAAa,KAAK,EAAE,WAAW,GAAG,GAAG;AACpF,oBAAI;AACF,wBAAM,SAAS,KAAK,MAAM,YAAY;AACtC,sBAAI,MAAM,QAAQ,OAAO,OAAO,GAAG;AACjC,2BAAO,OAAO,QACX,OAAO,CAAC,MAAW,KAAK,OAAO,EAAE,SAAS,QAAQ,EAClD,IAAI,CAAC,MAAW,EAAE,IAAI,EACtB,KAAK,IAAI;AAAA,kBACd;AAAA,gBACF,QAAQ;AAAA,gBAER;AAAA,cACF;AACA,kBAAI,QAAQ,KAAK,KAAK,MAAM,IAAI;AAC9B,sBAAM,KAAK,EAAE,KAAK,CAAC;AAAA,cACrB;AAAA,YACF;AAAA,UACF,WAAW,KAAK,SAAS,cAAc,KAAK,SAAS,YAAY;AAC/D,kBAAM,OAAY;AAAA,cAChB,cAAc;AAAA,gBACZ,IAAI,oBAAoB,KAAK,EAAE;AAAA,gBAC/B,MAAM,KAAK;AAAA,gBACX,MAAM,KAAK,SAAS,KAAK,aAAa,CAAC;AAAA,cACzC;AAAA,YACF;AACA,kBAAM,MACJ;AAAA,cACE;AAAA,cACA,KAAK;AAAA,YACP,KAAK;AACP,gBAAI,KAAK;AACP,mBAAK,mBAAmB;AACxB,mCAAqB;AAAA,YACvB;AACA,kBAAM,KAAK,IAAI;AAAA,UACjB;AAAA,QACF;AAAA,MACF;AACA,UAAI,MAAM,SAAS,GAAG;AACpB,YAAI,sBAAsB,CAAC,MAAM,KAAK,CAAC,MAAM,EAAE,gBAAgB,GAAG;AAChE,gBAAM,MAAM,SAAS,CAAC,EAAE,mBAAmB;AAAA,QAC7C;AACA,iBAAS,KAAK,EAAE,MAAM,SAAS,MAAM,CAAC;AAAA,MACxC;AAAA,IACF,WAAW,IAAI,SAAS,cAAc;AACpC,UAAI,WAAW,IAAI,YAAY,IAAI;AACnC,UAAI,CAAC,aAAa,IAAI,cAAc,IAAI,KAAK;AAC3C,cAAM,cAAc,IAAI,cAAc,IAAI;AAC1C,cAAM,eAAe,oBAAoB,WAAW;AACpD,mBAAW,QAAQ,UAAU;AAC3B,cAAI,KAAK,SAAS,eAAe,MAAM,QAAQ,KAAK,OAAO,GAAG;AAC5D,kBAAM,QAAQ,KAAK,QAAQ;AAAA,cACzB,CAAC,OACE,EAAE,SAAS,cAAc,EAAE,SAAS,gBACpC,EAAE,OAAO,eAAe,oBAAoB,EAAE,EAAE,MAAM;AAAA,YAC3D;AACA,gBAAI,OAAO;AACT,yBAAW,MAAM;AACjB;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,UAAI,WAAW;AACf,YAAM,aAAyB,CAAC;AAEhC,UAAI,MAAM,QAAQ,IAAI,OAAO,GAAG;AAC9B,cAAM,YAAsB,CAAC;AAC7B,mBAAW,KAAK,IAAI,SAAS;AAC3B,cAAI,OAAO,MAAM,UAAU;AACzB,sBAAU,KAAK,CAAC;AAAA,UAClB,WAAW,EAAE,SAAS,QAAQ;AAC5B,sBAAU,KAAK,EAAE,IAAI;AAAA,UACvB,WAAW,EAAE,SAAS,SAAS;AAC7B,uBAAW,KAAK;AAAA,cACd,YAAY;AAAA,gBACV,UAAU,EAAE,YAAY;AAAA,gBACxB,MAAM,EAAE;AAAA,cACV;AAAA,YACF,CAAC;AAAA,UACH,OAAO;AACL,sBAAU,KAAK,KAAK,UAAU,CAAC,CAAC;AAAA,UAClC;AAAA,QACF;AACA,mBAAW,UAAU,KAAK,IAAI;AAAA,MAChC,WAAW,OAAO,IAAI,YAAY,UAAU;AAC1C,mBAAW,IAAI;AAAA,MACjB,WAAW,IAAI,SAAS;AACtB,mBAAW,KAAK,UAAU,IAAI,OAAO;AAAA,MACvC;AAKA,YAAM,cAAc,EAAE,QAAQ,SAAS;AAEvC,YAAM,mBAAwB;AAAA,QAC5B,MAAM,YAAY;AAAA,QAClB,UAAU;AAAA,MACZ;AAEA,YAAM,YAAY,IAAI,cAAc,IAAI;AACxC,YAAM,SAAS,oBAAoB,SAAS;AAC5C,UAAI,QAAQ;AACV,yBAAiB,KAAK;AAAA,MACxB;AAEA,UAAI,WAAW,SAAS,GAAG;AACzB,yBAAiB,QAAQ;AAAA,MAC3B;AAEA,eAAS,KAAK;AAAA,QACZ,MAAM;AAAA,QACN,OAAO,CAAC,EAAE,iBAAiB,CAAC;AAAA,MAC9B,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,SAAS,SAAS,GAAG;AACvB,UAAM,cAAc,SAAS,SAAS,SAAS,CAAC;AAChD,QACE,YAAY,SAAS,WACrB,CAAC,YAAY,OAAO,KAAK,CAAC,MAAW,EAAE,gBAAgB,GACvD;AACA,eAAS,KAAK;AAAA,QACZ,MAAM;AAAA,QACN,OAAO,CAAC,EAAE,MAAM,WAAW,CAAC;AAAA,MAC9B,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAOO,SAAS,4BAA4B,QAAqD;AAC/F,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,kBAAkB;AAAA,EACpB,IAAI;AACJ,QAAM,iBAAiB,KAAK;AAE5B,QAAM,YAAY,gBAAgB,SAAS,OAAO,SAAS;AAC3D,QAAM,eACJ,OAAO,gBAAgB,OAAO,OAAO,iBAAiB,YAAY,OAAO,aAAa,KAAK,EAAE,SAAS,IAClG,OAAO,aAAa,KAAK,IACzB,yBAAyB,SAAS;AACxC,QAAM,WAAW,mBAAmB,SAAS,cAAc;AAG3D,QAAM,eAAe,QAAQ,SAAS,OAAO,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE;AAC5E,QAAM,YAAY,SAAS,SAAS,IAAI,KAAK,IAAI,CAAC,IAAI,YAAY,IAAI,SAAS,MAAM;AAErF,QAAM,YAAY,KAAK;AAEvB,QAAM,SAAiC;AAAA,IACrC,iBAAiB,OAAO,KAAK,IAAI,GAAG,SAAS,SAAS,CAAC,CAAC;AAAA,IACxD,YAAY;AAAA,IACZ,YAAY,GAAG,YAAY,IAAI,YAAY;AAAA,IAC3C,eAAe;AAAA,IACf,aAAa,OAAO,KAAK,QAAQ;AAAA,IACjC,0BAA0B,OAAO,KAAK,QAAQ;AAAA,IAC9C,uBAAuB,OAAO,KAAK,WAAW;AAAA,EAChD;AAEA,QAAM,mBAAwC;AAAA,IAC5C;AAAA,IACA,gBAAgB,KAAK;AAAA,EACvB;AAEA,QAAM,UAA+B;AAAA,IACnC;AAAA,EACF;AAEA,MAAI,QAAQ,cAAc;AACxB,YAAQ,oBAAoB;AAAA,MAC1B,MAAM;AAAA,MACN,OAAO,CAAC,EAAE,MAAM,QAAQ,aAAa,CAAC;AAAA,IACxC;AAAA,EACF;AAEA,QAAM,QAAQ,aAAa,QAAQ,KAAK;AACxC,MAAI,OAAO;AACT,YAAQ,QAAQ;AAAA,EAClB;AAEA,MAAI,OAAO,cAAc,OAAO,eAAe,UAAU,OAAO,eAAe,QAAQ;AACrF,QAAI,OAAO;AACX,UAAM,KAAK,OAAO,OAAO,UAAU,EAAE,YAAY;AACjD,QAAI,OAAO,OAAQ,QAAO;AAAA,aACjB,OAAO,SAAS,OAAO,WAAY,QAAO;AACnD,YAAQ,aAAa;AAAA,MACnB,uBAAuB;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,UAAQ,SAAS;AACjB,UAAQ,mBAAmB;AAC3B,UAAQ,YAAY,wBAAwB,SAAS;AAErD,SAAO;AAAA,IACL,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP,WAAW;AAAA,IACX,aAAa;AAAA,EACf;AACF;;;AFzeO,SAAS,kBACd,OACA,SACA,SAC6B;AAC7B,QAAM,SAAS,kCAAkC;AAEjD,GAAC,YAAY;AACX,UAAM,SAA2B;AAAA,MAC/B,MAAM;AAAA,MACN,SAAS,CAAC;AAAA,MACV,KAAK,MAAM;AAAA,MACX,UAAU,MAAM;AAAA,MAChB,OAAO,MAAM;AAAA,MACb,OAAO;AAAA,QACL,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,WAAW;AAAA,QACX,YAAY;AAAA,QACZ,aAAa;AAAA,QACb,MAAM,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,YAAY,GAAG,OAAO,EAAE;AAAA,MACrE;AAAA,MACA,YAAY;AAAA,MACZ,WAAW,KAAK,IAAI;AAAA,IACtB;AAEA,QAAI;AACF,aAAO,KAAK,EAAE,MAAM,SAAS,SAAS,OAAO,CAAC;AAE9C,YAAM,YAAY,SAAS;AAC3B,UAAI,CAAC,WAAW;AACd,cAAM,IAAI,MAAM,gEAAgE;AAAA,MAClF;AAEA,YAAM,EAAE,OAAO,UAAU,IAAI,uBAAuB,SAAS;AAE7D,YAAM,SAAS,SAAS;AAGxB,YAAM,eACJ,WAAW,kBAAkB,WAAW,OAAO,QAAQ,iBAAiB,WACpE,QAAQ,eACR;AACN,YAAM,WAAW,mBAAmB;AACpC,YAAM,OAAO,iBAAiB,MAAM,IAAI,QAAQ,QAAQ;AAExD,YAAM,cAAc,4BAA4B;AAAA,QAC9C;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAW,SAAS;AAAA,QACpB;AAAA,QACA,iBAAiB,MAAM;AAAA,QACvB,YAAY,SAAS;AAAA,MACvB,CAAC;AAED,YAAM,MAAM,MAAM,gBAAgB;AAAA,QAChC;AAAA,QACA,MAAM;AAAA,QACN,MAAM;AAAA,QACN,QAAQ,SAAS;AAAA,MACnB,CAAC;AAED,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,UAAU,MAAM,IAAI,KAAK;AAC/B,cAAM,IAAI,MAAM,8BAA8B,IAAI,MAAM,MAAM,OAAO,EAAE;AAAA,MACzE;AAEA,UAAI,CAAC,IAAI,MAAM;AACb,cAAM,IAAI,MAAM,+CAA+C;AAAA,MACjE;AAEA,YAAM,SAAS,IAAI,KAAK,UAAU;AAClC,YAAM,UAAU,IAAI,YAAY;AAChC,YAAM,OAAO,cAAc;AAS3B,YAAM,kBAAkB,CAAC,QAAuB;AAC9C,eAAO,UAAU,IAAI;AACrB,eAAO,MAAM,QAAQ,IAAI,MAAM;AAC/B,eAAO,MAAM,YAAY,IAAI,MAAM;AACnC,eAAO,MAAM,SAAS,IAAI,MAAM;AAChC,eAAO,MAAM,cAAc,IAAI,MAAM;AAErC,YAAI,MAAM,MAAM;AACd,gBAAM,IAAI,MAAM;AAChB,gBAAM,YAAa,OAAO,MAAM,SAAS,EAAE,SAAS,KAAM;AAC1D,gBAAM,aAAc,OAAO,MAAM,UAAU,EAAE,UAAU,KAAM;AAC7D,gBAAM,YAAa,OAAO,MAAM,aAAa,EAAE,aAAa,KAAM;AAClE,iBAAO,MAAM,OAAO;AAAA,YAClB,OAAO;AAAA,YACP,QAAQ;AAAA,YACR,WAAW;AAAA,YACX,YAAY;AAAA,YACZ,OAAO,YAAY,aAAa;AAAA,UAClC;AAAA,QACF;AAEA,eAAO,aAAa,IAAI;AACxB,mBAAW,MAAM,IAAI,QAAQ;AAC3B,iBAAO,KAAK,EAAE,GAAG,IAAI,SAAS,OAAO,CAAC;AAAA,QACxC;AAAA,MACF;AAEA,aAAO,MAAM;AACX,cAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,YAAI,KAAM;AAEV,wBAAgB,KAAK,KAAK,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC,CAAC,CAAC;AAAA,MACpE;AAEA,sBAAgB,KAAK,MAAM,CAAC;AAE5B,YAAM,aACJ,OAAO,eAAe,aAAa,OAAO,eAAe,WACrD,OAAO,aACP;AAEN,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,MACX,CAAC;AACD,aAAO,IAAI;AAAA,IACb,SAAS,OAAO;AACd,aAAO,aAAa,SAAS,QAAQ,UAAU,YAAY;AAC3D,aAAO,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC3E,aAAO,KAAK,EAAE,MAAM,SAAS,QAAQ,OAAO,YAAY,OAAO,OAAO,CAAC;AACvE,aAAO,IAAI;AAAA,IACb;AAAA,EACF,GAAG;AAEH,SAAO;AACT;;;AG3IA,IAAM,aACJ;AAEF,eAAsB,aACpB,KACsD;AACtD,QAAM,SAAS,MAAM,IAAI,eAAe,uBAAuB,WAAW;AAC1E,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,uBAAuB,MAAM;AACtC;AAEA,eAAe,oBAAoB,KAA6C;AAC9E,MAAK,MAAM,aAAa,GAAG,MAAO,MAAM;AACtC,eAAW,KAAK,gDAAgD,SAAS;AACzE;AAAA,EACF;AACA,MAAI;AACF,QAAI,IAAI,MAAO,KAAI,GAAG,OAAO,mCAA8B,MAAM;AAMjE,UAAM,SAAS,mBAAmB,EAAE;AACpC,UAAM,IAAI,eAAe,UAAU,EAAE,OAAO,MAAM,WAAW,CAAC,WAAW,GAAG,QAAQ,IAAI,OAAO,CAAC;AAChG,UAAM,UAAU,iBAAiB;AACjC,QAAI,CAAC,WAAW,mBAAmB,EAAE,YAAY,QAAQ;AAGvD,UAAI,SAAS;AACX,mBAAW,KAAK,sDAAsD,SAAS;AAC/E,mBAAW,KAAK,iBAAiB,OAAO,CAAC;AACzC;AAAA,MACF;AACA,YAAM,IAAI,MAAM,4BAA4B;AAAA,IAC9C;AACA,eAAW,KAAK,iBAAiB,OAAO,CAAC;AAAA,EAC3C,SAAS,KAAU;AACjB,eAAW,KAAK,2BAA2B,IAAI,OAAO,IAAI,OAAO;AAAA,EACnE;AACF;AAEO,SAAS,2BAA2B,MAAsB;AAC/D,QAAM,OAAO,QAAQ,IAAI,KAAK,EAAE,YAAY;AAE5C,MAAI,QAAQ,QAAS,QAAO;AAC5B,MAAI,QAAQ,UAAW,QAAO;AAC9B,SAAO;AACT;AAEA,eAAe,mBACb,KACA,aACe;AACf,MAAK,MAAM,aAAa,GAAG,MAAO,MAAM;AACtC,eAAW,KAAK,gDAAgD,SAAS;AACzE;AAAA,EACF;AACA,MAAI,CAAC,aAAa;AAChB,eAAW,KAAK,gCAAgC,OAAO;AACvD;AAAA,EACF;AACA,MAAI;AACF,QAAI,IAAI,MAAO,KAAI,GAAG,OAAO,gCAA2B,MAAM;AAI9D,UAAM,UAAU,MAAM,YAAY,QAAQ,KAAK,EAAE,OAAO,MAAM,YAAY,MAAM,QAAQ,IAAI,OAAO,CAAC;AACpG,QAAI,CAAC,SAAS;AACZ,iBAAW,KAAK,0BAA0B,OAAO;AACjD;AAAA,IACF;AACA,qBAAiB,aAAa,GAAG;AACjC,eAAW,KAAK,mBAAmB,OAAO,CAAC;AAAA,EAC7C,SAAS,KAAU;AACjB,eAAW,KAAK,0BAA0B,IAAI,OAAO,IAAI,OAAO;AAAA,EAClE;AACF;AAEA,eAAsB,yBACpB,MACA,KACA,aACe;AACf,QAAM,SAAS,QAAQ,IAAI,KAAK,EAAE,MAAM,KAAK,EAAE,OAAO,OAAO;AAC7D,QAAM,MAAM,2BAA2B,MAAM,CAAC,KAAK,EAAE;AAErD,MAAI,QAAQ,SAAS;AACnB,UAAM,mBAAmB,KAAK,WAAW;AACzC;AAAA,EACF;AAEA,MAAI,QAAQ,UAAU;AACpB,UAAM,oBAAoB,GAAG;AAC7B;AAAA,EACF;AAEA,MAAI,QAAQ,YAAY;AACtB,UAAM,aAAa,KAAK,WAAW;AACnC;AAAA,EACF;AAEA,MAAI,QAAQ,WAAW;AACrB,QAAI;AACF,UAAI,IAAI,MAAO,KAAI,GAAG,OAAO,2BAAsB,MAAM;AACzD,YAAM,IAAI,eAAe,UAAU,EAAE,OAAO,MAAM,WAAW,CAAC,WAAW,GAAG,QAAQ,IAAI,OAAO,CAAC;AAChG,iBAAW,KAAK,4CAA4C;AAAA,IAC9D,SAAS,KAAU;AACjB,iBAAW,KAAK,sBAAsB,IAAI,OAAO,IAAI,OAAO;AAAA,IAC9D;AACA;AAAA,EACF;AAEA,MAAI,QAAQ,SAAS;AACnB,QAAI,IAAI,OAAO;AACb,UAAI,GAAG,cAAc,oBAAoB;AACzC,UAAI,GAAG,OAAO,yCAAyC,MAAM;AAAA,IAC/D,OAAO;AACL,iBAAW,KAAK,gDAAgD;AAAA,IAClE;AACA;AAAA,EACF;AAEA,aAAW,KAAK,UAAU;AAC5B;;;AClIO,IAAM,gBAAgB;AAE7B,IAAM,cAAc;AAAA,EAClB,EAAE,MAAM,SAAS,aAAa,uCAAuC;AAAA,EACrE,EAAE,MAAM,UAAU,aAAa,kEAAkE;AAAA,EACjG,EAAE,MAAM,WAAW,aAAa,8BAA8B;AAAA,EAC9D,EAAE,MAAM,YAAY,aAAa,8CAA8C;AAAA,EAC/E,EAAE,MAAM,SAAS,aAAa,yBAAyB;AACzD;AAEe,SAAR,cAAkB,IAAwB;AAC/C,QAAM,cAAc,IAAI,uBAAuB,qBAAqB,CAAC;AAErE,KAAG,iBAAiB,aAAa;AAAA,IAC/B,MAAM;AAAA,IACN,SAAS;AAAA,IACT,KAAK;AAAA,IACL,QAAQ,CAAC;AAAA;AAAA,IACT,OAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,cAAc;AAAA,MACd;AAAA,IACF;AAAA,IACA,cAAc;AAAA,IACd,eAAe,OAAO,YAAiB,eAAe,OAAO;AAAA,EAC/D,CAAC;AAED,KAAG,gBAAgB,eAAe;AAAA,IAChC,aAAa;AAAA,IACb,wBAAwB,CAAC,WAAW;AAClC,YAAM,UAAU,OAAO,KAAK;AAC5B,UAAI,YAAY,IAAI;AAClB,eAAO,YAAY,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,EAAE,MAAM,aAAa,EAAE,YAAY,EAAE;AAAA,MAC9F;AACA,UAAI,KAAK,KAAK,OAAO,EAAG,QAAO;AAC/B,YAAM,QAAQ,QAAQ,YAAY;AAClC,aAAO,YAAY,OAAO,CAAC,MAAM,EAAE,KAAK,WAAW,KAAK,CAAC,EAAE,IAAI,CAAC,OAAO;AAAA,QACrE,OAAO,EAAE;AAAA,QACT,OAAO,EAAE;AAAA,QACT,aAAa,EAAE;AAAA,MACjB,EAAE;AAAA,IACJ;AAAA,IACA,SAAS,OAAO,MAAc,QAAiC;AAC7D,YAAM,yBAAyB,MAAM,KAAK,WAAW;AAAA,IACvD;AAAA,EACF,CAAC;AAGD,KAAG,GAAG,iBAAiB,OAAO,QAAQ,QAAQ;AAC5C,UAAM,YAAY,gBAAgB,GAAG;AAAA,EACvC,CAAC;AAID,KAAG,GAAG,gBAAgB,OAAO,OAAO,QAAQ;AAC1C,UAAM,WAAW;AAAA,MACf,IAAI,IAAI;AAAA,MACR,eAAe,IAAI;AAAA,MACnB,OAAO,MAAM,SAAS,IAAI;AAAA,IAC5B;AACA,UAAM,YAAY,gBAAgB,QAAQ;AAAA,EAC5C,CAAC;AAGD,KAAG,GAAG,iBAAiB,OAAO,QAAQ,QAAQ;AAC5C,UAAM,YAAY,gBAAgB,GAAG;AAAA,EACvC,CAAC;AACH;",
4
+ "sourcesContent": ["import { createHash, randomBytes } from \"node:crypto\";\nimport type { OAuthCredentials, OAuthLoginCallbacks } from \"@earendil-works/pi-ai\";\nimport { postAntigravity } from \"./protocol.ts\";\n\nexport const REDIRECT_URI = \"https://antigravity.google/oauth-callback\";\nexport const AUTH_URL = \"https://accounts.google.com/o/oauth2/auth\";\nexport const TOKEN_URL = \"https://oauth2.googleapis.com/token\";\nexport const OAUTH_CALLBACK_TIMEOUT_MS = 5 * 60 * 1000;\nexport const FALLBACK_PROJECT_ID = \"aicode-consumers\";\n\nexport const SCOPES = [\n \"https://www.googleapis.com/auth/cloud-platform\",\n \"https://www.googleapis.com/auth/userinfo.email\",\n \"https://www.googleapis.com/auth/userinfo.profile\",\n \"https://www.googleapis.com/auth/cclog\",\n \"https://www.googleapis.com/auth/experimentsandconfigs\",\n \"https://www.googleapis.com/auth/aicode\",\n \"openid\",\n];\n\nexport const CLIENT_ID =\n process.env.ANTIGRAVITY_CLIENT_ID ||\n Buffer.from(\n \"MTA3MTAwNjA2MDU5MS10bWhzc2luMmgyMWxjcmUyMzV2dG9sb2poNGc0MDNlcC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbQ==\",\n \"base64\"\n ).toString(\"utf-8\");\n\nexport const CLIENT_SECRET =\n process.env.ANTIGRAVITY_CLIENT_SECRET ||\n Buffer.from(\"R09DU1BYLUs1OEZXUjQ4NkxkTEoxbUxCOHNYQzR6NnFEQWY=\", \"base64\").toString(\"utf-8\");\n\nfunction base64Url(buf: Buffer): string {\n return buf.toString(\"base64url\");\n}\n\nexport function extractCodeFromInput(rawInput: string): string {\n let code = rawInput.trim();\n if (!code) {\n throw new Error(\"No authorization code provided\");\n }\n\n if (code.startsWith(\"code=\")) {\n code = code.slice(5).trim();\n if (!code) {\n throw new Error(\"No authorization code provided\");\n }\n }\n\n // Reject callback URLs and other non-code input here instead of passing\n // garbage downstream (Google rejects those with a bare invalid_grant).\n // Note: real codes contain \"/\" (e.g. \"4/0ATs\u2026\"), so only reject\n // characters a code can never contain.\n if (/[\\s:?#&=%+]/.test(code)) {\n throw new Error(\"Invalid authorization code: paste the code value itself, not the callback URL\");\n }\n\n return code;\n}\n\nfunction generatePKCE(): { verifier: string; challenge: string } {\n const verifier = base64Url(randomBytes(32));\n const challenge = base64Url(createHash(\"sha256\").update(verifier).digest());\n return { verifier, challenge };\n}\n\nexport async function fetchProjectId(token: string, signal?: AbortSignal): Promise<string> {\n try {\n const res = await postAntigravity({\n token,\n path: \"v1internal:loadCodeAssist\",\n body: { metadata: { ideType: \"ANTIGRAVITY\" } },\n signal,\n });\n if (res.ok) {\n const data = (await res.json()) as { cloudaicompanionProject?: string };\n if (data.cloudaicompanionProject) {\n return data.cloudaicompanionProject;\n }\n }\n } catch {\n // Fall back to standard project\n }\n return FALLBACK_PROJECT_ID;\n}\n\nexport async function loginAntigravity(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n const { verifier, challenge } = generatePKCE();\n // state is still sent for agy wire parity, but never validated back:\n // with code-only paste the callback URL (and its state) never reaches us.\n const state = randomBytes(16).toString(\"hex\");\n\n const authParams = new URLSearchParams({\n client_id: CLIENT_ID,\n response_type: \"code\",\n redirect_uri: REDIRECT_URI,\n scope: SCOPES.join(\" \"),\n code_challenge: challenge,\n code_challenge_method: \"S256\",\n state,\n access_type: \"offline\",\n prompt: \"consent\",\n });\n\n const authUrl = `${AUTH_URL}?${authParams.toString()}`;\n callbacks.onAuth({ url: authUrl });\n\n const manualPromptPromise = (async () => {\n const input = await callbacks.onPrompt({\n message: \"Paste authorization code from browser:\",\n });\n return extractCodeFromInput(input);\n })();\n\n const timeout = new Promise<never>((_, rej) =>\n setTimeout(() => rej(new Error(\"Timed out waiting for authorization code (5 minutes)\")), OAUTH_CALLBACK_TIMEOUT_MS)\n );\n\n const code = await Promise.race([manualPromptPromise, timeout]);\n\n const tokenParams = new URLSearchParams({\n client_id: CLIENT_ID,\n client_secret: CLIENT_SECRET,\n code,\n grant_type: \"authorization_code\",\n redirect_uri: REDIRECT_URI,\n code_verifier: verifier,\n });\n\n const tokenRes = await fetch(TOKEN_URL, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: tokenParams.toString(),\n });\n\n if (!tokenRes.ok) {\n const errText = await tokenRes.text();\n throw new Error(`Failed to exchange token (${tokenRes.status}): ${errText}`);\n }\n\n const tokens = (await tokenRes.json()) as {\n access_token: string;\n refresh_token?: string;\n expires_in?: number;\n };\n const projectId = await fetchProjectId(tokens.access_token);\n\n return {\n refresh: tokens.refresh_token || \"\",\n access: JSON.stringify({ token: tokens.access_token, projectId }),\n expires: Date.now() + (tokens.expires_in || 3600) * 1000,\n };\n}\n\nexport async function refreshAntigravityToken(\n credentials: OAuthCredentials,\n signal?: AbortSignal\n): Promise<OAuthCredentials> {\n const params = new URLSearchParams({\n client_id: CLIENT_ID,\n client_secret: CLIENT_SECRET,\n refresh_token: credentials.refresh,\n grant_type: \"refresh_token\",\n });\n\n const res = await fetch(TOKEN_URL, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: params.toString(),\n signal,\n });\n\n if (!res.ok) {\n const errText = await res.text();\n throw new Error(`Failed to refresh token (${res.status}): ${errText}`);\n }\n\n const tokens = (await res.json()) as {\n access_token: string;\n refresh_token?: string;\n expires_in?: number;\n };\n\n let existingProjectId = FALLBACK_PROJECT_ID;\n try {\n const parsed = JSON.parse(credentials.access);\n if (parsed.projectId) existingProjectId = parsed.projectId;\n } catch {\n // ignore\n }\n\n // Re-resolve: a login-time lookup failure pins the \"aicode-consumers\"\n // default forever unless refresh retries it. A failed lookup yields that\n // same default, which must never clobber a known value.\n const freshProjectId = await fetchProjectId(tokens.access_token, signal);\n const projectId = freshProjectId !== FALLBACK_PROJECT_ID ? freshProjectId : existingProjectId;\n\n return {\n refresh: tokens.refresh_token || credentials.refresh,\n access: JSON.stringify({ token: tokens.access_token, projectId }),\n expires: Date.now() + (tokens.expires_in || 3600) * 1000,\n };\n}\n\nexport function getApiKey(credentials: OAuthCredentials): string {\n return credentials.access;\n}\n\nexport function parseStoredCredentials(rawApiKey: string): { token: string; projectId: string } {\n try {\n const parsed = JSON.parse(rawApiKey);\n if (parsed.token) {\n return { token: parsed.token, projectId: parsed.projectId || FALLBACK_PROJECT_ID };\n }\n } catch {\n // If plain token\n }\n return { token: rawApiKey, projectId: FALLBACK_PROJECT_ID };\n}\n", "export const DEFAULT_ENDPOINT = \"https://daily-cloudcode-pa.googleapis.com\";\nexport const DEFAULT_USER_AGENT =\n \"antigravity/cli/1.1.28 (aidev_client; os_type=linux; arch=amd64; cl=978129418; auth_method=consumer)\";\n\nexport const PROVIDER_ID = \"antigravity\";\n\nexport interface AntigravityPostParams {\n token: string;\n /** Path below the endpoint, e.g. \"v1internal:retrieveUserQuotaSummary\". */\n path: string;\n body: unknown;\n signal?: AbortSignal;\n}\n\n/**\n * Owns the Antigravity base POST: endpoint, Wire Fingerprint headers, and\n * JSON envelope serialization. Callers pass path + body only.\n * Returns the raw Response: status/body reading stays with callers\n * (streaming needs the body reader; error texts differ per call site).\n */\nexport async function postAntigravity({\n token,\n path,\n body,\n signal,\n}: AntigravityPostParams): Promise<Response> {\n return fetch(`${DEFAULT_ENDPOINT}/${path}`, {\n method: \"POST\",\n headers: buildAntigravityHeaders(token),\n body: JSON.stringify(body),\n signal,\n });\n}\n\n/**\n * Builds the canonical HTTP request headers matching the official agy CLI Wire Fingerprint.\n */\nexport function buildAntigravityHeaders(token: string, userAgent = DEFAULT_USER_AGENT): Record<string, string> {\n return {\n Host: \"daily-cloudcode-pa.googleapis.com\",\n \"User-Agent\": userAgent,\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n };\n}\n", "import type { Model } from \"@earendil-works/pi-ai\";\nimport { DEFAULT_ENDPOINT, PROVIDER_ID } from \"./protocol.ts\";\n\n/**\n * Model Catalog map (pure, in-process): Public Model ID \u2194 Runtime Model ID\n * mapping, Model Plan resolution, the versioned snapshot store, Pi Model\n * synthesis, and presentation (display names, Subcommand table format).\n * No network, no credentials, no persistence \u2014 the wire side\n * (fetch/parse/publish) lives in catalog-refresh.ts and talks to this\n * module only through ingestCatalog (writes) and the snapshot accessors\n * (reads), so one ingested generation is the single freshness truth.\n */\nexport function extractBaseModelId(runtimeId: string): string {\n if (runtimeId === \"gemini-pro-agent\") return \"gemini-3.1-pro\";\n if (runtimeId.startsWith(\"gemini-3.1-pro-\")) return \"gemini-3.1-pro\";\n\n return runtimeId.replace(/-(?:high|medium|low|tiered|thinking|agent|extra-low)$/, \"\");\n}\n\n/**\n * Checks Model Family compatibility for thoughtSignature replay.\n * Official agy CLI wire captures demonstrate that:\n * - Gemini models (gemini-3.7, gemini-3.8, etc.) share thoughtSignatures seamlessly.\n * - Claude models replay thoughtSignatures within the Claude family, part-split\n * like Gemini (1.1.27 stream_turn8/9 counter-capture).\n * - Non-Gemini models (Claude, GPT-OSS) do NOT share signatures with Gemini models.\n */\nexport type ModelFamily = \"gemini\" | \"claude\" | \"gpt\" | \"unknown\";\n\n/**\n * The single model-identity predicate (see CONTEXT.md Model Family): which\n * family a model ID belongs to. Accepts any ID space \u2014 Public or Runtime\n * Model ID, with or without a `provider/` prefix \u2014 by stripping the prefix\n * before the prefix match, so every caller classifies identically. Unknown\n * (including missing) IDs report \"unknown\"; mapping that onto a Quota Pool\n * or plan flags stays with the consumer, preserving each caller's default.\n */\nexport function classifyModelFamily(modelId?: string): ModelFamily {\n const bare = ((modelId || \"\").split(\"/\").pop() || \"\").toLowerCase();\n if (bare.startsWith(\"gemini-\")) return \"gemini\";\n if (bare.startsWith(\"claude-\")) return \"claude\";\n if (bare.startsWith(\"gpt-\")) return \"gpt\";\n return \"unknown\";\n}\n\nexport function isCompatibleFamily(msgModel?: string, targetModelId?: string): boolean {\n if (!msgModel || !targetModelId) return true;\n if (msgModel === targetModelId) return true;\n\n const msgFamily = classifyModelFamily(msgModel);\n if (msgFamily !== \"unknown\" && msgFamily === classifyModelFamily(targetModelId)) return true;\n\n return extractBaseModelId(msgModel) === extractBaseModelId(targetModelId);\n}\n\nexport function getThinkingConfig(modelId: string, effort?: string): { includeThoughts: boolean; thinkingBudget: number } {\n const isOff = effort === \"off\" || effort === \"none\";\n if (isOff) {\n return { includeThoughts: false, thinkingBudget: 0 };\n }\n\n // Gemini 3.8 / 3.7 / 3.6 Flash\n if (modelId.includes(\"flash\")) {\n if (effort === \"high\" || effort === \"xhigh\" || modelId.endsWith(\"-high\") || modelId.endsWith(\"-agent\")) {\n return { includeThoughts: true, thinkingBudget: -1 };\n }\n if (effort === \"medium\" || modelId.endsWith(\"-medium\")) {\n return { includeThoughts: true, thinkingBudget: 4000 };\n }\n if (effort === \"low\" || effort === \"minimal\" || modelId.endsWith(\"-low\")) {\n return { includeThoughts: true, thinkingBudget: 1000 };\n }\n return { includeThoughts: true, thinkingBudget: -1 };\n }\n\n // Gemini 3.1 Pro\n if (modelId.includes(\"pro\")) {\n if (effort === \"low\" || effort === \"minimal\" || modelId.endsWith(\"-low\")) {\n return { includeThoughts: true, thinkingBudget: 1001 };\n }\n return { includeThoughts: true, thinkingBudget: 10001 };\n }\n\n // Claude models\n if (modelId.startsWith(\"claude-\")) {\n return { includeThoughts: true, thinkingBudget: 1024 };\n }\n\n // GPT-OSS 120B\n if (modelId.startsWith(\"gpt-oss-\")) {\n return { includeThoughts: true, thinkingBudget: 8192 };\n }\n\n return { includeThoughts: true, thinkingBudget: -1 };\n}\n\nexport interface AvailableModelItem {\n id: string;\n displayName?: string;\n modelEnum?: string;\n remainingFraction?: number;\n resetTime?: string;\n supportsThinking?: boolean;\n thinkingBudget?: number;\n minThinkingBudget?: number;\n supportsImages?: boolean;\n maxTokens?: number;\n maxOutputTokens?: number;\n}\n\nexport interface AvailableModelsCatalog {\n models: AvailableModelItem[];\n modelEnums: Record<string, string>;\n agentModelSorts?: string[];\n deprecated?: Record<string, string>;\n}\n\n// Active catalog state: single versioned snapshot. refreshCatalog owns writes;\n// request paths take one snapshot per call so enums, runtime IDs, and thinking\n// configs always come from the same refresh generation (see Model Plan in CONTEXT.md).\nexport interface CatalogThinking {\n budget?: number;\n supportsThinking?: boolean;\n}\n\nexport interface CatalogSnapshot {\n enums: Record<string, string>;\n runtimeIds: string[];\n thinking?: Record<string, CatalogThinking>;\n deprecated?: Record<string, string>;\n version: number;\n}\n\n/**\n * Builds the per-Runtime-ID thinking lookup for a snapshot generation from\n * parsed catalog items. Pure: shared by the refresh path and tests so both\n * derive the same snapshot shape from one parse.\n */\nexport function buildThinkingMap(models: AvailableModelItem[]): Record<string, CatalogThinking> {\n return Object.fromEntries(\n models.map((m) => [\n m.id,\n {\n ...(typeof m.thinkingBudget === \"number\" ? { budget: m.thinkingBudget } : {}),\n supportsThinking: m.supportsThinking ?? false,\n },\n ]),\n );\n}\n\nlet activeStore: CatalogSnapshot = {\n enums: {},\n runtimeIds: [],\n thinking: {},\n deprecated: {},\n version: 0,\n};\n\nexport function getCatalogSnapshot(): CatalogSnapshot {\n return {\n enums: { ...activeStore.enums },\n runtimeIds: [...activeStore.runtimeIds],\n thinking: Object.fromEntries(\n Object.entries(activeStore.thinking ?? {}).map(([id, info]) => [id, { ...info }]),\n ),\n deprecated: { ...(activeStore.deprecated ?? {}) },\n version: activeStore.version,\n };\n}\n\n/**\n * Records one complete refresh generation. Wire-side only: the sole writer is\n * the refresh path in catalog-refresh.ts. Request paths never call this \u2014\n * they read via getCatalogSnapshot and pass the snapshot explicitly.\n */\nexport function updateCatalogStore(\n enums: Record<string, string>,\n runtimeIds: string[],\n thinking: Record<string, CatalogThinking> = {},\n deprecated: Record<string, string> = {}\n): void {\n // A fresh generation is complete: replace instead of merging, so enums for\n // server-removed models are evicted instead of pinned forever.\n activeStore = {\n enums: { ...enums },\n runtimeIds: [...new Set(runtimeIds)],\n thinking: { ...thinking },\n deprecated: { ...deprecated },\n version: activeStore.version + 1,\n };\n}\n\n// Last full Model Catalog generation behind the single catalog seam: the\n// snapshot holds only IDs/enums/thinking, while the models table also needs\n// display names, quota flags, and sort order. Written only by ingestCatalog.\nlet lastCatalog: AvailableModelsCatalog | undefined;\n\n/**\n * Records one complete catalog generation: snapshot store plus the full\n * items the models table formats. The sole writer is the refresh path;\n * request paths read via getCatalogSnapshot / getStoredCatalog and never\n * write, so one generation is always the single freshness truth.\n */\nexport function ingestCatalog(catalog: AvailableModelsCatalog): void {\n updateCatalogStore(\n catalog.modelEnums,\n catalog.models.map((m) => m.id),\n buildThinkingMap(catalog.models),\n catalog.deprecated || {},\n );\n lastCatalog = catalog;\n}\n\n/** Full catalog behind the seam, if any generation was ingested yet. */\nexport function getStoredCatalog(): AvailableModelsCatalog | undefined {\n return lastCatalog;\n}\n\nfunction resolveRuntimeModelId(\n modelId: string,\n effort?: string,\n availableRuntimeIds: string[] = activeStore.runtimeIds\n): string {\n if (\n modelId.endsWith(\"-high\") ||\n modelId.endsWith(\"-medium\") ||\n modelId.endsWith(\"-low\") ||\n modelId.endsWith(\"-thinking\") ||\n modelId.endsWith(\"-agent\") ||\n modelId.endsWith(\"-extra-low\") ||\n modelId.endsWith(\"-tiered\")\n ) {\n return modelId;\n }\n\n const isOff = effort === \"off\" || effort === \"none\";\n const isLow = isOff || effort === \"low\" || effort === \"minimal\";\n const isMedium = effort === \"medium\";\n\n // Tier resolution against the live snapshot: effort picks suffix candidates\n // in preference order, the first one the server lists wins \u2014 so newly\n // released models (e.g. gemini-3.9-flash) resolve with no code change.\n if (Array.isArray(availableRuntimeIds) && availableRuntimeIds.length > 0) {\n const candidates: string[] = [];\n if (isLow) {\n candidates.push(`${modelId}-low`, `${modelId}-extra-low`, modelId);\n } else if (isMedium) {\n candidates.push(`${modelId}-medium`, modelId);\n } else {\n candidates.push(`${modelId}-high`, `${modelId}-thinking`, `${modelId}-agent`, modelId);\n }\n\n for (const cand of candidates) {\n if (availableRuntimeIds.includes(cand)) {\n return cand;\n }\n }\n }\n\n // Fallback heuristics when runtime ID catalog is not available\n if (modelId.includes(\"flash\")) {\n if (isLow) return `${modelId}-low`;\n if (isMedium) return `${modelId}-medium`;\n return `${modelId}-high`;\n }\n\n if (modelId.includes(\"pro\")) {\n if (isLow) return `${modelId}-low`;\n return `${modelId}-high`;\n }\n\n if (modelId.startsWith(\"claude-\")) {\n if (modelId.includes(\"opus\")) return `${modelId}-thinking`;\n return modelId;\n }\n\n if (modelId.startsWith(\"gpt-\")) {\n return `${modelId}-medium`;\n }\n\n return modelId;\n}\n\nexport interface ModelPlan {\n runtimeModelId: string;\n modelEnum: string;\n thinkingConfig: { includeThoughts: boolean; thinkingBudget: number };\n isNonGemini: boolean;\n isClaude: boolean;\n}\n\n/**\n * Resolves one Model Plan (see CONTEXT.md) from a single snapshot generation:\n * Runtime Model ID, model enum, thinking budget, and the non-Gemini flag that\n * switches tool schema mode. Pass an explicit snapshot in tests; otherwise the\n * live snapshot is read once, so enums and runtime IDs never mix generations.\n */\nexport function resolveModelPlan(\n publicModelId: string,\n effort?: string,\n snapshot: CatalogSnapshot = getCatalogSnapshot()\n): ModelPlan {\n const runtimeModelId = followRenames(\n resolveRuntimeModelId(publicModelId, effort, snapshot.runtimeIds),\n snapshot\n );\n const modelEnum = snapshot.enums[runtimeModelId];\n if (!modelEnum) {\n // Fail fast: a retired or mistyped ID must surface here with guidance,\n // not as a cryptic server rejection for an empty model_enum label.\n throw new Error(\n `Unknown model \"${runtimeModelId}\" (not in catalog snapshot v${snapshot.version}). ` +\n `Run /antigravity refresh and pick a current model.`\n );\n }\n const family = classifyModelFamily(runtimeModelId);\n return {\n runtimeModelId,\n modelEnum,\n thinkingConfig: resolveThinkingConfig(runtimeModelId, effort, snapshot),\n isNonGemini: family !== \"gemini\",\n isClaude: family === \"claude\",\n };\n}\n\n/**\n * Follows server-directed renames (deprecatedModelIds), e.g.\n * gemini-3.1-pro-high \u2192 gemini-pro-agent. Applied uniformly to derived and\n * explicitly passed IDs: the server lists the old ID as deprecated, so new\n * code must not keep sending it. Cycles terminate via the visited set.\n */\nfunction followRenames(runtimeModelId: string, snapshot: CatalogSnapshot): string {\n const renamed = snapshot.deprecated;\n if (!renamed) return runtimeModelId;\n let current = runtimeModelId;\n const seen = new Set<string>([current]);\n while (renamed[current] && !seen.has(renamed[current])) {\n current = renamed[current];\n seen.add(current);\n }\n return current;\n}\n\n/**\n * Resolves the thinking config for one Runtime Model ID from a single snapshot\n * generation. Snapshot wire values win; the hardcoded heuristic below only\n * serves snapshots without per-ID thinking data (pre-budget persists) and\n * models the wire marks as non-thinking resolve to disabled thoughts.\n */\nfunction resolveThinkingConfig(\n runtimeModelId: string,\n effort?: string,\n snapshot: CatalogSnapshot = getCatalogSnapshot()\n): { includeThoughts: boolean; thinkingBudget: number } {\n if (effort === \"off\" || effort === \"none\") {\n return { includeThoughts: false, thinkingBudget: 0 };\n }\n const info = snapshot.thinking?.[runtimeModelId];\n if (info) {\n if (typeof info.budget === \"number\") {\n return { includeThoughts: true, thinkingBudget: info.budget };\n }\n if (!info.supportsThinking) {\n return { includeThoughts: false, thinkingBudget: 0 };\n }\n }\n return getThinkingConfig(runtimeModelId, effort);\n}\n\n// Antigravity is quota-based with no per-token billing, so every model\n// reports zero cost instead of fictitious Gemini API prices.\n// Revisit if a metered paid tier ever appears.\nexport function estimateModelCost(_baseId: string): Model<any>[\"cost\"] {\n return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };\n}\n\nexport function synthesizeDynamicModel(baseId: string, items: AvailableModelItem[]): Model<any> {\n const repItem = items.find((it) => it.id === `${baseId}-high` || it.id === baseId) || items[0];\n\n const isFlash = baseId.includes(\"flash\");\n const isClaude = baseId.startsWith(\"claude-\");\n const isGpt = baseId.startsWith(\"gpt-\");\n\n // Static fallbacks mirror captures/agy_cli_1.1.27/models.resp.json\n // (Claude maxTokens 250000 / maxOutputTokens 64000, also seen on the wire\n // in stream_turn8/9). Live refresh overwrites these with catalog values.\n const defaultContext = isFlash ? 1048576 : isClaude ? 250000 : isGpt ? 128000 : 1048576;\n const defaultMaxOutput = isClaude ? 64000 : isGpt ? 32768 : 65536;\n\n return {\n id: baseId,\n name: formatModelDisplayName(baseId, repItem?.displayName),\n provider: PROVIDER_ID,\n api: \"antigravity-api\",\n baseUrl: DEFAULT_ENDPOINT,\n reasoning: repItem?.supportsThinking ?? true,\n input: repItem?.supportsImages ? [\"text\", \"image\"] : [\"text\"],\n cost: estimateModelCost(baseId),\n contextWindow: repItem?.maxTokens || defaultContext,\n maxTokens: repItem?.maxOutputTokens || defaultMaxOutput,\n };\n}\n\nexport function buildDynamicPublicModels(catalog?: AvailableModelsCatalog): Array<Model<any>> {\n if (!catalog || !Array.isArray(catalog.models) || catalog.models.length === 0) {\n return [];\n }\n\n // Filter eligible models according to agentModelSorts (strict agy CLI parity)\n const agentSortIds = catalog.agentModelSorts;\n const eligibleItems =\n Array.isArray(agentSortIds) && agentSortIds.length > 0\n ? catalog.models.filter((m) => agentSortIds.includes(m.id))\n : catalog.models;\n\n const runtimeGroups = new Map<string, AvailableModelItem[]>();\n for (const item of eligibleItems) {\n const baseId = extractBaseModelId(item.id);\n const list = runtimeGroups.get(baseId) || [];\n list.push(item);\n runtimeGroups.set(baseId, list);\n }\n\n // Preserve recommended ordering from agentModelSorts\n const orderedBaseIds: string[] = [];\n if (Array.isArray(agentSortIds)) {\n for (const sortId of agentSortIds) {\n const baseId = extractBaseModelId(sortId);\n if (runtimeGroups.has(baseId) && !orderedBaseIds.includes(baseId)) {\n orderedBaseIds.push(baseId);\n }\n }\n }\n\n for (const baseId of runtimeGroups.keys()) {\n if (!orderedBaseIds.includes(baseId)) {\n orderedBaseIds.push(baseId);\n }\n }\n\n return orderedBaseIds.map((baseId) => synthesizeDynamicModel(baseId, runtimeGroups.get(baseId)!));\n}\n\n/**\n * Model Catalog presentation (pure, in-process): display-name rules and the\n * Subcommand table format. No network, no snapshot access \u2014 everything enters\n * as plain CatalogSnapshot / AvailableModelsCatalog values, so tests pin the\n * rendered output without fetching. Lives here (not in catalog-refresh.ts)\n * so the wire module keeps only fetch/parse/publish.\n */\nexport function formatModelDisplayName(baseId: string, rawDisplayName?: string): string {\n if (rawDisplayName) {\n const cleaned = rawDisplayName.replace(/\\s*\\([^)]*\\)/g, \"\").trim();\n if (cleaned.length > 0) {\n return cleaned;\n }\n }\n const words = baseId.split(\"-\").map((w) => {\n if (/^\\d+(\\.\\d+)?$/.test(w)) return w;\n return w.charAt(0).toUpperCase() + w.slice(1);\n });\n return words.join(\" \");\n}\n\nfunction formatTokenCount(n?: number): string {\n if (typeof n !== \"number\") return \"n/a\";\n if (n % 1048576 === 0) return `${n / 1048576}M`;\n if (n % 1024 === 0) return `${n / 1024}k`;\n if (n % 1000 === 0) return `${n / 1000}k`;\n // ponytail: odd values (e.g. 65535) round to nearest KiB instead of a noisy decimal\n return `${Math.round(n / 1024)}k`;\n}\n\nexport function formatModelsList(catalog: AvailableModelsCatalog): string {\n const sorts = catalog.agentModelSorts;\n const recommendedOnly = Array.isArray(sorts) && sorts.length > 0;\n const rank = new Map((sorts || []).map((id, i) => [id, i]));\n\n const models = (recommendedOnly ? catalog.models.filter((m) => rank.has(m.id)) : catalog.models.slice()).sort(\n (a, b) => (rank.get(a.id) ?? Number.MAX_SAFE_INTEGER) - (rank.get(b.id) ?? Number.MAX_SAFE_INTEGER) ||\n a.id.localeCompare(b.id)\n );\n\n const rows = models.map((m) => {\n const flags: string[] = [];\n if (m.supportsThinking) flags.push(\"thinking\");\n if (m.supportsImages) flags.push(\"images\");\n return {\n id: m.id,\n name: formatModelDisplayName(m.id, m.displayName || m.id),\n ctx: `${formatTokenCount(m.maxTokens)}/${formatTokenCount(m.maxOutputTokens)}`,\n feat: flags.length > 0 ? flags.join(\", \") : \"-\",\n rem: typeof m.remainingFraction === \"number\" ? `${Math.round(m.remainingFraction * 100)}%` : \"N/A\",\n };\n });\n\n const headers = { id: \"Model\", name: \"Name\", ctx: \"Context\", feat: \"Features\", rem: \"Rem\" };\n const w = {\n id: Math.max(headers.id.length, ...rows.map((r) => r.id.length)),\n name: Math.max(headers.name.length, ...rows.map((r) => r.name.length)),\n ctx: Math.max(headers.ctx.length, ...rows.map((r) => r.ctx.length)),\n feat: Math.max(headers.feat.length, ...rows.map((r) => r.feat.length)),\n };\n\n const lines: string[] = [\n `Available Antigravity Models (${models.length}${recommendedOnly ? \" recommended\" : \"\"}):`,\n ` ${headers.id.padEnd(w.id)} ${headers.name.padEnd(w.name)} ${headers.ctx.padEnd(w.ctx)} ${headers.feat.padEnd(w.feat)} ${headers.rem}`,\n ];\n for (const r of rows) {\n lines.push(\n ` ${r.id.padEnd(w.id)} ${r.name.padEnd(w.name)} ${r.ctx.padEnd(w.ctx)} ${r.feat.padEnd(w.feat)} ${r.rem.padStart(4)}`\n );\n }\n\n return lines.join(\"\\n\");\n}\n", "import { postAntigravity } from \"./protocol.ts\";\nimport { parseStoredCredentials } from \"./auth.ts\";\nimport type { AvailableModelItem, AvailableModelsCatalog } from \"./model-catalog.ts\";\nimport { buildDynamicPublicModels, getCatalogSnapshot, getStoredCatalog, ingestCatalog, updateCatalogStore } from \"./model-catalog.ts\";\nimport type { Model } from \"@earendil-works/pi-ai\";\n\n/**\n * Model Catalog refresh (wire, ports & adapters): fetchAvailableModels fetch,\n * Capture Fixture-shaped parse, and Catalog Persistence publish.\n * Produces AvailableModelsCatalog values and records generations via\n * updateCatalogStore; all pure mapping and presentation lives in model-catalog.ts.\n */\nexport async function refreshCatalog(context: any): Promise<Array<Model<any>>> {\n // Restore from context.stored first for offline restart support,\n // but only into a pristine store: a failed refresh must not clobber a\n // fresher in-memory snapshot with older persisted data.\n const storedEnums = context.stored?.[\"pi-provider-antigravity\"]?.modelEnums;\n const storedRuntimeIds = context.stored?.[\"pi-provider-antigravity\"]?.runtimeIds;\n const storedThinking = context.stored?.[\"pi-provider-antigravity\"]?.thinking;\n const storedDeprecated = context.stored?.[\"pi-provider-antigravity\"]?.deprecated;\n if ((storedEnums || storedRuntimeIds || storedThinking || storedDeprecated) && getCatalogSnapshot().version === 0) {\n updateCatalogStore(storedEnums || {}, storedRuntimeIds || [], storedThinking || {}, storedDeprecated || {});\n }\n\n if (!context.allowNetwork) {\n return context.stored?.models || [];\n }\n\n try {\n const apiKey = context.credential?.access;\n if (!apiKey) {\n return context.stored?.models || [];\n }\n\n const { token, projectId } = parseStoredCredentials(apiKey);\n const catalog = await fetchAvailableModelsCatalog(token, projectId, context.signal);\n ingestCatalog(catalog);\n\n const dynamicModels = buildDynamicPublicModels(catalog);\n\n // Publish to Pi models-store.json from the stored generation (not the\n // transient parse), so stored and published are always one generation.\n if (context.publish) {\n const snap = getCatalogSnapshot();\n await context.publish({\n persist: {\n models: dynamicModels,\n checkedAt: Date.now(),\n \"pi-provider-antigravity\": {\n modelEnums: { ...snap.enums },\n runtimeIds: [...snap.runtimeIds],\n thinking: { ...(snap.thinking ?? {}) },\n deprecated: { ...(snap.deprecated ?? {}) },\n },\n },\n });\n }\n\n return dynamicModels;\n } catch {\n return context.stored?.models || [];\n }\n}\n\n/**\n * Freshness outcome behind the single catalog seam: whether a refresh\n * landed a new Catalog Generation. `commands.ts` reads only this \u2014\n * the snapshot version never crosses the seam.\n * fresh = a new generation landed (a bump also lands when the content is\n * identical: the version is a fetch-success signal, not a change signal).\n * stale = the fetch failed but a retained generation exists.\n * failed = the fetch failed and nothing is retained.\n */\nexport interface CatalogRefreshOutcome {\n status: \"fresh\" | \"stale\" | \"failed\";\n catalog?: AvailableModelsCatalog;\n}\n\n/**\n * Runs one refresh through the given Pi refresh hook and reports which\n * Catalog Generation the callers must show. The hook keeps its own\n * contract (swallow-and-fallback); only the freshness verdict moves here.\n */\nexport async function refreshCatalogGeneration(\n doRefresh: () => unknown,\n): Promise<CatalogRefreshOutcome> {\n const before = getCatalogSnapshot().version;\n await doRefresh();\n const catalog = getStoredCatalog();\n if (!catalog) return { status: \"failed\" };\n if (getCatalogSnapshot().version === before) return { status: \"stale\", catalog };\n return { status: \"fresh\", catalog };\n}\n\nexport function parseAvailableModels(data: any): AvailableModelsCatalog {\n const models: AvailableModelItem[] = [];\n const modelEnums: Record<string, string> = {};\n\n const modelsObj = data.models || {};\n for (const [id, info] of Object.entries<any>(modelsObj)) {\n if (id.startsWith(\"tab_\") || id.startsWith(\"chat_\")) continue; // hide internal completions\n\n const modelEnum = typeof info.model === \"string\" ? info.model : undefined;\n if (modelEnum) {\n modelEnums[id] = modelEnum;\n }\n\n const quotaInfo = info.quotaInfo || {};\n models.push({\n id,\n displayName: info.displayName || info.label || id,\n modelEnum,\n remainingFraction: quotaInfo.remainingFraction,\n resetTime: quotaInfo.resetTime,\n supportsThinking: Boolean(info.supportsThinking),\n thinkingBudget: typeof info.thinkingBudget === \"number\" ? info.thinkingBudget : undefined,\n minThinkingBudget: typeof info.minThinkingBudget === \"number\" ? info.minThinkingBudget : undefined,\n supportsImages: Boolean(info.supportsImages),\n maxTokens: typeof info.maxTokens === \"number\" ? info.maxTokens : undefined,\n maxOutputTokens: typeof info.maxOutputTokens === \"number\" ? info.maxOutputTokens : undefined,\n });\n }\n\n const agentModelSorts: string[] = [];\n if (Array.isArray(data.agentModelSorts)) {\n for (const sortGroup of data.agentModelSorts) {\n if (Array.isArray(sortGroup.groups)) {\n for (const group of sortGroup.groups) {\n if (Array.isArray(group.modelIds)) {\n agentModelSorts.push(...group.modelIds);\n }\n }\n }\n }\n }\n\n models.sort((a, b) => a.id.localeCompare(b.id));\n\n // Server-directed renames (old Runtime Model ID \u2192 current one).\n const deprecated: Record<string, string> = {};\n const rawDeprecated = (data as any).deprecatedModelIds;\n if (rawDeprecated && typeof rawDeprecated === \"object\") {\n for (const [oldId, info] of Object.entries<any>(rawDeprecated)) {\n if (info && typeof info.newModelId === \"string\") {\n deprecated[oldId] = info.newModelId;\n }\n }\n }\n\n return {\n models,\n modelEnums,\n ...(agentModelSorts.length > 0 ? { agentModelSorts } : {}),\n ...(Object.keys(deprecated).length > 0 ? { deprecated } : {}),\n };\n}\n\nexport async function fetchAvailableModelsCatalog(\n token: string,\n projectId: string,\n signal?: AbortSignal\n): Promise<AvailableModelsCatalog> {\n const res = await postAntigravity({\n token,\n path: \"v1internal:fetchAvailableModels\",\n body: { project: projectId },\n signal,\n });\n if (!res.ok) {\n const errText = await res.text();\n throw new Error(`Failed to fetch models (${res.status}): ${errText}`);\n }\n const json = await res.json();\n return parseAvailableModels(json);\n}\n\n\n", "import type { ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport { parseStoredCredentials } from \"./auth.ts\";\nimport { classifyModelFamily } from \"./model-catalog.ts\";\nimport { postAntigravity, PROVIDER_ID } from \"./protocol.ts\";\nimport type { QuotaFooterMode } from \"./settings.ts\";\n\n/**\n * Quota Status module (deep, mock): everything behind the footer slot and\n * /antigravity usage \u2014 Quota Pool wire parse, urgent-window math, ratio\n * self-calibration, throttle, cache, and calibration persistence.\n * Callers use footerFor / ingest / refresh / refreshAndPaint only; the fetch,\n * the ratio state, and the file shape behind the injected store are internal.\n * The store itself (file-backed or in-memory) arrives through QuotaStatusStore.\n */\n\nexport interface QuotaBucket {\n bucketId: string;\n displayName: string;\n window?: string;\n resetTime?: string;\n description?: string;\n remainingFraction: number;\n}\n\nexport interface QuotaGroup {\n displayName: string;\n description?: string;\n buckets: QuotaBucket[];\n}\n\nexport interface QuotaSummary {\n groups: QuotaGroup[];\n description?: string;\n}\n\nexport function renderProgressBar(fraction: number, width = 10): string {\n const clamped = Math.max(0, Math.min(1, fraction));\n const filled = Math.round(clamped * width);\n return `[${\"#\".repeat(filled)}${\"-\".repeat(width - filled)}]`;\n}\n\ntype QuotaWindowKind = \"5h\" | \"weekly\" | \"other\";\n\nconst QUOTA_WINDOW_RANK: Record<QuotaWindowKind, number> = {\n \"5h\": 0,\n weekly: 1,\n other: 2,\n};\n\nfunction classifyQuotaWindow(bucket: QuotaBucket): QuotaWindowKind {\n // The wire reports a machine `window` (\"5h\"/\"weekly\" in every capture). Trust\n // it \u2014 no display-prose guessing. Unknown or missing windows are \"other\".\n const w = (bucket.window || \"\").trim().toLowerCase();\n if (w === \"5h\") return \"5h\";\n if (w === \"weekly\") return \"weekly\";\n return \"other\";\n}\n\nfunction shortWindowLabel(bucket: QuotaBucket, kind: QuotaWindowKind): string {\n if (kind === \"5h\") return \"5h\";\n if (kind === \"weekly\") return \"Wk\";\n return bucket.displayName || bucket.bucketId || \"Quota\";\n}\n\nfunction sortQuotaBuckets(buckets: QuotaBucket[]): QuotaBucket[] {\n return buckets\n .slice()\n .sort((a, b) => QUOTA_WINDOW_RANK[classifyQuotaWindow(a)] - QUOTA_WINDOW_RANK[classifyQuotaWindow(b)]);\n}\n\nexport function formatResetRemaining(resetTime?: string): string | null {\n if (!resetTime) return null;\n const ts = Date.parse(resetTime);\n if (!Number.isFinite(ts)) return null;\n const delta = ts - Date.now();\n if (delta <= 0) return \"ready\";\n const totalMin = Math.ceil(delta / 60000);\n const days = Math.floor(totalMin / (60 * 24));\n const hours = Math.floor((totalMin % (60 * 24)) / 60);\n const mins = totalMin % 60;\n if (days > 0) return `in ${days}d ${hours}h`;\n if (hours > 0) return `in ${hours}h ${mins}m`;\n return `in ${mins}m`;\n}\n\nexport function parseQuotaSummary(data: any): QuotaSummary {\n const groups: QuotaGroup[] = [];\n\n for (const g of data.groups || []) {\n const buckets: QuotaBucket[] = [];\n for (const b of g.buckets || []) {\n buckets.push({\n bucketId: b.bucketId || \"unknown\",\n displayName: b.displayName || b.bucketId || \"Limit\",\n window: b.window,\n resetTime: b.resetTime,\n description: b.description,\n remainingFraction: typeof b.remainingFraction === \"number\" ? b.remainingFraction : 1,\n });\n }\n groups.push({\n displayName: g.displayName || \"Quota Group\",\n description: g.description,\n buckets,\n });\n }\n\n return {\n groups,\n description: data.description,\n };\n}\n\nexport function formatQuotaSummary(summary: QuotaSummary): string {\n const lines: string[] = [];\n\n for (const group of summary.groups) {\n if (lines.length > 0) lines.push(\"\");\n lines.push(`${group.displayName}`);\n for (const b of sortQuotaBuckets(group.buckets)) {\n const pct = Math.round(b.remainingFraction * 100);\n const label = shortWindowLabel(b, classifyQuotaWindow(b)).padEnd(3);\n const reset = formatResetRemaining(b.resetTime);\n const suffix = reset && pct < 100 ? ` (${reset})` : \"\";\n lines.push(` ${label} ${renderProgressBar(b.remainingFraction)} ${String(pct).padStart(3)}% left${suffix}`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\nexport function isGeminiQuotaGroup(group: QuotaGroup): boolean {\n // Bucket ids (\"gemini-5h\" vs \"3p-5h\") \u2014 not display prose (\"Gemini Models\").\n return (group.buckets || []).some((b) => (b.bucketId || \"\").toLowerCase().includes(\"gemini\"));\n}\n\nexport function selectQuotaGroup(groups: QuotaGroup[], modelId?: string): QuotaGroup | undefined {\n if (groups.length === 0) return undefined;\n const family = classifyModelFamily(modelId);\n if (family === \"claude\" || family === \"gpt\") {\n const found = groups.find((g) => !isGeminiQuotaGroup(g));\n if (found) return found;\n }\n return groups.find((g) => isGeminiQuotaGroup(g)) || groups[0];\n}\n\n// Compact one-line footer text (e.g. \"Wk 6% (2d 15h)\") showing the most\n// urgent window of the group backing the current model: 5h and weekly pools\n// differ in volume, so the wall you hit first is min(r5h, rWk \u00D7 R) \u2014 not min\n// fraction. R self-calibrates from observed deltas (see calibrateWeeklyTo5hRatio).\nexport const DEFAULT_WEEKLY_TO_5H_RATIO = 6.0;\n\nexport interface WindowFractionPair {\n \"5h\": number;\n weekly: number;\n}\n\nfunction windowBucket(buckets: QuotaBucket[], kind: QuotaWindowKind): QuotaBucket | undefined {\n return buckets.find((b) => classifyQuotaWindow(b) === kind);\n}\n\n// Pool-group key for persisted pairs: the 5h bucket's wire id minus its window\n// suffix (\"gemini-5h\" \u2192 \"gemini\"). Display names are upstream prose, and the raw\n// id would echo the window inside every record (\"gemini-5h\": { \"5h\", weekly }).\n// Unrecognized shapes keep the full id \u2014 matching only needs determinism.\nfunction poolKey(bucket: QuotaBucket): string {\n const id = bucket.bucketId || \"unknown\";\n return id.replace(/[-_](5h|five-?hour|weekly)$/i, \"\") || id;\n}\n\n// Plain-object form for JSON persistence (see coordinator state).\nexport function extractWindowFractionPairs(summary?: QuotaSummary): Record<string, WindowFractionPair> {\n const pairs: Record<string, WindowFractionPair> = {};\n for (const g of summary?.groups || []) {\n const bucket5h = windowBucket(g.buckets, \"5h\");\n const bucketWeekly = windowBucket(g.buckets, \"weekly\");\n if (bucket5h && bucketWeekly) pairs[poolKey(bucket5h)] = { \"5h\": bucket5h.remainingFraction, weekly: bucketWeekly.remainingFraction };\n }\n return pairs;\n}\n\n// Self-calibrates the 5h vs weekly volume ratio R from delta consumption:\n// the same absolute spend drops the 5h fraction R\u00D7 faster than weekly.\n// First valid pool wins; resets and noise fall outside the guards below.\nexport function calibrateWeeklyTo5hRatio(\n previous: Record<string, WindowFractionPair> | undefined,\n current: Record<string, WindowFractionPair> | undefined,\n currentRatio = DEFAULT_WEEKLY_TO_5H_RATIO,\n): number {\n if (!previous || !current) return currentRatio;\n for (const key of Object.keys(current)) {\n const prev = previous[key];\n const curr = current[key];\n if (!prev || !curr) continue;\n\n const dFiveHour = prev[\"5h\"] - curr[\"5h\"];\n const dWeekly = prev.weekly - curr.weekly;\n\n if (dFiveHour > 0.0001 && dWeekly > 0.00001) {\n const observedRatio = dFiveHour / dWeekly;\n if (observedRatio >= 1.0 && observedRatio <= 20.0) {\n return Math.round(observedRatio * 100) / 100;\n }\n }\n }\n return currentRatio;\n}\n\nfunction selectUrgentBucket(group: QuotaGroup, weeklyTo5hRatio: number): QuotaBucket | undefined {\n const buckets = group.buckets;\n if (buckets.length === 0) return undefined;\n const bucket5h = windowBucket(buckets, \"5h\");\n const bucketWeekly = windowBucket(buckets, \"weekly\");\n if (bucket5h && bucketWeekly) {\n return bucketWeekly.remainingFraction * weeklyTo5hRatio < bucket5h.remainingFraction ? bucketWeekly : bucket5h;\n }\n let worst = buckets[0];\n for (const b of buckets) {\n if (b.remainingFraction < worst.remainingFraction) worst = b;\n }\n return worst;\n}\n\nexport function buildQuotaFooter(\n summary: QuotaSummary,\n modelId?: string,\n weeklyTo5hRatio = DEFAULT_WEEKLY_TO_5H_RATIO,\n): string | undefined {\n const group = selectQuotaGroup(summary.groups, modelId);\n if (!group) return undefined;\n const urgent = selectUrgentBucket(group, weeklyTo5hRatio);\n return urgent ? formatQuotaWindowPart(urgent) : undefined;\n}\n\n// Both windows of the backing group, 5h first (e.g. \"5h 35% (26m) \u00B7 Wk 75% (6d 14h)\").\nexport function buildQuotaFooterBoth(summary: QuotaSummary, modelId?: string): string | undefined {\n const group = selectQuotaGroup(summary.groups, modelId);\n if (!group || group.buckets.length === 0) return undefined;\n return sortQuotaBuckets(group.buckets).map(formatQuotaWindowPart).join(\" \u00B7 \");\n}\n\n// Window prefix names the bucket (\"5h\"/\"Wk\"); unknown windows fall back to\n// the bucket's own display name.\nexport function formatQuotaWindowPart(bucket: QuotaBucket): string {\n const kind = classifyQuotaWindow(bucket);\n const prefix = kind === \"5h\" ? \"5h\" : kind === \"weekly\" ? \"Wk\" : bucket.displayName || bucket.bucketId;\n const pct = Math.round(bucket.remainingFraction * 100);\n const short = formatResetRemaining(bucket.resetTime)?.replace(/^in /, \"\");\n if (short && pct < 100) return `${prefix} ${pct}% (${short})`;\n return `${prefix} ${pct}%`;\n}\n\n// Threshold coloring for footer slots. Event handlers never see Theme, so this\n// uses basic ANSI (theme.fg(\"error\"/\"warning\") equivalents on dark) with a\n// foreground-only reset, mirroring Theme.fg instead of a full reset.\nconst ANSI_RED = \"\\x1b[31m\";\nconst ANSI_YELLOW = \"\\x1b[33m\";\nexport const ANSI_FG_RESET = \"\\x1b[39m\";\n\nexport const QUOTA_WARN_PCT = 30;\nexport const QUOTA_ALERT_PCT = 10;\n\nexport function colorizeQuotaFooter(footer: string | undefined): string | undefined {\n if (!footer) return undefined;\n const match = footer.match(/(\\d+)%/);\n if (!match) return footer;\n const pct = parseInt(match[1], 10);\n if (pct <= QUOTA_ALERT_PCT) return `${ANSI_RED}${footer}${ANSI_FG_RESET}`;\n if (pct <= QUOTA_WARN_PCT) return `${ANSI_YELLOW}${footer}${ANSI_FG_RESET}`;\n return footer;\n}\n\n// Colors each \" \u00B7 \"-separated window part by its own percentage.\nexport function colorizeQuotaFooterBoth(footer: string | undefined): string | undefined {\n if (!footer) return undefined;\n return footer\n .split(\" \u00B7 \")\n .map((part) => colorizeQuotaFooter(part) ?? part)\n .join(\" \u00B7 \");\n}\n\n// Namespaced by repo so no other extension (e.g. a personal-config \"quota\"\n// slot) can overwrite this footer slot, and vice versa.\nexport const QUOTA_STATUS_KEY = \"pi-provider-antigravity-footer-usage\";\nexport const QUOTA_REFRESH_INTERVAL_MS = 5 * 60 * 1000;\n// Baselines older than this may straddle a quota reset (negative or\n// meaningless deltas), so cross-session calibration ignores them. An hour\n// caps the straddle risk at ~1/5 of the 5h window while an hour of active\n// use still dwarfs the noise guards below thousands-fold.\nexport const MAX_OBSERVATION_AGE_MS = 60 * 60 * 1000;\n\n// Persisted Quota Pool calibration state (states.quota in the provider file).\n// The file shape is owned by the settings module; the coordinator only sees\n// this narrow view through its store.\nexport interface QuotaState {\n [key: string]: unknown;\n}\n\n// The seam behind the coordinator: production uses the file-backed store\n// from the settings module, tests use an in-memory adapter.\nexport interface QuotaStatusStore {\n loadMode: () => QuotaFooterMode;\n loadQuotaState: () => QuotaState | undefined;\n saveQuotaState: (state: {\n weeklyTo5hRatio: number;\n previousObservation: Record<string, WindowFractionPair>;\n updatedAt: number;\n }) => boolean;\n}\n\nexport interface RefreshOptions {\n force?: boolean;\n // Explicit looks at quota (/antigravity usage) bypass the mode and model\n // gates but still calibrate, persist, and share the throttle window.\n ignoreMode?: boolean;\n signal?: AbortSignal;\n}\n\nfunction isFiniteNumber(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value);\n}\n\n// Lenient shape check for persisted observations; garbage restores as absent.\nfunction validPairs(raw: unknown): Record<string, WindowFractionPair> | undefined {\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return undefined;\n const pairs: Record<string, WindowFractionPair> = {};\n for (const [key, value] of Object.entries(raw)) {\n const rec = value as { \"5h\"?: unknown; weekly?: unknown };\n const fiveHour = rec?.[\"5h\"];\n const weekly = rec?.weekly;\n if (isFiniteNumber(fiveHour) && isFiniteNumber(weekly)) pairs[key] = { \"5h\": fiveHour, weekly };\n }\n return Object.keys(pairs).length > 0 ? pairs : undefined;\n}\n\nexport type QuotaStatusCtx = Pick<ExtensionContext, \"ui\" | \"modelRegistry\" | \"model\">;\n\n// Footer quota belongs to this provider: foreign models (or none selected)\n// get no slot and trigger no quota fetch. Pi core keeps Model.id bare\n// (\"gemini-3.8-flash\"); the \"antigravity/...\" form is only /model selection\n// syntax, so the provider field is the single reliable signal.\nexport function isAntigravityModel(model?: { provider?: string }): boolean {\n return model?.provider === PROVIDER_ID;\n}\n\nexport function paintQuotaStatus(coord: QuotaStatusCoordinator, ctx: QuotaStatusCtx): void {\n const mode = coord.mode();\n if (mode === \"off\" || !isAntigravityModel(ctx.model)) {\n ctx.ui.setStatus(QUOTA_STATUS_KEY, undefined);\n return;\n }\n const plain = coord.footerFor(ctx.model?.id, mode);\n ctx.ui.setStatus(\n QUOTA_STATUS_KEY,\n mode === \"all\" ? colorizeQuotaFooterBoth(plain) : colorizeQuotaFooter(plain),\n );\n}\n\n// In-memory throttle around the quota fetch: at most one network call per\n// QUOTA_REFRESH_INTERVAL_MS, with inflight dedup. Failures keep the stale\n// footer instead of flashing errors in the status bar.\nexport class QuotaStatusCoordinator {\n private summary: QuotaSummary | undefined;\n private fetchedAt = 0;\n private inflight: Promise<QuotaSummary | undefined> | null = null;\n private weeklyTo5hRatio: number;\n private lastPairs: Record<string, WindowFractionPair> | undefined;\n private readonly store: QuotaStatusStore;\n\n constructor(store: QuotaStatusStore) {\n this.store = store;\n this.weeklyTo5hRatio = DEFAULT_WEEKLY_TO_5H_RATIO;\n this.loadPersisted();\n }\n\n get ratio(): number {\n return this.weeklyTo5hRatio;\n }\n\n // Single source of truth for the mode: the injected store.\n mode(): QuotaFooterMode {\n return this.store.loadMode();\n }\n\n footerFor(modelId?: string, mode: QuotaFooterMode = \"smart\"): string | undefined {\n if (!this.summary || mode === \"off\") return undefined;\n return mode === \"all\"\n ? buildQuotaFooterBoth(this.summary, modelId)\n : buildQuotaFooter(this.summary, modelId, this.weeklyTo5hRatio);\n }\n\n get isFresh(): boolean {\n return !!this.summary && Date.now() - this.fetchedAt < QUOTA_REFRESH_INTERVAL_MS;\n }\n\n refresh(ctx: QuotaStatusCtx, opts: RefreshOptions = {}): Promise<QuotaSummary | undefined> {\n if (!opts.ignoreMode && (this.mode() === \"off\" || !isAntigravityModel(ctx.model))) {\n return Promise.resolve(undefined);\n }\n if (this.inflight) return this.inflight;\n if (!opts.force && this.isFresh) return Promise.resolve(this.summary);\n return this.fetchOnce(ctx, opts.signal);\n }\n\n // The single ordering behind the footer slot: paint cached text instantly,\n // refresh when the throttle window expired, repaint with fresh text.\n // Event handlers delegate here instead of replicating the sequence.\n async refreshAndPaint(ctx: QuotaStatusCtx): Promise<void> {\n paintQuotaStatus(this, ctx);\n await this.refresh(ctx);\n paintQuotaStatus(this, ctx);\n }\n\n // One fetch for preview purposes even when the footer slot is off or the\n // current model belongs to another provider: opening quota settings is an\n // explicit look at quota (like /antigravity usage), and the preview is the\n // only place that quota is visible while a foreign model is selected.\n ensurePreview(ctx: QuotaStatusCtx): Promise<QuotaSummary | undefined> {\n if (this.summary) return Promise.resolve(this.summary);\n if (this.inflight) return this.inflight;\n return this.fetchOnce(ctx);\n }\n\n private fetchOnce(ctx: QuotaStatusCtx, signal?: AbortSignal): Promise<QuotaSummary | undefined> {\n this.inflight = this.fetch(ctx, signal).finally(() => {\n this.inflight = null;\n });\n return this.inflight;\n }\n\n private async fetch(ctx: QuotaStatusCtx, signal?: AbortSignal): Promise<QuotaSummary | undefined> {\n try {\n const apiKey = await ctx.modelRegistry?.getApiKeyForProvider?.(PROVIDER_ID);\n if (!apiKey) return undefined;\n const { token, projectId } = parseStoredCredentials(apiKey);\n if (!token) return undefined;\n const res = await postAntigravity({\n token,\n path: \"v1internal:retrieveUserQuotaSummary\",\n body: { project: projectId },\n signal,\n });\n if (!res.ok) return undefined;\n const summary = parseQuotaSummary(await res.json());\n this.ingest(summary);\n return summary;\n } catch {\n return undefined;\n }\n }\n\n // Accepts an externally fetched summary into the shared cache: same\n // calibration, persistence, and freshness as fetch().\n ingest(summary: QuotaSummary): void {\n const calibrated = calibrateWeeklyTo5hRatio(this.lastPairs, extractWindowFractionPairs(summary), this.weeklyTo5hRatio);\n this.weeklyTo5hRatio = calibrated;\n this.lastPairs = extractWindowFractionPairs(summary);\n this.summary = summary;\n this.fetchedAt = Date.now();\n this.saveState();\n }\n\n private loadPersisted(): void {\n const entry = this.store.loadQuotaState();\n const ratio = entry?.weeklyTo5hRatio;\n if (typeof ratio === \"number\" && Number.isFinite(ratio) && ratio > 0) {\n this.weeklyTo5hRatio = ratio;\n }\n const at = entry?.updatedAt;\n this.lastPairs = typeof at === \"number\" && Date.now() - at <= MAX_OBSERVATION_AGE_MS\n ? validPairs(entry?.previousObservation)\n : undefined;\n }\n\n // Persists calibration state through the injected store. Best-effort: a\n // stale ratio is still usable.\n private saveState(): void {\n try {\n this.store.saveQuotaState({\n weeklyTo5hRatio: this.weeklyTo5hRatio,\n previousObservation: this.lastPairs ?? {},\n updatedAt: Date.now(),\n });\n } catch {\n // Cache is best-effort; a stale ratio is still usable.\n }\n }\n}\n", "import * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport type { ExtensionCommandContext, Theme } from \"@earendil-works/pi-coding-agent\";\nimport { SettingsList, type SettingItem, type SettingsListTheme } from \"@earendil-works/pi-tui\";\nimport {\n ANSI_FG_RESET,\n colorizeQuotaFooter,\n colorizeQuotaFooterBoth,\n paintQuotaStatus,\n type QuotaStatusCoordinator,\n type QuotaStatusStore,\n} from \"./quota-status.ts\";\n\nexport const PROVIDER_CONFIG_FILE = \"pi-provider-antigravity.json\";\n\nexport interface ProviderFileConfig {\n settings?: { quotaFooter?: unknown; [key: string]: unknown };\n // Runtime state namespaced per subsystem (e.g. states.quota); unknown\n // entries pass through untouched.\n states?: { [name: string]: { [key: string]: unknown } | undefined };\n}\n\nexport type QuotaFooterMode = \"off\" | \"smart\" | \"all\";\n\n// Single opt-in file next to Pi's settings.json (NOT settings.json itself \u2014\n// Pi manages that file and may drop unknown keys). Pi resolves its dir via\n// PI_CODING_AGENT_DIR else ~/.pi/agent; mirror that:\n// { \"settings\": { \"quotaFooter\": \"smart\" } } // off (default) | smart | all\n// A \"states\" section holds runtime data namespaced per subsystem\n// (e.g. states.quota); unknown keys and sections pass through untouched. Read per call: tiny file, and edits apply\n// on the next refresh without a restart.\nexport function defaultConfigFile(env: NodeJS.ProcessEnv = process.env): string {\n // Mirrors Pi's canonical getAgentDir() (PI_CODING_AGENT_DIR else ~/.pi/agent)\n // \u2014 the same source pi-subagents imports from @earendil-works/pi-coding-agent.\n // Hand-rolled because a root value-import breaks plain-node tests: the\n // package index pulls @earendil-works/pi-server, which isn't installable here.\n const dir = (env.PI_CODING_AGENT_DIR || \"\").trim() || path.join(os.homedir(), \".pi\", \"agent\");\n return path.join(dir, PROVIDER_CONFIG_FILE);\n}\n\nexport function loadProviderConfig(file = defaultConfigFile()): ProviderFileConfig | undefined {\n try {\n const raw = JSON.parse(fs.readFileSync(file, \"utf-8\"));\n if (raw && typeof raw === \"object\" && !Array.isArray(raw)) return raw as ProviderFileConfig;\n } catch {\n // Missing/unreadable/invalid file means \"not configured\".\n }\n return undefined;\n}\n\nexport function saveProviderConfig(file: string, data: ProviderFileConfig): boolean {\n try {\n fs.mkdirSync(path.dirname(file), { recursive: true });\n fs.writeFileSync(file, JSON.stringify(data, null, 2) + \"\\n\", \"utf-8\");\n return true;\n } catch {\n return false;\n }\n}\n\nexport function normalizeFooterMode(value: unknown): QuotaFooterMode | undefined {\n const v = typeof value === \"string\" ? value.trim().toLowerCase() : \"\";\n return v === \"off\" || v === \"smart\" || v === \"all\" ? v : undefined;\n}\n\nexport function resolveFooterMode(config?: ProviderFileConfig): QuotaFooterMode {\n return normalizeFooterMode(config?.settings?.quotaFooter) ?? \"off\";\n}\n\n// Production QuotaStatusStore backed by the provider file. Reads per call:\n// tiny file, and edits apply on the next refresh without a restart.\nexport function fileQuotaStatusStore(file = defaultConfigFile()): QuotaStatusStore {\n return {\n loadMode: () => resolveFooterMode(loadProviderConfig(file)),\n loadQuotaState: () => {\n const quota = loadProviderConfig(file)?.states?.quota;\n return quota && typeof quota === \"object\" && !Array.isArray(quota) ? quota : undefined;\n },\n // Merges state into the existing file, preserving settings and unknown\n // keys. Never clobbers a file we couldn't parse.\n saveQuotaState: (state) => {\n try {\n let data: ProviderFileConfig = {};\n try {\n const raw = JSON.parse(fs.readFileSync(file, \"utf-8\"));\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return false;\n data = raw as ProviderFileConfig;\n } catch (err: any) {\n if (err?.code !== \"ENOENT\") return false;\n }\n const states =\n data.states && typeof data.states === \"object\" && !Array.isArray(data.states) ? data.states : {};\n const quota =\n states.quota && typeof states.quota === \"object\" && !Array.isArray(states.quota) ? states.quota : {};\n states.quota = { ...quota, ...state };\n data.states = states;\n return saveProviderConfig(file, data);\n } catch {\n // Cache is best-effort; a stale ratio is still usable.\n return false;\n }\n },\n };\n}\n\nexport interface SettingsFieldDef {\n key: string;\n label: string;\n description?: string;\n options: readonly string[];\n defaultValue?: string;\n // Per-option explanation shown on its own line under the live sample (only\n // the highlighted value's note is shown).\n optionNotes?: Record<string, string>;\n // Immediate side-effect after save (e.g. repaint). Absent = save only.\n onChange?: (ctx: ExtensionCommandContext, quotaStatus?: QuotaStatusCoordinator) => Promise<void>;\n}\n\n// Adding a setting = one row here. The TUI dialog and the select fallback\n// both render field pickers automatically once there are two or more rows.\n// Frozen: callers and tests read the registry, never mutate it.\nexport const SETTINGS_FIELDS: readonly SettingsFieldDef[] = Object.freeze([\n {\n key: \"quotaFooter\",\n label: \"Quota footer\",\n description: \"Show remaining quota in the footer\",\n options: [\"off\", \"smart\", \"all\"],\n defaultValue: \"off\",\n optionNotes: {\n smart:\n \"Picks whichever window runs out first (5h or weekly), weighting the weekly pool by a ratio learned from your usage\",\n all: \"Lists every window of the pool backing the current model (5h or weekly)\",\n },\n onChange: async (ctx, quotaStatus) => {\n if (!quotaStatus) return;\n // An explicit pick is an explicit look: the mode/model gates would\n // otherwise leave the preview blank while the footer is off or another\n // provider's model is selected. Nothing to fetch when the new value is off.\n if (quotaStatus.mode() !== \"off\") await quotaStatus.refresh(ctx, { ignoreMode: true });\n paintQuotaStatus(quotaStatus, ctx);\n },\n },\n]);\n\nexport function fieldDisplayValue(config: ProviderFileConfig | undefined, field: SettingsFieldDef): string {\n const raw = config?.settings?.[field.key];\n if (typeof raw === \"string\" && raw.trim()) return raw.trim();\n return field.defaultValue ?? \"unset\";\n}\n\nexport function saveSettingValue(field: SettingsFieldDef, value: string, file: string): boolean {\n const current = loadProviderConfig(file) ?? {};\n const settings =\n current.settings && typeof current.settings === \"object\" ? current.settings : {};\n return saveProviderConfig(file, { ...current, settings: { ...settings, [field.key]: value } });\n}\n\n// Shared by the TUI dialog (silent per cycle) and the select fallback:\n// saves, runs the field's side-effect, reports success.\nexport async function applySettingValue(\n field: SettingsFieldDef,\n value: string,\n file: string,\n ctx: ExtensionCommandContext,\n quotaStatus?: QuotaStatusCoordinator,\n): Promise<boolean> {\n if (!saveSettingValue(field, value, file)) {\n return false;\n }\n await field.onChange?.(ctx, quotaStatus);\n return true;\n}\n\n// Rendered footer sample for a mode, for settings previews. Model-aware like\n// the slot itself (a Claude model previews the 3P pool). Off has no sample: the\n// slot stays empty. The leading reset closes the dim description color the\n// dialog renders it in \u2014 the sample must read like the footer, where an\n// uncolored window is plain foreground and only low ones are yellow/red.\nexport function previewQuotaFooterText(\n coord: QuotaStatusCoordinator | undefined,\n modelId: string | undefined,\n mode: string,\n): string | undefined {\n if (!coord || mode === \"off\") return undefined;\n const plain = coord.footerFor(modelId, mode === \"all\" ? \"all\" : \"smart\");\n if (!plain) return undefined;\n const colored = mode === \"all\" ? colorizeQuotaFooterBoth(plain) : colorizeQuotaFooter(plain);\n return ANSI_FG_RESET + colored;\n}\n\n// Rows for pi-tui's SettingsList \u2014 the same list component Pi's /settings is\n// built on. Rows with `values` cycle inline on Enter/Space (no submenu).\nexport function buildSettingsItems(\n config: ProviderFileConfig | undefined,\n fields: readonly SettingsFieldDef[] = SETTINGS_FIELDS,\n): SettingItem[] {\n return fields.map((f) => ({\n id: f.key,\n label: f.label,\n description: f.description,\n currentValue: fieldDisplayValue(config, f),\n values: [...f.options],\n }));\n}\n\nexport function emitOutput(ctx: ExtensionCommandContext, text: string, type: \"info\" | \"warning\" | \"error\" = \"info\"): void {\n if (ctx.hasUI) {\n ctx.ui.notify(text, type);\n } else {\n if (type === \"error\" || type === \"warning\") console.error(text);\n else console.log(text);\n }\n}\n\n// Pi's canonical list theme when the host package is importable; otherwise an\n// identical mapping on the live theme (plain-node tests can't load the host\n// package root, and the mapping is five stable color roles).\nasync function settingsListTheme(theme: Theme): Promise<SettingsListTheme> {\n try {\n const pi = await import(\"@earendil-works/pi-coding-agent\");\n return pi.getSettingsListTheme();\n } catch {\n return {\n label: (text, selected) => (selected ? theme.fg(\"accent\", text) : text),\n value: (text, selected) => (selected ? theme.fg(\"accent\", text) : theme.fg(\"muted\", text)),\n description: (text) => theme.fg(\"dim\", text),\n cursor: theme.fg(\"accent\", \"\u2192 \"),\n hint: (text) => theme.fg(\"dim\", text),\n };\n }\n}\n\n// Single entry behind the settings seam: commands dispatches here and knows\n// nothing about dialogs, file paths, or previews.\nexport async function openSettings(\n ctx: ExtensionCommandContext,\n quotaStatus?: QuotaStatusCoordinator,\n): Promise<void> {\n const file = defaultConfigFile();\n if (ctx.mode === \"tui\" && ctx.hasUI) {\n await openSettingsDialog(ctx, quotaStatus, file);\n return;\n }\n const config = loadProviderConfig(file);\n if (!ctx.hasUI) {\n emitOutput(\n ctx,\n SETTINGS_FIELDS.map((f) => `${f.label}: ${fieldDisplayValue(config, f)}`).join(\"\\n\") +\n `\\nEdit ${file} to change.`,\n );\n return;\n }\n let field = SETTINGS_FIELDS[0];\n if (!field) return;\n if (SETTINGS_FIELDS.length > 1) {\n const labels = SETTINGS_FIELDS.map(\n (f) => `${f.label} (current: ${fieldDisplayValue(config, f)})`,\n );\n const choice = await ctx.ui.select(\"Antigravity settings\", labels);\n if (!choice) return; // dismissed \u2014 change nothing\n const found = SETTINGS_FIELDS[labels.indexOf(choice)];\n if (!found) return;\n field = found;\n }\n const picked = await ctx.ui.select(\n `${field.label} (current: ${fieldDisplayValue(config, field)})`,\n [...field.options],\n );\n if (!picked || !field.options.includes(picked)) return; // dismissed \u2014 change nothing\n if (await applySettingValue(field, picked, file, ctx, quotaStatus)) {\n emitOutput(ctx, `${field.label} set to ${picked}.`);\n } else {\n emitOutput(ctx, `Failed to write ${file}.`, \"error\");\n }\n}\n\nasync function openSettingsDialog(\n ctx: ExtensionCommandContext,\n quotaStatus: QuotaStatusCoordinator | undefined,\n file: string,\n): Promise<void> {\n if (quotaStatus) await quotaStatus.ensurePreview(ctx);\n await ctx.ui.custom<void>(async (tui, theme, _kb, done) => {\n const config = loadProviderConfig(file);\n const items = buildSettingsItems(config);\n const quotaField = SETTINGS_FIELDS.find((f) => f.key === \"quotaFooter\");\n // Live sample of what the footer will show for a mode, using cached\n // (just ensured) quota numbers. Model-aware like the slot itself.\n const paintPreview = (mode: string) => {\n const item = items.find((i) => i.id === \"quotaFooter\");\n if (!item) return;\n const base = quotaField?.description ?? \"\";\n const sample = mode === \"off\" ? \"hidden\" : previewQuotaFooterText(quotaStatus, ctx.model?.id, mode);\n const head = sample && base ? `${base}: ${sample}` : (sample ?? base);\n const note = quotaField?.optionNotes?.[mode];\n // Note on its own line, not appended: the sample closes the description\n // color, so anything trailing it would lose dim.\n item.description = note ? `${head}\\n${note}` : head;\n };\n paintPreview(resolveFooterMode(config));\n const list = new SettingsList(\n items,\n Math.min(items.length, 10),\n await settingsListTheme(theme),\n (id, newValue) => {\n const field = SETTINGS_FIELDS.find((f) => f.key === id);\n if (!field) return;\n list.invalidate();\n tui.requestRender();\n void applySettingValue(field, newValue, file, ctx, quotaStatus).then(() => {\n paintPreview(newValue);\n list.invalidate();\n tui.requestRender();\n });\n },\n () => done(),\n );\n // Pi-native frame: a border line above and below the list, like\n // /settings (SettingsSelectorComponent wraps its SettingsList in\n // DynamicBorders). Extension custom UI can't reuse pi's separate\n // focus target, so the wrapper delegates input to the inner list.\n const border = {\n render: (width: number) => [theme.fg(\"border\", \"\u2500\".repeat(Math.max(1, width)))],\n };\n return {\n render: (width: number) => [...border.render(width), ...list.render(width), ...border.render(width)],\n handleInput: (data: string) => list.handleInput(data),\n invalidate: () => list.invalidate(),\n };\n });\n}\n", "import {\n type AssistantMessage,\n type AssistantMessageEventStream,\n type Context,\n type Model,\n type SimpleStreamOptions,\n type TextContent,\n type ThinkingContent,\n createAssistantMessageEventStream,\n} from \"@earendil-works/pi-ai\";\nimport { parseStoredCredentials } from \"./auth.ts\";\nimport { createSseFeed, type SseFeedOutput } from \"./parser.ts\";\nimport { buildAntigravityRequestBody } from \"./builder.ts\";\nimport { postAntigravity } from \"./protocol.ts\";\nimport { resolveModelPlan, getCatalogSnapshot } from \"./model-catalog.ts\";\n\nexport function streamAntigravity(\n model: Model<any>,\n context: Context,\n options?: SimpleStreamOptions\n): AssistantMessageEventStream {\n const stream = createAssistantMessageEventStream();\n\n (async () => {\n const output: AssistantMessage = {\n role: \"assistant\",\n content: [],\n api: model.api,\n provider: model.provider,\n model: model.id,\n usage: {\n input: 0,\n output: 0,\n cacheRead: 0,\n cacheWrite: 0,\n totalTokens: 0,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n },\n stopReason: \"pending\",\n timestamp: Date.now(),\n };\n\n try {\n stream.push({ type: \"start\", partial: output });\n\n const rawApiKey = options?.apiKey;\n if (!rawApiKey) {\n throw new Error(\"Missing Antigravity credentials. Run /login antigravity first.\");\n }\n\n const { token, projectId } = parseStoredCredentials(rawApiKey);\n\n const effort = options?.reasoning;\n // trajectoryId is provider-specific (absent from the SDK options\n // type), so read it via runtime-checked `in` narrowing instead of a cast.\n const trajectoryId =\n options && \"trajectoryId\" in options && typeof options.trajectoryId === \"string\"\n ? options.trajectoryId\n : undefined;\n const snapshot = getCatalogSnapshot();\n const plan = resolveModelPlan(model.id, effort, snapshot);\n\n const requestBody = buildAntigravityRequestBody({\n projectId,\n plan,\n context,\n sessionId: options?.sessionId,\n trajectoryId,\n maxOutputTokens: model.maxTokens,\n toolChoice: options?.toolChoice,\n });\n\n const res = await postAntigravity({\n token,\n path: \"v1internal:streamGenerateContent?alt=sse\",\n body: requestBody,\n signal: options?.signal,\n });\n\n if (!res.ok) {\n const errText = await res.text();\n throw new Error(`Antigravity stream failed (${res.status}): ${errText}`);\n }\n\n if (!res.body) {\n throw new Error(\"No response stream received from Antigravity.\");\n }\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n const feed = createSseFeed();\n\n // Single driver for one feed output: the feed owns block events,\n // usage counts, and stopReason. Content lives in the feed's store \u2014\n // every output re-points at the live ref, so close() hands over the\n // final blocks with Thought Signatures already placed (no copy here).\n // The caller only attaches the `partial` live helper and pushes;\n // there is no translation seam. Cost math stays here: it needs\n // model.cost, which the feed never sees.\n const applyFeedOutput = (fed: SseFeedOutput) => {\n output.content = fed.content;\n output.usage.input = fed.usage.input;\n output.usage.cacheRead = fed.usage.cacheRead;\n output.usage.output = fed.usage.output;\n output.usage.totalTokens = fed.usage.total;\n\n if (model.cost) {\n const c = model.cost;\n const inputCost = (output.usage.input * (c.input || 0)) / 1_000_000;\n const outputCost = (output.usage.output * (c.output || 0)) / 1_000_000;\n const cacheCost = (output.usage.cacheRead * (c.cacheRead || 0)) / 1_000_000;\n output.usage.cost = {\n input: inputCost,\n output: outputCost,\n cacheRead: cacheCost,\n cacheWrite: 0,\n total: inputCost + outputCost + cacheCost,\n };\n }\n\n output.stopReason = fed.stopReason;\n for (const ev of fed.events) {\n stream.push({ ...ev, partial: output });\n }\n };\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n applyFeedOutput(feed.feed(decoder.decode(value, { stream: true })));\n }\n\n applyFeedOutput(feed.close());\n\n const doneReason: \"stop\" | \"toolUse\" | \"length\" =\n output.stopReason === \"toolUse\" || output.stopReason === \"length\"\n ? output.stopReason\n : \"stop\";\n\n stream.push({\n type: \"done\",\n reason: doneReason,\n message: output,\n });\n stream.end();\n } catch (error) {\n output.stopReason = options?.signal?.aborted ? \"aborted\" : \"error\";\n output.errorMessage = error instanceof Error ? error.message : String(error);\n stream.push({ type: \"error\", reason: output.stopReason, error: output });\n stream.end();\n }\n })();\n\n return stream;\n}\n", "import type { TextContent, ThinkingContent, ToolCall } from \"@earendil-works/pi-ai\";\n\n/**\n * One assembled content block, Pi-spelled (TextContent | ThinkingContent |\n * ToolCall): thinking blocks carry thinkingSignature, text blocks carry\n * textSignature, toolCalls carry the wire-spelled thoughtSignature.\n * The import is type-only \u2014 the module stays dependency-free at runtime.\n */\nexport type ParsedBlock = TextContent | ThinkingContent | ToolCall;\n\nexport interface ParsedStreamResult {\n content: ParsedBlock[];\n usage: {\n input: number;\n output: number;\n cacheRead: number;\n total: number;\n };\n stopReason: \"stop\" | \"toolUse\" | \"length\" | \"error\";\n}\n\n/**\n * Stream events in pi-ai spelling, minus the `partial` live helper the\n * caller attaches at push time. The feed is the single owner of the\n * response surface \u2014 block events, usage counts, and stopReason all come\n * out of here, so no caller translates between vocabularies. This union\n * must track AssistantMessageEvent (minus start/done/error); the push site\n * in stream.ts fails typecheck if the two drift apart.\n * The import stays type-only \u2014 the module is dependency-free at runtime.\n */\nexport type FeedStreamEvent =\n | { type: \"text_start\" | \"thinking_start\"; contentIndex: number }\n | { type: \"text_delta\" | \"thinking_delta\"; contentIndex: number; delta: string }\n | { type: \"text_end\" | \"thinking_end\"; contentIndex: number; content: string }\n | { type: \"toolcall_start\"; contentIndex: number }\n | { type: \"toolcall_delta\"; contentIndex: number; delta: string }\n | { type: \"toolcall_end\"; contentIndex: number; toolCall: ToolCall };\n\n\nexport interface SseFeedOutput {\n events: FeedStreamEvent[];\n usage: ParsedStreamResult[\"usage\"];\n stopReason: ParsedStreamResult[\"stopReason\"];\n /**\n * Live ref to the single assembled-block store owned by this feed.\n * Every event index is valid into it at the time the output is returned.\n * Read-only for callers: the feed is the sole writer. Signatures are\n * placement-complete only on close() (SDK spelling); no respelling downstream.\n */\n content: ParsedBlock[];\n}\n\n/**\n * Stateful incremental SSE reader: the single module that understands the\n * Antigravity Wire Fingerprint on the wire. Feed raw response chunks as they\n * arrive; line buffering, block accumulation, usage, stopReason, lone\n * Thought Signature attachment, event translation, and the single\n * assembled-block store all live here. Callers attach `partial` and push \u2014\n * never a parallel array, never a second vocabulary. close() is the single\n * final surface: terminal events plus the fully assembled content.\n */\nexport function createSseFeed(): {\n feed(chunk: string): SseFeedOutput;\n close(): SseFeedOutput;\n} {\n const content: ParsedBlock[] = [];\n const usage = {\n input: 0,\n output: 0,\n cacheRead: 0,\n total: 0,\n };\n let stopReason: ParsedStreamResult[\"stopReason\"] = \"stop\";\n let lastThoughtSignature: string | undefined;\n let buffer = \"\";\n let openType: \"text\" | \"thinking\" | null = null;\n\n const snapshot = (events: FeedStreamEvent[]): SseFeedOutput => ({\n events,\n usage: { ...usage },\n stopReason,\n content,\n });\n\n // Lone-signature turn: a Thought Signature that arrived detached from any\n // thinking part rides the first thinking block (SDK spelling), else the\n // last text block. Runs once at close, so the final content carries the\n // placement policy instead of every caller re-deriving it.\n const attachLoneSignature = (): void => {\n if (!lastThoughtSignature) return;\n const thinking = content.find((b): b is ThinkingContent => b.type === \"thinking\");\n if (thinking && !thinking.thinkingSignature) {\n thinking.thinkingSignature = lastThoughtSignature;\n } else if (!thinking) {\n for (let i = content.length - 1; i >= 0; i--) {\n const block = content[i];\n if (block?.type === \"text\" && !block.textSignature) {\n block.textSignature = lastThoughtSignature;\n break;\n }\n }\n }\n };\n\n const closeOpenBlock = (events: FeedStreamEvent[]): void => {\n if (openType === null) return;\n const contentIndex = content.length - 1;\n const block = content[contentIndex];\n if (block?.type === \"thinking\") {\n events.push({ type: \"thinking_end\", contentIndex, content: block.thinking });\n } else if (block?.type === \"text\") {\n events.push({ type: \"text_end\", contentIndex, content: block.text });\n }\n openType = null;\n };\n\n const processLine = (rawLine: string, events: FeedStreamEvent[]): void => {\n const line = rawLine.trim();\n if (!line.startsWith(\"data:\")) return;\n\n const dataStr = line.slice(5).trim();\n if (!dataStr || dataStr === \"[DONE]\") return;\n\n let payload: any;\n try {\n payload = JSON.parse(dataStr);\n } catch {\n return;\n }\n\n const response = payload.response || payload;\n\n // Usage metadata (promptTokenCount includes cached tokens per Google spec).\n // output counts thinking tokens too, mirroring the pi-ai Google adapter:\n // usageMetadata reports candidates and thoughts separately.\n if (response.usageMetadata) {\n const um = response.usageMetadata;\n const promptTokens = typeof um.promptTokenCount === \"number\" ? um.promptTokenCount : 0;\n const cacheRead = typeof um.cachedContentTokenCount === \"number\" ? um.cachedContentTokenCount : 0;\n usage.input = Math.max(0, promptTokens - cacheRead);\n usage.cacheRead = cacheRead;\n const candidates = typeof um.candidatesTokenCount === \"number\" ? um.candidatesTokenCount : 0;\n const thoughts = typeof um.thoughtsTokenCount === \"number\" ? um.thoughtsTokenCount : 0;\n usage.output = candidates + thoughts;\n if (typeof um.totalTokenCount === \"number\") usage.total = um.totalTokenCount;\n }\n\n const candidate = response.candidates?.[0];\n if (!candidate) return;\n\n // Sticky: length/error/toolUse are never downgraded by a later STOP.\n // Mirrors pi-ai mapStopReasonString: only STOP is benign, MAX_TOKENS is\n // length, every other reason (SAFETY, RECITATION, BLOCKLIST, ...) is error.\n if (candidate.finishReason === \"MAX_TOKENS\") {\n stopReason = \"length\";\n } else if (typeof candidate.finishReason === \"string\" && candidate.finishReason !== \"STOP\") {\n stopReason = \"error\";\n }\n\n const parts = candidate.content?.parts || [];\n for (const part of parts) {\n if (part.thoughtSignature) {\n lastThoughtSignature = part.thoughtSignature;\n }\n\n if (part.thought) {\n if (openType !== \"thinking\") {\n closeOpenBlock(events);\n content.push({ type: \"thinking\", thinking: \"\" });\n events.push({ type: \"thinking_start\", contentIndex: content.length - 1 });\n openType = \"thinking\";\n }\n const block = content[content.length - 1];\n if (block?.type !== \"thinking\") continue;\n const delta = part.text || \"\";\n block.thinking += delta;\n if (part.thoughtSignature) {\n block.thinkingSignature = part.thoughtSignature;\n }\n events.push({\n type: \"thinking_delta\",\n contentIndex: content.length - 1,\n delta,\n });\n } else if (part.functionCall) {\n closeOpenBlock(events);\n stopReason = \"toolUse\";\n const block: ToolCall = {\n type: \"toolCall\",\n id: part.functionCall.id || `call_${content.length}`,\n name: part.functionCall.name || \"\",\n arguments: part.functionCall.args || {},\n thoughtSignature: part.thoughtSignature || lastThoughtSignature,\n };\n content.push(block);\n const contentIndex = content.length - 1;\n events.push({ type: \"toolcall_start\", contentIndex });\n events.push({\n type: \"toolcall_delta\",\n contentIndex,\n delta: JSON.stringify(block.arguments),\n });\n events.push({ type: \"toolcall_end\", contentIndex, toolCall: block });\n } else if (part.text !== undefined) {\n if (openType !== \"text\") {\n closeOpenBlock(events);\n content.push({ type: \"text\", text: \"\" });\n events.push({ type: \"text_start\", contentIndex: content.length - 1 });\n openType = \"text\";\n }\n const block = content[content.length - 1];\n if (block?.type !== \"text\") continue;\n block.text += part.text;\n events.push({ type: \"text_delta\", contentIndex: content.length - 1, delta: part.text });\n }\n }\n };\n\n return {\n feed(chunk: string): SseFeedOutput {\n const events: FeedStreamEvent[] = [];\n buffer += chunk;\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\";\n for (const rawLine of lines) processLine(rawLine, events);\n return snapshot(events);\n },\n close(): SseFeedOutput {\n const events: FeedStreamEvent[] = [];\n if (buffer.trim() !== \"\") processLine(buffer, events);\n buffer = \"\";\n closeOpenBlock(events);\n attachLoneSignature();\n return snapshot(events);\n },\n };\n}\n\n", "import { createHash, randomUUID } from \"node:crypto\";\nimport type { ModelPlan } from \"./model-catalog.ts\";\nimport { classifyModelFamily, isCompatibleFamily } from \"./model-catalog.ts\";\nimport { PROVIDER_ID } from \"./protocol.ts\";\n\nexport interface BuildRequestBodyParams {\n projectId: string;\n plan: ModelPlan;\n context: {\n systemPrompt?: string;\n messages: Array<any>;\n tools?: Array<any>;\n };\n sessionId?: string;\n trajectoryId?: string;\n maxOutputTokens?: number;\n toolChoice?: string;\n}\n\nconst BASE64_SIGNATURE_PATTERN = /^[A-Za-z0-9+/_-]+={0,2}$/;\n\n/**\n * Gemini 3 rejects a `functionCall` part that carries no `thoughtSignature` while\n * it sits inside the current turn (validation covers \"all steps in the current\n * turn\"; a turn begins with the most recent user message that is not a\n * functionResponse) with 400 INVALID_ARGUMENT \"Function call is missing a\n * thought_signature in functionCall parts\". Dropping an unreplayable signature\n * (below) leaves exactly that state, and a cross-provider model switch mid-tool\n * turn is the everyday way to produce it: the other provider's tool call has no\n * signature to replay, and it is the pending step of the current turn.\n *\n * Google documents one escape hatch for `functionCall` parts \"that were not\n * generated by the API ... for example, when transferring history from a model\n * that does not include thought signatures\" \u2014 the literal sentinel below, which\n * trades that call's reasoning continuity (it is gone either way) for a request\n * the backend accepts.\n *\n * Live A/B probe 2026-09-10 (gemini-3.8-flash-low, mitmdump :18080, see\n * captures/pi_probe_sentinel/): unsigned foreign functionCall as the pending step\n * of the current turn \u2192 400 with the message above; the identical body with this\n * sentinel on that single part \u2192 200.\n *\n * Applied to foreign calls only (see ADR-0007). A signature we *should* have had \u2014\n * same provider and family, yet missing or unreadable (pipeline regression, damaged\n * session) \u2014 keeps failing loudly with the 400 above instead of being papered over,\n * so a lost signature stays visible as a bug rather than silently degrading the\n * model's reasoning for that call.\n */\nexport const SKIP_THOUGHT_SIGNATURE_VALIDATOR = \"skip_thought_signature_validator\";\n\n/**\n * Verifies that a thought signature candidate is a valid base64 string suitable\n * for Google Protobuf TYPE_BYTES fields in JSON mapping.\n */\nfunction isValidThoughtSignature(sig: unknown): sig is string {\n if (typeof sig !== \"string\" || sig.length === 0) return false;\n if (sig.includes(\"=\") && sig.length % 4 !== 0) return false;\n return BASE64_SIGNATURE_PATTERN.test(sig);\n}\n\n/**\n * Resolves a thoughtSignature candidate only when coming from the same provider and\n * compatible model family, and only when formatted as valid base64.\n */\nfunction resolveThoughtSignature(\n isSameProviderAndModel: boolean,\n sig: unknown\n): string | undefined {\n if (!isSameProviderAndModel) return undefined;\n return isValidThoughtSignature(sig) ? sig : undefined;\n}\n\n/**\n * Deterministically derives a persistent session ID from the initial conversation turn.\n */\nfunction deriveSessionId(\n context: { messages?: Array<any>; systemPrompt?: string },\n explicitSessionId?: string\n): string {\n if (explicitSessionId && typeof explicitSessionId === \"string\" && explicitSessionId.trim().length > 0) {\n return explicitSessionId.trim();\n }\n\n const firstMsg = context.messages?.[0];\n if (!firstMsg) {\n return randomUUID();\n }\n\n const seedContent =\n typeof firstMsg.content === \"string\"\n ? firstMsg.content\n : JSON.stringify(firstMsg.content || \"\");\n\n const hash = createHash(\"sha256\")\n .update(`antigravity:conversation:${seedContent}`)\n .digest()\n .subarray(0, 16);\n\n hash[6] = (hash[6] & 0x0f) | 0x40; // v4 UUID\n hash[8] = (hash[8] & 0x3f) | 0x80;\n const hex = [...hash].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\n/**\n * Computes deterministic v5 UUID trajectory ID from the session ID.\n */\nfunction resolveSessionTrajectory(sessionId: string): string {\n const bytes = createHash(\"sha1\").update(`antigravity:${sessionId}`).digest().subarray(0, 16);\n bytes[6] = (bytes[6] & 0x0f) | 0x50; // v5 UUID\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = [...bytes].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\n/**\n * Signed 64-bit `request.sessionId`. Constant in every captured agy turn (1.1.26-1.2.0) \u2014 no\n * conversation/account input. Re-capture (captures/README.md) if a future agy changes it.\n */\nconst AGY_SESSION_ID = \"-3750763034362895579\";\n\n/**\n * Pure-numeric session IDs (probe captures, `stream.ts` override) pass through verbatim.\n */\nfunction resolveNumericSessionId(sessionId: string): string {\n if (/^-?\\d+$/.test(sessionId)) {\n return sessionId;\n }\n return AGY_SESSION_ID;\n}\n\n/**\n * Normalizes tool call IDs for cross-provider compatibility.\n */\nfunction normalizeToolCallId(id: string | undefined): string | undefined {\n if (!id || typeof id !== \"string\") return id;\n return id.replace(/[^a-zA-Z0-9_-]/g, \"_\").slice(0, 64);\n}\n\nconst CUSTOM_TOOL_SCHEMA_ALLOW = new Set([\n \"type\",\n \"description\",\n \"properties\",\n \"required\",\n \"items\",\n \"enum\",\n]);\n\nfunction stripMetaSchema(schema: unknown): unknown {\n if (!schema || typeof schema !== \"object\" || Array.isArray(schema)) return schema;\n const omit = new Set([\"$schema\", \"$id\", \"$defs\", \"definitions\"]);\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(schema)) {\n if (!omit.has(key)) out[key] = stripMetaSchema(value);\n }\n return out;\n}\n\nfunction normalizeCustomToolSchema(schema: unknown): unknown {\n if (!schema || typeof schema !== \"object\") return schema;\n if (Array.isArray(schema)) return schema.map(normalizeCustomToolSchema);\n\n const s = schema as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(s)) {\n if (!CUSTOM_TOOL_SCHEMA_ALLOW.has(key)) {\n // Map string const to single-item enum for Claude bridge compatibility\n if (key === \"const\" && s.enum === undefined && typeof value === \"string\") {\n out.enum = [value];\n }\n continue;\n }\n if (key === \"type\" && Array.isArray(value)) {\n const scalar = value.find((e) => typeof e === \"string\" && e !== \"null\");\n if (scalar) out.type = scalar;\n continue;\n }\n if (key === \"properties\" && value && typeof value === \"object\" && !Array.isArray(value)) {\n const props: Record<string, unknown> = {};\n for (const [propName, propSchema] of Object.entries(value as Record<string, unknown>)) {\n props[propName] = normalizeCustomToolSchema(propSchema);\n }\n out.properties = props;\n continue;\n }\n if (key === \"enum\" && Array.isArray(value) && !value.every((e) => typeof e === \"string\")) {\n continue;\n }\n out[key] = normalizeCustomToolSchema(value);\n }\n return out;\n}\n\n/**\n * Strict agy parity: versioned captures (1.1.26/1.1.27, every declaration)\n * always use legacy `parameters`, never `parametersJsonSchema` \u2014 for Gemini\n * and non-Gemini models alike. Captured declarations also carry no\n * anyOf/patternProperties/const keys, hence the normalization below.\n */\nfunction convertTools(\n tools: Array<any> | undefined\n): Array<{ functionDeclarations: Array<any> }> | undefined {\n if (!tools?.length) return undefined;\n return [\n {\n functionDeclarations: tools.map((tool) => {\n const schema = stripMetaSchema(tool.parameters) || { type: \"object\", properties: {} };\n return {\n name: tool.name,\n description: tool.description,\n parameters: normalizeCustomToolSchema(schema),\n };\n }),\n },\n ];\n}\n\n/**\n * Translates one Turn Trace (see CONTEXT.md) into Antigravity wire contents.\n */\nfunction translateTurnTrace(\n context: { messages?: Array<any>; systemPrompt?: string },\n runtimeModelId: string\n): Array<any> {\n const contents: Array<any> = [];\n const messages = context.messages || [];\n\n // The sentinel is Gemini-specific evidence: the probe shows a Claude runtime model\n // accepts the same unsigned foreign functionCall with no sentinel at all (and merely\n // tolerates the sentinel), so Claude/GPT requests keep today's shape.\n const isGeminiRequest = classifyModelFamily(runtimeModelId) === \"gemini\";\n\n for (const msg of messages) {\n if (msg.role === \"user\") {\n const parts: Array<any> = [];\n if (typeof msg.content === \"string\") {\n parts.push({ text: msg.content });\n } else if (Array.isArray(msg.content)) {\n for (const item of msg.content) {\n if (item.type === \"text\") {\n parts.push({ text: item.text });\n } else if (item.type === \"image\") {\n parts.push({\n inlineData: {\n mimeType: item.mimeType || \"image/png\",\n data: item.data,\n },\n });\n }\n }\n }\n if (parts.length > 0) {\n contents.push({ role: \"user\", parts });\n }\n } else if (msg.role === \"assistant\") {\n if (msg.stopReason === \"error\" || msg.stopReason === \"aborted\") {\n continue;\n }\n\n const isSameProvider = !msg.provider || msg.provider === PROVIDER_ID;\n const isSameModel = isCompatibleFamily(msg.model, runtimeModelId);\n const isSameProviderAndModel = isSameProvider && isSameModel;\n\n const parts: Array<any> = [];\n // agy CLI shape (1.1.27 turn4 fixture): a thinking replay never carries its\n // own signature \u2014 it stays pending and rides on the NEXT text/functionCall\n // part. Only when the turn ends with no carrying part does it fall back\n // onto the last part (uncovered edge, same as the message-level fallback).\n // Signature fields are canonical SDK spellings only (textSignature /\n // thinkingSignature on blocks, thoughtSignature on toolCalls). No\n // legacy-spelling reads: pre-#26 sessions are abandoned (see #26).\n let pendingThinkingSig: string | undefined;\n if (typeof msg.content === \"string\") {\n parts.push({ text: msg.content });\n } else if (Array.isArray(msg.content)) {\n for (const item of msg.content) {\n if (item.type === \"text\") {\n if (typeof item.text !== \"string\" || item.text === \"\") {\n // Vacuous text block (e.g. a leading empty part the live feed\n // opened): drop the part but keep a valid signature pending\n // for the next part carrying one, mirroring the precedence\n // below. The Claude backend rejects {\"text\": \"\"} with 400\n // (text.text: Field required, observed live on multi-turn\n // replay); Gemini accepts the same payload (probed 200), but\n // an empty part carries nothing either way, and agy never\n // emits one, so this stays regardless of model family.\n pendingThinkingSig =\n resolveThoughtSignature(isSameProviderAndModel, item.textSignature) ||\n pendingThinkingSig;\n continue;\n }\n const part: any = { text: item.text };\n const sig =\n resolveThoughtSignature(\n isSameProviderAndModel,\n item.textSignature\n ) || pendingThinkingSig;\n if (sig) {\n part.thoughtSignature = sig;\n pendingThinkingSig = undefined;\n }\n parts.push(part);\n } else if (item.type === \"thinking\") {\n const candidateSig = item.thinkingSignature;\n const isForeignReasoning =\n !isSameProviderAndModel ||\n (typeof candidateSig === \"string\" && candidateSig.trim().startsWith(\"{\"));\n\n if (!isForeignReasoning) {\n // No thoughtSignature here by design: it stays pending for the\n // next text/functionCall part (agy CLI part-split shape).\n const part: any = {\n thought: true,\n text: item.thinking || \"\",\n };\n const sig = resolveThoughtSignature(true, candidateSig);\n if (sig) {\n pendingThinkingSig = sig;\n }\n parts.push(part);\n } else {\n // Cross-provider/model or foreign reasoning item: drop foreign signature and\n // serialize reasoning text (or summary) as plain text so the model retains\n // conversation context without failing Google's TYPE_BYTES base64 validation.\n let text = item.thinking || \"\";\n if (!text && typeof candidateSig === \"string\" && candidateSig.trim().startsWith(\"{\")) {\n try {\n const parsed = JSON.parse(candidateSig);\n if (Array.isArray(parsed.summary)) {\n text = parsed.summary\n .filter((s: any) => s && typeof s.text === \"string\")\n .map((s: any) => s.text)\n .join(\"\\n\");\n }\n } catch {\n // Ignore JSON parse errors\n }\n }\n if (text && text.trim() !== \"\") {\n parts.push({ text });\n }\n }\n } else if (item.type === \"tool_use\" || item.type === \"toolCall\") {\n const part: any = {\n functionCall: {\n id: normalizeToolCallId(item.id),\n name: item.name,\n args: item.input || item.arguments || {},\n },\n };\n const sig =\n resolveThoughtSignature(\n isSameProviderAndModel,\n item.thoughtSignature\n ) || pendingThinkingSig;\n if (sig) {\n part.thoughtSignature = sig;\n pendingThinkingSig = undefined;\n } else if (isGeminiRequest && !isSameProviderAndModel) {\n part.thoughtSignature = SKIP_THOUGHT_SIGNATURE_VALIDATOR;\n }\n parts.push(part);\n }\n }\n }\n if (parts.length > 0) {\n if (pendingThinkingSig && !parts.some((p) => p.thoughtSignature)) {\n parts[parts.length - 1].thoughtSignature = pendingThinkingSig;\n }\n contents.push({ role: \"model\", parts });\n }\n } else if (msg.role === \"toolResult\") {\n let toolName = msg.toolName || msg.name;\n if (!toolName && (msg.toolCallId || msg.id)) {\n const rawTargetId = msg.toolCallId || msg.id;\n const normTargetId = normalizeToolCallId(rawTargetId);\n for (const prev of messages) {\n if (prev.role === \"assistant\" && Array.isArray(prev.content)) {\n const found = prev.content.find(\n (c: any) =>\n (c.type === \"tool_use\" || c.type === \"toolCall\") &&\n (c.id === rawTargetId || normalizeToolCallId(c.id) === normTargetId)\n );\n if (found) {\n toolName = found.name;\n break;\n }\n }\n }\n }\n\n let toolText = \"\";\n const imageParts: Array<any> = [];\n\n if (Array.isArray(msg.content)) {\n const textParts: string[] = [];\n for (const c of msg.content) {\n if (typeof c === \"string\") {\n textParts.push(c);\n } else if (c.type === \"text\") {\n textParts.push(c.text);\n } else if (c.type === \"image\") {\n imageParts.push({\n inlineData: {\n mimeType: c.mimeType || \"image/png\",\n data: c.data,\n },\n });\n } else {\n textParts.push(JSON.stringify(c));\n }\n }\n toolText = textParts.join(\"\\n\");\n } else if (typeof msg.content === \"string\") {\n toolText = msg.content;\n } else if (msg.content) {\n toolText = JSON.stringify(msg.content);\n }\n\n // Strict agy parity (captured 2026-09-05 via mitmdump, agy 1.1.26):\n // - response is always { output: string }, never a top-level list.\n // - error tool results also use the \"output\" key.\n const responseObj = { output: toolText };\n\n const functionResponse: any = {\n name: toolName || \"tool\",\n response: responseObj,\n };\n\n const rawCallId = msg.toolCallId || msg.id;\n const callId = normalizeToolCallId(rawCallId);\n if (callId) {\n functionResponse.id = callId;\n }\n\n if (imageParts.length > 0) {\n functionResponse.parts = imageParts;\n }\n\n contents.push({\n role: \"model\",\n parts: [{ functionResponse }],\n });\n }\n }\n\n // Antigravity rejects requests ending with an incomplete model turn.\n if (contents.length > 0) {\n const lastContent = contents[contents.length - 1];\n if (\n lastContent.role === \"model\" &&\n !lastContent.parts?.some((p: any) => p.functionResponse)\n ) {\n contents.push({\n role: \"user\",\n parts: [{ text: \"continue\" }],\n });\n }\n }\n\n return contents;\n}\n\n/**\n * Builds the complete Antigravity Wire Fingerprint request envelope.\n * Encapsulates session identity derivation, Thought Signature replay,\n * Turn Trace translation, and tool schema normalization.\n */\nexport function buildAntigravityRequestBody(params: BuildRequestBodyParams): Record<string, any> {\n const {\n projectId,\n plan,\n context,\n maxOutputTokens = 65536,\n } = params;\n const runtimeModelId = plan.runtimeModelId;\n\n const sessionId = deriveSessionId(context, params.sessionId);\n const trajectoryId =\n params.trajectoryId && typeof params.trajectoryId === \"string\" && params.trajectoryId.trim().length > 0\n ? params.trajectoryId.trim()\n : resolveSessionTrajectory(sessionId);\n const contents = translateTurnTrace(context, runtimeModelId);\n\n // Request ID sequence increments by completed assistant turns\n const requestIndex = context.messages.filter((m) => m.role === \"assistant\").length;\n const requestId = `agent/${sessionId}/${Date.now()}/${trajectoryId}/${contents.length}`;\n\n const modelEnum = plan.modelEnum;\n\n const labels: Record<string, string> = {\n last_step_index: String(Math.max(0, contents.length - 1)),\n model_enum: modelEnum,\n request_id: `${trajectoryId}-${requestIndex}`,\n trajectory_id: trajectoryId,\n used_claude: String(plan.isClaude),\n used_claude_conservative: String(plan.isClaude),\n used_non_gemini_model: String(plan.isNonGemini),\n };\n\n const generationConfig: Record<string, any> = {\n maxOutputTokens,\n thinkingConfig: plan.thinkingConfig,\n };\n\n const request: Record<string, any> = {\n contents,\n };\n\n if (context.systemPrompt) {\n request.systemInstruction = {\n role: \"user\",\n parts: [{ text: context.systemPrompt }],\n };\n }\n\n const tools = convertTools(context.tools);\n if (tools) {\n request.tools = tools;\n }\n\n if (params.toolChoice && params.toolChoice !== \"auto\" && params.toolChoice !== \"Auto\") {\n let mode = \"AUTO\";\n const tc = String(params.toolChoice).toLowerCase();\n if (tc === \"none\") mode = \"NONE\";\n else if (tc === \"any\" || tc === \"required\") mode = \"ANY\";\n request.toolConfig = {\n functionCallingConfig: {\n mode,\n },\n };\n }\n\n request.labels = labels;\n request.generationConfig = generationConfig;\n request.sessionId = resolveNumericSessionId(sessionId);\n\n return {\n project: projectId,\n requestId,\n request,\n model: runtimeModelId,\n userAgent: \"antigravity\",\n requestType: \"agent\",\n };\n}\n", "import type { ExtensionCommandContext } from \"@earendil-works/pi-coding-agent\";\nimport { parseStoredCredentials } from \"./auth.ts\";\nimport { PROVIDER_ID } from \"./protocol.ts\";\nimport {\n formatQuotaSummary,\n paintQuotaStatus,\n type QuotaStatusCoordinator,\n} from \"./quota-status.ts\";\nimport { formatModelsList } from \"./model-catalog.ts\";\nimport { refreshCatalogGeneration } from \"./catalog-refresh.ts\";\n\nimport { emitOutput, openSettings } from \"./settings.ts\";\n\nconst USAGE_TEXT =\n \"Usage: /antigravity <command>\\n\\nCommands:\\n usage Show 5h and weekly quota pool limits\\n models List recommended models with context window and remaining quota (alias: model)\\n refresh Force refresh model catalog\\n settings Pick provider settings: Enter cycles values (alias: setting)\\n login Run /login antigravity\";\n\nexport async function resolveToken(\n ctx: ExtensionCommandContext\n): Promise<{ token: string; projectId: string } | null> {\n const apiKey = await ctx.modelRegistry?.getApiKeyForProvider?.(PROVIDER_ID);\n if (!apiKey) return null;\n return parseStoredCredentials(apiKey);\n}\n\nasync function runModelsSubcommand(ctx: ExtensionCommandContext): Promise<void> {\n if ((await resolveToken(ctx)) === null) {\n emitOutput(ctx, \"Not logged in. Run /login antigravity first.\", \"warning\");\n return;\n }\n try {\n if (ctx.hasUI) ctx.ui.notify(\"Fetching available models\u2026\", \"info\");\n // Single Model Catalog path: freshness lives behind the catalog seam \u2014\n // this module only branches on the verdict and prints.\n const { status, catalog } = await refreshCatalogGeneration(() =>\n ctx.modelRegistry?.refresh?.({ force: true, providers: [PROVIDER_ID], signal: ctx.signal }),\n );\n if (status === \"failed\" || !catalog) {\n throw new Error(\"no new generation received\");\n }\n if (status === \"stale\") {\n // Refresh failed: say so, but still show the retained generation \u2014\n // a stale list beats no list, as long as it is labeled.\n emitOutput(ctx, \"Failed to refresh models, showing last known list.\", \"warning\");\n emitOutput(ctx, formatModelsList(catalog));\n return;\n }\n emitOutput(ctx, formatModelsList(catalog));\n } catch (err: any) {\n emitOutput(ctx, `Failed to fetch models: ${err.message}`, \"error\");\n }\n}\n\nexport function parseAntigravitySubcommand(args: string): string {\n const sub = (args || \"\").trim().toLowerCase();\n // Aliases: \"model\" \u2192 \"models\", \"setting\" \u2192 \"settings\"\n if (sub === \"model\") return \"models\";\n if (sub === \"setting\") return \"settings\";\n return sub;\n}\n\nasync function runUsageSubcommand(\n ctx: ExtensionCommandContext,\n quotaStatus?: QuotaStatusCoordinator,\n): Promise<void> {\n if ((await resolveToken(ctx)) === null) {\n emitOutput(ctx, \"Not logged in. Run /login antigravity first.\", \"warning\");\n return;\n }\n if (!quotaStatus) {\n emitOutput(ctx, \"Quota status is unavailable.\", \"error\");\n return;\n }\n try {\n if (ctx.hasUI) ctx.ui.notify(\"Fetching quota summary\u2026\", \"info\");\n // Explicit look at quota: bypass the mode and model gates, but share the\n // fetch, calibration, and persistence with the footer \u2014 no redundant\n // fetch, no stale footer.\n const summary = await quotaStatus.refresh(ctx, { force: true, ignoreMode: true, signal: ctx.signal });\n if (!summary) {\n emitOutput(ctx, \"Failed to fetch usage.\", \"error\");\n return;\n }\n paintQuotaStatus(quotaStatus, ctx);\n emitOutput(ctx, formatQuotaSummary(summary));\n } catch (err: any) {\n emitOutput(ctx, `Failed to fetch usage: ${err.message}`, \"error\");\n }\n}\n\nexport async function runAntigravitySubcommand(\n args: string,\n ctx: ExtensionCommandContext,\n quotaStatus?: QuotaStatusCoordinator,\n): Promise<void> {\n const parts = (args || \"\").trim().split(/\\s+/).filter(Boolean);\n const sub = parseAntigravitySubcommand(parts[0] || \"\");\n\n if (sub === \"usage\") {\n await runUsageSubcommand(ctx, quotaStatus);\n return;\n }\n\n if (sub === \"models\") {\n await runModelsSubcommand(ctx);\n return;\n }\n\n if (sub === \"settings\") {\n await openSettings(ctx, quotaStatus);\n return;\n }\n\n if (sub === \"refresh\") {\n try {\n if (ctx.hasUI) ctx.ui.notify(\"Refreshing models\u2026\", \"info\");\n const { status } = await refreshCatalogGeneration(() =>\n ctx.modelRegistry?.refresh?.({ force: true, providers: [PROVIDER_ID], signal: ctx.signal }),\n );\n if (status === \"fresh\") {\n emitOutput(ctx, \"Antigravity models refreshed successfully.\");\n } else if (status === \"stale\") {\n emitOutput(ctx, \"Failed to refresh models, keeping last known list.\", \"warning\");\n } else {\n emitOutput(ctx, \"Failed to refresh models: no new generation received.\", \"error\");\n }\n } catch (err: any) {\n emitOutput(ctx, `Failed to refresh: ${err.message}`, \"error\");\n }\n return;\n }\n\n if (sub === \"login\") {\n if (ctx.hasUI) {\n ctx.ui.setEditorText(\"/login antigravity\");\n ctx.ui.notify(\"Press Enter to log in to Antigravity.\", \"info\");\n } else {\n emitOutput(ctx, \"Please run /login antigravity to authenticate.\");\n }\n return;\n }\n\n emitOutput(ctx, USAGE_TEXT);\n}\n", "import type { ExtensionAPI, ExtensionCommandContext } from \"@earendil-works/pi-coding-agent\";\nimport { loginAntigravity, refreshAntigravityToken, getApiKey } from \"./auth.ts\";\nimport { DEFAULT_ENDPOINT, PROVIDER_ID } from \"./protocol.ts\";\nimport { refreshCatalog } from \"./catalog-refresh.ts\";\nimport { QuotaStatusCoordinator } from \"./quota-status.ts\";\nimport { fileQuotaStatusStore } from \"./settings.ts\";\nimport { streamAntigravity } from \"./stream.ts\";\nimport { runAntigravitySubcommand } from \"./commands.ts\";\n\nexport { PROVIDER_ID };\nexport const PROVIDER_NAME = \"Antigravity\";\n\nconst SUBCOMMANDS = [\n { name: \"usage\", description: \"Show 5h and weekly quota pool limits\" },\n { name: \"models\", description: \"List recommended models with context window and remaining quota\" },\n { name: \"refresh\", description: \"Force refresh model catalog\" },\n { name: \"settings\", description: \"Pick provider settings: Enter cycles values\" },\n { name: \"login\", description: \"Run /login antigravity\" },\n];\n\nexport default function (pi: ExtensionAPI): void {\n const quotaStatus = new QuotaStatusCoordinator(fileQuotaStatusStore());\n\n pi.registerProvider(PROVIDER_ID, {\n name: PROVIDER_NAME,\n baseUrl: DEFAULT_ENDPOINT,\n api: \"antigravity-api\",\n models: [], // Purely dynamic provider per Pi SDK architecture\n oauth: {\n name: PROVIDER_NAME,\n login: loginAntigravity,\n refreshToken: refreshAntigravityToken,\n getApiKey,\n },\n streamSimple: streamAntigravity,\n refreshModels: async (context: any) => refreshCatalog(context),\n });\n\n pi.registerCommand(\"antigravity\", {\n description: \"Antigravity quota, models and settings\",\n getArgumentCompletions: (prefix) => {\n const trimmed = prefix.trim();\n if (trimmed === \"\") {\n return SUBCOMMANDS.map((s) => ({ value: s.name, label: s.name, description: s.description }));\n }\n if (/\\s/.test(trimmed)) return null;\n const lower = trimmed.toLowerCase();\n return SUBCOMMANDS.filter((s) => s.name.startsWith(lower)).map((s) => ({\n value: s.name,\n label: s.name,\n description: s.description,\n }));\n },\n handler: async (args: string, ctx: ExtensionCommandContext) => {\n await runAntigravitySubcommand(args, ctx, quotaStatus);\n },\n });\n\n // Footer quota slot: paint cached text instantly, refresh in background.\n pi.on(\"session_start\", async (_event, ctx) => {\n await quotaStatus.refreshAndPaint(ctx);\n });\n\n // Model switch changes which Quota Pool backs the footer \u2014 repaint instantly,\n // then fill in fresh text when switching (back) to an Antigravity model.\n pi.on(\"model_select\", async (event, ctx) => {\n const modelCtx = {\n ui: ctx.ui,\n modelRegistry: ctx.modelRegistry,\n model: event.model || ctx.model,\n };\n await quotaStatus.refreshAndPaint(modelCtx);\n });\n\n // Turn settled: refresh only when the throttle window expired.\n pi.on(\"agent_settled\", async (_event, ctx) => {\n await quotaStatus.refreshAndPaint(ctx);\n });\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["path", "createHash", "createHash"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-provider-antigravity",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Minimal Antigravity provider for Pi Coding Agent",
5
5
  "license": "MIT",
6
6
  "repository": {