@zhin.js/core 1.5.2 → 1.5.4

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 (97) hide show
  1. package/README.md +2 -2
  2. package/lib/adapter.d.ts +7 -1
  3. package/lib/adapter.js +28 -24
  4. package/lib/agent-prompt.d.ts +2 -3
  5. package/lib/agent-prompt.js +5 -0
  6. package/lib/built/ai-outbound/parse.d.ts +1 -1
  7. package/lib/built/ai-outbound/parse.js +2 -6
  8. package/lib/built/ai-outbound/plain-mention-rewrite.js +4 -4
  9. package/lib/built/ai-outbound/resolve.js +8 -8
  10. package/lib/built/ai-outbound/types.d.ts +3 -3
  11. package/lib/built/ai-trigger.d.ts +5 -0
  12. package/lib/built/ai-trigger.js +40 -13
  13. package/lib/built/authorization.js +4 -4
  14. package/lib/built/command.d.ts +2 -2
  15. package/lib/built/command.js +2 -2
  16. package/lib/built/common-adapter-tools.d.ts +11 -11
  17. package/lib/built/common-adapter-tools.js +13 -13
  18. package/lib/built/config.d.ts +0 -4
  19. package/lib/built/config.js +0 -4
  20. package/lib/built/dispatcher.d.ts +2 -0
  21. package/lib/built/dispatcher.js +1 -1
  22. package/lib/built/endpoint-commands.js +2 -2
  23. package/lib/built/endpoint-lifecycle-service.js +13 -13
  24. package/lib/built/endpoint-lifecycle.d.ts +3 -3
  25. package/lib/built/endpoint-lifecycle.js +2 -2
  26. package/lib/built/endpoint-manager.d.ts +1 -1
  27. package/lib/built/interactive-segments/index.d.ts +1 -1
  28. package/lib/built/interactive-segments/index.js +1 -1
  29. package/lib/built/interactive-segments/keyboard-segment.d.ts +0 -1
  30. package/lib/built/interactive-segments/keyboard-segment.js +0 -1
  31. package/lib/built/interactive-segments/onebot-keyboard.d.ts +0 -3
  32. package/lib/built/interactive-segments/onebot-keyboard.js +0 -2
  33. package/lib/built/interactive-segments/resolve.d.ts +0 -3
  34. package/lib/built/interactive-segments/resolve.js +0 -3
  35. package/lib/built/interactive-segments/types.d.ts +8 -20
  36. package/lib/built/interactive-segments/types.js +0 -4
  37. package/lib/built/login-assist.d.ts +2 -2
  38. package/lib/built/login-assist.js +2 -2
  39. package/lib/built/message-enrich.d.ts +1 -1
  40. package/lib/built/message-enrich.js +2 -7
  41. package/lib/built/permission.d.ts +0 -4
  42. package/lib/built/permission.js +0 -4
  43. package/lib/built/prepend-quote-context.js +5 -5
  44. package/lib/built/rich-segments/capabilities.js +0 -1
  45. package/lib/built/rich-segments/html-segment.js +1 -4
  46. package/lib/built/rich-segments/markdown-segment.js +1 -4
  47. package/lib/built/rich-segments/types.d.ts +0 -5
  48. package/lib/built/schema-endpoint-manager.js +6 -6
  49. package/lib/built/tool.d.ts +5 -3
  50. package/lib/built/tool.js +14 -23
  51. package/lib/command.d.ts +4 -4
  52. package/lib/command.js +8 -11
  53. package/lib/endpoint-capabilities.d.ts +2 -3
  54. package/lib/endpoint-capabilities.js +4 -4
  55. package/lib/endpoint.d.ts +2 -0
  56. package/lib/endpoint.js +1 -0
  57. package/lib/errors.d.ts +2 -2
  58. package/lib/errors.js +4 -4
  59. package/lib/host-plugin-registry.d.ts +6 -5
  60. package/lib/host-plugin-registry.js +9 -4
  61. package/lib/im-scene.d.ts +1 -1
  62. package/lib/im-scene.js +5 -5
  63. package/lib/im-session-id.d.ts +2 -2
  64. package/lib/im-session-id.js +4 -4
  65. package/lib/index.d.ts +1 -2
  66. package/lib/index.js +0 -2
  67. package/lib/jsx-runtime.d.ts +1 -0
  68. package/lib/jsx.d.ts +19 -21
  69. package/lib/message.d.ts +1 -1
  70. package/lib/message.js +1 -1
  71. package/lib/notice.d.ts +2 -2
  72. package/lib/plugin-context.d.ts +11 -13
  73. package/lib/plugin-context.js +21 -18
  74. package/lib/plugin-runtime/im/contracts.d.ts +91 -12
  75. package/lib/plugin-runtime/im/contracts.js +79 -1
  76. package/lib/plugin-runtime/im/im-runtime.d.ts +33 -23
  77. package/lib/plugin-runtime/im/im-runtime.js +104 -63
  78. package/lib/plugin-runtime/im/index.d.ts +2 -0
  79. package/lib/plugin-runtime/im/index.js +2 -0
  80. package/lib/plugin-runtime/im/message-bus.d.ts +38 -0
  81. package/lib/plugin-runtime/im/message-bus.js +40 -0
  82. package/lib/plugin-runtime/im/message-dispatcher.js +13 -73
  83. package/lib/plugin-runtime/im/outbound-renderer.d.ts +3 -2
  84. package/lib/plugin-runtime/im/outbound-renderer.js +44 -8
  85. package/lib/plugin-runtime/im/service-tokens.d.ts +18 -0
  86. package/lib/plugin-runtime/im/service-tokens.js +13 -0
  87. package/lib/plugin.d.ts +4 -7
  88. package/lib/plugin.js +7 -30
  89. package/lib/request.d.ts +2 -2
  90. package/lib/side-event/types.d.ts +0 -4
  91. package/lib/types.d.ts +6 -11
  92. package/lib/utils.js +3 -1
  93. package/package.json +13 -12
  94. package/lib/built/qrcode-segment.d.ts +0 -18
  95. package/lib/built/qrcode-segment.js +0 -78
  96. package/lib/types-generator.d.ts +0 -5
  97. package/lib/types-generator.js +0 -71
@@ -20,7 +20,7 @@ function readAllEndpoints(root) {
20
20
  return [...(raw?.endpoints ?? [])];
21
21
  }
22
22
  function assertUniqueName(endpoints, context, name, replaceIndex) {
23
- const conflict = endpoints.findIndex((e) => e.context === context && e.name === name);
23
+ const conflict = endpoints.findIndex((e) => e.context === context && e.id === name);
24
24
  if (conflict >= 0 && conflict !== replaceIndex) {
25
25
  throw new Error(`zhin.config 中已存在 ${context}/${name}`);
26
26
  }
@@ -83,13 +83,13 @@ export class EndpointLifecycleService {
83
83
  const endpoints = readAllEndpoints(this.root);
84
84
  await this.persistEndpoints(endpoints);
85
85
  const configPath = formatDisplayPath(loader.resolvedPath);
86
- const names = endpoints.map((e) => `${e.context}/${e.name}`).join(', ') || '(无)';
86
+ const names = endpoints.map((e) => `${e.context}/${e.id}`).join(', ') || '(无)';
87
87
  return {
88
88
  message: `✅ 已将 ${endpoints.length} 个 endpoint 写入 \`${configPath}\`:${names}`,
89
89
  };
90
90
  }
91
91
  async connectConfig(adapter, config) {
92
- const name = config.name;
92
+ const name = config.id;
93
93
  if (adapter.endpoints.has(name)) {
94
94
  throw new Error(`Endpoint ${name} 已在线,请先 /endpoint stop ${String(adapter.name)} ${name}`);
95
95
  }
@@ -118,16 +118,16 @@ export class EndpointLifecycleService {
118
118
  const config = await manager.addEndpoint(ctx);
119
119
  if (!config.context)
120
120
  config.context = adapterKey;
121
- if (!config.name?.trim()) {
122
- throw new Error('Endpoint 名称不能为空');
121
+ if (!config.id?.trim()) {
122
+ throw new Error('Endpoint id 不能为空');
123
123
  }
124
124
  const endpoints = readAllEndpoints(this.root);
125
125
  try {
126
- assertUniqueName(endpoints, config.context, config.name);
126
+ assertUniqueName(endpoints, config.context, config.id);
127
127
  }
128
128
  catch (err) {
129
129
  if (err instanceof Error && err.message.includes('已存在')) {
130
- const online = adapter.endpoints.has(config.name);
130
+ const online = adapter.endpoints.has(config.id);
131
131
  const hint = online
132
132
  ? '该 Endpoint 已在运行时注册,无需重复扫码;可发 /endpoints 确认。若 zhin.config.yml 未更新,请 /endpoint sync。'
133
133
  : '可用 /endpoint start 重试连接,或 /endpoint remove 后重新添加。';
@@ -145,12 +145,12 @@ export class EndpointLifecycleService {
145
145
  adapter.constructor.endpointNeedsRestart;
146
146
  const hint = needsRestart
147
147
  ? '配置已写入,请重启进程后连接。'
148
- : `配置已写入,可用 /endpoint start ${adapterKey} ${config.name} 重试。`;
148
+ : `配置已写入,可用 /endpoint start ${adapterKey} ${config.id} 重试。`;
149
149
  throw new Error(`${err instanceof Error ? err.message : String(err)}(${hint})`);
150
150
  }
151
151
  const prefix = initialReply ? `${initialReply}\n` : '';
152
152
  return {
153
- message: `${prefix}✅ 已添加并连接 endpoint \`${config.name}\`(${adapterKey}),已写入 zhin.config。`,
153
+ message: `${prefix}✅ 已添加并连接 endpoint \`${config.id}\`(${adapterKey}),已写入 zhin.config。`,
154
154
  config,
155
155
  };
156
156
  }
@@ -158,7 +158,7 @@ export class EndpointLifecycleService {
158
158
  const adapter = this.resolveAdapter(adapterKey);
159
159
  const manager = this.resolveManager(adapterKey);
160
160
  const endpoints = readAllEndpoints(this.root);
161
- const index = endpoints.findIndex((e) => e.context === adapterKey && e.name === name);
161
+ const index = endpoints.findIndex((e) => e.context === adapterKey && e.id === name);
162
162
  if (index < 0) {
163
163
  throw new Error(`zhin.config 中不存在 ${adapterKey}/${name}`);
164
164
  }
@@ -168,7 +168,7 @@ export class EndpointLifecycleService {
168
168
  const ctx = buildProvisionContext(this.root, message);
169
169
  const updated = await manager.editEndpoint(name, ctx);
170
170
  updated.context = adapterKey;
171
- updated.name = name;
171
+ updated.id = name;
172
172
  endpoints[index] = updated;
173
173
  await this.persistEndpoints(endpoints);
174
174
  try {
@@ -192,7 +192,7 @@ export class EndpointLifecycleService {
192
192
  throw new Error(`无法移除 ${adapterKey}/${name}`);
193
193
  }
194
194
  const endpoints = readAllEndpoints(this.root);
195
- const next = endpoints.filter((e) => !(e.context === adapterKey && e.name === name));
195
+ const next = endpoints.filter((e) => !(e.context === adapterKey && e.id === name));
196
196
  if (next.length === endpoints.length) {
197
197
  throw new Error(`zhin.config 中不存在 ${adapterKey}/${name}`);
198
198
  }
@@ -203,7 +203,7 @@ export class EndpointLifecycleService {
203
203
  const adapter = this.resolveAdapter(adapterKey);
204
204
  const manager = this.resolveManager(adapterKey);
205
205
  const endpoints = readAllEndpoints(this.root);
206
- const config = endpoints.find((e) => e.context === adapterKey && e.name === name);
206
+ const config = endpoints.find((e) => e.context === adapterKey && e.id === name);
207
207
  if (!config) {
208
208
  throw new Error(`zhin.config 中不存在 ${adapterKey}/${name}`);
209
209
  }
@@ -4,7 +4,7 @@ import type { Plugin } from '../plugin.js';
4
4
  export type EndpointLifecycleKind = 'connect' | 'disconnect' | 'error';
5
5
  export interface EndpointLifecyclePayload {
6
6
  adapter: string;
7
- endpointId: string;
7
+ endpointKey: string;
8
8
  endpoint: Endpoint;
9
9
  kind: EndpointLifecycleKind;
10
10
  error?: string;
@@ -12,9 +12,9 @@ export interface EndpointLifecyclePayload {
12
12
  detail?: Record<string, unknown>;
13
13
  }
14
14
  /** 可 JSON 序列化的生命周期载荷(不含 endpoint 实例引用) */
15
- export declare function serializeEndpointLifecyclePayload(payload: Pick<EndpointLifecyclePayload, 'adapter' | 'endpointId' | 'kind' | 'error' | 'phase' | 'detail'>): {
15
+ export declare function serializeEndpointLifecyclePayload(payload: Pick<EndpointLifecyclePayload, 'adapter' | 'endpointKey' | 'kind' | 'error' | 'phase' | 'detail'>): {
16
16
  adapter: string;
17
- endpointId: string;
17
+ endpointKey: string;
18
18
  kind: EndpointLifecycleKind;
19
19
  error: string | undefined;
20
20
  phase: string | undefined;
@@ -3,7 +3,7 @@ import { Notice } from '../notice.js';
3
3
  export function serializeEndpointLifecyclePayload(payload) {
4
4
  return {
5
5
  adapter: payload.adapter,
6
- endpointId: payload.endpointId,
6
+ endpointKey: payload.endpointKey,
7
7
  kind: payload.kind,
8
8
  error: payload.error,
9
9
  phase: payload.phase,
@@ -13,7 +13,7 @@ export function serializeEndpointLifecyclePayload(payload) {
13
13
  export async function emitEndpointLifecycle(plugin, adapter, endpoint, kind, detail) {
14
14
  const payload = {
15
15
  adapter: String(adapter.name),
16
- endpointId: endpoint.$id,
16
+ endpointKey: endpoint.$id,
17
17
  endpoint,
18
18
  kind,
19
19
  ...detail,
@@ -4,7 +4,7 @@ import type { Plugin } from '../plugin.js';
4
4
  /** zhin.config.endpoints[] 单条记录 */
5
5
  export type EndpointConfigRecord = Record<string, unknown> & {
6
6
  context: string;
7
- name: string;
7
+ id: string;
8
8
  };
9
9
  export type EndpointStatusExtra = Record<string, unknown>;
10
10
  export interface ProvisionContext {
@@ -4,5 +4,5 @@ export * from './action.js';
4
4
  export * from './resolve.js';
5
5
  export * from './handlers.js';
6
6
  export * from './fallback-store.js';
7
- export { KeyboardSegment, InteractiveSegment } from './keyboard-segment.js';
7
+ export { KeyboardSegment } from './keyboard-segment.js';
8
8
  export * from './onebot-keyboard.js';
@@ -4,5 +4,5 @@ export * from './action.js';
4
4
  export * from './resolve.js';
5
5
  export * from './handlers.js';
6
6
  export * from './fallback-store.js';
7
- export { KeyboardSegment, InteractiveSegment } from './keyboard-segment.js';
7
+ export { KeyboardSegment } from './keyboard-segment.js';
8
8
  export * from './onebot-keyboard.js';
@@ -9,4 +9,3 @@ export declare class KeyboardSegment {
9
9
  };
10
10
  }
11
11
  /** @deprecated 使用 {@link KeyboardSegment} */
12
- export declare const InteractiveSegment: typeof KeyboardSegment;
@@ -10,4 +10,3 @@ export class KeyboardSegment {
10
10
  }
11
11
  }
12
12
  /** @deprecated 使用 {@link KeyboardSegment} */
13
- export const InteractiveSegment = KeyboardSegment;
@@ -2,9 +2,6 @@ import type { MessageSegment, SendContent } from '../../types.js';
2
2
  import { type KeyboardSegmentData } from './types.js';
3
3
  /** OneBot / QQ 系 keyboard 段(各 adapter 在 $sendMessage 中识别) */
4
4
  export declare function keyboardToOneBotSegment(data: KeyboardSegmentData): MessageSegment;
5
- /** @deprecated 使用 {@link keyboardToOneBotSegment} */
6
- export declare const interactiveToKeyboardSegment: typeof keyboardToOneBotSegment;
7
5
  /** 出站前将 core keyboard 段展开为 OneBot keyboard(保留前置 text 等段) */
8
6
  export declare function expandKeyboardSegmentsInContent(content: SendContent): SendContent;
9
7
  /** @deprecated 使用 {@link expandKeyboardSegmentsInContent} */
10
- export declare const expandInteractiveSegmentsInContent: typeof expandKeyboardSegmentsInContent;
@@ -16,7 +16,6 @@ export function keyboardToOneBotSegment(data) {
16
16
  };
17
17
  }
18
18
  /** @deprecated 使用 {@link keyboardToOneBotSegment} */
19
- export const interactiveToKeyboardSegment = keyboardToOneBotSegment;
20
19
  function asKeyboardData(item) {
21
20
  if (item instanceof KeyboardSegment)
22
21
  return item.data;
@@ -50,4 +49,3 @@ export function expandKeyboardSegmentsInContent(content) {
50
49
  return out;
51
50
  }
52
51
  /** @deprecated 使用 {@link expandKeyboardSegmentsInContent} */
53
- export const expandInteractiveSegmentsInContent = expandKeyboardSegmentsInContent;
@@ -11,8 +11,5 @@ export declare function collectKeyboardFallbackMaps(content: SendContent | undef
11
11
  export declare function renderKeyboardAsText(data: KeyboardSegmentData): string;
12
12
  export declare function hasKeyboardSegment(content: SendContent | undefined): boolean;
13
13
  /** @deprecated 使用 {@link hasKeyboardSegment} */
14
- export declare const hasInteractiveSegment: typeof hasKeyboardSegment;
15
14
  /** 按 Adapter interactivePolicy 将 keyboard 段转为 native 保留或文本降级 */
16
15
  export declare function resolveKeyboardSegments(content: SendContent, policy: InteractivePolicy): SendContent;
17
- /** @deprecated 使用 {@link resolveKeyboardSegments} */
18
- export declare const resolveInteractiveSegments: typeof resolveKeyboardSegments;
@@ -80,7 +80,6 @@ export function hasKeyboardSegment(content) {
80
80
  });
81
81
  }
82
82
  /** @deprecated 使用 {@link hasKeyboardSegment} */
83
- export const hasInteractiveSegment = hasKeyboardSegment;
84
83
  /** 按 Adapter interactivePolicy 将 keyboard 段转为 native 保留或文本降级 */
85
84
  export function resolveKeyboardSegments(content, policy) {
86
85
  if (!hasKeyboardSegment(content))
@@ -109,5 +108,3 @@ export function resolveKeyboardSegments(content, policy) {
109
108
  }
110
109
  return packSegments(out);
111
110
  }
112
- /** @deprecated 使用 {@link resolveKeyboardSegments} */
113
- export const resolveInteractiveSegments = resolveKeyboardSegments;
@@ -1,11 +1,7 @@
1
1
  import type { MessageElement } from '../../types.js';
2
2
  export declare const KEYBOARD_SEGMENT_TYPE: "keyboard";
3
- /** @deprecated 使用 {@link KEYBOARD_SEGMENT_TYPE} */
4
- export declare const INTERACTIVE_SEGMENT_TYPE: "keyboard";
5
3
  export declare const ACTION_SEGMENT_TYPE: "action";
6
4
  export type ButtonStyle = 'primary' | 'danger' | 'secondary';
7
- /** @deprecated 使用 {@link ButtonStyle} */
8
- export type InteractiveButtonStyle = ButtonStyle;
9
5
  /** callback:平台回调 action 入站;command:QQ 指令预填后文本入站 */
10
6
  export type ButtonInteractionMode = 'callback' | 'command';
11
7
  export interface ButtonCommandOptions {
@@ -14,7 +10,7 @@ export interface ButtonCommandOptions {
14
10
  /** QQ action.reply — 预填时引用原消息 */
15
11
  reply?: boolean;
16
12
  }
17
- export interface ButtonData {
13
+ export type ButtonData = {
18
14
  id: string;
19
15
  label: string;
20
16
  payload: string;
@@ -23,26 +19,20 @@ export interface ButtonData {
23
19
  /** 默认 callback */
24
20
  mode?: ButtonInteractionMode;
25
21
  command?: ButtonCommandOptions;
26
- }
27
- /** @deprecated 使用 {@link ButtonData} */
28
- export type InteractiveButton = ButtonData;
29
- export interface KeyboardFallback {
22
+ };
23
+ export type KeyboardFallback = {
30
24
  hint: string;
31
25
  map: Record<string, string>;
32
- }
33
- /** @deprecated 使用 {@link KeyboardFallback} */
34
- export type InteractiveFallback = KeyboardFallback;
35
- export interface KeyboardSegmentData {
26
+ };
27
+ export type KeyboardSegmentData = {
36
28
  rows: ButtonData[][];
37
29
  fallback?: KeyboardFallback;
38
- }
39
- /** @deprecated 使用 {@link KeyboardSegmentData} */
40
- export type InteractiveSegmentData = KeyboardSegmentData;
41
- export interface ActionSegmentData {
30
+ };
31
+ export type ActionSegmentData = {
42
32
  id: string;
43
33
  payload: string;
44
34
  sourceMessageId?: string;
45
- }
35
+ };
46
36
  export type InteractivePolicy = 'native' | 'text';
47
37
  export declare const DEFAULT_INTERACTIVE_POLICY: InteractivePolicy;
48
38
  export type InteractiveHandler = (message: import('../../message.js').Message<any>) => Promise<boolean> | boolean;
@@ -54,8 +44,6 @@ export declare function isKeyboardSegment(item: MessageElement): item is Message
54
44
  type: typeof KEYBOARD_SEGMENT_TYPE;
55
45
  data: KeyboardSegmentData;
56
46
  };
57
- /** @deprecated 使用 {@link isKeyboardSegment} */
58
- export declare const isInteractiveSegment: typeof isKeyboardSegment;
59
47
  export declare function isActionSegment(item: MessageElement): item is MessageElement & {
60
48
  type: typeof ACTION_SEGMENT_TYPE;
61
49
  data: ActionSegmentData;
@@ -1,13 +1,9 @@
1
1
  export const KEYBOARD_SEGMENT_TYPE = 'keyboard';
2
- /** @deprecated 使用 {@link KEYBOARD_SEGMENT_TYPE} */
3
- export const INTERACTIVE_SEGMENT_TYPE = KEYBOARD_SEGMENT_TYPE;
4
2
  export const ACTION_SEGMENT_TYPE = 'action';
5
3
  export const DEFAULT_INTERACTIVE_POLICY = 'text';
6
4
  export function isKeyboardSegment(item) {
7
5
  return item != null && typeof item === 'object' && 'type' in item && item.type === KEYBOARD_SEGMENT_TYPE;
8
6
  }
9
- /** @deprecated 使用 {@link isKeyboardSegment} */
10
- export const isInteractiveSegment = isKeyboardSegment;
11
7
  export function isActionSegment(item) {
12
8
  return item != null && typeof item === 'object' && 'type' in item && item.type === ACTION_SEGMENT_TYPE;
13
9
  }
@@ -26,7 +26,7 @@ export interface PendingLoginTaskPayload {
26
26
  export interface PendingLoginTask {
27
27
  id: string;
28
28
  adapter: string;
29
- endpointId: string;
29
+ endpointKey: string;
30
30
  type: LoginAssistType;
31
31
  payload: PendingLoginTaskPayload;
32
32
  createdAt: number;
@@ -53,7 +53,7 @@ export declare class LoginAssist {
53
53
  * 生产者:等待用户输入后 resolve。会发出 endpoint.login.pending 事件,未消费前可被 listPending 拉取(刷新后可继续消费)。
54
54
  * 超时后 reject 并 emit `endpoint.login.expired`。
55
55
  */
56
- waitForInput(adapter: string, endpointId: string, type: LoginAssistType, payload?: PendingLoginTaskPayload, options?: WaitForInputOptions): Promise<string | Record<string, unknown>>;
56
+ waitForInput(adapter: string, endpointKey: string, type: LoginAssistType, payload?: PendingLoginTaskPayload, options?: WaitForInputOptions): Promise<string | Record<string, unknown>>;
57
57
  /**
58
58
  * 消费者:提交结果,对应 waitForInput 的 Promise 会 resolve。
59
59
  */
@@ -28,14 +28,14 @@ export class LoginAssist {
28
28
  * 生产者:等待用户输入后 resolve。会发出 endpoint.login.pending 事件,未消费前可被 listPending 拉取(刷新后可继续消费)。
29
29
  * 超时后 reject 并 emit `endpoint.login.expired`。
30
30
  */
31
- waitForInput(adapter, endpointId, type, payload = {}, options) {
31
+ waitForInput(adapter, endpointKey, type, payload = {}, options) {
32
32
  const id = `login-${Date.now()}-${++this.idSeq}`;
33
33
  const timeoutMs = resolveTimeoutMs(options?.timeoutMs, this.defaultTimeoutMs);
34
34
  const createdAt = Date.now();
35
35
  const task = {
36
36
  id,
37
37
  adapter,
38
- endpointId,
38
+ endpointKey,
39
39
  type,
40
40
  payload: { message: payload.message ?? '', ...payload },
41
41
  createdAt,
@@ -35,5 +35,5 @@ export interface SyntheticMessageInput {
35
35
  export declare function createSyntheticMessage(input: SyntheticMessageInput): AgentTurnMessage;
36
36
  /** hook context 中的 commMessage 字段(AIHookEvent.context) */
37
37
  export declare function commMessageFromHookContext(context: Record<string, unknown>): Message<any> | undefined;
38
- /** 兼容 tool parameters 上的 contextKey(endpointId / sceneId / scope 等) */
38
+ /** 兼容 tool parameters 上的 contextKey(endpointKey / sceneId / scope 等) */
39
39
  export declare function resolveContextKey(message: Message<any>, key: string): unknown;
@@ -1,7 +1,6 @@
1
1
  import { Message } from '../message.js';
2
2
  import { hasSenderRole } from './roles.js';
3
3
  import { resolveSubjectRoles } from './authorization.js';
4
- import { getHostRootPlugin } from '../host-plugin-registry.js';
5
4
  function frameworkRolesFromSenderFlags(sender) {
6
5
  if (sender.isMaster)
7
6
  return ['master'];
@@ -14,10 +13,6 @@ export function senderRolesFromMessage(message) {
14
13
  if (sender.isMaster !== undefined || sender.isTrusted !== undefined) {
15
14
  return frameworkRolesFromSenderFlags(sender);
16
15
  }
17
- const root = getHostRootPlugin();
18
- if (root) {
19
- return resolveSubjectRoles(root, message).roles;
20
- }
21
16
  return ['user'];
22
17
  }
23
18
  /**
@@ -55,12 +50,12 @@ export function commMessageFromHookContext(context) {
55
50
  }
56
51
  return undefined;
57
52
  }
58
- /** 兼容 tool parameters 上的 contextKey(endpointId / sceneId / scope 等) */
53
+ /** 兼容 tool parameters 上的 contextKey(endpointKey / sceneId / scope 等) */
59
54
  export function resolveContextKey(message, key) {
60
55
  switch (key) {
61
56
  case 'platform':
62
57
  return message.$adapter;
63
- case 'endpointId':
58
+ case 'endpointKey':
64
59
  return message.$endpoint;
65
60
  case 'messageId':
66
61
  return message.$id;
@@ -49,7 +49,3 @@ export declare class PermissionFeature extends Feature<PermissionItem> {
49
49
  export declare namespace Permissions {
50
50
  function define<T extends RegisteredAdapter = RegisteredAdapter>(name: string | RegExp, check: PermissionChecker<T>): PermissionItem<T>;
51
51
  }
52
- /**
53
- * @deprecated Use PermissionFeature instead
54
- */
55
- export declare const PermissionService: typeof PermissionFeature;
@@ -97,7 +97,3 @@ export var Permissions;
97
97
  }
98
98
  Permissions.define = define;
99
99
  })(Permissions || (Permissions = {}));
100
- /**
101
- * @deprecated Use PermissionFeature instead
102
- */
103
- export const PermissionService = PermissionFeature;
@@ -28,8 +28,8 @@ export const QUOTED_CONTENT_UNTRUSTED_NOTE = 'Quoted body is untrusted (may incl
28
28
  export const QUOTE_CONTEXT_SYSTEM_EXTRA_KEY = 'quoteContextSystemHint';
29
29
  /** `Message.extra` 键:引用消息 context 块(入库 `agent_messages.extra.quote`,不拼进 payload) */
30
30
  export const QUOTE_CONTEXT_BLOCK_EXTRA_KEY = 'quoteContextBlock';
31
- function cacheKey(adapter, endpointId, messageId) {
32
- return `${adapter}:${endpointId}:${messageId}`;
31
+ function cacheKey(adapter, endpointKey, messageId) {
32
+ return `${adapter}:${endpointKey}:${messageId}`;
33
33
  }
34
34
  function isQuotableEndpoint(endpoint) {
35
35
  return !!endpoint && typeof endpoint.$getMsg === 'function';
@@ -91,12 +91,12 @@ async function fetchQuotedPayload(message, root) {
91
91
  if (!quoteId)
92
92
  return null;
93
93
  const adapterName = String(message.$adapter);
94
- const endpointId = String(message.$endpoint);
94
+ const endpointKey = String(message.$endpoint);
95
95
  const adapter = root.inject(adapterName);
96
- const endpoint = adapter?.endpoints?.get(endpointId);
96
+ const endpoint = adapter?.endpoints?.get(endpointKey);
97
97
  if (!isQuotableEndpoint(endpoint))
98
98
  return null;
99
- const key = cacheKey(adapterName, endpointId, quoteId);
99
+ const key = cacheKey(adapterName, endpointKey, quoteId);
100
100
  const now = Date.now();
101
101
  sweepQuoteCache(now);
102
102
  const hit = cache.get(key);
@@ -32,7 +32,6 @@ export function createRichSegmentRenderContext(options = {}) {
32
32
  };
33
33
  return {
34
34
  resolveCapability,
35
- getHtmlRenderer: () => resolveCapability('html-renderer'),
36
35
  warn: options.warn,
37
36
  logContentChain: options.logContentChain,
38
37
  };
@@ -5,10 +5,7 @@ import { RICH_SEGMENT_MODE } from './types.js';
5
5
  async function resolveHtmlRenderer(ctx) {
6
6
  if (!ctx)
7
7
  return undefined;
8
- if (ctx.resolveCapability) {
9
- return ctx.resolveCapability('html-renderer');
10
- }
11
- return ctx.getHtmlRenderer?.();
8
+ return ctx.resolveCapability('html-renderer');
12
9
  }
13
10
  export class HtmlSegment extends RichSegment {
14
11
  segmentType = 'html';
@@ -5,10 +5,7 @@ import { RICH_SEGMENT_MODE } from './types.js';
5
5
  async function resolveHtmlRenderer(ctx) {
6
6
  if (!ctx)
7
7
  return undefined;
8
- if (ctx.resolveCapability) {
9
- return ctx.resolveCapability('html-renderer');
10
- }
11
- return ctx.getHtmlRenderer?.();
8
+ return ctx.resolveCapability('html-renderer');
12
9
  }
13
10
  export class MarkdownSegment extends RichSegment {
14
11
  segmentType = 'markdown';
@@ -21,9 +21,6 @@ export declare const RICH_SEGMENT_MODE: {
21
21
  export type RichSegmentMode = (typeof RICH_SEGMENT_MODE)[keyof typeof RICH_SEGMENT_MODE] | string;
22
22
  /** 任意已注册 kind → 渲染 mode */
23
23
  export type OutboundRichSegmentPolicy = Record<string, string>;
24
- /** @deprecated 使用 OutboundRichSegmentPolicy;保留别名便于迁移 */
25
- export type RichSegmentKind = string;
26
- export type RichRenderMode = string;
27
24
  /** html-renderer 可选包契约 */
28
25
  export interface HtmlRendererForRichSegment {
29
26
  render(html: string, options?: {
@@ -57,8 +54,6 @@ export type RichSegmentCapabilityId = 'html-renderer' | 'speech' | 'media-pipeli
57
54
  export interface RichSegmentRenderContext {
58
55
  /** 按 id 懒加载能力(core 或 optional 包 register loader) */
59
56
  resolveCapability: <T>(id: RichSegmentCapabilityId) => Promise<T | undefined>;
60
- /** @deprecated 请用 resolveCapability('html-renderer') */
61
- getHtmlRenderer?: () => Promise<HtmlRendererForRichSegment | undefined>;
62
57
  /** 可选:rich_segment stage 结构化日志 */
63
58
  logContentChain?: (fields: ContentChainLogFields) => void;
64
59
  warn?: (message: string) => void;
@@ -20,16 +20,16 @@ export class SchemaEndpointManager {
20
20
  async addEndpoint(ctx) {
21
21
  const prompt = new Prompt(this.adapter.plugin, ctx.message);
22
22
  await ctx.onStatusUpdate(`请按提示填写 ${String(this.adapter.name)} endpoint 配置:`);
23
- const name = await prompt.text('Endpoint 名称(zhin.config endpoints[].name):');
23
+ const name = await prompt.text('Endpoint id(zhin.config endpoints[].id):');
24
24
  const fields = await prompt.getValueWithSchema(this.schema);
25
25
  return {
26
26
  context: String(this.adapter.name),
27
- name: name.trim(),
27
+ id: name.trim(),
28
28
  ...fields,
29
29
  };
30
30
  }
31
31
  async editEndpoint(name, ctx) {
32
- const existing = this.listEndpoints().find((e) => e.name === name);
32
+ const existing = this.listEndpoints().find((e) => e.id === name);
33
33
  if (!existing) {
34
34
  throw new Error(`配置中不存在 ${String(this.adapter.name)}/${name}`);
35
35
  }
@@ -40,14 +40,14 @@ export class SchemaEndpointManager {
40
40
  ...existing,
41
41
  ...fields,
42
42
  context: String(this.adapter.name),
43
- name,
43
+ id: name,
44
44
  };
45
45
  }
46
46
  async removeEndpoint(name) {
47
- return this.listEndpoints().some((e) => e.name === name);
47
+ return this.listEndpoints().some((e) => e.id === name);
48
48
  }
49
49
  async startEndpoint(name, ctx) {
50
- const rec = this.listEndpoints().find((e) => e.name === name);
50
+ const rec = this.listEndpoints().find((e) => e.id === name);
51
51
  if (!rec)
52
52
  throw new Error(`配置中不存在 ${String(this.adapter.name)}/${name}`);
53
53
  await ctx.onStatusUpdate(`正在连接 ${String(this.adapter.name)}/${name}…`);
@@ -5,10 +5,12 @@
5
5
  import { Feature, FeatureJSON } from "@zhin.js/kernel";
6
6
  import type { Tool, ToolJsonSchema, ToolParametersSchema, PropertySchema, MaybePromise, ToolScope } from "../types.js";
7
7
  import type { Message } from "../message.js";
8
+ import { type PermissionHost } from '@zhin.js/permission';
8
9
  /**
9
- * 检查工具是否可被当前 Message 通讯上下文访问
10
+ * 检查工具是否可被当前 Message 通讯上下文访问。
11
+ * `host` 为 PermissionHost 实例;无 host 且有 permissions → deny。
10
12
  */
11
- export declare function canAccessTool(tool: Tool, message?: Message<any>): boolean;
13
+ export declare function canAccessTool(tool: Tool, message?: Message<any>, host?: PermissionHost | null): Promise<boolean>;
12
14
  /**
13
15
  * 从参数定义中提取参数信息
14
16
  */
@@ -122,7 +124,7 @@ export declare class ToolFeature extends Feature<Tool> {
122
124
  /**
123
125
  * 根据 Message 通讯上下文过滤工具
124
126
  */
125
- filterByContext(tools: Tool[], message?: Message<any>): Tool[];
127
+ filterByContext(tools: Tool[], message?: Message<any>, host?: PermissionHost | null): Promise<Tool[]>;
126
128
  /**
127
129
  * 按插件名获取工具
128
130
  */
package/lib/built/tool.js CHANGED
@@ -3,14 +3,12 @@
3
3
  * 工具仅面向 AI(通过 MCP 注册),不再与 Command 互转
4
4
  */
5
5
  import { Feature } from "@zhin.js/kernel";
6
- import { isBuiltinPermit, isPlatformPermit } from './permit-parse.js';
7
- import { checkBuiltinPermit } from './permit-check.js';
8
- import { checkPlatformPermit } from './platform-permit.js';
9
- import { senderRolesFromMessage } from './message-enrich.js';
6
+ import { toPermissionSubject } from '@zhin.js/permission';
10
7
  /**
11
- * 检查工具是否可被当前 Message 通讯上下文访问
8
+ * 检查工具是否可被当前 Message 通讯上下文访问。
9
+ * `host` 为 PermissionHost 实例;无 host 且有 permissions → deny。
12
10
  */
13
- export function canAccessTool(tool, message) {
11
+ export async function canAccessTool(tool, message, host) {
14
12
  if (!message)
15
13
  return !tool.platforms?.length && !tool.scopes?.length && !tool.permissions?.length;
16
14
  const adapter = String(message.$adapter);
@@ -23,21 +21,10 @@ export function canAccessTool(tool, message) {
23
21
  }
24
22
  if (!tool.permissions?.length)
25
23
  return true;
26
- const roles = senderRolesFromMessage(message);
27
- for (const permit of tool.permissions) {
28
- if (isBuiltinPermit(permit)) {
29
- if (!checkBuiltinPermit(permit, message, roles))
30
- return false;
31
- }
32
- else if (isPlatformPermit(permit)) {
33
- if (!checkPlatformPermit(permit, message))
34
- return false;
35
- }
36
- else {
37
- return false;
38
- }
39
- }
40
- return true;
24
+ if (!host)
25
+ return false;
26
+ const subject = toPermissionSubject(message);
27
+ return host.checkAll(tool.permissions, subject);
41
28
  }
42
29
  // ============================================================================
43
30
  // Tool 工具函数
@@ -330,8 +317,12 @@ export class ToolFeature extends Feature {
330
317
  /**
331
318
  * 根据 Message 通讯上下文过滤工具
332
319
  */
333
- filterByContext(tools, message) {
334
- return tools.filter(tool => canAccessTool(tool, message));
320
+ async filterByContext(tools, message, host) {
321
+ const results = await Promise.all(tools.map(async (tool) => ({
322
+ tool,
323
+ allowed: await canAccessTool(tool, message, host),
324
+ })));
325
+ return results.filter((r) => r.allowed).map((r) => r.tool);
335
326
  }
336
327
  /**
337
328
  * 按插件名获取工具
package/lib/command.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { MatchResult, SegmentMatcher } from 'segment-matcher';
2
2
  import { AdapterMessage, SendContent, RegisteredAdapter, MaybePromise } from './types.js';
3
3
  import type { Message } from './message.js';
4
- import { Plugin } from './plugin.js';
4
+ import { type PermissionHost } from '@zhin.js/permission';
5
5
  type ConstructFirstParam<T extends new (...args: any[]) => any> = T extends new (...args: [infer U, ...any[]]) => any ? U : never;
6
6
  type ConstructSecondParam<T extends new (...args: any[]) => any> = T extends new (...args: [any, infer V, ...any[]]) => any ? V : never;
7
7
  /**
@@ -28,14 +28,14 @@ export declare class MessageCommand<T extends RegisteredAdapter = RegisteredAdap
28
28
  /** 命令要求的 permit 列表(空表示所有人可用) */
29
29
  get requiredPermits(): readonly string[];
30
30
  /** 与 handle 一致的 permit 校验,供帮助菜单等场景过滤不可见命令 */
31
- checkPermits(message: Message<AdapterMessage<T>>, plugin: Plugin): Promise<boolean>;
31
+ checkPermits(message: Message<AdapterMessage<T>>, host: PermissionHost | null): Promise<boolean>;
32
32
  /**
33
33
  * 处理消息,自动匹配命令并执行回调
34
34
  * @param message 消息对象
35
- * @param plugin 插件实例
35
+ * @param host PermissionHost(null 时有 permit 的命令 fail-closed)
36
36
  * @returns 命令返回内容或undefined
37
37
  */
38
- handle(message: Message<AdapterMessage<T>>, plugin: Plugin): Promise<SendContent | undefined>;
38
+ handle(message: Message<AdapterMessage<T>>, host: PermissionHost | null): Promise<SendContent | undefined>;
39
39
  }
40
40
  export declare namespace MessageCommand {
41
41
  type Callback<T extends RegisteredAdapter> = (message: Message<AdapterMessage<T>>, result: MatchResult) => SendContent | undefined | Promise<SendContent | undefined>;