8000 GitHub - ziptofaf/inkbound: A local, image-enhanced interactive fiction app. Can either serve as a project management tool for a video game or a full blown narrative system. · GitHub
[go: up one dir, main page]

Skip to content
ziptofafPublic

About

A local, image-enhanced interactive fiction app. Can either serve as a project management tool for a video game or a full blown narrative system.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Inkbound

A local, image-enhanced interactive fiction app. React + Vite frontend, Express backend, and project files stored on disk.

Security: Inkbound has no accounts or login. It is meant for a single user on their own machine or a trusted private network. Do not expose it directly to the public internet; if you need remote access, put it behind a VPN or an authenticating reverse proxy.

Quick start: cp .env.example .env (optional), then docker compose up -d --build --wait --wait-timeout 180 and open http://localhost:3000. See Run.

Gallery cards, character portraits and image-reference pickers use lazy-loaded WebP thumbnails (640 px cards, 160 px miniatures). Hover/focus previews, opened images and AI references use the full-resolution originals. Derivatives are generated on demand and cached in each project’s .thumbnails/ folder; deleting that cache is safe. Existing artwork needs no migration or regeneration. The media route looks each image up by file in the project database instead of reading the whole project.

World mode (round 2)

World → Attributes Table defines the setting's character sheet: number, text or comma-separated list attributes, each with a name, optional unit/example and a default value. Characters → Attributes shows editable stat cards between Descriptions and Visuals. Character overrides persist independently of defaults; Reset inherits the current default. Deleting a definition removes its overrides. The optional magic starter set adds daily mana production (0 EMS/day) and magic affinity (empty list); other settings can define their own statistics. Definitions and values are stored in dedicated relational tables, including individual list items.

The narrator receives the definitions and effective values alongside character records, can query get_character_attributes, and can change existing values with set_character_attributes. Only the UI can change definitions. Update from story can propose attribute changes with evidence for review and undo; shared-world locks also protect attributes. These are stable capabilities, not automatically depleted resources.

Timeline (after World) provides a setting-wide calendar, independent main/side-story clocks, dated events and archive links. See Calendar and timeline for setup, narrator tools and the backed-up migration of existing projects.

The atlas supports multiple editable 3D planets, procedural palettes and terrain, sculpting, geographic markers, and kilometre distances. See Planet atlas for editing and migration details.

New projects ask for 5, 10, or 20 major locations and an optional map seed. In World, use Generate all regions for a complete world, or select a location and click Generate complete region to fill just that region. Both automatically generate missing descriptions/subareas, 5–10 key residents, a location image, and a concept for every resident. Text steps run before artwork; existing descriptions, characters and images are preserved. Jobs run on the server and survive browser refresh/navigation. Progress is saved after each step. Stop pauses safely; Resume generation retries unfinished work after a stop or provider error. If the server itself restarts, the job becomes resumable rather than automatically starting provider requests. Image generation uses the existing provider settings, references, 2 MP ComfyUI cap, cost ledger, and shared-host model swapping.

Individual controls remain available: Generate image / Generate another image saves new artwork; Generate description / Rewrite description generates text and subareas, keeping existing art and characters. Rewriting existing text asks for confirmation. Generate outline only lets you review location names before completing a region or world.

The atlas uses seeded terrain, fixed coordinates and an undirected route network. It is a schematic map rather than an AI-painted geographical image. Select a location to inspect its record, subareas and current residents. Travel is allowed along a connecting route; choose a subarea after arrival. Character records have stable IDs, separate home/current locations, appearance, personality, backstory, goals, knowledge and alive/dead/missing condition. Move character to changes their grouping without changing their origin or moving/breaking artwork. Initial NPC generation instructs the narrator that residents have never left home; review generated text for consistency.

Worlds can grow beyond their initial size. World → Atlas & records → New major location adds a city, wilderness or realm with your own description and an optional travel connection (including portals). It makes no AI request. Select a nested place and use Move this place inside to relocate it under any other region or place; all children, stable IDs and artwork are preserved. Cycles, duplicate sibling names and overflowing the 200-place limit are rejected before saving. Current main/active-side-story location pointers and accompanying characters follow a moved subtree; historical dialogue and archived story locations are unchanged. Other residents can be moved individually from Characters → Descriptions → Move character to, or their World record; home locations stay unchanged. The Areas tab links to the matching atlas record.

The narrator has create_location, move_character and move_area alongside create_subarea. Creation reuses an existing major location with the same name, and movement uses existing stable IDs rather than recreating records. These tools are available in main and side stories, but shared mutations stay disabled while an Update from story checkpoint holds the world lock.

Narration receives bounded local world data and a session summary, plus the existing beginning/end story window. Read-only get_location, get_character and search_world tools retrieve bounded records and paginated search results. search_world combines local Nemotron semantic retrieval with keyword/name/alias search; get_search_result optionally expands one matching source. See Project search and background indexing. World retrieval remains available when image tools are disabled. Historical entries retain their location; the narrator is instructed to distinguish private character knowledge from world truth and to leave player movement to the application's travel controls. These are narrative instructions, not a mechanical simulation of NPC knowledge.

Update from story reads new or edited text entries, builds a session summary and proposes updates to affected records one at a time. In World journal, inspect the replacement fields and expand their source evidence before applying. No canonical facts change until Apply updates. Applied updates are checkpointed, and immediate Undo last update restores the changed fields and summary. Generated lists and nested text notes are normalized before review. Invalid or oversized fields get at most one targeted repair, shared with evidence repair; no stored facts are silently truncated. If repair still fails for a character, location or lore entry, other valid proposals survive. The rejected reply is retained for inspection in the review and, after applying, in the journal event. Affected story entries remain pending for retry. World/story changes invalidate stale proposals and prevent unsafe undo. Character movement is an explicit control; reconciliation does not invent travel or rename entities. Removing old story text does not automatically undo already-applied world facts: edit records if you rewrite established history.

World data lives in each project's database (world, locations, areas, routes and worldEvents, plus structured characters). Location artwork lives in Locations/<stable-location-id>/; character concepts retain their browsable named folders. This implementation provides persistent world records and on-demand retrieval, not autonomous off-screen simulation. Existing round-1 projects can still open without world mode; create a new project to use the atlas.

Run

Docker Compose (including local embeddings)

From a fresh checkout with Docker Compose installed:

docker compose up -d --build --wait --wait-timeout 180

Open http://localhost:3000, then configure narration/image providers in AI Settings. No credential file is required to start. Settings, keys entered in the UI, databases and artwork persist in the inkbound-data Docker volume; docker compose down keeps it, while down -v deletes it. Existing local data/ is not imported automatically.

Compose builds two services: Inkbound and a CPU-only embedding server with two selectable models, Harrier 270M Q8_0 (default) and Nemotron 3.5 Embed 1B Q6_K. The first build downloads approximately 1.2 GiB of pinned, checksum-verified weights and compiles a pinned llama.cpp revision. These layers are cached for later builds. The model is included in the resulting image; normal starts need no Hugging Face connection. Native ARM64 (including Raspberry Pi 5) and x86-64 builds use the same Dockerfile. The embedding server is compiled for the CPU of the machine that builds it, so build the image where it will run rather than copying it to another machine. Allow several minutes for the first build. The embedding service uses about 650 MiB RAM with Harrier or 2 GiB with Nemotron, and four CPU threads by default; set INKBOUND_EMBEDDING_THREADS=2 before Compose to reduce contention. Its HTTP port is private to the Compose network.

The app automatically indexes project records and story text in the background, with resumable derived data stored in SQLite. Search project in the top bar opens the search browser and indexing controls. Narration uses the same hybrid engine through search_world; get_search_result can read one longer source on demand. GET /api/embeddings/status reports model readiness without inference. AI settings → Search memory → Embeddings switches model after a confirmation, then deletes and rebuilds every project's search memory. The client applies each model's tested prefixes and unit normalization. Chunks are split again if the model rejects their token length. A different model identity invalidates old vectors automatically. See search details.

Configuration goes in a .env file next to compose.yaml; copy .env.example for the full list. Cloud API keys can be set there (OPENROUTER_API_KEY, OPENAI_API_KEY, ANTHROPIC_API_KEY) or entered in AI settings. .env is ignored by Git.

By default the app is published only on 127.0.0.1. To reach it from other devices on a trusted network, set INKBOUND_BIND_ADDRESS=0.0.0.0 and INKBOUND_ALLOWED_HOSTS to the server's hostname/IP. Behind a reverse proxy, also set INKBOUND_PUBLIC_ORIGIN to the exact browser origin (for example https://inkbound.example.com); disable proxy buffering for streamed narration and allow uploads up to 32 MB. Requests with other Host headers or cross-origin API requests are refused.

Inside the container, localhost is the container itself. To reach LM Studio, llama.cpp or ComfyUI running on the Docker host, use http://host.docker.internal:<port> in AI settings (Compose maps it to the host), or the server's LAN address.

Useful maintenance commands (no AI requests):

docker compose logs --tail=100 -f app
docker compose exec -T app node server/db-backup.js data/backups/manual   # consistent database snapshots
docker compose exec -T app node server/warm-thumbnails.js                 # prebuild thumbnails for existing art

Back up the whole inkbound-data volume (for example with docker run --rm -v inkbound_inkbound-data:/data -v "$PWD":/out alpine tar czf /out/inkbound-data.tgz -C /data .), ideally while the app is stopped.

Without Docker

Requires Node.js 24 (the app uses built-in SQLite).

npm install
npm run dev

Open http://127.0.0.1:3000. If Sharp detects an incompatible system libvips on your machine, install with SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm install.

For a built production frontend:

npm run build
npm start

The app listens on loopback. Leave its terminal running. Change the port with PORT=3002 npm run dev.

Local development can optionally connect to an embedding server with INKBOUND_EMBEDDING_URL=http://127.0.0.1:8080. Without that variable, embeddings are disabled and the rest of the app works as before.

Connect AI

AI Settings offers LM Studio, OpenAI-compatible server, OpenRouter, Anthropic (Claude) and OpenAI (direct) profiles with separate URLs, model IDs, and backend-only bearer tokens. Switching profiles preserves both connections. Compatible servers use /v1/chat/completions and optionally /v1/models; enter a model ID manually when discovery is unavailable. Story and image-brief reasoning are independently configurable (default omits the field; none, medium, and xhigh send reasoning_effort). The Bonsai 2 profile uses medium for stories and none for briefs. Enable JSON-schema response format only when the server supports it; otherwise briefs request JSON through the system prompt. Streaming text, tool calls, usage costs, and timing metrics use the same processing as LM Studio.

OpenRouter narration reuses the backend key used for image generation. AI settings → Models & providers searches the live text-model catalogue and shows only endpoints in OpenRouter's zero-data-retention list. Select a model, then pin a specific provider endpoint. Provider cards show input, output, cache-read and cache-write prices in USD per million tokens, plus context limits, quantization, tool support and OpenRouter’s rolling 30-minute median throughput in tokens/second (P50). Missing speed data shows “Not reported.” Missing prices are shown as unknown; long-context pricing tiers use your configured context budget. Metadata is cached for five minutes and requires no paid inference. The saved backend OpenRouter key unlocks performance statistics; catalogue browsing also works without a key. The picker offers defaults for GLM 5.3 (z-ai/glm-5.3, deepinfra/fp4), GLM 5.3 Flash (z-ai/glm-5.3-flash, modal/nvfp4) and DeepSeek V4.1 Flash (deepseek/deepseek-v4.1-flash, novita/fp8).

Every OpenRouter text request, including tool continuations, image briefs, world generation and character summaries, sends provider.only, allow_fallbacks: false and zdr: true. An unavailable or no-longer-ZDR endpoint returns an error instead of routing elsewhere. Story and brief reasoning use OpenRouter's reasoning.effort, with choices drawn from model capabilities: GLM requires reasoning and defaults to low for both; DeepSeek defaults to high for stories and none for briefs. Pinning keeps routing stable, but cache hits depend on the provider; inspect reported cached-token counts to verify them. OpenRouter's ZDR policy permits temporary in-memory prompt caching. These routing restrictions apply to the OpenRouter text profile; image-provider settings are separate.

The OpenAI (direct) text profile uses the Responses API at api.openai.com and shares one server-side API key with direct image generation. It is available independently for narration, reconciliation, small tasks and summaries; adding it does not change existing selections. Refresh the model list to discover IDs available to your key. Vision capabilities and USD-per-million prices are provided for known models; unlisted prices stay unreported. Reasoning effort is model-specific and the output-token limit includes reasoning. Responses use store: false, and encrypted reasoning/function-call items are replayed only within the active tool loop, never saved in story messages. Requests are not automatically retried or rerouted.

For GPT-5.6 and later, reusable system/history prefixes and the current tool loop use OpenAI's 30-minute prompt cache by default. The profile can select 12 hours instead. Writes cost 1.25× ordinary input for 30 minutes or 2.5× for 12 hours; reads are discounted at the model's cached-input rate. Single-request helpers use explicit mode without breakpoints, avoiding unused cache writes. Older models retain their automatic caching behavior and do not receive these newer controls. Exact prefix matches and minimum cacheable lengths still apply; a cache hit is not guaranteed. Request details show cache reads and writes. Costs are estimates from reported usage and published Standard-tier rates, including long-context surcharges, before credits, not a balance or cash-charge meter. Sources: prompt caching, pricing.

Eligible subscribers can link their subscription to an API organization and claim API credits. Use a normal project API key belonging to that organization. Text and image requests consume the same credit balance; Inkbound does not sign into ChatGPT or read subscription tokens.

The Anthropic profile calls the Claude Messages API through the official @anthropic-ai/sdk, defaulting to claude-opus-5-5; refresh to list the models your key can use. Story and brief effort map to output_config.effort (default omits it: Opus 5.5 and Haiku 5.5 then run at medium, other current models at high). Claude models take no sampling parameters, so the creativity setting is not sent. Structured JSON uses output_config.format; schema constraints the API does not support (such as maxLength) are removed and still enforced by the app's own validation, and open-ended schemas fall back to prompt instructions. Narrator tool rounds replay Claude's original content blocks (including thinking) and use prompt caching. The system prompt and the story before the current turn are cached for an hour, because player turns are often more than five minutes apart (a one-hour cache write costs 2× input instead of 1.25×, and every read restarts the timer). The current turn's tool rounds use the five-minute cache, since they are re-read within seconds and never after the turn. Request details show tokens written to the cache, and how many of them for one hour. Opus 5.5, Opus 5, Sonnet 5.5 and Fable 5.1 requests opt into Anthropic's server-side refusal fallback (fallbacks: "default"); usage is recorded under the model that answered. Anthropic reports no request cost, so costs are estimated from published per-token prices (including cache reads and writes); unlisted models are recorded as unreported. Vision auto-detection reads the model's advertised image-input capability.

LM Studio defaults to http://localhost:1234/v1 (use http://host.docker.internal:1234/v1 from Docker). Start LM Studio's API server in its Developer tab. Open AI settings to change the address, refresh available models, and select one. Automatic selection prefers prism-ml/bonsai-27b when present; otherwise it picks an available text model. Embedding models are excluded. An optional LM Studio API token stays on the backend.

AI settings groups text models into Story narrator, Scratchpad, Stories reconciliation (formerly World and summaries), Small tasks, and Summaries. Stories reconciliation retains the existing world/region generation, full-story character-history summaries, side-story finalization and Update from story profile (worldText); the rename does not change saved models or credentials. Small tasks drafts image prompts and revisions using text only. Each group supports LM Studio, OpenAI-compatible servers, pinned zero-retention OpenRouter endpoints and Anthropic with independent credentials, reasoning and budgets. Reconciliation and small tasks can inherit the narrator. Scratchpad also defaults to the narrator and can use its own provider, model, reasoning and token budgets (scratchpadText), including independent vision for temporary attachments. Its composer opens the Scratchpad settings tab directly; previews and context estimates use that profile. Main and side-story narration keep using Story narrator. Summaries is a separate, initially disabled profile (summaryText), and requires advertised vision support or an explicit Force vision override. No paid model is silently selected during migration.

When Summaries is enabled, every new image (including concepts, area art, edits and regenerations) queues a short description using the generation prompt and a thumbnail. Images are saved before captioning; failed captions do not lose artwork. Short character briefs target roughly 250 words about current visuals, personality and goals, using current character records and the default concept thumbnail, not story history. Changed character records are refreshed after applying/undoing Update from story; linked participants are checked after archiving a side story. Unchanged, already summarized inputs are skipped. Side-story finalization does not itself reconcile full character records: apply Update from story to incorporate new events into those records.

The project hea 8000 der's Summaries button opens Fill missing summaries / resume, covering all image kinds and missing character briefs. Each completed response is checkpointed before saving the record. Closing the browser does not stop work; Pause after current request drains the current item. After a server restart or failure, resume explicitly. Requests are charged through the existing usage ledger; only missing/failed items are retried. The queue stores recovery data in SQLite taskCheckpoints; image descriptions (images.summary) and character briefs (characters.shortSummary) have dedicated columns. Source checks prevent stale outputs from replacing changed records. Derived fields do not invalidate world-update checkpoints or undo.

Vision requests reuse the existing card thumbnail when available, fitting within 640×640 and preserving aspect ratio; the request JPEG is generated in memory and is never permanently saved. Small tasks receives selected/mentioned character briefs and textual descriptions of selected concept versions, with bounded appearance/personality/goal fallback for unfilled briefs. It receives no concept pictures or old image-edit prompts. Image generation itself still receives the actual selected references. Narration uses get_character_brief first, then get_character_detailed for paged full records when needed. Legacy get_character calls remain accepted; tool results still last only for the current turn.

Creation handles new concepts, illustrations, world artwork and Regenerate, including reference-guided images. Editing handles Edit this image, with an independent OpenRouter, direct OpenAI or ComfyUI profile, model/workflow and target sizes. It defaults to using creation settings until you choose another provider; ComfyUI edits are capped at 2 MP. Edits retain the source aspect ratio and temporary/reference image behavior. All tabs save together; provider-reported costs or estimates from reported token usage are recorded.

Text requests queue on the actual provider host selected for their task. A compatible text profile sharing the active ComfyUI host participates in the existing Only run one model at a time lifecycle, including authenticated unload confirmation and restoration after success, failure or cancellation. LM Studio requests on the image host also share its inference queue; the Bonsai /models/load and /models/unload lifecycle is specific to compatible servers offering those endpoints.

OpenRouter, Anthropic and OpenAI each use one account-wide key, shared by every task that uses that provider (for OpenRouter and OpenAI, text and image tasks alike). Enter it in AI settings on any tab where the provider is chosen: the same API key field appears on each of them, shows where the current key comes from, and offers Clear saved key. A saved key is stored in data/settings.json and takes precedence over the environment (OPENROUTER_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY), which takes precedence over keys.json:

{"openrouter": "YOUR_OPENROUTER_KEY", "anthropic": "YOUR_ANTHROPIC_API_KEY", "openai": "YOUR_OPENAI_API_KEY"}

Create the Anthropic key in the Claude Console for the organization that holds your API credits. LM Studio and OpenAI-compatible servers keep an optional token per profile instead, since each profile can point at a different server. Credentials are not returned to the frontend, bundled, or stored with project images. Both credential and runtime data paths are excluded from Git and Vite file serving.

Image generation defaults to openai/gpt-image-2.5-flare. qwen/qwen-image-3 is also offered; refresh to discover other image models. The dedicated /api/v1/images API is used by default. Chat Completions mode is available for compatibility with models supporting image output there. A generation sends your image brief, the project's world prompt, art direction, and selected character sheets to OpenRouter.

ComfyUI images

Choose AI settings → Image provider → ComfyUI · Qwen-Image 2.1. The URL defaults to http://localhost:8188 (use http://host.docker.internal:8188 from Docker). OpenRouter remains available as a separate choice.

The bundled API workflows in server/workflows/ are adapted from standard Qwen-Image generation and editing workflows. Requests without references use generation; requests with references use editing. Existing prompt, style, character version, illustration reference, storage, and chat-display behavior is preserved. Settings expose the installed diffusion model, encoder, VAE filenames, and sampling steps (25 by default). Each request uses a fresh random seed and batch size one.

ComfyUI output is capped at 2,000,000 pixels, with dimensions rounded down to multiples of 32. Each uploaded reference is resized within the pixel budget in its own aspect ratio, without padding; the Qwen reference resolution is zero so it cannot silently upscale references above the limit. No final-output upscaling is performed. Existing OpenRouter images are unchanged. ComfyUI has no API charge and records $0 in the project ledger (electricity/hardware costs are not estimated).

App inference is queued by hostname (ports are ignored), across projects and browser tabs. Narration, brief preparation, revisions, summaries, and ComfyUI on the same host cannot overlap; different hosts run independently. Use the same hostname spelling for services on the same PC. ComfyUI's queue and the compatible text server's running slots are checked before shared-host work. Avoid starting work directly in other clients during app generation: the app cannot lock external clients. Cancelling removes or interrupts only the app's own prompt and waits for it to stop.

For ComfyUI and an OpenAI-compatible narrator on the same host, enable Only run one model at a time in AI settings. It defaults off until your server supports the lifecycle endpoints. Inkbound calls POST /models/unload at the narrator server's origin with {"model":"<configured narrator model>"} and its configured bearer token, then polls authenticated GET /models until that exact model reports status.value: "unloaded" before generating the image. Afterward (including failure or cancellation), it requests ComfyUI memory cleanup, waits for its GPU allocations to clear, and calls POST /models/load with the same model and authentication, waiting for /models to report "loaded" before resuming narration. Unknown, missing, failed, or timed-out states never authorize inference. Lifecycle requests are not cancelled with the image request. If restoration fails, the next request on that host retries it before inference. This recovery state lasts for the app server process; after a restart you may need to load the narrator manually. LM Studio and hosts that differ do not receive these Bonsai lifecycle calls. The existing 2,000,000-pixel limit still applies.

Build a world by hand

Everything can be made without AI, and AI tools stay available alongside:

  • Empty world: choose Empty as the world size when creating a project. It starts with one blank planet and no placeholder locations, roads or terrain. Add locations with + New major location, and places inside them as usual.
  • Characters: New character (in Characters, or under a location's "People in…") opens the same editor used for editing: name, aliases, every profile field, relationships (free text), condition, current location and home. Only the name is required, and a character may start unplaced. Manual edits can move any character directly, including dead ones, and the move is recorded in the world journal.
  • Artwork: upload PNG, JPEG or WebP files as a character concept (Upload concept in the character's Visuals), a place's artwork (Upload image on a location or area), or a free illustration (Upload illustration). Uploads are converted to PNG and stored exactly like generated images, so default concepts, viewing and deleting work the same. They show as "Uploaded" and cannot be regenerated, but can still be edited with AI.
  • Calendar: Timeline opens Set up this setting's calendar until one is saved: name, months (name and length, reorderable; a one-day month works as a festival day), a repeating week, and the current date with a month preview. Months and weekdays are fixed once saved.

Side stories

The Side stories tab follows independent groups of characters within the same project. Start side story here on a text or image entry selects it as the beginning. New side story also lets you choose a starting entry or start fresh. Include everything below the starting entry (illustrations included), or use the scrollable checklist to exclude individual entries. Creating moves the selected originals out of the main story, preserving their order and shared artwork. Choose participants and a premise for the separate thread. Subsequent dialogue, edits, regeneration, tool diagnostics and generated story images belong to that side story. Narration includes only this thread, its premise and bounded shared world data, excluding the main session memory. Characters, locations, reference artwork and the request-cost ledger remain shared. Its location selector changes the side-story viewpoint without moving the main protagonist.

Background references in an active side story selects main-story entries without moving them. Use the same Begin/End sliders as compaction, exact entry selectors, or the searchable checklist (text, date, recap title or message ID). A preview shows the resulting context and approximate tokens. Selected entries follow current main-story compactions: one complete summary per compaction, even if only part of its range was selected. Later edits, replacement or removal of compactions are reflected on the next request. Archived segments resolve to their short recap. Background is placed after setting instructions and before side-story dialogue, counts toward the narrator budget, and is excluded from side-story summary generation. Oversized mandatory background fails before inference. References are stored in the SQLite sideStoryContextMessages table and removed on finalization; the original messages remain untouched. Choosing references makes no AI request.

Finish side story opens a free review/setup dialog. Only Generate summaries with AI reads its selected text through the configured Stories reconciliation model in chronological context-sized chunks. Choose a summary budget of 100–10,000 words; detailed sections target 80–100% of their allocated word budget when the source supports it, and the helper's output budget also bounds each section. Regenerate summaries with AI is available while reviewing an unfinished side story, with an editable word budget. Incomplete endings, provider token-limit stops, and oversized output fail without clipping, automatic retries, or replacing the saved draft. The model also derives a narrator recap capped at 500 words and evidence-backed links to existing characters. Review/edit the title, detailed summary and recap before Save & archive returns only the short recap to the original position for moved entries (or appends it for a fresh story). Editing a main-story recap updates only that recap, preserving the detailed summary. Saving detailed-summary edits preserves the existing recap and links without model calls and marks them for review. The unchecked Rebuild recap and character links with AI when saving option explicitly authorizes a rebuild and replaces the recap; the separate saved-memory rebuild button also remains available. Manual recap edits remain independent. Existing archives can receive missing recaps and links using Build missing recaps & character links; completed work is saved individually so cancellation/failure can be resumed.

Turn segment into side story turns existing main-story entries into a finished side story. Choose where the segment begins and ends with the Begin and End sliders (the same control as Compact context); every eligible entry between them, illustrations included, is listed below with a checkbox to leave it in the main story. Moving either slider selects the whole range again and discards those manual exclusions. Prepare/review the title and summary, then save to move the selected originals into an archive and replace them with a recap at the position of the first one. Failure/cancellation leaves originals untouched; edits invalidate stale drafts. Unapproved drafts can be resumed or discarded. Saving preserves existing world facts and clears journal drafts/undo checkpoints tied to the previous story shape; Update from story still reads the detailed saved summary as evidence, rather than the shortened narrator recap. Unlike context compaction, this changes the main story itself: use it to close a chapter, and Compact context only to shorten what the narrator is sent.

Expand story opens a read-only reader with Recap, Detailed summary and Original dialogue views. Original entries load automatically as you scroll, include saved illustrations, and exclude failed/discarded alternatives. Viewing any level does not change model context. Character profiles show related story titles, roles (participant, affected, mentioned), and short relevance notes. Links use stable IDs and resolve current titles, so renames remain connected; a link does not imply the character knows every event.

Narration can call list_side_stories and get_side_story_summary without extra authorization to retrieve bounded pages of saved detailed summaries. get_character_brief and get_character_detailed include up to five story pointers and a count; additional links can be listed by character ID. These tool results remain within the current narration turn. Original archive dialogue continues to require the player to explicitly name the story in that turn. No semantic/vector index is used.

Illustrate summary opens the standard image brief/refinement and reference picker using the archive's saved summary. Generation uses the selected image provider and stores a cover in the project image library; it never inserts an image message into the main dialogue. Choose existing illustration opens a newest-first, searchable picker with hover previews; saving or removing a cover makes no model request and leaves the artwork and dialogue intact. Existing covers can also be edited/regenerated, and older artwork stays in the library. Costs remain in the shared ledger.

To recover an omitted detail, explicitly include the full archived title in your current action, for example: Look inside "The First Spell" for how Alice opened the gate. This enables get_side_story for only the named archive during that turn. The narrator can search a literal phrase or read bounded pages of original dialogue. Tool results appear in diagnostics when enabled, but are never automatically included in later prompts. Archives with duplicate titles must be renamed before lookup; images and discarded reply versions are excluded.

Context compaction

Compact context (in the story bar of the main story and of each active side story) replaces a range of entries with one summary in narrator requests. Pick the first and last entries with the Begin and End sliders. The dialog shows how many words the range contains and suggests a target length, which you can change (100–20,000 words; the result is approximate). Illustrations inside the range come along, but are never sent anyway. The summary is written by the Stories reconciliation model in chronological sections sized to its context and response budget. Each section receives the world prompt, setting overview, lore summaries and pinned lore, the calendar and the current world state, and can call read-only lookup tools (characters, places, lore, search_world, calendar and timeline reads, side-story summaries). It cannot create or move records, reserve names, generate images or change the timeline. Nothing is saved if a request fails or is stopped.

You keep reading the original entries, which are unchanged. Each compacted entry shows a small marker; click it to see exactly what the narrator receives instead, edit that summary by hand, recompact the same range, or remove the compaction so the entries are sent in full again. The narrator receives the summary once, at the position of the first compacted entry, with a header giving the entry count, dates and the place where the segment ends. Archived side stories inside the range are listed by title and ID in that header so get_side_story_summary can still reach them. The context meter counts each compaction as one entry and shows how many are active.

Compactions never overlap. Compacting a range that touches existing compactions replaces each of them entirely; their entries outside the new range are sent in full again. Editing, deleting or regenerating a compacted entry marks the compaction as changed (the marker shows a warning), but its summary is still sent until you recompact or remove it. Moving entries into a new side story, or turning them into one with Turn segment into side story, removes main-story compactions that covered them. Compaction applies only to narrator context: Update from story, side-story summaries and character summaries still read the original entries. Main-story compactions are stored in the project record, and side-story compactions in their side-story record, so no migration is needed. npm run test:compaction exercises the dialog in Playwright with mocked AI.

Scratchpad

The Include context and tools checkbox at the top defaults on. Uncheck it for lightweight chat: only this scratchpad's text conversation (or its saved compactions) is sent, with no system instructions, project prompt, world/lore, character data, location/date labels, archive index or tools. An empty scratchpad then has zero added context. Explicitly attached images are still user input if the selected model supports vision. The choice is saved per project and survives clearing the scratchpad; switching modes keeps its existing messages. Request previews and token estimates reflect the chosen mode. Turning context back on restores the worldbuilding behavior below.

The Scratchpad tab is a single out-of-story workspace per project, created on first use. It works like a fresh side story with no dialogue: the narrator receives the world prompt, lore, world database and current world state, but no main-story or side-story dialogue and no session summary. It acts as a worldbuilding assistant rather than a narrator. Every tool remains available (retrieval, record creation and movement, names, images), except that the timeline is read-only: get_calendar and get_timeline_events work, while set_story_date and save_timeline_event are not offered. Scratchpad entries carry no story date, stay out of the world journal and Update from story, and cannot be linked to timeline events, finished or archived. Clear scratchpad removes every entry; records created there, artwork and request costs are kept. Like active side stories, it stays usable during an Update from story with read-only world tools.

Scratchpad alone also exposes edit_character. Ask it to look up a character and save specific profile changes: descriptions, appearance, personality, backstory, goals, knowledge, relationships, short brief, aliases or alive/dead/missing status. The tool requires a stable character ID and replaces only supplied fields; read the full existing field before rewriting it. An appearance/personality/goals or other brief-source change clears a stale short brief unless a replacement is supplied, allowing readers to fall back to the detailed profile without another model call. Names/locations keep their existing controls, and attributes keep their existing tool. Character edits save directly to the shared database, appear in tool diagnostics, and create no story event or extra AI request. Main narration and ordinary side stories do not receive or execute this tool: existing profile reconciliation still uses Update from story. Running or paused reconciliation makes scratchpad edits unavailable until it is finished or discarded.

Write and illustrate

  • Create a project with a title, short description, and a world/system prompt.
  • Write an action and send it with Cmd/Ctrl + Enter. Sending an empty action continues the story. A fresh project also offers Let the narrator begin.
  • Alt/Option + Enter, or Alt/Option-click the send arrow, saves your typed entry without asking the narrator to respond. It works in the main story and active side stories, and the entry becomes normal text context for later turns. No AI request or charge is made.
  • Attach image lets a vision-capable narrator inspect a PNG, JPEG or WebP sketch alongside your next action (up to 10 MB). The image is available during that turn's tool calls, but is never saved to the database or artwork library and is not resent on later turns or regeneration. Unsent uploads stay in memory with each story's draft until sent, removed, or the page is reloaded. Remove an upload before using save-only. AI settings → Story narrator → vision for dialogue attachments provides auto-detection and a manual override for servers that support images without advertising it. Image requests reserve an approximate 4,096 tokens in addition to the action text; actual vision token costs depend on the selected provider.
  • Regenerate response adds another version of a narrator entry. Use the left/right arrows to choose a reply; versions and your selection survive reloads, and edits apply to the selected version. Sending your next action or continuing the story keeps only the chosen reply. Discarded versions never enter narrator context, and all generation charges remain in the project cost ledger. Regenerating an older entry still asks before removing later story text; saved artwork stays in the library. Failed or stopped regeneration preserves earlier versions so you can select them again.
  • Each text entry has separate Create a character and Create an illustration actions. Highlight a portion of the entry first to use just that line; otherwise the whole entry is used.
  • Both actions open an editable popup without making a model request. Write a description yourself, or explicitly press Draft with AI from selected text to request a name/description or scene brief using the Small tasks model. Review and change it before pressing Generate image. If the model is unavailable or its suggestion is wrong, enter the fields manually. No image is generated during prefill. For LM Studio models advertising a reasoning-off option, brief preparation and revisions use the native API with thinking disabled per request; narration keeps its default. OpenRouter checks the pinned endpoint's advertised capabilities before structured requests: strict JSON schema when supported, JSON-object mode with schema instructions otherwise, or schema instructions alone when neither format is available. Capability lookup failures also use instructions alone. Capabilities are cached for five minutes; no inference retry or provider fallback is added. Upstream errors include the provider's detailed error message when available. Brief preparation allows at least 4,096 output tokens; a higher response limit in AI settings also applies to briefs.
  • New sheet / New concept in the library opens the editor; requesting an AI draft uses the latest dialogue. Prefill also includes the preceding two text entries; explicit transformations override the old concept’s appearance.
  • Ask the narrator to revise this brief lets you supply feedback on the current draft and reference selections. Review the updated description and references before generating; character sheets also have a checkbox to omit the previous concept when it is inappropriate for a transformation.
  • Character names are unique within a project (matched without case sensitivity). A new character gets Characters/Name/concept0.png. Reusing the name edits the default concept (or the latest if no default is pinned), sends that image as a reference, and saves concept1.png, concept2.png, etc. Earlier concepts remain browsable. Deleted version numbers are not reused.
  • In Characters → Visuals, choose Set as default on any concept, or do so in its full image preview 8BD0 . The default stays pinned for portraits, new concepts and illustration references as temporary outfits are added. Use latest concept automatically instead clears the pin. Deleting the default falls back to the latest remaining concept. Changing this visual preference does not invalidate world-update drafts.
  • Use Regenerate on an illustration card or its full preview to reopen its saved prompt and references, edit them, and save a new image. Character references use the pinned default, falling back to the latest; the resolved versions are saved and retained for regeneration.
  • For illustrations, select characters from the reference browser; each selection uses that character’s default concept at generation time, or the latest if no default is pinned. Change concept version lists all of their concepts newest first, with thumbnails and enlarged hover/focus previews. Choose a specific version to keep its outfit or transformation, or Use character default to restore the default. Explicit version choices are saved with illustrations and retained when regenerating them. New briefs start with no saved illustration references; location art is never automatically selected. References show newest artwork first, support search by character name/alias or image prompt, and show a larger preview on thumbnail hover or keyboard focus. Up to four references can be combined. Helpers use full database names in briefs, and final image prompts tie each named character explicitly to its numbered concept reference.
  • Attach picture adds PNG/JPEG/WebP files to one generation or edit. An optional purpose label can identify an outfit or other detail. Attachments are sent last, after character concepts and saved scenes, within the four-reference limit. They live only in the current popup/request: no project files or library records are created, and regeneration requires attaching them again. Temporary inputs can total 20 MB and are normalized to at most 2 MP for generation; the browser preview shows the chosen input.
  • The character sidebar prioritizes recent successful get_character and create_character calls, including historical tool diagnostics where available. Characters with no recorded use sort alphabetically. Recent-use metadata does not change world facts or invalidate a journal proposal.
  • New images appear as separate Image studio chat entries, and are saved in the project library. These entries, their prompts, and their image data are excluded from future narrator requests. The narrator uses the world prompt and text story.
  • Characters → Edit character → Summarize character drafts Appearance, Personality, and Story so far from the entire saved text history. Long histories are read in chronological sections with carried-forward notes, rather than using the narrator’s trimmed context. The default concept (falling back to latest) is attached on the final pass when vision is advertised or enabled under AI Settings. Unknown capabilities default to text only. Review the draft and save it; image generation and deletion never replace saved character notes.
  • Characters → Edit character lets you rename an NPC after learning their name. Existing concepts move to the renamed folder and retain their IDs, version numbers, and illustration reference links. Historical story text and generation prompts remain as recorded.
  • Characters → Browse concepts opens a character’s complete visual history. Illustrations contains your scene images. Open any image to inspect its prompt or download it.
  • The narrator also has remember_character and generate_image tools when enabled in AI Settings. It can generate an image when you explicitly ask in the story; there is no automatic discovery pass or automatic NPC image generation.

Image entries and previews show the provider-reported USD cost. The project header totals reported charges starting from zero when tracking begins. The SQLite usage ledger retains charges after deleting images or rewriting story entries; it also records paid text, brief, world, and character-summary requests. Narrator replies show their combined request cost with an expandable breakdown for each tool round. Click the project total for a By model horizontal cost chart, ordered by reported USD spend, with request counts and each model’s percentage of the total. By day lists reported spend per local calendar day (for example 2026-10-26 · $15.23), newest first; open a day to see its models. This covers the entire saved ledger (including side stories and deleted content), combines providers for the same model, and separates unavailable prices from reported zero-cost requests. The Request history tab retains the paginated request list, including token, cache-hit, and reasoning counts when supplied by the provider. Missing costs are marked unavailable rather than estimated. Only costs returned by completed provider responses can be tracked.

The latest narrator response shows time to first token and tokens per second for its final model call. Text requests use streaming internally to measure actual output arrival, including reasoning, and provider token counts for throughput. Network latency is included; tool and image execution time is excluded. Providers without timing/token data show unavailable metrics.

Story generation reports writing/tool progress. Text appears as each complete model response arrives; tokens are not streamed individually. Stop cancels the current request. Provider failures leave your action and completed images saved so you can retry. Failed manual generations retain the edited brief.

Storage

data/
  settings.json
  backups/                      pre-migration database snapshots
  projects/<project UUID>/
    project.db                  SQLite: story, characters, world, usage ledger
    Characters/Olaf/concept0.png
    Characters/Olaf/concept1.png
    Illustrations/<image UUID>.png

Each project keeps its own story, world prompt, character descriptions, image prompts, image metadata, and PNG files. Illustrations use UUID filenames; character concepts are sequentially numbered. Character folders use readable sanitized names, with a suffix only for sanitized-name collisions. Each save is one SQLite transaction that writes only the rows that changed. A project is locked during generation to avoid conflicting edits. Deleting a project removes its directory permanently.

Project database

Each project has its own SQLite database, projects/<id>/project.db, next to its artwork. Settings (settings.json) and credentials (keys.json) stay outside project databases. The layout is defined in server/db/schema.js (Drizzle ORM, on Node's built-in node:sqlite). Common fields are columns; other or newly added fields are kept as JSON in each table's extra column until a migration promotes them. keyOrder is bookkeeping that lets records reload exactly.

To change the layout, edit server/db/schema.js and run npm run db:generate; commit the new file in server/db/migrations/. Each project database applies pending migrations when it is next opened, after saving a snapshot in data/backups/.

Projects from earlier versions are imported from project.json and usage.json on first start. Each import must reload identically before the files are renamed to project.json.pre-sqlite and usage.json.pre-sqlite, which are kept as backups; any mismatch stops startup with the JSON untouched. While a project.json exists it is authoritative: a database beside it is moved to data/backups/ and the project is imported again.

The Data tab (last project tab) browses the project's tables and runs SQL against its database. It is unrestricted: statements change data immediately, with no undo. Multiple statements run in order; if one fails, earlier ones remain applied.

Back up data/ to preserve your adventures. Change its location with INKBOUND_DATA_DIR=/absolute/path. Settings are global. Each text profile supports a context budget up to 1,048,576 tokens; choose a value that fits the model and pinned provider endpoint. The context budget is an approximate character-based token estimate: when it fills, the app keeps the opening (25% of the available story budget) and the latest events (75%), omitting the middle. Boundary entries may be shortened; saved story text is unchanged. Long stories are stored in full even when older entries are omitted from the AI request.

Provider resolutions vary. Megapixel settings are generation targets intended to control cost. The defaults request about 1 MP for sheets and 0.5 MP for illustrations; models with a higher minimum use a supported size instead. OpenAI image requests align dimensions to multiples of 16 and respect its minimum pixel budget; Qwen Image 3 uses supported 1K/2K tiers. The app saves the returned image at its original dimensions, without downscaling. Provider generation size determines cost. PNG conversion does not change dimensions.

Verification

Character naming presets can be mixed at world creation or under World → Naming presets: Modern (English/American), Slavic fantasy, Japanese fantasy, and Classic high fantasy. Every preset has over 300 given names and 150 surnames. Procedural allocation excludes used first names, aliases and former names, prefers distinct spellings, and spreads surname use. The narrator can call generate_character_names to reserve new names, then create_character to save the NPC; omitting its name also allocates one automatically. Previews are local and free. Existing character names are preserved. See naming research and allocation details.

npm test
npm run build

Integration tests use temporary projects and mock AI providers, with no paid generations. They cover persistence, narrator tools, editable brief preparation, sequential character versions, latest-concept and illustration references, original output dimensions, text-only narrator context, editing and regeneration, deletions, project locks, error recovery, settings validation, and credential isolation.

npm run test:browser builds once and runs every Playwright browser test in scripts/ (headless Chromium, mocked AI).

Continuous integration

.github/workflows/ci.yml is an example GitHub Actions workflow. It runs unit tests and browser tests (in the Playwright container) on pushes to main, pull requests and manual runs, then checks that the app's Docker image builds. Failed browser runs keep their screenshots as an artifact for a week. It needs no secrets and publishes or deploys nothing. In a fork, enable Actions under the repository's Actions tab; to publish images, add a registry login (for example docker/login-action for GHCR) and set push: true.

Qwen-Image 2.1 Turbo

In AI settings → ComfyUI workflow, select Turbo (8 steps). Install the official ComfyUI Turbo checkpoint qwen_image_2.1_turbo_int8_convrot.safetensors in the ComfyUI server's models/diffusion_models folder. The text encoder and VAE are shared with Standard. Standard retains its separate checkpoint and configurable step count.

Turbo uses Euler, CFG 1, and the eight sampling sigmas from the official Turbo model configuration, with a terminal zero for ComfyUI. Both generation and reference-based editing use this schedule; the 2-megapixel limit and narrator model-swapping behavior remain. The app checks that the checkpoint and custom-sampling nodes are installed before unloading the narrator. Saved image metadata identifies Standard versus Turbo.

Image dialogs include a searchable Model for this image selector showing your curated models from AI settings → Image model library. The library combines OpenRouter’s image catalog with Qwen Image 2.1 Standard and Turbo from the saved ComfyUI configurations. New images and regeneration start with the Creation default; “Edit this image” starts with the Editing default. Selections apply to one request only, including retrying that request, and never change saved AI settings. Local choices retain the selected configuration’s host, checkpoint, and model unload/reload behavior and stay within 2 MP. Catalog discovery does not generate an image or invoke a text model.

In AI settings → Image model library, browse the OpenRouter image catalog with descriptions and endpoint prices, and drag models into Your image models (or use Add/Remove). Reorder the selected list by dragging or using its arrow buttons, then save settings. Image dialogs show this curated list in order plus their task default. Before customization, only configured creation/editing models are listed. An empty list leaves only the task default. Prices retain their published per-image or per-token units and resolution variants; missing prices are unknown, not free. Catalog metadata is cached for five minutes.

Long-running world updates, side-story summaries/recaps, and world generation show a live progress panel. It reports completed sections or stages, the current record/phase, elapsed time, and (for text updates/summaries) model requests started. Progress counts work rather than estimating generation time; world-build totals can grow as residents are created. Read-only status polling does not invoke an AI model or save partial world changes. Side-story dialogs show the same status and Stop control. World builds remain resumable server jobs. World updates run as independent server jobs with recovery checkpoints (below). Side-story summary progress follows its original request.

Setting & lore

World → Setting & lore holds a short setting overview and independent named entries (for example magic systems, Sovereigns, religions or other worlds). Each entry has a brief summary and longer details. Opening, editing and saving make no AI requests. Explicit summary buttons use the Stories reconciliation model and return editable drafts; save to accept them. Lore is stored in the project SQLite database in the world's extensible fields, so existing projects need no migration.

Narration receives the overview and every lore entry’s short summary in both main and side stories. Pin full details for narrator includes an essential entry’s entire content on every turn. Unpinned details remain on demand. If instructions and required lore exceed the context budget, the meter and request preview report the problem before inference rather than silently cutting rules. Area descriptions and travel routes are omitted from the default world packet; current place names/IDs and hierarchy remain, and get_location retrieves details and named route destinations. search_world supports kind: "lore"; get_lore reads an entry by ID or exact title and pages longer text. Results belong to the current narration request and do not become permanent conversation context. Secrets remain narrator-only unless characters have learned them.

Update from story also reviews existing lore and proposes new topics from established world-wide facts in unsynchronized story entries. It shares topic discovery with the session-memory request, then drafts each affected record separately. New lore and changes appear alongside character/location updates for Apply, Skip or an explicit evidence override. Undo restores the prior lore. Editing the world during review invalidates the proposal. The setting overview is managed separately; regenerate it after significant lore changes if needed.

The expandable Update from story range indicator shows the earliest eligible entry, its message ID and the number of entries to read. Normal mode checks content hashes, so edited older entries can be revisited; it is not a single irreversible cursor. Select Very beginning or choose/paste an inclusive starting message ID to re-read existing material. Backfill modes default to Lore only, which leaves character/location records, session memory and normal sync hashes unchanged. Range overrides reset after preparing the draft. Archived side stories contribute detailed summaries in main-story order, not full transcripts; active side stories are excluded. Changing the range alone makes no AI request.

Incomplete generated lore is checked inside the existing one-repair allowance, including missing summary/details on new entries. If a lore update is still invalid, the review names the topic and source entries, preserves other valid proposals (and any earlier valid proposal for that topic), and does not apply the invalid text. Normal updates leave affected entries unsynchronized for retry; lore-only backfills continue to preserve normal sync history and can be retried using their selected range.

Update from story checkpoints: each completed model response is saved in the project's SQLite taskCheckpoints table before another request starts. This covers session memory/topic discovery, catalog selection, individual records and corrective replies, including lore-only backfills. A stopped, disconnected, failed or interrupted run exposes Resume update and Discard checkpoint in the World tab, with saved-response counts and section progress. Resume rebuilds the proposal using saved replies without calling the model again for those steps; a failed validation retries only the responsible reply. The original range, section split and newly reserved lore IDs are retained. Remaining requests use the current Stories reconciliation settings, allowing a model or budget change before retrying.

Checkpoints contain generated replies and input hashes, not credentials, and are not included in normal project responses or narrator context. The table is added through the normal backed-up database migration. Applying world facts still requires review. Saving the completed review draft and clearing its checkpoint happen in one transaction. Changes to the source story, world records or premise block reuse of stale checkpoints; explicit discard is required to start over. World updates return a short HTTP 202 acknowledgement and continue on the server when the browser is closed or refreshed. Polling reconnects to their progress. Explicit Stop or a server interruption can interrupt the current model request; a response that was never received/saved may need another paid attempt. Nothing resumes automatically or can recover work from runs made before checkpoint support was installed. This checkpoint mechanism applies to world reconciliation; side-story summary generation retains its existing saved-draft behavior.

While Update from story runs, main-story edits and shared-world mutations (including deleting/renaming characters or places, changing lore, image generation and raw SQL execution) are locked on the server as well as in the main controls. You can open, write, edit and regenerate active side-story dialogue, change its local position/title, or start a fresh side story. Its narrator can retrieve shared records, but shared-record creation, name reservation and image tools are temporarily disabled; unsolicited calls are rejected. Moving main-story entries or archiving a side story back into the main story waits for the update to finish. World updates and foreground narration have separate progress and Stop controls. Model requests still obey the existing per-host scheduling and VRAM safeguards.

Navigation and deep links

Projects and their views have stable URLs: /projects/:id/story, /side-stories/:id, /characters/:id/descriptions (or /visuals), /illustrations/:id, /world/lore/:id, and /areas/:id beneath the project URL. Side-story readers use /side-stories/:id/recap, /summary, or /original. Browser Back/Forward and the top-left back button restore the selected view; refreshing or opening a link directly works too. Record names are editable without changing their links.

Navigation from a side story retains its scope in ?story=... while browsing other tabs. Dialogue text drafts are kept per project/story in browser session storage, including across refreshes in that tab; attached images remain in memory only. Unsaved form edits trigger a warning before navigation. Generation/edit dialogs are temporary and do not have their own URLs.

npm run test:navigation runs an isolated browser regression against the production build with mocked AI and a temporary database. Install Playwright Chromium or set INKBOUND_TEST_BROWSER to a compatible executable. No live project data or paid AI calls are used.

Pausing Update from story

Pause lets the current AI request finish and saves its response before waiting. It starts no further requests, including repair requests. Pausing… remains visible after a refresh, and main-story/shared-record locks stay in place until the request finishes. If no more requests are needed, the review finishes normally. Stop still interrupts immediately and may require rerunning the interrupted request.

Use Resume update in World to continue from saved responses without paying for them again. Checkpoints and their main-story/shared-world locks survive pauses, Stop, failures and server restarts. Finish the update or explicitly discard its checkpoint to unlock changes. Active side stories can continue, with read-only world tools; AI settings remain editable so you can address provider/budget problems before resuming. npm run test:world-pause exercises this flow with a temporary database and mocked AI in Playwright.

Direct OpenAI is also available in image Creation and Editing, and its models can be added to the curated image model library. Flare, Sunburst, GPT Image 2, 1.5 and 1 Mini use the same saved OpenAI key (or OPENAI_API_KEY, or openai in keys.json). Requests use /v1/images/generations without references and /v1/images/edits with references, including new character concepts based on existing art. Images and reference legends preserve their existing ordering. Temporary references are sent inline, without permanent Files API uploads. Medium quality is the default; each image profile has a quality selector. Newer models receive dimensions divisible by 16, raised to their 655,360-pixel minimum; older models use their supported square/landscape/portrait sizes. The original generated pixels are saved without resizing. Request costs use reported text-input, image-input and image-output tokens; the direct Images API has no cached-input discount. Existing OpenRouter and ComfyUI defaults stay unchanged.

License

MIT

About

A local, image-enhanced interactive fiction app. Can either serve as a project management tool for a video game or a full blown narrative system.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

0