@zhin.js/adapter-icqq 8.0.2 → 8.0.5

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,92 @@
1
1
  # @zhin.js/adapter-icqq
2
2
 
3
+ ## 8.0.5
4
+
5
+ ### Patch Changes
6
+
7
+ - 36c7400: Replace `AgentPromptBuildContext.commMessage` with an authenticated platform projection and make Agent prompt assembly consume canonical Turn identity. Platform contributors and prompt hooks no longer receive a classic IM `Message`.
8
+
9
+ Remove the unused Message field and active-context escape hatch from PromptController; turn scheduling is keyed only by canonical session identity.
10
+
11
+ - Updated dependencies [6f9c366]
12
+ - Updated dependencies [373a56b]
13
+ - Updated dependencies [0de46a8]
14
+ - Updated dependencies [c106ecc]
15
+ - Updated dependencies [ca92e03]
16
+ - Updated dependencies [b0f37ae]
17
+ - Updated dependencies [ba08a2f]
18
+ - Updated dependencies [b08f7fe]
19
+ - Updated dependencies [daffd4c]
20
+ - Updated dependencies [36c7400]
21
+ - Updated dependencies [a9fa72e]
22
+ - Updated dependencies [c8de3ef]
23
+ - Updated dependencies [60f0fc8]
24
+ - Updated dependencies [574c990]
25
+ - Updated dependencies [d096f16]
26
+ - Updated dependencies [3f29623]
27
+ - Updated dependencies [2916852]
28
+ - Updated dependencies [d047869]
29
+ - Updated dependencies [162fa34]
30
+ - Updated dependencies [e62561e]
31
+ - Updated dependencies [3eeeb46]
32
+ - Updated dependencies [85d0f82]
33
+ - Updated dependencies [61bfc1c]
34
+ - Updated dependencies [04bad47]
35
+ - Updated dependencies [e40b048]
36
+ - Updated dependencies [5a5b1bb]
37
+ - Updated dependencies [f1708c3]
38
+ - Updated dependencies [d254a81]
39
+ - Updated dependencies [7123c47]
40
+ - Updated dependencies [05befc1]
41
+ - Updated dependencies [0663b6a]
42
+ - Updated dependencies [f28f9b3]
43
+ - Updated dependencies [9f340f7]
44
+ - Updated dependencies [e53444f]
45
+ - Updated dependencies [5eedd26]
46
+ - Updated dependencies [92b0dd7]
47
+ - Updated dependencies [f919b6f]
48
+ - Updated dependencies [098e411]
49
+ - Updated dependencies [e1b7c01]
50
+ - Updated dependencies [9b94f87]
51
+ - Updated dependencies [a7df753]
52
+ - @zhin.js/agent@1.1.5
53
+ - @zhin.js/im-contract@1.0.3
54
+ - @zhin.js/adapter@1.1.7
55
+ - @zhin.js/plugin-runtime@1.1.5
56
+ - @zhin.js/command@1.0.9
57
+ - @zhin.js/core@1.5.4
58
+ - zhin.js@6.0.4
59
+
60
+ ## 8.0.4
61
+
62
+ ### Patch Changes
63
+
64
+ - f8c7a54: fix: im
65
+ - Updated dependencies [f8c7a54]
66
+ - @zhin.js/logger@1.0.76
67
+ - @zhin.js/adapter@1.1.6
68
+ - @zhin.js/agent@1.1.4
69
+ - @zhin.js/command@1.0.8
70
+ - @zhin.js/core@1.5.3
71
+ - @zhin.js/im-contract@1.0.2
72
+ - @zhin.js/plugin-runtime@1.1.4
73
+ - zhin.js@6.0.3
74
+
75
+ ## 8.0.3
76
+
77
+ ### Patch Changes
78
+
79
+ - Updated dependencies [afc0e66]
80
+ - Updated dependencies [2e41ad5]
81
+ - Updated dependencies [9f57124]
82
+ - @zhin.js/core@1.5.2
83
+ - @zhin.js/adapter@1.1.5
84
+ - @zhin.js/im-contract@1.0.1
85
+ - @zhin.js/plugin-runtime@1.1.3
86
+ - @zhin.js/command@1.0.7
87
+ - @zhin.js/agent@1.1.3
88
+ - zhin.js@6.0.2
89
+
3
90
  ## 8.0.2
4
91
 
5
92
  ### Patch Changes
package/README.md CHANGED
@@ -5,8 +5,8 @@ ICQQ Plugin Runtime 适配器 — 通过 [@icqqjs/cli](https://github.com/icqqjs
5
5
  ## 功能特性
6
6
 
7
7
  - 群聊 / 私聊 / 群临时会话 / QQ 频道消息
8
- - 入站:`messageGatewayToken`(IPC 事件订阅)
9
- - 出站:`send({ target, payload })` → `send_group_msg` / `send_private_msg` / …
8
+ - 入站:`messageGatewayToken`(IPC 事件订阅),归一为 `gateway.receive({ conversation, message, content, ... })`
9
+ - 出站:`send({ conversation, payload })` → `send_group_msg` / `send_private_msg` / …(conversation kind/id/parent 路由)
10
10
  - Agent 工具:`agent/tools/`(戳一戳、群管、好友列表等)保留
11
11
  - Console Endpoint 管理:`src/endpoint.ts` 显式实现 `EndpointManagement`(好友/群/群成员列表、请求审批、删好友、踢人、禁言、设管理),Console 使用 `endpoint.friends` / `endpoint.groups` / `endpoint.group_members` 等规范 RPC
12
12
 
@@ -48,14 +48,14 @@ plugins:
48
48
 
49
49
  先执行 `icqq login`,再启动 Zhin。
50
50
 
51
- ## Send target
51
+ ## Send conversation
52
52
 
53
- | 类型 | target |
54
- |------|--------|
55
- | 私聊 | `private:uin` |
56
- | 群聊 | `group:gid` |
57
- | 群临时会话 | `temp:gid:uin` |
58
- | 频道 | `channel:guildId:channelId` |
53
+ | 类型 | conversation |
54
+ |------|--------------|
55
+ | 私聊 | `{ kind: 'private', id: uin }` |
56
+ | 群聊 | `{ kind: 'group', id: gid }` |
57
+ | 群临时会话 | `{ kind: 'private', id: uin, parent: { kind: 'group', id: gid } }` |
58
+ | 频道 | `{ kind: 'channel', id: channelId, parent: { kind: 'channel', id: guildId } }` |
59
59
 
60
60
  ## 架构
61
61
 
package/adapters/icqq.js CHANGED
@@ -26,8 +26,8 @@ export default defineAdapter({
26
26
  // Console loginAssist + host-router routes deferred.
27
27
  const config = resolveIcqqConfig(context.config);
28
28
  // 注册到插件运行时状态(icqq.endpoint list 的"运行中"数据源)
29
- context.use(icqqRuntimeStateToken).endpoints.set(config.name, {
30
- name: config.name,
29
+ context.use(icqqRuntimeStateToken).endpoints.set(config.id, {
30
+ id: config.id,
31
31
  mode: config.rpc ? 'rpc' : 'ipc',
32
32
  });
33
33
  const inbox = resolveInboxHooks(context);
package/adapters/icqq.ts CHANGED
@@ -42,8 +42,8 @@ export default defineAdapter<IcqqAdapterConfig>({
42
42
  // Console loginAssist + host-router routes deferred.
43
43
  const config = resolveIcqqConfig(context.config);
44
44
  // 注册到插件运行时状态(icqq.endpoint list 的"运行中"数据源)
45
- context.use(icqqRuntimeStateToken).endpoints.set(config.name, {
46
- name: config.name,
45
+ context.use(icqqRuntimeStateToken).endpoints.set(config.id, {
46
+ id: config.id,
47
47
  mode: config.rpc ? 'rpc' : 'ipc',
48
48
  });
49
49
  const inbox = resolveInboxHooks(context);
package/lib/endpoint.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * IcqqIpcEndpoint — lifecycle, outbound, IPC subscribe/admit for ICQQ daemon.
3
3
  */
4
- import type { EndpointControl, EndpointInstance, EndpointManagement } from '@zhin.js/adapter';
4
+ import type { EndpointControl, EndpointInstance, EndpointManagement, EndpointSendRequest } from '@zhin.js/adapter';
5
5
  import type { MessageGateway } from '@zhin.js/core/runtime';
6
6
  import type { CapabilityId } from '@zhin.js/plugin-runtime';
7
7
  import { type IcqqInboundMessage, type IpcEvent, type IpcResponse, type ResolvedIcqqConfig } from './protocol.js';
@@ -48,10 +48,7 @@ export declare class IcqqIpcEndpoint implements EndpointInstance {
48
48
  open(): void;
49
49
  close(): void;
50
50
  stop(): Promise<void>;
51
- send({ target, payload }: {
52
- readonly target: string;
53
- readonly payload: unknown;
54
- }): Promise<string>;
51
+ send({ conversation, payload }: EndpointSendRequest): Promise<string>;
55
52
  private static readonly EMOJI_MAP;
56
53
  private getEmojiId;
57
54
  /** Activity-feedback / OutboundHost reaction surface. */
package/lib/endpoint.js CHANGED
@@ -1,13 +1,13 @@
1
- import { formatCompact, getLogger } from '@zhin.js/logger';
1
+ import { formatCompact, getAdapterLogger, truncatePreview } from '@zhin.js/logger';
2
2
  import { registerIcqqAgentEndpoint } from './icqq-agent-deps.js';
3
3
  import { InboundMessageDeduper, findIcqqNestedMessageSource, isIcqqBotMentioned, isIcqqMessagePostType, normalizeIcqqInboundMessage, quotedPayloadFromIcqqSource, resolveIcqqQuoteIdFromEvent, shouldSkipSelfInboundMessage, unwrapIcqqIpcEventPayload, } from './icqq-inbound.js';
4
4
  import { buildIcqqInboxNoticeRow, buildIcqqInboxRequestRow, buildIcqqSystemRequestRow, isIcqqNoticePayload, isIcqqRequestPayload, } from './icqq-inbox.js';
5
5
  import { IcqqGuildCatalog, isIcqqGuildIpcEvent, normalizeIcqqGuildInboundMessage, } from './icqq-guild.js';
6
6
  import { IpcClient } from './ipc-client.js';
7
7
  import { materializeOutboundBase64, resolveIcqqOutboundMediaMode, } from './outbound-media.js';
8
- import { Actions, formatInboundContent, formatInboundTarget, formatOutboundBody, parseSendTarget, } from './protocol.js';
9
- const logger = getLogger('icqq');
8
+ import { Actions, formatInboundContent, formatOutboundBody, icqqInboundConversation, icqqOutboundTarget, } from './protocol.js';
10
9
  export class IcqqIpcEndpoint {
10
+ #logger;
11
11
  #options;
12
12
  #createIpc;
13
13
  name;
@@ -48,8 +48,9 @@ export class IcqqIpcEndpoint {
48
48
  /** 是否已有重连循环在跑(避免多次 schedule 叠套) */
49
49
  #reconnectRunning = false;
50
50
  constructor(options) {
51
+ this.#logger = getAdapterLogger('icqq', options.config.id);
51
52
  this.#options = options;
52
- this.name = options.config.name;
53
+ this.name = options.config.id;
53
54
  this.#createIpc = options.createIpc ?? defaultCreateIpc;
54
55
  }
55
56
  async request(action, params) {
@@ -66,19 +67,13 @@ export class IcqqIpcEndpoint {
66
67
  try {
67
68
  await this.#bindIpcSession();
68
69
  this.#unregisterAgent = registerIcqqAgentEndpoint(this.name, this);
69
- logger.info(formatCompact({
70
- op: 'connect',
71
- endpoint: this.name,
72
- mode: this.#options.config.rpc ? 'rpc' : 'ipc',
73
- friends: this.friends.size,
74
- groups: this.groups.size,
75
- }));
70
+ this.#logger.info(`connected (${this.#options.config.rpc ? 'rpc' : 'ipc'}) | friends: ${this.friends.size} | groups: ${this.groups.size}`);
76
71
  }
77
72
  catch (error) {
78
73
  await this.stop();
79
74
  // Startup connect failures are surfaced once (with stack) by AdapterIndex;
80
75
  // keep this at debug to avoid a duplicate error-level log.
81
- logger.debug(`Failed to connect ICQQ IPC: ${error instanceof Error ? error.stack ?? error.message : String(error)}`);
76
+ this.#logger.debug(`Failed to connect ICQQ IPC: ${error instanceof Error ? error.stack ?? error.message : String(error)}`);
82
77
  throw error;
83
78
  }
84
79
  }
@@ -117,10 +112,9 @@ export class IcqqIpcEndpoint {
117
112
  const jitter = Math.floor(Math.random() * 400);
118
113
  const delayMs = base + jitter;
119
114
  // 首次断开 WARN,后续重试静默为 DEBUG,避免刷屏
120
- const disconnectLog = attempt === 0 ? logger.warn.bind(logger) : logger.debug.bind(logger);
115
+ const disconnectLog = attempt === 0 ? this.#logger.warn.bind(this.#logger) : this.#logger.debug.bind(this.#logger);
121
116
  disconnectLog(formatCompact({
122
117
  op: 'disconnect',
123
- endpoint: this.name,
124
118
  ok: false,
125
119
  delay_ms: delayMs,
126
120
  attempt: attempt + 1,
@@ -130,18 +124,12 @@ export class IcqqIpcEndpoint {
130
124
  break;
131
125
  try {
132
126
  await this.#bindIpcSession();
133
- logger.info(formatCompact({
134
- op: 'reconnect',
135
- endpoint: this.name,
136
- ok: true,
137
- attempts: attempt + 1,
138
- friends: this.friends.size,
139
- groups: this.groups.size,
140
- }));
127
+ this.#logger.info(`reconnected after ${attempt + 1} attempt(s)`
128
+ + ` | friends: ${this.friends.size} | groups: ${this.groups.size}`);
141
129
  break;
142
130
  }
143
131
  catch (error) {
144
- const retryLog = attempt === 0 ? logger.warn.bind(logger) : logger.debug.bind(logger);
132
+ const retryLog = attempt === 0 ? this.#logger.warn.bind(this.#logger) : this.#logger.debug.bind(this.#logger);
145
133
  retryLog(formatCompact({
146
134
  op: 'reconnect',
147
135
  endpoint: this.name,
@@ -185,9 +173,9 @@ export class IcqqIpcEndpoint {
185
173
  // 置空 ipc:start 前 / stop 后的 request() 走防御性报错而非 TypeError
186
174
  this.ipc = undefined;
187
175
  this.#started = false;
188
- logger.debug(formatCompact({ op: 'disconnect', endpoint: this.name }));
176
+ this.#logger.debug(formatCompact({ op: 'disconnect' }));
189
177
  }
190
- async send({ target, payload }) {
178
+ async send({ conversation, payload }) {
191
179
  if (!this.ipc) {
192
180
  throw new Error(`icqq endpoint ${this.name} 未连接(IPC 未初始化或已停止),无法发送消息`);
193
181
  }
@@ -196,7 +184,7 @@ export class IcqqIpcEndpoint {
196
184
  ? materializeOutboundBase64(payload, mediaMode)
197
185
  : payload;
198
186
  const message = formatOutboundBody(content);
199
- const parsed = parseSendTarget(target);
187
+ const parsed = icqqOutboundTarget(conversation);
200
188
  let action;
201
189
  let params;
202
190
  switch (parsed.kind) {
@@ -227,15 +215,11 @@ export class IcqqIpcEndpoint {
227
215
  }
228
216
  const resp = await this.ipc.request(action, params);
229
217
  if (!resp.ok) {
218
+ this.#logger.warn(`send failed ${conversation.kind}:${conversation.id} | ${resp.error} | ${truncatePreview(typeof message === 'string' ? message : String(message), 80)}`);
230
219
  throw new Error(`发送消息失败: ${resp.error}`);
231
220
  }
232
221
  const messageId = String(resp.data?.message_id ?? `sent_${Date.now()}`);
233
- logger.debug(formatCompact({
234
- op: 'icqq_send',
235
- endpoint: this.name,
236
- target,
237
- messageId,
238
- }));
222
+ this.#logger.info(`send ${conversation.kind}:${conversation.id} | id: ${messageId} | ${truncatePreview(typeof message === 'string' ? message : String(message), 80)}`);
239
223
  return messageId;
240
224
  }
241
225
  // ── 消息回应(activity-feedback / typing reaction)────────────────────
@@ -265,7 +249,7 @@ export class IcqqIpcEndpoint {
265
249
  id: emojiId,
266
250
  });
267
251
  if (!resp.ok) {
268
- logger.warn(formatCompact({
252
+ this.#logger.warn(formatCompact({
269
253
  op: 'add_reaction',
270
254
  endpoint: this.name,
271
255
  message_id: messageId,
@@ -276,7 +260,7 @@ export class IcqqIpcEndpoint {
276
260
  }));
277
261
  return null;
278
262
  }
279
- logger.debug(formatCompact({
263
+ this.#logger.debug(formatCompact({
280
264
  op: 'add_reaction',
281
265
  endpoint: this.name,
282
266
  message_id: messageId,
@@ -287,7 +271,7 @@ export class IcqqIpcEndpoint {
287
271
  return emojiId;
288
272
  }
289
273
  catch (error) {
290
- logger.warn(formatCompact({
274
+ this.#logger.warn(formatCompact({
291
275
  op: 'add_reaction',
292
276
  endpoint: this.name,
293
277
  message_id: messageId,
@@ -307,7 +291,7 @@ export class IcqqIpcEndpoint {
307
291
  id: emojiId,
308
292
  });
309
293
  if (!resp.ok) {
310
- logger.warn(formatCompact({
294
+ this.#logger.warn(formatCompact({
311
295
  op: 'remove_reaction',
312
296
  endpoint: this.name,
313
297
  message_id: messageId,
@@ -318,7 +302,7 @@ export class IcqqIpcEndpoint {
318
302
  }));
319
303
  return;
320
304
  }
321
- logger.debug(formatCompact({
305
+ this.#logger.debug(formatCompact({
322
306
  op: 'remove_reaction',
323
307
  endpoint: this.name,
324
308
  message_id: messageId,
@@ -328,7 +312,7 @@ export class IcqqIpcEndpoint {
328
312
  }));
329
313
  }
330
314
  catch (error) {
331
- logger.warn(formatCompact({
315
+ this.#logger.warn(formatCompact({
332
316
  op: 'remove_reaction',
333
317
  endpoint: this.name,
334
318
  message_id: messageId,
@@ -345,7 +329,7 @@ export class IcqqIpcEndpoint {
345
329
  try {
346
330
  const resp = await this.request(Actions.RECALL_MSG, { message_id: messageId });
347
331
  if (!resp.ok) {
348
- logger.warn(formatCompact({
332
+ this.#logger.warn(formatCompact({
349
333
  op: 'recall_message',
350
334
  endpoint: this.name,
351
335
  message_id: messageId,
@@ -354,7 +338,7 @@ export class IcqqIpcEndpoint {
354
338
  }));
355
339
  return;
356
340
  }
357
- logger.debug(formatCompact({
341
+ this.#logger.debug(formatCompact({
358
342
  op: 'recall_message',
359
343
  endpoint: this.name,
360
344
  message_id: messageId,
@@ -362,7 +346,7 @@ export class IcqqIpcEndpoint {
362
346
  }));
363
347
  }
364
348
  catch (error) {
365
- logger.warn(formatCompact({
349
+ this.#logger.warn(formatCompact({
366
350
  op: 'recall_message',
367
351
  endpoint: this.name,
368
352
  message_id: messageId,
@@ -499,13 +483,14 @@ export class IcqqIpcEndpoint {
499
483
  }
500
484
  /** Test / internal: admit when open. */
501
485
  admit(msg) {
486
+ const conversation = msg.conversation;
502
487
  if (!this.#open) {
503
- logger.debug(formatCompact({
488
+ this.#logger.debug(formatCompact({
504
489
  op: 'icqq_inbound_skip',
505
490
  reason: 'endpoint_closed',
506
491
  endpoint: this.name,
507
492
  id: msg.id,
508
- target: msg.target,
493
+ target: `${conversation.kind}:${conversation.id}`,
509
494
  }));
510
495
  return;
511
496
  }
@@ -515,34 +500,31 @@ export class IcqqIpcEndpoint {
515
500
  content: msg.segments,
516
501
  rawMessage: msg.content,
517
502
  });
518
- logger.debug(formatCompact({
519
- op: 'icqq_inbound_admit',
520
- endpoint: this.name,
521
- id: msg.id,
522
- target: msg.target,
523
- sender: msg.sender,
524
- channelType: msg.channelType,
525
- mentioned: mentioned || undefined,
526
- preview: msg.content.slice(0, 80),
527
- segments: summarizeSegmentTypes(msg.segments),
528
- }));
503
+ this.#logger.info(`recv ${conversation.kind}:${conversation.id}`
504
+ + (msg.sender ? ` from ${msg.sender.name ?? msg.sender.id}` : '')
505
+ + (mentioned ? ' (mentioned)' : '')
506
+ + ` | ${truncatePreview(msg.content, 80)}`);
507
+ const quoteId = msg.metadata?.quote_id;
508
+ const { quote_id: _dropQuoteId, ...restMetadata } = msg.metadata ?? {};
529
509
  void this.#options.gateway.receive({
530
- adapter: this.#options.id,
531
- target: msg.target,
510
+ conversation,
511
+ message: { conversation, id: msg.id },
532
512
  content: msg.content,
533
513
  ...(msg.segments ? { segments: msg.segments } : {}),
534
514
  sender: msg.sender,
535
- id: msg.id,
515
+ endpointId: this.name,
516
+ ...(mentioned ? { mentioned: true } : {}),
517
+ ...(quoteId ? { replyTo: { id: String(quoteId) } } : {}),
536
518
  metadata: Object.freeze({
537
- endpoint: this.name,
538
519
  channelType: msg.channelType,
539
- ...msg.metadata,
540
- ...(mentioned ? { mentioned: true } : {}),
520
+ ...restMetadata,
541
521
  }),
542
522
  }).catch((err) => {
543
- logger.warn(formatCompact({
544
- op: 'icqq_gateway_receive_failed',
545
- target: msg.target,
523
+ this.#logger.warn(formatCompact({
524
+ op: 'recv',
525
+ endpoint: this.name,
526
+ target: `${conversation.kind}:${conversation.id}`,
527
+ ok: false,
546
528
  error: err instanceof Error ? err.message : String(err),
547
529
  }));
548
530
  });
@@ -585,7 +567,7 @@ export class IcqqIpcEndpoint {
585
567
  return;
586
568
  const data = payload;
587
569
  if (shouldSkipSelfInboundMessage(data)) {
588
- logger.debug(formatCompact({
570
+ this.#logger.debug(formatCompact({
589
571
  op: 'icqq_inbound_skip',
590
572
  reason: 'self',
591
573
  endpoint: this.name,
@@ -596,7 +578,7 @@ export class IcqqIpcEndpoint {
596
578
  }
597
579
  const normalized = normalizeIcqqInboundMessage(data);
598
580
  if (!normalized) {
599
- logger.debug(formatCompact({
581
+ this.#logger.debug(formatCompact({
600
582
  op: 'icqq_inbound_skip',
601
583
  reason: 'normalize_failed',
602
584
  endpoint: this.name,
@@ -606,7 +588,7 @@ export class IcqqIpcEndpoint {
606
588
  return;
607
589
  }
608
590
  if (!this.#inboundDeduper.shouldProcess(normalized.messageId)) {
609
- logger.debug(formatCompact({
591
+ this.#logger.debug(formatCompact({
610
592
  op: 'icqq_inbound_skip',
611
593
  reason: 'dedupe',
612
594
  endpoint: this.name,
@@ -616,14 +598,18 @@ export class IcqqIpcEndpoint {
616
598
  }
617
599
  this.admit({
618
600
  id: normalized.messageId,
619
- target: formatInboundTarget({
601
+ conversation: icqqInboundConversation(String(this.#options.id), {
620
602
  channelType: normalized.channelType,
621
603
  channelId: normalized.channelId,
622
604
  channelParentGroupId: normalized.channelParentGroupId,
623
605
  }),
624
606
  content: formatInboundContent(normalized.rawMessage),
625
607
  segments: normalized.content,
626
- sender: normalized.userId,
608
+ sender: {
609
+ id: normalized.userId,
610
+ name: normalized.nickname || undefined,
611
+ ...(normalized.senderRole ? { roles: [normalized.senderRole] } : {}),
612
+ },
627
613
  channelType: normalized.channelType,
628
614
  metadata: buildIcqqQuoteMetadata(data, {
629
615
  nickname: normalized.nickname,
@@ -637,7 +623,7 @@ export class IcqqIpcEndpoint {
637
623
  return;
638
624
  const normalized = normalizeIcqqGuildInboundMessage(payload);
639
625
  if (!normalized) {
640
- logger.debug(formatCompact({
626
+ this.#logger.debug(formatCompact({
641
627
  op: 'icqq_inbound_skip',
642
628
  reason: 'guild_normalize_failed',
643
629
  endpoint: this.name,
@@ -646,7 +632,7 @@ export class IcqqIpcEndpoint {
646
632
  }
647
633
  this.#guildCatalog.upsertFromInbound(normalized.raw);
648
634
  if (!this.#inboundDeduper.shouldProcess(`guild:${normalized.messageId}`)) {
649
- logger.debug(formatCompact({
635
+ this.#logger.debug(formatCompact({
650
636
  op: 'icqq_inbound_skip',
651
637
  reason: 'dedupe',
652
638
  endpoint: this.name,
@@ -656,14 +642,14 @@ export class IcqqIpcEndpoint {
656
642
  }
657
643
  this.admit({
658
644
  id: normalized.messageId,
659
- target: formatInboundTarget({
645
+ conversation: icqqInboundConversation(String(this.#options.id), {
660
646
  channelType: 'channel',
661
647
  channelId: normalized.channelId,
662
648
  guildId: normalized.guildId,
663
649
  }),
664
650
  content: formatInboundContent(normalized.rawMessage),
665
651
  segments: normalized.content,
666
- sender: normalized.userId,
652
+ sender: { id: normalized.userId, name: normalized.nickname || undefined },
667
653
  channelType: 'channel',
668
654
  metadata: {
669
655
  guildId: normalized.guildId,
@@ -674,7 +660,7 @@ export class IcqqIpcEndpoint {
674
660
  /** 收件箱行公共前缀:adapter 槽 localName + endpoint live 名(uin)。 */
675
661
  #inboxBase() {
676
662
  const id = String(this.#options.id);
677
- return { adapter: id.split('\0').pop() ?? id, endpointId: this.name };
663
+ return { adapter: id.split('\0').pop() ?? id, endpointKey: this.name };
678
664
  }
679
665
  #recordInboxRequest(payload) {
680
666
  const hooks = this.#options.inbox;
@@ -729,7 +715,7 @@ export class IcqqIpcEndpoint {
729
715
  }
730
716
  }
731
717
  catch (error) {
732
- logger.debug(formatCompact({
718
+ this.#logger.debug(formatCompact({
733
719
  op: 'inbox_pull_system_msg',
734
720
  endpoint: this.name,
735
721
  error: error instanceof Error ? error.message : String(error),
@@ -773,22 +759,8 @@ function toNumericId(value, label) {
773
759
  }
774
760
  return n;
775
761
  }
776
- function summarizeSegmentTypes(segments) {
777
- if (!segments?.length)
778
- return undefined;
779
- return segments
780
- .map((segment) => {
781
- if (typeof segment.type === 'string')
782
- return segment.type;
783
- if (segment.type && typeof segment.type === 'object' && 'name' in segment.type) {
784
- return String(segment.type.name);
785
- }
786
- return '?';
787
- })
788
- .join(',');
789
- }
790
762
  async function defaultCreateIpc(config) {
791
- const uin = Number(config.name);
763
+ const uin = Number(config.id);
792
764
  if (config.rpc) {
793
765
  return IpcClient.connectRpc(config.rpc);
794
766
  }
@@ -14,12 +14,12 @@ export interface IcqqAgentEndpoint {
14
14
  request(action: string, params?: Record<string, unknown>): Promise<IpcResponse>;
15
15
  }
16
16
  export interface IcqqAgentDeps {
17
- getEndpoint: (endpointId: string) => IcqqAgentEndpoint;
17
+ getEndpoint: (endpointKey: string) => IcqqAgentEndpoint;
18
18
  getAdapter?: () => {
19
19
  getEndpoint: (id: string) => IcqqAgentEndpoint;
20
20
  };
21
21
  }
22
- export declare function registerIcqqAgentEndpoint(endpointId: string, endpoint: IcqqAgentEndpoint): () => void;
22
+ export declare function registerIcqqAgentEndpoint(endpointKey: string, endpoint: IcqqAgentEndpoint): () => void;
23
23
  /** Optional override used by tests / transitional callers. Pass `null` to clear. */
24
24
  export declare function setIcqqAgentDeps(deps: IcqqAgentDeps | null): void;
25
25
  export declare function getIcqqAgentDeps(): IcqqAgentDeps;
@@ -4,11 +4,11 @@
4
4
  */
5
5
  const endpoints = new Map();
6
6
  let override = null;
7
- export function registerIcqqAgentEndpoint(endpointId, endpoint) {
8
- endpoints.set(endpointId, endpoint);
7
+ export function registerIcqqAgentEndpoint(endpointKey, endpoint) {
8
+ endpoints.set(endpointKey, endpoint);
9
9
  return () => {
10
- if (endpoints.get(endpointId) === endpoint) {
11
- endpoints.delete(endpointId);
10
+ if (endpoints.get(endpointKey) === endpoint) {
11
+ endpoints.delete(endpointKey);
12
12
  }
13
13
  };
14
14
  }
@@ -16,10 +16,10 @@ export function registerIcqqAgentEndpoint(endpointId, endpoint) {
16
16
  export function setIcqqAgentDeps(deps) {
17
17
  override = deps;
18
18
  }
19
- function lookup(endpointId) {
20
- const registered = endpoints.get(endpointId);
19
+ function lookup(endpointKey) {
20
+ const registered = endpoints.get(endpointKey);
21
21
  if (!registered)
22
- throw new Error(`Endpoint ${endpointId} 不存在`);
22
+ throw new Error(`Endpoint ${endpointKey} 不存在`);
23
23
  return registered;
24
24
  }
25
25
  export function getIcqqAgentDeps() {
@@ -4,7 +4,7 @@
4
4
  * ICQQ 的凭据由外部守护进程(@icqqjs/cli)按账号持有(~/.icqq/<uin>/),
5
5
  * 其 IPC/RPC 协议是「按 uin 连接 + auth + 账号级 action」,没有 add-account /
6
6
  * 触发 login 的 action——扫码登录只能经 `icqq login <uin>` CLI 完成。
7
- * 因此 add 不走 kv 凭据录入,而是 bindFlow:校验 uin、把 { name: '<uin>' }
7
+ * 因此 add 不走 kv 凭据录入,而是 bindFlow:校验 uin、把 { id: '<uin>' }
8
8
  * 追加到 plugins.icqq.endpoints,并引导用户先完成守护进程登录。
9
9
  */
10
10
  import { addEndpointToConfig, createEndpointCommands } from '@zhin.js/adapter';
@@ -16,17 +16,17 @@ export const icqqEndpointCommands = createEndpointCommands({
16
16
  running: (use) => use(icqqRuntimeStateToken).endpoints.values(),
17
17
  describeEntry: (entry) => (entry.rpc ? 'rpc(远程守护进程)' : 'ipc(本地守护进程)'),
18
18
  addDescription: '登记 ICQQ endpoint(uin 需先经 icqq login 完成扫码登录;重启 zhin 生效)',
19
- bindFlow: ({ name }) => {
20
- if (!name) {
19
+ bindFlow: ({ id }) => {
20
+ if (!id) {
21
21
  return '用法:icqq.endpoint add <uin>(uin 为纯数字 QQ 号,且需先执行 icqq login <uin> 完成扫码登录)';
22
22
  }
23
- if (!/^\d+$/.test(name)) {
23
+ if (!/^\d+$/.test(id)) {
24
24
  return 'icqq endpoint 名必须是纯数字 QQ 号(uin)';
25
25
  }
26
26
  try {
27
- const filePath = addEndpointToConfig('icqq', { name });
28
- return (`✅ endpoint「${name}」已追加到 ${filePath} 的 plugins.icqq.endpoints。\n` +
29
- `⚠️ 请确保已执行 icqq login ${name} 启动守护进程并完成扫码登录;重启 zhin 后生效。`);
27
+ const filePath = addEndpointToConfig('icqq', { id });
28
+ return (`✅ endpoint「${id}」已追加到 ${filePath} 的 plugins.icqq.endpoints。\n` +
29
+ `⚠️ 请确保已执行 icqq login ${id} 启动守护进程并完成扫码登录;重启 zhin 后生效。`);
30
30
  }
31
31
  catch (error) {
32
32
  return `添加失败:${error instanceof Error ? error.message : String(error)}`;
@@ -10,7 +10,7 @@ import type { IpcSystemMessage } from './types.js';
10
10
  /** 行级公共前缀:adapter 槽 localName + endpoint live 名(uin)。 */
11
11
  export interface IcqqInboxBase {
12
12
  readonly adapter: string;
13
- readonly endpointId: string;
13
+ readonly endpointKey: string;
14
14
  }
15
15
  /** post_type=request / request.* 判定(OneBot 事件壳)。 */
16
16
  export declare function isIcqqRequestPayload(payload: unknown): boolean;