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,56 +0,0 @@
1
- import { createHfDownloadPlan, ggufFiles, loadLocalHfModels } from './huggingface.js'
2
- import { fetchUncensoredGgufCatalog, type UncensoredCatalogEntry } from './uncensoredCatalog.js'
3
- import { chooseInstalledHfModelForRepo } from './modelPickerHfFlow.js'
4
- import { loadHfPickerModels } from './modelPickerData.js'
5
- import type { LoadedModelPickerData as LoadedData, ModelPickerState as State } from './modelPickerTypes.js'
6
-
7
- export async function openLocalCatalog(
8
- data: LoadedData,
9
- setState: (s: State) => void,
10
- ): Promise<void> {
11
- setState({ kind: 'localCatalogLoading', data })
12
- try {
13
- const installedModels = await loadLocalHfModels()
14
- const catalog = await fetchUncensoredGgufCatalog({
15
- machineSpec: data.machineSpec,
16
- installedModels,
17
- })
18
- setState({
19
- kind: 'localCatalog',
20
- data: { ...data, hfModels: await loadHfPickerModels() },
21
- catalog,
22
- })
23
- } catch (err: unknown) {
24
- setState({ kind: 'localCatalogError', data, message: (err as Error).message })
25
- }
26
- }
27
-
28
- export async function reviewCatalogModel(
29
- state: Extract<State, { kind: 'localCatalog' }>,
30
- entry: UncensoredCatalogEntry,
31
- setState: (s: State) => void,
32
- ): Promise<void> {
33
- const files = ggufFiles(entry.repo)
34
- const installed = chooseInstalledHfModelForRepo(
35
- await loadLocalHfModels(),
36
- entry.repo,
37
- files,
38
- entry.file.filename,
39
- state.data.machineSpec,
40
- )
41
- if (installed) {
42
- setState({
43
- kind: 'hfDone',
44
- data: { ...state.data, hfModels: await loadHfPickerModels() },
45
- model: installed,
46
- alreadyInstalled: true,
47
- })
48
- return
49
- }
50
- try {
51
- const plan = await createHfDownloadPlan(entry.repo.repoId, entry.file.filename)
52
- setState({ kind: 'hfReview', data: state.data, plan })
53
- } catch (err: unknown) {
54
- setState({ kind: 'hfError', data: state.data, message: (err as Error).message, input: entry.repo.repoId })
55
- }
56
- }
@@ -1,166 +0,0 @@
1
- import type React from 'react'
2
- import { OpenAIOAuthService } from '../auth/openaiOAuth/index.js'
3
- import { hasOpenAIOAuthCredentials, rmOpenAIOAuthCredentials } from '../auth/openaiOAuth/credentials.js'
4
- import { openExternalUrl } from '../utils/openExternal.js'
5
- import { hasKey, rmKey, setKey } from '../storage/secrets.js'
6
- import type { EthagentConfig, ProviderId } from '../storage/config.js'
7
- import {
8
- clearModelCatalogCache,
9
- discoverProviderModels,
10
- isOpenAIOAuthAllowedModel,
11
- OPENAI_OAUTH_DEFAULT_MODEL,
12
- } from './catalog.js'
13
- import type { CloudCredentialKind, CloudProviderId } from './modelPickerOptions.js'
14
- import type {
15
- LoadedModelPickerData as LoadedData,
16
- ModelPickerSelection,
17
- ModelPickerState as State,
18
- } from './modelPickerTypes.js'
19
- import { configForProvider, pickFallbackSelection } from './modelPickerViewHelpers.js'
20
-
21
- export async function submitKey(
22
- state: Extract<State, { kind: 'keyEntry' }>,
23
- value: string,
24
- currentConfig: EthagentConfig,
25
- setState: (s: State) => void,
26
- ): Promise<void> {
27
- const trimmed = value.trim()
28
- if (!trimmed) {
29
- setState({ ...state, error: 'key cannot be empty' })
30
- return
31
- }
32
- setState({ ...state, submitting: true, error: undefined })
33
- try {
34
- await setKey(state.provider, trimmed)
35
- const data = await refreshProviderKeyState(state.data, currentConfig, state.provider)
36
- setState({ kind: 'list', data })
37
- } catch (err: unknown) {
38
- setState({ ...state, submitting: false, error: (err as Error).message })
39
- }
40
- }
41
-
42
- export async function startOpenAIOAuthFlow(
43
- data: LoadedData,
44
- currentConfig: EthagentConfig,
45
- setState: (s: State) => void,
46
- serviceRef: React.MutableRefObject<OpenAIOAuthService | null>,
47
- onPick: (sel: ModelPickerSelection) => void,
48
- ): Promise<void> {
49
- serviceRef.current?.cleanup()
50
- const service = new OpenAIOAuthService()
51
- serviceRef.current = service
52
- setState({ kind: 'oauthLogin', data, phase: 'waiting' })
53
- try {
54
- const result = await service.start(authUrl => {
55
- openExternalUrl(authUrl)
56
- setState({ kind: 'oauthLogin', data, phase: 'waiting', url: authUrl })
57
- })
58
- if (serviceRef.current !== service) return
59
- setState({ kind: 'oauthLogin', data, phase: 'exchanging' })
60
- if (result.kind === 'apikey') {
61
- if (typeof result.apiKey !== 'string' || result.apiKey.length === 0) {
62
- throw new Error(`OAuth result was apikey kind but apiKey is ${typeof result.apiKey}; refusing to store.`)
63
- }
64
- try {
65
- await setKey('openai', result.apiKey)
66
- } catch (err) {
67
- throw new Error(`Storing the OpenAI API key failed: ${err instanceof Error ? err.message : String(err)}`)
68
- }
69
- }
70
- let refreshed: LoadedData
71
- try {
72
- refreshed = await refreshProviderKeyState(data, currentConfig, 'openai')
73
- } catch (err) {
74
- throw new Error(`Refreshing the OpenAI provider state failed: ${err instanceof Error ? err.message : String(err)}`)
75
- }
76
- if (serviceRef.current !== service) return
77
- serviceRef.current = null
78
- if (result.kind === 'oauth-only' && !isOpenAIOAuthAllowedModel(currentConfig.model)) {
79
- onPick({ kind: 'cloud', provider: 'openai', model: OPENAI_OAUTH_DEFAULT_MODEL, keyJustSet: true })
80
- return
81
- }
82
- setState({ kind: 'list', data: refreshed })
83
- } catch (err: unknown) {
84
- if (serviceRef.current !== service) return
85
- serviceRef.current = null
86
- const message = err instanceof Error ? err.message : String(err)
87
- if (message === 'OpenAI sign-in was cancelled.') {
88
- setState({ kind: 'list', data })
89
- return
90
- }
91
- setState({ kind: 'oauthLogin', data, phase: 'error', message })
92
- }
93
- }
94
-
95
- export async function deleteKey(
96
- state: Extract<State, { kind: 'keyManage' }>,
97
- currentConfig: EthagentConfig,
98
- setState: (s: State) => void,
99
- onPick: (sel: ModelPickerSelection) => void,
100
- currentProvider: ProviderId,
101
- ): Promise<void> {
102
- setState({ ...state, submitting: true, error: undefined })
103
- try {
104
- await rmKey(state.provider)
105
- if (state.provider === 'openai') await rmOpenAIOAuthCredentials()
106
- const data = await refreshProviderKeyState(state.data, currentConfig, state.provider)
107
- if (currentProvider === state.provider) {
108
- const fallback = pickFallbackSelection(data, state.provider)
109
- if (fallback) {
110
- onPick(fallback)
111
- return
112
- }
113
- }
114
- setState({ kind: 'list', data })
115
- } catch (err: unknown) {
116
- setState({ ...state, submitting: false, error: (err as Error).message })
117
- }
118
- }
119
-
120
- export async function signOutOAuth(
121
- state: Extract<State, { kind: 'oauthManage' }>,
122
- currentConfig: EthagentConfig,
123
- setState: (s: State) => void,
124
- onPick: (sel: ModelPickerSelection) => void,
125
- currentProvider: ProviderId,
126
- ): Promise<void> {
127
- setState({ ...state, submitting: true, error: undefined })
128
- try {
129
- await rmKey('openai')
130
- await rmOpenAIOAuthCredentials()
131
- const data = await refreshProviderKeyState(state.data, currentConfig, 'openai')
132
- if (currentProvider === 'openai') {
133
- const fallback = pickFallbackSelection(data, 'openai')
134
- if (fallback) {
135
- onPick(fallback)
136
- return
137
- }
138
- }
139
- setState({ kind: 'list', data })
140
- } catch (err: unknown) {
141
- setState({ ...state, submitting: false, error: (err as Error).message })
142
- }
143
- }
144
-
145
- async function refreshProviderKeyState(
146
- data: LoadedData,
147
- currentConfig: EthagentConfig,
148
- provider: CloudProviderId,
149
- ): Promise<LoadedData> {
150
- clearModelCatalogCache()
151
- const apiKeySet = await hasKey(provider)
152
- const oauthSet = provider === 'openai' ? await hasOpenAIOAuthCredentials() : false
153
- const keySet = apiKeySet || oauthSet
154
- const cloudKeys = { ...data.cloudKeys, [provider]: keySet }
155
- const cloudCredentialKinds: Partial<Record<ProviderId, CloudCredentialKind>> = { ...(data.cloudCredentialKinds ?? {}) }
156
- if (oauthSet) cloudCredentialKinds[provider] = 'oauth'
157
- else if (apiKeySet) cloudCredentialKinds[provider] = 'apikey'
158
- else delete cloudCredentialKinds[provider]
159
- const cloudCatalogs = { ...data.cloudCatalogs }
160
- if (keySet) {
161
- cloudCatalogs[provider] = await discoverProviderModels(configForProvider(currentConfig, provider))
162
- } else {
163
- delete cloudCatalogs[provider]
164
- }
165
- return { ...data, cloudKeys, cloudCatalogs, cloudCredentialKinds }
166
- }
@@ -1,41 +0,0 @@
1
- import { detectLlamaCpp } from './llamacpp.js'
2
- import { backfillMmprojForModels, loadLocalHfModels } from './huggingface.js'
3
- import type { LoadedModelPickerData } from './modelPickerTypes.js'
4
- import type { ModelPickerOptionsData } from './modelPickerOptions.js'
5
-
6
- export async function loadHfPickerModels(): Promise<ModelPickerOptionsData['hfModels']> {
7
- const installed = await loadLocalHfModels()
8
- const backfilled = await backfillMmprojForModels(installed)
9
- return backfilled.map(model => ({
10
- id: model.id,
11
- displayName: model.displayName,
12
- sizeBytes: model.sizeBytes,
13
- quantization: model.quantization,
14
- risk: model.risk,
15
- task: model.task,
16
- status: model.status,
17
- mmprojPath: model.mmprojPath,
18
- mmprojAvailable: model.mmprojAvailable,
19
- mmprojSizeBytes: model.mmprojSizeBytes,
20
- }))
21
- }
22
-
23
- export async function probeLlamaCpp(): Promise<ModelPickerOptionsData['llamaCpp']> {
24
- try {
25
- const status = await detectLlamaCpp()
26
- return {
27
- binaryPresent: status.binaryPresent,
28
- serverUp: status.serverUp,
29
- }
30
- } catch (err: unknown) {
31
- return { binaryPresent: false, serverUp: false, error: (err as Error).message }
32
- }
33
- }
34
-
35
- export async function refreshLocalModelData(data: LoadedModelPickerData): Promise<LoadedModelPickerData> {
36
- const hfModels = await loadHfPickerModels()
37
- return {
38
- ...data,
39
- hfModels,
40
- }
41
- }
@@ -1,132 +0,0 @@
1
- import React from 'react'
2
- import { Spinner } from '../ui/Spinner.js'
3
- import { theme } from '../ui/theme.js'
4
- import { contextWindowInfo } from '../runtime/compaction.js'
5
- import type { ProviderId } from '../storage/config.js'
6
- import type { HfCredibility, HfRisk } from './huggingface.js'
7
- import type { GgufMachineFit } from './modelRecommendation.js'
8
- import type { CloudProviderId, ModelPickerContextFit } from './modelPickerOptions.js'
9
-
10
- export function contextFitSubtitle(contextFit: ModelPickerContextFit): string {
11
- const threshold = contextFit.thresholdPercent ?? 90
12
- return `pending prompt needs ~${formatTokens(contextFit.usedTokens)} tokens; choose a model under ${threshold}% or use /compact.`
13
- }
14
-
15
- export function contextFitLabel(
16
- provider: ProviderId,
17
- model: string,
18
- baseLabel: string,
19
- contextFit?: ModelPickerContextFit | null,
20
- ): string {
21
- if (!contextFit) return baseLabel
22
- const info = contextWindowInfo(provider, model)
23
- const percent = info.tokens > 0 ? Math.round((contextFit.usedTokens / info.tokens) * 100) : 0
24
- return `${baseLabel} ${formatContextWindow(info.tokens)} ctx ${percent}%`
25
- }
26
-
27
- function formatTokens(count: number): string {
28
- if (count < 1000) return String(count)
29
- if (count < 10_000) return `${(count / 1000).toFixed(1)}k`
30
- return `${Math.round(count / 1000)}k`
31
- }
32
-
33
- function formatContextWindow(tokens: number): string {
34
- if (tokens >= 1_000_000) {
35
- const millions = tokens / 1_000_000
36
- return Number.isInteger(millions) ? `${millions}m` : `${millions.toFixed(1)}m`
37
- }
38
- if (tokens >= 1000) return `${Math.round(tokens / 1000)}k`
39
- return String(tokens)
40
- }
41
-
42
- export function formatBytes(bytes: number): string {
43
- if (bytes <= 0) return 'size unknown'
44
- const gb = bytes / 1e9
45
- if (gb >= 1) return `${gb.toFixed(1)} GB`
46
- return `${Math.round(bytes / 1e6)} MB`
47
- }
48
-
49
- export function modelMetadataSubtext(size: string, indicators: string[]): string | undefined {
50
- return [size, ...indicators].filter(Boolean).join(' · ') || undefined
51
- }
52
-
53
- export function riskColor(risk: string): string {
54
- if (risk === 'high') return theme.accentError
55
- if (risk === 'medium') return theme.dim
56
- return theme.accentPeriwinkle
57
- }
58
-
59
- export function fitColor(fit: GgufMachineFit): string {
60
- if (fit === 'too-large') return theme.accentError
61
- if (fit === 'tight') return theme.accentPeriwinkle
62
- return theme.dim
63
- }
64
-
65
- export function fitLabel(fit: GgufMachineFit, recommended: boolean): string {
66
- if (recommended && fit !== 'too-large') return 'Recommended for this machine'
67
- if (recommended) return 'Best match found; may be too large'
68
- return fileFitHint(fit)
69
- }
70
-
71
- function fileFitHint(fit: GgufMachineFit): string {
72
- switch (fit) {
73
- case 'fits': return 'Fits this machine'
74
- case 'tight': return 'May be slow or tight on memory'
75
- case 'too-large': return 'Likely too large for this machine'
76
- case 'unknown': return 'machine fit unknown'
77
- }
78
- }
79
-
80
- export function formatSignals(downloads: number | undefined, likes: number | undefined): string {
81
- const d = downloads == null ? 'downloads unknown' : `${downloads} downloads`
82
- const l = likes == null ? 'likes unknown' : `${likes} likes`
83
- return `${d}, ${l}`
84
- }
85
-
86
- export function friendlyFileName(filename: string): string {
87
- return filename.split('/').pop() ?? filename
88
- }
89
-
90
- export function safetyLabel(risk: HfRisk): string {
91
- if (risk === 'low') return 'reviewed'
92
- if (risk === 'medium') return 'needs review'
93
- return 'blocked'
94
- }
95
-
96
- export function credibilityLabel(credibility: HfCredibility): string {
97
- if (credibility === 'established') return 'established'
98
- if (credibility === 'normal') return 'some signals'
99
- return 'limited signals'
100
- }
101
-
102
- export function friendlyReasons(reasons: string[]): string[] {
103
- return reasons.map(reason => {
104
- if (reason.includes('compatible local model file')) return 'compatible local model file'
105
- if (reason.includes('selected file is not compatible')) return 'file is not compatible with local chat'
106
- if (reason.includes('revision is mutable')) return 'model link may point to changing files'
107
- if (reason.includes('license is missing')) return 'license is missing'
108
- if (reason.includes('limited public usage signals')) return 'source has limited public usage'
109
- if (reason.includes('pickle/bin')) return 'repo also contains risky model file formats'
110
- return reason
111
- })
112
- }
113
-
114
- export function providerKeyPlaceholder(provider: ProviderId): string {
115
- if (provider === 'openai') return 'sk-...'
116
- if (provider === 'anthropic') return 'sk-ant-...'
117
- if (provider === 'gemini') return 'AIza...'
118
- return ''
119
- }
120
-
121
- export function runnerPathPlaceholder(): string {
122
- if (process.platform === 'win32') return 'C:\\path\\to\\llama-server.exe'
123
- return '/path/to/llama-server'
124
- }
125
-
126
- export function isCloudProvider(value: string | undefined): value is CloudProviderId {
127
- return value === 'openai' || value === 'anthropic' || value === 'gemini'
128
- }
129
-
130
- export const ElapsedSpinner: React.FC<{ startedAt: number; label: string }> = ({ startedAt, label }) => {
131
- return <Spinner label={label} startedAt={startedAt} />
132
- }
@@ -1,192 +0,0 @@
1
- import React from 'react'
2
- import {
3
- addMmprojToInstalledModel,
4
- createHfDownloadPlan,
5
- downloadHfModel,
6
- fetchHuggingFaceRepoInfo,
7
- findLocalHfModel,
8
- ggufFiles,
9
- loadLocalHfModels,
10
- modelFromPlan,
11
- parseHuggingFaceRef,
12
- type HuggingFaceRepoInfo,
13
- type HuggingFaceSibling,
14
- type LocalHfModel,
15
- } from './huggingface.js'
16
- import { stopLlamaCppServer } from './llamacpp.js'
17
- import { orderGgufFilesForSpec, recommendGgufFile } from './modelRecommendation.js'
18
- import type { SpecSnapshot } from './runtimeDetection.js'
19
- import type { ModelPickerSelection, ModelPickerState as State } from './modelPickerTypes.js'
20
- import { loadHfPickerModels } from './modelPickerData.js'
21
- import { startAndPickHfModel } from './modelPickerLocalRunnerFlow.js'
22
- export async function findInstalledHfModelForInput(input: string): Promise<LocalHfModel | null> {
23
- const ref = parseHuggingFaceRef(input)
24
- const installed = await loadLocalHfModels()
25
- return installed.find(model =>
26
- model.status === 'ready'
27
- && model.repoId === ref.repoId
28
- && (!ref.filename || model.filename === ref.filename)
29
- ) ?? null
30
- }
31
-
32
- export function chooseInstalledHfModelForRepo(
33
- installed: LocalHfModel[],
34
- repo: HuggingFaceRepoInfo,
35
- files: HuggingFaceSibling[],
36
- requestedFilename: string | undefined,
37
- spec: SpecSnapshot | undefined,
38
- ): LocalHfModel | null {
39
- const compatibleFiles = new Set(files.map(file => file.filename))
40
- const candidates = installed.filter(model =>
41
- model.status === 'ready'
42
- && model.repoId === repo.repoId
43
- && compatibleFiles.has(model.filename)
44
- && (!requestedFilename || model.filename === requestedFilename)
45
- )
46
- if (requestedFilename || candidates.length <= 1) return candidates[0] ?? null
47
-
48
- const orderedFiles = spec
49
- ? orderGgufFilesForSpec(repo, files, spec).map(item => item.file.filename)
50
- : files.map(file => file.filename)
51
- for (const filename of orderedFiles) {
52
- const match = candidates.find(model => model.filename === filename)
53
- if (match) return match
54
- }
55
- return candidates[0] ?? null
56
- }
57
-
58
- export async function inspectHfInput(
59
- state: Extract<State, { kind: 'hfInput' }>,
60
- value: string,
61
- setState: (s: State) => void,
62
- ): Promise<void> {
63
- const input = value.trim()
64
- if (!input) {
65
- setState({ ...state, error: 'paste a model link or repo id' })
66
- return
67
- }
68
- setState({ kind: 'hfLoading', data: state.data, input })
69
- try {
70
- const ref = parseHuggingFaceRef(input)
71
- const repo = await fetchHuggingFaceRepoInfo(ref)
72
- const files = ggufFiles(repo)
73
- if (files.length === 0) {
74
- setState({
75
- kind: 'hfInput',
76
- data: state.data,
77
- error: 'no compatible local model files found; paste a different model link',
78
- })
79
- return
80
- }
81
- const installed = chooseInstalledHfModelForRepo(
82
- await loadLocalHfModels(),
83
- repo,
84
- files,
85
- ref.filename,
86
- state.data.machineSpec,
87
- )
88
- if (installed) {
89
- setState({
90
- kind: 'hfDone',
91
- data: { ...state.data, hfModels: await loadHfPickerModels() },
92
- model: installed,
93
- alreadyInstalled: true,
94
- })
95
- return
96
- }
97
- const recommendedFilename = state.data.machineSpec
98
- ? recommendGgufFile(repo, files, state.data.machineSpec)?.file.filename
99
- : files[0]?.filename
100
- if (ref.filename || files.length === 1) {
101
- const plan = await createHfDownloadPlan(input, ref.filename ?? recommendedFilename)
102
- setState({ kind: 'hfReview', data: state.data, plan })
103
- return
104
- }
105
- setState({ kind: 'hfFilePick', data: state.data, input, repo, files })
106
- } catch (err: unknown) {
107
- setState({ kind: 'hfInput', data: state.data, error: (err as Error).message })
108
- }
109
- }
110
-
111
- export async function reviewHfFile(
112
- state: Extract<State, { kind: 'hfFilePick' }>,
113
- filename: string,
114
- setState: (s: State) => void,
115
- ): Promise<void> {
116
- setState({ kind: 'hfLoading', data: state.data, input: state.input })
117
- try {
118
- const installed = chooseInstalledHfModelForRepo(
119
- await loadLocalHfModels(),
120
- state.repo,
121
- state.files,
122
- filename,
123
- state.data.machineSpec,
124
- )
125
- if (installed) {
126
- setState({
127
- kind: 'hfDone',
128
- data: { ...state.data, hfModels: await loadHfPickerModels() },
129
- model: installed,
130
- alreadyInstalled: true,
131
- })
132
- return
133
- }
134
- const plan = await createHfDownloadPlan(state.input, filename)
135
- setState({ kind: 'hfReview', data: state.data, plan })
136
- } catch (err: unknown) {
137
- setState({ kind: 'hfError', data: state.data, message: (err as Error).message, input: state.input })
138
- }
139
- }
140
-
141
- export async function startHfDownload(
142
- state: Extract<State, { kind: 'hfReview' }>,
143
- setState: (s: State) => void,
144
- abortRef: React.MutableRefObject<AbortController | null>,
145
- onPick: (sel: ModelPickerSelection) => void,
146
- ): Promise<void> {
147
- const controller = new AbortController()
148
- abortRef.current = controller
149
- setState({ kind: 'hfDownloading', data: state.data, plan: state.plan, progress: { status: 'starting', completed: 0, total: state.plan.sizeBytes } })
150
- try {
151
- for await (const progress of downloadHfModel(state.plan, controller.signal)) {
152
- if (controller.signal.aborted) return
153
- setState({ kind: 'hfDownloading', data: state.data, plan: state.plan, progress })
154
- }
155
- const model = await findLocalHfModel(`${state.plan.repoId}#${state.plan.filename}`)
156
- ?? modelFromPlan(state.plan, undefined, 'ready')
157
- const data = {
158
- ...state.data,
159
- hfModels: await loadHfPickerModels(),
160
- }
161
- await startAndPickHfModel(model, { kind: 'hfDone', data, model }, setState, onPick)
162
- } catch (err: unknown) {
163
- if (controller.signal.aborted) return
164
- setState({ kind: 'hfError', data: state.data, message: (err as Error).message, input: state.plan.repoId })
165
- } finally {
166
- abortRef.current = null
167
- }
168
- }
169
-
170
- export async function downloadMmprojAndContinue(
171
- state: Extract<State, { kind: 'mmprojOffer' }>,
172
- setState: (s: State) => void,
173
- onPick: (sel: ModelPickerSelection) => void,
174
- ): Promise<void> {
175
- setState({ kind: 'mmprojDownloading', data: state.data, model: state.model, progress: { status: 'starting' } })
176
- try {
177
- for await (const progress of addMmprojToInstalledModel(state.model.id)) {
178
- setState({ kind: 'mmprojDownloading', data: state.data, model: state.model, progress })
179
- }
180
- } catch (err: unknown) {
181
- setState({ kind: 'mmprojError', data: state.data, model: state.model, message: (err as Error).message })
182
- return
183
- }
184
- const updated = await findLocalHfModel(state.model.id)
185
- if (!updated || !updated.mmprojPath) {
186
- setState({ kind: 'mmprojError', data: state.data, model: state.model, message: 'projector downloaded but path was not persisted' })
187
- return
188
- }
189
- await stopLlamaCppServer().catch(() => null)
190
- const data = { ...state.data, hfModels: await loadHfPickerModels() }
191
- await startAndPickHfModel(updated, { kind: 'mmprojOffer', data, model: updated }, setState, onPick)
192
- }