weacpx 0.6.1 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bridge/bridge-main.js +7 -1
- package/dist/channels/types.d.ts +9 -0
- package/dist/cli.js +948 -257
- package/dist/commands/handlers/session-handler.d.ts +4 -2
- package/dist/commands/handlers/session-list-marker.d.ts +1 -0
- package/dist/commands/parse-command.d.ts +3 -0
- package/dist/plugin-api.d.ts +9 -0
- package/dist/plugin-api.js +157 -0
- package/dist/{weixin/messaging → runtime}/conversation-executor.d.ts +1 -1
- package/dist/runtime/core-home.d.ts +26 -0
- package/dist/runtime/turn-lane.d.ts +2 -0
- package/dist/sessions/active-turn-registry.d.ts +6 -0
- package/dist/sessions/session-service.d.ts +37 -2
- package/dist/state/types.d.ts +7 -0
- package/dist/weixin/agent/interface.d.ts +1 -0
- package/dist/weixin/api/config-cache.d.ts +18 -1
- package/dist/weixin/bot.d.ts +11 -0
- package/dist/weixin/messaging/completion-notice.d.ts +2 -0
- package/dist/weixin/messaging/foreground-gate.d.ts +3 -0
- package/dist/weixin/messaging/handle-weixin-message-turn.d.ts +4 -0
- package/dist/weixin/messaging/inbound.d.ts +7 -0
- package/dist/weixin/messaging/quota-manager.d.ts +15 -1
- package/dist/weixin/monitor/monitor.d.ts +8 -0
- package/package.json +1 -1
|
@@ -8,6 +8,7 @@ export interface SessionHandlerContext extends CommandRouterContext {
|
|
|
8
8
|
lifecycle: SessionLifecycleOps;
|
|
9
9
|
interaction: SessionInteractionOps;
|
|
10
10
|
recovery: SessionRenderRecoveryOps;
|
|
11
|
+
readonly activeTurns?: import("../../sessions/active-turn-registry.js").ActiveTurnRegistry;
|
|
11
12
|
}
|
|
12
13
|
export declare const sessionHelp: HelpTopicMetadata;
|
|
13
14
|
export declare const nativeSessionHelp: HelpTopicMetadata;
|
|
@@ -22,14 +23,15 @@ export declare function handleSessionShortcut(context: SessionHandlerContext, ch
|
|
|
22
23
|
workspace?: string;
|
|
23
24
|
}, createNew: boolean): Promise<RouterResponse>;
|
|
24
25
|
export declare function handleSessionAttach(context: SessionHandlerContext, chatKey: string, alias: string, agent: string, workspace: string, transportSession: string): Promise<RouterResponse>;
|
|
25
|
-
export declare function handleSessionUse(context: SessionHandlerContext, chatKey: string,
|
|
26
|
+
export declare function handleSessionUse(context: SessionHandlerContext, chatKey: string, input: string): Promise<RouterResponse>;
|
|
27
|
+
export declare function handleSessionUsePrevious(context: SessionHandlerContext, chatKey: string): Promise<RouterResponse>;
|
|
26
28
|
export declare function handleModeShow(context: SessionHandlerContext, chatKey: string): Promise<RouterResponse>;
|
|
27
29
|
export declare function handleModeSet(context: SessionHandlerContext, chatKey: string, modeId: string): Promise<RouterResponse>;
|
|
28
30
|
export declare function handleReplyModeShow(context: SessionHandlerContext, chatKey: string): Promise<RouterResponse>;
|
|
29
31
|
export declare function handleReplyModeSet(context: SessionHandlerContext, chatKey: string, replyMode: "stream" | "final" | "verbose"): Promise<RouterResponse>;
|
|
30
32
|
export declare function handleReplyModeReset(context: SessionHandlerContext, chatKey: string): Promise<RouterResponse>;
|
|
31
33
|
export declare function handleStatus(context: SessionHandlerContext, chatKey: string): Promise<RouterResponse>;
|
|
32
|
-
export declare function handleCancel(context: SessionHandlerContext, chatKey: string): Promise<RouterResponse>;
|
|
34
|
+
export declare function handleCancel(context: SessionHandlerContext, chatKey: string, alias?: string): Promise<RouterResponse>;
|
|
33
35
|
export declare function handleSessionReset(context: SessionHandlerContext, chatKey: string): Promise<RouterResponse>;
|
|
34
36
|
export declare function handleSessionTail(context: SessionHandlerContext, chatKey: string, lines?: number): Promise<RouterResponse>;
|
|
35
37
|
export declare function handleSessionRemove(context: SessionHandlerContext, chatKey: string, alias: string): Promise<RouterResponse>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function decorateUnread(label: string, hasUnread: boolean): string;
|
|
@@ -54,6 +54,7 @@ export type ParsedCommand = {
|
|
|
54
54
|
kind: "status";
|
|
55
55
|
} | {
|
|
56
56
|
kind: "cancel";
|
|
57
|
+
alias?: string;
|
|
57
58
|
} | {
|
|
58
59
|
kind: "session.reset";
|
|
59
60
|
} | {
|
|
@@ -118,6 +119,8 @@ export type ParsedCommand = {
|
|
|
118
119
|
} | {
|
|
119
120
|
kind: "session.use";
|
|
120
121
|
alias: string;
|
|
122
|
+
} | {
|
|
123
|
+
kind: "session.use.previous";
|
|
121
124
|
} | {
|
|
122
125
|
kind: "session.new";
|
|
123
126
|
alias: string;
|
package/dist/plugin-api.d.ts
CHANGED
|
@@ -7,3 +7,12 @@ export type { CommandHint } from "./commands/command-hints.js";
|
|
|
7
7
|
export type { AppLogger } from "./logging/app-logger.js";
|
|
8
8
|
export type { WeacpxPlugin } from "./plugins/types.js";
|
|
9
9
|
export { WEACPX_PLUGIN_API_VERSION, WEACPX_PLUGIN_API_SUPPORTED_VERSIONS, WEACPX_PLUGIN_MIN_CORE_VERSION, } from "./plugins/types.js";
|
|
10
|
+
export { createConversationExecutor } from "./runtime/conversation-executor.js";
|
|
11
|
+
export type { ConversationExecutor, ConversationExecutorLane } from "./runtime/conversation-executor.js";
|
|
12
|
+
export { resolveTurnLane } from "./runtime/turn-lane.js";
|
|
13
|
+
export { createActiveTurnRegistry } from "./sessions/active-turn-registry.js";
|
|
14
|
+
export type { ActiveTurnRegistry } from "./sessions/active-turn-registry.js";
|
|
15
|
+
export { toDisplaySessionAlias } from "./channels/channel-scope.js";
|
|
16
|
+
export type { SessionService } from "./sessions/session-service.js";
|
|
17
|
+
export type { BackgroundResult } from "./state/types.js";
|
|
18
|
+
export type { ChatRequestMetadata } from "./weixin/agent/interface.js";
|
package/dist/plugin-api.js
CHANGED
|
@@ -171,9 +171,166 @@ var init_types = __esm(() => {
|
|
|
171
171
|
init_compatibility();
|
|
172
172
|
});
|
|
173
173
|
|
|
174
|
+
// src/runtime/conversation-executor.ts
|
|
175
|
+
function createConversationExecutor() {
|
|
176
|
+
const states = new Map;
|
|
177
|
+
const getState = (conversationId) => {
|
|
178
|
+
const existing = states.get(conversationId);
|
|
179
|
+
if (existing)
|
|
180
|
+
return existing;
|
|
181
|
+
const created = { normalTails: new Map, activeControls: 0 };
|
|
182
|
+
states.set(conversationId, created);
|
|
183
|
+
return created;
|
|
184
|
+
};
|
|
185
|
+
const cleanupState = (conversationId, state) => {
|
|
186
|
+
if (state.normalTails.size === 0 && state.activeControls === 0) {
|
|
187
|
+
states.delete(conversationId);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
return {
|
|
191
|
+
run(conversationId, lane, task, sessionKey) {
|
|
192
|
+
const state = getState(conversationId);
|
|
193
|
+
if (lane === "control") {
|
|
194
|
+
state.activeControls += 1;
|
|
195
|
+
return Promise.resolve().then(task).finally(() => {
|
|
196
|
+
state.activeControls -= 1;
|
|
197
|
+
cleanupState(conversationId, state);
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
const key = sessionKey ?? DEFAULT_SESSION_KEY;
|
|
201
|
+
const previous = state.normalTails.get(key) ?? Promise.resolve();
|
|
202
|
+
const next = previous.then(() => task(), () => task());
|
|
203
|
+
state.normalTails.set(key, next);
|
|
204
|
+
return next.finally(() => {
|
|
205
|
+
if (state.normalTails.get(key) === next) {
|
|
206
|
+
state.normalTails.delete(key);
|
|
207
|
+
}
|
|
208
|
+
cleanupState(conversationId, state);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
var DEFAULT_SESSION_KEY = "__chat__";
|
|
214
|
+
|
|
215
|
+
// src/sessions/active-turn-registry.ts
|
|
216
|
+
function createActiveTurnRegistry() {
|
|
217
|
+
const byChat = new Map;
|
|
218
|
+
return {
|
|
219
|
+
markActive(chatKey, alias) {
|
|
220
|
+
const set = byChat.get(chatKey) ?? new Set;
|
|
221
|
+
set.add(alias);
|
|
222
|
+
byChat.set(chatKey, set);
|
|
223
|
+
},
|
|
224
|
+
markInactive(chatKey, alias) {
|
|
225
|
+
const set = byChat.get(chatKey);
|
|
226
|
+
if (!set)
|
|
227
|
+
return;
|
|
228
|
+
set.delete(alias);
|
|
229
|
+
if (set.size === 0)
|
|
230
|
+
byChat.delete(chatKey);
|
|
231
|
+
},
|
|
232
|
+
isActive(chatKey, alias) {
|
|
233
|
+
return byChat.get(chatKey)?.has(alias) ?? false;
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// src/channels/channel-scope.ts
|
|
239
|
+
function registerKnownChannelId(channelId) {
|
|
240
|
+
const normalized = channelId.trim();
|
|
241
|
+
if (!normalized || normalized.includes(":")) {
|
|
242
|
+
throw new Error("channel id must be non-empty and must not contain ':'");
|
|
243
|
+
}
|
|
244
|
+
KNOWN_CHANNEL_IDS.add(normalized);
|
|
245
|
+
}
|
|
246
|
+
function listKnownChannelIds() {
|
|
247
|
+
return Array.from(KNOWN_CHANNEL_IDS);
|
|
248
|
+
}
|
|
249
|
+
function getChannelIdFromChatKey(chatKey) {
|
|
250
|
+
const first = chatKey.split(":", 1)[0];
|
|
251
|
+
return first && KNOWN_CHANNEL_IDS.has(first) ? first : "weixin";
|
|
252
|
+
}
|
|
253
|
+
function toInternalSessionAlias(channelId, displayAlias) {
|
|
254
|
+
const normalized = displayAlias.trim();
|
|
255
|
+
if (normalized.length === 0) {
|
|
256
|
+
throw new Error("display session alias must be non-empty");
|
|
257
|
+
}
|
|
258
|
+
if (normalized.startsWith(`${channelId}:`)) {
|
|
259
|
+
return normalized;
|
|
260
|
+
}
|
|
261
|
+
return `${channelId}:${normalized}`;
|
|
262
|
+
}
|
|
263
|
+
function toDisplaySessionAlias(internalAlias) {
|
|
264
|
+
const [first, ...rest] = internalAlias.split(":");
|
|
265
|
+
if (first && KNOWN_CHANNEL_IDS.has(first) && rest.length > 0) {
|
|
266
|
+
return rest.join(":");
|
|
267
|
+
}
|
|
268
|
+
return internalAlias;
|
|
269
|
+
}
|
|
270
|
+
function isSessionAliasVisibleInChannel(alias, channelId) {
|
|
271
|
+
const [first] = alias.split(":", 1);
|
|
272
|
+
if (first && KNOWN_CHANNEL_IDS.has(first)) {
|
|
273
|
+
return first === channelId;
|
|
274
|
+
}
|
|
275
|
+
return channelId === "weixin";
|
|
276
|
+
}
|
|
277
|
+
function resolveSessionAliasForInput(channelId, displayAlias, existingAliases) {
|
|
278
|
+
const normalized = displayAlias.trim();
|
|
279
|
+
if (normalized.length === 0) {
|
|
280
|
+
throw new Error("display session alias must be non-empty");
|
|
281
|
+
}
|
|
282
|
+
if (normalized.startsWith(`${channelId}:`)) {
|
|
283
|
+
return normalized;
|
|
284
|
+
}
|
|
285
|
+
const scopedAlias = toInternalSessionAlias(channelId, normalized);
|
|
286
|
+
for (const alias of existingAliases) {
|
|
287
|
+
if (alias === scopedAlias)
|
|
288
|
+
return scopedAlias;
|
|
289
|
+
}
|
|
290
|
+
if (channelId === "weixin") {
|
|
291
|
+
for (const alias of existingAliases) {
|
|
292
|
+
if (alias === normalized)
|
|
293
|
+
return alias;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return scopedAlias;
|
|
297
|
+
}
|
|
298
|
+
function scopeDisplayAliasToInternal(channelId, displayAlias) {
|
|
299
|
+
const normalized = displayAlias.trim();
|
|
300
|
+
if (normalized.length === 0) {
|
|
301
|
+
throw new Error("display session alias must be non-empty");
|
|
302
|
+
}
|
|
303
|
+
return channelId === "weixin" ? normalized : toInternalSessionAlias(channelId, normalized);
|
|
304
|
+
}
|
|
305
|
+
function buildDefaultTransportSession(channelId, displayAlias) {
|
|
306
|
+
const normalized = displayAlias.trim();
|
|
307
|
+
if (normalized.length === 0) {
|
|
308
|
+
throw new Error("display session alias must be non-empty");
|
|
309
|
+
}
|
|
310
|
+
return channelId === "weixin" ? normalized : toInternalSessionAlias(channelId, normalized);
|
|
311
|
+
}
|
|
312
|
+
var KNOWN_CHANNEL_IDS;
|
|
313
|
+
var init_channel_scope = __esm(() => {
|
|
314
|
+
KNOWN_CHANNEL_IDS = new Set(["weixin"]);
|
|
315
|
+
});
|
|
316
|
+
|
|
174
317
|
// src/plugin-api.ts
|
|
175
318
|
init_types();
|
|
319
|
+
|
|
320
|
+
// src/runtime/turn-lane.ts
|
|
321
|
+
var CONTROL_COMMANDS = new Set(["/use", "/ss", "/cancel", "/stop"]);
|
|
322
|
+
function resolveTurnLane(text) {
|
|
323
|
+
const command = text.trim().toLowerCase().split(/\s+/)[0] ?? "";
|
|
324
|
+
return CONTROL_COMMANDS.has(command) ? "control" : "normal";
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// src/plugin-api.ts
|
|
328
|
+
init_channel_scope();
|
|
176
329
|
export {
|
|
330
|
+
toDisplaySessionAlias,
|
|
331
|
+
resolveTurnLane,
|
|
332
|
+
createConversationExecutor,
|
|
333
|
+
createActiveTurnRegistry,
|
|
177
334
|
WEACPX_PLUGIN_MIN_CORE_VERSION,
|
|
178
335
|
WEACPX_PLUGIN_API_VERSION,
|
|
179
336
|
WEACPX_PLUGIN_API_SUPPORTED_VERSIONS
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type ConversationExecutorLane = "normal" | "control";
|
|
2
2
|
type ConversationTask<T> = () => Promise<T>;
|
|
3
3
|
export type ConversationExecutor = {
|
|
4
|
-
run<T>(conversationId: string, lane: ConversationExecutorLane, task: ConversationTask<T
|
|
4
|
+
run<T>(conversationId: string, lane: ConversationExecutorLane, task: ConversationTask<T>, sessionKey?: string): Promise<T>;
|
|
5
5
|
};
|
|
6
6
|
export declare function createConversationExecutor(): ConversationExecutor;
|
|
7
7
|
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-user state directory name (`<home>/.weacpx/`). This is the SINGLE
|
|
3
|
+
* source of truth for that name; every config / state / runtime / plugin path
|
|
4
|
+
* is built on top of it.
|
|
5
|
+
*
|
|
6
|
+
* The weacpx→xacpx rename (0.8.0) changes the name HERE — and adds any
|
|
7
|
+
* legacy-directory fallback inside {@link coreHomeDir} (e.g. prefer
|
|
8
|
+
* `~/.xacpx`, fall back to an existing `~/.weacpx`) — so the whole tree of
|
|
9
|
+
* derived paths follows from one place rather than ~11 scattered literals.
|
|
10
|
+
*/
|
|
11
|
+
export declare const CORE_HOME_DIR_NAME = ".weacpx";
|
|
12
|
+
/**
|
|
13
|
+
* The core state root for a given user home: `<home>/.weacpx`.
|
|
14
|
+
*
|
|
15
|
+
* Pass the home directory the caller already resolved — this helper imposes no
|
|
16
|
+
* home-resolution policy of its own, so each caller's existing semantics are
|
|
17
|
+
* preserved (this is a pure refactor of the directory-name literal). It is the
|
|
18
|
+
* one function the 0.8.0 rename hooks into.
|
|
19
|
+
*/
|
|
20
|
+
export declare function coreHomeDir(home: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Display form for user-facing hints (e.g. "请查看日志:~/.weacpx/runtime/...").
|
|
23
|
+
* Keeps printed paths single-sourced with the real directory name. Always uses
|
|
24
|
+
* "/" separators since it is for display, not filesystem access.
|
|
25
|
+
*/
|
|
26
|
+
export declare function coreHomeDisplayPath(...segments: string[]): string;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface ActiveTurnRegistry {
|
|
2
|
+
markActive(chatKey: string, alias: string): void;
|
|
3
|
+
markInactive(chatKey: string, alias: string): void;
|
|
4
|
+
isActive(chatKey: string, alias: string): boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function createActiveTurnRegistry(): ActiveTurnRegistry;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AppConfig } from "../config/types";
|
|
2
2
|
import { AsyncMutex } from "../orchestration/async-mutex";
|
|
3
3
|
import type { StateStore } from "../state/state-store";
|
|
4
|
-
import type { AppState } from "../state/types";
|
|
4
|
+
import type { AppState, BackgroundResult } from "../state/types";
|
|
5
5
|
import type { AgentSession, ResolvedSession } from "../transport/types";
|
|
6
6
|
interface SessionListItem {
|
|
7
7
|
alias: string;
|
|
@@ -10,6 +10,25 @@ interface SessionListItem {
|
|
|
10
10
|
workspace: string;
|
|
11
11
|
isCurrent: boolean;
|
|
12
12
|
}
|
|
13
|
+
export interface SessionSwitchResult {
|
|
14
|
+
alias: string;
|
|
15
|
+
agent: string;
|
|
16
|
+
workspace: string;
|
|
17
|
+
previousAlias?: string;
|
|
18
|
+
}
|
|
19
|
+
export type FuzzyAliasResult = {
|
|
20
|
+
kind: "match";
|
|
21
|
+
alias: string;
|
|
22
|
+
} | {
|
|
23
|
+
kind: "ambiguous";
|
|
24
|
+
candidates: Array<{
|
|
25
|
+
alias: string;
|
|
26
|
+
agent: string;
|
|
27
|
+
workspace: string;
|
|
28
|
+
}>;
|
|
29
|
+
} | {
|
|
30
|
+
kind: "none";
|
|
31
|
+
};
|
|
13
32
|
interface NativeSessionAttachmentInput {
|
|
14
33
|
alias: string;
|
|
15
34
|
agent: string;
|
|
@@ -57,9 +76,24 @@ export declare class SessionService {
|
|
|
57
76
|
attachSession(alias: string, agent: string, workspace: string, transportSession: string, transportAgentCommand?: string): Promise<ResolvedSession>;
|
|
58
77
|
attachNativeSession(input: NativeSessionAttachmentInput): Promise<ResolvedSession>;
|
|
59
78
|
getSession(alias: string): Promise<ResolvedSession | null>;
|
|
79
|
+
/**
|
|
80
|
+
* Synchronously resolve a session by its internal alias (as stored in state).
|
|
81
|
+
* Returns null if the alias is unknown or if the referenced agent/workspace is
|
|
82
|
+
* no longer registered (i.e. toResolvedSession would throw).
|
|
83
|
+
*
|
|
84
|
+
* Used by handlePrompt to honour a `boundSessionAlias` captured at dispatch
|
|
85
|
+
* time without requiring an async state mutation.
|
|
86
|
+
*/
|
|
87
|
+
getResolvedSessionByInternalAlias(alias: string): ResolvedSession | null;
|
|
88
|
+
peekCurrentSessionAlias(chatKey: string): string | undefined;
|
|
60
89
|
getPreferredSessionForTransport(transportSession: string): Promise<ResolvedSession | null>;
|
|
61
90
|
findAttachedNativeSession(chatKey: string, agent: string, agentSessionId: string): Promise<ResolvedSession | null>;
|
|
62
|
-
useSession(chatKey: string, alias: string): Promise<
|
|
91
|
+
useSession(chatKey: string, alias: string): Promise<SessionSwitchResult>;
|
|
92
|
+
usePreviousSession(chatKey: string): Promise<SessionSwitchResult | null>;
|
|
93
|
+
setBackgroundResult(chatKey: string, alias: string, result: BackgroundResult): Promise<void>;
|
|
94
|
+
takeBackgroundResult(chatKey: string, alias: string): Promise<BackgroundResult | null>;
|
|
95
|
+
listBackgroundResultAliases(chatKey: string): string[];
|
|
96
|
+
resolveFuzzyAlias(chatKey: string, fragment: string): FuzzyAliasResult;
|
|
63
97
|
resolveAliasForChat(chatKey: string, displayAlias: string): Promise<string>;
|
|
64
98
|
buildDefaultTransportSessionForChat(chatKey: string, displayAlias: string): string;
|
|
65
99
|
listInternalAliases(): string[];
|
|
@@ -73,6 +107,7 @@ export declare class SessionService {
|
|
|
73
107
|
}>;
|
|
74
108
|
cacheNativeSessionList(chatKey: string, input: NativeSessionListInput): Promise<void>;
|
|
75
109
|
getNativeSessionList(chatKey: string, ttlMs?: number): Promise<NativeSessionListResult | null>;
|
|
110
|
+
private deleteNativeSessionListIfCurrent;
|
|
76
111
|
private toResolvedSession;
|
|
77
112
|
setSessionTransportAgentCommand(alias: string, transportAgentCommand: string | undefined): Promise<void>;
|
|
78
113
|
private mutate;
|
package/dist/state/types.d.ts
CHANGED
|
@@ -31,8 +31,15 @@ export interface LogicalSession {
|
|
|
31
31
|
created_at: string;
|
|
32
32
|
last_used_at: string;
|
|
33
33
|
}
|
|
34
|
+
export interface BackgroundResult {
|
|
35
|
+
text: string;
|
|
36
|
+
status: "done" | "error";
|
|
37
|
+
finished_at: string;
|
|
38
|
+
}
|
|
34
39
|
export interface ChatContextState {
|
|
35
40
|
current_session: string;
|
|
41
|
+
previous_session?: string;
|
|
42
|
+
background_results?: Record<string, BackgroundResult>;
|
|
36
43
|
}
|
|
37
44
|
export interface AppState {
|
|
38
45
|
sessions: Record<string, LogicalSession>;
|
|
@@ -66,6 +66,7 @@ export interface ChatRequestMetadata {
|
|
|
66
66
|
scheduledSessionAlias?: string;
|
|
67
67
|
/** Transient session descriptor for temp-mode scheduled prompts (no persisted alias). */
|
|
68
68
|
scheduledSessionDescriptor?: ScheduledSessionDescriptor;
|
|
69
|
+
boundSessionAlias?: string;
|
|
69
70
|
}
|
|
70
71
|
export interface ChatResponse {
|
|
71
72
|
/**
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
+
import { getConfig } from "./api.js";
|
|
1
2
|
/** Subset of getConfig fields that we actually need; add new fields here as needed. */
|
|
2
3
|
export interface CachedConfig {
|
|
3
4
|
typingTicket: string;
|
|
4
5
|
}
|
|
6
|
+
type GetConfigFn = typeof getConfig;
|
|
7
|
+
export interface WeixinConfigManagerOptions {
|
|
8
|
+
maxEntries?: number;
|
|
9
|
+
entryTtlMs?: number;
|
|
10
|
+
now?: () => number;
|
|
11
|
+
getConfig?: GetConfigFn;
|
|
12
|
+
}
|
|
5
13
|
/**
|
|
6
14
|
* Per-user getConfig cache with periodic random refresh (within 24h) and
|
|
7
15
|
* exponential-backoff retry (up to 1h) on failure.
|
|
@@ -10,9 +18,18 @@ export declare class WeixinConfigManager {
|
|
|
10
18
|
private apiOpts;
|
|
11
19
|
private log;
|
|
12
20
|
private cache;
|
|
21
|
+
private readonly maxEntries;
|
|
22
|
+
private readonly entryTtlMs;
|
|
23
|
+
private readonly now;
|
|
24
|
+
private readonly fetchConfig;
|
|
13
25
|
constructor(apiOpts: {
|
|
14
26
|
baseUrl: string;
|
|
15
27
|
token?: string;
|
|
16
|
-
}, log: (msg: string) => void);
|
|
28
|
+
}, log: (msg: string) => void, options?: WeixinConfigManagerOptions);
|
|
17
29
|
getForUser(userId: string, contextToken?: string): Promise<CachedConfig>;
|
|
30
|
+
cacheSizeForTests(): number;
|
|
31
|
+
hasCachedUserForTests(userId: string): boolean;
|
|
32
|
+
private prune;
|
|
33
|
+
private enforceMaxEntries;
|
|
18
34
|
}
|
|
35
|
+
export {};
|
package/dist/weixin/bot.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Agent } from "./agent/interface.js";
|
|
|
2
2
|
import type { PendingFinalChunk } from "./messaging/quota-manager.js";
|
|
3
3
|
import type { RuntimeMediaStore } from "../channels/media-store.js";
|
|
4
4
|
import type { PerfTracer } from "../perf/perf-tracer.js";
|
|
5
|
+
import type { ActiveTurnRegistry } from "../sessions/active-turn-registry.js";
|
|
5
6
|
export type LoginOptions = {
|
|
6
7
|
/** Override the API base URL. */
|
|
7
8
|
baseUrl?: string;
|
|
@@ -31,6 +32,16 @@ export type StartOptions = {
|
|
|
31
32
|
dropPendingFinal?: (chatKey: string) => void;
|
|
32
33
|
mediaStore?: RuntimeMediaStore;
|
|
33
34
|
perfTracer?: PerfTracer;
|
|
35
|
+
/** Read the chat's current session synchronously for dispatch-time binding. */
|
|
36
|
+
peekCurrentSessionAlias?: (chatKey: string) => string | undefined;
|
|
37
|
+
/** Persist a background turn's final result for later replay. */
|
|
38
|
+
setBackgroundResult?: (chatKey: string, alias: string, result: {
|
|
39
|
+
text: string;
|
|
40
|
+
status: "done" | "error";
|
|
41
|
+
finished_at: string;
|
|
42
|
+
}) => Promise<void>;
|
|
43
|
+
/** Shared in-flight turn registry for dispatch-time foreground tracking. */
|
|
44
|
+
activeTurns?: ActiveTurnRegistry;
|
|
34
45
|
};
|
|
35
46
|
/**
|
|
36
47
|
* Interactive QR-code login. Prints the QR code to the terminal and waits
|
|
@@ -27,6 +27,10 @@ export type HandleWeixinMessageTurnDeps = {
|
|
|
27
27
|
downloadMediaFromItemFn?: typeof downloadMediaFromItem;
|
|
28
28
|
allowedMediaRoots?: string[];
|
|
29
29
|
perfTracer?: PerfTracer;
|
|
30
|
+
isForeground?: () => boolean;
|
|
31
|
+
boundSessionAlias?: string;
|
|
32
|
+
onBackgroundFinal?: (alias: string, text: string, status: "done" | "error") => Promise<void>;
|
|
30
33
|
};
|
|
31
34
|
export declare function getWeixinMessageTurnLane(full: WeixinMessage): "normal" | "control";
|
|
35
|
+
export declare function buildWeixinChatKey(accountId: string, userId: string): string;
|
|
32
36
|
export declare function handleWeixinMessageTurn(full: WeixinMessage, deps: HandleWeixinMessageTurnDeps): Promise<void>;
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { ChannelMediaKind } from "../../channels/media-types.js";
|
|
2
2
|
import type { WeixinMessage, MessageItem } from "../api/types.js";
|
|
3
|
+
interface ContextTokenRetentionOptions {
|
|
4
|
+
maxTokensPerAccount?: number;
|
|
5
|
+
tokenTtlMs?: number;
|
|
6
|
+
now?: () => number;
|
|
7
|
+
}
|
|
8
|
+
export declare function configureContextTokenRetentionForTests(options?: ContextTokenRetentionOptions): void;
|
|
3
9
|
/**
|
|
4
10
|
* Restore the per-account context-token cache from disk into memory.
|
|
5
11
|
* Called at bot startup (after login). Missing/unreadable files are tolerated
|
|
@@ -78,3 +84,4 @@ export interface WeixinInboundMediaDescriptor {
|
|
|
78
84
|
fileName?: string;
|
|
79
85
|
}
|
|
80
86
|
export declare function extractWeixinMediaDescriptors(itemList?: MessageItem[]): WeixinInboundMediaDescriptor[];
|
|
87
|
+
export {};
|
|
@@ -24,11 +24,21 @@ export interface QuotaObserver {
|
|
|
24
24
|
onFinalReserved?(chatKey: string, snapshot: QuotaSnapshot): void;
|
|
25
25
|
onFinalRejected?(chatKey: string, snapshot: QuotaSnapshot): void;
|
|
26
26
|
}
|
|
27
|
+
export interface QuotaManagerOptions {
|
|
28
|
+
maxStates?: number;
|
|
29
|
+
stateTtlMs?: number;
|
|
30
|
+
maxPendingFinalChunksPerChat?: number;
|
|
31
|
+
now?: () => number;
|
|
32
|
+
}
|
|
27
33
|
export declare class QuotaManager {
|
|
28
34
|
private readonly states;
|
|
29
35
|
private readonly observer;
|
|
30
36
|
private readonly normalizeKey;
|
|
31
|
-
|
|
37
|
+
private readonly maxStates;
|
|
38
|
+
private readonly stateTtlMs;
|
|
39
|
+
private readonly maxPendingFinalChunksPerChat;
|
|
40
|
+
private readonly now;
|
|
41
|
+
constructor(observer?: QuotaObserver, normalizeKey?: (key: string) => string, options?: QuotaManagerOptions);
|
|
32
42
|
onInbound(chatKey: string): void;
|
|
33
43
|
reserveMidSegment(chatKey: string): boolean;
|
|
34
44
|
reserveFinal(chatKey: string): boolean;
|
|
@@ -41,4 +51,8 @@ export declare class QuotaManager {
|
|
|
41
51
|
clearPendingFinal(chatKey: string): void;
|
|
42
52
|
snapshot(chatKey: string): QuotaSnapshot;
|
|
43
53
|
private getOrCreate;
|
|
54
|
+
private prune;
|
|
55
|
+
private enforceMaxStates;
|
|
56
|
+
private trimPendingFinalChunks;
|
|
57
|
+
private deleteIfEmpty;
|
|
44
58
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Agent } from "../agent/interface.js";
|
|
2
2
|
import type { PendingFinalChunk } from "../messaging/quota-manager.js";
|
|
3
|
+
import type { ActiveTurnRegistry } from "../../sessions/active-turn-registry.js";
|
|
3
4
|
import type { RuntimeMediaStore } from "../../channels/media-store.js";
|
|
4
5
|
import type { PerfTracer } from "../../perf/perf-tracer.js";
|
|
5
6
|
export type MonitorWeixinOpts = {
|
|
@@ -22,6 +23,13 @@ export type MonitorWeixinOpts = {
|
|
|
22
23
|
mediaStore?: RuntimeMediaStore;
|
|
23
24
|
allowedMediaRoots?: string[];
|
|
24
25
|
perfTracer?: PerfTracer;
|
|
26
|
+
peekCurrentSessionAlias?: (chatKey: string) => string | undefined;
|
|
27
|
+
setBackgroundResult?: (chatKey: string, alias: string, result: {
|
|
28
|
+
text: string;
|
|
29
|
+
status: "done" | "error";
|
|
30
|
+
finished_at: string;
|
|
31
|
+
}) => Promise<void>;
|
|
32
|
+
activeTurns?: ActiveTurnRegistry;
|
|
25
33
|
};
|
|
26
34
|
/**
|
|
27
35
|
* Long-poll loop: getUpdates → process message → call agent → send reply.
|