← All posts

v2.4.0 - A Streamable Interface for Understanding

Keating 2.4 moves teaching into streamed OpenUI documents, adds mobile voice and a separate OpenTUI host, makes providers, storage, and learning history more legible, improves interrupted-session recovery, and turns training export into a documented evidence package.

Keating 2.4 changes the unit of collaboration between a learner and a model. A teaching move can now arrive as a streamed, typed interface that both participants can inspect and act on, while ordinary explanation remains ordinary prose. The model gets more freedom over composition, but less reason to spend turns calling tools just to put a question or a study plan on screen.

Understanding is a product constraint

In Understanding is the new bottleneck, Geoffrey Litt argues that understanding matters because it lets a human participate in the next creative loop, not only approve the last one. Explanations, retrieval checks, manipulable micro-worlds, and shared spaces can keep the human close enough to the work to form the next idea.

Rebecca Sutter's Understanding needs permission adds the constraint Keating cannot ignore: a speed regulator only works when a person is allowed to use it. A product cannot grant organizational permission, but it can reduce the cost of stopping to inspect, answer, revise, and resume. In 2.4, those actions move into the main teaching surface instead of living behind extra commands and tool-call ceremony.

The interface is now part of the teaching protocol

The web agent receives a generated grammar for Keating's curated OpenUI library. During a response it can mix Markdown with fenced OpenUI documents, and the browser can begin rendering a document before the whole response has finished. The library contains explanations, callouts, questions, quizzes, flashcards, study plans, concept maps, images, animations, and shared notes, all composed inside one LearningSurface root.

This is deliberately not a request for every sentence to become a widget. The prompt tells the model to use Markdown when prose is enough and OpenUI when manipulating or responding to a component materially improves understanding. Each interactive document also declares a lifecycle: ephemeral for the current moment, resumable for unfinished work in a session, or workspace for a learner-owned artifact.

Learner context without an opening round of tool calls

Previously, the default prompt could encourage the model to call learner-state, timeline, due-review, and goal tools at the start of a conversation. That repeated work the host already knew how to do, consumed model turns, and risked recording the same session start twice. Session-start hooks now load the complete durable learner profile before the first model turn: full learner state and session history, every goal and curriculum step, raw quiz and question-check evidence, card reviews, and current flashcard SRS state.

Those records are not top-N truncated, so there is no follow-up inspection schema or capability ceremony. The startup payload also identifies missing profile, performance, retention, grading, curriculum, and review evidence explicitly. Missing evidence stays uncertainty rather than silently becoming a learner trait or triggering an opening interview.

That context can now improve during ordinary teaching. When a learner reveals a motivation, preferred communication style, recurring friction, or an approach that clearly helps, the tutor can record a cautious learner-context update instead of waiting for a settings form. The prompt treats those observations as revisable evidence rather than personality labels, so personalization can become more specific without becoming overconfident.

A clean answer for the learner, structured evidence for the tutor

Question submission now has two representations with one source of truth. The tutor receives a typed envelope containing the complete answers, document metadata, form state, and any grading instruction. The transcript renders a concise review card with the learner's answer and relevant result summary. Transport JSON and internal instructions stay out of the visible conversation, so a learner returning later sees what they actually submitted instead of protocol noise.

The old ask-user path is now compatibility code

The default web model no longer receives ask_user_question. New conversational checks use the streamed OpenUI Question component. Historical sessions and older tool-produced cards still render, which lets the interaction contract migrate without making prior learning records unreadable.

Tools now describe effects, not every visible component

OpenUI did not remove Keating's deterministic pedagogy engine. It changed which responsibilities deserve model-visible schemas. The 2.4 boundary looks like this:

| Concern | Owner | Examples | Learner-facing composition | Markdown and OpenUI | Explanations, checks, plans, maps, shared notes | | Deterministic host reactions | Lifecycle hooks | Context hydration, evidence, persistence, idle notices | | Durable or external effects | Model-visible tools | Grading, artifact generation, evaluation, workspace execution | | Optional specialist work | Runtime-supported tool bundles | Media, workspace, improvement, voice |

The model-visible surface is consolidated by responsibility, and every schema supported by the current runtime is available immediately. Workspace work is presented through three consolidated operations, and teaching improvement through two. The older granular implementations remain internal adapters, preserving their tested behavior without making the model choose among a long list of narrowly different calls. The former 2,490-line browser-tools module was also split by responsibility, leaving a small assembly facade over teaching, assessment, media, improvement, and workspace modules.

Lesson plans and concept maps stopped being tools

In the first 2.4 cut the baseline still exposed plan, map, and verify as one-shot teaching tools. A model had to call each one with enough authored content to clear a minimum-length gate or the call came back with a refusal and the artifact never appeared. In practice models kept forgetting to author the body, and the rendered lesson plan or concept map arrived empty far too often.

Those three tools are gone from the web agent runtime. The model now streams a StudyPlan for a learner-owned plan, a ConceptMap for a Mermaid diagram, and an Explanation or SharedNotes for free-form scratch — all composed inside one LearningSurface as the response streams. The browser begins rendering each component as soon as its enclosing fence closes, so the learner sees the plan grow and can correct a step mid-stream instead of waiting for a one-shot tool result that might never come. Default lifecycle is workspace, so a plan or map survives across turns and across sessions for the same topic.

The CLI still ships keating plan <topic>, keating map <topic>, and keating verify <topic> as deterministic file artifacts. They use the same underlying planner and Mermaid generator, and they remain the right tool when a learner or a pipeline wants a checked-in .md or .mmd rather than a live chat surface. The split is intentional: streaming for teaching in the chat, deterministic files for offline use.

Concept-map labels now actually render

A separate bug made every Mermaid node look empty even when the source was correct. htmlLabels: true tells Mermaid to render node labels through <foreignObject> wrappers, but the SVG sanitizer was stripping every <foreignObject> it found — which left rectangles with no inner content. <foreignObject> is now permitted by the sanitizer (Mermaid is the only SVG source and does not place script-capable content inside it), htmlLabels is on, and <br/> markers inside label text produce real line breaks. Concept maps with multi-line labels now read the way the model writes them.

Choose the runtime boundary instead of inheriting one

The browser agent can now run against four explicit runtime families: its built-in browser and NodePod sandbox, Keating Cloud, a generic external HTTP runtime, or the machine hosting Keating itself. The model's tool schemas are filtered against the live runtime and refreshed when that runtime changes, so supported tools are immediately available without advertising execution features that are actually offline.

keating web 3000 --host \ --allow-local-exec --root=/path/to/project

Host mode is deliberately not called a sandbox

The host provider executes against the local project with the current user's authority. It therefore requires the explicit --allow-local-exec acknowledgement and should only be enabled for trusted sessions. Browser and NodePod isolation, Keating Cloud, and external endpoints retain their own boundaries; selecting host mode does not make local execution safe by renaming it.

keating web --help now documents every runtime option with copyable examples, the external POST /api/agent-runtime/execute contract, authentication environment variables, resource settings, and the difference between hosted execution and sandboxed execution. The web Help surface presents the same choices interactively, so the security boundary is visible where a learner or operator actually selects it.

A learning history you can actually navigate

The Usage page is no longer only a collection of totals. Recent sessions, deepest dives, curriculum bars, and active days now lead back to the exact saved conversation. Session rows expose the model, provider, token usage, and thinking level without putting every field in the default reading path. The result is progressive disclosure: the overview stays calm, but the underlying record remains one click away.

The charts follow the same contract. Topic categories, model share, feedback signals, daily activity, and self-evolution scores can be selected with a pointer or keyboard to reveal their underlying sessions, evidence, or policy run. Evolution details show field-level before and after values, numeric deltas, accepted candidate steps, and decision reasons. If the stored policies cannot establish a trustworthy diff, Keating renders an explicit error page instead of turning missing evidence into an empty success state.

Training data is now an evidence package, not a pile of strings

Training export now produces one self-describing ZIP. Its canonical JSONL records preserve full conversational context, tutor persona, source provenance, session and model metadata, thinking level, quality state, rewards, and the signals used to derive them. A manifest records counts, warnings, settings, and a deterministic source-group split; a dataset card explains the intended workflow; and a generated JSON Schema lets downstream jobs validate the same contract Keating validates at export time.

Compatibility files remain available for ChatML and Alpaca supervised training, plus reward, KTO, DPO, and GRPO-style post-training pipelines when the required evidence exists. Low-quality responses remain in the canonical and preference records where they are useful negative evidence, but are excluded from supervised fine-tuning files. Deduplication and group-aware train and validation assignment reduce leakage, while Zod schemas replace hand-built JSON fragments at the archive boundary.

A teaching turn should survive interruption

Keating now treats an interrupted generation as recoverable session state. Partial output is persisted before the agent lifecycle closes, and the chat can offer a retry or continuation path instead of silently discarding the work. Suggested prompts and focused retry controls make the next action legible on both desktop and narrow screens.

When more than one answer is worth considering, response comparison keeps the alternatives attached to the learner's actual prompt and records the explicit preference as training evidence. Comparisons now happen for one percent of eligible responses by default, and narrow screens show one response at a time behind an explicit alternative switcher instead of squeezing two essays into columns. That closes a loop across the release: a learner can inspect what happened, choose the response that helped, and later export that choice without pretending the preference was an automatically inferred fact.

Voice belongs in the composer

Voice is no longer a tiny microphone action pretending every speech provider behaves the same way. On mobile, the composer can become a press-and-hold surface for dictation, return the transcript to text for review, and switch back to the keyboard without losing the draft. Providers with duplex support can instead open a full-height realtime conversation with a live transcript, explicit connection state, and an escape route to speech-to-text when a live session cannot start.

The Keating mascot now carries the waiting state in both text and voice. That motion is intentionally small: it makes latency feel inhabited without turning the product into an imitation of Kimi, Claude, or DeepSeek. The surrounding paper-and-ink materials, compact terminal type, and pedagogical status copy remain Keating's own visual language.

Storage needs one authority at a time

Keating's browser data is now organized by lifetime and authority. Tiny bootstrap preferences that must exist before React or IndexedDB hydration may remain in localStorage. Durable app records, including provider definitions and provider-scoped keys, go through the selected storage backend. Learning evidence, goals, reviews, quizzes, and artifacts remain in the versioned learning-record database and portable archive boundary.

The IndexedDB layers now close stale connections on versionchange, report blocked upgrades, reconcile required stores and indexes, and keep record-schema normalization separate from database layout versions. A future server backend must implement the same interface and own offline synchronization itself; feature code must not quietly dual-write a server and the browser and hope they converge.

Provider setup should survive an imperfect provider

Saving a custom provider no longer depends on its model-list endpoint working. Discovery is advisory: if /models returns an error or an empty list, Keating saves the provider, explains what happened, and lets the learner enter the model manually. Custom models can carry an API key, while a provider-scoped key is reused by every model assigned to that provider instead of being copied into several records.

Thinking Machines Inkling now has copyable defaults for its Anthropic Messages-compatible endpoint and model identifier. The Proxy settings surface also explains the real architecture: browser calls use Keating's same-origin /api/chat-proxy bridge automatically when CORS requires it. There is no mystery proxy daemon or separate URL that a learner has to invent.

More information, fewer boxes

The ask and quiz flows now use reducer-owned form state, tighter internal spacing, and transitions for non-urgent navigation. Tool results open in their structured visualizer by default, and deeply nested values wrap within the available width instead of forcing a horizontal excavation. The shared blank-template parser also removes a small but risky split between the two assessment renderers.

Usage and Benchmark now share the application navigation, page width, learning-insights header, metric treatment, and responsive filters. Mobile chat text is 11px with an 18px line height and real gutters on both sides. These are modest choices, but together they let a learner read the work rather than the containers around it.

A second terminal host without abandoning Pi

The new keating tui command runs a separate OpenTUI host over the same Pi RPC runtime. Provider configuration, prompts, skills, extensions, sessions, and the deterministic engine stay on the existing path. keating shell remains unchanged, and /shell switches from the alternate host into the classic Pi interface.

That portability has an honest boundary. OpenTUI streams the shared transcript, prompts, follow-ups, notifications, status, and editor text, but it does not yet render the web's semantic OpenUI documents or every Pi modal primitive. When an extension requests a modal surface the host cannot represent, Keating explains the limitation and points the learner to /shell instead of approximating the interaction and losing information.

Compatibility is preserved; the migration is not finished

OpenUI is now a real streaming path, but it is not yet the only path. The remaining seams are:

  • Legacy quiz, deck, goal, image, and animation tools still produce their established artifacts when durable behavior has not moved to OpenUI actions.
  • Resumable and workspace OpenUI component state still needs domain-specific session and artifact adapters on top of the clarified storage authority model.
  • OpenUI assessments still need to join the web app's existing interaction registry so one active check can become the same focused, sticky learning surface used by legacy questions and quizzes.
  • Long-running tool results still move from a pending indicator to a completed result. Progress streaming remains to be added for image generation, animation, evaluation, and workspace execution.
  • The terminal needs renderers for the same semantic document model before web and OpenTUI can claim component parity.
  • Verification is still an upstream prompt instruction. The natural next step is a hook that derives a StudyPlan's verification checklist from its own items — so self-check rides along with the plan rather than depending on the model to remember it.

Those are migration seams, not reasons to hide the new architecture. Keeping the legacy renderer and adapters in place lets Keating change the live collaboration model while preserving old transcripts, Pi extensions, and the deterministic artifact contracts below the interface.

Why 2.4 instead of 3.0?

This is a large release, but release size and semantic-versioning impact are different questions. Keating 2.4 follows the 2.3.1 maintenance release and marks a broader additive transition. The new web protocol and terminal host are additive. The classic Pi shell still works, historical transcripts still render, and existing deterministic implementations remain behind compatibility adapters. That makes 2.4 an appropriate version while the old contracts continue to function.

A future removal of legacy transcript support, a breaking Pi extension contract, or a required switch to the new semantic document model would be a stronger case for 3.0. For now, 2.4 marks the point where Keating starts treating the learner's understanding, and their ability to participate in the next turn, as an interface responsibility.