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.
- package/README.md +27 -12
- package/agent-runtime/agentThread.ts +222 -0
- package/agent-runtime/agentThreadAdmission.ts +74 -0
- package/agent-runtime/dialogWritePlan.ts +42 -0
- package/agent-runtime/hostAdapter.ts +1 -0
- package/agent-runtime/hybridRecordStore.ts +5 -3
- package/agent-runtime/index.ts +43 -0
- package/agent-runtime/localLoop.ts +31 -2
- package/agent-runtime/localToolPolicy.ts +1 -0
- package/agent-runtime/localWorkspaceTools.ts +1258 -78
- package/agent-runtime/noloWorkspaceTools.ts +20 -1
- package/agent-runtime/runtimeToolPolicy.ts +1 -5
- package/agent-runtime/runtimeToolSurface.ts +1 -0
- package/agentAliases.ts +29 -9
- package/agentInternalCommandEntries.ts +11 -3
- package/agentMachineCommands.ts +1 -0
- package/agentRecordCommands.ts +2 -2
- package/agentRecordHelpers.ts +59 -6
- package/agentRunCommand.ts +84 -19
- package/ai/agent/_executeModel.ts +146 -0
- package/ai/agent/agentSlice.ts +556 -1
- package/ai/agent/appWorkingMemory.ts +126 -0
- package/ai/agent/buildEditingContext.ts +378 -0
- package/ai/agent/buildSystemPrompt.ts +599 -0
- package/ai/agent/canvasEditingContext.ts +49 -0
- package/ai/agent/cleanAgentMessages.ts +140 -0
- package/ai/agent/cliChatClient.ts +153 -0
- package/ai/agent/cliExecutor.ts +264 -46
- package/ai/agent/contextCompiler.ts +107 -0
- package/ai/agent/contextLayerContract.ts +44 -0
- package/ai/agent/createAgentSchema.ts +272 -0
- package/ai/agent/currentTimeContext.ts +65 -0
- package/ai/agent/executeToolCall.ts +58 -0
- package/ai/agent/fetchAgentContexts.ts +42 -0
- package/ai/agent/generatePrompt.ts +3 -0
- package/ai/agent/getFullChatContextKeys.ts +168 -0
- package/ai/agent/guidedCreation/capabilities.ts +84 -0
- package/ai/agent/guidedCreation/types.ts +38 -0
- package/ai/agent/isLiveAudioOnlyAgent.ts +26 -0
- package/ai/agent/liveAudioModel.ts +2 -0
- package/ai/agent/pageBuilderHandoffRules.ts +75 -0
- package/ai/agent/persistMessageWithFixedId.ts +37 -0
- package/ai/agent/planSlice.ts +259 -0
- package/ai/agent/referenceUtils.ts +229 -0
- package/ai/agent/runAgentBackground.ts +310 -0
- package/ai/agent/runAgentClientLoop.ts +164 -0
- package/ai/agent/runtimeGuidance.ts +116 -0
- package/ai/agent/serverOrigin.ts +7 -0
- package/ai/agent/startParallelAgentStreams.ts +424 -0
- package/ai/agent/startupProtocol.ts +53 -0
- package/ai/agent/streamAgentChatTurn.ts +2135 -0
- package/ai/agent/streamAgentChatTurnUtils.ts +923 -0
- package/ai/agent/toolFailureGuard.ts +99 -0
- package/ai/agent/types.ts +73 -0
- package/ai/agent/utils/imageOutput.ts +37 -0
- package/ai/agent/utils/publicImageAgentMode.ts +26 -0
- package/ai/ai.locale.ts +1165 -0
- package/ai/chat/accumulateToolCallChunks.ts +126 -0
- package/ai/chat/fetchUtils.ts +143 -0
- package/ai/chat/inlineImageUrlsForCustomProvider.ts +122 -0
- package/ai/chat/parseApiError.ts +67 -0
- package/ai/chat/parseMultilineSSE.ts +95 -0
- package/ai/chat/sendOpenAICompletionsRequest.ts +921 -0
- package/ai/chat/sendOpenAIResponseRequest.ts +720 -0
- package/ai/chat/serverProxyRetry.ts +102 -0
- package/ai/chat/shouldUseServerProxy.ts +18 -0
- package/ai/chat/streamRetry.ts +46 -0
- package/ai/chat/updateTotalUsage.ts +89 -0
- package/ai/context/buildReferenceContext.ts +437 -0
- package/ai/context/retention.ts +165 -0
- package/ai/context/tokenUtils.ts +78 -0
- package/ai/llm/agentCapabilities.ts +74 -0
- package/ai/llm/deepinfra.ts +79 -0
- package/ai/llm/fireworks.ts +68 -0
- package/ai/llm/generateRequestBody.ts +165 -0
- package/ai/llm/getModelContextWindow.ts +93 -0
- package/ai/llm/getPricing.ts +252 -0
- package/ai/llm/imagePricing.ts +66 -0
- package/ai/llm/isResponseAPIModel.ts +13 -0
- package/ai/llm/kimi.ts +22 -0
- package/ai/llm/mimo.ts +99 -0
- package/ai/llm/mistral.ts +21 -0
- package/ai/llm/models.ts +45 -0
- package/ai/llm/openrouterModels.ts +31 -0
- package/ai/llm/providers.ts +313 -0
- package/ai/llm/reasoningModels.ts +27 -0
- package/ai/llm/types.ts +82 -0
- package/ai/llm/usageRequestOptions.ts +59 -0
- package/ai/memory/policy.ts +49 -0
- package/ai/memory/queryShared.ts +167 -0
- package/ai/memory/recentRelationshipRecap.ts +251 -0
- package/ai/memory/remember.ts +111 -0
- package/ai/memory/scope.ts +74 -0
- package/ai/memory/store.ts +20 -0
- package/ai/memory/storeShared.ts +76 -0
- package/ai/memory/types.ts +52 -0
- package/ai/memory/understanding.ts +341 -0
- package/ai/memory/understandingGreeting.ts +264 -0
- package/ai/policy/personalizationDialog.ts +333 -0
- package/ai/policy/runtimePolicy.ts +526 -0
- package/ai/policy/selfUpdateFields.ts +48 -0
- package/ai/policy/types.ts +64 -0
- package/ai/skills/referenceRuntime.ts +274 -0
- package/ai/skills/skillDiagnostics.ts +251 -0
- package/ai/skills/skillDocBuilder.ts +139 -0
- package/ai/skills/skillSummaryMarker.ts +26 -0
- package/ai/token/calculatePrice.ts +576 -0
- package/ai/token/missingUsageEstimate.ts +42 -0
- package/ai/token/normalizeUsage.ts +124 -0
- package/ai/token/openaiImageGenerationUsage.ts +56 -0
- package/ai/token/prepareTokenUsageData.ts +93 -0
- package/ai/token/resolveBillingTarget.ts +52 -0
- package/ai/token/saveTokenRecord.ts +53 -0
- package/ai/token/types.ts +103 -0
- package/ai/tools/agent/agentTools.ts +214 -0
- package/ai/tools/agent/agentUpdateShared.ts +311 -0
- package/ai/tools/agent/callAgentTool.ts +139 -0
- package/ai/tools/agent/createAgentTool.ts +512 -0
- package/ai/tools/agent/createDialogTool.ts +69 -0
- package/ai/tools/agent/createSkillAgentTool.ts +62 -0
- package/ai/tools/agent/parallelBudget.ts +221 -0
- package/ai/tools/agent/prepareAgentDraftTool.ts +159 -0
- package/ai/tools/agent/presets/appBuilderPreset.ts +154 -0
- package/ai/tools/agent/runLlmTool.ts +96 -0
- package/ai/tools/agent/runStreamingAgentTool.ts +73 -0
- package/ai/tools/agent/skillAgentArgs.ts +106 -0
- package/ai/tools/agent/startAgentDialogTool.ts +54 -0
- package/ai/tools/agent/streamParallelAgentsTool.ts +123 -0
- package/ai/tools/agent/updateAgentTool.ts +96 -0
- package/ai/tools/agent/updateSelfTool.ts +113 -0
- package/ai/tools/amazonProductScraperTool.ts +86 -0
- package/ai/tools/apifyActorClient.ts +45 -0
- package/ai/tools/appEditGuard.ts +372 -0
- package/ai/tools/appReadSnapshot.ts +153 -0
- package/ai/tools/appTools.ts +1549 -0
- package/ai/tools/applyEditTool.ts +259 -0
- package/ai/tools/applyLineEditsTool.ts +315 -0
- package/ai/tools/browserTools/click.ts +33 -0
- package/ai/tools/browserTools/closeSession.ts +29 -0
- package/ai/tools/browserTools/common.ts +27 -0
- package/ai/tools/browserTools/openSession.ts +48 -0
- package/ai/tools/browserTools/readContent.ts +38 -0
- package/ai/tools/browserTools/selectOption.ts +46 -0
- package/ai/tools/browserTools/typeText.ts +42 -0
- package/ai/tools/category/createCategoryTool.ts +66 -0
- package/ai/tools/category/queryContentsByCategoryTool.ts +69 -0
- package/ai/tools/category/updateContentCategoryTool.ts +75 -0
- package/ai/tools/cfBrowserTools.ts +319 -0
- package/ai/tools/cfSpeechToTextTool.ts +49 -0
- package/ai/tools/checkEnvTool.ts +102 -0
- package/ai/tools/cloudflareCrawlTool.ts +289 -0
- package/ai/tools/codeSearchTool.ts +123 -0
- package/ai/tools/codeTools.ts +101 -0
- package/ai/tools/convertMarxistsBookTool.ts +103 -0
- package/ai/tools/createDocTool.ts +132 -0
- package/ai/tools/createSkillDocTool.ts +155 -0
- package/ai/tools/createWorkflowTool.ts +154 -0
- package/ai/tools/delayTool.ts +31 -0
- package/ai/tools/deleteSpacesTool.ts +325 -0
- package/ai/tools/deleteSpacesToolModel.ts +159 -0
- package/ai/tools/devReloadUtils.ts +29 -0
- package/ai/tools/dialogMessageSearch.ts +137 -0
- package/ai/tools/doctorSkillTool.ts +72 -0
- package/ai/tools/ecommerceScraperTool.ts +86 -0
- package/ai/tools/emailTools.ts +549 -0
- package/ai/tools/evalSkillTool.ts +92 -0
- package/ai/tools/exaSearchTool.ts +63 -0
- package/ai/tools/execShellTool.ts +266 -0
- package/ai/tools/executeSqlTool.ts +192 -0
- package/ai/tools/fetchWebpageSupport.ts +333 -0
- package/ai/tools/fetchWebpageTool.ts +97 -0
- package/ai/tools/geminiImagePreviewTool.ts +386 -0
- package/ai/tools/generateDocxTool.ts +215 -0
- package/ai/tools/googleSearchScraperTool.ts +106 -0
- package/ai/tools/importDataTool.ts +133 -0
- package/ai/tools/importSkillTool.ts +162 -0
- package/ai/tools/index.ts +2290 -0
- package/ai/tools/jdProductScraperTool.ts +821 -0
- package/ai/tools/listUserSpacesTool.ts +113 -0
- package/ai/tools/localFilesTool.ts +276 -0
- package/ai/tools/marxistsOfflineBook.ts +253 -0
- package/ai/tools/modelUsageTools.ts +206 -0
- package/ai/tools/noloWorkspaceReadTools.ts +467 -0
- package/ai/tools/olmOcrTool.ts +33 -0
- package/ai/tools/openaiImageTool.ts +267 -0
- package/ai/tools/prepareTools.ts +193 -0
- package/ai/tools/readDocTool.ts +88 -0
- package/ai/tools/readFileTool.ts +214 -0
- package/ai/tools/readTool.ts +194 -0
- package/ai/tools/readXPostTool.ts +229 -0
- package/ai/tools/readXhsProfileTool.ts +638 -0
- package/ai/tools/rememberMemoryTool.ts +84 -0
- package/ai/tools/remotionVideoTool.ts +151 -0
- package/ai/tools/searchDialogMessagesTool.ts +222 -0
- package/ai/tools/searchWorkspaceTool.ts +259 -0
- package/ai/tools/skillFollowup.ts +86 -0
- package/ai/tools/surfWeatherTool.ts +169 -0
- package/ai/tools/table/addTableRowTool.ts +217 -0
- package/ai/tools/table/createTableTool.ts +326 -0
- package/ai/tools/table/rowTools.ts +386 -0
- package/ai/tools/table/schemaTools.ts +244 -0
- package/ai/tools/table/shareTableTool.ts +148 -0
- package/ai/tools/table/toolShared.ts +129 -0
- package/ai/tools/taobaoTmallProductScraperTool.ts +49 -0
- package/ai/tools/toolApiClient.ts +279 -0
- package/ai/tools/toolPacks.ts +19 -0
- package/ai/tools/toolResultError.ts +42 -0
- package/ai/tools/toolRunSlice.ts +303 -0
- package/ai/tools/toolSchemaCompatibility.ts +53 -0
- package/ai/tools/uiAskChoiceTool.ts +104 -0
- package/ai/tools/updateContentTitleTool.ts +84 -0
- package/ai/tools/updateDocTool.ts +105 -0
- package/ai/tools/updateUserPreferenceProfileTool.ts +153 -0
- package/ai/tools/wereadGatewayTool.ts +152 -0
- package/ai/tools/whisperTool.ts +76 -0
- package/ai/tools/writeFileTool.ts +213 -0
- package/ai/tools/youtubeScraperTool.ts +116 -0
- package/ai/tools/ziweiChartTool.ts +678 -0
- package/ai/types.ts +58 -0
- package/ai/workflow/workflowExecutor.ts +323 -0
- package/ai/workflow/workflowSlice.ts +73 -0
- package/ai/workflow/workflowTypes.ts +106 -0
- package/app/actions/syncAppRecord.ts +73 -0
- package/app/constants/animationSets.ts +21 -0
- package/app/constants/appEditor.ts +99 -0
- package/app/favorite/deletedFavoriteProjection.ts +31 -0
- package/app/favorite/favoriteSlice.ts +690 -0
- package/app/favorite/type.ts +43 -0
- package/app/hooks/deleteDbKey.ts +254 -0
- package/app/i18n/client.ts +13 -0
- package/app/i18n/i18n.base.ts +14 -0
- package/app/i18n/i18n.config.ts +77 -0
- package/app/i18n/index.ts +15 -0
- package/app/i18n/mapLanguage.ts +11 -0
- package/app/i18n/translations/appBuilder.locale.ts +453 -0
- package/app/i18n/translations/error.locale.ts +246 -0
- package/app/i18n/translations/interface.locale.ts +2206 -0
- package/app/i18n/translations/pricing.locale.ts +397 -0
- package/app/i18n/translations/privacy.locale.ts +168 -0
- package/app/i18n/translations/seo.locale.ts +129 -0
- package/app/i18n/translations/terms.locale.ts +124 -0
- package/app/i18n/types.ts +6 -0
- package/app/notifications/model.ts +77 -0
- package/app/notifications/notificationSlice.ts +141 -0
- package/app/reducer.ts +36 -0
- package/app/settings/dbActionThunks.ts +34 -0
- package/app/settings/defaultSpacePreference.ts +87 -0
- package/app/settings/settingSlice.tsx +1212 -0
- package/app/settings/settingsRecord.ts +8 -0
- package/app/stateViews/runtime.ts +90 -0
- package/app/store.ts +55 -0
- package/app/theme/colors.ts +280 -0
- package/app/theme/fontPreference.ts +77 -0
- package/app/theme/theme.config.ts +120 -0
- package/app/theme/themeModeBootstrap.ts +123 -0
- package/app/types/appSummary.ts +118 -0
- package/app/types.ts +358 -0
- package/app/utils/appKeys.ts +55 -0
- package/app/utils/async.ts +28 -0
- package/app/utils/desktopAgentRuntimeTurnClient.ts +75 -0
- package/app/utils/env.ts +17 -0
- package/app/utils/fileUtils.ts +229 -0
- package/app/utils/imageUtils.ts +274 -0
- package/app/utils/myContentItems.ts +289 -0
- package/app/utils/retryAfter.ts +36 -0
- package/app/utils/retryFetch.ts +61 -0
- package/app/utils/toast.ts +3 -0
- package/auth/action/signUpAction.ts +227 -0
- package/auth/adminPermissions.ts +78 -0
- package/auth/authSlice.ts +531 -0
- package/auth/client/loginRequest.ts +14 -0
- package/auth/resetAuthScopedClientState.ts +27 -0
- package/auth/routes.ts +183 -0
- package/auth/token.ts +63 -0
- package/auth/types.ts +40 -0
- package/chat/chat.locale.ts +817 -0
- package/chat/dialog/actions/addCybotAction.ts +52 -0
- package/chat/dialog/actions/addReferenceKeysAction.ts +62 -0
- package/chat/dialog/actions/builtinDialogLlm.ts +35 -0
- package/chat/dialog/actions/cleanupCliSession.ts +40 -0
- package/chat/dialog/actions/createDialogAction.ts +379 -0
- package/chat/dialog/actions/createScheduledTaskAction.ts +78 -0
- package/chat/dialog/actions/handleSendMessageAction.ts +202 -0
- package/chat/dialog/actions/handleSendMessageResolver.ts +27 -0
- package/chat/dialog/actions/removeCybotAction.ts +6 -0
- package/chat/dialog/actions/removeDialogAgentAction.ts +44 -0
- package/chat/dialog/actions/replacePrimaryCybotAction.ts +6 -0
- package/chat/dialog/actions/setPrimaryDialogAgentAction.ts +52 -0
- package/chat/dialog/actions/updateDialogSummaryAction.ts +430 -0
- package/chat/dialog/actions/updateDialogTitleAction.ts +151 -0
- package/chat/dialog/actions/updateDialogTitlePolicy.ts +30 -0
- package/chat/dialog/actions/updateTokensAction.ts +233 -0
- package/chat/dialog/dialogAgents.ts +72 -0
- package/chat/dialog/dialogAttachmentCleanup.ts +135 -0
- package/chat/dialog/dialogSlice.ts +985 -0
- package/chat/dialog/dialogTitle.ts +81 -0
- package/chat/dialog/dialogTokenStats.ts +12 -0
- package/chat/dialog/dialogUrl.ts +6 -0
- package/chat/dialog/goal.ts +108 -0
- package/chat/dialog/objectAssistantRegistry.ts +286 -0
- package/chat/dialog/schedule.ts +17 -0
- package/chat/messages/activityCompletion.ts +97 -0
- package/chat/messages/fetchAndCacheMessages.ts +119 -0
- package/chat/messages/fetchConvMsgs.ts +51 -0
- package/chat/messages/fileUrl.ts +13 -0
- package/chat/messages/imagePayloadPersistence.ts +22 -0
- package/chat/messages/messageAgent.ts +8 -0
- package/chat/messages/messageContent.ts +203 -0
- package/chat/messages/messageContract.ts +88 -0
- package/chat/messages/messageSlice.ts +1386 -0
- package/chat/messages/toolThunks.ts +646 -0
- package/chat/messages/types.ts +8 -1
- package/chat/messages/web/toolDisplayName.ts +686 -0
- package/cli/agentAliases.ts +29 -9
- package/cli/agentInternalCommandEntries.ts +11 -3
- package/cli/agentMachineCommands.ts +1 -0
- package/cli/agentRecordCommands.ts +2 -2
- package/cli/agentRecordHelpers.ts +59 -6
- package/cli/agentRunCommand.ts +84 -19
- package/cli/client/agentRun.ts +92 -261
- package/cli/client/localRuntimeAdapter.ts +292 -50
- package/cli/commandRegistry.ts +7 -4
- package/cli/dialogAttachmentCleanup.ts +151 -0
- package/cli/dialogCommands.ts +163 -61
- package/cli/dialogInternalCommandEntries.ts +1 -1
- package/cli/docCreateCommands.ts +345 -0
- package/cli/docInternalCommandEntries.ts +11 -0
- package/cli/docPageHelpers.ts +80 -0
- package/cli/docSkillHelpers.ts +67 -0
- package/cli/docSpaceHelpers.ts +46 -0
- package/cli/internalCommandEntries.ts +4 -0
- package/cli/machineCommands.ts +18 -34
- package/cli/memoryCommands.ts +253 -0
- package/cli/memoryInternalCommandEntries.ts +9 -0
- package/cli/qoderUsageProbe.ts +252 -0
- package/cli/scriptCommandEntries.ts +0 -3
- package/cli/secretScan.ts +31 -0
- package/cli/tableCommands.ts +248 -13
- package/cli/workflowInternalCommandEntries.ts +2 -3
- package/client/agentRun.test.ts +119 -83
- package/client/agentRun.ts +92 -261
- package/client/localRuntimeAdapter.test.ts +894 -32
- package/client/localRuntimeAdapter.ts +292 -50
- package/commandRegistry.ts +7 -4
- package/connector-experimental/capabilities.ts +10 -9
- package/core/builtinAgents.ts +209 -0
- package/core/config.ts +15 -0
- package/core/crypto.ts +51 -0
- package/core/generateKeyPairFromSeedV1.ts +15 -0
- package/core/generateMainKey.ts +22 -0
- package/core/init.ts +41 -0
- package/core/localOrigins.ts +8 -0
- package/core/password.ts +13 -0
- package/core/prefix.ts +23 -0
- package/create/editor/transforms/fromMarkdown/index.ts +260 -0
- package/create/editor/transforms/fromMarkdown/inline.ts +285 -0
- package/create/editor/transforms/fromMarkdown/table.ts +130 -0
- package/create/editor/transforms/markdownToSlate.ts +24 -0
- package/create/editor/transforms/slateToRenderMarkdown.ts +218 -0
- package/create/editor/transforms/slateToSimplifiedMarkdown.ts +224 -0
- package/create/editor/transforms/slateToText.ts +33 -0
- package/create/editor/utils/slateUtils.ts +233 -0
- package/create/space/addSpaceAction.ts +193 -0
- package/create/space/category/categoryActions.ts +427 -0
- package/create/space/changeSpaceAction.ts +36 -0
- package/create/space/constants.ts +8 -0
- package/create/space/content/addContentAction.ts +116 -0
- package/create/space/content/contentThunks.ts +90 -0
- package/create/space/content/deleteContentFromSpaceAction.ts +187 -0
- package/create/space/content/deleteMultipleContentAction.ts +112 -0
- package/create/space/content/moveContentAction.ts +285 -0
- package/create/space/content/updateContentCategoryAction.ts +154 -0
- package/create/space/content/updateContentTitleAction.ts +151 -0
- package/create/space/content/uploadAndAddFileToSpaceAction.ts +98 -0
- package/create/space/contentKeyUtils.ts +117 -0
- package/create/space/deleteSpaceAction.ts +276 -0
- package/create/space/fetchSpaceAction.ts +70 -0
- package/create/space/fetchSpaceSidebarStateAction.ts +26 -0
- package/create/space/loadDefaultSpaceAction.ts +57 -0
- package/create/space/member/addMemberAction.ts +79 -0
- package/create/space/member/fetchUserSpaceMembershipsAction.ts +386 -0
- package/create/space/member/memberThunks.ts +47 -0
- package/create/space/member/removeMemberAction.ts +67 -0
- package/create/space/resolvePreferredSpaceId.ts +113 -0
- package/create/space/sidebarVisibleTypes.ts +260 -0
- package/create/space/space.locale.ts +1087 -0
- package/create/space/spaceAccess.ts +182 -0
- package/create/space/spaceCollapsedState.ts +54 -0
- package/create/space/spaceKeys.ts +65 -0
- package/create/space/spaceSlice.ts +328 -0
- package/create/space/spaceThunks.ts +410 -0
- package/create/space/types.ts +27 -0
- package/create/space/updateSpaceAction.ts +107 -0
- package/create/space/utils/permissions.ts +47 -0
- package/create/types.ts +21 -0
- package/database/actions/agentReadResolution.ts +33 -0
- package/database/actions/common.ts +243 -0
- package/database/actions/deleteFile.ts +40 -0
- package/database/actions/fileContent.ts +125 -0
- package/database/actions/patch.ts +155 -0
- package/database/actions/read.ts +358 -0
- package/database/actions/readAndWait.ts +260 -0
- package/database/actions/readRequestManager.ts +120 -0
- package/database/actions/readResolution.ts +125 -0
- package/database/actions/remove.ts +94 -0
- package/database/actions/replication.ts +477 -0
- package/database/actions/upload.ts +186 -0
- package/database/actions/upsert.ts +56 -0
- package/database/actions/write.ts +146 -0
- package/database/authority/ownerKey.ts +80 -0
- package/database/authority/recordAuthority.ts +67 -0
- package/database/authority/userAuthorityRegistry.ts +54 -0
- package/database/client/fetchUserData.ts +61 -0
- package/database/dbActionThunks.ts +1 -0
- package/database/dbSlice.ts +149 -0
- package/database/fileRing.ts +51 -0
- package/database/fileStorage.ts +232 -0
- package/database/fileUrl.ts +34 -0
- package/database/keys.ts +767 -0
- package/database/queryPrefixes.ts +14 -0
- package/database/requests.ts +443 -0
- package/database/runtimeServerContext.ts +54 -0
- package/database/tenantPlacement.ts +57 -0
- package/database/userPreferenceRegister.ts +131 -0
- package/database/utils/ulid.ts +1 -0
- package/dialogAttachmentCleanup.ts +151 -0
- package/dialogCommands.ts +163 -61
- package/dialogInternalCommandEntries.ts +1 -1
- package/docCreateCommands.ts +345 -0
- package/docInternalCommandEntries.ts +11 -0
- package/docPageHelpers.ts +80 -0
- package/docSkillHelpers.ts +67 -0
- package/docSpaceHelpers.ts +46 -0
- package/integrations/anthropic/anthropicModels.ts +47 -0
- package/integrations/deepseek/models.ts +37 -0
- package/integrations/google/models.ts +341 -0
- package/integrations/openai/chatCompletionStreamMode.ts +10 -0
- package/integrations/openai/filterAndCleanMessages.ts +284 -0
- package/integrations/openai/flexTier.ts +102 -0
- package/integrations/openai/generateOpenAIRequestBody.ts +271 -0
- package/integrations/openai/generateResponseRequestBody.ts +77 -0
- package/integrations/openai/models.ts +271 -0
- package/integrations/openai/providerBodyCompatibility.ts +23 -0
- package/integrations/openai/responsesHelpers.ts +297 -0
- package/integrations/x-reader/types.ts +109 -0
- package/integrations/xai/models.ts +13 -0
- package/integrations/xhs-reader/redaction.ts +68 -0
- package/integrations/xhs-reader/types.ts +277 -0
- package/integrations/xhs-reader/url.ts +80 -0
- package/internalCommandEntries.ts +4 -0
- package/machineCommands.ts +18 -34
- package/memoryCommands.ts +253 -0
- package/memoryInternalCommandEntries.ts +9 -0
- package/package.json +454 -9
- package/qoderUsageProbe.ts +252 -0
- package/render/contentIcon/types.ts +167 -0
- package/render/page/createPageAction.ts +183 -0
- package/render/page/docSlice.ts +495 -0
- package/render/page/types.ts +19 -0
- package/render/table/createTableAction.ts +274 -0
- package/render/table/deleteTableAction.ts +193 -0
- package/render/table/fetchAndCacheTableRows.ts +174 -0
- package/render/table/tableSlice.ts +1167 -0
- package/render/table/toolValueUtils.ts +363 -0
- package/render/table/types.ts +262 -0
- package/render/table/utils/tableSerialization.ts +50 -0
- package/scriptCommandEntries.ts +0 -3
- package/secretScan.ts +31 -0
- package/server/handlers/agentRun/types.ts +105 -0
- package/share/action.ts +225 -0
- package/share/helpers.ts +302 -0
- package/share/keys.ts +243 -0
- package/share/publicSharePolicy.ts +27 -0
- package/share/shareSlice.ts +53 -0
- package/share/types.ts +100 -0
- package/tableCommands.ts +248 -13
- package/workflowInternalCommandEntries.ts +2 -3
- package/ai/tools/agent/taskRunTool.ts +0 -112
- package/cli/client/taskRunPrompt.ts +0 -27
- package/cli/taskRunCommand.ts +0 -265
- package/client/taskRunPrompt.ts +0 -27
- 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
|
-
|
|
539
|
-
|
|
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).
|
|
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
|
|
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-
|
|
1397
|
-
prompt: "Use
|
|
1778
|
+
dbKey: "agent-local-table",
|
|
1779
|
+
prompt: "Use table tools.",
|
|
1398
1780
|
model: "gpt-4.1-mini",
|
|
1399
|
-
toolNames: ["
|
|
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: "
|
|
1416
|
-
input: "
|
|
1797
|
+
agentRef: "table",
|
|
1798
|
+
input: "采集并写入 table",
|
|
1417
1799
|
});
|
|
1418
1800
|
|
|
1419
|
-
|
|
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
|
|
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
|
-
|
|
1427
|
-
AUTH_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-
|
|
1432
|
-
toolNames: ["
|
|
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({
|
|
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("
|
|
1837
|
+
await adapter.loadAgentConfig("table-write");
|
|
1449
1838
|
const result = await adapter.executeTool({
|
|
1450
|
-
id: "call-
|
|
1451
|
-
name: "
|
|
1839
|
+
id: "call-create-table",
|
|
1840
|
+
name: "createTable",
|
|
1841
|
+
userInput: "采集这个用户并写入 table",
|
|
1452
1842
|
arguments: JSON.stringify({
|
|
1453
|
-
|
|
1454
|
-
|
|
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/
|
|
1461
|
-
auth: "Bearer
|
|
1851
|
+
url: "https://us.nolo.chat/api/table/create",
|
|
1852
|
+
auth: "Bearer token-1",
|
|
1462
1853
|
body: {
|
|
1463
|
-
|
|
1464
|
-
|
|
1854
|
+
title: "XHS profile",
|
|
1855
|
+
purpose: "agent_eval_workbench",
|
|
1856
|
+
columns: [{ name: "nickname" }],
|
|
1465
1857
|
},
|
|
1466
1858
|
},
|
|
1467
1859
|
]);
|
|
1468
|
-
expect(result.content).toContain("\"
|
|
1469
|
-
expect(result.metadata).toMatchObject({
|
|
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 {
|