muonroi-cli 1.8.3 → 1.8.5

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 (477) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +133 -122
  3. package/dist/packages/agent-harness-core/src/driver.d.ts +27 -1
  4. package/dist/packages/agent-harness-core/src/driver.js +46 -0
  5. package/dist/packages/agent-harness-core/src/event-tee.d.ts +48 -0
  6. package/dist/packages/agent-harness-core/src/event-tee.js +77 -0
  7. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +11 -0
  8. package/dist/packages/agent-harness-core/src/mcp-server.js +87 -15
  9. package/dist/packages/agent-harness-core/src/protocol.d.ts +66 -2
  10. package/dist/packages/agent-harness-core/src/protocol.js +15 -0
  11. package/dist/packages/agent-harness-core/src/visual-quality.d.ts +58 -0
  12. package/dist/packages/agent-harness-core/src/visual-quality.js +141 -0
  13. package/dist/packages/agent-harness-opentui/src/agent-mode.d.ts +6 -0
  14. package/dist/packages/agent-harness-opentui/src/agent-mode.js +14 -1
  15. package/dist/packages/agent-harness-opentui/src/input-bridge.d.ts +2 -10
  16. package/dist/packages/agent-harness-opentui/src/input-bridge.js +103 -16
  17. package/dist/packages/agent-harness-opentui/src/install.d.ts +8 -0
  18. package/dist/packages/agent-harness-opentui/src/install.js +10 -0
  19. package/dist/packages/agent-harness-opentui/src/semantic.js +12 -10
  20. package/dist/packages/agent-harness-opentui/src/visual-capture.d.ts +56 -0
  21. package/dist/packages/agent-harness-opentui/src/visual-capture.js +103 -0
  22. package/dist/src/agent-harness/mock-model.d.ts +28 -0
  23. package/dist/src/agent-harness/mock-model.js +63 -1
  24. package/dist/src/agent-harness/test-spawn.js +31 -0
  25. package/dist/src/cli/config/screen-providers.js +1 -1
  26. package/dist/src/cli/cost-forensics.d.ts +10 -0
  27. package/dist/src/cli/cost-forensics.js +30 -15
  28. package/dist/src/cli/keys-bundle.d.ts +1 -1
  29. package/dist/src/cli/keys-bundle.js +1 -1
  30. package/dist/src/cli/keys.d.ts +2 -2
  31. package/dist/src/cli/keys.js +19 -81
  32. package/dist/src/council/clarifier.d.ts +28 -2
  33. package/dist/src/council/clarifier.js +81 -15
  34. package/dist/src/council/context.js +49 -15
  35. package/dist/src/council/debate-checkpoint.d.ts +129 -0
  36. package/dist/src/council/debate-checkpoint.js +176 -0
  37. package/dist/src/council/debate-planner.js +51 -3
  38. package/dist/src/council/debate-summary.d.ts +25 -0
  39. package/dist/src/council/debate-summary.js +85 -0
  40. package/dist/src/council/debate.d.ts +169 -2
  41. package/dist/src/council/debate.js +1210 -134
  42. package/dist/src/council/index.d.ts +85 -1
  43. package/dist/src/council/index.js +634 -196
  44. package/dist/src/council/leader.d.ts +26 -0
  45. package/dist/src/council/leader.js +150 -9
  46. package/dist/src/council/llm.d.ts +32 -0
  47. package/dist/src/council/llm.js +231 -38
  48. package/dist/src/council/panel-select.d.ts +30 -0
  49. package/dist/src/council/panel-select.js +72 -0
  50. package/dist/src/council/planner.js +23 -0
  51. package/dist/src/council/preflight.d.ts +7 -0
  52. package/dist/src/council/preflight.js +14 -2
  53. package/dist/src/council/prompts.d.ts +30 -3
  54. package/dist/src/council/prompts.js +254 -84
  55. package/dist/src/council/stance-recall.d.ts +42 -0
  56. package/dist/src/council/stance-recall.js +57 -0
  57. package/dist/src/council/strip-think.d.ts +17 -0
  58. package/dist/src/council/strip-think.js +33 -0
  59. package/dist/src/council/types.d.ts +128 -0
  60. package/dist/src/ee/artifact-cache.d.ts +16 -0
  61. package/dist/src/ee/artifact-cache.js +32 -0
  62. package/dist/src/ee/auth.d.ts +1 -0
  63. package/dist/src/ee/auth.js +15 -2
  64. package/dist/src/ee/bridge.d.ts +10 -0
  65. package/dist/src/ee/bridge.js +58 -0
  66. package/dist/src/ee/client.js +81 -18
  67. package/dist/src/ee/export-transcripts.d.ts +1 -0
  68. package/dist/src/ee/export-transcripts.js +8 -10
  69. package/dist/src/ee/extract-session.js +29 -0
  70. package/dist/src/ee/extract-style.d.ts +58 -0
  71. package/dist/src/ee/extract-style.js +270 -0
  72. package/dist/src/ee/recall-ledger.d.ts +9 -0
  73. package/dist/src/ee/recall-ledger.js +3 -0
  74. package/dist/src/ee/scope.d.ts +1 -0
  75. package/dist/src/ee/scope.js +26 -1
  76. package/dist/src/ee/search.d.ts +7 -0
  77. package/dist/src/ee/search.js +24 -0
  78. package/dist/src/ee/transcript-emit.js +2 -0
  79. package/dist/src/ee/types.d.ts +22 -0
  80. package/dist/src/ee/who-am-i-brain.d.ts +35 -0
  81. package/dist/src/ee/who-am-i-brain.js +220 -0
  82. package/dist/src/ee/who-am-i.d.ts +10 -3
  83. package/dist/src/ee/who-am-i.js +12 -0
  84. package/dist/src/ee/workflow-event.d.ts +48 -0
  85. package/dist/src/ee/workflow-event.js +81 -0
  86. package/dist/src/flow/compaction/compress.d.ts +3 -3
  87. package/dist/src/flow/compaction/compress.js +45 -8
  88. package/dist/src/flow/compaction/extract.d.ts +4 -7
  89. package/dist/src/flow/compaction/extract.js +50 -10
  90. package/dist/src/flow/compaction/index.d.ts +13 -1
  91. package/dist/src/flow/compaction/index.js +70 -3
  92. package/dist/src/flow/compaction/input-guard.d.ts +24 -0
  93. package/dist/src/flow/compaction/input-guard.js +43 -0
  94. package/dist/src/flow/fold-planning.d.ts +36 -0
  95. package/dist/src/flow/fold-planning.js +83 -0
  96. package/dist/src/flow/hierarchy.d.ts +146 -0
  97. package/dist/src/flow/hierarchy.js +427 -0
  98. package/dist/src/flow/index.d.ts +1 -0
  99. package/dist/src/flow/index.js +2 -0
  100. package/dist/src/flow/run-artifacts.d.ts +102 -0
  101. package/dist/src/flow/run-artifacts.js +208 -0
  102. package/dist/src/generated/version.d.ts +1 -1
  103. package/dist/src/generated/version.js +1 -1
  104. package/dist/src/gsd/assessment-schema.d.ts +44 -0
  105. package/dist/src/gsd/assessment-schema.js +134 -0
  106. package/dist/src/gsd/capability-registry.d.ts +45 -0
  107. package/dist/src/gsd/capability-registry.js +337 -0
  108. package/dist/src/gsd/complexity-assessor.d.ts +39 -0
  109. package/dist/src/gsd/complexity-assessor.js +152 -0
  110. package/dist/src/gsd/config-bridge.d.ts +7 -0
  111. package/dist/src/gsd/config-bridge.js +114 -0
  112. package/dist/src/gsd/config-loader.d.ts +27 -0
  113. package/dist/src/gsd/config-loader.js +50 -0
  114. package/dist/src/gsd/council-context.d.ts +44 -0
  115. package/dist/src/gsd/council-context.js +114 -0
  116. package/dist/src/gsd/ee-closure.d.ts +28 -0
  117. package/dist/src/gsd/ee-closure.js +49 -0
  118. package/dist/src/gsd/flags.d.ts +55 -0
  119. package/dist/src/gsd/flags.js +83 -0
  120. package/dist/src/gsd/gsd-dispatch.d.ts +58 -0
  121. package/dist/src/gsd/gsd-dispatch.js +131 -0
  122. package/dist/src/gsd/gsd-runtime.d.ts +22 -0
  123. package/dist/src/gsd/gsd-runtime.js +37 -0
  124. package/dist/src/gsd/host-adapter.d.ts +11 -0
  125. package/dist/src/gsd/host-adapter.js +29 -0
  126. package/dist/src/gsd/index.d.ts +24 -1
  127. package/dist/src/gsd/index.js +27 -0
  128. package/dist/src/gsd/loop-host-contract.d.ts +21 -0
  129. package/dist/src/gsd/loop-host-contract.js +39 -0
  130. package/dist/src/gsd/loop-host.d.ts +69 -0
  131. package/dist/src/gsd/loop-host.js +245 -0
  132. package/dist/src/gsd/loop-resolver.d.ts +36 -0
  133. package/dist/src/gsd/loop-resolver.js +79 -0
  134. package/dist/src/gsd/model-tier.d.ts +13 -0
  135. package/dist/src/gsd/model-tier.js +45 -0
  136. package/dist/src/gsd/mutation-gate.d.ts +16 -0
  137. package/dist/src/gsd/mutation-gate.js +41 -0
  138. package/dist/src/gsd/native-roadmap.d.ts +89 -0
  139. package/dist/src/gsd/native-roadmap.js +343 -0
  140. package/dist/src/gsd/native-state.d.ts +47 -0
  141. package/dist/src/gsd/native-state.js +220 -0
  142. package/dist/src/gsd/paths.d.ts +23 -0
  143. package/dist/src/gsd/paths.js +66 -0
  144. package/dist/src/gsd/phase-dag.d.ts +12 -0
  145. package/dist/src/gsd/phase-dag.js +94 -0
  146. package/dist/src/gsd/phase-sync.d.ts +42 -0
  147. package/dist/src/gsd/phase-sync.js +321 -0
  148. package/dist/src/gsd/pil-gate-context.d.ts +13 -0
  149. package/dist/src/gsd/pil-gate-context.js +64 -0
  150. package/dist/src/gsd/pil-gate-critic.d.ts +19 -0
  151. package/dist/src/gsd/pil-gate-critic.js +74 -0
  152. package/dist/src/gsd/plan-council-prompts.d.ts +25 -0
  153. package/dist/src/gsd/plan-council-prompts.js +79 -0
  154. package/dist/src/gsd/plan-council.d.ts +44 -0
  155. package/dist/src/gsd/plan-council.js +251 -0
  156. package/dist/src/gsd/plan-gate-vocabulary.d.ts +40 -0
  157. package/dist/src/gsd/plan-gate-vocabulary.js +64 -0
  158. package/dist/src/gsd/product-workspace.d.ts +13 -0
  159. package/dist/src/gsd/product-workspace.js +124 -0
  160. package/dist/src/gsd/ship-bridge.d.ts +25 -0
  161. package/dist/src/gsd/ship-bridge.js +65 -0
  162. package/dist/src/gsd/state-document.d.ts +40 -0
  163. package/dist/src/gsd/state-document.js +163 -0
  164. package/dist/src/gsd/verdict-schema.d.ts +39 -0
  165. package/dist/src/gsd/verdict-schema.js +144 -0
  166. package/dist/src/gsd/verify-context.d.ts +22 -0
  167. package/dist/src/gsd/verify-context.js +27 -0
  168. package/dist/src/gsd/verify-council-prompts.d.ts +19 -0
  169. package/dist/src/gsd/verify-council-prompts.js +85 -0
  170. package/dist/src/gsd/verify-council.d.ts +25 -0
  171. package/dist/src/gsd/verify-council.js +119 -0
  172. package/dist/src/gsd/verify-gate-vocabulary.d.ts +25 -0
  173. package/dist/src/gsd/verify-gate-vocabulary.js +46 -0
  174. package/dist/src/gsd/workflow-engine.d.ts +60 -0
  175. package/dist/src/gsd/workflow-engine.js +207 -0
  176. package/dist/src/gsd/workflow-tools.d.ts +13 -0
  177. package/dist/src/gsd/workflow-tools.js +277 -0
  178. package/dist/src/hooks/index.js +1 -1
  179. package/dist/src/index.js +44 -11
  180. package/dist/src/maintain/pr-builder.js +23 -13
  181. package/dist/src/mcp/auto-setup.js +57 -32
  182. package/dist/src/mcp/client-pool.js +1 -1
  183. package/dist/src/mcp/ee-tools.js +1 -0
  184. package/dist/src/mcp/oauth-callback.js +2 -2
  185. package/dist/src/mcp/research-onboarding.js +8 -7
  186. package/dist/src/mcp/runtime.js +34 -2
  187. package/dist/src/mcp/setup-guide-text.d.ts +1 -1
  188. package/dist/src/mcp/setup-guide-text.js +77 -76
  189. package/dist/src/models/catalog-client.d.ts +87 -0
  190. package/dist/src/models/catalog-client.js +105 -38
  191. package/dist/src/models/catalog.json +528 -265
  192. package/dist/src/models/registry.d.ts +22 -7
  193. package/dist/src/models/registry.js +73 -10
  194. package/dist/src/ops/doctor.js +8 -8
  195. package/dist/src/orchestrator/auto-commit.js +1 -1
  196. package/dist/src/orchestrator/batch-turn-runner.js +2 -2
  197. package/dist/src/orchestrator/cache-prefix.d.ts +67 -0
  198. package/dist/src/orchestrator/cache-prefix.js +83 -0
  199. package/dist/src/orchestrator/compact-request.d.ts +32 -0
  200. package/dist/src/orchestrator/compact-request.js +41 -0
  201. package/dist/src/orchestrator/compaction.d.ts +10 -0
  202. package/dist/src/orchestrator/compaction.js +27 -7
  203. package/dist/src/orchestrator/council-manager.d.ts +12 -3
  204. package/dist/src/orchestrator/council-manager.js +65 -24
  205. package/dist/src/orchestrator/cross-turn-dedup.d.ts +6 -0
  206. package/dist/src/orchestrator/cross-turn-dedup.js +22 -1
  207. package/dist/src/orchestrator/error-utils.d.ts +29 -0
  208. package/dist/src/orchestrator/error-utils.js +132 -24
  209. package/dist/src/orchestrator/grounding-check.js +39 -1
  210. package/dist/src/orchestrator/message-processor.js +242 -33
  211. package/dist/src/orchestrator/orchestrator.d.ts +39 -3
  212. package/dist/src/orchestrator/orchestrator.js +651 -102
  213. package/dist/src/orchestrator/preprocessor.js +1 -1
  214. package/dist/src/orchestrator/proactive-compact-detector.d.ts +26 -0
  215. package/dist/src/orchestrator/proactive-compact-detector.js +36 -0
  216. package/dist/src/orchestrator/prompts.js +159 -159
  217. package/dist/src/orchestrator/reactive-delegation.d.ts +39 -0
  218. package/dist/src/orchestrator/reactive-delegation.js +59 -0
  219. package/dist/src/orchestrator/retry-classifier.d.ts +2 -1
  220. package/dist/src/orchestrator/retry-classifier.js +46 -2
  221. package/dist/src/orchestrator/safety-intercept.d.ts +45 -0
  222. package/dist/src/orchestrator/safety-intercept.js +55 -0
  223. package/dist/src/orchestrator/scope-reminder.js +1 -1
  224. package/dist/src/orchestrator/session-experience.d.ts +2 -1
  225. package/dist/src/orchestrator/session-experience.js +2 -1
  226. package/dist/src/orchestrator/should-run-gate.d.ts +5 -0
  227. package/dist/src/orchestrator/should-run-gate.js +18 -0
  228. package/dist/src/orchestrator/stall-watchdog.d.ts +24 -3
  229. package/dist/src/orchestrator/stall-watchdog.js +47 -13
  230. package/dist/src/orchestrator/stream-runner.js +62 -29
  231. package/dist/src/orchestrator/sub-agent-cap.d.ts +13 -1
  232. package/dist/src/orchestrator/sub-agent-cap.js +16 -1
  233. package/dist/src/orchestrator/sub-agent-model-tier.d.ts +13 -1
  234. package/dist/src/orchestrator/sub-agent-model-tier.js +16 -2
  235. package/dist/src/orchestrator/subagent-compactor.d.ts +53 -1
  236. package/dist/src/orchestrator/subagent-compactor.js +126 -15
  237. package/dist/src/orchestrator/tool-engine.d.ts +26 -0
  238. package/dist/src/orchestrator/tool-engine.js +669 -56
  239. package/dist/src/orchestrator/tool-limit-auto-recover.d.ts +22 -0
  240. package/dist/src/orchestrator/tool-limit-auto-recover.js +30 -0
  241. package/dist/src/orchestrator/turn-runner-deps.d.ts +19 -0
  242. package/dist/src/orchestrator/turn-watchdog.d.ts +37 -0
  243. package/dist/src/orchestrator/turn-watchdog.js +55 -0
  244. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  245. package/dist/src/pil/agent-operating-contract.js +1 -1
  246. package/dist/src/pil/cheap-model-playbook.d.ts +1 -1
  247. package/dist/src/pil/cheap-model-playbook.js +5 -1
  248. package/dist/src/pil/cheap-model-workbooks.d.ts +1 -1
  249. package/dist/src/pil/cheap-model-workbooks.js +1 -1
  250. package/dist/src/pil/discovery-types.d.ts +1 -0
  251. package/dist/src/pil/discovery.js +16 -11
  252. package/dist/src/pil/layer1-intent.d.ts +18 -6
  253. package/dist/src/pil/layer1-intent.js +66 -757
  254. package/dist/src/pil/layer15-context-scan.js +15 -1
  255. package/dist/src/pil/layer2_5-ponytail.js +8 -8
  256. package/dist/src/pil/layer3-ee-injection.js +23 -8
  257. package/dist/src/pil/layer4-gsd.js +69 -16
  258. package/dist/src/pil/layer5-context.js +7 -3
  259. package/dist/src/pil/layer6-output.d.ts +23 -0
  260. package/dist/src/pil/layer6-output.js +5 -1
  261. package/dist/src/pil/llm-classify.d.ts +33 -2
  262. package/dist/src/pil/llm-classify.js +123 -131
  263. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  264. package/dist/src/pil/native-capabilities-workbook.js +1 -0
  265. package/dist/src/pil/pipeline.js +34 -2
  266. package/dist/src/pil/response-tools.js +5 -3
  267. package/dist/src/pil/schema.d.ts +1 -0
  268. package/dist/src/pil/schema.js +2 -0
  269. package/dist/src/pil/types.d.ts +18 -0
  270. package/dist/src/playbook/directives.d.ts +4 -0
  271. package/dist/src/playbook/directives.js +17 -5
  272. package/dist/src/product-loop/backlog-builder.d.ts +14 -1
  273. package/dist/src/product-loop/backlog-builder.js +30 -6
  274. package/dist/src/product-loop/discovery-context-format.js +3 -1
  275. package/dist/src/product-loop/discovery-ecosystem.js +4 -1
  276. package/dist/src/product-loop/discovery-interview.js +32 -3
  277. package/dist/src/product-loop/discovery-schema.js +5 -1
  278. package/dist/src/product-loop/done-gate.js +3 -3
  279. package/dist/src/product-loop/ideal-trace.d.ts +7 -0
  280. package/dist/src/product-loop/ideal-trace.js +64 -0
  281. package/dist/src/product-loop/index.d.ts +13 -1
  282. package/dist/src/product-loop/index.js +333 -52
  283. package/dist/src/product-loop/loop-driver.d.ts +7 -0
  284. package/dist/src/product-loop/loop-driver.js +327 -116
  285. package/dist/src/product-loop/phase-plan.d.ts +5 -0
  286. package/dist/src/product-loop/phase-plan.js +39 -2
  287. package/dist/src/product-loop/phase-runner.js +9 -1
  288. package/dist/src/product-loop/progress-snapshot.js +4 -4
  289. package/dist/src/product-loop/sprint-runner.d.ts +111 -0
  290. package/dist/src/product-loop/sprint-runner.js +559 -16
  291. package/dist/src/product-loop/types.d.ts +36 -5
  292. package/dist/src/providers/adapter.d.ts +1 -1
  293. package/dist/src/providers/adapter.js +3 -4
  294. package/dist/src/providers/auth/browser-flow.d.ts +1 -1
  295. package/dist/src/providers/auth/browser-flow.js +1 -1
  296. package/dist/src/providers/auth/openai-oauth.js +1 -1
  297. package/dist/src/providers/auth/registry.js +0 -34
  298. package/dist/src/providers/auth/token-store.js +4 -1
  299. package/dist/src/providers/auth/types.d.ts +1 -1
  300. package/dist/src/providers/auth/types.js +1 -1
  301. package/dist/src/providers/capabilities.d.ts +24 -5
  302. package/dist/src/providers/capabilities.js +42 -24
  303. package/dist/src/providers/endpoints.d.ts +2 -2
  304. package/dist/src/providers/endpoints.js +11 -10
  305. package/dist/src/providers/keychain.d.ts +1 -1
  306. package/dist/src/providers/keychain.js +7 -9
  307. package/dist/src/providers/mcp-vision-bridge.js +82 -172
  308. package/dist/src/providers/openai-compatible.js +8 -1
  309. package/dist/src/providers/pricing.d.ts +2 -2
  310. package/dist/src/providers/pricing.js +3 -13
  311. package/dist/src/providers/runtime.d.ts +27 -2
  312. package/dist/src/providers/runtime.js +78 -15
  313. package/dist/src/providers/strategies/base.strategy.d.ts +16 -0
  314. package/dist/src/providers/strategies/base.strategy.js +24 -1
  315. package/dist/src/providers/strategies/{siliconflow.strategy.d.ts → opencode-go.strategy.d.ts} +3 -3
  316. package/dist/src/providers/strategies/opencode-go.strategy.js +83 -0
  317. package/dist/src/providers/strategies/registry.js +4 -4
  318. package/dist/src/providers/strategies/thinking-mode.d.ts +109 -0
  319. package/dist/src/providers/strategies/thinking-mode.js +280 -1
  320. package/dist/src/providers/strategies/zai.strategy.d.ts +14 -0
  321. package/dist/src/providers/strategies/zai.strategy.js +44 -0
  322. package/dist/src/providers/types.d.ts +5 -6
  323. package/dist/src/providers/types.js +2 -2
  324. package/dist/src/providers/vision-backend.d.ts +47 -0
  325. package/dist/src/providers/vision-backend.js +258 -0
  326. package/dist/src/providers/vision-proxy.d.ts +22 -9
  327. package/dist/src/providers/vision-proxy.js +63 -132
  328. package/dist/src/providers/wire-debug.js +95 -0
  329. package/dist/src/reporter/index.js +1 -1
  330. package/dist/src/router/decide.d.ts +13 -0
  331. package/dist/src/router/decide.js +138 -36
  332. package/dist/src/router/peak-hour.d.ts +38 -0
  333. package/dist/src/router/peak-hour.js +107 -0
  334. package/dist/src/router/step-router.js +3 -2
  335. package/dist/src/router/warm.js +4 -5
  336. package/dist/src/scaffold/bb-ecosystem-apply.js +47 -47
  337. package/dist/src/scaffold/bb-quality-gate.js +5 -5
  338. package/dist/src/scaffold/continuation-prompt.d.ts +11 -0
  339. package/dist/src/scaffold/continuation-prompt.js +86 -60
  340. package/dist/src/scaffold/init-new.js +453 -453
  341. package/dist/src/scaffold/point-to-existing.d.ts +21 -0
  342. package/dist/src/scaffold/point-to-existing.js +25 -0
  343. package/dist/src/self-qa/agentic-loop.js +22 -22
  344. package/dist/src/{ui/state → state}/active-run.d.ts +19 -0
  345. package/dist/src/{ui/state → state}/active-run.js +21 -0
  346. package/dist/src/{ui/status-bar/store.d.ts → state/status-bar-store.d.ts} +4 -1
  347. package/dist/src/{ui/status-bar/store.js → state/status-bar-store.js} +12 -9
  348. package/dist/src/state/turn-trace.d.ts +43 -0
  349. package/dist/src/state/turn-trace.js +32 -0
  350. package/dist/src/storage/db.js +2 -1
  351. package/dist/src/storage/index.d.ts +1 -1
  352. package/dist/src/storage/index.js +1 -1
  353. package/dist/src/storage/interaction-log.d.ts +1 -1
  354. package/dist/src/storage/interaction-log.js +5 -5
  355. package/dist/src/storage/migrations.js +196 -126
  356. package/dist/src/storage/session-experience-store.js +4 -4
  357. package/dist/src/storage/sessions.d.ts +28 -10
  358. package/dist/src/storage/sessions.js +112 -55
  359. package/dist/src/storage/transcript-view.js +1 -1
  360. package/dist/src/storage/transcript.d.ts +51 -0
  361. package/dist/src/storage/transcript.js +383 -112
  362. package/dist/src/storage/usage.js +14 -14
  363. package/dist/src/storage/workspaces.js +12 -12
  364. package/dist/src/tools/file.d.ts +15 -0
  365. package/dist/src/tools/file.js +32 -0
  366. package/dist/src/tools/native-tools.js +5 -0
  367. package/dist/src/tools/registry.d.ts +3 -0
  368. package/dist/src/tools/registry.js +460 -22
  369. package/dist/src/tools/research.d.ts +29 -0
  370. package/dist/src/tools/research.js +233 -0
  371. package/dist/src/types/index.d.ts +118 -3
  372. package/dist/src/ui/app.js +0 -0
  373. package/dist/src/ui/cards/product-status-card.js +1 -1
  374. package/dist/src/ui/components/bubble-body-guard.d.ts +27 -0
  375. package/dist/src/ui/components/bubble-body-guard.js +50 -0
  376. package/dist/src/ui/components/context-rail.d.ts +26 -0
  377. package/dist/src/ui/components/context-rail.js +33 -0
  378. package/dist/src/ui/components/council-conclusion-card.d.ts +73 -0
  379. package/dist/src/ui/components/council-conclusion-card.js +420 -0
  380. package/dist/src/ui/components/council-debate-pill.d.ts +36 -0
  381. package/dist/src/ui/components/council-debate-pill.js +34 -0
  382. package/dist/src/ui/components/council-info-card.js +2 -2
  383. package/dist/src/ui/components/council-leader-bubble.d.ts +10 -3
  384. package/dist/src/ui/components/council-leader-bubble.js +21 -11
  385. package/dist/src/ui/components/council-message-bubble.d.ts +15 -2
  386. package/dist/src/ui/components/council-message-bubble.js +16 -15
  387. package/dist/src/ui/components/council-phase-timeline.d.ts +9 -1
  388. package/dist/src/ui/components/council-phase-timeline.js +49 -15
  389. package/dist/src/ui/components/council-placeholder-bubble.d.ts +16 -9
  390. package/dist/src/ui/components/council-placeholder-bubble.js +32 -29
  391. package/dist/src/ui/components/council-question-card.js +12 -12
  392. package/dist/src/ui/components/council-rail-rounds.d.ts +26 -0
  393. package/dist/src/ui/components/council-rail-rounds.js +57 -0
  394. package/dist/src/ui/components/council-round-group.d.ts +38 -0
  395. package/dist/src/ui/components/council-round-group.js +88 -0
  396. package/dist/src/ui/components/council-status-list.d.ts +3 -1
  397. package/dist/src/ui/components/council-status-list.js +36 -24
  398. package/dist/src/ui/components/council-synthesis-banner.d.ts +7 -2
  399. package/dist/src/ui/components/council-synthesis-banner.js +20 -5
  400. package/dist/src/ui/components/halt-recovery-card.js +9 -5
  401. package/dist/src/ui/components/jump-to-latest-pill.d.ts +11 -0
  402. package/dist/src/ui/components/jump-to-latest-pill.js +14 -0
  403. package/dist/src/ui/components/prompt-box.js +18 -16
  404. package/dist/src/ui/components/session-tree-card.d.ts +14 -0
  405. package/dist/src/ui/components/session-tree-card.js +46 -0
  406. package/dist/src/ui/components/slash-inline-menu.d.ts +12 -0
  407. package/dist/src/ui/components/slash-inline-menu.js +26 -5
  408. package/dist/src/ui/components/task-list-panel.d.ts +14 -1
  409. package/dist/src/ui/components/task-list-panel.js +22 -2
  410. package/dist/src/ui/containers/modals-layer.d.ts +2 -1
  411. package/dist/src/ui/hooks/use-session-picker.d.ts +3 -3
  412. package/dist/src/ui/mcp-modal.js +2 -4
  413. package/dist/src/ui/modals/api-key-modal.js +1 -1
  414. package/dist/src/ui/modals/connect-modal.js +4 -3
  415. package/dist/src/ui/modals/session-picker-modal.d.ts +2 -2
  416. package/dist/src/ui/modals/session-picker-modal.js +3 -5
  417. package/dist/src/ui/picker-providers.d.ts +1 -1
  418. package/dist/src/ui/picker-providers.js +1 -1
  419. package/dist/src/ui/primitives/index.d.ts +1 -0
  420. package/dist/src/ui/primitives/index.js +2 -0
  421. package/dist/src/ui/primitives/semantic-primitives.d.ts +76 -0
  422. package/dist/src/ui/primitives/semantic-primitives.js +81 -0
  423. package/dist/src/ui/slash/compact.js +5 -7
  424. package/dist/src/ui/slash/cost.js +1 -1
  425. package/dist/src/ui/slash/council-inspect.js +4 -4
  426. package/dist/src/ui/slash/council.js +19 -1
  427. package/dist/src/ui/slash/debug.d.ts +3 -31
  428. package/dist/src/ui/slash/debug.js +9 -20
  429. package/dist/src/ui/slash/ideal.d.ts +6 -2
  430. package/dist/src/ui/slash/ideal.js +97 -7
  431. package/dist/src/ui/slash/menu-items.d.ts +7 -0
  432. package/dist/src/ui/slash/menu-items.js +12 -18
  433. package/dist/src/ui/slash/registry.d.ts +2 -0
  434. package/dist/src/ui/slash/registry.js +4 -0
  435. package/dist/src/ui/status-bar/cache-hit.d.ts +6 -0
  436. package/dist/src/ui/status-bar/cache-hit.js +9 -0
  437. package/dist/src/ui/status-bar/index.d.ts +1 -1
  438. package/dist/src/ui/status-bar/index.js +7 -3
  439. package/dist/src/ui/status-bar/usd-meter.d.ts +5 -4
  440. package/dist/src/ui/status-bar/usd-meter.js +6 -4
  441. package/dist/src/ui/theme.d.ts +1 -0
  442. package/dist/src/ui/theme.js +2 -0
  443. package/dist/src/ui/types.d.ts +7 -0
  444. package/dist/src/ui/use-app-logic.js +0 -0
  445. package/dist/src/ui/utils/format.d.ts +14 -0
  446. package/dist/src/ui/utils/format.js +23 -3
  447. package/dist/src/usage/downgrade.js +2 -2
  448. package/dist/src/usage/product-ledger.js +2 -2
  449. package/dist/src/utils/clipboard-image.js +23 -23
  450. package/dist/src/utils/install-manager.js +14 -11
  451. package/dist/src/utils/logger.js +2 -2
  452. package/dist/src/utils/permission-mode.js +5 -3
  453. package/dist/src/utils/redactor.js +1 -1
  454. package/dist/src/utils/settings.d.ts +153 -5
  455. package/dist/src/utils/settings.js +233 -29
  456. package/dist/src/utils/side-question.js +2 -2
  457. package/dist/src/utils/skills.js +3 -3
  458. package/dist/src/utils/visible-retry.d.ts +11 -0
  459. package/dist/src/utils/visible-retry.js +10 -1
  460. package/dist/src/verify/entrypoint.d.ts +1 -1
  461. package/dist/src/verify/entrypoint.js +1 -1
  462. package/dist/src/verify/recipes.d.ts +13 -0
  463. package/dist/src/verify/recipes.js +15 -0
  464. package/package.json +135 -132
  465. package/dist/src/providers/auth/gcloud.d.ts +0 -28
  466. package/dist/src/providers/auth/gcloud.js +0 -102
  467. package/dist/src/providers/auth/gemini-oauth.d.ts +0 -82
  468. package/dist/src/providers/auth/gemini-oauth.js +0 -472
  469. package/dist/src/providers/gemini.d.ts +0 -11
  470. package/dist/src/providers/gemini.js +0 -45
  471. package/dist/src/providers/siliconflow-sse-repair.d.ts +0 -58
  472. package/dist/src/providers/siliconflow-sse-repair.js +0 -177
  473. package/dist/src/providers/strategies/google.strategy.d.ts +0 -22
  474. package/dist/src/providers/strategies/google.strategy.js +0 -174
  475. package/dist/src/providers/strategies/siliconflow.strategy.js +0 -29
  476. package/dist/src/ui/containers/chat-feed.d.ts +0 -40
  477. package/dist/src/ui/containers/chat-feed.js +0 -66
@@ -0,0 +1,258 @@
1
+ import { getModelInfo, getVisionProxyRouting, MODELS, SWITCH_PROVIDER_ORDER } from "../models/registry.js";
2
+ import { apiBaseFor } from "./endpoints.js";
3
+ import { loadKeyForProvider } from "./keychain.js";
4
+ const REQUEST_TIMEOUT_MS = 90_000;
5
+ const DEFAULT_VISION_PROXY = {
6
+ default: { provider: "zai", model_id: "glm-4.6v-flash" },
7
+ ocr: { provider: "zai", model_id: "glm-4.6v-flash" },
8
+ design: { provider: "zai", model_id: "glm-5.2" },
9
+ fallback_chain: [
10
+ { provider: "xai", model_id: "grok-4.5" },
11
+ { provider: "zai", model_id: "glm-5.2" },
12
+ ],
13
+ };
14
+ const OCR_INTENT_RE = /\b(ocr|transcribe|read\s+(all\s+)?text|extract\s+text|text\s+in\s+(the\s+)?image|copy\s+text|what\s+(does|do)\s+it\s+say)\b/i;
15
+ export function looksLikeOcrIntent(text) {
16
+ return OCR_INTENT_RE.test(text);
17
+ }
18
+ export function resolveVisionChain(kind) {
19
+ const routing = getVisionProxyRouting() ?? DEFAULT_VISION_PROXY;
20
+ const chain = [];
21
+ const primary = routing[kind] ?? routing.default;
22
+ if (primary)
23
+ chain.push(primary);
24
+ for (const slot of routing.fallback_chain ?? []) {
25
+ if (!chain.some((s) => s.provider === slot.provider && s.model_id === slot.model_id)) {
26
+ chain.push(slot);
27
+ }
28
+ }
29
+ if (chain.length === 0 && routing.default)
30
+ return [routing.default];
31
+ return chain;
32
+ }
33
+ async function slotHasAvailableKey(slot) {
34
+ try {
35
+ await loadKeyForProvider(slot.provider);
36
+ return true;
37
+ }
38
+ catch {
39
+ return false;
40
+ }
41
+ }
42
+ /** Vision-proxy chain filtered to providers that currently have API keys. */
43
+ export async function resolveAvailableVisionChain(kind = "default") {
44
+ const available = [];
45
+ for (const slot of resolveVisionChain(kind)) {
46
+ if (await slotHasAvailableKey(slot))
47
+ available.push(slot);
48
+ }
49
+ return available;
50
+ }
51
+ export async function isVisionBackendAvailable(kind = "default") {
52
+ return (await resolveAvailableVisionChain(kind)).length > 0;
53
+ }
54
+ async function tryNativeVisionModel(modelId, source, excludeModelId) {
55
+ if (excludeModelId && modelId === excludeModelId)
56
+ return null;
57
+ const info = getModelInfo(modelId);
58
+ if (!info?.supportsVision)
59
+ return null;
60
+ const provider = info.provider;
61
+ const { isModelDisabled, isProviderDisabled } = await import("../utils/settings.js");
62
+ if (isProviderDisabled(provider) || isModelDisabled(modelId))
63
+ return null;
64
+ if (!(await slotHasAvailableKey({ provider, model_id: modelId })))
65
+ return null;
66
+ return { modelId, provider, source };
67
+ }
68
+ /**
69
+ * When vision-proxy backends have no keys, pick a catalog vision model (with key)
70
+ * so images can be sent natively instead of failing on a text-only primary.
71
+ */
72
+ export async function findNativeVisionFallback(opts) {
73
+ const exclude = opts?.excludeModelId;
74
+ const seen = new Set();
75
+ for (const kind of ["default", "ocr", "design"]) {
76
+ for (const slot of resolveVisionChain(kind)) {
77
+ if (seen.has(slot.model_id))
78
+ continue;
79
+ seen.add(slot.model_id);
80
+ const hit = await tryNativeVisionModel(slot.model_id, "vision_proxy_slot", exclude);
81
+ if (hit)
82
+ return hit;
83
+ }
84
+ }
85
+ const visionByProvider = new Map();
86
+ for (const m of MODELS) {
87
+ if (!m.supportsVision || !m.provider)
88
+ continue;
89
+ const list = visionByProvider.get(m.provider) ?? [];
90
+ list.push(m);
91
+ visionByProvider.set(m.provider, list);
92
+ }
93
+ for (const provider of SWITCH_PROVIDER_ORDER) {
94
+ for (const m of visionByProvider.get(provider) ?? []) {
95
+ if (seen.has(m.id))
96
+ continue;
97
+ seen.add(m.id);
98
+ const hit = await tryNativeVisionModel(m.id, "catalog_vision", exclude);
99
+ if (hit)
100
+ return hit;
101
+ }
102
+ }
103
+ for (const m of MODELS) {
104
+ if (!m.supportsVision)
105
+ continue;
106
+ if (seen.has(m.id))
107
+ continue;
108
+ const hit = await tryNativeVisionModel(m.id, "catalog_vision", exclude);
109
+ if (hit)
110
+ return hit;
111
+ }
112
+ return null;
113
+ }
114
+ export async function collectVisionUnavailableReasons(kind = "default") {
115
+ const reasons = [];
116
+ for (const slot of resolveVisionChain(kind)) {
117
+ if (await slotHasAvailableKey(slot)) {
118
+ reasons.push(`${slot.model_id}@${slot.provider}: API key present but backend unreachable`);
119
+ }
120
+ else {
121
+ reasons.push(`${slot.model_id}@${slot.provider}: no API key`);
122
+ }
123
+ }
124
+ reasons.push("no other vision-capable catalog model has a configured API key");
125
+ return reasons;
126
+ }
127
+ export async function callVisionBackend(chain, content, signal, responseFormat) {
128
+ const failureReasons = [];
129
+ if (chain.length === 0) {
130
+ return { ok: false, reason: "no vision backend available — configure ZAI_API_KEY or XAI_API_KEY" };
131
+ }
132
+ for (const slot of chain) {
133
+ const provider = slot.provider;
134
+ let apiKey;
135
+ try {
136
+ apiKey = await loadKeyForProvider(provider);
137
+ }
138
+ catch (err) {
139
+ const msg = err instanceof Error ? err.message : String(err);
140
+ failureReasons.push(`${slot.model_id}@${provider}: no API key (${msg})`);
141
+ continue;
142
+ }
143
+ const base = apiBaseFor(provider);
144
+ const result = await callVisionModelAt(base, slot.model_id, content, apiKey, signal, responseFormat);
145
+ if (result.ok) {
146
+ return { ok: true, text: result.text, model: slot.model_id, provider };
147
+ }
148
+ failureReasons.push(`${slot.model_id}@${provider}: ${result.reason}`);
149
+ console.warn(`[vision-backend] ${slot.model_id}@${provider} failed (${result.reason}), trying next...`);
150
+ }
151
+ return { ok: false, reason: failureReasons.join(" | ") || "no vision backend configured" };
152
+ }
153
+ async function callVisionModelAt(baseURL, model, content, apiKey, signal, responseFormat) {
154
+ const controller = new AbortController();
155
+ let timedOut = false;
156
+ const timeout = setTimeout(() => {
157
+ timedOut = true;
158
+ controller.abort();
159
+ }, REQUEST_TIMEOUT_MS);
160
+ if (signal) {
161
+ signal.addEventListener("abort", () => controller.abort(), { once: true });
162
+ }
163
+ const url = `${baseURL.replace(/\/$/, "")}/chat/completions`;
164
+ let res;
165
+ try {
166
+ res = await fetch(url, {
167
+ method: "POST",
168
+ headers: {
169
+ "Content-Type": "application/json",
170
+ Authorization: `Bearer ${apiKey}`,
171
+ },
172
+ body: JSON.stringify({
173
+ model,
174
+ messages: [{ role: "user", content }],
175
+ max_tokens: 3072,
176
+ temperature: 0.1,
177
+ ...(responseFormat ? { response_format: responseFormat } : {}),
178
+ }),
179
+ signal: controller.signal,
180
+ });
181
+ }
182
+ catch (err) {
183
+ clearTimeout(timeout);
184
+ if (timedOut)
185
+ return { ok: false, reason: `timeout after ${REQUEST_TIMEOUT_MS}ms` };
186
+ if (signal?.aborted)
187
+ throw err;
188
+ return { ok: false, reason: `network error: ${err instanceof Error ? err.message : String(err)}` };
189
+ }
190
+ clearTimeout(timeout);
191
+ if (!res.ok) {
192
+ const errText = await res.text().catch(() => "");
193
+ return { ok: false, reason: `HTTP ${res.status} ${errText.slice(0, 200)}` };
194
+ }
195
+ const data = (await res.json().catch(() => null));
196
+ const text = data?.choices?.[0]?.message?.content;
197
+ if (!text)
198
+ return { ok: false, reason: "empty response body" };
199
+ return { ok: true, text };
200
+ }
201
+ /** Ask the vision model to write as direct sight for the primary (text-only) agent. */
202
+ export function wrapAnalyzerInstructions(userPrompt, kind) {
203
+ const style = kind === "design"
204
+ ? "Output ONLY valid JSON matching the requested schema. No markdown fences."
205
+ : "Write in clear markdown. Use first-person present tense ('I see…', 'The screenshot shows…').";
206
+ return [
207
+ userPrompt,
208
+ "",
209
+ "IMPORTANT — output style for the primary coding agent:",
210
+ "- Write as a DIRECT visual observation. The reader will treat your words as its own native sight.",
211
+ "- Do NOT mention vision proxy, backend models, or that you are a separate model.",
212
+ `- ${style}`,
213
+ "- If something is illegible, cropped, or ambiguous, say exactly what is unclear and what would help (closer crop, higher resolution, different angle).",
214
+ ].join("\n");
215
+ }
216
+ /**
217
+ * Envelope injected into text-only model context — reads as native vision, not third-party analysis.
218
+ */
219
+ export function formatNativeVisionObservation(observation, opts) {
220
+ const subject = opts.imageCount > 1 ? `these ${opts.imageCount} images` : "this image";
221
+ const cacheHint = opts.cachedIds && opts.cachedIds.length > 0
222
+ ? `- Cached as ${opts.cachedIds.join(", ")} — use ask_vision_proxy with a specific question to inspect a detail`
223
+ : "";
224
+ return [
225
+ "<vision-observation>",
226
+ `You are viewing ${subject}. Treat everything below as your direct visual observation — reason and respond as if you saw it yourself.`,
227
+ "",
228
+ observation.trim(),
229
+ "",
230
+ "If any detail is unclear, cropped, or you need another angle:",
231
+ "- ask_vision_proxy with a precise question (region, color, text, UI element)",
232
+ "- analyze_image on the file path to re-inspect or compare a fresh screenshot",
233
+ "- ask the user to share another image or clarify what to focus on",
234
+ cacheHint,
235
+ "</vision-observation>",
236
+ ]
237
+ .filter((line) => line !== "")
238
+ .join("\n");
239
+ }
240
+ export function formatNativeVisionUnavailable(imageCount, reasons, cachedIds) {
241
+ const types = imageCount > 1 ? `${imageCount} images` : "1 image";
242
+ const cacheHint = cachedIds && cachedIds.length > 0 ? `Cached IDs: ${cachedIds.join(", ")} — try ask_vision_proxy anyway.` : "";
243
+ const detail = reasons.length > 0 ? reasons.join(" | ") : "vision backend unreachable";
244
+ return [
245
+ '<vision-observation status="unavailable">',
246
+ `${types} could not be analyzed (${detail}).`,
247
+ "Do NOT guess what the image contains.",
248
+ "Setup: configure ZAI_API_KEY or XAI_API_KEY for vision proxy, or switch to a vision-capable default model.",
249
+ "- Retry with analyze_image and the file path once a vision key is configured",
250
+ "- Use ask_vision_proxy if a cached image exists",
251
+ "- Ask the user to re-share the screenshot or describe what you need to see",
252
+ cacheHint,
253
+ "</vision-observation>",
254
+ ]
255
+ .filter(Boolean)
256
+ .join("\n");
257
+ }
258
+ //# sourceMappingURL=vision-backend.js.map
@@ -1,23 +1,36 @@
1
1
  /**
2
2
  * Vision proxy layer for text-only models (e.g. DeepSeek V4).
3
3
  *
4
- * When the active model does not support vision, image parts in the message
5
- * are extracted, sent to a cheap vision model on SiliconFlow
6
- * (Qwen/Qwen2.5-VL-32B-Instruct @ $0.27/M tokens), and replaced with
7
- * structured text descriptions before the message reaches the primary model.
4
+ * Image parts in messages are sent to the catalog-configured vision backend
5
+ * (default: Z.ai glm-4.6v-flash) and replaced with native-sight observations
6
+ * so the primary model reasons as if it saw the image directly.
8
7
  */
9
8
  import type { ModelMessage } from "ai";
9
+ import { type NativeVisionFallback, type VisionTaskKind } from "./vision-backend.js";
10
10
  export interface VisionProxyResult {
11
11
  messages: ModelMessage[];
12
12
  proxied: boolean;
13
13
  imageCount: number;
14
14
  }
15
- /**
16
- * Returns true when the model cannot handle image content natively.
17
- */
18
15
  export declare function needsVisionProxy(modelId: string): boolean;
16
+ export type ImageHandlingPlan = {
17
+ strategy: "proxy";
18
+ } | {
19
+ strategy: "native_model";
20
+ fallback: NativeVisionFallback;
21
+ } | {
22
+ strategy: "unavailable";
23
+ notice: string;
24
+ };
19
25
  /**
20
- * Process messages through the vision proxy.
21
- * Only messages with image parts are modified; text-only messages pass through.
26
+ * Decide how to handle images when the active model is text-only:
27
+ * proxy backend, switch to a native vision model, or surface unavailable.
22
28
  */
29
+ export declare function planImageHandlingForTextOnlyModel(opts: {
30
+ primaryModelId: string;
31
+ imageCount: number;
32
+ kind?: VisionTaskKind;
33
+ }): Promise<ImageHandlingPlan>;
34
+ /** True when proxy backend or a native vision model can handle images for this text-only model. */
35
+ export declare function canHandleImagesForTextOnlyModel(modelId: string): Promise<boolean>;
23
36
  export declare function proxyVision(messages: ModelMessage[], modelId: string, signal?: AbortSignal): Promise<VisionProxyResult>;
@@ -1,15 +1,13 @@
1
1
  /**
2
2
  * Vision proxy layer for text-only models (e.g. DeepSeek V4).
3
3
  *
4
- * When the active model does not support vision, image parts in the message
5
- * are extracted, sent to a cheap vision model on SiliconFlow
6
- * (Qwen/Qwen2.5-VL-32B-Instruct @ $0.27/M tokens), and replaced with
7
- * structured text descriptions before the message reaches the primary model.
4
+ * Image parts in messages are sent to the catalog-configured vision backend
5
+ * (default: Z.ai glm-4.6v-flash) and replaced with native-sight observations
6
+ * so the primary model reasons as if it saw the image directly.
8
7
  */
9
8
  import { getModelInfo } from "../models/registry.js";
10
- import { apiBaseFor } from "./endpoints.js";
11
- import { loadKeyForProvider } from "./keychain.js";
12
9
  import { UI_LAYOUT_SCHEMA_HINT } from "./mcp-vision-bridge.js";
10
+ import { callVisionBackend, collectVisionUnavailableReasons, findNativeVisionFallback, formatNativeVisionObservation, formatNativeVisionUnavailable, isVisionBackendAvailable, looksLikeOcrIntent, resolveAvailableVisionChain, wrapAnalyzerInstructions, } from "./vision-backend.js";
13
11
  const DESIGN_INTENT_PATTERNS = [
14
12
  /\bredesign\b/i,
15
13
  /\bdesign\s*(system|token|spec|review)?\b/i,
@@ -26,24 +24,40 @@ function looksLikeDesignIntent(text) {
26
24
  return false;
27
25
  return DESIGN_INTENT_PATTERNS.some((re) => re.test(text));
28
26
  }
29
- const VISION_MODELS = [
30
- "Qwen/Qwen3-VL-8B-Instruct",
31
- "Qwen/Qwen3-VL-30B-A3B-Instruct",
32
- "Qwen/Qwen3-VL-32B-Instruct",
33
- ];
34
- const SILICONFLOW_BASE = apiBaseFor("siliconflow");
35
- const REQUEST_TIMEOUT_MS = 90_000;
36
- /**
37
- * Returns true when the model cannot handle image content natively.
38
- */
39
27
  export function needsVisionProxy(modelId) {
40
28
  const info = getModelInfo(modelId);
41
29
  return info?.supportsVision === false;
42
30
  }
43
31
  /**
44
- * Process messages through the vision proxy.
45
- * Only messages with image parts are modified; text-only messages pass through.
32
+ * Decide how to handle images when the active model is text-only:
33
+ * proxy backend, switch to a native vision model, or surface unavailable.
46
34
  */
35
+ export async function planImageHandlingForTextOnlyModel(opts) {
36
+ if (!needsVisionProxy(opts.primaryModelId)) {
37
+ return { strategy: "proxy" };
38
+ }
39
+ const kind = opts.kind ?? "default";
40
+ if (await isVisionBackendAvailable(kind)) {
41
+ return { strategy: "proxy" };
42
+ }
43
+ const fallback = await findNativeVisionFallback({ excludeModelId: opts.primaryModelId });
44
+ if (fallback) {
45
+ return { strategy: "native_model", fallback };
46
+ }
47
+ const reasons = await collectVisionUnavailableReasons(kind);
48
+ return {
49
+ strategy: "unavailable",
50
+ notice: formatNativeVisionUnavailable(opts.imageCount, reasons),
51
+ };
52
+ }
53
+ /** True when proxy backend or a native vision model can handle images for this text-only model. */
54
+ export async function canHandleImagesForTextOnlyModel(modelId) {
55
+ if (!needsVisionProxy(modelId))
56
+ return true;
57
+ if (await isVisionBackendAvailable())
58
+ return true;
59
+ return (await findNativeVisionFallback({ excludeModelId: modelId })) !== null;
60
+ }
47
61
  export async function proxyVision(messages, modelId, signal) {
48
62
  if (!needsVisionProxy(modelId)) {
49
63
  return { messages, proxied: false, imageCount: 0 };
@@ -66,6 +80,7 @@ export async function proxyVision(messages, modelId, signal) {
66
80
  totalImages += imageParts.length;
67
81
  const userText = textParts.map((t) => t.text).join("\n");
68
82
  const designIntent = looksLikeDesignIntent(userText);
83
+ const ocrIntent = looksLikeOcrIntent(userText);
69
84
  const svgFast = trySvgFastPath(imageParts, designIntent);
70
85
  if (svgFast) {
71
86
  processed.push({
@@ -74,27 +89,22 @@ export async function proxyVision(messages, modelId, signal) {
74
89
  });
75
90
  continue;
76
91
  }
77
- const descriptions = await describeImages(imageParts, textParts, signal, designIntent);
92
+ const kind = designIntent ? "design" : ocrIntent ? "ocr" : "default";
93
+ const descriptions = await describeImages(imageParts, textParts, signal, kind);
78
94
  const newContent = [...textParts, { type: "text", text: descriptions }];
79
95
  processed.push({ ...msg, content: newContent });
80
96
  }
81
97
  return { messages: processed, proxied: totalImages > 0, imageCount: totalImages };
82
98
  }
83
- async function describeImages(images, contextTexts, signal, designIntent = false) {
84
- let apiKey;
85
- try {
86
- apiKey = await loadKeyForProvider("siliconflow");
87
- }
88
- catch {
89
- return buildFallbackDescription(images, ["SILICONFLOW_API_KEY not configured"]);
90
- }
99
+ async function describeImages(images, contextTexts, signal, kind = "default") {
91
100
  const userContext = contextTexts.map((t) => t.text).join("\n");
101
+ const designIntent = kind === "design";
92
102
  const visionContent = [];
93
103
  visionContent.push({
94
104
  type: "text",
95
- text: designIntent
105
+ text: wrapAnalyzerInstructions(designIntent
96
106
  ? buildDesignPrompt(userContext, images.length)
97
- : buildAnalysisPrompt(userContext, images.length),
107
+ : buildAnalysisPrompt(userContext, images.length, kind === "ocr"), kind),
98
108
  });
99
109
  for (const img of images) {
100
110
  visionContent.push({
@@ -106,24 +116,12 @@ async function describeImages(images, contextTexts, signal, designIntent = false
106
116
  });
107
117
  }
108
118
  const responseFormat = designIntent ? { type: "json_object" } : undefined;
109
- const failureReasons = [];
110
- for (const model of VISION_MODELS) {
111
- try {
112
- const result = await callVisionModel(model, visionContent, apiKey, signal, responseFormat);
113
- if (result.ok)
114
- return formatVisionResult(result.text, images.length, model, designIntent);
115
- failureReasons.push(`${model}: ${result.reason}`);
116
- console.warn(`[vision-proxy] ${model} failed (${result.reason}), trying next...`);
117
- }
118
- catch (err) {
119
- if (signal?.aborted)
120
- throw err;
121
- const msg = err instanceof Error ? err.message : String(err);
122
- failureReasons.push(`${model}: ${msg}`);
123
- console.warn(`[vision-proxy] ${model} threw (${msg}), trying next...`);
124
- }
119
+ const chain = await resolveAvailableVisionChain(kind);
120
+ const result = await callVisionBackend(chain, visionContent, signal, responseFormat);
121
+ if (result.ok) {
122
+ return formatNativeVisionObservation(result.text, { imageCount: images.length });
125
123
  }
126
- return buildFallbackDescription(images, failureReasons);
124
+ return formatNativeVisionUnavailable(images.length, [result.reason]);
127
125
  }
128
126
  function trySvgFastPath(images, designIntent) {
129
127
  if (images.length === 0)
@@ -142,64 +140,23 @@ function trySvgFastPath(images, designIntent) {
142
140
  return svg.length > 32_000 ? `${svg.slice(0, 32_000)}\n<!-- truncated -->` : svg;
143
141
  })
144
142
  .join("\n\n");
145
- const header = `[Vision Proxy — ${images.length} SVG source(s) passed through (fast-path, no vision call)]`;
146
- const guidance = designIntent
147
- ? `\nThe raw SVG below IS the layout contract. Map nodes to:\n${UI_LAYOUT_SCHEMA_HINT}\n`
148
- : "\nThe raw SVG below is vector text — read element attributes (x, y, width, fill, text content) directly.\n";
149
- return `\n${header}${guidance}\n\`\`\`svg\n${decoded}\n\`\`\`\n[/Vision Proxy]\n`;
143
+ const observation = designIntent
144
+ ? `I see ${images.length} SVG source(s). The vector markup below is exact — use element attributes (x, y, width, fill, text) directly.\n\nMap to layout schema:\n${UI_LAYOUT_SCHEMA_HINT}\n\n\`\`\`svg\n${decoded}\n\`\`\``
145
+ : `I see ${images.length} SVG source(s). Vector markup (read attributes directly):\n\n\`\`\`svg\n${decoded}\n\`\`\``;
146
+ return formatNativeVisionObservation(observation, { imageCount: images.length });
150
147
  }
151
- async function callVisionModel(model, content, apiKey, signal, responseFormat) {
152
- const controller = new AbortController();
153
- let timedOut = false;
154
- const timeout = setTimeout(() => {
155
- timedOut = true;
156
- controller.abort();
157
- }, REQUEST_TIMEOUT_MS);
158
- if (signal) {
159
- signal.addEventListener("abort", () => controller.abort(), { once: true });
160
- }
161
- let res;
162
- try {
163
- res = await fetch(`${SILICONFLOW_BASE}/chat/completions`, {
164
- method: "POST",
165
- headers: {
166
- "Content-Type": "application/json",
167
- Authorization: `Bearer ${apiKey}`,
168
- },
169
- body: JSON.stringify({
170
- model,
171
- messages: [{ role: "user", content }],
172
- max_tokens: 3072,
173
- temperature: 0.1,
174
- ...(responseFormat ? { response_format: responseFormat } : {}),
175
- }),
176
- signal: controller.signal,
177
- });
178
- }
179
- catch (err) {
180
- clearTimeout(timeout);
181
- if (timedOut)
182
- return { ok: false, reason: `timeout after ${REQUEST_TIMEOUT_MS}ms` };
183
- if (signal?.aborted)
184
- throw err;
185
- return { ok: false, reason: `network error: ${err instanceof Error ? err.message : String(err)}` };
186
- }
187
- clearTimeout(timeout);
188
- if (!res.ok) {
189
- const errText = await res.text().catch(() => "unknown error");
190
- return { ok: false, reason: `HTTP ${res.status} ${errText.slice(0, 200)}` };
191
- }
192
- const data = (await res.json().catch(() => null));
193
- const text = data?.choices?.[0]?.message?.content;
194
- if (!text)
195
- return { ok: false, reason: "empty response body" };
196
- return { ok: true, text };
197
- }
198
- function buildAnalysisPrompt(userContext, imageCount) {
148
+ function buildAnalysisPrompt(userContext, imageCount, ocrFocus) {
199
149
  const plural = imageCount > 1 ? `${imageCount} images` : "the image";
150
+ const ocrBlock = ocrFocus
151
+ ? [
152
+ "OCR focus: transcribe ALL visible text exactly (preserve line breaks, labels, error codes).",
153
+ "Note any text that is blurry or partially cut off.",
154
+ "",
155
+ ]
156
+ : [];
200
157
  return [
201
- `You are a vision model. Analyze ${plural} for a software developer working in a coding CLI tool.`,
202
- "Use your full vision capability — do NOT refuse on the basis of being text-only.",
158
+ `Analyze ${plural} for a software developer working in a coding CLI.`,
159
+ ...ocrBlock,
203
160
  "Focus on:",
204
161
  "- UI layout and component structure (if screenshot)",
205
162
  "- All visible text, labels, buttons, and form elements",
@@ -212,45 +169,19 @@ function buildAnalysisPrompt(userContext, imageCount) {
212
169
  userContext ? `\nDeveloper's context: "${userContext}"` : "",
213
170
  ].join("\n");
214
171
  }
215
- function formatVisionResult(description, imageCount, model, designIntent = false) {
216
- const usedModel = model ?? VISION_MODELS[0];
217
- const tag = designIntent ? "design contract via" : "analyzed via";
218
- const single = designIntent ? "design contract via" : "analyzed via";
219
- const header = imageCount > 1
220
- ? `[Vision Proxy — ${imageCount} images ${tag} ${usedModel}]`
221
- : `[Vision Proxy — image ${single} ${usedModel}]`;
222
- return `\n${header}\n${description}\n[/Vision Proxy]\n`;
223
- }
224
172
  function buildDesignPrompt(userContext, imageCount) {
225
173
  const plural = imageCount > 1 ? `${imageCount} images` : "the image";
226
174
  return [
227
- `Analyze ${plural} as a UI/UX design contract for a software developer who CANNOT see the image.`,
228
- "Output will be consumed by another AI agent to recreate or redesign this UI, so precision matters more than prose.",
229
- "",
230
- "Return ONLY a single JSON object matching this exact shape (no markdown, no commentary, no code fences):",
175
+ `Analyze ${plural} as a UI/UX design contract for a software developer.`,
176
+ "Return ONLY a single JSON object matching this exact shape (no markdown, no commentary):",
231
177
  UI_LAYOUT_SCHEMA_HINT,
232
178
  "",
233
179
  "Extraction rules:",
234
- "- bbox: pixel coordinates relative to the visible image (origin top-left). Estimate when unsure but never omit.",
235
- "- Colors: 6-digit lowercase hex (#rrggbb). Sample dominant pixel.",
236
- "- Typography sizePx: estimate from cap-height, round to nearest 2.",
237
- "- spacingScalePx / radiusScalePx: distinct observed values, sorted ascending.",
238
- "- components[]: every visible interactive or content element. Stable IDs like 'btn_signup'.",
239
- "- children[]: component IDs nested inside this component.",
240
- "- hierarchy[]: visual reading order of headings — level 1 hero, 2 section, 3 sub.",
180
+ "- bbox: pixel coordinates relative to the visible image (origin top-left).",
181
+ "- Colors: 6-digit lowercase hex (#rrggbb).",
182
+ "- components[]: every visible interactive or content element.",
241
183
  "- text: EXACT visible string. null only when no text (icon-only).",
242
- "- notes[]: things schema cannot capture — gradients, illustrations, motion.",
243
- "",
244
- "If a field is genuinely not determinable, use null. Never invent values.",
245
184
  userContext ? `\nDeveloper's context: "${userContext}"` : "",
246
185
  ].join("\n");
247
186
  }
248
- function buildFallbackDescription(images, reasons = []) {
249
- const count = images.length;
250
- const types = [...new Set(images.map((i) => i.mediaType))].join(", ");
251
- const detail = reasons.length > 0
252
- ? `Reason(s): ${reasons.join(" | ")}`
253
- : "Reason: unknown — check SILICONFLOW_API_KEY and model availability.";
254
- return `\n[Vision Proxy — unavailable, ${count} image(s) (${types}) could not be analyzed. ${detail}]\n`;
255
- }
256
187
  //# sourceMappingURL=vision-proxy.js.map