@clawpump/claw-agent 0.1.8 → 0.1.9

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 (60) hide show
  1. package/agent/.mailmap +4 -0
  2. package/agent/apps/desktop/README.md +3 -3
  3. package/agent/apps/desktop/assets/icon.icns +0 -0
  4. package/agent/apps/desktop/assets/icon.ico +0 -0
  5. package/agent/apps/desktop/assets/icon.png +0 -0
  6. package/agent/apps/desktop/electron/backend-ready.cjs +2 -2
  7. package/agent/apps/desktop/electron/dashboard-token.cjs +3 -3
  8. package/agent/apps/desktop/electron/hardening.cjs +1 -1
  9. package/agent/apps/desktop/electron/main.cjs +65 -65
  10. package/agent/apps/desktop/index.html +1 -1
  11. package/agent/apps/desktop/package.json +11 -11
  12. package/agent/apps/desktop/public/apple-touch-icon.png +0 -0
  13. package/agent/apps/desktop/public/claw-mark.png +0 -0
  14. package/agent/apps/desktop/scripts/set-exe-identity.cjs +2 -2
  15. package/agent/apps/desktop/src/app/chat/composer/controls.tsx +2 -0
  16. package/agent/apps/desktop/src/app/chat/composer/index.tsx +10 -0
  17. package/agent/apps/desktop/src/app/chat/composer/pod-credits.tsx +49 -0
  18. package/agent/apps/desktop/src/app/chat/index.tsx +1 -1
  19. package/agent/apps/desktop/src/app/chat/sidebar/index.tsx +4 -2
  20. package/agent/apps/desktop/src/app/desktop-controller.tsx +18 -0
  21. package/agent/apps/desktop/src/app/gateway/hooks/use-gateway-request.ts +1 -1
  22. package/agent/apps/desktop/src/app/messaging/index.tsx +5 -5
  23. package/agent/apps/desktop/src/app/routes.ts +9 -1
  24. package/agent/apps/desktop/src/app/session/hooks/use-message-stream.ts +3 -3
  25. package/agent/apps/desktop/src/app/settings/constants.ts +5 -5
  26. package/agent/apps/desktop/src/app/settings/model-settings.tsx +1 -1
  27. package/agent/apps/desktop/src/app/settings/providers-settings.tsx +46 -1
  28. package/agent/apps/desktop/src/app/settings/uninstall-section.tsx +5 -5
  29. package/agent/apps/desktop/src/app/types.ts +9 -1
  30. package/agent/apps/desktop/src/app/wallet/index.tsx +244 -0
  31. package/agent/apps/desktop/src/app/x402/index.tsx +162 -0
  32. package/agent/apps/desktop/src/components/assistant-ui/thread.tsx +1 -1
  33. package/agent/apps/desktop/src/components/brand-mark.tsx +2 -2
  34. package/agent/apps/desktop/src/components/chat/intro-copy.jsonl +6 -6
  35. package/agent/apps/desktop/src/components/chat/intro.tsx +4 -4
  36. package/agent/apps/desktop/src/components/model-picker.tsx +64 -4
  37. package/agent/apps/desktop/src/components/pod-setup-dialog.tsx +227 -0
  38. package/agent/apps/desktop/src/hermes.ts +109 -3
  39. package/agent/apps/desktop/src/i18n/en.ts +80 -78
  40. package/agent/apps/desktop/src/i18n/ja.ts +82 -82
  41. package/agent/apps/desktop/src/i18n/runtime.test.ts +2 -2
  42. package/agent/apps/desktop/src/i18n/zh-hant.ts +82 -82
  43. package/agent/apps/desktop/src/i18n/zh.ts +87 -87
  44. package/agent/apps/desktop/src/lib/desktop-fs.ts +1 -1
  45. package/agent/apps/desktop/src/lib/desktop-slash-commands.ts +4 -4
  46. package/agent/apps/desktop/src/store/composer.ts +7 -0
  47. package/agent/apps/desktop/src/store/onboarding.ts +5 -5
  48. package/agent/apps/desktop/src/themes/presets.ts +54 -54
  49. package/agent/cli.py +184 -10
  50. package/agent/hermes_cli/distribution.py +188 -8
  51. package/agent/hermes_cli/providers.py +29 -0
  52. package/agent/hermes_cli/web_server.py +180 -2
  53. package/agent/plugins/model-providers/usepod/__init__.py +7 -1
  54. package/agent/scripts/release.py +1 -0
  55. package/agent/web/src/components/ChatSidebar.tsx +5 -0
  56. package/agent/web/src/components/ModelPickerDialog.tsx +28 -1
  57. package/agent/web/src/components/PodCredits.tsx +57 -0
  58. package/agent/web/src/components/PodSetupDialog.tsx +240 -0
  59. package/agent/web/src/lib/api.ts +23 -0
  60. package/package.json +1 -1
@@ -3,8 +3,8 @@
3
3
  "productName": "Hermes",
4
4
  "private": true,
5
5
  "version": "0.15.1",
6
- "description": "Native desktop shell for Hermes Agent.",
7
- "author": "Nous Research",
6
+ "description": "Claw Agent by ClawPump — native desktop app for Solana agents, built on Hermes Agent by Nous Research.",
7
+ "author": "ClawPump (built on Hermes by Nous Research)",
8
8
  "type": "module",
9
9
  "main": "electron/main.cjs",
10
10
  "engines": {
@@ -146,7 +146,7 @@
146
146
  ]
147
147
  }
148
148
  ],
149
- "artifactName": "Hermes-${version}-${os}-${arch}.${ext}",
149
+ "artifactName": "Claw-Agent-${version}-${os}-${arch}.${ext}",
150
150
  "icon": "assets/icon",
151
151
  "directories": {
152
152
  "output": "release"
@@ -187,11 +187,11 @@
187
187
  "entitlements": "electron/entitlements.mac.plist",
188
188
  "entitlementsInherit": "electron/entitlements.mac.inherit.plist",
189
189
  "extendInfo": {
190
- "CFBundleDisplayName": "Hermes",
190
+ "CFBundleDisplayName": "Claw Agent",
191
191
  "CFBundleExecutable": "Hermes",
192
192
  "CFBundleName": "Hermes",
193
- "NSAudioCaptureUsageDescription": "Hermes uses audio capture for voice conversations.",
194
- "NSMicrophoneUsageDescription": "Hermes uses the microphone for voice input and voice conversations."
193
+ "NSAudioCaptureUsageDescription": "Claw Agent uses audio capture for voice conversations.",
194
+ "NSMicrophoneUsageDescription": "Claw Agent uses the microphone for voice input and voice conversations."
195
195
  },
196
196
  "gatekeeperAssess": false,
197
197
  "hardenedRuntime": true,
@@ -201,7 +201,7 @@
201
201
  ]
202
202
  },
203
203
  "dmg": {
204
- "title": "Install Hermes",
204
+ "title": "Install Claw Agent",
205
205
  "backgroundColor": "#f5f5f7",
206
206
  "iconSize": 96,
207
207
  "window": {
@@ -223,7 +223,7 @@
223
223
  ]
224
224
  },
225
225
  "win": {
226
- "legalTrademarks": "Hermes",
226
+ "legalTrademarks": "Claw Agent",
227
227
  "target": [
228
228
  "nsis",
229
229
  "msi"
@@ -233,7 +233,7 @@
233
233
  "linux": {
234
234
  "category": "Development",
235
235
  "maintainer": "Nous Research <support@nousresearch.com>",
236
- "synopsis": "Native desktop shell for Hermes Agent.",
236
+ "synopsis": "Claw Agent by ClawPump — Solana agents desktop app, built on Hermes.",
237
237
  "target": [
238
238
  "AppImage",
239
239
  "deb",
@@ -244,8 +244,8 @@
244
244
  "oneClick": false,
245
245
  "allowToChangeInstallationDirectory": true,
246
246
  "perMachine": false,
247
- "shortcutName": "Hermes",
248
- "uninstallDisplayName": "Hermes",
247
+ "shortcutName": "Claw Agent",
248
+ "uninstallDisplayName": "Claw Agent",
249
249
  "warningsAsErrors": false
250
250
  }
251
251
  }
@@ -68,8 +68,8 @@ async function stampExeIdentity(exe, desktopRoot = path.resolve(__dirname, '..')
68
68
  await rcedit(exe, {
69
69
  icon,
70
70
  'version-string': {
71
- ProductName: 'Hermes',
72
- FileDescription: 'Hermes',
71
+ ProductName: 'Claw Agent',
72
+ FileDescription: 'Claw Agent',
73
73
  CompanyName: 'Nous Research',
74
74
  LegalCopyright: 'Copyright (c) 2026 Nous Research'
75
75
  }
@@ -10,6 +10,7 @@ import { cn } from '@/lib/utils'
10
10
 
11
11
  import type { ConversationStatus } from './hooks/use-voice-conversation'
12
12
  import { ModelPill } from './model-pill'
13
+ import { PodCredits } from './pod-credits'
13
14
  import type { ChatBarState, VoiceStatus } from './types'
14
15
 
15
16
  export const ICON_BTN = 'size-(--composer-control-size) shrink-0 rounded-md'
@@ -83,6 +84,7 @@ export function ComposerControls({
83
84
 
84
85
  return (
85
86
  <div className="ml-auto flex shrink-0 items-center gap-(--composer-control-gap)">
87
+ <PodCredits provider={state.model.provider} />
86
88
  <ModelPill disabled={disabled} model={state.model} />
87
89
  {/* While the agent runs and the user is typing, steer takes over the mic's
88
90
  slot rather than crowding the row with an extra button. */}
@@ -27,6 +27,7 @@ import { triggerHaptic } from '@/lib/haptics'
27
27
  import { cn } from '@/lib/utils'
28
28
  import {
29
29
  $composerAttachments,
30
+ $pendingChatPrompt,
30
31
  clearComposerAttachments,
31
32
  clearSessionDraft,
32
33
  type ComposerAttachment,
@@ -1228,6 +1229,15 @@ export function ChatBar({
1228
1229
  const { attachments, text } = takeSessionDraft(activeQueueSessionKey)
1229
1230
  loadIntoComposer(text, attachments)
1230
1231
 
1232
+ // One-shot prompt from a "send to chat" flow (x402 "Use in chat", wallet
1233
+ // "Tokenize") that navigated to a fresh chat. Drain it AFTER the session
1234
+ // draft load so it isn't overwritten; append if there's already a draft.
1235
+ const pending = $pendingChatPrompt.get()
1236
+ if (pending) {
1237
+ $pendingChatPrompt.set(null)
1238
+ loadIntoComposer(text.trim() ? `${text.trimEnd()}\n\n${pending}` : pending, attachments)
1239
+ }
1240
+
1231
1241
  return () => {
1232
1242
  const editing = queueEditRef.current
1233
1243
 
@@ -0,0 +1,49 @@
1
+ import { useQuery } from '@tanstack/react-query'
2
+
3
+ import { getPodStatus } from '@/hermes'
4
+ import { Tip } from '@/components/ui/tooltip'
5
+
6
+ /**
7
+ * Compact "Pod $X.XX" credits pill, shown in the composer control row only when
8
+ * the active provider is UsePod (Pod). Reads the live pod balance from
9
+ * /api/clawpump/pod/status. Click-through is intentionally none — it's a glance
10
+ * indicator; top-ups go through the Set up Pod flow.
11
+ */
12
+ export function PodCredits({ provider }: { provider: string }) {
13
+ const isPod = provider === 'usepod'
14
+ const status = useQuery({
15
+ queryKey: ['pod-status'],
16
+ queryFn: getPodStatus,
17
+ enabled: isPod,
18
+ staleTime: 30_000,
19
+ refetchInterval: 60_000,
20
+ // Keep the last result while a refetch is in flight so the credits readout
21
+ // never flickers from "$0.94" back to a bare "Pod".
22
+ placeholderData: prev => prev
23
+ })
24
+
25
+ if (!isPod || !status.data?.connected) {
26
+ return null
27
+ }
28
+
29
+ const bal = status.data.balance_usdc
30
+ const low = bal != null && bal < 0.5
31
+ // bal is null only before the very first successful fetch (the backend caches
32
+ // the last good value thereafter) — show a loading dash, not a bare "Pod".
33
+ const text = bal != null ? `$${bal.toFixed(2)}` : '$…'
34
+
35
+ return (
36
+ <Tip label={bal != null ? `Pod credits: $${bal.toFixed(4)} USDC` : 'Pod connected — fetching balance…'}>
37
+ <span
38
+ className={`inline-flex shrink-0 items-center gap-1 rounded-md border px-1.5 py-0.5 text-[0.6875rem] font-medium ${
39
+ low
40
+ ? 'border-amber-500/40 bg-amber-500/10 text-amber-400'
41
+ : 'border-emerald-500/30 bg-emerald-500/10 text-emerald-400'
42
+ }`}
43
+ >
44
+ <img alt="" className="size-3 rounded-sm" src="/claw-mark.png" />
45
+ {text}
46
+ </span>
47
+ </Tip>
48
+ )
49
+ }
@@ -349,7 +349,7 @@ export function ChatView({
349
349
  }
350
350
 
351
351
  if (!gateway) {
352
- throw new Error('Hermes gateway unavailable')
352
+ throw new Error('Claw Agent gateway unavailable')
353
353
  }
354
354
 
355
355
  return gateway.request<ModelOptionsResponse>('model.options', { session_id: activeSessionId })
@@ -95,7 +95,7 @@ import {
95
95
  sessionPinId
96
96
  } from '@/store/session'
97
97
 
98
- import { type AppView, ARTIFACTS_ROUTE, MESSAGING_ROUTE, SKILLS_ROUTE } from '../../routes'
98
+ import { type AppView, ARTIFACTS_ROUTE, MESSAGING_ROUTE, SKILLS_ROUTE, WALLET_ROUTE, X402_ROUTE } from '../../routes'
99
99
  import { SidebarPanelLabel } from '../../shell/sidebar-label'
100
100
  import type { SidebarNavItem } from '../../types'
101
101
 
@@ -131,7 +131,9 @@ const SIDEBAR_NAV: SidebarNavItem[] = [
131
131
  route: SKILLS_ROUTE
132
132
  },
133
133
  { id: 'messaging', label: '', icon: props => <Codicon name="comment" {...props} />, route: MESSAGING_ROUTE },
134
- { id: 'artifacts', label: '', icon: props => <Codicon name="files" {...props} />, route: ARTIFACTS_ROUTE }
134
+ { id: 'artifacts', label: '', icon: props => <Codicon name="files" {...props} />, route: ARTIFACTS_ROUTE },
135
+ { id: 'wallet', label: 'Wallet', icon: props => <Codicon name="credit-card" {...props} />, route: WALLET_ROUTE },
136
+ { id: 'x402', label: 'x402', icon: props => <Codicon name="zap" {...props} />, route: X402_ROUTE }
135
137
  ]
136
138
 
137
139
  const WORKSPACE_PAGE = 5
@@ -137,6 +137,8 @@ const MessagingView = lazy(async () => ({ default: (await import('./messaging'))
137
137
  const ProfilesView = lazy(async () => ({ default: (await import('./profiles')).ProfilesView }))
138
138
  const SettingsView = lazy(async () => ({ default: (await import('./settings')).SettingsView }))
139
139
  const SkillsView = lazy(async () => ({ default: (await import('./skills')).SkillsView }))
140
+ const WalletView = lazy(async () => ({ default: (await import('./wallet')).WalletView }))
141
+ const X402View = lazy(async () => ({ default: (await import('./x402')).X402View }))
140
142
 
141
143
  // Latest cron-job sessions surfaced in the collapsed "Cron jobs" section. The
142
144
  // Cron sessions are written by a background scheduler tick (the desktop
@@ -1185,6 +1187,22 @@ export function DesktopController() {
1185
1187
  }
1186
1188
  path="artifacts"
1187
1189
  />
1190
+ <Route
1191
+ element={
1192
+ <Suspense fallback={null}>
1193
+ <WalletView setStatusbarItemGroup={setStatusbarItemGroup} />
1194
+ </Suspense>
1195
+ }
1196
+ path="wallet"
1197
+ />
1198
+ <Route
1199
+ element={
1200
+ <Suspense fallback={null}>
1201
+ <X402View setStatusbarItemGroup={setStatusbarItemGroup} />
1202
+ </Suspense>
1203
+ }
1204
+ path="x402"
1205
+ />
1188
1206
  <Route element={null} path="cron" />
1189
1207
  <Route element={null} path="profiles" />
1190
1208
  <Route element={null} path="settings" />
@@ -98,7 +98,7 @@ export function useGatewayRequest() {
98
98
  const gateway = gatewayRef.current
99
99
 
100
100
  if (!gateway) {
101
- throw new Error('Hermes gateway unavailable')
101
+ throw new Error('Claw Agent gateway unavailable')
102
102
  }
103
103
 
104
104
  try {
@@ -514,11 +514,11 @@ const PLATFORM_INTRO: Record<string, string> = {
514
514
  'On your Mattermost server, create a bot account or personal access token, then paste the server URL and token here.',
515
515
  matrix: 'Sign in to your homeserver with the bot account, then copy the access token, user ID, and homeserver URL.',
516
516
  signal:
517
- 'Run a signal-cli REST bridge somewhere reachable, then point Hermes at the URL and the registered phone number.',
517
+ 'Run a signal-cli REST bridge somewhere reachable, then point Claw Agent at the URL and the registered phone number.',
518
518
  whatsapp:
519
- 'Start the WhatsApp bridge that ships with Hermes, scan the QR code on first run, then enable the platform.',
519
+ 'Start the WhatsApp bridge that ships with Claw Agent, scan the QR code on first run, then enable the platform.',
520
520
  bluebubbles:
521
- 'Run BlueBubbles Server on a Mac with iMessage, expose its API, then point Hermes at the URL with the server password.',
521
+ 'Run BlueBubbles Server on a Mac with iMessage, expose its API, then point Claw Agent at the URL with the server password.',
522
522
  homeassistant:
523
523
  'In Home Assistant, open your profile and create a long-lived access token. Paste it here along with your HA URL.',
524
524
  email:
@@ -532,10 +532,10 @@ const PLATFORM_INTRO: Record<string, string> = {
532
532
  wecom_callback:
533
533
  'Set up a WeCom self-built app, expose its callback URL, and provide the corp ID, secret, agent ID, and AES key.',
534
534
  weixin:
535
- 'Run `hermes gateway setup`, select Weixin, then scan and confirm the QR code with a personal WeChat account. Hermes connects through Tencent\'s iLink Bot API and saves the credentials.',
535
+ 'Run `hermes gateway setup`, select Weixin, then scan and confirm the QR code with a personal WeChat account. Claw Agent connects through Tencent\'s iLink Bot API and saves the credentials.',
536
536
  qqbot: 'Register an app on the QQ Open Platform (q.qq.com) and copy the App ID and Client Secret.',
537
537
  api_server:
538
- 'Expose Hermes as an OpenAI-compatible API. Set an auth key, then point Open WebUI / LobeChat / etc. at the host:port.',
538
+ 'Expose Claw Agent as an OpenAI-compatible API. Set an auth key, then point Open WebUI / LobeChat / etc. at the host:port.',
539
539
  webhook:
540
540
  'Run an HTTP server that other tools (GitHub, GitLab, custom apps) can POST to. Use the secret to verify signatures.'
541
541
  }
@@ -8,6 +8,8 @@ export const ARTIFACTS_ROUTE = '/artifacts'
8
8
  export const CRON_ROUTE = '/cron'
9
9
  export const PROFILES_ROUTE = '/profiles'
10
10
  export const AGENTS_ROUTE = '/agents'
11
+ export const WALLET_ROUTE = '/wallet'
12
+ export const X402_ROUTE = '/x402'
11
13
 
12
14
  export type AppView =
13
15
  | 'agents'
@@ -19,6 +21,8 @@ export type AppView =
19
21
  | 'profiles'
20
22
  | 'settings'
21
23
  | 'skills'
24
+ | 'wallet'
25
+ | 'x402'
22
26
 
23
27
  export type AppRouteId =
24
28
  | 'agents'
@@ -30,6 +34,8 @@ export type AppRouteId =
30
34
  | 'profiles'
31
35
  | 'settings'
32
36
  | 'skills'
37
+ | 'wallet'
38
+ | 'x402'
33
39
 
34
40
  export interface AppRoute {
35
41
  id: AppRouteId
@@ -46,7 +52,9 @@ export const APP_ROUTES = [
46
52
  { id: 'artifacts', path: ARTIFACTS_ROUTE, view: 'artifacts' },
47
53
  { id: 'cron', path: CRON_ROUTE, view: 'cron' },
48
54
  { id: 'profiles', path: PROFILES_ROUTE, view: 'profiles' },
49
- { id: 'agents', path: AGENTS_ROUTE, view: 'agents' }
55
+ { id: 'agents', path: AGENTS_ROUTE, view: 'agents' },
56
+ { id: 'wallet', path: WALLET_ROUTE, view: 'wallet' },
57
+ { id: 'x402', path: X402_ROUTE, view: 'x402' }
50
58
  ] as const satisfies readonly AppRoute[]
51
59
 
52
60
  const APP_VIEW_BY_PATH = new Map<string, AppView>(APP_ROUTES.map(route => [route.path, route.view]))
@@ -671,7 +671,7 @@ export function useMessageStream({
671
671
  const streamId = state.streamId ?? `assistant-error-${Date.now()}`
672
672
  const groupId = state.pendingBranchGroup ?? undefined
673
673
  const prev = state.messages
674
- const error = errorMessage.trim() || 'Hermes reported an error'
674
+ const error = errorMessage.trim() || 'Claw Agent reported an error'
675
675
 
676
676
  const nextMessages = prev.some(m => m.id === streamId)
677
677
  ? prev.map(message =>
@@ -1108,7 +1108,7 @@ export function useMessageStream({
1108
1108
  }))
1109
1109
  }
1110
1110
  } else if (event.type === 'error') {
1111
- const errorMessage = payload?.message || 'Hermes reported an error'
1111
+ const errorMessage = payload?.message || 'Claw Agent reported an error'
1112
1112
  const looksLikeProviderSetup = isProviderSetupErrorMessage(errorMessage)
1113
1113
 
1114
1114
  // A turn that errors out has also ended — drop any open blocking prompt
@@ -1137,7 +1137,7 @@ export function useMessageStream({
1137
1137
  notify({
1138
1138
  id: `gateway-error:${errorMessage}`,
1139
1139
  kind: 'error',
1140
- title: 'Hermes error',
1140
+ title: 'Claw Agent error',
1141
1141
  message: errorMessage
1142
1142
  })
1143
1143
  }
@@ -435,10 +435,10 @@ export const FIELD_DESCRIPTIONS: Record<string, string> = defineFieldCopy({
435
435
  personality: 'Default assistant style for new sessions.',
436
436
  showReasoning: 'Show reasoning sections when the backend provides them.'
437
437
  },
438
- timezone: 'Used when Hermes needs local time context. Blank uses the system timezone.',
438
+ timezone: 'Used when Claw Agent needs local time context. Blank uses the system timezone.',
439
439
  agent: {
440
440
  imageInputMode: 'Controls how image attachments are sent to the model.',
441
- maxTurns: 'Upper bound for tool-calling turns before Hermes stops a run.'
441
+ maxTurns: 'Upper bound for tool-calling turns before Claw Agent stops a run.'
442
442
  },
443
443
  terminal: {
444
444
  cwd: 'Default project folder for tool and terminal work.',
@@ -452,9 +452,9 @@ export const FIELD_DESCRIPTIONS: Record<string, string> = defineFieldCopy({
452
452
  codeExecution: {
453
453
  mode: 'How strictly code execution is scoped to the current project.'
454
454
  },
455
- fileReadMaxChars: 'Maximum characters Hermes can read from one file request.',
455
+ fileReadMaxChars: 'Maximum characters Claw Agent can read from one file request.',
456
456
  approvals: {
457
- mode: 'How Hermes handles commands that need explicit approval.',
457
+ mode: 'How Claw Agent handles commands that need explicit approval.',
458
458
  timeout: 'How long approval prompts wait before timing out.'
459
459
  },
460
460
  security: {
@@ -493,7 +493,7 @@ export const FIELD_DESCRIPTIONS: Record<string, string> = defineFieldCopy({
493
493
  },
494
494
  updates: {
495
495
  nonInteractiveLocalChanges:
496
- 'When Hermes updates itself from the app (no terminal prompt), keep local source edits (stash) or throw them away (discard). Terminal updates always ask.'
496
+ 'When Claw Agent updates itself from the app (no terminal prompt), keep local source edits (stash) or throw them away (discard). Terminal updates always ask.'
497
497
  }
498
498
  })
499
499
 
@@ -489,7 +489,7 @@ export function ModelSettings({ onMainModelChanged }: ModelSettingsProps) {
489
489
  <p className="mt-2 text-xs text-muted-foreground">
490
490
  {selectedProviderRow?.auth_type === 'api_key'
491
491
  ? `${selectedProviderRow?.name} needs an API key — set it up to choose a model.`
492
- : `${selectedProviderRow?.name} signs in through your browser — Hermes runs the flow for you.`}
492
+ : `${selectedProviderRow?.name} signs in through your browser — Claw Agent runs the flow for you.`}
493
493
  </p>
494
494
  )}
495
495
  {config && mainModel && (reasoningSupported || fastSupported) && (
@@ -1,4 +1,5 @@
1
1
  import { useStore } from '@nanostores/react'
2
+ import { useQuery } from '@tanstack/react-query'
2
3
  import type { ReactNode } from 'react'
3
4
  import { useCallback, useEffect, useMemo, useState } from 'react'
4
5
 
@@ -11,9 +12,10 @@ import {
11
12
  providerTitle,
12
13
  sortProviders
13
14
  } from '@/components/desktop-onboarding-overlay'
15
+ import { PodSetupDialog } from '@/components/pod-setup-dialog'
14
16
  import { Button } from '@/components/ui/button'
15
17
  import { SearchField } from '@/components/ui/search-field'
16
- import { disconnectOAuthProvider, listOAuthProviders } from '@/hermes'
18
+ import { disconnectOAuthProvider, getPodStatus, listOAuthProviders } from '@/hermes'
17
19
  import { useI18n } from '@/i18n'
18
20
  import { Check, ChevronDown, ChevronRight, KeyRound, Loader2, Terminal, Trash2 } from '@/lib/icons'
19
21
  import { cn } from '@/lib/utils'
@@ -299,6 +301,12 @@ export function ProvidersSettings({ onClose, onViewChange, view }: ProvidersSett
299
301
  const { rowProps, vars } = useEnvCredentials()
300
302
  const [oauthProviders, setOauthProviders] = useState<OAuthProvider[]>([])
301
303
  const [openProvider, setOpenProvider] = useState<null | string>(null)
304
+ // ClawPump: "Pod" is set up by funding it from an agent wallet, not an OAuth
305
+ // sign-in — so it gets a promoted card in Accounts that opens the fund dialog.
306
+ const [podOpen, setPodOpen] = useState(false)
307
+ const podStatus = useQuery({ queryKey: ['pod-status'], queryFn: getPodStatus, staleTime: 30_000 })
308
+ const podConnected = podStatus.data?.connected ?? false
309
+ const podBalance = podStatus.data?.balance_usdc
302
310
  const [disconnecting, setDisconnecting] = useState<null | string>(null)
303
311
  // Free-text filter for the API-keys view (provider name / env-var key / desc).
304
312
  const [keyQuery, setKeyQuery] = useState('')
@@ -443,6 +451,35 @@ export function ProvidersSettings({ onClose, onViewChange, view }: ProvidersSett
443
451
 
444
452
  return (
445
453
  <SettingsContent>
454
+ <button
455
+ className="mb-4 flex w-full items-center gap-3 rounded-lg border border-border bg-card px-3 py-2.5 text-left transition-colors hover:border-primary/50"
456
+ onClick={() => setPodOpen(true)}
457
+ type="button"
458
+ >
459
+ <span className="grid size-9 shrink-0 place-items-center rounded-md bg-primary/15">
460
+ <img alt="" className="size-5 rounded-sm" src="/claw-mark.png" />
461
+ </span>
462
+ <span className="flex min-w-0 flex-1 flex-col">
463
+ <span className="text-sm font-semibold">Pod</span>
464
+ <span className="truncate text-xs text-muted-foreground">
465
+ {podConnected
466
+ ? podBalance != null
467
+ ? `Connected · $${podBalance.toFixed(2)} USDC left · click to top up`
468
+ : 'Connected — your inference provider'
469
+ : 'Pay-as-you-go inference — fund from your ClawPump wallet'}
470
+ </span>
471
+ </span>
472
+ {podConnected ? (
473
+ <span className="shrink-0 rounded-md border border-emerald-500/40 px-2 py-1 text-[0.62rem] font-medium uppercase tracking-wide text-emerald-400">
474
+ ✓ Connected
475
+ </span>
476
+ ) : (
477
+ <span className="shrink-0 rounded-md border border-primary/40 px-2 py-1 text-[0.62rem] font-medium uppercase tracking-wide text-primary">
478
+ Set up
479
+ </span>
480
+ )}
481
+ </button>
482
+
446
483
  <OAuthPicker
447
484
  disconnecting={disconnecting}
448
485
  onDisconnect={provider => void handleDisconnect(provider)}
@@ -450,6 +487,14 @@ export function ProvidersSettings({ onClose, onViewChange, view }: ProvidersSett
450
487
  onWantApiKey={() => onViewChange('keys')}
451
488
  providers={oauthProviders}
452
489
  />
490
+
491
+ <PodSetupDialog
492
+ onOpenChange={setPodOpen}
493
+ // The dialog shows its own "Pod ready" view + toast and its Done button
494
+ // closes it; nothing extra to do here.
495
+ onProvisioned={() => undefined}
496
+ open={podOpen}
497
+ />
453
498
  </SettingsContent>
454
499
  )
455
500
  }
@@ -21,22 +21,22 @@ const OPTIONS: ModeOption[] = [
21
21
  {
22
22
  mode: 'gui',
23
23
  title: 'Uninstall Chat GUI only',
24
- description: 'Remove this desktop app. The Hermes agent, your config, and chats all stay.',
24
+ description: 'Remove this desktop app. The Claw Agent, your config, and chats all stay.',
25
25
  consequence: 'the desktop Chat GUI (this app and its data)',
26
26
  needsAgent: false
27
27
  },
28
28
  {
29
29
  mode: 'lite',
30
30
  title: 'Uninstall GUI + agent, keep my data',
31
- description: 'Remove the app and the Hermes agent, but keep config, chats, and secrets for a future reinstall.',
32
- consequence: 'the Chat GUI and the Hermes agent (config, chats, and secrets are kept)',
31
+ description: 'Remove the app and the Claw Agent, but keep config, chats, and secrets for a future reinstall.',
32
+ consequence: 'the Chat GUI and the Claw Agent (config, chats, and secrets are kept)',
33
33
  needsAgent: true
34
34
  },
35
35
  {
36
36
  mode: 'full',
37
37
  title: 'Uninstall everything',
38
38
  description: 'Remove the app, the agent, and all user data — config, chats, scheduled jobs, secrets, logs.',
39
- consequence: 'EVERYTHING — the Chat GUI, the Hermes agent, and all of your config, chats, secrets, and logs',
39
+ consequence: 'EVERYTHING — the Chat GUI, the Claw Agent, and all of your config, chats, secrets, and logs',
40
40
  // full removes the agent (and user data), so it's an agent-removing option:
41
41
  // hide it on a lite client with no local agent, same as lite. A lite client
42
42
  // connecting to a remote backend has no local agent OR local user data the
@@ -151,7 +151,7 @@ export function UninstallSection() {
151
151
  </div>
152
152
  ) : (
153
153
  <div className="flex flex-col gap-2">
154
- <p className="text-sm font-medium">Uninstall Hermes</p>
154
+ <p className="text-sm font-medium">Uninstall Claw Agent</p>
155
155
  <p className="text-xs text-muted-foreground">
156
156
  Choose how much to remove. The app closes to finish the job; reopen the installer any time to come back.
157
157
  </p>
@@ -122,7 +122,15 @@ export type CommandDispatchResponse =
122
122
  | SendCommandDispatchResponse
123
123
  | PrefillCommandDispatchResponse
124
124
 
125
- export type SidebarNavId = 'artifacts' | 'command-center' | 'messaging' | 'new-session' | 'settings' | 'skills'
125
+ export type SidebarNavId =
126
+ | 'artifacts'
127
+ | 'command-center'
128
+ | 'messaging'
129
+ | 'new-session'
130
+ | 'settings'
131
+ | 'skills'
132
+ | 'wallet'
133
+ | 'x402'
126
134
 
127
135
  export interface SidebarNavItem {
128
136
  id: SidebarNavId