@zhin.js/core 1.5.11 → 1.5.12

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 +28 -8
  25. package/lib/plugin-runtime/im/im-runtime.js +459 -136
  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
package/README.md CHANGED
@@ -198,7 +198,7 @@ Core **不包含** ZhinAgent 实现。IM 侧的 AI 对话、工具收集、执
198
198
  |------|----------|
199
199
  | Provider | `AIProvider` 接口、`createSdkProviderAdapter`(AI SDK 传输) |
200
200
  | Agent 原语 | `ModelRegistry`、`agentLoop` |
201
- | 会话 / 上下文 | `ContextRepository`、`ImTranscriptStore`、`ContextManager`、`ConversationMemory` |
201
+ | 会话 / 上下文 | `ConversationEventStore`(IM 事实)与 Agent 的 `ContextRepository`(模型会话) |
202
202
  | 压缩 / 限流 / 输出 | `compactSession`、`RateLimiter`、`parseOutput`、`CostTracker` |
203
203
 
204
204
  完整 Agent 能力与配置见 [`@zhin.js/agent`](../agent/README.md) 与 [AI 模块](https://zhin.js.org/advanced/ai)。
@@ -237,7 +237,7 @@ export { htmlToFallbackText, coerceHtmlSegmentsToText, registerHtmlSegmentFallba
237
237
  // AI 原语(来自 @zhin.js/ai,非 ZhinAgent)
238
238
  export {
239
239
  AIProvider, createSdkProviderAdapter, ModelRegistry,
240
- ContextRepository, ContextManager, ConversationMemory, compactSession, ...
240
+ ContextRepository, ContextManager, compactSession, ...
241
241
  } from '@zhin.js/ai'
242
242
  ```
243
243
 
package/lib/adapter.d.ts CHANGED
@@ -106,6 +106,9 @@ export declare abstract class Adapter<R extends Endpoint = Endpoint, const Caps
106
106
  }
107
107
  export interface Adapters {
108
108
  }
109
+ /** Per-adapter live Endpoint class map for HandlerContext.$endpoint() typing. */
110
+ export interface AdapterEndpoints {
111
+ }
109
112
  export declare namespace Adapter {
110
113
  type Factory<R extends Adapter = Adapter> = {
111
114
  new (plugin: Plugin, name: string, config: Adapter.EndpointConfig<Adapter.InferEndpoint<R>>[]): R;
@@ -37,8 +37,6 @@ export interface AITriggerConfig {
37
37
  masters?: string[];
38
38
  /** 全局 trusted(trigger 级) */
39
39
  trusted?: string[];
40
- /** 是否在 AI 入参前拉取 $quote_id 对应消息正文(默认 true) */
41
- resolveQuotedMessages?: boolean;
42
40
  }
43
41
  /**
44
42
  * AI 触发检查结果
@@ -26,7 +26,6 @@ export const DEFAULT_AI_TRIGGER_CONFIG = {
26
26
  errorTemplate: '❌ AI 处理失败: {error}',
27
27
  masters: [],
28
28
  trusted: [],
29
- resolveQuotedMessages: true,
30
29
  };
31
30
  // ============================================================================
32
31
  // 工具函数
@@ -58,7 +58,6 @@ export class InboundMessagePipeline {
58
58
  recv: `${message.$channel.type}(${message.$channel.id})`,
59
59
  endpoint: message.$endpoint,
60
60
  preview: truncatePreview(segment.raw(message.$content)),
61
- ...(message.$quote_id ? { quote_id: message.$quote_id } : {}),
62
61
  }));
63
62
  }
64
63
  }
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * Inbound runner — one ordered entry for IM message.receive processing.
3
3
  */
4
- import { Message } from '../message.js';
5
4
  function getDispatcher(plugin) {
6
5
  const dispatcher = plugin?.inject('dispatcher');
7
6
  if (dispatcher && typeof dispatcher.dispatch === 'function') {
@@ -19,7 +18,6 @@ function getDispatcher(plugin) {
19
18
  */
20
19
  export async function runInboundMessage(options) {
21
20
  const { plugin, message, emitAdapterObservers } = options;
22
- Message.syncQuoteId(message);
23
21
  const dispatcher = getDispatcher(plugin);
24
22
  const root = plugin?.root;
25
23
  if (dispatcher && root) {
@@ -9,7 +9,6 @@
9
9
  * endpoint.login.pending — 有新待办时触发,payload: PendingLoginTask
10
10
  * endpoint.login.expired — 超时自动取消时触发,payload: PendingLoginTask
11
11
  */
12
- import type { Plugin } from '../plugin.js';
13
12
  export type LoginAssistType = 'qrcode' | 'sms' | 'slider' | 'device' | 'auth' | 'other';
14
13
  /** 默认 5 分钟;扫码/滑块长期挂起会泄漏 Promise 与 Map 条目 */
15
14
  export declare const DEFAULT_LOGIN_ASSIST_TIMEOUT_MS: number;
@@ -34,26 +33,40 @@ export interface PendingLoginTask {
34
33
  expiresAt?: number;
35
34
  }
36
35
  export interface WaitForInputOptions {
36
+ /** Endpoint-instance owner. Only this owner may cancel the task during rollback/retirement. */
37
+ owner: object;
37
38
  /**
38
39
  * 超时毫秒。默认 {@link DEFAULT_LOGIN_ASSIST_TIMEOUT_MS}。
39
40
  * 传 `0` / `Infinity` / 负数表示不超时(仅测试或显式长驻场景)。
40
41
  */
41
42
  timeoutMs?: number;
42
43
  }
44
+ export type LoginAssistEventName = 'endpoint.login.pending' | 'endpoint.login.resolved' | 'endpoint.login.expired';
45
+ /** Minimal bus — Plugin Runtime / classic Plugin / test EventEmitter all qualify. */
46
+ export interface LoginAssistBus {
47
+ emit(name: LoginAssistEventName, task: PendingLoginTask): void;
48
+ }
43
49
  export declare class LoginAssist {
44
- private readonly plugin;
50
+ #private;
51
+ private readonly bus;
45
52
  private readonly pending;
53
+ private readonly local;
46
54
  private idSeq;
47
55
  /** 构造时默认超时;可被 waitForInput options 覆盖 */
48
56
  private readonly defaultTimeoutMs;
49
- constructor(plugin: Plugin, options?: {
57
+ constructor(bus?: LoginAssistBus | null, options?: {
50
58
  defaultTimeoutMs?: number;
51
59
  });
60
+ /**
61
+ * Subscribe without requiring a Plugin EventEmitter.
62
+ * Returns unsubscribe.
63
+ */
64
+ subscribe(name: LoginAssistEventName, listener: (task: PendingLoginTask) => void): () => void;
52
65
  /**
53
66
  * 生产者:等待用户输入后 resolve。会发出 endpoint.login.pending 事件,未消费前可被 listPending 拉取(刷新后可继续消费)。
54
67
  * 超时后 reject 并 emit `endpoint.login.expired`。
55
68
  */
56
- waitForInput(adapter: string, endpointKey: string, type: LoginAssistType, payload?: PendingLoginTaskPayload, options?: WaitForInputOptions): Promise<string | Record<string, unknown>>;
69
+ waitForInput(adapter: string, endpointKey: string, type: LoginAssistType, payload: PendingLoginTaskPayload, options: WaitForInputOptions): Promise<string | Record<string, unknown>>;
57
70
  /**
58
71
  * 消费者:提交结果,对应 waitForInput 的 Promise 会 resolve。
59
72
  */
@@ -62,10 +75,15 @@ export declare class LoginAssist {
62
75
  * 消费者:取消某条待办,对应 Promise 会 reject。
63
76
  */
64
77
  cancel(id: string, reason?: string): boolean;
78
+ /**
79
+ * Cancel pending tasks created by one exact Endpoint instance.
80
+ */
81
+ cancelOwned(owner: object, reason?: string): number;
65
82
  /**
66
83
  * 列出未消费的待办(供 Web/CLI 展示,刷新后仍可拉取同一列表)。
67
84
  */
68
85
  listPending(): PendingLoginTask[];
86
+ hasPending(adapter?: string, endpointKey?: string): boolean;
69
87
  dispose(): void;
70
88
  }
71
89
  declare module '../plugin.js' {
@@ -15,20 +15,36 @@ export const DEFAULT_LOGIN_ASSIST_TIMEOUT_MS = 5 * 60 * 1000;
15
15
  // LoginAssist 服务
16
16
  // ============================================================================
17
17
  export class LoginAssist {
18
- plugin;
18
+ bus;
19
19
  pending = new Map();
20
+ local = new Map();
20
21
  idSeq = 0;
21
22
  /** 构造时默认超时;可被 waitForInput options 覆盖 */
22
23
  defaultTimeoutMs;
23
- constructor(plugin, options) {
24
- this.plugin = plugin;
24
+ constructor(bus, options) {
25
+ this.bus = bus ?? null;
25
26
  this.defaultTimeoutMs = options?.defaultTimeoutMs ?? DEFAULT_LOGIN_ASSIST_TIMEOUT_MS;
26
27
  }
28
+ /**
29
+ * Subscribe without requiring a Plugin EventEmitter.
30
+ * Returns unsubscribe.
31
+ */
32
+ subscribe(name, listener) {
33
+ let set = this.local.get(name);
34
+ if (!set) {
35
+ set = new Set();
36
+ this.local.set(name, set);
37
+ }
38
+ set.add(listener);
39
+ return () => {
40
+ set.delete(listener);
41
+ };
42
+ }
27
43
  /**
28
44
  * 生产者:等待用户输入后 resolve。会发出 endpoint.login.pending 事件,未消费前可被 listPending 拉取(刷新后可继续消费)。
29
45
  * 超时后 reject 并 emit `endpoint.login.expired`。
30
46
  */
31
- waitForInput(adapter, endpointKey, type, payload = {}, options) {
47
+ waitForInput(adapter, endpointKey, type, payload, options) {
32
48
  const id = `login-${Date.now()}-${++this.idSeq}`;
33
49
  const timeoutMs = resolveTimeoutMs(options?.timeoutMs, this.defaultTimeoutMs);
34
50
  const createdAt = Date.now();
@@ -42,14 +58,14 @@ export class LoginAssist {
42
58
  ...(timeoutMs != null ? { expiresAt: createdAt + timeoutMs } : {}),
43
59
  };
44
60
  const promise = new Promise((resolve, reject) => {
45
- const entry = { task, resolve, reject };
61
+ const entry = { task, owner: options.owner, resolve, reject };
46
62
  if (timeoutMs != null) {
47
63
  entry.timer = setTimeout(() => {
48
64
  if (!this.pending.has(id))
49
65
  return;
50
66
  this.pending.delete(id);
51
67
  try {
52
- this.plugin.emit('endpoint.login.expired', task);
68
+ this.#notify('endpoint.login.expired', task);
53
69
  }
54
70
  catch {
55
71
  /* observer failure must not leave the producer hanging forever */
@@ -60,7 +76,7 @@ export class LoginAssist {
60
76
  entry.timer.unref?.();
61
77
  }
62
78
  this.pending.set(id, entry);
63
- this.plugin.emit('endpoint.login.pending', task);
79
+ this.#notify('endpoint.login.pending', task);
64
80
  });
65
81
  return promise;
66
82
  }
@@ -75,6 +91,7 @@ export class LoginAssist {
75
91
  if (entry.timer)
76
92
  clearTimeout(entry.timer);
77
93
  entry.resolve(value);
94
+ this.#notify('endpoint.login.resolved', entry.task);
78
95
  return true;
79
96
  }
80
97
  /**
@@ -88,14 +105,44 @@ export class LoginAssist {
88
105
  if (entry.timer)
89
106
  clearTimeout(entry.timer);
90
107
  entry.reject(new Error(reason));
108
+ this.#notify('endpoint.login.resolved', entry.task);
91
109
  return true;
92
110
  }
111
+ /**
112
+ * Cancel pending tasks created by one exact Endpoint instance.
113
+ */
114
+ cancelOwned(owner, reason = 'superseded') {
115
+ let count = 0;
116
+ for (const [id, entry] of [...this.pending]) {
117
+ if (entry.owner !== owner)
118
+ continue;
119
+ this.pending.delete(id);
120
+ if (entry.timer)
121
+ clearTimeout(entry.timer);
122
+ entry.reject(new Error(reason));
123
+ this.#notify('endpoint.login.resolved', entry.task);
124
+ count += 1;
125
+ }
126
+ return count;
127
+ }
93
128
  /**
94
129
  * 列出未消费的待办(供 Web/CLI 展示,刷新后仍可拉取同一列表)。
95
130
  */
96
131
  listPending() {
97
132
  return [...this.pending.values()].map((e) => e.task);
98
133
  }
134
+ hasPending(adapter, endpointKey) {
135
+ if (adapter == null)
136
+ return this.pending.size > 0;
137
+ for (const entry of this.pending.values()) {
138
+ if (entry.task.adapter !== adapter)
139
+ continue;
140
+ if (endpointKey != null && entry.task.endpointKey !== endpointKey)
141
+ continue;
142
+ return true;
143
+ }
144
+ return false;
145
+ }
99
146
  dispose() {
100
147
  for (const [, entry] of this.pending) {
101
148
  if (entry.timer)
@@ -103,6 +150,23 @@ export class LoginAssist {
103
150
  entry.reject(new Error('LoginAssist disposed'));
104
151
  }
105
152
  this.pending.clear();
153
+ this.local.clear();
154
+ }
155
+ #notify(name, task) {
156
+ try {
157
+ this.bus?.emit(name, task);
158
+ }
159
+ catch {
160
+ /* bus observers must not break producer */
161
+ }
162
+ for (const listener of this.local.get(name) ?? []) {
163
+ try {
164
+ listener(task);
165
+ }
166
+ catch {
167
+ /* same */
168
+ }
169
+ }
106
170
  }
107
171
  }
108
172
  function resolveTimeoutMs(explicit, fallback) {
@@ -27,7 +27,6 @@ export interface SyntheticMessageInput {
27
27
  };
28
28
  channel: MessageChannel;
29
29
  id?: string;
30
- quote_id?: string;
31
30
  reply?: (content: SendContent, quote?: boolean | string) => Promise<string>;
32
31
  extra?: Record<string, unknown>;
33
32
  }
@@ -39,7 +39,6 @@ export function createSyntheticMessage(input) {
39
39
  $channel: input.channel,
40
40
  $timestamp: Date.now(),
41
41
  $raw: '',
42
- $quote_id: input.quote_id,
43
42
  });
44
43
  }
45
44
  /** hook context 中的 commMessage 字段(AIHookEvent.context) */
@@ -9,8 +9,8 @@ export function markdownToPlainText(md) {
9
9
  text = text.replace(/!\[[^\]]*\]\([^)]*\)/g, '');
10
10
  text = text.replace(/\[([^\]]+)\]\([^)]*\)/g, '$1');
11
11
  text = text.replace(/^#{1,6}\s+/gm, '');
12
- text = text.replace(/^\s*>\s?/gm, '');
13
- text = text.replace(/^\s*[-*+]\s+/gm, '');
12
+ text = text.replace(/^[ \t]*>[ \t]?/gm, '');
13
+ text = text.replace(/^[ \t]*[-*+][ \t]+/gm, '');
14
14
  text = text.replace(/\*\*([^*]+)\*\*/g, '$1');
15
15
  text = text.replace(/__([^_]+)__/g, '$1');
16
16
  text = text.replace(/\*([^*]+)\*/g, '$1');
@@ -1,4 +1,4 @@
1
- import { Prompt } from '../prompt.js';
1
+ import { SchemaInteraction } from '../schema-interaction.js';
2
2
  function readConfigEndpoints(root, context) {
3
3
  const configService = root.inject('config');
4
4
  const raw = configService?.getRaw(configService.primaryFile);
@@ -18,10 +18,10 @@ export class SchemaEndpointManager {
18
18
  return readConfigEndpoints(this.adapter.plugin.root, String(this.adapter.name));
19
19
  }
20
20
  async addEndpoint(ctx) {
21
- const prompt = new Prompt(this.adapter.plugin, ctx.message);
21
+ const interaction = new SchemaInteraction(this.adapter.plugin, ctx.message);
22
22
  await ctx.onStatusUpdate(`请按提示填写 ${String(this.adapter.name)} endpoint 配置:`);
23
- const name = await prompt.text('Endpoint id(zhin.config endpoints[].id):');
24
- const fields = await prompt.getValueWithSchema(this.schema);
23
+ const name = await interaction.text('Endpoint id(zhin.config endpoints[].id):');
24
+ const fields = await interaction.getValueWithSchema(this.schema);
25
25
  return {
26
26
  context: String(this.adapter.name),
27
27
  id: name.trim(),
@@ -34,8 +34,8 @@ export class SchemaEndpointManager {
34
34
  throw new Error(`配置中不存在 ${String(this.adapter.name)}/${name}`);
35
35
  }
36
36
  await ctx.onStatusUpdate(`编辑 ${String(this.adapter.name)}/${name},留空字段将保留原值。`);
37
- const prompt = new Prompt(this.adapter.plugin, ctx.message);
38
- const fields = await prompt.getValueWithSchema(this.schema);
37
+ const interaction = new SchemaInteraction(this.adapter.plugin, ctx.message);
38
+ const fields = await interaction.getValueWithSchema(this.schema);
39
39
  return {
40
40
  ...existing,
41
41
  ...fields,
@@ -1,38 +1,11 @@
1
- /**
2
- * Canonical Segment SSOT IM + AI 共用 JSON 形状(snake_case data)。
3
- * @see docs/architecture/segment-content-model.md
4
- */
5
- export interface SegmentBase {
6
- type: string;
7
- data: Record<string, unknown>;
8
- platform?: Record<string, unknown>;
9
- }
1
+ /** Canonical Segment SSOT lives in @zhin.js/im-contract. */
2
+ import type { MediaRef, SegmentBase } from '@zhin.js/im-contract';
3
+ export type { ForwardSegment, MediaRef, MentionSegment, ReplySegment, Segment, SegmentBase, TextSegment, } from '@zhin.js/im-contract';
10
4
  /**
11
5
  * 媒体引用占位(全框架唯一媒体表达)。
12
6
  * kind=file:平台侧不透明文件引用(如 Telegram file_id、Milky resource_id),
13
7
  * 非 URL/本地路径,消费方需经平台 API 解析。
14
8
  */
15
- export interface MediaRef {
16
- kind: 'url' | 'path' | 'base64' | 'file';
17
- value: string;
18
- mime_type?: string;
19
- file_name?: string;
20
- /** 字节数(已知时携带,供大小预检与日志) */
21
- size?: number;
22
- }
23
- export interface TextSegment extends SegmentBase {
24
- type: 'text';
25
- data: {
26
- text: string;
27
- };
28
- }
29
- export interface MentionSegment extends SegmentBase {
30
- type: 'mention';
31
- data: {
32
- target: string;
33
- name?: string;
34
- };
35
- }
36
9
  export interface ImageSegment extends SegmentBase {
37
10
  type: 'image';
38
11
  data: {
@@ -62,20 +35,6 @@ export interface FileSegment extends SegmentBase {
62
35
  name?: string;
63
36
  };
64
37
  }
65
- export interface ReplySegment extends SegmentBase {
66
- type: 'reply';
67
- data: {
68
- message_id: string;
69
- };
70
- }
71
- export interface ForwardSegment extends SegmentBase {
72
- type: 'forward';
73
- data: {
74
- forward_id: string;
75
- title?: string;
76
- messages?: Segment[][];
77
- };
78
- }
79
38
  export interface FaceSegment extends SegmentBase {
80
39
  type: 'face';
81
40
  data: {
@@ -96,4 +55,4 @@ export interface RpsSegment extends SegmentBase {
96
55
  };
97
56
  }
98
57
  /** 规范态 segment(严格校验 text / mention / image / audio / video / file / reply / forward / face / dice / rps) */
99
- export type Segment = TextSegment | MentionSegment | ImageSegment | AudioSegment | VideoSegment | FileSegment | ReplySegment | ForwardSegment | FaceSegment | DiceSegment | RpsSegment | SegmentBase;
58
+ export type LegacySpecificSegment = ImageSegment | AudioSegment | VideoSegment | FileSegment | FaceSegment | DiceSegment | RpsSegment;
@@ -1,5 +1 @@
1
- /**
2
- * Canonical Segment SSOT — IM + AI 共用 JSON 形状(snake_case data)。
3
- * @see docs/architecture/segment-content-model.md
4
- */
5
1
  export {};
@@ -0,0 +1,30 @@
1
+ import type { UserInteractionContent, UserInteractionRequest } from '@zhin.js/interaction';
2
+ import type { SendContent } from '../plugin-runtime/im/contracts.js';
3
+ import type { ButtonStyle } from './interactive-segments/types.js';
4
+ export interface UserInteractionAction {
5
+ readonly label: string;
6
+ readonly value: string;
7
+ readonly style?: ButtonStyle;
8
+ }
9
+ export interface UserInteractionView extends UserInteractionContent {
10
+ readonly actions?: readonly UserInteractionAction[];
11
+ }
12
+ export interface UserInteractionProgress extends UserInteractionContent {
13
+ readonly index: number;
14
+ readonly total: number;
15
+ }
16
+ export type UserInteractionParseResult = Readonly<{
17
+ ok: true;
18
+ value: unknown;
19
+ }> | Readonly<{
20
+ ok: false;
21
+ message: string;
22
+ }>;
23
+ /** Fail early for interaction definitions that could never produce an unambiguous answer. */
24
+ export declare function assertUserInteractionRequest(request: UserInteractionRequest): void;
25
+ /** Project semantic content into canonical Markdown + keyboard segments. */
26
+ export declare function renderUserInteraction(view: UserInteractionView): SendContent;
27
+ /** Derive presentation and controls from one discriminated request. */
28
+ export declare function projectUserInteraction(request: UserInteractionRequest, progress?: UserInteractionProgress): UserInteractionView;
29
+ /** Parse and validate one user reply without transport knowledge. */
30
+ export declare function parseUserInteractionAnswer(request: UserInteractionRequest, rawInput: string): UserInteractionParseResult;