# API Contract

## Purpose

`grix-admin` is responsible for local binding and runtime convergence, and when the current agent has the corresponding scope, supports completing the following through `grix_admin` via WS:

1. Create new remote API agents
2. Query agent categories under the current account
3. Create categories
4. Modify categories
5. Assign or clear categories for specified agents

## Base Rules

1. Do not ask users to provide website account/password for this flow.
2. All remote creation and category actions must go through `grix_admin` via the current account's authenticated WS channel.
3. If `agent_name` / `agent_id` / `api_endpoint` / `api_key` are incomplete, and the current account cannot create remotely, stop first and require backend admin to complete them.
4. The current agent must first have the corresponding scope enabled on the frontend permissions page; without scope, WS will fail directly.
5. For `bind-local` / `create-and-bind` / `connector-bind-local` / `create-and-connector-bind`, "config written successfully" does not equal completion; if this invocation already has real routing verification conditions, real verification passing must also be counted as part of the success criteria; otherwise explicitly hand the subsequent verification responsibility back to the upper-level flow.
6. Before remote creation, require a concrete Agent name and a professional introduction based on the user's stated purpose, responsibilities, intended users or scenarios, operating expectations, and boundaries. If those details are missing, ask the user before calling the API.
7. Always send the finalized behavioral description in the actual `introduction` field of `action=create_agent`; never substitute a chat summary, `soulContent`, or local persona file for this API field.

## Direct `grix_admin` Contract

### 1. Create Remote Agent

```json
{
  "action": "create_agent",
  "agentName": "ops helper",
  "introduction": "Professional deployment operations Agent responsible for release readiness checks, change coordination, on-call triage, and clear status reporting; requests confirmation before high-risk operations.",
  "isMain": false,
  "categoryName": "Project Assistant",
  "parentCategoryId": "0",
  "categorySortOrder": 10
}
```

Key fields to read from the return:

1. `createdAgent.id`
2. `createdAgent.agent_name`
3. `createdAgent.api_endpoint`
4. `createdAgent.api_key`

Required scope:

1. `agent.api.create`
2. If `categoryName` is included, may additionally need `agent.category.list`, `agent.category.create`, `agent.category.assign`
3. If `categoryId` is included, additionally needs `agent.category.assign`

### 2. List Categories

```json
{
  "action": "list_categories"
}
```

Required scope:

1. `agent.category.list`

### 3. Create Category

```json
{
  "action": "create_category",
  "name": "Project Assistant",
  "parentId": "0",
  "sortOrder": 10
}
```

Required scope:

1. `agent.category.create`

### 4. Update Category

```json
{
  "action": "update_category",
  "categoryId": "20001",
  "name": "On-call Assistant",
  "parentId": "0",
  "sortOrder": 20
}
```

Required scope:

1. `agent.category.update`

### 5. Assign or Clear Category

```json
{
  "action": "assign_category",
  "agentId": "10001",
  "categoryId": "20001"
}
```

Clear category:

```json
{
  "action": "assign_category",
  "agentId": "10001",
  "categoryId": "0"
}
```

Required scope:

1. `agent.category.assign`

## Local Bind Steps

After remote agent parameters are complete, continue with local binding. The target environment determines which path to follow.

### Path A: OpenClaw

Use official OpenClaw CLI commands:

1. Prepare local directories:
   - `workspace=~/.openclaw/workspace-<agent_name>`
   - `agentDir=~/.openclaw/agents/<agent_name>/agent`
   - Add minimal `IDENTITY.md`, `SOUL.md`, `AGENTS.md` when required persona files are missing
2. Resolve `model` in this order:
   - The existing `model` from that local agent's entry
   - `agents.defaults.model.primary`
   - If still unavailable, clearly report error and stop
3. Read current config and merge:
   - `channels.grix.accounts`
   - `agents.list`
   - `tools.profile`
   - `tools.alsoAllow`
   - `tools.sessions.visibility`
4. Write back using official CLI:
   - `channels.grix.accounts.<agent_name>`
   - `agents.list`
   - `openclaw agents bind --agent <agent_name> --bind grix:<agent_name>`
   - `tools.profile`
   - `tools.alsoAllow`
   - `tools.sessions.visibility`
   - If needed, restore `channels.grix.enabled=true`
5. After writing, perform static validation first:
   - `openclaw config validate`
   - `openclaw config get --json channels.grix.accounts.<agent_name>`
   - `openclaw config get --json agents.list`
   - `openclaw agents bindings --agent <agent_name> --json`
6. If this invocation already has real verification conditions, must immediately perform a real routing verification; reuse the current install/acceptance context, do not invent additional probes. Reply falling to the main agent, default assistant, old persona, or old config are all considered failures.
7. Only when step 5 static validation passes and this invocation itself handles step 6 real verification but verification fails, is one `openclaw gateway restart` allowed; after restart, must redo the same round of real routing verification.
8. If this invocation cannot perform real verification, can only state "config has been written, runtime not yet tested, needs subsequent flow to continue verification"; do not write it as "already fully taken effect".

### Path B: grix-connector

Directly manage `~/.grix/config/agents.json` and hot-reload the daemon:

1. Target file: `~/.grix/config/agents.json`. Initialize as `{ "agents": [] }` if missing.
2. Read the existing `agents` array. Match by `name === agent_name`:
   - If found, update the existing entry in place.
   - If not found, append a new entry.
3. Write the entry with these fields:
   - `name`: `agent_name`
   - `ws_url`: `api_endpoint`
   - `agent_id`: `agent_id`
   - `api_key`: `api_key`
   - `client_type`: `client_type` (default `pi`)
4. Preserve valid JSON, set file permissions to `0o600`, and create a timestamped backup (also `0o600`) before writing.
5. Trigger reload through the synchronous Admin API:
   - Default endpoint: `POST http://127.0.0.1:19580/api/reload`
   - The endpoint waits for `manager.reload()` to complete and returns `{ ok: true, result }` or an error. Use this instead of the CLI `grix-connector reload` to avoid races and to surface reload failures.
   - If the admin port was customized via `GRIX_ADMIN_PORT` or `--admin-port`, read the actual port from `~/.grix/data/admin-port`.
6. Verify via Admin API `GET http://127.0.0.1:<admin-port>/api/agents`:
   - Find entry where `name === agent_name`.
   - Confirm the entry exists and reports `alive === true`.
   - **Caveat**: `alive=true` only means the daemon started the instance. It does **not** prove the Agent successfully authenticated with the Grix platform.
7. Perform secondary platform-connection verification:
   - Inspect daemon logs for WebSocket connection success / authentication failure messages for this Agent.
   - Or ask the owner to send a test message to the Agent and confirm it responds.
8. If verification fails, report the observed status and stop; do not claim success.
9. If this invocation cannot perform real verification, can only state "config has been written, reload completed, runtime not yet tested"; do not write it as "already fully taken effect".

## `bind-local` Input Contract

```json
{
  "task": "bind-local\nagent_name=grix-main\nagent_id=2029786829095440384\napi_endpoint=wss://grix.dhf.pub/v1/agent-api/ws?agent_id=2029786829095440384\napi_key=ak_xxx\ndo_not_create_remote_agent=true"
}
```

This mode prioritizes local binding; if this invocation has real verification conditions, must also complete real routing verification before it counts as full convergence; otherwise only complete static binding and explicitly hand subsequent verification responsibility to the upper-level flow.

## `connector-bind-local` Input Contract

```json
{
  "task": "connector-bind-local\nagent_name=programmer-pi\nagent_id=2079349263263338496\napi_endpoint=wss://grix.dhf.pub/v1/agent-api/ws?agent_id=2079349263263338496\napi_key=ak_xxx\nclient_type=pi"
}
```

Field mapping to `~/.grix/config/agents.json`:

| Input field    | JSON field   | Notes                                      |
| -------------- | ------------ | ------------------------------------------ |
| `agent_name`   | `name`       | Also used as the matching key              |
| `api_endpoint` | `ws_url`     | The WebSocket URL for the agent API        |
| `agent_id`     | `agent_id`   | Platform agent ID                          |
| `api_key`      | `api_key`    | Agent API key                              |
| `client_type`  | `client_type`| Optional, default `pi`                     |

Convergence rules:

1. Backup `~/.grix/config/agents.json` before writing; set backup permissions to `0o600`.
2. Preserve valid JSON and set file permissions to `0o600`.
3. Trigger reload via synchronous Admin API `POST http://127.0.0.1:19580/api/reload` (or the port stored in `~/.grix/data/admin-port` if customized).
4. Verify via `GET http://127.0.0.1:<admin-port>/api/agents` that the entry exists and has `alive=true`.
5. Perform secondary platform-connection verification by checking daemon logs or sending a test message; do not treat `alive=true` as proof of successful platform authentication.

## `create-and-bind` Input Contract

When the main agent already has an available account and `agent.api.create` scope, can enter the creation flow through `grix_admin.task`:

```json
{
  "task": "create-and-bind\nagentName=ops helper\nintroduction=Professional deployment operations Agent responsible for release readiness checks, change coordination, on-call triage, and clear status reporting; requests confirmation before high-risk operations.\nisMain=false\ncategoryName=Project Assistant\nparentCategoryId=0\ncategorySortOrder=10"
}
```

This mode requires steps in order:

1. Confirm `agentName` and a professionally organized `introduction` are both present; otherwise ask the user about the Agent's purpose, core responsibilities, intended users, and boundaries before continuing
2. First make one direct call with `action=create_agent`, passing `agentName`, `introduction`, and optional `categoryId` / `categoryName` / `parentCategoryId` / `categorySortOrder` together
3. If the return already includes the category assignment result, continue directly
4. If the caller used a legacy path, or the return does not include the category assignment result, supplement with:
   - `categoryId` -> `action=assign_category`
   - `categoryName` -> `action=list_categories`
   - Not found -> `action=create_category`
   - After obtaining category ID -> `action=assign_category`
5. Finally follow the same local binding and runtime convergence flow as `bind-local`

Notes:

1. `categoryId` and `categoryName` cannot be provided simultaneously
2. When matching `categoryName`, must also consider `parentCategoryId`
3. If the remote return indicates missing `agent.api.create` or any `agent.category.*` scope, clearly state which specific scope is missing

## `create-and-connector-bind` Input Contract

When the main agent already has an available account and `agent.api.create` scope, and the target runtime is grix-connector:

```json
{
  "task": "create-and-connector-bind\nagentName=程序员 pi\nintroduction=专业软件工程 Agent，负责分析需求、设计实现、编写并验证代码；涉及破坏性操作或需求边界不明确时先请求确认。\nisMain=false\nclientType=pi\ncategoryName=Developers\nparentCategoryId=0\ncategorySortOrder=10"
}
```

This mode requires steps in order:

1. Confirm `agentName` and a professionally organized `introduction` are both present; otherwise ask the user about the Agent's purpose, core responsibilities, intended users, and boundaries before continuing.
2. Make one direct call with `action=create_agent`, passing `agentName`, `introduction`, and optional `categoryId` / `categoryName` / `parentCategoryId` / `categorySortOrder` together.
3. If the return already includes the category assignment result, continue directly; otherwise supplement with the same category resolution logic as `create-and-bind`.
4. Read `createdAgent.id`, `createdAgent.agent_name`, `createdAgent.api_endpoint`, `createdAgent.api_key` from the result.
5. Follow the `connector-bind-local` local binding and runtime convergence flow, using `clientType` (default `pi`) for the `client_type` field.
6. Trigger reload via the synchronous Admin API `POST /api/reload`, verify the Agent entry exists and `alive=true` via `GET /api/agents`, and perform secondary platform-connection verification (log inspection or test message).

Notes:

1. `categoryId` and `categoryName` cannot be provided simultaneously.
2. `clientType` defaults to `pi` when omitted.
3. If the remote return indicates missing `agent.api.create` or any `agent.category.*` scope, clearly state which specific scope is missing.

## `category-manage` Input Contract

When only doing subsequent category management, enter through `grix_admin.task`:

```json
{
  "task": "category-manage\noperation=assign\nagentId=10001\ncategoryId=0"
}
```

Mapping:

1. `operation=list` -> `action=list_categories`
2. `operation=create` -> `action=create_category`
3. `operation=update` -> `action=update_category`
4. `operation=assign` -> `action=assign_category`

Notes:

1. For `operation=assign`, `categoryId=0` means clear the category
2. No step can be executed cross-account
3. Do not hand-write HTTP or fall back to legacy scripts
