agentmint-hermes-runner 0.4.0__tar.gz
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.
- agentmint_hermes_runner-0.4.0/.github/workflows/ci.yml +22 -0
- agentmint_hermes_runner-0.4.0/.github/workflows/release.yml +20 -0
- agentmint_hermes_runner-0.4.0/.gitignore +12 -0
- agentmint_hermes_runner-0.4.0/LICENSE +21 -0
- agentmint_hermes_runner-0.4.0/PKG-INFO +117 -0
- agentmint_hermes_runner-0.4.0/README.md +92 -0
- agentmint_hermes_runner-0.4.0/agentmint-hermes/SKILL.md +221 -0
- agentmint_hermes_runner-0.4.0/examples/hermes_strategy_b.py +51 -0
- agentmint_hermes_runner-0.4.0/examples/stripe_link.py +38 -0
- agentmint_hermes_runner-0.4.0/examples/tempo_wallet.py +38 -0
- agentmint_hermes_runner-0.4.0/pyproject.toml +49 -0
- agentmint_hermes_runner-0.4.0/src/agentmint_hermes_runner/__init__.py +40 -0
- agentmint_hermes_runner-0.4.0/src/agentmint_hermes_runner/auth/__init__.py +5 -0
- agentmint_hermes_runner-0.4.0/src/agentmint_hermes_runner/auth/base.py +18 -0
- agentmint_hermes_runner-0.4.0/src/agentmint_hermes_runner/auth/bearer.py +29 -0
- agentmint_hermes_runner-0.4.0/src/agentmint_hermes_runner/auth/tempo.py +37 -0
- agentmint_hermes_runner-0.4.0/src/agentmint_hermes_runner/client.py +40 -0
- agentmint_hermes_runner-0.4.0/src/agentmint_hermes_runner/dispatcher.py +226 -0
- agentmint_hermes_runner-0.4.0/src/agentmint_hermes_runner/exceptions.py +28 -0
- agentmint_hermes_runner-0.4.0/src/agentmint_hermes_runner/hermes_patch.py +213 -0
- agentmint_hermes_runner-0.4.0/src/agentmint_hermes_runner/models.py +61 -0
- agentmint_hermes_runner-0.4.0/src/agentmint_hermes_runner/translation.py +93 -0
- agentmint_hermes_runner-0.4.0/tests/__init__.py +0 -0
- agentmint_hermes_runner-0.4.0/tests/test_bearer_auth.py +34 -0
- agentmint_hermes_runner-0.4.0/tests/test_client.py +43 -0
- agentmint_hermes_runner-0.4.0/tests/test_dispatch_batch.py +111 -0
- agentmint_hermes_runner-0.4.0/tests/test_dispatcher.py +70 -0
- agentmint_hermes_runner-0.4.0/tests/test_hermes_patch.py +163 -0
- agentmint_hermes_runner-0.4.0/tests/test_tempo_auth.py +33 -0
- agentmint_hermes_runner-0.4.0/tests/test_timeout.py +53 -0
- agentmint_hermes_runner-0.4.0/tests/test_translation.py +95 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: ${{ matrix.python-version }}
|
|
20
|
+
- run: pip install -e ".[dev]"
|
|
21
|
+
- run: ruff check .
|
|
22
|
+
- run: pytest -v
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
environment: pypi
|
|
11
|
+
permissions:
|
|
12
|
+
id-token: write
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.12"
|
|
18
|
+
- run: pip install build
|
|
19
|
+
- run: python -m build
|
|
20
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AgentMint
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agentmint-hermes-runner
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Route Hermes delegate_task(background=True) to named, persistent AgentMint subagents.
|
|
5
|
+
Project-URL: Homepage, https://github.com/mesutcelik/agentmint-hermes
|
|
6
|
+
Project-URL: Repository, https://github.com/mesutcelik/agentmint-hermes
|
|
7
|
+
Project-URL: Issues, https://github.com/mesutcelik/agentmint-hermes/issues
|
|
8
|
+
Author: AgentMint
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agentmint,ai-agents,delegation,hermes,mpp,stripe-link,subagents,tempo
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Requires-Dist: httpx>=0.27
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
23
|
+
Requires-Dist: ruff>=0.7; extra == 'dev'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# agentmint-hermes-runner
|
|
27
|
+
|
|
28
|
+
Route Hermes `delegate_task(background=True)` to named, persistent AgentMint subagents.
|
|
29
|
+
|
|
30
|
+
> Positioning + full quickstart in [`agentmint-hermes/SKILL.md`](agentmint-hermes/SKILL.md).
|
|
31
|
+
|
|
32
|
+
## Status
|
|
33
|
+
|
|
34
|
+
**v0.4.0** — alpha. Auth backends: `BearerAuth` (Stripe-Link), `TempoAuth` (Tempo USDC.e). Polling-only delivery. Hermes feature coverage matrix in [`agentmint-hermes/SKILL.md`](agentmint-hermes/SKILL.md).
|
|
35
|
+
|
|
36
|
+
## Install in Hermes
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
hermes skills install mesutcelik/agentmint-hermes/agentmint-hermes
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
(The third segment is the skill subfolder inside the repo, per Hermes' `GitHubSource` convention.)
|
|
43
|
+
|
|
44
|
+
## Three-line Hermes wiring (Strategy B)
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
import os
|
|
48
|
+
from agentmint_hermes_runner import (
|
|
49
|
+
AgentMintDispatcher, BearerAuth, install_delegate_task_wrapper,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
dispatcher = AgentMintDispatcher(auth=BearerAuth(jwt=os.environ["AGENTMINT_JWT"]))
|
|
53
|
+
install_delegate_task_wrapper(dispatcher, default_agent_name="default-worker")
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Every `delegate_task(background=True)` inside Hermes now routes to AgentMint's `default-worker` subagent. Its `/workspace/MEMORY.md` accumulates across every delegation. No HTTPS, no ngrok, no webhook secret — a daemon thread polls `agent.run.status` (free, Bearer-only) every 5 s and pushes completions onto Hermes' `completion_queue` directly. Server-side requires AgentMint API ≥ 0.7.0 for the polling endpoint.
|
|
57
|
+
|
|
58
|
+
## Install
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install agentmint-hermes-runner
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Surface
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from agentmint_hermes_runner import (
|
|
68
|
+
AgentMintDispatcher,
|
|
69
|
+
AgentMintWebhookReceiver,
|
|
70
|
+
BearerAuth, TempoAuth,
|
|
71
|
+
Task,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
dispatcher = AgentMintDispatcher(
|
|
75
|
+
auth=BearerAuth(jwt=os.environ["AGENTMINT_JWT"]),
|
|
76
|
+
webhook_url="https://my-gateway.example.com/agentmint-webhook", # optional
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Single dispatch (Hermes delegate_task analog):
|
|
80
|
+
result = dispatcher.dispatch(
|
|
81
|
+
agent_name="reviewer-myrepo",
|
|
82
|
+
goal="Review the diff in /workspace/pr-42 and flag risks.",
|
|
83
|
+
context="Project at /workspace, Python 3.11, uses Flask + PyJWT.",
|
|
84
|
+
toolsets=["terminal", "file"], # "web" raises UnsupportedToolset in v0.2
|
|
85
|
+
role="leaf", # or "orchestrator"
|
|
86
|
+
max_iterations=50,
|
|
87
|
+
child_timeout_seconds=600, # floor 30s; fires agent.cancel on expiry
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Batch dispatch (Hermes tasks=[…] analog):
|
|
91
|
+
results = dispatcher.dispatch_batch(
|
|
92
|
+
tasks=[
|
|
93
|
+
Task(agent_name="researcher-wasm", goal="WASM 2026 survey", context="…"),
|
|
94
|
+
Task(agent_name="researcher-riscv", goal="RISC-V 2026 survey", context="…"),
|
|
95
|
+
],
|
|
96
|
+
max_concurrent_children=3,
|
|
97
|
+
child_timeout_seconds=900,
|
|
98
|
+
)
|
|
99
|
+
# results in input order; failed/timeout/interrupted statuses returned in-band
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Test
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
pip install -e ".[dev]"
|
|
106
|
+
pytest
|
|
107
|
+
ruff check .
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Known unsupported (v0.2)
|
|
111
|
+
|
|
112
|
+
- **`toolsets=["web"]`** — no canonical AgentMint web-fetch skill yet. The supported harnesses (claude-code / codex / opencode) all have built-in web access via the harness itself, but we don't expose a Hermes-symmetric toolset for it. Raises `UnsupportedToolset` at compose time so the gap is loud, not silent.
|
|
113
|
+
- **`max_spawn_depth`** — AgentMint sandboxes aren't structurally bounded by depth.
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
MIT
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# agentmint-hermes-runner
|
|
2
|
+
|
|
3
|
+
Route Hermes `delegate_task(background=True)` to named, persistent AgentMint subagents.
|
|
4
|
+
|
|
5
|
+
> Positioning + full quickstart in [`agentmint-hermes/SKILL.md`](agentmint-hermes/SKILL.md).
|
|
6
|
+
|
|
7
|
+
## Status
|
|
8
|
+
|
|
9
|
+
**v0.4.0** — alpha. Auth backends: `BearerAuth` (Stripe-Link), `TempoAuth` (Tempo USDC.e). Polling-only delivery. Hermes feature coverage matrix in [`agentmint-hermes/SKILL.md`](agentmint-hermes/SKILL.md).
|
|
10
|
+
|
|
11
|
+
## Install in Hermes
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
hermes skills install mesutcelik/agentmint-hermes/agentmint-hermes
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
(The third segment is the skill subfolder inside the repo, per Hermes' `GitHubSource` convention.)
|
|
18
|
+
|
|
19
|
+
## Three-line Hermes wiring (Strategy B)
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
import os
|
|
23
|
+
from agentmint_hermes_runner import (
|
|
24
|
+
AgentMintDispatcher, BearerAuth, install_delegate_task_wrapper,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
dispatcher = AgentMintDispatcher(auth=BearerAuth(jwt=os.environ["AGENTMINT_JWT"]))
|
|
28
|
+
install_delegate_task_wrapper(dispatcher, default_agent_name="default-worker")
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Every `delegate_task(background=True)` inside Hermes now routes to AgentMint's `default-worker` subagent. Its `/workspace/MEMORY.md` accumulates across every delegation. No HTTPS, no ngrok, no webhook secret — a daemon thread polls `agent.run.status` (free, Bearer-only) every 5 s and pushes completions onto Hermes' `completion_queue` directly. Server-side requires AgentMint API ≥ 0.7.0 for the polling endpoint.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install agentmint-hermes-runner
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Surface
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from agentmint_hermes_runner import (
|
|
43
|
+
AgentMintDispatcher,
|
|
44
|
+
AgentMintWebhookReceiver,
|
|
45
|
+
BearerAuth, TempoAuth,
|
|
46
|
+
Task,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
dispatcher = AgentMintDispatcher(
|
|
50
|
+
auth=BearerAuth(jwt=os.environ["AGENTMINT_JWT"]),
|
|
51
|
+
webhook_url="https://my-gateway.example.com/agentmint-webhook", # optional
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Single dispatch (Hermes delegate_task analog):
|
|
55
|
+
result = dispatcher.dispatch(
|
|
56
|
+
agent_name="reviewer-myrepo",
|
|
57
|
+
goal="Review the diff in /workspace/pr-42 and flag risks.",
|
|
58
|
+
context="Project at /workspace, Python 3.11, uses Flask + PyJWT.",
|
|
59
|
+
toolsets=["terminal", "file"], # "web" raises UnsupportedToolset in v0.2
|
|
60
|
+
role="leaf", # or "orchestrator"
|
|
61
|
+
max_iterations=50,
|
|
62
|
+
child_timeout_seconds=600, # floor 30s; fires agent.cancel on expiry
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# Batch dispatch (Hermes tasks=[…] analog):
|
|
66
|
+
results = dispatcher.dispatch_batch(
|
|
67
|
+
tasks=[
|
|
68
|
+
Task(agent_name="researcher-wasm", goal="WASM 2026 survey", context="…"),
|
|
69
|
+
Task(agent_name="researcher-riscv", goal="RISC-V 2026 survey", context="…"),
|
|
70
|
+
],
|
|
71
|
+
max_concurrent_children=3,
|
|
72
|
+
child_timeout_seconds=900,
|
|
73
|
+
)
|
|
74
|
+
# results in input order; failed/timeout/interrupted statuses returned in-band
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Test
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
pip install -e ".[dev]"
|
|
81
|
+
pytest
|
|
82
|
+
ruff check .
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Known unsupported (v0.2)
|
|
86
|
+
|
|
87
|
+
- **`toolsets=["web"]`** — no canonical AgentMint web-fetch skill yet. The supported harnesses (claude-code / codex / opencode) all have built-in web access via the harness itself, but we don't expose a Hermes-symmetric toolset for it. Raises `UnsupportedToolset` at compose time so the gap is loud, not silent.
|
|
88
|
+
- **`max_spawn_depth`** — AgentMint sandboxes aren't structurally bounded by depth.
|
|
89
|
+
|
|
90
|
+
## License
|
|
91
|
+
|
|
92
|
+
MIT
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agentmint-hermes
|
|
3
|
+
description: Mint AgentMint pay-as-you-go subagents from Hermes. Provisions a persistent sandbox (Claude Code / Codex / OpenCode harness, any model via OpenRouter) for a long-running task; subsequent `delegate_task(background=true)` calls dispatch to it, hibernate the box between calls, and bill per run. Pay via Stripe-Link (link-cli) or Tempo USDC.e.
|
|
4
|
+
version: 0.2.0
|
|
5
|
+
author: AgentMint
|
|
6
|
+
license: MIT
|
|
7
|
+
platforms: [linux, macos]
|
|
8
|
+
metadata:
|
|
9
|
+
hermes:
|
|
10
|
+
tags: [Subagents, Delegation, Payments, MPP, Sandbox]
|
|
11
|
+
related_skills: [mpp-agent, stripe-link-cli]
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# AgentMint × Hermes
|
|
15
|
+
|
|
16
|
+
Long-lived, named, USDC-paid subagents that Hermes can `delegate_task` against. Each subagent has its own bounded sandbox (Upstash Box), its own filesystem that persists across calls (`/workspace/MEMORY.md` is the standard memory anchor), and a stable name (`reviewer-myrepo`, `support-acme`, …). Hibernates to zero between calls — billed only when running.
|
|
17
|
+
|
|
18
|
+
## When to use
|
|
19
|
+
|
|
20
|
+
- Hermes' main session needs a **specialist** (PR reviewer, compliance checker, customer-support agent, codebase oracle, etc.) that accumulates domain knowledge across days/weeks.
|
|
21
|
+
- A task naturally **fans out** into N independent slices and you want each one on its own sandbox.
|
|
22
|
+
- You want **`delegate_task(background=true)`** to dispatch the actual work somewhere with isolated credentials, not the Hermes gateway itself.
|
|
23
|
+
- You want to use a **specific harness × model** combination (e.g. opencode + `openrouter/fusion`, claude-code + claude-sonnet-4-6) without baking those choices into Hermes.
|
|
24
|
+
|
|
25
|
+
Not the right tool when:
|
|
26
|
+
- The task is one-shot and Hermes can answer directly.
|
|
27
|
+
- You need cross-subagent shared state (each subagent's MEMORY is its own).
|
|
28
|
+
|
|
29
|
+
## Two integration tiers
|
|
30
|
+
|
|
31
|
+
### Tier 1 — Direct (no Python, no Hermes code changes)
|
|
32
|
+
|
|
33
|
+
Hermes calls AgentMint's JSON-RPC `/a2a` endpoint via `terminal` using whichever wallet skill the user has authenticated. Mint once, run many times.
|
|
34
|
+
|
|
35
|
+
### Tier 2 — `delegate_task` background dispatch
|
|
36
|
+
|
|
37
|
+
Pip-install `agentmint-hermes-runner` and wire it into Hermes' gateway. After that, `delegate_task(background=True, …)` results route through PR #40946's async-delegation rail (merged 2026-06-15). Hermes' existing `_async_delegation_watcher` re-injects the result as a new turn in the originating session.
|
|
38
|
+
|
|
39
|
+
## Wallet matrix
|
|
40
|
+
|
|
41
|
+
AgentMint speaks the same `/a2a` endpoint for both wallets — pick whichever Hermes already has authenticated.
|
|
42
|
+
|
|
43
|
+
| Wallet | When | Path |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| **Stripe-Link** (`link-cli`) | User has a card, no crypto wallet. Best for non-developers. | Bearer JWT against a caller-wide credit wallet — bootstrap once, debits per call |
|
|
46
|
+
| **Tempo Wallet** | User has Tempo wallet authenticated; wants spend controls + service discovery | Per-call x402/MPP, USDC.e on Tempo (`eip155:4217`) |
|
|
47
|
+
|
|
48
|
+
Fetch the underlying wallet skill via `web_extract` before invoking, so its setup steps are in context:
|
|
49
|
+
|
|
50
|
+
- `https://agentmint.store/SKILL.md` — full AgentMint API (every method, every rail)
|
|
51
|
+
- Hermes' built-in `stripe-link-cli` skill for `link-cli`
|
|
52
|
+
- Hermes' built-in `mpp-agent` skill for `tempo request`
|
|
53
|
+
|
|
54
|
+
## Tier 1 — Direct mint + run
|
|
55
|
+
|
|
56
|
+
### Path A: Stripe-Link (link-cli + credit wallet)
|
|
57
|
+
|
|
58
|
+
Bootstrap once, then every subsequent `agent.*` call is Bearer-paid against a shared credit wallet — no Stripe per-call fee.
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# 1) Bootstrap the wallet (min $10) — via the stripe-link-cli skill
|
|
62
|
+
link-cli mpp pay https://api.agentmint.store/a2a \
|
|
63
|
+
-X POST -H 'Content-Type: application/json' \
|
|
64
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"credits.topup","params":{"amount_usd":10}}' \
|
|
65
|
+
--spend-request-id <lsrq_…>
|
|
66
|
+
# → response.access_token = <jwt> (caller-wide; works for any subagent)
|
|
67
|
+
|
|
68
|
+
# 2) Mint a subagent (Bearer-paid, 0.10 USDC equivalent debited from wallet)
|
|
69
|
+
curl -X POST https://api.agentmint.store/a2a \
|
|
70
|
+
-H 'Authorization: Bearer <jwt>' -H 'Content-Type: application/json' \
|
|
71
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"agent.create","params":{"name":"reviewer-myrepo"}}'
|
|
72
|
+
|
|
73
|
+
# 3) Run the subagent (per-call price debited from the same wallet)
|
|
74
|
+
curl -X POST https://api.agentmint.store/a2a \
|
|
75
|
+
-H 'Authorization: Bearer <jwt>' -H 'Content-Type: application/json' \
|
|
76
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"agent.run","params":{"name":"reviewer-myrepo","prompt":"…"}}'
|
|
77
|
+
|
|
78
|
+
# 4) Check the shared balance any time
|
|
79
|
+
curl -X POST https://api.agentmint.store/a2a \
|
|
80
|
+
-H 'Authorization: Bearer <jwt>' -H 'Content-Type: application/json' \
|
|
81
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"credits.balance","params":{}}'
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Same JWT works across every subagent the user mints — one balance, N subagents.
|
|
85
|
+
|
|
86
|
+
### Path B: Tempo Wallet
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
tempo wallet login # one-time, browser-based
|
|
90
|
+
tempo wallet whoami # confirms address + USDC balance
|
|
91
|
+
|
|
92
|
+
tempo request -X POST \
|
|
93
|
+
--json '{"jsonrpc":"2.0","id":1,"method":"agent.create","params":{"name":"reviewer-myrepo"}}' \
|
|
94
|
+
https://api.agentmint.store/a2a
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Pin to `tempo-request@0.5.2` — newer versions hit "Invalid base64 JSON header" against AgentMint's challenge. Downgrade with `tempo cli 0.0.0 downgrade tempo request cli to 0.5.2`.
|
|
98
|
+
|
|
99
|
+
## Tier 2 — `delegate_task(background=True)` dispatch (Strategy B)
|
|
100
|
+
|
|
101
|
+
`install_delegate_task_wrapper` monkey-patches `tools.async_delegation.dispatch_async_delegation` (the PR #40946 hook) so every `delegate_task(background=True, single-task)` call inside Hermes transparently routes to a named, persistent AgentMint subagent. Sync `delegate_task` and batch `delegate_task` are untouched.
|
|
102
|
+
|
|
103
|
+
Completion is delivered via **polling** against AgentMint's `agent.run.status` endpoint (Bearer-only, free). A daemon thread per dispatch polls every 5 s and pushes completions onto Hermes' `completion_queue` via `_push_completion_event`. No public HTTPS endpoint, no webhook secret, no HTTP route to register — polling is the only delivery mode.
|
|
104
|
+
|
|
105
|
+
### Step-by-step setup
|
|
106
|
+
|
|
107
|
+
**Step 1 — Bootstrap an AgentMint wallet (one-time)**
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Stripe-Link (recommended, supports polling) — min $10:
|
|
111
|
+
link-cli mpp pay https://api.agentmint.store/a2a \
|
|
112
|
+
-X POST -H 'Content-Type: application/json' \
|
|
113
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"credits.topup","params":{"amount_usd":10}}'
|
|
114
|
+
# → response.result.access_token = <JWT>
|
|
115
|
+
|
|
116
|
+
export AGENTMINT_JWT=<the JWT>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
(Tempo path also works, but polling is Bearer-only — Tempo users must use webhook mode, covered in "Webhook mode" below.)
|
|
120
|
+
|
|
121
|
+
**Step 2 — Pre-mint the subagent (one-time)**
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
curl -X POST https://api.agentmint.store/a2a \
|
|
125
|
+
-H "Authorization: Bearer $AGENTMINT_JWT" \
|
|
126
|
+
-H 'Content-Type: application/json' \
|
|
127
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"agent.create","params":{
|
|
128
|
+
"name":"default-worker",
|
|
129
|
+
"harness":"opencode",
|
|
130
|
+
"model":"openrouter/fusion"}}'
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
This is the entity that will REMEMBER across every Hermes delegation. Its `/workspace/MEMORY.md` accumulates context across every call. Cost: 0.10 USDC equivalent debited from the credit wallet.
|
|
134
|
+
|
|
135
|
+
**Step 3 — Install the Python adapter in Hermes' venv**
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
pip install agentmint-hermes-runner
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Verify: `python -c "import agentmint_hermes_runner; print(agentmint_hermes_runner.__version__)"` → `0.3.0` or higher.
|
|
142
|
+
|
|
143
|
+
**Step 4 — Add three lines to your Hermes gateway startup**
|
|
144
|
+
|
|
145
|
+
Put this in your Hermes gateway entry-point (or wherever you instantiate the gateway), BEFORE any `delegate_task(background=True)` call:
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
import os
|
|
149
|
+
from agentmint_hermes_runner import (
|
|
150
|
+
AgentMintDispatcher, BearerAuth, install_delegate_task_wrapper,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
dispatcher = AgentMintDispatcher(auth=BearerAuth(jwt=os.environ["AGENTMINT_JWT"]))
|
|
154
|
+
install_delegate_task_wrapper(dispatcher, default_agent_name="default-worker")
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
That's the entire wiring. The function returns an `uninstall()` callable if you want to undo it later (mostly useful in tests).
|
|
158
|
+
|
|
159
|
+
**Step 5 — Restart Hermes and test**
|
|
160
|
+
|
|
161
|
+
Restart the gateway so the new module loads and the patch is in effect. Then from inside a Hermes session:
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
> use delegate_task with background=true to ask: "Say hello and tell me what
|
|
165
|
+
you remember from prior calls."
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
The LLM will call `delegate_task(background=True, goal="…")`. Behind the scenes:
|
|
169
|
+
1. Adapter calls `agent.run` on AgentMint with `async: true`
|
|
170
|
+
2. AgentMint returns a `run_id` (e.g. `arun_a1b2c3d4`)
|
|
171
|
+
3. Adapter spawns a daemon thread that polls `agent.run.status` every 5 s
|
|
172
|
+
4. AgentMint finishes the run (~15-60 s typically)
|
|
173
|
+
5. Adapter calls `_push_completion_event` → Hermes' `completion_queue`
|
|
174
|
+
6. Hermes' `_async_delegation_watcher` drains and re-injects the result as a new turn
|
|
175
|
+
|
|
176
|
+
**Step 6 — Verify persistence (the value proposition)**
|
|
177
|
+
|
|
178
|
+
Call `delegate_task(background=true)` twice with different goals against the same Hermes session. The second response should reference details from the first — because `/workspace/MEMORY.md` survived between dispatches. That's the differentiator from Hermes' native `delegate_task` (which always starts fresh).
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
## Hermes `delegate_task` coverage (v0.3)
|
|
182
|
+
|
|
183
|
+
| Hermes feature | AgentMint via this runner | Notes |
|
|
184
|
+
|---|---|---|
|
|
185
|
+
| `goal` | ✅ Concatenated under `## Goal` | `dispatcher.dispatch(goal=…)` |
|
|
186
|
+
| `context` | ✅ Concatenated under `## Context` | Client-side concat — no server-side `context` field |
|
|
187
|
+
| `toolsets=["terminal", "file"]` restrictions | ✅ Soft hints in prompt ("Do not run shell commands.", …) | Sandbox can't structurally enforce; the harness should respect the hint |
|
|
188
|
+
| `toolsets=["web"]` | ❌ **Unsupported** — raises `UnsupportedToolset` | No canonical web-fetch skill in the AgentMint catalog yet; tracked separately |
|
|
189
|
+
| `role="leaf"` / `"orchestrator"` | ✅ Soft hint in prompt | Default `"leaf"` |
|
|
190
|
+
| `max_iterations` | ✅ Soft hint ("Soft iteration budget: ~N actions.") | Harness-dependent enforcement |
|
|
191
|
+
| `tasks=[{…}, {…}]` (batch) | ✅ `dispatcher.dispatch_batch(tasks=…)` — parallel via ThreadPoolExecutor, results in input order | Each Task targets a named subagent |
|
|
192
|
+
| `max_concurrent_children` | ✅ `max_concurrent_children=N` param to `dispatch_batch` | Default 3 |
|
|
193
|
+
| `child_timeout_seconds` | ✅ `child_timeout_seconds=N` param; floor 30s; fires `agent.cancel` on expiry | Single + batch |
|
|
194
|
+
| Interrupt cascade | ✅ `cancel_event=threading.Event` to `dispatch_batch` — fires `agent.cancel` on all in-flight | |
|
|
195
|
+
| `background=True` (PR #40946) | ✅ **`install_delegate_task_wrapper(...)` — Strategy B, polling** | Transparent to the LLM; polling-only |
|
|
196
|
+
| Result ordering (by task index) | ✅ `dispatch_batch` returns in input order regardless of completion order | |
|
|
197
|
+
| `max_spawn_depth` (nested delegation) | n/a | AgentMint sandboxes aren't depth-bounded structurally |
|
|
198
|
+
| `/agents` TUI overlay | n/a | Pure Hermes UI feature; use `dispatcher.list()` to enumerate subagents |
|
|
199
|
+
| Credential inheritance | **better** | Each subagent has its own credentials (no parent key sharing) |
|
|
200
|
+
| "Fresh conversation per call" | **inverted** | AgentMint subagents persist `/workspace/MEMORY.md` across calls — this is the core value |
|
|
201
|
+
|
|
202
|
+
## Pitfalls
|
|
203
|
+
|
|
204
|
+
- **Mode 1 (Stripe-Link) and Mode 2 (Tempo) don't share state.** Funds in the credit wallet (`account:<principal>`) are not transferable to a blockchain address and vice versa. Pick one model per principal.
|
|
205
|
+
- **JWT is caller-wide on Stripe-Link.** One token authorises any agent.* call against any subagent the principal owns. If lost, re-bootstrap via `credits.topup` with no Bearer — the server rotates the canonical jti. The old token is NOT auto-revoked; revoke it explicitly via `credits.revoke_token --jti <jti>` if you know the lost jti.
|
|
206
|
+
- **`name` is global + immutable.** First mint wins. Pick something specific enough to avoid collisions (`reviewer-mesutcelik-agentmint`, not `reviewer`). Released only by `agent.delete`.
|
|
207
|
+
- **Stripe-MPP only accepts `credits.topup`.** Trying `agent.create` over Stripe-MPP returns `400 use_bearer_after_topup`. Bootstrap with `credits.topup` first.
|
|
208
|
+
- **Tempo broadcast is client-side.** `tempo request` broadcasts before AgentMint's server sees the payment, so a server-side failure after `verify` still leaves the customer charged. Grep server logs for `[agentmint/refund-needed]` for manual operator refund triggers.
|
|
209
|
+
- **Wallet keys never enter Hermes context.** Both wallet skills store credentials under their own config dirs; never `cat`/`read_file` them.
|
|
210
|
+
|
|
211
|
+
## Verification
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
# Confirm the AgentMint endpoint is reachable + supports your wallet
|
|
215
|
+
curl -X POST https://api.agentmint.store/a2a \
|
|
216
|
+
-H 'Content-Type: application/json' \
|
|
217
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"agent.create","params":{}}'
|
|
218
|
+
# → 402 with accepts[] enumerating supported chains. Pick the one matching your wallet.
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
For Tier 2, the canary is a complete dispatch + webhook + re-injection cycle. See `examples/stripe_link.py` and `examples/tempo_wallet.py` in the agentmint-hermes repo.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Strategy B: wire AgentMint into Hermes so `delegate_task(background=True)`
|
|
2
|
+
transparently routes to a persistent AgentMint subagent.
|
|
3
|
+
|
|
4
|
+
Prerequisites:
|
|
5
|
+
1. Bootstrap an AgentMint credit wallet via link-cli (one-time, $10 min):
|
|
6
|
+
link-cli mpp pay https://api.agentmint.store/a2a -X POST \\
|
|
7
|
+
-H 'Content-Type: application/json' \\
|
|
8
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"credits.topup","params":{"amount_usd":10}}'
|
|
9
|
+
export AGENTMINT_JWT=<the access_token from the response>
|
|
10
|
+
|
|
11
|
+
2. Pre-mint your subagent (this is the entity that will REMEMBER across
|
|
12
|
+
every Hermes delegation):
|
|
13
|
+
curl -X POST https://api.agentmint.store/a2a \\
|
|
14
|
+
-H "Authorization: Bearer $AGENTMINT_JWT" \\
|
|
15
|
+
-H 'Content-Type: application/json' \\
|
|
16
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"agent.create",
|
|
17
|
+
"params":{"name":"default-worker","harness":"opencode",
|
|
18
|
+
"model":"openrouter/fusion"}}'
|
|
19
|
+
|
|
20
|
+
3. pip install agentmint-hermes-runner inside Hermes' virtualenv.
|
|
21
|
+
|
|
22
|
+
Drop this snippet into your Hermes gateway startup code (before any
|
|
23
|
+
delegate_task call). That's the entire wiring — no HTTPS endpoint, no
|
|
24
|
+
webhook secret, no HTTP route.
|
|
25
|
+
"""
|
|
26
|
+
import os
|
|
27
|
+
|
|
28
|
+
from agentmint_hermes_runner import (
|
|
29
|
+
AgentMintDispatcher,
|
|
30
|
+
BearerAuth,
|
|
31
|
+
install_delegate_task_wrapper,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def main() -> None:
|
|
36
|
+
dispatcher = AgentMintDispatcher(
|
|
37
|
+
auth=BearerAuth(jwt=os.environ["AGENTMINT_JWT"]),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
install_delegate_task_wrapper(
|
|
41
|
+
dispatcher=dispatcher,
|
|
42
|
+
default_agent_name="default-worker",
|
|
43
|
+
poll_interval=5.0,
|
|
44
|
+
)
|
|
45
|
+
# From here on, every delegate_task(background=True) call inside Hermes
|
|
46
|
+
# routes to the AgentMint subagent named "default-worker". Its
|
|
47
|
+
# /workspace/MEMORY.md accumulates context across every delegation.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
if __name__ == "__main__":
|
|
51
|
+
main()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Dispatch a generic 'hello' goal to a named AgentMint subagent via Stripe-Link.
|
|
2
|
+
|
|
3
|
+
Prerequisites:
|
|
4
|
+
1. Bootstrap a credit wallet via link-cli (one-time, min $10):
|
|
5
|
+
link-cli mpp pay https://api.agentmint.store/a2a \\
|
|
6
|
+
-X POST -H 'Content-Type: application/json' \\
|
|
7
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"credits.topup","params":{"amount_usd":10}}'
|
|
8
|
+
The response includes `result.access_token` — the wallet JWT.
|
|
9
|
+
|
|
10
|
+
2. export AGENTMINT_JWT=<the jwt>
|
|
11
|
+
3. Pre-mint a subagent once (or call dispatcher.create() in code):
|
|
12
|
+
agentmint agent create --name hello-bot
|
|
13
|
+
4. export AGENT_NAME=hello-bot
|
|
14
|
+
|
|
15
|
+
Run:
|
|
16
|
+
python examples/stripe_link.py
|
|
17
|
+
"""
|
|
18
|
+
import os
|
|
19
|
+
|
|
20
|
+
from agentmint_hermes_runner import AgentMintDispatcher, BearerAuth
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def main() -> None:
|
|
24
|
+
dispatcher = AgentMintDispatcher(
|
|
25
|
+
endpoint=os.environ.get("AGENTMINT_ENDPOINT", "https://api.agentmint.store/a2a"),
|
|
26
|
+
auth=BearerAuth(jwt=os.environ["AGENTMINT_JWT"]),
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
agent_name = os.environ["AGENT_NAME"]
|
|
30
|
+
result = dispatcher.dispatch(
|
|
31
|
+
agent_name=agent_name,
|
|
32
|
+
goal="Say hello and tell me what you remember from prior calls.",
|
|
33
|
+
)
|
|
34
|
+
print(result)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if __name__ == "__main__":
|
|
38
|
+
main()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Dispatch a generic 'hello' goal via Tempo (per-call USDC.e on eip155:4217).
|
|
2
|
+
|
|
3
|
+
Prerequisites:
|
|
4
|
+
1. tempo wallet login (one-time, browser-based)
|
|
5
|
+
2. Funded USDC.e on Tempo mainnet (eip155:4217)
|
|
6
|
+
3. tempo-request@0.5.2 — newer versions break with "Invalid base64 JSON
|
|
7
|
+
header". Downgrade with: `tempo cli 0.0.0 downgrade tempo request cli to 0.5.2`
|
|
8
|
+
4. Pre-mint a subagent once:
|
|
9
|
+
tempo request -X POST --json \\
|
|
10
|
+
'{"jsonrpc":"2.0","id":1,"method":"agent.create","params":{"name":"hello-bot"}}' \\
|
|
11
|
+
https://api.agentmint.store/a2a
|
|
12
|
+
5. export AGENT_NAME=hello-bot
|
|
13
|
+
6. (optional) export TEMPO_ACCOUNT=<account-name>
|
|
14
|
+
|
|
15
|
+
Run:
|
|
16
|
+
python examples/tempo_wallet.py
|
|
17
|
+
"""
|
|
18
|
+
import os
|
|
19
|
+
|
|
20
|
+
from agentmint_hermes_runner import AgentMintDispatcher, TempoAuth
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def main() -> None:
|
|
24
|
+
dispatcher = AgentMintDispatcher(
|
|
25
|
+
endpoint=os.environ.get("AGENTMINT_ENDPOINT", "https://api.agentmint.store/a2a"),
|
|
26
|
+
auth=TempoAuth(account=os.environ.get("TEMPO_ACCOUNT")),
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
agent_name = os.environ["AGENT_NAME"]
|
|
30
|
+
result = dispatcher.dispatch(
|
|
31
|
+
agent_name=agent_name,
|
|
32
|
+
goal="Say hello and tell me what you remember from prior calls.",
|
|
33
|
+
)
|
|
34
|
+
print(result)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if __name__ == "__main__":
|
|
38
|
+
main()
|