@volcengine/agent-identity 1.3.2

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,166 @@
1
+ ---
2
+ name: identity
3
+ description: |
4
+ 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, or diagnose risky tool calls.
5
+ Also activates for: 用户说登录、查身份、获取凭据、添加/配置API密钥、绑定环境变量、配置插件、风险检查.
6
+ metadata:
7
+ {
8
+ "openclaw":
9
+ {
10
+ "emoji": "🔐",
11
+ "skillKey": "identity",
12
+ "requires": { "config": ["plugins.entries.agent-identity.enabled"] },
13
+ },
14
+ }
15
+ ---
16
+
17
+ # Agent Identity
18
+
19
+ Use the agent-identity plugin for UserPool OIDC login, TIP token, credential hosting (OAuth2, API key), and optional tool risk approval (AuthZ).
20
+
21
+ **Agent rules:**
22
+
23
+ - Call tools directly. Never suggest slash commands to the user.
24
+ - 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.
25
+ - **Critical — auto-login on AuthZ block:** When ANY tool call fails with an error containing "TIP token required" or "session has no valid identity", you MUST immediately call `identity_login` to initiate login, display the `authUrl` to the user, and wait for them to complete authentication. After login succeeds, retry the original tool call. Do NOT skip the blocked action, try alternative approaches, or tell the user to run commands manually.
26
+ - **Critical — always report permission errors to the user:** When a tool call is blocked with errors like "CheckPermission denied", "permission denied", "requires approval", or any `[agent-identity]` prefixed error, you MUST:
27
+ 1. **Tell the user clearly** what was blocked and why (include the tool/skill name and the error message).
28
+ 2. **Never silently ignore** the error or skip to something else without informing the user.
29
+ 3. For "CheckPermission denied for skill ..." — tell the user they do not have access to that skill and suggest contacting an administrator.
30
+ 4. For "CheckPermission denied for tool ..." — tell the user the tool is restricted and they lack permission to use it.
31
+ 5. For "High-risk tool requires approval" — OpenClaw will present the native approval UI automatically; wait for the user to approve before retrying.
32
+ 6. For "Failed to verify permission" — report a temporary error and suggest retrying later.
33
+
34
+ ## Tool Parameters
35
+
36
+ ### identity_login
37
+
38
+ Starts OIDC login or refreshes TIP. **Call when:** "login", "登录", "sign in", "我需要先登录". Required before `identity_fetch`. No params.
39
+
40
+ **Returns:**
41
+
42
+ - Already logged in: `{ ok: true, sub, message }` — inform user they are already authenticated.
43
+ - Needs login: `{ ok: false, authUrl, message }` — **you MUST display the `authUrl` as a markdown link** and tell them to open it in a browser. After they authorize, a success message will be sent to the chat automatically. Example response: "Please complete login in your browser: [Click here to log in](authUrl)"
44
+ - Error: `{ error, authUrl: null }` — report the error.
45
+
46
+ ### identity_whoami
47
+
48
+ Brief identity check. **Call when:** "who am I", "查身份", "am I logged in", "当前登录状态". No params.
49
+
50
+ Returns: `sub`, `hasTip`, `loggedIn`, `sessionLoginAt`, `sessionExpiresAt`, `tipIssuedAt`, `tipExpiresAt`, `tipExpiresInSeconds`, `tipChain`.
51
+
52
+ ### identity_status
53
+
54
+ Full status including credentials and bindings. **Call when:** "status", "查看完整状态", "我的凭据和绑定", "show my credentials and bindings". No params.
55
+
56
+ Returns: `loggedIn`, `sub`, `hasTip`, `session` (loginAt, expiresAt), `tip` (issuedAt, expiresAt, chain), `credentialProviders`, `bindings`.
57
+
58
+ ### identity_logout
59
+
60
+ Clear session and TIP. **Call when:** "logout", "登出", "sign out". No params.
61
+
62
+ ### identity_list_credentials
63
+
64
+ 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"
65
+
66
+ | Param | Type | Required | Description |
67
+ | ------ | ------ | -------- | ------------------------------------------------------------------ |
68
+ | `page` | number | No | Page number (default: 1) |
69
+ | `name` | string | No | Filter providers by name (exact or prefix match) |
70
+ | `flow` | string | No | Filter providers by flow, e.g. `M2M` or `USER_FEDERATION` |
71
+
72
+ When looking for a specific provider (e.g. before `identity_fetch`), prefer passing `name` filter instead of paging through results.
73
+
74
+ Returns: `providers`, `storedOnly`, `page`, `hasMore`.
75
+
76
+ ### identity_list_roles
77
+
78
+ Lists **STS role credential providers** (not OAuth/API key — use `identity_list_credentials` for those). **Call when:** "role credentials", "STS providers", "IAM role 凭据", "有哪些角色凭据". Requires login session.
79
+
80
+ Optional param: `name` — prefix filter on provider name.
81
+
82
+ Returns: `providers` (each may include `identitySource`). To obtain temporary keys for a provider, use `identity_get_role_credentials` with that provider name.
83
+
84
+ ### identity_fetch
85
+
86
+ Adds a credential for a provider (OAuth2 or API key). **Call when the user wants to add, get, or configure credentials:**
87
+
88
+ - "帮我添加 Google 凭据", "get credentials for OpenAI", "connect my GitHub", "配置 API key", "授权访问某平台"
89
+
90
+ First ensure user is logged in (`identity_whoami`); if not, use `identity_login`. Then call `identity_fetch` with the provider.
91
+
92
+ | Param | Type | Required | Description |
93
+ | ------------- | -------- | -------- | --------------------------------------------------------------------------------------- |
94
+ | `provider` | string | Yes | Provider name (e.g. `google`, `openai`) |
95
+ | `flow` | string | No | `oauth2-user` (default for 3LO), `oauth2-m2m`, or `apikey`. Auto-inferred when omitted. |
96
+ | `redirectUrl` | string | No | OAuth redirect URL (when provider requires custom) |
97
+ | `scopes` | string[] | No | OAuth scopes (e.g. `["email", "profile"]`) |
98
+ | `returnValue` | boolean | No | When true and fetch succeeds, include credential `value` in result. Default false. |
99
+
100
+ **Response:**
101
+
102
+ - **OAuth2-user (success: true)**: Server had a cached token — credential added immediately. No further action needed.
103
+ - **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.
104
+ - **OAuth2-m2m** / **apikey**: `success: true`, `message` (completes immediately). If `returnValue: true`, also includes `value`.
105
+
106
+ ### identity_set_binding
107
+
108
+ Binds a stored credential to an env var for tool injection. **Call when:** "让工具能用我的凭据", "bind my credential for tools", "inject my key for API calls"
109
+
110
+ Credential must exist first (`identity_fetch`). Common env vars: `GOOGLE_ACCESS_TOKEN`, `OPENAI_API_KEY`, `GITHUB_TOKEN`.
111
+
112
+ | Param | Type | Required | Description |
113
+ | ---------- | ------ | -------- | ---------------------------------------------------------------------------------------- |
114
+ | `provider` | string | Yes | Provider name (e.g. `google`) |
115
+ | `envVar` | string | Yes | Env var for injection (e.g. `GOOGLE_ACCESS_TOKEN`). Must match `[A-Za-z_][A-Za-z0-9_]*`. |
116
+
117
+ If credential exists: binds it. Else: imports from `process.env[envVar]` as api_key.
118
+
119
+ ### identity_unset_binding
120
+
121
+ | Param | Type | Required | Description |
122
+ | ---------- | ------ | -------- | --------------------------------------- |
123
+ | `provider` | string | Yes | Provider name to unbind (e.g. `google`) |
124
+
125
+ ### identity_risk_check
126
+
127
+ Evaluates risk of a command or tool call. **Call when:** "这个命令安全吗", "is this risky", "帮我评估风险"
128
+
129
+ | Param | Type | Required | Description |
130
+ | ---------- | -------- | -------- | ------------------------------------------------------ |
131
+ | `command` | string | No* | Shell command to evaluate |
132
+ | `toolName` | string | No* | Tool name (e.g. write, apply_patch). Use with `params`. |
133
+ | `params` | object | No | Tool params |
134
+
135
+ *Provide either `command` or `toolName`. Returns `risk`, `reason`, `source`.
136
+
137
+ ### identity_list_risk_patterns
138
+
139
+ Returns built-in dangerous command patterns and sensitive paths. No params.
140
+
141
+ ### identity_config_suggest
142
+
143
+ Generates config snippets. **Call when:** "如何配置 identity 插件", "帮我配置登录", "怎么开启权限检查"
144
+
145
+ | Param | Type | Required | Description |
146
+ | ------- | ------ | -------- | --------------------------------------------------------------------------- |
147
+ | `intent`| string | No | `identity`, `userpool`, `authz`, `llm_risk`, or `full` (default) |
148
+ | `lang` | string | No | `en` or `zh`. Default: en |
149
+
150
+ Returns: `configPath`, `config` (JSON to merge), `instructions`, `nextSteps`.
151
+
152
+ ## Workflow: Adding a Credential
153
+
154
+ 1. **Check login**: `identity_whoami`. If not logged in, call `identity_login`. When it returns `authUrl`, **display as markdown link** `[Click here to log in](authUrl)`.
155
+ 2. **Add credential**: `identity_fetch` with `provider`. If it returns `authUrl`, display the link. The agent will be notified automatically when the user completes authorization. If not resumed automatically, **call `identity_fetch` again** to check status.
156
+ 3. **Bind for tools** (optional): `identity_set_binding` so the credential is injected as an env var when tools run.
157
+
158
+ ## Workflow: Checking Risk
159
+
160
+ 1. `identity_risk_check` with `command` or `toolName`+`params`. Returns risk level and reason.
161
+ 2. `identity_list_risk_patterns` to see what triggers high-risk.
162
+
163
+ ## Notes
164
+
165
+ - `identity_login`, `identity_logout`, `identity_whoami`, `identity_status`, and `identity_config_suggest` work without login.
166
+ - `identity_risk_check` and `identity_list_risk_patterns` work without login.
@@ -0,0 +1,243 @@
1
+ {
2
+ "id": "agent-identity",
3
+ "name": "Agent Identity",
4
+ "description": "UserPool (用户池) login, TIP token (工作负载令牌 GetWorkloadAccessTokenForJWT), credential 3LO (凭据托管), session management, optional agent/tool/skill permission control (CheckPermission) and risk approval. Integrates with Volcengine 智能体身份和权限管理平台. Credentials from config, env, or file; STS AssumeRole supported.",
5
+ "skills": ["./identity"],
6
+ "activation": {
7
+ "onStartup": true
8
+ },
9
+ "contracts": {
10
+ "tools": [
11
+ "identity_whoami",
12
+ "identity_logout",
13
+ "identity_status",
14
+ "identity_login",
15
+ "identity_list_credentials",
16
+ "identity_list_roles",
17
+ "identity_list_tips",
18
+ "identity_config",
19
+ "identity_config_suggest",
20
+ "identity_fetch",
21
+ "identity_get_role_credentials",
22
+ "identity_get_tip_token",
23
+ "identity_get_session_token",
24
+ "identity_set_binding",
25
+ "identity_unset_binding",
26
+ "identity_risk_check",
27
+ "identity_list_risk_patterns"
28
+ ]
29
+ },
30
+ "configSchema": {
31
+ "type": "object",
32
+ "additionalProperties": false,
33
+ "properties": {
34
+ "identity": {
35
+ "type": "object",
36
+ "description": "Identity API config (service code: id).",
37
+ "properties": {
38
+ "endpoint": {
39
+ "type": "string",
40
+ "description": "Identity API endpoint, e.g. https://id.cn-beijing.volcengineapi.com. Highest priority; when set, regionMetadataUrl is ignored for the base URL."
41
+ },
42
+ "regionMetadataUrl": {
43
+ "type": "string",
44
+ "description": "GET this URL for plain-text region id (e.g. http://100.96.0.96/latest/region_id). When endpoint is unset, builds https://id.{region}.volcengineapi.com. Request timeout ~10s; on failure falls back to https://id.cn-beijing.volcengineapi.com"
45
+ },
46
+ "accessKeyId": {
47
+ "type": "string",
48
+ "description": "Optional. When omitted, loaded from VOLCENGINE_ACCESS_KEY or credentialsFile"
49
+ },
50
+ "secretAccessKey": {
51
+ "type": "string",
52
+ "description": "Optional. When omitted, loaded from VOLCENGINE_SECRET_KEY or credentialsFile"
53
+ },
54
+ "sessionToken": {
55
+ "type": "string",
56
+ "description": "Optional STS session token (or VOLCENGINE_SESSION_TOKEN)"
57
+ },
58
+ "credentialsFile": {
59
+ "type": "string",
60
+ "description": "Path to credential JSON (VeFaaS style). Default: VOLCENGINE_CREDENTIALS_FILE or /var/run/secrets/iam/credential"
61
+ },
62
+ "credentialsMetadataUrl": {
63
+ "type": "string",
64
+ "description": "Full URL for remote credential fetch. When set with roleTrn, fetches from URL then AssumeRole with roleTrn. Response: AccessKeyId, SecretAccessKey, SessionToken. 404 falls through to credentialsFile. Caches AssumeRole result by ExpiredTime. Must be explicitly configured."
65
+ },
66
+ "roleTrn": {
67
+ "type": "string",
68
+ "description": "Role TRN for STS AssumeRole. When set (and workloadName not set), workload name is omitted; backend uses roleName. Priority: workloadName > roleTrn > params."
69
+ },
70
+ "workloadName": {
71
+ "type": "string",
72
+ "description": "Workload name for TIP. When set, takes precedence over roleTrn. Default when neither set: agentId or openclaw-agent."
73
+ },
74
+ "workloadPoolName": {
75
+ "type": "string",
76
+ "default": "default"
77
+ },
78
+ "audience": {
79
+ "type": "array",
80
+ "items": { "type": "string" },
81
+ "description": "Audience for the TIP token"
82
+ },
83
+ "durationSeconds": {
84
+ "type": "number",
85
+ "default": 3600
86
+ },
87
+ "subagentTipPropagation": {
88
+ "type": "boolean",
89
+ "default": false,
90
+ "description": "Propagate TIP and session to subagents (sessions_spawn, sessions_send). When true, subagents get their own TIP token."
91
+ },
92
+ "webchatSessionExchange": {
93
+ "type": "boolean",
94
+ "default": false,
95
+ "description": "Enable identity.session.put / identity.session.get gateway WS methods for webchat clients. Allows BFF to inject OIDC id_token into plugin sessions without redirect flow."
96
+ },
97
+ "personalSessionMode": {
98
+ "type": "boolean",
99
+ "default": false,
100
+ "description": "Single-user mode: store TIP, OIDC session, and credentials under agent:main:main only (no per-sender or per-channel-peer keys). Subagent sessions are unchanged. Not for multi-tenant or shared group chats."
101
+ },
102
+ "localServer": {
103
+ "type": "boolean",
104
+ "default": false,
105
+ "description": "Start a local HTTP-over-UDS server so other processes on the same machine can retrieve TIP tokens and session info."
106
+ },
107
+ "localServerAllowlist": {
108
+ "type": "array",
109
+ "items": { "type": "string" },
110
+ "description": "Additional process names/paths allowed to connect to the local server (beyond defaults)."
111
+ },
112
+ "localServerFailOpen": {
113
+ "type": "boolean",
114
+ "default": true,
115
+ "description": "When the local server cannot identify the connecting process (lsof unavailable, race condition, etc.), allow the request with a warning. Set to false for strict mode (deny unresolvable peers)."
116
+ }
117
+ }
118
+ },
119
+ "userpool": {
120
+ "type": "object",
121
+ "description": "UserPool OIDC: explicit (discoveryUrl+clientId) or dynamic (userPoolName+clientName)",
122
+ "properties": {
123
+ "discoveryUrl": {
124
+ "type": "string",
125
+ "description": "OIDC discovery base (explicit mode)"
126
+ },
127
+ "clientId": { "type": "string" },
128
+ "clientSecret": { "type": "string" },
129
+ "callbackUrl": {
130
+ "type": "string",
131
+ "description": "Full callback URL registered with UserPool client"
132
+ },
133
+ "scope": { "type": "string" },
134
+ "userPoolName": {
135
+ "type": "string",
136
+ "description": "Dynamic: resolve pool by name (from_veidentity style)"
137
+ },
138
+ "clientName": {
139
+ "type": "string",
140
+ "description": "Dynamic: resolve client by name"
141
+ },
142
+ "autoCreate": {
143
+ "type": "boolean",
144
+ "default": true,
145
+ "description": "Create UserPool/Client when not found (dynamic mode)"
146
+ },
147
+ "identityProvider": {
148
+ "type": "string",
149
+ "description": "External identity provider name to use in the OAuth2 authorization URL (identity_provider param). When omitted, the first provider returned by ListIdentityProviders is used automatically."
150
+ },
151
+ "useRelayCallback": {
152
+ "type": "boolean",
153
+ "description": "When true, the OIDC flow goes through the UserPool relay (redirect_relay_uri). The callback handler will detect the relay state format (base64 JSON with request_id/provider_id/request_state) and first forward the authorization code to the UserPool's generic_oauth callback endpoint, then perform the token exchange. The app state is recovered from request_state."
154
+ }
155
+ }
156
+ },
157
+ "authz": {
158
+ "type": "object",
159
+ "properties": {
160
+ "agentCheck": {
161
+ "type": "boolean",
162
+ "description": "Run CheckPermission for agents (resource type agent) in before_agent_start. Verifies that the authenticated user has permission to invoke the current agent. Uses the outermost actor from the TIP delegation chain as the resource id. Default: false.",
163
+ "default": false
164
+ },
165
+ "toolCheck": {
166
+ "type": "boolean",
167
+ "description": "Run CheckPermission for tools (resource type tool). Default: false.",
168
+ "default": false
169
+ },
170
+ "skillReadCheck": {
171
+ "type": "boolean",
172
+ "description": "Run CheckPermission for read of SKILL.md (resource type skill). Parses available_skills from system prompt. Default: false.",
173
+ "default": false
174
+ },
175
+ "requireRiskApproval": {
176
+ "type": "boolean",
177
+ "description": "Require user approval for high-risk tools. Default: false.",
178
+ "default": false
179
+ },
180
+ "namespaceName": {
181
+ "type": "string",
182
+ "description": "Namespace for CheckPermission (Cedar policy). Default: default.",
183
+ "default": "default"
184
+ },
185
+ "lowRiskBypass": {
186
+ "type": "boolean",
187
+ "description": "Skip TIP+CheckPermission for built-in low-risk tools",
188
+ "default": true
189
+ },
190
+ "lowRiskTools": {
191
+ "type": "array",
192
+ "items": { "type": "string" },
193
+ "description": "Extra tool names to treat as low-risk"
194
+ },
195
+ "enableLlmRiskCheck": {
196
+ "type": "boolean",
197
+ "description": "Use LLM to re-evaluate user-provided commands/paths when rules return medium. Risk reason is shown in approval prompts and block messages.",
198
+ "default": false
199
+ },
200
+ "llmRiskCheck": {
201
+ "type": "object",
202
+ "description": "LLM provider config for risk check. Evaluates commands (exec, process) and file paths (write, apply_patch). Reason is passed to approval flow.",
203
+ "properties": {
204
+ "endpoint": {
205
+ "type": "string",
206
+ "description": "Base URL: Ollama (http://localhost:11434) or OpenAI-compat (https://api.openai.com/v1)"
207
+ },
208
+ "api": {
209
+ "type": "string",
210
+ "enum": ["ollama", "openai-completions"],
211
+ "description": "API style: ollama for /api/generate, openai-completions for /chat/completions",
212
+ "default": "ollama"
213
+ },
214
+ "model": {
215
+ "type": "string",
216
+ "description": "Model name (e.g. qwen3:8b, gpt-4o-mini)"
217
+ },
218
+ "apiKey": {
219
+ "type": "string",
220
+ "description": "API key for OpenAI-compatible providers (omit for Ollama)"
221
+ },
222
+ "timeoutMs": {
223
+ "type": "number",
224
+ "description": "Timeout in ms",
225
+ "default": 10000
226
+ },
227
+ "cacheTtlMs": {
228
+ "type": "number",
229
+ "description": "Cache TTL in ms for same tool+params. 0 to disable. Default 300000",
230
+ "default": 300000
231
+ }
232
+ }
233
+ },
234
+ "approvalTtlSeconds": {
235
+ "type": "number",
236
+ "description": "Approval TTL in seconds",
237
+ "default": 300
238
+ }
239
+ }
240
+ }
241
+ }
242
+ }
243
+ }
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@volcengine/agent-identity",
3
+ "version": "1.3.2",
4
+ "description": "Agent Identity: UserPool (用户池) login, TIP token (工作负载令牌), credential hosting (凭据托管 OAuth2/API key), optional tool/skill permission control (CheckPermission) and risk approval. Integrates with Volcengine 智能体身份和权限管理平台.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
10
+ "build": "tsc",
11
+ "build:types": "tsc --emitDeclarationOnly",
12
+ "build:bundle": "npm run clean && npm run build:types && node -e \"require('node:fs').rmSync('dist/src',{recursive:true,force:true})\" && esbuild index.ts --bundle --platform=node --target=node22 --format=esm --outfile=dist/index.js --sourcemap --minify --external:openclaw --external:openclaw/* --external:koffi",
13
+ "test": "vitest run",
14
+ "test:ci": "vitest run --reporter=verbose",
15
+ "lint": "tsc --noEmit",
16
+ "prepublishOnly": "npm run build:bundle"
17
+ },
18
+ "keywords": [
19
+ "openclaw",
20
+ "identity",
21
+ "volcengine",
22
+ "userpool",
23
+ "credential",
24
+ "TIP",
25
+ "auth",
26
+ "agent"
27
+ ],
28
+ "license": "Apache-2.0",
29
+ "optionalDependencies": {
30
+ "koffi": "^2.15.2"
31
+ },
32
+ "devDependencies": {
33
+ "@sinclair/typebox": "0.34.48",
34
+ "@types/node": "^22.0.0",
35
+ "esbuild": "^0.27.3",
36
+ "jose": "^5.9.6",
37
+ "json5": "^2.2.3",
38
+ "openclaw": "^2026.3.28",
39
+ "typescript": "^5.7.0",
40
+ "vitest": "^4.1.0",
41
+ "ws": "^8.19.0",
42
+ "yaml": "^2.4.0"
43
+ },
44
+ "peerDependencies": {
45
+ "@mariozechner/pi-agent-core": "^0.58.0",
46
+ "openclaw": ">=2026.3.24"
47
+ },
48
+ "peerDependenciesMeta": {
49
+ "openclaw": {
50
+ "optional": true
51
+ }
52
+ },
53
+ "openclaw": {
54
+ "extensions": [
55
+ "./dist/index.js"
56
+ ],
57
+ "compat": {
58
+ "pluginApi": ">=2026.3.24",
59
+ "minGatewayVersion": "2026.3.24"
60
+ },
61
+ "build": {
62
+ "openclawVersion": "2026.3.28",
63
+ "pluginSdkVersion": "2026.3.28"
64
+ }
65
+ },
66
+ "files": [
67
+ "dist",
68
+ "README.md",
69
+ "README-cn.md",
70
+ "identity/SKILL.md",
71
+ "openclaw.plugin.json"
72
+ ]
73
+ }