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