ethagent 3.0.1 → 3.0.2
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.
package/package.json
CHANGED
package/src/chat/ChatScreen.tsx
CHANGED
|
@@ -23,6 +23,7 @@ import type { ModelPickerSelection } from '../models/ModelPicker.js'
|
|
|
23
23
|
import type { ModelPickerContextFit } from '../models/modelPickerOptions.js'
|
|
24
24
|
import type { CopyResult } from '../utils/clipboard.js'
|
|
25
25
|
import { useKeybinding, useRegisterKeybindingContext } from '../app/keybindings/KeybindingProvider.js'
|
|
26
|
+
import { TITLE_ANIMATION_FRAMES, TITLE_ANIMATION_INTERVAL_MS, TITLE_STATIC, setTerminalTitle } from '../ui/terminalTitle.js'
|
|
26
27
|
import { useCancelRequest } from '../app/hooks/useCancelRequest.js'
|
|
27
28
|
import { useExitOnCtrlC } from '../app/hooks/useExitOnCtrlC.js'
|
|
28
29
|
import {
|
|
@@ -1475,7 +1476,22 @@ export const ChatScreen: React.FC<ChatScreenProps> = ({ config: initialConfig, o
|
|
|
1475
1476
|
],
|
|
1476
1477
|
)
|
|
1477
1478
|
|
|
1478
|
-
const busy = pullInFlight || Boolean(compactionUi)
|
|
1479
|
+
const busy = streaming || pullInFlight || Boolean(compactionUi)
|
|
1480
|
+
|
|
1481
|
+
useEffect(() => {
|
|
1482
|
+
if (!busy) {
|
|
1483
|
+
setTerminalTitle(TITLE_STATIC)
|
|
1484
|
+
return
|
|
1485
|
+
}
|
|
1486
|
+
let i = 0
|
|
1487
|
+
setTerminalTitle(TITLE_ANIMATION_FRAMES[0])
|
|
1488
|
+
const id = setInterval(() => {
|
|
1489
|
+
i = (i + 1) % TITLE_ANIMATION_FRAMES.length
|
|
1490
|
+
setTerminalTitle(TITLE_ANIMATION_FRAMES[i] ?? TITLE_STATIC)
|
|
1491
|
+
}, TITLE_ANIMATION_INTERVAL_MS)
|
|
1492
|
+
return () => clearInterval(id)
|
|
1493
|
+
}, [busy])
|
|
1494
|
+
|
|
1479
1495
|
const slashSuggestions = useMemo(
|
|
1480
1496
|
() => getSlashSuggestions(mcpManagerRef.current?.getPromptSuggestions() ?? []),
|
|
1481
1497
|
[mcpSnapshot],
|
package/src/cli/main.tsx
CHANGED
|
@@ -14,6 +14,7 @@ import { runResetCommand } from './reset.js'
|
|
|
14
14
|
import { runPreviewCommand } from './preview.js'
|
|
15
15
|
import { checkForUpdates } from './updateNotice.js'
|
|
16
16
|
import { Spinner } from '../ui/Spinner.js'
|
|
17
|
+
import { TITLE_STATIC, clearTerminalTitle, setTerminalTitle } from '../ui/terminalTitle.js'
|
|
17
18
|
|
|
18
19
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
19
20
|
|
|
@@ -87,6 +88,10 @@ const AppRoot: React.FC<{ setExitCode: (code: number) => void; currentVersion: s
|
|
|
87
88
|
return () => { cancelled = true }
|
|
88
89
|
}, [currentVersion])
|
|
89
90
|
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
setTerminalTitle(TITLE_STATIC)
|
|
93
|
+
}, [])
|
|
94
|
+
|
|
90
95
|
useEffect(() => {
|
|
91
96
|
if (phase.kind === 'cancelled') {
|
|
92
97
|
setExitCode(1)
|
|
@@ -152,6 +157,8 @@ const AppRoot: React.FC<{ setExitCode: (code: number) => void; currentVersion: s
|
|
|
152
157
|
|
|
153
158
|
async function runDefault(currentVersion: string): Promise<number> {
|
|
154
159
|
let exitCode = 0
|
|
160
|
+
setTerminalTitle(TITLE_STATIC)
|
|
161
|
+
process.once('exit', clearTerminalTitle)
|
|
155
162
|
const instance = render(
|
|
156
163
|
<AppInputProvider>
|
|
157
164
|
<KeybindingProvider>
|
|
@@ -15,7 +15,7 @@ type PublicSkillsProfile = {
|
|
|
15
15
|
name: string
|
|
16
16
|
description: string
|
|
17
17
|
version: string
|
|
18
|
-
agentWallet
|
|
18
|
+
agentWallet?: string
|
|
19
19
|
imageUrl?: string
|
|
20
20
|
skills: PublicSkill[]
|
|
21
21
|
}
|
|
@@ -34,7 +34,7 @@ type AgentCard = {
|
|
|
34
34
|
pushNotifications: boolean
|
|
35
35
|
}
|
|
36
36
|
producer: { name: string; url: string }
|
|
37
|
-
agent_wallet
|
|
37
|
+
agent_wallet?: string
|
|
38
38
|
skills: Array<{
|
|
39
39
|
id: string
|
|
40
40
|
name: string
|
|
@@ -66,7 +66,7 @@ export function defaultPublicSkillsProfile(identity: EthagentIdentity): PublicSk
|
|
|
66
66
|
name,
|
|
67
67
|
description,
|
|
68
68
|
version: '1.0.0',
|
|
69
|
-
agentWallet,
|
|
69
|
+
...(agentWallet ? { agentWallet } : {}),
|
|
70
70
|
...(imageUrl ? { imageUrl } : {}),
|
|
71
71
|
skills: [
|
|
72
72
|
{
|
|
@@ -135,7 +135,7 @@ export function renderPublicSkillsJson(profile: PublicSkillsProfile): string {
|
|
|
135
135
|
const summary = {
|
|
136
136
|
schema: 'ethagent.public-skills.v1',
|
|
137
137
|
producer: ETHAGENT_PRODUCER,
|
|
138
|
-
agent_wallet: profile.agentWallet,
|
|
138
|
+
...(profile.agentWallet ? { agent_wallet: profile.agentWallet } : {}),
|
|
139
139
|
visibility: 'public',
|
|
140
140
|
name: profile.name,
|
|
141
141
|
description: profile.description,
|
|
@@ -191,6 +191,7 @@ export function createAgentCard(profile: PublicSkillsProfile, url?: string): Age
|
|
|
191
191
|
name: profile.name,
|
|
192
192
|
description: profile.description,
|
|
193
193
|
version: profile.version,
|
|
194
|
+
...(profile.agentWallet ? { agent_wallet: profile.agentWallet } : {}),
|
|
194
195
|
protocolVersion: '0.2.6',
|
|
195
196
|
...(url ? { url } : {}),
|
|
196
197
|
...(profile.imageUrl ? { image: profile.imageUrl } : {}),
|
|
@@ -201,7 +202,6 @@ export function createAgentCard(profile: PublicSkillsProfile, url?: string): Age
|
|
|
201
202
|
pushNotifications: false,
|
|
202
203
|
},
|
|
203
204
|
producer: ETHAGENT_PRODUCER,
|
|
204
|
-
agent_wallet: profile.agentWallet,
|
|
205
205
|
skills: profile.skills.map(skill => ({
|
|
206
206
|
id: skill.id,
|
|
207
207
|
name: skill.name,
|
|
@@ -181,16 +181,15 @@ function serializeOperatorsPointer(pointer: EthagentOperatorsPointer): Record<st
|
|
|
181
181
|
export function withEthagentBackupPointer(
|
|
182
182
|
registration: Record<string, unknown> | null,
|
|
183
183
|
backup: EthagentBackupPointer,
|
|
184
|
-
publicDiscovery
|
|
185
|
-
registrationPointer
|
|
186
|
-
ownerAddress
|
|
184
|
+
publicDiscovery: EthagentPublicDiscoveryPointer | undefined,
|
|
185
|
+
registrationPointer: EthagentRegistrationPointer | undefined,
|
|
186
|
+
ownerAddress: Address,
|
|
187
187
|
): Record<string, unknown> {
|
|
188
|
-
const inferredOwnerAddress = ownerAddress ?? backup.agentAddress
|
|
189
188
|
return withEthagentPointers(registration, {
|
|
190
189
|
backup,
|
|
191
190
|
publicDiscovery,
|
|
192
191
|
registration: registrationPointer,
|
|
193
|
-
|
|
192
|
+
ownerAddress,
|
|
194
193
|
})
|
|
195
194
|
}
|
|
196
195
|
|
|
@@ -202,29 +201,26 @@ export function withEthagentPointers(
|
|
|
202
201
|
registration?: EthagentRegistrationPointer
|
|
203
202
|
ensName?: string
|
|
204
203
|
operators?: EthagentOperatorsPointer
|
|
205
|
-
ownerAddress
|
|
204
|
+
ownerAddress: Address
|
|
206
205
|
},
|
|
207
206
|
): Record<string, unknown> {
|
|
208
207
|
const next: Record<string, unknown> = registration ? { ...registration } : {}
|
|
209
208
|
const prior = objectField(next, 'x-ethagent') ?? {}
|
|
210
209
|
const { backup, publicDiscovery, registration: registrationPointer, operators } = pointers
|
|
211
210
|
const updatedAt = publicDiscovery?.updatedAt ?? backup?.createdAt
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
: undefined
|
|
211
|
+
if (!pointers.ownerAddress) {
|
|
212
|
+
throw new Error('withEthagentPointers requires ownerAddress')
|
|
213
|
+
}
|
|
214
|
+
const ownerAddress = getAddress(pointers.ownerAddress)
|
|
217
215
|
const priorX402 = objectField(prior, 'x402') ?? {}
|
|
218
216
|
const ext: Record<string, unknown> = {
|
|
219
217
|
...prior,
|
|
220
218
|
version: 1,
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
},
|
|
227
|
-
} : {}),
|
|
219
|
+
agentAddress: ownerAddress,
|
|
220
|
+
x402: {
|
|
221
|
+
...priorX402,
|
|
222
|
+
walletAddress: ownerAddress,
|
|
223
|
+
},
|
|
228
224
|
...(backup ? {
|
|
229
225
|
backup: {
|
|
230
226
|
cid: backup.cid,
|
|
@@ -252,8 +248,11 @@ export function withEthagentPointers(
|
|
|
252
248
|
delete ext.transfer
|
|
253
249
|
delete ext.handoff
|
|
254
250
|
next['x-ethagent'] = ext
|
|
255
|
-
|
|
256
|
-
|
|
251
|
+
const agentWalletService = registrationPointer
|
|
252
|
+
? { name: 'agentWallet' as const, endpoint: `eip155:${registrationPointer.chainId}:${ownerAddress}` }
|
|
253
|
+
: undefined
|
|
254
|
+
if (publicDiscovery || agentWalletService) {
|
|
255
|
+
next.services = withEthagentServices(next.services, publicDiscovery, pointers.ensName, agentWalletService)
|
|
257
256
|
}
|
|
258
257
|
if (registrationPointer && registrationPointer.agentId !== undefined) {
|
|
259
258
|
next.registrations = withRegistrationsArray(next.registrations, registrationPointer)
|
|
@@ -272,10 +271,18 @@ function serializeTransferSnapshotMetadata(metadata: TransferSnapshotMetadata):
|
|
|
272
271
|
}
|
|
273
272
|
}
|
|
274
273
|
|
|
275
|
-
function
|
|
274
|
+
function withEthagentServices(
|
|
275
|
+
input: unknown,
|
|
276
|
+
publicDiscovery: EthagentPublicDiscoveryPointer | undefined,
|
|
277
|
+
ensName: string | undefined,
|
|
278
|
+
agentWallet: { name: 'agentWallet'; endpoint: string } | undefined,
|
|
279
|
+
): unknown[] {
|
|
276
280
|
const prior = Array.isArray(input) ? input.filter(item => item && typeof item === 'object') : []
|
|
277
281
|
const services = prior.filter(item => !isEthagentManagedService(item)) as unknown[]
|
|
278
|
-
if (
|
|
282
|
+
if (agentWallet) {
|
|
283
|
+
pushUniqueService(services, agentWallet)
|
|
284
|
+
}
|
|
285
|
+
if (publicDiscovery?.agentCardCid) {
|
|
279
286
|
const endpoint = `ipfs://${publicDiscovery.agentCardCid}`
|
|
280
287
|
pushUniqueService(services, {
|
|
281
288
|
type: 'a2a',
|
|
@@ -284,7 +291,7 @@ function withPublicDiscoveryServices(input: unknown, publicDiscovery: EthagentPu
|
|
|
284
291
|
url: endpoint,
|
|
285
292
|
})
|
|
286
293
|
}
|
|
287
|
-
if (publicDiscovery
|
|
294
|
+
if (publicDiscovery?.skillsCid) {
|
|
288
295
|
const endpoint = `ipfs://${publicDiscovery.skillsCid}`
|
|
289
296
|
pushUniqueService(services, {
|
|
290
297
|
type: 'A2A-skills',
|
|
@@ -313,6 +320,7 @@ function isEthagentManagedService(item: unknown): boolean {
|
|
|
313
320
|
const obj = item as Record<string, unknown>
|
|
314
321
|
const type = obj.type
|
|
315
322
|
const name = obj.name
|
|
323
|
+
if (name === 'agentWallet') return true
|
|
316
324
|
if (name === 'ENS') return true
|
|
317
325
|
if (type === 'a2a' && (name === undefined || name === 'agent-card')) return true
|
|
318
326
|
return (type === 'A2A-skills' || type === 'ipfs') && name === 'public-skills'
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const TITLE_STATIC = 'ethagent'
|
|
2
|
+
export const TITLE_ANIMATION_FRAMES = [
|
|
3
|
+
'ethagent ⠋',
|
|
4
|
+
'ethagent ⠙',
|
|
5
|
+
'ethagent ⠹',
|
|
6
|
+
'ethagent ⠸',
|
|
7
|
+
'ethagent ⠼',
|
|
8
|
+
'ethagent ⠴',
|
|
9
|
+
'ethagent ⠦',
|
|
10
|
+
'ethagent ⠧',
|
|
11
|
+
'ethagent ⠇',
|
|
12
|
+
'ethagent ⠏',
|
|
13
|
+
] as const
|
|
14
|
+
export const TITLE_ANIMATION_INTERVAL_MS = 80
|
|
15
|
+
|
|
16
|
+
export function setTerminalTitle(title: string): void {
|
|
17
|
+
const clean = title.replace(/[\x00-\x1f]/g, '')
|
|
18
|
+
if (process.platform === 'win32') {
|
|
19
|
+
process.title = clean
|
|
20
|
+
}
|
|
21
|
+
if (process.stdout.isTTY) {
|
|
22
|
+
process.stdout.write(`\x1b]0;${clean}\x07`)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function clearTerminalTitle(): void {
|
|
27
|
+
if (process.stdout.isTTY) {
|
|
28
|
+
process.stdout.write('\x1b]0;\x07')
|
|
29
|
+
}
|
|
30
|
+
}
|