grix-connector 3.1.13 → 3.1.14

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 (73) hide show
  1. package/README.md +241 -241
  2. package/dist/default-skills/grix-access-control/SKILL.md +31 -31
  3. package/dist/default-skills/grix-admin/SKILL.md +35 -35
  4. package/dist/default-skills/grix-agent-dispatch/SKILL.md +89 -89
  5. package/dist/default-skills/grix-chat-state/SKILL.md +56 -56
  6. package/dist/default-skills/grix-egg/SKILL.md +90 -90
  7. package/dist/default-skills/grix-group/SKILL.md +35 -35
  8. package/dist/default-skills/grix-owner-relay/SKILL.md +66 -66
  9. package/dist/default-skills/grix-query/SKILL.md +38 -38
  10. package/dist/default-skills/message-send/SKILL.md +36 -36
  11. package/dist/default-skills/message-unsend/SKILL.md +27 -27
  12. package/dist/default-skills/tailnet-file-share/SKILL.md +65 -65
  13. package/dist/grix.js +0 -0
  14. package/dist/service/platform-adapter.js +59 -16
  15. package/openclaw-plugin/skills/grix-admin/SKILL.md +202 -202
  16. package/openclaw-plugin/skills/grix-admin/references/api-contract.md +210 -210
  17. package/openclaw-plugin/skills/grix-egg/SKILL.md +81 -81
  18. package/openclaw-plugin/skills/grix-egg/references/api-contract.md +40 -40
  19. package/openclaw-plugin/skills/grix-group/SKILL.md +164 -164
  20. package/openclaw-plugin/skills/grix-group/references/api-contract.md +97 -97
  21. package/openclaw-plugin/skills/grix-query/SKILL.md +247 -247
  22. package/openclaw-plugin/skills/grix-register/SKILL.md +86 -86
  23. package/openclaw-plugin/skills/grix-register/references/api-contract.md +76 -76
  24. package/openclaw-plugin/skills/grix-register/references/grix-concepts.md +26 -26
  25. package/openclaw-plugin/skills/grix-register/references/handoff-contract.md +24 -24
  26. package/openclaw-plugin/skills/grix-register/references/openclaw-setup.md +6 -6
  27. package/openclaw-plugin/skills/grix-register/references/user-replies.md +25 -25
  28. package/openclaw-plugin/skills/grix-update/SKILL.md +310 -310
  29. package/openclaw-plugin/skills/grix-update/references/cron-setup.md +56 -56
  30. package/openclaw-plugin/skills/grix-update/references/update-contract.md +149 -149
  31. package/openclaw-plugin/skills/message-send/SKILL.md +197 -197
  32. package/openclaw-plugin/skills/message-unsend/SKILL.md +186 -186
  33. package/openclaw-plugin/skills/message-unsend/flowchart.mermaid +27 -27
  34. package/openclaw-plugin/skills/openclaw-memory-setup/SKILL.md +282 -282
  35. package/openclaw-plugin/skills/openclaw-memory-setup/references/case-study-macpro.md +52 -52
  36. package/openclaw-plugin/skills/openclaw-memory-setup/references/host-readiness.md +147 -147
  37. package/openclaw.plugin.json +24 -24
  38. package/package.json +121 -121
  39. package/scripts/install-guardian.mjs +27 -27
  40. package/scripts/install-guardian.sh +25 -25
  41. package/scripts/upgrade-guardian.sh +104 -104
  42. package/dist/adapter/claude/claude-bridge-server.js +0 -1
  43. package/dist/adapter/claude/claude-tools.js +0 -1
  44. package/dist/adapter/claude/claude-worker-client.js +0 -1
  45. package/dist/adapter/claude/mcp-http-launcher.js +0 -2
  46. package/dist/adapter/claude/result-timeout.js +0 -1
  47. package/dist/adapter/deepseek/deepseek-adapter.js +0 -6
  48. package/dist/adapter/deepseek/index.js +0 -1
  49. package/dist/adapter/qwen/index.js +0 -1
  50. package/dist/adapter/qwen/qwen-adapter.js +0 -4
  51. package/dist/aibot/client.js +0 -1
  52. package/dist/aibot/index.js +0 -1
  53. package/dist/aibot/types.js +0 -0
  54. package/dist/core/file-ops/handler.js +0 -1
  55. package/dist/core/file-ops/list-files.js +0 -1
  56. package/dist/core/file-ops/types.js +0 -0
  57. package/dist/default-skills/grix-task-status/SKILL.md +0 -36
  58. package/dist/log.js +0 -3
  59. package/dist/main.js +0 -31
  60. package/dist/mcp/stream-http/config.js +0 -1
  61. package/dist/mcp/stream-http/connection-binding.js +0 -1
  62. package/dist/mcp/stream-http/event-tool-executor.js +0 -1
  63. package/dist/mcp/stream-http/gateway.js +0 -1
  64. package/dist/mcp/stream-http/index.js +0 -1
  65. package/dist/mcp/stream-http/security.js +0 -1
  66. package/dist/mcp/stream-http/session-manager.js +0 -1
  67. package/dist/mcp/stream-http/tool-executor.js +0 -1
  68. package/dist/mcp/stream-http/tool-registry.js +0 -1
  69. package/dist/mcp/stream-http/tool-schemas.js +0 -1
  70. package/dist/session/index.js +0 -1
  71. package/dist/session/manager.js +0 -1
  72. package/dist/transport/index.js +0 -1
  73. package/dist/transport/json-rpc.js +0 -3
@@ -1,247 +1,247 @@
1
- ---
2
- name: grix-query
3
- description: Use the typed `grix_query` tool for Grix contact lookup, keyword search, session search, and session message history lookup. Trigger when users ask to find contacts, search conversations, list visible sessions, or inspect recent messages in a known session.
4
- ---
5
-
6
- # Grix Query
7
-
8
- Use the `grix_query` tool for read-only Grix lookup actions.
9
- This skill is only for querying existing contacts, sessions, and raw session messages.
10
-
11
- ## Workflow
12
-
13
- 1. Parse the user request into one action:
14
- `contact_search`, `session_search`, `message_history`, or `message_search`.
15
- 2. Validate required fields before any tool call.
16
- 3. Start with one `grix_query` call for the first page.
17
- 4. If the result is paginated and `has_more` is `true`, continue paging when the user asked for all results, when the target is still unresolved, or when one page is clearly insufficient.
18
- 5. If the user wants message history or in-session keyword search but no `sessionId` is known, locate the target session first through `session_search` or ask the user for a precise target.
19
- 6. Return exact remediation for scope, auth, and parameter failures.
20
-
21
- ## Tool Contract
22
-
23
- For Grix query actions, always call:
24
-
25
- 1. Tool: `grix_query`
26
- 2. `action`: one of `contact_search`, `session_search`, `message_history`, or `message_search`
27
- 3. `accountId`: required; pass the exact current Grix account ID
28
-
29
- Rules:
30
-
31
- 1. Pass query parameters with their exact typed field names.
32
- 2. For `contact_search` and `session_search`, use exactly one of these modes:
33
- exact lookup with `id`, keyword search with `keyword`, or list-all with neither.
34
- 3. If both `id` and `keyword` are present, the backend will prioritize `id`; avoid sending both unless you explicitly want exact-match behavior.
35
- 4. Use `sessionId`, `beforeId`, and `limit` explicitly for message reads; `message_search` also requires `keyword`.
36
- 5. Never invent a `sessionId`. Resolve it from context, from a previous tool result, or ask the user.
37
- 6. Use one tool call per page. Repeated calls are allowed only for pagination or for resolving an ambiguous target.
38
- 7. When paging `contact_search` or `session_search`, keep the same filter and advance `offset`.
39
- 8. When paging `message_history` or `message_search`, reuse the same `sessionId` and set `beforeId` to the oldest message ID from the previous page.
40
-
41
- ## Lookup Usage
42
-
43
- ### Single Lookup (with ID)
44
-
45
- When the user provides one exact ID, pass it for a precise match:
46
-
47
- 1. Single contact lookup: `action: "contact_search"` + `id`
48
- 2. Single session lookup: `action: "session_search"` + `id`
49
-
50
- ID meaning:
51
-
52
- 1. `contact_search.id`: contact or Agent numeric ID, e.g. `1002`
53
- 2. `session_search.id`: exact session ID string, e.g. `task_room_9083`
54
-
55
- Examples:
56
-
57
- ```json
58
- {
59
- "action": "contact_search",
60
- "accountId": "primary",
61
- "id": "1002"
62
- }
63
- ```
64
-
65
- ```json
66
- {
67
- "action": "session_search",
68
- "accountId": "primary",
69
- "id": "task_room_9083"
70
- }
71
- ```
72
-
73
- ### Keyword Search
74
-
75
- When the user provides a fuzzy name, title, username, or other search phrase, pass `keyword`:
76
-
77
- ```json
78
- {
79
- "action": "contact_search",
80
- "accountId": "primary",
81
- "keyword": "atlas user"
82
- }
83
- ```
84
-
85
- ```json
86
- {
87
- "action": "session_search",
88
- "accountId": "primary",
89
- "keyword": "taskroom9083"
90
- }
91
- ```
92
-
93
- ### List All (without ID or keyword)
94
-
95
- When the user asks to list all contacts or sessions, call without `id` and without `keyword`:
96
-
97
- ```json
98
- {
99
- "action": "contact_search",
100
- "accountId": "primary"
101
- }
102
- ```
103
-
104
- ```json
105
- {
106
- "action": "session_search",
107
- "accountId": "primary"
108
- }
109
- ```
110
-
111
- Returns a paginated result with `has_more`, `list`, and default page size of 20.
112
- Use `limit` and `offset` to paginate through results.
113
-
114
- If the user asks for all results, keep fetching additional pages until `has_more` is `false`.
115
- If the user only needs one match or one page is enough to answer, stop after the first sufficient page.
116
-
117
- ```json
118
- {
119
- "action": "contact_search",
120
- "accountId": "primary",
121
- "limit": 50,
122
- "offset": 20
123
- }
124
- ```
125
-
126
- ## Action Contracts
127
-
128
- ### contact_search
129
-
130
- Purpose: search the owner's Grix contact directory.
131
-
132
- **Without `id` and without `keyword`**: returns all contacts (friends + agents) in a paginated list, sorted by `created_at` descending. Default page size 20.
133
-
134
- **With `id`**: returns the exact matching contact record.
135
-
136
- **With `keyword`**: searches contact remark name, nickname, username, and numeric ID prefix.
137
-
138
- Input:
139
-
140
- 1. `id` (contact ID, numeric string) — optional
141
- 2. `keyword` — optional
142
- 3. `limit` — optional, default 20
143
- 4. `offset` — optional, default 0
144
-
145
- Guardrails:
146
-
147
- 1. Use `id` when the target contact ID is already known and you need the exact entry.
148
- 2. Use `keyword` for fuzzy search; do not use `id` for partial matches.
149
- 3. Without `id` and `keyword`, the result includes both user contacts and agent contacts merged and sorted.
150
- 4. Check `has_more` to determine if additional pages exist.
151
- 5. When paging, keep the same filters and increase `offset` by the number of items already fetched.
152
- 6. If the user asked for all matches, continue until `has_more` is `false`.
153
- 7. Do not jump directly to session history from a vague contact hint; resolve the contact or session first.
154
-
155
- ### session_search
156
-
157
- Purpose: search the owner's visible sessions.
158
-
159
- **Without `id` and without `keyword`**: returns all visible sessions in a paginated list, ordered by pinned status and `last_active_at`. Default page size 20.
160
-
161
- **With `id`**: returns the exact matching session.
162
-
163
- **With `keyword`**: searches session title and `session_id`.
164
-
165
- Input:
166
-
167
- 1. `id` (session ID) — optional
168
- 2. `keyword` — optional
169
- 3. `limit` — optional, default 20
170
- 4. `offset` — optional, default 0
171
-
172
- Guardrails:
173
-
174
- 1. Use `id` when the target session ID is already known.
175
- 2. Use `keyword` for fuzzy search by title or session ID text.
176
- 3. Without `id` and `keyword`, the result shows all sessions the agent can see.
177
- 4. Check `has_more` to determine if additional pages exist.
178
- 5. When paging, keep the same filters and increase `offset` by the number of items already fetched.
179
- 6. If the user asked for all matches, continue until `has_more` is `false`.
180
- 7. If multiple sessions match, present the candidates and let the user choose before reading history.
181
-
182
- ### message_history
183
-
184
- Purpose: read recent message history from a known session.
185
-
186
- Required input:
187
-
188
- 1. `sessionId`
189
-
190
- Optional input:
191
-
192
- 1. `beforeId`
193
- 2. `limit`
194
-
195
- Guardrails:
196
-
197
- 1. Only call this after the target session is unambiguous.
198
- 2. Use `beforeId` only for older-page pagination.
199
- 3. For the next page, set `beforeId` to the oldest message ID returned in the previous page.
200
- 4. If the user asked for more history and `has_more` is `true`, keep paging until enough history is collected or no more pages remain.
201
- 5. Do not claim to have full history if only one page was fetched.
202
-
203
- ### message_search
204
-
205
- Purpose: search messages by keyword inside one known session.
206
-
207
- Required input:
208
-
209
- 1. `sessionId`
210
- 2. `keyword`
211
-
212
- Optional input:
213
-
214
- 1. `beforeId`
215
- 2. `limit`
216
-
217
- Guardrails:
218
-
219
- 1. Only call this after the target session is unambiguous.
220
- 2. `keyword` must be the real search phrase; do not fake an empty keyword just to reuse this action.
221
- 3. For the next page, keep the same `keyword` and `sessionId`, and set `beforeId` to the oldest message ID returned in the previous page.
222
- 4. If the user asked for all matches and `has_more` is `true`, keep paging until enough matches are collected or no more pages remain.
223
- 5. If the user only asked whether a keyword appeared, one sufficient page can stop the search, but state clearly that the result is partial when you did not exhaust all pages.
224
-
225
- ## Error Handling Rules
226
-
227
- 1. `403/20011`:
228
- report missing scope and ask the owner to grant the required scope in the Aibot Agent permission page.
229
- 2. `401/10001`:
230
- report invalid key/auth and suggest checking agent config or rotating the API key.
231
- 3. `403/10002`:
232
- report the agent is not active or has an invalid provider type.
233
- 4. `400/10003`:
234
- report invalid or missing parameters and ask the user for corrected values.
235
- 5. `404/4004`:
236
- report the target session does not exist or is not visible.
237
- 6. Other errors:
238
- return the backend `msg` and stop automatic retries.
239
-
240
- ## Response Style
241
-
242
- 1. State the query result first.
243
- 2. Include key identifiers from successful lookups:
244
- `peer_id` / `peer_type` for contacts, `session_id` for sessions, and message identifiers for history.
245
- 3. If only part of a paginated result was fetched, state that clearly.
246
- 4. If multiple pages were fetched, summarize that the answer is merged from several pages.
247
- 5. Never hide scope or auth errors behind generic wording.
1
+ ---
2
+ name: grix-query
3
+ description: Use the typed `grix_query` tool for Grix contact lookup, keyword search, session search, and session message history lookup. Trigger when users ask to find contacts, search conversations, list visible sessions, or inspect recent messages in a known session.
4
+ ---
5
+
6
+ # Grix Query
7
+
8
+ Use the `grix_query` tool for read-only Grix lookup actions.
9
+ This skill is only for querying existing contacts, sessions, and raw session messages.
10
+
11
+ ## Workflow
12
+
13
+ 1. Parse the user request into one action:
14
+ `contact_search`, `session_search`, `message_history`, or `message_search`.
15
+ 2. Validate required fields before any tool call.
16
+ 3. Start with one `grix_query` call for the first page.
17
+ 4. If the result is paginated and `has_more` is `true`, continue paging when the user asked for all results, when the target is still unresolved, or when one page is clearly insufficient.
18
+ 5. If the user wants message history or in-session keyword search but no `sessionId` is known, locate the target session first through `session_search` or ask the user for a precise target.
19
+ 6. Return exact remediation for scope, auth, and parameter failures.
20
+
21
+ ## Tool Contract
22
+
23
+ For Grix query actions, always call:
24
+
25
+ 1. Tool: `grix_query`
26
+ 2. `action`: one of `contact_search`, `session_search`, `message_history`, or `message_search`
27
+ 3. `accountId`: required; pass the exact current Grix account ID
28
+
29
+ Rules:
30
+
31
+ 1. Pass query parameters with their exact typed field names.
32
+ 2. For `contact_search` and `session_search`, use exactly one of these modes:
33
+ exact lookup with `id`, keyword search with `keyword`, or list-all with neither.
34
+ 3. If both `id` and `keyword` are present, the backend will prioritize `id`; avoid sending both unless you explicitly want exact-match behavior.
35
+ 4. Use `sessionId`, `beforeId`, and `limit` explicitly for message reads; `message_search` also requires `keyword`.
36
+ 5. Never invent a `sessionId`. Resolve it from context, from a previous tool result, or ask the user.
37
+ 6. Use one tool call per page. Repeated calls are allowed only for pagination or for resolving an ambiguous target.
38
+ 7. When paging `contact_search` or `session_search`, keep the same filter and advance `offset`.
39
+ 8. When paging `message_history` or `message_search`, reuse the same `sessionId` and set `beforeId` to the oldest message ID from the previous page.
40
+
41
+ ## Lookup Usage
42
+
43
+ ### Single Lookup (with ID)
44
+
45
+ When the user provides one exact ID, pass it for a precise match:
46
+
47
+ 1. Single contact lookup: `action: "contact_search"` + `id`
48
+ 2. Single session lookup: `action: "session_search"` + `id`
49
+
50
+ ID meaning:
51
+
52
+ 1. `contact_search.id`: contact or Agent numeric ID, e.g. `1002`
53
+ 2. `session_search.id`: exact session ID string, e.g. `task_room_9083`
54
+
55
+ Examples:
56
+
57
+ ```json
58
+ {
59
+ "action": "contact_search",
60
+ "accountId": "primary",
61
+ "id": "1002"
62
+ }
63
+ ```
64
+
65
+ ```json
66
+ {
67
+ "action": "session_search",
68
+ "accountId": "primary",
69
+ "id": "task_room_9083"
70
+ }
71
+ ```
72
+
73
+ ### Keyword Search
74
+
75
+ When the user provides a fuzzy name, title, username, or other search phrase, pass `keyword`:
76
+
77
+ ```json
78
+ {
79
+ "action": "contact_search",
80
+ "accountId": "primary",
81
+ "keyword": "atlas user"
82
+ }
83
+ ```
84
+
85
+ ```json
86
+ {
87
+ "action": "session_search",
88
+ "accountId": "primary",
89
+ "keyword": "taskroom9083"
90
+ }
91
+ ```
92
+
93
+ ### List All (without ID or keyword)
94
+
95
+ When the user asks to list all contacts or sessions, call without `id` and without `keyword`:
96
+
97
+ ```json
98
+ {
99
+ "action": "contact_search",
100
+ "accountId": "primary"
101
+ }
102
+ ```
103
+
104
+ ```json
105
+ {
106
+ "action": "session_search",
107
+ "accountId": "primary"
108
+ }
109
+ ```
110
+
111
+ Returns a paginated result with `has_more`, `list`, and default page size of 20.
112
+ Use `limit` and `offset` to paginate through results.
113
+
114
+ If the user asks for all results, keep fetching additional pages until `has_more` is `false`.
115
+ If the user only needs one match or one page is enough to answer, stop after the first sufficient page.
116
+
117
+ ```json
118
+ {
119
+ "action": "contact_search",
120
+ "accountId": "primary",
121
+ "limit": 50,
122
+ "offset": 20
123
+ }
124
+ ```
125
+
126
+ ## Action Contracts
127
+
128
+ ### contact_search
129
+
130
+ Purpose: search the owner's Grix contact directory.
131
+
132
+ **Without `id` and without `keyword`**: returns all contacts (friends + agents) in a paginated list, sorted by `created_at` descending. Default page size 20.
133
+
134
+ **With `id`**: returns the exact matching contact record.
135
+
136
+ **With `keyword`**: searches contact remark name, nickname, username, and numeric ID prefix.
137
+
138
+ Input:
139
+
140
+ 1. `id` (contact ID, numeric string) — optional
141
+ 2. `keyword` — optional
142
+ 3. `limit` — optional, default 20
143
+ 4. `offset` — optional, default 0
144
+
145
+ Guardrails:
146
+
147
+ 1. Use `id` when the target contact ID is already known and you need the exact entry.
148
+ 2. Use `keyword` for fuzzy search; do not use `id` for partial matches.
149
+ 3. Without `id` and `keyword`, the result includes both user contacts and agent contacts merged and sorted.
150
+ 4. Check `has_more` to determine if additional pages exist.
151
+ 5. When paging, keep the same filters and increase `offset` by the number of items already fetched.
152
+ 6. If the user asked for all matches, continue until `has_more` is `false`.
153
+ 7. Do not jump directly to session history from a vague contact hint; resolve the contact or session first.
154
+
155
+ ### session_search
156
+
157
+ Purpose: search the owner's visible sessions.
158
+
159
+ **Without `id` and without `keyword`**: returns all visible sessions in a paginated list, ordered by pinned status and `last_active_at`. Default page size 20.
160
+
161
+ **With `id`**: returns the exact matching session.
162
+
163
+ **With `keyword`**: searches session title and `session_id`.
164
+
165
+ Input:
166
+
167
+ 1. `id` (session ID) — optional
168
+ 2. `keyword` — optional
169
+ 3. `limit` — optional, default 20
170
+ 4. `offset` — optional, default 0
171
+
172
+ Guardrails:
173
+
174
+ 1. Use `id` when the target session ID is already known.
175
+ 2. Use `keyword` for fuzzy search by title or session ID text.
176
+ 3. Without `id` and `keyword`, the result shows all sessions the agent can see.
177
+ 4. Check `has_more` to determine if additional pages exist.
178
+ 5. When paging, keep the same filters and increase `offset` by the number of items already fetched.
179
+ 6. If the user asked for all matches, continue until `has_more` is `false`.
180
+ 7. If multiple sessions match, present the candidates and let the user choose before reading history.
181
+
182
+ ### message_history
183
+
184
+ Purpose: read recent message history from a known session.
185
+
186
+ Required input:
187
+
188
+ 1. `sessionId`
189
+
190
+ Optional input:
191
+
192
+ 1. `beforeId`
193
+ 2. `limit`
194
+
195
+ Guardrails:
196
+
197
+ 1. Only call this after the target session is unambiguous.
198
+ 2. Use `beforeId` only for older-page pagination.
199
+ 3. For the next page, set `beforeId` to the oldest message ID returned in the previous page.
200
+ 4. If the user asked for more history and `has_more` is `true`, keep paging until enough history is collected or no more pages remain.
201
+ 5. Do not claim to have full history if only one page was fetched.
202
+
203
+ ### message_search
204
+
205
+ Purpose: search messages by keyword inside one known session.
206
+
207
+ Required input:
208
+
209
+ 1. `sessionId`
210
+ 2. `keyword`
211
+
212
+ Optional input:
213
+
214
+ 1. `beforeId`
215
+ 2. `limit`
216
+
217
+ Guardrails:
218
+
219
+ 1. Only call this after the target session is unambiguous.
220
+ 2. `keyword` must be the real search phrase; do not fake an empty keyword just to reuse this action.
221
+ 3. For the next page, keep the same `keyword` and `sessionId`, and set `beforeId` to the oldest message ID returned in the previous page.
222
+ 4. If the user asked for all matches and `has_more` is `true`, keep paging until enough matches are collected or no more pages remain.
223
+ 5. If the user only asked whether a keyword appeared, one sufficient page can stop the search, but state clearly that the result is partial when you did not exhaust all pages.
224
+
225
+ ## Error Handling Rules
226
+
227
+ 1. `403/20011`:
228
+ report missing scope and ask the owner to grant the required scope in the Aibot Agent permission page.
229
+ 2. `401/10001`:
230
+ report invalid key/auth and suggest checking agent config or rotating the API key.
231
+ 3. `403/10002`:
232
+ report the agent is not active or has an invalid provider type.
233
+ 4. `400/10003`:
234
+ report invalid or missing parameters and ask the user for corrected values.
235
+ 5. `404/4004`:
236
+ report the target session does not exist or is not visible.
237
+ 6. Other errors:
238
+ return the backend `msg` and stop automatic retries.
239
+
240
+ ## Response Style
241
+
242
+ 1. State the query result first.
243
+ 2. Include key identifiers from successful lookups:
244
+ `peer_id` / `peer_type` for contacts, `session_id` for sessions, and message identifiers for history.
245
+ 3. If only part of a paginated result was fetched, state that clearly.
246
+ 4. If multiple pages were fetched, summarize that the answer is merged from several pages.
247
+ 5. Never hide scope or auth errors behind generic wording.