@zhin.js/core 1.5.11 → 1.5.13

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 +2 -2
  2. package/lib/adapter.d.ts +3 -0
  3. package/lib/built/ai-trigger.d.ts +0 -2
  4. package/lib/built/ai-trigger.js +0 -1
  5. package/lib/built/inbound-pipeline.js +0 -1
  6. package/lib/built/inbound-runner.js +0 -2
  7. package/lib/built/login-assist.d.ts +22 -4
  8. package/lib/built/login-assist.js +71 -7
  9. package/lib/built/message-enrich.d.ts +0 -1
  10. package/lib/built/message-enrich.js +0 -1
  11. package/lib/built/rich-segments/markdown-to-text.js +2 -2
  12. package/lib/built/schema-endpoint-manager.js +6 -6
  13. package/lib/built/segment-contract/types.d.ts +4 -45
  14. package/lib/built/segment-contract/types.js +0 -4
  15. package/lib/built/user-interaction.d.ts +30 -0
  16. package/lib/built/user-interaction.js +248 -0
  17. package/lib/feature/handler.d.ts +1 -1
  18. package/lib/feature/handler.js +1 -1
  19. package/lib/im-scene.js +0 -2
  20. package/lib/index.d.ts +4 -3
  21. package/lib/index.js +3 -3
  22. package/lib/message.d.ts +0 -5
  23. package/lib/message.js +0 -13
  24. package/lib/plugin-runtime/im/im-runtime.d.ts +30 -8
  25. package/lib/plugin-runtime/im/im-runtime.js +471 -145
  26. package/lib/plugin-runtime/im/index.d.ts +2 -0
  27. package/lib/plugin-runtime/im/index.js +2 -0
  28. package/lib/plugin-runtime/im/login-assist-host.d.ts +6 -0
  29. package/lib/plugin-runtime/im/login-assist-host.js +6 -0
  30. package/lib/plugin-runtime/im/message-dispatcher.d.ts +2 -2
  31. package/lib/plugin-runtime/im/message-dispatcher.js +2 -2
  32. package/lib/plugin-runtime/im/outbound-segments.d.ts +4 -0
  33. package/lib/plugin-runtime/im/outbound-segments.js +26 -0
  34. package/lib/plugin-runtime/im/side-event-gateway.d.ts +13 -0
  35. package/lib/plugin-runtime/im/side-event-gateway.js +2 -0
  36. package/lib/plugin.d.ts +1 -0
  37. package/lib/{prompt.d.ts → schema-interaction.d.ts} +6 -6
  38. package/lib/{prompt.js → schema-interaction.js} +12 -12
  39. package/lib/side-event/base.d.ts +7 -0
  40. package/lib/side-event/dispatch.d.ts +15 -0
  41. package/lib/side-event/dispatch.js +140 -0
  42. package/lib/side-event/index.d.ts +1 -0
  43. package/lib/side-event/index.js +1 -0
  44. package/lib/side-event/normalize.d.ts +2 -0
  45. package/lib/side-event/normalize.js +4 -0
  46. package/lib/side-event/types.d.ts +4 -2
  47. package/lib/side-event/types.js +1 -1
  48. package/lib/system-event.d.ts +15 -0
  49. package/lib/system-event.js +7 -0
  50. package/lib/types.d.ts +0 -15
  51. package/package.json +11 -10
  52. package/lib/built/prepend-quote-context.d.ts +0 -34
  53. package/lib/built/prepend-quote-context.js +0 -130
  54. package/lib/message-quote.d.ts +0 -10
  55. package/lib/message-quote.js +0 -90
@@ -0,0 +1,248 @@
1
+ /** Fail early for interaction definitions that could never produce an unambiguous answer. */
2
+ export function assertUserInteractionRequest(request) {
3
+ if (!request.title.trim())
4
+ throw new TypeError('User interaction title must not be empty');
5
+ if (request.timeout !== undefined && (!Number.isFinite(request.timeout) || request.timeout <= 0)) {
6
+ throw new RangeError('User interaction timeout must be a positive finite number');
7
+ }
8
+ if (request.type === 'text' && !validRange(request.minLength, request.maxLength)) {
9
+ throw new RangeError('User interaction text length range is invalid');
10
+ }
11
+ if (request.type === 'number' && !validBounds(request.min, request.max)) {
12
+ throw new RangeError('User interaction number range is invalid');
13
+ }
14
+ if (request.type === 'select' || request.type === 'multiselect') {
15
+ if (request.options.length === 0)
16
+ throw new TypeError('User interaction options must not be empty');
17
+ const labels = new Set();
18
+ for (const option of request.options) {
19
+ const label = option.label.trim().toLocaleLowerCase();
20
+ if (!label)
21
+ throw new TypeError('User interaction option label must not be empty');
22
+ if (labels.has(label))
23
+ throw new TypeError(`Duplicate user interaction option label: ${option.label}`);
24
+ labels.add(label);
25
+ }
26
+ }
27
+ if (request.type === 'multiselect') {
28
+ if (request.separator === '')
29
+ throw new TypeError('User interaction separator must not be empty');
30
+ if (!validRange(request.minSelections, request.maxSelections)) {
31
+ throw new RangeError('User interaction selection range is invalid');
32
+ }
33
+ }
34
+ if (request.type === 'list') {
35
+ if (request.separator === '')
36
+ throw new TypeError('User interaction separator must not be empty');
37
+ if (!validRange(request.minItems, request.maxItems)) {
38
+ throw new RangeError('User interaction list length range is invalid');
39
+ }
40
+ }
41
+ }
42
+ /** Project semantic content into canonical Markdown + keyboard segments. */
43
+ export function renderUserInteraction(view) {
44
+ const markdown = [
45
+ `### ${view.title}`,
46
+ view.description?.trim(),
47
+ view.tip?.trim() ? quoteTip(view.tip.trim()) : undefined,
48
+ ].filter((part) => !!part).join('\n\n');
49
+ const actions = view.actions ?? [];
50
+ if (actions.length === 0) {
51
+ return [{ type: 'markdown', data: { content: markdown } }];
52
+ }
53
+ const buttons = actions.map((action, index) => ({
54
+ id: `interaction-${index + 1}`,
55
+ label: action.label,
56
+ payload: action.value,
57
+ ...(action.style ? { style: action.style } : {}),
58
+ mode: 'command',
59
+ command: { enter: true, reply: false },
60
+ }));
61
+ return [
62
+ { type: 'markdown', data: { content: markdown } },
63
+ {
64
+ type: 'keyboard',
65
+ data: {
66
+ rows: chunk(buttons, 5),
67
+ fallback: {
68
+ hint: '也可以直接回复对应内容。',
69
+ map: Object.fromEntries(actions.map((action, index) => [String(index + 1), action.value])),
70
+ },
71
+ },
72
+ },
73
+ ];
74
+ }
75
+ /** Derive presentation and controls from one discriminated request. */
76
+ export function projectUserInteraction(request, progress) {
77
+ const instruction = instructionFor(request);
78
+ const optionList = request.type === 'select' || request.type === 'multiselect'
79
+ ? renderOptions(request.options)
80
+ : undefined;
81
+ const optionsHaveDescriptions = request.type === 'select' || request.type === 'multiselect'
82
+ ? request.options.some((option) => !!option.description)
83
+ : false;
84
+ const actions = actionsFor(request);
85
+ const stepDescription = [
86
+ progress ? `**${progress.index}/${progress.total} · ${request.title}**` : undefined,
87
+ request.description,
88
+ optionList && (!actions || optionsHaveDescriptions) ? optionList : undefined,
89
+ ].filter((part) => !!part).join('\n\n');
90
+ return Object.freeze({
91
+ title: progress?.title ?? request.title,
92
+ description: progress
93
+ ? [progress.description, stepDescription].filter(Boolean).join('\n\n')
94
+ : stepDescription || undefined,
95
+ tip: [request.tip, instruction, progress?.tip].filter(Boolean).join('\n'),
96
+ ...(actions ? { actions: Object.freeze(actions) } : {}),
97
+ });
98
+ }
99
+ /** Parse and validate one user reply without transport knowledge. */
100
+ export function parseUserInteractionAnswer(request, rawInput) {
101
+ const raw = rawInput.trim();
102
+ switch (request.type) {
103
+ case 'text': {
104
+ if (raw.length < (request.minLength ?? 0))
105
+ return invalid(`请至少输入 ${request.minLength} 个字符`);
106
+ if (request.maxLength !== undefined && raw.length > request.maxLength) {
107
+ return invalid(`请不要超过 ${request.maxLength} 个字符`);
108
+ }
109
+ if (request.pattern) {
110
+ request.pattern.lastIndex = 0;
111
+ if (!request.pattern.test(raw))
112
+ return invalid('输入格式不正确');
113
+ }
114
+ return valid(raw);
115
+ }
116
+ case 'number': {
117
+ const value = Number(raw);
118
+ if (!raw || !Number.isFinite(value))
119
+ return invalid('请输入有效数字');
120
+ if (request.integer && !Number.isInteger(value))
121
+ return invalid('请输入整数');
122
+ if (request.min !== undefined && value < request.min)
123
+ return invalid(`请输入不小于 ${request.min} 的数字`);
124
+ if (request.max !== undefined && value > request.max)
125
+ return invalid(`请输入不大于 ${request.max} 的数字`);
126
+ return valid(value);
127
+ }
128
+ case 'confirm': {
129
+ const normalized = raw.toLocaleLowerCase();
130
+ const confirmLabel = request.confirmLabel?.trim().toLocaleLowerCase();
131
+ const cancelLabel = request.cancelLabel?.trim().toLocaleLowerCase();
132
+ if (CONFIRM_VALUES.has(normalized) || (confirmLabel && normalized === confirmLabel))
133
+ return valid(true);
134
+ if (CANCEL_VALUES.has(normalized) || (cancelLabel && normalized === cancelLabel))
135
+ return valid(false);
136
+ return invalid('请选择确认或取消');
137
+ }
138
+ case 'select': {
139
+ const option = resolveOption(request.options, raw);
140
+ return option ? valid(option.value) : invalid('请选择一个有效选项');
141
+ }
142
+ case 'multiselect': {
143
+ const separator = request.separator ?? ',';
144
+ const tokens = raw.split(separator).map((token) => token.trim()).filter(Boolean);
145
+ const selected = uniqueOptions(tokens.map((token) => resolveOption(request.options, token)));
146
+ if (selected.length !== tokens.length)
147
+ return invalid('多选中包含无效选项');
148
+ if (selected.length < (request.minSelections ?? 0))
149
+ return invalid(`请至少选择 ${request.minSelections} 项`);
150
+ if (request.maxSelections !== undefined && selected.length > request.maxSelections) {
151
+ return invalid(`请最多选择 ${request.maxSelections} 项`);
152
+ }
153
+ return valid(Object.freeze(selected.map((option) => option.value)));
154
+ }
155
+ case 'list': {
156
+ const separator = request.separator ?? ',';
157
+ const tokens = raw.split(separator).map((token) => token.trim()).filter(Boolean);
158
+ if (tokens.length < (request.minItems ?? 0))
159
+ return invalid(`请至少输入 ${request.minItems} 项`);
160
+ if (request.maxItems !== undefined && tokens.length > request.maxItems)
161
+ return invalid(`请最多输入 ${request.maxItems} 项`);
162
+ if (request.valueType === 'number') {
163
+ const values = tokens.map(Number);
164
+ return values.every(Number.isFinite) ? valid(Object.freeze(values)) : invalid('列表中包含无效数字');
165
+ }
166
+ if (request.valueType === 'boolean') {
167
+ const values = tokens.map(parseBoolean);
168
+ return values.every((value) => value !== undefined)
169
+ ? valid(Object.freeze(values))
170
+ : invalid('布尔列表只接受 true/false、yes/no 或 是/否');
171
+ }
172
+ return valid(Object.freeze(tokens));
173
+ }
174
+ }
175
+ }
176
+ function actionsFor(request) {
177
+ if (request.type === 'confirm') {
178
+ return [
179
+ { label: request.confirmLabel ?? '确认', value: 'yes', style: 'primary' },
180
+ { label: request.cancelLabel ?? '取消', value: 'no', style: 'danger' },
181
+ ];
182
+ }
183
+ if (request.type === 'select' && request.options.length <= 10) {
184
+ return request.options.map((option, index) => ({ label: option.label, value: String(index + 1), style: 'secondary' }));
185
+ }
186
+ return undefined;
187
+ }
188
+ function instructionFor(request) {
189
+ switch (request.type) {
190
+ case 'confirm': return '请选择确认或取消。';
191
+ case 'select': return '可以点击按钮,或回复序号/选项名称。';
192
+ case 'multiselect': return `回复序号或选项名称,多项用“${request.separator ?? ','}”分隔。`;
193
+ case 'list': return `多项用“${request.separator ?? ','}”分隔。`;
194
+ default: return undefined;
195
+ }
196
+ }
197
+ function renderOptions(options) {
198
+ return options.map((option, index) => `${index + 1}. ${option.label}${option.description ? ` — ${option.description}` : ''}`).join('\n');
199
+ }
200
+ function resolveOption(options, raw) {
201
+ const index = Number(raw);
202
+ if (Number.isInteger(index) && index >= 1 && index <= options.length)
203
+ return options[index - 1];
204
+ const normalized = raw.toLocaleLowerCase();
205
+ return options.find((option) => option.label.trim().toLocaleLowerCase() === normalized);
206
+ }
207
+ function uniqueOptions(options) {
208
+ const result = [];
209
+ for (const option of options)
210
+ if (option && !result.includes(option))
211
+ result.push(option);
212
+ return result;
213
+ }
214
+ function parseBoolean(raw) {
215
+ const normalized = raw.toLocaleLowerCase();
216
+ if (CONFIRM_VALUES.has(normalized))
217
+ return true;
218
+ if (CANCEL_VALUES.has(normalized))
219
+ return false;
220
+ return undefined;
221
+ }
222
+ function quoteTip(tip) {
223
+ return tip.split('\n').map((line) => `> 💡 ${line}`).join('\n');
224
+ }
225
+ function valid(value) {
226
+ return Object.freeze({ ok: true, value });
227
+ }
228
+ function invalid(message) {
229
+ return Object.freeze({ ok: false, message });
230
+ }
231
+ function validRange(minimum, maximum) {
232
+ return (minimum === undefined || (Number.isFinite(minimum) && minimum >= 0))
233
+ && (maximum === undefined || (Number.isFinite(maximum) && maximum >= 0))
234
+ && (minimum === undefined || maximum === undefined || minimum <= maximum);
235
+ }
236
+ function validBounds(minimum, maximum) {
237
+ return (minimum === undefined || Number.isFinite(minimum))
238
+ && (maximum === undefined || Number.isFinite(maximum))
239
+ && (minimum === undefined || maximum === undefined || minimum <= maximum);
240
+ }
241
+ const CONFIRM_VALUES = new Set(['1', 'y', 'yes', 'true', '是', '确认', '同意']);
242
+ const CANCEL_VALUES = new Set(['2', 'n', 'no', 'false', '否', '取消', '拒绝']);
243
+ function chunk(items, size) {
244
+ const rows = [];
245
+ for (let index = 0; index < items.length; index += size)
246
+ rows.push(items.slice(index, index + size));
247
+ return rows;
248
+ }
@@ -1,5 +1,5 @@
1
1
  /** Authoring API for Handler Feature — implementation in `@zhin.js/handler`. */
2
- export { defineHandler, parseHandlerDefinition, HandlerIndex, isHandlerIndex, handlerFeatureId, handlerFeature, type HandlerEventMap, type HandlerDefinition, type HandlerDescriptor, } from '@zhin.js/handler';
2
+ export { defineHandler, parseHandlerDefinition, HandlerIndex, isHandlerIndex, handlerFeatureId, handlerFeature, handlerEventFromLocalName, resolveHandlerEvent, type HandlerEventMap, type HandlerDefinition, type HandlerDescriptor, type HandlerContext, type HandlerDispatchOptions, } from '@zhin.js/handler';
3
3
  import type { Plugin } from '../plugin.js';
4
4
  type KnownKeys<T> = {
5
5
  [K in keyof T as string extends K ? never : number extends K ? never : K]: T[K];
@@ -1,2 +1,2 @@
1
1
  /** Authoring API for Handler Feature — implementation in `@zhin.js/handler`. */
2
- export { defineHandler, parseHandlerDefinition, HandlerIndex, isHandlerIndex, handlerFeatureId, handlerFeature, } from '@zhin.js/handler';
2
+ export { defineHandler, parseHandlerDefinition, HandlerIndex, isHandlerIndex, handlerFeatureId, handlerFeature, handlerEventFromLocalName, resolveHandlerEvent, } from '@zhin.js/handler';
package/lib/im-scene.js CHANGED
@@ -68,10 +68,8 @@ export function messageToIMDeliveryTarget(message) {
68
68
  const scene = sceneRefFromMessage(message);
69
69
  if (!scene)
70
70
  return undefined;
71
- const quoteId = nonEmptyString(message.$quote_id);
72
71
  return {
73
72
  channel: 'im',
74
73
  scene,
75
- ...(quoteId ? { quoteId } : {}),
76
74
  };
77
75
  }
package/lib/index.d.ts CHANGED
@@ -8,8 +8,10 @@ export * from './message.js';
8
8
  export * from './im-scene.js';
9
9
  export * from './notice.js';
10
10
  export * from './request.js';
11
+ export * from './system-event.js';
11
12
  export * from './side-event/index.js';
12
- export * from './prompt.js';
13
+ export * from './schema-interaction.js';
14
+ export type * from '@zhin.js/interaction';
13
15
  export * from './types.js';
14
16
  export * from './agent-prompt.js';
15
17
  export * from './utils.js';
@@ -43,6 +45,7 @@ export * from './built/login-assist.js';
43
45
  export * from './built/generated-qrcode.js';
44
46
  export * from './built/rich-segments/index.js';
45
47
  export * from './built/interactive-segments/index.js';
48
+ export * from './built/user-interaction.js';
46
49
  export * from './built/ai-outbound/index.js';
47
50
  export { loadHtmlRenderer, seedHtmlRenderer, HTML_RENDERER_PACKAGE } from './built/html-renderer-loader.js';
48
51
  export { loadSpeechPipeline, seedSpeechPipeline, SPEECH_PACKAGE } from './built/speech-loader.js';
@@ -69,8 +72,6 @@ export * from './built/introspection-format.js';
69
72
  export * from './built/management-command-guard.js';
70
73
  export * from './built/html-to-text.js';
71
74
  export * from './built/html-segment-fallback.js';
72
- export * from './built/prepend-quote-context.js';
73
- export * from './message-quote.js';
74
75
  export * from '@zhin.js/database';
75
76
  export * from '@zhin.js/logger';
76
77
  export { Schema } from '@zhin.js/schema';
package/lib/index.js CHANGED
@@ -9,8 +9,9 @@ export * from './message.js';
9
9
  export * from './im-scene.js';
10
10
  export * from './notice.js';
11
11
  export * from './request.js';
12
+ export * from './system-event.js';
12
13
  export * from './side-event/index.js';
13
- export * from './prompt.js';
14
+ export * from './schema-interaction.js';
14
15
  export * from './types.js';
15
16
  export * from './agent-prompt.js';
16
17
  export * from './utils.js';
@@ -44,6 +45,7 @@ export * from './built/login-assist.js';
44
45
  export * from './built/generated-qrcode.js';
45
46
  export * from './built/rich-segments/index.js';
46
47
  export * from './built/interactive-segments/index.js';
48
+ export * from './built/user-interaction.js';
47
49
  export * from './built/ai-outbound/index.js';
48
50
  export { loadHtmlRenderer, seedHtmlRenderer, HTML_RENDERER_PACKAGE } from './built/html-renderer-loader.js';
49
51
  export { loadSpeechPipeline, seedSpeechPipeline, SPEECH_PACKAGE } from './built/speech-loader.js';
@@ -65,8 +67,6 @@ export * from './built/introspection-format.js';
65
67
  export * from './built/management-command-guard.js';
66
68
  export * from './built/html-to-text.js';
67
69
  export * from './built/html-segment-fallback.js';
68
- export * from './built/prepend-quote-context.js';
69
- export * from './message-quote.js';
70
70
  // ── 外部库 re-export ──────────────────────────────────────────────────
71
71
  export * from '@zhin.js/database';
72
72
  export * from '@zhin.js/logger';
package/lib/message.d.ts CHANGED
@@ -44,8 +44,6 @@ export interface MessageBase {
44
44
  $channel: MessageChannel;
45
45
  $timestamp: number;
46
46
  $raw: string;
47
- /** 本条消息引用的上游 message_id(平台原样字符串) */
48
- $quote_id?: string;
49
47
  }
50
48
  /**
51
49
  * 完整消息类型,支持扩展
@@ -56,9 +54,6 @@ export declare namespace Message {
56
54
  * 工具方法:合并自定义字段与基础消息结构
57
55
  */
58
56
  function from<T extends object>(input: T, format: MessageBase): Message<T>;
59
- function quoteIdFromContent(content: MessageElement[]): string | undefined;
60
- function syncQuoteId(message: Message<any>): void;
61
- function alignReplySegments(content: MessageElement[], quoteId?: string): void;
62
57
  function actionPayload(message: Message<any>): string | undefined;
63
58
  function isAction(message: Message<any>): boolean;
64
59
  }
package/lib/message.js CHANGED
@@ -1,4 +1,3 @@
1
- import { alignReplySegments as alignReplySegmentsImpl, quoteIdFromContent as quoteIdFromContentImpl, syncQuoteId as syncQuoteIdImpl, } from "./message-quote.js";
2
1
  import { isActionMessage as isActionMessageImpl } from "./built/interactive-segments/action.js";
3
2
  export var Message;
4
3
  (function (Message) {
@@ -9,18 +8,6 @@ export var Message;
9
8
  return Object.assign({}, input, format);
10
9
  }
11
10
  Message.from = from;
12
- function quoteIdFromContent(content) {
13
- return quoteIdFromContentImpl(content);
14
- }
15
- Message.quoteIdFromContent = quoteIdFromContent;
16
- function syncQuoteId(message) {
17
- syncQuoteIdImpl(message);
18
- }
19
- Message.syncQuoteId = syncQuoteId;
20
- function alignReplySegments(content, quoteId) {
21
- alignReplySegmentsImpl(content, quoteId);
22
- }
23
- Message.alignReplySegments = alignReplySegments;
24
11
  function actionPayload(message) {
25
12
  for (const item of message.$content ?? []) {
26
13
  if (typeof item === 'string')
@@ -1,15 +1,20 @@
1
1
  import { Scope, generationAdmissionBinder, type GenerationAdmissionGate, type PluginId, type RuntimeSnapshot, type SnapshotLease, type SnapshotReader } from '@zhin.js/plugin-runtime';
2
2
  import { MessageBus } from './message-bus.js';
3
- import { type EndpointManagement, type EndpointManagementCapability, type AdapterEndpointPhase } from '@zhin.js/adapter';
4
- import { type ConversationRef, type DeliveryReceipt, type MessageRef } from '@zhin.js/im-contract';
3
+ import { type AdapterOperation, type EndpointManagement, type EndpointManagementCapability, type AdapterEndpointPhase, type EndpointContentResolveContext } from '@zhin.js/adapter';
4
+ import { type ConversationEventStore, type ConversationContextBlock, type ConversationReference, type ConversationResolution, type ConversationRef, type DeliveryReceipt, type MessageRef } from '@zhin.js/im-contract';
5
5
  import { Message, type ConversationAddress, type IncomingMessage, type MessageDispatchResult, type MessageGateway, type MessageSenderRef, type SendRequest } from './contracts.js';
6
- import type { CommandPrompt } from '@zhin.js/command';
6
+ import { LoginAssist } from '../../built/login-assist.js';
7
+ import type { Notice } from '../../notice.js';
8
+ import type { Request } from '../../request.js';
9
+ import type { SystemEvent } from '../../system-event.js';
10
+ import type { UserInteraction } from '@zhin.js/interaction';
7
11
  import { OutboundRenderer } from './outbound-renderer.js';
8
12
  import { type RuntimeInteractiveHandler } from './interactive.js';
9
13
  export declare const messageGatewayToken: import("@zhin.js/plugin-runtime").Token<MessageGateway>;
10
- /** Generation-owned terminal route after interactive/command routing misses. */
14
+ /** Generation-owned ingress hooks before ordinary dispatch and after it misses. */
11
15
  export interface IngressRoute {
12
- route(message: Message, lease: SnapshotLease, requester: PluginId): Promise<boolean>;
16
+ preRoute?(message: Message, lease: SnapshotLease, requester: PluginId, conversationSequence: number | undefined): Promise<boolean>;
17
+ route(message: Message, lease: SnapshotLease, requester: PluginId, conversationSequence: number | undefined): Promise<boolean>;
13
18
  }
14
19
  export declare const ingressRouteToken: import("@zhin.js/plugin-runtime").Token<IngressRoute>;
15
20
  /** Console 实时消息事件(SSE 推送源;content 仅为截断预览,不含完整原始段)。 */
@@ -33,6 +38,7 @@ export interface ImRuntimeOptions {
33
38
  */
34
39
  readonly commandPrefix?: string;
35
40
  readonly renderer?: OutboundRenderer;
41
+ readonly conversationEvents?: ConversationEventStore;
36
42
  /** Process-root ingress claim (pending interaction, authentication challenge, etc.). */
37
43
  readonly inboundClaim?: (message: Message) => boolean | Promise<boolean>;
38
44
  /**
@@ -45,10 +51,20 @@ export interface ImRuntimeOptions {
45
51
  }
46
52
  export declare class ImRuntime implements MessageGateway {
47
53
  #private;
54
+ conversationEvents: ConversationEventStore;
48
55
  constructor(options?: ImRuntimeOptions);
56
+ /** Process composition replaces the bootstrap memory store after required DB activation. */
57
+ replaceConversationEventStore(store: ConversationEventStore): void;
58
+ resolveConversationReference(lease: SnapshotLease, reference: ConversationReference, context: EndpointContentResolveContext): Promise<ConversationResolution>;
59
+ readConversationContext(conversation: ConversationRef, consumer: string, throughSequence: number, limit?: number, excludeMessageId?: string): Promise<Readonly<{
60
+ blocks: readonly ConversationContextBlock[];
61
+ cursor: number;
62
+ }>>;
63
+ commitConversationContext(conversation: ConversationRef, consumer: string, cursor: number): Promise<void>;
49
64
  attach(snapshots: SnapshotReader): void;
50
65
  readonly permissionHost: import("@zhin.js/permission").PermissionHost;
51
66
  readonly messageBus: MessageBus;
67
+ readonly loginAssist: LoginAssist;
52
68
  install(resources: Scope): void;
53
69
  [generationAdmissionBinder](gate: GenerationAdmissionGate): MessageGateway;
54
70
  /**
@@ -57,12 +73,12 @@ export declare class ImRuntime implements MessageGateway {
57
73
  */
58
74
  registerInteractiveHandler(prefix: string, handler: RuntimeInteractiveHandler): () => void;
59
75
  /**
60
- * Bound Prompt for this conversation.
76
+ * User interaction bound to this conversation.
61
77
  * `bind.subjectId` waits for that user (e.g. master) instead of the message sender.
62
78
  */
63
- createPrompt(message: Message, bind?: {
79
+ createInteraction(message: Message, bind?: {
64
80
  readonly subjectId: string;
65
- }): CommandPrompt | undefined;
81
+ }): UserInteraction | undefined;
66
82
  /**
67
83
  * 订阅消息事件(入站 dispatch 完成后 / 出站发送成功后回调)。
68
84
  * 返回注销函数。listener 抛错不会阻断消息链路。
@@ -70,14 +86,19 @@ export declare class ImRuntime implements MessageGateway {
70
86
  onMessage(listener: (event: RuntimeMessageEvent) => void): () => void;
71
87
  receive(input: IncomingMessage): Promise<MessageDispatchResult>;
72
88
  send(request: SendRequest): Promise<DeliveryReceipt>;
89
+ receiveNotice(notice: Notice): Promise<void>;
90
+ receiveRequest(request: Request): Promise<void>;
91
+ receiveSystem(event: SystemEvent): Promise<void>;
73
92
  /** Console `endpoint.list` — empty until Adapter Feature projection is ready. */
74
93
  listEndpoints(): readonly {
94
+ readonly id: string;
75
95
  readonly name: string;
76
96
  readonly adapter: string;
77
97
  readonly owner: string;
78
98
  readonly connected: boolean;
79
99
  readonly status: 'online' | 'offline';
80
100
  readonly phase: AdapterEndpointPhase;
101
+ readonly operations: readonly AdapterOperation[];
81
102
  readonly managementCapabilities: readonly EndpointManagementCapability[];
82
103
  }[];
83
104
  /**
@@ -97,6 +118,7 @@ export declare class ImRuntime implements MessageGateway {
97
118
  readonly connected: boolean;
98
119
  readonly status: 'online' | 'offline';
99
120
  readonly phase: AdapterEndpointPhase;
121
+ readonly operations: readonly AdapterOperation[];
100
122
  readonly managementCapabilities: readonly EndpointManagementCapability[];
101
123
  } | null;
102
124
  sendEndpointMessage(input: {