From 96be0accae8917419e8a604000d74715931b6c75 Mon Sep 17 00:00:00 2001 From: Saul Carlin Date: Mon, 16 Feb 2026 23:31:29 -0500 Subject: [PATCH] docs: rewrite Convos POOL-MANAGER.md as factual HTTP API reference --- extensions/convos/POOL-MANAGER.md | 415 +++++++++++++++++++----------- 1 file changed, 259 insertions(+), 156 deletions(-) diff --git a/extensions/convos/POOL-MANAGER.md b/extensions/convos/POOL-MANAGER.md index 0950e9dd49c0..969b666f907f 100644 --- a/extensions/convos/POOL-MANAGER.md +++ b/extensions/convos/POOL-MANAGER.md @@ -1,66 +1,94 @@ -# Convos Pool Manager Integration +# Convos HTTP API -How to provision OpenClaw instances with Convos from a pool manager. +HTTP endpoints registered by the Convos plugin. All endpoints accept JSON request bodies and return JSON responses. Non-matching HTTP methods return `405`. -## Architecture +## Authentication -Each OpenClaw instance runs exactly one Convos conversation. The pool manager provisions fresh instances, each bound to a single conversation. No setup flow or onboarding is needed. +All endpoints check for a `poolApiKey` in the OpenClaw config at `channels.convos.poolApiKey`. When set, requests must include: -## Provisioning Flow +``` +Authorization: Bearer +``` -### 1. Configure the instance +When `poolApiKey` is not configured, all requests are allowed without authentication. -The pool manager should configure the OpenClaw instance with an API key and enable the Convos plugin. This can be done via CLI commands or by writing the config file directly. +Unauthorized requests return `401`: -```bash -# Set the AI provider token -openclaw config set auth.profiles.anthropic:default.provider anthropic -openclaw config set auth.profiles.anthropic:default.mode token -# (API key is set via ANTHROPIC_API_KEY env var or openclaw config set) +```json +{ "error": "Unauthorized" } +``` + +## Constraints + +- **One conversation per process.** `/convos/conversation` and `/convos/join` return `409` if the instance is already bound to a conversation. +- **No restart required.** After `/convos/conversation` or `/convos/join`, the instance is immediately live with full message handling. +- **XMTP keys are created lazily.** Enabling the plugin does not create XMTP state. Keys are created on the first `/convos/conversation` or `/convos/join` call. +- **`/convos/setup` endpoints are for the Control UI onboarding flow** (show QR, wait for join, confirm). They are separate from `/convos/conversation`. +- **Setup auto-cleanup.** An active setup instance is automatically stopped after 10 minutes if `/convos/setup/complete` is not called. +- **`instructions` parameter.** When provided to `/convos/conversation` or `/convos/join`, the value is written to `~/.openclaw/workspace/INSTRUCTIONS.md`, which is loaded into the agent system prompt on each invocation. + +## Endpoints + +| Method | Path | Description | +| ------ | --------------------------- | ----------------------------------------------- | +| GET | `/convos/status` | Health check | +| POST | `/convos/conversation` | Create a new conversation | +| POST | `/convos/join` | Join an existing conversation via invite URL | +| POST | `/convos/conversation/send` | Send a message into the active conversation | +| POST | `/convos/rename` | Rename the conversation and agent profile | +| POST | `/convos/lock` | Lock or unlock the conversation | +| POST | `/convos/explode` | Destroy the conversation (irreversible) | +| POST | `/convos/setup` | Start onboarding setup flow (Control UI) | +| GET | `/convos/setup/status` | Poll setup join status | +| POST | `/convos/setup/complete` | Finalize setup and write config | +| POST | `/convos/setup/cancel` | Cancel an active setup | +| POST | `/convos/reset` | Re-run setup with a fresh identity (force mode) | + +--- + +### GET /convos/status -# Enable convos plugin and set environment -openclaw config set channels.convos.enabled true -openclaw config set channels.convos.env dev +Returns instance health. The `streaming` field reflects whether the XMTP child process is alive. + +**Response (no conversation bound):** + +```json +{ "ready": true, "conversation": null, "streaming": false } ``` -Or write `~/.openclaw/openclaw.json` directly: +**Response (conversation bound, stream alive):** ```json -{ - "auth": { - "profiles": { - "anthropic:default": { - "provider": "anthropic", - "mode": "token" - } - } - }, - "channels": { - "convos": { - "enabled": true, - "env": "dev" - } - } -} +{ "ready": true, "conversation": { "id": "abc123..." }, "streaming": true } ``` -### 2. Start the gateway +**Response (conversation bound, stream dead):** -```bash -openclaw gateway run --port 18789 +```json +{ "ready": true, "conversation": { "id": "abc123..." }, "streaming": false } ``` -At this point, the Convos plugin is loaded and HTTP routes are available, but no XMTP identity or conversation exists yet. +--- + +### POST /convos/conversation -### 3. Create a conversation +Create a new XMTP identity and conversation. Writes config and starts the message stream and join-request processor. Returns `409` if the instance is already bound. -```bash -curl -s -X POST http://localhost:18789/convos/conversation \ - -H 'Content-Type: application/json' \ - -d '{"name":"My Agent","env":"dev"}' | jq . -``` +**Request body:** -Response: +| Param | Type | Default | Description | +| ------------ | ------ | ---------------- | ------------------------------------------------------ | +| name | string | `"Convos Agent"` | Conversation display name | +| profileName | string | value of `name` | Agent profile display name (independent of convo name) | +| profileImage | string | — | URL for the agent profile image | +| description | string | — | Conversation description | +| imageUrl | string | — | Conversation image URL | +| permissions | string | — | `"all-members"` or `"admin-only"` | +| env | string | from config | `"dev"` or `"production"` | +| accountId | string | — | Account ID (for multi-account setups) | +| instructions | string | — | Written to `INSTRUCTIONS.md` for the agent prompt | + +**Response (`200`):** ```json { @@ -70,168 +98,243 @@ Response: } ``` -This single call does everything: +**Response (`409`):** + +```json +{ + "error": "Instance already bound to a conversation. Terminate process and provision a new one." +} +``` + +--- + +### POST /convos/join + +Join an existing conversation via invite URL. Creates an XMTP identity, sends a join request, and waits up to 60 seconds for acceptance. Writes config and starts the message stream on success. Returns `409` if the instance is already bound. -- Creates a new XMTP identity (private keys stored in `~/.convos/identities/`) -- Creates a new XMTP conversation -- Writes config (`ownerConversationId`, `identityId`, `env`, `enabled`) -- Starts the message stream and join-request processor -- The instance is immediately ready to send and receive messages +**Request body:** -### 4. Share the invite +| Param | Type | Default | Description | +| ------------ | ------ | ---------------- | ------------------------------------------------- | +| inviteUrl | string | **required** | Full invite URL or slug | +| profileName | string | `"Convos Agent"` | Agent profile display name | +| profileImage | string | — | URL for the agent profile image | +| env | string | from config | `"dev"` or `"production"` | +| accountId | string | — | Account ID (for multi-account setups) | +| instructions | string | — | Written to `INSTRUCTIONS.md` for the agent prompt | -Give the `inviteUrl` to the user. They open it in the Convos iOS app to join the conversation. +**Response (`200` — joined):** -### Alternative: Join an existing conversation +```json +{ "status": "joined", "conversationId": "abc123..." } +``` -If the user already created a conversation and has an invite: +**Response (`200` — join request not accepted within timeout):** -```bash -curl -s -X POST http://localhost:18789/convos/join \ - -H 'Content-Type: application/json' \ - -d '{"inviteUrl":"https://dev.convos.org/v2?i=...","name":"My Agent","env":"dev"}' | jq . +```json +{ "status": "waiting_for_acceptance" } ``` -## When are XMTP keys created? +**Response (`409`):** -XMTP identity and private keys are created lazily on the first `/convos/conversation` or `/convos/join` call. Enabling the plugin does NOT create any XMTP state. The `convos` CLI manages keys internally in `~/.convos/identities/`. +```json +{ + "error": "Instance already bound to a conversation. Terminate process and provision a new one." +} +``` + +--- -## Important constraints +### POST /convos/conversation/send -- **One conversation per process.** Calling `/convos/conversation` or `/convos/join` a second time returns `409`. To provision a new conversation, terminate the process and start a fresh one. -- **No setup flow needed.** The `/convos/setup` endpoints exist for the Control UI onboarding experience (show QR, wait for join, confirm). The pool manager should skip these entirely. -- **No restart needed.** After `/convos/conversation` or `/convos/join`, the instance is immediately live with full message handling. No gateway restart required. +Send a text message into the active conversation. -## Customizing agent behavior +**Request body:** -Write operator-level directives to `~/.openclaw/workspace/INSTRUCTIONS.md` to customize the agent's system prompt. This is an OpenClaw core feature (not Convos-specific) — any text in that file is injected into every agent invocation as operator-provided instructions. +| Param | Type | Default | Description | +| ------- | ------ | ------------ | ---------------- | +| message | string | **required** | The message text | -- Write the file before or after starting the gateway; it is loaded fresh on each agent invocation, so changes take effect immediately without a restart. -- Use it for personality, tone, tool restrictions, domain knowledge, or any behavioral guardrails. -- Each instance can have its own `INSTRUCTIONS.md`, or you can bake a shared one into your golden image. +**Response (`200`):** -Example `~/.openclaw/workspace/INSTRUCTIONS.md`: +```json +{ "success": true, "messageId": "msg-id-here" } +``` -```markdown -You are a customer support agent for Acme Corp. +**Response (`400`):** -- Always greet the user by name if known. -- Never discuss competitor products. -- Escalate billing disputes to a human operator. +```json +{ "error": "No active conversation" } ``` -For the golden checkpoint approach, write `INSTRUCTIONS.md` during step 1 (building the golden image) so every instance launched from the checkpoint inherits the same directives. +--- -## HTTP API Reference +### POST /convos/rename -| Method | Path | Description | -| ------ | --------------------------- | ------------------------------------------------- | -| GET | `/convos/status` | Health check — ready, conversation, streaming | -| POST | `/convos/conversation` | Create a new conversation (returns invite URL) | -| POST | `/convos/join` | Join an existing conversation via invite URL | -| POST | `/convos/conversation/send` | Send a message | -| POST | `/convos/rename` | Rename the conversation | -| POST | `/convos/lock` | Lock/unlock the conversation (`{"unlock": true}`) | -| POST | `/convos/explode` | Destroy the conversation (irreversible) | +Rename the conversation and update the agent profile name. -All endpoints accept JSON bodies. All return JSON responses. +**Request body:** -## Request body parameters +| Param | Type | Default | Description | +| ----- | ------ | ------------ | ------------ | +| name | string | **required** | The new name | -### POST /convos/conversation +**Response (`200`):** -| Param | Type | Default | Description | -| --------- | ------ | ----------- | ------------------------------------- | -| name | string | "OpenClaw" | Conversation and profile display name | -| env | string | from config | "dev" or "production" | -| accountId | string | "default" | Account ID (for multi-account setups) | +```json +{ "ok": true } +``` -### POST /convos/join +**Response (`400`):** -| Param | Type | Default | Description | -| --------- | ------ | ----------- | --------------------- | -| inviteUrl | string | required | Invite URL or slug | -| name | string | "OpenClaw" | Profile display name | -| env | string | from config | "dev" or "production" | -| accountId | string | "default" | Account ID | +```json +{ "error": "No active conversation" } +``` -### GET /convos/status +--- -Returns the instance health. The `streaming` field confirms the XMTP child process is alive (not just that the instance variable is set). +### POST /convos/lock -```jsonc -// Gateway up, no conversation bound -{ "ready": true, "conversation": null, "streaming": false } +Lock or unlock the conversation. -// Conversation bound, XMTP stream alive -{ "ready": true, "conversation": { "id": "abc..." }, "streaming": true } +**Request body:** -// Conversation bound, XMTP stream dead -{ "ready": true, "conversation": { "id": "abc..." }, "streaming": false } +| Param | Type | Default | Description | +| ------ | ------- | ------- | ---------------------------- | +| unlock | boolean | `false` | Set `true` to unlock instead | + +**Response (`200`):** + +```json +{ "ok": true, "locked": true } ``` -## Recommendation: Golden Checkpoint Instead of Pool Manager +**Response (`400`):** -Because XMTP identity creation happens lazily on `/convos/conversation`, you can eliminate the pool manager entirely using a golden checkpoint approach: +```json +{ "error": "No active conversation" } +``` -### The insight +--- -A fully configured OpenClaw instance — with API key, plugin enabled, env set, gateway ready — is identical before the `/convos/conversation` call. No XMTP state exists yet. This means you can snapshot this state once and stamp out copies on demand. +### POST /convos/explode -### Golden checkpoint flow +Destroy the conversation. Irreversible. The instance is unbound after this call. -1. **Build the golden image once:** +**Request body:** empty or `{}` - ```bash - # Configure everything except the conversation - openclaw config set auth.profiles.anthropic:default.provider anthropic - openclaw config set auth.profiles.anthropic:default.mode token - openclaw config set channels.convos.enabled true - openclaw config set channels.convos.env dev +**Response (`200`):** - # (Optional) Write operator-level directives - mkdir -p ~/.openclaw/workspace - cat > ~/.openclaw/workspace/INSTRUCTIONS.md << 'EOF' - You are a helpful assistant for Acme Corp. - EOF - ``` +```json +{ "ok": true, "exploded": true } +``` + +**Response (`400`):** + +```json +{ "error": "No active conversation" } +``` + +--- + +### POST /convos/setup + +Start the Control UI onboarding flow. Creates a new XMTP identity and conversation, generates a QR code, and starts an instance that accepts join requests. If a setup is already running and `force` is not set, returns the cached response. + +**Request body:** + +| Param | Type | Default | Description | +| --------- | ------- | -------------- | ---------------------------------------- | +| name | string | — | Profile display name | +| env | string | `"production"` | `"dev"` or `"production"` | +| accountId | string | — | Account ID | +| force | boolean | `false` | Tear down existing setup and start fresh | + +**Response (`200`):** + +```json +{ + "inviteUrl": "https://dev.convos.org/v2?i=...", + "conversationId": "abc123...", + "qrDataUrl": "data:image/png;base64,..." +} +``` - Snapshot this as your golden checkpoint (container image, VM snapshot, Sprite checkpoint, etc). No XMTP keys, no conversations, no per-instance state. +--- -2. **On demand, launch from checkpoint:** - - Restore the golden checkpoint into a fresh instance - - Start the gateway - - `POST /convos/conversation` — creates XMTP identity + conversation in one call - - Instance is immediately live +### GET /convos/setup/status -3. **Each instance is fully independent:** - - Own XMTP identity (created at launch, not baked into the image) - - Own conversation - - Own `~/.convos/identities/` directory - - No shared state between instances +Poll whether a user has joined the setup conversation. -### Why this eliminates the pool manager +**Response (`200` — no join yet):** + +```json +{ "active": true, "joined": false, "joinerInboxId": null } +``` -| Pool manager approach | Golden checkpoint approach | -| ---------------------------------------------- | --------------------------------------------------------- | -| Pre-provisions N instances, keeps them warm | Launches instances on demand from snapshot | -| Manages a registry of available instances | No registry — each instance is stateless until first call | -| Assigns pre-created conversations to users | Creates conversation at the moment the user needs it | -| Wastes resources on idle instances | Zero idle cost — instances only exist when needed | -| Complex: health checks, recycling, rebalancing | Simple: launch, call one endpoint, done | +**Response (`200` — user joined):** + +```json +{ "active": true, "joined": true, "joinerInboxId": "inbox-id-here" } +``` + +**Response (`200` — no active setup):** + +```json +{ "active": false, "joined": false, "joinerInboxId": null } +``` + +--- + +### POST /convos/setup/complete + +Finalize setup. Writes `identityId`, `ownerConversationId`, `env`, and `enabled` to the OpenClaw config. Adds the joiner's inbox ID to `channels.convos.allowFrom`. Stops and cleans up the setup instance. + +**Request body:** empty or `{}` + +**Response (`200`):** + +```json +{ "saved": true, "conversationId": "abc123..." } +``` + +**Response (`400`):** + +```json +{ "error": "No active setup to complete. Run convos.setup first." } +``` + +--- + +### POST /convos/setup/cancel + +Cancel an active setup. Stops the setup instance and discards the pending config. + +**Request body:** empty or `{}` + +**Response (`200`):** + +```json +{ "cancelled": true } +``` + +If no setup was active: + +```json +{ "cancelled": false } +``` -### Cold start time +--- -The only tradeoff is cold start time. Launching from a golden checkpoint adds: +### POST /convos/reset -- Checkpoint restore time (platform-dependent) -- Gateway startup (~1-3s) -- `/convos/conversation` call (~2-5s for XMTP identity creation + conversation) +Re-run setup with a fresh identity. Equivalent to `/convos/setup` with `force: true`. -Total: roughly 5-10 seconds from launch to a live conversation. If this is acceptable, no pool manager is needed. +**Request body:** -### When you still need a pool manager +| Param | Type | Default | Description | +| --------- | ------ | -------------- | ------------------------- | +| env | string | `"production"` | `"dev"` or `"production"` | +| accountId | string | — | Account ID | -- Cold start time is unacceptable (sub-second provisioning required) -- You need to pre-assign conversations before users arrive -- You're running on infrastructure that doesn't support fast checkpoints +**Response:** Same as `/convos/setup`.