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,494 +0,0 @@
1
- import type { EthagentConfig, ProviderId } from '../storage/config.js'
2
- import { getConfigPath, localProviderBaseUrlFor, saveConfig } from '../storage/config.js'
3
- import { detectLlamaCpp } from '../models/llamacpp.js'
4
- import { detectSpec } from '../models/runtimeDetection.js'
5
- import { hasKey } from '../storage/secrets.js'
6
- import { getIdentityStatus } from '../storage/identity.js'
7
- import { discoverProviderModels, type ModelCatalogResult } from '../models/catalog.js'
8
- import { getLocalHfCacheDir, loadLocalHfModels } from '../models/huggingface.js'
9
- import { copyToClipboard } from '../utils/clipboard.js'
10
- import { parseSegments } from '../utils/markdownSegments.js'
11
- import { exportSessionMarkdown } from '../storage/sessionExport.js'
12
- import { rewindWorkspaceEdits } from '../storage/rewind.js'
13
- import type { SessionMessage } from '../storage/sessions.js'
14
- import path from 'node:path'
15
- import { setCwd } from '../runtime/cwd.js'
16
- import type { SessionMode } from '../runtime/sessionMode.js'
17
- import type { ContextUsage } from '../runtime/compaction.js'
18
- import { formatModelDisplayName } from '../models/modelDisplay.js'
19
- import { providerDisplayName } from '../models/modelPickerOptions.js'
20
- import type { McpManager } from '../mcp/manager.js'
21
- import { runIdentity, runMcp } from './slashCommandHandlers.js'
22
- import { renderStatus } from './slashCommandViews.js'
23
-
24
- export type IdentityRequestAction =
25
- | 'manage'
26
- | 'create'
27
- | 'load'
28
-
29
- export type SlashContext = {
30
- config: EthagentConfig
31
- turns: number
32
- approxTokens: number
33
- contextUsage: ContextUsage
34
- startedAt: number
35
- sessionId: string
36
- cwd: string
37
- mode: SessionMode
38
- sessionMessages: () => SessionMessage[]
39
- assistantTurns: () => string[]
40
- onReplaceConfig: (next: EthagentConfig) => void
41
- onChangeCwd: (next: string) => void
42
- onClear: () => void
43
- onExit: () => void
44
- onResumeRequest: () => void
45
- onModelPickerRequest: () => void
46
- onRewindRequest: () => void
47
- onPermissionsRequest: () => void
48
- onCompactRequest: () => void
49
- onIdentityRequest: (action?: IdentityRequestAction) => void
50
- onCopyPickerRequest: (turnText: string, turnLabel: string) => void
51
- mcp?: McpManager
52
- }
53
-
54
- export type SlashResult =
55
- | { kind: 'note'; text: string; variant?: 'info' | 'error' | 'dim' }
56
- | { kind: 'submit'; text: string }
57
- | { kind: 'handled' }
58
-
59
- type CommandSpec = {
60
- name: string
61
- aliases?: string[]
62
- summary: string
63
- hidden?: boolean
64
- requiresArgs?: boolean
65
- blockedInPlan?: boolean
66
- enterBehavior?: 'execute' | 'fill'
67
- run: (args: string, ctx: SlashContext) => Promise<SlashResult> | SlashResult
68
- }
69
-
70
- export type SlashSuggestion = {
71
- name: string
72
- summary: string
73
- completion: string
74
- executeOnEnter: boolean
75
- }
76
-
77
- export function parseSlash(input: string): { name: string; args: string } | null {
78
- const trimmed = input.trim()
79
- if (!trimmed.startsWith('/')) return null
80
- const body = trimmed.slice(1)
81
- const spaceIdx = body.search(/\s/)
82
- if (spaceIdx === -1) return { name: body.toLowerCase(), args: '' }
83
- return {
84
- name: body.slice(0, spaceIdx).toLowerCase(),
85
- args: body.slice(spaceIdx + 1).trim(),
86
- }
87
- }
88
-
89
- const COMMANDS: CommandSpec[] = [
90
- {
91
- name: 'help',
92
- summary: 'show this list',
93
- run: () => ({ kind: 'note', text: renderHelp() }),
94
- },
95
- {
96
- name: 'exit',
97
- aliases: ['quit'],
98
- summary: 'exit the agent',
99
- run: (_args, ctx) => {
100
- ctx.onExit()
101
- return { kind: 'handled' }
102
- },
103
- },
104
- {
105
- name: 'new',
106
- aliases: ['clear'],
107
- summary: 'clear the transcript and start a new session',
108
- run: (_args, ctx) => {
109
- ctx.onClear()
110
- return { kind: 'handled' }
111
- },
112
- },
113
- {
114
- name: 'status',
115
- summary: 'provider, model, session id, turns, context, state, cwd, elapsed',
116
- run: (_args, ctx) => ({ kind: 'note', text: renderStatus(ctx) }),
117
- },
118
- {
119
- name: 'context',
120
- summary: 'show model context usage and compaction options',
121
- run: (_args, ctx) => ({ kind: 'note', text: renderContext(ctx) }),
122
- },
123
- {
124
- name: 'cd',
125
- requiresArgs: true,
126
- enterBehavior: 'fill',
127
- summary: 'change working directory · /cd <path>',
128
- run: async (args, ctx) => {
129
- const target = args.trim()
130
- if (!target) return { kind: 'note', variant: 'error', text: 'usage: /cd <path>' }
131
- try {
132
- const next = setCwd(target, ctx.cwd)
133
- ctx.onChangeCwd(next)
134
- return { kind: 'note', text: `Cwd: ${next}`, variant: 'dim' }
135
- } catch (err: unknown) {
136
- return { kind: 'note', variant: 'error', text: `Cd failed: ${(err as Error).message}` }
137
- }
138
- },
139
- },
140
- {
141
- name: 'config',
142
- summary: 'show resolved config',
143
- run: (_args, ctx) => ({
144
- kind: 'note',
145
- text: `${JSON.stringify(ctx.config, null, 2)}\npath: ${getConfigPath()}`,
146
- }),
147
- },
148
- {
149
- name: 'models',
150
- summary: 'list models for the current provider',
151
- run: async (_args, ctx) => {
152
- if (ctx.config.provider === 'llamacpp') {
153
- const installed = await loadLocalHfModels()
154
- if (installed.length === 0) {
155
- return {
156
- kind: 'note',
157
- text: 'No local model files downloaded. Open Alt+P and choose "Add Local Model File".',
158
- }
159
- }
160
- const lines = installed.map(m => {
161
- const marker = m.id === ctx.config.model ? '*' : ' '
162
- const q = m.quantization ? ` ${m.quantization}` : ''
163
- const displayName = formatModelDisplayName('llamacpp', m.id, { displayName: m.displayName, maxLength: 64 })
164
- return `${marker} ${displayName}${q} ${formatBytes(m.sizeBytes)} ${m.risk}`
165
- })
166
- return { kind: 'note', text: ['installed Hugging Face models:', ...lines].join('\n') }
167
- }
168
-
169
- const catalog = await discoverProviderModels(ctx.config)
170
- return {
171
- kind: 'note',
172
- text: renderModelCatalog(catalog, ctx.config.model),
173
- variant: catalog.status === 'fallback' ? 'dim' : 'info',
174
- }
175
- },
176
- },
177
- {
178
- name: 'model',
179
- enterBehavior: 'fill',
180
- summary: 'open picker or switch model · /model [name]',
181
- run: async (args, ctx) => {
182
- const name = args.trim()
183
- if (!name) {
184
- ctx.onModelPickerRequest()
185
- return { kind: 'handled' }
186
- }
187
- if (ctx.config.provider === 'llamacpp') {
188
- const installed = await loadLocalHfModels()
189
- if (!installed.some(m => m.id === name)) {
190
- return {
191
- kind: 'note',
192
- variant: 'error',
193
- text: `'${name}' is not downloaded. Open Alt+P and choose "View Full Catalog" or "Add Local Model File".`,
194
- }
195
- }
196
- } else {
197
- const catalog = await discoverProviderModels(ctx.config)
198
- if (catalog.status === 'ok' && !catalog.entries.some(entry => entry.id === name)) {
199
- return {
200
- kind: 'note',
201
- variant: 'error',
202
- text: `'${name}' was not found for ${providerDisplayName(ctx.config.provider)}. use /models to inspect available models.`,
203
- }
204
- }
205
- }
206
- const next: EthagentConfig = {
207
- ...ctx.config,
208
- model: name,
209
- baseUrl: baseUrlForModelSwitch(ctx.config),
210
- }
211
- await saveConfig(next)
212
- ctx.onReplaceConfig(next)
213
- return { kind: 'note', text: `Now using ${providerDisplayName(next.provider)} · ${formatModelDisplayName(next.provider, name, { maxLength: 64 })}.` }
214
- },
215
- },
216
- {
217
- name: 'resume',
218
- summary: 'reopen a prior session',
219
- run: (_args, ctx) => {
220
- ctx.onResumeRequest()
221
- return { kind: 'handled' }
222
- },
223
- },
224
- {
225
- name: 'rewind',
226
- aliases: ['checkpoint'],
227
- summary: 'restore recent managed file edits · /rewind [n]',
228
- run: async (args, ctx) => {
229
- const trimmed = args.trim()
230
- if (!trimmed) {
231
- ctx.onRewindRequest()
232
- return { kind: 'handled' }
233
- }
234
- const steps = trimmed ? Number.parseInt(trimmed, 10) : 1
235
- if (!Number.isFinite(steps) || steps < 1) {
236
- return { kind: 'note', variant: 'error', text: 'usage: /rewind [n]' }
237
- }
238
- const result = await rewindWorkspaceEdits(ctx.cwd, steps)
239
- if (result.reverted === 0) {
240
- return { kind: 'note', variant: 'error', text: 'No managed edits available to rewind in this directory.' }
241
- }
242
- const files = result.files.map(file => path.relative(ctx.cwd, file) || path.basename(file))
243
- return {
244
- kind: 'note',
245
- text: `Rewound ${result.reverted} edit${result.reverted === 1 ? '' : 's'}.\n${files.join('\n')}`,
246
- variant: 'dim',
247
- }
248
- },
249
- },
250
- {
251
- name: 'compact',
252
- summary: 'summarize older turns to free up context',
253
- run: (_args, ctx) => {
254
- ctx.onCompactRequest()
255
- return { kind: 'handled' }
256
- },
257
- },
258
- {
259
- name: 'permissions',
260
- summary: 'review or remove saved permission rules for this project',
261
- run: (_args, ctx) => {
262
- ctx.onPermissionsRequest()
263
- return { kind: 'handled' }
264
- },
265
- },
266
- {
267
- name: 'copy',
268
- summary: 'copy an assistant reply to the clipboard · /copy [n] (1 = latest)',
269
- run: async (args, ctx) => {
270
- const assistant = ctx.assistantTurns()
271
- if (assistant.length === 0) {
272
- return { kind: 'note', variant: 'error', text: 'Nothing to copy yet.' }
273
- }
274
- let offset = 1
275
- const trimmed = args.trim()
276
- if (trimmed) {
277
- const parsed = Number.parseInt(trimmed, 10)
278
- if (!Number.isFinite(parsed) || parsed < 1) {
279
- return { kind: 'note', variant: 'error', text: 'usage: /copy [n] (n counts back from the latest reply, 1 = most recent)' }
280
- }
281
- offset = parsed
282
- }
283
- const index = assistant.length - offset
284
- if (index < 0) {
285
- return { kind: 'note', variant: 'error', text: `Only ${assistant.length} assistant reply on record.` }
286
- }
287
- const text = assistant[index] ?? ''
288
- const label = offset === 1 ? 'Latest reply' : `Reply #${offset} back`
289
- const segments = parseSegments(text)
290
- const hasCode = segments.some(segment => segment.kind === 'code')
291
- if (!hasCode) {
292
- const result = await copyToClipboard(text)
293
- if (!result.ok) {
294
- return { kind: 'note', variant: 'error', text: `Copy failed: ${result.error}` }
295
- }
296
- return { kind: 'note', text: `${label} copied to clipboard · ${result.chars} chars`, variant: 'dim' }
297
- }
298
- ctx.onCopyPickerRequest(text, label)
299
- return { kind: 'handled' }
300
- },
301
- },
302
- {
303
- name: 'export',
304
- blockedInPlan: true,
305
- summary: 'write the transcript to a markdown file',
306
- run: async (_args, ctx) => {
307
- const messages = ctx.sessionMessages()
308
- if (messages.length === 0) {
309
- return { kind: 'note', variant: 'error', text: 'Nothing to export yet.' }
310
- }
311
- try {
312
- const file = await exportSessionMarkdown(ctx.sessionId, messages, {
313
- model: ctx.config.model,
314
- provider: ctx.config.provider,
315
- })
316
- return { kind: 'note', text: `Exported to ${file}` }
317
- } catch (err: unknown) {
318
- return { kind: 'note', variant: 'error', text: `Export failed: ${(err as Error).message}` }
319
- }
320
- },
321
- },
322
- {
323
- name: 'mcp',
324
- enterBehavior: 'fill',
325
- summary: 'manage MCP servers · /mcp [status|approve|reject|reconnect|enable|disable|add-json]',
326
- run: async (args, ctx) => runMcp(args, ctx),
327
- },
328
- {
329
- name: 'identity',
330
- enterBehavior: 'fill',
331
- summary: 'agent identity · /identity [status|create|load|remove confirm]',
332
- run: async (args, ctx) => runIdentity(args, ctx),
333
- },
334
- {
335
- name: 'doctor',
336
- summary: 'spec, config, local runtime, key presence',
337
- run: async (_args, ctx) => {
338
- const [spec, keys, identity, llamaCpp, hfModels] = await Promise.all([
339
- detectSpec(),
340
- Promise.all(
341
- (['openai', 'anthropic', 'gemini'] as ProviderId[]).map(async p => [p, await hasKey(p)] as const),
342
- ),
343
- getIdentityStatus(ctx.config),
344
- detectLlamaCpp(),
345
- loadLocalHfModels(),
346
- ])
347
- return { kind: 'note', text: renderDoctor(spec, keys, identity, ctx, llamaCpp, hfModels.length) }
348
- },
349
- },
350
- ]
351
-
352
- function renderHelp(): string {
353
- const visibleCommands = COMMANDS.filter(c => !c.hidden)
354
- const maxName = Math.max(...visibleCommands.map(c => commandLabel(c).length))
355
- const lines = visibleCommands.map(c => {
356
- const label = commandLabel(c)
357
- return ` ${label.padEnd(maxName)} ${c.summary}`
358
- })
359
- return [
360
- 'slash commands:',
361
- ...lines,
362
- '',
363
- 'shortcuts: esc cancels · ctrl+c twice exits · alt+p model · alt+i identity · shift+tab mode.',
364
- ].join('\n')
365
- }
366
-
367
- function commandLabel(cmd: CommandSpec): string {
368
- if (!cmd.aliases || cmd.aliases.length === 0) return `/${cmd.name}`
369
- return `/${cmd.name} (${cmd.aliases.map(a => `/${a}`).join(', ')})`
370
- }
371
-
372
- function renderContext(ctx: SlashContext): string {
373
- const usage = ctx.contextUsage
374
- const free = Math.max(0, usage.windowTokens - usage.usedTokens)
375
- const action =
376
- usage.percent >= 90
377
- ? 'Context is near the model limit. New requests will ask you to summarize into a new conversation, switch models, ignore and send, or cancel.'
378
- : usage.percent >= 75
379
- ? 'Context is getting full. Consider /compact before a new task boundary.'
380
- : 'Context has comfortable room.'
381
- return [
382
- 'context usage:',
383
- ` model ${providerDisplayName(ctx.config.provider)} · ${formatModelDisplayName(ctx.config.provider, ctx.config.model, { maxLength: 72 })}`,
384
- ` used ~${usage.usedTokens} / ${usage.windowTokens} tokens (${usage.percent}%)`,
385
- ` free ~${free} tokens`,
386
- ` estimate ${usage.confidence} (${usage.source})`,
387
- ' session active',
388
- '',
389
- action,
390
- ].join('\n')
391
- }
392
-
393
- function renderDoctor(
394
- spec: Awaited<ReturnType<typeof detectSpec>>,
395
- keys: ReadonlyArray<readonly [ProviderId, boolean]>,
396
- identity: Awaited<ReturnType<typeof getIdentityStatus>>,
397
- ctx: SlashContext,
398
- llamaCpp: Awaited<ReturnType<typeof detectLlamaCpp>>,
399
- hfModelCount: number,
400
- ): string {
401
- const lines: string[] = ['diagnostics:']
402
- lines.push(` platform ${spec.platform}/${spec.arch}${spec.isAppleSilicon ? ' (apple silicon)' : ''}`)
403
- lines.push(` ram ${formatGB(spec.effectiveRamBytes)}${spec.gpuVramBytes ? ` · vram ${formatGB(spec.gpuVramBytes)}` : ''}`)
404
- lines.push(` local run ${llamaCpp.binaryPresent ? 'installed' : 'not installed'} · server ${llamaCpp.serverUp ? 'up' : 'down'}`)
405
- lines.push(` hf models ${hfModelCount} downloaded`)
406
- lines.push('')
407
- lines.push('config:')
408
- lines.push(` provider ${providerDisplayName(ctx.config.provider)}`)
409
- lines.push(` model ${formatModelDisplayName(ctx.config.provider, ctx.config.model, { maxLength: 72 })}`)
410
- if (ctx.config.baseUrl) lines.push(` baseUrl ${ctx.config.baseUrl}`)
411
- if (ctx.config.provider === 'llamacpp') lines.push(` hf cache ${getLocalHfCacheDir()}`)
412
- lines.push(` path ${getConfigPath()}`)
413
- lines.push('')
414
- lines.push('keys:')
415
- for (const [provider, present] of keys) {
416
- lines.push(` ${providerDisplayName(provider).padEnd(9)} ${present ? 'set' : 'not set'}`)
417
- }
418
- lines.push('')
419
- lines.push('identity:')
420
- if (identity) {
421
- lines.push(` address ${identity.address}`)
422
- lines.push(` backend ${identity.backend}`)
423
- if (identity.source) lines.push(` source ${identity.source}`)
424
- if (identity.agentId) lines.push(` token #${identity.agentId}`)
425
- } else {
426
- lines.push(' address not set')
427
- }
428
- return lines.join('\n')
429
- }
430
-
431
- function renderModelCatalog(catalog: ModelCatalogResult, currentModel: string): string {
432
- const title = catalog.status === 'fallback'
433
- ? `${providerDisplayName(catalog.provider)} models (fallback${catalog.error ? `: ${catalog.error}` : ''}):`
434
- : `${providerDisplayName(catalog.provider)} models:`
435
- const lines = catalog.entries.map(entry => {
436
- const marker = entry.id === currentModel ? '*' : ' '
437
- const suffix = entry.source === 'fallback' ? ' fallback' : ''
438
- return `${marker} ${formatModelDisplayName(catalog.provider, entry.id, { maxLength: 72 })}${suffix}`
439
- })
440
- return [title, ...lines].join('\n')
441
- }
442
-
443
- function baseUrlForModelSwitch(config: EthagentConfig): string | undefined {
444
- if (config.provider === 'llamacpp') return localProviderBaseUrlFor('llamacpp', config.baseUrl)
445
- if (config.provider === 'openai') return config.baseUrl
446
- return undefined
447
- }
448
-
449
- function formatBytes(bytes: number): string {
450
- if (bytes <= 0) return '—'
451
- const gb = bytes / (1024 * 1024 * 1024)
452
- if (gb >= 1) return `${gb.toFixed(1)}GB`
453
- const mb = bytes / (1024 * 1024)
454
- return `${mb.toFixed(0)}MB`
455
- }
456
-
457
- function formatGB(bytes: number): string {
458
- const gb = bytes / (1024 * 1024 * 1024)
459
- if (gb >= 10) return `${Math.round(gb)}GB`
460
- return `${gb.toFixed(1)}GB`
461
- }
462
-
463
- export async function dispatchSlash(input: string, ctx: SlashContext): Promise<SlashResult | null> {
464
- const parsed = parseSlash(input)
465
- if (!parsed) return null
466
- const cmd = COMMANDS.find(c => c.name === parsed.name || c.aliases?.includes(parsed.name))
467
- if (!cmd) {
468
- try {
469
- const promptText = await ctx.mcp?.runPromptSlash(parsed.name, parsed.args)
470
- if (promptText !== null && promptText !== undefined) return { kind: 'submit', text: promptText }
471
- } catch (err: unknown) {
472
- return { kind: 'note', variant: 'error', text: `MCP prompt failed: ${(err as Error).message}` }
473
- }
474
- return { kind: 'note', variant: 'error', text: `Unknown command: /${parsed.name}. Try /help` }
475
- }
476
- if (ctx.mode === 'plan' && cmd.blockedInPlan) {
477
- return { kind: 'note', variant: 'error', text: `/${cmd.name} is blocked in plan mode.` }
478
- }
479
- return cmd.run(parsed.args, ctx)
480
- }
481
-
482
- export function getSlashSuggestions(extra: SlashSuggestion[] = []): SlashSuggestion[] {
483
- return [...COMMANDS.filter(c => !c.hidden).map(c => ({
484
- name: c.name,
485
- summary: c.summary,
486
- completion: c.requiresArgs || c.enterBehavior === 'fill' ? `/${c.name} ` : `/${c.name}`,
487
- executeOnEnter: !c.requiresArgs && c.enterBehavior !== 'fill',
488
- })), ...extra]
489
- }
490
-
491
- function nthTokenStart(value: string, tokenIndex: number): number {
492
- const matches = [...value.matchAll(/\S+/g)]
493
- return matches[tokenIndex]?.index ?? -1
494
- }
@@ -1,42 +0,0 @@
1
- import { splitFileChangeResult } from '../tools/fileDiff.js'
2
- import type { ContinuityEditReviewState } from './views/ContinuityEditReviewView.js'
3
-
4
- export function privateContinuityEditReviewFromToolResult(
5
- name: string,
6
- input: Record<string, unknown>,
7
- result: { ok: boolean; summary: string; content: string },
8
- ): ContinuityEditReviewState | null {
9
- if (name !== 'propose_private_continuity_edit' || !result.ok) return null
10
- const file = normalizePrivateContinuityFile(input.file)
11
- if (!file) return null
12
- const parsed = splitFileChangeResult(result.content)
13
- const filePath = extractReviewFilePath(parsed.content)
14
- if (!filePath) return null
15
- return {
16
- file,
17
- filePath,
18
- summary: result.summary,
19
- ...(parsed.diff ? { diff: parsed.diff } : {}),
20
- }
21
- }
22
-
23
- function normalizePrivateContinuityFile(value: unknown): ContinuityEditReviewState['file'] | null {
24
- if (typeof value !== 'string') return null
25
- if (/^soul\.md$/i.test(value.trim())) return 'SOUL.md'
26
- if (/^memory\.md$/i.test(value.trim())) return 'MEMORY.md'
27
- return null
28
- }
29
-
30
- function extractReviewFilePath(content: string): string | null {
31
- for (const line of content.split(/\r?\n/)) {
32
- const review = line.match(/^(?:[-*]\s+)?review file:\s*(.+)$/i)
33
- if (review?.[1]?.trim()) return cleanReviewFilePath(review[1])
34
- const updated = line.match(/^(?:[-*]\s+)?updated local private continuity file\s+(.+)$/i)
35
- if (updated?.[1]?.trim()) return cleanReviewFilePath(updated[1])
36
- }
37
- return null
38
- }
39
-
40
- function cleanReviewFilePath(value: string): string {
41
- return value.trim().replace(/^`+|`+$/g, '').trim()
42
- }