pbt-extra-model-kinds 0.1.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.
@@ -0,0 +1,3 @@
1
+ .venv/
2
+ dist/
3
+ __pycache__/
@@ -0,0 +1,25 @@
1
+ Metadata-Version: 2.5
2
+ Name: pbt-extra-model-kinds
3
+ Version: 0.1.0
4
+ Summary: Extra pbt model kinds: python_modal, agent_modal and agent37.
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: httpx>=0.27
7
+ Requires-Dist: modal>=1.0
8
+ Requires-Dist: prompt-build-tool>=0.5.10
9
+ Description-Content-Type: text/markdown
10
+
11
+ # pbt-extra-model-kinds
12
+
13
+ Extra model kinds for [pbt](https://pypi.org/project/prompt-build-tool/):
14
+
15
+ | `model_type` | Module | Runs |
16
+ |----------------|-----------------|--------------------------------------------------------|
17
+ | `python_modal` | `modal_exec` | the bullet's upstream Python in a Modal sandbox |
18
+ | `agent_modal` | `agent_exec` | an OpenCode agent (plus optional MCP server) on Modal |
19
+ | `agent37` | `agent37_exec` | a Hermes agent on an Agent37 workspace (`agent37.md`) |
20
+
21
+ Installing the package is enough: pbt >= 0.5.10 finds the kinds through the
22
+ `pbt.model_kinds` entry point group and loads them on first use. Nothing has
23
+ to import it.
24
+
25
+ Configuration is read from the environment — see each module's docstring.
@@ -0,0 +1,15 @@
1
+ # pbt-extra-model-kinds
2
+
3
+ Extra model kinds for [pbt](https://pypi.org/project/prompt-build-tool/):
4
+
5
+ | `model_type` | Module | Runs |
6
+ |----------------|-----------------|--------------------------------------------------------|
7
+ | `python_modal` | `modal_exec` | the bullet's upstream Python in a Modal sandbox |
8
+ | `agent_modal` | `agent_exec` | an OpenCode agent (plus optional MCP server) on Modal |
9
+ | `agent37` | `agent37_exec` | a Hermes agent on an Agent37 workspace (`agent37.md`) |
10
+
11
+ Installing the package is enough: pbt >= 0.5.10 finds the kinds through the
12
+ `pbt.model_kinds` entry point group and loads them on first use. Nothing has
13
+ to import it.
14
+
15
+ Configuration is read from the environment — see each module's docstring.
@@ -0,0 +1,150 @@
1
+ # Agent37 — what the platform can do, and what the agent37 node uses
2
+
3
+ Reference for `agent37_exec.py`, the *preserve disk between runs* agent node.
4
+ Source: <https://www.agent37.com/docs> (full index: `/docs/llms.txt`) and the
5
+ dashboard Playground (`agent37.com/dashboard/cloud/playground`), which runs any
6
+ call below against a real instance and copies it as cURL.
7
+
8
+ ## How the node maps onto Agent37
9
+
10
+ | pbt | Agent37 / Hermes | Value |
11
+ | - | - | - |
12
+ | Server key (`AGENT37_API_KEY`) or a user's key (Settings) | Workspace | one shared instance per key, named `pbt-shared` |
13
+ | pbt user (signed-in email, else browser session id) | Hermes **profile** | `u-` + `sha256(user)[:20]` |
14
+ | Node | Hermes **session** | `session_id = sha256(node id)[:32]` |
15
+ | Node's folder | dir on the instance's disk | `~/.hermes/profiles/<profile>/pbt/<session>/` |
16
+ | Node's *Create public URL* | public port | `20000 + int(session[:8], 16) % 10000`, label `pbt-<session[:24]>` |
17
+ | Node's *Create browser* | signed URL to the desktop | port 6901, `AGENT37_DESKTOP_TTL_SECONDS` |
18
+
19
+ Per run: `exec` (make profile if new, write its config, make the folder) →
20
+ `POST /v1/responses {profile, session_id, input, stream}` → collect files.
21
+ Instance is brought up only when that fails as unreachable. See the module
22
+ docstring for the lifecycle in full.
23
+
24
+ ## Two planes, one key
25
+
26
+ | Plane | Base URL | Auth header |
27
+ | - | - | - |
28
+ | **Sandbox API** — manage instances | `https://api.agent37.com/v1` | `Authorization: Bearer sk_live_…` |
29
+ | **Agent API** — talk to the agent on one instance | `https://{instanceId}.agent37.app` | `X-Agent37-Key: sk_live_…` |
30
+
31
+ Any request to an instance URL wakes a sleeping instance (held up to ~3 min, then
32
+ `503 wake_timeout`). Sandbox API reads never reset the idle timer.
33
+
34
+ ## Sandbox API — `api.agent37.com/v1`
35
+
36
+ ### Instances — lifecycle
37
+
38
+ | Call | Endpoint | Used by the node |
39
+ | - | - | - |
40
+ | Create | `POST /instances` `{template, name, budget, auto_sleep, idle_timeout_seconds, env…}` | ✅ first run, with auto-sleep |
41
+ | List | `GET /instances` | ✅ find `pbt-shared` by name |
42
+ | Get | `GET /instances/{id}` | ✅ status polling |
43
+ | Edit | `PATCH /instances/{id}` — labels, metadata, env, auto-sleep | ✅ turns auto-sleep on |
44
+ | Start | `POST /instances/{id}/start` — start stopped, wake sleeping | ✅ when the send fails |
45
+ | Stop | `POST /instances/{id}/stop` — disk-only billing until `start` | — (auto-sleep instead) |
46
+ | Restart | `POST /instances/{id}/restart` — same image and files | — |
47
+ | Update | `POST /instances/{id}/update` — new template image, keeps `/home/node` | — |
48
+ | Resize | `POST /instances/{id}/resize` — CPU / memory / disk, same id and URLs | — |
49
+ | Fork | `POST /instances/{id}/fork` — independent copy | — |
50
+ | Delete | `DELETE /instances/{id}` | — (do it by hand to reset everyone) |
51
+
52
+ Statuses: `running`, `sleeping`, `stopped`, `deleting`, `deleted`, `failed`.
53
+ `409 try_again` while a sleep checkpoint is being written — retry with backoff.
54
+
55
+ ### Auto-sleep
56
+
57
+ `auto_sleep: true` + `idle_timeout_seconds` (300–86400, default 900). No bytes on
58
+ any instance URL (or exec) for that long → checkpoint → `sleeping`, billed disk
59
+ only. Wake is transparent and takes seconds; after a long sleep it may boot fresh
60
+ (~2 min, files kept, memory lost), so poll `GET /v1/health` before sending work.
61
+
62
+ ### Access
63
+
64
+ | Call | Endpoint | Used by the node |
65
+ | - | - | - |
66
+ | Exec | `POST /instances/{id}/exec` `{command}` → `{exit_code, stdout, stderr}`; output capped at 512 KB; wakes a sleeper | ✅ profile + config + folder setup, listing outputs |
67
+ | Signed URL | `POST /instances/{id}/signed-url` `{port, ttl_seconds}` | ✅ *Create browser*: noVNC desktop link (port 6901) in the log, before the turn |
68
+ | List public URLs | `GET /instances/{id}/public-ports` | ✅ find the bullet's port by label `pbt-<session>` |
69
+ | Create public URL | `POST /instances/{id}/public-ports` `{port, label}` — one port, permanent, no auth (webhooks, embeds); `DELETE …/public-ports/{port}`; max 50 per instance | ✅ *Create public URL*: port 20000–29999 picked by session id, told to the agent |
70
+ | SSH | your own keypair: ssh, scp, sftp, VS Code Remote | — |
71
+ | Logs / metrics | boot + runtime logs; CPU / memory / disk history | — |
72
+
73
+ ### Budget — managed services (LLM router, Brave Search, Composio, Perflo)
74
+
75
+ | Call | Endpoint | Used by the node |
76
+ | - | - | - |
77
+ | Get budget | `GET /instances/{id}/budget` | — |
78
+ | Set budget | `PATCH /instances/{id}/budget` | set once at create (`AGENT37_BUDGET_MICROS`) |
79
+ | Top up | `POST /instances/{id}/budget/top-up` | — |
80
+ | Usage | `GET /instances/{id}/usage` → `{period, total_micros, by_integration}` | — |
81
+
82
+ Compute is billed separately, per minute awake, from the workspace wallet.
83
+
84
+ ### Templates, backups, crons
85
+
86
+ | Area | What it gives | Used by the node |
87
+ | - | - | - |
88
+ | Templates | `GET /templates[/{name}]`; build from a Dockerfile in the cloud | ✅ `hermes-vnc-desktop` (see server/README.md) |
89
+ | Backups | `GET/POST /instances/{id}/backups`, `POST /instances/{id}/restore` | — |
90
+ | Crons | `/instances/{id}/crons` — a prompt on a schedule, per `profile`, fresh session per firing, wakes the instance; max 50 | — |
91
+ | Integrations | Composio: Gmail, Slack, Notion, 250+ apps, OAuth handled | — |
92
+ | Messaging | Telegram, WhatsApp, Slack, Discord… to the agent | — |
93
+
94
+ ## Agent API — `{instanceId}.agent37.app`
95
+
96
+ | Call | Endpoint | Used by the node |
97
+ | - | - | - |
98
+ | Send a message | `POST /v1/responses` `{input, session_id, profile, model, provider, reasoning_effort, files, stream}` | ✅ one turn per run |
99
+ | Stream | SSE: `response.created`, `response.output_text.delta`, `response.tool_call.started/failed`, `response.completed/failed` | ✅ tool calls go to the run log |
100
+ | Reconnect | `GET /v1/responses/{id}/stream` — replays the turn (~30 min) | — (a dropped stream fails the run) |
101
+ | Cancel | `POST /v1/responses/{id}/cancel` | ✅ on timeout and on `session_busy` |
102
+ | Sessions | `GET/PATCH/DELETE /v1/sessions[/{id}]?profile=` — list, transcript, `active_response_id` | — |
103
+ | Files | `GET /v1/files?path=` list; `/v1/files/content` read; `/v1/files/archive` tar.gz a folder; write; `DELETE` (recursive, no undo) | ✅ `content` to collect outputs |
104
+ | Models | `GET /v1/models?profile=` | — |
105
+ | Health | `GET /v1/health` → `{healthy}` | ✅ after bring-up |
106
+
107
+ ### Sessions and profiles
108
+
109
+ - **Session** = one conversation. Omit `session_id` and one is minted; send an
110
+ unknown 32-hex id and Hermes starts a fresh thread under it — which is how the
111
+ node gets a stable id per node without storing anything.
112
+ - One turn per session at a time: a second send is `409 session_busy` with
113
+ `error.response_id`.
114
+ - **Profile** (Hermes only) = a named setup with its own `SOUL.md`, skills,
115
+ memory, config and session store: `~/.hermes/profiles/<name>`. Lowercase
116
+ letters, digits, `-`, `_`. Unknown → `404 profile_not_found`. Each runs as its
117
+ own process, started on first request, stopped after 10 idle minutes.
118
+ Created with `hermes profile create <name> [--clone]`; configured with
119
+ `hermes --profile=<name> config set …`.
120
+ - Profiles separate config, memory and history — **not the disk**. They are one
121
+ Linux user; an agent can read another profile's files by path.
122
+
123
+ ## Errors the node branches on
124
+
125
+ | Code | Status | Node does |
126
+ | - | - | - |
127
+ | `container_unavailable` | 502 | instance stopped → start it, resend |
128
+ | `container_unreachable`, `upstream_unreachable` | 502 | bring up, resend |
129
+ | `wake_timeout`, `wake_failed`, `host_mesh_not_ready` | 503 | bring up, resend |
130
+ | `not_found` | 404 | instance gone → find / create, resend |
131
+ | `try_again` | 409 | bring up (start retries it with backoff) |
132
+ | `instance_limit_reached` | 409 | create waits for a free slot (`AGENT37_QUEUE_SECONDS`) |
133
+ | `profile_not_found` | 404 | create the profile, resend once |
134
+ | `session_busy` | 409 | cancel the running turn, resend once |
135
+ | `insufficient_balance`, `instance_suspended` | 402 | fail — top up the wallet |
136
+ | `upstream_timeout` | 504 | fail (15 min of silence) |
137
+
138
+ ## Settings the node reads
139
+
140
+ | Env var | Default | Meaning |
141
+ | - | - | - |
142
+ | `AGENT37_API_KEY` | — | server's key; the shared instance. Users can set their own in Settings |
143
+ | `AGENT37_INSTANCE_NAME` | `pbt-shared` | the shared instance's name |
144
+ | `AGENT37_INSTANCE_ID` | `9c7c78hguk` | share this existing instance (server key only); wins over the name |
145
+ | `AGENT37_TEMPLATE` | `hermes-vnc-desktop` | template it is created from |
146
+ | `AGENT37_IDLE_SECONDS` | `600` | auto-sleep after this much idle |
147
+ | `AGENT37_BUDGET_MICROS` | `1000000` | managed-services cap per instance |
148
+ | `AGENT37_MANAGED_MODEL` | off | run on Agent37's model instead of the run's provider key |
149
+ | `AGENT37_QUEUE_SECONDS` / `AGENT37_START_SECONDS` / `AGENT_READY_SECONDS` | 900 / 900 / 240 | waits for a slot, a start, a healthy agent |
150
+ | `AGENT37_DESKTOP_TTL_SECONDS` | `300` | lifetime of the desktop link |
@@ -0,0 +1,20 @@
1
+ [project]
2
+ name = "pbt-extra-model-kinds"
3
+ version = "0.1.0"
4
+ description = "Extra pbt model kinds: python_modal, agent_modal and agent37."
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ dependencies = [
8
+ "prompt-build-tool>=0.5.10",
9
+ "modal>=1.0",
10
+ "httpx>=0.27",
11
+ ]
12
+
13
+ [project.entry-points."pbt.model_kinds"]
14
+ python_modal = "pbt_extra_model_kinds.modal_exec"
15
+ agent_modal = "pbt_extra_model_kinds.agent_exec"
16
+ agent37 = "pbt_extra_model_kinds.agent37_exec"
17
+
18
+ [build-system]
19
+ requires = ["hatchling"]
20
+ build-backend = "hatchling.build"
@@ -0,0 +1,9 @@
1
+ """
2
+ Extra pbt model kinds. Importing this package registers all of them:
3
+ ``python_modal`` (`modal_exec`), ``agent_modal`` (`agent_exec`) and
4
+ ``agent37`` (`agent37_exec`).
5
+ """
6
+
7
+ from . import agent37_exec, agent_exec, modal_exec
8
+
9
+ __all__ = ["agent37_exec", "agent_exec", "modal_exec"]