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,290 +0,0 @@
1
- import { getKey } from '../storage/secrets.js'
2
- import type { Message, MessageContentBlock, Provider, ProviderCompleteOptions, StreamEvent } from './contracts.js'
3
- import { ProviderError } from './contracts.js'
4
- import { providerErrorFromResponse } from './errors.js'
5
- import { fetchWithRetryStreamEvents } from './retry.js'
6
- import { iterSseEvents } from './sse.js'
7
- import { hasImageBlocks, ImageLoadError, loadImageBlock } from '../utils/images.js'
8
-
9
- export type AnthropicToolDefinition = {
10
- name: string
11
- description: string
12
- input_schema: {
13
- type: 'object'
14
- properties?: Record<string, unknown>
15
- required?: string[]
16
- }
17
- }
18
-
19
- type AnthropicStreamMessage = {
20
- type?: string
21
- message?: {
22
- usage?: {
23
- input_tokens?: number
24
- output_tokens?: number
25
- }
26
- }
27
- usage?: {
28
- input_tokens?: number
29
- output_tokens?: number
30
- }
31
- delta?: {
32
- type?: string
33
- text?: string
34
- thinking?: string
35
- stop_reason?: string
36
- partial_json?: string
37
- }
38
- content_block?: {
39
- type?: string
40
- id?: string
41
- name?: string
42
- input?: Record<string, unknown>
43
- }
44
- index?: number
45
- error?: {
46
- type?: string
47
- message?: string
48
- }
49
- }
50
-
51
- const ANTHROPIC_VERSION = '2023-06-01'
52
- const READ_TIMEOUT_MS = 45_000
53
- const DEFAULT_MAX_TOKENS = 4096
54
-
55
- export class AnthropicProvider implements Provider {
56
- readonly id = 'anthropic' as const
57
- readonly model: string
58
- readonly supportsTools: boolean
59
- private readonly tools: AnthropicToolDefinition[]
60
-
61
- constructor(opts: { model: string; tools?: AnthropicToolDefinition[] }) {
62
- this.model = opts.model
63
- this.tools = opts.tools ?? []
64
- this.supportsTools = this.tools.length > 0
65
- }
66
-
67
- async *complete(
68
- messages: Message[],
69
- signal: AbortSignal,
70
- options: ProviderCompleteOptions = {},
71
- ): AsyncIterable<StreamEvent> {
72
- const apiKey = await getKey('anthropic')
73
- if (!apiKey) {
74
- const error = new ProviderError('missing API key for anthropic (/doctor to verify)')
75
- yield { type: 'error', message: error.message }
76
- return
77
- }
78
-
79
- if (hasImageBlocks(messages) && !supportsAnthropicImages(this.model)) {
80
- yield { type: 'error', message: `image input is not enabled for ${this.model}` }
81
- return
82
- }
83
-
84
- let split: { system?: string; conversation: Awaited<ReturnType<typeof splitMessages>>['conversation'] }
85
- try {
86
- split = await splitMessages(messages)
87
- } catch (err: unknown) {
88
- if (err instanceof ImageLoadError) {
89
- yield { type: 'error', message: err.message }
90
- return
91
- }
92
- throw err
93
- }
94
- const { system, conversation } = split
95
-
96
- let response: Response
97
- try {
98
- response = yield* fetchWithRetryStreamEvents('https://api.anthropic.com/v1/messages', {
99
- method: 'POST',
100
- headers: {
101
- 'content-type': 'application/json',
102
- accept: 'text/event-stream',
103
- 'anthropic-version': ANTHROPIC_VERSION,
104
- 'x-api-key': apiKey,
105
- },
106
- body: JSON.stringify({
107
- model: this.model,
108
- max_tokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,
109
- stream: true,
110
- system,
111
- messages: conversation,
112
- tools: this.tools.length > 0 ? this.tools : undefined,
113
- }),
114
- }, { signal, rateLimitResetProvider: 'anthropic' })
115
- } catch (err: unknown) {
116
- if (signal.aborted) return
117
- yield { type: 'error', message: (err as Error).message || 'network error' }
118
- return
119
- }
120
-
121
- if (!response.ok) {
122
- const error = await providerErrorFromResponse(this.id, response)
123
- yield { type: 'error', message: error.message }
124
- return
125
- }
126
- if (!response.body) {
127
- yield { type: 'error', message: 'empty response body' }
128
- return
129
- }
130
-
131
- let inputTokens: number | undefined
132
- let outputTokens: number | undefined
133
- let stopReason: 'end_turn' | 'tool_use' | 'max_tokens' | 'stop_sequence' | 'unknown' = 'unknown'
134
- const toolBuffers = new Map<number, { id: string; name: string; json: string }>()
135
-
136
- try {
137
- for await (const frame of iterSseEvents(response.body, signal, READ_TIMEOUT_MS)) {
138
- const eventType = frame.event
139
- let parsed: AnthropicStreamMessage
140
- try {
141
- parsed = JSON.parse(frame.data) as AnthropicStreamMessage
142
- } catch {
143
- continue
144
- }
145
-
146
- const type = parsed.type ?? eventType ?? ''
147
- if (type === 'message_start') {
148
- inputTokens = parsed.message?.usage?.input_tokens ?? inputTokens
149
- outputTokens = parsed.message?.usage?.output_tokens ?? outputTokens
150
- continue
151
- }
152
- if (type === 'content_block_start' && parsed.content_block?.type === 'tool_use') {
153
- const id = parsed.content_block.id ?? `tool-${parsed.index ?? 0}`
154
- const name = parsed.content_block.name ?? 'unknown'
155
- const json = parsed.content_block.input ? JSON.stringify(parsed.content_block.input) : ''
156
- toolBuffers.set(parsed.index ?? 0, { id, name, json })
157
- yield { type: 'tool_use_start', id, name }
158
- continue
159
- }
160
- if (type === 'content_block_delta') {
161
- if (parsed.delta?.type === 'text_delta' && parsed.delta.text) {
162
- yield { type: 'text', delta: parsed.delta.text }
163
- } else if (parsed.delta?.type === 'thinking_delta' && parsed.delta.thinking) {
164
- yield { type: 'thinking', delta: parsed.delta.thinking }
165
- } else if (parsed.delta?.type === 'input_json_delta' && typeof parsed.delta.partial_json === 'string') {
166
- const buffer = toolBuffers.get(parsed.index ?? 0)
167
- if (buffer) {
168
- buffer.json += parsed.delta.partial_json
169
- yield { type: 'tool_use_delta', id: buffer.id, delta: parsed.delta.partial_json }
170
- }
171
- }
172
- continue
173
- }
174
- if (type === 'content_block_stop') {
175
- const buffer = toolBuffers.get(parsed.index ?? 0)
176
- if (buffer) {
177
- let input: Record<string, unknown> = {}
178
- try {
179
- input = buffer.json.trim() ? JSON.parse(buffer.json) as Record<string, unknown> : {}
180
- } catch {
181
- input = {}
182
- }
183
- yield { type: 'tool_use_stop', id: buffer.id, name: buffer.name, input }
184
- toolBuffers.delete(parsed.index ?? 0)
185
- }
186
- continue
187
- }
188
- if (type === 'message_delta') {
189
- inputTokens = parsed.usage?.input_tokens ?? inputTokens
190
- outputTokens = parsed.usage?.output_tokens ?? outputTokens
191
- stopReason = normalizeStopReason(parsed.delta?.stop_reason)
192
- continue
193
- }
194
- if (type === 'error') {
195
- const message = parsed.error?.message || 'anthropic stream error'
196
- const transient = parsed.error?.type === 'overloaded_error' || parsed.error?.type === 'rate_limit_error'
197
- throw new ProviderError(message, { transient })
198
- }
199
- if (type === 'message_stop') {
200
- break
201
- }
202
- }
203
- } catch (err: unknown) {
204
- if (signal.aborted) return
205
- yield { type: 'error', message: (err as Error).message || 'stream error' }
206
- return
207
- }
208
-
209
- if (signal.aborted) return
210
- yield { type: 'done', inputTokens, outputTokens, stopReason }
211
- }
212
- }
213
-
214
- async function splitMessages(messages: Message[]): Promise<{
215
- system?: string
216
- conversation: Array<{
217
- role: 'user' | 'assistant'
218
- content: Array<
219
- | { type: 'text'; text: string }
220
- | { type: 'image'; source: { type: 'base64'; media_type: string; data: string } }
221
- | { type: 'tool_use'; id: string; name: string; input: Record<string, unknown> }
222
- | { type: 'tool_result'; tool_use_id: string; content: string; is_error?: boolean }
223
- >
224
- }>
225
- }> {
226
- const systemParts: string[] = []
227
- const conversation: Array<{
228
- role: 'user' | 'assistant'
229
- content: Array<
230
- | { type: 'text'; text: string }
231
- | { type: 'image'; source: { type: 'base64'; media_type: string; data: string } }
232
- | { type: 'tool_use'; id: string; name: string; input: Record<string, unknown> }
233
- | { type: 'tool_result'; tool_use_id: string; content: string; is_error?: boolean }
234
- >
235
- }> = []
236
-
237
- for (const message of messages) {
238
- const blocks = normalizeBlocks(message.content)
239
- if (blocks.length === 0) continue
240
- if (message.role === 'system') {
241
- const systemText = blocks.filter(block => block.type === 'text').map(block => block.text).join('\n\n').trim()
242
- if (systemText) systemParts.push(systemText)
243
- continue
244
- }
245
- conversation.push({
246
- role: message.role,
247
- content: await Promise.all(blocks.map(async block => {
248
- if (block.type === 'text') return { type: 'text', text: block.text }
249
- if (block.type === 'image') {
250
- const loaded = await loadImageBlock(block)
251
- if (!loaded.dataBase64 || !loaded.mimeType) throw new Error(`could not load image: ${block.path}`)
252
- return { type: 'image', source: { type: 'base64', media_type: loaded.mimeType, data: loaded.dataBase64 } }
253
- }
254
- if (block.type === 'tool_use') return { type: 'tool_use', id: block.id, name: block.name, input: block.input }
255
- return { type: 'tool_result', tool_use_id: block.toolUseId, content: block.content, is_error: block.isError }
256
- })),
257
- })
258
- }
259
-
260
- return {
261
- system: systemParts.length > 0 ? systemParts.join('\n\n') : undefined,
262
- conversation,
263
- }
264
- }
265
-
266
- function normalizeBlocks(content: Message['content']): MessageContentBlock[] {
267
- if (typeof content === 'string') {
268
- const text = content.trim()
269
- return text ? [{ type: 'text', text }] : []
270
- }
271
- return content.filter(block => {
272
- if (block.type === 'text') return block.text.trim().length > 0
273
- return true
274
- })
275
- }
276
-
277
- export function supportsAnthropicImages(model: string): boolean {
278
- const normalized = model.toLowerCase()
279
- return normalized.includes('claude-3')
280
- || normalized.includes('claude-sonnet-4')
281
- || normalized.includes('claude-opus-4')
282
- || normalized.includes('claude-haiku-4')
283
- }
284
-
285
- function normalizeStopReason(value?: string): 'end_turn' | 'tool_use' | 'max_tokens' | 'stop_sequence' | 'unknown' {
286
- if (value === 'end_turn' || value === 'tool_use' || value === 'max_tokens' || value === 'stop_sequence') {
287
- return value
288
- }
289
- return 'unknown'
290
- }
@@ -1,71 +0,0 @@
1
- import type { ProviderId } from '../storage/config.js'
2
- import type { RetryEvent } from '../utils/withRetry.js'
3
-
4
- export type Role = 'system' | 'user' | 'assistant'
5
-
6
- export type TextBlock = {
7
- type: 'text'
8
- text: string
9
- }
10
-
11
- export type ImageBlock = {
12
- type: 'image'
13
- path: string
14
- mimeType?: string
15
- url?: string
16
- dataBase64?: string
17
- }
18
-
19
- export type ToolUseBlock = {
20
- type: 'tool_use'
21
- id: string
22
- name: string
23
- input: Record<string, unknown>
24
- }
25
-
26
- export type ToolResultBlock = {
27
- type: 'tool_result'
28
- toolUseId: string
29
- content: string
30
- isError?: boolean
31
- }
32
-
33
- export type MessageContentBlock = TextBlock | ImageBlock | ToolUseBlock | ToolResultBlock
34
-
35
- export type Message = {
36
- role: Role
37
- content: string | MessageContentBlock[]
38
- }
39
-
40
- export type ProviderRetryStreamEvent = { type: 'retry' } & RetryEvent
41
-
42
- export type StreamEvent =
43
- | { type: 'text'; delta: string }
44
- | { type: 'thinking'; delta: string }
45
- | { type: 'thinking_end' }
46
- | ProviderRetryStreamEvent
47
- | { type: 'tool_use_start'; id: string; name: string }
48
- | { type: 'tool_use_delta'; id: string; delta: string }
49
- | { type: 'tool_use_stop'; id: string; name: string; input: Record<string, unknown> }
50
- | { type: 'done'; inputTokens?: number; outputTokens?: number; stopReason?: 'end_turn' | 'tool_use' | 'max_tokens' | 'stop_sequence' | 'unknown' }
51
- | { type: 'error'; message: string }
52
-
53
- export type ProviderCompleteOptions = {
54
- maxTokens?: number
55
- }
56
-
57
- export interface Provider {
58
- readonly id: ProviderId
59
- readonly model: string
60
- readonly supportsTools: boolean
61
- complete(messages: Message[], signal: AbortSignal, options?: ProviderCompleteOptions): AsyncIterable<StreamEvent>
62
- }
63
-
64
- export class ProviderError extends Error {
65
- readonly transient: boolean
66
- constructor(message: string, options: { transient?: boolean } = {}) {
67
- super(message)
68
- this.name = 'ProviderError'
69
- this.transient = options.transient ?? false
70
- }
71
- }
@@ -1,80 +0,0 @@
1
- import type { ProviderId } from '../storage/config.js'
2
- import { ProviderError } from './contracts.js'
3
- import { formatGeminiRateLimitMessage } from './gemini.js'
4
- import { providerDisplayName } from '../models/providerDisplay.js'
5
-
6
- type ErrorBody =
7
- | string
8
- | {
9
- error?: {
10
- message?: string
11
- type?: string
12
- }
13
- message?: string
14
- detail?: string
15
- }
16
-
17
- export async function providerErrorFromResponse(
18
- provider: ProviderId,
19
- response: Response,
20
- ): Promise<ProviderError> {
21
- if (provider === 'gemini' && response.status === 429) {
22
- const short = await formatGeminiRateLimitMessage(response.clone())
23
- if (short) return new ProviderError(short, { transient: true })
24
- }
25
-
26
- const detail = await readErrorDetail(response)
27
-
28
- if (
29
- provider === 'gemini'
30
- && response.status === 400
31
- && /API[_ ]?key( not valid| not found|_invalid)|invalid api key/i.test(detail)
32
- ) {
33
- return new ProviderError(
34
- 'Gemini: API key rejected — verify your key at https://aistudio.google.com/app/apikey, then run /key gemini to set it again',
35
- )
36
- }
37
-
38
- if (provider !== 'llamacpp') {
39
- const name = providerDisplayName(provider)
40
- if (response.status === 401 || response.status === 403) {
41
- return new ProviderError(`auth failed: check your ${name} key (/doctor to verify)`)
42
- }
43
- if (response.status === 429) {
44
- return new ProviderError(detail || `${name} rate limit exceeded`, { transient: true })
45
- }
46
- if (response.status >= 500) {
47
- return new ProviderError(detail || `${name} server error (${response.status})`, { transient: true })
48
- }
49
- }
50
-
51
- return new ProviderError(
52
- detail ? `HTTP ${response.status}: ${detail}` : `HTTP ${response.status}`,
53
- { transient: response.status === 429 || response.status >= 500 },
54
- )
55
- }
56
-
57
- async function readErrorDetail(response: Response): Promise<string> {
58
- let text = ''
59
- try {
60
- text = (await response.text()).trim()
61
- } catch {
62
- return ''
63
- }
64
- if (!text) return ''
65
-
66
- try {
67
- const parsed = JSON.parse(text) as ErrorBody
68
- const nestedMessage =
69
- typeof parsed === 'object' && parsed !== null
70
- ? parsed.error?.message ?? parsed.message ?? parsed.detail ?? ''
71
- : ''
72
- return normalizeDetail(nestedMessage || text)
73
- } catch {
74
- return normalizeDetail(text)
75
- }
76
- }
77
-
78
- function normalizeDetail(detail: string): string {
79
- return detail.replace(/\s+/g, ' ').trim().slice(0, 400)
80
- }