Capability first · transport second

Arcane runtime entity modules

The synchronized runtime ships 14 entity modules with 30 public ESM bindings. This page explains each module's capability and host assumptions. The exact constructor/function/value contracts are canonical in the Arcane shared entity inventory, with later SDK additions documented below and in the current runtime entity inventory.

SDK 0.97.1Runtime 0.8.12Protocol arcane/1
On this page

The synchronized runtime ships 14 entity modules with 30 public ESM bindings. This page explains each module's capability and host assumptions. The exact constructor/function/value contracts are canonical in the Arcane shared entity inventory, with later SDK additions documented below and in the current runtime entity inventory.

Entity creation and serialization are usually cross-host. Methods that touch DOM, DBOPFS, local storage, AI, or object URLs require the corresponding browser or native-WebView dependency.

Apps import entities with arcane-os/entities/<file>.js; the SDK-managed import map resolves these names through the selected runtime routes. Default root applications use their installed npm package directly. Absolute /arcane/entities/... examples below assume an explicitly configured physical or virtual /arcane route. Use the corresponding package specifier in direct npm applications; entity inputs and behavior are the same.

Canonical inventory

Module Exports Capability Availability / normalization
ApiModelRecord.js default Frozen HTTP(S) model response snapshot. Cross-host; normalized.
Calculation.js default Frozen bounded expression and finite result. Cross-host; normalized.
Chat.js default Conversation messages, tool exchanges, memories, and optional persistence. Browser/native WebView; AI/DBOPFS behavior mixed.
CommunicationMessage.js default, communicationChannels Frozen provider-neutral message. Cross-host; normalized.
CommunicationThread.js default Frozen provider-neutral thread containing normalized messages. Cross-host; normalized.
Document.js default File entity specialization for the documents table. Browser/native WebView; DBOPFS behavior mixed.
File.js default MIME-aware file open/save and DBOPFS persistence. Browser/native WebView; mixed storage/rendering errors.
Image.js default Image validation, upload, persistence, data URL, Blob URL, and revocation. Browser/native WebView; mixed File/Blob/DBOPFS errors.
IntentEnvelope.js six named exports Immutable canonical intent record, serialization, rehydration, and privacy-safe audit projection. Cross-host; strict normalized coded errors.
Preference.js default, preferenceSchema Boolean, number, select, and text preference definitions. Cross-host; normalized.
TerminalSession.js default, terminalShells Frozen terminal session identity, shell, and state. Cross-host; normalized.
Theme.js five exports Semantic theme tokens, conversion, serialization, and DOM application. Values cross-host; apply/clear need DOM; normalized/mixed.
User.js default, profileUpdateFromSearchParams User preferences/profile state and pure URL-query profile mapping. Browser/native WebView module; mapping itself has no side effects.
Weather.js four classes Frozen location, observation, day, and snapshot weather entities. Cross-host; normalized.

ApiModelRecord.js

Overview

ApiModelRecord freezes an endpoint, fetch time, metadata, and parsed value so provider reads can be cached or emitted without leaking mutable response state.

Example

javascript
import ApiModelRecord from '/arcane/entities/ApiModelRecord.js';

const record = new ApiModelRecord({
    endpoint: 'https://example.invalid/model',
    fetchedAt: new Date().toISOString(),
    metadata: {},
    value: {ready: true}
});

Calculation.js

Overview

Calculation owns a bounded expression, finite result, creation time, and toJSON() projection. Use CalculatorEngine to produce validated instances.

Example

javascript
import Calculation from '/arcane/entities/Calculation.js';

console.log(new Calculation({expression: '2 + 2', result: 4}).toJSON());

Chat.js

Overview

ChatEntity owns message history, saved state, tool exchanges, memory lookup, and optional app-scoped DBOPFS persistence. It installs no independent native authority; AI and storage dependencies must be available to the host.

Each new chat receives a timestamp plus independently generated random identifier in its default .jsonl filename, so chats created during the same clock tick do not share a storage key. Explicit filenames and existing saved files remain unchanged; no saved records are renamed or migrated.

messages returns provider-facing recurring context. An unresolved structural call and its matching results remain raw only through their one active provider continuation. Once that continuation settles, messages replaces the protocol with complete ordinary visible call, public result, and assistant content. transcript returns the narrow human-readable projection owned by the durable storage boundary. User and assistant records contain role, complete visible content, and their real timestamp. An assistant record may also carry its application-owned display name. A visible tool record may additionally carry its public name and plain result status. A requested-call record uses the call's required user-facing message; a settled tool result uses its supplied public message. System prompts, reasoning, provider-extension fields, memory flags, raw tool calls, call IDs, argument objects, and raw tool returns never enter new DBOPFS writes. Messages added with persist:false participate only in their current operation; they are not retained in messages, transcript, memory extraction, or DBOPFS. Setting chat.persist=false applies the same rule to new additions: the append returns false before adding records to retained state or starting a write. Existing retained messages and saved history remain unchanged. An already accepted persistence write keeps its operation ownership and may finish. One complete nonblank assistant record is also a durable conversation entry, so a model-authored opening can be stored and survive maintenance before the first ordinary user turn. No synthetic user record is required or written.

aiName defaults to an empty string. addAIMessage(text,{name=chat.aiName,...}) and addTurn({name=chat.aiName,...}) capture that application-owned name on the new assistant record. A nonblank string is preserved exactly; blank or nonstring names are omitted. Provider message.name never establishes that display attribution. The name is transcript-only metadata: messages and memory extraction omit it, including during a pending tool continuation. Changing aiName affects later messages only; loading existing records does not rename or rewrite them.

An assistant record may open an ordered array of structural calls with unique IDs in transient provider state. Until every pending ID receives exactly one matching nonblank role:'tool' result in the same active session, another user/provider turn is rejected. The durable transcript keeps only each call's user-facing message and any normalized tool name or result status. Existing stored files are not rewritten on load. Persistence failures roll back the complete turn rather than leaving provider and stored history divergent.

Example

javascript
import Chat from '/arcane/entities/Chat.js';

const chat = new Chat();
chat.addUserMessage('Hello.');

CommunicationMessage.js

Overview

Normalizes one provider message and exports the supported channel values: email, SMS, MMS, RCS, WhatsApp, and other.

Example

javascript
import CommunicationMessage from '/arcane/entities/CommunicationMessage.js';

const message = new CommunicationMessage({
    id: 'message-1',
    channel: 'email',
    body: 'Hello'
});

CommunicationThread.js

Overview

Normalizes one conversation thread and its CommunicationMessage records into a frozen provider-neutral object.

Example

javascript
import CommunicationThread from '/arcane/entities/CommunicationThread.js';

const thread = new CommunicationThread({id: 'thread-1', messages: []});

Document.js

Overview

DocumentEntity specializes FileEntity for the app-scoped documents table. Its persistence methods require the same DBOPFS lifecycle and ownership proof as the base file entity.

Example

javascript
import DocumentEntity from '/arcane/entities/Document.js';

const document = new DocumentEntity();
console.log(document.tableName);

File.js

Overview

FileEntity loads and saves supported file formats through app-scoped DBOPFS and uses the shared Markdown renderer where appropriate.

Example

javascript
import FileEntity from '/arcane/entities/File.js';

const file = new FileEntity();
console.log(file.fileName);

Image.js

Overview

ImageEntity extends FileEntity with image validation, upload, base64 data URLs, Blob URLs, and explicit Blob URL revocation.

Example

javascript
import ImageEntity from '/arcane/entities/Image.js';

console.log(typeof ImageEntity.prototype.revokeBlobURL);

IntentEnvelope.js

Overview

Creates immutable canonical v1 intent records while keeping trusted provenance separate from hostile payload input. Serialization, rehydration, and the audit projection are bounded and deterministic.

Example

javascript
import {
    createIntentEnvelope,
    serializeIntentEnvelope
} from '/arcane/entities/IntentEnvelope.js';

const intent = createIntentEnvelope(
    {originalExpression: 'Summarize the record.', normalizedGoal: 'summarize'},
    {source: 'example'}
);

console.log(serializeIntentEnvelope(intent));

Preference.js

Overview

Defines preference types, option normalization, value validation, JSON projection, and schema normalization for shared stores and forms.

Example

javascript
import Preference from '/arcane/entities/Preference.js';

const preference = new Preference({
    key: 'density',
    type: 'select',
    defaultValue: 'comfortable',
    options: ['comfortable', 'compact']
});

TerminalSession.js

Overview

Normalizes terminal session id, shell, state, updates, and JSON projection. The entity does not start a process; TerminalClient owns the native bridge call.

Example

javascript
import TerminalSession from '/arcane/entities/TerminalSession.js';

console.log(TerminalSession.shell('auto'));

Theme.js

Overview

Owns the shared light/dark semantic token sets, color conversion, JSON projection, DOM application, and clearing. Token work is cross-host; apply() and clear() require a document root.

Example

javascript
import Theme, {arcaneDarkThemeTokens} from '/arcane/entities/Theme.js';

const theme = new Theme({name: 'example', tokens: arcaneDarkThemeTokens});
console.log(theme.toJSON());

User.js

Overview

Owns persisted user preference/profile fields, explicit-preference updates, fresh reads, saves, and JSON projection. It installs window.user after its DBLS/DBOPFS lifecycle and emits user-entity-loaded.

AI_name

Saved AI-name preference alongside AI_personality. It is a string with an empty-string default and preserves the complete supplied text, including Unicode, whitespace, and an explicitly empty value. The property setter follows the same persistence setting as AI_personality; non-string assignments throw Error('AI_name must be string').

explicit, toJSON(), load(), refresh(), and updateExplicit() include this field through the existing profile schema. Loading an older record that omits it leaves a new entity's empty default in memory and does not rewrite the stored record. Partial updates that omit it retain the current value.

javascript
await user.updateExplicit(
    {AI_name: 'Captain Moonboots'}
);

The application owns naming choices and any mapping to its AI display name; this preference changes neither the human username nor chat history. No automatic migration or naming policy runs when it is loaded or saved.

updateExplicit(update)

Accepts an ordinary partial profile object, a JSON string, or a function current => partialOrNull. It returns Promise<UserEntityData> with the resulting explicit profile, retaining the existing field setters and their validation behavior.

The function is called once after the latest durable profile has been read, inside the existing serialized update and Web Lock. It may return a promise. Return only the fields to change, or return null to skip the durable write and receive the fresh profile unchanged. This no-write meaning applies only to the callback result; ordinary object/string inputs and direct null retain their existing behavior. With persist=false, the callback uses current memory and performs no durable read or write.

javascript
const displayedName = user.username;
const profile = await user.updateExplicit(function renameTheCaptain(current) {
    if (current.username !== displayedName) return null;
    return {username: 'Captain Moonboots'};
});

Callbacks compute a patch without mutating current. Do not call or await updateExplicit(), withFreshExplicit(), or refresh() on the same instance inside the callback: those operations depend on the current queue entry finishing. Fetch outside the callback; compare against current at the write boundary. Rendering and unrelated background work need not await this update.

A thrown or rejected callback causes no save and rejects with the original error. A failed save restores the fresh baseline in memory and rejects with the save error. The persistence setting is restored and later queued updates remain usable. The existing current_time and last_successful_time setters keep their immediate in-memory assignment, then save only their timestamp field through this same fresh partial-update owner. This preserves newer durable profile fields when TimeGuard records a check. Automatic timestamp save failures are reported through Arcane logging; callers requiring durable completion use and await updateExplicit({current_time: value}) or the corresponding last_successful_time partial update.

profileUpdateFromSearchParams(searchParams)

Named export from arcane-os/entities/User.js. Pass an existing URLSearchParams; the helper returns a new ordinary profile-update record.

Query parameter Profile field
subscription, otherwise subscription_key subscription_key
TWiN (exact case) license_key
name username
email email
phone phone

Presence selects subscription over the fallback, even when its first value is empty. Missing or empty selected values are omitted. Repeated parameters use their first value, following URLSearchParams.get(). Normal URL decoding occurs once when the caller creates the parameters; the helper preserves the decoded strings, including spaces, Unicode, literal plus signs, and leading zeros, without trimming, further decoding, or numeric conversion. Whitespace is a supplied nonempty value. Unknown keys, including zipcode, are ignored; no extra name fields are inferred.

The helper does not mutate the parameters, read or change the current URL, hydrate, render, or save. Importing User.js still has its existing browser singleton lifecycle. Applications own URL capture/removal, manual-draft protection, readiness, and their awaited user.updateExplicit(update) call.

Example

javascript
import {profileUpdateFromSearchParams} from 'arcane-os/entities/User.js';

const params = new URLSearchParams('name=Captain+Moonboots&phone=00123');
const update = profileUpdateFromSearchParams(params);
// {username: 'Captain Moonboots', phone: '00123'}

Weather.js

Overview

Exports four frozen normalized records: WeatherLocation, WeatherObservation, WeatherDay, and WeatherSnapshot.

Example

javascript
import {WeatherLocation} from '/arcane/entities/Weather.js';

const location = new WeatherLocation({name: 'Houston'});
console.log(location.toJSON());

Availability and protocol details

Entity modules are delivered by browser ESM at /arcane/entities/. They do not cross Core by being imported. Individual persistence, AI, DOM, or native calls cross the dependency named by that entity. See availability and normalization and the deep protocol guide.