ethagent 0.2.0 → 1.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 (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +114 -32
  3. package/bin/ethagent.js +11 -2
  4. package/package.json +30 -8
  5. package/src/app/FirstRun.tsx +412 -0
  6. package/src/app/hooks/useCancelRequest.ts +22 -0
  7. package/src/app/hooks/useDoublePress.ts +46 -0
  8. package/src/app/hooks/useExitOnCtrlC.ts +36 -0
  9. package/src/app/input/AppInputProvider.tsx +116 -0
  10. package/src/app/input/appInputParser.ts +279 -0
  11. package/src/app/keybindings/KeybindingProvider.tsx +134 -0
  12. package/src/app/keybindings/resolver.ts +42 -0
  13. package/src/app/keybindings/types.ts +26 -0
  14. package/src/chat/ChatBottomPane.tsx +280 -0
  15. package/src/chat/ChatInput.tsx +722 -0
  16. package/src/chat/ChatScreen.tsx +1575 -0
  17. package/src/chat/ContextLimitView.tsx +95 -0
  18. package/src/chat/ContinuityEditReviewView.tsx +48 -0
  19. package/src/chat/ConversationStack.tsx +47 -0
  20. package/src/chat/CopyPicker.tsx +52 -0
  21. package/src/chat/MessageList.tsx +609 -0
  22. package/src/chat/PermissionPrompt.tsx +153 -0
  23. package/src/chat/PermissionsView.tsx +159 -0
  24. package/src/chat/PlanApprovalView.tsx +91 -0
  25. package/src/chat/ResumeView.tsx +267 -0
  26. package/src/chat/RewindView.tsx +386 -0
  27. package/src/chat/SessionStatus.tsx +51 -0
  28. package/src/chat/TranscriptView.tsx +202 -0
  29. package/src/chat/chatInputState.ts +247 -0
  30. package/src/chat/chatPaste.ts +49 -0
  31. package/src/chat/chatScreenUtils.ts +187 -0
  32. package/src/chat/chatSessionState.ts +142 -0
  33. package/src/chat/chatTurnOrchestrator.ts +701 -0
  34. package/src/chat/commands.ts +673 -0
  35. package/src/chat/textCursor.ts +202 -0
  36. package/src/chat/toolResultDisplay.ts +8 -0
  37. package/src/chat/transcriptViewport.ts +247 -0
  38. package/src/cli/ResetConfirmView.tsx +61 -0
  39. package/src/cli/main.tsx +177 -0
  40. package/src/cli/preview.tsx +19 -0
  41. package/src/cli/reset.ts +106 -0
  42. package/src/identity/continuity/editor.ts +149 -0
  43. package/src/identity/continuity/envelope.ts +345 -0
  44. package/src/identity/continuity/history.ts +153 -0
  45. package/src/identity/continuity/privateEdit.ts +334 -0
  46. package/src/identity/continuity/publicSkills.ts +173 -0
  47. package/src/identity/continuity/snapshots.ts +183 -0
  48. package/src/identity/continuity/storage.ts +507 -0
  49. package/src/identity/crypto/backupEnvelope.ts +486 -0
  50. package/src/identity/crypto/eth.ts +137 -0
  51. package/src/identity/hub/IdentityHub.tsx +868 -0
  52. package/src/identity/hub/identityHubEffects.ts +1146 -0
  53. package/src/identity/hub/identityHubModel.ts +291 -0
  54. package/src/identity/hub/identityHubReducer.ts +212 -0
  55. package/src/identity/hub/screens/BusyScreen.tsx +26 -0
  56. package/src/identity/hub/screens/ContinuityDashboardScreen.tsx +144 -0
  57. package/src/identity/hub/screens/CreateFlow.tsx +206 -0
  58. package/src/identity/hub/screens/DetailsScreen.tsx +64 -0
  59. package/src/identity/hub/screens/EditProfileFlow.tsx +145 -0
  60. package/src/identity/hub/screens/ErrorScreen.tsx +35 -0
  61. package/src/identity/hub/screens/IdentitySummary.tsx +70 -0
  62. package/src/identity/hub/screens/MenuScreen.tsx +117 -0
  63. package/src/identity/hub/screens/NetworkScreen.tsx +41 -0
  64. package/src/identity/hub/screens/RebackupStorageScreen.tsx +50 -0
  65. package/src/identity/hub/screens/RecoveryConfirmScreen.tsx +85 -0
  66. package/src/identity/hub/screens/RestoreFlow.tsx +206 -0
  67. package/src/identity/hub/screens/StorageCredentialScreen.tsx +128 -0
  68. package/src/identity/hub/screens/WalletApprovalScreen.tsx +43 -0
  69. package/src/identity/profile/imagePicker.ts +180 -0
  70. package/src/identity/registry/erc8004.ts +1106 -0
  71. package/src/identity/registry/registryConfig.ts +69 -0
  72. package/src/identity/storage/ipfs.ts +212 -0
  73. package/src/identity/storage/pinataJwt.ts +53 -0
  74. package/src/identity/wallet/browserWallet.ts +393 -0
  75. package/src/identity/wallet/wallet-page/wallet.html +1082 -0
  76. package/src/mcp/approvals.ts +113 -0
  77. package/src/mcp/config.ts +235 -0
  78. package/src/mcp/manager.ts +541 -0
  79. package/src/mcp/names.ts +19 -0
  80. package/src/mcp/output.ts +96 -0
  81. package/src/models/ModelPicker.tsx +1446 -0
  82. package/src/models/catalog.ts +296 -0
  83. package/src/models/huggingface.ts +651 -0
  84. package/src/models/llamacpp.ts +810 -0
  85. package/src/models/llamacppPreflight.ts +150 -0
  86. package/src/models/modelDisplay.ts +105 -0
  87. package/src/models/modelPickerOptions.ts +421 -0
  88. package/src/models/modelRecommendation.ts +140 -0
  89. package/src/models/runtimeDetection.ts +81 -0
  90. package/src/models/uncensoredCatalog.ts +86 -0
  91. package/src/providers/anthropic.ts +259 -0
  92. package/src/providers/contracts.ts +62 -0
  93. package/src/providers/errors.ts +62 -0
  94. package/src/providers/gemini.ts +152 -0
  95. package/src/providers/openai-chat.ts +472 -0
  96. package/src/providers/registry.ts +42 -0
  97. package/src/providers/retry.ts +58 -0
  98. package/src/providers/sse.ts +93 -0
  99. package/src/runtime/compaction.ts +389 -0
  100. package/src/runtime/cwd.ts +43 -0
  101. package/src/runtime/sessionMode.ts +55 -0
  102. package/src/runtime/systemPrompt.ts +209 -0
  103. package/src/runtime/toolClaimGuards.ts +143 -0
  104. package/src/runtime/toolExecution.ts +304 -0
  105. package/src/runtime/toolIntent.ts +163 -0
  106. package/src/runtime/turn.ts +858 -0
  107. package/src/storage/atomicWrite.ts +68 -0
  108. package/src/storage/config.ts +189 -0
  109. package/src/storage/factoryReset.ts +130 -0
  110. package/src/storage/history.ts +58 -0
  111. package/src/storage/identity.ts +99 -0
  112. package/src/storage/permissions.ts +76 -0
  113. package/src/storage/rewind.ts +246 -0
  114. package/src/storage/secrets.ts +181 -0
  115. package/src/storage/sessionExport.ts +49 -0
  116. package/src/storage/sessions.ts +482 -0
  117. package/src/tools/bashSafety.ts +174 -0
  118. package/src/tools/bashTool.ts +140 -0
  119. package/src/tools/changeDirectoryTool.ts +213 -0
  120. package/src/tools/contracts.ts +179 -0
  121. package/src/tools/deleteFileTool.ts +111 -0
  122. package/src/tools/editTool.ts +160 -0
  123. package/src/tools/editUtils.ts +170 -0
  124. package/src/tools/listDirectoryTool.ts +55 -0
  125. package/src/tools/mcpResourceTools.ts +95 -0
  126. package/src/tools/permissionRules.ts +85 -0
  127. package/src/tools/privateContinuityEditTool.ts +178 -0
  128. package/src/tools/privateContinuityReadTool.ts +107 -0
  129. package/src/tools/readTool.ts +85 -0
  130. package/src/tools/registry.ts +67 -0
  131. package/src/tools/writeFileTool.ts +142 -0
  132. package/src/ui/BrandSplash.tsx +193 -0
  133. package/src/ui/ProgressBar.tsx +34 -0
  134. package/src/ui/Select.tsx +143 -0
  135. package/src/ui/Spinner.tsx +269 -0
  136. package/src/ui/Surface.tsx +47 -0
  137. package/src/ui/TextInput.tsx +97 -0
  138. package/src/ui/theme.ts +59 -0
  139. package/src/utils/clipboard.ts +216 -0
  140. package/src/utils/markdownSegments.ts +51 -0
  141. package/src/utils/messages.ts +35 -0
  142. package/src/utils/withRetry.ts +280 -0
  143. package/src/cli.tsx +0 -147
@@ -0,0 +1,280 @@
1
+ import React from 'react'
2
+ import type { EthagentConfig } from '../storage/config.js'
3
+ import type { PermissionDecision, PermissionRequest, SessionPermissionRule } from '../tools/contracts.js'
4
+ import { type ModelPickerSelection, ModelPicker } from '../models/ModelPicker.js'
5
+ import type { ModelPickerContextFit } from '../models/modelPickerOptions.js'
6
+ import { ResumeView } from './ResumeView.js'
7
+ import { RewindView } from './RewindView.js'
8
+ import { PermissionsView } from './PermissionsView.js'
9
+ import { CopyPicker } from './CopyPicker.js'
10
+ import { PermissionPrompt } from './PermissionPrompt.js'
11
+ import { PlanApprovalView, type PlanApprovalAction } from './PlanApprovalView.js'
12
+ import { ChatInput } from './ChatInput.js'
13
+ import { IdentityHub, type IdentityHubInitialAction, type IdentityHubResult } from '../identity/hub/IdentityHub.js'
14
+ import type { CopyResult } from '../utils/clipboard.js'
15
+ import { getSlashSuggestions } from './commands.js'
16
+ import { Box, Text } from 'ink'
17
+ import { theme } from '../ui/theme.js'
18
+ import { Spinner } from '../ui/Spinner.js'
19
+ import { ContextLimitView, type ContextLimitAction } from './ContextLimitView.js'
20
+ import type { ContextUsage } from '../runtime/compaction.js'
21
+ import {
22
+ ContinuityEditReviewView,
23
+ type ContinuityEditReviewAction,
24
+ type ContinuityEditReviewState,
25
+ } from './ContinuityEditReviewView.js'
26
+
27
+ export type Overlay = 'none' | 'modelPicker' | 'resume' | 'rewind' | 'copyPicker' | 'permission' | 'permissions' | 'planApproval' | 'identity' | 'contextLimit' | 'continuityEditReview'
28
+ export type CopyPickerState = { turnText: string; turnLabel: string } | null
29
+ export type IdentityOverlayState = {
30
+ initialAction: IdentityHubInitialAction | undefined
31
+ existing: { address: string } | null
32
+ }
33
+ export type ContextLimitState = {
34
+ usage: ContextUsage
35
+ prompt: string
36
+ } | null
37
+
38
+ export type BottomPaneActivity = {
39
+ label: string
40
+ hint?: string
41
+ } | null
42
+
43
+ type ChatBottomPaneProps = {
44
+ overlay: Overlay
45
+ config: EthagentConfig
46
+ sessionId: string
47
+ cwd: string
48
+ currentSessionId: string
49
+ copyPickerState: CopyPickerState
50
+ contextLimitState: ContextLimitState
51
+ continuityEditReview: ContinuityEditReviewState | null
52
+ modelPickerContextFit: ModelPickerContextFit | null
53
+ permissionRequest: PermissionRequest | null
54
+ history: string[]
55
+ busy: boolean
56
+ streaming: boolean
57
+ activity: BottomPaneActivity
58
+ placeholderHints: string[]
59
+ queuedInputs: string[]
60
+ slashSuggestions: ReturnType<typeof getSlashSuggestions>
61
+ planApprovalContextLabel: string
62
+ footerRight: React.ReactNode
63
+ handleModelPick: (sel: ModelPickerSelection) => void | Promise<void>
64
+ handleModelPickerCancel: () => void
65
+ handleResumePick: (id: string) => void | Promise<void>
66
+ handleResumeClearAll: () => void | Promise<void>
67
+ identityOverlay: IdentityOverlayState | null
68
+ handleIdentityResult: (result: IdentityHubResult) => void
69
+ handleRestoreConversation: (turnId: string) => void
70
+ handleCopyDone: (result: CopyResult, label: string) => void
71
+ handleCopyCancel: () => void
72
+ resolvePermission: (decision: PermissionDecision) => void
73
+ handlePlanApproval: (action: PlanApprovalAction) => void | Promise<void>
74
+ handlePlanApprovalCancel: () => void
75
+ handleContextLimitAction: (action: ContextLimitAction) => void | Promise<void>
76
+ handleContextLimitCancel: () => void
77
+ handleContinuityEditReviewAction: (action: ContinuityEditReviewAction) => void | Promise<void>
78
+ handleContinuityEditReviewCancel: () => void
79
+ onPermissionRulesChanged: (rules: SessionPermissionRule[]) => void
80
+ onConfigChange: (config: EthagentConfig) => void
81
+ handleSubmit: (value: string) => void | Promise<void>
82
+ setOverlay: React.Dispatch<React.SetStateAction<Overlay>>
83
+ pushNote: (text: string, kind?: 'info' | 'error' | 'dim') => void
84
+ }
85
+
86
+ export function ChatBottomPane({
87
+ overlay,
88
+ config,
89
+ sessionId,
90
+ cwd,
91
+ currentSessionId,
92
+ copyPickerState,
93
+ contextLimitState,
94
+ continuityEditReview,
95
+ modelPickerContextFit,
96
+ permissionRequest,
97
+ history,
98
+ busy,
99
+ streaming,
100
+ activity,
101
+ placeholderHints,
102
+ queuedInputs,
103
+ slashSuggestions,
104
+ planApprovalContextLabel,
105
+ footerRight,
106
+ handleModelPick,
107
+ handleModelPickerCancel,
108
+ handleResumePick,
109
+ handleResumeClearAll,
110
+ identityOverlay,
111
+ handleIdentityResult,
112
+ handleRestoreConversation,
113
+ handleCopyDone,
114
+ handleCopyCancel,
115
+ resolvePermission,
116
+ handlePlanApproval,
117
+ handlePlanApprovalCancel,
118
+ handleContextLimitAction,
119
+ handleContextLimitCancel,
120
+ handleContinuityEditReviewAction,
121
+ handleContinuityEditReviewCancel,
122
+ onPermissionRulesChanged,
123
+ onConfigChange,
124
+ handleSubmit,
125
+ setOverlay,
126
+ pushNote,
127
+ }: ChatBottomPaneProps): React.ReactNode {
128
+ if (overlay === 'modelPicker') {
129
+ return (
130
+ <ModelPicker
131
+ currentConfig={config}
132
+ currentProvider={config.provider}
133
+ currentModel={config.model}
134
+ contextFit={modelPickerContextFit}
135
+ onPick={handleModelPick}
136
+ onCancel={handleModelPickerCancel}
137
+ />
138
+ )
139
+ }
140
+
141
+ if (overlay === 'resume') {
142
+ return (
143
+ <ResumeView
144
+ currentSessionId={sessionId}
145
+ onResume={handleResumePick}
146
+ onClearAll={handleResumeClearAll}
147
+ onCancel={() => setOverlay('none')}
148
+ />
149
+ )
150
+ }
151
+
152
+ if (overlay === 'rewind') {
153
+ return (
154
+ <RewindView
155
+ cwd={cwd}
156
+ currentSessionId={currentSessionId}
157
+ onRestoreConversation={handleRestoreConversation}
158
+ onDone={(message, variant = 'info') => {
159
+ setOverlay('none')
160
+ pushNote(message, variant)
161
+ }}
162
+ onCancel={() => setOverlay('none')}
163
+ />
164
+ )
165
+ }
166
+
167
+ if (overlay === 'permissions') {
168
+ return (
169
+ <PermissionsView
170
+ cwd={cwd}
171
+ onRulesChanged={onPermissionRulesChanged}
172
+ onNotice={(message, variant = 'info') => {
173
+ pushNote(message, variant)
174
+ }}
175
+ onCancel={() => setOverlay('none')}
176
+ />
177
+ )
178
+ }
179
+
180
+ if (overlay === 'copyPicker' && copyPickerState) {
181
+ return (
182
+ <CopyPicker
183
+ turnText={copyPickerState.turnText}
184
+ turnLabel={copyPickerState.turnLabel}
185
+ onDone={handleCopyDone}
186
+ onCancel={handleCopyCancel}
187
+ />
188
+ )
189
+ }
190
+
191
+ if (overlay === 'permission' && permissionRequest) {
192
+ return (
193
+ <PermissionPrompt
194
+ request={permissionRequest}
195
+ onDecision={resolvePermission}
196
+ onCancel={() => resolvePermission('deny')}
197
+ />
198
+ )
199
+ }
200
+
201
+ if (overlay === 'identity' && identityOverlay) {
202
+ return (
203
+ <IdentityHub
204
+ mode="manage"
205
+ config={config}
206
+ cwd={cwd}
207
+ initialAction={identityOverlay.initialAction}
208
+ onComplete={handleIdentityResult}
209
+ onConfigChange={onConfigChange}
210
+ />
211
+ )
212
+ }
213
+
214
+ if (overlay === 'planApproval') {
215
+ return (
216
+ <PlanApprovalView
217
+ contextLabel={planApprovalContextLabel}
218
+ onSelect={handlePlanApproval}
219
+ onCancel={handlePlanApprovalCancel}
220
+ />
221
+ )
222
+ }
223
+
224
+ if (overlay === 'contextLimit' && contextLimitState) {
225
+ return (
226
+ <ContextLimitView
227
+ usage={contextLimitState.usage}
228
+ promptPreview={summarizePrompt(contextLimitState.prompt)}
229
+ onSelect={handleContextLimitAction}
230
+ onCancel={handleContextLimitCancel}
231
+ />
232
+ )
233
+ }
234
+
235
+ if (overlay === 'continuityEditReview' && continuityEditReview) {
236
+ return (
237
+ <ContinuityEditReviewView
238
+ review={continuityEditReview}
239
+ onSelect={handleContinuityEditReviewAction}
240
+ onCancel={handleContinuityEditReviewCancel}
241
+ />
242
+ )
243
+ }
244
+
245
+ return (
246
+ <Box flexDirection="column" width="100%">
247
+ {activity ? (
248
+ <Box marginLeft={2} marginBottom={1}>
249
+ <Spinner active label={activity.label} hint={activity.hint} />
250
+ </Box>
251
+ ) : streaming ? (
252
+ <Box marginLeft={2} marginBottom={1}>
253
+ <Spinner active hint="esc to cancel" />
254
+ </Box>
255
+ ) : null}
256
+ <ChatInput
257
+ onSubmit={handleSubmit}
258
+ history={history}
259
+ disabled={busy}
260
+ placeholderHints={placeholderHints}
261
+ queuedMessages={queuedInputs}
262
+ slashSuggestions={slashSuggestions}
263
+ footerRight={footerRight}
264
+ cwd={cwd}
265
+ />
266
+ <Box marginLeft={2} marginTop={0} flexDirection="column">
267
+ <Text>
268
+ <Text color={theme.dim}>workspace · </Text>
269
+ <Text color={theme.textSubtle}>{cwd}</Text>
270
+ </Text>
271
+ </Box>
272
+ </Box>
273
+ )
274
+ }
275
+
276
+ function summarizePrompt(text: string): string {
277
+ const normalized = text.replace(/\s+/g, ' ').trim()
278
+ if (normalized.length <= 100) return normalized
279
+ return `${normalized.slice(0, 97)}...`
280
+ }