nolo-cli 0.1.45 → 0.1.47
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 +272 -118
- package/index.js +73946 -0
- package/package.json +10 -635
- package/LICENSE +0 -21
- package/agent-runtime/agentConfigOptions.ts +0 -12
- package/agent-runtime/agentRecordConfig.ts +0 -101
- package/agent-runtime/agentRecordKeys.ts +0 -14
- package/agent-runtime/agentThread.ts +0 -222
- package/agent-runtime/agentThreadAdmission.ts +0 -74
- package/agent-runtime/dialogMessageRecord.ts +0 -16
- package/agent-runtime/dialogWritePlan.ts +0 -211
- package/agent-runtime/hostAdapter.ts +0 -81
- package/agent-runtime/hybridRecordStore.ts +0 -170
- package/agent-runtime/index.ts +0 -184
- package/agent-runtime/localDialogRead.ts +0 -23
- package/agent-runtime/localLoop.ts +0 -334
- package/agent-runtime/localToolPolicy.ts +0 -179
- package/agent-runtime/localWorkspaceTools.ts +0 -2310
- package/agent-runtime/noloWorkspaceTools.ts +0 -390
- package/agent-runtime/openAiCompatibleProvider.ts +0 -74
- package/agent-runtime/openAiCompatibleProviderConfig.ts +0 -27
- package/agent-runtime/platformChatProvider.ts +0 -229
- package/agent-runtime/providerResolution.ts +0 -302
- package/agent-runtime/runtimeDecision.ts +0 -70
- package/agent-runtime/runtimeFacts.ts +0 -40
- package/agent-runtime/runtimeToolPolicy.ts +0 -191
- package/agent-runtime/runtimeToolSurface.ts +0 -199
- package/agent-runtime/shellCommandPolicy.ts +0 -120
- package/agent-runtime/types.ts +0 -137
- package/agentAliases.ts +0 -40
- package/agentCommandSupport.ts +0 -81
- package/agentInternalCommandEntries.ts +0 -44
- package/agentListCommands.ts +0 -185
- package/agentListHelpers.ts +0 -242
- package/agentMachineCommands.ts +0 -426
- package/agentNameResolver.ts +0 -164
- package/agentPullCommand.ts +0 -142
- package/agentRecordCommands.ts +0 -224
- package/agentRecordHelpers.ts +0 -592
- package/agentRunCommand.ts +0 -568
- package/agentRuntimeCommands.ts +0 -7
- package/agentRuntimeLocal.ts +0 -45
- package/ai/agent/_executeModel.ts +0 -146
- package/ai/agent/agentSlice.ts +0 -557
- package/ai/agent/appWorkingMemory.ts +0 -126
- package/ai/agent/buildEditingContext.ts +0 -378
- package/ai/agent/buildSystemPrompt.ts +0 -599
- package/ai/agent/canvasEditingContext.ts +0 -49
- package/ai/agent/cleanAgentMessages.ts +0 -140
- package/ai/agent/cliChatClient.ts +0 -153
- package/ai/agent/cliExecutor.ts +0 -1465
- package/ai/agent/cliPrompt.ts +0 -10
- package/ai/agent/contextCompiler.ts +0 -107
- package/ai/agent/contextLayerContract.ts +0 -44
- package/ai/agent/createAgentSchema.ts +0 -272
- package/ai/agent/currentTimeContext.ts +0 -65
- package/ai/agent/executeToolCall.ts +0 -58
- package/ai/agent/fetchAgentContexts.ts +0 -42
- package/ai/agent/generatePrompt.ts +0 -3
- package/ai/agent/getFullChatContextKeys.ts +0 -168
- package/ai/agent/guidedCreation/capabilities.ts +0 -84
- package/ai/agent/guidedCreation/types.ts +0 -38
- package/ai/agent/isLiveAudioOnlyAgent.ts +0 -26
- package/ai/agent/liveAudioModel.ts +0 -2
- package/ai/agent/machineRunPermissions.ts +0 -113
- package/ai/agent/pageBuilderHandoffRules.ts +0 -75
- package/ai/agent/persistMessageWithFixedId.ts +0 -37
- package/ai/agent/planSlice.ts +0 -259
- package/ai/agent/referenceUtils.ts +0 -229
- package/ai/agent/runAgentBackground.ts +0 -310
- package/ai/agent/runAgentClientLoop.ts +0 -164
- package/ai/agent/runtimeGuidance.ts +0 -116
- package/ai/agent/serverOrigin.ts +0 -7
- package/ai/agent/startParallelAgentStreams.ts +0 -424
- package/ai/agent/startupProtocol.ts +0 -53
- package/ai/agent/streamAgentChatTurn.ts +0 -2135
- package/ai/agent/streamAgentChatTurnUtils.ts +0 -923
- package/ai/agent/toolFailureGuard.ts +0 -99
- package/ai/agent/types.ts +0 -73
- package/ai/agent/utils/imageOutput.ts +0 -37
- package/ai/agent/utils/publicImageAgentMode.ts +0 -26
- package/ai/agent.ts +0 -2
- package/ai/ai.locale.ts +0 -1165
- package/ai/chat/accumulateToolCallChunks.ts +0 -126
- package/ai/chat/fetchUtils.ts +0 -143
- package/ai/chat/inlineImageUrlsForCustomProvider.ts +0 -122
- package/ai/chat/parseApiError.ts +0 -67
- package/ai/chat/parseMultilineSSE.ts +0 -95
- package/ai/chat/sendOpenAICompletionsRequest.ts +0 -921
- package/ai/chat/sendOpenAIResponseRequest.ts +0 -720
- package/ai/chat/serverProxyRetry.ts +0 -102
- package/ai/chat/shouldUseServerProxy.ts +0 -18
- package/ai/chat/streamRetry.ts +0 -46
- package/ai/chat/updateTotalUsage.ts +0 -89
- package/ai/context/buildReferenceContext.ts +0 -437
- package/ai/context/retention.ts +0 -165
- package/ai/context/tokenUtils.ts +0 -78
- package/ai/index.ts +0 -1
- package/ai/llm/agentCapabilities.ts +0 -74
- package/ai/llm/deepinfra.ts +0 -79
- package/ai/llm/fireworks.ts +0 -68
- package/ai/llm/generateRequestBody.ts +0 -165
- package/ai/llm/getModelContextWindow.ts +0 -93
- package/ai/llm/getPricing.ts +0 -252
- package/ai/llm/imagePricing.ts +0 -66
- package/ai/llm/isResponseAPIModel.ts +0 -13
- package/ai/llm/kimi.ts +0 -22
- package/ai/llm/mimo.ts +0 -99
- package/ai/llm/mistral.ts +0 -21
- package/ai/llm/models.ts +0 -45
- package/ai/llm/openrouterModels.ts +0 -31
- package/ai/llm/providers.ts +0 -313
- package/ai/llm/reasoningModels.ts +0 -27
- package/ai/llm/types.ts +0 -82
- package/ai/llm/usageRequestOptions.ts +0 -59
- package/ai/memory/policy.ts +0 -49
- package/ai/memory/queryShared.ts +0 -167
- package/ai/memory/recentRelationshipRecap.ts +0 -251
- package/ai/memory/remember.ts +0 -111
- package/ai/memory/scope.ts +0 -74
- package/ai/memory/store.ts +0 -20
- package/ai/memory/storeShared.ts +0 -76
- package/ai/memory/types.ts +0 -52
- package/ai/memory/understanding.ts +0 -341
- package/ai/memory/understandingGreeting.ts +0 -264
- package/ai/policy/personalizationDialog.ts +0 -333
- package/ai/policy/runtimePolicy.ts +0 -526
- package/ai/policy/selfUpdateFields.ts +0 -48
- package/ai/policy/types.ts +0 -64
- package/ai/skills/referenceRuntime.ts +0 -274
- package/ai/skills/skillDiagnostics.ts +0 -251
- package/ai/skills/skillDocBuilder.ts +0 -139
- package/ai/skills/skillDocProtocol.ts +0 -526
- package/ai/skills/skillSummaryMarker.ts +0 -26
- package/ai/token/calculatePrice.ts +0 -576
- package/ai/token/missingUsageEstimate.ts +0 -42
- package/ai/token/normalizeUsage.ts +0 -124
- package/ai/token/openaiImageGenerationUsage.ts +0 -56
- package/ai/token/prepareTokenUsageData.ts +0 -93
- package/ai/token/resolveBillingTarget.ts +0 -52
- package/ai/token/saveTokenRecord.ts +0 -53
- package/ai/token/types.ts +0 -103
- package/ai/tools/agent/agentTools.ts +0 -214
- package/ai/tools/agent/agentUpdateShared.ts +0 -311
- package/ai/tools/agent/callAgentTool.ts +0 -139
- package/ai/tools/agent/createAgentTool.ts +0 -512
- package/ai/tools/agent/createDialogTool.ts +0 -69
- package/ai/tools/agent/createSkillAgentTool.ts +0 -62
- package/ai/tools/agent/parallelBudget.ts +0 -221
- package/ai/tools/agent/prepareAgentDraftTool.ts +0 -159
- package/ai/tools/agent/presets/appBuilderPreset.ts +0 -154
- package/ai/tools/agent/runLlmTool.ts +0 -96
- package/ai/tools/agent/runStreamingAgentTool.ts +0 -73
- package/ai/tools/agent/skillAgentArgs.ts +0 -106
- package/ai/tools/agent/startAgentDialogTool.ts +0 -54
- package/ai/tools/agent/streamParallelAgentsTool.ts +0 -123
- package/ai/tools/agent/updateAgentTool.ts +0 -96
- package/ai/tools/agent/updateSelfTool.ts +0 -113
- package/ai/tools/amazonProductScraperTool.ts +0 -86
- package/ai/tools/apifyActorClient.ts +0 -45
- package/ai/tools/appEditGuard.ts +0 -372
- package/ai/tools/appReadSnapshot.ts +0 -153
- package/ai/tools/appTools.ts +0 -1549
- package/ai/tools/applyEditTool.ts +0 -259
- package/ai/tools/applyLineEditsTool.ts +0 -315
- package/ai/tools/browserTools/click.ts +0 -33
- package/ai/tools/browserTools/closeSession.ts +0 -29
- package/ai/tools/browserTools/common.ts +0 -27
- package/ai/tools/browserTools/openSession.ts +0 -48
- package/ai/tools/browserTools/readContent.ts +0 -38
- package/ai/tools/browserTools/selectOption.ts +0 -46
- package/ai/tools/browserTools/typeText.ts +0 -42
- package/ai/tools/category/createCategoryTool.ts +0 -66
- package/ai/tools/category/queryContentsByCategoryTool.ts +0 -69
- package/ai/tools/category/updateContentCategoryTool.ts +0 -75
- package/ai/tools/cfBrowserTools.ts +0 -319
- package/ai/tools/cfSpeechToTextTool.ts +0 -49
- package/ai/tools/checkEnvTool.ts +0 -102
- package/ai/tools/cloudflareCrawlTool.ts +0 -289
- package/ai/tools/codeSearchTool.ts +0 -123
- package/ai/tools/codeTools.ts +0 -101
- package/ai/tools/convertMarxistsBookTool.ts +0 -103
- package/ai/tools/createDocTool.ts +0 -132
- package/ai/tools/createSkillDocTool.ts +0 -155
- package/ai/tools/createWorkflowTool.ts +0 -154
- package/ai/tools/delayTool.ts +0 -31
- package/ai/tools/deleteSpacesTool.ts +0 -325
- package/ai/tools/deleteSpacesToolModel.ts +0 -159
- package/ai/tools/devReloadUtils.ts +0 -29
- package/ai/tools/dialogMessageSearch.ts +0 -137
- package/ai/tools/doctorSkillTool.ts +0 -72
- package/ai/tools/ecommerceScraperTool.ts +0 -86
- package/ai/tools/emailTools.ts +0 -549
- package/ai/tools/evalSkillTool.ts +0 -92
- package/ai/tools/exaSearchTool.ts +0 -63
- package/ai/tools/execShellTool.ts +0 -266
- package/ai/tools/executeSqlTool.ts +0 -192
- package/ai/tools/fetchWebpageSupport.ts +0 -333
- package/ai/tools/fetchWebpageTool.ts +0 -97
- package/ai/tools/geminiImagePreviewTool.ts +0 -386
- package/ai/tools/generateDocxTool.ts +0 -215
- package/ai/tools/googleSearchScraperTool.ts +0 -106
- package/ai/tools/importDataTool.ts +0 -133
- package/ai/tools/importSkillTool.ts +0 -162
- package/ai/tools/index.ts +0 -2290
- package/ai/tools/jdProductScraperTool.ts +0 -821
- package/ai/tools/listUserSpacesTool.ts +0 -113
- package/ai/tools/localFilesTool.ts +0 -276
- package/ai/tools/marxistsOfflineBook.ts +0 -253
- package/ai/tools/modelUsageTools.ts +0 -206
- package/ai/tools/noloWorkspaceReadTools.ts +0 -467
- package/ai/tools/olmOcrTool.ts +0 -33
- package/ai/tools/openaiImageTool.ts +0 -267
- package/ai/tools/prepareTools.ts +0 -193
- package/ai/tools/readDocTool.ts +0 -88
- package/ai/tools/readFileTool.ts +0 -214
- package/ai/tools/readTool.ts +0 -194
- package/ai/tools/readXPostTool.ts +0 -229
- package/ai/tools/readXhsProfileTool.ts +0 -638
- package/ai/tools/rememberMemoryTool.ts +0 -84
- package/ai/tools/remotionVideoTool.ts +0 -151
- package/ai/tools/searchDialogMessagesTool.ts +0 -222
- package/ai/tools/searchWorkspaceTool.ts +0 -259
- package/ai/tools/skillFollowup.ts +0 -86
- package/ai/tools/surfWeatherTool.ts +0 -169
- package/ai/tools/table/addTableRowTool.ts +0 -217
- package/ai/tools/table/createTableTool.ts +0 -326
- package/ai/tools/table/rowTools.ts +0 -386
- package/ai/tools/table/schemaTools.ts +0 -244
- package/ai/tools/table/shareTableTool.ts +0 -148
- package/ai/tools/table/toolShared.ts +0 -129
- package/ai/tools/taobaoTmallProductScraperTool.ts +0 -49
- package/ai/tools/toolApiClient.ts +0 -279
- package/ai/tools/toolNameAliases.ts +0 -70
- package/ai/tools/toolPacks.ts +0 -19
- package/ai/tools/toolResultError.ts +0 -42
- package/ai/tools/toolRunSlice.ts +0 -303
- package/ai/tools/toolSchemaCompatibility.ts +0 -53
- package/ai/tools/uiAskChoiceTool.ts +0 -104
- package/ai/tools/updateContentTitleTool.ts +0 -84
- package/ai/tools/updateDocTool.ts +0 -105
- package/ai/tools/updateUserPreferenceProfileTool.ts +0 -153
- package/ai/tools/wereadGatewayTool.ts +0 -152
- package/ai/tools/whisperTool.ts +0 -76
- package/ai/tools/writeFileTool.ts +0 -213
- package/ai/tools/youtubeScraperTool.ts +0 -116
- package/ai/tools/ziweiChartTool.ts +0 -678
- package/ai/types.ts +0 -58
- package/ai/workflow/workflowExecutor.ts +0 -323
- package/ai/workflow/workflowSlice.ts +0 -73
- package/ai/workflow/workflowTypes.ts +0 -106
- package/app/actions/syncAppRecord.ts +0 -73
- package/app/constants/animationSets.ts +0 -21
- package/app/constants/appEditor.ts +0 -99
- package/app/favorite/deletedFavoriteProjection.ts +0 -31
- package/app/favorite/favoriteSlice.ts +0 -690
- package/app/favorite/type.ts +0 -43
- package/app/hooks/deleteDbKey.ts +0 -254
- package/app/i18n/client.ts +0 -13
- package/app/i18n/i18n.base.ts +0 -14
- package/app/i18n/i18n.config.ts +0 -77
- package/app/i18n/index.ts +0 -15
- package/app/i18n/mapLanguage.ts +0 -11
- package/app/i18n/translations/appBuilder.locale.ts +0 -453
- package/app/i18n/translations/error.locale.ts +0 -246
- package/app/i18n/translations/interface.locale.ts +0 -2206
- package/app/i18n/translations/pricing.locale.ts +0 -397
- package/app/i18n/translations/privacy.locale.ts +0 -168
- package/app/i18n/translations/seo.locale.ts +0 -129
- package/app/i18n/translations/terms.locale.ts +0 -124
- package/app/i18n/types.ts +0 -6
- package/app/notifications/model.ts +0 -77
- package/app/notifications/notificationSlice.ts +0 -141
- package/app/reducer.ts +0 -36
- package/app/settings/dbActionThunks.ts +0 -34
- package/app/settings/defaultSpacePreference.ts +0 -87
- package/app/settings/settingSlice.tsx +0 -1212
- package/app/settings/settingsRecord.ts +0 -8
- package/app/stateViews/runtime.ts +0 -90
- package/app/store.ts +0 -55
- package/app/theme/colors.ts +0 -280
- package/app/theme/fontPreference.ts +0 -77
- package/app/theme/theme.config.ts +0 -120
- package/app/theme/themeModeBootstrap.ts +0 -123
- package/app/types/appSummary.ts +0 -118
- package/app/types.ts +0 -358
- package/app/utils/appKeys.ts +0 -55
- package/app/utils/async.ts +0 -28
- package/app/utils/desktopAgentRuntimeTurnClient.ts +0 -75
- package/app/utils/env.ts +0 -17
- package/app/utils/fileUtils.ts +0 -229
- package/app/utils/imageUtils.ts +0 -274
- package/app/utils/myContentItems.ts +0 -317
- package/app/utils/retryAfter.ts +0 -36
- package/app/utils/retryFetch.ts +0 -61
- package/app/utils/toast.ts +0 -3
- package/auth/action/signUpAction.ts +0 -227
- package/auth/adminPermissions.ts +0 -78
- package/auth/authSlice.ts +0 -531
- package/auth/client/loginRequest.ts +0 -14
- package/auth/resetAuthScopedClientState.ts +0 -27
- package/auth/routes.ts +0 -183
- package/auth/token.ts +0 -63
- package/auth/types.ts +0 -40
- package/authCommands.ts +0 -230
- package/chat/chat.locale.ts +0 -817
- package/chat/dialog/actions/addCybotAction.ts +0 -52
- package/chat/dialog/actions/addReferenceKeysAction.ts +0 -62
- package/chat/dialog/actions/builtinDialogLlm.ts +0 -35
- package/chat/dialog/actions/cleanupCliSession.ts +0 -40
- package/chat/dialog/actions/createDialogAction.ts +0 -379
- package/chat/dialog/actions/createScheduledTaskAction.ts +0 -78
- package/chat/dialog/actions/handleSendMessageAction.ts +0 -202
- package/chat/dialog/actions/handleSendMessageResolver.ts +0 -27
- package/chat/dialog/actions/removeCybotAction.ts +0 -6
- package/chat/dialog/actions/removeDialogAgentAction.ts +0 -44
- package/chat/dialog/actions/replacePrimaryCybotAction.ts +0 -6
- package/chat/dialog/actions/setPrimaryDialogAgentAction.ts +0 -52
- package/chat/dialog/actions/updateDialogSummaryAction.ts +0 -430
- package/chat/dialog/actions/updateDialogTitleAction.ts +0 -151
- package/chat/dialog/actions/updateDialogTitlePolicy.ts +0 -30
- package/chat/dialog/actions/updateTokensAction.ts +0 -233
- package/chat/dialog/dialogAgents.ts +0 -72
- package/chat/dialog/dialogAttachmentCleanup.ts +0 -135
- package/chat/dialog/dialogSlice.ts +0 -985
- package/chat/dialog/dialogTitle.ts +0 -81
- package/chat/dialog/dialogTokenStats.ts +0 -12
- package/chat/dialog/dialogUrl.ts +0 -6
- package/chat/dialog/goal.ts +0 -108
- package/chat/dialog/objectAssistantRegistry.ts +0 -286
- package/chat/dialog/schedule.ts +0 -17
- package/chat/messages/activityCompletion.ts +0 -97
- package/chat/messages/fetchAndCacheMessages.ts +0 -119
- package/chat/messages/fetchConvMsgs.ts +0 -51
- package/chat/messages/fetchMessages.ts +0 -84
- package/chat/messages/fileUrl.ts +0 -13
- package/chat/messages/imagePayloadPersistence.ts +0 -22
- package/chat/messages/messageAgent.ts +0 -8
- package/chat/messages/messageContent.ts +0 -203
- package/chat/messages/messageContract.ts +0 -88
- package/chat/messages/messageSlice.ts +0 -1386
- package/chat/messages/toolThunks.ts +0 -646
- package/chat/messages/types.ts +0 -192
- package/chat/messages/web/toolDisplayName.ts +0 -686
- package/cli/agentAliases.ts +0 -40
- package/cli/agentCommandSupport.ts +0 -81
- package/cli/agentInternalCommandEntries.ts +0 -44
- package/cli/agentListCommands.ts +0 -185
- package/cli/agentListHelpers.ts +0 -242
- package/cli/agentMachineCommands.ts +0 -426
- package/cli/agentNameResolver.ts +0 -164
- package/cli/agentPullCommand.ts +0 -142
- package/cli/agentRecordCommands.ts +0 -224
- package/cli/agentRecordHelpers.ts +0 -592
- package/cli/agentRunCommand.ts +0 -568
- package/cli/agentRuntimeLocal.ts +0 -45
- package/cli/authCommands.ts +0 -230
- package/cli/cliCommandAdapters.ts +0 -28
- package/cli/cliCommandDispatch.ts +0 -66
- package/cli/cliCommandFactories.ts +0 -66
- package/cli/cliCommandTypes.ts +0 -49
- package/cli/cliEnvHelpers.ts +0 -116
- package/cli/cliSpaceHelpers.ts +0 -39
- package/cli/client/agentConfigResolver.ts +0 -1
- package/cli/client/agentRun.ts +0 -735
- package/cli/client/compactDialog.ts +0 -222
- package/cli/client/hybridRecordStore.ts +0 -52
- package/cli/client/localAgentRecords.ts +0 -7
- package/cli/client/localDialogRecords.ts +0 -30
- package/cli/client/localProviderResolver.ts +0 -1
- package/cli/client/localRuntimeAdapter.ts +0 -1098
- package/cli/client/localToolPolicy.ts +0 -8
- package/cli/client/profileConfig.ts +0 -101
- package/cli/client/streamingOutput.ts +0 -38
- package/cli/commandRegistry.ts +0 -98
- package/cli/connectorRunArtifact.ts +0 -120
- package/cli/connectorWebSocketTarget.ts +0 -112
- package/cli/defaultServer.ts +0 -1
- package/cli/dialogAttachmentCleanup.ts +0 -151
- package/cli/dialogCommands.ts +0 -1120
- package/cli/dialogInternalCommandEntries.ts +0 -17
- package/cli/docCreateCommands.ts +0 -345
- package/cli/docInternalCommandEntries.ts +0 -11
- package/cli/docPageHelpers.ts +0 -80
- package/cli/docSkillHelpers.ts +0 -67
- package/cli/docSpaceHelpers.ts +0 -46
- package/cli/globalRecordOperations.ts +0 -258
- package/cli/internalCommandEntries.ts +0 -18
- package/cli/localRuntimeAuthority.ts +0 -182
- package/cli/localRuntimeDb.ts +0 -26
- package/cli/machineCommands.ts +0 -312
- package/cli/machineDaemonCommands.ts +0 -185
- package/cli/machineHeartbeatCommands.ts +0 -30
- package/cli/machineStatusCommands.ts +0 -95
- package/cli/machineWatchCommands.ts +0 -41
- package/cli/machineWsPromptMaterialization.ts +0 -89
- package/cli/machineWsRunDispatch.ts +0 -695
- package/cli/machineWsSession.ts +0 -213
- package/cli/memoryCommands.ts +0 -253
- package/cli/memoryInternalCommandEntries.ts +0 -9
- package/cli/offlineMarxistsAgentCommand.ts +0 -315
- package/cli/qoderUsageProbe.ts +0 -252
- package/cli/runtimeDoctorCommands.ts +0 -174
- package/cli/runtimeModeArgs.ts +0 -33
- package/cli/scriptCommandEntries.ts +0 -66
- package/cli/secretScan.ts +0 -31
- package/cli/systemInternalCommandEntries.ts +0 -65
- package/cli/tableCommands.ts +0 -453
- package/cli/tui/readlineWorkspace.ts +0 -210
- package/cli/tui/session.ts +0 -554
- package/cli/updateCommands.ts +0 -134
- package/cli/workflowInternalCommandEntries.ts +0 -10
- package/cliCommandAdapters.ts +0 -28
- package/cliCommandDispatch.ts +0 -66
- package/cliCommandFactories.ts +0 -66
- package/cliCommandTypes.ts +0 -49
- package/cliEnvHelpers.ts +0 -116
- package/cliSpaceHelpers.ts +0 -39
- package/client/agentConfigResolver.test.ts +0 -69
- package/client/agentConfigResolver.ts +0 -1
- package/client/agentRun.test.ts +0 -1346
- package/client/agentRun.ts +0 -735
- package/client/compactDialog.test.ts +0 -238
- package/client/compactDialog.ts +0 -222
- package/client/hybridRecordStore.test.ts +0 -157
- package/client/hybridRecordStore.ts +0 -52
- package/client/localAgentRecords.test.ts +0 -27
- package/client/localAgentRecords.ts +0 -7
- package/client/localDialogRecords.test.ts +0 -124
- package/client/localDialogRecords.ts +0 -30
- package/client/localProviderResolver.test.ts +0 -102
- package/client/localProviderResolver.ts +0 -1
- package/client/localRuntimeAdapter.test.ts +0 -2809
- package/client/localRuntimeAdapter.ts +0 -1098
- package/client/localRuntimeDryRun.test.ts +0 -135
- package/client/localToolPolicy.test.ts +0 -48
- package/client/localToolPolicy.ts +0 -8
- package/client/profileConfig.test.ts +0 -106
- package/client/profileConfig.ts +0 -101
- package/client/streamingOutput.test.ts +0 -22
- package/client/streamingOutput.ts +0 -38
- package/commandRegistry.ts +0 -98
- package/connector-experimental/capabilities.ts +0 -75
- package/connector-experimental/codexBinary.ts +0 -41
- package/connector-experimental/heartbeatLoop.ts +0 -22
- package/connector-experimental/machineInfo.ts +0 -46
- package/connector-experimental/protocol.ts +0 -54
- package/connectorRunArtifact.ts +0 -120
- package/connectorWebSocketTarget.ts +0 -112
- package/core/builtinAgents.ts +0 -209
- package/core/config.ts +0 -15
- package/core/crypto.ts +0 -51
- package/core/generateKeyPairFromSeedV1.ts +0 -15
- package/core/generateMainKey.ts +0 -22
- package/core/init.ts +0 -41
- package/core/localOrigins.ts +0 -8
- package/core/password.ts +0 -13
- package/core/prefix.ts +0 -23
- package/create/editor/transforms/fromMarkdown/index.ts +0 -260
- package/create/editor/transforms/fromMarkdown/inline.ts +0 -285
- package/create/editor/transforms/fromMarkdown/table.ts +0 -130
- package/create/editor/transforms/markdownToSlate.ts +0 -24
- package/create/editor/transforms/slateToRenderMarkdown.ts +0 -218
- package/create/editor/transforms/slateToSimplifiedMarkdown.ts +0 -224
- package/create/editor/transforms/slateToText.ts +0 -33
- package/create/editor/utils/slateUtils.ts +0 -233
- package/create/space/addSpaceAction.ts +0 -193
- package/create/space/category/categoryActions.ts +0 -427
- package/create/space/changeSpaceAction.ts +0 -36
- package/create/space/constants.ts +0 -8
- package/create/space/content/addContentAction.ts +0 -116
- package/create/space/content/contentThunks.ts +0 -90
- package/create/space/content/deleteContentFromSpaceAction.ts +0 -187
- package/create/space/content/deleteMultipleContentAction.ts +0 -112
- package/create/space/content/moveContentAction.ts +0 -285
- package/create/space/content/updateContentCategoryAction.ts +0 -154
- package/create/space/content/updateContentTitleAction.ts +0 -151
- package/create/space/content/uploadAndAddFileToSpaceAction.ts +0 -98
- package/create/space/contentKeyUtils.ts +0 -117
- package/create/space/deleteSpaceAction.ts +0 -276
- package/create/space/fetchSpaceAction.ts +0 -70
- package/create/space/fetchSpaceSidebarStateAction.ts +0 -26
- package/create/space/loadDefaultSpaceAction.ts +0 -57
- package/create/space/member/addMemberAction.ts +0 -79
- package/create/space/member/fetchUserSpaceMembershipsAction.ts +0 -386
- package/create/space/member/memberThunks.ts +0 -47
- package/create/space/member/removeMemberAction.ts +0 -67
- package/create/space/resolvePreferredSpaceId.ts +0 -113
- package/create/space/sidebarVisibleTypes.ts +0 -260
- package/create/space/space.locale.ts +0 -1087
- package/create/space/spaceAccess.ts +0 -182
- package/create/space/spaceCollapsedState.ts +0 -54
- package/create/space/spaceKeys.ts +0 -65
- package/create/space/spaceSlice.ts +0 -328
- package/create/space/spaceThunks.ts +0 -410
- package/create/space/types.ts +0 -27
- package/create/space/updateSpaceAction.ts +0 -107
- package/create/space/utils/permissions.ts +0 -47
- package/create/types.ts +0 -21
- package/database/actions/agentReadResolution.ts +0 -33
- package/database/actions/common.ts +0 -243
- package/database/actions/deleteFile.ts +0 -40
- package/database/actions/fileContent.ts +0 -125
- package/database/actions/patch.ts +0 -155
- package/database/actions/read.ts +0 -358
- package/database/actions/readAndWait.ts +0 -260
- package/database/actions/readRequestManager.ts +0 -120
- package/database/actions/readResolution.ts +0 -125
- package/database/actions/remove.ts +0 -94
- package/database/actions/replication.ts +0 -477
- package/database/actions/upload.ts +0 -186
- package/database/actions/upsert.ts +0 -56
- package/database/actions/write.ts +0 -146
- package/database/authority/ownerKey.ts +0 -80
- package/database/authority/recordAuthority.ts +0 -67
- package/database/authority/userAuthorityRegistry.ts +0 -54
- package/database/client/fetchUserData.ts +0 -61
- package/database/config.ts +0 -40
- package/database/dbActionThunks.ts +0 -1
- package/database/dbSlice.ts +0 -149
- package/database/fileRing.ts +0 -51
- package/database/fileStorage.ts +0 -232
- package/database/fileUrl.ts +0 -34
- package/database/keys.ts +0 -767
- package/database/queryPrefixes.ts +0 -14
- package/database/requests.ts +0 -443
- package/database/runtimeServerContext.ts +0 -54
- package/database/server/authorityStoreTypes.ts +0 -31
- package/database/server/cliAuthorityBrokerClient.ts +0 -227
- package/database/server/cliAuthorityBrokerServer.ts +0 -283
- package/database/server/cliAuthorityBrokerTypes.ts +0 -43
- package/database/server/cliAuthorityStoreDriver.ts +0 -98
- package/database/server/db.ts +0 -99
- package/database/server/dbPath.ts +0 -20
- package/database/server/ensureDbOpen.ts +0 -12
- package/database/server/legacyServerDb.ts +0 -58
- package/database/server/levelAuthorityStore.ts +0 -114
- package/database/server/memoryAuthorityStore.ts +0 -133
- package/database/server/serverStoreFactory.ts +0 -118
- package/database/tenantPlacement.ts +0 -57
- package/database/tombstones.ts +0 -52
- package/database/userDataMerge.ts +0 -95
- package/database/userPreferenceRegister.ts +0 -131
- package/database/utils/ulid.ts +0 -1
- package/defaultServer.ts +0 -1
- package/dialogAttachmentCleanup.ts +0 -151
- package/dialogCommands.ts +0 -1120
- package/dialogInternalCommandEntries.ts +0 -17
- package/docCreateCommands.ts +0 -345
- package/docInternalCommandEntries.ts +0 -11
- package/docPageHelpers.ts +0 -80
- package/docSkillHelpers.ts +0 -67
- package/docSpaceHelpers.ts +0 -46
- package/globalRecordOperations.ts +0 -258
- package/index.ts +0 -78
- package/integrations/anthropic/anthropicModels.ts +0 -47
- package/integrations/deepseek/models.ts +0 -37
- package/integrations/google/models.ts +0 -341
- package/integrations/openai/chatCompletionStreamMode.ts +0 -10
- package/integrations/openai/filterAndCleanMessages.ts +0 -284
- package/integrations/openai/flexTier.ts +0 -102
- package/integrations/openai/generateOpenAIRequestBody.ts +0 -271
- package/integrations/openai/generateResponseRequestBody.ts +0 -77
- package/integrations/openai/models.ts +0 -271
- package/integrations/openai/providerBodyCompatibility.ts +0 -23
- package/integrations/openai/responsesHelpers.ts +0 -297
- package/integrations/x-reader/types.ts +0 -109
- package/integrations/xai/models.ts +0 -13
- package/integrations/xhs-reader/redaction.ts +0 -68
- package/integrations/xhs-reader/types.ts +0 -277
- package/integrations/xhs-reader/url.ts +0 -80
- package/internalCommandEntries.ts +0 -18
- package/localRuntimeAuthority.ts +0 -182
- package/localRuntimeDb.ts +0 -26
- package/machineCommands.ts +0 -312
- package/machineDaemonCommands.ts +0 -185
- package/machineHeartbeatCommands.ts +0 -30
- package/machineStatusCommands.ts +0 -95
- package/machineWatchCommands.ts +0 -41
- package/machineWsPromptMaterialization.ts +0 -89
- package/machineWsRunDispatch.ts +0 -695
- package/machineWsSession.ts +0 -213
- package/memoryCommands.ts +0 -253
- package/memoryInternalCommandEntries.ts +0 -9
- package/offlineMarxistsAgentCommand.ts +0 -315
- package/qoderUsageProbe.ts +0 -252
- package/render/contentIcon/types.ts +0 -167
- package/render/page/createPageAction.ts +0 -183
- package/render/page/docSlice.ts +0 -495
- package/render/page/types.ts +0 -19
- package/render/table/activityColumns.ts +0 -6
- package/render/table/createTableAction.ts +0 -274
- package/render/table/deleteTableAction.ts +0 -193
- package/render/table/fetchAndCacheTableRows.ts +0 -174
- package/render/table/tableSlice.ts +0 -1167
- package/render/table/toolValueUtils.ts +0 -363
- package/render/table/types.ts +0 -262
- package/render/table/utils/tableSerialization.ts +0 -50
- package/runtimeDoctorCommands.ts +0 -174
- package/runtimeModeArgs.ts +0 -33
- package/scriptCommandEntries.ts +0 -66
- package/secretScan.ts +0 -31
- package/server/handlers/agentRun/types.ts +0 -105
- package/share/action.ts +0 -225
- package/share/helpers.ts +0 -302
- package/share/keys.ts +0 -243
- package/share/publicSharePolicy.ts +0 -27
- package/share/shareSlice.ts +0 -53
- package/share/types.ts +0 -100
- package/systemInternalCommandEntries.ts +0 -65
- package/tableCommands.ts +0 -453
- package/tui/readlineWorkspace.ts +0 -210
- package/tui/session.ts +0 -554
- package/updateCommands.ts +0 -134
- package/workflowInternalCommandEntries.ts +0 -10
|
@@ -1,2310 +0,0 @@
|
|
|
1
|
-
import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
2
|
-
import { dirname, relative, resolve, sep } from "node:path";
|
|
3
|
-
|
|
4
|
-
import type {
|
|
5
|
-
AgentRuntimeToolCallInput,
|
|
6
|
-
AgentRuntimeToolResult,
|
|
7
|
-
} from "./hostAdapter";
|
|
8
|
-
|
|
9
|
-
type LocalWorkspaceToolArgs = {
|
|
10
|
-
workspaceRoot: string;
|
|
11
|
-
commandTimeoutMs?: number;
|
|
12
|
-
commandOutputLimit?: number;
|
|
13
|
-
commandPrefix?: string[];
|
|
14
|
-
};
|
|
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
|
-
|
|
52
|
-
type WorkspaceFileArgs = {
|
|
53
|
-
path?: unknown;
|
|
54
|
-
content?: unknown;
|
|
55
|
-
oldText?: unknown;
|
|
56
|
-
newText?: unknown;
|
|
57
|
-
expectedReplacements?: unknown;
|
|
58
|
-
maxDepth?: unknown;
|
|
59
|
-
entryType?: unknown;
|
|
60
|
-
startLine?: unknown;
|
|
61
|
-
endLine?: unknown;
|
|
62
|
-
maxLines?: unknown;
|
|
63
|
-
tailLines?: unknown;
|
|
64
|
-
query?: unknown;
|
|
65
|
-
pattern?: unknown;
|
|
66
|
-
glob?: unknown;
|
|
67
|
-
exclude?: unknown;
|
|
68
|
-
maxResults?: unknown;
|
|
69
|
-
includeIgnored?: unknown;
|
|
70
|
-
literal?: unknown;
|
|
71
|
-
caseSensitive?: unknown;
|
|
72
|
-
contextLines?: unknown;
|
|
73
|
-
command?: unknown;
|
|
74
|
-
cmd?: unknown;
|
|
75
|
-
branch?: unknown;
|
|
76
|
-
paths?: unknown;
|
|
77
|
-
message?: unknown;
|
|
78
|
-
staged?: unknown;
|
|
79
|
-
baseUrl?: unknown;
|
|
80
|
-
base?: unknown;
|
|
81
|
-
waitSelector?: unknown;
|
|
82
|
-
scrollSelector?: unknown;
|
|
83
|
-
focusSelector?: unknown;
|
|
84
|
-
expectText?: unknown;
|
|
85
|
-
screenshotPath?: unknown;
|
|
86
|
-
metricsPath?: unknown;
|
|
87
|
-
shell?: unknown;
|
|
88
|
-
_activity?: unknown;
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
type OpenAiCompatibleTool = Record<string, unknown> & {
|
|
92
|
-
function?: Record<string, unknown> & { name?: string };
|
|
93
|
-
};
|
|
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
|
-
|
|
104
|
-
const WORKSPACE_TOOL_NAMES = [
|
|
105
|
-
"listFiles",
|
|
106
|
-
"readFile",
|
|
107
|
-
"writeFile",
|
|
108
|
-
"editFile",
|
|
109
|
-
"globFiles",
|
|
110
|
-
"searchFiles",
|
|
111
|
-
"startPreview",
|
|
112
|
-
"getPreviewStatus",
|
|
113
|
-
"stopPreview",
|
|
114
|
-
"releasePreview",
|
|
115
|
-
"captureVisualState",
|
|
116
|
-
"execShell",
|
|
117
|
-
] as const;
|
|
118
|
-
|
|
119
|
-
const DEFAULT_LOCAL_CODING_TOOL_NAMES = [
|
|
120
|
-
"listFiles",
|
|
121
|
-
"readFile",
|
|
122
|
-
"writeFile",
|
|
123
|
-
"editFile",
|
|
124
|
-
"globFiles",
|
|
125
|
-
"searchFiles",
|
|
126
|
-
] as const;
|
|
127
|
-
|
|
128
|
-
const SHELL_TOOL_NAMES = ["execShell"] as const;
|
|
129
|
-
const WORKSPACE_TOOL_NAME_SET = new Set<string>(WORKSPACE_TOOL_NAMES);
|
|
130
|
-
const REMOVED_WORKSPACE_TOOL_NAMES = new Set([
|
|
131
|
-
"gitStatus",
|
|
132
|
-
"gitDiff",
|
|
133
|
-
"gitCreateBranch",
|
|
134
|
-
"gitAdd",
|
|
135
|
-
"gitCommit",
|
|
136
|
-
"commitWorkspace",
|
|
137
|
-
]);
|
|
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
|
-
|
|
332
|
-
function buildWorkspacePathProperty() {
|
|
333
|
-
return {
|
|
334
|
-
type: "string",
|
|
335
|
-
description: "Path relative to the workspace root.",
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
|
|
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 {
|
|
400
|
-
return {
|
|
401
|
-
type: "function",
|
|
402
|
-
function: {
|
|
403
|
-
name: "listFiles",
|
|
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,
|
|
459
|
-
},
|
|
460
|
-
required: ["path"],
|
|
461
|
-
};
|
|
462
|
-
}
|
|
463
|
-
return {
|
|
464
|
-
type: "object",
|
|
465
|
-
properties: {
|
|
466
|
-
path,
|
|
467
|
-
startLine,
|
|
468
|
-
endLine,
|
|
469
|
-
maxLines,
|
|
470
|
-
_activity,
|
|
471
|
-
},
|
|
472
|
-
required: ["path"],
|
|
473
|
-
};
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
function buildReadWorkspaceFileTool(args?: {
|
|
477
|
-
descriptionVariant?: ReadFileDescriptionVariant;
|
|
478
|
-
parameterVariant?: ReadFileParameterVariant;
|
|
479
|
-
}): OpenAiCompatibleTool {
|
|
480
|
-
return {
|
|
481
|
-
type: "function",
|
|
482
|
-
function: {
|
|
483
|
-
name: "readFile",
|
|
484
|
-
description: buildReadWorkspaceDescription(args?.descriptionVariant),
|
|
485
|
-
parameters: buildReadWorkspaceParameters(args?.parameterVariant),
|
|
486
|
-
},
|
|
487
|
-
};
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
function buildWriteWorkspaceFileTool(): OpenAiCompatibleTool {
|
|
491
|
-
return {
|
|
492
|
-
type: "function",
|
|
493
|
-
function: {
|
|
494
|
-
name: "writeFile",
|
|
495
|
-
description:
|
|
496
|
-
"Write full UTF-8 file content inside the workspace. For new files or deliberate whole-file rewrites only. For existing files, prefer editFile for targeted edits. Warn that whole-file rewrites can cause line-ending churn.",
|
|
497
|
-
parameters: {
|
|
498
|
-
type: "object",
|
|
499
|
-
properties: {
|
|
500
|
-
path: buildWorkspacePathProperty(),
|
|
501
|
-
content: {
|
|
502
|
-
type: "string",
|
|
503
|
-
description: "Full UTF-8 file content to write.",
|
|
504
|
-
},
|
|
505
|
-
_activity: buildActivityProperty(),
|
|
506
|
-
},
|
|
507
|
-
required: ["path", "content"],
|
|
508
|
-
},
|
|
509
|
-
},
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
function buildReplaceWorkspaceTextTool(): OpenAiCompatibleTool {
|
|
514
|
-
return {
|
|
515
|
-
type: "function",
|
|
516
|
-
function: {
|
|
517
|
-
name: "editFile",
|
|
518
|
-
description:
|
|
519
|
-
"Use for small, exact edits in one workspace file without constructing a patch. Read the file first when you need the exact oldText; set expectedReplacements to avoid accidental broad edits. When expected replacement count fails, report a blocker instead of falling back to a full-file rewrite.",
|
|
520
|
-
parameters: {
|
|
521
|
-
type: "object",
|
|
522
|
-
properties: {
|
|
523
|
-
path: buildWorkspacePathProperty(),
|
|
524
|
-
oldText: {
|
|
525
|
-
type: "string",
|
|
526
|
-
description: "Exact text currently present in the file.",
|
|
527
|
-
},
|
|
528
|
-
newText: {
|
|
529
|
-
type: "string",
|
|
530
|
-
description: "Replacement text to write in place of oldText.",
|
|
531
|
-
},
|
|
532
|
-
expectedReplacements: {
|
|
533
|
-
type: "integer",
|
|
534
|
-
description: "Expected replacement count. Defaults to 1.",
|
|
535
|
-
},
|
|
536
|
-
_activity: buildActivityProperty(),
|
|
537
|
-
},
|
|
538
|
-
required: ["path", "oldText", "newText"],
|
|
539
|
-
},
|
|
540
|
-
},
|
|
541
|
-
};
|
|
542
|
-
}
|
|
543
|
-
|
|
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 {
|
|
630
|
-
return {
|
|
631
|
-
type: "function",
|
|
632
|
-
function: {
|
|
633
|
-
name: "searchFiles",
|
|
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(),
|
|
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),
|
|
721
|
-
},
|
|
722
|
-
};
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
function buildPreviewLifecycleTool(toolName: string): OpenAiCompatibleTool {
|
|
726
|
-
const descriptions: Record<string, string> = {
|
|
727
|
-
startPreview: "Start the local preview stack for the current workspace.",
|
|
728
|
-
getPreviewStatus: "Read local preview status, including localApiOrigin and process state.",
|
|
729
|
-
stopPreview: "Stop the local preview stack for the current workspace.",
|
|
730
|
-
releasePreview: "Release the local preview slot for the current workspace after stopping preview.",
|
|
731
|
-
};
|
|
732
|
-
return {
|
|
733
|
-
type: "function",
|
|
734
|
-
function: {
|
|
735
|
-
name: toolName,
|
|
736
|
-
description: descriptions[toolName] ?? "Run a local preview lifecycle action.",
|
|
737
|
-
parameters: { type: "object", properties: {} },
|
|
738
|
-
},
|
|
739
|
-
};
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
function buildCaptureVisualStateTool(): OpenAiCompatibleTool {
|
|
743
|
-
return {
|
|
744
|
-
type: "function",
|
|
745
|
-
function: {
|
|
746
|
-
name: "captureVisualState",
|
|
747
|
-
description: "Capture a real local app screenshot and DOM/computed-style metrics for a selected UI state.",
|
|
748
|
-
parameters: {
|
|
749
|
-
type: "object",
|
|
750
|
-
properties: {
|
|
751
|
-
baseUrl: {
|
|
752
|
-
type: "string",
|
|
753
|
-
description: "Optional local preview base URL. When omitted, the tool reads preview:status.",
|
|
754
|
-
},
|
|
755
|
-
path: {
|
|
756
|
-
type: "string",
|
|
757
|
-
description: "App route to open, for example / or /dialog-123. Defaults to /.",
|
|
758
|
-
},
|
|
759
|
-
waitSelector: {
|
|
760
|
-
type: "string",
|
|
761
|
-
description: "CSS selector that must become visible before capture.",
|
|
762
|
-
},
|
|
763
|
-
scrollSelector: {
|
|
764
|
-
type: "string",
|
|
765
|
-
description: "Optional CSS selector to scroll into view before capture.",
|
|
766
|
-
},
|
|
767
|
-
focusSelector: {
|
|
768
|
-
type: "string",
|
|
769
|
-
description: "Optional CSS selector for the target element whose rect/style should be reported.",
|
|
770
|
-
},
|
|
771
|
-
expectText: {
|
|
772
|
-
type: "string",
|
|
773
|
-
description: "Optional visible text expected on the page before capture.",
|
|
774
|
-
},
|
|
775
|
-
screenshotPath: {
|
|
776
|
-
type: "string",
|
|
777
|
-
description: "Workspace-relative screenshot path. Defaults under test-results/frontend-agent/.",
|
|
778
|
-
},
|
|
779
|
-
metricsPath: {
|
|
780
|
-
type: "string",
|
|
781
|
-
description: "Workspace-relative metrics JSON path. Defaults under test-results/frontend-agent/.",
|
|
782
|
-
},
|
|
783
|
-
},
|
|
784
|
-
required: ["waitSelector"],
|
|
785
|
-
},
|
|
786
|
-
},
|
|
787
|
-
};
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
function buildExecShellTool(toolName: string): OpenAiCompatibleTool {
|
|
791
|
-
return {
|
|
792
|
-
type: "function",
|
|
793
|
-
function: {
|
|
794
|
-
name: toolName,
|
|
795
|
-
description:
|
|
796
|
-
"Run a shell command in the local workspace. Commands already execute from the workspace root; do not cd into guessed paths such as /workspace, /repo, /home/user, or /home/user/workspace. Prefer one command that performs a complete verification or related git operation instead of many tiny commands. For branch setup, prefer idempotent commands such as git switch -C <branch> when replacing or recreating a benchmark branch is acceptable. Use portable POSIX commands for macOS/BSD shells; avoid GNU-only flags such as cat -A. Do not use cat -A. Do not use brittle byte-offset commands such as xxd -s -32. For separator lines, use echo or printf '%s\\n'. For text-file content or trailing-newline checks, prefer readFile over shell byte inspection when that tool is available. Do not run repeated git status, git log, git rev-parse, or branch checks after a successful command already returned the needed clean status and commit information; use one final verification command. If a command fails, use the error output to adjust the next command rather than repeating the same shape.",
|
|
797
|
-
parameters: {
|
|
798
|
-
type: "object",
|
|
799
|
-
properties: {
|
|
800
|
-
cmd: {
|
|
801
|
-
type: "string",
|
|
802
|
-
description: "Shell command to run.",
|
|
803
|
-
},
|
|
804
|
-
command: {
|
|
805
|
-
type: "string",
|
|
806
|
-
description: "Shell command to run.",
|
|
807
|
-
},
|
|
808
|
-
_activity: buildActivityProperty(),
|
|
809
|
-
},
|
|
810
|
-
},
|
|
811
|
-
},
|
|
812
|
-
};
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
function wrapPowerShellCommand(command: string) {
|
|
816
|
-
return [
|
|
817
|
-
"[Console]::InputEncoding=[System.Text.Encoding]::UTF8",
|
|
818
|
-
"[Console]::OutputEncoding=[System.Text.Encoding]::UTF8",
|
|
819
|
-
"$OutputEncoding=[System.Text.Encoding]::UTF8",
|
|
820
|
-
"$PSStyle.OutputRendering='PlainText'",
|
|
821
|
-
command,
|
|
822
|
-
].join("; ");
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
function findPowerShellExecutable() {
|
|
826
|
-
return Bun.which("pwsh") || Bun.which("powershell.exe") || Bun.which("powershell");
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
function buildPowerShellCommand(command: string) {
|
|
830
|
-
const executable = findPowerShellExecutable();
|
|
831
|
-
if (!executable) throw new Error("PowerShell is not available on this machine.");
|
|
832
|
-
return [
|
|
833
|
-
executable,
|
|
834
|
-
"-NoLogo",
|
|
835
|
-
"-NoProfile",
|
|
836
|
-
"-NonInteractive",
|
|
837
|
-
"-ExecutionPolicy",
|
|
838
|
-
"Bypass",
|
|
839
|
-
"-Command",
|
|
840
|
-
wrapPowerShellCommand(command),
|
|
841
|
-
];
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
function buildBashCommand(command: string) {
|
|
845
|
-
const executable = Bun.which("bash") || Bun.which("sh");
|
|
846
|
-
if (!executable) throw new Error("bash/sh is not available on this machine.");
|
|
847
|
-
return [executable, "-lc", command];
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
function buildWorkspaceShellCommand(args: {
|
|
851
|
-
toolName: string;
|
|
852
|
-
command: string;
|
|
853
|
-
shell?: unknown;
|
|
854
|
-
}) {
|
|
855
|
-
if (args.shell === "powershell") return buildPowerShellCommand(args.command);
|
|
856
|
-
if (args.shell === "bash") return buildBashCommand(args.command);
|
|
857
|
-
return process.platform === "win32"
|
|
858
|
-
? buildPowerShellCommand(args.command)
|
|
859
|
-
: buildBashCommand(args.command);
|
|
860
|
-
}
|
|
861
|
-
|
|
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
|
-
}) {
|
|
872
|
-
if (toolName === "listFiles") {
|
|
873
|
-
return buildListWorkspaceFilesTool({
|
|
874
|
-
descriptionVariant: args?.listFilesDescriptionVariant,
|
|
875
|
-
parameterVariant: args?.listFilesParameterVariant,
|
|
876
|
-
});
|
|
877
|
-
}
|
|
878
|
-
if (toolName === "readFile") {
|
|
879
|
-
return buildReadWorkspaceFileTool({
|
|
880
|
-
descriptionVariant: args?.readFileDescriptionVariant,
|
|
881
|
-
parameterVariant: args?.readFileParameterVariant,
|
|
882
|
-
});
|
|
883
|
-
}
|
|
884
|
-
if (toolName === "writeFile") {
|
|
885
|
-
return buildWriteWorkspaceFileTool();
|
|
886
|
-
}
|
|
887
|
-
if (toolName === "editFile") {
|
|
888
|
-
return buildReplaceWorkspaceTextTool();
|
|
889
|
-
}
|
|
890
|
-
if (toolName === "globFiles") {
|
|
891
|
-
return buildGlobWorkspaceFilesTool({
|
|
892
|
-
descriptionVariant: args?.globFilesDescriptionVariant,
|
|
893
|
-
parameterVariant: args?.globFilesParameterVariant,
|
|
894
|
-
});
|
|
895
|
-
}
|
|
896
|
-
if (toolName === "searchFiles") {
|
|
897
|
-
return buildSearchWorkspaceTool({
|
|
898
|
-
descriptionVariant: args?.searchFilesDescriptionVariant,
|
|
899
|
-
parameterVariant: args?.searchFilesParameterVariant,
|
|
900
|
-
});
|
|
901
|
-
}
|
|
902
|
-
if (toolName === "startPreview" || toolName === "getPreviewStatus" || toolName === "stopPreview" || toolName === "releasePreview") {
|
|
903
|
-
return buildPreviewLifecycleTool(toolName);
|
|
904
|
-
}
|
|
905
|
-
if (toolName === "captureVisualState") return buildCaptureVisualStateTool();
|
|
906
|
-
if (toolName === "execShell") return buildExecShellTool(toolName);
|
|
907
|
-
return null;
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
function filterDeclaredWorkspaceToolNames(args: {
|
|
911
|
-
toolNames?: string[];
|
|
912
|
-
exposeShellTools: boolean;
|
|
913
|
-
}) {
|
|
914
|
-
return (args.toolNames ?? []).filter((toolName) =>
|
|
915
|
-
WORKSPACE_TOOL_NAME_SET.has(toolName) &&
|
|
916
|
-
!REMOVED_WORKSPACE_TOOL_NAMES.has(toolName) &&
|
|
917
|
-
(args.exposeShellTools || !SHELL_TOOL_NAMES.includes(toolName as any))
|
|
918
|
-
);
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
export function buildLocalWorkspaceToolset(args: {
|
|
922
|
-
declaredToolNames?: string[];
|
|
923
|
-
exposeShellTools?: boolean;
|
|
924
|
-
useDeclaredToolNamesOnly?: boolean;
|
|
925
|
-
}) {
|
|
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
|
-
}
|
|
937
|
-
const toolNames = new Set([
|
|
938
|
-
...DEFAULT_LOCAL_CODING_TOOL_NAMES,
|
|
939
|
-
...(exposeShellTools ? SHELL_TOOL_NAMES : []),
|
|
940
|
-
...filterDeclaredWorkspaceToolNames({
|
|
941
|
-
toolNames: args.declaredToolNames,
|
|
942
|
-
exposeShellTools,
|
|
943
|
-
}),
|
|
944
|
-
]);
|
|
945
|
-
return {
|
|
946
|
-
toolNames: [...toolNames],
|
|
947
|
-
exposeShellTools,
|
|
948
|
-
};
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
export function buildLocalWorkspacePolicyToolNames(args: {
|
|
952
|
-
declaredToolNames?: string[];
|
|
953
|
-
exposeShellTools?: boolean;
|
|
954
|
-
useDeclaredToolNamesOnly?: boolean;
|
|
955
|
-
}) {
|
|
956
|
-
if (args.useDeclaredToolNamesOnly) {
|
|
957
|
-
return filterDeclaredWorkspaceToolNames({
|
|
958
|
-
toolNames: args.declaredToolNames,
|
|
959
|
-
exposeShellTools: args.exposeShellTools === true,
|
|
960
|
-
});
|
|
961
|
-
}
|
|
962
|
-
return [...new Set([
|
|
963
|
-
...buildLocalWorkspaceToolset({
|
|
964
|
-
exposeShellTools: args.exposeShellTools,
|
|
965
|
-
}).toolNames,
|
|
966
|
-
...filterDeclaredWorkspaceToolNames({
|
|
967
|
-
toolNames: args.declaredToolNames,
|
|
968
|
-
exposeShellTools: args.exposeShellTools === true,
|
|
969
|
-
}),
|
|
970
|
-
])].filter((toolName) => !REMOVED_WORKSPACE_TOOL_NAMES.has(toolName));
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
export function buildLocalWorkspaceOpenAiTools(args: {
|
|
974
|
-
toolNames?: string[];
|
|
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;
|
|
984
|
-
}) {
|
|
985
|
-
const declaredTools = new Set(args.toolNames ?? []);
|
|
986
|
-
return WORKSPACE_TOOL_NAMES
|
|
987
|
-
.filter((toolName) => {
|
|
988
|
-
if (!declaredTools.has(toolName)) return false;
|
|
989
|
-
if (!args.exposeShellTools && toolName === "execShell") {
|
|
990
|
-
return false;
|
|
991
|
-
}
|
|
992
|
-
return true;
|
|
993
|
-
})
|
|
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
|
-
}))
|
|
1004
|
-
.filter((tool): tool is OpenAiCompatibleTool => Boolean(tool));
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
function parseWorkspaceToolArguments(raw: string): WorkspaceFileArgs {
|
|
1008
|
-
try {
|
|
1009
|
-
const parsed = JSON.parse(raw || "{}");
|
|
1010
|
-
return parsed && typeof parsed === "object" ? parsed as WorkspaceFileArgs : {};
|
|
1011
|
-
} catch {
|
|
1012
|
-
return {};
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
function normalizeWorkspaceRelativePath(args: {
|
|
1017
|
-
workspaceRoot: string;
|
|
1018
|
-
targetPath: string;
|
|
1019
|
-
}) {
|
|
1020
|
-
const relativePath = relative(args.workspaceRoot, args.targetPath);
|
|
1021
|
-
return relativePath || ".";
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
function isPathInsideWorkspace(args: {
|
|
1025
|
-
workspaceRoot: string;
|
|
1026
|
-
targetPath: string;
|
|
1027
|
-
}) {
|
|
1028
|
-
const relativePath = relative(args.workspaceRoot, args.targetPath);
|
|
1029
|
-
return relativePath === "" || (!relativePath.startsWith("..") && !relativePath.includes(`..${sep}`));
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
function requireWorkspaceToolPath(args: WorkspaceFileArgs) {
|
|
1033
|
-
const requestedPath = typeof args.path === "string" ? args.path.trim() : "";
|
|
1034
|
-
if (!requestedPath) throw new Error("Workspace tool requires a non-empty path.");
|
|
1035
|
-
return requestedPath;
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
function requireWorkspaceFileContent(args: WorkspaceFileArgs) {
|
|
1039
|
-
if (typeof args.content !== "string") {
|
|
1040
|
-
throw new Error("writeFile requires string content.");
|
|
1041
|
-
}
|
|
1042
|
-
return args.content;
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
function requireWorkspaceOldText(args: WorkspaceFileArgs) {
|
|
1046
|
-
if (typeof args.oldText !== "string" || !args.oldText) {
|
|
1047
|
-
throw new Error("editFile requires non-empty oldText.");
|
|
1048
|
-
}
|
|
1049
|
-
return args.oldText;
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
function requireWorkspaceNewText(args: WorkspaceFileArgs) {
|
|
1053
|
-
if (typeof args.newText !== "string") {
|
|
1054
|
-
throw new Error("editFile requires string newText.");
|
|
1055
|
-
}
|
|
1056
|
-
return args.newText;
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
function readExpectedReplacementCount(args: WorkspaceFileArgs) {
|
|
1060
|
-
if (args.expectedReplacements === undefined) return 1;
|
|
1061
|
-
const value = Number(args.expectedReplacements);
|
|
1062
|
-
if (!Number.isInteger(value) || value < 1) {
|
|
1063
|
-
throw new Error("editFile expectedReplacements must be a positive integer.");
|
|
1064
|
-
}
|
|
1065
|
-
return value;
|
|
1066
|
-
}
|
|
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
|
-
|
|
1158
|
-
function countExactTextOccurrences(args: {
|
|
1159
|
-
content: string;
|
|
1160
|
-
oldText: string;
|
|
1161
|
-
}) {
|
|
1162
|
-
return args.content.split(args.oldText).length - 1;
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
function pluralizeReplacement(count: number) {
|
|
1166
|
-
return count === 1 ? "replacement" : "replacements";
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
function requireWorkspaceSearchQuery(args: WorkspaceFileArgs) {
|
|
1170
|
-
const query = typeof args.query === "string" ? args.query.trim() : "";
|
|
1171
|
-
if (!query) throw new Error("searchFiles requires a non-empty query.");
|
|
1172
|
-
return query;
|
|
1173
|
-
}
|
|
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
|
-
|
|
1233
|
-
function requireShellCommand(args: WorkspaceFileArgs, toolName: string) {
|
|
1234
|
-
const command = typeof args.cmd === "string"
|
|
1235
|
-
? args.cmd.trim()
|
|
1236
|
-
: typeof args.command === "string"
|
|
1237
|
-
? args.command.trim()
|
|
1238
|
-
: "";
|
|
1239
|
-
if (!command) throw new Error(`${toolName} requires a non-empty command.`);
|
|
1240
|
-
return command;
|
|
1241
|
-
}
|
|
1242
|
-
|
|
1243
|
-
function requireVisualWaitSelector(args: WorkspaceFileArgs) {
|
|
1244
|
-
const selector = typeof args.waitSelector === "string" ? args.waitSelector.trim() : "";
|
|
1245
|
-
if (!selector) throw new Error("captureVisualState requires a non-empty waitSelector.");
|
|
1246
|
-
return selector;
|
|
1247
|
-
}
|
|
1248
|
-
|
|
1249
|
-
function readOptionalStringArg(value: unknown) {
|
|
1250
|
-
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1253
|
-
function readVisualStateCaptureArgs(args: WorkspaceFileArgs) {
|
|
1254
|
-
const baseUrl = readOptionalStringArg(args.baseUrl) ?? readOptionalStringArg(args.base);
|
|
1255
|
-
const path = readOptionalStringArg(args.path) ?? "/";
|
|
1256
|
-
const waitSelector = requireVisualWaitSelector(args);
|
|
1257
|
-
const scrollSelector = readOptionalStringArg(args.scrollSelector);
|
|
1258
|
-
const focusSelector = readOptionalStringArg(args.focusSelector);
|
|
1259
|
-
const expectText = readOptionalStringArg(args.expectText);
|
|
1260
|
-
const screenshotPath =
|
|
1261
|
-
readOptionalStringArg(args.screenshotPath) ?? "test-results/frontend-agent/visual-state.png";
|
|
1262
|
-
const metricsPath =
|
|
1263
|
-
readOptionalStringArg(args.metricsPath) ?? "test-results/frontend-agent/visual-state-metrics.json";
|
|
1264
|
-
return {
|
|
1265
|
-
baseUrl,
|
|
1266
|
-
path,
|
|
1267
|
-
waitSelector,
|
|
1268
|
-
scrollSelector,
|
|
1269
|
-
focusSelector,
|
|
1270
|
-
expectText,
|
|
1271
|
-
screenshotPath,
|
|
1272
|
-
metricsPath,
|
|
1273
|
-
};
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
|
-
async function readWorkspacePackageScripts(workspaceRoot: string): Promise<{
|
|
1277
|
-
scripts: string[];
|
|
1278
|
-
error?: string;
|
|
1279
|
-
}> {
|
|
1280
|
-
try {
|
|
1281
|
-
const raw = await readFile(resolve(workspaceRoot, "package.json"), "utf8");
|
|
1282
|
-
const parsed = JSON.parse(raw);
|
|
1283
|
-
const scripts = parsed && typeof parsed === "object" && parsed.scripts && typeof parsed.scripts === "object"
|
|
1284
|
-
? Object.keys(parsed.scripts).sort()
|
|
1285
|
-
: [];
|
|
1286
|
-
return { scripts };
|
|
1287
|
-
} catch (error) {
|
|
1288
|
-
return {
|
|
1289
|
-
scripts: [],
|
|
1290
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1291
|
-
};
|
|
1292
|
-
}
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
|
-
function formatAvailableScripts(scripts: string[]) {
|
|
1296
|
-
return scripts.length ? scripts.join(", ") : "(none)";
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
|
-
async function runWorkspacePackageScript(args: {
|
|
1300
|
-
workspaceRoot: string;
|
|
1301
|
-
script: string;
|
|
1302
|
-
extraArgs?: string[];
|
|
1303
|
-
commandTimeoutMs?: number;
|
|
1304
|
-
}): Promise<AgentRuntimeToolResult> {
|
|
1305
|
-
const script = args.script.trim();
|
|
1306
|
-
if (!script) throw new Error("package script name is required.");
|
|
1307
|
-
const extraArgs = args.extraArgs ?? [];
|
|
1308
|
-
const packageScripts = await readWorkspacePackageScripts(args.workspaceRoot);
|
|
1309
|
-
if (!packageScripts.scripts.includes(script)) {
|
|
1310
|
-
return {
|
|
1311
|
-
content: [
|
|
1312
|
-
`script not found: ${script}`,
|
|
1313
|
-
`available scripts: ${formatAvailableScripts(packageScripts.scripts)}`,
|
|
1314
|
-
...(packageScripts.error ? [`package.json read error: ${packageScripts.error}`] : []),
|
|
1315
|
-
].join("\n"),
|
|
1316
|
-
metadata: {
|
|
1317
|
-
script,
|
|
1318
|
-
exitCode: 1,
|
|
1319
|
-
reason: "script-not-found",
|
|
1320
|
-
availableScripts: packageScripts.scripts,
|
|
1321
|
-
...(packageScripts.error ? { packageJsonError: packageScripts.error } : {}),
|
|
1322
|
-
},
|
|
1323
|
-
};
|
|
1324
|
-
}
|
|
1325
|
-
const result = await runWorkspaceCommand({
|
|
1326
|
-
workspaceRoot: args.workspaceRoot,
|
|
1327
|
-
command: ["bun", "run", script, ...(extraArgs.length ? ["--", ...extraArgs] : [])],
|
|
1328
|
-
timeoutMs: args.commandTimeoutMs,
|
|
1329
|
-
});
|
|
1330
|
-
return {
|
|
1331
|
-
content: result.content,
|
|
1332
|
-
metadata: {
|
|
1333
|
-
script,
|
|
1334
|
-
args: extraArgs,
|
|
1335
|
-
exitCode: result.exitCode,
|
|
1336
|
-
reason: result.exitCode === 0 ? "ok" : "script-failed",
|
|
1337
|
-
timedOut: result.timedOut,
|
|
1338
|
-
stdoutTail: result.stdout.trim().slice(-4000),
|
|
1339
|
-
stderrTail: result.stderr.trim().slice(-4000),
|
|
1340
|
-
},
|
|
1341
|
-
};
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
function truncateToolOutput(value: string, limit = 20_000) {
|
|
1345
|
-
if (value.length <= limit) return value;
|
|
1346
|
-
return `${value.slice(0, limit)}\n\n[truncated ${value.length - limit} chars]`;
|
|
1347
|
-
}
|
|
1348
|
-
|
|
1349
|
-
function parseLastJsonObject(text: string): Record<string, unknown> | null {
|
|
1350
|
-
const trimmed = text.trim();
|
|
1351
|
-
for (let index = trimmed.lastIndexOf("{"); index >= 0; index = trimmed.lastIndexOf("{", index - 1)) {
|
|
1352
|
-
try {
|
|
1353
|
-
const parsed = JSON.parse(trimmed.slice(index));
|
|
1354
|
-
return parsed && typeof parsed === "object" ? parsed as Record<string, unknown> : null;
|
|
1355
|
-
} catch {
|
|
1356
|
-
// Try the previous opening brace.
|
|
1357
|
-
}
|
|
1358
|
-
}
|
|
1359
|
-
return null;
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
async function writeProcessInput(proc: any, input: string) {
|
|
1363
|
-
const stdin = proc.stdin;
|
|
1364
|
-
if (!stdin) return;
|
|
1365
|
-
const encoded = new TextEncoder().encode(input);
|
|
1366
|
-
if (typeof stdin.getWriter === "function") {
|
|
1367
|
-
const writer = stdin.getWriter();
|
|
1368
|
-
try {
|
|
1369
|
-
await writer.write(encoded);
|
|
1370
|
-
await writer.close();
|
|
1371
|
-
} finally {
|
|
1372
|
-
writer.releaseLock();
|
|
1373
|
-
}
|
|
1374
|
-
return;
|
|
1375
|
-
}
|
|
1376
|
-
if (typeof stdin.write === "function") {
|
|
1377
|
-
await stdin.write(encoded);
|
|
1378
|
-
await stdin.end?.();
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
|
-
async function runWorkspaceCommand(args: {
|
|
1383
|
-
workspaceRoot: string;
|
|
1384
|
-
command: string[];
|
|
1385
|
-
stdin?: string;
|
|
1386
|
-
timeoutMs?: number;
|
|
1387
|
-
outputLimit?: number;
|
|
1388
|
-
commandPrefix?: string[];
|
|
1389
|
-
}) {
|
|
1390
|
-
const timeoutMs =
|
|
1391
|
-
typeof args.timeoutMs === "number" && Number.isFinite(args.timeoutMs) && args.timeoutMs > 0
|
|
1392
|
-
? args.timeoutMs
|
|
1393
|
-
: undefined;
|
|
1394
|
-
const detached = process.platform !== "win32";
|
|
1395
|
-
const command = [
|
|
1396
|
-
...(args.commandPrefix ?? []),
|
|
1397
|
-
...args.command,
|
|
1398
|
-
];
|
|
1399
|
-
const proc = Bun.spawn(command, {
|
|
1400
|
-
cwd: resolve(args.workspaceRoot),
|
|
1401
|
-
stdout: "pipe",
|
|
1402
|
-
stderr: "pipe",
|
|
1403
|
-
stdin: args.stdin === undefined ? "ignore" : "pipe",
|
|
1404
|
-
detached,
|
|
1405
|
-
});
|
|
1406
|
-
if (args.stdin !== undefined) await writeProcessInput(proc, args.stdin);
|
|
1407
|
-
const stdoutPromise = new Response(proc.stdout).text();
|
|
1408
|
-
const stderrPromise = new Response(proc.stderr).text();
|
|
1409
|
-
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
1410
|
-
const timeoutResult = Symbol("timeout");
|
|
1411
|
-
const exitOrTimeout = timeoutMs
|
|
1412
|
-
? await Promise.race([
|
|
1413
|
-
proc.exited,
|
|
1414
|
-
new Promise<typeof timeoutResult>((resolveTimeout) => {
|
|
1415
|
-
timeout = setTimeout(() => resolveTimeout(timeoutResult), timeoutMs);
|
|
1416
|
-
}),
|
|
1417
|
-
])
|
|
1418
|
-
: await proc.exited;
|
|
1419
|
-
if (timeout) clearTimeout(timeout);
|
|
1420
|
-
const timedOut = exitOrTimeout === timeoutResult;
|
|
1421
|
-
if (timedOut) {
|
|
1422
|
-
const kill = (signal: NodeJS.Signals) => {
|
|
1423
|
-
if (detached && typeof proc.pid === "number") {
|
|
1424
|
-
try {
|
|
1425
|
-
process.kill(-proc.pid, signal);
|
|
1426
|
-
return;
|
|
1427
|
-
} catch {
|
|
1428
|
-
// Fall through to killing the immediate child.
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
|
-
try {
|
|
1432
|
-
proc.kill(signal);
|
|
1433
|
-
} catch {
|
|
1434
|
-
// The command may have exited after the timeout won the race.
|
|
1435
|
-
}
|
|
1436
|
-
};
|
|
1437
|
-
kill("SIGTERM");
|
|
1438
|
-
await Promise.race([
|
|
1439
|
-
proc.exited.catch(() => 124),
|
|
1440
|
-
new Promise((resolveKill) => setTimeout(resolveKill, 500)),
|
|
1441
|
-
]);
|
|
1442
|
-
kill("SIGKILL");
|
|
1443
|
-
}
|
|
1444
|
-
const [stdout, rawStderr] = await Promise.all([
|
|
1445
|
-
stdoutPromise,
|
|
1446
|
-
stderrPromise,
|
|
1447
|
-
]);
|
|
1448
|
-
const exitCode = timedOut ? 124 : Number(exitOrTimeout);
|
|
1449
|
-
const stderr = timedOut
|
|
1450
|
-
? `${rawStderr.trim() ? `${rawStderr.trim()}\n` : ""}command timed out after ${timeoutMs ?? "unknown"}ms\n`
|
|
1451
|
-
: rawStderr;
|
|
1452
|
-
return {
|
|
1453
|
-
stdout,
|
|
1454
|
-
stderr,
|
|
1455
|
-
exitCode,
|
|
1456
|
-
timedOut,
|
|
1457
|
-
content: truncateToolOutput([
|
|
1458
|
-
stdout.trim() ? `stdout:\n${stdout.trim()}` : "",
|
|
1459
|
-
stderr.trim() ? `stderr:\n${stderr.trim()}` : "",
|
|
1460
|
-
`exitCode: ${exitCode}`,
|
|
1461
|
-
].filter(Boolean).join("\n\n"), args.outputLimit),
|
|
1462
|
-
};
|
|
1463
|
-
}
|
|
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
|
-
|
|
1525
|
-
export function resolveLocalWorkspaceToolPath(args: {
|
|
1526
|
-
workspaceRoot: string;
|
|
1527
|
-
requestedPath: string;
|
|
1528
|
-
}) {
|
|
1529
|
-
const workspaceRoot = resolve(args.workspaceRoot);
|
|
1530
|
-
const targetPath = resolve(workspaceRoot, args.requestedPath);
|
|
1531
|
-
if (!isPathInsideWorkspace({ workspaceRoot, targetPath })) {
|
|
1532
|
-
throw new Error(`Workspace tool path escapes workspace root: ${args.requestedPath}`);
|
|
1533
|
-
}
|
|
1534
|
-
return targetPath;
|
|
1535
|
-
}
|
|
1536
|
-
|
|
1537
|
-
async function readFileTool(args: {
|
|
1538
|
-
call: AgentRuntimeToolCallInput;
|
|
1539
|
-
workspaceRoot: string;
|
|
1540
|
-
}): Promise<AgentRuntimeToolResult> {
|
|
1541
|
-
const parsed = parseWorkspaceToolArguments(args.call.arguments);
|
|
1542
|
-
const requestedPath = requireWorkspaceToolPath(parsed);
|
|
1543
|
-
const sliceArgs = readFileSliceArgs(parsed);
|
|
1544
|
-
const absolutePath = resolveLocalWorkspaceToolPath({
|
|
1545
|
-
workspaceRoot: args.workspaceRoot,
|
|
1546
|
-
requestedPath,
|
|
1547
|
-
});
|
|
1548
|
-
const content = await readFile(absolutePath, "utf8");
|
|
1549
|
-
const activity = extractActivity(parsed);
|
|
1550
|
-
const sliced = sliceReadFileContent({
|
|
1551
|
-
content,
|
|
1552
|
-
...sliceArgs,
|
|
1553
|
-
});
|
|
1554
|
-
return {
|
|
1555
|
-
content: sliced.content,
|
|
1556
|
-
metadata: {
|
|
1557
|
-
path: normalizeWorkspaceRelativePath({
|
|
1558
|
-
workspaceRoot: resolve(args.workspaceRoot),
|
|
1559
|
-
targetPath: absolutePath,
|
|
1560
|
-
}),
|
|
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 } : {}),
|
|
1570
|
-
},
|
|
1571
|
-
};
|
|
1572
|
-
}
|
|
1573
|
-
|
|
1574
|
-
async function writeFileTool(args: {
|
|
1575
|
-
call: AgentRuntimeToolCallInput;
|
|
1576
|
-
workspaceRoot: string;
|
|
1577
|
-
}): Promise<AgentRuntimeToolResult> {
|
|
1578
|
-
// TODO: Explore cheaply including diff stat/EOL warning after writeFile.
|
|
1579
|
-
// If implementation is larger than a small safe change, defer it.
|
|
1580
|
-
// Exact boundaries: writeFileTool function in localWorkspaceTools.ts.
|
|
1581
|
-
// Consider adding a post-write hook that runs `git diff --stat` and checks line endings.
|
|
1582
|
-
// Keep it optional and non-blocking.
|
|
1583
|
-
const parsed = parseWorkspaceToolArguments(args.call.arguments);
|
|
1584
|
-
const requestedPath = requireWorkspaceToolPath(parsed);
|
|
1585
|
-
const content = requireWorkspaceFileContent(parsed);
|
|
1586
|
-
const absolutePath = resolveLocalWorkspaceToolPath({
|
|
1587
|
-
workspaceRoot: args.workspaceRoot,
|
|
1588
|
-
requestedPath,
|
|
1589
|
-
});
|
|
1590
|
-
await mkdir(dirname(absolutePath), { recursive: true });
|
|
1591
|
-
await writeFile(absolutePath, content, "utf8");
|
|
1592
|
-
const relativePath = normalizeWorkspaceRelativePath({
|
|
1593
|
-
workspaceRoot: resolve(args.workspaceRoot),
|
|
1594
|
-
targetPath: absolutePath,
|
|
1595
|
-
});
|
|
1596
|
-
const activity = extractActivity(parsed);
|
|
1597
|
-
return {
|
|
1598
|
-
content: `wrote ${relativePath}`,
|
|
1599
|
-
metadata: {
|
|
1600
|
-
path: relativePath,
|
|
1601
|
-
bytes: Buffer.byteLength(content),
|
|
1602
|
-
...(activity ? { activity } : {}),
|
|
1603
|
-
},
|
|
1604
|
-
};
|
|
1605
|
-
}
|
|
1606
|
-
|
|
1607
|
-
async function editFileTool(args: {
|
|
1608
|
-
call: AgentRuntimeToolCallInput;
|
|
1609
|
-
workspaceRoot: string;
|
|
1610
|
-
}): Promise<AgentRuntimeToolResult> {
|
|
1611
|
-
const parsed = parseWorkspaceToolArguments(args.call.arguments);
|
|
1612
|
-
const requestedPath = requireWorkspaceToolPath(parsed);
|
|
1613
|
-
const oldText = requireWorkspaceOldText(parsed);
|
|
1614
|
-
const newText = requireWorkspaceNewText(parsed);
|
|
1615
|
-
const expectedReplacements = readExpectedReplacementCount(parsed);
|
|
1616
|
-
const absolutePath = resolveLocalWorkspaceToolPath({
|
|
1617
|
-
workspaceRoot: args.workspaceRoot,
|
|
1618
|
-
requestedPath,
|
|
1619
|
-
});
|
|
1620
|
-
const content = await readFile(absolutePath, "utf8");
|
|
1621
|
-
const replacementCount = countExactTextOccurrences({ content, oldText });
|
|
1622
|
-
if (replacementCount !== expectedReplacements) {
|
|
1623
|
-
throw new Error(
|
|
1624
|
-
`editFile expected ${expectedReplacements} ${pluralizeReplacement(expectedReplacements)} ` +
|
|
1625
|
-
`but found ${replacementCount} in ${requestedPath}.`
|
|
1626
|
-
);
|
|
1627
|
-
}
|
|
1628
|
-
const nextContent = content.split(oldText).join(newText);
|
|
1629
|
-
await writeFile(absolutePath, nextContent, "utf8");
|
|
1630
|
-
const relativePath = normalizeWorkspaceRelativePath({
|
|
1631
|
-
workspaceRoot: resolve(args.workspaceRoot),
|
|
1632
|
-
targetPath: absolutePath,
|
|
1633
|
-
});
|
|
1634
|
-
const activity = extractActivity(parsed);
|
|
1635
|
-
return {
|
|
1636
|
-
content: `replaced ${replacementCount} occurrence${replacementCount === 1 ? "" : "s"} in ${relativePath}`,
|
|
1637
|
-
metadata: {
|
|
1638
|
-
path: relativePath,
|
|
1639
|
-
replacements: replacementCount,
|
|
1640
|
-
bytes: Buffer.byteLength(nextContent),
|
|
1641
|
-
...(activity ? { activity } : {}),
|
|
1642
|
-
},
|
|
1643
|
-
};
|
|
1644
|
-
}
|
|
1645
|
-
|
|
1646
|
-
async function formatWorkspaceDirEntry(args: {
|
|
1647
|
-
workspaceRoot: string;
|
|
1648
|
-
dirPath: string;
|
|
1649
|
-
name: string;
|
|
1650
|
-
}) {
|
|
1651
|
-
const absolutePath = resolve(args.dirPath, args.name);
|
|
1652
|
-
const info = await stat(absolutePath);
|
|
1653
|
-
const relativePath = normalizeWorkspaceRelativePath({
|
|
1654
|
-
workspaceRoot: resolve(args.workspaceRoot),
|
|
1655
|
-
targetPath: absolutePath,
|
|
1656
|
-
});
|
|
1657
|
-
return info.isDirectory() ? `${relativePath}/` : relativePath;
|
|
1658
|
-
}
|
|
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
|
-
|
|
1712
|
-
async function listFilesTool(args: {
|
|
1713
|
-
call: AgentRuntimeToolCallInput;
|
|
1714
|
-
workspaceRoot: string;
|
|
1715
|
-
}): Promise<AgentRuntimeToolResult> {
|
|
1716
|
-
const parsed = parseWorkspaceToolArguments(args.call.arguments);
|
|
1717
|
-
const requestedPath = typeof parsed.path === "string" && parsed.path.trim()
|
|
1718
|
-
? parsed.path.trim()
|
|
1719
|
-
: ".";
|
|
1720
|
-
const maxDepth = readWorkspaceMaxDepth(parsed);
|
|
1721
|
-
const maxResults = readWorkspaceMaxResults(parsed);
|
|
1722
|
-
const entryType = readWorkspaceEntryType(parsed);
|
|
1723
|
-
const dirPath = resolveLocalWorkspaceToolPath({
|
|
1724
|
-
workspaceRoot: args.workspaceRoot,
|
|
1725
|
-
requestedPath,
|
|
1726
|
-
});
|
|
1727
|
-
const listed = await listWorkspaceEntries({
|
|
1728
|
-
workspaceRoot: args.workspaceRoot,
|
|
1729
|
-
dirPath,
|
|
1730
|
-
maxDepth,
|
|
1731
|
-
maxResults,
|
|
1732
|
-
entryType,
|
|
1733
|
-
});
|
|
1734
|
-
return {
|
|
1735
|
-
content: listed.entries.join("\n"),
|
|
1736
|
-
metadata: {
|
|
1737
|
-
path: requestedPath,
|
|
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 } : {}),
|
|
1746
|
-
},
|
|
1747
|
-
};
|
|
1748
|
-
}
|
|
1749
|
-
|
|
1750
|
-
async function searchFilesTool(args: {
|
|
1751
|
-
call: AgentRuntimeToolCallInput;
|
|
1752
|
-
workspaceRoot: string;
|
|
1753
|
-
}): Promise<AgentRuntimeToolResult> {
|
|
1754
|
-
const parsed = parseWorkspaceToolArguments(args.call.arguments);
|
|
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;
|
|
1761
|
-
const requestedPath = typeof parsed.path === "string" && parsed.path.trim()
|
|
1762
|
-
? parsed.path.trim()
|
|
1763
|
-
: ".";
|
|
1764
|
-
const includeIgnored = parsed.includeIgnored === true;
|
|
1765
|
-
const searchPath = resolveLocalWorkspaceToolPath({
|
|
1766
|
-
workspaceRoot: args.workspaceRoot,
|
|
1767
|
-
requestedPath,
|
|
1768
|
-
});
|
|
1769
|
-
const relativeSearchPath = normalizeWorkspaceRelativePath({
|
|
1770
|
-
workspaceRoot: resolve(args.workspaceRoot),
|
|
1771
|
-
targetPath: searchPath,
|
|
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
|
-
];
|
|
1805
|
-
const result = await (async () => {
|
|
1806
|
-
try {
|
|
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
|
-
});
|
|
1817
|
-
} catch {
|
|
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
|
-
});
|
|
1828
|
-
}
|
|
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
|
-
})));
|
|
1859
|
-
return {
|
|
1860
|
-
content: outputLines.join("\n"),
|
|
1861
|
-
metadata: {
|
|
1862
|
-
query,
|
|
1863
|
-
path: requestedPath,
|
|
1864
|
-
searchedPath: relativeSearchPath,
|
|
1865
|
-
exclude,
|
|
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 } : {}),
|
|
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 } : {}),
|
|
2103
|
-
},
|
|
2104
|
-
};
|
|
2105
|
-
}
|
|
2106
|
-
|
|
2107
|
-
async function resolveVisualStateBaseUrl(args: {
|
|
2108
|
-
workspaceRoot: string;
|
|
2109
|
-
explicitBaseUrl?: string;
|
|
2110
|
-
commandTimeoutMs?: number;
|
|
2111
|
-
}) {
|
|
2112
|
-
if (args.explicitBaseUrl) return args.explicitBaseUrl;
|
|
2113
|
-
const statusResult = await runWorkspaceCommand({
|
|
2114
|
-
workspaceRoot: args.workspaceRoot,
|
|
2115
|
-
command: ["bun", "run", "preview:status"],
|
|
2116
|
-
timeoutMs: args.commandTimeoutMs,
|
|
2117
|
-
});
|
|
2118
|
-
if (statusResult.exitCode !== 0) throw new Error(statusResult.content);
|
|
2119
|
-
const status = parseLastJsonObject(statusResult.stdout);
|
|
2120
|
-
const localApiOrigin = typeof status?.localApiOrigin === "string" ? status.localApiOrigin : "";
|
|
2121
|
-
if (!localApiOrigin) {
|
|
2122
|
-
throw new Error("captureVisualState could not read localApiOrigin from preview:status.");
|
|
2123
|
-
}
|
|
2124
|
-
return localApiOrigin;
|
|
2125
|
-
}
|
|
2126
|
-
|
|
2127
|
-
async function captureVisualStateTool(args: {
|
|
2128
|
-
call: AgentRuntimeToolCallInput;
|
|
2129
|
-
workspaceRoot: string;
|
|
2130
|
-
commandTimeoutMs?: number;
|
|
2131
|
-
}): Promise<AgentRuntimeToolResult> {
|
|
2132
|
-
const parsed = parseWorkspaceToolArguments(args.call.arguments);
|
|
2133
|
-
const captureArgs = readVisualStateCaptureArgs(parsed);
|
|
2134
|
-
const baseUrl = await resolveVisualStateBaseUrl({
|
|
2135
|
-
workspaceRoot: args.workspaceRoot,
|
|
2136
|
-
explicitBaseUrl: captureArgs.baseUrl,
|
|
2137
|
-
commandTimeoutMs: args.commandTimeoutMs,
|
|
2138
|
-
});
|
|
2139
|
-
const extraArgs = [
|
|
2140
|
-
"--base",
|
|
2141
|
-
baseUrl,
|
|
2142
|
-
"--path",
|
|
2143
|
-
captureArgs.path,
|
|
2144
|
-
"--wait-selector",
|
|
2145
|
-
captureArgs.waitSelector,
|
|
2146
|
-
...(captureArgs.scrollSelector ? ["--scroll-selector", captureArgs.scrollSelector] : []),
|
|
2147
|
-
...(captureArgs.focusSelector ? ["--focus-selector", captureArgs.focusSelector] : []),
|
|
2148
|
-
...(captureArgs.expectText ? ["--expect-text", captureArgs.expectText] : []),
|
|
2149
|
-
"--screenshot",
|
|
2150
|
-
captureArgs.screenshotPath,
|
|
2151
|
-
"--metrics",
|
|
2152
|
-
captureArgs.metricsPath,
|
|
2153
|
-
];
|
|
2154
|
-
const result = await runWorkspacePackageScript({
|
|
2155
|
-
workspaceRoot: args.workspaceRoot,
|
|
2156
|
-
script: "probe:visual-review",
|
|
2157
|
-
extraArgs,
|
|
2158
|
-
commandTimeoutMs: args.commandTimeoutMs,
|
|
2159
|
-
});
|
|
2160
|
-
const metrics = parseLastJsonObject(String(result.metadata?.stdoutTail ?? result.content));
|
|
2161
|
-
return {
|
|
2162
|
-
content: [
|
|
2163
|
-
`pageUrl: ${typeof metrics?.pageUrl === "string" ? metrics.pageUrl : ""}`,
|
|
2164
|
-
`screenshotPath: ${captureArgs.screenshotPath}`,
|
|
2165
|
-
`metricsPath: ${captureArgs.metricsPath}`,
|
|
2166
|
-
`waitSelector: ${captureArgs.waitSelector}`,
|
|
2167
|
-
...(captureArgs.focusSelector ? [`focusSelector: ${captureArgs.focusSelector}`] : []),
|
|
2168
|
-
...(captureArgs.expectText ? [`expectText: ${captureArgs.expectText}`] : []),
|
|
2169
|
-
"",
|
|
2170
|
-
result.content,
|
|
2171
|
-
].join("\n").trim(),
|
|
2172
|
-
metadata: {
|
|
2173
|
-
...result.metadata,
|
|
2174
|
-
script: "probe:visual-review",
|
|
2175
|
-
args: extraArgs,
|
|
2176
|
-
baseUrl,
|
|
2177
|
-
path: captureArgs.path,
|
|
2178
|
-
waitSelector: captureArgs.waitSelector,
|
|
2179
|
-
scrollSelector: captureArgs.scrollSelector,
|
|
2180
|
-
focusSelector: captureArgs.focusSelector,
|
|
2181
|
-
expectText: captureArgs.expectText,
|
|
2182
|
-
screenshotPath: captureArgs.screenshotPath,
|
|
2183
|
-
metricsPath: captureArgs.metricsPath,
|
|
2184
|
-
...(metrics ?? {}),
|
|
2185
|
-
},
|
|
2186
|
-
};
|
|
2187
|
-
}
|
|
2188
|
-
|
|
2189
|
-
async function previewLifecycleTool(args: {
|
|
2190
|
-
call: AgentRuntimeToolCallInput;
|
|
2191
|
-
workspaceRoot: string;
|
|
2192
|
-
commandTimeoutMs?: number;
|
|
2193
|
-
script: "preview:start" | "preview:status" | "preview:stop" | "preview:release";
|
|
2194
|
-
}): Promise<AgentRuntimeToolResult> {
|
|
2195
|
-
const result = await runWorkspacePackageScript({
|
|
2196
|
-
workspaceRoot: args.workspaceRoot,
|
|
2197
|
-
script: args.script,
|
|
2198
|
-
commandTimeoutMs: args.commandTimeoutMs,
|
|
2199
|
-
});
|
|
2200
|
-
const summary = parseLastJsonObject(String(result.metadata?.stdoutTail ?? result.content)) ?? {};
|
|
2201
|
-
return {
|
|
2202
|
-
content: [
|
|
2203
|
-
typeof summary.previewUrl === "string" ? `previewUrl: ${summary.previewUrl}` : "",
|
|
2204
|
-
typeof summary.localApiOrigin === "string" ? `localApiOrigin: ${summary.localApiOrigin}` : "",
|
|
2205
|
-
typeof summary.serverDbPath === "string" ? `serverDbPath: ${summary.serverDbPath}` : "",
|
|
2206
|
-
"",
|
|
2207
|
-
result.content,
|
|
2208
|
-
].filter((line, index, lines) => line || (index > 0 && lines[index - 1])).join("\n").trim(),
|
|
2209
|
-
metadata: {
|
|
2210
|
-
...result.metadata,
|
|
2211
|
-
script: args.script,
|
|
2212
|
-
...(summary ?? {}),
|
|
2213
|
-
},
|
|
2214
|
-
};
|
|
2215
|
-
}
|
|
2216
|
-
|
|
2217
|
-
async function execShellTool(args: {
|
|
2218
|
-
call: AgentRuntimeToolCallInput;
|
|
2219
|
-
workspaceRoot: string;
|
|
2220
|
-
commandTimeoutMs?: number;
|
|
2221
|
-
commandPrefix?: string[];
|
|
2222
|
-
}): Promise<AgentRuntimeToolResult> {
|
|
2223
|
-
const parsed = parseWorkspaceToolArguments(args.call.arguments);
|
|
2224
|
-
const command = requireShellCommand(parsed, args.call.name);
|
|
2225
|
-
const result = await runWorkspaceCommand({
|
|
2226
|
-
workspaceRoot: args.workspaceRoot,
|
|
2227
|
-
command: buildWorkspaceShellCommand({
|
|
2228
|
-
toolName: args.call.name,
|
|
2229
|
-
command,
|
|
2230
|
-
shell: parsed.shell,
|
|
2231
|
-
}),
|
|
2232
|
-
timeoutMs: args.commandTimeoutMs,
|
|
2233
|
-
outputLimit: args.commandOutputLimit,
|
|
2234
|
-
commandPrefix: args.commandPrefix,
|
|
2235
|
-
});
|
|
2236
|
-
const activity = extractActivity(parsed);
|
|
2237
|
-
return {
|
|
2238
|
-
content: result.content,
|
|
2239
|
-
metadata: {
|
|
2240
|
-
exitCode: result.exitCode,
|
|
2241
|
-
timedOut: result.timedOut,
|
|
2242
|
-
...(activity ? { activity } : {}),
|
|
2243
|
-
},
|
|
2244
|
-
};
|
|
2245
|
-
}
|
|
2246
|
-
|
|
2247
|
-
export function createLocalWorkspaceToolExecutors(args: LocalWorkspaceToolArgs) {
|
|
2248
|
-
return {
|
|
2249
|
-
editFile: (call: AgentRuntimeToolCallInput) => editFileTool({
|
|
2250
|
-
call,
|
|
2251
|
-
workspaceRoot: args.workspaceRoot,
|
|
2252
|
-
}),
|
|
2253
|
-
globFiles: (call: AgentRuntimeToolCallInput) => globFilesTool({
|
|
2254
|
-
call,
|
|
2255
|
-
workspaceRoot: args.workspaceRoot,
|
|
2256
|
-
}),
|
|
2257
|
-
listFiles: (call: AgentRuntimeToolCallInput) => listFilesTool({
|
|
2258
|
-
call,
|
|
2259
|
-
workspaceRoot: args.workspaceRoot,
|
|
2260
|
-
}),
|
|
2261
|
-
readFile: (call: AgentRuntimeToolCallInput) => readFileTool({
|
|
2262
|
-
call,
|
|
2263
|
-
workspaceRoot: args.workspaceRoot,
|
|
2264
|
-
}),
|
|
2265
|
-
writeFile: (call: AgentRuntimeToolCallInput) => writeFileTool({
|
|
2266
|
-
call,
|
|
2267
|
-
workspaceRoot: args.workspaceRoot,
|
|
2268
|
-
}),
|
|
2269
|
-
searchFiles: (call: AgentRuntimeToolCallInput) => searchFilesTool({
|
|
2270
|
-
call,
|
|
2271
|
-
workspaceRoot: args.workspaceRoot,
|
|
2272
|
-
}),
|
|
2273
|
-
startPreview: (call: AgentRuntimeToolCallInput) => previewLifecycleTool({
|
|
2274
|
-
call,
|
|
2275
|
-
workspaceRoot: args.workspaceRoot,
|
|
2276
|
-
commandTimeoutMs: args.commandTimeoutMs,
|
|
2277
|
-
script: "preview:start",
|
|
2278
|
-
}),
|
|
2279
|
-
getPreviewStatus: (call: AgentRuntimeToolCallInput) => previewLifecycleTool({
|
|
2280
|
-
call,
|
|
2281
|
-
workspaceRoot: args.workspaceRoot,
|
|
2282
|
-
commandTimeoutMs: args.commandTimeoutMs,
|
|
2283
|
-
script: "preview:status",
|
|
2284
|
-
}),
|
|
2285
|
-
stopPreview: (call: AgentRuntimeToolCallInput) => previewLifecycleTool({
|
|
2286
|
-
call,
|
|
2287
|
-
workspaceRoot: args.workspaceRoot,
|
|
2288
|
-
commandTimeoutMs: args.commandTimeoutMs,
|
|
2289
|
-
script: "preview:stop",
|
|
2290
|
-
}),
|
|
2291
|
-
releasePreview: (call: AgentRuntimeToolCallInput) => previewLifecycleTool({
|
|
2292
|
-
call,
|
|
2293
|
-
workspaceRoot: args.workspaceRoot,
|
|
2294
|
-
commandTimeoutMs: args.commandTimeoutMs,
|
|
2295
|
-
script: "preview:release",
|
|
2296
|
-
}),
|
|
2297
|
-
captureVisualState: (call: AgentRuntimeToolCallInput) => captureVisualStateTool({
|
|
2298
|
-
call,
|
|
2299
|
-
workspaceRoot: args.workspaceRoot,
|
|
2300
|
-
commandTimeoutMs: args.commandTimeoutMs,
|
|
2301
|
-
}),
|
|
2302
|
-
execShell: (call: AgentRuntimeToolCallInput) => execShellTool({
|
|
2303
|
-
call,
|
|
2304
|
-
workspaceRoot: args.workspaceRoot,
|
|
2305
|
-
commandTimeoutMs: args.commandTimeoutMs,
|
|
2306
|
-
commandOutputLimit: args.commandOutputLimit,
|
|
2307
|
-
commandPrefix: args.commandPrefix,
|
|
2308
|
-
}),
|
|
2309
|
-
};
|
|
2310
|
-
}
|