@zhin.js/core 1.5.7 → 1.5.9
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/lib/adapter.d.ts +1 -2
- package/lib/adapter.js +31 -11
- package/lib/built/adapter-process.d.ts +5 -0
- package/lib/built/adapter-process.js +5 -0
- package/lib/built/ai-outbound/parse.d.ts +1 -1
- package/lib/built/ai-outbound/parse.js +1 -1
- package/lib/built/ai-outbound/prompt.d.ts +1 -1
- package/lib/built/ai-outbound/prompt.js +1 -1
- package/lib/built/ai-outbound/structured-detect.js +1 -2
- package/lib/built/ai-outbound/types.d.ts +1 -3
- package/lib/built/ai-trigger.d.ts +0 -5
- package/lib/built/ai-trigger.js +0 -1
- package/lib/endpoint.d.ts +3 -3
- package/lib/endpoint.js +1 -1
- package/lib/feature/handler.d.ts +10 -0
- package/lib/feature/handler.js +2 -0
- package/lib/plugin-runtime/im/im-runtime.d.ts +20 -19
- package/lib/plugin-runtime/im/im-runtime.js +256 -50
- package/lib/plugin-runtime/im/message-dispatcher.d.ts +2 -1
- package/lib/plugin-runtime/im/message-dispatcher.js +2 -2
- package/package.json +18 -8
package/lib/adapter.d.ts
CHANGED
|
@@ -88,8 +88,7 @@ export declare abstract class Adapter<R extends Endpoint = Endpoint, const Caps
|
|
|
88
88
|
sendMessage(options: SendOptions): Promise<string>;
|
|
89
89
|
/**
|
|
90
90
|
* `call.recallMessage` 事件链(prompt.ts 超时撤回等)的统一出口:
|
|
91
|
-
* 经 canonical `EndpointControl`
|
|
92
|
-
* `resolveEndpointControl` 迁移桥适配。
|
|
91
|
+
* 经 canonical `EndpointControl` 端口撤回。
|
|
93
92
|
*/
|
|
94
93
|
private recallEndpointMessage;
|
|
95
94
|
/**
|
package/lib/adapter.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { assertOutbound, DEFAULT_ENDPOINT_CAPABILITIES, hasInbound, } from "./endpoint-capabilities.js";
|
|
2
|
-
import {
|
|
2
|
+
import { endpointControlOf } from "@zhin.js/adapter";
|
|
3
3
|
import { connectEndpointInstance, disconnectEndpointInstance } from "./built/connect-endpoint-instance.js";
|
|
4
4
|
import { EventEmitter } from "node:events";
|
|
5
5
|
import { getOutboundReplyStore } from "./built/dispatcher.js";
|
|
@@ -123,10 +123,17 @@ export class Adapter extends EventEmitter {
|
|
|
123
123
|
return false;
|
|
124
124
|
}
|
|
125
125
|
this.#pendingMessages++;
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
126
|
+
try {
|
|
127
|
+
await this.inboundPipeline.receive(message, () => {
|
|
128
|
+
EventEmitter.prototype.emit.call(this, 'message.receive', message);
|
|
129
|
+
});
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
// Inbound pipeline errors must not leak the concurrency budget; the
|
|
134
|
+
// non-await path uses InboundMessagePipeline.decrementPending in finally.
|
|
135
|
+
this.#pendingMessages = Math.max(0, this.#pendingMessages - 1);
|
|
136
|
+
}
|
|
130
137
|
}
|
|
131
138
|
/**
|
|
132
139
|
* 出站富媒体能力(Publisher 按此过滤/降级;各 adapter 可覆盖)。
|
|
@@ -218,20 +225,26 @@ export class Adapter extends EventEmitter {
|
|
|
218
225
|
}
|
|
219
226
|
/**
|
|
220
227
|
* `call.recallMessage` 事件链(prompt.ts 超时撤回等)的统一出口:
|
|
221
|
-
* 经 canonical `EndpointControl`
|
|
222
|
-
* `resolveEndpointControl` 迁移桥适配。
|
|
228
|
+
* 经 canonical `EndpointControl` 端口撤回。
|
|
223
229
|
*/
|
|
224
230
|
async recallEndpointMessage(endpointKey, messageId) {
|
|
225
231
|
const endpoint = this.endpoints.get(endpointKey);
|
|
226
232
|
if (!endpoint)
|
|
227
233
|
throw new Error(`Endpoint ${endpointKey} not found`);
|
|
228
234
|
assertOutbound(endpoint);
|
|
229
|
-
const control =
|
|
235
|
+
const control = endpointControlOf(endpoint);
|
|
230
236
|
if (!control?.recall) {
|
|
231
237
|
throw new Error(`Endpoint ${endpointKey} does not support recall`);
|
|
232
238
|
}
|
|
233
239
|
this.logger.debug(formatCompact({ op: 'recall_message', msgId: messageId, endpoint: endpointKey }));
|
|
234
|
-
await control.recall(
|
|
240
|
+
await control.recall({
|
|
241
|
+
conversation: {
|
|
242
|
+
endpoint: { id: endpointKey, adapter: String(this.name) },
|
|
243
|
+
kind: 'private',
|
|
244
|
+
id: endpointKey,
|
|
245
|
+
},
|
|
246
|
+
id: messageId,
|
|
247
|
+
});
|
|
235
248
|
}
|
|
236
249
|
/**
|
|
237
250
|
* 编辑已发送的消息。
|
|
@@ -244,7 +257,7 @@ export class Adapter extends EventEmitter {
|
|
|
244
257
|
if (!endpoint)
|
|
245
258
|
throw new Error(`Endpoint ${options.endpoint} not found`);
|
|
246
259
|
assertOutbound(endpoint);
|
|
247
|
-
const control =
|
|
260
|
+
const control = endpointControlOf(endpoint);
|
|
248
261
|
if (control?.edit) {
|
|
249
262
|
const rendered = await this.renderSendMessage({
|
|
250
263
|
context: options.context,
|
|
@@ -253,7 +266,14 @@ export class Adapter extends EventEmitter {
|
|
|
253
266
|
type: options.type,
|
|
254
267
|
content: options.content,
|
|
255
268
|
});
|
|
256
|
-
await control.edit(
|
|
269
|
+
await control.edit({
|
|
270
|
+
conversation: {
|
|
271
|
+
endpoint: { id: options.endpoint, adapter: String(this.name) },
|
|
272
|
+
kind: options.type === 'group' ? 'group' : 'private',
|
|
273
|
+
id: options.id,
|
|
274
|
+
},
|
|
275
|
+
id: options.messageId,
|
|
276
|
+
}, rendered.content);
|
|
257
277
|
this.logger.debug(formatCompact({
|
|
258
278
|
edit: `${options.type}(${options.id})`,
|
|
259
279
|
endpoint: options.endpoint,
|
|
@@ -31,6 +31,11 @@ export declare class ProcessEndpoint implements Endpoint<{
|
|
|
31
31
|
content: string;
|
|
32
32
|
ts: number;
|
|
33
33
|
}>;
|
|
34
|
+
get control(): {
|
|
35
|
+
recall: (message: {
|
|
36
|
+
id: string;
|
|
37
|
+
}) => Promise<void>;
|
|
38
|
+
};
|
|
34
39
|
$recallMessage(id: string): Promise<void>;
|
|
35
40
|
$sendMessage(options: SendOptions): Promise<string>;
|
|
36
41
|
$connect(): Promise<void>;
|
|
@@ -5,7 +5,7 @@ export declare function parseOutboundSegment(raw: unknown): Segment | null;
|
|
|
5
5
|
/** 去掉嵌入 JSON 候选末尾的 markdown 围栏(模型常见 ```json … ``` 误输出)。 */
|
|
6
6
|
export declare function trimTrailingMarkdownFence(raw: string): string;
|
|
7
7
|
export declare function unwrapAiOutboundJsonCandidate(raw: string): string;
|
|
8
|
-
/**
|
|
8
|
+
/** Extract an embedded AI outbound JSON object from mixed prose. */
|
|
9
9
|
export declare function extractEmbeddedAiOutboundJson(plain: string): {
|
|
10
10
|
prose: string;
|
|
11
11
|
jsonRaw: string;
|
|
@@ -31,7 +31,7 @@ export function unwrapAiOutboundJsonCandidate(raw) {
|
|
|
31
31
|
}
|
|
32
32
|
return trimTrailingMarkdownFence(trimmed);
|
|
33
33
|
}
|
|
34
|
-
/**
|
|
34
|
+
/** Extract an embedded AI outbound JSON object from mixed prose. */
|
|
35
35
|
export function extractEmbeddedAiOutboundJson(plain) {
|
|
36
36
|
const trimmed = plain.trim();
|
|
37
37
|
if (!trimmed.includes('{'))
|
|
@@ -7,5 +7,5 @@ export declare function buildAiOutboundPromptHint(input: {
|
|
|
7
7
|
rosterLines?: string[];
|
|
8
8
|
forceJsonOnly?: boolean;
|
|
9
9
|
}): string;
|
|
10
|
-
/**
|
|
10
|
+
/** Detect whether inbound text explicitly requests a structured handoff. */
|
|
11
11
|
export declare function detectInboundHandoffIntent(content: string): boolean;
|
|
@@ -33,7 +33,7 @@ export function buildAiOutboundPromptHint(input) {
|
|
|
33
33
|
}
|
|
34
34
|
return lines.join('\n');
|
|
35
35
|
}
|
|
36
|
-
/**
|
|
36
|
+
/** Detect whether inbound text explicitly requests a structured handoff. */
|
|
37
37
|
export function detectInboundHandoffIntent(content) {
|
|
38
38
|
const t = content.trim();
|
|
39
39
|
if (!t)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/** structured_only:满足任一即进入结构化出站模式。 */
|
|
2
2
|
export function isStructuredOutboundRequired(input) {
|
|
3
|
-
return Boolean(input.
|
|
4
|
-
|| input.toolRequiresStructured
|
|
3
|
+
return Boolean(input.toolRequiresStructured
|
|
5
4
|
|| input.inboundHandoffIntent
|
|
6
5
|
|| input.adapterHasExtensions);
|
|
7
6
|
}
|
|
@@ -48,9 +48,7 @@ export interface AiOutboundParseContext {
|
|
|
48
48
|
extensions?: readonly AiOutboundExtensionDefinition[];
|
|
49
49
|
}
|
|
50
50
|
export interface StructuredOutboundDetectInput {
|
|
51
|
-
/**
|
|
52
|
-
collaborationCell?: boolean;
|
|
53
|
-
/** 工具链要求结构化出站(如 group_delegate message JSON) */
|
|
51
|
+
/** 工具链要求结构化出站。 */
|
|
54
52
|
toolRequiresStructured?: boolean;
|
|
55
53
|
/** 用户入站含 handoff / @ 意图 */
|
|
56
54
|
inboundHandoffIntent?: boolean;
|
|
@@ -39,11 +39,6 @@ export interface AITriggerConfig {
|
|
|
39
39
|
trusted?: string[];
|
|
40
40
|
/** 是否在 AI 入参前拉取 $quote_id 对应消息正文(默认 true) */
|
|
41
41
|
resolveQuotedMessages?: boolean;
|
|
42
|
-
/**
|
|
43
|
-
* 协作单元内 peer Bot 入站策略(ADR 0023)。
|
|
44
|
-
* mention-only:仅被 @ 时触发;off:与普通人消息相同规则。
|
|
45
|
-
*/
|
|
46
|
-
peerMode?: 'mention-only' | 'off';
|
|
47
42
|
}
|
|
48
43
|
/**
|
|
49
44
|
* AI 触发检查结果
|
package/lib/built/ai-trigger.js
CHANGED
package/lib/endpoint.d.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
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';
|
|
3
|
+
import type { EndpointWithControl, EndpointWithManagement } from '@zhin.js/adapter';
|
|
4
4
|
export type { EndpointChannel, EndpointChannelParent, EndpointFriend, EndpointGroup, EndpointManagement, EndpointWithManagement, EndpointManagementCapability, } from '@zhin.js/adapter';
|
|
5
5
|
export { endpointManagementCapabilityIds, listEndpointManagementCapabilities, resolveEndpointManagement, } from '@zhin.js/adapter';
|
|
6
6
|
export type { EndpointControl, EndpointWithControl } from '@zhin.js/adapter';
|
|
7
|
-
export {
|
|
7
|
+
export { endpointControlOf } from '@zhin.js/adapter';
|
|
8
8
|
export type { EndpointCapability, EndpointCapabilitiesConfig, InboundEndpoint, OutboundEndpoint, FullEndpoint, CapableEndpoint, } from './endpoint-capabilities.js';
|
|
9
9
|
export { DEFAULT_ENDPOINT_CAPABILITIES, OutboundNotSupportedError, InboundNotSupportedError, resolveEndpointCapabilities, registerEndpointCapabilities, getEndpointCapabilities, getAdapterCapabilities, hasInbound, hasOutbound, assertInbound, assertOutbound, } from './endpoint-capabilities.js';
|
|
10
10
|
/**
|
|
11
11
|
* Endpoint 接口:全双工平台机器人(入站 + 出站)。
|
|
12
12
|
* 纯入站 / 纯出站请实现 InboundEndpoint / OutboundEndpoint。
|
|
13
13
|
*/
|
|
14
|
-
export type Endpoint<Config extends object = object, Event extends object = object> = FullEndpoint<Config, Event> & EndpointWithManagement;
|
|
14
|
+
export type Endpoint<Config extends object = object, Event extends object = object> = FullEndpoint<Config, Event> & EndpointWithManagement & EndpointWithControl;
|
|
15
15
|
export declare namespace Endpoint {
|
|
16
16
|
type Config<K extends keyof Adapters = keyof Adapters> = Adapter.EndpointConfig<Adapter.InferEndpoint<Adapters[K]>> & EndpointCapabilitiesConfig & {
|
|
17
17
|
context: K;
|
package/lib/endpoint.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { endpointManagementCapabilityIds, listEndpointManagementCapabilities, resolveEndpointManagement, } from '@zhin.js/adapter';
|
|
2
|
-
export {
|
|
2
|
+
export { endpointControlOf } from '@zhin.js/adapter';
|
|
3
3
|
export { DEFAULT_ENDPOINT_CAPABILITIES, OutboundNotSupportedError, InboundNotSupportedError, resolveEndpointCapabilities, registerEndpointCapabilities, getEndpointCapabilities, getAdapterCapabilities, hasInbound, hasOutbound, assertInbound, assertOutbound, } from './endpoint-capabilities.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Authoring API for Handler Feature — implementation in `@zhin.js/feature-kit`. */
|
|
2
|
+
export { defineHandler, parseHandlerDefinition, HandlerIndex, isHandlerIndex, handlerFeatureId, handlerFeature, type HandlerEventMap, type HandlerDefinition, type HandlerDescriptor, } from '@zhin.js/feature-kit';
|
|
3
|
+
import type { Plugin } from '../plugin.js';
|
|
4
|
+
type KnownKeys<T> = {
|
|
5
|
+
[K in keyof T as string extends K ? never : number extends K ? never : K]: T[K];
|
|
6
|
+
};
|
|
7
|
+
declare module '@zhin.js/feature-kit' {
|
|
8
|
+
interface HandlerEventMap extends KnownKeys<Plugin.Lifecycle> {
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { Scope, type PluginId, type RuntimeSnapshot, type SnapshotLease, type
|
|
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 } from '@zhin.js/adapter';
|
|
3
|
+
import { type EndpointManagement, type EndpointManagementCapability, type AdapterEndpointPhase } from '@zhin.js/adapter';
|
|
4
4
|
import { 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
7
|
import { OutboundRenderer } from './outbound-renderer.js';
|
|
7
8
|
import { type RuntimeInteractiveHandler } from './interactive.js';
|
|
8
9
|
export declare const messageGatewayToken: import("@zhin.js/plugin-runtime").Token<MessageGateway>;
|
|
@@ -45,15 +46,23 @@ export interface ImRuntimeOptions {
|
|
|
45
46
|
export declare class ImRuntime implements MessageGateway {
|
|
46
47
|
#private;
|
|
47
48
|
constructor(options?: ImRuntimeOptions);
|
|
48
|
-
attach(snapshots:
|
|
49
|
+
attach(snapshots: SnapshotReader): void;
|
|
49
50
|
readonly permissionHost: import("@zhin.js/permission").PermissionHost;
|
|
50
51
|
readonly messageBus: MessageBus;
|
|
51
52
|
install(resources: Scope): void;
|
|
53
|
+
[generationAdmissionBinder](gate: GenerationAdmissionGate): MessageGateway;
|
|
52
54
|
/**
|
|
53
55
|
* 注册 interactive action 回跳 handler(prefix 最长匹配;返回注销函数)。
|
|
54
56
|
* 在 Command dispatch 之前路由:action 段 / 数字回跳 / 指令预填 payload。
|
|
55
57
|
*/
|
|
56
58
|
registerInteractiveHandler(prefix: string, handler: RuntimeInteractiveHandler): () => void;
|
|
59
|
+
/**
|
|
60
|
+
* Bound Prompt for this conversation.
|
|
61
|
+
* `bind.subjectId` waits for that user (e.g. master) instead of the message sender.
|
|
62
|
+
*/
|
|
63
|
+
createPrompt(message: Message, bind?: {
|
|
64
|
+
readonly subjectId: string;
|
|
65
|
+
}): CommandPrompt | undefined;
|
|
57
66
|
/**
|
|
58
67
|
* 订阅消息事件(入站 dispatch 完成后 / 出站发送成功后回调)。
|
|
59
68
|
* 返回注销函数。listener 抛错不会阻断消息链路。
|
|
@@ -68,7 +77,7 @@ export declare class ImRuntime implements MessageGateway {
|
|
|
68
77
|
readonly owner: string;
|
|
69
78
|
readonly connected: boolean;
|
|
70
79
|
readonly status: 'online' | 'offline';
|
|
71
|
-
readonly phase:
|
|
80
|
+
readonly phase: AdapterEndpointPhase;
|
|
72
81
|
readonly managementCapabilities: readonly EndpointManagementCapability[];
|
|
73
82
|
}[];
|
|
74
83
|
/**
|
|
@@ -87,7 +96,7 @@ export declare class ImRuntime implements MessageGateway {
|
|
|
87
96
|
readonly adapter: string;
|
|
88
97
|
readonly connected: boolean;
|
|
89
98
|
readonly status: 'online' | 'offline';
|
|
90
|
-
readonly phase:
|
|
99
|
+
readonly phase: AdapterEndpointPhase;
|
|
91
100
|
readonly managementCapabilities: readonly EndpointManagementCapability[];
|
|
92
101
|
} | null;
|
|
93
102
|
sendEndpointMessage(input: {
|
|
@@ -102,8 +111,7 @@ export declare class ImRuntime implements MessageGateway {
|
|
|
102
111
|
addEndpointReaction(input: {
|
|
103
112
|
readonly adapter: string;
|
|
104
113
|
readonly endpointKey: string;
|
|
105
|
-
readonly message
|
|
106
|
-
readonly messageId?: string;
|
|
114
|
+
readonly message: MessageRef;
|
|
107
115
|
readonly emoji: string;
|
|
108
116
|
readonly sceneType?: string;
|
|
109
117
|
readonly channelId?: string;
|
|
@@ -111,22 +119,19 @@ export declare class ImRuntime implements MessageGateway {
|
|
|
111
119
|
removeEndpointReaction(input: {
|
|
112
120
|
readonly adapter: string;
|
|
113
121
|
readonly endpointKey: string;
|
|
114
|
-
readonly message
|
|
115
|
-
readonly messageId?: string;
|
|
122
|
+
readonly message: MessageRef;
|
|
116
123
|
readonly reactionId: string;
|
|
117
124
|
}): Promise<void>;
|
|
118
125
|
/** Activity-feedback autoRemove: recall a previously sent status message. */
|
|
119
126
|
recallEndpointMessage(input: {
|
|
120
127
|
readonly adapter: string;
|
|
121
128
|
readonly endpointKey: string;
|
|
122
|
-
readonly message
|
|
123
|
-
readonly messageId?: string;
|
|
129
|
+
readonly message: MessageRef;
|
|
124
130
|
}): Promise<void>;
|
|
125
131
|
editEndpointMessage(input: {
|
|
126
132
|
readonly adapter: string;
|
|
127
133
|
readonly endpointKey: string;
|
|
128
|
-
readonly message
|
|
129
|
-
readonly messageId?: string;
|
|
134
|
+
readonly message: MessageRef;
|
|
130
135
|
readonly content: unknown;
|
|
131
136
|
}): Promise<string | null>;
|
|
132
137
|
setEndpointTyping(input: {
|
|
@@ -135,10 +140,6 @@ export declare class ImRuntime implements MessageGateway {
|
|
|
135
140
|
readonly conversation: ConversationRef;
|
|
136
141
|
readonly active?: boolean;
|
|
137
142
|
}): Promise<void>;
|
|
138
|
-
/**
|
|
139
|
-
|
|
140
|
-
* the Endpoint exists but implements no management operations; null means it
|
|
141
|
-
* cannot be resolved.
|
|
142
|
-
*/
|
|
143
|
-
getEndpointManagement(adapter: string, endpointKey: string): EndpointManagement | null;
|
|
143
|
+
/** Run one management operation while the Endpoint generation stays leased. */
|
|
144
|
+
withEndpointManagement<T>(adapter: string, endpointKey: string, run: (management: EndpointManagement) => T | Promise<T>): Promise<T | null>;
|
|
144
145
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { createToken, htmlRendererToken, } from '@zhin.js/plugin-runtime';
|
|
1
|
+
import { createToken, generationAdmissionBinder, htmlRendererToken, } from '@zhin.js/plugin-runtime';
|
|
2
2
|
import { createPermissionHost, permissionHostToken } from '@zhin.js/permission';
|
|
3
3
|
import { MessageBus, messageBusToken } from './message-bus.js';
|
|
4
|
-
import { adapterFeatureId, isAdapterIndex,
|
|
5
|
-
import {
|
|
4
|
+
import { adapterFeatureId, isAdapterIndex, endpointControlOf, resolveEndpointManagement, } from '@zhin.js/adapter';
|
|
5
|
+
import { isDeliveryReceipt, } from '@zhin.js/im-contract';
|
|
6
6
|
import { isMiddlewareIndex, middlewareFeatureId } from '@zhin.js/middleware';
|
|
7
|
+
import { isHandlerIndex, handlerFeatureId } from '../../feature/handler.js';
|
|
7
8
|
import { formatCompact, getLogger, truncatePreview } from '@zhin.js/logger';
|
|
8
9
|
import { Message, createOutboundEnvelope, } from './contracts.js';
|
|
9
10
|
import { defaultCommandPrefixResolver, MessageDispatcher } from './message-dispatcher.js';
|
|
@@ -21,6 +22,7 @@ export class ImRuntime {
|
|
|
21
22
|
#renderer;
|
|
22
23
|
#messageListeners = new Set();
|
|
23
24
|
#interactiveHandlers = [];
|
|
25
|
+
#promptClaims = new Map();
|
|
24
26
|
#snapshots;
|
|
25
27
|
#inboundClaim;
|
|
26
28
|
#enrichSender;
|
|
@@ -45,12 +47,25 @@ export class ImRuntime {
|
|
|
45
47
|
resources.provide(permissionHostToken, this.permissionHost);
|
|
46
48
|
resources.provide(messageBusToken, this.messageBus);
|
|
47
49
|
}
|
|
50
|
+
[generationAdmissionBinder](gate) {
|
|
51
|
+
const gateway = {
|
|
52
|
+
receive: async (input) => gate.enter(() => this.#receive(input, gate))
|
|
53
|
+
?? Object.freeze({ matched: false }),
|
|
54
|
+
send: async (request) => gate.enter(() => this.send(request))
|
|
55
|
+
?? failedReceipt('generation_not_admitted'),
|
|
56
|
+
registerInteractiveHandler: (prefix, handler) => this.#registerInteractiveHandler(prefix, handler, gate),
|
|
57
|
+
};
|
|
58
|
+
return Object.freeze(gateway);
|
|
59
|
+
}
|
|
48
60
|
/**
|
|
49
61
|
* 注册 interactive action 回跳 handler(prefix 最长匹配;返回注销函数)。
|
|
50
62
|
* 在 Command dispatch 之前路由:action 段 / 数字回跳 / 指令预填 payload。
|
|
51
63
|
*/
|
|
52
64
|
registerInteractiveHandler(prefix, handler) {
|
|
53
|
-
|
|
65
|
+
return this.#registerInteractiveHandler(prefix, handler);
|
|
66
|
+
}
|
|
67
|
+
#registerInteractiveHandler(prefix, handler, admission) {
|
|
68
|
+
const entry = Object.freeze({ prefix, handler, admission });
|
|
54
69
|
this.#interactiveHandlers.push(entry);
|
|
55
70
|
return () => {
|
|
56
71
|
const index = this.#interactiveHandlers.indexOf(entry);
|
|
@@ -58,6 +73,168 @@ export class ImRuntime {
|
|
|
58
73
|
this.#interactiveHandlers.splice(index, 1);
|
|
59
74
|
};
|
|
60
75
|
}
|
|
76
|
+
// ==========================================================================
|
|
77
|
+
// Prompt claim — 命令对话式交互
|
|
78
|
+
// ==========================================================================
|
|
79
|
+
#promptConversationKey(message, subjectId = message.sender?.id ?? '') {
|
|
80
|
+
const conv = message.conversation;
|
|
81
|
+
return `${conv.endpoint.adapter}:${conv.endpoint.id}:${conv.kind}:${conv.id}:${subjectId}`;
|
|
82
|
+
}
|
|
83
|
+
#resolvePromptClaim(message) {
|
|
84
|
+
const key = this.#promptConversationKey(message);
|
|
85
|
+
const claim = this.#promptClaims.get(key);
|
|
86
|
+
if (!claim)
|
|
87
|
+
return false;
|
|
88
|
+
claim.resolve(message.content);
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
#claimNextMessage(message, timeout, timeoutText, signal, subjectId) {
|
|
92
|
+
return new Promise((resolve, reject) => {
|
|
93
|
+
if (signal?.aborted) {
|
|
94
|
+
reject(abortError(signal, timeoutText));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const key = this.#promptConversationKey(message, subjectId);
|
|
98
|
+
const existing = this.#promptClaims.get(key);
|
|
99
|
+
if (existing) {
|
|
100
|
+
clearTimeout(existing.timer);
|
|
101
|
+
existing.reject(new Error('Prompt superseded'));
|
|
102
|
+
}
|
|
103
|
+
const timer = setTimeout(() => {
|
|
104
|
+
settle(undefined, new Error(timeoutText));
|
|
105
|
+
}, timeout);
|
|
106
|
+
const onAbort = () => settle(undefined, abortError(signal, timeoutText));
|
|
107
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
108
|
+
const settle = (value, error) => {
|
|
109
|
+
if (this.#promptClaims.get(key) !== claim)
|
|
110
|
+
return;
|
|
111
|
+
this.#promptClaims.delete(key);
|
|
112
|
+
clearTimeout(timer);
|
|
113
|
+
signal?.removeEventListener('abort', onAbort);
|
|
114
|
+
if (value !== undefined)
|
|
115
|
+
resolve(value);
|
|
116
|
+
else
|
|
117
|
+
reject(error ?? new Error(timeoutText));
|
|
118
|
+
};
|
|
119
|
+
const claim = {
|
|
120
|
+
resolve: (raw) => settle(raw),
|
|
121
|
+
reject: (error) => settle(undefined, error),
|
|
122
|
+
timer,
|
|
123
|
+
};
|
|
124
|
+
this.#promptClaims.set(key, claim);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
#buildCommandPrompt(message, subjectId) {
|
|
128
|
+
const DEFAULT_TIMEOUT = 3 * 60 * 1000;
|
|
129
|
+
const DEFAULT_TIMEOUT_TEXT = '输入超时';
|
|
130
|
+
const claim = (timeout, timeoutText, signal) => this.#claimNextMessage(message, timeout, timeoutText, signal, subjectId);
|
|
131
|
+
const reply = (content) => message.$reply(content);
|
|
132
|
+
return {
|
|
133
|
+
async text(tips, options) {
|
|
134
|
+
await reply(tips);
|
|
135
|
+
try {
|
|
136
|
+
return await claim(options?.timeout ?? DEFAULT_TIMEOUT, options?.timeoutText ?? DEFAULT_TIMEOUT_TEXT, options?.signal);
|
|
137
|
+
}
|
|
138
|
+
catch (e) {
|
|
139
|
+
if (options?.default !== undefined)
|
|
140
|
+
return options.default;
|
|
141
|
+
await reply(e.message);
|
|
142
|
+
throw e;
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
async number(tips, options) {
|
|
146
|
+
await reply(tips);
|
|
147
|
+
try {
|
|
148
|
+
const raw = await claim(options?.timeout ?? DEFAULT_TIMEOUT, options?.timeoutText ?? DEFAULT_TIMEOUT_TEXT, options?.signal);
|
|
149
|
+
return +raw;
|
|
150
|
+
}
|
|
151
|
+
catch (e) {
|
|
152
|
+
if (options?.default !== undefined)
|
|
153
|
+
return options.default;
|
|
154
|
+
await reply(e.message);
|
|
155
|
+
throw e;
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
async confirm(tips, options) {
|
|
159
|
+
const condition = options?.condition ?? 'yes';
|
|
160
|
+
await reply(`${tips}\n输入"${condition}"以确认`);
|
|
161
|
+
try {
|
|
162
|
+
const raw = await claim(options?.timeout ?? DEFAULT_TIMEOUT, options?.timeoutText ?? DEFAULT_TIMEOUT_TEXT, options?.signal);
|
|
163
|
+
return raw === condition;
|
|
164
|
+
}
|
|
165
|
+
catch (e) {
|
|
166
|
+
if (options?.default !== undefined)
|
|
167
|
+
return options.default;
|
|
168
|
+
await reply(e.message);
|
|
169
|
+
throw e;
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
async list(tips, options) {
|
|
173
|
+
const separator = options?.separator ?? ',';
|
|
174
|
+
await reply(`${tips}\n值之间使用"${separator}"分隔`);
|
|
175
|
+
try {
|
|
176
|
+
const raw = await claim(options?.timeout ?? DEFAULT_TIMEOUT, options?.timeoutText ?? DEFAULT_TIMEOUT_TEXT, options?.signal);
|
|
177
|
+
const type = options?.type ?? 'text';
|
|
178
|
+
return raw.split(separator).map((v) => {
|
|
179
|
+
if (type === 'number')
|
|
180
|
+
return +v;
|
|
181
|
+
if (type === 'boolean')
|
|
182
|
+
return v === 'true';
|
|
183
|
+
return v;
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
catch (e) {
|
|
187
|
+
if (options?.default !== undefined)
|
|
188
|
+
return options.default;
|
|
189
|
+
await reply(e.message);
|
|
190
|
+
throw e;
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
async pick(tips, options) {
|
|
194
|
+
const items = options.options.map((o, i) => `${i + 1}.${o.label}`);
|
|
195
|
+
const separator = options.separator ?? ',';
|
|
196
|
+
if (options.multiple)
|
|
197
|
+
items.push(`多选请用"${separator}"分隔`);
|
|
198
|
+
await reply(`${tips}\n${items.join('\n')}`);
|
|
199
|
+
try {
|
|
200
|
+
const raw = await claim(options.timeout ?? DEFAULT_TIMEOUT, options.timeoutText ?? DEFAULT_TIMEOUT_TEXT, options.signal);
|
|
201
|
+
if (!options.multiple) {
|
|
202
|
+
return options.options.find((_, i) => i + 1 === +raw)?.value;
|
|
203
|
+
}
|
|
204
|
+
const indices = raw.split(separator).map(Number);
|
|
205
|
+
return options.options
|
|
206
|
+
.filter((_, i) => indices.includes(i + 1))
|
|
207
|
+
.map((o) => o.value);
|
|
208
|
+
}
|
|
209
|
+
catch (e) {
|
|
210
|
+
if (options.default !== undefined)
|
|
211
|
+
return options.default;
|
|
212
|
+
await reply(e.message);
|
|
213
|
+
throw e;
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Bound Prompt for this conversation.
|
|
220
|
+
* `bind.subjectId` waits for that user (e.g. master) instead of the message sender.
|
|
221
|
+
*/
|
|
222
|
+
createPrompt(message, bind) {
|
|
223
|
+
const subjectId = bind?.subjectId?.trim();
|
|
224
|
+
if (bind && !subjectId)
|
|
225
|
+
return undefined;
|
|
226
|
+
if (typeof message.$reply !== 'function' || !message.conversation)
|
|
227
|
+
return undefined;
|
|
228
|
+
return this.#buildCommandPrompt(message, subjectId);
|
|
229
|
+
}
|
|
230
|
+
#createPromptForSource(source) {
|
|
231
|
+
if (!source || typeof source !== 'object')
|
|
232
|
+
return undefined;
|
|
233
|
+
const msg = source;
|
|
234
|
+
if (typeof msg.$reply !== 'function' || !msg.conversation)
|
|
235
|
+
return undefined;
|
|
236
|
+
return this.#buildCommandPrompt(msg);
|
|
237
|
+
}
|
|
61
238
|
/**
|
|
62
239
|
* 订阅消息事件(入站 dispatch 完成后 / 出站发送成功后回调)。
|
|
63
240
|
* 返回注销函数。listener 抛错不会阻断消息链路。
|
|
@@ -77,6 +254,9 @@ export class ImRuntime {
|
|
|
77
254
|
}
|
|
78
255
|
}
|
|
79
256
|
async receive(input) {
|
|
257
|
+
return this.#receive(input);
|
|
258
|
+
}
|
|
259
|
+
async #receive(input, admission) {
|
|
80
260
|
const lease = this.#acquire();
|
|
81
261
|
let active = true;
|
|
82
262
|
try {
|
|
@@ -113,15 +293,20 @@ export class ImRuntime {
|
|
|
113
293
|
},
|
|
114
294
|
}, lease.value);
|
|
115
295
|
}, enrichedSender, Object.freeze({ ...input.metadata }), input.segments ? Object.freeze([...input.segments]) : undefined, input.message, input.endpointId, input.mentioned, input.replyTo);
|
|
296
|
+
await runHandlers(lease.value, 'message.receive', message);
|
|
116
297
|
let result = Object.freeze({ matched: false });
|
|
117
298
|
const claimed = await this.#inboundClaim?.(message) === true;
|
|
118
299
|
if (claimed) {
|
|
119
300
|
result = Object.freeze({ matched: true, command: 'interaction', owner: requester });
|
|
120
301
|
}
|
|
302
|
+
else if (this.#resolvePromptClaim(message)) {
|
|
303
|
+
result = Object.freeze({ matched: true, command: 'prompt', owner: requester });
|
|
304
|
+
}
|
|
121
305
|
else {
|
|
306
|
+
const promptFactory = (source) => this.#createPromptForSource(source);
|
|
122
307
|
await runMiddleware(lease.value, message, async () => {
|
|
123
|
-
result = await this.#dispatchInteractive(message, requester)
|
|
124
|
-
?? await this.#dispatcher.dispatch(message, lease.value);
|
|
308
|
+
result = await this.#dispatchInteractive(message, requester, admission)
|
|
309
|
+
?? await this.#dispatcher.dispatch(message, lease.value, promptFactory);
|
|
125
310
|
const ingressRoute = resolveIngressRoute(lease.value);
|
|
126
311
|
if (!result.matched && ingressRoute) {
|
|
127
312
|
logger.debug(formatCompact({ op: 'unmatched', conv: formatConversationLog(conversation) }));
|
|
@@ -273,58 +458,59 @@ export class ImRuntime {
|
|
|
273
458
|
}
|
|
274
459
|
/** Activity-feedback: add a message reaction when the live Endpoint supports it. */
|
|
275
460
|
async addEndpointReaction(input) {
|
|
276
|
-
|
|
277
|
-
return control?.addReaction?.(legacyMessageTarget(input.message, input.messageId), input.emoji, {
|
|
461
|
+
return this.#withEndpointControl(input.adapter, input.endpointKey, (control) => control.addReaction?.(input.message, input.emoji, {
|
|
278
462
|
sceneType: input.sceneType,
|
|
279
463
|
channelId: input.channelId,
|
|
280
|
-
}) ?? null;
|
|
464
|
+
}) ?? null, null);
|
|
281
465
|
}
|
|
282
466
|
async removeEndpointReaction(input) {
|
|
283
|
-
await this.#
|
|
284
|
-
?.removeReaction?.(legacyMessageTarget(input.message, input.messageId), input.reactionId);
|
|
467
|
+
await this.#withEndpointControl(input.adapter, input.endpointKey, (control) => control.removeReaction?.(input.message, input.reactionId), undefined);
|
|
285
468
|
}
|
|
286
469
|
/** Activity-feedback autoRemove: recall a previously sent status message. */
|
|
287
470
|
async recallEndpointMessage(input) {
|
|
288
|
-
await this.#
|
|
289
|
-
?.recall?.(legacyMessageTarget(input.message, input.messageId));
|
|
471
|
+
await this.#withEndpointControl(input.adapter, input.endpointKey, (control) => control.recall?.(input.message), undefined);
|
|
290
472
|
}
|
|
291
473
|
async editEndpointMessage(input) {
|
|
292
|
-
return this.#
|
|
293
|
-
?.edit?.(legacyMessageTarget(input.message, input.messageId), input.content) ?? null;
|
|
474
|
+
return this.#withEndpointControl(input.adapter, input.endpointKey, (control) => control.edit?.(input.message, input.content) ?? null, null);
|
|
294
475
|
}
|
|
295
476
|
async setEndpointTyping(input) {
|
|
296
|
-
await this.#
|
|
297
|
-
?.typing?.(input.conversation, input.active);
|
|
477
|
+
await this.#withEndpointControl(input.adapter, input.endpointKey, (control) => control.typing?.(input.conversation, input.active), undefined);
|
|
298
478
|
}
|
|
299
|
-
#
|
|
479
|
+
async #withEndpointControl(adapter, endpointKey, run, fallback) {
|
|
480
|
+
let lease;
|
|
300
481
|
try {
|
|
301
|
-
|
|
302
|
-
try {
|
|
303
|
-
const endpoint = requireAdapters(lease.value).instance(adapter, endpointKey);
|
|
304
|
-
return endpoint ?? null;
|
|
305
|
-
}
|
|
306
|
-
finally {
|
|
307
|
-
lease.release();
|
|
308
|
-
}
|
|
482
|
+
lease = this.#acquire();
|
|
309
483
|
}
|
|
310
484
|
catch {
|
|
311
|
-
return
|
|
485
|
+
return fallback;
|
|
486
|
+
}
|
|
487
|
+
try {
|
|
488
|
+
const endpoint = requireAdapters(lease.value).instance(adapter, endpointKey);
|
|
489
|
+
const control = endpointControlOf(endpoint);
|
|
490
|
+
return control ? await run(control) : fallback;
|
|
491
|
+
}
|
|
492
|
+
finally {
|
|
493
|
+
lease.release();
|
|
312
494
|
}
|
|
313
495
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
* cannot be resolved.
|
|
322
|
-
*/
|
|
323
|
-
getEndpointManagement(adapter, endpointKey) {
|
|
324
|
-
const endpoint = this.#liveEndpoint(adapter, endpointKey);
|
|
325
|
-
if (!endpoint)
|
|
496
|
+
/** Run one management operation while the Endpoint generation stays leased. */
|
|
497
|
+
async withEndpointManagement(adapter, endpointKey, run) {
|
|
498
|
+
let lease;
|
|
499
|
+
try {
|
|
500
|
+
lease = this.#acquire();
|
|
501
|
+
}
|
|
502
|
+
catch {
|
|
326
503
|
return null;
|
|
327
|
-
|
|
504
|
+
}
|
|
505
|
+
try {
|
|
506
|
+
const endpoint = requireAdapters(lease.value).instance(adapter, endpointKey);
|
|
507
|
+
if (!endpoint)
|
|
508
|
+
return null;
|
|
509
|
+
return await run(resolveEndpointManagement(endpoint) ?? Object.freeze({}));
|
|
510
|
+
}
|
|
511
|
+
finally {
|
|
512
|
+
lease.release();
|
|
513
|
+
}
|
|
328
514
|
}
|
|
329
515
|
async #sendWithSnapshot(request, snapshot) {
|
|
330
516
|
const adapter = request.conversation.endpoint.id;
|
|
@@ -381,8 +567,21 @@ export class ImRuntime {
|
|
|
381
567
|
catch {
|
|
382
568
|
return receipt ?? failedReceipt('outbound_middleware_failed');
|
|
383
569
|
}
|
|
384
|
-
if (!terminalEntered)
|
|
570
|
+
if (!terminalEntered) {
|
|
571
|
+
logger.debug(formatCompact({
|
|
572
|
+
op: 'replychain_runtime_send',
|
|
573
|
+
status: 'suppressed',
|
|
574
|
+
reason: 'middleware_stopped_before_terminal',
|
|
575
|
+
conv: formatConversationLog(request.conversation),
|
|
576
|
+
}));
|
|
385
577
|
return suppressedReceipt();
|
|
578
|
+
}
|
|
579
|
+
logger.debug(formatCompact({
|
|
580
|
+
op: 'replychain_runtime_send',
|
|
581
|
+
status: receipt?.status ?? 'missing',
|
|
582
|
+
code: receipt?.failure?.code,
|
|
583
|
+
conv: formatConversationLog(request.conversation),
|
|
584
|
+
}));
|
|
386
585
|
return receipt ?? failedReceipt('outbound_delivery_incomplete');
|
|
387
586
|
}
|
|
388
587
|
#acquire() {
|
|
@@ -394,13 +593,13 @@ export class ImRuntime {
|
|
|
394
593
|
* interactive 回跳分发(Command dispatch 之前):action 段 / 中央 fallback
|
|
395
594
|
* 数字回跳 / 指令预填 payload → prefix 最长匹配 handler。
|
|
396
595
|
*/
|
|
397
|
-
async #dispatchInteractive(message, requester) {
|
|
596
|
+
async #dispatchInteractive(message, requester, admission) {
|
|
398
597
|
if (this.#interactiveHandlers.length === 0)
|
|
399
598
|
return undefined;
|
|
400
599
|
const payload = resolveRuntimeInteractivePayload(message);
|
|
401
600
|
if (!payload)
|
|
402
601
|
return undefined;
|
|
403
|
-
const handler = findRuntimeInteractiveHandler(this.#interactiveHandlers, payload);
|
|
602
|
+
const handler = findRuntimeInteractiveHandler(this.#interactiveHandlers.filter((entry) => !entry.admission || entry.admission === admission), payload);
|
|
404
603
|
if (!handler)
|
|
405
604
|
return undefined;
|
|
406
605
|
const handled = await handler(message);
|
|
@@ -439,13 +638,6 @@ function isDirectHtmlConsumer(snapshot, adapter) {
|
|
|
439
638
|
const owner = snapshot.capabilities.get(adapter)?.owner;
|
|
440
639
|
return adapterTypeName(snapshot.tree.get(owner)?.packageName) === 'sandbox';
|
|
441
640
|
}
|
|
442
|
-
function legacyMessageTarget(message, messageId) {
|
|
443
|
-
if (message)
|
|
444
|
-
return formatLegacyMessageRef(message);
|
|
445
|
-
if (messageId)
|
|
446
|
-
return messageId;
|
|
447
|
-
throw new TypeError('message or messageId is required');
|
|
448
|
-
}
|
|
449
641
|
async function prepareOutboundPayload(rendered, conversation, snapshot, finalizeInteractive = false) {
|
|
450
642
|
const adapter = conversation.endpoint.id;
|
|
451
643
|
const directHtml = isDirectHtmlConsumer(snapshot, adapter);
|
|
@@ -529,6 +721,15 @@ async function runMiddleware(snapshot, input, terminal, target) {
|
|
|
529
721
|
else
|
|
530
722
|
await terminal();
|
|
531
723
|
}
|
|
724
|
+
function handlers(snapshot) {
|
|
725
|
+
const projection = snapshot.projections.get(handlerFeatureId);
|
|
726
|
+
return isHandlerIndex(projection) ? projection : undefined;
|
|
727
|
+
}
|
|
728
|
+
async function runHandlers(snapshot, event, ...args) {
|
|
729
|
+
const index = handlers(snapshot);
|
|
730
|
+
if (index)
|
|
731
|
+
await index.dispatch(event, ...args);
|
|
732
|
+
}
|
|
532
733
|
function normalizeConsoleContent(content) {
|
|
533
734
|
if (typeof content === 'string')
|
|
534
735
|
return content;
|
|
@@ -581,3 +782,8 @@ function flattenContent(content) {
|
|
|
581
782
|
}
|
|
582
783
|
return String(content);
|
|
583
784
|
}
|
|
785
|
+
function abortError(signal, fallback) {
|
|
786
|
+
if (signal?.reason instanceof Error)
|
|
787
|
+
return signal.reason;
|
|
788
|
+
return new Error(fallback);
|
|
789
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CommandPromptFactory } from '@zhin.js/command';
|
|
1
2
|
import type { RuntimeSnapshot } from '@zhin.js/plugin-runtime';
|
|
2
3
|
import type { Message, MessageDispatchResult } from './contracts.js';
|
|
3
4
|
/**
|
|
@@ -13,5 +14,5 @@ export declare const defaultCommandPrefixResolver: CommandPrefixResolver;
|
|
|
13
14
|
export declare class MessageDispatcher {
|
|
14
15
|
private readonly resolvePrefix;
|
|
15
16
|
constructor(resolvePrefix?: CommandPrefixResolver);
|
|
16
|
-
dispatch(message: Message, snapshot: RuntimeSnapshot): Promise<MessageDispatchResult>;
|
|
17
|
+
dispatch(message: Message, snapshot: RuntimeSnapshot, promptFactory?: CommandPromptFactory): Promise<MessageDispatchResult>;
|
|
17
18
|
}
|
|
@@ -26,7 +26,7 @@ export class MessageDispatcher {
|
|
|
26
26
|
constructor(resolvePrefix = defaultCommandPrefixResolver) {
|
|
27
27
|
this.resolvePrefix = resolvePrefix;
|
|
28
28
|
}
|
|
29
|
-
async dispatch(message, snapshot) {
|
|
29
|
+
async dispatch(message, snapshot, promptFactory) {
|
|
30
30
|
const prefix = this.resolvePrefix(message, snapshot);
|
|
31
31
|
let input = message.content.trim();
|
|
32
32
|
if (prefix && !input.startsWith(prefix)) {
|
|
@@ -43,7 +43,7 @@ export class MessageDispatcher {
|
|
|
43
43
|
? stripCommandPrefix(message.segments, prefix)
|
|
44
44
|
: undefined;
|
|
45
45
|
const matchInput = structuredInput ?? input;
|
|
46
|
-
const result = await commands.dispatch(matchInput, message);
|
|
46
|
+
const result = await commands.dispatch(matchInput, message, promptFactory, prefix);
|
|
47
47
|
if (result.matched && result.value !== undefined) {
|
|
48
48
|
if (!result.owner)
|
|
49
49
|
throw new Error('Matched Command is missing its owner');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/core",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.9",
|
|
4
4
|
"description": "Zhin机器人核心框架",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -60,6 +60,11 @@
|
|
|
60
60
|
"types": "./lib/feature/middleware.d.ts",
|
|
61
61
|
"development": "./src/feature/middleware.ts",
|
|
62
62
|
"import": "./lib/feature/middleware.js"
|
|
63
|
+
},
|
|
64
|
+
"./feature/handler": {
|
|
65
|
+
"types": "./lib/feature/handler.d.ts",
|
|
66
|
+
"development": "./src/feature/handler.ts",
|
|
67
|
+
"import": "./lib/feature/handler.js"
|
|
63
68
|
}
|
|
64
69
|
},
|
|
65
70
|
"files": [
|
|
@@ -70,16 +75,17 @@
|
|
|
70
75
|
"segment-matcher": "^1.0.5",
|
|
71
76
|
"smol-toml": "^1.7.1",
|
|
72
77
|
"yaml": "^2.9.0",
|
|
73
|
-
"@zhin.js/adapter": "1.1.
|
|
74
|
-
"@zhin.js/command": "1.0.
|
|
75
|
-
"@zhin.js/component": "1.0.
|
|
78
|
+
"@zhin.js/adapter": "1.1.9",
|
|
79
|
+
"@zhin.js/command": "1.0.13",
|
|
80
|
+
"@zhin.js/component": "1.0.10",
|
|
76
81
|
"@zhin.js/database": "1.0.79",
|
|
82
|
+
"@zhin.js/feature-kit": "1.0.10",
|
|
77
83
|
"@zhin.js/im-contract": "1.0.3",
|
|
78
84
|
"@zhin.js/kernel": "1.0.7",
|
|
79
85
|
"@zhin.js/logger": "1.0.76",
|
|
80
|
-
"@zhin.js/middleware": "1.0.
|
|
81
|
-
"@zhin.js/permission": "1.0.
|
|
82
|
-
"@zhin.js/plugin-runtime": "1.1.
|
|
86
|
+
"@zhin.js/middleware": "1.0.10",
|
|
87
|
+
"@zhin.js/permission": "1.0.2",
|
|
88
|
+
"@zhin.js/plugin-runtime": "1.1.6",
|
|
83
89
|
"@zhin.js/schema": "1.0.73"
|
|
84
90
|
},
|
|
85
91
|
"peerDependencies": {
|
|
@@ -95,7 +101,7 @@
|
|
|
95
101
|
"@types/qrcode": "^1.5.6",
|
|
96
102
|
"ajv": "8.18.0",
|
|
97
103
|
"typescript": "^6.0.3",
|
|
98
|
-
"@zhin.js/ai": "1.5.
|
|
104
|
+
"@zhin.js/ai": "1.5.4"
|
|
99
105
|
},
|
|
100
106
|
"repository": {
|
|
101
107
|
"type": "git",
|
|
@@ -137,6 +143,10 @@
|
|
|
137
143
|
{
|
|
138
144
|
"package": "@zhin.js/middleware",
|
|
139
145
|
"api": "^1.0.0"
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"package": "@zhin.js/feature-kit",
|
|
149
|
+
"api": "^1.0.0"
|
|
140
150
|
}
|
|
141
151
|
],
|
|
142
152
|
"plugins": []
|