ruflo 3.10.36 → 3.10.37

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 +416 -416
  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/.devcontainer/Dockerfile +9 -9
  22. package/src/ruvocal/.devcontainer/devcontainer.json +36 -36
  23. package/src/ruvocal/.dockerignore +16 -16
  24. package/src/ruvocal/.eslintignore +13 -13
  25. package/src/ruvocal/.eslintrc.cjs +45 -45
  26. package/src/ruvocal/.gcloudignore +18 -18
  27. package/src/ruvocal/.github/ISSUE_TEMPLATE/bug-report--chat-ui-.md +43 -43
  28. package/src/ruvocal/.github/ISSUE_TEMPLATE/config-support.md +9 -9
  29. package/src/ruvocal/.github/ISSUE_TEMPLATE/feature-request--chat-ui-.md +17 -17
  30. package/src/ruvocal/.github/ISSUE_TEMPLATE/huggingchat.md +11 -11
  31. package/src/ruvocal/.github/release.yml +16 -16
  32. package/src/ruvocal/.github/workflows/build-docs.yml +18 -18
  33. package/src/ruvocal/.github/workflows/build-image.yml +142 -142
  34. package/src/ruvocal/.github/workflows/build-pr-docs.yml +20 -20
  35. package/src/ruvocal/.github/workflows/deploy-dev.yml +63 -63
  36. package/src/ruvocal/.github/workflows/deploy-prod.yml +78 -78
  37. package/src/ruvocal/.github/workflows/lint-and-test.yml +84 -84
  38. package/src/ruvocal/.github/workflows/slugify.yaml +72 -72
  39. package/src/ruvocal/.github/workflows/trufflehog.yml +17 -17
  40. package/src/ruvocal/.github/workflows/upload-pr-documentation.yml +16 -16
  41. package/src/ruvocal/.husky/lint-stage-config.js +4 -4
  42. package/src/ruvocal/.husky/pre-commit +2 -2
  43. package/src/ruvocal/.prettierignore +14 -14
  44. package/src/ruvocal/.prettierrc +7 -7
  45. package/src/ruvocal/CLAUDE.md +126 -126
  46. package/src/ruvocal/Dockerfile +96 -96
  47. package/src/ruvocal/LICENSE +202 -202
  48. package/src/ruvocal/PRIVACY.md +41 -41
  49. package/src/ruvocal/README.md +164 -164
  50. package/src/ruvocal/chart/Chart.yaml +5 -5
  51. package/src/ruvocal/chart/env/dev.yaml +260 -260
  52. package/src/ruvocal/chart/env/prod.yaml +273 -273
  53. package/src/ruvocal/chart/templates/_helpers.tpl +22 -22
  54. package/src/ruvocal/chart/templates/config.yaml +10 -10
  55. package/src/ruvocal/chart/templates/deployment.yaml +81 -81
  56. package/src/ruvocal/chart/templates/hpa.yaml +45 -45
  57. package/src/ruvocal/chart/templates/infisical.yaml +24 -24
  58. package/src/ruvocal/chart/templates/ingress-internal.yaml +32 -32
  59. package/src/ruvocal/chart/templates/ingress.yaml +32 -32
  60. package/src/ruvocal/chart/templates/network-policy.yaml +36 -36
  61. package/src/ruvocal/chart/templates/service-account.yaml +13 -13
  62. package/src/ruvocal/chart/templates/service-monitor.yaml +17 -17
  63. package/src/ruvocal/chart/templates/service.yaml +21 -21
  64. package/src/ruvocal/chart/values.yaml +73 -73
  65. package/src/ruvocal/cloudbuild.yaml +68 -68
  66. package/src/ruvocal/config/branding.env.example +19 -19
  67. package/src/ruvocal/docker-compose.yml +21 -21
  68. package/src/ruvocal/docs/adr/ADR-029-HUGGINGFACE-CHAT-UI-CLOUD-RUN.md +1236 -1236
  69. package/src/ruvocal/docs/adr/ADR-033-RUVECTOR-RUFLO-MCP-INTEGRATION.md +111 -111
  70. package/src/ruvocal/docs/adr/ADR-034-OPTIONAL-MCP-BACKENDS.md +117 -117
  71. package/src/ruvocal/docs/adr/ADR-035-MCP-TOOL-GROUPS.md +186 -186
  72. package/src/ruvocal/docs/adr/ADR-037-AUTOPILOT-CHAT-MODE.md +1500 -1500
  73. package/src/ruvocal/docs/adr/ADR-038-RUVOCAL-FORK.md +286 -286
  74. package/src/ruvocal/docs/source/_toctree.yml +30 -30
  75. package/src/ruvocal/docs/source/configuration/common-issues.md +38 -38
  76. package/src/ruvocal/docs/source/configuration/llm-router.md +105 -105
  77. package/src/ruvocal/docs/source/configuration/mcp-tools.md +84 -84
  78. package/src/ruvocal/docs/source/configuration/metrics.md +9 -9
  79. package/src/ruvocal/docs/source/configuration/open-id.md +57 -57
  80. package/src/ruvocal/docs/source/configuration/overview.md +89 -89
  81. package/src/ruvocal/docs/source/configuration/theming.md +20 -20
  82. package/src/ruvocal/docs/source/developing/architecture.md +48 -48
  83. package/src/ruvocal/docs/source/index.md +53 -53
  84. package/src/ruvocal/docs/source/installation/docker.md +43 -43
  85. package/src/ruvocal/docs/source/installation/helm.md +43 -43
  86. package/src/ruvocal/docs/source/installation/local.md +62 -62
  87. package/src/ruvocal/entrypoint.sh +18 -18
  88. package/src/ruvocal/mcp-bridge/Dockerfile +45 -45
  89. package/src/ruvocal/mcp-bridge/cloudbuild.yaml +49 -49
  90. package/src/ruvocal/mcp-bridge/index.js +1902 -1902
  91. package/src/ruvocal/mcp-bridge/mcp-stdio-kernel.js +159 -159
  92. package/src/ruvocal/mcp-bridge/package-lock.json +762 -762
  93. package/src/ruvocal/mcp-bridge/package.json +17 -17
  94. package/src/ruvocal/mcp-bridge/test-harness.js +470 -470
  95. package/src/ruvocal/package-lock.json +11741 -11741
  96. package/src/ruvocal/package.json +121 -121
  97. package/src/ruvocal/postcss.config.js +6 -6
  98. package/src/ruvocal/rvf.manifest.json +204 -204
  99. package/src/ruvocal/scripts/config.ts +64 -64
  100. package/src/ruvocal/scripts/generate-welcome.mjs +181 -181
  101. package/src/ruvocal/scripts/populate.ts +288 -288
  102. package/src/ruvocal/scripts/samples.txt +194 -194
  103. package/src/ruvocal/scripts/setups/vitest-setup-server.ts +44 -44
  104. package/src/ruvocal/scripts/updateLocalEnv.ts +48 -48
  105. package/src/ruvocal/src/ambient.d.ts +7 -7
  106. package/src/ruvocal/src/app.d.ts +29 -29
  107. package/src/ruvocal/src/app.html +53 -53
  108. package/src/ruvocal/src/hooks.server.ts +32 -32
  109. package/src/ruvocal/src/hooks.ts +6 -6
  110. package/src/ruvocal/src/lib/APIClient.ts +148 -148
  111. package/src/ruvocal/src/lib/actions/clickOutside.ts +18 -18
  112. package/src/ruvocal/src/lib/actions/snapScrollToBottom.ts +346 -346
  113. package/src/ruvocal/src/lib/buildPrompt.ts +33 -33
  114. package/src/ruvocal/src/lib/components/AnnouncementBanner.svelte +20 -20
  115. package/src/ruvocal/src/lib/components/BackgroundGenerationPoller.svelte +168 -168
  116. package/src/ruvocal/src/lib/components/CodeBlock.svelte +73 -73
  117. package/src/ruvocal/src/lib/components/CopyToClipBoardBtn.svelte +92 -92
  118. package/src/ruvocal/src/lib/components/DeleteConversationModal.svelte +75 -75
  119. package/src/ruvocal/src/lib/components/EditConversationModal.svelte +100 -100
  120. package/src/ruvocal/src/lib/components/ExpandNavigation.svelte +22 -22
  121. package/src/ruvocal/src/lib/components/FoundationBackground.svelte +242 -242
  122. package/src/ruvocal/src/lib/components/HoverTooltip.svelte +44 -44
  123. package/src/ruvocal/src/lib/components/HtmlPreviewModal.svelte +143 -143
  124. package/src/ruvocal/src/lib/components/InfiniteScroll.svelte +50 -50
  125. package/src/ruvocal/src/lib/components/MobileNav.svelte +300 -300
  126. package/src/ruvocal/src/lib/components/Modal.svelte +115 -115
  127. package/src/ruvocal/src/lib/components/ModelCardMetadata.svelte +71 -71
  128. package/src/ruvocal/src/lib/components/NavConversationItem.svelte +151 -151
  129. package/src/ruvocal/src/lib/components/NavMenu.svelte +313 -313
  130. package/src/ruvocal/src/lib/components/Pagination.svelte +97 -97
  131. package/src/ruvocal/src/lib/components/PaginationArrow.svelte +27 -27
  132. package/src/ruvocal/src/lib/components/Portal.svelte +24 -24
  133. package/src/ruvocal/src/lib/components/RetryBtn.svelte +18 -18
  134. package/src/ruvocal/src/lib/components/RuFloUniverse.svelte +185 -185
  135. package/src/ruvocal/src/lib/components/RufloHelpModal.svelte +411 -411
  136. package/src/ruvocal/src/lib/components/ScrollToBottomBtn.svelte +47 -47
  137. package/src/ruvocal/src/lib/components/ScrollToPreviousBtn.svelte +77 -77
  138. package/src/ruvocal/src/lib/components/ShareConversationModal.svelte +182 -182
  139. package/src/ruvocal/src/lib/components/StopGeneratingBtn.svelte +69 -69
  140. package/src/ruvocal/src/lib/components/SubscribeModal.svelte +87 -87
  141. package/src/ruvocal/src/lib/components/Switch.svelte +36 -36
  142. package/src/ruvocal/src/lib/components/SystemPromptModal.svelte +44 -44
  143. package/src/ruvocal/src/lib/components/Toast.svelte +27 -27
  144. package/src/ruvocal/src/lib/components/Tooltip.svelte +30 -30
  145. package/src/ruvocal/src/lib/components/WelcomeModal.svelte +46 -46
  146. package/src/ruvocal/src/lib/components/chat/Alternatives.svelte +77 -77
  147. package/src/ruvocal/src/lib/components/chat/BlockWrapper.svelte +72 -72
  148. package/src/ruvocal/src/lib/components/chat/ChatInput.svelte +490 -490
  149. package/src/ruvocal/src/lib/components/chat/ChatIntroduction.svelte +123 -123
  150. package/src/ruvocal/src/lib/components/chat/ChatMessage.svelte +548 -548
  151. package/src/ruvocal/src/lib/components/chat/ChatWindow.svelte +1057 -1057
  152. package/src/ruvocal/src/lib/components/chat/FileDropzone.svelte +92 -92
  153. package/src/ruvocal/src/lib/components/chat/ImageLightbox.svelte +66 -66
  154. package/src/ruvocal/src/lib/components/chat/MarkdownBlock.svelte +23 -23
  155. package/src/ruvocal/src/lib/components/chat/MarkdownRenderer.svelte +69 -69
  156. package/src/ruvocal/src/lib/components/chat/MarkdownRenderer.svelte.test.ts +58 -58
  157. package/src/ruvocal/src/lib/components/chat/MessageAvatar.svelte +103 -103
  158. package/src/ruvocal/src/lib/components/chat/ModelSwitch.svelte +64 -64
  159. package/src/ruvocal/src/lib/components/chat/OpenReasoningResults.svelte +81 -81
  160. package/src/ruvocal/src/lib/components/chat/TaskGroup.svelte +88 -88
  161. package/src/ruvocal/src/lib/components/chat/ToolUpdate.svelte +273 -273
  162. package/src/ruvocal/src/lib/components/chat/UploadedFile.svelte +253 -253
  163. package/src/ruvocal/src/lib/components/chat/UrlFetchModal.svelte +203 -203
  164. package/src/ruvocal/src/lib/components/chat/VoiceRecorder.svelte +214 -214
  165. package/src/ruvocal/src/lib/components/icons/IconBurger.svelte +20 -20
  166. package/src/ruvocal/src/lib/components/icons/IconCheap.svelte +20 -20
  167. package/src/ruvocal/src/lib/components/icons/IconChevron.svelte +24 -24
  168. package/src/ruvocal/src/lib/components/icons/IconDazzled.svelte +40 -40
  169. package/src/ruvocal/src/lib/components/icons/IconFast.svelte +20 -20
  170. package/src/ruvocal/src/lib/components/icons/IconLoading.svelte +22 -22
  171. package/src/ruvocal/src/lib/components/icons/IconMCP.svelte +28 -28
  172. package/src/ruvocal/src/lib/components/icons/IconMoon.svelte +21 -21
  173. package/src/ruvocal/src/lib/components/icons/IconNew.svelte +20 -20
  174. package/src/ruvocal/src/lib/components/icons/IconOmni.svelte +90 -90
  175. package/src/ruvocal/src/lib/components/icons/IconPaperclip.svelte +24 -24
  176. package/src/ruvocal/src/lib/components/icons/IconPro.svelte +37 -37
  177. package/src/ruvocal/src/lib/components/icons/IconShare.svelte +21 -21
  178. package/src/ruvocal/src/lib/components/icons/IconSun.svelte +93 -93
  179. package/src/ruvocal/src/lib/components/icons/Logo.svelte +68 -68
  180. package/src/ruvocal/src/lib/components/icons/LogoHuggingFaceBorderless.svelte +54 -54
  181. package/src/ruvocal/src/lib/components/mcp/AddServerForm.svelte +250 -250
  182. package/src/ruvocal/src/lib/components/mcp/MCPServerManager.svelte +185 -185
  183. package/src/ruvocal/src/lib/components/mcp/ServerCard.svelte +203 -203
  184. package/src/ruvocal/src/lib/components/players/AudioPlayer.svelte +82 -82
  185. package/src/ruvocal/src/lib/components/voice/AudioWaveform.svelte +96 -96
  186. package/src/ruvocal/src/lib/components/wasm/GalleryPanel.svelte +357 -357
  187. package/src/ruvocal/src/lib/constants/mcpExamples.ts +114 -114
  188. package/src/ruvocal/src/lib/constants/mime.ts +11 -11
  189. package/src/ruvocal/src/lib/constants/pagination.ts +1 -1
  190. package/src/ruvocal/src/lib/constants/publicSepToken.ts +1 -1
  191. package/src/ruvocal/src/lib/constants/routerExamples.ts +133 -133
  192. package/src/ruvocal/src/lib/constants/rvagentPresets.ts +206 -206
  193. package/src/ruvocal/src/lib/createShareLink.ts +27 -27
  194. package/src/ruvocal/src/lib/jobs/refresh-conversation-stats.ts +297 -297
  195. package/src/ruvocal/src/lib/migrations/lock.ts +56 -56
  196. package/src/ruvocal/src/lib/migrations/migrations.spec.ts +74 -74
  197. package/src/ruvocal/src/lib/migrations/migrations.ts +109 -109
  198. package/src/ruvocal/src/lib/migrations/routines/01-update-search-assistants.ts +50 -50
  199. package/src/ruvocal/src/lib/migrations/routines/02-update-assistants-models.ts +48 -48
  200. package/src/ruvocal/src/lib/migrations/routines/04-update-message-updates.ts +151 -151
  201. package/src/ruvocal/src/lib/migrations/routines/05-update-message-files.ts +56 -56
  202. package/src/ruvocal/src/lib/migrations/routines/06-trim-message-updates.ts +56 -56
  203. package/src/ruvocal/src/lib/migrations/routines/08-update-featured-to-review.ts +32 -32
  204. package/src/ruvocal/src/lib/migrations/routines/09-delete-empty-conversations.spec.ts +214 -214
  205. package/src/ruvocal/src/lib/migrations/routines/09-delete-empty-conversations.ts +88 -88
  206. package/src/ruvocal/src/lib/migrations/routines/10-update-reports-assistantid.ts +29 -29
  207. package/src/ruvocal/src/lib/migrations/routines/index.ts +15 -15
  208. package/src/ruvocal/src/lib/server/__tests__/conversation-stop-generating.spec.ts +103 -103
  209. package/src/ruvocal/src/lib/server/abortRegistry.ts +57 -57
  210. package/src/ruvocal/src/lib/server/abortedGenerations.ts +43 -43
  211. package/src/ruvocal/src/lib/server/adminToken.ts +62 -62
  212. package/src/ruvocal/src/lib/server/api/__tests__/conversations-id.spec.ts +296 -296
  213. package/src/ruvocal/src/lib/server/api/__tests__/conversations-message.spec.ts +216 -216
  214. package/src/ruvocal/src/lib/server/api/__tests__/conversations.spec.ts +235 -235
  215. package/src/ruvocal/src/lib/server/api/__tests__/misc.spec.ts +72 -72
  216. package/src/ruvocal/src/lib/server/api/__tests__/testHelpers.ts +86 -86
  217. package/src/ruvocal/src/lib/server/api/__tests__/user-reports.spec.ts +78 -78
  218. package/src/ruvocal/src/lib/server/api/__tests__/user.spec.ts +239 -239
  219. package/src/ruvocal/src/lib/server/api/types.ts +37 -37
  220. package/src/ruvocal/src/lib/server/api/utils/requireAuth.ts +22 -22
  221. package/src/ruvocal/src/lib/server/api/utils/resolveConversation.ts +69 -69
  222. package/src/ruvocal/src/lib/server/api/utils/resolveModel.ts +27 -27
  223. package/src/ruvocal/src/lib/server/api/utils/superjsonResponse.ts +15 -15
  224. package/src/ruvocal/src/lib/server/apiToken.ts +11 -11
  225. package/src/ruvocal/src/lib/server/auth.ts +554 -554
  226. package/src/ruvocal/src/lib/server/config.ts +187 -187
  227. package/src/ruvocal/src/lib/server/conversation.ts +83 -83
  228. package/src/ruvocal/src/lib/server/database/__tests__/rvf.spec.ts +709 -709
  229. package/src/ruvocal/src/lib/server/database/postgres.ts +700 -700
  230. package/src/ruvocal/src/lib/server/database/rvf.ts +1078 -1078
  231. package/src/ruvocal/src/lib/server/database.ts +145 -145
  232. package/src/ruvocal/src/lib/server/endpoints/document.ts +68 -68
  233. package/src/ruvocal/src/lib/server/endpoints/endpoints.ts +43 -43
  234. package/src/ruvocal/src/lib/server/endpoints/images.ts +211 -211
  235. package/src/ruvocal/src/lib/server/endpoints/openai/endpointOai.ts +266 -266
  236. package/src/ruvocal/src/lib/server/endpoints/openai/openAIChatToTextGenerationStream.ts +212 -212
  237. package/src/ruvocal/src/lib/server/endpoints/openai/openAICompletionToTextGenerationStream.ts +32 -32
  238. package/src/ruvocal/src/lib/server/endpoints/preprocessMessages.ts +61 -61
  239. package/src/ruvocal/src/lib/server/exitHandler.ts +59 -59
  240. package/src/ruvocal/src/lib/server/files/downloadFile.ts +34 -34
  241. package/src/ruvocal/src/lib/server/files/uploadFile.ts +29 -29
  242. package/src/ruvocal/src/lib/server/findRepoRoot.ts +13 -13
  243. package/src/ruvocal/src/lib/server/generateFromDefaultEndpoint.ts +46 -46
  244. package/src/ruvocal/src/lib/server/hooks/error.ts +37 -37
  245. package/src/ruvocal/src/lib/server/hooks/fetch.ts +22 -22
  246. package/src/ruvocal/src/lib/server/hooks/handle.ts +250 -250
  247. package/src/ruvocal/src/lib/server/hooks/init.ts +51 -51
  248. package/src/ruvocal/src/lib/server/isURLLocal.spec.ts +31 -31
  249. package/src/ruvocal/src/lib/server/isURLLocal.ts +74 -74
  250. package/src/ruvocal/src/lib/server/logger.ts +42 -42
  251. package/src/ruvocal/src/lib/server/mcp/clientPool.spec.ts +175 -175
  252. package/src/ruvocal/src/lib/server/mcp/hf.ts +32 -32
  253. package/src/ruvocal/src/lib/server/mcp/httpClient.ts +122 -122
  254. package/src/ruvocal/src/lib/server/mcp/registry.ts +76 -76
  255. package/src/ruvocal/src/lib/server/mcp/tools.ts +196 -196
  256. package/src/ruvocal/src/lib/server/metrics.ts +255 -255
  257. package/src/ruvocal/src/lib/server/models.ts +518 -518
  258. package/src/ruvocal/src/lib/server/requestContext.ts +55 -55
  259. package/src/ruvocal/src/lib/server/router/arch.ts +230 -230
  260. package/src/ruvocal/src/lib/server/router/endpoint.ts +316 -316
  261. package/src/ruvocal/src/lib/server/router/multimodal.ts +28 -28
  262. package/src/ruvocal/src/lib/server/router/policy.ts +49 -49
  263. package/src/ruvocal/src/lib/server/router/toolsRoute.ts +51 -51
  264. package/src/ruvocal/src/lib/server/router/types.ts +21 -21
  265. package/src/ruvocal/src/lib/server/sendSlack.ts +23 -23
  266. package/src/ruvocal/src/lib/server/textGeneration/generate.ts +258 -258
  267. package/src/ruvocal/src/lib/server/textGeneration/index.ts +96 -96
  268. package/src/ruvocal/src/lib/server/textGeneration/mcp/fileRefs.ts +155 -155
  269. package/src/ruvocal/src/lib/server/textGeneration/mcp/routerResolution.ts +108 -108
  270. package/src/ruvocal/src/lib/server/textGeneration/mcp/runMcpFlow.ts +831 -831
  271. package/src/ruvocal/src/lib/server/textGeneration/mcp/toolInvocation.ts +349 -349
  272. package/src/ruvocal/src/lib/server/textGeneration/mcp/wasmTools.test.ts +633 -633
  273. package/src/ruvocal/src/lib/server/textGeneration/reasoning.ts +23 -23
  274. package/src/ruvocal/src/lib/server/textGeneration/title.ts +83 -83
  275. package/src/ruvocal/src/lib/server/textGeneration/types.ts +28 -28
  276. package/src/ruvocal/src/lib/server/textGeneration/utils/prepareFiles.ts +88 -88
  277. package/src/ruvocal/src/lib/server/textGeneration/utils/routing.ts +21 -21
  278. package/src/ruvocal/src/lib/server/textGeneration/utils/toolPrompt.ts +49 -49
  279. package/src/ruvocal/src/lib/server/urlSafety.ts +77 -77
  280. package/src/ruvocal/src/lib/server/usageLimits.ts +30 -30
  281. package/src/ruvocal/src/lib/stores/autopilotStore.svelte.ts +175 -175
  282. package/src/ruvocal/src/lib/stores/backgroundGenerations.svelte.ts +32 -32
  283. package/src/ruvocal/src/lib/stores/backgroundGenerations.ts +1 -1
  284. package/src/ruvocal/src/lib/stores/errors.ts +9 -9
  285. package/src/ruvocal/src/lib/stores/isAborted.ts +3 -3
  286. package/src/ruvocal/src/lib/stores/isPro.ts +4 -4
  287. package/src/ruvocal/src/lib/stores/loading.ts +3 -3
  288. package/src/ruvocal/src/lib/stores/mcpServers.ts +534 -534
  289. package/src/ruvocal/src/lib/stores/pendingChatInput.ts +3 -3
  290. package/src/ruvocal/src/lib/stores/pendingMessage.ts +9 -9
  291. package/src/ruvocal/src/lib/stores/settings.ts +182 -182
  292. package/src/ruvocal/src/lib/stores/shareModal.ts +13 -13
  293. package/src/ruvocal/src/lib/stores/titleUpdate.ts +8 -8
  294. package/src/ruvocal/src/lib/stores/wasmMcp.ts +472 -472
  295. package/src/ruvocal/src/lib/switchTheme.ts +124 -124
  296. package/src/ruvocal/src/lib/types/AbortedGeneration.ts +8 -8
  297. package/src/ruvocal/src/lib/types/Assistant.ts +31 -31
  298. package/src/ruvocal/src/lib/types/AssistantStats.ts +11 -11
  299. package/src/ruvocal/src/lib/types/ConfigKey.ts +4 -4
  300. package/src/ruvocal/src/lib/types/ConvSidebar.ts +9 -9
  301. package/src/ruvocal/src/lib/types/Conversation.ts +27 -27
  302. package/src/ruvocal/src/lib/types/ConversationStats.ts +13 -13
  303. package/src/ruvocal/src/lib/types/Message.ts +41 -41
  304. package/src/ruvocal/src/lib/types/MessageEvent.ts +10 -10
  305. package/src/ruvocal/src/lib/types/MessageUpdate.ts +139 -139
  306. package/src/ruvocal/src/lib/types/MigrationResult.ts +7 -7
  307. package/src/ruvocal/src/lib/types/Model.ts +23 -23
  308. package/src/ruvocal/src/lib/types/Report.ts +12 -12
  309. package/src/ruvocal/src/lib/types/Review.ts +6 -6
  310. package/src/ruvocal/src/lib/types/Semaphore.ts +19 -19
  311. package/src/ruvocal/src/lib/types/Session.ts +22 -22
  312. package/src/ruvocal/src/lib/types/Settings.ts +93 -93
  313. package/src/ruvocal/src/lib/types/SharedConversation.ts +9 -9
  314. package/src/ruvocal/src/lib/types/Template.ts +6 -6
  315. package/src/ruvocal/src/lib/types/Timestamps.ts +4 -4
  316. package/src/ruvocal/src/lib/types/TokenCache.ts +6 -6
  317. package/src/ruvocal/src/lib/types/Tool.ts +77 -77
  318. package/src/ruvocal/src/lib/types/UrlDependency.ts +5 -5
  319. package/src/ruvocal/src/lib/types/User.ts +14 -14
  320. package/src/ruvocal/src/lib/utils/PublicConfig.svelte.ts +75 -75
  321. package/src/ruvocal/src/lib/utils/auth.ts +17 -17
  322. package/src/ruvocal/src/lib/utils/chunk.ts +33 -33
  323. package/src/ruvocal/src/lib/utils/cookiesAreEnabled.ts +13 -13
  324. package/src/ruvocal/src/lib/utils/debounce.ts +17 -17
  325. package/src/ruvocal/src/lib/utils/deepestChild.ts +6 -6
  326. package/src/ruvocal/src/lib/utils/favicon.ts +21 -21
  327. package/src/ruvocal/src/lib/utils/fetchJSON.ts +23 -23
  328. package/src/ruvocal/src/lib/utils/file2base64.ts +14 -14
  329. package/src/ruvocal/src/lib/utils/formatUserCount.ts +37 -37
  330. package/src/ruvocal/src/lib/utils/generationState.spec.ts +75 -75
  331. package/src/ruvocal/src/lib/utils/generationState.ts +26 -26
  332. package/src/ruvocal/src/lib/utils/getHref.ts +41 -41
  333. package/src/ruvocal/src/lib/utils/getReturnFromGenerator.ts +7 -7
  334. package/src/ruvocal/src/lib/utils/haptics.ts +64 -64
  335. package/src/ruvocal/src/lib/utils/hashConv.ts +12 -12
  336. package/src/ruvocal/src/lib/utils/hf.ts +17 -17
  337. package/src/ruvocal/src/lib/utils/isDesktop.ts +7 -7
  338. package/src/ruvocal/src/lib/utils/isUrl.ts +8 -8
  339. package/src/ruvocal/src/lib/utils/isVirtualKeyboard.ts +16 -16
  340. package/src/ruvocal/src/lib/utils/loadAttachmentsFromUrls.ts +115 -115
  341. package/src/ruvocal/src/lib/utils/marked.spec.ts +96 -96
  342. package/src/ruvocal/src/lib/utils/marked.ts +531 -531
  343. package/src/ruvocal/src/lib/utils/mcpValidation.ts +147 -147
  344. package/src/ruvocal/src/lib/utils/mergeAsyncGenerators.ts +38 -38
  345. package/src/ruvocal/src/lib/utils/messageUpdates.spec.ts +262 -262
  346. package/src/ruvocal/src/lib/utils/messageUpdates.ts +324 -324
  347. package/src/ruvocal/src/lib/utils/mime.ts +56 -56
  348. package/src/ruvocal/src/lib/utils/models.ts +14 -14
  349. package/src/ruvocal/src/lib/utils/parseBlocks.ts +120 -120
  350. package/src/ruvocal/src/lib/utils/parseIncompleteMarkdown.ts +644 -644
  351. package/src/ruvocal/src/lib/utils/parseStringToList.ts +10 -10
  352. package/src/ruvocal/src/lib/utils/randomUuid.ts +14 -14
  353. package/src/ruvocal/src/lib/utils/searchTokens.ts +33 -33
  354. package/src/ruvocal/src/lib/utils/sha256.ts +7 -7
  355. package/src/ruvocal/src/lib/utils/stringifyError.ts +12 -12
  356. package/src/ruvocal/src/lib/utils/sum.ts +3 -3
  357. package/src/ruvocal/src/lib/utils/template.spec.ts +59 -59
  358. package/src/ruvocal/src/lib/utils/template.ts +53 -53
  359. package/src/ruvocal/src/lib/utils/timeout.ts +9 -9
  360. package/src/ruvocal/src/lib/utils/toolProgress.spec.ts +46 -46
  361. package/src/ruvocal/src/lib/utils/toolProgress.ts +11 -11
  362. package/src/ruvocal/src/lib/utils/tree/addChildren.spec.ts +102 -102
  363. package/src/ruvocal/src/lib/utils/tree/addChildren.ts +48 -48
  364. package/src/ruvocal/src/lib/utils/tree/addSibling.spec.ts +81 -81
  365. package/src/ruvocal/src/lib/utils/tree/addSibling.ts +41 -41
  366. package/src/ruvocal/src/lib/utils/tree/buildSubtree.spec.ts +110 -110
  367. package/src/ruvocal/src/lib/utils/tree/buildSubtree.ts +24 -24
  368. package/src/ruvocal/src/lib/utils/tree/convertLegacyConversation.spec.ts +31 -31
  369. package/src/ruvocal/src/lib/utils/tree/convertLegacyConversation.ts +36 -36
  370. package/src/ruvocal/src/lib/utils/tree/isMessageId.spec.ts +15 -15
  371. package/src/ruvocal/src/lib/utils/tree/isMessageId.ts +5 -5
  372. package/src/ruvocal/src/lib/utils/tree/tree.d.ts +14 -14
  373. package/src/ruvocal/src/lib/utils/tree/treeHelpers.spec.ts +167 -167
  374. package/src/ruvocal/src/lib/utils/updates.ts +39 -39
  375. package/src/ruvocal/src/lib/utils/urlParams.ts +13 -13
  376. package/src/ruvocal/src/lib/wasm/idb.ts +438 -438
  377. package/src/ruvocal/src/lib/wasm/index.ts +1213 -1213
  378. package/src/ruvocal/src/lib/wasm/tests/wasm-capabilities.test.ts +565 -565
  379. package/src/ruvocal/src/lib/wasm/wasm.worker.ts +332 -332
  380. package/src/ruvocal/src/lib/wasm/workerClient.ts +166 -166
  381. package/src/ruvocal/src/lib/workers/autopilotWorker.ts +221 -221
  382. package/src/ruvocal/src/lib/workers/detailFetchWorker.ts +100 -100
  383. package/src/ruvocal/src/lib/workers/markdownWorker.ts +61 -61
  384. package/src/ruvocal/src/routes/+error.svelte +20 -20
  385. package/src/ruvocal/src/routes/+layout.svelte +324 -324
  386. package/src/ruvocal/src/routes/+layout.ts +91 -91
  387. package/src/ruvocal/src/routes/+page.svelte +168 -168
  388. package/src/ruvocal/src/routes/.well-known/oauth-cimd/+server.ts +37 -37
  389. package/src/ruvocal/src/routes/__debug/openai/+server.ts +21 -21
  390. package/src/ruvocal/src/routes/admin/export/+server.ts +159 -159
  391. package/src/ruvocal/src/routes/admin/stats/compute/+server.ts +16 -16
  392. package/src/ruvocal/src/routes/api/conversation/[id]/+server.ts +40 -40
  393. package/src/ruvocal/src/routes/api/conversation/[id]/message/[messageId]/+server.ts +42 -42
  394. package/src/ruvocal/src/routes/api/conversations/+server.ts +48 -48
  395. package/src/ruvocal/src/routes/api/fetch-url/+server.ts +147 -147
  396. package/src/ruvocal/src/routes/api/mcp/health/+server.ts +292 -292
  397. package/src/ruvocal/src/routes/api/mcp/servers/+server.ts +32 -32
  398. package/src/ruvocal/src/routes/api/models/+server.ts +25 -25
  399. package/src/ruvocal/src/routes/api/transcribe/+server.ts +104 -104
  400. package/src/ruvocal/src/routes/api/user/+server.ts +15 -15
  401. package/src/ruvocal/src/routes/api/user/validate-token/+server.ts +20 -20
  402. package/src/ruvocal/src/routes/api/v2/conversations/+server.ts +48 -48
  403. package/src/ruvocal/src/routes/api/v2/conversations/[id]/+server.ts +94 -94
  404. package/src/ruvocal/src/routes/api/v2/conversations/[id]/message/[messageId]/+server.ts +43 -43
  405. package/src/ruvocal/src/routes/api/v2/conversations/import-share/+server.ts +23 -23
  406. package/src/ruvocal/src/routes/api/v2/debug/config/+server.ts +16 -16
  407. package/src/ruvocal/src/routes/api/v2/debug/refresh/+server.ts +30 -30
  408. package/src/ruvocal/src/routes/api/v2/export/+server.ts +196 -196
  409. package/src/ruvocal/src/routes/api/v2/feature-flags/+server.ts +14 -14
  410. package/src/ruvocal/src/routes/api/v2/models/+server.ts +38 -38
  411. package/src/ruvocal/src/routes/api/v2/models/[namespace]/+server.ts +8 -8
  412. package/src/ruvocal/src/routes/api/v2/models/[namespace]/[model]/+server.ts +8 -8
  413. package/src/ruvocal/src/routes/api/v2/models/[namespace]/[model]/subscribe/+server.ts +28 -28
  414. package/src/ruvocal/src/routes/api/v2/models/[namespace]/subscribe/+server.ts +28 -28
  415. package/src/ruvocal/src/routes/api/v2/models/old/+server.ts +7 -7
  416. package/src/ruvocal/src/routes/api/v2/models/refresh/+server.ts +33 -33
  417. package/src/ruvocal/src/routes/api/v2/public-config/+server.ts +7 -7
  418. package/src/ruvocal/src/routes/api/v2/user/+server.ts +17 -17
  419. package/src/ruvocal/src/routes/api/v2/user/billing-orgs/+server.ts +73 -73
  420. package/src/ruvocal/src/routes/api/v2/user/reports/+server.ts +17 -17
  421. package/src/ruvocal/src/routes/api/v2/user/settings/+server.ts +110 -110
  422. package/src/ruvocal/src/routes/conversation/+server.ts +115 -115
  423. package/src/ruvocal/src/routes/conversation/[id]/+page.svelte +586 -586
  424. package/src/ruvocal/src/routes/conversation/[id]/+page.ts +60 -60
  425. package/src/ruvocal/src/routes/conversation/[id]/+server.ts +740 -740
  426. package/src/ruvocal/src/routes/conversation/[id]/message/[messageId]/prompt/+server.ts +66 -66
  427. package/src/ruvocal/src/routes/conversation/[id]/share/+server.ts +69 -69
  428. package/src/ruvocal/src/routes/conversation/[id]/stop-generating/+server.ts +35 -35
  429. package/src/ruvocal/src/routes/healthcheck/+server.ts +3 -3
  430. package/src/ruvocal/src/routes/login/+server.ts +5 -5
  431. package/src/ruvocal/src/routes/login/callback/+server.ts +103 -103
  432. package/src/ruvocal/src/routes/login/callback/updateUser.spec.ts +157 -157
  433. package/src/ruvocal/src/routes/login/callback/updateUser.ts +215 -215
  434. package/src/ruvocal/src/routes/logout/+server.ts +18 -18
  435. package/src/ruvocal/src/routes/metrics/+server.ts +18 -18
  436. package/src/ruvocal/src/routes/models/+page.svelte +233 -233
  437. package/src/ruvocal/src/routes/models/[...model]/+page.svelte +161 -161
  438. package/src/ruvocal/src/routes/models/[...model]/+page.ts +14 -14
  439. package/src/ruvocal/src/routes/models/[...model]/thumbnail.png/+server.ts +64 -64
  440. package/src/ruvocal/src/routes/models/[...model]/thumbnail.png/ModelThumbnail.svelte +28 -28
  441. package/src/ruvocal/src/routes/privacy/+page.svelte +11 -11
  442. package/src/ruvocal/src/routes/r/[id]/+page.ts +34 -34
  443. package/src/ruvocal/src/routes/settings/(nav)/+layout.svelte +282 -282
  444. package/src/ruvocal/src/routes/settings/(nav)/+layout.ts +1 -1
  445. package/src/ruvocal/src/routes/settings/(nav)/+server.ts +59 -59
  446. package/src/ruvocal/src/routes/settings/(nav)/[...model]/+page.svelte +464 -464
  447. package/src/ruvocal/src/routes/settings/(nav)/[...model]/+page.ts +14 -14
  448. package/src/ruvocal/src/routes/settings/(nav)/application/+page.svelte +362 -362
  449. package/src/ruvocal/src/routes/settings/+layout.svelte +40 -40
  450. package/src/ruvocal/src/styles/highlight-js.css +195 -195
  451. package/src/ruvocal/src/styles/main.css +144 -144
  452. package/src/ruvocal/static/chatui/favicon-dark.svg +3 -3
  453. package/src/ruvocal/static/chatui/favicon-dev.svg +3 -3
  454. package/src/ruvocal/static/chatui/favicon.svg +3 -3
  455. package/src/ruvocal/static/chatui/icon.svg +3 -3
  456. package/src/ruvocal/static/chatui/logo.svg +7 -7
  457. package/src/ruvocal/static/chatui/manifest.json +54 -54
  458. package/src/ruvocal/static/chatui/welcome.js +184 -184
  459. package/src/ruvocal/static/huggingchat/favicon-dark.svg +4 -4
  460. package/src/ruvocal/static/huggingchat/favicon-dev.svg +4 -4
  461. package/src/ruvocal/static/huggingchat/favicon.svg +4 -4
  462. package/src/ruvocal/static/huggingchat/fulltext-logo.svg +1 -1
  463. package/src/ruvocal/static/huggingchat/icon.svg +4 -4
  464. package/src/ruvocal/static/huggingchat/logo.svg +4 -4
  465. package/src/ruvocal/static/huggingchat/manifest.json +54 -54
  466. package/src/ruvocal/static/huggingchat/routes.chat.json +226 -226
  467. package/src/ruvocal/static/robots.txt +10 -10
  468. package/src/ruvocal/static/wasm/rvagent_wasm.js +1539 -1539
  469. package/src/ruvocal/stub/@reflink/reflink/package.json +5 -5
  470. package/src/ruvocal/svelte.config.js +53 -53
  471. package/src/ruvocal/tailwind.config.cjs +30 -30
  472. package/src/ruvocal/tsconfig.json +19 -19
  473. package/src/ruvocal/vite.config.ts +87 -87
  474. package/src/scripts/deploy.sh +116 -116
  475. package/src/scripts/generate-config.js +245 -245
  476. package/src/scripts/generate-welcome.js +187 -187
  477. package/src/scripts/package-rvf.sh +116 -116
  478. package/src/ruvocal/.claude-flow/daemon-state.json +0 -135
  479. package/src/ruvocal/.claude-flow/data/pending-insights.jsonl +0 -0
  480. package/src/ruvocal/.claude-flow/data/ranked-context.json +0 -5
  481. package/src/ruvocal/.claude-flow/logs/daemon.log +0 -31
  482. package/src/ruvocal/.claude-flow/logs/headless/audit_1777949411822_juxau0_prompt.log +0 -989
  483. package/src/ruvocal/.claude-flow/logs/headless/audit_1777949411822_juxau0_result.log +0 -67
  484. package/src/ruvocal/.claude-flow/logs/headless/audit_1777950042278_jvj5xq_prompt.log +0 -989
  485. package/src/ruvocal/.claude-flow/logs/headless/audit_1777950042278_jvj5xq_result.log +0 -93
  486. package/src/ruvocal/.claude-flow/logs/headless/optimize_1777949531823_yt5yc2_prompt.log +0 -1498
  487. package/src/ruvocal/.claude-flow/logs/headless/optimize_1777949531823_yt5yc2_result.log +0 -93
  488. package/src/ruvocal/.claude-flow/logs/headless/testgaps_1777949771821_elw1j4_prompt.log +0 -1498
  489. package/src/ruvocal/.claude-flow/logs/headless/testgaps_1777949771821_elw1j4_result.log +0 -100
  490. package/src/ruvocal/.claude-flow/metrics/codebase-map.json +0 -11
  491. package/src/ruvocal/.claude-flow/metrics/consolidation.json +0 -6
  492. package/src/ruvocal/.claude-flow/neural/stats.json +0 -6
  493. package/src/ruvocal/.claude-flow/sessions/current.json +0 -13
  494. package/src/ruvocal/.swarm/attestation.db +0 -0
  495. package/src/ruvocal/.swarm/hnsw.index +0 -0
  496. package/src/ruvocal/.swarm/hnsw.metadata.json +0 -1
  497. package/src/ruvocal/.swarm/memory.db +0 -0
  498. package/src/ruvocal/.swarm/schema.sql +0 -305
@@ -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
+ });