pi-ui-extend 1.0.41 → 1.0.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/README.md +50 -13
  2. package/dist/app/app.d.ts +13 -0
  3. package/dist/app/app.js +158 -8
  4. package/dist/app/cli/install.js +2 -2
  5. package/dist/app/commands/command-controller.d.ts +4 -0
  6. package/dist/app/commands/command-controller.js +11 -0
  7. package/dist/app/commands/command-git-actions.d.ts +25 -0
  8. package/dist/app/commands/command-git-actions.js +381 -0
  9. package/dist/app/commands/command-host.d.ts +4 -0
  10. package/dist/app/commands/command-host.js +5 -2
  11. package/dist/app/commands/command-model-actions.d.ts +1 -0
  12. package/dist/app/commands/command-model-actions.js +57 -35
  13. package/dist/app/commands/command-navigation-actions.d.ts +1 -0
  14. package/dist/app/commands/command-navigation-actions.js +16 -3
  15. package/dist/app/commands/command-registry.d.ts +2 -0
  16. package/dist/app/commands/command-registry.js +15 -1
  17. package/dist/app/commands/command-session-actions.js +9 -3
  18. package/dist/app/commands/reload-context-inventory.d.ts +11 -0
  19. package/dist/app/commands/reload-context-inventory.js +70 -0
  20. package/dist/app/extensions/extension-actions-controller.d.ts +1 -0
  21. package/dist/app/extensions/extension-actions-controller.js +6 -0
  22. package/dist/app/extensions/subagent-catalog-state.d.ts +9 -0
  23. package/dist/app/extensions/subagent-catalog-state.js +23 -0
  24. package/dist/app/input/autocomplete-controller.js +61 -34
  25. package/dist/app/input/input-action-controller.d.ts +2 -0
  26. package/dist/app/input/input-action-controller.js +8 -1
  27. package/dist/app/input/input-controller.d.ts +2 -1
  28. package/dist/app/input/input-controller.js +7 -2
  29. package/dist/app/input/prompt-enhancer-controller.js +53 -35
  30. package/dist/app/input/voice-controller.d.ts +41 -45
  31. package/dist/app/input/voice-controller.js +351 -419
  32. package/dist/app/model/model-usage-controller.js +17 -7
  33. package/dist/app/model/model-usage-status.d.ts +4 -1
  34. package/dist/app/model/model-usage-status.js +270 -46
  35. package/dist/app/popup/menu-items-controller.d.ts +13 -3
  36. package/dist/app/popup/menu-items-controller.js +37 -21
  37. package/dist/app/popup/popup-action-controller.d.ts +12 -2
  38. package/dist/app/popup/popup-action-controller.js +77 -24
  39. package/dist/app/popup/popup-menu-controller.d.ts +36 -14
  40. package/dist/app/popup/popup-menu-controller.js +239 -69
  41. package/dist/app/rendering/dcp-stats.d.ts +9 -4
  42. package/dist/app/rendering/dcp-stats.js +40 -425
  43. package/dist/app/rendering/editor-panels.js +10 -4
  44. package/dist/app/rendering/popup-menu-renderer.d.ts +3 -5
  45. package/dist/app/rendering/popup-menu-renderer.js +42 -37
  46. package/dist/app/rendering/render-controller.js +23 -2
  47. package/dist/app/rendering/status-line-renderer.d.ts +5 -0
  48. package/dist/app/rendering/status-line-renderer.js +41 -5
  49. package/dist/app/rendering/tab-line-renderer.js +26 -20
  50. package/dist/app/runtime.d.ts +12 -1
  51. package/dist/app/runtime.js +120 -12
  52. package/dist/app/screen/mouse-controller.d.ts +2 -0
  53. package/dist/app/screen/mouse-controller.js +17 -7
  54. package/dist/app/screen/status-controller.d.ts +4 -0
  55. package/dist/app/screen/status-controller.js +5 -0
  56. package/dist/app/session/lazy-session-manager.js +34 -0
  57. package/dist/app/session/session-event-controller.d.ts +1 -0
  58. package/dist/app/session/session-event-controller.js +10 -1
  59. package/dist/app/session/session-history.d.ts +1 -0
  60. package/dist/app/session/session-history.js +12 -1
  61. package/dist/app/session/session-lifecycle-controller.d.ts +7 -1
  62. package/dist/app/session/session-lifecycle-controller.js +16 -1
  63. package/dist/app/session/tabs-controller.d.ts +26 -1
  64. package/dist/app/session/tabs-controller.js +454 -147
  65. package/dist/app/subagents/subagents-files.js +60 -1
  66. package/dist/app/subagents/subagents-model.d.ts +1 -0
  67. package/dist/app/subagents/subagents-model.js +18 -2
  68. package/dist/app/subagents/subagents-widget-controller.d.ts +1 -0
  69. package/dist/app/subagents/subagents-widget-controller.js +6 -0
  70. package/dist/app/types.d.ts +16 -1
  71. package/dist/app/workspace/workspace-actions-controller.js +10 -2
  72. package/dist/app/workspace/workspace-undo.d.ts +1 -0
  73. package/dist/app/workspace/workspace-undo.js +1 -0
  74. package/dist/bundled-extensions/question/index.js +9 -1
  75. package/dist/bundled-extensions/question/remote.d.ts +4 -0
  76. package/dist/bundled-extensions/question/remote.js +33 -0
  77. package/dist/bundled-extensions/telegram-connector/bot.d.ts +43 -0
  78. package/dist/bundled-extensions/telegram-connector/bot.js +166 -0
  79. package/dist/bundled-extensions/telegram-connector/config.d.ts +8 -0
  80. package/dist/bundled-extensions/telegram-connector/config.js +87 -0
  81. package/dist/bundled-extensions/telegram-connector/coordinator.d.ts +66 -0
  82. package/dist/bundled-extensions/telegram-connector/coordinator.js +413 -0
  83. package/dist/bundled-extensions/telegram-connector/index.d.ts +3 -0
  84. package/dist/bundled-extensions/telegram-connector/index.js +195 -0
  85. package/dist/bundled-extensions/terminal-bell/index.d.ts +0 -8
  86. package/dist/bundled-extensions/terminal-bell/index.js +0 -76
  87. package/dist/bundled-extensions/workspace-undo/index.d.ts +26 -0
  88. package/dist/bundled-extensions/workspace-undo/index.js +191 -0
  89. package/dist/config.d.ts +21 -2
  90. package/dist/config.js +159 -32
  91. package/dist/default-pix-config.js +25 -5
  92. package/dist/schemas/index.d.ts +1 -0
  93. package/dist/schemas/index.js +1 -0
  94. package/dist/schemas/pi-tools-suite-schema.d.ts +88 -62
  95. package/dist/schemas/pi-tools-suite-schema.js +54 -83
  96. package/dist/schemas/pix-schema.d.ts +19 -2
  97. package/dist/schemas/pix-schema.js +47 -5
  98. package/dist/schemas/tasks-schema.d.ts +18 -0
  99. package/dist/schemas/tasks-schema.js +41 -0
  100. package/docs/concurrency.md +9 -1
  101. package/docs/desktop-mvp.md +22 -6
  102. package/docs/desktop-task-manager.md +148 -84
  103. package/docs/release.md +30 -6
  104. package/external/pi-tools-suite/README.md +197 -84
  105. package/external/pi-tools-suite/docs/evals.md +1 -1
  106. package/external/pi-tools-suite/docs/session-recovery.md +47 -14
  107. package/external/pi-tools-suite/docs/subagent-model-pools.md +48 -30
  108. package/external/pi-tools-suite/docs/ui-qa-subagent.md +440 -0
  109. package/external/pi-tools-suite/package.json +6 -1
  110. package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +2 -1
  111. package/external/pi-tools-suite/src/antigravity-auth/constants.ts +9 -3
  112. package/external/pi-tools-suite/src/antigravity-auth/headers.ts +41 -3
  113. package/external/pi-tools-suite/src/antigravity-auth/models.ts +94 -21
  114. package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +5 -17
  115. package/external/pi-tools-suite/src/antigravity-auth/payload.ts +72 -7
  116. package/external/pi-tools-suite/src/antigravity-auth/stream.ts +13 -1
  117. package/external/pi-tools-suite/src/async-subagents/agents/frontier-review.md +23 -0
  118. package/external/pi-tools-suite/src/async-subagents/agents/implement.md +1 -1
  119. package/external/pi-tools-suite/src/async-subagents/agents/presets.jsonc +16 -0
  120. package/external/pi-tools-suite/src/async-subagents/agents/research.md +5 -3
  121. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/browser.mjs +346 -0
  122. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/desktop.mjs +1038 -0
  123. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/tui.mjs +759 -0
  124. package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/scripts/browser-qa-runner.mjs +32 -23
  125. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/chrome-devtools/chrome-devtools-provider.mjs +895 -0
  126. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/linux/linux-atspi.py +494 -0
  127. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/macos/macos-accessibility.swift +1085 -0
  128. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/native-terminal/bridge-client.mjs +50 -0
  129. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/native-terminal/native-terminal-host.mjs +501 -0
  130. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/windows/windows-uia.ps1 +454 -0
  131. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-auth.md +114 -0
  132. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-chrome-devtools.md +91 -0
  133. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-playwright.md +145 -0
  134. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser.md +82 -0
  135. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-linux.md +26 -0
  136. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-macos.md +29 -0
  137. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-windows.md +23 -0
  138. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop.md +54 -0
  139. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui-native-terminal.md +59 -0
  140. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui-pty.md +37 -0
  141. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui.md +60 -0
  142. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/scripts/ui-qa-runner.mjs +535 -0
  143. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa.md +75 -0
  144. package/external/pi-tools-suite/src/async-subagents/commands.ts +15 -71
  145. package/external/pi-tools-suite/src/async-subagents/core/activity.ts +33 -0
  146. package/external/pi-tools-suite/src/async-subagents/core/agent-catalog.ts +5 -4
  147. package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +1 -1
  148. package/external/pi-tools-suite/src/async-subagents/core/agents-dir.ts +8 -5
  149. package/external/pi-tools-suite/src/async-subagents/core/browser-qa.ts +16 -2
  150. package/external/pi-tools-suite/src/async-subagents/core/config.ts +160 -327
  151. package/external/pi-tools-suite/src/async-subagents/core/model-selection.ts +2 -1
  152. package/external/pi-tools-suite/src/async-subagents/core/registry.ts +158 -33
  153. package/external/pi-tools-suite/src/async-subagents/core/routing.ts +21 -5
  154. package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +40 -26
  155. package/external/pi-tools-suite/src/async-subagents/core/state.ts +40 -0
  156. package/external/pi-tools-suite/src/async-subagents/core/types.ts +7 -0
  157. package/external/pi-tools-suite/src/async-subagents/core/ultrawork-auto.ts +51 -45
  158. package/external/pi-tools-suite/src/async-subagents/index.ts +69 -5
  159. package/external/pi-tools-suite/src/async-subagents/lib.ts +16 -9
  160. package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +8 -5
  161. package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +5 -4
  162. package/external/pi-tools-suite/src/async-subagents/types.ts +1 -0
  163. package/external/pi-tools-suite/src/coding-discipline/index.ts +90 -59
  164. package/external/pi-tools-suite/src/config.ts +55 -1
  165. package/external/pi-tools-suite/src/context-gateway/accounting-log.ts +282 -0
  166. package/external/pi-tools-suite/src/context-gateway/config.ts +77 -3
  167. package/external/pi-tools-suite/src/context-gateway/efficiency.ts +414 -0
  168. package/external/pi-tools-suite/src/context-gateway/enforcement.ts +222 -0
  169. package/external/pi-tools-suite/src/context-gateway/index.ts +206 -19
  170. package/external/pi-tools-suite/src/context-gateway/storeless-capabilities.ts +7 -7
  171. package/external/pi-tools-suite/src/context-gateway/telemetry.ts +101 -28
  172. package/external/pi-tools-suite/src/context-gateway/types.ts +16 -3
  173. package/external/pi-tools-suite/src/context-inventory.ts +99 -0
  174. package/external/pi-tools-suite/src/dcp/auto-compress-budget.ts +39 -4
  175. package/external/pi-tools-suite/src/dcp/auto-compress.ts +128 -49
  176. package/external/pi-tools-suite/src/dcp/commands.ts +32 -156
  177. package/external/pi-tools-suite/src/dcp/compress-tool.ts +177 -46
  178. package/external/pi-tools-suite/src/dcp/compression-blocks.ts +6 -58
  179. package/external/pi-tools-suite/src/dcp/compression-preview.ts +9 -0
  180. package/external/pi-tools-suite/src/dcp/compression-progress.ts +22 -0
  181. package/external/pi-tools-suite/src/dcp/config.ts +114 -17
  182. package/external/pi-tools-suite/src/dcp/conversation-index.ts +36 -9
  183. package/external/pi-tools-suite/src/dcp/diagnostics.ts +41 -0
  184. package/external/pi-tools-suite/src/dcp/fresh-tool-results.ts +38 -0
  185. package/external/pi-tools-suite/src/dcp/index.ts +338 -66
  186. package/external/pi-tools-suite/src/dcp/journal.ts +126 -4
  187. package/external/pi-tools-suite/src/dcp/progress-controller.ts +2 -1
  188. package/external/pi-tools-suite/src/dcp/prompts.ts +83 -192
  189. package/external/pi-tools-suite/src/dcp/protected-continuity.ts +175 -0
  190. package/external/pi-tools-suite/src/dcp/pruner-candidates.ts +93 -3
  191. package/external/pi-tools-suite/src/dcp/pruner-message-ids.ts +6 -10
  192. package/external/pi-tools-suite/src/dcp/pruner-nudge.ts +36 -33
  193. package/external/pi-tools-suite/src/dcp/pruner-tools.ts +6 -3
  194. package/external/pi-tools-suite/src/dcp/pruner.ts +1 -0
  195. package/external/pi-tools-suite/src/dcp/routine-pressure.ts +86 -0
  196. package/external/pi-tools-suite/src/dcp/state.ts +9 -0
  197. package/external/pi-tools-suite/src/dcp/statistics.d.ts +8 -0
  198. package/external/pi-tools-suite/src/dcp/statistics.js +156 -0
  199. package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +78 -22
  200. package/external/pi-tools-suite/src/index.ts +7 -1
  201. package/external/pi-tools-suite/src/lib/project.ts +36 -1
  202. package/external/pi-tools-suite/src/model-tools/index.ts +10 -7
  203. package/external/pi-tools-suite/src/repo-discovery/index.ts +304 -4
  204. package/external/pi-tools-suite/src/resource-registry/index.ts +2551 -0
  205. package/external/pi-tools-suite/src/session-recovery/index.ts +17 -0
  206. package/external/pi-tools-suite/src/shell-command-policy.ts +219 -0
  207. package/external/pi-tools-suite/src/todo/index.ts +21 -0
  208. package/external/pi-tools-suite/src/todo/todo.ts +19 -1
  209. package/external/pi-tools-suite/src/tool-descriptions.ts +47 -19
  210. package/package.json +10 -9
  211. package/schemas/pi-tools-suite.json +466 -287
  212. package/schemas/pix.json +129 -11
  213. package/schemas/tasks.json +131 -0
  214. package/skills/simplify/SKILL.md +33 -5
  215. package/docs/desktop-markdown-media.md +0 -77
  216. package/external/pi-tools-suite/docs/browser-qa-subagent.md +0 -177
  217. package/external/pi-tools-suite/docs/dcp-emergency-current-turn.md +0 -102
  218. package/external/pi-tools-suite/src/async-subagents/agents/browser-qa.md +0 -598
  219. package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +0 -54
  220. package/external/pi-tools-suite/src/skill-installer/index.ts +0 -333
  221. package/skills/playwright-cli/SKILL.md +0 -420
  222. package/skills/playwright-cli/references/element-attributes.md +0 -23
  223. package/skills/playwright-cli/references/playwright-tests.md +0 -50
  224. package/skills/playwright-cli/references/request-mocking.md +0 -87
  225. package/skills/playwright-cli/references/running-code.md +0 -241
  226. package/skills/playwright-cli/references/session-management.md +0 -273
  227. package/skills/playwright-cli/references/spec-driven-testing.md +0 -311
  228. package/skills/playwright-cli/references/storage-state.md +0 -290
  229. package/skills/playwright-cli/references/test-generation.md +0 -142
  230. package/skills/playwright-cli/references/tracing.md +0 -154
  231. package/skills/playwright-cli/references/video-recording.md +0 -147
  232. package/skills/spec-lite/SKILL.md +0 -140
  233. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/examples/qa-auth.example.jsonc +0 -0
  234. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/examples/qa-flow.example.jsonc +0 -0
  235. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/vendor/fflate.LICENSE +0 -0
  236. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/vendor/fflate.mjs +0 -0
@@ -1,5 +1,12 @@
1
1
  import { MODEL_USAGE_POLL_INTERVAL_MS, MODEL_USAGE_STATUS_TICK_MS } from "../constants.js";
2
2
  import { formatModelUsageStatusLabel, modelUsageDescriptor, queryModelUsageStatus, } from "./model-usage-status.js";
3
+ function descriptorCacheKey(descriptor) {
4
+ if (!descriptor)
5
+ return undefined;
6
+ if (descriptor.kind !== "google-antigravity")
7
+ return descriptor.modelKey;
8
+ return `${descriptor.modelKey}\0${(descriptor.quotaModelCandidates ?? [descriptor.quotaModelKey]).join("|")}`;
9
+ }
3
10
  export class AppModelUsageController {
4
11
  host;
5
12
  queryUsageStatus;
@@ -38,18 +45,20 @@ export class AppModelUsageController {
38
45
  refreshNow() {
39
46
  const session = this.host.runtimeSession();
40
47
  this.syncActiveModel(session);
41
- const descriptor = modelUsageDescriptor(session?.model);
48
+ const descriptor = modelUsageDescriptor(session?.model, session?.thinkingLevel);
42
49
  if (!descriptor)
43
50
  return { kind: "unsupported" };
44
51
  const promise = this.refresh(true, descriptor);
45
52
  return promise ? { kind: "started", promise } : { kind: "in-flight" };
46
53
  }
47
54
  tick(force = false) {
48
- this.syncActiveModel(this.host.runtimeSession());
49
- const descriptor = modelUsageDescriptor(this.host.runtimeSession()?.model);
55
+ const session = this.host.runtimeSession();
56
+ this.syncActiveModel(session);
57
+ const descriptor = modelUsageDescriptor(session?.model, session?.thinkingLevel);
50
58
  if (!descriptor)
51
59
  return;
52
- const lastAttemptAt = this.lastAttemptAt.get(descriptor.modelKey) ?? 0;
60
+ const cacheKey = descriptorCacheKey(descriptor);
61
+ const lastAttemptAt = this.lastAttemptAt.get(cacheKey) ?? 0;
53
62
  if (force || Date.now() - lastAttemptAt >= MODEL_USAGE_POLL_INTERVAL_MS) {
54
63
  this.refresh(force, descriptor);
55
64
  return;
@@ -58,10 +67,11 @@ export class AppModelUsageController {
58
67
  this.host.render();
59
68
  }
60
69
  refresh(force = false, activeDescriptor) {
61
- const descriptor = activeDescriptor ?? modelUsageDescriptor(this.host.runtimeSession()?.model);
70
+ const session = this.host.runtimeSession();
71
+ const descriptor = activeDescriptor ?? modelUsageDescriptor(session?.model, session?.thinkingLevel);
62
72
  if (!descriptor)
63
73
  return undefined;
64
- const modelKey = descriptor.modelKey;
74
+ const modelKey = descriptorCacheKey(descriptor);
65
75
  if (this.inFlightModelKeys.has(modelKey))
66
76
  return undefined;
67
77
  const lastAttemptAt = this.lastAttemptAt.get(modelKey) ?? 0;
@@ -86,7 +96,7 @@ export class AppModelUsageController {
86
96
  });
87
97
  }
88
98
  syncActiveModel(session) {
89
- const nextModelKey = modelUsageDescriptor(session?.model)?.modelKey;
99
+ const nextModelKey = descriptorCacheKey(modelUsageDescriptor(session?.model, session?.thinkingLevel));
90
100
  if (nextModelKey === this.activeModelKey)
91
101
  return false;
92
102
  this.activeModelKey = nextModelKey;
@@ -7,6 +7,7 @@ export type ModelUsageDescriptor = BaseModelUsageDescriptor & ({
7
7
  } | {
8
8
  readonly kind: "google-antigravity";
9
9
  readonly quotaModelKey: string;
10
+ readonly quotaModelCandidates?: readonly string[];
10
11
  readonly account?: AntigravityQuotaAccount;
11
12
  readonly accounts?: readonly AntigravityQuotaAccount[];
12
13
  });
@@ -96,6 +97,7 @@ type AntigravityQuotaAccount = {
96
97
  readonly cachedQuota?: AntigravityCachedQuota;
97
98
  readonly cachedQuotaUpdatedAt?: number;
98
99
  readonly projectId: string;
100
+ readonly managedProjectId?: string;
99
101
  readonly accountIndex?: number;
100
102
  readonly accountCount?: number;
101
103
  readonly cacheKey: string;
@@ -107,8 +109,9 @@ type GoogleQuotaResponse = {
107
109
  resetTime?: string;
108
110
  };
109
111
  }>;
112
+ readonly source?: "cached";
110
113
  };
111
- export declare function modelUsageDescriptor(model: SessionModel | undefined): ModelUsageDescriptor | undefined;
114
+ export declare function modelUsageDescriptor(model: SessionModel | undefined, thinkingLevel?: string): ModelUsageDescriptor | undefined;
112
115
  export declare function queryModelUsageStatus(descriptor: ModelUsageDescriptor): Promise<ModelUsageStatus | undefined>;
113
116
  export declare function queryAccountUsageReport(now?: number): Promise<AccountUsageReport>;
114
117
  export declare function formatAccountUsageReport(report: AccountUsageReport, now?: number): string;
@@ -2,31 +2,46 @@ import { createHash } from "node:crypto";
2
2
  import { readFile } from "node:fs/promises";
3
3
  import { homedir } from "node:os";
4
4
  import { join } from "node:path";
5
- import { ModelRuntime, readStoredCredential } from "@earendil-works/pi-coding-agent";
5
+ import { getAgentDir, ModelRuntime, readStoredCredential } from "@earendil-works/pi-coding-agent";
6
6
  import { formatCompactProgressBar } from "../../context-progress-bar.js";
7
7
  import { APP_ICONS } from "../icons.js";
8
8
  const OPENAI_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
9
9
  const ZAI_QUOTA_URL = "https://api.z.ai/api/monitor/usage/quota/limit";
10
10
  const ZHIPU_QUOTA_URL = "https://bigmodel.cn/api/monitor/usage/quota/limit";
11
- const GOOGLE_QUOTA_API_URL = "https://cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels";
11
+ const GOOGLE_ANTIGRAVITY_ENDPOINT_DAILY = "https://daily-cloudcode-pa.googleapis.com";
12
+ const GOOGLE_ANTIGRAVITY_ENDPOINT_PROD = "https://cloudcode-pa.googleapis.com";
13
+ const GOOGLE_ANTIGRAVITY_ENDPOINTS = [GOOGLE_ANTIGRAVITY_ENDPOINT_DAILY, GOOGLE_ANTIGRAVITY_ENDPOINT_PROD];
12
14
  const GOOGLE_TOKEN_REFRESH_URL = "https://oauth2.googleapis.com/token";
13
- const GOOGLE_ANTIGRAVITY_USER_AGENT = "antigravity/1.11.9 windows/amd64";
15
+ const GOOGLE_ANTIGRAVITY_CLI_VERSION = "1.1.24";
16
+ const GOOGLE_ANTIGRAVITY_CLI_CHANGE_LIST = "974782877";
14
17
  const REQUEST_TIMEOUT_MS = 10_000;
15
18
  const DAY_SECONDS = 86_400;
16
19
  const HOUR_SECONDS = 3_600;
17
20
  const MODEL_USAGE_WARNING_MIN_USED_PERCENT = 5;
18
21
  const MODEL_USAGE_WARNING_MIN_ELAPSED_SECONDS = 6 * HOUR_SECONDS;
19
- const PI_AUTH_PATH = join(homedir(), ".pi", "agent", "auth.json");
20
22
  const DEFAULT_ANTIGRAVITY_PROJECT_ID = "rising-fact-p41fc";
23
+ function normalizeGoogleAntigravityPlatform(platform = process.platform) {
24
+ return platform === "win32" ? "windows" : platform || "unknown";
25
+ }
26
+ function normalizeGoogleAntigravityArch(arch = process.arch) {
27
+ if (arch === "x64")
28
+ return "amd64";
29
+ if (arch === "ia32")
30
+ return "386";
31
+ return arch || "unknown";
32
+ }
33
+ function buildGoogleAntigravityUserAgent() {
34
+ return `antigravity/cli/${GOOGLE_ANTIGRAVITY_CLI_VERSION} (aidev_client; os_type=${normalizeGoogleAntigravityPlatform()}; arch=${normalizeGoogleAntigravityArch()}; cl=${GOOGLE_ANTIGRAVITY_CLI_CHANGE_LIST}; auth_method=consumer)`;
35
+ }
21
36
  function getPiAuthPath() {
22
37
  return process.env.NODE_ENV === "test" && process.env.PI_TOOLS_SUITE_TEST_AUTH_PATH
23
38
  ? process.env.PI_TOOLS_SUITE_TEST_AUTH_PATH
24
- : PI_AUTH_PATH;
39
+ : join(getAgentDir(), "auth.json");
25
40
  }
26
41
  const OPENAI_QUOTA_PROVIDERS = new Set(["openai", "openai-codex"]);
27
42
  const ZHIPU_QUOTA_PROVIDERS = new Set(["zai", "zhipuai-coding-plan"]);
28
43
  const ANTIGRAVITY_QUOTA_PROVIDERS = new Set(["antigravity", "google-antigravity"]);
29
- export function modelUsageDescriptor(model) {
44
+ export function modelUsageDescriptor(model, thinkingLevel) {
30
45
  if (!model)
31
46
  return undefined;
32
47
  const provider = model.provider.toLowerCase();
@@ -40,10 +55,12 @@ export function modelUsageDescriptor(model) {
40
55
  const quotaModelKey = resolveAntigravityQuotaModelKey(model);
41
56
  if (!quotaModelKey)
42
57
  return undefined;
58
+ const quotaModelCandidates = antigravityQuotaModelCandidates(quotaModelKey, thinkingLevel);
43
59
  return {
44
60
  kind: "google-antigravity",
45
61
  modelKey: `${model.provider}/${model.id}`,
46
62
  quotaModelKey,
63
+ ...(quotaModelCandidates.length > 1 ? { quotaModelCandidates } : {}),
47
64
  };
48
65
  }
49
66
  return undefined;
@@ -395,8 +412,14 @@ export function resolveAntigravityQuotaModelKey(model) {
395
412
  if (normalized.includes("claude") && normalized.includes("opus") && normalized.includes("thinking")) {
396
413
  return "claude-opus-4-6-thinking";
397
414
  }
415
+ // Sonnet thinking rides the base claude-sonnet-4-6 quota bucket.
398
416
  if (normalized.includes("claude") && normalized.includes("sonnet"))
399
417
  return "claude-sonnet-4-6";
418
+ // Versioned flash generations get their own buckets; keep them ahead of the
419
+ // legacy gemini-3-flash alias so 3.5-3.8 don't collapse into it.
420
+ const versionedFlash = normalized.match(/gemini-3\.([5-8])-flash/u) ?? normalized.match(/\bg3\.([5-8])-flash\b/u);
421
+ if (versionedFlash)
422
+ return `gemini-3.${versionedFlash[1]}-flash`;
400
423
  if (normalized.includes("gemini-2.5-flash") || /\bg2[.-]?5-flash\b/u.test(normalized))
401
424
  return "gemini-2.5-flash";
402
425
  if (normalized.includes("gemini-3-flash") || /\bg3-flash\b/u.test(normalized))
@@ -407,10 +430,37 @@ export function resolveAntigravityQuotaModelKey(model) {
407
430
  return "gemini-3.1-pro-low";
408
431
  if (/\bg3(?:-pro)?\b/u.test(normalized))
409
432
  return "gemini-3.1-pro-low";
433
+ // GPT-OSS has no Google-side quota bucket; callers treat undefined as "no
434
+ // quota status for this model".
410
435
  return undefined;
411
436
  }
437
+ function antigravityQuotaModelCandidates(quotaModelKey, thinkingLevel) {
438
+ const candidates = [quotaModelKey];
439
+ const level = thinkingLevel?.toLowerCase();
440
+ const tier = level === "high" || level === "xhigh" || level === "max"
441
+ ? "high"
442
+ : level === undefined || level === "medium"
443
+ ? "medium"
444
+ : "low";
445
+ const versionedFlash = quotaModelKey.match(/^gemini-3\.([5-8])-flash$/u);
446
+ if (versionedFlash) {
447
+ if (versionedFlash[1] === "5") {
448
+ candidates.push(tier === "high"
449
+ ? "gemini-3-flash-agent"
450
+ : tier === "medium"
451
+ ? "gemini-3.5-flash-low"
452
+ : "gemini-3.5-flash-extra-low");
453
+ }
454
+ else {
455
+ candidates.push(`${quotaModelKey}-${tier}`);
456
+ }
457
+ }
458
+ if (quotaModelKey === "gemini-3.1-pro-low" && tier === "high")
459
+ candidates.push("gemini-pro-agent");
460
+ return candidates;
461
+ }
412
462
  export function googleAntigravityUsageStatusFromResponse(data, descriptor, now = Date.now()) {
413
- const window = googleAntigravityWindowFromResponse(data, descriptor.quotaModelKey, now);
463
+ const window = googleAntigravityWindowFromResponse(data, descriptor.quotaModelKey, now, descriptor.quotaModelCandidates);
414
464
  if (!window)
415
465
  return undefined;
416
466
  const weekly = window.windowSeconds >= DAY_SECONDS ? window : undefined;
@@ -424,8 +474,13 @@ export function googleAntigravityUsageStatusFromResponse(data, descriptor, now =
424
474
  ...(hourly ? { hourly } : {}),
425
475
  };
426
476
  }
427
- function googleAntigravityWindowFromResponse(data, quotaModelKey, now) {
428
- const quotaInfo = data.models[quotaModelKey]?.quotaInfo;
477
+ function googleAntigravityWindowFromResponse(data, quotaModelKey, now, quotaModelCandidates = [quotaModelKey]) {
478
+ const quotaInfo = quotaModelCandidates
479
+ .map((candidate) => data.models[candidate]?.quotaInfo)
480
+ .find((candidate) => candidate !== undefined)
481
+ ?? (data.source === "cached" && /^gemini-3\.[5-8]-flash$/u.test(quotaModelKey)
482
+ ? data.models["gemini-3-flash"]?.quotaInfo
483
+ : undefined);
429
484
  if (!quotaInfo)
430
485
  return undefined;
431
486
  const resetAt = parseResetTime(quotaInfo.resetTime, now);
@@ -435,31 +490,112 @@ function googleAntigravityWindowFromResponse(data, quotaModelKey, now) {
435
490
  windowSeconds: Math.max(0, Math.round((resetAt - now) / 1000)),
436
491
  };
437
492
  }
493
+ function googleAntigravityQuotaPoolForModel(quotaModelKey) {
494
+ return quotaModelKey.toLowerCase().includes("claude") ? "non-gemini" : "gemini";
495
+ }
496
+ function googleAntigravityQuotaPoolForBucket(bucketId) {
497
+ if (!bucketId)
498
+ return undefined;
499
+ if (bucketId.startsWith("gemini-"))
500
+ return "gemini";
501
+ if (bucketId.startsWith("3p-"))
502
+ return "non-gemini";
503
+ return undefined;
504
+ }
505
+ function googleAntigravityWindowFromSummaryBucket(bucket, now, windowSeconds) {
506
+ const remainingFraction = Number.isFinite(bucket.remainingFraction) ? bucket.remainingFraction : 0;
507
+ return {
508
+ remainingPercent: clampPercent(Math.round(remainingFraction * 100)),
509
+ resetAt: parseResetTime(bucket.resetTime, now),
510
+ windowSeconds,
511
+ hasKnownWindowDuration: true,
512
+ };
513
+ }
514
+ function mostConstrainedGoogleQuotaBucket(buckets) {
515
+ return buckets.reduce((best, bucket) => {
516
+ if (!best)
517
+ return bucket;
518
+ const bestRemaining = Number.isFinite(best.remainingFraction) ? best.remainingFraction : 0;
519
+ const nextRemaining = Number.isFinite(bucket.remainingFraction) ? bucket.remainingFraction : 0;
520
+ return nextRemaining < bestRemaining ? bucket : best;
521
+ }, undefined);
522
+ }
523
+ function googleAntigravityWindowsFromQuotaSummary(data, pool, now) {
524
+ const buckets = (data.groups ?? [])
525
+ .flatMap((group) => group.buckets ?? [])
526
+ .filter((bucket) => googleAntigravityQuotaPoolForBucket(bucket.bucketId) === pool);
527
+ const hourlyBucket = mostConstrainedGoogleQuotaBucket(buckets.filter((bucket) => bucket.window === "5h"));
528
+ const weeklyBucket = mostConstrainedGoogleQuotaBucket(buckets.filter((bucket) => bucket.window === "weekly"));
529
+ return {
530
+ ...(hourlyBucket ? { hourly: googleAntigravityWindowFromSummaryBucket(hourlyBucket, now, 5 * HOUR_SECONDS) } : {}),
531
+ ...(weeklyBucket ? { weekly: googleAntigravityWindowFromSummaryBucket(weeklyBucket, now, 7 * DAY_SECONDS) } : {}),
532
+ };
533
+ }
534
+ function aggregateGoogleAntigravityWindows(windows) {
535
+ if (windows.length === 0)
536
+ return undefined;
537
+ const hasKnownWindowDuration = windows.every((window) => window.hasKnownWindowDuration === true);
538
+ return {
539
+ remainingPercent: clampPercent(Math.round(windows.reduce((sum, window) => sum + window.remainingPercent, 0) / windows.length)),
540
+ resetAt: Math.min(...windows.map((window) => window.resetAt)),
541
+ windowSeconds: hasKnownWindowDuration
542
+ ? windows[0]?.windowSeconds ?? 0
543
+ : Math.min(...windows.map((window) => window.windowSeconds)),
544
+ ...(hasKnownWindowDuration ? { hasKnownWindowDuration: true } : {}),
545
+ };
546
+ }
438
547
  async function queryGoogleAntigravityModelUsage(descriptor) {
439
548
  const now = Date.now();
440
- const accounts = await readAllAntigravityQuotaAccounts();
549
+ const accounts = await readAllAntigravityQuotaAccounts(descriptor.modelKey);
441
550
  if (accounts.length === 0)
442
551
  return undefined;
443
- const windows = (await Promise.all(accounts.map(async (account) => {
552
+ const pool = googleAntigravityQuotaPoolForModel(descriptor.quotaModelKey);
553
+ const accountWindows = (await Promise.all(accounts.map(async (account) => {
554
+ try {
555
+ const summary = await fetchGoogleAntigravityQuotaSummaryForAccount(account);
556
+ const summaryWindows = googleAntigravityWindowsFromQuotaSummary(summary, pool, now);
557
+ if (summaryWindows.hourly || summaryWindows.weekly)
558
+ return summaryWindows;
559
+ }
560
+ catch {
561
+ // Current Antigravity exposes retrieveUserQuotaSummary, but older or
562
+ // partially rolled-out backends may not. Fall through to the legacy
563
+ // per-model quota response before consulting cached provider data.
564
+ }
444
565
  try {
445
566
  const response = await fetchGoogleAntigravityQuotaForAccount(account, now);
446
- return googleAntigravityWindowFromResponse(response, descriptor.quotaModelKey, now);
567
+ const liveWindow = googleAntigravityWindowFromResponse(response, descriptor.quotaModelKey, now, descriptor.quotaModelCandidates);
568
+ if (liveWindow)
569
+ return { legacy: liveWindow };
570
+ // Google may successfully return fetchAvailableModels while omitting
571
+ // the quotaInfo bucket for a current versioned Flash route. Antigravity
572
+ // stores the last provider-observed aggregate buckets on each account;
573
+ // use that cached shared Flash quota when live data has no matching key.
574
+ const cachedResponse = googleQuotaResponseFromCachedQuota(account.cachedQuota, account.cachedQuotaUpdatedAt, now);
575
+ const cachedWindow = cachedResponse
576
+ ? googleAntigravityWindowFromResponse(cachedResponse, descriptor.quotaModelKey, now, descriptor.quotaModelCandidates)
577
+ : undefined;
578
+ if (!cachedWindow)
579
+ return undefined;
580
+ return { legacy: cachedWindow };
447
581
  }
448
582
  catch {
449
583
  return undefined;
450
584
  }
451
- }))).filter((window) => window !== undefined);
452
- if (windows.length === 0)
585
+ }))).filter((windows) => windows !== undefined);
586
+ if (accountWindows.length === 0)
587
+ return undefined;
588
+ let hourly = aggregateGoogleAntigravityWindows(accountWindows.map((windows) => windows.hourly).filter((window) => window !== undefined));
589
+ let weekly = aggregateGoogleAntigravityWindows(accountWindows.map((windows) => windows.weekly).filter((window) => window !== undefined));
590
+ const legacy = aggregateGoogleAntigravityWindows(accountWindows.map((windows) => windows.legacy).filter((window) => window !== undefined));
591
+ if (!hourly && !weekly && legacy) {
592
+ if (legacy.windowSeconds >= DAY_SECONDS)
593
+ weekly = legacy;
594
+ else
595
+ hourly = legacy;
596
+ }
597
+ if (!hourly && !weekly)
453
598
  return undefined;
454
- const resetAt = Math.min(...windows.map((window) => window.resetAt));
455
- const windowSeconds = Math.max(0, Math.round((resetAt - now) / 1000));
456
- const aggregateWindow = {
457
- remainingPercent: clampPercent(Math.round(windows.reduce((sum, window) => sum + window.remainingPercent, 0) / windows.length)),
458
- resetAt,
459
- windowSeconds,
460
- };
461
- const weekly = aggregateWindow.windowSeconds >= DAY_SECONDS ? aggregateWindow : undefined;
462
- const hourly = weekly ? undefined : aggregateWindow;
463
599
  return {
464
600
  modelKey: descriptor.modelKey,
465
601
  provider: "google-antigravity",
@@ -474,6 +610,10 @@ const GOOGLE_ACCOUNT_QUOTA_WINDOWS = [
474
610
  { label: "Claude Sonnet", quotaModelKey: "claude-sonnet-4-6" },
475
611
  { label: "G2.5 Flash", quotaModelKey: "gemini-2.5-flash" },
476
612
  { label: "G3 Flash", quotaModelKey: "gemini-3-flash" },
613
+ { label: "G3.5 Flash", quotaModelKey: "gemini-3.5-flash" },
614
+ { label: "G3.6 Flash", quotaModelKey: "gemini-3.6-flash" },
615
+ { label: "G3.7 Flash", quotaModelKey: "gemini-3.7-flash" },
616
+ { label: "G3.8 Flash", quotaModelKey: "gemini-3.8-flash" },
477
617
  { label: "G3 Pro", quotaModelKey: "gemini-3.1-pro-low" },
478
618
  ];
479
619
  async function queryGoogleAntigravityAccountUsage(now) {
@@ -500,8 +640,10 @@ async function queryGoogleAntigravityAccountUsage(now) {
500
640
  }));
501
641
  return results;
502
642
  }
503
- async function readAllAntigravityQuotaAccounts() {
504
- const credential = (await readPiAuth()).antigravity;
643
+ async function readAllAntigravityQuotaAccounts(modelKey = "antigravity/") {
644
+ const auth = await readPiAuth();
645
+ const provider = modelKey.slice(0, modelKey.indexOf("/")).toLowerCase();
646
+ const credential = auth[provider] ?? auth.antigravity;
505
647
  if (!credential)
506
648
  return [];
507
649
  const credentialClient = getGoogleOAuthClientCredentials(credential);
@@ -513,6 +655,7 @@ async function readAllAntigravityQuotaAccounts() {
513
655
  ...(credential.email ? { fallbackEmail: credential.email } : {}),
514
656
  ...(credentialClient ? { clientCredentials: credentialClient } : {}),
515
657
  ...(accountIndex === activeIndex && activeAccess ? { accessToken: activeAccess.accessToken } : {}),
658
+ ...(accountIndex === activeIndex && activeAccess?.projectId ? { accessProjectId: activeAccess.projectId } : {}),
516
659
  accountIndex,
517
660
  accountCount: accounts.length,
518
661
  })).filter((account) => account !== undefined);
@@ -523,6 +666,7 @@ async function readAllAntigravityQuotaAccounts() {
523
666
  ...(credential.email ? { fallbackEmail: credential.email } : {}),
524
667
  ...(credentialClient ? { clientCredentials: credentialClient } : {}),
525
668
  ...(fallbackAccess ? { accessToken: fallbackAccess.accessToken } : {}),
669
+ ...(fallbackAccess?.projectId ? { accessProjectId: fallbackAccess.projectId } : {}),
526
670
  }) : undefined;
527
671
  return account ? [account] : [];
528
672
  }
@@ -556,7 +700,10 @@ function getGoogleOAuthClientCredentials(...sources) {
556
700
  if (clientId)
557
701
  return { clientId, ...(clientSecret ? { clientSecret } : {}) };
558
702
  }
559
- return undefined;
703
+ const clientId = process.env.PI_ANTIGRAVITY_GOOGLE_CLIENT_ID?.trim();
704
+ const clientSecret = process.env.PI_ANTIGRAVITY_GOOGLE_CLIENT_SECRET?.trim();
705
+ if (clientId)
706
+ return { clientId, ...(clientSecret ? { clientSecret } : {}) };
560
707
  }
561
708
  function storedAntigravityAccounts(credential) {
562
709
  return Array.isArray(credential.accounts)
@@ -585,11 +732,12 @@ function antigravityQuotaAccount(account, options = {}) {
585
732
  if (!refreshToken)
586
733
  return undefined;
587
734
  const email = account.email || options.fallbackEmail;
588
- const projectId = account.projectId || account.managedProjectId || DEFAULT_ANTIGRAVITY_PROJECT_ID;
735
+ const projectId = options.accessProjectId || account.projectId || account.managedProjectId || DEFAULT_ANTIGRAVITY_PROJECT_ID;
589
736
  const clientCredentials = getGoogleOAuthClientCredentials(account, options.clientCredentials);
590
737
  return {
591
738
  refreshToken,
592
739
  projectId,
740
+ ...(account.managedProjectId ? { managedProjectId: account.managedProjectId } : {}),
593
741
  cacheKey: email ? email.toLowerCase() : shortHash(refreshToken),
594
742
  ...(options.accessToken ? { accessToken: options.accessToken } : {}),
595
743
  ...(clientCredentials ? { clientId: clientCredentials.clientId } : {}),
@@ -608,7 +756,7 @@ function googleQuotaResponseFromCachedQuota(cachedQuota, cachedQuotaUpdatedAt, n
608
756
  addCachedQuotaModels(models, cachedQuota.claude, ["claude-opus-4-6-thinking", "claude-sonnet-4-6"], cachedQuotaUpdatedAt, now);
609
757
  addCachedQuotaModels(models, cachedQuota["gemini-flash"], ["gemini-2.5-flash", "gemini-3-flash"], cachedQuotaUpdatedAt, now);
610
758
  addCachedQuotaModels(models, cachedQuota["gemini-pro"], ["gemini-3.1-pro-low"], cachedQuotaUpdatedAt, now);
611
- return Object.keys(models).length > 0 ? { models } : undefined;
759
+ return Object.keys(models).length > 0 ? { models, source: "cached" } : undefined;
612
760
  }
613
761
  function addCachedQuotaModels(models, quota, quotaModelKeys, cachedQuotaUpdatedAt, now) {
614
762
  if (!quota)
@@ -669,15 +817,44 @@ function shortHash(value) {
669
817
  return createHash("sha256").update(value).digest("hex").slice(0, 12);
670
818
  }
671
819
  async function fetchGoogleAntigravityQuotaForAccount(account, now = Date.now()) {
672
- if (account.accessToken)
673
- return await fetchGoogleAntigravityQuota(account.accessToken, account.projectId);
674
- if (account.clientId) {
675
- const { accessToken } = await refreshGoogleAntigravityAccessToken(account);
676
- return await fetchGoogleAntigravityQuota(accessToken, account.projectId);
820
+ let liveError;
821
+ try {
822
+ return await fetchWithGoogleAntigravityAccountAccess(account, (accessToken) => fetchGoogleAntigravityQuota(accessToken, account.projectId));
823
+ }
824
+ catch (error) {
825
+ liveError = error;
677
826
  }
678
827
  const cachedResponse = googleQuotaResponseFromCachedQuota(account.cachedQuota, account.cachedQuotaUpdatedAt, now);
679
828
  if (cachedResponse)
680
829
  return cachedResponse;
830
+ if (liveError)
831
+ throw liveError;
832
+ throw new Error("Missing Google OAuth client credentials, cannot query live Antigravity quota.");
833
+ }
834
+ async function fetchGoogleAntigravityQuotaSummaryForAccount(account) {
835
+ return await fetchWithGoogleAntigravityAccountAccess(account, (accessToken) => fetchGoogleAntigravityQuotaSummary(accessToken, account));
836
+ }
837
+ async function fetchWithGoogleAntigravityAccountAccess(account, fetcher) {
838
+ let lastError;
839
+ if (account.accessToken) {
840
+ try {
841
+ return await fetcher(account.accessToken);
842
+ }
843
+ catch (error) {
844
+ lastError = error;
845
+ }
846
+ }
847
+ if (account.clientId) {
848
+ try {
849
+ const { accessToken } = await refreshGoogleAntigravityAccessToken(account);
850
+ return await fetcher(accessToken);
851
+ }
852
+ catch (error) {
853
+ lastError = error;
854
+ }
855
+ }
856
+ if (lastError)
857
+ throw lastError;
681
858
  throw new Error("Missing Google OAuth client credentials, cannot query live Antigravity quota.");
682
859
  }
683
860
  async function refreshGoogleAntigravityAccessToken(account) {
@@ -704,21 +881,68 @@ async function refreshGoogleAntigravityAccessToken(account) {
704
881
  throw new Error("Google token refresh did not return an access token.");
705
882
  return { accessToken: data.access_token };
706
883
  }
884
+ function googleAntigravityQuotaHeaders(accessToken) {
885
+ return {
886
+ "User-Agent": buildGoogleAntigravityUserAgent(),
887
+ Authorization: `Bearer ${accessToken}`,
888
+ "Content-Type": "application/json",
889
+ "Accept-Encoding": "gzip",
890
+ };
891
+ }
892
+ async function fetchGoogleAntigravityQuotaSummary(accessToken, account) {
893
+ const errors = [];
894
+ const primaryProjectId = account.managedProjectId || account.projectId;
895
+ let primaryGot403 = false;
896
+ const tryProject = async (projectId) => {
897
+ for (const endpoint of GOOGLE_ANTIGRAVITY_ENDPOINTS) {
898
+ const response = await fetchWithTimeout(`${endpoint}/v1internal:retrieveUserQuotaSummary`, {
899
+ method: "POST",
900
+ headers: googleAntigravityQuotaHeaders(accessToken),
901
+ body: JSON.stringify({ project: projectId }),
902
+ });
903
+ if (response.ok)
904
+ return response.json();
905
+ if (response.status === 403)
906
+ primaryGot403 = true;
907
+ const errorText = await response.text().catch(() => "");
908
+ errors.push(`retrieveUserQuotaSummary ${response.status} at ${endpoint}${errorText ? `: ${errorText.slice(0, 200)}` : ""}`);
909
+ }
910
+ return undefined;
911
+ };
912
+ const primary = await tryProject(primaryProjectId);
913
+ if (primary)
914
+ return primary;
915
+ if (primaryGot403 && account.managedProjectId && account.projectId !== account.managedProjectId) {
916
+ const fallback = await tryProject(account.projectId);
917
+ if (fallback)
918
+ return fallback;
919
+ }
920
+ throw new Error(errors.join("; ") || "Google Antigravity quota summary request failed");
921
+ }
707
922
  async function fetchGoogleAntigravityQuota(accessToken, projectId) {
708
- const response = await fetchWithTimeout(GOOGLE_QUOTA_API_URL, {
709
- method: "POST",
710
- headers: {
711
- "Content-Type": "application/json",
712
- Authorization: `Bearer ${accessToken}`,
713
- "User-Agent": GOOGLE_ANTIGRAVITY_USER_AGENT,
714
- },
715
- body: JSON.stringify({ project: projectId }),
716
- });
717
- if (!response.ok) {
718
- const errorText = await response.text();
719
- throw new Error(`Google quota request failed (${response.status}): ${errorText}`);
923
+ const errors = [];
924
+ for (const endpoint of GOOGLE_ANTIGRAVITY_ENDPOINTS) {
925
+ const url = `${endpoint}/v1internal:fetchAvailableModels`;
926
+ const response = await fetchWithTimeout(url, {
927
+ method: "POST",
928
+ headers: googleAntigravityQuotaHeaders(accessToken),
929
+ body: JSON.stringify({ project: projectId }),
930
+ });
931
+ if (response.ok)
932
+ return response.json();
933
+ if (response.status === 403 && projectId) {
934
+ const retry = await fetchWithTimeout(url, {
935
+ method: "POST",
936
+ headers: googleAntigravityQuotaHeaders(accessToken),
937
+ body: JSON.stringify({}),
938
+ });
939
+ if (retry.ok)
940
+ return retry.json();
941
+ }
942
+ const errorText = await response.text().catch(() => "");
943
+ errors.push(`fetchAvailableModels ${response.status} at ${endpoint}${errorText ? `: ${errorText.slice(0, 200)}` : ""}`);
720
944
  }
721
- return response.json();
945
+ throw new Error(errors.join("; ") || "Google Antigravity quota request failed");
722
946
  }
723
947
  function parseResetTime(value, now) {
724
948
  if (!value)
@@ -1,11 +1,18 @@
1
1
  import type { AgentSessionRuntime, SessionInfo } from "@earendil-works/pi-coding-agent";
2
2
  import type { PopupMenuItem } from "../../ui.js";
3
- import type { Entry, ModelMenuValue, QueueMessageMenuValue, ResumeMenuValue, SessionModel, SlashCommand, ThinkingMenuValue, UserMessageJumpMenuValue, UserMessageMenuValue } from "../types.js";
3
+ import type { Entry, ModelMenuValue, QueueMessageMenuValue, ResumeMenuValue, SessionModel, SlashCommand, ThinkingLevel, ThinkingMenuValue, UserMessageJumpMenuValue, UserMessageMenuValue } from "../types.js";
4
4
  export type AppMenuItemsControllerHost = {
5
5
  runtime(): AgentSessionRuntime | undefined;
6
+ draftModelState?(): {
7
+ models: readonly SessionModel[];
8
+ modelRef?: string;
9
+ thinkingLevel?: ThinkingLevel;
10
+ } | undefined;
11
+ visibleModels(): readonly string[] | undefined;
6
12
  getBuiltinSlashCommands(): readonly SlashCommand[];
7
13
  getEntries(): readonly Entry[];
8
14
  getResumeSessions(): readonly SessionInfo[];
15
+ getOpenSessionPaths?(): readonly string[];
9
16
  };
10
17
  export declare class AppMenuItemsController {
11
18
  private readonly host;
@@ -18,17 +25,20 @@ export declare class AppMenuItemsController {
18
25
  getSlashCommandMatches(query: string, limit?: number): import("../../fuzzy.js").FuzzyMatch<SlashCommand>[];
19
26
  getSlashCommandMenuItems(query: string): PopupMenuItem<SlashCommand>[];
20
27
  modelRef(model: SessionModel): string;
21
- getModelMenuItems(query: string): PopupMenuItem<ModelMenuValue>[];
28
+ getModelMenuItems(query: string, includeHidden?: boolean): PopupMenuItem<ModelMenuValue>[];
22
29
  getThinkingMenuItems(query: string): PopupMenuItem<ThinkingMenuValue>[];
23
30
  getUserMessageMenuItems(): PopupMenuItem<UserMessageMenuValue>[];
24
31
  getUserMessageJumpMenuItems(query: string): PopupMenuItem<UserMessageJumpMenuValue>[];
25
32
  isUserMessageJumpLoading(): boolean;
26
33
  refreshUserMessageJumpMenuItems(): Promise<void>;
27
34
  getQueueMessageMenuItems(): PopupMenuItem<QueueMessageMenuValue>[];
28
- getResumeMenuItems(query: string, limit?: number): PopupMenuItem<ResumeMenuValue>[];
35
+ getResumeMenuItems(query: string, limit?: number, options?: {
36
+ draft?: boolean;
37
+ }): PopupMenuItem<ResumeMenuValue>[];
29
38
  private getResumeMenuItemsLoader;
30
39
  private getAllSlashCommands;
31
40
  private isCurrentModel;
41
+ private isVisibleModel;
32
42
  private getModelMenuModels;
33
43
  private thinkingLevelDescription;
34
44
  }