@zhin.js/adapter-onebot12 1.1.22 → 1.1.24

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,53 @@
1
1
  # @zhin.js/adapter-onebot12
2
2
 
3
+ ## 1.1.24
4
+
5
+ ### Patch Changes
6
+
7
+ - cbf1fb1: Unify Console page blueprints (`definePageBlueprint`, `defineAdapterPage`, `defineCommentReviewPage`, `projectBlueprintToImCard`, `projectBlueprintToCliPanel`) into `@zhin.js/console-contract` and `@zhin.js/page`, add zero-esbuild pure Blueprint synthesis + static client bundle mounting to `@zhin.js/pagemanager`, remove `@zhin.js/client` (React 19) and `.tsx` from all 30 server-side plugins and adapters (including `bilibili` and `juejin`), and remove legacy Era-1 Koa `PageManager` code.
8
+ - Updated dependencies [cbf1fb1]
9
+ - Updated dependencies [008ccad]
10
+ - Updated dependencies [a1f1028]
11
+ - @zhin.js/console-contract@1.1.4
12
+ - @zhin.js/page@1.1.16
13
+ - @zhin.js/core@1.1.58
14
+ - @zhin.js/im-contract@1.1.13
15
+ - @zhin.js/host-http@1.1.16
16
+ - @zhin.js/adapter@1.1.30
17
+ - @zhin.js/adapter-onebot-events@0.0.3
18
+ - @zhin.js/feature-kit@1.1.14
19
+ - @zhin.js/skill@1.1.15
20
+
21
+ ## 1.1.23
22
+
23
+ ### Patch Changes
24
+
25
+ - bc6ccb1: Deep audit bug fixes and 7 architectural improvements:
26
+ - Sink Agent Turn ingress pipeline from `@zhin.js/cli` into `@zhin.js/agent/runtime` (`plugin-runtime/ingress/`) and decompose `ingress-route.ts` into modular pipeline stages.
27
+ - Bind Session State Plane (`JsxButtonRegistry`, `InteractionRuntime`, `RuntimeInteractiveRouter`) to `PluginIncarnationRef` so active buttons and interactions survive unrelated-plugin HMR while expiring when the owning plugin reloads, and support per-conversation fallback numbering (`numberingScope: 'conversation'`).
28
+ - Establish `conversation_events` as the canonical message SSOT and reference `conversation_event_id`, `conversation_key`, `platform_message_id`, and `message_key` in `agent_messages` (`AGENT_MESSAGE_QUERY_VERSION = 2`), threading `conversationSequence` across inbound and outbound delivery receipts.
29
+ - Push down `DatabaseContextRepository` and `DatabaseScheduleJobStorage` full-session/full-table memory scans to indexed SQL queries.
30
+ - Add kernel-level inbound `replyTo` and `mentioned` auto-promotion (`promoteInboundMessagePayload`) plus reusable `InboundDeduper` and `TtlLookupCache` primitives in `@zhin.js/adapter`.
31
+ - Add `MiddlewareContext.dispatch` (`InboundDispatchSummary`) and `onAfterDispatch` hooks for `after-dispatch` middlewares, and support incremental rendering of in-place `envelope.payload` array mutations in `OutboundDeliveryRuntime`.
32
+ - Unify `HistoryWindowTracker` and `AgentCompactionRuntime` token/message budgets (`resolveUnifiedHistoryAndCompactionBudget`).
33
+ - Fix schema validation, database transaction connection cleanup and SQLite WAL mode, schedule timer wheel head-of-line blocking, security policy bash read paths and subagent guard propagation, group merge window permission carrier handoff, micro-compact namespaced tool matching, and WebSocket force-close / inbound segment mapping across adapters.
34
+ - Updated dependencies [6163abc]
35
+ - Updated dependencies [fed2c27]
36
+ - Updated dependencies [bc6ccb1]
37
+ - Updated dependencies [72b5a12]
38
+ - Updated dependencies [aa41889]
39
+ - Updated dependencies [4427e72]
40
+ - Updated dependencies [22bd14a]
41
+ - @zhin.js/core@1.1.57
42
+ - @zhin.js/client@1.1.25
43
+ - @zhin.js/page@1.1.15
44
+ - @zhin.js/im-contract@1.1.12
45
+ - @zhin.js/adapter@1.1.29
46
+ - @zhin.js/host-http@1.1.15
47
+ - @zhin.js/feature-kit@1.1.13
48
+ - @zhin.js/adapter-onebot-events@0.0.2
49
+ - @zhin.js/skill@1.1.14
50
+
3
51
  ## 1.1.22
4
52
 
5
53
  ### Patch Changes
package/lib/protocol.d.ts CHANGED
@@ -121,6 +121,8 @@ export declare function senderNickname(ev: OneBot12Event): string | undefined;
121
121
  * 新 Plugin Runtime 的 Message.content 为纯文本,mention 信息只能经 metadata.mentioned 传递。
122
122
  */
123
123
  export declare function isBotMentioned(ev: OneBot12Event): boolean;
124
+ /** 提取入站消息中的回复引用 message_id。 */
125
+ export declare function extractQuoteId(ev: OneBot12Event): string | undefined;
124
126
  /**
125
127
  * canonical MediaRef → OneBot 12 媒体字段(扩展字段降级形状)。
126
128
  * spec 正式投递形状是 `file_id`(先 upload_file 物化,见
package/lib/protocol.js CHANGED
@@ -159,6 +159,19 @@ export function isBotMentioned(ev) {
159
159
  return false;
160
160
  return ev.message.some((seg) => seg.type === 'mention' && String(seg.data?.['user_id'] ?? '') === selfId);
161
161
  }
162
+ /** 提取入站消息中的回复引用 message_id。 */
163
+ export function extractQuoteId(ev) {
164
+ if (!Array.isArray(ev.message))
165
+ return undefined;
166
+ for (const seg of ev.message) {
167
+ if (seg?.type === 'reply') {
168
+ const id = seg.data?.['message_id'] ?? seg.data?.['id'];
169
+ if (id != null && String(id).trim() !== '')
170
+ return String(id);
171
+ }
172
+ }
173
+ return undefined;
174
+ }
162
175
  /** OneBot 12 携带媒体的段类型。 */
163
176
  const ONEBOT12_MEDIA_TYPES = new Set(['image', 'voice', 'audio', 'video', 'file']);
164
177
  /** 媒体段 data 里的 canonical 字段,归一后不重复进 wire。 */
package/lib/webhook.js CHANGED
@@ -1,9 +1,10 @@
1
1
  import { createOnebot12SelfProfile } from './self-profile.js';
2
+ import { toCanonicalSegments } from '@zhin.js/core';
2
3
  import { acceptHttpIngress } from 'imhelper';
3
4
  import { createEndpointSpecification, endpointSpec, createRecallEndpointControl, } from 'zhin.js/adapter';
4
5
  import { formatCompact, getLogger } from '@zhin.js/logger';
5
6
  import { createOneBot12EndpointManagement } from './endpoint-management.js';
6
- import { buildSendMessageParams, matchesOneBot12Self, callOneBot12Action, formatInboundContent, formatOutboundSegments, isBotMentioned, isMessageEvent, onebot12InboundConversation, senderNickname, senderUserId, uploadOneBot12MediaSegments, } from './protocol.js';
7
+ import { buildSendMessageParams, matchesOneBot12Self, callOneBot12Action, extractQuoteId, formatInboundContent, formatOutboundSegments, isBotMentioned, isMessageEvent, onebot12InboundConversation, senderNickname, senderUserId, uploadOneBot12MediaSegments, } from './protocol.js';
7
8
  import { receiveOneBot12SideEvent } from './side-event-dispatch.js';
8
9
  import { createOneBot12ContentPort } from './content-port.js';
9
10
  import { callOnebot12Client, readOnebot12Response, onebot12ClientOptions, forwardOnebot12ClientEvents, Onebot12Client } from './client.js';
@@ -125,15 +126,19 @@ export class OneBot12WebhookEndpoint extends Onebot12Client {
125
126
  const content = formatInboundContent(ev);
126
127
  const nickname = senderNickname(ev);
127
128
  const mentioned = isBotMentioned(ev);
129
+ const quoteId = extractQuoteId(ev);
130
+ const segments = Array.isArray(ev.message) ? toCanonicalSegments(ev.message) : undefined;
128
131
  void this[endpointSpec].events.message({
129
132
  event: ev,
130
133
  ...(typeof ev.time === 'number' ? { platformSentAt: ev.time * 1000 } : {}),
131
134
  conversation,
132
135
  message: { conversation, id: ev.message_id },
133
136
  content,
137
+ ...(segments?.length ? { segments } : {}),
134
138
  sender: { id: senderUserId(ev), ...(nickname ? { name: nickname } : {}) },
135
139
  endpointId: this.#options.config.id,
136
140
  ...(mentioned ? { mentioned: true } : {}),
141
+ ...(quoteId ? { replyTo: { id: quoteId } } : {}),
137
142
  metadata: Object.freeze({
138
143
  detail_type: ev.detail_type,
139
144
  user_id: ev.user_id,
@@ -1,4 +1,5 @@
1
1
  import { createOnebot12SelfProfile } from './self-profile.js';
2
+ import { toCanonicalSegments } from '@zhin.js/core';
2
3
  /**
3
4
  * OneBot12 WS client endpoint — outbound connect to OneBot implementation.
4
5
  */
@@ -6,7 +7,7 @@ import WebSocket from 'ws';
6
7
  import { createEndpointSpecification, endpointSpec, createRecallEndpointControl, createEndpointLifecycle, } from 'zhin.js/adapter';
7
8
  import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
8
9
  import { createOneBot12EndpointManagement } from './endpoint-management.js';
9
- import { buildSendMessageParams, matchesOneBot12Self, buildWsConnectOptions, formatInboundContent, formatOutboundSegments, isBotMentioned, isMessageEvent, onebot12InboundConversation, senderNickname, senderUserId, uploadOneBot12MediaSegments, } from './protocol.js';
10
+ import { buildSendMessageParams, matchesOneBot12Self, buildWsConnectOptions, extractQuoteId, formatInboundContent, formatOutboundSegments, isBotMentioned, isMessageEvent, onebot12InboundConversation, senderNickname, senderUserId, uploadOneBot12MediaSegments, } from './protocol.js';
10
11
  import { receiveOneBot12SideEvent } from './side-event-dispatch.js';
11
12
  import { createOneBot12ContentPort } from './content-port.js';
12
13
  import { callOnebot12Client, readOnebot12Response, onebot12ClientOptions, forwardOnebot12ClientEvents, Onebot12Client } from './client.js';
@@ -143,15 +144,19 @@ export class OneBot12WsEndpoint extends Onebot12Client {
143
144
  const content = formatInboundContent(ev);
144
145
  const nickname = senderNickname(ev);
145
146
  const mentioned = isBotMentioned(ev);
147
+ const quoteId = extractQuoteId(ev);
148
+ const segments = Array.isArray(ev.message) ? toCanonicalSegments(ev.message) : undefined;
146
149
  void this[endpointSpec].events.message({
147
150
  event: ev,
148
151
  ...(typeof ev.time === 'number' ? { platformSentAt: ev.time * 1000 } : {}),
149
152
  conversation,
150
153
  message: { conversation, id: ev.message_id },
151
154
  content,
155
+ ...(segments?.length ? { segments } : {}),
152
156
  sender: { id: senderUserId(ev), name: senderNickname(ev) },
153
157
  endpointId: this.#options.config.id,
154
158
  ...(mentioned ? { mentioned: true } : {}),
159
+ ...(quoteId ? { replyTo: { id: quoteId } } : {}),
155
160
  metadata: Object.freeze({
156
161
  detail_type: ev.detail_type,
157
162
  user_id: ev.user_id,
@@ -185,8 +190,12 @@ export class OneBot12WsEndpoint extends Onebot12Client {
185
190
  const ws = create(url, { headers });
186
191
  this.#ws = ws;
187
192
  handle.onForceClose(() => {
193
+ if (this.#ws === ws) {
194
+ this.#rpc.disconnect();
195
+ }
188
196
  try {
189
197
  ws.close();
198
+ ws.terminate?.();
190
199
  }
191
200
  catch {
192
201
  /* ignore */
package/lib/ws-types.d.ts CHANGED
@@ -3,6 +3,7 @@ export interface OneBot12WsSocket {
3
3
  readonly readyState: number;
4
4
  send(data: string): void;
5
5
  close(code?: number, reason?: string): void;
6
+ terminate?(): void;
6
7
  on(event: 'open' | 'message' | 'close' | 'error', listener: (...args: unknown[]) => void): void;
7
8
  }
8
9
  export interface OneBot12WsCreateOptions {
@@ -1,11 +1,12 @@
1
1
  import { createOnebot12SelfProfile } from './self-profile.js';
2
+ import { toCanonicalSegments } from '@zhin.js/core';
2
3
  /**
3
4
  * OneBot12 reverse WSS endpoint — accepts inbound WebSocket from OneBot implementation.
4
5
  */
5
6
  import { createEndpointSpecification, endpointSpec, createEndpointLifecycle, createRecallEndpointControl, } from 'zhin.js/adapter';
6
7
  import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
7
8
  import { createOneBot12EndpointManagement } from './endpoint-management.js';
8
- import { buildSendMessageParams, matchesOneBot12Self, formatInboundContent, formatOutboundSegments, isBotMentioned, isMessageEvent, onebot12InboundConversation, senderNickname, senderUserId, uploadOneBot12MediaSegments, } from './protocol.js';
9
+ import { buildSendMessageParams, matchesOneBot12Self, extractQuoteId, formatInboundContent, formatOutboundSegments, isBotMentioned, isMessageEvent, onebot12InboundConversation, senderNickname, senderUserId, uploadOneBot12MediaSegments, } from './protocol.js';
9
10
  import { receiveOneBot12SideEvent } from './side-event-dispatch.js';
10
11
  import { createOneBot12ContentPort } from './content-port.js';
11
12
  import { callOnebot12Client, readOnebot12Response, onebot12ClientOptions, forwardOnebot12ClientEvents, Onebot12Client } from './client.js';
@@ -144,15 +145,19 @@ export class OneBot12WssEndpoint extends Onebot12Client {
144
145
  const conversation = { ...onebot12InboundConversation(String(this.#options.id), ev), endpoint: this[endpointSpec].identity };
145
146
  const nickname = senderNickname(ev);
146
147
  const mentioned = isBotMentioned(ev);
148
+ const quoteId = extractQuoteId(ev);
149
+ const segments = Array.isArray(ev.message) ? toCanonicalSegments(ev.message) : undefined;
147
150
  void this[endpointSpec].events.message({
148
151
  event: ev,
149
152
  ...(typeof ev.time === 'number' ? { platformSentAt: ev.time * 1000 } : {}),
150
153
  conversation,
151
154
  message: { conversation, id: ev.message_id },
152
155
  content: formatInboundContent(ev),
156
+ ...(segments?.length ? { segments } : {}),
153
157
  sender: { id: senderUserId(ev), name: senderNickname(ev) },
154
158
  endpointId: this.#options.config.id,
155
159
  ...(mentioned ? { mentioned: true } : {}),
160
+ ...(quoteId ? { replyTo: { id: quoteId } } : {}),
156
161
  metadata: Object.freeze({
157
162
  detail_type: ev.detail_type,
158
163
  user_id: ev.user_id,
@@ -188,8 +193,12 @@ export class OneBot12WssEndpoint extends Onebot12Client {
188
193
  await this.#connectionLifecycle.start(async (lifecycleHandle) => {
189
194
  this.#ws = socket;
190
195
  lifecycleHandle.onForceClose(() => {
196
+ if (this.#ws === socket) {
197
+ this.#rpc.disconnect();
198
+ }
191
199
  try {
192
200
  socket.close();
201
+ socket.terminate?.();
193
202
  }
194
203
  catch {
195
204
  /* ignore */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-onebot12",
3
- "version": "1.1.22",
3
+ "version": "1.1.24",
4
4
  "description": "Zhin.js OneBot 12 adapter for Plugin Runtime (WebSocket client)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -42,31 +42,30 @@
42
42
  "@imhelper/onebot-v12": "1.0.7",
43
43
  "imhelper": "1.0.7",
44
44
  "ws": "^8.21.1",
45
- "@zhin.js/adapter-onebot-events": "0.0.1",
46
- "@zhin.js/adapter": "1.1.28",
47
- "@zhin.js/client": "1.1.24",
48
- "@zhin.js/console-contract": "1.1.3",
49
- "@zhin.js/core": "1.1.56",
50
- "@zhin.js/feature-kit": "1.1.12",
51
- "@zhin.js/host-http": "1.1.14",
52
- "@zhin.js/im-contract": "1.1.11",
45
+ "@zhin.js/adapter-onebot-events": "0.0.3",
46
+ "@zhin.js/adapter": "1.1.30",
47
+ "@zhin.js/console-contract": "1.1.4",
48
+ "@zhin.js/core": "1.1.58",
49
+ "@zhin.js/feature-kit": "1.1.14",
50
+ "@zhin.js/host-http": "1.1.16",
51
+ "@zhin.js/im-contract": "1.1.13",
53
52
  "@zhin.js/logger": "1.1.2",
54
- "@zhin.js/page": "1.1.14",
55
- "@zhin.js/skill": "1.1.13"
53
+ "@zhin.js/page": "1.1.16",
54
+ "@zhin.js/skill": "1.1.15"
56
55
  },
57
56
  "devDependencies": {
58
57
  "@types/node": "^26.1.2",
59
58
  "@types/ws": "^8.18.1",
60
59
  "typescript": "^6.0.3",
61
60
  "vitest": "^4.1.10",
62
- "@zhin.js/host-http": "1.1.14",
63
- "zhin.js": "1.1.21"
61
+ "@zhin.js/host-http": "1.1.16",
62
+ "zhin.js": "1.1.23"
64
63
  },
65
64
  "peerDependencies": {
66
- "@zhin.js/adapter": "^1.1.28",
67
- "@zhin.js/command": "^1.1.14",
68
- "@zhin.js/core": "^1.1.56",
69
- "zhin.js": "^1.1.21"
65
+ "@zhin.js/adapter": "^1.1.30",
66
+ "@zhin.js/command": "^1.1.16",
67
+ "@zhin.js/core": "^1.1.58",
68
+ "zhin.js": "^1.1.23"
70
69
  },
71
70
  "peerDependenciesMeta": {
72
71
  "@zhin.js/command": {
@@ -1,6 +1,5 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
- import { definePage } from '@zhin.js/console-contract';
3
- import { createAdapterConsolePage } from '@zhin.js/client';
2
+ import { definePage, createAdapterConsolePage } from '@zhin.js/console-contract';
4
3
  export const meta = definePage({
5
4
  title: 'OneBot 12',
6
5
  titleI18n: { en: 'OneBot 12' },
@@ -1,5 +1,4 @@
1
- import { definePage } from '@zhin.js/console-contract';
2
- import { createAdapterConsolePage } from '@zhin.js/client';
1
+ import { definePage, createAdapterConsolePage } from '@zhin.js/console-contract';
3
2
 
4
3
  export const meta = definePage({
5
4
  title: 'OneBot 12',
package/plugin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
- import { definePlugin, pluginConsoleRpcToken } from 'zhin.js';
2
+ import { definePlugin } from 'zhin.js';
3
3
  import { createOnebot12ConsoleRpc } from "./lib/console.js";
4
4
  export default definePlugin({
5
5
  name: 'onebot12',
@@ -8,6 +8,8 @@ export default definePlugin({
8
8
  displayName: 'OneBot 12 Adapter',
9
9
  },
10
10
  setup(context) {
11
- context.resources.provide(pluginConsoleRpcToken, createOnebot12ConsoleRpc(context.resources, () => context.config.get()));
11
+ return {
12
+ consoleRpc: createOnebot12ConsoleRpc(context.resources, () => context.config.get()),
13
+ };
12
14
  },
13
15
  });
package/src/protocol.ts CHANGED
@@ -278,6 +278,18 @@ export function isBotMentioned(ev: OneBot12Event): boolean {
278
278
  );
279
279
  }
280
280
 
281
+ /** 提取入站消息中的回复引用 message_id。 */
282
+ export function extractQuoteId(ev: OneBot12Event): string | undefined {
283
+ if (!Array.isArray(ev.message)) return undefined;
284
+ for (const seg of ev.message) {
285
+ if (seg?.type === 'reply') {
286
+ const id = seg.data?.['message_id'] ?? seg.data?.['id'];
287
+ if (id != null && String(id).trim() !== '') return String(id);
288
+ }
289
+ }
290
+ return undefined;
291
+ }
292
+
281
293
  /** OneBot 12 携带媒体的段类型。 */
282
294
  const ONEBOT12_MEDIA_TYPES = new Set(['image', 'voice', 'audio', 'video', 'file']);
283
295
 
package/src/webhook.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { createOnebot12SelfProfile } from './self-profile.js';
2
+ import { toCanonicalSegments } from '@zhin.js/core';
2
3
  import { acceptHttpIngress } from 'imhelper';
3
4
  /**
4
5
  * OneBot12 HTTP webhook endpoint — POST inbound + api_url outbound.
@@ -21,6 +22,7 @@ import {
21
22
  buildSendMessageParams,
22
23
  matchesOneBot12Self,
23
24
  callOneBot12Action,
25
+ extractQuoteId,
24
26
  formatInboundContent,
25
27
  formatOutboundSegments,
26
28
  isBotMentioned,
@@ -164,15 +166,19 @@ export class OneBot12WebhookEndpoint extends Onebot12Client implements Endpoint
164
166
  const content = formatInboundContent(ev);
165
167
  const nickname = senderNickname(ev);
166
168
  const mentioned = isBotMentioned(ev);
169
+ const quoteId = extractQuoteId(ev);
170
+ const segments = Array.isArray(ev.message) ? toCanonicalSegments(ev.message) : undefined;
167
171
  void this[endpointSpec].events.message({
168
172
  event: ev,
169
173
  ...(typeof ev.time === 'number' ? { platformSentAt: ev.time * 1000 } : {}),
170
174
  conversation,
171
175
  message: { conversation, id: ev.message_id },
172
176
  content,
177
+ ...(segments?.length ? { segments } : {}),
173
178
  sender: { id: senderUserId(ev), ...(nickname ? { name: nickname } : {}) },
174
179
  endpointId: this.#options.config.id,
175
180
  ...(mentioned ? { mentioned: true } : {}),
181
+ ...(quoteId ? { replyTo: { id: quoteId } } : {}),
176
182
  metadata: Object.freeze({
177
183
  detail_type: ev.detail_type,
178
184
  user_id: ev.user_id,
@@ -1,4 +1,5 @@
1
1
  import { createOnebot12SelfProfile } from './self-profile.js';
2
+ import { toCanonicalSegments } from '@zhin.js/core';
2
3
  /**
3
4
  * OneBot12 WS client endpoint — outbound connect to OneBot implementation.
4
5
  */
@@ -22,6 +23,7 @@ import {
22
23
  buildSendMessageParams,
23
24
  matchesOneBot12Self,
24
25
  buildWsConnectOptions,
26
+ extractQuoteId,
25
27
  formatInboundContent,
26
28
  formatOutboundSegments,
27
29
  isBotMentioned,
@@ -182,15 +184,19 @@ export class OneBot12WsEndpoint extends Onebot12Client implements Endpoint {
182
184
  const content = formatInboundContent(ev);
183
185
  const nickname = senderNickname(ev);
184
186
  const mentioned = isBotMentioned(ev);
187
+ const quoteId = extractQuoteId(ev);
188
+ const segments = Array.isArray(ev.message) ? toCanonicalSegments(ev.message) : undefined;
185
189
  void this[endpointSpec].events.message({
186
190
  event: ev,
187
191
  ...(typeof ev.time === 'number' ? { platformSentAt: ev.time * 1000 } : {}),
188
192
  conversation,
189
193
  message: { conversation, id: ev.message_id },
190
194
  content,
195
+ ...(segments?.length ? { segments } : {}),
191
196
  sender: { id: senderUserId(ev), name: senderNickname(ev) },
192
197
  endpointId: this.#options.config.id,
193
198
  ...(mentioned ? { mentioned: true } : {}),
199
+ ...(quoteId ? { replyTo: { id: quoteId } } : {}),
194
200
  metadata: Object.freeze({
195
201
  detail_type: ev.detail_type,
196
202
  user_id: ev.user_id,
@@ -228,8 +234,12 @@ export class OneBot12WsEndpoint extends Onebot12Client implements Endpoint {
228
234
  const ws = create(url, { headers });
229
235
  this.#ws = ws;
230
236
  handle.onForceClose(() => {
237
+ if (this.#ws === ws) {
238
+ this.#rpc.disconnect();
239
+ }
231
240
  try {
232
241
  ws.close();
242
+ ws.terminate?.();
233
243
  } catch {
234
244
  /* ignore */
235
245
  }
package/src/ws-types.ts CHANGED
@@ -4,6 +4,7 @@ export interface OneBot12WsSocket {
4
4
  readonly readyState: number;
5
5
  send(data: string): void;
6
6
  close(code?: number, reason?: string): void;
7
+ terminate?(): void;
7
8
  on(event: 'open' | 'message' | 'close' | 'error', listener: (...args: unknown[]) => void): void;
8
9
  }
9
10
 
@@ -1,4 +1,5 @@
1
1
  import { createOnebot12SelfProfile } from './self-profile.js';
2
+ import { toCanonicalSegments } from '@zhin.js/core';
2
3
  /**
3
4
  * OneBot12 reverse WSS endpoint — accepts inbound WebSocket from OneBot implementation.
4
5
  */
@@ -20,6 +21,7 @@ import { createOneBot12EndpointManagement } from './endpoint-management.js';
20
21
  import {
21
22
  buildSendMessageParams,
22
23
  matchesOneBot12Self,
24
+ extractQuoteId,
23
25
  formatInboundContent,
24
26
  formatOutboundSegments,
25
27
  isBotMentioned,
@@ -179,15 +181,19 @@ export class OneBot12WssEndpoint extends Onebot12Client implements Endpoint {
179
181
  const conversation = { ...onebot12InboundConversation(String(this.#options.id), ev), endpoint: this[endpointSpec].identity };
180
182
  const nickname = senderNickname(ev);
181
183
  const mentioned = isBotMentioned(ev);
184
+ const quoteId = extractQuoteId(ev);
185
+ const segments = Array.isArray(ev.message) ? toCanonicalSegments(ev.message) : undefined;
182
186
  void this[endpointSpec].events.message({
183
187
  event: ev,
184
188
  ...(typeof ev.time === 'number' ? { platformSentAt: ev.time * 1000 } : {}),
185
189
  conversation,
186
190
  message: { conversation, id: ev.message_id },
187
191
  content: formatInboundContent(ev),
192
+ ...(segments?.length ? { segments } : {}),
188
193
  sender: { id: senderUserId(ev), name: senderNickname(ev) },
189
194
  endpointId: this.#options.config.id,
190
195
  ...(mentioned ? { mentioned: true } : {}),
196
+ ...(quoteId ? { replyTo: { id: quoteId } } : {}),
191
197
  metadata: Object.freeze({
192
198
  detail_type: ev.detail_type,
193
199
  user_id: ev.user_id,
@@ -225,8 +231,12 @@ export class OneBot12WssEndpoint extends Onebot12Client implements Endpoint {
225
231
  await this.#connectionLifecycle.start(async (lifecycleHandle) => {
226
232
  this.#ws = socket;
227
233
  lifecycleHandle.onForceClose(() => {
234
+ if (this.#ws === socket) {
235
+ this.#rpc.disconnect();
236
+ }
228
237
  try {
229
238
  socket.close();
239
+ socket.terminate?.();
230
240
  } catch {
231
241
  /* ignore */
232
242
  }