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
@@ -21,6 +21,21 @@ export async function iterateInterview(opts) {
21
21
  // not web) are deferred unless the user explicitly re-runs with more context.
22
22
  const specificity = computePromptSpecificity(opts.idea);
23
23
  const skipOptionalForMinimal = specificity === "minimal";
24
+ // G2-b: for a minimal OR well-specified ("detailed") prompt the recommender's
25
+ // primary is high-confidence — minimal picks smallest-scope defaults, detailed
26
+ // respects the stated context. In both cases surfacing a card for EVERY
27
+ // required question (productType/targetPlatform/audience/…) is over-asking the
28
+ // user already complained about: they accept by reflex. Instead auto-accept
29
+ // the recommender primary for required questions and surface ONE summary card
30
+ // (the user gate) listing the assumptions so the user can proceed or adjust.
31
+ // "moderate" prompts keep the per-question cards (genuinely ambiguous). Escape
32
+ // hatch: MUONROI_DISCOVERY_AUTOFILL=0 restores per-question cards everywhere.
33
+ const autoFillRequired = (specificity === "minimal" || specificity === "detailed") && process.env.MUONROI_DISCOVERY_AUTOFILL !== "0";
34
+ const assumed = [];
35
+ // G1 follow-up: keep the recommendation behind each auto-filled assumption so
36
+ // the user-gate "edit: <field>" path can re-render the SAME per-question card
37
+ // (Recommended + alternatives) without a second recommender LLM call.
38
+ const assumedRec = new Map();
24
39
  // Build the repo brief ONCE per interview run for existing projects. The
25
40
  // brief replaces the Muonroi vendor preamble inside leader prompts so
26
41
  // rationales must cite real files, deps, and scripts. Skipped for greenfield —
@@ -74,12 +89,33 @@ export async function iterateInterview(opts) {
74
89
  else {
75
90
  recommendation = await opts.recommender.leaderRecommend(recInput);
76
91
  }
92
+ // G2-b: auto-accept the recommender primary for required questions on a
93
+ // minimal/well-specified prompt — no per-question card. Validated (+ FE
94
+ // policy) so a malformed recommendation falls back to the normal card flow.
95
+ // The assumed answers are surfaced together on the single user-gate card.
96
+ let autoAccepted = false;
97
+ if (autoFillRequired && effectivelyRequired && recommendation.primary?.value != null) {
98
+ const v = recommendation.primary.value;
99
+ const validation = validateAnswer(question.id, v);
100
+ const feLib = question.id === "frontendApproach" ? v?.library : undefined;
101
+ const fePolicyOk = !feLib || isFePolicyAccepted(feLib);
102
+ if (validation.ok && fePolicyOk) {
103
+ await recordRecommendation(flowDir, runId, question.id, toEntry(recommendation), recommendation.costUsd);
104
+ await saveDiscoveryAnswer(flowDir, runId, question.id, v);
105
+ assumed.push({ id: question.id, value: v });
106
+ assumedRec.set(question.id, recommendation);
107
+ autoAccepted = true;
108
+ // Fall through to the post-answer user-gate check below (skip the card).
109
+ }
110
+ }
77
111
  // Per-question skip-attempt counter for required questions.
78
112
  // Resets when the user provides a real answer (accept/override).
79
113
  let skipAttempts = 0;
80
114
  const MAX_SKIP_ATTEMPTS = 3;
81
115
  const _debugInterview = process.env.MUONROI_DEBUG_LEADER === "1";
82
- for (;;) {
116
+ // When auto-accepted above, the card loop body is skipped entirely (the
117
+ // for-condition is false on entry) and we go straight to the gate check.
118
+ for (; !autoAccepted;) {
83
119
  const _iterStart = Date.now();
84
120
  if (_debugInterview) {
85
121
  process.stderr.write(`[interview-timing] iter-start: ${JSON.stringify({ questionId: question.id, skipAttempts })}\n`);
@@ -176,14 +212,32 @@ export async function iterateInterview(opts) {
176
212
  if (refreshed &&
177
213
  allRequiredAnswered(refreshed.questionsAnswered, refreshedPlatforms) &&
178
214
  !refreshed.userGatePassed) {
179
- const gate = await opts.userPrompt({ questionId: "__user_gate__" });
180
- if (gate.action === "proceed") {
181
- await markUserGatePassed(flowDir, runId);
215
+ // Gate loop: "proceed" exits the whole interview, "abort" throws,
216
+ // "edit-field" re-asks one assumed answer then RE-shows the gate (so the
217
+ // user can revise several before proceeding), "ask-more" falls through to
218
+ // keep iterating optional questions.
219
+ let proceeded = false;
220
+ for (;;) {
221
+ const gate = await opts.userPrompt({
222
+ questionId: "__user_gate__",
223
+ assumptions: assumed.length > 0 ? assumed : undefined,
224
+ });
225
+ if (gate.action === "proceed") {
226
+ await markUserGatePassed(flowDir, runId);
227
+ proceeded = true;
228
+ break;
229
+ }
230
+ if (gate.action === "abort")
231
+ throw new Error("discovery aborted at user gate");
232
+ if (gate.action === "edit-field") {
233
+ await reAskAssumedField(opts, flowDir, runId, gate.fieldId, assumed, assumedRec.get(gate.fieldId));
234
+ continue; // re-render the gate with the updated assumption
235
+ }
236
+ // ask-more: stop gating, continue iterating optional questions
182
237
  break;
183
238
  }
184
- if (gate.action === "abort")
185
- throw new Error("discovery aborted at user gate");
186
- // ask-more: continue iterating optional questions
239
+ if (proceeded)
240
+ break;
187
241
  }
188
242
  }
189
243
  const finalState = await readDiscoveryState(flowDir, runId);
@@ -197,6 +251,66 @@ export async function iterateInterview(opts) {
197
251
  await markDone(flowDir, runId);
198
252
  return ctx;
199
253
  }
254
+ /**
255
+ * G1 follow-up: re-ask ONE auto-filled assumption from the user gate. Renders
256
+ * the same per-question card (Recommended + alternatives) as the normal flow by
257
+ * reusing the stored recommendation, validates (+ FE policy), persists the new
258
+ * answer, and updates the in-memory `assumed` list so the re-rendered gate shows
259
+ * the revised value. "skip" cancels the edit (keeps the current assumption);
260
+ * "abort" aborts the whole interview, consistent with the per-question card.
261
+ */
262
+ async function reAskAssumedField(opts, flowDir, runId, fieldId, assumed, recommendation) {
263
+ const question = DISCOVERY_QUESTIONS.find((q) => q.id === fieldId);
264
+ if (!question)
265
+ return; // unknown field id — ignore defensively, keep current value
266
+ for (;;) {
267
+ const ans = await opts.userPrompt({ questionId: fieldId, recommendation });
268
+ if (ans.action === "abort")
269
+ throw new Error("discovery aborted by user");
270
+ let chosenValue;
271
+ if (ans.action === "accept") {
272
+ if (recommendation?.primary?.value == null)
273
+ continue; // nothing to accept — re-prompt
274
+ chosenValue = recommendation.primary.value;
275
+ }
276
+ else if (ans.action === "override") {
277
+ chosenValue = ans.value;
278
+ }
279
+ else if (ans.action === "skip") {
280
+ return; // user backed out of editing this field — keep the existing assumption
281
+ }
282
+ else {
283
+ continue; // more-options / edit-field / proceed / ask-more are no-ops here
284
+ }
285
+ const validation = validateAnswer(fieldId, chosenValue);
286
+ if (!validation.ok) {
287
+ if (fieldId !== "frontendApproach") {
288
+ await opts.userPrompt({ questionId: fieldId, message: validation.reason ?? "invalid answer" });
289
+ }
290
+ continue;
291
+ }
292
+ if (fieldId === "frontendApproach") {
293
+ const lib = chosenValue?.library;
294
+ if (lib && !isFePolicyAccepted(lib)) {
295
+ await opts.userPrompt({
296
+ questionId: fieldId,
297
+ message: "FE policy: library must be shadcn, radix, headlessui, or none",
298
+ });
299
+ continue;
300
+ }
301
+ }
302
+ if (ans.action === "override" && recommendation) {
303
+ await appendUserOverride(flowDir, runId, fieldId, recommendation.primary.value, chosenValue, ans.reason);
304
+ }
305
+ await saveDiscoveryAnswer(flowDir, runId, fieldId, chosenValue);
306
+ const entry = assumed.find((a) => a.id === fieldId);
307
+ if (entry)
308
+ entry.value = chosenValue;
309
+ else
310
+ assumed.push({ id: fieldId, value: chosenValue });
311
+ return;
312
+ }
313
+ }
200
314
  function allRequiredAnswered(answered, platforms) {
201
315
  const baseRequired = REQUIRED_QUESTION_IDS.every((id) => answered.includes(id));
202
316
  if (!baseRequired)
@@ -142,9 +142,9 @@ async function runCustomerDebate(ctx) {
142
142
  const criteriaText = ctx.criteria
143
143
  .map((c) => `- ${c.id}: ${c.status}${c.evidence ? ` (Evidence: ${c.evidence})` : ""}`)
144
144
  .join("\n");
145
- let conversation = `System: You are in a "Definition of Done" debate.
146
- PO's goal: Prove the product is ready to ship.
147
- Customer's goal: Ensure all requirements are met and it's high quality.
145
+ let conversation = `System: You are in a "Definition of Done" debate.
146
+ PO's goal: Prove the product is ready to ship.
147
+ Customer's goal: Ensure all requirements are met and it's high quality.
148
148
  Criteria:\n${criteriaText}\n`;
149
149
  for (let r = 1; r <= rounds; r++) {
150
150
  const poPrompt = `${conversation}\nRound ${r}: PO, explain why this is ready to ship.`;
@@ -93,15 +93,29 @@ function buildLiveTuiAsk(emit, respondToQuestion) {
93
93
  export function buildGatherUserPrompt(tuiAsk) {
94
94
  return async (args) => {
95
95
  if (args.questionId === "__user_gate__") {
96
- const choice = await tuiAsk("All required questions answered. Proceed to research or ask more?", [
97
- "proceed",
98
- "ask-more",
99
- "abort",
100
- ]);
96
+ // G2-b: when required answers were auto-filled from the recommender
97
+ // (minimal/well-specified prompt), this single card IS the summary — list
98
+ // the assumptions so the user reviews them in one place instead of N
99
+ // sequential per-question cards. "ask-more" lets them revisit/expand.
100
+ const hasAssumptions = !!args.assumptions && args.assumptions.length > 0;
101
+ const label = hasAssumptions
102
+ ? `Assumed from your prompt:\n${args
103
+ .assumptions.map((a) => ` • ${a.id} = ${JSON.stringify(a.value)}`)
104
+ .join("\n")}\n\nProceed, edit one of the assumptions, or ask more to adjust/expand?`
105
+ : "All required questions answered. Proceed to research or ask more?";
106
+ // G1 follow-up: one "edit: <field>" option per auto-filled assumption so a
107
+ // single wrong default can be revised in place — no abort+reprompt or
108
+ // MUONROI_DISCOVERY_AUTOFILL=0. The prefix must match the parse below.
109
+ const EDIT_PREFIX = "edit: ";
110
+ const editOpts = hasAssumptions ? args.assumptions.map((a) => `${EDIT_PREFIX}${a.id}`) : [];
111
+ const choice = await tuiAsk(label, ["proceed", ...editOpts, "ask-more", "abort"]);
101
112
  if (choice === "proceed")
102
113
  return { action: "proceed" };
103
114
  if (choice === "abort")
104
115
  return { action: "abort" };
116
+ if (choice.startsWith(EDIT_PREFIX)) {
117
+ return { action: "edit-field", fieldId: choice.slice(EDIT_PREFIX.length) };
118
+ }
105
119
  return { action: "ask-more" };
106
120
  }
107
121
  if (args.message) {
@@ -39,10 +39,10 @@ function wrapLLMForUsageTracking(llm, ctx) {
39
39
  }
40
40
  };
41
41
  return {
42
- generate: (modelId, system, prompt, maxTokens, onUsage) => llm.generate(modelId, system, prompt, maxTokens, (u) => {
42
+ generate: (modelId, system, prompt, maxTokens, onUsage, signal) => llm.generate(modelId, system, prompt, maxTokens, (u) => {
43
43
  recorder(modelId)(u);
44
44
  onUsage?.(u);
45
- }),
45
+ }, signal),
46
46
  debate: (modelId, system, prompt, signal, persistTrace, options, onUsage) => llm.debate(modelId, system, prompt, signal, persistTrace, options, (u) => {
47
47
  recorder(modelId)(u);
48
48
  onUsage?.(u);
@@ -684,24 +684,24 @@ export async function* runLoopDriver(ctx) {
684
684
  },
685
685
  };
686
686
  // Synthesize ProductSpec
687
- const synthesisPrompt = `Synthesize a ProductSpec JSON based on the following:
688
- Idea: ${ctx.idea}
689
- Clarified Spec: ${JSON.stringify(clarifiedSpec)}
690
- Debate Summary: ${debateState.runningSummary}
691
- Research Findings: ${debateState.researchFindings ?? "N/A"}
692
-
693
- Output ONLY a JSON object matching this interface:
694
- interface ProductSpec {
695
- idea: string;
696
- persona: string;
697
- mvp: string[];
698
- phase2: string[];
699
- architecture: string;
700
- ioContract: string;
701
- folderStructure: string;
702
- sprintEstimate: number;
703
- costEstimate: number;
704
- }
687
+ const synthesisPrompt = `Synthesize a ProductSpec JSON based on the following:
688
+ Idea: ${ctx.idea}
689
+ Clarified Spec: ${JSON.stringify(clarifiedSpec)}
690
+ Debate Summary: ${debateState.runningSummary}
691
+ Research Findings: ${debateState.researchFindings ?? "N/A"}
692
+
693
+ Output ONLY a JSON object matching this interface:
694
+ interface ProductSpec {
695
+ idea: string;
696
+ persona: string;
697
+ mvp: string[];
698
+ phase2: string[];
699
+ architecture: string;
700
+ ioContract: string;
701
+ folderStructure: string;
702
+ sprintEstimate: number;
703
+ costEstimate: number;
704
+ }
705
705
  `;
706
706
  // The scoping phase's only LLM call. Wrapped so a provider hang/
707
707
  // timeout leaves a council_error audit row instead of swallowing the
@@ -20,10 +20,10 @@ function readLatestSprintStage(runId) {
20
20
  try {
21
21
  const db = getDatabase();
22
22
  const row = db
23
- .prepare(`SELECT metadata_json, created_at
24
- FROM interaction_logs
25
- WHERE session_id = ? AND event_type = 'ui_interaction' AND event_subtype = 'sprint_stage'
26
- ORDER BY id DESC
23
+ .prepare(`SELECT metadata_json, created_at
24
+ FROM interaction_logs
25
+ WHERE session_id = ? AND event_type = 'ui_interaction' AND event_subtype = 'sprint_stage'
26
+ ORDER BY id DESC
27
27
  LIMIT 1`)
28
28
  .get(runId);
29
29
  if (!row)
@@ -31,10 +31,10 @@ const WATCH_DIRS = ["src/ui/", "src/self-qa/", "src/agent-harness/", "packages/a
31
31
  function isEnabled(forceEnable) {
32
32
  if (forceEnable === true)
33
33
  return true;
34
- const v = process.env["MUONROI_SPRINT_SELF_VERIFY"];
34
+ const v = process.env.MUONROI_SPRINT_SELF_VERIFY;
35
35
  if (v === "0" || (v && v.toLowerCase() === "false"))
36
36
  return false;
37
- if (process.env["CI"] === "true" || process.env["NODE_ENV"] === "ci")
37
+ if (process.env.CI === "true" || process.env.NODE_ENV === "ci")
38
38
  return false;
39
39
  // Default: ON. v === "1"/"true" still works; absence of env var also enables.
40
40
  return true;
@@ -179,6 +179,18 @@ describe("transformThinkingModeBody — backfill / disable (code 20015 fix)", ()
179
179
  expect(asst[0].reasoning_content).toBe("real thought"); // untouched
180
180
  expect(asst[1].reasoning_content).toBe(""); // backfilled
181
181
  });
182
+ it("A (default): backfills content: '' on a reasoning-only turn that lacks content and tool_calls", () => {
183
+ const body = {
184
+ messages: [
185
+ { role: "user", content: "go" },
186
+ { role: "assistant", content: null, reasoning_content: "only thought" },
187
+ ],
188
+ };
189
+ const out = transformThinkingModeBody(body);
190
+ const asst = out.messages.find((m) => m.role === "assistant");
191
+ expect(asst.reasoning_content).toBe("only thought");
192
+ expect(asst.content).toBe("");
193
+ });
182
194
  it("A (default): does not touch non-assistant messages", () => {
183
195
  const body = {
184
196
  messages: [
@@ -1,8 +1,4 @@
1
1
  /**
2
- * Tests for src/providers/auth/gemini-oauth.ts
3
- *
4
- * Login flow (callback server mocked), refresh (expiry pre-emption + mutex),
5
- * revoke, authHeaders, loadGeminiTokensWithRefresh.
6
- * No live network calls — all HTTP via mockFetch, no real browser opens.
2
+ * Tests for Agy Google OAuth (replaces legacy gemini public client)
7
3
  */
8
4
  export {};
@@ -1,9 +1,5 @@
1
1
  /**
2
- * Tests for src/providers/auth/gemini-oauth.ts
3
- *
4
- * Login flow (callback server mocked), refresh (expiry pre-emption + mutex),
5
- * revoke, authHeaders, loadGeminiTokensWithRefresh.
6
- * No live network calls — all HTTP via mockFetch, no real browser opens.
2
+ * Tests for Agy Google OAuth (replaces legacy gemini public client)
7
3
  */
8
4
  import { describe, expect, it, vi } from "vitest";
9
5
  import { GeminiOAuthProvider, loadGeminiTokensWithRefresh } from "../gemini-oauth.js";
@@ -0,0 +1,28 @@
1
+ /**
2
+ * src/providers/auth/gcloud.ts
3
+ *
4
+ * Reads and refreshes tokens from Google Cloud SDK credentials
5
+ * (~/.config/gcloud/application_default_credentials.json).
6
+ *
7
+ * The gcloud ADC token uses GCP's official OAuth client with cloud-platform
8
+ * scopes, which IS accepted by generativelanguage.googleapis.com (unlike the
9
+ * Agy OAuth token whose scope is restricted to cloudcode-pa).
10
+ *
11
+ * Usage:
12
+ * 1. User runs `gcloud auth application-default login` once.
13
+ * 2. This file reads the resulting credentials file, extracts the token,
14
+ * and returns it as an OAuthTokens-compatible object.
15
+ * 3. The token auto-refreshes when expired (using the refresh_token inside
16
+ * the credentials file).
17
+ */
18
+ import type { OAuthTokens } from "./types.js";
19
+ /**
20
+ * Try to load a token from gcloud ADC.
21
+ * Returns null if gcloud is not installed or no credentials exist.
22
+ */
23
+ export declare function loadGcloudToken(): Promise<OAuthTokens | null>;
24
+ /**
25
+ * Obtain auth headers from a gcloud-derived OAuthTokens.
26
+ * Compatible with ProviderOAuth.authHeaders() signature.
27
+ */
28
+ export declare function gcloudAuthHeaders(tokens: OAuthTokens): Record<string, string>;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * src/providers/auth/gcloud.ts
3
+ *
4
+ * Reads and refreshes tokens from Google Cloud SDK credentials
5
+ * (~/.config/gcloud/application_default_credentials.json).
6
+ *
7
+ * The gcloud ADC token uses GCP's official OAuth client with cloud-platform
8
+ * scopes, which IS accepted by generativelanguage.googleapis.com (unlike the
9
+ * Agy OAuth token whose scope is restricted to cloudcode-pa).
10
+ *
11
+ * Usage:
12
+ * 1. User runs `gcloud auth application-default login` once.
13
+ * 2. This file reads the resulting credentials file, extracts the token,
14
+ * and returns it as an OAuthTokens-compatible object.
15
+ * 3. The token auto-refreshes when expired (using the refresh_token inside
16
+ * the credentials file).
17
+ */
18
+ import { readFile } from "node:fs/promises";
19
+ import { homedir } from "node:os";
20
+ import { join } from "node:path";
21
+ import { OAuthRefreshError } from "./types.js";
22
+ // ---------------------------------------------------------------------------
23
+ // Paths
24
+ // ---------------------------------------------------------------------------
25
+ const GCLOUD_ADC_PATH = join(homedir(), ".config", "gcloud", "application_default_credentials.json");
26
+ // ---------------------------------------------------------------------------
27
+ // Loader
28
+ // ---------------------------------------------------------------------------
29
+ /**
30
+ * Load gcloud Application Default Credentials file.
31
+ */
32
+ async function loadADC() {
33
+ try {
34
+ const raw = await readFile(GCLOUD_ADC_PATH, "utf8");
35
+ const creds = JSON.parse(raw);
36
+ if (creds.type !== "authorized_user") {
37
+ // service-account keys have a different shape; skip
38
+ return null;
39
+ }
40
+ return creds;
41
+ }
42
+ catch {
43
+ return null;
44
+ }
45
+ }
46
+ // ---------------------------------------------------------------------------
47
+ // Token exchange
48
+ // ---------------------------------------------------------------------------
49
+ /**
50
+ * Exchange a gcloud refresh_token for a fresh access_token.
51
+ * Google's OAuth token endpoint accepts gcloud ADC credentials.
52
+ */
53
+ async function exchangeRefreshToken(clientId, clientSecret, refreshToken) {
54
+ const body = new URLSearchParams({
55
+ client_id: clientId,
56
+ client_secret: clientSecret,
57
+ refresh_token: refreshToken,
58
+ grant_type: "refresh_token",
59
+ });
60
+ let res;
61
+ try {
62
+ res = await fetch("https://oauth2.googleapis.com/token", {
63
+ method: "POST",
64
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
65
+ body,
66
+ });
67
+ }
68
+ catch (err) {
69
+ throw new OAuthRefreshError("google", `gcloud ADC refresh network error: ${err.message}`);
70
+ }
71
+ if (!res.ok) {
72
+ const text = await res.text().catch(() => "");
73
+ throw new OAuthRefreshError("google", `gcloud ADC refresh failed (${res.status}): ${text}`);
74
+ }
75
+ const data = (await res.json());
76
+ return {
77
+ accessToken: data.access_token,
78
+ refreshToken,
79
+ expiresAt: Date.now() + data.expires_in * 1000,
80
+ };
81
+ }
82
+ // ---------------------------------------------------------------------------
83
+ // Public API
84
+ // ---------------------------------------------------------------------------
85
+ /**
86
+ * Try to load a token from gcloud ADC.
87
+ * Returns null if gcloud is not installed or no credentials exist.
88
+ */
89
+ export async function loadGcloudToken() {
90
+ const creds = await loadADC();
91
+ if (!creds)
92
+ return null;
93
+ return exchangeRefreshToken(creds.client_id, creds.client_secret, creds.refresh_token);
94
+ }
95
+ /**
96
+ * Obtain auth headers from a gcloud-derived OAuthTokens.
97
+ * Compatible with ProviderOAuth.authHeaders() signature.
98
+ */
99
+ export function gcloudAuthHeaders(tokens) {
100
+ return { Authorization: `Bearer ${tokens.accessToken}` };
101
+ }
102
+ //# sourceMappingURL=gcloud.js.map
@@ -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 type { OAuthCallbackServer } from "../../mcp/oauth-callback.js";
19
19
  import type { FetchFn } from "./device-flow.js";
@@ -21,12 +21,13 @@ import type { OAuthTokens, ProviderOAuth } from "./types.js";
21
21
  export type CallbackServerFn = (opts: {
22
22
  onCode: (code: string, state: string) => void;
23
23
  timeoutMs?: number;
24
+ host?: string;
24
25
  }) => Promise<OAuthCallbackServer>;
25
26
  export type OpenBrowserFn = (url: string) => void;
26
- export declare class GeminiOAuthProvider implements ProviderOAuth {
27
+ export declare class AgyOAuthProvider implements ProviderOAuth {
27
28
  readonly providerId: "google";
28
- private readonly clientId;
29
- private readonly clientSecret;
29
+ private clientId;
30
+ private clientSecret;
30
31
  private readonly fetchFn;
31
32
  private readonly callbackServerFn;
32
33
  private readonly openBrowserFn;
@@ -46,10 +47,36 @@ export declare class GeminiOAuthProvider implements ProviderOAuth {
46
47
  authHeaders(tokens: OAuthTokens): Record<string, string>;
47
48
  private _fetchUserEmail;
48
49
  }
49
- /** Default Gemini OAuth provider singleton (uses live Google endpoints). */
50
- export declare const geminiOAuth: GeminiOAuthProvider;
50
+ /** Back-compat for tests that still reference the old class name. */
51
+ export { AgyOAuthProvider as GeminiOAuthProvider };
52
+ /** Default Agy OAuth provider singleton (uses agy's Google client registration). */
53
+ export declare const agyOAuth: AgyOAuthProvider;
54
+ /** Back-compat alias (some code still references the old name). */
55
+ export declare const geminiOAuth: AgyOAuthProvider;
56
+ /**
57
+ * Load stored OAuth tokens for the Google provider using Agy's registration.
58
+ * - Auto-imports from ~/.gemini/oauth_creds.json (agy storage).
59
+ * - Refreshes using the agy client/secret when near expiry.
60
+ */
61
+ export declare function loadAgyTokensWithRefresh(oauthProvider?: AgyOAuthProvider): Promise<OAuthTokens | null>;
62
+ /** Back-compat alias for older call sites. */
63
+ export declare const loadGeminiTokensWithRefresh: typeof loadAgyTokensWithRefresh;
64
+ /**
65
+ * Load a custom OAuth token using user-provided GCP OAuth client credentials.
66
+ * Users register their own OAuth client in GCP Console → use these credentials
67
+ * to obtain tokens with the `generativelanguage` scope (not restricted to
68
+ * cloudcode-pa like the Agy client).
69
+ *
70
+ * Settings sources (in priority order):
71
+ * 1. settings.json → providers → google → oauthClientId / oauthClientSecret
72
+ * 2. MUONROI_GOOGLE_CLIENT_ID / MUONROI_GOOGLE_CLIENT_SECRET env vars
73
+ *
74
+ * Returns null when no custom client is configured.
75
+ */
76
+ export declare function loadCustomOAuthTokens(): Promise<OAuthTokens | null>;
51
77
  /**
52
- * Load stored OAuth tokens for Google/Gemini and auto-refresh if within the
53
- * expiry window. Returns null if no tokens are stored.
78
+ * Perform a full OAuth login with a custom client (Approach C).
79
+ * Users run `keys login google` with their own GCP OAuth client configured
80
+ * in settings → this opens the browser with the right scopes.
54
81
  */
55
- export declare function loadGeminiTokensWithRefresh(oauthProvider?: GeminiOAuthProvider): Promise<OAuthTokens | null>;
82
+ export declare function loginWithCustomClient(): Promise<OAuthTokens | null>;