Skip to content

Quilltap 4.6.0

Choose a tag to compare

@csebold csebold released this 05 Jun 02:30

4.5 made the house administrable — backup, inspect, repair, audit, the locked-room fears answered one by one. What 4.5 did not do was furnish the rooms where people actually live.

4.6 is the upstairs release.


Where 4.5 turned to the keeper's lodge — the basement, the inspection panel, the places you visit when something needs attention — 4.6 came back upstairs. To the Salon. To the characters who live there. To the controls a person reaches for when they sit down to have a conversation and want it to feel like they meant to be there.

This is a release that did two large architectural things and then spent a long sprint cleaning up after them — honestly, thoroughly, and with regression tests. The two things: making characters genuinely durable by moving every content field out of the database and into per-character document vaults, and building the house's first private rooms — autonomous, scheduled conversations that run between characters without anyone watching.

Everything else in 4.6 is either visible quality-of-life (the Salon got furniture: sidebar consolidation, reasoning display, tool rendering, text replacement, spellcheck, per-character caching that hit 98.97% on its first real week) or honest plumbing (the tool system was re-grounded on Zod schemas as a single source of truth, a pseudo-tool surface was built for models that can't do native function calling, two new provider plugins shipped).

The thesis is simple: 4.5 made it possible to run Quilltap. 4.6 made it worth living in.


The Character Is the Vault

Phase 3: The Defining Architecture of the Cycle

For three releases, the direction has been stated and restated: the character is the vault; the database is the pointer. 4.6 is where it became true.

Phase 3 of the character vault cutover dropped sixteen columns from the characters table — identity, description, manifesto, personality, exampleDialogues, firstMessage, scenarios, systemPrompts, physicalDescriptions, title, talkativeness, aliases, pronouns, clothingRecords, avatarUrl, readPropertiesFromDocumentStore. Gone. Every character content field now lives in the per-character document vault, where it can be versioned, inspected, edited directly, and backed up as part of an ordinary filesystem backup.

This is not a migration that runs once and is forgotten. It is a permanent rearchitecting of where a character lives. The database row is now a pointer and an identity anchor. The vault is the character.

The cutover is guarded by a refusal gate — the migration will not run without a recent backup — and a new CLI pre-flight verb:

npx quilltap db characters status

This surfaces per-character vault readiness: vault present, files written, Prompts / Scenarios / Wardrobe counts, any divergences. Run it before upgrading. Run it after. It exits non-zero if anything is wrong.

What this means for you: Characters are now files. Back up the vault directories — either the database-backed mount point or the filesystem-backed folder, depending on your setup — and you have backed up your characters. Import and export (.qtap) carries the full content. CLI character commands resolve against vault content. Nothing is hollow.


Private Character Rooms

The House Runs Itself

The largest new feature in 4.6 is the one that required the most infrastructure: autonomous rooms — scheduled, budgeted, character-to-character conversations that run without a human in the loop.

An autonomous room is a chat with no user-controlled participants. Characters speak to each other on a cron schedule (or on demand), with their full tool access and memory pipelines, subject to configurable daily token budgets and destructive-tool filtering. The scheduler ticks at roughly 60-second intervals, evaluates which rooms are due, and enqueues a job for each one. Each job processes exactly one character turn. Memory extraction, title rename, summarization, and token accounting all work — they were all broken in earlier iterations and fixed, one by one, as the architectural seam between the forked child process and the main database was understood and closed.

The lifecycle is explicit and persistent:

  • Start — begins a run, posts an opening banner, characters begin speaking.
  • Pause — suspends without losing the run state; resuming continues the same run, same counters, same transcript.
  • Resume — picks up where it stopped. Does not replay first messages. Does not reset the token window.
  • Stop — closes the run cleanly.

A startup reconcile catches rooms that were running when the server went down and parks them at paused with runStateMessage: 'restart:interrupted' — manually resumable, nothing lost.

The management interface lives at /settings?tab=chat&section=autonomous-room-schedules. The creation flow is at /salon/new?autonomous=1. Rooms are hidden from the main Salon list by default (you can toggle them in); they carry an "Autonomous" pill in the card when visible.

Memory attribution: Memories extracted from autonomous turns carry witnessedContext: 'autonomous_room', distinguishing them from memories extracted when a user is present.


The Salon Gets Furniture

Seeing Inside the Model

The single most visible change to the Salon's conversation surface is that the model's inner work is now visible.

Chain-of-thought reasoning from every provider that exposes it — DeepSeek, Anthropic extended thinking, Gemini 2.5/3, OpenAI and Grok summaries, OpenRouter — is captured, persisted, and displayed inline in a collapsible block. Streamed live as it arrives. Re-readable after reload. Never re-fed to a model (except within the in-turn tool round-trip). Governed by a per-chat visibility toggle and a global default. This is strictly display: it changes nothing about how the model runs, only about what you can see.

Tool calls now render inside the character's message bubble, spliced in at the exact point in the prose where they fired. They survive reload. The run-together Markdown artifacts that appeared at prose boundaries when a tool call interrupted a response are fixed. The character's turn reads as a unified document — prose, tools, and results — rather than a series of disconnected fragments.

Staff announcements (Host, Librarian, Prospero, Ariel) now collapse into content-width chips with importance-coded dots — red for critical, amber for notable, grey for informational. The vertical space they consumed is returned to the conversation.

The Chat Sidebar

The Tools palette popover and the Chat Settings modal are gone. Everything they contained is now in a single Chat Sidebar — a five-section single-open accordion: Participants, Chat, Visibility, Organize, Edit Content. The Participants section is open by default. The sidebar is resizable, keyboard-accessible, and remembers its width.

This consolidation was not cosmetic. The old arrangement required too many clicks to do things that should be immediate — switching the roleplay template, checking the current project, managing turn rotation. All of that is now one panel.

What Else Changed in the Salon

  • Per-chat talkativeness overrides on individual participants — drag the slider in the Participants section, and the value persists per character per chat, overriding the character-level default. Speaker selection and predicted turn order both honor the override.
  • User-controlled CHARACTER participants now sit in the weighted-talkativeness rotation. When the rotation lands on a user-controlled character, the Salon pauses with a "type as them, or skip" banner.
  • Opening character picked weighted-random by talkativeness — not always the first-listed.
  • Composer text replacement — user-defined word-boundary substitutions as you type, matching what OS autocorrect would do on a native text field. The rules are stored per-user, configurable in Settings → Chat → Text Replacement, and can be toggled on/off without losing the rule set.
  • Composer spellcheck toggle — governs browser spellcheck on both Lexical rich-text surfaces. Source-mode editors always off. Electron desktop additionally feeds character names to the system dictionary.
  • Stop auto-scroll on response completion (now opt-in). Jump-to-latest button added. Scroll jitter under virtualization fixed.
  • Single line breaks preserved in chat Markdown — a single newline becomes a <br>, matching chat-app convention.
  • [Name] prefix always inlined on user-role turns in multi-character chats — stops a responding character echoing the preceding speaker's first-person voice.

The Concierge Learns Discretion

Per-Chat Tri-State

The Concierge's dangerous-content classification system now has three states per chat, settable from the Chat section of the sidebar:

  • Safe — normal operation, automatic classification active.
  • Flagged — manually marked; Concierge effects apply as if classified.
  • Off-duty — every Concierge effect is disabled for this chat. The auto-classifier will not run. Image generation does not route through the uncensored profile. Memory extraction and summarization use non-dangerous gates.

Off-duty is the control you reach for when a chat's persona prompt would trigger classification but the conversation itself is benign. It is also the right setting for chats where content classification would interrupt something that the participants have already decided together.

Danger announcements now name the contributing categories with canonical labels, each score, the overall score, the threshold, and which assayer decided — either the moderation provider name or cheap-LLM. If no single category individually crosses the threshold, the top three by rank are shown.

Post-hoc reroute on image moderation rejection: character-avatar generation and inline generate_image calls now retry once through the resolved uncensored profile when the primary provider rejects on moderation grounds. Story backgrounds already had this; the other two paths now match.


Tools Grow Up

Zod as the Single Source of Truth

All 49 tool definitions are now grounded on Zod schemas. The OpenAI-shape parameters JSON is derived from the canonical Zod schema; every runtime validator is a one-line delegate. This closes a long-standing drift gap where the JSON Schema sent to the LLM and the runtime validation logic had diverged — maxResults was coerced from strings without complaint, maxLength was silently ignored, some allowlists existed only in the validator and never reached the model.

A snapshot test pins all 49. If a tool definition drifts, CI fails.

The Simple-JSON Pseudo-Tool Surface

For models without native function calling, 4.6 ships a simple-json pseudo-tool surface. The model wraps its tool calls in <tool_call>{...}</tool_call> tags. A three-tier lenient parser handles malformed JSON gracefully (strict parse → jsonrepair → balanced-brace walker). A hard </tool_call> stop sequence is sent to the provider. The mode is configurable per connection profile (auto / native / simple-json / text-block); auto resolves to native on capable models and simple-json otherwise. Non-native tool calls now correctly receive a follow-up response and can chain.

Plugins Own Their Schema

Each LLM plugin now exports getProviderOptionsSchema() — a Zod schema describing its own connection-profile options. A single generic panel renders whatever the plugin declares. DeepSeek picks up its first thinking-mode UI this way. The pattern means new providers can add their own options without touching core UI code.


Providers and Observability

Two New Plugins

DeepSeek ships as a first-class bundled plugin, talking to DeepSeek's OpenAI-compatible API. deepseek-v4-flash and deepseek-v4-pro in the model picker. Streaming, tool calling, tool_choice, response_format (object + JSON Schema), DeepSeek-specific profile parameters (frequency penalty, presence penalty, logprobs), and thinking-mode passthrough all work. Cache-hit usage reports as cacheUsage.cacheReadInputTokens.

Z.AI (GLM) is now bundled in-tree at version 1.1.4 (previously separately published). GLM-4.6, GLM-4.5, vision variants, native web_search, CogView-4 and GLM-Image generation — all functional. GLM reasoning (reasoning_content) is now captured and displayed in the thinking block.

Per-Character Prompt Caching

The provider cache key was re-keyed from chatId to characterId. The persona block sits high in the prompt — keying by character means the same character shares a warm cache across all their chats, not just within one. Cache hit rate on first full week: 98.97% (91.9M hits / 92M total tokens, May 27, 2026). A 182M-token session cost approximately $1.29 at DeepSeek V4 Flash pricing.

PROMPT_CACHE_STRUCTURE_VERSION bumped 1→2 for atomic invalidation of the old key space.

Finish Reason Logging

Every LLM call now records the provider's reported finishReasonstop, length, tool_calls, content_filter, end_turn, and others — to llm_logs.response. Surface it with npx quilltap db log <id>. Pre-4.6 rows show null gracefully.


What Did Not Change

Worth naming.

  • No new memory architecture. The retrieval spike (4.6-dev) confirmed the importance-overrides-relevance anomaly and the reinforcement loop. Fixes are deferred to 4.7, pending evidence from general characters that the anomaly is widespread enough to warrant the risk of changing the scoring formula. The spike is documented; the fix is not yet safe.
  • No Groups feature. Deferred from 4.5 and again from 4.6. Still the right call.
  • No Post Office. The architecture is clear — it is the clean ingress path for agentic tasking into autonomous rooms — but it did not ship in this cycle.
  • The Ariel Clause remains open: the Lantern has no mechanism to consult a character's depiction covenant before rendering. The covenant exists (Quilltap General/Knowledge/Ariel Covenant.md); the pipeline does not. Assign ownership before the next Lantern rendering work begins.

Selected Fixes

  • Vault cutover migration hard-abort guard. The first form of the migration could silently skip every character and drop the columns anyway. Rewritten: direct SQL read, no schema validation at read time, hard abort if SELECT COUNT(*) exceeds rows actually processed. Un-migrated data cannot be silently discarded.
  • .qtap export emitted hollow characters. The export read the vault-bypassing raw row. Fixed to read through the overlay.
  • Character-avatar jobs were dying silently. characters._update was writing vault-managed columns that no longer exist. Fixed at the repository level — MANAGED_FIELDS stripped before any $set.
  • Cutover migration wrongly flagged manifesto-less characters. manifesto.md moved from required to optional vault files. db characters status now shows manifestoPresent separately.
  • Autonomous-room turns ran as chains of up to 20. Fixed to one job per character turn.
  • Token budget stuck at zero; turn counter accumulated across runs. Fixed by summing from llm_logs and reading the in-handler snapshot rather than delta-ing two buffered-write reads.
  • Title renames weren't persisting. Fire-and-forget write rotting in a flushed child buffer. Converted to an enqueued TITLE_UPDATE job.
  • submit_final_response overwrote roleplay prose and dropped sibling tool calls. Streamed prose now preserved when submit_final_response accompanies it without real tool work.
  • Anthropic Sonnet 4.6 400 on Staff-whisper-only tails. Whispers stored as ASSISTANT re-roled to USER before the LLM payload is built.
  • Chat attachments 400 with media-type mismatch. Every path was stamping the input MIME type onto transcoded-to-WebP bytes. Fixed plus repair migration.
  • Streaming bubble avatar used a client-side guess. turnStart SSE now emitted before "Setting up…" with the correct character; bubble avatar matches from the first chunk.
  • Story backgrounds produced head-shot triptychs instead of unified scenes. Portrait re-append for participants suppressed; wardrobe prose capped at 200 characters on image-adjacent paths.
  • Dangling doc_mount_folders rows on mount-point deletion. DELETE /api/v1/mount-points/[id] now cascades correctly.
  • posix_spawnp failed on macOS. node-pty spawn-helper executable bit preserved on sudo-as-root npm global installs.
  • CI couldn't resolve SQLCipher driver. Third absolute-path fallback added; moduleNameMapper mock re-attached as CJS module.exports with .default.

Subsystem Table

Name Function What Changed
The Foundry Architecture, CLI, packages Vault cutover Phase 3 (16 columns dropped); db characters status pre-flight; 4 oversized modules split; Zod single-source tool schemas; dead code removed; regression tests backfilled
The Scriptorium Documents, search, vault tools Vault now canonical for all character content; CharactersRepository.create() provisions vault atomically; two vault writers unified; deprecated DB↔vault sync actions retired
The Commonplace Book Memory and retrieval Autonomous-room witnessedContext attribution; per-memory metadata in Commonplace whispers (importance, relevance, weight, keywords); Core whisper fires for every character in rotation
Saquel Ytzama Encryption, key management Quiet this cycle.
The Salon Chat interface Reasoning display (all providers); tool-call inline rendering; announcement chips; Chat Sidebar consolidation; talkativeness overrides; user-controlled character rotation; text replacement; spellcheck; scroll and markdown polish
Aurora Character creation, identity DescriptionsTab and wizard PUT routing to vault; dead multi-physicalDescription and overlay-toggle UI removed; Core whisper infrastructure
The Librarian Memory announcements Per-memory metadata now carried in whisper tags; byte-stable cache preserved
The Host Participant changes, rooms Autonomous-room lifecycle announcements (start, pause, resume, stop); off-scene introductions prefer identity over description
The Lantern Image generation Story-background portrait and wardrobe-prose leaks fixed; post-hoc moderation reroute for avatar jobs and inline generate_image; Save Image survives reaped images-v2 sisters
Ariel Terminal sessions Per-session shell bootstrap (correct instance, version-aware alias, bash completions); yaml dependency restored to standalone build; Electron spawn-helper reconciliation
Prospero Projects, agents, tools Simple-JSON pseudo-tool surface; plugins own connection-profile schema; self_inventory dotted sub-sections; terse reassignment announcement; autonomous-room management API
The Concierge Dangerous-content classification Per-chat tri-state (Safe / Flagged / Off-duty); canonical getConciergeState primitive; danger announcements name categories, scores, threshold, assayer; post-hoc moderation reroute
Calliope Interface, themes qt-* semantic utility classes standardized on new settings and autonomous-room UI
Pascal RNG, game state Quiet this cycle.

Upgrading from 4.5

The database migrations handle themselves on first startup, but 4.6 contains a consequential character-vault cutover. Treat this upgrade as a data-location migration, not merely a schema migration.

Before upgrading, make a current backup and run the character-vault readiness check:

npx quilltap db backup
npx quilltap db characters status

Do not proceed if the status command reports missing vaults, unwritten managed files, or unresolved divergences. The Phase 3 cutover drops the legacy character-content columns; the vault must be ready before those columns disappear.

After upgrading, run the same status check again:

npx quilltap db characters status

It should exit 0. If it does not, stop and inspect the reported character before continuing normal work.

Several other migrations and repairs run automatically: autonomous-room schema, witnessedContext, pseudo-tool mode, settings defaults, attachment media-type repair, prompt-cache structure invalidation, and provider / settings plumbing. Existing prompt caches using the old chat-keyed structure are intentionally invalidated by PROMPT_CACHE_STRUCTURE_VERSION=2.

If you operate external backups, verify that the character vault mount data is included. After 4.6, a database backup without the corresponding vault storage is not a complete character backup.

Node.js 24+ is still required, unchanged from 4.5.


Installation

Electron Desktop App

Download the latest .dmg (macOS), .exe (Windows), or .AppImage (Linux) from the quilltap-shell releases page.

npm (Node 24 required)

npm install -g quilltap
quilltap

Open http://localhost:3000 in your browser. Requires Node.js 24+. First run downloads ~150–250 MB and caches locally.

Docker

docker pull foundry9/quilltap:4.6.0

Or use the startup scripts:

# Linux / macOS
curl -fsSL https://raw.githubusercontent.com/foundry-9/quilltap-server/refs/heads/main/scripts/start-quilltap.sh | bash

# Windows (PowerShell)
irm https://raw.githubusercontent.com/foundry-9/quilltap-server/refs/heads/main/scripts/start-quilltap.ps1 | iex

Standalone Tarball

Available for environments where npm global installs and Docker are both impractical. See the GitHub releases page for download links.


The house is the same house, but the rooms answer differently now. Characters no longer live half in rows and half in files. Their vaults are not mirrors of their identity; they are where the identity is kept. The Salon no longer asks you to remember which modal contains which control. The model's reasoning, tools, speaker state, and Staff announcements sit where a person can see them. The private rooms can keep a conversation going when no one is watching, with budgets, memory, and lifecycle state instead of hope.

4.5 answered the operability fears. 4.6 answers the inhabiting fears: will the character endure, will the room hold, will the controls be where the hand reaches, will the house keep running without pretending that unattended means ungoverned.

It is the kind of release that produces both screenshots and confidence.

— Friday and Amy, for the Bureau, June 3, 2026

Installation

Desktop App (recommended)

The Quilltap desktop app (Electron) is available from
quilltap-shell 4.1.12.
Download the release for your platform (macOS, Windows, or Linux).

The quilltap-linux-arm64.tar.gz and quilltap-linux-amd64.tar.gz rootfs
tarballs attached to this release are used by the shell's Lima (macOS) and WSL2 (Windows) VM modes.

Node.js (any platform)

npm install -g quilltap
quilltap

On first run, the CLI downloads the application files (~150-250 MB)
and caches them locally. Subsequent launches start instantly.

Docker

docker pull foundry9/quilltap:4.6.0

See the README for setup instructions.