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
|
@@ -13,14 +13,63 @@ type LocalWorkspaceToolArgs = {
|
|
|
13
13
|
commandPrefix?: string[];
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
+
export type ActivityRef =
|
|
17
|
+
| { type: "file"; path: string }
|
|
18
|
+
| { type: "terminal"; id?: string; label?: string }
|
|
19
|
+
| { type: "url"; url: string; label?: string };
|
|
20
|
+
|
|
21
|
+
export type ToolActivityAction = {
|
|
22
|
+
title: string;
|
|
23
|
+
kind?: string;
|
|
24
|
+
detail?: string;
|
|
25
|
+
refs?: ActivityRef[];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type ToolActivityPhase = {
|
|
29
|
+
id: string;
|
|
30
|
+
title: string;
|
|
31
|
+
index?: number;
|
|
32
|
+
total?: number;
|
|
33
|
+
status?: "pending" | "running" | "success" | "failed";
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type ActivityPlan = {
|
|
37
|
+
title?: string;
|
|
38
|
+
phases: Array<{
|
|
39
|
+
id: string;
|
|
40
|
+
title: string;
|
|
41
|
+
index?: number;
|
|
42
|
+
status?: "pending" | "running" | "success" | "failed";
|
|
43
|
+
}>;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type ToolActivity = Partial<ToolActivityAction> & {
|
|
47
|
+
phase?: ToolActivityPhase;
|
|
48
|
+
action?: ToolActivityAction;
|
|
49
|
+
plan?: ActivityPlan;
|
|
50
|
+
};
|
|
51
|
+
|
|
16
52
|
type WorkspaceFileArgs = {
|
|
17
53
|
path?: unknown;
|
|
18
54
|
content?: unknown;
|
|
19
55
|
oldText?: unknown;
|
|
20
56
|
newText?: unknown;
|
|
21
57
|
expectedReplacements?: unknown;
|
|
58
|
+
maxDepth?: unknown;
|
|
59
|
+
entryType?: unknown;
|
|
60
|
+
startLine?: unknown;
|
|
61
|
+
endLine?: unknown;
|
|
62
|
+
maxLines?: unknown;
|
|
63
|
+
tailLines?: unknown;
|
|
22
64
|
query?: unknown;
|
|
65
|
+
pattern?: unknown;
|
|
66
|
+
glob?: unknown;
|
|
67
|
+
exclude?: unknown;
|
|
68
|
+
maxResults?: unknown;
|
|
23
69
|
includeIgnored?: unknown;
|
|
70
|
+
literal?: unknown;
|
|
71
|
+
caseSensitive?: unknown;
|
|
72
|
+
contextLines?: unknown;
|
|
24
73
|
command?: unknown;
|
|
25
74
|
cmd?: unknown;
|
|
26
75
|
branch?: unknown;
|
|
@@ -36,17 +85,28 @@ type WorkspaceFileArgs = {
|
|
|
36
85
|
screenshotPath?: unknown;
|
|
37
86
|
metricsPath?: unknown;
|
|
38
87
|
shell?: unknown;
|
|
88
|
+
_activity?: unknown;
|
|
39
89
|
};
|
|
40
90
|
|
|
41
91
|
type OpenAiCompatibleTool = Record<string, unknown> & {
|
|
42
92
|
function?: Record<string, unknown> & { name?: string };
|
|
43
93
|
};
|
|
44
94
|
|
|
95
|
+
export type GlobFilesDescriptionVariant = "brief" | "strategy" | "workflow" | "antiShell";
|
|
96
|
+
export type GlobFilesParameterVariant = "minimal" | "scoped" | "rich";
|
|
97
|
+
export type SearchFilesDescriptionVariant = "brief" | "strategy" | "workflow" | "antiShell";
|
|
98
|
+
export type SearchFilesParameterVariant = "minimal" | "scoped" | "rich";
|
|
99
|
+
export type ReadFileDescriptionVariant = "brief" | "strategy" | "workflow" | "antiShell";
|
|
100
|
+
export type ReadFileParameterVariant = "minimal" | "scoped" | "rich";
|
|
101
|
+
export type ListFilesDescriptionVariant = "brief" | "strategy" | "workflow" | "antiShell";
|
|
102
|
+
export type ListFilesParameterVariant = "minimal" | "scoped" | "rich";
|
|
103
|
+
|
|
45
104
|
const WORKSPACE_TOOL_NAMES = [
|
|
46
105
|
"listFiles",
|
|
47
106
|
"readFile",
|
|
48
107
|
"writeFile",
|
|
49
108
|
"editFile",
|
|
109
|
+
"globFiles",
|
|
50
110
|
"searchFiles",
|
|
51
111
|
"startPreview",
|
|
52
112
|
"getPreviewStatus",
|
|
@@ -61,6 +121,7 @@ const DEFAULT_LOCAL_CODING_TOOL_NAMES = [
|
|
|
61
121
|
"readFile",
|
|
62
122
|
"writeFile",
|
|
63
123
|
"editFile",
|
|
124
|
+
"globFiles",
|
|
64
125
|
"searchFiles",
|
|
65
126
|
] as const;
|
|
66
127
|
|
|
@@ -75,6 +136,199 @@ const REMOVED_WORKSPACE_TOOL_NAMES = new Set([
|
|
|
75
136
|
"commitWorkspace",
|
|
76
137
|
]);
|
|
77
138
|
|
|
139
|
+
function buildActivityProperty() {
|
|
140
|
+
const refsProperty = {
|
|
141
|
+
type: "array",
|
|
142
|
+
items: {
|
|
143
|
+
type: "object",
|
|
144
|
+
properties: {
|
|
145
|
+
type: { type: "string", enum: ["file", "terminal", "url"] },
|
|
146
|
+
path: { type: "string" },
|
|
147
|
+
id: { type: "string" },
|
|
148
|
+
label: { type: "string" },
|
|
149
|
+
url: { type: "string" },
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
description: "Referenced files, terminals, or URLs for this action.",
|
|
153
|
+
};
|
|
154
|
+
const actionProperty = {
|
|
155
|
+
type: "object",
|
|
156
|
+
properties: {
|
|
157
|
+
title: { type: "string", description: "Short human-readable action label." },
|
|
158
|
+
kind: {
|
|
159
|
+
type: "string",
|
|
160
|
+
enum: ["read", "write", "edit", "search", "terminal", "version", "test", "build", "preview", "other"],
|
|
161
|
+
},
|
|
162
|
+
detail: { type: "string", description: "Optional concise detail shown after the label." },
|
|
163
|
+
refs: refsProperty,
|
|
164
|
+
},
|
|
165
|
+
required: ["title"],
|
|
166
|
+
};
|
|
167
|
+
const planPhaseProperty = {
|
|
168
|
+
type: "object",
|
|
169
|
+
properties: {
|
|
170
|
+
id: { type: "string", description: "Stable phase id reused by activity.phase.id." },
|
|
171
|
+
title: { type: "string", description: "Human-readable planned task phase." },
|
|
172
|
+
index: { type: "number" },
|
|
173
|
+
status: { type: "string", enum: ["pending", "running", "success", "failed"] },
|
|
174
|
+
},
|
|
175
|
+
required: ["id", "title"],
|
|
176
|
+
};
|
|
177
|
+
return {
|
|
178
|
+
type: "object",
|
|
179
|
+
description:
|
|
180
|
+
"Optional lightweight human-readable activity hint for UI display only. Prefer { plan, phase, action }. The executor ignores this for execution semantics but echoes it back in result metadata.",
|
|
181
|
+
properties: {
|
|
182
|
+
title: { type: "string", description: "Short human-readable action label." },
|
|
183
|
+
kind: {
|
|
184
|
+
type: "string",
|
|
185
|
+
enum: ["read", "write", "edit", "search", "terminal", "version", "test", "build", "preview", "other"],
|
|
186
|
+
},
|
|
187
|
+
detail: { type: "string", description: "Optional longer description shown on expand." },
|
|
188
|
+
refs: refsProperty,
|
|
189
|
+
plan: {
|
|
190
|
+
type: "object",
|
|
191
|
+
properties: {
|
|
192
|
+
title: { type: "string", description: "Optional label for the visible task progress block." },
|
|
193
|
+
phases: {
|
|
194
|
+
type: "array",
|
|
195
|
+
items: planPhaseProperty,
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
required: ["phases"],
|
|
199
|
+
},
|
|
200
|
+
phase: {
|
|
201
|
+
type: "object",
|
|
202
|
+
properties: {
|
|
203
|
+
id: { type: "string", description: "Stable phase id reused across related tool calls." },
|
|
204
|
+
title: { type: "string", description: "Human-readable current task phase." },
|
|
205
|
+
index: { type: "number" },
|
|
206
|
+
total: { type: "number" },
|
|
207
|
+
status: { type: "string", enum: ["pending", "running", "success", "failed"] },
|
|
208
|
+
},
|
|
209
|
+
required: ["id", "title"],
|
|
210
|
+
},
|
|
211
|
+
action: actionProperty,
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function readTrimmedString(value: unknown): string | undefined {
|
|
217
|
+
if (typeof value !== "string") return undefined;
|
|
218
|
+
const trimmed = value.trim();
|
|
219
|
+
return trimmed || undefined;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function readFiniteNumber(value: unknown): number | undefined {
|
|
223
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function extractActivityRefs(rawRefs: unknown): ActivityRef[] | undefined {
|
|
227
|
+
if (!Array.isArray(rawRefs)) return undefined;
|
|
228
|
+
const refs = rawRefs.flatMap((entry): ActivityRef[] => {
|
|
229
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return [];
|
|
230
|
+
const ref = entry as Record<string, unknown>;
|
|
231
|
+
if (ref.type === "file") {
|
|
232
|
+
const path = readTrimmedString(ref.path);
|
|
233
|
+
return path ? [{ type: "file", path }] : [];
|
|
234
|
+
}
|
|
235
|
+
if (ref.type === "terminal") {
|
|
236
|
+
const id = readTrimmedString(ref.id);
|
|
237
|
+
const label = readTrimmedString(ref.label);
|
|
238
|
+
return id || label
|
|
239
|
+
? [{ type: "terminal", ...(id ? { id } : {}), ...(label ? { label } : {}) }]
|
|
240
|
+
: [];
|
|
241
|
+
}
|
|
242
|
+
if (ref.type === "url") {
|
|
243
|
+
const url = readTrimmedString(ref.url);
|
|
244
|
+
const label = readTrimmedString(ref.label);
|
|
245
|
+
return url ? [{ type: "url", url, ...(label ? { label } : {}) }] : [];
|
|
246
|
+
}
|
|
247
|
+
return [];
|
|
248
|
+
});
|
|
249
|
+
return refs.length ? refs : undefined;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function extractActivityAction(rawAction: unknown): ToolActivityAction | undefined {
|
|
253
|
+
if (!rawAction || typeof rawAction !== "object" || Array.isArray(rawAction)) return undefined;
|
|
254
|
+
const obj = rawAction as Record<string, unknown>;
|
|
255
|
+
const title = readTrimmedString(obj.title);
|
|
256
|
+
if (!title) return undefined;
|
|
257
|
+
const kind = readTrimmedString(obj.kind);
|
|
258
|
+
const detail = readTrimmedString(obj.detail);
|
|
259
|
+
const refs = extractActivityRefs(obj.refs);
|
|
260
|
+
return {
|
|
261
|
+
title,
|
|
262
|
+
...(kind ? { kind } : {}),
|
|
263
|
+
...(detail ? { detail } : {}),
|
|
264
|
+
...(refs ? { refs } : {}),
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function extractActivityPhase(rawPhase: unknown): ToolActivityPhase | undefined {
|
|
269
|
+
if (!rawPhase || typeof rawPhase !== "object" || Array.isArray(rawPhase)) return undefined;
|
|
270
|
+
const obj = rawPhase as Record<string, unknown>;
|
|
271
|
+
const title = readTrimmedString(obj.title);
|
|
272
|
+
if (!title) return undefined;
|
|
273
|
+
const id = readTrimmedString(obj.id) || title.toLowerCase().replace(/\s+/g, "-");
|
|
274
|
+
const index = readFiniteNumber(obj.index);
|
|
275
|
+
const total = readFiniteNumber(obj.total);
|
|
276
|
+
const status =
|
|
277
|
+
obj.status === "pending" ||
|
|
278
|
+
obj.status === "running" ||
|
|
279
|
+
obj.status === "success" ||
|
|
280
|
+
obj.status === "failed"
|
|
281
|
+
? obj.status
|
|
282
|
+
: undefined;
|
|
283
|
+
return {
|
|
284
|
+
id,
|
|
285
|
+
title,
|
|
286
|
+
...(index !== undefined ? { index } : {}),
|
|
287
|
+
...(total !== undefined ? { total } : {}),
|
|
288
|
+
...(status ? { status } : {}),
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function extractActivityPlan(rawPlan: unknown): ActivityPlan | undefined {
|
|
293
|
+
if (!rawPlan || typeof rawPlan !== "object" || Array.isArray(rawPlan)) return undefined;
|
|
294
|
+
const obj = rawPlan as Record<string, unknown>;
|
|
295
|
+
if (!Array.isArray(obj.phases)) return undefined;
|
|
296
|
+
const phases = obj.phases.flatMap((entry, index): ActivityPlan["phases"] => {
|
|
297
|
+
const phase = extractActivityPhase(entry);
|
|
298
|
+
if (!phase) return [];
|
|
299
|
+
return [{
|
|
300
|
+
id: phase.id,
|
|
301
|
+
title: phase.title,
|
|
302
|
+
index: phase.index ?? index + 1,
|
|
303
|
+
...(phase.status ? { status: phase.status } : {}),
|
|
304
|
+
}];
|
|
305
|
+
});
|
|
306
|
+
if (phases.length === 0) return undefined;
|
|
307
|
+
const title = readTrimmedString(obj.title);
|
|
308
|
+
return {
|
|
309
|
+
...(title ? { title } : {}),
|
|
310
|
+
phases,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function extractActivity(parsed: WorkspaceFileArgs): ToolActivity | undefined {
|
|
315
|
+
const raw = parsed._activity;
|
|
316
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return undefined;
|
|
317
|
+
const obj = raw as Record<string, unknown>;
|
|
318
|
+
const nestedAction = extractActivityAction(obj.action);
|
|
319
|
+
const legacyAction = extractActivityAction(obj);
|
|
320
|
+
const action = nestedAction || legacyAction;
|
|
321
|
+
const phase = extractActivityPhase(obj.phase);
|
|
322
|
+
const plan = extractActivityPlan(obj.plan);
|
|
323
|
+
if (!action && !plan) return undefined;
|
|
324
|
+
return {
|
|
325
|
+
...(action ? action : {}),
|
|
326
|
+
...(phase ? { phase } : {}),
|
|
327
|
+
...(nestedAction ? { action: nestedAction } : {}),
|
|
328
|
+
...(plan ? { plan } : {}),
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
78
332
|
function buildWorkspacePathProperty() {
|
|
79
333
|
return {
|
|
80
334
|
type: "string",
|
|
@@ -82,36 +336,153 @@ function buildWorkspacePathProperty() {
|
|
|
82
336
|
};
|
|
83
337
|
}
|
|
84
338
|
|
|
85
|
-
function
|
|
339
|
+
function buildListWorkspaceDescription(variant?: ListFilesDescriptionVariant) {
|
|
340
|
+
if (variant === "brief") {
|
|
341
|
+
return "List files and directories inside a workspace directory.";
|
|
342
|
+
}
|
|
343
|
+
if (variant === "workflow") {
|
|
344
|
+
return "List a bounded directory overview inside the workspace. Use first when you need to understand nearby structure, choose a subdirectory, or inspect a non-code project. Then use globFiles for path-pattern discovery, searchFiles for text/content discovery, and readFile only for the specific files or line ranges you need.";
|
|
345
|
+
}
|
|
346
|
+
if (variant === "antiShell") {
|
|
347
|
+
return "List a bounded directory overview inside the workspace. Do not use execShell with ls/find/tree for normal directory overviews when listFiles can show the needed depth and limit. Use globFiles for path-pattern discovery and searchFiles for content search.";
|
|
348
|
+
}
|
|
349
|
+
return "List a bounded directory overview inside the workspace. Use for scanning nearby structure, not for path-pattern discovery across the repo. Prefer globFiles for finding files by name/extension and searchFiles for text inside files.";
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function buildListWorkspaceParameters(variant?: ListFilesParameterVariant) {
|
|
353
|
+
const path = buildWorkspacePathProperty();
|
|
354
|
+
const _activity = buildActivityProperty();
|
|
355
|
+
const maxDepth = {
|
|
356
|
+
type: "integer",
|
|
357
|
+
description: "Maximum directory depth to include. Defaults to 1 for the requested directory only.",
|
|
358
|
+
};
|
|
359
|
+
const maxResults = {
|
|
360
|
+
type: "integer",
|
|
361
|
+
description: "Maximum number of entries to return. Use a small value for large directories.",
|
|
362
|
+
};
|
|
363
|
+
const entryType = {
|
|
364
|
+
type: "string",
|
|
365
|
+
enum: ["all", "files", "directories"],
|
|
366
|
+
description: "Filter returned entries. Defaults to all.",
|
|
367
|
+
};
|
|
368
|
+
if (variant === "minimal") {
|
|
369
|
+
return {
|
|
370
|
+
type: "object",
|
|
371
|
+
properties: { path, _activity },
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
if (variant === "rich") {
|
|
375
|
+
return {
|
|
376
|
+
type: "object",
|
|
377
|
+
properties: {
|
|
378
|
+
path,
|
|
379
|
+
maxDepth,
|
|
380
|
+
maxResults,
|
|
381
|
+
entryType,
|
|
382
|
+
_activity,
|
|
383
|
+
},
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
return {
|
|
387
|
+
type: "object",
|
|
388
|
+
properties: {
|
|
389
|
+
path,
|
|
390
|
+
maxResults,
|
|
391
|
+
_activity,
|
|
392
|
+
},
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function buildListWorkspaceFilesTool(args?: {
|
|
397
|
+
descriptionVariant?: ListFilesDescriptionVariant;
|
|
398
|
+
parameterVariant?: ListFilesParameterVariant;
|
|
399
|
+
}): OpenAiCompatibleTool {
|
|
86
400
|
return {
|
|
87
401
|
type: "function",
|
|
88
402
|
function: {
|
|
89
403
|
name: "listFiles",
|
|
90
|
-
description:
|
|
91
|
-
parameters:
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
404
|
+
description: buildListWorkspaceDescription(args?.descriptionVariant),
|
|
405
|
+
parameters: buildListWorkspaceParameters(args?.parameterVariant),
|
|
406
|
+
},
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function buildReadWorkspaceDescription(variant?: ReadFileDescriptionVariant) {
|
|
411
|
+
if (variant === "brief") {
|
|
412
|
+
return "Read a UTF-8 text file inside the workspace.";
|
|
413
|
+
}
|
|
414
|
+
if (variant === "workflow") {
|
|
415
|
+
return "Read a UTF-8 text file inside the workspace after listFiles, globFiles, or searchFiles has narrowed the target. Use line ranges from searchFiles matches and tailLines for logs or generated output. Read the whole file only when the task truly needs all content or exact edit context.";
|
|
416
|
+
}
|
|
417
|
+
if (variant === "antiShell") {
|
|
418
|
+
return "Read a UTF-8 text file inside the workspace. Do not use execShell with cat/sed/head/tail for normal text reads when readFile can return the needed file or line range. Use searchFiles first for content search, then readFile only the relevant lines when possible.";
|
|
419
|
+
}
|
|
420
|
+
return "Read a UTF-8 text file inside the workspace. Read before editing when you need exact text for editFile. For discovery or classification tasks, avoid batch-reading every candidate; report candidate paths first, or read only one to three representative files with maxLines or focused line ranges. Use line ranges for large code, docs, data, configs, and logs after searchFiles returns line numbers. Read the whole file only when the task truly needs all content or exact edit context.";
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function buildReadWorkspaceParameters(variant?: ReadFileParameterVariant) {
|
|
424
|
+
const path = buildWorkspacePathProperty();
|
|
425
|
+
const _activity = buildActivityProperty();
|
|
426
|
+
const startLine = {
|
|
427
|
+
type: "integer",
|
|
428
|
+
description: "1-based first line to return. Use with endLine or maxLines after searchFiles gives a line number.",
|
|
429
|
+
};
|
|
430
|
+
const endLine = {
|
|
431
|
+
type: "integer",
|
|
432
|
+
description: "1-based last line to return, inclusive.",
|
|
433
|
+
};
|
|
434
|
+
const maxLines = {
|
|
435
|
+
type: "integer",
|
|
436
|
+
description: "Maximum number of lines to return starting at startLine, or from the start of the file when startLine is omitted. Use for lightweight previews of candidate configs, docs, data, and logs instead of reading many whole files. When the user gives a read/preview budget, each readFile preview consumes one budget slot.",
|
|
437
|
+
};
|
|
438
|
+
const tailLines = {
|
|
439
|
+
type: "integer",
|
|
440
|
+
description: "Return only the last N lines, useful for logs and generated text.",
|
|
441
|
+
};
|
|
442
|
+
if (variant === "minimal") {
|
|
443
|
+
return {
|
|
444
|
+
type: "object",
|
|
445
|
+
properties: { path, _activity },
|
|
446
|
+
required: ["path"],
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
if (variant === "rich") {
|
|
450
|
+
return {
|
|
451
|
+
type: "object",
|
|
452
|
+
properties: {
|
|
453
|
+
path,
|
|
454
|
+
startLine,
|
|
455
|
+
endLine,
|
|
456
|
+
maxLines,
|
|
457
|
+
tailLines,
|
|
458
|
+
_activity,
|
|
96
459
|
},
|
|
460
|
+
required: ["path"],
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
return {
|
|
464
|
+
type: "object",
|
|
465
|
+
properties: {
|
|
466
|
+
path,
|
|
467
|
+
startLine,
|
|
468
|
+
endLine,
|
|
469
|
+
maxLines,
|
|
470
|
+
_activity,
|
|
97
471
|
},
|
|
472
|
+
required: ["path"],
|
|
98
473
|
};
|
|
99
474
|
}
|
|
100
475
|
|
|
101
|
-
function buildReadWorkspaceFileTool(
|
|
476
|
+
function buildReadWorkspaceFileTool(args?: {
|
|
477
|
+
descriptionVariant?: ReadFileDescriptionVariant;
|
|
478
|
+
parameterVariant?: ReadFileParameterVariant;
|
|
479
|
+
}): OpenAiCompatibleTool {
|
|
102
480
|
return {
|
|
103
481
|
type: "function",
|
|
104
482
|
function: {
|
|
105
483
|
name: "readFile",
|
|
106
|
-
description:
|
|
107
|
-
|
|
108
|
-
parameters: {
|
|
109
|
-
type: "object",
|
|
110
|
-
properties: {
|
|
111
|
-
path: buildWorkspacePathProperty(),
|
|
112
|
-
},
|
|
113
|
-
required: ["path"],
|
|
114
|
-
},
|
|
484
|
+
description: buildReadWorkspaceDescription(args?.descriptionVariant),
|
|
485
|
+
parameters: buildReadWorkspaceParameters(args?.parameterVariant),
|
|
115
486
|
},
|
|
116
487
|
};
|
|
117
488
|
}
|
|
@@ -131,6 +502,7 @@ function buildWriteWorkspaceFileTool(): OpenAiCompatibleTool {
|
|
|
131
502
|
type: "string",
|
|
132
503
|
description: "Full UTF-8 file content to write.",
|
|
133
504
|
},
|
|
505
|
+
_activity: buildActivityProperty(),
|
|
134
506
|
},
|
|
135
507
|
required: ["path", "content"],
|
|
136
508
|
},
|
|
@@ -161,6 +533,7 @@ function buildReplaceWorkspaceTextTool(): OpenAiCompatibleTool {
|
|
|
161
533
|
type: "integer",
|
|
162
534
|
description: "Expected replacement count. Defaults to 1.",
|
|
163
535
|
},
|
|
536
|
+
_activity: buildActivityProperty(),
|
|
164
537
|
},
|
|
165
538
|
required: ["path", "oldText", "newText"],
|
|
166
539
|
},
|
|
@@ -168,28 +541,183 @@ function buildReplaceWorkspaceTextTool(): OpenAiCompatibleTool {
|
|
|
168
541
|
};
|
|
169
542
|
}
|
|
170
543
|
|
|
171
|
-
function
|
|
544
|
+
function buildSearchWorkspaceDescription(variant?: SearchFilesDescriptionVariant) {
|
|
545
|
+
if (variant === "brief") {
|
|
546
|
+
return "Search workspace file contents.";
|
|
547
|
+
}
|
|
548
|
+
if (variant === "workflow") {
|
|
549
|
+
return "Search file contents inside the current workspace after listFiles or globFiles has scoped the likely area, or directly when the task names text to find. Use for code, docs, data, configs, logs, and other text files. Return line numbers, then use readFile with focused ranges for interpretation or editing.";
|
|
550
|
+
}
|
|
551
|
+
if (variant === "antiShell") {
|
|
552
|
+
return "Search text inside workspace files for code, docs, data, configs, logs, or other text-like assets. Do not use execShell for content search when searchFiles can directly find matching lines. Use globFiles instead for path-only discovery, then readFile for exact surrounding content when needed.";
|
|
553
|
+
}
|
|
554
|
+
return "Search file contents inside the current workspace using ripgrep when available. Use for grep-like text or regex search across code, docs, data, configs, logs, and other text files. Use globFiles instead when you only need to find files by name or extension before readFile.";
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function buildSearchWorkspaceParameters(variant?: SearchFilesParameterVariant) {
|
|
558
|
+
const query = {
|
|
559
|
+
type: "string",
|
|
560
|
+
description: "Search query. Treated as a regular expression unless literal is true.",
|
|
561
|
+
};
|
|
562
|
+
const path = buildWorkspacePathProperty();
|
|
563
|
+
const includeIgnored = {
|
|
564
|
+
type: "boolean",
|
|
565
|
+
description:
|
|
566
|
+
"When true, search files ignored by .gitignore such as .tmp. Defaults to false; .git and node_modules remain excluded.",
|
|
567
|
+
};
|
|
568
|
+
const exclude = {
|
|
569
|
+
type: "array",
|
|
570
|
+
items: { type: "string" },
|
|
571
|
+
description: "Glob patterns to exclude from content search, for example ['dist/**', 'build/**', 'exports/**', '*.log'].",
|
|
572
|
+
};
|
|
573
|
+
const maxResults = {
|
|
574
|
+
type: "integer",
|
|
575
|
+
description: "Maximum number of matching output lines to return. Use a small value when you only need candidate files or examples.",
|
|
576
|
+
};
|
|
577
|
+
const literal = {
|
|
578
|
+
type: "boolean",
|
|
579
|
+
description: "When true, search for query as literal text instead of a regular expression.",
|
|
580
|
+
};
|
|
581
|
+
const caseSensitive = {
|
|
582
|
+
type: "boolean",
|
|
583
|
+
description: "When false, search case-insensitively. Defaults to true.",
|
|
584
|
+
};
|
|
585
|
+
const contextLines = {
|
|
586
|
+
type: "integer",
|
|
587
|
+
description: "Number of surrounding context lines to include around each match. Defaults to 0.",
|
|
588
|
+
};
|
|
589
|
+
if (variant === "minimal") {
|
|
590
|
+
return {
|
|
591
|
+
type: "object",
|
|
592
|
+
properties: { query, _activity: buildActivityProperty() },
|
|
593
|
+
required: ["query"],
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
if (variant === "rich") {
|
|
597
|
+
return {
|
|
598
|
+
type: "object",
|
|
599
|
+
properties: {
|
|
600
|
+
query,
|
|
601
|
+
path,
|
|
602
|
+
exclude,
|
|
603
|
+
includeIgnored,
|
|
604
|
+
maxResults,
|
|
605
|
+
literal,
|
|
606
|
+
caseSensitive,
|
|
607
|
+
contextLines,
|
|
608
|
+
_activity: buildActivityProperty(),
|
|
609
|
+
},
|
|
610
|
+
required: ["query"],
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
return {
|
|
614
|
+
type: "object",
|
|
615
|
+
properties: {
|
|
616
|
+
query,
|
|
617
|
+
path,
|
|
618
|
+
includeIgnored,
|
|
619
|
+
maxResults,
|
|
620
|
+
_activity: buildActivityProperty(),
|
|
621
|
+
},
|
|
622
|
+
required: ["query"],
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
function buildSearchWorkspaceTool(args?: {
|
|
627
|
+
descriptionVariant?: SearchFilesDescriptionVariant;
|
|
628
|
+
parameterVariant?: SearchFilesParameterVariant;
|
|
629
|
+
}): OpenAiCompatibleTool {
|
|
172
630
|
return {
|
|
173
631
|
type: "function",
|
|
174
632
|
function: {
|
|
175
633
|
name: "searchFiles",
|
|
176
|
-
description:
|
|
177
|
-
parameters:
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
634
|
+
description: buildSearchWorkspaceDescription(args?.descriptionVariant),
|
|
635
|
+
parameters: buildSearchWorkspaceParameters(args?.parameterVariant),
|
|
636
|
+
},
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
function buildGlobWorkspaceDescription(variant?: GlobFilesDescriptionVariant) {
|
|
641
|
+
if (variant === "brief") {
|
|
642
|
+
return "Find workspace files by path glob without reading file contents.";
|
|
643
|
+
}
|
|
644
|
+
if (variant === "workflow") {
|
|
645
|
+
return "Find files by glob pattern inside the current workspace without reading file contents. Use after listFiles when you know the likely area, or first when the task names a filename, extension, asset type, config, or document pattern. Then use searchFiles for text inside candidates or readFile for the specific candidate paths.";
|
|
646
|
+
}
|
|
647
|
+
if (variant === "antiShell") {
|
|
648
|
+
return "Find workspace files by path glob without reading file contents. Use for code, docs, data, images, configs, logs, or any file discovery task. Do not use execShell or listFiles for path discovery when globFiles can directly narrow candidate files. Use before opening or reading files, and use searchFiles instead for searching text inside files.";
|
|
649
|
+
}
|
|
650
|
+
return "Find files by glob pattern inside the current workspace without reading file contents. Use for code, docs, data, images, configs, logs, or any file discovery task. Use one bounded glob with brace groups before repeated narrow globs when a task names several extensions or config names, for example **/*.{png,jpg,svg}, **/{package.json,bunfig.toml,tsconfig*.json}, or src/**/*.ts. Prefer searchFiles for searching text inside files, and readFile only after candidate paths are narrow enough.";
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
function buildGlobWorkspaceParameters(variant?: GlobFilesParameterVariant) {
|
|
654
|
+
const pattern = {
|
|
655
|
+
type: "string",
|
|
656
|
+
description: "Glob pattern for files. For candidate discovery, combine related names or extensions in one brace-group pattern before making repeated narrow calls, for example '**/*.ts', 'packages/**/local*.test.ts', '**/*.{png,jpg,svg}', or '**/{package.json,bunfig.toml,tsconfig*.json}'.",
|
|
657
|
+
};
|
|
658
|
+
const path = buildWorkspacePathProperty();
|
|
659
|
+
const includeIgnored = {
|
|
660
|
+
type: "boolean",
|
|
661
|
+
description:
|
|
662
|
+
"When true, include files ignored by .gitignore. Defaults to false; .git and node_modules remain excluded.",
|
|
663
|
+
};
|
|
664
|
+
const maxResults = {
|
|
665
|
+
type: "integer",
|
|
666
|
+
description: "Maximum number of file paths to return. Use a small value when you only need candidate paths before readFile; if results are truncated, report that and narrow the pattern or path before reading.",
|
|
667
|
+
};
|
|
668
|
+
const exclude = {
|
|
669
|
+
type: "array",
|
|
670
|
+
items: { type: "string" },
|
|
671
|
+
description: "Glob patterns to exclude from results, for example ['dist/**', 'build/**', 'exports/**', '*.tmp'].",
|
|
672
|
+
};
|
|
673
|
+
if (variant === "minimal") {
|
|
674
|
+
return {
|
|
675
|
+
type: "object",
|
|
676
|
+
properties: { pattern, _activity: buildActivityProperty() },
|
|
677
|
+
required: ["pattern"],
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
if (variant === "rich") {
|
|
681
|
+
return {
|
|
682
|
+
type: "object",
|
|
683
|
+
properties: {
|
|
684
|
+
pattern,
|
|
685
|
+
path,
|
|
686
|
+
exclude,
|
|
687
|
+
includeIgnored,
|
|
688
|
+
maxResults,
|
|
689
|
+
_activity: buildActivityProperty(),
|
|
192
690
|
},
|
|
691
|
+
required: ["pattern"],
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
return {
|
|
695
|
+
type: "object",
|
|
696
|
+
properties: {
|
|
697
|
+
pattern,
|
|
698
|
+
glob: {
|
|
699
|
+
type: "string",
|
|
700
|
+
description: "Alias for pattern, kept for compatibility with codeSearch-style prompts.",
|
|
701
|
+
},
|
|
702
|
+
path,
|
|
703
|
+
exclude,
|
|
704
|
+
includeIgnored,
|
|
705
|
+
maxResults,
|
|
706
|
+
_activity: buildActivityProperty(),
|
|
707
|
+
},
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
function buildGlobWorkspaceFilesTool(args?: {
|
|
712
|
+
descriptionVariant?: GlobFilesDescriptionVariant;
|
|
713
|
+
parameterVariant?: GlobFilesParameterVariant;
|
|
714
|
+
}): OpenAiCompatibleTool {
|
|
715
|
+
return {
|
|
716
|
+
type: "function",
|
|
717
|
+
function: {
|
|
718
|
+
name: "globFiles",
|
|
719
|
+
description: buildGlobWorkspaceDescription(args?.descriptionVariant),
|
|
720
|
+
parameters: buildGlobWorkspaceParameters(args?.parameterVariant),
|
|
193
721
|
},
|
|
194
722
|
};
|
|
195
723
|
}
|
|
@@ -277,6 +805,7 @@ function buildExecShellTool(toolName: string): OpenAiCompatibleTool {
|
|
|
277
805
|
type: "string",
|
|
278
806
|
description: "Shell command to run.",
|
|
279
807
|
},
|
|
808
|
+
_activity: buildActivityProperty(),
|
|
280
809
|
},
|
|
281
810
|
},
|
|
282
811
|
},
|
|
@@ -330,12 +859,27 @@ function buildWorkspaceShellCommand(args: {
|
|
|
330
859
|
: buildBashCommand(args.command);
|
|
331
860
|
}
|
|
332
861
|
|
|
333
|
-
function buildWorkspaceToolDefinition(toolName: string
|
|
862
|
+
function buildWorkspaceToolDefinition(toolName: string, args?: {
|
|
863
|
+
listFilesDescriptionVariant?: ListFilesDescriptionVariant;
|
|
864
|
+
listFilesParameterVariant?: ListFilesParameterVariant;
|
|
865
|
+
readFileDescriptionVariant?: ReadFileDescriptionVariant;
|
|
866
|
+
readFileParameterVariant?: ReadFileParameterVariant;
|
|
867
|
+
globFilesDescriptionVariant?: GlobFilesDescriptionVariant;
|
|
868
|
+
globFilesParameterVariant?: GlobFilesParameterVariant;
|
|
869
|
+
searchFilesDescriptionVariant?: SearchFilesDescriptionVariant;
|
|
870
|
+
searchFilesParameterVariant?: SearchFilesParameterVariant;
|
|
871
|
+
}) {
|
|
334
872
|
if (toolName === "listFiles") {
|
|
335
|
-
return buildListWorkspaceFilesTool(
|
|
873
|
+
return buildListWorkspaceFilesTool({
|
|
874
|
+
descriptionVariant: args?.listFilesDescriptionVariant,
|
|
875
|
+
parameterVariant: args?.listFilesParameterVariant,
|
|
876
|
+
});
|
|
336
877
|
}
|
|
337
878
|
if (toolName === "readFile") {
|
|
338
|
-
return buildReadWorkspaceFileTool(
|
|
879
|
+
return buildReadWorkspaceFileTool({
|
|
880
|
+
descriptionVariant: args?.readFileDescriptionVariant,
|
|
881
|
+
parameterVariant: args?.readFileParameterVariant,
|
|
882
|
+
});
|
|
339
883
|
}
|
|
340
884
|
if (toolName === "writeFile") {
|
|
341
885
|
return buildWriteWorkspaceFileTool();
|
|
@@ -343,8 +887,17 @@ function buildWorkspaceToolDefinition(toolName: string) {
|
|
|
343
887
|
if (toolName === "editFile") {
|
|
344
888
|
return buildReplaceWorkspaceTextTool();
|
|
345
889
|
}
|
|
890
|
+
if (toolName === "globFiles") {
|
|
891
|
+
return buildGlobWorkspaceFilesTool({
|
|
892
|
+
descriptionVariant: args?.globFilesDescriptionVariant,
|
|
893
|
+
parameterVariant: args?.globFilesParameterVariant,
|
|
894
|
+
});
|
|
895
|
+
}
|
|
346
896
|
if (toolName === "searchFiles") {
|
|
347
|
-
return buildSearchWorkspaceTool(
|
|
897
|
+
return buildSearchWorkspaceTool({
|
|
898
|
+
descriptionVariant: args?.searchFilesDescriptionVariant,
|
|
899
|
+
parameterVariant: args?.searchFilesParameterVariant,
|
|
900
|
+
});
|
|
348
901
|
}
|
|
349
902
|
if (toolName === "startPreview" || toolName === "getPreviewStatus" || toolName === "stopPreview" || toolName === "releasePreview") {
|
|
350
903
|
return buildPreviewLifecycleTool(toolName);
|
|
@@ -368,8 +921,19 @@ function filterDeclaredWorkspaceToolNames(args: {
|
|
|
368
921
|
export function buildLocalWorkspaceToolset(args: {
|
|
369
922
|
declaredToolNames?: string[];
|
|
370
923
|
exposeShellTools?: boolean;
|
|
924
|
+
useDeclaredToolNamesOnly?: boolean;
|
|
371
925
|
}) {
|
|
372
926
|
const exposeShellTools = args.exposeShellTools === true;
|
|
927
|
+
if (args.useDeclaredToolNamesOnly) {
|
|
928
|
+
const toolNames = new Set(filterDeclaredWorkspaceToolNames({
|
|
929
|
+
toolNames: args.declaredToolNames,
|
|
930
|
+
exposeShellTools,
|
|
931
|
+
}));
|
|
932
|
+
return {
|
|
933
|
+
toolNames: [...toolNames],
|
|
934
|
+
exposeShellTools,
|
|
935
|
+
};
|
|
936
|
+
}
|
|
373
937
|
const toolNames = new Set([
|
|
374
938
|
...DEFAULT_LOCAL_CODING_TOOL_NAMES,
|
|
375
939
|
...(exposeShellTools ? SHELL_TOOL_NAMES : []),
|
|
@@ -387,7 +951,14 @@ export function buildLocalWorkspaceToolset(args: {
|
|
|
387
951
|
export function buildLocalWorkspacePolicyToolNames(args: {
|
|
388
952
|
declaredToolNames?: string[];
|
|
389
953
|
exposeShellTools?: boolean;
|
|
954
|
+
useDeclaredToolNamesOnly?: boolean;
|
|
390
955
|
}) {
|
|
956
|
+
if (args.useDeclaredToolNamesOnly) {
|
|
957
|
+
return filterDeclaredWorkspaceToolNames({
|
|
958
|
+
toolNames: args.declaredToolNames,
|
|
959
|
+
exposeShellTools: args.exposeShellTools === true,
|
|
960
|
+
});
|
|
961
|
+
}
|
|
391
962
|
return [...new Set([
|
|
392
963
|
...buildLocalWorkspaceToolset({
|
|
393
964
|
exposeShellTools: args.exposeShellTools,
|
|
@@ -402,6 +973,14 @@ export function buildLocalWorkspacePolicyToolNames(args: {
|
|
|
402
973
|
export function buildLocalWorkspaceOpenAiTools(args: {
|
|
403
974
|
toolNames?: string[];
|
|
404
975
|
exposeShellTools?: boolean;
|
|
976
|
+
listFilesDescriptionVariant?: ListFilesDescriptionVariant;
|
|
977
|
+
listFilesParameterVariant?: ListFilesParameterVariant;
|
|
978
|
+
readFileDescriptionVariant?: ReadFileDescriptionVariant;
|
|
979
|
+
readFileParameterVariant?: ReadFileParameterVariant;
|
|
980
|
+
globFilesDescriptionVariant?: GlobFilesDescriptionVariant;
|
|
981
|
+
globFilesParameterVariant?: GlobFilesParameterVariant;
|
|
982
|
+
searchFilesDescriptionVariant?: SearchFilesDescriptionVariant;
|
|
983
|
+
searchFilesParameterVariant?: SearchFilesParameterVariant;
|
|
405
984
|
}) {
|
|
406
985
|
const declaredTools = new Set(args.toolNames ?? []);
|
|
407
986
|
return WORKSPACE_TOOL_NAMES
|
|
@@ -412,7 +991,16 @@ export function buildLocalWorkspaceOpenAiTools(args: {
|
|
|
412
991
|
}
|
|
413
992
|
return true;
|
|
414
993
|
})
|
|
415
|
-
.map((toolName) => buildWorkspaceToolDefinition(toolName
|
|
994
|
+
.map((toolName) => buildWorkspaceToolDefinition(toolName, {
|
|
995
|
+
listFilesDescriptionVariant: args.listFilesDescriptionVariant,
|
|
996
|
+
listFilesParameterVariant: args.listFilesParameterVariant,
|
|
997
|
+
readFileDescriptionVariant: args.readFileDescriptionVariant,
|
|
998
|
+
readFileParameterVariant: args.readFileParameterVariant,
|
|
999
|
+
globFilesDescriptionVariant: args.globFilesDescriptionVariant,
|
|
1000
|
+
globFilesParameterVariant: args.globFilesParameterVariant,
|
|
1001
|
+
searchFilesDescriptionVariant: args.searchFilesDescriptionVariant,
|
|
1002
|
+
searchFilesParameterVariant: args.searchFilesParameterVariant,
|
|
1003
|
+
}))
|
|
416
1004
|
.filter((tool): tool is OpenAiCompatibleTool => Boolean(tool));
|
|
417
1005
|
}
|
|
418
1006
|
|
|
@@ -477,6 +1065,96 @@ function readExpectedReplacementCount(args: WorkspaceFileArgs) {
|
|
|
477
1065
|
return value;
|
|
478
1066
|
}
|
|
479
1067
|
|
|
1068
|
+
function readPositiveIntegerArg(args: {
|
|
1069
|
+
value: unknown;
|
|
1070
|
+
name: string;
|
|
1071
|
+
max?: number;
|
|
1072
|
+
}) {
|
|
1073
|
+
if (args.value === undefined) return undefined;
|
|
1074
|
+
const value = Number(args.value);
|
|
1075
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
1076
|
+
throw new Error(`${args.name} must be a positive integer.`);
|
|
1077
|
+
}
|
|
1078
|
+
return args.max ? Math.min(value, args.max) : value;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
function readFileSliceArgs(args: WorkspaceFileArgs) {
|
|
1082
|
+
const startLine = readPositiveIntegerArg({ value: args.startLine, name: "startLine" });
|
|
1083
|
+
const endLine = readPositiveIntegerArg({ value: args.endLine, name: "endLine" });
|
|
1084
|
+
const maxLines = readPositiveIntegerArg({ value: args.maxLines, name: "maxLines", max: 2000 });
|
|
1085
|
+
const tailLines = readPositiveIntegerArg({ value: args.tailLines, name: "tailLines", max: 2000 });
|
|
1086
|
+
if (tailLines !== undefined && (startLine !== undefined || endLine !== undefined)) {
|
|
1087
|
+
throw new Error("tailLines cannot be combined with startLine or endLine.");
|
|
1088
|
+
}
|
|
1089
|
+
if (endLine !== undefined && startLine !== undefined && endLine < startLine) {
|
|
1090
|
+
throw new Error("endLine must be greater than or equal to startLine.");
|
|
1091
|
+
}
|
|
1092
|
+
return {
|
|
1093
|
+
startLine,
|
|
1094
|
+
endLine,
|
|
1095
|
+
maxLines,
|
|
1096
|
+
tailLines,
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
function splitTextLines(content: string) {
|
|
1101
|
+
const lines = content.split(/\r?\n/);
|
|
1102
|
+
if (lines.length > 1 && lines[lines.length - 1] === "") lines.pop();
|
|
1103
|
+
return lines;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
function sliceReadFileContent(args: {
|
|
1107
|
+
content: string;
|
|
1108
|
+
startLine?: number;
|
|
1109
|
+
endLine?: number;
|
|
1110
|
+
maxLines?: number;
|
|
1111
|
+
tailLines?: number;
|
|
1112
|
+
}) {
|
|
1113
|
+
const lines = splitTextLines(args.content);
|
|
1114
|
+
const totalLines = lines.length;
|
|
1115
|
+
if (
|
|
1116
|
+
args.startLine === undefined &&
|
|
1117
|
+
args.endLine === undefined &&
|
|
1118
|
+
args.maxLines === undefined &&
|
|
1119
|
+
args.tailLines === undefined
|
|
1120
|
+
) {
|
|
1121
|
+
return {
|
|
1122
|
+
content: args.content,
|
|
1123
|
+
startLine: 1,
|
|
1124
|
+
endLine: totalLines,
|
|
1125
|
+
totalLines,
|
|
1126
|
+
truncated: false,
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
if (args.tailLines !== undefined) {
|
|
1130
|
+
const startIndex = Math.max(0, totalLines - args.tailLines);
|
|
1131
|
+
const selected = lines.slice(startIndex);
|
|
1132
|
+
return {
|
|
1133
|
+
content: selected.join("\n"),
|
|
1134
|
+
startLine: startIndex + 1,
|
|
1135
|
+
endLine: startIndex + selected.length,
|
|
1136
|
+
totalLines,
|
|
1137
|
+
truncated: startIndex > 0,
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
const startLine = args.startLine ?? 1;
|
|
1141
|
+
let endLine = args.endLine ?? totalLines;
|
|
1142
|
+
if (args.maxLines !== undefined) {
|
|
1143
|
+
endLine = Math.min(endLine, startLine + args.maxLines - 1);
|
|
1144
|
+
}
|
|
1145
|
+
const startIndex = Math.min(Math.max(startLine - 1, 0), totalLines);
|
|
1146
|
+
const endIndex = Math.min(Math.max(endLine, 0), totalLines);
|
|
1147
|
+
const selected = lines.slice(startIndex, endIndex);
|
|
1148
|
+
const effectiveEndLine = selected.length ? startIndex + selected.length : startIndex;
|
|
1149
|
+
return {
|
|
1150
|
+
content: selected.join("\n"),
|
|
1151
|
+
startLine,
|
|
1152
|
+
endLine: effectiveEndLine,
|
|
1153
|
+
totalLines,
|
|
1154
|
+
truncated: startLine > 1 || effectiveEndLine < totalLines,
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
1157
|
+
|
|
480
1158
|
function countExactTextOccurrences(args: {
|
|
481
1159
|
content: string;
|
|
482
1160
|
oldText: string;
|
|
@@ -494,6 +1172,64 @@ function requireWorkspaceSearchQuery(args: WorkspaceFileArgs) {
|
|
|
494
1172
|
return query;
|
|
495
1173
|
}
|
|
496
1174
|
|
|
1175
|
+
function requireWorkspaceGlobPattern(args: WorkspaceFileArgs) {
|
|
1176
|
+
const pattern = typeof args.pattern === "string" && args.pattern.trim()
|
|
1177
|
+
? args.pattern.trim()
|
|
1178
|
+
: typeof args.glob === "string" && args.glob.trim()
|
|
1179
|
+
? args.glob.trim()
|
|
1180
|
+
: "";
|
|
1181
|
+
if (!pattern) throw new Error("globFiles requires a non-empty pattern.");
|
|
1182
|
+
return pattern;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
function readWorkspaceMaxResults(args: WorkspaceFileArgs) {
|
|
1186
|
+
if (args.maxResults === undefined) return undefined;
|
|
1187
|
+
const value = Number(args.maxResults);
|
|
1188
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
1189
|
+
throw new Error("maxResults must be a positive integer.");
|
|
1190
|
+
}
|
|
1191
|
+
return Math.min(value, 500);
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
function readWorkspaceMaxDepth(args: WorkspaceFileArgs) {
|
|
1195
|
+
if (args.maxDepth === undefined) return 1;
|
|
1196
|
+
const value = Number(args.maxDepth);
|
|
1197
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
1198
|
+
throw new Error("maxDepth must be a positive integer.");
|
|
1199
|
+
}
|
|
1200
|
+
return Math.min(value, 10);
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
function readWorkspaceEntryType(args: WorkspaceFileArgs) {
|
|
1204
|
+
if (args.entryType === undefined) return "all";
|
|
1205
|
+
if (args.entryType === "all" || args.entryType === "files" || args.entryType === "directories") {
|
|
1206
|
+
return args.entryType;
|
|
1207
|
+
}
|
|
1208
|
+
throw new Error("entryType must be one of all, files, or directories.");
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
function readWorkspaceContextLines(args: WorkspaceFileArgs) {
|
|
1212
|
+
if (args.contextLines === undefined) return undefined;
|
|
1213
|
+
const value = Number(args.contextLines);
|
|
1214
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
1215
|
+
throw new Error("contextLines must be a non-negative integer.");
|
|
1216
|
+
}
|
|
1217
|
+
return Math.min(value, 20);
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
function readWorkspaceExcludeGlobs(args: WorkspaceFileArgs) {
|
|
1221
|
+
if (args.exclude === undefined) return [];
|
|
1222
|
+
if (typeof args.exclude === "string" && args.exclude.trim()) {
|
|
1223
|
+
return [args.exclude.trim()];
|
|
1224
|
+
}
|
|
1225
|
+
if (!Array.isArray(args.exclude)) {
|
|
1226
|
+
throw new Error("exclude must be a glob string or an array of glob strings.");
|
|
1227
|
+
}
|
|
1228
|
+
return args.exclude.flatMap((item) =>
|
|
1229
|
+
typeof item === "string" && item.trim() ? [item.trim()] : []
|
|
1230
|
+
);
|
|
1231
|
+
}
|
|
1232
|
+
|
|
497
1233
|
function requireShellCommand(args: WorkspaceFileArgs, toolName: string) {
|
|
498
1234
|
const command = typeof args.cmd === "string"
|
|
499
1235
|
? args.cmd.trim()
|
|
@@ -726,6 +1462,66 @@ async function runWorkspaceCommand(args: {
|
|
|
726
1462
|
};
|
|
727
1463
|
}
|
|
728
1464
|
|
|
1465
|
+
async function runWorkspaceCommandLimitedLines(args: {
|
|
1466
|
+
workspaceRoot: string;
|
|
1467
|
+
command: string[];
|
|
1468
|
+
maxLines: number;
|
|
1469
|
+
commandPrefix?: string[];
|
|
1470
|
+
}) {
|
|
1471
|
+
const command = [
|
|
1472
|
+
...(args.commandPrefix ?? []),
|
|
1473
|
+
...args.command,
|
|
1474
|
+
];
|
|
1475
|
+
const proc = Bun.spawn(command, {
|
|
1476
|
+
cwd: resolve(args.workspaceRoot),
|
|
1477
|
+
stdout: "pipe",
|
|
1478
|
+
stderr: "pipe",
|
|
1479
|
+
stdin: "ignore",
|
|
1480
|
+
detached: false,
|
|
1481
|
+
});
|
|
1482
|
+
const stderrPromise = new Response(proc.stderr).text();
|
|
1483
|
+
const reader = proc.stdout.getReader();
|
|
1484
|
+
const decoder = new TextDecoder();
|
|
1485
|
+
const lines: string[] = [];
|
|
1486
|
+
let pending = "";
|
|
1487
|
+
let limitedByMaxResults = false;
|
|
1488
|
+
try {
|
|
1489
|
+
while (lines.length < args.maxLines) {
|
|
1490
|
+
const chunk = await reader.read();
|
|
1491
|
+
if (chunk.done) break;
|
|
1492
|
+
pending += decoder.decode(chunk.value, { stream: true });
|
|
1493
|
+
const parts = pending.split(/\r?\n/);
|
|
1494
|
+
pending = parts.pop() ?? "";
|
|
1495
|
+
for (const part of parts) {
|
|
1496
|
+
if (!part) continue;
|
|
1497
|
+
lines.push(part);
|
|
1498
|
+
if (lines.length >= args.maxLines) {
|
|
1499
|
+
limitedByMaxResults = true;
|
|
1500
|
+
break;
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
if (!limitedByMaxResults && pending.trim()) lines.push(pending);
|
|
1505
|
+
} finally {
|
|
1506
|
+
reader.releaseLock();
|
|
1507
|
+
}
|
|
1508
|
+
if (limitedByMaxResults) {
|
|
1509
|
+
try {
|
|
1510
|
+
proc.kill("SIGTERM");
|
|
1511
|
+
} catch {
|
|
1512
|
+
// The command may already have exited.
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
const exitCode = limitedByMaxResults ? 0 : Number(await proc.exited);
|
|
1516
|
+
const stderr = await stderrPromise;
|
|
1517
|
+
return {
|
|
1518
|
+
stdout: lines.join("\n"),
|
|
1519
|
+
stderr,
|
|
1520
|
+
exitCode,
|
|
1521
|
+
limitedByMaxResults,
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
|
|
729
1525
|
export function resolveLocalWorkspaceToolPath(args: {
|
|
730
1526
|
workspaceRoot: string;
|
|
731
1527
|
requestedPath: string;
|
|
@@ -744,19 +1540,33 @@ async function readFileTool(args: {
|
|
|
744
1540
|
}): Promise<AgentRuntimeToolResult> {
|
|
745
1541
|
const parsed = parseWorkspaceToolArguments(args.call.arguments);
|
|
746
1542
|
const requestedPath = requireWorkspaceToolPath(parsed);
|
|
1543
|
+
const sliceArgs = readFileSliceArgs(parsed);
|
|
747
1544
|
const absolutePath = resolveLocalWorkspaceToolPath({
|
|
748
1545
|
workspaceRoot: args.workspaceRoot,
|
|
749
1546
|
requestedPath,
|
|
750
1547
|
});
|
|
751
1548
|
const content = await readFile(absolutePath, "utf8");
|
|
752
|
-
|
|
1549
|
+
const activity = extractActivity(parsed);
|
|
1550
|
+
const sliced = sliceReadFileContent({
|
|
753
1551
|
content,
|
|
1552
|
+
...sliceArgs,
|
|
1553
|
+
});
|
|
1554
|
+
return {
|
|
1555
|
+
content: sliced.content,
|
|
754
1556
|
metadata: {
|
|
755
1557
|
path: normalizeWorkspaceRelativePath({
|
|
756
1558
|
workspaceRoot: resolve(args.workspaceRoot),
|
|
757
1559
|
targetPath: absolutePath,
|
|
758
1560
|
}),
|
|
759
|
-
bytes: Buffer.byteLength(content),
|
|
1561
|
+
bytes: Buffer.byteLength(sliced.content),
|
|
1562
|
+
totalBytes: Buffer.byteLength(content),
|
|
1563
|
+
startLine: sliced.startLine,
|
|
1564
|
+
endLine: sliced.endLine,
|
|
1565
|
+
totalLines: sliced.totalLines,
|
|
1566
|
+
truncated: sliced.truncated,
|
|
1567
|
+
...(sliceArgs.maxLines ? { maxLines: sliceArgs.maxLines } : {}),
|
|
1568
|
+
...(sliceArgs.tailLines ? { tailLines: sliceArgs.tailLines } : {}),
|
|
1569
|
+
...(activity ? { activity } : {}),
|
|
760
1570
|
},
|
|
761
1571
|
};
|
|
762
1572
|
}
|
|
@@ -783,11 +1593,13 @@ async function writeFileTool(args: {
|
|
|
783
1593
|
workspaceRoot: resolve(args.workspaceRoot),
|
|
784
1594
|
targetPath: absolutePath,
|
|
785
1595
|
});
|
|
1596
|
+
const activity = extractActivity(parsed);
|
|
786
1597
|
return {
|
|
787
1598
|
content: `wrote ${relativePath}`,
|
|
788
1599
|
metadata: {
|
|
789
1600
|
path: relativePath,
|
|
790
1601
|
bytes: Buffer.byteLength(content),
|
|
1602
|
+
...(activity ? { activity } : {}),
|
|
791
1603
|
},
|
|
792
1604
|
};
|
|
793
1605
|
}
|
|
@@ -819,12 +1631,14 @@ async function editFileTool(args: {
|
|
|
819
1631
|
workspaceRoot: resolve(args.workspaceRoot),
|
|
820
1632
|
targetPath: absolutePath,
|
|
821
1633
|
});
|
|
1634
|
+
const activity = extractActivity(parsed);
|
|
822
1635
|
return {
|
|
823
1636
|
content: `replaced ${replacementCount} occurrence${replacementCount === 1 ? "" : "s"} in ${relativePath}`,
|
|
824
1637
|
metadata: {
|
|
825
1638
|
path: relativePath,
|
|
826
1639
|
replacements: replacementCount,
|
|
827
1640
|
bytes: Buffer.byteLength(nextContent),
|
|
1641
|
+
...(activity ? { activity } : {}),
|
|
828
1642
|
},
|
|
829
1643
|
};
|
|
830
1644
|
}
|
|
@@ -843,6 +1657,58 @@ async function formatWorkspaceDirEntry(args: {
|
|
|
843
1657
|
return info.isDirectory() ? `${relativePath}/` : relativePath;
|
|
844
1658
|
}
|
|
845
1659
|
|
|
1660
|
+
async function listWorkspaceEntries(args: {
|
|
1661
|
+
workspaceRoot: string;
|
|
1662
|
+
dirPath: string;
|
|
1663
|
+
maxDepth: number;
|
|
1664
|
+
entryType: "all" | "files" | "directories";
|
|
1665
|
+
maxResults?: number;
|
|
1666
|
+
}) {
|
|
1667
|
+
const entries: string[] = [];
|
|
1668
|
+
let truncated = false;
|
|
1669
|
+
let visitedEntries = 0;
|
|
1670
|
+
let limitedByMaxDepth = false;
|
|
1671
|
+
const visit = async (dirPath: string, depth: number) => {
|
|
1672
|
+
if (truncated) return;
|
|
1673
|
+
const names = (await readdir(dirPath)).sort((left, right) => left.localeCompare(right));
|
|
1674
|
+
for (const name of names) {
|
|
1675
|
+
if (truncated) break;
|
|
1676
|
+
const absolutePath = resolve(dirPath, name);
|
|
1677
|
+
const info = await stat(absolutePath);
|
|
1678
|
+
visitedEntries += 1;
|
|
1679
|
+
const relativePath = normalizeWorkspaceRelativePath({
|
|
1680
|
+
workspaceRoot: resolve(args.workspaceRoot),
|
|
1681
|
+
targetPath: absolutePath,
|
|
1682
|
+
});
|
|
1683
|
+
const isDirectory = info.isDirectory();
|
|
1684
|
+
if (
|
|
1685
|
+
args.entryType === "all" ||
|
|
1686
|
+
(args.entryType === "files" && !isDirectory) ||
|
|
1687
|
+
(args.entryType === "directories" && isDirectory)
|
|
1688
|
+
) {
|
|
1689
|
+
entries.push(isDirectory ? `${relativePath}/` : relativePath);
|
|
1690
|
+
if (args.maxResults && entries.length >= args.maxResults) {
|
|
1691
|
+
truncated = true;
|
|
1692
|
+
break;
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
if (isDirectory && depth < args.maxDepth) {
|
|
1696
|
+
await visit(absolutePath, depth + 1);
|
|
1697
|
+
} else if (isDirectory && depth >= args.maxDepth && !limitedByMaxDepth) {
|
|
1698
|
+
const childNames = await readdir(absolutePath);
|
|
1699
|
+
limitedByMaxDepth = childNames.length > 0;
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
};
|
|
1703
|
+
await visit(args.dirPath, 1);
|
|
1704
|
+
return {
|
|
1705
|
+
entries,
|
|
1706
|
+
truncated,
|
|
1707
|
+
visitedEntries,
|
|
1708
|
+
limitedByMaxDepth,
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1711
|
+
|
|
846
1712
|
async function listFilesTool(args: {
|
|
847
1713
|
call: AgentRuntimeToolCallInput;
|
|
848
1714
|
workspaceRoot: string;
|
|
@@ -851,21 +1717,32 @@ async function listFilesTool(args: {
|
|
|
851
1717
|
const requestedPath = typeof parsed.path === "string" && parsed.path.trim()
|
|
852
1718
|
? parsed.path.trim()
|
|
853
1719
|
: ".";
|
|
1720
|
+
const maxDepth = readWorkspaceMaxDepth(parsed);
|
|
1721
|
+
const maxResults = readWorkspaceMaxResults(parsed);
|
|
1722
|
+
const entryType = readWorkspaceEntryType(parsed);
|
|
854
1723
|
const dirPath = resolveLocalWorkspaceToolPath({
|
|
855
1724
|
workspaceRoot: args.workspaceRoot,
|
|
856
1725
|
requestedPath,
|
|
857
1726
|
});
|
|
858
|
-
const
|
|
859
|
-
const entries = await Promise.all(names.map((name) => formatWorkspaceDirEntry({
|
|
1727
|
+
const listed = await listWorkspaceEntries({
|
|
860
1728
|
workspaceRoot: args.workspaceRoot,
|
|
861
1729
|
dirPath,
|
|
862
|
-
|
|
863
|
-
|
|
1730
|
+
maxDepth,
|
|
1731
|
+
maxResults,
|
|
1732
|
+
entryType,
|
|
1733
|
+
});
|
|
864
1734
|
return {
|
|
865
|
-
content: entries.join("\n"),
|
|
1735
|
+
content: listed.entries.join("\n"),
|
|
866
1736
|
metadata: {
|
|
867
1737
|
path: requestedPath,
|
|
868
|
-
count: entries.length,
|
|
1738
|
+
count: listed.entries.length,
|
|
1739
|
+
maxDepth,
|
|
1740
|
+
entryType,
|
|
1741
|
+
truncated: listed.truncated,
|
|
1742
|
+
limitedByMaxResults: listed.truncated,
|
|
1743
|
+
limitedByMaxDepth: listed.limitedByMaxDepth,
|
|
1744
|
+
visitedEntries: listed.visitedEntries,
|
|
1745
|
+
...(maxResults ? { maxResults } : {}),
|
|
869
1746
|
},
|
|
870
1747
|
};
|
|
871
1748
|
}
|
|
@@ -876,6 +1753,11 @@ async function searchFilesTool(args: {
|
|
|
876
1753
|
}): Promise<AgentRuntimeToolResult> {
|
|
877
1754
|
const parsed = parseWorkspaceToolArguments(args.call.arguments);
|
|
878
1755
|
const query = requireWorkspaceSearchQuery(parsed);
|
|
1756
|
+
const exclude = readWorkspaceExcludeGlobs(parsed);
|
|
1757
|
+
const maxResults = readWorkspaceMaxResults(parsed);
|
|
1758
|
+
const contextLines = readWorkspaceContextLines(parsed);
|
|
1759
|
+
const literal = parsed.literal === true;
|
|
1760
|
+
const caseSensitive = parsed.caseSensitive === false ? false : true;
|
|
879
1761
|
const requestedPath = typeof parsed.path === "string" && parsed.path.trim()
|
|
880
1762
|
? parsed.path.trim()
|
|
881
1763
|
: ".";
|
|
@@ -888,47 +1770,336 @@ async function searchFilesTool(args: {
|
|
|
888
1770
|
workspaceRoot: resolve(args.workspaceRoot),
|
|
889
1771
|
targetPath: searchPath,
|
|
890
1772
|
});
|
|
1773
|
+
const rgCommand = [
|
|
1774
|
+
"rg",
|
|
1775
|
+
"--line-number",
|
|
1776
|
+
"--no-heading",
|
|
1777
|
+
"--hidden",
|
|
1778
|
+
...(literal ? ["--fixed-strings"] : []),
|
|
1779
|
+
...(caseSensitive ? [] : ["--ignore-case"]),
|
|
1780
|
+
...(contextLines !== undefined ? ["--context", String(contextLines)] : []),
|
|
1781
|
+
...(includeIgnored ? ["--no-ignore"] : []),
|
|
1782
|
+
"--glob",
|
|
1783
|
+
"!node_modules",
|
|
1784
|
+
"--glob",
|
|
1785
|
+
"!.git",
|
|
1786
|
+
...exclude.flatMap((excludePattern) => ["--glob", `!${excludePattern}`]),
|
|
1787
|
+
"--",
|
|
1788
|
+
query,
|
|
1789
|
+
relativeSearchPath,
|
|
1790
|
+
];
|
|
1791
|
+
const grepCommand = [
|
|
1792
|
+
"grep",
|
|
1793
|
+
"-R",
|
|
1794
|
+
"-n",
|
|
1795
|
+
"-I",
|
|
1796
|
+
...(literal ? ["-F"] : []),
|
|
1797
|
+
...(caseSensitive ? [] : ["-i"]),
|
|
1798
|
+
...(contextLines !== undefined ? ["-C", String(contextLines)] : []),
|
|
1799
|
+
"--exclude-dir=node_modules",
|
|
1800
|
+
"--exclude-dir=.git",
|
|
1801
|
+
...exclude.flatMap((excludePattern) => ["--exclude", excludePattern]),
|
|
1802
|
+
query,
|
|
1803
|
+
relativeSearchPath,
|
|
1804
|
+
];
|
|
891
1805
|
const result = await (async () => {
|
|
892
1806
|
try {
|
|
893
|
-
return
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
"--glob",
|
|
904
|
-
"!.git",
|
|
905
|
-
query,
|
|
906
|
-
relativeSearchPath,
|
|
907
|
-
],
|
|
908
|
-
});
|
|
1807
|
+
return maxResults && !contextLines
|
|
1808
|
+
? await runWorkspaceCommandLimitedLines({
|
|
1809
|
+
workspaceRoot: args.workspaceRoot,
|
|
1810
|
+
command: rgCommand,
|
|
1811
|
+
maxLines: maxResults,
|
|
1812
|
+
})
|
|
1813
|
+
: await runWorkspaceCommand({
|
|
1814
|
+
workspaceRoot: args.workspaceRoot,
|
|
1815
|
+
command: rgCommand,
|
|
1816
|
+
});
|
|
909
1817
|
} catch {
|
|
910
|
-
return
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
relativeSearchPath,
|
|
921
|
-
],
|
|
922
|
-
});
|
|
1818
|
+
return maxResults && !contextLines
|
|
1819
|
+
? await runWorkspaceCommandLimitedLines({
|
|
1820
|
+
workspaceRoot: args.workspaceRoot,
|
|
1821
|
+
command: grepCommand,
|
|
1822
|
+
maxLines: maxResults,
|
|
1823
|
+
})
|
|
1824
|
+
: await runWorkspaceCommand({
|
|
1825
|
+
workspaceRoot: args.workspaceRoot,
|
|
1826
|
+
command: grepCommand,
|
|
1827
|
+
});
|
|
923
1828
|
}
|
|
924
1829
|
})();
|
|
1830
|
+
let outputLines = result.stdout
|
|
1831
|
+
.split(/\r?\n/)
|
|
1832
|
+
.map((line) => line.trimEnd().replace(/^\.\//, ""))
|
|
1833
|
+
.filter(Boolean);
|
|
1834
|
+
let limitedByMaxResults = "limitedByMaxResults" in result && result.limitedByMaxResults === true;
|
|
1835
|
+
if (outputLines.length === 0 && result.exitCode === 0 && (includeIgnored || !(await hasRootGitignore(args.workspaceRoot)))) {
|
|
1836
|
+
const fallback = await scanWorkspaceTextMatches({
|
|
1837
|
+
workspaceRoot: args.workspaceRoot,
|
|
1838
|
+
relativeSearchPath,
|
|
1839
|
+
query,
|
|
1840
|
+
exclude,
|
|
1841
|
+
maxResults,
|
|
1842
|
+
literal,
|
|
1843
|
+
caseSensitive,
|
|
1844
|
+
contextLines,
|
|
1845
|
+
});
|
|
1846
|
+
outputLines = fallback.lines;
|
|
1847
|
+
limitedByMaxResults = fallback.limitedByMaxResults;
|
|
1848
|
+
}
|
|
1849
|
+
if (contextLines && outputLines.length > 0) {
|
|
1850
|
+
const limited = limitSearchOutputByMatches(outputLines, maxResults);
|
|
1851
|
+
outputLines = limited.lines;
|
|
1852
|
+
limitedByMaxResults = limitedByMaxResults || limited.truncated;
|
|
1853
|
+
}
|
|
1854
|
+
const matchLines = outputLines.filter((line) => /:\d+:/.test(line));
|
|
1855
|
+
const matchedFiles = Array.from(new Set(matchLines.flatMap((line) => {
|
|
1856
|
+
const match = line.match(/^(.*?):\d+:/);
|
|
1857
|
+
return match?.[1] ? [match[1]] : [];
|
|
1858
|
+
})));
|
|
925
1859
|
return {
|
|
926
|
-
content:
|
|
1860
|
+
content: outputLines.join("\n"),
|
|
927
1861
|
metadata: {
|
|
928
1862
|
query,
|
|
929
1863
|
path: requestedPath,
|
|
1864
|
+
searchedPath: relativeSearchPath,
|
|
1865
|
+
exclude,
|
|
930
1866
|
includeIgnored,
|
|
1867
|
+
literal,
|
|
1868
|
+
caseSensitive,
|
|
1869
|
+
...(contextLines === undefined ? {} : { contextLines }),
|
|
1870
|
+
count: outputLines.length,
|
|
1871
|
+
matchCount: matchLines.length,
|
|
1872
|
+
matchedFiles,
|
|
1873
|
+
truncated: limitedByMaxResults,
|
|
1874
|
+
limitedByMaxResults,
|
|
1875
|
+
...(maxResults ? { maxResults } : {}),
|
|
931
1876
|
exitCode: result.exitCode,
|
|
1877
|
+
...(extractActivity(parsed) ? { activity: extractActivity(parsed) } : {}),
|
|
1878
|
+
},
|
|
1879
|
+
};
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
async function hasRootGitignore(workspaceRoot: string) {
|
|
1883
|
+
try {
|
|
1884
|
+
await stat(resolve(workspaceRoot, ".gitignore"));
|
|
1885
|
+
return true;
|
|
1886
|
+
} catch {
|
|
1887
|
+
return false;
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
async function readRootGitignorePatterns(workspaceRoot: string) {
|
|
1892
|
+
try {
|
|
1893
|
+
const content = await readFile(resolve(workspaceRoot, ".gitignore"), "utf8");
|
|
1894
|
+
return content
|
|
1895
|
+
.split(/\r?\n/)
|
|
1896
|
+
.map((line) => line.trim())
|
|
1897
|
+
.filter((line) => line && !line.startsWith("#") && !line.startsWith("!"))
|
|
1898
|
+
.map((line) => {
|
|
1899
|
+
const unanchored = line.replace(/^\//, "");
|
|
1900
|
+
return unanchored.endsWith("/") ? `${unanchored}**` : unanchored;
|
|
1901
|
+
});
|
|
1902
|
+
} catch {
|
|
1903
|
+
return [];
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
async function filterRootGitignoredFiles(args: {
|
|
1908
|
+
workspaceRoot: string;
|
|
1909
|
+
files: string[];
|
|
1910
|
+
includeIgnored: boolean;
|
|
1911
|
+
}) {
|
|
1912
|
+
if (args.includeIgnored) return args.files;
|
|
1913
|
+
const patterns = await readRootGitignorePatterns(args.workspaceRoot);
|
|
1914
|
+
if (patterns.length === 0) return args.files;
|
|
1915
|
+
const globs = patterns.map((pattern) => new Bun.Glob(pattern));
|
|
1916
|
+
return args.files.filter((file) => !globs.some((glob) => glob.match(file)));
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
function scanWorkspaceGlobFiles(args: {
|
|
1920
|
+
workspaceRoot: string;
|
|
1921
|
+
pattern: string;
|
|
1922
|
+
relativeSearchPath: string;
|
|
1923
|
+
exclude: string[];
|
|
1924
|
+
}) {
|
|
1925
|
+
const pathPrefix = args.relativeSearchPath === "."
|
|
1926
|
+
? ""
|
|
1927
|
+
: `${args.relativeSearchPath.replace(/\/+$/, "")}/`;
|
|
1928
|
+
return Array.from(new Bun.Glob(args.pattern).scanSync({
|
|
1929
|
+
cwd: args.workspaceRoot,
|
|
1930
|
+
dot: true,
|
|
1931
|
+
onlyFiles: true,
|
|
1932
|
+
}))
|
|
1933
|
+
.map((line) => line.trim().replace(/^\.\//, ""))
|
|
1934
|
+
.filter(Boolean)
|
|
1935
|
+
.filter((line) => !line.startsWith(".git/") && !line.startsWith("node_modules/"))
|
|
1936
|
+
.filter((line) => !pathPrefix || line === args.relativeSearchPath || line.startsWith(pathPrefix))
|
|
1937
|
+
.filter((line) => !args.exclude.some((pattern) => new Bun.Glob(pattern).match(line)))
|
|
1938
|
+
.sort((left, right) => left.localeCompare(right));
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
function limitSearchOutputByMatches(lines: string[], maxResults?: number) {
|
|
1942
|
+
if (!maxResults) return { lines, truncated: false };
|
|
1943
|
+
const limited: string[] = [];
|
|
1944
|
+
let matches = 0;
|
|
1945
|
+
for (const line of lines) {
|
|
1946
|
+
if (/:\d+:/.test(line)) {
|
|
1947
|
+
if (matches >= maxResults) {
|
|
1948
|
+
return { lines: limited, truncated: true };
|
|
1949
|
+
}
|
|
1950
|
+
matches += 1;
|
|
1951
|
+
}
|
|
1952
|
+
limited.push(line);
|
|
1953
|
+
}
|
|
1954
|
+
return { lines: limited, truncated: false };
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
async function scanWorkspaceTextMatches(args: {
|
|
1958
|
+
workspaceRoot: string;
|
|
1959
|
+
relativeSearchPath: string;
|
|
1960
|
+
query: string;
|
|
1961
|
+
exclude: string[];
|
|
1962
|
+
maxResults?: number;
|
|
1963
|
+
literal: boolean;
|
|
1964
|
+
caseSensitive: boolean;
|
|
1965
|
+
contextLines?: number;
|
|
1966
|
+
}) {
|
|
1967
|
+
const files = scanWorkspaceGlobFiles({
|
|
1968
|
+
workspaceRoot: args.workspaceRoot,
|
|
1969
|
+
pattern: "**/*",
|
|
1970
|
+
relativeSearchPath: args.relativeSearchPath,
|
|
1971
|
+
exclude: args.exclude,
|
|
1972
|
+
});
|
|
1973
|
+
const results: string[] = [];
|
|
1974
|
+
const seenContext = new Set<string>();
|
|
1975
|
+
let matchCount = 0;
|
|
1976
|
+
const literalQuery = args.caseSensitive ? args.query : args.query.toLowerCase();
|
|
1977
|
+
const regex = args.literal
|
|
1978
|
+
? undefined
|
|
1979
|
+
: new RegExp(args.query, args.caseSensitive ? "" : "i");
|
|
1980
|
+
const pushLine = (file: string, lineNumber: number, text: string, isMatch: boolean) => {
|
|
1981
|
+
const separator = isMatch ? ":" : "-";
|
|
1982
|
+
const key = `${file}:${lineNumber}:${separator}`;
|
|
1983
|
+
if (seenContext.has(key)) return;
|
|
1984
|
+
seenContext.add(key);
|
|
1985
|
+
results.push(`${file}${separator}${lineNumber}${separator}${text}`);
|
|
1986
|
+
};
|
|
1987
|
+
for (const file of files) {
|
|
1988
|
+
let content = "";
|
|
1989
|
+
try {
|
|
1990
|
+
content = await readFile(resolve(args.workspaceRoot, file), "utf8");
|
|
1991
|
+
} catch {
|
|
1992
|
+
continue;
|
|
1993
|
+
}
|
|
1994
|
+
if (content.includes("\u0000")) continue;
|
|
1995
|
+
const lines = content.split(/\r?\n/);
|
|
1996
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
1997
|
+
const line = lines[index] ?? "";
|
|
1998
|
+
const haystack = args.caseSensitive ? line : line.toLowerCase();
|
|
1999
|
+
const matched = args.literal
|
|
2000
|
+
? haystack.includes(literalQuery)
|
|
2001
|
+
: regex?.test(line) === true;
|
|
2002
|
+
if (!matched) continue;
|
|
2003
|
+
matchCount += 1;
|
|
2004
|
+
const contextLines = args.contextLines ?? 0;
|
|
2005
|
+
const start = Math.max(0, index - contextLines);
|
|
2006
|
+
const end = Math.min(lines.length - 1, index + contextLines);
|
|
2007
|
+
for (let contextIndex = start; contextIndex <= end; contextIndex += 1) {
|
|
2008
|
+
pushLine(file, contextIndex + 1, lines[contextIndex] ?? "", contextIndex === index);
|
|
2009
|
+
}
|
|
2010
|
+
if (args.maxResults && matchCount >= args.maxResults) {
|
|
2011
|
+
return {
|
|
2012
|
+
lines: results,
|
|
2013
|
+
limitedByMaxResults: true,
|
|
2014
|
+
};
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
return {
|
|
2019
|
+
lines: results,
|
|
2020
|
+
limitedByMaxResults: false,
|
|
2021
|
+
};
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
async function globFilesTool(args: {
|
|
2025
|
+
call: AgentRuntimeToolCallInput;
|
|
2026
|
+
workspaceRoot: string;
|
|
2027
|
+
}): Promise<AgentRuntimeToolResult> {
|
|
2028
|
+
const parsed = parseWorkspaceToolArguments(args.call.arguments);
|
|
2029
|
+
const pattern = requireWorkspaceGlobPattern(parsed);
|
|
2030
|
+
const exclude = readWorkspaceExcludeGlobs(parsed);
|
|
2031
|
+
const maxResults = readWorkspaceMaxResults(parsed);
|
|
2032
|
+
const requestedPath = typeof parsed.path === "string" && parsed.path.trim()
|
|
2033
|
+
? parsed.path.trim()
|
|
2034
|
+
: ".";
|
|
2035
|
+
const includeIgnored = parsed.includeIgnored === true;
|
|
2036
|
+
const searchPath = resolveLocalWorkspaceToolPath({
|
|
2037
|
+
workspaceRoot: args.workspaceRoot,
|
|
2038
|
+
requestedPath,
|
|
2039
|
+
});
|
|
2040
|
+
const relativeSearchPath = normalizeWorkspaceRelativePath({
|
|
2041
|
+
workspaceRoot: resolve(args.workspaceRoot),
|
|
2042
|
+
targetPath: searchPath,
|
|
2043
|
+
});
|
|
2044
|
+
const command = [
|
|
2045
|
+
"rg",
|
|
2046
|
+
"--files",
|
|
2047
|
+
"--hidden",
|
|
2048
|
+
...(includeIgnored ? ["--no-ignore"] : []),
|
|
2049
|
+
"--glob",
|
|
2050
|
+
pattern,
|
|
2051
|
+
"--glob",
|
|
2052
|
+
"!node_modules",
|
|
2053
|
+
"--glob",
|
|
2054
|
+
"!.git",
|
|
2055
|
+
...exclude.flatMap((excludePattern) => ["--glob", `!${excludePattern}`]),
|
|
2056
|
+
...(relativeSearchPath === "." ? [] : [relativeSearchPath]),
|
|
2057
|
+
];
|
|
2058
|
+
const result = await runWorkspaceCommand({
|
|
2059
|
+
workspaceRoot: args.workspaceRoot,
|
|
2060
|
+
command,
|
|
2061
|
+
});
|
|
2062
|
+
const pathPrefix = relativeSearchPath === "." ? "" : `${relativeSearchPath.replace(/\/+$/, "")}/`;
|
|
2063
|
+
let files = result.stdout
|
|
2064
|
+
.split(/\r?\n/)
|
|
2065
|
+
.map((line) => line.trim().replace(/^\.\//, ""))
|
|
2066
|
+
.filter(Boolean)
|
|
2067
|
+
.filter((line) => !pathPrefix || line === relativeSearchPath || line.startsWith(pathPrefix))
|
|
2068
|
+
.sort((left, right) => left.localeCompare(right));
|
|
2069
|
+
files = await filterRootGitignoredFiles({
|
|
2070
|
+
workspaceRoot: args.workspaceRoot,
|
|
2071
|
+
files,
|
|
2072
|
+
includeIgnored,
|
|
2073
|
+
});
|
|
2074
|
+
if (files.length === 0 && (includeIgnored || !(await hasRootGitignore(args.workspaceRoot)))) {
|
|
2075
|
+
files = scanWorkspaceGlobFiles({
|
|
2076
|
+
workspaceRoot: args.workspaceRoot,
|
|
2077
|
+
pattern,
|
|
2078
|
+
relativeSearchPath,
|
|
2079
|
+
exclude,
|
|
2080
|
+
});
|
|
2081
|
+
}
|
|
2082
|
+
const commandLimitedByMaxResults = result.limitedByMaxResults === true;
|
|
2083
|
+
const totalCount = commandLimitedByMaxResults ? undefined : files.length;
|
|
2084
|
+
const limitedFiles = maxResults ? files.slice(0, maxResults) : files;
|
|
2085
|
+
const limitedByMaxResults = commandLimitedByMaxResults || limitedFiles.length < (totalCount ?? limitedFiles.length);
|
|
2086
|
+
const activity = extractActivity(parsed);
|
|
2087
|
+
return {
|
|
2088
|
+
content: limitedFiles.join("\n"),
|
|
2089
|
+
metadata: {
|
|
2090
|
+
pattern,
|
|
2091
|
+
effectivePattern: pathPrefix ? `${pathPrefix}${pattern}` : pattern,
|
|
2092
|
+
path: requestedPath,
|
|
2093
|
+
searchedPath: relativeSearchPath,
|
|
2094
|
+
exclude,
|
|
2095
|
+
includeIgnored,
|
|
2096
|
+
count: limitedFiles.length,
|
|
2097
|
+
...(totalCount === undefined ? {} : { totalCount }),
|
|
2098
|
+
truncated: limitedByMaxResults,
|
|
2099
|
+
limitedByMaxResults,
|
|
2100
|
+
...(maxResults ? { maxResults } : {}),
|
|
2101
|
+
exitCode: result.exitCode,
|
|
2102
|
+
...(activity ? { activity } : {}),
|
|
932
2103
|
},
|
|
933
2104
|
};
|
|
934
2105
|
}
|
|
@@ -1062,9 +2233,14 @@ async function execShellTool(args: {
|
|
|
1062
2233
|
outputLimit: args.commandOutputLimit,
|
|
1063
2234
|
commandPrefix: args.commandPrefix,
|
|
1064
2235
|
});
|
|
2236
|
+
const activity = extractActivity(parsed);
|
|
1065
2237
|
return {
|
|
1066
2238
|
content: result.content,
|
|
1067
|
-
metadata: {
|
|
2239
|
+
metadata: {
|
|
2240
|
+
exitCode: result.exitCode,
|
|
2241
|
+
timedOut: result.timedOut,
|
|
2242
|
+
...(activity ? { activity } : {}),
|
|
2243
|
+
},
|
|
1068
2244
|
};
|
|
1069
2245
|
}
|
|
1070
2246
|
|
|
@@ -1074,6 +2250,10 @@ export function createLocalWorkspaceToolExecutors(args: LocalWorkspaceToolArgs)
|
|
|
1074
2250
|
call,
|
|
1075
2251
|
workspaceRoot: args.workspaceRoot,
|
|
1076
2252
|
}),
|
|
2253
|
+
globFiles: (call: AgentRuntimeToolCallInput) => globFilesTool({
|
|
2254
|
+
call,
|
|
2255
|
+
workspaceRoot: args.workspaceRoot,
|
|
2256
|
+
}),
|
|
1077
2257
|
listFiles: (call: AgentRuntimeToolCallInput) => listFilesTool({
|
|
1078
2258
|
call,
|
|
1079
2259
|
workspaceRoot: args.workspaceRoot,
|