# SillyTavern migration compatibility This reference defines what the Agent RP importer preserves and what it executes. Imported data is untrusted content: preservation supports later export and migration, while execution is restricted to explicitly supported text semantics. ## Character cards | Input | Import | Runtime behavior | |---|---:|---| | Standalone Character Card V1/V2/V3 JSON | Yes | Same card semantics as PNG; original bytes remain a Session attachment and never enter model content | | Character Card V1 JSON fields in PNG `chara` metadata | Yes | Identity, description, personality, scenario, examples, and `first_mes` | | Character Card V2 in PNG `chara` metadata | Yes | V1 behavior, alternate greetings, character system prompt, post-history instructions, and character lorebook | | Character Card V3 in PNG `ccv3` metadata | Yes | V2 behavior, nickname, and V3 lorebook fields in the safe subset | | PNG containing both `ccv3` and `chara` | Yes | `ccv3` takes precedence | | Unknown card fields and `extensions` values | Yes | Preserved without entering the prompt unless a supported field owns the behavior | | Future V3 minor versions | Degraded | Imported and preserved; the result reports that future behavior may be inactive | | Independent SillyTavern World Info JSON | Yes | Session-owned bounded matching subset; original JSON and unsupported fields remain exportable | | Character Card V3 CHARX | Yes | Root `card.json`, original archive, embedded icon, background, emotion/expression images, and other card fields above; unsupported asset types remain preserved but inert | Card `system_prompt` replaces the fallback identity instruction when non-empty and supports `{{original}}`. `post_history_instructions` is appended after the Agent RP behavioral contract. V3 cards from compatible exporters may omit either prompt override; Agent RP treats an omitted value as an empty string but still rejects a present non-string value. V2 validation remains strict. `{{char}}`, ``, and `` resolve to the V3 nickname when present, otherwise the card name. Prompt assembly keeps the provider prefix reusable across append-only turns. The leading run of static system-role preset modules enters the provider system field; extraction stops at the first user- or assistant-role module so authored role order is preserved. Active World Info, mutable state, Agent state-settlement guidance, Tavern Helper before-history injections, and preset modules containing supported turn-dependent macros or EJS are materialized after retained chat history. The current state is emitted once as the final after-history module rather than persisted as a changing DSH runtime-context snapshot, so a state update cannot move the first request difference ahead of earlier stable content and old state copies do not accumulate in later requests. A native card containing turn-dependent syntax keeps a stable identity in the provider system field and places its fully rendered card prompt at the same tail boundary. Other static preset modules retain their original side of `chatHistory`, in-chat depth injections retain their requested depth, and a preset without an enabled `chatHistory` marker retains its complete original module order. A continuous request-tail chain of assistant tool calls and their tool results stays intact; after-history and in-chat modules are inserted before the complete chain rather than between its transactions. Display-regex output containing a complete HTML document runs in the isolated light-frontend frame even when a card labels its Markdown fence as `text` instead of `html`; ordinary fenced markup examples remain inert. Sanitized inline frontends retain inert author-defined wrapper elements when the same output contains presentational HTML, so CSS selectors keep their intended containers without enabling author scripts, event attributes, or additional network access. Message routing is defined by the shared [message display plan](message-display-architecture.md), which keeps the current iframe runtime available while a future DSH text-body extension can add a parallel native adapter. Collapsed greeting choices do not expose frontend source and create the preview frame only after expansion. An active light frontend may read the current card's bounded greeting list and replace only the first assistant message with one of those exact greetings; it does not receive the rest of the Session transcript or a general mutation channel. Its `createChatMessages` facade accepts only one non-empty user message at the end of the Session. The registered compatibility frame must still have browser-owned user activation when the Host receives the request; a successful append grants the same frame one short-lived `/trigger`, allowing action buttons to save before generation without allowing load-time scripts to create messages or start the model. Successful local snapshot updates use the active Persona name and synthetic card-local indexes rather than claiming to mirror the complete Host transcript. Each mounted frame receives an opaque registration used to authenticate its resize reports and declared capability requests; unknown, unregistered, or source-mismatched requests fail closed. It also reports only fixed lifecycle states such as DOM readiness, visible-content presence, document replacement, and coarse runtime failure, allowing local acceptance tools to diagnose a blank frame without reading card content. Opaque-origin frames receive bounded in-memory `localStorage` and `sessionStorage` facades so UI caches cannot force same-origin access; these stores survive a same-frame document rewrite but are not Host or browser-profile storage. The common `getVariables`, `replaceVariables`, `updateVariablesWith`, and `insertOrAssignVariables` APIs read bounded Session snapshots and replace only the named global, preset, character, chat, or message namespace through that registered frame. A frame that requests an unapproved HTTPS resource reports only its normalized origin and CSP resource class. The player approves script, style, font, image, media, embedded-page, and data-connection access independently before the frame is recompiled; dynamically inserted second-stage resources use the same flow and never inherit approval from another class. A per-card compatibility test mode persistently allows every HTTPS resource class and string-to-code evaluation inside the isolated frame to avoid staged prompts while adapting a trusted card. Approved embedded HTTPS pages inherit the outer sandbox restrictions; compatibility mode does not add same-origin, Host DOM, or file access and can be returned to prompted mode from the character details. Character-library World Info bodies are fetched in bounded pages only after the read-only section opens, so selecting a card does not transfer or mount every embedded entry. Tavern Helper creates its hidden displayed-message DOM only when a script calls `retrieveDisplayedMessage` or `refreshOneMessage`; scripts that use only variables, events, buttons, or generation APIs do not duplicate the transcript or trigger its embedded resources. ## Character lorebooks Enabled entries support constant activation, literal or regular-expression primary and secondary keys, selective conditions, case sensitivity, scan depth, insertion order, `before_char` and `after_char` placement, priority, and token budget. Each active entry enters the prompt once. EJS in an otherwise compatible active entry is rendered before token accounting. The current subset supports `<% %>`, `<%= %>`, `<%- %>`, comments, whitespace slurping, conditions, loops, `print`, character and user names, the primary character-world name through `charLoreBook`, role-aware recent-message metadata and readers, `variables`, `stat_data`, read-only `getvar` aliases, deterministic JSON-data Lodash helpers, YAML serialization, replayable `Date`, and bounded reads of plain Session-owned World Info through `getwi` or `getWorldInfo`. Promises that settle entirely inside the isolated runtime may be awaited. The same renderer is used for model-facing character fields and imported preset modules. Host-backed async APIs, includes, variable writes, nested World Info template evaluation, dynamic World Info activation, prompt injection, and `@@` decorators remain preserved but inactive. The exact matrix is documented in [EJS compatibility](ejs-compatibility.md). ## Independent World Info SillyTavern World Info JSON with a top-level `entries` object or array is accepted. Enabled entries support constant activation, literal or regular-expression primary and secondary keys, all four `selectiveLogic` modes, case sensitivity, whole-word matching for literal keys, per-entry scan depth, insertion order, and before/after-character positions. Imported books remain Session-owned and combine with an imported card's embedded lorebook. Decorators, probability, vector matching, timed effects, recursive controls, character-field matching, and advanced insertion positions remain preserved but inert. The importer does not execute a partially supported entry when its unsupported fields would change whether or where it activates. Regular-expression keys run in one isolated QuickJS context per World Info inspection pass. The matcher enforces limits on input length, pattern length, evaluation count, memory, stack, and interpreter work; it exposes no Host JavaScript objects, files, network, process, modules, or browser APIs. An invalid, interrupted, or over-limit expression skips only its entry and receives a distinct status in the World Info inspector. When the isolated runtime cannot start, patterns equivalent to literal substring lookups still work and other regex entries report that the runtime is unavailable instead of being described as an unsupported feature. Each source book keeps its own cap, then every active book shares a player-adjustable Session budget (4096 tokens by default); priority decides which matched entries survive that final cap. `ignoreBudget` can bypass a source-book cap but not the Session safety cap. Token budgeting uses a deterministic local estimate; it does not claim byte-for-byte parity with a SillyTavern model tokenizer. Prompt assembly, the World Info management projection, and private-card auditing call the same `native-v0` world-engine adapter. Its full result retains prompt contributions inside the Session runtime, while acceptance diagnostics expose only entry, activation, exclusion, reason, template, and token counts. ## Standalone regex packs A UTF-8 `.json` export of at most 2 MiB containing one SillyTavern regex rule or an ordered array of up to 256 rules can be imported independently in the Agent RP resource center. A pack is not attached to a Character Card or disguised as a Chat Completion preset. “Start roleplay” can select up to 16 packs; pack order and rule order are preserved, and the resulting global-scope program runs before preset and Character Card regexes. The selected content is copied into the Session log. Editing or removing the reusable library copy therefore does not alter an existing Session, and prompt rules cannot become active in unrelated Sessions. Display rules transform only the rendered view, prompt rules transform only the model-facing copy, and shared rules participate in both according to the imported SillyTavern flags. Tavern Helper exposes the selected rules through `getTavernRegexes({ type: 'global' })` and the legacy `{ scope: 'global' }` query. ## Tavern Helper scripts and MVU Enabled global, character, and preset scripts run in separate browser sandbox frames without same-origin access to the Host. The Host keys frames, variables, injected prompts, buttons, errors, and generation queues by script-tree scope plus the original script id, while the public API continues to expose the original id. Scripts with the same id in different scopes therefore remain independent. The compatibility runtime provides Session-scoped variables, events, script buttons and metadata, display-message refresh, regex and script-tree readers and writers, World Info access, prompt injection, a typed and bounded isolated-popup capability, approval-gated generation APIs, and two Host-persistent JSON surfaces. Localforage values are isolated by character, preset, script-tree scope and script id. The legacy `extension_settings` object is shared only among scripts in the same character/preset script tree and is isolated from other cards, presets, and tree scopes. Each legacy unscoped namespace and the former browser-global settings object can be claimed only once by their first requesting installation; data is copied without deletion, overwrite, or later resurrection after a scoped clear, and later installations start empty. Its DOM subset includes indexed jQuery-style collections, common traversal, visibility, data, event, and sizing methods, plus a hidden mirror of the current messages for display readers and bounded override reporting. Script failures are isolated and reported by script name and, when available, line and column without copying source URLs, stacks, or source into diagnostics. Classic scripts execute directly. A side-effect dependency with no nested imports or ESM exports executes in its own classic scope before the entry script; its `window`, `parent`, and `top` references resolve to a restricted facade of the script frame rather than the Host. Other ESM keeps its original module boundaries and may use static imports or literal dynamic imports with complete HTTPS URLs from built-in or player-approved origins. Non-literal, relative, bare, non-HTTPS, oversized, and unapproved root imports fail before execution. Each Tavern execution document uses an opaque-origin `data:` navigation with exactly `allow-scripts allow-same-origin allow-forms`; the `allow-same-origin` token does not give that data document the DSH origin, Host DOM, or browser storage. The navigation carries only a small loader shell. After that shell proves it is the registered frame with a `null` opaque origin, the Host delivers the runtime program and private Session snapshot through `postMessage`; neither value is copied into the navigation URL. A frame that does not finish this handshake within 15 seconds reports a startup timeout instead of remaining in `booting` indefinitely, and a later valid ready message recovers it. Network fetch and parent-page access remain disabled in the execution document, and module loading is restricted by its script policy. Static HTTPS image, stylesheet, and child-frame origins discovered in inspected scripts require independent approvals scoped to the current character, preset, script-tree scope, and script. Preflight and active Sessions derive pending module, image, stylesheet, and child-frame grants through the same resource plan and normalize library-backed attachment ids to the stable character or preset library owner, so a grant accepted before launch is not requested again after launch. The Character Library can apply the exact displayed grants only to the new Session through browser-tab storage or remember them for the same card and preset, and either choice launches from the same confirmation instead of requiring an approval round trip before the start action. The active runtime derives all ten permission categories from one deduplicated plan; module, image, stylesheet, and child-frame requests are startup resources, while runtime-discovered font, identity, external-window, standard-generation, custom-generation, and model-list requests are interaction work and do not imply that the Session is still loading. Content-free diagnostics verify both category and lifecycle totals against the displayed total. Each registered script frame also reduces CSP violations to a supported resource class and an exact public HTTPS origin, reports at most 64 unique pairs, and rejects paths, credentials, unsupported directives, extra fields, and messages from any other window. A font referenced only after an approved stylesheet loads becomes an exact script-owned font request; approving it adds only that origin to `font-src` and rebuilds the affected sandbox. Host diagnostics retain only aggregate blocked-resource counts, distinct-origin counts, and resource-class counts; they do not serialize the origin, script identity, URL, or source content. An approved child frame keeps its own HTTPS origin, browser storage, and forms while remaining unable to reach the Host. Each script resolves and starts independently, so one slow, rejected, or permission-blocked dependency does not postpone unrelated script frames. Pending permissions share one Session control and open a central permission view instead of occupying one control per request. Approving an origin restarts only the affected script frame and preserves the readiness state of unrelated scripts. The script document provides a local `#extensionsMenu`; entries registered through the standard SillyTavern extension menu appear inside the existing script panel instead of being injected into the DSH page. The public mobile-phone readiness marker also enables one Host-owned trigger and a persistent `小手机` action in the Session controls. The Host uses the module's direct open API when available, preserves an already-open phone while its Host panel is hidden, and retains pointer and click fallbacks for older draggable launchers. The marked frame receives bundled SVG fallbacks for common Font Awesome classes and small scoped compatibility rules for its light form controls and launcher spacing. The dependency may restyle and bind the trigger but cannot use it to reach the DSH page. MVU initialization recognizes both `` content and ordered `[initvar]` lorebook entries. The Host provides the public `Mvu` variable read and replacement APIs and canonical lifecycle events, and persists listener changes made during initialization or completed updates. Model-facing state and settlement rules resolve `{{user}}`, `{{char}}`, and their legacy tag aliases from the turn's snapshotted identities without rewriting persisted state. Remaining brace text in JSON strings keeps its JSON meaning without entering DSH prompt-variable interpolation. A public MagVarUpdate bundle imported only for its side effects is replaced by this Host capability; its SillyTavern settings panel and parent-page UI are not mounted. The common MVU-Zod path receives fixed YAML and Zod browser modules when its inspected dependency source requires those globals. ## Security and degradation Importing never executes lorebook regular expressions, decorators, unknown extension code, or unsupported assets. During prompt assembly and inspection, regular-expression keys and the supported EJS subset run in separate bounded QuickJS contexts. Neither runtime receives Node globals, modules, files, network APIs, live wall-clock time, unseeded randomness, browser objects, or Host callbacks; EJS receives only the recorded Session-boundary time and per-turn replay seed. A template or regex failure excludes only that prompt module or lorebook entry and reports a stable category without copying private source into diagnostics. Tavern Helper execution is limited to the isolated browser runtime above. Imported replacement rules run only in Agent RP's isolated text pipeline: display rules transform rendered message text, while prompt rules transform the model-facing copy without changing the stored transcript. Replacement strings support SillyTavern's `$&`, `$0`, `{{match}}`, numeric capture, and named capture forms. Mixed prose and sanitized inline HTML inherit the Host message theme inside their isolated frame; complete frontend documents retain their own theme. Unapproved remote assets are not fetched, and data-URL assets remain isolated inside the frame. CHARX indexes declared embedded PNG, JPEG, WebP, GIF, and AVIF images while keeping their payloads compressed until one image is requested; code, audio, video, models, fonts, and unknown asset types remain inside the preserved archive and are not executed. Asset records, group-only greetings, and unknown extensions remain in preserved raw JSON. A standalone Character Card JSON transport must be a `.json` file containing valid UTF-8; the Host stores its original bytes as an opaque attachment, so neither its bytes nor its path are sent to the model. A complete PNG, JSON, or CHARX Character Card transport is limited to 64 MiB, while its decoded card definition is limited separately to 8 MiB; embedded CHARX media does not consume the definition allowance. One CHARX may contain at most 4096 entries and expand to at most 128 MiB. Before a new Session starts, the library combines static resource plans for the selected light frontend and enabled Tavern Helper scripts. The light-frontend plan classifies directly declared scripts, styles, fonts, images, media, frames, and data connections from rendered greetings and display replacements. The Tavern plan resolves fixed module, image, stylesheet, and child-frame origins without evaluating script source. The launch control remains disabled while either plan is loading or has pending grants, and one confirmation saves every currently listed resource under its card-owned class or script-owned identity. Script grants remain scoped to the selected card, preset, script scope, script id, resource class, and origin. Concurrent plans share one in-flight fetch for an identical dependency URL while retaining independent cancellation; the underlying request stops only after every waiter cancels. Resources declared only after an approved stylesheet or script loads remain a later prompt unless the player explicitly enables the card's sandbox-only HTTPS compatibility mode. Model generation and external API calls remain separate call-time decisions. Future world engines and ecosystem extensions use the [safe extension capability protocol](extension-capabilities.md): fixed capability identifiers, explicit Session-owned state, fail-closed version and permission resolution, isolated execution, and content-free diagnostics. The protocol does not authorize arbitrary Host plugins or shared mutable globals. ## Public format sources The implementation is independent and follows public interoperability formats rather than copying SillyTavern implementation code: - [Character Card V2 specification](https://github.com/malfoyslastname/character-card-spec-v2), reviewed at `8083fb388615ccbce768e97cbbd49d2b3214632c` - [Character Card V3 specification](https://github.com/kwaroran/character-card-spec-v3), reviewed at `f3a86af019fbd99f788f7a1155f399655b34ab35` - [SillyTavern](https://github.com/SillyTavern/SillyTavern) observable PNG and chat formats, reviewed at `8172dcd0ee672d3cd9a5e5f7af134f91a45cd2b8` PNG chunk extraction uses the MIT-licensed `png-chunks-extract` package. The MIT-licensed `png-chunk-text` encoder is retained only for generated test fixtures. No SillyTavern AGPL source is included. Isolated EJS evaluation uses the MIT-licensed `quickjs-emscripten-core` and the embedded release-sync QuickJS variant. The implementation is based on public EJS syntax and observable interoperability behavior; no AGPL template-extension source is included. ESM import inspection uses the MIT-licensed `es-module-lexer`. Public MagVarUpdate and MVU-Zod behavior is implemented against their documented globals and events; their source is not included in this package.