nolo-cli 0.1.40 → 0.1.42

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 (482) hide show
  1. package/README.md +27 -12
  2. package/agent-runtime/agentThread.ts +222 -0
  3. package/agent-runtime/agentThreadAdmission.ts +74 -0
  4. package/agent-runtime/dialogWritePlan.ts +42 -0
  5. package/agent-runtime/hostAdapter.ts +1 -0
  6. package/agent-runtime/hybridRecordStore.ts +5 -3
  7. package/agent-runtime/index.ts +43 -0
  8. package/agent-runtime/localLoop.ts +31 -2
  9. package/agent-runtime/localToolPolicy.ts +1 -0
  10. package/agent-runtime/localWorkspaceTools.ts +1258 -78
  11. package/agent-runtime/noloWorkspaceTools.ts +20 -1
  12. package/agent-runtime/runtimeToolPolicy.ts +1 -5
  13. package/agent-runtime/runtimeToolSurface.ts +1 -0
  14. package/agentAliases.ts +29 -9
  15. package/agentInternalCommandEntries.ts +11 -3
  16. package/agentMachineCommands.ts +1 -0
  17. package/agentRecordCommands.ts +2 -2
  18. package/agentRecordHelpers.ts +59 -6
  19. package/agentRunCommand.ts +84 -19
  20. package/ai/agent/_executeModel.ts +146 -0
  21. package/ai/agent/agentSlice.ts +556 -1
  22. package/ai/agent/appWorkingMemory.ts +126 -0
  23. package/ai/agent/buildEditingContext.ts +378 -0
  24. package/ai/agent/buildSystemPrompt.ts +599 -0
  25. package/ai/agent/canvasEditingContext.ts +49 -0
  26. package/ai/agent/cleanAgentMessages.ts +140 -0
  27. package/ai/agent/cliChatClient.ts +153 -0
  28. package/ai/agent/cliExecutor.ts +264 -46
  29. package/ai/agent/contextCompiler.ts +107 -0
  30. package/ai/agent/contextLayerContract.ts +44 -0
  31. package/ai/agent/createAgentSchema.ts +272 -0
  32. package/ai/agent/currentTimeContext.ts +65 -0
  33. package/ai/agent/executeToolCall.ts +58 -0
  34. package/ai/agent/fetchAgentContexts.ts +42 -0
  35. package/ai/agent/generatePrompt.ts +3 -0
  36. package/ai/agent/getFullChatContextKeys.ts +168 -0
  37. package/ai/agent/guidedCreation/capabilities.ts +84 -0
  38. package/ai/agent/guidedCreation/types.ts +38 -0
  39. package/ai/agent/isLiveAudioOnlyAgent.ts +26 -0
  40. package/ai/agent/liveAudioModel.ts +2 -0
  41. package/ai/agent/pageBuilderHandoffRules.ts +75 -0
  42. package/ai/agent/persistMessageWithFixedId.ts +37 -0
  43. package/ai/agent/planSlice.ts +259 -0
  44. package/ai/agent/referenceUtils.ts +229 -0
  45. package/ai/agent/runAgentBackground.ts +310 -0
  46. package/ai/agent/runAgentClientLoop.ts +164 -0
  47. package/ai/agent/runtimeGuidance.ts +116 -0
  48. package/ai/agent/serverOrigin.ts +7 -0
  49. package/ai/agent/startParallelAgentStreams.ts +424 -0
  50. package/ai/agent/startupProtocol.ts +53 -0
  51. package/ai/agent/streamAgentChatTurn.ts +2135 -0
  52. package/ai/agent/streamAgentChatTurnUtils.ts +923 -0
  53. package/ai/agent/toolFailureGuard.ts +99 -0
  54. package/ai/agent/types.ts +73 -0
  55. package/ai/agent/utils/imageOutput.ts +37 -0
  56. package/ai/agent/utils/publicImageAgentMode.ts +26 -0
  57. package/ai/ai.locale.ts +1165 -0
  58. package/ai/chat/accumulateToolCallChunks.ts +126 -0
  59. package/ai/chat/fetchUtils.ts +143 -0
  60. package/ai/chat/inlineImageUrlsForCustomProvider.ts +122 -0
  61. package/ai/chat/parseApiError.ts +67 -0
  62. package/ai/chat/parseMultilineSSE.ts +95 -0
  63. package/ai/chat/sendOpenAICompletionsRequest.ts +921 -0
  64. package/ai/chat/sendOpenAIResponseRequest.ts +720 -0
  65. package/ai/chat/serverProxyRetry.ts +102 -0
  66. package/ai/chat/shouldUseServerProxy.ts +18 -0
  67. package/ai/chat/streamRetry.ts +46 -0
  68. package/ai/chat/updateTotalUsage.ts +89 -0
  69. package/ai/context/buildReferenceContext.ts +437 -0
  70. package/ai/context/retention.ts +165 -0
  71. package/ai/context/tokenUtils.ts +78 -0
  72. package/ai/llm/agentCapabilities.ts +74 -0
  73. package/ai/llm/deepinfra.ts +79 -0
  74. package/ai/llm/fireworks.ts +68 -0
  75. package/ai/llm/generateRequestBody.ts +165 -0
  76. package/ai/llm/getModelContextWindow.ts +93 -0
  77. package/ai/llm/getPricing.ts +252 -0
  78. package/ai/llm/imagePricing.ts +66 -0
  79. package/ai/llm/isResponseAPIModel.ts +13 -0
  80. package/ai/llm/kimi.ts +22 -0
  81. package/ai/llm/mimo.ts +99 -0
  82. package/ai/llm/mistral.ts +21 -0
  83. package/ai/llm/models.ts +45 -0
  84. package/ai/llm/openrouterModels.ts +31 -0
  85. package/ai/llm/providers.ts +313 -0
  86. package/ai/llm/reasoningModels.ts +27 -0
  87. package/ai/llm/types.ts +82 -0
  88. package/ai/llm/usageRequestOptions.ts +59 -0
  89. package/ai/memory/policy.ts +49 -0
  90. package/ai/memory/queryShared.ts +167 -0
  91. package/ai/memory/recentRelationshipRecap.ts +251 -0
  92. package/ai/memory/remember.ts +111 -0
  93. package/ai/memory/scope.ts +74 -0
  94. package/ai/memory/store.ts +20 -0
  95. package/ai/memory/storeShared.ts +76 -0
  96. package/ai/memory/types.ts +52 -0
  97. package/ai/memory/understanding.ts +341 -0
  98. package/ai/memory/understandingGreeting.ts +264 -0
  99. package/ai/policy/personalizationDialog.ts +333 -0
  100. package/ai/policy/runtimePolicy.ts +526 -0
  101. package/ai/policy/selfUpdateFields.ts +48 -0
  102. package/ai/policy/types.ts +64 -0
  103. package/ai/skills/referenceRuntime.ts +274 -0
  104. package/ai/skills/skillDiagnostics.ts +251 -0
  105. package/ai/skills/skillDocBuilder.ts +139 -0
  106. package/ai/skills/skillSummaryMarker.ts +26 -0
  107. package/ai/token/calculatePrice.ts +576 -0
  108. package/ai/token/missingUsageEstimate.ts +42 -0
  109. package/ai/token/normalizeUsage.ts +124 -0
  110. package/ai/token/openaiImageGenerationUsage.ts +56 -0
  111. package/ai/token/prepareTokenUsageData.ts +93 -0
  112. package/ai/token/resolveBillingTarget.ts +52 -0
  113. package/ai/token/saveTokenRecord.ts +53 -0
  114. package/ai/token/types.ts +103 -0
  115. package/ai/tools/agent/agentTools.ts +214 -0
  116. package/ai/tools/agent/agentUpdateShared.ts +311 -0
  117. package/ai/tools/agent/callAgentTool.ts +139 -0
  118. package/ai/tools/agent/createAgentTool.ts +512 -0
  119. package/ai/tools/agent/createDialogTool.ts +69 -0
  120. package/ai/tools/agent/createSkillAgentTool.ts +62 -0
  121. package/ai/tools/agent/parallelBudget.ts +221 -0
  122. package/ai/tools/agent/prepareAgentDraftTool.ts +159 -0
  123. package/ai/tools/agent/presets/appBuilderPreset.ts +154 -0
  124. package/ai/tools/agent/runLlmTool.ts +96 -0
  125. package/ai/tools/agent/runStreamingAgentTool.ts +73 -0
  126. package/ai/tools/agent/skillAgentArgs.ts +106 -0
  127. package/ai/tools/agent/startAgentDialogTool.ts +54 -0
  128. package/ai/tools/agent/streamParallelAgentsTool.ts +123 -0
  129. package/ai/tools/agent/updateAgentTool.ts +96 -0
  130. package/ai/tools/agent/updateSelfTool.ts +113 -0
  131. package/ai/tools/amazonProductScraperTool.ts +86 -0
  132. package/ai/tools/apifyActorClient.ts +45 -0
  133. package/ai/tools/appEditGuard.ts +372 -0
  134. package/ai/tools/appReadSnapshot.ts +153 -0
  135. package/ai/tools/appTools.ts +1549 -0
  136. package/ai/tools/applyEditTool.ts +259 -0
  137. package/ai/tools/applyLineEditsTool.ts +315 -0
  138. package/ai/tools/browserTools/click.ts +33 -0
  139. package/ai/tools/browserTools/closeSession.ts +29 -0
  140. package/ai/tools/browserTools/common.ts +27 -0
  141. package/ai/tools/browserTools/openSession.ts +48 -0
  142. package/ai/tools/browserTools/readContent.ts +38 -0
  143. package/ai/tools/browserTools/selectOption.ts +46 -0
  144. package/ai/tools/browserTools/typeText.ts +42 -0
  145. package/ai/tools/category/createCategoryTool.ts +66 -0
  146. package/ai/tools/category/queryContentsByCategoryTool.ts +69 -0
  147. package/ai/tools/category/updateContentCategoryTool.ts +75 -0
  148. package/ai/tools/cfBrowserTools.ts +319 -0
  149. package/ai/tools/cfSpeechToTextTool.ts +49 -0
  150. package/ai/tools/checkEnvTool.ts +102 -0
  151. package/ai/tools/cloudflareCrawlTool.ts +289 -0
  152. package/ai/tools/codeSearchTool.ts +123 -0
  153. package/ai/tools/codeTools.ts +101 -0
  154. package/ai/tools/convertMarxistsBookTool.ts +103 -0
  155. package/ai/tools/createDocTool.ts +132 -0
  156. package/ai/tools/createSkillDocTool.ts +155 -0
  157. package/ai/tools/createWorkflowTool.ts +154 -0
  158. package/ai/tools/delayTool.ts +31 -0
  159. package/ai/tools/deleteSpacesTool.ts +325 -0
  160. package/ai/tools/deleteSpacesToolModel.ts +159 -0
  161. package/ai/tools/devReloadUtils.ts +29 -0
  162. package/ai/tools/dialogMessageSearch.ts +137 -0
  163. package/ai/tools/doctorSkillTool.ts +72 -0
  164. package/ai/tools/ecommerceScraperTool.ts +86 -0
  165. package/ai/tools/emailTools.ts +549 -0
  166. package/ai/tools/evalSkillTool.ts +92 -0
  167. package/ai/tools/exaSearchTool.ts +63 -0
  168. package/ai/tools/execShellTool.ts +266 -0
  169. package/ai/tools/executeSqlTool.ts +192 -0
  170. package/ai/tools/fetchWebpageSupport.ts +333 -0
  171. package/ai/tools/fetchWebpageTool.ts +97 -0
  172. package/ai/tools/geminiImagePreviewTool.ts +386 -0
  173. package/ai/tools/generateDocxTool.ts +215 -0
  174. package/ai/tools/googleSearchScraperTool.ts +106 -0
  175. package/ai/tools/importDataTool.ts +133 -0
  176. package/ai/tools/importSkillTool.ts +162 -0
  177. package/ai/tools/index.ts +2290 -0
  178. package/ai/tools/jdProductScraperTool.ts +821 -0
  179. package/ai/tools/listUserSpacesTool.ts +113 -0
  180. package/ai/tools/localFilesTool.ts +276 -0
  181. package/ai/tools/marxistsOfflineBook.ts +253 -0
  182. package/ai/tools/modelUsageTools.ts +206 -0
  183. package/ai/tools/noloWorkspaceReadTools.ts +467 -0
  184. package/ai/tools/olmOcrTool.ts +33 -0
  185. package/ai/tools/openaiImageTool.ts +267 -0
  186. package/ai/tools/prepareTools.ts +193 -0
  187. package/ai/tools/readDocTool.ts +88 -0
  188. package/ai/tools/readFileTool.ts +214 -0
  189. package/ai/tools/readTool.ts +194 -0
  190. package/ai/tools/readXPostTool.ts +229 -0
  191. package/ai/tools/readXhsProfileTool.ts +638 -0
  192. package/ai/tools/rememberMemoryTool.ts +84 -0
  193. package/ai/tools/remotionVideoTool.ts +151 -0
  194. package/ai/tools/searchDialogMessagesTool.ts +222 -0
  195. package/ai/tools/searchWorkspaceTool.ts +259 -0
  196. package/ai/tools/skillFollowup.ts +86 -0
  197. package/ai/tools/surfWeatherTool.ts +169 -0
  198. package/ai/tools/table/addTableRowTool.ts +217 -0
  199. package/ai/tools/table/createTableTool.ts +326 -0
  200. package/ai/tools/table/rowTools.ts +386 -0
  201. package/ai/tools/table/schemaTools.ts +244 -0
  202. package/ai/tools/table/shareTableTool.ts +148 -0
  203. package/ai/tools/table/toolShared.ts +129 -0
  204. package/ai/tools/taobaoTmallProductScraperTool.ts +49 -0
  205. package/ai/tools/toolApiClient.ts +279 -0
  206. package/ai/tools/toolPacks.ts +19 -0
  207. package/ai/tools/toolResultError.ts +42 -0
  208. package/ai/tools/toolRunSlice.ts +303 -0
  209. package/ai/tools/toolSchemaCompatibility.ts +53 -0
  210. package/ai/tools/uiAskChoiceTool.ts +104 -0
  211. package/ai/tools/updateContentTitleTool.ts +84 -0
  212. package/ai/tools/updateDocTool.ts +105 -0
  213. package/ai/tools/updateUserPreferenceProfileTool.ts +153 -0
  214. package/ai/tools/wereadGatewayTool.ts +152 -0
  215. package/ai/tools/whisperTool.ts +76 -0
  216. package/ai/tools/writeFileTool.ts +213 -0
  217. package/ai/tools/youtubeScraperTool.ts +116 -0
  218. package/ai/tools/ziweiChartTool.ts +678 -0
  219. package/ai/types.ts +58 -0
  220. package/ai/workflow/workflowExecutor.ts +323 -0
  221. package/ai/workflow/workflowSlice.ts +73 -0
  222. package/ai/workflow/workflowTypes.ts +106 -0
  223. package/app/actions/syncAppRecord.ts +73 -0
  224. package/app/constants/animationSets.ts +21 -0
  225. package/app/constants/appEditor.ts +99 -0
  226. package/app/favorite/deletedFavoriteProjection.ts +31 -0
  227. package/app/favorite/favoriteSlice.ts +690 -0
  228. package/app/favorite/type.ts +43 -0
  229. package/app/hooks/deleteDbKey.ts +254 -0
  230. package/app/i18n/client.ts +13 -0
  231. package/app/i18n/i18n.base.ts +14 -0
  232. package/app/i18n/i18n.config.ts +77 -0
  233. package/app/i18n/index.ts +15 -0
  234. package/app/i18n/mapLanguage.ts +11 -0
  235. package/app/i18n/translations/appBuilder.locale.ts +453 -0
  236. package/app/i18n/translations/error.locale.ts +246 -0
  237. package/app/i18n/translations/interface.locale.ts +2206 -0
  238. package/app/i18n/translations/pricing.locale.ts +397 -0
  239. package/app/i18n/translations/privacy.locale.ts +168 -0
  240. package/app/i18n/translations/seo.locale.ts +129 -0
  241. package/app/i18n/translations/terms.locale.ts +124 -0
  242. package/app/i18n/types.ts +6 -0
  243. package/app/notifications/model.ts +77 -0
  244. package/app/notifications/notificationSlice.ts +141 -0
  245. package/app/reducer.ts +36 -0
  246. package/app/settings/dbActionThunks.ts +34 -0
  247. package/app/settings/defaultSpacePreference.ts +87 -0
  248. package/app/settings/settingSlice.tsx +1212 -0
  249. package/app/settings/settingsRecord.ts +8 -0
  250. package/app/stateViews/runtime.ts +90 -0
  251. package/app/store.ts +55 -0
  252. package/app/theme/colors.ts +280 -0
  253. package/app/theme/fontPreference.ts +77 -0
  254. package/app/theme/theme.config.ts +120 -0
  255. package/app/theme/themeModeBootstrap.ts +123 -0
  256. package/app/types/appSummary.ts +118 -0
  257. package/app/types.ts +358 -0
  258. package/app/utils/appKeys.ts +55 -0
  259. package/app/utils/async.ts +28 -0
  260. package/app/utils/desktopAgentRuntimeTurnClient.ts +75 -0
  261. package/app/utils/env.ts +17 -0
  262. package/app/utils/fileUtils.ts +229 -0
  263. package/app/utils/imageUtils.ts +274 -0
  264. package/app/utils/myContentItems.ts +289 -0
  265. package/app/utils/retryAfter.ts +36 -0
  266. package/app/utils/retryFetch.ts +61 -0
  267. package/app/utils/toast.ts +3 -0
  268. package/auth/action/signUpAction.ts +227 -0
  269. package/auth/adminPermissions.ts +78 -0
  270. package/auth/authSlice.ts +531 -0
  271. package/auth/client/loginRequest.ts +14 -0
  272. package/auth/resetAuthScopedClientState.ts +27 -0
  273. package/auth/routes.ts +183 -0
  274. package/auth/token.ts +63 -0
  275. package/auth/types.ts +40 -0
  276. package/chat/chat.locale.ts +817 -0
  277. package/chat/dialog/actions/addCybotAction.ts +52 -0
  278. package/chat/dialog/actions/addReferenceKeysAction.ts +62 -0
  279. package/chat/dialog/actions/builtinDialogLlm.ts +35 -0
  280. package/chat/dialog/actions/cleanupCliSession.ts +40 -0
  281. package/chat/dialog/actions/createDialogAction.ts +379 -0
  282. package/chat/dialog/actions/createScheduledTaskAction.ts +78 -0
  283. package/chat/dialog/actions/handleSendMessageAction.ts +202 -0
  284. package/chat/dialog/actions/handleSendMessageResolver.ts +27 -0
  285. package/chat/dialog/actions/removeCybotAction.ts +6 -0
  286. package/chat/dialog/actions/removeDialogAgentAction.ts +44 -0
  287. package/chat/dialog/actions/replacePrimaryCybotAction.ts +6 -0
  288. package/chat/dialog/actions/setPrimaryDialogAgentAction.ts +52 -0
  289. package/chat/dialog/actions/updateDialogSummaryAction.ts +430 -0
  290. package/chat/dialog/actions/updateDialogTitleAction.ts +151 -0
  291. package/chat/dialog/actions/updateDialogTitlePolicy.ts +30 -0
  292. package/chat/dialog/actions/updateTokensAction.ts +233 -0
  293. package/chat/dialog/dialogAgents.ts +72 -0
  294. package/chat/dialog/dialogAttachmentCleanup.ts +135 -0
  295. package/chat/dialog/dialogSlice.ts +985 -0
  296. package/chat/dialog/dialogTitle.ts +81 -0
  297. package/chat/dialog/dialogTokenStats.ts +12 -0
  298. package/chat/dialog/dialogUrl.ts +6 -0
  299. package/chat/dialog/goal.ts +108 -0
  300. package/chat/dialog/objectAssistantRegistry.ts +286 -0
  301. package/chat/dialog/schedule.ts +17 -0
  302. package/chat/messages/activityCompletion.ts +97 -0
  303. package/chat/messages/fetchAndCacheMessages.ts +119 -0
  304. package/chat/messages/fetchConvMsgs.ts +51 -0
  305. package/chat/messages/fileUrl.ts +13 -0
  306. package/chat/messages/imagePayloadPersistence.ts +22 -0
  307. package/chat/messages/messageAgent.ts +8 -0
  308. package/chat/messages/messageContent.ts +203 -0
  309. package/chat/messages/messageContract.ts +88 -0
  310. package/chat/messages/messageSlice.ts +1386 -0
  311. package/chat/messages/toolThunks.ts +646 -0
  312. package/chat/messages/types.ts +8 -1
  313. package/chat/messages/web/toolDisplayName.ts +686 -0
  314. package/cli/agentAliases.ts +29 -9
  315. package/cli/agentInternalCommandEntries.ts +11 -3
  316. package/cli/agentMachineCommands.ts +1 -0
  317. package/cli/agentRecordCommands.ts +2 -2
  318. package/cli/agentRecordHelpers.ts +59 -6
  319. package/cli/agentRunCommand.ts +84 -19
  320. package/cli/client/agentRun.ts +92 -261
  321. package/cli/client/localRuntimeAdapter.ts +292 -50
  322. package/cli/commandRegistry.ts +7 -4
  323. package/cli/dialogAttachmentCleanup.ts +151 -0
  324. package/cli/dialogCommands.ts +163 -61
  325. package/cli/dialogInternalCommandEntries.ts +1 -1
  326. package/cli/docCreateCommands.ts +345 -0
  327. package/cli/docInternalCommandEntries.ts +11 -0
  328. package/cli/docPageHelpers.ts +80 -0
  329. package/cli/docSkillHelpers.ts +67 -0
  330. package/cli/docSpaceHelpers.ts +46 -0
  331. package/cli/internalCommandEntries.ts +4 -0
  332. package/cli/machineCommands.ts +18 -34
  333. package/cli/memoryCommands.ts +253 -0
  334. package/cli/memoryInternalCommandEntries.ts +9 -0
  335. package/cli/qoderUsageProbe.ts +252 -0
  336. package/cli/scriptCommandEntries.ts +0 -3
  337. package/cli/secretScan.ts +31 -0
  338. package/cli/tableCommands.ts +248 -13
  339. package/cli/workflowInternalCommandEntries.ts +2 -3
  340. package/client/agentRun.test.ts +119 -83
  341. package/client/agentRun.ts +92 -261
  342. package/client/localRuntimeAdapter.test.ts +894 -32
  343. package/client/localRuntimeAdapter.ts +292 -50
  344. package/commandRegistry.ts +7 -4
  345. package/connector-experimental/capabilities.ts +10 -9
  346. package/core/builtinAgents.ts +209 -0
  347. package/core/config.ts +15 -0
  348. package/core/crypto.ts +51 -0
  349. package/core/generateKeyPairFromSeedV1.ts +15 -0
  350. package/core/generateMainKey.ts +22 -0
  351. package/core/init.ts +41 -0
  352. package/core/localOrigins.ts +8 -0
  353. package/core/password.ts +13 -0
  354. package/core/prefix.ts +23 -0
  355. package/create/editor/transforms/fromMarkdown/index.ts +260 -0
  356. package/create/editor/transforms/fromMarkdown/inline.ts +285 -0
  357. package/create/editor/transforms/fromMarkdown/table.ts +130 -0
  358. package/create/editor/transforms/markdownToSlate.ts +24 -0
  359. package/create/editor/transforms/slateToRenderMarkdown.ts +218 -0
  360. package/create/editor/transforms/slateToSimplifiedMarkdown.ts +224 -0
  361. package/create/editor/transforms/slateToText.ts +33 -0
  362. package/create/editor/utils/slateUtils.ts +233 -0
  363. package/create/space/addSpaceAction.ts +193 -0
  364. package/create/space/category/categoryActions.ts +427 -0
  365. package/create/space/changeSpaceAction.ts +36 -0
  366. package/create/space/constants.ts +8 -0
  367. package/create/space/content/addContentAction.ts +116 -0
  368. package/create/space/content/contentThunks.ts +90 -0
  369. package/create/space/content/deleteContentFromSpaceAction.ts +187 -0
  370. package/create/space/content/deleteMultipleContentAction.ts +112 -0
  371. package/create/space/content/moveContentAction.ts +285 -0
  372. package/create/space/content/updateContentCategoryAction.ts +154 -0
  373. package/create/space/content/updateContentTitleAction.ts +151 -0
  374. package/create/space/content/uploadAndAddFileToSpaceAction.ts +98 -0
  375. package/create/space/contentKeyUtils.ts +117 -0
  376. package/create/space/deleteSpaceAction.ts +276 -0
  377. package/create/space/fetchSpaceAction.ts +70 -0
  378. package/create/space/fetchSpaceSidebarStateAction.ts +26 -0
  379. package/create/space/loadDefaultSpaceAction.ts +57 -0
  380. package/create/space/member/addMemberAction.ts +79 -0
  381. package/create/space/member/fetchUserSpaceMembershipsAction.ts +386 -0
  382. package/create/space/member/memberThunks.ts +47 -0
  383. package/create/space/member/removeMemberAction.ts +67 -0
  384. package/create/space/resolvePreferredSpaceId.ts +113 -0
  385. package/create/space/sidebarVisibleTypes.ts +260 -0
  386. package/create/space/space.locale.ts +1087 -0
  387. package/create/space/spaceAccess.ts +182 -0
  388. package/create/space/spaceCollapsedState.ts +54 -0
  389. package/create/space/spaceKeys.ts +65 -0
  390. package/create/space/spaceSlice.ts +328 -0
  391. package/create/space/spaceThunks.ts +410 -0
  392. package/create/space/types.ts +27 -0
  393. package/create/space/updateSpaceAction.ts +107 -0
  394. package/create/space/utils/permissions.ts +47 -0
  395. package/create/types.ts +21 -0
  396. package/database/actions/agentReadResolution.ts +33 -0
  397. package/database/actions/common.ts +243 -0
  398. package/database/actions/deleteFile.ts +40 -0
  399. package/database/actions/fileContent.ts +125 -0
  400. package/database/actions/patch.ts +155 -0
  401. package/database/actions/read.ts +358 -0
  402. package/database/actions/readAndWait.ts +260 -0
  403. package/database/actions/readRequestManager.ts +120 -0
  404. package/database/actions/readResolution.ts +125 -0
  405. package/database/actions/remove.ts +94 -0
  406. package/database/actions/replication.ts +477 -0
  407. package/database/actions/upload.ts +186 -0
  408. package/database/actions/upsert.ts +56 -0
  409. package/database/actions/write.ts +146 -0
  410. package/database/authority/ownerKey.ts +80 -0
  411. package/database/authority/recordAuthority.ts +67 -0
  412. package/database/authority/userAuthorityRegistry.ts +54 -0
  413. package/database/client/fetchUserData.ts +61 -0
  414. package/database/dbActionThunks.ts +1 -0
  415. package/database/dbSlice.ts +149 -0
  416. package/database/fileRing.ts +51 -0
  417. package/database/fileStorage.ts +232 -0
  418. package/database/fileUrl.ts +34 -0
  419. package/database/keys.ts +767 -0
  420. package/database/queryPrefixes.ts +14 -0
  421. package/database/requests.ts +443 -0
  422. package/database/runtimeServerContext.ts +54 -0
  423. package/database/tenantPlacement.ts +57 -0
  424. package/database/userPreferenceRegister.ts +131 -0
  425. package/database/utils/ulid.ts +1 -0
  426. package/dialogAttachmentCleanup.ts +151 -0
  427. package/dialogCommands.ts +163 -61
  428. package/dialogInternalCommandEntries.ts +1 -1
  429. package/docCreateCommands.ts +345 -0
  430. package/docInternalCommandEntries.ts +11 -0
  431. package/docPageHelpers.ts +80 -0
  432. package/docSkillHelpers.ts +67 -0
  433. package/docSpaceHelpers.ts +46 -0
  434. package/integrations/anthropic/anthropicModels.ts +47 -0
  435. package/integrations/deepseek/models.ts +37 -0
  436. package/integrations/google/models.ts +341 -0
  437. package/integrations/openai/chatCompletionStreamMode.ts +10 -0
  438. package/integrations/openai/filterAndCleanMessages.ts +284 -0
  439. package/integrations/openai/flexTier.ts +102 -0
  440. package/integrations/openai/generateOpenAIRequestBody.ts +271 -0
  441. package/integrations/openai/generateResponseRequestBody.ts +77 -0
  442. package/integrations/openai/models.ts +271 -0
  443. package/integrations/openai/providerBodyCompatibility.ts +23 -0
  444. package/integrations/openai/responsesHelpers.ts +297 -0
  445. package/integrations/x-reader/types.ts +109 -0
  446. package/integrations/xai/models.ts +13 -0
  447. package/integrations/xhs-reader/redaction.ts +68 -0
  448. package/integrations/xhs-reader/types.ts +277 -0
  449. package/integrations/xhs-reader/url.ts +80 -0
  450. package/internalCommandEntries.ts +4 -0
  451. package/machineCommands.ts +18 -34
  452. package/memoryCommands.ts +253 -0
  453. package/memoryInternalCommandEntries.ts +9 -0
  454. package/package.json +454 -9
  455. package/qoderUsageProbe.ts +252 -0
  456. package/render/contentIcon/types.ts +167 -0
  457. package/render/page/createPageAction.ts +183 -0
  458. package/render/page/docSlice.ts +495 -0
  459. package/render/page/types.ts +19 -0
  460. package/render/table/createTableAction.ts +274 -0
  461. package/render/table/deleteTableAction.ts +193 -0
  462. package/render/table/fetchAndCacheTableRows.ts +174 -0
  463. package/render/table/tableSlice.ts +1167 -0
  464. package/render/table/toolValueUtils.ts +363 -0
  465. package/render/table/types.ts +262 -0
  466. package/render/table/utils/tableSerialization.ts +50 -0
  467. package/scriptCommandEntries.ts +0 -3
  468. package/secretScan.ts +31 -0
  469. package/server/handlers/agentRun/types.ts +105 -0
  470. package/share/action.ts +225 -0
  471. package/share/helpers.ts +302 -0
  472. package/share/keys.ts +243 -0
  473. package/share/publicSharePolicy.ts +27 -0
  474. package/share/shareSlice.ts +53 -0
  475. package/share/types.ts +100 -0
  476. package/tableCommands.ts +248 -13
  477. package/workflowInternalCommandEntries.ts +2 -3
  478. package/ai/tools/agent/taskRunTool.ts +0 -112
  479. package/cli/client/taskRunPrompt.ts +0 -27
  480. package/cli/taskRunCommand.ts +0 -265
  481. package/client/taskRunPrompt.ts +0 -27
  482. package/taskRunCommand.ts +0 -265
@@ -4,7 +4,7 @@ import { join } from "node:path";
4
4
  import { tmpdir } from "node:os";
5
5
 
6
6
  import { runLocalAgentTurn } from "../agent-runtime/localLoop";
7
- import { MIMO_MONTH_AGENT_KEY } from "../agentAliases";
7
+ import { LOCAL_CODEX_AGENT_KEY, LOCAL_QODER_AGENT_KEY, MIMO_MONTH_AGENT_KEY } from "../agentAliases";
8
8
  import { createCliLocalRuntimeAdapter } from "./localRuntimeAdapter";
9
9
 
10
10
  describe("CLI local runtime adapter", () => {
@@ -13,6 +13,7 @@ describe("CLI local runtime adapter", () => {
13
13
  "readFile",
14
14
  "writeFile",
15
15
  "editFile",
16
+ "globFiles",
16
17
  "searchFiles",
17
18
  "execShell",
18
19
  ];
@@ -21,6 +22,7 @@ describe("CLI local runtime adapter", () => {
21
22
  "readFile",
22
23
  "writeFile",
23
24
  "editFile",
25
+ "globFiles",
24
26
  "searchFiles",
25
27
  "execShell",
26
28
  ];
@@ -36,6 +38,7 @@ describe("CLI local runtime adapter", () => {
36
38
  "readSpace",
37
39
  "readDoc",
38
40
  "readSkillDoc",
41
+ "listTables",
39
42
  "queryTableRows",
40
43
  "cliWhoami",
41
44
  "cliDoctor",
@@ -49,6 +52,87 @@ describe("CLI local runtime adapter", () => {
49
52
  return request?.body?.tools?.map((tool: any) => tool.function.name) ?? [];
50
53
  }
51
54
 
55
+ function publicSchemaKeys(schema: any) {
56
+ return Object.keys(schema.parameters.properties).filter((key) => key !== "_activity");
57
+ }
58
+
59
+ test("loads stored local CLI agent records before falling back to built-ins", async () => {
60
+ const adapter = createCliLocalRuntimeAdapter({
61
+ env: {
62
+ NOLO_LOCAL_USER_ID: "0e95801d90",
63
+ },
64
+ db: {
65
+ get: async (key) => {
66
+ if (key !== LOCAL_CODEX_AGENT_KEY) throw new Error(`not found: ${key}`);
67
+ return {
68
+ dbKey: LOCAL_CODEX_AGENT_KEY,
69
+ id: "01LOCALCODEXCLI000000NEW",
70
+ name: "User Edited Local Codex",
71
+ prompt: "User-owned local Codex prompt.",
72
+ apiSource: "cli",
73
+ provider: "cli",
74
+ cliProvider: "codex",
75
+ model: "gpt-5.4",
76
+ };
77
+ },
78
+ put: async () => {},
79
+ batch: async () => {},
80
+ iterator: () => (async function* () {})(),
81
+ },
82
+ fetchImpl: async () => new Response("not found", { status: 404 }),
83
+ } as any);
84
+
85
+ await expect(adapter.loadAgentConfig(LOCAL_CODEX_AGENT_KEY)).resolves.toMatchObject({
86
+ key: LOCAL_CODEX_AGENT_KEY,
87
+ name: "User Edited Local Codex",
88
+ prompt: "User-owned local Codex prompt.",
89
+ apiSource: "cli",
90
+ provider: "cli",
91
+ cliProvider: "codex",
92
+ model: "gpt-5.4",
93
+ });
94
+ });
95
+
96
+ test("falls back to built-in local Codex and Qoder CLI agents without machine binding", async () => {
97
+ const adapter = createCliLocalRuntimeAdapter({
98
+ env: {
99
+ NOLO_LOCAL_USER_ID: "user-1",
100
+ },
101
+ db: {
102
+ get: async (key) => {
103
+ throw new Error(`not found: ${key}`);
104
+ },
105
+ put: async () => {},
106
+ batch: async () => {},
107
+ iterator: () => (async function* () {})(),
108
+ },
109
+ fetchImpl: async () => new Response("not found", { status: 404 }),
110
+ } as any);
111
+
112
+ const codex = await adapter.loadAgentConfig(LOCAL_CODEX_AGENT_KEY);
113
+ const qoder = await adapter.loadAgentConfig(LOCAL_QODER_AGENT_KEY);
114
+
115
+ expect(codex).toMatchObject({
116
+ key: LOCAL_CODEX_AGENT_KEY,
117
+ name: "Local Codex",
118
+ apiSource: "cli",
119
+ provider: "cli",
120
+ cliProvider: "codex",
121
+ });
122
+ expect(qoder).toMatchObject({
123
+ key: LOCAL_QODER_AGENT_KEY,
124
+ name: "Local Qoder",
125
+ apiSource: "cli",
126
+ provider: "cli",
127
+ cliProvider: "qoder",
128
+ model: "Qwen3.7-Max",
129
+ });
130
+ expect((codex as any)?.runtimeBinding).toBeUndefined();
131
+ expect((qoder as any)?.runtimeBinding).toBeUndefined();
132
+ expect((codex as any)?.rawRecord?.runtimeBinding).toBeUndefined();
133
+ expect((qoder as any)?.rawRecord?.runtimeBinding).toBeUndefined();
134
+ });
135
+
52
136
  test("runs cli-provider agents through the local CLI executor instead of OpenAI-compatible direct mode", async () => {
53
137
  const cliExecutions: Array<{ provider: string; prompt: string; options: any }> = [];
54
138
  const adapter = createCliLocalRuntimeAdapter({
@@ -535,11 +619,8 @@ describe("CLI local runtime adapter", () => {
535
619
  env: {
536
620
  NOLO_LOCAL_USER_ID: "user-1",
537
621
  },
538
- db: {
539
- get: async (key) => {
540
- if (!store.has(key)) throw new Error(`not found: ${key}`);
541
- return store.get(key);
542
- },
622
+ store: {
623
+ read: async (key) => store.get(key) ?? null,
543
624
  put: async (key, value) => {
544
625
  store.set(key, value);
545
626
  },
@@ -892,7 +973,7 @@ describe("CLI local runtime adapter", () => {
892
973
 
893
974
  expect(result.content).toBe("platform longer retry ok");
894
975
  expect(attempts).toBe(6);
895
- expect(retryDelays.length).toBe(5);
976
+ expect(retryDelays.length).toBeGreaterThanOrEqual(3);
896
977
  expect(retryDelays[0]).toBeGreaterThan(0);
897
978
  });
898
979
 
@@ -1327,7 +1408,6 @@ describe("CLI local runtime adapter", () => {
1327
1408
  "execShell",
1328
1409
  "checkEnv",
1329
1410
  "queryTableRows",
1330
- "taskRun",
1331
1411
  ],
1332
1412
  }),
1333
1413
  put: async () => {},
@@ -1351,6 +1431,308 @@ describe("CLI local runtime adapter", () => {
1351
1431
  expect(toolNamesFromRequest(requests[0])).toEqual(SHELL_LOCAL_CODING_TOOL_NAMES);
1352
1432
  });
1353
1433
 
1434
+ test("can expose only declared local workspace tools for tool ablations", async () => {
1435
+ const requests: Array<{ body: any }> = [];
1436
+ const adapter = createCliLocalRuntimeAdapter({
1437
+ env: {
1438
+ OPENAI_API_KEY: "sk-local",
1439
+ NOLO_LOCAL_OPENAI_BASE_URL: "http://127.0.0.1:11434/v1",
1440
+ NOLO_LOCAL_WORKSPACE_TOOLSET: "declared-only",
1441
+ },
1442
+ db: {
1443
+ get: async () => ({
1444
+ dbKey: MIMO_MONTH_AGENT_KEY,
1445
+ prompt: "Use exactly the declared local tools.",
1446
+ model: "mimo-v2.5-pro",
1447
+ provider: "custom",
1448
+ tools: ["listFiles", "readFile", "execShell"],
1449
+ }),
1450
+ put: async () => {},
1451
+ batch: async () => {},
1452
+ iterator: () => (async function* () {})(),
1453
+ },
1454
+ fetchImpl: async (_url, init) => {
1455
+ requests.push({ body: JSON.parse(String(init?.body)) });
1456
+ return Response.json({
1457
+ choices: [{ message: { content: "done" } }],
1458
+ });
1459
+ },
1460
+ });
1461
+
1462
+ await runLocalAgentTurn({
1463
+ adapter,
1464
+ agentRef: MIMO_MONTH_AGENT_KEY,
1465
+ input: "inspect cwd",
1466
+ });
1467
+
1468
+ expect(toolNamesFromRequest(requests[0])).toEqual(["listFiles", "readFile", "execShell"]);
1469
+ });
1470
+
1471
+ test("defaults local workspace tools to strategy descriptions and rich parameters", async () => {
1472
+ const requests: Array<{ body: any }> = [];
1473
+ const adapter = createCliLocalRuntimeAdapter({
1474
+ env: {
1475
+ OPENAI_API_KEY: "sk-local",
1476
+ NOLO_LOCAL_OPENAI_BASE_URL: "http://127.0.0.1:11434/v1",
1477
+ NOLO_LOCAL_WORKSPACE_TOOLSET: "declared-only",
1478
+ },
1479
+ db: {
1480
+ get: async () => ({
1481
+ dbKey: MIMO_MONTH_AGENT_KEY,
1482
+ prompt: "Use local coding tools.",
1483
+ model: "mimo-v2.5-pro",
1484
+ provider: "custom",
1485
+ tools: ["listFiles", "readFile", "globFiles", "searchFiles"],
1486
+ }),
1487
+ put: async () => {},
1488
+ batch: async () => {},
1489
+ iterator: () => (async function* () {})(),
1490
+ },
1491
+ fetchImpl: async (_url, init) => {
1492
+ requests.push({ body: JSON.parse(String(init?.body)) });
1493
+ return Response.json({
1494
+ choices: [{ message: { content: "done" } }],
1495
+ });
1496
+ },
1497
+ });
1498
+
1499
+ await runLocalAgentTurn({
1500
+ adapter,
1501
+ agentRef: MIMO_MONTH_AGENT_KEY,
1502
+ input: "inspect cwd",
1503
+ });
1504
+
1505
+ const tools = new Map(
1506
+ requests[0]?.body.tools.map((tool: any) => [tool.function.name, tool.function]),
1507
+ );
1508
+ const listFiles = tools.get("listFiles") as any;
1509
+ const readFile = tools.get("readFile") as any;
1510
+ const globFiles = tools.get("globFiles") as any;
1511
+ const searchFiles = tools.get("searchFiles") as any;
1512
+
1513
+ expect(listFiles.description).toContain("not for path-pattern discovery across the repo");
1514
+ expect(publicSchemaKeys(listFiles)).toEqual([
1515
+ "path",
1516
+ "maxDepth",
1517
+ "maxResults",
1518
+ "entryType",
1519
+ ]);
1520
+ expect(readFile.description).toContain("Use line ranges");
1521
+ expect(publicSchemaKeys(readFile)).toEqual([
1522
+ "path",
1523
+ "startLine",
1524
+ "endLine",
1525
+ "maxLines",
1526
+ "tailLines",
1527
+ ]);
1528
+ expect(globFiles.description).toContain("any file discovery task");
1529
+ expect(publicSchemaKeys(globFiles)).toEqual([
1530
+ "pattern",
1531
+ "path",
1532
+ "exclude",
1533
+ "includeIgnored",
1534
+ "maxResults",
1535
+ ]);
1536
+ expect(searchFiles.description).toContain("using ripgrep when available");
1537
+ expect(publicSchemaKeys(searchFiles)).toEqual([
1538
+ "query",
1539
+ "path",
1540
+ "exclude",
1541
+ "includeIgnored",
1542
+ "maxResults",
1543
+ "literal",
1544
+ "caseSensitive",
1545
+ "contextLines",
1546
+ ]);
1547
+ });
1548
+
1549
+ test("can vary globFiles schema through local runtime env", async () => {
1550
+ const requests: Array<{ body: any }> = [];
1551
+ const adapter = createCliLocalRuntimeAdapter({
1552
+ env: {
1553
+ OPENAI_API_KEY: "sk-local",
1554
+ NOLO_LOCAL_OPENAI_BASE_URL: "http://127.0.0.1:11434/v1",
1555
+ NOLO_GLOBFILES_DESCRIPTION_VARIANT: "antiShell",
1556
+ NOLO_GLOBFILES_PARAMETER_VARIANT: "rich",
1557
+ },
1558
+ db: {
1559
+ get: async () => ({
1560
+ dbKey: MIMO_MONTH_AGENT_KEY,
1561
+ prompt: "Use local coding tools.",
1562
+ model: "mimo-v2.5-pro",
1563
+ provider: "custom",
1564
+ tools: ["globFiles"],
1565
+ }),
1566
+ put: async () => {},
1567
+ batch: async () => {},
1568
+ iterator: () => (async function* () {})(),
1569
+ },
1570
+ fetchImpl: async (_url, init) => {
1571
+ requests.push({ body: JSON.parse(String(init?.body)) });
1572
+ return Response.json({
1573
+ choices: [{ message: { content: "done" } }],
1574
+ });
1575
+ },
1576
+ });
1577
+
1578
+ await runLocalAgentTurn({
1579
+ adapter,
1580
+ agentRef: MIMO_MONTH_AGENT_KEY,
1581
+ input: "find tests",
1582
+ });
1583
+
1584
+ const globFiles = requests[0]?.body.tools.find((tool: any) => tool.function.name === "globFiles")?.function;
1585
+ expect(globFiles.description).toContain("Do not use execShell or listFiles for path discovery");
1586
+ expect(publicSchemaKeys(globFiles)).toEqual([
1587
+ "pattern",
1588
+ "path",
1589
+ "exclude",
1590
+ "includeIgnored",
1591
+ "maxResults",
1592
+ ]);
1593
+ });
1594
+
1595
+ test("can vary listFiles schema through local runtime env", async () => {
1596
+ const requests: Array<{ body: any }> = [];
1597
+ const adapter = createCliLocalRuntimeAdapter({
1598
+ env: {
1599
+ OPENAI_API_KEY: "sk-local",
1600
+ NOLO_LOCAL_OPENAI_BASE_URL: "http://127.0.0.1:11434/v1",
1601
+ NOLO_LISTFILES_DESCRIPTION_VARIANT: "antiShell",
1602
+ NOLO_LISTFILES_PARAMETER_VARIANT: "rich",
1603
+ },
1604
+ db: {
1605
+ get: async () => ({
1606
+ dbKey: MIMO_MONTH_AGENT_KEY,
1607
+ prompt: "Use local coding tools.",
1608
+ model: "mimo-v2.5-pro",
1609
+ provider: "custom",
1610
+ tools: ["listFiles"],
1611
+ }),
1612
+ put: async () => {},
1613
+ batch: async () => {},
1614
+ iterator: () => (async function* () {})(),
1615
+ },
1616
+ fetchImpl: async (_url, init) => {
1617
+ requests.push({ body: JSON.parse(String(init?.body)) });
1618
+ return Response.json({
1619
+ choices: [{ message: { content: "done" } }],
1620
+ });
1621
+ },
1622
+ });
1623
+
1624
+ await runLocalAgentTurn({
1625
+ adapter,
1626
+ agentRef: MIMO_MONTH_AGENT_KEY,
1627
+ input: "list directories",
1628
+ });
1629
+
1630
+ const listFiles = requests[0]?.body.tools.find((tool: any) => tool.function.name === "listFiles")?.function;
1631
+ expect(listFiles.description).toContain("Do not use execShell with ls/find/tree");
1632
+ expect(publicSchemaKeys(listFiles)).toEqual([
1633
+ "path",
1634
+ "maxDepth",
1635
+ "maxResults",
1636
+ "entryType",
1637
+ ]);
1638
+ });
1639
+
1640
+ test("can vary readFile schema through local runtime env", async () => {
1641
+ const requests: Array<{ body: any }> = [];
1642
+ const adapter = createCliLocalRuntimeAdapter({
1643
+ env: {
1644
+ OPENAI_API_KEY: "sk-local",
1645
+ NOLO_LOCAL_OPENAI_BASE_URL: "http://127.0.0.1:11434/v1",
1646
+ NOLO_READFILE_DESCRIPTION_VARIANT: "strategy",
1647
+ NOLO_READFILE_PARAMETER_VARIANT: "rich",
1648
+ },
1649
+ db: {
1650
+ get: async () => ({
1651
+ dbKey: MIMO_MONTH_AGENT_KEY,
1652
+ prompt: "Use local coding tools.",
1653
+ model: "mimo-v2.5-pro",
1654
+ provider: "custom",
1655
+ tools: ["readFile"],
1656
+ }),
1657
+ put: async () => {},
1658
+ batch: async () => {},
1659
+ iterator: () => (async function* () {})(),
1660
+ },
1661
+ fetchImpl: async (_url, init) => {
1662
+ requests.push({ body: JSON.parse(String(init?.body)) });
1663
+ return Response.json({
1664
+ choices: [{ message: { content: "done" } }],
1665
+ });
1666
+ },
1667
+ });
1668
+
1669
+ await runLocalAgentTurn({
1670
+ adapter,
1671
+ agentRef: MIMO_MONTH_AGENT_KEY,
1672
+ input: "read a file range",
1673
+ });
1674
+
1675
+ const readFile = requests[0]?.body.tools.find((tool: any) => tool.function.name === "readFile")?.function;
1676
+ expect(readFile.description).toContain("Use line ranges");
1677
+ expect(readFile.parameters.properties.maxLines.description).toContain("each readFile preview consumes one budget slot");
1678
+ expect(publicSchemaKeys(readFile)).toEqual([
1679
+ "path",
1680
+ "startLine",
1681
+ "endLine",
1682
+ "maxLines",
1683
+ "tailLines",
1684
+ ]);
1685
+ });
1686
+
1687
+ test("can vary searchFiles schema through local runtime env", async () => {
1688
+ const requests: Array<{ body: any }> = [];
1689
+ const adapter = createCliLocalRuntimeAdapter({
1690
+ env: {
1691
+ OPENAI_API_KEY: "sk-local",
1692
+ NOLO_LOCAL_OPENAI_BASE_URL: "http://127.0.0.1:11434/v1",
1693
+ NOLO_SEARCHFILES_DESCRIPTION_VARIANT: "antiShell",
1694
+ NOLO_SEARCHFILES_PARAMETER_VARIANT: "rich",
1695
+ },
1696
+ db: {
1697
+ get: async () => ({
1698
+ dbKey: MIMO_MONTH_AGENT_KEY,
1699
+ prompt: "Use local coding tools.",
1700
+ model: "mimo-v2.5-pro",
1701
+ provider: "custom",
1702
+ tools: ["searchFiles"],
1703
+ }),
1704
+ put: async () => {},
1705
+ batch: async () => {},
1706
+ iterator: () => (async function* () {})(),
1707
+ },
1708
+ fetchImpl: async (_url, init) => {
1709
+ requests.push({ body: JSON.parse(String(init?.body)) });
1710
+ return Response.json({
1711
+ choices: [{ message: { content: "done" } }],
1712
+ });
1713
+ },
1714
+ });
1715
+
1716
+ await runLocalAgentTurn({
1717
+ adapter,
1718
+ agentRef: MIMO_MONTH_AGENT_KEY,
1719
+ input: "find TODO",
1720
+ });
1721
+
1722
+ const searchFiles = requests[0]?.body.tools.find((tool: any) => tool.function.name === "searchFiles")?.function;
1723
+ expect(searchFiles.description).toContain("Do not use execShell for content search");
1724
+ expect(publicSchemaKeys(searchFiles)).toEqual([
1725
+ "query",
1726
+ "path",
1727
+ "exclude",
1728
+ "includeIgnored",
1729
+ "maxResults",
1730
+ "literal",
1731
+ "caseSensitive",
1732
+ "contextLines",
1733
+ ]);
1734
+ });
1735
+
1354
1736
  test("allows default semantic workspace tools without legacy agent tool declarations", async () => {
1355
1737
  const workspaceRoot = mkdtempSync(join(tmpdir(), "nolo-cli-runtime-"));
1356
1738
  try {
@@ -1384,7 +1766,7 @@ describe("CLI local runtime adapter", () => {
1384
1766
  }
1385
1767
  });
1386
1768
 
1387
- test("exposes declared taskRun as a platform tool in local runtime requests", async () => {
1769
+ test("exposes declared server table write tools in local runtime requests", async () => {
1388
1770
  const requests: Array<{ body: any }> = [];
1389
1771
  const adapter = createCliLocalRuntimeAdapter({
1390
1772
  env: {
@@ -1393,10 +1775,10 @@ describe("CLI local runtime adapter", () => {
1393
1775
  },
1394
1776
  db: {
1395
1777
  get: async () => ({
1396
- dbKey: "agent-local-taskrun",
1397
- prompt: "Use taskRun.",
1778
+ dbKey: "agent-local-table",
1779
+ prompt: "Use table tools.",
1398
1780
  model: "gpt-4.1-mini",
1399
- toolNames: ["taskRun"],
1781
+ toolNames: ["createTable", "addTableRow", "addTableRows"],
1400
1782
  }),
1401
1783
  put: async () => {},
1402
1784
  batch: async () => {},
@@ -1412,24 +1794,27 @@ describe("CLI local runtime adapter", () => {
1412
1794
 
1413
1795
  await runLocalAgentTurn({
1414
1796
  adapter,
1415
- agentRef: "taskrun",
1416
- input: "read task context",
1797
+ agentRef: "table",
1798
+ input: "采集并写入 table",
1417
1799
  });
1418
1800
 
1419
- expect(toolNamesFromRequest(requests[0])).toContain("taskRun");
1801
+ const toolNames = toolNamesFromRequest(requests[0]);
1802
+ expect(toolNames).toContain("createTable");
1803
+ expect(toolNames).toContain("addTableRow");
1804
+ expect(toolNames).toContain("addTableRows");
1420
1805
  });
1421
1806
 
1422
- test("executes declared taskRun through the server task-run bridge", async () => {
1807
+ test("executes declared createTable through the server table bridge when capture is explicit", async () => {
1423
1808
  const requests: Array<{ url: string; auth: string | null; body: any }> = [];
1424
1809
  const adapter = createCliLocalRuntimeAdapter({
1425
1810
  env: {
1426
- BASE_URL: "https://us.nolo.chat",
1427
- AUTH_TOKEN: "machine-token",
1811
+ NOLO_SERVER: "https://us.nolo.chat",
1812
+ AUTH_TOKEN: "token-1",
1428
1813
  },
1429
1814
  db: {
1430
1815
  get: async () => ({
1431
- dbKey: "agent-local-taskrun",
1432
- toolNames: ["taskRun"],
1816
+ dbKey: "agent-local-table-write",
1817
+ toolNames: ["createTable"],
1433
1818
  }),
1434
1819
  put: async () => {},
1435
1820
  batch: async () => {},
@@ -1441,32 +1826,81 @@ describe("CLI local runtime adapter", () => {
1441
1826
  auth: new Headers(init?.headers).get("authorization"),
1442
1827
  body: JSON.parse(String(init?.body)),
1443
1828
  });
1444
- return Response.json({ ok: true, row: { rowId: "task-1" } });
1829
+ return Response.json({
1830
+ ok: true,
1831
+ tenantId: "user-1",
1832
+ tableId: "table-1",
1833
+ });
1445
1834
  },
1446
1835
  });
1447
1836
 
1448
- await adapter.loadAgentConfig("taskrun");
1837
+ await adapter.loadAgentConfig("table-write");
1449
1838
  const result = await adapter.executeTool({
1450
- id: "call-taskrun",
1451
- name: "taskRun",
1839
+ id: "call-create-table",
1840
+ name: "createTable",
1841
+ userInput: "采集这个用户并写入 table",
1452
1842
  arguments: JSON.stringify({
1453
- action: "readTaskContext",
1454
- dbKey: "row-user-NOLOTASKBOARD-task-1",
1843
+ title: "XHS profile",
1844
+ purpose: "agent_eval_workbench",
1845
+ columns: [{ name: "nickname" }],
1455
1846
  }),
1456
1847
  });
1457
1848
 
1458
1849
  expect(requests).toEqual([
1459
1850
  {
1460
- url: "https://us.nolo.chat/api/task-run",
1461
- auth: "Bearer machine-token",
1851
+ url: "https://us.nolo.chat/api/table/create",
1852
+ auth: "Bearer token-1",
1462
1853
  body: {
1463
- action: "readTaskContext",
1464
- dbKey: "row-user-NOLOTASKBOARD-task-1",
1854
+ title: "XHS profile",
1855
+ purpose: "agent_eval_workbench",
1856
+ columns: [{ name: "nickname" }],
1465
1857
  },
1466
1858
  },
1467
1859
  ]);
1468
- expect(result.content).toContain("\"ok\":true");
1469
- expect(result.metadata).toMatchObject({ serverPlatformTool: true });
1860
+ expect(result.content).toContain("\"tableId\":\"table-1\"");
1861
+ expect(result.metadata).toMatchObject({
1862
+ serverPlatformTool: true,
1863
+ tableWrite: true,
1864
+ });
1865
+ });
1866
+
1867
+ test("blocks local table writes when the current user request is not explicit capture", async () => {
1868
+ const adapter = createCliLocalRuntimeAdapter({
1869
+ env: {
1870
+ NOLO_SERVER: "https://us.nolo.chat",
1871
+ AUTH_TOKEN: "token-1",
1872
+ },
1873
+ db: {
1874
+ get: async () => ({
1875
+ dbKey: "agent-local-table-blocked",
1876
+ toolNames: ["createTable"],
1877
+ }),
1878
+ put: async () => {},
1879
+ batch: async () => {},
1880
+ iterator: () => (async function* () {})(),
1881
+ },
1882
+ fetchImpl: async () => {
1883
+ throw new Error("blocked createTable must not reach the server");
1884
+ },
1885
+ });
1886
+
1887
+ await adapter.loadAgentConfig("table-blocked");
1888
+ const result = await adapter.executeTool({
1889
+ id: "call-create-table-blocked",
1890
+ name: "createTable",
1891
+ userInput: "总结这个用户画像",
1892
+ arguments: JSON.stringify({
1893
+ title: "XHS profile",
1894
+ columns: [{ name: "nickname" }],
1895
+ }),
1896
+ });
1897
+
1898
+ const parsed = JSON.parse(result.content);
1899
+ expect(parsed.error).toBe("knowledge_capture_requires_confirmation");
1900
+ expect(result.metadata).toMatchObject({
1901
+ serverPlatformTool: true,
1902
+ tableWriteBlocked: true,
1903
+ });
1470
1904
  });
1471
1905
 
1472
1906
  test("adds typed CLI workspace tools to the default nolo local agent request", async () => {
@@ -1896,6 +2330,434 @@ describe("CLI local runtime adapter", () => {
1896
2330
  });
1897
2331
  });
1898
2332
 
2333
+ test("exposes read_xhs_profile to OpenAI-compatible providers when the agent declares it", async () => {
2334
+ const requests: Array<{ body: any }> = [];
2335
+ const adapter = createCliLocalRuntimeAdapter({
2336
+ env: {
2337
+ OPENAI_API_KEY: "sk-local",
2338
+ NOLO_LOCAL_OPENAI_BASE_URL: "http://127.0.0.1:11434/v1",
2339
+ },
2340
+ db: {
2341
+ get: async () => ({
2342
+ dbKey: "agent-local-xhs",
2343
+ prompt: "Read XHS profile.",
2344
+ model: "gpt-4.1-mini",
2345
+ toolNames: ["read_xhs_profile"],
2346
+ }),
2347
+ put: async () => {},
2348
+ batch: async () => {},
2349
+ iterator: () => (async function* () {})(),
2350
+ },
2351
+ fetchImpl: async (_url, init) => {
2352
+ requests.push({ body: JSON.parse(String(init?.body)) });
2353
+ return Response.json({
2354
+ choices: [{ message: { content: "done" } }],
2355
+ });
2356
+ },
2357
+ });
2358
+
2359
+ await runLocalAgentTurn({
2360
+ adapter,
2361
+ agentRef: "xhs",
2362
+ input: "读取小红书用户主页",
2363
+ });
2364
+
2365
+ expect(toolNamesFromRequest(requests[0])).toContain("read_xhs_profile");
2366
+ });
2367
+
2368
+ test("exposes read_x_post to OpenAI-compatible providers when the agent declares it", async () => {
2369
+ const requests: Array<{ body: any }> = [];
2370
+ const adapter = createCliLocalRuntimeAdapter({
2371
+ env: {
2372
+ OPENAI_API_KEY: "sk-local",
2373
+ NOLO_LOCAL_OPENAI_BASE_URL: "http://127.0.0.1:11434/v1",
2374
+ },
2375
+ db: {
2376
+ get: async () => ({
2377
+ dbKey: "agent-local-x-post",
2378
+ prompt: "Read X posts.",
2379
+ model: "gpt-4.1-mini",
2380
+ toolNames: ["read_x_post"],
2381
+ }),
2382
+ put: async () => {},
2383
+ batch: async () => {},
2384
+ iterator: () => (async function* () {})(),
2385
+ },
2386
+ fetchImpl: async (_url, init) => {
2387
+ requests.push({ body: JSON.parse(String(init?.body)) });
2388
+ return Response.json({
2389
+ choices: [{ message: { content: "done" } }],
2390
+ });
2391
+ },
2392
+ });
2393
+
2394
+ await runLocalAgentTurn({
2395
+ adapter,
2396
+ agentRef: "x-post",
2397
+ input: "读取 X 帖子",
2398
+ });
2399
+
2400
+ expect(toolNamesFromRequest(requests[0])).toContain("read_x_post");
2401
+ });
2402
+
2403
+ test("adds read_x_post to local web-capable agent tool surface", async () => {
2404
+ const requests: Array<{ body: any }> = [];
2405
+ const adapter = createCliLocalRuntimeAdapter({
2406
+ env: {
2407
+ OPENAI_API_KEY: "sk-local",
2408
+ NOLO_LOCAL_OPENAI_BASE_URL: "http://127.0.0.1:11434/v1",
2409
+ },
2410
+ db: {
2411
+ get: async () => ({
2412
+ dbKey: "agent-local-web-reader",
2413
+ prompt: "Read web pages.",
2414
+ model: "gpt-4.1-mini",
2415
+ toolNames: ["fetchWebpage"],
2416
+ }),
2417
+ put: async () => {},
2418
+ batch: async () => {},
2419
+ iterator: () => (async function* () {})(),
2420
+ },
2421
+ fetchImpl: async (_url, init) => {
2422
+ requests.push({ body: JSON.parse(String(init?.body)) });
2423
+ return Response.json({
2424
+ choices: [{ message: { content: "done" } }],
2425
+ });
2426
+ },
2427
+ });
2428
+
2429
+ await runLocalAgentTurn({
2430
+ adapter,
2431
+ agentRef: "web-reader",
2432
+ input: "读取 X 帖子",
2433
+ });
2434
+
2435
+ expect(toolNamesFromRequest(requests[0])).toContain("read_x_post");
2436
+ });
2437
+
2438
+ test("does not expose read_xhs_profile when the agent does not declare it", async () => {
2439
+ const requests: Array<{ body: any }> = [];
2440
+ const adapter = createCliLocalRuntimeAdapter({
2441
+ env: {
2442
+ OPENAI_API_KEY: "sk-local",
2443
+ NOLO_LOCAL_OPENAI_BASE_URL: "http://127.0.0.1:11434/v1",
2444
+ },
2445
+ db: {
2446
+ get: async () => ({
2447
+ dbKey: "agent-local-no-xhs",
2448
+ prompt: "Be helpful.",
2449
+ model: "gpt-4.1-mini",
2450
+ }),
2451
+ put: async () => {},
2452
+ batch: async () => {},
2453
+ iterator: () => (async function* () {})(),
2454
+ },
2455
+ fetchImpl: async (_url, init) => {
2456
+ requests.push({ body: JSON.parse(String(init?.body)) });
2457
+ return Response.json({
2458
+ choices: [{ message: { content: "done" } }],
2459
+ });
2460
+ },
2461
+ });
2462
+
2463
+ await runLocalAgentTurn({
2464
+ adapter,
2465
+ agentRef: "no-xhs",
2466
+ input: "hello",
2467
+ });
2468
+
2469
+ expect(toolNamesFromRequest(requests[0])).not.toContain("read_xhs_profile");
2470
+ });
2471
+
2472
+ test("executes read_xhs_profile locally through the desktop bridge", async () => {
2473
+ const xhsCalls: Array<{ args: any }> = [];
2474
+ const adapter = createCliLocalRuntimeAdapter({
2475
+ env: {
2476
+ NOLO_LOCAL_USER_ID: "user-1",
2477
+ },
2478
+ db: {
2479
+ get: async () => ({
2480
+ dbKey: "agent-user-1-xhs",
2481
+ id: "xhs-agent",
2482
+ prompt: "Read XHS.",
2483
+ toolNames: ["read_xhs_profile"],
2484
+ }),
2485
+ put: async () => {},
2486
+ batch: async () => {},
2487
+ iterator: () => (async function* () {})(),
2488
+ },
2489
+ localToolExecutors: {
2490
+ read_xhs_profile: async (call) => {
2491
+ const parsed = JSON.parse(call.arguments || "{}");
2492
+ xhsCalls.push({ args: parsed });
2493
+ return {
2494
+ content: JSON.stringify({
2495
+ ok: true,
2496
+ data: {
2497
+ profile: { nickname: "test-soul", redId: "12345" },
2498
+ notes: [],
2499
+ noteDetails: [],
2500
+ analysis: {
2501
+ totalNotes: 0,
2502
+ commentBuckets: [],
2503
+ },
2504
+ },
2505
+ }),
2506
+ metadata: { xhsLocalBridge: true },
2507
+ };
2508
+ },
2509
+ },
2510
+ fetchImpl: async () => Response.json({}),
2511
+ });
2512
+
2513
+ await adapter.loadAgentConfig("xhs-agent");
2514
+ const result = await adapter.executeTool({
2515
+ id: "call-xhs-1",
2516
+ name: "read_xhs_profile",
2517
+ arguments: JSON.stringify({
2518
+ url: "https://www.xiaohongshu.com/user/profile/5d2be8720000000010007556",
2519
+ maxScrollPages: 3,
2520
+ }),
2521
+ });
2522
+
2523
+ expect(xhsCalls).toHaveLength(1);
2524
+ expect(xhsCalls[0].args.url).toBe(
2525
+ "https://www.xiaohongshu.com/user/profile/5d2be8720000000010007556"
2526
+ );
2527
+ expect(xhsCalls[0].args.maxScrollPages).toBe(3);
2528
+ expect(result.metadata).toMatchObject({ xhsLocalBridge: true });
2529
+ const parsed = JSON.parse(result.content);
2530
+ expect(parsed.ok).toBe(true);
2531
+ expect(parsed.data.profile.nickname).toBe("test-soul");
2532
+ });
2533
+
2534
+ test("default read_x_post executor calls the local bridge reader", async () => {
2535
+ const readerCalls: Array<{ args: any }> = [];
2536
+ const adapter = createCliLocalRuntimeAdapter({
2537
+ env: {
2538
+ NOLO_LOCAL_USER_ID: "user-1",
2539
+ },
2540
+ db: {
2541
+ get: async () => ({
2542
+ dbKey: "agent-user-1-x-post-default",
2543
+ id: "x-post-default",
2544
+ prompt: "Read X posts.",
2545
+ toolNames: ["read_x_post"],
2546
+ }),
2547
+ put: async () => {},
2548
+ batch: async () => {},
2549
+ iterator: () => (async function* () {})(),
2550
+ },
2551
+ readXPost: async (args) => {
2552
+ readerCalls.push({ args });
2553
+ return {
2554
+ rawData: {
2555
+ ok: true,
2556
+ backend: "desktop_local_browser",
2557
+ fetchedAt: "2026-06-08T00:00:00.000Z",
2558
+ data: {
2559
+ id: "2063842222307704944",
2560
+ url: "https://x.com/BohuTANG/status/2063842222307704944",
2561
+ text: "mock x post",
2562
+ author: {
2563
+ handle: "BohuTANG",
2564
+ displayName: "Bohu",
2565
+ },
2566
+ },
2567
+ } as any,
2568
+ displayData: "已读取 X 帖子:@BohuTANG",
2569
+ };
2570
+ },
2571
+ fetchImpl: async () => Response.json({}),
2572
+ });
2573
+
2574
+ await adapter.loadAgentConfig("x-post-default");
2575
+ const result = await adapter.executeTool({
2576
+ id: "call-x-post-default",
2577
+ name: "read_x_post",
2578
+ arguments: JSON.stringify({
2579
+ url: "https://x.com/BohuTANG/status/2063842222307704944",
2580
+ keepOpen: true,
2581
+ }),
2582
+ });
2583
+
2584
+ expect(readerCalls).toHaveLength(1);
2585
+ expect(readerCalls[0].args).toMatchObject({
2586
+ url: "https://x.com/BohuTANG/status/2063842222307704944",
2587
+ keepOpen: true,
2588
+ });
2589
+ expect(result.metadata).toMatchObject({ xPostLocalBridge: true });
2590
+ const parsed = JSON.parse(result.content);
2591
+ expect(parsed.ok).toBe(true);
2592
+ expect(parsed.data.text).toBe("mock x post");
2593
+ });
2594
+
2595
+ test("default read_xhs_profile executor calls the local bridge reader", async () => {
2596
+ const readerCalls: Array<{ args: any; thunkApi: any }> = [];
2597
+ const adapter = createCliLocalRuntimeAdapter({
2598
+ env: {
2599
+ NOLO_LOCAL_USER_ID: "user-1",
2600
+ },
2601
+ db: {
2602
+ get: async () => ({
2603
+ dbKey: "agent-user-1-xhs-default",
2604
+ id: "xhs-default",
2605
+ prompt: "Read XHS.",
2606
+ toolNames: ["read_xhs_profile"],
2607
+ }),
2608
+ put: async () => {},
2609
+ batch: async () => {},
2610
+ iterator: () => (async function* () {})(),
2611
+ },
2612
+ readXhsProfile: async (args, thunkApi) => {
2613
+ readerCalls.push({ args, thunkApi });
2614
+ return {
2615
+ rawData: {
2616
+ ok: true,
2617
+ data: {
2618
+ profile: { nickname: "default-bridge", redId: "67890" },
2619
+ notes: [],
2620
+ noteDetails: [],
2621
+ commentsByNote: {},
2622
+ analysis: {
2623
+ totalNotes: 0,
2624
+ averageLikes: 0,
2625
+ averageComments: 0,
2626
+ averageCollects: 0,
2627
+ averageShares: 0,
2628
+ commentBuckets: [],
2629
+ themes: [],
2630
+ },
2631
+ },
2632
+ fetchedAt: "2026-06-03T00:00:00.000Z",
2633
+ } as any,
2634
+ displayData: "小红书用户: default-bridge",
2635
+ };
2636
+ },
2637
+ fetchImpl: async () => Response.json({}),
2638
+ });
2639
+
2640
+ await adapter.loadAgentConfig("xhs-default");
2641
+ const result = await adapter.executeTool({
2642
+ id: "call-xhs-default",
2643
+ name: "read_xhs_profile",
2644
+ arguments: JSON.stringify({
2645
+ url: "https://www.xiaohongshu.com/user/profile/5d2be8720000000010007556",
2646
+ includeComments: true,
2647
+ maxCommentPagesPerNote: 1,
2648
+ }),
2649
+ });
2650
+
2651
+ expect(readerCalls).toHaveLength(1);
2652
+ expect(readerCalls[0].args).toMatchObject({
2653
+ url: "https://www.xiaohongshu.com/user/profile/5d2be8720000000010007556",
2654
+ includeComments: true,
2655
+ maxCommentPagesPerNote: 1,
2656
+ });
2657
+ expect(readerCalls[0].args.profileDir).toBeUndefined();
2658
+ expect(readerCalls[0].thunkApi).toBeUndefined();
2659
+ expect(result.metadata).toMatchObject({
2660
+ xhsLocalBridge: true,
2661
+ displayData: "小红书用户: default-bridge",
2662
+ });
2663
+ expect(JSON.parse(result.content).data.profile.nickname).toBe("default-bridge");
2664
+ });
2665
+
2666
+ test("default read_xhs_profile executor ignores explicit XHS profile env", async () => {
2667
+ const readerCalls: Array<{ args: any }> = [];
2668
+ const adapter = createCliLocalRuntimeAdapter({
2669
+ env: {
2670
+ NOLO_LOCAL_USER_ID: "user-1",
2671
+ NOLO_XHS_READER_PROFILE_DIR: "/tmp/custom-xhs-profile",
2672
+ },
2673
+ db: {
2674
+ get: async () => ({
2675
+ dbKey: "agent-user-1-xhs-env",
2676
+ id: "xhs-env",
2677
+ prompt: "Read XHS.",
2678
+ toolNames: ["read_xhs_profile"],
2679
+ }),
2680
+ put: async () => {},
2681
+ batch: async () => {},
2682
+ iterator: () => (async function* () {})(),
2683
+ },
2684
+ readXhsProfile: async (args) => {
2685
+ readerCalls.push({ args });
2686
+ return {
2687
+ rawData: {
2688
+ ok: true,
2689
+ data: {
2690
+ profile: { nickname: "env-bridge" },
2691
+ notes: [],
2692
+ noteDetails: [],
2693
+ commentsByNote: {},
2694
+ analysis: { totalNotes: 0, commentBuckets: [] },
2695
+ },
2696
+ fetchedAt: "2026-06-03T00:00:00.000Z",
2697
+ } as any,
2698
+ displayData: "小红书用户: env-bridge",
2699
+ };
2700
+ },
2701
+ fetchImpl: async () => Response.json({}),
2702
+ });
2703
+
2704
+ await adapter.loadAgentConfig("xhs-env");
2705
+ await adapter.executeTool({
2706
+ id: "call-xhs-env",
2707
+ name: "read_xhs_profile",
2708
+ arguments: JSON.stringify({
2709
+ url: "https://www.xiaohongshu.com/user/profile/5d2be8720000000010007556",
2710
+ }),
2711
+ });
2712
+
2713
+ expect(readerCalls).toHaveLength(1);
2714
+ expect(readerCalls[0].args.profileDir).toBeUndefined();
2715
+ });
2716
+
2717
+ test("read_xhs_profile executor does not falsely succeed when bridge is unavailable", async () => {
2718
+ const adapter = createCliLocalRuntimeAdapter({
2719
+ env: {
2720
+ NOLO_LOCAL_USER_ID: "user-1",
2721
+ },
2722
+ db: {
2723
+ get: async () => ({
2724
+ dbKey: "agent-user-1-xhs-fail",
2725
+ id: "xhs-fail",
2726
+ prompt: "Read XHS.",
2727
+ toolNames: ["read_xhs_profile"],
2728
+ }),
2729
+ put: async () => {},
2730
+ batch: async () => {},
2731
+ iterator: () => (async function* () {})(),
2732
+ },
2733
+ localToolExecutors: {
2734
+ read_xhs_profile: async () => ({
2735
+ content: JSON.stringify({
2736
+ ok: false,
2737
+ code: "network_error",
2738
+ message: "read_xhs_profile needs Playwright but it is not installed.",
2739
+ }),
2740
+ metadata: { xhsLocalBridge: true, bridgeError: true },
2741
+ }),
2742
+ },
2743
+ fetchImpl: async () => Response.json({}),
2744
+ });
2745
+
2746
+ await adapter.loadAgentConfig("xhs-fail");
2747
+ const result = await adapter.executeTool({
2748
+ id: "call-xhs-fail",
2749
+ name: "read_xhs_profile",
2750
+ arguments: JSON.stringify({
2751
+ url: "https://www.xiaohongshu.com/user/profile/5d2be8720000000010007556",
2752
+ }),
2753
+ });
2754
+
2755
+ const parsed = JSON.parse(result.content);
2756
+ expect(parsed.ok).toBe(false);
2757
+ expect(parsed.code).toBe("network_error");
2758
+ expect(result.metadata).toMatchObject({ bridgeError: true });
2759
+ });
2760
+
1899
2761
  test("runs allowed workspace file tools through default local executors", async () => {
1900
2762
  const workspaceRoot = mkdtempSync(join(tmpdir(), "nolo-cli-runtime-"));
1901
2763
  try {