ethagent 3.3.3 → 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 -259
  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,174 +0,0 @@
1
- import type { SelectOption } from '../ui/Select.js'
2
- import type { ProviderId } from '../storage/config.js'
3
- import { defaultModelFor, type EthagentConfig } from '../storage/config.js'
4
- import type { LlamaCppInstallResult } from './llamacpp.js'
5
- import { orderGgufFilesForSpec, type GgufMachineFit } from './modelRecommendation.js'
6
- import {
7
- localModelId,
8
- type HuggingFaceRepoInfo,
9
- type HuggingFaceSibling,
10
- } from './huggingface.js'
11
- import {
12
- MODEL_PICKER_CLOUD_PROVIDERS,
13
- orderModelsForContextFit,
14
- type CloudProviderId,
15
- type ModelPickerContextFit,
16
- } from './modelPickerOptions.js'
17
- import { formatModelDisplayName } from './modelDisplay.js'
18
- import { contextFitLabel, formatBytes, modelMetadataSubtext } from './modelPickerDisplay.js'
19
- import type { LoadedModelPickerData as LoadedData, ModelPickerSelection } from './modelPickerTypes.js'
20
- import type { ModelCatalogResult } from './catalog.js'
21
- import type { SpecSnapshot } from './runtimeDetection.js'
22
-
23
- export function buildCatalogOptions(
24
- provider: CloudProviderId,
25
- catalog: ModelCatalogResult | undefined,
26
- currentProvider: ProviderId,
27
- currentModel: string,
28
- contextFit?: ModelPickerContextFit | null,
29
- ): SelectOption<string>[] {
30
- if (!catalog || catalog.entries.length === 0) {
31
- return [{
32
- value: `hdr:catalog-empty:${provider}`,
33
- label: 'No Models Found',
34
- disabled: true,
35
- role: 'notice',
36
- prefix: 'note',
37
- }]
38
- }
39
- const sourceById = new Map(catalog.entries.map(entry => [entry.id, entry.source]))
40
- return orderModelsForContextFit(provider, catalog.entries.map(entry => entry.id), contextFit).map(id => {
41
- const active = currentProvider === provider && currentModel === id
42
- const suffix = sourceById.get(id) === 'fallback' ? ' fallback' : ''
43
- const displayName = formatModelDisplayName(provider, id, { maxLength: 64 })
44
- return {
45
- value: `full:${provider}:${id}`,
46
- label: contextFitLabel(provider, id, `${displayName}${active ? ' *' : ''}${suffix}`, contextFit),
47
- role: 'option',
48
- }
49
- })
50
- }
51
-
52
- export function parseCloudValue(value: string): { provider: CloudProviderId; model: string } | null {
53
- if (!value.startsWith('c:')) return null
54
- const rest = value.slice(2)
55
- const sep = rest.indexOf(':')
56
- if (sep === -1) return null
57
- const provider = rest.slice(0, sep)
58
- const model = rest.slice(sep + 1)
59
- if (!isCloudProvider(provider) || !model) return null
60
- return { provider, model }
61
- }
62
-
63
- export function localModelOptionIndex(
64
- options: SelectOption<string>[],
65
- currentProvider: ProviderId,
66
- currentModel: string,
67
- ): number {
68
- return options.findIndex(opt => {
69
- if (opt.disabled) return false
70
- if (opt.value.startsWith('hf:')) return opt.value.slice(3) === currentModel && currentProvider === 'llamacpp'
71
- if (opt.value.startsWith('uc:')) return opt.value.slice(3) === currentModel && currentProvider === 'llamacpp'
72
- return false
73
- })
74
- }
75
-
76
- export function localOrCloudOptionIndex(
77
- options: SelectOption<string>[],
78
- currentProvider: ProviderId,
79
- currentModel: string,
80
- ): number {
81
- return options.findIndex(opt => {
82
- if (opt.disabled) return false
83
- if (opt.value.startsWith('hf:')) return opt.value.slice(3) === currentModel && currentProvider === 'llamacpp'
84
- if (opt.value.startsWith('uc:')) return opt.value.slice(3) === currentModel && currentProvider === 'llamacpp'
85
- const cloud = parseCloudValue(opt.value)
86
- return cloud?.provider === currentProvider && cloud.model === currentModel
87
- })
88
- }
89
-
90
- export function parseFullCatalogValue(value: string): { provider: CloudProviderId; model: string } | null {
91
- if (!value.startsWith('full:')) return null
92
- const rest = value.slice(5)
93
- const sep = rest.indexOf(':')
94
- if (sep === -1) return null
95
- const provider = rest.slice(0, sep)
96
- const model = rest.slice(sep + 1)
97
- if (!isCloudProvider(provider) || !model) return null
98
- return { provider, model }
99
- }
100
-
101
- export function parseKeyValue(value: string): { action: 'set' | 'edit' | 'manage'; provider: CloudProviderId } | null {
102
- if (!value.startsWith('key:')) return null
103
- const parts = value.split(':')
104
- if (parts.length !== 3) return null
105
- const action = parts[1]
106
- const provider = parts[2]
107
- if (action !== 'set' && action !== 'edit' && action !== 'manage') return null
108
- if (!provider) return null
109
- if (!isCloudProvider(provider)) return null
110
- return { action, provider }
111
- }
112
-
113
- export function pickFallbackSelection(data: LoadedData, removed: ProviderId): ModelPickerSelection | null {
114
- for (const provider of MODEL_PICKER_CLOUD_PROVIDERS) {
115
- if (provider === removed) continue
116
- if (data.cloudKeys[provider] !== true) continue
117
- const catalogModel = data.cloudCatalogs[provider]?.entries[0]?.id
118
- const model = catalogModel ?? defaultModelFor(provider)
119
- return { kind: 'cloud', provider, model, keyJustSet: false }
120
- }
121
- if (data.hfModels.length > 0) {
122
- return { kind: 'llamacpp', model: data.hfModels[0]!.id }
123
- }
124
- return null
125
- }
126
-
127
- export function configForProvider(config: EthagentConfig, provider: CloudProviderId): EthagentConfig {
128
- return {
129
- ...config,
130
- provider,
131
- model: config.provider === provider ? config.model : defaultModelFor(provider),
132
- baseUrl: provider === 'openai' && config.provider === 'openai' ? config.baseUrl : undefined,
133
- }
134
- }
135
-
136
- export function buildHfFileOptions(
137
- repo: HuggingFaceRepoInfo,
138
- files: HuggingFaceSibling[],
139
- spec: SpecSnapshot | undefined,
140
- installedModelIds: string[] = [],
141
- ): SelectOption<string>[] {
142
- const ordered = spec
143
- ? orderGgufFilesForSpec(repo, files, spec)
144
- : files.map(file => ({ file, fit: 'unknown' as GgufMachineFit, score: 0, budgetBytes: 0 }))
145
- const recommended = spec ? ordered[0]?.file.filename : undefined
146
- const installed = new Set(installedModelIds)
147
- return ordered.map(item => {
148
- const size = item.file.sizeBytes ? formatBytes(item.file.sizeBytes) : ''
149
- const indicators = [
150
- item.file.filename === recommended ? 'Recommended' : '',
151
- installed.has(localModelId(repo.repoId, item.file.filename)) ? 'Installed' : '',
152
- ]
153
- return {
154
- value: item.file.filename,
155
- label: item.file.filename,
156
- subtext: modelMetadataSubtext(size, indicators),
157
- role: 'option' as const,
158
- }
159
- })
160
- }
161
-
162
- export function buildRunnerRecoveryOptions(
163
- _result: Extract<LlamaCppInstallResult, { ok: false }>,
164
- ): SelectOption<'stop-and-retry' | 'path' | 'back'>[] {
165
- return [
166
- { value: 'stop-and-retry', label: 'Stop and Retry', hint: 'Stop background runners and try the install again' },
167
- { value: 'path', label: 'Use Existing Runner Path' },
168
- { value: 'back', label: 'Back To Picker' },
169
- ]
170
- }
171
-
172
- function isCloudProvider(value: string): value is CloudProviderId {
173
- return MODEL_PICKER_CLOUD_PROVIDERS.includes(value as CloudProviderId)
174
- }
@@ -1,139 +0,0 @@
1
- import {
2
- fileFormat,
3
- quantizationFromFilename,
4
- type HuggingFaceRepoInfo,
5
- type HuggingFaceSibling,
6
- } from './huggingface.js'
7
- import type { SpecSnapshot } from './runtimeDetection.js'
8
-
9
- const GB = 1024 * 1024 * 1024
10
-
11
- export const FEATURED_HF_REPO = 'HauhauCS/Qwen3.5-9B-Uncensored-HauhauCS-Aggressive'
12
- export const FEATURED_HF_REPO_URL = `https://huggingface.co/${FEATURED_HF_REPO}`
13
-
14
- export type GgufMachineFit = 'fits' | 'tight' | 'too-large' | 'unknown'
15
-
16
- export type GgufFileRecommendation = {
17
- file: HuggingFaceSibling
18
- fit: GgufMachineFit
19
- score: number
20
- budgetBytes: number
21
- estimatedRequiredBytes?: number
22
- }
23
-
24
- export function recommendGgufFile(
25
- repo: HuggingFaceRepoInfo,
26
- files: HuggingFaceSibling[],
27
- spec: SpecSnapshot,
28
- ): GgufFileRecommendation | null {
29
- return orderGgufFilesForSpec(repo, files, spec)[0] ?? null
30
- }
31
-
32
- export function orderGgufFilesForSpec(
33
- repo: HuggingFaceRepoInfo,
34
- files: HuggingFaceSibling[],
35
- spec: SpecSnapshot,
36
- ): GgufFileRecommendation[] {
37
- return files
38
- .filter(file => fileFormat(file.filename) === 'gguf')
39
- .map(file => scoreGgufFile(repo, file, spec))
40
- .sort((a, b) =>
41
- b.score - a.score
42
- || fitRank(b.fit) - fitRank(a.fit)
43
- || (a.file.sizeBytes ?? Number.MAX_SAFE_INTEGER) - (b.file.sizeBytes ?? Number.MAX_SAFE_INTEGER)
44
- || a.file.filename.localeCompare(b.file.filename),
45
- )
46
- }
47
-
48
- export function estimateGgufMachineFit(sizeBytes: number | undefined, spec: SpecSnapshot): {
49
- fit: GgufMachineFit
50
- budgetBytes: number
51
- estimatedRequiredBytes?: number
52
- } {
53
- const budgetBytes = ggufBudgetBytes(spec)
54
- if (!sizeBytes || sizeBytes <= 0) return { fit: 'unknown', budgetBytes }
55
- const estimatedRequiredBytes = Math.ceil(sizeBytes * 1.25 + GB)
56
- if (estimatedRequiredBytes <= budgetBytes) return { fit: 'fits', budgetBytes, estimatedRequiredBytes }
57
- if (estimatedRequiredBytes <= budgetBytes * 1.15) return { fit: 'tight', budgetBytes, estimatedRequiredBytes }
58
- return { fit: 'too-large', budgetBytes, estimatedRequiredBytes }
59
- }
60
-
61
- function scoreGgufFile(
62
- repo: HuggingFaceRepoInfo,
63
- file: HuggingFaceSibling,
64
- spec: SpecSnapshot,
65
- ): GgufFileRecommendation {
66
- const fit = estimateGgufMachineFit(file.sizeBytes, spec)
67
- const lower = `${repo.repoId} ${file.filename} ${repo.tags.join(' ')}`.toLowerCase()
68
- const score =
69
- fitScore(fit.fit)
70
- + taskScore(lower)
71
- + quantizationScore(quantizationFromFilename(file.filename))
72
- + sizeScore(file.sizeBytes, fit.fit)
73
-
74
- return {
75
- file,
76
- fit: fit.fit,
77
- budgetBytes: fit.budgetBytes,
78
- estimatedRequiredBytes: fit.estimatedRequiredBytes,
79
- score,
80
- }
81
- }
82
-
83
- function ggufBudgetBytes(spec: SpecSnapshot): number {
84
- if (spec.isAppleSilicon) return Math.floor(spec.effectiveRamBytes * 0.7)
85
- const cpuBudget = Math.floor(spec.effectiveRamBytes * 0.55)
86
- if (spec.gpuVramBytes !== null && spec.gpuVramBytes >= 8 * GB) {
87
- return Math.max(cpuBudget, Math.floor(spec.gpuVramBytes * 0.85))
88
- }
89
- return cpuBudget
90
- }
91
-
92
- function fitScore(fit: GgufMachineFit): number {
93
- switch (fit) {
94
- case 'fits': return 1000
95
- case 'tight': return 600
96
- case 'unknown': return 250
97
- case 'too-large': return -1000
98
- }
99
- }
100
-
101
- function fitRank(fit: GgufMachineFit): number {
102
- switch (fit) {
103
- case 'fits': return 3
104
- case 'tight': return 2
105
- case 'unknown': return 1
106
- case 'too-large': return 0
107
- }
108
- }
109
-
110
- function taskScore(text: string): number {
111
- if (/(embed|embedding|rerank)/.test(text)) return -350
112
- if (/(vision|vlm|multimodal)/.test(text)) return -250
113
- if (/(instruct|chat|assistant)/.test(text)) return 250
114
- if (/(code|coder|coding)/.test(text)) return 120
115
- if (/(^|[-_\s])base($|[-_\s])/.test(text)) return -100
116
- return 0
117
- }
118
-
119
- function quantizationScore(quantization: string | undefined): number {
120
- if (!quantization) return 0
121
- if (quantization === 'Q8_0') return 210
122
- if (quantization === 'Q6_K') return 195
123
- if (quantization === 'Q5_K_M') return 185
124
- if (quantization === 'Q5_K_S') return 175
125
- if (quantization.startsWith('Q5')) return 165
126
- if (quantization === 'Q4_K_M') return 155
127
- if (quantization === 'Q4_K_S') return 140
128
- if (quantization.startsWith('Q4')) return 125
129
- if (quantization.startsWith('IQ4')) return 115
130
- if (quantization.startsWith('IQ3') || quantization.startsWith('Q3')) return 85
131
- if (quantization === 'F16' || quantization === 'BF16') return 150
132
- if (quantization === 'F32') return 90
133
- return 50
134
- }
135
-
136
- function sizeScore(sizeBytes: number | undefined, fit: GgufMachineFit): number {
137
- if (!sizeBytes || fit === 'too-large') return 0
138
- return Math.min(sizeBytes / GB, 20) * 4
139
- }
@@ -1,16 +0,0 @@
1
- import type { ProviderId } from '../storage/config.js'
2
-
3
- export type CloudProviderId = Exclude<ProviderId, 'llamacpp'>
4
-
5
- export function cloudProviderDisplayName(provider: CloudProviderId): string {
6
- switch (provider) {
7
- case 'openai': return 'OpenAI'
8
- case 'anthropic': return 'Anthropic'
9
- case 'gemini': return 'Gemini'
10
- }
11
- }
12
-
13
- export function providerDisplayName(provider: ProviderId): string {
14
- if (provider === 'llamacpp') return 'llama.cpp'
15
- return cloudProviderDisplayName(provider)
16
- }
@@ -1,81 +0,0 @@
1
- import os from 'node:os'
2
- import { spawn } from 'node:child_process'
3
-
4
- export type SpecSnapshot = {
5
- platform: NodeJS.Platform
6
- arch: string
7
- cpuCores: number
8
- totalRamBytes: number
9
- effectiveRamBytes: number
10
- isAppleSilicon: boolean
11
- gpuVramBytes: number | null
12
- }
13
-
14
- function runCommand(cmd: string, args: string[], timeoutMs = 2000): Promise<{ code: number; stdout: string; stderr: string } | null> {
15
- return new Promise(resolve => {
16
- let settled = false
17
- let child: ReturnType<typeof spawn>
18
- try {
19
- child = spawn(cmd, args, { windowsHide: true })
20
- } catch {
21
- resolve(null)
22
- return
23
- }
24
- let stdout = ''
25
- let stderr = ''
26
- const timer = setTimeout(() => {
27
- if (settled) return
28
- settled = true
29
- try { child.kill() } catch { void 0 }
30
- resolve(null)
31
- }, timeoutMs)
32
-
33
- child.stdout?.on('data', d => { stdout += d.toString() })
34
- child.stderr?.on('data', d => { stderr += d.toString() })
35
- child.on('error', () => {
36
- if (settled) return
37
- settled = true
38
- clearTimeout(timer)
39
- resolve(null)
40
- })
41
- child.on('close', code => {
42
- if (settled) return
43
- settled = true
44
- clearTimeout(timer)
45
- resolve({ code: code ?? -1, stdout, stderr })
46
- })
47
- })
48
- }
49
-
50
- async function detectNvidiaVram(): Promise<number | null> {
51
- const result = await runCommand('nvidia-smi', ['--query-gpu=memory.total', '--format=csv,noheader,nounits'])
52
- if (!result || result.code !== 0) return null
53
- const firstLine = result.stdout.split('\n').map(l => l.trim()).find(Boolean)
54
- if (!firstLine) return null
55
- const mib = Number.parseInt(firstLine, 10)
56
- if (!Number.isFinite(mib) || mib <= 0) return null
57
- return mib * 1024 * 1024
58
- }
59
-
60
- export async function detectSpec(): Promise<SpecSnapshot> {
61
- const platform = process.platform
62
- const arch = process.arch
63
- const cpuCores = os.cpus().length
64
- const totalRamBytes = os.totalmem()
65
- const isAppleSilicon = platform === 'darwin' && arch === 'arm64'
66
- const effectiveRamBytes = isAppleSilicon ? Math.floor(totalRamBytes * 0.75) : totalRamBytes
67
-
68
- const [gpuVramBytes] = await Promise.all([
69
- isAppleSilicon ? Promise.resolve(null) : detectNvidiaVram(),
70
- ])
71
-
72
- return {
73
- platform,
74
- arch,
75
- cpuCores,
76
- totalRamBytes,
77
- effectiveRamBytes,
78
- isAppleSilicon,
79
- gpuVramBytes,
80
- }
81
- }
@@ -1,86 +0,0 @@
1
- import {
2
- fetchHuggingFaceRepoInfo,
3
- ggufFiles,
4
- localModelId,
5
- type HuggingFaceRepoInfo,
6
- type HuggingFaceSibling,
7
- type LocalHfModel,
8
- } from './huggingface.js'
9
- import {
10
- FEATURED_HF_REPO,
11
- estimateGgufMachineFit,
12
- recommendGgufFile,
13
- type GgufMachineFit,
14
- } from './modelRecommendation.js'
15
- import type { SpecSnapshot } from './runtimeDetection.js'
16
-
17
- export type UncensoredCatalogEntry = {
18
- repo: HuggingFaceRepoInfo
19
- file: HuggingFaceSibling
20
- fit: GgufMachineFit
21
- recommended: boolean
22
- installed: boolean
23
- }
24
-
25
- type FetchImpl = typeof fetch
26
-
27
- const GB = 1024 * 1024 * 1024
28
- const FEATURED_FILE_ORDER = [
29
- { filename: 'Qwen3.5-9B-Uncensored-HauhauCS-Aggressive-BF16.gguf', fallbackSizeBytes: Math.round(17 * GB) },
30
- { filename: 'Qwen3.5-9B-Uncensored-HauhauCS-Aggressive-Q8_0.gguf', fallbackSizeBytes: Math.round(8.9 * GB) },
31
- { filename: 'Qwen3.5-9B-Uncensored-HauhauCS-Aggressive-Q6_K.gguf', fallbackSizeBytes: Math.round(6.9 * GB) },
32
- { filename: 'Qwen3.5-9B-Uncensored-HauhauCS-Aggressive-Q4_K_M.gguf', fallbackSizeBytes: Math.round(5.3 * GB) },
33
- { filename: 'mmproj-Qwen3.5-9B-Uncensored-HauhauCS-Aggressive-BF16.gguf', fallbackSizeBytes: Math.round(0.88 * GB) },
34
- ] as const
35
-
36
- export async function fetchUncensoredGgufCatalog(args: {
37
- machineSpec?: SpecSnapshot
38
- installedModels: LocalHfModel[]
39
- fetchImpl?: FetchImpl
40
- limit?: number
41
- }): Promise<UncensoredCatalogEntry[]> {
42
- const fetchImpl = args.fetchImpl ?? fetch
43
- const repos = await Promise.allSettled([
44
- fetchHuggingFaceRepoInfo({ repoId: FEATURED_HF_REPO }, fetchImpl),
45
- ])
46
- const installed = new Set(args.installedModels.filter(model => model.status === 'ready').map(model => model.id))
47
- const entries: UncensoredCatalogEntry[] = []
48
-
49
- for (const result of repos) {
50
- if (result.status !== 'fulfilled') continue
51
- const repo = result.value
52
- if (repo.repoId !== FEATURED_HF_REPO) continue
53
- const files = pickFeaturedFiles(repo)
54
- if (files.length === 0) continue
55
- const runnable = files.filter(file => !isVisionEncoder(file.filename))
56
- const recommendedFilename = args.machineSpec
57
- ? recommendGgufFile(repo, runnable, args.machineSpec)?.file.filename
58
- : undefined
59
- for (const file of files.slice(0, args.limit ?? FEATURED_FILE_ORDER.length)) {
60
- const recommended = recommendedFilename === file.filename
61
- entries.push({
62
- repo,
63
- file,
64
- fit: args.machineSpec ? estimateGgufMachineFit(file.sizeBytes, args.machineSpec).fit : 'unknown',
65
- recommended,
66
- installed: installed.has(localModelId(repo.repoId, file.filename)),
67
- })
68
- }
69
- }
70
-
71
- return entries
72
- }
73
-
74
- function pickFeaturedFiles(repo: HuggingFaceRepoInfo): HuggingFaceSibling[] {
75
- const byName = new Map(ggufFiles(repo).map(file => [file.filename, file] as const))
76
- return FEATURED_FILE_ORDER
77
- .map(spec => {
78
- const file = byName.get(spec.filename)
79
- if (file) return file
80
- return { filename: spec.filename, sizeBytes: spec.fallbackSizeBytes } satisfies HuggingFaceSibling
81
- })
82
- }
83
-
84
- function isVisionEncoder(filename: string): boolean {
85
- return filename.toLowerCase().startsWith('mmproj-')
86
- }