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,95 @@
1
+ import React, { useState } from 'react'
2
+ import { Box, Text } from 'ink'
3
+ import type { ContextUsage } from '../runtime/compaction.js'
4
+ import { useAppInput } from '../app/input/AppInputProvider.js'
5
+ import { theme } from '../ui/theme.js'
6
+
7
+ export type ContextLimitAction = 'compact' | 'switchModel' | 'send' | 'cancel'
8
+
9
+ type ContextLimitViewProps = {
10
+ usage: ContextUsage
11
+ promptPreview: string
12
+ onSelect: (action: ContextLimitAction) => void | Promise<void>
13
+ onCancel: () => void
14
+ }
15
+
16
+ export const CONTEXT_LIMIT_OPTIONS: Array<{ action: ContextLimitAction; label: string; detail: string }> = [
17
+ {
18
+ action: 'compact',
19
+ label: 'Summarize and move to new conversation',
20
+ detail: 'Summarize this transcript into a new conversation, then send the pending message.',
21
+ },
22
+ {
23
+ action: 'switchModel',
24
+ label: 'Switch to larger-context model',
25
+ detail: 'Pick a model that can fit this conversation, then send the pending message.',
26
+ },
27
+ {
28
+ action: 'send',
29
+ label: 'Ignore warning and send',
30
+ detail: 'May hit rate/context limits faster or degrade local/cloud model behavior.',
31
+ },
32
+ {
33
+ action: 'cancel',
34
+ label: 'Cancel',
35
+ detail: 'Return to the prompt without sending the pending message.',
36
+ },
37
+ ]
38
+
39
+ export const ContextLimitView: React.FC<ContextLimitViewProps> = ({
40
+ usage,
41
+ promptPreview,
42
+ onSelect,
43
+ onCancel,
44
+ }) => {
45
+ const [selected, setSelected] = useState(0)
46
+
47
+ useAppInput((_input, key) => {
48
+ if (key.escape) {
49
+ onCancel()
50
+ return
51
+ }
52
+ if (key.upArrow) {
53
+ setSelected(i => Math.max(0, i - 1))
54
+ return
55
+ }
56
+ if (key.downArrow) {
57
+ setSelected(i => Math.min(CONTEXT_LIMIT_OPTIONS.length - 1, i + 1))
58
+ return
59
+ }
60
+ if (key.return) {
61
+ const picked = CONTEXT_LIMIT_OPTIONS[selected]
62
+ if (picked) void onSelect(picked.action)
63
+ }
64
+ })
65
+
66
+ return (
67
+ <Box flexDirection="column" borderStyle="round" borderColor={theme.accentPeach} paddingX={1}>
68
+ <Text color={theme.accentPeach} bold>context limit</Text>
69
+ <Text color={theme.dim}>
70
+ {`Context ${usage.percent}% · ~${formatTokens(usage.usedTokens)} / ${formatTokens(usage.windowTokens)} tokens (${usage.source}).`}
71
+ </Text>
72
+ {usage.percent >= 100 ? (
73
+ <Text color={theme.accentPeach}>
74
+ This transcript is over the selected model's estimated window. You can still send, but summarizing first is safer.
75
+ </Text>
76
+ ) : null}
77
+ <Text color={theme.textSubtle}>{`Pending: ${promptPreview || '(empty)'}`}</Text>
78
+ <Box flexDirection="column" marginTop={1}>
79
+ {CONTEXT_LIMIT_OPTIONS.map((option, index) => (
80
+ <Text key={option.action} color={index === selected ? theme.accentPrimary : theme.text}>
81
+ {index === selected ? '> ' : ' '}
82
+ {option.label}
83
+ <Text color={theme.dim}>{` · ${option.detail}`}</Text>
84
+ </Text>
85
+ ))}
86
+ </Box>
87
+ </Box>
88
+ )
89
+ }
90
+
91
+ function formatTokens(count: number): string {
92
+ if (count < 1000) return String(count)
93
+ if (count < 10_000) return `${(count / 1000).toFixed(1)}k`
94
+ return `${Math.round(count / 1000)}k`
95
+ }
@@ -0,0 +1,48 @@
1
+ import React from 'react'
2
+ import { Box, Text } from 'ink'
3
+ import { Surface } from '../ui/Surface.js'
4
+ import { Select } from '../ui/Select.js'
5
+ import { theme } from '../ui/theme.js'
6
+
7
+ export type ContinuityEditReviewState = {
8
+ file: 'SOUL.md' | 'MEMORY.md'
9
+ filePath: string
10
+ summary: string
11
+ }
12
+
13
+ export type ContinuityEditReviewAction = 'open' | 'save-publish' | 'later'
14
+
15
+ export const ContinuityEditReviewView: React.FC<{
16
+ review: ContinuityEditReviewState
17
+ onSelect: (action: ContinuityEditReviewAction) => void | Promise<void>
18
+ onCancel: () => void
19
+ }> = ({ review, onSelect, onCancel }) => (
20
+ <Surface
21
+ title="Private Continuity Updated"
22
+ subtitle="Review the file, then publish an encrypted snapshot."
23
+ footer="enter select · esc later"
24
+ >
25
+ <Box flexDirection="column">
26
+ <Text color={theme.accentMint}>{review.summary}</Text>
27
+ <Box marginTop={1} flexDirection="column">
28
+ <Text color={theme.textSubtle}>review file</Text>
29
+ <Text color={theme.text}>{review.filePath}</Text>
30
+ </Box>
31
+ <Box marginTop={1} flexDirection="column">
32
+ <Text color={theme.textSubtle}>saved locally</Text>
33
+ <Text color={theme.dim}>Previous version saved in identity history. /rewind does not restore identity continuity.</Text>
34
+ </Box>
35
+ </Box>
36
+ <Box marginTop={1}>
37
+ <Select<ContinuityEditReviewAction>
38
+ options={[
39
+ { value: 'open', label: `open ${review.file}`, hint: 'review the edited private file now' },
40
+ { value: 'save-publish', label: 'publish snapshot now', hint: 'go directly to wallet approval' },
41
+ { value: 'later', label: 'later', hint: 'keep the local draft unpublished' },
42
+ ]}
43
+ onSubmit={onSelect}
44
+ onCancel={onCancel}
45
+ />
46
+ </Box>
47
+ </Surface>
48
+ )
@@ -0,0 +1,47 @@
1
+ import React from 'react'
2
+ import { Box } from 'ink'
3
+ import { TranscriptView } from './TranscriptView.js'
4
+ import type { MessageRow } from './MessageList.js'
5
+
6
+ type ConversationStackProps = {
7
+ header: React.ReactNode
8
+ rows: MessageRow[]
9
+ transcriptActive?: boolean
10
+ bottomVariant?: 'prompt' | 'overlay'
11
+ bottom: React.ReactNode
12
+ status?: React.ReactNode
13
+ sessionKey: number
14
+ onVisibleReasoningIdsChange?: (ids: string[]) => void
15
+ }
16
+
17
+ export const ConversationStack: React.FC<ConversationStackProps> = ({
18
+ header,
19
+ rows,
20
+ transcriptActive = true,
21
+ bottomVariant = 'prompt',
22
+ bottom,
23
+ status,
24
+ sessionKey,
25
+ onVisibleReasoningIdsChange,
26
+ }) => {
27
+ return (
28
+ <Box flexDirection="column" padding={1}>
29
+ {header}
30
+ <TranscriptView
31
+ key={`transcript-${sessionKey}`}
32
+ rows={rows}
33
+ active={transcriptActive}
34
+ bottomVariant={bottomVariant}
35
+ onVisibleReasoningIdsChange={onVisibleReasoningIdsChange}
36
+ />
37
+ <Box marginTop={1} width="100%">
38
+ {bottom}
39
+ </Box>
40
+ {status ? (
41
+ <Box marginTop={1}>
42
+ {status}
43
+ </Box>
44
+ ) : null}
45
+ </Box>
46
+ )
47
+ }
@@ -0,0 +1,52 @@
1
+ import React from 'react'
2
+ import { Surface } from '../ui/Surface.js'
3
+ import { Select } from '../ui/Select.js'
4
+ import { parseSegments, type Segment } from '../utils/markdownSegments.js'
5
+ import { copyToClipboard, type CopyResult } from '../utils/clipboard.js'
6
+
7
+ type CopyPickerProps = {
8
+ turnText: string
9
+ turnLabel: string
10
+ onDone: (result: CopyResult, label: string) => void
11
+ onCancel: () => void
12
+ }
13
+
14
+ type Choice = { index: number; segment: Segment | null; label: string }
15
+
16
+ export const CopyPicker: React.FC<CopyPickerProps> = ({ turnText, turnLabel, onDone, onCancel }) => {
17
+ const segments = parseSegments(turnText)
18
+ const choices: Choice[] = [
19
+ { index: -1, segment: null, label: `all (${turnText.length} chars)` },
20
+ ...segments.map((segment, i) => ({ index: i, segment, label: segment.preview })),
21
+ ]
22
+
23
+ const options = choices.map(c => ({
24
+ value: c.index,
25
+ label: c.label,
26
+ hint: c.segment ? (c.segment.kind === 'code' ? 'code block' : undefined) : 'full reply',
27
+ }))
28
+
29
+ const handleSubmit = (index: number) => {
30
+ const chosen = choices.find(c => c.index === index)
31
+ const payload = chosen?.segment ? chosen.segment.content : turnText
32
+ const label = chosen?.label ?? 'copy'
33
+ void copyToClipboard(payload).then(result => onDone(result, label))
34
+ }
35
+
36
+ return (
37
+ <Surface
38
+ title={`Copy From ${turnLabel}`}
39
+ subtitle="Choose the full reply or an extracted segment."
40
+ footer="enter copies · esc closes"
41
+ >
42
+ <Select<number>
43
+ options={options}
44
+ initialIndex={0}
45
+ onSubmit={handleSubmit}
46
+ onCancel={onCancel}
47
+ />
48
+ </Surface>
49
+ )
50
+ }
51
+
52
+