@downcity/plugins 1.0.153 → 1.0.157

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 (175) hide show
  1. package/bin/chat/ChatPlugin.d.ts +2 -10
  2. package/bin/chat/ChatPlugin.d.ts.map +1 -1
  3. package/bin/chat/ChatPlugin.js +3 -13
  4. package/bin/chat/ChatPlugin.js.map +1 -1
  5. package/bin/chat/PROMPT.direct.d.ts +1 -1
  6. package/bin/chat/PROMPT.direct.d.ts.map +1 -1
  7. package/bin/chat/PROMPT.direct.js +1 -1
  8. package/bin/chat/PROMPT.direct.js.map +1 -1
  9. package/bin/chat/access/ChatAccessActions.d.ts +11 -0
  10. package/bin/chat/access/ChatAccessActions.d.ts.map +1 -0
  11. package/bin/chat/access/ChatAccessActions.js +137 -0
  12. package/bin/chat/access/ChatAccessActions.js.map +1 -0
  13. package/bin/chat/access/ChatAccessRuntime.d.ts +17 -0
  14. package/bin/chat/access/ChatAccessRuntime.d.ts.map +1 -0
  15. package/bin/chat/access/ChatAccessRuntime.js +31 -0
  16. package/bin/chat/access/ChatAccessRuntime.js.map +1 -0
  17. package/bin/chat/access/ChatAccessSchema.d.ts +16 -0
  18. package/bin/chat/access/ChatAccessSchema.d.ts.map +1 -0
  19. package/bin/chat/access/ChatAccessSchema.js +99 -0
  20. package/bin/chat/access/ChatAccessSchema.js.map +1 -0
  21. package/bin/chat/access/ChatAccessService.d.ts +44 -0
  22. package/bin/chat/access/ChatAccessService.d.ts.map +1 -0
  23. package/bin/chat/access/ChatAccessService.js +281 -0
  24. package/bin/chat/access/ChatAccessService.js.map +1 -0
  25. package/bin/chat/access/ChatAccessStore.d.ts +67 -0
  26. package/bin/chat/access/ChatAccessStore.d.ts.map +1 -0
  27. package/bin/chat/access/ChatAccessStore.js +367 -0
  28. package/bin/chat/access/ChatAccessStore.js.map +1 -0
  29. package/bin/chat/accounts/Crypto.d.ts +1 -1
  30. package/bin/chat/accounts/Crypto.js +1 -1
  31. package/bin/chat/accounts/Store.d.ts +1 -1
  32. package/bin/chat/accounts/Store.js +1 -1
  33. package/bin/chat/channels/BaseChatChannel.d.ts +34 -21
  34. package/bin/chat/channels/BaseChatChannel.d.ts.map +1 -1
  35. package/bin/chat/channels/BaseChatChannel.js +71 -58
  36. package/bin/chat/channels/BaseChatChannel.js.map +1 -1
  37. package/bin/chat/channels/BaseChatChannelQueue.d.ts +1 -1
  38. package/bin/chat/channels/BaseChatChannelQueue.d.ts.map +1 -1
  39. package/bin/chat/channels/BaseChatChannelQueue.js +2 -14
  40. package/bin/chat/channels/BaseChatChannelQueue.js.map +1 -1
  41. package/bin/chat/channels/Configuration.d.ts +2 -2
  42. package/bin/chat/channels/Configuration.d.ts.map +1 -1
  43. package/bin/chat/channels/Configuration.js +3 -3
  44. package/bin/chat/channels/Configuration.js.map +1 -1
  45. package/bin/chat/channels/feishu/Configuration.js +4 -4
  46. package/bin/chat/channels/feishu/Configuration.js.map +1 -1
  47. package/bin/chat/channels/feishu/Feishu.d.ts +2 -0
  48. package/bin/chat/channels/feishu/Feishu.d.ts.map +1 -1
  49. package/bin/chat/channels/feishu/Feishu.js +8 -7
  50. package/bin/chat/channels/feishu/Feishu.js.map +1 -1
  51. package/bin/chat/channels/feishu/FeishuMessageHandler.d.ts +9 -17
  52. package/bin/chat/channels/feishu/FeishuMessageHandler.d.ts.map +1 -1
  53. package/bin/chat/channels/feishu/FeishuMessageHandler.js +6 -17
  54. package/bin/chat/channels/feishu/FeishuMessageHandler.js.map +1 -1
  55. package/bin/chat/channels/qq/Configuration.js +4 -4
  56. package/bin/chat/channels/qq/Configuration.js.map +1 -1
  57. package/bin/chat/channels/qq/QQ.d.ts +2 -0
  58. package/bin/chat/channels/qq/QQ.d.ts.map +1 -1
  59. package/bin/chat/channels/qq/QQ.js +8 -7
  60. package/bin/chat/channels/qq/QQ.js.map +1 -1
  61. package/bin/chat/channels/qq/QQMessageHandler.d.ts +9 -17
  62. package/bin/chat/channels/qq/QQMessageHandler.d.ts.map +1 -1
  63. package/bin/chat/channels/qq/QQMessageHandler.js +7 -25
  64. package/bin/chat/channels/qq/QQMessageHandler.js.map +1 -1
  65. package/bin/chat/channels/telegram/Bot.d.ts +12 -0
  66. package/bin/chat/channels/telegram/Bot.d.ts.map +1 -1
  67. package/bin/chat/channels/telegram/Bot.js +24 -7
  68. package/bin/chat/channels/telegram/Bot.js.map +1 -1
  69. package/bin/chat/channels/telegram/Configuration.js +4 -4
  70. package/bin/chat/channels/telegram/Configuration.js.map +1 -1
  71. package/bin/chat/channels/telegram/TelegramMessageHandler.d.ts +9 -17
  72. package/bin/chat/channels/telegram/TelegramMessageHandler.d.ts.map +1 -1
  73. package/bin/chat/channels/telegram/TelegramMessageHandler.js +5 -16
  74. package/bin/chat/channels/telegram/TelegramMessageHandler.js.map +1 -1
  75. package/bin/chat/runtime/ChatChannelActions.d.ts +3 -2
  76. package/bin/chat/runtime/ChatChannelActions.d.ts.map +1 -1
  77. package/bin/chat/runtime/ChatChannelActions.js +57 -0
  78. package/bin/chat/runtime/ChatChannelActions.js.map +1 -1
  79. package/bin/chat/runtime/ChatChannelConfig.d.ts +2 -2
  80. package/bin/chat/runtime/ChatChannelConfig.js +3 -3
  81. package/bin/chat/runtime/ChatChannelConfig.js.map +1 -1
  82. package/bin/chat/runtime/ChatChannelCore.d.ts +2 -2
  83. package/bin/chat/runtime/ChatChannelCore.js +1 -1
  84. package/bin/chat/runtime/ChatChannelLifecycle.d.ts +1 -1
  85. package/bin/chat/runtime/ChatChannelLifecycle.js +1 -1
  86. package/bin/chat/runtime/PluginPoints.d.ts +0 -24
  87. package/bin/chat/runtime/PluginPoints.d.ts.map +1 -1
  88. package/bin/chat/runtime/PluginPoints.js +0 -24
  89. package/bin/chat/runtime/PluginPoints.js.map +1 -1
  90. package/bin/chat/runtime/QueuedUserMessage.d.ts.map +1 -1
  91. package/bin/chat/runtime/QueuedUserMessage.js +0 -2
  92. package/bin/chat/runtime/QueuedUserMessage.js.map +1 -1
  93. package/bin/chat/types/ChannelConfiguration.d.ts +4 -4
  94. package/bin/chat/types/ChannelConfiguration.d.ts.map +1 -1
  95. package/bin/chat/types/ChatAccess.d.ts +300 -0
  96. package/bin/chat/types/ChatAccess.d.ts.map +1 -0
  97. package/bin/chat/types/ChatAccess.js +22 -0
  98. package/bin/chat/types/ChatAccess.js.map +1 -0
  99. package/bin/chat/types/ChatPromptContext.d.ts +0 -15
  100. package/bin/chat/types/ChatPromptContext.d.ts.map +1 -1
  101. package/bin/index.d.ts +4 -4
  102. package/bin/index.d.ts.map +1 -1
  103. package/bin/index.js +3 -3
  104. package/bin/index.js.map +1 -1
  105. package/bin/skill/Config.d.ts +1 -1
  106. package/bin/skill/Config.js +1 -1
  107. package/bin/skill/runtime/Paths.d.ts +1 -1
  108. package/bin/skill/runtime/Paths.js +1 -1
  109. package/package.json +6 -4
  110. package/scripts/chat-access.test.mjs +126 -0
  111. package/scripts/chat-channel-config.test.mjs +82 -0
  112. package/src/chat/ChatPlugin.ts +4 -24
  113. package/src/chat/PROMPT.direct.ts +1 -1
  114. package/src/chat/PROMPT.direct.ts.txt +1 -1
  115. package/src/chat/access/ChatAccessActions.ts +159 -0
  116. package/src/chat/access/ChatAccessRuntime.ts +41 -0
  117. package/src/chat/access/ChatAccessSchema.ts +104 -0
  118. package/src/chat/access/ChatAccessService.ts +311 -0
  119. package/src/chat/access/ChatAccessStore.ts +462 -0
  120. package/src/chat/accounts/Crypto.ts +1 -1
  121. package/src/chat/accounts/Store.ts +1 -1
  122. package/src/chat/channels/BaseChatChannel.ts +86 -80
  123. package/src/chat/channels/BaseChatChannelQueue.ts +2 -14
  124. package/src/chat/channels/Configuration.ts +3 -3
  125. package/src/chat/channels/feishu/Configuration.ts +4 -4
  126. package/src/chat/channels/feishu/Feishu.ts +11 -9
  127. package/src/chat/channels/feishu/FeishuMessageHandler.ts +16 -40
  128. package/src/chat/channels/qq/Configuration.ts +4 -4
  129. package/src/chat/channels/qq/QQ.ts +11 -9
  130. package/src/chat/channels/qq/QQMessageHandler.ts +17 -49
  131. package/src/chat/channels/telegram/Bot.ts +29 -9
  132. package/src/chat/channels/telegram/Configuration.ts +4 -4
  133. package/src/chat/channels/telegram/TelegramMessageHandler.ts +15 -39
  134. package/src/chat/runtime/ChatChannelActions.ts +72 -1
  135. package/src/chat/runtime/ChatChannelConfig.ts +3 -3
  136. package/src/chat/runtime/ChatChannelCore.ts +1 -1
  137. package/src/chat/runtime/ChatChannelLifecycle.ts +1 -1
  138. package/src/chat/runtime/PluginPoints.ts +0 -24
  139. package/src/chat/runtime/QueuedUserMessage.ts +0 -2
  140. package/src/chat/types/ChannelConfiguration.ts +4 -4
  141. package/src/chat/types/ChatAccess.ts +335 -0
  142. package/src/chat/types/ChatPromptContext.ts +0 -17
  143. package/src/index.ts +27 -31
  144. package/src/skill/Config.ts +1 -1
  145. package/src/skill/runtime/Paths.ts +1 -1
  146. package/bin/auth/runtime/AuthorizationConfig.d.ts +0 -41
  147. package/bin/auth/runtime/AuthorizationConfig.d.ts.map +0 -1
  148. package/bin/auth/runtime/AuthorizationConfig.js +0 -185
  149. package/bin/auth/runtime/AuthorizationConfig.js.map +0 -1
  150. package/bin/auth/runtime/AuthorizationPolicy.d.ts +0 -28
  151. package/bin/auth/runtime/AuthorizationPolicy.d.ts.map +0 -1
  152. package/bin/auth/runtime/AuthorizationPolicy.js +0 -124
  153. package/bin/auth/runtime/AuthorizationPolicy.js.map +0 -1
  154. package/bin/auth/runtime/AuthorizationStore.d.ts +0 -29
  155. package/bin/auth/runtime/AuthorizationStore.d.ts.map +0 -1
  156. package/bin/auth/runtime/AuthorizationStore.js +0 -179
  157. package/bin/auth/runtime/AuthorizationStore.js.map +0 -1
  158. package/bin/auth/types/AuthPlugin.d.ts +0 -403
  159. package/bin/auth/types/AuthPlugin.d.ts.map +0 -1
  160. package/bin/auth/types/AuthPlugin.js +0 -111
  161. package/bin/auth/types/AuthPlugin.js.map +0 -1
  162. package/bin/chat/runtime/ChatAuthorizationRuntime.d.ts +0 -22
  163. package/bin/chat/runtime/ChatAuthorizationRuntime.d.ts.map +0 -1
  164. package/bin/chat/runtime/ChatAuthorizationRuntime.js +0 -230
  165. package/bin/chat/runtime/ChatAuthorizationRuntime.js.map +0 -1
  166. package/bin/chat/runtime/PluginDispatch.d.ts +0 -43
  167. package/bin/chat/runtime/PluginDispatch.d.ts.map +0 -1
  168. package/bin/chat/runtime/PluginDispatch.js +0 -57
  169. package/bin/chat/runtime/PluginDispatch.js.map +0 -1
  170. package/src/auth/runtime/AuthorizationConfig.ts +0 -247
  171. package/src/auth/runtime/AuthorizationPolicy.ts +0 -167
  172. package/src/auth/runtime/AuthorizationStore.ts +0 -220
  173. package/src/auth/types/AuthPlugin.ts +0 -540
  174. package/src/chat/runtime/ChatAuthorizationRuntime.ts +0 -270
  175. package/src/chat/runtime/PluginDispatch.ts +0 -89
@@ -4,7 +4,7 @@
4
4
  * 关键点(中文)
5
5
  * - 通过 context 显式注入 runtime 依赖。
6
6
  * - 统一注册 dispatcher,暴露 sendText/sendAction 能力。
7
- * - 基类只保留授权、工具发送与入站编排;存储/队列细节已下沉到辅助模块。
7
+ * - 基类只保留 Chat Access、工具发送与入站编排;存储/队列细节已下沉到辅助模块。
8
8
  */
9
9
 
10
10
  import { registerChatSender } from "@/chat/runtime/ChatSendRegistry.js";
@@ -19,9 +19,10 @@ import type { AgentContext } from "@downcity/agent";
19
19
  import { resolveChatQueueStore } from "@/chat/runtime/ChatQueue.js";
20
20
  import { deleteChatSessionById } from "@/chat/runtime/ChatSessionDelete.js";
21
21
  import {
22
- guardIncomingChat,
23
- observeIncomingChatPrincipal,
24
- } from "@/chat/runtime/PluginDispatch.js";
22
+ create_chat_access_service,
23
+ resolve_chat_access_issuer,
24
+ } from "@/chat/access/ChatAccessRuntime.js";
25
+ import type { ChatAccessDecision } from "@/chat/types/ChatAccess.js";
25
26
  import {
26
27
  appendToolOutboundChannelHistory,
27
28
  resolveChannelSessionId,
@@ -108,9 +109,9 @@ export type IncomingChatMessage = {
108
109
  };
109
110
 
110
111
  /**
111
- * 入站授权判定输入。
112
+ * 入站 Chat Access 判定输入。
112
113
  */
113
- export type IncomingAuthorizationParams = {
114
+ export type IncomingChatAccessParams = {
114
115
  chatId: string;
115
116
  chatType?: string;
116
117
  userId?: string;
@@ -119,12 +120,9 @@ export type IncomingAuthorizationParams = {
119
120
  };
120
121
 
121
122
  /**
122
- * 入站授权判定结果。
123
+ * 入站 Chat Access 判定结果。
123
124
  */
124
- export type IncomingAuthorizationResult = {
125
- decision: "allow" | "block";
126
- reason: string;
127
- };
125
+ export type IncomingChatAccessResult = ChatAccessDecision;
128
126
 
129
127
  /**
130
128
  * Chat channel 基类。
@@ -134,6 +132,7 @@ export abstract class BaseChatChannel {
134
132
  protected readonly context: AgentContext;
135
133
  protected readonly rootPath: string;
136
134
  protected readonly logger: Logger;
135
+ private readonly access_notice_sent_at = new Map<string, number>();
137
136
 
138
137
  protected constructor(params: {
139
138
  channel: ChatDispatchChannel;
@@ -159,22 +158,57 @@ export abstract class BaseChatChannel {
159
158
  params: ChannelSendTextParams,
160
159
  ): Promise<void>;
161
160
 
161
+ /**
162
+ * SDK 未绑定 City Chat Account 时的平台稳定 issuer 回退值。
163
+ */
164
+ protected getAccessIssuerFallback(): string {
165
+ return "";
166
+ }
167
+
168
+ /**
169
+ * 格式化 Chat Access 中需要原样展示的值。
170
+ *
171
+ * 说明(中文)
172
+ * - 默认返回纯文本,避免通用层绑定具体平台的富文本语法。
173
+ * - 支持富文本的平台可覆写此方法,确保标识符和命令可被准确展示、复制。
174
+ */
175
+ protected format_access_code(value: string): string {
176
+ return value;
177
+ }
178
+
179
+ /**
180
+ * 格式化 Chat Access 管理命令。
181
+ *
182
+ * 说明(中文)
183
+ * - 默认复用平台的原样值格式,避免通用层引入具体富文本语法。
184
+ * - 平台可单独覆写为块级代码,改善长命令的阅读和复制体验。
185
+ */
186
+ protected format_access_command(command: string): string {
187
+ return this.format_access_code(command);
188
+ }
189
+
162
190
  protected sendActionToPlatform?(
163
191
  params: ChannelSendActionParams,
164
192
  ): Promise<void>;
165
193
 
166
194
  /**
167
- * 发送授权提示文本。
195
+ * 发送 Chat Access 提示文本。
168
196
  *
169
197
  * 关键点(中文)
170
198
  * - 一律按普通消息发送,不挂 reply,避免把“权限提示”误挂到某条消息下面。
171
199
  */
172
- protected async sendAuthorizationText(params: {
200
+ protected async sendAccessText(params: {
173
201
  chatId: string;
174
202
  text: string;
175
203
  chatType?: string;
176
204
  messageThreadId?: number;
177
205
  }): Promise<void> {
206
+ const notice_key = `${params.chatId}:${params.text}`;
207
+ const current_time = Date.now();
208
+ const last_sent_at = this.access_notice_sent_at.get(notice_key) || 0;
209
+ if (current_time - last_sent_at < 60_000) return;
210
+ if (this.access_notice_sent_at.size >= 1_000) this.access_notice_sent_at.clear();
211
+ this.access_notice_sent_at.set(notice_key, current_time);
178
212
  await this.sendTextToPlatform({
179
213
  chatId: params.chatId,
180
214
  text: params.text,
@@ -186,83 +220,55 @@ export abstract class BaseChatChannel {
186
220
  }
187
221
 
188
222
  /**
189
- * 记录入站观测主体。
223
+ * 执行入站 Chat Access 判定。
190
224
  */
191
- protected async observeIncomingAuthorization(
192
- params: IncomingAuthorizationParams,
193
- ): Promise<void> {
194
- await observeIncomingChatPrincipal({
195
- context: this.context,
225
+ protected async evaluateIncomingAccess(
226
+ params: IncomingChatAccessParams,
227
+ ): Promise<IncomingChatAccessResult> {
228
+ const issuer =
229
+ resolve_chat_access_issuer(this.context, this.channel) ||
230
+ String(this.getAccessIssuerFallback() || "").trim();
231
+ return create_chat_access_service(this.context).evaluate({
196
232
  channel: this.channel,
197
- chatId: params.chatId,
198
- chatType: params.chatType,
199
- chatTitle: params.chatTitle,
200
- userId: params.userId,
201
- username: params.username,
233
+ issuer,
234
+ subject_id: String(params.userId || "").trim(),
235
+ display_name: String(params.username || "").trim() || undefined,
236
+ chat_id: String(params.chatId || "").trim(),
237
+ chat_type: String(params.chatType || "").trim() || undefined,
238
+ chat_title: String(params.chatTitle || "").trim() || undefined,
202
239
  });
203
240
  }
204
241
 
205
242
  /**
206
- * 执行入站授权判定。
243
+ * 生成 Chat Access 拒绝提示文案。
207
244
  */
208
- protected async evaluateIncomingAuthorization(
209
- params: IncomingAuthorizationParams,
210
- ): Promise<IncomingAuthorizationResult> {
211
- try {
212
- await guardIncomingChat({
213
- context: this.context,
214
- channel: this.channel,
215
- input: {
216
- channel: this.channel,
217
- chatId: params.chatId,
218
- chatType: params.chatType,
219
- userId: params.userId,
220
- username: params.username,
221
- chatTitle: params.chatTitle,
222
- },
223
- });
224
- return {
225
- decision: "allow",
226
- reason: "allow",
227
- };
228
- } catch (error) {
229
- return {
230
- decision: "block",
231
- reason: String(error || "blocked"),
232
- };
233
- }
234
- }
235
-
236
- /**
237
- * 生成未授权提示文案。
238
- */
239
- protected buildUnauthorizedBlockedText(params?: {
240
- userId?: string;
241
- chatId?: string;
242
- chatType?: string;
245
+ protected buildAccessBlockedText(params: {
246
+ result: IncomingChatAccessResult;
243
247
  }): string {
244
- const userId = String(params?.userId || "").trim();
245
- const chatId = String(params?.chatId || "").trim();
246
- const chatType = String(params?.chatType || "").trim();
247
- const lines = [
248
- "当前会话权限不足,已拒绝处理。请联系管理员调整你的角色。",
249
- ];
250
-
251
- if (userId) {
252
- lines.push(
253
- "",
254
- "请把下面命令发给管理员:",
255
- `downcity chat auth set ${this.channel}:${userId}`,
256
- );
248
+ const agent_id = String(this.context.config.id || "agent").trim() || "agent";
249
+ const displayed_agent_id = this.format_access_code(agent_id);
250
+ if (params.result.reason === "identity_missing") {
251
+ return "当前平台身份无法识别,请联系管理员检查 Chat 账号配置。";
257
252
  }
258
-
259
- if (chatId || chatType) {
260
- lines.push("", "来源信息:");
261
- if (chatId) lines.push(`chatId: ${chatId}`);
262
- if (chatType) lines.push(`chatType: ${chatType}`);
253
+ if (params.result.reason === "grant_denied") {
254
+ return `当前账号未获准访问 Agent "${displayed_agent_id}"。`;
263
255
  }
264
-
265
- return lines.join("\n");
256
+ const request_id = String(params.result.request_id || "").trim();
257
+ if (!request_id) {
258
+ return `当前账号尚未获准访问 Agent "${displayed_agent_id}"。`;
259
+ }
260
+ const displayed_request_id = this.format_access_code(request_id);
261
+ const approval_command = this.format_access_command(
262
+ `downcity chat access approve ${request_id} --agent ${agent_id}`,
263
+ );
264
+ return [
265
+ `当前账号尚未获准访问 Agent "${displayed_agent_id}"。`,
266
+ "",
267
+ `访问请求:${displayed_request_id}`,
268
+ "",
269
+ "请将下面命令发送给管理员:",
270
+ approval_command,
271
+ ].join("\n");
266
272
  }
267
273
 
268
274
  /**
@@ -3,13 +3,12 @@
3
3
  *
4
4
  * 关键点(中文)
5
5
  * - audit / exec 入队都需要走同一套 prepare + emit 流程。
6
- * - 这些逻辑抽离后,`BaseChatChannel` 只负责渠道层输入整理与授权判断。
6
+ * - 这些逻辑抽离后,`BaseChatChannel` 只负责渠道层输入整理与 Chat Access 判断。
7
7
  */
8
8
 
9
9
  import { resolveChatQueueStore } from "@/chat/runtime/ChatQueue.js";
10
10
  import { buildQueuedUserMessageWithInfo } from "@/chat/runtime/QueuedUserMessage.js";
11
11
  import { appendExecIngress } from "@/chat/runtime/ChatIngressStore.js";
12
- import { resolveIncomingChatUserRole } from "@/chat/runtime/PluginDispatch.js";
13
12
  import {
14
13
  emitChatEnqueueEffect,
15
14
  prepareChatEnqueue,
@@ -190,18 +189,9 @@ export async function enqueueExecChannelMessage(
190
189
  params: EnqueueExecChannelMessageParams,
191
190
  ): Promise<{ chatKey: string; position: number }> {
192
191
  const msg = params.message;
193
- const userRole = await resolveIncomingChatUserRole({
194
- context: params.context,
195
- channel: params.channel,
196
- userId: msg.userId,
197
- });
198
192
  const inboundExtra =
199
193
  msg.extra && typeof msg.extra === "object" ? stripUndefinedMeta(msg.extra) : {};
200
- const mergedExtra: JsonObject = {
201
- ...inboundExtra,
202
- roleId: userRole?.roleId || "unknown",
203
- permissions: userRole?.permissions || [],
204
- };
194
+ const mergedExtra: JsonObject = { ...inboundExtra };
205
195
 
206
196
  const chatKey = await resolveOrCreateChannelSessionId({
207
197
  context: params.context,
@@ -218,8 +208,6 @@ export async function enqueueExecChannelMessage(
218
208
  messageId: msg.messageId,
219
209
  userId: msg.userId,
220
210
  username: msg.username,
221
- roleId: userRole?.roleId,
222
- permissions: userRole?.permissions,
223
211
  receivedAt: msg.receivedAt,
224
212
  userTimezone: msg.userTimezone,
225
213
  text: msg.text,
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * 关键点(中文)
5
5
  * - 每个 channel 继承该基类,集中声明自己的配置字段。
6
- * - 统一提供“可写 ship 字段”提取逻辑,避免在 service 中重复硬编码。
6
+ * - 统一提供“可写 Agent 配置字段”提取逻辑,避免在 service 中重复硬编码。
7
7
  */
8
8
 
9
9
  import type {
@@ -29,7 +29,7 @@ export abstract class ChatChannelConfiguration {
29
29
  /**
30
30
  * 返回可通过 `chat.configure` 更新的运行态字段定义。
31
31
  */
32
- getWritableShipFields(): ChatChannelConfigurationField[] {
33
- return this.describe().fields.ship.filter((field) => field.writable);
32
+ get_writable_agent_config_fields(): ChatChannelConfigurationField[] {
33
+ return this.describe().fields.agent_config.filter((field) => field.writable);
34
34
  }
35
35
  }
@@ -28,14 +28,14 @@ export class FeishuChannelConfiguration extends ChatChannelConfiguration {
28
28
  canConfigure: true,
29
29
  },
30
30
  fields: {
31
- ship: [
31
+ agent_config: [
32
32
  {
33
33
  key: "enabled",
34
34
  label: "Enabled",
35
35
  description:
36
36
  "Whether feishu channel should be started by runtime.",
37
37
  type: "boolean",
38
- source: "ship_json",
38
+ source: "agent_config",
39
39
  required: false,
40
40
  nullable: false,
41
41
  writable: true,
@@ -47,9 +47,9 @@ export class FeishuChannelConfiguration extends ChatChannelConfiguration {
47
47
  key: "channelAccountId",
48
48
  label: "Chat Account ID",
49
49
  description:
50
- "Bind this chat platform to a chat account row in ~/.downcity/downcity.db channel_accounts.",
50
+ "Bind this chat platform to a channel_accounts row in the global downcity.db.",
51
51
  type: "string",
52
- source: "ship_json",
52
+ source: "agent_config",
53
53
  required: false,
54
54
  nullable: true,
55
55
  writable: true,
@@ -81,6 +81,11 @@ export class FeishuBot extends BaseChatChannel {
81
81
  return this.buildChatKey(params.chatId);
82
82
  }
83
83
 
84
+ /** SDK 直连时使用飞书 App ID 作为 issuer。 */
85
+ protected getAccessIssuerFallback(): string {
86
+ return String(this.appId || "").trim();
87
+ }
88
+
84
89
  protected async sendTextToPlatform(
85
90
  params: ChannelSendTextParams,
86
91
  ): Promise<void> {
@@ -209,16 +214,13 @@ export class FeishuBot extends BaseChatChannel {
209
214
  rememberChat: (threadId, value) => {
210
215
  this.knownChats.set(threadId, value);
211
216
  },
212
- observeIncomingAuthorization: async (params) => {
213
- await this.observeIncomingAuthorization(params);
214
- },
215
- evaluateIncomingAuthorization: async (params) =>
216
- await this.evaluateIncomingAuthorization(params),
217
- sendAuthorizationText: async (params) => {
218
- await this.sendAuthorizationText(params);
217
+ evaluateIncomingAccess: async (params) =>
218
+ await this.evaluateIncomingAccess(params),
219
+ sendAccessText: async (params) => {
220
+ await this.sendAccessText(params);
219
221
  },
220
- buildUnauthorizedBlockedText: (params) =>
221
- this.buildUnauthorizedBlockedText(params),
222
+ buildAccessBlockedText: (params) =>
223
+ this.buildAccessBlockedText(params),
222
224
  runInChat: async (chatKey, fn) => {
223
225
  await this.runInChat(chatKey, fn);
224
226
  },
@@ -2,7 +2,7 @@
2
2
  * Feishu 入站消息处理器。
3
3
  *
4
4
  * 关键点(中文)
5
- * - 负责单条飞书消息的去重、解析、授权、附件保存与执行入队。
5
+ * - 负责单条飞书消息的去重、解析、Chat Access、附件保存与执行入队。
6
6
  * - 不持有 `FeishuBot` 实例;所有副作用通过显式依赖注入。
7
7
  * - 解析失败/执行失败通过渠道门面提供的发送函数回写错误消息。
8
8
  */
@@ -11,8 +11,8 @@ import path from "path";
11
11
  import type { Logger } from "@downcity/agent";
12
12
  import type { AgentContext } from "@downcity/agent";
13
13
  import type {
14
- IncomingAuthorizationParams,
15
- IncomingAuthorizationResult,
14
+ IncomingChatAccessParams,
15
+ IncomingChatAccessResult,
16
16
  } from "@/chat/channels/BaseChatChannel.js";
17
17
  import type { JsonObject } from "@downcity/agent";
18
18
  import type { InboundReplyContext } from "@/chat/types/ReplyContext.js";
@@ -109,34 +109,22 @@ export interface FeishuMessageHandlerOptions {
109
109
  threadId: string,
110
110
  value: { chatId: string; chatType: string; chatTitle?: string },
111
111
  ): void;
112
+ /** 入站 Chat Access 判定。 */
113
+ evaluateIncomingAccess(
114
+ params: IncomingChatAccessParams,
115
+ ): Promise<IncomingChatAccessResult>;
112
116
  /**
113
- * 入站主体观测。
117
+ * 发送 Chat Access 失败提示。
114
118
  */
115
- observeIncomingAuthorization(
116
- params: IncomingAuthorizationParams,
117
- ): Promise<void>;
118
- /**
119
- * 入站授权判定。
120
- */
121
- evaluateIncomingAuthorization(
122
- params: IncomingAuthorizationParams,
123
- ): Promise<IncomingAuthorizationResult>;
124
- /**
125
- * 发送授权失败提示。
126
- */
127
- sendAuthorizationText(params: {
119
+ sendAccessText(params: {
128
120
  chatId: string;
129
121
  text: string;
130
122
  chatType?: string;
131
123
  }): Promise<void>;
132
124
  /**
133
- * 构建授权失败提示文案。
125
+ * 构建 Chat Access 失败提示文案。
134
126
  */
135
- buildUnauthorizedBlockedText(params?: {
136
- userId?: string;
137
- chatId?: string;
138
- chatType?: string;
139
- }): string;
127
+ buildAccessBlockedText(params: { result: IncomingChatAccessResult }): string;
140
128
  /**
141
129
  * 按 chatKey 串行执行。
142
130
  */
@@ -337,7 +325,7 @@ async function parseIncomingMessage(params: {
337
325
  }
338
326
 
339
327
  /**
340
- * 授权后处理消息内容。
328
+ * Chat Access 通过后处理消息内容。
341
329
  */
342
330
  async function handleAuthorizedMessage(params: {
343
331
  options: FeishuMessageHandlerOptions;
@@ -374,31 +362,19 @@ async function handleAuthorizedMessage(params: {
374
362
  const resolvedChatTitle = await options.resolveChatTitle(chatId);
375
363
  const chatTitle = resolvedChatTitle || (chatType === "p2p" ? actorName : undefined);
376
364
 
377
- await options.observeIncomingAuthorization({
378
- chatId,
379
- chatType,
380
- chatTitle,
381
- userId: actorId,
382
- username: actorName,
383
- });
384
-
385
- const authResult = await options.evaluateIncomingAuthorization({
365
+ const access_result = await options.evaluateIncomingAccess({
386
366
  chatId,
387
367
  chatType,
388
368
  chatTitle,
389
369
  userId: actorId,
390
370
  username: actorName,
391
371
  });
392
- if (authResult.decision !== "allow") {
372
+ if (!access_result.allowed) {
393
373
  if (chatType === "p2p") {
394
- await options.sendAuthorizationText({
374
+ await options.sendAccessText({
395
375
  chatId,
396
376
  chatType,
397
- text: options.buildUnauthorizedBlockedText({
398
- chatId,
399
- chatType,
400
- userId: actorId,
401
- }),
377
+ text: options.buildAccessBlockedText({ result: access_result }),
402
378
  });
403
379
  }
404
380
  return true;
@@ -28,13 +28,13 @@ export class QqChannelConfiguration extends ChatChannelConfiguration {
28
28
  canConfigure: true,
29
29
  },
30
30
  fields: {
31
- ship: [
31
+ agent_config: [
32
32
  {
33
33
  key: "enabled",
34
34
  label: "Enabled",
35
35
  description: "Whether qq channel should be started by runtime.",
36
36
  type: "boolean",
37
- source: "ship_json",
37
+ source: "agent_config",
38
38
  required: false,
39
39
  nullable: false,
40
40
  writable: true,
@@ -46,9 +46,9 @@ export class QqChannelConfiguration extends ChatChannelConfiguration {
46
46
  key: "channelAccountId",
47
47
  label: "Chat Account ID",
48
48
  description:
49
- "Bind this chat platform to a chat account row in ~/.downcity/downcity.db channel_accounts.",
49
+ "Bind this chat platform to a channel_accounts row in the global downcity.db.",
50
50
  type: "string",
51
- source: "ship_json",
51
+ source: "agent_config",
52
52
  required: false,
53
53
  nullable: true,
54
54
  writable: true,
@@ -82,6 +82,11 @@ export class QQBot extends BaseChatChannel {
82
82
  return `qq-${chatType}-${params.chatId}`;
83
83
  }
84
84
 
85
+ /** SDK 直连时使用 QQ App ID 作为 issuer。 */
86
+ protected getAccessIssuerFallback(): string {
87
+ return String(this.appId || "").trim();
88
+ }
89
+
85
90
  protected async sendTextToPlatform(
86
91
  params: ChannelSendTextParams,
87
92
  ): Promise<void> {
@@ -266,16 +271,13 @@ export class QQBot extends BaseChatChannel {
266
271
  logger: this.logger,
267
272
  getBotUserId: () => this.botUserId,
268
273
  getChatKey: (params) => this.getChatKey(params),
269
- observeIncomingAuthorization: async (params) => {
270
- await this.observeIncomingAuthorization(params);
271
- },
272
- evaluateIncomingAuthorization: async (params) =>
273
- await this.evaluateIncomingAuthorization(params),
274
- sendAuthorizationText: async (params) => {
275
- await this.sendAuthorizationText(params);
274
+ evaluateIncomingAccess: async (params) =>
275
+ await this.evaluateIncomingAccess(params),
276
+ sendAccessText: async (params) => {
277
+ await this.sendAccessText(params);
276
278
  },
277
- buildUnauthorizedBlockedText: (params) =>
278
- this.buildUnauthorizedBlockedText(params),
279
+ buildAccessBlockedText: (params) =>
280
+ this.buildAccessBlockedText(params),
279
281
  shouldSkipDuplicatedInboundMessage: async (eventType, messageId) =>
280
282
  await this.shouldSkipDuplicatedInboundMessage(eventType, messageId),
281
283
  enqueueAuditMessage: async (params) => {
@@ -2,7 +2,7 @@
2
2
  * QQ 入站消息处理器。
3
3
  *
4
4
  * 关键点(中文)
5
- * - 负责 group / c2c / channel 三类入站消息的授权、去重、审计与执行入队。
5
+ * - 负责 group / c2c / channel 三类入站消息的 Chat Access、去重、审计与执行入队。
6
6
  * - 不直接持有 `QQBot` 实例;所有副作用通过显式函数注入。
7
7
  * - `QQBot` 因此只保留渠道生命周期、dispatch 分流、发送与命令处理。
8
8
  */
@@ -11,8 +11,8 @@ import type { Logger } from "@downcity/agent";
11
11
  import type { AgentContext } from "@downcity/agent";
12
12
  import type {
13
13
  ChannelChatKeyParams,
14
- IncomingAuthorizationParams,
15
- IncomingAuthorizationResult,
14
+ IncomingChatAccessParams,
15
+ IncomingChatAccessResult,
16
16
  } from "@/chat/channels/BaseChatChannel.js";
17
17
  import type { ChannelUserMessageMeta } from "@/chat/channels/BaseChatChannelSupport.js";
18
18
  import {
@@ -69,34 +69,22 @@ export interface QQMessageHandlerOptions {
69
69
  * 计算 channel chatKey。
70
70
  */
71
71
  getChatKey(params: ChannelChatKeyParams): string;
72
+ /** 入站 Chat Access 判定。 */
73
+ evaluateIncomingAccess(
74
+ params: IncomingChatAccessParams,
75
+ ): Promise<IncomingChatAccessResult>;
72
76
  /**
73
- * 入站主体观测。
77
+ * 发送 Chat Access 失败提示。
74
78
  */
75
- observeIncomingAuthorization(
76
- params: IncomingAuthorizationParams,
77
- ): Promise<void>;
78
- /**
79
- * 入站授权判定。
80
- */
81
- evaluateIncomingAuthorization(
82
- params: IncomingAuthorizationParams,
83
- ): Promise<IncomingAuthorizationResult>;
84
- /**
85
- * 发送授权失败提示。
86
- */
87
- sendAuthorizationText(params: {
79
+ sendAccessText(params: {
88
80
  chatId: string;
89
81
  text: string;
90
82
  chatType?: string;
91
83
  }): Promise<void>;
92
84
  /**
93
- * 构建授权失败提示文案。
85
+ * 构建 Chat Access 失败提示文案。
94
86
  */
95
- buildUnauthorizedBlockedText(params?: {
96
- userId?: string;
97
- chatId?: string;
98
- chatType?: string;
99
- }): string;
87
+ buildAccessBlockedText(params: { result: IncomingChatAccessResult }): string;
100
88
  /**
101
89
  * 入站去重判断。
102
90
  */
@@ -234,22 +222,14 @@ export async function handleQqChannelMessage(
234
222
  actorName: actor.username,
235
223
  });
236
224
 
237
- await options.observeIncomingAuthorization({
238
- chatId: channelId,
239
- chatType,
240
- chatTitle,
241
- userId: actor.userId,
242
- username: actor.username,
243
- });
244
-
245
- const authResult = await options.evaluateIncomingAuthorization({
225
+ const access_result = await options.evaluateIncomingAccess({
246
226
  chatId: channelId,
247
227
  chatType,
248
228
  chatTitle,
249
229
  userId: actor.userId,
250
230
  username: actor.username,
251
231
  });
252
- if (authResult.decision !== "allow") return;
232
+ if (!access_result.allowed) return;
253
233
 
254
234
  const botUserId = options.getBotUserId();
255
235
  if (actor.userId && botUserId && actor.userId === botUserId) {
@@ -347,31 +327,19 @@ async function handleQqInboundMessage(
347
327
  ? stripQqBotMention(rawContent, botUserId)
348
328
  : extractQqTextContent(rawContent);
349
329
 
350
- await options.observeIncomingAuthorization({
351
- chatId,
352
- chatType,
353
- chatTitle,
354
- userId: actor.userId,
355
- username: actor.username,
356
- });
357
-
358
- const authResult = await options.evaluateIncomingAuthorization({
330
+ const access_result = await options.evaluateIncomingAccess({
359
331
  chatId,
360
332
  chatType,
361
333
  chatTitle,
362
334
  userId: actor.userId,
363
335
  username: actor.username,
364
336
  });
365
- if (authResult.decision !== "allow") {
337
+ if (!access_result.allowed) {
366
338
  if (!isGroup) {
367
- await options.sendAuthorizationText({
339
+ await options.sendAccessText({
368
340
  chatId,
369
341
  chatType,
370
- text: options.buildUnauthorizedBlockedText({
371
- chatId,
372
- chatType,
373
- userId: actor.userId,
374
- }),
342
+ text: options.buildAccessBlockedText({ result: access_result }),
375
343
  });
376
344
  }
377
345
  return;