ruflo 3.10.45 → 3.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (498) hide show
  1. package/README.md +412 -412
  2. package/bin/ruflo.js +77 -77
  3. package/package.json +113 -113
  4. package/src/chat-ui/Dockerfile +25 -25
  5. package/src/chat-ui/patch-mcp-url-safety.sh +28 -28
  6. package/src/config/config.example.json +76 -76
  7. package/src/mcp-bridge/Dockerfile +45 -45
  8. package/src/mcp-bridge/index.js +1692 -1692
  9. package/src/mcp-bridge/mcp-stdio-kernel.js +159 -159
  10. package/src/mcp-bridge/package.json +17 -17
  11. package/src/mcp-bridge/test-harness.js +470 -470
  12. package/src/nginx/Dockerfile +10 -10
  13. package/src/nginx/nginx.conf +67 -67
  14. package/src/nginx/static/favicon-dark.svg +4 -4
  15. package/src/nginx/static/favicon.svg +4 -4
  16. package/src/nginx/static/icon.svg +5 -5
  17. package/src/nginx/static/logo.svg +9 -9
  18. package/src/nginx/static/manifest.json +22 -22
  19. package/src/nginx/static/welcome.js +184 -184
  20. package/src/ruvocal/.claude/skills/add-model-descriptions/SKILL.md +73 -73
  21. package/src/ruvocal/.claude-flow/daemon-state.json +135 -0
  22. package/src/ruvocal/.claude-flow/data/pending-insights.jsonl +0 -0
  23. package/src/ruvocal/.claude-flow/data/ranked-context.json +5 -0
  24. package/src/ruvocal/.claude-flow/logs/daemon.log +31 -0
  25. package/src/ruvocal/.claude-flow/logs/headless/audit_1777949411822_juxau0_prompt.log +989 -0
  26. package/src/ruvocal/.claude-flow/logs/headless/audit_1777949411822_juxau0_result.log +67 -0
  27. package/src/ruvocal/.claude-flow/logs/headless/audit_1777950042278_jvj5xq_prompt.log +989 -0
  28. package/src/ruvocal/.claude-flow/logs/headless/audit_1777950042278_jvj5xq_result.log +93 -0
  29. package/src/ruvocal/.claude-flow/logs/headless/optimize_1777949531823_yt5yc2_prompt.log +1498 -0
  30. package/src/ruvocal/.claude-flow/logs/headless/optimize_1777949531823_yt5yc2_result.log +93 -0
  31. package/src/ruvocal/.claude-flow/logs/headless/testgaps_1777949771821_elw1j4_prompt.log +1498 -0
  32. package/src/ruvocal/.claude-flow/logs/headless/testgaps_1777949771821_elw1j4_result.log +100 -0
  33. package/src/ruvocal/.claude-flow/metrics/codebase-map.json +11 -0
  34. package/src/ruvocal/.claude-flow/metrics/consolidation.json +6 -0
  35. package/src/ruvocal/.claude-flow/neural/stats.json +6 -0
  36. package/src/ruvocal/.claude-flow/sessions/current.json +13 -0
  37. package/src/ruvocal/.devcontainer/Dockerfile +9 -9
  38. package/src/ruvocal/.devcontainer/devcontainer.json +36 -36
  39. package/src/ruvocal/.dockerignore +16 -16
  40. package/src/ruvocal/.eslintignore +13 -13
  41. package/src/ruvocal/.eslintrc.cjs +45 -45
  42. package/src/ruvocal/.gcloudignore +18 -18
  43. package/src/ruvocal/.github/ISSUE_TEMPLATE/bug-report--chat-ui-.md +43 -43
  44. package/src/ruvocal/.github/ISSUE_TEMPLATE/config-support.md +9 -9
  45. package/src/ruvocal/.github/ISSUE_TEMPLATE/feature-request--chat-ui-.md +17 -17
  46. package/src/ruvocal/.github/ISSUE_TEMPLATE/huggingchat.md +11 -11
  47. package/src/ruvocal/.github/release.yml +16 -16
  48. package/src/ruvocal/.github/workflows/build-docs.yml +18 -18
  49. package/src/ruvocal/.github/workflows/build-image.yml +142 -142
  50. package/src/ruvocal/.github/workflows/build-pr-docs.yml +20 -20
  51. package/src/ruvocal/.github/workflows/deploy-dev.yml +63 -63
  52. package/src/ruvocal/.github/workflows/deploy-prod.yml +78 -78
  53. package/src/ruvocal/.github/workflows/lint-and-test.yml +84 -84
  54. package/src/ruvocal/.github/workflows/slugify.yaml +72 -72
  55. package/src/ruvocal/.github/workflows/trufflehog.yml +17 -17
  56. package/src/ruvocal/.github/workflows/upload-pr-documentation.yml +16 -16
  57. package/src/ruvocal/.husky/lint-stage-config.js +4 -4
  58. package/src/ruvocal/.husky/pre-commit +2 -2
  59. package/src/ruvocal/.prettierignore +14 -14
  60. package/src/ruvocal/.prettierrc +7 -7
  61. package/src/ruvocal/.swarm/attestation.db +0 -0
  62. package/src/ruvocal/.swarm/hnsw.index +0 -0
  63. package/src/ruvocal/.swarm/hnsw.metadata.json +1 -0
  64. package/src/ruvocal/.swarm/memory.db +0 -0
  65. package/src/ruvocal/.swarm/schema.sql +305 -0
  66. package/src/ruvocal/CLAUDE.md +126 -126
  67. package/src/ruvocal/Dockerfile +96 -96
  68. package/src/ruvocal/LICENSE +202 -202
  69. package/src/ruvocal/PRIVACY.md +41 -41
  70. package/src/ruvocal/README.md +164 -164
  71. package/src/ruvocal/chart/Chart.yaml +5 -5
  72. package/src/ruvocal/chart/env/dev.yaml +260 -260
  73. package/src/ruvocal/chart/env/prod.yaml +273 -273
  74. package/src/ruvocal/chart/templates/_helpers.tpl +22 -22
  75. package/src/ruvocal/chart/templates/config.yaml +10 -10
  76. package/src/ruvocal/chart/templates/deployment.yaml +81 -81
  77. package/src/ruvocal/chart/templates/hpa.yaml +45 -45
  78. package/src/ruvocal/chart/templates/infisical.yaml +24 -24
  79. package/src/ruvocal/chart/templates/ingress-internal.yaml +32 -32
  80. package/src/ruvocal/chart/templates/ingress.yaml +32 -32
  81. package/src/ruvocal/chart/templates/network-policy.yaml +36 -36
  82. package/src/ruvocal/chart/templates/service-account.yaml +13 -13
  83. package/src/ruvocal/chart/templates/service-monitor.yaml +17 -17
  84. package/src/ruvocal/chart/templates/service.yaml +21 -21
  85. package/src/ruvocal/chart/values.yaml +73 -73
  86. package/src/ruvocal/cloudbuild.yaml +68 -68
  87. package/src/ruvocal/config/branding.env.example +19 -19
  88. package/src/ruvocal/docker-compose.yml +21 -21
  89. package/src/ruvocal/docs/adr/ADR-029-HUGGINGFACE-CHAT-UI-CLOUD-RUN.md +1236 -1236
  90. package/src/ruvocal/docs/adr/ADR-033-RUVECTOR-RUFLO-MCP-INTEGRATION.md +111 -111
  91. package/src/ruvocal/docs/adr/ADR-034-OPTIONAL-MCP-BACKENDS.md +117 -117
  92. package/src/ruvocal/docs/adr/ADR-035-MCP-TOOL-GROUPS.md +186 -186
  93. package/src/ruvocal/docs/adr/ADR-037-AUTOPILOT-CHAT-MODE.md +1500 -1500
  94. package/src/ruvocal/docs/adr/ADR-038-RUVOCAL-FORK.md +286 -286
  95. package/src/ruvocal/docs/source/_toctree.yml +30 -30
  96. package/src/ruvocal/docs/source/configuration/common-issues.md +38 -38
  97. package/src/ruvocal/docs/source/configuration/llm-router.md +105 -105
  98. package/src/ruvocal/docs/source/configuration/mcp-tools.md +84 -84
  99. package/src/ruvocal/docs/source/configuration/metrics.md +9 -9
  100. package/src/ruvocal/docs/source/configuration/open-id.md +57 -57
  101. package/src/ruvocal/docs/source/configuration/overview.md +89 -89
  102. package/src/ruvocal/docs/source/configuration/theming.md +20 -20
  103. package/src/ruvocal/docs/source/developing/architecture.md +48 -48
  104. package/src/ruvocal/docs/source/index.md +53 -53
  105. package/src/ruvocal/docs/source/installation/docker.md +43 -43
  106. package/src/ruvocal/docs/source/installation/helm.md +43 -43
  107. package/src/ruvocal/docs/source/installation/local.md +62 -62
  108. package/src/ruvocal/entrypoint.sh +18 -18
  109. package/src/ruvocal/mcp-bridge/Dockerfile +45 -45
  110. package/src/ruvocal/mcp-bridge/cloudbuild.yaml +49 -49
  111. package/src/ruvocal/mcp-bridge/index.js +1902 -1902
  112. package/src/ruvocal/mcp-bridge/mcp-stdio-kernel.js +159 -159
  113. package/src/ruvocal/mcp-bridge/package-lock.json +762 -762
  114. package/src/ruvocal/mcp-bridge/package.json +17 -17
  115. package/src/ruvocal/mcp-bridge/test-harness.js +470 -470
  116. package/src/ruvocal/package-lock.json +11741 -11741
  117. package/src/ruvocal/package.json +121 -121
  118. package/src/ruvocal/postcss.config.js +6 -6
  119. package/src/ruvocal/rvf.manifest.json +204 -204
  120. package/src/ruvocal/scripts/config.ts +64 -64
  121. package/src/ruvocal/scripts/generate-welcome.mjs +181 -181
  122. package/src/ruvocal/scripts/populate.ts +288 -288
  123. package/src/ruvocal/scripts/samples.txt +194 -194
  124. package/src/ruvocal/scripts/setups/vitest-setup-server.ts +44 -44
  125. package/src/ruvocal/scripts/updateLocalEnv.ts +48 -48
  126. package/src/ruvocal/src/ambient.d.ts +7 -7
  127. package/src/ruvocal/src/app.d.ts +29 -29
  128. package/src/ruvocal/src/app.html +53 -53
  129. package/src/ruvocal/src/hooks.server.ts +32 -32
  130. package/src/ruvocal/src/hooks.ts +6 -6
  131. package/src/ruvocal/src/lib/APIClient.ts +148 -148
  132. package/src/ruvocal/src/lib/actions/clickOutside.ts +18 -18
  133. package/src/ruvocal/src/lib/actions/snapScrollToBottom.ts +346 -346
  134. package/src/ruvocal/src/lib/buildPrompt.ts +33 -33
  135. package/src/ruvocal/src/lib/components/AnnouncementBanner.svelte +20 -20
  136. package/src/ruvocal/src/lib/components/BackgroundGenerationPoller.svelte +168 -168
  137. package/src/ruvocal/src/lib/components/CodeBlock.svelte +73 -73
  138. package/src/ruvocal/src/lib/components/CopyToClipBoardBtn.svelte +92 -92
  139. package/src/ruvocal/src/lib/components/DeleteConversationModal.svelte +75 -75
  140. package/src/ruvocal/src/lib/components/EditConversationModal.svelte +100 -100
  141. package/src/ruvocal/src/lib/components/ExpandNavigation.svelte +22 -22
  142. package/src/ruvocal/src/lib/components/FoundationBackground.svelte +242 -242
  143. package/src/ruvocal/src/lib/components/HoverTooltip.svelte +44 -44
  144. package/src/ruvocal/src/lib/components/HtmlPreviewModal.svelte +143 -143
  145. package/src/ruvocal/src/lib/components/InfiniteScroll.svelte +50 -50
  146. package/src/ruvocal/src/lib/components/MobileNav.svelte +300 -300
  147. package/src/ruvocal/src/lib/components/Modal.svelte +115 -115
  148. package/src/ruvocal/src/lib/components/ModelCardMetadata.svelte +71 -71
  149. package/src/ruvocal/src/lib/components/NavConversationItem.svelte +151 -151
  150. package/src/ruvocal/src/lib/components/NavMenu.svelte +313 -313
  151. package/src/ruvocal/src/lib/components/Pagination.svelte +97 -97
  152. package/src/ruvocal/src/lib/components/PaginationArrow.svelte +27 -27
  153. package/src/ruvocal/src/lib/components/Portal.svelte +24 -24
  154. package/src/ruvocal/src/lib/components/RetryBtn.svelte +18 -18
  155. package/src/ruvocal/src/lib/components/RuFloUniverse.svelte +185 -185
  156. package/src/ruvocal/src/lib/components/RufloHelpModal.svelte +411 -411
  157. package/src/ruvocal/src/lib/components/ScrollToBottomBtn.svelte +47 -47
  158. package/src/ruvocal/src/lib/components/ScrollToPreviousBtn.svelte +77 -77
  159. package/src/ruvocal/src/lib/components/ShareConversationModal.svelte +182 -182
  160. package/src/ruvocal/src/lib/components/StopGeneratingBtn.svelte +69 -69
  161. package/src/ruvocal/src/lib/components/SubscribeModal.svelte +87 -87
  162. package/src/ruvocal/src/lib/components/Switch.svelte +36 -36
  163. package/src/ruvocal/src/lib/components/SystemPromptModal.svelte +44 -44
  164. package/src/ruvocal/src/lib/components/Toast.svelte +27 -27
  165. package/src/ruvocal/src/lib/components/Tooltip.svelte +30 -30
  166. package/src/ruvocal/src/lib/components/WelcomeModal.svelte +46 -46
  167. package/src/ruvocal/src/lib/components/chat/Alternatives.svelte +77 -77
  168. package/src/ruvocal/src/lib/components/chat/BlockWrapper.svelte +72 -72
  169. package/src/ruvocal/src/lib/components/chat/ChatInput.svelte +490 -490
  170. package/src/ruvocal/src/lib/components/chat/ChatIntroduction.svelte +123 -123
  171. package/src/ruvocal/src/lib/components/chat/ChatMessage.svelte +548 -548
  172. package/src/ruvocal/src/lib/components/chat/ChatWindow.svelte +1057 -1057
  173. package/src/ruvocal/src/lib/components/chat/FileDropzone.svelte +92 -92
  174. package/src/ruvocal/src/lib/components/chat/ImageLightbox.svelte +66 -66
  175. package/src/ruvocal/src/lib/components/chat/MarkdownBlock.svelte +23 -23
  176. package/src/ruvocal/src/lib/components/chat/MarkdownRenderer.svelte +69 -69
  177. package/src/ruvocal/src/lib/components/chat/MarkdownRenderer.svelte.test.ts +58 -58
  178. package/src/ruvocal/src/lib/components/chat/MessageAvatar.svelte +103 -103
  179. package/src/ruvocal/src/lib/components/chat/ModelSwitch.svelte +64 -64
  180. package/src/ruvocal/src/lib/components/chat/OpenReasoningResults.svelte +81 -81
  181. package/src/ruvocal/src/lib/components/chat/TaskGroup.svelte +88 -88
  182. package/src/ruvocal/src/lib/components/chat/ToolUpdate.svelte +273 -273
  183. package/src/ruvocal/src/lib/components/chat/UploadedFile.svelte +253 -253
  184. package/src/ruvocal/src/lib/components/chat/UrlFetchModal.svelte +203 -203
  185. package/src/ruvocal/src/lib/components/chat/VoiceRecorder.svelte +214 -214
  186. package/src/ruvocal/src/lib/components/icons/IconBurger.svelte +20 -20
  187. package/src/ruvocal/src/lib/components/icons/IconCheap.svelte +20 -20
  188. package/src/ruvocal/src/lib/components/icons/IconChevron.svelte +24 -24
  189. package/src/ruvocal/src/lib/components/icons/IconDazzled.svelte +40 -40
  190. package/src/ruvocal/src/lib/components/icons/IconFast.svelte +20 -20
  191. package/src/ruvocal/src/lib/components/icons/IconLoading.svelte +22 -22
  192. package/src/ruvocal/src/lib/components/icons/IconMCP.svelte +28 -28
  193. package/src/ruvocal/src/lib/components/icons/IconMoon.svelte +21 -21
  194. package/src/ruvocal/src/lib/components/icons/IconNew.svelte +20 -20
  195. package/src/ruvocal/src/lib/components/icons/IconOmni.svelte +90 -90
  196. package/src/ruvocal/src/lib/components/icons/IconPaperclip.svelte +24 -24
  197. package/src/ruvocal/src/lib/components/icons/IconPro.svelte +37 -37
  198. package/src/ruvocal/src/lib/components/icons/IconShare.svelte +21 -21
  199. package/src/ruvocal/src/lib/components/icons/IconSun.svelte +93 -93
  200. package/src/ruvocal/src/lib/components/icons/Logo.svelte +68 -68
  201. package/src/ruvocal/src/lib/components/icons/LogoHuggingFaceBorderless.svelte +54 -54
  202. package/src/ruvocal/src/lib/components/mcp/AddServerForm.svelte +250 -250
  203. package/src/ruvocal/src/lib/components/mcp/MCPServerManager.svelte +185 -185
  204. package/src/ruvocal/src/lib/components/mcp/ServerCard.svelte +203 -203
  205. package/src/ruvocal/src/lib/components/players/AudioPlayer.svelte +82 -82
  206. package/src/ruvocal/src/lib/components/voice/AudioWaveform.svelte +96 -96
  207. package/src/ruvocal/src/lib/components/wasm/GalleryPanel.svelte +357 -357
  208. package/src/ruvocal/src/lib/constants/mcpExamples.ts +114 -114
  209. package/src/ruvocal/src/lib/constants/mime.ts +11 -11
  210. package/src/ruvocal/src/lib/constants/pagination.ts +1 -1
  211. package/src/ruvocal/src/lib/constants/publicSepToken.ts +1 -1
  212. package/src/ruvocal/src/lib/constants/routerExamples.ts +133 -133
  213. package/src/ruvocal/src/lib/constants/rvagentPresets.ts +206 -206
  214. package/src/ruvocal/src/lib/createShareLink.ts +27 -27
  215. package/src/ruvocal/src/lib/jobs/refresh-conversation-stats.ts +297 -297
  216. package/src/ruvocal/src/lib/migrations/lock.ts +56 -56
  217. package/src/ruvocal/src/lib/migrations/migrations.spec.ts +74 -74
  218. package/src/ruvocal/src/lib/migrations/migrations.ts +109 -109
  219. package/src/ruvocal/src/lib/migrations/routines/01-update-search-assistants.ts +50 -50
  220. package/src/ruvocal/src/lib/migrations/routines/02-update-assistants-models.ts +48 -48
  221. package/src/ruvocal/src/lib/migrations/routines/04-update-message-updates.ts +151 -151
  222. package/src/ruvocal/src/lib/migrations/routines/05-update-message-files.ts +56 -56
  223. package/src/ruvocal/src/lib/migrations/routines/06-trim-message-updates.ts +56 -56
  224. package/src/ruvocal/src/lib/migrations/routines/08-update-featured-to-review.ts +32 -32
  225. package/src/ruvocal/src/lib/migrations/routines/09-delete-empty-conversations.spec.ts +214 -214
  226. package/src/ruvocal/src/lib/migrations/routines/09-delete-empty-conversations.ts +88 -88
  227. package/src/ruvocal/src/lib/migrations/routines/10-update-reports-assistantid.ts +29 -29
  228. package/src/ruvocal/src/lib/migrations/routines/index.ts +15 -15
  229. package/src/ruvocal/src/lib/server/__tests__/conversation-stop-generating.spec.ts +103 -103
  230. package/src/ruvocal/src/lib/server/abortRegistry.ts +57 -57
  231. package/src/ruvocal/src/lib/server/abortedGenerations.ts +43 -43
  232. package/src/ruvocal/src/lib/server/adminToken.ts +62 -62
  233. package/src/ruvocal/src/lib/server/api/__tests__/conversations-id.spec.ts +296 -296
  234. package/src/ruvocal/src/lib/server/api/__tests__/conversations-message.spec.ts +216 -216
  235. package/src/ruvocal/src/lib/server/api/__tests__/conversations.spec.ts +235 -235
  236. package/src/ruvocal/src/lib/server/api/__tests__/misc.spec.ts +72 -72
  237. package/src/ruvocal/src/lib/server/api/__tests__/testHelpers.ts +86 -86
  238. package/src/ruvocal/src/lib/server/api/__tests__/user-reports.spec.ts +78 -78
  239. package/src/ruvocal/src/lib/server/api/__tests__/user.spec.ts +239 -239
  240. package/src/ruvocal/src/lib/server/api/types.ts +37 -37
  241. package/src/ruvocal/src/lib/server/api/utils/requireAuth.ts +22 -22
  242. package/src/ruvocal/src/lib/server/api/utils/resolveConversation.ts +69 -69
  243. package/src/ruvocal/src/lib/server/api/utils/resolveModel.ts +27 -27
  244. package/src/ruvocal/src/lib/server/api/utils/superjsonResponse.ts +15 -15
  245. package/src/ruvocal/src/lib/server/apiToken.ts +11 -11
  246. package/src/ruvocal/src/lib/server/auth.ts +554 -554
  247. package/src/ruvocal/src/lib/server/config.ts +187 -187
  248. package/src/ruvocal/src/lib/server/conversation.ts +83 -83
  249. package/src/ruvocal/src/lib/server/database/__tests__/rvf.spec.ts +709 -709
  250. package/src/ruvocal/src/lib/server/database/postgres.ts +700 -700
  251. package/src/ruvocal/src/lib/server/database/rvf.ts +1078 -1078
  252. package/src/ruvocal/src/lib/server/database.ts +145 -145
  253. package/src/ruvocal/src/lib/server/endpoints/document.ts +68 -68
  254. package/src/ruvocal/src/lib/server/endpoints/endpoints.ts +43 -43
  255. package/src/ruvocal/src/lib/server/endpoints/images.ts +211 -211
  256. package/src/ruvocal/src/lib/server/endpoints/openai/endpointOai.ts +266 -266
  257. package/src/ruvocal/src/lib/server/endpoints/openai/openAIChatToTextGenerationStream.ts +212 -212
  258. package/src/ruvocal/src/lib/server/endpoints/openai/openAICompletionToTextGenerationStream.ts +32 -32
  259. package/src/ruvocal/src/lib/server/endpoints/preprocessMessages.ts +61 -61
  260. package/src/ruvocal/src/lib/server/exitHandler.ts +59 -59
  261. package/src/ruvocal/src/lib/server/files/downloadFile.ts +34 -34
  262. package/src/ruvocal/src/lib/server/files/uploadFile.ts +29 -29
  263. package/src/ruvocal/src/lib/server/findRepoRoot.ts +13 -13
  264. package/src/ruvocal/src/lib/server/generateFromDefaultEndpoint.ts +46 -46
  265. package/src/ruvocal/src/lib/server/hooks/error.ts +37 -37
  266. package/src/ruvocal/src/lib/server/hooks/fetch.ts +22 -22
  267. package/src/ruvocal/src/lib/server/hooks/handle.ts +250 -250
  268. package/src/ruvocal/src/lib/server/hooks/init.ts +51 -51
  269. package/src/ruvocal/src/lib/server/isURLLocal.spec.ts +31 -31
  270. package/src/ruvocal/src/lib/server/isURLLocal.ts +74 -74
  271. package/src/ruvocal/src/lib/server/logger.ts +42 -42
  272. package/src/ruvocal/src/lib/server/mcp/clientPool.spec.ts +175 -175
  273. package/src/ruvocal/src/lib/server/mcp/hf.ts +32 -32
  274. package/src/ruvocal/src/lib/server/mcp/httpClient.ts +122 -122
  275. package/src/ruvocal/src/lib/server/mcp/registry.ts +76 -76
  276. package/src/ruvocal/src/lib/server/mcp/tools.ts +196 -196
  277. package/src/ruvocal/src/lib/server/metrics.ts +255 -255
  278. package/src/ruvocal/src/lib/server/models.ts +518 -518
  279. package/src/ruvocal/src/lib/server/requestContext.ts +55 -55
  280. package/src/ruvocal/src/lib/server/router/arch.ts +230 -230
  281. package/src/ruvocal/src/lib/server/router/endpoint.ts +316 -316
  282. package/src/ruvocal/src/lib/server/router/multimodal.ts +28 -28
  283. package/src/ruvocal/src/lib/server/router/policy.ts +49 -49
  284. package/src/ruvocal/src/lib/server/router/toolsRoute.ts +51 -51
  285. package/src/ruvocal/src/lib/server/router/types.ts +21 -21
  286. package/src/ruvocal/src/lib/server/sendSlack.ts +23 -23
  287. package/src/ruvocal/src/lib/server/textGeneration/generate.ts +258 -258
  288. package/src/ruvocal/src/lib/server/textGeneration/index.ts +96 -96
  289. package/src/ruvocal/src/lib/server/textGeneration/mcp/fileRefs.ts +155 -155
  290. package/src/ruvocal/src/lib/server/textGeneration/mcp/routerResolution.ts +108 -108
  291. package/src/ruvocal/src/lib/server/textGeneration/mcp/runMcpFlow.ts +831 -831
  292. package/src/ruvocal/src/lib/server/textGeneration/mcp/toolInvocation.ts +349 -349
  293. package/src/ruvocal/src/lib/server/textGeneration/mcp/wasmTools.test.ts +633 -633
  294. package/src/ruvocal/src/lib/server/textGeneration/reasoning.ts +23 -23
  295. package/src/ruvocal/src/lib/server/textGeneration/title.ts +83 -83
  296. package/src/ruvocal/src/lib/server/textGeneration/types.ts +28 -28
  297. package/src/ruvocal/src/lib/server/textGeneration/utils/prepareFiles.ts +88 -88
  298. package/src/ruvocal/src/lib/server/textGeneration/utils/routing.ts +21 -21
  299. package/src/ruvocal/src/lib/server/textGeneration/utils/toolPrompt.ts +49 -49
  300. package/src/ruvocal/src/lib/server/urlSafety.ts +77 -77
  301. package/src/ruvocal/src/lib/server/usageLimits.ts +30 -30
  302. package/src/ruvocal/src/lib/stores/autopilotStore.svelte.ts +175 -175
  303. package/src/ruvocal/src/lib/stores/backgroundGenerations.svelte.ts +32 -32
  304. package/src/ruvocal/src/lib/stores/backgroundGenerations.ts +1 -1
  305. package/src/ruvocal/src/lib/stores/errors.ts +9 -9
  306. package/src/ruvocal/src/lib/stores/isAborted.ts +3 -3
  307. package/src/ruvocal/src/lib/stores/isPro.ts +4 -4
  308. package/src/ruvocal/src/lib/stores/loading.ts +3 -3
  309. package/src/ruvocal/src/lib/stores/mcpServers.ts +534 -534
  310. package/src/ruvocal/src/lib/stores/pendingChatInput.ts +3 -3
  311. package/src/ruvocal/src/lib/stores/pendingMessage.ts +9 -9
  312. package/src/ruvocal/src/lib/stores/settings.ts +182 -182
  313. package/src/ruvocal/src/lib/stores/shareModal.ts +13 -13
  314. package/src/ruvocal/src/lib/stores/titleUpdate.ts +8 -8
  315. package/src/ruvocal/src/lib/stores/wasmMcp.ts +472 -472
  316. package/src/ruvocal/src/lib/switchTheme.ts +124 -124
  317. package/src/ruvocal/src/lib/types/AbortedGeneration.ts +8 -8
  318. package/src/ruvocal/src/lib/types/Assistant.ts +31 -31
  319. package/src/ruvocal/src/lib/types/AssistantStats.ts +11 -11
  320. package/src/ruvocal/src/lib/types/ConfigKey.ts +4 -4
  321. package/src/ruvocal/src/lib/types/ConvSidebar.ts +9 -9
  322. package/src/ruvocal/src/lib/types/Conversation.ts +27 -27
  323. package/src/ruvocal/src/lib/types/ConversationStats.ts +13 -13
  324. package/src/ruvocal/src/lib/types/Message.ts +41 -41
  325. package/src/ruvocal/src/lib/types/MessageEvent.ts +10 -10
  326. package/src/ruvocal/src/lib/types/MessageUpdate.ts +139 -139
  327. package/src/ruvocal/src/lib/types/MigrationResult.ts +7 -7
  328. package/src/ruvocal/src/lib/types/Model.ts +23 -23
  329. package/src/ruvocal/src/lib/types/Report.ts +12 -12
  330. package/src/ruvocal/src/lib/types/Review.ts +6 -6
  331. package/src/ruvocal/src/lib/types/Semaphore.ts +19 -19
  332. package/src/ruvocal/src/lib/types/Session.ts +22 -22
  333. package/src/ruvocal/src/lib/types/Settings.ts +93 -93
  334. package/src/ruvocal/src/lib/types/SharedConversation.ts +9 -9
  335. package/src/ruvocal/src/lib/types/Template.ts +6 -6
  336. package/src/ruvocal/src/lib/types/Timestamps.ts +4 -4
  337. package/src/ruvocal/src/lib/types/TokenCache.ts +6 -6
  338. package/src/ruvocal/src/lib/types/Tool.ts +77 -77
  339. package/src/ruvocal/src/lib/types/UrlDependency.ts +5 -5
  340. package/src/ruvocal/src/lib/types/User.ts +14 -14
  341. package/src/ruvocal/src/lib/utils/PublicConfig.svelte.ts +75 -75
  342. package/src/ruvocal/src/lib/utils/auth.ts +17 -17
  343. package/src/ruvocal/src/lib/utils/chunk.ts +33 -33
  344. package/src/ruvocal/src/lib/utils/cookiesAreEnabled.ts +13 -13
  345. package/src/ruvocal/src/lib/utils/debounce.ts +17 -17
  346. package/src/ruvocal/src/lib/utils/deepestChild.ts +6 -6
  347. package/src/ruvocal/src/lib/utils/favicon.ts +21 -21
  348. package/src/ruvocal/src/lib/utils/fetchJSON.ts +23 -23
  349. package/src/ruvocal/src/lib/utils/file2base64.ts +14 -14
  350. package/src/ruvocal/src/lib/utils/formatUserCount.ts +37 -37
  351. package/src/ruvocal/src/lib/utils/generationState.spec.ts +75 -75
  352. package/src/ruvocal/src/lib/utils/generationState.ts +26 -26
  353. package/src/ruvocal/src/lib/utils/getHref.ts +41 -41
  354. package/src/ruvocal/src/lib/utils/getReturnFromGenerator.ts +7 -7
  355. package/src/ruvocal/src/lib/utils/haptics.ts +64 -64
  356. package/src/ruvocal/src/lib/utils/hashConv.ts +12 -12
  357. package/src/ruvocal/src/lib/utils/hf.ts +17 -17
  358. package/src/ruvocal/src/lib/utils/isDesktop.ts +7 -7
  359. package/src/ruvocal/src/lib/utils/isUrl.ts +8 -8
  360. package/src/ruvocal/src/lib/utils/isVirtualKeyboard.ts +16 -16
  361. package/src/ruvocal/src/lib/utils/loadAttachmentsFromUrls.ts +115 -115
  362. package/src/ruvocal/src/lib/utils/marked.spec.ts +96 -96
  363. package/src/ruvocal/src/lib/utils/marked.ts +531 -531
  364. package/src/ruvocal/src/lib/utils/mcpValidation.ts +147 -147
  365. package/src/ruvocal/src/lib/utils/mergeAsyncGenerators.ts +38 -38
  366. package/src/ruvocal/src/lib/utils/messageUpdates.spec.ts +262 -262
  367. package/src/ruvocal/src/lib/utils/messageUpdates.ts +324 -324
  368. package/src/ruvocal/src/lib/utils/mime.ts +56 -56
  369. package/src/ruvocal/src/lib/utils/models.ts +14 -14
  370. package/src/ruvocal/src/lib/utils/parseBlocks.ts +120 -120
  371. package/src/ruvocal/src/lib/utils/parseIncompleteMarkdown.ts +644 -644
  372. package/src/ruvocal/src/lib/utils/parseStringToList.ts +10 -10
  373. package/src/ruvocal/src/lib/utils/randomUuid.ts +14 -14
  374. package/src/ruvocal/src/lib/utils/searchTokens.ts +33 -33
  375. package/src/ruvocal/src/lib/utils/sha256.ts +7 -7
  376. package/src/ruvocal/src/lib/utils/stringifyError.ts +12 -12
  377. package/src/ruvocal/src/lib/utils/sum.ts +3 -3
  378. package/src/ruvocal/src/lib/utils/template.spec.ts +59 -59
  379. package/src/ruvocal/src/lib/utils/template.ts +53 -53
  380. package/src/ruvocal/src/lib/utils/timeout.ts +9 -9
  381. package/src/ruvocal/src/lib/utils/toolProgress.spec.ts +46 -46
  382. package/src/ruvocal/src/lib/utils/toolProgress.ts +11 -11
  383. package/src/ruvocal/src/lib/utils/tree/addChildren.spec.ts +102 -102
  384. package/src/ruvocal/src/lib/utils/tree/addChildren.ts +48 -48
  385. package/src/ruvocal/src/lib/utils/tree/addSibling.spec.ts +81 -81
  386. package/src/ruvocal/src/lib/utils/tree/addSibling.ts +41 -41
  387. package/src/ruvocal/src/lib/utils/tree/buildSubtree.spec.ts +110 -110
  388. package/src/ruvocal/src/lib/utils/tree/buildSubtree.ts +24 -24
  389. package/src/ruvocal/src/lib/utils/tree/convertLegacyConversation.spec.ts +31 -31
  390. package/src/ruvocal/src/lib/utils/tree/convertLegacyConversation.ts +36 -36
  391. package/src/ruvocal/src/lib/utils/tree/isMessageId.spec.ts +15 -15
  392. package/src/ruvocal/src/lib/utils/tree/isMessageId.ts +5 -5
  393. package/src/ruvocal/src/lib/utils/tree/tree.d.ts +14 -14
  394. package/src/ruvocal/src/lib/utils/tree/treeHelpers.spec.ts +167 -167
  395. package/src/ruvocal/src/lib/utils/updates.ts +39 -39
  396. package/src/ruvocal/src/lib/utils/urlParams.ts +13 -13
  397. package/src/ruvocal/src/lib/wasm/idb.ts +438 -438
  398. package/src/ruvocal/src/lib/wasm/index.ts +1213 -1213
  399. package/src/ruvocal/src/lib/wasm/tests/wasm-capabilities.test.ts +565 -565
  400. package/src/ruvocal/src/lib/wasm/wasm.worker.ts +332 -332
  401. package/src/ruvocal/src/lib/wasm/workerClient.ts +166 -166
  402. package/src/ruvocal/src/lib/workers/autopilotWorker.ts +221 -221
  403. package/src/ruvocal/src/lib/workers/detailFetchWorker.ts +100 -100
  404. package/src/ruvocal/src/lib/workers/markdownWorker.ts +61 -61
  405. package/src/ruvocal/src/routes/+error.svelte +20 -20
  406. package/src/ruvocal/src/routes/+layout.svelte +324 -324
  407. package/src/ruvocal/src/routes/+layout.ts +91 -91
  408. package/src/ruvocal/src/routes/+page.svelte +168 -168
  409. package/src/ruvocal/src/routes/.well-known/oauth-cimd/+server.ts +37 -37
  410. package/src/ruvocal/src/routes/__debug/openai/+server.ts +21 -21
  411. package/src/ruvocal/src/routes/admin/export/+server.ts +159 -159
  412. package/src/ruvocal/src/routes/admin/stats/compute/+server.ts +16 -16
  413. package/src/ruvocal/src/routes/api/conversation/[id]/+server.ts +40 -40
  414. package/src/ruvocal/src/routes/api/conversation/[id]/message/[messageId]/+server.ts +42 -42
  415. package/src/ruvocal/src/routes/api/conversations/+server.ts +48 -48
  416. package/src/ruvocal/src/routes/api/fetch-url/+server.ts +147 -147
  417. package/src/ruvocal/src/routes/api/mcp/health/+server.ts +292 -292
  418. package/src/ruvocal/src/routes/api/mcp/servers/+server.ts +32 -32
  419. package/src/ruvocal/src/routes/api/models/+server.ts +25 -25
  420. package/src/ruvocal/src/routes/api/transcribe/+server.ts +104 -104
  421. package/src/ruvocal/src/routes/api/user/+server.ts +15 -15
  422. package/src/ruvocal/src/routes/api/user/validate-token/+server.ts +20 -20
  423. package/src/ruvocal/src/routes/api/v2/conversations/+server.ts +48 -48
  424. package/src/ruvocal/src/routes/api/v2/conversations/[id]/+server.ts +94 -94
  425. package/src/ruvocal/src/routes/api/v2/conversations/[id]/message/[messageId]/+server.ts +43 -43
  426. package/src/ruvocal/src/routes/api/v2/conversations/import-share/+server.ts +23 -23
  427. package/src/ruvocal/src/routes/api/v2/debug/config/+server.ts +16 -16
  428. package/src/ruvocal/src/routes/api/v2/debug/refresh/+server.ts +30 -30
  429. package/src/ruvocal/src/routes/api/v2/export/+server.ts +196 -196
  430. package/src/ruvocal/src/routes/api/v2/feature-flags/+server.ts +14 -14
  431. package/src/ruvocal/src/routes/api/v2/models/+server.ts +38 -38
  432. package/src/ruvocal/src/routes/api/v2/models/[namespace]/+server.ts +8 -8
  433. package/src/ruvocal/src/routes/api/v2/models/[namespace]/[model]/+server.ts +8 -8
  434. package/src/ruvocal/src/routes/api/v2/models/[namespace]/[model]/subscribe/+server.ts +28 -28
  435. package/src/ruvocal/src/routes/api/v2/models/[namespace]/subscribe/+server.ts +28 -28
  436. package/src/ruvocal/src/routes/api/v2/models/old/+server.ts +7 -7
  437. package/src/ruvocal/src/routes/api/v2/models/refresh/+server.ts +33 -33
  438. package/src/ruvocal/src/routes/api/v2/public-config/+server.ts +7 -7
  439. package/src/ruvocal/src/routes/api/v2/user/+server.ts +17 -17
  440. package/src/ruvocal/src/routes/api/v2/user/billing-orgs/+server.ts +73 -73
  441. package/src/ruvocal/src/routes/api/v2/user/reports/+server.ts +17 -17
  442. package/src/ruvocal/src/routes/api/v2/user/settings/+server.ts +110 -110
  443. package/src/ruvocal/src/routes/conversation/+server.ts +115 -115
  444. package/src/ruvocal/src/routes/conversation/[id]/+page.svelte +586 -586
  445. package/src/ruvocal/src/routes/conversation/[id]/+page.ts +60 -60
  446. package/src/ruvocal/src/routes/conversation/[id]/+server.ts +740 -740
  447. package/src/ruvocal/src/routes/conversation/[id]/message/[messageId]/prompt/+server.ts +66 -66
  448. package/src/ruvocal/src/routes/conversation/[id]/share/+server.ts +69 -69
  449. package/src/ruvocal/src/routes/conversation/[id]/stop-generating/+server.ts +35 -35
  450. package/src/ruvocal/src/routes/healthcheck/+server.ts +3 -3
  451. package/src/ruvocal/src/routes/login/+server.ts +5 -5
  452. package/src/ruvocal/src/routes/login/callback/+server.ts +103 -103
  453. package/src/ruvocal/src/routes/login/callback/updateUser.spec.ts +157 -157
  454. package/src/ruvocal/src/routes/login/callback/updateUser.ts +215 -215
  455. package/src/ruvocal/src/routes/logout/+server.ts +18 -18
  456. package/src/ruvocal/src/routes/metrics/+server.ts +18 -18
  457. package/src/ruvocal/src/routes/models/+page.svelte +233 -233
  458. package/src/ruvocal/src/routes/models/[...model]/+page.svelte +161 -161
  459. package/src/ruvocal/src/routes/models/[...model]/+page.ts +14 -14
  460. package/src/ruvocal/src/routes/models/[...model]/thumbnail.png/+server.ts +64 -64
  461. package/src/ruvocal/src/routes/models/[...model]/thumbnail.png/ModelThumbnail.svelte +28 -28
  462. package/src/ruvocal/src/routes/privacy/+page.svelte +11 -11
  463. package/src/ruvocal/src/routes/r/[id]/+page.ts +34 -34
  464. package/src/ruvocal/src/routes/settings/(nav)/+layout.svelte +282 -282
  465. package/src/ruvocal/src/routes/settings/(nav)/+layout.ts +1 -1
  466. package/src/ruvocal/src/routes/settings/(nav)/+server.ts +59 -59
  467. package/src/ruvocal/src/routes/settings/(nav)/[...model]/+page.svelte +464 -464
  468. package/src/ruvocal/src/routes/settings/(nav)/[...model]/+page.ts +14 -14
  469. package/src/ruvocal/src/routes/settings/(nav)/application/+page.svelte +362 -362
  470. package/src/ruvocal/src/routes/settings/+layout.svelte +40 -40
  471. package/src/ruvocal/src/styles/highlight-js.css +195 -195
  472. package/src/ruvocal/src/styles/main.css +144 -144
  473. package/src/ruvocal/static/chatui/favicon-dark.svg +3 -3
  474. package/src/ruvocal/static/chatui/favicon-dev.svg +3 -3
  475. package/src/ruvocal/static/chatui/favicon.svg +3 -3
  476. package/src/ruvocal/static/chatui/icon.svg +3 -3
  477. package/src/ruvocal/static/chatui/logo.svg +7 -7
  478. package/src/ruvocal/static/chatui/manifest.json +54 -54
  479. package/src/ruvocal/static/chatui/welcome.js +184 -184
  480. package/src/ruvocal/static/huggingchat/favicon-dark.svg +4 -4
  481. package/src/ruvocal/static/huggingchat/favicon-dev.svg +4 -4
  482. package/src/ruvocal/static/huggingchat/favicon.svg +4 -4
  483. package/src/ruvocal/static/huggingchat/fulltext-logo.svg +1 -1
  484. package/src/ruvocal/static/huggingchat/icon.svg +4 -4
  485. package/src/ruvocal/static/huggingchat/logo.svg +4 -4
  486. package/src/ruvocal/static/huggingchat/manifest.json +54 -54
  487. package/src/ruvocal/static/huggingchat/routes.chat.json +226 -226
  488. package/src/ruvocal/static/robots.txt +10 -10
  489. package/src/ruvocal/static/wasm/rvagent_wasm.js +1539 -1539
  490. package/src/ruvocal/stub/@reflink/reflink/package.json +5 -5
  491. package/src/ruvocal/svelte.config.js +53 -53
  492. package/src/ruvocal/tailwind.config.cjs +30 -30
  493. package/src/ruvocal/tsconfig.json +19 -19
  494. package/src/ruvocal/vite.config.ts +87 -87
  495. package/src/scripts/deploy.sh +116 -116
  496. package/src/scripts/generate-config.js +245 -245
  497. package/src/scripts/generate-welcome.js +187 -187
  498. package/src/scripts/package-rvf.sh +116 -116
@@ -1,633 +1,633 @@
1
- /**
2
- * Comprehensive WASM MCP Tools Test Suite
3
- * Tests all 15 rvAgent tools with edge cases and performance benchmarks
4
- */
5
-
6
- import { describe, it, expect, beforeEach, afterEach } from "vitest";
7
-
8
- // Import the tool execution state and function
9
- // We'll need to create a test helper since the actual implementation is in toolInvocation.ts
10
-
11
- // Mock implementations for testing
12
- const createTestState = () => {
13
- const virtualFS = new Map<string, string>();
14
- const todoList: { id: string; task: string; completed: boolean; created: number }[] = [];
15
- let todoIdCounter = 1;
16
- const memoryStore = new Map<string, { key: string; value: string; tags: string[] }>();
17
- const witnessChain: { hash: string; prevHash: string; action: string; data: unknown; timestamp: number }[] = [];
18
- let lastWitnessHash = "genesis";
19
-
20
- const simpleHash = (data: string): string => {
21
- let hash = 0;
22
- for (let i = 0; i < data.length; i++) {
23
- const char = data.charCodeAt(i);
24
- hash = ((hash << 5) - hash) + char;
25
- hash = hash & hash;
26
- }
27
- return Math.abs(hash).toString(16).padStart(8, "0");
28
- };
29
-
30
- const addWitnessEntry = (action: string, data: unknown): string => {
31
- const entry = {
32
- hash: "",
33
- prevHash: lastWitnessHash,
34
- action,
35
- data,
36
- timestamp: Date.now(),
37
- };
38
- entry.hash = simpleHash(JSON.stringify(entry));
39
- witnessChain.push(entry);
40
- lastWitnessHash = entry.hash;
41
- return entry.hash;
42
- };
43
-
44
- const galleryTemplates = [
45
- { id: "development-agent", name: "Development Agent", category: "development", description: "Full-featured dev agent", tags: ["development", "coding", "files"] },
46
- { id: "research-agent", name: "Research Agent", category: "research", description: "Research & analysis agent", tags: ["research", "memory", "search"] },
47
- { id: "security-agent", name: "Security Agent", category: "security", description: "Security audit agent", tags: ["security", "audit", "compliance"] },
48
- { id: "multi-agent-orchestrator", name: "Multi-Agent Orchestrator", category: "orchestration", description: "Coordinate multiple agents", tags: ["orchestration", "parallel", "subagents"] },
49
- ];
50
- let activeTemplateId: string | null = null;
51
-
52
- const executeWasmTool = (
53
- toolName: string,
54
- args: Record<string, unknown>
55
- ): { success: boolean; result: string; error?: string } => {
56
- try {
57
- addWitnessEntry(`tool:${toolName}`, { args });
58
-
59
- switch (toolName) {
60
- // File Operations
61
- case "read_file": {
62
- const path = String(args.path || "");
63
- if (!path) return { success: false, result: "", error: "path is required" };
64
- const content = virtualFS.get(path);
65
- if (content === undefined) return { success: false, result: "", error: `File not found: ${path}` };
66
- return { success: true, result: content };
67
- }
68
- case "write_file": {
69
- const path = String(args.path || "");
70
- const content = String(args.content || "");
71
- if (!path) return { success: false, result: "", error: "path is required" };
72
- virtualFS.set(path, content);
73
- return { success: true, result: `Successfully wrote ${content.length} bytes to ${path}` };
74
- }
75
- case "list_files": {
76
- const files = Array.from(virtualFS.keys());
77
- if (files.length === 0) return { success: true, result: "No files in virtual filesystem" };
78
- return { success: true, result: `Files:\n${files.map(f => `- ${f}`).join("\n")}` };
79
- }
80
- case "delete_file": {
81
- const path = String(args.path || "");
82
- if (!path) return { success: false, result: "", error: "path is required" };
83
- if (!virtualFS.has(path)) return { success: false, result: "", error: `File not found: ${path}` };
84
- virtualFS.delete(path);
85
- return { success: true, result: `Deleted: ${path}` };
86
- }
87
- case "edit_file": {
88
- const path = String(args.path || "");
89
- const oldContent = String(args.old_content || args.oldContent || "");
90
- const newContent = String(args.new_content || args.newContent || "");
91
- if (!path) return { success: false, result: "", error: "path is required" };
92
- const existing = virtualFS.get(path);
93
- if (existing === undefined) return { success: false, result: "", error: `File not found: ${path}` };
94
- if (!existing.includes(oldContent)) return { success: false, result: "", error: `old_content not found in file` };
95
- virtualFS.set(path, existing.replace(oldContent, newContent));
96
- return { success: true, result: `Successfully edited ${path}` };
97
- }
98
- // Search Tools
99
- case "grep": {
100
- const pattern = String(args.pattern || "");
101
- const targetPath = args.path ? String(args.path) : null;
102
- if (!pattern) return { success: false, result: "", error: "pattern is required" };
103
- try {
104
- const regex = new RegExp(pattern, "gi");
105
- const results: string[] = [];
106
- for (const [filePath, content] of virtualFS.entries()) {
107
- if (targetPath && filePath !== targetPath) continue;
108
- const lines = content.split("\n");
109
- lines.forEach((line, idx) => {
110
- if (regex.test(line)) results.push(`${filePath}:${idx + 1}: ${line}`);
111
- });
112
- }
113
- return { success: true, result: results.length > 0 ? results.join("\n") : "No matches found" };
114
- } catch {
115
- return { success: false, result: "", error: `Invalid regex: ${pattern}` };
116
- }
117
- }
118
- case "glob": {
119
- const pattern = String(args.pattern || "");
120
- if (!pattern) return { success: false, result: "", error: "pattern is required" };
121
- const globPattern = pattern.replace(/\*/g, ".*").replace(/\?/g, ".");
122
- const regex = new RegExp(`^${globPattern}$`);
123
- const matches = Array.from(virtualFS.keys()).filter(f => regex.test(f));
124
- return { success: true, result: matches.length > 0 ? matches.join("\n") : "No matches found" };
125
- }
126
- // Task Management
127
- case "todo_add": {
128
- const task = String(args.task || "");
129
- if (!task) return { success: false, result: "", error: "task is required" };
130
- const id = `todo-${todoIdCounter++}`;
131
- todoList.push({ id, task, completed: false, created: Date.now() });
132
- return { success: true, result: `Added task: ${task} (id: ${id})` };
133
- }
134
- case "todo_list": {
135
- if (todoList.length === 0) return { success: true, result: "No tasks in todo list" };
136
- const formatted = todoList.map(t => `${t.completed ? "✓" : "○"} [${t.id}] ${t.task}`).join("\n");
137
- return { success: true, result: `Tasks:\n${formatted}` };
138
- }
139
- case "todo_complete": {
140
- const id = String(args.id || "");
141
- if (!id) return { success: false, result: "", error: "id is required" };
142
- const todo = todoList.find(t => t.id === id);
143
- if (!todo) return { success: false, result: "", error: `Task not found: ${id}` };
144
- todo.completed = true;
145
- return { success: true, result: `Completed: ${todo.task}` };
146
- }
147
- // Memory Tools
148
- case "memory_store": {
149
- const key = String(args.key || "");
150
- const value = String(args.value || "");
151
- if (!key || !value) return { success: false, result: "", error: "key and value are required" };
152
- const tags = Array.isArray(args.tags) ? args.tags.map(String) : [];
153
- memoryStore.set(key, { key, value, tags });
154
- return { success: true, result: `Stored memory: ${key}` };
155
- }
156
- case "memory_search": {
157
- const query = String(args.query || "").toLowerCase();
158
- if (!query) return { success: false, result: "", error: "query is required" };
159
- const topK = typeof args.top_k === "number" ? args.top_k : 5;
160
- const results = Array.from(memoryStore.values())
161
- .filter(m => m.key.toLowerCase().includes(query) || m.value.toLowerCase().includes(query) || m.tags.some(t => t.toLowerCase().includes(query)))
162
- .slice(0, topK)
163
- .map(m => `[${m.key}] ${m.value.slice(0, 100)}${m.value.length > 100 ? "..." : ""}`);
164
- return { success: true, result: results.length > 0 ? `Found ${results.length} results:\n${results.join("\n")}` : "No memories found" };
165
- }
166
- // Witness Chain
167
- case "witness_log": {
168
- const action = String(args.action || "");
169
- if (!action) return { success: false, result: "", error: "action is required" };
170
- const data = args.data || {};
171
- const hash = addWitnessEntry(action, data);
172
- return { success: true, result: `Logged to witness chain: ${action} (hash: ${hash})` };
173
- }
174
- case "witness_verify": {
175
- let valid = true;
176
- let prevHash = "genesis";
177
- for (const entry of witnessChain) {
178
- if (entry.prevHash !== prevHash) { valid = false; break; }
179
- prevHash = entry.hash;
180
- }
181
- return { success: true, result: `Witness chain: ${valid ? "VALID" : "INVALID"} (${witnessChain.length} entries)` };
182
- }
183
- // Gallery Tools
184
- case "gallery_list": {
185
- const category = args.category ? String(args.category) : null;
186
- const filtered = category ? galleryTemplates.filter(t => t.category === category) : galleryTemplates;
187
- const list = filtered.map(t => `- ${t.id}: ${t.name} (${t.category})`).join("\n");
188
- return { success: true, result: `Gallery Templates:\n${list}` };
189
- }
190
- case "gallery_load": {
191
- const id = String(args.id || "");
192
- if (!id) return { success: false, result: "", error: "id is required" };
193
- const template = galleryTemplates.find(t => t.id === id);
194
- if (!template) return { success: false, result: "", error: `Template not found: ${id}` };
195
- activeTemplateId = id;
196
- return { success: true, result: `Loaded template: ${template.name}\nDescription: ${template.description}` };
197
- }
198
- case "gallery_search": {
199
- const query = String(args.query || "").toLowerCase();
200
- if (!query) return { success: false, result: "", error: "query is required" };
201
- const matches = galleryTemplates.filter(t =>
202
- t.name.toLowerCase().includes(query) || t.description.toLowerCase().includes(query) || t.tags.some(tag => tag.toLowerCase().includes(query))
203
- );
204
- if (matches.length === 0) return { success: true, result: "No templates found" };
205
- const list = matches.map(t => `- ${t.id}: ${t.name}\n ${t.description}`).join("\n");
206
- return { success: true, result: `Found ${matches.length} templates:\n${list}` };
207
- }
208
- default:
209
- return { success: false, result: "", error: `Unknown tool: ${toolName}` };
210
- }
211
- } catch (e) {
212
- return { success: false, result: "", error: e instanceof Error ? e.message : String(e) };
213
- }
214
- };
215
-
216
- return {
217
- virtualFS,
218
- todoList,
219
- memoryStore,
220
- witnessChain,
221
- galleryTemplates,
222
- executeWasmTool,
223
- getActiveTemplateId: () => activeTemplateId,
224
- };
225
- };
226
-
227
- describe("WASM MCP Tools", () => {
228
- let state: ReturnType<typeof createTestState>;
229
-
230
- beforeEach(() => {
231
- state = createTestState();
232
- });
233
-
234
- // ================================
235
- // File Operations Tests
236
- // ================================
237
- describe("File Operations", () => {
238
- it("write_file creates a new file", () => {
239
- const result = state.executeWasmTool("write_file", { path: "test.txt", content: "Hello World" });
240
- expect(result.success).toBe(true);
241
- expect(result.result).toContain("11 bytes");
242
- expect(state.virtualFS.get("test.txt")).toBe("Hello World");
243
- });
244
-
245
- it("read_file reads existing file", () => {
246
- state.virtualFS.set("test.txt", "Hello World");
247
- const result = state.executeWasmTool("read_file", { path: "test.txt" });
248
- expect(result.success).toBe(true);
249
- expect(result.result).toBe("Hello World");
250
- });
251
-
252
- it("read_file returns error for non-existent file", () => {
253
- const result = state.executeWasmTool("read_file", { path: "nonexistent.txt" });
254
- expect(result.success).toBe(false);
255
- expect(result.error).toContain("File not found");
256
- });
257
-
258
- it("list_files returns empty message when no files", () => {
259
- const result = state.executeWasmTool("list_files", {});
260
- expect(result.success).toBe(true);
261
- expect(result.result).toContain("No files");
262
- });
263
-
264
- it("list_files shows all files", () => {
265
- state.virtualFS.set("a.txt", "A");
266
- state.virtualFS.set("b.txt", "B");
267
- const result = state.executeWasmTool("list_files", {});
268
- expect(result.success).toBe(true);
269
- expect(result.result).toContain("a.txt");
270
- expect(result.result).toContain("b.txt");
271
- });
272
-
273
- it("delete_file removes existing file", () => {
274
- state.virtualFS.set("test.txt", "content");
275
- const result = state.executeWasmTool("delete_file", { path: "test.txt" });
276
- expect(result.success).toBe(true);
277
- expect(state.virtualFS.has("test.txt")).toBe(false);
278
- });
279
-
280
- it("delete_file returns error for non-existent file", () => {
281
- const result = state.executeWasmTool("delete_file", { path: "nonexistent.txt" });
282
- expect(result.success).toBe(false);
283
- expect(result.error).toContain("File not found");
284
- });
285
-
286
- it("edit_file replaces content", () => {
287
- state.virtualFS.set("test.txt", "Hello World");
288
- const result = state.executeWasmTool("edit_file", { path: "test.txt", old_content: "World", new_content: "Universe" });
289
- expect(result.success).toBe(true);
290
- expect(state.virtualFS.get("test.txt")).toBe("Hello Universe");
291
- });
292
-
293
- it("edit_file returns error when old_content not found", () => {
294
- state.virtualFS.set("test.txt", "Hello World");
295
- const result = state.executeWasmTool("edit_file", { path: "test.txt", old_content: "NOTFOUND", new_content: "X" });
296
- expect(result.success).toBe(false);
297
- expect(result.error).toContain("old_content not found");
298
- });
299
-
300
- it("handles files with special characters in content", () => {
301
- const content = "Line1\nLine2\tTab\r\nWindows\n日本語\n🎉";
302
- state.executeWasmTool("write_file", { path: "special.txt", content });
303
- const result = state.executeWasmTool("read_file", { path: "special.txt" });
304
- expect(result.success).toBe(true);
305
- expect(result.result).toBe(content);
306
- });
307
-
308
- it("handles empty file content", () => {
309
- state.executeWasmTool("write_file", { path: "empty.txt", content: "" });
310
- const result = state.executeWasmTool("read_file", { path: "empty.txt" });
311
- expect(result.success).toBe(true);
312
- expect(result.result).toBe("");
313
- });
314
-
315
- it("handles paths with directories", () => {
316
- state.executeWasmTool("write_file", { path: "src/lib/file.ts", content: "export {}" });
317
- const result = state.executeWasmTool("read_file", { path: "src/lib/file.ts" });
318
- expect(result.success).toBe(true);
319
- expect(result.result).toBe("export {}");
320
- });
321
- });
322
-
323
- // ================================
324
- // Search Tools Tests
325
- // ================================
326
- describe("Search Tools", () => {
327
- beforeEach(() => {
328
- state.virtualFS.set("src/index.ts", "import { foo } from './foo';\nexport const bar = 42;");
329
- state.virtualFS.set("src/foo.ts", "export const foo = 'hello';\nexport const FOO = 'WORLD';");
330
- state.virtualFS.set("README.md", "# Project\n\nThis is a test project.");
331
- });
332
-
333
- it("grep finds pattern in files", () => {
334
- const result = state.executeWasmTool("grep", { pattern: "foo" });
335
- expect(result.success).toBe(true);
336
- expect(result.result).toContain("src/index.ts");
337
- expect(result.result).toContain("src/foo.ts");
338
- });
339
-
340
- it("grep searches specific file", () => {
341
- const result = state.executeWasmTool("grep", { pattern: "export", path: "src/foo.ts" });
342
- expect(result.success).toBe(true);
343
- expect(result.result).toContain("src/foo.ts");
344
- expect(result.result).not.toContain("src/index.ts");
345
- });
346
-
347
- it("grep returns no matches message", () => {
348
- const result = state.executeWasmTool("grep", { pattern: "NOTFOUND" });
349
- expect(result.success).toBe(true);
350
- expect(result.result).toBe("No matches found");
351
- });
352
-
353
- it("grep supports regex patterns", () => {
354
- const result = state.executeWasmTool("grep", { pattern: "\\d+" });
355
- expect(result.success).toBe(true);
356
- expect(result.result).toContain("42");
357
- });
358
-
359
- it("grep handles invalid regex", () => {
360
- const result = state.executeWasmTool("grep", { pattern: "[invalid" });
361
- expect(result.success).toBe(false);
362
- expect(result.error).toContain("Invalid regex");
363
- });
364
-
365
- it("glob finds matching files", () => {
366
- const result = state.executeWasmTool("glob", { pattern: "*.ts" });
367
- expect(result.success).toBe(true);
368
- // Note: our simple glob implementation requires full path match
369
- });
370
-
371
- it("glob returns no matches for non-matching pattern", () => {
372
- const result = state.executeWasmTool("glob", { pattern: "*.xyz" });
373
- expect(result.success).toBe(true);
374
- expect(result.result).toBe("No matches found");
375
- });
376
- });
377
-
378
- // ================================
379
- // Task Management Tests
380
- // ================================
381
- describe("Task Management", () => {
382
- it("todo_add creates new task", () => {
383
- const result = state.executeWasmTool("todo_add", { task: "Write tests" });
384
- expect(result.success).toBe(true);
385
- expect(result.result).toContain("todo-1");
386
- expect(state.todoList).toHaveLength(1);
387
- });
388
-
389
- it("todo_list shows empty when no tasks", () => {
390
- const result = state.executeWasmTool("todo_list", {});
391
- expect(result.success).toBe(true);
392
- expect(result.result).toContain("No tasks");
393
- });
394
-
395
- it("todo_list shows all tasks", () => {
396
- state.executeWasmTool("todo_add", { task: "Task 1" });
397
- state.executeWasmTool("todo_add", { task: "Task 2" });
398
- const result = state.executeWasmTool("todo_list", {});
399
- expect(result.success).toBe(true);
400
- expect(result.result).toContain("Task 1");
401
- expect(result.result).toContain("Task 2");
402
- expect(result.result).toContain("○"); // uncompleted
403
- });
404
-
405
- it("todo_complete marks task as done", () => {
406
- state.executeWasmTool("todo_add", { task: "Task 1" });
407
- const completeResult = state.executeWasmTool("todo_complete", { id: "todo-1" });
408
- expect(completeResult.success).toBe(true);
409
-
410
- const listResult = state.executeWasmTool("todo_list", {});
411
- expect(listResult.result).toContain("✓");
412
- });
413
-
414
- it("todo_complete returns error for invalid id", () => {
415
- const result = state.executeWasmTool("todo_complete", { id: "todo-999" });
416
- expect(result.success).toBe(false);
417
- expect(result.error).toContain("Task not found");
418
- });
419
- });
420
-
421
- // ================================
422
- // Memory Tools Tests
423
- // ================================
424
- describe("Memory Tools", () => {
425
- it("memory_store saves entry", () => {
426
- const result = state.executeWasmTool("memory_store", { key: "pattern-1", value: "Use async/await" });
427
- expect(result.success).toBe(true);
428
- expect(state.memoryStore.has("pattern-1")).toBe(true);
429
- });
430
-
431
- it("memory_store with tags", () => {
432
- const result = state.executeWasmTool("memory_store", { key: "pattern-2", value: "Error handling", tags: ["best-practice", "async"] });
433
- expect(result.success).toBe(true);
434
- const stored = state.memoryStore.get("pattern-2");
435
- expect(stored?.tags).toContain("best-practice");
436
- });
437
-
438
- it("memory_search finds matching entries", () => {
439
- state.executeWasmTool("memory_store", { key: "auth-pattern", value: "JWT tokens for authentication" });
440
- state.executeWasmTool("memory_store", { key: "cache-pattern", value: "Use Redis for caching" });
441
-
442
- const result = state.executeWasmTool("memory_search", { query: "auth" });
443
- expect(result.success).toBe(true);
444
- expect(result.result).toContain("auth-pattern");
445
- expect(result.result).not.toContain("cache-pattern");
446
- });
447
-
448
- it("memory_search respects top_k limit", () => {
449
- for (let i = 0; i < 10; i++) {
450
- state.executeWasmTool("memory_store", { key: `test-${i}`, value: `Test value ${i}` });
451
- }
452
- const result = state.executeWasmTool("memory_search", { query: "test", top_k: 3 });
453
- expect(result.success).toBe(true);
454
- expect(result.result).toContain("Found 3 results");
455
- });
456
-
457
- it("memory_search returns no matches message", () => {
458
- const result = state.executeWasmTool("memory_search", { query: "nonexistent" });
459
- expect(result.success).toBe(true);
460
- expect(result.result).toBe("No memories found");
461
- });
462
-
463
- it("memory_search searches by tags", () => {
464
- state.executeWasmTool("memory_store", { key: "p1", value: "Value", tags: ["security", "critical"] });
465
- const result = state.executeWasmTool("memory_search", { query: "security" });
466
- expect(result.success).toBe(true);
467
- expect(result.result).toContain("p1");
468
- });
469
- });
470
-
471
- // ================================
472
- // Witness Chain Tests
473
- // ================================
474
- describe("Witness Chain", () => {
475
- it("witness_log creates entry", () => {
476
- const result = state.executeWasmTool("witness_log", { action: "file_created", data: { path: "test.txt" } });
477
- expect(result.success).toBe(true);
478
- expect(result.result).toContain("hash:");
479
- // Chain includes tool calls + explicit log
480
- expect(state.witnessChain.length).toBeGreaterThan(0);
481
- });
482
-
483
- it("witness_verify validates chain integrity", () => {
484
- state.executeWasmTool("witness_log", { action: "action1" });
485
- state.executeWasmTool("witness_log", { action: "action2" });
486
- const result = state.executeWasmTool("witness_verify", {});
487
- expect(result.success).toBe(true);
488
- expect(result.result).toContain("VALID");
489
- });
490
-
491
- it("all tool calls are logged to witness chain", () => {
492
- const initialLength = state.witnessChain.length;
493
- state.executeWasmTool("write_file", { path: "a.txt", content: "A" });
494
- state.executeWasmTool("read_file", { path: "a.txt" });
495
- expect(state.witnessChain.length).toBe(initialLength + 2);
496
- });
497
-
498
- it("witness chain hash linking is correct", () => {
499
- state.executeWasmTool("witness_log", { action: "a1" });
500
- state.executeWasmTool("witness_log", { action: "a2" });
501
-
502
- const chain = state.witnessChain;
503
- for (let i = 1; i < chain.length; i++) {
504
- expect(chain[i].prevHash).toBe(chain[i - 1].hash);
505
- }
506
- });
507
- });
508
-
509
- // ================================
510
- // Gallery Tools Tests
511
- // ================================
512
- describe("Gallery Tools", () => {
513
- it("gallery_list shows all templates", () => {
514
- const result = state.executeWasmTool("gallery_list", {});
515
- expect(result.success).toBe(true);
516
- expect(result.result).toContain("development-agent");
517
- expect(result.result).toContain("research-agent");
518
- });
519
-
520
- it("gallery_list filters by category", () => {
521
- const result = state.executeWasmTool("gallery_list", { category: "security" });
522
- expect(result.success).toBe(true);
523
- expect(result.result).toContain("security-agent");
524
- expect(result.result).not.toContain("development-agent");
525
- });
526
-
527
- it("gallery_load activates template", () => {
528
- const result = state.executeWasmTool("gallery_load", { id: "development-agent" });
529
- expect(result.success).toBe(true);
530
- expect(result.result).toContain("Development Agent");
531
- expect(state.getActiveTemplateId()).toBe("development-agent");
532
- });
533
-
534
- it("gallery_load returns error for invalid id", () => {
535
- const result = state.executeWasmTool("gallery_load", { id: "nonexistent" });
536
- expect(result.success).toBe(false);
537
- expect(result.error).toContain("Template not found");
538
- });
539
-
540
- it("gallery_search finds by name", () => {
541
- const result = state.executeWasmTool("gallery_search", { query: "research" });
542
- expect(result.success).toBe(true);
543
- expect(result.result).toContain("research-agent");
544
- });
545
-
546
- it("gallery_search finds by tags", () => {
547
- const result = state.executeWasmTool("gallery_search", { query: "coding" });
548
- expect(result.success).toBe(true);
549
- expect(result.result).toContain("development-agent");
550
- });
551
-
552
- it("gallery_search returns no matches message", () => {
553
- const result = state.executeWasmTool("gallery_search", { query: "xyz123" });
554
- expect(result.success).toBe(true);
555
- expect(result.result).toContain("No templates found");
556
- });
557
- });
558
-
559
- // ================================
560
- // Edge Cases & Error Handling
561
- // ================================
562
- describe("Edge Cases", () => {
563
- it("handles missing required parameters", () => {
564
- expect(state.executeWasmTool("read_file", {}).success).toBe(false);
565
- expect(state.executeWasmTool("write_file", { path: "x" }).success).toBe(true); // content defaults to ""
566
- expect(state.executeWasmTool("todo_add", {}).success).toBe(false);
567
- expect(state.executeWasmTool("memory_store", { key: "k" }).success).toBe(false);
568
- });
569
-
570
- it("handles unknown tool names", () => {
571
- const result = state.executeWasmTool("unknown_tool", {});
572
- expect(result.success).toBe(false);
573
- expect(result.error).toContain("Unknown tool");
574
- });
575
-
576
- it("handles large file content", () => {
577
- const largeContent = "x".repeat(1000000); // 1MB
578
- const writeResult = state.executeWasmTool("write_file", { path: "large.txt", content: largeContent });
579
- expect(writeResult.success).toBe(true);
580
-
581
- const readResult = state.executeWasmTool("read_file", { path: "large.txt" });
582
- expect(readResult.success).toBe(true);
583
- expect(readResult.result.length).toBe(1000000);
584
- });
585
-
586
- it("handles concurrent-like operations", () => {
587
- // Simulate multiple operations
588
- for (let i = 0; i < 100; i++) {
589
- state.executeWasmTool("write_file", { path: `file${i}.txt`, content: `content${i}` });
590
- }
591
- const listResult = state.executeWasmTool("list_files", {});
592
- expect(listResult.success).toBe(true);
593
- expect(state.virtualFS.size).toBe(100);
594
- });
595
- });
596
-
597
- // ================================
598
- // Performance Benchmarks
599
- // ================================
600
- describe("Performance", () => {
601
- it("file operations complete in under 1ms", () => {
602
- const start = performance.now();
603
- for (let i = 0; i < 100; i++) {
604
- state.executeWasmTool("write_file", { path: `perf${i}.txt`, content: "test" });
605
- }
606
- const duration = performance.now() - start;
607
- expect(duration).toBeLessThan(100); // 100 ops in <100ms = <1ms each
608
- });
609
-
610
- it("memory search scales with O(n)", () => {
611
- // Insert 1000 entries
612
- for (let i = 0; i < 1000; i++) {
613
- state.executeWasmTool("memory_store", { key: `key-${i}`, value: `value-${i}` });
614
- }
615
-
616
- const start = performance.now();
617
- for (let i = 0; i < 10; i++) {
618
- state.executeWasmTool("memory_search", { query: "key-500" });
619
- }
620
- const duration = performance.now() - start;
621
- expect(duration).toBeLessThan(100); // 10 searches in <100ms
622
- });
623
-
624
- it("witness chain grows correctly", () => {
625
- const initialLength = state.witnessChain.length;
626
- // Each witness_log creates 2 entries: one for the tool call audit + one for the explicit log
627
- for (let i = 0; i < 100; i++) {
628
- state.executeWasmTool("witness_log", { action: `action-${i}` });
629
- }
630
- expect(state.witnessChain.length).toBe(initialLength + 200); // 100 calls * 2 entries each
631
- });
632
- });
633
- });
1
+ /**
2
+ * Comprehensive WASM MCP Tools Test Suite
3
+ * Tests all 15 rvAgent tools with edge cases and performance benchmarks
4
+ */
5
+
6
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
7
+
8
+ // Import the tool execution state and function
9
+ // We'll need to create a test helper since the actual implementation is in toolInvocation.ts
10
+
11
+ // Mock implementations for testing
12
+ const createTestState = () => {
13
+ const virtualFS = new Map<string, string>();
14
+ const todoList: { id: string; task: string; completed: boolean; created: number }[] = [];
15
+ let todoIdCounter = 1;
16
+ const memoryStore = new Map<string, { key: string; value: string; tags: string[] }>();
17
+ const witnessChain: { hash: string; prevHash: string; action: string; data: unknown; timestamp: number }[] = [];
18
+ let lastWitnessHash = "genesis";
19
+
20
+ const simpleHash = (data: string): string => {
21
+ let hash = 0;
22
+ for (let i = 0; i < data.length; i++) {
23
+ const char = data.charCodeAt(i);
24
+ hash = ((hash << 5) - hash) + char;
25
+ hash = hash & hash;
26
+ }
27
+ return Math.abs(hash).toString(16).padStart(8, "0");
28
+ };
29
+
30
+ const addWitnessEntry = (action: string, data: unknown): string => {
31
+ const entry = {
32
+ hash: "",
33
+ prevHash: lastWitnessHash,
34
+ action,
35
+ data,
36
+ timestamp: Date.now(),
37
+ };
38
+ entry.hash = simpleHash(JSON.stringify(entry));
39
+ witnessChain.push(entry);
40
+ lastWitnessHash = entry.hash;
41
+ return entry.hash;
42
+ };
43
+
44
+ const galleryTemplates = [
45
+ { id: "development-agent", name: "Development Agent", category: "development", description: "Full-featured dev agent", tags: ["development", "coding", "files"] },
46
+ { id: "research-agent", name: "Research Agent", category: "research", description: "Research & analysis agent", tags: ["research", "memory", "search"] },
47
+ { id: "security-agent", name: "Security Agent", category: "security", description: "Security audit agent", tags: ["security", "audit", "compliance"] },
48
+ { id: "multi-agent-orchestrator", name: "Multi-Agent Orchestrator", category: "orchestration", description: "Coordinate multiple agents", tags: ["orchestration", "parallel", "subagents"] },
49
+ ];
50
+ let activeTemplateId: string | null = null;
51
+
52
+ const executeWasmTool = (
53
+ toolName: string,
54
+ args: Record<string, unknown>
55
+ ): { success: boolean; result: string; error?: string } => {
56
+ try {
57
+ addWitnessEntry(`tool:${toolName}`, { args });
58
+
59
+ switch (toolName) {
60
+ // File Operations
61
+ case "read_file": {
62
+ const path = String(args.path || "");
63
+ if (!path) return { success: false, result: "", error: "path is required" };
64
+ const content = virtualFS.get(path);
65
+ if (content === undefined) return { success: false, result: "", error: `File not found: ${path}` };
66
+ return { success: true, result: content };
67
+ }
68
+ case "write_file": {
69
+ const path = String(args.path || "");
70
+ const content = String(args.content || "");
71
+ if (!path) return { success: false, result: "", error: "path is required" };
72
+ virtualFS.set(path, content);
73
+ return { success: true, result: `Successfully wrote ${content.length} bytes to ${path}` };
74
+ }
75
+ case "list_files": {
76
+ const files = Array.from(virtualFS.keys());
77
+ if (files.length === 0) return { success: true, result: "No files in virtual filesystem" };
78
+ return { success: true, result: `Files:\n${files.map(f => `- ${f}`).join("\n")}` };
79
+ }
80
+ case "delete_file": {
81
+ const path = String(args.path || "");
82
+ if (!path) return { success: false, result: "", error: "path is required" };
83
+ if (!virtualFS.has(path)) return { success: false, result: "", error: `File not found: ${path}` };
84
+ virtualFS.delete(path);
85
+ return { success: true, result: `Deleted: ${path}` };
86
+ }
87
+ case "edit_file": {
88
+ const path = String(args.path || "");
89
+ const oldContent = String(args.old_content || args.oldContent || "");
90
+ const newContent = String(args.new_content || args.newContent || "");
91
+ if (!path) return { success: false, result: "", error: "path is required" };
92
+ const existing = virtualFS.get(path);
93
+ if (existing === undefined) return { success: false, result: "", error: `File not found: ${path}` };
94
+ if (!existing.includes(oldContent)) return { success: false, result: "", error: `old_content not found in file` };
95
+ virtualFS.set(path, existing.replace(oldContent, newContent));
96
+ return { success: true, result: `Successfully edited ${path}` };
97
+ }
98
+ // Search Tools
99
+ case "grep": {
100
+ const pattern = String(args.pattern || "");
101
+ const targetPath = args.path ? String(args.path) : null;
102
+ if (!pattern) return { success: false, result: "", error: "pattern is required" };
103
+ try {
104
+ const regex = new RegExp(pattern, "gi");
105
+ const results: string[] = [];
106
+ for (const [filePath, content] of virtualFS.entries()) {
107
+ if (targetPath && filePath !== targetPath) continue;
108
+ const lines = content.split("\n");
109
+ lines.forEach((line, idx) => {
110
+ if (regex.test(line)) results.push(`${filePath}:${idx + 1}: ${line}`);
111
+ });
112
+ }
113
+ return { success: true, result: results.length > 0 ? results.join("\n") : "No matches found" };
114
+ } catch {
115
+ return { success: false, result: "", error: `Invalid regex: ${pattern}` };
116
+ }
117
+ }
118
+ case "glob": {
119
+ const pattern = String(args.pattern || "");
120
+ if (!pattern) return { success: false, result: "", error: "pattern is required" };
121
+ const globPattern = pattern.replace(/\*/g, ".*").replace(/\?/g, ".");
122
+ const regex = new RegExp(`^${globPattern}$`);
123
+ const matches = Array.from(virtualFS.keys()).filter(f => regex.test(f));
124
+ return { success: true, result: matches.length > 0 ? matches.join("\n") : "No matches found" };
125
+ }
126
+ // Task Management
127
+ case "todo_add": {
128
+ const task = String(args.task || "");
129
+ if (!task) return { success: false, result: "", error: "task is required" };
130
+ const id = `todo-${todoIdCounter++}`;
131
+ todoList.push({ id, task, completed: false, created: Date.now() });
132
+ return { success: true, result: `Added task: ${task} (id: ${id})` };
133
+ }
134
+ case "todo_list": {
135
+ if (todoList.length === 0) return { success: true, result: "No tasks in todo list" };
136
+ const formatted = todoList.map(t => `${t.completed ? "✓" : "○"} [${t.id}] ${t.task}`).join("\n");
137
+ return { success: true, result: `Tasks:\n${formatted}` };
138
+ }
139
+ case "todo_complete": {
140
+ const id = String(args.id || "");
141
+ if (!id) return { success: false, result: "", error: "id is required" };
142
+ const todo = todoList.find(t => t.id === id);
143
+ if (!todo) return { success: false, result: "", error: `Task not found: ${id}` };
144
+ todo.completed = true;
145
+ return { success: true, result: `Completed: ${todo.task}` };
146
+ }
147
+ // Memory Tools
148
+ case "memory_store": {
149
+ const key = String(args.key || "");
150
+ const value = String(args.value || "");
151
+ if (!key || !value) return { success: false, result: "", error: "key and value are required" };
152
+ const tags = Array.isArray(args.tags) ? args.tags.map(String) : [];
153
+ memoryStore.set(key, { key, value, tags });
154
+ return { success: true, result: `Stored memory: ${key}` };
155
+ }
156
+ case "memory_search": {
157
+ const query = String(args.query || "").toLowerCase();
158
+ if (!query) return { success: false, result: "", error: "query is required" };
159
+ const topK = typeof args.top_k === "number" ? args.top_k : 5;
160
+ const results = Array.from(memoryStore.values())
161
+ .filter(m => m.key.toLowerCase().includes(query) || m.value.toLowerCase().includes(query) || m.tags.some(t => t.toLowerCase().includes(query)))
162
+ .slice(0, topK)
163
+ .map(m => `[${m.key}] ${m.value.slice(0, 100)}${m.value.length > 100 ? "..." : ""}`);
164
+ return { success: true, result: results.length > 0 ? `Found ${results.length} results:\n${results.join("\n")}` : "No memories found" };
165
+ }
166
+ // Witness Chain
167
+ case "witness_log": {
168
+ const action = String(args.action || "");
169
+ if (!action) return { success: false, result: "", error: "action is required" };
170
+ const data = args.data || {};
171
+ const hash = addWitnessEntry(action, data);
172
+ return { success: true, result: `Logged to witness chain: ${action} (hash: ${hash})` };
173
+ }
174
+ case "witness_verify": {
175
+ let valid = true;
176
+ let prevHash = "genesis";
177
+ for (const entry of witnessChain) {
178
+ if (entry.prevHash !== prevHash) { valid = false; break; }
179
+ prevHash = entry.hash;
180
+ }
181
+ return { success: true, result: `Witness chain: ${valid ? "VALID" : "INVALID"} (${witnessChain.length} entries)` };
182
+ }
183
+ // Gallery Tools
184
+ case "gallery_list": {
185
+ const category = args.category ? String(args.category) : null;
186
+ const filtered = category ? galleryTemplates.filter(t => t.category === category) : galleryTemplates;
187
+ const list = filtered.map(t => `- ${t.id}: ${t.name} (${t.category})`).join("\n");
188
+ return { success: true, result: `Gallery Templates:\n${list}` };
189
+ }
190
+ case "gallery_load": {
191
+ const id = String(args.id || "");
192
+ if (!id) return { success: false, result: "", error: "id is required" };
193
+ const template = galleryTemplates.find(t => t.id === id);
194
+ if (!template) return { success: false, result: "", error: `Template not found: ${id}` };
195
+ activeTemplateId = id;
196
+ return { success: true, result: `Loaded template: ${template.name}\nDescription: ${template.description}` };
197
+ }
198
+ case "gallery_search": {
199
+ const query = String(args.query || "").toLowerCase();
200
+ if (!query) return { success: false, result: "", error: "query is required" };
201
+ const matches = galleryTemplates.filter(t =>
202
+ t.name.toLowerCase().includes(query) || t.description.toLowerCase().includes(query) || t.tags.some(tag => tag.toLowerCase().includes(query))
203
+ );
204
+ if (matches.length === 0) return { success: true, result: "No templates found" };
205
+ const list = matches.map(t => `- ${t.id}: ${t.name}\n ${t.description}`).join("\n");
206
+ return { success: true, result: `Found ${matches.length} templates:\n${list}` };
207
+ }
208
+ default:
209
+ return { success: false, result: "", error: `Unknown tool: ${toolName}` };
210
+ }
211
+ } catch (e) {
212
+ return { success: false, result: "", error: e instanceof Error ? e.message : String(e) };
213
+ }
214
+ };
215
+
216
+ return {
217
+ virtualFS,
218
+ todoList,
219
+ memoryStore,
220
+ witnessChain,
221
+ galleryTemplates,
222
+ executeWasmTool,
223
+ getActiveTemplateId: () => activeTemplateId,
224
+ };
225
+ };
226
+
227
+ describe("WASM MCP Tools", () => {
228
+ let state: ReturnType<typeof createTestState>;
229
+
230
+ beforeEach(() => {
231
+ state = createTestState();
232
+ });
233
+
234
+ // ================================
235
+ // File Operations Tests
236
+ // ================================
237
+ describe("File Operations", () => {
238
+ it("write_file creates a new file", () => {
239
+ const result = state.executeWasmTool("write_file", { path: "test.txt", content: "Hello World" });
240
+ expect(result.success).toBe(true);
241
+ expect(result.result).toContain("11 bytes");
242
+ expect(state.virtualFS.get("test.txt")).toBe("Hello World");
243
+ });
244
+
245
+ it("read_file reads existing file", () => {
246
+ state.virtualFS.set("test.txt", "Hello World");
247
+ const result = state.executeWasmTool("read_file", { path: "test.txt" });
248
+ expect(result.success).toBe(true);
249
+ expect(result.result).toBe("Hello World");
250
+ });
251
+
252
+ it("read_file returns error for non-existent file", () => {
253
+ const result = state.executeWasmTool("read_file", { path: "nonexistent.txt" });
254
+ expect(result.success).toBe(false);
255
+ expect(result.error).toContain("File not found");
256
+ });
257
+
258
+ it("list_files returns empty message when no files", () => {
259
+ const result = state.executeWasmTool("list_files", {});
260
+ expect(result.success).toBe(true);
261
+ expect(result.result).toContain("No files");
262
+ });
263
+
264
+ it("list_files shows all files", () => {
265
+ state.virtualFS.set("a.txt", "A");
266
+ state.virtualFS.set("b.txt", "B");
267
+ const result = state.executeWasmTool("list_files", {});
268
+ expect(result.success).toBe(true);
269
+ expect(result.result).toContain("a.txt");
270
+ expect(result.result).toContain("b.txt");
271
+ });
272
+
273
+ it("delete_file removes existing file", () => {
274
+ state.virtualFS.set("test.txt", "content");
275
+ const result = state.executeWasmTool("delete_file", { path: "test.txt" });
276
+ expect(result.success).toBe(true);
277
+ expect(state.virtualFS.has("test.txt")).toBe(false);
278
+ });
279
+
280
+ it("delete_file returns error for non-existent file", () => {
281
+ const result = state.executeWasmTool("delete_file", { path: "nonexistent.txt" });
282
+ expect(result.success).toBe(false);
283
+ expect(result.error).toContain("File not found");
284
+ });
285
+
286
+ it("edit_file replaces content", () => {
287
+ state.virtualFS.set("test.txt", "Hello World");
288
+ const result = state.executeWasmTool("edit_file", { path: "test.txt", old_content: "World", new_content: "Universe" });
289
+ expect(result.success).toBe(true);
290
+ expect(state.virtualFS.get("test.txt")).toBe("Hello Universe");
291
+ });
292
+
293
+ it("edit_file returns error when old_content not found", () => {
294
+ state.virtualFS.set("test.txt", "Hello World");
295
+ const result = state.executeWasmTool("edit_file", { path: "test.txt", old_content: "NOTFOUND", new_content: "X" });
296
+ expect(result.success).toBe(false);
297
+ expect(result.error).toContain("old_content not found");
298
+ });
299
+
300
+ it("handles files with special characters in content", () => {
301
+ const content = "Line1\nLine2\tTab\r\nWindows\n日本語\n🎉";
302
+ state.executeWasmTool("write_file", { path: "special.txt", content });
303
+ const result = state.executeWasmTool("read_file", { path: "special.txt" });
304
+ expect(result.success).toBe(true);
305
+ expect(result.result).toBe(content);
306
+ });
307
+
308
+ it("handles empty file content", () => {
309
+ state.executeWasmTool("write_file", { path: "empty.txt", content: "" });
310
+ const result = state.executeWasmTool("read_file", { path: "empty.txt" });
311
+ expect(result.success).toBe(true);
312
+ expect(result.result).toBe("");
313
+ });
314
+
315
+ it("handles paths with directories", () => {
316
+ state.executeWasmTool("write_file", { path: "src/lib/file.ts", content: "export {}" });
317
+ const result = state.executeWasmTool("read_file", { path: "src/lib/file.ts" });
318
+ expect(result.success).toBe(true);
319
+ expect(result.result).toBe("export {}");
320
+ });
321
+ });
322
+
323
+ // ================================
324
+ // Search Tools Tests
325
+ // ================================
326
+ describe("Search Tools", () => {
327
+ beforeEach(() => {
328
+ state.virtualFS.set("src/index.ts", "import { foo } from './foo';\nexport const bar = 42;");
329
+ state.virtualFS.set("src/foo.ts", "export const foo = 'hello';\nexport const FOO = 'WORLD';");
330
+ state.virtualFS.set("README.md", "# Project\n\nThis is a test project.");
331
+ });
332
+
333
+ it("grep finds pattern in files", () => {
334
+ const result = state.executeWasmTool("grep", { pattern: "foo" });
335
+ expect(result.success).toBe(true);
336
+ expect(result.result).toContain("src/index.ts");
337
+ expect(result.result).toContain("src/foo.ts");
338
+ });
339
+
340
+ it("grep searches specific file", () => {
341
+ const result = state.executeWasmTool("grep", { pattern: "export", path: "src/foo.ts" });
342
+ expect(result.success).toBe(true);
343
+ expect(result.result).toContain("src/foo.ts");
344
+ expect(result.result).not.toContain("src/index.ts");
345
+ });
346
+
347
+ it("grep returns no matches message", () => {
348
+ const result = state.executeWasmTool("grep", { pattern: "NOTFOUND" });
349
+ expect(result.success).toBe(true);
350
+ expect(result.result).toBe("No matches found");
351
+ });
352
+
353
+ it("grep supports regex patterns", () => {
354
+ const result = state.executeWasmTool("grep", { pattern: "\\d+" });
355
+ expect(result.success).toBe(true);
356
+ expect(result.result).toContain("42");
357
+ });
358
+
359
+ it("grep handles invalid regex", () => {
360
+ const result = state.executeWasmTool("grep", { pattern: "[invalid" });
361
+ expect(result.success).toBe(false);
362
+ expect(result.error).toContain("Invalid regex");
363
+ });
364
+
365
+ it("glob finds matching files", () => {
366
+ const result = state.executeWasmTool("glob", { pattern: "*.ts" });
367
+ expect(result.success).toBe(true);
368
+ // Note: our simple glob implementation requires full path match
369
+ });
370
+
371
+ it("glob returns no matches for non-matching pattern", () => {
372
+ const result = state.executeWasmTool("glob", { pattern: "*.xyz" });
373
+ expect(result.success).toBe(true);
374
+ expect(result.result).toBe("No matches found");
375
+ });
376
+ });
377
+
378
+ // ================================
379
+ // Task Management Tests
380
+ // ================================
381
+ describe("Task Management", () => {
382
+ it("todo_add creates new task", () => {
383
+ const result = state.executeWasmTool("todo_add", { task: "Write tests" });
384
+ expect(result.success).toBe(true);
385
+ expect(result.result).toContain("todo-1");
386
+ expect(state.todoList).toHaveLength(1);
387
+ });
388
+
389
+ it("todo_list shows empty when no tasks", () => {
390
+ const result = state.executeWasmTool("todo_list", {});
391
+ expect(result.success).toBe(true);
392
+ expect(result.result).toContain("No tasks");
393
+ });
394
+
395
+ it("todo_list shows all tasks", () => {
396
+ state.executeWasmTool("todo_add", { task: "Task 1" });
397
+ state.executeWasmTool("todo_add", { task: "Task 2" });
398
+ const result = state.executeWasmTool("todo_list", {});
399
+ expect(result.success).toBe(true);
400
+ expect(result.result).toContain("Task 1");
401
+ expect(result.result).toContain("Task 2");
402
+ expect(result.result).toContain("○"); // uncompleted
403
+ });
404
+
405
+ it("todo_complete marks task as done", () => {
406
+ state.executeWasmTool("todo_add", { task: "Task 1" });
407
+ const completeResult = state.executeWasmTool("todo_complete", { id: "todo-1" });
408
+ expect(completeResult.success).toBe(true);
409
+
410
+ const listResult = state.executeWasmTool("todo_list", {});
411
+ expect(listResult.result).toContain("✓");
412
+ });
413
+
414
+ it("todo_complete returns error for invalid id", () => {
415
+ const result = state.executeWasmTool("todo_complete", { id: "todo-999" });
416
+ expect(result.success).toBe(false);
417
+ expect(result.error).toContain("Task not found");
418
+ });
419
+ });
420
+
421
+ // ================================
422
+ // Memory Tools Tests
423
+ // ================================
424
+ describe("Memory Tools", () => {
425
+ it("memory_store saves entry", () => {
426
+ const result = state.executeWasmTool("memory_store", { key: "pattern-1", value: "Use async/await" });
427
+ expect(result.success).toBe(true);
428
+ expect(state.memoryStore.has("pattern-1")).toBe(true);
429
+ });
430
+
431
+ it("memory_store with tags", () => {
432
+ const result = state.executeWasmTool("memory_store", { key: "pattern-2", value: "Error handling", tags: ["best-practice", "async"] });
433
+ expect(result.success).toBe(true);
434
+ const stored = state.memoryStore.get("pattern-2");
435
+ expect(stored?.tags).toContain("best-practice");
436
+ });
437
+
438
+ it("memory_search finds matching entries", () => {
439
+ state.executeWasmTool("memory_store", { key: "auth-pattern", value: "JWT tokens for authentication" });
440
+ state.executeWasmTool("memory_store", { key: "cache-pattern", value: "Use Redis for caching" });
441
+
442
+ const result = state.executeWasmTool("memory_search", { query: "auth" });
443
+ expect(result.success).toBe(true);
444
+ expect(result.result).toContain("auth-pattern");
445
+ expect(result.result).not.toContain("cache-pattern");
446
+ });
447
+
448
+ it("memory_search respects top_k limit", () => {
449
+ for (let i = 0; i < 10; i++) {
450
+ state.executeWasmTool("memory_store", { key: `test-${i}`, value: `Test value ${i}` });
451
+ }
452
+ const result = state.executeWasmTool("memory_search", { query: "test", top_k: 3 });
453
+ expect(result.success).toBe(true);
454
+ expect(result.result).toContain("Found 3 results");
455
+ });
456
+
457
+ it("memory_search returns no matches message", () => {
458
+ const result = state.executeWasmTool("memory_search", { query: "nonexistent" });
459
+ expect(result.success).toBe(true);
460
+ expect(result.result).toBe("No memories found");
461
+ });
462
+
463
+ it("memory_search searches by tags", () => {
464
+ state.executeWasmTool("memory_store", { key: "p1", value: "Value", tags: ["security", "critical"] });
465
+ const result = state.executeWasmTool("memory_search", { query: "security" });
466
+ expect(result.success).toBe(true);
467
+ expect(result.result).toContain("p1");
468
+ });
469
+ });
470
+
471
+ // ================================
472
+ // Witness Chain Tests
473
+ // ================================
474
+ describe("Witness Chain", () => {
475
+ it("witness_log creates entry", () => {
476
+ const result = state.executeWasmTool("witness_log", { action: "file_created", data: { path: "test.txt" } });
477
+ expect(result.success).toBe(true);
478
+ expect(result.result).toContain("hash:");
479
+ // Chain includes tool calls + explicit log
480
+ expect(state.witnessChain.length).toBeGreaterThan(0);
481
+ });
482
+
483
+ it("witness_verify validates chain integrity", () => {
484
+ state.executeWasmTool("witness_log", { action: "action1" });
485
+ state.executeWasmTool("witness_log", { action: "action2" });
486
+ const result = state.executeWasmTool("witness_verify", {});
487
+ expect(result.success).toBe(true);
488
+ expect(result.result).toContain("VALID");
489
+ });
490
+
491
+ it("all tool calls are logged to witness chain", () => {
492
+ const initialLength = state.witnessChain.length;
493
+ state.executeWasmTool("write_file", { path: "a.txt", content: "A" });
494
+ state.executeWasmTool("read_file", { path: "a.txt" });
495
+ expect(state.witnessChain.length).toBe(initialLength + 2);
496
+ });
497
+
498
+ it("witness chain hash linking is correct", () => {
499
+ state.executeWasmTool("witness_log", { action: "a1" });
500
+ state.executeWasmTool("witness_log", { action: "a2" });
501
+
502
+ const chain = state.witnessChain;
503
+ for (let i = 1; i < chain.length; i++) {
504
+ expect(chain[i].prevHash).toBe(chain[i - 1].hash);
505
+ }
506
+ });
507
+ });
508
+
509
+ // ================================
510
+ // Gallery Tools Tests
511
+ // ================================
512
+ describe("Gallery Tools", () => {
513
+ it("gallery_list shows all templates", () => {
514
+ const result = state.executeWasmTool("gallery_list", {});
515
+ expect(result.success).toBe(true);
516
+ expect(result.result).toContain("development-agent");
517
+ expect(result.result).toContain("research-agent");
518
+ });
519
+
520
+ it("gallery_list filters by category", () => {
521
+ const result = state.executeWasmTool("gallery_list", { category: "security" });
522
+ expect(result.success).toBe(true);
523
+ expect(result.result).toContain("security-agent");
524
+ expect(result.result).not.toContain("development-agent");
525
+ });
526
+
527
+ it("gallery_load activates template", () => {
528
+ const result = state.executeWasmTool("gallery_load", { id: "development-agent" });
529
+ expect(result.success).toBe(true);
530
+ expect(result.result).toContain("Development Agent");
531
+ expect(state.getActiveTemplateId()).toBe("development-agent");
532
+ });
533
+
534
+ it("gallery_load returns error for invalid id", () => {
535
+ const result = state.executeWasmTool("gallery_load", { id: "nonexistent" });
536
+ expect(result.success).toBe(false);
537
+ expect(result.error).toContain("Template not found");
538
+ });
539
+
540
+ it("gallery_search finds by name", () => {
541
+ const result = state.executeWasmTool("gallery_search", { query: "research" });
542
+ expect(result.success).toBe(true);
543
+ expect(result.result).toContain("research-agent");
544
+ });
545
+
546
+ it("gallery_search finds by tags", () => {
547
+ const result = state.executeWasmTool("gallery_search", { query: "coding" });
548
+ expect(result.success).toBe(true);
549
+ expect(result.result).toContain("development-agent");
550
+ });
551
+
552
+ it("gallery_search returns no matches message", () => {
553
+ const result = state.executeWasmTool("gallery_search", { query: "xyz123" });
554
+ expect(result.success).toBe(true);
555
+ expect(result.result).toContain("No templates found");
556
+ });
557
+ });
558
+
559
+ // ================================
560
+ // Edge Cases & Error Handling
561
+ // ================================
562
+ describe("Edge Cases", () => {
563
+ it("handles missing required parameters", () => {
564
+ expect(state.executeWasmTool("read_file", {}).success).toBe(false);
565
+ expect(state.executeWasmTool("write_file", { path: "x" }).success).toBe(true); // content defaults to ""
566
+ expect(state.executeWasmTool("todo_add", {}).success).toBe(false);
567
+ expect(state.executeWasmTool("memory_store", { key: "k" }).success).toBe(false);
568
+ });
569
+
570
+ it("handles unknown tool names", () => {
571
+ const result = state.executeWasmTool("unknown_tool", {});
572
+ expect(result.success).toBe(false);
573
+ expect(result.error).toContain("Unknown tool");
574
+ });
575
+
576
+ it("handles large file content", () => {
577
+ const largeContent = "x".repeat(1000000); // 1MB
578
+ const writeResult = state.executeWasmTool("write_file", { path: "large.txt", content: largeContent });
579
+ expect(writeResult.success).toBe(true);
580
+
581
+ const readResult = state.executeWasmTool("read_file", { path: "large.txt" });
582
+ expect(readResult.success).toBe(true);
583
+ expect(readResult.result.length).toBe(1000000);
584
+ });
585
+
586
+ it("handles concurrent-like operations", () => {
587
+ // Simulate multiple operations
588
+ for (let i = 0; i < 100; i++) {
589
+ state.executeWasmTool("write_file", { path: `file${i}.txt`, content: `content${i}` });
590
+ }
591
+ const listResult = state.executeWasmTool("list_files", {});
592
+ expect(listResult.success).toBe(true);
593
+ expect(state.virtualFS.size).toBe(100);
594
+ });
595
+ });
596
+
597
+ // ================================
598
+ // Performance Benchmarks
599
+ // ================================
600
+ describe("Performance", () => {
601
+ it("file operations complete in under 1ms", () => {
602
+ const start = performance.now();
603
+ for (let i = 0; i < 100; i++) {
604
+ state.executeWasmTool("write_file", { path: `perf${i}.txt`, content: "test" });
605
+ }
606
+ const duration = performance.now() - start;
607
+ expect(duration).toBeLessThan(100); // 100 ops in <100ms = <1ms each
608
+ });
609
+
610
+ it("memory search scales with O(n)", () => {
611
+ // Insert 1000 entries
612
+ for (let i = 0; i < 1000; i++) {
613
+ state.executeWasmTool("memory_store", { key: `key-${i}`, value: `value-${i}` });
614
+ }
615
+
616
+ const start = performance.now();
617
+ for (let i = 0; i < 10; i++) {
618
+ state.executeWasmTool("memory_search", { query: "key-500" });
619
+ }
620
+ const duration = performance.now() - start;
621
+ expect(duration).toBeLessThan(100); // 10 searches in <100ms
622
+ });
623
+
624
+ it("witness chain grows correctly", () => {
625
+ const initialLength = state.witnessChain.length;
626
+ // Each witness_log creates 2 entries: one for the tool call audit + one for the explicit log
627
+ for (let i = 0; i < 100; i++) {
628
+ state.executeWasmTool("witness_log", { action: `action-${i}` });
629
+ }
630
+ expect(state.witnessChain.length).toBe(initialLength + 200); // 100 calls * 2 entries each
631
+ });
632
+ });
633
+ });