@geminilight/mindos 0.6.73 → 0.6.75

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 (257) hide show
  1. package/_standalone/.mindos-build-version +1 -1
  2. package/_standalone/.next/BUILD_ID +1 -1
  3. package/_standalone/.next/app-path-routes-manifest.json +30 -30
  4. package/_standalone/.next/build-manifest.json +3 -3
  5. package/_standalone/.next/cache/.previewinfo +1 -1
  6. package/_standalone/.next/cache/.rscinfo +1 -1
  7. package/_standalone/.next/cache/config.json +3 -3
  8. package/_standalone/.next/prerender-manifest.json +3 -3
  9. package/_standalone/.next/react-loadable-manifest.json +5 -5
  10. package/_standalone/.next/required-server-files.json +14 -1
  11. package/_standalone/.next/server/app/.well-known/agent-card.json/route_client-reference-manifest.js +1 -1
  12. package/_standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  13. package/_standalone/.next/server/app/_global-error.html +2 -2
  14. package/_standalone/.next/server/app/_global-error.rsc +1 -1
  15. package/_standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  16. package/_standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  17. package/_standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  18. package/_standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  19. package/_standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  20. package/_standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  21. package/_standalone/.next/server/app/_not-found/page.js +1 -1
  22. package/_standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  23. package/_standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  24. package/_standalone/.next/server/app/agents/[agentKey]/page.js +1 -1
  25. package/_standalone/.next/server/app/agents/[agentKey]/page.js.nft.json +1 -1
  26. package/_standalone/.next/server/app/agents/[agentKey]/page_client-reference-manifest.js +1 -1
  27. package/_standalone/.next/server/app/agents/page.js +1 -1
  28. package/_standalone/.next/server/app/agents/page.js.nft.json +1 -1
  29. package/_standalone/.next/server/app/agents/page_client-reference-manifest.js +1 -1
  30. package/_standalone/.next/server/app/api/a2a/agents/route_client-reference-manifest.js +1 -1
  31. package/_standalone/.next/server/app/api/a2a/delegations/route_client-reference-manifest.js +1 -1
  32. package/_standalone/.next/server/app/api/a2a/discover/route_client-reference-manifest.js +1 -1
  33. package/_standalone/.next/server/app/api/a2a/route_client-reference-manifest.js +1 -1
  34. package/_standalone/.next/server/app/api/acp/config/route_client-reference-manifest.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_client-reference-manifest.js +1 -1
  38. package/_standalone/.next/server/app/api/acp/session/route_client-reference-manifest.js +1 -1
  39. package/_standalone/.next/server/app/api/agent-activity/route_client-reference-manifest.js +1 -1
  40. package/_standalone/.next/server/app/api/agents/copy-skill/route.js.nft.json +1 -1
  41. package/_standalone/.next/server/app/api/agents/copy-skill/route_client-reference-manifest.js +1 -1
  42. package/_standalone/.next/server/app/api/agents/custom/detect/route_client-reference-manifest.js +1 -1
  43. package/_standalone/.next/server/app/api/agents/custom/route_client-reference-manifest.js +1 -1
  44. package/_standalone/.next/server/app/api/ask/route.js +1 -1
  45. package/_standalone/.next/server/app/api/ask/route.js.nft.json +1 -1
  46. package/_standalone/.next/server/app/api/ask/route_client-reference-manifest.js +1 -1
  47. package/_standalone/.next/server/app/api/ask-sessions/route_client-reference-manifest.js +1 -1
  48. package/_standalone/.next/server/app/api/auth/route_client-reference-manifest.js +1 -1
  49. package/_standalone/.next/server/app/api/backlinks/route.js.nft.json +1 -1
  50. package/_standalone/.next/server/app/api/backlinks/route_client-reference-manifest.js +1 -1
  51. package/_standalone/.next/server/app/api/bootstrap/route.js.nft.json +1 -1
  52. package/_standalone/.next/server/app/api/bootstrap/route_client-reference-manifest.js +1 -1
  53. package/_standalone/.next/server/app/api/changes/route.js.nft.json +1 -1
  54. package/_standalone/.next/server/app/api/changes/route_client-reference-manifest.js +1 -1
  55. package/_standalone/.next/server/app/api/channels/verify/route.js.nft.json +1 -1
  56. package/_standalone/.next/server/app/api/channels/verify/route_client-reference-manifest.js +1 -1
  57. package/_standalone/.next/server/app/api/connect/route_client-reference-manifest.js +1 -1
  58. package/_standalone/.next/server/app/api/embedding/route.js.nft.json +1 -1
  59. package/_standalone/.next/server/app/api/embedding/route_client-reference-manifest.js +1 -1
  60. package/_standalone/.next/server/app/api/export/route.js.nft.json +1 -1
  61. package/_standalone/.next/server/app/api/export/route_client-reference-manifest.js +1 -1
  62. package/_standalone/.next/server/app/api/extract-pdf/route.js.nft.json +1 -1
  63. package/_standalone/.next/server/app/api/extract-pdf/route_client-reference-manifest.js +1 -1
  64. package/_standalone/.next/server/app/api/file/import/route.js.nft.json +1 -1
  65. package/_standalone/.next/server/app/api/file/import/route_client-reference-manifest.js +1 -1
  66. package/_standalone/.next/server/app/api/file/raw/route.js.nft.json +1 -1
  67. package/_standalone/.next/server/app/api/file/raw/route_client-reference-manifest.js +1 -1
  68. package/_standalone/.next/server/app/api/file/route.js.nft.json +1 -1
  69. package/_standalone/.next/server/app/api/file/route_client-reference-manifest.js +1 -1
  70. package/_standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  71. package/_standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  72. package/_standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  73. package/_standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  74. package/_standalone/.next/server/app/api/graph/route.js.nft.json +1 -1
  75. package/_standalone/.next/server/app/api/graph/route_client-reference-manifest.js +1 -1
  76. package/_standalone/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
  77. package/_standalone/.next/server/app/api/im/activity/route_client-reference-manifest.js +1 -1
  78. package/_standalone/.next/server/app/api/im/config/route.js +1 -1
  79. package/_standalone/.next/server/app/api/im/config/route_client-reference-manifest.js +1 -1
  80. package/_standalone/.next/server/app/api/im/status/route.js +1 -7
  81. package/_standalone/.next/server/app/api/im/status/route.js.nft.json +1 -1
  82. package/_standalone/.next/server/app/api/im/status/route_client-reference-manifest.js +1 -1
  83. package/_standalone/.next/server/app/api/im/test/route.js.nft.json +1 -1
  84. package/_standalone/.next/server/app/api/im/test/route_client-reference-manifest.js +1 -1
  85. package/_standalone/.next/server/app/api/im/webhook/feishu/route.js +1 -7
  86. package/_standalone/.next/server/app/api/im/webhook/feishu/route.js.nft.json +1 -1
  87. package/_standalone/.next/server/app/api/im/webhook/feishu/route_client-reference-manifest.js +1 -1
  88. package/_standalone/.next/server/app/api/im/webhook-status/route.js +1 -7
  89. package/_standalone/.next/server/app/api/im/webhook-status/route.js.nft.json +1 -1
  90. package/_standalone/.next/server/app/api/im/webhook-status/route_client-reference-manifest.js +1 -1
  91. package/_standalone/.next/server/app/api/inbox/clip/route.js.nft.json +1 -1
  92. package/_standalone/.next/server/app/api/inbox/clip/route_client-reference-manifest.js +1 -1
  93. package/_standalone/.next/server/app/api/inbox/route.js.nft.json +1 -1
  94. package/_standalone/.next/server/app/api/inbox/route_client-reference-manifest.js +1 -1
  95. package/_standalone/.next/server/app/api/init/route.js.nft.json +1 -1
  96. package/_standalone/.next/server/app/api/init/route_client-reference-manifest.js +1 -1
  97. package/_standalone/.next/server/app/api/lint/route.js.nft.json +1 -1
  98. package/_standalone/.next/server/app/api/lint/route_client-reference-manifest.js +1 -1
  99. package/_standalone/.next/server/app/api/mcp/agents/route.js.nft.json +1 -1
  100. package/_standalone/.next/server/app/api/mcp/agents/route_client-reference-manifest.js +1 -1
  101. package/_standalone/.next/server/app/api/mcp/direct-tools/route_client-reference-manifest.js +1 -1
  102. package/_standalone/.next/server/app/api/mcp/install/route_client-reference-manifest.js +1 -1
  103. package/_standalone/.next/server/app/api/mcp/install-skill/route_client-reference-manifest.js +1 -1
  104. package/_standalone/.next/server/app/api/mcp/restart/route_client-reference-manifest.js +1 -1
  105. package/_standalone/.next/server/app/api/mcp/status/route_client-reference-manifest.js +1 -1
  106. package/_standalone/.next/server/app/api/mcp/tools/route_client-reference-manifest.js +1 -1
  107. package/_standalone/.next/server/app/api/mcp/uninstall/route_client-reference-manifest.js +1 -1
  108. package/_standalone/.next/server/app/api/monitoring/route.js.nft.json +1 -1
  109. package/_standalone/.next/server/app/api/monitoring/route_client-reference-manifest.js +1 -1
  110. package/_standalone/.next/server/app/api/recent-files/route.js.nft.json +1 -1
  111. package/_standalone/.next/server/app/api/recent-files/route_client-reference-manifest.js +1 -1
  112. package/_standalone/.next/server/app/api/restart/route_client-reference-manifest.js +1 -1
  113. package/_standalone/.next/server/app/api/search/route.js.nft.json +1 -1
  114. package/_standalone/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
  115. package/_standalone/.next/server/app/api/settings/list-models/route_client-reference-manifest.js +1 -1
  116. package/_standalone/.next/server/app/api/settings/reset-token/route_client-reference-manifest.js +1 -1
  117. package/_standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
  118. package/_standalone/.next/server/app/api/settings/route_client-reference-manifest.js +1 -1
  119. package/_standalone/.next/server/app/api/settings/test-key/route_client-reference-manifest.js +1 -1
  120. package/_standalone/.next/server/app/api/setup/check-path/route_client-reference-manifest.js +1 -1
  121. package/_standalone/.next/server/app/api/setup/check-port/route_client-reference-manifest.js +1 -1
  122. package/_standalone/.next/server/app/api/setup/generate-token/route_client-reference-manifest.js +1 -1
  123. package/_standalone/.next/server/app/api/setup/ls/route_client-reference-manifest.js +1 -1
  124. package/_standalone/.next/server/app/api/setup/route_client-reference-manifest.js +1 -1
  125. package/_standalone/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  126. package/_standalone/.next/server/app/api/space-overview/route.js.nft.json +1 -1
  127. package/_standalone/.next/server/app/api/space-overview/route_client-reference-manifest.js +1 -1
  128. package/_standalone/.next/server/app/api/sync/route_client-reference-manifest.js +1 -1
  129. package/_standalone/.next/server/app/api/tree-version/route.js.nft.json +1 -1
  130. package/_standalone/.next/server/app/api/tree-version/route_client-reference-manifest.js +1 -1
  131. package/_standalone/.next/server/app/api/uninstall/route_client-reference-manifest.js +1 -1
  132. package/_standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  133. package/_standalone/.next/server/app/api/update-check/route_client-reference-manifest.js +1 -1
  134. package/_standalone/.next/server/app/api/update-status/route_client-reference-manifest.js +1 -1
  135. package/_standalone/.next/server/app/api/workflows/route.js.nft.json +1 -1
  136. package/_standalone/.next/server/app/api/workflows/route_client-reference-manifest.js +1 -1
  137. package/_standalone/.next/server/app/changelog/page.js +1 -1
  138. package/_standalone/.next/server/app/changelog/page.js.nft.json +1 -1
  139. package/_standalone/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
  140. package/_standalone/.next/server/app/changes/page.js +1 -1
  141. package/_standalone/.next/server/app/changes/page.js.nft.json +1 -1
  142. package/_standalone/.next/server/app/changes/page_client-reference-manifest.js +1 -1
  143. package/_standalone/.next/server/app/echo/[segment]/page.js +1 -1
  144. package/_standalone/.next/server/app/echo/[segment]/page.js.nft.json +1 -1
  145. package/_standalone/.next/server/app/echo/[segment]/page_client-reference-manifest.js +1 -1
  146. package/_standalone/.next/server/app/echo/page.js +1 -1
  147. package/_standalone/.next/server/app/echo/page.js.nft.json +1 -1
  148. package/_standalone/.next/server/app/echo/page_client-reference-manifest.js +1 -1
  149. package/_standalone/.next/server/app/explore/page.js +1 -1
  150. package/_standalone/.next/server/app/explore/page.js.nft.json +1 -1
  151. package/_standalone/.next/server/app/explore/page_client-reference-manifest.js +1 -1
  152. package/_standalone/.next/server/app/help/page.js +1 -1
  153. package/_standalone/.next/server/app/help/page.js.nft.json +1 -1
  154. package/_standalone/.next/server/app/help/page_client-reference-manifest.js +1 -1
  155. package/_standalone/.next/server/app/inbox/history/page.js +1 -1
  156. package/_standalone/.next/server/app/inbox/history/page.js.nft.json +1 -1
  157. package/_standalone/.next/server/app/inbox/history/page_client-reference-manifest.js +1 -1
  158. package/_standalone/.next/server/app/login/page.js +1 -1
  159. package/_standalone/.next/server/app/login/page.js.nft.json +1 -1
  160. package/_standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
  161. package/_standalone/.next/server/app/page.js +1 -1
  162. package/_standalone/.next/server/app/page.js.nft.json +1 -1
  163. package/_standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  164. package/_standalone/.next/server/app/setup/page.js +2 -2
  165. package/_standalone/.next/server/app/setup/page.js.nft.json +1 -1
  166. package/_standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  167. package/_standalone/.next/server/app/trash/page.js +3 -3
  168. package/_standalone/.next/server/app/trash/page.js.nft.json +1 -1
  169. package/_standalone/.next/server/app/trash/page_client-reference-manifest.js +1 -1
  170. package/_standalone/.next/server/app/view/[...path]/page.js +3 -3
  171. package/_standalone/.next/server/app/view/[...path]/page.js.nft.json +1 -1
  172. package/_standalone/.next/server/app/view/[...path]/page_client-reference-manifest.js +1 -1
  173. package/_standalone/.next/server/app-paths-manifest.json +30 -30
  174. package/_standalone/.next/server/chunks/1076.js +1 -1
  175. package/_standalone/.next/server/chunks/{2449.js → 2424.js} +2 -2
  176. package/_standalone/.next/server/chunks/2792.js +1 -0
  177. package/_standalone/.next/server/chunks/2885.js +7 -0
  178. package/_standalone/.next/server/chunks/3800.js +1 -1
  179. package/_standalone/.next/server/chunks/5299.js +1 -1
  180. package/_standalone/.next/server/chunks/5464.js +1 -1
  181. package/_standalone/.next/server/chunks/6022.js +28 -28
  182. package/_standalone/.next/server/chunks/6539.js +1 -1
  183. package/_standalone/.next/server/chunks/8388.js +2 -2
  184. package/_standalone/.next/server/middleware-build-manifest.js +1 -1
  185. package/_standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  186. package/_standalone/.next/server/pages/500.html +2 -2
  187. package/_standalone/.next/server/server-reference-manifest.js +1 -1
  188. package/_standalone/.next/server/server-reference-manifest.json +1 -1
  189. package/_standalone/.next/static/chunks/{5331.c89084fd7f67887d.js → 4342.ccc18d7a45f204a4.js} +5 -5
  190. package/_standalone/.next/static/chunks/{4094.09364c01df411380.js → 4496.f77a677ac2034e48.js} +1 -1
  191. package/_standalone/.next/static/chunks/{5358-15618fc9b20018ee.js → 5358-15ceeb8c1cd9889d.js} +6 -6
  192. package/_standalone/.next/static/chunks/{6902-edc5c487c696bd0b.js → 6902-53a31f849968ddf8.js} +1 -1
  193. package/_standalone/.next/static/chunks/9207-456dc6941d557f33.js +1 -0
  194. package/_standalone/.next/static/chunks/app/{layout-fcbde5bee626d21a.js → layout-09f08f52e3cbfc47.js} +66 -66
  195. package/_standalone/.next/static/chunks/app/setup/{page-e40f3c5704c3c857.js → page-64d7fa097ef3f367.js} +1 -1
  196. package/_standalone/.next/static/chunks/app/trash/page-8e0ea3ba71702757.js +1 -0
  197. package/_standalone/.next/static/chunks/app/view/[...path]/page-04f43178679a8bfb.js +12 -0
  198. package/_standalone/.next/static/chunks/{webpack-dc486b68118d1328.js → webpack-ec9c6076012b75f3.js} +1 -1
  199. package/_standalone/.next/trace +72 -72
  200. package/_standalone/__tests__/agent/provider-presets.test.ts +32 -0
  201. package/_standalone/__tests__/api/im-config-feishu-conversation.test.ts +39 -0
  202. package/_standalone/__tests__/api/im-webhook-feishu.test.ts +21 -12
  203. package/_standalone/__tests__/api/pi-subagents.test.ts +81 -0
  204. package/_standalone/__tests__/ask/file-chip-variants.test.tsx +18 -0
  205. package/_standalone/__tests__/ask/message-list-agent-attribution.test.tsx +2 -0
  206. package/_standalone/__tests__/im/feishu-dispatcher.test.ts +151 -0
  207. package/_standalone/__tests__/im/feishu-webhook.test.ts +85 -57
  208. package/_standalone/__tests__/im/feishu-ws-client.test.ts +91 -0
  209. package/_standalone/__tests__/settings/custom-provider-form.test.ts +30 -0
  210. package/_standalone/components/ask/FileChip.tsx +1 -1
  211. package/_standalone/components/ask/MessageList.tsx +2 -2
  212. package/_standalone/components/settings/CustomProviderFields.tsx +1 -1
  213. package/_standalone/components/settings/CustomProvidersCard.tsx +3 -0
  214. package/_standalone/components/settings/useCustomProviderForm.ts +78 -24
  215. package/_standalone/components/shared/ProviderSelect.tsx +6 -5
  216. package/_standalone/next.config.ts +28 -4
  217. package/_standalone/package-lock.json +20 -2
  218. package/_standalone/package.json +3 -1
  219. package/_standalone/scripts/feishu-long-connection.ts +32 -0
  220. package/_standalone/server.js +1 -1
  221. package/_standalone/tsconfig.tsbuildinfo +1 -1
  222. package/app/__tests__/agent/provider-presets.test.ts +32 -0
  223. package/app/__tests__/api/im-config-feishu-conversation.test.ts +39 -0
  224. package/app/__tests__/api/im-webhook-feishu.test.ts +21 -12
  225. package/app/__tests__/api/pi-subagents.test.ts +81 -0
  226. package/app/__tests__/ask/file-chip-variants.test.tsx +18 -0
  227. package/app/__tests__/ask/message-list-agent-attribution.test.tsx +2 -0
  228. package/app/__tests__/im/feishu-dispatcher.test.ts +151 -0
  229. package/app/__tests__/im/feishu-webhook.test.ts +85 -57
  230. package/app/__tests__/im/feishu-ws-client.test.ts +91 -0
  231. package/app/__tests__/settings/custom-provider-form.test.ts +30 -0
  232. package/app/app/api/ask/route.ts +2 -0
  233. package/app/app/api/im/config/route.ts +2 -0
  234. package/app/app/api/im/webhook/feishu/route.ts +4 -2
  235. package/app/components/ask/FileChip.tsx +1 -1
  236. package/app/components/ask/MessageList.tsx +2 -2
  237. package/app/components/settings/CustomProviderFields.tsx +1 -1
  238. package/app/components/settings/CustomProvidersCard.tsx +3 -0
  239. package/app/components/settings/useCustomProviderForm.ts +78 -24
  240. package/app/components/shared/ProviderSelect.tsx +6 -5
  241. package/app/lib/agent/providers.ts +44 -6
  242. package/app/lib/im/feishu-dispatcher.ts +111 -0
  243. package/app/lib/im/feishu-ws-client.ts +72 -0
  244. package/app/lib/im/types.ts +14 -0
  245. package/app/lib/im/webhook/feishu.ts +44 -55
  246. package/app/next.config.ts +28 -4
  247. package/app/package.json +3 -1
  248. package/app/scripts/feishu-long-connection.ts +32 -0
  249. package/bin/cli.js +3 -1
  250. package/bin/commands/feishu-ws.js +39 -0
  251. package/package.json +1 -1
  252. package/scripts/build-runtime-archive.sh +36 -2
  253. package/_standalone/.next/static/chunks/9207-3b19c55a3c974a09.js +0 -1
  254. package/_standalone/.next/static/chunks/app/trash/page-e623ff0ab35de002.js +0 -1
  255. package/_standalone/.next/static/chunks/app/view/[...path]/page-49c4eff6ffdb5168.js +0 -12
  256. /package/_standalone/.next/static/{Dn8EHqUedSzanCfrM8WWS → dZDCx13MSM8QVQk2QNRs8}/_buildManifest.js +0 -0
  257. /package/_standalone/.next/static/{Dn8EHqUedSzanCfrM8WWS → dZDCx13MSM8QVQk2QNRs8}/_ssgManifest.js +0 -0
@@ -0,0 +1,32 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { PROVIDER_PRESETS, getDefaultBaseUrl, groupedProviders } from '@/lib/agent/providers';
3
+
4
+
5
+ describe('provider presets', () => {
6
+ it('includes LM Studio as an explicit local preset', () => {
7
+ const preset = PROVIDER_PRESETS['lm-studio'];
8
+ expect(preset.name).toBe('LM Studio');
9
+ expect(preset.fixedBaseUrl).toBe('http://localhost:1234/v1');
10
+ expect(preset.apiKeyFallback).toBe('lm-studio');
11
+ expect(preset.supportsListModels).toBe(true);
12
+ });
13
+
14
+ it('resolves LM Studio default base URL from its fixed preset URL', () => {
15
+ expect(getDefaultBaseUrl('lm-studio')).toBe('http://localhost:1234/v1');
16
+ });
17
+
18
+ it('includes vLLM as an explicit local preset', () => {
19
+ const preset = PROVIDER_PRESETS.vllm;
20
+ expect(preset.name).toBe('vLLM');
21
+ expect(preset.fixedBaseUrl).toBe('http://localhost:8000/v1');
22
+ expect(preset.apiKeyFallback).toBe('vllm');
23
+ expect(preset.supportsListModels).toBe(true);
24
+ });
25
+
26
+ it('groups Ollama, LM Studio, and vLLM under local providers', () => {
27
+ const groups = groupedProviders();
28
+ expect(groups.local).toContain('ollama');
29
+ expect(groups.local).toContain('lm-studio');
30
+ expect(groups.local).toContain('vllm');
31
+ });
32
+ });
@@ -100,4 +100,43 @@ describe('/api/im/config Feishu conversation', () => {
100
100
  }),
101
101
  }));
102
102
  });
103
+
104
+ it('stores transport mode for Feishu conversation settings', async () => {
105
+ readIMConfig.mockReturnValue({
106
+ providers: {
107
+ feishu: {
108
+ app_id: 'cli_xxx',
109
+ app_secret: 'secret',
110
+ conversation: {
111
+ enabled: true,
112
+ transport: 'webhook',
113
+ allow_group_mentions: true,
114
+ },
115
+ },
116
+ },
117
+ });
118
+ const { PUT } = await importRoute();
119
+ const req = new NextRequest('http://localhost/api/im/config', {
120
+ method: 'PUT',
121
+ body: JSON.stringify({
122
+ platform: 'feishu',
123
+ conversation: {
124
+ enabled: true,
125
+ transport: 'long_connection',
126
+ },
127
+ }),
128
+ });
129
+
130
+ const res = await PUT(req);
131
+ expect(res.status).toBe(200);
132
+ expect(writeIMConfig).toHaveBeenCalledWith(expect.objectContaining({
133
+ providers: expect.objectContaining({
134
+ feishu: expect.objectContaining({
135
+ conversation: expect.objectContaining({
136
+ transport: 'long_connection',
137
+ }),
138
+ }),
139
+ }),
140
+ }));
141
+ });
103
142
  });
@@ -2,14 +2,14 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
2
  import { NextRequest } from 'next/server';
3
3
 
4
4
  const getPlatformConfig = vi.fn();
5
- const handleFeishuWebhook = vi.fn();
5
+ const dispatchFeishuWebhook = vi.fn();
6
6
 
7
7
  vi.mock('@/lib/im/config', () => ({
8
8
  getPlatformConfig,
9
9
  }));
10
10
 
11
- vi.mock('@/lib/im/webhook/feishu', () => ({
12
- handleFeishuWebhook,
11
+ vi.mock('@/lib/im/feishu-dispatcher', () => ({
12
+ dispatchFeishuWebhook,
13
13
  }));
14
14
 
15
15
  async function importRoute() {
@@ -36,15 +36,14 @@ describe('POST /api/im/webhook/feishu', () => {
36
36
  expect(await res.json()).toEqual({ error: 'Feishu is not configured' });
37
37
  });
38
38
 
39
- it('returns challenge response when webhook helper resolves a challenge', async () => {
39
+ it('delegates webhook protocol handling to the SDK dispatcher wrapper', async () => {
40
40
  getPlatformConfig.mockReturnValue({
41
41
  app_id: 'cli_xxx',
42
42
  app_secret: 'secret',
43
43
  conversation: { enabled: true, encrypt_key: 'encrypt', public_base_url: 'https://mindos.example.com' },
44
44
  });
45
45
 
46
- handleFeishuWebhook.mockResolvedValue({
47
- kind: 'challenge',
46
+ dispatchFeishuWebhook.mockResolvedValue({
48
47
  body: { challenge: 'challenge-token' },
49
48
  status: 200,
50
49
  });
@@ -53,24 +52,34 @@ describe('POST /api/im/webhook/feishu', () => {
53
52
  const req = new NextRequest('http://localhost/api/im/webhook/feishu', {
54
53
  method: 'POST',
55
54
  body: JSON.stringify({ challenge: 'challenge-token' }),
56
- headers: { 'content-type': 'application/json' },
55
+ headers: {
56
+ 'content-type': 'application/json',
57
+ 'x-lark-request-timestamp': '1',
58
+ },
57
59
  });
58
60
 
59
61
  const res = await POST(req);
62
+ expect(dispatchFeishuWebhook).toHaveBeenCalledWith({
63
+ config: expect.objectContaining({ app_id: 'cli_xxx' }),
64
+ body: { challenge: 'challenge-token' },
65
+ headers: expect.objectContaining({
66
+ 'content-type': 'application/json',
67
+ 'x-lark-request-timestamp': '1',
68
+ }),
69
+ });
60
70
  expect(res.status).toBe(200);
61
71
  expect(await res.json()).toEqual({ challenge: 'challenge-token' });
62
72
  });
63
73
 
64
- it('returns accepted when webhook helper accepts an inbound event for async processing', async () => {
74
+ it('returns accepted when dispatcher queues an inbound event', async () => {
65
75
  getPlatformConfig.mockReturnValue({
66
76
  app_id: 'cli_xxx',
67
77
  app_secret: 'secret',
68
78
  conversation: { enabled: true, encrypt_key: 'encrypt', public_base_url: 'https://mindos.example.com' },
69
79
  });
70
80
 
71
- handleFeishuWebhook.mockResolvedValue({
72
- kind: 'accepted',
73
- body: { ok: true },
81
+ dispatchFeishuWebhook.mockResolvedValue({
82
+ body: { ok: true, queued: true },
74
83
  status: 202,
75
84
  });
76
85
 
@@ -83,6 +92,6 @@ describe('POST /api/im/webhook/feishu', () => {
83
92
 
84
93
  const res = await POST(req);
85
94
  expect(res.status).toBe(202);
86
- expect(await res.json()).toEqual({ ok: true });
95
+ expect(await res.json()).toEqual({ ok: true, queued: true });
87
96
  });
88
97
  });
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Tests for built-in pi-subagents extension support.
3
+ *
4
+ * Verifies that MindOS correctly bundles and loads pi-subagents as a default
5
+ * extension, providing subagent tools (subagent, subagent_status) to the Agent.
6
+ */
7
+
8
+ import { describe, expect, it, beforeAll } from 'vitest';
9
+ import fs from 'fs';
10
+ import path from 'path';
11
+
12
+ const PROJECT_ROOT = path.resolve(__dirname, '..', '..');
13
+
14
+ describe('pi-subagents built-in extension', () => {
15
+ describe('dependency installation', () => {
16
+ it('pi-subagents is listed in package.json dependencies', () => {
17
+ const pkgPath = path.join(PROJECT_ROOT, 'package.json');
18
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
19
+
20
+ expect(pkg.dependencies).toHaveProperty('pi-subagents');
21
+ expect(pkg.dependencies['pi-subagents']).toMatch(/^\^?0\.\d+\.\d+$/);
22
+ });
23
+
24
+ it('pi-subagents is installed in node_modules', () => {
25
+ const indexPath = path.join(PROJECT_ROOT, 'node_modules', 'pi-subagents', 'index.ts');
26
+ expect(fs.existsSync(indexPath)).toBe(true);
27
+ });
28
+
29
+ it('pi-subagents has expected structure (agents directory)', () => {
30
+ const agentsDir = path.join(PROJECT_ROOT, 'node_modules', 'pi-subagents', 'agents');
31
+ expect(fs.existsSync(agentsDir)).toBe(true);
32
+
33
+ // Should have builtin agents like scout.md, planner.md
34
+ const agentFiles = fs.readdirSync(agentsDir);
35
+ expect(agentFiles.some((f) => f.endsWith('.md'))).toBe(true);
36
+ });
37
+ });
38
+
39
+ describe('extension path registration', () => {
40
+ let askRouteContent: string;
41
+
42
+ beforeAll(() => {
43
+ const routePath = path.join(PROJECT_ROOT, 'app', 'api', 'ask', 'route.ts');
44
+ askRouteContent = fs.readFileSync(routePath, 'utf-8');
45
+ });
46
+
47
+ it('ask/route.ts includes pi-subagents in additionalExtensionPaths', () => {
48
+ expect(askRouteContent).toContain('pi-subagents');
49
+ expect(askRouteContent).toContain("path.join(projectRoot, 'app', 'node_modules', 'pi-subagents', 'index.ts')");
50
+ });
51
+
52
+ it('pi-subagents path is after user extensions (scanExtensionPaths)', () => {
53
+ // User extensions should have priority, so scanExtensionPaths() comes first
54
+ const scanIndex = askRouteContent.indexOf('scanExtensionPaths()');
55
+ const subagentsIndex = askRouteContent.indexOf('pi-subagents');
56
+
57
+ expect(scanIndex).toBeGreaterThan(-1);
58
+ expect(subagentsIndex).toBeGreaterThan(scanIndex);
59
+ });
60
+ });
61
+
62
+ describe('extension exports', () => {
63
+ it('pi-subagents index.ts is valid TypeScript with default export', async () => {
64
+ const indexPath = path.join(PROJECT_ROOT, 'node_modules', 'pi-subagents', 'index.ts');
65
+ const content = fs.readFileSync(indexPath, 'utf-8');
66
+
67
+ // Extension should have a default export function
68
+ expect(content).toMatch(/export\s+default\s+function/);
69
+ });
70
+
71
+ it('pi-subagents registers subagent tool via pi.registerTool', async () => {
72
+ const indexPath = path.join(PROJECT_ROOT, 'node_modules', 'pi-subagents', 'index.ts');
73
+ const content = fs.readFileSync(indexPath, 'utf-8');
74
+
75
+ // Should call pi.registerTool with the subagent tool
76
+ expect(content).toContain('pi.registerTool');
77
+ // Should have tool definition for 'subagent'
78
+ expect(content).toMatch(/name:\s*['"]subagent['"]/);
79
+ });
80
+ });
81
+ });
@@ -0,0 +1,18 @@
1
+ // @vitest-environment jsdom
2
+ import { describe, it, expect } from 'vitest';
3
+ import React from 'react';
4
+ import { renderToStaticMarkup } from 'react-dom/server';
5
+ import FileChip from '@/components/ask/FileChip';
6
+
7
+ describe('FileChip variants', () => {
8
+ it('renders ACP agent chips with an amber bot icon', () => {
9
+ const html = renderToStaticMarkup(
10
+ <FileChip path="Claude Code" variant="agent" onRemove={() => {}} />,
11
+ );
12
+
13
+ expect(html).toContain('Claude Code');
14
+ expect(html).toContain('lucide-bot');
15
+ expect(html).toContain('text-[var(--amber)]');
16
+ expect(html).not.toContain('lucide-zap');
17
+ });
18
+ });
@@ -46,6 +46,7 @@ describe('MessageList agent attribution', () => {
46
46
 
47
47
  expect(html).toContain('Claude Code');
48
48
  expect(html).toContain('Hello from Claude.');
49
+ expect(html).toContain('lucide-bot');
49
50
  });
50
51
 
51
52
  it('keeps the agent badge visible on assistant error bubbles', () => {
@@ -67,5 +68,6 @@ describe('MessageList agent attribution', () => {
67
68
 
68
69
  expect(html).toContain('Claude Code');
69
70
  expect(html).toContain('ACP Agent Error: timeout');
71
+ expect(html).toContain('lucide-bot');
70
72
  });
71
73
  });
@@ -0,0 +1,151 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import type { FeishuConfig } from '@/lib/im/types';
3
+
4
+ const { dispatcherInvoke, dispatcherConstruct, generateChallenge } = vi.hoisted(() => {
5
+ const dispatcherInvoke = vi.fn();
6
+ const dispatcherConstruct = vi.fn();
7
+ const generateChallenge = vi.fn();
8
+ return { dispatcherInvoke, dispatcherConstruct, generateChallenge };
9
+ });
10
+
11
+ vi.mock('@larksuiteoapi/node-sdk', () => ({
12
+ EventDispatcher: class MockEventDispatcher {
13
+ constructor(params: unknown) {
14
+ dispatcherConstruct(params);
15
+ }
16
+
17
+ register() {
18
+ return this;
19
+ }
20
+
21
+ async invoke(data: unknown) {
22
+ return await dispatcherInvoke(data);
23
+ }
24
+ },
25
+ generateChallenge,
26
+ }));
27
+
28
+ describe('Feishu dispatcher', () => {
29
+ beforeEach(() => {
30
+ vi.clearAllMocks();
31
+ generateChallenge.mockReturnValue({ isChallenge: false, challenge: undefined });
32
+ });
33
+
34
+ it('returns ignored when webhook is not ready', async () => {
35
+ const { dispatchFeishuWebhook } = await import('@/lib/im/feishu-dispatcher');
36
+ const config: FeishuConfig = {
37
+ app_id: 'cli_xxx',
38
+ app_secret: 'secret',
39
+ conversation: {
40
+ enabled: true,
41
+ encrypt_key: 'encrypt',
42
+ },
43
+ };
44
+
45
+ const result = await dispatchFeishuWebhook({
46
+ config,
47
+ body: { event: { message: { content: '{"text":"hi"}' } } },
48
+ headers: {},
49
+ });
50
+
51
+ expect(result).toEqual({
52
+ status: 202,
53
+ body: { ok: false, ignored: true, reason: 'Public base URL is required for Feishu event callbacks.' },
54
+ });
55
+ });
56
+
57
+ it('returns challenge response using SDK helper', async () => {
58
+ const { dispatchFeishuWebhook } = await import('@/lib/im/feishu-dispatcher');
59
+ generateChallenge.mockReturnValue({
60
+ isChallenge: true,
61
+ challenge: { challenge: 'challenge-token' },
62
+ });
63
+
64
+ const result = await dispatchFeishuWebhook({
65
+ config: {
66
+ app_id: 'cli_xxx',
67
+ app_secret: 'secret',
68
+ conversation: {
69
+ enabled: true,
70
+ encrypt_key: 'encrypt',
71
+ public_base_url: 'https://mindos.example.com',
72
+ verification_token: 'verification-token',
73
+ },
74
+ },
75
+ body: { challenge: 'challenge-token' },
76
+ headers: { 'x-lark-request-timestamp': '1' },
77
+ });
78
+
79
+ expect(generateChallenge).toHaveBeenCalledWith(
80
+ expect.objectContaining({ challenge: 'challenge-token' }),
81
+ { encryptKey: 'encrypt' },
82
+ );
83
+ expect(dispatcherInvoke).not.toHaveBeenCalled();
84
+ expect(result).toEqual({
85
+ status: 200,
86
+ body: { challenge: 'challenge-token' },
87
+ });
88
+ });
89
+
90
+ it('returns 401 when SDK verification fails', async () => {
91
+ const { dispatchFeishuWebhook } = await import('@/lib/im/feishu-dispatcher');
92
+ dispatcherInvoke.mockResolvedValue(undefined);
93
+
94
+ const result = await dispatchFeishuWebhook({
95
+ config: {
96
+ app_id: 'cli_xxx',
97
+ app_secret: 'secret',
98
+ conversation: {
99
+ enabled: true,
100
+ encrypt_key: 'encrypt',
101
+ public_base_url: 'https://mindos.example.com',
102
+ verification_token: 'verification-token',
103
+ },
104
+ },
105
+ body: { schema: '2.0', header: { event_type: 'im.message.receive_v1' } },
106
+ headers: {
107
+ 'x-lark-request-timestamp': '1',
108
+ 'x-lark-request-nonce': 'nonce',
109
+ 'x-lark-signature': 'bad-signature',
110
+ },
111
+ });
112
+
113
+ expect(result).toEqual({
114
+ status: 401,
115
+ body: { ok: false, error: 'Invalid Feishu webhook signature or payload.' },
116
+ });
117
+ });
118
+
119
+ it('uses SDK dispatcher and returns the handler response for accepted events', async () => {
120
+ const { dispatchFeishuWebhook } = await import('@/lib/im/feishu-dispatcher');
121
+ dispatcherInvoke.mockResolvedValue({ ok: true, queued: true, reason: 'direct_message' });
122
+
123
+ const result = await dispatchFeishuWebhook({
124
+ config: {
125
+ app_id: 'cli_xxx',
126
+ app_secret: 'secret',
127
+ conversation: {
128
+ enabled: true,
129
+ encrypt_key: 'encrypt',
130
+ public_base_url: 'https://mindos.example.com',
131
+ verification_token: 'verification-token',
132
+ },
133
+ },
134
+ body: { schema: '2.0', header: { event_type: 'im.message.receive_v1' } },
135
+ headers: {
136
+ 'x-lark-request-timestamp': '1',
137
+ 'x-lark-request-nonce': 'nonce',
138
+ 'x-lark-signature': 'signature',
139
+ },
140
+ });
141
+
142
+ expect(dispatcherInvoke).toHaveBeenCalledWith(expect.objectContaining({
143
+ schema: '2.0',
144
+ header: { event_type: 'im.message.receive_v1' },
145
+ }));
146
+ expect(result).toEqual({
147
+ status: 202,
148
+ body: { ok: true, queued: true, reason: 'direct_message' },
149
+ });
150
+ });
151
+ });
@@ -1,14 +1,35 @@
1
- import { describe, it, expect } from 'vitest';
2
- import type { FeishuConfig, FeishuWebhookEventEnvelope } from '@/lib/im/types';
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import type { FeishuConfig, FeishuSdkMessageEvent, FeishuWebhookEventEnvelope } from '@/lib/im/types';
3
3
 
4
- import {
5
- buildFeishuWebhookStatus,
6
- normalizeFeishuIncomingMessage,
7
- shouldProcessFeishuEvent,
8
- } from '@/lib/im/webhook/feishu';
4
+ vi.mock('@/lib/im/executor', () => ({
5
+ sendIMMessage: vi.fn().mockResolvedValue({ ok: true, messageId: 'msg_1', timestamp: '2026-04-10T00:00:00.000Z' }),
6
+ }));
7
+
8
+ vi.mock('@/lib/agent/headless', () => ({
9
+ runHeadlessAgent: vi.fn().mockResolvedValue({ text: 'Hello from MindOS', thinking: '', toolCalls: [] }),
10
+ }));
11
+
12
+ vi.mock('@/lib/im/conversation-store', () => ({
13
+ appendConversationTurn: vi.fn(),
14
+ getConversationHistory: vi.fn(() => ({ sessionId: 'session', messages: [] })),
15
+ }));
16
+
17
+ vi.mock('@/lib/im/activity', () => ({
18
+ recordActivity: vi.fn(),
19
+ }));
20
+
21
+ async function importModule() {
22
+ return await import('@/lib/im/webhook/feishu');
23
+ }
9
24
 
10
25
  describe('Feishu webhook helpers', () => {
11
- it('builds pending status when conversation is enabled but public url is missing', () => {
26
+ beforeEach(() => {
27
+ vi.clearAllMocks();
28
+ vi.spyOn(console, 'error').mockImplementation(() => {});
29
+ });
30
+
31
+ it('builds pending status when conversation is enabled but public url is missing', async () => {
32
+ const { buildFeishuWebhookStatus } = await importModule();
12
33
  const config: FeishuConfig = {
13
34
  app_id: 'cli_xxx',
14
35
  app_secret: 'secret',
@@ -27,7 +48,8 @@ describe('Feishu webhook helpers', () => {
27
48
  });
28
49
  });
29
50
 
30
- it('builds ready status when conversation is enabled and callback URL is available', () => {
51
+ it('builds ready status when conversation is enabled and callback URL is available', async () => {
52
+ const { buildFeishuWebhookStatus } = await importModule();
31
53
  const config: FeishuConfig = {
32
54
  app_id: 'cli_xxx',
33
55
  app_secret: 'secret',
@@ -47,27 +69,8 @@ describe('Feishu webhook helpers', () => {
47
69
  });
48
70
  });
49
71
 
50
- it('rejects challenge with wrong verification token', async () => {
51
- const { handleFeishuWebhook } = await import('@/lib/im/webhook/feishu');
52
- const result = await handleFeishuWebhook({
53
- config: {
54
- app_id: 'cli_xxx',
55
- app_secret: 'secret',
56
- conversation: {
57
- enabled: true,
58
- encrypt_key: 'encrypt',
59
- public_base_url: 'https://mindos.example.com',
60
- verification_token: 'expected-token',
61
- },
62
- },
63
- body: { challenge: 'challenge-token', token: 'wrong-token' },
64
- });
65
-
66
- expect(result.status).toBe(401);
67
- expect(result.body).toEqual({ ok: false, error: 'Invalid verification token' });
68
- });
69
-
70
- it('normalizes a dm text message into the shared incoming message shape', () => {
72
+ it('normalizes a dm text message into the shared incoming message shape', async () => {
73
+ const { normalizeFeishuIncomingMessage } = await importModule();
71
74
  const payload: FeishuWebhookEventEnvelope = {
72
75
  header: {
73
76
  event_type: 'im.message.receive_v1',
@@ -99,7 +102,8 @@ describe('Feishu webhook helpers', () => {
99
102
  });
100
103
  });
101
104
 
102
- it('processes direct messages even without mentions', () => {
105
+ it('processes direct messages even without mentions', async () => {
106
+ const { shouldProcessFeishuEvent } = await importModule();
103
107
  const payload: FeishuWebhookEventEnvelope = {
104
108
  event: {
105
109
  message: {
@@ -112,7 +116,8 @@ describe('Feishu webhook helpers', () => {
112
116
  expect(shouldProcessFeishuEvent(payload)).toEqual({ ok: true, reason: 'direct_message' });
113
117
  });
114
118
 
115
- it('ignores group messages without bot mentions', () => {
119
+ it('ignores group messages without bot mentions', async () => {
120
+ const { shouldProcessFeishuEvent } = await importModule();
116
121
  const payload: FeishuWebhookEventEnvelope = {
117
122
  event: {
118
123
  message: {
@@ -126,34 +131,57 @@ describe('Feishu webhook helpers', () => {
126
131
  expect(shouldProcessFeishuEvent(payload)).toEqual({ ok: false, reason: 'group_without_mention' });
127
132
  });
128
133
 
129
- it('ignores events with empty text after normalization', async () => {
130
- const { handleFeishuWebhook } = await import('@/lib/im/webhook/feishu');
131
- const result = await handleFeishuWebhook({
132
- config: {
133
- app_id: 'cli_xxx',
134
- app_secret: 'secret',
135
- conversation: {
136
- enabled: true,
137
- encrypt_key: 'encrypt',
138
- public_base_url: 'https://mindos.example.com',
139
- },
134
+ it('queues processing for direct message sdk events', async () => {
135
+ const { handleFeishuMessageReceiveEvent } = await importModule();
136
+ const payload: FeishuSdkMessageEvent = {
137
+ event_type: 'im.message.receive_v1',
138
+ sender: {
139
+ sender_id: { open_id: 'ou_sender_1' },
140
140
  },
141
- body: {
142
- event: {
143
- message: {
144
- chat_type: 'p2p',
145
- chat_id: 'oc_chat_001',
146
- message_id: 'om_001',
147
- content: JSON.stringify({ text: ' ' }),
148
- },
149
- sender: {
150
- sender_id: { open_id: 'ou_sender_1' },
151
- },
152
- },
141
+ message: {
142
+ message_id: 'om_001',
143
+ chat_id: 'oc_chat_001',
144
+ chat_type: 'p2p',
145
+ content: JSON.stringify({ text: '你好' }),
146
+ },
147
+ };
148
+
149
+ const result = await handleFeishuMessageReceiveEvent(payload);
150
+
151
+ expect(result).toEqual(expect.objectContaining({ ok: true, queued: true, reason: 'direct_message' }));
152
+ });
153
+
154
+ it('ignores sdk events with empty text after normalization', async () => {
155
+ const { handleFeishuMessageReceiveEvent } = await importModule();
156
+ const result = await handleFeishuMessageReceiveEvent({
157
+ event_type: 'im.message.receive_v1',
158
+ sender: {
159
+ sender_id: { open_id: 'ou_sender_1' },
160
+ },
161
+ message: {
162
+ chat_type: 'p2p',
163
+ chat_id: 'oc_chat_001',
164
+ message_id: 'om_001',
165
+ content: JSON.stringify({ text: ' ' }),
166
+ },
167
+ });
168
+
169
+ expect(result).toEqual({ ok: true, ignored: true, reason: 'empty_text' });
170
+ });
171
+
172
+ it('ignores sdk events missing required message identifiers', async () => {
173
+ const { handleFeishuMessageReceiveEvent } = await importModule();
174
+ const result = await handleFeishuMessageReceiveEvent({
175
+ event_type: 'im.message.receive_v1',
176
+ sender: {
177
+ sender_id: { open_id: 'ou_sender_1' },
178
+ },
179
+ message: {
180
+ chat_type: 'p2p',
181
+ content: JSON.stringify({ text: 'hello' }),
153
182
  },
154
183
  });
155
184
 
156
- expect(result.status).toBe(202);
157
- expect(result.body).toEqual({ ok: true, ignored: true, reason: 'empty_text' });
185
+ expect(result).toEqual({ ok: true, ignored: true, reason: 'invalid_event_payload' });
158
186
  });
159
187
  });