@clawpump/claw-agent 0.1.4 → 0.1.5

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/agent/cli.py CHANGED
@@ -3347,6 +3347,9 @@ class HermesCLI:
3347
3347
  # _handle_update_command() so the relaunch happens on the main thread,
3348
3348
  # not the background process_loop thread.
3349
3349
  self._pending_relaunch: list[str] | None = None
3350
+ # ClawPump: queued UsePod ("Pod") provider switch, applied post-turn by
3351
+ # _activate_pending_pod() after usepod_provision auto-applies its token.
3352
+ self._pending_pod_activation: dict | None = None
3350
3353
  self._last_ctrl_c_time = 0
3351
3354
  self._clarify_state = None
3352
3355
  self._clarify_freetext = False
@@ -5141,7 +5144,10 @@ class HermesCLI:
5141
5144
  skip_memory=self.ignore_rules,
5142
5145
  tool_progress_callback=self._on_tool_progress,
5143
5146
  tool_start_callback=self._on_tool_start if self._inline_diffs_enabled else None,
5144
- tool_complete_callback=self._on_tool_complete if self._inline_diffs_enabled else None,
5147
+ # Always wired: _on_tool_complete also drives ClawPump's UsePod
5148
+ # ("Pod") auto-apply, which must not depend on the inline-diffs
5149
+ # display toggle. Diff rendering inside it is still gated on it.
5150
+ tool_complete_callback=self._on_tool_complete,
5145
5151
  stream_delta_callback=self._stream_delta if self.streaming_enabled else None,
5146
5152
  tool_gen_callback=self._on_tool_gen_start if self.streaming_enabled else None,
5147
5153
  )
@@ -7819,6 +7825,23 @@ class HermesCLI:
7819
7825
  self._close_model_picker()
7820
7826
  return
7821
7827
  provider_data = providers[selected]
7828
+ # Distribution overlay (e.g. ClawPump): promoted providers that need
7829
+ # a guided setup (UsePod "Pod" — a plugin api-key provider that can't
7830
+ # be quick-switched) route to a step-by-step setup flow instead of a
7831
+ # failing switch. Hand the agent the configure instruction.
7832
+ if provider_data.get("clawpump_setup"):
7833
+ self._close_model_picker()
7834
+ try:
7835
+ from hermes_cli.distribution import usepod_setup_request_message
7836
+ _msg = usepod_setup_request_message()
7837
+ except Exception:
7838
+ _msg = (
7839
+ "Help me set up the Pod (UsePod) provider step by step and "
7840
+ "fund it from my ClawPump wallet."
7841
+ )
7842
+ if hasattr(self, "_pending_input"):
7843
+ self._pending_input.put(_msg)
7844
+ return
7822
7845
  # Use the curated model list from list_authenticated_providers()
7823
7846
  # (same lists as `hermes model` and gateway pickers).
7824
7847
  # Only fall back to the live provider catalog when the curated
@@ -7933,6 +7956,15 @@ class HermesCLI:
7933
7956
  except Exception:
7934
7957
  providers = []
7935
7958
 
7959
+ # Distribution overlay (e.g. ClawPump): pin promoted providers
7960
+ # (UsePod "Pod") so they're discoverable. Selecting one routes to a
7961
+ # guided setup (see _handle_model_picker_selection). No-op on vanilla.
7962
+ try:
7963
+ from hermes_cli.distribution import promote_picker_providers
7964
+ providers = promote_picker_providers(ctx, providers)
7965
+ except Exception:
7966
+ pass
7967
+
7936
7968
  if not providers:
7937
7969
  _cprint(" No authenticated providers found.")
7938
7970
  _cprint("")
@@ -11076,7 +11108,41 @@ class HermesCLI:
11076
11108
  logger.debug("Edit snapshot capture failed for %s", function_name, exc_info=True)
11077
11109
 
11078
11110
  def _on_tool_complete(self, tool_call_id: str, function_name: str, function_args: dict, function_result: str):
11079
- """Render file edits with inline diff after write-capable tools complete."""
11111
+ """Render file edits with inline diff after write-capable tools complete.
11112
+
11113
+ Also intercepts the ClawPump ``usepod_provision`` MCP tool to auto-apply
11114
+ the returned Pod token (downstream overlay; no-op on vanilla Hermes).
11115
+ Runs mid-turn in the agent thread, so it only persists credentials/config
11116
+ and queues the live provider switch — the switch itself happens at the
11117
+ post-turn boundary (``_activate_pending_pod``) to avoid mutating the
11118
+ agent while it is still executing tools.
11119
+ """
11120
+ # ClawPump: auto-apply a freshly provisioned UsePod ("Pod") token.
11121
+ try:
11122
+ from hermes_cli import distribution as _dist
11123
+
11124
+ _tok = _dist.usepod_provision_token(function_name, function_result)
11125
+ if _tok:
11126
+ api_token, deposit_code = _tok
11127
+ if _dist.persist_usepod_credentials(api_token, deposit_code):
11128
+ model, provider, base_url = _dist.usepod_pod_switch_target(
11129
+ api_token, current_model=getattr(self, "model", "") or ""
11130
+ )
11131
+ # Queue the switch; config.yaml is persisted in
11132
+ # _activate_pending_pod once we're on the main thread (so a
11133
+ # failed/invalid token never strands config on Pod).
11134
+ self._pending_pod_activation = {
11135
+ "model": model,
11136
+ "provider": provider,
11137
+ "base_url": base_url,
11138
+ "api_key": api_token,
11139
+ }
11140
+ _cprint(f" ✓ Pod token applied — switching to Pod ({model}) after this turn.")
11141
+ except Exception:
11142
+ logger.debug("UsePod auto-apply failed", exc_info=True)
11143
+
11144
+ if not getattr(self, "_inline_diffs_enabled", True):
11145
+ return
11080
11146
  snapshot = self._pending_edit_snapshots.pop(tool_call_id, None)
11081
11147
  try:
11082
11148
  from agent.display import render_edit_diff_with_delta
@@ -11091,6 +11157,68 @@ class HermesCLI:
11091
11157
  except Exception:
11092
11158
  logger.debug("Edit diff preview failed for %s", function_name, exc_info=True)
11093
11159
 
11160
+ def _activate_pending_pod(self) -> None:
11161
+ """Apply a queued UsePod ("Pod") provider switch at a post-turn boundary.
11162
+
11163
+ Mirrors ``_apply_model_switch_result``: updates the live agent + CLI
11164
+ provider state so the next turn runs on Pod, with no relaunch and no
11165
+ manual ``claw model`` paste. Set by ``_on_tool_complete`` when
11166
+ ``usepod_provision`` returns a token. No-op when nothing is queued.
11167
+ """
11168
+ pending = getattr(self, "_pending_pod_activation", None)
11169
+ if not pending:
11170
+ return
11171
+ self._pending_pod_activation = None
11172
+ try:
11173
+ old_model = self.model
11174
+ model = pending["model"]
11175
+ provider = pending["provider"]
11176
+ base_url = pending.get("base_url") or ""
11177
+ api_key = pending.get("api_key") or ""
11178
+
11179
+ self.model = model
11180
+ self.provider = provider
11181
+ self.requested_provider = provider
11182
+ self._explicit_api_key = api_key
11183
+ self._explicit_base_url = base_url
11184
+ if api_key:
11185
+ self.api_key = api_key
11186
+ if base_url:
11187
+ self.base_url = base_url
11188
+
11189
+ if self.agent is not None:
11190
+ try:
11191
+ self.agent.switch_model(
11192
+ new_model=model,
11193
+ new_provider=provider,
11194
+ api_key=api_key,
11195
+ base_url=base_url,
11196
+ )
11197
+ # switch_model derives api_mode from provider+base_url when
11198
+ # not passed; sync the CLI's copy so a later re-resolution
11199
+ # (or relaunch) doesn't carry the old provider's wire mode.
11200
+ self.api_mode = getattr(self.agent, "api_mode", self.api_mode)
11201
+ except Exception as exc:
11202
+ _cprint(f" ⚠ Pod switch applied to next session ({exc}).")
11203
+
11204
+ # Persist the provider switch (model.default + model.provider) so a
11205
+ # later relaunch boots onto Pod. base_url is intentionally NOT
11206
+ # persisted — it's re-derived from USEPOD_API_KEY at startup.
11207
+ try:
11208
+ save_config_value("model.default", model)
11209
+ save_config_value("model.provider", provider)
11210
+ except Exception:
11211
+ logger.debug("Pod config persist failed", exc_info=True)
11212
+
11213
+ self._pending_model_switch_note = (
11214
+ f"[Note: the session was just switched from {old_model} to {model} "
11215
+ f"running on your UsePod Pod (paid in USDC from your ClawPump "
11216
+ f"wallet). Adjust your self-identification accordingly.]"
11217
+ )
11218
+ _cprint(f" ✓ Now running on Pod: {model}")
11219
+ except Exception:
11220
+ logger.debug("Pod activation failed", exc_info=True)
11221
+
11094
11222
  # ====================================================================
11095
11223
  # Voice mode methods
11096
11224
  # ====================================================================
@@ -12403,6 +12531,11 @@ class HermesCLI:
12403
12531
  # Update history with full conversation
12404
12532
  self.conversation_history = result.get("messages", self.conversation_history) if result else self.conversation_history
12405
12533
 
12534
+ # ClawPump: apply a queued UsePod ("Pod") provider switch now that
12535
+ # the agent thread has joined (safe post-turn boundary). No-op unless
12536
+ # usepod_provision auto-applied a token during this turn.
12537
+ self._activate_pending_pod()
12538
+
12406
12539
  # If auto-compression fired mid-turn, the agent created a new
12407
12540
  # continuation session and mutated self.agent.session_id. Sync
12408
12541
  # the CLI's session_id so /status, /resume, title generation,
@@ -1,6 +1,6 @@
1
1
  """``hermes clawpump`` — one-command install/auth for the ClawPump MCP.
2
2
 
3
- ClawPump ships its full feature set (131 tools) as an MCP server, bundled in
3
+ ClawPump ships its full feature set (133 tools) as an MCP server, bundled in
4
4
  the Hermes catalog as two entries:
5
5
 
6
6
  - ``clawpump`` remote OAuth over Streamable HTTP (browser, paste cpk_*)
@@ -59,7 +59,7 @@ def _cmd_setup(args) -> int:
59
59
  print()
60
60
  print(color(" ClawPump setup", Colors.GREEN + Colors.BOLD))
61
61
  print(color(" ──────────────", Colors.GREEN))
62
- print(" ClawPump's 131 tools (agents, trading, perps, DCA, lending,")
62
+ print(" ClawPump's 133 tools (agents, trading, perps, DCA, lending,")
63
63
  print(" token launch, marketplace, predictions, gift cards, agent")
64
64
  print(" mail, intelligence) come in through an MCP server. Two ways")
65
65
  print(" to connect:")
@@ -221,7 +221,7 @@ def add_parser(subparsers) -> None:
221
221
  help="ClawPump (Solana token launch, trading, perps, DeFi) via MCP",
222
222
  description=(
223
223
  "Install and authenticate the ClawPump MCP server, then manage which "
224
- "of its 131 tools are enabled. Subcommands: setup (default: status), "
224
+ "of its 133 tools are enabled. Subcommands: setup (default: status), "
225
225
  "login, status, tools."
226
226
  ),
227
227
  )
@@ -123,7 +123,8 @@ COMMAND_REGISTRY: list[CommandDef] = [
123
123
  # Configuration
124
124
  CommandDef("config", "Show current configuration", "Configuration",
125
125
  cli_only=True),
126
- CommandDef("model", "Switch model for this session", "Configuration",
126
+ CommandDef("model", "Switch model or provider for this session", "Configuration",
127
+ aliases=("provider",),
127
128
  args_hint="[model] [--provider name] [--global] [--refresh]"),
128
129
  CommandDef("codex-runtime", "Toggle codex app-server runtime for OpenAI/Codex models",
129
130
  "Configuration", aliases=("codex_runtime",),
@@ -228,3 +228,230 @@ def try_self_update(project_root: Any) -> bool:
228
228
  sys.exit(1)
229
229
  print("✓ ClawPump agent updated. Restart your session with `claw`.")
230
230
  return True
231
+
232
+
233
+ # ── Model picker: promoted providers ──────────────────────────────────────
234
+ # ClawPump pins UsePod ("Pod") to the top of the /model + /provider picker so
235
+ # users can discover it. UsePod is a plugin api-key provider, so it can't be
236
+ # quick-switched like a configured provider — selecting it instead triggers a
237
+ # guided setup (the picker handler keys off the ``clawpump_setup`` flag and
238
+ # hands the agent a "configure Pod" instruction). No-op on vanilla Hermes.
239
+ PICKER_PROMOTED_PROVIDER = "usepod"
240
+ # Honest, defensible value-prop (no fabricated savings %). Swap in a real,
241
+ # backed figure here if/when there is one.
242
+ PICKER_PROMOTED_TAGLINE = "pay-per-use USDC from your ClawPump wallet"
243
+ PICKER_PROMOTED_DISPLAY = "Pod"
244
+
245
+
246
+ def promote_picker_providers(ctx: Any, providers: Any) -> Any:
247
+ """Pin the promoted provider (UsePod) to the top of the model picker.
248
+
249
+ Adds a Pod row (seeded with the provider's fallback models) tagged with
250
+ ``clawpump_setup`` so the picker routes its selection to the guided setup
251
+ flow rather than a (failing) quick-switch. Idempotent; returns the list
252
+ unchanged on any error so the picker never breaks.
253
+ """
254
+ try:
255
+ rows = list(providers or [])
256
+ except Exception:
257
+ return providers
258
+
259
+ slug = PICKER_PROMOTED_PROVIDER
260
+ existing = next(
261
+ (r for r in rows if isinstance(r, dict) and r.get("slug") == slug), None
262
+ )
263
+ others = [
264
+ r for r in rows if not (isinstance(r, dict) and r.get("slug") == slug)
265
+ ]
266
+
267
+ if existing is None:
268
+ models = []
269
+ try:
270
+ from providers import get_provider_profile
271
+
272
+ pp = get_provider_profile(slug)
273
+ models = list(getattr(pp, "fallback_models", ()) or [])
274
+ except Exception:
275
+ models = []
276
+ existing = {
277
+ "slug": slug,
278
+ "name": PICKER_PROMOTED_DISPLAY,
279
+ "models": models,
280
+ "total_models": len(models),
281
+ "is_current": False,
282
+ "authenticated": False,
283
+ }
284
+
285
+ # Selecting Pod always routes to setup (it can't be quick-switched).
286
+ existing["clawpump_setup"] = True
287
+ name = str(existing.get("name") or PICKER_PROMOTED_DISPLAY)
288
+ if PICKER_PROMOTED_TAGLINE not in name:
289
+ existing["name"] = f"{name} · {PICKER_PROMOTED_TAGLINE}"
290
+
291
+ return [existing] + others
292
+
293
+
294
+ def usepod_setup_request_message() -> str:
295
+ """The instruction enqueued to the agent when a user picks Pod in the
296
+ picker but it isn't configured — drives the step-by-step setup + funding."""
297
+ return (
298
+ "I picked Pod (UsePod) in the model picker but it isn't set up yet. "
299
+ "Walk me through configuring it: (1) ask how much USDC to put on the pod "
300
+ "(e.g. 5) — optionally check get_balance first; (2) ask which ClawPump "
301
+ "agent wallet should fund it — call list_agents and let me pick one by "
302
+ "agent_id; (3) once I approve the amount, call usepod_provision with that "
303
+ "amount, the chosen agent_id, and confirm_deposit: true. It registers a "
304
+ "fresh pod and funds it in one call. I do NOT need to paste anything: the "
305
+ "moment usepod_provision returns the api_token, Hermes auto-applies it "
306
+ "(writes USEPOD_API_KEY) and switches this session onto Pod automatically. "
307
+ "Just confirm the amount before the on-chain spend and report the funding "
308
+ "signature."
309
+ )
310
+
311
+
312
+ # ── UsePod "Pod" auto-apply (downstream) ──────────────────────────────────
313
+ # When the agent calls the ClawPump MCP tool ``usepod_provision`` it gets back
314
+ # ``{api_token, deposit_code, amount, signature, funding_error}``. Instead of
315
+ # telling the user to paste the api_token via ``claw model``, the CLI intercepts
316
+ # that result (cli.py ``_on_tool_complete``), persists the token, and switches
317
+ # the live session onto Pod. These helpers hold the downstream-owned logic so
318
+ # the cli.py touch points stay tiny and no-op on vanilla Hermes.
319
+ USEPOD_PROVISION_TOOL_SUFFIX = "usepod_provision"
320
+
321
+
322
+ def _is_clawpump_provision_tool(name: str) -> bool:
323
+ """True only for the ClawPump MCP's usepod_provision tool.
324
+
325
+ The runtime names MCP tools ``mcp_<server>_<tool>`` — the remote entry is
326
+ ``mcp_clawpump_usepod_provision`` and the stdio entry
327
+ ``mcp_clawpump_stdio_usepod_provision``; both start with ``mcp_clawpump``.
328
+ Binding to that namespace (rather than a bare ``endswith``) keeps a
329
+ differently-named MCP server from minting a result that auto-rewrites the
330
+ user's inference credential.
331
+ """
332
+ n = str(name or "")
333
+ if n == USEPOD_PROVISION_TOOL_SUFFIX:
334
+ return True
335
+ return n.startswith("mcp_clawpump") and n.endswith(USEPOD_PROVISION_TOOL_SUFFIX)
336
+
337
+
338
+ def _unwrap_provision_payload(value: Any, _depth: int = 0):
339
+ """Return the innermost dict carrying ``api_token``, unwrapping envelopes.
340
+
341
+ Hermes' MCP client wraps a tool's text result as ``{"result": "<json>"}``
342
+ (and ``{"result": ..., "structuredContent": {...}}`` when the server sends
343
+ structured output) — see ``tools/mcp_tool.py``. The provision JSON therefore
344
+ arrives nested and string-escaped, so we re-parse ``result`` /
345
+ ``structuredContent`` until we find the object with the token.
346
+ """
347
+ if _depth > 4:
348
+ return None
349
+ if isinstance(value, str):
350
+ import json
351
+
352
+ try:
353
+ value = json.loads(value)
354
+ except Exception:
355
+ return None
356
+ if not isinstance(value, dict):
357
+ return None
358
+ if value.get("api_token"):
359
+ return value
360
+ for key in ("structuredContent", "result", "data"):
361
+ if key in value:
362
+ inner = _unwrap_provision_payload(value[key], _depth + 1)
363
+ if inner is not None:
364
+ return inner
365
+ return None
366
+
367
+
368
+ def usepod_provision_token(function_name: str, function_result: Any):
369
+ """Extract ``(api_token, deposit_code)`` from a ``usepod_provision`` result.
370
+
371
+ Returns ``None`` unless *function_name* is the ClawPump MCP's
372
+ usepod_provision tool AND the result carries a usable ``api_token``.
373
+ Tolerant of the result arriving as a dict, a JSON string, or the
374
+ ``{"result": "<escaped json>"}`` envelope Hermes' MCP client emits.
375
+ """
376
+ if not _is_clawpump_provision_tool(function_name):
377
+ return None
378
+
379
+ api_token = ""
380
+ deposit_code = ""
381
+ payload = _unwrap_provision_payload(function_result)
382
+ if isinstance(payload, dict):
383
+ api_token = str(payload.get("api_token") or "").strip()
384
+ deposit_code = str(payload.get("deposit_code") or "").strip()
385
+
386
+ # Last-resort fallback: dig the fields straight out of the raw text. The
387
+ # quotes may be backslash-escaped (the JSON is a string value inside the
388
+ # ``{"result": "..."}`` envelope), so tolerate leading backslashes.
389
+ if not api_token and isinstance(function_result, str):
390
+ import re
391
+
392
+ m = re.search(r'\\*"api_token\\*"\s*:\s*\\*"([^"\\]+)', function_result)
393
+ if m:
394
+ api_token = m.group(1).strip()
395
+ m = re.search(r'\\*"deposit_code\\*"\s*:\s*\\*"([0-9a-fA-F]{16})', function_result)
396
+ if m:
397
+ deposit_code = m.group(1).strip()
398
+
399
+ if not api_token:
400
+ return None
401
+ # A funding_error means the deposit failed but the pod/token are still
402
+ # valid — applying the token is exactly right (the user funds later).
403
+ return (api_token, deposit_code)
404
+
405
+
406
+ def usepod_pod_switch_target(api_token: str, current_model: str = ""):
407
+ """Return ``(model, provider, base_url)`` to switch onto for a Pod token.
408
+
409
+ Keeps the user's current model when UsePod's offline catalog lists it,
410
+ otherwise falls back to the provider's first advertised Pod model. The
411
+ secret-bearing base_url is derived from the token (never persisted to
412
+ config.yaml).
413
+ """
414
+ provider = "usepod"
415
+ models = []
416
+ try:
417
+ from providers import get_provider_profile
418
+
419
+ pp = get_provider_profile(provider)
420
+ models = list(getattr(pp, "fallback_models", ()) or [])
421
+ except Exception:
422
+ models = []
423
+
424
+ model = ""
425
+ cur = (current_model or "").strip()
426
+ if cur and cur in models:
427
+ model = cur
428
+ elif models:
429
+ model = models[0]
430
+ else:
431
+ model = cur or "claude-opus-4-8"
432
+
433
+ base_url = ""
434
+ try:
435
+ from hermes_cli.auth import _resolve_usepod_base_url
436
+
437
+ base_url = _resolve_usepod_base_url(api_token)
438
+ except Exception:
439
+ base_url = ""
440
+
441
+ return (model, provider, base_url)
442
+
443
+
444
+ def persist_usepod_credentials(api_token: str, deposit_code: str = "") -> bool:
445
+ """Write USEPOD_API_KEY (+ deposit_code) to ~/.hermes/.env. Returns success."""
446
+ try:
447
+ from hermes_cli.config import save_env_value
448
+
449
+ save_env_value("USEPOD_API_KEY", api_token)
450
+ if deposit_code:
451
+ try:
452
+ save_env_value("USEPOD_DEPOSIT_CODE", deposit_code)
453
+ except Exception:
454
+ pass # deposit_code is a convenience; the token is what matters
455
+ return True
456
+ except Exception:
457
+ return False
@@ -3,7 +3,7 @@
3
3
  manifest_version: 1
4
4
 
5
5
  name: clawpump
6
- description: ClawPump — Solana AI agents, trading, perps, DCA, token launch, marketplace, lending, predictions, gift cards, agent mail, x402 paid APIs, market intelligence (131 tools).
6
+ description: ClawPump — Solana AI agents, trading, perps, DCA, token launch, marketplace, lending, predictions, gift cards, agent mail, x402 paid APIs, UsePod pods, market intelligence (134 tools).
7
7
  source: https://mcp.clawpump.tech
8
8
 
9
9
  # ClawPump ships a remote MCP server: Streamable HTTP + native OAuth 2.1 +
@@ -29,13 +29,13 @@ auth:
29
29
  # first probe / first connect.
30
30
 
31
31
  # Tool selection at install time.
32
- # ClawPump exposes 131 tools; 37 of them move real funds or are irreversible
32
+ # ClawPump exposes 134 tools; 38 of them move real funds or are irreversible
33
33
  # (perps orders, swaps, DCA/limit orders, lending deposit/withdraw, prediction
34
34
  # positions, marketplace bids, external-wallet changes, wallet transfers,
35
35
  # token launches, gift-card purchases/withdrawals, agent-mail
36
36
  # provisioning/sending, x402 paid-API execution).
37
37
  # Hermes does NOT auto-gate MCP tools by their destructiveHint, so we ship a
38
- # read-mostly default: the 94 safe tools below are pre-checked in the install
38
+ # read-mostly default: the 96 safe tools below are pre-checked in the install
39
39
  # checklist (and applied directly if the server can't be probed before auth).
40
40
  # Users opt into the financial tools per their threat model in the checklist,
41
41
  # or later with `hermes mcp configure clawpump`.
@@ -118,6 +118,8 @@ tools:
118
118
  - pay_sh_search
119
119
  - pay_sh_provider_details
120
120
  - pay_sh_prepare_call
121
+ # dexter x402 marketplace discovery (free search; paid with the agent wallet)
122
+ - dexter_search
121
123
  # perps (read / preview only)
122
124
  - perps_markets
123
125
  - perps_market_data
@@ -155,6 +157,12 @@ tools:
155
157
  # whitelist
156
158
  - get_whitelist
157
159
  - add_to_whitelist
160
+ # usepod / Pod (set up + fund the user's own inference pod from the wallet)
161
+ # provision is the Pod-setup entry point. Its only fund-moving path (passing
162
+ # an `amount`) is double-gated by `confirm_deposit: true`, so it's safe to
163
+ # ship enabled. The standalone top-up tool `usepod_deposit` is a pure
164
+ # fund-mover and stays opt-in with the other financial tools below.
165
+ - usepod_provision
158
166
 
159
167
  post_install: |
160
168
  On first connection Hermes opens a browser to ClawPump — log in and it
@@ -168,8 +176,8 @@ post_install: |
168
176
  dca_create, limit_order_create, jup_lend_deposit/withdraw, predictions_open/
169
177
  close, place_bid / accept_marketplace_bid, set_external_wallet,
170
178
  wallet_transfer, agent_card_create/cancel/reveal/withdraw,
171
- agent_mail_create/send, pay_sh_execute_approved, and the token-launch
172
- tools). Several require explicit confirm flags (confirmRisk,
179
+ agent_mail_create/send, pay_sh_execute_approved, usepod_deposit, and the
180
+ token-launch tools). Several require explicit confirm flags (confirmRisk,
173
181
  confirm_launch, confirm_spend, confirm_send, confirm_transfer,
174
182
  confirm_payment, …).
175
183
  Enable the ones you want and review every call:
@@ -3,7 +3,7 @@
3
3
  manifest_version: 1
4
4
 
5
5
  name: clawpump-stdio
6
- description: ClawPump (stdio) — the same 131 tools via the npm package, authed with a cpk_* API key (no browser).
6
+ description: ClawPump (stdio) — the same 134 tools via the npm package, authed with a cpk_* API key (no browser).
7
7
  source: https://www.npmjs.com/package/@clawpump/agents
8
8
 
9
9
  # Same tool surface as the `clawpump` remote entry, but run locally over stdio
@@ -31,8 +31,8 @@ auth:
31
31
  secret: false
32
32
  default: ""
33
33
 
34
- # Same read-mostly default as the remote `clawpump` entry: 94 safe tools
35
- # pre-checked; the 37 financial/irreversible tools are opt-in. See the
34
+ # Same read-mostly default as the remote `clawpump` entry: 96 safe tools
35
+ # pre-checked; the 38 financial/irreversible tools are opt-in. See the
36
36
  # `clawpump` manifest for the rationale (Hermes does not auto-gate MCP tools
37
37
  # by destructiveHint).
38
38
  tools:
@@ -114,6 +114,8 @@ tools:
114
114
  - pay_sh_search
115
115
  - pay_sh_provider_details
116
116
  - pay_sh_prepare_call
117
+ # dexter x402 marketplace discovery (free search; paid with the agent wallet)
118
+ - dexter_search
117
119
  # perps (read / preview only)
118
120
  - perps_markets
119
121
  - perps_market_data
@@ -151,12 +153,18 @@ tools:
151
153
  # whitelist
152
154
  - get_whitelist
153
155
  - add_to_whitelist
156
+ # usepod / Pod (set up + fund the user's own inference pod from the wallet).
157
+ # provision is the Pod-setup entry point; its fund-moving path (an `amount`)
158
+ # is double-gated by `confirm_deposit: true`. The standalone top-up tool
159
+ # `usepod_deposit` is a pure fund-mover and stays opt-in (financial) below.
160
+ - usepod_provision
154
161
 
155
162
  post_install: |
156
163
  Requires Node.js / npx on PATH. Your cpk_* key is stored in ~/.hermes/.env
157
164
  and sent directly as a Bearer token to the ClawPump backend — no browser.
158
165
 
159
- Financial / irreversible tools are OFF by default. Enable the ones you want:
166
+ Financial / irreversible tools (incl. usepod_deposit) are OFF by default.
167
+ Enable the ones you want:
160
168
  hermes mcp configure clawpump-stdio
161
169
 
162
170
  Start a new Hermes session to load the ClawPump tools.
@@ -14,7 +14,7 @@ metadata:
14
14
  ClawPump is a Solana platform for AI agents: each agent has a wallet and can
15
15
  trade, run perps, DCA, lend, launch tokens, buy gift cards, send email from
16
16
  its own inbox, and be bought/sold on a marketplace. Its full feature set
17
- reaches Hermes through the **ClawPump MCP server** (131 tools, 10 resources,
17
+ reaches Hermes through the **ClawPump MCP server** (134 tools, 10 resources,
18
18
  10 prompts).
19
19
 
20
20
  ## Enabling ClawPump
@@ -44,7 +44,7 @@ Most tools act on a specific agent. Resolve it once with `list_agents`, then
44
44
  pass `agent_id`. If the user set `CLAWPUMP_DEFAULT_AGENT` (or has exactly one
45
45
  agent), `agent_id` can usually be omitted. When unsure which agent, ask.
46
46
 
47
- ## What's available (131 tools, 20 groups)
47
+ ## What's available (134 tools, 21 groups)
48
48
 
49
49
  | Group | Representative tools |
50
50
  |-------|----------------------|
@@ -62,7 +62,8 @@ agent), `agent_id` can usually be omitted. When unsure which agent, ask.
62
62
  | Agent cards (Laso) | `agent_card_search_merchants`, `agent_card_search_gift_cards`, `agent_card_quote`, `agent_card_create`, `agent_card_list`, `agent_card_get`, `agent_card_status`, `agent_card_data`, `agent_card_balance`, `agent_card_reveal`, `agent_card_cancel`, `agent_card_refresh`, `agent_card_withdraw`, `agent_card_withdrawals`, `agent_card_connect`, `agent_card_connect_link` |
63
63
  | Agent mail | `agent_mail_create`, `agent_mail_get_address`, `agent_mail_send`, `agent_mail_list`, `agent_mail_read` |
64
64
  | Pay.sh x402 (agent-wallet paid APIs) | `pay_sh_search`, `pay_sh_provider_details`, `pay_sh_prepare_call`, `pay_sh_execute_approved` |
65
- | UsePod (pay inference from the wallet) | `usepod_deposit` |
65
+ | Dexter x402 marketplace (discovery) | `dexter_search` |
66
+ | UsePod (run + fund your own inference pod) | `usepod_provision`, `usepod_deposit` |
66
67
  | Wallet & billing | `get_balance`, `get_budget`, `get_usage`, `get_transactions`, `get_wallet_summaries`, `get_wallet_history`, `get_private_wallet_balance`, `get_balance_history`, `sync_billing`, `wallet_transfer` |
67
68
  | Market intelligence | `intelligence_capabilities`, `intelligence_market`, `intelligence_signals`, `intelligence_macro`, `intelligence_perps` |
68
69
  | Integrations | `list_integrations`, `save_integration`, `remove_integration`, `get_linked_accounts` |
@@ -70,8 +71,10 @@ agent), `agent_id` can usually be omitted. When unsure which agent, ask.
70
71
  | Whitelist | `get_whitelist`, `add_to_whitelist`, `remove_from_whitelist` |
71
72
  | Utility | `get_model_catalog`, `get_news_feed` |
72
73
 
73
- By default only the **94 read-mostly** tools are enabled. The financial ones
74
- below are opt-in via `hermes mcp configure clawpump`.
74
+ By default only the **95 read-mostly** tools are enabled (including
75
+ `usepod_provision`, whose only fund-moving path is double-gated by
76
+ `confirm_deposit`). The financial ones below are opt-in via
77
+ `hermes mcp configure clawpump`.
75
78
 
76
79
  ## ⚠️ FINANCIAL & IRREVERSIBLE TOOLS — confirmation is mandatory
77
80
 
@@ -138,4 +141,47 @@ Common patterns:
138
141
  - **Deliverability (avoid spam):** write genuine, complete messages — a specific subject, a greeting, 2–4 sentences of real purpose, and a sign-off. Never send one-word/empty bodies or test-like "hello": low-content mail from a fresh sender domain is the #1 spam trigger. Do **not** use fake-urgent or deceptive subjects ("IMPORTANT!!", "URGENT") to game filters — that backfires and is deceptive. Tell first-time recipients to mark "Not spam" + add the address to contacts; that trains their inbox fastest.
139
142
  - **Fund an external wallet (e.g. a pay.sh allowance):** `get_balance` → `add_to_whitelist` (user-approved address) → (confirm) `wallet_transfer`. See the `pay-sh` skill.
140
143
  - **Paid x402 API call (agent wallet pays):** `pay_sh_search` → `pay_sh_provider_details` (price) → `pay_sh_prepare_call` → (confirm price with user) → `pay_sh_execute_approved`. The agent needs the `x402` skill (`update_agent` with `enabled_skills`).
144
+ - **Discover x402 APIs on Dexter:** `dexter_search` (free, by capability — e.g. "ETH price", "image generation") returns matching resources with their payable `resourceUrl`, HTTP method, per-call USDC price, and input schema. To use one, call its `resourceUrl` with the agent's x402 rail (same custodial wallet, capped per call) — quote the price and confirm with the user first. No separate Dexter wallet.
141
145
  - **Pay your Hermes inference from the ClawPump wallet (UsePod):** if the user runs Hermes on their own UsePod pod (`USEPOD_API_KEY` in `~/.hermes/.env`), they can top that pod up straight from their ClawPump wallet instead of funding it manually at usepod.ai. Flow: `get_balance` (confirm the agent wallet holds enough USDC) → ask for the amount + their 16-hex `deposit_code` (from UsePod `/v1/register`) → quote both back → (confirm) `usepod_deposit` with `confirm_deposit: true`. The full amount goes on-chain into that pod (no fee); UsePod then draws inference cost from it. This is **separate** from ClawPump's own hosted chat/credits — it just pays the user's own pod from their wallet.
146
+
147
+ ## Setting up Pod (UsePod) — auto flow
148
+
149
+ When a user picks **Pod** in the `/provider` (or `/model`) picker and it isn't
150
+ configured, the picker hands you a request to set it up. Pod is a drop-in
151
+ inference provider whose usage is paid in USDC from the user's ClawPump wallet.
152
+ **Make this as automatic as possible — there is nothing to paste and nothing to
153
+ restart: the CLI auto-applies the token and switches the session onto Pod the
154
+ moment `usepod_provision` returns.**
155
+
156
+ 1. **Ask the amount + which agent wallet.** "How much USDC do you want to put on
157
+ your Pod? (e.g. 5)" — optionally check `get_balance` first so you know it can
158
+ cover it. Then "Which ClawPump agent wallet should fund it?" — call
159
+ `list_agents` and let the user pick one; use its `agent_id`. (If there is only
160
+ one agent you may skip the question and omit `agent_id`.)
161
+ 2. **Create + fund in one call** — `usepod_provision` registers a brand-new pod
162
+ (no signup, no human step) and funds it from the chosen agent wallet. After
163
+ the user approves the amount, call `usepod_provision` with that `amount`, the
164
+ selected `agent_id`, and `confirm_deposit: true`. It returns `api_token` (the
165
+ pod credential) + `deposit_code` and the funding `signature`.
166
+ 3. **It's applied automatically** — in the interactive Hermes CLI, the instant
167
+ `usepod_provision` returns, Hermes writes `USEPOD_API_KEY` (+
168
+ `USEPOD_DEPOSIT_CODE`) to `~/.hermes/.env` and switches this session onto
169
+ Pod. No `claw model`, no paste, no restart. Watch for Hermes' own
170
+ confirmation line (`✓ Now running on Pod: …`) and then tell the user "Your
171
+ pod is funded and this session is now running on Pod — the next message bills
172
+ from your pod." Report the funding `signature`. Do **not** print the
173
+ `api_token` (it's a secret and is already applied). (On non-interactive
174
+ runtimes — bots/API — the credential is saved but the live switch happens on
175
+ the next start; say "restart to run on Pod" instead of claiming it switched.)
176
+
177
+ Notes:
178
+ - **Top up an existing pod** (user already set Pod up before): skip provisioning
179
+ — ask for the amount + their 16-hex `deposit_code`, then `usepod_deposit` with
180
+ `confirm_deposit: true`. (`usepod_deposit` is a fund-mover and is opt-in: if
181
+ it isn't available, the user enables it via `hermes mcp configure clawpump`.)
182
+ - `usepod_provision` registers the pod first and funds best-effort, so if funding
183
+ fails the user still gets a valid `api_token` (look for `funding_error`) — the
184
+ token is still auto-applied; fund it afterward with `usepod_deposit`.
185
+ - This is **separate** from ClawPump's hosted chat/credits — it pays the user's
186
+ own pod from their wallet. Always confirm the amount before the on-chain spend
187
+ and report the tx signature.
package/agent/uv.lock CHANGED
@@ -1602,21 +1602,25 @@ version = "0.15.1"
1602
1602
  source = { editable = "." }
1603
1603
  dependencies = [
1604
1604
  { name = "croniter" },
1605
+ { name = "fastapi" },
1605
1606
  { name = "fire" },
1606
1607
  { name = "httpx", extra = ["socks"] },
1607
1608
  { name = "jinja2" },
1608
1609
  { name = "openai" },
1609
1610
  { name = "prompt-toolkit" },
1610
1611
  { name = "psutil" },
1612
+ { name = "ptyprocess", marker = "sys_platform != 'win32'" },
1611
1613
  { name = "pydantic" },
1612
1614
  { name = "pyjwt", extra = ["crypto"] },
1613
1615
  { name = "python-dotenv" },
1616
+ { name = "pywinpty", marker = "sys_platform == 'win32'" },
1614
1617
  { name = "pyyaml" },
1615
1618
  { name = "requests" },
1616
1619
  { name = "rich" },
1617
1620
  { name = "ruamel-yaml" },
1618
1621
  { name = "tenacity" },
1619
1622
  { name = "tzdata", marker = "sys_platform == 'win32'" },
1623
+ { name = "uvicorn", extra = ["standard"] },
1620
1624
  ]
1621
1625
 
1622
1626
  [package.optional-dependencies]
@@ -1632,11 +1636,9 @@ all = [
1632
1636
  { name = "google-auth-httplib2" },
1633
1637
  { name = "google-auth-oauthlib" },
1634
1638
  { name = "mcp" },
1635
- { name = "ptyprocess", marker = "sys_platform != 'win32'" },
1636
1639
  { name = "pytest" },
1637
1640
  { name = "pytest-asyncio" },
1638
1641
  { name = "pytest-timeout" },
1639
- { name = "pywinpty", marker = "sys_platform == 'win32'" },
1640
1642
  { name = "ruff" },
1641
1643
  { name = "setuptools" },
1642
1644
  { name = "simple-term-menu" },
@@ -1739,10 +1741,6 @@ modal = [
1739
1741
  parallel-web = [
1740
1742
  { name = "parallel-web" },
1741
1743
  ]
1742
- pty = [
1743
- { name = "ptyprocess", marker = "sys_platform != 'win32'" },
1744
- { name = "pywinpty", marker = "sys_platform == 'win32'" },
1745
- ]
1746
1744
  slack = [
1747
1745
  { name = "aiohttp" },
1748
1746
  { name = "slack-bolt" },
@@ -1755,9 +1753,7 @@ termux = [
1755
1753
  { name = "agent-client-protocol" },
1756
1754
  { name = "honcho-ai" },
1757
1755
  { name = "mcp" },
1758
- { name = "ptyprocess", marker = "sys_platform != 'win32'" },
1759
1756
  { name = "python-telegram-bot", extra = ["webhooks"] },
1760
- { name = "pywinpty", marker = "sys_platform == 'win32'" },
1761
1757
  { name = "simple-term-menu" },
1762
1758
  { name = "starlette" },
1763
1759
  ]
@@ -1770,9 +1766,7 @@ termux-all = [
1770
1766
  { name = "google-auth-oauthlib" },
1771
1767
  { name = "honcho-ai" },
1772
1768
  { name = "mcp" },
1773
- { name = "ptyprocess", marker = "sys_platform != 'win32'" },
1774
1769
  { name = "python-telegram-bot", extra = ["webhooks"] },
1775
- { name = "pywinpty", marker = "sys_platform == 'win32'" },
1776
1770
  { name = "simple-term-menu" },
1777
1771
  { name = "starlette" },
1778
1772
  { name = "uvicorn", extra = ["standard"] },
@@ -1822,6 +1816,7 @@ requires-dist = [
1822
1816
  { name = "elevenlabs", marker = "extra == 'tts-premium'", specifier = "==1.59.0" },
1823
1817
  { name = "exa-py", marker = "extra == 'exa'", specifier = "==2.10.2" },
1824
1818
  { name = "fal-client", marker = "extra == 'fal'", specifier = "==0.13.1" },
1819
+ { name = "fastapi", specifier = ">=0.104.0,<1" },
1825
1820
  { name = "fastapi", marker = "extra == 'web'", specifier = "==0.133.1" },
1826
1821
  { name = "faster-whisper", marker = "extra == 'voice'", specifier = "==1.2.1" },
1827
1822
  { name = "fire", specifier = "==0.7.1" },
@@ -1868,7 +1863,7 @@ requires-dist = [
1868
1863
  { name = "parallel-web", marker = "extra == 'parallel-web'", specifier = "==0.4.2" },
1869
1864
  { name = "prompt-toolkit", specifier = "==3.0.52" },
1870
1865
  { name = "psutil", specifier = "==7.2.2" },
1871
- { name = "ptyprocess", marker = "sys_platform != 'win32' and extra == 'pty'", specifier = "==0.7.0" },
1866
+ { name = "ptyprocess", marker = "sys_platform != 'win32'", specifier = ">=0.7.0,<1" },
1872
1867
  { name = "pydantic", specifier = "==2.13.4" },
1873
1868
  { name = "pyjwt", extras = ["crypto"], specifier = "==2.12.1" },
1874
1869
  { name = "pytest", marker = "extra == 'dev'", specifier = "==9.0.2" },
@@ -1877,7 +1872,7 @@ requires-dist = [
1877
1872
  { name = "python-dotenv", specifier = "==1.2.2" },
1878
1873
  { name = "python-telegram-bot", extras = ["webhooks"], marker = "extra == 'messaging'", specifier = "==22.6" },
1879
1874
  { name = "python-telegram-bot", extras = ["webhooks"], marker = "extra == 'termux'", specifier = "==22.6" },
1880
- { name = "pywinpty", marker = "sys_platform == 'win32' and extra == 'pty'", specifier = "==2.0.15" },
1875
+ { name = "pywinpty", marker = "sys_platform == 'win32'", specifier = ">=2.0.0,<3" },
1881
1876
  { name = "pyyaml", specifier = "==6.0.3" },
1882
1877
  { name = "qrcode", marker = "extra == 'dingtalk'", specifier = "==7.4.2" },
1883
1878
  { name = "qrcode", marker = "extra == 'feishu'", specifier = "==7.4.2" },
@@ -1900,6 +1895,7 @@ requires-dist = [
1900
1895
  { name = "tenacity", specifier = "==9.1.4" },
1901
1896
  { name = "ty", marker = "extra == 'dev'", specifier = "==0.0.21" },
1902
1897
  { name = "tzdata", marker = "sys_platform == 'win32'", specifier = "==2025.3" },
1898
+ { name = "uvicorn", extras = ["standard"], specifier = ">=0.24.0,<1" },
1903
1899
  { name = "uvicorn", extras = ["standard"], marker = "extra == 'web'", specifier = "==0.41.0" },
1904
1900
  { name = "youtube-transcript-api", marker = "extra == 'youtube'", specifier = "==1.2.4" },
1905
1901
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawpump/claw-agent",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },