@zhin.js/core 1.3.5 → 1.4.1

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 (55) hide show
  1. package/README.md +29 -43
  2. package/lib/adapter.js +17 -2
  3. package/lib/built/command.d.ts +5 -2
  4. package/lib/built/command.js +4 -1
  5. package/lib/built/interactive-segments/fallback-store.d.ts +29 -0
  6. package/lib/built/interactive-segments/fallback-store.js +63 -0
  7. package/lib/built/interactive-segments/handlers.d.ts +7 -0
  8. package/lib/built/interactive-segments/handlers.js +20 -4
  9. package/lib/built/interactive-segments/index.d.ts +1 -0
  10. package/lib/built/interactive-segments/index.js +1 -0
  11. package/lib/built/interactive-segments/resolve.d.ts +10 -1
  12. package/lib/built/interactive-segments/resolve.js +30 -1
  13. package/lib/built/login-assist.d.ts +19 -2
  14. package/lib/built/login-assist.js +43 -4
  15. package/lib/built/segment-contract/index.d.ts +3 -1
  16. package/lib/built/segment-contract/index.js +3 -1
  17. package/lib/built/segment-contract/json-schema.d.ts +28 -0
  18. package/lib/built/segment-contract/json-schema.js +128 -0
  19. package/lib/built/segment-contract/media.d.ts +11 -1
  20. package/lib/built/segment-contract/media.js +30 -0
  21. package/lib/built/segment-contract/text.d.ts +7 -0
  22. package/lib/built/segment-contract/text.js +34 -0
  23. package/lib/built/segment-contract/types.d.ts +6 -2
  24. package/lib/built/segment-contract/validate.js +1 -0
  25. package/lib/command.d.ts +5 -2
  26. package/lib/command.js +5 -2
  27. package/lib/endpoint.d.ts +4 -1
  28. package/lib/endpoint.js +1 -0
  29. package/lib/feature/adapter.d.ts +2 -0
  30. package/lib/feature/adapter.js +2 -0
  31. package/lib/feature/command.d.ts +2 -0
  32. package/lib/feature/command.js +2 -0
  33. package/lib/feature/component.d.ts +2 -0
  34. package/lib/feature/component.js +2 -0
  35. package/lib/feature/middleware.d.ts +2 -0
  36. package/lib/feature/middleware.js +2 -0
  37. package/lib/plugin-runtime/im/contracts.d.ts +37 -3
  38. package/lib/plugin-runtime/im/contracts.js +8 -1
  39. package/lib/plugin-runtime/im/im-runtime.d.ts +47 -2
  40. package/lib/plugin-runtime/im/im-runtime.js +176 -11
  41. package/lib/plugin-runtime/im/index.d.ts +1 -0
  42. package/lib/plugin-runtime/im/index.js +1 -0
  43. package/lib/plugin-runtime/im/interactive.d.ts +25 -0
  44. package/lib/plugin-runtime/im/interactive.js +41 -0
  45. package/lib/plugin-runtime/im/message-dispatcher.d.ts +12 -2
  46. package/lib/plugin-runtime/im/message-dispatcher.js +153 -12
  47. package/lib/plugin-runtime/im/outbound-segments.d.ts +53 -6
  48. package/lib/plugin-runtime/im/outbound-segments.js +173 -10
  49. package/lib/plugin.d.ts +6 -3
  50. package/lib/plugin.js +38 -24
  51. package/lib/tool-zod.d.ts +16 -1
  52. package/lib/tool-zod.js +138 -51
  53. package/lib/utils.d.ts +3 -0
  54. package/lib/utils.js +6 -3
  55. package/package.json +57 -10
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Canonical outbound segment 的 JSON Schema SSOT —— 供 AI structured output
3
+ * (AI SDK `Output.object({ schema })`)约束模型直接输出 zhin 消息段数组。
4
+ *
5
+ * 与 validate.ts(@zhin.js/schema 运行时校验)表达同一组约束的 JSON Schema 形态;
6
+ * 严格段类型集合必须与 assert.ts 的 STRICT_CANONICAL_TYPES 保持一致
7
+ * (tests/segment-contract/json-schema.test.ts 有交叉防漂移测试)。
8
+ *
9
+ * 注意:解析侧 parseOutboundSegment 对严格段要求 canonical 形态
10
+ * (如 image 必须携带 data.media: MediaRef),本 schema 与之对齐。
11
+ */
12
+ const platformJsonSchema = {
13
+ type: 'object',
14
+ additionalProperties: true,
15
+ description: '平台专有字段(一般无需输出)',
16
+ };
17
+ /** MediaRef(canonical 媒体引用):types.ts 的 MediaRef 接口 */
18
+ export const mediaRefJsonSchema = {
19
+ type: 'object',
20
+ properties: {
21
+ kind: {
22
+ type: 'string',
23
+ enum: ['url', 'path', 'base64', 'file'],
24
+ description: 'url=http(s) 链接;path=本地文件路径;base64=内联 base64 数据;file=平台不透明文件引用(如 Telegram file_id)',
25
+ },
26
+ value: { type: 'string', description: '媒体内容:URL / 文件路径 / 纯 base64' },
27
+ mime_type: { type: 'string', description: '如 image/png、audio/mpeg' },
28
+ },
29
+ required: ['kind', 'value'],
30
+ additionalProperties: false,
31
+ };
32
+ function strictBranch(type, data) {
33
+ return {
34
+ type: 'object',
35
+ properties: {
36
+ type: { const: type },
37
+ data,
38
+ platform: platformJsonSchema,
39
+ },
40
+ required: ['type', 'data'],
41
+ additionalProperties: false,
42
+ };
43
+ }
44
+ function dataObject(properties, required) {
45
+ return { type: 'object', properties, required: [...required], additionalProperties: false };
46
+ }
47
+ /** 宽松分支:未纳入严格契约的段类型,仅约束顶层形状(与 isCanonicalSegment 宽松路径一致) */
48
+ const looseBranch = {
49
+ type: 'object',
50
+ properties: {
51
+ type: {
52
+ type: 'string',
53
+ enum: ['video', 'audio', 'voice', 'record', 'file', 'link', 'markdown', 'html', 'keyboard', 'action'],
54
+ },
55
+ data: { type: 'object' },
56
+ platform: platformJsonSchema,
57
+ },
58
+ required: ['type', 'data'],
59
+ additionalProperties: false,
60
+ };
61
+ /** 严格段类型集合(SSOT:assert.ts STRICT_CANONICAL_TYPES) */
62
+ export const STRICT_OUTBOUND_SEGMENT_TYPES = [
63
+ 'text', 'mention', 'image', 'reply', 'forward', 'face', 'dice', 'rps',
64
+ ];
65
+ /**
66
+ * 单条 outbound 消息段的 JSON Schema。
67
+ * 严格段镜像 validate.ts 的 data 约束;宽松段走 generic 分支。
68
+ */
69
+ export const outboundSegmentJsonSchema = {
70
+ description: 'zhin 消息段 {type, data, platform?}',
71
+ anyOf: [
72
+ strictBranch('text', dataObject({
73
+ text: { type: 'string', description: '文本内容' },
74
+ }, ['text'])),
75
+ strictBranch('mention', dataObject({
76
+ target: { type: 'string', description: '被 @ 用户的平台 id' },
77
+ name: { type: 'string' },
78
+ }, ['target'])),
79
+ strictBranch('image', dataObject({
80
+ media: mediaRefJsonSchema,
81
+ alt: { type: 'string' },
82
+ }, ['media'])),
83
+ strictBranch('reply', dataObject({
84
+ message_id: { type: 'string', description: '被引用消息的平台消息 id' },
85
+ }, ['message_id'])),
86
+ strictBranch('forward', dataObject({
87
+ forward_id: { type: 'string' },
88
+ title: { type: 'string' },
89
+ messages: { type: 'array', items: { type: 'array' } },
90
+ }, ['forward_id'])),
91
+ strictBranch('face', dataObject({
92
+ id: { anyOf: [{ type: 'string' }, { type: 'number' }], description: '平台表情 id' },
93
+ name: { type: 'string' },
94
+ }, ['id'])),
95
+ strictBranch('dice', dataObject({
96
+ result: { type: 'number' },
97
+ }, [])),
98
+ strictBranch('rps', dataObject({
99
+ result: { type: 'number' },
100
+ }, [])),
101
+ looseBranch,
102
+ ],
103
+ };
104
+ /**
105
+ * AI 结构化出站根对象(ADR 0025 JSON DSL 的 schema 形态)。
106
+ * 与 parseAiOutboundJson / ZhinAiOutboundPayload 对齐:text、mentions、segments
107
+ * 均为可选(provider strict 模式兼容性考虑),"至少一项"由 prompt 与下游校验兜底。
108
+ */
109
+ export const aiOutboundJsonSchema = {
110
+ type: 'object',
111
+ properties: {
112
+ text: {
113
+ type: 'string',
114
+ description: '纯文本回复正文;与 segments 至少输出一项。使用 mentions 时必填',
115
+ },
116
+ mentions: {
117
+ type: 'array',
118
+ items: { type: 'string' },
119
+ description: '要 @ 的会话成员引用(昵称或 id,由宿主解析为平台账号);需配合 text 使用',
120
+ },
121
+ segments: {
122
+ type: 'array',
123
+ items: outboundSegmentJsonSchema,
124
+ description: 'zhin 消息段数组,如 [{type:"image",data:{media:{kind:"url",value:"https://…"}}},{type:"text",data:{text:"…"}}]',
125
+ },
126
+ },
127
+ additionalProperties: false,
128
+ };
@@ -1,4 +1,4 @@
1
- import type { MediaRef } from './types.js';
1
+ import type { MediaRef, Segment } from './types.js';
2
2
  import { isMediaRef } from './validate.js';
3
3
  export { isMediaRef };
4
4
  export declare function mediaRefFromLegacyData(data: Record<string, unknown>): MediaRef | undefined;
@@ -6,3 +6,13 @@ export declare function mediaRefToLegacyFields(media: MediaRef): {
6
6
  url?: string;
7
7
  file?: string;
8
8
  };
9
+ export interface SegmentMediaRef {
10
+ readonly type: string;
11
+ readonly media: MediaRef;
12
+ }
13
+ /**
14
+ * 从入站 canonical 段收集媒体引用(image / audio / video / file)。
15
+ * 兼容 canonical `data.media` 与旧轨 url/file/base64 字段;
16
+ * 无媒体段时返回空数组(调用方无需特判 undefined)。
17
+ */
18
+ export declare function collectSegmentMedia(segments: readonly Segment[] | undefined): SegmentMediaRef[];
@@ -5,6 +5,13 @@ export function mediaRefFromLegacyData(data) {
5
5
  return data.media;
6
6
  }
7
7
  const mimeType = typeof data.mime_type === 'string' ? data.mime_type : undefined;
8
+ // 平台不透明文件引用(Telegram file_id / Milky resource_id 等)
9
+ const fileRef = typeof data.file_id === 'string' && data.file_id.trim()
10
+ ? data.file_id.trim()
11
+ : undefined;
12
+ if (fileRef) {
13
+ return { kind: 'file', value: fileRef, ...(mimeType ? { mime_type: mimeType } : {}) };
14
+ }
8
15
  const base64 = typeof data.base64 === 'string' && data.base64.trim()
9
16
  ? data.base64.trim()
10
17
  : typeof data.data === 'string' && data.data.trim() && !String(data.data).startsWith('http')
@@ -34,6 +41,29 @@ export function mediaRefToLegacyFields(media) {
34
41
  return { url: media.value, file: media.value };
35
42
  if (media.kind === 'path')
36
43
  return { file: media.value, url: media.value };
44
+ if (media.kind === 'file')
45
+ return { file: media.value, url: media.value };
37
46
  const encoded = media.value.startsWith('base64://') ? media.value : `base64://${media.value}`;
38
47
  return { file: encoded, url: encoded };
39
48
  }
49
+ /** 入站段携带媒体引用的段类型(纯文本视图无法承载的信息)。 */
50
+ const MEDIA_SEGMENT_TYPES = new Set(['image', 'audio', 'video', 'file']);
51
+ /**
52
+ * 从入站 canonical 段收集媒体引用(image / audio / video / file)。
53
+ * 兼容 canonical `data.media` 与旧轨 url/file/base64 字段;
54
+ * 无媒体段时返回空数组(调用方无需特判 undefined)。
55
+ */
56
+ export function collectSegmentMedia(segments) {
57
+ if (!segments?.length)
58
+ return [];
59
+ const out = [];
60
+ for (const segment of segments) {
61
+ if (!segment || typeof segment.type !== 'string' || !MEDIA_SEGMENT_TYPES.has(segment.type)) {
62
+ continue;
63
+ }
64
+ const media = mediaRefFromLegacyData(segment.data ?? {});
65
+ if (media)
66
+ out.push({ type: segment.type, media });
67
+ }
68
+ return out;
69
+ }
@@ -0,0 +1,7 @@
1
+ import type { Segment } from './types.js';
2
+ /**
3
+ * 提取纯文本视图:text 段原文拼接,mention/at 段渲染为 `@name`(无名回退
4
+ * `@target`,`all` → `@all`);其余段类型不产生文本(媒体 / 回复等结构化
5
+ * 信息经 `segments` 轨道消费,不污染命令输入)。
6
+ */
7
+ export declare function segmentsToPlainText(segments: readonly Segment[]): string;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Canonical Segment[] → 纯文本视图(SSOT)。
3
+ * 入站双轨:结构化段为唯一内部契约,纯文本视图供命令匹配 / AI 兜底消费。
4
+ * @see docs/architecture/segment-content-model.md
5
+ */
6
+ import { readMentionName, readMentionTarget } from './mention.js';
7
+ /**
8
+ * 提取纯文本视图:text 段原文拼接,mention/at 段渲染为 `@name`(无名回退
9
+ * `@target`,`all` → `@all`);其余段类型不产生文本(媒体 / 回复等结构化
10
+ * 信息经 `segments` 轨道消费,不污染命令输入)。
11
+ */
12
+ export function segmentsToPlainText(segments) {
13
+ let out = '';
14
+ for (const segment of segments) {
15
+ // Segment 含 SegmentBase 兜底成员,type 窄化不能联动窄化 data,统一按字典读
16
+ const data = segment.data;
17
+ if (segment.type === 'text') {
18
+ if (typeof data.text === 'string')
19
+ out += data.text;
20
+ continue;
21
+ }
22
+ if (segment.type === 'mention' || segment.type === 'at') {
23
+ const name = readMentionName(data);
24
+ if (name) {
25
+ out += `@${name}`;
26
+ continue;
27
+ }
28
+ const target = readMentionTarget(data);
29
+ if (target)
30
+ out += `@${target}`;
31
+ }
32
+ }
33
+ return out;
34
+ }
@@ -7,9 +7,13 @@ export interface SegmentBase {
7
7
  data: Record<string, unknown>;
8
8
  platform?: Record<string, unknown>;
9
9
  }
10
- /** 媒体引用占位(完整 schema 随 adapter 迁移补齐) */
10
+ /**
11
+ * 媒体引用占位(完整 schema 随 adapter 迁移补齐)。
12
+ * kind=file:平台侧不透明文件引用(如 Telegram file_id、Milky resource_id),
13
+ * 非 URL/本地路径,消费方需经平台 API 解析。
14
+ */
11
15
  export interface MediaRef {
12
- kind: 'url' | 'path' | 'base64';
16
+ kind: 'url' | 'path' | 'base64' | 'file';
13
17
  value: string;
14
18
  mime_type?: string;
15
19
  }
@@ -4,6 +4,7 @@ const mediaKindSchema = Schema.union([
4
4
  Schema.const('url'),
5
5
  Schema.const('path'),
6
6
  Schema.const('base64'),
7
+ Schema.const('file'),
7
8
  ]);
8
9
  export const mediaRefSchema = Schema.object({
9
10
  kind: mediaKindSchema.required(),
package/lib/command.d.ts CHANGED
@@ -5,8 +5,11 @@ import { Plugin } from './plugin.js';
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
  /**
8
- * MessageCommand类:命令系统核心,基于segment-matcher实现。
9
- * 支持多平台命令注册、作用域限制、参数解析、异步处理等。
8
+ * MessageCommand类:经典命令系统(segment-matcher)。
9
+ *
10
+ * @deprecated 新插件请用 `defineCommand`(`zhin.js/command`)+ `commands/` 约定目录。
11
+ * 本类仍供 Agent init / game-kit hub / legacy `CommandFeature` 使用;计划随经典
12
+ * Plugin 路径一并退役。见 `docs/contributing/public-api-surface.md`。
10
13
  */
11
14
  export declare class MessageCommand<T extends RegisteredAdapter = RegisteredAdapter> extends SegmentMatcher {
12
15
  #private;
package/lib/command.js CHANGED
@@ -1,7 +1,10 @@
1
1
  import { SegmentMatcher } from 'segment-matcher';
2
2
  /**
3
- * MessageCommand类:命令系统核心,基于segment-matcher实现。
4
- * 支持多平台命令注册、作用域限制、参数解析、异步处理等。
3
+ * MessageCommand类:经典命令系统(segment-matcher)。
4
+ *
5
+ * @deprecated 新插件请用 `defineCommand`(`zhin.js/command`)+ `commands/` 约定目录。
6
+ * 本类仍供 Agent init / game-kit hub / legacy `CommandFeature` 使用;计划随经典
7
+ * Plugin 路径一并退役。见 `docs/contributing/public-api-surface.md`。
5
8
  */
6
9
  export class MessageCommand extends SegmentMatcher {
7
10
  #callbacks = [];
package/lib/endpoint.d.ts CHANGED
@@ -1,12 +1,15 @@
1
1
  import type { Adapters, Adapter } from './adapter.js';
2
2
  import type { EndpointCapabilitiesConfig, FullEndpoint } from './endpoint-capabilities.js';
3
+ import type { EndpointWithManagement } from '@zhin.js/adapter';
4
+ export type { EndpointChannel, EndpointChannelParent, EndpointFriend, EndpointGroup, EndpointManagement, EndpointWithManagement, EndpointManagementCapability, } from '@zhin.js/adapter';
5
+ export { endpointManagementCapabilityIds, listEndpointManagementCapabilities, resolveEndpointManagement, } from '@zhin.js/adapter';
3
6
  export type { EndpointCapability, EndpointCapabilitiesConfig, InboundEndpoint, OutboundEndpoint, FullEndpoint, CapableEndpoint, } from './endpoint-capabilities.js';
4
7
  export { DEFAULT_ENDPOINT_CAPABILITIES, OutboundNotSupportedError, InboundNotSupportedError, resolveEndpointCapabilities, registerEndpointCapabilities, getEndpointCapabilities, getAdapterCapabilities, hasInbound, hasOutbound, assertInbound, assertOutbound, } from './endpoint-capabilities.js';
5
8
  /**
6
9
  * Endpoint 接口:全双工平台机器人(入站 + 出站)。
7
10
  * 纯入站 / 纯出站请实现 InboundEndpoint / OutboundEndpoint。
8
11
  */
9
- export type Endpoint<Config extends object = object, Event extends object = object> = FullEndpoint<Config, Event>;
12
+ export type Endpoint<Config extends object = object, Event extends object = object> = FullEndpoint<Config, Event> & EndpointWithManagement;
10
13
  export declare namespace Endpoint {
11
14
  type Config<K extends keyof Adapters = keyof Adapters> = Adapter.EndpointConfig<Adapter.InferEndpoint<Adapters[K]>> & EndpointCapabilitiesConfig & {
12
15
  context: K;
package/lib/endpoint.js CHANGED
@@ -1 +1,2 @@
1
+ export { endpointManagementCapabilityIds, listEndpointManagementCapabilities, resolveEndpointManagement, } from '@zhin.js/adapter';
1
2
  export { DEFAULT_ENDPOINT_CAPABILITIES, OutboundNotSupportedError, InboundNotSupportedError, resolveEndpointCapabilities, registerEndpointCapabilities, getEndpointCapabilities, getAdapterCapabilities, hasInbound, hasOutbound, assertInbound, assertOutbound, } from './endpoint-capabilities.js';
@@ -0,0 +1,2 @@
1
+ /** Authoring API for Adapter Feature — implementation in `@zhin.js/adapter`. */
2
+ export * from '@zhin.js/adapter';
@@ -0,0 +1,2 @@
1
+ /** Authoring API for Adapter Feature — implementation in `@zhin.js/adapter`. */
2
+ export * from '@zhin.js/adapter';
@@ -0,0 +1,2 @@
1
+ /** Authoring API for Command Feature — implementation in `@zhin.js/command`. */
2
+ export * from '@zhin.js/command';
@@ -0,0 +1,2 @@
1
+ /** Authoring API for Command Feature — implementation in `@zhin.js/command`. */
2
+ export * from '@zhin.js/command';
@@ -0,0 +1,2 @@
1
+ /** Authoring API for Component Feature — implementation in `@zhin.js/component`. */
2
+ export * from '@zhin.js/component';
@@ -0,0 +1,2 @@
1
+ /** Authoring API for Component Feature — implementation in `@zhin.js/component`. */
2
+ export * from '@zhin.js/component';
@@ -0,0 +1,2 @@
1
+ /** Authoring API for Middleware Feature — implementation in `@zhin.js/middleware`. */
2
+ export * from '@zhin.js/middleware';
@@ -0,0 +1,2 @@
1
+ /** Authoring API for Middleware Feature — implementation in `@zhin.js/middleware`. */
2
+ export * from '@zhin.js/middleware';
@@ -1,4 +1,6 @@
1
- import type { CapabilityId, PluginId } from '@zhin.js/plugin-runtime';
1
+ import type { CapabilityId, PluginId, RuntimeSnapshot } from '@zhin.js/plugin-runtime';
2
+ import type { MediaRef, Segment } from '../../built/segment-contract/types.js';
3
+ export type { MediaRef, Segment };
2
4
  declare const componentCallBrand: "zhin.component-call/1";
3
5
  declare const rawContentBrand: "zhin.raw-content/1";
4
6
  export interface ComponentCall<TProps = unknown> {
@@ -18,7 +20,18 @@ export declare function isRawContent(value: SendContent): value is RawContent;
18
20
  export interface IncomingMessage {
19
21
  readonly adapter: CapabilityId;
20
22
  readonly target: string;
23
+ /**
24
+ * 纯文本视图:与 `segments` 同源(adapter 从同一份入站载荷派生二者)。
25
+ * 触发判定与 Console 预览读取此字段;命令匹配在有 segments 时优先使用结构化视图。
26
+ */
21
27
  readonly content: string;
28
+ /**
29
+ * 结构化段视图(canonical Segment SSOT,见 built/segment-contract)。
30
+ * 与 `content` 同源:segments 承载纯文本无法表达的媒体(image/audio/video/file
31
+ * 的 MediaRef)、mention、reply 等信息。旧 adapter 未迁移时可缺省,
32
+ * 读取方必须容忍 undefined。
33
+ */
34
+ readonly segments?: readonly Segment[];
22
35
  readonly id?: string;
23
36
  readonly sender?: string;
24
37
  readonly metadata?: Readonly<Record<string, unknown>>;
@@ -48,6 +61,18 @@ export interface OutboundEnvelope {
48
61
  export interface MessageGateway {
49
62
  receive(input: IncomingMessage): Promise<MessageDispatchResult>;
50
63
  send(request: SendRequest): Promise<unknown>;
64
+ /**
65
+ * 注册 interactive action 回跳 handler(prefix 最长匹配;返回注销函数)。
66
+ * 平台 callback 的 action 段、'text' 端点的数字回跳与指令预填直出
67
+ * payload 都会路由到这里。
68
+ */
69
+ registerInteractiveHandler(prefix: string, handler: (message: Message) => Promise<boolean> | boolean): () => void;
70
+ /**
71
+ * Command miss(或非前缀文本)后的回退处理:Host AI 对话、单文件 bot 用。
72
+ * 返回 true 表示已处理(回复已发送);后注册者覆盖前者。
73
+ * `requester` 是消息所属 Adapter Endpoint 的 owner(用于 CapabilityIngress 继承)。
74
+ */
75
+ setUnmatchedHandler(handler: (message: Message, snapshot: RuntimeSnapshot, requester: PluginId) => Promise<boolean>): void;
51
76
  }
52
77
  export interface MessageDispatchResult {
53
78
  readonly matched: boolean;
@@ -63,9 +88,18 @@ export declare class Message {
63
88
  readonly id?: string | undefined;
64
89
  readonly sender?: string | undefined;
65
90
  readonly metadata: Readonly<Record<string, unknown>>;
66
- constructor(adapter: CapabilityId, target: string, content: string, generation: number, reply: (content: SendContent, requester?: PluginId) => Promise<unknown>, id?: string | undefined, sender?: string | undefined, metadata?: Readonly<Record<string, unknown>>);
91
+ /**
92
+ * 结构化段视图(与 `content` 纯文本视图同源,见 IncomingMessage.segments)。
93
+ * Command dispatcher 优先使用此字段,以支持 mention、image 等结构化参数。
94
+ */
95
+ readonly segments?: readonly Segment[] | undefined;
96
+ constructor(adapter: CapabilityId, target: string, content: string, generation: number, reply: (content: SendContent, requester?: PluginId) => Promise<unknown>, id?: string | undefined, sender?: string | undefined, metadata?: Readonly<Record<string, unknown>>,
97
+ /**
98
+ * 结构化段视图(与 `content` 纯文本视图同源,见 IncomingMessage.segments)。
99
+ * Command dispatcher 优先使用此字段,以支持 mention、image 等结构化参数。
100
+ */
101
+ segments?: readonly Segment[] | undefined);
67
102
  readonly $reply: (content: SendContent) => Promise<unknown>;
68
103
  readonly $replyFrom: (requester: PluginId, content: SendContent) => Promise<unknown>;
69
104
  }
70
105
  export declare function createOutboundEnvelope(request: Omit<OutboundEnvelope, 'payload' | 'replace'>, initialPayload: unknown): OutboundEnvelope;
71
- export {};
@@ -30,7 +30,13 @@ export class Message {
30
30
  id;
31
31
  sender;
32
32
  metadata;
33
- constructor(adapter, target, content, generation, reply, id, sender, metadata = Object.freeze({})) {
33
+ segments;
34
+ constructor(adapter, target, content, generation, reply, id, sender, metadata = Object.freeze({}),
35
+ /**
36
+ * 结构化段视图(与 `content` 纯文本视图同源,见 IncomingMessage.segments)。
37
+ * Command dispatcher 优先使用此字段,以支持 mention、image 等结构化参数。
38
+ */
39
+ segments) {
34
40
  this.adapter = adapter;
35
41
  this.target = target;
36
42
  this.content = content;
@@ -38,6 +44,7 @@ export class Message {
38
44
  this.id = id;
39
45
  this.sender = sender;
40
46
  this.metadata = metadata;
47
+ this.segments = segments;
41
48
  this.$reply = (content) => reply(content);
42
49
  this.$replyFrom = (requester, content) => reply(content, requester);
43
50
  Object.freeze(this);
@@ -1,8 +1,31 @@
1
- import { Scope, type PluginId, type RuntimeSnapshot, type SnapshotStore } from '@zhin.js/plugin-runtime';
1
+ import { Scope, type CapabilityId, type PluginId, type RuntimeSnapshot, type SnapshotStore } from '@zhin.js/plugin-runtime';
2
+ import { type EndpointManagement, type EndpointManagementCapability } from '@zhin.js/adapter';
2
3
  import { Message, type ChannelParent, type IncomingMessage, type MessageDispatchResult, type MessageGateway, type SendRequest } from './contracts.js';
3
4
  import { OutboundRenderer } from './outbound-renderer.js';
5
+ import { type RuntimeInteractiveHandler } from './interactive.js';
4
6
  export declare const messageGatewayToken: import("@zhin.js/plugin-runtime").Token<MessageGateway>;
7
+ /** Console 实时消息事件(SSE 推送源;content 仅为截断预览,不含完整原始段)。 */
8
+ export interface RuntimeMessageEvent {
9
+ readonly direction: 'inbound' | 'outbound';
10
+ readonly adapter: CapabilityId;
11
+ readonly target: string;
12
+ /** inbound:发送者 id。 */
13
+ readonly sender?: string;
14
+ /** outbound:发起方插件。 */
15
+ readonly requester?: PluginId;
16
+ /** inbound:从 target 前缀解析的场景(`group:xx` → `group`)。 */
17
+ readonly channelType?: string;
18
+ /** 预览文本,截断至 200 字。 */
19
+ readonly contentPreview: string;
20
+ readonly messageId?: string;
21
+ readonly timestamp: number;
22
+ }
23
+ export declare const messagePreviewLimit = 200;
5
24
  export interface ImRuntimeOptions {
25
+ /**
26
+ * 全局静态命令前缀(如 `'/'`)。缺省时按适配器实例 config 的
27
+ * `commandPrefix` 解析(`endpoints[i]` 可逐项覆盖),默认 `''` 无前缀。
28
+ */
6
29
  readonly commandPrefix?: string;
7
30
  readonly renderer?: OutboundRenderer;
8
31
  }
@@ -17,15 +40,27 @@ export declare class ImRuntime implements MessageGateway {
17
40
  */
18
41
  setUnmatchedHandler(handler: (message: Message, snapshot: RuntimeSnapshot, requester: PluginId) => Promise<boolean>): void;
19
42
  install(resources: Scope): void;
43
+ /**
44
+ * 注册 interactive action 回跳 handler(prefix 最长匹配;返回注销函数)。
45
+ * 在 Command dispatch 之前路由:action 段 / 数字回跳 / 指令预填 payload。
46
+ */
47
+ registerInteractiveHandler(prefix: string, handler: RuntimeInteractiveHandler): () => void;
48
+ /**
49
+ * 订阅消息事件(入站 dispatch 完成后 / 出站发送成功后回调)。
50
+ * 返回注销函数。listener 抛错不会阻断消息链路。
51
+ */
52
+ onMessage(listener: (event: RuntimeMessageEvent) => void): () => void;
20
53
  receive(input: IncomingMessage): Promise<MessageDispatchResult>;
21
54
  send(request: SendRequest): Promise<unknown>;
22
55
  /** Console `endpoint.list` — empty until Adapter Feature projection is ready. */
23
56
  listEndpoints(): readonly {
24
57
  readonly name: string;
25
58
  readonly adapter: string;
59
+ readonly owner: string;
26
60
  readonly connected: boolean;
27
61
  readonly status: 'online' | 'offline';
28
62
  readonly phase: 'pending' | 'starting' | 'online' | 'failed' | 'unconfigured';
63
+ readonly managementCapabilities: readonly EndpointManagementCapability[];
29
64
  }[];
30
65
  getEndpoint(adapter: string, endpointId: string): {
31
66
  readonly name: string;
@@ -33,6 +68,7 @@ export declare class ImRuntime implements MessageGateway {
33
68
  readonly connected: boolean;
34
69
  readonly status: 'online' | 'offline';
35
70
  readonly phase: 'pending' | 'starting' | 'online' | 'failed' | 'unconfigured';
71
+ readonly managementCapabilities: readonly EndpointManagementCapability[];
36
72
  } | null;
37
73
  sendEndpointMessage(input: {
38
74
  readonly adapter: string;
@@ -65,6 +101,15 @@ export declare class ImRuntime implements MessageGateway {
65
101
  readonly endpointId: string;
66
102
  readonly messageId: string;
67
103
  }): Promise<void>;
68
- /** Console endpoint 社交/群管 RPC:解析 live Endpoint 实例(无则 null)。 */
104
+ /**
105
+ * Console endpoint 社交/群管 RPC:解析 live Endpoint 实例(无则 null)。
106
+ * @deprecated Host callers should use `getEndpointManagement()`.
107
+ */
69
108
  getLiveEndpoint(adapter: string, endpointId: string): unknown | null;
109
+ /**
110
+ * Narrow Host seam for Console social/group management. An empty object means
111
+ * the Endpoint exists but implements no management operations; null means it
112
+ * cannot be resolved.
113
+ */
114
+ getEndpointManagement(adapter: string, endpointId: string): EndpointManagement | null;
70
115
  }