@geminilight/mindos 0.6.61 → 0.6.65

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 (313) hide show
  1. package/_standalone/.antigravity/mcp_config.json +14 -0
  2. package/_standalone/.mindos-build-version +1 -1
  3. package/_standalone/.next/BUILD_ID +1 -1
  4. package/_standalone/.next/app-path-routes-manifest.json +23 -23
  5. package/_standalone/.next/build-manifest.json +2 -2
  6. package/_standalone/.next/cache/.previewinfo +1 -1
  7. package/_standalone/.next/cache/.rscinfo +1 -1
  8. package/_standalone/.next/cache/config.json +3 -3
  9. package/_standalone/.next/prerender-manifest.json +3 -3
  10. package/_standalone/.next/server/app/.well-known/agent-card.json/route_client-reference-manifest.js +1 -1
  11. package/_standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  12. package/_standalone/.next/server/app/_global-error.html +2 -2
  13. package/_standalone/.next/server/app/_global-error.rsc +1 -1
  14. package/_standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  15. package/_standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  16. package/_standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  17. package/_standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  18. package/_standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  19. package/_standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  20. package/_standalone/.next/server/app/_not-found/page.js +1 -1
  21. package/_standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  22. package/_standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  23. package/_standalone/.next/server/app/agents/[agentKey]/page.js +1 -1
  24. package/_standalone/.next/server/app/agents/[agentKey]/page.js.nft.json +1 -1
  25. package/_standalone/.next/server/app/agents/[agentKey]/page_client-reference-manifest.js +1 -1
  26. package/_standalone/.next/server/app/agents/page.js +1 -1
  27. package/_standalone/.next/server/app/agents/page.js.nft.json +1 -1
  28. package/_standalone/.next/server/app/agents/page_client-reference-manifest.js +1 -1
  29. package/_standalone/.next/server/app/api/a2a/agents/route_client-reference-manifest.js +1 -1
  30. package/_standalone/.next/server/app/api/a2a/delegations/route_client-reference-manifest.js +1 -1
  31. package/_standalone/.next/server/app/api/a2a/discover/route_client-reference-manifest.js +1 -1
  32. package/_standalone/.next/server/app/api/a2a/route_client-reference-manifest.js +1 -1
  33. package/_standalone/.next/server/app/api/acp/config/route_client-reference-manifest.js +1 -1
  34. package/_standalone/.next/server/app/api/acp/detect/route.js +1 -1
  35. package/_standalone/.next/server/app/api/acp/detect/route_client-reference-manifest.js +1 -1
  36. package/_standalone/.next/server/app/api/acp/install/route_client-reference-manifest.js +1 -1
  37. package/_standalone/.next/server/app/api/acp/registry/route.js +1 -1
  38. package/_standalone/.next/server/app/api/acp/registry/route_client-reference-manifest.js +1 -1
  39. package/_standalone/.next/server/app/api/acp/session/route_client-reference-manifest.js +1 -1
  40. package/_standalone/.next/server/app/api/agent-activity/route_client-reference-manifest.js +1 -1
  41. package/_standalone/.next/server/app/api/agents/copy-skill/route.js.nft.json +1 -1
  42. package/_standalone/.next/server/app/api/agents/copy-skill/route_client-reference-manifest.js +1 -1
  43. package/_standalone/.next/server/app/api/agents/custom/detect/route_client-reference-manifest.js +1 -1
  44. package/_standalone/.next/server/app/api/agents/custom/route_client-reference-manifest.js +1 -1
  45. package/_standalone/.next/server/app/api/ask/route.js +53 -47
  46. package/_standalone/.next/server/app/api/ask/route.js.nft.json +1 -1
  47. package/_standalone/.next/server/app/api/ask/route_client-reference-manifest.js +1 -1
  48. package/_standalone/.next/server/app/api/ask-sessions/route_client-reference-manifest.js +1 -1
  49. package/_standalone/.next/server/app/api/auth/route_client-reference-manifest.js +1 -1
  50. package/_standalone/.next/server/app/api/backlinks/route.js.nft.json +1 -1
  51. package/_standalone/.next/server/app/api/backlinks/route_client-reference-manifest.js +1 -1
  52. package/_standalone/.next/server/app/api/bootstrap/route.js.nft.json +1 -1
  53. package/_standalone/.next/server/app/api/bootstrap/route_client-reference-manifest.js +1 -1
  54. package/_standalone/.next/server/app/api/changes/route.js.nft.json +1 -1
  55. package/_standalone/.next/server/app/api/changes/route_client-reference-manifest.js +1 -1
  56. package/_standalone/.next/server/app/api/export/route.js.nft.json +1 -1
  57. package/_standalone/.next/server/app/api/export/route_client-reference-manifest.js +1 -1
  58. package/_standalone/.next/server/app/api/extract-pdf/route_client-reference-manifest.js +1 -1
  59. package/_standalone/.next/server/app/api/file/import/route.js +1 -1
  60. package/_standalone/.next/server/app/api/file/import/route.js.nft.json +1 -1
  61. package/_standalone/.next/server/app/api/file/import/route_client-reference-manifest.js +1 -1
  62. package/_standalone/.next/server/app/api/file/raw/route.js.nft.json +1 -1
  63. package/_standalone/.next/server/app/api/file/raw/route_client-reference-manifest.js +1 -1
  64. package/_standalone/.next/server/app/api/file/route.js.nft.json +1 -1
  65. package/_standalone/.next/server/app/api/file/route_client-reference-manifest.js +1 -1
  66. package/_standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  67. package/_standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  68. package/_standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  69. package/_standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  70. package/_standalone/.next/server/app/api/graph/route.js.nft.json +1 -1
  71. package/_standalone/.next/server/app/api/graph/route_client-reference-manifest.js +1 -1
  72. package/_standalone/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
  73. package/_standalone/.next/server/app/api/inbox/route.js.nft.json +1 -1
  74. package/_standalone/.next/server/app/api/inbox/route_client-reference-manifest.js +1 -1
  75. package/_standalone/.next/server/app/api/init/route.js.nft.json +1 -1
  76. package/_standalone/.next/server/app/api/init/route_client-reference-manifest.js +1 -1
  77. package/_standalone/.next/server/app/api/mcp/agents/route.js +1 -1
  78. package/_standalone/.next/server/app/api/mcp/agents/route.js.nft.json +1 -1
  79. package/_standalone/.next/server/app/api/mcp/agents/route_client-reference-manifest.js +1 -1
  80. package/_standalone/.next/server/app/api/mcp/install/route_client-reference-manifest.js +1 -1
  81. package/_standalone/.next/server/app/api/mcp/install-skill/route_client-reference-manifest.js +1 -1
  82. package/_standalone/.next/server/app/api/mcp/restart/route_client-reference-manifest.js +1 -1
  83. package/_standalone/.next/server/app/api/mcp/status/route_client-reference-manifest.js +1 -1
  84. package/_standalone/.next/server/app/api/mcp/uninstall/route_client-reference-manifest.js +1 -1
  85. package/_standalone/.next/server/app/api/monitoring/route.js.nft.json +1 -1
  86. package/_standalone/.next/server/app/api/monitoring/route_client-reference-manifest.js +1 -1
  87. package/_standalone/.next/server/app/api/recent-files/route.js.nft.json +1 -1
  88. package/_standalone/.next/server/app/api/recent-files/route_client-reference-manifest.js +1 -1
  89. package/_standalone/.next/server/app/api/restart/route_client-reference-manifest.js +1 -1
  90. package/_standalone/.next/server/app/api/search/route.js.nft.json +1 -1
  91. package/_standalone/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
  92. package/_standalone/.next/server/app/api/settings/list-models/route.js +1 -1
  93. package/_standalone/.next/server/app/api/settings/list-models/route_client-reference-manifest.js +1 -1
  94. package/_standalone/.next/server/app/api/settings/reset-token/route_client-reference-manifest.js +1 -1
  95. package/_standalone/.next/server/app/api/settings/route.js +1 -1
  96. package/_standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
  97. package/_standalone/.next/server/app/api/settings/route_client-reference-manifest.js +1 -1
  98. package/_standalone/.next/server/app/api/settings/test-key/route.js +1 -1
  99. package/_standalone/.next/server/app/api/settings/test-key/route_client-reference-manifest.js +1 -1
  100. package/_standalone/.next/server/app/api/setup/check-path/route_client-reference-manifest.js +1 -1
  101. package/_standalone/.next/server/app/api/setup/check-port/route_client-reference-manifest.js +1 -1
  102. package/_standalone/.next/server/app/api/setup/generate-token/route_client-reference-manifest.js +1 -1
  103. package/_standalone/.next/server/app/api/setup/ls/route_client-reference-manifest.js +1 -1
  104. package/_standalone/.next/server/app/api/setup/route.js +1 -1
  105. package/_standalone/.next/server/app/api/setup/route_client-reference-manifest.js +1 -1
  106. package/_standalone/.next/server/app/api/skills/route.js +1 -1
  107. package/_standalone/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  108. package/_standalone/.next/server/app/api/sync/route_client-reference-manifest.js +1 -1
  109. package/_standalone/.next/server/app/api/tree-version/route.js.nft.json +1 -1
  110. package/_standalone/.next/server/app/api/tree-version/route_client-reference-manifest.js +1 -1
  111. package/_standalone/.next/server/app/api/uninstall/route_client-reference-manifest.js +1 -1
  112. package/_standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  113. package/_standalone/.next/server/app/api/update-check/route_client-reference-manifest.js +1 -1
  114. package/_standalone/.next/server/app/api/update-status/route_client-reference-manifest.js +1 -1
  115. package/_standalone/.next/server/app/api/workflows/route.js.nft.json +1 -1
  116. package/_standalone/.next/server/app/api/workflows/route_client-reference-manifest.js +1 -1
  117. package/_standalone/.next/server/app/changes/page.js +1 -1
  118. package/_standalone/.next/server/app/changes/page.js.nft.json +1 -1
  119. package/_standalone/.next/server/app/changes/page_client-reference-manifest.js +1 -1
  120. package/_standalone/.next/server/app/echo/[segment]/page.js +2 -2
  121. package/_standalone/.next/server/app/echo/[segment]/page.js.nft.json +1 -1
  122. package/_standalone/.next/server/app/echo/[segment]/page_client-reference-manifest.js +1 -1
  123. package/_standalone/.next/server/app/echo/page.js +1 -1
  124. package/_standalone/.next/server/app/echo/page.js.nft.json +1 -1
  125. package/_standalone/.next/server/app/echo/page_client-reference-manifest.js +1 -1
  126. package/_standalone/.next/server/app/explore/page.js +1 -1
  127. package/_standalone/.next/server/app/explore/page.js.nft.json +1 -1
  128. package/_standalone/.next/server/app/explore/page_client-reference-manifest.js +1 -1
  129. package/_standalone/.next/server/app/help/page.js +1 -1
  130. package/_standalone/.next/server/app/help/page.js.nft.json +1 -1
  131. package/_standalone/.next/server/app/help/page_client-reference-manifest.js +1 -1
  132. package/_standalone/.next/server/app/inbox/history/page.js +1 -1
  133. package/_standalone/.next/server/app/inbox/history/page.js.nft.json +1 -1
  134. package/_standalone/.next/server/app/inbox/history/page_client-reference-manifest.js +1 -1
  135. package/_standalone/.next/server/app/login/page.js +1 -1
  136. package/_standalone/.next/server/app/login/page.js.nft.json +1 -1
  137. package/_standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
  138. package/_standalone/.next/server/app/page.js +1 -1
  139. package/_standalone/.next/server/app/page.js.nft.json +1 -1
  140. package/_standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  141. package/_standalone/.next/server/app/setup/page.js +2 -2
  142. package/_standalone/.next/server/app/setup/page.js.nft.json +1 -1
  143. package/_standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  144. package/_standalone/.next/server/app/trash/page.js +3 -3
  145. package/_standalone/.next/server/app/trash/page.js.nft.json +1 -1
  146. package/_standalone/.next/server/app/trash/page_client-reference-manifest.js +1 -1
  147. package/_standalone/.next/server/app/view/[...path]/page.js +2 -2
  148. package/_standalone/.next/server/app/view/[...path]/page.js.nft.json +1 -1
  149. package/_standalone/.next/server/app/view/[...path]/page_client-reference-manifest.js +1 -1
  150. package/_standalone/.next/server/app/wiki/page.js +1 -1
  151. package/_standalone/.next/server/app/wiki/page.js.nft.json +1 -1
  152. package/_standalone/.next/server/app/wiki/page_client-reference-manifest.js +1 -1
  153. package/_standalone/.next/server/app-paths-manifest.json +23 -23
  154. package/_standalone/.next/server/chunks/122.js +222 -0
  155. package/_standalone/.next/server/chunks/1550.js +1 -1
  156. package/_standalone/.next/server/chunks/1750.js +1 -1
  157. package/_standalone/.next/server/chunks/3113.js +52 -0
  158. package/_standalone/.next/server/chunks/6539.js +1 -1
  159. package/_standalone/.next/server/chunks/8388.js +3 -3
  160. package/_standalone/.next/server/chunks/953.js +3 -3
  161. package/_standalone/.next/server/pages/500.html +2 -2
  162. package/_standalone/.next/server/server-reference-manifest.js +1 -1
  163. package/_standalone/.next/server/server-reference-manifest.json +1 -1
  164. package/_standalone/.next/static/chunks/1001-99da82ec8d8c136f.js +1 -0
  165. package/_standalone/.next/static/chunks/1088-77544af0a50cb7a4.js +1 -0
  166. package/_standalone/.next/static/chunks/1467-87dde7eed498806f.js +1 -0
  167. package/_standalone/.next/static/chunks/5149-4d828886dda479fa.js +1 -0
  168. package/_standalone/.next/static/chunks/5581-c671163a2fe1b312.js +29 -0
  169. package/_standalone/.next/static/chunks/{7266-bb7be1128eccd48e.js → 5718-3837c3210a0e175f.js} +2 -2
  170. package/_standalone/.next/static/chunks/6636-53238eff89503f03.js +6 -0
  171. package/_standalone/.next/static/chunks/6757-1c1a89720fdda8f0.js +1 -0
  172. package/_standalone/.next/static/chunks/7129-20e9d2463a9da646.js +1 -0
  173. package/_standalone/.next/static/chunks/7294-cac25d97869afadc.js +1 -0
  174. package/_standalone/.next/static/chunks/8225-21e5cebc3731ddf0.js +1 -0
  175. package/_standalone/.next/static/chunks/8520-b51810e66293ceb8.js +22 -0
  176. package/_standalone/.next/static/chunks/9207-dc9c31b351a2ed78.js +1 -0
  177. package/_standalone/.next/static/chunks/app/agents/[agentKey]/page-2f5cf97e03dc1cc9.js +1 -0
  178. package/_standalone/.next/static/chunks/app/agents/page-50eac58d511dcc6e.js +1 -0
  179. package/_standalone/.next/static/chunks/app/echo/[segment]/page-2a00f4686adf3885.js +11 -0
  180. package/_standalone/.next/static/chunks/app/layout-2cb7a6602d2e5d5f.js +168 -0
  181. package/_standalone/.next/static/chunks/app/{page-6a1f8d21c12b829e.js → page-5ab911b2226f6ff7.js} +1 -1
  182. package/_standalone/.next/static/chunks/app/setup/page-907b7c57fad2292b.js +1 -0
  183. package/_standalone/.next/static/chunks/app/trash/page-11a511b065ea84c2.js +1 -0
  184. package/_standalone/.next/static/chunks/app/view/[...path]/page-26e47dd4c533a58c.js +12 -0
  185. package/_standalone/.next/static/chunks/app/wiki/page-dce495b9048022fb.js +1 -0
  186. package/_standalone/.next/static/css/67e7918f5ed7d147.css +1 -0
  187. package/_standalone/.next/trace +65 -65
  188. package/_standalone/__tests__/acp/registry.test.ts +30 -20
  189. package/_standalone/__tests__/api/ask-attachments.test.ts +194 -0
  190. package/_standalone/__tests__/api/mcp-install.test.ts +49 -2
  191. package/_standalone/__tests__/api/settings.test.ts +16 -12
  192. package/_standalone/__tests__/api/setup.test.ts +11 -9
  193. package/_standalone/__tests__/api/test-key.test.ts +0 -10
  194. package/_standalone/__tests__/components/UpdateToast.test.ts +344 -0
  195. package/_standalone/__tests__/core/context.test.ts +48 -426
  196. package/_standalone/__tests__/lib/pi-skills.test.ts +4 -4
  197. package/_standalone/__tests__/lib/settings-ai-client.test.ts +32 -12
  198. package/_standalone/__tests__/setup.ts +5 -5
  199. package/_standalone/app/globals.css +4 -4
  200. package/_standalone/components/ActivityBar.tsx +17 -6
  201. package/_standalone/components/Panel.tsx +24 -6
  202. package/_standalone/components/SidebarLayout.tsx +36 -8
  203. package/_standalone/components/agents/AgentsMcpSection.tsx +2 -2
  204. package/_standalone/components/agents/AgentsOverviewSection.tsx +5 -1
  205. package/_standalone/components/agents/AgentsPanelA2aTab.tsx +173 -113
  206. package/_standalone/components/agents/AgentsSkillsSection.tsx +2 -2
  207. package/_standalone/components/ask/AskContent.tsx +83 -44
  208. package/_standalone/components/ask/AskHeader.tsx +8 -1
  209. package/_standalone/components/ask/MessageList.tsx +37 -3
  210. package/_standalone/components/ask/ProviderModelCapsule.tsx +444 -174
  211. package/_standalone/components/home/InboxSection.tsx +25 -25
  212. package/_standalone/components/settings/AiTab.tsx +353 -298
  213. package/_standalone/components/settings/CustomProviderFields.tsx +121 -0
  214. package/_standalone/components/settings/CustomProvidersCard.tsx +154 -0
  215. package/_standalone/components/settings/KnowledgeTab.tsx +6 -20
  216. package/_standalone/components/settings/McpAgentInstall.tsx +7 -2
  217. package/_standalone/components/settings/Primitives.tsx +48 -104
  218. package/_standalone/components/settings/ProviderModal.tsx +87 -0
  219. package/_standalone/components/settings/SettingsContent.tsx +2 -5
  220. package/_standalone/components/settings/TestButton.tsx +64 -0
  221. package/_standalone/components/settings/types.ts +3 -9
  222. package/_standalone/components/settings/useCustomProviderForm.ts +132 -0
  223. package/_standalone/components/setup/StepAI.tsx +12 -5
  224. package/_standalone/components/shared/ModelInput.tsx +220 -0
  225. package/_standalone/components/shared/ProviderSelect.tsx +126 -36
  226. package/_standalone/hooks/useAskChat.ts +100 -13
  227. package/_standalone/hooks/useAskPanel.ts +17 -1
  228. package/_standalone/lib/settings-ai-client.ts +17 -8
  229. package/_standalone/tsconfig.tsbuildinfo +1 -1
  230. package/app/.antigravity/mcp_config.json +14 -0
  231. package/app/app/api/ask/route.ts +154 -45
  232. package/app/app/api/mcp/agents/route.ts +3 -3
  233. package/app/app/api/settings/list-models/route.ts +36 -9
  234. package/app/app/api/settings/route.ts +14 -42
  235. package/app/app/api/settings/test-key/route.ts +78 -2
  236. package/app/app/api/setup/route.ts +36 -18
  237. package/app/app/api/skills/route.ts +1 -1
  238. package/app/app/globals.css +4 -4
  239. package/app/app/layout.tsx +5 -3
  240. package/app/app/view/[...path]/page.tsx +5 -0
  241. package/app/components/ActivityBar.tsx +17 -6
  242. package/app/components/HomeContent.tsx +11 -0
  243. package/app/components/InboxView.tsx +656 -0
  244. package/app/components/Panel.tsx +24 -6
  245. package/app/components/SidebarLayout.tsx +36 -8
  246. package/app/components/UpdateToast.tsx +255 -0
  247. package/app/components/agents/AgentDetailContent.tsx +8 -8
  248. package/app/components/agents/AgentsMcpSection.tsx +2 -2
  249. package/app/components/agents/AgentsOverviewSection.tsx +5 -1
  250. package/app/components/agents/AgentsPanelA2aTab.tsx +173 -113
  251. package/app/components/agents/AgentsSkillsSection.tsx +2 -2
  252. package/app/components/ask/AskContent.tsx +83 -44
  253. package/app/components/ask/AskHeader.tsx +8 -1
  254. package/app/components/ask/MessageList.tsx +37 -3
  255. package/app/components/ask/ProviderModelCapsule.tsx +444 -174
  256. package/app/components/home/InboxSection.tsx +25 -25
  257. package/app/components/settings/AiTab.tsx +353 -298
  258. package/app/components/settings/CustomProviderFields.tsx +121 -0
  259. package/app/components/settings/CustomProvidersCard.tsx +154 -0
  260. package/app/components/settings/KnowledgeTab.tsx +6 -20
  261. package/app/components/settings/McpAgentInstall.tsx +7 -2
  262. package/app/components/settings/Primitives.tsx +48 -104
  263. package/app/components/settings/ProviderModal.tsx +87 -0
  264. package/app/components/settings/SettingsContent.tsx +2 -5
  265. package/app/components/settings/TestButton.tsx +64 -0
  266. package/app/components/settings/types.ts +3 -9
  267. package/app/components/settings/useCustomProviderForm.ts +132 -0
  268. package/app/components/setup/StepAI.tsx +12 -5
  269. package/app/components/shared/ModelInput.tsx +220 -0
  270. package/app/components/shared/ProviderSelect.tsx +126 -36
  271. package/app/hooks/useAskChat.ts +100 -13
  272. package/app/hooks/useAskPanel.ts +17 -1
  273. package/app/lib/acp/registry.ts +92 -10
  274. package/app/lib/agent/context.ts +65 -0
  275. package/app/lib/agent/providers.ts +25 -0
  276. package/app/lib/agent/tools.ts +1 -1
  277. package/app/lib/custom-endpoints.ts +160 -0
  278. package/app/lib/fs.ts +8 -1
  279. package/app/lib/i18n/modules/ai-chat.ts +6 -0
  280. package/app/lib/i18n/modules/knowledge.ts +16 -0
  281. package/app/lib/i18n/modules/onboarding.ts +4 -0
  282. package/app/lib/i18n/modules/settings.ts +88 -2
  283. package/app/lib/mcp-agents.ts +11 -0
  284. package/app/lib/pi-integration/skills.ts +16 -4
  285. package/app/lib/settings-ai-client.ts +17 -8
  286. package/app/lib/settings.ts +68 -72
  287. package/app/lib/types.ts +4 -0
  288. package/bin/lib/mcp-agents.js +11 -0
  289. package/bin/lib/mcp-install.js +71 -7
  290. package/package.json +1 -1
  291. package/_standalone/.next/server/chunks/530.js +0 -218
  292. package/_standalone/.next/server/chunks/8955.js +0 -52
  293. package/_standalone/.next/static/chunks/1369-7d0ac5d1564eed1e.js +0 -1
  294. package/_standalone/.next/static/chunks/3427-2e61a5df1f5e55fb.js +0 -1
  295. package/_standalone/.next/static/chunks/5581-0c700c20718bd916.js +0 -29
  296. package/_standalone/.next/static/chunks/6297-085daa21037d5f81.js +0 -1
  297. package/_standalone/.next/static/chunks/6636-9bbc90fb3b8731fe.js +0 -6
  298. package/_standalone/.next/static/chunks/7637-904b0a381dc3ec02.js +0 -1
  299. package/_standalone/.next/static/chunks/8520-76d1b05072178b43.js +0 -22
  300. package/_standalone/.next/static/chunks/8658-16ff58b75ae37fbb.js +0 -1
  301. package/_standalone/.next/static/chunks/9905-a19d379cb225246e.js +0 -1
  302. package/_standalone/.next/static/chunks/app/agents/[agentKey]/page-0ea3571c8fbae823.js +0 -1
  303. package/_standalone/.next/static/chunks/app/agents/page-66858acbcd1d4bf8.js +0 -1
  304. package/_standalone/.next/static/chunks/app/echo/[segment]/page-bf5c290fa3ccff09.js +0 -11
  305. package/_standalone/.next/static/chunks/app/layout-a5d5925b47e87cc3.js +0 -164
  306. package/_standalone/.next/static/chunks/app/setup/page-821714e7477be46c.js +0 -1
  307. package/_standalone/.next/static/chunks/app/trash/page-40bc7316806acd62.js +0 -1
  308. package/_standalone/.next/static/chunks/app/view/[...path]/page-6fbb14b8f322d0f0.js +0 -12
  309. package/_standalone/.next/static/chunks/app/wiki/page-ba36eccf4fe62cfe.js +0 -1
  310. package/_standalone/.next/static/css/b57c4eb3cc88308b.css +0 -1
  311. package/_standalone/lib/agent/context.ts +0 -403
  312. /package/_standalone/.next/static/{5GmVArEG8OX03azKICsGq → eIlwbGas1iRGonlPyEwj7}/_buildManifest.js +0 -0
  313. /package/_standalone/.next/static/{5GmVArEG8OX03azKICsGq → eIlwbGas1iRGonlPyEwj7}/_ssgManifest.js +0 -0
@@ -1,428 +1,50 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import {
3
- truncateToolOutputs,
4
- hardPrune,
5
- estimateTokens,
6
- estimateStringTokens,
7
- getContextLimit,
8
- needsCompact,
9
- compactMessages,
10
- } from '../../lib/agent/context';
11
- import type { AgentMessage } from '@mariozechner/pi-agent-core';
12
- import type { Model } from '@mariozechner/pi-ai';
13
-
14
- // Mock pi-ai's complete() intercept the summarizer call
15
- vi.mock('@mariozechner/pi-ai', async (importOriginal) => {
16
- const actual = await importOriginal<typeof import('@mariozechner/pi-ai')>();
17
- return {
18
- ...actual,
19
- complete: vi.fn(),
20
- };
21
- });
22
-
23
- import { complete } from '@mariozechner/pi-ai';
24
- const mockComplete = vi.mocked(complete);
25
-
26
- // ---------------------------------------------------------------------------
27
- // Helpers — build AgentMessage objects matching pi-ai types
28
- // ---------------------------------------------------------------------------
29
-
30
- const now = Date.now();
31
-
32
- /** Create a UserMessage */
33
- function userMsg(text: string): AgentMessage {
34
- return { role: 'user', content: text, timestamp: now } as AgentMessage;
35
- }
36
-
37
- /** Create an AssistantMessage */
38
- function assistantMsg(text: string): AgentMessage {
39
- return {
40
- role: 'assistant',
41
- content: [{ type: 'text', text }],
42
- api: 'anthropic-messages',
43
- provider: 'anthropic',
44
- model: 'claude-sonnet-4-6',
45
- usage: { inputTokens: 0, outputTokens: 0 },
46
- } as AgentMessage;
47
- }
48
-
49
- /** Create a ToolResultMessage */
50
- function toolResultMsg(toolName: string, text: string): AgentMessage {
51
- return {
52
- role: 'toolResult',
53
- toolCallId: `call_${toolName}`,
54
- toolName,
55
- content: [{ type: 'text', text }],
56
- } as AgentMessage;
57
- }
58
-
59
- /** Create an assistant message with a tool call */
60
- function assistantWithToolCall(toolName: string): AgentMessage {
61
- return {
62
- role: 'assistant',
63
- content: [{ type: 'toolCall', toolCallId: `call_${toolName}`, toolName, args: {} }],
64
- api: 'anthropic-messages',
65
- provider: 'anthropic',
66
- model: 'claude-sonnet-4-6',
67
- usage: { inputTokens: 0, outputTokens: 0 },
68
- } as AgentMessage;
69
- }
70
-
71
- /** Dummy Model (only used as pass-through; complete is mocked) */
72
- const fakeModel = {} as Model<any>;
73
-
74
- // ---------------------------------------------------------------------------
75
- // Token estimation
76
- // ---------------------------------------------------------------------------
77
-
78
- describe('context: token estimation', () => {
79
- it('estimates string tokens as length/4', () => {
80
- expect(estimateStringTokens('abcd')).toBe(1);
81
- expect(estimateStringTokens('abcde')).toBe(2);
82
- expect(estimateStringTokens('')).toBe(0);
83
- });
84
-
85
- it('estimates message tokens for string content', () => {
86
- const msgs = [userMsg('a'.repeat(100))];
87
- expect(estimateTokens(msgs)).toBe(25);
88
- });
89
-
90
- it('returns 0 for messages with no content', () => {
91
- const msgs = [{ role: 'user' as const, content: undefined }] as unknown as AgentMessage[];
92
- expect(estimateTokens(msgs)).toBe(0);
93
- });
94
-
95
- it('estimates tokens for array content (assistant with text)', () => {
96
- const msgs = [assistantMsg('a'.repeat(100))];
97
- expect(estimateTokens(msgs)).toBe(25);
98
- });
99
-
100
- it('estimates tokens for tool call args', () => {
101
- const msgs = [assistantWithToolCall('read_file')];
102
- // args = {} → JSON.stringify = "{}" = 2 chars → ceil(2/4) = 1
103
- expect(estimateTokens(msgs)).toBeGreaterThanOrEqual(1);
104
- });
105
- });
106
-
107
- // ---------------------------------------------------------------------------
108
- // Context limits
109
- // ---------------------------------------------------------------------------
110
-
111
- describe('context: model limits', () => {
112
- it('returns correct limit for known models', () => {
113
- expect(getContextLimit('claude-3.5-sonnet')).toBe(200_000);
114
- expect(getContextLimit('gpt-4o-mini')).toBe(128_000);
115
- expect(getContextLimit('gpt-3.5-turbo')).toBe(16_000);
116
- });
117
-
118
- it('returns default for unknown models', () => {
119
- expect(getContextLimit('llama-3-70b')).toBe(100_000);
120
- });
121
-
122
- it('matches gpt-4o before gpt-4 (prefix sort)', () => {
123
- expect(getContextLimit('gpt-4o-mini')).toBe(128_000);
124
- expect(getContextLimit('gpt-4o-2024-05-13')).toBe(128_000);
125
- expect(getContextLimit('gpt-4-turbo')).toBe(128_000);
126
- });
127
-
128
- it('matches gpt-5 models correctly', () => {
129
- expect(getContextLimit('gpt-5')).toBe(200_000);
130
- expect(getContextLimit('gpt-5.4')).toBe(200_000);
131
- });
132
-
133
- it('is case-insensitive', () => {
134
- expect(getContextLimit('Claude-3.5-Sonnet')).toBe(200_000);
135
- expect(getContextLimit('GPT-4O-MINI')).toBe(128_000);
136
- });
137
-
138
- it('needsCompact detects threshold breach', () => {
139
- const bigMsg = userMsg('x'.repeat(560_001));
140
- expect(needsCompact([bigMsg], '', 'claude-3.5-sonnet')).toBe(true);
141
-
142
- const smallMsg = userMsg('hello');
143
- expect(needsCompact([smallMsg], '', 'claude-3.5-sonnet')).toBe(false);
144
- });
145
- });
146
-
147
- // ---------------------------------------------------------------------------
148
- // truncateToolOutputs
149
- // ---------------------------------------------------------------------------
150
-
151
- describe('context: truncateToolOutputs', () => {
152
- it('truncates long tool outputs in non-last tool messages', () => {
153
- const msgs: AgentMessage[] = [
154
- userMsg('hello'),
155
- toolResultMsg('read_file', 'x'.repeat(5000)),
156
- assistantMsg('done'),
157
- toolResultMsg('read_file', 'y'.repeat(5000)), // last tool — kept intact
158
- ];
159
- const result = truncateToolOutputs(msgs);
160
- // First tool msg (idx 1) should be truncated (read_file limit = 2000)
161
- const firstTool = result[1] as any;
162
- expect(firstTool.content[0].text.length).toBeLessThan(5000);
163
- expect(firstTool.content[0].text).toContain('[...truncated');
164
- // Last tool msg (idx 3) should be untouched
165
- const lastTool = result[3] as any;
166
- expect(lastTool.content[0].text).toBe('y'.repeat(5000));
167
- });
168
-
169
- it('handles empty messages array', () => {
170
- expect(truncateToolOutputs([])).toEqual([]);
171
- });
172
-
173
- it('handles messages with no tool messages', () => {
174
- const msgs = [userMsg('hi'), assistantMsg('hello')];
175
- const result = truncateToolOutputs(msgs);
176
- expect(result).toEqual(msgs);
177
- });
178
-
179
- it('does not truncate short tool outputs', () => {
180
- const msgs: AgentMessage[] = [
181
- toolResultMsg('write_file', 'File written: test.md'),
182
- toolResultMsg('read_file', 'short content'),
183
- ];
184
- const result = truncateToolOutputs(msgs);
185
- // Last tool kept intact, first tool is short enough
186
- expect((result[0] as any).content[0].text).toBe('File written: test.md');
187
- });
188
- });
189
-
190
- // ---------------------------------------------------------------------------
191
- // hardPrune
192
- // ---------------------------------------------------------------------------
193
-
194
- describe('context: hardPrune', () => {
195
- it('returns messages unchanged when under threshold', () => {
196
- const msgs = [userMsg('hello'), assistantMsg('hi')];
197
- const result = hardPrune(msgs, '', 'claude');
198
- expect(result).toBe(msgs); // same reference — no pruning
199
- });
200
-
201
- it('prunes earliest messages when over 90% context', () => {
202
- const msgs: AgentMessage[] = [
203
- userMsg('a'.repeat(400_000)),
204
- assistantMsg('b'.repeat(400_000)),
205
- userMsg('c'.repeat(100)),
206
- assistantMsg('d'.repeat(100)),
207
- ];
208
- const result = hardPrune(msgs, '', 'claude');
209
- expect(result.length).toBeLessThan(msgs.length);
210
- });
211
-
212
- it('ensures first message is user role after pruning', () => {
213
- const msgs: AgentMessage[] = [
214
- userMsg('a'.repeat(100_000)),
215
- assistantMsg('b'.repeat(100_000)),
216
- assistantMsg('c'.repeat(1000)),
217
- userMsg('keep this'),
218
- assistantMsg('and this'),
219
- ];
220
- const result = hardPrune(msgs, '', 'gpt-3.5');
221
- expect(result.length).toBeGreaterThan(0);
222
- expect((result[0] as any).role).toBe('user');
223
- });
224
-
225
- it('skips toolResult messages that are orphaned after cut', () => {
226
- const msgs: AgentMessage[] = [
227
- userMsg('a'.repeat(100_000)),
228
- assistantMsg('b'.repeat(100_000)),
229
- toolResultMsg('read_file', 'x'.repeat(1000)),
230
- userMsg('keep'),
231
- assistantMsg('response'),
232
- ];
233
- const result = hardPrune(msgs, '', 'gpt-3.5');
234
- expect((result[0] as any).role).toBe('user');
235
- expect((result[0] as any).role).not.toBe('toolResult');
236
- });
237
-
238
- it('injects synthetic user message when no user message remains', () => {
239
- const msgs: AgentMessage[] = [
240
- userMsg('a'.repeat(200_000)),
241
- assistantMsg('b'.repeat(1000)),
242
- assistantMsg('c'.repeat(1000)),
243
- ];
244
- const result = hardPrune(msgs, '', 'gpt-3.5');
245
- expect((result[0] as any).role).toBe('user');
246
- const content = (result[0] as any).content;
247
- expect(typeof content === 'string' && content.includes('truncated')).toBe(true);
248
- });
249
-
250
- it('preserves messages when exactly at threshold', () => {
251
- const msgs = [userMsg('small'), assistantMsg('also small')];
252
- const result = hardPrune(msgs, '', 'claude');
253
- expect(result).toBe(msgs);
254
- });
255
- });
256
-
257
- // ---------------------------------------------------------------------------
258
- // compactMessages
259
- // ---------------------------------------------------------------------------
260
-
261
- describe('context: compactMessages', () => {
262
- beforeEach(() => {
263
- mockComplete.mockReset();
264
- });
265
-
266
- it('returns uncompacted when fewer than 6 messages', async () => {
267
- const msgs = [userMsg('a'), assistantMsg('b'), userMsg('c'), assistantMsg('d')];
268
- const result = await compactMessages(msgs, fakeModel, 'test-key', '', 'claude');
269
- expect(result.compacted).toBe(false);
270
- expect(result.messages).toBe(msgs);
271
- expect(mockComplete).not.toHaveBeenCalled();
272
- });
273
-
274
- it('returns uncompacted when early messages < 2', async () => {
275
- const msgs = [
276
- userMsg('only-early'),
277
- assistantMsg('r1'), userMsg('r2'), assistantMsg('r3'),
278
- userMsg('r4'), assistantMsg('r5'), userMsg('r6'),
279
- ];
280
- const result = await compactMessages(msgs, fakeModel, 'test-key', '', 'claude');
281
- expect(result.compacted).toBe(false);
282
- });
283
-
284
- it('prepends summary as separate user message when recentMessages[0] is assistant', async () => {
285
- mockComplete.mockResolvedValue({
286
- role: 'assistant',
287
- content: [{ type: 'text', text: 'Summary of conversation.' }],
288
- api: 'anthropic-messages', provider: 'anthropic', model: 'claude',
289
- usage: { inputTokens: 0, outputTokens: 0 },
290
- } as any);
291
-
292
- const msgs: AgentMessage[] = [
293
- userMsg('early1'),
294
- assistantMsg('early2'),
295
- assistantMsg('recent1'),
296
- userMsg('recent2'),
297
- assistantMsg('recent3'),
298
- userMsg('recent4'),
299
- assistantMsg('recent5'),
300
- userMsg('recent6'),
301
- ];
302
-
303
- const result = await compactMessages(msgs, fakeModel, 'test-key', '', 'claude');
304
- expect(result.compacted).toBe(true);
305
- expect(result.messages.length).toBe(7); // 1 summary + 6 recent
306
- expect((result.messages[0] as any).role).toBe('user');
307
- const content = (result.messages[0] as any).content;
308
- expect(typeof content === 'string').toBe(true);
309
- expect(content).toContain('Summary of conversation.');
310
- expect((result.messages[1] as any).role).toBe('assistant');
311
- });
312
-
313
- it('merges summary into first user message to avoid consecutive user→user', async () => {
314
- mockComplete.mockResolvedValue({
315
- role: 'assistant',
316
- content: [{ type: 'text', text: 'This is the summary.' }],
317
- api: 'anthropic-messages', provider: 'anthropic', model: 'claude',
318
- usage: { inputTokens: 0, outputTokens: 0 },
319
- } as any);
320
-
321
- const msgs: AgentMessage[] = [
322
- userMsg('early1'),
323
- assistantMsg('early2'),
324
- userMsg('recent-user-first'),
325
- assistantMsg('recent2'),
326
- userMsg('recent3'),
327
- assistantMsg('recent4'),
328
- userMsg('recent5'),
329
- assistantMsg('recent6'),
330
- ];
331
-
332
- const result = await compactMessages(msgs, fakeModel, 'test-key', '', 'claude');
333
- expect(result.compacted).toBe(true);
334
- expect(result.messages.length).toBe(6);
335
- expect((result.messages[0] as any).role).toBe('user');
336
- const content = (result.messages[0] as any).content;
337
- expect(content).toContain('This is the summary.');
338
- expect(content).toContain('recent-user-first');
339
- expect((result.messages[1] as any).role).toBe('assistant');
340
- });
341
-
342
- it('handles multimodal (array) content in first user message', async () => {
343
- mockComplete.mockResolvedValue({
344
- role: 'assistant',
345
- content: [{ type: 'text', text: 'Multimodal summary.' }],
346
- api: 'anthropic-messages', provider: 'anthropic', model: 'claude',
347
- usage: { inputTokens: 0, outputTokens: 0 },
348
- } as any);
349
-
350
- const multimodalUser: AgentMessage = {
351
- role: 'user',
352
- content: [
353
- { type: 'image', image: new Uint8Array(0), mimeType: 'image/png' },
354
- { type: 'text', text: 'describe this image' },
355
- ],
356
- timestamp: now,
357
- } as unknown as AgentMessage;
358
-
359
- const msgs: AgentMessage[] = [
360
- userMsg('early1'),
361
- assistantMsg('early2'),
362
- multimodalUser,
363
- assistantMsg('recent2'),
364
- userMsg('recent3'),
365
- assistantMsg('recent4'),
366
- userMsg('recent5'),
367
- assistantMsg('recent6'),
368
- ];
369
-
370
- const result = await compactMessages(msgs, fakeModel, 'test-key', '', 'claude');
371
- expect(result.compacted).toBe(true);
372
- expect(result.messages.length).toBe(6);
373
- expect((result.messages[0] as any).role).toBe('user');
374
- const content = (result.messages[0] as any).content;
375
- expect(Array.isArray(content)).toBe(true);
376
- const parts = content as any[];
377
- expect(parts[0].type).toBe('text');
378
- expect(parts[0].text).toContain('Multimodal summary.');
379
- });
380
-
381
- it('falls back to hardPrune when complete() throws, then throws if pruning insufficient', async () => {
382
- mockComplete.mockRejectedValue(new Error('API error'));
383
-
384
- const msgs: AgentMessage[] = [
385
- userMsg('early1'), assistantMsg('early2'),
386
- userMsg('recent1'), assistantMsg('recent2'),
387
- userMsg('recent3'), assistantMsg('recent4'),
388
- userMsg('recent5'), assistantMsg('recent6'),
389
- ];
390
-
391
- // Small messages on a large model — hardPrune won't help, so it throws
392
- await expect(
393
- compactMessages(msgs, fakeModel, 'test-key', '', 'claude'),
394
- ).rejects.toThrow('Context compaction failed');
395
- });
396
-
397
- it('does not split between assistant tool-call and tool result (M6)', async () => {
398
- mockComplete.mockResolvedValue({
399
- role: 'assistant',
400
- content: [{ type: 'text', text: 'Tool summary.' }],
401
- api: 'anthropic-messages', provider: 'anthropic', model: 'claude',
402
- usage: { inputTokens: 0, outputTokens: 0 },
403
- } as any);
404
-
405
- const msgs: AgentMessage[] = [
406
- userMsg('e1'),
407
- assistantMsg('e2'),
408
- userMsg('e3'),
409
- assistantWithToolCall('read_file'),
410
- toolResultMsg('read_file', 'file content here'),
411
- userMsg('r1'),
412
- assistantMsg('r2'),
413
- userMsg('r3'),
414
- assistantMsg('r4'),
415
- userMsg('r5'),
416
- ];
417
-
418
- const result = await compactMessages(msgs, fakeModel, 'test-key', '', 'claude');
419
- expect(result.compacted).toBe(true);
420
-
421
- for (let i = 0; i < result.messages.length; i++) {
422
- if ((result.messages[i] as any).role === 'toolResult') {
423
- expect(i).toBeGreaterThan(0);
424
- expect((result.messages[i - 1] as any).role).toBe('assistant');
425
- }
426
- }
1
+ import { describe, it, expect } from 'vitest';
2
+ import { estimateStringTokens } from '../../lib/agent/context';
3
+
4
+ describe('Context estimation', () => {
5
+ describe('estimateStringTokens', () => {
6
+ it('estimates ASCII text tokens (~0.25 per char)', () => {
7
+ const text = 'Hello, this is a test sentence with some words.';
8
+ const tokens = estimateStringTokens(text);
9
+ // ~48 chars → ~12 tokens
10
+ expect(tokens).toBeGreaterThan(5);
11
+ expect(tokens).toBeLessThan(25);
12
+ });
13
+
14
+ it('estimates CJK text tokens (~1.5 per char)', () => {
15
+ const text = '你好世界这是测试';
16
+ const tokens = estimateStringTokens(text);
17
+ // 8 CJK chars → ~12 tokens
18
+ expect(tokens).toBeGreaterThan(8);
19
+ expect(tokens).toBeLessThan(20);
20
+ });
21
+
22
+ it('handles mixed CJK and ASCII', () => {
23
+ const text = 'Hello 你好 World 世界';
24
+ const tokens = estimateStringTokens(text);
25
+ expect(tokens).toBeGreaterThan(5);
26
+ expect(tokens).toBeLessThan(20);
27
+ });
28
+
29
+ it('returns 0 for empty string', () => {
30
+ expect(estimateStringTokens('')).toBe(0);
31
+ });
32
+
33
+ it('estimates a typical system prompt size', () => {
34
+ // Simulate a moderate system prompt
35
+ const prompt = 'You are a helpful assistant.\n'.repeat(100);
36
+ const tokens = estimateStringTokens(prompt);
37
+ // ~2800 chars ~700 tokens
38
+ expect(tokens).toBeGreaterThan(500);
39
+ expect(tokens).toBeLessThan(1000);
40
+ });
41
+
42
+ it('estimates large content (20K chars) reasonably', () => {
43
+ const large = 'a'.repeat(20_000);
44
+ const tokens = estimateStringTokens(large);
45
+ // 20000 ASCII chars ~5000 tokens
46
+ expect(tokens).toBeGreaterThanOrEqual(4000);
47
+ expect(tokens).toBeLessThanOrEqual(6000);
48
+ });
427
49
  });
428
50
  });
@@ -38,13 +38,13 @@ describe('pi skill integration', () => {
38
38
  expect(result).toEqual({ name: 'test-skill', description: 'useful helper' });
39
39
  });
40
40
 
41
- it('scans MindOS skill directories with precedence and metadata', () => {
41
+ it('scans MindOS skill directories with precedence and metadata', async () => {
42
42
  writeSkill(path.join(projectRoot, 'app', 'data', 'skills'), 'mindos', '---\nname: mindos\ndescription: builtin\n---\n');
43
43
  writeSkill(path.join(projectRoot, 'skills'), 'project-helper', '---\nname: project-helper\ndescription: project builtin\n---\n');
44
44
  writeSkill(path.join(mindRoot, '.skills'), 'user-helper', '---\nname: user-helper\ndescription: user custom\n---\n');
45
45
  writeSkill(path.join(tempRoot, '.mindos', 'skills'), 'global-helper', '---\nname: global-helper\ndescription: global skill\n---\n');
46
46
 
47
- const skills = scanSkillDirs({ projectRoot, mindRoot, disabledSkills: ['project-helper'] });
47
+ const skills = await scanSkillDirs({ projectRoot, mindRoot, disabledSkills: ['project-helper'] });
48
48
 
49
49
  expect(skills.map((skill) => skill.name)).toEqual(['mindos', 'project-helper', 'user-helper', 'global-helper']);
50
50
  expect(skills.find((skill) => skill.name === 'mindos')).toMatchObject({ source: 'builtin', editable: false, origin: 'app-builtin', enabled: true });
@@ -53,11 +53,11 @@ describe('pi skill integration', () => {
53
53
  expect(skills.find((skill) => skill.name === 'global-helper')).toMatchObject({ source: 'user', editable: true, origin: 'mindos-global', enabled: true });
54
54
  });
55
55
 
56
- it('knowledge base skill takes precedence over global skill with same name', () => {
56
+ it('knowledge base skill takes precedence over global skill with same name', async () => {
57
57
  writeSkill(path.join(mindRoot, '.skills'), 'shared-skill', '---\nname: shared-skill\ndescription: from kb\n---\n');
58
58
  writeSkill(path.join(tempRoot, '.mindos', 'skills'), 'shared-skill', '---\nname: shared-skill\ndescription: from global\n---\n');
59
59
 
60
- const skills = scanSkillDirs({ projectRoot, mindRoot });
60
+ const skills = await scanSkillDirs({ projectRoot, mindRoot });
61
61
  const matched = skills.filter((s) => s.name === 'shared-skill');
62
62
  expect(matched).toHaveLength(1);
63
63
  expect(matched[0].origin).toBe('mindos-user');
@@ -6,8 +6,11 @@ describe('isAiConfiguredForAsk', () => {
6
6
  expect(
7
7
  isAiConfiguredForAsk({
8
8
  ai: {
9
- provider: 'anthropic',
10
- providers: { anthropic: { apiKey: '***set***' }, openai: { apiKey: '' } },
9
+ activeProvider: 'p_anthro01',
10
+ providers: [
11
+ { id: 'p_anthro01', name: 'Anthropic', protocol: 'anthropic', apiKey: 'sk-ant-test', model: 'claude-sonnet-4-6', baseUrl: '' },
12
+ { id: 'p_openai01', name: 'OpenAI', protocol: 'openai', apiKey: '', model: 'gpt-5.4', baseUrl: '' },
13
+ ],
11
14
  },
12
15
  envOverrides: {},
13
16
  }),
@@ -18,8 +21,11 @@ describe('isAiConfiguredForAsk', () => {
18
21
  expect(
19
22
  isAiConfiguredForAsk({
20
23
  ai: {
21
- provider: 'anthropic',
22
- providers: { anthropic: { apiKey: '' }, openai: { apiKey: '' } },
24
+ activeProvider: 'p_anthro01',
25
+ providers: [
26
+ { id: 'p_anthro01', name: 'Anthropic', protocol: 'anthropic', apiKey: '', model: 'claude-sonnet-4-6', baseUrl: '' },
27
+ { id: 'p_openai01', name: 'OpenAI', protocol: 'openai', apiKey: '', model: 'gpt-5.4', baseUrl: '' },
28
+ ],
23
29
  },
24
30
  envOverrides: { ANTHROPIC_API_KEY: true },
25
31
  }),
@@ -30,8 +36,11 @@ describe('isAiConfiguredForAsk', () => {
30
36
  expect(
31
37
  isAiConfiguredForAsk({
32
38
  ai: {
33
- provider: 'anthropic',
34
- providers: { anthropic: { apiKey: '' }, openai: { apiKey: '***set***' } },
39
+ activeProvider: 'p_anthro01',
40
+ providers: [
41
+ { id: 'p_anthro01', name: 'Anthropic', protocol: 'anthropic', apiKey: '', model: 'claude-sonnet-4-6', baseUrl: '' },
42
+ { id: 'p_openai01', name: 'OpenAI', protocol: 'openai', apiKey: 'sk-openai-test', model: 'gpt-5.4', baseUrl: '' },
43
+ ],
35
44
  },
36
45
  envOverrides: {},
37
46
  }),
@@ -42,8 +51,11 @@ describe('isAiConfiguredForAsk', () => {
42
51
  expect(
43
52
  isAiConfiguredForAsk({
44
53
  ai: {
45
- provider: 'openai',
46
- providers: { anthropic: { apiKey: '' }, openai: { apiKey: '***set***' } },
54
+ activeProvider: 'p_openai01',
55
+ providers: [
56
+ { id: 'p_anthro01', name: 'Anthropic', protocol: 'anthropic', apiKey: '', model: 'claude-sonnet-4-6', baseUrl: '' },
57
+ { id: 'p_openai01', name: 'OpenAI', protocol: 'openai', apiKey: 'sk-openai-test', model: 'gpt-5.4', baseUrl: '' },
58
+ ],
47
59
  },
48
60
  envOverrides: {},
49
61
  }),
@@ -54,18 +66,26 @@ describe('isAiConfiguredForAsk', () => {
54
66
  expect(
55
67
  isAiConfiguredForAsk({
56
68
  ai: {
57
- provider: 'openai',
58
- providers: { anthropic: { apiKey: '' }, openai: { apiKey: '' } },
69
+ activeProvider: 'p_openai01',
70
+ providers: [
71
+ { id: 'p_anthro01', name: 'Anthropic', protocol: 'anthropic', apiKey: '', model: 'claude-sonnet-4-6', baseUrl: '' },
72
+ { id: 'p_openai01', name: 'OpenAI', protocol: 'openai', apiKey: '', model: 'gpt-5.4', baseUrl: '' },
73
+ ],
59
74
  },
60
75
  envOverrides: { OPENAI_API_KEY: true },
61
76
  }),
62
77
  ).toBe(true);
63
78
  });
64
79
 
65
- it('treats missing provider as anthropic (error path)', () => {
80
+ it('treats missing provider as first entry fallback (error path)', () => {
66
81
  expect(
67
82
  isAiConfiguredForAsk({
68
- ai: { providers: { anthropic: { apiKey: '' }, openai: { apiKey: '' } } },
83
+ ai: {
84
+ providers: [
85
+ { id: 'p_anthro01', name: 'Anthropic', protocol: 'anthropic', apiKey: '', model: 'claude-sonnet-4-6', baseUrl: '' },
86
+ { id: 'p_openai01', name: 'OpenAI', protocol: 'openai', apiKey: '', model: 'gpt-5.4', baseUrl: '' },
87
+ ],
88
+ },
69
89
  envOverrides: {},
70
90
  }),
71
91
  ).toBe(false);
@@ -23,11 +23,11 @@ export function getTestMindRoot() {
23
23
  vi.mock('@/lib/settings', () => ({
24
24
  readSettings: () => ({
25
25
  ai: {
26
- provider: 'anthropic' as const,
27
- providers: {
28
- anthropic: { apiKey: '', model: 'claude-sonnet-4-6' },
29
- openai: { apiKey: '', model: 'gpt-5.4', baseUrl: '' },
30
- },
26
+ activeProvider: 'p_anthro01',
27
+ providers: [
28
+ { id: 'p_anthro01', name: 'Anthropic', protocol: 'anthropic', apiKey: '', model: 'claude-sonnet-4-6', baseUrl: '' },
29
+ { id: 'p_openai01', name: 'OpenAI', protocol: 'openai', apiKey: '', model: 'gpt-5.4', baseUrl: '' },
30
+ ],
31
31
  },
32
32
  mindRoot: '',
33
33
  }),
@@ -70,7 +70,7 @@ body {
70
70
  --muted-foreground: #685f52;
71
71
  --accent: #d9d3c6;
72
72
  --accent-foreground: #1c1a17;
73
- --destructive: oklch(0.56 0.14 24);
73
+ --destructive: #b33b3b;
74
74
  --destructive-foreground: #ffffff;
75
75
  --border: rgba(28, 26, 23, 0.1);
76
76
  --input: rgba(28, 26, 23, 0.12);
@@ -108,7 +108,7 @@ body {
108
108
  --muted-foreground: #8a8275;
109
109
  --accent: #2e2b22;
110
110
  --accent-foreground: #e8e4dc;
111
- --destructive: oklch(0.56 0.14 22);
111
+ --destructive: #b33636;
112
112
  --destructive-foreground: #ffffff;
113
113
  --border: rgba(232, 228, 220, 0.08);
114
114
  --input: rgba(232, 228, 220, 0.1);
@@ -320,8 +320,8 @@ body {
320
320
 
321
321
  .modal-backdrop {
322
322
  background: rgba(10, 9, 6, 0.72);
323
- backdrop-filter: blur(8px);
324
323
  -webkit-backdrop-filter: blur(8px);
324
+ backdrop-filter: blur(8px);
325
325
  }
326
326
  .dark .modal-backdrop {
327
327
  background: rgba(0, 0, 0, 0.65);
@@ -329,8 +329,8 @@ body {
329
329
 
330
330
  .overlay-backdrop {
331
331
  background: rgba(10, 9, 6, 0.35);
332
- backdrop-filter: blur(2px);
333
332
  -webkit-backdrop-filter: blur(2px);
333
+ backdrop-filter: blur(2px);
334
334
  }
335
335
  .dark .overlay-backdrop {
336
336
  background: rgba(0, 0, 0, 0.4);