Studio Guide

This guide explains how each major element of Emocentric Studio works and what happens underneath when a creator uses it.

The studio is organized around one loop: design an agent, test it against the live engine, save versions, publish one version, and monitor real relationships after deployment.

Mental Model

The studio has five durable building blocks.

The most important distinction is draft versus live. Editing changes the current draft head. Publishing chooses one version to expose through live surfaces. A newer draft does not affect public users until it is deployed.

App Dashboard

The app dashboard is the first working area after sign-in. It is reached from /apps/{appId}.

Top Bar

The top bar keeps account-level navigation and recovery actions close at hand.

App Header

The large app heading names the current app and summarizes the app workflow: blueprints become characters, characters go live.

The segmented control changes the visible collection.

Blueprint Cards

Each blueprint card opens the blueprint editor. Metrics summarize the blueprint's current head.

The add card creates a new blueprint with a name, description, default persona, and the built-in send_message action.

Character Cards

Each character card opens the character workspace. The card shows provenance and live status.

The delete button soft-deletes the character. Deleted characters can be restored from the top bar recovery modal.

Corpus Cards

The Corpus tab reuses blueprint cards but opens the corpus editor route. This is useful when the creator is mostly collecting source material before tuning persona or actions.

Blueprint Editor

Blueprint editor routes are reached from /apps/{appId}/blueprints/{blueprintId} and /apps/{appId}/corpra/{blueprintId}. Both routes use the same editor shell with different default tabs.

Left Rail

The icon rail switches editor sections without leaving the blueprint. Desktop shows icons; mobile uses a horizontal tab bar.

The header shows the blueprint name, mode, head version, and save state.

Status Panel

The right status panel is a compact control center.

Overview

Overview edits the blueprint envelope rather than the agent internals.

Models

Model routing controls Studio's planner heartbeat.

Profile

Profile edits blueprint.persona.

Changes stay in the blueprint draft until saved.

Behavior

Behavior has two related editors.

Changing the set of behavior signals matters at deployment time. If a published version changes the emotion palette, existing live relationships need a rebaseline before the new version can go live.

Knowledge

Knowledge writes seed facts to blueprint.seedFacts.

These seed facts become initial reliable context for characters created from the blueprint.

Actions

Actions are the tools an agent may choose when a conversation needs more than free text.

Custom actions can be surfaced to API callers and action webhooks after deployment.

Fields

Fields define reusable inputs for future blueprint instantiation.

Corpus

Corpus stores source material and references attached to the blueprint.

Corpus is distinct from seed facts. Corpus is source material for authoring and provisioning; seed facts are runtime memory seeds.

Raw JSON

Raw JSON exposes the full blueprint envelope.

The envelope includes:

{
  "name": "Blueprint name",
  "description": "Creator-facing description",
  "blueprint": {},
  "studio": {},
  "corpus": {},
  "fieldSchema": []
}

Applying JSON updates the draft in memory. Saving writes a new blueprint version.

Character Workspace

The character workspace is reached from /apps/{appId}/characters/{characterId} and its surface routes such as /character, /emotions, /memory, /actions, /apps, /history, /monitor, and /deploy.

Workspace Header

The header shows the active character name and status badge.

Agent Rail

The rail is the main workspace navigation.

In the current agent layout, the selected rail item controls the center pane. The assistant chat remains available in the inspector for most authoring surfaces.

Character Surface

Character is the settings surface for identity and runtime defaults.

Manual edits call the character version API and create or coalesce a saved version.

Emotions Surface

Emotions splits behavior into signal definitions and temperament.

These settings affect how the runtime appraises turns, stores emotional state, and chooses tone.

Memory Surface

Memory edits seed facts for the character's current blueprint version.

This is author-time knowledge. Live relationship memory is shown in Pulse and Monitor.

Actions Surface

Actions is the character-level action editor.

When deployed, selected actions are returned inline through the REST API and can be delivered through configured action webhooks.

Apps Surface

Apps is a read-only summary of where the character sits.

Use Deploy for actual publishing and API key management.

History Surface

History shows the version graph and version inspector.

Checking out a version repoints the draft head but does not publish it.

Assistant And Simulations

The assistant panel is a persistent testing and editing companion.

New Menu

The New button opens three workspace modes.

History Popover

The history button lists prior simulations and comparisons.

Composer

The composer sends messages to the active simulation.

Studio roleplay messages are debounced into a small batch so quick multi-message input reaches the engine as one turn.

Roleplay Simulation

A roleplay simulation uses the creator's special studio relationship for the character.

On send:

Studio traffic is excluded from deployed end-user monitoring, but it still uses persisted engine state.

Compare Simulation

Compare sends one prompt through multiple selected versions.

Use Compare when deciding whether one version is better before publishing.

Tuner

The Tuner turns natural language edit instructions into a new version.

The Tuner is versioned like every other edit path. It does not silently overwrite live deployments.

Live Pulse

Live Pulse is the runtime inspector for the active simulation. It opens from the rail as History/Pulse or from the pulse button in the assistant header.

Feelings

Feelings shows the current emotional state for the active relationship.

Memory

Memory shows the live fact bank for the active relationship.

These edits affect the current simulation relationship only. They are not blueprint seed facts.

Events

Events appear when event-kind facts exist for the relationship. Studio context events and deployed context events can become event memories, depending on runtime appraisal and memory writes.

Last Decision

Last Decision shows the latest selected action and reason.

Deploy

Deploy publishes one selected version to one or more surfaces.

Deploy Graph

The center graph is the same version DAG used by History, but selection is publish-focused.

Surfaces Panel

The surfaces panel controls deployment channels.

Toggling a surface includes or excludes it from the next publish operation. Publishing disables live surfaces that are no longer selected.

API Keys

API keys are app-scoped and shown through the character deploy panel when the API surface is active.

The deployed REST API uses Bearer keys with scopes; keys never trust account or app ids from request bodies.

iMessage and WhatsApp surface details show link helpers when a line reference is available.

Rebaseline

If the selected version changes the emotion palette compared with the current live version, Deploy asks for rebaseline confirmation.

Rebaseline resets existing live relationships' emotional state to the new signal baselines. It leaves memory intact.

Monitor

Monitor is the read-only observability surface for deployed traffic. Studio test traffic is excluded.

Sessions Sidebar

The sidebar shows live deployment activity.

Overview polling runs periodically while the page is visible.

Transcript Center

The center panel shows the selected deployed simulation transcript.

Live State Inspector

The inspector shows the selected end user's live state.

Monitor snapshots use the live release version when available, otherwise the head version for never-published characters.

Public And API Surfaces

Once deployed, a character can be reached through several paths.

Web Chat

The web surface serves a public chat page at:

/c/{characterId}

Public web chat uses the public chat route, not the creator's studio relationship.

REST Chat

The API surface accepts chat turns at:

POST /api/v1/emotive/{characterId}
Authorization: Bearer emo-...
Content-Type: application/json

{
  "end_user": "player_42",
  "text": "hi",
  "clientTurnId": "4ef85f6e-b981-49b6-8eac-420a7f30f208"
}

Each stable end_user becomes an isolated relationship. With no identity block, turns, events, conversation hydration, and action-result polling all resolve the canonical external_app/api surface. An optional identity block may only use authority: "external_app" and provider api, game, or host; privileged Studio/platform identities are rejected. Callers must send the same block to all four endpoints, and omitted fields still default to external_app/api.

API-backed clients can hydrate or create the same canonical main conversation without a separate application database:

POST /api/v1/emotive/{characterId}/conversation
Authorization: Bearer emo-...
Content-Type: application/json

{ "end_user": "player_42", "ensure": true, "threadKey": "main" }

Omit ensure for a read-only existence/history check. The response includes pendingActionEventIds and pendingActionExpiresAt; the latter is the earliest authoritative ISO-8601 deadline and is null if and only if no ids remain. Poll them through POST /api/v1/emotive/{characterId}/action-results with the same end_user, simulationId, and event ids. Reusing a clientTurnId with identical text safely replays a committed turn without reserving another paid-turn slot. ensure: true is optional and creates no paid reservation; it is guarded by a separate per-deployment daily new-relationship cap. Direct REST turns and context events may create their canonical relationship atomically, and first reserve paid-turn quota in a short account-serialized transaction before identity creation or model work. Concurrent users therefore cannot overshoot the deployment/account turn caps. Read-only checks and existing conversations continue to work when new-relationship capacity is exhausted. The MVP conversation endpoint accepts only the canonical main thread.

App-scoped chat keys can also manage the same relationship's active user memories without learning Studio relationship or user ids. POST /api/v1/emotive/{characterId}/memories pages newest-first, POST /api/v1/emotive/{characterId}/memories/search performs semantic search, and PATCH or DELETE /api/v1/emotive/{characterId}/memories/{memoryId} updates or removes one memory. All four requests resolve the supplied end_user and identity without creating an unknown relationship; mutation requests include the expected current content so stale editors fail with a conflict.

Public turn errors preserve the human-readable error field and add a stable code for retry states:

Branch on code, never on mutable error prose. A code-less 409 or 503 has no implied retry semantics.

Owner-authenticated provisioning can verify an exact plaintext key with POST /api/apps/{appId}/api-keys/verify and { "apiKey": "emo-..." }. It returns only validity, the matched app id, and scopes; cross-app/account, altered, disabled, and revoked keys return { "valid": false }. The check is read-only. A failed downstream provisioning flow can revoke its newly minted key through DELETE /api/apps/{appId}/api-keys/{keyId}. Both responses are private and non-cacheable.

Context Events

The API surface accepts structured events at:

POST /api/v1/emotive/{characterId}/events
Authorization: Bearer emo-...
Content-Type: application/json

{
  "end_user": "player_42",
  "payload": {
    "kind": "action",
    "name": "opened_chest",
    "description": "The player opened the crypt chest."
  }
}

Events enter the same relationship memory and emotional-state system as chat turns.

Action Results

When a deployed turn selects an action, callers receive structured action intents plus pendingActionEventIds and pendingActionExpiresAt. Conversation hydration and action-result polling return the same pending envelope; terminal responses use an empty id list and null expiry. Action webhook executors can report outcomes through:

POST /api/v1/action-events/{eventId}/result
Authorization: Bearer emo-...

The signed agent.action.selected body always includes top-level createdAt and expiresAt values. For ordinary actions, Studio persists expiresAt once as createdAt + ACTION_EXECUTION_TIMEOUT_MS using the creation-time setting (clamped to 30 seconds–60 minutes). Changing the setting later affects only new events. A first callback is accepted only when Studio evaluates it strictly before this deadline; callbacks at or after it return 409, and Studio terminalizes the timed-out action without materializing late transcript output. Scheduled followups instead use the active 30-minute claim lease: the body expiresAt exactly equals the claim wrapper's leaseExpiresAt, and ordinary expiry scans do not shorten that lease. Claim token, action event, and an unexpired lease are all required through result write, outbound queueing, and no-output finalization.

Migration 0035 intentionally leaves older rows nullable because their original signed deadline was not stored. Keep ACTION_EXECUTION_TIMEOUT_MS frozen while those rows drain (no more than 60 minutes); the legacy computed fallback is isolated to nullable rows and is never used for newly-created work.

Studio tests persist action transcripts but suppress production action webhook delivery.

Versioning Rules

The studio is intentionally version-heavy.

This makes it possible to experiment aggressively without accidentally changing deployed behavior.

Runtime Guarantees

The server runtime provides several important guarantees.

Common States

Docs Route

This page is served from docs/studio.md. The /docs route first looks for local Markdown pages and renders them. If a local page does not exist and DOCS_ORIGIN is configured, the request is proxied to that origin.