@m1a0rz/agent-identity 0.2.5 → 0.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.
Files changed (77) hide show
  1. package/README-cn.md +19 -9
  2. package/README.md +22 -12
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +62 -22
  5. package/dist/src/actions/identity-actions.d.ts +7 -1
  6. package/dist/src/actions/identity-actions.d.ts.map +1 -1
  7. package/dist/src/actions/identity-actions.js +82 -34
  8. package/dist/src/commands/identity-commands.d.ts.map +1 -1
  9. package/dist/src/commands/identity-commands.js +14 -5
  10. package/dist/src/hooks/after-tool-call.d.ts +12 -0
  11. package/dist/src/hooks/after-tool-call.d.ts.map +1 -0
  12. package/dist/src/hooks/after-tool-call.js +31 -0
  13. package/dist/src/hooks/before-agent-start.d.ts +3 -3
  14. package/dist/src/hooks/before-agent-start.d.ts.map +1 -1
  15. package/dist/src/hooks/before-agent-start.js +10 -22
  16. package/dist/src/hooks/before-tool-call.d.ts +20 -14
  17. package/dist/src/hooks/before-tool-call.d.ts.map +1 -1
  18. package/dist/src/hooks/before-tool-call.js +207 -127
  19. package/dist/src/hooks/llm-input.d.ts +2 -0
  20. package/dist/src/hooks/llm-input.d.ts.map +1 -1
  21. package/dist/src/hooks/llm-input.js +15 -4
  22. package/dist/src/hooks/sessions-send-propagation.d.ts +1 -0
  23. package/dist/src/hooks/sessions-send-propagation.d.ts.map +1 -1
  24. package/dist/src/hooks/sessions-send-propagation.js +4 -2
  25. package/dist/src/hooks/sessions-spawn-propagation.d.ts.map +1 -1
  26. package/dist/src/hooks/sessions-spawn-propagation.js +4 -2
  27. package/dist/src/hooks/subagent-ended-cleanup.d.ts +1 -0
  28. package/dist/src/hooks/subagent-ended-cleanup.d.ts.map +1 -1
  29. package/dist/src/hooks/subagent-ended-cleanup.js +4 -1
  30. package/dist/src/risk/low-risk-tools.d.ts.map +1 -1
  31. package/dist/src/risk/low-risk-tools.js +0 -2
  32. package/dist/src/services/tip-acquisition.d.ts +0 -1
  33. package/dist/src/services/tip-acquisition.d.ts.map +1 -1
  34. package/dist/src/services/tip-acquisition.js +2 -2
  35. package/dist/src/services/tip-propagation.d.ts.map +1 -1
  36. package/dist/src/services/tip-propagation.js +1 -2
  37. package/dist/src/services/tip-with-refresh.d.ts +1 -1
  38. package/dist/src/services/tip-with-refresh.d.ts.map +1 -1
  39. package/dist/src/services/tip-with-refresh.js +3 -5
  40. package/dist/src/store/credential-env-snapshot.d.ts +38 -0
  41. package/dist/src/store/credential-env-snapshot.d.ts.map +1 -0
  42. package/dist/src/store/credential-env-snapshot.js +101 -0
  43. package/dist/src/store/encryption.d.ts +30 -0
  44. package/dist/src/store/encryption.d.ts.map +1 -0
  45. package/dist/src/store/encryption.js +147 -0
  46. package/dist/src/store/group-sender-store.d.ts +35 -0
  47. package/dist/src/store/group-sender-store.d.ts.map +1 -0
  48. package/dist/src/store/group-sender-store.js +112 -0
  49. package/dist/src/store/session-store.d.ts.map +1 -1
  50. package/dist/src/store/session-store.js +91 -8
  51. package/dist/src/store/tip-store.d.ts +11 -6
  52. package/dist/src/store/tip-store.d.ts.map +1 -1
  53. package/dist/src/store/tip-store.js +23 -54
  54. package/dist/src/tools/identity-approve-tool.d.ts.map +1 -1
  55. package/dist/src/tools/identity-approve-tool.js +3 -1
  56. package/dist/src/tools/identity-fetch.d.ts.map +1 -1
  57. package/dist/src/tools/identity-fetch.js +3 -1
  58. package/dist/src/tools/identity-list-credentials.d.ts +2 -0
  59. package/dist/src/tools/identity-list-credentials.d.ts.map +1 -1
  60. package/dist/src/tools/identity-list-credentials.js +8 -4
  61. package/dist/src/tools/identity-login.d.ts.map +1 -1
  62. package/dist/src/tools/identity-login.js +2 -1
  63. package/dist/src/tools/identity-logout.d.ts.map +1 -1
  64. package/dist/src/tools/identity-logout.js +2 -1
  65. package/dist/src/tools/identity-set-binding.d.ts.map +1 -1
  66. package/dist/src/tools/identity-set-binding.js +2 -1
  67. package/dist/src/tools/identity-status.d.ts.map +1 -1
  68. package/dist/src/tools/identity-status.js +2 -1
  69. package/dist/src/tools/identity-unset-binding.d.ts.map +1 -1
  70. package/dist/src/tools/identity-unset-binding.js +2 -1
  71. package/dist/src/tools/identity-whoami.d.ts.map +1 -1
  72. package/dist/src/tools/identity-whoami.js +2 -1
  73. package/dist/src/utils/derive-session-key.d.ts +7 -0
  74. package/dist/src/utils/derive-session-key.d.ts.map +1 -1
  75. package/dist/src/utils/derive-session-key.js +84 -15
  76. package/package.json +1 -1
  77. package/skills/SKILL.md +75 -150
@@ -64,6 +64,18 @@ export function isSubagentSessionKey(sessionKey) {
64
64
  return true;
65
65
  return raw.includes(":subagent:");
66
66
  }
67
+ /**
68
+ * Check whether sessionKey represents a group/channel conversation.
69
+ * Examples:
70
+ * - agent:main:telegram:group:-1001
71
+ * - agent:main:slack:channel:C123456
72
+ */
73
+ export function isGroupOrChannelSessionKey(sessionKey) {
74
+ const raw = (sessionKey ?? "").trim().toLowerCase();
75
+ if (!raw)
76
+ return false;
77
+ return raw.includes(":group:") || raw.includes(":channel:");
78
+ }
67
79
  /**
68
80
  * Resolve workload name for GetWorkloadAccessToken when roleTrn is not set.
69
81
  * For subagent keys (agent:X:subagent:uuid or nested): use last segment (uuid) as workload name.
@@ -92,24 +104,81 @@ function normalizeAccountId(value) {
92
104
  return trimmed || DEFAULT_ACCOUNT_ID;
93
105
  }
94
106
  /**
95
- * Extract peer/chat id from from/to for group sessions.
96
- * Channel-specific heuristics (e.g. telegram:group:123 or telegram:123 for group chat).
97
- */
107
+ * Extract group peer id from channel-specific "from/to" shapes.
108
+ * Return null for direct messages.
109
+ */
98
110
  function extractGroupPeerId(channel, from, to) {
99
- const raw = (to ?? from ?? "").trim();
100
- if (!raw)
111
+ const ch = (channel ?? "").trim().toLowerCase();
112
+ if (!ch)
101
113
  return null;
102
- const lower = raw.toLowerCase();
103
- const channelPrefix = `${channel}:`;
104
- if (lower.startsWith(channelPrefix)) {
105
- const rest = raw.slice(channelPrefix.length);
106
- const parts = rest.split(":");
107
- if (parts[0] === "group" && parts[1])
108
- return parts[1];
109
- if (parts.length >= 1)
110
- return parts[0];
114
+ const fromRaw = (from ?? "").trim();
115
+ const toRaw = (to ?? "").trim();
116
+ const pickByPrefix = (value, prefix) => {
117
+ if (!value)
118
+ return null;
119
+ if (!value.toLowerCase().startsWith(prefix.toLowerCase()))
120
+ return null;
121
+ const id = value.slice(prefix.length).trim();
122
+ return id || null;
123
+ };
124
+ const pickFirst = (...values) => {
125
+ for (const v of values) {
126
+ if (v)
127
+ return v;
128
+ }
129
+ return null;
130
+ };
131
+ switch (ch) {
132
+ case "feishu":
133
+ // Group: to="chat:oc_xxx"; DM: to="user:ou_xxx"
134
+ return pickByPrefix(toRaw, "chat:");
135
+ case "telegram": {
136
+ // Group/topic: telegram:group:<chatId>[:topic:<threadId>]
137
+ const g1 = pickByPrefix(fromRaw, "telegram:group:");
138
+ if (g1)
139
+ return g1;
140
+ const g2 = pickByPrefix(toRaw, "telegram:group:");
141
+ if (g2)
142
+ return g2;
143
+ // Legacy fallback: telegram:-100... is usually group/supergroup
144
+ const f = pickByPrefix(fromRaw, "telegram:");
145
+ if (f && f.startsWith("-"))
146
+ return f;
147
+ const t = pickByPrefix(toRaw, "telegram:");
148
+ if (t && t.startsWith("-"))
149
+ return t;
150
+ return null;
151
+ }
152
+ case "slack":
153
+ // Group/channel: slack:channel:<id> / slack:group:<id>, to=channel:<id>
154
+ return pickFirst(pickByPrefix(fromRaw, "slack:channel:"), pickByPrefix(fromRaw, "slack:group:"), pickByPrefix(toRaw, "channel:"));
155
+ case "discord":
156
+ // Guild/thread channel: discord:channel:<id>, to=channel:<id>; DM uses user:<id>
157
+ return pickFirst(pickByPrefix(fromRaw, "discord:channel:"), pickByPrefix(toRaw, "channel:"));
158
+ case "signal":
159
+ // Group: group:<id>; DM: signal:<recipient>
160
+ return pickFirst(pickByPrefix(fromRaw, "group:"), pickByPrefix(toRaw, "group:"));
161
+ case "imessage":
162
+ // Group: imessage:group:<id>, to=chat_id:<id>; DM: imessage:<sender>
163
+ return pickFirst(pickByPrefix(fromRaw, "imessage:group:"), pickByPrefix(toRaw, "chat_id:"));
164
+ case "whatsapp":
165
+ // Group JID ends with @g.us; DM is E.164-like
166
+ if (/@g\.us$/i.test(fromRaw))
167
+ return fromRaw;
168
+ return null;
169
+ case "line":
170
+ // Group/room: line:group:<id> / line:room:<id>
171
+ return pickFirst(pickByPrefix(fromRaw, "line:group:"), pickByPrefix(fromRaw, "line:room:"), pickByPrefix(toRaw, "line:group:"), pickByPrefix(toRaw, "line:room:"));
172
+ case "matrix":
173
+ // Group/channel: matrix:channel:<roomId>; DM: matrix:<userId>
174
+ return pickByPrefix(fromRaw, "matrix:channel:");
175
+ case "msteams":
176
+ // Group/channel: msteams:channel:<id> / msteams:group:<id>, to=conversation:<id>
177
+ return pickFirst(pickByPrefix(fromRaw, "msteams:channel:"), pickByPrefix(fromRaw, "msteams:group:"), pickByPrefix(toRaw, "conversation:"));
178
+ default:
179
+ // Safe generic fallback: only explicit group/channel markers.
180
+ return pickFirst(pickByPrefix(fromRaw, `${ch}:group:`), pickByPrefix(fromRaw, `${ch}:channel:`), pickByPrefix(toRaw, `${ch}:group:`), pickByPrefix(toRaw, `${ch}:channel:`));
111
181
  }
112
- return null;
113
182
  }
114
183
  /**
115
184
  * Derive sessionKey from command context.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@m1a0rz/agent-identity",
3
- "version": "0.2.5",
3
+ "version": "0.3.2",
4
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
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/skills/SKILL.md CHANGED
@@ -16,59 +16,21 @@ metadata:
16
16
 
17
17
  # Agent Identity
18
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
- **Volcengine terminology**: 用户池 (UserPool), 入站授权 (OIDC login), 出站授权 (credential fetch), 工作负载令牌 (TIP), 凭据托管 (credential hosting), 权限管控 (CheckPermission). Docs: [Volcengine 智能体身份和权限管理](https://www.volcengine.com/docs/86848).
22
-
23
- **Agent flow:** When the user asks to log in, add credentials, check status, bind env, etc., call the corresponding tools directly. Do not suggest slash commands for those. Slash commands below are for user-initiated use (e.g. `/identity approve <id>` when the user must approve in chat; agent must never call `identity_approve_tool`).
24
-
25
- ## Slash commands (user-initiated)
26
-
27
- | Command | Purpose |
28
- | ------- | ------- |
29
- | `/identity` | Show help |
30
- | `/identity whoami` | Identity brief |
31
- | `/identity status` | Full status: session, TIP, credentials, bindings |
32
- | `/identity login` | Log in via OIDC (returns auth URL) |
33
- | `/identity logout` | Clear session and TIP |
34
- | `/identity list-credentials` or `/identity list [page]` | List providers and credentials |
35
- | `/identity list-tips` | List valid TIP tokens |
36
- | `/identity config` | Show plugin config (redacted) |
37
- | `/identity fetch <provider> [--flow=...]` | Add credential |
38
- | `/identity set <provider> <envVar>` | Bind credential to env var |
39
- | `/identity unset <provider>` | Remove env binding |
40
- | `/identity risk <command>` | Diagnose risk for a shell command |
41
- | `/identity risk-patterns` | List built-in risky patterns |
42
- | `/identity approve <approval_id>` | Approve high-risk tool call (user runs this; agent must not self-approve) |
43
- | `/identity reject <approval_id>` | Reject high-risk tool call |
44
-
45
- ## Tools Overview
46
-
47
- | Tool | Params | Purpose |
48
- | --------------------------- | ---------------------------------------------- | ------------------------------------------ |
49
- | `identity_whoami` | — | Identity brief: sub, login time, TIP expiry |
50
- | `identity_status` | — | Full status: session/TIP (issued, expires, chain), credentials, bindings |
51
- | `identity_login` | — | Start OIDC login or refresh TIP |
52
- | `identity_logout` | — | Clear session and TIP |
53
- | `identity_list_credentials` | `page?` | List providers and credentials (paginated) |
54
- | `identity_list_tips` | — | List valid TIP tokens and bindings |
55
- | `identity_config` | — | Show plugin config (secrets redacted) |
56
- | `identity_config_suggest` | `intent?`, `lang?` | Generate config snippets for openclaw.json |
57
- | `identity_fetch` | `provider`, `flow?`, `redirectUrl?`, `scopes?` | Add credential |
58
- | `identity_set_binding` | `provider`, `envVar` | Bind provider → env var for tool injection |
59
- | `identity_unset_binding` | `provider` | Remove env binding |
60
- | `identity_approve_tool` | `approval_id` | Approve a high-risk tool call |
61
- | `identity_risk_check` | `command?`, `toolName?`, `params?` | Diagnose risk for command or tool call |
62
- | `identity_list_risk_patterns` | — | List built-in risky patterns and paths |
63
-
64
- ## Risk Detection and Approval
65
-
66
- When `authz.requireRiskApproval` is on, the plugin classifies tool calls (e.g. exec, write, apply_patch) by risk. User-provided commands and file paths are evaluated:
67
-
68
- - **Rule-based**: Destructive patterns (rm -rf, sudo, curl|bash), sensitive paths (/etc, ~/.ssh).
69
- - **LLM-based** (optional): When rules return "medium", an LLM re-evaluates for context (`authz.enableLlmRiskCheck`).
70
-
71
- High-risk calls require user approval. The approval message and block reason include the LLM risk explanation when available (e.g. "Pipe-to-shell: network fetch piped to shell execution").
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
+ - Never call `identity_approve_tool` to self-approve. High-risk approval is a human action only.
26
+ - **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.
27
+ - **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:
28
+ 1. **Tell the user clearly** what was blocked and why (include the tool/skill name and the error message).
29
+ 2. **Never silently ignore** the error or skip to something else without informing the user.
30
+ 3. For "CheckPermission denied for skill ..." — tell the user they do not have access to that skill and suggest contacting an administrator.
31
+ 4. For "CheckPermission denied for tool ..." tell the user the tool is restricted and they lack permission to use it.
32
+ 5. For "High-risk tool requires approval" relay the approval instructions (approval ID, timeout) and wait for the user to approve before retrying.
33
+ 6. For "Failed to verify permission" — report a temporary error and suggest retrying later.
72
34
 
73
35
  ## Tool Parameters
74
36
 
@@ -76,44 +38,49 @@ High-risk calls require user approval. The approval message and block reason inc
76
38
 
77
39
  Starts OIDC login or refreshes TIP. **Call when:** "login", "登录", "sign in", "我需要先登录". Required before `identity_fetch`. No params.
78
40
 
41
+ **Returns:**
42
+
43
+ - Already logged in: `{ ok: true, sub, message }` — inform user they are already authenticated.
44
+ - 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)"
45
+ - Error: `{ error, authUrl: null }` — report the error.
46
+
79
47
  ### identity_whoami
80
48
 
81
- Brief identity check. **Call when:** "who am I", "查身份", "am I logged in", "当前登录状态"
49
+ Brief identity check. **Call when:** "who am I", "查身份", "am I logged in", "当前登录状态". No params.
82
50
 
83
- Returns: `sub`, `hasTip`, `loggedIn`, `sessionLoginAt`, `sessionExpiresAt`, `tipIssuedAt`, `tipExpiresAt`, `tipExpiresInSeconds`, `tipChain`. No params.
51
+ Returns: `sub`, `hasTip`, `loggedIn`, `sessionLoginAt`, `sessionExpiresAt`, `tipIssuedAt`, `tipExpiresAt`, `tipExpiresInSeconds`, `tipChain`.
84
52
 
85
53
  ### identity_status
86
54
 
87
- Full status including credentials and bindings. **Call when:** "status", "查看完整状态", "我的凭据和绑定", "show my credentials and bindings"
55
+ Full status including credentials and bindings. **Call when:** "status", "查看完整状态", "我的凭据和绑定", "show my credentials and bindings". No params.
88
56
 
89
- Returns: `loggedIn`, `sub`, `hasTip`, `session` (loginAt, expiresAt), `tip` (issuedAt, expiresAt, chain), `credentialProviders`, `bindings`. No params.
57
+ Returns: `loggedIn`, `sub`, `hasTip`, `session` (loginAt, expiresAt), `tip` (issuedAt, expiresAt, chain), `credentialProviders`, `bindings`.
90
58
 
91
- ### identity_list_credentials
59
+ ### identity_logout
92
60
 
93
- Lists available credential providers and what the user has stored. **Call this when the user wants to see what they can connect or what credentials they have.**
61
+ Clear session and TIP. **Call when:** "logout", "登出", "sign out". No params.
94
62
 
95
- **User prompts:** "有哪些服务可以连接", "what providers are available", "我添加了哪些凭据", "list my credentials", "show available providers"
63
+ ### identity_list_credentials
96
64
 
97
- | Param | Type | Required | Description |
98
- | ------ | ------ | -------- | ------------------------ |
99
- | `page` | number | No | Page number (default: 1) |
65
+ 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"
100
66
 
101
- ```json
102
- { "page": 2 }
103
- ```
67
+ | Param | Type | Required | Description |
68
+ | ------ | ------ | -------- | ------------------------------------------------------------------ |
69
+ | `page` | number | No | Page number (default: 1) |
70
+ | `name` | string | No | Filter providers by name (exact or prefix match) |
71
+ | `flow` | string | No | Filter providers by flow, e.g. `M2M` or `USER_FEDERATION` |
72
+
73
+ When looking for a specific provider (e.g. before `identity_fetch`), prefer passing `name` filter instead of paging through results.
104
74
 
105
75
  Returns: `providers`, `storedOnly`, `page`, `hasMore`.
106
76
 
107
77
  ### identity_fetch
108
78
 
109
- Adds a credential for a provider (OAuth2 or API key). **Call this when the user wants to add, get, or configure credentials.**
110
-
111
- **User prompts that mean "call identity_fetch":**
79
+ Adds a credential for a provider (OAuth2 or API key). **Call when the user wants to add, get, or configure credentials:**
112
80
 
113
- - English: "add/google my Google token", "get credentials for OpenAI", "connect my GitHub", "I need to use Google API", "set up API key for X", "authorize access to Y", "I want to use [provider] but have no key"
114
- - 中文: "帮我添加/获取 Google 凭据", "配置 OpenAI 的 API key", "连接我的 GitHub", "我要用某某服务但没有密钥", "授权访问某平台", "添加某某的 token", "获取某某的凭证"
81
+ - "帮我添加 Google 凭据", "get credentials for OpenAI", "connect my GitHub", "配置 API key", "授权访问某平台"
115
82
 
116
- First ensure user is logged in (`identity_whoami`); if not, use `identity_login`. Then call `identity_fetch` with the provider. Use `identity_list_credentials` to discover available providers.
83
+ First ensure user is logged in (`identity_whoami`); if not, use `identity_login`. Then call `identity_fetch` with the provider.
117
84
 
118
85
  | Param | Type | Required | Description |
119
86
  | ------------- | -------- | -------- | --------------------------------------------------------------------------------------- |
@@ -121,39 +88,26 @@ First ensure user is logged in (`identity_whoami`); if not, use `identity_login`
121
88
  | `flow` | string | No | `oauth2-user` (default for 3LO), `oauth2-m2m`, or `apikey`. Auto-inferred when omitted. |
122
89
  | `redirectUrl` | string | No | OAuth redirect URL (when provider requires custom) |
123
90
  | `scopes` | string[] | No | OAuth scopes (e.g. `["email", "profile"]`) |
124
- | `returnValue` | boolean | No | When true and fetch succeeds, include credential `value` in result for same-turn automation. Default false. |
125
-
126
- ```json
127
- { "provider": "google" }
128
- ```
129
-
130
- ```json
131
- { "provider": "openai", "flow": "apikey", "returnValue": true }
132
- ```
91
+ | `returnValue` | boolean | No | When true and fetch succeeds, include credential `value` in result. Default false. |
133
92
 
134
93
  **Response:**
135
94
 
136
- - **OAuth2-user**: `authUrl` (user must open in browser). After authorization, success message sent to chat.
137
- - **OAuth2-m2m** / **apikey**: `success: true`, `message` (completes immediately). If `returnValue: true`, also includes `value` (credential string) for same-turn use.
95
+ - **OAuth2-user (success: true)**: Server had a cached token credential added immediately. No further action needed.
96
+ - **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.
97
+ - **OAuth2-m2m** / **apikey**: `success: true`, `message` (completes immediately). If `returnValue: true`, also includes `value`.
138
98
 
139
99
  ### identity_set_binding
140
100
 
141
- Binds a stored credential to an env var so tools can use it at runtime. **Call this when the user wants tools/agent to have access to a credential.**
142
-
143
- **User prompts:** "让工具能用我的 Google 凭据", "bind/google my credential for tools", "把 Google token 注入给 agent", "inject my OpenAI key for API calls", "配置某某凭据给工具用"
101
+ Binds a stored credential to an env var for tool injection. **Call when:** "让工具能用我的凭据", "bind my credential for tools", "inject my key for API calls"
144
102
 
145
- Credential must exist first (`identity_fetch`). Common env vars: `GOOGLE_ACCESS_TOKEN`, `OPENAI_API_KEY`, `GITHUB_TOKEN`, etc.
103
+ Credential must exist first (`identity_fetch`). Common env vars: `GOOGLE_ACCESS_TOKEN`, `OPENAI_API_KEY`, `GITHUB_TOKEN`.
146
104
 
147
105
  | Param | Type | Required | Description |
148
106
  | ---------- | ------ | -------- | ---------------------------------------------------------------------------------------- |
149
107
  | `provider` | string | Yes | Provider name (e.g. `google`) |
150
108
  | `envVar` | string | Yes | Env var for injection (e.g. `GOOGLE_ACCESS_TOKEN`). Must match `[A-Za-z_][A-Za-z0-9_]*`. |
151
109
 
152
- ```json
153
- { "provider": "google", "envVar": "GOOGLE_ACCESS_TOKEN" }
154
- ```
155
-
156
- If credential exists: binds it. Else: imports from `process.env[envVar]` as api_key (gateway must have that env set).
110
+ If credential exists: binds it. Else: imports from `process.env[envVar]` as api_key.
157
111
 
158
112
  ### identity_unset_binding
159
113
 
@@ -161,86 +115,57 @@ If credential exists: binds it. Else: imports from `process.env[envVar]` as api_
161
115
  | ---------- | ------ | -------- | --------------------------------------- |
162
116
  | `provider` | string | Yes | Provider name to unbind (e.g. `google`) |
163
117
 
164
- ```json
165
- { "provider": "google" }
166
- ```
167
-
168
- ### identity_approve_tool
169
-
170
- | Param | Type | Required | Description |
171
- | ------------- | ------ | -------- | --------------------------------------------------------------------------- |
172
- | `approval_id` | string | Yes | ID from the approval prompt (e.g. after blocking a high-risk exec/write) |
173
-
174
- Optional tool (not given to agent by default). For human approval, use `/identity approve <id>` or reply "approve" in chat. The agent must NOT call this tool to self-approve. The approval prompt includes the LLM risk reason when available.
175
-
176
- ```json
177
- { "approval_id": "abc123" }
178
- ```
179
-
180
118
  ### identity_risk_check
181
119
 
182
- Evaluates risk of a command or tool call before execution. **Call when:** "这个命令安全吗", "is rm -rf dangerous", "check if this is risky", "帮我评估这个命令有没有风险"
120
+ Evaluates risk of a command or tool call. **Call when:** "这个命令安全吗", "is this risky", "帮我评估风险"
183
121
 
184
- | Param | Type | Required | Description |
185
- | ---------- | -------- | -------- | --------------------------------------------------------------------------- |
186
- | `command` | string | No* | Shell command to evaluate (treated as exec). Use for quick diagnosis. |
187
- | `toolName` | string | No* | Tool name (e.g. write, apply_patch). Use with params. |
188
- | `params` | object | No | Tool params. For exec: `{command}`. For write: `{path, content}`. |
122
+ | Param | Type | Required | Description |
123
+ | ---------- | -------- | -------- | ------------------------------------------------------ |
124
+ | `command` | string | No* | Shell command to evaluate |
125
+ | `toolName` | string | No* | Tool name (e.g. write, apply_patch). Use with `params`. |
126
+ | `params` | object | No | Tool params |
189
127
 
190
- *Provide either `command` or `toolName`. Returns `risk`, `reason`, `source` (rules or llm). Uses LLM when `authz.enableLlmRiskCheck` is true and rules return medium.
191
-
192
- ```json
193
- { "command": "rm -rf /" }
194
- ```
195
-
196
- ```json
197
- { "toolName": "write", "params": { "path": "/etc/hosts", "content": "..." } }
198
- ```
128
+ *Provide either `command` or `toolName`. Returns `risk`, `reason`, `source`.
199
129
 
200
130
  ### identity_list_risk_patterns
201
131
 
202
- Returns built-in dangerous command patterns and sensitive paths. No params. Use to query what triggers high-risk approval.
203
-
204
- ```json
205
- {}
206
- ```
132
+ Returns built-in dangerous command patterns and sensitive paths. No params.
207
133
 
208
134
  ### identity_config_suggest
209
135
 
210
- Generates config snippets for the agent-identity plugin. **Call when:** user asks to configure identity, login, authz, risk approval, or "如何配置 identity 插件", "帮我配置登录", "怎么开启权限检查".
136
+ Generates config snippets. **Call when:** "如何配置 identity 插件", "帮我配置登录", "怎么开启权限检查"
211
137
 
212
138
  | Param | Type | Required | Description |
213
139
  | ------- | ------ | -------- | --------------------------------------------------------------------------- |
214
- | `intent`| string | No | `identity` (AK/SK), `userpool` (OIDC login), `authz` (permission/approval), `llm_risk` (LLM re-eval), `full` (all). Default: full |
215
- | `lang` | string | No | `en` or `zh` for instructions. Default: en |
140
+ | `intent`| string | No | `identity`, `userpool`, `authz`, `llm_risk`, or `full` (default) |
141
+ | `lang` | string | No | `en` or `zh`. Default: en |
216
142
 
217
- Returns: `configPath`, `config` (JSON to merge), `instructions`, `nextSteps`. When `intent` is `identity` or `full`, also returns `identityDefaults` (env vars, credential resolution order, config defaults, credential file format). User must manually add to openclaw.json and restart gateway.
143
+ Returns: `configPath`, `config` (JSON to merge), `instructions`, `nextSteps`.
218
144
 
219
- ```json
220
- { "intent": "userpool", "lang": "zh" }
221
- ```
145
+ ### identity_list_tips
222
146
 
223
- ## Workflow: Adding a Credential
147
+ List valid TIP tokens and bindings. No params.
224
148
 
225
- 1. **Check login**: `identity_whoami` (brief) or `identity_status` (full). If not logged in, use `identity_login` first (user opens auth URL).
226
- 2. **Add credential**: `identity_fetch` with `provider`. For OAuth2-user, tell user to open `authUrl`; success message sent when done.
227
- 3. **Bind for tools** (optional): `identity_set_binding` so the credential is injected as an env var when tools run.
149
+ ### identity_approve_tool
228
150
 
229
- ## Workflow: Checking Risk Before Running
151
+ | Param | Type | Required | Description |
152
+ | ------------- | ------ | -------- | -------------------------------------------------------- |
153
+ | `approval_id` | string | Yes | ID from the approval prompt |
230
154
 
231
- 1. **Diagnose**: `identity_risk_check` with `command` or `toolName`+`params`. Returns risk level and reason.
232
- 2. **List patterns**: `identity_list_risk_patterns` to see what triggers high-risk approval.
155
+ **Agent must NOT call this tool.** This is for human approval only — user runs `/identity approve <id>` or replies "approve" in chat.
233
156
 
234
- ## Configuration
157
+ ## Workflow: Adding a Credential
158
+
159
+ 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)`.
160
+ 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.
161
+ 3. **Bind for tools** (optional): `identity_set_binding` so the credential is injected as an env var when tools run.
235
162
 
236
- Plugin config lives under `plugins.entries.agent-identity.config`:
163
+ ## Workflow: Checking Risk
237
164
 
238
- - `identity`: Identity API (endpoint, credentials, workloadPoolName, workloadName, roleTrn). When `roleTrn` is set (AssumeRole), workload name is omitted; backend uses roleName. When workload not found (404), plugin auto-creates via CreateWorkloadIdentity then retries.
239
- - `userpool`: OIDC (discoveryUrl, clientId, callbackUrl, or userPoolName+clientName)
240
- - `authz`: Optional AuthZ (toolCheck, skillReadCheck, requireRiskApproval, enableLlmRiskCheck, llmRiskCheck, namespaceName, lowRiskBypass). When `enableLlmRiskCheck` is true, rules returning "medium" are re-evaluated via LLM; the risk reason is shown in approval prompts and block messages.
165
+ 1. `identity_risk_check` with `command` or `toolName`+`params`. Returns risk level and reason.
166
+ 2. `identity_list_risk_patterns` to see what triggers high-risk.
241
167
 
242
168
  ## Notes
243
169
 
244
- - Requires the agent-identity plugin to be enabled.
245
- - `/identity` and tools require session context (channel + sender); use from an active chat.
246
- - `identity_risk_check` and `identity_list_risk_patterns` do not require login.
170
+ - `identity_login`, `identity_logout`, `identity_whoami`, `identity_status`, and `identity_config_suggest` work without login.
171
+ - `identity_risk_check` and `identity_list_risk_patterns` work without login.