ethagent 1.1.2 → 2.0.1

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 (268) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +124 -32
  3. package/package.json +8 -3
  4. package/src/app/FirstRun.tsx +190 -146
  5. package/src/app/FirstRunTimeline.tsx +47 -0
  6. package/src/app/input/AppInputProvider.tsx +1 -1
  7. package/src/app/keybindings/KeybindingProvider.tsx +1 -1
  8. package/src/chat/ChatBottomPane.tsx +0 -1
  9. package/src/chat/ChatInput.tsx +6 -6
  10. package/src/chat/ChatScreen.tsx +35 -15
  11. package/src/chat/ContextLimitView.tsx +4 -4
  12. package/src/chat/ContinuityEditReviewView.tsx +10 -22
  13. package/src/chat/CopyPicker.tsx +0 -1
  14. package/src/chat/MessageList.tsx +62 -45
  15. package/src/chat/PermissionPrompt.tsx +13 -9
  16. package/src/chat/PlanApprovalView.tsx +3 -3
  17. package/src/chat/ResumeView.tsx +1 -4
  18. package/src/chat/RewindView.tsx +2 -2
  19. package/src/chat/chatInputState.ts +1 -1
  20. package/src/chat/chatScreenUtils.ts +22 -11
  21. package/src/chat/chatSessionState.ts +2 -2
  22. package/src/chat/chatTurnOrchestrator.ts +16 -81
  23. package/src/chat/commands.ts +1 -1
  24. package/src/chat/textCursor.ts +1 -1
  25. package/src/chat/transcriptViewport.ts +2 -7
  26. package/src/cli/ResetConfirmView.tsx +1 -1
  27. package/src/cli/main.tsx +9 -3
  28. package/src/cli/preview.tsx +0 -5
  29. package/src/cli/updateNotice.ts +4 -2
  30. package/src/identity/continuity/editor.ts +7 -107
  31. package/src/identity/continuity/envelope.ts +1048 -40
  32. package/src/identity/continuity/history.ts +4 -4
  33. package/src/identity/continuity/localBackup.ts +249 -0
  34. package/src/identity/continuity/privateEdit/apply.ts +170 -0
  35. package/src/identity/continuity/privateEdit/diff.ts +82 -0
  36. package/src/identity/continuity/privateEdit/files.ts +23 -0
  37. package/src/identity/continuity/privateEdit/types.ts +28 -0
  38. package/src/identity/continuity/privateEdit.ts +10 -298
  39. package/src/identity/continuity/publicSkills.ts +8 -9
  40. package/src/identity/continuity/snapshots.ts +17 -6
  41. package/src/identity/continuity/storage/defaults.ts +111 -0
  42. package/src/identity/continuity/storage/files.ts +72 -0
  43. package/src/identity/continuity/storage/markdown.ts +81 -0
  44. package/src/identity/continuity/storage/paths.ts +24 -0
  45. package/src/identity/continuity/storage/scaffold.ts +124 -0
  46. package/src/identity/continuity/storage/status.ts +86 -0
  47. package/src/identity/continuity/storage/types.ts +27 -0
  48. package/src/identity/continuity/storage.ts +32 -507
  49. package/src/identity/continuity/zipWriter.ts +95 -0
  50. package/src/identity/crypto/backupEnvelope.ts +14 -247
  51. package/src/identity/crypto/eth.ts +7 -7
  52. package/src/identity/ens/agentRecords.ts +96 -0
  53. package/src/identity/ens/ensAutomation/contracts.ts +38 -0
  54. package/src/identity/ens/ensAutomation/delete.ts +80 -0
  55. package/src/identity/ens/ensAutomation/names.ts +14 -0
  56. package/src/identity/ens/ensAutomation/operators.ts +29 -0
  57. package/src/identity/ens/ensAutomation/read.ts +114 -0
  58. package/src/identity/ens/ensAutomation/root.ts +63 -0
  59. package/src/identity/ens/ensAutomation/setup.ts +284 -0
  60. package/src/identity/ens/ensAutomation/transactions.ts +107 -0
  61. package/src/identity/ens/ensAutomation/types.ts +126 -0
  62. package/src/identity/ens/ensAutomation.ts +29 -0
  63. package/src/identity/ens/ensLookup/client.ts +43 -0
  64. package/src/identity/ens/ensLookup/constants.ts +26 -0
  65. package/src/identity/ens/ensLookup/discovery.ts +70 -0
  66. package/src/identity/ens/ensLookup/names.ts +34 -0
  67. package/src/identity/ens/ensLookup/records.ts +45 -0
  68. package/src/identity/ens/ensLookup/resolve.ts +75 -0
  69. package/src/identity/ens/ensLookup/tokenReference.ts +17 -0
  70. package/src/identity/ens/ensLookup/types.ts +38 -0
  71. package/src/identity/ens/ensLookup/validation.ts +72 -0
  72. package/src/identity/ens/ensLookup.ts +19 -0
  73. package/src/identity/ens/ensRegistration.ts +199 -0
  74. package/src/identity/ens/resolverDelegation.ts +48 -0
  75. package/src/identity/hub/IdentityHub.tsx +13 -817
  76. package/src/identity/hub/OperationalRoutes.tsx +370 -0
  77. package/src/identity/hub/Routes.tsx +361 -0
  78. package/src/identity/hub/advancedEnsValidation.ts +45 -0
  79. package/src/identity/hub/{screens → components}/DetailsScreen.tsx +14 -8
  80. package/src/identity/hub/{screens → components}/ErrorScreen.tsx +15 -5
  81. package/src/identity/hub/components/FlowTimeline.tsx +27 -0
  82. package/src/identity/hub/components/IdentitySummary.tsx +190 -0
  83. package/src/identity/hub/components/MenuScreen.tsx +237 -0
  84. package/src/identity/hub/{screens → components}/NetworkScreen.tsx +3 -3
  85. package/src/identity/hub/{screens/RebackupStorageScreen.tsx → components/PinataJwtInput.tsx} +21 -18
  86. package/src/identity/hub/components/UnlinkedIdentityScreen.tsx +76 -0
  87. package/src/identity/hub/{screens → components}/WalletApprovalScreen.tsx +9 -8
  88. package/src/identity/hub/components/menuFlagsFromReconciliation.ts +68 -0
  89. package/src/identity/hub/effects/create.ts +310 -0
  90. package/src/identity/hub/effects/ens/flows.ts +218 -0
  91. package/src/identity/hub/effects/ens/index.ts +11 -0
  92. package/src/identity/hub/effects/ens/transactions.ts +239 -0
  93. package/src/identity/hub/effects/index.ts +74 -0
  94. package/src/identity/hub/effects/profile/profileState.ts +173 -0
  95. package/src/identity/hub/effects/publicProfile/index.ts +5 -0
  96. package/src/identity/hub/effects/publicProfile/runPublicProfileSave.ts +646 -0
  97. package/src/identity/hub/effects/rebackup/index.ts +7 -0
  98. package/src/identity/hub/effects/rebackup/operatorVault.ts +378 -0
  99. package/src/identity/hub/effects/rebackup/runRebackup.ts +451 -0
  100. package/src/identity/hub/effects/receipts.ts +46 -0
  101. package/src/identity/hub/effects/restore/apply.ts +112 -0
  102. package/src/identity/hub/effects/restore/auth.ts +159 -0
  103. package/src/identity/hub/effects/restore/discover.ts +86 -0
  104. package/src/identity/hub/effects/restore/envelopes.ts +21 -0
  105. package/src/identity/hub/effects/restore/fetch.ts +25 -0
  106. package/src/identity/hub/effects/restore/index.ts +22 -0
  107. package/src/identity/hub/effects/restore/recovery.ts +135 -0
  108. package/src/identity/hub/effects/restore/resolve.ts +102 -0
  109. package/src/identity/hub/effects/restore/restoreEffects.ts +22 -0
  110. package/src/identity/hub/effects/restore/shared.ts +91 -0
  111. package/src/identity/hub/effects/restoreAdmin.ts +93 -0
  112. package/src/identity/hub/effects/shared/profilePrep.ts +139 -0
  113. package/src/identity/hub/effects/shared/snapshot.ts +336 -0
  114. package/src/identity/hub/effects/shared/sync.ts +190 -0
  115. package/src/identity/hub/effects/token-transfer/index.ts +6 -0
  116. package/src/identity/hub/effects/token-transfer/progress.ts +59 -0
  117. package/src/identity/hub/effects/token-transfer/runTokenTransfer.ts +299 -0
  118. package/src/identity/hub/effects/types.ts +53 -0
  119. package/src/identity/hub/effects/vault/preflight.ts +50 -0
  120. package/src/identity/hub/flows/continuity/ContinuityDashboardScreen.tsx +170 -0
  121. package/src/identity/hub/flows/continuity/RebackupStorageScreen.tsx +28 -0
  122. package/src/identity/hub/{screens → flows/continuity}/RecoveryConfirmScreen.tsx +28 -19
  123. package/src/identity/hub/flows/continuity/SavePromptScreen.tsx +49 -0
  124. package/src/identity/hub/{screens → flows/create}/CreateFlow.tsx +61 -62
  125. package/src/identity/hub/flows/custody/CustodyEditFlow.tsx +347 -0
  126. package/src/identity/hub/flows/custody/custodyEffects.ts +321 -0
  127. package/src/identity/hub/flows/custody/custodyFlowActions.ts +236 -0
  128. package/src/identity/hub/flows/custody/custodyFlowEffects.ts +163 -0
  129. package/src/identity/hub/flows/custody/custodyFlowHelpers.ts +25 -0
  130. package/src/identity/hub/flows/custody/custodyFlowRoutes.tsx +239 -0
  131. package/src/identity/hub/flows/custody/custodyFlowTypes.ts +45 -0
  132. package/src/identity/hub/flows/custody/useCustodyFlow.tsx +25 -0
  133. package/src/identity/hub/flows/ens/EnsEditAdvancedScreens.tsx +336 -0
  134. package/src/identity/hub/flows/ens/EnsEditFlow.tsx +397 -0
  135. package/src/identity/hub/flows/ens/EnsEditMaintenanceScreens.tsx +332 -0
  136. package/src/identity/hub/flows/ens/EnsEditReviewScreens.tsx +471 -0
  137. package/src/identity/hub/flows/ens/EnsEditRunners.tsx +198 -0
  138. package/src/identity/hub/flows/ens/EnsEditShared.tsx +162 -0
  139. package/src/identity/hub/flows/ens/EnsEditSimpleScreens.tsx +518 -0
  140. package/src/identity/hub/flows/ens/IdentityHubEnsFlow.tsx +299 -0
  141. package/src/identity/hub/flows/ens/OperatorWalletsScreen.tsx +398 -0
  142. package/src/identity/hub/flows/ens/ensEditCopy.ts +117 -0
  143. package/src/identity/hub/flows/ens/ensEditTypes.ts +91 -0
  144. package/src/identity/hub/flows/profile/EditProfileFlow.tsx +271 -0
  145. package/src/identity/hub/flows/restore/RestoreFlow.tsx +324 -0
  146. package/src/identity/hub/flows/restore/useRestoreFlowEffects.ts +77 -0
  147. package/src/identity/hub/{screens → flows/settings}/StorageCredentialScreen.tsx +23 -44
  148. package/src/identity/hub/flows/token-transfer/IdentityHubTokenTransferFlow.tsx +162 -0
  149. package/src/identity/hub/flows/token-transfer/TokenTransferScreens.tsx +256 -0
  150. package/src/identity/hub/identityHubReducer.ts +164 -99
  151. package/src/identity/hub/model/continuity.ts +94 -0
  152. package/src/identity/hub/model/copy.ts +35 -0
  153. package/src/identity/hub/model/custody.ts +54 -0
  154. package/src/identity/hub/model/ens.ts +49 -0
  155. package/src/identity/hub/model/errors.ts +140 -0
  156. package/src/identity/hub/model/format.ts +15 -0
  157. package/src/identity/hub/model/identity.ts +94 -0
  158. package/src/identity/hub/model/network.ts +32 -0
  159. package/src/identity/hub/model/transfer.ts +57 -0
  160. package/src/identity/hub/operatorWallets.ts +131 -0
  161. package/src/identity/hub/reconciliation/agentReconciliation/hook.ts +46 -0
  162. package/src/identity/hub/reconciliation/agentReconciliation/ownership.ts +129 -0
  163. package/src/identity/hub/reconciliation/agentReconciliation/run.ts +302 -0
  164. package/src/identity/hub/reconciliation/agentReconciliation/types.ts +17 -0
  165. package/src/identity/hub/reconciliation/index.ts +21 -0
  166. package/src/identity/hub/reconciliation/useAgentReconciliation.ts +10 -0
  167. package/src/identity/hub/reconciliation/walletSetup.ts +220 -0
  168. package/src/identity/hub/txGuard.ts +51 -0
  169. package/src/identity/hub/types.ts +17 -0
  170. package/src/identity/hub/useIdentityHubContinuity.ts +136 -0
  171. package/src/identity/hub/useIdentityHubController.ts +396 -0
  172. package/src/identity/hub/useIdentityHubSideEffects.ts +309 -0
  173. package/src/identity/hub/utils.ts +79 -0
  174. package/src/identity/identityCompat.ts +34 -0
  175. package/src/identity/profile/agentIcon.ts +61 -0
  176. package/src/identity/profile/imagePicker.ts +12 -12
  177. package/src/identity/registry/erc8004/abi.ts +14 -0
  178. package/src/identity/registry/erc8004/chains.ts +150 -0
  179. package/src/identity/registry/erc8004/client.ts +11 -0
  180. package/src/identity/registry/erc8004/discovery.ts +511 -0
  181. package/src/identity/registry/erc8004/metadata.ts +335 -0
  182. package/src/identity/registry/erc8004/ownership.ts +121 -0
  183. package/src/identity/registry/erc8004/preflight.ts +123 -0
  184. package/src/identity/registry/erc8004/transactions.ts +77 -0
  185. package/src/identity/registry/erc8004/types.ts +88 -0
  186. package/src/identity/registry/erc8004/uri.ts +59 -0
  187. package/src/identity/registry/erc8004/utils.ts +58 -0
  188. package/src/identity/registry/erc8004.ts +53 -1106
  189. package/src/identity/registry/fieldParsers.ts +28 -0
  190. package/src/identity/registry/operatorVault/bytecode.ts +98 -0
  191. package/src/identity/registry/operatorVault/constants.ts +38 -0
  192. package/src/identity/registry/operatorVault/read.ts +246 -0
  193. package/src/identity/registry/operatorVault/transactions.ts +81 -0
  194. package/src/identity/registry/operatorVault.ts +44 -0
  195. package/src/identity/storage/ipfs.ts +26 -24
  196. package/src/identity/wallet/browserWallet/gas.ts +41 -0
  197. package/src/identity/wallet/browserWallet/html.ts +106 -0
  198. package/src/identity/wallet/browserWallet/http.ts +28 -0
  199. package/src/identity/wallet/browserWallet/requestServer.ts +106 -0
  200. package/src/identity/wallet/browserWallet/requests.ts +191 -0
  201. package/src/identity/wallet/browserWallet/session.ts +325 -0
  202. package/src/identity/wallet/browserWallet/types.ts +192 -0
  203. package/src/identity/wallet/browserWallet/validation.ts +74 -0
  204. package/src/identity/wallet/browserWallet.ts +30 -393
  205. package/src/identity/wallet/page/constants.ts +5 -0
  206. package/src/identity/wallet/page/controller.ts +251 -0
  207. package/src/identity/wallet/page/copy.ts +340 -0
  208. package/src/identity/wallet/page/grainient.ts +278 -0
  209. package/src/identity/wallet/page/html.ts +28 -0
  210. package/src/identity/wallet/page/markup.ts +50 -0
  211. package/src/identity/wallet/page/state.ts +9 -0
  212. package/src/identity/wallet/page/styles/base.ts +259 -0
  213. package/src/identity/wallet/page/styles/components.ts +262 -0
  214. package/src/identity/wallet/page/styles/index.ts +5 -0
  215. package/src/identity/wallet/page/styles/responsive.ts +247 -0
  216. package/src/identity/wallet/page/types.ts +47 -0
  217. package/src/identity/wallet/page/view.ts +535 -0
  218. package/src/identity/wallet/page/walletProvider.ts +70 -0
  219. package/src/identity/wallet/page.tsx +38 -0
  220. package/src/identity/wallet/walletPurposeCompat.ts +27 -0
  221. package/src/mcp/manager.ts +0 -1
  222. package/src/models/ModelPicker.tsx +36 -30
  223. package/src/models/catalog.ts +5 -2
  224. package/src/models/huggingface.ts +9 -9
  225. package/src/models/llamacpp.ts +13 -13
  226. package/src/models/modelDisplay.ts +75 -0
  227. package/src/models/modelPickerOptions.ts +16 -3
  228. package/src/models/modelRecommendation.ts +0 -1
  229. package/src/providers/errors.ts +16 -0
  230. package/src/providers/gemini.ts +252 -39
  231. package/src/providers/registry.ts +2 -2
  232. package/src/providers/retry.ts +1 -1
  233. package/src/runtime/sessionMode.ts +1 -1
  234. package/src/runtime/systemPrompt.ts +2 -0
  235. package/src/runtime/toolExecution.ts +18 -22
  236. package/src/runtime/toolIntent.ts +0 -20
  237. package/src/runtime/turn.ts +0 -92
  238. package/src/storage/atomicWrite.ts +4 -1
  239. package/src/storage/config.ts +181 -5
  240. package/src/storage/identity.ts +9 -3
  241. package/src/storage/secrets.ts +2 -2
  242. package/src/tools/bashSafety.ts +8 -0
  243. package/src/tools/changeDirectoryTool.ts +1 -1
  244. package/src/tools/deleteFileTool.ts +4 -4
  245. package/src/tools/editTool.ts +4 -4
  246. package/src/tools/editUtils.ts +5 -5
  247. package/src/tools/privateContinuityEditTool.ts +4 -5
  248. package/src/tools/privateContinuityReadTool.ts +1 -2
  249. package/src/tools/registry.ts +30 -0
  250. package/src/tools/writeFileTool.ts +5 -5
  251. package/src/ui/BrandSplash.tsx +20 -85
  252. package/src/ui/ProgressBar.tsx +3 -5
  253. package/src/ui/Select.tsx +20 -8
  254. package/src/ui/Spinner.tsx +38 -3
  255. package/src/ui/Surface.tsx +2 -2
  256. package/src/ui/TextInput.tsx +63 -20
  257. package/src/ui/theme.ts +7 -34
  258. package/src/utils/openExternal.ts +21 -0
  259. package/src/utils/withRetry.ts +47 -3
  260. package/src/identity/hub/identityHubEffects.ts +0 -937
  261. package/src/identity/hub/identityHubModel.ts +0 -371
  262. package/src/identity/hub/screens/ContinuityDashboardScreen.tsx +0 -156
  263. package/src/identity/hub/screens/EditProfileFlow.tsx +0 -146
  264. package/src/identity/hub/screens/IdentitySummary.tsx +0 -106
  265. package/src/identity/hub/screens/MenuScreen.tsx +0 -117
  266. package/src/identity/hub/screens/RestoreFlow.tsx +0 -206
  267. package/src/identity/wallet/wallet-page/wallet.html +0 -1202
  268. /package/src/identity/hub/{screens → components}/BusyScreen.tsx +0 -0
@@ -1,371 +0,0 @@
1
- import type { EthagentConfig, EthagentIdentity, SelectableNetwork } from '../../storage/config.js'
2
- import {
3
- RegisterAgentPreflightError,
4
- supportedErc8004ChainForId,
5
- type Erc8004AgentCandidate,
6
- } from '../registry/erc8004.js'
7
- import { AgentStateOwnerMismatchError } from '../crypto/backupEnvelope.js'
8
- import { ContinuitySnapshotOwnerMismatchError } from '../continuity/envelope.js'
9
- import type { ContinuityWorkingTreeStatus } from '../continuity/storage.js'
10
- import { resolveSelectedNetwork } from '../registry/registryConfig.js'
11
-
12
- export const PREFLIGHT_AGENT_URI = 'ipfs://bafybeigdyrztma2dbfczw7q6ooozbxlqzyw5r7w4f3qw2axvvxqg3w6y7q'
13
-
14
- export type IdentityHubErrorView = {
15
- title: string
16
- detail?: string
17
- hint?: string
18
- }
19
-
20
- export function initialAgentState(name: string, description: string, ownerAddress: string): Record<string, unknown> {
21
- return {
22
- version: 1,
23
- name,
24
- description,
25
- ownerAddress,
26
- createdAt: new Date().toISOString(),
27
- preferences: {},
28
- memory: {},
29
- }
30
- }
31
-
32
- export function identityHubErrorView(err: unknown): IdentityHubErrorView {
33
- if (err instanceof RegisterAgentPreflightError) {
34
- return {
35
- title: err.title,
36
- detail: err.detail,
37
- hint: err.hint,
38
- }
39
- }
40
- if (err instanceof AgentStateOwnerMismatchError) {
41
- return {
42
- title: 'backup locked to another wallet',
43
- detail: `wallet ${shortAddress(err.currentOwner)} cannot read state encrypted for ${shortAddress(err.backupOwner)}.`,
44
- hint: 'Use the wallet that created this backup.',
45
- }
46
- }
47
- if (err instanceof ContinuitySnapshotOwnerMismatchError) {
48
- return {
49
- title: 'continuity locked to another wallet',
50
- detail: `wallet ${shortAddress(err.currentOwner)} cannot read SOUL.md or MEMORY.md encrypted for ${shortAddress(err.snapshotOwner)}.`,
51
- hint: 'Use the wallet that created this continuity snapshot.',
52
- }
53
- }
54
- const message = err instanceof Error ? err.message : String(err)
55
- if (message === 'fetch failed') {
56
- return {
57
- title: 'storage unavailable',
58
- detail: 'could not reach storage.',
59
- hint: 'check the connection, then try again.',
60
- }
61
- }
62
- return {
63
- title: 'identity error',
64
- detail: message,
65
- }
66
- }
67
-
68
- export function pinataErrorText(err: unknown): string {
69
- const view = identityHubErrorView(err)
70
- return view.detail ?? view.title
71
- }
72
-
73
- export function isRegistrationPreflightError(err: unknown): boolean {
74
- return err instanceof RegisterAgentPreflightError
75
- }
76
-
77
- export function shortCid(cid: string): string {
78
- if (cid.length <= 18) return cid
79
- return `${cid.slice(0, 10)}...${cid.slice(-6)}`
80
- }
81
-
82
- export function shortAddress(address: string): string {
83
- if (address.length <= 14) return address
84
- return `${address.slice(0, 6)}...${address.slice(-4)}`
85
- }
86
-
87
- export function shortHash(value: string): string {
88
- if (value.length <= 18) return value
89
- return `${value.slice(0, 8)}...${value.slice(-6)}`
90
- }
91
-
92
- export function tokenLabel(candidate: Erc8004AgentCandidate): string {
93
- return tokenCandidateLabel(candidate)
94
- }
95
-
96
- export function tokenCandidateLabel(candidate: Erc8004AgentCandidate): string {
97
- return candidate.name?.trim() || `Agent Token #${candidate.agentId.toString()}`
98
- }
99
-
100
- export function tokenCandidateSelectLabel(
101
- candidate: Erc8004AgentCandidate,
102
- current = false,
103
- ): string {
104
- return `${tokenCandidateLabel(candidate)}${current ? ' *' : ''}`
105
- }
106
-
107
- export function tokenCandidateHint(candidate: Erc8004AgentCandidate): string {
108
- const chain = supportedErc8004ChainForId(candidate.chainId)
109
- const network = chain?.network ? networkLabel(chain.network) : chain?.name ?? `chain ${candidate.chainId}`
110
- const parts = [
111
- candidate.name?.trim() ? `token #${candidate.agentId.toString()}` : null,
112
- network,
113
- candidate.backup?.createdAt ? `backup ${formatDate(candidate.backup.createdAt)}` : null,
114
- ].filter((part): part is string => Boolean(part))
115
- return parts.join(' · ')
116
- }
117
-
118
- export function isCurrentAgentCandidate(
119
- identity: EthagentIdentity | undefined,
120
- candidate: Erc8004AgentCandidate,
121
- ): boolean {
122
- if (!identity?.agentId) return false
123
- if (identity.agentId !== candidate.agentId.toString()) return false
124
-
125
- const owner = identity.ownerAddress ?? identity.address
126
- if (owner && owner.toLowerCase() !== candidate.ownerAddress.toLowerCase()) return false
127
- if (identity.chainId !== undefined && identity.chainId !== candidate.chainId) return false
128
- if (
129
- identity.identityRegistryAddress
130
- && identity.identityRegistryAddress.toLowerCase() !== candidate.identityRegistryAddress.toLowerCase()
131
- ) {
132
- return false
133
- }
134
- return true
135
- }
136
-
137
- export function storageLabel(apiUrl: string): string {
138
- void apiUrl
139
- return 'IPFS'
140
- }
141
-
142
- const NETWORK_LABELS: Record<SelectableNetwork, string> = {
143
- mainnet: 'ethereum mainnet',
144
- arbitrum: 'arbitrum one',
145
- base: 'base',
146
- optimism: 'optimism',
147
- polygon: 'polygon',
148
- }
149
-
150
- export function networkLabel(network: SelectableNetwork): string {
151
- return NETWORK_LABELS[network]
152
- }
153
-
154
- const NETWORK_SUBTITLES: Record<SelectableNetwork, string> = {
155
- mainnet: 'agent tokens on ethereum mainnet.',
156
- arbitrum: 'agent tokens on arbitrum one.',
157
- base: 'agent tokens on base.',
158
- optimism: 'agent tokens on optimism.',
159
- polygon: 'agent tokens on polygon.',
160
- }
161
-
162
- export function networkSubtitle(network: SelectableNetwork): string {
163
- return NETWORK_SUBTITLES[network]
164
- }
165
-
166
- export function networkMenuTagline(): string {
167
- return 'choose where your agent token is created or found.'
168
- }
169
-
170
- export function currentNetworkLine(config?: EthagentConfig): string {
171
- return networkLabel(resolveSelectedNetwork(config))
172
- }
173
-
174
- export function chainSummaryRow(config?: EthagentConfig, identity?: EthagentIdentity): {
175
- label: string
176
- value: string
177
- tone: 'ok' | 'dim'
178
- } {
179
- const network = resolveSelectedNetwork(config)
180
- const fromIdentity = identity?.chainId ? supportedErc8004ChainForId(identity.chainId)?.name.toLowerCase() : undefined
181
- const value = fromIdentity ?? networkLabel(network)
182
- return { label: 'chain', value, tone: identity?.chainId ? 'ok' : 'dim' }
183
- }
184
-
185
- export function lastBackupLabel(identity?: EthagentIdentity): string {
186
- const created = identity?.backup?.createdAt
187
- return created ? formatDate(created) : 'never'
188
- }
189
-
190
- export function identitySummaryRows(
191
- identity: EthagentIdentity | undefined,
192
- config?: EthagentConfig,
193
- ): Array<{
194
- label: string
195
- value: string
196
- tone: 'ok' | 'dim'
197
- }> {
198
- const backup = identity?.backup
199
- const owner = identity?.ownerAddress ?? identity?.address
200
- const ownerValue = owner ? shortAddress(owner) : 'not connected'
201
- const tokenValue = identity?.agentId ? `#${identity.agentId}` : 'not created'
202
- const chain = chainSummaryRow(config, identity)
203
- const stateValue = backup?.cid ? shortCid(backup.cid) : 'not saved yet'
204
- const skillsValue = identity?.publicSkills?.cid ? shortCid(identity.publicSkills.cid) : 'not saved'
205
- const cardValue = identity?.publicSkills?.agentCardCid ? shortCid(identity.publicSkills.agentCardCid) : 'not saved'
206
- const imageValue = typeof identity?.state?.imageUrl === 'string' && identity.state.imageUrl.trim() ? 'attached' : 'not attached'
207
- return [
208
- { label: 'owner', value: ownerValue, tone: identity ? 'ok' : 'dim' },
209
- { label: 'token', value: tokenValue, tone: identity?.agentId ? 'ok' : 'dim' },
210
- { label: 'network', value: chain.value, tone: chain.tone },
211
- { label: 'state', value: stateValue, tone: backup ? 'ok' : 'dim' },
212
- { label: 'skills', value: skillsValue, tone: identity?.publicSkills?.cid ? 'ok' : 'dim' },
213
- { label: 'card', value: cardValue, tone: identity?.publicSkills?.agentCardCid ? 'ok' : 'dim' },
214
- { label: 'image', value: imageValue, tone: imageValue === 'attached' ? 'ok' : 'dim' },
215
- ]
216
- }
217
-
218
- export type LocalChangeStatusView = {
219
- label: string
220
- detail: string
221
- tone: 'ok' | 'warn' | 'dim'
222
- files: string[]
223
- hasLocalChanges: boolean
224
- }
225
-
226
- export function changedContinuitySnapshotFiles(
227
- workingStatus?: ContinuityWorkingTreeStatus | null,
228
- ): string[] {
229
- if (!workingStatus?.localContentHashes || !workingStatus.publishedContentHashes) return []
230
- const files: Array<keyof typeof workingStatus.localContentHashes> = ['SOUL.md', 'MEMORY.md', 'skills.json']
231
- return files.filter(file => workingStatus.localContentHashes?.[file] !== workingStatus.publishedContentHashes?.[file])
232
- }
233
-
234
- export function localChangeStatusView(
235
- workingStatus?: ContinuityWorkingTreeStatus | null,
236
- ): LocalChangeStatusView {
237
- if (!workingStatus) {
238
- return {
239
- label: 'Local Changes',
240
- detail: 'Checking status...',
241
- tone: 'dim',
242
- files: [],
243
- hasLocalChanges: false,
244
- }
245
- }
246
-
247
- if (workingStatus.publishState === 'published') {
248
- return {
249
- label: 'Local Changes',
250
- detail: 'None detected',
251
- tone: 'ok',
252
- files: [],
253
- hasLocalChanges: false,
254
- }
255
- }
256
-
257
- if (workingStatus.publishState === 'local-changes') {
258
- const files = changedContinuitySnapshotFiles(workingStatus)
259
- return {
260
- label: 'Local Changes',
261
- detail: files.length > 0 ? `Detected: ${files.join(', ')}` : 'Detected: local files differ from saved snapshot',
262
- tone: 'warn',
263
- files,
264
- hasLocalChanges: true,
265
- }
266
- }
267
-
268
- if (workingStatus.publishState === 'not-published') {
269
- return {
270
- label: 'Local Changes',
271
- detail: 'Snapshot not saved yet',
272
- tone: 'warn',
273
- files: [],
274
- hasLocalChanges: false,
275
- }
276
- }
277
-
278
- if (workingStatus.publishState === 'verify-needed') {
279
- return {
280
- label: 'Local Changes',
281
- detail: 'Unable to verify saved snapshot',
282
- tone: 'warn',
283
- files: [],
284
- hasLocalChanges: false,
285
- }
286
- }
287
-
288
- return {
289
- label: 'Local Changes',
290
- detail: 'Local files not restored',
291
- tone: 'warn',
292
- files: [],
293
- hasLocalChanges: false,
294
- }
295
- }
296
-
297
- export type IdentityDetailSection = {
298
- title: string
299
- rows: Array<{
300
- label: string
301
- value: string
302
- tone: 'ok' | 'dim'
303
- }>
304
- }
305
-
306
- export function identityDetailSections(
307
- identity: EthagentIdentity | undefined,
308
- config?: EthagentConfig,
309
- ): IdentityDetailSection[] {
310
- const backup = identity?.backup
311
- const owner = identity?.ownerAddress ?? identity?.address
312
- const chain = chainSummaryRow(config, identity)
313
- const stateCid = backup?.cid ?? 'not saved yet'
314
- const registrationCid = identity?.metadataCid ?? 'not saved yet'
315
- const publicSkillsCid = identity?.publicSkills?.cid ?? 'not saved'
316
- const agentCardCid = identity?.publicSkills?.agentCardCid ?? 'not saved'
317
-
318
- return [
319
- {
320
- title: 'Agent',
321
- rows: [
322
- { label: 'token', value: identity?.agentId ? `#${identity.agentId}` : 'not created', tone: identity?.agentId ? 'ok' : 'dim' },
323
- { label: 'network', value: chain.value, tone: chain.tone },
324
- { label: 'registration', value: registrationCid, tone: identity?.metadataCid ? 'ok' : 'dim' },
325
- ],
326
- },
327
- {
328
- title: 'Owner',
329
- rows: [
330
- { label: 'wallet', value: owner ?? 'not connected', tone: owner ? 'ok' : 'dim' },
331
- ],
332
- },
333
- {
334
- title: 'Recovery',
335
- rows: [
336
- { label: 'state CID', value: stateCid, tone: backup?.cid ? 'ok' : 'dim' },
337
- { label: 'skills CID', value: publicSkillsCid, tone: identity?.publicSkills?.cid ? 'ok' : 'dim' },
338
- { label: 'agent card CID', value: agentCardCid, tone: identity?.publicSkills?.agentCardCid ? 'ok' : 'dim' },
339
- { label: 'storage', value: backup?.ipfsApiUrl ? storageLabel(backup.ipfsApiUrl) : 'not saved yet', tone: backup?.ipfsApiUrl ? 'ok' : 'dim' },
340
- { label: 'created', value: identity?.createdAt ? formatDate(identity.createdAt) : 'not created', tone: identity?.createdAt ? 'ok' : 'dim' },
341
- { label: 'last backup', value: backup?.createdAt ? formatDate(backup.createdAt) : 'never', tone: backup?.createdAt ? 'ok' : 'dim' },
342
- { label: 'status', value: backup?.status ?? 'unknown', tone: backup?.status ? 'ok' : 'dim' },
343
- ],
344
- },
345
- ]
346
- }
347
-
348
- export type CopyableField = {
349
- label: string
350
- value: string
351
- }
352
-
353
- export function copyableIdentityFields(identity?: EthagentIdentity): CopyableField[] {
354
- if (!identity) return []
355
- const fields: CopyableField[] = []
356
- if (identity.backup?.cid) fields.push({ label: 'State CID', value: identity.backup.cid })
357
- if (identity.publicSkills?.cid) fields.push({ label: 'Skills CID', value: identity.publicSkills.cid })
358
- if (identity.publicSkills?.agentCardCid) fields.push({ label: 'Agent Card CID', value: identity.publicSkills.agentCardCid })
359
- if (identity.metadataCid) fields.push({ label: 'Registration CID', value: identity.metadataCid })
360
- if (identity.agentUri) fields.push({ label: 'Agent URI', value: identity.agentUri })
361
- const owner = identity.ownerAddress ?? identity.address
362
- if (owner) fields.push({ label: 'Owner Address', value: owner })
363
- if (identity.agentId) fields.push({ label: 'Token ID', value: identity.agentId })
364
- return fields
365
- }
366
-
367
- function formatDate(input: string): string {
368
- const date = new Date(input)
369
- if (Number.isNaN(date.getTime())) return input
370
- return date.toISOString().slice(0, 10)
371
- }
@@ -1,156 +0,0 @@
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
- import type { EthagentConfig, EthagentIdentity } from '../../../storage/config.js'
7
- import type { ContinuityWorkingTreeStatus } from '../../continuity/storage.js'
8
- import { IdentitySummary } from './IdentitySummary.js'
9
- import { shortCid } from '../identityHubModel.js'
10
-
11
- type PrivateAction = 'soul' | 'memory' | 'back'
12
- type PublicAction = 'edit' | 'skills' | 'back'
13
-
14
- type CommonProps = {
15
- identity?: EthagentIdentity
16
- config?: EthagentConfig
17
- workingStatus?: ContinuityWorkingTreeStatus | null
18
- ready: boolean
19
- notice?: string
20
- editorOpened?: boolean
21
- footer: React.ReactNode
22
- onBack: () => void
23
- }
24
-
25
- const SaveFromHubHint: React.FC<{ workingStatus?: ContinuityWorkingTreeStatus | null }> = ({ workingStatus }) => {
26
- const needsBackup = workingStatus?.publishState === 'local-changes'
27
- || workingStatus?.publishState === 'not-published'
28
- || workingStatus?.publishState === 'verify-needed'
29
- if (!needsBackup) return null
30
- return (
31
- <Box marginTop={1}>
32
- <Text color={theme.accentPeach}>Save path: Identity Hub &gt; Save Snapshot Now &gt; Yes, Save Snapshot Now.</Text>
33
- </Box>
34
- )
35
- }
36
-
37
- export const PrivateContinuityScreen: React.FC<CommonProps & {
38
- onOpenSoul: () => void
39
- onOpenMemory: () => void
40
- }> = ({
41
- identity,
42
- config,
43
- workingStatus,
44
- ready,
45
- notice,
46
- editorOpened,
47
- footer,
48
- onOpenSoul,
49
- onOpenMemory,
50
- onBack,
51
- }) => (
52
- <Surface title="Private Memory Files" subtitle={notice ?? privateSubtitle(ready)} footer={footer}>
53
- <IdentitySummary identity={identity} config={config} workingStatus={workingStatus} compact />
54
- <PrivateRows identity={identity} ready={ready} />
55
- <SaveFromHubHint workingStatus={workingStatus} />
56
- {editorOpened && (
57
- <Box marginTop={1}>
58
- <Text color={theme.accentPeach}>Save with ctrl+s in your editor</Text>
59
- </Box>
60
- )}
61
- <Box marginTop={1}>
62
- <Select<PrivateAction>
63
- options={[
64
- { value: 'soul', role: 'section', prefix: '--', label: 'Open Local Files' },
65
- { value: 'soul', label: 'Open SOUL.md', hint: 'Edit persona and operating preferences', disabled: !ready },
66
- { value: 'memory', label: 'Open MEMORY.md', hint: 'Edit private working memory for this agent', disabled: !ready },
67
- { value: 'back', role: 'section', prefix: '--', label: 'Navigation' },
68
- { value: 'back', label: 'Back To Identity Hub', hint: 'Return without changing private files', role: 'utility' },
69
- ]}
70
- hintLayout="inline"
71
- onSubmit={choice => {
72
- if (choice === 'soul') return onOpenSoul()
73
- if (choice === 'memory') return onOpenMemory()
74
- return onBack()
75
- }}
76
- onCancel={onBack}
77
- />
78
- </Box>
79
- </Surface>
80
- )
81
-
82
- export const PublicSkillsScreen: React.FC<CommonProps & {
83
- onEditProfile: () => void
84
- onOpenSkills: () => void
85
- }> = ({ identity, config, workingStatus, notice, editorOpened, footer, onEditProfile, onOpenSkills, onBack }) => (
86
- <Surface title="Public Profile" subtitle={notice ?? 'Manage public metadata, skills.json, and the agent card.'} footer={footer}>
87
- <IdentitySummary identity={identity} config={config} workingStatus={workingStatus} compact />
88
- <PublicProfileRows identity={identity} />
89
- <SaveFromHubHint workingStatus={workingStatus} />
90
- {editorOpened && (
91
- <Box marginTop={1}>
92
- <Text color={theme.accentPeach}>Save with ctrl+s in your editor</Text>
93
- </Box>
94
- )}
95
- <Box marginTop={1}>
96
- <Select<PublicAction>
97
- options={[
98
- { value: 'edit', role: 'section', prefix: '--', label: 'Profile' },
99
- { value: 'edit', label: 'Edit Name, Description, Image', hint: 'Upload a local image to IPFS automatically' },
100
- { value: 'skills', role: 'section', prefix: '--', label: 'Capabilities' },
101
- { value: 'skills', label: 'Open skills.json', hint: 'Edit public capabilities and notes' },
102
- { value: 'back', role: 'section', prefix: '--', label: 'Navigation' },
103
- { value: 'back', label: 'Back To Identity Hub', hint: 'Return without changing public metadata', role: 'utility' },
104
- ]}
105
- hintLayout="inline"
106
- onSubmit={choice => {
107
- if (choice === 'edit') return onEditProfile()
108
- if (choice === 'skills') return onOpenSkills()
109
- return onBack()
110
- }}
111
- onCancel={onBack}
112
- />
113
- </Box>
114
- </Surface>
115
- )
116
-
117
- const PrivateRows: React.FC<{ identity?: EthagentIdentity; ready: boolean }> = ({ identity, ready }) => (
118
- <Box flexDirection="column" marginTop={1}>
119
- <Text>
120
- <Text color={theme.dim}>{'Local Files'.padEnd(13)}</Text>
121
- <Text color={ready ? theme.text : theme.dim}>{ready ? 'SOUL.md and MEMORY.md Ready' : 'Missing Local Working Files'}</Text>
122
- </Text>
123
- <Text>
124
- <Text color={theme.dim}>{'Snapshot'.padEnd(13)}</Text>
125
- <Text color={identity?.backup?.cid ? theme.text : theme.dim}>{identity?.backup?.cid ? shortCid(identity.backup.cid) : 'Not Saved Yet'}</Text>
126
- </Text>
127
- </Box>
128
- )
129
-
130
- const PublicProfileRows: React.FC<{ identity?: EthagentIdentity }> = ({ identity }) => (
131
- <Box flexDirection="column" marginTop={1}>
132
- <Text>
133
- <Text color={theme.dim}>{'skills.json'.padEnd(13)}</Text>
134
- <Text color={identity?.publicSkills?.cid ? theme.text : theme.dim}>{identity?.publicSkills?.cid ? shortCid(identity.publicSkills.cid) : 'Not Saved'}</Text>
135
- </Text>
136
- <Text>
137
- <Text color={theme.dim}>{'Agent Card'.padEnd(13)}</Text>
138
- <Text color={identity?.publicSkills?.agentCardCid ? theme.text : theme.dim}>{identity?.publicSkills?.agentCardCid ? shortCid(identity.publicSkills.agentCardCid) : 'Not Saved'}</Text>
139
- </Text>
140
- <Text>
141
- <Text color={theme.dim}>{'Image'.padEnd(13)}</Text>
142
- <Text color={readStateString(identity?.state, 'imageUrl') ? theme.text : theme.dim}>{readStateString(identity?.state, 'imageUrl') ? 'Attached' : 'Not Attached'}</Text>
143
- </Text>
144
- </Box>
145
- )
146
-
147
- function privateSubtitle(ready: boolean): string {
148
- return ready
149
- ? 'SOUL.md and MEMORY.md are private local files on this machine.'
150
- : 'Use "Refetch Latest Snapshot" from the Identity Hub menu to recover files.'
151
- }
152
-
153
- function readStateString(state: Record<string, unknown> | undefined, key: string): string {
154
- const value = state?.[key]
155
- return typeof value === 'string' ? value.trim() : ''
156
- }
@@ -1,146 +0,0 @@
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 { TextInput } from '../../../ui/TextInput.js'
6
- import { theme } from '../../../ui/theme.js'
7
- import type { Step } from '../identityHubReducer.js'
8
-
9
- type EditProfileFlowProps = {
10
- step: Extract<Step, { kind: 'edit-profile-name' | 'edit-profile-description' | 'edit-profile-image' }>
11
- onNameSubmit: (name: string) => void
12
- onDescriptionSubmit: (description: string) => void
13
- onImageSubmit: (imagePath: string) => void
14
- onImagePick: () => void
15
- onBack: () => void
16
- onMenu: () => void
17
- }
18
-
19
- const footerHint = (hint: string) => <Text color={theme.dim}>{hint}</Text>
20
-
21
- export const EditProfileFlow: React.FC<EditProfileFlowProps> = ({
22
- step,
23
- onNameSubmit,
24
- onDescriptionSubmit,
25
- onImageSubmit,
26
- onImagePick,
27
- onBack,
28
- onMenu,
29
- }) => {
30
- const [manualImagePath, setManualImagePath] = React.useState(false)
31
-
32
- if (step.kind === 'edit-profile-name') {
33
- const currentName = readStateString(step.identity.state, 'name')
34
- return (
35
- <Surface
36
- title="Rename Agent Identity"
37
- subtitle="This updates public token metadata and the Agent Card."
38
- footer={footerHint('enter continues · esc back')}
39
- >
40
- <Text color={theme.dim}>Currently: {currentName || '(unnamed)'}</Text>
41
- <TextInput
42
- key="edit-profile-name"
43
- initialValue={currentName}
44
- placeholder="agent name"
45
- validate={value => value.trim().length >= 2 ? null : 'name must be at least 2 characters'}
46
- onSubmit={value => onNameSubmit(value.trim())}
47
- onCancel={onMenu}
48
- />
49
- </Surface>
50
- )
51
- }
52
-
53
- if (step.kind === 'edit-profile-image') {
54
- const currentImage = readStateString(step.identity.state, 'imageUrl')
55
- if (!manualImagePath) {
56
- return (
57
- <Surface
58
- title="Upload Agent Image"
59
- subtitle="Choose a local image. ethagent uploads it to IPFS and attaches it to token metadata."
60
- footer={footerHint('enter select · esc back')}
61
- >
62
- <Box flexDirection="column">
63
- <Text color={theme.dim}>Current: {currentImage || '(no image)'}</Text>
64
- {step.error ? <Text color="#e87070">{step.error}</Text> : null}
65
- </Box>
66
- <Box marginTop={1}>
67
- <Select<'choose' | 'manual' | 'skip' | 'delete' | 'back'>
68
- options={[
69
- { value: 'choose', role: 'section', prefix: '--', label: 'Image' },
70
- { value: 'choose', label: 'Choose Image File', hint: 'Open the operating system file picker' },
71
- { value: 'manual', label: 'Enter Path Manually', hint: 'Fallback if a file picker is unavailable' },
72
- ...(currentImage ? [{ value: 'delete' as const, label: 'Delete Current Image', hint: 'Remove the attached image from public profile' }] : []),
73
- { value: 'skip', label: currentImage ? 'Keep Current Image' : 'No Image', hint: 'Save without changing the image' },
74
- { value: 'back', role: 'section', prefix: '--', label: 'Navigation' },
75
- { value: 'back', label: 'Back', hint: 'Return to description', role: 'utility' },
76
- ]}
77
- hintLayout="inline"
78
- onSubmit={choice => {
79
- if (choice === 'choose') return onImagePick()
80
- if (choice === 'manual') return setManualImagePath(true)
81
- if (choice === 'delete') return onImageSubmit('delete')
82
- if (choice === 'skip') return onImageSubmit('')
83
- return onBack()
84
- }}
85
- onCancel={onBack}
86
- />
87
- </Box>
88
- </Surface>
89
- )
90
- }
91
- return (
92
- <Surface
93
- title="Enter Image Path"
94
- subtitle="Fallback path entry. URLs are rejected because ethagent uploads the local file itself."
95
- footer={footerHint('enter saves · esc back')}
96
- >
97
- <Text color={theme.dim}>Current: {currentImage || '(no image)'}</Text>
98
- <TextInput
99
- key="edit-profile-image"
100
- placeholder="local image path"
101
- allowEmpty
102
- validate={value => validateImagePath(value)}
103
- onSubmit={value => onImageSubmit(value.trim())}
104
- onCancel={() => setManualImagePath(false)}
105
- />
106
- </Surface>
107
- )
108
- }
109
-
110
- const currentDescription = readStateString(step.identity.state, 'description')
111
- return (
112
- <Surface
113
- title="Describe Agent Identity"
114
- subtitle="This updates public token metadata and the Agent Card."
115
- footer={footerHint('enter continues · esc back')}
116
- >
117
- <Text color={theme.dim}>Currently: {currentDescription || '(no description)'}</Text>
118
- <TextInput
119
- key="edit-profile-description"
120
- initialValue={currentDescription}
121
- placeholder="description"
122
- allowEmpty
123
- multiline
124
- onSubmit={value => onDescriptionSubmit(value.trim())}
125
- onCancel={onBack}
126
- />
127
- </Surface>
128
- )
129
- }
130
-
131
- function validateImagePath(value: string): string | null {
132
- const trimmed = value.trim()
133
- if (!trimmed) return null
134
- if (/^https?:\/\//i.test(trimmed) || /^ipfs:\/\//i.test(trimmed)) {
135
- return 'enter a local image file path; ethagent will upload it to IPFS'
136
- }
137
- if (!/\.(png|jpe?g|gif|webp|svg)$/i.test(trimmed)) {
138
- return 'image must be png, jpg, gif, webp, or svg'
139
- }
140
- return null
141
- }
142
-
143
- function readStateString(state: Record<string, unknown> | undefined, key: string): string {
144
- const value = state?.[key]
145
- return typeof value === 'string' ? value : ''
146
- }