ethagent 3.3.4 → 4.0.0

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 (322) hide show
  1. package/.claude-plugin/marketplace.json +11 -0
  2. package/.claude-plugin/plugin.json +35 -0
  3. package/LICENSE +1 -1
  4. package/README.md +64 -104
  5. package/commands/ethagent.md +40 -0
  6. package/package.json +16 -16
  7. package/src/app/keybindings/KeybindingProvider.tsx +1 -6
  8. package/src/app/keybindings/types.ts +1 -6
  9. package/src/cli/ResetConfirmView.tsx +54 -53
  10. package/src/cli/demo.ts +86 -0
  11. package/src/cli/hookIo.ts +45 -0
  12. package/src/cli/main.tsx +94 -123
  13. package/src/cli/memoryGuard.ts +49 -0
  14. package/src/cli/reset.ts +28 -70
  15. package/src/cli/sessionStart.ts +33 -0
  16. package/src/cli/status.ts +46 -0
  17. package/src/cli/sync.ts +167 -0
  18. package/src/cli/syncAdapters/claude-code.ts +86 -0
  19. package/src/cli/syncAdapters/codex.ts +66 -0
  20. package/src/cli/syncAdapters/index.ts +45 -0
  21. package/src/cli/syncAdapters/managedBlock.ts +175 -0
  22. package/src/cli/syncAdapters/shared.ts +63 -0
  23. package/src/identity/continuity/envelopeParse.ts +20 -1
  24. package/src/identity/continuity/publicSkills.ts +3 -1
  25. package/src/identity/continuity/skills/publicSkillsSync.ts +2 -1
  26. package/src/identity/continuity/skills/scaffold.ts +5 -2
  27. package/src/identity/continuity/snapshots.ts +12 -5
  28. package/src/identity/continuity/storage/defaults.ts +20 -19
  29. package/src/identity/continuity/storage/status.ts +1 -1
  30. package/src/identity/ens/ensLookup/constants.ts +1 -1
  31. package/src/identity/manager/IdentityManager.tsx +33 -0
  32. package/src/identity/{hub → manager}/OperationalRoutes.tsx +37 -18
  33. package/src/identity/{hub → manager}/Routes.tsx +48 -34
  34. package/src/identity/{hub → manager}/continuity/ContinuityDashboardScreen.tsx +9 -19
  35. package/src/identity/{hub → manager}/continuity/RebackupStorageScreen.tsx +3 -3
  36. package/src/identity/manager/continuity/RecoveryConfirmScreen.tsx +102 -0
  37. package/src/identity/{hub → manager}/continuity/SavePromptScreen.tsx +2 -3
  38. package/src/identity/{hub → manager}/continuity/completion.ts +1 -1
  39. package/src/identity/{hub → manager}/continuity/effects.ts +1 -1
  40. package/src/identity/{hub → manager}/continuity/skills/DeleteSkillConfirmScreen.tsx +2 -2
  41. package/src/identity/{hub → manager}/continuity/skills/NewSkillScreen.tsx +0 -5
  42. package/src/identity/{hub → manager}/continuity/skills/NewSkillVisibilityScreen.tsx +4 -4
  43. package/src/identity/{hub → manager}/continuity/skills/SkillActionsScreen.tsx +6 -22
  44. package/src/identity/{hub → manager}/continuity/skills/SkillsTreeScreen.tsx +5 -17
  45. package/src/identity/{hub → manager}/continuity/snapshot.ts +1 -1
  46. package/src/identity/{hub → manager}/continuity/vault.ts +1 -1
  47. package/src/identity/{hub → manager}/create/CreateFlow.tsx +59 -32
  48. package/src/identity/{hub → manager}/create/effects.ts +19 -10
  49. package/src/identity/manager/create/importScan.ts +122 -0
  50. package/src/identity/{hub → manager}/custody/CustodyEditFlow.tsx +17 -61
  51. package/src/identity/{hub → manager}/custody/actions.ts +1 -15
  52. package/src/identity/{hub → manager}/custody/routes.tsx +20 -40
  53. package/src/identity/{hub → manager}/custody/transactions.ts +1 -0
  54. package/src/identity/{hub → manager}/custody/types.ts +1 -2
  55. package/src/identity/{hub → manager}/custody/useCustodyEffects.ts +1 -1
  56. package/src/identity/{hub → manager}/ens/EnsEditAdvancedScreens.tsx +2 -2
  57. package/src/identity/{hub → manager}/ens/EnsEditMaintenanceScreens.tsx +12 -23
  58. package/src/identity/{hub → manager}/ens/EnsEditReviewScreens.tsx +18 -42
  59. package/src/identity/{hub → manager}/ens/EnsEditRunners.tsx +1 -1
  60. package/src/identity/{hub → manager}/ens/EnsEditShared.tsx +0 -2
  61. package/src/identity/{hub → manager}/ens/EnsEditSimpleScreens.tsx +10 -19
  62. package/src/identity/{hub → manager}/ens/EnsFlow.tsx +133 -41
  63. package/src/identity/{hub → manager}/ens/EnsOperatorWalletsScreen.tsx +14 -19
  64. package/src/identity/{hub → manager}/ens/editCopy.ts +1 -14
  65. package/src/identity/{hub → manager}/profile/EditProfileFlow.tsx +99 -66
  66. package/src/identity/{hub → manager}/profile/effects.ts +1 -3
  67. package/src/identity/{hub → manager}/profile/operatorSave.ts +1 -1
  68. package/src/identity/{hub → manager}/profile/state.ts +1 -1
  69. package/src/identity/{hub/identityHubReducer.ts → manager/reducer.ts} +25 -26
  70. package/src/identity/{hub → manager}/restore/RestoreFlow.tsx +16 -24
  71. package/src/identity/{hub → manager}/restore/apply.ts +1 -1
  72. package/src/identity/{hub → manager}/restore/auth.ts +1 -1
  73. package/src/identity/{hub → manager}/restore/discover.ts +1 -1
  74. package/src/identity/{hub → manager}/restore/fetch.ts +1 -1
  75. package/src/identity/{hub → manager}/restore/restoreAdmin.ts +1 -1
  76. package/src/identity/{hub → manager}/restore/useRestoreEffects.ts +2 -9
  77. package/src/identity/{hub → manager}/settings/StorageCredentialScreen.tsx +10 -25
  78. package/src/identity/{hub → manager}/shared/components/DetailsScreen.tsx +5 -7
  79. package/src/identity/{hub → manager}/shared/components/ErrorScreen.tsx +6 -10
  80. package/src/identity/{hub → manager}/shared/components/FlowTimeline.tsx +4 -3
  81. package/src/identity/{hub → manager}/shared/components/IdentitySummary.tsx +19 -59
  82. package/src/identity/manager/shared/components/LazyMenu.tsx +147 -0
  83. package/src/identity/manager/shared/components/MenuScreen.tsx +220 -0
  84. package/src/identity/manager/shared/components/OperationCompleteScreen.tsx +28 -0
  85. package/src/identity/{hub → manager}/shared/components/UnlinkedIdentityScreen.tsx +9 -10
  86. package/src/identity/{hub → manager}/shared/components/WalletApprovalScreen.tsx +1 -2
  87. package/src/identity/manager/shared/components/Wordmark.tsx +54 -0
  88. package/src/identity/{hub → manager}/shared/components/menuFlagsFromReconciliation.ts +39 -15
  89. package/src/identity/{hub → manager}/shared/effects/profilePrep.ts +1 -1
  90. package/src/identity/manager/shared/effects/types.ts +30 -0
  91. package/src/identity/{hub → manager}/shared/model/copy.ts +0 -4
  92. package/src/identity/{hub → manager}/shared/model/errors.ts +32 -3
  93. package/src/identity/{hub → manager}/shared/model/network.ts +2 -2
  94. package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/hook.ts +5 -0
  95. package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/run.ts +1 -1
  96. package/src/identity/{hub/shared/reconciliation/useAgentReconciliation.ts → manager/shared/reconciliation/index.ts} +6 -0
  97. package/src/identity/{hub → manager}/shared/utils.ts +6 -10
  98. package/src/identity/{hub → manager}/transfer/TokenTransferFlow.tsx +3 -3
  99. package/src/identity/{hub → manager}/transfer/TokenTransferScreens.tsx +4 -10
  100. package/src/identity/{hub → manager}/transfer/effects.ts +1 -1
  101. package/src/identity/{hub → manager}/types.ts +5 -6
  102. package/src/identity/{hub/useIdentityHubContinuity.ts → manager/useContinuity.ts} +59 -27
  103. package/src/identity/{hub/useIdentityHubController.ts → manager/useController.ts} +38 -35
  104. package/src/identity/{hub/useIdentityHubSideEffects.ts → manager/useSideEffects.ts} +40 -4
  105. package/src/identity/registry/erc8004/discovery.ts +3 -17
  106. package/src/identity/registry/erc8004/utils.ts +1 -1
  107. package/src/identity/storage/ipfs.ts +21 -1
  108. package/src/identity/wallet/browserWallet/html.ts +10 -2
  109. package/src/identity/wallet/browserWallet/http.ts +18 -0
  110. package/src/identity/wallet/browserWallet/requestServer.ts +5 -1
  111. package/src/identity/wallet/browserWallet/requests.ts +10 -28
  112. package/src/identity/wallet/browserWallet/session.ts +26 -33
  113. package/src/identity/wallet/browserWallet/validation.ts +14 -0
  114. package/src/identity/wallet/browserWallet/walletPageSource.ts +22 -40
  115. package/src/identity/wallet/page/boot.ts +43 -0
  116. package/src/identity/wallet/page/config.ts +59 -0
  117. package/src/identity/wallet/page/constants.ts +12 -0
  118. package/src/identity/wallet/page/copy.ts +47 -68
  119. package/src/identity/wallet/page/css.ts +638 -0
  120. package/src/identity/wallet/page/{errorView.ts → errors.ts} +5 -14
  121. package/src/identity/wallet/page/{controller.ts → flow.ts} +4 -71
  122. package/src/identity/wallet/page/markup.ts +44 -34
  123. package/src/identity/wallet/page/{walletProvider.ts → provider.ts} +0 -3
  124. package/src/identity/wallet/page/resize.ts +95 -0
  125. package/src/identity/wallet/page/state.ts +135 -8
  126. package/src/identity/wallet/page/timeline.ts +161 -0
  127. package/src/identity/wallet/page/view.ts +22 -302
  128. package/src/storage/config.ts +30 -80
  129. package/src/storage/reset.ts +31 -0
  130. package/src/storage/secrets.ts +1 -16
  131. package/src/ui/Select.tsx +27 -5
  132. package/src/ui/Spinner.tsx +16 -15
  133. package/src/ui/Surface.tsx +21 -17
  134. package/src/ui/TextArea.tsx +173 -0
  135. package/src/ui/TextInput.tsx +31 -133
  136. package/src/ui/theme.ts +22 -13
  137. package/src/utils/clipboard.ts +0 -140
  138. package/src/app/FirstRun.tsx +0 -577
  139. package/src/app/FirstRunTimeline.tsx +0 -51
  140. package/src/app/firstRunConfig.ts +0 -26
  141. package/src/app/hooks/useCancelRequest.ts +0 -22
  142. package/src/app/hooks/useDoublePress.ts +0 -46
  143. package/src/app/hooks/useExitOnCtrlC.ts +0 -36
  144. package/src/auth/openaiOAuth/credentials.ts +0 -47
  145. package/src/auth/openaiOAuth/crypto.ts +0 -23
  146. package/src/auth/openaiOAuth/index.ts +0 -238
  147. package/src/auth/openaiOAuth/landingPage.ts +0 -116
  148. package/src/auth/openaiOAuth/listener.ts +0 -151
  149. package/src/auth/openaiOAuth/refresh.ts +0 -70
  150. package/src/auth/openaiOAuth/shared.ts +0 -115
  151. package/src/chat/ChatBottomPane.tsx +0 -296
  152. package/src/chat/ChatScreen.tsx +0 -1685
  153. package/src/chat/ConversationStack.tsx +0 -56
  154. package/src/chat/MessageList.tsx +0 -638
  155. package/src/chat/SessionStatus.tsx +0 -53
  156. package/src/chat/chatEnvironment.ts +0 -16
  157. package/src/chat/chatScreenUtils.ts +0 -194
  158. package/src/chat/chatSessionState.ts +0 -146
  159. package/src/chat/chatTurnContext.ts +0 -50
  160. package/src/chat/chatTurnOrchestrator.ts +0 -603
  161. package/src/chat/chatTurnRows.ts +0 -64
  162. package/src/chat/commands.ts +0 -494
  163. package/src/chat/continuityEditReview.ts +0 -42
  164. package/src/chat/display/DiffView.tsx +0 -193
  165. package/src/chat/display/SyntaxText.tsx +0 -192
  166. package/src/chat/display/toolCallDisplay.ts +0 -103
  167. package/src/chat/display/toolResultDisplay.ts +0 -19
  168. package/src/chat/input/ChatInput.tsx +0 -625
  169. package/src/chat/input/chatInputHelpers.ts +0 -62
  170. package/src/chat/input/chatInputState.ts +0 -247
  171. package/src/chat/input/chatPaste.ts +0 -49
  172. package/src/chat/input/imageRefs.ts +0 -30
  173. package/src/chat/input/inputRendering.tsx +0 -93
  174. package/src/chat/input/textCursor.ts +0 -212
  175. package/src/chat/messageMarkdown.ts +0 -220
  176. package/src/chat/messageRows.ts +0 -43
  177. package/src/chat/planImplementation.ts +0 -62
  178. package/src/chat/slashCommandHandlers.ts +0 -122
  179. package/src/chat/slashCommandViews.ts +0 -120
  180. package/src/chat/transcript/TranscriptView.tsx +0 -184
  181. package/src/chat/transcript/transcriptViewport.ts +0 -295
  182. package/src/chat/views/ContextLimitView.tsx +0 -95
  183. package/src/chat/views/ContinuityEditReviewView.tsx +0 -50
  184. package/src/chat/views/CopyPicker.tsx +0 -50
  185. package/src/chat/views/PermissionPrompt.tsx +0 -156
  186. package/src/chat/views/PermissionsView.tsx +0 -165
  187. package/src/chat/views/PlanApprovalView.tsx +0 -91
  188. package/src/chat/views/ResumeView.tsx +0 -273
  189. package/src/chat/views/RewindView.tsx +0 -412
  190. package/src/cli/preview.tsx +0 -14
  191. package/src/cli/updateNotice.ts +0 -54
  192. package/src/identity/continuity/privateEdit/apply.ts +0 -170
  193. package/src/identity/continuity/privateEdit/diff.ts +0 -6
  194. package/src/identity/continuity/privateEdit/files.ts +0 -23
  195. package/src/identity/continuity/privateEdit/types.ts +0 -28
  196. package/src/identity/continuity/privateEdit.ts +0 -46
  197. package/src/identity/hub/IdentityHub.tsx +0 -14
  198. package/src/identity/hub/continuity/RecoveryConfirmScreen.tsx +0 -104
  199. package/src/identity/hub/ens/effects.ts +0 -218
  200. package/src/identity/hub/shared/components/MenuScreen.tsx +0 -241
  201. package/src/identity/hub/shared/effects/types.ts +0 -53
  202. package/src/identity/hub/shared/reconciliation/index.ts +0 -14
  203. package/src/identity/wallet/page/grainient.ts +0 -278
  204. package/src/identity/wallet/page/html.ts +0 -28
  205. package/src/identity/wallet/page/styles/base.ts +0 -260
  206. package/src/identity/wallet/page/styles/components.ts +0 -262
  207. package/src/identity/wallet/page/styles/index.ts +0 -5
  208. package/src/identity/wallet/page/styles/responsive.ts +0 -247
  209. package/src/identity/wallet/page.tsx +0 -38
  210. package/src/mcp/approvals.ts +0 -113
  211. package/src/mcp/config.ts +0 -235
  212. package/src/mcp/manager.ts +0 -482
  213. package/src/mcp/managerHelpers.ts +0 -70
  214. package/src/mcp/names.ts +0 -19
  215. package/src/mcp/output.ts +0 -96
  216. package/src/models/ModelPicker.tsx +0 -1009
  217. package/src/models/catalog.ts +0 -327
  218. package/src/models/huggingface.ts +0 -712
  219. package/src/models/huggingfaceStorage.ts +0 -136
  220. package/src/models/llamacpp.ts +0 -848
  221. package/src/models/llamacppCommands.ts +0 -44
  222. package/src/models/llamacppConfig.ts +0 -34
  223. package/src/models/llamacppDiscovery.ts +0 -176
  224. package/src/models/llamacppOutput.ts +0 -65
  225. package/src/models/llamacppPreflight.ts +0 -158
  226. package/src/models/modelDisplay.ts +0 -180
  227. package/src/models/modelPickerCatalogFlow.ts +0 -56
  228. package/src/models/modelPickerCredentials.ts +0 -166
  229. package/src/models/modelPickerData.ts +0 -41
  230. package/src/models/modelPickerDisplay.tsx +0 -132
  231. package/src/models/modelPickerHfFlow.ts +0 -192
  232. package/src/models/modelPickerLocalRunnerFlow.ts +0 -115
  233. package/src/models/modelPickerOptions.ts +0 -457
  234. package/src/models/modelPickerTypes.ts +0 -69
  235. package/src/models/modelPickerUninstallFlow.ts +0 -48
  236. package/src/models/modelPickerViewHelpers.ts +0 -174
  237. package/src/models/modelRecommendation.ts +0 -139
  238. package/src/models/providerDisplay.ts +0 -16
  239. package/src/models/runtimeDetection.ts +0 -81
  240. package/src/models/uncensoredCatalog.ts +0 -86
  241. package/src/providers/anthropic.ts +0 -290
  242. package/src/providers/contracts.ts +0 -71
  243. package/src/providers/errors.ts +0 -80
  244. package/src/providers/gemini.ts +0 -391
  245. package/src/providers/openai-chat.ts +0 -474
  246. package/src/providers/openai-responses-format.ts +0 -177
  247. package/src/providers/openai-responses.ts +0 -306
  248. package/src/providers/openaiChatWire.ts +0 -124
  249. package/src/providers/registry.ts +0 -120
  250. package/src/providers/retry.ts +0 -58
  251. package/src/providers/sse.ts +0 -93
  252. package/src/runtime/compaction.ts +0 -395
  253. package/src/runtime/cwd.ts +0 -43
  254. package/src/runtime/providerTurn.ts +0 -38
  255. package/src/runtime/sessionMode.ts +0 -55
  256. package/src/runtime/systemPrompt.ts +0 -213
  257. package/src/runtime/textToolParser.ts +0 -161
  258. package/src/runtime/toolClaimGuards.ts +0 -143
  259. package/src/runtime/toolExecution.ts +0 -304
  260. package/src/runtime/toolIntent.ts +0 -143
  261. package/src/runtime/turn.ts +0 -369
  262. package/src/runtime/turnNudges.ts +0 -223
  263. package/src/runtime/turnTypes.ts +0 -86
  264. package/src/storage/factoryReset.ts +0 -127
  265. package/src/storage/history.ts +0 -58
  266. package/src/storage/permissions.ts +0 -76
  267. package/src/storage/rewind.ts +0 -266
  268. package/src/storage/sessionExport.ts +0 -49
  269. package/src/storage/sessions.ts +0 -495
  270. package/src/tools/bashSafety.ts +0 -186
  271. package/src/tools/bashTool.ts +0 -140
  272. package/src/tools/changeDirectoryTool.ts +0 -213
  273. package/src/tools/contracts.ts +0 -192
  274. package/src/tools/deleteFileTool.ts +0 -116
  275. package/src/tools/editTool.ts +0 -165
  276. package/src/tools/editUtils.ts +0 -170
  277. package/src/tools/fileDiff.ts +0 -261
  278. package/src/tools/listDirectoryTool.ts +0 -55
  279. package/src/tools/listSkillFilesTool.ts +0 -77
  280. package/src/tools/listSkillsTool.ts +0 -68
  281. package/src/tools/mcpResourceTools.ts +0 -95
  282. package/src/tools/permissionRules.ts +0 -85
  283. package/src/tools/privateContinuityEditTool.ts +0 -187
  284. package/src/tools/privateContinuityReadTool.ts +0 -106
  285. package/src/tools/readSkillTool.ts +0 -107
  286. package/src/tools/readTool.ts +0 -85
  287. package/src/tools/registry.ts +0 -103
  288. package/src/tools/writeFileTool.ts +0 -167
  289. package/src/ui/BrandSplash.tsx +0 -133
  290. package/src/ui/terminalTitle.ts +0 -30
  291. package/src/utils/images.ts +0 -140
  292. package/src/utils/markdownSegments.ts +0 -51
  293. package/src/utils/messages.ts +0 -37
  294. package/src/utils/withRetry.ts +0 -324
  295. /package/src/identity/{hub → manager}/continuity/state.ts +0 -0
  296. /package/src/identity/{hub → manager}/custody/helpers.ts +0 -0
  297. /package/src/identity/{hub → manager}/custody/preflight.ts +0 -0
  298. /package/src/identity/{hub → manager}/custody/state.ts +0 -0
  299. /package/src/identity/{hub → manager}/custody/useCustodyFlow.tsx +0 -0
  300. /package/src/identity/{hub → manager}/ens/EnsEditFlow.tsx +0 -0
  301. /package/src/identity/{hub → manager}/ens/advancedEnsValidation.ts +0 -0
  302. /package/src/identity/{hub → manager}/ens/state.ts +0 -0
  303. /package/src/identity/{hub → manager}/ens/transactions.ts +0 -0
  304. /package/src/identity/{hub → manager}/ens/types.ts +0 -0
  305. /package/src/identity/{hub → manager}/profile/identity.ts +0 -0
  306. /package/src/identity/{hub → manager}/restore/envelopes.ts +0 -0
  307. /package/src/identity/{hub → manager}/restore/helpers.ts +0 -0
  308. /package/src/identity/{hub → manager}/restore/recovery.ts +0 -0
  309. /package/src/identity/{hub → manager}/restore/resolve.ts +0 -0
  310. /package/src/identity/{hub → manager}/shared/components/BusyScreen.tsx +0 -0
  311. /package/src/identity/{hub → manager}/shared/components/NetworkScreen.tsx +0 -0
  312. /package/src/identity/{hub → manager}/shared/components/PinataJwtInput.tsx +0 -0
  313. /package/src/identity/{hub → manager}/shared/effects/receipts.ts +0 -0
  314. /package/src/identity/{hub → manager}/shared/effects/sync.ts +0 -0
  315. /package/src/identity/{hub → manager}/shared/model/format.ts +0 -0
  316. /package/src/identity/{hub → manager}/shared/operatorWallets.ts +0 -0
  317. /package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/ownership.ts +0 -0
  318. /package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/types.ts +0 -0
  319. /package/src/identity/{hub → manager}/shared/reconciliation/walletSetup.ts +0 -0
  320. /package/src/identity/{hub → manager}/shared/txGuard.ts +0 -0
  321. /package/src/identity/{hub → manager}/transfer/progress.ts +0 -0
  322. /package/src/identity/{hub → manager}/transfer/state.ts +0 -0
@@ -1,140 +0,0 @@
1
- import path from 'node:path'
2
- import { spawn } from 'node:child_process'
3
- import { z } from 'zod'
4
- import type { Tool } from './contracts.js'
5
- import { assessBashCommand, validateBashCommandInput } from './bashSafety.js'
6
- import { resolveWorkspacePath } from './readTool.js'
7
-
8
- const schema = z.object({
9
- command: z.string().min(1),
10
- cwd: z.string().optional(),
11
- })
12
-
13
- export const bashTool: Tool<typeof schema> = {
14
- name: 'run_bash',
15
- kind: 'bash',
16
- description: 'Run a shell command in the current workspace and return stdout, stderr, and the exit code.',
17
- inputSchema: schema,
18
- inputSchemaJson: {
19
- type: 'object',
20
- properties: {
21
- command: { type: 'string', description: 'Shell command to run.' },
22
- cwd: { type: 'string', description: 'Optional working directory inside the workspace.' },
23
- },
24
- required: ['command'],
25
- },
26
- parse(input) {
27
- const parsed = schema.parse(input)
28
- const validationError = validateBashCommandInput(parsed.command)
29
- if (validationError) {
30
- throw new Error(validationError)
31
- }
32
- return parsed
33
- },
34
- async buildPermissionRequest(input, context) {
35
- const cwd = input.cwd ? resolveWorkspacePath(context.workspaceRoot, input.cwd) : context.workspaceRoot
36
- const safety = assessBashCommand(input.command)
37
- return {
38
- kind: 'bash',
39
- command: input.command,
40
- commandPrefix: safety.commandPrefix,
41
- cwd,
42
- title: 'Allow shell command?',
43
- subtitle: `${input.command}\n${cwd}`,
44
- warning: safety.warning,
45
- canPersistExact: safety.canPersistExact,
46
- canPersistPrefix: safety.canPersistPrefix,
47
- }
48
- },
49
- async execute(input, context) {
50
- const cwd = input.cwd ? resolveWorkspacePath(context.workspaceRoot, input.cwd) : context.workspaceRoot
51
- const output = await runCommand(input.command, cwd, context.abortSignal)
52
- const relativeCwd = path.relative(context.workspaceRoot, cwd) || '.'
53
- const stdout = output.stdout.trim()
54
- const stderr = output.stderr.trim()
55
- const parts = [
56
- `cwd: ${relativeCwd}`,
57
- `exit: ${output.exitCode}`,
58
- stdout ? `stdout:\n${truncate(stdout)}` : '',
59
- stderr ? `stderr:\n${truncate(stderr)}` : '',
60
- ].filter(Boolean)
61
- return {
62
- ok: output.exitCode === 0,
63
- summary: `ran ${input.command}`,
64
- content: parts.join('\n\n'),
65
- }
66
- },
67
- }
68
-
69
- function runCommand(
70
- command: string,
71
- cwd: string,
72
- abortSignal?: AbortSignal,
73
- ): Promise<{ stdout: string; stderr: string; exitCode: number }> {
74
- return new Promise((resolve, reject) => {
75
- if (abortSignal?.aborted) {
76
- reject(new Error('command cancelled'))
77
- return
78
- }
79
-
80
- const child = spawn(command, {
81
- cwd,
82
- shell: true,
83
- windowsHide: true,
84
- })
85
- let settled = false
86
- const timeout = setTimeout(() => {
87
- killChildProcessTree(child.pid)
88
- }, 60_000)
89
- let stdout = ''
90
- let stderr = ''
91
- const onAbort = () => {
92
- cleanup()
93
- killChildProcessTree(child.pid)
94
- if (!settled) {
95
- settled = true
96
- reject(new Error('command cancelled'))
97
- }
98
- }
99
- const cleanup = () => {
100
- clearTimeout(timeout)
101
- abortSignal?.removeEventListener('abort', onAbort)
102
- }
103
-
104
- abortSignal?.addEventListener('abort', onAbort, { once: true })
105
-
106
- child.stdout.on('data', chunk => { stdout += String(chunk) })
107
- child.stderr.on('data', chunk => { stderr += String(chunk) })
108
- child.on('error', error => {
109
- cleanup()
110
- if (settled) return
111
- settled = true
112
- reject(error)
113
- })
114
- child.on('close', code => {
115
- cleanup()
116
- if (settled) return
117
- settled = true
118
- resolve({ stdout, stderr, exitCode: code ?? 1 })
119
- })
120
- })
121
- }
122
-
123
- function killChildProcessTree(pid: number | undefined): void {
124
- if (!pid) return
125
- if (process.platform === 'win32') {
126
- const killer = spawn('taskkill', ['/pid', String(pid), '/t', '/f'], { windowsHide: true })
127
- killer.on('error', () => {})
128
- killer.unref()
129
- return
130
- }
131
- try {
132
- process.kill(pid, 'SIGTERM')
133
- } catch {
134
- }
135
- }
136
-
137
- function truncate(text: string, max = 4000): string {
138
- if (text.length <= max) return text
139
- return `${text.slice(0, max - 3)}...`
140
- }
@@ -1,213 +0,0 @@
1
- import fs from 'node:fs/promises'
2
- import fsSync from 'node:fs'
3
- import os from 'node:os'
4
- import path from 'node:path'
5
- import { z } from 'zod'
6
- import type { Tool } from './contracts.js'
7
- import { resolveUserPath } from '../runtime/cwd.js'
8
-
9
- const schema = z.object({
10
- path: z.string().min(1),
11
- })
12
-
13
- export const changeDirectoryTool: Tool<typeof schema> = {
14
- name: 'change_directory',
15
- kind: 'cd',
16
- description: 'Change the current working directory for subsequent tool use.',
17
- inputSchema: schema,
18
- inputSchemaJson: {
19
- type: 'object',
20
- properties: {
21
- path: { type: 'string', description: 'Target directory path. May be relative to the current workspace or begin with ~.' },
22
- },
23
- required: ['path'],
24
- },
25
- parse(input) {
26
- return schema.parse(input)
27
- },
28
- async buildPermissionRequest(input, context) {
29
- const fullPath = resolveTargetDirectory(context.workspaceRoot, input.path)
30
- return {
31
- kind: 'cd',
32
- path: fullPath,
33
- relativePath: path.relative(context.workspaceRoot, fullPath) || path.basename(fullPath),
34
- directoryPath: path.dirname(fullPath),
35
- title: 'Allow directory change?',
36
- subtitle: fullPath,
37
- }
38
- },
39
- async execute(input, context) {
40
- const fullPath = resolveTargetDirectory(context.workspaceRoot, input.path)
41
- const stat = await fs.stat(fullPath)
42
- if (!stat.isDirectory()) throw new Error(`not a directory: ${input.path}`)
43
- context.changeDirectory?.(fullPath)
44
- return {
45
- ok: true,
46
- summary: `changed directory to ${fullPath}`,
47
- content: fullPath,
48
- }
49
- },
50
- }
51
-
52
- function resolveTargetDirectory(workspaceRoot: string, requestedPath: string): string {
53
- return resolveDirectoryIntent(requestedPath, workspaceRoot)
54
- }
55
-
56
- function resolveDirectoryIntent(input: string, workspaceRoot: string): string {
57
- const normalized = normalizeIntentInput(input)
58
- if (!normalized) {
59
- throw new Error('Missing directory path')
60
- }
61
-
62
- if (looksLikeConcretePath(normalized)) {
63
- return resolveUserPath(normalized, workspaceRoot)
64
- }
65
-
66
- const scoped = resolveScopedPhrase(normalized, workspaceRoot)
67
- if (scoped) return scoped
68
-
69
- const direct = resolveDirectoryHint(normalized, workspaceRoot)
70
- if (direct) return direct
71
-
72
- return resolveUserPath(normalized, workspaceRoot)
73
- }
74
-
75
- function resolveScopedPhrase(input: string, workspaceRoot: string): string | undefined {
76
- const normalized = simplifyNaturalPhrase(input)
77
- const parts = normalized
78
- .split(/\b(?:in|into|inside|under|within)\b/g)
79
- .map(part => part.trim())
80
- .filter(Boolean)
81
- if (parts.length < 2) return undefined
82
-
83
- const baseHint = parts.at(-1)
84
- if (!baseHint) return undefined
85
- const targetHint = parts.slice(0, -1).join(' ').trim()
86
- const baseDir = resolveDirectoryHint(baseHint, workspaceRoot)
87
- if (!baseDir) return undefined
88
- if (!targetHint) return baseDir
89
-
90
- if (looksLikeConcretePath(targetHint)) {
91
- return path.resolve(baseDir, targetHint)
92
- }
93
-
94
- const match = findNamedChild(baseDir, targetHint)
95
- if (match) return match
96
-
97
- const segments = targetHint
98
- .split(/[\\/]/)
99
- .map(part => part.trim())
100
- .filter(Boolean)
101
- if (segments.length === 0) return baseDir
102
-
103
- let current = baseDir
104
- for (const segment of segments) {
105
- const next = findNamedChild(current, segment)
106
- if (!next) {
107
- return path.join(baseDir, ...segments)
108
- }
109
- current = next
110
- }
111
- return current
112
- }
113
-
114
- function resolveDirectoryHint(input: string, workspaceRoot: string): string | undefined {
115
- const hint = simplifyNaturalPhrase(input)
116
- if (!hint) return undefined
117
- if (looksLikeConcretePath(hint)) return resolveUserPath(hint, workspaceRoot)
118
-
119
- const normalizedHint = hint.toLowerCase()
120
- const anchors = buildSearchAnchors(workspaceRoot)
121
-
122
- for (const candidate of anchors) {
123
- if (path.basename(candidate).toLowerCase() === normalizedHint) return candidate
124
- }
125
-
126
- for (const anchor of anchors) {
127
- const child = findNamedChild(anchor, normalizedHint)
128
- if (child) return child
129
- }
130
-
131
- return undefined
132
- }
133
-
134
- function buildSearchAnchors(workspaceRoot: string): string[] {
135
- const home = os.homedir()
136
- const seen = new Set<string>()
137
- const out: string[] = []
138
- const add = (candidate: string) => {
139
- const resolved = path.resolve(candidate)
140
- if (seen.has(resolved)) return
141
- seen.add(resolved)
142
- out.push(resolved)
143
- }
144
-
145
- for (const dir of ancestorDirectories(workspaceRoot)) add(dir)
146
- add(home)
147
- for (const child of safeReadDirectories(home)) add(child)
148
- return out
149
- }
150
-
151
- function ancestorDirectories(start: string): string[] {
152
- const out: string[] = []
153
- let current = path.resolve(start)
154
- while (true) {
155
- out.push(current)
156
- const parent = path.dirname(current)
157
- if (parent === current) break
158
- current = parent
159
- }
160
- return out
161
- }
162
-
163
- function findNamedChild(parent: string, nameOrPhrase: string): string | undefined {
164
- const wanted = simplifyNaturalPhrase(nameOrPhrase).toLowerCase()
165
- if (!wanted) return undefined
166
-
167
- const children = safeReadDirectories(parent)
168
- for (const child of children) {
169
- if (path.basename(child).toLowerCase() === wanted) return child
170
- }
171
-
172
- return undefined
173
- }
174
-
175
- function safeReadDirectories(dir: string): string[] {
176
- try {
177
- return fsSync.readdirSync(dir, { withFileTypes: true })
178
- .filter(entry => entry.isDirectory())
179
- .map(entry => path.join(dir, entry.name))
180
- } catch {
181
- return []
182
- }
183
- }
184
-
185
- function normalizeIntentInput(input: string): string {
186
- return input
187
- .trim()
188
- .replace(/\?+$/, '')
189
- .replace(/^["'`]+|["'`]+$/g, '')
190
- .replace(/\s+/g, ' ')
191
- .trim()
192
- }
193
-
194
- function simplifyNaturalPhrase(input: string): string {
195
- return input
196
- .toLowerCase()
197
- .replace(/\b(?:the|my)\b/g, ' ')
198
- .replace(/\b(?:folder|directory)\b/g, ' ')
199
- .replace(/\s+/g, ' ')
200
- .trim()
201
- }
202
-
203
- function looksLikeConcretePath(input: string): boolean {
204
- return (
205
- input.startsWith('~') ||
206
- /^[A-Za-z]:[\\/]/.test(input) ||
207
- input.startsWith('/') ||
208
- input.startsWith('./') ||
209
- input.startsWith('../') ||
210
- input.includes('\\') ||
211
- input.includes('/')
212
- )
213
- }
@@ -1,192 +0,0 @@
1
- import { z } from 'zod'
2
- import type { EthagentConfig } from '../storage/config.js'
3
-
4
- import type { McpRuntime } from '../mcp/manager.js'
5
-
6
- export type ToolKind = 'read' | 'write' | 'edit' | 'delete' | 'bash' | 'cd' | 'private-continuity-read' | 'private-continuity-edit' | 'mcp'
7
-
8
- export type PermissionRequest =
9
- | {
10
- kind: 'read'
11
- path: string
12
- relativePath: string
13
- directoryPath: string
14
- title: string
15
- subtitle: string
16
- }
17
- | {
18
- kind: 'write'
19
- path: string
20
- relativePath: string
21
- directoryPath: string
22
- title: string
23
- subtitle: string
24
- before: string
25
- after: string
26
- diff: string
27
- changeSummary: string
28
- }
29
- | {
30
- kind: 'edit'
31
- path: string
32
- relativePath: string
33
- directoryPath: string
34
- title: string
35
- subtitle: string
36
- before: string
37
- after: string
38
- diff: string
39
- changeSummary: string
40
- }
41
- | {
42
- kind: 'private-continuity-read'
43
- path: string
44
- relativePath: string
45
- directoryPath: string
46
- title: string
47
- subtitle: string
48
- file: 'SOUL.md' | 'MEMORY.md'
49
- range: string
50
- }
51
- | {
52
- kind: 'private-skill-read'
53
- path: string
54
- relativePath: string
55
- directoryPath: string
56
- title: string
57
- subtitle: string
58
- skillName: string
59
- mode: 'list' | 'read'
60
- }
61
- | {
62
- kind: 'private-continuity-edit'
63
- path: string
64
- relativePath: string
65
- directoryPath: string
66
- title: string
67
- subtitle: string
68
- file: 'SOUL.md' | 'MEMORY.md'
69
- before: string
70
- after: string
71
- diff: string
72
- changeSummary: string
73
- }
74
- | {
75
- kind: 'delete'
76
- path: string
77
- relativePath: string
78
- directoryPath: string
79
- title: string
80
- subtitle: string
81
- before: string
82
- after: string
83
- diff: string
84
- changeSummary: string
85
- }
86
- | {
87
- kind: 'bash'
88
- command: string
89
- commandPrefix: string
90
- cwd: string
91
- title: string
92
- subtitle: string
93
- warning?: string
94
- canPersistExact: boolean
95
- canPersistPrefix: boolean
96
- }
97
- | {
98
- kind: 'mcp'
99
- title: string
100
- subtitle: string
101
- serverName: string
102
- normalizedServerName: string
103
- toolName: string
104
- toolKey: string
105
- readOnly: boolean
106
- destructive: boolean
107
- openWorld: boolean
108
- canPersistServer: boolean
109
- }
110
- | {
111
- kind: 'cd'
112
- path: string
113
- relativePath: string
114
- directoryPath: string
115
- title: string
116
- subtitle: string
117
- }
118
-
119
- export type PermissionMode = 'default' | 'plan' | 'accept-edits'
120
-
121
- export const SessionPermissionRuleSchema = z.union([
122
- z.object({ kind: z.literal('read'), scope: z.literal('kind') }),
123
- z.object({ kind: z.literal('read'), scope: z.literal('path'), path: z.string().min(1) }),
124
- z.object({ kind: z.literal('read'), scope: z.literal('directory'), path: z.string().min(1) }),
125
- z.object({ kind: z.literal('edit'), scope: z.literal('kind') }),
126
- z.object({ kind: z.literal('edit'), scope: z.literal('path'), path: z.string().min(1) }),
127
- z.object({ kind: z.literal('edit'), scope: z.literal('directory'), path: z.string().min(1) }),
128
- z.object({ kind: z.literal('write'), scope: z.literal('kind') }),
129
- z.object({ kind: z.literal('write'), scope: z.literal('path'), path: z.string().min(1) }),
130
- z.object({ kind: z.literal('write'), scope: z.literal('directory'), path: z.string().min(1) }),
131
- z.object({ kind: z.literal('delete'), scope: z.literal('kind') }),
132
- z.object({ kind: z.literal('delete'), scope: z.literal('path'), path: z.string().min(1) }),
133
- z.object({ kind: z.literal('delete'), scope: z.literal('directory'), path: z.string().min(1) }),
134
- z.object({ kind: z.literal('cd'), scope: z.literal('kind') }),
135
- z.object({ kind: z.literal('cd'), scope: z.literal('path'), path: z.string().min(1) }),
136
- z.object({ kind: z.literal('cd'), scope: z.literal('directory'), path: z.string().min(1) }),
137
- z.object({ kind: z.literal('bash'), scope: z.literal('command'), command: z.string().min(1), cwd: z.string().min(1) }),
138
- z.object({ kind: z.literal('bash'), scope: z.literal('prefix'), commandPrefix: z.string().min(1), cwd: z.string().min(1) }),
139
- z.object({ kind: z.literal('mcp'), scope: z.literal('tool'), toolKey: z.string().min(1) }),
140
- z.object({ kind: z.literal('mcp'), scope: z.literal('server'), normalizedServerName: z.string().min(1) }),
141
- ])
142
-
143
- export type SessionPermissionRule = z.infer<typeof SessionPermissionRuleSchema>
144
-
145
- export type PermissionDecision =
146
- | 'allow-once'
147
- | 'allow-kind-project'
148
- | 'allow-path-project'
149
- | 'allow-directory-project'
150
- | 'allow-command-project'
151
- | 'allow-command-prefix-project'
152
- | 'allow-mcp-tool-project'
153
- | 'allow-mcp-server-project'
154
- | 'deny'
155
-
156
- export type ToolResult =
157
- | { ok: true; summary: string; content: string }
158
- | { ok: false; summary: string; content: string }
159
-
160
- export type ToolExecutionContext = {
161
- workspaceRoot: string
162
- config?: EthagentConfig
163
- abortSignal?: AbortSignal
164
- mcp?: McpRuntime
165
- changeDirectory?: (next: string) => void
166
- checkpoint?: {
167
- sessionId: string
168
- turnId: string
169
- messageRole: 'user'
170
- promptSnippet: string
171
- checkpointLabel: string
172
- }
173
- }
174
-
175
- export type Tool<Input extends z.ZodTypeAny = z.ZodTypeAny> = {
176
- name: string
177
- kind: ToolKind
178
- description: string
179
- inputSchema: Input
180
- inputSchemaJson: {
181
- type: 'object'
182
- properties?: Record<string, unknown>
183
- required?: string[]
184
- oneOf?: Array<Record<string, unknown>>
185
- anyOf?: Array<Record<string, unknown>>
186
- additionalProperties?: boolean
187
- }
188
- readOnly?: boolean
189
- parse(input: Record<string, unknown>): z.infer<Input>
190
- buildPermissionRequest(input: z.infer<Input>, context: ToolExecutionContext): Promise<PermissionRequest>
191
- execute(input: z.infer<Input>, context: ToolExecutionContext): Promise<ToolResult>
192
- }
@@ -1,116 +0,0 @@
1
- import fs from 'node:fs/promises'
2
- import path from 'node:path'
3
- import { z } from 'zod'
4
- import { recordRewindSnapshot } from '../storage/rewind.js'
5
- import type { Tool } from './contracts.js'
6
- import { formatFileChangeResult, renderUnifiedFileDiff } from './fileDiff.js'
7
- import { resolveWorkspacePath } from './readTool.js'
8
-
9
- const schema = z.object({
10
- path: z.string().min(1),
11
- })
12
-
13
- export const deleteFileTool: Tool<typeof schema> = {
14
- name: 'delete_file',
15
- kind: 'delete',
16
- description: 'Delete one file in the current workspace. Use this for user requests to remove a file; do not use run_bash for normal file deletion.',
17
- inputSchema: schema,
18
- inputSchemaJson: {
19
- type: 'object',
20
- properties: {
21
- path: { type: 'string', description: 'Path to the file to delete.' },
22
- },
23
- required: ['path'],
24
- },
25
- parse(input) {
26
- return schema.parse(input)
27
- },
28
- async buildPermissionRequest(input, context) {
29
- const prepared = await prepareDelete(input, context)
30
- return {
31
- kind: 'delete',
32
- path: prepared.fullPath,
33
- relativePath: prepared.relativePath,
34
- directoryPath: path.dirname(prepared.fullPath),
35
- title: 'Allow file delete?',
36
- subtitle: prepared.fullPath,
37
- before: preview(prepared.before),
38
- after: '(deleted)',
39
- diff: renderUnifiedFileDiff({ filePath: prepared.relativePath, before: prepared.before, after: '' }),
40
- changeSummary: `delete ${prepared.relativePath}`,
41
- }
42
- },
43
- async execute(input, context) {
44
- const prepared = await prepareDelete(input, context)
45
- const rewindWarning = await tryRecordRewindSnapshot({
46
- workspaceRoot: context.workspaceRoot,
47
- filePath: prepared.fullPath,
48
- relativePath: prepared.relativePath,
49
- existedBefore: true,
50
- previousContent: prepared.before,
51
- changeSummary: `restore deleted ${prepared.relativePath}`,
52
- createdAt: new Date().toISOString(),
53
- sessionId: context.checkpoint?.sessionId,
54
- turnId: context.checkpoint?.turnId,
55
- messageRole: context.checkpoint?.messageRole,
56
- promptSnippet: context.checkpoint?.promptSnippet,
57
- checkpointLabel: context.checkpoint?.checkpointLabel,
58
- })
59
- await fs.unlink(prepared.fullPath)
60
- return {
61
- ok: true,
62
- summary: `deleted ${prepared.relativePath}`,
63
- content: formatFileChangeResult(
64
- rewindWarning
65
- ? `deleted ${prepared.fullPath}\nwarning: ${rewindWarning}`
66
- : `deleted ${prepared.fullPath}`,
67
- renderUnifiedFileDiff({ filePath: prepared.relativePath, before: prepared.before, after: '' }),
68
- ),
69
- }
70
- },
71
- }
72
-
73
- async function prepareDelete(input: z.infer<typeof schema>, context: { workspaceRoot: string }) {
74
- assertSafeDeletePath(input.path)
75
- const fullPath = resolveWorkspacePath(context.workspaceRoot, input.path)
76
- const stats = await fs.stat(fullPath)
77
- if (stats.isDirectory()) {
78
- throw new Error('Tool delete_file path points to a directory; provide a file path')
79
- }
80
- const before = await fs.readFile(fullPath, 'utf8')
81
- return {
82
- fullPath,
83
- relativePath: path.relative(context.workspaceRoot, fullPath) || path.basename(fullPath),
84
- before,
85
- }
86
- }
87
-
88
- function assertSafeDeletePath(requestedPath: string): void {
89
- const trimmed = requestedPath.trim()
90
- if (trimmed !== requestedPath || trimmed.length === 0) {
91
- throw new Error('Tool delete_file path must be a clean workspace-relative file path')
92
- }
93
- if (/[|;&<>`]/.test(trimmed)) {
94
- throw new Error('Tool delete_file path must not contain shell operators')
95
- }
96
- if (/^(?:rm|del|erase|rmdir|remove-item|mkdir|type|cat|echo|copy|move|mv|cp)\b/i.test(trimmed)) {
97
- throw new Error('Tool delete_file path looks like a shell command; pass only the file path')
98
- }
99
- }
100
-
101
- async function tryRecordRewindSnapshot(
102
- snapshot: Parameters<typeof recordRewindSnapshot>[0],
103
- ): Promise<string | undefined> {
104
- try {
105
- await recordRewindSnapshot(snapshot)
106
- return undefined
107
- } catch (error: unknown) {
108
- const message = (error as Error).message || 'rewind checkpoint could not be recorded'
109
- return `rewind checkpoint was not recorded (${message})`
110
- }
111
- }
112
-
113
- function preview(text: string, max = 1200): string {
114
- if (text.length <= max) return text
115
- return `${text.slice(0, max - 3)}...`
116
- }