nolo-cli 0.1.45 → 0.1.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (615) hide show
  1. package/README.md +272 -118
  2. package/index.js +73433 -0
  3. package/package.json +10 -635
  4. package/LICENSE +0 -21
  5. package/agent-runtime/agentConfigOptions.ts +0 -12
  6. package/agent-runtime/agentRecordConfig.ts +0 -101
  7. package/agent-runtime/agentRecordKeys.ts +0 -14
  8. package/agent-runtime/agentThread.ts +0 -222
  9. package/agent-runtime/agentThreadAdmission.ts +0 -74
  10. package/agent-runtime/dialogMessageRecord.ts +0 -16
  11. package/agent-runtime/dialogWritePlan.ts +0 -211
  12. package/agent-runtime/hostAdapter.ts +0 -81
  13. package/agent-runtime/hybridRecordStore.ts +0 -170
  14. package/agent-runtime/index.ts +0 -184
  15. package/agent-runtime/localDialogRead.ts +0 -23
  16. package/agent-runtime/localLoop.ts +0 -334
  17. package/agent-runtime/localToolPolicy.ts +0 -179
  18. package/agent-runtime/localWorkspaceTools.ts +0 -2310
  19. package/agent-runtime/noloWorkspaceTools.ts +0 -390
  20. package/agent-runtime/openAiCompatibleProvider.ts +0 -74
  21. package/agent-runtime/openAiCompatibleProviderConfig.ts +0 -27
  22. package/agent-runtime/platformChatProvider.ts +0 -229
  23. package/agent-runtime/providerResolution.ts +0 -302
  24. package/agent-runtime/runtimeDecision.ts +0 -70
  25. package/agent-runtime/runtimeFacts.ts +0 -40
  26. package/agent-runtime/runtimeToolPolicy.ts +0 -191
  27. package/agent-runtime/runtimeToolSurface.ts +0 -199
  28. package/agent-runtime/shellCommandPolicy.ts +0 -120
  29. package/agent-runtime/types.ts +0 -137
  30. package/agentAliases.ts +0 -40
  31. package/agentCommandSupport.ts +0 -81
  32. package/agentInternalCommandEntries.ts +0 -44
  33. package/agentListCommands.ts +0 -185
  34. package/agentListHelpers.ts +0 -242
  35. package/agentMachineCommands.ts +0 -426
  36. package/agentNameResolver.ts +0 -164
  37. package/agentPullCommand.ts +0 -142
  38. package/agentRecordCommands.ts +0 -224
  39. package/agentRecordHelpers.ts +0 -592
  40. package/agentRunCommand.ts +0 -568
  41. package/agentRuntimeCommands.ts +0 -7
  42. package/agentRuntimeLocal.ts +0 -45
  43. package/ai/agent/_executeModel.ts +0 -146
  44. package/ai/agent/agentSlice.ts +0 -557
  45. package/ai/agent/appWorkingMemory.ts +0 -126
  46. package/ai/agent/buildEditingContext.ts +0 -378
  47. package/ai/agent/buildSystemPrompt.ts +0 -599
  48. package/ai/agent/canvasEditingContext.ts +0 -49
  49. package/ai/agent/cleanAgentMessages.ts +0 -140
  50. package/ai/agent/cliChatClient.ts +0 -153
  51. package/ai/agent/cliExecutor.ts +0 -1465
  52. package/ai/agent/cliPrompt.ts +0 -10
  53. package/ai/agent/contextCompiler.ts +0 -107
  54. package/ai/agent/contextLayerContract.ts +0 -44
  55. package/ai/agent/createAgentSchema.ts +0 -272
  56. package/ai/agent/currentTimeContext.ts +0 -65
  57. package/ai/agent/executeToolCall.ts +0 -58
  58. package/ai/agent/fetchAgentContexts.ts +0 -42
  59. package/ai/agent/generatePrompt.ts +0 -3
  60. package/ai/agent/getFullChatContextKeys.ts +0 -168
  61. package/ai/agent/guidedCreation/capabilities.ts +0 -84
  62. package/ai/agent/guidedCreation/types.ts +0 -38
  63. package/ai/agent/isLiveAudioOnlyAgent.ts +0 -26
  64. package/ai/agent/liveAudioModel.ts +0 -2
  65. package/ai/agent/machineRunPermissions.ts +0 -113
  66. package/ai/agent/pageBuilderHandoffRules.ts +0 -75
  67. package/ai/agent/persistMessageWithFixedId.ts +0 -37
  68. package/ai/agent/planSlice.ts +0 -259
  69. package/ai/agent/referenceUtils.ts +0 -229
  70. package/ai/agent/runAgentBackground.ts +0 -310
  71. package/ai/agent/runAgentClientLoop.ts +0 -164
  72. package/ai/agent/runtimeGuidance.ts +0 -116
  73. package/ai/agent/serverOrigin.ts +0 -7
  74. package/ai/agent/startParallelAgentStreams.ts +0 -424
  75. package/ai/agent/startupProtocol.ts +0 -53
  76. package/ai/agent/streamAgentChatTurn.ts +0 -2135
  77. package/ai/agent/streamAgentChatTurnUtils.ts +0 -923
  78. package/ai/agent/toolFailureGuard.ts +0 -99
  79. package/ai/agent/types.ts +0 -73
  80. package/ai/agent/utils/imageOutput.ts +0 -37
  81. package/ai/agent/utils/publicImageAgentMode.ts +0 -26
  82. package/ai/agent.ts +0 -2
  83. package/ai/ai.locale.ts +0 -1165
  84. package/ai/chat/accumulateToolCallChunks.ts +0 -126
  85. package/ai/chat/fetchUtils.ts +0 -143
  86. package/ai/chat/inlineImageUrlsForCustomProvider.ts +0 -122
  87. package/ai/chat/parseApiError.ts +0 -67
  88. package/ai/chat/parseMultilineSSE.ts +0 -95
  89. package/ai/chat/sendOpenAICompletionsRequest.ts +0 -921
  90. package/ai/chat/sendOpenAIResponseRequest.ts +0 -720
  91. package/ai/chat/serverProxyRetry.ts +0 -102
  92. package/ai/chat/shouldUseServerProxy.ts +0 -18
  93. package/ai/chat/streamRetry.ts +0 -46
  94. package/ai/chat/updateTotalUsage.ts +0 -89
  95. package/ai/context/buildReferenceContext.ts +0 -437
  96. package/ai/context/retention.ts +0 -165
  97. package/ai/context/tokenUtils.ts +0 -78
  98. package/ai/index.ts +0 -1
  99. package/ai/llm/agentCapabilities.ts +0 -74
  100. package/ai/llm/deepinfra.ts +0 -79
  101. package/ai/llm/fireworks.ts +0 -68
  102. package/ai/llm/generateRequestBody.ts +0 -165
  103. package/ai/llm/getModelContextWindow.ts +0 -93
  104. package/ai/llm/getPricing.ts +0 -252
  105. package/ai/llm/imagePricing.ts +0 -66
  106. package/ai/llm/isResponseAPIModel.ts +0 -13
  107. package/ai/llm/kimi.ts +0 -22
  108. package/ai/llm/mimo.ts +0 -99
  109. package/ai/llm/mistral.ts +0 -21
  110. package/ai/llm/models.ts +0 -45
  111. package/ai/llm/openrouterModels.ts +0 -31
  112. package/ai/llm/providers.ts +0 -313
  113. package/ai/llm/reasoningModels.ts +0 -27
  114. package/ai/llm/types.ts +0 -82
  115. package/ai/llm/usageRequestOptions.ts +0 -59
  116. package/ai/memory/policy.ts +0 -49
  117. package/ai/memory/queryShared.ts +0 -167
  118. package/ai/memory/recentRelationshipRecap.ts +0 -251
  119. package/ai/memory/remember.ts +0 -111
  120. package/ai/memory/scope.ts +0 -74
  121. package/ai/memory/store.ts +0 -20
  122. package/ai/memory/storeShared.ts +0 -76
  123. package/ai/memory/types.ts +0 -52
  124. package/ai/memory/understanding.ts +0 -341
  125. package/ai/memory/understandingGreeting.ts +0 -264
  126. package/ai/policy/personalizationDialog.ts +0 -333
  127. package/ai/policy/runtimePolicy.ts +0 -526
  128. package/ai/policy/selfUpdateFields.ts +0 -48
  129. package/ai/policy/types.ts +0 -64
  130. package/ai/skills/referenceRuntime.ts +0 -274
  131. package/ai/skills/skillDiagnostics.ts +0 -251
  132. package/ai/skills/skillDocBuilder.ts +0 -139
  133. package/ai/skills/skillDocProtocol.ts +0 -526
  134. package/ai/skills/skillSummaryMarker.ts +0 -26
  135. package/ai/token/calculatePrice.ts +0 -576
  136. package/ai/token/missingUsageEstimate.ts +0 -42
  137. package/ai/token/normalizeUsage.ts +0 -124
  138. package/ai/token/openaiImageGenerationUsage.ts +0 -56
  139. package/ai/token/prepareTokenUsageData.ts +0 -93
  140. package/ai/token/resolveBillingTarget.ts +0 -52
  141. package/ai/token/saveTokenRecord.ts +0 -53
  142. package/ai/token/types.ts +0 -103
  143. package/ai/tools/agent/agentTools.ts +0 -214
  144. package/ai/tools/agent/agentUpdateShared.ts +0 -311
  145. package/ai/tools/agent/callAgentTool.ts +0 -139
  146. package/ai/tools/agent/createAgentTool.ts +0 -512
  147. package/ai/tools/agent/createDialogTool.ts +0 -69
  148. package/ai/tools/agent/createSkillAgentTool.ts +0 -62
  149. package/ai/tools/agent/parallelBudget.ts +0 -221
  150. package/ai/tools/agent/prepareAgentDraftTool.ts +0 -159
  151. package/ai/tools/agent/presets/appBuilderPreset.ts +0 -154
  152. package/ai/tools/agent/runLlmTool.ts +0 -96
  153. package/ai/tools/agent/runStreamingAgentTool.ts +0 -73
  154. package/ai/tools/agent/skillAgentArgs.ts +0 -106
  155. package/ai/tools/agent/startAgentDialogTool.ts +0 -54
  156. package/ai/tools/agent/streamParallelAgentsTool.ts +0 -123
  157. package/ai/tools/agent/updateAgentTool.ts +0 -96
  158. package/ai/tools/agent/updateSelfTool.ts +0 -113
  159. package/ai/tools/amazonProductScraperTool.ts +0 -86
  160. package/ai/tools/apifyActorClient.ts +0 -45
  161. package/ai/tools/appEditGuard.ts +0 -372
  162. package/ai/tools/appReadSnapshot.ts +0 -153
  163. package/ai/tools/appTools.ts +0 -1549
  164. package/ai/tools/applyEditTool.ts +0 -259
  165. package/ai/tools/applyLineEditsTool.ts +0 -315
  166. package/ai/tools/browserTools/click.ts +0 -33
  167. package/ai/tools/browserTools/closeSession.ts +0 -29
  168. package/ai/tools/browserTools/common.ts +0 -27
  169. package/ai/tools/browserTools/openSession.ts +0 -48
  170. package/ai/tools/browserTools/readContent.ts +0 -38
  171. package/ai/tools/browserTools/selectOption.ts +0 -46
  172. package/ai/tools/browserTools/typeText.ts +0 -42
  173. package/ai/tools/category/createCategoryTool.ts +0 -66
  174. package/ai/tools/category/queryContentsByCategoryTool.ts +0 -69
  175. package/ai/tools/category/updateContentCategoryTool.ts +0 -75
  176. package/ai/tools/cfBrowserTools.ts +0 -319
  177. package/ai/tools/cfSpeechToTextTool.ts +0 -49
  178. package/ai/tools/checkEnvTool.ts +0 -102
  179. package/ai/tools/cloudflareCrawlTool.ts +0 -289
  180. package/ai/tools/codeSearchTool.ts +0 -123
  181. package/ai/tools/codeTools.ts +0 -101
  182. package/ai/tools/convertMarxistsBookTool.ts +0 -103
  183. package/ai/tools/createDocTool.ts +0 -132
  184. package/ai/tools/createSkillDocTool.ts +0 -155
  185. package/ai/tools/createWorkflowTool.ts +0 -154
  186. package/ai/tools/delayTool.ts +0 -31
  187. package/ai/tools/deleteSpacesTool.ts +0 -325
  188. package/ai/tools/deleteSpacesToolModel.ts +0 -159
  189. package/ai/tools/devReloadUtils.ts +0 -29
  190. package/ai/tools/dialogMessageSearch.ts +0 -137
  191. package/ai/tools/doctorSkillTool.ts +0 -72
  192. package/ai/tools/ecommerceScraperTool.ts +0 -86
  193. package/ai/tools/emailTools.ts +0 -549
  194. package/ai/tools/evalSkillTool.ts +0 -92
  195. package/ai/tools/exaSearchTool.ts +0 -63
  196. package/ai/tools/execShellTool.ts +0 -266
  197. package/ai/tools/executeSqlTool.ts +0 -192
  198. package/ai/tools/fetchWebpageSupport.ts +0 -333
  199. package/ai/tools/fetchWebpageTool.ts +0 -97
  200. package/ai/tools/geminiImagePreviewTool.ts +0 -386
  201. package/ai/tools/generateDocxTool.ts +0 -215
  202. package/ai/tools/googleSearchScraperTool.ts +0 -106
  203. package/ai/tools/importDataTool.ts +0 -133
  204. package/ai/tools/importSkillTool.ts +0 -162
  205. package/ai/tools/index.ts +0 -2290
  206. package/ai/tools/jdProductScraperTool.ts +0 -821
  207. package/ai/tools/listUserSpacesTool.ts +0 -113
  208. package/ai/tools/localFilesTool.ts +0 -276
  209. package/ai/tools/marxistsOfflineBook.ts +0 -253
  210. package/ai/tools/modelUsageTools.ts +0 -206
  211. package/ai/tools/noloWorkspaceReadTools.ts +0 -467
  212. package/ai/tools/olmOcrTool.ts +0 -33
  213. package/ai/tools/openaiImageTool.ts +0 -267
  214. package/ai/tools/prepareTools.ts +0 -193
  215. package/ai/tools/readDocTool.ts +0 -88
  216. package/ai/tools/readFileTool.ts +0 -214
  217. package/ai/tools/readTool.ts +0 -194
  218. package/ai/tools/readXPostTool.ts +0 -229
  219. package/ai/tools/readXhsProfileTool.ts +0 -638
  220. package/ai/tools/rememberMemoryTool.ts +0 -84
  221. package/ai/tools/remotionVideoTool.ts +0 -151
  222. package/ai/tools/searchDialogMessagesTool.ts +0 -222
  223. package/ai/tools/searchWorkspaceTool.ts +0 -259
  224. package/ai/tools/skillFollowup.ts +0 -86
  225. package/ai/tools/surfWeatherTool.ts +0 -169
  226. package/ai/tools/table/addTableRowTool.ts +0 -217
  227. package/ai/tools/table/createTableTool.ts +0 -326
  228. package/ai/tools/table/rowTools.ts +0 -386
  229. package/ai/tools/table/schemaTools.ts +0 -244
  230. package/ai/tools/table/shareTableTool.ts +0 -148
  231. package/ai/tools/table/toolShared.ts +0 -129
  232. package/ai/tools/taobaoTmallProductScraperTool.ts +0 -49
  233. package/ai/tools/toolApiClient.ts +0 -279
  234. package/ai/tools/toolNameAliases.ts +0 -70
  235. package/ai/tools/toolPacks.ts +0 -19
  236. package/ai/tools/toolResultError.ts +0 -42
  237. package/ai/tools/toolRunSlice.ts +0 -303
  238. package/ai/tools/toolSchemaCompatibility.ts +0 -53
  239. package/ai/tools/uiAskChoiceTool.ts +0 -104
  240. package/ai/tools/updateContentTitleTool.ts +0 -84
  241. package/ai/tools/updateDocTool.ts +0 -105
  242. package/ai/tools/updateUserPreferenceProfileTool.ts +0 -153
  243. package/ai/tools/wereadGatewayTool.ts +0 -152
  244. package/ai/tools/whisperTool.ts +0 -76
  245. package/ai/tools/writeFileTool.ts +0 -213
  246. package/ai/tools/youtubeScraperTool.ts +0 -116
  247. package/ai/tools/ziweiChartTool.ts +0 -678
  248. package/ai/types.ts +0 -58
  249. package/ai/workflow/workflowExecutor.ts +0 -323
  250. package/ai/workflow/workflowSlice.ts +0 -73
  251. package/ai/workflow/workflowTypes.ts +0 -106
  252. package/app/actions/syncAppRecord.ts +0 -73
  253. package/app/constants/animationSets.ts +0 -21
  254. package/app/constants/appEditor.ts +0 -99
  255. package/app/favorite/deletedFavoriteProjection.ts +0 -31
  256. package/app/favorite/favoriteSlice.ts +0 -690
  257. package/app/favorite/type.ts +0 -43
  258. package/app/hooks/deleteDbKey.ts +0 -254
  259. package/app/i18n/client.ts +0 -13
  260. package/app/i18n/i18n.base.ts +0 -14
  261. package/app/i18n/i18n.config.ts +0 -77
  262. package/app/i18n/index.ts +0 -15
  263. package/app/i18n/mapLanguage.ts +0 -11
  264. package/app/i18n/translations/appBuilder.locale.ts +0 -453
  265. package/app/i18n/translations/error.locale.ts +0 -246
  266. package/app/i18n/translations/interface.locale.ts +0 -2206
  267. package/app/i18n/translations/pricing.locale.ts +0 -397
  268. package/app/i18n/translations/privacy.locale.ts +0 -168
  269. package/app/i18n/translations/seo.locale.ts +0 -129
  270. package/app/i18n/translations/terms.locale.ts +0 -124
  271. package/app/i18n/types.ts +0 -6
  272. package/app/notifications/model.ts +0 -77
  273. package/app/notifications/notificationSlice.ts +0 -141
  274. package/app/reducer.ts +0 -36
  275. package/app/settings/dbActionThunks.ts +0 -34
  276. package/app/settings/defaultSpacePreference.ts +0 -87
  277. package/app/settings/settingSlice.tsx +0 -1212
  278. package/app/settings/settingsRecord.ts +0 -8
  279. package/app/stateViews/runtime.ts +0 -90
  280. package/app/store.ts +0 -55
  281. package/app/theme/colors.ts +0 -280
  282. package/app/theme/fontPreference.ts +0 -77
  283. package/app/theme/theme.config.ts +0 -120
  284. package/app/theme/themeModeBootstrap.ts +0 -123
  285. package/app/types/appSummary.ts +0 -118
  286. package/app/types.ts +0 -358
  287. package/app/utils/appKeys.ts +0 -55
  288. package/app/utils/async.ts +0 -28
  289. package/app/utils/desktopAgentRuntimeTurnClient.ts +0 -75
  290. package/app/utils/env.ts +0 -17
  291. package/app/utils/fileUtils.ts +0 -229
  292. package/app/utils/imageUtils.ts +0 -274
  293. package/app/utils/myContentItems.ts +0 -317
  294. package/app/utils/retryAfter.ts +0 -36
  295. package/app/utils/retryFetch.ts +0 -61
  296. package/app/utils/toast.ts +0 -3
  297. package/auth/action/signUpAction.ts +0 -227
  298. package/auth/adminPermissions.ts +0 -78
  299. package/auth/authSlice.ts +0 -531
  300. package/auth/client/loginRequest.ts +0 -14
  301. package/auth/resetAuthScopedClientState.ts +0 -27
  302. package/auth/routes.ts +0 -183
  303. package/auth/token.ts +0 -63
  304. package/auth/types.ts +0 -40
  305. package/authCommands.ts +0 -230
  306. package/chat/chat.locale.ts +0 -817
  307. package/chat/dialog/actions/addCybotAction.ts +0 -52
  308. package/chat/dialog/actions/addReferenceKeysAction.ts +0 -62
  309. package/chat/dialog/actions/builtinDialogLlm.ts +0 -35
  310. package/chat/dialog/actions/cleanupCliSession.ts +0 -40
  311. package/chat/dialog/actions/createDialogAction.ts +0 -379
  312. package/chat/dialog/actions/createScheduledTaskAction.ts +0 -78
  313. package/chat/dialog/actions/handleSendMessageAction.ts +0 -202
  314. package/chat/dialog/actions/handleSendMessageResolver.ts +0 -27
  315. package/chat/dialog/actions/removeCybotAction.ts +0 -6
  316. package/chat/dialog/actions/removeDialogAgentAction.ts +0 -44
  317. package/chat/dialog/actions/replacePrimaryCybotAction.ts +0 -6
  318. package/chat/dialog/actions/setPrimaryDialogAgentAction.ts +0 -52
  319. package/chat/dialog/actions/updateDialogSummaryAction.ts +0 -430
  320. package/chat/dialog/actions/updateDialogTitleAction.ts +0 -151
  321. package/chat/dialog/actions/updateDialogTitlePolicy.ts +0 -30
  322. package/chat/dialog/actions/updateTokensAction.ts +0 -233
  323. package/chat/dialog/dialogAgents.ts +0 -72
  324. package/chat/dialog/dialogAttachmentCleanup.ts +0 -135
  325. package/chat/dialog/dialogSlice.ts +0 -985
  326. package/chat/dialog/dialogTitle.ts +0 -81
  327. package/chat/dialog/dialogTokenStats.ts +0 -12
  328. package/chat/dialog/dialogUrl.ts +0 -6
  329. package/chat/dialog/goal.ts +0 -108
  330. package/chat/dialog/objectAssistantRegistry.ts +0 -286
  331. package/chat/dialog/schedule.ts +0 -17
  332. package/chat/messages/activityCompletion.ts +0 -97
  333. package/chat/messages/fetchAndCacheMessages.ts +0 -119
  334. package/chat/messages/fetchConvMsgs.ts +0 -51
  335. package/chat/messages/fetchMessages.ts +0 -84
  336. package/chat/messages/fileUrl.ts +0 -13
  337. package/chat/messages/imagePayloadPersistence.ts +0 -22
  338. package/chat/messages/messageAgent.ts +0 -8
  339. package/chat/messages/messageContent.ts +0 -203
  340. package/chat/messages/messageContract.ts +0 -88
  341. package/chat/messages/messageSlice.ts +0 -1386
  342. package/chat/messages/toolThunks.ts +0 -646
  343. package/chat/messages/types.ts +0 -192
  344. package/chat/messages/web/toolDisplayName.ts +0 -686
  345. package/cli/agentAliases.ts +0 -40
  346. package/cli/agentCommandSupport.ts +0 -81
  347. package/cli/agentInternalCommandEntries.ts +0 -44
  348. package/cli/agentListCommands.ts +0 -185
  349. package/cli/agentListHelpers.ts +0 -242
  350. package/cli/agentMachineCommands.ts +0 -426
  351. package/cli/agentNameResolver.ts +0 -164
  352. package/cli/agentPullCommand.ts +0 -142
  353. package/cli/agentRecordCommands.ts +0 -224
  354. package/cli/agentRecordHelpers.ts +0 -592
  355. package/cli/agentRunCommand.ts +0 -568
  356. package/cli/agentRuntimeLocal.ts +0 -45
  357. package/cli/authCommands.ts +0 -230
  358. package/cli/cliCommandAdapters.ts +0 -28
  359. package/cli/cliCommandDispatch.ts +0 -66
  360. package/cli/cliCommandFactories.ts +0 -66
  361. package/cli/cliCommandTypes.ts +0 -49
  362. package/cli/cliEnvHelpers.ts +0 -116
  363. package/cli/cliSpaceHelpers.ts +0 -39
  364. package/cli/client/agentConfigResolver.ts +0 -1
  365. package/cli/client/agentRun.ts +0 -735
  366. package/cli/client/compactDialog.ts +0 -222
  367. package/cli/client/hybridRecordStore.ts +0 -52
  368. package/cli/client/localAgentRecords.ts +0 -7
  369. package/cli/client/localDialogRecords.ts +0 -30
  370. package/cli/client/localProviderResolver.ts +0 -1
  371. package/cli/client/localRuntimeAdapter.ts +0 -1098
  372. package/cli/client/localToolPolicy.ts +0 -8
  373. package/cli/client/profileConfig.ts +0 -101
  374. package/cli/client/streamingOutput.ts +0 -38
  375. package/cli/commandRegistry.ts +0 -98
  376. package/cli/connectorRunArtifact.ts +0 -120
  377. package/cli/connectorWebSocketTarget.ts +0 -112
  378. package/cli/defaultServer.ts +0 -1
  379. package/cli/dialogAttachmentCleanup.ts +0 -151
  380. package/cli/dialogCommands.ts +0 -1120
  381. package/cli/dialogInternalCommandEntries.ts +0 -17
  382. package/cli/docCreateCommands.ts +0 -345
  383. package/cli/docInternalCommandEntries.ts +0 -11
  384. package/cli/docPageHelpers.ts +0 -80
  385. package/cli/docSkillHelpers.ts +0 -67
  386. package/cli/docSpaceHelpers.ts +0 -46
  387. package/cli/globalRecordOperations.ts +0 -258
  388. package/cli/internalCommandEntries.ts +0 -18
  389. package/cli/localRuntimeAuthority.ts +0 -182
  390. package/cli/localRuntimeDb.ts +0 -26
  391. package/cli/machineCommands.ts +0 -312
  392. package/cli/machineDaemonCommands.ts +0 -185
  393. package/cli/machineHeartbeatCommands.ts +0 -30
  394. package/cli/machineStatusCommands.ts +0 -95
  395. package/cli/machineWatchCommands.ts +0 -41
  396. package/cli/machineWsPromptMaterialization.ts +0 -89
  397. package/cli/machineWsRunDispatch.ts +0 -695
  398. package/cli/machineWsSession.ts +0 -213
  399. package/cli/memoryCommands.ts +0 -253
  400. package/cli/memoryInternalCommandEntries.ts +0 -9
  401. package/cli/offlineMarxistsAgentCommand.ts +0 -315
  402. package/cli/qoderUsageProbe.ts +0 -252
  403. package/cli/runtimeDoctorCommands.ts +0 -174
  404. package/cli/runtimeModeArgs.ts +0 -33
  405. package/cli/scriptCommandEntries.ts +0 -66
  406. package/cli/secretScan.ts +0 -31
  407. package/cli/systemInternalCommandEntries.ts +0 -65
  408. package/cli/tableCommands.ts +0 -453
  409. package/cli/tui/readlineWorkspace.ts +0 -210
  410. package/cli/tui/session.ts +0 -554
  411. package/cli/updateCommands.ts +0 -134
  412. package/cli/workflowInternalCommandEntries.ts +0 -10
  413. package/cliCommandAdapters.ts +0 -28
  414. package/cliCommandDispatch.ts +0 -66
  415. package/cliCommandFactories.ts +0 -66
  416. package/cliCommandTypes.ts +0 -49
  417. package/cliEnvHelpers.ts +0 -116
  418. package/cliSpaceHelpers.ts +0 -39
  419. package/client/agentConfigResolver.test.ts +0 -69
  420. package/client/agentConfigResolver.ts +0 -1
  421. package/client/agentRun.test.ts +0 -1346
  422. package/client/agentRun.ts +0 -735
  423. package/client/compactDialog.test.ts +0 -238
  424. package/client/compactDialog.ts +0 -222
  425. package/client/hybridRecordStore.test.ts +0 -157
  426. package/client/hybridRecordStore.ts +0 -52
  427. package/client/localAgentRecords.test.ts +0 -27
  428. package/client/localAgentRecords.ts +0 -7
  429. package/client/localDialogRecords.test.ts +0 -124
  430. package/client/localDialogRecords.ts +0 -30
  431. package/client/localProviderResolver.test.ts +0 -102
  432. package/client/localProviderResolver.ts +0 -1
  433. package/client/localRuntimeAdapter.test.ts +0 -2809
  434. package/client/localRuntimeAdapter.ts +0 -1098
  435. package/client/localRuntimeDryRun.test.ts +0 -135
  436. package/client/localToolPolicy.test.ts +0 -48
  437. package/client/localToolPolicy.ts +0 -8
  438. package/client/profileConfig.test.ts +0 -106
  439. package/client/profileConfig.ts +0 -101
  440. package/client/streamingOutput.test.ts +0 -22
  441. package/client/streamingOutput.ts +0 -38
  442. package/commandRegistry.ts +0 -98
  443. package/connector-experimental/capabilities.ts +0 -75
  444. package/connector-experimental/codexBinary.ts +0 -41
  445. package/connector-experimental/heartbeatLoop.ts +0 -22
  446. package/connector-experimental/machineInfo.ts +0 -46
  447. package/connector-experimental/protocol.ts +0 -54
  448. package/connectorRunArtifact.ts +0 -120
  449. package/connectorWebSocketTarget.ts +0 -112
  450. package/core/builtinAgents.ts +0 -209
  451. package/core/config.ts +0 -15
  452. package/core/crypto.ts +0 -51
  453. package/core/generateKeyPairFromSeedV1.ts +0 -15
  454. package/core/generateMainKey.ts +0 -22
  455. package/core/init.ts +0 -41
  456. package/core/localOrigins.ts +0 -8
  457. package/core/password.ts +0 -13
  458. package/core/prefix.ts +0 -23
  459. package/create/editor/transforms/fromMarkdown/index.ts +0 -260
  460. package/create/editor/transforms/fromMarkdown/inline.ts +0 -285
  461. package/create/editor/transforms/fromMarkdown/table.ts +0 -130
  462. package/create/editor/transforms/markdownToSlate.ts +0 -24
  463. package/create/editor/transforms/slateToRenderMarkdown.ts +0 -218
  464. package/create/editor/transforms/slateToSimplifiedMarkdown.ts +0 -224
  465. package/create/editor/transforms/slateToText.ts +0 -33
  466. package/create/editor/utils/slateUtils.ts +0 -233
  467. package/create/space/addSpaceAction.ts +0 -193
  468. package/create/space/category/categoryActions.ts +0 -427
  469. package/create/space/changeSpaceAction.ts +0 -36
  470. package/create/space/constants.ts +0 -8
  471. package/create/space/content/addContentAction.ts +0 -116
  472. package/create/space/content/contentThunks.ts +0 -90
  473. package/create/space/content/deleteContentFromSpaceAction.ts +0 -187
  474. package/create/space/content/deleteMultipleContentAction.ts +0 -112
  475. package/create/space/content/moveContentAction.ts +0 -285
  476. package/create/space/content/updateContentCategoryAction.ts +0 -154
  477. package/create/space/content/updateContentTitleAction.ts +0 -151
  478. package/create/space/content/uploadAndAddFileToSpaceAction.ts +0 -98
  479. package/create/space/contentKeyUtils.ts +0 -117
  480. package/create/space/deleteSpaceAction.ts +0 -276
  481. package/create/space/fetchSpaceAction.ts +0 -70
  482. package/create/space/fetchSpaceSidebarStateAction.ts +0 -26
  483. package/create/space/loadDefaultSpaceAction.ts +0 -57
  484. package/create/space/member/addMemberAction.ts +0 -79
  485. package/create/space/member/fetchUserSpaceMembershipsAction.ts +0 -386
  486. package/create/space/member/memberThunks.ts +0 -47
  487. package/create/space/member/removeMemberAction.ts +0 -67
  488. package/create/space/resolvePreferredSpaceId.ts +0 -113
  489. package/create/space/sidebarVisibleTypes.ts +0 -260
  490. package/create/space/space.locale.ts +0 -1087
  491. package/create/space/spaceAccess.ts +0 -182
  492. package/create/space/spaceCollapsedState.ts +0 -54
  493. package/create/space/spaceKeys.ts +0 -65
  494. package/create/space/spaceSlice.ts +0 -328
  495. package/create/space/spaceThunks.ts +0 -410
  496. package/create/space/types.ts +0 -27
  497. package/create/space/updateSpaceAction.ts +0 -107
  498. package/create/space/utils/permissions.ts +0 -47
  499. package/create/types.ts +0 -21
  500. package/database/actions/agentReadResolution.ts +0 -33
  501. package/database/actions/common.ts +0 -243
  502. package/database/actions/deleteFile.ts +0 -40
  503. package/database/actions/fileContent.ts +0 -125
  504. package/database/actions/patch.ts +0 -155
  505. package/database/actions/read.ts +0 -358
  506. package/database/actions/readAndWait.ts +0 -260
  507. package/database/actions/readRequestManager.ts +0 -120
  508. package/database/actions/readResolution.ts +0 -125
  509. package/database/actions/remove.ts +0 -94
  510. package/database/actions/replication.ts +0 -477
  511. package/database/actions/upload.ts +0 -186
  512. package/database/actions/upsert.ts +0 -56
  513. package/database/actions/write.ts +0 -146
  514. package/database/authority/ownerKey.ts +0 -80
  515. package/database/authority/recordAuthority.ts +0 -67
  516. package/database/authority/userAuthorityRegistry.ts +0 -54
  517. package/database/client/fetchUserData.ts +0 -61
  518. package/database/config.ts +0 -40
  519. package/database/dbActionThunks.ts +0 -1
  520. package/database/dbSlice.ts +0 -149
  521. package/database/fileRing.ts +0 -51
  522. package/database/fileStorage.ts +0 -232
  523. package/database/fileUrl.ts +0 -34
  524. package/database/keys.ts +0 -767
  525. package/database/queryPrefixes.ts +0 -14
  526. package/database/requests.ts +0 -443
  527. package/database/runtimeServerContext.ts +0 -54
  528. package/database/server/authorityStoreTypes.ts +0 -31
  529. package/database/server/cliAuthorityBrokerClient.ts +0 -227
  530. package/database/server/cliAuthorityBrokerServer.ts +0 -283
  531. package/database/server/cliAuthorityBrokerTypes.ts +0 -43
  532. package/database/server/cliAuthorityStoreDriver.ts +0 -98
  533. package/database/server/db.ts +0 -99
  534. package/database/server/dbPath.ts +0 -20
  535. package/database/server/ensureDbOpen.ts +0 -12
  536. package/database/server/legacyServerDb.ts +0 -58
  537. package/database/server/levelAuthorityStore.ts +0 -114
  538. package/database/server/memoryAuthorityStore.ts +0 -133
  539. package/database/server/serverStoreFactory.ts +0 -118
  540. package/database/tenantPlacement.ts +0 -57
  541. package/database/tombstones.ts +0 -52
  542. package/database/userDataMerge.ts +0 -95
  543. package/database/userPreferenceRegister.ts +0 -131
  544. package/database/utils/ulid.ts +0 -1
  545. package/defaultServer.ts +0 -1
  546. package/dialogAttachmentCleanup.ts +0 -151
  547. package/dialogCommands.ts +0 -1120
  548. package/dialogInternalCommandEntries.ts +0 -17
  549. package/docCreateCommands.ts +0 -345
  550. package/docInternalCommandEntries.ts +0 -11
  551. package/docPageHelpers.ts +0 -80
  552. package/docSkillHelpers.ts +0 -67
  553. package/docSpaceHelpers.ts +0 -46
  554. package/globalRecordOperations.ts +0 -258
  555. package/index.ts +0 -78
  556. package/integrations/anthropic/anthropicModels.ts +0 -47
  557. package/integrations/deepseek/models.ts +0 -37
  558. package/integrations/google/models.ts +0 -341
  559. package/integrations/openai/chatCompletionStreamMode.ts +0 -10
  560. package/integrations/openai/filterAndCleanMessages.ts +0 -284
  561. package/integrations/openai/flexTier.ts +0 -102
  562. package/integrations/openai/generateOpenAIRequestBody.ts +0 -271
  563. package/integrations/openai/generateResponseRequestBody.ts +0 -77
  564. package/integrations/openai/models.ts +0 -271
  565. package/integrations/openai/providerBodyCompatibility.ts +0 -23
  566. package/integrations/openai/responsesHelpers.ts +0 -297
  567. package/integrations/x-reader/types.ts +0 -109
  568. package/integrations/xai/models.ts +0 -13
  569. package/integrations/xhs-reader/redaction.ts +0 -68
  570. package/integrations/xhs-reader/types.ts +0 -277
  571. package/integrations/xhs-reader/url.ts +0 -80
  572. package/internalCommandEntries.ts +0 -18
  573. package/localRuntimeAuthority.ts +0 -182
  574. package/localRuntimeDb.ts +0 -26
  575. package/machineCommands.ts +0 -312
  576. package/machineDaemonCommands.ts +0 -185
  577. package/machineHeartbeatCommands.ts +0 -30
  578. package/machineStatusCommands.ts +0 -95
  579. package/machineWatchCommands.ts +0 -41
  580. package/machineWsPromptMaterialization.ts +0 -89
  581. package/machineWsRunDispatch.ts +0 -695
  582. package/machineWsSession.ts +0 -213
  583. package/memoryCommands.ts +0 -253
  584. package/memoryInternalCommandEntries.ts +0 -9
  585. package/offlineMarxistsAgentCommand.ts +0 -315
  586. package/qoderUsageProbe.ts +0 -252
  587. package/render/contentIcon/types.ts +0 -167
  588. package/render/page/createPageAction.ts +0 -183
  589. package/render/page/docSlice.ts +0 -495
  590. package/render/page/types.ts +0 -19
  591. package/render/table/activityColumns.ts +0 -6
  592. package/render/table/createTableAction.ts +0 -274
  593. package/render/table/deleteTableAction.ts +0 -193
  594. package/render/table/fetchAndCacheTableRows.ts +0 -174
  595. package/render/table/tableSlice.ts +0 -1167
  596. package/render/table/toolValueUtils.ts +0 -363
  597. package/render/table/types.ts +0 -262
  598. package/render/table/utils/tableSerialization.ts +0 -50
  599. package/runtimeDoctorCommands.ts +0 -174
  600. package/runtimeModeArgs.ts +0 -33
  601. package/scriptCommandEntries.ts +0 -66
  602. package/secretScan.ts +0 -31
  603. package/server/handlers/agentRun/types.ts +0 -105
  604. package/share/action.ts +0 -225
  605. package/share/helpers.ts +0 -302
  606. package/share/keys.ts +0 -243
  607. package/share/publicSharePolicy.ts +0 -27
  608. package/share/shareSlice.ts +0 -53
  609. package/share/types.ts +0 -100
  610. package/systemInternalCommandEntries.ts +0 -65
  611. package/tableCommands.ts +0 -453
  612. package/tui/readlineWorkspace.ts +0 -210
  613. package/tui/session.ts +0 -554
  614. package/updateCommands.ts +0 -134
  615. 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
- }