DeskMate

Character pack format

The .deskmate file for sharing a character — what is inside, what is deliberately left out, and how import decides what to keep.

A character pack is a single file for sharing a character: a persona, the parts of its configuration that make it itself, a description of its voice, and — when the avatar may be shared — the avatar. Packs are exported and imported in the console’s Characters page. You can also drag a pack onto the console or the character window.

A pack is a plain zip with the extension .deskmate. The reference implementation is src/core/characters/pack.ts.

my-character.deskmate
├── manifest.json
├── character.json
├── persona.md
└── avatar/                 (only when the avatar is included)
    ├── avatar.json
    └── mio.vrm

The zip reader accepts stored and deflate entries and no zip64; paths pointing outside the archive are refused.

manifest.json

{
  "format": "deskmate.character",
  "version": 1,
  "avatar": "included"
}
FieldValues
formatAlways "deskmate.character". Anything else isn’t a pack.
versionInteger. A pack newer than the app understands is refused.
avatar"none", "builtin" (a reference to the built-in avatar, no files), or "included" (an avatar/ folder).

character.json

Only these fields are read; anything else is ignored.

{
  "name": "Mio",
  "language": "en",
  "affect": {
    "baseline": { "valence": 0.1, "arousal": -0.2 },
    "halfLifeMinutes": 10
  },
  "urge": {
    "minGapMinutes": 20,
    "quietAfterTalkSeconds": 90,
    "chatAfterSilenceMinutes": 25
  },
  "voice": {
    "vendor": "elevenlabs",
    "model": "<model id>",
    "voice": "<voice id>",
    "cloned": false,
    "rate": 1,
    "pitch": 0,
    "volume": 1
  }
}
  • name (string) and language (string, such as "en", "zh", "ja") are required.
  • affect.baseline is where the mood settles at rest, both numbers in [-1, 1]. halfLifeMinutes is how fast mood returns to it.
  • urge sets how often the character speaks up unasked. Numbers only.
  • voice is optional and is described below. rate, pitch and volume are optional numbers.

Voice

A voice in a pack is told of by vendor, not by the exporter’s own model service. The pack never contains a service id or an API key.

FieldMeaning
vendorOne of cosyvoice, elevenlabs, minimax, volcengine, fish.
modelThe vendor’s model id.
voiceThe vendor’s voice id.
clonedtrue when the voice was made by the exporter in their own account, so it speaks only there.

cloned is decided at export time by asking the vendor’s voice list: system voices are not cloned; for vendors where that can’t be told, or when the lookup fails, the voice is treated as cloned.

On import, a voice is used only if it isn’t cloned and the importer has a model service from the same vendor. Otherwise the new character has no voice and shows subtitles only, and the importer is told why. rate, pitch and volume are kept only when the voice itself is.

persona.md

The character’s persona, as Markdown. It becomes the character’s instructions to the model. Treat packs from people you don’t know accordingly: before anything is created, the app shows the persona and warns that it will become the character’s instructions, and creates the character only after the user confirms. The same applies to packs dragged onto the character window.

avatar/

Present only when manifest.avatar is "included". It is the character’s avatar/ folder: the model and its avatar.json (format).

Whether an avatar goes in is decided at export:

AvatarResult
Built-inNot included; manifest.avatar is "builtin".
VRM that allows redistributionIncluded.
VRM whose terms say noNever included, whatever the user says; the exporter is told why.
VRM 0.x with license OtherThe terms are behind the author’s link, which a program can’t read. The exporter is asked to confirm they have the right to share it; included only if they do.
Live2DThe exporter is asked to confirm they have the right to share it; included only if they do.

For a VRM, the terms are read from the .vrm file itself on every export. The meta in avatar.json is only a cache and is never trusted.

Cubism Core never goes into a pack. If a pack contains it anyway, import drops it.

What a pack never contains

No API keys, service ids, memories, conversation history, or emotion state. There are tests that search the output for keys and for memory content.

Importing

  • Import always creates a new character and never modifies an existing one. If the name is taken, (2), (3)… is appended.
  • No avatar in the pack → the built-in avatar. A Live2D avatar is installed even if the importer has no Cubism Core yet, and they’re told to import Core; once they do, the character shows it with no second import.
  • A pack whose manifest.version is higher than the app supports is refused.

Writing one by hand

Zip the files above, with manifest.json at the root, and rename the result .deskmate. For a character with no avatar, only manifest.json ("avatar": "none"), character.json and persona.md are needed.

zip my-character.deskmate manifest.json character.json persona.md

Last updated on