---
name: identity
description: |
  UserPool login, TIP token, credential hosting, and tool risk evaluation. Activate when user needs to check identity (whoami/status), log in, list/add credentials, manage env bindings, configure the plugin, diagnose risky tool calls, or work in identity.headless (workload TIP / machine identity).
  Also activates for: 用户说登录、查身份、获取凭据、添加/配置API密钥、绑定环境变量、配置插件、风险检查、无头/机器身份（headless）与 TIP。
metadata:
  {
    "openclaw":
      {
        "emoji": "🔐",
        "skillKey": "identity",
        "requires": { "config": ["plugins.entries.agent-identity.enabled"] },
      },
  }
---

# Agent Identity

Use the agent-identity plugin for UserPool OIDC login, TIP token, credential hosting (OAuth2, API key), and optional tool risk approval (AuthZ).

When **`plugins.entries.agent-identity.config.identity.headless`** is **true** (workload / machine identity): there is **no interactive UserPool login**; the workload **TIP** is minted via AK/SK (`GetWorkloadAccessToken`). Treat **`identity_whoami` / `identity_status`** as reflecting **TIP presence** (`loggedIn` ≈ valid TIP), not an end-user OIDC session. **`identity_login`** is disabled (`state: "headless_disabled"`). **`identity_get_session_token`** is unavailable. **`identity_get_role_credentials`** must use **`useTip: true`**. **`identity_list_credentials`** still calls the control plane (SigV4); **`identity_list_roles`** lists role providers **excluding** rows whose `IdentityPool.UserPool` is non-empty (user-pool-bound STS is hidden). **`identity_exchange_credential`** with `tip_required`: use **`identity_get_tip_token`** (or local server **`GET /token`**) instead of suggesting login. **Gateway `identity.session.put` / `identity.session.get`** return **`NOT_ENABLED`** even if `webchatSessionExchange` is true.

**Agent rules:**

- Call tools directly. Never suggest slash commands to the user.
- When any tool returns an `authUrl` field, **always display it as a markdown link** to the user, e.g. `[Click here to log in](https://...)` or `[Click here to authorize](https://...)`. Never paste the bare URL as plain text — long OAuth URLs with special characters get truncated by chat platforms.
- **The user is already authenticated** by the time you receive a message — the plugin gates unauthenticated traffic before the LLM is ever invoked. Do NOT preemptively call `identity_whoami` / `identity_login` to "check" or "ensure" login before other tools; only invoke them when the user explicitly asks (login, logout, switch account, "who am I").
- **Always report permission errors to the user.** When a tool call is blocked by an `[agent-identity]` error (e.g. `CheckPermission denied`, `requires approval`, `Failed to verify permission`), tell the user clearly which tool/skill was blocked and the reason — never silently skip or fall back. For `requires approval`, OpenClaw will show the native approval UI; wait for the user before retrying. For `CheckPermission denied`, suggest contacting an administrator.

**Response style for identity tool results:**

- Treat fields like `message`, `hint`, `summary` in identity tool JSON results as **status descriptors for you, not finished prose for the user**. Do not parrot them verbatim — re-phrase them in the user's language and current conversational tone.
- Reply to the user **concisely** (usually one short sentence). Do not re-explain OIDC, TIP tokens, sessions, or other internals — the user does not care about plumbing.
- When `identity_login` returns `alreadyLoggedIn: true`, just acknowledge in one line (e.g. "You are already logged in." / "你已登录，可以继续操作了。"). Do NOT lecture about token refresh.
- When `identity_fetch` succeeds, confirm the credential in one line and, if no binding exists yet, mention `identity_set_binding` as an optional next step — but do not run it without being asked.
- When `identity_whoami` / `identity_status` returns rich detail, summarise the 1–2 facts the user actually asked about; do not dump the full JSON.

## Tool Parameters

### identity_login

Starts OIDC login or refreshes TIP. **Only call when the user explicitly asks to log in, switch account, or re-authenticate** (e.g. "重新登录", "切换账号", "sign in again"). The plugin already gates unauthenticated traffic, so do NOT call this as a precondition for other tools. No params.

Returns:

- **Headless mode** (`identity.headless`): `{ ok: false, state: "headless_disabled", hint }` — there is no OIDC login; tell the user the workload TIP is obtained automatically (e.g. `identity_get_tip_token` / local **`GET /token`**). Do **not** show an auth URL.
- Already logged in: `{ ok: true, sub, message }` — tell the user they are already authenticated.
- Needs login: `{ ok: false, authUrl, message }` — **display `authUrl` as a markdown link** and tell them to open it. A success notification will be pushed to the chat after they authorize.
- Error: `{ error, authUrl: null }` — report the error.

### identity_whoami

Brief identity check. **Only call when the user asks** "who am I", "查身份", "当前登录状态". Do NOT use as a defensive pre-check before other tools. No params.

Returns: `sub`, `hasTip`, `loggedIn`, `sessionLoginAt`, `sessionExpiresAt`, `tipIssuedAt`, `tipExpiresAt`, `tipExpiresInSeconds`, `tipChain`.

### identity_status

Full status including credentials and bindings. **Call when the user asks** "status", "查看完整状态", "我的凭据和绑定". No params.

Returns: `loggedIn`, `sub`, `hasTip`, `session` (loginAt, expiresAt), `tip` (issuedAt, expiresAt, chain), `credentialProviders`, `bindings`.

### identity_get_tip_token

Returns the workload **TIP JWT** for the current session. **Call when** a skill or flow needs the token for downstream Volcengine / Ark APIs, or when diagnosing missing TIP in **`identity.headless`**. No params. **Never** echo the raw token in chat.

### identity_logout

Clear session and TIP. **Call when:** "logout", "登出", "sign out". No params.

### identity_list_credentials

Lists available credential providers and stored credentials. Supports filtering by name or flow to locate a specific provider without paging through all results. **Call when:** "有哪些服务可以连接", "what providers are available", "我添加了哪些凭据", "list my credentials", "查找某个 provider"

| Param  | Type   | Required | Description                                                        |
| ------ | ------ | -------- | ------------------------------------------------------------------ |
| `page` | number | No       | Page number (default: 1)                                           |
| `name` | string | No       | Filter providers by name (exact or prefix match)                   |
| `flow` | string | No       | Filter providers by flow, e.g. `M2M` or `USER_FEDERATION`         |

When looking for a specific provider (e.g. before `identity_fetch`), prefer passing `name` filter instead of paging through results.

Returns: `providers`, `storedOnly`, `page`, `hasMore`.

### identity_list_roles

Lists **STS role credential providers** (not OAuth/API key — use `identity_list_credentials` for those). **Call when:** "role credentials", "STS providers", "IAM role 凭据", "有哪些角色凭据". In normal mode this expects a user OIDC session and may narrow by configured user pool. In **`identity.headless`** mode the same API is called **without** `UserPoolName`; results **exclude** providers whose `IdentityPool.UserPool` object is non-empty (user-pool-bound). Use **`identity_get_role_credentials`** with **`useTip: true`** for workload STS.

Optional param: `name` — prefix filter on provider name.

Returns: `providers` (each may include `identitySource`). To obtain temporary keys for a provider, use `identity_get_role_credentials` with that provider name.

### identity_fetch

Adds a credential for a provider (OAuth2 or API key). **Call when the user wants to add, get, or configure credentials:**

- "帮我添加 Google 凭据", "get credentials for OpenAI", "connect my GitHub", "配置 API key", "授权访问某平台"

Call `identity_fetch` directly with the `provider`. Do not pre-check login — the user is already authenticated by the time the agent runs. The `authUrl` returned here is for **provider-level OAuth authorization** (e.g. authorizing GitHub), not for IDP login.

| Param         | Type     | Required | Description                                                                             |
| ------------- | -------- | -------- | --------------------------------------------------------------------------------------- |
| `provider`    | string   | Yes      | Provider name (e.g. `google`, `openai`)                                                 |
| `flow`        | string   | No       | `oauth2-user` (default for 3LO), `oauth2-m2m`, or `apikey`. Auto-inferred when omitted. |
| `redirectUrl` | string   | No       | OAuth redirect URL (when provider requires custom)                                      |
| `scopes`      | string[] | No       | OAuth scopes (e.g. `["email", "profile"]`)                                              |
| `returnValue` | boolean  | No       | When true and fetch succeeds, include credential `value` in result. Default false.       |

**Response:**

- **OAuth2-user (success: true)**: Server had a cached token — credential added immediately. No further action needed.
- **OAuth2-user (authUrl)**: User must authorize. **Display as markdown link** `[Click here to authorize](authUrl)`. A background process will detect when authorization completes and trigger the agent to continue automatically. If that does not happen within a reasonable time, **call `identity_fetch` again with the same provider** to check status.
- **OAuth2-m2m** / **apikey**: `success: true`, `message` (completes immediately). If `returnValue: true`, also includes `value`.

### identity_set_binding

Binds a stored credential to an env var for tool injection. **Call when:** "让工具能用我的凭据", "bind my credential for tools", "inject my key for API calls"

Credential must exist first (`identity_fetch`). Common env vars: `GOOGLE_ACCESS_TOKEN`, `OPENAI_API_KEY`, `GITHUB_TOKEN`.

| Param      | Type   | Required | Description                                                                              |
| ---------- | ------ | -------- | ---------------------------------------------------------------------------------------- |
| `provider` | string | Yes      | Provider name (e.g. `google`)                                                            |
| `envVar`   | string | Yes      | Env var for injection (e.g. `GOOGLE_ACCESS_TOKEN`). Must match `[A-Za-z_][A-Za-z0-9_]*`. |

If credential exists: binds it. Else: imports from `process.env[envVar]` as api_key.

### identity_unset_binding

| Param      | Type   | Required | Description                             |
| ---------- | ------ | -------- | --------------------------------------- |
| `provider` | string | Yes      | Provider name to unbind (e.g. `google`) |

### identity_risk_check

Evaluates risk of a command or tool call. **Call when:** "这个命令安全吗", "is this risky", "帮我评估风险"

| Param      | Type     | Required | Description                                            |
| ---------- | -------- | -------- | ------------------------------------------------------ |
| `command`  | string   | No*      | Shell command to evaluate                              |
| `toolName` | string   | No*      | Tool name (e.g. write, apply_patch). Use with `params`. |
| `params`   | object   | No       | Tool params                                            |

*Provide either `command` or `toolName`. Returns `risk`, `reason`, `source`.

### identity_list_risk_patterns

Returns built-in dangerous command patterns and sensitive paths. No params.

### identity_exchange_credential

Exchanges the current session's TIP token for a downstream credential through an operator-configured profile (`identity.credentialExchanges` in plugin config). **Call when:** a skill's `Identity Credential Runtime Contract` declares an `exchange` binding — invoke the tool with the binding's `id`, then map `value` to the env var the contract specifies. Do NOT use this tool to fetch an arbitrary credential the user mentions in chat; only call when an active contract requires it.

| Param          | Type    | Required | Description                                                                                                  |
| -------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------ |
| `id`           | string  | Yes      | Configured exchange profile id.                                                                              |
| `returnValue`  | boolean | No       | When true, include the exchanged credential in `result.value` for env mapping. Default false.                |
| `forceRefresh` | boolean | No       | Bypass the per-session cache and call the exchange endpoint again. Default false.                            |

**Response states**:

- `credential_exchanged` (`success: true`): Exchange OK. When `returnValue: true`, also includes `value` (the exchanged credential), `cacheHit`, and `expiresAt` (Unix ms). Map `value` **verbatim** to the env var declared by the active contract.
- `no_session_context` (`success: false`): Tool was invoked outside an active chat session — surface to the user as a configuration / context error.
- `unknown_exchange_profile` (`success: false`): The requested profile id is not configured. Tell the user the profile is missing and suggest the operator declare it under `identity.credentialExchanges`.
- `tip_required` (`success: false`): Session has no TIP. Suggest **`/identity login`** or **`identity_login`** in normal mode; in **`identity.headless`** suggest **`identity_get_tip_token`** or fixing workload AK/SK / pool config instead of user login.
- `exchange_failed` (`success: false`): The exchange endpoint returned an error or the network call failed. Surface `error` verbatim and check the endpoint or its policy.

**Critical**: NEVER print the `value` in chat replies, thoughts, summaries, logs, or command echoes. NEVER inline it into command strings. Reference it only through the env var declared by the contract.

### identity_config_suggest

Generates config snippets. **Call when:** "如何配置 identity 插件", "帮我配置登录", "怎么开启权限检查"

| Param   | Type   | Required | Description                                                                 |
| ------- | ------ | -------- | --------------------------------------------------------------------------- |
| `intent`| string | No       | `identity`, `userpool`, `authz`, `llm_risk`, or `full` (default)            |
| `lang`  | string | No       | `en` or `zh`. Default: en                                                   |

Returns: `configPath`, `config` (JSON to merge), `instructions`, `nextSteps`.

## Workflow: Adding a Credential

1. **Add credential**: call `identity_fetch` with the `provider`. If it returns `authUrl`, **display as markdown link** `[Click here to authorize](authUrl)` — this is provider-level OAuth, not IDP login. The agent will be notified automatically when the user completes authorization. If not resumed automatically, call `identity_fetch` again to check status.
2. **Bind for tools** (optional): `identity_set_binding` so the credential is injected as an env var when tools run.

## Workflow: Checking Risk

1. `identity_risk_check` with `command` or `toolName`+`params`. Returns risk level and reason.
2. `identity_list_risk_patterns` to see what triggers high-risk.

## Notes

- The plugin establishes identity **before** the LLM is invoked (user OIDC session + TIP in normal mode; **workload TIP only** in **`identity.headless`**). Do not call `identity_whoami` / `identity_login` as a defensive pre-check unless the user asks.
- `identity_risk_check` and `identity_list_risk_patterns` are pure utilities and require no special state.
