/** One historical conversation for a chat. */ export interface ChatSessionRecord { sessionId: string; /** Short human label (usually the first prompt). */ title: string; createdAt: number; lastActiveAt: number; workDir: string; } export interface ChatBinding { channel: string; chatKey: string; chatType: "p2p" | "group"; /** The active session id for this chat. */ sessionId: string; ownerKey: string; createdAt: number; lastActiveAt: number; /** Per-chat UI language override (`zh` / `en`); falls back to the plugin config. */ language?: "zh" | "en"; /** Per-chat notification level for streaming replies; falls back to the plugin config. */ notifyLevel?: "full" | "important" | "result"; /** Per-chat proactive progress-notice interval ms (0 disables); falls back to the plugin config. */ progressTimeoutMs?: number; /** Per-chat auto-compaction switch; falls back to the plugin config. */ autoCompact?: boolean; /** Per-chat auto-compaction threshold percentage; falls back to the plugin config. */ autoCompactThresholdPct?: number; /** Timestamp when the first-time welcome card was sent (undefined = not yet welcomed). */ welcomedAt?: number; /** Web mirror session id (shared with DSH Web for viewing). */ webMirrorSessionId?: string; /** Session lock status: which channel currently owns write access. */ lockOwner?: "feishu" | "web"; /** Timestamp when the lock was acquired (for timeout detection). */ lockAcquiredAt?: number; /** Lock timeout in milliseconds (default 5 minutes). */ lockTimeoutMs?: number; /** Queued messages waiting for lock release (Web → Feishu bridge). */ queuedMessages?: Array<{ text: string; senderKey: string; timestamp: number; replyRef?: string; images?: readonly string[]; files?: readonly string[]; /** Source channel of the message; preserved so replay routes to the right runner. */ channel?: string; }>; /** Every conversation ever opened in this chat, oldest last. */ sessions: ChatSessionRecord[]; } /** Callback type for binding change events. */ export type BindingChangeCallback = (binding: ChatBinding, changeType: "add" | "update" | "delete") => void; export declare class BindingStore { private readonly map; private readonly file; private readonly changeListeners; private readonly logger?; /** Whether the last write attempt failed — see {@link save}. */ private writeFailed; constructor(stateDir?: string, logger?: { warn?: (...args: unknown[]) => void; }); /** * Register a listener for binding changes. * @param callback - Function called when any binding is added, updated, or deleted * @returns Unsubscribe function */ onChange(callback: BindingChangeCallback): () => void; /** * Emit a change event to all registered listeners. */ private emitChange; /** * Iterate over all bindings in the store. * @returns Iterator of all ChatBinding entries */ entries(): IterableIterator; /** * Get all bindings as an array. */ list(): ChatBinding[]; /** * Find bindings by webMirrorSessionId. * @param sessionId - The session ID to search for * @returns Array of bindings that have this sessionId as their webMirrorSessionId */ findByWebMirror(sessionId: string): ChatBinding[]; get(channel: string, chatKey: string): ChatBinding | undefined; put(binding: ChatBinding): void; delete(channel: string, chatKey: string): void; private load; /** * Persist the map. Never throws — every live binding already works in memory, * and `put` is called on the hot path of each message, so a failure must not * take the message down with it. * * What it must not do is be *silent* about it. This file is the only record of * which session a chat is talking to, so a failed write means the next boot * cannot resume any of these conversations: the user sends a message into a * chat that looks fine and gets a brand-new session with no history. Nothing * observable goes wrong until then, which is why the one report available — a * log line naming the unwritable path — has to happen here. * * Reported on the *transition* rather than per call: the state dir is either * writable or it is not, so a per-call warning would emit one line per message * and bury itself. */ private save; } //# sourceMappingURL=binding.d.ts.map