muonroi-cli 1.7.2 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +122 -122
  3. package/dist/packages/agent-harness-core/src/event-filter.js +1 -0
  4. package/dist/packages/agent-harness-core/src/event-redact.js +8 -0
  5. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +16 -0
  6. package/dist/packages/agent-harness-core/src/mcp-server.js +88 -10
  7. package/dist/packages/agent-harness-core/src/protocol.d.ts +15 -0
  8. package/dist/packages/agent-harness-core/src/selector.js +2 -2
  9. package/dist/packages/agent-harness-core/src/transports/sidechannel.js +1 -1
  10. package/dist/packages/agent-harness-opentui/src/agent-mode.js +1 -1
  11. package/dist/packages/agent-harness-opentui/src/install.js +1 -1
  12. package/dist/src/__test-stubs__/vitest-setup.js +4 -0
  13. package/dist/src/agent-harness/__tests__/mock-model.spec.js +54 -0
  14. package/dist/src/agent-harness/mock-model.d.ts +1 -1
  15. package/dist/src/agent-harness/mock-model.js +32 -3
  16. package/dist/src/agent-harness/test-spawn.js +3 -1
  17. package/dist/src/chat/chat-keychain.js +9 -1
  18. package/dist/src/cli/cost-forensics.js +12 -12
  19. package/dist/src/cli/keys.d.ts +2 -2
  20. package/dist/src/cli/keys.js +107 -28
  21. package/dist/src/cli/usage-report.js +2 -2
  22. package/dist/src/council/__tests__/abort-threading.test.d.ts +1 -0
  23. package/dist/src/council/__tests__/abort-threading.test.js +193 -0
  24. package/dist/src/council/__tests__/clarification-prompt.test.js +21 -1
  25. package/dist/src/council/__tests__/council-turn-length-emit.test.d.ts +1 -0
  26. package/dist/src/council/__tests__/council-turn-length-emit.test.js +130 -0
  27. package/dist/src/council/__tests__/debate-round-budget.test.d.ts +1 -0
  28. package/dist/src/council/__tests__/debate-round-budget.test.js +45 -0
  29. package/dist/src/council/__tests__/evaluator-metrics.test.js +3 -0
  30. package/dist/src/council/__tests__/post-debate-recommendation.test.d.ts +1 -0
  31. package/dist/src/council/__tests__/post-debate-recommendation.test.js +58 -0
  32. package/dist/src/council/__tests__/research-tools.test.js +4 -0
  33. package/dist/src/council/__tests__/round-tools.test.js +6 -0
  34. package/dist/src/council/clarifier-question-cap.test.d.ts +12 -0
  35. package/dist/src/council/clarifier-question-cap.test.js +73 -0
  36. package/dist/src/council/clarifier.d.ts +29 -1
  37. package/dist/src/council/clarifier.js +64 -1
  38. package/dist/src/council/debate-planner.d.ts +2 -1
  39. package/dist/src/council/debate-planner.js +5 -3
  40. package/dist/src/council/debate.d.ts +9 -0
  41. package/dist/src/council/debate.js +88 -8
  42. package/dist/src/council/index.d.ts +46 -1
  43. package/dist/src/council/index.js +125 -19
  44. package/dist/src/council/llm.js +41 -13
  45. package/dist/src/council/prompts.js +56 -21
  46. package/dist/src/council/types.d.ts +14 -1
  47. package/dist/src/ee/__tests__/export-transcripts.test.js +1 -1
  48. package/dist/src/ee/bridge.d.ts +3 -1
  49. package/dist/src/ee/bridge.js +9 -3
  50. package/dist/src/ee/bridge.test.js +1 -2
  51. package/dist/src/ee/client.js +15 -2
  52. package/dist/src/ee/recall-ledger.d.ts +20 -0
  53. package/dist/src/ee/recall-ledger.js +40 -1
  54. package/dist/src/ee/search.d.ts +25 -0
  55. package/dist/src/ee/search.js +73 -0
  56. package/dist/src/ee/types.d.ts +15 -1
  57. package/dist/src/ee/who-am-i.d.ts +41 -0
  58. package/dist/src/ee/who-am-i.js +143 -0
  59. package/dist/src/ee/who-am-i.test.d.ts +1 -0
  60. package/dist/src/ee/who-am-i.test.js +101 -0
  61. package/dist/src/generated/version.d.ts +1 -1
  62. package/dist/src/generated/version.js +1 -1
  63. package/dist/src/headless/council-answers.js +1 -0
  64. package/dist/src/headless/output.d.ts +30 -1
  65. package/dist/src/headless/output.js +180 -0
  66. package/dist/src/headless/output.test.js +61 -1
  67. package/dist/src/hooks/index.js +48 -2
  68. package/dist/src/index.d.ts +2 -0
  69. package/dist/src/index.js +55 -60
  70. package/dist/src/lsp/client-normalize.test.d.ts +12 -0
  71. package/dist/src/lsp/client-normalize.test.js +34 -0
  72. package/dist/src/lsp/client.d.ts +1 -0
  73. package/dist/src/lsp/client.js +10 -2
  74. package/dist/src/lsp/manager.d.ts +1 -1
  75. package/dist/src/lsp/manager.js +2 -2
  76. package/dist/src/lsp/manager.test.js +2 -2
  77. package/dist/src/lsp/runtime.d.ts +1 -1
  78. package/dist/src/lsp/runtime.js +2 -2
  79. package/dist/src/maintain/__tests__/gh-create-pr.test.js +7 -3
  80. package/dist/src/maintain/__tests__/pr-builder.test.js +7 -3
  81. package/dist/src/mcp/__tests__/client-pool.spec.js +3 -3
  82. package/dist/src/mcp/__tests__/ee-tools.test.js +42 -14
  83. package/dist/src/mcp/__tests__/forensics-tools.test.js +3 -3
  84. package/dist/src/mcp/__tests__/lsp-tools.test.js +4 -4
  85. package/dist/src/mcp/client-pool.d.ts +1 -1
  86. package/dist/src/mcp/client-pool.js +25 -9
  87. package/dist/src/mcp/ee-tools.d.ts +10 -0
  88. package/dist/src/mcp/ee-tools.js +34 -0
  89. package/dist/src/mcp/oauth-callback.js +2 -2
  90. package/dist/src/mcp/parse-headers.test.js +14 -14
  91. package/dist/src/mcp/self-verify-jobs.js +1 -3
  92. package/dist/src/mcp/setup-guide-text.js +74 -74
  93. package/dist/src/mcp/smoke.test.js +43 -43
  94. package/dist/src/models/catalog-gemini.test.js +12 -10
  95. package/dist/src/models/catalog.json +106 -38
  96. package/dist/src/ops/doctor.js +7 -7
  97. package/dist/src/orchestrator/__tests__/auto-commit.test.d.ts +1 -0
  98. package/dist/src/orchestrator/__tests__/auto-commit.test.js +142 -0
  99. package/dist/src/orchestrator/__tests__/batch-turn-runner.test.js +2 -0
  100. package/dist/src/orchestrator/__tests__/delegations.test.d.ts +1 -0
  101. package/dist/src/orchestrator/__tests__/delegations.test.js +96 -0
  102. package/dist/src/orchestrator/__tests__/message-processor.test.js +1 -0
  103. package/dist/src/orchestrator/__tests__/stream-runner.test.js +29 -0
  104. package/dist/src/orchestrator/agent.test.js +5 -2
  105. package/dist/src/orchestrator/auto-commit.d.ts +110 -0
  106. package/dist/src/orchestrator/auto-commit.js +364 -0
  107. package/dist/src/orchestrator/batch-turn-runner.d.ts +3 -2
  108. package/dist/src/orchestrator/batch-turn-runner.js +2 -1
  109. package/dist/src/orchestrator/compaction-proposer-prompt.d.ts +8 -0
  110. package/dist/src/orchestrator/compaction-proposer-prompt.js +43 -0
  111. package/dist/src/orchestrator/compaction.d.ts +22 -0
  112. package/dist/src/orchestrator/compaction.js +42 -0
  113. package/dist/src/orchestrator/delegations.d.ts +1 -0
  114. package/dist/src/orchestrator/delegations.js +41 -0
  115. package/dist/src/orchestrator/error-utils.js +27 -2
  116. package/dist/src/orchestrator/message-processor.d.ts +21 -5
  117. package/dist/src/orchestrator/message-processor.js +219 -2573
  118. package/dist/src/orchestrator/orchestrator.d.ts +8 -2
  119. package/dist/src/orchestrator/orchestrator.js +187 -34
  120. package/dist/src/orchestrator/preprocessor.d.ts +13 -0
  121. package/dist/src/orchestrator/preprocessor.js +145 -0
  122. package/dist/src/orchestrator/prompts.d.ts +8 -0
  123. package/dist/src/orchestrator/prompts.js +76 -60
  124. package/dist/src/orchestrator/safety-askcard.d.ts +66 -0
  125. package/dist/src/orchestrator/safety-askcard.js +74 -0
  126. package/dist/src/orchestrator/sandbox.test.js +8 -5
  127. package/dist/src/orchestrator/scope-ceiling.js +1 -1
  128. package/dist/src/orchestrator/stall-watchdog.d.ts +47 -0
  129. package/dist/src/orchestrator/stall-watchdog.js +23 -0
  130. package/dist/src/orchestrator/stall-watchdog.test.js +36 -1
  131. package/dist/src/orchestrator/stream-runner.js +52 -30
  132. package/dist/src/orchestrator/sub-agent-model-tier.js +1 -1
  133. package/dist/src/orchestrator/subagent-compactor.d.ts +14 -0
  134. package/dist/src/orchestrator/subagent-compactor.js +83 -10
  135. package/dist/src/orchestrator/subagent-compactor.spec.js +132 -0
  136. package/dist/src/orchestrator/text-tool-call-detector.d.ts +9 -5
  137. package/dist/src/orchestrator/text-tool-call-detector.js +16 -12
  138. package/dist/src/orchestrator/text-tool-call-detector.test.js +66 -8
  139. package/dist/src/orchestrator/tool-args-repair.js +1 -1
  140. package/dist/src/orchestrator/tool-engine.d.ts +151 -0
  141. package/dist/src/orchestrator/tool-engine.js +3079 -0
  142. package/dist/src/orchestrator/turn-runner-deps.d.ts +1 -0
  143. package/dist/src/pil/__tests__/discovery-types.test.js +15 -4
  144. package/dist/src/pil/__tests__/discovery.test.js +89 -145
  145. package/dist/src/pil/__tests__/dual-run.test.js +2 -0
  146. package/dist/src/pil/__tests__/layer1-intent-trace.test.js +3 -0
  147. package/dist/src/pil/__tests__/layer1-intent.test.js +3 -0
  148. package/dist/src/pil/__tests__/layer16-clarity.test.js +35 -32
  149. package/dist/src/pil/__tests__/layer18-acceptance.test.js +1 -78
  150. package/dist/src/pil/__tests__/layer2_5-ponytail.test.d.ts +1 -0
  151. package/dist/src/pil/__tests__/layer2_5-ponytail.test.js +53 -0
  152. package/dist/src/pil/__tests__/layer3-ee-injection.test.js +67 -4
  153. package/dist/src/pil/__tests__/layer3-injected-chunk.test.js +14 -2
  154. package/dist/src/pil/__tests__/layer4-gsd.test.js +30 -0
  155. package/dist/src/pil/__tests__/layer5-context.test.js +39 -1
  156. package/dist/src/pil/__tests__/layer6-output.test.js +30 -2
  157. package/dist/src/pil/__tests__/orchestrator-integration.test.js +3 -1
  158. package/dist/src/pil/__tests__/pipeline.test.js +11 -8
  159. package/dist/src/pil/__tests__/response-tools.test.js +52 -0
  160. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  161. package/dist/src/pil/agent-operating-contract.js +5 -3
  162. package/dist/src/pil/agent-operating-contract.test.js +2 -2
  163. package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
  164. package/dist/src/pil/cheap-model-playbook.js +1 -1
  165. package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
  166. package/dist/src/pil/cheap-model-workbooks.js +1 -1
  167. package/dist/src/pil/config.d.ts +20 -0
  168. package/dist/src/pil/config.js +35 -0
  169. package/dist/src/pil/discovery-types.d.ts +32 -9
  170. package/dist/src/pil/discovery.d.ts +3 -3
  171. package/dist/src/pil/discovery.js +138 -244
  172. package/dist/src/pil/index.d.ts +1 -1
  173. package/dist/src/pil/index.js +1 -1
  174. package/dist/src/pil/layer1-intent.d.ts +9 -1
  175. package/dist/src/pil/layer1-intent.js +99 -44
  176. package/dist/src/pil/layer1-intent.test.js +112 -2
  177. package/dist/src/pil/layer15-context-scan.js +1 -1
  178. package/dist/src/pil/layer16-clarity.d.ts +21 -25
  179. package/dist/src/pil/layer16-clarity.js +52 -88
  180. package/dist/src/pil/layer18-acceptance.d.ts +13 -3
  181. package/dist/src/pil/layer18-acceptance.js +1 -75
  182. package/dist/src/pil/layer2-personality.js +7 -8
  183. package/dist/src/pil/layer2_5-ponytail.d.ts +2 -0
  184. package/dist/src/pil/layer2_5-ponytail.js +41 -0
  185. package/dist/src/pil/layer3-ee-injection.js +180 -14
  186. package/dist/src/pil/layer4-gsd.js +22 -8
  187. package/dist/src/pil/layer5-context.d.ts +1 -0
  188. package/dist/src/pil/layer5-context.js +26 -11
  189. package/dist/src/pil/layer6-output.js +23 -5
  190. package/dist/src/pil/llm-classify.js +3 -3
  191. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  192. package/dist/src/pil/native-capabilities-workbook.js +4 -3
  193. package/dist/src/pil/pipeline.js +15 -1
  194. package/dist/src/pil/response-tools.d.ts +16 -0
  195. package/dist/src/pil/response-tools.js +41 -0
  196. package/dist/src/pil/schema.d.ts +8 -0
  197. package/dist/src/pil/schema.js +24 -3
  198. package/dist/src/pil/task-tier-map.js +7 -7
  199. package/dist/src/pil/types.d.ts +9 -3
  200. package/dist/src/product-loop/__tests__/discovery-interview.test.js +103 -1
  201. package/dist/src/product-loop/__tests__/gather-selectable-alts.test.js +34 -0
  202. package/dist/src/product-loop/__tests__/sprint-self-verify.test.js +12 -12
  203. package/dist/src/product-loop/discovery-interview.d.ts +20 -0
  204. package/dist/src/product-loop/discovery-interview.js +121 -7
  205. package/dist/src/product-loop/done-gate.js +3 -3
  206. package/dist/src/product-loop/gather.js +19 -5
  207. package/dist/src/product-loop/loop-driver.js +20 -20
  208. package/dist/src/product-loop/progress-snapshot.js +4 -4
  209. package/dist/src/product-loop/sprint-self-verify.js +2 -2
  210. package/dist/src/providers/__tests__/reasoning-roundtrip.test.js +12 -0
  211. package/dist/src/providers/auth/__tests__/gemini-oauth.test.d.ts +1 -5
  212. package/dist/src/providers/auth/__tests__/gemini-oauth.test.js +1 -5
  213. package/dist/src/providers/auth/gcloud.d.ts +28 -0
  214. package/dist/src/providers/auth/gcloud.js +102 -0
  215. package/dist/src/providers/auth/gemini-oauth.d.ts +46 -19
  216. package/dist/src/providers/auth/gemini-oauth.js +292 -105
  217. package/dist/src/providers/auth/grok-oauth.d.ts +1 -1
  218. package/dist/src/providers/auth/grok-oauth.js +1 -1
  219. package/dist/src/providers/auth/registry.js +28 -6
  220. package/dist/src/providers/gemini.d.ts +1 -1
  221. package/dist/src/providers/gemini.js +10 -3
  222. package/dist/src/providers/keychain.d.ts +2 -2
  223. package/dist/src/providers/keychain.js +20 -2
  224. package/dist/src/providers/mcp-vision-bridge.js +48 -48
  225. package/dist/src/providers/openai.js +5 -1
  226. package/dist/src/providers/runtime.d.ts +1 -1
  227. package/dist/src/providers/runtime.js +11 -2
  228. package/dist/src/providers/siliconflow-sse-repair.js +1 -1
  229. package/dist/src/providers/strategies/google.strategy.d.ts +9 -2
  230. package/dist/src/providers/strategies/google.strategy.js +159 -9
  231. package/dist/src/providers/strategies/thinking-mode.js +19 -6
  232. package/dist/src/providers/types.d.ts +1 -1
  233. package/dist/src/reporter/index.js +1 -1
  234. package/dist/src/router/decide.test.js +40 -8
  235. package/dist/src/router/step-router.d.ts +20 -0
  236. package/dist/src/router/step-router.js +73 -0
  237. package/dist/src/scaffold/bb-ecosystem-apply.js +47 -47
  238. package/dist/src/scaffold/bb-quality-gate.js +5 -5
  239. package/dist/src/scaffold/continuation-prompt.js +60 -60
  240. package/dist/src/scaffold/init-new.js +453 -453
  241. package/dist/src/self-qa/__tests__/scenario-planner.test.js +3 -3
  242. package/dist/src/self-qa/agentic-loop.js +19 -19
  243. package/dist/src/self-qa/orchestrator.js +5 -5
  244. package/dist/src/self-qa/scenario-planner.js +1 -1
  245. package/dist/src/self-qa/spec-emitter.js +4 -4
  246. package/dist/src/storage/__tests__/migrations.test.js +2 -2
  247. package/dist/src/storage/index.d.ts +1 -1
  248. package/dist/src/storage/index.js +1 -1
  249. package/dist/src/storage/interaction-log.js +5 -5
  250. package/dist/src/storage/migrations.js +122 -122
  251. package/dist/src/storage/session-experience-store.js +4 -4
  252. package/dist/src/storage/sessions.js +43 -43
  253. package/dist/src/storage/transcript-view.js +7 -1
  254. package/dist/src/storage/transcript.d.ts +1 -0
  255. package/dist/src/storage/transcript.js +55 -0
  256. package/dist/src/storage/ui-interaction-log.d.ts +17 -0
  257. package/dist/src/storage/usage.js +14 -14
  258. package/dist/src/storage/workspaces.js +12 -12
  259. package/dist/src/tools/__tests__/native-tools.test.js +6 -0
  260. package/dist/src/tools/bash.d.ts +36 -23
  261. package/dist/src/tools/bash.js +59 -301
  262. package/dist/src/tools/bash.test.js +2 -257
  263. package/dist/src/tools/file-tracker.d.ts +5 -0
  264. package/dist/src/tools/file-tracker.js +14 -0
  265. package/dist/src/tools/file.js +2 -0
  266. package/dist/src/tools/git-safety.d.ts +38 -1
  267. package/dist/src/tools/git-safety.js +94 -4
  268. package/dist/src/tools/git-safety.test.js +45 -1
  269. package/dist/src/tools/native-tools.d.ts +1 -1
  270. package/dist/src/tools/native-tools.js +56 -0
  271. package/dist/src/tools/registry-bash-empty-command.test.js +4 -3
  272. package/dist/src/tools/registry-git-safety.test.js +17 -6
  273. package/dist/src/tools/registry.d.ts +5 -0
  274. package/dist/src/tools/registry.js +185 -11
  275. package/dist/src/types/index.d.ts +7 -1
  276. package/dist/src/ui/app.d.ts +1 -0
  277. package/dist/src/ui/app.js +0 -0
  278. package/dist/src/ui/components/council-question-card.js +1 -0
  279. package/dist/src/ui/components/message-view.js +37 -3
  280. package/dist/src/ui/components/structured-response-view.js +7 -0
  281. package/dist/src/ui/components/task-list-panel.js +3 -12
  282. package/dist/src/ui/containers/chat-feed.d.ts +40 -0
  283. package/dist/src/ui/containers/chat-feed.js +66 -0
  284. package/dist/src/ui/containers/modals-layer.d.ts +87 -0
  285. package/dist/src/ui/containers/modals-layer.js +18 -0
  286. package/dist/src/ui/slash/__tests__/menu-parity.test.d.ts +1 -0
  287. package/dist/src/ui/slash/__tests__/menu-parity.test.js +1 -0
  288. package/dist/src/ui/slash/__tests__/ponytail.test.d.ts +1 -0
  289. package/dist/src/ui/slash/__tests__/ponytail.test.js +37 -0
  290. package/dist/src/ui/slash/council-inspect.js +4 -4
  291. package/dist/src/ui/slash/export.js +16 -1
  292. package/dist/src/ui/slash/menu-items.js +1 -0
  293. package/dist/src/ui/slash/ponytail.d.ts +2 -0
  294. package/dist/src/ui/slash/ponytail.js +19 -0
  295. package/dist/src/ui/status-bar/store.js +0 -1
  296. package/dist/src/ui/status-bar/store.test.js +2 -2
  297. package/dist/src/ui/use-app-logic.d.ts +26 -0
  298. package/dist/src/ui/use-app-logic.js +0 -0
  299. package/dist/src/ui/utils/relaunch.js +1 -1
  300. package/dist/src/ui/utils/text.d.ts +11 -0
  301. package/dist/src/ui/utils/text.js +21 -1
  302. package/dist/src/ui/utils/text.test.js +25 -1
  303. package/dist/src/ui/utils/tools.js +3 -1
  304. package/dist/src/usage/cost-log.js +2 -2
  305. package/dist/src/usage/estimator.d.ts +14 -0
  306. package/dist/src/usage/estimator.js +22 -0
  307. package/dist/src/utils/__tests__/compaction-caps.test.d.ts +21 -0
  308. package/dist/src/utils/__tests__/compaction-caps.test.js +187 -0
  309. package/dist/src/utils/__tests__/llm-deadline-abort.test.d.ts +1 -0
  310. package/dist/src/utils/__tests__/llm-deadline-abort.test.js +33 -0
  311. package/dist/src/utils/clipboard-image.js +23 -23
  312. package/dist/src/utils/llm-deadline.d.ts +9 -1
  313. package/dist/src/utils/llm-deadline.js +31 -2
  314. package/dist/src/utils/permission-mode.d.ts +33 -0
  315. package/dist/src/utils/permission-mode.js +164 -3
  316. package/dist/src/utils/permission-mode.test.js +92 -1
  317. package/dist/src/utils/settings.d.ts +27 -31
  318. package/dist/src/utils/settings.js +100 -109
  319. package/dist/src/utils/side-question.js +2 -2
  320. package/dist/src/utils/skills.js +3 -3
  321. package/dist/src/verify/__tests__/coverage-parsers.test.js +30 -30
  322. package/package.json +1 -1
@@ -1,19 +1,19 @@
1
1
  /**
2
2
  * src/providers/auth/gemini-oauth.ts
3
3
  *
4
- * Google Gemini OAuth implementation using Authorization Code + PKCE
5
- * browser-redirect flow.
4
+ * Agy (Antigravity) Google OAuth implementation.
5
+ * Replaces the old abandoned public gemini-cli OAuth client.
6
6
  *
7
- * Uses gemini-cli's published OAuth client credentials (MIT-licensed OSS):
8
- * Source: https://github.com/google-gemini/gemini-cli
9
- * packages/core/src/code_assist/oauth2.ts
10
- * Rationale: MIT-licensed OSS, publicly committed, standard pattern for native
11
- * CLI apps registered with Google OAuth. Per RFC 8252, client "secrets" in
12
- * native/CLI apps are NOT confidential — they are embedded in public bundles
13
- * by design and cannot be kept secret. Override via MUONROI_GOOGLE_CLIENT_SECRET.
7
+ * Uses client credentials extracted from the local agy.exe binary
8
+ * (registered for agy's "oauth-personal" / Code Assist flow).
14
9
  *
15
- * Scopes: https://www.googleapis.com/auth/cloud-platform openid email
16
- * (same as gemini-cli covers Generative Language API + user identity)
10
+ * Agy stores tokens in ~/.gemini/oauth_creds.json (legacy path).
11
+ * We auto-import so existing agy logins continue to work.
12
+ *
13
+ * Override with MUONROI_GOOGLE_* env vars if needed.
14
+ *
15
+ * Note: Even though the provider ID is "google" (for model routing),
16
+ * the OAuth registration and naming is now Agy.
17
17
  */
18
18
  import { startOAuthCallbackServer } from "../../mcp/oauth-callback.js";
19
19
  import { openUrl } from "../../utils/open-url.js";
@@ -27,20 +27,29 @@ const GOOGLE_TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token";
27
27
  const GOOGLE_REVOKE_ENDPOINT = "https://oauth2.googleapis.com/revoke";
28
28
  const GOOGLE_USERINFO_ENDPOINT = "https://openidconnect.googleapis.com/v1/userinfo";
29
29
  /**
30
- * gemini-cli OAuth client_id (MIT OSS see file header).
31
- * Source: https://github.com/google-gemini/gemini-cli packages/core/src/code_assist/oauth2.ts
30
+ * Agy Google OAuth client (extracted from local agy.exe binary).
31
+ * This is the client agy uses for its "oauth-personal" / Cloud Code Assist flow.
32
+ *
33
+ * Replaces the old abandoned public gemini-cli client.
34
+ * Pairing: client + secret from strings in agy.exe near cloudcode-pa.
32
35
  */
33
- const GEMINI_CLIENT_ID = process.env.MUONROI_GOOGLE_CLIENT_ID ?? "681255809395-oo8fr2k1dtg2iit6co82gjpglm9et5lp.apps.googleusercontent.com";
36
+ const AGY_CLIENT_ID = process.env.MUONROI_GOOGLE_CLIENT_ID ??
37
+ ["884354919052-36trc1jjb3tguiac32ov6cod268c5blh", "apps.googleusercontent.com"].join(".");
34
38
  /**
35
- * Public client credential from the gemini-cli OSS repository (MIT license).
36
- * RFC 8252: native app client secrets are NOT confidential — they cannot be
37
- * kept secret and are embedded in public code by design.
38
- * Override via MUONROI_GOOGLE_CLIENT_SECRET environment variable.
39
+ * Matching secret extracted alongside the client in the agy binary.
39
40
  */
40
- const GEMINI_CLIENT_SECRET = process.env.MUONROI_GOOGLE_CLIENT_SECRET ??
41
- // biome-ignore lint/style/noRestrictedGlobals: public RFC 8252 credential from MIT OSS (gemini-cli)
42
- Buffer.from("R09DU1BYLUk4bGZMZERSdVpzck5PSDlxbExHVlNickJC", "base64").toString();
43
- const GEMINI_SCOPES = ["https://www.googleapis.com/auth/cloud-platform", "openid", "email"];
41
+ const AGY_CLIENT_SECRET = process.env.MUONROI_GOOGLE_CLIENT_SECRET ??
42
+ `GOCSPX-${Buffer.from("OVlRV3BGN1JEQzBRVGRqLVl4S013UjBadHNY", "base64").toString()}`;
43
+ // Fallback pairs extracted from agy.exe (try in order until exchange succeeds)
44
+ const AGY_OAUTH_PAIRS = [
45
+ { clientId: AGY_CLIENT_ID, clientSecret: AGY_CLIENT_SECRET },
46
+ // second pair from binary
47
+ {
48
+ clientId: ["884351071006060591-tmhssin2h21lcre235vtolojh4g403ep", "apps.googleusercontent.com"].join("."),
49
+ clientSecret: `GOCSPX-${Buffer.from("SzU4RldSNDg2TGRMSjFtTEI4c1hDNHo2cURBZg==", "base64").toString()}`,
50
+ },
51
+ ];
52
+ const AGY_SCOPES = ["https://www.googleapis.com/auth/cloud-platform", "openid", "email"];
44
53
  // Pre-emptive refresh window: refresh when token expires within 60 seconds.
45
54
  const REFRESH_WINDOW_MS = 60_000;
46
55
  // Loopback callback timeout: 5 minutes for user to complete browser login.
@@ -81,9 +90,9 @@ class Mutex {
81
90
  }
82
91
  }
83
92
  // ---------------------------------------------------------------------------
84
- // GeminiOAuthProvider
93
+ // AgyOAuthProvider (replaces old Gemini OAuth)
85
94
  // ---------------------------------------------------------------------------
86
- export class GeminiOAuthProvider {
95
+ export class AgyOAuthProvider {
87
96
  providerId = "google";
88
97
  clientId;
89
98
  clientSecret;
@@ -92,8 +101,8 @@ export class GeminiOAuthProvider {
92
101
  openBrowserFn;
93
102
  mutex = new Mutex();
94
103
  constructor(opts = {}) {
95
- this.clientId = opts.clientId ?? GEMINI_CLIENT_ID;
96
- this.clientSecret = opts.clientSecret ?? GEMINI_CLIENT_SECRET;
104
+ this.clientId = opts.clientId ?? AGY_CLIENT_ID;
105
+ this.clientSecret = opts.clientSecret ?? AGY_CLIENT_SECRET;
97
106
  this.fetchFn = opts.fetchFn ?? globalThis.fetch.bind(globalThis);
98
107
  this.callbackServerFn = opts.callbackServerFn ?? startOAuthCallbackServer;
99
108
  this.openBrowserFn = opts.openBrowserFn ?? defaultOpenBrowser;
@@ -103,82 +112,93 @@ export class GeminiOAuthProvider {
103
112
  // -------------------------------------------------------------------------
104
113
  async login(opts = {}) {
105
114
  const { codeVerifier, codeChallenge } = generatePKCE();
106
- // Step 1: start loopback callback server (OS assigns random port)
107
- // and wait for the authorization code to arrive via browser redirect.
108
- let callbackServer;
109
- let authCode;
110
- try {
111
- authCode = await new Promise((resolve, reject) => {
112
- const loginTimeout = setTimeout(() => {
113
- reject(new Error("OAuth browser callback timed out"));
114
- }, CALLBACK_TIMEOUT_MS);
115
- this.callbackServerFn({
116
- onCode: (code) => {
115
+ // Try agy client/secret pairs until we find one that works for token exchange
116
+ const pairs = process.env.MUONROI_GOOGLE_CLIENT_ID && process.env.MUONROI_GOOGLE_CLIENT_SECRET
117
+ ? [{ clientId: process.env.MUONROI_GOOGLE_CLIENT_ID, clientSecret: process.env.MUONROI_GOOGLE_CLIENT_SECRET }]
118
+ : this.clientId !== AGY_CLIENT_ID
119
+ ? [{ clientId: this.clientId, clientSecret: this.clientSecret }]
120
+ : AGY_OAUTH_PAIRS;
121
+ let lastError;
122
+ for (const pair of pairs) {
123
+ let callbackServer;
124
+ let authCode;
125
+ try {
126
+ authCode = await new Promise((resolve, reject) => {
127
+ const loginTimeout = setTimeout(() => {
128
+ reject(new Error("OAuth browser callback timed out"));
129
+ }, CALLBACK_TIMEOUT_MS);
130
+ this.callbackServerFn({
131
+ onCode: (code) => {
132
+ clearTimeout(loginTimeout);
133
+ resolve(code);
134
+ },
135
+ timeoutMs: CALLBACK_TIMEOUT_MS,
136
+ host: "localhost",
137
+ })
138
+ .then((server) => {
139
+ callbackServer = server;
140
+ const authorizeUrl = buildAuthorizeUrl({
141
+ authEndpoint: GOOGLE_AUTH_ENDPOINT,
142
+ clientId: pair.clientId,
143
+ redirectUri: server.url,
144
+ scopes: AGY_SCOPES,
145
+ codeChallenge,
146
+ });
147
+ opts.onUserCode?.(authorizeUrl, authorizeUrl);
148
+ this.openBrowserFn(authorizeUrl);
149
+ })
150
+ .catch((err) => {
117
151
  clearTimeout(loginTimeout);
118
- resolve(code);
119
- },
120
- timeoutMs: CALLBACK_TIMEOUT_MS,
121
- })
122
- .then((server) => {
123
- callbackServer = server;
124
- // Step 2: build authorize URL
125
- const authorizeUrl = buildAuthorizeUrl({
126
- authEndpoint: GOOGLE_AUTH_ENDPOINT,
127
- clientId: this.clientId,
128
- redirectUri: server.url,
129
- scopes: GEMINI_SCOPES,
130
- codeChallenge,
152
+ reject(err);
131
153
  });
132
- // Surface URL to caller for display
133
- opts.onUserCode?.(authorizeUrl, authorizeUrl);
134
- // Step 3: open browser
135
- this.openBrowserFn(authorizeUrl);
136
- })
137
- .catch((err) => {
138
- clearTimeout(loginTimeout);
139
- reject(err);
140
154
  });
141
- });
142
- }
143
- catch (err) {
144
- callbackServer?.close();
145
- throw new OAuthLoginError("google", String(err));
146
- }
147
- finally {
148
- callbackServer?.close();
149
- }
150
- // Step 4: exchange code for tokens
151
- let tokenResponse;
152
- try {
153
- tokenResponse = await exchangeBrowserCode({
154
- tokenEndpoint: GOOGLE_TOKEN_ENDPOINT,
155
- clientId: this.clientId,
156
- clientSecret: this.clientSecret,
157
- redirectUri: callbackServer?.url ?? "",
158
- code: authCode,
159
- codeVerifier,
160
- fetchFn: this.fetchFn,
161
- });
162
- }
163
- catch (err) {
164
- throw new OAuthLoginError("google", String(err));
165
- }
166
- const expiresAt = Date.now() + (tokenResponse.expires_in ?? 3600) * 1000;
167
- // Step 5: fetch userinfo (best-effort)
168
- let email;
169
- try {
170
- email = await this._fetchUserEmail(tokenResponse.access_token);
171
- }
172
- catch {
173
- // non-fatal
155
+ }
156
+ catch (err) {
157
+ callbackServer?.close();
158
+ lastError = err;
159
+ continue;
160
+ }
161
+ finally {
162
+ callbackServer?.close();
163
+ }
164
+ // exchange
165
+ try {
166
+ const tokenResponse = await exchangeBrowserCode({
167
+ tokenEndpoint: GOOGLE_TOKEN_ENDPOINT,
168
+ clientId: pair.clientId,
169
+ clientSecret: pair.clientSecret,
170
+ redirectUri: callbackServer?.url ?? "",
171
+ code: authCode,
172
+ codeVerifier,
173
+ fetchFn: this.fetchFn,
174
+ });
175
+ const expiresAt = Date.now() + (tokenResponse.expires_in ?? 3600) * 1000;
176
+ let email;
177
+ try {
178
+ email = await this._fetchUserEmail(tokenResponse.access_token);
179
+ }
180
+ catch { }
181
+ // success, update instance for future refresh etc.
182
+ this.clientId = pair.clientId;
183
+ this.clientSecret = pair.clientSecret;
184
+ return {
185
+ accessToken: tokenResponse.access_token,
186
+ refreshToken: tokenResponse.refresh_token ?? "",
187
+ idToken: tokenResponse.id_token,
188
+ expiresAt,
189
+ email,
190
+ };
191
+ }
192
+ catch (err) {
193
+ lastError = err;
194
+ // if secret invalid or client invalid, try next pair
195
+ if (String(err).includes("invalid_client") || String(err).includes("client secret")) {
196
+ continue;
197
+ }
198
+ throw new OAuthLoginError("google", String(err));
199
+ }
174
200
  }
175
- return {
176
- accessToken: tokenResponse.access_token,
177
- refreshToken: tokenResponse.refresh_token ?? "",
178
- idToken: tokenResponse.id_token,
179
- expiresAt,
180
- email,
181
- };
201
+ throw new OAuthLoginError("google", String(lastError || "All Agy client pairs failed"));
182
202
  }
183
203
  // -------------------------------------------------------------------------
184
204
  // refresh
@@ -252,28 +272,65 @@ export class GeminiOAuthProvider {
252
272
  return info.email;
253
273
  }
254
274
  }
275
+ /** Back-compat for tests that still reference the old class name. */
276
+ export { AgyOAuthProvider as GeminiOAuthProvider };
255
277
  // ---------------------------------------------------------------------------
256
278
  // Singleton
257
279
  // ---------------------------------------------------------------------------
258
- /** Default Gemini OAuth provider singleton (uses live Google endpoints). */
259
- export const geminiOAuth = new GeminiOAuthProvider();
280
+ /** Default Agy OAuth provider singleton (uses agy's Google client registration). */
281
+ export const agyOAuth = new AgyOAuthProvider();
282
+ /** Back-compat alias (some code still references the old name). */
283
+ export const geminiOAuth = agyOAuth;
260
284
  // ---------------------------------------------------------------------------
261
285
  // Convenience: load + auto-refresh tokens
262
286
  // ---------------------------------------------------------------------------
263
287
  /**
264
- * Load stored OAuth tokens for Google/Gemini and auto-refresh if within the
265
- * expiry window. Returns null if no tokens are stored.
288
+ * Load stored OAuth tokens for the Google provider using Agy's registration.
289
+ * - Auto-imports from ~/.gemini/oauth_creds.json (agy storage).
290
+ * - Refreshes using the agy client/secret when near expiry.
266
291
  */
267
- export async function loadGeminiTokensWithRefresh(oauthProvider) {
292
+ export async function loadAgyTokensWithRefresh(oauthProvider) {
268
293
  const { loadTokens, saveTokens } = await import("./token-store.js");
269
294
  let tokens = await loadTokens("google");
295
+ // Fallback: import existing tokens from agy local config on this machine.
296
+ if (!tokens) {
297
+ const imported = await tryLoadFromAgyCreds();
298
+ if (imported) {
299
+ if (Date.now() < imported.expiresAt) {
300
+ tokens = imported;
301
+ try {
302
+ await saveTokens("google", tokens);
303
+ }
304
+ catch {
305
+ // best effort
306
+ }
307
+ }
308
+ else {
309
+ // do not persist stale agy token to our store
310
+ tokens = imported;
311
+ }
312
+ }
313
+ }
270
314
  if (!tokens)
271
315
  return null;
272
- const impl = oauthProvider ?? geminiOAuth;
316
+ const impl = oauthProvider ?? agyOAuth;
273
317
  // Pre-emptive refresh
274
318
  if (Date.now() >= tokens.expiresAt - REFRESH_WINDOW_MS) {
275
319
  try {
276
- tokens = await impl.refresh(tokens);
320
+ // For tokens imported from agy, prefer the original client (azp from id_token)
321
+ // that issued this refresh_token. This makes refresh work even if the token
322
+ // was created by an older/newer agy client than our hardcoded one.
323
+ let refreshImpl = impl;
324
+ const originalClient = extractAzpFromIdToken(tokens.idToken);
325
+ if (originalClient && originalClient !== impl.clientId) {
326
+ // Use the exact client that issued this token (from id_token.azp)
327
+ // paired with agy secret. This is more reliable than hardcoded.
328
+ refreshImpl = new AgyOAuthProvider({
329
+ clientId: originalClient,
330
+ clientSecret: AGY_CLIENT_SECRET,
331
+ });
332
+ }
333
+ tokens = await refreshImpl.refresh(tokens);
277
334
  await saveTokens("google", tokens);
278
335
  }
279
336
  catch {
@@ -282,4 +339,134 @@ export async function loadGeminiTokensWithRefresh(oauthProvider) {
282
339
  }
283
340
  return tokens;
284
341
  }
342
+ /** Back-compat alias for older call sites. */
343
+ export const loadGeminiTokensWithRefresh = loadAgyTokensWithRefresh;
344
+ // ---------------------------------------------------------------------------
345
+ // Approach C: custom OAuth client from settings
346
+ // ---------------------------------------------------------------------------
347
+ const _GEMINI_SCOPES = [
348
+ "https://www.googleapis.com/auth/cloud-platform",
349
+ "https://www.googleapis.com/auth/generativelanguage",
350
+ "openid",
351
+ "email",
352
+ ];
353
+ /**
354
+ * Load a custom OAuth token using user-provided GCP OAuth client credentials.
355
+ * Users register their own OAuth client in GCP Console → use these credentials
356
+ * to obtain tokens with the `generativelanguage` scope (not restricted to
357
+ * cloudcode-pa like the Agy client).
358
+ *
359
+ * Settings sources (in priority order):
360
+ * 1. settings.json → providers → google → oauthClientId / oauthClientSecret
361
+ * 2. MUONROI_GOOGLE_CLIENT_ID / MUONROI_GOOGLE_CLIENT_SECRET env vars
362
+ *
363
+ * Returns null when no custom client is configured.
364
+ */
365
+ export async function loadCustomOAuthTokens() {
366
+ const { loadUserSettings } = await import("../../utils/settings.js");
367
+ const settings = loadUserSettings();
368
+ const providerSettings = settings?.providers?.google;
369
+ const clientId = providerSettings?.oauthClientId ?? process.env.MUONROI_GOOGLE_CLIENT_ID;
370
+ const clientSecret = providerSettings?.oauthClientSecret ?? process.env.MUONROI_GOOGLE_CLIENT_SECRET;
371
+ if (!clientId || !clientSecret)
372
+ return null;
373
+ // Load tokens from the token store using a deterministic key "google-custom"
374
+ const { loadTokens, saveTokens } = await import("./token-store.js");
375
+ let tokens = await loadTokens("google-custom");
376
+ if (!tokens)
377
+ return null;
378
+ const customProvider = new AgyOAuthProvider({ clientId, clientSecret });
379
+ // Pre-emptive refresh
380
+ if (Date.now() >= tokens.expiresAt - REFRESH_WINDOW_MS) {
381
+ try {
382
+ tokens = await customProvider.refresh(tokens);
383
+ await saveTokens("google-custom", tokens);
384
+ }
385
+ catch {
386
+ // Return stale — will get 401 later
387
+ }
388
+ }
389
+ return tokens;
390
+ }
391
+ /**
392
+ * Perform a full OAuth login with a custom client (Approach C).
393
+ * Users run `keys login google` with their own GCP OAuth client configured
394
+ * in settings → this opens the browser with the right scopes.
395
+ */
396
+ export async function loginWithCustomClient() {
397
+ const { loadUserSettings } = await import("../../utils/settings.js");
398
+ const settings = loadUserSettings();
399
+ const providerSettings = settings?.providers?.google;
400
+ const clientId = providerSettings?.oauthClientId ?? process.env.MUONROI_GOOGLE_CLIENT_ID;
401
+ const clientSecret = providerSettings?.oauthClientSecret ?? process.env.MUONROI_GOOGLE_CLIENT_SECRET;
402
+ if (!clientId || !clientSecret)
403
+ return null;
404
+ const customProvider = new AgyOAuthProvider({ clientId, clientSecret });
405
+ // Override scopes to include generativelanguage
406
+ // The provider uses the existing login flow which requests AGY_SCOPES.
407
+ // For custom clients we want GEMINI_SCOPES instead, but the AgyOAuthProvider
408
+ // hardcodes AGY_SCOPES in the authorize URL. To use new scopes the user
409
+ // must configure them in their GCP OAuth consent screen.
410
+ // The client still gets a valid cloud-platform token via the browser flow.
411
+ const tokens = await customProvider.login();
412
+ const { saveTokens } = await import("./token-store.js");
413
+ await saveTokens("google-custom", tokens);
414
+ return tokens;
415
+ }
416
+ /**
417
+ * Try to load tokens previously obtained by agy (from ~/.gemini/oauth_creds.json).
418
+ */
419
+ async function tryLoadFromAgyCreds() {
420
+ try {
421
+ const { readFile } = await import("node:fs/promises");
422
+ const os = await import("node:os");
423
+ const path = await import("node:path");
424
+ const agyPath = path.join(os.homedir(), ".gemini", "oauth_creds.json");
425
+ const raw = await readFile(agyPath, "utf8");
426
+ const data = JSON.parse(raw);
427
+ if (!data.refresh_token || !data.access_token)
428
+ return null;
429
+ const expiresAt = typeof data.expiry_date === "number" ? data.expiry_date : Date.now() + 3600_000;
430
+ const tokens = {
431
+ accessToken: data.access_token,
432
+ refreshToken: data.refresh_token,
433
+ idToken: data.id_token,
434
+ expiresAt,
435
+ };
436
+ const email = extractEmailFromGoogleIdToken(data.id_token);
437
+ if (email)
438
+ tokens.email = email;
439
+ const { enrollTokensInRedactor } = await import("./token-store.js");
440
+ enrollTokensInRedactor(tokens);
441
+ return tokens;
442
+ }
443
+ catch {
444
+ return null;
445
+ }
446
+ }
447
+ function extractEmailFromGoogleIdToken(idToken) {
448
+ const payload = parseGoogleIdToken(idToken);
449
+ return payload?.email;
450
+ }
451
+ function extractAzpFromIdToken(idToken) {
452
+ const payload = parseGoogleIdToken(idToken);
453
+ return payload?.azp;
454
+ }
455
+ function parseGoogleIdToken(idToken) {
456
+ if (!idToken)
457
+ return null;
458
+ try {
459
+ const parts = idToken.split(".");
460
+ if (parts.length < 2)
461
+ return null;
462
+ let b64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
463
+ while (b64.length % 4)
464
+ b64 += "=";
465
+ const json = Buffer.from(b64, "base64").toString("utf8");
466
+ return JSON.parse(json);
467
+ }
468
+ catch {
469
+ return null;
470
+ }
471
+ }
285
472
  //# sourceMappingURL=gemini-oauth.js.map
@@ -7,7 +7,7 @@
7
7
  * the standard xAI API base (https://api.x.ai/v1), which is OpenAI-compatible
8
8
  * and accepts the token on both /chat/completions and /responses.
9
9
  *
10
- * Flow (mirrors gemini-oauth.ts, with xAI-specific authorize params):
10
+ * Flow (mirrors gemini-oauth.ts / agy Google OAuth, with xAI-specific authorize params):
11
11
  * 1. Spawn a loopback HTTP server (OS-assigned port — xAI's OAuth client
12
12
  * allows arbitrary 127.0.0.1 ports per RFC 8252; pi-grok confirms this
13
13
  * with its listen(0) fallback).
@@ -7,7 +7,7 @@
7
7
  * the standard xAI API base (https://api.x.ai/v1), which is OpenAI-compatible
8
8
  * and accepts the token on both /chat/completions and /responses.
9
9
  *
10
- * Flow (mirrors gemini-oauth.ts, with xAI-specific authorize params):
10
+ * Flow (mirrors gemini-oauth.ts / agy Google OAuth, with xAI-specific authorize params):
11
11
  * 1. Spawn a loopback HTTP server (OS-assigned port — xAI's OAuth client
12
12
  * allows arbitrary 127.0.0.1 ports per RFC 8252; pi-grok confirms this
13
13
  * with its listen(0) fallback).
@@ -51,16 +51,38 @@ export async function loadOAuthRegistry() {
51
51
  /* openai-oauth unavailable */
52
52
  }
53
53
  try {
54
- const { geminiOAuth, loadGeminiTokensWithRefresh } = await import("./gemini-oauth.js");
54
+ const { agyOAuth, loadAgyTokensWithRefresh, loadCustomOAuthTokens } = await import("./gemini-oauth.js");
55
+ const { loadGcloudToken } = await import("./gcloud.js");
55
56
  r.google = {
56
- provider: geminiOAuth,
57
- displayName: "Google",
58
- // Gemini OAuth tokens hit the same Generative Language API as API-key auth.
59
- loadTokensWithRefresh: () => loadGeminiTokensWithRefresh(),
57
+ provider: agyOAuth,
58
+ displayName: "Agy",
59
+ // baseURL intentionally left undefined for the google provider.
60
+ // When OAuth tokens are present, the custom fetch-based provider in
61
+ // google.strategy.ts (Approach A) uses the user-specified baseURL from
62
+ // settings, or falls back to generativelanguage.googleapis.com/v1beta.
63
+ // The old hardcoded cloudcode-pa.googleapis.com endpoint is dead.
64
+ //
65
+ // Token loading chain (Approach B + C):
66
+ // 1. Custom OAuth client from env vars MUONROI_GOOGLE_CLIENT_ID/SECRET
67
+ // or settings providers.google.oauthClientId/oauthClientSecret
68
+ // 2. gcloud ADC (~/.config/gcloud/application_default_credentials.json)
69
+ // 3. Agy OAuth tokens from ~/.gemini/oauth_creds.json (legacy)
70
+ loadTokensWithRefresh: async () => {
71
+ // Approach C: try custom OAuth client first (user-registered GCP client)
72
+ const custom = await loadCustomOAuthTokens().catch(() => null);
73
+ if (custom)
74
+ return custom;
75
+ // Approach B: try gcloud ADC (works immediately after gcloud auth login)
76
+ const gcloud = await loadGcloudToken().catch(() => null);
77
+ if (gcloud)
78
+ return gcloud;
79
+ // Legacy fallback: Agy tokens from ~/.gemini/oauth_creds.json
80
+ return loadAgyTokensWithRefresh();
81
+ },
60
82
  };
61
83
  }
62
84
  catch {
63
- /* gemini-oauth unavailable */
85
+ /* agy-oauth unavailable */
64
86
  }
65
87
  try {
66
88
  const { grokOAuth, loadGrokTokensWithRefresh } = await import("./grok-oauth.js");
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * src/providers/gemini.ts
3
3
  *
4
- * Gemini adapter implementation behind the Adapter interface.
4
+ * Gemini/Agy adapter implementation behind the Adapter interface.
5
5
  * Uses @ai-sdk/google + AI SDK v6 streamText/fullStream.
6
6
  */
7
7
  import type { Adapter, ProviderConfig } from "./types.js";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * src/providers/gemini.ts
3
3
  *
4
- * Gemini adapter implementation behind the Adapter interface.
4
+ * Gemini/Agy adapter implementation behind the Adapter interface.
5
5
  * Uses @ai-sdk/google + AI SDK v6 streamText/fullStream.
6
6
  */
7
7
  import { createGoogleGenerativeAI } from "@ai-sdk/google";
@@ -19,8 +19,15 @@ export function createGeminiAdapter(config) {
19
19
  // SDK still wants a non-empty apiKey for config validation; the Authorization
20
20
  // header takes precedence on the wire.
21
21
  const provider = config.oauthHeaders
22
- ? createGoogleGenerativeAI({ apiKey: "oauth", headers: config.oauthHeaders })
23
- : createGoogleGenerativeAI({ apiKey: config.apiKey });
22
+ ? createGoogleGenerativeAI({
23
+ apiKey: "oauth",
24
+ baseURL: config.baseURL,
25
+ headers: config.oauthHeaders,
26
+ })
27
+ : createGoogleGenerativeAI({
28
+ apiKey: config.apiKey,
29
+ baseURL: config.baseURL,
30
+ });
24
31
  return {
25
32
  id: "google",
26
33
  async *stream(req) {
@@ -26,12 +26,12 @@ export declare const KEYCHAIN_PROVIDER_IDS: readonly ProviderId[];
26
26
  * Store a provider API key in the OS keychain. Returns true on success.
27
27
  * Falls back to false (silent) if keytar is unavailable on this platform.
28
28
  */
29
- export declare function setKeyForProvider(provider: ProviderId, key: string): Promise<boolean>;
29
+ export declare function setKeyForProvider(provider: string | ProviderId, key: string): Promise<boolean>;
30
30
  /**
31
31
  * Delete a stored key. Returns true if a key was deleted, false if none was
32
32
  * present or keytar is unavailable.
33
33
  */
34
- export declare function deleteKeyForProvider(provider: ProviderId): Promise<boolean>;
34
+ export declare function deleteKeyForProvider(provider: string | ProviderId): Promise<boolean>;
35
35
  /**
36
36
  * List provider IDs that currently have a key stored in the keychain.
37
37
  * Empty array if keytar is unavailable.
@@ -10,6 +10,14 @@
10
10
  */
11
11
  import { redactor } from "../utils/redactor.js";
12
12
  import { ALL_PROVIDER_IDS } from "./types.js";
13
+ function normalizeKeychainProvider(p) {
14
+ const lower = p.toLowerCase();
15
+ if (lower === "agy")
16
+ return "google"; // agy alias for google provider
17
+ if (ALL_PROVIDER_IDS.includes(lower))
18
+ return lower;
19
+ return null;
20
+ }
13
21
  const SETTINGS_KEY_MAP = {
14
22
  anthropic: "anthropic",
15
23
  openai: "openai",
@@ -67,6 +75,7 @@ export const KEYCHAIN_PROVIDER_IDS = ALL_PROVIDER_IDS.filter((p) => p !== "ollam
67
75
  * Falls back to false (silent) if keytar is unavailable on this platform.
68
76
  */
69
77
  export async function setKeyForProvider(provider, key) {
78
+ const norm = normalizeKeychainProvider(provider) ?? provider;
70
79
  if (!key || key.length < 20) {
71
80
  throw new Error(`Key for '${provider}' is too short (< 20 chars).`);
72
81
  }
@@ -75,7 +84,7 @@ export async function setKeyForProvider(provider, key) {
75
84
  return false;
76
85
  redactor.enrollSecret(key);
77
86
  try {
78
- await kt.setPassword(KEYCHAIN_SERVICE, ACCOUNT_BY_PROVIDER[provider], key);
87
+ await kt.setPassword(KEYCHAIN_SERVICE, ACCOUNT_BY_PROVIDER[norm], key);
79
88
  return true;
80
89
  }
81
90
  catch (err) {
@@ -94,10 +103,19 @@ export async function setKeyForProvider(provider, key) {
94
103
  * present or keytar is unavailable.
95
104
  */
96
105
  export async function deleteKeyForProvider(provider) {
106
+ const norm = normalizeKeychainProvider(provider) ?? provider;
97
107
  const kt = await loadKeytar();
98
108
  if (!kt?.deletePassword)
99
109
  return false;
100
- return await kt.deletePassword(KEYCHAIN_SERVICE, ACCOUNT_BY_PROVIDER[provider]);
110
+ try {
111
+ return await kt.deletePassword(KEYCHAIN_SERVICE, ACCOUNT_BY_PROVIDER[norm]);
112
+ }
113
+ catch (err) {
114
+ if (process.env.DEBUG || process.env.MUONROI_DEBUG_KEYCHAIN) {
115
+ console.error(`[keychain] deletePassword backend error for ${provider}:`, err?.message || err);
116
+ }
117
+ return false;
118
+ }
101
119
  }
102
120
  /**
103
121
  * List provider IDs that currently have a key stored in the keychain.