@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.
- package/README.md +2 -2
- package/lib/adapter.d.ts +3 -0
- package/lib/built/ai-trigger.d.ts +0 -2
- package/lib/built/ai-trigger.js +0 -1
- package/lib/built/inbound-pipeline.js +0 -1
- package/lib/built/inbound-runner.js +0 -2
- package/lib/built/login-assist.d.ts +22 -4
- package/lib/built/login-assist.js +71 -7
- package/lib/built/message-enrich.d.ts +0 -1
- package/lib/built/message-enrich.js +0 -1
- package/lib/built/rich-segments/markdown-to-text.js +2 -2
- package/lib/built/schema-endpoint-manager.js +6 -6
- package/lib/built/segment-contract/types.d.ts +4 -45
- package/lib/built/segment-contract/types.js +0 -4
- package/lib/built/user-interaction.d.ts +30 -0
- package/lib/built/user-interaction.js +248 -0
- package/lib/feature/handler.d.ts +1 -1
- package/lib/feature/handler.js +1 -1
- package/lib/im-scene.js +0 -2
- package/lib/index.d.ts +4 -3
- package/lib/index.js +3 -3
- package/lib/message.d.ts +0 -5
- package/lib/message.js +0 -13
- package/lib/plugin-runtime/im/im-runtime.d.ts +30 -8
- package/lib/plugin-runtime/im/im-runtime.js +471 -145
- package/lib/plugin-runtime/im/index.d.ts +2 -0
- package/lib/plugin-runtime/im/index.js +2 -0
- package/lib/plugin-runtime/im/login-assist-host.d.ts +6 -0
- package/lib/plugin-runtime/im/login-assist-host.js +6 -0
- package/lib/plugin-runtime/im/message-dispatcher.d.ts +2 -2
- package/lib/plugin-runtime/im/message-dispatcher.js +2 -2
- package/lib/plugin-runtime/im/outbound-segments.d.ts +4 -0
- package/lib/plugin-runtime/im/outbound-segments.js +26 -0
- package/lib/plugin-runtime/im/side-event-gateway.d.ts +13 -0
- package/lib/plugin-runtime/im/side-event-gateway.js +2 -0
- package/lib/plugin.d.ts +1 -0
- package/lib/{prompt.d.ts → schema-interaction.d.ts} +6 -6
- package/lib/{prompt.js → schema-interaction.js} +12 -12
- package/lib/side-event/base.d.ts +7 -0
- package/lib/side-event/dispatch.d.ts +15 -0
- package/lib/side-event/dispatch.js +140 -0
- package/lib/side-event/index.d.ts +1 -0
- package/lib/side-event/index.js +1 -0
- package/lib/side-event/normalize.d.ts +2 -0
- package/lib/side-event/normalize.js +4 -0
- package/lib/side-event/types.d.ts +4 -2
- package/lib/side-event/types.js +1 -1
- package/lib/system-event.d.ts +15 -0
- package/lib/system-event.js +7 -0
- package/lib/types.d.ts +0 -15
- package/package.json +11 -10
- package/lib/built/prepend-quote-context.d.ts +0 -34
- package/lib/built/prepend-quote-context.js +0 -130
- package/lib/message-quote.d.ts +0 -10
- 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
|
|
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,
|
|
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;
|
package/lib/built/ai-trigger.js
CHANGED
|
@@ -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
|
|
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(
|
|
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
|
|
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
|
-
|
|
18
|
+
bus;
|
|
19
19
|
pending = new Map();
|
|
20
|
+
local = new Map();
|
|
20
21
|
idSeq = 0;
|
|
21
22
|
/** 构造时默认超时;可被 waitForInput options 覆盖 */
|
|
22
23
|
defaultTimeoutMs;
|
|
23
|
-
constructor(
|
|
24
|
-
this.
|
|
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
|
|
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
|
|
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
|
|
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) {
|
|
@@ -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(
|
|
13
|
-
text = text.replace(
|
|
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 {
|
|
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
|
|
21
|
+
const interaction = new SchemaInteraction(this.adapter.plugin, ctx.message);
|
|
22
22
|
await ctx.onStatusUpdate(`请按提示填写 ${String(this.adapter.name)} endpoint 配置:`);
|
|
23
|
-
const name = await
|
|
24
|
-
const fields = await
|
|
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
|
|
38
|
-
const fields = await
|
|
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
|
-
|
|
3
|
-
|
|
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
|
|
58
|
+
export type LegacySpecificSegment = ImageSegment | AudioSegment | VideoSegment | FileSegment | FaceSegment | DiceSegment | RpsSegment;
|
|
@@ -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;
|