type PushHandler = (data: unknown) => void; /** * RPC Engine — communicates with the parent window (web platform) via postMessage. * * Security model: * 1. On init, the SDK sends { type: '__charx_rpc__', method: 'sdk.init' } to the parent. * 2. The platform responds with { type: '__charx_ready__', token, origin } — a per-session * secret token that proves the parent is the legitimate platform. * 3. All subsequent outgoing RPC messages include the token. * 4. The platform validates the token on every call, rejecting unauthorised frames. * 5. Incoming messages are accepted only from window.parent (e.source check). * * Protocol (matches html-message.tsx): * iframe → parent: { type: '__charx_rpc__', iframeId, token?, id?, method, args[] } * parent → iframe: { type: '__charx_ready__', iframeId, token, origin } * parent → iframe: { type: '__charx_rpc_response__', iframeId, id, result?, error? } * parent → iframe: { type: '__charx_push__', event, data } */ declare class RpcEngine { private pending; private pushHandlers; private readonly iframeId; private readonly target; /** Session token issued by the platform after the init handshake */ private _token; /** Platform's origin — used as targetOrigin for all outgoing messages after handshake */ private _platformOrigin; /** Resolvers waiting for the __charx_ready__ handshake */ private _readyResolvers; private _handshakeDone; constructor(target?: Window); private static _genId; private handleMessage; /** * Returns a Promise that resolves when the platform handshake completes. * Used by CharxSdk.init() to wait for the session token. */ waitReady(): Promise; get iframeIdValue(): string; /** * Two-way call — returns a Promise that resolves with the platform's response. * Rejects if the platform returns an error or if `timeoutMs` elapses with no response. */ call(method: string, args?: unknown[], timeoutMs?: number): Promise; /** Fire-and-forget — no response expected. */ post(method: string, args?: unknown[]): Promise; /** Subscribe to push events sent by the platform */ push(event: string, handler: PushHandler): () => void; /** Destroy the engine — rejects all pending calls and removes the message listener */ destroy(): void; } /** * RPC method names — dot-notation strings matching the platform's handleCharxRpc switch cases. * Used as the `method` field in { type: '__charx_rpc__', method, args[] } messages. */ declare const METHODS: { readonly CHARACTER_GET_INFO: "character.getInfo"; readonly CHARACTER_GET_PERSONA: "character.getPersona"; readonly CHARACTER_GET_SETTINGS: "character.getSettings"; readonly CHAT_SEND: "chat.send"; readonly CHAT_STOP: "chat.stop"; readonly CHAT_REGENERATE: "chat.regenerate"; readonly CHAT_CONTINUE: "chat.continue"; readonly CHAT_SET_INPUT: "chat.setInput"; readonly CHAT_GET_INPUT: "chat.getInput"; readonly CHAT_SET_GREETING_INDEX: "chat.setGreetingIndex"; readonly CHAT_GET_GREETING_INDEX: "chat.getGreetingIndex"; readonly CHAT_SET_FIRST_MES: "chat.setFirstMesIndex"; /** Fetch conversation history. Platform args: [{ limit?, offset? }] */ readonly CHAT_GET_HISTORY: "chat.getHistory"; /** @deprecated Platform doesn't handle this; use CHAT_GET_HISTORY */ readonly CHAT_MESSAGE_LIST: "chat.messages.list"; readonly CHAT_MESSAGE_DELETE: "chat.messages.delete"; readonly CHAT_MESSAGE_EDIT: "chat.messages.edit"; /** @deprecated Compatibility placeholder only; current hosts may reject this call. */ readonly CHAT_MESSAGE_SWIPE: "chat.messages.swipe"; /** Silent background LLM generation — equivalent to SillyTavern generateRaw() */ readonly CHAT_GENERATE_RAW: "chat.generateRaw"; /** Inject a system prompt into the next user-facing generation call */ readonly CHAT_INJECT_PROMPT: "chat.injectPrompt"; /** Insert a system message into the conversation without triggering AI generation */ readonly CHAT_INSERT_SYSTEM_MESSAGE: "chat.insertSystemMessage"; /** Get the current conversation ID */ readonly CHAT_GET_CONVERSATION_ID: "chat.getConversationId"; readonly VARIABLE_GET: "variable.get"; readonly VARIABLE_GET_ALL: "variable.getAll"; readonly VARIABLE_SET: "variable.set"; readonly VARIABLE_SET_MANY: "variable.setMany"; readonly VARIABLE_INCREMENT: "variable.increment"; readonly VARIABLE_DECREMENT: "variable.decrement"; readonly VARIABLE_DELETE: "variable.delete"; readonly VARIABLE_CLEAR: "variable.clear"; readonly STORAGE_GET: "storage.get"; readonly STORAGE_SET: "storage.set"; readonly STORAGE_REMOVE: "storage.remove"; readonly STORAGE_CLEAR: "storage.clear"; readonly STORAGE_KEYS: "storage.keys"; readonly UI_TOAST: "ui.toast"; readonly UI_ALERT: "ui.alert"; readonly UI_CONFIRM: "ui.confirm"; readonly UI_PROMPT: "ui.prompt"; readonly UI_PANEL_OPEN: "ui.openPanel"; readonly UI_PANEL_CLOSE: "ui.closePanel"; readonly UI_CHAT_INPUT_HIDE: "ui.hideChatInput"; readonly UI_CHAT_INPUT_SHOW: "ui.showChatInput"; readonly UI_THEME_SET: "ui.setTheme"; readonly UI_SCROLL_TO_BOTTOM: "ui.scrollToBottom"; readonly MEDIA_SPEAK: "media.speak"; readonly MEDIA_SPEAK_STOP: "media.stopSpeak"; readonly MEDIA_BGM_PLAY: "media.bgm.play"; readonly MEDIA_BGM_STOP: "media.bgm.stop"; readonly MEDIA_BGM_PAUSE: "media.bgm.pause"; readonly MEDIA_BGM_RESUME: "media.bgm.resume"; readonly MEDIA_BGM_VOLUME: "media.bgm.setVolume"; readonly MEDIA_SFX_PLAY: "media.sfx.play"; readonly MEDIA_SFX_PRELOAD: "media.sfx.preload"; readonly WORLDBOOK_GET_ALL: "worldbook.getAll"; readonly WORLDBOOK_TOGGLE: "worldbook.toggle"; readonly WORLDBOOK_BATCH_TOGGLE: "worldbook.batchToggle"; readonly WORLDBOOK_SET_CONTENT: "worldbook.setContent"; readonly WORLDBOOK_CLEAR_CONTENT: "worldbook.clearContent"; readonly WORLDBOOK_RESET: "worldbook.reset"; readonly GAME_SAVE: "game.save"; readonly GAME_LOAD: "game.load"; readonly GAME_SAVE_LIST: "game.listSaves"; readonly GAME_SAVE_DELETE: "game.deleteSave"; readonly GAME_ACHIEVEMENT_UNLOCK: "game.achievements.unlock"; readonly GAME_ACHIEVEMENT_LIST: "game.achievements.list"; readonly GAME_RANKING_SUBMIT: "game.ranking.submit"; readonly GAME_RANKING_TOP: "game.ranking.getTop"; readonly GAME_RANKING_SELF: "game.ranking.getSelf"; }; /** * Push event names — platform sends `{ type: '__charx_push__', event, data }` * to iframes to notify them of state changes. */ declare const PUSH: { /** * Platform pushes this after a generation request is accepted. * Payload: `{ requestId, userMessageId?, assistantMessageId? }` */ readonly CHAT_STARTED: "chat.started"; readonly VARIABLE_CHANGED: "variable.changed"; /** Platform pushes this when a new chat message is received */ readonly CHAT_MESSAGE: "chat.message"; /** * Platform pushes streaming chunks. * Payload: `{ chunk: string; done: boolean; messageId: string | number }` * When `done === true`, the stream is complete. */ readonly CHAT_STREAM_CHUNK: "chat.stream_chunk"; /** * Platform pushes this when a chat generation fails mid-stream. * Payload: `{ error: string }` */ readonly CHAT_ERROR: "chat.error"; /** * Platform pushes this when the active conversation changes (user switches chat). * Payload: `{ conversationId: number }` * Equivalent to SillyTavern CHAT_CHANGED event. */ readonly CHAT_CHANGED: "chat.changed"; /** * Platform pushes this after a message is saved/updated (stream complete or manual edit via SDK). * Payload: `{ id: string; role: string; content: string; createdAt: number }` * Equivalent to SillyTavern MESSAGE_UPDATED event. */ readonly MESSAGE_UPDATED: "message.updated"; /** * Platform pushes this when a user manually edits a message via sdk.chat.messages.edit(). * Payload: `{ id: string; role: string; content: string; createdAt: number }` * Equivalent to SillyTavern MESSAGE_EDITED event. */ readonly MESSAGE_EDITED: "message.edited"; /** * Platform pushes this when a message is deleted via sdk.chat.messages.delete(). * Payload: `{ id: string; role: string; content: string; createdAt: number }` * Equivalent to SillyTavern MESSAGE_DELETED event. */ readonly MESSAGE_DELETED: "message.deleted"; }; /** iframe → parent RPC request */ interface RpcMessage { type: '__charx_rpc__'; iframeId: string; id?: string; method: string; args?: unknown[]; } /** parent → iframe RPC response */ interface RpcResponse { type: '__charx_rpc_response__'; iframeId: string; id: string; result: unknown; } /** parent → iframe push notification */ interface PushMessage { type: '__charx_push__'; iframeId?: string; event: string; data: unknown; } interface Message { id: string; role: 'user' | 'assistant' | 'system'; content: string; createdAt: number; tokenCount?: number; swipes?: string[]; activeSwipe?: number; variables?: Record; } interface SendOptions { onData?: (chunk: string) => void; onComplete?: (message: Message) => void; onError?: (error: Error) => void; onStart?: (payload: ChatStreamStartPayload) => void; model?: string; temperature?: number; injectedSystemPrompts?: string[]; timeoutMs?: number; requestId?: string; } /** Options for sdk.chat.generateRaw() — silent background LLM call */ interface GenerateRawOptions { /** Override the model for this call */ model?: string; temperature?: number; maxTokens?: number; /** * When true, prepend the character context (system prompt, worldbook, etc.) * from the current conversation. Defaults to true when conversationId is available. */ includeCharacterContext?: boolean; timeoutMs?: number; } /** A message passed to generateRaw() as part of the messages array */ interface GenerateRawMessage { role: 'system' | 'user' | 'assistant'; content: string; } interface ChatHistoryOptions { limit?: number; offset?: number; page?: number; pageSize?: number; } interface MessagesModule { list(params?: ChatHistoryOptions): Promise; getAll(): Promise; delete(id: string): Promise; edit(id: string, content: string): Promise; /** * @deprecated Compatibility placeholder only. * Current creator preview and web chat hosts may reject this call. * Prefer regenerate(), continue(), edit(), or explicit message state in variables. */ swipe(id: string, direction: 'left' | 'right'): Promise; } interface ChatStreamStartPayload { requestId?: string; userMessageId?: string | number; assistantMessageId?: string | number; } interface ChatStreamChunkPayload { requestId?: string; chunk: string; done: false; messageId?: string | number; assistantMessageId?: string | number; } interface ChatStreamDonePayload { requestId?: string; chunk: ''; done: true; messageId?: string | number; assistantMessageId?: string | number; fullContent?: string; interrupted?: boolean; } interface ChatErrorPayload { requestId?: string; message?: string; error?: string; code?: string; } type ChatStreamPayload = ChatStreamChunkPayload | ChatStreamDonePayload; interface ChatSendTask { requestId: string; done: Promise; stop(): Promise; } interface CharacterInfo { id: string | number; name: string; avatar?: string; displayIntro?: string; firstMes?: string; alternateGreetings?: string[]; tags?: string[]; creator?: string; version?: string | number; } interface PersonaInfo { id?: string | number; name?: string; avatar?: string; callName?: string; gender?: string; description?: string; } interface CharacterSettings { model?: string | null; } type VariableMap = Record; type Unsubscribe = () => void; interface ToastOptions { text: string; type?: 'success' | 'error' | 'warning' | 'info'; duration?: number; icon?: string; } /** ToastOptions 或可直接展示的简短文本值。 */ type ToastInput = ToastOptions | string | number | boolean; interface PanelOptions { url: string; title?: string; mode?: 'fullscreen' | 'sidebar' | 'modal'; width?: number | string; } /** PanelOptions 或扩展面板 URL 简写。 */ type PanelInput = PanelOptions | string; interface AlertOptions { message: string; title?: string; confirmText?: string; } /** AlertOptions 或提示文案简写。 */ type AlertInput = AlertOptions | string; interface ConfirmOptions { message: string; title?: string; confirmText?: string; cancelText?: string; } /** ConfirmOptions 或确认文案简写。 */ type ConfirmInput = ConfirmOptions | string; interface PromptOptions { message: string; title?: string; placeholder?: string; defaultValue?: string; } /** PromptOptions 或提示文案简写。 */ type PromptInput = PromptOptions | string; interface SpeakOptions { voice?: 'character' | 'system' | string; speed?: number; pitch?: number; } interface BgmOptions { loop?: boolean; volume?: number; fadeIn?: number; } interface BgmStopOptions { fadeOut?: number; } interface SfxOptions { volume?: number; } interface SaveSlot { slot: number; description?: string; savedAt: number; snapshot?: Record; } interface Achievement { id: string; name: string; description: string; unlockedAt?: number; } interface RankEntry { userId: string; username: string; score: number; rank: number; } interface WorldbookEntryState { entryKey: string; comment: string; /** The full text content of the entry — now included in getAll() response */ content: string; /** Trigger keywords for this entry */ keys: string[]; enabled: boolean; baseEnabled: boolean; isOverridden: boolean; constant: boolean; insertionOrder: number; } interface ConversationWorldbookResponse { conversationId: number; characterId: number; entries: WorldbookEntryState[]; totalCount: number; enabledCount: number; } type ChatStatus = 'idle' | 'sending' | 'streaming' | 'error'; interface UseChatReturn { messages: Message[]; status: ChatStatus; error: Error | null; currentRequestId: string | null; currentAssistantMessageId: string | null; send: (message: string, options?: SendOptions) => void; sendStream: (message: string, options?: SendOptions) => ChatSendTask; stop: () => Promise; regenerate: () => Promise; continue: () => Promise; deleteMessage: (id: string) => Promise; editMessage: (id: string, content: string) => Promise; /** * @deprecated Compatibility placeholder only. * Current creator preview and web chat hosts may reject this call. */ swipeMessage: (id: string, direction: 'left' | 'right') => Promise; generateRaw: (messages: GenerateRawMessage[], options?: GenerateRawOptions) => Promise; injectPrompt: (content: string) => Promise; insertSystemMessage: (content: string) => Promise; getConversationId: () => Promise; loadMore: () => Promise; hasMore: boolean; } declare class ChatModule { private rpc; readonly messages: MessagesModule; constructor(rpc: RpcEngine); private static genRequestId; /** * Send a message to the character. * * The platform processes the request through its normal chat flow. * `onData` fires for each streaming chunk; `onComplete` fires when the * stream finishes; `onError` fires if the generation fails. */ send(message: string, options?: SendOptions): void; stop(): Promise; regenerate(): Promise; continue(): Promise; getInput(): Promise; setInput(text: string): Promise; /** Set the active greeting index before the first user message is sent */ setGreetingIndex(index: number): Promise; /** Get the current active greeting index */ getGreetingIndex(): Promise; /** @deprecated Use setGreetingIndex(index) */ setFirstMesIndex(index: number): Promise; sendStream(message: string, options?: SendOptions): ChatSendTask; /** * Inject a system prompt into the next user-facing generation call. * Equivalent to SillyTavern injectPrompts(). * The injected prompt is cleared automatically after the next chat.send(). */ injectPrompt(content: string): Promise; /** * Insert a system message into the conversation without triggering AI generation. * Equivalent to SillyTavern createChatMessages() with role=system. */ insertSystemMessage(content: string): Promise; /** Get the current conversation ID */ getConversationId(): Promise; /** * Subscribe to conversation switch events. * Fires when the user navigates to a different conversation. * Equivalent to SillyTavern CHAT_CHANGED event. */ onChatChanged(callback: (payload: { conversationId: number; }) => void): Unsubscribe; /** * Subscribe to message-updated events. * Fires when an assistant message is fully saved (stream complete) or updated. * Equivalent to SillyTavern MESSAGE_UPDATED event. */ onMessageUpdated(callback: (message: Message) => void): Unsubscribe; /** * Subscribe to message-edited events. * Fires when a message is edited via sdk.chat.messages.edit(). * Equivalent to SillyTavern MESSAGE_EDITED event. */ onMessageEdited(callback: (message: Message) => void): Unsubscribe; /** * Silent background LLM generation — equivalent to SillyTavern generateRaw(). * Calls the AI model without adding any messages to the chat history. * When includeCharacterContext is true (default), the character's system prompt * and worldbook are included in the request context. */ generateRaw(messages: GenerateRawMessage[], options?: GenerateRawOptions): Promise; /** * Subscribe to message-deleted events. * Fires when a message is deleted via sdk.chat.messages.delete(). * Equivalent to SillyTavern MESSAGE_DELETED event. */ onMessageDeleted(callback: (message: Message) => void): Unsubscribe; } declare class CharacterModule { private rpc; constructor(rpc: RpcEngine); getInfo(): Promise; getPersona(): Promise; getSettings(): Promise; } declare class VariableModule { private rpc; private observedValues; constructor(rpc: RpcEngine); getAll(): Promise; get(key: string): Promise; set(key: string, value: T): Promise; setMany(vars: VariableMap): Promise; increment(key: string, delta?: number): Promise; decrement(key: string, delta?: number): Promise; delete(key: string): Promise; clear(): Promise; /** * Watch a single variable for changes pushed from the platform. * Requires the platform to send `{ type: '__charx_push__', event: 'variable.changed', data: { key, value } }`. */ watch(key: string, callback: (value: T, oldValue: T | undefined) => void): Unsubscribe; /** * Watch all variables — callback receives the full variable map on any change. * Re-fetches the complete map after each push event so the callback always * receives a consistent snapshot (the push payload is a single { key, value }, * not the full map). */ watchAll(callback: (vars: VariableMap) => void): Unsubscribe; } declare class StorageModule { private rpc; constructor(rpc: RpcEngine); set(key: string, value: T): Promise; get(key: string): Promise; getOrDefault(key: string, defaultValue: T): Promise; has(key: string): Promise; remove(key: string): Promise; clear(): Promise; keys(): Promise; } declare class UIModule { private rpc; constructor(rpc: RpcEngine); toast(options: ToastInput): Promise; alert(options: AlertOptions): Promise; alert(message: string): Promise; confirm(options: ConfirmOptions): Promise; confirm(message: string): Promise; prompt(options: PromptOptions): Promise; prompt(message: string, defaultValue?: string): Promise; openPanel(options: PanelInput): Promise; closePanel(): Promise; hideChatInput(): Promise; showChatInput(): Promise; setTheme(theme: 'dark' | 'light' | 'system'): Promise; scrollToBottom(): Promise; } declare class BgmController { private rpc; constructor(rpc: RpcEngine); play(url: string, options?: BgmOptions): Promise; stop(options?: BgmStopOptions): Promise; pause(): Promise; resume(): Promise; setVolume(volume: number): Promise; } declare class SfxController { private rpc; constructor(rpc: RpcEngine); play(url: string, options?: SfxOptions): Promise; preload(urls: string[]): Promise; } declare class MediaModule { private rpc; readonly bgm: BgmController; readonly sfx: SfxController; constructor(rpc: RpcEngine); speak(text: string, options?: SpeakOptions): Promise; stopSpeak(): Promise; } declare class GameAchievementsController { private rpc; constructor(rpc: RpcEngine); unlock(achievementId: string): Promise; list(): Promise; } declare class GameRankingController { private rpc; constructor(rpc: RpcEngine); submit(score: number, options?: { category?: string; }): Promise; getTop(options?: { limit?: number; category?: string; }): Promise; getSelf(options?: { category?: string; }): Promise; } declare class GameModule { private rpc; readonly achievements: GameAchievementsController; readonly ranking: GameRankingController; constructor(rpc: RpcEngine); save(options: { slot: number; description?: string; }): Promise; load(options: { slot: number; }): Promise>; listSaves(): Promise; deleteSave(slot: number): Promise; unlockAchievement(achievementId: string): Promise; listAchievements(): Promise; submitScore(score: number, options?: { category?: string; }): Promise; getTop(options?: { limit?: number; category?: string; }): Promise; getSelfRank(options?: { category?: string; }): Promise; } /** * Shared lightweight Markdown → HTML renderer. * Handles: bold+italic, bold, italic, inline code, line breaks. * All input is HTML-escaped first to prevent XSS. * Exported so MdRenderer and other consumers share one implementation. */ declare function renderMarkdown(text: string): string; declare class UtilsModule { private _rpc; constructor(_rpc: RpcEngine); clipboard: { writeText: (text: string) => Promise; readText: () => Promise; }; format: { /** Convert basic Markdown to safe HTML (bold, italic, inline code, line breaks). */ markdown: typeof renderMarkdown; /** Escape arbitrary text so it is safe to inject into innerHTML. */ sanitize: (html: string) => string; }; } declare class WorldbookModule { private rpc; constructor(rpc: RpcEngine); /** 获取当前会话所有世界书条目状态 */ getAll(): Promise; /** 启用某个条目 */ enable(entryKey: string): Promise; /** 禁用某个条目 */ disable(entryKey: string): Promise; /** 批量设置多个条目启用状态 */ batchToggle(entries: Array<{ entryKey: string; enabled: boolean; }>): Promise; /** 覆盖某个条目的内容(仅对当前会话有效) */ setContent(entryKey: string, content: string): Promise; /** 清除某个条目的内容覆盖(恢复模板内容) */ clearContent(entryKey: string): Promise; /** 重置所有覆盖(恢复全部条目到模板默认状态) */ reset(): Promise; } declare class CharxSdk { private static instance; private rpc; private _ready; private _chat?; private _character?; private _variable?; private _storage?; private _ui?; private _media?; private _game?; private _utils?; private _worldbook?; private constructor(); static getInstance(): CharxSdk; get isReady(): boolean; /** * Initialize the SDK. * Sends sdk.init to the platform and waits for a __charx_ready__ handshake * response containing a session token. All subsequent RPC calls carry this * token — the platform rejects calls from frames that skipped the handshake. * * Throws if the platform does not respond within 8 seconds (not embedded in * the platform, or running in a non-iframe context). */ init(): Promise; getRpc(): RpcEngine; get chat(): ChatModule; get character(): CharacterModule; get variable(): VariableModule; get storage(): StorageModule; get ui(): UIModule; get media(): MediaModule; get game(): GameModule; get utils(): UtilsModule; get worldbook(): WorldbookModule; } export { type Achievement, type AlertInput, type AlertOptions, type BgmOptions, type BgmStopOptions, type CharacterInfo, type CharacterSettings, CharxSdk, type ChatErrorPayload, type ChatHistoryOptions, type ChatSendTask, type ChatStatus, type ChatStreamChunkPayload, type ChatStreamDonePayload, type ChatStreamPayload, type ChatStreamStartPayload, type ConfirmInput, type ConfirmOptions, type ConversationWorldbookResponse, METHODS as EVENT, type GenerateRawMessage, type GenerateRawOptions, METHODS, type Message, type MessagesModule, PUSH, type PanelInput, type PanelOptions, type PersonaInfo, type PromptInput, type PromptOptions, type PushMessage, type RankEntry, type RpcMessage, type RpcResponse, type SaveSlot, type SendOptions, type SfxOptions, type SpeakOptions, type ToastInput, type ToastOptions, type Unsubscribe, type UseChatReturn, type VariableMap, type WorldbookEntryState };