ethagent 3.0.0 → 3.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 (26) hide show
  1. package/package.json +1 -1
  2. package/src/identity/continuity/envelope.ts +9 -9
  3. package/src/identity/continuity/publicSkills.ts +17 -0
  4. package/src/identity/hub/OperationalRoutes.tsx +11 -39
  5. package/src/identity/hub/continuity/ContinuityDashboardScreen.tsx +3 -23
  6. package/src/identity/hub/continuity/RecoveryConfirmScreen.tsx +5 -5
  7. package/src/identity/hub/continuity/SavePromptScreen.tsx +1 -3
  8. package/src/identity/hub/continuity/skills/SkillActionsScreen.tsx +151 -0
  9. package/src/identity/hub/continuity/skills/SkillsTreeScreen.tsx +1 -33
  10. package/src/identity/hub/continuity/state.ts +9 -9
  11. package/src/identity/hub/create/CreateFlow.tsx +1 -1
  12. package/src/identity/hub/custody/routes.tsx +1 -1
  13. package/src/identity/hub/ens/EnsEditAdvancedScreens.tsx +0 -1
  14. package/src/identity/hub/ens/EnsEditMaintenanceScreens.tsx +0 -1
  15. package/src/identity/hub/identityHubReducer.ts +3 -9
  16. package/src/identity/hub/profile/EditProfileFlow.tsx +5 -5
  17. package/src/identity/hub/restore/recovery.ts +3 -3
  18. package/src/identity/hub/shared/components/IdentitySummary.tsx +22 -2
  19. package/src/identity/hub/shared/components/MenuScreen.tsx +4 -4
  20. package/src/identity/hub/shared/components/UnlinkedIdentityScreen.tsx +3 -3
  21. package/src/identity/hub/shared/components/menuFlagsFromReconciliation.ts +1 -1
  22. package/src/identity/hub/useIdentityHubContinuity.ts +3 -3
  23. package/src/identity/wallet/page/copy.ts +43 -43
  24. package/src/models/modelPickerOptions.ts +2 -0
  25. package/src/identity/hub/continuity/skills/DeleteSkillScreen.tsx +0 -123
  26. package/src/identity/hub/continuity/skills/SkillVisibilityScreen.tsx +0 -171
@@ -62,10 +62,8 @@ export type Step =
62
62
  | { kind: 'continuity-skills-tree'; notice?: string; editorOpened?: boolean }
63
63
  | { kind: 'continuity-skill-new'; error?: string }
64
64
  | { kind: 'continuity-skill-new-visibility'; name: string; error?: string }
65
- | { kind: 'continuity-skill-delete'; notice?: string }
65
+ | { kind: 'continuity-skill-actions'; relativePath: string; notice?: string }
66
66
  | { kind: 'continuity-skill-delete-confirm'; target: { kind: 'skill'; relativePath: string } }
67
- | { kind: 'continuity-skill-visibility'; notice?: string }
68
- | { kind: 'continuity-skill-visibility-pick'; relativePath: string }
69
67
  | { kind: 'rebackup-confirm'; back: Step }
70
68
  | { kind: 'recovery-refetch-confirm'; back: Step }
71
69
  | { kind: 'recovery-refetching'; identity: EthagentIdentity; registry: Erc8004RegistryConfig; back: Step }
@@ -186,14 +184,10 @@ function backStep(from: Step): Step {
186
184
  return { kind: 'continuity-skills-tree' }
187
185
  case 'continuity-skill-new-visibility':
188
186
  return { kind: 'continuity-skill-new' }
189
- case 'continuity-skill-delete':
187
+ case 'continuity-skill-actions':
190
188
  return { kind: 'continuity-skills-tree' }
191
189
  case 'continuity-skill-delete-confirm':
192
- return { kind: 'continuity-skill-delete' }
193
- case 'continuity-skill-visibility':
194
- return { kind: 'continuity-skills-tree' }
195
- case 'continuity-skill-visibility-pick':
196
- return { kind: 'continuity-skill-visibility' }
190
+ return { kind: 'continuity-skill-actions', relativePath: from.target.relativePath }
197
191
  case 'rebackup-confirm':
198
192
  case 'recovery-refetch-confirm':
199
193
  case 'recovery-refetching':
@@ -57,7 +57,7 @@ export const EditProfileFlow: React.FC<EditProfileFlowProps> = ({
57
57
  const currentName = step.name ?? readIdentityStateString(step.identity.state, 'name')
58
58
  return (
59
59
  <Surface
60
- title="Edit Name, Description, Icon"
60
+ title="Edit Profile"
61
61
  subtitle={<FlowTimeline steps={EDIT_PROFILE_STEPS} current={1} />}
62
62
  footer={footerHint(EDIT_NEXT_FOOTER)}
63
63
  >
@@ -112,7 +112,7 @@ export const EditProfileFlow: React.FC<EditProfileFlowProps> = ({
112
112
  const draftDescription = step.description ?? currentDescription
113
113
  return (
114
114
  <Surface
115
- title="Edit Name, Description, Icon"
115
+ title="Edit Profile"
116
116
  subtitle={<FlowTimeline steps={EDIT_PROFILE_STEPS} current={2} />}
117
117
  footer={footerHint(EDIT_DESCRIPTION_FOOTER)}
118
118
  >
@@ -145,7 +145,7 @@ const AgentIconStep: React.FC<{
145
145
  if (entryMode) {
146
146
  return (
147
147
  <Surface
148
- title="Edit Name, Description, Icon"
148
+ title="Edit Profile"
149
149
  subtitle={<FlowTimeline steps={EDIT_PROFILE_STEPS} current={3} />}
150
150
  footer={footerHint(EDIT_NEXT_FOOTER)}
151
151
  >
@@ -165,7 +165,7 @@ const AgentIconStep: React.FC<{
165
165
 
166
166
  return (
167
167
  <Surface
168
- title="Edit Name, Description, Icon"
168
+ title="Edit Profile"
169
169
  subtitle={<FlowTimeline steps={EDIT_PROFILE_STEPS} current={3} />}
170
170
  footer={footerHint('enter select · esc back')}
171
171
  >
@@ -208,7 +208,7 @@ const EditProfileReviewStep: React.FC<{
208
208
  const currentIcon = readIdentityStateString(step.identity.state, 'imageUrl')
209
209
  return (
210
210
  <Surface
211
- title="Edit Name, Description, Icon"
211
+ title="Edit Profile"
212
212
  subtitle={<FlowTimeline steps={EDIT_PROFILE_STEPS} current={4} />}
213
213
  footer={footerHint('enter save · esc back')}
214
214
  >
@@ -78,7 +78,7 @@ export async function runRecoveryRefetch(
78
78
  walletSignature: wallet.signature,
79
79
  currentOwnerAddress: getAddress(wallet.account),
80
80
  })
81
- callbacks.onRestoreProgress?.({ phase: 'writing', label: 'restoring SOUL.md, MEMORY.md, and skills.json...' })
81
+ callbacks.onRestoreProgress?.({ phase: 'writing', label: 'restoring SOUL.md, MEMORY.md, and skills...' })
82
82
  const transferSnapshot = transferSnapshotMetadataFromEnvelope(envelope)
83
83
  const refreshedBackup: BackupMetadata = {
84
84
  cid: candidate.backup.cid,
@@ -136,10 +136,10 @@ export async function runRecoveryRefetch(
136
136
  const publicSkillsRestored = await restorePublishedPublicSkills(nextIdentity, apiUrl, candidate.publicDiscovery?.skillsCid)
137
137
  await ensureIdentityMarkdownScaffold(nextIdentity)
138
138
  await syncPublicSkillsManifest(nextIdentity).catch(() => null)
139
- await recordPublishedContinuitySnapshot({ identity: nextIdentity, label: 'Refetched Latest Snapshot From Chain' }).catch(() => null)
139
+ await recordPublishedContinuitySnapshot({ identity: nextIdentity, label: 'Refetched Latest Snapshot From Onchain' }).catch(() => null)
140
140
  if (publicSkillsRestored) {
141
141
  const contentHashes = await localContinuitySnapshotContentHashes(nextIdentity)
142
142
  await updatePublishedContinuitySnapshotContentHashes(nextIdentity, candidate.backup.cid, contentHashes).catch(() => null)
143
143
  }
144
- await callbacks.onIdentityComplete(nextIdentity, 'Latest Published Snapshot Restored From Chain', 'update')
144
+ await callbacks.onIdentityComplete(nextIdentity, 'Latest Published Snapshot Restored From Onchain', 'update')
145
145
  }
@@ -28,9 +28,10 @@ interface IdentitySummaryProps {
28
28
  hideHeader?: boolean
29
29
  tokenLinked?: boolean
30
30
  onchainOwner?: string
31
+ compact?: boolean
31
32
  }
32
33
 
33
- export const IdentitySummary: React.FC<IdentitySummaryProps> = ({ identity, config, workingStatus, hideLocalChanges = false, hideHeader = false, tokenLinked = true, onchainOwner }) => {
34
+ export const IdentitySummary: React.FC<IdentitySummaryProps> = ({ identity, config, workingStatus, hideLocalChanges = false, hideHeader = false, tokenLinked = true, onchainOwner, compact = false }) => {
34
35
  if (!identity) {
35
36
  return (
36
37
  <Text color={theme.dim}>No agent yet. Create or load one.</Text>
@@ -59,6 +60,25 @@ export const IdentitySummary: React.FC<IdentitySummaryProps> = ({ identity, conf
59
60
  ? `${tokenValue} · ${displayValue(networkValue)}`
60
61
  : displayValue(tokenValue)
61
62
 
63
+ if (compact) {
64
+ const name = stateName || 'Active Agent'
65
+ const tokenSegment = identity.agentId ? `#${identity.agentId}` : null
66
+ const networkSegment = identity.agentId ? displayValue(networkValue) : null
67
+ const ensSegment = ensStatus.kind === 'linked'
68
+ ? ensStatus.name
69
+ : ensStatus.kind === 'issue'
70
+ ? ensStatus.name
71
+ : null
72
+ return (
73
+ <Text>
74
+ <Text color={theme.accentPeriwinkle} bold>{name}</Text>
75
+ {tokenSegment ? <><Text color={theme.dim}> · </Text><Text color={theme.text}>{tokenSegment}</Text></> : null}
76
+ {networkSegment ? <><Text color={theme.dim}> · </Text><Text color={theme.text}>{networkSegment}</Text></> : null}
77
+ {ensSegment ? <><Text color={theme.dim}> · </Text><Text color={ensStatus.kind === 'issue' ? theme.accentError : theme.accentPeriwinkle}>{ensSegment}</Text></> : null}
78
+ </Text>
79
+ )
80
+ }
81
+
62
82
  return (
63
83
  <Box flexDirection="column">
64
84
  {hideHeader ? null : (
@@ -100,7 +120,7 @@ export const IdentitySummary: React.FC<IdentitySummaryProps> = ({ identity, conf
100
120
  }
101
121
  const pendingCell = {
102
122
  label: 'Pending',
103
- value: <Text color={theme.dim}>local ahead of chain, owner rotates pointer</Text>,
123
+ value: <Text color={theme.dim}>local ahead of onchain, owner rotates pointer</Text>,
104
124
  }
105
125
  return (
106
126
  <>
@@ -112,7 +112,7 @@ export const MenuScreen: React.FC<MenuScreenProps> = ({
112
112
  const options: Array<SelectOption<Action>> = identity
113
113
  ? [
114
114
  { value: 'public-profile', role: 'section', label: 'Public Identity' },
115
- { value: 'public-profile', label: 'Public Profile', hint: 'Identity card and profile fields' },
115
+ { value: 'public-profile', label: 'Public Profile', hint: 'Agent card and profile fields' },
116
116
  { value: 'ens-name', label: 'ENS Name', hint: ensNameHint, disabled: flags?.ensNameDisabled ?? false },
117
117
  { value: 'continuity', role: 'section', label: 'Continuity' },
118
118
  { value: 'continuity', label: 'Soul & Memory', hint: 'Edit SOUL.md and MEMORY.md' },
@@ -197,7 +197,7 @@ function renderReconciliationBanner(r: AgentReconciliation, identity: EthagentId
197
197
  return (
198
198
  <>
199
199
  <Text color={theme.accentError} bold>Agent Unlinked</Text>
200
- <Text color={theme.textSubtle}>{tokenLabel} was transferred. Local SOUL.md, MEMORY.md, skills.json remain. Back them up before this directory is reused.</Text>
200
+ <Text color={theme.textSubtle}>{tokenLabel} was transferred. Local SOUL.md, MEMORY.md, and skills remain. Back them up before this directory is reused.</Text>
201
201
  <Text color={theme.textSubtle}>Use Load Agent or New Agent to re-enable disabled actions.</Text>
202
202
  </>
203
203
  )
@@ -205,7 +205,7 @@ function renderReconciliationBanner(r: AgentReconciliation, identity: EthagentId
205
205
  return (
206
206
  <>
207
207
  <Text color={theme.accentError} bold>Agent Unlinked</Text>
208
- <Text color={theme.textSubtle}>{tokenLabel} left without Prepare Transfer. Back up local SOUL.md, MEMORY.md, skills.json before loading another agent.</Text>
208
+ <Text color={theme.textSubtle}>{tokenLabel} left without Prepare Transfer. Back up local SOUL.md, MEMORY.md, and skills before loading another agent.</Text>
209
209
  <Text color={theme.textSubtle}>For continuity handoff: ask the new holder to return the token, then run Prepare Transfer before re-sending.</Text>
210
210
  <Text color={theme.textSubtle}>Use Load Agent or New Agent to re-enable disabled actions.</Text>
211
211
  </>
@@ -224,7 +224,7 @@ function renderReconciliationBanner(r: AgentReconciliation, identity: EthagentId
224
224
  }
225
225
  const lines: string[] = []
226
226
  if (r.custody === 'mid-flow-uri-pending') lines.push('Advanced setup pending. Open Custody Mode to finish.')
227
- if (r.agentUri === 'local-newer') lines.push('Local state newer than chain. Save Snapshot Now to publish.')
227
+ if (r.agentUri === 'local-newer') lines.push('Local state newer than onchain. Save Snapshot Now to publish.')
228
228
  if (r.agentUri === 'chain-newer') lines.push('Onchain agentURI is newer than local. Refetch Latest.')
229
229
  if (r.vault === 'missing') lines.push('Recorded vault address has no contract at it. Open Custody Mode to redeploy.')
230
230
  if (r.workingTree === 'dirty') lines.push('Local edits pending. Save Snapshot Now to publish.')
@@ -33,7 +33,7 @@ export const UnlinkedIdentityScreen: React.FC<UnlinkedIdentityScreenProps> = ({
33
33
  ]
34
34
  if (onRetry) {
35
35
  options.push({ value: 'retry', role: 'section', label: 'Recheck' })
36
- options.push({ value: 'retry', label: 'Retry Ownership Check', hint: 'Re-query the chain to confirm the current owner', role: 'utility' })
36
+ options.push({ value: 'retry', label: 'Retry Ownership Check', hint: 'Re-query onchain to confirm the current owner', role: 'utility' })
37
37
  }
38
38
 
39
39
  const tokenLabel = agentId ? `Token #${agentId}` : 'Token'
@@ -48,13 +48,13 @@ export const UnlinkedIdentityScreen: React.FC<UnlinkedIdentityScreenProps> = ({
48
48
  <Box flexDirection="column">
49
49
  {transferSnapshot ? (
50
50
  <Text color={theme.textSubtle}>
51
- {tokenLabel} was transferred. Local SOUL.md, MEMORY.md, skills.json remain. Back them up before this directory is reused.
51
+ {tokenLabel} was transferred. Local SOUL.md, MEMORY.md, and skills remain. Back them up before this directory is reused.
52
52
  </Text>
53
53
  ) : (
54
54
  <>
55
55
  <Text color={theme.accentPeriwinkle}>{tokenLabel} left this wallet without Prepare Transfer, so the new holder has no continuity handoff.</Text>
56
56
  <Text color={theme.textSubtle}>
57
- Local SOUL.md, MEMORY.md, skills.json remain. Back them up before this directory is reused.
57
+ Local SOUL.md, MEMORY.md, and skills remain. Back them up before this directory is reused.
58
58
  </Text>
59
59
  </>
60
60
  )}
@@ -57,6 +57,6 @@ export function menuFlagsFromReconciliation(r: AgentReconciliation, perspective:
57
57
  custodyAsterisk: custodyAsterisk && !isOperator,
58
58
  ...(custodyHint ? { custodyHint } : {}),
59
59
  saveSnapshotAsterisk: r.agentUri === 'local-newer',
60
- ...(r.agentUri === 'local-newer' ? { saveSnapshotHint: 'Local newer than chain' } : {}),
60
+ ...(r.agentUri === 'local-newer' ? { saveSnapshotHint: 'Local newer than onchain' } : {}),
61
61
  }
62
62
  }
@@ -208,7 +208,7 @@ export function useIdentityHubContinuity({
208
208
 
209
209
  const deleteSkill = async (relativePath: string): Promise<void> => {
210
210
  await mutateSkillsTree({
211
- backStep: { kind: 'continuity-skill-delete' },
211
+ backStep: { kind: 'continuity-skills-tree' },
212
212
  run: async id => {
213
213
  await deleteSkillEntry(id, relativePath)
214
214
  return `deleted ${relativePath}`
@@ -221,8 +221,8 @@ export function useIdentityHubContinuity({
221
221
  visibility: SkillVisibility,
222
222
  ): Promise<void> => {
223
223
  await mutateSkillsTree({
224
- backStep: { kind: 'continuity-skill-visibility' },
225
- successStep: notice => ({ kind: 'continuity-skill-visibility', notice }),
224
+ backStep: { kind: 'continuity-skill-actions', relativePath },
225
+ successStep: notice => ({ kind: 'continuity-skill-actions', relativePath, notice }),
226
226
  run: async id => {
227
227
  await setSkillVisibilityStorage(id, relativePath, visibility)
228
228
  const display = relativePath.split('/')[0] ?? relativePath
@@ -17,19 +17,19 @@ export interface FlowCopy {
17
17
  }
18
18
 
19
19
  export const FLOW_COPY: Record<string, FlowCopy> = {
20
- account: { accent: "sign", tabTitle: "Connect Wallet", label: "Wallet Request", title: "Connect Wallet", detail: null },
20
+ account: { accent: "sign", tabTitle: "Connect Wallet", label: "Connection Request", title: "Connect Wallet", detail: null },
21
21
  sign: { accent: "sign", tabTitle: "Sign Message", label: "Signature Request", title: "Sign Message", detail: "message" },
22
- "sign-transaction": { accent: "transaction", tabTitle: "Sign And Save", label: "Identity Signature", title: "Sign And Save", detail: "message" },
23
- transaction: { accent: "transaction", tabTitle: "Submit Transaction", label: "Transaction Request", title: "Submit Transaction", detail: "registry" },
22
+ "sign-transaction": { accent: "transaction", tabTitle: "Sign Snapshot", label: "Snapshot Signature", title: "Sign Snapshot", detail: "message" },
23
+ transaction: { accent: "transaction", tabTitle: "Submit Transaction", label: "Onchain Transaction", title: "Submit Transaction", detail: "registry" },
24
24
  };
25
25
 
26
26
  export const TRANSACTION_TITLES: Record<string, string> = {
27
27
  "register-agent": "Mint Agent Token",
28
28
  "create-agent": "Create Agent",
29
- "update-ens-records": "Use ENS Owner Wallet",
30
- "clear-ens-records": "Use ENS Owner Wallet",
31
- "create-simple-ens-subdomain": "Use Connected Wallet",
32
- "set-simple-ens-records": "Use Connected Wallet",
29
+ "update-ens-records": "Submit With ENS Controller Wallet",
30
+ "clear-ens-records": "Submit With ENS Controller Wallet",
31
+ "create-simple-ens-subdomain": "Submit With Connected Wallet",
32
+ "set-simple-ens-records": "Submit With Connected Wallet",
33
33
  "create-agent-ens-subdomain": "Owner Wallet Required",
34
34
  "set-agent-ens-records": "Owner Wallet Required",
35
35
  "publish-transfer-snapshot": "Switch Back to Sender Wallet",
@@ -48,7 +48,7 @@ export const STATE_TITLES = {
48
48
  preparingTransaction: "Preparing Transaction",
49
49
  approveTransaction: "Review Transaction",
50
50
  error: "Wallet Error",
51
- default: "Wallet Request",
51
+ default: "Wallet Action",
52
52
  };
53
53
 
54
54
  export interface SubCopy { text: string; hint: string; }
@@ -64,14 +64,14 @@ export interface PurposeCopyEntry {
64
64
  export const PURPOSE_COPY: Record<string, PurposeCopyEntry> = {
65
65
  "connect-operator-wallet": {
66
66
  flowTitle: "Connect Wallet",
67
- account: { text: "Connect Wallet", hint: "Reads your operator wallet address so the agent ENS subdomain can resolve to it. No signature or transaction." },
67
+ account: { text: "Connect Wallet", hint: "Reads the operator wallet address for the agent ENS records. No signature or transaction." },
68
68
  prepare: { text: "Reading Operator Wallet...", hint: "Return to the terminal." },
69
69
  },
70
70
  "create-agent": {
71
71
  flowTitle: "Create Agent",
72
72
  sign: { text: "Sign With Owner Wallet", hint: "Creates encrypted recovery access. No token approval." },
73
73
  prepare: { text: "Saving Snapshot...", hint: "Keep this page open." },
74
- transaction: { text: "Use Owner Wallet", hint: "Submits one onchain transaction that mints a new ERC-8004 agent token to this wallet." },
74
+ transaction: { text: "Submit With Owner Wallet", hint: "Submits one onchain transaction that mints a new ERC-8004 agent token to this wallet." },
75
75
  },
76
76
  "restore-owner-wallet": {
77
77
  flowTitle: "Owner Wallet Required",
@@ -87,91 +87,91 @@ export const PURPOSE_COPY: Record<string, PurposeCopyEntry> = {
87
87
  flowTitle: "Owner Wallet Required",
88
88
  sign: { text: "Sign With Owner Wallet", hint: "Encrypts local files before publishing. Owner wallet is required because this update changes ownership-protected fields." },
89
89
  prepare: { text: "Saving Snapshot...", hint: "Keep this page open." },
90
- transaction: { text: "Use Owner Wallet", hint: "Publishes the updated snapshot to the ERC-8004 token URI." },
90
+ transaction: { text: "Submit With Owner Wallet", hint: "Publishes the updated snapshot to the ERC-8004 token URI." },
91
91
  },
92
92
  "update-snapshot-operator": {
93
93
  flowTitle: "Operator Wallet Save",
94
94
  sign: { text: "Sign With Operator Wallet", hint: "Signs the encrypted snapshot for restore access." },
95
95
  prepare: { text: "Saving Snapshot...", hint: "Keep this page open." },
96
- transaction: { text: "Use Operator Wallet", hint: "Rotates the ERC-8004 token URI through the Vault." },
96
+ transaction: { text: "Submit With Operator Wallet", hint: "Publishes the latest agent snapshot through the Vault." },
97
97
  },
98
98
  "update-snapshot-connected": {
99
99
  flowTitle: "Save Snapshot",
100
100
  sign: { text: "Sign With Connected Wallet", hint: "Encrypts local files before publishing. Single-wallet setup; no token approval." },
101
101
  prepare: { text: "Saving Snapshot...", hint: "Keep this page open." },
102
- transaction: { text: "Use Connected Wallet", hint: "Publishes the updated snapshot to the ERC-8004 token URI." },
102
+ transaction: { text: "Submit With Connected Wallet", hint: "Publishes the updated snapshot to the ERC-8004 token URI." },
103
103
  },
104
104
  "update-ens": {
105
105
  flowTitle: "Update ENS in Agent Snapshot",
106
106
  sign: { text: "Sign With Owner Wallet", hint: "Saves a new agent snapshot with this ENS name. No onchain ENS records change in this signature." },
107
107
  prepare: { text: "Saving Snapshot...", hint: "Keep this page open." },
108
- transaction: { text: "Use Owner Wallet", hint: "Publishes the updated ERC-8004 token URI pointing to the new snapshot." },
108
+ transaction: { text: "Submit With Owner Wallet", hint: "Publishes the updated ERC-8004 token URI pointing to the new snapshot." },
109
109
  },
110
110
  "clear-ens": {
111
- flowTitle: "Clear ENS from Agent Snapshot",
111
+ flowTitle: "Unlink ENS from Agent",
112
112
  sign: { text: "Sign With Owner Wallet", hint: "Saves a new agent snapshot with no ENS name. No onchain ENS records change in this signature." },
113
113
  prepare: { text: "Saving Snapshot...", hint: "Keep this page open." },
114
- transaction: { text: "Use Owner Wallet", hint: "Publishes the updated ERC-8004 token URI pointing to the new snapshot." },
114
+ transaction: { text: "Submit With Owner Wallet", hint: "Publishes the updated ERC-8004 token URI pointing to the new snapshot." },
115
115
  },
116
116
  "update-profile-owner": {
117
117
  flowTitle: "Owner Wallet Required",
118
118
  sign: { text: "Sign With Owner Wallet", hint: "Saves public profile changes. Owner wallet is required because this update changes ownership-protected fields." },
119
119
  prepare: { text: "Preparing Profile Update...", hint: "Keep this page open." },
120
- transaction: { text: "Use Owner Wallet", hint: "Publishes the updated public profile to the ERC-8004 token URI." },
120
+ transaction: { text: "Submit With Owner Wallet", hint: "Publishes the updated public profile to the ERC-8004 token URI." },
121
121
  },
122
122
  "update-profile-operator": {
123
123
  flowTitle: "Operator Wallet Profile Update",
124
124
  sign: { text: "Sign With Operator Wallet", hint: "Signs the encrypted snapshot for restore access." },
125
125
  prepare: { text: "Preparing Profile Update...", hint: "Keep this page open." },
126
- transaction: { text: "Use Operator Wallet", hint: "Rotates the agent URI through the Vault. No ENS write." },
126
+ transaction: { text: "Submit With Operator Wallet", hint: "Publishes the updated agent profile through the Vault. No ENS write." },
127
127
  },
128
128
  "update-profile-connected": {
129
129
  flowTitle: "Update Public Profile",
130
130
  sign: { text: "Sign With Connected Wallet", hint: "Saves public profile changes. Single-wallet setup; no token approval." },
131
131
  prepare: { text: "Preparing Profile Update...", hint: "Keep this page open." },
132
- transaction: { text: "Use Connected Wallet", hint: "Publishes the updated public profile to the ERC-8004 token URI." },
132
+ transaction: { text: "Submit With Connected Wallet", hint: "Publishes the updated public profile to the ERC-8004 token URI." },
133
133
  },
134
134
  "update-ens-records": {
135
135
  flowTitle: "Update ENS Records",
136
- sign: { text: "Sign With ENS Owner Wallet", hint: "Authorizes ENS record changes. No token approval." },
136
+ sign: { text: "Sign With ENS Controller Wallet", hint: "Authorizes ENS record changes. No token approval." },
137
137
  prepare: { text: "Preparing ENS Transaction...", hint: "Keep this page open." },
138
- transaction: { text: "Use ENS Owner Wallet", hint: "Submit one Ethereum Mainnet ENS record transaction." },
138
+ transaction: { text: "Submit With ENS Controller Wallet", hint: "Submit one Ethereum Mainnet ENS record transaction." },
139
139
  },
140
140
  "clear-ens-records": {
141
- flowTitle: "Unlink ENS",
142
- sign: { text: "Sign With ENS Owner Wallet", hint: "Authorizes clearing ethagent ENS text records. No token approval." },
141
+ flowTitle: "Clear ENS Records",
142
+ sign: { text: "Sign With ENS Controller Wallet", hint: "Authorizes clearing ethagent ENS text records. No token approval." },
143
143
  prepare: { text: "Preparing ENS Transaction...", hint: "Keep this page open." },
144
- transaction: { text: "Use ENS Owner Wallet", hint: "Submit one Ethereum Mainnet transaction to clear ethagent ENS text records." },
144
+ transaction: { text: "Submit With ENS Controller Wallet", hint: "Submit one Ethereum Mainnet transaction to clear ethagent ENS text records." },
145
145
  },
146
146
  "create-simple-ens-subdomain": {
147
147
  flowTitle: "Review ENS Subdomain Creation",
148
148
  sign: { text: "Sign With Connected Wallet", hint: "Connected wallet owns the ENS root and is creating the agent subdomain." },
149
149
  prepare: { text: "Preparing ENS Transaction...", hint: "Keep this page open." },
150
- transaction: { text: "Use Connected Wallet", hint: "Creates the agent ENS subdomain on Ethereum Mainnet. Must come from the parent name's owner." },
150
+ transaction: { text: "Submit With Connected Wallet", hint: "Creates the agent ENS subdomain on Ethereum Mainnet. Must come from the parent name's owner." },
151
151
  },
152
152
  "set-simple-ens-records": {
153
153
  flowTitle: "Apply ENS Records",
154
154
  sign: { text: "Sign With Connected Wallet", hint: "Connected wallet writes the agent ENS records." },
155
155
  prepare: { text: "Preparing ENS Transaction...", hint: "Keep this page open." },
156
- transaction: { text: "Use Connected Wallet", hint: "Writes the agent ENS text records onchain. Must come from the name's controller." },
156
+ transaction: { text: "Submit With Connected Wallet", hint: "Writes the agent ENS text records onchain. Must come from the name's controller." },
157
157
  },
158
158
  "create-agent-ens-subdomain": {
159
159
  flowTitle: "Review Agent ENS Subdomain",
160
160
  sign: { text: "Sign With Owner Wallet", hint: "Owner wallet controls the ENS root that the agent subdomain hangs off." },
161
161
  prepare: { text: "Preparing ENS Transaction...", hint: "Keep this page open." },
162
- transaction: { text: "Use Owner Wallet", hint: "Creates the agent ENS subdomain on Ethereum Mainnet. Must come from the parent name's owner wallet." },
162
+ transaction: { text: "Submit With Owner Wallet", hint: "Creates the agent ENS subdomain on Ethereum Mainnet. Must come from the parent name's owner wallet." },
163
163
  },
164
164
  "set-agent-ens-records": {
165
165
  flowTitle: "Apply Agent ENS Records",
166
166
  sign: { text: "Sign With Owner Wallet", hint: "Owner wallet writes the agent ENS records pointing at the operator wallet." },
167
167
  prepare: { text: "Preparing ENS Transaction...", hint: "Keep this page open." },
168
- transaction: { text: "Use Owner Wallet", hint: "Writes the agent ENS text and address records onchain. Must come from the name's controller." },
168
+ transaction: { text: "Submit With Owner Wallet", hint: "Writes the agent ENS text and address records onchain. Must come from the name's controller." },
169
169
  },
170
170
  "update-operators": {
171
171
  flowTitle: "Owner Wallet Required",
172
172
  sign: { text: "Sign With Owner Wallet", hint: "Authorizes the new operator wallet access list. No token approval." },
173
173
  prepare: { text: "Preparing Operator Wallets...", hint: "Keep this page open." },
174
- transaction: { text: "Use Owner Wallet", hint: "Publishes the updated operator wallet access list onchain." },
174
+ transaction: { text: "Submit With Owner Wallet", hint: "Publishes the updated operator wallet access list onchain." },
175
175
  },
176
176
  "operator-proof": {
177
177
  flowTitle: "Operator Wallet Required",
@@ -181,7 +181,7 @@ export const PURPOSE_COPY: Record<string, PurposeCopyEntry> = {
181
181
  "sync-operator-vault": {
182
182
  flowTitle: "Owner Wallet Required",
183
183
  prepare: { text: "Preparing Vault Operator Update...", hint: "Keep this page open." },
184
- transaction: { text: "Use Owner Wallet", hint: "Updates the Vault metadata-operator list so authorized operator wallets can rotate the agent URI." },
184
+ transaction: { text: "Submit With Owner Wallet", hint: "Updates the Vault metadata-operator list so authorized operator wallets can rotate the agent URI." },
185
185
  },
186
186
  "refetch-snapshot": {
187
187
  flowTitle: "Refetch Latest Snapshot",
@@ -189,7 +189,7 @@ export const PURPOSE_COPY: Record<string, PurposeCopyEntry> = {
189
189
  prepare: { text: "Verifying Wallet...", hint: "Return to the terminal." },
190
190
  },
191
191
  "prepare-transfer-sender": {
192
- flowTitle: "Use Sender Wallet",
192
+ flowTitle: "Submit With Sender Wallet",
193
193
  sign: { text: "Sign With Sender Wallet", hint: "Creates sender restore access. No token approval." },
194
194
  prepare: { text: "Verifying Sender Wallet...", hint: "Return to the terminal." },
195
195
  },
@@ -201,47 +201,47 @@ export const PURPOSE_COPY: Record<string, PurposeCopyEntry> = {
201
201
  "publish-transfer-snapshot": {
202
202
  flowTitle: "Switch Back to Sender Wallet",
203
203
  prepare: { text: "Preparing Token Update...", hint: "Keep this page open." },
204
- transaction: { text: "Use Sender Wallet", hint: "Submits one transaction to publish the transfer snapshot to the ERC-8004 token URI." },
204
+ transaction: { text: "Submit With Sender Wallet", hint: "Submits one transaction to publish the transfer snapshot to the ERC-8004 token URI." },
205
205
  },
206
206
  "deploy-agent-vault": {
207
207
  flowTitle: "Deploy Vault",
208
208
  prepare: { text: "Preparing Vault Deploy...", hint: "Keep this page open." },
209
- transaction: { text: "Use Owner Wallet", hint: "Deploys the Vault contract onchain. One-time setup per agent." },
209
+ transaction: { text: "Submit With Owner Wallet", hint: "Deploys the Vault contract onchain. One-time setup per agent." },
210
210
  errorContext: "While submitting the Vault deploy",
211
211
  },
212
212
  "deposit-agent-vault": {
213
213
  flowTitle: "Deposit Token Into Vault",
214
214
  prepare: { text: "Preparing Vault Deposit...", hint: "Keep this page open." },
215
- transaction: { text: "Use Owner Wallet", hint: "Sends the agent token to the Vault so the vault can save updates on your behalf." },
215
+ transaction: { text: "Submit With Owner Wallet", hint: "Sends the agent token to the Vault so the vault can save updates on your behalf." },
216
216
  errorContext: "While submitting the Vault deposit",
217
217
  },
218
218
  "unwrap-agent-vault": {
219
219
  flowTitle: "Unwrap Token From Vault",
220
220
  prepare: { text: "Preparing Vault Unwrap...", hint: "Keep this page open." },
221
- transaction: { text: "Use Owner Wallet", hint: "Returns the agent token from the Vault to your owner wallet." },
221
+ transaction: { text: "Submit With Owner Wallet", hint: "Returns the agent token from the Vault to your owner wallet." },
222
222
  },
223
223
  "rotate-agent-uri-vault-owner": {
224
224
  flowTitle: "Save Update Through Vault",
225
- sign: { text: "Sign With Owner Wallet", hint: "Approves the new snapshot before saving onchain. No token approval." },
225
+ sign: { text: "Sign With Owner Wallet", hint: "Signs the new snapshot before saving onchain. No token approval." },
226
226
  prepare: { text: "Preparing Update...", hint: "Keep this page open." },
227
- transaction: { text: "Use Owner Wallet", hint: "Saves your update onchain through the Vault. The vault holds your token, so updates go through it." },
227
+ transaction: { text: "Submit With Owner Wallet", hint: "Saves your update onchain through the Vault. Your token is locked in its dedicated Vault, so updates go through it." },
228
228
  },
229
229
  "rotate-agent-uri-vault-operator": {
230
230
  flowTitle: "Save Update Through Vault",
231
- sign: { text: "Sign With Operator Wallet", hint: "Approves the new snapshot before saving onchain. No token approval." },
231
+ sign: { text: "Sign With Operator Wallet", hint: "Signs the new snapshot before saving onchain. No token approval." },
232
232
  prepare: { text: "Preparing Update...", hint: "Keep this page open." },
233
- transaction: { text: "Use Operator Wallet", hint: "Rotates the ERC-8004 token URI through the Vault. The vault holds your token, so the operator wallet calls the vault to publish." },
233
+ transaction: { text: "Submit With Operator Wallet", hint: "Publishes the latest agent snapshot through the Vault. Your token is locked in its dedicated Vault, so the operator wallet calls the vault to publish." },
234
234
  },
235
235
  "withdraw-vault": {
236
236
  flowTitle: "Withdraw Token From Vault",
237
237
  prepare: { text: "Preparing Token Withdrawal...", hint: "Keep this page open." },
238
- transaction: { text: "Use Owner Wallet", hint: "Temporarily returns the agent token from the vault to your owner wallet. Vault stays configured so you can redeposit later." },
238
+ transaction: { text: "Submit With Owner Wallet", hint: "Temporarily returns the agent token from the vault to your owner wallet. Vault stays configured so you can redeposit later." },
239
239
  errorContext: "While submitting the Vault withdraw",
240
240
  },
241
241
  "delete-ens-subdomain": {
242
242
  flowTitle: "Delete ENS Subdomain",
243
243
  prepare: { text: "Preparing Subdomain Deletion...", hint: "Keep this page open." },
244
- transaction: { text: "Use Owner Wallet", hint: "Clears the subdomain entry in the parent ENS name. After this, the label is freed for reuse." },
244
+ transaction: { text: "Submit With Owner Wallet", hint: "Clears the subdomain entry in the parent ENS name. After this, the label is freed for reuse." },
245
245
  errorContext: "While deleting the ENS subdomain",
246
246
  },
247
247
  };
@@ -285,13 +285,13 @@ export function ensTokenChainHint(): string {
285
285
  }
286
286
  export function transactionCopy(): SubCopy {
287
287
  const txCopy = requirePurposeSubCopy("transaction");
288
- const expected = config.expectedAccount ? " Expected Wallet: " + shortAddr(config.expectedAccount) + "." : "";
288
+ const expected = config.expectedAccount ? " Required wallet:" + shortAddr(config.expectedAccount) + "." : "";
289
289
  const text = config.expectedAccount ? txCopy.text + " (" + shortAddr(config.expectedAccount) + ")" : txCopy.text;
290
290
  return { text, hint: txCopy.hint + expected + ensTokenChainHint() };
291
291
  }
292
292
  export function signCopy(): SubCopy {
293
293
  const sigCopy = requirePurposeSubCopy("sign");
294
- const expected = config.expectedAccount ? " Expected Wallet: " + shortAddr(config.expectedAccount) + "." : "";
294
+ const expected = config.expectedAccount ? " Required wallet:" + shortAddr(config.expectedAccount) + "." : "";
295
295
  const text = config.expectedAccount ? sigCopy.text + " (" + shortAddr(config.expectedAccount) + ")" : sigCopy.text;
296
296
  return { text, hint: sigCopy.hint + expected + ensTokenChainHint() };
297
297
  }
@@ -68,6 +68,7 @@ export function buildModelPickerOptions(
68
68
 
69
69
  options.push(sectionOption('hdr:local', 'Local Models'))
70
70
  appendHfModelOptions(options, data, context, 'Added From Links', 46)
71
+ options.push(groupOption('hdr:local:manage', 'Manage'))
71
72
  options.push(utilityOption('hf:download', 'Add Local Model File', LOCAL_MODEL_LINK_HINT))
72
73
  options.push(utilityOption('local:catalog', 'View Full Catalog', 'Curated local GGUF files'))
73
74
  if (data.hfModels.length > 0) {
@@ -109,6 +110,7 @@ export function buildModelPickerOptions(
109
110
  contextFitLabel(provider, model, `${displayName}${active ? ' *' : ''}`, context.contextFit),
110
111
  ))
111
112
  }
113
+ options.push(groupOption(`hdr:cloud:${provider}:manage`, 'Manage'))
112
114
  options.push(utilityOption(`catalog:${provider}`, 'Full Catalog'))
113
115
  const manageLabel = provider === 'openai' && data.cloudCredentialKinds?.openai === 'oauth'
114
116
  ? 'Manage ChatGPT Sign-in'
@@ -1,123 +0,0 @@
1
- import React, { useEffect, useState } from 'react'
2
- import { Box, Text } from 'ink'
3
- import { Surface } from '../../../../ui/Surface.js'
4
- import { Select, type SelectOption } from '../../../../ui/Select.js'
5
- import { theme } from '../../../../ui/theme.js'
6
- import type { EthagentIdentity } from '../../../../storage/config.js'
7
- import { listSkills } from '../../../continuity/skills/loadSkills.js'
8
- import type { SkillIndexEntry } from '../../../continuity/skills/types.js'
9
-
10
- type DeleteTarget =
11
- | { kind: 'skill'; relativePath: string }
12
- | { kind: 'cancel' }
13
-
14
- interface DeleteSkillScreenProps {
15
- identity?: EthagentIdentity
16
- notice?: string
17
- footer: React.ReactNode
18
- onPick: (target: { kind: 'skill'; relativePath: string }) => void
19
- onCancel: () => void
20
- }
21
-
22
- export const DeleteSkillScreen: React.FC<DeleteSkillScreenProps> = ({
23
- identity,
24
- notice,
25
- footer,
26
- onPick,
27
- onCancel,
28
- }) => {
29
- const [entries, setEntries] = useState<SkillIndexEntry[] | null>(null)
30
- const [error, setError] = useState<string | null>(null)
31
-
32
- useEffect(() => {
33
- let cancelled = false
34
- if (!identity) {
35
- setEntries([])
36
- return () => { cancelled = true }
37
- }
38
- listSkills(identity)
39
- .then(list => { if (!cancelled) { setEntries(list); setError(null) } })
40
- .catch(err => {
41
- if (cancelled) return
42
- setEntries([])
43
- setError(String((err as Error).message ?? err))
44
- })
45
- return () => { cancelled = true }
46
- }, [identity])
47
-
48
- const subtitle = notice ?? 'Pick a skill to delete. The whole folder will be removed.'
49
- const isLoading = entries === null
50
- const skills = entries ?? []
51
- const hasAnything = skills.length > 0
52
-
53
- const options = buildOptions(skills, isLoading)
54
-
55
- return (
56
- <Surface title="Delete Skill" subtitle={subtitle} footer={footer} tone="error">
57
- {error && (
58
- <Box marginTop={1}>
59
- <Text color={theme.accentError}>{error}</Text>
60
- </Box>
61
- )}
62
- {!isLoading && !hasAnything && (
63
- <Box marginTop={1}>
64
- <Text color={theme.dim}>Nothing to delete. The skills tree is empty.</Text>
65
- </Box>
66
- )}
67
- <Box marginTop={1}>
68
- <Select<DeleteTarget>
69
- options={options}
70
- hintLayout="inline"
71
- onSubmit={choice => {
72
- if (choice.kind === 'cancel') return onCancel()
73
- return onPick(choice)
74
- }}
75
- onCancel={onCancel}
76
- />
77
- </Box>
78
- </Surface>
79
- )
80
- }
81
-
82
- function buildOptions(
83
- entries: SkillIndexEntry[],
84
- isLoading: boolean,
85
- ): Array<SelectOption<DeleteTarget>> {
86
- const rows: Array<SelectOption<DeleteTarget>> = []
87
- const noopValue: DeleteTarget = { kind: 'cancel' }
88
-
89
- if (isLoading) {
90
- rows.push({
91
- value: noopValue,
92
- role: 'notice',
93
- label: 'Loading...',
94
- labelColor: theme.dim,
95
- indent: 0,
96
- })
97
- } else if (entries.length > 0) {
98
- rows.push({ value: noopValue, role: 'section', label: 'Targets' })
99
- rows.push({ value: noopValue, role: 'notice', label: 'skills/', labelColor: theme.dim, indent: 3 })
100
- const sorted = [...entries].sort((a, b) => a.name.localeCompare(b.name))
101
- for (let i = 0; i < sorted.length; i++) {
102
- const skill = sorted[i]
103
- if (!skill) continue
104
- const isLast = i === sorted.length - 1
105
- const branch = isLast ? '└── ' : '├── '
106
- rows.push({
107
- value: { kind: 'skill', relativePath: skill.relativePath },
108
- label: `${branch}${skill.name}/`,
109
- indent: 3,
110
- })
111
- }
112
- }
113
-
114
- rows.push({ value: noopValue, role: 'section', label: 'Navigation' })
115
- rows.push({
116
- value: { kind: 'cancel' },
117
- label: 'Back',
118
- hint: 'Return to Skills',
119
- role: 'utility',
120
- })
121
-
122
- return rows
123
- }